diff --git a/Install/Program Files to Install/Autodesk.SteelConnections.ASIFC.dll b/Install/Program Files to Install/Autodesk.SteelConnections.ASIFC.dll index 5d1825ae..a868c86f 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/IFCExporterUIOverride.dll b/Install/Program Files to Install/IFCExporterUIOverride.dll new file mode 100644 index 00000000..db6f693c Binary files /dev/null and b/Install/Program Files to Install/IFCExporterUIOverride.dll differ diff --git a/Install/Program Files to Install/Revit.IFC.Export.dll b/Install/Program Files to Install/Revit.IFC.Export.dll new file mode 100644 index 00000000..539f4b58 Binary files /dev/null and b/Install/Program Files to Install/Revit.IFC.Export.dll differ diff --git a/Install/Program Files to Install/Revit.IFC.Import.dll b/Install/Program Files to Install/Revit.IFC.Import.dll new file mode 100644 index 00000000..129b4cb7 Binary files /dev/null and b/Install/Program Files to Install/Revit.IFC.Import.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..f66ee86a 100644 --- a/Install/Program Files to Install/bundle/Contents/Resources/ADSKIFCExporterHelp.htm +++ b/Install/Program Files to Install/bundle/Contents/Resources/ADSKIFCExporterHelp.htm @@ -67,9 +67,9 @@
-

Revit IFC 2024

+

Revit IFC 2025

- This application seamlessly replaces the built-in IFC capabilities of Revit 2024. It contains enhancements to functionality and defect fixes provided by Autodesk and Open Source contributors that did not appear in the initial shipping version. + This application seamlessly replaces the built-in IFC capabilities of Revit 2025. It contains enhancements to functionality and defect fixes provided by Autodesk and Open Source contributors that did not appear in the initial shipping version.

@@ -81,7 +81,7 @@

Getting Started

Revit IFC

- Revit IFC 2024 contains up-to-date improvements on the default IFC capabilities of Revit contributed by Autodesk and our Open + Revit IFC 2025 contains up-to-date improvements on the default IFC capabilities of Revit contributed by Autodesk and our Open Source contributors. While this app is not necessary for IFC support, it is recommended that users that depend on the quality of their IFC download this app and keep it up-to-date, as new enhancements and defect fixes are added, For more information on IFC, please visit the buildingSMART website or the Revit wiki. @@ -236,150 +236,51 @@

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.0.49
+
25.2.0.5

General:

    -
  • This is the major update of IFC Exporter for Revit 2024. -
  • It contains a various improvements and bug fixes for the basic Revit 2024. -
-

-
-

- Improvements: -

    -
  • Added export of the floor slab edge level. -
  • Added Width as an exported quantity to IFC for some assembly-based walls. -
  • Implemented IfcMaterialLayerSetUsage assigning to a single occurence. -
  • Improved export of sloped slabs. -
  • Improved stability when exporting projects to IFC with non-standard Author information in Project Standards. -
  • Updated French and German localization resources -
-

-
-

- Bug Fixes: -

    -
  • Fixed an unexpected error during file export. -
  • Fixed bug with inverted geometry after export of parts as ceilings. -
  • Fixed bug with swapped Height and Width values of opening in Base Quantities in exported IFC. -
  • Fixed export of NetSideArea, GrossSideArea, Height and Width to IFC4 QTO for curtain walls. -
  • Fixed geometry transformation for some cases. -
  • Fixed missing material associations for solid model bodies. -
  • Fixed openings local placement export. -
-

-
-
-
- -
24.1.1.6
-

- General: -

    -
  • This is the minor update of IFC Exporter for Revit 2024. -
  • It contains a various improvements and bug fixes for the basic Revit 2024. -
-

-
-

- Improvements: -

    -
  • The default import processor has been changed to Hybrid -
-

-
-

- Bug Fixes: -

    -
  • Fixed a bunch of potential bugs related to the compatibility with older Revit versions. -
  • Fix sketch-based openings in sloped slabs. -
  • Fixed placement of some specific wall sweep elements. -
-

-
-
-
- -
24.1.0.22
-

- General: -

    -
  • This is the first version of IFC Exporter for Revit 2024. -
  • It contains a various improvements and bug fixes for the basic Revit 2024. +
  • This is the first version of IFC Exporter for Revit 2025. +
  • It contains a various improvements and bug fixes for the basic Revit 2025.


Improvements:

    -
  • Added IFC2x3 Qto sets. -
  • Added material shared parameters export. -
  • Added the BarRole attribute when exporting IfcReinforcingBars to IFC. -
  • Added type entities for rebar and assemblies when exporting to IFC. -
  • Added support for exporting predefined types when exporting spaces to IFC4+. -
  • Added support of new measure units. -
  • Allowed export of rebar with slightly invalid transforms to IFC. -
  • Implemented exporting user defined parameters of 'Real' type as IfcReal with Revit display values. -
  • Implemented exporting of IfcRailingType entity. -
  • Implemented support of all 4 Revit velocity data types on export. -
  • Improved display of openings when export or linking in IFC files. -
  • Improved calculation of the linked levels elevation. -
  • Improved export of IfcPropertyEnumeratedValue. -
  • Improved export of insulation and lining as anything. -
  • Improved export of projects to IFC that contained family instances with invalid placements. -
  • Improved how classifications are viewed in certain older external applications when exporting to IFC. -
  • Improved list/bounded/table user defined properties values export for Instances and Types. -
  • Improved placement of federated (and some separate) links. -
  • Improved processing of representation items. -
  • Improved support for exporting advanced BReps to IFC if the unofficial IFC4 Design Transfer View is used. -
  • Improved the behavior for export of elements split by levels. -
  • Improved user defined property set mapping, especially for IFC2x3 entities that previously had no type entity exported. -
  • Updated 4x3 enums according to IFC4.3.1.0 Documentation. +
  • Added type information when exporting Rebar couplers to IFC. +
  • Allowed export of some elements to IFC4 Reference View [Structural] that had no material assignments. +
  • Allowed Revit to preserve the active site after IFC export. +
  • Allowed some old IFC configuration settings to be upgraded. +
  • Better treatment of IfcSpatialZones and zones in general. +
  • Improved base quantities calculation for spatial elements. +
  • Improved category mapping dialog. +
  • Improved error handling for invalid Revit file with missing project base and survey points. +
  • Improved export of classification data to IFC for curtain wall doors. +
  • Improved export of classification description to IFC. +
  • Improved export of IsExternal parameter, removed support at type level. +
  • Improved performance of some elongated elements export to IFC 4. +
  • Improved positioning of linked instances when exporting to IFC. +
  • 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. +
  • Improved the validity of exported IFC files when exporting some planar geometry.


Bug Fixes:

    -
  • Corrected the value for many MEP parameters exported to IFC. -
  • Fixed colour for exported pipe fitting. -
  • Fixed inconsistent IfcMaterialConstituent naming. -
  • Fixed export of a user-defined structural element type. -
  • Fixed export of IfcFurnitureType.AssemblyPlace attribute. -
  • Fixed export of IFC properties associated with the top-level IfcProject entity. -
  • Fixed export of materials from hosted wall sweep. -
  • Fixed export of material layer parameters of a ceiling element. -
  • Fixed export of user defined properties for roofs. -
  • Fixed export of some assemblies to IFC that resulted in orphaned entities. -
  • Fixed export of stairs layer name. -
  • Fixed IFC Classification export. -
  • Fixed IFCMEASUREWITHUNIT. -
  • Fixed log file creation for linking. -
  • Fixed the calculation of the height parameter for some railings in metric projects when a previous value had been calculated that wasn't applicable to this railing. -
  • Fixed the issue when IfcCovering sill's body was exported as Brep instead of SweptSolid. -
  • Fixed the processing of openings when exporting a wall as shape aspects (components). -
  • Fixed the slope common property set parameter calculation when exporting some stringers to IFC. -
  • Fixed issue with IfcCountMeasure value in IFC4x3. -
  • Fixed Revit Data types export. -
  • Fixed window opening misalignment. -
  • Removed the use of several Revit built-in parameters when exporting IFC properties that have the same name but are different. +
  • Corrected placement of some families inside assemblies when exporting to IFC. +
  • Fixed bug related to the exported grids to IFC if the project base elevation was significantly far from the origin. +
  • Fixed export of GrossArea to IFC4 QTO base quantities for Revit walls exported as IfcCovering. +
  • Fixed export of some missing elements when exporting linked documents to IFC. +
  • Fixed export of walls with openings placed next to clippings. +
  • Fixed Width parameter for IfcSlab and IfcCovering exported to IFC4 QTO base quantities. +
  • Fixed wrong elevation of linked models exporting host file with links to one IFC file.



- -
-
24.0.0.0
-

- General: - -

    -
  • This is the first version of IFC Extension for Revit 2024. -
-
-
- diff --git a/Install/Program Files to Install/bundle/PackageContents.xml b/Install/Program Files to Install/bundle/PackageContents.xml index 3ec722c5..64878809 100644 --- a/Install/Program Files to Install/bundle/PackageContents.xml +++ b/Install/Program Files to Install/bundle/PackageContents.xml @@ -1,10 +1,10 @@  - + - - - - - + + + + + \ No newline at end of file diff --git a/Install/RevitIFCSetupWix/Product.wxs b/Install/RevitIFCSetupWix/Product.wxs index 32dd1740..ead8a261 100644 --- a/Install/RevitIFCSetupWix/Product.wxs +++ b/Install/RevitIFCSetupWix/Product.wxs @@ -2,7 +2,7 @@ - + @@ -19,7 +19,7 @@ - + @@ -44,7 +44,7 @@ - + @@ -55,12 +55,12 @@ - + - + @@ -73,9 +73,9 @@ - + - + @@ -117,6 +117,12 @@ + + + + + + @@ -133,14 +139,10 @@ + + + - - - - - - - @@ -154,27 +156,15 @@ - - - - - - - - - - - - diff --git a/Install/RevitIFCSetupWix/RevitIFCSetupWix.wixproj b/Install/RevitIFCSetupWix/RevitIFCSetupWix.wixproj index 4556867e..f7a1c7ce 100644 --- a/Install/RevitIFCSetupWix/RevitIFCSetupWix.wixproj +++ b/Install/RevitIFCSetupWix/RevitIFCSetupWix.wixproj @@ -6,7 +6,7 @@ 3.8 7dfbd495-c588-4c7b-b8f6-5b793adb06f2 2.0 - IFC for Revit 2024.2.0.49 + IFC for Revit 2025.2.0.5 Package $(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..ac68ba99 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 RevitIFC2025.2.0.msi product.wixobj -ext WixUIExtension -copy RevitIFC2024.2.0.msi %1..\Releasex64 -del RevitIFC2024.2.0.msi +copy RevitIFC2025.2.0.msi %1..\Releasex64 +del RevitIFC2025.2.0.msi -echo %1..\Releasex64\RevitIFC2024.2.0.msi +echo %1..\Releasex64\RevitIFC2025.2.0.msi diff --git a/Revit.IFC.sln b/Revit.IFC.sln index f8d6db56..26d7a673 100644 --- a/Revit.IFC.sln +++ b/Revit.IFC.sln @@ -1,23 +1,23 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.32228.343 +# Visual Studio Version 17 +VisualStudioVersion = 17.8.34330.188 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Revit.IFC.Import", "Source\Revit.IFC.Import\Revit.IFC.Import.csproj", "{7F987D09-9716-4F50-ADE0-278E4B537101}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Revit.IFC.Import", "Source\Revit.IFC.Import\Revit.IFC.Import.csproj", "{7F987D09-9716-4F50-ADE0-278E4B537101}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Revit.IFC.Export", "Source\Revit.IFC.Export\Revit.IFC.Export.csproj", "{BCE5141A-291B-4CD8-A69B-7B9345AA00E9}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Revit.IFC.Export", "Source\Revit.IFC.Export\Revit.IFC.Export.csproj", "{BCE5141A-291B-4CD8-A69B-7B9345AA00E9}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Revit.IFC.Common", "Source\Revit.IFC.Common\Revit.IFC.Common.csproj", "{032EA4DC-181F-4453-9F93-E08DE1C07D95}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Revit.IFC.Common", "Source\Revit.IFC.Common\Revit.IFC.Common.csproj", "{032EA4DC-181F-4453-9F93-E08DE1C07D95}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IFCExporterUIOverride", "Source\IFCExporterUIOverride\IFCExporterUIOverride.csproj", "{BF694550-5BEB-4DCF-8EC2-A5904690DC17}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IFCExporterUIOverride", "Source\IFCExporterUIOverride\IFCExporterUIOverride.csproj", "{BF694550-5BEB-4DCF-8EC2-A5904690DC17}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Install", "Install", "{88C55E9A-2767-48B7-A035-E3D864C4FF09}" EndProject Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "RevitIFCSetupWix", "Install\RevitIFCSetupWix\RevitIFCSetupWix.wixproj", "{7DFBD495-C588-4C7B-B8F6-5B793ADB06F2}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RevitIFCTools", "Source\RevitIFCTools\RevitIFCTools.csproj", "{FFD592F6-DB2C-4E5D-9C38-9CCC9EABB3EA}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RevitIFCTools", "Source\RevitIFCTools\RevitIFCTools.csproj", "{FFD592F6-DB2C-4E5D-9C38-9CCC9EABB3EA}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Revit.IFC.Import.Core", "Source\Revit.IFC.Import.Core\Revit.IFC.Import.Core.csproj", "{B1E159B7-4B12-45A9-B83F-159E37798D44}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Revit.IFC.Import.Core", "Source\Revit.IFC.Import.Core\Revit.IFC.Import.Core.csproj", "{B1E159B7-4B12-45A9-B83F-159E37798D44}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/Source/IFCExporterUIOverride/IFCCategoryMapping/IFCCategoryMapping.xaml b/Source/IFCExporterUIOverride/IFCCategoryMapping/IFCCategoryMapping.xaml new file mode 100644 index 00000000..95873f57 --- /dev/null +++ b/Source/IFCExporterUIOverride/IFCCategoryMapping/IFCCategoryMapping.xaml @@ -0,0 +1,342 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - + diff --git a/Source/IFCExporterUIOverride/IFCExporterUIWindow.xaml.cs b/Source/IFCExporterUIOverride/IFCExporterUIWindow.xaml.cs index 80e58e2d..a50240fe 100644 --- a/Source/IFCExporterUIOverride/IFCExporterUIWindow.xaml.cs +++ b/Source/IFCExporterUIOverride/IFCExporterUIWindow.xaml.cs @@ -21,19 +21,18 @@ using Autodesk.Revit.DB.IFC; using Autodesk.Revit.UI; using Autodesk.UI.Windows; -using Microsoft.Win32; using Revit.IFC.Common.Utility; using Revit.IFC.Export.Utility; using System; using System.IO; using System.Collections.Generic; using System.Linq; -using System.Web.Script.Serialization; using System.Windows; using System.Windows.Controls; using UserInterfaceUtility.Json; using Revit.IFC.Common.Enums; using Revit.IFC.Common.Extensions; +using Newtonsoft.Json; namespace BIM.IFC.Export.UI { @@ -186,11 +185,34 @@ private void UpdateConfigurationsList(String currentConfigName) InitializeConfigurationList(currentConfigName); } + private void InitComboBoxCategoryMapping(Document document) + { + string originalSelectedItem = (string) comboBoxCategoryMapping.SelectedItem; + comboBoxCategoryMapping.SelectedItem = null; + comboBoxCategoryMapping.Items.Clear(); + + comboBoxCategoryMapping.Items.Add(Properties.Resources.InSessionConfiguration); + + IList mappingList = IFCCategoryTemplate.ListNames(document); + if (mappingList != null) + { + foreach (string mappingName in mappingList) + { + comboBoxCategoryMapping.Items.Add(mappingName); + } + } + + comboBoxCategoryMapping.SelectedItem = originalSelectedItem != null && comboBoxCategoryMapping.Items.Contains(originalSelectedItem) ? + originalSelectedItem : Properties.Resources.InSessionConfiguration; + } + /// /// Initializes the comboboxes via the configuration options. /// private void InitializeConfigurationOptions() { + Document document = IFCExport.TheDocument; + if (!comboboxIfcType.HasItems) { comboboxIfcType.Items.Add(new IFCVersionAttributes(IFCVersion.IFC2x2)); @@ -230,7 +252,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 +271,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 @@ -280,13 +301,18 @@ private void InitializeConfigurationOptions() comboboxLinkedFiles.Items.Add(new IFCLinkedFileExportAs(LinkedFileExportAs.ExportSameProject)); comboboxLinkedFiles.Items.Add(new IFCLinkedFileExportAs(LinkedFileExportAs.ExportSameSite)); } + + if (!comboBoxCategoryMapping.HasItems) + { + InitComboBoxCategoryMapping(document); + } } 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; @@ -326,6 +352,8 @@ private void UpdateActiveConfigurationOptions(IFCExportConfiguration configurati UpdateExchangeRequirement(configuration); + UpdateFacilityType(configuration); + foreach (IFCFileFormatAttributes format in comboboxFileType.Items.Cast()) { if (configuration.IFCFileType == format.FileType) @@ -371,6 +399,9 @@ private void UpdateActiveConfigurationOptions(IFCExportConfiguration configurati } } + string categoryMapping = configuration.CategoryMapping ?? Properties.Resources.InSessionConfiguration; + comboBoxCategoryMapping.SelectedItem = categoryMapping; + UpdatePhaseAttributes(configuration); checkboxExportBaseQuantities.IsChecked = configuration.ExportBaseQuantities; @@ -409,6 +440,10 @@ private void UpdateActiveConfigurationOptions(IFCExportConfiguration configurati userDefinedParameterMappingTable.Text = configuration.ExportUserDefinedParameterMappingFileName; checkBoxExportUserDefinedParameterMapping.IsChecked = configuration.ExportUserDefinedParameterMapping; + checkbox_ExportHostAsSingleEntity.IsChecked = configuration.ExportHostAsSingleEntity; + + 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 +489,11 @@ private void UpdateActiveConfigurationOptions(IFCExportConfiguration configurati checkBoxExportSpecificSchedules, checkBox_TriangulationOnly, checkbox_UseTypeNameOnly, - checkbox_UseVisibleRevitNameAsEntityName + checkbox_UseVisibleRevitNameAsEntityName, + checkbox_ExportHostAsSingleEntity, + checkbox_OwnerHistoryLastModified, + comboBoxCategoryMapping, + buttonCategoryMapping }; foreach (UIElement element in configurationElements) @@ -466,7 +505,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 +536,12 @@ private void UpdateActiveConfigurationOptions(IFCExportConfiguration configurati checkbox_UseVisibleRevitNameAsEntityName.IsChecked = configuration.UseVisibleRevitNameAsEntityName; checkbox_UseVisibleRevitNameAsEntityName.IsEnabled = true; + checkbox_ExportHostAsSingleEntity.IsChecked = configuration.ExportHostAsSingleEntity; + checkbox_ExportHostAsSingleEntity.IsEnabled = true; + + checkbox_OwnerHistoryLastModified.IsChecked = configuration.OwnerHistoryLastModified; + checkbox_OwnerHistoryLastModified.IsEnabled = true; + if (configuration.IFCVersion.Equals(IFCVersion.IFC2x3FM)) { DoCOBieSpecificSetup(configuration); @@ -677,6 +722,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); } /// @@ -705,50 +751,49 @@ private void buttonSaveSetup_Click(object sender, RoutedEventArgs e) return; } - SaveFileDialog saveFileDialog = new SaveFileDialog(); - saveFileDialog.AddExtension = true; - - saveFileDialog.DefaultExt = "json"; - saveFileDialog.Filter = Properties.Resources.ConfigurationFilePrefix + " (*.json)|*.json"; - saveFileDialog.FileName = Properties.Resources.ConfigurationFilePrefix + " - " + configuration.Name + ".json"; - saveFileDialog.InitialDirectory = GetDefaultDirectory(); - saveFileDialog.OverwritePrompt = false; - - bool? fileDialogResult = saveFileDialog.ShowDialog(); - if (fileDialogResult.HasValue && fileDialogResult.Value) + FileSaveDialog fileSaveDialog = new FileSaveDialog(Properties.Resources.ConfigurationFilePrefix + " (*.json)|*.json"); + fileSaveDialog.InitialFileName = GetDefaultDirectory() + "\\" + Properties.Resources.ConfigurationFilePrefix + " - " + configuration.Name + ".json"; + + if (fileSaveDialog.Show() == ItemSelectionDialogResult.Confirmed) { - IFCExportConfiguration configToSave = configuration.Clone(); - configToSave.Name = Path.GetFileNameWithoutExtension(saveFileDialog.FileName); - using (StreamWriter sw = new StreamWriter(saveFileDialog.FileName)) + try + { + ModelPath modelPath = fileSaveDialog.GetSelectedModelPath(); + string fileName = ModelPathUtils.ConvertModelPathToUserVisiblePath(modelPath); + IFCExportConfiguration configToSave = configuration.Clone(); + configToSave.Name = Path.GetFileNameWithoutExtension(fileName); + using (StreamWriter sw = new StreamWriter(fileName)) + { + JsonSerializerSettings dateFormatSettings = new JsonSerializerSettings + { + DateFormatHandling = DateFormatHandling.MicrosoftDateFormat + }; + sw.Write(SerializerUtils.FormatOutput(JsonConvert.SerializeObject(configToSave, dateFormatSettings))); + } + } + catch { - JavaScriptSerializer js = new JavaScriptSerializer(); - sw.Write(SerializerUtils.FormatOutput(js.Serialize(configToSave))); + // TODO: Give error. } } } private void buttonLoadSetup_Click(object sender, RoutedEventArgs e) { - OpenFileDialog openFileDialog = new Microsoft.Win32.OpenFileDialog(); - - // Set filter for file extension and default file extension - openFileDialog.DefaultExt = ".json"; - openFileDialog.Filter = Properties.Resources.ConfigurationFilePrefix + " (*.json)|*.json"; - openFileDialog.InitialDirectory = GetDefaultDirectory(); + FileOpenDialog fileOpenDialog = new FileOpenDialog(Properties.Resources.ConfigurationFilePrefix + " (*.json)|*.json"); + fileOpenDialog.Title = Properties.Resources.LoadSetup; // Display OpenFileDialog by calling ShowDialog method - bool? result = openFileDialog.ShowDialog(); - - // Get the selected file name and display in a TextBox - if (result.HasValue && result.Value) + if (fileOpenDialog.Show() == ItemSelectionDialogResult.Confirmed) { + // Get the selected file name and display in a TextBox try { - using (StreamReader sr = new StreamReader(openFileDialog.FileName)) + ModelPath modelPath = fileOpenDialog.GetSelectedModelPath(); + string fileName = ModelPathUtils.ConvertModelPathToUserVisiblePath(modelPath); + + using (StreamReader sr = new StreamReader(fileName)) { - JavaScriptSerializer jsConvert = new JavaScriptSerializer(); - jsConvert.RegisterConverters(new JavaScriptConverter[] { - new IFCExportConfigurationConverter() }); - IFCExportConfiguration configuration = jsConvert.Deserialize(sr.ReadToEnd()); + IFCExportConfiguration configuration = JsonConvert.DeserializeObject(sr.ReadToEnd(), new IFCExportConfigurationConverter()); if (configuration != null) { if (m_configurationsMap.HasName(configuration.Name)) @@ -789,6 +834,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); } } @@ -1120,6 +1167,8 @@ private void comboboxIfcType_SelectionChanged(object sender, SelectionChangedEve } UpdateExchangeRequirement(configuration); + + UpdateFacilityType(configuration); } if (configuration.IFCVersion.Equals(IFCVersion.IFC2x3FM)) @@ -1403,7 +1452,7 @@ private void buttonBrowse_Click(object sender, RoutedEventArgs e) { IFCExportConfiguration configuration = GetSelectedConfiguration(); - OpenFileDialog dlg = new OpenFileDialog(); + Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog(); // Set filter for file extension and default file extension dlg.DefaultExt = ".txt"; @@ -1438,7 +1487,7 @@ private void buttonParameterMappingBrowse_Click(object sender, RoutedEventArgs e { IFCExportConfiguration configuration = GetSelectedConfiguration(); - OpenFileDialog dlg = new OpenFileDialog(); + Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog(); dlg.DefaultExt = ".txt"; dlg.Filter = Properties.Resources.UserDefinedParameterMappingTable + @"|*.txt"; @@ -1516,6 +1565,18 @@ private void buttonAddressInformation_Click(object sender, RoutedEventArgs e) }; addressInformationWindow.ShowDialog(); } + private void buttonCategoryMapping_Click(object sender, RoutedEventArgs e) + { + IFCCategoryMapping categoryMapping = new IFCCategoryMapping() + { + Owner = this + }; + categoryMapping.ShowDialog(); + + // Refresh the category mapping pulldown in case we deleted an option. + InitComboBoxCategoryMapping(IFCExport.TheDocument); + } + private void buttonClassification_Click(object sender, RoutedEventArgs e) { @@ -1562,6 +1623,30 @@ private void Checkbox_UseTypeNameOnly_Unchecked(object sender, RoutedEventArgs e configuration.UseTypeNameOnlyForIfcType = false; } + private void Checkbox_ExportHostAsSingleEntity_Checked(object sender, RoutedEventArgs e) + { + IFCExportConfiguration configuration = GetSelectedConfiguration(); + configuration.ExportHostAsSingleEntity = true; + } + + private void Checkbox_ExportHostAsSingleEntity_Unchecked(object sender, RoutedEventArgs e) + { + IFCExportConfiguration configuration = GetSelectedConfiguration(); + configuration.ExportHostAsSingleEntity = 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(); @@ -1583,6 +1668,54 @@ private void comboBoxExchangeRequirement_SelectionChanged(object sender, Selecti } } + private void comboBoxFacilityType_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + if (comboBoxFacilityType.SelectedValue != null) + { + IFCExportConfiguration configuration = GetSelectedConfiguration(); + configuration.FacilityType = IFCFacilityTypes.GetFacilityEnum(comboBoxFacilityType.SelectedValue.ToString()); + UpdateFacilityPredefinedType(configuration); + } + } + + private void comboBoxFacilityPredefinedType_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + if (comboBoxFacilityPredefinedType.SelectedValue != null) + { + IFCExportConfiguration configuration = GetSelectedConfiguration(); + configuration.FacilityPredefinedType = + IFCFacilityTypes.GetFacilityPredefinedTypeEnum(configuration.FacilityType, + comboBoxFacilityPredefinedType.SelectedValue.ToString()); + } + } + + private void comboBoxCategoryMapping_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + if (comboBoxCategoryMapping.SelectedValue != null) + { + Document doc = IFCExport.TheDocument; + IFCExportConfiguration configuration = GetSelectedConfiguration(); + string categoryMappingName = (string)comboBoxCategoryMapping.SelectedItem; + + try + { + if (!string.IsNullOrWhiteSpace(categoryMappingName) && + IFCCategoryTemplate.FindByName(doc, categoryMappingName) != null) + { + configuration.CategoryMapping = categoryMappingName; + } + else + { + configuration.CategoryMapping = null; + } + } + catch + { + configuration.CategoryMapping = null; + } + } + } + private void UpdateExchangeRequirement(IFCExportConfiguration configuration) { if (IFCExchangeRequirements.ExchangeRequirements.ContainsKey(configuration.IFCVersion)) @@ -1599,6 +1732,48 @@ private void UpdateExchangeRequirement(IFCExportConfiguration configuration) comboBoxExchangeRequirement.IsEnabled = !configuration.IsBuiltIn; } + private void UpdateFacilityPredefinedType(IFCExportConfiguration configuration) + { + System.Windows.Visibility predefinedTypeVisibility = System.Windows.Visibility.Hidden; + KnownFacilityTypes facilityType = configuration.FacilityType; + if (IFCFacilityTypes.FacilityTypes.ContainsKey(configuration.IFCVersion)) + { + IList facilityPredefinedTypes = IFCFacilityTypes.FacilityPredefinedTypesForUI(facilityType); + comboBoxFacilityPredefinedType.ItemsSource = facilityPredefinedTypes; + comboBoxFacilityPredefinedType.SelectedItem = IFCFacilityTypes.ToFullLabel(facilityType, configuration.FacilityPredefinedType); + + if ((facilityPredefinedTypes?.Count ?? 0) > 0) + { + predefinedTypeVisibility = System.Windows.Visibility.Visible; + } + } + + comboBoxFacilityPredefinedType.Visibility = predefinedTypeVisibility; + labelFacilityPredefinedType.Visibility = predefinedTypeVisibility; + } + private void UpdateFacilityType(IFCExportConfiguration configuration) + { + if (IFCFacilityTypes.FacilityTypes.ContainsKey(configuration.IFCVersion)) + { + comboBoxFacilityType.ItemsSource = IFCFacilityTypes.FacilityTypesForUI(configuration.IFCVersion); + comboBoxFacilityType.SelectedItem = configuration.FacilityType.ToFullLabel(); + + comboBoxFacilityType.Visibility = System.Windows.Visibility.Visible; + labelFacilityType.Visibility = System.Windows.Visibility.Visible; + } + else + { + comboBoxFacilityType.ItemsSource = null; + comboBoxFacilityType.SelectedItem = null; + + comboBoxFacilityType.Visibility = System.Windows.Visibility.Hidden; + labelFacilityType.Visibility = System.Windows.Visibility.Hidden; + } + + UpdateFacilityPredefinedType(configuration); + comboBoxFacilityType.IsEnabled = true; + } + private void TextBox_EPSG_TextChanged(object sender, TextChangedEventArgs e) { } @@ -1726,7 +1901,9 @@ private void button_ExcludeElement_Click(object sender, RoutedEventArgs e) { IFCExportConfiguration configuration = GetSelectedConfiguration(); string desc = ""; - EntityTree entityTree = new EntityTree(configuration.IFCVersion, configuration.ExcludeFilter, desc, singleNodeSelection: false) + + EntityTree entityTree = new EntityTree(configuration.IFCVersion, + configuration.ExcludeFilter, desc, false) { Owner = this, Title = Properties.Resources.IFCEntitySelection diff --git a/Source/IFCExporterUIOverride/IFCFacilityTypes.cs b/Source/IFCExporterUIOverride/IFCFacilityTypes.cs new file mode 100644 index 00000000..530617ef --- /dev/null +++ b/Source/IFCExporterUIOverride/IFCFacilityTypes.cs @@ -0,0 +1,321 @@ +// +// BIM IFC export alternate UI library: this library works with Autodesk(R) Revit(R) to provide an alternate user interface for the export of IFC files from Revit. +// Copyright (C) 2016 Autodesk, Inc. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +using System; +using System.Collections.Generic; +using System.Linq; +using Autodesk.Revit.DB; +using BIM.IFC.Export.UI.Properties; +using Revit.IFC.Common.Enums; +using Revit.IFC.Export.Toolkit.IFC4x3; + +namespace BIM.IFC.Export.UI +{ + class IFCFacilityTypes + { + /// + /// The list of known facility types + /// + static IDictionary> KnownFacilityTypesByVersion = new Dictionary>(); + static IDictionary> KnownFacilityTypesLocalized = new Dictionary>(); + + static IDictionary> KnownFacilityPredefinedTypesLocalized = new Dictionary>(); + + static void Initialize() + { + if (KnownFacilityTypesByVersion.Count != 0) + return; + + // For IFC4.3 + const IFCVersion ifcVersion = IFCVersion.IFC4x3; + KnownFacilityTypesByVersion.Add(ifcVersion, + new List() + { + KnownFacilityTypes.Bridge, + KnownFacilityTypes.Building, + KnownFacilityTypes.MarineFacility, + KnownFacilityTypes.Railway, + KnownFacilityTypes.Road + } + ); + + IList facilityTypeNameListForUI = + new List(KnownFacilityTypesByVersion[ifcVersion].Select(x => x.ToFullLabel())); + KnownFacilityTypesLocalized.Add(ifcVersion, facilityTypeNameListForUI); + + IList bridgePredefinedTypesForUI = new List(); + foreach (IFCBridgeType type in Enum.GetValues(typeof(IFCBridgeType))) + { + bridgePredefinedTypesForUI.Add(type.ToFullLabel()); + } + KnownFacilityPredefinedTypesLocalized[KnownFacilityTypes.Bridge] = bridgePredefinedTypesForUI; + + IList marineFacilityPredefinedTypesForUI = new List(); + foreach (IFCMarineFacilityType type in Enum.GetValues(typeof(IFCMarineFacilityType))) + { + marineFacilityPredefinedTypesForUI.Add(type.ToFullLabel()); + } + KnownFacilityPredefinedTypesLocalized[KnownFacilityTypes.MarineFacility] = marineFacilityPredefinedTypesForUI; + } + + /// + /// Get the list of known facility types. + /// + public static IDictionary> FacilityTypes + { + get + { + Initialize(); + return KnownFacilityTypesByVersion; + } + } + + /// + /// Get the list of facility types for UI based on the given IFC Version. + /// + /// The IFC Version. + /// The list of known facility types. + public static IList FacilityTypesForUI(IFCVersion ifcVers) + { + Initialize(); + return KnownFacilityTypesLocalized.FirstOrDefault(x => x.Key == ifcVers).Value; + } + + /// + /// Get the list of facility predefined types for UI based on the given facility type. + /// + /// The facility type. + /// The list of known facility predefined types. + public static IList FacilityPredefinedTypesForUI(KnownFacilityTypes facilityType) + { + Initialize(); + return KnownFacilityPredefinedTypesLocalized.FirstOrDefault(x => x.Key == facilityType).Value; + } + + /// + /// Get the enumeration value of the facility type given the localized string from the UI + /// + /// the string value from the UI + /// The facility type enumeration + public static KnownFacilityTypes GetFacilityEnum(string uiFacilityTypeStringValue) + { + KnownFacilityTypes facilityType = KnownFacilityTypes.NotDefined; + + if (Resources.FacilityBuilding.Equals(uiFacilityTypeStringValue)) + facilityType = KnownFacilityTypes.Building; + else if (Resources.FacilityBridge.Equals(uiFacilityTypeStringValue)) + facilityType = KnownFacilityTypes.Bridge; + else if (Resources.FacilityMarineFacility.Equals(uiFacilityTypeStringValue)) + facilityType = KnownFacilityTypes.MarineFacility; + else if (Resources.FacilityRoad.Equals(uiFacilityTypeStringValue)) + facilityType = KnownFacilityTypes.Road; + else if (Resources.FacilityRailway.Equals(uiFacilityTypeStringValue)) + facilityType = KnownFacilityTypes.Railway; + + return facilityType; + } + + /// + /// Get the enumeration value of the bridge type given the localized string from the UI + /// + /// the string value from the UI + /// The bridge type enumeration + private static IFCBridgeType GetBridgePredefinedTypeEnum(string uiBridgeTypeStringValue) + { + IFCBridgeType bridgeTypeEnum = IFCBridgeType.NOTDEFINED; + + if (Resources.BridgeArched.Equals(uiBridgeTypeStringValue)) + bridgeTypeEnum = IFCBridgeType.ARCHED; + else if (Resources.BridgeCableStayed.Equals(uiBridgeTypeStringValue)) + bridgeTypeEnum = IFCBridgeType.CABLE_STAYED; + else if (Resources.BridgeCantilever.Equals(uiBridgeTypeStringValue)) + bridgeTypeEnum = IFCBridgeType.CANTILEVER; + else if (Resources.BridgeCulvert.Equals(uiBridgeTypeStringValue)) + bridgeTypeEnum = IFCBridgeType.CULVERT; + else if (Resources.BridgeFramework.Equals(uiBridgeTypeStringValue)) + bridgeTypeEnum = IFCBridgeType.FRAMEWORK; + else if (Resources.BridgeGirder.Equals(uiBridgeTypeStringValue)) + bridgeTypeEnum = IFCBridgeType.GIRDER; + else if (Resources.BridgeSuspension.Equals(uiBridgeTypeStringValue)) + bridgeTypeEnum = IFCBridgeType.SUSPENSION; + else if (Resources.BridgeTruss.Equals(uiBridgeTypeStringValue)) + bridgeTypeEnum = IFCBridgeType.TRUSS; + else if (Resources.UserDefined.Equals(uiBridgeTypeStringValue)) + bridgeTypeEnum = IFCBridgeType.USERDEFINED; + + return bridgeTypeEnum; + } + + /// + /// Get the enumeration value of the marine facility type given the localized string from the UI + /// + /// the string value from the UI + /// The bridge type enumeration + private static IFCMarineFacilityType GetMarineFacilityPredefinedTypeEnum(string uiMarineFacilityTypeStringValue) + { + IFCMarineFacilityType marineFacilityTypeEnum = IFCMarineFacilityType.NOTDEFINED; + + if (Resources.MarineFacilityBarrierBeach.Equals(uiMarineFacilityTypeStringValue)) + marineFacilityTypeEnum = IFCMarineFacilityType.BARRIERBEACH; + else if (Resources.MarineFacilityBreakwater.Equals(uiMarineFacilityTypeStringValue)) + marineFacilityTypeEnum = IFCMarineFacilityType.BREAKWATER; + else if (Resources.MarineFacilityCanal.Equals(uiMarineFacilityTypeStringValue)) + marineFacilityTypeEnum = IFCMarineFacilityType.CANAL; + else if (Resources.MarineFacilityDryDock.Equals(uiMarineFacilityTypeStringValue)) + marineFacilityTypeEnum = IFCMarineFacilityType.DRYDOCK; + else if (Resources.MarineFacilityFloatingDock.Equals(uiMarineFacilityTypeStringValue)) + marineFacilityTypeEnum = IFCMarineFacilityType.FLOATINGDOCK; + else if (Resources.MarineFacilityHydrolift.Equals(uiMarineFacilityTypeStringValue)) + marineFacilityTypeEnum = IFCMarineFacilityType.HYDROLIFT; + else if (Resources.MarineFacilityJetty.Equals(uiMarineFacilityTypeStringValue)) + marineFacilityTypeEnum = IFCMarineFacilityType.JETTY; + else if (Resources.MarineFacilityLaunchRecovery.Equals(uiMarineFacilityTypeStringValue)) + marineFacilityTypeEnum = IFCMarineFacilityType.LAUNCHRECOVERY; + else if (Resources.MarineFacilityMarineDefense.Equals(uiMarineFacilityTypeStringValue)) + marineFacilityTypeEnum = IFCMarineFacilityType.MARINEDEFENCE; + else if (Resources.MarineFacilityNavigationalChannel.Equals(uiMarineFacilityTypeStringValue)) + marineFacilityTypeEnum = IFCMarineFacilityType.NAVIGATIONALCHANNEL; + else if (Resources.MarineFacilityPort.Equals(uiMarineFacilityTypeStringValue)) + marineFacilityTypeEnum = IFCMarineFacilityType.PORT; + else if (Resources.MarineFacilityQuay.Equals(uiMarineFacilityTypeStringValue)) + marineFacilityTypeEnum = IFCMarineFacilityType.QUAY; + else if (Resources.MarineFacilityRevetment.Equals(uiMarineFacilityTypeStringValue)) + marineFacilityTypeEnum = IFCMarineFacilityType.REVETMENT; + else if (Resources.MarineFacilityShipLift.Equals(uiMarineFacilityTypeStringValue)) + marineFacilityTypeEnum = IFCMarineFacilityType.SHIPLIFT; + else if (Resources.MarineFacilityShipLock.Equals(uiMarineFacilityTypeStringValue)) + marineFacilityTypeEnum = IFCMarineFacilityType.SHIPLOCK; + else if (Resources.MarineFacilityShipyard.Equals(uiMarineFacilityTypeStringValue)) + marineFacilityTypeEnum = IFCMarineFacilityType.SHIPYARD; + else if (Resources.MarineFacilitySlipway.Equals(uiMarineFacilityTypeStringValue)) + marineFacilityTypeEnum = IFCMarineFacilityType.SLIPWAY; + else if (Resources.UserDefined.Equals(uiMarineFacilityTypeStringValue)) + marineFacilityTypeEnum = IFCMarineFacilityType.USERDEFINED; + else if (Resources.MarineFacilityWaterway.Equals(uiMarineFacilityTypeStringValue)) + marineFacilityTypeEnum = IFCMarineFacilityType.WATERWAY; + else if (Resources.MarineFacilityWaterwayShiplift.Equals(uiMarineFacilityTypeStringValue)) + marineFacilityTypeEnum = IFCMarineFacilityType.WATERWAYSHIPLIFT; + + return marineFacilityTypeEnum; + } + + /// + /// Get the enumeration value of the facility predefined type given the localized string from the UI + /// + /// the string value from the UI + /// the facility predefined type enumeration + public static Enum GetFacilityPredefinedTypeEnum(KnownFacilityTypes facilityType, string uiFacilityPredefinedTypeStringValue) + { + switch (facilityType) + { + case KnownFacilityTypes.Bridge: + return GetBridgePredefinedTypeEnum(uiFacilityPredefinedTypeStringValue); + case KnownFacilityTypes.MarineFacility: + return GetMarineFacilityPredefinedTypeEnum(uiFacilityPredefinedTypeStringValue); + } + + return null; + } + + /// + /// Parse the Facility Type name string into the associated Enum + /// + /// The Facility Type Name + /// The FacilityType enum + public static KnownFacilityTypes ParseFacilityTypeEnum(string erName) + { + if (Enum.TryParse(erName, out KnownFacilityTypes erEnum)) + { + return erEnum; + } + + return KnownFacilityTypes.NotDefined; + } + + /// + /// Parse the Facility Predefined Type name string into the associated Enum + /// + /// The Facility Type + /// The Facility Predefined Type Name + /// The FacilityPredefinedType enum + public static Enum ParseFacilityPredefinedTypeEnum(KnownFacilityTypes facilityTypes, string fptName) + { + switch (facilityTypes) + { + case KnownFacilityTypes.Bridge: + { + if (Enum.TryParse(fptName, out IFCBridgeType bridgeType)) + { + return bridgeType; + } + break; + } + case KnownFacilityTypes.MarineFacility: + { + if (Enum.TryParse(fptName, out IFCMarineFacilityType marineFacilityType)) + { + return marineFacilityType; + } + break; + } + } + + return null; + } + + /// + /// Return the validated enum value for a facility type. + /// + /// The facility type. + /// The unvalidated enum. + /// The enum if valid, or null if invalid. + public static Enum ValidatedPredefinedTypeEnum(KnownFacilityTypes facilityType, Enum facilityPredefinedTypeEnum) + { + if (facilityPredefinedTypeEnum == null) + return null; + + switch (facilityType) + { + case KnownFacilityTypes.Bridge: + return facilityPredefinedTypeEnum.GetType() == typeof(IFCBridgeType) ? + facilityPredefinedTypeEnum : null; + case KnownFacilityTypes.MarineFacility: + return facilityPredefinedTypeEnum.GetType() == typeof(IFCMarineFacilityType) ? + facilityPredefinedTypeEnum : null; + } + return null; + } + + public static string ToFullLabel(KnownFacilityTypes facilityType, Enum facility) + { + Enum validatedEnum = ValidatedPredefinedTypeEnum(facilityType, facility); + if (validatedEnum == null) + return null; + + switch (facilityType) + { + case KnownFacilityTypes.Bridge: + return ((IFCBridgeType)validatedEnum).ToFullLabel(); + case KnownFacilityTypes.MarineFacility: + return ((IFCMarineFacilityType)validatedEnum).ToFullLabel(); + } + + return null; + } + } +} diff --git a/Source/IFCExporterUIOverride/IFCLinkedDocumentExporter.cs b/Source/IFCExporterUIOverride/IFCLinkedDocumentExporter.cs new file mode 100644 index 00000000..7c168d2c --- /dev/null +++ b/Source/IFCExporterUIOverride/IFCLinkedDocumentExporter.cs @@ -0,0 +1,572 @@ +// BIM IFC export alternate UI library: this library works with Autodesk(R) Revit(R) to provide an alternate user interface for the export of IFC +// files from Revit. Copyright (C) 2016 Autodesk, Inc. +// +// This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +namespace BIM.IFC.Export.UI +{ + using System; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using Autodesk.Revit.DB; + using Autodesk.Revit.DB.IFC; + using Revit.IFC.Common.Utility; + using Revit.IFC.Export.Utility; + + /// + /// Defines the + /// + public class IFCLinkedDocumentExporter + { + + #region Private Fields + + /// + /// Defines the document + /// + private readonly Document document; + + /// + /// Defines the ifcOptions + /// + private readonly IFCExportOptions ifcOptions; + + /// + /// Gets or sets the ErrorMessage + /// + private string errorMessage = null; + + /// + /// Defines the numBadInstances + /// + private int numBadInstances = 0; + + #endregion Private Fields + + #region Internal Fields + + /// + /// Gets or sets the LinkGUIDsCache + /// + internal IDictionary linkGUIDsCache = + new Dictionary(); + + /// + /// Gets or sets the LinkInstanceTranforms + /// + internal IDictionary linkInstanceTranforms = null; + + #endregion Internal Fields + + #region Public Constructors + + /// + /// Initializes a new instance of the class. + /// + /// The document + /// The ifcOptions + public IFCLinkedDocumentExporter(Document document, IFCExportOptions ifcOptions) + { + this.document = document; + this.ifcOptions = ifcOptions; + } + + #endregion Public Constructors + + #region Private Methods + + /// + /// The SerializeTransform + /// + /// The tr + /// The + private static string SerializeTransform(Transform tr) + { + string retVal = string.Empty; + //serialize the transform values + retVal += SerializeXYZ(tr.Origin) + ";"; + retVal += SerializeXYZ(tr.BasisX) + ";"; + retVal += SerializeXYZ(tr.BasisY) + ";"; + retVal += SerializeXYZ(tr.BasisZ) + ";"; + return retVal; + } + + /// + /// The SerializeXYZ + /// + /// The value + /// The + private static string SerializeXYZ(XYZ value) + { + //transform to string + return value.ToString(); + } + + /// + /// The AddExpandedElementIdContent + /// + /// The messageString + /// The formatString + /// The items + private void AddExpandedElementIdContent(ref string messageString, string formatString, IList items) + { + if (messageString != "") + messageString += "\n"; + + if (items.Count > 0) + messageString += string.Format(formatString, ElementIdListToString(items)); + } + + /// + /// The AddExpandedStringContent + /// + /// The messageString + /// The formatString + /// The items + private void AddExpandedStringContent(ref string messageString, string formatString, IList items) + { + if (messageString != "") + messageString += "\n"; + + if (items.Count > 0) + messageString += string.Format(formatString, FileNameListToString(items)); + } + + // This modifies an existing string to display an expanded error message to the user. This modifies an existing string to display an expanded + // error message to the user. + /// + /// The ElementIdListToString + /// + /// The elementIds + /// The + private string ElementIdListToString(IList elementIds) + { + string elementString = ""; + foreach (ElementId elementId in elementIds) + { + if (elementString != "") + elementString += ", "; + elementString += elementId.ToString(); + } + return elementString; + } + + /// + /// The ExportLinkedDocuments + /// + /// The document + /// The fileName + /// The linkGUIDsCache + /// The idToTransform + /// The exportOptions + /// The originalFilterViewId + private void ExportLinkedDocuments(Document document, string fileName, + IDictionary linkGUIDsCache, + IDictionary idToTransform, + IFCExportOptions exportOptions, ElementId originalFilterViewId) + { + // get the extension + int index = fileName.LastIndexOf('.'); + if (index <= 0) + return; + string sExtension = fileName.Substring(index); + fileName = fileName.Substring(0, index); + + // get all the revit link instances + IDictionary rvtLinkNamesDict = new Dictionary(); + IDictionary> rvtLinkNamesToInstancesDict = + new Dictionary>(); + + try + { + View filterView = document.GetElement(originalFilterViewId) as View; + foreach (RevitLinkInstance rvtLinkInstance in idToTransform.Keys) + { + if (!IsLinkVisible(rvtLinkInstance, filterView)) + continue; + + // get the link document + Document linkDocument = rvtLinkInstance.GetLinkDocument(); + if (linkDocument == null) + continue; + + // get the link file path and name + String linkPathName = ""; + Parameter originalFileNameParam = linkDocument.ProjectInformation.LookupParameter("Original IFC File Name"); + if (originalFileNameParam != null && originalFileNameParam.StorageType == StorageType.String) + linkPathName = originalFileNameParam.AsString(); + else + linkPathName = linkDocument.PathName; + + // get the link file name + string linkFileName = GetLinkFileName(linkDocument, linkPathName); + + // add to names count dictionary + if (!rvtLinkNamesDict.Keys.Contains(linkFileName)) + rvtLinkNamesDict.Add(linkFileName, 0); + rvtLinkNamesDict[linkFileName]++; + + // add to names instances dictionary + if (!rvtLinkNamesToInstancesDict.Keys.Contains(linkPathName)) + rvtLinkNamesToInstancesDict.Add(linkPathName, new List()); + rvtLinkNamesToInstancesDict[linkPathName].Add(rvtLinkInstance); + } + } + catch + { + } + + foreach (KeyValuePair> linkPathNames in rvtLinkNamesToInstancesDict) + { + string linkPathName = linkPathNames.Key; + + // get the link instances + List currRvtLinkInstances = rvtLinkNamesToInstancesDict[linkPathName]; + IList linkFileNames = new List(); + IList> serTransforms = new List>(); + + Document linkDocument = null; + + foreach (RevitLinkInstance currRvtLinkInstance in currRvtLinkInstances) + { + // Nothing to report if the element itself is null. + if (currRvtLinkInstance == null) + continue; + + ElementId instanceId = currRvtLinkInstance.Id; + + // get the link document and the unit scale + linkDocument = linkDocument ?? currRvtLinkInstance.GetLinkDocument(); + + // get the link file path and name + string linkFileName = GetLinkFileName(linkDocument, linkPathName); + + //if link was an IFC file then make a different formating to the file name + if ((linkPathName.Length >= 4 && linkPathName.Substring(linkPathName.Length - 4).ToLower() == ".ifc") || + (linkPathName.Length >= 7 && linkPathName.Substring(linkPathName.Length - 7).ToLower() == ".ifcxml") || + (linkPathName.Length >= 7 && linkPathName.Substring(linkPathName.Length - 7).ToLower() == ".ifczip")) + { + string fName = fileName; + + //get output path and add to the new file name + index = fName.LastIndexOf("\\"); + if (index > 0) + fName = fName.Substring(0, index + 1); + else + fName = ""; + + //construct IFC file name + linkFileName = fName + linkFileName + "-"; + + //add guid + linkFileName += linkGUIDsCache[instanceId]; + } + else + { + // check if there are multiple instances with the same name + bool bMultiple = (rvtLinkNamesDict[linkFileName] > 1); + + // add the path + linkFileName = fileName + "-" + linkFileName; + + // add the guid + if (bMultiple) + { + linkFileName += "-"; + linkFileName += linkGUIDsCache[instanceId]; + } + } + + // add the extension + linkFileName += sExtension; + + linkFileNames.Add(linkFileName); + + // serialize transform + serTransforms.Add(Tuple.Create(instanceId, SerializeTransform(idToTransform[currRvtLinkInstance]))); + } + + if (linkDocument != null) + { + // export + try + { + int numLinkInstancesToExport = linkFileNames.Count; + exportOptions.AddOption("NumberOfExportedLinkInstances", numLinkInstancesToExport.ToString()); + + for (int ind = 0; ind < numLinkInstancesToExport; ind++) + { + string optionName = (ind == 0) ? "ExportLinkId" : "ExportLinkId" + (ind + 1).ToString(); + exportOptions.AddOption(optionName, serTransforms[ind].Item1.ToString()); + + optionName = (ind == 0) ? "ExportLinkInstanceTransform" : "ExportLinkInstanceTransform" + (ind + 1).ToString(); + exportOptions.AddOption(optionName, serTransforms[ind].Item2); + + // Don't pass in file name for the first link instance. + if (ind == 0) + continue; + + optionName = "ExportLinkInstanceFileName" + (ind + 1).ToString(); + exportOptions.AddOption(optionName, linkFileNames[ind]); + } + + // Pass in the first value; the rest will be in the options. + string path_ = Path.GetDirectoryName(linkFileNames[0]); + string fileName_ = Path.GetFileName(linkFileNames[0]); + + // Normally, IFC export would need a transaction, even if no permanent changes are made. For linked documents, though, that's + // handled by the export itself. + using (IFCLinkDocumentExportScope scope = new IFCLinkDocumentExportScope(linkDocument)) + { + linkDocument.Export(path_, fileName_, exportOptions); + } + } + catch + { + } + } + } + } + + /// + /// The FileNameListToString + /// + /// The fileNames + /// The + private string FileNameListToString(IList fileNames) + { + string fileNameListString = ""; + foreach (string fileName in fileNames) + { + if (fileNameListString != "") + fileNameListString += "; "; + fileNameListString += fileNames; + } + return fileNameListString; + } + + /// + /// The GetLinkedInstanceInfo + /// + /// The linkInstances + /// The + private (IDictionary, string, int) GetLinkedInstanceInfo( + IList linkInstances) + { + IDictionary linkedInstanceTransforms = + new SortedDictionary(new ElementComparer()); + + // We will keep track of the instances we can't export. Reasons we can't export: + // 1. Couldn't create a temporary document for exporting the linked instance. + // 2. The document for the linked instance can't be found. + // 3. The linked instance is mirrored, non-conformal, or scaled. + IList noTempDoc = new List(); + + IList nonConformalInst = new List(); + IList scaledInst = new List(); + IList instHasReflection = new List(); + + foreach (RevitLinkInstance currRvtLinkInstance in linkInstances) + { + // Nothing to report if the element itself is null. + if (currRvtLinkInstance == null) + continue; + + // get the link document and the unit scale + Document linkDocument = currRvtLinkInstance.GetLinkDocument(); + if (linkDocument == null) + { + // We can't distinguish between unloaded and an error condition, so we won't get a likely extraneous error. + continue; + } + + double lengthScaleFactorLink = UnitUtils.ConvertFromInternalUnits( + 1.0, + linkDocument.GetUnits().GetFormatOptions(SpecTypeId.Length).GetUnitTypeId()); + + // get the link transform + Transform tr = currRvtLinkInstance.GetTransform(); + + // We can't handle non-conformal, scaled, or mirrored transforms. + ElementId instanceId = currRvtLinkInstance.Id; + if (!tr.IsConformal) + { + nonConformalInst.Add(instanceId); + numBadInstances++; + continue; + } + + if (tr.HasReflection) + { + instHasReflection.Add(instanceId); + numBadInstances++; + continue; + } + + if (!MathUtil.IsAlmostEqual(tr.Determinant, 1.0)) + { + scaledInst.Add(instanceId); + numBadInstances++; + continue; + } + + // scale the transform origin + tr.Origin *= lengthScaleFactorLink; + + linkedInstanceTransforms[currRvtLinkInstance] = tr; + } + + // Show user errors, if any. + string expandedContent = string.Empty; + AddExpandedStringContent(ref expandedContent, Properties.Resources.LinkInstanceExportCantCreateDoc, noTempDoc); + AddExpandedElementIdContent(ref expandedContent, Properties.Resources.LinkInstanceExportNonConformal, nonConformalInst); + AddExpandedElementIdContent(ref expandedContent, Properties.Resources.LinkInstanceExportScaled, scaledInst); + AddExpandedElementIdContent(ref expandedContent, Properties.Resources.LinkInstanceExportHasReflection, instHasReflection); + + return (linkedInstanceTransforms, expandedContent, numBadInstances); + } + + /// + /// The GetLinkFileName + /// + /// The linkDocument + /// The linkPathName + /// The + private string GetLinkFileName(Document linkDocument, string linkPathName) + { + int index = linkPathName.LastIndexOf("\\"); + if (index <= 0) + return linkDocument.Title; + + string linkFileName = linkPathName.Substring(index + 1); + // remove the extension + index = linkFileName.LastIndexOf('.'); + if (index > 0) + linkFileName = linkFileName.Substring(0, index); + return linkFileName; + } + + /// + /// Checks if element is visible for certain view + /// + /// The element + /// The filterView + /// The + private bool IsLinkVisible(Element element, View filterView) + { + if (filterView == null) + return true; + + if (element.IsHidden(filterView)) + return false; + + if (!(element.Category?.get_Visible(filterView) ?? false)) + return false; + + return filterView.IsElementVisibleInTemporaryViewMode(TemporaryViewMode.TemporaryHideIsolate, element.Id); + } + + #endregion Private Methods + + #region Public Methods + + /// + /// The ExportSeparateDocuments + /// + /// The fullName + public void ExportSeparateDocuments(string fullName) + { + // We can't use the FilterViewId for linked documents, because the intermediate code assumes that it is in the exported document. As such, we + // will pass it in a special place. + ElementId originalFilterViewId = ifcOptions.FilterViewId; + if (originalFilterViewId != ElementId.InvalidElementId) + { + ifcOptions.AddOption("HostViewId", ifcOptions.FilterViewId.ToString()); + ifcOptions.FilterViewId = ElementId.InvalidElementId; + } + ExportOptionsCache.HostDocument = document; + ifcOptions.AddOption("ExportingLinks", LinkedFileExportAs.ExportAsSeparate.ToString()); + + ExportLinkedDocuments(document, fullName, linkGUIDsCache, linkInstanceTranforms, ifcOptions, originalFilterViewId); + } + + /// + /// The GetErrors + /// + /// The + public string GetErrors() + { + return errorMessage; + } + + /// + /// The GetNumberOfBadInstances + /// + /// The + public int GetNumberOfBadInstances() + { + return numBadInstances; + } + + /// + /// The ExportLinkedDocuments + /// + /// The linkExportKind + public void SetExportOption(LinkedFileExportAs linkExportKind) + { + int numBadInstances = 0; + + // Cache for links guids + FilteredElementCollector collector = new FilteredElementCollector(document); + ElementFilter elementFilter = new ElementClassFilter(typeof(RevitLinkInstance)); + List rvtLinkInstances = + collector.WherePasses(elementFilter).Cast().ToList(); + + string linkGuidOptionString = string.Empty; + + // Get the transforms of the links we can export, and error message information, including number, for the ones we can't. + (linkInstanceTranforms, errorMessage, numBadInstances) = + GetLinkedInstanceInfo(rvtLinkInstances); + + ISet existingGUIDs = new HashSet(); + bool inSeprateLinks = linkExportKind == LinkedFileExportAs.ExportAsSeparate; + + foreach (RevitLinkInstance linkInstance in linkInstanceTranforms.Keys) + { + Parameter parameter = linkInstance.get_Parameter(BuiltInParameter.IFC_GUID); + + string sGUID = GUIDUtil.GetSimpleElementIFCGUID(linkInstance); + + string sGUIDlower = sGUID.ToLower(); + + while (existingGUIDs.Contains(sGUIDlower)) + { + sGUID += "-"; + sGUIDlower += "-"; + } + existingGUIDs.Add(sGUIDlower); + + if (!inSeprateLinks) + linkGuidOptionString += linkInstance.Id.ToString() + "," + sGUID + ";"; + else + linkGUIDsCache.Add(linkInstance.Id, sGUID); + } + + if (!inSeprateLinks) + { + ifcOptions.AddOption("ExportingLinks", linkExportKind.ToString()); + ifcOptions.AddOption("FederatedLinkInfo", linkGuidOptionString); + } + } + + #endregion Public Methods + } +} 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 @@ private string m_purpose = null; - [ScriptIgnore] + [JsonIgnore] public string Purpose { get { return m_purpose; } @@ -50,7 +50,7 @@ public string Purpose /// The description of the address. /// private string m_description = null; - [ScriptIgnore] + [JsonIgnore] public string Description { get { return m_description; } @@ -67,7 +67,7 @@ public string Description /// The user defined purpose of the address. /// private string m_userDefinedPurpose = null; - [ScriptIgnore] + [JsonIgnore] public string UserDefinedPurpose { get { return m_userDefinedPurpose; } @@ -83,7 +83,7 @@ public string UserDefinedPurpose /// The internal location of the address. /// private string m_internalLocation = null; - [ScriptIgnore] + [JsonIgnore] public string InternalLocation { get { return m_internalLocation; } @@ -99,7 +99,7 @@ public string InternalLocation /// First line for the address. /// private string m_addressLine1 = string.Empty; - [ScriptIgnore] + [JsonIgnore] public string AddressLine1 { get { return m_addressLine1; } @@ -115,7 +115,7 @@ public string AddressLine1 /// Second line for the address. We limit to just 2 line addresses typically done in many applications. /// private string m_addressLine2 = string.Empty; - [ScriptIgnore] + [JsonIgnore] public string AddressLine2 { get { return m_addressLine2; } @@ -131,7 +131,7 @@ public string AddressLine2 /// PO Box address /// private string m_pOBox = null; - [ScriptIgnore] + [JsonIgnore] public string POBox { get { return m_pOBox; } @@ -147,7 +147,7 @@ public string POBox /// The city of the address. /// private string m_townOrCity = null; - [ScriptIgnore] + [JsonIgnore] public string TownOrCity { get { return m_townOrCity; } @@ -163,7 +163,7 @@ public string TownOrCity /// Region, province or state of the address. /// private string m_regionOrState = null; - [ScriptIgnore] + [JsonIgnore] public string RegionOrState { get { return m_regionOrState; } @@ -179,7 +179,7 @@ public string RegionOrState /// The postal code or zip code of the address /// private string m_postalCode = null; - [ScriptIgnore] + [JsonIgnore] public string PostalCode { get { return m_postalCode; } @@ -195,7 +195,7 @@ public string PostalCode /// The country of the address. /// private string m_country = null; - [ScriptIgnore] + [JsonIgnore] public string Country { get { return m_country; } diff --git a/Source/Revit.IFC.Common/Extension/IFCClassification.cs b/Source/Revit.IFC.Common/Extension/IFCClassification.cs index 4e767c9f..a03373b8 100644 --- a/Source/Revit.IFC.Common/Extension/IFCClassification.cs +++ b/Source/Revit.IFC.Common/Extension/IFCClassification.cs @@ -21,7 +21,7 @@ using System.Linq; using System.Text; using System.ComponentModel; -using System.Web.Script.Serialization; +using Newtonsoft.Json; namespace Revit.IFC.Common.Extensions { @@ -83,7 +83,7 @@ public DateTime ClassificationEditionDate get { return classificationEditionDate; } set { - classificationEditionDate = value.Date; + classificationEditionDate = new DateTime(value.Ticks, DateTimeKind.Utc); // Call OnPropertyChanged whenever the property is updated OnPropertyChanged("datePicker1"); } @@ -123,7 +123,7 @@ public string ClassificationFieldName /// This property is only used for the UI message. It will not be stored in the schema /// private string classificationTabMsg; - [ScriptIgnore] + [JsonIgnore] public string ClassificationTabMsg { get { return classificationTabMsg; } 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..843c920e 100644 --- a/Source/Revit.IFC.Common/Properties/AssemblyInfo.cs +++ b/Source/Revit.IFC.Common/Properties/AssemblyInfo.cs @@ -1,4 +1,3 @@ -#if IFC_OPENSOURCE using System.Reflection; // General Information about an assembly is controlled through the following @@ -7,15 +6,14 @@ [assembly: AssemblyTitle("IFC Common for Revit")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Autodesk")] +[assembly: AssemblyCompany("Autodesk, Inc.")] [assembly: AssemblyProduct("IFC Import/Exporter for Revit")] -[assembly: AssemblyCopyright("© 2012-2023 Autodesk, Inc.All rights reserved.")] +[assembly: AssemblyCopyright("© 2012-2024 Autodesk, Inc.All rights reserved.")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] -[assembly: AssemblyVersion("24.2.0.49")] -[assembly: AssemblyFileVersion("24.2.0.49")] -#endif +[assembly: AssemblyVersion("25.2.0.5")] +[assembly: AssemblyFileVersion("25.2.0.5")] // Version information can now be found in Source\Foundation\RevitENU\Version.cs diff --git a/Source/Revit.IFC.Common/Revit.IFC.Common.csproj b/Source/Revit.IFC.Common/Revit.IFC.Common.csproj index 4a4ad893..016a0670 100644 --- a/Source/Revit.IFC.Common/Revit.IFC.Common.csproj +++ b/Source/Revit.IFC.Common/Revit.IFC.Common.csproj @@ -1,126 +1,26 @@ - - + - Debug - x86 - 8.0.30703 - 2.0 - {032EA4DC-181F-4453-9F93-E08DE1C07D95} - Library - Properties Revit.IFC.Common Revit.IFC.Common - v4.8 - - - 512 - {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - 4 - Publish\ - true - Disk - false - Foreground - 7 - Days - false - false - true - 0 - 1.0.0.%2a - false - false - true - Revit.IFC - - - - - - false - x64 - bin\Debugx64\ - - - false - bin\Releasex64\ - x64 + + + + + - - ..\..\..\..\Program Files\Autodesk\Revit 2024\RevitAPI.dll - - - ..\..\..\..\Program Files\Autodesk\Revit 2024\RevitAPIIFC.dll - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - False - .NET Framework 2.0 %28x86%29 - false - - - False - .NET Framework 3.0 %28x86%29 - true - - - False - .NET Framework 3.5 - false - - - False - Windows Installer 3.1 - true - + + ..\..\..\..\Program Files\Autodesk\Revit 2025\RevitAPI.dll + + + ..\..\..\..\Program Files\Autodesk\Revit 2025\RevitAPIIFC.dll + - - - xcopy "$(TargetPath)" "C:\ProgramData\Autodesk\ApplicationPlugins\IFC 2024.bundle\Contents\2024\" /F /R /Y /I - - \ No newline at end of file diff --git a/Source/Revit.IFC.Common/Revit.IFC.Common.props b/Source/Revit.IFC.Common/Revit.IFC.Common.props deleted file mode 100644 index 0e84fc8e..00000000 --- a/Source/Revit.IFC.Common/Revit.IFC.Common.props +++ /dev/null @@ -1,25 +0,0 @@ - - - - bin\Debugx64\ - DEBUG;TRACE;IFC_OPENSOURCE - true - false - false - 4 - full - prompt - - - bin\Releasex64\ - TRACE;IFC_OPENSOURCE - false - true - false - 4 - none - prompt - - - - diff --git a/Source/Revit.IFC.Export/Utility/AllocatedGeometryObjectCache.cs b/Source/Revit.IFC.Common/Utility/AllocatedGeometryObjectCache.cs similarity index 86% rename from Source/Revit.IFC.Export/Utility/AllocatedGeometryObjectCache.cs rename to Source/Revit.IFC.Common/Utility/AllocatedGeometryObjectCache.cs index a4a6fb32..d746e987 100644 --- a/Source/Revit.IFC.Export/Utility/AllocatedGeometryObjectCache.cs +++ b/Source/Revit.IFC.Common/Utility/AllocatedGeometryObjectCache.cs @@ -1,58 +1,60 @@ -// -// BIM IFC library: this library works with Autodesk(R) Revit(R) to export IFC files containing model geometry. -// Copyright (C) 2012 Autodesk, Inc. -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; either -// version 2.1 of the License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -// - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -using Autodesk.Revit.DB; - -namespace Revit.IFC.Export.Utility -{ - /// - /// This class aggregates all allocated geometry objects so that they can be disposed of before returning from the IFC export. - /// Although the garbage collector would eventually catch up and dispose of these objects on its own, doing it preemptively is - /// necessary in order to avoid debug errors triggered by Revit when running automated tests. - /// - public class AllocatedGeometryObjectCache - { - private List m_geometryObjects = new List(); - - /// - /// Adds a new object to the cache. - /// - /// The object. - public void AddGeometryObject(GeometryObject geometryObject) - { - m_geometryObjects.Add(geometryObject); - } - - /// - /// Executes Dispose for all geometry objects contained in the cache. - /// - public void DisposeCache() - { - foreach (GeometryObject geometryObject in m_geometryObjects) - { - geometryObject.Dispose(); - } - } - } +// +// BIM IFC library: this library works with Autodesk(R) Revit(R) to export IFC files containing model geometry. +// Copyright (C) 2012 Autodesk, Inc. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using Autodesk.Revit.DB; + +namespace Revit.IFC.Common.Utility +{ + /// + /// This class aggregates all allocated geometry objects so that they can be disposed of before returning from the IFC export. + /// Although the garbage collector would eventually catch up and dispose of these objects on its own, doing it preemptively is + /// necessary in order to avoid debug errors triggered by Revit when running automated tests. + /// + public class AllocatedGeometryObjectCache + { + private List GeometryObjects { get; set; } = new List(); + + /// + /// Adds a new object to the cache. + /// + /// The object. + public void AddGeometryObject(GeometryObject geometryObject) + { + GeometryObjects.Add(geometryObject); + } + + /// + /// Executes Dispose for all geometry objects contained in the cache. + /// + public void DisposeCache() + { + foreach (GeometryObject geometryObject in GeometryObjects) + { + geometryObject.Dispose(); + } + + GeometryObjects.Clear(); + } + } } \ No newline at end of file diff --git a/Source/Revit.IFC.Common/Utility/COBieCompanyInfo.cs b/Source/Revit.IFC.Common/Utility/COBieCompanyInfo.cs index bcbc9580..66160935 100644 --- a/Source/Revit.IFC.Common/Utility/COBieCompanyInfo.cs +++ b/Source/Revit.IFC.Common/Utility/COBieCompanyInfo.cs @@ -4,7 +4,7 @@ using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; -using System.Web.Script.Serialization; +using Newtonsoft.Json; namespace Revit.IFC.Common.Utility { @@ -29,8 +29,7 @@ public COBieCompanyInfo(string compInfoStr) { if (!string.IsNullOrEmpty(compInfoStr)) { - JavaScriptSerializer js = new JavaScriptSerializer(); - COBieCompanyInfo compInfo = js.Deserialize(compInfoStr); + COBieCompanyInfo compInfo = JsonConvert.DeserializeObject(compInfoStr); CompanyType = compInfo.CompanyType; CompanyName = compInfo.CompanyName; StreetAddress = compInfo.StreetAddress; @@ -63,8 +62,7 @@ public bool PhoneValidator() public string ToJsonString() { - JavaScriptSerializer js = new JavaScriptSerializer(); - return js.Serialize(this); + return JsonConvert.SerializeObject(this); } } } \ No newline at end of file diff --git a/Source/Revit.IFC.Common/Utility/COBieProjectInfo.cs b/Source/Revit.IFC.Common/Utility/COBieProjectInfo.cs index b8624be0..58b8e367 100644 --- a/Source/Revit.IFC.Common/Utility/COBieProjectInfo.cs +++ b/Source/Revit.IFC.Common/Utility/COBieProjectInfo.cs @@ -3,7 +3,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -using System.Web.Script.Serialization; +using Newtonsoft.Json; namespace Revit.IFC.Common.Utility { @@ -27,8 +27,7 @@ public COBieProjectInfo(string projInfoStr) { if (!string.IsNullOrEmpty(projInfoStr)) { - JavaScriptSerializer js = new JavaScriptSerializer(); - COBieProjectInfo projInfo = js.Deserialize(projInfoStr); + COBieProjectInfo projInfo = JsonConvert.DeserializeObject(projInfoStr); BuildingName_Number = projInfo.BuildingName_Number; BuildingType = projInfo.BuildingType; BuildingDescription = projInfo.BuildingDescription; @@ -42,8 +41,7 @@ public COBieProjectInfo(string projInfoStr) public string ToJsonString() { - JavaScriptSerializer js = new JavaScriptSerializer(); - return js.Serialize(this); + return JsonConvert.SerializeObject(this); } } } \ No newline at end of file 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..2789a664 100644 --- a/Source/Revit.IFC.Common/Utility/IfcSchemaEntityTree.cs +++ b/Source/Revit.IFC.Common/Utility/IfcSchemaEntityTree.cs @@ -189,16 +189,22 @@ public string DumpTree() static IDictionary> DeprecatedOrUnsupportedDict = new Dictionary>() { - { Ifc4Schema, new HashSet() { "IfcAnnotation", "IfcProxy", "IfcOpeningStandardCase", "IfcBeamStandardCase", "IfcColumnStandardCase", "IfcDoorStandardCase", + { Ifc4x3Schema, new HashSet() { "IfcProxy", "IfcOpeningStandardCase", "IfcBeamStandardCase", "IfcColumnStandardCase", "IfcDoorStandardCase", "IfcMemberStandardCase", "IfcPlateStandardCase", "IfcSlabElementedCase", "IfcSlabStandardCase", "IfcWallElementedCase", "IfcWallStandardCase", "IfcWindowStandardCase", "IfcDoorStyle", "IfcWindowStyle", "IfcBuilding", "IfcBuildingStorey" } }, - { Ifc2x3Schema, new HashSet(){ "IfcAnnotation", "IfcElectricalElement", "IfcEquipmentElement", "IfcBuilding", "IfcBuildingStorey" } }, - { Ifc2x2Schema, new HashSet(){ "IfcAnnotation", "IfcBuilding", "IfcBuildingStorey" } } + { Ifc4Schema, new HashSet() { "IfcProxy", "IfcOpeningStandardCase", "IfcBeamStandardCase", "IfcColumnStandardCase", "IfcDoorStandardCase", + "IfcMemberStandardCase", "IfcPlateStandardCase", "IfcSlabElementedCase", "IfcSlabStandardCase", "IfcWallElementedCase", + "IfcWallStandardCase", "IfcWindowStandardCase", "IfcDoorStyle", "IfcWindowStyle", "IfcBuilding", "IfcBuildingStorey" } }, + { Ifc2x3Schema, new HashSet(){ "IfcElectricalElement", "IfcEquipmentElement", "IfcBuilding", "IfcBuildingStorey" } }, + { Ifc2x2Schema, new HashSet(){ "IfcBuilding", "IfcBuildingStorey" } } }; - static IDictionary m_IFCSchemaDict = new Dictionary(); - static IDictionary m_IFCSchemaTries { get; set; } - static IDictionary>> m_IFCEntityPredefTypeDict = new Dictionary>>(); + static IDictionary IFCSchemaDict { get; set; } = + new Dictionary(); + + static IDictionary IFCSchemaTries { get; set; } + + static IDictionary>> IFCEntityPredefTypeDict = new Dictionary>>(); /// /// return the standardized IFC schema name based on the various enumeration of IFCVersion @@ -257,16 +263,16 @@ static public IfcSchemaEntityTree GetEntityDictFor(IFCVersion ifcFileVersion) static public IfcSchemaEntityTree GetEntityDictFor(string schemaFile, string schemaLoc = null) { schemaFile = schemaFile.ToUpper(); - if (m_IFCSchemaDict.ContainsKey(schemaFile)) - return m_IFCSchemaDict[schemaFile]; + if (IFCSchemaDict.ContainsKey(schemaFile)) + return IFCSchemaDict[schemaFile]; // if not found, process the file and add into the static dictionary IfcSchemaEntityTree entityTree = PopulateEntityDictFor(schemaFile, schemaLoc); if (entityTree == null) return null; - m_IFCSchemaDict.Add(schemaFile, entityTree); - m_IFCEntityPredefTypeDict.Add(schemaFile, entityTree.PredefinedTypeEnumDict); + IFCSchemaDict.Add(schemaFile, entityTree); + IFCEntityPredefTypeDict.Add(schemaFile, entityTree.PredefinedTypeEnumDict); return entityTree; } @@ -323,28 +329,28 @@ static void ProcessSchemaFile(string dirLocation, ref HashSet schemaProc foreach (FileInfo fileInfo in dirInfo.GetFiles("*.xsd")) { string schemaId = Path.GetFileNameWithoutExtension(fileInfo.Name).ToUpper(); - if (!schemaProcessed.Contains(fileInfo.Name) && !m_IFCSchemaDict.ContainsKey(schemaId)) + if (!schemaProcessed.Contains(fileInfo.Name) && !IFCSchemaDict.ContainsKey(schemaId)) { IfcSchemaEntityTree entityTree = new IfcSchemaEntityTree(); bool success = ProcessIFCXMLSchema.ProcessIFCSchema(fileInfo, ref entityTree); if (success) { schemaProcessed.Add(fileInfo.Name); - m_IFCSchemaDict.Add(schemaId, entityTree); - m_IFCEntityPredefTypeDict.Add(schemaId, entityTree.PredefinedTypeEnumDict); + IFCSchemaDict.Add(schemaId, entityTree); + IFCEntityPredefTypeDict.Add(schemaId, entityTree.PredefinedTypeEnumDict); } } } } - static bool m_AllIFCSchemaProcessed = false; + static bool AllIFCSchemaProcessed { get; set; } = false; /// /// Get All IFC schema inside the designated folder. They will be cached /// static public void GetAllEntityDict() { - if (m_AllIFCSchemaProcessed) + if (AllIFCSchemaProcessed) return; HashSet schemaProcessed = new HashSet(); @@ -358,8 +364,8 @@ static public void GetAllEntityDict() ProcessSchemaFile(DirectoryUtil.IFCSchemaLocation, ref schemaProcessed); } - if (schemaProcessed.Count == m_IFCSchemaDict.Count) - m_AllIFCSchemaProcessed = true; + if (schemaProcessed.Count == IFCSchemaDict.Count) + AllIFCSchemaProcessed = true; } /// @@ -368,7 +374,7 @@ static public void GetAllEntityDict() /// The list of the schema trees static public IList GetAllCachedSchemaTrees() { - return m_IFCSchemaDict.Select(x => x.Value).ToList(); + return IFCSchemaDict.Select(x => x.Value).ToList(); } /// @@ -377,7 +383,7 @@ static public IList GetAllCachedSchemaTrees() /// the list of IFC schema names static public IList GetAllCachedSchemaNames() { - return m_IFCSchemaDict.Select(x => x.Key).ToList(); + return IFCSchemaDict.Select(x => x.Key).ToList(); } /// @@ -432,8 +438,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); @@ -679,9 +684,7 @@ static public IList GetPredefinedTypeList(IFCVersion context, string ifc /// List of PredefinedType strings static public IList GetPredefinedTypeList(IfcSchemaEntityTree ifcEntitySchemaTree, string ifcEntity) { - IList predefinedtypeList = new List(); - - if (ifcEntitySchemaTree == null || ifcEntitySchemaTree.IfcEntityDict == null || ifcEntitySchemaTree.IfcEntityDict.Count == 0) + if ((ifcEntitySchemaTree?.IfcEntityDict?.Count ?? 0) == 0) throw new Exception("Unable to locate IFC Schema xsd file! Make sure the relevant xsd exists."); if (string.IsNullOrEmpty(ifcEntity)) @@ -716,7 +719,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 (string projectedCRSName, string projectedCRSDesc, string epsgCode 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/Utility/SolidMeshGeometryInfo.cs b/Source/Revit.IFC.Common/Utility/SolidMeshGeometryInfo.cs similarity index 60% rename from Source/Revit.IFC.Export/Utility/SolidMeshGeometryInfo.cs rename to Source/Revit.IFC.Common/Utility/SolidMeshGeometryInfo.cs index 7723c739..6364bd8f 100644 --- a/Source/Revit.IFC.Export/Utility/SolidMeshGeometryInfo.cs +++ b/Source/Revit.IFC.Common/Utility/SolidMeshGeometryInfo.cs @@ -1,259 +1,334 @@ -// -// BIM IFC library: this library works with Autodesk(R) Revit(R) to export IFC files containing model geometry. -// Copyright (C) 2012 Autodesk, Inc. -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; either -// version 2.1 of the License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -// -using System; -using System.Collections.Generic; - -using Autodesk.Revit.DB; -using Autodesk.Revit.DB.IFC; -using Revit.IFC.Common.Utility; - -namespace Revit.IFC.Export.Utility -{ - /// - /// A solid with extra pertinant information. - /// - public class SolidInfo - { - /// - /// The constructor. - /// - /// The solid. - /// The optional owner element for this solid. - public SolidInfo(Solid solid, Element ownerElement) - { - Solid = solid; - OwnerElement = ownerElement; - } - - /// - /// The contained solid. - /// - public Solid Solid { get; protected set; } - - /// - /// The element that contains the solid in its GeometryElement. - /// This is optional, and can be unset (null). - /// - public Element OwnerElement { get; protected set; } - } - - /// - /// A container class for lists of solids and meshes. - /// - /// - /// Added in 2013, this is a migration from the IFCSolidMeshGeometryInfo class - /// that is used within the BeamExporter, BodyExporter, WallExporter, - /// FamilyInstanceExporter and RepresentationUtil classes. - /// - public class SolidMeshGeometryInfo - { - // A list of collected solids, and the external element (if any) that generated them. - // In general, this will be this will be non-null if the geometry come from an - // Instance/Symbol pair, and will be the element that contains the geometry - // that the Instance is pointing to. - private IList m_SolidInfoList; - - // A list of collected meshes. - private List m_MeshesList; - - /// - /// Creates a default SolidMeshGeometryInfo with empty solidsList and meshesList. - /// - public SolidMeshGeometryInfo() - { - m_SolidInfoList = new List(); - m_MeshesList = new List(); - } - - /// - /// Appends a given Solid to the solidsList. - /// - /// - /// The Solid we are appending to the solidsList. - /// - public void AddSolid(Solid solidToAdd, Element externalElement) - { - m_SolidInfoList.Add(new SolidInfo(solidToAdd, externalElement)); - } - - /// - /// Appends a given Mesh to the meshesList. - /// - /// - /// The Mesh we are appending to the meshesList. - /// - public void AddMesh(Mesh meshToAdd) - { - m_MeshesList.Add(meshToAdd); - } - - /// - /// Returns the list of Solids and their generating external elements. - /// - /// We return a List instead of an IList for the AddRange functionality. - public List GetSolids() - { - List solids = new List(); - foreach (SolidInfo solidInfo in m_SolidInfoList) - { - solids.Add(solidInfo.Solid); - } - return solids; - } - - /// - /// Returns the list of Solids and their generating external elements. - /// - public IList GetSolidInfos() - { - return m_SolidInfoList; - } - - /// - /// Returns the list of Meshes. - /// - public List GetMeshes() - { - return m_MeshesList; - } - - /// - /// Returns the number of Solids in solidsList. - /// - public int SolidsCount() - { - return m_SolidInfoList.Count; - } - - /// - /// Returns the number of Meshes in meshesList. - /// - public int MeshesCount() - { - return m_MeshesList.Count; - } - - /// - /// This method takes the solidsList and clips all of its solids between the given range. - /// - /// The Element from which we obtain our BoundingBoxXYZ. - /// The top-level GeometryElement from which to gather X and Y - /// coordinates for the intersecting solid. - /// The IFCRange whose Z values we use to create an intersecting - /// solid to clip the solids in this class's internal solidsList. - /// If range boundaries are equal, method returns, performing no clippings. - public void ClipSolidsList(GeometryElement geomElem, IFCRange range) - { - if (geomElem == null) - { - throw new ArgumentNullException("geomElemToUse"); - } - - if (MathUtil.IsAlmostEqual(range.Start, range.End) || SolidsCount() == 0) - { - return; - } - - double bottomZ; - double boundDifference; - if (range.Start < range.End) - { - bottomZ = range.Start; - boundDifference = range.End - range.Start; - } - else - { - bottomZ = range.End; - boundDifference = range.Start - range.End; - } - - // create a new solid using the X and Y of the bounding box on the top level GeometryElement and the Z of the IFCRange - BoundingBoxXYZ elemBoundingBox = geomElem.GetBoundingBox(); - XYZ pointA = new XYZ(elemBoundingBox.Min.X, elemBoundingBox.Min.Y, bottomZ); - XYZ pointB = new XYZ(elemBoundingBox.Max.X, elemBoundingBox.Min.Y, bottomZ); - XYZ pointC = new XYZ(elemBoundingBox.Max.X, elemBoundingBox.Max.Y, bottomZ); - XYZ pointD = new XYZ(elemBoundingBox.Min.X, elemBoundingBox.Max.Y, bottomZ); - - List perimeter = new List(); - - try - { - perimeter.Add(Line.CreateBound(pointA, pointB)); - perimeter.Add(Line.CreateBound(pointB, pointC)); - perimeter.Add(Line.CreateBound(pointC, pointD)); - perimeter.Add(Line.CreateBound(pointD, pointA)); - } - catch - { - // One of the boundary lines was invalid. Do nothing. - return; - } - - List boxPerimeterList = new List(); - boxPerimeterList.Add(CurveLoop.Create(perimeter)); - Solid intersectionSolid = GeometryCreationUtilities.CreateExtrusionGeometry(boxPerimeterList, XYZ.BasisZ, boundDifference); - - // cycle through the elements in solidsList and intersect them against intersectionSolid to create a new list - List clippedSolidsList = new List(); - Solid currSolid; - - foreach (SolidInfo solidAndElement in m_SolidInfoList) - { - Solid solid = solidAndElement.Solid; - - try - { - // ExecuteBooleanOperation can throw if it fails. In this case, just ignore the clipping. - currSolid = BooleanOperationsUtils.ExecuteBooleanOperation(solid, intersectionSolid, BooleanOperationsType.Intersect); - if (currSolid != null && currSolid.Volume != 0) - { - clippedSolidsList.Add(new SolidInfo(currSolid, solidAndElement.OwnerElement)); - } - } - catch - { - // unable to perform intersection, add original solid instead - clippedSolidsList.Add(solidAndElement); - } - } - - m_SolidInfoList = clippedSolidsList; - } - - /// - /// Splits any solid volumes which consist of multiple closed bodies into individual solids (and updates the storage accordingly). - /// - public void SplitSolidsList() - { - IList splitSolidsList = new List(); - - foreach (SolidInfo solidInfo in m_SolidInfoList) - { - Element element = solidInfo.OwnerElement; - IList splitSolids = GeometryUtil.SplitVolumes(solidInfo.Solid); - foreach (Solid splitSolid in splitSolids) - { - splitSolidsList.Add(new SolidInfo(splitSolid, element)); - } - } - - m_SolidInfoList = splitSolidsList; - } - } +// +// BIM IFC library: this library works with Autodesk(R) Revit(R) to export IFC files containing model geometry. +// Copyright (C) 2012 Autodesk, Inc. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +using System; +using System.Collections.Generic; +using System.Linq; +using Autodesk.Revit.DB; +using Autodesk.Revit.DB.IFC; + +namespace Revit.IFC.Common.Utility +{ + /// + /// A solid with extra pertinant information. + /// + public class SolidInfo + { + /// + /// The constructor. + /// + /// The solid. + /// The optional owner element for this solid. + public SolidInfo(Solid solid, Element ownerElement) + { + Solid = solid; + OwnerElement = ownerElement; + } + + /// + /// The contained solid. + /// + public Solid Solid { get; protected set; } + + /// + /// The element that contains the solid in its GeometryElement. + /// This is optional, and can be unset (null). + /// + public Element OwnerElement { get; protected set; } + } + + /// + /// A container class for lists of solids and meshes. + /// + /// + /// Added in 2013, this is a migration from the IFCSolidMeshGeometryInfo class + /// that is used within the BeamExporter, BodyExporter, WallExporter, + /// FamilyInstanceExporter and RepresentationUtil classes. + /// + public class SolidMeshGeometryInfo + { + // A list of collected solids, and the external element (if any) that generated them. + // In general, this will be this will be non-null if the geometry come from an + // Instance/Symbol pair, and will be the element that contains the geometry + // that the Instance is pointing to. + public IList SolidInfoList { get; set; } = new List(); + + // A list of collected meshes. + public List MeshesList { get; protected set; } = new List(); + + /// + /// Creates a default SolidMeshGeometryInfo with empty solidsList and meshesList. + /// + public SolidMeshGeometryInfo() + { + } + + /// + /// Appends a given Solid to the solidsList. + /// + /// + /// The Solid we are appending to the solidsList. + /// + public void AddSolid(Solid solidToAdd, Element externalElement) + { + SolidInfoList.Add(new SolidInfo(solidToAdd, externalElement)); + } + + /// + /// Appends a given Mesh to the meshesList. + /// + /// + /// The Mesh we are appending to the meshesList. + /// + public void AddMesh(Mesh meshToAdd) + { + MeshesList.Add(meshToAdd); + } + + /// + /// Returns the list of Solids and their generating external elements. + /// + /// We return a List instead of an IList for the AddRange functionality. + public List GetSolids() + { + List solids = new List(); + foreach (SolidInfo solidInfo in SolidInfoList) + { + solids.Add(solidInfo.Solid); + } + return solids; + } + + /// + /// Returns the list of Meshes. + /// + public List GetMeshes() + { + return MeshesList; + } + + /// + /// Returns the number of Solids in solidsList. + /// + public int SolidsCount() + { + return SolidInfoList.Count; + } + + /// + /// Returns the number of Meshes in meshesList. + /// + public int MeshesCount() + { + return MeshesList.Count; + } + + /// + /// This method takes the solidsList and clips all of its solids between the given range. + /// + /// The Element from which we obtain our BoundingBoxXYZ. + /// The top-level GeometryElement from which to gather X and Y + /// coordinates for the intersecting solid. + /// The IFCRange whose Z values we use to create an intersecting + /// solid to clip the solids in this class's internal solidsList. + /// If range boundaries are equal, method returns, performing no clippings. + public void ClipSolidsList(GeometryElement geomElem, IFCRange range) + { + if (geomElem == null) + { + throw new ArgumentNullException("geomElemToUse"); + } + + if (MathUtil.IsAlmostEqual(range.Start, range.End) || SolidsCount() == 0) + { + return; + } + + double bottomZ; + double boundDifference; + if (range.Start < range.End) + { + bottomZ = range.Start; + boundDifference = range.End - range.Start; + } + else + { + bottomZ = range.End; + boundDifference = range.Start - range.End; + } + + // create a new solid using the X and Y of the bounding box on the top level GeometryElement and the Z of the IFCRange + BoundingBoxXYZ elemBoundingBox = geomElem.GetBoundingBox(); + XYZ pointA = new XYZ(elemBoundingBox.Min.X, elemBoundingBox.Min.Y, bottomZ); + XYZ pointB = new XYZ(elemBoundingBox.Max.X, elemBoundingBox.Min.Y, bottomZ); + XYZ pointC = new XYZ(elemBoundingBox.Max.X, elemBoundingBox.Max.Y, bottomZ); + XYZ pointD = new XYZ(elemBoundingBox.Min.X, elemBoundingBox.Max.Y, bottomZ); + + List perimeter = new List(); + + try + { + perimeter.Add(Line.CreateBound(pointA, pointB)); + perimeter.Add(Line.CreateBound(pointB, pointC)); + perimeter.Add(Line.CreateBound(pointC, pointD)); + perimeter.Add(Line.CreateBound(pointD, pointA)); + } + catch + { + // One of the boundary lines was invalid. Do nothing. + return; + } + + List boxPerimeterList = new List(); + boxPerimeterList.Add(CurveLoop.Create(perimeter)); + Solid intersectionSolid = GeometryCreationUtilities.CreateExtrusionGeometry(boxPerimeterList, XYZ.BasisZ, boundDifference); + + // cycle through the elements in solidsList and intersect them against intersectionSolid to create a new list + List clippedSolidsList = new List(); + Solid currSolid; + + foreach (SolidInfo solidAndElement in SolidInfoList) + { + Solid solid = solidAndElement.Solid; + + try + { + // ExecuteBooleanOperation can throw if it fails. In this case, just ignore the clipping. + currSolid = BooleanOperationsUtils.ExecuteBooleanOperation(solid, intersectionSolid, BooleanOperationsType.Intersect); + if (currSolid != null && currSolid.Volume != 0) + { + clippedSolidsList.Add(new SolidInfo(currSolid, solidAndElement.OwnerElement)); + } + } + catch + { + // unable to perform intersection, add original solid instead + clippedSolidsList.Add(solidAndElement); + } + } + + SolidInfoList = clippedSolidsList; + } + + /// + /// Transforms a geometry by a given transform. + /// + /// The geometry element created by "GetTransformed" is a copy which will have its own allocated + /// membership - this needs to be stored and disposed of (see AllocatedGeometryObjectCache + /// for details) + /// The geometry. + /// The transform. + /// The cache that will prevent the data from being disposed. + /// The transformed geometry. + public static GeometryElement GetTransformedGeometry(GeometryElement geomElem, + Transform trf, AllocatedGeometryObjectCache geometryObjectCache) + { + if (geomElem == null) + return null; + + GeometryElement currGeomElem = geomElem.GetTransformed(trf); + geometryObjectCache.AddGeometryObject(currGeomElem); + return currGeomElem; + } + + + /// + /// Collects all solids and meshes within all nested levels of a given GeometryElement. + /// + /// + /// This is intended as a private helper method for the GetSolidMeshGeometry type collection methods. + /// + /// The GeometryElement we are collecting solids and meshes from. + /// The element that contains the geomElem. It can be null. + /// The initial Transform applied on the GeometryElement. + /// The cache that will prevent the data from being disposed. + private void CollectSolidMeshGeometry(GeometryElement geomElem, + Element containingElement, Transform trf, AllocatedGeometryObjectCache geometryObjectCache) + { + if (geomElem == null) + return; + + GeometryElement currGeomElem = geomElem; + Transform localTrf = trf; + if (localTrf == null) + localTrf = Transform.Identity; + else if (!localTrf.IsIdentity) + currGeomElem = GetTransformedGeometry(geomElem, localTrf, geometryObjectCache); + + // iterate through the GeometryObjects contained in the GeometryElement + foreach (GeometryObject geomObj in currGeomElem) + { + // Add try catch here because in a rare cases we find solid that throws exception/invalid solid.Faces + try + { + Solid solid = geomObj as Solid; + if (solid != null && solid.Faces.Size > 0) + { + AddSolid(solid, containingElement); + } + else + { + Mesh mesh = geomObj as Mesh; + if (mesh != null) + { + AddMesh(mesh); + } + else + { + // if the current geomObj is castable as a GeometryInstance, then we perform the same collection on its symbol geometry + GeometryInstance inst = geomObj as GeometryInstance; + + if (inst != null) + { + try + { + GeometryElement instanceSymbol = inst.GetSymbolGeometry(); + if (instanceSymbol != null && instanceSymbol.Count() != 0) + { + Transform instanceTransform = localTrf.Multiply(inst.Transform); + Element symbol = inst.GetDocument()?.GetElement(inst.GetSymbolGeometryId().SymbolId); + CollectSolidMeshGeometry(instanceSymbol, symbol, + instanceTransform, geometryObjectCache); + } + } + catch + { + } + } + } + } + } + catch + { + } + } + } + + /// + /// Collects all solids and meshes within all nested levels of a given GeometryElement. + /// + /// + /// This is intended as a helper method for the GetSolidMeshGeometry type collection methods. + /// + /// The GeometryElement we are collecting solids and meshes from. + public void CollectSolidMeshGeometry(GeometryElement geomElem, AllocatedGeometryObjectCache geometryObjectCache) + { + CollectSolidMeshGeometry(geomElem, null, Transform.Identity, geometryObjectCache); + } + } } \ No newline at end of file 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 @@ public static void SetLocalPlacementsRelativeToAssembly(ExporterIFC exporterIFC, 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..a80ae327 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 } } @@ -356,28 +355,23 @@ private static IFCAnyHandle CreateBeamAxis(ExporterIFC exporterIFC, Element elem } /// - /// Creates a new IfcBeamType and relates it to the current element. + /// Creates a new type entity appropriate to the object and relates it to the current element. /// /// The exporter. /// The ProductWrapper class. /// The element handle. /// The element. /// The material id used for the element type. - public static void ExportBeamType(ExporterIFC exporterIFC, ProductWrapper wrapper, IFCAnyHandle elementHandle, Element element, string predefinedType) + public static void ExportBeamType(ExporterIFC exporterIFC, ProductWrapper wrapper, IFCAnyHandle elementHandle, + Element element, IFCExportInfoPair exportType) { - if (elementHandle == null || element == null) + if (elementHandle == null) return; - Document doc = element.Document; - ElementId typeElemId = element.GetTypeId(); - ElementType elementType = doc.GetElement(typeElemId) as ElementType; + ElementType elementType = element?.Document?.GetElement(element?.GetTypeId()) as ElementType; if (elementType == null) return; - string preDefinedTypeSearch = predefinedType; - if (string.IsNullOrEmpty(preDefinedTypeSearch)) - preDefinedTypeSearch = "NULL"; - IFCExportInfoPair exportType = new IFCExportInfoPair(IFCEntityType.IfcBeamType, preDefinedTypeSearch); IFCAnyHandle beamType = ExporterCacheManager.ElementTypeToHandleCache.Find(elementType, exportType); if (!IFCAnyHandleUtil.IsNullOrHasNoValue(beamType)) { @@ -386,8 +380,8 @@ public static void ExportBeamType(ExporterIFC exporterIFC, ProductWrapper wrappe } // Property sets will be set later. - beamType = IFCInstanceExporter.CreateBeamType(exporterIFC.GetFile(), elementType, null, - null, null, predefinedType); + beamType = IFCInstanceExporter.CreateGenericIFCType(exportType, elementType, null, exporterIFC.GetFile(), + null, null); wrapper.RegisterHandleWithElementType(elementType, exportType, beamType, null); @@ -538,8 +532,7 @@ public static void ExportBeamType(ExporterIFC exporterIFC, ProductWrapper wrappe 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,8 +543,8 @@ public static void ExportBeamType(ExporterIFC exporterIFC, ProductWrapper wrappe 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.CreateGenericIFCEntity(exportType, exporterIFC, element, instanceGUID, + ExporterCacheManager.OwnerHistoryHandle, extrusionCreationData.GetLocalPlacement(), prodRep); IFCAnyHandle mpSetUsage; if (materialProfileSet != null) @@ -559,7 +552,7 @@ public static void ExportBeamType(ExporterIFC exporterIFC, ProductWrapper wrappe productWrapper.AddElement(element, beam, setter, extrusionCreationData, true, exportType); - ExportBeamType(exporterIFC, productWrapper, beam, element, exportType.ValidatedPredefinedType); + ExportBeamType(exporterIFC, productWrapper, beam, element, exportType); 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..ad9edff8 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 { /// @@ -36,6 +35,19 @@ namespace Revit.IFC.Export.Exporter /// class BodyExporter { + class FaceSetInfo + { + public FaceSetInfo(HashSet faceSetHandles, bool isClosed) + { + FaceSetHandles = faceSetHandles; + IsClosed = isClosed; + } + + public HashSet FaceSetHandles { get; } = new HashSet(); + + public bool IsClosed { get; } = false; + } + /// /// Sets best material id for current export state. /// @@ -166,15 +178,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 +259,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 +288,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 +320,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 +357,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. /// @@ -594,6 +638,71 @@ public static bool CanCreateClosedShell(Mesh mesh) return (unmatchedEdgeSz == 0); } + /// + /// Checks if the faces can create a closed shell. + /// + /// + /// Limitation: This could let through an edge shared an even number of times greater than 2. + /// + /// The collection of face handles. + /// True if can, false if can't. + /// This is an optimized version of CanCreateClosedShell(ICollection faceSet). + public static bool CanCreateClosedShell(ICollection>> faceSetIndices) + { + int numFaces = faceSetIndices.Count; + + // Do simple checks first. + if (numFaces < 4) + return false; + + // Try to match up edges. + IDictionary> unmatchedEdges = new Dictionary>(); + int unmatchedEdgeSz = 0; + + foreach (IList> face in faceSetIndices) + { + foreach (IList points in face) + { + int sizeOfBoundary = points.Count; + if (sizeOfBoundary < 3) + return false; + + for (int ii = 0; ii < sizeOfBoundary; ii++) + { + int pt1 = points[ii]; + int pt2 = points[(ii + 1) % sizeOfBoundary]; + + if (unmatchedEdges.TryGetValue(pt2, out IList unmatchedEdgesPt2) && + unmatchedEdgesPt2.Contains(pt1)) + { + unmatchedEdgesPt2.Remove(pt1); + unmatchedEdgeSz--; + } + else + { + if (unmatchedEdges.TryGetValue(pt1, out IList unmatchedEdgesPt1) && + unmatchedEdgesPt1.Contains(pt2)) + { + // An edge with the same orientation exists twice; can't create solid. + return false; + } + + if (unmatchedEdgesPt1 == null) + { + unmatchedEdgesPt1 = new List(); + unmatchedEdges[pt1] = unmatchedEdgesPt1; + } + + unmatchedEdgesPt1.Add(pt2); + unmatchedEdgeSz++; + } + } + } + } + + return (unmatchedEdgeSz == 0); + } + /// /// Checks if the faces can create a closed shell. /// @@ -670,65 +779,103 @@ public static bool CanCreateClosedShell(ICollection faceSet) return (unmatchedEdgeSz == 0); } - // This is a simplified routine for solids that are composed of planar faces with polygonal edges. This - // allows us to use the edges as the boundaries of the faces. + // This is a simplified routine for solids that are composed of planar faces with + // polygonal edges. This allows us to use the edges as the boundaries of the faces. private static bool ExportPlanarBodyIfPossible(ExporterIFC exporterIFC, Solid solid, - IList> currentFaceHashSetList, Transform lcs) + IList currentFaceHashSetList, Transform lcs) { IFCFile file = exporterIFC.GetFile(); + IList planarFaces = new List(); foreach (Face face in solid.Faces) { - if (!(face is PlanarFace)) + PlanarFace planarFace = face as PlanarFace; + if (planarFace == null) return false; - } - HashSet currentFaceSet = new HashSet(); - IDictionary vertexCache = new SortedDictionary(new GeometryUtil.XYZComparer()); + planarFaces.Add(planarFace); + } - foreach (Face face in solid.Faces) + double vertexTolerance = ExporterCacheManager.Document.Application.VertexTolerance; + IFCXYZFuzzyComparer ifcXYZFuzzyComparer = new IFCXYZFuzzyComparer(vertexTolerance); + + // Prepare polyloop data and check the polyloops for duplicate points before creating + // IFC instances to avoid unnecessary points and faces in the IFC document. + IDictionary>> polyloops = new Dictionary>>(); + ICollection>> faceSetIndices = new HashSet>>(); + foreach (PlanarFace planarFace in planarFaces) { - HashSet faceBounds = new HashSet(); - EdgeArrayArray edgeArrayArray = face.EdgeLoops; - - int edgeArraySize = edgeArrayArray.Size; - IList> edgeArrayVertices = new List>(); - - int outerEdgeArrayIndex = 0; - double maxArea = 0.0; // Only used/set if edgeArraySize > 1. - XYZ faceNormal = (face as PlanarFace).FaceNormal; + EdgeArrayArray edgeArrayArray = planarFace.EdgeLoops; + IList> edgeArrayVertices = new List>(); foreach (EdgeArray edgeArray in edgeArrayArray) { - IList vertices = new List(); - IList vertexXYZs = new List(); + ISet uniqueVertices = new SortedSet(ifcXYZFuzzyComparer); + IList vertices = new List(); foreach (Edge edge in edgeArray) { - Curve curve = edge.AsCurveFollowingFace(face); - + Curve curve = edge.AsCurveFollowingFace(planarFace); IList curvePoints = curve.Tessellate(); int numPoints = curvePoints.Count; - // Don't add last point to vertices, as this will be added in the next edge. for (int idx = 0; idx < numPoints - 1; idx++) { - if (!vertexCache.TryGetValue(curvePoints[idx], out IFCAnyHandle pointHandle)) - { - XYZ pointScaled = TransformAndScalePoint(exporterIFC, curvePoints[idx], lcs); - pointHandle = ExporterUtil.CreateCartesianPoint(file, pointScaled); - vertexCache[curvePoints[idx]] = pointHandle; - } + uniqueVertices.Add(curvePoints[idx]); + vertices.Add(curvePoints[idx]); + } + } + + // Polyloop contains duplicates or small edges + if (uniqueVertices.Count < 3) + { + return false; + } - vertices.Add(pointHandle); - vertexXYZs.Add(curvePoints[idx]); + edgeArrayVertices.Add(vertices); + } + + polyloops.Add(planarFace, edgeArrayVertices); + } + + HashSet currentFaceSet = new HashSet(); + IDictionary vertexCache = new SortedDictionary(ifcXYZFuzzyComparer); + foreach (KeyValuePair>> polyloop in polyloops) + { + IList> planarFaceIndices = new List>(); + + HashSet faceBounds = new HashSet(); + IList> edgeArrayVertices = new List>(); + XYZ faceNormal = polyloop.Key.FaceNormal; + int outerEdgeArrayIndex = 0; + int edgeArraySize = polyloop.Value.Count; + double? maxArea = null; // Only used/set if edgeArraySize > 1. + + foreach (IList edgeLoop in polyloop.Value) + { + IList edgeLoopIndices = new List(); + + IList vertices = new List(); + IList vertexXYZs = new List(); + + foreach (XYZ vertex in edgeLoop) + { + if (!vertexCache.TryGetValue(vertex, out IFCAnyHandle pointHandle)) + { + XYZ scaledPoint = TransformAndScalePoint(exporterIFC, vertex, lcs); + pointHandle = ExporterUtil.CreateCartesianPoint(file, scaledPoint); + vertexCache[vertex] = pointHandle; } + + vertices.Add(pointHandle); + edgeLoopIndices.Add(pointHandle.Id); + vertexXYZs.Add(vertex); } if (edgeArraySize > 1) { double currArea = Math.Abs(GeometryUtil.ComputePolygonalLoopArea(vertexXYZs, faceNormal, vertexXYZs[0])); - if (currArea > maxArea) + if (currArea > (maxArea ?? 0.0)) { outerEdgeArrayIndex = edgeArrayVertices.Count; maxArea = currArea; @@ -736,6 +883,7 @@ public static bool CanCreateClosedShell(ICollection faceSet) } edgeArrayVertices.Add(vertices); + planarFaceIndices.Add(edgeLoopIndices); } for (int ii = 0; ii < edgeArraySize; ii++) @@ -759,10 +907,16 @@ public static bool CanCreateClosedShell(ICollection faceSet) IFCAnyHandle currFace = IFCInstanceExporter.CreateFace(file, faceBounds); currentFaceSet.Add(currFace); } + + faceSetIndices.Add(planarFaceIndices); } if (currentFaceSet.Count > 0) - currentFaceHashSetList.Add(currentFaceSet); + { + bool canCreateClosedShell = CanCreateClosedShell(faceSetIndices); + currentFaceHashSetList.Add(new FaceSetInfo(currentFaceSet, canCreateClosedShell)); + } + return true; } @@ -1163,7 +1317,9 @@ private static bool ExportPlanarFacetsIfPossible(IFCFile file, TriangulatedShell bool sameSense, IDictionary cartesianPoints) { bool allowAdvancedCurve = !ExporterCacheManager.ExportOptionsCache.ExportAsOlderThanIFC4; - IFCAnyHandle baseCurve = GeometryUtil.CreateIFCCurveFromRevitCurve(file, exporterIFC, curve, allowAdvancedCurve, cartesianPoints, false); + const GeometryUtil.TrimCurvePreference trimCurvePreference = GeometryUtil.TrimCurvePreference.BaseCurve; + IFCAnyHandle baseCurve = GeometryUtil.CreateIFCCurveFromRevitCurve(file, + exporterIFC, curve, allowAdvancedCurve, cartesianPoints, trimCurvePreference, null); if (IFCAnyHandleUtil.IsNullOrHasNoValue(baseCurve)) return null; @@ -1176,7 +1332,12 @@ private static bool ExportPlanarFacetsIfPossible(IFCFile file, TriangulatedShell IDictionary cartesianPoints, Transform additionalTrf = null) { bool allowAdvancedCurve = !ExporterCacheManager.ExportOptionsCache.ExportAsOlderThanIFC4; - IFCAnyHandle ifcCurve = GeometryUtil.CreateIFCCurveFromRevitCurve(file, exporterIFC, curve, allowAdvancedCurve, cartesianPoints, true, additionalTrf); + const GeometryUtil.TrimCurvePreference trimCurvePreference = GeometryUtil.TrimCurvePreference.TrimmedCurve; + IFCAnyHandle ifcCurve = GeometryUtil.CreateIFCCurveFromRevitCurve(file, exporterIFC, + curve, allowAdvancedCurve, cartesianPoints, trimCurvePreference, additionalTrf); + if (IFCAnyHandleUtil.IsNullOrHasNoValue(ifcCurve)) + return null; + IFCAnyHandle sweptCurve = null; bool isBound = false; @@ -1569,9 +1730,15 @@ private static bool IsRightHanded(XYZ testPoint, XYZ axis, XYZ origin, XYZ uDeri 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 +1748,7 @@ private static bool IsRightHanded(XYZ testPoint, XYZ axis, XYZ origin, XYZ uDeri { return null; } - + foreach (var (outerLoop, loops) in sortedEdgeLoop) { if (outerLoop == null || loops == null) @@ -1720,6 +1887,10 @@ private static bool IsRightHanded(XYZ testPoint, XYZ axis, XYZ origin, XYZ uDeri IFCAnyHandle axisPosition = IFCInstanceExporter.CreateAxis1Placement(file, location, axis); IFCAnyHandle sweptCurve = CreateProfileCurveFromCurve(file, exporterIFC, profileCurve, Resources.ConicalFaceProfileCurve, cartesianPoints); + if (IFCAnyHandleUtil.IsNullOrHasNoValue(sweptCurve)) + { + return null; + } // The profile position is optional in IFC4+. surface = IFCInstanceExporter.CreateSurfaceOfRevolution(file, sweptCurve, null, axisPosition); @@ -1756,6 +1927,10 @@ private static bool IsRightHanded(XYZ testPoint, XYZ axis, XYZ origin, XYZ uDeri IFCAnyHandle axisPosition = IFCInstanceExporter.CreateAxis1Placement(file, location, axis); IFCAnyHandle sweptCurve = CreateProfileCurveFromCurve(file, exporterIFC, profileCurve, Resources.RevolvedFaceProfileCurve, cartesianPoints); + if (IFCAnyHandleUtil.IsNullOrHasNoValue(sweptCurve)) + { + return null; + } // The profile position is optional in IFC4+. surface = IFCInstanceExporter.CreateSurfaceOfRevolution(file, sweptCurve, null, axisPosition); @@ -1827,6 +2002,10 @@ private static bool IsRightHanded(XYZ testPoint, XYZ axis, XYZ origin, XYZ uDeri IFCAnyHandle direction = ExporterUtil.CreateDirection(file, dirIFC); IFCAnyHandle sweptCurve = CreateProfileCurveFromCurve(file, exporterIFC, firstProfileCurve, Resources.RuledFaceProfileCurve, cartesianPoints, basePlaneTrf.Inverse); + if (IFCAnyHandleUtil.IsNullOrHasNoValue(sweptCurve)) + { + return null; + } surface = IFCInstanceExporter.CreateSurfaceOfLinearExtrusion(file, sweptCurve, sweptCurvePosition, direction, depth); } @@ -1937,17 +2116,26 @@ private static bool IsRightHanded(XYZ testPoint, XYZ axis, XYZ origin, XYZ uDeri 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 +2161,7 @@ private static bool IsRightHanded(XYZ testPoint, XYZ axis, XYZ origin, XYZ uDeri { // 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 +2174,7 @@ private static bool IsRightHanded(XYZ testPoint, XYZ axis, XYZ origin, XYZ uDeri { 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 +2211,13 @@ private static bool IsRightHanded(XYZ testPoint, XYZ axis, XYZ origin, XYZ uDeri 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 +2238,25 @@ private static bool IsRightHanded(XYZ testPoint, XYZ axis, XYZ origin, XYZ uDeri 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); - return ExportIfcFacesAsPolygonalFaceSet(file, ifcFaceHandles, vertexCoords, isClosed, ifcColourRgbList, opacity); + IFCAnyHandle coordinatesHnd = IFCInstanceExporter.CreateCartesianPointList3D(file, vertexCoords); + IFCAnyHandle polygonalFaceSet = IFCInstanceExporter.CreatePolygonalFaceSet(file, coordinatesHnd, isClosed, ifcFaceHandles, null); + return (polygonalFaceSet, numFaces); + } + + 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 +2272,8 @@ private static bool IsRightHanded(XYZ testPoint, XYZ axis, XYZ origin, XYZ uDeri { 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 +2284,56 @@ private static bool IsRightHanded(XYZ testPoint, XYZ axis, XYZ origin, XYZ uDeri { 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 +2343,75 @@ private static bool IsRightHanded(XYZ testPoint, XYZ axis, XYZ origin, XYZ uDeri 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 +2437,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 + const bool tryToMerge = true; + if (!triMerge.SimplifyAndMergeFaces(tryToMerge)) { - triMerge.SimplifyAndMergeFaces(ignoreMerge); - } - catch - { - if (ignoreMerge) - { - return faces; - } - else - { - triMerge.Reset(); - triMerge.SimplifyAndMergeFaces(false); - } + triMerge.Reset(); + triMerge.SimplifyAndMergeFaces(!tryToMerge); } @@ -2230,27 +2473,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 +2481,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 +2610,76 @@ private static int MaximumAllowedFacets(BodyExporterOptions options) 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 }); - } - - // 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); + IList> coordList = new List>(); + IList> coordIdx = new List>(); + (coordList, coordIdx) = GetGeometryCoordinateInfo(exporterIFC, geom, lcs, options); - 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 +2692,7 @@ private static int MaximumAllowedFacets(BodyExporterOptions options) /// 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 +2708,10 @@ private static int MaximumAllowedFacets(BodyExporterOptions options) 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 +2727,117 @@ private static int MaximumAllowedFacets(BodyExporterOptions options) 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) @@ -2551,7 +2861,7 @@ private static bool AreTessellationControlsEqual(SolidOrShellTessellationControl } private static bool ExportBodyAsSolid(ExporterIFC exporterIFC, Element element, BodyExporterOptions options, - IList> currentFaceHashSetList, GeometryObject geomObject, Transform lcs) + IList currentFaceHashSetList, GeometryObject geomObject, Transform lcs) { IFCFile file = exporterIFC.GetFile(); Document document = element.Document; @@ -2575,7 +2885,8 @@ private static bool AreTessellationControlsEqual(SolidOrShellTessellationControl // 2. Try tessellationControlsOriginal, but only if they are different. // 3. Try a coarse tessellation, but only if the original wasn't coarse. bool useSolidTessellation = false; - for (int pass = 0; pass < 3; pass++) + const int finalPass = 2; + for (int pass = 0; pass <= finalPass; pass++) { SolidOrShellTessellationControls tessellationControlsToUse = null; @@ -2617,11 +2928,17 @@ private static bool AreTessellationControlsEqual(SolidOrShellTessellationControl break; } } - catch + catch (Exception ex) { - string errMsg = String.Format("TessellateSolidOrShell failed in IFC export for element \"{0}\" with id {1}", element.Name, element.Id); + string errMsg = String.Format("Pass {0}: TessellateSolidOrShell failed in IFC export for reason: \"{1}\" in element \"{2}\" with id {3}", pass, ex.Message, element.Name, element.Id); document.Application.WriteJournalComment(errMsg, false/*timestamp*/); - return false; + + // If the facetation failed, but we can try again, lets do so. + if (pass == finalPass) + { + return false; + } + continue; } } @@ -2674,7 +2991,10 @@ private static bool AreTessellationControlsEqual(SolidOrShellTessellationControl } } } - currentFaceHashSetList.Add(currentFaceSet); + + // Current assumption: if it was a Solid in Revit, don't spend the time to validate + // the result. + currentFaceHashSetList.Add(new FaceSetInfo(currentFaceSet, true)); // Call GC.KeepAlive(solidFacetation) at this point to maintain a reference to solidFacetation // and prevent the object deletion by the garbage collector after try-catch block. @@ -2698,7 +3018,7 @@ private static bool AreTessellationControlsEqual(SolidOrShellTessellationControl // Can't resetMaterials if we already have partially populated our body items with extrusions (canExportSolidModelRep) int numExtrusions = bodyItems.Count; bool resetMaterials = (numExtrusions == 0); - IList> currentFaceHashSetList = new List>(); + IList currentFaceHashSetList = new List(); IList startIndexForObject = new List(); BodyData bodyData = BodyData.Create(bodyDataIn, resetMaterials); @@ -2752,11 +3072,13 @@ private static bool AreTessellationControlsEqual(SolidOrShellTessellationControl // First, see if this could be represented as a simple swept solid. if (exportAsBReps && (currAnalyzer != null)) { - SweptSolidExporter sweptSolidExporter = SweptSolidExporter.Create(exporterIFC, element, currAnalyzer, geomObject); + SweptSolidExporter sweptSolidExporter = SweptSolidExporter.Create(exporterIFC, element, currAnalyzer, + geomObject, GenerateAdditionalInfo.GenerateBody, isCoarse); HashSet facetHnds = sweptSolidExporter?.Facets; if (facetHnds != null && facetHnds.Count != 0) { - currentFaceHashSetList.Add(facetHnds); + // Current assumption: SweptSolidExporter produces valid solid geometry. + currentFaceHashSetList.Add(new FaceSetInfo(facetHnds, true)); alreadyExported = true; GraphicsStyle style = document.GetElement(geomObject.GraphicsStyleId) as GraphicsStyle; bodyData.AddRepresentationItemInfo(document, style, materialId, sweptSolidExporter.RepresentationItem); @@ -2782,7 +3104,7 @@ private static bool AreTessellationControlsEqual(SolidOrShellTessellationControl Transform trfToUse = null; if (instanceGeometry) trfToUse = GeometryUtil.GetScaledTransform(exporterIFC); - else if (!instanceGeometry && element.AssemblyInstanceId != ElementId.InvalidElementId) + else if (!instanceGeometry && ExporterUtil.IsContainedInAssembly(element)) trfToUse = Transform.Identity; // If we are using the Reference View, try a triangulated face set. @@ -2791,7 +3113,7 @@ private static bool AreTessellationControlsEqual(SolidOrShellTessellationControl if (!alreadyExported && canExportAsTessellatedFaceSet) { 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) @@ -2831,9 +3153,13 @@ private static bool AreTessellationControlsEqual(SolidOrShellTessellationControl if (currentFaceSet.Count == 0) continue; + // Default is true until we see that it is false. This generally maintains + // current behavior, but probably needs to be improved. + bool canExportAsClosedShell = true; + if (exportAsBReps) { - bool canExportAsClosedShell = (currentFaceSet.Count >= 4); + canExportAsClosedShell = (currentFaceSet.Count >= 4); if (canExportAsClosedShell) { if ((geomObject is Mesh) && (numBReps == 1)) @@ -2878,7 +3204,7 @@ private static bool AreTessellationControlsEqual(SolidOrShellTessellationControl } } - currentFaceHashSetList.Add(new HashSet(currentFaceSet)); + currentFaceHashSetList.Add(new FaceSetInfo(new HashSet(currentFaceSet), canExportAsClosedShell)); } } } @@ -2906,11 +3232,19 @@ private static bool AreTessellationControlsEqual(SolidOrShellTessellationControl } else { - startIndexForObject.Add(currentFaceHashSetList.Count); // end index for last object. + int size = currentFaceHashSetList.Count; + startIndexForObject.Add(size); // end index for last object. + + for (int ii = 0; ii < size && exportAsBReps; ii++) + { + if (!currentFaceHashSetList[ii].IsClosed) + { + exportAsBReps = false; + } + } IList repMapItems = new List(); - int size = currentFaceHashSetList.Count; if (exportAsBReps) { int brepIndex = -1; @@ -2924,14 +3258,13 @@ private static bool AreTessellationControlsEqual(SolidOrShellTessellationControl currMatId = materialIds[brepIndex]; currStyle = document.GetElement(splitGeometryList[brepIndex].GraphicsStyleId) as GraphicsStyle; } - HashSet currentFaceHashSet = currentFaceHashSetList[ii]; + HashSet currentFaceHashSet = currentFaceHashSetList[ii].FaceSetHandles; IFCAnyHandle faceOuter = IFCInstanceExporter.CreateClosedShell(file, currentFaceHashSet); - IFCAnyHandle brepHnd = RepresentationUtil.CreateFacetedBRep(exporterIFC, document, - options.CreatingVoid, faceOuter, currMatId); + IFCAnyHandle brepHnd = RepresentationUtil.CreateFacetedBRep(exporterIFC, + document, options.CreatingVoid, faceOuter, currMatId); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(brepHnd)) + if (bodyItems.AddIfNotNull(brepHnd)) { - bodyItems.Add(brepHnd); bodyData.AddRepresentationItemInfo(document, currStyle, currMatId, brepHnd); } } @@ -2942,7 +3275,7 @@ private static bool AreTessellationControlsEqual(SolidOrShellTessellationControl int matToUse = -1; for (int ii = 0; ii < size; ii++) { - HashSet currentFaceHashSet = currentFaceHashSetList[ii]; + HashSet currentFaceHashSet = currentFaceHashSetList[ii].FaceSetHandles; if (startIndexForObject[matToUse + 1] == ii) matToUse++; @@ -3113,6 +3446,7 @@ public override int GetHashCode() // 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 +3708,8 @@ public override int GetHashCode() 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 +3745,7 @@ public override int GetHashCode() 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 +3809,12 @@ public override int GetHashCode() 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 +4087,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..320d4408 100644 --- a/Source/Revit.IFC.Export/Exporter/CeilingExporter.cs +++ b/Source/Revit.IFC.Export/Exporter/CeilingExporter.cs @@ -50,7 +50,7 @@ class CeilingExporter /// public static void ExportCeilingElement(ExporterIFC exporterIFC, Ceiling ceiling, ref GeometryElement geomElement, ProductWrapper productWrapper) { - string ifcEnumType = ExporterUtil.GetIFCTypeFromExportTable(exporterIFC, ceiling); + string ifcEnumType = ExporterUtil.GetIFCTypeFromExportTable(ceiling); string pdefFromParam = ExporterUtil.GetExportTypeFromTypeParameter(ceiling, null); if (!String.IsNullOrEmpty(pdefFromParam)) ifcEnumType = pdefFromParam; @@ -81,7 +81,7 @@ public static void ExportCovering(ExporterIFC exporterIFC, Element element, ref // For IFC4RV export, Element will be split into its parts(temporarily) in order to export the wall by its parts // If Parts are created by code and not by user then their name should be equal to Material name. bool setMaterialNameToPartName = ExporterUtil.CreateParts(element, layersetInfo.MaterialIds.Count, ref geomElem); - ExporterUtil.ExportPartAs exportPartAs = ExporterUtil.CanExportByComponentsOrParts(element); + ExporterUtil.ExportPartAs exportPartAs = ExporterUtil.CanExportByComponentsOrParts(element, ref geomElem); bool exportByComponents = exportPartAs == ExporterUtil.ExportPartAs.ShapeAspect; bool exportParts = exportPartAs == ExporterUtil.ExportPartAs.Part; @@ -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..a09742ec 100644 --- a/Source/Revit.IFC.Export/Exporter/CurtainSystemExporter.cs +++ b/Source/Revit.IFC.Export/Exporter/CurtainSystemExporter.cs @@ -33,21 +33,12 @@ namespace Revit.IFC.Export.Exporter /// class CurtainSystemExporter { - /// - /// Exports curtain object as container. + /// Exports a curtain object as container. /// - /// - /// Collection of elements contained in the host curtain element. - /// - /// - /// The curtain wall element. - /// - /// - /// The ExporterIFC object. - /// - /// - /// The ProductWrapper. - /// + /// Collection of elements contained in the host curtain element. + /// The curtain system element. + /// The ExporterIFC object. + /// The ProductWrapper. public static void ExportCurtainObjectCommonAsContainer(ICollection allSubElements, Element wallElement, ExporterIFC exporterIFC, ProductWrapper origWrapper, PlacementSetter currSetter) { @@ -74,7 +65,7 @@ class CurtainSystemExporter alreadyVisited.Add(subElem.Id); // Respect element visibility settings. - if (!ElementFilteringUtil.CanExportElement(exporterIFC, subElem, false) || !ElementFilteringUtil.IsElementVisible(subElem)) + if (!ElementFilteringUtil.CanExportElement(subElem, false) || !ElementFilteringUtil.IsElementVisible(subElem)) continue; GeometryElement geomElem = subElem.get_Geometry(geomOptions); @@ -85,62 +76,13 @@ class CurtainSystemExporter { 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) - { - if (ExporterCacheManager.ExportOptionsCache.ExportAs2x2) - ProxyElementExporter.Export(exporterIFC, subElem, geomElem, productWrapper, exportType); - else - { - IFCAnyHandle currLocalPlacement = currSetter.LocalPlacement; - - if (exportType.ExportInstance == IFCEntityType.IfcCurtainWall) - { - // 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"); - } - - FamilyInstanceExporter.ExportFamilyInstanceAsMappedItem(exporterIFC, subElem as Mullion, exportType, exportType.ValidatedPredefinedType, productWrapper, - ElementId.InvalidElementId, null, currLocalPlacement); - } - } - else + IFCAnyHandle currLocalPlacement = currSetter.LocalPlacement; + using (IFCExportBodyParams extraParams = new IFCExportBodyParams()) { - FamilyInstance subFamInst = subElem as FamilyInstance; - - if (exportType.ExportInstance == IFCEntityType.IfcCurtainWall) - { - // 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. - ElementId catId = CategoryUtil.GetSafeCategoryId(subElem); - exportType = ElementFilteringUtil.GetExportTypeFromCategoryId(catId); - } - - - if (ExporterCacheManager.ExportOptionsCache.ExportAs2x2) - { - if ((exportType.ExportInstance == IFCEntityType.UnKnown) || - (exportType.ExportInstance == IFCEntityType.IfcPlate) || - (exportType.ExportInstance == IFCEntityType.IfcMember)) - exportType.SetValueWithPair(IFCEntityType.IfcBuildingElementProxy, ifcEnumType); - } - else - { - if (exportType.ExportInstance == IFCEntityType.UnKnown) - { - exportType.SetValueWithPair(IFCEntityType.IfcPlateType, "CURTAIN_PANEL"); - } - } - - IFCAnyHandle currLocalPlacement = currSetter.LocalPlacement; - using (IFCExportBodyParams extraParams = new IFCExportBodyParams()) - { - FamilyInstanceExporter.ExportFamilyInstanceAsMappedItem(exporterIFC, subFamInst, exportType, ifcEnumType, productWrapper, - ElementId.InvalidElementId, null, currLocalPlacement); - } + FamilyInstanceExporter.ExportFamilyInstanceAsMappedItem(exporterIFC, + subElem as FamilyInstance, exportType, productWrapper, ElementId.InvalidElementId, null, currLocalPlacement); } } else if (subElem is CurtainGridLine) @@ -164,26 +106,13 @@ class CurtainSystemExporter /// /// Exports curtain object as one Brep. /// - /// - /// Collection of elements contained in the host curtain element. - /// - /// - /// The curtain wall element. - /// - /// - /// The ExporterIFC object. - /// - /// - /// The PlacementSetter object. - /// - /// - /// The local placement handle. - /// - /// - /// The handle. - /// - public static IFCAnyHandle ExportCurtainObjectCommonAsOneBRep(ICollection allSubElements, Element wallElement, - ExporterIFC exporterIFC) + /// Collection of elements contained in the host curtain element. + /// The curtain wall element. + /// The ExporterIFC object. + /// The PlacementSetter object. + /// The local placement handle. + public static void ExportCurtainObjectAsOneEntity(IFCAnyHandle parentHnd, + ICollection allSubElements, Element wallElement, ExporterIFC exporterIFC) { IFCAnyHandle prodDefRep = null; Document document = wallElement.Document; @@ -205,12 +134,11 @@ class CurtainSystemExporter { Element subElem = wallElement.Document.GetElement(subElemId); GeometryElement geomElem = subElem.get_Geometry(geomOptions); - if (geomElem == null) - continue; - - if (alreadyVisited.Contains(subElem.Id)) + if (geomElem == null || alreadyVisited.Contains(subElemId)) + { continue; - alreadyVisited.Add(subElem.Id); + } + alreadyVisited.Add(subElemId); // Export tessellated geometry when IFC4 Reference View is selected @@ -221,7 +149,9 @@ class CurtainSystemExporter if (triFaceSet != null && triFaceSet.Count > 0) { foreach (IFCAnyHandle triFaceSetItem in triFaceSet) + { bodyItems.Add(triFaceSetItem); + } useFallbackBREP = false; // no need to do Brep since it is successful } } @@ -229,9 +159,8 @@ class CurtainSystemExporter else if (ExporterCacheManager.ExportOptionsCache.ExportAs4DesignTransferView) { IFCAnyHandle advancedBRep = BodyExporter.ExportBodyAsAdvancedBrep(exporterIFC, subElem, geomElem); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(advancedBRep)) + if (bodyItems.AddIfNotNull(advancedBRep)) { - bodyItems.Add(advancedBRep); useFallbackBREP = false; // no need to do Brep since it is successful } } @@ -243,64 +172,67 @@ class CurtainSystemExporter IFCAnyHandle outer = IFCInstanceExporter.CreateClosedShell(file, faces); if (!IFCAnyHandleUtil.IsNullOrHasNoValue(outer)) - bodyItems.Add(RepresentationUtil.CreateFacetedBRep(exporterIFC, document, + { + bodyItems.Add(RepresentationUtil.CreateFacetedBRep(exporterIFC, document, false, outer, ElementId.InvalidElementId)); + } } } if (bodyItems.Count == 0) - return prodDefRep; + { + return; + } ElementId catId = CategoryUtil.GetSafeCategoryId(wallElement); IFCAnyHandle shapeRep; // Use tessellated geometry in Reference View if ((ExporterCacheManager.ExportOptionsCache.ExportAs4ReferenceView || ExporterCacheManager.ExportOptionsCache.ExportAs4General) && !useFallbackBREP) + { shapeRep = RepresentationUtil.CreateTessellatedRep(exporterIFC, wallElement, catId, contextOfItems, bodyItems, null); + } else if (ExporterCacheManager.ExportOptionsCache.ExportAs4DesignTransferView && !useFallbackBREP) + { shapeRep = RepresentationUtil.CreateAdvancedBRepRep(exporterIFC, wallElement, catId, contextOfItems, bodyItems, null); + } else + { shapeRep = RepresentationUtil.CreateBRepRep(exporterIFC, wallElement, catId, contextOfItems, bodyItems); + } if (IFCAnyHandleUtil.IsNullOrHasNoValue(shapeRep)) - return prodDefRep; + { + return; + } - IList shapeReps = new List(); - shapeReps.Add(shapeRep); + IList shapeReps = new List() { shapeRep }; IFCAnyHandle boundingBoxRep = BoundingBoxExporter.ExportBoundingBox(exporterIFC, wallElement.get_Geometry(geomOptions), Transform.Identity); if (boundingBoxRep != null) + { shapeReps.Add(boundingBoxRep); + } prodDefRep = IFCInstanceExporter.CreateProductDefinitionShape(file, null, null, shapeReps); - return prodDefRep; + IFCAnyHandleUtil.SetAttribute(parentHnd, "Representation", prodDefRep); } /// /// Checks if the curtain element can be exported as container. /// - /// - /// It checks if all sub elements to be exported have geometries. - /// - /// - /// Collection of elements contained in the host curtain element. - /// - /// - /// The Revit document. - /// - /// - /// True if it can be exported as container, false otherwise. - /// + /// It checks if all sub elements to be exported have geometries. + /// Collection of elements contained in the host curtain element. + /// The Revit document. + /// True if it can be exported as container, false otherwise. private static bool CanExportCurtainWallAsContainer(ICollection allSubElements, Document document) { Options geomOptions = GeometryUtil.GetIFCExportGeometryOptions(); 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); @@ -322,16 +254,17 @@ private static bool CanExportCurtainWallAsContainer(ICollection allSu /// Collection of elements contained in the host curtain element. /// The element to be exported. /// The ProductWrapper. - private static void ExportBase(ExporterIFC exporterIFC, ICollection allSubElements, Element element, ProductWrapper wrapper) + private static void ExportBase(ExporterIFC exporterIFC, ICollection allSubElements, Element element, + ProductWrapper wrapper) { - Common.Enums.IFCEntityType elementClassTypeEnum = Common.Enums.IFCEntityType.IfcRoof; - if (element is Wall || element is CurtainSystem || IsLegacyCurtainElement(element)) - elementClassTypeEnum = Common.Enums.IFCEntityType.IfcCurtainWall; - else if (element is RoofBase) - elementClassTypeEnum = Common.Enums.IFCEntityType.IfcRoof; + string ifcEnumType; + IFCExportInfoPair exportType = ExporterUtil.GetProductExportType(exporterIFC, element, out ifcEnumType); - if (ExporterCacheManager.ExportOptionsCache.IsElementInExcludeList(elementClassTypeEnum)) + if (exportType.IsUnKnown || + ExporterCacheManager.ExportOptionsCache.IsElementInExcludeList(exportType.ExportInstance)) + { return; + } IFCFile file = exporterIFC.GetFile(); IFCAnyHandle ownerHistory = ExporterCacheManager.OwnerHistoryHandle; @@ -355,23 +288,9 @@ private static void ExportBase(ExporterIFC exporterIFC, ICollection a string objectType = NamingUtil.CreateIFCObjectName(exporterIFC, element); IFCAnyHandle prodRepHnd = null; - IFCAnyHandle elemHnd = null; string elemGUID = GUIDUtil.CreateGUID(element); - if (element is Wall || element is CurtainSystem || IsLegacyCurtainElement(element)) - { - elemHnd = IFCInstanceExporter.CreateCurtainWall(exporterIFC, element, elemGUID, ownerHistory, localPlacement, prodRepHnd, null); - } - else if (element is RoofBase) - { - //need to convert the string to enum - string ifcEnumType = ExporterUtil.GetIFCTypeFromExportTable(exporterIFC, element); - //ifcEnumType = IFCValidateEntry.GetValidIFCPredefinedType(element, ifcEnumType); - elemHnd = IFCInstanceExporter.CreateRoof(exporterIFC, element, elemGUID, ownerHistory, localPlacement, prodRepHnd, ifcEnumType); - } - else - { - return; - } + IFCAnyHandle elemHnd = IFCInstanceExporter.CreateGenericIFCEntity(exportType, + exporterIFC, element, elemGUID, ownerHistory, localPlacement, prodRepHnd); if (IFCAnyHandleUtil.IsNullOrHasNoValue(elemHnd)) return; @@ -379,12 +298,9 @@ private static void ExportBase(ExporterIFC exporterIFC, ICollection a wrapper.AddElement(element, elemHnd, setter, null, true, null); bool canExportCurtainWallAsContainer = CanExportCurtainWallAsContainer(allSubElements, element.Document); - IFCAnyHandle rep = null; if (!canExportCurtainWallAsContainer) { - rep = ExportCurtainObjectCommonAsOneBRep(allSubElements, element, exporterIFC); - if (IFCAnyHandleUtil.IsNullOrHasNoValue(rep)) - return; + ExportCurtainObjectAsOneEntity(elemHnd, allSubElements, element, exporterIFC); } else { @@ -490,16 +406,20 @@ private static void ExportBase(ExporterIFC exporterIFC, ICollection a { // Don't export the Curtain Wall itself, which has no useful geometry; instead export all of the GReps of the // mullions and panels. - CurtainGridSet gridSet = CurtainSystemExporter.GetCurtainGridSet(hostElement); + CurtainGridSet gridSet = GetCurtainGridSet(hostElement); 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 +525,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..6412a9d9 100644 --- a/Source/Revit.IFC.Export/Exporter/CurveElementExporter.cs +++ b/Source/Revit.IFC.Export/Exporter/CurveElementExporter.cs @@ -72,144 +72,140 @@ private static bool ShouldCurveElementBeExported(CurveElement curveElement) return true; } - /// - /// Exports a curve element to IFC curve annotation. - /// - /// - /// The ExporterIFC object. - /// - /// - /// The curve element to be exported. - /// - /// - /// The geometry element. - /// - /// - /// The ProductWrapper. - /// - public static void ExportCurveElement(ExporterIFC exporterIFC, CurveElement curveElement, GeometryElement geometryElement, - ProductWrapper productWrapper) + private static void ExportCurveBasedElementCommon(ExporterIFC exporterIFC, Element element, + GeometryElement geometryElement, ProductWrapper productWrapper, SketchPlane sketchPlane) { - if (geometryElement == null || !ShouldCurveElementBeExported(curveElement)) - return; - - SketchPlane sketchPlane = curveElement.SketchPlane; - if (sketchPlane == null) - return; + string ifcEnumType = null; + IFCExportInfoPair exportType = + ExporterUtil.GetProductExportType(exporterIFC, element, out ifcEnumType); // Check the intended IFC entity or type name is in the exclude list specified in the UI - IFCEntityType elementClassTypeEnum = IFCEntityType.IfcAnnotation; - if (ExporterCacheManager.ExportOptionsCache.IsElementInExcludeList(elementClassTypeEnum)) + if (ExporterCacheManager.ExportOptionsCache.IsElementInExcludeList(exportType.ExportInstance)) return; - ElementId categoryId = CategoryUtil.GetSafeCategoryId(curveElement); - - string ifcEnumType = null; - if (ExporterCacheManager.ExportOptionsCache.ExportAs4x3) - { - // We only support IfcAnnotation for curves. But if we are exporting to IFC4x3, - // and the user has supplued a predefined type for the IfcAnnotation, we will use it. - IFCExportInfoPair exportType = - ExporterUtil.GetProductExportType(exporterIFC, curveElement, out ifcEnumType); - if (exportType.ExportInstance != IFCEntityType.IfcAnnotation) - ifcEnumType = null; - } + ElementId categoryId = CategoryUtil.GetSafeCategoryId(element); + ElementId sketchPlaneId = sketchPlane?.Id ?? ElementId.InvalidElementId; + // If we are exporting an IfcAnnotation, we will do a little extra work to get the local placement close + // to the sketch plane origin, if there is a sketch plane. We could also do this in the generic case, + // but for now just keeping the existing IfcAnnotation code more or less the same. + bool exportingAnnotation = exportType.ExportInstance == IFCEntityType.IfcAnnotation; IFCFile file = exporterIFC.GetFile(); using (IFCTransaction transaction = new IFCTransaction(file)) { // Check for containment override IFCAnyHandle overrideContainerHnd = null; - ElementId overrideContainerId = ParameterUtil.OverrideContainmentParameter(exporterIFC, curveElement, out overrideContainerHnd); + ElementId overrideContainerId = ParameterUtil.OverrideContainmentParameter(exporterIFC, element, out overrideContainerHnd); - using (PlacementSetter setter = PlacementSetter.Create(exporterIFC, curveElement, null, null, overrideContainerId, overrideContainerHnd)) + using (PlacementSetter setter = PlacementSetter.Create(exporterIFC, element, null, null, overrideContainerId, overrideContainerHnd)) { IFCAnyHandle localPlacement = setter.LocalPlacement; - IFCAnyHandle axisPlacement = GeometryUtil.GetRelativePlacementFromLocalPlacement(localPlacement); - - Plane planeSK = sketchPlane.GetPlane(); - XYZ projDir = planeSK.Normal; - XYZ origin = planeSK.Origin; - bool useOffsetTrf = false; - if (projDir.IsAlmostEqualTo(XYZ.BasisZ)) + + bool allowAdvancedCurve = !ExporterCacheManager.ExportOptionsCache.ExportAsOlderThanIFC4; + const GeometryUtil.TrimCurvePreference trimCurvePreference = GeometryUtil.TrimCurvePreference.UsePolyLineOrTrim; + + IList curves = new List(); + List curvesFromGeomElem = + GeometryUtil.GetCurvesFromGeometryElement(geometryElement); + foreach (Curve curve in curvesFromGeomElem) { - XYZ offset = XYZ.BasisZ * setter.Offset; - origin -= offset; + curves.AddIfNotNull(GeometryUtil.CreateIFCCurveFromRevitCurve(file, + exporterIFC, curve, allowAdvancedCurve, null, trimCurvePreference, null)); } - else - useOffsetTrf = true; - Transform curveLCS = GeometryUtil.CreateTransformFromPlane(planeSK); - curveLCS.Origin = origin; + HashSet curveSet = new HashSet(curves); + IFCAnyHandle repItemHnd = IFCInstanceExporter.CreateGeometricCurveSet(file, curveSet); - IList curves = null; + IFCAnyHandle curveStyle = file.CreateStyle(exporterIFC, repItemHnd); - if (ExporterCacheManager.ExportOptionsCache.ExportAs4ReferenceView) + if (exportingAnnotation) { - Transform trf = null; - if (useOffsetTrf) + CurveAnnotationCache annotationCache = ExporterCacheManager.CurveAnnotationCache; + IFCAnyHandle curveAnno = annotationCache.GetAnnotation(sketchPlaneId, curveStyle); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(curveAnno)) { - XYZ offsetOrig = -XYZ.BasisZ * setter.Offset; - trf = Transform.CreateTranslation(offsetOrig); + AddCurvesToAnnotation(curveAnno, curves); } - - curves = new List(); - //Curve curve = (geometryElement as GeometryObject) as Curve; - List curvesFromGeomElem = GeometryUtil.GetCurvesFromGeometryElement(geometryElement); - foreach (Curve curve in curvesFromGeomElem) + else { - IFCAnyHandle curveHnd = GeometryUtil.CreatePolyCurveFromCurve(exporterIFC, curve, trf); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(curveHnd)) - curves.Add(curveHnd); + curveAnno = CreateCurveAnnotation(exporterIFC, element, + categoryId, Transform.Identity, setter, + localPlacement, repItemHnd, ifcEnumType); + productWrapper.AddAnnotation(curveAnno, setter.LevelInfo, true); + + annotationCache.AddAnnotation(sketchPlaneId, curveStyle, curveAnno); } } else { - IFCGeometryInfo info = IFCGeometryInfo.CreateCurveGeometryInfo(exporterIFC, curveLCS, projDir, false); + string guid = GUIDUtil.CreateGUID(element); + IFCAnyHandle productHandle = CreateAnnotationProductRepresentation(exporterIFC, + file, element, categoryId, repItemHnd); + IFCAnyHandle curveHandle = IFCInstanceExporter.CreateGenericIFCEntity(exportType, + exporterIFC, element, guid, ExporterCacheManager.OwnerHistoryHandle, + localPlacement, productHandle); + productWrapper.AddElement(element, curveHandle, setter.LevelInfo, null, true, exportType); + } + } + transaction.Commit(); + } + } - if (useOffsetTrf) - { - XYZ offsetOrig = -XYZ.BasisZ * setter.Offset; - Transform trf = Transform.CreateTranslation(offsetOrig); - ExporterIFCUtils.CollectGeometryInfo(exporterIFC, info, geometryElement, XYZ.Zero, false, trf); - } - else - { - ExporterIFCUtils.CollectGeometryInfo(exporterIFC, info, geometryElement, XYZ.Zero, false); - } + /// + /// Exports a curve element to the appropriate IFC entity. + /// + /// The ExporterIFC object. + /// The curve element to be exported. + /// The geometry element. + /// The ProductWrapper. + public static void ExportCurveElement(ExporterIFC exporterIFC, CurveElement curveElement, + GeometryElement geometryElement, ProductWrapper productWrapper) + { + if (geometryElement == null || !ShouldCurveElementBeExported(curveElement)) + return; - curves = info.GetCurves(); - } + SketchPlane sketchPlane = curveElement.SketchPlane; + if (sketchPlane == null) + return; - if (curves.Count != 1) - { - throw new Exception("IFC: expected 1 curve when export curve element."); - } + ExportCurveBasedElementCommon(exporterIFC, curveElement, geometryElement, productWrapper, sketchPlane); + } - HashSet curveSet = new HashSet(curves); - IFCAnyHandle repItemHnd = IFCInstanceExporter.CreateGeometricCurveSet(file, curveSet); + /// + /// Exports a site property line element to the appropriate IFC entity. + /// + /// The ExporterIFC object. + /// The site property line element to be exported. + /// The geometry element. + /// The ProductWrapper. + public static void ExportPropertyLineElement(ExporterIFC exporterIFC, PropertyLine propertyLine, + GeometryElement geometryElement, ProductWrapper productWrapper) + { + if (geometryElement == null) + return; - IFCAnyHandle curveStyle = file.CreateStyle(exporterIFC, repItemHnd); + ExportCurveBasedElementCommon(exporterIFC, propertyLine, geometryElement, productWrapper, null); + } - CurveAnnotationCache annotationCache = ExporterCacheManager.CurveAnnotationCache; - IFCAnyHandle curveAnno = annotationCache.GetAnnotation(sketchPlane.Id, curveStyle); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(curveAnno)) - { - AddCurvesToAnnotation(curveAnno, curves); - } - else - { - curveAnno = CreateCurveAnnotation(exporterIFC, curveElement, - categoryId, sketchPlane.Id, curveLCS, curveStyle, setter, - localPlacement, repItemHnd, ifcEnumType); - productWrapper.AddAnnotation(curveAnno, setter.LevelInfo, true); + static IFCAnyHandle CreateAnnotationProductRepresentation(ExporterIFC exporterIFC, + IFCFile file, Element curveElement, ElementId categoryId, IFCAnyHandle repItemHnd) + { + HashSet bodyItems = new HashSet() { repItemHnd }; + IFCAnyHandle contextOfItems = + ExporterCacheManager.GetOrCreate3DContextHandle(exporterIFC, IFCRepresentationIdentifier.Annotation); - annotationCache.AddAnnotation(sketchPlane.Id, curveStyle, curveAnno); - } - } - transaction.Commit(); - } + // Property lines are 2D plan view objects in Revit, so they should stay as such. + bool is3D = !(curveElement is PropertyLine); + IFCAnyHandle bodyRepHnd = RepresentationUtil.CreateAnnotationSetRep(exporterIFC, + curveElement, categoryId, contextOfItems, bodyItems, is3D); + + if (IFCAnyHandleUtil.IsNullOrHasNoValue(bodyRepHnd)) + throw new Exception("Failed to create shape representation."); + + List shapes = new List() { bodyRepHnd }; + + return IFCInstanceExporter.CreateProductDefinitionShape(file, null, null, shapes); } /// @@ -226,24 +222,13 @@ private static bool ShouldCurveElementBeExported(CurveElement curveElement) /// The representation item. /// The handle. static IFCAnyHandle CreateCurveAnnotation(ExporterIFC exporterIFC, Element curveElement, - ElementId categoryId, ElementId sketchPlaneId, Transform curveLCS, - IFCAnyHandle curveStyle, PlacementSetter placementSetter, IFCAnyHandle localPlacement, + ElementId categoryId, Transform curveLCS, + PlacementSetter placementSetter, IFCAnyHandle localPlacement, IFCAnyHandle repItemHnd, string predefinedType) { - HashSet bodyItems = new HashSet() { repItemHnd }; - IFCAnyHandle contextOfItems = - ExporterCacheManager.GetOrCreate3DContextHandle(exporterIFC, IFCRepresentationIdentifier.Annotation); - - IFCAnyHandle bodyRepHnd = RepresentationUtil.CreateAnnotationSetRep(exporterIFC, - curveElement, categoryId, contextOfItems, bodyItems); - - if (IFCAnyHandleUtil.IsNullOrHasNoValue(bodyRepHnd)) - throw new Exception("Failed to create shape representation."); - - List shapes = new List() { bodyRepHnd }; - IFCFile file = exporterIFC.GetFile(); - IFCAnyHandle prodShapeHnd = IFCInstanceExporter.CreateProductDefinitionShape(file, null, null, shapes); + IFCAnyHandle prodShapeHnd = CreateAnnotationProductRepresentation(exporterIFC, file, + curveElement, categoryId, repItemHnd); XYZ xDir = curveLCS.BasisX; XYZ zDir = curveLCS.BasisZ; XYZ origin = curveLCS.Origin; @@ -272,6 +257,9 @@ private static bool ShouldCurveElementBeExported(CurveElement curveElement) /// The curves. static void AddCurvesToAnnotation(IFCAnyHandle annotation, IList curves) { + if ((curves?.Count ?? 0) == 0) + return; + IFCAnyHandleUtil.ValidateSubTypeOf(annotation, false, IFCEntityType.IfcAnnotation); IFCAnyHandle prodShapeHnd = IFCAnyHandleUtil.GetRepresentation(annotation); @@ -291,7 +279,10 @@ static void AddCurvesToAnnotation(IFCAnyHandle annotation, IList c throw new InvalidOperationException("Expected GeometricSet for IfcAnnotation."); HashSet newElements = IFCAnyHandleUtil.GetAggregateInstanceAttribute>(repItemHnd, "Elements"); - newElements.Add(curves[0]); + foreach (IFCAnyHandle curve in curves) + { + newElements.Add(curve); + } IFCAnyHandleUtil.SetAttribute(repItemHnd, "Elements", newElements); } } diff --git a/Source/Revit.IFC.Export/Exporter/Exporter.cs b/Source/Revit.IFC.Export/Exporter/Exporter.cs index 27732f69..615e7659 100644 --- a/Source/Revit.IFC.Export/Exporter/Exporter.cs +++ b/Source/Revit.IFC.Export/Exporter/Exporter.cs @@ -38,6 +38,7 @@ using Revit.IFC.Export.Properties; using System.Reflection; using Autodesk.Revit.DB.Steel; +using Autodesk.Revit.DB.Analysis; namespace Revit.IFC.Export.Exporter { @@ -181,6 +182,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); }; @@ -296,7 +299,7 @@ protected void ExportAdvanceSteelElements(ExporterIFC exporterIFC, Autodesk.Revi if (File.Exists(Path.GetDirectoryName(dllPath) + @"\Autodesk.SteelConnections.ASIFC.dll")) assembly = Assembly.LoadFrom(Path.GetDirectoryName(dllPath) + @"\Autodesk.SteelConnections.ASIFC.dll"); else - assembly = Assembly.LoadFrom(AppDomain.CurrentDomain.BaseDirectory + @"\Addins\SteelConnections\Autodesk.SteelConnections.ASIFC.dll"); + assembly = Assembly.LoadFrom(Path.Combine(AppContext.BaseDirectory, @"Addins\SteelConnections\Autodesk.SteelConnections.ASIFC.dll")); if (assembly != null) { @@ -378,6 +381,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,11 +427,10 @@ 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; + IFCAnyHandle facilityPlacement = CreateBuildingPlacement(exporterIFC.GetFile()); + IFCAnyHandle facilityHnd = CreateFacilityFromProjectInfo(exporterIFC, document, facilityPlacement, true); } ExportOptionsCache exportOptionsCache = ExporterCacheManager.ExportOptionsCache; @@ -655,7 +672,7 @@ protected virtual bool CanExportElement(ExporterIFC exporterIFC, Autodesk.Revit. { } } - return ElementFilteringUtil.CanExportElement(exporterIFC, element, false); + return ElementFilteringUtil.CanExportElement(element, false); } /// @@ -691,11 +708,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); } @@ -917,6 +929,11 @@ public virtual void ExportElementImpl(ExporterIFC exporterIFC, Element element, PipeInsulation pipeInsulation = element as PipeInsulation; PipeInsulationExporter.ExportPipeInsulation(exporterIFC, pipeInsulation, geomElem, productWrapper); } + else if (element is PropertyLine) + { + PropertyLine propertyLine = element as PropertyLine; + CurveElementExporter.ExportPropertyLineElement(exporterIFC, propertyLine, geomElem, productWrapper); + } else if (element is Railing) { if (ExporterCacheManager.RailingCache.Contains(element.Id)) @@ -1022,7 +1039,7 @@ public virtual void ExportElementImpl(ExporterIFC exporterIFC, Element element, // to have this check before the (element is HostObject check. exported = ProxyElementExporter.Export(exporterIFC, element, geomElem, productWrapper, exportType); } - else if (elementIsFabricationPart || (element is HostObject) || (element is DirectShape)) + else if (elementIsFabricationPart || (element is HostObject) || (element is DirectShape) || (element is MassLevelData)) { exported = GenericElementExporter.ExportElement(exporterIFC, element, geomElem, productWrapper); } @@ -1031,10 +1048,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); + } } } @@ -1184,6 +1205,42 @@ private void BeginLinkedDocumentExport(ExporterIFC exporterIFC, Document documen BeginDocumentExportCommon(exporterIFC, document); } + private IFCAnyHandle CreateFacilityPart(ExporterIFC exporterIFC, Level level, string objectType, + IFCAnyHandle objectPlacement, IFCElementComposition compositionType, double elevation, string predefinedType) + { + IFCAnyHandle ownerHistory = ExporterCacheManager.OwnerHistoryHandle; + + if (!ExporterCacheManager.ExportOptionsCache.ExportAsOlderThanIFC4x3) + { + switch (ExporterCacheManager.ExportOptionsCache.FacilityType) + { + case KnownFacilityTypes.Bridge: + { + return IFCInstanceExporter.CreateBridgePart(exporterIFC, level, ownerHistory, objectType, + objectPlacement, compositionType, predefinedType); + } + case KnownFacilityTypes.MarineFacility: + { + return IFCInstanceExporter.CreateMarinePart(exporterIFC, level, ownerHistory, objectType, + objectPlacement, compositionType); + } + case KnownFacilityTypes.Railway: + { + return IFCInstanceExporter.CreateRailwayPart(exporterIFC, level, ownerHistory, objectType, + objectPlacement, compositionType); + } + case KnownFacilityTypes.Road: + { + return IFCInstanceExporter.CreateRoadPart(exporterIFC, level, ownerHistory, objectType, + objectPlacement, compositionType); + } + } + } + + return IFCInstanceExporter.CreateBuildingStorey(exporterIFC, level, + ownerHistory, objectType, objectPlacement, compositionType, elevation); + } + /// /// Initializes the common properties at the beginning of the export process. /// @@ -1209,10 +1266,8 @@ private void BeginDocumentExportCommon(ExporterIFC exporterIFC, Document documen bool exportBuilding = ExportBuilding(allLevels); // Skip Building if there is no Storey to be exported - if (exportBuilding) - { - CreateBuildingFromProjectInfo(exporterIFC, document, buildingPlacement); - + if (CreateFacilityFromProjectInfo(exporterIFC, document, buildingPlacement, exportBuilding) != null) + { IList unassignedBaseLevels = new List(); double lengthScale = UnitUtil.ScaleLengthForRevitAPI(); @@ -1283,25 +1338,32 @@ private void BeginDocumentExportCommon(ExporterIFC exporterIFC, Document documen IFCAnyHandle placement = ExporterUtil.CreateLocalPlacement(file, buildingPlacement, orig, null, null); string bsObjectType = NamingUtil.GetObjectTypeOverride(level, null); IFCElementComposition ifcComposition = LevelUtil.GetElementCompositionTypeOverride(level); - IFCAnyHandle buildingStorey = IFCInstanceExporter.CreateBuildingStorey(exporterIFC, level, ExporterCacheManager.OwnerHistoryHandle, - bsObjectType, placement, ifcComposition, elevation); + + // IFC4.3 questions: How do we best support predefined type for IfcFacilityParts other than + // IfcBuildingStoreys? + // Are nested IfcFacilityParts more common/needed, or is that an OK limitation for Revit to only + // have one level supported? + // Do we need to sort by elevation, even though elevation is only for building stories? + string predefinedType = null; + IFCAnyHandle facilityPart = CreateFacilityPart(exporterIFC, level, bsObjectType, placement, + ifcComposition, elevation, predefinedType); // Create classification reference when level has classification field name assigned to it - ClassificationUtil.CreateClassification(exporterIFC, file, level, buildingStorey); + ClassificationUtil.CreateClassification(exporterIFC, file, level, facilityPart); - prevBuildingStorey = buildingStorey; + prevBuildingStorey = facilityPart; prevPlacement = placement; prevHeight = height; prevElev = elev; - levelInfo = IFCLevelInfo.Create(buildingStorey, placement, height, elev, lengthScale, true); + levelInfo = IFCLevelInfo.Create(facilityPart, placement, height, elev, lengthScale, true); ExporterCacheManager.LevelInfoCache.AddLevelInfo(exporterIFC, level.Id, levelInfo, true); // if we have coincident levels, add buildingstories for them but use the old handle. for (int jj = 0; jj < coincidentLevels.Count; jj++) { level = allLevels[ii + jj + 1]; - levelInfo = IFCLevelInfo.Create(buildingStorey, placement, height, elev, lengthScale, true); + levelInfo = IFCLevelInfo.Create(facilityPart, placement, height, elev, lengthScale, true); ExporterCacheManager.LevelInfoCache.AddLevelInfo(exporterIFC, level.Id, levelInfo, true); } @@ -1320,9 +1382,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 +1458,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 +1552,10 @@ private void EndHostDocumentExport(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 +1564,11 @@ private void EndHostDocumentExport(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 { @@ -1561,28 +1625,24 @@ private void EndHostDocumentExport(ExporterIFC exporterIFC, Document document) IFCAnyHandle projectHandle = ExporterCacheManager.ProjectHandle; IFCAnyHandle siteHandle = ExporterCacheManager.SiteHandle; - IFCAnyHandle buildingHandle = ExporterCacheManager.BuildingHandle; + IFCAnyHandle facilityHandle = ExporterCacheManager.BuildingHandle; bool projectHasSite = !IFCAnyHandleUtil.IsNullOrHasNoValue(siteHandle); - bool projectHasBuilding = !IFCAnyHandleUtil.IsNullOrHasNoValue(buildingHandle); + bool projectHasFacility = !IFCAnyHandleUtil.IsNullOrHasNoValue(facilityHandle); - IFCAnyHandle siteOrbuildingHnd = siteHandle; - - if (!projectHasSite) + if (!projectHasSite && !projectHasFacility) { - if (!projectHasBuilding) - { - // if at this point the buildingHnd is null, which means that the model does not - // have Site nor any Level assigned to the BuildingStorey, create the IfcBuilding - // as the general container for all the elements (should be backward compatible). - IFCAnyHandle buildingPlacement = CreateBuildingPlacement(file); - buildingHandle = CreateBuildingFromProjectInfo(exporterIFC, document, buildingPlacement); - ExporterCacheManager.BuildingHandle = buildingHandle; - projectHasBuilding = true; - } - siteOrbuildingHnd = buildingHandle; + // if at this point the facilityHnd is null, which means that the model does not + // have Site nor any Level assigned to the FacilityPart, create the IfcFacility + // as the general container for all the elements (should be backward compatible). + IFCAnyHandle facilityPlacement = CreateBuildingPlacement(file); + facilityHandle = CreateFacilityFromProjectInfo(exporterIFC, document, facilityPlacement, true); + ExporterCacheManager.BuildingHandle = facilityHandle; + projectHasFacility = true; } + IFCAnyHandle siteOrFacilityHnd = projectHasFacility ? facilityHandle : siteHandle; + // Last chance to create the building handle was just above. if (projectHasSite) { @@ -1594,12 +1654,12 @@ private void EndHostDocumentExport(ExporterIFC exporterIFC, Document document) ExporterCacheManager.ContainmentCache.AddRelation(projectHandle, siteHandle); } - if (projectHasBuilding) + if (projectHasFacility) { - // assoc. site to the building. - ExporterCacheManager.ContainmentCache.AddRelation(siteHandle, buildingHandle); + // assoc. site to the facility. + ExporterCacheManager.ContainmentCache.AddRelation(siteHandle, facilityHandle); - IFCAnyHandle buildingPlacement = IFCAnyHandleUtil.GetObjectPlacement(buildingHandle); + IFCAnyHandle buildingPlacement = IFCAnyHandleUtil.GetObjectPlacement(facilityHandle); IFCAnyHandle relPlacement = IFCAnyHandleUtil.GetObjectPlacement(siteHandle); GeometryUtil.SetPlacementRelTo(buildingPlacement, relPlacement); } @@ -1607,8 +1667,8 @@ private void EndHostDocumentExport(ExporterIFC exporterIFC, Document document) else { // relate building and project if no site - if (projectHasBuilding) - ExporterCacheManager.ContainmentCache.AddRelation(projectHandle, buildingHandle); + if (projectHasFacility) + ExporterCacheManager.ContainmentCache.AddRelation(projectHandle, facilityHandle); } // relate assembly elements to assemblies @@ -1688,7 +1748,7 @@ private void EndHostDocumentExport(ExporterIFC exporterIFC, Document document) // Relate levels and products. This may create new orphaned elements, so deal with those next. RelateLevels(exporterIFC, document); - IFCAnyHandle defContainerObjectPlacement = IFCAnyHandleUtil.GetObjectPlacement(siteOrbuildingHnd); + IFCAnyHandle defContainerObjectPlacement = IFCAnyHandleUtil.GetObjectPlacement(siteOrFacilityHnd); Transform defContainerTrf = ExporterUtil.GetTotalTransformFromLocalPlacement(defContainerObjectPlacement); Transform defContainerInvTrf = defContainerTrf.Inverse; @@ -1705,40 +1765,40 @@ private void EndHostDocumentExport(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); bool containerIsSite = projectHasSite; - bool containerIsBuilding = projectHasBuilding; + bool containerIsFacility = projectHasFacility; IFCAnyHandle containerObjectPlacement = null; if (!IFCAnyHandleUtil.IsNullOrHasNoValue(overrideContainer)) { containerObjectPlacement = IFCAnyHandleUtil.GetObjectPlacement(overrideContainer); containerIsSite = IFCAnyHandleUtil.IsTypeOf(overrideContainer, IFCEntityType.IfcSite); - containerIsBuilding = !containerIsSite && + containerIsFacility = !containerIsSite && IFCAnyHandleUtil.IsTypeOf(overrideContainer, IFCEntityType.IfcBuilding); } else { - // Default behavior (generally site). + // Default behavior (generally facility). containerObjectPlacement = defContainerObjectPlacement; } - if (containerIsSite) - relatedElementSetForSite.Add(elemHnd); - else if (containerIsBuilding) + if (containerIsFacility) relatedElementSetForBuilding.Add(elemHnd); - + else if (containerIsSite) + relatedElementSetForSite.Add(elemHnd); + UpdateLocalPlacementForElement(elemHnd, file, containerObjectPlacement, null); } - if (relatedElementSetForBuilding.Count > 0 && projectHasBuilding) + if (relatedElementSetForBuilding.Count > 0 && projectHasFacility) { string guid = GUIDUtil.CreateSubElementGUID(projectInfo, (int)IFCProjectSubElements.RelContainedInBuildingSpatialStructure); IFCInstanceExporter.CreateRelContainedInSpatialStructure(file, guid, - ownerHistory, null, null, relatedElementSetForBuilding, buildingHandle); + ownerHistory, null, null, relatedElementSetForBuilding, facilityHandle); } if (relatedElementSetForSite.Count > 0 && projectHasSite) @@ -1760,9 +1820,9 @@ private void EndHostDocumentExport(ExporterIFC exporterIFC, Document document) foreach (IFCAnyHandle indivSpace in buildingSpaces) { bool containerIsSite = projectHasSite; - bool containerIsBuilding = projectHasBuilding; + bool containerIsBuilding = projectHasFacility; - // 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.OverrideSpaceContainmentParameter(exporterIFC, document, indivSpace, out overrideContainer); IFCAnyHandle containerObjectPlacement = null; @@ -1779,22 +1839,22 @@ private void EndHostDocumentExport(ExporterIFC exporterIFC, Document document) } else { - // Default behavior (generally site). + // Default behavior (generally facility). containerObjectPlacement = defContainerObjectPlacement; 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); } if (relatedElementSetForBuilding.Count > 0) { - ExporterCacheManager.ContainmentCache.AddRelations(buildingHandle, null, relatedElementSetForBuilding); + ExporterCacheManager.ContainmentCache.AddRelations(facilityHandle, null, relatedElementSetForBuilding); } if (relatedElementSetForSite.Count > 0) @@ -1816,8 +1876,8 @@ private void EndHostDocumentExport(ExporterIFC exporterIFC, Document document) // These elements are created internally, but we allow custom property sets for them. Create them here. using (ProductWrapper productWrapper = ProductWrapper.Create(exporterIFC, true)) { - if (projectHasBuilding) - productWrapper.AddBuilding(projectInfo, buildingHandle); + if (projectHasFacility) + productWrapper.AddBuilding(projectInfo, facilityHandle); if (projectInfo != null) ExporterUtil.ExportRelatedProperties(exporterIFC, projectInfo, productWrapper); } @@ -2101,10 +2161,11 @@ private void EndHostDocumentExport(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 +2181,6 @@ private void EndHostDocumentExport(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) @@ -2192,9 +2239,9 @@ private void EndHostDocumentExport(ExporterIFC exporterIFC, Document document) } // Create systems. - ExportCachedSystem(exporterIFC, document, file, ExporterCacheManager.SystemsCache.BuiltInSystemsCache, ownerHistory, buildingHandle, projectHasBuilding, false); - ExportCachedSystem(exporterIFC, document, file, ExporterCacheManager.SystemsCache.ElectricalSystemsCache, ownerHistory, buildingHandle, projectHasBuilding, true); - ExportCableTraySystem(document, file, ExporterCacheManager.MEPCache.CableElementsCache, ownerHistory, buildingHandle, projectHasBuilding); + ExportCachedSystem(exporterIFC, document, file, ExporterCacheManager.SystemsCache.BuiltInSystemsCache, ownerHistory, facilityHandle, projectHasFacility, false); + ExportCachedSystem(exporterIFC, document, file, ExporterCacheManager.SystemsCache.ElectricalSystemsCache, ownerHistory, facilityHandle, projectHasFacility, true); + ExportCableTraySystem(document, file, ExporterCacheManager.MEPCache.CableElementsCache, ownerHistory, facilityHandle, projectHasFacility); // Add presentation layer assignments - this is in addition to those created internally. // Any representation in this list will override any internal assignment. @@ -2215,6 +2262,20 @@ private void EndHostDocumentExport(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 +2305,10 @@ private void EndHostDocumentExport(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 +2317,8 @@ private void EndHostDocumentExport(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 +2346,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 +2373,7 @@ public IFCFileDocumentInfo(Document document) // 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 +2382,7 @@ public IFCFileDocumentInfo(Document document) 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 +2394,7 @@ public IFCFileDocumentInfo(Document document) } 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 +2463,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 +2516,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 +2913,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 +2958,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 +2986,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 +2998,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 +3031,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,1308 +3264,175 @@ static public IFCAnyHandle CreateIFCAddress(IFCFile file, Document document, Pro return postalAddress; } + + /// + /// Creates the global direction and sets the cardinal directions in 3D. + /// + /// The IFC exporter object. + private void CreateGlobalDirection(ExporterIFC exporterIFC) + { + // Note that we do not use the ExporterUtil.CreateDirection functions below, as they try + // to match the input XYZ to one of the "global" directions that we are creating below. + IFCAnyHandle xDirPos = null; + IFCAnyHandle xDirNeg = null; + IFCAnyHandle yDirPos = null; + IFCAnyHandle yDirNeg = null; + IFCAnyHandle zDirPos = null; + IFCAnyHandle zDirNeg = null; + + IFCFile file = exporterIFC.GetFile(); + IList xxp = new List(); + xxp.Add(1.0); xxp.Add(0.0); xxp.Add(0.0); + xDirPos = IFCInstanceExporter.CreateDirection(file, xxp); + + IList xxn = new List(); + xxn.Add(-1.0); xxn.Add(0.0); xxn.Add(0.0); + xDirNeg = IFCInstanceExporter.CreateDirection(file, xxn); + + IList yyp = new List(); + yyp.Add(0.0); yyp.Add(1.0); yyp.Add(0.0); + yDirPos = IFCInstanceExporter.CreateDirection(file, yyp); + + IList yyn = new List(); + yyn.Add(0.0); yyn.Add(-1.0); yyn.Add(0.0); + yDirNeg = IFCInstanceExporter.CreateDirection(file, yyn); + + IList zzp = new List(); + zzp.Add(0.0); zzp.Add(0.0); zzp.Add(1.0); + zDirPos = IFCInstanceExporter.CreateDirection(file, zzp); + + IList zzn = new List(); + zzn.Add(0.0); zzn.Add(0.0); zzn.Add(-1.0); + zDirNeg = IFCInstanceExporter.CreateDirection(file, zzn); + + ExporterIFCUtils.SetGlobal3DDirectionHandles(true, xDirPos, yDirPos, zDirPos); + ExporterIFCUtils.SetGlobal3DDirectionHandles(false, xDirNeg, yDirNeg, zDirNeg); + } - private IFCAnyHandle CreateSIUnit(IFCFile file, ForgeTypeId specTypeId, IFCUnit ifcUnitType, IFCSIUnitName unitName, IFCSIPrefix? prefix, ForgeTypeId unitTypeId) + /// + /// Creates the global direction and sets the cardinal directions in 2D. + /// + /// The IFC exporter object. + private void CreateGlobalDirection2D(ExporterIFC exporterIFC) { - 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); - } + IFCAnyHandle xDirPos2D = null; + IFCAnyHandle xDirNeg2D = null; + IFCAnyHandle yDirPos2D = null; + IFCAnyHandle yDirNeg2D = null; + IFCFile file = exporterIFC.GetFile(); + + IList xxp = new List(); + xxp.Add(1.0); xxp.Add(0.0); + xDirPos2D = IFCInstanceExporter.CreateDirection(file, xxp); + + IList xxn = new List(); + xxn.Add(-1.0); xxn.Add(0.0); + xDirNeg2D = IFCInstanceExporter.CreateDirection(file, xxn); + + IList yyp = new List(); + yyp.Add(0.0); yyp.Add(1.0); + yDirPos2D = IFCInstanceExporter.CreateDirection(file, yyp); - return siUnit; + IList yyn = new List(); + yyn.Add(0.0); yyn.Add(-1.0); + yDirNeg2D = IFCInstanceExporter.CreateDirection(file, yyn); + ExporterIFCUtils.SetGlobal2DDirectionHandles(true, xDirPos2D, yDirPos2D); + ExporterIFCUtils.SetGlobal2DDirectionHandles(false, xDirNeg2D, yDirNeg2D); } /// - /// Creates the IfcUnitAssignment. This is a long list of units that we correctly translate from our internal units to known units. + /// Creates the global cartesian origin then sets the 3D and 2D origins. /// /// The IFC exporter object. - /// The document provides ProjectUnit and DisplayUnitSystem. - /// The IFC handle. - private IFCAnyHandle CreateDefaultUnits(ExporterIFC exporterIFC, Document doc) + private void CreateGlobalCartesianOrigin(ExporterIFC exporterIFC) { - HashSet unitSet = new HashSet(); + + IFCAnyHandle origin2d = null; + IFCAnyHandle origin = null; + IFCFile file = exporterIFC.GetFile(); - bool exportToCOBIE = ExporterCacheManager.ExportOptionsCache.ExportAsCOBIE; + IList measure = new List(); + measure.Add(0.0); measure.Add(0.0); measure.Add(0.0); + origin = IFCInstanceExporter.CreateCartesianPoint(file, measure); + + IList measure2d = new List(); + measure2d.Add(0.0); measure2d.Add(0.0); + origin2d = IFCInstanceExporter.CreateCartesianPoint(file, measure2d); + ExporterIFCUtils.SetGlobal3DOriginHandle(origin); + ExporterIFCUtils.SetGlobal2DOriginHandle(origin2d); + } + + private static bool ValidateContainedHandle(IFCAnyHandle initialHandle) + { + if (ExporterCacheManager.ElementsInAssembliesCache.Contains(initialHandle)) + return false; - Dictionary, IFCAnyHandle> addedDerivedUnitElements = new Dictionary, IFCAnyHandle>(); - Action, IFCAnyHandle, int> createDerivedUnitElement = (elements, unit, exponent) => + try { - var pair = new Tuple(unit, exponent); - if (!addedDerivedUnitElements.ContainsKey(pair)) - { - var element = IFCInstanceExporter.CreateDerivedUnitElement(file, unit, exponent); - elements.Add(addedDerivedUnitElements[pair] = element); - } + if (!IFCAnyHandleUtil.HasRelDecomposes(initialHandle)) + return true; + } + catch + { + } + + return false; + } - elements.Add(addedDerivedUnitElements[pair]); - }; + /// + /// Remove contained or invalid handles from this set. + /// + /// The initial set that may have contained or invalid handles. + /// A cleaned set. + public static HashSet RemoveContainedHandlesFromSet(ICollection initialSet) + { + HashSet filteredSet = new HashSet(); - IFCAnyHandle lenSIBaseUnit = null; + if (initialSet != 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 + foreach (IFCAnyHandle initialHandle in initialSet) { - //Couldn't find display unit type conversion -- assuming foot - if (exportToCOBIE) - lenConvName = "foot"; - else - lenConvName = "FOOT"; - lenConversionBased = true; - lenUseDefault = true; + if (ValidateContainedHandle(initialHandle)) + filteredSet.Add(initialHandle); } + } - 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); + return filteredSet; + } - 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); - } + private class IFCLevelExportInfo + { + public IFCLevelExportInfo() { } - unitSet.Add(lenSIUnit); // created above, so unique. - ExporterCacheManager.UnitsCache.AddUnit(SpecTypeId.Length, lenSIUnit, lengthScaleFactor, 0.0); - } + public IDictionary> LevelMapping { get; set; } = + new Dictionary>(); - { - bool areaConversionBased = false; - bool areaUseDefault = false; + public IList OrphanedLevelInfos { get; set; } = new List(); - IFCUnit areaUnitType = IFCUnit.AreaUnit; - IFCSIPrefix? areaPrefix = null; - IFCSIUnitName areaUnitName = IFCSIUnitName.Square_Metre; - string areaConvName = null; + public void UnionLevelInfoRelated(ElementId toLevelId, IFCLevelInfo fromLevel) + { + if (fromLevel == null) + return; - 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 + if (toLevelId == ElementId.InvalidElementId) { - //Couldn't find display unit type conversion -- assuming foot - if (exportToCOBIE) - areaConvName = "foot"; - else - areaConvName = "SQUARE FOOT"; - areaConversionBased = true; - areaUseDefault = true; + OrphanedLevelInfos.Add(fromLevel); + return; } - double areaScaleFactor = UnitUtils.ConvertFromInternalUnits(1.0, areaUseDefault ? UnitTypeId.SquareFeet : areaFormatOptions.GetUnitTypeId()); - IFCAnyHandle areaSiUnit = IFCInstanceExporter.CreateSIUnit(file, areaUnitType, areaPrefix, areaUnitName); - if (areaConversionBased) + IList levelMappingList; + if (!LevelMapping.TryGetValue(toLevelId, out levelMappingList)) { - 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); + levelMappingList = new List(); + LevelMapping[toLevelId] = levelMappingList; } - - unitSet.Add(areaSiUnit); // created above, so unique. - ExporterCacheManager.UnitsCache.AddUnit(SpecTypeId.Area, areaSiUnit, areaScaleFactor, 0.0); + levelMappingList.Add(fromLevel); } + public void TransferOrphanedLevelInfo(ElementId toLevelId) { - 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(); - createDerivedUnitElement(elements, massSIUnit, -1); - createDerivedUnitElement(elements, lenSIBaseUnit, -2); - createDerivedUnitElement(elements, timeSIUnit, 3); - createDerivedUnitElement(elements, lumenSIUnit, 1); - - IFCAnyHandle luminousEfficacyUnit = IFCInstanceExporter.CreateDerivedUnit(file, elements, - IFCDerivedUnitEnum.UserDefined, "Luminous Efficacy"); - - double electricalEfficacyFactor = UnitUtils.ConvertFromInternalUnits(1.0, UnitTypeId.LumensPerWatt); - ExporterCacheManager.UnitsCache.AddUnit(SpecTypeId.Efficacy, luminousEfficacyUnit, electricalEfficacyFactor, 0.0); - ExporterCacheManager.UnitsCache["LUMINOUSEFFICACY"] = luminousEfficacyUnit; - - unitSet.Add(luminousEfficacyUnit); - } - - // Electrical Resistivity - support Ohm * M = (kg * m^3)/(s^3 * A^2) only - { - ISet elements = new HashSet(); - createDerivedUnitElement(elements, massSIUnit, 1); - createDerivedUnitElement(elements, lenSIBaseUnit, 3); - createDerivedUnitElement(elements, timeSIUnit, -3); - createDerivedUnitElement(elements, currentSIUnit, -2); - - IFCAnyHandle electricalResistivityUnit = IFCInstanceExporter.CreateDerivedUnit(file, elements, - IFCDerivedUnitEnum.UserDefined, "Electrical Resistivity"); - - double electricalResistivityFactor = UnitUtils.ConvertFromInternalUnits(1.0, UnitTypeId.OhmMeters); - ExporterCacheManager.UnitsCache.AddUnit(SpecTypeId.ElectricalResistivity, electricalResistivityUnit, electricalResistivityFactor, 0.0); - ExporterCacheManager.UnitsCache["ELECTRICALRESISTIVITY"] = electricalResistivityUnit; - - unitSet.Add(electricalResistivityUnit); - } - // Sound Power - support watt only. - { - ISet elements = new HashSet(); - createDerivedUnitElement(elements, massSIUnit, 1); - createDerivedUnitElement(elements, lenSIBaseUnit, 2); - createDerivedUnitElement(elements, timeSIUnit, -3); - - IFCAnyHandle soundPowerUnit = IFCInstanceExporter.CreateDerivedUnit(file, elements, - IFCDerivedUnitEnum.SoundPowerUnit, null); - unitSet.Add(soundPowerUnit); - - double soundPowerFactor = UnitUtils.ConvertFromInternalUnits(1.0, UnitTypeId.Watts); - ExporterCacheManager.UnitsCache.AddUnit(SpecTypeId.Wattage, soundPowerUnit, soundPowerFactor, 0.0); - } - - // Sound Pressure - support Pascal only. - { - ISet elements = new HashSet(); - createDerivedUnitElement(elements, massSIUnit, 1); - createDerivedUnitElement(elements, lenSIBaseUnit, -1); - createDerivedUnitElement(elements, timeSIUnit, -2); - - IFCAnyHandle soundPressureUnit = IFCInstanceExporter.CreateDerivedUnit(file, elements, - IFCDerivedUnitEnum.SoundPressureUnit, null); - unitSet.Add(soundPressureUnit); - - double soundPressureFactor = UnitUtils.ConvertFromInternalUnits(1.0, UnitTypeId.Pascals); - ExporterCacheManager.UnitsCache.AddUnit(SpecTypeId.HvacPressure, soundPressureUnit, soundPressureFactor, 0.0); - } - - // Linear Velocity - support m/s only. - { - ISet elements = new HashSet(); - createDerivedUnitElement(elements, lenSIBaseUnit, 1); - createDerivedUnitElement(elements, timeSIUnit, -1); - - IFCAnyHandle linearVelocityUnit = IFCInstanceExporter.CreateDerivedUnit(file, elements, - IFCDerivedUnitEnum.LinearVelocityUnit, null); - - double linearVelocityFactor = UnitUtils.ConvertFromInternalUnits(1.0, UnitTypeId.MetersPerSecond); - ExporterCacheManager.UnitsCache.AddUnit(SpecTypeId.HvacVelocity, linearVelocityUnit, linearVelocityFactor, 0.0); - - unitSet.Add(linearVelocityUnit); - } - - // Currency - disallowed for IC2x3 Coordination View 2.0. If we find a currency, export it as a real. - if (!ExporterCacheManager.ExportOptionsCache.ExportAs2x3CoordinationView2) - { - FormatOptions currencyFormatOptions = doc.GetUnits().GetFormatOptions(SpecTypeId.Currency); - ForgeTypeId currencySymbol = currencyFormatOptions.GetSymbolTypeId(); - - IFCAnyHandle currencyUnit = null; - - // Some of these are guesses for IFC2x3, since multiple currencies may use the same symbol, - // but no detail is given on which currency is being used. For IFC4, we just use the label. - if (!ExporterCacheManager.ExportOptionsCache.ExportAsOlderThanIFC4) - { - string currencyLabel = null; - try - { - currencyLabel = LabelUtils.GetLabelForSymbol(currencySymbol); - currencyUnit = IFCInstanceExporter.CreateMonetaryUnit4(file, currencyLabel); - } - catch - { - currencyUnit = null; - } - } - else - { - IFCCurrencyType? currencyType = null; - - if (currencySymbol.Equals(SymbolTypeId.UsDollar)) - { - currencyType = IFCCurrencyType.USD; - } - else if (currencySymbol.Equals(SymbolTypeId.EuroPrefix) || - currencySymbol.Equals(SymbolTypeId.EuroSuffix)) - { - currencyType = IFCCurrencyType.EUR; - } - else if (currencySymbol.Equals(SymbolTypeId.UkPound)) - { - currencyType = IFCCurrencyType.GBP; - } - else if (currencySymbol.Equals(SymbolTypeId.ChineseHongKongDollar)) - { - currencyType = IFCCurrencyType.HKD; - } - else if (currencySymbol.Equals(SymbolTypeId.Krone)) - { - currencyType = IFCCurrencyType.NOK; - } - else if (currencySymbol.Equals(SymbolTypeId.Shekel)) - { - currencyType = IFCCurrencyType.ILS; - } - else if (currencySymbol.Equals(SymbolTypeId.Yen)) - { - currencyType = IFCCurrencyType.JPY; - } - else if (currencySymbol.Equals(SymbolTypeId.Won)) - { - currencyType = IFCCurrencyType.KRW; - } - else if (currencySymbol.Equals(SymbolTypeId.Baht)) - { - currencyType = IFCCurrencyType.THB; - } - else if (currencySymbol.Equals(SymbolTypeId.Dong)) - { - currencyType = IFCCurrencyType.VND; - } - - if (currencyType.HasValue) - currencyUnit = IFCInstanceExporter.CreateMonetaryUnit2x3(file, currencyType.Value); - } - - if (currencyUnit != null) - { - unitSet.Add(currencyUnit); // created above, so unique. - // We will cache the currency, if we create it. If we don't, we'll export currencies as numbers. - ExporterCacheManager.UnitsCache["CURRENCY"] = currencyUnit; - } - } - - // Pressure - support Pascal, kPa and MPa. - { - IFCSIPrefix? prefix = null; - FormatOptions pressureFormatOptions = doc.GetUnits().GetFormatOptions(SpecTypeId.HvacPressure); - ForgeTypeId pressureUnit = pressureFormatOptions.GetUnitTypeId(); - if (pressureUnit.Equals(UnitTypeId.Pascals)) - { - // This space intentionally left blank. - } - else if (pressureUnit.Equals(UnitTypeId.Kilopascals)) - { - prefix = IFCSIPrefix.Kilo; - } - else if (pressureUnit.Equals(UnitTypeId.Megapascals)) - { - prefix = IFCSIPrefix.Mega; - } - else - { - pressureUnit = UnitTypeId.Pascals; - } - - IFCAnyHandle pressureSIUnit = CreateSIUnit(file, SpecTypeId.HvacPressure, IFCUnit.PressureUnit, IFCSIUnitName.Pascal, - prefix, pressureUnit); - unitSet.Add(pressureSIUnit); // created above, so unique. - } - - // Friction loss - support Pa/m only. - { - ISet elements = new HashSet(); - createDerivedUnitElement(elements, lenSIBaseUnit, -2); - createDerivedUnitElement(elements, massSIUnit, 1); - createDerivedUnitElement(elements, timeSIUnit, -2); - - IFCAnyHandle frictionLossUnit = IFCInstanceExporter.CreateDerivedUnit(file, elements, - IFCDerivedUnitEnum.UserDefined, "Friction Loss"); - - double frictionLossFactor = UnitUtils.ConvertFromInternalUnits(1.0, UnitTypeId.PascalsPerMeter); - ExporterCacheManager.UnitsCache.AddUnit(SpecTypeId.HvacFriction, frictionLossUnit, frictionLossFactor, 0.0); - ExporterCacheManager.UnitsCache["FRICTIONLOSS"] = frictionLossUnit; - - unitSet.Add(frictionLossUnit); - } - - // Area/Planar Force - support N/m2 only, and Linear Force - support N/m only - { - ISet elements = new HashSet(); - createDerivedUnitElement(elements, massSIUnit, 1); - createDerivedUnitElement(elements, timeSIUnit, -2); - - IFCAnyHandle linearForceUnit = IFCInstanceExporter.CreateDerivedUnit(file, elements, - IFCDerivedUnitEnum.LinearForceUnit, null); - - double linearForceFactor = UnitUtils.ConvertFromInternalUnits(1.0, UnitTypeId.NewtonsPerMeter); - ExporterCacheManager.UnitsCache.AddUnit(SpecTypeId.LinearForce, linearForceUnit, linearForceFactor, 0.0); - unitSet.Add(linearForceUnit); - - elements = new HashSet(); - createDerivedUnitElement(elements, massSIUnit, 1); - createDerivedUnitElement(elements, lenSIBaseUnit, -1); - createDerivedUnitElement(elements, timeSIUnit, -2); - - IFCAnyHandle planarForceUnit = IFCInstanceExporter.CreateDerivedUnit(file, elements, - IFCDerivedUnitEnum.PlanarForceUnit, null); - - double planarForceFactor = UnitUtils.ConvertFromInternalUnits(1.0, UnitTypeId.NewtonsPerSquareMeter); - ExporterCacheManager.UnitsCache.AddUnit(SpecTypeId.AreaForce, planarForceUnit, planarForceFactor, 0.0); - unitSet.Add(planarForceUnit); - } - - // Specific heat - support metric J/(kg * K) = m^2/(s^2 * K) only. - { - ISet elements = new HashSet(); - createDerivedUnitElement(elements, lenSIBaseUnit, 2); - createDerivedUnitElement(elements, timeSIUnit, -2); - createDerivedUnitElement(elements, tempBaseSIUnit, -1); - - IFCAnyHandle specificHeatUnit = IFCInstanceExporter.CreateDerivedUnit(file, elements, - IFCDerivedUnitEnum.SpecificHeatCapacityUnit, null); - unitSet.Add(specificHeatUnit); - - double specificHeatFactor = UnitUtils.ConvertFromInternalUnits(1.0, UnitTypeId.JoulesPerKilogramDegreeCelsius); - ExporterCacheManager.UnitsCache.AddUnit(SpecTypeId.SpecificHeat, specificHeatUnit, specificHeatFactor, 0.0); - } - - // Heat flux density - support metric W/m^2 = kg/s^3 only. - { - ISet elements = new HashSet(); - createDerivedUnitElement(elements, massSIUnit, 1); - createDerivedUnitElement(elements, timeSIUnit, -3); - - IFCAnyHandle heatFluxDensityUnit = IFCInstanceExporter.CreateDerivedUnit(file, elements, - IFCDerivedUnitEnum.HeatFluxDensityUnit, null); - unitSet.Add(heatFluxDensityUnit); - - double heatFluxDensityFactor = UnitUtils.ConvertFromInternalUnits(1.0, UnitTypeId.WattsPerSquareMeter); - ExporterCacheManager.UnitsCache.AddUnit(SpecTypeId.HvacPowerDensity, heatFluxDensityUnit, heatFluxDensityFactor, 0.0); - } - - // Heating value - support metric J/kg = m^2/s^2 only. - { - ISet elements = new HashSet(); - createDerivedUnitElement(elements, lenSIBaseUnit, 2); - createDerivedUnitElement(elements, timeSIUnit, -2); - - IFCAnyHandle heatingValueUnit = IFCInstanceExporter.CreateDerivedUnit(file, elements, - IFCDerivedUnitEnum.HeatingValueUnit, null); - unitSet.Add(heatingValueUnit); - - double heatingValueFactor = UnitUtils.ConvertFromInternalUnits(1.0, UnitTypeId.JoulesPerGram); - heatingValueFactor *= 1.0e+3; // --> gram to kilogram - ExporterCacheManager.UnitsCache.AddUnit(SpecTypeId.SpecificHeatOfVaporization, heatingValueUnit, heatingValueFactor, 0.0); - } - - // Permeability (Permeance) - support metric kg/(Pa * s * m^2) = s/m only. - { - ISet elements = new HashSet(); - createDerivedUnitElement(elements, timeSIUnit, 1); - createDerivedUnitElement(elements, lenSIBaseUnit, -1); - - IFCAnyHandle permeabilityUnit = IFCInstanceExporter.CreateDerivedUnit(file, elements, - IFCDerivedUnitEnum.VaporPermeabilityUnit, null); - unitSet.Add(permeabilityUnit); - - double permeabilityFactor = UnitUtils.ConvertFromInternalUnits(1.0, UnitTypeId.NanogramsPerPascalSecondSquareMeter); - permeabilityFactor *= 1.0e-12; // --> Nanogram to kilogram - ExporterCacheManager.UnitsCache.AddUnit(SpecTypeId.Permeability, permeabilityUnit, permeabilityFactor, 0.0); - } - - // Dynamic viscosity - support metric Pa * s = kg/(m * s) only. - { - ISet elements = new HashSet(); - createDerivedUnitElement(elements, massSIUnit, 1); - createDerivedUnitElement(elements, lenSIBaseUnit, -1); - createDerivedUnitElement(elements, timeSIUnit, -1); - - IFCAnyHandle viscosityUnit = IFCInstanceExporter.CreateDerivedUnit(file, elements, - IFCDerivedUnitEnum.DynamicViscosityUnit, null); - unitSet.Add(viscosityUnit); - - double viscosityFactor = UnitUtils.ConvertFromInternalUnits(1.0, UnitTypeId.KilogramsPerMeterSecond); - ExporterCacheManager.UnitsCache.AddUnit(SpecTypeId.HvacViscosity, viscosityUnit, viscosityFactor, 0.0); - } - - // Thermal expansion coefficient - support metric 1/K only. - { - ISet elements = new HashSet(); - createDerivedUnitElement(elements, tempBaseSIUnit, -1); - - IFCAnyHandle thermalExpansionCoefficientUnit = IFCInstanceExporter.CreateDerivedUnit(file, elements, - IFCDerivedUnitEnum.ThermalExpansionCoefficientUnit, null); - unitSet.Add(thermalExpansionCoefficientUnit); - - double thermalExpansionCoefficientFactor = UnitUtils.ConvertFromInternalUnits(1.0, UnitTypeId.InverseDegreesCelsius); - ExporterCacheManager.UnitsCache.AddUnit(SpecTypeId.ThermalExpansionCoefficient, thermalExpansionCoefficientUnit, thermalExpansionCoefficientFactor, 0.0); - } - - // Modulus of elasticity - support Pascal only. - { - ISet elements = new HashSet(); - createDerivedUnitElement(elements, massSIUnit, 1); - createDerivedUnitElement(elements, lenSIBaseUnit, -1); - createDerivedUnitElement(elements, timeSIUnit, -2); - - IFCAnyHandle modulusOfElasticityUnit = IFCInstanceExporter.CreateDerivedUnit(file, elements, - IFCDerivedUnitEnum.ModulusOfElasticityUnit, null); - unitSet.Add(modulusOfElasticityUnit); - - double modulusOfElasticityFactor = UnitUtils.ConvertFromInternalUnits(1.0, UnitTypeId.Pascals); - ExporterCacheManager.UnitsCache.AddUnit(SpecTypeId.Stress, modulusOfElasticityUnit, modulusOfElasticityFactor, 0.0); - } - - // Isothermal moisture capacity - support m3 / kg only. - { - ISet elements = new HashSet(); - createDerivedUnitElement(elements, lenSIBaseUnit, 3); - createDerivedUnitElement(elements, massSIUnit, -1); - - IFCAnyHandle isothermalMoistureCapacityUnit = IFCInstanceExporter.CreateDerivedUnit(file, elements, - IFCDerivedUnitEnum.IsothermalMoistureCapacityUnit, null); - unitSet.Add(isothermalMoistureCapacityUnit); - - double isothermalMoistureCapacityFactor = UnitUtils.ConvertFromInternalUnits(1.0, UnitTypeId.CubicMetersPerKilogram); - ExporterCacheManager.UnitsCache.AddUnit(SpecTypeId.IsothermalMoistureCapacity, isothermalMoistureCapacityUnit, isothermalMoistureCapacityFactor, 0.0); - } - - // Moisture diffusivity - support metric m^2/s only. - { - ISet elements = new HashSet(); - createDerivedUnitElement(elements, lenSIBaseUnit, 2); - createDerivedUnitElement(elements, timeSIUnit, -1); - - IFCAnyHandle moistureDiffusivityUnit = IFCInstanceExporter.CreateDerivedUnit(file, elements, - IFCDerivedUnitEnum.MoistureDiffusivityUnit, null); - unitSet.Add(moistureDiffusivityUnit); - - double moistureDiffusivityFactor = UnitUtils.ConvertFromInternalUnits(1.0, UnitTypeId.SquareMetersPerSecond); - ExporterCacheManager.UnitsCache.AddUnit(SpecTypeId.Diffusivity, moistureDiffusivityUnit, moistureDiffusivityFactor, 0.0); - } - - // Area density - support metric kg/m^2 only. - if (!ExporterCacheManager.ExportOptionsCache.ExportAsOlderThanIFC4) - { - ISet elements = new HashSet(); - createDerivedUnitElement(elements, massSIUnit, 1); - createDerivedUnitElement(elements, lenSIBaseUnit, 2); - - IFCAnyHandle areaDensityUnit = IFCInstanceExporter.CreateDerivedUnit(file, elements, - Toolkit.IFC4.IFCDerivedUnit.AREADENSITYUNIT, null); - unitSet.Add(areaDensityUnit); - - double areaDensityFactor = UnitUtils.ConvertFromInternalUnits(1.0, UnitTypeId.KilogramsPerSquareMeter); - ExporterCacheManager.UnitsCache.AddUnit(SpecTypeId.MassPerUnitArea, areaDensityUnit, areaDensityFactor, 0.0); - } - - // Mass per length - support metric kg/m only. - { - ISet elements = new HashSet(); - createDerivedUnitElement(elements, massSIUnit, 1); - createDerivedUnitElement(elements, lenSIBaseUnit, -1); - - IFCAnyHandle massPerLenghtUnit = IFCInstanceExporter.CreateDerivedUnit(file, elements, - IFCDerivedUnitEnum.MassPerLengthUnit, null); - unitSet.Add(massPerLenghtUnit); - - double massPerLenghtFactor = UnitUtils.ConvertFromInternalUnits(1.0, UnitTypeId.KilogramsPerMeter); - ExporterCacheManager.UnitsCache.AddUnit(SpecTypeId.MassPerUnitLength, massPerLenghtUnit, massPerLenghtFactor, 0.0); - } - - // Thermal resistance - support metric (m^2 * K)/W = (s^3 * K) / kg only. - { - ISet elements = new HashSet(); - createDerivedUnitElement(elements, timeSIUnit, 3); - createDerivedUnitElement(elements, tempBaseSIUnit, 1); - createDerivedUnitElement(elements, massSIUnit, -1); - - IFCAnyHandle thermalResistanceUnit = IFCInstanceExporter.CreateDerivedUnit(file, elements, - IFCDerivedUnitEnum.ThermalResistanceUnit, null); - unitSet.Add(thermalResistanceUnit); - - double thermalResistanceFactor = UnitUtils.ConvertFromInternalUnits(1.0, UnitTypeId.SquareMeterKelvinsPerWatt); - ExporterCacheManager.UnitsCache.AddUnit(SpecTypeId.ThermalResistance, thermalResistanceUnit, thermalResistanceFactor, 0.0); - } - - // Acceleration - support metric m/s^2 only. - { - ISet elements = new HashSet(); - createDerivedUnitElement(elements, lenSIBaseUnit, 1); - createDerivedUnitElement(elements, timeSIUnit, -2); - - IFCAnyHandle thermalResistanceUnit = IFCInstanceExporter.CreateDerivedUnit(file, elements, - IFCDerivedUnitEnum.AccelerationUnit, null); - unitSet.Add(thermalResistanceUnit); - - double thermalResistanceFactor = UnitUtils.ConvertFromInternalUnits(1.0, UnitTypeId.MetersPerSecondSquared); - ExporterCacheManager.UnitsCache.AddUnit(SpecTypeId.Acceleration, thermalResistanceUnit, thermalResistanceFactor, 0.0); - } - - // Angular velocity - support rad/s only. - { - ISet elements = new HashSet(); - - createDerivedUnitElement(elements, angleSIUnit, 1); - createDerivedUnitElement(elements, timeSIUnit, -1); - - IFCAnyHandle angularVelocityUnit = IFCInstanceExporter.CreateDerivedUnit(file, elements, - IFCDerivedUnitEnum.AngularVelocityUnit, null); - unitSet.Add(angularVelocityUnit); - - double angularVelocityFactor = UnitUtils.ConvertFromInternalUnits(1.0, UnitTypeId.RadiansPerSecond); - ExporterCacheManager.UnitsCache.AddUnit(SpecTypeId.Pulsation, angularVelocityUnit, angularVelocityFactor, 0.0); - } - - // Linear stiffness - support N/m = kg/s^2 only. - { - ISet elements = new HashSet(); - - createDerivedUnitElement(elements, massSIUnit, 1); - createDerivedUnitElement(elements, timeSIUnit, -2); - - IFCAnyHandle linearStiffnessUnit = IFCInstanceExporter.CreateDerivedUnit(file, elements, - IFCDerivedUnitEnum.LinearStiffnessUnit, null); - unitSet.Add(linearStiffnessUnit); - - double linearStiffnessFactor = UnitUtils.ConvertFromInternalUnits(1.0, UnitTypeId.NewtonsPerMeter); - ExporterCacheManager.UnitsCache.AddUnit(SpecTypeId.PointSpringCoefficient, linearStiffnessUnit, linearStiffnessFactor, 0.0); - } - - // Warping constant - support m^6 only. - { - ISet elements = new HashSet(); - - createDerivedUnitElement(elements, lenSIBaseUnit, 6); - - IFCAnyHandle wrappingConstantUnit = IFCInstanceExporter.CreateDerivedUnit(file, elements, - IFCDerivedUnitEnum.WarpingConstantUnit, null); - unitSet.Add(wrappingConstantUnit); - - double wrappingConstantFactor = UnitUtils.ConvertFromInternalUnits(1.0, UnitTypeId.MetersToTheSixthPower); - ExporterCacheManager.UnitsCache.AddUnit(SpecTypeId.WarpingConstant, wrappingConstantUnit, wrappingConstantFactor, 0.0); - } - - // Linear moment - support N-m / m = (kg * m)/s^2 only. - { - ISet elements = new HashSet(); - - createDerivedUnitElement(elements, massSIUnit, 1); - createDerivedUnitElement(elements, lenSIBaseUnit, 1); - createDerivedUnitElement(elements, timeSIUnit, -2); - - IFCAnyHandle linearMomentUnit = IFCInstanceExporter.CreateDerivedUnit(file, elements, - IFCDerivedUnitEnum.LinearMomentUnit, null); - unitSet.Add(linearMomentUnit); - - double linearMomentFactor = UnitUtils.ConvertFromInternalUnits(1.0, UnitTypeId.NewtonMetersPerMeter); - ExporterCacheManager.UnitsCache.AddUnit(SpecTypeId.LinearMoment, linearMomentUnit, linearMomentFactor, 0.0); - } - - // Torque - support N-m = (kg * m^2)/s^2 only. - { - ISet elements = new HashSet(); - - createDerivedUnitElement(elements, massSIUnit, 1); - createDerivedUnitElement(elements, lenSIBaseUnit, 2); - createDerivedUnitElement(elements, timeSIUnit, -2); - - IFCAnyHandle torqueUnit = IFCInstanceExporter.CreateDerivedUnit(file, elements, - IFCDerivedUnitEnum.Torqueunit, null); - unitSet.Add(torqueUnit); - - double torqueFactor = UnitUtils.ConvertFromInternalUnits(1.0, UnitTypeId.NewtonMeters); - ExporterCacheManager.UnitsCache.AddUnit(SpecTypeId.Moment, torqueUnit, torqueFactor, 0.0); - } - - // GSA only units. - if (exportToCOBIE) - { - // Derived imperial mass unit - { - IFCUnit unitType = IFCUnit.MassUnit; - IFCAnyHandle dims = IFCInstanceExporter.CreateDimensionalExponents(file, 0, 1, 0, 0, 0, 0, 0); - double factor = 0.45359237; // --> pound to kilogram - string convName = "pound"; - - IFCAnyHandle convFactor = IFCInstanceExporter.CreateMeasureWithUnit(file, Toolkit.IFCDataUtil.CreateAsMassMeasure(factor), massSIUnit); - IFCAnyHandle massUnit = IFCInstanceExporter.CreateConversionBasedUnit(file, dims, unitType, convName, convFactor); - unitSet.Add(massUnit); // created above, so unique. - } - - // Air Changes per Hour - { - IFCUnit unitType = IFCUnit.FrequencyUnit; - IFCAnyHandle dims = IFCInstanceExporter.CreateDimensionalExponents(file, 0, 0, -1, 0, 0, 0, 0); - double factor = 1.0 / 3600.0; // --> seconds to hours - string convName = "ACH"; - - IFCAnyHandle convFactor = IFCInstanceExporter.CreateMeasureWithUnit(file, Toolkit.IFCDataUtil.CreateAsTimeMeasure(factor), timeSIUnit); - IFCAnyHandle achUnit = IFCInstanceExporter.CreateConversionBasedUnit(file, dims, unitType, convName, convFactor); - unitSet.Add(achUnit); // created above, so unique. - ExporterCacheManager.UnitsCache["ACH"] = achUnit; - } - } - - return IFCInstanceExporter.CreateUnitAssignment(file, unitSet); - } - - /// - /// Creates the global direction and sets the cardinal directions in 3D. - /// - /// The IFC exporter object. - private void CreateGlobalDirection(ExporterIFC exporterIFC) - { - // Note that we do not use the ExporterUtil.CreateDirection functions below, as they try - // to match the input XYZ to one of the "global" directions that we are creating below. - IFCAnyHandle xDirPos = null; - IFCAnyHandle xDirNeg = null; - IFCAnyHandle yDirPos = null; - IFCAnyHandle yDirNeg = null; - IFCAnyHandle zDirPos = null; - IFCAnyHandle zDirNeg = null; - - IFCFile file = exporterIFC.GetFile(); - IList xxp = new List(); - xxp.Add(1.0); xxp.Add(0.0); xxp.Add(0.0); - xDirPos = IFCInstanceExporter.CreateDirection(file, xxp); - - IList xxn = new List(); - xxn.Add(-1.0); xxn.Add(0.0); xxn.Add(0.0); - xDirNeg = IFCInstanceExporter.CreateDirection(file, xxn); - - IList yyp = new List(); - yyp.Add(0.0); yyp.Add(1.0); yyp.Add(0.0); - yDirPos = IFCInstanceExporter.CreateDirection(file, yyp); - - IList yyn = new List(); - yyn.Add(0.0); yyn.Add(-1.0); yyn.Add(0.0); - yDirNeg = IFCInstanceExporter.CreateDirection(file, yyn); - - IList zzp = new List(); - zzp.Add(0.0); zzp.Add(0.0); zzp.Add(1.0); - zDirPos = IFCInstanceExporter.CreateDirection(file, zzp); - - IList zzn = new List(); - zzn.Add(0.0); zzn.Add(0.0); zzn.Add(-1.0); - zDirNeg = IFCInstanceExporter.CreateDirection(file, zzn); - - ExporterIFCUtils.SetGlobal3DDirectionHandles(true, xDirPos, yDirPos, zDirPos); - ExporterIFCUtils.SetGlobal3DDirectionHandles(false, xDirNeg, yDirNeg, zDirNeg); - } - - /// - /// Creates the global direction and sets the cardinal directions in 2D. - /// - /// The IFC exporter object. - private void CreateGlobalDirection2D(ExporterIFC exporterIFC) - { - IFCAnyHandle xDirPos2D = null; - IFCAnyHandle xDirNeg2D = null; - IFCAnyHandle yDirPos2D = null; - IFCAnyHandle yDirNeg2D = null; - IFCFile file = exporterIFC.GetFile(); - - IList xxp = new List(); - xxp.Add(1.0); xxp.Add(0.0); - xDirPos2D = IFCInstanceExporter.CreateDirection(file, xxp); - - IList xxn = new List(); - xxn.Add(-1.0); xxn.Add(0.0); - xDirNeg2D = IFCInstanceExporter.CreateDirection(file, xxn); - - IList yyp = new List(); - yyp.Add(0.0); yyp.Add(1.0); - yDirPos2D = IFCInstanceExporter.CreateDirection(file, yyp); - - IList yyn = new List(); - yyn.Add(0.0); yyn.Add(-1.0); - yDirNeg2D = IFCInstanceExporter.CreateDirection(file, yyn); - ExporterIFCUtils.SetGlobal2DDirectionHandles(true, xDirPos2D, yDirPos2D); - ExporterIFCUtils.SetGlobal2DDirectionHandles(false, xDirNeg2D, yDirNeg2D); - } - - /// - /// Creates the global cartesian origin then sets the 3D and 2D origins. - /// - /// The IFC exporter object. - private void CreateGlobalCartesianOrigin(ExporterIFC exporterIFC) - { - - IFCAnyHandle origin2d = null; - IFCAnyHandle origin = null; - - IFCFile file = exporterIFC.GetFile(); - IList measure = new List(); - measure.Add(0.0); measure.Add(0.0); measure.Add(0.0); - origin = IFCInstanceExporter.CreateCartesianPoint(file, measure); - - IList measure2d = new List(); - measure2d.Add(0.0); measure2d.Add(0.0); - origin2d = IFCInstanceExporter.CreateCartesianPoint(file, measure2d); - ExporterIFCUtils.SetGlobal3DOriginHandle(origin); - ExporterIFCUtils.SetGlobal2DOriginHandle(origin2d); - } - - private static bool ValidateContainedHandle(IFCAnyHandle initialHandle) - { - if (ExporterCacheManager.ElementsInAssembliesCache.Contains(initialHandle)) - return false; - - try - { - if (!IFCAnyHandleUtil.HasRelDecomposes(initialHandle)) - return true; - } - catch - { - } - - return false; - } - - /// - /// Remove contained or invalid handles from this set. - /// - /// The initial set that may have contained or invalid handles. - /// A cleaned set. - public static HashSet RemoveContainedHandlesFromSet(ICollection initialSet) - { - HashSet filteredSet = new HashSet(); - - if (initialSet != null) - { - foreach (IFCAnyHandle initialHandle in initialSet) - { - if (ValidateContainedHandle(initialHandle)) - filteredSet.Add(initialHandle); - } - } - - return filteredSet; - } - - private class IFCLevelExportInfo - { - public IFCLevelExportInfo() { } - - public IDictionary> LevelMapping { get; set; } = - new Dictionary>(); - - public IList OrphanedLevelInfos { get; set; } = new List(); - - public void UnionLevelInfoRelated(ElementId toLevelId, IFCLevelInfo fromLevel) - { - if (fromLevel == null) - return; - - if (toLevelId == ElementId.InvalidElementId) - { - OrphanedLevelInfos.Add(fromLevel); - return; - } - - IList levelMappingList; - if (!LevelMapping.TryGetValue(toLevelId, out levelMappingList)) - { - levelMappingList = new List(); - LevelMapping[toLevelId] = levelMappingList; - } - levelMappingList.Add(fromLevel); - } - - public void TransferOrphanedLevelInfo(ElementId toLevelId) - { - if (toLevelId == ElementId.InvalidElementId) - return; + if (toLevelId == ElementId.InvalidElementId) + return; if (OrphanedLevelInfos.Count == 0) return; @@ -4672,48 +3612,86 @@ private IFCAnyHandle CreateBuildingPlacement(IFCFile file) return IFCInstanceExporter.CreateLocalPlacement(file, null, ExporterUtil.CreateAxis2Placement3D(file)); } - private IFCAnyHandle CreateBuildingFromProjectInfo(ExporterIFC exporterIFC, Document document, IFCAnyHandle buildingPlacement) + private IFCAnyHandle CreateFacilityFromProjectInfo(ExporterIFC exporterIFC, Document document, + IFCAnyHandle facilityPlacement, bool allowBuildingExport) { + KnownFacilityTypes facilityType = ExporterCacheManager.ExportOptionsCache.FacilityType; + bool exportingBuilding = facilityType == KnownFacilityTypes.Building; + if (exportingBuilding && !allowBuildingExport) + return null; + + COBieProjectInfo cobieProjectInfo = ExporterCacheManager.ExportOptionsCache.COBieProjectInfo; + bool exportingCOBIE = exportingBuilding && + ExporterCacheManager.ExportOptionsCache.ExportAs2x3COBIE24DesignDeliverable && + cobieProjectInfo != null; + ProjectInfo projectInfo = document.ProjectInformation; IFCAnyHandle ownerHistory = ExporterCacheManager.OwnerHistoryHandle; - string buildingName = string.Empty; - string buildingDescription = null; - string buildingLongName = null; - string buildingObjectType = null; + string facilityName = string.Empty; + string facilityDescription = null; + string facilityLongName = null; + string facilityObjectType = null; - COBieProjectInfo cobieProjectInfo = ExporterCacheManager.ExportOptionsCache.COBieProjectInfo; - if (ExporterCacheManager.ExportOptionsCache.ExportAs2x3COBIE24DesignDeliverable && cobieProjectInfo != null) + if (exportingCOBIE) { - buildingName = cobieProjectInfo.BuildingName_Number; - buildingDescription = cobieProjectInfo.BuildingDescription; + facilityName = cobieProjectInfo.BuildingName_Number; + facilityDescription = cobieProjectInfo.BuildingDescription; } else if (projectInfo != null) { try { - buildingName = projectInfo.BuildingName; + facilityName = projectInfo.BuildingName; } catch (Autodesk.Revit.Exceptions.InvalidOperationException) { } - buildingDescription = NamingUtil.GetOverrideStringValue(projectInfo, "BuildingDescription", null); - buildingLongName = NamingUtil.GetOverrideStringValue(projectInfo, "BuildingLongName", buildingName); - buildingObjectType = NamingUtil.GetOverrideStringValue(projectInfo, "BuildingObjectType", null); + + facilityDescription = NamingUtil.GetOverrideStringValue(projectInfo, "FacilityDescription", null); + facilityLongName = NamingUtil.GetOverrideStringValue(projectInfo, "FacilityLongName", facilityName); + facilityObjectType = NamingUtil.GetOverrideStringValue(projectInfo, "FacilityObjectType", null); } IFCFile file = exporterIFC.GetFile(); - IFCAnyHandle address = null; - if (Exporter.NeedToCreateAddressForBuilding(document)) - address = Exporter.CreateIFCAddress(file, document, projectInfo); - - string buildingGUID = GUIDUtil.CreateProjectLevelGUID(document, GUIDUtil.ProjectLevelGUIDType.Building); - IFCAnyHandle buildingHandle = IFCInstanceExporter.CreateBuilding(exporterIFC, - buildingGUID, ownerHistory, buildingName, buildingDescription, buildingObjectType, buildingPlacement, null, buildingLongName, - Toolkit.IFCElementComposition.Element, null, null, address); - ExporterCacheManager.BuildingHandle = buildingHandle; - - if (ExporterCacheManager.ExportOptionsCache.ExportAs2x3COBIE24DesignDeliverable && cobieProjectInfo != null) + IFCAnyHandle address = exportingBuilding && NeedToCreateAddressForBuilding(document) ? + CreateIFCAddress(file, document, projectInfo) : null; + + string facilityPredefinedType = ExporterCacheManager.ExportOptionsCache.FacilityPredefinedType; + + string facilityGUID = GUIDUtil.CreateProjectLevelGUID(document, GUIDUtil.ProjectLevelGUIDType.Building); + IFCAnyHandle facilityHandle = null; + + switch (facilityType) + { + case KnownFacilityTypes.Building: + facilityHandle = IFCInstanceExporter.CreateBuilding(exporterIFC, + facilityGUID, ownerHistory, facilityName, facilityDescription, facilityObjectType, facilityPlacement, null, + facilityLongName, IFCElementComposition.Element, null, null, address); + break; + case KnownFacilityTypes.Bridge: + facilityHandle =IFCInstanceExporter.CreateBridge(exporterIFC, + facilityGUID, ownerHistory, facilityName, facilityDescription, facilityObjectType, facilityPlacement, null, + facilityLongName, IFCElementComposition.Element, facilityPredefinedType); + break; + case KnownFacilityTypes.MarineFacility: + facilityHandle = IFCInstanceExporter.CreateMarineFacility(exporterIFC, + facilityGUID, ownerHistory, facilityName, facilityDescription, facilityObjectType, facilityPlacement, null, + facilityLongName, IFCElementComposition.Element, facilityPredefinedType); + break; + case KnownFacilityTypes.Road: + facilityHandle = IFCInstanceExporter.CreateRoad(exporterIFC, + facilityGUID, ownerHistory, facilityName, facilityDescription, facilityObjectType, facilityPlacement, null, + facilityLongName, IFCElementComposition.Element, facilityPredefinedType); + break; + case KnownFacilityTypes.Railway: + facilityHandle = IFCInstanceExporter.CreateRailway(exporterIFC, + facilityGUID, ownerHistory, facilityName, facilityDescription, facilityObjectType, facilityPlacement, null, + facilityLongName, IFCElementComposition.Element, facilityPredefinedType); + break; + } + + if (exportingCOBIE) { string classificationParamValue = cobieProjectInfo.BuildingType; @@ -4723,16 +3701,17 @@ private IFCAnyHandle CreateBuildingFromProjectInfo(ExporterIFC exporterIFC, Docu { string relGuidName = classificationItemCode + ":" + classificationItemName; string relGuid = GUIDUtil.GenerateIFCGuidFrom( - GUIDUtil.CreateGUIDString(IFCEntityType.IfcRelAssociatesClassification, relGuidName, - buildingHandle)); - ClassificationReferenceKey key = new ClassificationReferenceKey(null, + GUIDUtil.CreateGUIDString(IFCEntityType.IfcRelAssociatesClassification, relGuidName, + facilityHandle)); + ClassificationReferenceKey key = new ClassificationReferenceKey(null, classificationItemCode, classificationItemName, null, null); - ExporterCacheManager.ClassificationCache.AddRelation(file, key, relGuid, - "BuildingType", buildingHandle); + ExporterCacheManager.ClassificationCache.AddRelation(file, key, relGuid, + "BuildingType", facilityHandle); } } - return buildingHandle; + ExporterCacheManager.BuildingHandle = facilityHandle; + return facilityHandle; } /// @@ -4998,9 +3977,7 @@ private bool RememberWCSOrGeoReference (double eastings, double northings, doubl Element baseEquipment = systemElem.BaseEquipment; if (baseEquipment != null) { - IFCAnyHandle memberHandle = ExporterCacheManager.MEPCache.Find(baseEquipment.Id); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(memberHandle)) - system.Value.Add(memberHandle); + system.Value.AddIfNotNull(ExporterCacheManager.MEPCache.Find(baseEquipment.Id)); } if (isElectricalSystem) @@ -5012,9 +3989,7 @@ private bool RememberWCSOrGeoReference (double eastings, double northings, doubl ElementSet members = systemElem.Elements; foreach (Element member in members) { - IFCAnyHandle memberHandle = ExporterCacheManager.MEPCache.Find(member.Id); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(memberHandle)) - system.Value.Add(memberHandle); + system.Value.AddIfNotNull(ExporterCacheManager.MEPCache.Find(member.Id)); } } catch @@ -5028,8 +4003,7 @@ private bool RememberWCSOrGeoReference (double eastings, double northings, doubl ElementType systemElemType = doc.GetElement(systemElem.GetTypeId()) as ElementType; string name = NamingUtil.GetNameOverride(systemElem, systemElem.Name); string desc = NamingUtil.GetDescriptionOverride(systemElem, null); - string objectType = NamingUtil.GetObjectTypeOverride(systemElem, - (systemElemType != null) ? systemElemType.Name : ""); + string objectType = NamingUtil.GetObjectTypeOverride(systemElem, systemElemType?.Name ?? string.Empty); string systemGUID = GUIDUtil.CreateGUID(systemElem); IFCAnyHandle systemHandle = null; @@ -5044,7 +4018,7 @@ private bool RememberWCSOrGeoReference (double eastings, double northings, doubl string ifcEnumType; IFCExportInfoPair exportAs = ExporterUtil.GetObjectExportType(exporterIFC, systemElem, out ifcEnumType); - string predefinedType = exportAs.ValidatedPredefinedType; + string predefinedType = exportAs.PredefinedType; if (predefinedType == null) { Toolkit.IFC4.IFCDistributionSystem systemType = ConnectorExporter.GetMappedIFCDistributionSystemFromElement(systemElem); diff --git a/Source/Revit.IFC.Export/Exporter/ExporterInitializer.cs b/Source/Revit.IFC.Export/Exporter/ExporterInitializer.cs index 59342d9e..7a0be1c2 100644 --- a/Source/Revit.IFC.Export/Exporter/ExporterInitializer.cs +++ b/Source/Revit.IFC.Export/Exporter/ExporterInitializer.cs @@ -25,7 +25,6 @@ using Revit.IFC.Export.Utility; using Revit.IFC.Common.Enums; using Revit.IFC.Common.Utility; -using GeometryGym.Ifc; namespace Revit.IFC.Export.Exporter { @@ -189,231 +188,85 @@ private static void InitUserDefinedPropertySets(IList quantityDescriptions = new List(); // get the Pset definitions (using the same file as PropertyMap) - IEnumerable userDefinedPsetDefs = PropertyMap.LoadUserDefinedPset(); - PropertyValueType propValueType = PropertyValueType.SingleValue; - + IEnumerable userDefinedPsetDefs = PropertyMap.LoadUserDefinedPset(); bool exportPre4 = (ExporterCacheManager.ExportOptionsCache.ExportAs2x2 || ExporterCacheManager.ExportOptionsCache.ExportAs2x3); // Loop through each definition and add the Pset entries into Cache - foreach (IfcPropertySetTemplate psetDef in userDefinedPsetDefs) + foreach (UserDefinedPropertySet propertySet in userDefinedPsetDefs) { // Add Propertyset entry Description description = null; - BuiltInParameter builtInParameter = BuiltInParameter.INVALID; - if (string.Compare(psetDef.Name, "Attribute Mapping", true) == 0) + if (string.Compare(propertySet.Name, "Attribute Mapping", true) == 0) { AttributeSetDescription attributeDescription = new AttributeSetDescription(); ExporterCacheManager.AttributeCache.AddAttributeSet(attributeDescription); - foreach (IfcPropertyTemplate prop in psetDef.HasPropertyTemplates.Values) + foreach (UserDefinedProperty property in propertySet.Properties) { - IfcSimplePropertyTemplate template = prop as IfcSimplePropertyTemplate; - if (template != null) - { - PropertyType dataType; - if (!Enum.TryParse(template.PrimaryMeasureType.ToLower().Replace("ifc", ""), true, out dataType)) - { - dataType = PropertyType.Text; - } - List mappings = new List(); - foreach (IfcRelAssociates associates in template.HasAssociations) - { - IfcRelAssociatesClassification associatesClassification = associates as IfcRelAssociatesClassification; - if (associatesClassification != null) - { - IfcClassificationReference classificationReference = associatesClassification.RelatingClassification as IfcClassificationReference; - if (classificationReference != null) - { - string id = classificationReference.Identification; - if (id.ToLower().StartsWith("builtinparameter.")) - { - id = id.Substring("BuiltInParameter.".Length); - if (Enum.TryParse(id, out builtInParameter) && builtInParameter != Autodesk.Revit.DB.BuiltInParameter.INVALID) - { - mappings.Add(new AttributeEntryMap(template.Name, builtInParameter)); - } - else - { - // report as error in log when we create log file. - } - } - else - mappings.Add(new AttributeEntryMap(id, BuiltInParameter.INVALID)); - } - } - } - - AttributeEntry aSE = new AttributeEntry(template.Name, dataType, mappings); - attributeDescription.AddEntry(aSE); - } + // Data types to export is not provided or invalid. + if ((property.IfcPropertyTypes?.Count ?? 0) == 0) + continue; + + PropertyType dataType = property.FirstIfcPropertyTypeOrDefault(PropertyType.Text); + List entryMap = property.GetEntryMap((name, parameter) => new AttributeEntryMap(name, parameter)); + AttributeEntry aSE = new AttributeEntry(property.Name, dataType, entryMap); + attributeDescription.AddEntry(aSE); } - description = attributeDescription; + description = attributeDescription; } - else if (psetDef.TemplateType == IfcPropertySetTemplateTypeEnum.QTO_OCCURRENCEDRIVEN || psetDef.TemplateType == IfcPropertySetTemplateTypeEnum.QTO_TYPEDRIVENONLY || psetDef.TemplateType == IfcPropertySetTemplateTypeEnum.QTO_TYPEDRIVENOVERRIDE) + else if (propertySet.Type?.StartsWith("Qto_", StringComparison.InvariantCultureIgnoreCase) ?? false) { QuantityDescription quantityDescription = new QuantityDescription(); quantityDescriptions.Add(quantityDescription); description = quantityDescription; - foreach (IfcPropertyTemplate prop in psetDef.HasPropertyTemplates.Values) + foreach (UserDefinedProperty property in propertySet.Properties) { - IfcSimplePropertyTemplate template = prop as IfcSimplePropertyTemplate; - if (template != null) - { - List mappings = new List(); - foreach (IfcRelAssociates associates in template.HasAssociations) - { - IfcRelAssociatesClassification associatesClassification = associates as IfcRelAssociatesClassification; - if (associatesClassification != null) - { - IfcClassificationReference classificationReference = associatesClassification.RelatingClassification as IfcClassificationReference; - if (classificationReference != null) - { - string id = classificationReference.Identification; - if (id.ToLower().StartsWith("builtinparameter.")) - { - id = id.Substring("BuiltInParameter.".Length); - if (Enum.TryParse(id, out builtInParameter) && builtInParameter != Autodesk.Revit.DB.BuiltInParameter.INVALID) - { - mappings.Add(new QuantityEntryMap(template.Name, builtInParameter)); - } - else - { - // report as error in log when we create log file. - } - } - else - mappings.Add(new QuantityEntryMap(id, BuiltInParameter.INVALID)); - } - } - } - QuantityType quantityType = QuantityType.Real; - switch (template.TemplateType) - { - case IfcSimplePropertyTemplateTypeEnum.Q_AREA: - quantityType = QuantityType.Area; - break; - case IfcSimplePropertyTemplateTypeEnum.Q_LENGTH: - quantityType = QuantityType.PositiveLength; - break; - case IfcSimplePropertyTemplateTypeEnum.Q_VOLUME: - quantityType = QuantityType.Volume; - break; - case IfcSimplePropertyTemplateTypeEnum.Q_WEIGHT: - quantityType = QuantityType.Weight; - break; - default: - quantityType = QuantityType.Real; - break; - } - QuantityEntry quantityEntry = new QuantityEntry(prop.Name, mappings) { QuantityType = quantityType }; - quantityDescription.AddEntry(quantityEntry); - } + // Data types to export is not provided or invalid. + if ((property.IfcPropertyTypes?.Count ?? 0) == 0) + continue; + + QuantityType quantityType = property.FirstIfcPropertyTypeOrDefault(QuantityType.Real); + IList entryMap = property.GetEntryMap((name, parameter) => new QuantityEntryMap(name, parameter)); + QuantityEntry quantityEntry = new QuantityEntry(property.Name, entryMap) { QuantityType = quantityType }; + quantityDescription.AddEntry(quantityEntry); } } else { PropertySetDescription userDefinedPropertySet = new PropertySetDescription(); description = userDefinedPropertySet; - foreach (IfcPropertyTemplate prop in psetDef.HasPropertyTemplates.Values) + foreach(UserDefinedProperty property in propertySet.Properties) { - IfcSimplePropertyTemplate template = prop as IfcSimplePropertyTemplate; - if (template != null) + PropertyType primaryType = property.FirstIfcPropertyTypeOrDefault(PropertyType.Text); // force default to Text/string if the type does not match with any correct datatype + PropertyType secondaryType = property.GetIfcPropertyAtOrDefault(1, PropertyType.Text); + IList entryMap = property.GetEntryMap((name, parameter) => new PropertySetEntryMap(name, parameter)); + if (entryMap.Count > 0) { - IfcValue defaultValue = null; - PropertyType dataType; - if (!Enum.TryParse(template.PrimaryMeasureType.ToLower().Replace("ifc", ""), true, out dataType)) - { - dataType = PropertyType.Text; // force default to Text/string if the type does not match with any correct datatype - } - - PropertyType secondaryDataType; - if (!Enum.TryParse(template.SecondaryMeasureType.ToLower().Replace("ifc", ""), true, out secondaryDataType)) - { - secondaryDataType = PropertyType.Text; // force default to Text/string if the type does not match with any correct datatype - } - List mappings = new List(); - foreach (IfcRelAssociates associates in template.HasAssociations) - { - IfcRelAssociatesClassification associatesClassification = associates as IfcRelAssociatesClassification; - if (associatesClassification != null) - { - IfcClassificationReference classificationReference = associatesClassification.RelatingClassification as IfcClassificationReference; - if (classificationReference != null) - { - string id = classificationReference.Identification; - if (id.ToLower().StartsWith("builtinparameter.")) - { - id = id.Substring("BuiltInParameter.".Length); - if (Enum.TryParse(id, out builtInParameter) && builtInParameter != Autodesk.Revit.DB.BuiltInParameter.INVALID) - { - mappings.Add(new PropertySetEntryMap(template.Name, builtInParameter)); - } - else - { - // report as error in log when we create log file. - } - } - else - mappings.Add(new PropertySetEntryMap(id, BuiltInParameter.INVALID)); - } - } - else - { - IfcRelAssociatesConstraint associatesConstraint = associates as IfcRelAssociatesConstraint; - if (associatesConstraint != null) - { - IfcMetric metric = associatesConstraint.RelatingConstraint as IfcMetric; - if (metric != null) - { - defaultValue = metric.DataValue as IfcValue; - } - } - } - } - if (mappings.Count > 0) - { - PropertySetEntry pSE = new PropertySetEntry(dataType, prop.Name, mappings); - pSE.DefaultValue = defaultValue; - userDefinedPropertySet.AddEntry(pSE); - } - else + PropertySetEntry propertySetEntry = new PropertySetEntry(primaryType, property.Name, entryMap); + userDefinedPropertySet.AddEntry(propertySetEntry); + } + else + { + PropertySetEntry propertySetEntry = new PropertySetEntry(property.Name) { - switch (template.TemplateType) - { - case IfcSimplePropertyTemplateTypeEnum.P_LISTVALUE: - propValueType = PropertyValueType.ListValue; - break; - case IfcSimplePropertyTemplateTypeEnum.P_BOUNDEDVALUE: - propValueType = PropertyValueType.BoundedValue; - break; - case IfcSimplePropertyTemplateTypeEnum.P_TABLEVALUE: - propValueType = PropertyValueType.TableValue; - break; - default: - propValueType = PropertyValueType.SingleValue; - break; - } - - PropertySetEntry pSE = new PropertySetEntry(prop.Name); - pSE.PropertyName = prop.Name; - pSE.PropertyType = dataType; - pSE.PropertyArgumentType = secondaryDataType; - pSE.DefaultValue = defaultValue; - pSE.PropertyValueType = propValueType; - userDefinedPropertySet.AddEntry(pSE); - } + PropertyName = property.Name, + PropertyType = primaryType, + PropertyArgumentType = secondaryType, + PropertyValueType = property.IfcPropertyValueType + }; + userDefinedPropertySet.AddEntry(propertySetEntry); } } + userDefinedPropertySets.Add(userDefinedPropertySet); } - description.Name = psetDef.Name; - description.DescriptionOfSet = psetDef.Description; - string[] applicableElements = psetDef.ApplicableEntity.Split(",".ToCharArray()); - foreach (string elem in applicableElements) + description.Name = propertySet.Name; + description.DescriptionOfSet = string.Empty; + + foreach (string elem in propertySet.IfcEntities) { - Common.Enums.IFCEntityType ifcEntity; - if (Enum.TryParse(elem, out ifcEntity)) + if (Enum.TryParse(elem, out IFCEntityType ifcEntity)) { bool usedCompatibleType = false; @@ -445,7 +298,6 @@ private static void InitUserDefinedPropertySets(IList 0) ExporterCacheManager.ParameterCache.Quantities.Add(quantityDescriptions); - } private static bool IsSupportedFieldType(ScheduleFieldType fieldType) diff --git a/Source/Revit.IFC.Export/Exporter/ExtrusionExporter.cs b/Source/Revit.IFC.Export/Exporter/ExtrusionExporter.cs index 146f13b2..7895cb67 100644 --- a/Source/Revit.IFC.Export/Exporter/ExtrusionExporter.cs +++ b/Source/Revit.IFC.Export/Exporter/ExtrusionExporter.cs @@ -278,8 +278,7 @@ private static bool GetCenterAndRadiusOfCurveLoop(CurveLoop curveLoop, out XYZ c private static IFCAnyHandle CreateCircleBasedProfileDefIfPossible(ExporterIFC exporterIFC, string profileName, CurveLoop curveLoop, Transform lcs, XYZ projDir) { - IList curveLoops = new List(); - curveLoops.Add(curveLoop); + IList curveLoops = new List() { curveLoop }; return CreateCircleBasedProfileDefIfPossible(exporterIFC, profileName, curveLoops, lcs, projDir); } @@ -334,58 +333,24 @@ private static bool GetCenterAndRadiusOfCurveLoop(CurveLoop curveLoop, out XYZ c if (ExporterCacheManager.ExportOptionsCache.ExportAs4ReferenceView) { - XYZ xDir = lcs.BasisX; - XYZ yDir = lcs.BasisY; - XYZ zDir = lcs.BasisZ; - XYZ orig = lcs.Origin; - - ctr -= orig; - - IList newCtr = new List(); - newCtr.Add(UnitUtil.ScaleLength(xDir.DotProduct(ctr))); - newCtr.Add(UnitUtil.ScaleLength(yDir.DotProduct(ctr))); - newCtr.Add(UnitUtil.ScaleLength(zDir.DotProduct(ctr))); - - IFCAnyHandle location = IFCInstanceExporter.CreateCartesianPoint(file, newCtr); - XYZ projDirToUse = projDir; - XYZ refDirToUse = new XYZ(1.0, 0.0, 0.0); if (curveLoops[0].HasPlane()) { projDirToUse = curveLoops[0].GetPlane().Normal; - refDirToUse = curveLoops[0].GetPlane().XVec; } - IList axisDir = new List(); - axisDir.Add(projDirToUse.X); - axisDir.Add(projDirToUse.Y); - axisDir.Add(projDirToUse.Z); - IFCAnyHandle axisDirectionOpt = ExporterUtil.CreateDirection(file, axisDir); - - IList refDir = new List(); - refDir.Add(1.0); - refDir.Add(0.0); - refDir.Add(0.0); - IFCAnyHandle refDirectionOpt = ExporterUtil.CreateDirection(file, refDirToUse); - - IFCAnyHandle defPosition = IFCInstanceExporter.CreateAxis2Placement3D(file, location, axisDirectionOpt, refDirectionOpt); - IFCAnyHandle outerCurve = GeometryUtil.CreateIFCCurveFromCurveLoop(exporterIFC, curveLoops[0], lcs, projDirToUse); - //if (MathUtil.IsAlmostZero(innerRadius)) if (numLoops == 1) - return IFCInstanceExporter.CreateArbitraryClosedProfileDef(file, IFCProfileType.Area, profileName, outerCurve); - else { - IFCAnyHandle innerCurve = GeometryUtil.CreateIFCCurveFromCurveLoop(exporterIFC, curveLoops[1], lcs, projDirToUse); - HashSet innerCurves = new HashSet(); - innerCurves.Add(innerCurve); - return IFCInstanceExporter.CreateArbitraryProfileDefWithVoids(file, IFCProfileType.Area, profileName, outerCurve, innerCurves); + return IFCInstanceExporter.CreateArbitraryClosedProfileDef(file, IFCProfileType.Area, profileName, outerCurve); } + + IFCAnyHandle innerCurve = GeometryUtil.CreateIFCCurveFromCurveLoop(exporterIFC, curveLoops[1], lcs, projDirToUse); + HashSet innerCurves = new HashSet() { innerCurve }; + return IFCInstanceExporter.CreateArbitraryProfileDefWithVoids(file, IFCProfileType.Area, profileName, outerCurve, innerCurves); } else { - IList arcs = new List(); - if (numLoops == 2) { XYZ checkCtr; @@ -404,23 +369,25 @@ private static bool GetCenterAndRadiusOfCurveLoop(CurveLoop curveLoop, out XYZ c ctr -= orig; - IList newCtr = new List(); - newCtr.Add(UnitUtil.ScaleLength(xDir.DotProduct(ctr))); - newCtr.Add(UnitUtil.ScaleLength(yDir.DotProduct(ctr))); + IList newCtr = new List() + { + UnitUtil.ScaleLength(xDir.DotProduct(ctr)), + UnitUtil.ScaleLength(yDir.DotProduct(ctr)) + }; IFCAnyHandle location = IFCInstanceExporter.CreateCartesianPoint(file, newCtr); - IList refDir = new List(); - refDir.Add(1.0); - refDir.Add(0.0); + IList refDir = new List() { 1.0, 0.0 }; IFCAnyHandle refDirectionOpt = ExporterUtil.CreateDirection(file, refDir); IFCAnyHandle defPosition = IFCInstanceExporter.CreateAxis2Placement2D(file, location, null, refDirectionOpt); if (MathUtil.IsAlmostZero(innerRadius)) + { return IFCInstanceExporter.CreateCircleProfileDef(file, IFCProfileType.Area, profileName, defPosition, radius); - else - return IFCInstanceExporter.CreateCircleHollowProfileDef(file, IFCProfileType.Area, profileName, defPosition, radius, radius - innerRadius); + } + + return IFCInstanceExporter.CreateCircleHollowProfileDef(file, IFCProfileType.Area, profileName, defPosition, radius, radius - innerRadius); } } @@ -723,17 +690,22 @@ private static bool GetCenterAndRadiusOfCurveLoop(CurveLoop curveLoop, out XYZ c overallWidth, overallDepth, webThickness, flangeThickness, filletRadius); } + /// + /// Check to see if a curve loop is oriented clockwise. + /// + /// The curve loop to check. + /// The direction to compare against. /// true if the curve loop is clockwise, false otherwise. - private static bool SafeIsCurveLoopClockwise(CurveLoop curveLoop, XYZ dir) + private static bool? SafeIsCurveLoopClockwise(CurveLoop curveLoop, XYZ dir) { if (curveLoop == null) - return false; + return null; if (curveLoop.IsOpen()) - return false; + return null; if ((curveLoop.Count() == 1) && !(curveLoop.First().IsBound)) - return false; + return null; return !curveLoop.IsCounterclockwise(dir); } @@ -745,7 +717,7 @@ private static bool SafeIsCurveLoopClockwise(CurveLoop curveLoop, XYZ dir) /// Extrusion direction /// Output parameter for the "corrected" LCS /// True if the LCS is set - private static bool CorrectCurveLoopOrientation(IList curveLoops, XYZ extrDir, out Transform lcs) + public static bool CorrectCurveLoopOrientation(IList curveLoops, XYZ extrDir, out Transform lcs) { lcs = null; int loopSz = curveLoops.Count; @@ -773,8 +745,10 @@ private static bool CorrectCurveLoopOrientation(IList curveLoops, XYZ } else if (firstCurve) { - if (SafeIsCurveLoopClockwise(curveLoop, extrDir)) + if (SafeIsCurveLoopClockwise(curveLoop, extrDir).GetValueOrDefault(false)) + { curveLoop.Flip(); + } try { @@ -790,8 +764,10 @@ private static bool CorrectCurveLoopOrientation(IList curveLoops, XYZ } else { - if (!SafeIsCurveLoopClockwise(curveLoop, extrDir)) + if (!SafeIsCurveLoopClockwise(curveLoop, extrDir).GetValueOrDefault(true)) + { curveLoop.Flip(); + } } firstCurve = false; @@ -1069,9 +1045,8 @@ public static IFCAnyHandle CreateSweptArea(ExporterIFC exporterIFC, string profi { if (isCCW) curveLoop.Flip(); - IFCAnyHandle innerCurve = GeometryUtil.CreateIFCCurveFromCurveLoop(exporterIFC, curveLoop, lcs, sweptDirection); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(innerCurve)) - innerCurves.Add(innerCurve); + innerCurves.AddIfNotNull(GeometryUtil.CreateIFCCurveFromCurveLoop( + exporterIFC, curveLoop, lcs, sweptDirection)); } } @@ -1897,11 +1872,9 @@ private static bool AllowMultipleClipPlanesForCategory(ElementId cuttingElementC // A list of IfcCurve entities. if (ExporterCacheManager.ExportOptionsCache.ExportAs4ReferenceView) { - IFCAnyHandle curveHnd = GeometryUtil.CreatePolyCurveFromCurve(exporterIFC, baseCurve, - extrusionLCS, extrusionDir); profileCurves = new List(); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(curveHnd)) - profileCurves.Add(curveHnd); + profileCurves.AddIfNotNull(GeometryUtil.CreatePolyCurveFromCurve(exporterIFC, + baseCurve, extrusionLCS, extrusionDir)); } else { diff --git a/Source/Revit.IFC.Export/Exporter/FamilyInstanceExporter.cs b/Source/Revit.IFC.Export/Exporter/FamilyInstanceExporter.cs index 41185fc2..8fac0e04 100644 --- a/Source/Revit.IFC.Export/Exporter/FamilyInstanceExporter.cs +++ b/Source/Revit.IFC.Export/Exporter/FamilyInstanceExporter.cs @@ -156,14 +156,13 @@ private static bool IsExtrusionFriendlyType(IFCEntityType entityType) using (IFCTransaction tr = new IFCTransaction(file)) { - string ifcEnumType; - IFCExportInfoPair exportType = ExporterUtil.GetProductExportType(exporterIFC, familyInstance, out ifcEnumType); + IFCExportInfoPair exportType = ExporterUtil.GetProductExportType(exporterIFC, familyInstance, out _); if (exportType.IsUnKnown) return; // TODO: This step now appears to be redundant with the rest of the steps, but to change it is too much of risk of regression. Reserve it for future refactoring - if (ExportGenericToSpecificElement(exporterIFC, familyInstance, ref geometryElement, exportType, ifcEnumType, productWrapper)) + if (ExportGenericToSpecificElement(exporterIFC, familyInstance, ref geometryElement, exportType, productWrapper)) { tr.Commit(); return; @@ -176,7 +175,7 @@ private static bool IsExtrusionFriendlyType(IFCEntityType entityType) // We will not split walls and columns if the assemblyId is set, as we would like to keep the original wall // associated with the assembly, on the level of the assembly. bool splitColumn = (exportType.ExportInstance == IFCEntityType.IfcColumn) && (ExporterCacheManager.ExportOptionsCache.WallAndColumnSplitting) && - (familyInstance.AssemblyInstanceId == ElementId.InvalidElementId); + !ExporterUtil.IsContainedInAssembly(familyInstance); if (splitColumn) { LevelUtil.CreateSplitLevelRangesForElement(exporterIFC, exportType, familyInstance, out levels, out ranges); @@ -185,7 +184,7 @@ private static bool IsExtrusionFriendlyType(IFCEntityType entityType) int numPartsToExport = ranges.Count; if (numPartsToExport == 0) { - ExportFamilyInstanceAsMappedItem(exporterIFC, familyInstance, exportType, ifcEnumType, productWrapper, + ExportFamilyInstanceAsMappedItem(exporterIFC, familyInstance, exportType, productWrapper, ElementId.InvalidElementId, null, null); } else @@ -195,7 +194,7 @@ private static bool IsExtrusionFriendlyType(IFCEntityType entityType) for (int ii = 0; ii < numPartsToExport; ii++) { rangeSetter.IncreaseRangeIndex(); - ExportFamilyInstanceAsMappedItem(exporterIFC, familyInstance, exportType, ifcEnumType, productWrapper, + ExportFamilyInstanceAsMappedItem(exporterIFC, familyInstance, exportType, productWrapper, levels[ii], ranges[ii], null); } } @@ -208,7 +207,7 @@ private static bool IsExtrusionFriendlyType(IFCEntityType entityType) foreach (KeyValuePair levelRange in levelRangeList) { rangeSetter.IncreaseRangeIndex(); - ExportFamilyInstanceAsMappedItem(exporterIFC, familyInstance, exportType, ifcEnumType, productWrapper, levelRange.Key, levelRange.Value, null); + ExportFamilyInstanceAsMappedItem(exporterIFC, familyInstance, exportType, productWrapper, levelRange.Key, levelRange.Value, null); } } } @@ -231,7 +230,7 @@ private static bool IsExtrusionFriendlyType(IFCEntityType entityType) /// The instance element. /// The type element. /// For FamilyInstances, the primary symbol. - /// True if we should ues the instance geometry. + /// True if we should use the instance geometry. /// True if we are exporting parts. /// The entity type information. /// The created list of property sets. @@ -239,7 +238,7 @@ private static bool IsExtrusionFriendlyType(IFCEntityType entityType) public static IFCAnyHandle CreateTypeEntityHandle(ExporterIFC exporterIFC, TypeObjectKey typeKey, ref FamilyTypeInfo typeInfo, DoorWindowInfo doorWindowInfo, IList representations3D, IList trfRepMapList, - IList representations2D, Element familyInstance, ElementType familySymbol, + IList representations2D, IDictionary trfRep2DMapList, Element familyInstance, ElementType familySymbol, ElementType originalFamilySymbol, ElementId overrideLevelId, bool useInstanceGeometry, bool exportParts, IFCExportInfoPair exportType, out HashSet propertySets) { @@ -254,15 +253,22 @@ private static bool IsExtrusionFriendlyType(IFCEntityType entityType) IList repMapList = new List(); { IFCAnyHandle origin = null; + int originTransformIndex = -1; if (representations3D != null) { int num = 0; foreach (IFCAnyHandle rep in representations3D) { if (trfRepMapList[num] == null) + { + originTransformIndex = -1; origin = ExporterUtil.CreateAxis2Placement3D(file); + } else + { + originTransformIndex = num; origin = ExporterUtil.CreateAxis2Placement3D(file, trfRepMapList[num].Origin, trfRepMapList[num].BasisZ, trfRepMapList[num].BasisX); // Used by 'Axis' MappedRepresentation + } repMap3dHnd = IFCInstanceExporter.CreateRepresentationMap(file, origin, rep); repMapList.Add(repMap3dHnd); @@ -276,7 +282,17 @@ private static bool IsExtrusionFriendlyType(IFCEntityType entityType) origin = ExporterUtil.CreateAxis2Placement3D(file); foreach (IFCAnyHandle rep in representations2D) { - repMap2dHnd = IFCInstanceExporter.CreateRepresentationMap(file, origin, rep); + IFCAnyHandle currentOrigin = origin; + if (trfRep2DMapList?.TryGetValue(rep, out Transform rep2DTransform) ?? false) + { + // Add 3D represantation origin transform if set. + if (originTransformIndex >= 0) + rep2DTransform = trfRepMapList[originTransformIndex].Multiply(rep2DTransform); + + currentOrigin = ExporterUtil.CreateAxis2Placement3D(file, rep2DTransform.Origin, rep2DTransform.BasisZ, rep2DTransform.BasisX); + } + + repMap2dHnd = IFCInstanceExporter.CreateRepresentationMap(file, currentOrigin, rep); repMapList.Add(repMap2dHnd); } } @@ -309,37 +325,28 @@ private static bool IsExtrusionFriendlyType(IFCEntityType entityType) { case IFCEntityType.IfcBeam: { - string beamType = exportType.ValidatedPredefinedType; - if (string.IsNullOrEmpty(beamType) || beamType.Equals("NOTDEFINED", StringComparison.InvariantCultureIgnoreCase)) - beamType = "Beam"; - typeStyle = IFCInstanceExporter.CreateBeamType(file, familySymbol, guid, - propertySets, repMapList, beamType); + string beamType = exportType.GetPredefinedTypeOrDefault("Beam"); + typeStyle = IFCInstanceExporter.CreateBeamType(file, familySymbol, guid, propertySets, repMapList, beamType); break; } case IFCEntityType.IfcColumn: { - string columnType = exportType.ValidatedPredefinedType; - if (string.IsNullOrEmpty(columnType) || columnType.Equals("NOTDEFINED", StringComparison.InvariantCultureIgnoreCase)) - columnType = "Column"; + string columnType = exportType.GetPredefinedTypeOrDefault("Column"); typeStyle = IFCInstanceExporter.CreateColumnType(file, familySymbol, guid, propertySets, repMapList, columnType); break; } case IFCEntityType.IfcMember: { - string memberType = exportType.ValidatedPredefinedType; - if (string.IsNullOrEmpty(memberType) || memberType.Equals("NOTDEFINED", StringComparison.InvariantCultureIgnoreCase)) - memberType = "Brace"; + string memberType = exportType.GetPredefinedTypeOrDefault("Brace"); typeStyle = IFCInstanceExporter.CreateMemberType(file, familySymbol, guid, propertySets, repMapList, memberType); break; } case IFCEntityType.IfcDoor: { - IFCAnyHandle doorLining = DoorWindowUtil.CreateDoorLiningProperties(exporterIFC, - familyInstance); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(doorLining)) - propertySets.Add(doorLining); + propertySets.AddIfNotNull(DoorWindowUtil.CreateDoorLiningProperties( + exporterIFC, familyInstance)); IList doorPanels = DoorWindowUtil.CreateDoorPanelProperties(exporterIFC, doorWindowInfo, familyInstance); @@ -364,13 +371,13 @@ private static bool IsExtrusionFriendlyType(IFCEntityType entityType) case IFCEntityType.IfcSpace: { typeStyle = IFCInstanceExporter.CreateSpaceType(file, familySymbol, guid, - propertySets, repMapList, exportType.ValidatedPredefinedType); + propertySets, repMapList, exportType.GetPredefinedTypeOrDefault()); break; } case IFCEntityType.IfcSystemFurnitureElement: { typeStyle = IFCInstanceExporter.CreateSystemFurnitureElementType(file, - familySymbol, guid, propertySets, repMapList, exportType.ValidatedPredefinedType); + familySymbol, guid, propertySets, repMapList, exportType.GetPredefinedTypeOrDefault()); break; } case IFCEntityType.IfcWindow: @@ -378,9 +385,8 @@ private static bool IsExtrusionFriendlyType(IFCEntityType entityType) IFCWindowStyleOperation operationType = DoorWindowUtil.GetIFCWindowStyleOperation(originalFamilySymbol); IFCWindowStyleConstruction constructionType = DoorWindowUtil.GetIFCWindowStyleConstruction(familyInstance); - IFCAnyHandle windowLining = DoorWindowUtil.CreateWindowLiningProperties(exporterIFC, familyInstance, null); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(windowLining)) - propertySets.Add(windowLining); + propertySets.AddIfNotNull(DoorWindowUtil.CreateWindowLiningProperties( + exporterIFC, familyInstance, null)); IList windowPanels = DoorWindowUtil.CreateWindowPanelProperties(exporterIFC, familyInstance, null); @@ -412,7 +418,7 @@ private static bool IsExtrusionFriendlyType(IFCEntityType entityType) { typeStyle = IFCInstanceExporter.CreateFurnitureType(file, familySymbol, guid, propertySets, repMapList, null, null, null, - exportType.ValidatedPredefinedType); + exportType.GetPredefinedTypeOrDefault()); break; } } @@ -429,9 +435,7 @@ private static bool IsExtrusionFriendlyType(IFCEntityType entityType) if (IFCAnyHandleUtil.IsNullOrHasNoValue(typeStyle)) { // This covers many generic types. If we can't find it in the list here, do custom exports. - typeStyle = FamilyExporterUtil.ExportGenericType(exporterIFC, exportType, - exportType.ValidatedPredefinedType, propertySets, repMapList, familyInstance, - familySymbol, guid); + typeStyle = FamilyExporterUtil.ExportGenericType(exporterIFC, exportType, propertySets, repMapList, familyInstance, familySymbol, guid); } if (IFCAnyHandleUtil.IsNullOrHasNoValue(typeStyle)) @@ -453,6 +457,15 @@ private static bool IsExtrusionFriendlyType(IFCEntityType entityType) , new ElementId(BuiltInCategory.OST_PipeCurves) , new ElementId(BuiltInCategory.OST_PipeFitting) }; + + private static readonly HashSet s_LinedOnlySet = new HashSet() + { + new ElementId(BuiltInCategory.OST_DuctAccessory) + , new ElementId(BuiltInCategory.OST_DuctCurves) + , new ElementId(BuiltInCategory.OST_DuctFitting) + , new ElementId(BuiltInCategory.OST_FlexDuctCurves) + }; + private static bool CanHaveInsulationOrLining(IFCExportInfoPair exportType, ElementId categoryId) { // This is intended to reduce the number of exceptions thrown in GetLiningIds and GetInsulationIds. @@ -464,6 +477,16 @@ private static bool CanHaveInsulationOrLining(IFCExportInfoPair exportType, Elem return s_InsulatedOrLinedSet.Contains(categoryId); } + /// + /// Checks if a given category is compatible with having insulation. + /// + /// The category id. + /// True if it supports insulation, false otherwise. + public static bool CategoryCanHaveLining(ElementId categoryId) + { + return s_LinedOnlySet.Contains(categoryId); + } + private static bool CanHaveSystemDefinition(IFCExportInfoPair exportType, ElementId categoryId) { if (exportType.ExportType != IFCEntityType.IfcCableSegmentType && exportType.ExportType != IFCEntityType.IfcCableCarrierSegmentType && @@ -512,12 +535,11 @@ private static bool IsTransformValid(Transform transform) /// The ExporterIFC object. /// The family instance to be exported. /// The export type. - /// The string value represents the IFC type. /// The ProductWrapper. /// The level id. /// The range of this family instance to be exported. public static void ExportFamilyInstanceAsMappedItem(ExporterIFC exporterIFC, FamilyInstance familyInstance, IFCExportInfoPair exportType, - string ifcEnumType, ProductWrapper wrapper, ElementId overrideLevelId, IFCRange range, IFCAnyHandle parentLocalPlacement) + ProductWrapper wrapper, ElementId overrideLevelId, IFCRange range, IFCAnyHandle parentLocalPlacement) { bool exportParts = PartExporter.CanExportParts(familyInstance); bool isSplit = range != null; @@ -561,6 +583,7 @@ private static bool IsTransformValid(Transform transform) IFCExportBodyParams extrusionData = null; IList repMapTrfList = new List(); + IDictionary repMap2DTrfDict = new Dictionary(); XYZ orig = XYZ.Zero; XYZ extrudeDirection = null; @@ -577,7 +600,7 @@ private static bool IsTransformValid(Transform transform) FamilyTypeInfo typeInfo = new FamilyTypeInfo(); IFCExportBodyParams extraParams = typeInfo.extraParams; - exportType = FamilyExporterUtil.AdjustExportTypeForSchema(exportType, exportType.ValidatedPredefinedType); + exportType = FamilyExporterUtil.AdjustExportTypeForSchema(exportType); bool flipped = doorWindowInfo?.FlippedSymbol ?? false; ElementId overrideMaterialId = ExporterUtil.GetSingleMaterial(familyInstance); @@ -688,7 +711,7 @@ private static bool IsTransformValid(Transform transform) // Get a profile name. string profileName = NamingUtil.GetProfileName(familySymbol); - StructuralMemberAxisInfo axisInfo = StructuralMemberExporter.GetStructuralMemberAxisTransform(familyInstance); + StructuralMemberAxisInfo axisInfo = StructuralMemberExporter.GetStructuralMemberAxisTransform(exportGeometryElement); if (axisInfo != null) { orig = axisInfo.LCSAsTransform.Origin; @@ -728,9 +751,8 @@ private static bool IsTransformValid(Transform transform) } typeInfo.MaterialIdList = extraClippingData.MaterialIds; - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(bodyRepresentation)) + if (representations3D.AddIfNotNull(bodyRepresentation)) { - representations3D.Add(bodyRepresentation); repMapTrfList.Add(null); if (materialAndProfile != null) typeInfo.MaterialAndProfile = materialAndProfile; // Keep material and profile information in the type info for later creation @@ -757,9 +779,8 @@ private static bool IsTransformValid(Transform transform) ElementId catId = CategoryUtil.GetSafeCategoryId(familyInstance); IFCAnyHandle axisRep = StructuralMemberExporter.CreateStructuralMemberAxis(exporterIFC, familyInstance, catId, axisInfo, newLCS); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(axisRep)) + if (representations3D.AddIfNotNull(axisRep)) { - representations3D.Add(axisRep); // This offset is going to be applied later. Need to scale the coordinate into the correct unit scale offset.Origin = UnitUtil.ScaleLength(offset.Origin); repMapTrfList.Add(offset); @@ -797,9 +818,8 @@ private static bool IsTransformValid(Transform transform) offsetTransform = bodyData.OffsetTransform; IFCAnyHandle bodyRepHnd = bodyData.RepresentationHnd; - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(bodyRepHnd)) + if (representations3D.AddIfNotNull(bodyRepHnd)) { - representations3D.Add(bodyRepHnd); repMapTrfList.Add(null); } @@ -828,9 +848,8 @@ private static bool IsTransformValid(Transform transform) ElementId catId = CategoryUtil.GetSafeCategoryId(familyInstance); IFCAnyHandle axisRep = StructuralMemberExporter.CreateStructuralMemberAxis(exporterIFC, familyInstance, catId, axisInfo, newLCS); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(axisRep)) + if (representations3D.AddIfNotNull(axisRep)) { - representations3D.Add(axisRep); repMapTrfList.Add(null); } } @@ -904,11 +923,10 @@ private static bool IsTransformValid(Transform transform) curve = curve.CreateTransformed(doorWindowTrf.Multiply(flipTrf)); } - IFCAnyHandle curveHnd = GeometryUtil.CreatePolyCurveFromCurve(exporterIFC, curve); if (curveSet == null) curveSet = new HashSet(); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(curveHnd)) - curveSet.Add(curveHnd); + curveSet.AddIfNotNull(GeometryUtil.CreatePolyCurveFromCurve( + exporterIFC, curve)); } } else @@ -925,12 +943,17 @@ private static bool IsTransformValid(Transform transform) { IFCAnyHandle contextOfItems2d = ExporterCacheManager.Get2DContextHandle(IFCRepresentationIdentifier.Annotation); IFCAnyHandle curveRepresentationItem = IFCInstanceExporter.CreateGeometricSet(file, curveSet); - HashSet bodyItems = new HashSet(); - bodyItems.Add(curveRepresentationItem); - IFCAnyHandle planRepresentation = RepresentationUtil.CreateGeometricSetRep(exporterIFC, familyInstance, categoryId, "FootPrint", - contextOfItems2d, bodyItems); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(planRepresentation)) - representations2D.Add(planRepresentation); + HashSet bodyItems = new HashSet() { curveRepresentationItem }; + IFCAnyHandle curveRep = RepresentationUtil.CreateGeometricSetRep(exporterIFC, familyInstance, categoryId, "FootPrint", contextOfItems2d, bodyItems); + + // Move 2D represantion along Z direction because the local placement of element + // can be shifted to closer to the exported geometry location. + if (representations2D.AddIfNotNull(curveRep) && !MathUtil.IsAlmostZero(curveOffset.Z)) + { + Transform moveTransform = Transform.Identity; + moveTransform.Origin = new XYZ(0, 0, UnitUtil.ScaleLength(curveOffset.Z)); + repMap2DTrfDict.Add(curveRep, moveTransform); + } } } } @@ -964,8 +987,9 @@ private static bool IsTransformValid(Transform transform) { typeStyle = CreateTypeEntityHandle(exporterIFC, typeKey, ref typeInfo, doorWindowInfo, representations3D, repMapTrfList, representations2D, - familyInstance, familySymbol, originalFamilySymbol, overrideLevelId, - useInstanceGeometry, exportParts, exportType, out propertySets); + repMap2DTrfDict, familyInstance, familySymbol, originalFamilySymbol, + overrideLevelId, useInstanceGeometry, exportParts, exportType, + out propertySets); } if (!IFCAnyHandleUtil.IsNullOrHasNoValue(typeStyle)) @@ -1120,42 +1144,51 @@ private static bool IsTransformValid(Transform transform) if ((overrideLevelId == null || overrideLevelId == ElementId.InvalidElementId) && overrideContainerId != ElementId.InvalidElementId) overrideLevelId = overrideContainerId; - if (familyInstance.AssemblyInstanceId != null && familyInstance.AssemblyInstanceId != ElementId.InvalidElementId) + if (ExporterUtil.IsContainedInAssembly(familyInstance)) { - if (ExporterCacheManager.AssemblyInstanceCache.TryGetValue(familyInstance.AssemblyInstanceId, out AssemblyInstanceInfo assInfo)) + if (overrideLevelId == ElementId.InvalidElementId) { - if (overrideLevelId == ElementId.InvalidElementId) - overrideLevelId = assInfo.AssignedLevelId; + overrideLevelId = ExporterCacheManager.LevelInfoCache.GetLevelIdOfObject(doc.GetElement(familyInstance.AssemblyInstanceId)); + } - double newOffset = trf.Origin.Z; - string shapeType = null; - foreach (IFCAnyHandle shapeRep in shapeReps) + // Determine if any of the IfcShapeRepresentations associated with this FamilyInstance are "Body" representations. + string shapeType = null; + foreach (IFCAnyHandle shapeRep in shapeReps) + { + if (IFCAnyHandleUtil.GetRepresentationIdentifier(shapeRep).Equals("Body")) { - if (IFCAnyHandleUtil.GetRepresentationIdentifier(shapeRep).Equals("Body")) - { - shapeType = IFCAnyHandleUtil.GetBaseRepresentationType(shapeRep); - } + shapeType = IFCAnyHandleUtil.GetBaseRepresentationType(shapeRep); } + } - if (!string.IsNullOrEmpty(shapeType) && (shapeType.Contains("Brep") || shapeType.Equals("Tessellation"))) + // Adjust the Origin is in the Family Instance Trf in the Z direction only. + // For BReps and Tessellations, retrieve an adjustment using the FamilySymbol origin. + XYZ adjustedOrigin = trf.Origin; + if (!string.IsNullOrEmpty(shapeType) && (shapeType.Contains("Brep") || shapeType.Equals("Tessellation"))) + { + LocationPoint loc = familyInstance.Location as LocationPoint; + if (loc != null) { - // Use LocationPoint for the offset if any as the Brep/Tessellation will have reference to it - LocationPoint loc = familyInstance.Location as LocationPoint; - if (loc != null) - { - newOffset = loc.Point.Z; - } - else + //XYZ familyInstanceAssemblyOffset = exporterIFC.GetFamilyInstanceAssemblyOffset(familyInstance); + XYZ familyInstanceAssemblyOffset = XYZ.Zero; + LocationPoint familySymbolLocation = familyInstance.Symbol.Location as LocationPoint; + if (familySymbolLocation != null) + familyInstanceAssemblyOffset = XYZ.Zero - familySymbolLocation.Point; + adjustedOrigin = new XYZ(trf.Origin.X, trf.Origin.Y, loc.Point.Z + familyInstanceAssemblyOffset.Z); + } + else + { + BoundingBoxXYZ bbox = familyInstance.get_BoundingBox(null); + if (bbox != null) { - BoundingBoxXYZ bbox = familyInstance.get_BoundingBox(null); - if (bbox != null) - { - newOffset = bbox.Min.Z; - } + adjustedOrigin = new XYZ(trf.Origin.X, trf.Origin.Y, bbox.Min.Z); } } - trf.Origin = new XYZ(trf.Origin.X, trf.Origin.Y, newOffset); + // The current trf includes a style transformation derived from ExtrusionCreationData. + // But it was not utilized for representation creation in Brep/Tessellation cases, + // using the original coordinates instead, to avoid potential incorrect coordinates. + trf.Origin = adjustedOrigin; } } @@ -1183,7 +1216,7 @@ private static bool IsTransformValid(Transform transform) GUIDUtil.CreateGUIDString(familyInstance, subElementIndex.ToString())); IFCAnyHandle overrideLocalPlacement = null; - bool isChildInContainer = familyInstance.AssemblyInstanceId != ElementId.InvalidElementId; + bool isChildInContainer = ExporterUtil.IsContainedInAssembly(familyInstance); if (parentLocalPlacement != null) { @@ -1199,16 +1232,18 @@ private static bool IsTransformValid(Transform transform) { case IFCEntityType.IfcBeam: { - if (exportType.HasUndefinedPredefinedType()) - exportType.ValidatedPredefinedType = "BEAM"; + exportType.SetPredefinedTypeIfNotDefined("BEAM"); instanceHandle = FamilyExporterUtil.ExportGenericInstance(exportType, exporterIFC, familyInstance, wrapper, setter, extraParams, instanceGUID, ownerHistory, exportParts ? null : repHnd, overrideLocalPlacement); - IFCAnyHandle placementToUse = localPlacement; + + IFCAnyHandle placementToUse = GetPlacementToUse(file, instanceHandle, localPlacement, extraParams, originalTrf, + typeInfo.StyleTransform, useInstanceGeometry); + Transform offsetTransformToUse = GetOffsetTransformtoUse(offsetTransform, setter.Offset, useInstanceGeometry); // NOTE: We do not expect openings here, as they are created as part of creating an extrusion in ExportBody above. // However, if this were the case, we would have exported this beam in ExportBeamAsStandardElement above. - OpeningUtil.CreateOpeningsIfNecessary(instanceHandle, familyInstance, extraParams, offsetTransform, + OpeningUtil.CreateOpeningsIfNecessary(instanceHandle, familyInstance, extraParams, offsetTransformToUse, exporterIFC, placementToUse, setter, wrapper); wrapper.AddElement(familyInstance, instanceHandle, setter, extraParams, true, exportType); @@ -1225,8 +1260,7 @@ private static bool IsTransformValid(Transform transform) } case IFCEntityType.IfcColumn: { - if (exportType.HasUndefinedPredefinedType()) - exportType.ValidatedPredefinedType = "COLUMN"; + exportType.SetPredefinedTypeIfNotDefined("COLUMN"); instanceHandle = FamilyExporterUtil.ExportGenericInstance(exportType, exporterIFC, familyInstance, wrapper, setter, extraParams, instanceGUID, ownerHistory, exportParts ? null : repHnd, overrideLocalPlacement); @@ -1301,14 +1335,17 @@ private static bool IsTransformValid(Transform transform) } case IFCEntityType.IfcMember: { - if (exportType.HasUndefinedPredefinedType()) - exportType.ValidatedPredefinedType = "BRACE"; + exportType.SetPredefinedTypeIfNotDefined("BRACE"); instanceHandle = FamilyExporterUtil.ExportGenericInstance(exportType, exporterIFC, familyInstance, wrapper, setter, extraParams, instanceGUID, ownerHistory, exportParts ? null : repHnd, overrideLocalPlacement); - OpeningUtil.CreateOpeningsIfNecessary(instanceHandle, familyInstance, extraParams, offsetTransform, - exporterIFC, localPlacement, setter, wrapper); + IFCAnyHandle placementToUse = GetPlacementToUse(file, instanceHandle, localPlacement, extraParams, originalTrf, + typeInfo.StyleTransform, useInstanceGeometry); + Transform offsetTransformToUse = GetOffsetTransformtoUse(offsetTransform, setter.Offset, useInstanceGeometry); + + OpeningUtil.CreateOpeningsIfNecessary(instanceHandle, familyInstance, extraParams, offsetTransformToUse, + exporterIFC, placementToUse, setter, wrapper); wrapper.AddElement(familyInstance, instanceHandle, setter, extraParams, true, exportType); if (CreateMaterialAssociation(file, instanceHandle, materialProfileSet, null)) @@ -1346,11 +1383,11 @@ private static bool IsTransformValid(Transform transform) if (!ExporterCacheManager.ExportOptionsCache.ExportAsOlderThanIFC4) { // It is PreDefinedType attribute in IFC4 - operationType = FamilyExporterUtil.GetPreDefinedType(familyInstance, familySymbol, ifcEnumType); + operationType = FamilyExporterUtil.GetPreDefinedType(familyInstance, familySymbol, exportType.PredefinedType); } else { - operationType = FamilyExporterUtil.GetPreDefinedType(familyInstance, familySymbol, ifcEnumType); + operationType = FamilyExporterUtil.GetPreDefinedType(familyInstance, familySymbol, exportType.PredefinedType); } string operationTypeStr = operationType.ToString(); @@ -1389,7 +1426,7 @@ private static bool IsTransformValid(Transform transform) else { instanceHandle = IFCInstanceExporter.CreateBuildingElementProxy(exporterIFC, familyInstance, instanceGUID, - ownerHistory, localPlacementToUse, repHnd, exportType.ValidatedPredefinedType); + ownerHistory, localPlacementToUse, repHnd, exportType.GetPredefinedTypeOrDefault()); } bool associateToLevel = !containedInSpace && !isChildInContainer; @@ -1419,7 +1456,10 @@ private static bool IsTransformValid(Transform transform) ExporterCacheManager.MEPCache.Register(familyInstance, instanceHandle); // For ducts and pipes, check later if there is an associated duct or pipe. if (CanHaveInsulationOrLining(exportType, categoryId)) - ExporterCacheManager.MEPCache.CoveredElementsCache.Add(familyInstance.Id); + { + ExporterCacheManager.MEPCache.CoveredElementsCache[familyInstance.Id] = categoryId; + } + // For cable trays and conduits, we might create systems during the end of export. if (CanHaveSystemDefinition(exportType, categoryId)) ExporterCacheManager.MEPCache.CableElementsCache.Add(familyInstance.Id); @@ -1454,7 +1494,7 @@ private static bool IsTransformValid(Transform transform) { DoorWindowDelayedOpeningCreator delayedCreator = DoorWindowDelayedOpeningCreator.Create(exporterIFC, doorWindowInfo, instanceHandle, setter.LevelId); if (delayedCreator != null) - ExporterCacheManager.DoorWindowDelayedOpeningCreatorCache.Add(delayedCreator); + ExporterCacheManager.DoorWindowDelayedOpeningCreatorCache.Add(delayedCreator, false); } } } @@ -1465,12 +1505,11 @@ private static bool IsTransformValid(Transform transform) /// The ExporterIFC object. /// The element to be exported. /// The geometry element. - /// The export type. - /// The string value represents the IFC type. + /// The export type. /// The ProductWrapper. /// True if the elements was exported, false otherwise. static public bool ExportGenericToSpecificElement(ExporterIFC exporterIFC, Element element, ref GeometryElement geometryElement, IFCExportInfoPair exportType, - string ifcEnumTypeString, ProductWrapper productWrapper) + ProductWrapper productWrapper) { // This function is here because it was originally used exclusive by FamilyInstances. Moving forward, this will be combined with some other // functions to attempt to create a way to export any element as any IFC entity. There will still be functions that do a better job of mapping @@ -1498,10 +1537,10 @@ private static bool IsTransformValid(Transform transform) return false; } case IFCEntityType.IfcCovering: - CeilingExporter.ExportCovering(exporterIFC, element, ref geometryElement, ifcEnumTypeString, productWrapper); + CeilingExporter.ExportCovering(exporterIFC, element, ref geometryElement, exportType.PredefinedType, productWrapper); return true; case IFCEntityType.IfcRamp: - RampExporter.ExportRamp(exporterIFC, ifcEnumTypeString, element, geometryElement, 1, productWrapper); + RampExporter.ExportRamp(exporterIFC, exportType.PredefinedType, element, geometryElement, 1, productWrapper); return true; case IFCEntityType.IfcRailing: if (ExporterCacheManager.RailingCache.Contains(element.Id)) @@ -1523,14 +1562,14 @@ private static bool IsTransformValid(Transform transform) RoofExporter.ExportRoof(exporterIFC, element, ref geometryElement, productWrapper); return true; case IFCEntityType.IfcSlab: - FloorExporter.ExportGenericSlab(exporterIFC, element, geometryElement, ifcEnumTypeString, productWrapper); + FloorExporter.ExportGenericSlab(exporterIFC, element, geometryElement, exportType.PredefinedType, productWrapper); //TODO return true; case IFCEntityType.IfcStair: - StairsExporter.ExportStairAsSingleGeometry(exporterIFC, ifcEnumTypeString, element, geometryElement, new List() { 0 }, productWrapper); + StairsExporter.ExportStairAsSingleGeometry(exporterIFC, exportType.PredefinedType, element, geometryElement, new List() { 0 }, productWrapper); return true; case IFCEntityType.IfcWall: - WallExporter.ExportWall(exporterIFC, ifcEnumTypeString, element, null, ref geometryElement, productWrapper); + WallExporter.ExportWall(exporterIFC, exportType.PredefinedType, element, null, ref geometryElement, productWrapper); return true; } return false; diff --git a/Source/Revit.IFC.Export/Exporter/FilledRegionExporter.cs b/Source/Revit.IFC.Export/Exporter/FilledRegionExporter.cs index 3183e220..47055cef 100644 --- a/Source/Revit.IFC.Export/Exporter/FilledRegionExporter.cs +++ b/Source/Revit.IFC.Export/Exporter/FilledRegionExporter.cs @@ -115,7 +115,7 @@ class FilledRegionExporter HashSet bodyItems = new HashSet() { representItem }; IFCAnyHandle context2D = ExporterCacheManager.Get2DContextHandle(IFCRepresentationIdentifier.Annotation); IFCAnyHandle bodyRepHnd = RepresentationUtil.CreateAnnotationSetRep(exporterIFC, filledRegion, categoryId, - context2D, bodyItems); + context2D, bodyItems, false); if (IFCAnyHandleUtil.IsNullOrHasNoValue(bodyRepHnd)) return; diff --git a/Source/Revit.IFC.Export/Exporter/FloorExporter.cs b/Source/Revit.IFC.Export/Exporter/FloorExporter.cs index 7244b144..686c8d85 100644 --- a/Source/Revit.IFC.Export/Exporter/FloorExporter.cs +++ b/Source/Revit.IFC.Export/Exporter/FloorExporter.cs @@ -261,7 +261,7 @@ class FloorExporter // For IFC4RV export, Element will be split into its parts(temporarily) in order to export the wall by its parts // If Parts are created by code and not by user then their names should be equal to Material names. bool setMaterialNameToPartName = ExporterUtil.CreateParts(floorElement, layersetInfo.MaterialIds.Count, ref geometryElement); - ExporterUtil.ExportPartAs exportPartAs = ExporterUtil.CanExportByComponentsOrParts(floorElement); + ExporterUtil.ExportPartAs exportPartAs = ExporterUtil.CanExportByComponentsOrParts(floorElement, ref geometryElement); bool exportByComponents = exportPartAs == ExporterUtil.ExportPartAs.ShapeAspect; bool exportParts = exportPartAs == ExporterUtil.ExportPartAs.Part; @@ -363,7 +363,7 @@ class FloorExporter ExtrusionExporter.ExtraClippingData extraClippingData = null; HandleAndData floorAndProperties = ExtrusionExporter.CreateExtrusionWithClippingAndProperties(exporterIFC, floorElement, false, - catId, solids[0], extrusionAnalyzerFloorBasePlane, floorOrigin, floorExtrusionDirection, null, + catId, solids[0], extrusionAnalyzerFloorBasePlane, floorOrigin, floorExtrusionDirection, null, out extraClippingData, addInfo: additionalInfo); if (extraClippingData.CompletelyClipped) @@ -383,19 +383,25 @@ class FloorExporter representations.Add(footprintShapeRep); } + IFCAnyHandle prodRep = null; if (exportByComponents) { - IFCAnyHandle prodRep = RepresentationUtil.CreateProductDefinitionShapeWithoutBodyRep(exporterIFC, floorElement, catId, geometryElement, representations); - prodReps.Add(prodRep); + prodRep = RepresentationUtil.CreateProductDefinitionShapeWithoutBodyRep(exporterIFC, floorElement, catId, geometryElement, representations); } else if (representations.Count > 0 && floorAndProperties.Handle != null) // Only when at least the body rep exists will come here { - IFCAnyHandle prodRep = IFCInstanceExporter.CreateProductDefinitionShape(file, null, null, representations); + prodRep = IFCInstanceExporter.CreateProductDefinitionShape(file, null, null, representations); + } + + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(prodRep)) + { prodReps.Add(prodRep); } if (floorAndProperties.Data != null) + { loopExtraParams.Add(floorAndProperties.Data); + } } } } @@ -410,7 +416,7 @@ class FloorExporter canExportAsInternalExtrusion = openingDataList == null || openingDataList.Count == 0; } - if (canExportAsInternalExtrusion && ExporterCacheManager.ExportOptionsCache.ExportAsOlderThanIFC4x3) + if (canExportAsInternalExtrusion) { loopExtraParams.Clear(); IList extrusionParams = new List(); @@ -464,7 +470,10 @@ class FloorExporter if (exportByComponents) { prodDefHnd = RepresentationUtil.CreateProductDefinitionShapeWithoutBodyRep(exporterIFC, floorElement, catId, geometryElement, null); - prodReps.Add(prodDefHnd); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(prodDefHnd)) + { + prodReps.Add(prodDefHnd); + } } else { @@ -494,7 +503,7 @@ class FloorExporter switch (exportType.ExportInstance) { case IFCEntityType.IfcCovering: - exportType.ValidatedPredefinedType = IFCValidateEntry.GetValidIFCType(floorElement, ifcEnumType, "FLOORING"); + exportType.PredefinedType = IFCValidateEntry.GetValidIFCType(floorElement, ifcEnumType, "FLOORING"); break; case IFCEntityType.IfcSlab: { @@ -515,7 +524,7 @@ class FloorExporter } } - exportType.ValidatedPredefinedType = IFCValidateEntry.GetValidIFCType(floorElement, ifcEnumType, isBaseSlab ? "BASESLAB" : "FLOOR"); + exportType.PredefinedType = IFCValidateEntry.GetValidIFCType(floorElement, ifcEnumType, isBaseSlab ? "BASESLAB" : "FLOOR"); } break; } @@ -577,7 +586,7 @@ class FloorExporter for (int ii = 0; ii < numReps; ii++) { - IFCExportBodyParams loopExtraParam = ii < loopExtraParams.Count ? loopExtraParams[ii] : null; + IFCExportBodyParams loopExtraParam = ii < loopExtraParams.Count ? loopExtraParams[ii] : ecData; productWrapper.AddElement(floorElement, slabHnds[ii], placementSetter, loopExtraParam, true, exportType); ExporterCacheManager.TypeRelationsCache.Add(typeHandle, slabHnds[ii]); diff --git a/Source/Revit.IFC.Export/Exporter/GenericElementExporter.cs b/Source/Revit.IFC.Export/Exporter/GenericElementExporter.cs index 5b9be076..08e3fb55 100644 --- a/Source/Revit.IFC.Export/Exporter/GenericElementExporter.cs +++ b/Source/Revit.IFC.Export/Exporter/GenericElementExporter.cs @@ -32,7 +32,7 @@ class GenericElementExporter // Check the intended IFC entity or type name is in the exclude list specified in the UI if (exportType.ExportInstance == IFCEntityType.UnKnown) - exportType.SetValueWithPair(IFCEntityType.IfcBuildingElementProxy, exportType.ValidatedPredefinedType); + exportType.SetByTypeAndPredefinedType(IFCEntityType.IfcBuildingElementProxy, exportType.PredefinedType); if (ExporterCacheManager.ExportOptionsCache.IsElementInExcludeList(exportType.ExportInstance)) return null; @@ -78,11 +78,8 @@ class GenericElementExporter HashSet propertySetsOpt = new HashSet(); IList repMapListOpt = new List(); - string typeGuid = FamilyExporterUtil.GetGUIDForFamilySymbol(element as FamilyInstance, - familySymbol, exportType); - styleHandle = FamilyExporterUtil.ExportGenericType(exporterIFC, exportType, - exportType.ValidatedPredefinedType, propertySetsOpt, repMapListOpt, - element, familySymbol, typeGuid); + string typeGuid = FamilyExporterUtil.GetGUIDForFamilySymbol(element as FamilyInstance, familySymbol, exportType); + styleHandle = FamilyExporterUtil.ExportGenericType(exporterIFC, exportType, propertySetsOpt, repMapListOpt, element, familySymbol, typeGuid); productWrapper.RegisterHandleWithElementType(familySymbol, exportType, styleHandle, propertySetsOpt); } @@ -219,7 +216,7 @@ private static GeometryInstance GetTheGeometryInstance(GeometryElement geomElem) extraParams.GetLocalPlacement()); IFCAnyHandle typeStyle = FamilyInstanceExporter.CreateTypeEntityHandle(exporterIFC, - typeKey, ref typeInfo, null, representations3D, repMapTrfList, null, + typeKey, ref typeInfo, null, representations3D, repMapTrfList, null, null, element, elementType, elementType, ElementId.InvalidElementId, false, false, exportType, out HashSet propertySets); @@ -283,7 +280,7 @@ private static GeometryInstance GetTheGeometryInstance(GeometryElement geomElem) { string instanceGUID = GUIDUtil.CreateGUID(element); - bool isChildInContainer = element.AssemblyInstanceId != ElementId.InvalidElementId; + bool isChildInContainer = ExporterUtil.IsContainedInAssembly(element); if (IFCAnyHandleUtil.IsNullOrHasNoValue(instanceHandle)) { @@ -304,7 +301,7 @@ private static GeometryInstance GetTheGeometryInstance(GeometryElement geomElem) else { instanceHandle = IFCInstanceExporter.CreateBuildingElementProxy(exporterIFC, element, instanceGUID, - ownerHistory, localPlacementToUse, repHnd, exportType.ValidatedPredefinedType); + ownerHistory, localPlacementToUse, repHnd, exportType.GetPredefinedTypeOrDefault()); } bool associateToLevel = !containedInSpace && !isChildInContainer; @@ -358,8 +355,7 @@ private static GeometryInstance GetTheGeometryInstance(GeometryElement geomElem) public static bool ExportElement(ExporterIFC exporterIFC, Element element, GeometryElement geometryElement, ProductWrapper productWrapper) { - string ifcEnumType; - IFCExportInfoPair exportType = ExporterUtil.GetProductExportType(exporterIFC, element, out ifcEnumType); + IFCExportInfoPair exportType = ExporterUtil.GetProductExportType(exporterIFC, element, out _); // Check the intended IFC entity or type name is in the exclude list specified in the UI IFCEntityType elementClassTypeEnum; @@ -373,7 +369,7 @@ private static GeometryInstance GetTheGeometryInstance(GeometryElement geomElem) return true; if (FamilyInstanceExporter.ExportGenericToSpecificElement(exporterIFC, - element, ref geometryElement, exportType, ifcEnumType, productWrapper)) + element, ref geometryElement, exportType, productWrapper)) return true; return (ExportSimpleGenericElement(exporterIFC, element, geometryElement, productWrapper, diff --git a/Source/Revit.IFC.Export/Exporter/GenericMEPExporter.cs b/Source/Revit.IFC.Export/Exporter/GenericMEPExporter.cs index 37588788..d01dd68c 100644 --- a/Source/Revit.IFC.Export/Exporter/GenericMEPExporter.cs +++ b/Source/Revit.IFC.Export/Exporter/GenericMEPExporter.cs @@ -59,7 +59,7 @@ class GenericMEPExporter // associated with the assembly, on the level of the assembly. if ((exportType.ExportType == IFCEntityType.IfcDuctSegmentType) && (ExporterCacheManager.ExportOptionsCache.WallAndColumnSplitting) && - (element.AssemblyInstanceId == ElementId.InvalidElementId)) + !ExporterUtil.IsContainedInAssembly(element)) { LevelUtil.CreateSplitLevelRangesForElement(exporterIFC, exportType, element, out levels, out ranges); @@ -207,8 +207,7 @@ class GenericMEPExporter IList repMapListOpt = new List(); string typeGuid = FamilyExporterUtil.GetGUIDForFamilySymbol(element as FamilyInstance, type, exportType); - styleHandle = FamilyExporterUtil.ExportGenericType(exporterIFC, exportType, ifcEnumType, - propertySetsOpt, repMapListOpt, element, type, typeGuid); + styleHandle = FamilyExporterUtil.ExportGenericType(exporterIFC, exportType, propertySetsOpt, repMapListOpt, element, type, typeGuid); if (!IFCAnyHandleUtil.IsNullOrHasNoValue(styleHandle)) { productWrapper.RegisterHandleWithElementType(type, exportType, styleHandle, null); diff --git a/Source/Revit.IFC.Export/Exporter/GridExporter.cs b/Source/Revit.IFC.Export/Exporter/GridExporter.cs index e1840e0b..8fe6a0ee 100644 --- a/Source/Revit.IFC.Export/Exporter/GridExporter.cs +++ b/Source/Revit.IFC.Export/Exporter/GridExporter.cs @@ -440,15 +440,18 @@ public class GridRepresentationData if (currentLevel != null) { - levelIds.Add(currentLevel.Elevation, currentLevel.Id); + levelIds.Add(currentLevel.ProjectElevation, currentLevel.Id); } else { foreach (ElementId levelId in ExporterCacheManager.LevelInfoCache.BuildingStoriesByElevation) { Level level = document.GetElement(levelId) as Level; - if (!levelIds.ContainsKey(level.Elevation)) - levelIds.Add(level.Elevation, levelId); + double? projectElevation = level?.ProjectElevation; + if (projectElevation.HasValue && !levelIds.ContainsKey(projectElevation.Value)) + { + levelIds.Add(projectElevation.Value, levelId); + } } } diff --git a/Source/Revit.IFC.Export/Exporter/HostObjectExporter.cs b/Source/Revit.IFC.Export/Exporter/HostObjectExporter.cs index 7befce3d..0a865d5e 100644 --- a/Source/Revit.IFC.Export/Exporter/HostObjectExporter.cs +++ b/Source/Revit.IFC.Export/Exporter/HostObjectExporter.cs @@ -46,15 +46,13 @@ class HostObjectExporter /// The level id. /// The IFCLayerSetDirection. /// True if the geometry contains BRep geoemtry. If so, we will export an IfcMaterialList + /// Material layer info. /// True if exported successfully, false otherwise. public static bool ExportHostObjectMaterials(ExporterIFC exporterIFC, HostObject hostObject, IList elemHnds, GeometryElement geometryElement, ProductWrapper productWrapper, - ElementId levelId, Toolkit.IFCLayerSetDirection direction, bool containsBRepGeometry, IFCAnyHandle typeHnd = null) + ElementId levelId, IFCLayerSetDirection direction, bool containsBRepGeometry, IFCAnyHandle typeHnd = null, MaterialLayerSetInfo layersetInfo = null) { - if (hostObject == null) - return true; //nothing to do - - if (elemHnds == null || (elemHnds.Count == 0)) + if (hostObject == null || ((elemHnds?.Count ?? 0) == 0)) return true; //nothing to do IFCFile file = exporterIFC.GetFile(); @@ -72,9 +70,9 @@ class HostObjectExporter wallHeight = boundingBox.Max.Z - boundingBox.Min.Z; } - MaterialLayerSetInfo mlsInfo = new MaterialLayerSetInfo(exporterIFC, hostObject, productWrapper, geometryElement); + MaterialLayerSetInfo mlsInfo = layersetInfo == null ? new MaterialLayerSetInfo(exporterIFC, hostObject, productWrapper, geometryElement) : layersetInfo; IFCAnyHandle materialLayerSet = mlsInfo.MaterialLayerSetHandle; - List materialIds = mlsInfo.MaterialIds.Select(x => x.m_baseMatId).ToList(); + List materialIds = mlsInfo.MaterialIds.Select(x => x.BaseMatId).ToList(); // Among all the calls of this method the problem of material association absence was found only // for CeilingAndFloor host object type (see JIRA item REVIT-164913) @@ -181,7 +179,8 @@ class HostObjectExporter double offsetFromReferenceLine = flipDirSense ? -scaledOffset : scaledOffset; IFCDirectionSense sense = flipDirSense ? IFCDirectionSense.Negative : IFCDirectionSense.Positive; - layerSetUsage = CategoryUtil.CreateMaterialLayerSetUsage(file, materialLayerSet, direction, sense, offsetFromReferenceLine); + layerSetUsage = IFCInstanceExporter.CreateMaterialLayerSetUsage(file, materialLayerSet, + direction, sense, offsetFromReferenceLine); } ExporterCacheManager.MaterialSetUsageCache.Add(layerSetUsage, elemHnd); } @@ -243,18 +242,22 @@ class HostObjectExporter /// The level id. /// The IFCLayerSetDirection. /// True if the geometry contains BRep geoemtry. If so, we will export an IfcMaterialList. If null, we will calculate. + /// Material layer info. /// True if exported successfully, false otherwise. public static bool ExportHostObjectMaterials(ExporterIFC exporterIFC, HostObject hostObject, IFCAnyHandle elemHnd, GeometryElement geometryElement, ProductWrapper productWrapper, - ElementId levelId, Toolkit.IFCLayerSetDirection direction, bool? containsBRepGeometry, IFCAnyHandle typeHnd) + ElementId levelId, IFCLayerSetDirection direction, bool? containsBRepGeometry, IFCAnyHandle typeHnd, MaterialLayerSetInfo layersetInfo = null) { - IList elemHnds = new List(); - elemHnds.Add(elemHnd); + if (IFCAnyHandleUtil.IsNullOrHasNoValue(elemHnd)) + return false; + + IList elemHnds = new List() { elemHnd }; // Setting doesContainBRepGeometry to false below preserves the original behavior that we created IfcMaterialLists for all geometries. // TODO: calculate, or pass in, a valid bool value for Ceilings, Roofs, and Wall Sweeps. bool doesContainBRepGeometry = containsBRepGeometry.HasValue ? containsBRepGeometry.Value : false; - return ExportHostObjectMaterials(exporterIFC, hostObject, elemHnds, geometryElement, productWrapper, levelId, direction, doesContainBRepGeometry, typeHnd); + return ExportHostObjectMaterials(exporterIFC, hostObject, elemHnds, geometryElement, productWrapper, levelId, + direction, doesContainBRepGeometry, typeHnd, layersetInfo); } /// diff --git a/Source/Revit.IFC.Export/Exporter/HostedSweepExporter.cs b/Source/Revit.IFC.Export/Exporter/HostedSweepExporter.cs index 5aed8a29..5e82b6d0 100644 --- a/Source/Revit.IFC.Export/Exporter/HostedSweepExporter.cs +++ b/Source/Revit.IFC.Export/Exporter/HostedSweepExporter.cs @@ -18,13 +18,9 @@ // using System; -using System.Collections.Generic; using Autodesk.Revit.DB; using Autodesk.Revit.DB.IFC; using Revit.IFC.Export.Utility; -using Revit.IFC.Export.Toolkit; -using Revit.IFC.Common.Utility; -using Revit.IFC.Common.Enums; namespace Revit.IFC.Export.Exporter { @@ -42,115 +38,7 @@ class HostedSweepExporter /// The ProductWrapper. public static void Export(ExporterIFC exporterIFC, HostedSweep hostedSweep, GeometryElement geometryElement, ProductWrapper productWrapper) { - ElementId catId = CategoryUtil.GetSafeCategoryId(hostedSweep); - if (catId == new ElementId(BuiltInCategory.OST_Gutter)) - ExportGutter(exporterIFC, hostedSweep, geometryElement, productWrapper); - else - GenericElementExporter.ExportElement(exporterIFC, hostedSweep, geometryElement, productWrapper); - } - - /// - /// Exports a gutter element. - /// - /// The ExporterIFC object. - /// The element. - /// The geometry element. - /// The ProductWrapper. - public static void ExportGutter(ExporterIFC exporterIFC, Element element, GeometryElement geometryElement, ProductWrapper productWrapper) - { - // Check the intended IFC entity or type name is in the exclude list specified in the UI - Common.Enums.IFCEntityType elementClassTypeEnum = Common.Enums.IFCEntityType.IfcPipeSegmentType; - if (ExporterCacheManager.ExportOptionsCache.IsElementInExcludeList(elementClassTypeEnum)) - return; - - IFCFile file = exporterIFC.GetFile(); - - using (IFCTransaction tr = new IFCTransaction(file)) - { - // Check for containment override - IFCAnyHandle overrideContainerHnd = null; - ElementId overrideContainerId = ParameterUtil.OverrideContainmentParameter(exporterIFC, element, out overrideContainerHnd); - - using (PlacementSetter setter = PlacementSetter.Create(exporterIFC, element, null, null, overrideContainerId, overrideContainerHnd)) - { - using (IFCExportBodyParams ecData = new IFCExportBodyParams()) - { - ecData.SetLocalPlacement(setter.LocalPlacement); - - ElementId categoryId = CategoryUtil.GetSafeCategoryId(element); - - BodyExporterOptions bodyExporterOptions = new BodyExporterOptions(true, ExportOptionsCache.ExportTessellationLevel.ExtraLow); - IFCAnyHandle bodyRep = BodyExporter.ExportBody(exporterIFC, element, categoryId, ElementId.InvalidElementId, - geometryElement, bodyExporterOptions, ecData).RepresentationHnd; - if (IFCAnyHandleUtil.IsNullOrHasNoValue(bodyRep)) - { - if (ecData != null) - ecData.ClearOpenings(); - return; - } - string originalTag = NamingUtil.CreateIFCElementId(element); - - // In Revit, we don't have a corresponding type, so we create one for every gutter. - IFCAnyHandle origin = ExporterUtil.CreateAxis2Placement3D(file); - IFCAnyHandle repMap3dHnd = IFCInstanceExporter.CreateRepresentationMap(file, origin, bodyRep); - List repMapList = new List(); - repMapList.Add(repMap3dHnd); - string elementTypeName = NamingUtil.CreateIFCObjectName(exporterIFC, element); - - string typeGuid = GUIDUtil.CreateSubElementGUID(element, (int)IFCHostedSweepSubElements.PipeSegmentType); - IFCAnyHandle style = IFCInstanceExporter.CreatePipeSegmentType(file, null, typeGuid, - null, repMapList, IFCPipeSegmentType.Gutter); - IFCAnyHandleUtil.OverrideNameAttribute(style, elementTypeName); - IFCExportInfoPair exportInfo = new IFCExportInfoPair(IFCEntityType.IfcPipeSegmentType, IFCPipeSegmentType.Gutter.ToString()); - - IFCAnyHandleUtil.SetAttribute(style, "Tag", originalTag); - IFCAnyHandleUtil.SetAttribute(style, "ElementType", elementTypeName); - - List representationMaps = GeometryUtil.GetRepresentationMaps(style); - IFCAnyHandle mappedItem = ExporterUtil.CreateDefaultMappedItem(file, representationMaps[0]); - - ISet representations = new HashSet(); - representations.Add(mappedItem); - - IFCAnyHandle bodyMappedItemRep = RepresentationUtil.CreateBodyMappedItemRep(exporterIFC, - element, categoryId, exporterIFC.Get3DContextHandle("Body"), representations); - if (IFCAnyHandleUtil.IsNullOrHasNoValue(bodyMappedItemRep)) - return; - - List shapeReps = new List(); - shapeReps.Add(bodyMappedItemRep); - - IFCAnyHandle boundingBoxRep = BoundingBoxExporter.ExportBoundingBox(exporterIFC, geometryElement, Transform.Identity); - if (boundingBoxRep != null) - shapeReps.Add(boundingBoxRep); - - IFCAnyHandle prodRep = IFCInstanceExporter.CreateProductDefinitionShape(file, null, null, shapeReps); - IFCAnyHandle localPlacementToUse; - ElementId roomId = setter.UpdateRoomRelativeCoordinates(element, out localPlacementToUse); - if (roomId == ElementId.InvalidElementId) - localPlacementToUse = ecData.GetLocalPlacement(); - - string guid = GUIDUtil.CreateGUID(element); - - IFCAnyHandle elemHnd = IFCInstanceExporter.CreateFlowSegment(exporterIFC, element, guid, - ExporterCacheManager.OwnerHistoryHandle, localPlacementToUse, prodRep); - - bool containedInSpace = (roomId != ElementId.InvalidElementId); - productWrapper.AddElement(element, elemHnd, setter.LevelInfo, ecData, !containedInSpace, exportInfo); - - if (containedInSpace) - ExporterCacheManager.SpaceInfoCache.RelateToSpace(roomId, elemHnd); - - // Associate segment with type. - ExporterCacheManager.TypeRelationsCache.Add(style, elemHnd); - - OpeningUtil.CreateOpeningsIfNecessary(elemHnd, element, ecData, null, - exporterIFC, localPlacementToUse, setter, productWrapper); - } - - tr.Commit(); - } - } + GenericElementExporter.ExportElement(exporterIFC, hostedSweep, geometryElement, productWrapper); } } } \ No newline at end of file diff --git a/Source/Revit.IFC.Export/Exporter/IFCEntityAndPsetList.cs b/Source/Revit.IFC.Export/Exporter/IFCEntityAndPsetList.cs index 082c613a..c4f4d8e2 100644 --- a/Source/Revit.IFC.Export/Exporter/IFCEntityAndPsetList.cs +++ b/Source/Revit.IFC.Export/Exporter/IFCEntityAndPsetList.cs @@ -4,9 +4,9 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -using System.Web.Script.Serialization; using System.Runtime.Serialization; using System.Xml; +using Newtonsoft.Json; namespace Revit.IFC.Export.Utility { @@ -103,10 +103,7 @@ public IFCCertifiedEntitiesAndPSets() if (File.Exists(filePath)) { - // JavaScriptSerializer does not support mapping of Json array into Hashset (it expects List) and this code below gets the data into Lists first - // and then transfers the data to the final form using Hashset for performance reason - JavaScriptSerializer jsonConvert = new JavaScriptSerializer(); - IDictionary CertifiedEntityAndPsetList = jsonConvert.Deserialize>(File.ReadAllText(filePath)); + IDictionary CertifiedEntityAndPsetList = JsonConvert.DeserializeObject>(File.ReadAllText(filePath)); // Copy the data to the desired format using Hashset in IFCEntityAndPsetList foreach (KeyValuePair entPsetData in CertifiedEntityAndPsetList) { diff --git a/Source/Revit.IFC.Export/Exporter/PartExporter.cs b/Source/Revit.IFC.Export/Exporter/PartExporter.cs index d1c093ef..18ade433 100644 --- a/Source/Revit.IFC.Export/Exporter/PartExporter.cs +++ b/Source/Revit.IFC.Export/Exporter/PartExporter.cs @@ -293,8 +293,7 @@ public static void ExportPartAsBuildingElement(ExporterIFC exporterIFC, Element case PartExportMode.AsBuildingElement: case PartExportMode.ShapeRepresentationOnly: { - string ifcEnumType = null; - IFCExportInfoPair exportType = ExporterUtil.GetProductExportType(exporterIFC, hostElement, out ifcEnumType); + IFCExportInfoPair exportType = ExporterUtil.GetProductExportType(exporterIFC, hostElement, out _); // Check the intended IFC entity or type name is in the exclude list specified in the UI IFCEntityType elementClassTypeEnum; @@ -371,7 +370,8 @@ public static void ExportPartAsBuildingElement(ExporterIFC exporterIFC, Element } hostTrf = ExporterUtil.GetTransformFromLocalPlacementHnd(originalPlacement); hostTrf = ExporterUtil.UnscaleTransformOrigin(hostTrf); - geometryElement = GeometryUtil.GetTransformedGeometry(geometryElement, hostTrf.Inverse); + geometryElement = SolidMeshGeometryInfo.GetTransformedGeometry(geometryElement, hostTrf.Inverse, + ExporterCacheManager.AllocatedGeometryObjectCache); } // The host placement setter has registered the transform. Since the part geometry is transformed relative to the host, // the placement for the part needs to be inversed @@ -445,8 +445,8 @@ public static void ExportPartAsBuildingElement(ExporterIFC exporterIFC, Element IFCAnyHandle ownerHistory = ExporterCacheManager.OwnerHistoryHandle; - string ifcEnumType = null; - IFCExportInfoPair exportType = ExporterUtil.GetProductExportType(exporterIFC, (hostElement != null) ? hostElement : partElement, out ifcEnumType); + IFCExportInfoPair exportType = ExporterUtil.GetProductExportType(exporterIFC, (hostElement != null) ? hostElement : partElement, out _); + string ifcEnumType = exportType.GetPredefinedTypeOrDefault(); string partGUID = GUIDUtil.GenerateIFCGuidFrom(partElement, exportType); IFCAnyHandle ifcPart = null; if (exportMode != PartExportMode.AsBuildingElement) @@ -499,12 +499,12 @@ public static void ExportPartAsBuildingElement(ExporterIFC exporterIFC, Element break; default: ifcPart = IFCInstanceExporter.CreateBuildingElementProxy(exporterIFC, partElement, partGUID, ownerHistory, - extrusionCreationData.GetLocalPlacement(), prodRep, exportType.ValidatedPredefinedType); + extrusionCreationData.GetLocalPlacement(), prodRep, ifcEnumType); break; } } - if (setMaterialNameToPartName) + if (setMaterialNameToPartName && (bodyData?.MaterialIds?.Count ?? 0) > 0) { Material material = partElement.Document.GetElement(bodyData.MaterialIds[0]) as Material; if (material != null) @@ -693,10 +693,12 @@ public static void ExportPartAsBuildingElement(ExporterIFC exporterIFC, Element // material. It is not very correct, but it will produce consistent output var matList = (from x in layersetInfo.MaterialIds - where !MathUtil.IsAlmostZero(x.m_matWidth) - select new MaterialLayerSetInfo.MaterialInfo(x.m_baseMatId, x.m_layerName, x.m_matWidth, x.m_function)).ToList(); - if (matList != null && matList.Count > 0) + where !MathUtil.IsAlmostZero(x.Width) + select new MaterialLayerSetInfo.MaterialInfo(x.BaseMatId, x.LayerName, x.Width, x.Function)).ToList(); + if ((matList?.Count ?? 0) > 0) + { layersetInfoList.Add(matList[0]); // add only the first non-zero thickness + } } else { @@ -716,7 +718,7 @@ public static void ExportPartAsBuildingElement(ExporterIFC exporterIFC, Element } else { - IList matInfoList = layersetInfo.MaterialIds.Where(x => !MathUtil.IsAlmostZero(x.m_matWidth)).Select(x => x.m_baseMatId).ToList(); + IList matInfoList = layersetInfo.MaterialIds.Where(x => !MathUtil.IsAlmostZero(x.Width)).Select(x => x.BaseMatId).ToList(); // There is a chance that the material list is already correct or just in reverse order, so handle it before moving on to manual sequencing MaterialLayerSetInfo.CompareTwoLists compStat = MaterialLayerSetInfo.CompareMaterialInfoList(bodyData.MaterialIds, matInfoList); switch (compStat) @@ -738,7 +740,7 @@ public static void ExportPartAsBuildingElement(ExporterIFC exporterIFC, Element layersetInfoList = CollectMaterialInfoList(hostElement, associatedPartsList, layersetInfo); // There is a chance that the manually collected list will be reversed or incorrect, so check it again - IList newMatInfoList = layersetInfoList.Where(x => !MathUtil.IsAlmostZero(x.m_matWidth)).Select(x => x.m_baseMatId).ToList(); + IList newMatInfoList = layersetInfoList.Where(x => !MathUtil.IsAlmostZero(x.Width)).Select(x => x.BaseMatId).ToList(); compStat = MaterialLayerSetInfo.CompareMaterialInfoList(newMatInfoList, matInfoList); if (compStat == MaterialLayerSetInfo.CompareTwoLists.ListsReversedEqual) layersetInfoList = layersetInfoList.Reverse().ToList(); @@ -752,14 +754,24 @@ public static void ExportPartAsBuildingElement(ExporterIFC exporterIFC, Element { // We still cannot match the layer, it could be no layer. Use only the first material MaterialLayerSetInfo.MaterialInfo layer1 = layersetInfo.MaterialIds[0]; - layersetInfo.SingleMaterialOverride(layer1.m_baseMatId, layer1.m_matWidth); + layersetInfo.SingleMaterialOverride(layer1.BaseMatId, layer1.Width); layersetInfoList.Add(layer1); } } } // Scan through the prodReps and remove any existing "Body" rep if it is already in there, if there is, it will be removed and replaced with the parts - List prodReps = IFCAnyHandleUtil.GetRepresentations(hostProdDefShape); + List prodReps; + if (hostProdDefShape == null) + { + hostProdDefShape = IFCInstanceExporter.CreateProductDefinitionShape(file, null, null, null); + prodReps = new List(); + } + else + { + prodReps = IFCAnyHandleUtil.GetRepresentations(hostProdDefShape); + } + int repToRemove = -1; for (int rCnt = 0; rCnt < prodReps.Count; ++rCnt) { @@ -781,7 +793,7 @@ public static void ExportPartAsBuildingElement(ExporterIFC exporterIFC, Element int layerSetInfoCount = layersetInfoList.Count; int cnt = 0; - var layerInfoInxs = new List<(int idx, IFCAnyHandle rep)>(itemReps.Count); + var layerInfoInxs = new List<(int Index, IFCAnyHandle Representation)>(itemReps.Count); foreach (IFCAnyHandle itemRep in itemReps) { int layerInfoIdx = -1; @@ -800,19 +812,26 @@ public static void ExportPartAsBuildingElement(ExporterIFC exporterIFC, Element cnt++; } - layerInfoInxs = layerInfoInxs.OrderBy(x => x.idx).ToList(); + layerInfoInxs = layerInfoInxs.OrderBy(x => x.Index).ToList(); - var uniqueNames = new Dictionary<(string, double), string>(new MaterialLayerSetInfo.NameAndWidthComparer()); + HashSet shapeAspectNameUsed = new HashSet(); foreach (var layerInfo in layerInfoInxs) { - string layerName = (layerInfo.idx != -1) ? layersetInfoList[layerInfo.idx].m_layerName : "Layer"; - double layerWidth = (layerInfo.idx != -1) ? layersetInfoList[layerInfo.idx].m_matWidth : 0.0; + int layerIndex = layerInfo.Index; - if (string.IsNullOrEmpty(layerName)) - layerName = "Layer"; + string shapeAspectName = (layerIndex != -1) ? layersetInfoList[layerIndex].ShapeAspectName : "Layer"; + if (string.IsNullOrEmpty(shapeAspectName)) + shapeAspectName = "Layer"; + + // Ensure IfcShapeAspect name is unique and save it for consistensy to IfcMaterialConstituent. + shapeAspectName = NamingUtil.GetUniqueNameWithinSet(shapeAspectName, shapeAspectNameUsed); + if (layerIndex >= 0 && layerIndex < layerSetInfoCount) + { + layersetInfoList[layerIndex].ShapeAspectName = shapeAspectName; + } - string uniqueName = MaterialLayerSetInfo.GetUniqueMaterialNameWithWidth(layerName, layerWidth, uniqueNames); - RepresentationUtil.CreateRepForShapeAspect(exporterIFC, hostElement, hostProdDefShape, representationType, uniqueName, layerInfo.rep); + RepresentationUtil.CreateRepForShapeAspect(exporterIFC, hostElement, hostProdDefShape, representationType, + shapeAspectName, layerInfo.Representation); } return hostShapeRep; @@ -940,7 +959,7 @@ private static IList CollectMaterialInfoList( foreach (MaterialLayerSetInfo.MaterialInfo layerInfo in layersetInfo.MaterialIds) { // Face's material ID == layer's material ID && face's width == layer's width - if (faceInfo.Value.Item2.MaterialElementId == layerInfo.m_baseMatId && MathUtil.IsAlmostEqual(faceInfo.Value.Item1, layerInfo.m_matWidth)) + if (faceInfo.Value.Item2.MaterialElementId == layerInfo.BaseMatId && MathUtil.IsAlmostEqual(faceInfo.Value.Item1, layerInfo.Width)) { layersetInfoList.Add(layerInfo); break; @@ -990,7 +1009,7 @@ public static bool ShouldExportParts(Element hostElement, int layersCount) layersCount > 1)); } /// - /// Checks if elemnt has associated parts and all conditions are met for exporting it as Parts. + /// Checks if element has associated parts and all conditions are met for exporting it as Parts. /// /// The host element. /// True if host element can export the parts, false otherwise. @@ -1118,6 +1137,7 @@ private static IFCExtrusionAxes GetDefaultExtrusionAxesForHost(ExporterIFC expor case IFCEntityType.IfcCovering: case IFCEntityType.IfcSlab: case IFCEntityType.IfcRoof: + case IFCEntityType.IfcBuildingElementProxy: return IFCExtrusionAxes.TryZ; default: return IFCExtrusionAxes.TryXY; diff --git a/Source/Revit.IFC.Export/Exporter/PropertySet/Calculators/GrossAreaCalculator.cs b/Source/Revit.IFC.Export/Exporter/PropertySet/Calculators/GrossAreaCalculator.cs index ce8bc42d..3683445e 100644 --- a/Source/Revit.IFC.Export/Exporter/PropertySet/Calculators/GrossAreaCalculator.cs +++ b/Source/Revit.IFC.Export/Exporter/PropertySet/Calculators/GrossAreaCalculator.cs @@ -76,13 +76,20 @@ public override bool Calculate(ExporterIFC exporterIFC, IFCExportBodyParams extr if (m_Area > MathUtil.Eps() * MathUtil.Eps()) return true; - if (extrusionCreationData == null) - return false; + if (extrusionCreationData != null) + { + m_Area = extrusionCreationData.ScaledArea; - m_Area = extrusionCreationData.ScaledArea; + if (m_Area > MathUtil.Eps() * MathUtil.Eps()) + return true; + } - if (m_Area > MathUtil.Eps() * MathUtil.Eps()) - return true; + if (ParameterUtil.GetDoubleValueFromElementOrSymbol(element, BuiltInParameter.HOST_AREA_COMPUTED, out m_Area) != null) + { + m_Area = UnitUtil.ScaleArea(m_Area); + if (m_Area > MathUtil.Eps() * MathUtil.Eps()) + return true; + } return false; } diff --git a/Source/Revit.IFC.Export/Exporter/PropertySet/Calculators/GrossCeilingAreaCalculator.cs b/Source/Revit.IFC.Export/Exporter/PropertySet/Calculators/GrossCeilingAreaCalculator.cs index b7c8ba7d..7f9f7983 100644 --- a/Source/Revit.IFC.Export/Exporter/PropertySet/Calculators/GrossCeilingAreaCalculator.cs +++ b/Source/Revit.IFC.Export/Exporter/PropertySet/Calculators/GrossCeilingAreaCalculator.cs @@ -76,6 +76,10 @@ public override bool Calculate(ExporterIFC exporterIFC, IFCExportBodyParams extr if (m_Area > MathUtil.Eps() * MathUtil.Eps()) return true; + m_Area = UnitUtil.ScaleArea(CalculateSpatialElementGrossCeilingArea(element as SpatialElement)); + if (m_Area > MathUtil.Eps() * MathUtil.Eps()) + return true; + if (ParameterUtil.GetDoubleValueFromElementOrSymbol(element, BuiltInParameter.HOST_AREA_COMPUTED, out m_Area) != null) { m_Area = UnitUtil.ScaleArea(m_Area); @@ -94,6 +98,45 @@ public override bool Calculate(ExporterIFC exporterIFC, IFCExportBodyParams extr return false; } + private double CalculateSpatialElementGrossCeilingArea(SpatialElement spatialElement) + { + double area = 0.0; + + if (spatialElement == null) + return area; + + // Get the outer boundary loops of the SpatialElement. + IList> boundaryLoops = spatialElement.GetBoundarySegments(new SpatialElementBoundaryOptions()); + + //Search for a outer loop with the largest area. + foreach (IList boundaryLoop in boundaryLoops) + { + CurveLoop curveLoop = new CurveLoop(); + foreach (BoundarySegment boundarySegment in boundaryLoop) + { + try + { + Curve curve = boundarySegment.GetCurve(); + curveLoop.Append(curve); + } + catch (Autodesk.Revit.Exceptions.ArgumentException) + { + //For some special cases, BoundarySegments of the element are not valid for CurveLoop creation + //(curveLoop.Append(curve) throws exception because "This curve will make the loop discontinuous.") + + return 0.0; + } + } + + double loopArea = ExporterIFCUtils.ComputeAreaOfCurveLoops(new List() { curveLoop }); + + if (area < loopArea) + area = loopArea; + } + + return area; + } + /// /// Gets the calculated double value. /// diff --git a/Source/Revit.IFC.Export/Exporter/PropertySet/Calculators/GrossFloorAreaCalculator.cs b/Source/Revit.IFC.Export/Exporter/PropertySet/Calculators/GrossFloorAreaCalculator.cs index d198a5b4..780498d6 100644 --- a/Source/Revit.IFC.Export/Exporter/PropertySet/Calculators/GrossFloorAreaCalculator.cs +++ b/Source/Revit.IFC.Export/Exporter/PropertySet/Calculators/GrossFloorAreaCalculator.cs @@ -76,6 +76,10 @@ public override bool Calculate(ExporterIFC exporterIFC, IFCExportBodyParams extr if (m_Area > MathUtil.Eps() * MathUtil.Eps()) return true; + m_Area = UnitUtil.ScaleArea(CalculateSpatialElementGrossFloorArea(element as SpatialElement)); + if (m_Area > MathUtil.Eps() * MathUtil.Eps()) + return true; + if (extrusionCreationData == null) return false; @@ -87,6 +91,45 @@ public override bool Calculate(ExporterIFC exporterIFC, IFCExportBodyParams extr return false; } + private double CalculateSpatialElementGrossFloorArea(SpatialElement spatialElement) + { + double area = 0.0; + + if (spatialElement == null) + return area; + + // Get the outer boundary loops of the SpatialElement. + IList> boundaryLoops = spatialElement.GetBoundarySegments(new SpatialElementBoundaryOptions()); + + //Search for a outer loop with the largest area. + foreach (IList boundaryLoop in boundaryLoops) + { + CurveLoop curveLoop = new CurveLoop(); + foreach (BoundarySegment boundarySegment in boundaryLoop) + { + try + { + Curve curve = boundarySegment.GetCurve(); + curveLoop.Append(curve); + } + catch (Autodesk.Revit.Exceptions.ArgumentException) + { + //For some special cases, BoundarySegments of the element are not valid for CurveLoop creation + //(curveLoop.Append(curve) throws exception because "This curve will make the loop discontinuous.") + + return 0.0; + } + } + + double loopArea = ExporterIFCUtils.ComputeAreaOfCurveLoops(new List() { curveLoop }); + + if (area < loopArea) + area = loopArea; + } + + return area; + } + /// /// Gets the calculated double value. /// diff --git a/Source/Revit.IFC.Export/Exporter/PropertySet/Calculators/GrossVolumeCalculator.cs b/Source/Revit.IFC.Export/Exporter/PropertySet/Calculators/GrossVolumeCalculator.cs index 08977bc8..bb90be15 100644 --- a/Source/Revit.IFC.Export/Exporter/PropertySet/Calculators/GrossVolumeCalculator.cs +++ b/Source/Revit.IFC.Export/Exporter/PropertySet/Calculators/GrossVolumeCalculator.cs @@ -17,6 +17,7 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // +using System.Collections.Generic; using Autodesk.Revit.DB; using Autodesk.Revit.DB.IFC; using Revit.IFC.Export.Utility; @@ -38,7 +39,7 @@ class GrossVolumeCalculator : PropertyCalculator /// The SlabGrossVolumeCalculator instance. /// public static GrossVolumeCalculator Instance { get; } = new GrossVolumeCalculator(); - + /// /// Calculates gross volume. /// @@ -56,6 +57,10 @@ public override bool Calculate(ExporterIFC exporterIFC, IFCExportBodyParams extr return true; } + m_Volume = UnitUtil.ScaleVolume(CalculateSpatialElementGrossVolume(element as SpatialElement, extrusionCreationData)); + if (m_Volume > MathUtil.Eps() * MathUtil.Eps() * MathUtil.Eps()) + return true; + if (extrusionCreationData == null) return false; @@ -68,6 +73,47 @@ public override bool Calculate(ExporterIFC exporterIFC, IFCExportBodyParams extr return (m_Volume > MathUtil.Eps() * MathUtil.Eps() * MathUtil.Eps()); } + private double CalculateSpatialElementGrossVolume(SpatialElement spatialElement, IFCExportBodyParams extrusionCreationData) + { + double area = 0.0; + + if (spatialElement == null || extrusionCreationData == null) + return area; + + // Get the outer boundary loops of the SpatialElement. + IList> boundaryLoops = spatialElement.GetBoundarySegments(new SpatialElementBoundaryOptions()); + + //Search for a outer loop with the largest area. + foreach (IList boundaryLoop in boundaryLoops) + { + CurveLoop curveLoop = new CurveLoop(); + foreach (BoundarySegment boundarySegment in boundaryLoop) + { + try + { + Curve curve = boundarySegment.GetCurve(); + curveLoop.Append(curve); + } + catch (Autodesk.Revit.Exceptions.ArgumentException) + { + //For some special cases, BoundarySegments of the element are not valid for CurveLoop creation + //(curveLoop.Append(curve) throws exception because "This curve will make the loop discontinuous.") + + return 0.0; + } + } + + double loopArea = ExporterIFCUtils.ComputeAreaOfCurveLoops(new List() { curveLoop }); + + if (area < loopArea) + area = loopArea; + } + + double length = UnitUtil.UnscaleLength(extrusionCreationData.ScaledLength); + + return area * length; + } + /// /// Gets the calculated double value. /// diff --git a/Source/Revit.IFC.Export/Exporter/PropertySet/Calculators/HeightCalculator.cs b/Source/Revit.IFC.Export/Exporter/PropertySet/Calculators/HeightCalculator.cs index c9b685db..2fe8be59 100644 --- a/Source/Revit.IFC.Export/Exporter/PropertySet/Calculators/HeightCalculator.cs +++ b/Source/Revit.IFC.Export/Exporter/PropertySet/Calculators/HeightCalculator.cs @@ -112,7 +112,7 @@ public static HeightCalculator Instance } else if (IFCAnyHandleUtil.IsSubTypeOf(hnd, IFCEntityType.IfcCurtainWall)) { - BoundingBoxXYZ boundingBox = (element as Wall).get_BoundingBox(null); + BoundingBoxXYZ boundingBox = element.get_BoundingBox(null); if (boundingBox != null) m_Height = boundingBox.Max.Z - boundingBox.Min.Z; } diff --git a/Source/Revit.IFC.Export/Exporter/PropertySet/Calculators/IsExternalCalculator.cs b/Source/Revit.IFC.Export/Exporter/PropertySet/Calculators/IsExternalCalculator.cs index eb80db16..60fb76d0 100644 --- a/Source/Revit.IFC.Export/Exporter/PropertySet/Calculators/IsExternalCalculator.cs +++ b/Source/Revit.IFC.Export/Exporter/PropertySet/Calculators/IsExternalCalculator.cs @@ -35,7 +35,7 @@ class IsExternalCalculator : PropertyCalculator /// /// A boolean variable to keep the calculated value. /// - private bool IsExternal { get; set; } = false; + private bool? IsExternal { get; set; } = false; /// /// The IsExternalCalculator instance. @@ -53,6 +53,10 @@ class IsExternalCalculator : PropertyCalculator public override bool Calculate(ExporterIFC exporterIFC, IFCExportBodyParams extrusionCreationData, Element element, ElementType elementType, EntryMap entryMap) { IsExternal = CategoryUtil.IsElementExternal(element); + + if (IsExternal == null) + return false; + return true; } @@ -62,7 +66,7 @@ public override bool Calculate(ExporterIFC exporterIFC, IFCExportBodyParams extr /// The boolean value. public override bool GetBooleanValue() { - return IsExternal; + return IsExternal ?? false; } } } \ No newline at end of file diff --git a/Source/Revit.IFC.Export/Exporter/PropertySet/Calculators/LengthCalculator.cs b/Source/Revit.IFC.Export/Exporter/PropertySet/Calculators/LengthCalculator.cs index 7e71caba..9dca4799 100644 --- a/Source/Revit.IFC.Export/Exporter/PropertySet/Calculators/LengthCalculator.cs +++ b/Source/Revit.IFC.Export/Exporter/PropertySet/Calculators/LengthCalculator.cs @@ -128,7 +128,7 @@ public override bool Calculate(ExporterIFC exporterIFC, IFCExportBodyParams extr // For Slab, length is the major edge of the rectangle area profile (get it from ScaledWidth) // Also for Stair support IFCAnyHandle hnd = ExporterCacheManager.ElementToHandleCache.Find(element.Id); - if (IFCAnyHandleUtil.IsSubTypeOf(hnd, IFCEntityType.IfcSlab) || + if (PropertyUtil.IsWidthLengthReversed(hnd) || CategoryUtil.GetSafeCategoryId(element).Value == (long)BuiltInCategory.OST_StairsStringerCarriage) { m_Length = extrusionCreationData.ScaledWidth; diff --git a/Source/Revit.IFC.Export/Exporter/PropertySet/Calculators/NetFloorAreaCalculator.cs b/Source/Revit.IFC.Export/Exporter/PropertySet/Calculators/NetFloorAreaCalculator.cs index b4eed18d..8c668bfc 100644 --- a/Source/Revit.IFC.Export/Exporter/PropertySet/Calculators/NetFloorAreaCalculator.cs +++ b/Source/Revit.IFC.Export/Exporter/PropertySet/Calculators/NetFloorAreaCalculator.cs @@ -79,6 +79,10 @@ public override bool Calculate(ExporterIFC exporterIFC, IFCExportBodyParams extr if (m_Area > MathUtil.Eps() * MathUtil.Eps()) return true; + m_Area = UnitUtil.ScaleArea(CalculateSpatialElementNetFloorArea(element as SpatialElement)); + if (m_Area > MathUtil.Eps() * MathUtil.Eps()) + return true; + ElementId categoryId = CategoryUtil.GetSafeCategoryId(element); IFCAnyHandle hnd = ExporterCacheManager.ElementToHandleCache.Find(element.Id); if (element is SpatialElement @@ -102,6 +106,53 @@ public override bool Calculate(ExporterIFC exporterIFC, IFCExportBodyParams extr return false; } + private double CalculateSpatialElementNetFloorArea(SpatialElement spatialElement) + { + double netFloorArea = 0.0; + + if (spatialElement == null) + return netFloorArea; + + // Get the boundary loops of the SpatialElement + IList> boundaryLoops = spatialElement.GetBoundarySegments(new SpatialElementBoundaryOptions()); + + double outerLoopArea = 0.0; + double loopsArea = 0.0; + foreach (IList boundaryLoop in boundaryLoops) + { + CurveLoop curveLoop = new CurveLoop(); + foreach (BoundarySegment boundarySegment in boundaryLoop) + { + try + { + Curve curve = boundarySegment.GetCurve(); + curveLoop.Append(curve); + } + catch (Autodesk.Revit.Exceptions.ArgumentException) + { + //For some special cases, BoundarySegments of the element are not valid for CurveLoop creation + //(curveLoop.Append(curve) throws exception because "This curve will make the loop discontinuous.") + + return 0.0; + } + } + + double loopArea = ExporterIFCUtils.ComputeAreaOfCurveLoops(new List() { curveLoop }); + + if (outerLoopArea < loopArea) + outerLoopArea = loopArea; + + loopsArea += loopArea; + } + + //To define the net area, we need to subtract the area of the holes from the area of the outerLoopArea. + //loopsArea is the sum of the areas of all the loops. + double innerLoopsArea = loopsArea - outerLoopArea; + netFloorArea = outerLoopArea - innerLoopsArea; + + return netFloorArea; + } + /// /// Gets the calculated double value. /// diff --git a/Source/Revit.IFC.Export/Exporter/PropertySet/Calculators/WidthCalculator.cs b/Source/Revit.IFC.Export/Exporter/PropertySet/Calculators/WidthCalculator.cs index 48e63f35..b18f988a 100644 --- a/Source/Revit.IFC.Export/Exporter/PropertySet/Calculators/WidthCalculator.cs +++ b/Source/Revit.IFC.Export/Exporter/PropertySet/Calculators/WidthCalculator.cs @@ -124,8 +124,8 @@ public override bool Calculate(ExporterIFC exporterIFC, IFCExportBodyParams extr if (extrusionCreationData == null) return false; - // For Slab width is the lesser edge of the rectangle area profile (get it from ScaledHeight) - if (IFCAnyHandleUtil.IsSubTypeOf(hnd, IFCEntityType.IfcSlab)) + // For Slab width is the length of the extrusion of the rectangle area profile (get it from ScaledHeight) + if (PropertyUtil.IsWidthLengthReversed(hnd)) { m_Width = extrusionCreationData.ScaledHeight; } diff --git a/Source/Revit.IFC.Export/Exporter/PropertySet/ClassificationUtil.cs b/Source/Revit.IFC.Export/Exporter/PropertySet/ClassificationUtil.cs index b3622164..bf04fb57 100644 --- a/Source/Revit.IFC.Export/Exporter/PropertySet/ClassificationUtil.cs +++ b/Source/Revit.IFC.Export/Exporter/PropertySet/ClassificationUtil.cs @@ -73,37 +73,39 @@ public static void CreateUniformatClassification(ExporterIFC exporterIFC, IFCFil // Create Uniformat classification, if it is not set. string uniformatKeyString = "Uniformat"; string uniformatDescription = ""; - string uniformatCode = null; - if (ParameterUtil.GetStringValueFromElementOrSymbol(element, BuiltInParameter.UNIFORMAT_CODE, false, out uniformatCode) == null) - ParameterUtil.GetStringValueFromElementOrSymbol(element, "Assembly Code", out uniformatCode); - - if (!String.IsNullOrWhiteSpace(uniformatCode)) - { - if (ParameterUtil.GetStringValueFromElementOrSymbol(element, BuiltInParameter.UNIFORMAT_DESCRIPTION, false, out uniformatDescription) == null) - ParameterUtil.GetStringValueFromElementOrSymbol(element, "Assembly Description", out uniformatDescription); - } if (!ExporterCacheManager.ClassificationCache.ClassificationHandles.TryGetValue(uniformatKeyString, out IFCAnyHandle classification)) { - classification = IFCInstanceExporter.CreateClassification(file, "CSI (Construction Specifications Institute)", "1998", 0, 0, 0, + classification = IFCInstanceExporter.CreateClassification(file, "CSI (Construction Specifications Institute)", "1998", 0, 0, 0, uniformatKeyString, "UniFormat Classification", GetUniformatURL()); ExporterCacheManager.ClassificationCache.ClassificationHandles.Add(uniformatKeyString, classification); } - if (!String.IsNullOrEmpty(uniformatCode)) + + foreach (IFCAnyHandle elemHnd in elemHnds) { + if (!IFCAnyHandleUtil.IsSubTypeOf(elemHnd, constraintEntType)) + continue; + + ElementId elementId = ExporterCacheManager.HandleToElementCache.Find(elemHnd); + Element elementToUse = (elementId == ElementId.InvalidElementId) ? element : element?.Document?.GetElement(elementId); + if (elementToUse == null) + continue; + + if (ParameterUtil.GetStringValueFromElementOrSymbol(elementToUse, BuiltInParameter.UNIFORMAT_CODE, false, out string uniformatCode) == null) + ParameterUtil.GetStringValueFromElementOrSymbol(elementToUse, "Assembly Code", out uniformatCode); + + if (!String.IsNullOrWhiteSpace(uniformatCode)) { - foreach (IFCAnyHandle elemHnd in elemHnds) - { - if (IFCAnyHandleUtil.IsSubTypeOf(elemHnd, constraintEntType)) - { - ClassificationReferenceKey key = new ClassificationReferenceKey(GetUniformatURL(), - uniformatCode, uniformatKeyString, uniformatDescription, classification); - InsertClassificationReference(file, key, elemHnd); - } - } + if (ParameterUtil.GetStringValueFromElementOrSymbol(elementToUse, BuiltInParameter.UNIFORMAT_DESCRIPTION, false, out string uniformatRefName) == null) + ParameterUtil.GetStringValueFromElementOrSymbol(elementToUse, "Assembly Description", out uniformatRefName); + + ClassificationReferenceKey key = new ClassificationReferenceKey(GetUniformatURL(), + uniformatCode, uniformatRefName, uniformatDescription, classification); + InsertClassificationReference(file, key, elemHnd); } } + } /// @@ -162,15 +164,15 @@ public static void CreateUniformatClassification(ExporterIFC exporterIFC, IFCFil ParseClassificationCode(paramClassificationCode, classificationCodeFieldName, out classificationName, out classificationCode, out classificationRefName); - if (string.IsNullOrEmpty(classificationDescription)) + if (string.IsNullOrEmpty(classificationRefName)) { if (string.Compare(classificationCodeFieldName, "Assembly Code", true) == 0) { - ParameterUtil.GetStringValueFromElementOrSymbol(element, elementType, BuiltInParameter.UNIFORMAT_DESCRIPTION, false, out classificationDescription); + ParameterUtil.GetStringValueFromElementOrSymbol(element, elementType, BuiltInParameter.UNIFORMAT_DESCRIPTION, false, out classificationRefName); } else if (string.Compare(classificationCodeFieldName, "OmniClass Number", true) == 0) { - ParameterUtil.GetStringValueFromElementOrSymbol(element, elementType, BuiltInParameter.OMNICLASS_DESCRIPTION, false, out classificationDescription); + ParameterUtil.GetStringValueFromElementOrSymbol(element, elementType, BuiltInParameter.OMNICLASS_DESCRIPTION, false, out classificationRefName); } } // If classificationName is empty, there is no classification to export. diff --git a/Source/Revit.IFC.Export/Exporter/PropertySet/Description.cs b/Source/Revit.IFC.Export/Exporter/PropertySet/Description.cs index a89c9d60..3b5dd7ba 100644 --- a/Source/Revit.IFC.Export/Exporter/PropertySet/Description.cs +++ b/Source/Revit.IFC.Export/Exporter/PropertySet/Description.cs @@ -171,28 +171,25 @@ public bool IsAppropriateObjectType(IFCAnyHandle handle) { if (handle == null) return false; - //if (ObjectType == "") - // return true; - + // ObjectType information comes from PSD's Applicable Type. This may be a comma separated list of applicable type IFCEntityType hndEntity = IFCAnyHandleUtil.GetEntityType(handle); - if (ObjectType.IndexOf(hndEntity.ToString(), StringComparison.InvariantCultureIgnoreCase) < 0) + if (ObjectType.IndexOf(hndEntity.ToString(), + StringComparison.InvariantCultureIgnoreCase) >= 0) + { + return true; + } + + // The use of ObjectType in the PSD is confusing at best. The purpose and its consistency is questionable. + // If the entity is not found in this ObjectType, try the "old" way to compare the ObjectType attribute value + string objectType = IFCAnyHandleUtil.GetObjectType(handle); + if (string.IsNullOrEmpty(objectType)) { - // The use of ObjectType in the PSD is confusing at best. The purpose and its consistency is questionable. - // If the entity is not found in this ObjectType, try the "old" way to compare the ObjectType attribute value - string objectType = IFCAnyHandleUtil.GetObjectType(handle); - if (!string.IsNullOrEmpty(objectType)) - { - if (ObjectType.IndexOf(objectType, StringComparison.InvariantCultureIgnoreCase) < 0) - return false; - else - return true; - } return false; } - else - return true; - //return (NamingUtil.IsEqualIgnoringCaseAndSpaces(ObjectType, objectType)); + + return ObjectType.IndexOf(objectType, + StringComparison.InvariantCultureIgnoreCase) >= 0; } /// @@ -208,13 +205,8 @@ public bool IsAppropriateObjectType(IFCEntityType entityType) return false; // ObjectType information comes from PSD's Applicable Type. This may be a comma separated list of applicable type - if (ObjectType.IndexOf(entityType.ToString(), StringComparison.InvariantCultureIgnoreCase) < 0) - return false; - else - return true; - - //string objectType = IFCAnyHandleUtil.GetObjectType(handle); - //return (NamingUtil.IsEqualIgnoringCaseAndSpaces(ObjectType, objectType)); + return ObjectType.IndexOf(entityType.ToString(), + StringComparison.InvariantCultureIgnoreCase) >= 0; } } } diff --git a/Source/Revit.IFC.Export/Exporter/PropertySet/ElectricalCurrentPropertyUtil.cs b/Source/Revit.IFC.Export/Exporter/PropertySet/ElectricalCurrentPropertyUtil.cs deleted file mode 100644 index 41caed4c..00000000 --- a/Source/Revit.IFC.Export/Exporter/PropertySet/ElectricalCurrentPropertyUtil.cs +++ /dev/null @@ -1,121 +0,0 @@ -// -// BIM IFC library: this library works with Autodesk(R) Revit(R) to export IFC files containing model geometry. -// Copyright (C) 2012 Autodesk, Inc. -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; either -// version 2.1 of the License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -// - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Autodesk.Revit.DB; -using Autodesk.Revit.DB.IFC; -using Revit.IFC.Export.Utility; -using Revit.IFC.Export.Toolkit; -using Revit.IFC.Common.Utility; - -namespace Revit.IFC.Export.Exporter.PropertySet -{ - /// - /// Provides static methods to create varies IFC properties. - /// - public class ElectricalCurrentPropertyUtil : PropertyUtil - { - /// - /// Create a label property. - /// - /// The IFC file. - /// The name of the property. - /// The value of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateElectricalCurrentMeasureProperty(IFCFile file, string propertyName, double value, PropertyValueType valueType) - { - switch (valueType) - { - case PropertyValueType.EnumeratedValue: - { - IList valueList = new List(); - valueList.Add(IFCDataUtil.CreateAsElectricCurrentMeasure(value)); - return IFCInstanceExporter.CreatePropertyEnumeratedValue(file, propertyName, null, valueList, null); - } - case PropertyValueType.SingleValue: - return IFCInstanceExporter.CreatePropertySingleValue(file, propertyName, null, IFCDataUtil.CreateAsElectricCurrentMeasure(value), null); - default: - throw new InvalidOperationException("Missing case!"); - } - } - - /// - /// Create a label property, or retrieve from cache. - /// - /// The IFC file. - /// The name of the property. - /// The value of the property. - /// The value type of the property. - /// The created or cached property handle. - public static IFCAnyHandle CreateElectricalCurrentMeasurePropertyFromCache(IFCFile file, string propertyName, double value, PropertyValueType valueType) - { - // We have a partial cache here - we will only cache multiples of 15 degrees. - bool canCache = false; - double ampsDiv5 = Math.Floor(value / 5.0 + 0.5); - double integerAmps = ampsDiv5 * 5.0; - if (MathUtil.IsAlmostEqual(value, integerAmps)) - { - canCache = true; - value = integerAmps; - } - - IFCAnyHandle propertyHandle; - if (canCache) - { - propertyHandle = ExporterCacheManager.PropertyInfoCache.ElectricCurrentCache.Find(propertyName, value); - if (propertyHandle != null) - return propertyHandle; - } - - propertyHandle = CreateElectricalCurrentMeasureProperty(file, propertyName, value, valueType); - - if (canCache && !IFCAnyHandleUtil.IsNullOrHasNoValue(propertyHandle)) - { - ExporterCacheManager.PropertyInfoCache.ElectricCurrentCache.Add(propertyName, value, propertyHandle); - } - - return propertyHandle; - } - - /// - /// Create an electrical current measure property from the element's or type's parameter. - /// - /// The IFC file. - /// The Element. - /// The name of the parameter. - /// The name of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateElectricalCurrentMeasurePropertyFromElement(IFCFile file, Element elem, string revitParameterName, string ifcPropertyName, PropertyValueType valueType) - { - double propertyValue; - if (ParameterUtil.GetDoubleValueFromElement(elem, revitParameterName, out propertyValue) != null) - { - propertyValue = UnitUtil.ScaleElectricCurrent(propertyValue); - return CreateElectricalCurrentMeasurePropertyFromCache(file, ifcPropertyName, propertyValue, valueType); - } - - return null; - } - } -} diff --git a/Source/Revit.IFC.Export/Exporter/PropertySet/ElectricalVoltagePropertyUtil.cs b/Source/Revit.IFC.Export/Exporter/PropertySet/ElectricalVoltagePropertyUtil.cs deleted file mode 100644 index c6edf85e..00000000 --- a/Source/Revit.IFC.Export/Exporter/PropertySet/ElectricalVoltagePropertyUtil.cs +++ /dev/null @@ -1,121 +0,0 @@ -// -// BIM IFC library: this library works with Autodesk(R) Revit(R) to export IFC files containing model geometry. -// Copyright (C) 2012 Autodesk, Inc. -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; either -// version 2.1 of the License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -// - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Autodesk.Revit.DB; -using Autodesk.Revit.DB.IFC; -using Revit.IFC.Export.Utility; -using Revit.IFC.Export.Toolkit; -using Revit.IFC.Common.Utility; - -namespace Revit.IFC.Export.Exporter.PropertySet -{ - /// - /// Provides static methods to create varies IFC properties. - /// - public class ElectricVoltagePropertyUtil : PropertyUtil - { - /// - /// Create a label property. - /// - /// The IFC file. - /// The name of the property. - /// The value of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateElectricVoltageMeasureProperty(IFCFile file, string propertyName, double value, PropertyValueType valueType) - { - IFCData electricalVoltageData = IFCDataUtil.CreateAsElectricVoltageMeasure(value); - return CreateCommonProperty(file, propertyName, electricalVoltageData, valueType, null); - } - - /// - /// Create a label property, or retrieve from cache. - /// - /// The IFC file. - /// The name of the property. - /// The value of the property. - /// The value type of the property. - /// The created or cached property handle. - public static IFCAnyHandle CreateElectricalVoltageMeasurePropertyFromCache(IFCFile file, string propertyName, double value, PropertyValueType valueType) - { - // We have a partial cache here - we will only cache multiples of 15 degrees. - bool canCache = false; - double ampsDiv5 = Math.Floor(value / 5.0 + 0.5); - double integerAmps = ampsDiv5 * 5.0; - if (MathUtil.IsAlmostEqual(value, integerAmps)) - { - canCache = true; - value = integerAmps; - } - - IFCAnyHandle propertyHandle; - if (canCache) - { - propertyHandle = ExporterCacheManager.PropertyInfoCache.ElectricVoltageCache.Find(propertyName, value); - if (propertyHandle != null) - return propertyHandle; - } - - propertyHandle = CreateElectricVoltageMeasureProperty(file, propertyName, value, valueType); - - if (canCache && !IFCAnyHandleUtil.IsNullOrHasNoValue(propertyHandle)) - { - ExporterCacheManager.PropertyInfoCache.ElectricVoltageCache.Add(propertyName, value, propertyHandle); - } - - return propertyHandle; - } - - /// - /// Create an electrical voltage measure property from the element's parameter. - /// - /// The IFC file. - /// The Element. - /// The name of the parameter. - /// The name of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateElectricVoltageMeasurePropertyFromElement(IFCFile file, Element elem, string revitParameterName, string ifcPropertyName, PropertyValueType valueType) - { - double propertyValue; - Parameter param = ParameterUtil.GetDoubleValueFromElement(elem, revitParameterName, out propertyValue); - if ( param != null) - { - if (!ParameterUtil.ParameterDataTypeIsEqualTo(param, SpecTypeId.Number)) - propertyValue = UnitUtil.ScaleElectricVoltage(propertyValue); - - if (valueType == PropertyValueType.BoundedValue) - { - IList boundedData = GetBoundedDataFromElement(elem, revitParameterName, propertyValue, SpecTypeId.ElectricalPotential, "IfcElectricVoltageMeasure"); - return CreateBoundedValuePropertyFromList(file, ifcPropertyName, boundedData, null); - } - else - { - return CreateElectricalVoltageMeasurePropertyFromCache(file, ifcPropertyName, propertyValue, valueType); - } - } - - return null; - } - } -} diff --git a/Source/Revit.IFC.Export/Exporter/PropertySet/FrequencyPropertyUtil.cs b/Source/Revit.IFC.Export/Exporter/PropertySet/FrequencyPropertyUtil.cs deleted file mode 100644 index dc2967ba..00000000 --- a/Source/Revit.IFC.Export/Exporter/PropertySet/FrequencyPropertyUtil.cs +++ /dev/null @@ -1,107 +0,0 @@ -// -// BIM IFC library: this library works with Autodesk(R) Revit(R) to export IFC files containing model geometry. -// Copyright (C) 2012 Autodesk, Inc. -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; either -// version 2.1 of the License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -// - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Autodesk.Revit.DB; -using Autodesk.Revit.DB.IFC; -using Revit.IFC.Export.Utility; -using Revit.IFC.Export.Toolkit; -using Revit.IFC.Common.Utility; - -namespace Revit.IFC.Export.Exporter.PropertySet -{ - /// - /// Provides static methods to create varies IFC properties. Inherit from PropertyUtil for protected helper functions. - /// - public class FrequencyPropertyUtil : PropertyUtil - { - /// Create a FrequencyMeasure property. - /// The IFC file. - /// The name of the property. - /// The value of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateFrequencyProperty(IFCFile file, string propertyName, double value, PropertyValueType valueType) - { - IFCData frequencyData = IFCDataUtil.CreateAsFrequencyMeasure(value); - return CreateCommonProperty(file, propertyName, frequencyData, valueType, null); - } - - /// - /// Create a Frequency measure property from the element's parameter. - /// - /// The IFC file. - /// The ExporterIFC. - /// The Element. - /// The name of the parameter. - /// The name of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateFrequencyPropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, string ifcPropertyName, PropertyValueType valueType) - { - double propertyValue; - if (ParameterUtil.GetDoubleValueFromElement(elem, revitParameterName, out propertyValue) != null) - { - if (valueType == PropertyValueType.BoundedValue) - { - IList boundedData = GetBoundedDataFromElement(elem, revitParameterName, propertyValue, SpecTypeId.Number, "IfcFrequencyMeasure"); - return CreateBoundedValuePropertyFromList(file, ifcPropertyName, boundedData, null); - } - else - { - return CreateFrequencyProperty(file, ifcPropertyName, propertyValue, valueType); - } - } - return null; - } - - /// - /// Create a Frequency measure property from the element's parameter. - /// - /// The IFC file. - /// The ExporterIFC. - /// The Element. - /// The name of the parameter. - /// The built in parameter to use, if revitParameterName isn't found. - /// The name of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateFrequencyPropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) - { - IFCAnyHandle propHnd = CreateFrequencyPropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, valueType); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) - return propHnd; - - if (revitBuiltInParam != BuiltInParameter.INVALID) - { - string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); - propHnd = CreateFrequencyPropertyFromElement(file, exporterIFC, elem, builtInParamName, ifcPropertyName, valueType); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) - return propHnd; - } - - return null; - } - } -} \ No newline at end of file diff --git a/Source/Revit.IFC.Export/Exporter/PropertySet/PositivePlaneAnglePropertyUtil.cs b/Source/Revit.IFC.Export/Exporter/PropertySet/PositivePlaneAnglePropertyUtil.cs deleted file mode 100644 index 52041095..00000000 --- a/Source/Revit.IFC.Export/Exporter/PropertySet/PositivePlaneAnglePropertyUtil.cs +++ /dev/null @@ -1,136 +0,0 @@ -// -// BIM IFC library: this library works with Autodesk(R) Revit(R) to export IFC files containing model geometry. -// Copyright (C) 2012 Autodesk, Inc. -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; either -// version 2.1 of the License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -// - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Autodesk.Revit.DB; -using Autodesk.Revit.DB.IFC; -using Revit.IFC.Export.Utility; -using Revit.IFC.Export.Toolkit; -using Revit.IFC.Common.Utility; - -namespace Revit.IFC.Export.Exporter.PropertySet -{ - /// - /// Provides static methods to create varies IFC properties. - /// - public class PositivePlaneAnglePropertyUtil : PropertyUtil - { - /// - /// Create a label property. - /// - /// The IFC file. - /// The name of the property. - /// The value of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreatePositivePlaneAngleMeasureProperty(IFCFile file, string propertyName, double value, PropertyValueType valueType) - { - // Ensure it is positive. Don't throw, but should tell user. - if (value <= MathUtil.Eps()) - return null; - - switch (valueType) - { - case PropertyValueType.EnumeratedValue: - { - IList valueList = new List(); - valueList.Add(IFCDataUtil.CreateAsPositivePlaneAngleMeasure(value)); - return IFCInstanceExporter.CreatePropertyEnumeratedValue(file, propertyName, null, valueList, null); - } - case PropertyValueType.SingleValue: - return IFCInstanceExporter.CreatePropertySingleValue(file, propertyName, null, IFCDataUtil.CreateAsPositivePlaneAngleMeasure(value), null); - default: - throw new InvalidOperationException("Missing case!"); - } - } - - /// - /// Create a label property, or retrieve from cache. - /// - /// The IFC file. - /// The name of the property. - /// The value of the property. - /// The value type of the property. - /// The created or cached property handle. - public static IFCAnyHandle CreatePositivePlaneAngleMeasurePropertyFromCache(IFCFile file, string propertyName, double value, PropertyValueType valueType) - { - // We have a partial cache here - we will only cache multiples of 15 degrees. - bool canCache = false; - double degreesDiv15 = Math.Floor(value / 15.0 + 0.5); - double integerDegrees = degreesDiv15 * 15.0; - if (MathUtil.IsAlmostEqual(value, integerDegrees)) - { - canCache = true; - value = integerDegrees; - } - - IFCAnyHandle propertyHandle; - if (canCache) - { - propertyHandle = ExporterCacheManager.PropertyInfoCache.PositivePlaneAngleCache.Find(propertyName, value); - if (propertyHandle != null) - return propertyHandle; - } - - propertyHandle = CreatePositivePlaneAngleMeasureProperty(file, propertyName, value, valueType); - - if (canCache && !IFCAnyHandleUtil.IsNullOrHasNoValue(propertyHandle)) - { - ExporterCacheManager.PropertyInfoCache.PositivePlaneAngleCache.Add(propertyName, value, propertyHandle); - } - - return propertyHandle; - } - - /// - /// Create a plane angle measure property from the element's parameter. - /// - /// The IFC file. - /// The Element. - /// The name of the parameter. - /// The name of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreatePositivePlaneAngleMeasurePropertyFromElement(IFCFile file, Element elem, string revitParameterName, string ifcPropertyName, PropertyValueType valueType) - { - double propertyValue; - Parameter param = ParameterUtil.GetDoubleValueFromElement(elem, revitParameterName, out propertyValue); - if (param != null) - { - if (!ParameterUtil.ParameterDataTypeIsEqualTo(param, SpecTypeId.Number)) - propertyValue = UnitUtil.ScaleAngle(propertyValue); - - if (valueType == PropertyValueType.BoundedValue) - { - IList boundedData = GetBoundedDataFromElement(elem, revitParameterName, propertyValue, SpecTypeId.Angle, "IfcPositivePlaneAngleMeasure"); - return CreateBoundedValuePropertyFromList(file, ifcPropertyName, boundedData, null); - } - else - { - return CreatePositivePlaneAngleMeasurePropertyFromCache(file, ifcPropertyName, propertyValue, valueType); - } - } - - return null; - } - } -} \ No newline at end of file diff --git a/Source/Revit.IFC.Export/Exporter/PropertySet/PreDefinedPropertySetEntry.cs b/Source/Revit.IFC.Export/Exporter/PropertySet/PreDefinedPropertySetEntry.cs index 8d1f7d9d..c8538bdc 100644 --- a/Source/Revit.IFC.Export/Exporter/PropertySet/PreDefinedPropertySetEntry.cs +++ b/Source/Revit.IFC.Export/Exporter/PropertySet/PreDefinedPropertySetEntry.cs @@ -21,8 +21,6 @@ using System.Collections.Generic; using Autodesk.Revit.DB; using Autodesk.Revit.DB.IFC; -using Revit.IFC.Export.Utility; -using GeometryGym.Ifc; namespace Revit.IFC.Export.Exporter.PropertySet { diff --git a/Source/Revit.IFC.Export/Exporter/PropertySet/PropertySetEntry.cs b/Source/Revit.IFC.Export/Exporter/PropertySet/PropertySetEntry.cs index f509c0e7..fc3121ef 100644 --- a/Source/Revit.IFC.Export/Exporter/PropertySet/PropertySetEntry.cs +++ b/Source/Revit.IFC.Export/Exporter/PropertySet/PropertySetEntry.cs @@ -22,7 +22,6 @@ using Autodesk.Revit.DB; using Autodesk.Revit.DB.IFC; using Revit.IFC.Export.Utility; -using GeometryGym.Ifc; namespace Revit.IFC.Export.Exporter.PropertySet { @@ -264,10 +263,41 @@ public enum PropertyType AngularVelocity, IfcCostValue, IfcRelaxation, - ElectricalResistivity, FrictionLoss, LinearMoment, - LinearStiffness + LinearStiffness, + CostPerArea, + ApparentPowerDensity, + CostRateEnergy, + CostRatePower, + Efficacy, + Luminance, + ElectricalPowerDensity, + PowerPerLength, + ElectricalResistivity, + HeatCapacityPerArea, + ThermalGradientCoefficientForMoistureCapacity, + ThermalMass, + AirFlowDensity, + AirFlowDividedByCoolingLoad, + AirFlowDividedByVolume, + AreaDividedByCoolingLoad, + AreaDividedByHeatingLoad, + CoolingLoadDividedByArea, + CoolingLoadDividedByVolume, + FlowPerPower, + HvacFriction, + HeatingLoadDividedByArea, + HeatingLoadDividedByVolume, + PowerPerFlow, + PipingFriction, + AreaSpringCoefficient, + LineSpringCoefficient, + MassPerUnitArea, + ReinforcementAreaPerUnitLength, + RotationalLineSpringCoefficient, + RotationalPointSpringCoefficient, + UnitWeight } /// @@ -297,8 +327,6 @@ public class PropertySetEntry : Entry IFCAnyHandle DefaultProperty { get; set; } = null; - public IfcValue DefaultValue { get; set; } = null; - public IList CombinedParameterData { get; set; } = null; /// @@ -342,28 +370,6 @@ public PropertySetEntry(PropertyType propertyType, string propertyName, IEnumera { PropertyType = propertyType; } - - private IFCAnyHandle SetDefaultProperty(IFCFile file) - { - if (DefaultProperty == null) - { - if (DefaultValue != null) - { - switch (PropertyType) - { - case PropertyType.Label: - return DefaultProperty = PropertyUtil.CreateLabelProperty(file, PropertyName, DefaultValue.ValueString, PropertyValueType, PropertyEnumerationType); - case PropertyType.Text: - return DefaultProperty = PropertyUtil.CreateTextProperty(file, PropertyName, DefaultValue.ValueString, PropertyValueType); - case PropertyType.Identifier: - return DefaultProperty = PropertyUtil.CreateIdentifierProperty(file, PropertyName, DefaultValue.ValueString, PropertyValueType); - //todo make this work for all values - } - } - } - - return DefaultProperty; - } private string GetParameterValueById(Element element, ElementId paramId) { @@ -433,7 +439,7 @@ private IFCAnyHandle CreateTextPropertyFromCombinedParameterData(IFCFile file, E return propHnd; } - return SetDefaultProperty(file); + return null; } /// @@ -923,10 +929,6 @@ public static PropertySetEntry CreateParameterEntry(Parameter parameter, BuiltIn { propertyType = PropertyType.Currency; } - else if (type == SpecTypeId.Efficacy) - { - propertyType = PropertyType.ElectricalEfficacy; - } else if (type == SpecTypeId.Energy || type == SpecTypeId.HvacEnergy) { @@ -958,19 +960,11 @@ public static PropertySetEntry CreateParameterEntry(Parameter parameter, BuiltIn { propertyType = PropertyType.ElectricVoltage; } - else if (type == SpecTypeId.ElectricalResistivity) - { - propertyType = PropertyType.ElectricalResistivity; - } else if (type == SpecTypeId.ElectricalFrequency || type == SpecTypeId.StructuralFrequency) { propertyType = PropertyType.Frequency; } - else if (type == SpecTypeId.HvacFriction) - { - propertyType = PropertyType.FrictionLoss; - } else if (type == SpecTypeId.LuminousFlux) { propertyType = PropertyType.LuminousFlux; @@ -1126,6 +1120,134 @@ public static PropertySetEntry CreateParameterEntry(Parameter parameter, BuiltIn { propertyType = PropertyType.WarpingConstant; } + else if (type == SpecTypeId.CostPerArea) + { + propertyType = PropertyType.CostPerArea; + } + else if (type == SpecTypeId.ApparentPowerDensity) + { + propertyType = PropertyType.ApparentPowerDensity; + } + else if (type == SpecTypeId.CostRateEnergy) + { + propertyType = PropertyType.CostRateEnergy; + } + else if (type == SpecTypeId.CostRatePower) + { + propertyType = PropertyType.CostRatePower; + } + else if (type == SpecTypeId.Efficacy) + { + propertyType = PropertyType.ElectricalEfficacy; + } + else if (type == SpecTypeId.Luminance) + { + propertyType = PropertyType.Luminance; + } + else if (type == SpecTypeId.ElectricalPowerDensity) + { + propertyType = PropertyType.ElectricalPowerDensity; + } + else if (type == SpecTypeId.PowerPerLength) + { + propertyType = PropertyType.PowerPerLength; + } + else if (type == SpecTypeId.ElectricalResistivity) + { + propertyType = PropertyType.ElectricalResistivity; + } + else if (type == SpecTypeId.HeatCapacityPerArea) + { + propertyType = PropertyType.HeatCapacityPerArea; + } + else if (type == SpecTypeId.ThermalGradientCoefficientForMoistureCapacity) + { + propertyType = PropertyType.ThermalGradientCoefficientForMoistureCapacity; + } + else if (type == SpecTypeId.ThermalMass) + { + propertyType = PropertyType.ThermalMass; + } + else if (type == SpecTypeId.AirFlowDensity) + { + propertyType = PropertyType.AirFlowDensity; + } + else if (type == SpecTypeId.AirFlowDividedByCoolingLoad) + { + propertyType = PropertyType.AirFlowDividedByCoolingLoad; + } + else if (type == SpecTypeId.AirFlowDividedByVolume) + { + propertyType = PropertyType.AirFlowDividedByVolume; + } + else if (type == SpecTypeId.AreaDividedByCoolingLoad) + { + propertyType = PropertyType.AreaDividedByCoolingLoad; + } + else if (type == SpecTypeId.AreaDividedByHeatingLoad) + { + propertyType = PropertyType.AreaDividedByHeatingLoad; + } + else if (type == SpecTypeId.CoolingLoadDividedByArea) + { + propertyType = PropertyType.CoolingLoadDividedByArea; + } + else if (type == SpecTypeId.CoolingLoadDividedByVolume) + { + propertyType = PropertyType.CoolingLoadDividedByVolume; + } + else if (type == SpecTypeId.FlowPerPower) + { + propertyType = PropertyType.FlowPerPower; + } + else if (type == SpecTypeId.HvacFriction) + { + propertyType = PropertyType.FrictionLoss; + } + else if (type == SpecTypeId.HeatingLoadDividedByArea) + { + propertyType = PropertyType.HeatingLoadDividedByArea; + } + else if (type == SpecTypeId.HeatingLoadDividedByVolume) + { + propertyType = PropertyType.HeatingLoadDividedByVolume; + } + else if (type == SpecTypeId.PowerPerFlow) + { + propertyType = PropertyType.PowerPerFlow; + } + else if (type == SpecTypeId.PipingFriction) + { + propertyType = PropertyType.PipingFriction; + } + else if (type == SpecTypeId.AreaSpringCoefficient) + { + propertyType = PropertyType.AreaSpringCoefficient; + } + else if (type == SpecTypeId.LineSpringCoefficient) + { + propertyType = PropertyType.LineSpringCoefficient; + } + else if (type == SpecTypeId.MassPerUnitArea) + { + propertyType = PropertyType.MassPerUnitArea; + } + else if (type == SpecTypeId.ReinforcementAreaPerUnitLength) + { + propertyType = PropertyType.ReinforcementAreaPerUnitLength; + } + else if (type == SpecTypeId.RotationalLineSpringCoefficient) + { + propertyType = PropertyType.RotationalLineSpringCoefficient; + } + else if (type == SpecTypeId.RotationalPointSpringCoefficient) + { + propertyType = PropertyType.RotationalPointSpringCoefficient; + } + else if (type == SpecTypeId.UnitWeight) + { + propertyType = PropertyType.UnitWeight; + } else { assigned = false; diff --git a/Source/Revit.IFC.Export/Exporter/PropertySet/PropertySetEntryMap.cs b/Source/Revit.IFC.Export/Exporter/PropertySet/PropertySetEntryMap.cs index 35a6c023..4fbb477f 100644 --- a/Source/Revit.IFC.Export/Exporter/PropertySet/PropertySetEntryMap.cs +++ b/Source/Revit.IFC.Export/Exporter/PropertySet/PropertySetEntryMap.cs @@ -127,7 +127,7 @@ private static IFCData GetPropertyDataFromString(string valueString, PropertyTyp case PropertyType.AreaDensity: { if (double.TryParse(valueString, out double value)) - data = IFCDataUtil.CreateAsMeasure(value, "IfcAreaDensityMeasure"); + data = IFCDataUtil.CreateAsAreaDensityMeasure(value); break; } case PropertyType.Boolean: @@ -143,7 +143,7 @@ private static IFCData GetPropertyDataFromString(string valueString, PropertyTyp case PropertyType.ColorTemperature: { if (double.TryParse(valueString, out double value)) - data = IFCDataUtil.CreateAsMeasure(value, "IfcReal"); + data = IFCDataUtil.CreateAsReal(value); break; } case PropertyType.Count: @@ -157,14 +157,14 @@ private static IFCData GetPropertyDataFromString(string valueString, PropertyTyp if (double.TryParse(valueString, out double value)) { data = ExporterCacheManager.UnitsCache.ContainsKey("CURRENCY") ? - IFCDataUtil.CreateAsMeasure(value, "IfcMonetaryMeasure") : IFCDataUtil.CreateAsMeasure(value, "IfcReal"); + IFCDataUtil.CreateAsMonetaryMeasure(value) : IFCDataUtil.CreateAsReal(value); } break; } case PropertyType.ElectricalEfficacy: { if (double.TryParse(valueString, out double value)) - data = IFCDataUtil.CreateAsMeasure(value, "IfcReal"); + data = IFCDataUtil.CreateAsReal(value); break; } case PropertyType.ElectricCurrent: @@ -182,7 +182,7 @@ private static IFCData GetPropertyDataFromString(string valueString, PropertyTyp case PropertyType.Force: { if (Double.TryParse(valueString, out double value)) - data = IFCDataUtil.CreateAsMeasure(value, "IfcForceMeasure"); + data = IFCDataUtil.CreateAsForceMeasure(value); break; } case PropertyType.Frequency: @@ -205,13 +205,13 @@ private static IFCData GetPropertyDataFromString(string valueString, PropertyTyp case PropertyType.Illuminance: { if (Double.TryParse(valueString, out double value)) - data = IFCDataUtil.CreateAsMeasure(value, "IfcIlluminanceMeasure"); + data = IFCDataUtil.CreateAsIlluminanceMeasure(value); break; } case PropertyType.HeatFluxDensity: { if (Double.TryParse(valueString, out double value)) - data = IFCDataUtil.CreateAsMeasure(value, "IfcHeatFluxDensityMeasure"); + data = IFCDataUtil.CreateAsHeatFluxDensityMeasure(value); break; } case PropertyType.Label: @@ -228,7 +228,7 @@ private static IFCData GetPropertyDataFromString(string valueString, PropertyTyp case PropertyType.LinearVelocity: { if (Double.TryParse(valueString, out double value)) - data = IFCDataUtil.CreateAsMeasure(value, "IfcLinearVelocityMeasure"); + data = IFCDataUtil.CreateAsLinearVelocityMeasure(value); break; } case PropertyType.Logical: @@ -240,43 +240,43 @@ private static IFCData GetPropertyDataFromString(string valueString, PropertyTyp case PropertyType.LuminousFlux: { if (Double.TryParse(valueString, out double value)) - data = IFCDataUtil.CreateAsMeasure(value, "IfcLuminousFluxMeasure"); + data = IFCDataUtil.CreateAsLuminousFluxMeasure(value); break; } case PropertyType.LuminousIntensity: { if (Double.TryParse(valueString, out double value)) - data = IFCDataUtil.CreateAsMeasure(value, "IfcLuminousIntensityMeasure"); + data = IFCDataUtil.CreateAsLuminousIntensityMeasure(value); break; } case PropertyType.LinearForce: { if (Double.TryParse(valueString, out double value)) - data = IFCDataUtil.CreateAsMeasure(value, "IfcLinearForceMeasure"); + data = IFCDataUtil.CreateAsLinearForceMeasure(value); break; } case PropertyType.Mass: { if (Double.TryParse(valueString, out double value)) - data = IFCDataUtil.CreateAsMeasure(value, "IfcMassMeasure"); + data = IFCDataUtil.CreateAsMassMeasure(value); break; } case PropertyType.MassDensity: { if (Double.TryParse(valueString, out double value)) - data = IFCDataUtil.CreateAsMeasure(value, "IfcMassDensityMeasure"); + data = IFCDataUtil.CreateAsMassDensityMeasure(value); break; } case PropertyType.MassFlowRate: { if (Double.TryParse(valueString, out double value)) - data = IFCDataUtil.CreateAsMeasure(value, "IfcMassFlowRateMeasure"); + data = IFCDataUtil.CreateAsMassFlowRateMeasure(value); break; } case PropertyType.ModulusOfElasticity: { if (Double.TryParse(valueString, out double value)) - data = IFCDataUtil.CreateAsMeasure(value, "IfcModulusOfElasticityMeasure"); + data = IFCDataUtil.CreateAsModulusOfElasticityMeasure(value); break; } case PropertyType.NormalisedRatio: @@ -299,7 +299,7 @@ private static IFCData GetPropertyDataFromString(string valueString, PropertyTyp case PropertyType.PlanarForce: { if (Double.TryParse(valueString, out double value)) - data = IFCDataUtil.CreateAsMeasure(value, "IfcPlanarForceMeasure"); + data = IFCDataUtil.CreateAsPlanarForceMeasure(value); break; } case PropertyType.PositiveLength: @@ -328,7 +328,7 @@ private static IFCData GetPropertyDataFromString(string valueString, PropertyTyp case PropertyType.Pressure: { if (Double.TryParse(valueString, out double value)) - data = IFCDataUtil.CreateAsMeasure(value, "IfcPressureMeasure"); + data = IFCDataUtil.CreateAsPressureMeasure(value); break; } case PropertyType.Ratio: @@ -345,7 +345,7 @@ private static IFCData GetPropertyDataFromString(string valueString, PropertyTyp case PropertyType.RotationalFrequency: { if (Double.TryParse(valueString, out double value)) - data = IFCDataUtil.CreateAsMeasure(value, "IfcRotationalFrequencyMeasure"); + data = IFCDataUtil.CreateAsRotationalFrequencyMeasure(value); break; } case PropertyType.SoundPower: @@ -387,13 +387,13 @@ private static IFCData GetPropertyDataFromString(string valueString, PropertyTyp case PropertyType.VolumetricFlowRate: { if (Double.TryParse(valueString, out double value)) - data = IFCDataUtil.CreateAsMeasure(value, "IfcVolumetricFlowRateMeasure"); + data = IFCDataUtil.CreateAsVolumetricFlowRateMeasure(value); break; } case PropertyType.Time: { if (Double.TryParse(valueString, out double value)) - data = IFCDataUtil.CreateAsMeasure(value, "IfcTimeMeasure"); + data = IFCDataUtil.CreateAsTimeMeasure(value); break; } // Known unhandled cases: @@ -438,26 +438,26 @@ private static IFCData GetPropertyDataFromString(string valueString, PropertyTyp { case PropertyType.Area: { - propHnd = PropertyUtil.CreateAreaMeasurePropertyFromElement(file, exporterIFC, element, revitParamNameToUse, + propHnd = PropertyUtil.CreateAreaPropertyFromElement(file, element, revitParamNameToUse, builtInParameter, ifcPropertyName, valueType); break; } case PropertyType.Acceleration: { - propHnd = PropertyUtil.CreateAccelerationMeasurePropertyFromElement(file, exporterIFC, element, revitParamNameToUse, + propHnd = PropertyUtil.CreateAccelerationPropertyFromElement(file, element, revitParamNameToUse, builtInParameter, ifcPropertyName, valueType); break; } case PropertyType.AngularVelocity: { - propHnd = PropertyUtil.CreateAngularVelocityMeasurePropertyFromElement(file, exporterIFC, element, revitParamNameToUse, + propHnd = PropertyUtil.CreateAngularVelocityPropertyFromElement(file, element, revitParamNameToUse, builtInParameter, ifcPropertyName, valueType); break; } case PropertyType.AreaDensity: { - propHnd = PropertyUtil.CreateAreaDensityPropertyFromElement(file, exporterIFC, element, revitParamNameToUse, - builtInParameter, ifcPropertyName, valueType); + propHnd = PropertyUtil.CreateAreaDensityPropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType); break; } case PropertyType.Boolean: @@ -468,13 +468,13 @@ private static IFCData GetPropertyDataFromString(string valueString, PropertyTyp case PropertyType.ClassificationReference: { propHnd = PropertyUtil.CreateClassificationReferencePropertyFromElement(file, exporterIFC, element, revitParamNameToUse, - builtInParameter, ifcPropertyName); + builtInParameter, ifcPropertyName); break; } case PropertyType.ColorTemperature: { - propHnd = PropertyUtil.CreateColorTemperaturePropertyFromElement(file, exporterIFC, element, revitParamNameToUse, builtInParameter, - ifcPropertyName, valueType); + propHnd = PropertyUtil.CreateUserDefinedRealPropertyFromElement(file, element, revitParamNameToUse, builtInParameter, + ifcPropertyName, valueType, SpecTypeId.ColorTemperature, "COLORTEMPERATURE"); break; } case PropertyType.Count: @@ -485,77 +485,62 @@ private static IFCData GetPropertyDataFromString(string valueString, PropertyTyp } case PropertyType.Currency: { - propHnd = PropertyUtil.CreateCurrencyPropertyFromElement(file, exporterIFC, element, revitParamNameToUse, builtInParameter, - ifcPropertyName, valueType); + propHnd = PropertyUtil.CreateCurrencyPropertyFromElement(file, exporterIFC, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType); break; } case PropertyType.DynamicViscosity: { - propHnd = PropertyUtil.CreateDynamicViscosityPropertyFromElement(file, exporterIFC, element, revitParamNameToUse, + propHnd = PropertyUtil.CreateDynamicViscosityPropertyFromElement(file, element, revitParamNameToUse, builtInParameter, ifcPropertyName, valueType); break; } - case PropertyType.ElectricalEfficacy: - { - propHnd = PropertyUtil.CreateElectricalEfficacyPropertyFromElement(file, exporterIFC, element, revitParamNameToUse, builtInParameter, - ifcPropertyName, valueType); - break; - } - case PropertyType.ElectricalResistivity: - { - propHnd = PropertyUtil.CreateElectricalResistivityPropertyFromElement(file, exporterIFC, element, revitParamNameToUse, builtInParameter, - ifcPropertyName, valueType); - break; - } case PropertyType.ElectricCurrent: { - propHnd = ElectricalCurrentPropertyUtil.CreateElectricalCurrentMeasurePropertyFromElement(file, element, revitParamNameToUse, ifcPropertyName, valueType); + propHnd = PropertyUtil.CreateElectricCurrentPropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType); break; } case PropertyType.ElectricVoltage: { - propHnd = ElectricVoltagePropertyUtil.CreateElectricVoltageMeasurePropertyFromElement(file, element, revitParamNameToUse, ifcPropertyName, valueType); + propHnd = PropertyUtil.CreateElectricVoltagePropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType); break; } case PropertyType.Energy: { - propHnd = PropertyUtil.CreateEnergyMeasurePropertyFromElement(file, exporterIFC, element, revitParamNameToUse, + propHnd = PropertyUtil.CreateEnergyPropertyFromElement(file, element, revitParamNameToUse, builtInParameter, ifcPropertyName, valueType); break; } case PropertyType.Force: { - propHnd = FrequencyPropertyUtil.CreateForcePropertyFromElement(file, exporterIFC, element, revitParamNameToUse, builtInParameter, - ifcPropertyName, valueType); + propHnd = PropertyUtil.CreateForcePropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType); break; } case PropertyType.Frequency: { - propHnd = FrequencyPropertyUtil.CreateFrequencyPropertyFromElement(file, exporterIFC, element, revitParamNameToUse, builtInParameter, - ifcPropertyName, valueType); - break; - } - case PropertyType.FrictionLoss: - { - propHnd = PropertyUtil.CreateFrictionLossPropertyFromElement(file, exporterIFC, element, revitParamNameToUse, builtInParameter, - ifcPropertyName, valueType); + propHnd = PropertyUtil.CreateFrequencyPropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType); break; } case PropertyType.HeatingValue: { - propHnd = PropertyUtil.CreateHeatingValuePropertyFromElement(file, exporterIFC, element, revitParamNameToUse, builtInParameter, - ifcPropertyName, valueType); + propHnd = PropertyUtil.CreateHeatingValuePropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType); break; } case PropertyType.Identifier: { - propHnd = PropertyUtil.CreateIdentifierPropertyFromElement(file, element, revitParamNameToUse, builtInParameter, ifcPropertyName, valueType); + propHnd = PropertyUtil.CreateIdentifierPropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType); break; } case PropertyType.Illuminance: { - propHnd = PropertyUtil.CreateIlluminancePropertyFromElement(file, exporterIFC, element, revitParamNameToUse, builtInParameter, - ifcPropertyName, valueType); + propHnd = PropertyUtil.CreateIlluminancePropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType); break; } case PropertyType.Integer: @@ -565,55 +550,56 @@ private static IFCData GetPropertyDataFromString(string valueString, PropertyTyp } case PropertyType.IonConcentration: { - propHnd = PropertyUtil.CreateIonConcentrationPropertyFromElement(file, exporterIFC, element, revitParamNameToUse, builtInParameter, - ifcPropertyName, valueType); + propHnd = PropertyUtil.CreateIonConcentrationPropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType); break; } case PropertyType.IsothermalMoistureCapacity: { - propHnd = PropertyUtil.CreateIsothermalMoistureCapacityPropertyFromElement(file, exporterIFC, element, revitParamNameToUse, builtInParameter, - ifcPropertyName, valueType); + propHnd = PropertyUtil.CreateIsothermalMoistureCapacityPropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType); break; } case PropertyType.HeatFluxDensity: { - propHnd = PropertyUtil.CreateHeatFluxDensityPropertyFromElement(file, exporterIFC, element, revitParamNameToUse, builtInParameter, - ifcPropertyName, valueType); + propHnd = PropertyUtil.CreateHeatFluxDensityPropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType); break; } case PropertyType.Label: { - propHnd = PropertyUtil.CreateLabelPropertyFromElement(file, element, revitParamNameToUse, builtInParameter, ifcPropertyName, valueType, propertyEnumerationType); + propHnd = PropertyUtil.CreateLabelPropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType, propertyEnumerationType); break; } case PropertyType.Length: { - propHnd = PropertyUtil.CreateLengthMeasurePropertyFromElement(file, exporterIFC, element, revitParamNameToUse, + propHnd = PropertyUtil.CreateLengthPropertyFromElement(file, element, revitParamNameToUse, builtInParameter, ifcPropertyName, valueType); break; } case PropertyType.LinearForce: { - propHnd = PropertyUtil.CreateLinearForcePropertyFromElement(file, exporterIFC, element, revitParamNameToUse, builtInParameter, - ifcPropertyName, valueType); + propHnd = PropertyUtil.CreateLinearForcePropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType); break; } case PropertyType.LinearMoment: { - propHnd = PropertyUtil.CreateLinearMomentMeasurePropertyFromElement(file, exporterIFC, element, revitParamNameToUse, + propHnd = PropertyUtil.CreateLinearMomentPropertyFromElement(file, element, revitParamNameToUse, builtInParameter, ifcPropertyName, valueType); break; } case PropertyType.LinearStiffness: { - propHnd = PropertyUtil.CreateLinearStiffnessMeasurePropertyFromElement(file, exporterIFC, element, revitParamNameToUse, + propHnd = PropertyUtil.CreateLinearStiffnessPropertyFromElement(file, element, revitParamNameToUse, builtInParameter, ifcPropertyName, valueType); break; } case PropertyType.LinearVelocity: { - propHnd = PropertyUtil.CreateLinearVelocityPropertyFromElement(file, exporterIFC, element, revitParamNameToUse, - ifcPropertyName, valueType); + propHnd = PropertyUtil.CreateLinearVelocityPropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType); break; } case PropertyType.Logical: @@ -623,217 +609,412 @@ private static IFCData GetPropertyDataFromString(string valueString, PropertyTyp } case PropertyType.LuminousFlux: { - propHnd = PropertyUtil.CreateLuminousFluxMeasurePropertyFromElement(file, exporterIFC, element, revitParamNameToUse, + propHnd = PropertyUtil.CreateLuminousFluxPropertyFromElement(file, element, revitParamNameToUse, builtInParameter, ifcPropertyName, valueType); break; } case PropertyType.LuminousIntensity: { - propHnd = PropertyUtil.CreateLuminousIntensityPropertyFromElement(file, exporterIFC, element, revitParamNameToUse, builtInParameter, - ifcPropertyName, valueType); + propHnd = PropertyUtil.CreateLuminousIntensityPropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType); break; } case PropertyType.Mass: { - propHnd = PropertyUtil.CreateMassPropertyFromElement(file, exporterIFC, element, revitParamNameToUse, builtInParameter, - ifcPropertyName, valueType); + propHnd = PropertyUtil.CreateMassPropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType); break; } case PropertyType.MassDensity: { - propHnd = PropertyUtil.CreateMassDensityPropertyFromElement(file, exporterIFC, element, revitParamNameToUse, builtInParameter, - ifcPropertyName, valueType); + propHnd = PropertyUtil.CreateMassDensityPropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType); break; } case PropertyType.MassFlowRate: { - propHnd = PropertyUtil.CreateMassFlowRatePropertyFromElement(file, exporterIFC, element, revitParamNameToUse, builtInParameter, - ifcPropertyName, valueType); + propHnd = PropertyUtil.CreateMassFlowRatePropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType); break; } case PropertyType.MassPerLength: { - propHnd = PropertyUtil.CreateMassPerLengthMeasurePropertyFromElement(file, exporterIFC, element, revitParamNameToUse, + propHnd = PropertyUtil.CreateMassPerLengthPropertyFromElement(file, element, revitParamNameToUse, builtInParameter, ifcPropertyName, valueType); break; } case PropertyType.ModulusOfElasticity: { - propHnd = PropertyUtil.CreateModulusOfElasticityPropertyFromElement(file, exporterIFC, element, revitParamNameToUse, builtInParameter, - ifcPropertyName, valueType); + propHnd = PropertyUtil.CreateModulusOfElasticityPropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType); break; } case PropertyType.MoistureDiffusivity: { - propHnd = PropertyUtil.CreateMoistureDiffusivityPropertyFromElement(file, exporterIFC, element, revitParamNameToUse, builtInParameter, - ifcPropertyName, valueType); + propHnd = PropertyUtil.CreateMoistureDiffusivityPropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType); break; } case PropertyType.MomentOfInertia: { - propHnd = PropertyUtil.CreateMomentOfInertiaPropertyFromElement(file, exporterIFC, element, revitParamNameToUse, builtInParameter, - ifcPropertyName, valueType); + propHnd = PropertyUtil.CreateMomentOfInertiaPropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType); break; } case PropertyType.NormalisedRatio: { - propHnd = PropertyUtil.CreateNormalisedRatioPropertyFromElement(file, exporterIFC, element, revitParamNameToUse, - ifcPropertyName, valueType); + propHnd = PropertyUtil.CreateNormalisedRatioPropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType); break; } case PropertyType.Numeric: { - propHnd = PropertyUtil.CreateNumericPropertyFromElement(file, element, revitParamNameToUse, ifcPropertyName, valueType); + propHnd = PropertyUtil.CreateNumericPropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType); break; } case PropertyType.PlaneAngle: { - propHnd = PropertyUtil.CreatePlaneAngleMeasurePropertyFromElement(file, element, revitParamNameToUse, ifcPropertyName, - valueType); + propHnd = PropertyUtil.CreatePlaneAnglePropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType); break; } case PropertyType.PlanarForce: { - propHnd = PropertyUtil.CreatePlanarForcePropertyFromElement(file, exporterIFC, element, revitParamNameToUse, builtInParameter, - ifcPropertyName, valueType); + propHnd = PropertyUtil.CreatePlanarForcePropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType); break; } case PropertyType.PositiveLength: { - propHnd = PropertyUtil.CreatePositiveLengthMeasurePropertyFromElement(file, exporterIFC, element, revitParamNameToUse, + propHnd = PropertyUtil.CreatePositiveLengthPropertyFromElement(file, element, revitParamNameToUse, builtInParameter, ifcPropertyName, valueType); break; } case PropertyType.PositiveRatio: { - propHnd = PropertyUtil.CreatePositiveRatioPropertyFromElement(file, exporterIFC, element, revitParamNameToUse, - ifcPropertyName, valueType); + propHnd = PropertyUtil.CreatePositiveRatioPropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType); break; } case PropertyType.PositivePlaneAngle: { - propHnd = PositivePlaneAnglePropertyUtil.CreatePositivePlaneAngleMeasurePropertyFromElement(file, element, revitParamNameToUse, ifcPropertyName, - valueType); + propHnd = PropertyUtil.CreatePositivePlaneAnglePropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType); break; } case PropertyType.Power: { - propHnd = PropertyUtil.CreatePowerPropertyFromElement(file, exporterIFC, element, revitParamNameToUse, builtInParameter, - ifcPropertyName, valueType); + propHnd = PropertyUtil.CreatePowerPropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType); break; } case PropertyType.Pressure: { - propHnd = FrequencyPropertyUtil.CreatePressurePropertyFromElement(file, exporterIFC, element, revitParamNameToUse, builtInParameter, - ifcPropertyName, valueType); + propHnd = PropertyUtil.CreatePressurePropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType); break; } case PropertyType.Ratio: { - propHnd = PropertyUtil.CreateRatioPropertyFromElement(file, exporterIFC, element, revitParamNameToUse, ifcPropertyName, - valueType); + propHnd = PropertyUtil.CreateRatioPropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType); break; } case PropertyType.Real: { - propHnd = PropertyUtil.CreateRealPropertyFromElement(file, element, revitParamNameToUse, ifcPropertyName, valueType); + propHnd = PropertyUtil.CreateRealPropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType); break; } case PropertyType.RotationalFrequency: { - propHnd = PropertyUtil.CreateRotationalFrequencyPropertyFromElement(file, exporterIFC, element, revitParamNameToUse, builtInParameter, - ifcPropertyName, valueType); + propHnd = PropertyUtil.CreateRotationalFrequencyPropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType); break; } case PropertyType.SoundPower: { - propHnd = PropertyUtil.CreateSoundPowerPropertyFromElement(file, exporterIFC, element, revitParamNameToUse, + propHnd = PropertyUtil.CreateSoundPowerPropertyFromElement(file, element, revitParamNameToUse, builtInParameter, ifcPropertyName, valueType); break; } case PropertyType.SoundPressure: { - propHnd = PropertyUtil.CreateSoundPressurePropertyFromElement(file, exporterIFC, element, revitParamNameToUse, + propHnd = PropertyUtil.CreateSoundPressurePropertyFromElement(file, element, revitParamNameToUse, builtInParameter, ifcPropertyName, valueType); break; } case PropertyType.SpecificHeatCapacity: { - propHnd = PropertyUtil.CreateSpecificHeatCapacityPropertyFromElement(file, exporterIFC, element, revitParamNameToUse, + propHnd = PropertyUtil.CreateSpecificHeatCapacityPropertyFromElement(file, element, revitParamNameToUse, builtInParameter, ifcPropertyName, valueType); break; } case PropertyType.Text: { - propHnd = PropertyUtil.CreateTextPropertyFromElement(file, element, revitParamNameToUse, builtInParameter, ifcPropertyName, valueType, propertyEnumerationType); + propHnd = PropertyUtil.CreateTextPropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType, propertyEnumerationType); break; } case PropertyType.ThermalConductivity: { - propHnd = PropertyUtil.CreateThermalConductivityPropertyFromElement(file, exporterIFC, element, revitParamNameToUse, + propHnd = PropertyUtil.CreateThermalConductivityPropertyFromElement(file, element, revitParamNameToUse, builtInParameter, ifcPropertyName, valueType); break; } case PropertyType.ThermalExpansionCoefficient: { - propHnd = PropertyUtil.CreateThermalExpansionCoefficientPropertyFromElement(file, exporterIFC, element, revitParamNameToUse, + propHnd = PropertyUtil.CreateThermalExpansionCoefficientPropertyFromElement(file, element, revitParamNameToUse, builtInParameter, ifcPropertyName, valueType); break; } case PropertyType.ThermalResistance: { - propHnd = PropertyUtil.CreateThermalResistanceMeasurePropertyFromElement(file, exporterIFC, element, revitParamNameToUse, + propHnd = PropertyUtil.CreateThermalResistancePropertyFromElement(file, element, revitParamNameToUse, builtInParameter, ifcPropertyName, valueType); break; } case PropertyType.ThermalTransmittance: { - propHnd = PropertyUtil.CreateThermalTransmittancePropertyFromElement(file, exporterIFC, element, revitParamNameToUse, + propHnd = PropertyUtil.CreateThermalTransmittancePropertyFromElement(file, element, revitParamNameToUse, builtInParameter, ifcPropertyName, valueType); break; } case PropertyType.ThermodynamicTemperature: { - propHnd = PropertyUtil.CreateThermodynamicTemperaturePropertyFromElement(file, exporterIFC, element, revitParamNameToUse, + propHnd = PropertyUtil.CreateThermodynamicTemperaturePropertyFromElement(file, element, revitParamNameToUse, builtInParameter, ifcPropertyName, valueType); break; } case PropertyType.VaporPermeability: { - propHnd = PropertyUtil.CreateVaporPermeabilityMeasurePropertyFromElement(file, exporterIFC, element, revitParamNameToUse, + propHnd = PropertyUtil.CreateVaporPermeabilityPropertyFromElement(file, element, revitParamNameToUse, builtInParameter, ifcPropertyName, valueType); break; } case PropertyType.Volume: { - propHnd = PropertyUtil.CreateVolumeMeasurePropertyFromElement(file, exporterIFC, element, revitParamNameToUse, + propHnd = PropertyUtil.CreateVolumePropertyFromElement(file, element, revitParamNameToUse, builtInParameter, ifcPropertyName, valueType); break; } case PropertyType.VolumetricFlowRate: { - propHnd = PropertyUtil.CreateVolumetricFlowRatePropertyFromElement(file, exporterIFC, element, revitParamNameToUse, + propHnd = PropertyUtil.CreateVolumetricFlowRatePropertyFromElement(file, element, revitParamNameToUse, builtInParameter, ifcPropertyName, valueType); break; } case PropertyType.Time: { - propHnd = PropertyUtil.CreateTimePropertyFromElement(file, exporterIFC, element, revitParamNameToUse, + propHnd = PropertyUtil.CreateTimePropertyFromElement(file, element, revitParamNameToUse, builtInParameter, ifcPropertyName, valueType); break; } case PropertyType.Torque: { - propHnd = PropertyUtil.CreateTorqueMeasurePropertyFromElement(file, exporterIFC, element, revitParamNameToUse, + propHnd = PropertyUtil.CreateTorquePropertyFromElement(file, element, revitParamNameToUse, builtInParameter, ifcPropertyName, valueType); break; } case PropertyType.WarpingConstant: { - propHnd = PropertyUtil.CreateWarpingConstantMeasurePropertyFromElement(file, exporterIFC, element, revitParamNameToUse, + propHnd = PropertyUtil.CreateWarpingConstantPropertyFromElement(file, element, revitParamNameToUse, builtInParameter, ifcPropertyName, valueType); break; } + case PropertyType.CostPerArea: + { + propHnd = PropertyUtil.CreateUserDefinedRealPropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType, SpecTypeId.CostPerArea, "COSTPERAREA"); + break; + } + case PropertyType.ApparentPowerDensity: + { + propHnd = PropertyUtil.CreateUserDefinedRealPropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType, SpecTypeId.ApparentPowerDensity, "APPARENTPOWERDENSITY"); + break; + } + case PropertyType.CostRateEnergy: + { + propHnd = PropertyUtil.CreateUserDefinedRealPropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType, SpecTypeId.CostRateEnergy, "COSTRATEENERGY"); + break; + } + case PropertyType.CostRatePower: + { + propHnd = PropertyUtil.CreateUserDefinedRealPropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType, SpecTypeId.CostRatePower, "COSTRATEPOWER"); + break; + } + case PropertyType.ElectricalEfficacy: + { + propHnd = PropertyUtil.CreateUserDefinedRealPropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType, SpecTypeId.Efficacy, "LUMINOUSEFFICACY"); + break; + } + case PropertyType.Luminance: + { + propHnd = PropertyUtil.CreateUserDefinedRealPropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType, SpecTypeId.Luminance, "LUMINANCE"); + break; + } + case PropertyType.ElectricalPowerDensity: + { + propHnd = PropertyUtil.CreateUserDefinedRealPropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType, SpecTypeId.ElectricalPowerDensity, "ELECTRICALPOWERDENSITY"); + break; + } + case PropertyType.PowerPerLength: + { + propHnd = PropertyUtil.CreateUserDefinedRealPropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType, SpecTypeId.PowerPerLength, "POWERPERLENGTH"); + break; + } + case PropertyType.ElectricalResistivity: + { + propHnd = PropertyUtil.CreateUserDefinedRealPropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType, SpecTypeId.ElectricalResistivity, "ELECTRICALRESISTIVITY"); + break; + } + case PropertyType.HeatCapacityPerArea: + { + propHnd = PropertyUtil.CreateUserDefinedRealPropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType, SpecTypeId.HeatCapacityPerArea, "HEATCAPACITYPERAREA"); + break; + } + case PropertyType.ThermalGradientCoefficientForMoistureCapacity: + { + propHnd = PropertyUtil.CreateUserDefinedRealPropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType, SpecTypeId.ThermalGradientCoefficientForMoistureCapacity, "THERMALGRADIENTCOEFFICIENTFORMOISTURECAPACITY"); + break; + } + case PropertyType.ThermalMass: + { + propHnd = PropertyUtil.CreateUserDefinedRealPropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType, SpecTypeId.ThermalMass, "THERMALMASS"); + break; + } + case PropertyType.AirFlowDensity: + { + propHnd = PropertyUtil.CreateUserDefinedRealPropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType, SpecTypeId.AirFlowDensity, "AIRFLOWDENSITY"); + break; + } + case PropertyType.AirFlowDividedByCoolingLoad: + { + propHnd = PropertyUtil.CreateUserDefinedRealPropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType, SpecTypeId.AirFlowDividedByCoolingLoad, "AIRFLOWDIVIDEDBYCOOLINGLOAD"); + break; + } + case PropertyType.AirFlowDividedByVolume: + { + propHnd = PropertyUtil.CreateUserDefinedRealPropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType, SpecTypeId.AirFlowDividedByVolume, "AIRFLOWDIVIDEDBYVOLUME"); + break; + } + case PropertyType.AreaDividedByCoolingLoad: + { + propHnd = PropertyUtil.CreateUserDefinedRealPropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType, SpecTypeId.AreaDividedByCoolingLoad, "AREADIVIDEDBYCOOLINGLOAD"); + break; + } + case PropertyType.AreaDividedByHeatingLoad: + { + propHnd = PropertyUtil.CreateUserDefinedRealPropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType, SpecTypeId.AreaDividedByHeatingLoad, "AREADIVIDEDBYHEATINGLOAD"); + break; + } + case PropertyType.CoolingLoadDividedByArea: + { + propHnd = PropertyUtil.CreateUserDefinedRealPropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType, SpecTypeId.CoolingLoadDividedByArea, "COOLINGLOADDIVIDEDBYAREA"); + break; + } + case PropertyType.CoolingLoadDividedByVolume: + { + propHnd = PropertyUtil.CreateUserDefinedRealPropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType, SpecTypeId.CoolingLoadDividedByVolume, "COOLINGLOADDIVIDEDBYVOLUME"); + break; + } + case PropertyType.FlowPerPower: + { + propHnd = PropertyUtil.CreateUserDefinedRealPropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType, SpecTypeId.FlowPerPower, "FLOWPERPOWER"); + break; + } + case PropertyType.FrictionLoss: + { + propHnd = PropertyUtil.CreateUserDefinedRealPropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType, SpecTypeId.HvacFriction, "FRICTIONLOSS"); + break; + } + case PropertyType.HeatingLoadDividedByArea: + { + propHnd = PropertyUtil.CreateUserDefinedRealPropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType, SpecTypeId.HeatingLoadDividedByArea, "HEATINGLOADDIVIDEDBYAREA"); + break; + } + case PropertyType.HeatingLoadDividedByVolume: + { + propHnd = PropertyUtil.CreateUserDefinedRealPropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType, SpecTypeId.HeatingLoadDividedByVolume, "HEATINGLOADDIVIDEDBYVOLUME"); + break; + } + case PropertyType.PowerPerFlow: + { + propHnd = PropertyUtil.CreateUserDefinedRealPropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType, SpecTypeId.PowerPerFlow, "POWERPERFLOW"); + break; + } + case PropertyType.PipingFriction: + { + propHnd = PropertyUtil.CreateUserDefinedRealPropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType, SpecTypeId.PipingFriction, "PIPINGFRICTION"); + break; + } + case PropertyType.AreaSpringCoefficient: + { + propHnd = PropertyUtil.CreateUserDefinedRealPropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType, SpecTypeId.AreaSpringCoefficient, "AREASPRINGCOEFFICIENT"); + break; + } + case PropertyType.LineSpringCoefficient: + { + propHnd = PropertyUtil.CreateUserDefinedRealPropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType, SpecTypeId.LineSpringCoefficient, "LINESPRINGCOEFFICIENT"); + break; + } + case PropertyType.MassPerUnitArea: + { + propHnd = PropertyUtil.CreateUserDefinedRealPropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType, SpecTypeId.MassPerUnitArea, "MASSPERUNITAREA"); + break; + } + case PropertyType.ReinforcementAreaPerUnitLength: + { + propHnd = PropertyUtil.CreateUserDefinedRealPropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType, SpecTypeId.ReinforcementAreaPerUnitLength, "REINFORCEMENTAREAPERUNITLENGTH"); + break; + } + case PropertyType.RotationalLineSpringCoefficient: + { + propHnd = PropertyUtil.CreateUserDefinedRealPropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType, SpecTypeId.RotationalLineSpringCoefficient, "ROTATIONALLINESPRINGCOEFFICIENT"); + break; + } + case PropertyType.RotationalPointSpringCoefficient: + { + propHnd = PropertyUtil.CreateUserDefinedRealPropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType, SpecTypeId.RotationalPointSpringCoefficient, "ROTATIONALPOINTSPRINGCOEFFICIENT"); + break; + } + case PropertyType.UnitWeight: + { + propHnd = PropertyUtil.CreateUserDefinedRealPropertyFromElement(file, element, revitParamNameToUse, + builtInParameter, ifcPropertyName, valueType, SpecTypeId.UnitWeight, "UNITWEIGHT"); + break; + } // Known unhandled cases: case PropertyType.IfcCalendarDate: case PropertyType.IfcClassificationReference: @@ -1095,61 +1276,61 @@ private static IFCData GetPropertyDataFromString(string valueString, PropertyTyp case PropertyType.Real: { double val = (useCalculator) ? PropertyCalculator.GetDoubleValue() : double.Parse(propertyValue); - propHnd = PropertyUtil.CreateRealPropertyFromCache(file, propertyName, val, valueType); + propHnd = PropertyUtil.CreateRealPropertyFromCache(file, propertyName, new List() { val }, valueType, null); break; } case PropertyType.Length: { double val = (useCalculator) ? PropertyCalculator.GetDoubleValue() : double.Parse(propertyValue); - propHnd = PropertyUtil.CreateLengthMeasurePropertyFromCache(file, propertyName, val, valueType); + propHnd = PropertyUtil.CreateLengthPropertyFromCache(file, propertyName, new List() { val }, valueType, null); break; } case PropertyType.PositiveLength: { double val = (useCalculator) ? PropertyCalculator.GetDoubleValue() : double.Parse(propertyValue); - propHnd = PropertyUtil.CreatePositiveLengthMeasureProperty(file, propertyName, val, valueType); + propHnd = PropertyUtil.CreatePositiveLengthProperty(file, propertyName, new List() { val }, valueType, null); break; } case PropertyType.NormalisedRatio: { double val = (useCalculator) ? PropertyCalculator.GetDoubleValue() : double.Parse(propertyValue); - propHnd = PropertyUtil.CreateNormalisedRatioMeasureProperty(file, propertyName, val, valueType); + propHnd = PropertyUtil.CreateNormalisedRatioProperty(file, propertyName, new List() { val }, valueType, null); break; } case PropertyType.Numeric: { double val = (useCalculator) ? PropertyCalculator.GetDoubleValue() : double.Parse(propertyValue); - propHnd = PropertyUtil.CreateNumericPropertyFromCache(file, propertyName, val, valueType); + propHnd = PropertyUtil.CreateNumericPropertyFromCache(file, propertyName, new List() { val }, valueType, null); break; } case PropertyType.PositiveRatio: { double val = (useCalculator) ? PropertyCalculator.GetDoubleValue() : double.Parse(propertyValue); - propHnd = PropertyUtil.CreatePositiveRatioMeasureProperty(file, propertyName, val, valueType); + propHnd = PropertyUtil.CreatePositiveRatioProperty(file, propertyName, new List() { val }, valueType, null); break; } case PropertyType.Ratio: { double val = (useCalculator) ? PropertyCalculator.GetDoubleValue() : double.Parse(propertyValue); - propHnd = PropertyUtil.CreateRatioMeasureProperty(file, propertyName, val, valueType); + propHnd = PropertyUtil.CreateRatioProperty(file, propertyName, new List() { val }, valueType, null); break; } case PropertyType.PlaneAngle: { double val = (useCalculator) ? PropertyCalculator.GetDoubleValue() : double.Parse(propertyValue); - propHnd = PropertyUtil.CreatePlaneAngleMeasurePropertyFromCache(file, propertyName, val, valueType); + propHnd = PropertyUtil.CreatePlaneAnglePropertyFromCache(file, propertyName, new List() { val }, valueType, null); break; } case PropertyType.PositivePlaneAngle: { double val = (useCalculator) ? PropertyCalculator.GetDoubleValue() : double.Parse(propertyValue); - propHnd = PositivePlaneAnglePropertyUtil.CreatePositivePlaneAngleMeasurePropertyFromCache(file, propertyName, val, valueType); + propHnd = PropertyUtil.CreatePositivePlaneAnglePropertyFromCache(file, propertyName, new List() { val }, valueType, null); break; } case PropertyType.Area: { double val = (useCalculator) ? PropertyCalculator.GetDoubleValue() : double.Parse(propertyValue); - propHnd = PropertyUtil.CreateAreaMeasureProperty(file, propertyName, val, valueType); + propHnd = PropertyUtil.CreateAreaProperty(file, propertyName, new List() { val }, valueType, null); break; } case PropertyType.Count: @@ -1169,37 +1350,37 @@ private static IFCData GetPropertyDataFromString(string valueString, PropertyTyp case PropertyType.Frequency: { double val = (useCalculator) ? PropertyCalculator.GetDoubleValue() : double.Parse(propertyValue); - propHnd = FrequencyPropertyUtil.CreateFrequencyProperty(file, propertyName, val, valueType); + propHnd = PropertyUtil.CreateFrequencyProperty(file, propertyName, new List() { val }, valueType, null); break; } case PropertyType.Power: { double val = (useCalculator) ? PropertyCalculator.GetDoubleValue() : double.Parse(propertyValue); - propHnd = PropertyUtil.CreatePowerPropertyFromCache(file, propertyName, val, valueType); + propHnd = PropertyUtil.CreatePowerPropertyFromCache(file, propertyName, new List() { val }, valueType, null); break; } case PropertyType.ThermodynamicTemperature: { double val = (useCalculator) ? PropertyCalculator.GetDoubleValue() : double.Parse(propertyValue); - propHnd = PropertyUtil.CreateThermodynamicTemperaturePropertyFromCache(file, propertyName, val, valueType); + propHnd = PropertyUtil.CreateThermodynamicTemperaturePropertyFromCache(file, propertyName, new List() { val }, valueType, null); break; } case PropertyType.ThermalTransmittance: { double val = (useCalculator) ? PropertyCalculator.GetDoubleValue() : double.Parse(propertyValue); - propHnd = PropertyUtil.CreateThermalTransmittancePropertyFromCache(file, propertyName, val, valueType); + propHnd = PropertyUtil.CreateThermalTransmittancePropertyFromCache(file, propertyName, new List() { val }, valueType, null); break; } case PropertyType.VolumetricFlowRate: { double val = (useCalculator) ? PropertyCalculator.GetDoubleValue() : double.Parse(propertyValue); - propHnd = PropertyUtil.CreateVolumetricFlowRateMeasureProperty(file, propertyName, val, valueType); + propHnd = PropertyUtil.CreateVolumetricFlowRateProperty(file, propertyName, new List() { val }, valueType, null); break; } case PropertyType.LinearVelocity: { double val = (useCalculator) ? PropertyCalculator.GetDoubleValue() : double.Parse(propertyValue); - propHnd = PropertyUtil.CreateLinearVelocityMeasureProperty(file, propertyName, val, valueType); + propHnd = PropertyUtil.CreateLinearVelocityProperty(file, propertyName, new List() { val }, valueType, null); break; } default: diff --git a/Source/Revit.IFC.Export/Exporter/PropertySet/PropertyUtil.cs b/Source/Revit.IFC.Export/Exporter/PropertySet/PropertyUtil.cs index 1bb85224..694cf281 100644 --- a/Source/Revit.IFC.Export/Exporter/PropertySet/PropertyUtil.cs +++ b/Source/Revit.IFC.Export/Exporter/PropertySet/PropertyUtil.cs @@ -27,6 +27,7 @@ using Revit.IFC.Export.Toolkit; using System.ComponentModel; using System.Linq; +using System.Collections; namespace Revit.IFC.Export.Exporter.PropertySet { @@ -61,39 +62,34 @@ public static ISet EntitiesWithNoRelatedType } } - - - public static IFCAnyHandle CreateCommonProperty(IFCFile file, string propertyName, IFCData valueData, PropertyValueType valueType, string unitTypeKey) + public static IFCAnyHandle CreateCommonPropertyFromList(IFCFile file, string propertyName, IList valueList, PropertyValueType valueType, string unitTypeKey) { - IFCAnyHandle unitHnd = (!ExporterCacheManager.ExportOptionsCache.ExportAs4ReferenceView && unitTypeKey != null) ? ExporterCacheManager.UnitsCache[unitTypeKey] : null; + if (valueList == null || valueList.All(x => x == null)) + return null; + + IFCAnyHandle unitHnd = (!ExporterCacheManager.ExportOptionsCache.ExportAs4ReferenceView && unitTypeKey != null) ? ExporterCacheManager.UnitsCache.FindUserDefinedUnit(unitTypeKey) : null; switch (valueType) { case PropertyValueType.EnumeratedValue: { - IList valueList = new List(); - valueList.Add(valueData); return IFCInstanceExporter.CreatePropertyEnumeratedValue(file, propertyName, null, valueList, null); } case PropertyValueType.SingleValue: { - return IFCInstanceExporter.CreatePropertySingleValue(file, propertyName, null, valueData, unitHnd); + return IFCInstanceExporter.CreatePropertySingleValue(file, propertyName, null, valueList.First(), unitHnd); } case PropertyValueType.ListValue: { - IList valueList = new List(); - valueList.Add(valueData); return IFCInstanceExporter.CreatePropertyListValue(file, propertyName, null, valueList, unitHnd); } case PropertyValueType.BoundedValue: { - IList valueList = new List(); - valueList.Add(valueData); return CreateBoundedValuePropertyFromList(file, propertyName, valueList, unitTypeKey); } case PropertyValueType.TableValue: { - // must be handled in CreatePropertyFromElementBase + // for now is handled in CreatePropertyFromElementBase as Multiline Text throw new InvalidOperationException("Unhandled table property!"); } default: @@ -101,6 +97,11 @@ public static IFCAnyHandle CreateCommonProperty(IFCFile file, string propertyNam } } + public static IFCAnyHandle CreateCommonProperty(IFCFile file, string propertyName, IFCData valueData, PropertyValueType valueType, string unitTypeKey) + { + return CreateCommonPropertyFromList(file, propertyName, new List() { valueData }, valueType, unitTypeKey); + } + /// /// Creates an IfcPropertyBoundedValue. /// @@ -112,7 +113,7 @@ protected static IFCAnyHandle CreateBoundedValuePropertyFromList(IFCFile file, s { if (valueDataList.Count < 1) throw new InvalidOperationException("Invalid bounded property!"); - IFCAnyHandle unitHnd = (!ExporterCacheManager.ExportOptionsCache.ExportAs4ReferenceView && unitTypeKey != null) ? ExporterCacheManager.UnitsCache[unitTypeKey] : null; + IFCAnyHandle unitHnd = (!ExporterCacheManager.ExportOptionsCache.ExportAs4ReferenceView && unitTypeKey != null) ? ExporterCacheManager.UnitsCache.FindUserDefinedUnit(unitTypeKey) : null; IFCData setPointValue = valueDataList[0]; IFCData upperBoundValue = valueDataList.Count > 1 ? valueDataList[1] : null; @@ -125,7 +126,7 @@ protected static IFCAnyHandle CreateBoundedValuePropertyFromList(IFCFile file, s else { return IFCInstanceExporter.CreatePropertyBoundedValue(file, propertyName, null, lowerBoundValue, upperBoundValue, setPointValue, unitHnd); - } + } } /// @@ -140,8 +141,8 @@ protected static IFCAnyHandle CreateBoundedValuePropertyFromList(IFCFile file, s /// The created property handle. public static IFCAnyHandle CreateTableProperty(IFCFile file, string propertyName, IList definingValues, IList definedValues, string definingUnitTypeKey, string definedUnitTypeKey) { - IFCAnyHandle definingUnitHnd = (!ExporterCacheManager.ExportOptionsCache.ExportAs4ReferenceView && definingUnitTypeKey != null) ? ExporterCacheManager.UnitsCache[definingUnitTypeKey] : null; - IFCAnyHandle definedUnitHnd = (!ExporterCacheManager.ExportOptionsCache.ExportAs4ReferenceView && definedUnitTypeKey != null) ? ExporterCacheManager.UnitsCache[definedUnitTypeKey] : null; + IFCAnyHandle definingUnitHnd = (!ExporterCacheManager.ExportOptionsCache.ExportAs4ReferenceView && definingUnitTypeKey != null) ? ExporterCacheManager.UnitsCache.FindUserDefinedUnit(definingUnitTypeKey) : null; + IFCAnyHandle definedUnitHnd = (!ExporterCacheManager.ExportOptionsCache.ExportAs4ReferenceView && definedUnitTypeKey != null) ? ExporterCacheManager.UnitsCache.FindUserDefinedUnit(definedUnitTypeKey) : null; return IFCInstanceExporter.CreatePropertyTableValue(file, propertyName, null, definingValues, definedValues, definingUnitHnd, definedUnitHnd); } @@ -151,7 +152,7 @@ public static IFCAnyHandle CreateTableProperty(IFCFile file, string propertyName /// /// The IFC file. /// The name of the property. - /// The value of the property. + /// The values of the property. /// The value type of the property. /// The created property handle. public static IFCAnyHandle CreateLabelProperty(IFCFile file, string propertyName, string value, PropertyValueType valueType, @@ -208,7 +209,7 @@ public static IFCAnyHandle CreateTableProperty(IFCFile file, string propertyName /// /// The IFC file. /// The name of the property. - /// The value of the property. + /// The values of the property. /// The value type of the property. /// The created property handle. public static IFCAnyHandle CreateTextProperty(IFCFile file, string propertyName, string value, PropertyValueType valueType) @@ -222,7 +223,7 @@ public static IFCAnyHandle CreateTextProperty(IFCFile file, string propertyName, /// /// The IFC file. /// The name of the property. - /// The value of the property. + /// The values of the property. /// The value type of the property. /// The created property handle. public static IFCAnyHandle CreateTextPropertyFromCache(IFCFile file, string propertyName, string value, PropertyValueType valueType) @@ -306,7 +307,7 @@ public static IFCAnyHandle CreateTextPropertyFromElement(IFCFile file, Element e /// The IFC file. /// The id of the parameter that generated the value. /// The name of the property. - /// The value of the property. + /// The values of the property. /// The value type of the property. /// Whether to cache all strings (true), or only the empty string (false). /// The type of the enum, null if valueType isn't EnumeratedValue. @@ -376,7 +377,7 @@ public static IFCAnyHandle CreateTextPropertyFromElement(IFCFile file, Element e /// /// The IFC file. /// The name of the property. - /// The value of the property. + /// The values of the property. /// The value type of the property. /// The created property handle. public static IFCAnyHandle CreateIdentifierProperty(IFCFile file, string propertyName, string value, PropertyValueType valueType) @@ -390,7 +391,7 @@ public static IFCAnyHandle CreateIdentifierProperty(IFCFile file, string propert /// /// The IFC file. /// The name of the property. - /// The value of the property. + /// The values of the property. /// The value type of the property. /// The created property handle. public static IFCAnyHandle CreateIdentifierPropertyFromCache(IFCFile file, string propertyName, string value, PropertyValueType valueType) @@ -411,7 +412,7 @@ public static IFCAnyHandle CreateIdentifierPropertyFromCache(IFCFile file, strin /// /// The IFC file. /// The name of the property. - /// The value of the property. + /// The values of the property. /// The value type of the property. /// The created property handle. public static IFCAnyHandle CreateBooleanProperty(IFCFile file, string propertyName, bool value, PropertyValueType valueType) @@ -425,7 +426,7 @@ public static IFCAnyHandle CreateBooleanProperty(IFCFile file, string propertyNa /// /// The IFC file. /// The name of the property. - /// The value of the property. + /// The values of the property. /// The value type of the property. /// The created property handle. public static IFCAnyHandle CreateLogicalProperty(IFCFile file, string propertyName, IFCLogical value, PropertyValueType valueType) @@ -479,7 +480,7 @@ public static IFCAnyHandle CreateLogicalPropertyFromCache(IFCFile file, string p /// /// The IFC file. /// The name of the property. - /// The value of the property. + /// The values of the property. /// The value type of the property. /// The created property handle. public static IFCAnyHandle CreateIntegerProperty(IFCFile file, string propertyName, int value, PropertyValueType valueType) @@ -519,2433 +520,1980 @@ public static IFCAnyHandle CreateIntegerPropertyFromCache(IFCFile file, string p internal static double? CanCacheDouble(double value) { - // We have a partial cache here - cache multiples of 0.5 up to 10. - if (MathUtil.IsAlmostZero(value)) - return 0.0; - - double valueTimes2 = Math.Floor(value * 2 + MathUtil.Eps()); - if (valueTimes2 > 0 && valueTimes2 <= 20 && MathUtil.IsAlmostZero(value * 2 - valueTimes2)) - return valueTimes2 / 2; - - return null; - } - - internal static double? CanCacheLength(double unscaledValue, double value) - { - // We have a partial cache here, based on the unscaledValue. + // We have a partial cache here // Cache multiples of +/- 0.05 up to 10. - // Cache multiples of +/- 50 up to 10000. + // Cache multiples of +/- 0.5 up to 300. + // Cache multiples of +/- 5 reset. if (MathUtil.IsAlmostZero(value)) return 0.0; - // approximate tests for most common scales are good enough here. - bool isNegative = (unscaledValue < 0); - double unscaledPositiveValue = isNegative ? -unscaledValue : unscaledValue; - double eps = MathUtil.Eps(); - - if (unscaledPositiveValue <= 10.0 + eps) - { - double unscaledPositiveValueTimes2 = Math.Floor(unscaledPositiveValue * 2 + eps); - if (MathUtil.IsAlmostZero(unscaledPositiveValue * 2 - unscaledPositiveValueTimes2)) - { - double scaledPositiveValue = UnitUtil.ScaleLength(unscaledPositiveValueTimes2 / 2); - return isNegative ? -scaledPositiveValue : scaledPositiveValue; - } - return null; - } + double multiplier = 5.0; + if (Math.Abs(value) <= 10.0 + MathUtil.Eps()) + multiplier = 0.05; + else if (Math.Abs(value) <= 300.0 + MathUtil.Eps()) + multiplier = 0.5; - if (unscaledPositiveValue <= 10000.0 + eps) - { - double unscaledPositiveValueDiv50 = Math.Floor(unscaledPositiveValue / 50.0 + eps); - if (MathUtil.IsAlmostEqual(unscaledPositiveValue / 50.0, unscaledPositiveValueDiv50)) - { - double scaledPositiveValue = UnitUtil.ScaleLength(unscaledPositiveValueDiv50 * 50.0); - return isNegative ? -scaledPositiveValue : scaledPositiveValue; - } - } + double valueCorrected = Math.Floor(value / multiplier + MathUtil.Eps()); + if (MathUtil.IsAlmostZero(value / multiplier - valueCorrected)) + return valueCorrected * multiplier; return null; } - internal static double? CanCachePower(double value) + /// Create a count measure property. + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateCountMeasureProperty(IFCFile file, string propertyName, double value, PropertyValueType valueType) { - // Allow caching of values between 0 and 300, in multiples of 5 - double eps = MathUtil.Eps(); - if (value < -eps || value > 300.0 + eps) - return null; - if (MathUtil.IsAlmostZero(value % 5.0)) - return Math.Truncate(value + 0.5); - return null; + IFCData countData = IFCDataUtil.CreateAsCountMeasure(value); + return CreateCommonProperty(file, propertyName, countData, valueType, null); } - internal static double? CanCacheTemperature(double value) + /// Create a count measure property. From IFC4x3 onward the value has been changed to Integer + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateCountMeasureProperty(IFCFile file, string propertyName, int value, PropertyValueType valueType) { - // Allow caching of integral temperatures and half-degrees. - if (MathUtil.IsAlmostEqual(value * 2.0, Math.Truncate(value * 2.0))) - return Math.Truncate(value * 2.0) / 2.0; - return null; + IFCData countData = IFCDataUtil.CreateAsCountMeasure(value); + return CreateCommonProperty(file, propertyName, countData, valueType, null); } - internal static double? CanCacheThermalTransmittance(double value) + /// Create a ClassificationReference property. + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The created property handle. + public static IFCAnyHandle CreateClassificationReferenceProperty(IFCFile file, string propertyName, string value) { - // Allow caching of values between 0 and 6.0, in multiples of 0.05 - double eps = MathUtil.Eps(); - if (value < -eps || value > 6.0 + eps) - return null; - if (MathUtil.IsAlmostEqual(value * 20.0, Math.Truncate(value * 20.0))) - return Math.Truncate(value * 20.0) / 20.0; - return null; + IFCAnyHandle classificationReferenceHandle = + IFCInstanceExporter.CreateClassificationReference(file, null, value, null, null, null); + return IFCInstanceExporter.CreatePropertyReferenceValue(file, propertyName, null, null, classificationReferenceHandle); } /// - /// Create a real property. + /// Create a Time measure property from the element's parameter. /// /// The IFC file. - /// The name of the property. - /// The value of the property. + /// The Element. + /// The name of the parameter. + /// The name of the property. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreateRealProperty(IFCFile file, string propertyName, double value, PropertyValueType valueType) + public static IFCAnyHandle CreateTimePropertyFromElement(IFCFile file, Element elem, + string revitParameterName, string ifcPropertyName, PropertyValueType valueType) + { + return CreateDoublePropertyFromElement(file, elem, revitParameterName, ifcPropertyName, + "IfcTimeMeasure", SpecTypeId.Time, valueType); + } + + public static IFCAnyHandle CreateUserDefinedRealPropertyFromElement(IFCFile file, Element elem, + string revitParameterName, string ifcPropertyName, PropertyValueType valueType, ForgeTypeId specType, string unitTypeKey) { - IFCData realData = IFCDataUtil.CreateAsReal(value); - return CreateCommonProperty(file, propertyName, realData, valueType, null); + IList doubleValues = GetDoubleValuesFromParameterByType(elem, revitParameterName, specType, valueType); + return CreateRealProperty(file, ifcPropertyName, doubleValues, valueType, unitTypeKey); } /// - /// Create a numeric property. + /// Create a currency measure property from the element's parameter. /// /// The IFC file. - /// The name of the property. - /// The value of the property. + /// The ExporterIFC. + /// The Element. + /// The name of the parameter. + /// The name of the property. Also, the backup name of the parameter. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreateNumericProperty(IFCFile file, string propertyName, double value, PropertyValueType valueType) + public static IFCAnyHandle CreateCurrencyPropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, + string revitParameterName, string ifcPropertyName, PropertyValueType valueType) { - IFCData NumericData = IFCDataUtil.CreateAsNumeric(value); - return CreateCommonProperty(file, propertyName, NumericData, valueType, null); + string measureName = ExporterCacheManager.UnitsCache.ContainsKey("CURRENCY") ? "IfcMonetaryMeasure" : "IfcReal"; + + IList doubleValues = GetDoubleValuesFromParameterByType(elem, revitParameterName, SpecTypeId.Number, valueType); + return CreateRealProperty(file, ifcPropertyName, doubleValues, valueType, measureName); } - /// Create a real property, using a cached value if possible. - /// The IFC file. - /// The name of the property. - /// The value of the property. - /// The value type of the property. - /// The created or cached property handle. - public static IFCAnyHandle CreateRealPropertyFromCache(IFCFile file, string propertyName, double value, PropertyValueType valueType) + public static IFCAnyHandle CreateUserDefinedRealPropertyFromElement(IFCFile file, Element elem, + string revitParameterName, BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType, ForgeTypeId specType, string unitTypeKey) { - double? adjustedValue = CanCacheDouble(value); - bool canCache = adjustedValue.HasValue; - if (canCache) - { - value = adjustedValue.GetValueOrDefault(); - } - - IFCAnyHandle propertyHandle; - if (canCache) - { - propertyHandle = ExporterCacheManager.PropertyInfoCache.RealCache.Find(propertyName, value); - if (propertyHandle != null) - return propertyHandle; - } - - propertyHandle = CreateRealProperty(file, propertyName, value, valueType); + IFCAnyHandle propHnd = CreateUserDefinedRealPropertyFromElement(file, elem, revitParameterName, ifcPropertyName, valueType, specType, unitTypeKey); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; - if (canCache && !IFCAnyHandleUtil.IsNullOrHasNoValue(propertyHandle)) + if (revitBuiltInParam != BuiltInParameter.INVALID) { - ExporterCacheManager.PropertyInfoCache.RealCache.Add(propertyName, value, propertyHandle); + string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); + propHnd = CreateUserDefinedRealPropertyFromElement(file, elem, builtInParamName, ifcPropertyName, valueType, specType, unitTypeKey); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; } - return propertyHandle; + return null; } - /// Create a numeric property, using a cached value if possible. + /// + /// Create a currency property from the element's parameter. + /// /// The IFC file. - /// The name of the property. - /// The value of the property. + /// The ExporterIFC. + /// The Element. + /// The name of the parameter. + /// The built in parameter to use, if revitParameterName isn't found. + /// The name of the property. /// The value type of the property. - /// The created or cached property handle. - public static IFCAnyHandle CreateNumericPropertyFromCache(IFCFile file, string propertyName, double value, PropertyValueType valueType) + /// The created property handle. + public static IFCAnyHandle CreateCurrencyPropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, + string revitParameterName, BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) { - double? adjustedValue = CanCacheDouble(value); - bool canCache = adjustedValue.HasValue; - if (canCache) - { - value = adjustedValue.GetValueOrDefault(); - } - - IFCAnyHandle propertyHandle; - if (canCache) - { - propertyHandle = ExporterCacheManager.PropertyInfoCache.NumericCache.Find(propertyName, value); - if (propertyHandle != null) - return propertyHandle; - } - - propertyHandle = CreateNumericProperty(file, propertyName, value, valueType); + IFCAnyHandle propHnd = CreateCurrencyPropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; - if (canCache && !IFCAnyHandleUtil.IsNullOrHasNoValue(propertyHandle)) + if (revitBuiltInParam != BuiltInParameter.INVALID) { - ExporterCacheManager.PropertyInfoCache.NumericCache.Add(propertyName, value, propertyHandle); + string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); + propHnd = CreateCurrencyPropertyFromElement(file, exporterIFC, elem, builtInParamName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; } - return propertyHandle; + return null; } - /// Create a Thermodyanamic Temperature property, using a cached value if possible. + /// + /// Create a Time property from the element's parameter. + /// /// The IFC file. - /// The name of the property. - /// The value of the property. + /// The Element. + /// The name of the parameter. + /// The built in parameter to use, if revitParameterName isn't found. + /// The name of the property. /// The value type of the property. - /// The created or cached property handle. - public static IFCAnyHandle CreateThermodynamicTemperaturePropertyFromCache(IFCFile file, string propertyName, double value, PropertyValueType valueType) + /// The created property handle. + public static IFCAnyHandle CreateTimePropertyFromElement(IFCFile file, Element elem, + string revitParameterName, BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) { - double? adjustedValue = CanCacheTemperature(value); - bool canCache = adjustedValue.HasValue; - if (canCache) - value = adjustedValue.GetValueOrDefault(); + IFCAnyHandle propHnd = CreateTimePropertyFromElement(file, elem, revitParameterName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; - IFCAnyHandle propertyHandle; - if (canCache) + if (revitBuiltInParam != BuiltInParameter.INVALID) { - propertyHandle = ExporterCacheManager.PropertyInfoCache.ThermodynamicTemperatureCache.Find(propertyName, value); - if (propertyHandle != null) - return propertyHandle; + string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); + propHnd = CreateTimePropertyFromElement(file, elem, builtInParamName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; } - propertyHandle = CreateThermodynamicTemperatureProperty(file, propertyName, value, valueType); - - if (canCache && !IFCAnyHandleUtil.IsNullOrHasNoValue(propertyHandle)) - ExporterCacheManager.PropertyInfoCache.ThermodynamicTemperatureCache.Add(propertyName, value, propertyHandle); - - return propertyHandle; + return null; } - /// Create a Power measure property, using a cached value if possible. + /// + /// Create an IfcClassificationReference property from the element's parameter. + /// /// The IFC file. - /// The name of the property. - /// The value of the property. - /// The value type of the property. - /// The created or cached property handle. - public static IFCAnyHandle CreatePowerPropertyFromCache(IFCFile file, string propertyName, double value, PropertyValueType valueType) + /// The ExporterIFC. + /// The Element. + /// The name of the parameter. + /// The name of the property. + /// The created property handle. + public static IFCAnyHandle CreateClassificationReferencePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, + string revitParameterName, string ifcPropertyName) { - double? adjustedValue = CanCachePower(value); - bool canCache = adjustedValue.HasValue; - if (canCache) - value = adjustedValue.GetValueOrDefault(); - - IFCAnyHandle propertyHandle; - if (canCache) - { - propertyHandle = ExporterCacheManager.PropertyInfoCache.PowerCache.Find(propertyName, value); - if (propertyHandle != null) - return propertyHandle; - } - - propertyHandle = CreatePowerProperty(file, propertyName, value, valueType); + if (elem == null) + return null; - if (canCache && !IFCAnyHandleUtil.IsNullOrHasNoValue(propertyHandle)) - ExporterCacheManager.PropertyInfoCache.PowerCache.Add(propertyName, value, propertyHandle); + string propertyValue; + if (ParameterUtil.GetStringValueFromElement(elem, revitParameterName, out propertyValue) != null) + return CreateClassificationReferenceProperty(file, ifcPropertyName, propertyValue); - return propertyHandle; + return null; } - /// Create a Thermal Transmittance property, using a cached value if possible. + /// + /// Create a generic measure property from the element's parameter. + /// /// The IFC file. - /// The name of the property. - /// The value of the property. - /// The value type of the property. - /// The created or cached property handle. - public static IFCAnyHandle CreateThermalTransmittancePropertyFromCache(IFCFile file, string propertyName, double value, PropertyValueType valueType) + /// The ExporterIFC. + /// The Element. + /// The name of the parameter. + /// The name of the property. + /// The IfcMeasure type of the property. + /// Identifier of the property spec. + /// The property value type of the property. + /// The created property handle. + private static IFCAnyHandle CreateDoublePropertyFromElement(IFCFile file, Element elem, + string revitParameterName, string ifcPropertyName, string measureType, ForgeTypeId specTypeId, PropertyValueType valueType) { - double? adjustedValue = CanCacheThermalTransmittance(value); - bool canCache = adjustedValue.HasValue; - if (canCache) - value = adjustedValue.GetValueOrDefault(); - - IFCAnyHandle propertyHandle; - if (canCache) - { - propertyHandle = ExporterCacheManager.PropertyInfoCache.ThermalTransmittanceCache.Find(propertyName, value); - if (propertyHandle != null) - return propertyHandle; - } + IList doubleValues = GetDoubleValuesFromParameterByType(elem, revitParameterName, specTypeId, valueType); - propertyHandle = CreateThermalTransmittanceProperty(file, propertyName, value, valueType); + IList doubleData = new List(); + foreach (var val in doubleValues) + doubleData.Add(val.HasValue ? IFCData.CreateDoubleOfType(val.Value, measureType) : null); - if (canCache && !IFCAnyHandleUtil.IsNullOrHasNoValue(propertyHandle)) - ExporterCacheManager.PropertyInfoCache.ThermalTransmittanceCache.Add(propertyName, value, propertyHandle); - - return propertyHandle; + return CreateCommonPropertyFromList(file, ifcPropertyName, doubleData, valueType, null); } /// - /// Creates a length measure property or gets one from cache. + /// Create an IfcClassificationReference property from the element's parameter. /// /// The IFC file. - /// The name of the property. - /// The unscaled value of the property, used for cache purposes. - /// The value type of the property. + /// The ExporterIFC. + /// The Element. + /// The name of the parameter. + /// The built in parameter to use, if revitParameterName isn't found. + /// The name of the property. /// The created property handle. - public static IFCAnyHandle CreateLengthMeasurePropertyFromCache(IFCFile file, string propertyName, double value, - PropertyValueType valueType) + public static IFCAnyHandle CreateClassificationReferencePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, + string revitParameterName, BuiltInParameter revitBuiltInParam, string ifcPropertyName) { - double unscaledValue = UnitUtil.UnscaleLength(value); + IFCAnyHandle propHnd = CreateClassificationReferencePropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; - double? adjustedValue = CanCacheLength(unscaledValue, value); - bool canCache = adjustedValue.HasValue; - if (canCache) + if (revitBuiltInParam != BuiltInParameter.INVALID) { - value = adjustedValue.GetValueOrDefault(); + string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); + propHnd = CreateClassificationReferencePropertyFromElement(file, exporterIFC, elem, builtInParamName, ifcPropertyName); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; } - IFCAnyHandle propertyHandle; - if (canCache) - { - propertyHandle = ExporterCacheManager.PropertyInfoCache.LengthMeasureCache.Find(propertyName, value); - if (propertyHandle != null) - return propertyHandle; - } - - switch (valueType) - { - case PropertyValueType.EnumeratedValue: - { - IList valueList = new List(); - valueList.Add(IFCDataUtil.CreateAsLengthMeasure(value)); - propertyHandle = IFCInstanceExporter.CreatePropertyEnumeratedValue(file, propertyName, null, valueList, null); - break; - } - case PropertyValueType.SingleValue: - propertyHandle = IFCInstanceExporter.CreatePropertySingleValue(file, propertyName, null, IFCDataUtil.CreateAsLengthMeasure(value), null); - break; - default: - throw new InvalidOperationException("Missing case!"); - } - - if (canCache && !IFCAnyHandleUtil.IsNullOrHasNoValue(propertyHandle)) - { - ExporterCacheManager.PropertyInfoCache.LengthMeasureCache.Add(propertyName, value, propertyHandle); - } - - return propertyHandle; + return null; } /// - /// Creates a vapor permeability measure property. + /// Create a label property from the element's parameter. /// /// The IFC file. - /// The name of the property. - /// The value of the property. + /// The Element. + /// The name of the parameter. + /// The name of the property. /// The value type of the property. + /// The type of the enum, null if valueType isn't EnumeratedValue. /// The created property handle. - public static IFCAnyHandle CreateVaporPermeabilityMeasureProperty(IFCFile file, string propertyName, double value, PropertyValueType valueType) + public static IFCAnyHandle CreateLabelPropertyFromElement(IFCFile file, Element elem, string revitParameterName, string ifcPropertyName, + PropertyValueType valueType, Type propertyEnumerationType) { - IFCData vaporPermeabilityData = IFCDataUtil.CreateAsVaporPermeabilityMeasure(value); - return CreateCommonProperty(file, propertyName, vaporPermeabilityData, valueType, null); - } + if (elem == null) + return null; - /// - /// Creates a volume measure property. - /// - /// The IFC file. - /// The name of the property. - /// The value of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateVolumeMeasureProperty(IFCFile file, string propertyName, double value, PropertyValueType valueType) - { - IFCData volumeData = IFCDataUtil.CreateAsVolumeMeasure(value); - return CreateCommonProperty(file, propertyName, volumeData, valueType, null); - } + string propertyValue; + Parameter parameter = ParameterUtil.GetStringValueFromElement(elem, revitParameterName, out propertyValue); + if (parameter != null) + return CreateLabelPropertyFromCache(file, parameter.Id, ifcPropertyName, propertyValue, valueType, false, propertyEnumerationType); - /// - /// Creates a sound power measure property. - /// - /// The IFC file. - /// The name of the property. - /// The value of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateSoundPowerMeasureProperty(IFCFile file, string propertyName, double value, PropertyValueType valueType) - { - IFCData soundPowerData = IFCDataUtil.CreateAsSoundPowerMeasure(value); - return CreateCommonProperty(file, propertyName, soundPowerData, valueType, null); + return null; } /// - /// Creates a sound pressure measure property. + /// Create a label property from the element's parameter. /// /// The IFC file. - /// The name of the property. - /// The value of the property. + /// The Element. + /// The name of the parameter. + /// The built in parameter. + /// The name of the property. /// The value type of the property. + /// The type of the enum, null if valueType isn't EnumeratedValue. /// The created property handle. - public static IFCAnyHandle CreateSoundPressureMeasureProperty(IFCFile file, string propertyName, double value, PropertyValueType valueType) + public static IFCAnyHandle CreateLabelPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType, Type propertyEnumerationType) { - IFCData soundPressureData = IFCDataUtil.CreateAsSoundPressureMeasure(value); - return CreateCommonProperty(file, propertyName, soundPressureData, valueType, null); - } + // For Instance + IFCAnyHandle propHnd = CreateLabelPropertyFromElement(file, elem, revitParameterName, ifcPropertyName, valueType, + propertyEnumerationType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; - /// - /// Creates Specific Heat Capacity measure property. - /// - /// The IFC file. - /// The name of the property. - /// The value of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateSpecificHeatCapacityMeasureProperty(IFCFile file, string propertyName, double value, PropertyValueType valueType) - { - IFCData specificHeatCapacityData = IFCDataUtil.CreateAsSpecificHeatCapacityMeasure(value); - return CreateCommonProperty(file, propertyName, specificHeatCapacityData, valueType, null); + if (revitBuiltInParam != BuiltInParameter.INVALID) + { + string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); + propHnd = CreateLabelPropertyFromElement(file, elem, builtInParamName, ifcPropertyName, valueType, propertyEnumerationType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; + } + + return null; } /// - /// Creates DynamicViscosity measure property. + /// Create an identifier property from the element's parameter. /// /// The IFC file. - /// The name of the property. - /// The value of the property. + /// The Element. + /// The name of the parameter. + /// The name of the property. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreateDynamicViscosityMeasureProperty(IFCFile file, string propertyName, double value, PropertyValueType valueType) + public static IFCAnyHandle CreateIdentifierPropertyFromElement(IFCFile file, Element elem, string revitParameterName, string ifcPropertyName, PropertyValueType valueType) { - IFCData dynamicViscosityData = IFCDataUtil.CreateAsDynamicViscosityMeasure(value); - return CreateCommonProperty(file, propertyName, dynamicViscosityData, valueType, null); + if (elem == null) + return null; + + string propertyValue; + if (ParameterUtil.GetStringValueFromElement(elem, revitParameterName, out propertyValue) != null) + return CreateIdentifierPropertyFromCache(file, ifcPropertyName, propertyValue, valueType); + + return null; } /// - /// Creates ThermalConductivity measure property. + /// Create an identifier property from the element's parameter. /// /// The IFC file. - /// The name of the property. - /// The value of the property. + /// The Element. + /// The name of the parameter. + /// The built in parameter. + /// The name of the property. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreateThermalConductivityMeasureProperty(IFCFile file, string propertyName, double value, PropertyValueType valueType) + public static IFCAnyHandle CreateIdentifierPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) { - IFCData thermalConductivityData = IFCDataUtil.CreateAsThermalConductivityMeasure(value); - return CreateCommonProperty(file, propertyName, thermalConductivityData, valueType, null); + // For Instance + IFCAnyHandle propHnd = CreateIdentifierPropertyFromElement(file, elem, revitParameterName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; + + if (revitBuiltInParam != BuiltInParameter.INVALID) + { + string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); + propHnd = CreateIdentifierPropertyFromElement(file, elem, builtInParamName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; + } + + return null; } /// - /// Creates ThermalExpansionCoefficient measure property. + /// Create a boolean property from the element's parameter. /// /// The IFC file. - /// The name of the property. - /// The value of the property. + /// The Element. + /// The name of the parameter. + /// The name of the property. Also, the backup name of the parameter. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreateThermalExpansionCoefficientMeasureProperty(IFCFile file, string propertyName, double value, PropertyValueType valueType) + public static IFCAnyHandle CreateBooleanPropertyFromElement(IFCFile file, Element elem, + string revitParameterName, string ifcPropertyName, PropertyValueType valueType) { - IFCData thermalExpansionCoefficientData = IFCDataUtil.CreateAsThermalExpansionCoefficientMeasure(value); - return CreateCommonProperty(file, propertyName, thermalExpansionCoefficientData, valueType, null); + int propertyValue; + if (ParameterUtil.GetIntValueFromElement(elem, revitParameterName, out propertyValue) != null) + return CreateBooleanPropertyFromCache(file, ifcPropertyName, propertyValue != 0, valueType); + if (ParameterUtil.GetIntValueFromElement(elem, ifcPropertyName, out propertyValue) != null) + return CreateBooleanPropertyFromCache(file, ifcPropertyName, propertyValue != 0, valueType); + + return null; } /// - /// Create a positive length measure property. + /// Create a logical property from the element's or type's parameter. /// /// The IFC file. - /// The name of the property. - /// The value of the property. + /// The Element. + /// The name of the parameter. + /// The name of the property. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreatePositiveLengthMeasureProperty(IFCFile file, string propertyName, double value, PropertyValueType valueType) + public static IFCAnyHandle CreateLogicalPropertyFromElement(IFCFile file, Element elem, + string revitParameterName, string ifcPropertyName, PropertyValueType valueType) { - if (value > MathUtil.Eps()) + IFCLogical ifcLogical = IFCLogical.Unknown; + int propertyValue; + if (ParameterUtil.GetIntValueFromElement(elem, revitParameterName, out propertyValue) != null) { - IFCData posLengthData = IFCDataUtil.CreateAsPositiveLengthMeasure(value); - return CreateCommonProperty(file, propertyName, posLengthData, valueType, null); + ifcLogical = propertyValue != 0 ? IFCLogical.True : IFCLogical.False; } - return null; - } - /// - /// Create a linear velocity measure property. - /// - /// The IFC file. - /// The name of the property. - /// The value of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateLinearVelocityMeasureProperty(IFCFile file, string propertyName, double value, PropertyValueType valueType) - { - IFCData linearVelocityData = IFCDataUtil.CreateAsLinearVelocityMeasure(value); - return CreateCommonProperty(file, propertyName, linearVelocityData, valueType, null); + return CreateLogicalPropertyFromCache(file, ifcPropertyName, ifcLogical, valueType); } - - /// - /// Create a ratio measure property. + /// Create an integer property from the element's parameter. /// /// The IFC file. - /// The name of the property. - /// The value of the property. + /// The Element. + /// The name of the parameter. + /// The name of the property. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreateRatioMeasureProperty(IFCFile file, string propertyName, double value, PropertyValueType valueType) + public static IFCAnyHandle CreateIntegerPropertyFromElement(IFCFile file, Element elem, + string revitParameterName, string ifcPropertyName, PropertyValueType valueType) { - IFCData data = IFCDataUtil.CreateRatioMeasureData(value); - return CreateCommonProperty(file, propertyName, data, valueType, null); + int propertyValue; + if (ParameterUtil.GetIntValueFromElement(elem, revitParameterName, out propertyValue) != null) + return CreateIntegerPropertyFromCache(file, ifcPropertyName, propertyValue, valueType); + + return null; } /// - /// Create a normalised ratio measure property. + /// Create a ratio measure data from string value. /// - /// The IFC file. - /// The name of the property. - /// The value of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateNormalisedRatioMeasureProperty(IFCFile file, string propertyName, double value, PropertyValueType valueType) + /// The values of the property. + /// The created property data. + public static IFCData CreateRatioMeasureDataFromString(string value) { - IFCData data = IFCDataUtil.CreateNormalisedRatioMeasureData(value); - return CreateCommonProperty(file, propertyName, data, valueType, null); + double propertyValue; + if (Double.TryParse(value, out propertyValue)) + return IFCDataUtil.CreateRatioMeasureData(propertyValue); + + return null; } + + /// - /// Create a positive ratio measure property. + /// Create a normalised ratio measure data from string value. /// - /// The IFC file. - /// The name of the property. - /// The value of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreatePositiveRatioMeasureProperty(IFCFile file, string propertyName, double value, PropertyValueType valueType) + /// The values of the property. + /// The created property data. + public static IFCData CreateNormalisedRatioMeasureDataFromString(string value) { - IFCData data = IFCDataUtil.CreatePositiveRatioMeasureData(value); - return CreateCommonProperty(file, propertyName, data, valueType, null); + double propertyValue; + if (Double.TryParse(value, out propertyValue)) + return IFCDataUtil.CreateNormalisedRatioMeasureData(propertyValue); + + return null; } + /// - /// Create a label property. + /// Create a positive ratio measure data from string value. /// - /// The IFC file. - /// The name of the property. - /// The value of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreatePlaneAngleMeasureProperty(IFCFile file, string propertyName, double value, PropertyValueType valueType) + /// The values of the property. + /// The created property data. + public static IFCData CreatePositiveRatioMeasureDataFromString(string value) { - IFCData planeAngleData = IFCDataUtil.CreateAsPlaneAngleMeasure(value); - return CreateCommonProperty(file, propertyName, planeAngleData, valueType, null); + double propertyValue; + if (Double.TryParse(value, out propertyValue)) + return IFCDataUtil.CreatePositiveRatioMeasureData(propertyValue); + + return null; } /// - /// Create a label property, or retrieve from cache. + /// Create a count measure property from the element's parameter. /// /// The IFC file. - /// The name of the property. - /// The value of the property. + /// The ExporterIFC. + /// The Element. + /// The name of the parameter. + /// The name of the property. /// The value type of the property. - /// The created or cached property handle. - public static IFCAnyHandle CreatePlaneAngleMeasurePropertyFromCache(IFCFile file, string propertyName, double value, PropertyValueType valueType) + /// The created property handle. + public static IFCAnyHandle CreateCountMeasurePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, + string revitParameterName, string ifcPropertyName, PropertyValueType valueType) { - // We have a partial cache here - we will only cache multiples of 15 degrees. - bool canCache = false; - double degreesDiv15 = Math.Floor(value / 15.0 + 0.5); - double integerDegrees = degreesDiv15 * 15.0; - if (MathUtil.IsAlmostEqual(value, integerDegrees)) - { - canCache = true; - value = integerDegrees; - } - - IFCAnyHandle propertyHandle; - if (canCache) + int propertyValue; + double propertyValueReal; + if (ParameterUtil.GetDoubleValueFromElement(elem, revitParameterName, out propertyValueReal) != null) { - propertyHandle = ExporterCacheManager.PropertyInfoCache.PlaneAngleCache.Find(propertyName, value); - if (propertyHandle != null) - return propertyHandle; + if (ExporterCacheManager.ExportOptionsCache.ExportAsOlderThanIFC4x3) + { + return CreateCountMeasureProperty(file, ifcPropertyName, propertyValueReal, valueType); + } + else if (MathUtil.IsAlmostInteger(propertyValueReal)) + { + propertyValue = (int)Math.Floor(propertyValueReal); + return CreateCountMeasureProperty(file, ifcPropertyName, propertyValue, valueType); + } } - propertyHandle = CreatePlaneAngleMeasureProperty(file, propertyName, value, valueType); - - if (canCache && !IFCAnyHandleUtil.IsNullOrHasNoValue(propertyHandle)) - { - ExporterCacheManager.PropertyInfoCache.PlaneAngleCache.Add(propertyName, value, propertyHandle); - } + if (ParameterUtil.GetIntValueFromElement(elem, revitParameterName, out propertyValue) != null) + return CreateCountMeasureProperty(file, ifcPropertyName, propertyValue, valueType); - return propertyHandle; + return null; } /// - /// Create a area measure property. + /// Create a count measure property from the element's parameter. /// /// The IFC file. - /// The name of the property. - /// The value of the property. + /// The ExporterIFC. + /// The Element. + /// The name of the parameter. + /// The built in parameter to use, if revitParameterName isn't found. + /// The name of the property. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreateAreaMeasureProperty(IFCFile file, string propertyName, double value, PropertyValueType valueType) + public static IFCAnyHandle CreateCountMeasurePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, + string revitParameterName, BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) { - IFCData areaData = IFCDataUtil.CreateAsAreaMeasure(value); - return CreateCommonProperty(file, propertyName, areaData, valueType, null); - } + IFCAnyHandle propHnd = CreateCountMeasurePropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; - /// - /// Create a Acceleration measure property. - /// - /// The IFC file. - /// The name of the property. - /// The value of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateAccelerationMeasureProperty(IFCFile file, string propertyName, double value, PropertyValueType valueType) - { - IFCData accelerationData = IFCDataUtil.CreateAsAccelerationMeasure(value); - return CreateCommonProperty(file, propertyName, accelerationData, valueType, null); - } + if (revitBuiltInParam != BuiltInParameter.INVALID) + { + string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); + propHnd = CreateCountMeasurePropertyFromElement(file, exporterIFC, elem, builtInParamName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; + } - /// - /// Create a Energy measure property. - /// - /// The IFC file. - /// The name of the property. - /// The value of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateEnergyMeasureProperty(IFCFile file, string propertyName, double value, PropertyValueType valueType) - { - IFCData energyData = IFCDataUtil.CreateAsEnergyMeasure(value); - return CreateCommonProperty(file, propertyName, energyData, valueType, null); + return null; } /// - /// Create a LinearMoment measure property. + /// Creates the shared beam and column QTO values. /// - /// The IFC file. - /// The name of the property. - /// The value of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateLinearMomentMeasureProperty(IFCFile file, string propertyName, double value, PropertyValueType valueType) + /// + /// This code uses the native implementation for creating these quantities, and the native class for storing the information. + /// This will be obsoleted. + /// + /// The exporter. + /// The element handle. + /// The beam or column element. + /// The FamilyTypeInfo containing the appropriate data. + /// The list of geometries for the exported column only, used if split walls and columns is set. + /// The geomObjects is used if we have the split by level option. It is intended only for columns, as beams and members are not split by level. + /// In this case, we use the solids in the list to determine the real volume of the exported objects. If the list contains meshes, we won't export the volume at all. + public static void CreateBeamColumnBaseQuantities(ExporterIFC exporterIFC, IFCAnyHandle elemHandle, Element element, FamilyTypeInfo typeInfo, IList geomObjects) { - IFCData linearMomentData = IFCDataUtil.CreateAsLinearMomentMeasure(value); - return CreateCommonProperty(file, propertyName, linearMomentData, valueType, null); - } + // Make sure QTO export is enabled. + if (!ExporterCacheManager.ExportOptionsCache.ExportBaseQuantities || (ExporterCacheManager.ExportOptionsCache.ExportAsCOBIE)) + return; - /// - /// Create a MassPerLength measure property. - /// - /// The IFC file. - /// The name of the property. - /// The value of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateMassPerLengthMeasureProperty(IFCFile file, string propertyName, double value, PropertyValueType valueType) - { - IFCData massPerLengthData = IFCDataUtil.CreateAsMassPerLengthMeasure(value); - return CreateCommonProperty(file, propertyName, massPerLengthData, valueType, null); - } + IFCFile file = exporterIFC.GetFile(); + HashSet quantityHnds = new HashSet(); + double scaledLength = typeInfo.extraParams.ScaledLength; + //According to investigation of current code the passed in typeInfo contains grossArea + double scaledGrossArea = typeInfo.extraParams.ScaledArea; + double crossSectionArea = scaledGrossArea; + double scaledOuterPerimeter = typeInfo.extraParams.ScaledOuterPerimeter; + double scaledInnerPerimeter = typeInfo.extraParams.ScaledInnerPerimeter; + double outSurfaceArea = 0.0; - /// - /// Create a Torque measure property. - /// - /// The IFC file. - /// The name of the property. - /// The value of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateTorqueMeasureProperty(IFCFile file, string propertyName, double value, PropertyValueType valueType) - { - IFCData torqueData = IFCDataUtil.CreateAsTorqueMeasure(value); - return CreateCommonProperty(file, propertyName, torqueData, valueType, null); - } + if (scaledLength > MathUtil.Eps()) + { + IFCAnyHandle quantityHnd = IFCInstanceExporter.CreateQuantityLength(file, "Length", null, null, scaledLength); + quantityHnds.Add(quantityHnd); + } - /// - /// Create a LinearStiffness measure property. - /// - /// The IFC file. - /// The name of the property. - /// The value of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateLinearStiffnessMeasureProperty(IFCFile file, string propertyName, double value, PropertyValueType valueType) - { - IFCData linearStiffnessData = IFCDataUtil.CreateAsLinearStiffnessMeasure(value); - return CreateCommonProperty(file, propertyName, linearStiffnessData, valueType, null); + if (MathUtil.AreaIsAlmostZero(crossSectionArea)) + { + if (element != null) + { + ParameterUtil.GetDoubleValueFromElement(element, BuiltInParameter.HOST_AREA_COMPUTED, out crossSectionArea); + crossSectionArea = UnitUtil.ScaleArea(crossSectionArea); + } + } + + if (!MathUtil.AreaIsAlmostZero(crossSectionArea)) + { + IFCAnyHandle quantityHnd = IFCInstanceExporter.CreateQuantityArea(file, "CrossSectionArea", null, null, crossSectionArea); + quantityHnds.Add(quantityHnd); + } + + if (!MathUtil.AreaIsAlmostZero(scaledGrossArea) && !MathUtil.IsAlmostZero(scaledLength) && !MathUtil.IsAlmostZero(scaledOuterPerimeter)) + { + double scaledPerimeter = scaledOuterPerimeter + scaledInnerPerimeter; + //According to the IFC documentation, OuterSurfaceArea does not include the end caps area, only Length * Perimeter + outSurfaceArea = UnitUtil.ScaleArea(UnitUtil.UnscaleLength(scaledLength) * UnitUtil.UnscaleLength(scaledPerimeter)); + IFCAnyHandle quantityHnd = IFCInstanceExporter.CreateQuantityArea(file, "OuterSurfaceArea", null, null, outSurfaceArea); + quantityHnds.Add(quantityHnd); + } + + // Compute GrossSurfaceArea if both CrossSectionAre and OuterSurfaceArea cannot be determined separately + if (MathUtil.AreaIsAlmostZero(crossSectionArea) && MathUtil.AreaIsAlmostZero(outSurfaceArea)) + { + double scaledPerimeter = scaledOuterPerimeter + scaledInnerPerimeter; + double grossSurfaceArea = scaledGrossArea * 2 + UnitUtil.ScaleArea(UnitUtil.UnscaleLength(scaledLength) * UnitUtil.UnscaleLength(scaledPerimeter)); + IFCAnyHandle quantityHnd = IFCInstanceExporter.CreateQuantityArea(file, "GrossSurfaceArea", null, null, grossSurfaceArea); + quantityHnds.Add(quantityHnd); + } + + double grossVolume = UnitUtil.ScaleVolume(UnitUtil.UnscaleLength(scaledLength) * UnitUtil.UnscaleArea(scaledGrossArea)); + double netVolume = 0.0; + if (element != null) + { + // If we are splitting columns, we will look at the actual geometry used when exporting this segment + // of the column, but only if we have the geomObjects passed in. + if (geomObjects != null && ExporterCacheManager.ExportOptionsCache.WallAndColumnSplitting) + { + foreach (GeometryObject geomObj in geomObjects) + { + // We don't suport calculating the volume of Meshes at this time. + if (geomObj is Mesh) + { + netVolume = 0.0; + break; + } + + if (geomObj is Solid) + netVolume += (geomObj as Solid).Volume; + } + } + else + ParameterUtil.GetDoubleValueFromElement(element, BuiltInParameter.HOST_VOLUME_COMPUTED, out netVolume); + netVolume = UnitUtil.ScaleVolume(netVolume); + } + + if (!MathUtil.VolumeIsAlmostZero(grossVolume)) + { + IFCAnyHandle quantityHnd = IFCInstanceExporter.CreateQuantityVolume(file, "GrossVolume", null, null, grossVolume); + quantityHnds.Add(quantityHnd); + } + + if (!MathUtil.VolumeIsAlmostZero(netVolume)) + { + IFCAnyHandle quantityHnd = IFCInstanceExporter.CreateQuantityVolume(file, "NetVolume", null, null, netVolume); + quantityHnds.Add(quantityHnd); + } + + string quantitySetName = string.Empty; + if (!ExporterCacheManager.ExportOptionsCache.ExportAsOlderThanIFC4) + { + if (IFCAnyHandleUtil.IsSubTypeOf(elemHandle, Common.Enums.IFCEntityType.IfcColumn)) + quantitySetName = "Qto_ColumnBaseQuantities"; + if (IFCAnyHandleUtil.IsSubTypeOf(elemHandle, Common.Enums.IFCEntityType.IfcBeam)) + quantitySetName = "Qto_BeamBaseQuantities"; + if (IFCAnyHandleUtil.IsSubTypeOf(elemHandle, Common.Enums.IFCEntityType.IfcMember)) + quantitySetName = "Qto_MemberBaseQuantities"; + } + CreateAndRelateBaseQuantities(file, exporterIFC, elemHandle, quantityHnds, quantitySetName); } /// - /// Create a AngularVelocity measure property. + /// Creates the spatial element quantities required by GSA before COBIE and adds them to the export. /// - /// The IFC file. - /// The name of the property. - /// The value of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateAngularVelocityMeasureProperty(IFCFile file, string propertyName, double value, PropertyValueType valueType) + /// The exporter. + /// The element handle. + /// The quantity name. + /// The area name. + /// The area. + public static void CreatePreCOBIEGSAQuantities(ExporterIFC exporterIFC, IFCAnyHandle elemHnd, string quantityName, string areaName, double area) { - IFCData angularVelocityData = IFCDataUtil.CreateAsAngularVelocityMeasure(value); - return CreateCommonProperty(file, propertyName, angularVelocityData, valueType, null); + IFCFile file = exporterIFC.GetFile(); + IFCAnyHandle ownerHistory = ExporterCacheManager.OwnerHistoryHandle; + IFCAnyHandle areaQuantityHnd = IFCInstanceExporter.CreateQuantityArea(file, quantityName, null, null, area); + HashSet areaQuantityHnds = new HashSet(); + areaQuantityHnds.Add(areaQuantityHnd); + + PropertyUtil.CreateAndRelateBaseQuantities(file, exporterIFC, elemHnd, areaQuantityHnds, quantityName, null, areaName); } /// - /// Create a ThermalResistance measure property. + /// Creates the opening quantities and adds them to the export. /// - /// The IFC file. - /// The name of the property. - /// The value of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateThermalResistanceMeasureProperty(IFCFile file, string propertyName, double value, PropertyValueType valueType) + /// The exporter. + /// The opening element handle. + /// The extrusion creation data. + public static void CreateOpeningQuantities(ExporterIFC exporterIFC, IFCAnyHandle openingElement, IFCExportBodyParams extraParams) { - IFCData thermalResistanceData = IFCDataUtil.CreateAsThermalResistanceMeasure(value); - return CreateCommonProperty(file, propertyName, thermalResistanceData, valueType, null); + IFCFile file = exporterIFC.GetFile(); + HashSet quantityHnds = new HashSet(); + if (extraParams.ScaledLength > MathUtil.Eps()) + { + IFCAnyHandle quantityHnd = IFCInstanceExporter.CreateQuantityLength(file, "Depth", null, null, extraParams.ScaledLength); + quantityHnds.Add(quantityHnd); + } + if (extraParams.ScaledHeight > MathUtil.Eps()) + { + IFCAnyHandle quantityHnd = IFCInstanceExporter.CreateQuantityLength(file, "Height", null, null, extraParams.ScaledHeight); + quantityHnds.Add(quantityHnd); + quantityHnd = IFCInstanceExporter.CreateQuantityLength(file, "Width", null, null, extraParams.ScaledWidth); + quantityHnds.Add(quantityHnd); + } + else if (extraParams.ScaledArea > MathUtil.Eps()) + { + IFCAnyHandle quantityHnd = IFCInstanceExporter.CreateQuantityArea(file, "Area", null, null, extraParams.ScaledArea); + quantityHnds.Add(quantityHnd); + } + + string quantitySetName = string.Empty; + if (!ExporterCacheManager.ExportOptionsCache.ExportAsOlderThanIFC4) + { + quantitySetName = "Qto_OpeningElementBaseQuantities"; + } + CreateAndRelateBaseQuantities(file, exporterIFC, openingElement, quantityHnds, quantitySetName); } /// - /// Create a WarpingConstant measure property. + /// Creates and caches area and volume base quantities for slabs. /// - /// The IFC file. - /// The name of the property. - /// The value of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateWarpingConstantMeasureProperty(IFCFile file, string propertyName, double value, PropertyValueType valueType) + /// The exporter. + /// The slab handle. + /// The IFCExportBodyParams containing the slab extrusion creation data. + /// The slab outer loop. + public static void CreateSlabBaseQuantities(ExporterIFC exporterIFC, IFCAnyHandle slabHnd, IFCExportBodyParams extrusionData, CurveLoop outerCurveLoop) { - IFCData warpingConstantData = IFCDataUtil.CreateAsWarpingConstantMeasure(value); - return CreateCommonProperty(file, propertyName, warpingConstantData, valueType, null); - } + if (extrusionData != null) + { + IFCFile file = exporterIFC.GetFile(); + HashSet quantityHnds = new HashSet(); - /// Create a count measure property. - /// The IFC file. - /// The name of the property. - /// The value of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateCountMeasureProperty(IFCFile file, string propertyName, double value, PropertyValueType valueType) - { - IFCData countData = IFCDataUtil.CreateAsCountMeasure(value); - return CreateCommonProperty(file, propertyName, countData, valueType, null); - } + double netArea = extrusionData.ScaledArea; + if (!MathUtil.IsAlmostZero(netArea)) + { + IFCAnyHandle quantityHnd = IFCInstanceExporter.CreateQuantityArea(file, "NetArea", null, null, netArea); + quantityHnds.Add(quantityHnd); + } - /// Create a count measure property. From IFC4x3 onward the value has been changed to Integer - /// The IFC file. - /// The name of the property. - /// The value of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateCountMeasureProperty(IFCFile file, string propertyName, int value, PropertyValueType valueType) - { - IFCData countData = IFCDataUtil.CreateAsCountMeasure(value); - return CreateCommonProperty(file, propertyName, countData, valueType, null); - } + //The length, area and volume may have different base length units, it safer to unscale and rescale the results. + double unscaledArea = UnitUtil.UnscaleArea(netArea); + double unscaledLength = UnitUtil.UnscaleLength(extrusionData.ScaledLength); + double netVolume = UnitUtil.ScaleVolume(unscaledArea * unscaledLength); + if (!MathUtil.IsAlmostZero(netVolume)) + { + IFCAnyHandle quantityHnd = IFCInstanceExporter.CreateQuantityArea(file, "NetVolume", null, null, netVolume); + quantityHnds.Add(quantityHnd); + } - /// Create a ThermodynamicTemperature property. - /// The IFC file. - /// The name of the property. - /// The value of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateThermodynamicTemperatureProperty(IFCFile file, string propertyName, double value, PropertyValueType valueType) - { - IFCData thermodynamicTemperatureMeasureData = IFCDataUtil.CreateAsThermodynamicTemperatureMeasure(value); - return CreateCommonProperty(file, propertyName, thermodynamicTemperatureMeasureData, valueType, null); - } + if (outerCurveLoop != null) + { + double unscaledSlabGrossArea = ExporterIFCUtils.ComputeAreaOfCurveLoops(new List() { outerCurveLoop }); + double scaledSlabGrossArea = UnitUtil.ScaleArea(unscaledSlabGrossArea); + if (!MathUtil.IsAlmostZero(scaledSlabGrossArea)) + { + IFCAnyHandle quantityHnd = IFCInstanceExporter.CreateQuantityArea(file, "GrossArea", null, null, scaledSlabGrossArea); + quantityHnds.Add(quantityHnd); + } - /// Create a ClassificationReference property. - /// The IFC file. - /// The name of the property. - /// The value of the property. - /// The created property handle. - public static IFCAnyHandle CreateClassificationReferenceProperty(IFCFile file, string propertyName, string value) - { - IFCAnyHandle classificationReferenceHandle = - IFCInstanceExporter.CreateClassificationReference(file, null, value, null, null, null); - return IFCInstanceExporter.CreatePropertyReferenceValue(file, propertyName, null, null, classificationReferenceHandle); - } + double grossVolume = UnitUtil.ScaleVolume(unscaledArea * unscaledLength); + if (!MathUtil.IsAlmostZero(grossVolume)) + { + IFCAnyHandle quantityHnd = IFCInstanceExporter.CreateQuantityArea(file, "GrossVolume", null, null, grossVolume); + quantityHnds.Add(quantityHnd); + } + } - /// Create an IlluminanceMeasure property. - /// The IFC file. - /// The name of the property. - /// The value of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateIlluminanceProperty(IFCFile file, string propertyName, double value, PropertyValueType valueType) - { - IFCData illuminanceData = IFCDataUtil.CreateAsIlluminanceMeasure(value); - return CreateCommonProperty(file, propertyName, illuminanceData, valueType, null); + ExporterCacheManager.BaseQuantitiesCache.Add(slabHnd, quantityHnds); + } } - - /// Create a LuminousFluxMeasure property. - /// The IFC file. - /// The name of the property. - /// The value of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateLuminousFluxMeasureProperty(IFCFile file, string propertyName, double value, PropertyValueType valueType) + /// + /// Creates the wall base quantities and adds them to the export. + /// + /// The exporter. + /// The wall element. + /// The wall handle. + /// The list of solids for the entity created for the wall element. + /// The list of meshes for the entity created for the wall element. + /// The scaled length. + /// The scaled depth. + /// The scaled foot print area. + /// If we are splitting walls by level, the list of solids and meshes represent the currently + /// exported section of wall, not the entire wall. + public static void CreateWallBaseQuantities(ExporterIFC exporterIFC, Wall wallElement, + IList solids, IList meshes, + IFCAnyHandle wallHnd, + double scaledLength, double scaledDepth, double scaledFootPrintArea, + IFCExportBodyParams extrusionData, HashSet widthAsComplexQty = null) { - IFCData luminousFluxData = IFCDataUtil.CreateAsLuminousFluxMeasure(value); - return CreateCommonProperty(file, propertyName, luminousFluxData, valueType, null); - } + IFCFile file = exporterIFC.GetFile(); + HashSet quantityHnds = new HashSet(); + if (scaledDepth > MathUtil.Eps()) + { + IFCAnyHandle quantityHnd = IFCInstanceExporter.CreateQuantityLength(file, "Height", null, null, scaledDepth); + quantityHnds.Add(quantityHnd); + } - /// Create a LuminousIntensityMeasure property. - /// The IFC file. - /// The name of the property. - /// The value of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateLuminousIntensityProperty(IFCFile file, string propertyName, double value, PropertyValueType valueType) - { - IFCData luminousIntensityData = IFCDataUtil.CreateAsLuminousIntensityMeasure(value); - return CreateCommonProperty(file, propertyName, luminousIntensityData, valueType, null); - } + if (!MathUtil.IsAlmostZero(scaledLength)) + { + IFCAnyHandle quantityHnd = IFCInstanceExporter.CreateQuantityLength(file, "Length", null, null, scaledLength); + quantityHnds.Add(quantityHnd); + } + else if (wallElement.Location != null) + { + Curve wallAxis = (wallElement.Location as LocationCurve).Curve; + if (wallAxis != null) + { + double axisLength = UnitUtil.ScaleLength(wallAxis.Length); + IFCAnyHandle quantityHnd = IFCInstanceExporter.CreateQuantityLength(file, "Length", null, null, axisLength); + quantityHnds.Add(quantityHnd); + } + } - /// Create a ForceMeasure property. - /// The IFC file. - /// The name of the property. - /// The value of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateForceProperty(IFCFile file, string propertyName, double value, PropertyValueType valueType) - { - IFCData forceData = IFCDataUtil.CreateAsForceMeasure(value); - return CreateCommonProperty(file, propertyName, forceData, valueType, null); - } - /// Create a LinearForceMeasure property. - /// The IFC file. - /// The name of the property. - /// The value of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateLinearForceProperty(IFCFile file, string propertyName, double value, PropertyValueType valueType) - { - IFCData linearForceData = IFCDataUtil.CreateAsLinearForceMeasure(value); - return CreateCommonProperty(file, propertyName, linearForceData, valueType, null); - } + double scaledWidth = 0.0; + if (wallElement != null) + { + scaledWidth = UnitUtil.ScaleLength(wallElement.Width); + if (!MathUtil.IsAlmostZero(scaledWidth)) + { + if ((widthAsComplexQty?.Count ?? 0) == 0) + { + IFCAnyHandle quantityHnd = IFCInstanceExporter.CreateQuantityLength(file, "Width", null, null, scaledWidth); + quantityHnds.Add(quantityHnd); + } + else + { + quantityHnds.UnionWith(widthAsComplexQty); + } + } + } - public static IFCAnyHandle CreateLinearForcePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) - { + if (!MathUtil.IsAlmostZero(scaledFootPrintArea)) + { + IFCAnyHandle quantityHnd = IFCInstanceExporter.CreateQuantityArea(file, "GrossFootprintArea", null, null, scaledFootPrintArea); + quantityHnds.Add(quantityHnd); + } - IFCAnyHandle propHnd = CreateDoublePropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, - "IfcLinearForceMeasure", SpecTypeId.LinearForce, valueType); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) - return propHnd; + double netArea = 0; + double grossArea = 0; + double volume = 0; - if (revitBuiltInParam != BuiltInParameter.INVALID) + // We will only assign the area if we have all solids that we are exporting; we won't bother calcuting values for Meshes. + if (solids != null && (meshes == null || meshes.Count == 0)) { - string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); - propHnd = CreateDoublePropertyFromElement(file, exporterIFC, elem, builtInParamName, ifcPropertyName, - "IfcLinearForceMeasure", SpecTypeId.LinearForce, valueType); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) - return propHnd; + //To determine the side of the wall that is suitable for calculating BaseQuantities, + //we group the faces by normal and calculate the total area of each side. + Dictionary, double)> wallSides = new Dictionary, double)>(); + foreach (Solid solid in solids) + { + foreach (Face face in solid.Faces) + { + XYZ faceNormal = face.ComputeNormal(new UV(0, 0)); + if (MathUtil.IsAlmostZero(faceNormal.Z)) + { + double faceArea = face.Area; + if (wallSides.Any()) + { + bool faceAdded = false; + foreach (var wallSide in wallSides) + { + if (faceNormal.IsAlmostEqualTo(wallSide.Key)) + { + List sideFaces = wallSide.Value.Item1; + sideFaces.Add(face); + double sumArea = wallSide.Value.Item2 + faceArea; + wallSides[wallSide.Key] = ( sideFaces, sumArea); + faceAdded = true; + break; + } + } + if(!faceAdded) + { + wallSides.Add(faceNormal, (new List { face }, face.Area)); + } + } + else + { + wallSides.Add(faceNormal, (new List { face }, face.Area)); + } + } + } + volume += solid.Volume; + } + + KeyValuePair, double)> largestSide = new KeyValuePair, double)>(); + foreach (var wallSide in wallSides) + { + if (wallSide.Value.Item2 > largestSide.Value.Item2) + largestSide = wallSide; + } + + List facesOfLargestWallSide = largestSide.Value.Item1; + netArea = largestSide.Value.Item2; + + foreach (Face face in facesOfLargestWallSide) + { + double largestFaceGrossArea = 0.0; + IList fCurveLoops = face.GetEdgesAsCurveLoops(); + for (int ii = 0; ii < fCurveLoops.Count; ii++) + { + double grArea = ExporterIFCUtils.ComputeAreaOfCurveLoops(new List() { fCurveLoops[ii] }); + if (grArea > largestFaceGrossArea) + largestFaceGrossArea = grArea; + } + grossArea += largestFaceGrossArea; + } } - return null; - } + netArea = UnitUtil.ScaleArea(netArea); + grossArea = UnitUtil.ScaleArea(grossArea); + volume = UnitUtil.ScaleVolume(volume); - public static IFCAnyHandle CreatePlanarForcePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) - { + if (scaledDepth > MathUtil.Eps() && !MathUtil.IsAlmostZero(scaledWidth) && !MathUtil.IsAlmostZero(grossArea)) + { + double grossVolume = UnitUtil.ScaleVolume(UnitUtil.UnscaleLength(scaledWidth) * UnitUtil.UnscaleArea(grossArea)); + IFCAnyHandle quantityHnd = IFCInstanceExporter.CreateQuantityVolume(file, "GrossVolume", null, null, grossVolume); + quantityHnds.Add(quantityHnd); + } - IFCAnyHandle propHnd = CreateDoublePropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, - "IfcPlanarForceMeasure", SpecTypeId.AreaForce, valueType); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) - return propHnd; + if (!MathUtil.IsAlmostZero(grossArea)) + { + IFCAnyHandle quantityHnd = IFCInstanceExporter.CreateQuantityArea(file, "GrossSideArea", null, null, grossArea); + quantityHnds.Add(quantityHnd); + } - if (revitBuiltInParam != BuiltInParameter.INVALID) + if (!MathUtil.IsAlmostZero(netArea)) { - string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); - propHnd = CreateDoublePropertyFromElement(file, exporterIFC, elem, builtInParamName, ifcPropertyName, - "IfcPlanarForceMeasure", SpecTypeId.AreaForce, valueType); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) - return propHnd; + IFCAnyHandle quantityHnd = IFCInstanceExporter.CreateQuantityArea(file, "NetSideArea", null, null, netArea); + quantityHnds.Add(quantityHnd); } - return null; - } + if (!MathUtil.IsAlmostZero(volume)) + { + IFCAnyHandle quantityHnd = IFCInstanceExporter.CreateQuantityVolume(file, "NetVolume", null, null, volume); + quantityHnds.Add(quantityHnd); + } - /// Create a PlanarForceMeasure property. - /// The IFC file. - /// The name of the property. - /// The value of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreatePlanarForceProperty(IFCFile file, string propertyName, double value, PropertyValueType valueType) - { - IFCData planarForceData = IFCDataUtil.CreateAsPlanarForceMeasure(value); - return CreateCommonProperty(file, propertyName, planarForceData, valueType, null); + string quantitySetName = string.Empty; + if (ExporterCacheManager.ExportOptionsCache.ExportAs4) + { + quantitySetName = "Qto_WallBaseQuantities"; + } + + CreateAndRelateBaseQuantities(file, exporterIFC, wallHnd, quantityHnds, quantitySetName); } - /// Create a PowerMeasure property. - /// The IFC file. - /// The name of the property. - /// The value of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreatePowerProperty(IFCFile file, string propertyName, double value, PropertyValueType valueType) + /// + /// Creates and relate base quantities to quantity handle. + /// + /// The file. + /// The exporter. + /// The element handle. + /// The quantity handles. + static public void CreateAndRelateBaseQuantities(IFCFile file, ExporterIFC exporterIFC, IFCAnyHandle elemHnd, HashSet quantityHnds, + string quantitySetName = null, string description = null, string methodOfMeasurement = null) { - IFCData powerData = IFCDataUtil.CreateAsPowerMeasure(value); - return CreateCommonProperty(file, propertyName, powerData, valueType, null); + if (quantityHnds.Count > 0) + { + if (string.IsNullOrEmpty(quantitySetName)) + quantitySetName = "BaseQuantities"; + IFCAnyHandle ownerHistory = ExporterCacheManager.OwnerHistoryHandle; + + // Skip if the elementHandle has the associated QuantitySet has been created before + if (!ExporterCacheManager.QtoSetCreated.Contains((elemHnd, quantitySetName))) + { + string quantityGuid = GUIDUtil.GenerateIFCGuidFrom( + GUIDUtil.CreateGUIDString(IFCEntityType.IfcElementQuantity, quantitySetName, elemHnd)); + IFCAnyHandle quantity = IFCInstanceExporter.CreateElementQuantity(file, elemHnd, + quantityGuid, ownerHistory, quantitySetName, description, + methodOfMeasurement, quantityHnds); + HashSet relatedObjects = new HashSet(); + relatedObjects.Add(elemHnd); + + string quantityRelGuid = GUIDUtil.GenerateIFCGuidFrom( + GUIDUtil.CreateGUIDString(IFCEntityType.IfcRelDefinesByProperties, quantitySetName, elemHnd)); + ExporterUtil.CreateRelDefinesByProperties(file, quantityRelGuid, ownerHistory, null, null, + relatedObjects, quantity); + } + } } - /// Create a ThermalTransmittance property. - /// The IFC file. - /// The name of the property. - /// The value of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateThermalTransmittanceProperty(IFCFile file, string propertyName, double value, PropertyValueType valueType) + /// + /// Creates the shared beam, column and member QTO values. + /// + /// The exporter. + /// The element handle. + /// The element. + /// The IFCExportBodyParams containing the appropriate data. + public static void CreateBeamColumnMemberBaseQuantities(ExporterIFC exporterIFC, IFCAnyHandle elemHandle, Element element, IFCExportBodyParams ecData) { - IFCData thermalTransmittanceData = IFCDataUtil.CreateAsThermalTransmittanceMeasure(value); - return CreateCommonProperty(file, propertyName, thermalTransmittanceData, valueType, null); + FamilyTypeInfo ifcTypeInfo = new FamilyTypeInfo() { extraParams = ecData }; + CreateBeamColumnBaseQuantities(exporterIFC, elemHandle, element, ifcTypeInfo, null); } - /// Create a VolumetricFlowRate property. - /// The IFC file. - /// The name of the property. - /// The value of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateVolumetricFlowRateMeasureProperty(IFCFile file, string propertyName, double value, PropertyValueType valueType) + /// + /// True if QTO width and length values should be reversed. + /// + /// The element handle. + public static bool IsWidthLengthReversed(IFCAnyHandle elemHandle) { - IFCData volumetricFlowRateData = IFCDataUtil.CreateAsVolumetricFlowRateMeasure(value); - return CreateCommonProperty(file, propertyName, volumetricFlowRateData, valueType, null); + return (IFCAnyHandleUtil.IsSubTypeOf(elemHandle, IFCEntityType.IfcSlab) || IFCAnyHandleUtil.IsSubTypeOf(elemHandle, IFCEntityType.IfcCovering)); } /// - /// Create a VolumetricFlowRate measure property from the element's parameter. + /// Creates property sets for Revit groups and parameters, if export options is set. /// - /// The IFC file. /// The ExporterIFC. - /// The Element. - /// The name of the parameter. - /// The name of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateVolumetricFlowRatePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, string ifcPropertyName, PropertyValueType valueType) + /// The Element. + /// The collection of IFCAnyHandles to relate properties to. + /// Forces properties creation even if 'Export internal properties' is unchecked. + public static void CreateInternalRevitPropertySets(ExporterIFC exporterIFC, Element element, + ISet elementSets, bool forceCreate) { - return CreateDoublePropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, - "IfcVolumetricFlowRateMeasure", SpecTypeId.AirFlow, valueType); - } + if (exporterIFC == null || element == null || + (!ExporterCacheManager.ExportOptionsCache.PropertySetOptions.ExportInternalRevit && !forceCreate)) + return; - /// - /// Create a Time measure property from the element's parameter. - /// - /// The IFC file. - /// The ExporterIFC. - /// The Element. - /// The name of the parameter. - /// The name of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateTimePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, string ifcPropertyName, PropertyValueType valueType) - { - return CreateDoublePropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, - "IfcTimeMeasure", SpecTypeId.Time, valueType); - } + // We will allow creating internal Revit property sets for element types with no associated element handles. + if (((elementSets?.Count ?? 0) == 0) && !(element is ElementType)) + return; - /// - /// Create a Sound power measure property from the element's parameter. - /// - /// The IFC file. - /// The ExporterIFC. - /// The Element. - /// The name of the parameter. - /// The name of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateSoundPowerPropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, string ifcPropertyName, PropertyValueType valueType) - { - double propertyValue; - Parameter param = ParameterUtil.GetDoubleValueFromElement(elem, revitParameterName, out propertyValue); - if (param != null) - { - if (!ParameterUtil.ParameterDataTypeIsEqualTo(param, SpecTypeId.Number)) - propertyValue = UnitUtil.ScaleSoundPower(propertyValue); + IFCFile file = exporterIFC.GetFile(); - if (valueType == PropertyValueType.BoundedValue) - { - IList boundedData = GetBoundedDataFromElement(elem, revitParameterName, propertyValue, SpecTypeId.Wattage, "IfcSoundPowerMeasure"); - return CreateBoundedValuePropertyFromList(file, ifcPropertyName, boundedData, null); - } - else - { - return CreateSoundPowerMeasureProperty(file, ifcPropertyName, propertyValue, valueType); - } + ElementId typeId = element.GetTypeId(); + Element elementType = element.Document.GetElement(typeId); + int whichStart = elementType != null ? 0 : (element is ElementType ? 1 : 0); + if (whichStart == 1) + { + typeId = element.Id; + elementType = element as ElementType; } - return null; - } - /// - /// Create a Sound pressure measure property from the element's parameter. - /// - /// The IFC file. - /// The ExporterIFC. - /// The Element. - /// The name of the parameter. - /// The name of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateSoundPressurePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, string ifcPropertyName, PropertyValueType valueType) - { - double propertyValue; - Parameter param = ParameterUtil.GetDoubleValueFromElement(elem, revitParameterName, out propertyValue); - if (param != null) + SortedDictionary)>[] propertySets; + propertySets = new SortedDictionary)>[2]; + propertySets[0] = new SortedDictionary)>(); + propertySets[1] = new SortedDictionary)>(); + + // pass through: element and element type. If the element is a ElementType, there will only be one pass. + for (int which = whichStart; which < 2; which++) { - if (!ParameterUtil.ParameterDataTypeIsEqualTo(param, SpecTypeId.Number)) - propertyValue = UnitUtil.ScaleSoundPressure(propertyValue); + Element whichElement = (which == 0) ? element : elementType; + if (whichElement == null) + continue; + + // If we have already processed this element, just add the new + // IFC entities. + if (ExporterCacheManager.CreatedInternalPropertySets.TryAppend(whichElement.Id, elementSets)) + continue; - if (valueType == PropertyValueType.BoundedValue) + ElementId whichElementId = whichElement.Id; + + bool createType = (which == 1); + if (createType) { - IList boundedData = GetBoundedDataFromElement(elem, revitParameterName, propertyValue, SpecTypeId.HvacPressure, "IfcSoundPressureMeasure"); - return CreateBoundedValuePropertyFromList(file, ifcPropertyName, boundedData, null); + if (ExporterCacheManager.TypePropertyInfoCache.HasTypeProperties(typeId)) + continue; } - else + + IDictionary parameterElementCache = + ParameterUtil.GetNonIFCParametersForElement(whichElementId); + if (parameterElementCache == null) + continue; + + foreach (KeyValuePair parameterElementGroup in parameterElementCache) { - return CreateSoundPressureMeasureProperty(file, ifcPropertyName, propertyValue, valueType); + ForgeTypeId parameterGroup = new ForgeTypeId(parameterElementGroup.Key); + string groupName = LabelUtils.GetLabelForGroup(parameterGroup); + + // We are only going to append the "(Type)" suffix if we aren't also exporting the corresponding entity type. + // In general, we'd like to always export them entity type, regardles of whether it holds any geometry or not - it can hold + // at least the parameteric information. When this is acheived, when can get rid of this entirely. + // Unfortunately, IFC2x3 doesn't have types for all entities, so for IFC2x3 at least this will continue to exist + // in some fashion. + // There was a suggestion in SourceForge that we could "merge" the instance/type property sets in the cases where we aren't + // creating an entity type, and in the cases where two properties had the same name, use the instance over type. + // However, given our intention to generally export all types, this seems like a lot of work for diminishing returns. + if (whichElement is ElementType) + if (which == 1 && !ExporterCacheManager.ElementTypeToHandleCache.IsRegistered(whichElement as ElementType)) + groupName += Properties.Resources.PropertySetTypeSuffix; + + HashSet currPropertiesForGroup = new HashSet(); + propertySets[which][parameterElementGroup.Key] = (groupName, currPropertiesForGroup); + + foreach (Parameter parameter in parameterElementGroup.Value.ParameterCache.Values) + { + if (!parameter.HasValue) + continue; + + Definition parameterDefinition = parameter.Definition; + if (parameterDefinition == null) + continue; + + string parameterCaption = parameterDefinition.Name; + + switch (parameter.StorageType) + { + case StorageType.None: + break; + case StorageType.Integer: + { + int value = parameter.AsInteger(); + string valueAsString = parameter.AsValueString(); + + // YesNo or actual integer? + if (parameterDefinition.GetDataType() == SpecTypeId.Boolean.YesNo) + { + currPropertiesForGroup.Add(CreateBooleanPropertyFromCache(file, parameterCaption, value != 0, PropertyValueType.SingleValue)); + } + else if (parameterDefinition.GetDataType().Empty() && (valueAsString != null)) + { + // This is probably an internal enumerated type that should be exported as a string. + currPropertiesForGroup.Add(CreateIdentifierPropertyFromCache(file, parameterCaption, valueAsString, PropertyValueType.SingleValue)); + } + else + { + currPropertiesForGroup.Add(CreateIntegerPropertyFromCache(file, parameterCaption, value, PropertyValueType.SingleValue)); + } + break; + } + case StorageType.Double: + { + double value = parameter.AsDouble(); + IFCAnyHandle propertyHandle = CreateRealPropertyBasedOnParameterType(file, parameter, parameterCaption, value, PropertyValueType.SingleValue); + + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propertyHandle)) + currPropertiesForGroup.Add(propertyHandle); + break; + } + case StorageType.String: + { + string value = parameter.AsString(); + if (!string.IsNullOrEmpty(value)) + currPropertiesForGroup.Add(CreateTextPropertyFromCache(file, parameterCaption, value, PropertyValueType.SingleValue)); + break; + } + case StorageType.ElementId: + { + if (parameter.AsElementId() != ElementId.InvalidElementId) + { + string valueString = parameter.AsValueString(); + currPropertiesForGroup.Add(CreateLabelPropertyFromCache(file, parameter.Id, parameterCaption, valueString, PropertyValueType.SingleValue, true, null)); + } + break; + } + } + } } } - return null; - } - - /// - /// Create a SpecificHeat Capacity measure property from the element's parameter. - /// - /// The IFC file. - /// The ExporterIFC. - /// The Element. - /// The name of the parameter. - /// The name of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateSpecificHeatCapacityPropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, string ifcPropertyName, PropertyValueType valueType) - { - double propertyValue; - Parameter param = ParameterUtil.GetDoubleValueFromElement(elem, revitParameterName, out propertyValue); - if (param != null) + for (int which = whichStart; which < 2; which++) { - if (!ParameterUtil.ParameterDataTypeIsEqualTo(param, SpecTypeId.Number)) - propertyValue = UnitUtil.ScaleSpecificHeatCapacity(propertyValue); + Element whichElement = (which == 0) ? element : elementType; + if (whichElement == null) + continue; + + HashSet createdPropertySets = new HashSet(); - if (valueType == PropertyValueType.BoundedValue) + int size = propertySets[which].Count; + if (size == 0) { - IList boundedData = GetBoundedDataFromElement(elem, revitParameterName, propertyValue, SpecTypeId.SpecificHeat, "IfcSpecificHeatCapacityMeasure"); - return CreateBoundedValuePropertyFromList(file, ifcPropertyName, boundedData, null); + ExporterCacheManager.TypePropertyInfoCache.AddNewElementHandles(typeId, elementSets); + continue; } - else + + bool materialProperties = element is Material; + foreach (KeyValuePair)> currPropertySet in propertySets[which]) { - return CreateSpecificHeatCapacityMeasureProperty(file, ifcPropertyName, propertyValue, valueType); - } - } - return null; - } + if (currPropertySet.Value.Item2.Count == 0) + continue; - /// - /// Create a Dynamic Viscosity measure property from the element's parameter. - /// - /// The IFC file. - /// The ExporterIFC. - /// The Element. - /// The name of the parameter. - /// The name of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateDynamicViscosityPropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, string ifcPropertyName, PropertyValueType valueType) - { - double propertyValue; - Parameter param = ParameterUtil.GetDoubleValueFromElement(elem, revitParameterName, out propertyValue); - if (param != null) - { - if (!ParameterUtil.ParameterDataTypeIsEqualTo(param, SpecTypeId.Number)) - propertyValue = UnitUtil.ScaleDynamicViscosity(propertyValue); + if (materialProperties) + { + MaterialPropertiesUtil.ExportGenericMaterialPropertySet(file, elementSets?.ToList().First(), currPropertySet.Value.Item2, null, currPropertySet.Value.Item1); + } + else + { + string psetGUID = GUIDUtil.GenerateIFCGuidFrom( + GUIDUtil.CreateGUIDString(whichElement, "IfcPropertySet: " + currPropertySet.Key.ToString())); - if (valueType == PropertyValueType.BoundedValue) - { - IList boundedData = GetBoundedDataFromElement(elem, revitParameterName, propertyValue, SpecTypeId.HvacViscosity, "IfcDynamicViscosityMeasure"); - return CreateBoundedValuePropertyFromList(file, ifcPropertyName, boundedData, null); + IFCAnyHandle propertySet = IFCInstanceExporter.CreatePropertySet(file, psetGUID, + ExporterCacheManager.OwnerHistoryHandle, currPropertySet.Value.Item1, null, + currPropertySet.Value.Item2); + createdPropertySets.Add(propertySet); + } } - else + + // Don't need to create relations for material properties + if (!materialProperties) { - return CreateDynamicViscosityMeasureProperty(file, ifcPropertyName, propertyValue, valueType); + if (which == 0) + ExporterCacheManager.CreatedInternalPropertySets.Add(whichElement.Id, createdPropertySets, elementSets); + else + ExporterCacheManager.TypePropertyInfoCache.AddNewTypeProperties(typeId, createdPropertySets, elementSets); } } - return null; } /// - /// Create an friction loss custom measure property from the element's parameter. + /// Get a unit type of parameter. + /// IFCUnit for each one. /// - /// The IFC file. - /// The ExporterIFC. - /// The Element. - /// The name of the parameter. - /// The name of the property. Also, the backup name of the parameter. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateElectricalResistivityPropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, string ifcPropertyName, PropertyValueType valueType) - { - double propertyValue; - Parameter param = ParameterUtil.GetDoubleValueFromElement(elem, revitParameterName, out propertyValue); - if (param != null) - { - if (!ParameterUtil.ParameterDataTypeIsEqualTo(param, SpecTypeId.Number)) - propertyValue = UnitUtil.ScaleDouble(SpecTypeId.ElectricalResistivity, propertyValue); - - if (valueType == PropertyValueType.BoundedValue) - { - IList boundedData = GetBoundedDataFromElement(elem, revitParameterName, propertyValue, SpecTypeId.ElectricalResistivity, "IfcReal"); - return CreateBoundedValuePropertyFromList(file, ifcPropertyName, boundedData, "ELECTRICALRESISTIVITY"); - } - else - { - return CreateElectricalResistivityPropertyFromValue(file, ifcPropertyName, propertyValue); - } - } - return null; - } - - public static IFCAnyHandle CreateElectricalResistivityPropertyFromValue(IFCFile file, string ifcPropertyName, double propertyValue) + /// The parameter. + /// The parameter unit type. + public static ForgeTypeId GetParameterUnitType(Parameter parameter) { - IFCData electricalEfficacyData = IFCDataUtil.CreateAsMeasure(propertyValue, "IfcReal"); - return CreateCommonProperty(file, ifcPropertyName, electricalEfficacyData, - PropertyValueType.SingleValue, "ELECTRICALRESISTIVITY"); - } + ForgeTypeId parameterUnitType = null; - /// - /// Create an friction loss custom measure property from the element's parameter. - /// - /// The IFC file. - /// The ExporterIFC. - /// The Element. - /// The name of the parameter. - /// The name of the property. Also, the backup name of the parameter. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateFrictionLossPropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, string ifcPropertyName, PropertyValueType valueType) - { - double propertyValue; - Parameter param = ParameterUtil.GetDoubleValueFromElement(elem, revitParameterName, out propertyValue); - if (param != null) + try { - if (!ParameterUtil.ParameterDataTypeIsEqualTo(param, SpecTypeId.Number)) - propertyValue = UnitUtil.ScaleDouble(SpecTypeId.HvacFriction, propertyValue); - - if (valueType == PropertyValueType.BoundedValue) - { - IList boundedData = GetBoundedDataFromElement(elem, revitParameterName, propertyValue, SpecTypeId.HvacFriction, "IfcReal"); - return CreateBoundedValuePropertyFromList(file, ifcPropertyName, boundedData, "FRICTIONLOSS"); - } - else - { - return CreateFrictionLossPropertyFromValue(file, ifcPropertyName, propertyValue); - } + parameterUnitType = parameter?.GetUnitTypeId(); } - return null; - } - - public static IFCAnyHandle CreateFrictionLossPropertyFromValue(IFCFile file, string ifcPropertyName, double propertyValue) - { - IFCData electricalEfficacyData = IFCDataUtil.CreateAsMeasure(propertyValue, "IfcReal"); - return CreateCommonProperty(file, ifcPropertyName, electricalEfficacyData, - PropertyValueType.SingleValue, "FRICTIONLOSS"); - } - - /// - /// Create a Color Temperature measure property from the element's parameter. This will be an IfcReal with a custom unit. - /// - /// The IFC file. - /// The ExporterIFC. - /// The Element. - /// The name of the parameter. - /// The name of the property. Also, the backup name of the parameter. - /// The created property handle. - public static IFCAnyHandle CreateColorTemperaturePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, string ifcPropertyName) - { - double propertyValue; - Parameter param = ParameterUtil.GetDoubleValueFromElement(elem, revitParameterName, out propertyValue); - if (param != null) + catch { - if (!ParameterUtil.ParameterDataTypeIsEqualTo(param, SpecTypeId.Number)) - propertyValue = UnitUtil.ScaleDouble(SpecTypeId.ColorTemperature, propertyValue); - return CreateColorTemperaturePropertyFromValue(file, ifcPropertyName, propertyValue); + // GetUnitTypeId() can fail for reasons that don't seem to be knowable in + // advance, so we won't scale value in these cases. } - return null; - } - public static IFCAnyHandle CreateColorTemperaturePropertyFromValue(IFCFile file, string ifcPropertyName, double propertyValue) - { - IFCData colorTemperatureData = IFCDataUtil.CreateAsMeasure(propertyValue, "IfcReal"); - return CreateCommonProperty(file, ifcPropertyName, colorTemperatureData, - PropertyValueType.SingleValue, "COLORTEMPERATURE"); + return parameterUnitType; } /// - /// Create an electrical efficacy custom measure property from the element's parameter. + /// Creates property from real parameter. + /// There are many different ParameterTypes in Revit that share the same unit dimensions, but that + /// have potentially different display units (e.g. Bar Diameter could be in millimeters while the project + /// default length parameter is in meters.) For now, we will only support one unit type. At a later + /// point, we could decide to have different caches for each parameter type, and export a different + /// IFCUnit for each one. /// /// The IFC file. - /// The ExporterIFC. - /// The Element. - /// The name of the parameter. - /// The name of the property. Also, the backup name of the parameter. + /// The parameter. + /// The name of the property. + /// The value of the property. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreateElectricalEfficacyPropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, string ifcPropertyName, PropertyValueType valueType) + public static IFCAnyHandle CreateRealPropertyBasedOnParameterType(IFCFile file, Parameter parameter, string propertyName, double propertyValue, PropertyValueType valueType) { - double propertyValue; - Parameter param = ParameterUtil.GetDoubleValueFromElement(elem, revitParameterName, out propertyValue); - if (param != null) - { - if (!ParameterUtil.ParameterDataTypeIsEqualTo(param, SpecTypeId.Number)) - propertyValue = UnitUtil.ScaleDouble(SpecTypeId.Efficacy, propertyValue); + if (parameter == null) + return null; - if (valueType == PropertyValueType.BoundedValue) - { - IList boundedData = GetBoundedDataFromElement(elem, revitParameterName, propertyValue, SpecTypeId.Efficacy, "IfcReal"); - return CreateBoundedValuePropertyFromList(file, ifcPropertyName, boundedData, "LUMINOUSEFFICACY"); - } - else - { - return CreateElectricalEfficacyPropertyFromValue(file, ifcPropertyName, propertyValue); - } - } - return null; - } + ForgeTypeId type = parameter.Definition?.GetDataType(); + ForgeTypeId fallbackUnitType = GetParameterUnitType(parameter); - public static IFCAnyHandle CreateElectricalEfficacyPropertyFromValue(IFCFile file, string ifcPropertyName, double propertyValue) - { - IFCData electricalEfficacyData = IFCDataUtil.CreateAsMeasure(propertyValue, "IfcReal"); - return CreateCommonProperty(file, ifcPropertyName, electricalEfficacyData, - PropertyValueType.SingleValue, "LUMINOUSEFFICACY"); + return CreateRealPropertyByType(file, type, propertyName, propertyValue, valueType, fallbackUnitType); } /// - /// Create a currency measure property from the element's parameter. + /// Creates property from real parameter. /// /// The IFC file. - /// The ExporterIFC. - /// The Element. - /// The name of the parameter. - /// The name of the property. Also, the backup name of the parameter. + /// The type of the parameter. + /// The name of the property. + /// The value of the property. /// The value type of the property. + /// The optional unit type. Can be used for scaling in final case /// The created property handle. - public static IFCAnyHandle CreateCurrencyPropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, string ifcPropertyName, PropertyValueType valueType) + public static IFCAnyHandle CreateRealPropertyByType(IFCFile file, ForgeTypeId parameterType, string propertyName, double propertyValue, PropertyValueType valueType, ForgeTypeId fallbackUnitType = null) { - double propertyValue; - if (ParameterUtil.GetDoubleValueFromElement(elem, revitParameterName, out propertyValue) != null) + IFCAnyHandle propertyHandle = null; + + if (parameterType == SpecTypeId.Acceleration) { - string measureName = ExporterCacheManager.UnitsCache.ContainsKey("CURRENCY") ? "IfcMonetaryMeasure" : "IfcReal"; - if (valueType == PropertyValueType.BoundedValue) - { - IList boundedData = GetBoundedDataFromElement(elem, revitParameterName, propertyValue, SpecTypeId.Number, measureName); - return CreateBoundedValuePropertyFromList(file, ifcPropertyName, boundedData, null); - } - else - { - IFCData currencyData = IFCDataUtil.CreateAsMeasure(propertyValue, measureName); - return CreateCommonProperty(file, ifcPropertyName, currencyData, PropertyValueType.SingleValue, null); - } + double scaledValue = UnitUtil.ScaleAcceleration(propertyValue); + propertyHandle = CreateAccelerationPropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, null); } - return null; - } - - /// - /// Create a ThermodynamicTemperature measure property from the element's parameter. - /// - /// The IFC file. - /// The ExporterIFC. - /// The Element. - /// The name of the parameter. - /// The name of the property. Also, the backup name of the parameter. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateThermodynamicTemperaturePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, string ifcPropertyName, PropertyValueType valueType) - { - double propertyValue; - Parameter param = ParameterUtil.GetDoubleValueFromElement(elem, revitParameterName, out propertyValue); - if (param != null) + else if (parameterType == SpecTypeId.Energy || + parameterType == SpecTypeId.HvacEnergy) { - if (!ParameterUtil.ParameterDataTypeIsEqualTo(param, SpecTypeId.Number)) - propertyValue = UnitUtil.ScaleThermodynamicTemperature(propertyValue); - - if (valueType == PropertyValueType.BoundedValue) - { - IList boundedData = GetBoundedDataFromElement(elem, revitParameterName, propertyValue, SpecTypeId.HvacTemperature, "IfcThermodynamicTemperatureMeasure"); - return CreateBoundedValuePropertyFromList(file, ifcPropertyName, boundedData, null); - } - else - { - return CreateThermodynamicTemperaturePropertyFromCache(file, ifcPropertyName, propertyValue, valueType); - } + double scaledValue = UnitUtil.ScaleEnergy(propertyValue); + propertyHandle = CreateEnergyPropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, null); } - - param = ParameterUtil.GetDoubleValueFromElement(elem, ifcPropertyName, out propertyValue); - if (param != null) + else if (parameterType == SpecTypeId.LinearMoment) { - if (!ParameterUtil.ParameterDataTypeIsEqualTo(param, SpecTypeId.Number)) - propertyValue = UnitUtil.ScaleThermodynamicTemperature(propertyValue); - - if (valueType == PropertyValueType.BoundedValue) - { - IList boundedData = GetBoundedDataFromElement(elem, ifcPropertyName, propertyValue, SpecTypeId.HvacTemperature, "IfcThermodynamicTemperatureMeasure"); - return CreateBoundedValuePropertyFromList(file, ifcPropertyName, boundedData, null); - } - else - { - return CreateThermodynamicTemperaturePropertyFromCache(file, ifcPropertyName, propertyValue, valueType); - } + double scaledValue = UnitUtil.ScaleLinearMoment(propertyValue); + propertyHandle = CreateLinearMomentPropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, null); } - return null; - } - - /// - /// Create a friction loss property from the element's parameter. This will be an IfcReal with a special temperature unit. - /// - /// The IFC file. - /// The ExporterIFC. - /// The Element. - /// The name of the parameter. - /// The built in parameter to use, if revitParameterName isn't found. - /// The name of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateFrictionLossPropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) - { - IFCAnyHandle propHnd = CreateFrictionLossPropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, valueType); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) - return propHnd; - - if (revitBuiltInParam != BuiltInParameter.INVALID) + else if (parameterType == SpecTypeId.MassPerUnitLength || + parameterType == SpecTypeId.PipeMassPerUnitLength) { - string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); - propHnd = CreateFrictionLossPropertyFromElement(file, exporterIFC, elem, builtInParamName, ifcPropertyName, valueType); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) - return propHnd; + double scaledValue = UnitUtil.ScaleMassPerLength(propertyValue); + propertyHandle = CreateMassPerLengthPropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, null); } - - return null; - - } - /// - /// Create a electrical resistivity property from the element's parameter. This will be an IfcReal with a special temperature unit. - /// - /// The IFC file. - /// The ExporterIFC. - /// The Element. - /// The name of the parameter. - /// The built in parameter to use, if revitParameterName isn't found. - /// The name of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateElectricalResistivityPropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) - { - IFCAnyHandle propHnd = CreateElectricalResistivityPropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, valueType); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) - return propHnd; - - if (revitBuiltInParam != BuiltInParameter.INVALID) + else if (parameterType == SpecTypeId.Moment) { - string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); - propHnd = CreateElectricalResistivityPropertyFromElement(file, exporterIFC, elem, builtInParamName, ifcPropertyName, valueType); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) - return propHnd; + double scaledValue = UnitUtil.ScaleTorque(propertyValue); + propertyHandle = CreateTorquePropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, null); } - - return null; - } - - /// - /// Create a color temperature property from the element's parameter. This will be an IfcReal with a special temperature unit. - /// - /// The IFC file. - /// The ExporterIFC. - /// The Element. - /// The name of the parameter. - /// The built in parameter to use, if revitParameterName isn't found. - /// The name of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateColorTemperaturePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) - { - IFCAnyHandle propHnd = CreateColorTemperaturePropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) - return propHnd; - - if (revitBuiltInParam != BuiltInParameter.INVALID) + else if (parameterType == SpecTypeId.PointSpringCoefficient) { - string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); - propHnd = CreateColorTemperaturePropertyFromElement(file, exporterIFC, elem, builtInParamName, ifcPropertyName); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) - return propHnd; + double scaledValue = UnitUtil.ScaleLinearStiffness(propertyValue); + propertyHandle = CreateLinearStiffnessPropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, null); } - - return null; - } - - /// - /// Create an electrical efficacy custom property from the element's parameter. - /// - /// The IFC file. - /// The ExporterIFC. - /// The Element. - /// The name of the parameter. - /// The built in parameter to use, if revitParameterName isn't found. - /// The name of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateElectricalEfficacyPropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) - { - IFCAnyHandle propHnd = CreateElectricalEfficacyPropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, valueType); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) - return propHnd; - - if (revitBuiltInParam != BuiltInParameter.INVALID) + else if (parameterType == SpecTypeId.Pulsation) { - string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); - propHnd = CreateElectricalEfficacyPropertyFromElement(file, exporterIFC, elem, builtInParamName, ifcPropertyName, valueType); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) - return propHnd; + double scaledValue = UnitUtil.ScaleAngularVelocity(propertyValue); + propertyHandle = CreateAngularVelocityPropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, null); } - - return null; - } - - /// - /// Create a currency property from the element's parameter. - /// - /// The IFC file. - /// The ExporterIFC. - /// The Element. - /// The name of the parameter. - /// The built in parameter to use, if revitParameterName isn't found. - /// The name of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateCurrencyPropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) - { - IFCAnyHandle propHnd = CreateCurrencyPropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, valueType); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) - return propHnd; - - if (revitBuiltInParam != BuiltInParameter.INVALID) + else if (parameterType == SpecTypeId.ThermalResistance) { - string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); - propHnd = CreateCurrencyPropertyFromElement(file, exporterIFC, elem, builtInParamName, ifcPropertyName, valueType); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) - return propHnd; + double scaledValue = UnitUtil.ScaleThermalResistance(propertyValue); + propertyHandle = CreateThermalResistancePropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, null); } - - return null; - } - - /// - /// Create a ThermodynamicTemperature measure property from the element's parameter. - /// - /// The IFC file. - /// The ExporterIFC. - /// The Element. - /// The name of the parameter. - /// The built in parameter to use, if revitParameterName isn't found. - /// The name of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateThermodynamicTemperaturePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) - { - IFCAnyHandle propHnd = CreateThermodynamicTemperaturePropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, valueType); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) - return propHnd; - - if (revitBuiltInParam != BuiltInParameter.INVALID) + else if (parameterType == SpecTypeId.WarpingConstant) { - string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); - propHnd = CreateThermodynamicTemperaturePropertyFromElement(file, exporterIFC, elem, builtInParamName, ifcPropertyName, valueType); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) - return propHnd; + double scaledValue = UnitUtil.ScaleWarpingConstant(propertyValue); + propertyHandle = CreateWarpingConstantPropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, null); } - - return null; - } - - /// - /// Create a VolumetricFlowRate measure property from the element's parameter. - /// - /// The IFC file. - /// The ExporterIFC. - /// The Element. - /// The name of the parameter. - /// The built in parameter to use, if revitParameterName isn't found. - /// The name of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateVolumetricFlowRatePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) - { - IFCAnyHandle propHnd = CreateVolumetricFlowRatePropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, valueType); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) - return propHnd; - - if (revitBuiltInParam != BuiltInParameter.INVALID) + else if (parameterType == SpecTypeId.Angle || + parameterType == SpecTypeId.Rotation || + parameterType == SpecTypeId.RotationAngle) { - string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); - propHnd = CreateVolumetricFlowRatePropertyFromElement(file, exporterIFC, elem, builtInParamName, ifcPropertyName, valueType); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) - return propHnd; + double scaledValue = UnitUtil.ScaleAngle(propertyValue); + propertyHandle = CreatePlaneAnglePropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, null); } - - return null; - } - - /// - /// Create a Time property from the element's parameter. - /// - /// The IFC file. - /// The ExporterIFC. - /// The Element. - /// The name of the parameter. - /// The built in parameter to use, if revitParameterName isn't found. - /// The name of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateTimePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) - { - IFCAnyHandle propHnd = CreateTimePropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, valueType); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) - return propHnd; - - if (revitBuiltInParam != BuiltInParameter.INVALID) + else if (parameterType == SpecTypeId.Slope || + parameterType == SpecTypeId.HvacSlope || + parameterType == SpecTypeId.PipingSlope || + parameterType == SpecTypeId.DemandFactor || + parameterType == SpecTypeId.Factor) { - string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); - propHnd = CreateTimePropertyFromElement(file, exporterIFC, elem, builtInParamName, ifcPropertyName, valueType); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) - return propHnd; + propertyHandle = CreatePositiveRatioPropertyFromCache(file, propertyName, + new List() { propertyValue }, valueType, null); } - - return null; - } - - /// - /// Create a Sound power property from the element's parameter. - /// - /// The IFC file. - /// The ExporterIFC. - /// The Element. - /// The name of the parameter. - /// The built in parameter to use, if revitParameterName isn't found. - /// The name of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateSoundPowerPropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) - { - IFCAnyHandle propHnd = CreateSoundPowerPropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, valueType); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) - return propHnd; - - if (revitBuiltInParam != BuiltInParameter.INVALID) + else if (parameterType == SpecTypeId.Area || + parameterType == SpecTypeId.CrossSection || + parameterType == SpecTypeId.ReinforcementArea || + parameterType == SpecTypeId.SectionArea) { - string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); - propHnd = CreateSoundPowerPropertyFromElement(file, exporterIFC, elem, builtInParamName, ifcPropertyName, valueType); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) - return propHnd; + double scaledValue = UnitUtil.ScaleArea(propertyValue); + propertyHandle = CreateAreaPropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, null); } - - return null; - } - - /// - /// Create a Sound pressure property from the element's parameter. - /// - /// The IFC file. - /// The ExporterIFC. - /// The Element. - /// The name of the parameter. - /// The built in parameter to use, if revitParameterName isn't found. - /// The name of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateSoundPressurePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) - { - IFCAnyHandle propHnd = CreateSoundPressurePropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, valueType); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) - return propHnd; - - if (revitBuiltInParam != BuiltInParameter.INVALID) + else if (parameterType == SpecTypeId.BarDiameter || + parameterType == SpecTypeId.CrackWidth || + parameterType == SpecTypeId.Displacement || + parameterType == SpecTypeId.Distance || + parameterType == SpecTypeId.CableTraySize || + parameterType == SpecTypeId.ConduitSize || + parameterType == SpecTypeId.Length || + parameterType == SpecTypeId.DuctInsulationThickness || + parameterType == SpecTypeId.DuctLiningThickness || + parameterType == SpecTypeId.DuctSize || + parameterType == SpecTypeId.HvacRoughness || + parameterType == SpecTypeId.PipeDimension || + parameterType == SpecTypeId.PipeInsulationThickness || + parameterType == SpecTypeId.PipeSize || + parameterType == SpecTypeId.PipingRoughness || + parameterType == SpecTypeId.ReinforcementCover || + parameterType == SpecTypeId.ReinforcementLength || + parameterType == SpecTypeId.ReinforcementSpacing || + parameterType == SpecTypeId.SectionDimension || + parameterType == SpecTypeId.SectionProperty || + parameterType == SpecTypeId.WireDiameter || + parameterType == SpecTypeId.SurfaceAreaPerUnitLength) { - string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); - propHnd = CreateSoundPressurePropertyFromElement(file, exporterIFC, elem, builtInParamName, ifcPropertyName, valueType); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) - return propHnd; + double scaledValue = UnitUtil.ScaleLength(propertyValue); + propertyHandle = CreateLengthPropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, null); } - - return null; - } - - /// - /// Create a Specific Heat Capacity property from the element's parameter. - /// - /// The IFC file. - /// The ExporterIFC. - /// The Element. - /// The name of the parameter. - /// The built in parameter to use, if revitParameterName isn't found. - /// The name of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateSpecificHeatCapacityPropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) - { - IFCAnyHandle propHnd = CreateSpecificHeatCapacityPropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, valueType); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) - return propHnd; - - if (revitBuiltInParam != BuiltInParameter.INVALID) + else if (parameterType == SpecTypeId.Currency) { - string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); - propHnd = CreateSpecificHeatCapacityPropertyFromElement(file, exporterIFC, elem, builtInParamName, ifcPropertyName, valueType); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) - return propHnd; + IFCData currencyData = ExporterCacheManager.UnitsCache.ContainsKey("CURRENCY") ? + IFCDataUtil.CreateAsMonetaryMeasure(propertyValue) : + IFCDataUtil.CreateAsReal(propertyValue); + propertyHandle = CreateCommonProperty(file, propertyName, currencyData, + valueType, null); } - - return null; - } - - /// - /// Create a Dynamic Viscosity property from the element's parameter. - /// - /// The IFC file. - /// The ExporterIFC. - /// The Element. - /// The name of the parameter. - /// The built in parameter to use, if revitParameterName isn't found. - /// The name of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateDynamicViscosityPropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) - { - IFCAnyHandle propHnd = CreateDynamicViscosityPropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, valueType); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) - return propHnd; - - if (revitBuiltInParam != BuiltInParameter.INVALID) + else if (parameterType == SpecTypeId.ApparentPower || + parameterType == SpecTypeId.ElectricalPower || + parameterType == SpecTypeId.Wattage || + parameterType == SpecTypeId.CoolingLoad || + parameterType == SpecTypeId.HeatGain || + parameterType == SpecTypeId.HeatingLoad || + parameterType == SpecTypeId.HvacPower) { - string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); - propHnd = CreateDynamicViscosityPropertyFromElement(file, exporterIFC, elem, builtInParamName, ifcPropertyName, valueType); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) - return propHnd; + double scaledValue = UnitUtil.ScalePower(propertyValue); + propertyHandle = CreatePowerPropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, null); } - - return null; - } - - /// - /// Create an IfcClassificationReference property from the element's parameter. - /// - /// The IFC file. - /// The ExporterIFC. - /// The Element. - /// The name of the parameter. - /// The name of the property. - /// The created property handle. - public static IFCAnyHandle CreateClassificationReferencePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, string ifcPropertyName) - { - if (elem == null) - return null; - - string propertyValue; - if (ParameterUtil.GetStringValueFromElement(elem, revitParameterName, out propertyValue) != null) - return CreateClassificationReferenceProperty(file, ifcPropertyName, propertyValue); - - return null; - } - - /// - /// Create a generic measure property from the element's parameter. - /// - /// The IFC file. - /// The ExporterIFC. - /// The Element. - /// The name of the parameter. - /// The name of the property. - /// The IfcMeasure type of the property. - /// Identifier of the property spec. - /// The property value type of the property. - /// The created property handle. - private static IFCAnyHandle CreateDoublePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, string ifcPropertyName, string measureType, ForgeTypeId specTypeId, PropertyValueType valueType) - { - double propertyValue; - Parameter param = ParameterUtil.GetDoubleValueFromElement(elem, revitParameterName, out propertyValue); - if (param != null) + else if (parameterType == SpecTypeId.Current) { - if (!ParameterUtil.ParameterDataTypeIsEqualTo(param, SpecTypeId.Number)) - propertyValue = UnitUtil.ScaleDouble(specTypeId, propertyValue); - - if (valueType == PropertyValueType.BoundedValue) - { - IList boundedData = GetBoundedDataFromElement(elem, revitParameterName, propertyValue, specTypeId, measureType); - return CreateBoundedValuePropertyFromList(file, ifcPropertyName, boundedData, null); - } - else - { - IFCData doubleData = IFCDataUtil.CreateAsMeasure(propertyValue, measureType); - return CreateCommonProperty(file, ifcPropertyName, doubleData, valueType, null); - } + double scaledValue = UnitUtil.ScaleElectricCurrent(propertyValue); + propertyHandle = CreateElectricCurrentPropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, null); } - return null; - } - - /// - /// Create a list of bounded data. - /// - /// The Element. - /// The name of the parameter. - /// The SetPoint value. - /// Identifier of the property spec. - /// The IfcMeasure type of the property. - /// List of bounded data. Null if unset. - public static IList GetBoundedDataFromElement(Element elem, string revitParameterName, double propertyValue, ForgeTypeId specTypeId, string measureType) - { - IList boundedData = new List(); - - IList boundedValues = GetBoundedValuesFromElement(elem, revitParameterName, specTypeId); - boundedValues.Insert(0, propertyValue); - foreach (double? val in boundedValues) + else if (parameterType == SpecTypeId.Diffusivity) { - if (!val.HasValue) - boundedData.Add(null); - else - boundedData.Add(IFCDataUtil.CreateAsMeasure(val.Value, measureType)); + double scaledValue = UnitUtil.ScaleMoistureDiffusivity(propertyValue); + propertyHandle = CreateMoistureDiffusivityPropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, null); } - return boundedData; - } - - /// - /// Reads bounded values from element - /// - /// The Element. - /// The name of the parameter. - /// Identifier of the property spec. - /// List of bounded values. Null if unset. - public static IList GetBoundedValuesFromElement(Element elem, string revitParameterName, ForgeTypeId specTypeId) - { - IList boundedValues = new List(); - - double upperBound; - Parameter param = ParameterUtil.GetDoubleValueFromElement(elem, revitParameterName + ".UpperBoundValue", out upperBound); - if (param != null) + else if (parameterType == SpecTypeId.ElectricalFrequency || + parameterType == SpecTypeId.StructuralFrequency) { - if (!ParameterUtil.ParameterDataTypeIsEqualTo(param, SpecTypeId.Number)) - boundedValues.Add(UnitUtil.ScaleDouble(specTypeId, upperBound)); + propertyHandle = CreateFrequencyPropertyFromCache(file, propertyName, + new List() { propertyValue }, valueType, null); } - else + else if (parameterType == SpecTypeId.Illuminance) { - boundedValues.Add(null); + double scaledValue = UnitUtil.ScaleIlluminance(propertyValue); + propertyHandle = CreateIlluminancePropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, null); } - - double lowerBound; - param = ParameterUtil.GetDoubleValueFromElement(elem, revitParameterName + ".LowerBoundValue", out lowerBound); - if (param != null) + else if (parameterType == SpecTypeId.LuminousFlux) { - if (!ParameterUtil.ParameterDataTypeIsEqualTo(param, SpecTypeId.Number)) - boundedValues.Add(UnitUtil.ScaleDouble(specTypeId, lowerBound)); + double scaledValue = UnitUtil.ScaleLuminousFlux(propertyValue); + propertyHandle = CreateLuminousFluxPropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, null); } - else + else if (parameterType == SpecTypeId.LuminousIntensity) { - boundedValues.Add(null); + double scaledValue = UnitUtil.ScaleLuminousIntensity(propertyValue); + propertyHandle = CreateLuminousIntensityPropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, null); } - - return boundedValues; - } - - /// - /// Create a Force measure property from the element's parameter. - /// - /// The IFC file. - /// The ExporterIFC. - /// The Element. - /// The name of the parameter. - /// The name of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateForcePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, string ifcPropertyName, PropertyValueType valueType) - { - return CreateDoublePropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, - "IfcForceMeasure", SpecTypeId.Force, valueType); - } - - /// - /// Create a Power measure property from the element's parameter. - /// - /// The IFC file. - /// The ExporterIFC. - /// The Element. - /// The name of the parameter. - /// The name of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreatePowerPropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, string ifcPropertyName, PropertyValueType valueType) - { - double propertyValue; - Parameter powerParam = ParameterUtil.GetDoubleValueFromElement(elem, revitParameterName, out propertyValue); - if (powerParam != null) + else if (parameterType == SpecTypeId.ElectricalPotential) { - // We are going to do a little hack here which we will need to extend in a nice way. The built-in parameter corresponding - // to "TotalWattage" is a string value in Revit that is likely going to be in the current units, and doesn't need to be scaled twice. - bool needToScale = !(ifcPropertyName == "TotalWattage" && powerParam.StorageType == StorageType.String) - && !ParameterUtil.ParameterDataTypeIsEqualTo(powerParam, SpecTypeId.Number); - - double scaledpropertyValue = needToScale ? UnitUtil.ScalePower(propertyValue) : propertyValue; - - if (valueType == PropertyValueType.BoundedValue) - { - IList boundedData = GetBoundedDataFromElement(elem, revitParameterName, propertyValue, - needToScale ? SpecTypeId.HvacPower : SpecTypeId.Number, "IfcPowerMeasure"); - return CreateBoundedValuePropertyFromList(file, ifcPropertyName, boundedData, null); - } - else - { - return CreatePowerPropertyFromCache(file, ifcPropertyName, scaledpropertyValue, valueType); - } + double scaledValue = UnitUtil.ScaleElectricVoltage(propertyValue); + propertyHandle = CreateElectricVoltagePropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, null); } - return null; - } - - /// - /// Create a Luminous flux measure property from the element's parameter. - /// - /// The IFC file. - /// The ExporterIFC. - /// The Element. - /// The name of the parameter. - /// The name of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateLuminousFluxMeasurePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, string ifcPropertyName, PropertyValueType valueType) - { - return CreateDoublePropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, - "IfcLuminousFluxMeasure", SpecTypeId.LuminousFlux, valueType); - } - - /// - /// Create a Luminous intensity measure property from the element's parameter. - /// - /// The IFC file. - /// The ExporterIFC. - /// The Element. - /// The name of the parameter. - /// The name of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateLuminousIntensityMeasurePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, string ifcPropertyName, PropertyValueType valueType) - { - return CreateDoublePropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, - "IfcLuminousIntensityMeasure", SpecTypeId.LuminousIntensity, valueType); - } - - /// - /// Create a illuminance measure property from the element's parameter. - /// - /// The IFC file. - /// The ExporterIFC. - /// The Element. - /// The name of the parameter. - /// The name of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateIlluminanceMeasurePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, string ifcPropertyName, PropertyValueType valueType) - { - return CreateDoublePropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, - "IfcIlluminanceMeasure", SpecTypeId.Illuminance, valueType); - } - - /// - /// Create a heat flux density measure property from the element's parameter. - /// - /// The IFC file. - /// The ExporterIFC. - /// The Element. - /// The name of the parameter. - /// The name of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateHeatFluxDensityMeasurePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, string ifcPropertyName, PropertyValueType valueType) - { - return CreateDoublePropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, - "IfcHeatFluxDensityMeasure", SpecTypeId.HvacPowerDensity, valueType); - } - - /// - /// Create a Mass measure property from the element's parameter. - /// - /// The IFC file. - /// The ExporterIFC. - /// The Element. - /// The name of the parameter. - /// The name of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateMassMeasurePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, string ifcPropertyName, PropertyValueType valueType) - { - return CreateDoublePropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, - "IfcMassMeasure", SpecTypeId.Mass, valueType); - } - - /// - /// Create a pressure measure property from the element's parameter. - /// - /// The IFC file. - /// The ExporterIFC. - /// The Element. - /// The name of the parameter. - /// The name of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreatePressurePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, string ifcPropertyName, PropertyValueType valueType) - { - return CreateDoublePropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, - "IfcPressureMeasure", SpecTypeId.HvacPressure, valueType); - } - - /// - /// Create a ThermalConductivity measure property from the element's parameter. - /// - /// The IFC file. - /// The ExporterIFC. - /// The Element. - /// The name of the parameter. - /// The name of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateThermalConductivityPropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, string ifcPropertyName, PropertyValueType valueType) - { - double propertyValue; - Parameter param = ParameterUtil.GetDoubleValueFromElement(elem, revitParameterName, out propertyValue); - if (param != null) + else if (parameterType == SpecTypeId.HvacTemperature || + parameterType == SpecTypeId.ElectricalTemperature || + parameterType == SpecTypeId.PipingTemperature) { - if (!ParameterUtil.ParameterDataTypeIsEqualTo(param, SpecTypeId.Number)) - propertyValue = UnitUtil.ScaleThermalConductivity(propertyValue); - - if (valueType == PropertyValueType.BoundedValue) - { - IList boundedData = GetBoundedDataFromElement(elem, revitParameterName, propertyValue, SpecTypeId.ThermalConductivity, "IfcThermalConductivityMeasure"); - return CreateBoundedValuePropertyFromList(file, ifcPropertyName, boundedData, null); - } - else - { - return CreateThermalConductivityMeasureProperty(file, ifcPropertyName, propertyValue, valueType); - } + double scaledValue = UnitUtil.ScaleThermodynamicTemperature(propertyValue); + propertyHandle = CreateThermodynamicTemperaturePropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, null); } - return null; - } - - /// - /// Create a ThermalExpansionCoefficient measure property from the element's parameter. - /// - /// The IFC file. - /// The ExporterIFC. - /// The Element. - /// The name of the parameter. - /// The name of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateThermalExpansionCoefficientPropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, string ifcPropertyName, PropertyValueType valueType) - { - double propertyValue; - Parameter param = ParameterUtil.GetDoubleValueFromElement(elem, revitParameterName, out propertyValue); - if (param != null) + else if (parameterType == SpecTypeId.HeatTransferCoefficient) { - if (!ParameterUtil.ParameterDataTypeIsEqualTo(param, SpecTypeId.Number)) - propertyValue = UnitUtil.ScaleThermalExpansionCoefficient(propertyValue); - - if (valueType == PropertyValueType.BoundedValue) - { - IList boundedData = GetBoundedDataFromElement(elem, revitParameterName, propertyValue, SpecTypeId.ThermalExpansionCoefficient, "IfcThermalExpansionCoefficientMeasure"); - return CreateBoundedValuePropertyFromList(file, ifcPropertyName, boundedData, null); - } - else - { - return CreateThermalExpansionCoefficientMeasureProperty(file, ifcPropertyName, propertyValue, valueType); - } + double scaledValue = UnitUtil.ScaleThermalTransmittance(propertyValue); + propertyHandle = CreateThermalTransmittancePropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, null); } - return null; - } - - /// - /// Create a ThermalTransmittance measure property from the element's parameter. - /// - /// The IFC file. - /// The ExporterIFC. - /// The Element. - /// The name of the parameter. - /// The name of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateThermalTransmittancePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, string ifcPropertyName, PropertyValueType valueType) - { - double propertyValue; - Parameter param = ParameterUtil.GetDoubleValueFromElement(elem, revitParameterName, out propertyValue); - if (param != null) + else if (parameterType == SpecTypeId.Force || + parameterType == SpecTypeId.Weight) { - if (!ParameterUtil.ParameterDataTypeIsEqualTo(param, SpecTypeId.Number)) - propertyValue = UnitUtil.ScaleThermalTransmittance(propertyValue); - - if (valueType == PropertyValueType.BoundedValue) - { - IList boundedData = GetBoundedDataFromElement(elem, revitParameterName, propertyValue, SpecTypeId.HeatTransferCoefficient, "IfcThermalTransmittanceMeasure"); - return CreateBoundedValuePropertyFromList(file, ifcPropertyName, boundedData, null); - } - else - { - return CreateThermalTransmittancePropertyFromCache(file, ifcPropertyName, propertyValue, valueType); - } + double scaledValue = UnitUtil.ScaleForce(propertyValue); + propertyHandle = CreateForcePropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, null); } - return null; - } - - /// - /// Create an IfcClassificationReference property from the element's parameter. - /// - /// The IFC file. - /// The ExporterIFC. - /// The Element. - /// The name of the parameter. - /// The built in parameter to use, if revitParameterName isn't found. - /// The name of the property. - /// The created property handle. - public static IFCAnyHandle CreateClassificationReferencePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, BuiltInParameter revitBuiltInParam, string ifcPropertyName) - { - IFCAnyHandle propHnd = CreateClassificationReferencePropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) - return propHnd; - - if (revitBuiltInParam != BuiltInParameter.INVALID) + else if (parameterType == SpecTypeId.AreaForce) { - string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); - propHnd = CreateClassificationReferencePropertyFromElement(file, exporterIFC, elem, builtInParamName, ifcPropertyName); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) - return propHnd; + double scaledValue = UnitUtil.ScalePlanarForce(propertyValue); + propertyHandle = CreatePlanarForcePropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, null); } - - return null; - } - - /// - /// Create a Force measure property from the element's parameter. - /// - /// The IFC file. - /// The ExporterIFC. - /// The Element. - /// The name of the parameter. - /// The built in parameter to use, if revitParameterName isn't found. - /// The name of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateForcePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) - { - IFCAnyHandle propHnd = CreateForcePropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, valueType); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) - return propHnd; - - if (revitBuiltInParam != BuiltInParameter.INVALID) + else if (parameterType == SpecTypeId.LinearForce || + parameterType == SpecTypeId.WeightPerUnitLength) { - string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); - propHnd = CreateForcePropertyFromElement(file, exporterIFC, elem, builtInParamName, ifcPropertyName, valueType); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) - return propHnd; + double scaledValue = UnitUtil.ScaleLinearForce(propertyValue); + propertyHandle = CreateLinearForcePropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, null); } - - return null; - } - - /// - /// Create a Power measure property from the element's parameter. - /// - /// The IFC file. - /// The ExporterIFC. - /// The Element. - /// The name of the parameter. - /// The built in parameter to use, if revitParameterName isn't found. - /// The name of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreatePowerPropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) - { - IFCAnyHandle propHnd = CreatePowerPropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, valueType); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) - return propHnd; - - if (revitBuiltInParam != BuiltInParameter.INVALID) + else if (parameterType == SpecTypeId.AirFlow || + parameterType == SpecTypeId.Flow) { - string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); - propHnd = CreatePowerPropertyFromElement(file, exporterIFC, elem, builtInParamName, ifcPropertyName, valueType); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) - return propHnd; + double scaledValue = UnitUtil.ScaleVolumetricFlowRate(propertyValue); + propertyHandle = CreateVolumetricFlowRatePropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, null); } - - return null; - } - - /// - /// Create a Mass measure property from the element's parameter. - /// - /// The IFC file. - /// The ExporterIFC. - /// The Element. - /// The name of the parameter. - /// The built in parameter to use, if revitParameterName isn't found. - /// The name of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateMassPropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) - { - IFCAnyHandle propHnd = CreateMassMeasurePropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, valueType); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) - return propHnd; - - if (revitBuiltInParam != BuiltInParameter.INVALID) + else if (parameterType == SpecTypeId.HvacPressure || + parameterType == SpecTypeId.PipingPressure || + parameterType == SpecTypeId.Stress) { - string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); - propHnd = CreateMassMeasurePropertyFromElement(file, exporterIFC, elem, builtInParamName, ifcPropertyName, valueType); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) - return propHnd; + double scaledValue = UnitUtil.ScalePressure(propertyValue); + propertyHandle = CreatePressurePropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, null); } - - return null; - } - - /// - /// Create a Mass density measure property from the element's parameter. - /// - /// The IFC file. - /// The ExporterIFC. - /// The Element. - /// The name of the parameter. - /// The built in parameter to use, if revitParameterName isn't found. - /// The name of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateMassDensityPropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) - { - IFCAnyHandle propHnd = CreateMassDensityPropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, valueType); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) - return propHnd; - - if (revitBuiltInParam != BuiltInParameter.INVALID) + else if (parameterType == SpecTypeId.HvacVelocity || + parameterType == SpecTypeId.PipingVelocity || + parameterType == SpecTypeId.StructuralVelocity || + parameterType == SpecTypeId.Speed) { - string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); - propHnd = CreateMassDensityPropertyFromElement(file, exporterIFC, elem, builtInParamName, ifcPropertyName, valueType); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) - return propHnd; + double scaledValue = UnitUtil.ScaleLinearVelocity(propertyValue); + propertyHandle = CreateLinearVelocityPropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, null); } - - return null; - } - - /// - /// Create a Mass density measure property from the element's parameter. - /// - /// The IFC file. - /// The ExporterIFC. - /// The Element. - /// The name of the parameter. - /// The name of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateMassDensityPropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, string ifcPropertyName, PropertyValueType valueType) - { - return CreateDoublePropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, - "IfcMassDensityMeasure", SpecTypeId.MassDensity, valueType); - } - - - /// - /// Create a Modulus Of Elasticity measure property from the element's parameter. - /// - /// The IFC file. - /// The ExporterIFC. - /// The Element. - /// The name of the parameter. - /// The built in parameter to use, if revitParameterName isn't found. - /// The name of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateModulusOfElasticityPropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) - { - IFCAnyHandle propHnd = CreateModulusOfElasticityPropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, valueType); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) - return propHnd; - - if (revitBuiltInParam != BuiltInParameter.INVALID) + else if (parameterType == SpecTypeId.Mass || + parameterType == SpecTypeId.PipingMass) { - string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); - propHnd = CreateModulusOfElasticityPropertyFromElement(file, exporterIFC, elem, builtInParamName, ifcPropertyName, valueType); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) - return propHnd; + double scaledValue = UnitUtil.ScaleMass(propertyValue); + propertyHandle = CreateMassPropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, null); } - - return null; - } - - /// - /// Create a Modulus Of Elasticity measure property from the element's parameter. - /// - /// The IFC file. - /// The ExporterIFC. - /// The Element. - /// The name of the parameter. - /// The name of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateModulusOfElasticityPropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, string ifcPropertyName, PropertyValueType valueType) - { - return CreateDoublePropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, - "IfcModulusOfElasticityMeasure", SpecTypeId.Stress, valueType); - } - - /// - /// Create a Heating Value measure property from the element's parameter. - /// - /// The IFC file. - /// The ExporterIFC. - /// The Element. - /// The name of the parameter. - /// The built in parameter to use, if revitParameterName isn't found. - /// The name of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateHeatingValuePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) - { - IFCAnyHandle propHnd = CreateHeatingValuePropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, valueType); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) - return propHnd; - - if (revitBuiltInParam != BuiltInParameter.INVALID) + else if (parameterType == SpecTypeId.MassDensity || + parameterType == SpecTypeId.HvacDensity) { - string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); - propHnd = CreateHeatingValuePropertyFromElement(file, exporterIFC, elem, builtInParamName, ifcPropertyName, valueType); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) - return propHnd; + double scaledValue = UnitUtil.ScaleMassDensity(propertyValue); + propertyHandle = CreateMassDensityPropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, null); } - - return null; - } - - /// - /// Create a Heating Value measure property from the element's parameter. - /// - /// The IFC file. - /// The ExporterIFC. - /// The Element. - /// The name of the parameter. - /// The name of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateHeatingValuePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, string ifcPropertyName, PropertyValueType valueType) - { - return CreateDoublePropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, - "IfcHeatingValueMeasure", SpecTypeId.SpecificHeatOfVaporization, valueType); - } - - /// - /// Create a Moisture Diffusivity measure property from the element's parameter. - /// - /// The IFC file. - /// The ExporterIFC. - /// The Element. - /// The name of the parameter. - /// The built in parameter to use, if revitParameterName isn't found. - /// The name of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateMoistureDiffusivityPropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) - { - IFCAnyHandle propHnd = CreateMoistureDiffusivityPropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, valueType); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) - return propHnd; - - if (revitBuiltInParam != BuiltInParameter.INVALID) + else if (parameterType == SpecTypeId.PipingDensity) { - string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); - propHnd = CreateMoistureDiffusivityPropertyFromElement(file, exporterIFC, elem, builtInParamName, ifcPropertyName, valueType); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) - return propHnd; + double scaledValue = UnitUtil.ScaleIonConcentration(propertyValue); + propertyHandle = CreateIonConcentrationPropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, null); + } + else if (parameterType == SpecTypeId.MomentOfInertia) + { + double scaledValue = UnitUtil.ScaleMomentOfInertia(propertyValue); + propertyHandle = CreateMomentOfInertiaPropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, null); + } + else if (parameterType == SpecTypeId.Number) + { + propertyHandle = CreateRealPropertyFromCache(file, propertyName, new List() { propertyValue }, valueType, null); + } + else if (parameterType == SpecTypeId.PipingVolume || + parameterType == SpecTypeId.ReinforcementVolume || + parameterType == SpecTypeId.SectionModulus || + parameterType == SpecTypeId.Volume) + { + double scaledValue = UnitUtil.ScaleVolume(propertyValue); + propertyHandle = CreateVolumePropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, null); + } + else if (parameterType == SpecTypeId.PipingMassPerTime || + parameterType == SpecTypeId.HvacMassPerTime) + { + double scaledValue = UnitUtil.ScaleMassFlowRate(propertyValue); + propertyHandle = CreateMassFlowRatePropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, null); + } + else if (parameterType == SpecTypeId.AngularSpeed) + { + double scaledValue = UnitUtil.ScaleRotationalFrequency(propertyValue); + propertyHandle = CreateRotationalFrequencyPropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, null); + } + else if (parameterType == SpecTypeId.ThermalConductivity) + { + double scaledValue = UnitUtil.ScaleThermalConductivity(propertyValue); + propertyHandle = CreateThermalConductivityPropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, null); + } + else if (parameterType == SpecTypeId.SpecificHeat) + { + double scaledValue = UnitUtil.ScaleSpecificHeatCapacity(propertyValue); + propertyHandle = CreateSpecificHeatCapacityPropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, null); + } + else if (parameterType == SpecTypeId.Permeability) + { + double scaledValue = UnitUtil.ScaleVaporPermeability(propertyValue); + propertyHandle = CreateVaporPermeabilityPropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, null); + } + else if (parameterType == SpecTypeId.HvacViscosity || + parameterType == SpecTypeId.PipingViscosity) + { + double scaledValue = UnitUtil.ScaleDynamicViscosity(propertyValue); + propertyHandle = CreateDynamicViscosityPropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, null); + } + else if (parameterType == SpecTypeId.ThermalExpansionCoefficient) + { + double scaledValue = UnitUtil.ScaleThermalExpansionCoefficient(propertyValue); + propertyHandle = CreateThermalExpansionCoefficientPropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, null); + } + else if (parameterType == SpecTypeId.SpecificHeatOfVaporization) + { + double scaledValue = UnitUtil.ScaleHeatingValue(propertyValue); + propertyHandle = CreateHeatingValuePropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, null); + } + else if (parameterType == SpecTypeId.IsothermalMoistureCapacity) + { + double scaledValue = UnitUtil.ScaleIsothermalMoistureCapacity(propertyValue); + propertyHandle = CreateIsothermalMoistureCapacityPropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, null); + } + else if (parameterType == SpecTypeId.HvacPowerDensity) + { + double scaledValue = UnitUtil.ScaleHeatFluxDensity(propertyValue); + propertyHandle = CreateHeatFluxDensityPropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, null); + } + else if (parameterType == SpecTypeId.MassPerUnitArea && !ExporterCacheManager.ExportOptionsCache.ExportAsOlderThanIFC4) + { + double scaledValue = UnitUtil.ScaleAreaDensity(propertyValue); + propertyHandle = CreateAreaDensityPropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, null); + } + else if (parameterType == SpecTypeId.Time || + parameterType == SpecTypeId.Period) + { + double scaledValue = UnitUtil.ScaleTime(propertyValue); + IFCData timeData = IFCDataUtil.CreateAsTimeMeasure(scaledValue); + propertyHandle = CreateCommonProperty(file, propertyName, timeData, + valueType, null); + } + else if (parameterType == SpecTypeId.ColorTemperature) + { + double scaledValue = UnitUtil.ScaleDouble(SpecTypeId.ColorTemperature, propertyValue); + propertyHandle = CreateRealPropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, "COLORTEMPERATURE"); + } + else if (parameterType == SpecTypeId.CostPerArea) + { + double scaledValue = UnitUtil.ScaleDouble(SpecTypeId.CostPerArea, propertyValue); + propertyHandle = CreateRealPropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, "COSTPERAREA"); + } + else if (parameterType == SpecTypeId.ApparentPowerDensity) + { + double scaledValue = UnitUtil.ScaleDouble(SpecTypeId.ApparentPowerDensity, propertyValue); + propertyHandle = CreateRealPropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, "APPARENTPOWERDENSITY"); + } + else if (parameterType == SpecTypeId.CostRateEnergy) + { + double scaledValue = UnitUtil.ScaleDouble(SpecTypeId.CostRateEnergy, propertyValue); + propertyHandle = CreateRealPropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, "COSTRATEENERGY"); + } + else if (parameterType == SpecTypeId.CostRatePower) + { + double scaledValue = UnitUtil.ScaleDouble(SpecTypeId.CostRatePower, propertyValue); + propertyHandle = CreateRealPropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, "COSTRATEPOWER"); + } + else if (parameterType == SpecTypeId.Efficacy) + { + double scaledValue = UnitUtil.ScaleDouble(SpecTypeId.Efficacy, propertyValue); + propertyHandle = CreateRealPropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, "LUMINOUSEFFICACY"); + } + else if (parameterType == SpecTypeId.Luminance) + { + double scaledValue = UnitUtil.ScaleDouble(SpecTypeId.Luminance, propertyValue); + propertyHandle = CreateRealPropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, "LUMINANCE"); + } + else if (parameterType == SpecTypeId.ElectricalPowerDensity) + { + double scaledValue = UnitUtil.ScaleDouble(SpecTypeId.ElectricalPowerDensity, propertyValue); + propertyHandle = CreateRealPropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, "ELECTRICALPOWERDENSITY"); + } + else if (parameterType == SpecTypeId.PowerPerLength) + { + double scaledValue = UnitUtil.ScaleDouble(SpecTypeId.PowerPerLength, propertyValue); + propertyHandle = CreateRealPropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, "POWERPERLENGTH"); + } + else if (parameterType == SpecTypeId.ElectricalResistivity) + { + double scaledValue = UnitUtil.ScaleDouble(SpecTypeId.ElectricalResistivity, propertyValue); + propertyHandle = CreateRealPropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, "ELECTRICALRESISTIVITY"); + } + else if (parameterType == SpecTypeId.HeatCapacityPerArea) + { + double scaledValue = UnitUtil.ScaleDouble(SpecTypeId.HeatCapacityPerArea, propertyValue); + propertyHandle = CreateRealPropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, "HEATCAPACITYPERAREA"); + } + else if (parameterType == SpecTypeId.ThermalGradientCoefficientForMoistureCapacity) + { + double scaledValue = UnitUtil.ScaleDouble(SpecTypeId.ThermalGradientCoefficientForMoistureCapacity, propertyValue); + propertyHandle = CreateRealPropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, "THERMALGRADIENTCOEFFICIENTFORMOISTURECAPACITY"); + } + else if (parameterType == SpecTypeId.ThermalMass) + { + double scaledValue = UnitUtil.ScaleDouble(SpecTypeId.ThermalMass, propertyValue); + propertyHandle = CreateRealPropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, "THERMALMASS"); + } + else if (parameterType == SpecTypeId.AirFlowDensity) + { + double scaledValue = UnitUtil.ScaleDouble(SpecTypeId.AirFlowDensity, propertyValue); + propertyHandle = CreateRealPropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, "AIRFLOWDENSITY"); + } + else if (parameterType == SpecTypeId.AirFlowDividedByCoolingLoad) + { + double scaledValue = UnitUtil.ScaleDouble(SpecTypeId.AirFlowDividedByCoolingLoad, propertyValue); + propertyHandle = CreateRealPropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, "AIRFLOWDIVIDEDBYCOOLINGLOAD"); + } + else if (parameterType == SpecTypeId.AirFlowDividedByVolume) + { + double scaledValue = UnitUtil.ScaleDouble(SpecTypeId.AirFlowDividedByVolume, propertyValue); + propertyHandle = CreateRealPropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, "AIRFLOWDIVIDEDBYVOLUME"); + } + else if (parameterType == SpecTypeId.AreaDividedByCoolingLoad) + { + double scaledValue = UnitUtil.ScaleDouble(SpecTypeId.AreaDividedByCoolingLoad, propertyValue); + propertyHandle = CreateRealPropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, "AREADIVIDEDBYCOOLINGLOAD"); + } + else if (parameterType == SpecTypeId.AreaDividedByHeatingLoad) + { + double scaledValue = UnitUtil.ScaleDouble(SpecTypeId.AreaDividedByHeatingLoad, propertyValue); + propertyHandle = CreateRealPropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, "AREADIVIDEDBYHEATINGLOAD"); + } + else if (parameterType == SpecTypeId.CoolingLoadDividedByArea) + { + double scaledValue = UnitUtil.ScaleDouble(SpecTypeId.CoolingLoadDividedByArea, propertyValue); + propertyHandle = CreateRealPropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, "COOLINGLOADDIVIDEDBYAREA"); + } + else if (parameterType == SpecTypeId.CoolingLoadDividedByVolume) + { + double scaledValue = UnitUtil.ScaleDouble(SpecTypeId.CoolingLoadDividedByVolume, propertyValue); + propertyHandle = CreateRealPropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, "COOLINGLOADDIVIDEDBYVOLUME"); + } + else if (parameterType == SpecTypeId.FlowPerPower) + { + double scaledValue = UnitUtil.ScaleDouble(SpecTypeId.FlowPerPower, propertyValue); + propertyHandle = CreateRealPropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, "FLOWPERPOWER"); + } + else if (parameterType == SpecTypeId.HvacFriction) + { + double scaledValue = UnitUtil.ScaleDouble(SpecTypeId.HvacFriction, propertyValue); + propertyHandle = CreateRealPropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, "FRICTIONLOSS"); + } + else if (parameterType == SpecTypeId.HeatingLoadDividedByArea) + { + double scaledValue = UnitUtil.ScaleDouble(SpecTypeId.HeatingLoadDividedByArea, propertyValue); + propertyHandle = CreateRealPropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, "HEATINGLOADDIVIDEDBYAREA"); + } + else if (parameterType == SpecTypeId.HeatingLoadDividedByVolume) + { + double scaledValue = UnitUtil.ScaleDouble(SpecTypeId.HeatingLoadDividedByVolume, propertyValue); + propertyHandle = CreateRealPropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, "HEATINGLOADDIVIDEDBYVOLUME"); + } + else if (parameterType == SpecTypeId.PowerPerFlow) + { + double scaledValue = UnitUtil.ScaleDouble(SpecTypeId.PowerPerFlow, propertyValue); + propertyHandle = CreateRealPropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, "POWERPERFLOW"); + } + else if (parameterType == SpecTypeId.PipingFriction) + { + double scaledValue = UnitUtil.ScaleDouble(SpecTypeId.PipingFriction, propertyValue); + propertyHandle = CreateRealPropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, "PIPINGFRICTION"); + } + else if (parameterType == SpecTypeId.AreaSpringCoefficient) + { + double scaledValue = UnitUtil.ScaleDouble(SpecTypeId.AreaSpringCoefficient, propertyValue); + propertyHandle = CreateRealPropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, "AREASPRINGCOEFFICIENT"); + } + else if (parameterType == SpecTypeId.LineSpringCoefficient) + { + double scaledValue = UnitUtil.ScaleDouble(SpecTypeId.LineSpringCoefficient, propertyValue); + propertyHandle = CreateRealPropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, "LINESPRINGCOEFFICIENT"); + } + else if (parameterType == SpecTypeId.MassPerUnitArea) + { + double scaledValue = UnitUtil.ScaleDouble(SpecTypeId.MassPerUnitArea, propertyValue); + propertyHandle = CreateRealPropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, "MASSPERUNITAREA"); + } + else if (parameterType == SpecTypeId.ReinforcementAreaPerUnitLength) + { + double scaledValue = UnitUtil.ScaleDouble(SpecTypeId.ReinforcementAreaPerUnitLength, propertyValue); + propertyHandle = CreateRealPropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, "REINFORCEMENTAREAPERUNITLENGTH"); + } + else if (parameterType == SpecTypeId.RotationalLineSpringCoefficient) + { + double scaledValue = UnitUtil.ScaleDouble(SpecTypeId.RotationalLineSpringCoefficient, propertyValue); + propertyHandle = CreateRealPropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, "ROTATIONALLINESPRINGCOEFFICIENT"); + } + else if (parameterType == SpecTypeId.RotationalPointSpringCoefficient) + { + double scaledValue = UnitUtil.ScaleDouble(SpecTypeId.RotationalPointSpringCoefficient, propertyValue); + propertyHandle = CreateRealPropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, "ROTATIONALPOINTSPRINGCOEFFICIENT"); + } + else if (parameterType == SpecTypeId.UnitWeight) + { + double scaledValue = UnitUtil.ScaleDouble(SpecTypeId.UnitWeight, propertyValue); + propertyHandle = CreateRealPropertyFromCache(file, propertyName, + new List() { scaledValue }, valueType, "UNITWEIGHT"); } + else + { + double scaledValue = propertyValue; + if (fallbackUnitType != null) + scaledValue = UnitUtils.ConvertFromInternalUnits(propertyValue, fallbackUnitType); - return null; - } + propertyHandle = CreateRealPropertyFromCache(file, propertyName, new List() { scaledValue }, valueType, null); + } - /// - /// Create a Diffusivity measure property from the element's parameter. - /// - /// The IFC file. - /// The ExporterIFC. - /// The Element. - /// The name of the parameter. - /// The name of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateMoistureDiffusivityPropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, string ifcPropertyName, PropertyValueType valueType) - { - return CreateDoublePropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, - "IfcMoistureDiffusivityMeasure", SpecTypeId.Diffusivity, valueType); + return propertyHandle; } /// - /// Create a Moment Of Inertia measure property from the element's parameter. + /// Creates and associates the common property sets associated with ElementTypes. These are handled differently than for elements. /// - /// The IFC file. - /// The ExporterIFC. - /// The Element. - /// The name of the parameter. - /// The built in parameter to use, if revitParameterName isn't found. - /// The name of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateMomentOfInertiaPropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) + /// The IFC exporter object. + /// The element type whose properties are exported. + /// The handles of property sets already associated with the type. + /// The handle of the entity associated with the element type object. + public static void CreateElementTypeProperties(ExporterIFC exporterIFC, ElementType elementType, + HashSet existingPropertySets, IFCAnyHandle prodTypeHnd) { - IFCAnyHandle propHnd = CreateMomentOfInertiaPropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, valueType); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) - return propHnd; - - if (revitBuiltInParam != BuiltInParameter.INVALID) - { - string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); - propHnd = CreateMomentOfInertiaPropertyFromElement(file, exporterIFC, elem, builtInParamName, ifcPropertyName, valueType); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) - return propHnd; - } + HashSet propertySets = new HashSet(); - return null; - } + // Pass in an empty set of handles - we don't want IfcRelDefinesByProperties for type properties. + ISet associatedObjectIds = new HashSet(); + CreateInternalRevitPropertySets(exporterIFC, elementType, associatedObjectIds, false); - /// - /// Create a Moment Of Inertia measure property from the element's parameter. - /// - /// The IFC file. - /// The ExporterIFC. - /// The Element. - /// The name of the parameter. - /// The name of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateMomentOfInertiaPropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, string ifcPropertyName, PropertyValueType valueType) - { - return CreateDoublePropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, - "IfcMomentOfInertiaMeasure", SpecTypeId.MomentOfInertia, valueType); - } + TypePropertyInfo additionalPropertySets = null; + ElementId typeId = elementType.Id; + if (ExporterCacheManager.TypePropertyInfoCache.TryGetValue(typeId, out additionalPropertySets)) + propertySets.UnionWith(additionalPropertySets.PropertySets); - /// - /// Create a Isothermal Moisture Capacity measure property from the element's parameter. - /// - /// The IFC file. - /// The ExporterIFC. - /// The Element. - /// The name of the parameter. - /// The built in parameter to use, if revitParameterName isn't found. - /// The name of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateIsothermalMoistureCapacityPropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) - { - IFCAnyHandle propHnd = CreateIsothermalMoistureCapacityPropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, valueType); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) - return propHnd; + if (existingPropertySets != null && existingPropertySets.Count > 0) + propertySets.UnionWith(existingPropertySets); - if (revitBuiltInParam != BuiltInParameter.INVALID) + IFCFile file = exporterIFC.GetFile(); + using (IFCTransaction transaction = new IFCTransaction(file)) { - string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); - propHnd = CreateIsothermalMoistureCapacityPropertyFromElement(file, exporterIFC, elem, builtInParamName, ifcPropertyName, valueType); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) - return propHnd; - } + Document doc = elementType.Document; + + IFCAnyHandle ownerHistory = ExporterCacheManager.OwnerHistoryHandle; + + IList currPsetsToCreate = + ExporterUtil.GetCurrPSetsToCreate(prodTypeHnd, PSetsToProcess.Type); + foreach (PropertySetDescription currDesc in currPsetsToCreate) + { + // Last conditional check: if the property set comes from a ViewSchedule, check if the element is in the schedule. + if (currDesc.ViewScheduleId != ElementId.InvalidElementId) + if (!ExporterCacheManager.ViewScheduleElementCache[currDesc.ViewScheduleId].Contains(typeId)) + continue; + + ElementOrConnector elementOrConnector = new ElementOrConnector(elementType); + ISet props = currDesc.ProcessEntries(file, exporterIFC, null, elementOrConnector, elementType, prodTypeHnd); + if (props.Count > 0) + { + string paramSetName = currDesc.Name; + string guid = GUIDUtil.GenerateIFCGuidFrom( + GUIDUtil.CreateGUIDString(IFCEntityType.IfcPropertySet, paramSetName, prodTypeHnd)); + + IFCAnyHandle propertySet = IFCInstanceExporter.CreatePropertySet(file, guid, ownerHistory, paramSetName, null, props); + propertySets.Add(propertySet); + } + } + + if (propertySets.Count != 0) + { + prodTypeHnd.SetAttribute("HasPropertySets", propertySets); + // Don't assign the property sets to the instances if we have just assigned them to the type. + if (additionalPropertySets != null) + additionalPropertySets.AssignedToType = true; + } + + transaction.Commit(); + } + } + + public static IList GetDoubleValuesFromParameterByType(Element elem, string revitParameterName, ForgeTypeId specTypeId, PropertyValueType valueType) + { + List values = new List(); + + switch (valueType) + { + case PropertyValueType.SingleValue: + case PropertyValueType.ListValue: // TODO: REVIT-193510 + case PropertyValueType.TableValue: + { + double? propertyValue = GetScaledDoubleValueFromParameter(elem, revitParameterName, specTypeId); + if (propertyValue.HasValue) + values.Add(propertyValue.Value); + } + break; + case PropertyValueType.BoundedValue: + { + double? valueSetPoint = GetScaledDoubleValueFromParameter(elem, revitParameterName + ".SetPointValue", specTypeId); + double? valueUpper = GetScaledDoubleValueFromParameter(elem, revitParameterName + ".UpperBoundValue", specTypeId); + double? valueLower = GetScaledDoubleValueFromParameter(elem, revitParameterName + ".LowerBoundValue", specTypeId); + + if (valueUpper == null && valueLower == null && valueSetPoint == null) + valueUpper = GetScaledDoubleValueFromParameter(elem, revitParameterName, specTypeId); + + if (valueUpper != null || valueLower != null || valueSetPoint != null) + { + values.Add(valueSetPoint); + values.Add(valueUpper); + values.Add(valueLower); + } + } + break; + default: + throw new InvalidOperationException("Missing case!"); + + } + return values; + } + + public static double? GetScaledDoubleValueFromParameter(Element elem, string revitParameterName, ForgeTypeId specTypeId) + { + double propertyValue = 0.0; + Parameter param = ParameterUtil.GetDoubleValueFromElement(elem, revitParameterName, out propertyValue); + if (param != null) + { + if (!ParameterUtil.ParameterDataTypeIsEqualTo(param, SpecTypeId.Number) && + param.StorageType != StorageType.String) //The built-in parameter corresponding to "TotalWattage" is a string value in Revit that is likely going to be in the current units, and doesn't need to be scaled twice. + { + propertyValue = UnitUtil.ScaleDouble(specTypeId, propertyValue); + } + + // Convert value from internal to displayed units if we want to export it as Real + if (specTypeId == SpecTypeId.Number) + { + ForgeTypeId paramUnitType = GetParameterUnitType(param); + if (paramUnitType != null) + propertyValue = UnitUtils.ConvertFromInternalUnits(propertyValue, paramUnitType); + } + + return propertyValue; + } return null; } + #region Create___PropertyFromElement_1 /// - /// Create a Isothermal Moisture Capacity measure property from the element's parameter. + /// Create Area measure property from the element's parameter. /// /// The IFC file. - /// The ExporterIFC. /// The Element. /// The name of the parameter. + /// The built in parameter to use. /// The name of the property. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreateIsothermalMoistureCapacityPropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, string ifcPropertyName, PropertyValueType valueType) + public static IFCAnyHandle CreateAreaPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) { - return CreateDoublePropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, - "IfcIsothermalMoistureCapacityMeasure", SpecTypeId.IsothermalMoistureCapacity, valueType); + IFCAnyHandle propHnd = CreateAreaPropertyFromElement(file, elem, revitParameterName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; + + if (revitBuiltInParam != BuiltInParameter.INVALID) + { + string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); + propHnd = CreateAreaPropertyFromElement(file, elem, builtInParamName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; + } + + return null; } /// - /// Create a Isothermal Moisture Capacity measure property from the element's parameter. + /// Create Acceleration measure property from the element's parameter. /// /// The IFC file. - /// The ExporterIFC. /// The Element. /// The name of the parameter. - /// The built in parameter to use, if revitParameterName isn't found. + /// The built in parameter to use. /// The name of the property. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreateIonConcentrationPropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) + public static IFCAnyHandle CreateAccelerationPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) { - IFCAnyHandle propHnd = CreateIonConcentrationPropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, valueType); + IFCAnyHandle propHnd = CreateAccelerationPropertyFromElement(file, elem, revitParameterName, ifcPropertyName, valueType); if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) return propHnd; if (revitBuiltInParam != BuiltInParameter.INVALID) { string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); - propHnd = CreateIonConcentrationPropertyFromElement(file, exporterIFC, elem, builtInParamName, ifcPropertyName, valueType); + propHnd = CreateAccelerationPropertyFromElement(file, elem, builtInParamName, ifcPropertyName, valueType); if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) return propHnd; } @@ -2954,44 +2502,54 @@ public static IList GetBoundedDataFromElement(Element elem, string revi } /// - /// Create a Ion Concentration measure property from the element's parameter. + /// Create AngularVelocity measure property from the element's parameter. /// /// The IFC file. - /// The ExporterIFC. /// The Element. /// The name of the parameter. + /// The built in parameter to use. /// The name of the property. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreateIonConcentrationPropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, string ifcPropertyName, PropertyValueType valueType) + public static IFCAnyHandle CreateAngularVelocityPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) { - return CreateDoublePropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, - "IfcIonConcentrationMeasure", SpecTypeId.PipingDensity, valueType); + IFCAnyHandle propHnd = CreateAngularVelocityPropertyFromElement(file, elem, revitParameterName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; + + if (revitBuiltInParam != BuiltInParameter.INVALID) + { + string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); + propHnd = CreateAngularVelocityPropertyFromElement(file, elem, builtInParamName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; + } + + return null; } /// - /// Create a Mass flow rate measure property from the element's parameter. + /// Create AreaDensity measure property from the element's parameter. /// /// The IFC file. - /// The ExporterIFC. /// The Element. /// The name of the parameter. - /// The built in parameter to use, if revitParameterName isn't found. + /// The built in parameter to use. /// The name of the property. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreateMassFlowRatePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) + public static IFCAnyHandle CreateAreaDensityPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) { - IFCAnyHandle propHnd = CreateMassFlowRatePropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, valueType); + IFCAnyHandle propHnd = CreateAreaDensityPropertyFromElement(file, elem, revitParameterName, ifcPropertyName, valueType); if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) return propHnd; if (revitBuiltInParam != BuiltInParameter.INVALID) { string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); - propHnd = CreateMassFlowRatePropertyFromElement(file, exporterIFC, elem, builtInParamName, ifcPropertyName, valueType); + propHnd = CreateAreaDensityPropertyFromElement(file, elem, builtInParamName, ifcPropertyName, valueType); if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) return propHnd; } @@ -3000,44 +2558,54 @@ public static IList GetBoundedDataFromElement(Element elem, string revi } /// - /// Create a Mass flow rate measure property from the element's parameter. + /// Create DynamicViscosity measure property from the element's parameter. /// /// The IFC file. - /// The ExporterIFC. /// The Element. /// The name of the parameter. + /// The built in parameter to use. /// The name of the property. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreateMassFlowRatePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, string ifcPropertyName, PropertyValueType valueType) + public static IFCAnyHandle CreateDynamicViscosityPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) { - return CreateDoublePropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, - "IfcMassFlowRateMeasure", SpecTypeId.PipingMassPerTime, valueType); + IFCAnyHandle propHnd = CreateDynamicViscosityPropertyFromElement(file, elem, revitParameterName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; + + if (revitBuiltInParam != BuiltInParameter.INVALID) + { + string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); + propHnd = CreateDynamicViscosityPropertyFromElement(file, elem, builtInParamName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; + } + + return null; } /// - /// Create a Rotational frequency measure property from the element's parameter. + /// Create ElectricCurrent measure property from the element's parameter. /// /// The IFC file. - /// The ExporterIFC. /// The Element. /// The name of the parameter. - /// The built in parameter to use, if revitParameterName isn't found. + /// The built in parameter to use. /// The name of the property. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreateRotationalFrequencyPropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) + public static IFCAnyHandle CreateElectricCurrentPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) { - IFCAnyHandle propHnd = CreateRotationalFrequencyPropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, valueType); + IFCAnyHandle propHnd = CreateElectricCurrentPropertyFromElement(file, elem, revitParameterName, ifcPropertyName, valueType); if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) return propHnd; if (revitBuiltInParam != BuiltInParameter.INVALID) { string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); - propHnd = CreateRotationalFrequencyPropertyFromElement(file, exporterIFC, elem, builtInParamName, ifcPropertyName, valueType); + propHnd = CreateElectricCurrentPropertyFromElement(file, elem, builtInParamName, ifcPropertyName, valueType); if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) return propHnd; } @@ -3046,43 +2614,54 @@ public static IList GetBoundedDataFromElement(Element elem, string revi } /// - /// Create a Rotational frequency measure property from the element's parameter. + /// Create ElectricVoltage measure property from the element's parameter. /// /// The IFC file. - /// The ExporterIFC. /// The Element. /// The name of the parameter. + /// The built in parameter to use. /// The name of the property. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreateRotationalFrequencyPropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, string ifcPropertyName, PropertyValueType valueType) + public static IFCAnyHandle CreateElectricVoltagePropertyFromElement(IFCFile file, Element elem, string revitParameterName, + BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) { - return CreateDoublePropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, - "IfcRotationalFrequencyMeasure", SpecTypeId.AngularSpeed, valueType); + IFCAnyHandle propHnd = CreateElectricVoltagePropertyFromElement(file, elem, revitParameterName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; + + if (revitBuiltInParam != BuiltInParameter.INVALID) + { + string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); + propHnd = CreateElectricVoltagePropertyFromElement(file, elem, builtInParamName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; + } + + return null; } + /// - /// Create an Area density measure property from the element's parameter. + /// Create Energy measure property from the element's parameter. /// /// The IFC file. - /// The ExporterIFC. /// The Element. /// The name of the parameter. - /// The built in parameter to use, if revitParameterName isn't found. + /// The built in parameter to use. /// The name of the property. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreateAreaDensityPropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) + public static IFCAnyHandle CreateEnergyPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) { - IFCAnyHandle propHnd = CreateAreaDensityPropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, valueType); + IFCAnyHandle propHnd = CreateEnergyPropertyFromElement(file, elem, revitParameterName, ifcPropertyName, valueType); if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) return propHnd; if (revitBuiltInParam != BuiltInParameter.INVALID) { string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); - propHnd = CreateAreaDensityPropertyFromElement(file, exporterIFC, elem, builtInParamName, ifcPropertyName, valueType); + propHnd = CreateEnergyPropertyFromElement(file, elem, builtInParamName, ifcPropertyName, valueType); if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) return propHnd; } @@ -3091,44 +2670,54 @@ public static IList GetBoundedDataFromElement(Element elem, string revi } /// - /// Create an Area density measure property from the element's parameter. + /// Create Force measure property from the element's parameter. /// /// The IFC file. - /// The ExporterIFC. /// The Element. /// The name of the parameter. + /// The built in parameter to use. /// The name of the property. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreateAreaDensityPropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, string ifcPropertyName, PropertyValueType valueType) + public static IFCAnyHandle CreateForcePropertyFromElement(IFCFile file, Element elem, string revitParameterName, + BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) { - return CreateDoublePropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, - "IfcAreaDensityMeasure", SpecTypeId.MassPerUnitArea, valueType); + IFCAnyHandle propHnd = CreateForcePropertyFromElement(file, elem, revitParameterName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; + + if (revitBuiltInParam != BuiltInParameter.INVALID) + { + string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); + propHnd = CreateForcePropertyFromElement(file, elem, builtInParamName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; + } + + return null; } /// - /// Create a Luminous flux measure property from the element's parameter. + /// Create Frequency measure property from the element's parameter. /// /// The IFC file. - /// The ExporterIFC. /// The Element. /// The name of the parameter. - /// The built in parameter to use, if revitParameterName isn't found. + /// The built in parameter to use. /// The name of the property. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreateLuminousFluxMeasurePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) + public static IFCAnyHandle CreateFrequencyPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) { - IFCAnyHandle propHnd = CreateLuminousFluxMeasurePropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, valueType); + IFCAnyHandle propHnd = CreateFrequencyPropertyFromElement(file, elem, revitParameterName, ifcPropertyName, valueType); if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) return propHnd; if (revitBuiltInParam != BuiltInParameter.INVALID) { string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); - propHnd = CreateLuminousFluxMeasurePropertyFromElement(file, exporterIFC, elem, builtInParamName, ifcPropertyName, valueType); + propHnd = CreateFrequencyPropertyFromElement(file, elem, builtInParamName, ifcPropertyName, valueType); if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) return propHnd; } @@ -3137,27 +2726,26 @@ public static IList GetBoundedDataFromElement(Element elem, string revi } /// - /// Create a Luminous intensity measure property from the element's parameter. + /// Create HeatingValue measure property from the element's parameter. /// /// The IFC file. - /// The ExporterIFC. /// The Element. /// The name of the parameter. - /// The built in parameter to use, if revitParameterName isn't found. + /// The built in parameter to use. /// The name of the property. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreateLuminousIntensityPropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) + public static IFCAnyHandle CreateHeatingValuePropertyFromElement(IFCFile file, Element elem, string revitParameterName, + BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) { - IFCAnyHandle propHnd = CreateLuminousIntensityMeasurePropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, valueType); + IFCAnyHandle propHnd = CreateHeatingValuePropertyFromElement(file, elem, revitParameterName, ifcPropertyName, valueType); if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) return propHnd; if (revitBuiltInParam != BuiltInParameter.INVALID) { string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); - propHnd = CreateLuminousIntensityMeasurePropertyFromElement(file, exporterIFC, elem, builtInParamName, ifcPropertyName, valueType); + propHnd = CreateHeatingValuePropertyFromElement(file, elem, builtInParamName, ifcPropertyName, valueType); if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) return propHnd; } @@ -3166,27 +2754,26 @@ public static IList GetBoundedDataFromElement(Element elem, string revi } /// - /// Create a heat flux density measure property from the element's parameter. + /// Create Illuminance measure property from the element's parameter. /// /// The IFC file. - /// The ExporterIFC. /// The Element. /// The name of the parameter. - /// The built in parameter to use, if revitParameterName isn't found. + /// The built in parameter to use. /// The name of the property. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreateHeatFluxDensityPropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) + public static IFCAnyHandle CreateIlluminancePropertyFromElement(IFCFile file, Element elem, string revitParameterName, + BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) { - IFCAnyHandle propHnd = CreateHeatFluxDensityMeasurePropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, valueType); + IFCAnyHandle propHnd = CreateIlluminancePropertyFromElement(file, elem, revitParameterName, ifcPropertyName, valueType); if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) return propHnd; if (revitBuiltInParam != BuiltInParameter.INVALID) { string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); - propHnd = CreateHeatFluxDensityMeasurePropertyFromElement(file, exporterIFC, elem, builtInParamName, ifcPropertyName, valueType); + propHnd = CreateIlluminancePropertyFromElement(file, elem, builtInParamName, ifcPropertyName, valueType); if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) return propHnd; } @@ -3195,27 +2782,26 @@ public static IList GetBoundedDataFromElement(Element elem, string revi } /// - /// Create an illuminance measure property from the element's parameter. + /// Create IonConcentration measure property from the element's parameter. /// /// The IFC file. - /// The ExporterIFC. /// The Element. /// The name of the parameter. - /// The built in parameter to use, if revitParameterName isn't found. + /// The built in parameter to use. /// The name of the property. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreateIlluminancePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) + public static IFCAnyHandle CreateIonConcentrationPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) { - IFCAnyHandle propHnd = CreateIlluminanceMeasurePropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, valueType); + IFCAnyHandle propHnd = CreateIonConcentrationPropertyFromElement(file, elem, revitParameterName, ifcPropertyName, valueType); if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) return propHnd; if (revitBuiltInParam != BuiltInParameter.INVALID) { string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); - propHnd = CreateIlluminanceMeasurePropertyFromElement(file, exporterIFC, elem, builtInParamName, ifcPropertyName, valueType); + propHnd = CreateIonConcentrationPropertyFromElement(file, elem, builtInParamName, ifcPropertyName, valueType); if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) return propHnd; } @@ -3224,27 +2810,26 @@ public static IList GetBoundedDataFromElement(Element elem, string revi } /// - /// Create a pressure measure property from the element's parameter. + /// Create IsothermalMoistureCapacity measure property from the element's parameter. /// /// The IFC file. - /// The ExporterIFC. /// The Element. /// The name of the parameter. - /// The built in parameter to use, if revitParameterName isn't found. + /// The built in parameter to use. /// The name of the property. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreatePressurePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) + public static IFCAnyHandle CreateIsothermalMoistureCapacityPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) { - IFCAnyHandle propHnd = CreatePressurePropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, valueType); + IFCAnyHandle propHnd = CreateIsothermalMoistureCapacityPropertyFromElement(file, elem, revitParameterName, ifcPropertyName, valueType); if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) return propHnd; if (revitBuiltInParam != BuiltInParameter.INVALID) { string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); - propHnd = CreatePressurePropertyFromElement(file, exporterIFC, elem, builtInParamName, ifcPropertyName, valueType); + propHnd = CreateIsothermalMoistureCapacityPropertyFromElement(file, elem, builtInParamName, ifcPropertyName, valueType); if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) return propHnd; } @@ -3253,27 +2838,26 @@ public static IList GetBoundedDataFromElement(Element elem, string revi } /// - /// Create a ThermalConductivity measure property from the element's parameter. + /// Create HeatFluxDensity measure property from the element's parameter. /// /// The IFC file. - /// The ExporterIFC. /// The Element. /// The name of the parameter. - /// The built in parameter to use, if revitParameterName isn't found. + /// The built in parameter to use. /// The name of the property. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreateThermalConductivityPropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) + public static IFCAnyHandle CreateHeatFluxDensityPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) { - IFCAnyHandle propHnd = CreateThermalConductivityPropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, valueType); + IFCAnyHandle propHnd = CreateHeatFluxDensityPropertyFromElement(file, elem, revitParameterName, ifcPropertyName, valueType); if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) return propHnd; if (revitBuiltInParam != BuiltInParameter.INVALID) { string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); - propHnd = CreateThermalConductivityPropertyFromElement(file, exporterIFC, elem, builtInParamName, ifcPropertyName, valueType); + propHnd = CreateHeatFluxDensityPropertyFromElement(file, elem, builtInParamName, ifcPropertyName, valueType); if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) return propHnd; } @@ -3282,27 +2866,26 @@ public static IList GetBoundedDataFromElement(Element elem, string revi } /// - /// Create a ThermalExpansionCoefficient measure property from the element's parameter. + /// Create Length measure property from the element's parameter. /// /// The IFC file. - /// The ExporterIFC. /// The Element. /// The name of the parameter. - /// The built in parameter to use, if revitParameterName isn't found. + /// The built in parameter to use. /// The name of the property. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreateThermalExpansionCoefficientPropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) + public static IFCAnyHandle CreateLengthPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) { - IFCAnyHandle propHnd = CreateThermalExpansionCoefficientPropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, valueType); + IFCAnyHandle propHnd = CreateLengthPropertyFromElement(file, elem, revitParameterName, ifcPropertyName, valueType); if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) return propHnd; if (revitBuiltInParam != BuiltInParameter.INVALID) { string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); - propHnd = CreateThermalExpansionCoefficientPropertyFromElement(file, exporterIFC, elem, builtInParamName, ifcPropertyName, valueType); + propHnd = CreateLengthPropertyFromElement(file, elem, builtInParamName, ifcPropertyName, valueType); if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) return propHnd; } @@ -3311,27 +2894,26 @@ public static IList GetBoundedDataFromElement(Element elem, string revi } /// - /// Create a ThermalTransmittance measure property from the element's parameter. + /// Create LinearForce measure property from the element's parameter. /// /// The IFC file. - /// The ExporterIFC. /// The Element. /// The name of the parameter. - /// The built in parameter to use, if revitParameterName isn't found. + /// The built in parameter to use. /// The name of the property. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreateThermalTransmittancePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) + public static IFCAnyHandle CreateLinearForcePropertyFromElement(IFCFile file, Element elem, string revitParameterName, + BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) { - IFCAnyHandle propHnd = CreateThermalTransmittancePropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, valueType); + IFCAnyHandle propHnd = CreateLinearForcePropertyFromElement(file, elem, revitParameterName, ifcPropertyName, valueType); if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) return propHnd; if (revitBuiltInParam != BuiltInParameter.INVALID) { string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); - propHnd = CreateThermalTransmittancePropertyFromElement(file, exporterIFC, elem, builtInParamName, ifcPropertyName, valueType); + propHnd = CreateLinearForcePropertyFromElement(file, elem, builtInParamName, ifcPropertyName, valueType); if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) return propHnd; } @@ -3340,53 +2922,54 @@ public static IList GetBoundedDataFromElement(Element elem, string revi } /// - /// Create a label property from the element's parameter. + /// Create LinearMoment measure property from the element's parameter. /// /// The IFC file. /// The Element. /// The name of the parameter. + /// The built in parameter to use. /// The name of the property. /// The value type of the property. - /// The type of the enum, null if valueType isn't EnumeratedValue. /// The created property handle. - public static IFCAnyHandle CreateLabelPropertyFromElement(IFCFile file, Element elem, string revitParameterName, string ifcPropertyName, - PropertyValueType valueType, Type propertyEnumerationType) + public static IFCAnyHandle CreateLinearMomentPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) { - if (elem == null) - return null; + IFCAnyHandle propHnd = CreateLinearMomentPropertyFromElement(file, elem, revitParameterName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; - string propertyValue; - Parameter parameter = ParameterUtil.GetStringValueFromElement(elem, revitParameterName, out propertyValue); - if (parameter != null) - return CreateLabelPropertyFromCache(file, parameter.Id, ifcPropertyName, propertyValue, valueType, false, propertyEnumerationType); + if (revitBuiltInParam != BuiltInParameter.INVALID) + { + string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); + propHnd = CreateLinearMomentPropertyFromElement(file, elem, builtInParamName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; + } return null; } /// - /// Create a label property from the element's parameter. + /// Create LinearStiffness measure property from the element's parameter. /// /// The IFC file. /// The Element. /// The name of the parameter. - /// The built in parameter. + /// The built in parameter to use. /// The name of the property. /// The value type of the property. - /// The type of the enum, null if valueType isn't EnumeratedValue. /// The created property handle. - public static IFCAnyHandle CreateLabelPropertyFromElement(IFCFile file, Element elem, string revitParameterName, - BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType, Type propertyEnumerationType) + public static IFCAnyHandle CreateLinearStiffnessPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) { - // For Instance - IFCAnyHandle propHnd = CreateLabelPropertyFromElement(file, elem, revitParameterName, ifcPropertyName, valueType, - propertyEnumerationType); + IFCAnyHandle propHnd = CreateLinearStiffnessPropertyFromElement(file, elem, revitParameterName, ifcPropertyName, valueType); if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) return propHnd; if (revitBuiltInParam != BuiltInParameter.INVALID) { string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); - propHnd = CreateLabelPropertyFromElement(file, elem, builtInParamName, ifcPropertyName, valueType, propertyEnumerationType); + propHnd = CreateLinearStiffnessPropertyFromElement(file, elem, builtInParamName, ifcPropertyName, valueType); if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) return propHnd; } @@ -3395,48 +2978,54 @@ public static IList GetBoundedDataFromElement(Element elem, string revi } /// - /// Create an identifier property from the element's parameter. + /// Create LinearVelocity measure property from the element's parameter. /// /// The IFC file. /// The Element. /// The name of the parameter. + /// The built in parameter to use. /// The name of the property. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreateIdentifierPropertyFromElement(IFCFile file, Element elem, string revitParameterName, string ifcPropertyName, PropertyValueType valueType) + public static IFCAnyHandle CreateLinearVelocityPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) { - if (elem == null) - return null; + IFCAnyHandle propHnd = CreateLinearVelocityPropertyFromElement(file, elem, revitParameterName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; - string propertyValue; - if (ParameterUtil.GetStringValueFromElement(elem, revitParameterName, out propertyValue) != null) - return CreateIdentifierPropertyFromCache(file, ifcPropertyName, propertyValue, valueType); + if (revitBuiltInParam != BuiltInParameter.INVALID) + { + string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); + propHnd = CreateLinearVelocityPropertyFromElement(file, elem, builtInParamName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; + } return null; } /// - /// Create an identifier property from the element's parameter. + /// Create LuminousFlux measure property from the element's parameter. /// /// The IFC file. /// The Element. /// The name of the parameter. - /// The built in parameter. + /// The built in parameter to use. /// The name of the property. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreateIdentifierPropertyFromElement(IFCFile file, Element elem, string revitParameterName, - BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) + public static IFCAnyHandle CreateLuminousFluxPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) { - // For Instance - IFCAnyHandle propHnd = CreateIdentifierPropertyFromElement(file, elem, revitParameterName, ifcPropertyName, valueType); + IFCAnyHandle propHnd = CreateLuminousFluxPropertyFromElement(file, elem, revitParameterName, ifcPropertyName, valueType); if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) return propHnd; if (revitBuiltInParam != BuiltInParameter.INVALID) { string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); - propHnd = CreateIdentifierPropertyFromElement(file, elem, builtInParamName, ifcPropertyName, valueType); + propHnd = CreateLuminousFluxPropertyFromElement(file, elem, builtInParamName, ifcPropertyName, valueType); if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) return propHnd; } @@ -3445,899 +3034,754 @@ public static IFCAnyHandle CreateIdentifierPropertyFromElement(IFCFile file, Ele } /// - /// Create a boolean property from the element's parameter. + /// Create LuminousIntensity measure property from the element's parameter. /// /// The IFC file. /// The Element. /// The name of the parameter. - /// The name of the property. Also, the backup name of the parameter. + /// The built in parameter to use. + /// The name of the property. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreateBooleanPropertyFromElement(IFCFile file, Element elem, - string revitParameterName, string ifcPropertyName, PropertyValueType valueType) + public static IFCAnyHandle CreateLuminousIntensityPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) { - int propertyValue; - if (ParameterUtil.GetIntValueFromElement(elem, revitParameterName, out propertyValue) != null) - return CreateBooleanPropertyFromCache(file, ifcPropertyName, propertyValue != 0, valueType); - if (ParameterUtil.GetIntValueFromElement(elem, ifcPropertyName, out propertyValue) != null) - return CreateBooleanPropertyFromCache(file, ifcPropertyName, propertyValue != 0, valueType); + IFCAnyHandle propHnd = CreateLuminousIntensityPropertyFromElement(file, elem, revitParameterName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; + + if (revitBuiltInParam != BuiltInParameter.INVALID) + { + string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); + propHnd = CreateLuminousIntensityPropertyFromElement(file, elem, builtInParamName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; + } return null; } /// - /// Create a logical property from the element's or type's parameter. + /// Create Mass measure property from the element's parameter. /// /// The IFC file. /// The Element. /// The name of the parameter. + /// The built in parameter to use. /// The name of the property. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreateLogicalPropertyFromElement(IFCFile file, Element elem, - string revitParameterName, string ifcPropertyName, PropertyValueType valueType) + public static IFCAnyHandle CreateMassPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) { - IFCLogical ifcLogical = IFCLogical.Unknown; - int propertyValue; - if (ParameterUtil.GetIntValueFromElement(elem, revitParameterName, out propertyValue) != null) - { - ifcLogical = propertyValue != 0 ? IFCLogical.True : IFCLogical.False; - } + IFCAnyHandle propHnd = CreateMassPropertyFromElement(file, elem, revitParameterName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; - return CreateLogicalPropertyFromCache(file, ifcPropertyName, ifcLogical, valueType); + if (revitBuiltInParam != BuiltInParameter.INVALID) + { + string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); + propHnd = CreateMassPropertyFromElement(file, elem, builtInParamName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; + } + + return null; } /// - /// Create an integer property from the element's parameter. + /// Create MassDensity measure property from the element's parameter. /// /// The IFC file. /// The Element. /// The name of the parameter. + /// The built in parameter to use. /// The name of the property. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreateIntegerPropertyFromElement(IFCFile file, Element elem, - string revitParameterName, string ifcPropertyName, PropertyValueType valueType) + public static IFCAnyHandle CreateMassDensityPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) { - int propertyValue; - if (ParameterUtil.GetIntValueFromElement(elem, revitParameterName, out propertyValue) != null) - return CreateIntegerPropertyFromCache(file, ifcPropertyName, propertyValue, valueType); + IFCAnyHandle propHnd = CreateMassDensityPropertyFromElement(file, elem, revitParameterName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; + + if (revitBuiltInParam != BuiltInParameter.INVALID) + { + string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); + propHnd = CreateMassDensityPropertyFromElement(file, elem, builtInParamName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; + } return null; } - /// Create a real property from the element's parameter. + /// + /// Create MassFlowRate measure property from the element's parameter. + /// /// The IFC file. /// The Element. /// The name of the parameter. - /// The name of the property. Also, the backup name of the parameter. + /// The built in parameter to use. + /// The name of the property. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreateRealPropertyFromElement(IFCFile file, Element elem, string revitParameterName, string ifcPropertyName, - PropertyValueType valueType) + public static IFCAnyHandle CreateMassFlowRatePropertyFromElement(IFCFile file, Element elem, string revitParameterName, + BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) { - double propertyValue; - Parameter param = ParameterUtil.GetDoubleValueFromElement(elem, revitParameterName, out propertyValue); - if (param == null) - param = ParameterUtil.GetDoubleValueFromElement(elem, ifcPropertyName, out propertyValue); + IFCAnyHandle propHnd = CreateMassFlowRatePropertyFromElement(file, elem, revitParameterName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; - if (param != null) + if (revitBuiltInParam != BuiltInParameter.INVALID) { - if (valueType == PropertyValueType.BoundedValue) - { - IList boundedData = GetBoundedDataFromElement(elem, revitParameterName, propertyValue, SpecTypeId.Number, "IfcReal"); - return CreateBoundedValuePropertyFromList(file, ifcPropertyName, boundedData, null); - } - else - { - ForgeTypeId paramUnitType = GetParameterUnitType(param); - if (paramUnitType != null) - propertyValue = UnitUtils.ConvertFromInternalUnits(propertyValue, paramUnitType); - - return CreateRealPropertyFromCache(file, ifcPropertyName, propertyValue, valueType); - } + string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); + propHnd = CreateMassFlowRatePropertyFromElement(file, elem, builtInParamName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; } return null; } - /// Create a numeric property from the element's parameter. + /// + /// Create MassPerLength measure property from the element's parameter. + /// /// The IFC file. /// The Element. /// The name of the parameter. - /// The name of the property. Also, the backup name of the parameter. + /// The built in parameter to use. + /// The name of the property. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreateNumericPropertyFromElement(IFCFile file, Element elem, string revitParameterName, string ifcPropertyName, - PropertyValueType valueType) + public static IFCAnyHandle CreateMassPerLengthPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) { - double propertyValue; - if (ParameterUtil.GetDoubleValueFromElement(elem, revitParameterName, out propertyValue) != null) - { - if (valueType == PropertyValueType.BoundedValue) - { - IList boundedData = GetBoundedDataFromElement(elem, revitParameterName, propertyValue, SpecTypeId.Number, "IfcNumericMeasure"); - return CreateBoundedValuePropertyFromList(file, ifcPropertyName, boundedData, null); - } - else - { - return CreateNumericPropertyFromCache(file, ifcPropertyName, propertyValue, valueType); - } - } + IFCAnyHandle propHnd = CreateMassPerLengthPropertyFromElement(file, elem, revitParameterName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; - if (ParameterUtil.GetDoubleValueFromElement(elem, ifcPropertyName, out propertyValue) != null) + if (revitBuiltInParam != BuiltInParameter.INVALID) { - if (valueType == PropertyValueType.BoundedValue) - { - IList boundedData = GetBoundedDataFromElement(elem, ifcPropertyName, propertyValue, SpecTypeId.Number, "IfcNumericMeasure"); - return CreateBoundedValuePropertyFromList(file, ifcPropertyName, boundedData, null); - } - else - { - return CreateNumericPropertyFromCache(file, ifcPropertyName, propertyValue, valueType); - } + string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); + propHnd = CreateMassPerLengthPropertyFromElement(file, elem, builtInParamName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; } return null; } /// - /// Create a length property from the element's or type's parameter. + /// Create ModulusOfElasticity measure property from the element's parameter. /// /// The IFC file. - /// The ExporterIFC. /// The Element. /// The name of the parameter. - /// The name of the built-in parameter, can be null. + /// The built in parameter to use. /// The name of the property. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreateLengthMeasurePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, string builtInParameterName, string ifcPropertyName, PropertyValueType valueType) + public static IFCAnyHandle CreateModulusOfElasticityPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) { - double propertyValue; - - Parameter param = ParameterUtil.GetDoubleValueFromElement(elem, revitParameterName, out propertyValue); - if (param != null) - { - if (!ParameterUtil.ParameterDataTypeIsEqualTo(param, SpecTypeId.Number)) - propertyValue = UnitUtil.ScaleLength(propertyValue); - - if (valueType == PropertyValueType.BoundedValue) - { - IList boundedData = GetBoundedDataFromElement(elem, revitParameterName, propertyValue, SpecTypeId.Length, "IfcLengthMeasure"); - return CreateBoundedValuePropertyFromList(file, ifcPropertyName, boundedData, null); - } - else - { - return CreateLengthMeasurePropertyFromCache(file, ifcPropertyName, propertyValue, valueType); - } - } - + IFCAnyHandle propHnd = CreateModulusOfElasticityPropertyFromElement(file, elem, revitParameterName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; - if (builtInParameterName != null) + if (revitBuiltInParam != BuiltInParameter.INVALID) { - param = ParameterUtil.GetDoubleValueFromElement(elem, builtInParameterName, out propertyValue); - if (param != null) - { - if (!ParameterUtil.ParameterDataTypeIsEqualTo(param, SpecTypeId.Number)) - propertyValue = UnitUtil.ScaleLength(propertyValue); - return CreateLengthMeasurePropertyFromCache(file, ifcPropertyName, propertyValue, valueType); - } + string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); + propHnd = CreateModulusOfElasticityPropertyFromElement(file, elem, builtInParamName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; } return null; } /// - /// Create a positive length property from the element's or type's parameter. + /// Create MoistureDiffusivity measure property from the element's parameter. /// /// The IFC file. - /// The ExporterIFC. /// The Element. /// The name of the parameter. - /// The name of the built-in parameter, can be null. + /// The built in parameter to use. /// The name of the property. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreatePositiveLengthMeasurePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, string builtInParameterName, string ifcPropertyName, PropertyValueType valueType) + public static IFCAnyHandle CreateMoistureDiffusivityPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) { - double propertyValue; - Parameter param = ParameterUtil.GetDoubleValueFromElement(elem, revitParameterName, out propertyValue); - if (param != null) - { - if (!ParameterUtil.ParameterDataTypeIsEqualTo(param, SpecTypeId.Number)) - propertyValue = UnitUtil.ScaleLength(propertyValue); - - if (valueType == PropertyValueType.BoundedValue) - { - IList boundedData = GetBoundedDataFromElement(elem, revitParameterName, propertyValue, SpecTypeId.Length, "IfcPositiveLengthMeasure"); - return CreateBoundedValuePropertyFromList(file, ifcPropertyName, boundedData, null); - } - else - { - return CreatePositiveLengthMeasureProperty(file, ifcPropertyName, propertyValue, valueType); - } - } + IFCAnyHandle propHnd = CreateMoistureDiffusivityPropertyFromElement(file, elem, revitParameterName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; - if (builtInParameterName != null) + if (revitBuiltInParam != BuiltInParameter.INVALID) { - param = ParameterUtil.GetDoubleValueFromElement(elem, builtInParameterName, out propertyValue); - if (param != null) - { - if (!ParameterUtil.ParameterDataTypeIsEqualTo(param, SpecTypeId.Number)) - propertyValue = UnitUtil.ScaleLength(propertyValue); - return CreatePositiveLengthMeasureProperty(file, ifcPropertyName, propertyValue, valueType); - } + string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); + propHnd = CreateMoistureDiffusivityPropertyFromElement(file, elem, builtInParamName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; } return null; } /// - /// Create a length property from the element's parameter. + /// Create MomentOfInertia measure property from the element's parameter. /// /// The IFC file. - /// The ExporterIFC. /// The Element. /// The name of the parameter. - /// The optional built-in parameter. + /// The built in parameter to use. /// The name of the property. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreateLengthMeasurePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) + public static IFCAnyHandle CreateMomentOfInertiaPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) { - string builtInParamName = null; - if (revitBuiltInParam != BuiltInParameter.INVALID) - builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); - - IFCAnyHandle propHnd = CreateLengthMeasurePropertyFromElement(file, exporterIFC, elem, revitParameterName, builtInParamName, ifcPropertyName, valueType); + IFCAnyHandle propHnd = CreateMomentOfInertiaPropertyFromElement(file, elem, revitParameterName, ifcPropertyName, valueType); if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) return propHnd; + if (revitBuiltInParam != BuiltInParameter.INVALID) + { + string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); + propHnd = CreateMomentOfInertiaPropertyFromElement(file, elem, builtInParamName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; + } + return null; } /// - /// Create a positive length property from the element's parameter. + /// Create NormalisedRatio measure property from the element's parameter. /// /// The IFC file. - /// The ExporterIFC. /// The Element. /// The name of the parameter. - /// The optional built-in parameter. + /// The built in parameter to use. /// The name of the property. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreatePositiveLengthMeasurePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) + public static IFCAnyHandle CreateNormalisedRatioPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) { - string builtInParamName = null; - if (revitBuiltInParam != BuiltInParameter.INVALID) - builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); - - IFCAnyHandle propHnd = CreatePositiveLengthMeasurePropertyFromElement(file, exporterIFC, elem, revitParameterName, builtInParamName, ifcPropertyName, valueType); + IFCAnyHandle propHnd = CreateNormalisedRatioPropertyFromElement(file, elem, revitParameterName, ifcPropertyName, valueType); if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) return propHnd; + if (revitBuiltInParam != BuiltInParameter.INVALID) + { + string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); + propHnd = CreateNormalisedRatioPropertyFromElement(file, elem, builtInParamName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; + } + return null; } /// - /// Create a ratio property from the element's parameter. + /// Create Numeric measure property from the element's parameter. /// /// The IFC file. - /// The ExporterIFC. /// The Element. /// The name of the parameter. + /// The built in parameter to use. /// The name of the property. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreateRatioPropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, string ifcPropertyName, PropertyValueType valueType) + public static IFCAnyHandle CreateNumericPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) { - double propertyValue; - if (ParameterUtil.GetDoubleValueFromElement(elem, revitParameterName, out propertyValue) != null) - return CreateRatioMeasureProperty(file, ifcPropertyName, propertyValue, valueType); - - return null; - } + IFCAnyHandle propHnd = CreateNumericPropertyFromElement(file, elem, revitParameterName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; - /// - /// Create a ratio measure data from string value. - /// - /// The value of the property. - /// The created property data. - public static IFCData CreateRatioMeasureDataFromString(string value) - { - double propertyValue; - if (Double.TryParse(value, out propertyValue)) - return IFCDataUtil.CreateRatioMeasureData(propertyValue); + if (revitBuiltInParam != BuiltInParameter.INVALID) + { + string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); + propHnd = CreateNumericPropertyFromElement(file, elem, builtInParamName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; + } return null; } /// - /// Create a normalised ratio property from the element's parameter. + /// Create PlaneAngle measure property from the element's parameter. /// /// The IFC file. - /// The ExporterIFC. /// The Element. /// The name of the parameter. - /// The name of the property. Also, the backup name of the parameter. + /// The built in parameter to use. + /// The name of the property. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreateNormalisedRatioPropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, string ifcPropertyName, PropertyValueType valueType) + public static IFCAnyHandle CreatePlaneAnglePropertyFromElement(IFCFile file, Element elem, string revitParameterName, + BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) { - double propertyValue; - if (ParameterUtil.GetDoubleValueFromElement(elem, revitParameterName, out propertyValue) != null) - return CreateNormalisedRatioMeasureProperty(file, ifcPropertyName, propertyValue, valueType); - if (ParameterUtil.GetDoubleValueFromElement(elem, ifcPropertyName, out propertyValue) != null) - return CreateNormalisedRatioMeasureProperty(file, ifcPropertyName, propertyValue, valueType); - - return null; - } + IFCAnyHandle propHnd = CreatePlaneAnglePropertyFromElement(file, elem, revitParameterName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; - /// - /// Create a normalised ratio measure data from string value. - /// - /// The value of the property. - /// The created property data. - public static IFCData CreateNormalisedRatioMeasureDataFromString(string value) - { - double propertyValue; - if (Double.TryParse(value, out propertyValue)) - return IFCDataUtil.CreateNormalisedRatioMeasureData(propertyValue); + if (revitBuiltInParam != BuiltInParameter.INVALID) + { + string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); + propHnd = CreatePlaneAnglePropertyFromElement(file, elem, builtInParamName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; + } return null; } /// - /// Create a linear velocity property from the element's or type's parameter. + /// Create PlanarForce measure property from the element's parameter. /// /// The IFC file. - /// The ExporterIFC. /// The Element. /// The name of the parameter. - /// The name of the property. Also, the backup name of the parameter. + /// The built in parameter to use. + /// The name of the property. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreateLinearVelocityPropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, string ifcPropertyName, PropertyValueType valueType) + public static IFCAnyHandle CreatePlanarForcePropertyFromElement(IFCFile file, Element elem, string revitParameterName, + BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) { + IFCAnyHandle propHnd = CreatePlanarForcePropertyFromElement(file, elem, revitParameterName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; - IFCAnyHandle linearVelocityHandle = CreateDoublePropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, - "IfcLinearVelocityMeasure", SpecTypeId.HvacVelocity, valueType); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(linearVelocityHandle)) - return linearVelocityHandle; + if (revitBuiltInParam != BuiltInParameter.INVALID) + { + string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); + propHnd = CreatePlanarForcePropertyFromElement(file, elem, builtInParamName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; + } return null; } /// - /// Create a positive ratio property from the element's parameter. + /// Create PositiveLength measure property from the element's parameter. /// /// The IFC file. - /// The ExporterIFC. /// The Element. /// The name of the parameter. - /// The name of the property. Also, the backup name of the parameter. + /// The built in parameter to use. + /// The name of the property. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreatePositiveRatioPropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, string ifcPropertyName, PropertyValueType valueType) + public static IFCAnyHandle CreatePositiveLengthPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) { - double propertyValue; - if (ParameterUtil.GetDoubleValueFromElement(elem, revitParameterName, out propertyValue) != null) - return CreatePositiveRatioMeasureProperty(file, ifcPropertyName, propertyValue, valueType); - if (ParameterUtil.GetDoubleValueFromElement(elem, ifcPropertyName, out propertyValue) != null) - return CreatePositiveRatioMeasureProperty(file, ifcPropertyName, propertyValue, valueType); - - return null; - } + IFCAnyHandle propHnd = CreatePositiveLengthPropertyFromElement(file, elem, revitParameterName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; - /// - /// Create a positive ratio measure data from string value. - /// - /// The value of the property. - /// The created property data. - public static IFCData CreatePositiveRatioMeasureDataFromString(string value) - { - double propertyValue; - if (Double.TryParse(value, out propertyValue)) - return IFCDataUtil.CreatePositiveRatioMeasureData(propertyValue); + if (revitBuiltInParam != BuiltInParameter.INVALID) + { + string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); + propHnd = CreatePositiveLengthPropertyFromElement(file, elem, builtInParamName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; + } return null; } /// - /// Create a plane angle measure property from the element's parameter. + /// Create PositiveRatio measure property from the element's parameter. /// /// The IFC file. /// The Element. /// The name of the parameter. + /// The built in parameter to use. /// The name of the property. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreatePlaneAngleMeasurePropertyFromElement(IFCFile file, Element elem, string revitParameterName, string ifcPropertyName, PropertyValueType valueType) + public static IFCAnyHandle CreatePositiveRatioPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) { - double propertyValue; - Parameter param = ParameterUtil.GetDoubleValueFromElement(elem, revitParameterName, out propertyValue); - if (param != null) - { - if (!ParameterUtil.ParameterDataTypeIsEqualTo(param, SpecTypeId.Number)) - propertyValue = UnitUtil.ScaleAngle(propertyValue); + IFCAnyHandle propHnd = CreatePositiveRatioPropertyFromElement(file, elem, revitParameterName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; - if (valueType == PropertyValueType.BoundedValue) - { - IList boundedData = GetBoundedDataFromElement(elem, revitParameterName, propertyValue, SpecTypeId.Angle, "IfcPlaneAngleMeasure"); - return CreateBoundedValuePropertyFromList(file, ifcPropertyName, boundedData, null); - } - else - { - return CreatePlaneAngleMeasurePropertyFromCache(file, ifcPropertyName, propertyValue, valueType); - } + if (revitBuiltInParam != BuiltInParameter.INVALID) + { + string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); + propHnd = CreatePositiveRatioPropertyFromElement(file, elem, builtInParamName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; } return null; } /// - /// Create an area measure property from the element's parameter. + /// Create PositivePlaneAngle measure property from the element's parameter. /// /// The IFC file. - /// The ExporterIFC. /// The Element. /// The name of the parameter. + /// The built in parameter to use. /// The name of the property. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreateAreaMeasurePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, string ifcPropertyName, PropertyValueType valueType) + public static IFCAnyHandle CreatePositivePlaneAnglePropertyFromElement(IFCFile file, Element elem, string revitParameterName, + BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) { - double propertyValue; - Parameter param = ParameterUtil.GetDoubleValueFromElement(elem, revitParameterName, out propertyValue); - if (param != null) - { - if (!ParameterUtil.ParameterDataTypeIsEqualTo(param, SpecTypeId.Number)) - propertyValue = UnitUtil.ScaleArea(propertyValue); + IFCAnyHandle propHnd = CreatePositivePlaneAnglePropertyFromElement(file, elem, revitParameterName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; - if (valueType == PropertyValueType.BoundedValue) - { - IList boundedData = GetBoundedDataFromElement(elem, revitParameterName, propertyValue, SpecTypeId.Area, "IfcAreaMeasure"); - return CreateBoundedValuePropertyFromList(file, ifcPropertyName, boundedData, null); - } - else - { - return CreateAreaMeasureProperty(file, ifcPropertyName, propertyValue, valueType); - } + if (revitBuiltInParam != BuiltInParameter.INVALID) + { + string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); + propHnd = CreatePositivePlaneAnglePropertyFromElement(file, elem, builtInParamName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; } + return null; } /// - /// Create an Energy measure property from the element's parameter. + /// Create Power measure property from the element's parameter. /// /// The IFC file. - /// The ExporterIFC. /// The Element. /// The name of the parameter. + /// The built in parameter to use. /// The name of the property. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreateEnergyMeasurePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, string ifcPropertyName, PropertyValueType valueType) + public static IFCAnyHandle CreatePowerPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) { - double propertyValue; - Parameter param = ParameterUtil.GetDoubleValueFromElement(elem, revitParameterName, out propertyValue); - if (param != null) - { - if (!ParameterUtil.ParameterDataTypeIsEqualTo(param, SpecTypeId.Number)) - propertyValue = UnitUtil.ScaleEnergy(propertyValue); - - if (valueType == PropertyValueType.BoundedValue) - { - IList boundedData = GetBoundedDataFromElement(elem, revitParameterName, propertyValue, SpecTypeId.Energy, "IfcEnergyMeasure"); - return CreateBoundedValuePropertyFromList(file, ifcPropertyName, boundedData, null); - } - else - { - return CreateEnergyMeasureProperty(file, ifcPropertyName, propertyValue, valueType); - } - } - return null; - } + IFCAnyHandle propHnd = CreatePowerPropertyFromElement(file, elem, revitParameterName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; - /// - /// Create an ThermalResistance measure property from the element's parameter. - /// - /// The IFC file. - /// The ExporterIFC. - /// The Element. - /// The name of the parameter. - /// The name of the property. - /// The value type of the property. - /// The created property handle. - public static IFCAnyHandle CreateThermalResistanceMeasurePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, string ifcPropertyName, PropertyValueType valueType) - { - double propertyValue; - Parameter param = ParameterUtil.GetDoubleValueFromElement(elem, revitParameterName, out propertyValue); - if (param != null) + if (revitBuiltInParam != BuiltInParameter.INVALID) { - if (!ParameterUtil.ParameterDataTypeIsEqualTo(param, SpecTypeId.Number)) - propertyValue = UnitUtil.ScaleThermalResistance(propertyValue); - - if (valueType == PropertyValueType.BoundedValue) - { - IList boundedData = GetBoundedDataFromElement(elem, revitParameterName, propertyValue, SpecTypeId.ThermalResistance, "IfcThermalResistanceMeasure"); - return CreateBoundedValuePropertyFromList(file, ifcPropertyName, boundedData, null); - } - else - { - return CreateThermalResistanceMeasureProperty(file, ifcPropertyName, propertyValue, valueType); - } + string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); + propHnd = CreatePowerPropertyFromElement(file, elem, builtInParamName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; } + return null; } /// - /// Create an MassPerLength measure property from the element's parameter. + /// Create Pressure measure property from the element's parameter. /// /// The IFC file. - /// The ExporterIFC. /// The Element. /// The name of the parameter. + /// The built in parameter to use. /// The name of the property. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreateMassPerLengthMeasurePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, string ifcPropertyName, PropertyValueType valueType) + public static IFCAnyHandle CreatePressurePropertyFromElement(IFCFile file, Element elem, string revitParameterName, + BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) { - double propertyValue; - Parameter param = ParameterUtil.GetDoubleValueFromElement(elem, revitParameterName, out propertyValue); - if (param != null) - { - if (!ParameterUtil.ParameterDataTypeIsEqualTo(param, SpecTypeId.Number)) - propertyValue = UnitUtil.ScaleMassPerLength(propertyValue); + IFCAnyHandle propHnd = CreatePressurePropertyFromElement(file, elem, revitParameterName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; - if (valueType == PropertyValueType.BoundedValue) - { - IList boundedData = GetBoundedDataFromElement(elem, revitParameterName, propertyValue, SpecTypeId.MassPerUnitLength, "IfcMassPerLengthMeasure"); - return CreateBoundedValuePropertyFromList(file, ifcPropertyName, boundedData, null); - } - else - { - return CreateMassPerLengthMeasureProperty(file, ifcPropertyName, propertyValue, valueType); - } + if (revitBuiltInParam != BuiltInParameter.INVALID) + { + string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); + propHnd = CreatePressurePropertyFromElement(file, elem, builtInParamName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; } + return null; } /// - /// Create an Acceleration measure property from the element's parameter. + /// Create Ratio measure property from the element's parameter. /// /// The IFC file. - /// The ExporterIFC. /// The Element. /// The name of the parameter. + /// The built in parameter to use. /// The name of the property. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreateAccelerationMeasurePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, string ifcPropertyName, PropertyValueType valueType) + public static IFCAnyHandle CreateRatioPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) { - double propertyValue; - Parameter param = ParameterUtil.GetDoubleValueFromElement(elem, revitParameterName, out propertyValue); - if (param != null) - { - if (!ParameterUtil.ParameterDataTypeIsEqualTo(param, SpecTypeId.Number)) - propertyValue = UnitUtil.ScaleAcceleration(propertyValue); + IFCAnyHandle propHnd = CreateRatioPropertyFromElement(file, elem, revitParameterName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; - if (valueType == PropertyValueType.BoundedValue) - { - IList boundedData = GetBoundedDataFromElement(elem, revitParameterName, propertyValue, SpecTypeId.Acceleration, "IfcAccelerationMeasure"); - return CreateBoundedValuePropertyFromList(file, ifcPropertyName, boundedData, null); - } - else - { - return CreateAccelerationMeasureProperty(file, ifcPropertyName, propertyValue, valueType); - } + if (revitBuiltInParam != BuiltInParameter.INVALID) + { + string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); + propHnd = CreateRatioPropertyFromElement(file, elem, builtInParamName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; } + return null; } - /// - /// Create an LinearMoment measure property from the element's parameter. + /// Create Real measure property from the element's parameter. /// /// The IFC file. - /// The ExporterIFC. /// The Element. /// The name of the parameter. + /// The built in parameter to use. /// The name of the property. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreateLinearMomentMeasurePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, string ifcPropertyName, PropertyValueType valueType) + public static IFCAnyHandle CreateRealPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) { - double propertyValue; - Parameter param = ParameterUtil.GetDoubleValueFromElement(elem, revitParameterName, out propertyValue); - if (param != null) - { - if (!ParameterUtil.ParameterDataTypeIsEqualTo(param, SpecTypeId.Number)) - propertyValue = UnitUtil.ScaleLinearMoment(propertyValue); + IFCAnyHandle propHnd = CreateRealPropertyFromElement(file, elem, revitParameterName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; - if (valueType == PropertyValueType.BoundedValue) - { - IList boundedData = GetBoundedDataFromElement(elem, revitParameterName, propertyValue, SpecTypeId.LinearMoment, "IfcLinearMomentMeasure"); - return CreateBoundedValuePropertyFromList(file, ifcPropertyName, boundedData, null); - } - else - { - return CreateLinearMomentMeasureProperty(file, ifcPropertyName, propertyValue, valueType); - } + if (revitBuiltInParam != BuiltInParameter.INVALID) + { + string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); + propHnd = CreateRealPropertyFromElement(file, elem, builtInParamName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; } + return null; } /// - /// Create an Torque measure property from the element's parameter. + /// Create RotationalFrequency measure property from the element's parameter. /// /// The IFC file. - /// The ExporterIFC. /// The Element. /// The name of the parameter. + /// The built in parameter to use. /// The name of the property. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreateTorqueMeasurePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, string ifcPropertyName, PropertyValueType valueType) + public static IFCAnyHandle CreateRotationalFrequencyPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) { - double propertyValue; - Parameter param = ParameterUtil.GetDoubleValueFromElement(elem, revitParameterName, out propertyValue); - if (param != null) - { - if (!ParameterUtil.ParameterDataTypeIsEqualTo(param, SpecTypeId.Number)) - propertyValue = UnitUtil.ScaleTorque(propertyValue); + IFCAnyHandle propHnd = CreateRotationalFrequencyPropertyFromElement(file, elem, revitParameterName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; - if (valueType == PropertyValueType.BoundedValue) - { - IList boundedData = GetBoundedDataFromElement(elem, revitParameterName, propertyValue, SpecTypeId.Moment, "IfcTorqueMeasure"); - return CreateBoundedValuePropertyFromList(file, ifcPropertyName, boundedData, null); - } - else - { - return CreateTorqueMeasureProperty(file, ifcPropertyName, propertyValue, valueType); - } + if (revitBuiltInParam != BuiltInParameter.INVALID) + { + string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); + propHnd = CreateRotationalFrequencyPropertyFromElement(file, elem, builtInParamName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; } + return null; } /// - /// Create an LinearStiffness measure property from the element's parameter. + /// Create SoundPower measure property from the element's parameter. /// /// The IFC file. - /// The ExporterIFC. /// The Element. /// The name of the parameter. + /// The built in parameter to use. /// The name of the property. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreateLinearStiffnessMeasurePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, string ifcPropertyName, PropertyValueType valueType) + public static IFCAnyHandle CreateSoundPowerPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) { - double propertyValue; - Parameter param = ParameterUtil.GetDoubleValueFromElement(elem, revitParameterName, out propertyValue); - if (param != null) - { - if (!ParameterUtil.ParameterDataTypeIsEqualTo(param, SpecTypeId.Number)) - propertyValue = UnitUtil.ScaleLinearStiffness(propertyValue); + IFCAnyHandle propHnd = CreateSoundPowerPropertyFromElement(file, elem, revitParameterName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; - if (valueType == PropertyValueType.BoundedValue) - { - IList boundedData = GetBoundedDataFromElement(elem, revitParameterName, propertyValue, SpecTypeId.PointSpringCoefficient, "IfcLinearStiffnessMeasure"); - return CreateBoundedValuePropertyFromList(file, ifcPropertyName, boundedData, null); - } - else - { - return CreateLinearStiffnessMeasureProperty(file, ifcPropertyName, propertyValue, valueType); - } + if (revitBuiltInParam != BuiltInParameter.INVALID) + { + string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); + propHnd = CreateSoundPowerPropertyFromElement(file, elem, builtInParamName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; } + return null; } /// - /// Create an AngularVelocity measure property from the element's parameter. + /// Create SoundPressure measure property from the element's parameter. /// /// The IFC file. - /// The ExporterIFC. /// The Element. /// The name of the parameter. + /// The built in parameter to use. /// The name of the property. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreateAngularVelocityMeasurePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, string ifcPropertyName, PropertyValueType valueType) + public static IFCAnyHandle CreateSoundPressurePropertyFromElement(IFCFile file, Element elem, string revitParameterName, + BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) { - double propertyValue; - Parameter param = ParameterUtil.GetDoubleValueFromElement(elem, revitParameterName, out propertyValue); - if (param != null) - { - if (!ParameterUtil.ParameterDataTypeIsEqualTo(param, SpecTypeId.Number)) - propertyValue = UnitUtil.ScaleAngularVelocity(propertyValue); + IFCAnyHandle propHnd = CreateSoundPressurePropertyFromElement(file, elem, revitParameterName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; - if (valueType == PropertyValueType.BoundedValue) - { - IList boundedData = GetBoundedDataFromElement(elem, revitParameterName, propertyValue, SpecTypeId.Pulsation, "IfcAngularVelocityMeasure"); - return CreateBoundedValuePropertyFromList(file, ifcPropertyName, boundedData, null); - } - else - { - return CreateAngularVelocityMeasureProperty(file, ifcPropertyName, propertyValue, valueType); - } + if (revitBuiltInParam != BuiltInParameter.INVALID) + { + string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); + propHnd = CreateSoundPressurePropertyFromElement(file, elem, builtInParamName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; } + return null; } /// - /// Create an WarpingConstant measure property from the element's parameter. + /// Create SpecificHeatCapacity measure property from the element's parameter. /// /// The IFC file. - /// The ExporterIFC. /// The Element. /// The name of the parameter. + /// The built in parameter to use. /// The name of the property. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreateWarpingConstantMeasurePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, string ifcPropertyName, PropertyValueType valueType) + public static IFCAnyHandle CreateSpecificHeatCapacityPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) { - double propertyValue; - Parameter param = ParameterUtil.GetDoubleValueFromElement(elem, revitParameterName, out propertyValue); - if (param != null) - { - if (!ParameterUtil.ParameterDataTypeIsEqualTo(param, SpecTypeId.Number)) - propertyValue = UnitUtil.ScaleWarpingConstant(propertyValue); + IFCAnyHandle propHnd = CreateSpecificHeatCapacityPropertyFromElement(file, elem, revitParameterName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; - if (valueType == PropertyValueType.BoundedValue) - { - IList boundedData = GetBoundedDataFromElement(elem, revitParameterName, propertyValue, SpecTypeId.WarpingConstant, "IfcWarpingConstantMeasure"); - return CreateBoundedValuePropertyFromList(file, ifcPropertyName, boundedData, null); - } - else - { - return CreateWarpingConstantMeasureProperty(file, ifcPropertyName, propertyValue, valueType); - } + if (revitBuiltInParam != BuiltInParameter.INVALID) + { + string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); + propHnd = CreateSpecificHeatCapacityPropertyFromElement(file, elem, builtInParamName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; } + return null; } /// - /// Create an vapor permeability measure property from the element's parameter. + /// Create ThermalConductivity measure property from the element's parameter. /// /// The IFC file. - /// The ExporterIFC. /// The Element. /// The name of the parameter. + /// The built in parameter to use. /// The name of the property. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreateVaporPermeabilityMeasurePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, string ifcPropertyName, PropertyValueType valueType) + public static IFCAnyHandle CreateThermalConductivityPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) { - double propertyValue; - Parameter param = ParameterUtil.GetDoubleValueFromElement(elem, revitParameterName, out propertyValue); - if (param != null) - { - if (!ParameterUtil.ParameterDataTypeIsEqualTo(param, SpecTypeId.Number)) - propertyValue = UnitUtil.ScaleVaporPermeability(propertyValue); + IFCAnyHandle propHnd = CreateThermalConductivityPropertyFromElement(file, elem, revitParameterName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; - if (valueType == PropertyValueType.BoundedValue) - { - IList boundedData = GetBoundedDataFromElement(elem, revitParameterName, propertyValue, SpecTypeId.Permeability, "IfcVaporPermeabilityMeasure"); - return CreateBoundedValuePropertyFromList(file, ifcPropertyName, boundedData, null); - } - else - { - return CreateVaporPermeabilityMeasureProperty(file, ifcPropertyName, propertyValue, valueType); - } + if (revitBuiltInParam != BuiltInParameter.INVALID) + { + string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); + propHnd = CreateThermalConductivityPropertyFromElement(file, elem, builtInParamName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; } + return null; } /// - /// Create an volume measure property from the element's parameter. + /// Create ThermalExpansionCoefficient measure property from the element's parameter. /// /// The IFC file. - /// The ExporterIFC. /// The Element. /// The name of the parameter. + /// The built in parameter to use. /// The name of the property. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreateVolumeMeasurePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, string ifcPropertyName, PropertyValueType valueType) + public static IFCAnyHandle CreateThermalExpansionCoefficientPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) { - double propertyValue; - Parameter param = ParameterUtil.GetDoubleValueFromElement(elem, revitParameterName, out propertyValue); - if (param != null) - { - if (!ParameterUtil.ParameterDataTypeIsEqualTo(param, SpecTypeId.Number)) - propertyValue = UnitUtil.ScaleVolume(propertyValue); + IFCAnyHandle propHnd = CreateThermalExpansionCoefficientPropertyFromElement(file, elem, revitParameterName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; - if (valueType == PropertyValueType.BoundedValue) - { - IList boundedData = GetBoundedDataFromElement(elem, revitParameterName, propertyValue, SpecTypeId.Volume, "IfcVolumeMeasure"); - return CreateBoundedValuePropertyFromList(file, ifcPropertyName, boundedData, null); - } - else - { - return CreateVolumeMeasureProperty(file, ifcPropertyName, propertyValue, valueType); - } + if (revitBuiltInParam != BuiltInParameter.INVALID) + { + string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); + propHnd = CreateThermalExpansionCoefficientPropertyFromElement(file, elem, builtInParamName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; } + return null; } /// - /// Create a count measure property from the element's parameter. + /// Create ThermalResistance measure property from the element's parameter. /// /// The IFC file. - /// The ExporterIFC. /// The Element. /// The name of the parameter. + /// The built in parameter to use. /// The name of the property. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreateCountMeasurePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, string ifcPropertyName, PropertyValueType valueType) + public static IFCAnyHandle CreateThermalResistancePropertyFromElement(IFCFile file, Element elem, string revitParameterName, + BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) { - int propertyValue; - double propertyValueReal; - if (ParameterUtil.GetDoubleValueFromElement(elem, revitParameterName, out propertyValueReal) != null) + IFCAnyHandle propHnd = CreateThermalResistancePropertyFromElement(file, elem, revitParameterName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; + + if (revitBuiltInParam != BuiltInParameter.INVALID) { - if (ExporterCacheManager.ExportOptionsCache.ExportAsOlderThanIFC4x3) - { - return CreateCountMeasureProperty(file, ifcPropertyName, propertyValueReal, valueType); - } - else if (MathUtil.IsAlmostInteger(propertyValueReal)) - { - propertyValue = (int)Math.Floor(propertyValueReal); - return CreateCountMeasureProperty(file, ifcPropertyName, propertyValue, valueType); - } + string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); + propHnd = CreateThermalResistancePropertyFromElement(file, elem, builtInParamName, ifcPropertyName, valueType); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) + return propHnd; } - if (ParameterUtil.GetIntValueFromElement(elem, revitParameterName, out propertyValue) != null) - return CreateCountMeasureProperty(file, ifcPropertyName, propertyValue, valueType); - return null; } /// - /// Create an area measure property from the element's parameter. + /// Create ThermalTransmittance measure property from the element's parameter. /// /// The IFC file. - /// The ExporterIFC. /// The Element. /// The name of the parameter. - /// The built in parameter to use, if revitParameterName isn't found. + /// The built in parameter to use. /// The name of the property. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreateAreaMeasurePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) + public static IFCAnyHandle CreateThermalTransmittancePropertyFromElement(IFCFile file, Element elem, string revitParameterName, + BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) { - IFCAnyHandle propHnd = CreateAreaMeasurePropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, valueType); + IFCAnyHandle propHnd = CreateThermalTransmittancePropertyFromElement(file, elem, revitParameterName, ifcPropertyName, valueType); if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) return propHnd; if (revitBuiltInParam != BuiltInParameter.INVALID) { string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); - propHnd = CreateAreaMeasurePropertyFromElement(file, exporterIFC, elem, builtInParamName, ifcPropertyName, valueType); + propHnd = CreateThermalTransmittancePropertyFromElement(file, elem, builtInParamName, ifcPropertyName, valueType); if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) return propHnd; } @@ -4346,27 +3790,26 @@ public static IFCAnyHandle CreatePlaneAngleMeasurePropertyFromElement(IFCFile fi } /// - /// Create an Energy measure property from the element's parameter. + /// Create ThermodynamicTemperature measure property from the element's parameter. /// /// The IFC file. - /// The ExporterIFC. /// The Element. /// The name of the parameter. - /// The built in parameter to use, if revitParameterName isn't found. + /// The built in parameter to use. /// The name of the property. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreateEnergyMeasurePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) + public static IFCAnyHandle CreateThermodynamicTemperaturePropertyFromElement(IFCFile file, Element elem, string revitParameterName, + BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) { - IFCAnyHandle propHnd = CreateEnergyMeasurePropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, valueType); + IFCAnyHandle propHnd = CreateThermodynamicTemperaturePropertyFromElement(file, elem, revitParameterName, ifcPropertyName, valueType); if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) return propHnd; if (revitBuiltInParam != BuiltInParameter.INVALID) { string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); - propHnd = CreateEnergyMeasurePropertyFromElement(file, exporterIFC, elem, builtInParamName, ifcPropertyName, valueType); + propHnd = CreateThermodynamicTemperaturePropertyFromElement(file, elem, builtInParamName, ifcPropertyName, valueType); if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) return propHnd; } @@ -4375,27 +3818,26 @@ public static IFCAnyHandle CreatePlaneAngleMeasurePropertyFromElement(IFCFile fi } /// - /// Create an ThermalResistance measure property from the element's parameter. + /// Create VaporPermeability measure property from the element's parameter. /// /// The IFC file. - /// The ExporterIFC. /// The Element. /// The name of the parameter. - /// The built in parameter to use, if revitParameterName isn't found. + /// The built in parameter to use. /// The name of the property. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreateThermalResistanceMeasurePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) + public static IFCAnyHandle CreateVaporPermeabilityPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) { - IFCAnyHandle propHnd = CreateThermalResistanceMeasurePropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, valueType); + IFCAnyHandle propHnd = CreateVaporPermeabilityPropertyFromElement(file, elem, revitParameterName, ifcPropertyName, valueType); if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) return propHnd; if (revitBuiltInParam != BuiltInParameter.INVALID) { string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); - propHnd = CreateThermalResistanceMeasurePropertyFromElement(file, exporterIFC, elem, builtInParamName, ifcPropertyName, valueType); + propHnd = CreateVaporPermeabilityPropertyFromElement(file, elem, builtInParamName, ifcPropertyName, valueType); if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) return propHnd; } @@ -4404,27 +3846,26 @@ public static IFCAnyHandle CreatePlaneAngleMeasurePropertyFromElement(IFCFile fi } /// - /// Create an MassPerLength measure property from the element's parameter. + /// Create Volume measure property from the element's parameter. /// /// The IFC file. - /// The ExporterIFC. /// The Element. /// The name of the parameter. - /// The built in parameter to use, if revitParameterName isn't found. + /// The built in parameter to use. /// The name of the property. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreateMassPerLengthMeasurePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) + public static IFCAnyHandle CreateVolumePropertyFromElement(IFCFile file, Element elem, string revitParameterName, + BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) { - IFCAnyHandle propHnd = CreateMassPerLengthMeasurePropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, valueType); + IFCAnyHandle propHnd = CreateVolumePropertyFromElement(file, elem, revitParameterName, ifcPropertyName, valueType); if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) return propHnd; if (revitBuiltInParam != BuiltInParameter.INVALID) { string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); - propHnd = CreateMassPerLengthMeasurePropertyFromElement(file, exporterIFC, elem, builtInParamName, ifcPropertyName, valueType); + propHnd = CreateVolumePropertyFromElement(file, elem, builtInParamName, ifcPropertyName, valueType); if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) return propHnd; } @@ -4433,27 +3874,26 @@ public static IFCAnyHandle CreatePlaneAngleMeasurePropertyFromElement(IFCFile fi } /// - /// Create an Acceleration measure property from the element's parameter. + /// Create VolumetricFlowRate measure property from the element's parameter. /// /// The IFC file. - /// The ExporterIFC. /// The Element. /// The name of the parameter. - /// The built in parameter to use, if revitParameterName isn't found. + /// The built in parameter to use. /// The name of the property. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreateAccelerationMeasurePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) + public static IFCAnyHandle CreateVolumetricFlowRatePropertyFromElement(IFCFile file, Element elem, string revitParameterName, + BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) { - IFCAnyHandle propHnd = CreateAccelerationMeasurePropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, valueType); + IFCAnyHandle propHnd = CreateVolumetricFlowRatePropertyFromElement(file, elem, revitParameterName, ifcPropertyName, valueType); if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) return propHnd; if (revitBuiltInParam != BuiltInParameter.INVALID) { string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); - propHnd = CreateAccelerationMeasurePropertyFromElement(file, exporterIFC, elem, builtInParamName, ifcPropertyName, valueType); + propHnd = CreateVolumetricFlowRatePropertyFromElement(file, elem, builtInParamName, ifcPropertyName, valueType); if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) return propHnd; } @@ -4462,27 +3902,26 @@ public static IFCAnyHandle CreatePlaneAngleMeasurePropertyFromElement(IFCFile fi } /// - /// Create an LinearMoment measure property from the element's parameter. + /// Create Torque measure property from the element's parameter. /// /// The IFC file. - /// The ExporterIFC. /// The Element. /// The name of the parameter. - /// The built in parameter to use, if revitParameterName isn't found. + /// The built in parameter to use. /// The name of the property. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreateLinearMomentMeasurePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) + public static IFCAnyHandle CreateTorquePropertyFromElement(IFCFile file, Element elem, string revitParameterName, + BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) { - IFCAnyHandle propHnd = CreateLinearMomentMeasurePropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, valueType); + IFCAnyHandle propHnd = CreateTorquePropertyFromElement(file, elem, revitParameterName, ifcPropertyName, valueType); if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) return propHnd; if (revitBuiltInParam != BuiltInParameter.INVALID) { string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); - propHnd = CreateLinearMomentMeasurePropertyFromElement(file, exporterIFC, elem, builtInParamName, ifcPropertyName, valueType); + propHnd = CreateTorquePropertyFromElement(file, elem, builtInParamName, ifcPropertyName, valueType); if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) return propHnd; } @@ -4491,1285 +3930,2706 @@ public static IFCAnyHandle CreatePlaneAngleMeasurePropertyFromElement(IFCFile fi } /// - /// Create an Torque measure property from the element's parameter. + /// Create WarpingConstant measure property from the element's parameter. /// /// The IFC file. - /// The ExporterIFC. /// The Element. /// The name of the parameter. - /// The built in parameter to use, if revitParameterName isn't found. + /// The built in parameter to use. /// The name of the property. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreateTorqueMeasurePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) + public static IFCAnyHandle CreateWarpingConstantPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) { - IFCAnyHandle propHnd = CreateTorqueMeasurePropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, valueType); + IFCAnyHandle propHnd = CreateWarpingConstantPropertyFromElement(file, elem, revitParameterName, ifcPropertyName, valueType); if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) return propHnd; if (revitBuiltInParam != BuiltInParameter.INVALID) { string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); - propHnd = CreateTorqueMeasurePropertyFromElement(file, exporterIFC, elem, builtInParamName, ifcPropertyName, valueType); + propHnd = CreateWarpingConstantPropertyFromElement(file, elem, builtInParamName, ifcPropertyName, valueType); if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) return propHnd; } return null; } + #endregion + + #region Create___PropertyFromElement_2 /// - /// Create an LinearStiffness measure property from the element's parameter. + /// Create a Area measure property from the element's parameter. /// /// The IFC file. - /// The ExporterIFC. /// The Element. /// The name of the parameter. - /// The built in parameter to use, if revitParameterName isn't found. - /// The name of the property. + /// The name of the property. Also, the backup name of the parameter. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreateLinearStiffnessMeasurePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) + public static IFCAnyHandle CreateAreaPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + string ifcPropertyName, PropertyValueType valueType) { - IFCAnyHandle propHnd = CreateLinearStiffnessMeasurePropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, valueType); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) - return propHnd; + IList doubleValues = GetDoubleValuesFromParameterByType(elem, revitParameterName, SpecTypeId.Area, valueType); + IFCAnyHandle property = CreateAreaPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); - if (revitBuiltInParam != BuiltInParameter.INVALID) + if (property == null) { - string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); - propHnd = CreateLinearStiffnessMeasurePropertyFromElement(file, exporterIFC, elem, builtInParamName, ifcPropertyName, valueType); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) - return propHnd; + doubleValues = GetDoubleValuesFromParameterByType(elem, ifcPropertyName, SpecTypeId.Area, valueType); + property = CreateAreaPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); } - return null; + return property; } /// - /// Create an AngularVelocity measure property from the element's parameter. + /// Create a Acceleration measure property from the element's parameter. /// /// The IFC file. - /// The ExporterIFC. /// The Element. /// The name of the parameter. - /// The built in parameter to use, if revitParameterName isn't found. - /// The name of the property. + /// The name of the property. Also, the backup name of the parameter. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreateAngularVelocityMeasurePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) + public static IFCAnyHandle CreateAccelerationPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + string ifcPropertyName, PropertyValueType valueType) { - IFCAnyHandle propHnd = CreateAngularVelocityMeasurePropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, valueType); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) - return propHnd; + IList doubleValues = GetDoubleValuesFromParameterByType(elem, revitParameterName, SpecTypeId.Acceleration, valueType); + IFCAnyHandle property = CreateAccelerationPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); - if (revitBuiltInParam != BuiltInParameter.INVALID) + if (property == null) { - string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); - propHnd = CreateAngularVelocityMeasurePropertyFromElement(file, exporterIFC, elem, builtInParamName, ifcPropertyName, valueType); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) - return propHnd; + doubleValues = GetDoubleValuesFromParameterByType(elem, ifcPropertyName, SpecTypeId.Acceleration, valueType); + property = CreateAccelerationPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); } - return null; + return property; } /// - /// Create an WarpingConstant measure property from the element's parameter. + /// Create a AngularVelocity measure property from the element's parameter. /// /// The IFC file. - /// The ExporterIFC. /// The Element. /// The name of the parameter. - /// The built in parameter to use, if revitParameterName isn't found. - /// The name of the property. + /// The name of the property. Also, the backup name of the parameter. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreateWarpingConstantMeasurePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) + public static IFCAnyHandle CreateAngularVelocityPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + string ifcPropertyName, PropertyValueType valueType) { - IFCAnyHandle propHnd = CreateWarpingConstantMeasurePropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, valueType); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) - return propHnd; + IList doubleValues = GetDoubleValuesFromParameterByType(elem, revitParameterName, SpecTypeId.Pulsation, valueType); + IFCAnyHandle property = CreateAngularVelocityPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); - if (revitBuiltInParam != BuiltInParameter.INVALID) + if (property == null) { - string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); - propHnd = CreateWarpingConstantMeasurePropertyFromElement(file, exporterIFC, elem, builtInParamName, ifcPropertyName, valueType); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) - return propHnd; + doubleValues = GetDoubleValuesFromParameterByType(elem, ifcPropertyName, SpecTypeId.Pulsation, valueType); + property = CreateAngularVelocityPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); } - return null; + return property; } /// - /// Create an vapor permeability measure property from the element's parameter. + /// Create a AreaDensity measure property from the element's parameter. /// /// The IFC file. - /// The ExporterIFC. /// The Element. /// The name of the parameter. - /// The built in parameter to use, if revitParameterName isn't found. - /// The name of the property. + /// The name of the property. Also, the backup name of the parameter. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreateVaporPermeabilityMeasurePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) + public static IFCAnyHandle CreateAreaDensityPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + string ifcPropertyName, PropertyValueType valueType) { - IFCAnyHandle propHnd = CreateVaporPermeabilityMeasurePropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, valueType); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) - return propHnd; + IList doubleValues = GetDoubleValuesFromParameterByType(elem, revitParameterName, SpecTypeId.MassPerUnitArea, valueType); + IFCAnyHandle property = CreateAreaDensityPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); - if (revitBuiltInParam != BuiltInParameter.INVALID) + if (property == null) { - string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); - propHnd = CreateVaporPermeabilityMeasurePropertyFromElement(file, exporterIFC, elem, builtInParamName, ifcPropertyName, valueType); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) - return propHnd; + doubleValues = GetDoubleValuesFromParameterByType(elem, ifcPropertyName, SpecTypeId.MassPerUnitArea, valueType); + property = CreateAreaDensityPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); } - return null; + return property; } /// - /// Create an volume measure property from the element's parameter. + /// Create a DynamicViscosity measure property from the element's parameter. /// /// The IFC file. - /// The ExporterIFC. /// The Element. /// The name of the parameter. - /// The built in parameter to use, if revitParameterName isn't found. - /// The name of the property. + /// The name of the property. Also, the backup name of the parameter. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreateVolumeMeasurePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) + public static IFCAnyHandle CreateDynamicViscosityPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + string ifcPropertyName, PropertyValueType valueType) { - IFCAnyHandle propHnd = CreateVolumeMeasurePropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, valueType); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) - return propHnd; + IList doubleValues = GetDoubleValuesFromParameterByType(elem, revitParameterName, SpecTypeId.HvacViscosity, valueType); + IFCAnyHandle property = CreateDynamicViscosityPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); - if (revitBuiltInParam != BuiltInParameter.INVALID) + if (property == null) { - string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); - propHnd = CreateVolumeMeasurePropertyFromElement(file, exporterIFC, elem, builtInParamName, ifcPropertyName, valueType); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) - return propHnd; + doubleValues = GetDoubleValuesFromParameterByType(elem, ifcPropertyName, SpecTypeId.HvacViscosity, valueType); + property = CreateDynamicViscosityPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); } - return null; + return property; } /// - /// Create a count measure property from the element's parameter. + /// Create a ElectricCurrent measure property from the element's parameter. /// /// The IFC file. - /// The ExporterIFC. /// The Element. /// The name of the parameter. - /// The built in parameter to use, if revitParameterName isn't found. - /// The name of the property. + /// The name of the property. Also, the backup name of the parameter. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreateCountMeasurePropertyFromElement(IFCFile file, ExporterIFC exporterIFC, Element elem, - string revitParameterName, BuiltInParameter revitBuiltInParam, string ifcPropertyName, PropertyValueType valueType) + public static IFCAnyHandle CreateElectricCurrentPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + string ifcPropertyName, PropertyValueType valueType) { - IFCAnyHandle propHnd = CreateCountMeasurePropertyFromElement(file, exporterIFC, elem, revitParameterName, ifcPropertyName, valueType); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) - return propHnd; + IList doubleValues = GetDoubleValuesFromParameterByType(elem, revitParameterName, SpecTypeId.Current, valueType); + IFCAnyHandle property = CreateElectricCurrentPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); - if (revitBuiltInParam != BuiltInParameter.INVALID) + if (property == null) { - string builtInParamName = LabelUtils.GetLabelFor(revitBuiltInParam); - propHnd = CreateCountMeasurePropertyFromElement(file, exporterIFC, elem, builtInParamName, ifcPropertyName, valueType); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propHnd)) - return propHnd; + doubleValues = GetDoubleValuesFromParameterByType(elem, ifcPropertyName, SpecTypeId.Current, valueType); + property = CreateElectricCurrentPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); } - return null; + return property; } /// - /// Creates the shared beam and column QTO values. + /// Create a ElectricVoltage measure property from the element's parameter. /// - /// - /// This code uses the native implementation for creating these quantities, and the native class for storing the information. - /// This will be obsoleted. - /// - /// The exporter. - /// The element handle. - /// The beam or column element. - /// The FamilyTypeInfo containing the appropriate data. - /// The list of geometries for the exported column only, used if split walls and columns is set. - /// The geomObjects is used if we have the split by level option. It is intended only for columns, as beams and members are not split by level. - /// In this case, we use the solids in the list to determine the real volume of the exported objects. If the list contains meshes, we won't export the volume at all. - public static void CreateBeamColumnBaseQuantities(ExporterIFC exporterIFC, IFCAnyHandle elemHandle, Element element, FamilyTypeInfo typeInfo, IList geomObjects) + /// The IFC file. + /// The Element. + /// The name of the parameter. + /// The name of the property. Also, the backup name of the parameter. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateElectricVoltagePropertyFromElement(IFCFile file, Element elem, string revitParameterName, + string ifcPropertyName, PropertyValueType valueType) { - // Make sure QTO export is enabled. - if (!ExporterCacheManager.ExportOptionsCache.ExportBaseQuantities || (ExporterCacheManager.ExportOptionsCache.ExportAsCOBIE)) - return; - - IFCFile file = exporterIFC.GetFile(); - HashSet quantityHnds = new HashSet(); - double scaledLength = typeInfo.extraParams.ScaledLength; - //According to investigation of current code the passed in typeInfo contains grossArea - double scaledGrossArea = typeInfo.extraParams.ScaledArea; - double crossSectionArea = scaledGrossArea; - double scaledOuterPerimeter = typeInfo.extraParams.ScaledOuterPerimeter; - double scaledInnerPerimeter = typeInfo.extraParams.ScaledInnerPerimeter; - double outSurfaceArea = 0.0; + IList doubleValues = GetDoubleValuesFromParameterByType(elem, revitParameterName, SpecTypeId.ElectricalPotential, valueType); + IFCAnyHandle property = CreateElectricVoltagePropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); - if (scaledLength > MathUtil.Eps()) + if (property == null) { - IFCAnyHandle quantityHnd = IFCInstanceExporter.CreateQuantityLength(file, "Length", null, null, scaledLength); - quantityHnds.Add(quantityHnd); + doubleValues = GetDoubleValuesFromParameterByType(elem, ifcPropertyName, SpecTypeId.ElectricalPotential, valueType); + property = CreateElectricVoltagePropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); } - if (MathUtil.AreaIsAlmostZero(crossSectionArea)) - { - if (element != null) - { - ParameterUtil.GetDoubleValueFromElement(element, BuiltInParameter.HOST_AREA_COMPUTED, out crossSectionArea); - crossSectionArea = UnitUtil.ScaleArea(crossSectionArea); - } - } + return property; + } - if (!MathUtil.AreaIsAlmostZero(crossSectionArea)) + /// + /// Create a Energy measure property from the element's parameter. + /// + /// The IFC file. + /// The Element. + /// The name of the parameter. + /// The name of the property. Also, the backup name of the parameter. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateEnergyPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + string ifcPropertyName, PropertyValueType valueType) + { + IList doubleValues = GetDoubleValuesFromParameterByType(elem, revitParameterName, SpecTypeId.Energy, valueType); + IFCAnyHandle property = CreateEnergyPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); + + if (property == null) { - IFCAnyHandle quantityHnd = IFCInstanceExporter.CreateQuantityArea(file, "CrossSectionArea", null, null, crossSectionArea); - quantityHnds.Add(quantityHnd); + doubleValues = GetDoubleValuesFromParameterByType(elem, ifcPropertyName, SpecTypeId.Energy, valueType); + property = CreateEnergyPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); } - if (!MathUtil.AreaIsAlmostZero(scaledGrossArea) && !MathUtil.IsAlmostZero(scaledLength) && !MathUtil.IsAlmostZero(scaledOuterPerimeter)) + return property; + } + + /// + /// Create a Force measure property from the element's parameter. + /// + /// The IFC file. + /// The Element. + /// The name of the parameter. + /// The name of the property. Also, the backup name of the parameter. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateForcePropertyFromElement(IFCFile file, Element elem, string revitParameterName, + string ifcPropertyName, PropertyValueType valueType) + { + IList doubleValues = GetDoubleValuesFromParameterByType(elem, revitParameterName, SpecTypeId.Force, valueType); + IFCAnyHandle property = CreateForcePropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); + + if (property == null) { - double scaledPerimeter = scaledOuterPerimeter + scaledInnerPerimeter; - //According to the IFC documentation, OuterSurfaceArea does not include the end caps area, only Length * Perimeter - outSurfaceArea = UnitUtil.ScaleArea(UnitUtil.UnscaleLength(scaledLength) * UnitUtil.UnscaleLength(scaledPerimeter)); - IFCAnyHandle quantityHnd = IFCInstanceExporter.CreateQuantityArea(file, "OuterSurfaceArea", null, null, outSurfaceArea); - quantityHnds.Add(quantityHnd); + doubleValues = GetDoubleValuesFromParameterByType(elem, ifcPropertyName, SpecTypeId.Force, valueType); + property = CreateForcePropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); } - // Compute GrossSurfaceArea if both CrossSectionAre and OuterSurfaceArea cannot be determined separately - if (MathUtil.AreaIsAlmostZero(crossSectionArea) && MathUtil.AreaIsAlmostZero(outSurfaceArea)) + return property; + } + + /// + /// Create a Frequency measure property from the element's parameter. + /// + /// The IFC file. + /// The Element. + /// The name of the parameter. + /// The name of the property. Also, the backup name of the parameter. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateFrequencyPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + string ifcPropertyName, PropertyValueType valueType) + { + IList doubleValues = GetDoubleValuesFromParameterByType(elem, revitParameterName, SpecTypeId.Number, valueType); + IFCAnyHandle property = CreateFrequencyPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); + + if (property == null) { - double scaledPerimeter = scaledOuterPerimeter + scaledInnerPerimeter; - double grossSurfaceArea = scaledGrossArea * 2 + UnitUtil.ScaleArea(UnitUtil.UnscaleLength(scaledLength) * UnitUtil.UnscaleLength(scaledPerimeter)); - IFCAnyHandle quantityHnd = IFCInstanceExporter.CreateQuantityArea(file, "GrossSurfaceArea", null, null, grossSurfaceArea); - quantityHnds.Add(quantityHnd); + doubleValues = GetDoubleValuesFromParameterByType(elem, ifcPropertyName, SpecTypeId.Number, valueType); + property = CreateFrequencyPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); } - double grossVolume = UnitUtil.ScaleVolume(UnitUtil.UnscaleLength(scaledLength) * UnitUtil.UnscaleArea(scaledGrossArea)); - double netVolume = 0.0; - if (element != null) - { - // If we are splitting columns, we will look at the actual geometry used when exporting this segment - // of the column, but only if we have the geomObjects passed in. - if (geomObjects != null && ExporterCacheManager.ExportOptionsCache.WallAndColumnSplitting) - { - foreach (GeometryObject geomObj in geomObjects) - { - // We don't suport calculating the volume of Meshes at this time. - if (geomObj is Mesh) - { - netVolume = 0.0; - break; - } + return property; + } - if (geomObj is Solid) - netVolume += (geomObj as Solid).Volume; - } - } - else - ParameterUtil.GetDoubleValueFromElement(element, BuiltInParameter.HOST_VOLUME_COMPUTED, out netVolume); - netVolume = UnitUtil.ScaleVolume(netVolume); - } + /// + /// Create a HeatingValue measure property from the element's parameter. + /// + /// The IFC file. + /// The Element. + /// The name of the parameter. + /// The name of the property. Also, the backup name of the parameter. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateHeatingValuePropertyFromElement(IFCFile file, Element elem, string revitParameterName, + string ifcPropertyName, PropertyValueType valueType) + { + IList doubleValues = GetDoubleValuesFromParameterByType(elem, revitParameterName, SpecTypeId.SpecificHeatOfVaporization, valueType); + IFCAnyHandle property = CreateHeatingValuePropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); - if (!MathUtil.VolumeIsAlmostZero(grossVolume)) + if (property == null) { - IFCAnyHandle quantityHnd = IFCInstanceExporter.CreateQuantityVolume(file, "GrossVolume", null, null, grossVolume); - quantityHnds.Add(quantityHnd); + doubleValues = GetDoubleValuesFromParameterByType(elem, ifcPropertyName, SpecTypeId.SpecificHeatOfVaporization, valueType); + property = CreateHeatingValuePropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); } - if (!MathUtil.VolumeIsAlmostZero(netVolume)) + return property; + } + + /// + /// Create a Illuminance measure property from the element's parameter. + /// + /// The IFC file. + /// The Element. + /// The name of the parameter. + /// The name of the property. Also, the backup name of the parameter. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateIlluminancePropertyFromElement(IFCFile file, Element elem, string revitParameterName, + string ifcPropertyName, PropertyValueType valueType) + { + IList doubleValues = GetDoubleValuesFromParameterByType(elem, revitParameterName, SpecTypeId.Illuminance, valueType); + IFCAnyHandle property = CreateIlluminancePropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); + + if (property == null) { - IFCAnyHandle quantityHnd = IFCInstanceExporter.CreateQuantityVolume(file, "NetVolume", null, null, netVolume); - quantityHnds.Add(quantityHnd); + doubleValues = GetDoubleValuesFromParameterByType(elem, ifcPropertyName, SpecTypeId.Illuminance, valueType); + property = CreateIlluminancePropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); } - string quantitySetName = string.Empty; - if (!ExporterCacheManager.ExportOptionsCache.ExportAsOlderThanIFC4) + return property; + } + + /// + /// Create a IonConcentration measure property from the element's parameter. + /// + /// The IFC file. + /// The Element. + /// The name of the parameter. + /// The name of the property. Also, the backup name of the parameter. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateIonConcentrationPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + string ifcPropertyName, PropertyValueType valueType) + { + IList doubleValues = GetDoubleValuesFromParameterByType(elem, revitParameterName, SpecTypeId.PipingDensity, valueType); + IFCAnyHandle property = CreateIonConcentrationPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); + + if (property == null) { - if (IFCAnyHandleUtil.IsSubTypeOf(elemHandle, Common.Enums.IFCEntityType.IfcColumn)) - quantitySetName = "Qto_ColumnBaseQuantities"; - if (IFCAnyHandleUtil.IsSubTypeOf(elemHandle, Common.Enums.IFCEntityType.IfcBeam)) - quantitySetName = "Qto_BeamBaseQuantities"; - if (IFCAnyHandleUtil.IsSubTypeOf(elemHandle, Common.Enums.IFCEntityType.IfcMember)) - quantitySetName = "Qto_MemberBaseQuantities"; + doubleValues = GetDoubleValuesFromParameterByType(elem, ifcPropertyName, SpecTypeId.PipingDensity, valueType); + property = CreateIonConcentrationPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); } - CreateAndRelateBaseQuantities(file, exporterIFC, elemHandle, quantityHnds, quantitySetName); + + return property; } /// - /// Creates the spatial element quantities required by GSA before COBIE and adds them to the export. + /// Create a IsothermalMoistureCapacity measure property from the element's parameter. /// - /// The exporter. - /// The element handle. - /// The quantity name. - /// The area name. - /// The area. - public static void CreatePreCOBIEGSAQuantities(ExporterIFC exporterIFC, IFCAnyHandle elemHnd, string quantityName, string areaName, double area) + /// The IFC file. + /// The Element. + /// The name of the parameter. + /// The name of the property. Also, the backup name of the parameter. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateIsothermalMoistureCapacityPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + string ifcPropertyName, PropertyValueType valueType) { - IFCFile file = exporterIFC.GetFile(); - IFCAnyHandle ownerHistory = ExporterCacheManager.OwnerHistoryHandle; - IFCAnyHandle areaQuantityHnd = IFCInstanceExporter.CreateQuantityArea(file, quantityName, null, null, area); - HashSet areaQuantityHnds = new HashSet(); - areaQuantityHnds.Add(areaQuantityHnd); + IList doubleValues = GetDoubleValuesFromParameterByType(elem, revitParameterName, SpecTypeId.IsothermalMoistureCapacity, valueType); + IFCAnyHandle property = CreateIsothermalMoistureCapacityPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); - PropertyUtil.CreateAndRelateBaseQuantities(file, exporterIFC, elemHnd, areaQuantityHnds, quantityName, null, areaName); + if (property == null) + { + doubleValues = GetDoubleValuesFromParameterByType(elem, ifcPropertyName, SpecTypeId.IsothermalMoistureCapacity, valueType); + property = CreateIsothermalMoistureCapacityPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); + } + + return property; } /// - /// Creates the opening quantities and adds them to the export. + /// Create a HeatFluxDensity measure property from the element's parameter. /// - /// The exporter. - /// The opening element handle. - /// The extrusion creation data. - public static void CreateOpeningQuantities(ExporterIFC exporterIFC, IFCAnyHandle openingElement, IFCExportBodyParams extraParams) + /// The IFC file. + /// The Element. + /// The name of the parameter. + /// The name of the property. Also, the backup name of the parameter. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateHeatFluxDensityPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + string ifcPropertyName, PropertyValueType valueType) { - IFCFile file = exporterIFC.GetFile(); - HashSet quantityHnds = new HashSet(); - if (extraParams.ScaledLength > MathUtil.Eps()) - { - IFCAnyHandle quantityHnd = IFCInstanceExporter.CreateQuantityLength(file, "Depth", null, null, extraParams.ScaledLength); - quantityHnds.Add(quantityHnd); - } - if (extraParams.ScaledHeight > MathUtil.Eps()) - { - IFCAnyHandle quantityHnd = IFCInstanceExporter.CreateQuantityLength(file, "Height", null, null, extraParams.ScaledHeight); - quantityHnds.Add(quantityHnd); - quantityHnd = IFCInstanceExporter.CreateQuantityLength(file, "Width", null, null, extraParams.ScaledWidth); - quantityHnds.Add(quantityHnd); - } - else if (extraParams.ScaledArea > MathUtil.Eps()) - { - IFCAnyHandle quantityHnd = IFCInstanceExporter.CreateQuantityArea(file, "Area", null, null, extraParams.ScaledArea); - quantityHnds.Add(quantityHnd); - } + IList doubleValues = GetDoubleValuesFromParameterByType(elem, revitParameterName, SpecTypeId.HvacPowerDensity, valueType); + IFCAnyHandle property = CreateHeatFluxDensityPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); - string quantitySetName = string.Empty; - if (!ExporterCacheManager.ExportOptionsCache.ExportAsOlderThanIFC4) + if (property == null) { - quantitySetName = "Qto_OpeningElementBaseQuantities"; + doubleValues = GetDoubleValuesFromParameterByType(elem, ifcPropertyName, SpecTypeId.HvacPowerDensity, valueType); + property = CreateHeatFluxDensityPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); } - CreateAndRelateBaseQuantities(file, exporterIFC, openingElement, quantityHnds, quantitySetName); + + return property; } /// - /// Creates the wall base quantities and adds them to the export. + /// Create a Length measure property from the element's parameter. /// - /// The exporter. - /// The wall element. - /// The wall handle. - /// The list of solids for the entity created for the wall element. - /// The list of meshes for the entity created for the wall element. - /// The scaled length. - /// The scaled depth. - /// The scaled foot print area. - /// If we are splitting walls by level, the list of solids and meshes represent the currently - /// exported section of wall, not the entire wall. - public static void CreateWallBaseQuantities(ExporterIFC exporterIFC, Wall wallElement, - IList solids, IList meshes, - IFCAnyHandle wallHnd, - double scaledLength, double scaledDepth, double scaledFootPrintArea, - IFCExportBodyParams extrustionData, HashSet widthAsComplexQty = null) + /// The IFC file. + /// The Element. + /// The name of the parameter. + /// The name of the property. Also, the backup name of the parameter. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateLengthPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + string ifcPropertyName, PropertyValueType valueType) { - IFCFile file = exporterIFC.GetFile(); - HashSet quantityHnds = new HashSet(); - if (scaledDepth > MathUtil.Eps()) - { - IFCAnyHandle quantityHnd = IFCInstanceExporter.CreateQuantityLength(file, "Height", null, null, scaledDepth); - quantityHnds.Add(quantityHnd); - } + IList doubleValues = GetDoubleValuesFromParameterByType(elem, revitParameterName, SpecTypeId.Length, valueType); + IFCAnyHandle property = CreateLengthPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); - if (!MathUtil.IsAlmostZero(scaledLength)) - { - IFCAnyHandle quantityHnd = IFCInstanceExporter.CreateQuantityLength(file, "Length", null, null, scaledLength); - quantityHnds.Add(quantityHnd); - } - else if (wallElement.Location != null) + if (property == null) { - Curve wallAxis = (wallElement.Location as LocationCurve).Curve; - if (wallAxis != null) - { - double axisLength = UnitUtil.ScaleLength(wallAxis.Length); - IFCAnyHandle quantityHnd = IFCInstanceExporter.CreateQuantityLength(file, "Length", null, null, axisLength); - quantityHnds.Add(quantityHnd); - } + doubleValues = GetDoubleValuesFromParameterByType(elem, ifcPropertyName, SpecTypeId.Length, valueType); + property = CreateLengthPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); } + return property; + } - double scaledWidth = 0.0; - if (wallElement != null) - { - scaledWidth = UnitUtil.ScaleLength(wallElement.Width); - if (!MathUtil.IsAlmostZero(scaledWidth)) - { - if ((widthAsComplexQty?.Count ?? 0) == 0) - { - IFCAnyHandle quantityHnd = IFCInstanceExporter.CreateQuantityLength(file, "Width", null, null, scaledWidth); - quantityHnds.Add(quantityHnd); - } - else - { - quantityHnds.UnionWith(widthAsComplexQty); - } - } - } + /// + /// Create a LinearForce measure property from the element's parameter. + /// + /// The IFC file. + /// The Element. + /// The name of the parameter. + /// The name of the property. Also, the backup name of the parameter. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateLinearForcePropertyFromElement(IFCFile file, Element elem, string revitParameterName, + string ifcPropertyName, PropertyValueType valueType) + { + IList doubleValues = GetDoubleValuesFromParameterByType(elem, revitParameterName, SpecTypeId.LinearForce, valueType); + IFCAnyHandle property = CreateLinearForcePropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); - if (!MathUtil.IsAlmostZero(scaledFootPrintArea)) + if (property == null) { - IFCAnyHandle quantityHnd = IFCInstanceExporter.CreateQuantityArea(file, "GrossFootprintArea", null, null, scaledFootPrintArea); - quantityHnds.Add(quantityHnd); + doubleValues = GetDoubleValuesFromParameterByType(elem, ifcPropertyName, SpecTypeId.LinearForce, valueType); + property = CreateLinearForcePropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); } - double netArea = 0; - double grossArea = 0; - double volume = 0; + return property; + } - // We will only assign the area if we have all solids that we are exporting; we won't bother calcuting values for Meshes. - if (solids != null && (meshes == null || meshes.Count == 0)) - { - foreach (Solid solid in solids) - { - double largestFaceNetArea = 0.0; - double largestFaceGrossArea = 0.0; - foreach (Face face in solid.Faces) - { - XYZ fNormal = face.ComputeNormal(new UV(0, 0)); - if (MathUtil.IsAlmostZero(fNormal.Z)) - { - if (face.Area > largestFaceNetArea) - largestFaceNetArea = face.Area; // collecting largest face on the XY plane. It will be used for NetArea + /// + /// Create a LinearMoment measure property from the element's parameter. + /// + /// The IFC file. + /// The Element. + /// The name of the parameter. + /// The name of the property. Also, the backup name of the parameter. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateLinearMomentPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + string ifcPropertyName, PropertyValueType valueType) + { + IList doubleValues = GetDoubleValuesFromParameterByType(elem, revitParameterName, SpecTypeId.LinearMoment, valueType); + IFCAnyHandle property = CreateLinearMomentPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); - IList fCurveLoops = face.GetEdgesAsCurveLoops(); - double grArea = ExporterIFCUtils.ComputeAreaOfCurveLoops(new List() { fCurveLoops[0] }); - if (grArea > largestFaceGrossArea) - largestFaceGrossArea = grArea; - } - } - netArea += largestFaceNetArea; - grossArea += largestFaceGrossArea; - volume += solid.Volume; - } + if (property == null) + { + doubleValues = GetDoubleValuesFromParameterByType(elem, ifcPropertyName, SpecTypeId.LinearMoment, valueType); + property = CreateLinearMomentPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); } - netArea = UnitUtil.ScaleArea(netArea); - grossArea = UnitUtil.ScaleArea(grossArea); - volume = UnitUtil.ScaleVolume(volume); + return property; + } - if (scaledDepth > MathUtil.Eps() && !MathUtil.IsAlmostZero(scaledWidth) && !MathUtil.IsAlmostZero(grossArea)) - { - double grossVolume = UnitUtil.ScaleVolume(UnitUtil.UnscaleLength(scaledWidth) * UnitUtil.UnscaleArea(grossArea)); - IFCAnyHandle quantityHnd = IFCInstanceExporter.CreateQuantityVolume(file, "GrossVolume", null, null, grossVolume); - quantityHnds.Add(quantityHnd); - } + /// + /// Create a LinearStiffness measure property from the element's parameter. + /// + /// The IFC file. + /// The Element. + /// The name of the parameter. + /// The name of the property. Also, the backup name of the parameter. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateLinearStiffnessPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + string ifcPropertyName, PropertyValueType valueType) + { + IList doubleValues = GetDoubleValuesFromParameterByType(elem, revitParameterName, SpecTypeId.PointSpringCoefficient, valueType); + IFCAnyHandle property = CreateLinearStiffnessPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); - if (!MathUtil.IsAlmostZero(grossArea)) + if (property == null) { - IFCAnyHandle quantityHnd = IFCInstanceExporter.CreateQuantityArea(file, "GrossSideArea", null, null, grossArea); - quantityHnds.Add(quantityHnd); + doubleValues = GetDoubleValuesFromParameterByType(elem, ifcPropertyName, SpecTypeId.PointSpringCoefficient, valueType); + property = CreateLinearStiffnessPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); } - if (!MathUtil.IsAlmostZero(netArea)) - { - IFCAnyHandle quantityHnd = IFCInstanceExporter.CreateQuantityArea(file, "NetSideArea", null, null, netArea); - quantityHnds.Add(quantityHnd); - } + return property; + } - if (!MathUtil.IsAlmostZero(volume)) - { - IFCAnyHandle quantityHnd = IFCInstanceExporter.CreateQuantityVolume(file, "NetVolume", null, null, volume); - quantityHnds.Add(quantityHnd); - } + /// + /// Create a LinearVelocity measure property from the element's parameter. + /// + /// The IFC file. + /// The Element. + /// The name of the parameter. + /// The name of the property. Also, the backup name of the parameter. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateLinearVelocityPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + string ifcPropertyName, PropertyValueType valueType) + { + IList doubleValues = GetDoubleValuesFromParameterByType(elem, revitParameterName, SpecTypeId.HvacVelocity, valueType); + IFCAnyHandle property = CreateLinearVelocityPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); - string quantitySetName = string.Empty; - if (ExporterCacheManager.ExportOptionsCache.ExportAs4) + if (property == null) { - quantitySetName = "Qto_WallBaseQuantities"; + doubleValues = GetDoubleValuesFromParameterByType(elem, ifcPropertyName, SpecTypeId.HvacVelocity, valueType); + property = CreateLinearVelocityPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); } - CreateAndRelateBaseQuantities(file, exporterIFC, wallHnd, quantityHnds, quantitySetName); + return property; } /// - /// Creates and relate base quantities to quantity handle. + /// Create a LuminousFlux measure property from the element's parameter. /// - /// The file. - /// The exporter. - /// The element handle. - /// The quantity handles. - static public void CreateAndRelateBaseQuantities(IFCFile file, ExporterIFC exporterIFC, IFCAnyHandle elemHnd, HashSet quantityHnds, - string quantitySetName = null, string description = null, string methodOfMeasurement = null) + /// The IFC file. + /// The Element. + /// The name of the parameter. + /// The name of the property. Also, the backup name of the parameter. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateLuminousFluxPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + string ifcPropertyName, PropertyValueType valueType) { - if (quantityHnds.Count > 0) - { - if (string.IsNullOrEmpty(quantitySetName)) - quantitySetName = "BaseQuantities"; - IFCAnyHandle ownerHistory = ExporterCacheManager.OwnerHistoryHandle; - - // Skip if the elementHandle has the associated QuantitySet has been created before - if (!ExporterCacheManager.QtoSetCreated.Contains((elemHnd, quantitySetName))) - { - string quantityGuid = GUIDUtil.GenerateIFCGuidFrom( - GUIDUtil.CreateGUIDString(IFCEntityType.IfcElementQuantity, quantitySetName, elemHnd)); - IFCAnyHandle quantity = IFCInstanceExporter.CreateElementQuantity(file, elemHnd, - quantityGuid, ownerHistory, quantitySetName, description, - methodOfMeasurement, quantityHnds); - HashSet relatedObjects = new HashSet(); - relatedObjects.Add(elemHnd); + IList doubleValues = GetDoubleValuesFromParameterByType(elem, revitParameterName, SpecTypeId.LuminousFlux, valueType); + IFCAnyHandle property = CreateLuminousFluxPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); - string quantityRelGuid = GUIDUtil.GenerateIFCGuidFrom( - GUIDUtil.CreateGUIDString(IFCEntityType.IfcRelDefinesByProperties, quantitySetName, elemHnd)); - ExporterUtil.CreateRelDefinesByProperties(file, quantityRelGuid, ownerHistory, null, null, - relatedObjects, quantity); - } + if (property == null) + { + doubleValues = GetDoubleValuesFromParameterByType(elem, ifcPropertyName, SpecTypeId.LuminousFlux, valueType); + property = CreateLuminousFluxPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); } + + return property; } /// - /// Creates the shared beam, column and member QTO values. + /// Create a LuminousIntensity measure property from the element's parameter. /// - /// The exporter. - /// The element handle. - /// The element. - /// The IFCExportBodyParams containing the appropriate data. - public static void CreateBeamColumnMemberBaseQuantities(ExporterIFC exporterIFC, IFCAnyHandle elemHandle, Element element, IFCExportBodyParams ecData) + /// The IFC file. + /// The Element. + /// The name of the parameter. + /// The name of the property. Also, the backup name of the parameter. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateLuminousIntensityPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + string ifcPropertyName, PropertyValueType valueType) { - FamilyTypeInfo ifcTypeInfo = new FamilyTypeInfo() { extraParams = ecData }; - CreateBeamColumnBaseQuantities(exporterIFC, elemHandle, element, ifcTypeInfo, null); + IList doubleValues = GetDoubleValuesFromParameterByType(elem, revitParameterName, SpecTypeId.LuminousIntensity, valueType); + IFCAnyHandle property = CreateLuminousIntensityPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); + + if (property == null) + { + doubleValues = GetDoubleValuesFromParameterByType(elem, ifcPropertyName, SpecTypeId.LuminousIntensity, valueType); + property = CreateLuminousIntensityPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); + } + + return property; } /// - /// Creates property sets for Revit groups and parameters, if export options is set. + /// Create a Mass measure property from the element's parameter. /// - /// The ExporterIFC. - /// The Element. - /// The collection of IFCAnyHandles to relate properties to. - /// Forces properties creation even if 'Export internal properties' is unchecked. - public static void CreateInternalRevitPropertySets(ExporterIFC exporterIFC, Element element, - ISet elementSets, bool forceCreate) + /// The IFC file. + /// The Element. + /// The name of the parameter. + /// The name of the property. Also, the backup name of the parameter. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateMassPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + string ifcPropertyName, PropertyValueType valueType) { - if (exporterIFC == null || element == null || - (!ExporterCacheManager.ExportOptionsCache.PropertySetOptions.ExportInternalRevit && !forceCreate)) - return; - - // We will allow creating internal Revit property sets for element types with no associated element handles. - if (((elementSets?.Count ?? 0) == 0) && !(element is ElementType)) - return; + IList doubleValues = GetDoubleValuesFromParameterByType(elem, revitParameterName, SpecTypeId.Mass, valueType); + IFCAnyHandle property = CreateMassPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); - IFCFile file = exporterIFC.GetFile(); - - ElementId typeId = element.GetTypeId(); - Element elementType = element.Document.GetElement(typeId); - int whichStart = elementType != null ? 0 : (element is ElementType ? 1 : 0); - if (whichStart == 1) + if (property == null) { - typeId = element.Id; - elementType = element as ElementType; + doubleValues = GetDoubleValuesFromParameterByType(elem, ifcPropertyName, SpecTypeId.Mass, valueType); + property = CreateMassPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); } - SortedDictionary)>[] propertySets; - propertySets = new SortedDictionary)>[2]; - propertySets[0] = new SortedDictionary)>(); - propertySets[1] = new SortedDictionary)>(); + return property; + } - // pass through: element and element type. If the element is a ElementType, there will only be one pass. - for (int which = whichStart; which < 2; which++) - { - Element whichElement = (which == 0) ? element : elementType; - if (whichElement == null) - continue; - - // If we have already processed this element, just add the new - // IFC entities. - if (ExporterCacheManager.CreatedInternalPropertySets.TryAppend(whichElement.Id, elementSets)) - continue; + /// + /// Create a MassDensity measure property from the element's parameter. + /// + /// The IFC file. + /// The Element. + /// The name of the parameter. + /// The name of the property. Also, the backup name of the parameter. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateMassDensityPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + string ifcPropertyName, PropertyValueType valueType) + { + IList doubleValues = GetDoubleValuesFromParameterByType(elem, revitParameterName, SpecTypeId.MassDensity, valueType); + IFCAnyHandle property = CreateMassDensityPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); - ElementId whichElementId = whichElement.Id; + if (property == null) + { + doubleValues = GetDoubleValuesFromParameterByType(elem, ifcPropertyName, SpecTypeId.MassDensity, valueType); + property = CreateMassDensityPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); + } - bool createType = (which == 1); - if (createType) - { - if (ExporterCacheManager.TypePropertyInfoCache.HasTypeProperties(typeId)) - continue; - } + return property; + } - IDictionary parameterElementCache = - ParameterUtil.GetNonIFCParametersForElement(whichElementId); - if (parameterElementCache == null) - continue; + /// + /// Create a MassFlowRate measure property from the element's parameter. + /// + /// The IFC file. + /// The Element. + /// The name of the parameter. + /// The name of the property. Also, the backup name of the parameter. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateMassFlowRatePropertyFromElement(IFCFile file, Element elem, string revitParameterName, + string ifcPropertyName, PropertyValueType valueType) + { + IList doubleValues = GetDoubleValuesFromParameterByType(elem, revitParameterName, SpecTypeId.PipingMassPerTime, valueType); + IFCAnyHandle property = CreateMassFlowRatePropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); - foreach (KeyValuePair parameterElementGroup in parameterElementCache) - { - ForgeTypeId parameterGroup = new ForgeTypeId (parameterElementGroup.Key); - string groupName = LabelUtils.GetLabelForGroup(parameterGroup); + if (property == null) + { + doubleValues = GetDoubleValuesFromParameterByType(elem, ifcPropertyName, SpecTypeId.PipingMassPerTime, valueType); + property = CreateMassFlowRatePropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); + } - // We are only going to append the "(Type)" suffix if we aren't also exporting the corresponding entity type. - // In general, we'd like to always export them entity type, regardles of whether it holds any geometry or not - it can hold - // at least the parameteric information. When this is acheived, when can get rid of this entirely. - // Unfortunately, IFC2x3 doesn't have types for all entities, so for IFC2x3 at least this will continue to exist - // in some fashion. - // There was a suggestion in SourceForge that we could "merge" the instance/type property sets in the cases where we aren't - // creating an entity type, and in the cases where two properties had the same name, use the instance over type. - // However, given our intention to generally export all types, this seems like a lot of work for diminishing returns. - if (whichElement is ElementType) - if (which == 1 && !ExporterCacheManager.ElementTypeToHandleCache.IsRegistered(whichElement as ElementType)) - groupName += Properties.Resources.PropertySetTypeSuffix; + return property; + } - HashSet currPropertiesForGroup = new HashSet(); - propertySets[which][parameterElementGroup.Key] = (groupName, currPropertiesForGroup); + /// + /// Create a MassPerLength measure property from the element's parameter. + /// + /// The IFC file. + /// The Element. + /// The name of the parameter. + /// The name of the property. Also, the backup name of the parameter. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateMassPerLengthPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + string ifcPropertyName, PropertyValueType valueType) + { + IList doubleValues = GetDoubleValuesFromParameterByType(elem, revitParameterName, SpecTypeId.MassPerUnitLength, valueType); + IFCAnyHandle property = CreateMassPerLengthPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); - foreach (Parameter parameter in parameterElementGroup.Value.ParameterCache.Values) - { - if (!parameter.HasValue) - continue; + if (property == null) + { + doubleValues = GetDoubleValuesFromParameterByType(elem, ifcPropertyName, SpecTypeId.MassPerUnitLength, valueType); + property = CreateMassPerLengthPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); + } - Definition parameterDefinition = parameter.Definition; - if (parameterDefinition == null) - continue; + return property; + } - string parameterCaption = parameterDefinition.Name; + /// + /// Create a ModulusOfElasticity measure property from the element's parameter. + /// + /// The IFC file. + /// The Element. + /// The name of the parameter. + /// The name of the property. Also, the backup name of the parameter. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateModulusOfElasticityPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + string ifcPropertyName, PropertyValueType valueType) + { + IList doubleValues = GetDoubleValuesFromParameterByType(elem, revitParameterName, SpecTypeId.Stress, valueType); + IFCAnyHandle property = CreateModulusOfElasticityPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); - switch (parameter.StorageType) - { - case StorageType.None: - break; - case StorageType.Integer: - { - int value = parameter.AsInteger(); - string valueAsString = parameter.AsValueString(); + if (property == null) + { + doubleValues = GetDoubleValuesFromParameterByType(elem, ifcPropertyName, SpecTypeId.Stress, valueType); + property = CreateModulusOfElasticityPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); + } - // YesNo or actual integer? - if (parameterDefinition.GetDataType() == SpecTypeId.Boolean.YesNo) - { - currPropertiesForGroup.Add(CreateBooleanPropertyFromCache(file, parameterCaption, value != 0, PropertyValueType.SingleValue)); - } - else if (parameterDefinition.GetDataType().Empty() && (valueAsString != null)) - { - // This is probably an internal enumerated type that should be exported as a string. - currPropertiesForGroup.Add(CreateIdentifierPropertyFromCache(file, parameterCaption, valueAsString, PropertyValueType.SingleValue)); - } - else - { - currPropertiesForGroup.Add(CreateIntegerPropertyFromCache(file, parameterCaption, value, PropertyValueType.SingleValue)); - } - break; - } - case StorageType.Double: - { - double value = parameter.AsDouble(); - IFCAnyHandle propertyHandle = CreateRealPropertyBasedOnParameterType(file, parameter, parameterCaption, value, PropertyValueType.SingleValue); + return property; + } - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propertyHandle)) - currPropertiesForGroup.Add(propertyHandle); - break; - } - case StorageType.String: - { - string value = parameter.AsString(); - if (!string.IsNullOrEmpty(value)) - currPropertiesForGroup.Add(CreateTextPropertyFromCache(file, parameterCaption, value, PropertyValueType.SingleValue)); - break; - } - case StorageType.ElementId: - { - if (parameter.AsElementId() != ElementId.InvalidElementId) - { - string valueString = parameter.AsValueString(); - currPropertiesForGroup.Add(CreateLabelPropertyFromCache(file, parameter.Id, parameterCaption, valueString, PropertyValueType.SingleValue, true, null)); - } - break; - } - } - } - } - } + /// + /// Create a MoistureDiffusivity measure property from the element's parameter. + /// + /// The IFC file. + /// The Element. + /// The name of the parameter. + /// The name of the property. Also, the backup name of the parameter. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateMoistureDiffusivityPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + string ifcPropertyName, PropertyValueType valueType) + { + IList doubleValues = GetDoubleValuesFromParameterByType(elem, revitParameterName, SpecTypeId.Diffusivity, valueType); + IFCAnyHandle property = CreateMoistureDiffusivityPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); - for (int which = whichStart; which < 2; which++) + if (property == null) { - Element whichElement = (which == 0) ? element : elementType; - if (whichElement == null) - continue; + doubleValues = GetDoubleValuesFromParameterByType(elem, ifcPropertyName, SpecTypeId.Diffusivity, valueType); + property = CreateMoistureDiffusivityPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); + } - HashSet createdPropertySets = new HashSet(); + return property; + } - int size = propertySets[which].Count; - if (size == 0) - { - ExporterCacheManager.TypePropertyInfoCache.AddNewElementHandles(typeId, elementSets); - continue; - } + /// + /// Create a MomentOfInertia measure property from the element's parameter. + /// + /// The IFC file. + /// The Element. + /// The name of the parameter. + /// The name of the property. Also, the backup name of the parameter. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateMomentOfInertiaPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + string ifcPropertyName, PropertyValueType valueType) + { + IList doubleValues = GetDoubleValuesFromParameterByType(elem, revitParameterName, SpecTypeId.MomentOfInertia, valueType); + IFCAnyHandle property = CreateMomentOfInertiaPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); - bool materialProperties = element is Material; - foreach (KeyValuePair)> currPropertySet in propertySets[which]) - { - if (currPropertySet.Value.Item2.Count == 0) - continue; + if (property == null) + { + doubleValues = GetDoubleValuesFromParameterByType(elem, ifcPropertyName, SpecTypeId.MomentOfInertia, valueType); + property = CreateMomentOfInertiaPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); + } - if (materialProperties) - { - MaterialPropertiesUtil.ExportGenericMaterialPropertySet(file, elementSets?.ToList().First(), currPropertySet.Value.Item2, null, currPropertySet.Value.Item1); - } - else - { - string psetGUID = GUIDUtil.GenerateIFCGuidFrom( - GUIDUtil.CreateGUIDString(whichElement, "IfcPropertySet: " + currPropertySet.Key.ToString())); + return property; + } - IFCAnyHandle propertySet = IFCInstanceExporter.CreatePropertySet(file, psetGUID, - ExporterCacheManager.OwnerHistoryHandle, currPropertySet.Value.Item1, null, - currPropertySet.Value.Item2); - createdPropertySets.Add(propertySet); - } - } + /// + /// Create a NormalisedRatio measure property from the element's parameter. + /// + /// The IFC file. + /// The Element. + /// The name of the parameter. + /// The name of the property. Also, the backup name of the parameter. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateNormalisedRatioPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + string ifcPropertyName, PropertyValueType valueType) + { + IList doubleValues = GetDoubleValuesFromParameterByType(elem, revitParameterName, SpecTypeId.Number, valueType); + IFCAnyHandle property = CreateNormalisedRatioPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); - // Don't need to create relations for material properties - if (!materialProperties) - { - if (which == 0) - ExporterCacheManager.CreatedInternalPropertySets.Add(whichElement.Id, createdPropertySets, elementSets); - else - ExporterCacheManager.TypePropertyInfoCache.AddNewTypeProperties(typeId, createdPropertySets, elementSets); - } + if (property == null) + { + doubleValues = GetDoubleValuesFromParameterByType(elem, ifcPropertyName, SpecTypeId.Number, valueType); + property = CreateNormalisedRatioPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); } + + return property; } /// - /// Get a unit type of parameter. - /// IFCUnit for each one. + /// Create a Numeric measure property from the element's parameter. /// - /// The parameter. - /// The parameter unit type. - public static ForgeTypeId GetParameterUnitType(Parameter parameter) + /// The IFC file. + /// The Element. + /// The name of the parameter. + /// The name of the property. Also, the backup name of the parameter. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateNumericPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + string ifcPropertyName, PropertyValueType valueType) { - ForgeTypeId parameterUnitType = null; + IList doubleValues = GetDoubleValuesFromParameterByType(elem, revitParameterName, SpecTypeId.Number, valueType); + IFCAnyHandle property = CreateNumericPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); - try - { - parameterUnitType = parameter?.GetUnitTypeId(); - } - catch + if (property == null) { - // GetUnitTypeId() can fail for reasons that don't seem to be knowable in - // advance, so we won't scale value in these cases. + doubleValues = GetDoubleValuesFromParameterByType(elem, ifcPropertyName, SpecTypeId.Number, valueType); + property = CreateNumericPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); } - return parameterUnitType; + return property; } /// - /// Creates property from real parameter. - /// There are many different ParameterTypes in Revit that share the same unit dimensions, but that - /// have potentially different display units (e.g. Bar Diameter could be in millimeters while the project - /// default length parameter is in meters.) For now, we will only support one unit type. At a later - /// point, we could decide to have different caches for each parameter type, and export a different - /// IFCUnit for each one. + /// Create a PlaneAngle measure property from the element's parameter. /// /// The IFC file. - /// The parameter. - /// The name of the property. - /// The value of the property. + /// The Element. + /// The name of the parameter. + /// The name of the property. Also, the backup name of the parameter. /// The value type of the property. /// The created property handle. - public static IFCAnyHandle CreateRealPropertyBasedOnParameterType(IFCFile file, Parameter parameter, string propertyName, double propertyValue, PropertyValueType valueType) + public static IFCAnyHandle CreatePlaneAnglePropertyFromElement(IFCFile file, Element elem, string revitParameterName, + string ifcPropertyName, PropertyValueType valueType) { - if (parameter == null) - return null; + IList doubleValues = GetDoubleValuesFromParameterByType(elem, revitParameterName, SpecTypeId.Angle, valueType); + IFCAnyHandle property = CreatePlaneAnglePropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); - ForgeTypeId type = parameter.Definition?.GetDataType(); - ForgeTypeId fallbackUnitType = GetParameterUnitType(parameter); + if (property == null) + { + doubleValues = GetDoubleValuesFromParameterByType(elem, ifcPropertyName, SpecTypeId.Angle, valueType); + property = CreatePlaneAnglePropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); + } - return CreateRealPropertyByType(file, type, propertyName, propertyValue, valueType, fallbackUnitType); + return property; } /// - /// Creates property from real parameter. + /// Create a PlanarForce measure property from the element's parameter. /// /// The IFC file. - /// The type of the parameter. - /// The name of the property. - /// The value of the property. + /// The Element. + /// The name of the parameter. + /// The name of the property. Also, the backup name of the parameter. /// The value type of the property. - /// The optional unit type. Can be used for scaling in final case /// The created property handle. - public static IFCAnyHandle CreateRealPropertyByType(IFCFile file, ForgeTypeId parameterType, string propertyName, double propertyValue, PropertyValueType valueType, ForgeTypeId fallbackUnitType = null) + public static IFCAnyHandle CreatePlanarForcePropertyFromElement(IFCFile file, Element elem, string revitParameterName, + string ifcPropertyName, PropertyValueType valueType) { - IFCAnyHandle propertyHandle = null; + IList doubleValues = GetDoubleValuesFromParameterByType(elem, revitParameterName, SpecTypeId.AreaForce, valueType); + IFCAnyHandle property = CreatePlanarForcePropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); - if (parameterType == SpecTypeId.Acceleration) + if (property == null) { - double scaledValue = UnitUtil.ScaleAcceleration(propertyValue); - propertyHandle = CreateAccelerationMeasureProperty(file, propertyName, - scaledValue, valueType); + doubleValues = GetDoubleValuesFromParameterByType(elem, ifcPropertyName, SpecTypeId.AreaForce, valueType); + property = CreatePlanarForcePropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); } - else if (parameterType == SpecTypeId.Energy || - parameterType == SpecTypeId.HvacEnergy) - { - double scaledValue = UnitUtil.ScaleEnergy(propertyValue); - propertyHandle = CreateEnergyMeasureProperty(file, propertyName, - scaledValue, valueType); - } - else if (parameterType == SpecTypeId.LinearMoment) + + return property; + } + + /// + /// Create a PositiveLength measure property from the element's parameter. + /// + /// The IFC file. + /// The Element. + /// The name of the parameter. + /// The name of the property. Also, the backup name of the parameter. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreatePositiveLengthPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + string ifcPropertyName, PropertyValueType valueType) + { + IList doubleValues = GetDoubleValuesFromParameterByType(elem, revitParameterName, SpecTypeId.Length, valueType); + IFCAnyHandle property = CreatePositiveLengthPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); + + if (property == null) { - double scaledValue = UnitUtil.ScaleLinearMoment(propertyValue); - propertyHandle = CreateLinearMomentMeasureProperty(file, propertyName, - scaledValue, valueType); + doubleValues = GetDoubleValuesFromParameterByType(elem, ifcPropertyName, SpecTypeId.Length, valueType); + property = CreatePositiveLengthPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); } - else if (parameterType == SpecTypeId.MassPerUnitLength || - parameterType == SpecTypeId.PipeMassPerUnitLength) + + return property; + } + + /// + /// Create a PositiveRatio measure property from the element's parameter. + /// + /// The IFC file. + /// The Element. + /// The name of the parameter. + /// The name of the property. Also, the backup name of the parameter. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreatePositiveRatioPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + string ifcPropertyName, PropertyValueType valueType) + { + IList doubleValues = GetDoubleValuesFromParameterByType(elem, revitParameterName, SpecTypeId.Number, valueType); + IFCAnyHandle property = CreatePositiveRatioPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); + + if (property == null) { - double scaledValue = UnitUtil.ScaleMassPerLength(propertyValue); - propertyHandle = CreateMassPerLengthMeasureProperty(file, propertyName, - scaledValue, valueType); + doubleValues = GetDoubleValuesFromParameterByType(elem, ifcPropertyName, SpecTypeId.Number, valueType); + property = CreatePositiveRatioPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); } - else if (parameterType == SpecTypeId.Moment) + + return property; + } + + /// + /// Create a PositivePlaneAngle measure property from the element's parameter. + /// + /// The IFC file. + /// The Element. + /// The name of the parameter. + /// The name of the property. Also, the backup name of the parameter. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreatePositivePlaneAnglePropertyFromElement(IFCFile file, Element elem, string revitParameterName, + string ifcPropertyName, PropertyValueType valueType) + { + IList doubleValues = GetDoubleValuesFromParameterByType(elem, revitParameterName, SpecTypeId.Angle, valueType); + IFCAnyHandle property = CreatePositivePlaneAnglePropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); + + if (property == null) { - double scaledValue = UnitUtil.ScaleTorque(propertyValue); - propertyHandle = CreateTorqueMeasureProperty(file, propertyName, - scaledValue, valueType); + doubleValues = GetDoubleValuesFromParameterByType(elem, ifcPropertyName, SpecTypeId.Angle, valueType); + property = CreatePositivePlaneAnglePropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); } - else if (parameterType == SpecTypeId.PointSpringCoefficient) + + return property; + } + + /// + /// Create a Power measure property from the element's parameter. + /// + /// The IFC file. + /// The Element. + /// The name of the parameter. + /// The name of the property. Also, the backup name of the parameter. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreatePowerPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + string ifcPropertyName, PropertyValueType valueType) + { + IList doubleValues = GetDoubleValuesFromParameterByType(elem, revitParameterName, SpecTypeId.HvacPower, valueType); + IFCAnyHandle property = CreatePowerPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); + + if (property == null) { - double scaledValue = UnitUtil.ScaleLinearStiffness(propertyValue); - propertyHandle = CreateLinearStiffnessMeasureProperty(file, propertyName, - scaledValue, valueType); + doubleValues = GetDoubleValuesFromParameterByType(elem, ifcPropertyName, SpecTypeId.HvacPower, valueType); + property = CreatePowerPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); } - else if (parameterType == SpecTypeId.Pulsation) + + return property; + } + + /// + /// Create a Pressure measure property from the element's parameter. + /// + /// The IFC file. + /// The Element. + /// The name of the parameter. + /// The name of the property. Also, the backup name of the parameter. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreatePressurePropertyFromElement(IFCFile file, Element elem, string revitParameterName, + string ifcPropertyName, PropertyValueType valueType) + { + IList doubleValues = GetDoubleValuesFromParameterByType(elem, revitParameterName, SpecTypeId.HvacPressure, valueType); + IFCAnyHandle property = CreatePressurePropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); + + if (property == null) { - double scaledValue = UnitUtil.ScaleAngularVelocity(propertyValue); - propertyHandle = CreateAngularVelocityMeasureProperty(file, propertyName, - scaledValue, valueType); + doubleValues = GetDoubleValuesFromParameterByType(elem, ifcPropertyName, SpecTypeId.HvacPressure, valueType); + property = CreatePressurePropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); } - else if (parameterType == SpecTypeId.ThermalResistance) + + return property; + } + + /// + /// Create a Ratio measure property from the element's parameter. + /// + /// The IFC file. + /// The Element. + /// The name of the parameter. + /// The name of the property. Also, the backup name of the parameter. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateRatioPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + string ifcPropertyName, PropertyValueType valueType) + { + IList doubleValues = GetDoubleValuesFromParameterByType(elem, revitParameterName, SpecTypeId.Number, valueType); + IFCAnyHandle property = CreateRatioPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); + + if (property == null) { - double scaledValue = UnitUtil.ScaleThermalResistance(propertyValue); - propertyHandle = CreateThermalResistanceMeasureProperty(file, propertyName, - scaledValue, valueType); + doubleValues = GetDoubleValuesFromParameterByType(elem, ifcPropertyName, SpecTypeId.Number, valueType); + property = CreateRatioPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); } - else if (parameterType == SpecTypeId.WarpingConstant) + + return property; + } + + /// + /// Create a Real measure property from the element's parameter. + /// + /// The IFC file. + /// The Element. + /// The name of the parameter. + /// The name of the property. Also, the backup name of the parameter. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateRealPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + string ifcPropertyName, PropertyValueType valueType) + { + IList doubleValues = GetDoubleValuesFromParameterByType(elem, revitParameterName, SpecTypeId.Number, valueType); + IFCAnyHandle property = CreateRealPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); + + if (property == null) { - double scaledValue = UnitUtil.ScaleWarpingConstant(propertyValue); - propertyHandle = CreateWarpingConstantMeasureProperty(file, propertyName, - scaledValue, valueType); + doubleValues = GetDoubleValuesFromParameterByType(elem, ifcPropertyName, SpecTypeId.Number, valueType); + property = CreateRealPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); } - else if (parameterType == SpecTypeId.Angle || - parameterType == SpecTypeId.Rotation || - parameterType == SpecTypeId.RotationAngle) + + return property; + } + + /// + /// Create a RotationalFrequency measure property from the element's parameter. + /// + /// The IFC file. + /// The Element. + /// The name of the parameter. + /// The name of the property. Also, the backup name of the parameter. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateRotationalFrequencyPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + string ifcPropertyName, PropertyValueType valueType) + { + IList doubleValues = GetDoubleValuesFromParameterByType(elem, revitParameterName, SpecTypeId.AngularSpeed, valueType); + IFCAnyHandle property = CreateRotationalFrequencyPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); + + if (property == null) { - propertyHandle = CreatePlaneAngleMeasurePropertyFromCache(file, propertyName, - UnitUtil.ScaleAngle(propertyValue), valueType); + doubleValues = GetDoubleValuesFromParameterByType(elem, ifcPropertyName, SpecTypeId.AngularSpeed, valueType); + property = CreateRotationalFrequencyPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); } - else if (parameterType == SpecTypeId.Slope || - parameterType == SpecTypeId.HvacSlope || - parameterType == SpecTypeId.PipingSlope || - parameterType == SpecTypeId.DemandFactor || - parameterType == SpecTypeId.Factor) + + return property; + } + + /// + /// Create a SoundPower measure property from the element's parameter. + /// + /// The IFC file. + /// The Element. + /// The name of the parameter. + /// The name of the property. Also, the backup name of the parameter. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateSoundPowerPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + string ifcPropertyName, PropertyValueType valueType) + { + IList doubleValues = GetDoubleValuesFromParameterByType(elem, revitParameterName, SpecTypeId.Wattage, valueType); + IFCAnyHandle property = CreateSoundPowerPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); + + if (property == null) { - propertyHandle = CreatePositiveRatioMeasureProperty(file, propertyName, - propertyValue, valueType); + doubleValues = GetDoubleValuesFromParameterByType(elem, ifcPropertyName, SpecTypeId.Wattage, valueType); + property = CreateSoundPowerPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); } - else if (parameterType == SpecTypeId.Area || - parameterType == SpecTypeId.CrossSection || - parameterType == SpecTypeId.ReinforcementArea || - parameterType == SpecTypeId.SectionArea) + + return property; + } + + /// + /// Create a SoundPressure measure property from the element's parameter. + /// + /// The IFC file. + /// The Element. + /// The name of the parameter. + /// The name of the property. Also, the backup name of the parameter. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateSoundPressurePropertyFromElement(IFCFile file, Element elem, string revitParameterName, + string ifcPropertyName, PropertyValueType valueType) + { + IList doubleValues = GetDoubleValuesFromParameterByType(elem, revitParameterName, SpecTypeId.HvacPressure, valueType); + IFCAnyHandle property = CreateSoundPressurePropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); + + if (property == null) { - double scaledValue = UnitUtil.ScaleArea(propertyValue); - propertyHandle = CreateAreaMeasureProperty(file, propertyName, - scaledValue, valueType); + doubleValues = GetDoubleValuesFromParameterByType(elem, ifcPropertyName, SpecTypeId.HvacPressure, valueType); + property = CreateSoundPressurePropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); } - else if (parameterType == SpecTypeId.BarDiameter || - parameterType == SpecTypeId.CrackWidth || - parameterType == SpecTypeId.Displacement || - parameterType == SpecTypeId.Distance || - parameterType == SpecTypeId.CableTraySize || - parameterType == SpecTypeId.ConduitSize || - parameterType == SpecTypeId.Length || - parameterType == SpecTypeId.DuctInsulationThickness || - parameterType == SpecTypeId.DuctLiningThickness || - parameterType == SpecTypeId.DuctSize || - parameterType == SpecTypeId.HvacRoughness || - parameterType == SpecTypeId.PipeDimension || - parameterType == SpecTypeId.PipeInsulationThickness || - parameterType == SpecTypeId.PipeSize || - parameterType == SpecTypeId.PipingRoughness || - parameterType == SpecTypeId.ReinforcementCover || - parameterType == SpecTypeId.ReinforcementLength || - parameterType == SpecTypeId.ReinforcementSpacing || - parameterType == SpecTypeId.SectionDimension || - parameterType == SpecTypeId.SectionProperty || - parameterType == SpecTypeId.WireDiameter || - parameterType == SpecTypeId.SurfaceAreaPerUnitLength) + + return property; + } + + /// + /// Create a SpecificHeatCapacity measure property from the element's parameter. + /// + /// The IFC file. + /// The Element. + /// The name of the parameter. + /// The name of the property. Also, the backup name of the parameter. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateSpecificHeatCapacityPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + string ifcPropertyName, PropertyValueType valueType) + { + IList doubleValues = GetDoubleValuesFromParameterByType(elem, revitParameterName, SpecTypeId.SpecificHeat, valueType); + IFCAnyHandle property = CreateSpecificHeatCapacityPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); + + if (property == null) { - propertyHandle = CreateLengthMeasurePropertyFromCache(file, propertyName, - UnitUtil.ScaleLength(propertyValue), valueType); + doubleValues = GetDoubleValuesFromParameterByType(elem, ifcPropertyName, SpecTypeId.SpecificHeat, valueType); + property = CreateSpecificHeatCapacityPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); } - else if (parameterType == SpecTypeId.ColorTemperature) + + return property; + } + + /// + /// Create a ThermalConductivity measure property from the element's parameter. + /// + /// The IFC file. + /// The Element. + /// The name of the parameter. + /// The name of the property. Also, the backup name of the parameter. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateThermalConductivityPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + string ifcPropertyName, PropertyValueType valueType) + { + IList doubleValues = GetDoubleValuesFromParameterByType(elem, revitParameterName, SpecTypeId.ThermalConductivity, valueType); + IFCAnyHandle property = CreateThermalConductivityPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); + + if (property == null) { - double scaledValue = UnitUtil.ScaleDouble(SpecTypeId.ColorTemperature, propertyValue); - propertyHandle = CreateColorTemperaturePropertyFromValue(file, propertyName, scaledValue); + doubleValues = GetDoubleValuesFromParameterByType(elem, ifcPropertyName, SpecTypeId.ThermalConductivity, valueType); + property = CreateThermalConductivityPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); } - else if (parameterType == SpecTypeId.Currency) + + return property; + } + + /// + /// Create a ThermalExpansionCoefficient measure property from the element's parameter. + /// + /// The IFC file. + /// The Element. + /// The name of the parameter. + /// The name of the property. Also, the backup name of the parameter. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateThermalExpansionCoefficientPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + string ifcPropertyName, PropertyValueType valueType) + { + IList doubleValues = GetDoubleValuesFromParameterByType(elem, revitParameterName, SpecTypeId.ThermalExpansionCoefficient, valueType); + IFCAnyHandle property = CreateThermalExpansionCoefficientPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); + + if (property == null) { - IFCData currencyData = ExporterCacheManager.UnitsCache.ContainsKey("CURRENCY") ? - IFCDataUtil.CreateAsMeasure(propertyValue, "IfcMonetaryMeasure") : - IFCDataUtil.CreateAsMeasure(propertyValue, "IfcReal"); - propertyHandle = CreateCommonProperty(file, propertyName, currencyData, - valueType, null); + doubleValues = GetDoubleValuesFromParameterByType(elem, ifcPropertyName, SpecTypeId.ThermalExpansionCoefficient, valueType); + property = CreateThermalExpansionCoefficientPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); } - else if (parameterType == SpecTypeId.ApparentPower || - parameterType == SpecTypeId.ElectricalPower || - parameterType == SpecTypeId.Wattage || - parameterType == SpecTypeId.CoolingLoad || - parameterType == SpecTypeId.HeatGain || - parameterType == SpecTypeId.HeatingLoad || - parameterType == SpecTypeId.HvacPower) + + return property; + } + + /// + /// Create a ThermalResistance measure property from the element's parameter. + /// + /// The IFC file. + /// The Element. + /// The name of the parameter. + /// The name of the property. Also, the backup name of the parameter. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateThermalResistancePropertyFromElement(IFCFile file, Element elem, string revitParameterName, + string ifcPropertyName, PropertyValueType valueType) + { + IList doubleValues = GetDoubleValuesFromParameterByType(elem, revitParameterName, SpecTypeId.ThermalResistance, valueType); + IFCAnyHandle property = CreateThermalResistancePropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); + + if (property == null) { - double scaledValue = UnitUtil.ScalePower(propertyValue); - propertyHandle = CreatePowerProperty(file, propertyName, - scaledValue, valueType); + doubleValues = GetDoubleValuesFromParameterByType(elem, ifcPropertyName, SpecTypeId.ThermalResistance, valueType); + property = CreateThermalResistancePropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); } - else if (parameterType == SpecTypeId.Current) + + return property; + } + + /// + /// Create a ThermalTransmittance measure property from the element's parameter. + /// + /// The IFC file. + /// The Element. + /// The name of the parameter. + /// The name of the property. Also, the backup name of the parameter. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateThermalTransmittancePropertyFromElement(IFCFile file, Element elem, string revitParameterName, + string ifcPropertyName, PropertyValueType valueType) + { + IList doubleValues = GetDoubleValuesFromParameterByType(elem, revitParameterName, SpecTypeId.HeatTransferCoefficient, valueType); + IFCAnyHandle property = CreateThermalTransmittancePropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); + + if (property == null) { - double scaledValue = UnitUtil.ScaleElectricCurrent(propertyValue); - propertyHandle = ElectricalCurrentPropertyUtil.CreateElectricalCurrentMeasureProperty(file, propertyName, - scaledValue, valueType); + doubleValues = GetDoubleValuesFromParameterByType(elem, ifcPropertyName, SpecTypeId.HeatTransferCoefficient, valueType); + property = CreateThermalTransmittancePropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); } - else if (parameterType == SpecTypeId.Diffusivity) - { - double scaledValue = UnitUtil.ScaleMoistureDiffusivity(propertyValue); - IFCData moistureDiffusivityData = IFCDataUtil.CreateAsMoistureDiffusivityMeasure(scaledValue); - propertyHandle = CreateCommonProperty(file, propertyName, moistureDiffusivityData, - valueType, null); - } - else if (parameterType == SpecTypeId.Efficacy) - { - double scaledValue = UnitUtil.ScaleDouble(SpecTypeId.Efficacy, propertyValue); - propertyHandle = CreateElectricalEfficacyPropertyFromValue(file, propertyName, scaledValue); - } - else if (parameterType == SpecTypeId.ElectricalFrequency || - parameterType == SpecTypeId.StructuralFrequency) - { - propertyHandle = FrequencyPropertyUtil.CreateFrequencyProperty(file, propertyName, - propertyValue, valueType); - } - else if (parameterType == SpecTypeId.Illuminance) - { - double scaledValue = UnitUtil.ScaleIlluminance(propertyValue); - propertyHandle = CreateIlluminanceProperty(file, propertyName, - scaledValue, valueType); - } - else if (parameterType == SpecTypeId.LuminousFlux) - { - double scaledValue = UnitUtil.ScaleLuminousFlux(propertyValue); - propertyHandle = CreateLuminousFluxMeasureProperty(file, propertyName, - scaledValue, valueType); - } - else if (parameterType == SpecTypeId.LuminousIntensity) - { - double scaledValue = UnitUtil.ScaleLuminousIntensity(propertyValue); - propertyHandle = CreateLuminousIntensityProperty(file, propertyName, - scaledValue, valueType); - } - else if (parameterType == SpecTypeId.ElectricalPotential) - { - double scaledValue = UnitUtil.ScaleElectricVoltage(propertyValue); - propertyHandle = ElectricVoltagePropertyUtil.CreateElectricVoltageMeasureProperty(file, propertyName, - scaledValue, valueType); - } - else if (parameterType == SpecTypeId.ElectricalTemperature || - parameterType == SpecTypeId.HvacTemperature || - parameterType == SpecTypeId.PipingTemperature) - { - double scaledValue = UnitUtil.ScaleDouble(SpecTypeId.HvacTemperature, propertyValue); - IFCData temperatureData = IFCDataUtil.CreateAsMeasure(scaledValue, "IfcThermodynamicTemperatureMeasure"); - propertyHandle = CreateCommonProperty(file, propertyName, temperatureData, - valueType, null); - } - else if (parameterType == SpecTypeId.HeatTransferCoefficient) - { - double scaledValue = UnitUtil.ScaleThermalTransmittance(propertyValue); - IFCData temperatureData = IFCDataUtil.CreateAsMeasure(scaledValue, "IfcThermalTransmittanceMeasure"); - propertyHandle = CreateCommonProperty(file, propertyName, temperatureData, - valueType, null); - } - else if (parameterType == SpecTypeId.Force || - parameterType == SpecTypeId.Weight) - { - double scaledValue = UnitUtil.ScaleForce(propertyValue); - propertyHandle = CreateForceProperty(file, propertyName, - scaledValue, valueType); - } - else if (parameterType == SpecTypeId.AreaForce) - { - double scaledValue = UnitUtil.ScalePlanarForce(propertyValue); - propertyHandle = CreatePlanarForceProperty(file, propertyName, - scaledValue, valueType); - } - else if (parameterType == SpecTypeId.LinearForce || - parameterType == SpecTypeId.WeightPerUnitLength) - { - double scaledValue = UnitUtil.ScaleLinearForce(propertyValue); - propertyHandle = CreateLinearForceProperty(file, propertyName, - scaledValue, valueType); - } - else if (parameterType == SpecTypeId.AirFlow || - parameterType == SpecTypeId.Flow) - { - double scaledValue = UnitUtil.ScaleVolumetricFlowRate(propertyValue); - propertyHandle = CreateVolumetricFlowRateMeasureProperty(file, propertyName, - scaledValue, valueType); - } - else if (parameterType == SpecTypeId.HvacFriction) - { - double scaledValue = UnitUtil.ScaleDouble(SpecTypeId.HvacFriction, propertyValue); - IFCData frictionData = IFCDataUtil.CreateAsMeasure(scaledValue, "IfcReal"); - propertyHandle = CreateCommonProperty(file, propertyName, frictionData, - valueType, "FRICTIONLOSS"); - } - else if (parameterType == SpecTypeId.HvacPressure || - parameterType == SpecTypeId.PipingPressure || - parameterType == SpecTypeId.Stress) - { - double scaledValue = UnitUtil.ScalePressure(propertyValue); - IFCData pressureData = IFCDataUtil.CreateAsMeasure(scaledValue, "IfcPressureMeasure"); - propertyHandle = CreateCommonProperty(file, propertyName, pressureData, - valueType, null); - } - else if (parameterType == SpecTypeId.HvacVelocity || - parameterType == SpecTypeId.PipingVelocity || - parameterType == SpecTypeId.StructuralVelocity || - parameterType == SpecTypeId.Speed) - { - double scaledValue = UnitUtil.ScaleDouble(SpecTypeId.HvacVelocity, propertyValue); - IFCData linearVelocityData = IFCDataUtil.CreateAsMeasure(scaledValue, "IfcLinearVelocityMeasure"); - propertyHandle = CreateCommonProperty(file, propertyName, linearVelocityData, - valueType, null); - } - else if (parameterType == SpecTypeId.Mass || - parameterType == SpecTypeId.PipingMass) - { - double scaledValue = UnitUtil.ScaleDouble(SpecTypeId.Mass, propertyValue); - IFCData massData = IFCDataUtil.CreateAsMeasure(scaledValue, "IfcMassMeasure"); - propertyHandle = CreateCommonProperty(file, propertyName, massData, - valueType, null); - } - else if (parameterType == SpecTypeId.MassDensity || - parameterType == SpecTypeId.HvacDensity) - { - double scaledValue = UnitUtil.ScaleMassDensity(propertyValue); - IFCData massDensityData = IFCDataUtil.CreateAsMeasure(scaledValue, "IfcMassDensityMeasure"); - propertyHandle = CreateCommonProperty(file, propertyName, massDensityData, - valueType, null); - } - else if (parameterType == SpecTypeId.PipingDensity) - { - double scaledValue = UnitUtil.ScaleIonConcentration(propertyValue); - IFCData ionConcentrationData = IFCDataUtil.CreateAsIonConcentrationMeasure(scaledValue); - propertyHandle = CreateCommonProperty(file, propertyName, ionConcentrationData, - valueType, null); - } - else if (parameterType == SpecTypeId.MomentOfInertia) - { - double scaledValue = UnitUtil.ScaleMomentOfInertia(propertyValue); - IFCData momentOfInertiaData = IFCDataUtil.CreateAsMomentOfInertiaMeasure(scaledValue); - propertyHandle = CreateCommonProperty(file, propertyName, momentOfInertiaData, - valueType, null); - } - else if (parameterType == SpecTypeId.Number) - { - propertyHandle = CreateRealPropertyFromCache(file, propertyName, propertyValue, valueType); - } - else if (parameterType == SpecTypeId.PipingVolume || - parameterType == SpecTypeId.ReinforcementVolume || - parameterType == SpecTypeId.SectionModulus || - parameterType == SpecTypeId.Volume) - { - double scaledValue = UnitUtil.ScaleVolume(propertyValue); - propertyHandle = CreateVolumeMeasureProperty(file, propertyName, - scaledValue, valueType); - } - else if (parameterType == SpecTypeId.PipingMassPerTime || - parameterType == SpecTypeId.HvacMassPerTime) - { - double scaledValue = UnitUtil.ScaleDouble(SpecTypeId.PipingMassPerTime, propertyValue); - IFCData massFlowRateData = IFCDataUtil.CreateAsMeasure(scaledValue, "IfcMassFlowRateMeasure"); - propertyHandle = CreateCommonProperty(file, propertyName, massFlowRateData, - valueType, null); - } - else if (parameterType == SpecTypeId.AngularSpeed) - { - double scaledValue = UnitUtil.ScaleDouble(SpecTypeId.AngularSpeed, propertyValue); - IFCData rotationalFrequencyData = IFCDataUtil.CreateAsMeasure(scaledValue, "IfcRotationalFrequencyMeasure"); - propertyHandle = CreateCommonProperty(file, propertyName, rotationalFrequencyData, - valueType, null); - } - else if (parameterType == SpecTypeId.ThermalConductivity) - { - double scaledValue = UnitUtil.ScaleThermalConductivity(propertyValue); - IFCData thermalConductivityData = IFCDataUtil.CreateAsThermalConductivityMeasure(scaledValue); - propertyHandle = CreateCommonProperty(file, propertyName, thermalConductivityData, - valueType, null); - } - else if (parameterType == SpecTypeId.SpecificHeat) - { - double scaledValue = UnitUtil.ScaleSpecificHeatCapacity(propertyValue); - IFCData specificHeatData = IFCDataUtil.CreateAsSpecificHeatCapacityMeasure(scaledValue); - propertyHandle = CreateCommonProperty(file, propertyName, specificHeatData, - valueType, null); - } - else if (parameterType == SpecTypeId.Permeability) + + return property; + } + + /// + /// Create a ThermodynamicTemperature measure property from the element's parameter. + /// + /// The IFC file. + /// The Element. + /// The name of the parameter. + /// The name of the property. Also, the backup name of the parameter. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateThermodynamicTemperaturePropertyFromElement(IFCFile file, Element elem, string revitParameterName, + string ifcPropertyName, PropertyValueType valueType) + { + IList doubleValues = GetDoubleValuesFromParameterByType(elem, revitParameterName, SpecTypeId.HvacTemperature, valueType); + IFCAnyHandle property = CreateThermodynamicTemperaturePropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); + + if (property == null) { - double scaledValue = UnitUtil.ScaleVaporPermeability(propertyValue); - IFCData permeabilityData = IFCDataUtil.CreateAsVaporPermeabilityMeasure(scaledValue); - propertyHandle = CreateCommonProperty(file, propertyName, permeabilityData, - valueType, null); + doubleValues = GetDoubleValuesFromParameterByType(elem, ifcPropertyName, SpecTypeId.HvacTemperature, valueType); + property = CreateThermodynamicTemperaturePropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); } - else if (parameterType == SpecTypeId.HvacViscosity || - parameterType == SpecTypeId.PipingViscosity) + + return property; + } + + /// + /// Create a VaporPermeability measure property from the element's parameter. + /// + /// The IFC file. + /// The Element. + /// The name of the parameter. + /// The name of the property. Also, the backup name of the parameter. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateVaporPermeabilityPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + string ifcPropertyName, PropertyValueType valueType) + { + IList doubleValues = GetDoubleValuesFromParameterByType(elem, revitParameterName, SpecTypeId.Permeability, valueType); + IFCAnyHandle property = CreateVaporPermeabilityPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); + + if (property == null) { - double scaledValue = UnitUtil.ScaleDynamicViscosity(propertyValue); - IFCData hvacViscosityData = IFCDataUtil.CreateAsDynamicViscosityMeasure(scaledValue); - propertyHandle = CreateCommonProperty(file, propertyName, hvacViscosityData, - valueType, null); + doubleValues = GetDoubleValuesFromParameterByType(elem, ifcPropertyName, SpecTypeId.Permeability, valueType); + property = CreateVaporPermeabilityPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); } - else if (parameterType == SpecTypeId.ThermalExpansionCoefficient) + + return property; + } + + /// + /// Create a Volume measure property from the element's parameter. + /// + /// The IFC file. + /// The Element. + /// The name of the parameter. + /// The name of the property. Also, the backup name of the parameter. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateVolumePropertyFromElement(IFCFile file, Element elem, string revitParameterName, + string ifcPropertyName, PropertyValueType valueType) + { + IList doubleValues = GetDoubleValuesFromParameterByType(elem, revitParameterName, SpecTypeId.Volume, valueType); + IFCAnyHandle property = CreateVolumePropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); + + if (property == null) { - double scaledValue = UnitUtil.ScaleThermalExpansionCoefficient(propertyValue); - IFCData thermalExpansionCoefficientData = IFCDataUtil.CreateAsThermalExpansionCoefficientMeasure(scaledValue); - propertyHandle = CreateCommonProperty(file, propertyName, thermalExpansionCoefficientData, - valueType, null); + doubleValues = GetDoubleValuesFromParameterByType(elem, ifcPropertyName, SpecTypeId.Volume, valueType); + property = CreateVolumePropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); } - else if (parameterType == SpecTypeId.ElectricalResistivity) + + return property; + } + + /// + /// Create a VolumetricFlowRate measure property from the element's parameter. + /// + /// The IFC file. + /// The Element. + /// The name of the parameter. + /// The name of the property. Also, the backup name of the parameter. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateVolumetricFlowRatePropertyFromElement(IFCFile file, Element elem, string revitParameterName, + string ifcPropertyName, PropertyValueType valueType) + { + IList doubleValues = GetDoubleValuesFromParameterByType(elem, revitParameterName, SpecTypeId.AirFlow, valueType); + IFCAnyHandle property = CreateVolumetricFlowRatePropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); + + if (property == null) { - double scaledValue = UnitUtil.ScaleDouble(SpecTypeId.ElectricalResistivity, propertyValue); - IFCData electricalResistivityData = IFCDataUtil.CreateAsMeasure(scaledValue, "IfcReal"); - propertyHandle = CreateCommonProperty(file, propertyName, electricalResistivityData, - valueType, "ELECTRICALRESISTIVITY"); + doubleValues = GetDoubleValuesFromParameterByType(elem, ifcPropertyName, SpecTypeId.AirFlow, valueType); + property = CreateVolumetricFlowRatePropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); } - else if (parameterType == SpecTypeId.SpecificHeatOfVaporization) + + return property; + } + + /// + /// Create a Torque measure property from the element's parameter. + /// + /// The IFC file. + /// The Element. + /// The name of the parameter. + /// The name of the property. Also, the backup name of the parameter. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateTorquePropertyFromElement(IFCFile file, Element elem, string revitParameterName, + string ifcPropertyName, PropertyValueType valueType) + { + IList doubleValues = GetDoubleValuesFromParameterByType(elem, revitParameterName, SpecTypeId.Moment, valueType); + IFCAnyHandle property = CreateTorquePropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); + + if (property == null) { - double scaledValue = UnitUtil.ScaleHeatingValue(propertyValue); - IFCData heatingValueData = IFCDataUtil.CreateAsHeatingValueMeasure(scaledValue); - propertyHandle = CreateCommonProperty(file, propertyName, heatingValueData, - valueType, null); + doubleValues = GetDoubleValuesFromParameterByType(elem, ifcPropertyName, SpecTypeId.Moment, valueType); + property = CreateTorquePropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); } - else if (parameterType == SpecTypeId.IsothermalMoistureCapacity) + + return property; + } + + /// + /// Create a WarpingConstant measure property from the element's parameter. + /// + /// The IFC file. + /// The Element. + /// The name of the parameter. + /// The name of the property. Also, the backup name of the parameter. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateWarpingConstantPropertyFromElement(IFCFile file, Element elem, string revitParameterName, + string ifcPropertyName, PropertyValueType valueType) + { + IList doubleValues = GetDoubleValuesFromParameterByType(elem, revitParameterName, SpecTypeId.WarpingConstant, valueType); + IFCAnyHandle property = CreateWarpingConstantPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); + + if (property == null) { - double scaledValue = UnitUtil.ScaleIsothermalMoistureCapacity(propertyValue); - IFCData isothermalMoistureCapacityData = IFCDataUtil.CreateAsIsothermalMoistureCapacityMeasure(scaledValue); - propertyHandle = CreateCommonProperty(file, propertyName, isothermalMoistureCapacityData, - valueType, null); + doubleValues = GetDoubleValuesFromParameterByType(elem, ifcPropertyName, SpecTypeId.WarpingConstant, valueType); + property = CreateWarpingConstantPropertyFromCache(file, ifcPropertyName, doubleValues, valueType, null); } - else if (parameterType == SpecTypeId.HvacPowerDensity) + + return property; + } + #endregion + + #region Create___PropertyFromCache + + /// Create property, using a cached value if possible. + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The function to craete property. + /// The property type. + /// The created or cached property handle. + public static IFCAnyHandle CreateGenericPropertyFromCache(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey, + Func, PropertyValueType, string, IFCAnyHandle> createProperty, PropertyType propertyType) + { + if ((values?.Count ?? 0) == 0) + return null; + + bool canCache = false; + double value = 0.0; + if (values.ElementAt(0) != null && valueType == PropertyValueType.SingleValue && string.IsNullOrEmpty(unitTypeKey)) { - double scaledValue = UnitUtil.ScaleHeatFluxDensity(propertyValue); - IFCData heatFluxDensityData = IFCDataUtil.CreateAsHeatFluxDensityMeasure(scaledValue); - propertyHandle = CreateCommonProperty(file, propertyName, heatFluxDensityData, - valueType, null); + bool isLengthProeprty = (propertyType == PropertyType.Length); + value = values.ElementAt(0).Value; + + double? adjustedValue = (isLengthProeprty) ? CanCacheDouble(UnitUtil.UnscaleLength(value)) : CanCacheDouble(value); + canCache = adjustedValue.HasValue; + if (canCache) + { + value = (isLengthProeprty) ? UnitUtil.UnscaleLength(adjustedValue.GetValueOrDefault()) : adjustedValue.GetValueOrDefault(); + values[0] = value; + } } - else if (parameterType == SpecTypeId.MassPerUnitArea && !ExporterCacheManager.ExportOptionsCache.ExportAsOlderThanIFC4) + + IFCAnyHandle propertyHandle; + if (canCache) { - double scaledValue = UnitUtil.ScaleAreaDensity(propertyValue); - IFCData areaDensityData = IFCDataUtil.CreateAsAreaDensityMeasure(scaledValue); - propertyHandle = CreateCommonProperty(file, propertyName, areaDensityData, - valueType, null); + propertyHandle = ExporterCacheManager.PropertyInfoCache.GetDoubleChache(propertyType).Find(propertyName, value); + if (propertyHandle != null) + return propertyHandle; } - else if (parameterType == SpecTypeId.Time || - parameterType == SpecTypeId.Period) + + propertyHandle = createProperty(file, propertyName, values, valueType, unitTypeKey); + + if (canCache && !IFCAnyHandleUtil.IsNullOrHasNoValue(propertyHandle)) { - double scaledValue = UnitUtil.ScaleTime(propertyValue); - IFCData timeData = IFCDataUtil.CreateAsTimeMeasure(scaledValue); - propertyHandle = CreateCommonProperty(file, propertyName, timeData, - valueType, null); + ExporterCacheManager.PropertyInfoCache.GetDoubleChache(propertyType).Add(propertyName, value, propertyHandle); } - else - { - double scaledValue = propertyValue; - if (fallbackUnitType != null) - scaledValue = UnitUtils.ConvertFromInternalUnits(propertyValue, fallbackUnitType); - propertyHandle = CreateRealPropertyFromCache(file, propertyName, scaledValue, valueType); - } + return propertyHandle; + } + + + /// Create Area property, using a cached value if possible. + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created or cached property handle. + public static IFCAnyHandle CreateAreaPropertyFromCache(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericPropertyFromCache(file, propertyName, values, valueType, unitTypeKey, CreateAreaProperty, PropertyType.Area); + } + + /// Create Acceleration property, using a cached value if possible. + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created or cached property handle. + public static IFCAnyHandle CreateAccelerationPropertyFromCache(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericPropertyFromCache(file, propertyName, values, valueType, unitTypeKey, CreateAccelerationProperty, PropertyType.Acceleration); + } + + /// Create AngularVelocity property, using a cached value if possible. + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created or cached property handle. + public static IFCAnyHandle CreateAngularVelocityPropertyFromCache(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericPropertyFromCache(file, propertyName, values, valueType, unitTypeKey, CreateAngularVelocityProperty, PropertyType.AngularVelocity); + } + + /// Create AreaDensity property, using a cached value if possible. + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created or cached property handle. + public static IFCAnyHandle CreateAreaDensityPropertyFromCache(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericPropertyFromCache(file, propertyName, values, valueType, unitTypeKey, CreateAreaDensityProperty, PropertyType.AreaDensity); + } + + /// Create DynamicViscosity property, using a cached value if possible. + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created or cached property handle. + public static IFCAnyHandle CreateDynamicViscosityPropertyFromCache(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericPropertyFromCache(file, propertyName, values, valueType, unitTypeKey, CreateDynamicViscosityProperty, PropertyType.DynamicViscosity); + } + + /// Create ElectricCurrent property, using a cached value if possible. + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created or cached property handle. + public static IFCAnyHandle CreateElectricCurrentPropertyFromCache(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericPropertyFromCache(file, propertyName, values, valueType, unitTypeKey, CreateElectricCurrentProperty, PropertyType.ElectricCurrent); + } + + /// Create ElectricVoltage property, using a cached value if possible. + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created or cached property handle. + public static IFCAnyHandle CreateElectricVoltagePropertyFromCache(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericPropertyFromCache(file, propertyName, values, valueType, unitTypeKey, CreateElectricVoltageProperty, PropertyType.ElectricVoltage); + } + + /// Create Energy property, using a cached value if possible. + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created or cached property handle. + public static IFCAnyHandle CreateEnergyPropertyFromCache(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericPropertyFromCache(file, propertyName, values, valueType, unitTypeKey, CreateEnergyProperty, PropertyType.Energy); + } + + /// Create Force property, using a cached value if possible. + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created or cached property handle. + public static IFCAnyHandle CreateForcePropertyFromCache(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericPropertyFromCache(file, propertyName, values, valueType, unitTypeKey, CreateForceProperty, PropertyType.Force); + } + + /// Create Frequency property, using a cached value if possible. + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created or cached property handle. + public static IFCAnyHandle CreateFrequencyPropertyFromCache(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericPropertyFromCache(file, propertyName, values, valueType, unitTypeKey, CreateFrequencyProperty, PropertyType.Frequency); + } + + /// Create HeatingValue property, using a cached value if possible. + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created or cached property handle. + public static IFCAnyHandle CreateHeatingValuePropertyFromCache(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericPropertyFromCache(file, propertyName, values, valueType, unitTypeKey, CreateHeatingValueProperty, PropertyType.HeatingValue); + } + + /// Create Illuminance property, using a cached value if possible. + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created or cached property handle. + public static IFCAnyHandle CreateIlluminancePropertyFromCache(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericPropertyFromCache(file, propertyName, values, valueType, unitTypeKey, CreateIlluminanceProperty, PropertyType.Illuminance); + } + + /// Create IonConcentration property, using a cached value if possible. + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created or cached property handle. + public static IFCAnyHandle CreateIonConcentrationPropertyFromCache(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericPropertyFromCache(file, propertyName, values, valueType, unitTypeKey, CreateIonConcentrationProperty, PropertyType.IonConcentration); + } + + /// Create IsothermalMoistureCapacity property, using a cached value if possible. + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created or cached property handle. + public static IFCAnyHandle CreateIsothermalMoistureCapacityPropertyFromCache(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericPropertyFromCache(file, propertyName, values, valueType, unitTypeKey, CreateIsothermalMoistureCapacityProperty, PropertyType.IsothermalMoistureCapacity); + } + + /// Create HeatFluxDensity property, using a cached value if possible. + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created or cached property handle. + public static IFCAnyHandle CreateHeatFluxDensityPropertyFromCache(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericPropertyFromCache(file, propertyName, values, valueType, unitTypeKey, CreateHeatFluxDensityProperty, PropertyType.HeatFluxDensity); + } + + /// Create Length property, using a cached value if possible. + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created or cached property handle. + public static IFCAnyHandle CreateLengthPropertyFromCache(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericPropertyFromCache(file, propertyName, values, valueType, unitTypeKey, CreateLengthProperty, PropertyType.Length); + } + + /// Create LinearForce property, using a cached value if possible. + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created or cached property handle. + public static IFCAnyHandle CreateLinearForcePropertyFromCache(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericPropertyFromCache(file, propertyName, values, valueType, unitTypeKey, CreateLinearForceProperty, PropertyType.LinearForce); + } + + /// Create LinearMoment property, using a cached value if possible. + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created or cached property handle. + public static IFCAnyHandle CreateLinearMomentPropertyFromCache(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericPropertyFromCache(file, propertyName, values, valueType, unitTypeKey, CreateLinearMomentProperty, PropertyType.LinearMoment); + } + + /// Create LinearStiffness property, using a cached value if possible. + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created or cached property handle. + public static IFCAnyHandle CreateLinearStiffnessPropertyFromCache(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericPropertyFromCache(file, propertyName, values, valueType, unitTypeKey, CreateLinearStiffnessProperty, PropertyType.LinearStiffness); + } + + /// Create LinearVelocity property, using a cached value if possible. + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created or cached property handle. + public static IFCAnyHandle CreateLinearVelocityPropertyFromCache(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericPropertyFromCache(file, propertyName, values, valueType, unitTypeKey, CreateLinearVelocityProperty, PropertyType.LinearVelocity); + } + + /// Create LuminousFlux property, using a cached value if possible. + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created or cached property handle. + public static IFCAnyHandle CreateLuminousFluxPropertyFromCache(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericPropertyFromCache(file, propertyName, values, valueType, unitTypeKey, CreateLuminousFluxProperty, PropertyType.LuminousFlux); + } + + /// Create LuminousIntensity property, using a cached value if possible. + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created or cached property handle. + public static IFCAnyHandle CreateLuminousIntensityPropertyFromCache(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericPropertyFromCache(file, propertyName, values, valueType, unitTypeKey, CreateLuminousIntensityProperty, PropertyType.LuminousIntensity); + } + + /// Create Mass property, using a cached value if possible. + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created or cached property handle. + public static IFCAnyHandle CreateMassPropertyFromCache(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericPropertyFromCache(file, propertyName, values, valueType, unitTypeKey, CreateMassProperty, PropertyType.Mass); + } + + /// Create MassDensity property, using a cached value if possible. + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created or cached property handle. + public static IFCAnyHandle CreateMassDensityPropertyFromCache(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericPropertyFromCache(file, propertyName, values, valueType, unitTypeKey, CreateMassDensityProperty, PropertyType.MassDensity); + } + + /// Create MassFlowRate property, using a cached value if possible. + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created or cached property handle. + public static IFCAnyHandle CreateMassFlowRatePropertyFromCache(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericPropertyFromCache(file, propertyName, values, valueType, unitTypeKey, CreateMassFlowRateProperty, PropertyType.MassFlowRate); + } + + /// Create MassPerLength property, using a cached value if possible. + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created or cached property handle. + public static IFCAnyHandle CreateMassPerLengthPropertyFromCache(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericPropertyFromCache(file, propertyName, values, valueType, unitTypeKey, CreateMassPerLengthProperty, PropertyType.MassPerLength); + } + + /// Create ModulusOfElasticity property, using a cached value if possible. + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created or cached property handle. + public static IFCAnyHandle CreateModulusOfElasticityPropertyFromCache(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericPropertyFromCache(file, propertyName, values, valueType, unitTypeKey, CreateModulusOfElasticityProperty, PropertyType.ModulusOfElasticity); + } + + /// Create MoistureDiffusivity property, using a cached value if possible. + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created or cached property handle. + public static IFCAnyHandle CreateMoistureDiffusivityPropertyFromCache(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericPropertyFromCache(file, propertyName, values, valueType, unitTypeKey, CreateMoistureDiffusivityProperty, PropertyType.MoistureDiffusivity); + } + + /// Create MomentOfInertia property, using a cached value if possible. + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created or cached property handle. + public static IFCAnyHandle CreateMomentOfInertiaPropertyFromCache(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericPropertyFromCache(file, propertyName, values, valueType, unitTypeKey, CreateMomentOfInertiaProperty, PropertyType.MomentOfInertia); + } + + /// Create NormalisedRatio property, using a cached value if possible. + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created or cached property handle. + public static IFCAnyHandle CreateNormalisedRatioPropertyFromCache(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericPropertyFromCache(file, propertyName, values, valueType, unitTypeKey, CreateNormalisedRatioProperty, PropertyType.NormalisedRatio); + } + + /// Create Numeric property, using a cached value if possible. + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created or cached property handle. + public static IFCAnyHandle CreateNumericPropertyFromCache(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericPropertyFromCache(file, propertyName, values, valueType, unitTypeKey, CreateNumericProperty, PropertyType.Numeric); + } + + /// Create PlaneAngle property, using a cached value if possible. + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created or cached property handle. + public static IFCAnyHandle CreatePlaneAnglePropertyFromCache(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericPropertyFromCache(file, propertyName, values, valueType, unitTypeKey, CreatePlaneAngleProperty, PropertyType.PlaneAngle); + } + + /// Create PlanarForce property, using a cached value if possible. + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created or cached property handle. + public static IFCAnyHandle CreatePlanarForcePropertyFromCache(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericPropertyFromCache(file, propertyName, values, valueType, unitTypeKey, CreatePlanarForceProperty, PropertyType.PlanarForce); + } + + /// Create PositiveLength property, using a cached value if possible. + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created or cached property handle. + public static IFCAnyHandle CreatePositiveLengthPropertyFromCache(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericPropertyFromCache(file, propertyName, values, valueType, unitTypeKey, CreatePositiveLengthProperty, PropertyType.PositiveLength); + } + + /// Create PositiveRatio property, using a cached value if possible. + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created or cached property handle. + public static IFCAnyHandle CreatePositiveRatioPropertyFromCache(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericPropertyFromCache(file, propertyName, values, valueType, unitTypeKey, CreatePositiveRatioProperty, PropertyType.PositiveRatio); + } + + /// Create PositivePlaneAngle property, using a cached value if possible. + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created or cached property handle. + public static IFCAnyHandle CreatePositivePlaneAnglePropertyFromCache(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericPropertyFromCache(file, propertyName, values, valueType, unitTypeKey, CreatePositivePlaneAngleProperty, PropertyType.PositivePlaneAngle); + } + + /// Create Power property, using a cached value if possible. + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created or cached property handle. + public static IFCAnyHandle CreatePowerPropertyFromCache(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericPropertyFromCache(file, propertyName, values, valueType, unitTypeKey, CreatePowerProperty, PropertyType.Power); + } + + /// Create Pressure property, using a cached value if possible. + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created or cached property handle. + public static IFCAnyHandle CreatePressurePropertyFromCache(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericPropertyFromCache(file, propertyName, values, valueType, unitTypeKey, CreatePressureProperty, PropertyType.Pressure); + } + + /// Create Ratio property, using a cached value if possible. + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created or cached property handle. + public static IFCAnyHandle CreateRatioPropertyFromCache(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericPropertyFromCache(file, propertyName, values, valueType, unitTypeKey, CreateRatioProperty, PropertyType.Ratio); + } + + /// Create Real property, using a cached value if possible. + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created or cached property handle. + public static IFCAnyHandle CreateRealPropertyFromCache(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericPropertyFromCache(file, propertyName, values, valueType, unitTypeKey, CreateRealProperty, PropertyType.Real); + } + + /// Create RotationalFrequency property, using a cached value if possible. + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created or cached property handle. + public static IFCAnyHandle CreateRotationalFrequencyPropertyFromCache(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericPropertyFromCache(file, propertyName, values, valueType, unitTypeKey, CreateRotationalFrequencyProperty, PropertyType.RotationalFrequency); + } + + /// Create SoundPower property, using a cached value if possible. + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created or cached property handle. + public static IFCAnyHandle CreateSoundPowerPropertyFromCache(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericPropertyFromCache(file, propertyName, values, valueType, unitTypeKey, CreateSoundPowerProperty, PropertyType.SoundPower); + } + + /// Create SoundPressure property, using a cached value if possible. + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created or cached property handle. + public static IFCAnyHandle CreateSoundPressurePropertyFromCache(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericPropertyFromCache(file, propertyName, values, valueType, unitTypeKey, CreateSoundPressureProperty, PropertyType.SoundPressure); + } + + /// Create SpecificHeatCapacity property, using a cached value if possible. + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created or cached property handle. + public static IFCAnyHandle CreateSpecificHeatCapacityPropertyFromCache(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericPropertyFromCache(file, propertyName, values, valueType, unitTypeKey, CreateSpecificHeatCapacityProperty, PropertyType.SpecificHeatCapacity); + } + + /// Create ThermalConductivity property, using a cached value if possible. + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created or cached property handle. + public static IFCAnyHandle CreateThermalConductivityPropertyFromCache(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericPropertyFromCache(file, propertyName, values, valueType, unitTypeKey, CreateThermalConductivityProperty, PropertyType.ThermalConductivity); + } + + /// Create ThermalExpansionCoefficient property, using a cached value if possible. + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created or cached property handle. + public static IFCAnyHandle CreateThermalExpansionCoefficientPropertyFromCache(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericPropertyFromCache(file, propertyName, values, valueType, unitTypeKey, CreateThermalExpansionCoefficientProperty, PropertyType.ThermalExpansionCoefficient); + } + + /// Create ThermalResistance property, using a cached value if possible. + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created or cached property handle. + public static IFCAnyHandle CreateThermalResistancePropertyFromCache(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericPropertyFromCache(file, propertyName, values, valueType, unitTypeKey, CreateThermalResistanceProperty, PropertyType.ThermalResistance); + } + + /// Create ThermalTransmittance property, using a cached value if possible. + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created or cached property handle. + public static IFCAnyHandle CreateThermalTransmittancePropertyFromCache(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericPropertyFromCache(file, propertyName, values, valueType, unitTypeKey, CreateThermalTransmittanceProperty, PropertyType.ThermalTransmittance); + } + + /// Create ThermodynamicTemperature property, using a cached value if possible. + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created or cached property handle. + public static IFCAnyHandle CreateThermodynamicTemperaturePropertyFromCache(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericPropertyFromCache(file, propertyName, values, valueType, unitTypeKey, CreateThermodynamicTemperatureProperty, PropertyType.ThermodynamicTemperature); + } + + /// Create VaporPermeability property, using a cached value if possible. + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created or cached property handle. + public static IFCAnyHandle CreateVaporPermeabilityPropertyFromCache(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericPropertyFromCache(file, propertyName, values, valueType, unitTypeKey, CreateVaporPermeabilityProperty, PropertyType.VaporPermeability); + } + + /// Create Volume property, using a cached value if possible. + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created or cached property handle. + public static IFCAnyHandle CreateVolumePropertyFromCache(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericPropertyFromCache(file, propertyName, values, valueType, unitTypeKey, CreateVolumeProperty, PropertyType.Volume); + } + + /// Create VolumetricFlowRate property, using a cached value if possible. + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created or cached property handle. + public static IFCAnyHandle CreateVolumetricFlowRatePropertyFromCache(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericPropertyFromCache(file, propertyName, values, valueType, unitTypeKey, CreateVolumetricFlowRateProperty, PropertyType.VolumetricFlowRate); + } + + /// Create Torque property, using a cached value if possible. + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created or cached property handle. + public static IFCAnyHandle CreateTorquePropertyFromCache(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericPropertyFromCache(file, propertyName, values, valueType, unitTypeKey, CreateTorqueProperty, PropertyType.Torque); + } + + /// Create WarpingConstant property, using a cached value if possible. + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created or cached property handle. + public static IFCAnyHandle CreateWarpingConstantPropertyFromCache(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericPropertyFromCache(file, propertyName, values, valueType, unitTypeKey, CreateWarpingConstantProperty, PropertyType.WarpingConstant); + } + + + #endregion + + #region Create___Property + + /// + /// Create property. + /// + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The craete measure function. + /// The created property handle. + public static IFCAnyHandle CreateGenericProperty(IFCFile file, string propertyName, IList values, PropertyValueType valueType, + string unitTypeKey, Func createMeasure) + { + if (values == null) + return null; + + List dataList = new List(); + foreach (var val in values) + dataList.Add(val.HasValue ? createMeasure(val.Value) : null); + return CreateCommonPropertyFromList(file, propertyName, dataList, valueType, unitTypeKey); + } + + /// + /// Create Area property. + /// + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateAreaProperty(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericProperty(file, propertyName, values, valueType, unitTypeKey, IFCDataUtil.CreateAsAreaMeasure); + } + + /// + /// Create Acceleration property. + /// + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateAccelerationProperty(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericProperty(file, propertyName, values, valueType, unitTypeKey, IFCDataUtil.CreateAsAccelerationMeasure); + } + + /// + /// Create AngularVelocity property. + /// + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateAngularVelocityProperty(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericProperty(file, propertyName, values, valueType, unitTypeKey, IFCDataUtil.CreateAsAngularVelocityMeasure); + } + + /// + /// Create AreaDensity property. + /// + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateAreaDensityProperty(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericProperty(file, propertyName, values, valueType, unitTypeKey, IFCDataUtil.CreateAsAreaDensityMeasure); + } + + /// + /// Create DynamicViscosity property. + /// + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateDynamicViscosityProperty(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericProperty(file, propertyName, values, valueType, unitTypeKey, IFCDataUtil.CreateAsDynamicViscosityMeasure); + } + + /// + /// Create ElectricCurrent property. + /// + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateElectricCurrentProperty(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericProperty(file, propertyName, values, valueType, unitTypeKey, IFCDataUtil.CreateAsElectricCurrentMeasure); + } + + /// + /// Create ElectricVoltage property. + /// + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateElectricVoltageProperty(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericProperty(file, propertyName, values, valueType, unitTypeKey, IFCDataUtil.CreateAsElectricVoltageMeasure); + } + + /// + /// Create Energy property. + /// + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateEnergyProperty(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericProperty(file, propertyName, values, valueType, unitTypeKey, IFCDataUtil.CreateAsEnergyMeasure); + } + + /// + /// Create Force property. + /// + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateForceProperty(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericProperty(file, propertyName, values, valueType, unitTypeKey, IFCDataUtil.CreateAsForceMeasure); + } + + /// + /// Create Frequency property. + /// + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateFrequencyProperty(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericProperty(file, propertyName, values, valueType, unitTypeKey, IFCDataUtil.CreateAsFrequencyMeasure); + } + + /// + /// Create HeatingValue property. + /// + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateHeatingValueProperty(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericProperty(file, propertyName, values, valueType, unitTypeKey, IFCDataUtil.CreateAsHeatingValueMeasure); + } + + /// + /// Create Illuminance property. + /// + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateIlluminanceProperty(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericProperty(file, propertyName, values, valueType, unitTypeKey, IFCDataUtil.CreateAsIlluminanceMeasure); + } + + /// + /// Create IonConcentration property. + /// + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateIonConcentrationProperty(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericProperty(file, propertyName, values, valueType, unitTypeKey, IFCDataUtil.CreateAsIonConcentrationMeasure); + } + + /// + /// Create IsothermalMoistureCapacity property. + /// + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateIsothermalMoistureCapacityProperty(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericProperty(file, propertyName, values, valueType, unitTypeKey, IFCDataUtil.CreateAsIsothermalMoistureCapacityMeasure); + } + + /// + /// Create HeatFluxDensity property. + /// + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateHeatFluxDensityProperty(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericProperty(file, propertyName, values, valueType, unitTypeKey, IFCDataUtil.CreateAsHeatFluxDensityMeasure); + } + + /// + /// Create Length property. + /// + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateLengthProperty(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericProperty(file, propertyName, values, valueType, unitTypeKey, IFCDataUtil.CreateAsLengthMeasure); + } + + /// + /// Create LinearForce property. + /// + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateLinearForceProperty(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericProperty(file, propertyName, values, valueType, unitTypeKey, IFCDataUtil.CreateAsLinearForceMeasure); + } + + /// + /// Create LinearMoment property. + /// + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateLinearMomentProperty(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericProperty(file, propertyName, values, valueType, unitTypeKey, IFCDataUtil.CreateAsLinearMomentMeasure); + } + + /// + /// Create LinearStiffness property. + /// + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateLinearStiffnessProperty(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericProperty(file, propertyName, values, valueType, unitTypeKey, IFCDataUtil.CreateAsLinearStiffnessMeasure); + } + + /// + /// Create LinearVelocity property. + /// + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateLinearVelocityProperty(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericProperty(file, propertyName, values, valueType, unitTypeKey, IFCDataUtil.CreateAsLinearVelocityMeasure); + } + + /// + /// Create LuminousFlux property. + /// + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateLuminousFluxProperty(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericProperty(file, propertyName, values, valueType, unitTypeKey, IFCDataUtil.CreateAsLuminousFluxMeasure); + } + + /// + /// Create LuminousIntensity property. + /// + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateLuminousIntensityProperty(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericProperty(file, propertyName, values, valueType, unitTypeKey, IFCDataUtil.CreateAsLuminousIntensityMeasure); + } + + /// + /// Create Mass property. + /// + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateMassProperty(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericProperty(file, propertyName, values, valueType, unitTypeKey, IFCDataUtil.CreateAsMassMeasure); + } + + /// + /// Create MassDensity property. + /// + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateMassDensityProperty(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericProperty(file, propertyName, values, valueType, unitTypeKey, IFCDataUtil.CreateAsMassDensityMeasure); + } + + /// + /// Create MassFlowRate property. + /// + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateMassFlowRateProperty(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericProperty(file, propertyName, values, valueType, unitTypeKey, IFCDataUtil.CreateAsMassFlowRateMeasure); + } + + /// + /// Create MassPerLength property. + /// + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateMassPerLengthProperty(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericProperty(file, propertyName, values, valueType, unitTypeKey, IFCDataUtil.CreateAsMassPerLengthMeasure); + } + + /// + /// Create ModulusOfElasticity property. + /// + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateModulusOfElasticityProperty(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericProperty(file, propertyName, values, valueType, unitTypeKey, IFCDataUtil.CreateAsModulusOfElasticityMeasure); + } + + /// + /// Create MoistureDiffusivity property. + /// + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateMoistureDiffusivityProperty(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericProperty(file, propertyName, values, valueType, unitTypeKey, IFCDataUtil.CreateAsMoistureDiffusivityMeasure); + } + + /// + /// Create MomentOfInertia property. + /// + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateMomentOfInertiaProperty(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericProperty(file, propertyName, values, valueType, unitTypeKey, IFCDataUtil.CreateAsMomentOfInertiaMeasure); + } + + /// + /// Create NormalisedRatio property. + /// + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateNormalisedRatioProperty(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericProperty(file, propertyName, values, valueType, unitTypeKey, IFCDataUtil.CreateAsNormalisedRatioMeasure); + } + + /// + /// Create Numeric property. + /// + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateNumericProperty(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericProperty(file, propertyName, values, valueType, unitTypeKey, IFCDataUtil.CreateAsNumeric); + } + + /// + /// Create PlaneAngle property. + /// + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreatePlaneAngleProperty(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericProperty(file, propertyName, values, valueType, unitTypeKey, IFCDataUtil.CreateAsPlaneAngleMeasure); + } + + /// + /// Create PlanarForce property. + /// + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreatePlanarForceProperty(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericProperty(file, propertyName, values, valueType, unitTypeKey, IFCDataUtil.CreateAsPlanarForceMeasure); + } - return propertyHandle; + /// + /// Create PositiveLength property. + /// + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreatePositiveLengthProperty(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericProperty(file, propertyName, values, valueType, unitTypeKey, IFCDataUtil.CreateAsPositiveLengthMeasure); } /// - /// Creates and associates the common property sets associated with ElementTypes. These are handled differently than for elements. + /// Create PositiveRatio property. /// - /// The IFC exporter object. - /// The element type whose properties are exported. - /// The handles of property sets already associated with the type. - /// The handle of the entity associated with the element type object. - public static void CreateElementTypeProperties(ExporterIFC exporterIFC, ElementType elementType, - HashSet existingPropertySets, IFCAnyHandle prodTypeHnd) + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreatePositiveRatioProperty(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) { - HashSet propertySets = new HashSet(); + return CreateGenericProperty(file, propertyName, values, valueType, unitTypeKey, IFCDataUtil.CreateAsPositiveRatioMeasure); + } - // Pass in an empty set of handles - we don't want IfcRelDefinesByProperties for type properties. - ISet associatedObjectIds = new HashSet(); - CreateInternalRevitPropertySets(exporterIFC, elementType, associatedObjectIds, false); + /// + /// Create PositivePlaneAngle property. + /// + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreatePositivePlaneAngleProperty(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericProperty(file, propertyName, values, valueType, unitTypeKey, IFCDataUtil.CreateAsPositivePlaneAngleMeasure); + } - TypePropertyInfo additionalPropertySets = null; - ElementId typeId = elementType.Id; - if (ExporterCacheManager.TypePropertyInfoCache.TryGetValue(typeId, out additionalPropertySets)) - propertySets.UnionWith(additionalPropertySets.PropertySets); + /// + /// Create Power property. + /// + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreatePowerProperty(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericProperty(file, propertyName, values, valueType, unitTypeKey, IFCDataUtil.CreateAsPowerMeasure); + } - if (existingPropertySets != null && existingPropertySets.Count > 0) - propertySets.UnionWith(existingPropertySets); + /// + /// Create Pressure property. + /// + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreatePressureProperty(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericProperty(file, propertyName, values, valueType, unitTypeKey, IFCDataUtil.CreateAsPressureMeasure); + } - IFCFile file = exporterIFC.GetFile(); - using (IFCTransaction transaction = new IFCTransaction(file)) - { - Document doc = elementType.Document; + /// + /// Create Ratio property. + /// + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateRatioProperty(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericProperty(file, propertyName, values, valueType, unitTypeKey, IFCDataUtil.CreateAsRatioMeasure); + } - IFCAnyHandle ownerHistory = ExporterCacheManager.OwnerHistoryHandle; + /// + /// Create Real property. + /// + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateRealProperty(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericProperty(file, propertyName, values, valueType, unitTypeKey, IFCDataUtil.CreateAsReal); + } - IList currPsetsToCreate = - ExporterUtil.GetCurrPSetsToCreate(prodTypeHnd, PSetsToProcess.Type); - foreach (PropertySetDescription currDesc in currPsetsToCreate) - { - // Last conditional check: if the property set comes from a ViewSchedule, check if the element is in the schedule. - if (currDesc.ViewScheduleId != ElementId.InvalidElementId) - if (!ExporterCacheManager.ViewScheduleElementCache[currDesc.ViewScheduleId].Contains(typeId)) - continue; + /// + /// Create RotationalFrequency property. + /// + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateRotationalFrequencyProperty(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericProperty(file, propertyName, values, valueType, unitTypeKey, IFCDataUtil.CreateAsRotationalFrequencyMeasure); + } - ElementOrConnector elementOrConnector = new ElementOrConnector(elementType); - ISet props = currDesc.ProcessEntries(file, exporterIFC, null, elementOrConnector, elementType, prodTypeHnd); - if (props.Count > 0) - { - string paramSetName = currDesc.Name; - string guid = GUIDUtil.GenerateIFCGuidFrom( - GUIDUtil.CreateGUIDString(IFCEntityType.IfcPropertySet, paramSetName, prodTypeHnd)); + /// + /// Create SoundPower property. + /// + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateSoundPowerProperty(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericProperty(file, propertyName, values, valueType, unitTypeKey, IFCDataUtil.CreateAsSoundPowerMeasure); + } - IFCAnyHandle propertySet = IFCInstanceExporter.CreatePropertySet(file, guid, ownerHistory, paramSetName, null, props); - propertySets.Add(propertySet); - } - } + /// + /// Create SoundPressure property. + /// + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateSoundPressureProperty(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericProperty(file, propertyName, values, valueType, unitTypeKey, IFCDataUtil.CreateAsSoundPressureMeasure); + } - if (propertySets.Count != 0) - { - prodTypeHnd.SetAttribute("HasPropertySets", propertySets); - // Don't assign the property sets to the instances if we have just assigned them to the type. - if (additionalPropertySets != null) - additionalPropertySets.AssignedToType = true; - } + /// + /// Create SpecificHeatCapacity property. + /// + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateSpecificHeatCapacityProperty(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericProperty(file, propertyName, values, valueType, unitTypeKey, IFCDataUtil.CreateAsSpecificHeatCapacityMeasure); + } - transaction.Commit(); - } + /// + /// Create ThermalConductivity property. + /// + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateThermalConductivityProperty(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericProperty(file, propertyName, values, valueType, unitTypeKey, IFCDataUtil.CreateAsThermalConductivityMeasure); + } + + /// + /// Create ThermalExpansionCoefficient property. + /// + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateThermalExpansionCoefficientProperty(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericProperty(file, propertyName, values, valueType, unitTypeKey, IFCDataUtil.CreateAsThermalExpansionCoefficientMeasure); + } + + /// + /// Create ThermalResistance property. + /// + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateThermalResistanceProperty(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericProperty(file, propertyName, values, valueType, unitTypeKey, IFCDataUtil.CreateAsThermalResistanceMeasure); + } + + /// + /// Create ThermalTransmittance property. + /// + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateThermalTransmittanceProperty(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericProperty(file, propertyName, values, valueType, unitTypeKey, IFCDataUtil.CreateAsThermalTransmittanceMeasure); + } + + /// + /// Create ThermodynamicTemperature property. + /// + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateThermodynamicTemperatureProperty(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericProperty(file, propertyName, values, valueType, unitTypeKey, IFCDataUtil.CreateAsThermodynamicTemperatureMeasure); + } + + /// + /// Create VaporPermeability property. + /// + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateVaporPermeabilityProperty(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericProperty(file, propertyName, values, valueType, unitTypeKey, IFCDataUtil.CreateAsVaporPermeabilityMeasure); + } + + /// + /// Create Volume property. + /// + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateVolumeProperty(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericProperty(file, propertyName, values, valueType, unitTypeKey, IFCDataUtil.CreateAsVolumeMeasure); + } + + /// + /// Create VolumetricFlowRate property. + /// + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateVolumetricFlowRateProperty(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericProperty(file, propertyName, values, valueType, unitTypeKey, IFCDataUtil.CreateAsVolumetricFlowRateMeasure); + } + + /// + /// Create Torque property. + /// + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateTorqueProperty(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericProperty(file, propertyName, values, valueType, unitTypeKey, IFCDataUtil.CreateAsTorqueMeasure); + } + + /// + /// Create WarpingConstant property. + /// + /// The IFC file. + /// The name of the property. + /// The values of the property. + /// The value type of the property. + /// The created property handle. + public static IFCAnyHandle CreateWarpingConstantProperty(IFCFile file, string propertyName, IList values, PropertyValueType valueType, string unitTypeKey) + { + return CreateGenericProperty(file, propertyName, values, valueType, unitTypeKey, IFCDataUtil.CreateAsWarpingConstantMeasure); } + #endregion + } } \ No newline at end of file diff --git a/Source/Revit.IFC.Export/Exporter/ProxyElementExporter.cs b/Source/Revit.IFC.Export/Exporter/ProxyElementExporter.cs index f2fec6ff..bf64d725 100644 --- a/Source/Revit.IFC.Export/Exporter/ProxyElementExporter.cs +++ b/Source/Revit.IFC.Export/Exporter/ProxyElementExporter.cs @@ -49,7 +49,7 @@ class ProxyElementExporter if (exportType == null) { - exportType = new IFCExportInfoPair(IFCEntityType.IfcBuildingElementProxy, IFCEntityType.IfcBuildingElementProxyType, "NOTDEFINED"); + exportType = new IFCExportInfoPair(IFCEntityType.IfcBuildingElementProxy, IFCEntityType.IfcBuildingElementProxyType, null); } IFCFile file = exporterIFC.GetFile(); diff --git a/Source/Revit.IFC.Export/Exporter/RailingExporter.cs b/Source/Revit.IFC.Export/Exporter/RailingExporter.cs index b8adcc09..db74023a 100644 --- a/Source/Revit.IFC.Export/Exporter/RailingExporter.cs +++ b/Source/Revit.IFC.Export/Exporter/RailingExporter.cs @@ -144,7 +144,7 @@ public static void ExportRailingElement(ExporterIFC exporterIFC, Railing railing IFCExportInfoPair exportType = ExporterUtil.GetProductExportType(exporterIFC, railing, out ifcEnumType); if (exportType.IsUnKnown) { - ifcEnumType = ExporterUtil.GetIFCTypeFromExportTable(exporterIFC, railing); + ifcEnumType = ExporterUtil.GetIFCTypeFromExportTable(railing); } ExportRailing(exporterIFC, railing, geomElement, ifcEnumType, productWrapper); diff --git a/Source/Revit.IFC.Export/Exporter/RampExporter.cs b/Source/Revit.IFC.Export/Exporter/RampExporter.cs index 182f0940..05c708e4 100644 --- a/Source/Revit.IFC.Export/Exporter/RampExporter.cs +++ b/Source/Revit.IFC.Export/Exporter/RampExporter.cs @@ -369,9 +369,9 @@ public static string GetIFCRampType(string rampTypeName) string predefType = ifcEnumType; IFCExportInfoPair exportTypePair = ExporterUtil.GetProductExportType(exporterIFC, ramp, out ifcEnumType); - if (!string.IsNullOrEmpty(exportTypePair.ValidatedPredefinedType)) + if (!exportTypePair.IsPredefinedTypeDefault) { - predefType = exportTypePair.ValidatedPredefinedType; + predefType = exportTypePair.PredefinedType; } SortedDictionary> rampFlights = null; @@ -579,7 +579,7 @@ public static string GetIFCRampType(string rampTypeName) IFCAnyHandle localPlacement = ecData.GetLocalPlacement(); IFCAnyHandle rampHnd = IFCInstanceExporter.CreateRamp(exporterIFC, ramp, guid, ownerHistory, - localPlacement, representation, exportTypePair.ValidatedPredefinedType); + localPlacement, representation, exportTypePair.GetPredefinedTypeOrDefault()); productWrapper.AddElement(ramp, rampHnd, placementSetter.LevelInfo, ecData, true, exportTypePair); CategoryUtil.CreateMaterialAssociation(exporterIFC, rampHnd, bodyData.MaterialIds); @@ -593,7 +593,7 @@ public static string GetIFCRampType(string rampTypeName) List components = new List(); IList componentExtrusionData = new List(); IFCAnyHandle containedRampHnd = IFCInstanceExporter.CreateRamp(exporterIFC, ramp, containedRampGuid, ownerHistory, - containedRampLocalPlacement, representation, exportTypePair.ValidatedPredefinedType); + containedRampLocalPlacement, representation, exportTypePair.GetPredefinedTypeOrDefault()); components.Add(containedRampHnd); componentExtrusionData.Add(ecData); //productWrapper.AddElement(containedRampHnd, placementSetter.LevelInfo, ecData, false); @@ -603,7 +603,7 @@ public static string GetIFCRampType(string rampTypeName) IFCAnyHandle localPlacement = ecData.GetLocalPlacement(); IFCAnyHandle rampHnd = IFCInstanceExporter.CreateRamp(exporterIFC, ramp, guid, ownerHistory, - localPlacement, null, exportTypePair.ValidatedPredefinedType); + localPlacement, null, exportTypePair.GetPredefinedTypeOrDefault()); productWrapper.AddElement(ramp, rampHnd, placementSetter.LevelInfo, ecData, true, exportTypePair); string typeGuid = GUIDUtil.CreateGUID(rampType); @@ -633,19 +633,19 @@ public static string GetIFCRampType(string rampTypeName) /// The ProductWrapper. public static void Export(ExporterIFC exporterIFC, Element element, GeometryElement geometryElement, ProductWrapper productWrapper) { - string ifcEnumType = ExporterUtil.GetIFCTypeFromExportTable(exporterIFC, element); + IFCExportInfoPair exportType = ExporterUtil.GetProductExportType(exporterIFC, element, out _); IFCFile file = exporterIFC.GetFile(); using (IFCTransaction tr = new IFCTransaction(file)) { - StairsExporter.ExportLegacyStairOrRampAsContainer(exporterIFC, ifcEnumType, element, geometryElement, productWrapper); + StairsExporter.ExportLegacyStairOrRampAsContainer(exporterIFC, exportType.GetPredefinedTypeOrDefault(), element, geometryElement, productWrapper); // If we didn't create a handle here, then the element wasn't a "native" Ramp, and is likely a FamilyInstance or a DirectShape. if (IFCAnyHandleUtil.IsNullOrHasNoValue(productWrapper.GetAnElement())) { int numFlights = GetNumFlightsForRamp(exporterIFC, element); if (numFlights > 0) - ExportRamp(exporterIFC, ifcEnumType, element, geometryElement, numFlights, productWrapper); + ExportRamp(exporterIFC, exportType.PredefinedType, element, geometryElement, numFlights, productWrapper); } tr.Commit(); diff --git a/Source/Revit.IFC.Export/Exporter/RebarCouplerExporter.cs b/Source/Revit.IFC.Export/Exporter/RebarCouplerExporter.cs index ba853da7..ba410d4b 100644 --- a/Source/Revit.IFC.Export/Exporter/RebarCouplerExporter.cs +++ b/Source/Revit.IFC.Export/Exporter/RebarCouplerExporter.cs @@ -28,6 +28,7 @@ using Revit.IFC.Export.Exporter.PropertySet; using Revit.IFC.Common.Utility; using Revit.IFC.Common.Enums; +using System.Reflection; namespace Revit.IFC.Export.Exporter @@ -53,22 +54,22 @@ public static void ExportCoupler(ExporterIFC exporterIFC, RebarCoupler coupler, if (familySymbol == null) return; + string ifcEnumType; + IFCExportInfoPair exportType = ExporterUtil.GetProductExportType(exporterIFC, coupler, out ifcEnumType); + // Check the intended IFC entity or type name is in the exclude list specified in the UI - IFCEntityType elementClassTypeEnum = IFCEntityType.IfcMechanicalFastener; - if (ExporterCacheManager.ExportOptionsCache.IsElementInExcludeList(elementClassTypeEnum)) + if (ExporterCacheManager.ExportOptionsCache.IsElementInExcludeList(exportType.ExportInstance)) return; ElementId categoryId = CategoryUtil.GetSafeCategoryId(coupler); IFCFile file = exporterIFC.GetFile(); IFCAnyHandle ownerHistory = ExporterCacheManager.OwnerHistoryHandle; - Options options = GeometryUtil.GetIFCExportGeometryOptions(); ; - string ifcEnumType; - IFCExportInfoPair exportType = ExporterUtil.GetProductExportType(exporterIFC, coupler, out ifcEnumType); + Options options = GeometryUtil.GetIFCExportGeometryOptions(); using (IFCTransaction tr = new IFCTransaction(file)) { - var typeKey = new TypeObjectKey(typeId, ElementId.InvalidElementId, false, exportType, ElementId.InvalidElementId); + TypeObjectKey typeKey = new TypeObjectKey(typeId, ElementId.InvalidElementId, false, exportType, ElementId.InvalidElementId); FamilyTypeInfo currentTypeInfo = ExporterCacheManager.FamilySymbolToTypeInfoCache.Find(typeKey); @@ -90,8 +91,7 @@ public static void ExportCoupler(ExporterIFC exporterIFC, RebarCoupler coupler, repMap.Add(IFCInstanceExporter.CreateRepresentationMap(file, origin, bodyData.RepresentationHnd)); string typeGuid = GUIDUtil.GenerateIFCGuidFrom(familySymbol, exportType); - IFCAnyHandle styleHandle = FamilyExporterUtil.ExportGenericType(exporterIFC, exportType, - ifcEnumType, propertySetsOpt, repMap, coupler, familySymbol, typeGuid); + IFCAnyHandle styleHandle = FamilyExporterUtil.ExportGenericType(exporterIFC, exportType, propertySetsOpt, repMap, coupler, familySymbol, typeGuid); productWrapper.RegisterHandleWithElementType(familySymbol, exportType, styleHandle, propertySetsOpt); if (!IFCAnyHandleUtil.IsNullOrHasNoValue(styleHandle)) @@ -108,6 +108,8 @@ public static void ExportCoupler(ExporterIFC exporterIFC, RebarCoupler coupler, ISet createdRebarCouplerHandles = new HashSet(); string origInstanceName = NamingUtil.GetNameOverride(coupler, NamingUtil.GetIFCName(coupler)); + bool hasTypeInfo = !IFCAnyHandleUtil.IsNullOrHasNoValue(currentTypeInfo.Style); + for (int idx = 0; idx < nCouplerQuantity; idx++) { string instanceGUID = GUIDUtil.GenerateIFCGuidFrom( @@ -158,6 +160,11 @@ public static void ExportCoupler(ExporterIFC exporterIFC, RebarCoupler coupler, createdRebarCouplerHandles.Add(instanceHandle); productWrapper.AddElement(coupler, instanceHandle, setter, null, true, exportType); + + if (hasTypeInfo) + { + ExporterCacheManager.TypeRelationsCache.Add(currentTypeInfo.Style, instanceHandle); + } } } diff --git a/Source/Revit.IFC.Export/Exporter/RebarExporter.cs b/Source/Revit.IFC.Export/Exporter/RebarExporter.cs index cc805b1a..69df5f05 100644 --- a/Source/Revit.IFC.Export/Exporter/RebarExporter.cs +++ b/Source/Revit.IFC.Export/Exporter/RebarExporter.cs @@ -313,7 +313,7 @@ private static IFCReinforcingBarRole GetReinforcingBarRole(string role) IFCAnyHandle localPlacement = ecData.GetLocalPlacement(); buildingElementProxy = IFCInstanceExporter.CreateBuildingElementProxy(exporterIFC, element, guid, - ownerHistory, localPlacement, representation, exportType.ValidatedPredefinedType); + ownerHistory, localPlacement, representation, exportType.GetPredefinedTypeOrDefault()); productWrapper.AddElement(element, buildingElementProxy, placementSetter.LevelInfo, ecData, true, exportType); } @@ -483,7 +483,7 @@ private static ISet ExportRebar(ExporterIFC exporterIFC, continue; Rebar rebar = rebarElement as Rebar; - if ((rebar != null) && (rebar.DistributionType == DistributionType.VaryingLength || rebar.IsRebarFreeForm())) + if (rebar != null && rebar.CanHaveVaryingLengthBars) { baseCurves = GetRebarCenterlineCurves(rebar, true, false, false, MultiplanarOption.IncludeAllMultiplanarCurves, ii); DoubleParameterValue barLengthParamVal = rebar.GetParameterValueAtIndex(barLengthParamId, ii) as DoubleParameterValue; @@ -841,7 +841,7 @@ static void CacheSubelementParameterValues(Element element, ParameterSet paramet if (element is Rebar) { Rebar rebar = element as Rebar; - if (rebar.DistributionType != DistributionType.VaryingLength && !rebar.IsRebarFreeForm()) + if (!rebar.CanHaveVaryingLengthBars) return; foreach (Parameter param in parameters) diff --git a/Source/Revit.IFC.Export/Exporter/RoofExporter.cs b/Source/Revit.IFC.Export/Exporter/RoofExporter.cs index e6709f15..10800495 100644 --- a/Source/Revit.IFC.Export/Exporter/RoofExporter.cs +++ b/Source/Revit.IFC.Export/Exporter/RoofExporter.cs @@ -26,6 +26,7 @@ using Revit.IFC.Common.Utility; using Revit.IFC.Common.Enums; using System.Linq; +using Revit.IFC.Export.Exporter.PropertySet; namespace Revit.IFC.Export.Exporter { @@ -43,12 +44,16 @@ class RoofExporter /// The roof element. /// The geometry element. /// The ProductWrapper. - /// Export roof as single geometry. - public static void ExportRoof(ExporterIFC exporterIFC, Element roof, ref GeometryElement geometryElement, - ProductWrapper productWrapper, bool exportRoofAsSingleGeometry = false) + public static IFCAnyHandle ExportRoof(ExporterIFC exporterIFC, Element roof, ref GeometryElement geometryElement, + ProductWrapper productWrapper) { if (roof == null || geometryElement == null) - return; + return null; + + bool exportRoofAsSingleGeometry = ExporterCacheManager.ExportOptionsCache.ExportHostAsSingleEntity || + (productWrapper != null && + !ExporterCacheManager.ExportOptionsCache.ExportAsOlderThanIFC4 && + IFCAnyHandleUtil.IsNullOrHasNoValue(productWrapper.GetAnElement())); string ifcEnumType; IFCExportInfoPair roofExportType = ExporterUtil.GetProductExportType(exporterIFC, roof, out ifcEnumType); @@ -61,6 +66,7 @@ class RoofExporter IFCFile file = exporterIFC.GetFile(); Document doc = roof.Document; + IFCAnyHandle roofHnd = null; using (SubTransaction tempPartTransaction = new SubTransaction(doc)) { // For IFC4RV export, Roof will be split into its parts(temporarily) in order to export the roof by its parts @@ -69,7 +75,8 @@ class RoofExporter ExporterUtil.CreateParts(roof, layersetInfo.MaterialIds.Count, ref geometryElement); } - bool exportByComponents = ExporterUtil.CanExportByComponentsOrParts(roof) == ExporterUtil.ExportPartAs.ShapeAspect; + bool exportByComponents = + ExporterUtil.CanExportByComponentsOrParts(roof, ref geometryElement) == ExporterUtil.ExportPartAs.ShapeAspect; using (IFCTransaction tr = new IFCTransaction(file)) { @@ -102,16 +109,11 @@ class RoofExporter categoryId, geometryElement, bodyExporterOptions, null, ecData, out bodyData, instanceGeometry: true); if (bodyData != null && bodyData.MaterialIds != null) materialIds = bodyData.MaterialIds; - } - else - { - prodRep = RepresentationUtil.CreateProductDefinitionShapeWithoutBodyRep(exporterIFC, roof, categoryId, geometryElement, representations); - } - - if (IFCAnyHandleUtil.IsNullOrHasNoValue(prodRep)) - { - ecData.ClearOpenings(); - return; + if (IFCAnyHandleUtil.IsNullOrHasNoValue(prodRep)) + { + ecData.ClearOpenings(); + return null; + } } bool exportSlab = ((ecData.ScaledLength > MathUtil.Eps() || exportByComponents) && @@ -121,9 +123,23 @@ class RoofExporter IFCAnyHandle ownerHistory = ExporterCacheManager.OwnerHistoryHandle; IFCAnyHandle localPlacement = ecData.GetLocalPlacement(); - IFCAnyHandle roofHnd = IFCInstanceExporter.CreateGenericIFCEntity( - roofExportType, exporterIFC, roof, guid, ownerHistory, - localPlacement, exportSlab ? null : prodRep); + if (exportByComponents) + { + if (exportSlab || exportRoofAsSingleGeometry) + { + prodRep = RepresentationUtil.CreateProductDefinitionShapeWithoutBodyRep(exporterIFC, roof, categoryId, geometryElement, representations); + IFCAnyHandle hostShapeRepFromParts = PartExporter.ExportHostPartAsShapeAspects(exporterIFC, roof, prodRep, + productWrapper, placementSetter, localPlacement, ElementId.InvalidElementId, layersetInfo, ecData); + } + else + { + ecData.ClearOpenings(); + return null; + } + } + + roofHnd = IFCInstanceExporter.CreateGenericIFCEntity(roofExportType, exporterIFC, roof, guid, + ownerHistory, localPlacement, exportSlab ? null : prodRep); IFCAnyHandle typeHnd = ExporterUtil.CreateGenericTypeFromElement(roof, roofExportType, file, productWrapper); @@ -134,12 +150,6 @@ class RoofExporter if (!(roof is RoofBase)) CategoryUtil.CreateMaterialAssociation(exporterIFC, roofHnd, materialIds); - if (exportByComponents && (exportSlab || exportRoofAsSingleGeometry)) - { - IFCAnyHandle hostShapeRepFromParts = PartExporter.ExportHostPartAsShapeAspects(exporterIFC, roof, prodRep, - productWrapper, placementSetter, localPlacement, ElementId.InvalidElementId, layersetInfo, ecData); - } - Transform offsetTransform = (bodyData != null) ? bodyData.OffsetTransform : Transform.Identity; if (exportSlab) @@ -194,7 +204,7 @@ class RoofExporter } else if (layersetInfo != null && layersetInfo.MaterialIds != null) { - materialIds = layersetInfo.MaterialIds.Select(x => x.m_baseMatId).ToList(); + materialIds = layersetInfo.MaterialIds.Select(x => x.BaseMatId).ToList(); CategoryUtil.CreateMaterialAssociation(exporterIFC, roofHnd, materialIds); } } @@ -204,6 +214,8 @@ class RoofExporter } } } + + return roofHnd; } /// @@ -222,11 +234,6 @@ public static void Export(ExporterIFC exporterIFC, RoofBase roof, ref GeometryEl bool exportParts = PartExporter.CanExportParts(roof); bool exportAsCurtainRoof = CurtainSystemExporter.IsCurtainSystem(roof); - // if there is only a single part that we can get from the roof geometry, we will not create the aggregation with IfcSlab, but directly export the IfcRoof - bool exportAsSingleGeometry = false; - if (productWrapper != null && !ExporterCacheManager.ExportOptionsCache.ExportAsOlderThanIFC4) - exportAsSingleGeometry = IFCAnyHandleUtil.IsNullOrHasNoValue(productWrapper.GetAnElement()); - if (exportParts) { if (!PartExporter.CanExportElementInPartExport(roof, roof.LevelId, false)) @@ -239,26 +246,26 @@ public static void Export(ExporterIFC exporterIFC, RoofBase roof, ref GeometryEl } else { - string ifcEnumType; - IFCExportInfoPair roofExportType = ExporterUtil.GetProductExportType(exporterIFC, roof, out ifcEnumType); + IFCExportInfoPair roofExportType = ExporterUtil.GetProductExportType(exporterIFC, roof, out _); + IFCAnyHandle roofHnd = null; if (roofExportType.ExportInstance != IFCEntityType.IfcRoof) { - ExportRoof(exporterIFC, roof, ref geometryElement, productWrapper, exportAsSingleGeometry); + roofHnd = ExportRoof(exporterIFC, roof, ref geometryElement, productWrapper); } else { - IFCAnyHandle roofHnd = ExportRoofOrFloorAsContainer(exporterIFC, roof, + roofHnd = ExportRoofOrFloorAsContainer(exporterIFC, roof, geometryElement, productWrapper); if (IFCAnyHandleUtil.IsNullOrHasNoValue(roofHnd)) { - ExportRoof(exporterIFC, roof, ref geometryElement, productWrapper, exportAsSingleGeometry); + roofHnd = ExportRoof(exporterIFC, roof, ref geometryElement, productWrapper); } } // call for host objects; curtain roofs excused from call (no material information) if (!ExporterCacheManager.ExportOptionsCache.ExportAs4ReferenceView) - HostObjectExporter.ExportHostObjectMaterials(exporterIFC, roof, productWrapper.GetAnElement(), + HostObjectExporter.ExportHostObjectMaterials(exporterIFC, roof, roofHnd, geometryElement, productWrapper, ElementId.InvalidElementId, IFCLayerSetDirection.Axis3, null, null); } tr.Commit(); @@ -266,14 +273,16 @@ public static void Export(ExporterIFC exporterIFC, RoofBase roof, ref GeometryEl } /// - /// Exports a roof or floor as a container of multiple roof slabs. Returns the handle, if successful. + /// Exports a roof or floor as a container of multiple roof slabs. Returns the handle, + /// if successful. /// /// The exporter. /// The roof or floor element. /// The geometry of the element. /// The product wrapper. /// The roof handle. - /// For floors, if there is only one component, return null, as we do not want to create a container. + /// For floors, if there is only one component, return null, as we do not want to + /// create a container. public static IFCAnyHandle ExportRoofOrFloorAsContainer(ExporterIFC exporterIFC, Element element, GeometryElement geometry, ProductWrapper productWrapper) { @@ -285,8 +294,7 @@ public static void Export(ExporterIFC exporterIFC, RoofBase roof, ref GeometryEl if (!elementIsRoof && !elementIsFloor) return null; - IFCExportInfoPair roofExportType = ExporterUtil.GetProductExportType(exporterIFC, element, - out _); + IFCExportInfoPair roofExportType = ExporterUtil.GetProductExportType(exporterIFC, element, out _); if (roofExportType.IsUnKnown) { IFCEntityType elementClassTypeEnum = @@ -303,18 +311,14 @@ public static void Export(ExporterIFC exporterIFC, RoofBase roof, ref GeometryEl { using (IFCTransaction transaction = new IFCTransaction(file)) { - MaterialLayerSetInfo layersetInfo = new MaterialLayerSetInfo(exporterIFC, element, - productWrapper); - bool hasLayers = false; - if (layersetInfo.MaterialIds.Count > 1) - hasLayers = true; - bool exportByComponents = - ExporterCacheManager.ExportOptionsCache.ExportAs4ReferenceView && hasLayers; + MaterialLayerSetInfo layersetInfo = new MaterialLayerSetInfo(exporterIFC, element, productWrapper); + bool hasLayers = (layersetInfo.MaterialIds.Count > 1); + bool exportByComponents = ExporterCacheManager.ExportOptionsCache.ExportAs4ReferenceView && hasLayers; // Check for containment override IFCAnyHandle overrideContainerHnd = null; - ElementId overrideContainerId = ParameterUtil.OverrideContainmentParameter(exporterIFC, - element, out overrideContainerHnd); + ElementId overrideContainerId = ParameterUtil.OverrideContainmentParameter(exporterIFC, element, + out overrideContainerHnd); // We want to delay creating entity handles until as late as possible, so that if we // abort the IFC transaction, we don't have to delete elements. This is both for @@ -323,21 +327,27 @@ public static void Export(ExporterIFC exporterIFC, RoofBase roof, ref GeometryEl IList hostObjectSubcomponents = null; try { - hostObjectSubcomponents = - ExporterIFCUtils.ComputeSubcomponents(element as HostObject); + hostObjectSubcomponents = ExporterIFCUtils.ComputeSubcomponents(element as HostObject); + if (hostObjectSubcomponents == null) + return null; } catch { return null; } - if (hostObjectSubcomponents == null) - return null; + bool createSubcomponents = !ExporterCacheManager.ExportOptionsCache.ExportHostAsSingleEntity; int numSubcomponents = hostObjectSubcomponents.Count; if (numSubcomponents == 0 || (elementIsFloor && numSubcomponents == 1)) + { return null; + } + // TODO: If we find exactly 1 sub-component and we are exporting a floor, then we will + // continue, but with createSubcomponents set to false. + // if (createSubcomponents && elementIsFloor && numSubcomponents == 1) createSubcomponents = false; + using (PlacementSetter setter = PlacementSetter.Create(exporterIFC, element, null, null, overrideContainerId, overrideContainerHnd)) { IFCAnyHandle localPlacement = setter.LocalPlacement; @@ -353,27 +363,12 @@ public static void Export(ExporterIFC exporterIFC, RoofBase roof, ref GeometryEl using (TransformSetter trfSetter = TransformSetter.Create()) { - IList geometryList = new List(); - geometryList.Add(geometry); + IList geometryList = new List() { geometry }; trfSetter.InitializeFromBoundingBox(exporterIFC, geometryList, extrusionCreationData); - IFCAnyHandle prodRepHnd = null; - - string elementGUID = GUIDUtil.CreateGUID(element); - - hostObjectHandle = IFCInstanceExporter.CreateGenericIFCEntity( - roofExportType, exporterIFC, element, elementGUID, ownerHistory, - localPlacement, prodRepHnd); - - if (IFCAnyHandleUtil.IsNullOrHasNoValue(hostObjectHandle)) - return null; - - IList elementHandles = new List(); - elementHandles.Add(hostObjectHandle); - // If element is floor, then the profile curve loop of hostObjectSubComponent is computed from the top face of the floor // else if element is roof, then the profile curve loop is taken from the bottom face of the roof instead - XYZ extrusionDir = elementIsFloor ? new XYZ(0, 0, -1) : new XYZ(0, 0, 1); + XYZ extrusionDir = elementIsFloor ? -XYZ.BasisZ : XYZ.BasisZ; ElementId catId = CategoryUtil.GetSafeCategoryId(element); @@ -382,103 +377,134 @@ public static void Export(ExporterIFC exporterIFC, RoofBase roof, ref GeometryEl IList hostObjectOpeningLoops = new List(); double maximumScaledDepth = 0.0; - using (IFCExportBodyParams slabExtrusionCreationData = new IFCExportBodyParams()) + int loopNum = 0; + int subElementStart = elementIsRoof ? (int)IFCRoofSubElements.RoofSlabStart : (int)IFCSlabSubElements.SubSlabStart; + + // Figure out the appropriate slabExportType from the main handle. + IFCExportInfoPair subInfoPair; + switch (roofExportType.ExportInstance) { - slabExtrusionCreationData.SetLocalPlacement(extrusionCreationData.GetLocalPlacement()); - slabExtrusionCreationData.ReuseLocalPlacement = false; - slabExtrusionCreationData.ForceOffset = true; + case IFCEntityType.IfcRoof: + subInfoPair = new IFCExportInfoPair(IFCEntityType.IfcSlab, "Roof"); + break; + case IFCEntityType.IfcSlab: + subInfoPair = roofExportType; + break; + default: + subInfoPair = new IFCExportInfoPair(IFCEntityType.IfcBuildingElementPart); + break; + } - int loopNum = 0; - int subElementStart = elementIsRoof ? (int)IFCRoofSubElements.RoofSlabStart : (int)IFCSlabSubElements.SubSlabStart; + List hostBodyItems = new List(); + IFCAnyHandle contextOfItems = ExporterCacheManager.Get3DContextHandle(IFCRepresentationIdentifier.Body); + IList elementHandles = new List(); - // Figure out the appropriate slabExportType from the main handle. - IFCExportInfoPair subInfoPair; - switch (roofExportType.ExportInstance) + foreach (HostObjectSubcomponentInfo hostObjectSubcomponent in hostObjectSubcomponents) + { + IFCExportBodyParams slabExtrusionCreationData = null; + + if (createSubcomponents) { - case IFCEntityType.IfcRoof: - subInfoPair = new IFCExportInfoPair(IFCEntityType.IfcSlab, "Roof"); - break; - case IFCEntityType.IfcSlab: - subInfoPair = roofExportType; - break; - default: - subInfoPair = new IFCExportInfoPair(IFCEntityType.IfcBuildingElementPart); - break; + slabExtrusionCreationData = new IFCExportBodyParams(); + + slabExtrusionCreationData.SetLocalPlacement(extrusionCreationData.GetLocalPlacement()); + slabExtrusionCreationData.ReuseLocalPlacement = false; + slabExtrusionCreationData.ForceOffset = true; + + trfSetter.InitializeFromBoundingBox(exporterIFC, geometryList, slabExtrusionCreationData); } - foreach (HostObjectSubcomponentInfo hostObjectSubcomponent in hostObjectSubcomponents) + Plane plane = hostObjectSubcomponent.GetPlane(); + Transform lcs = GeometryUtil.CreateTransformFromPlane(plane); + + IList curveLoops = new List(); + CurveLoop slabCurveLoop = hostObjectSubcomponent.GetCurveLoop(); + curveLoops.Add(slabCurveLoop); + + double slope = Math.Abs(plane.Normal.Z); + double scaledDepth = UnitUtil.ScaleLength(hostObjectSubcomponent.Depth); + double scaledExtrusionDepth = scaledDepth * slope; + IList matLayerNames = new List(); + + // Create representation items based on the layers + // Because in this case, the Roof components are not derived from Parts, but by "splitting" geometry part that can be extruded, + // the creation of the Items for IFC4RV will be different by using "manual" split based on the layer thickness + IList bodyItems = new List(); + if (!exportByComponents) { - trfSetter.InitializeFromBoundingBox(exporterIFC, geometryList, slabExtrusionCreationData); - Plane plane = hostObjectSubcomponent.GetPlane(); - Transform lcs = GeometryUtil.CreateTransformFromPlane(plane); - - IList curveLoops = new List(); - - CurveLoop slabCurveLoop = hostObjectSubcomponent.GetCurveLoop(); - curveLoops.Add(slabCurveLoop); - double slope = Math.Abs(plane.Normal.Z); - double scaledDepth = UnitUtil.ScaleLength(hostObjectSubcomponent.Depth); - double scaledExtrusionDepth = scaledDepth * slope; - IList shapeReps = new List(); - IFCAnyHandle prodDefShape = IFCInstanceExporter.CreateProductDefinitionShape(file, null, null, shapeReps); - IFCAnyHandle contextOfItems = ExporterCacheManager.Get3DContextHandle(IFCRepresentationIdentifier.Body); - string representationType = ShapeRepresentationType.SweptSolid.ToString(); - - // Create representation items based on the layers - // Because in this case, the Roof components are not derived from Parts, but by "splitting" geometry part that can be extruded, - // the creation of the Items for IFC4RV will be different by using "manual" split based on the layer thickness - HashSet bodyItems = new HashSet(); - if (!exportByComponents) + IFCAnyHandle itemShapeRep = + ExtrusionExporter.CreateExtrudedSolidFromCurveLoop(exporterIFC, + null, curveLoops, lcs, extrusionDir, scaledExtrusionDepth, false, + out IList validatedCurveLoops); + if (IFCAnyHandleUtil.IsNullOrHasNoValue(itemShapeRep)) + { + productWrapper.ClearInternalHandleWrapperData(element); + if ((validatedCurveLoops?.Count ?? 0) == 0) continue; + + return null; + } + ElementId matId = HostObjectExporter.GetFirstLayerMaterialId(element as HostObject); + BodyExporter.CreateSurfaceStyleForRepItem(exporterIFC, + element.Document, false, itemShapeRep, matId); + bodyItems.Add(itemShapeRep); + } + else + { + List MaterialIds = layersetInfo.MaterialIds; + ElementId typeElemId = element.GetTypeId(); + // From CollectMaterialLayerSet() Roofs with no components are only allowed one material. It arbitrarily chooses the thickest material. + // To be consistant with Roof(as Slab), we will reverse the order. + IFCAnyHandle materialLayerSet = ExporterCacheManager.MaterialSetCache.FindLayerSet(typeElemId); + bool materialHandleIsNotValid = IFCAnyHandleUtil.IsNullOrHasNoValue(materialLayerSet); + if (IFCAnyHandleUtil.IsNullOrHasNoValue(materialLayerSet) || materialHandleIsNotValid) + MaterialIds.Reverse(); + + double scaleProj = extrusionDir.DotProduct(plane.Normal); + foreach (MaterialLayerSetInfo.MaterialInfo matLayerInfo in MaterialIds) { - IFCAnyHandle itemShapeRep = ExtrusionExporter.CreateExtrudedSolidFromCurveLoop(exporterIFC, null, curveLoops, lcs, extrusionDir, scaledExtrusionDepth, false, out IList validatedCurveLoops); + double itemExtrDepth = matLayerInfo.Width; + double scaledItemExtrDepth = UnitUtil.ScaleLength(itemExtrDepth) * slope; + IFCAnyHandle itemShapeRep = ExtrusionExporter.CreateExtrudedSolidFromCurveLoop(exporterIFC, null, curveLoops, lcs, extrusionDir, scaledItemExtrDepth, false, out _); if (IFCAnyHandleUtil.IsNullOrHasNoValue(itemShapeRep)) { productWrapper.ClearInternalHandleWrapperData(element); - if ((validatedCurveLoops?.Count ?? 0) == 0) continue; - return null; } - ElementId matId = HostObjectExporter.GetFirstLayerMaterialId(element as HostObject); - BodyExporter.CreateSurfaceStyleForRepItem(exporterIFC, element.Document, false, itemShapeRep, matId); + + BodyExporter.CreateSurfaceStyleForRepItem(exporterIFC, element.Document, false, itemShapeRep, matLayerInfo.BaseMatId); + bodyItems.Add(itemShapeRep); + matLayerNames.Add(matLayerInfo.LayerName); + + XYZ offset = new XYZ(0, 0, itemExtrDepth / scaleProj); // offset is calculated as extent in the direction of extrusion + lcs.Origin += offset; } - else + } + + if (createSubcomponents) + { + IList shapeReps = new List { - List MaterialIds = layersetInfo.MaterialIds; - ElementId typeElemId = element.GetTypeId(); - // From CollectMaterialLayerSet() Roofs with no components are only allowed one material. It arbitrarily chooses the thickest material. - // To be consistant with Roof(as Slab), we will reverse the order. - IFCAnyHandle materialLayerSet = ExporterCacheManager.MaterialSetCache.FindLayerSet(typeElemId); - bool materialHandleIsNotValid = IFCAnyHandleUtil.IsNullOrHasNoValue(materialLayerSet); - if (IFCAnyHandleUtil.IsNullOrHasNoValue(materialLayerSet) || materialHandleIsNotValid) - MaterialIds.Reverse(); - - double scaleProj = extrusionDir.DotProduct(plane.Normal); - foreach (MaterialLayerSetInfo.MaterialInfo matLayerInfo in MaterialIds) + RepresentationUtil.CreateSweptSolidRep(exporterIFC, element, + catId, contextOfItems, bodyItems.ToHashSet(), null, null) + }; + + IFCAnyHandle prodDefShape = + IFCInstanceExporter.CreateProductDefinitionShape(file, null, null, + shapeReps); + + if (exportByComponents) + { + string representationType = ShapeRepresentationType.SweptSolid.ToString(); + int count = bodyItems.Count(); + for (int ii = 0; ii < count; ii++) { - double itemExtrDepth = matLayerInfo.m_matWidth; - double scaledItemExtrDepth = UnitUtil.ScaleLength(itemExtrDepth) * slope; - IFCAnyHandle itemShapeRep = ExtrusionExporter.CreateExtrudedSolidFromCurveLoop(exporterIFC, null, curveLoops, lcs, extrusionDir, scaledItemExtrDepth, false, out _); - if (IFCAnyHandleUtil.IsNullOrHasNoValue(itemShapeRep)) - { - productWrapper.ClearInternalHandleWrapperData(element); - return null; - } - - BodyExporter.CreateSurfaceStyleForRepItem(exporterIFC, element.Document, false, itemShapeRep, matLayerInfo.m_baseMatId); - - bodyItems.Add(itemShapeRep); - RepresentationUtil.CreateRepForShapeAspect(exporterIFC, element, prodDefShape, representationType, matLayerInfo.m_layerName, itemShapeRep); - - XYZ offset = new XYZ(0, 0, itemExtrDepth / scaleProj); // offset is calculated as extent in the direction of extrusion - lcs.Origin += offset; + RepresentationUtil.CreateRepForShapeAspect(exporterIFC, element, + prodDefShape, representationType, matLayerNames[ii], + bodyItems[ii]); } } - IFCAnyHandle shapeRep = RepresentationUtil.CreateSweptSolidRep(exporterIFC, element, catId, contextOfItems, bodyItems, null, null); - shapeReps.Add(shapeRep); - IFCAnyHandleUtil.SetAttribute(prodDefShape, "Representations", shapeReps); - // We could replace the code below to just use the newer, and better, // GenerateIFCGuidFrom. The code below maintains compatibility with older // versions while generating a stable GUID for all slabs (in the unlikely @@ -499,10 +525,13 @@ public static void Export(ExporterIFC exporterIFC, RoofBase roof, ref GeometryEl slabExtrusionCreationData.ScaledOuterPerimeter = UnitUtil.ScaleLength(curveLoops[0].GetExactLength()); slabExtrusionCreationData.Slope = UnitUtil.ScaleAngle(MathUtil.SafeAcos(Math.Abs(slope))); + if (ExporterCacheManager.ExportOptionsCache.ExportBaseQuantities) + PropertyUtil.CreateSlabBaseQuantities(exporterIFC, slabHnd, slabExtrusionCreationData, curveLoops[0]); + productWrapper.AddElement(null, slabHnd, setter, slabExtrusionCreationData, false, roofExportType); // Create type - IFCAnyHandle slabRoofTypeHnd = ExporterUtil.CreateGenericTypeFromElement(element, + IFCAnyHandle slabRoofTypeHnd = ExporterUtil.CreateGenericTypeFromElement(element, roofExportType, exporterIFC.GetFile(), productWrapper); ExporterCacheManager.TypeRelationsCache.Add(slabRoofTypeHnd, slabHnd); @@ -521,8 +550,33 @@ public static void Export(ExporterIFC exporterIFC, RoofBase roof, ref GeometryEl CategoryUtil.CreateMaterialAssociation(slabHnd, layersetInfo.MaterialLayerSetHandle); } } + else + { + hostBodyItems.AddRange(bodyItems); + } + } + + IFCAnyHandle hostProdDefShape = null; + if (hostBodyItems.Count() > 0) + { + IList shapeReps = new List + { + RepresentationUtil.CreateSweptSolidRep(exporterIFC, element, + catId, contextOfItems, hostBodyItems.ToHashSet(), null, null) + }; + + hostProdDefShape = IFCInstanceExporter.CreateProductDefinitionShape( + file, null, null, shapeReps); } + string elementGUID = GUIDUtil.CreateGUID(element); + + hostObjectHandle = IFCInstanceExporter.CreateGenericIFCEntity( + roofExportType, exporterIFC, element, elementGUID, ownerHistory, + localPlacement, hostProdDefShape); + + elementHandles.Add(hostObjectHandle); + productWrapper.AddElement(element, hostObjectHandle, setter, extrusionCreationData, true, roofExportType); ExporterUtil.RelateObjects(exporterIFC, null, hostObjectHandle, slabHandles); @@ -613,38 +667,38 @@ public static string GetIFCRoofType(string roofTypeName) { string typeName = NamingUtil.RemoveSpacesAndUnderscores(roofTypeName); - if (String.Compare(typeName, "ROOFTYPEENUM", true) == 0 || - String.Compare(typeName, "ROOFTYPEENUMFREEFORM", true) == 0) + if (string.Compare(typeName, "ROOFTYPEENUM", true) == 0 || + string.Compare(typeName, "ROOFTYPEENUMFREEFORM", true) == 0) return "FREEFORM"; - if (String.Compare(typeName, "FLAT", true) == 0 || - String.Compare(typeName, "FLATROOF", true) == 0) + if (string.Compare(typeName, "FLAT", true) == 0 || + string.Compare(typeName, "FLATROOF", true) == 0) return "FLAT_ROOF"; - if (String.Compare(typeName, "SHED", true) == 0 || - String.Compare(typeName, "SHEDROOF", true) == 0) + if (string.Compare(typeName, "SHED", true) == 0 || + string.Compare(typeName, "SHEDROOF", true) == 0) return "SHED_ROOF"; - if (String.Compare(typeName, "GABLE", true) == 0 || - String.Compare(typeName, "GABLEROOF", true) == 0) + if (string.Compare(typeName, "GABLE", true) == 0 || + string.Compare(typeName, "GABLEROOF", true) == 0) return "GABLE_ROOF"; - if (String.Compare(typeName, "HIP", true) == 0 || - String.Compare(typeName, "HIPROOF", true) == 0) + if (string.Compare(typeName, "HIP", true) == 0 || + string.Compare(typeName, "HIPROOF", true) == 0) return "HIP_ROOF"; - if (String.Compare(typeName, "HIPPED_GABLE", true) == 0 || - String.Compare(typeName, "HIPPED_GABLEROOF", true) == 0) + if (string.Compare(typeName, "HIPPED_GABLE", true) == 0 || + string.Compare(typeName, "HIPPED_GABLEROOF", true) == 0) return "HIPPED_GABLE_ROOF"; - if (String.Compare(typeName, "MANSARD", true) == 0 || - String.Compare(typeName, "MANSARDROOF", true) == 0) + if (string.Compare(typeName, "MANSARD", true) == 0 || + string.Compare(typeName, "MANSARDROOF", true) == 0) return "MANSARD_ROOF"; - if (String.Compare(typeName, "BARREL", true) == 0 || - String.Compare(typeName, "BARRELROOF", true) == 0) + if (string.Compare(typeName, "BARREL", true) == 0 || + string.Compare(typeName, "BARRELROOF", true) == 0) return "BARREL_ROOF"; - if (String.Compare(typeName, "BUTTERFLY", true) == 0 || - String.Compare(typeName, "BUTTERFLYROOF", true) == 0) + if (string.Compare(typeName, "BUTTERFLY", true) == 0 || + string.Compare(typeName, "BUTTERFLYROOF", true) == 0) return "BUTTERFLY_ROOF"; - if (String.Compare(typeName, "PAVILION", true) == 0 || - String.Compare(typeName, "PAVILIONROOF", true) == 0) + if (string.Compare(typeName, "PAVILION", true) == 0 || + string.Compare(typeName, "PAVILIONROOF", true) == 0) return "PAVILION_ROOF"; - if (String.Compare(typeName, "DOME", true) == 0 || - String.Compare(typeName, "DOMEROOF", true) == 0) + if (string.Compare(typeName, "DOME", true) == 0 || + string.Compare(typeName, "DOMEROOF", true) == 0) return "DOME_ROOF"; return typeName; //return unchanged. Validation for ENUM will be done later specific to schema version diff --git a/Source/Revit.IFC.Export/Exporter/SpatialElementExporter.cs b/Source/Revit.IFC.Export/Exporter/SpatialElementExporter.cs index c433cac9..c626b1db 100644 --- a/Source/Revit.IFC.Export/Exporter/SpatialElementExporter.cs +++ b/Source/Revit.IFC.Export/Exporter/SpatialElementExporter.cs @@ -27,7 +27,7 @@ using Revit.IFC.Export.Exporter.PropertySet; using Revit.IFC.Common.Enums; using Revit.IFC.Common.Utility; - +using System.Linq; namespace Revit.IFC.Export.Exporter { @@ -88,7 +88,7 @@ public static void ExportSpatialElement(ExporterIFC exporterIFC, SpatialElement // Force the default export to IfcSpace for Spatial Element if it is set to UnKnown if (exportInfo.IsUnKnown) { - exportInfo.SetValueWithPair(IFCEntityType.IfcSpace, ifcEnumType); + exportInfo.SetByTypeAndPredefinedType(IFCEntityType.IfcSpace, ifcEnumType); } // Check the intended IFC entity or type name is in the exclude list specified in the UI @@ -101,18 +101,21 @@ public static void ExportSpatialElement(ExporterIFC exporterIFC, SpatialElement using (PlacementSetter setter = PlacementSetter.Create(exporterIFC, spatialElement, null, null)) { SpatialElementGeometryResults spatialElemGeomResult = null; - if (!CreateIFCSpace(exporterIFC, spatialElement, productWrapper, setter, exportInfo, out spatialElemGeomResult)) + IFCAnyHandle spaceHnd = CreateIFCSpace(exporterIFC, spatialElement, + productWrapper, setter, exportInfo, out spatialElemGeomResult); + if (IFCAnyHandleUtil.IsNullOrHasNoValue(spaceHnd)) + { return; - - bool isArea = (spatialElement is Area); + } // Do not create boundary information for areas. - if (!isArea && (ExporterCacheManager.ExportOptionsCache.SpaceBoundaryLevel == 1)) + if (!(spatialElement is Area) && + (ExporterCacheManager.ExportOptionsCache.SpaceBoundaryLevel == 1)) { Document document = spatialElement.Document; ElementId levelId = spatialElement.LevelId; IFCLevelInfo levelInfo = exporterIFC.GetLevelInfo(levelId); - double baseHeightNonScaled = (levelInfo != null) ? levelInfo.Elevation : 0.0; + double baseHeightNonScaled = spatialElement.Level?.Elevation ?? 0.0; try { @@ -145,7 +148,9 @@ public static void ExportSpatialElement(ExporterIFC exporterIFC, SpatialElement if (boundingElement == null) continue; - bool isObjectExt = CategoryUtil.IsElementExternal(boundingElement); + IFCInternalOrExternal internalOrExternal = IFCInternalOrExternal.NotDefined; + if (CategoryUtil.IsElementExternal(boundingElement).HasValue) + internalOrExternal = CategoryUtil.IsElementExternal(boundingElement).Value ? IFCInternalOrExternal.External : IFCInternalOrExternal.Internal; IFCGeometryInfo info = IFCGeometryInfo.CreateSurfaceGeometryInfo(spatialElement.Document.Application.VertexTolerance); @@ -162,7 +167,7 @@ public static void ExportSpatialElement(ExporterIFC exporterIFC, SpatialElement boundingElement.Id, setter.LevelId, connectionGeometry, IFCPhysicalOrVirtual.Physical, - isObjectExt ? IFCInternalOrExternal.External : IFCInternalOrExternal.Internal); + internalOrExternal); if (!ProcessIFCSpaceBoundary(exporterIFC, spaceBoundary, file)) ExporterCacheManager.SpaceBoundaryCache.Add(spaceBoundary); @@ -204,7 +209,10 @@ public static void ExportSpatialElement(ExporterIFC exporterIFC, SpatialElement else if (boundingElement is Autodesk.Revit.DB.Architecture.Room) physOrVirt = IFCPhysicalOrVirtual.NotDefined; - bool isObjectExt = CategoryUtil.IsElementExternal(boundingElement); + IFCInternalOrExternal internalOrExternal = IFCInternalOrExternal.NotDefined; + if (CategoryUtil.IsElementExternal(boundingElement).HasValue) + internalOrExternal = CategoryUtil.IsElementExternal(boundingElement).Value ? IFCInternalOrExternal.External : IFCInternalOrExternal.Internal; + bool isObjectPhys = (physOrVirt == IFCPhysicalOrVirtual.Physical); SpaceBoundary spaceBoundary = new SpaceBoundary(null, @@ -212,8 +220,8 @@ public static void ExportSpatialElement(ExporterIFC exporterIFC, SpatialElement boundingElement.Id, setter.LevelId, !IFCAnyHandleUtil.IsNullOrHasNoValue(connectionGeometry) ? connectionGeometry : null, - physOrVirt, - isObjectExt ? IFCInternalOrExternal.External : IFCInternalOrExternal.Internal); + physOrVirt, + internalOrExternal); if (!ProcessIFCSpaceBoundary(exporterIFC, spaceBoundary, file)) ExporterCacheManager.SpaceBoundaryCache.Add(spaceBoundary); @@ -340,7 +348,7 @@ public static void ExportSpatialElement(ExporterIFC exporterIFC, SpatialElement spatialElement.Id, elemId, setter.LevelId, !IFCAnyHandleUtil.IsNullOrHasNoValue(insConnectionGeom) ? insConnectionGeom : null, physOrVirt, - isObjectExt ? IFCInternalOrExternal.External : IFCInternalOrExternal.Internal); + internalOrExternal); if (!ProcessIFCSpaceBoundary(exporterIFC, instBoundary, file)) ExporterCacheManager.SpaceBoundaryCache.Add(instBoundary); } @@ -348,9 +356,6 @@ public static void ExportSpatialElement(ExporterIFC exporterIFC, SpatialElement } } } - - CreateZoneInfos(exporterIFC, file, spatialElement, productWrapper); - CreateSpaceOccupantInfo(file, spatialElement, productWrapper); } transaction.Commit(); } @@ -363,7 +368,7 @@ public static void ExportSpatialElement(ExporterIFC exporterIFC, SpatialElement /// The ExporterIFC object. /// The Revit document. /// The set of exported spaces. This is used to try to export using the standard routine for spaces that failed. - public static ISet ExportSpatialElement2ndLevel(Revit.IFC.Export.Exporter.Exporter ifcExporter, ExporterIFC exporterIFC, Document document) + public static ISet ExportSpatialElement2ndLevel(Exporter ifcExporter, ExporterIFC exporterIFC, Document document) { ISet exportedSpaceIds = new HashSet(); @@ -403,7 +408,7 @@ public static ISet ExportSpatialElement2ndLevel(Revit.IFC.Export.Expo if (!ElementFilteringUtil.IsElementVisible(spatialElement)) continue; - if (!ElementFilteringUtil.ShouldElementBeExported(exporterIFC, spatialElement, false)) + if (!ElementFilteringUtil.ShouldElementBeExported(spatialElement, false)) continue; if (ElementFilteringUtil.IsRoomInInvalidPhase(spatialElement)) @@ -425,8 +430,12 @@ public static ISet ExportSpatialElement2ndLevel(Revit.IFC.Export.Expo { // We won't use the SpatialElementGeometryResults, as these are 1st level boundaries, not 2nd level. SpatialElementGeometryResults results = null; - if (!CreateIFCSpace(exporterIFC, spatialElement, productWrapper, setter, null, out results)) + IFCAnyHandle spaceHnd = CreateIFCSpace(exporterIFC, + spatialElement, productWrapper, setter, null, out results); + if (IFCAnyHandleUtil.IsNullOrHasNoValue(spaceHnd)) + { continue; + } exportedSpaceIds.Add(spatialElement.Id); @@ -456,8 +465,6 @@ public static ISet ExportSpatialElement2ndLevel(Revit.IFC.Export.Expo } } } - CreateZoneInfos(exporterIFC, file, spatialElement, productWrapper); - CreateSpaceOccupantInfo(file, spatialElement, productWrapper); ExporterUtil.ExportRelatedProperties(exporterIFC, spatialElement, productWrapper); } @@ -506,12 +513,14 @@ public static ISet ExportSpatialElement2ndLevel(Revit.IFC.Export.Expo else if (boundingElement is Autodesk.Revit.DB.Architecture.Room) physOrVirt = IFCPhysicalOrVirtual.NotDefined; - bool isObjectExt = CategoryUtil.IsElementExternal(boundingElement); + IFCInternalOrExternal internalOrExternal = IFCInternalOrExternal.NotDefined; + if (CategoryUtil.IsElementExternal(boundingElement).HasValue) + internalOrExternal = CategoryUtil.IsElementExternal(boundingElement).Value ? IFCInternalOrExternal.External : IFCInternalOrExternal.Internal; SpaceBoundary spaceBoundary = new SpaceBoundary(name, spatialElement.Id, boundingElement?.Id ?? ElementId.InvalidElementId, levelId, connectionGeometry, physOrVirt, - isObjectExt ? IFCInternalOrExternal.External : IFCInternalOrExternal.Internal); + internalOrExternal); if (!ProcessIFCSpaceBoundary(exporterIFC, spaceBoundary, file)) ExporterCacheManager.SpaceBoundaryCache.Add(spaceBoundary); @@ -658,13 +667,12 @@ static IFCAnyHandle CreateConnectionSurfaceGeometry(ExporterIFC exporterIFC, Ene static double GetScaledHeight(SpatialElement spatialElement, ElementId levelId, IFCLevelInfo levelInfo) { Document document = spatialElement.Document; - bool isArea = spatialElement is Area; - + ElementId topLevelId = ElementId.InvalidElementId; double topOffset = 0.0; // These values are internally set for areas, but are invalid. Ignore them and just use the level height. - if (!isArea) + if (!(spatialElement is Area)) { ParameterUtil.GetElementIdValueFromElement(spatialElement, BuiltInParameter.ROOM_UPPER_LEVEL, out topLevelId); ParameterUtil.GetDoubleValueFromElement(spatialElement, BuiltInParameter.ROOM_UPPER_OFFSET, out topOffset); @@ -815,6 +823,23 @@ static XYZ GetSpaceBoundaryOffset(PlacementSetter setter) "Space Category (BOMA)", "http://www.BOMA.org"); } + static void AddAreaToAreaScheme(Element spatialElement, IFCAnyHandle spaceHnd) + { + Element areaScheme = (spatialElement as Area)?.AreaScheme; + + if (areaScheme != null) + { + ElementId areaSchemeId = areaScheme.Id; + HashSet areas = null; + if (!ExporterCacheManager.AreaSchemeCache.TryGetValue(areaSchemeId, out areas)) + { + areas = new HashSet(); + ExporterCacheManager.AreaSchemeCache[areaSchemeId] = areas; + } + areas.Add(spaceHnd); + } + } + /// /// Creates IFC room/space/area item, not include boundaries. /// @@ -822,8 +847,8 @@ static XYZ GetSpaceBoundaryOffset(PlacementSetter setter) /// The spatial element. /// The ProductWrapper. /// The PlacementSetter. - /// True if created successfully, false otherwise. - static bool CreateIFCSpace(ExporterIFC exporterIFC, SpatialElement spatialElement, ProductWrapper productWrapper, + /// Returns the created handle, or null. + static IFCAnyHandle CreateIFCSpace(ExporterIFC exporterIFC, SpatialElement spatialElement, ProductWrapper productWrapper, PlacementSetter setter, IFCExportInfoPair exportInfo, out SpatialElementGeometryResults results) { results = null; @@ -833,7 +858,7 @@ static XYZ GetSpaceBoundaryOffset(PlacementSetter setter) // Avoid throwing for a spatial element with no location. if (spatialElement.Location == null) - return false; + return null; IList curveLoops = null; GeometryElement geomElem = null; @@ -876,7 +901,7 @@ static XYZ GetSpaceBoundaryOffset(PlacementSetter setter) double scaledRoomHeight = GetScaledHeight(spatialElement, levelId, levelInfo); if (scaledRoomHeight <= 0.0) - return false; + return null; double dArea = 0.0; // Will be calculated later. IFCFile file = exporterIFC.GetFile(); @@ -914,23 +939,23 @@ static XYZ GetSpaceBoundaryOffset(PlacementSetter setter) { curveLoops = ExporterIFCUtils.GetRoomBoundaryAsCurveLoopArray(spatialElement, options, true); if (curveLoops == null || curveLoops.Count == 0) - return false; + return null; } catch { - return false; + return null; } double bottomOffset; ParameterUtil.GetDoubleValueFromElement(spatialElement, BuiltInParameter.ROOM_LOWER_OFFSET, out bottomOffset); - double elevation = (levelInfo != null) ? levelInfo.Elevation : 0.0; + double elevation = spatialElement.Level?.Elevation ?? 0.0; XYZ orig = new XYZ(0, 0, elevation + bottomOffset); Transform lcs = Transform.CreateTranslation(orig); // room calculated as level offset. IFCAnyHandle shapeRep = ExtrusionExporter.CreateExtrudedSolidFromCurveLoop(exporterIFC, null, curveLoops, lcs, XYZ.BasisZ, scaledRoomHeight, true, out _); if (IFCAnyHandleUtil.IsNullOrHasNoValue(shapeRep)) - return false; + return null; // Spaces shouldn't have styled items. HashSet bodyItems = new HashSet() { shapeRep }; @@ -954,9 +979,11 @@ static XYZ GetSpaceBoundaryOffset(PlacementSetter setter) if (exportInfo.ExportInstance == IFCEntityType.IfcSpace) { - IFCInternalOrExternal internalOrExternal = CategoryUtil.IsElementExternal(spatialElement) ? IFCInternalOrExternal.External : IFCInternalOrExternal.Internal; - string preDefinedType = string.IsNullOrWhiteSpace(exportInfo.ValidatedPredefinedType) ? - "NOTDEFINED" : exportInfo.ValidatedPredefinedType; + IFCInternalOrExternal internalOrExternal = IFCInternalOrExternal.NotDefined; + if(CategoryUtil.IsElementExternal(spatialElement).HasValue) + internalOrExternal = CategoryUtil.IsElementExternal(spatialElement).Value ? IFCInternalOrExternal.External : IFCInternalOrExternal.Internal; + + string preDefinedType = exportInfo.GetPredefinedTypeOrDefault(); spaceHnd = IFCInstanceExporter.CreateSpace(exporterIFC, spatialElement, GUIDUtil.CreateGUID(spatialElement), ExporterCacheManager.OwnerHistoryHandle, @@ -981,21 +1008,7 @@ static XYZ GetSpaceBoundaryOffset(PlacementSetter setter) if (spaceHnd != null) { productWrapper.AddSpace(spatialElement, spaceHnd, levelInfo, extraParams, true, exportInfo); - if (spatialElementAsArea != null) - { - Element areaScheme = spatialElementAsArea.AreaScheme; - if (areaScheme != null) - { - ElementId areaSchemeId = areaScheme.Id; - HashSet areas = null; - if (!ExporterCacheManager.AreaSchemeCache.TryGetValue(areaSchemeId, out areas)) - { - areas = new HashSet(); - ExporterCacheManager.AreaSchemeCache[areaSchemeId] = areas; - } - areas.Add(spaceHnd); - } - } + AddAreaToAreaScheme(spatialElement, spaceHnd); } } @@ -1003,7 +1016,7 @@ static XYZ GetSpaceBoundaryOffset(PlacementSetter setter) ExporterCacheManager.SpaceInfoCache.SetSpaceHandle(spatialElement, spaceHnd); // Find Ceiling as a Space boundary and keep the relationship in a cache for use later - bool ret = GetCeilingSpaceBoundary(spatialElement, out results); + GetCeilingSpaceBoundary(spatialElement, out results); if (!MathUtil.IsAlmostZero(dArea)) { @@ -1026,8 +1039,8 @@ static XYZ GetSpaceBoundaryOffset(PlacementSetter setter) // Export Classifications for SpatialElement for GSA/COBIE. if (ExporterCacheManager.ExportOptionsCache.ExportAsCOBIE) CreateCOBIESpaceClassifications(file, spaceHnd, spatialElement); - - return true; + + return spaceHnd; } /// @@ -1111,21 +1124,21 @@ static private IFCAnyHandle CreateSpatialZoneEnergyAnalysisPSet(IFCFile file, El string paramValue = ""; if (ParameterUtil.GetStringValueFromElement(element, "Spatial Zone Conditioning Requirement", out paramValue) != null) { - IFCData paramVal = Revit.IFC.Export.Toolkit.IFCDataUtil.CreateAsLabel(paramValue); + IFCData paramVal = IFCDataUtil.CreateAsLabel(paramValue); IFCAnyHandle propSingleValue = IFCInstanceExporter.CreatePropertySingleValue(file, "SpatialZoneConditioningRequirement", null, paramVal, null); properties.Add(propSingleValue); } if (ParameterUtil.GetStringValueFromElement(element, "HVAC System Type", out paramValue) != null) { - IFCData paramVal = Revit.IFC.Export.Toolkit.IFCDataUtil.CreateAsLabel(paramValue); + IFCData paramVal = IFCDataUtil.CreateAsLabel(paramValue); IFCAnyHandle propSingleValue = IFCInstanceExporter.CreatePropertySingleValue(file, "HVACSystemType", null, paramVal, null); properties.Add(propSingleValue); } if (ParameterUtil.GetStringValueFromElement(element, "User Defined HVAC System Type", out paramValue) != null) { - IFCData paramVal = Revit.IFC.Export.Toolkit.IFCDataUtil.CreateAsLabel(paramValue); + IFCData paramVal = IFCDataUtil.CreateAsLabel(paramValue); IFCAnyHandle propSingleValue = IFCInstanceExporter.CreatePropertySingleValue(file, "UserDefinedHVACSystemType", null, paramVal, null); properties.Add(propSingleValue); } @@ -1133,8 +1146,8 @@ static private IFCAnyHandle CreateSpatialZoneEnergyAnalysisPSet(IFCFile file, El double infiltrationRate = 0.0; if (ParameterUtil.GetDoubleValueFromElement(element, "Infiltration Rate", out infiltrationRate) != null) { - IFCData paramVal = Revit.IFC.Export.Toolkit.IFCDataUtil.CreateAsReal(infiltrationRate); - IFCAnyHandle unitHnd = !ExporterCacheManager.ExportOptionsCache.ExportAs4ReferenceView ? ExporterCacheManager.UnitsCache["ACH"] : null; + IFCData paramVal = IFCDataUtil.CreateAsReal(infiltrationRate); + IFCAnyHandle unitHnd = !ExporterCacheManager.ExportOptionsCache.ExportAs4ReferenceView ? ExporterCacheManager.UnitsCache.FindUserDefinedUnit("ACH") : null; IFCAnyHandle propSingleValue = IFCInstanceExporter.CreatePropertySingleValue(file, "InfiltrationRate", null, paramVal, unitHnd); properties.Add(propSingleValue); } @@ -1142,7 +1155,7 @@ static private IFCAnyHandle CreateSpatialZoneEnergyAnalysisPSet(IFCFile file, El int isDaylitZone = 0; if (ParameterUtil.GetIntValueFromElement(element, "Is Daylit Zone", out isDaylitZone) != null) { - IFCData paramVal = Revit.IFC.Export.Toolkit.IFCDataUtil.CreateAsBoolean(isDaylitZone != 0); + IFCData paramVal = IFCDataUtil.CreateAsBoolean(isDaylitZone != 0); IFCAnyHandle propSingleValue = IFCInstanceExporter.CreatePropertySingleValue(file, "IsDaylitZone", null, paramVal, null); properties.Add(propSingleValue); } @@ -1150,7 +1163,7 @@ static private IFCAnyHandle CreateSpatialZoneEnergyAnalysisPSet(IFCFile file, El int numberOfDaylightSensors = 0; if (ParameterUtil.GetIntValueFromElement(element, "Number of Daylight Sensors", out numberOfDaylightSensors) != null) { - IFCData paramVal = Revit.IFC.Export.Toolkit.IFCDataUtil.CreateAsInteger(numberOfDaylightSensors); + IFCData paramVal = IFCDataUtil.CreateAsInteger(numberOfDaylightSensors); IFCAnyHandle propSingleValue = IFCInstanceExporter.CreatePropertySingleValue(file, "NumberOfDaylightSensors", null, paramVal, null); properties.Add(propSingleValue); } @@ -1159,15 +1172,14 @@ static private IFCAnyHandle CreateSpatialZoneEnergyAnalysisPSet(IFCFile file, El if (ParameterUtil.GetDoubleValueFromElement(element, "Design Illuminance", out designIlluminance) != null) { double scaledValue = UnitUtil.ScaleIlluminance(designIlluminance); - IFCData paramVal = Revit.IFC.Export.Toolkit.IFCDataUtil.CreateAsReal(designIlluminance); - IFCAnyHandle unitHnd = !ExporterCacheManager.ExportOptionsCache.ExportAs4ReferenceView ? ExporterCacheManager.UnitsCache["LUX"] : null; - IFCAnyHandle propSingleValue = IFCInstanceExporter.CreatePropertySingleValue(file, "DesignIlluminance", null, paramVal, unitHnd); + IFCData paramVal = IFCDataUtil.CreateAsIlluminanceMeasure(designIlluminance); + IFCAnyHandle propSingleValue = IFCInstanceExporter.CreatePropertySingleValue(file, "DesignIlluminance", null, paramVal, null); properties.Add(propSingleValue); } if (ParameterUtil.GetStringValueFromElement(element, "Lighting Controls Type", out paramValue) != null) { - IFCData paramVal = Revit.IFC.Export.Toolkit.IFCDataUtil.CreateAsLabel(paramValue); + IFCData paramVal = IFCDataUtil.CreateAsLabel(paramValue); IFCAnyHandle propSingleValue = IFCInstanceExporter.CreatePropertySingleValue(file, "LightingControlsType", null, paramVal, null); properties.Add(propSingleValue); } @@ -1183,131 +1195,13 @@ static private IFCAnyHandle CreateSpatialZoneEnergyAnalysisPSet(IFCFile file, El return null; } - - /// - /// Get the name of the net planned area property, depending on the current schema, for levels and zones. - /// - /// The name of the net planned area property. - /// Note that PSet_SpaceCommon has had the property "NetPlannedArea" since IFC2x3. - static public string GetLevelAndZoneNetPlannedAreaName() - { - return ExporterCacheManager.ExportOptionsCache.ExportAsOlderThanIFC4 ? "NetAreaPlanned" : "NetPlannedArea"; - } - - /// - /// Get the name of the gross planned area property, depending on the current schema, for levels and zones. - /// - /// The name of the net planned area property. - /// Note that PSet_SpaceCommon has had the property "GrossPlannedArea" since IFC2x3. - static public string GetLevelAndZoneGrossPlannedAreaName() - { - return ExporterCacheManager.ExportOptionsCache.ExportAsOlderThanIFC4 ? "GrossAreaPlanned" : "GrossPlannedArea"; - } - /// /// Creates zone common property set. /// - /// The exporter. /// The file. /// The element. + /// The index to use for the shared parameter names. /// The handle. - static private IFCAnyHandle CreateZoneCommonPSet(ExporterIFC exporterIFC, IFCFile file, Element element) - { - // Property Sets. We don't use the generic Property Set mechanism because Zones aren't "real" elements. - HashSet properties = new HashSet(); - - IFCAnyHandle propSingleValue = PropertyUtil.CreateLabelPropertyFromElement(file, element, - "ZoneCategory", BuiltInParameter.INVALID, "Category", PropertyValueType.SingleValue, null); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propSingleValue)) - { - properties.Add(propSingleValue); - } - - string grossPlannedAreaName = GetLevelAndZoneGrossPlannedAreaName(); - propSingleValue = PropertyUtil.CreateAreaMeasurePropertyFromElement(file, exporterIFC, element, - "Pset_ZoneCommon." + grossPlannedAreaName, BuiltInParameter.INVALID, grossPlannedAreaName, PropertyValueType.SingleValue); - if (IFCAnyHandleUtil.IsNullOrHasNoValue(propSingleValue)) - { - // For backward compatibility - propSingleValue = PropertyUtil.CreateAreaMeasurePropertyFromElement(file, exporterIFC, element, - "Zone" + grossPlannedAreaName, BuiltInParameter.INVALID, grossPlannedAreaName, PropertyValueType.SingleValue); - } - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propSingleValue)) - properties.Add(propSingleValue); - - string netPlannedAreaName = GetLevelAndZoneNetPlannedAreaName(); - propSingleValue = PropertyUtil.CreateAreaMeasurePropertyFromElement(file, exporterIFC, element, - "Pset_ZoneCommon." + netPlannedAreaName, BuiltInParameter.INVALID, netPlannedAreaName, PropertyValueType.SingleValue); - if (IFCAnyHandleUtil.IsNullOrHasNoValue(propSingleValue)) - { - // For backward compatibility - propSingleValue = PropertyUtil.CreateAreaMeasurePropertyFromElement(file, exporterIFC, element, - "Zone" + netPlannedAreaName, BuiltInParameter.INVALID, netPlannedAreaName, PropertyValueType.SingleValue); - } - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propSingleValue)) - properties.Add(propSingleValue); - - propSingleValue = PropertyUtil.CreateBooleanPropertyFromElement(file, element, - "Pset_ZoneCommon.PubliclyAccessible", "PubliclyAccessible", PropertyValueType.SingleValue); - if (IFCAnyHandleUtil.IsNullOrHasNoValue(propSingleValue)) - { - propSingleValue = PropertyUtil.CreateBooleanPropertyFromElement(file, element, - "ZonePubliclyAccessible", "PubliclyAccessible", PropertyValueType.SingleValue); - } - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propSingleValue)) - { - properties.Add(propSingleValue); - } - - propSingleValue = PropertyUtil.CreateBooleanPropertyFromElement(file, element, - "Pset_ZoneCommon.HandicapAccessible", "HandicapAccessible", PropertyValueType.SingleValue); - if (IFCAnyHandleUtil.IsNullOrHasNoValue(propSingleValue)) - { - propSingleValue = PropertyUtil.CreateBooleanPropertyFromElement(file, element, - "ZoneHandicapAccessible", "HandicapAccessible", PropertyValueType.SingleValue); - - } - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propSingleValue)) - { - properties.Add(propSingleValue); - } - - propSingleValue = PropertyUtil.CreateBooleanPropertyFromElement(file, element, - "Pset_ZoneCommon.IsExternal", "IsExternal", PropertyValueType.SingleValue); - if (IFCAnyHandleUtil.IsNullOrHasNoValue(propSingleValue)) - { - propSingleValue = PropertyUtil.CreateBooleanPropertyFromElement(file, element, - "ZoneIsExternal", "IsExternal", PropertyValueType.SingleValue); - - } - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propSingleValue)) - { - properties.Add(propSingleValue); - } - - propSingleValue = PropertyUtil.CreateIdentifierPropertyFromElement(file, element, - "Pset_ZoneCommon.Reference", BuiltInParameter.INVALID, "Reference", PropertyValueType.SingleValue); - if (IFCAnyHandleUtil.IsNullOrHasNoValue(propSingleValue)) - { - propSingleValue = PropertyUtil.CreateIdentifierPropertyFromElement(file, element, - "ZoneReference", BuiltInParameter.INVALID, "Reference", PropertyValueType.SingleValue); - } - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propSingleValue)) - { - properties.Add(propSingleValue); - } - - if (properties.Count > 0) - { - string psetGuid = GUIDUtil.GenerateIFCGuidFrom( - GUIDUtil.CreateGUIDString(element, "Pset_ZoneCommon")); - return IFCInstanceExporter.CreatePropertySet(file, psetGuid, - ExporterCacheManager.OwnerHistoryHandle, "Pset_ZoneCommon", null, properties); - } - - return null; - } - /// /// Creates the ePset_SpaceOccupant. /// @@ -1322,14 +1216,14 @@ private static IFCAnyHandle CreatePSetSpaceOccupant(IFCFile file, Element elemen string paramValue = ""; if (ParameterUtil.GetStringValueFromElement(element, "Space Occupant Organization Abbreviation", out paramValue) != null) { - IFCData paramVal = Revit.IFC.Export.Toolkit.IFCDataUtil.CreateAsLabel(paramValue); + IFCData paramVal = IFCDataUtil.CreateAsLabel(paramValue); IFCAnyHandle propSingleValue = IFCInstanceExporter.CreatePropertySingleValue(file, "SpaceOccupantOrganizationAbbreviation", null, paramVal, null); properties.Add(propSingleValue); } if (ParameterUtil.GetStringValueFromElement(element, "Space Occupant Organization Name", out paramValue) != null) { - IFCData paramVal = Revit.IFC.Export.Toolkit.IFCDataUtil.CreateAsLabel(paramValue); + IFCData paramVal = IFCDataUtil.CreateAsLabel(paramValue); IFCAnyHandle propSingleValue = IFCInstanceExporter.CreatePropertySingleValue(file, "SpaceOccupantOrganizationName", null, paramVal, null); properties.Add(propSingleValue); } @@ -1380,10 +1274,13 @@ private static IFCAnyHandle CreatePSetSpaceOccupant(IFCFile file, Element elemen /// The exporterIFC object. /// The IFCFile object. /// The element. - /// The ProductWrapper. - static void CreateSpaceOccupantInfo(IFCFile file, Element element, ProductWrapper productWrapper) + /// The space handle. + public static void CreateSpaceOccupantInfo(IFCFile file, Element element, IFCAnyHandle spaceHnd) { - IFCAnyHandle spaceHnd = productWrapper.GetElementOfType(IFCEntityType.IfcSpace); + if (IFCAnyHandleUtil.IsNullOrHasNoValue(spaceHnd)) + { + return; + } bool exportToCOBIE = ExporterCacheManager.ExportOptionsCache.ExportAsCOBIE; @@ -1447,11 +1344,9 @@ static void CreateSpaceOccupantInfo(IFCFile file, Element element, ProductWrappe } } - static private void CreateGSAInformation(ExporterIFC exporterIFC, Element element, + static private void CreateGSAInformation(IFCFile file, Element element, ZoneInfo zoneInfo, string zoneObjectType) { - IFCFile file = exporterIFC.GetFile(); - bool isSpatialZone = NamingUtil.IsEqualIgnoringCaseAndSpaces(zoneObjectType, "SpatialZone"); if (isSpatialZone) { @@ -1489,13 +1384,16 @@ static void CreateSpaceOccupantInfo(IFCFile file, Element element, ProductWrappe /// /// Collect information to create zones and cache them to create when end export. /// - /// The exporterIFC object. /// The IFCFile object. /// The element. - /// The ProductWrapper. - static void CreateZoneInfos(ExporterIFC exporterIFC, IFCFile file, Element element, - ProductWrapper productWrapper) + /// The space handle. + static public void CreateZoneInfos(IFCFile file, Element element, IFCAnyHandle spaceHandle) { + if (IFCAnyHandleUtil.IsNullOrHasNoValue(spaceHandle)) + { + return; + } + bool exportToCOBIE = ExporterCacheManager.ExportOptionsCache.ExportAsCOBIE; // Extra zone information, since Revit doesn't have architectural zones. @@ -1514,13 +1412,11 @@ static void CreateSpaceOccupantInfo(IFCFile file, Element element, ProductWrappe string zoneClassificationCode = zoneInfoFinder.GetPropZoneValue(ZoneInfoLabel.ClassificationCode); - IFCAnyHandle roomHandle = productWrapper.GetElementOfType(IFCEntityType.IfcSpace); - ZoneInfo zoneInfo = ExporterCacheManager.ZoneInfoCache.Find(zoneName); if (zoneInfo == null) { - IFCAnyHandle zoneCommonPropertySetHandle = CreateZoneCommonPSet(exporterIFC, file, element); - zoneInfo = new ZoneInfo(zoneInfoFinder, roomHandle, zoneCommonPropertySetHandle); + zoneInfo = new ZoneInfo(zoneInfoFinder, spaceHandle); + zoneInfo.CollectZoneCommonPSetData(file, element, zoneInfoFinder.CurrentZoneNumber); zoneInfo.ConditionalAddClassification(file, zoneClassificationCode); ExporterCacheManager.ZoneInfoCache.Register(zoneName, zoneInfo); } @@ -1528,19 +1424,31 @@ static void CreateSpaceOccupantInfo(IFCFile file, Element element, ProductWrappe { // If description, long name or object type were empty, overwrite. zoneInfo.UpdateZoneInfo(zoneInfoFinder); - zoneInfo.RoomHandles.Add(roomHandle); + zoneInfo.RoomHandles.Add(spaceHandle); + zoneInfo.CollectZoneCommonPSetData(file, element, zoneInfoFinder.CurrentZoneNumber); zoneInfo.ConditionalAddClassification(file, zoneClassificationCode); - - if (IFCAnyHandleUtil.IsNullOrHasNoValue(zoneInfo.ZoneCommonProperySetHandle)) - zoneInfo.ZoneCommonProperySetHandle = CreateZoneCommonPSet(exporterIFC, file, element); } if (exportToCOBIE) { string zoneObjectType = zoneInfoFinder.GetPropZoneValue(ZoneInfoLabel.ObjectType); - CreateGSAInformation(exporterIFC, element, zoneInfo, zoneObjectType); + CreateGSAInformation(file, element, zoneInfo, zoneObjectType); } } while (zoneInfoFinder.IncrementCount()); // prevent infinite loop. } + + /// + /// Determine if the export instance type is compatible with IfcZone. + /// + /// The export type to check. + /// True if non-null, and an entity instance type that can go in a zone. + public static bool IsZoneCompatible(IFCExportInfoPair exportType) + { + if (exportType == null) + return false; + + return exportType.ExportInstance == IFCEntityType.IfcSpace || + exportType.ExportInstance == IFCEntityType.IfcSpatialZone; + } } } \ No newline at end of file diff --git a/Source/Revit.IFC.Export/Exporter/StairsExporter.cs b/Source/Revit.IFC.Export/Exporter/StairsExporter.cs index cefb21da..c05cc07a 100644 --- a/Source/Revit.IFC.Export/Exporter/StairsExporter.cs +++ b/Source/Revit.IFC.Export/Exporter/StairsExporter.cs @@ -884,7 +884,7 @@ public static List GetFlightsOffsetList(ExporterIFC exporterIFC, Stairs List components = new List(); IList componentExtrusionData = new List(); IFCAnyHandle containedStairHnd = IFCInstanceExporter.CreateStair(exporterIFC, stair, containedStairGuid, ownerHistory, - containedStairLocalPlacement, representation, exportType.ValidatedPredefinedType); + containedStairLocalPlacement, representation, exportType.GetPredefinedTypeOrDefault()); // Create appropriate type @@ -900,7 +900,7 @@ public static List GetFlightsOffsetList(ExporterIFC exporterIFC, Stairs IFCAnyHandle localPlacement = ecData.GetLocalPlacement(); IFCAnyHandle stairContainerHnd = IFCInstanceExporter.CreateStair(exporterIFC, stair, stairGuid, ownerHistory, - localPlacement, null, exportType.ValidatedPredefinedType); + localPlacement, null, exportType.GetPredefinedTypeOrDefault()); // Create appropriate type for the container //string contPredefType = GetValidatedStairType(stair as Stairs, ifcEnumType); @@ -974,14 +974,16 @@ public static List GetFlightsOffsetList(ExporterIFC exporterIFC, Stairs IFCAnyHandle ownerHistory = ExporterCacheManager.OwnerHistoryHandle; string stairGUID = GUIDUtil.CreateGUID(stair); IFCAnyHandle stairLocalPlacement = placementSetter.LocalPlacement; - //string stairType = GetIFCStairType(ifcEnumType); - string predefType = GetValidatedStairType(stair, null); + + string predefinedType = ifcEnumType; + if (string.IsNullOrWhiteSpace(predefinedType)) + predefinedType = GetValidatedStairType(stair, null); // override by stair components if predefined type is not set IFCAnyHandle stairContainerHnd = IFCInstanceExporter.CreateStair(exporterIFC, stair, stairGUID, ownerHistory, - stairLocalPlacement, null, predefType); + stairLocalPlacement, null, predefinedType); // Create appropriate type - IFCExportInfoPair exportType = new IFCExportInfoPair(IFCEntityType.IfcStair, predefType); + IFCExportInfoPair exportType = new IFCExportInfoPair(IFCEntityType.IfcStair, predefinedType); IFCAnyHandle stairTypeHnd = ExporterUtil.CreateGenericTypeFromElement(stair, exportType, exporterIFC.GetFile(), productWrapper); ExporterCacheManager.TypeRelationsCache.Add(stairTypeHnd, stairContainerHnd); @@ -996,67 +998,66 @@ public static List GetFlightsOffsetList(ExporterIFC exporterIFC, Stairs { index++; StairsRun run = doc.GetElement(runId) as StairsRun; - - using (IFCExportBodyParams ecData = new IFCExportBodyParams()) - { - ecData.AllowVerticalOffsetOfBReps = false; - ecData.SetLocalPlacement(ExporterUtil.CreateLocalPlacement(file, placementSetter.LocalPlacement, null)); - ecData.ReuseLocalPlacement = true; - ecData.IFCCADLayerOverride = ifcCADLayer; - GeometryElement runGeometryElement = run.get_Geometry(geomOptions); - BodyExporterOptions bodyExporterOptions = new BodyExporterOptions(true, ExportOptionsCache.ExportTessellationLevel.ExtraLow); - BodyData bodyData = BodyExporter.ExportBody(exporterIFC, run, categoryId, ElementId.InvalidElementId, runGeometryElement, - bodyExporterOptions, ecData); + IFCExportBodyParams ecData = new IFCExportBodyParams(); + ecData.AllowVerticalOffsetOfBReps = false; + ecData.SetLocalPlacement(ExporterUtil.CreateLocalPlacement(file, placementSetter.LocalPlacement, null)); + ecData.ReuseLocalPlacement = true; + ecData.IFCCADLayerOverride = ifcCADLayer; + GeometryElement runGeometryElement = run.get_Geometry(geomOptions); - IFCAnyHandle bodyRep = bodyData.RepresentationHnd; - if (IFCAnyHandleUtil.IsNullOrHasNoValue(bodyRep)) - { - ecData.ClearOpenings(); - continue; - } + BodyExporterOptions bodyExporterOptions = new BodyExporterOptions(true, ExportOptionsCache.ExportTessellationLevel.ExtraLow); + BodyData bodyData = BodyExporter.ExportBody(exporterIFC, run, categoryId, ElementId.InvalidElementId, runGeometryElement, + bodyExporterOptions, ecData); - IList reps = new List(); - reps.Add(bodyRep); + IFCAnyHandle bodyRep = bodyData.RepresentationHnd; + if (IFCAnyHandleUtil.IsNullOrHasNoValue(bodyRep)) + { + ecData.ClearOpenings(); + ecData.Dispose(); + continue; + } - if (!ExporterCacheManager.ExportOptionsCache.ExportAsCoordinationView2) - { - CreateWalkingLineAndFootprint(exporterIFC, run, bodyData, categoryId, trf, ref reps); - } + IList reps = new List(); + reps.Add(bodyRep); - Transform boundingBoxTrf = (bodyData.OffsetTransform == null) ? Transform.Identity : bodyData.OffsetTransform.Inverse; - IFCAnyHandle boundingBoxRep = BoundingBoxExporter.ExportBoundingBox(exporterIFC, runGeometryElement, boundingBoxTrf); - if (boundingBoxRep != null) - reps.Add(boundingBoxRep); + if (!ExporterCacheManager.ExportOptionsCache.ExportAsCoordinationView2) + { + CreateWalkingLineAndFootprint(exporterIFC, run, bodyData, categoryId, trf, ref reps); + } - IFCAnyHandle representation = IFCInstanceExporter.CreateProductDefinitionShape(exporterIFC.GetFile(), null, null, reps); + Transform boundingBoxTrf = (bodyData.OffsetTransform == null) ? Transform.Identity : bodyData.OffsetTransform.Inverse; + IFCAnyHandle boundingBoxRep = BoundingBoxExporter.ExportBoundingBox(exporterIFC, runGeometryElement, boundingBoxTrf); + if (boundingBoxRep != null) + reps.Add(boundingBoxRep); - string runGUID = GUIDUtil.CreateGUID(run); - string origRunName = IFCAnyHandleUtil.GetStringAttribute(stairContainerHnd, "Name") + " Run " + index; - string runName = NamingUtil.GetNameOverride(run, origRunName); + IFCAnyHandle representation = IFCInstanceExporter.CreateProductDefinitionShape(exporterIFC.GetFile(), null, null, reps); - IFCAnyHandle runLocalPlacement = ecData.GetLocalPlacement(); - string runElementTag = NamingUtil.GetTagOverride(run); + string runGUID = GUIDUtil.CreateGUID(run); + string origRunName = IFCAnyHandleUtil.GetStringAttribute(stairContainerHnd, "Name") + " Run " + index; + string runName = NamingUtil.GetNameOverride(run, origRunName); - string flightPredefType = GetValidatedStairFlightType(run); + IFCAnyHandle runLocalPlacement = ecData.GetLocalPlacement(); + string runElementTag = NamingUtil.GetTagOverride(run); - IFCAnyHandle stairFlightHnd = IFCInstanceExporter.CreateStairFlight(exporterIFC, run, runGUID, ownerHistory, runLocalPlacement, - representation, run.ActualRisersNumber, run.ActualTreadsNumber, stair.ActualRiserHeight, stair.ActualTreadDepth, flightPredefType); - IFCAnyHandleUtil.OverrideNameAttribute(stairFlightHnd, runName); - // Create type - IFCExportInfoPair flightEportType = new IFCExportInfoPair(IFCEntityType.IfcStairFlight, flightPredefType); - IFCAnyHandle flightTypeHnd = ExporterUtil.CreateGenericTypeFromElement(run, flightEportType, exporterIFC.GetFile(), productWrapper); - ExporterCacheManager.TypeRelationsCache.Add(flightTypeHnd, stairFlightHnd); + string flightPredefType = GetValidatedStairFlightType(run); - componentHandles.Add(stairFlightHnd); - componentExtrusionData.Add(ecData); + IFCAnyHandle stairFlightHnd = IFCInstanceExporter.CreateStairFlight(exporterIFC, run, runGUID, ownerHistory, runLocalPlacement, + representation, run.ActualRisersNumber, run.ActualTreadsNumber, stair.ActualRiserHeight, stair.ActualTreadDepth, flightPredefType); + IFCAnyHandleUtil.OverrideNameAttribute(stairFlightHnd, runName); + // Create type + IFCExportInfoPair flightEportType = new IFCExportInfoPair(IFCEntityType.IfcStairFlight, flightPredefType); + IFCAnyHandle flightTypeHnd = ExporterUtil.CreateGenericTypeFromElement(run, flightEportType, exporterIFC.GetFile(), productWrapper); + ExporterCacheManager.TypeRelationsCache.Add(flightTypeHnd, stairFlightHnd); - CategoryUtil.CreateMaterialAssociation(exporterIFC, stairFlightHnd, bodyData.MaterialIds); + componentHandles.Add(stairFlightHnd); + componentExtrusionData.Add(ecData); - productWrapper.AddElement(run, stairFlightHnd, placementSetter.LevelInfo, ecData, false, flightEportType); + CategoryUtil.CreateMaterialAssociation(exporterIFC, stairFlightHnd, bodyData.MaterialIds); - ExporterCacheManager.HandleToElementCache.Register(stairFlightHnd, run.Id); - } + productWrapper.AddElement(run, stairFlightHnd, placementSetter.LevelInfo, ecData, false, flightEportType); + + ExporterCacheManager.HandleToElementCache.Register(stairFlightHnd, run.Id); } // Get List of landings to export their geometry. @@ -1067,64 +1068,63 @@ public static List GetFlightsOffsetList(ExporterIFC exporterIFC, Stairs index++; StairsLanding landing = doc.GetElement(landingId) as StairsLanding; - using (IFCExportBodyParams ecData = new IFCExportBodyParams()) + IFCExportBodyParams ecData = new IFCExportBodyParams(); + ecData.AllowVerticalOffsetOfBReps = false; + ecData.SetLocalPlacement(ExporterUtil.CreateLocalPlacement(file, placementSetter.LocalPlacement, null)); + ecData.ReuseLocalPlacement = true; + + GeometryElement landingGeometryElement = landing.get_Geometry(geomOptions); + + BodyExporterOptions bodyExporterOptions = new BodyExporterOptions(true, ExportOptionsCache.ExportTessellationLevel.ExtraLow); + BodyData bodyData = BodyExporter.ExportBody(exporterIFC, landing, categoryId, ElementId.InvalidElementId, landingGeometryElement, + bodyExporterOptions, ecData); + + IFCAnyHandle bodyRep = bodyData.RepresentationHnd; + if (IFCAnyHandleUtil.IsNullOrHasNoValue(bodyRep)) { - ecData.AllowVerticalOffsetOfBReps = false; - ecData.SetLocalPlacement(ExporterUtil.CreateLocalPlacement(file, placementSetter.LocalPlacement, null)); - ecData.ReuseLocalPlacement = true; + ecData.ClearOpenings(); + ecData.Dispose(); + continue; + } - GeometryElement landingGeometryElement = landing.get_Geometry(geomOptions); + // create Boundary rep. + IList reps = new List(); + reps.Add(bodyRep); - BodyExporterOptions bodyExporterOptions = new BodyExporterOptions(true, ExportOptionsCache.ExportTessellationLevel.ExtraLow); - BodyData bodyData = BodyExporter.ExportBody(exporterIFC, landing, categoryId, ElementId.InvalidElementId, landingGeometryElement, - bodyExporterOptions, ecData); + if (!ExporterCacheManager.ExportOptionsCache.ExportAsCoordinationView2) + { + CreateWalkingLineAndFootprint(exporterIFC, landing, bodyData, categoryId, trf, ref reps); + } - IFCAnyHandle bodyRep = bodyData.RepresentationHnd; - if (IFCAnyHandleUtil.IsNullOrHasNoValue(bodyRep)) - { - ecData.ClearOpenings(); - continue; - } + Transform boundingBoxTrf = (bodyData.OffsetTransform == null) ? Transform.Identity : bodyData.OffsetTransform.Inverse; + IFCAnyHandle boundingBoxRep = BoundingBoxExporter.ExportBoundingBox(exporterIFC, landingGeometryElement, boundingBoxTrf); + if (boundingBoxRep != null) + reps.Add(boundingBoxRep); - // create Boundary rep. - IList reps = new List(); - reps.Add(bodyRep); + string landingGUID = GUIDUtil.CreateGUID(landing); + string origLandingName = IFCAnyHandleUtil.GetStringAttribute(stairContainerHnd, "Name") + " Landing " + index; + string landingName = NamingUtil.GetNameOverride(landing, origLandingName); + IFCAnyHandle landingLocalPlacement = ecData.GetLocalPlacement(); - if (!ExporterCacheManager.ExportOptionsCache.ExportAsCoordinationView2) - { - CreateWalkingLineAndFootprint(exporterIFC, landing, bodyData, categoryId, trf, ref reps); - } + IFCAnyHandle representation = IFCInstanceExporter.CreateProductDefinitionShape(exporterIFC.GetFile(), null, null, reps); - Transform boundingBoxTrf = (bodyData.OffsetTransform == null) ? Transform.Identity : bodyData.OffsetTransform.Inverse; - IFCAnyHandle boundingBoxRep = BoundingBoxExporter.ExportBoundingBox(exporterIFC, landingGeometryElement, boundingBoxTrf); - if (boundingBoxRep != null) - reps.Add(boundingBoxRep); - - string landingGUID = GUIDUtil.CreateGUID(landing); - string origLandingName = IFCAnyHandleUtil.GetStringAttribute(stairContainerHnd, "Name") + " Landing " + index; - string landingName = NamingUtil.GetNameOverride(landing, origLandingName); - IFCAnyHandle landingLocalPlacement = ecData.GetLocalPlacement(); - - IFCAnyHandle representation = IFCInstanceExporter.CreateProductDefinitionShape(exporterIFC.GetFile(), null, null, reps); - - string landingPredefinedType = "LANDING"; - IFCAnyHandle landingHnd = IFCInstanceExporter.CreateSlab(exporterIFC, landing, landingGUID, ownerHistory, - landingLocalPlacement, representation, landingPredefinedType); - IFCAnyHandleUtil.OverrideNameAttribute(landingHnd, landingName); - - // Create type - IFCExportInfoPair landingExportType = new IFCExportInfoPair(IFCEntityType.IfcSlab, landingPredefinedType); - IFCAnyHandle landingTypeHnd = ExporterUtil.CreateGenericTypeFromElement(landing, landingExportType, exporterIFC.GetFile(), productWrapper); - ExporterCacheManager.TypeRelationsCache.Add(landingTypeHnd, landingHnd); - - componentHandles.Add(landingHnd); - componentExtrusionData.Add(ecData); + string landingPredefinedType = "LANDING"; + IFCAnyHandle landingHnd = IFCInstanceExporter.CreateSlab(exporterIFC, landing, landingGUID, ownerHistory, + landingLocalPlacement, representation, landingPredefinedType); + IFCAnyHandleUtil.OverrideNameAttribute(landingHnd, landingName); - CategoryUtil.CreateMaterialAssociation(exporterIFC, landingHnd, bodyData.MaterialIds); + // Create type + IFCExportInfoPair landingExportType = new IFCExportInfoPair(IFCEntityType.IfcSlab, landingPredefinedType); + IFCAnyHandle landingTypeHnd = ExporterUtil.CreateGenericTypeFromElement(landing, landingExportType, exporterIFC.GetFile(), productWrapper); + ExporterCacheManager.TypeRelationsCache.Add(landingTypeHnd, landingHnd); - productWrapper.AddElement(landing, landingHnd, placementSetter.LevelInfo, ecData, false, landingExportType); - ExporterCacheManager.HandleToElementCache.Register(landingHnd, landing.Id); - } + componentHandles.Add(landingHnd); + componentExtrusionData.Add(ecData); + + CategoryUtil.CreateMaterialAssociation(exporterIFC, landingHnd, bodyData.MaterialIds); + + productWrapper.AddElement(landing, landingHnd, placementSetter.LevelInfo, ecData, false, landingExportType); + ExporterCacheManager.HandleToElementCache.Register(landingHnd, landing.Id); } // Get List of supports to export their geometry. Supports are not exposed to API, so export as generic Element. @@ -1135,45 +1135,44 @@ public static List GetFlightsOffsetList(ExporterIFC exporterIFC, Stairs index++; Element support = doc.GetElement(supportId); - using (IFCExportBodyParams ecData = new IFCExportBodyParams()) - { - ecData.SetLocalPlacement(ExporterUtil.CreateLocalPlacement(file, placementSetter.LocalPlacement, null)); - ecData.ReuseLocalPlacement = true; - ecData.IFCCADLayerOverride = ifcCADLayer; + IFCExportBodyParams ecData = new IFCExportBodyParams(); + ecData.SetLocalPlacement(ExporterUtil.CreateLocalPlacement(file, placementSetter.LocalPlacement, null)); + ecData.ReuseLocalPlacement = true; + ecData.IFCCADLayerOverride = ifcCADLayer; - GeometryElement supportGeometryElement = support.get_Geometry(geomOptions); - BodyData bodyData; - BodyExporterOptions bodyExporterOptions = new BodyExporterOptions(true, ExportOptionsCache.ExportTessellationLevel.ExtraLow); - IFCAnyHandle representation = RepresentationUtil.CreateAppropriateProductDefinitionShape(exporterIFC, - support, categoryId, supportGeometryElement, bodyExporterOptions, null, ecData, out bodyData, instanceGeometry: true); + GeometryElement supportGeometryElement = support.get_Geometry(geomOptions); + BodyData bodyData; + BodyExporterOptions bodyExporterOptions = new BodyExporterOptions(true, ExportOptionsCache.ExportTessellationLevel.ExtraLow); + IFCAnyHandle representation = RepresentationUtil.CreateAppropriateProductDefinitionShape(exporterIFC, + support, categoryId, supportGeometryElement, bodyExporterOptions, null, ecData, out bodyData, instanceGeometry: true); - if (IFCAnyHandleUtil.IsNullOrHasNoValue(representation)) - { - ecData.ClearOpenings(); - continue; - } + if (IFCAnyHandleUtil.IsNullOrHasNoValue(representation)) + { + ecData.ClearOpenings(); + ecData.Dispose(); + continue; + } - string supportGUID = GUIDUtil.CreateGUID(support); - string origSupportName = IFCAnyHandleUtil.GetStringAttribute(stairContainerHnd, "Name") + " Stringer " + index; - string supportName = NamingUtil.GetNameOverride(support, origSupportName); - IFCAnyHandle supportLocalPlacement = ecData.GetLocalPlacement(); + string supportGUID = GUIDUtil.CreateGUID(support); + string origSupportName = IFCAnyHandleUtil.GetStringAttribute(stairContainerHnd, "Name") + " Stringer " + index; + string supportName = NamingUtil.GetNameOverride(support, origSupportName); + IFCAnyHandle supportLocalPlacement = ecData.GetLocalPlacement(); - string stringerPredefType = "STRINGER"; - IFCExportInfoPair stringerExportInfo = new IFCExportInfoPair(IFCEntityType.IfcMember, stringerPredefType); - IFCAnyHandle type = GetMemberTypeHandle(exporterIFC, support); + string stringerPredefType = "STRINGER"; + IFCExportInfoPair stringerExportInfo = new IFCExportInfoPair(IFCEntityType.IfcMember, stringerPredefType); + IFCAnyHandle type = GetMemberTypeHandle(exporterIFC, support); - IFCAnyHandle supportHnd = IFCInstanceExporter.CreateMember(exporterIFC, support, supportGUID, ownerHistory, - supportLocalPlacement, representation, stringerPredefType); - IFCAnyHandleUtil.OverrideNameAttribute(supportHnd, supportName); - componentHandles.Add(supportHnd); - componentExtrusionData.Add(ecData); + IFCAnyHandle supportHnd = IFCInstanceExporter.CreateMember(exporterIFC, support, supportGUID, ownerHistory, + supportLocalPlacement, representation, stringerPredefType); + IFCAnyHandleUtil.OverrideNameAttribute(supportHnd, supportName); + componentHandles.Add(supportHnd); + componentExtrusionData.Add(ecData); - CategoryUtil.CreateMaterialAssociation(exporterIFC, supportHnd, bodyData.MaterialIds); + CategoryUtil.CreateMaterialAssociation(exporterIFC, supportHnd, bodyData.MaterialIds); - productWrapper.AddElement(support, supportHnd, placementSetter.LevelInfo, ecData, false, stringerExportInfo); + productWrapper.AddElement(support, supportHnd, placementSetter.LevelInfo, ecData, false, stringerExportInfo); - ExporterCacheManager.TypeRelationsCache.Add(type, supportHnd); - } + ExporterCacheManager.TypeRelationsCache.Add(type, supportHnd); } StairRampContainerInfo stairRampInfo = new StairRampContainerInfo(stairContainerHnd, componentHandles, stairLocalPlacement); @@ -1285,11 +1284,11 @@ public static List GetFlightsOffsetList(ExporterIFC exporterIFC, Stairs HashSet flightHnds = new HashSet(); List representations = new List(); - if ((ii < walkingLineCount) && !IFCAnyHandleUtil.IsNullOrHasNoValue(walkingLineReps[ii])) - representations.Add(walkingLineReps[ii]); + if (ii < walkingLineCount) + representations.AddIfNotNull(walkingLineReps[ii]); - if ((ii < boundaryRepCount) && !IFCAnyHandleUtil.IsNullOrHasNoValue(boundaryReps[ii])) - representations.Add(boundaryReps[ii]); + if (ii < boundaryRepCount) + representations.AddIfNotNull(boundaryReps[ii]); representations.Add(bodyRep); @@ -1344,7 +1343,7 @@ public static List GetFlightsOffsetList(ExporterIFC exporterIFC, Stairs flightHnd = IFCInstanceExporter.CreateRampFlight(exporterIFC, legacyStair, flightCompGUID, ExporterCacheManager.OwnerHistoryHandle, newLocalPlacement, newProdRep, ifcType); components[compIdx].Add(flightHnd); - exportInfo.SetValueWithPair(IFCEntityType.IfcRampFlight, ifcType); + exportInfo.SetByTypeAndPredefinedType(IFCEntityType.IfcRampFlight, ifcType); } else { @@ -1354,7 +1353,7 @@ public static List GetFlightsOffsetList(ExporterIFC exporterIFC, Stairs flightHnd = IFCInstanceExporter.CreateStairFlight(exporterIFC, legacyStair, flightCompGUID, ExporterCacheManager.OwnerHistoryHandle, newLocalPlacement, newProdRep, numRisers[ii], numTreads[ii], riserHeight, treadsLength[ii], ifcType); components[compIdx].Add(flightHnd); - exportInfo.SetValueWithPair(IFCEntityType.IfcStairFlight, ifcType); + exportInfo.SetByTypeAndPredefinedType(IFCEntityType.IfcStairFlight, ifcType); } IFCAnyHandleUtil.OverrideNameAttribute(flightHnd, stairName); @@ -1383,11 +1382,11 @@ public static List GetFlightsOffsetList(ExporterIFC exporterIFC, Stairs } List representations = new List(); - if (((ii + runCount) < walkingLineCount) && !IFCAnyHandleUtil.IsNullOrHasNoValue(walkingLineReps[ii + runCount])) - representations.Add(walkingLineReps[ii + runCount]); + if ((ii + runCount) < walkingLineCount) + representations.AddIfNotNull(walkingLineReps[ii + runCount]); - if (((ii + runCount) < boundaryRepCount) && !IFCAnyHandleUtil.IsNullOrHasNoValue(boundaryReps[ii + runCount])) - representations.Add(boundaryReps[ii + runCount]); + if ((ii + runCount) < boundaryRepCount) + representations.AddIfNotNull(boundaryReps[ii + runCount]); representations.Add(bodyRep); @@ -1501,11 +1500,14 @@ public static List GetFlightsOffsetList(ExporterIFC exporterIFC, Stairs if (isRamp) { string rampType = RampExporter.GetIFCRampType(ifcEnumType); - string stairName = NamingUtil.GetIFCName(legacyStair); + string rampName = NamingUtil.GetIFCName(legacyStair); IFCAnyHandle containedRampHnd = IFCInstanceExporter.CreateRamp(exporterIFC, legacyStair, GUIDUtil.CreateGUID(legacyStair), ExporterCacheManager.OwnerHistoryHandle, placementSetter.LocalPlacement, null, rampType); - IFCAnyHandleUtil.OverrideNameAttribute(containedRampHnd, stairName); + IFCAnyHandleUtil.OverrideNameAttribute(containedRampHnd, rampName); IFCExportInfoPair exportInfo = new IFCExportInfoPair(IFCEntityType.IfcRamp, rampType); + IFCAnyHandle typeHnd = ExporterUtil.CreateGenericTypeFromElement(legacyStair, exportInfo, file, productWrapper); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(typeHnd)) + ExporterCacheManager.TypeRelationsCache.Add(typeHnd, containedRampHnd); productWrapper.AddElement(legacyStair, containedRampHnd, placementSetter.LevelInfo, ifcECData, true, exportInfo); createdStairs.Add(containedRampHnd); } @@ -1517,6 +1519,9 @@ public static List GetFlightsOffsetList(ExporterIFC exporterIFC, Stairs placementSetter.LocalPlacement, null, stairType); IFCAnyHandleUtil.OverrideNameAttribute(containedStairHnd, stairName); IFCExportInfoPair exportInfo = new IFCExportInfoPair(IFCEntityType.IfcStair, stairType); + IFCAnyHandle typeHnd = ExporterUtil.CreateGenericTypeFromElement(legacyStair, exportInfo, file, productWrapper); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(typeHnd)) + ExporterCacheManager.TypeRelationsCache.Add(typeHnd, containedStairHnd); productWrapper.AddElement(legacyStair, containedStairHnd, placementSetter.LevelInfo, ifcECData, true, exportInfo); createdStairs.Add(containedStairHnd); } @@ -1613,11 +1618,11 @@ public static List GetFlightsOffsetList(ExporterIFC exporterIFC, Stairs public static void Export(ExporterIFC exporterIFC, Element element, GeometryElement geometryElement, ProductWrapper productWrapper) { // Check the intended IFC entity or type name is in the exclude list specified in the UI - Common.Enums.IFCEntityType elementClassTypeEnum = Common.Enums.IFCEntityType.IfcStair; - if (ExporterCacheManager.ExportOptionsCache.IsElementInExcludeList(elementClassTypeEnum)) + if (ExporterCacheManager.ExportOptionsCache.IsElementInExcludeList(IFCEntityType.IfcStair)) return; - string ifcEnumType = ExporterUtil.GetIFCTypeFromExportTable(exporterIFC, element); + IFCExportInfoPair exportType = ExporterUtil.GetProductExportType(exporterIFC, element, out _); + string ifcEnumType = exportType.GetPredefinedTypeOrDefault(); IFCFile file = exporterIFC.GetFile(); using (IFCTransaction tr = new IFCTransaction(file)) @@ -1628,7 +1633,7 @@ public static void Export(ExporterIFC exporterIFC, Element element, GeometryElem List flightOffsets = GetFlightsOffsetList(exporterIFC, stair); if (flightOffsets.Count > 0) { - ExportStairsAsContainer(exporterIFC, ifcEnumType, stair, geometryElement, flightOffsets, productWrapper); + ExportStairsAsContainer(exporterIFC, exportType.PredefinedType, stair, geometryElement, flightOffsets, productWrapper); if (IFCAnyHandleUtil.IsNullOrHasNoValue(productWrapper.GetAnElement())) ExportStairAsSingleGeometry(exporterIFC, ifcEnumType, element, geometryElement, flightOffsets, productWrapper); } @@ -1681,9 +1686,7 @@ static IList CreateBoundaryLineReps(ExporterIFC exporterIFC, IFCLe { if (ExporterCacheManager.ExportOptionsCache.ExportAs4ReferenceView) { - IFCAnyHandle curveHnd = GeometryUtil.CreatePolyCurveFromCurve(exporterIFC, curve); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(curveHnd)) - curveSet.Add(curveHnd); + curveSet.AddIfNotNull(GeometryUtil.CreatePolyCurveFromCurve(exporterIFC, curve)); } else { @@ -1691,9 +1694,9 @@ static IList CreateBoundaryLineReps(ExporterIFC exporterIFC, IFCLe ExporterIFCUtils.CollectGeometryInfo(exporterIFC, info, curve, XYZ.Zero, false); IList curves = info.GetCurves(); - if (curves.Count == 1 && !IFCAnyHandleUtil.IsNullOrHasNoValue(curves[0])) + if (curves.Count == 1) { - curveSet.Add(curves[0]); + curveSet.AddIfNotNull(curves[0]); } } } @@ -1729,8 +1732,7 @@ static IList CreateWalkLineReps(ExporterIFC exporterIFC, IFCLegacy IFCAnyHandle curve = GeometryUtil.CreateIFCCurveFromCurves(exporterIFC, curves, lcs, projDir); if (!IFCAnyHandleUtil.IsNullOrHasNoValue(curve)) { - HashSet bodyItems = new HashSet(); - bodyItems.Add(curve); + HashSet bodyItems = new HashSet() { curve }; walkLineReps.Add(RepresentationUtil.CreateShapeRepresentation(exporterIFC, legacyStairElem, cateId, contextOfItemsWalkLine, "Axis", "Curve2D", bodyItems)); } @@ -1770,11 +1772,10 @@ private static void CreateWalkingLineAndFootprint(ExporterIFC exporterIFC, Eleme boundaryTrf, runBoundaryProjDir); if (!IFCAnyHandleUtil.IsNullOrHasNoValue(boundaryHnd)) { - HashSet geomSelectSet = new HashSet(); - geomSelectSet.Add(boundaryHnd); + HashSet geomSelectSet = new HashSet() { boundaryHnd }; - HashSet boundaryItems = new HashSet(); - boundaryItems.Add(IFCInstanceExporter.CreateGeometricSet(file, geomSelectSet)); + HashSet boundaryItems = new HashSet() + { IFCInstanceExporter.CreateGeometricSet(file, geomSelectSet) }; IFCAnyHandle boundaryRep = RepresentationUtil.CreateGeometricSetRep(exporterIFC, element, categoryId, "FootPrint", contextOfItemsFootPrint, boundaryItems); @@ -1791,11 +1792,10 @@ private static void CreateWalkingLineAndFootprint(ExporterIFC exporterIFC, Eleme boundaryTrf, runBoundaryProjDir); if (!IFCAnyHandleUtil.IsNullOrHasNoValue(walkingLineHnd)) { - HashSet geomSelectSet = new HashSet(); - geomSelectSet.Add(walkingLineHnd); + HashSet geomSelectSet = new HashSet() { walkingLineHnd }; - HashSet walkingLineItems = new HashSet(); - walkingLineItems.Add(IFCInstanceExporter.CreateGeometricSet(file, geomSelectSet)); + HashSet walkingLineItems = new HashSet() + { IFCInstanceExporter.CreateGeometricSet(file, geomSelectSet) }; IFCAnyHandle walkingLineRep = RepresentationUtil.CreateGeometricSetRep(exporterIFC, element, categoryId, "Axis", contextOfItemsAxis, walkingLineItems); diff --git a/Source/Revit.IFC.Export/Exporter/StructuralMemberExporter.cs b/Source/Revit.IFC.Export/Exporter/StructuralMemberExporter.cs index dbfc2369..0ef68f87 100644 --- a/Source/Revit.IFC.Export/Exporter/StructuralMemberExporter.cs +++ b/Source/Revit.IFC.Export/Exporter/StructuralMemberExporter.cs @@ -194,7 +194,9 @@ public static IFCAnyHandle CreateStructuralMemberAxis(ExporterIFC exporterIFC, E curve = curve.CreateTransformed(offset.Inverse.Multiply(axisInfo.LCSAsTransform)); IDictionary cachePoints = new Dictionary(); - IFCAnyHandle ifcCurveHnd = GeometryUtil.CreateIFCCurveFromRevitCurve(exporterIFC.GetFile(), exporterIFC, curve, true, cachePoints, true); + const GeometryUtil.TrimCurvePreference trimCurvePreference = GeometryUtil.TrimCurvePreference.TrimmedCurve; + IFCAnyHandle ifcCurveHnd = GeometryUtil.CreateIFCCurveFromRevitCurve(exporterIFC.GetFile(), + exporterIFC, curve, true, cachePoints, trimCurvePreference, null); IList axis_items = new List(); if (!(IFCAnyHandleUtil.IsNullOrHasNoValue(ifcCurveHnd))) axis_items.Add(ifcCurveHnd); diff --git a/Source/Revit.IFC.Export/Exporter/SweptSolidExporter.cs b/Source/Revit.IFC.Export/Exporter/SweptSolidExporter.cs index 33c08bb6..2ffe5a69 100644 --- a/Source/Revit.IFC.Export/Exporter/SweptSolidExporter.cs +++ b/Source/Revit.IFC.Export/Exporter/SweptSolidExporter.cs @@ -146,9 +146,11 @@ public static SimpleSweptSolidAnalyzer CanExportAsSweptSolid(ExporterIFC exporte /// The element. /// The solid. /// The normal of the plane that the path lies on. + /// A set of flags for which representations we should generate. + /// True if we should do a coarse parameterization of the directrix. /// The SweptSolidExporter. public static SweptSolidExporter Create(ExporterIFC exporterIFC, Element element, SimpleSweptSolidAnalyzer sweptAnalyzer, GeometryObject geomObject, - GenerateAdditionalInfo addInfo = GenerateAdditionalInfo.GenerateBody) + GenerateAdditionalInfo addInfo, bool isCoarse) { try { @@ -157,7 +159,7 @@ public static SimpleSweptSolidAnalyzer CanExportAsSweptSolid(ExporterIFC exporte SweptSolidExporter sweptSolidExporter = null; - IList faceBoundaryTypes; + IList faceBoundaryTypes; IList faceBoundaries = GeometryUtil.GetFaceBoundaries(sweptAnalyzer.ProfileFace, null, out faceBoundaryTypes); string profileName = null; @@ -202,7 +204,7 @@ public static SimpleSweptSolidAnalyzer CanExportAsSweptSolid(ExporterIFC exporte } else { - sweptSolidExporter.RepresentationItem = CreateSimpleSweptSolid(exporterIFC, profileName, faceBoundaries, sweptAnalyzer.ReferencePlaneNormal, sweptAnalyzer.PathCurve); + sweptSolidExporter.RepresentationItem = CreateSimpleSweptSolid(exporterIFC, profileName, faceBoundaries, sweptAnalyzer.PathCurve); sweptSolidExporter.RepresentationType = ShapeRepresentationType.AdvancedSweptSolid; if ((addInfo & GenerateAdditionalInfo.GenerateFootprint) != 0) { @@ -213,7 +215,8 @@ public static SimpleSweptSolidAnalyzer CanExportAsSweptSolid(ExporterIFC exporte } else { - sweptSolidExporter.Facets = CreateSimpleSweptSolidAsBRep(exporterIFC, profileName, faceBoundaries, sweptAnalyzer.ReferencePlaneNormal, sweptAnalyzer.PathCurve); + sweptSolidExporter.Facets = CreateSimpleSweptSolidAsBRep(exporterIFC, faceBoundaries, + sweptAnalyzer.PathCurve, isCoarse); sweptSolidExporter.RepresentationType = ShapeRepresentationType.Brep; } } @@ -255,7 +258,7 @@ public static IFCAnyHandle CreateSimpleSweptSolid(ExporterIFC exporterIFC, Eleme profileName = type.Name; } - return CreateSimpleSweptSolid(exporterIFC, profileName, faceBoundaries, sweptAnalyzer.ReferencePlaneNormal, sweptAnalyzer.PathCurve); + return CreateSimpleSweptSolid(exporterIFC, profileName, faceBoundaries, sweptAnalyzer.PathCurve); } } catch (Exception) @@ -266,9 +269,9 @@ public static IFCAnyHandle CreateSimpleSweptSolid(ExporterIFC exporterIFC, Eleme return null; } - private static bool CanCreateSimpleSweptSolid(IList profileCurveLoops, XYZ normal, Curve directrix) + private static bool CanCreateSimpleSweptSolid(IList profileCurveLoops, Curve directrix) { - if (directrix == null || normal == null || profileCurveLoops == null || profileCurveLoops.Count == 0) + if (directrix == null || profileCurveLoops == null || profileCurveLoops.Count == 0) return false; if (directrix is Arc) @@ -336,14 +339,14 @@ private static void CreateAxisAndProfileCurveLCS(Curve directrix, double param, /// The path curve. /// The swept solid handle. public static IFCAnyHandle CreateSimpleSweptSolid(ExporterIFC exporterIFC, string profileName, IList profileCurveLoops, - XYZ normal, Curve directrix) + Curve directrix) { // see definition of IfcSurfaceCurveSweptAreaSolid from // http://www.buildingsmart-tech.org/ifc/IFC2x4/rc4/html/schema/ifcgeometricmodelresource/lexical/ifcsurfacecurvesweptareasolid.htm IFCAnyHandle simpleSweptSolidHnd = null; - if (!CanCreateSimpleSweptSolid(profileCurveLoops, normal, directrix)) + if (!CanCreateSimpleSweptSolid(profileCurveLoops, directrix)) return simpleSweptSolidHnd; bool isBound = directrix.IsBound; @@ -363,7 +366,7 @@ private static void CreateAxisAndProfileCurveLCS(Curve directrix, double param, return null; } - if (curveLoops == null || curveLoops.Count == 0) + if ((curveLoops?.Count ?? 0) == 0) return simpleSweptSolidHnd; double startParam = 0.0, endParam = 1.0; @@ -373,28 +376,22 @@ private static void CreateAxisAndProfileCurveLCS(Curve directrix, double param, if (isBound) { // Put the parameters in range of [0, 2*Pi] - double inRangeStarParam = (directrix.GetEndParameter(0) % (2 * Math.PI)); + double inRangeStartParam = (directrix.GetEndParameter(0) % (2 * Math.PI)); double inRangeEndParam = (directrix.GetEndParameter(1) % (2 * Math.PI)); // We want the angle direction is anti-clockwise (+ direction), therefore we will always start with the smaller one - if (inRangeEndParam < inRangeStarParam) + if (inRangeEndParam < inRangeStartParam) { - double tmp = inRangeStarParam; - inRangeStarParam = inRangeEndParam; - inRangeEndParam = tmp; + (inRangeStartParam, inRangeEndParam) = (inRangeEndParam, inRangeStartParam); } + // If start param is negative, we will reset it to 0 and shift the end accordingly - if (inRangeStarParam < 0) - { - double parRange = inRangeEndParam - inRangeStarParam; - inRangeStarParam = 0.0; - inRangeEndParam = parRange; - } + inRangeEndParam -= inRangeStartParam; endParam = UnitUtil.ScaleAngle(inRangeEndParam); - //endParam = UnitUtil.ScaleAngle(MathUtil.PutInRange(directrix.GetEndParameter(1), Math.PI, 2 * Math.PI) - - // MathUtil.PutInRange(originalStartParam, Math.PI, 2 * Math.PI)); } else + { endParam = 2.0 * Math.PI; + } } // Start creating IFC entities. @@ -403,9 +400,8 @@ private static void CreateAxisAndProfileCurveLCS(Curve directrix, double param, if (IFCAnyHandleUtil.IsNullOrHasNoValue(sweptArea)) return simpleSweptSolidHnd; - IFCAnyHandle curveHandle = null; IFCAnyHandle referenceSurfaceHandle = ExtrusionExporter.CreateSurfaceOfLinearExtrusionFromCurve(exporterIFC, directrix, axisLCS, 1.0, 1.0, - out curveHandle); + out IFCAnyHandle curveHandle); // Should this be moved up? Check. XYZ scaledOrigin = ExporterIFCUtils.TransformAndScalePoint(exporterIFC, axisLCS.Origin); @@ -421,12 +417,12 @@ private static void CreateAxisAndProfileCurveLCS(Curve directrix, double param, return simpleSweptSolidHnd; } - private static IList CreateRoughParametricTessellation(Curve curve) + private static IList CreateParametricTessellation(Curve curve, bool isCoarse) { IList originalTessellation = curve.Tessellate(); int numPoints = originalTessellation.Count; - int numTargetPoints = Math.Min(numPoints, 12); + int numTargetPoints = isCoarse ? Math.Min(numPoints, 12) : numPoints; int numInteriorPoints = numTargetPoints - 2; IList roughTessellation = new List(numTargetPoints); @@ -479,20 +475,20 @@ private static void AddScaledPointToList(ExporterIFC exporterIFC, IList lis /// Creates a facetation of a simple swept solid from a list of curve loops. /// /// The exporter. - /// The profile name. /// The profile curve loops. /// The normal of the plane that the path lies on. /// The path curve. + /// If true, create a coarse approximation of the directrix. /// The list of facet handles. - public static HashSet CreateSimpleSweptSolidAsBRep(ExporterIFC exporterIFC, string profileName, IList profileCurveLoops, - XYZ normal, Curve directrix) + public static HashSet CreateSimpleSweptSolidAsBRep(ExporterIFC exporterIFC, IList profileCurveLoops, + Curve directrix, bool isCoarse) { // see definition of IfcSurfaceCurveSweptAreaSolid from // http://www.buildingsmart-tech.org/ifc/IFC2x4/rc4/html/schema/ifcgeometricmodelresource/lexical/ifcsurfacecurvesweptareasolid.htm HashSet facetHnds = null; - if (!CanCreateSimpleSweptSolid(profileCurveLoops, normal, directrix)) + if (!CanCreateSimpleSweptSolid(profileCurveLoops, directrix)) return facetHnds; // An extra requirement, as we can't tessellate an unbound curve. @@ -557,7 +553,7 @@ private static void AddScaledPointToList(ExporterIFC exporterIFC, IList lis // Tessellate the Directrix. This only works for bound Directrix curves. Unfortunately, we get XYZ values, which we will have to convert // back to parameter values to get the local transform. - IList tessellatedDirectrixParameters = CreateRoughParametricTessellation(directrix); + IList tessellatedDirectrixParameters = CreateParametricTessellation(directrix, isCoarse); // Create all of the other outlines by transformng the first tessellated outline to the current transform. Transform profilePlaneTrf = Transform.CreateTranslation(ExporterIFCUtils.TransformAndScalePoint(exporterIFC, profileLCS.Origin)); diff --git a/Source/Revit.IFC.Export/Exporter/TextNoteExporter.cs b/Source/Revit.IFC.Export/Exporter/TextNoteExporter.cs index b37a7654..8563f414 100644 --- a/Source/Revit.IFC.Export/Exporter/TextNoteExporter.cs +++ b/Source/Revit.IFC.Export/Exporter/TextNoteExporter.cs @@ -117,7 +117,7 @@ public static void Export(ExporterIFC exporterIFC, TextNote textNote, ProductWra HashSet bodyItems = new HashSet() { repItemHnd }; IFCAnyHandle context2d = ExporterCacheManager.Get2DContextHandle(IFCRepresentationIdentifier.Annotation); IFCAnyHandle bodyRepHnd = RepresentationUtil.CreateAnnotationSetRep(exporterIFC, - textNote, catId, context2d, bodyItems); + textNote, catId, context2d, bodyItems, false); if (IFCAnyHandleUtil.IsNullOrHasNoValue(bodyRepHnd)) throw new Exception("Failed to create shape representation."); diff --git a/Source/Revit.IFC.Export/Exporter/WallExporter.cs b/Source/Revit.IFC.Export/Exporter/WallExporter.cs index 7a837fe8..df038d60 100644 --- a/Source/Revit.IFC.Export/Exporter/WallExporter.cs +++ b/Source/Revit.IFC.Export/Exporter/WallExporter.cs @@ -76,7 +76,7 @@ private static bool GetDifferenceFromWallJoins(Document doc, ElementId wallId, S if (solidMeshInfo.GetMeshes().Count != 0) return false; - IList solidInfos = solidMeshInfo.GetSolidInfos(); + IList solidInfos = solidMeshInfo.SolidInfoList; foreach (SolidInfo solidInfo in solidInfos) { try @@ -492,6 +492,12 @@ private static bool HasUnsupportedCutsWallSweeps(Wall wallElement, bool hasCutsW baseBodyItemHnd = null; bodyItemHnd = null; } + //If there is clipping right next to the opening it can cause incorrect geometry as far one clipping face will be missed. In this case, export wall it as BRep. + else if (wallHasOpening && hasClipping && IsOpeningsIntersectClippings(wallElement, openingDataList)) + { + tr.RollBack(); + return null; + } else tr.Commit(); } @@ -586,7 +592,7 @@ private static void GetSolidsAndMeshes(Document doc, ExporterIFC exporterIFC, Ge (range == null) ? GeometryUtil.GetSplitSolidMeshGeometry(geometryElement) : GeometryUtil.GetSplitClippedSolidMeshGeometry(geometryElement, range); - foreach (SolidInfo solidInfo in solidMeshInfo.GetSolidInfos()) + foreach (SolidInfo solidInfo in solidMeshInfo.SolidInfoList) { // Walls can have integral wall sweeps. These wall sweeps will be exported // separately by the WallSweep element itself. If we try to include the wall sweep @@ -618,7 +624,7 @@ private static IList GetSolidOfWallSweep(GeometryElement geometryElement, (range == null) ? GeometryUtil.GetSplitSolidMeshGeometry(geometryElement) : GeometryUtil.GetSplitClippedSolidMeshGeometry(geometryElement, range); - foreach (SolidInfo solidInfo in solidMeshInfo.GetSolidInfos()) + foreach (SolidInfo solidInfo in solidMeshInfo.SolidInfoList) { if (solidInfo.OwnerElement is WallSweep) solids.Add(solidInfo.Solid); @@ -733,13 +739,6 @@ private static bool HasUnsupportedStackWallOpenings(Wall wallElement) return (openingElements != null && openingElements.Count > 0); } - private static void ConditionallyAddRepresentation(bool condition, - IList representationList, IFCAnyHandle representation) - { - if (condition && !IFCAnyHandleUtil.IsNullOrHasNoValue(representation)) - representationList.Add(representation); - } - private static string CalculateElementGUID(Element element) { // The first part maintains GUID compatibility with previous versions, @@ -759,11 +758,8 @@ private static string CalculateElementGUID(Element element) Element element, Wall wallElement, out string ifcEnumType) { IFCExportInfoPair exportType = ExporterUtil.GetProductExportType(exporterIFC, element, out ifcEnumType); - IFCExportInfoPair genericExportType = new IFCExportInfoPair(exportType.ExportInstance, exportType.ExportType, ifcEnumType); - - genericExportType.SetValueWithPair(exportType.ExportInstance, ifcEnumType); - if (wallElement != null && ExporterUtil.IsNotDefined(ifcEnumType) + if (wallElement != null && exportType.IsPredefinedTypeDefault && (exportType.ExportInstance == IFCEntityType.IfcWall || exportType.ExportInstance == IFCEntityType.IfcWallStandardCase)) { WallType wallType = wallElement.WallType; @@ -831,14 +827,14 @@ private static string CalculateElementGUID(Element element) bool exportByComponents = false; bool exportParts = false; bool setMaterialNameToPartName = false; - MaterialLayerSetInfo layersetInfo = new MaterialLayerSetInfo(exporterIFC, element, origWrapper); + MaterialLayerSetInfo layersetInfo = new MaterialLayerSetInfo(exporterIFC, element, origWrapper, geometryElement); // For IFC4RV export, wall will be split into its parts(temporarily) in order to export the wall by its parts // If Parts are created by code and not by user then their name should be equal to Material name. if (exportingWallElement) // If it is not Wall, e.g. FamilyInstance, skip split to Parts as this may cause problem later { setMaterialNameToPartName = ExporterUtil.CreateParts(element, layersetInfo.MaterialIds.Count, ref geometryElement); - ExporterUtil.ExportPartAs exportPartAs = ExporterUtil.CanExportByComponentsOrParts(element); + ExporterUtil.ExportPartAs exportPartAs = ExporterUtil.CanExportByComponentsOrParts(element, ref geometryElement); exportByComponents = (exportPartAs == ExporterUtil.ExportPartAs.ShapeAspect) && exportingWallElement; exportParts = exportPartAs == ExporterUtil.ExportPartAs.Part; } @@ -856,16 +852,10 @@ private static string CalculateElementGUID(Element element) if (!exportParts) { if (!(element is FamilyInstance)) - { + { // For IFC4 RV, only collect the solid and mesh here. Split Wall will be handled when processing individual parts later - if (exportByComponents) - { - GetSolidsAndMeshes(element.Document, exporterIFC, geometryElement, null, ref solids, ref meshes, out hasCutsWallSweep); - } - else - { - GetSolidsAndMeshes(element.Document, exporterIFC, geometryElement, range, ref solids, ref meshes, out hasCutsWallSweep); - } + GetSolidsAndMeshes(element.Document, exporterIFC, geometryElement, exportByComponents ? null : range, + ref solids, ref meshes, out hasCutsWallSweep); if (solids.Count == 0 && meshes.Count == 0) return null; } @@ -883,6 +873,7 @@ private static string CalculateElementGUID(Element element) exportingInplaceOpenings = false; geomElemToUse = geometryElement; } + Transform trf = Transform.Identity; if (geomElemToUse != geometryElement) trf = famInstWallElem.GetTransform(); @@ -941,8 +932,7 @@ private static string CalculateElementGUID(Element element) if (baseLevelId != ElementId.InvalidElementId) { Element baseLevel = doc.GetElement(baseLevelId); - if (baseLevel is Level) - baseWallElevation = (baseLevel as Level).Elevation; + baseWallElevation = (baseLevel as Level)?.Elevation ?? 0.0; } IFCAnyHandle axisRep = null; @@ -955,10 +945,10 @@ private static string CalculateElementGUID(Element element) bool exportedBodyDirectly = false; Curve centerCurve = GetWallAxis(wallElement); - XYZ localXDir = new XYZ(1, 0, 0); - XYZ localYDir = new XYZ(0, 1, 0); - XYZ localZDir = new XYZ(0, 0, 1); - XYZ localOrig = new XYZ(0, 0, 0); + XYZ localXDir = XYZ.BasisX; + XYZ localYDir = XYZ.BasisY; + XYZ localZDir = XYZ.BasisZ; + XYZ localOrig = XYZ.Zero; double eps = MathUtil.Eps(); if (centerCurve != null) @@ -1075,14 +1065,13 @@ private static string CalculateElementGUID(Element element) { string identifierOpt = "Axis"; // IFC2x2 convention string representationTypeOpt = null; - + HashSet axisItemSet = new HashSet(); if (ExporterCacheManager.ExportOptionsCache.ExportAs4ReferenceView) { - IFCAnyHandle axisHandle = GeometryUtil.CreatePolyCurveFromCurve(exporterIFC, trimmedCurve); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(axisHandle)) - axisItemSet.Add(axisHandle); + axisItemSet.AddIfNotNull(GeometryUtil.CreatePolyCurveFromCurve( + exporterIFC, trimmedCurve)); representationTypeOpt = "Curve3D"; // We use Curve3D for IFC4RV } else @@ -1092,10 +1081,9 @@ private static string CalculateElementGUID(Element element) IList tmpAxisHandles = info.GetCurves(); foreach (IFCAnyHandle axisHandle in tmpAxisHandles) { - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(axisHandle)) + if (axisItemSet.AddIfNotNull(axisHandle)) { // We will only export the first curve as the axis. - axisItemSet.Add(axisHandle); break; } } @@ -1109,41 +1097,62 @@ private static string CalculateElementGUID(Element element) IFCAnyHandle contextOfItemsAxis = ExporterCacheManager.Get3DContextHandle(IFCRepresentationIdentifier.Axis); axisRep = RepresentationUtil.CreateShapeRepresentation(exporterIFC, element, catId, contextOfItemsAxis, identifierOpt, representationTypeOpt, axisItemSet); - - // If it is export by components, there will be no body at this step, the exportedAsWallWithAxis will be set to true here - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(axisRep) && exportByComponents) - exportedAsWallWithAxis = true; } } } - exportType = CalculateExportType(exporterIFC, element, wallElement, out ifcEnumType); - - IList cutPairOpenings = new List(); - // We only try to export by extrusion using this function if: - // 1. We aren't trying to create parts or components. - // 2. We have a native Revit wall whose non-trimmed axis we are exporting. - // 3. We don't have a wall that's part of a stacked wall, if the stacked wall has openings. - // Any of the cases above could mean that the internal API function would return - // incorrect results (generally, missing openings or clippings). - - if (CanTryToCreateAsExtrusion(wallElement, exportParts, exportByComponents, exportingWallElement, - exportingAxis, trimmedCurve, isCurtainPanel, hasCutsWallSweep)) + // We try this first because it can fail. If it does, we revert to standard export. + using (IFCExportBodyParams extraParams = new IFCExportBodyParams()) { - bool isCompletelyClipped; - bodyRep = TryToCreateAsExtrusion(exporterIFC, wallElement, exportType, - connectedWalls, solids, meshes, baseWallElevation, catId, - centerCurve, trimmedCurve, orientationTrf, depth, zSpan, range, setter, - out cutPairOpenings, out isCompletelyClipped, out scaledFootprintArea, out scaledLength); - if (isCompletelyClipped) - return null; + IFCExportBodyParams partECData = null; + IFCAnyHandle prodRep = null; + IFCAnyHandle hostShapeRepFromPartsList = null; + + if (!exportParts && exportByComponents) + { + partECData = new IFCExportBodyParams(); + hostShapeRepFromPartsList = PartExporter.ExportHostPartAsShapeAspects(exporterIFC, + element, prodRep, localWrapper, setter, localPlacement, overrideLevelId, layersetInfo, + partECData, solidsOfWallSweep); + if (IFCAnyHandleUtil.IsNullOrHasNoValue(hostShapeRepFromPartsList)) + { + partECData.ClearOpenings(); + extraParams.ClearOpenings(); + exportByComponents = false; + } + } - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(bodyRep)) + // If it is export by components, there will be no body at this step, the exportedAsWallWithAxis will be set to true here + if (exportByComponents && !IFCAnyHandleUtil.IsNullOrHasNoValue(axisRep)) + { exportedAsWallWithAxis = true; - } + } + + exportType = CalculateExportType(exporterIFC, element, wallElement, out ifcEnumType); + + IList cutPairOpenings = new List(); + // We only try to export by extrusion using this function if: + // 1. We aren't trying to create parts or components. + // 2. We have a native Revit wall whose non-trimmed axis we are exporting. + // 3. We don't have a wall that's part of a stacked wall, if the stacked wall has openings. + // Any of the cases above could mean that the internal API function would return + // incorrect results (generally, missing openings or clippings). + + if (CanTryToCreateAsExtrusion(wallElement, exportParts, exportByComponents, exportingWallElement, + exportingAxis, trimmedCurve, isCurtainPanel, hasCutsWallSweep)) + { + bool isCompletelyClipped; + bodyRep = TryToCreateAsExtrusion(exporterIFC, wallElement, exportType, + connectedWalls, solids, meshes, baseWallElevation, catId, + centerCurve, trimmedCurve, orientationTrf, depth, zSpan, range, setter, + out cutPairOpenings, out isCompletelyClipped, out scaledFootprintArea, out scaledLength); + if (isCompletelyClipped) + return null; + + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(bodyRep)) + exportedAsWallWithAxis = true; + } - using (IFCExportBodyParams extraParams = new IFCExportBodyParams()) - { BodyData bodyData = null; // If it is not a Wall object (FamilyInstance) then this part needs to run even for IFC4RV @@ -1207,19 +1216,24 @@ private static string CalculateElementGUID(Element element) } } - IFCAnyHandle prodRep = null; if (!exportParts) { IList representations = new List(); - ConditionallyAddRepresentation(exportingAxis, representations, axisRep); - ConditionallyAddRepresentation(true, representations, bodyRep); - + if (exportingAxis) + representations.AddIfNotNull(axisRep); + //If at this step we do not have a body representation, it is possible that + //the body representation was created during PartExporter.ExportHostPartAsShapeAspects execution. + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(bodyRep)) + representations.Add(bodyRep); + else + representations.AddIfNotNull(hostShapeRepFromPartsList); + IFCAnyHandle boundingBoxRep = null; if ((solids.Count > 0) || (meshes.Count > 0)) boundingBoxRep = BoundingBoxExporter.ExportBoundingBox(exporterIFC, solids, meshes, Transform.Identity); else boundingBoxRep = BoundingBoxExporter.ExportBoundingBox(exporterIFC, geometryElement, Transform.Identity); - ConditionallyAddRepresentation(true, representations, boundingBoxRep); + representations.AddIfNotNull(boundingBoxRep); prodRep = IFCInstanceExporter.CreateProductDefinitionShape(file, null, null, representations); } @@ -1229,13 +1243,13 @@ private static string CalculateElementGUID(Element element) IFCAnyHandle wallHnd = null; string elemGUID = CalculateElementGUID(element); - + if (exportedAsWallWithAxis && CanExportAsWallStandardCase(wallElement, exportParts) && (exportType.ExportInstance == IFCEntityType.IfcWall || exportType.ExportInstance == IFCEntityType.IfcWallStandardCase)) { wallHnd = IFCInstanceExporter.CreateWallStandardCase(exporterIFC, element, elemGUID, ownerHistory, localPlacement, prodRep, ifcEnumType); - exportType.SetValueWithPair(IFCEntityType.IfcWallStandardCase, ifcEnumType); + exportType.SetByTypeAndPredefinedType(IFCEntityType.IfcWallStandardCase, ifcEnumType); } else { @@ -1244,25 +1258,14 @@ private static string CalculateElementGUID(Element element) } if (exportParts && !exportByComponents) + { PartExporter.ExportHostPart(exporterIFC, element, wallHnd, localWrapper, setter, localPlacement, overrideLevelId, setMaterialNameToPartName); + } else if (exportByComponents) { - using (IFCExportBodyParams partECData = new IFCExportBodyParams()) - { - IFCAnyHandle hostShapeRepFromPartsList = PartExporter.ExportHostPartAsShapeAspects(exporterIFC, element, prodRep, - localWrapper, setter, localPlacement, overrideLevelId, layersetInfo, partECData, solidsOfWallSweep); - if (IFCAnyHandleUtil.IsNullOrHasNoValue(hostShapeRepFromPartsList)) - { - partECData.ClearOpenings(); - extraParams.ClearOpenings(); - // Delete Wall handle when there is no representation from the parts and return null - IFCAnyHandleUtil.Delete(wallHnd); - return null; - } - - Transform offsetTransform = (bodyData != null) ? bodyData.OffsetTransform : Transform.Identity; - OpeningUtil.CreateOpeningsIfNecessary(wallHnd, element, partECData, offsetTransform, exporterIFC, localPlacement, setter, localWrapper); - } + // We can only associate the openings with the wall after the wall handle has been created. + Transform offsetTransform = (bodyData != null) ? bodyData.OffsetTransform : Transform.Identity; + OpeningUtil.CreateOpeningsIfNecessary(wallHnd, element, partECData, offsetTransform, exporterIFC, localPlacement, setter, localWrapper); } localWrapper.AddElement(element, wallHnd, setter, extraParams, true, exportType); @@ -1297,7 +1300,7 @@ private static string CalculateElementGUID(Element element) scaledLength = MathUtil.IsAlmostZero(scaledLength) ? extraParams.ScaledLength : scaledLength; if (exportByComponents && layersetInfo != null) { - PropertyUtil.CreateWallBaseQuantities(exporterIFC, wallElement, solids, meshes, wallHnd, scaledLength, depth, + PropertyUtil.CreateWallBaseQuantities(exporterIFC, wallElement, solids, meshes, wallHnd, scaledLength, depth, scaledFootprintArea, extraParams, layersetInfo.LayerQuantityWidthHnd); } else @@ -1307,7 +1310,7 @@ private static string CalculateElementGUID(Element element) } } else - { + { if (!exportParts) { // Only export one material for 2x2; for future versions, export the whole list. @@ -1338,7 +1341,7 @@ private static string CalculateElementGUID(Element element) scaledLength = MathUtil.IsAlmostZero(scaledLength) ? extraParams.ScaledLength : scaledLength; if (layersetInfo != null) { - PropertyUtil.CreateWallBaseQuantities(exporterIFC, wallElement, solids, meshes, wallHnd, scaledLength, depth, + PropertyUtil.CreateWallBaseQuantities(exporterIFC, wallElement, solids, meshes, wallHnd, scaledLength, depth, scaledFootprintArea, extraParams, layersetInfo.LayerQuantityWidthHnd); } else @@ -1355,7 +1358,7 @@ private static string CalculateElementGUID(Element element) HostObject hostObject = element as HostObject; if (!ExporterCacheManager.ExportOptionsCache.ExportAs2x2 || exportedAsWallWithAxis) HostObjectExporter.ExportHostObjectMaterials(exporterIFC, hostObject, localWrapper.GetAnElement(), - geometryElement, localWrapper, wallLevelId, Toolkit.IFCLayerSetDirection.Axis2, !exportedAsWallWithAxis, null); + geometryElement, localWrapper, wallLevelId, Toolkit.IFCLayerSetDirection.Axis2, !exportedAsWallWithAxis, null, layersetInfo); } ExportGenericType(exporterIFC, localWrapper, wallHnd, element, matId, ifcEnumType); @@ -1391,7 +1394,7 @@ private static string CalculateElementGUID(Element element) // We will not split walls and columns if the assemblyId is set, as we would like to keep the original wall // associated with the assembly, on the level of the assembly. - bool splitWall = ExporterCacheManager.ExportOptionsCache.WallAndColumnSplitting && (element.AssemblyInstanceId == ElementId.InvalidElementId); + bool splitWall = ExporterCacheManager.ExportOptionsCache.WallAndColumnSplitting && !ExporterUtil.IsContainedInAssembly(element); if (splitWall) { IList levels = new List(); @@ -1405,9 +1408,9 @@ private static string CalculateElementGUID(Element element) int numPartsToExport = ranges.Count; if (numPartsToExport == 0) { - IFCAnyHandle wallElemHnd = ExportWallBase(exporterIFC, ifcEnumType, element, connectedWalls, ref geometryElement, productWrapper, ElementId.InvalidElementId, null); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(wallElemHnd)) - createdWalls.Add(wallElemHnd); + createdWalls.AddIfNotNull(ExportWallBase(exporterIFC, ifcEnumType, element, + connectedWalls, ref geometryElement, productWrapper, + ElementId.InvalidElementId, null)); } else { @@ -1416,9 +1419,9 @@ private static string CalculateElementGUID(Element element) for (int ii = 0; ii < numPartsToExport; ii++) { rangeSetter.IncreaseRangeIndex(); - IFCAnyHandle wallElemHnd = ExportWallBase(exporterIFC, ifcEnumType, element, connectedWalls, ref geometryElement, productWrapper, levels[ii], ranges[ii]); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(wallElemHnd)) - createdWalls.Add(wallElemHnd); + createdWalls.AddIfNotNull(ExportWallBase(exporterIFC, ifcEnumType, + element, connectedWalls, ref geometryElement, productWrapper, + levels[ii], ranges[ii])); } } } @@ -1431,9 +1434,8 @@ private static string CalculateElementGUID(Element element) foreach (KeyValuePair levelRange in levelRangeList) { rangeSetter.IncreaseRangeIndex(); - IFCAnyHandle wallElemHnd = ExportDummyWall(exporterIFC, element, geometryElement, productWrapper, levelRange.Key, levelRange.Value); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(wallElemHnd)) - createdWalls.Add(wallElemHnd); + createdWalls.AddIfNotNull(ExportDummyWall(exporterIFC, element, + geometryElement, productWrapper, levelRange.Key, levelRange.Value)); } } } @@ -1463,7 +1465,9 @@ public static void Export(ExporterIFC exporterIFC, Wall wallElement, ref Geometr // We originally skipped exporting the wall only if the containing curtain wall was also exported. // However, if the container isn't being exported, the panel shouldn't be either. if ((container is Wall) && ((container as Wall).CurtainGrid != null)) + { return; + } } } @@ -1475,14 +1479,20 @@ public static void Export(ExporterIFC exporterIFC, Wall wallElement, ref Geometr // We skip over the "stacked wall" but the invidual walls inside that stacked wall will still be exported. if (wallTypeKind == WallKind.Stacked) + { return; + } - IList> connectedWalls = new List>(2); - connectedWalls.Add(ExporterIFCUtils.GetConnectedWalls(wallElement, IFCConnectedWallDataLocation.Start)); - connectedWalls.Add(ExporterIFCUtils.GetConnectedWalls(wallElement, IFCConnectedWallDataLocation.End)); + IList> connectedWalls = new List> + { + ExporterIFCUtils.GetConnectedWalls(wallElement, IFCConnectedWallDataLocation.Start), + ExporterIFCUtils.GetConnectedWalls(wallElement, IFCConnectedWallDataLocation.End) + }; if (CurtainSystemExporter.IsCurtainSystem(wallElement)) + { CurtainSystemExporter.ExportWall(exporterIFC, wallElement, productWrapper); + } else { // ExportWall may decide to export as an IfcFooting for some retaining and foundation walls. @@ -1628,7 +1638,7 @@ public static void Export(ExporterIFC exporterIFC, Wall wallElement, ref Geometr return; IFCExportInfoPair exportType = ExporterUtil.GetProductExportType(exporterIFC, element, out _); - exportType.ValidatedPredefinedType = ifcTypeEnum; + exportType.PredefinedType = ifcTypeEnum; IFCAnyHandle wallType = ExporterCacheManager.ElementTypeToHandleCache.Find(elementType, exportType); if (!IFCAnyHandleUtil.IsNullOrHasNoValue(wallType)) @@ -1638,8 +1648,7 @@ public static void Export(ExporterIFC exporterIFC, Wall wallElement, ref Geometr } string guid = GUIDUtil.GenerateIFCGuidFrom(elementType, exportType); - wallType = FamilyExporterUtil.ExportGenericType(exporterIFC, exportType, - exportType.ValidatedPredefinedType, null, null, element, elementType, guid); + wallType = FamilyExporterUtil.ExportGenericType(exporterIFC, exportType, null, null, element, elementType, guid); wrapper.RegisterHandleWithElementType(elementType, exportType, wallType, null); @@ -1888,6 +1897,64 @@ static bool IsConnectedWithNonVerticalWall(IList> co return false; } + /// + /// Identifies if any of the wall openings intersect with the floor clippings. + /// + /// The wall element. + /// The wall openings data. + /// Returns true if any of wall openings intersect with floor clippings + static bool IsOpeningsIntersectClippings(Wall wallElement, IList openingDataList) + { + Document doc = wallElement.Document; + ICollection joinedElements = JoinGeometryUtils.GetJoinedElements(doc, wallElement); + + foreach (IFCOpeningData openingData in openingDataList) + { + ElementId openingId = openingData.OpeningElementId; + Element openingElement = doc.GetElement(openingId); + if (openingElement == null) + continue; + + GeometryElement openingGeometry = openingElement.get_Geometry(new Options()); + if (openingGeometry == null) + continue; + + SolidMeshGeometryInfo openingSolidMeshInfo = GeometryUtil.GetSplitSolidMeshGeometry(openingGeometry); + foreach (ElementId joinedElementId in joinedElements) + { + Element joinedElement = doc.GetElement(joinedElementId); + if (joinedElement == null || !(joinedElement is Floor)) + continue; + + GeometryElement joinedElementGeometry = joinedElement.get_Geometry(new Options()); + if (joinedElementGeometry == null) + continue; + + SolidMeshGeometryInfo joinedElementSolidMeshInfo = GeometryUtil.GetSplitSolidMeshGeometry(openingGeometry); + + foreach (SolidInfo openingSolidInfo in openingSolidMeshInfo.SolidInfoList) + { + foreach (SolidInfo joinedElementSolidInfo in joinedElementSolidMeshInfo.SolidInfoList) + { + try + { + BooleanOperationsUtils.ExecuteBooleanOperationModifyingOriginalSolid(openingSolidInfo.Solid, + joinedElementSolidInfo.Solid, BooleanOperationsType.Intersect); + } + catch + { + continue; + } + + return true; + } + } + } + } + + return false; + } + /// /// Gets the curve loop(s) that represent the bottom or top face of the wall. /// @@ -1963,4 +2030,4 @@ static IFCConnectionType GetIFCConnectionTypeFromLocation(IFCConnectedWallDataLo } } } -} \ No newline at end of file +} diff --git a/Source/Revit.IFC.Export/Exporter/WallSweepExporter.cs b/Source/Revit.IFC.Export/Exporter/WallSweepExporter.cs index 4f5ef559..64b75a6d 100644 --- a/Source/Revit.IFC.Export/Exporter/WallSweepExporter.cs +++ b/Source/Revit.IFC.Export/Exporter/WallSweepExporter.cs @@ -50,8 +50,8 @@ public static void Export(ExporterIFC exporterIFC, WallSweep wallSweep, Geometry return; HostObjectExporter.ExportHostObjectMaterials(exporterIFC, wallSweep, productWrapper.GetAnElement(), - geometryElement, productWrapper, - ElementId.InvalidElementId, Toolkit.IFCLayerSetDirection.Axis2, null, null); + geometryElement, productWrapper, + ElementId.InvalidElementId, Toolkit.IFCLayerSetDirection.Axis2, null, null); } } } diff --git a/Source/Revit.IFC.Export/Exporter/ZoneInfo.cs b/Source/Revit.IFC.Export/Exporter/ZoneInfo.cs index 0f1f804d..372c7883 100644 --- a/Source/Revit.IFC.Export/Exporter/ZoneInfo.cs +++ b/Source/Revit.IFC.Export/Exporter/ZoneInfo.cs @@ -19,6 +19,7 @@ using System; using System.Collections.Generic; +using System.Linq; using Autodesk.Revit.DB; using Autodesk.Revit.DB.IFC; using Revit.IFC.Common.Utility; @@ -70,7 +71,7 @@ void InitBasePropZoneLabels() } } - private int CurrentZoneNumber { get; set; } = 1; + public int CurrentZoneNumber { get; private set; } = 1; private void SetPropZoneLabels() { @@ -182,9 +183,7 @@ public class ZoneInfo /// /// Container with string information. /// The room handle for this zone. - /// The Pset_ZoneCommon handle for this zone. - public ZoneInfo(ZoneInfoFinder zoneInfoFinder, IFCAnyHandle roomHandle, - IFCAnyHandle zoneCommonPSetHandle) + public ZoneInfo(ZoneInfoFinder zoneInfoFinder, IFCAnyHandle roomHandle) { if (zoneInfoFinder != null) { @@ -195,7 +194,6 @@ public class ZoneInfo } RoomHandles.Add(roomHandle); - ZoneCommonProperySetHandle = zoneCommonPSetHandle; } /// @@ -222,22 +220,17 @@ public void UpdateZoneInfo(ZoneInfoFinder zoneInfoFinder) if (zoneInfoFinder == null) return; - string newObjectType = zoneInfoFinder.GetPropZoneValue(ZoneInfoLabel.ObjectType); - string newDescription = zoneInfoFinder.GetPropZoneValue(ZoneInfoLabel.Description); - string newLongName = zoneInfoFinder.GetPropZoneValue(ZoneInfoLabel.LongName); - string newGroupName = zoneInfoFinder.GetPropZoneValue(ZoneInfoLabel.GroupName); - if (string.IsNullOrEmpty(ObjectType)) - ObjectType = newObjectType; + ObjectType = zoneInfoFinder.GetPropZoneValue(ZoneInfoLabel.ObjectType); if (string.IsNullOrEmpty(Description)) - Description = newDescription; + Description = zoneInfoFinder.GetPropZoneValue(ZoneInfoLabel.Description); if (string.IsNullOrEmpty(LongName)) - LongName = newLongName; + LongName = zoneInfoFinder.GetPropZoneValue(ZoneInfoLabel.LongName); if (string.IsNullOrEmpty(GroupName)) - GroupName = newGroupName; + GroupName = zoneInfoFinder.GetPropZoneValue(ZoneInfoLabel.GroupName); } /// @@ -285,6 +278,152 @@ public void ConditionalAddClassification(IFCFile file, string zoneClassification ExporterCacheManager.ClassificationCache.FindOrCreateClassificationReference(file, key); } + static private IFCAnyHandle CreateLabelPropertyFromPattern(string[] patterns, string basePropertyName, + IFCFile file, Element element) + { + foreach (string pattern in patterns) + { + string propertyName = string.Format(pattern, basePropertyName); + IFCAnyHandle propSingleValue = PropertyUtil.CreateLabelPropertyFromElement(file, element, + propertyName, BuiltInParameter.INVALID, basePropertyName, PropertyValueType.SingleValue, + null); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propSingleValue)) + return propSingleValue; + } + return null; + } + + static private IFCAnyHandle CreateIdentifierPropertyFromPattern(string[] patterns, string basePropertyName, + IFCFile file, Element element) + { + foreach (string pattern in patterns) + { + string propertyName = string.Format(pattern, basePropertyName); + IFCAnyHandle propSingleValue = PropertyUtil.CreateIdentifierPropertyFromElement(file, + element, propertyName, BuiltInParameter.INVALID, basePropertyName, + PropertyValueType.SingleValue); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propSingleValue)) + return propSingleValue; + } + return null; + } + + static private IFCAnyHandle CreateAreaMeasurePropertyFromPattern(string[] patterns, string basePropertyName, + IFCFile file, Element element) + { + foreach (string pattern in patterns) + { + string propertyName = string.Format(pattern, basePropertyName); + IFCAnyHandle propSingleValue = PropertyUtil.CreateAreaPropertyFromElement(file, + element, propertyName, BuiltInParameter.INVALID, basePropertyName, + PropertyValueType.SingleValue); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propSingleValue)) + return propSingleValue; + } + return null; + } + + static private IFCAnyHandle CreateBooleanPropertyFromPattern(string[] patterns, string basePropertyName, + IFCFile file, Element element) + { + foreach (string pattern in patterns) + { + string propertyName = string.Format(pattern, basePropertyName); + IFCAnyHandle propSingleValue = PropertyUtil.CreateBooleanPropertyFromElement(file, element, + propertyName, basePropertyName, PropertyValueType.SingleValue); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(propSingleValue)) + return propSingleValue; + } + return null; + } + + /// + /// Get the name of the net planned area property, depending on the current schema, for levels and zones. + /// + /// The name of the net planned area property. + /// Note that PSet_SpaceCommon has had the property "NetPlannedArea" since IFC2x3. + static private string GetLevelAndZoneNetPlannedAreaName() + { + return ExporterCacheManager.ExportOptionsCache.ExportAsOlderThanIFC4 ? "NetAreaPlanned" : "NetPlannedArea"; + } + + /// + /// Get the name of the gross planned area property, depending on the current schema, for levels and zones. + /// + /// The name of the net planned area property. + /// Note that PSet_SpaceCommon has had the property "GrossPlannedArea" since IFC2x3. + static private string GetLevelAndZoneGrossPlannedAreaName() + { + return ExporterCacheManager.ExportOptionsCache.ExportAsOlderThanIFC4 ? "GrossAreaPlanned" : "GrossPlannedArea"; + } + + /// + /// Collect the information needed to create PSet_ZoneCommon. + /// + /// The IFC file. + /// The Revit element. + /// The index of the zone for the current space. + public void CollectZoneCommonPSetData(IFCFile file, Element element, int index) + { + // We don't use the generic Property Set mechanism because Zones aren't "real" elements. + string indexString = (index > 1) ? index.ToString() : string.Empty; + const string basePSetName = "Pset_ZoneCommon"; + + string[] patterns = new string[2] { + basePSetName + indexString + ".{0}", + "Zone" + "{0}" + indexString + }; + + ZoneCommonHandles.AddIfNotNullAndNewKey("Category", + CreateLabelPropertyFromPattern(patterns, "Category", file, element)); + + string grossPlannedAreaName = GetLevelAndZoneGrossPlannedAreaName(); + ZoneCommonHandles.AddIfNotNullAndNewKey(grossPlannedAreaName, + CreateAreaMeasurePropertyFromPattern(patterns, grossPlannedAreaName, file, + element)); + + string netPlannedAreaName = GetLevelAndZoneNetPlannedAreaName(); + ZoneCommonHandles.AddIfNotNullAndNewKey(netPlannedAreaName, + CreateAreaMeasurePropertyFromPattern(patterns, netPlannedAreaName, file, element)); + + ZoneCommonHandles.AddIfNotNullAndNewKey("PubliclyAccessible", + CreateBooleanPropertyFromPattern(patterns, "PubliclyAccessible", file, element)); + + ZoneCommonHandles.AddIfNotNullAndNewKey("HandicapAccessible", + CreateBooleanPropertyFromPattern(patterns, "HandicapAccessible", file, element)); + + ZoneCommonHandles.AddIfNotNullAndNewKey("IsExternal", + CreateBooleanPropertyFromPattern(patterns, "IsExternal", file, element)); + + if (ExporterCacheManager.ExportOptionsCache.ExportAsOlderThanIFC4x3) + { + ZoneCommonHandles.AddIfNotNullAndNewKey("Reference", + CreateIdentifierPropertyFromPattern(patterns, "Reference", file, element)); + } + + if (ZoneCommonHandles.Count > 0 && ZoneCommonGUID == null) + { + string psetName = basePSetName + indexString; + ZoneCommonGUID = GUIDUtil.GenerateIFCGuidFrom( + GUIDUtil.CreateGUIDString(element, psetName)); + } + } + + /// + /// Create the PSet_ZoneCommon property set, if applicable. + /// + /// The IFCFile parameter. + /// The handle to the PSet_ZoneCommon property set, if created. + public IFCAnyHandle CreateZoneCommonPSetData(IFCFile file) + { + if (ZoneCommonHandles.Count == 0 || ZoneCommonGUID == null) + return null; + + return IFCInstanceExporter.CreatePropertySet(file, ZoneCommonGUID, + ExporterCacheManager.OwnerHistoryHandle, "PSet_ZoneCommon", null, + ZoneCommonHandles.Values.ToHashSet()); + } + /// /// The long name, for IFC4+. /// @@ -301,9 +440,12 @@ public void ConditionalAddClassification(IFCFile file, string zoneClassification public IDictionary ClassificationReferences { get; set; } = new Dictionary(); + public IDictionary ZoneCommonHandles { get; set; } = + new Dictionary(); + /// - /// The associated Pset_ZoneCommon handle, if any. + /// The GUID for the Pset_ZoneCommon. /// - public IFCAnyHandle ZoneCommonProperySetHandle { get; set; } = null; + public string ZoneCommonGUID { get; private set; } = null; } } \ No newline at end of file diff --git a/Source/Revit.IFC.Export/Properties/AssemblyInfo.cs b/Source/Revit.IFC.Export/Properties/AssemblyInfo.cs index 3c41c267..18a06945 100644 --- a/Source/Revit.IFC.Export/Properties/AssemblyInfo.cs +++ b/Source/Revit.IFC.Export/Properties/AssemblyInfo.cs @@ -1,7 +1,6 @@ using System.Reflection; #region Using directives -#if IFC_OPENSOURCE // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. @@ -10,17 +9,14 @@ [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Autodesk")] [assembly: AssemblyProduct("IFC Exporter for Revit")] -[assembly: AssemblyCopyright("© 2012-2023 Autodesk, Inc. All rights reserved.")] +[assembly: AssemblyCopyright("© 2012-2024 Autodesk, Inc. All rights reserved.")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] -[assembly: AssemblyVersion("24.2.0.49")] -[assembly: AssemblyFileVersion("24.2.0.49")] - -#endif +[assembly: AssemblyVersion("25.2.0.5")] +[assembly: AssemblyFileVersion("25.2.0.5")] // Version information can now be found in Source\Foundation\RevitENU\Version.cs // #endregion - diff --git a/Source/Revit.IFC.Export/Properties/Resources.Designer.cs b/Source/Revit.IFC.Export/Properties/Resources.Designer.cs index a5a45b5d..9496f017 100644 --- a/Source/Revit.IFC.Export/Properties/Resources.Designer.cs +++ b/Source/Revit.IFC.Export/Properties/Resources.Designer.cs @@ -19,7 +19,7 @@ namespace Revit.IFC.Export.Properties { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Resources { diff --git a/Source/Revit.IFC.Export/Revit.IFC.Export.csproj b/Source/Revit.IFC.Export/Revit.IFC.Export.csproj index 251b1a8e..7994e715 100644 --- a/Source/Revit.IFC.Export/Revit.IFC.Export.csproj +++ b/Source/Revit.IFC.Export/Revit.IFC.Export.csproj @@ -1,356 +1,28 @@ - - + - Debug - x86 - 8.0.30703 - 2.0 - {BCE5141A-291B-4CD8-A69B-7B9345AA00E9} - Library - Properties Revit.IFC.Export Revit.IFC.Export - v4.8 - - - 512 - {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - 4 - Publish\ - true - Disk - false - Foreground - 7 - Days - false - false - true - 0 - 1.0.0.%2a - false - false - true - Revit.IFC - - - - - - false - x64 - bin\Debugx64\ - - - false - bin\Releasex64\ - x64 + True + + - - ..\..\..\..\Program Files\Autodesk\Revit 2024\RevitAPI.dll - - - ..\..\..\..\Program Files\Autodesk\Revit 2024\RevitAPIIFC.dll - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + True True Resources.resx - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - False - .NET Framework 2.0 %28x86%29 - false - - - False - .NET Framework 3.0 %28x86%29 - true - - - False - .NET Framework 3.5 - false - - - False - Windows Installer 3.1 - true - - {032EA4DC-181F-4453-9F93-E08DE1C07D95} - Revit.IFC.Common False + All + All @@ -364,40 +36,49 @@ MSBuild:Compile Revit.IFC.Export.Utility - PreserveNewest + True + True - - - 4.6.6 - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - 4.6.6 - - - 0.1.22 - - - 10.0.3 - - - - - xcopy "$(TargetPath)" "C:\ProgramData\Autodesk\ApplicationPlugins\IFC 2024.bundle\Contents\2024\" /F /R /Y /I -xcopy "$(TargetDir)GeometryGymIFC.dll" "C:\ProgramData\Autodesk\ApplicationPlugins\IFC 2024.bundle\Contents\2024\" /F /R /Y /I - - - \ No newline at end of file + + + + + + all + + + 4.6.6 + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + 4.6.6 + + + + + + ..\..\..\..\Program Files\Autodesk\Revit 2025\RevitAPI.dll + + + ..\..\..\..\Program Files\Autodesk\Revit 2025\RevitAPIIFC.dll + + + ..\..\..\..\Program Files\Autodesk\Revit 2025\RevitAPIUI.dll + + + diff --git a/Source/Revit.IFC.Export/Revit.IFC.Export.props b/Source/Revit.IFC.Export/Revit.IFC.Export.props deleted file mode 100644 index 0e84fc8e..00000000 --- a/Source/Revit.IFC.Export/Revit.IFC.Export.props +++ /dev/null @@ -1,25 +0,0 @@ - - - - bin\Debugx64\ - DEBUG;TRACE;IFC_OPENSOURCE - true - false - false - 4 - full - prompt - - - bin\Releasex64\ - TRACE;IFC_OPENSOURCE - false - true - false - 4 - none - prompt - - - - diff --git a/Source/Revit.IFC.Export/Toolkit/IFCDataUtil.cs b/Source/Revit.IFC.Export/Toolkit/IFCDataUtil.cs index 2f618231..75d3041f 100644 --- a/Source/Revit.IFC.Export/Toolkit/IFCDataUtil.cs +++ b/Source/Revit.IFC.Export/Toolkit/IFCDataUtil.cs @@ -142,7 +142,7 @@ public static IFCData CreateAsInteger(int value) /// The IFCData object. public static IFCData CreateAsReal(double value) { - return CreateAsMeasure(value, "IfcReal"); + return CreateAsMeasureWithUnit(value, "IfcReal"); } /// @@ -152,7 +152,7 @@ public static IFCData CreateAsReal(double value) /// The IFCData object. public static IFCData CreateAsNumeric(double value) { - return CreateAsMeasure(value, "IfcNumericMeasure"); + return CreateAsMeasureWithUnit(value, "IfcNumericMeasure"); } /// @@ -162,7 +162,7 @@ public static IFCData CreateAsNumeric(double value) /// The IFCData object. public static IFCData CreateAsRatioMeasure(double value) { - return CreateAsMeasure(value, "IfcRatioMeasure"); + return CreateAsMeasureWithUnit(value, "IfcRatioMeasure"); } /// @@ -172,7 +172,7 @@ public static IFCData CreateAsRatioMeasure(double value) /// The IFCData object. public static IFCData CreateAsNormalisedRatioMeasure(double value) { - return CreateAsMeasure(value, "IfcNormalisedRatioMeasure"); + return CreateAsMeasureWithUnit(value, "IfcNormalisedRatioMeasure"); } /// @@ -182,7 +182,7 @@ public static IFCData CreateAsNormalisedRatioMeasure(double value) /// The IFCData object. public static IFCData CreateAsSpecularExponent(double value) { - return CreateAsMeasure(value, "IfcSpecularExponent"); + return CreateAsMeasureWithUnit(value, "IfcSpecularExponent"); } /// @@ -192,7 +192,7 @@ public static IFCData CreateAsSpecularExponent(double value) /// The IFCData object. public static IFCData CreateAsPositiveRatioMeasure(double value) { - return CreateAsMeasure(value, "IfcPositiveRatioMeasure"); + return CreateAsMeasureWithUnit(value, "IfcPositiveRatioMeasure"); } /// @@ -202,7 +202,7 @@ public static IFCData CreateAsPositiveRatioMeasure(double value) /// The IFCData object. public static IFCData CreateAsLengthMeasure(double value) { - return CreateAsMeasure(value, "IfcLengthMeasure"); + return CreateAsMeasureWithUnit(value, "IfcLengthMeasure"); } /// @@ -212,7 +212,7 @@ public static IFCData CreateAsLengthMeasure(double value) /// The IFCData object. public static IFCData CreateAsVolumeMeasure(double value) { - return CreateAsMeasure(value, "IfcVolumeMeasure"); + return CreateAsMeasureWithUnit(value, "IfcVolumeMeasure"); } /// @@ -222,7 +222,10 @@ public static IFCData CreateAsVolumeMeasure(double value) /// The IFCData object. public static IFCData CreateAsPositiveLengthMeasure(double value) { - return CreateAsMeasure(value, "IfcPositiveLengthMeasure"); + if (value > MathUtil.Eps()) + return CreateAsMeasureWithUnit(value, "IfcPositiveLengthMeasure"); + else + return null; } /// @@ -232,7 +235,10 @@ public static IFCData CreateAsPositiveLengthMeasure(double value) /// The IFCData object. public static IFCData CreateAsPositivePlaneAngleMeasure(double value) { - return CreateAsMeasure(value, "IfcPositivePlaneAngleMeasure"); + if (value > MathUtil.Eps()) + return CreateAsMeasureWithUnit(value, "IfcPositivePlaneAngleMeasure"); + else + return null; } /// @@ -242,7 +248,7 @@ public static IFCData CreateAsPositivePlaneAngleMeasure(double value) /// The IFCData object. public static IFCData CreateAsPlaneAngleMeasure(double value) { - return CreateAsMeasure(value, "IfcPlaneAngleMeasure"); + return CreateAsMeasureWithUnit(value, "IfcPlaneAngleMeasure"); } /// @@ -252,7 +258,7 @@ public static IFCData CreateAsPlaneAngleMeasure(double value) /// The IFCData object. public static IFCData CreateAsAreaMeasure(double value) { - return CreateAsMeasure(value, "IfcAreaMeasure"); + return CreateAsMeasureWithUnit(value, "IfcAreaMeasure"); } /// @@ -262,7 +268,7 @@ public static IFCData CreateAsAreaMeasure(double value) /// The IFCData object. public static IFCData CreateAsAccelerationMeasure(double value) { - return CreateAsMeasure(value, "IfcAccelerationMeasure"); + return CreateAsMeasureWithUnit(value, "IfcAccelerationMeasure"); } /// @@ -272,7 +278,43 @@ public static IFCData CreateAsAccelerationMeasure(double value) /// The IFCData object. public static IFCData CreateAsEnergyMeasure(double value) { - return CreateAsMeasure(value, "IfcEnergyMeasure"); + return CreateAsMeasureWithUnit(value, "IfcEnergyMeasure"); + } + + + /// + /// Creates corresponding ifc unit for a measure name + /// + public static void CreateCorrespondingUnit(string measureName) + { + ForgeTypeId specType = UnitMappingUtil.GetUnitSpecTypeFromString(measureName); + UnitMappingUtil.GetOrCreateUnitInfo(specType); + } + + /// + /// Creates an IFCData object as an IfcMeasure of the right type + /// and creates the corresponding Revit unit. + /// + /// The int value. + /// The type of IfcMeasure (e.g. IfcForceMeasure). + /// The IFCData object. + public static IFCData CreateAsMeasureWithUnit(int value, string measureName) + { + CreateCorrespondingUnit(measureName); + return CreateAsMeasure(value, measureName); + } + + /// + /// Creates an IFCData object as an IfcMeasure of the right type + /// and creates the corresponding Revit unit. + /// + /// The double value. + /// The type of IfcMeasure (e.g. IfcForceMeasure). + /// The IFCData object. + public static IFCData CreateAsMeasureWithUnit(double value, string measureName) + { + CreateCorrespondingUnit(measureName); + return CreateAsMeasure(value, measureName); } /// @@ -282,7 +324,7 @@ public static IFCData CreateAsEnergyMeasure(double value) /// The IFCData object. public static IFCData CreateAsLinearMomentMeasure(double value) { - return CreateAsMeasure(value, "IfcLinearMomentMeasure"); + return CreateAsMeasureWithUnit(value, "IfcLinearMomentMeasure"); } /// @@ -292,7 +334,7 @@ public static IFCData CreateAsLinearMomentMeasure(double value) /// The IFCData object. public static IFCData CreateAsMassPerLengthMeasure(double value) { - return CreateAsMeasure(value, "IfcMassPerLengthMeasure"); + return CreateAsMeasureWithUnit(value, "IfcMassPerLengthMeasure"); } /// @@ -302,7 +344,7 @@ public static IFCData CreateAsMassPerLengthMeasure(double value) /// The IFCData object. public static IFCData CreateAsTorqueMeasure(double value) { - return CreateAsMeasure(value, "IfcTorqueMeasure"); + return CreateAsMeasureWithUnit(value, "IfcTorqueMeasure"); } /// @@ -312,7 +354,7 @@ public static IFCData CreateAsTorqueMeasure(double value) /// The IFCData object. public static IFCData CreateAsLinearStiffnessMeasure(double value) { - return CreateAsMeasure(value, "IfcLinearStiffnessMeasure"); + return CreateAsMeasureWithUnit(value, "IfcLinearStiffnessMeasure"); } /// @@ -322,7 +364,7 @@ public static IFCData CreateAsLinearStiffnessMeasure(double value) /// The IFCData object. public static IFCData CreateAsAngularVelocityMeasure(double value) { - return CreateAsMeasure(value, "IfcAngularVelocityMeasure"); + return CreateAsMeasureWithUnit(value, "IfcAngularVelocityMeasure"); } /// @@ -332,7 +374,7 @@ public static IFCData CreateAsAngularVelocityMeasure(double value) /// The IFCData object. public static IFCData CreateAsThermalResistanceMeasure(double value) { - return CreateAsMeasure(value, "IfcThermalResistanceMeasure"); + return CreateAsMeasureWithUnit(value, "IfcThermalResistanceMeasure"); } /// @@ -342,7 +384,7 @@ public static IFCData CreateAsThermalResistanceMeasure(double value) /// The IFCData object. public static IFCData CreateAsWarpingConstantMeasure(double value) { - return CreateAsMeasure(value, "IfcWarpingConstantMeasure"); + return CreateAsMeasureWithUnit(value, "IfcWarpingConstantMeasure"); } /// @@ -352,7 +394,7 @@ public static IFCData CreateAsWarpingConstantMeasure(double value) /// The IFCData object. public static IFCData CreateAsLinearVelocityMeasure(double value) { - return CreateAsMeasure(value, "IfcLinearVelocityMeasure"); + return CreateAsMeasureWithUnit(value, "IfcLinearVelocityMeasure"); } /// @@ -362,7 +404,7 @@ public static IFCData CreateAsLinearVelocityMeasure(double value) /// The IFCData object. public static IFCData CreateAsCountMeasure(double value) { - return CreateAsMeasure(value, "IfcCountMeasure"); + return CreateAsMeasureWithUnit(value, "IfcCountMeasure"); } /// @@ -372,7 +414,7 @@ public static IFCData CreateAsCountMeasure(double value) /// The IFCData object. public static IFCData CreateAsCountMeasure(int value) { - return CreateAsMeasure(value, "IfcCountMeasure"); + return CreateAsMeasureWithUnit(value, "IfcCountMeasure"); } /// @@ -382,7 +424,7 @@ public static IFCData CreateAsCountMeasure(int value) /// The IFCData object. public static IFCData CreateAsParameterValue(double value) { - return CreateAsMeasure(value, "IfcParameterValue"); + return CreateAsMeasureWithUnit(value, "IfcParameterValue"); } /// @@ -392,7 +434,7 @@ public static IFCData CreateAsParameterValue(double value) /// The IFCData object. public static IFCData CreateAsPowerMeasure(double value) { - return CreateAsMeasure(value, "IfcPowerMeasure"); + return CreateAsMeasureWithUnit(value, "IfcPowerMeasure"); } /// @@ -402,7 +444,7 @@ public static IFCData CreateAsPowerMeasure(double value) /// The IFCData object. public static IFCData CreateAsSoundPowerMeasure(double value) { - return CreateAsMeasure(value, "IfcSoundPowerMeasure"); + return CreateAsMeasureWithUnit(value, "IfcSoundPowerMeasure"); } /// @@ -412,7 +454,7 @@ public static IFCData CreateAsSoundPowerMeasure(double value) /// The IFCData object. public static IFCData CreateAsSoundPressureMeasure(double value) { - return CreateAsMeasure(value, "IfcSoundPressureMeasure"); + return CreateAsMeasureWithUnit(value, "IfcSoundPressureMeasure"); } /// @@ -422,27 +464,27 @@ public static IFCData CreateAsSoundPressureMeasure(double value) /// The IFCData object. public static IFCData CreateAsFrequencyMeasure(double value) { - return CreateAsMeasure(value, "IfcFrequencyMeasure"); + return CreateAsMeasureWithUnit(value, "IfcFrequencyMeasure"); } /// - /// Creates an IFCData object as IfcElectricalCurrentMeasure. + /// Creates an IFCData object as IfcElectricCurrentMeasure. /// /// The double value. /// The IFCData object. public static IFCData CreateAsElectricCurrentMeasure(double value) { - return CreateAsMeasure(value, "IfcElectricCurrentMeasure"); + return CreateAsMeasureWithUnit(value, "IfcElectricCurrentMeasure"); } /// - /// Creates an IFCData object as IfcElectricalVoltageMeasure. + /// Creates an IFCData object as IfcElectricVoltageMeasure. /// /// The double value. /// The IFCData object. public static IFCData CreateAsElectricVoltageMeasure(double value) { - return CreateAsMeasure(value, "IfcElectricVoltageMeasure"); + return CreateAsMeasureWithUnit(value, "IfcElectricVoltageMeasure"); } /// @@ -452,7 +494,7 @@ public static IFCData CreateAsElectricVoltageMeasure(double value) /// The IFCData object. public static IFCData CreateAsThermodynamicTemperatureMeasure(double value) { - return CreateAsMeasure(value, "IfcThermodynamicTemperatureMeasure"); + return CreateAsMeasureWithUnit(value, "IfcThermodynamicTemperatureMeasure"); } /// @@ -462,7 +504,7 @@ public static IFCData CreateAsThermodynamicTemperatureMeasure(double value) /// The IFCData object. public static IFCData CreateAsDynamicViscosityMeasure(double value) { - return CreateAsMeasure(value, "IfcDynamicViscosityMeasure"); + return CreateAsMeasureWithUnit(value, "IfcDynamicViscosityMeasure"); } /// @@ -472,7 +514,7 @@ public static IFCData CreateAsDynamicViscosityMeasure(double value) /// The IFCData object. public static IFCData CreateAsIsothermalMoistureCapacityMeasure(double value) { - return CreateAsMeasure(value, "IfcIsothermalMoistureCapacityMeasure"); + return CreateAsMeasureWithUnit(value, "IfcIsothermalMoistureCapacityMeasure"); } /// @@ -482,7 +524,7 @@ public static IFCData CreateAsIsothermalMoistureCapacityMeasure(double value) /// The IFCData object. public static IFCData CreateAsMassDensityMeasure(double value) { - return CreateAsMeasure(value, "IfcMassDensityMeasure"); + return CreateAsMeasureWithUnit(value, "IfcMassDensityMeasure"); } /// @@ -492,7 +534,7 @@ public static IFCData CreateAsMassDensityMeasure(double value) /// The IFCData object. public static IFCData CreateAsModulusOfElasticityMeasure(double value) { - return CreateAsMeasure(value, "IfcModulusOfElasticityMeasure"); + return CreateAsMeasureWithUnit(value, "IfcModulusOfElasticityMeasure"); } /// @@ -502,7 +544,7 @@ public static IFCData CreateAsModulusOfElasticityMeasure(double value) /// The IFCData object. public static IFCData CreateAsVaporPermeabilityMeasure(double value) { - return CreateAsMeasure(value, "IfcVaporPermeabilityMeasure"); + return CreateAsMeasureWithUnit(value, "IfcVaporPermeabilityMeasure"); } /// @@ -512,7 +554,7 @@ public static IFCData CreateAsVaporPermeabilityMeasure(double value) /// The IFCData object. public static IFCData CreateAsThermalExpansionCoefficientMeasure(double value) { - return CreateAsMeasure(value, "IfcThermalExpansionCoefficientMeasure"); + return CreateAsMeasureWithUnit(value, "IfcThermalExpansionCoefficientMeasure"); } /// @@ -522,9 +564,19 @@ public static IFCData CreateAsThermalExpansionCoefficientMeasure(double value) /// The IFCData object. public static IFCData CreateAsPressureMeasure(double value) { - return CreateAsMeasure(value, "IfcPressureMeasure"); + return CreateAsMeasureWithUnit(value, "IfcPressureMeasure"); } + /// + /// Creates an IFCData object as IfcMonetaryMeasure. + /// + /// The double value. + /// The IFCData object. + public static IFCData CreateAsMonetaryMeasure(double value) + { + return CreateAsMeasureWithUnit(value, "IfcMonetaryMeasure"); + } + /// /// Creates an IFCData object as IfcSpecificHeatCapacityMeasure. /// @@ -532,7 +584,7 @@ public static IFCData CreateAsPressureMeasure(double value) /// The IFCData object. public static IFCData CreateAsSpecificHeatCapacityMeasure(double value) { - return CreateAsMeasure(value, "IfcSpecificHeatCapacityMeasure"); + return CreateAsMeasureWithUnit(value, "IfcSpecificHeatCapacityMeasure"); } /// @@ -542,7 +594,7 @@ public static IFCData CreateAsSpecificHeatCapacityMeasure(double value) /// The IFCData object. public static IFCData CreateAsHeatingValueMeasure(double value) { - return CreateAsMeasure(value, "IfcHeatingValueMeasure"); + return CreateAsMeasureWithUnit(value, "IfcHeatingValueMeasure"); } /// @@ -552,7 +604,7 @@ public static IFCData CreateAsHeatingValueMeasure(double value) /// The IFCData object. public static IFCData CreateAsMoistureDiffusivityMeasure(double value) { - return CreateAsMeasure(value, "IfcMoistureDiffusivityMeasure"); + return CreateAsMeasureWithUnit(value, "IfcMoistureDiffusivityMeasure"); } /// @@ -562,7 +614,7 @@ public static IFCData CreateAsMoistureDiffusivityMeasure(double value) /// The IFCData object. public static IFCData CreateAsIonConcentrationMeasure(double value) { - return CreateAsMeasure(value, "IfcIonConcentrationMeasure"); + return CreateAsMeasureWithUnit(value, "IfcIonConcentrationMeasure"); } /// @@ -572,7 +624,7 @@ public static IFCData CreateAsIonConcentrationMeasure(double value) /// The IFCData object. public static IFCData CreateAsMomentOfInertiaMeasure(double value) { - return CreateAsMeasure(value, "IfcMomentOfInertiaMeasure"); + return CreateAsMeasureWithUnit(value, "IfcMomentOfInertiaMeasure"); } /// @@ -582,7 +634,7 @@ public static IFCData CreateAsMomentOfInertiaMeasure(double value) /// The IFCData object. public static IFCData CreateAsHeatFluxDensityMeasure(double value) { - return CreateAsMeasure(value, "IfcHeatFluxDensityMeasure"); + return CreateAsMeasureWithUnit(value, "IfcHeatFluxDensityMeasure"); } /// @@ -592,7 +644,7 @@ public static IFCData CreateAsHeatFluxDensityMeasure(double value) /// The IFCData object. public static IFCData CreateAsAreaDensityMeasure(double value) { - return CreateAsMeasure(value, "IfcAreaDensityMeasure"); + return CreateAsMeasureWithUnit(value, "IfcAreaDensityMeasure"); } /// @@ -602,8 +654,29 @@ public static IFCData CreateAsAreaDensityMeasure(double value) /// The IFCData object. public static IFCData CreateAsThermalConductivityMeasure(double value) { - return CreateAsMeasure(value, "IfcThermalConductivityMeasure"); + return CreateAsMeasureWithUnit(value, "IfcThermalConductivityMeasure"); + } + + /// + /// Creates an IFCData object as IfcRotationalFrequencyMeasure. + /// + /// The double value. + /// The IFCData object. + public static IFCData CreateAsRotationalFrequencyMeasure(double value) + { + return CreateAsMeasureWithUnit(value, "IfcRotationalFrequencyMeasure"); + } + + /// + /// Creates an IFCData object as IfcMassFlowRateMeasure. + /// + /// The double value. + /// The IFCData object. + public static IFCData CreateAsMassFlowRateMeasure(double value) + { + return CreateAsMeasureWithUnit(value, "IfcMassFlowRateMeasure"); } + /// /// Creates an IFCData object as IfcThermalTransmittanceMeasure. @@ -612,7 +685,7 @@ public static IFCData CreateAsThermalConductivityMeasure(double value) /// The IFCData object. public static IFCData CreateAsThermalTransmittanceMeasure(double value) { - return CreateAsMeasure(value, "IfcThermalTransmittanceMeasure"); + return CreateAsMeasureWithUnit(value, "IfcThermalTransmittanceMeasure"); } /// @@ -683,7 +756,7 @@ public static IFCData CreateRatioMeasureDataCommon(double value, PropertyType pr /// The IFCData object. public static IFCData CreateAsVolumetricFlowRateMeasure(double value) { - return CreateAsMeasure(value, "IfcVolumetricFlowRateMeasure"); + return CreateAsMeasureWithUnit(value, "IfcVolumetricFlowRateMeasure"); } /// @@ -693,7 +766,7 @@ public static IFCData CreateAsVolumetricFlowRateMeasure(double value) /// The IFCData object. public static IFCData CreateAsIlluminanceMeasure(double value) { - return CreateAsMeasure(value, "IfcIlluminanceMeasure"); + return CreateAsMeasureWithUnit(value, "IfcIlluminanceMeasure"); } /// @@ -703,7 +776,7 @@ public static IFCData CreateAsIlluminanceMeasure(double value) /// The IFCData object. public static IFCData CreateAsLuminousFluxMeasure(double value) { - return CreateAsMeasure(value, "IfcLuminousFluxMeasure"); + return CreateAsMeasureWithUnit(value, "IfcLuminousFluxMeasure"); } /// @@ -713,7 +786,7 @@ public static IFCData CreateAsLuminousFluxMeasure(double value) /// The IFCData object. public static IFCData CreateAsLuminousIntensityMeasure(double value) { - return CreateAsMeasure(value, "IfcLuminousIntensityMeasure"); + return CreateAsMeasureWithUnit(value, "IfcLuminousIntensityMeasure"); } /// @@ -723,7 +796,7 @@ public static IFCData CreateAsLuminousIntensityMeasure(double value) /// The IFCData object. public static IFCData CreateAsForceMeasure(double value) { - return CreateAsMeasure(value, "IfcForceMeasure"); + return CreateAsMeasureWithUnit(value, "IfcForceMeasure"); } /// @@ -733,7 +806,7 @@ public static IFCData CreateAsForceMeasure(double value) /// the IFCData object public static IFCData CreateAsLinearForceMeasure(double value) { - return CreateAsMeasure(value, "IfcLinearForceMeasure"); + return CreateAsMeasureWithUnit(value, "IfcLinearForceMeasure"); } /// @@ -743,7 +816,7 @@ public static IFCData CreateAsLinearForceMeasure(double value) /// the IFCData object public static IFCData CreateAsMassMeasure(double value) { - return CreateAsMeasure(value, "IfcMassMeasure"); + return CreateAsMeasureWithUnit(value, "IfcMassMeasure"); } /// @@ -753,7 +826,7 @@ public static IFCData CreateAsMassMeasure(double value) /// the IFCData object public static IFCData CreateAsTimeMeasure(double value) { - return CreateAsMeasure(value, "IfcTimeMeasure"); + return CreateAsMeasureWithUnit(value, "IfcTimeMeasure"); } /// @@ -763,7 +836,7 @@ public static IFCData CreateAsTimeMeasure(double value) /// the IFCData object public static IFCData CreateAsPlanarForceMeasure(double value) { - return CreateAsMeasure(value, "IfcPlanarForceMeasure"); + return CreateAsMeasureWithUnit(value, "IfcPlanarForceMeasure"); } /// diff --git a/Source/Revit.IFC.Export/Toolkit/IFCInstanceExporter.cs b/Source/Revit.IFC.Export/Toolkit/IFCInstanceExporter.cs index 761549e7..e486890c 100644 --- a/Source/Revit.IFC.Export/Toolkit/IFCInstanceExporter.cs +++ b/Source/Revit.IFC.Export/Toolkit/IFCInstanceExporter.cs @@ -25,6 +25,8 @@ using Revit.IFC.Export.Utility; using Revit.IFC.Common.Enums; using Revit.IFC.Common.Utility; +using Revit.IFC.Export.Exporter.PropertySet; +using System.Xml.Linq; namespace Revit.IFC.Export.Toolkit { @@ -435,19 +437,25 @@ private static void SetPropertyDefinition(IFCAnyHandle propertyDefinition, Eleme string guid, IFCAnyHandle ownerHistory, string name, string description, string objectType) { - string overrideObjectType = objectType; - if (element != null) + string overrideObjectType = (element != null) ? + NamingUtil.GetObjectTypeOverride(obj, element, objectType) : + objectType; + + if (string.IsNullOrEmpty(overrideObjectType) && (element != null)) { - if (string.IsNullOrEmpty(objectType)) - objectType = NamingUtil.GetFamilyAndTypeName(element); - overrideObjectType = NamingUtil.GetObjectTypeOverride(obj, element, objectType); + overrideObjectType = NamingUtil.GetFamilyAndTypeName(element); } + IFCAnyHandleUtil.SetAttribute(obj, "ObjectType", overrideObjectType); if (ExporterCacheManager.ExportOptionsCache.ExportAs2x2) + { SetRoot(obj, element, guid, ownerHistory, name, description); + } else + { SetObjectDefinition(obj, element, guid, ownerHistory, name, description); + } } /// @@ -554,6 +562,50 @@ private static void SetPropertyDefinition(IFCAnyHandle propertyDefinition, Eleme } } + /// + /// Sets attributes to IfcFacilityPart. + /// + /// The IfcFacilityPart. + /// The GUID. + /// The owner history. + /// The name. + /// The description. + /// The object type. + /// The object placement. + /// The representation object. + /// The long name. + /// The composition type. + private static void SetFacilityPart(IFCAnyHandle facilityPart, Element element, + string guid, IFCAnyHandle ownerHistory, string name, string description, string objectType, + IFCAnyHandle objectPlacement, IFCAnyHandle representation, string longName, + IFCElementComposition compositionType) + { + SetSpatialStructureElement(facilityPart, element, guid, ownerHistory, name, description, objectType, + objectPlacement, representation, longName, compositionType); + } + + /// + /// Sets attributes to IfcFacility. + /// + /// The IfcFacility. + /// The GUID. + /// The owner history. + /// The name. + /// The description. + /// The object type. + /// The object placement. + /// The representation object. + /// The long name. + /// The composition type. + private static void SetFacility(IFCAnyHandle facility, Element element, + string guid, IFCAnyHandle ownerHistory, string name, string description, string objectType, + IFCAnyHandle objectPlacement, IFCAnyHandle representation, string longName, + IFCElementComposition compositionType) + { + SetSpatialStructureElement(facility, element, guid, ownerHistory, name, description, objectType, + objectPlacement, representation, longName, compositionType); + } + /// /// Sets attributes to IfcSpatialStructureElement. /// @@ -568,11 +620,9 @@ private static void SetPropertyDefinition(IFCAnyHandle propertyDefinition, Eleme /// The long name. /// The composition type. private static void SetSpatialStructureElement(IFCAnyHandle spatialStructureElement, Element element, - string guid, IFCAnyHandle ownerHistory, string name, string description, - string objectType, - IFCAnyHandle objectPlacement, IFCAnyHandle representation, - string longName, - IFCElementComposition compositionType) + string guid, IFCAnyHandle ownerHistory, string name, string description, + string objectType, IFCAnyHandle objectPlacement, IFCAnyHandle representation, string longName, + IFCElementComposition compositionType) { IFCAnyHandleUtil.SetAttribute(spatialStructureElement, "CompositionType", compositionType); if (ExporterCacheManager.ExportOptionsCache.ExportAsOlderThanIFC4) @@ -1806,12 +1856,12 @@ public static IFCAnyHandle CreateLocalPlacement(IFCFile file, IFCAnyHandle place IFCAnyHandleUtil.SetAttribute(building, "ElevationOfRefHeight", elevationOfRefHeight); IFCAnyHandleUtil.SetAttribute(building, "ElevationOfTerrain", elevationOfTerrain); IFCAnyHandleUtil.SetAttribute(building, "BuildingAddress", buildingAddress); - SetSpatialStructureElement(building, null, guid, ownerHistory, name, description, objectType, objectPlacement, representation, longName, compositionType); + SetFacility(building, null, guid, ownerHistory, name, description, objectType, objectPlacement, representation, longName, compositionType); return building; } /// - /// Creates a handle representing an IfcBuildingStorey and assigns it to the file. + /// Creates an IfcBridge, and assigns it to the file. /// /// The file. /// The GUID. @@ -1823,13 +1873,111 @@ public static IFCAnyHandle CreateLocalPlacement(IFCFile file, IFCAnyHandle place /// The representation object. /// The long name. /// The composition type. - /// The elevation with flooring measurement. + /// The predefined type of the bridge. /// The handle. - public static IFCAnyHandle CreateBuildingStorey(ExporterIFC exporterIFC, Level level, IFCAnyHandle ownerHistory, string objectType, IFCAnyHandle objectPlacement, - IFCElementComposition compositionType, double elevation) + public static IFCAnyHandle CreateBridge(ExporterIFC exporterIFC, string guid, IFCAnyHandle ownerHistory, + string name, string description, string objectType, IFCAnyHandle objectPlacement, IFCAnyHandle representation, + string longName, IFCElementComposition compositionType, string predefinedType) + { + IFCAnyHandle bridge = CreateInstance(exporterIFC.GetFile(), IFCEntityType.IfcBridge, null); + IFCAnyHandleUtil.SetAttribute(bridge, "PredefinedType", predefinedType); + SetFacility(bridge, null, guid, ownerHistory, name, description, objectType, objectPlacement, representation, longName, compositionType); + return bridge; + } + + /// + /// Creates an IfcRoad, and assigns it to the file. + /// + /// The file. + /// The GUID. + /// The owner history. + /// The name. + /// The description. + /// The object type. + /// The object placement. + /// The representation object. + /// The long name. + /// The composition type. + /// The predefined type of the bridge. + /// The handle. + public static IFCAnyHandle CreateRoad(ExporterIFC exporterIFC, string guid, IFCAnyHandle ownerHistory, + string name, string description, string objectType, IFCAnyHandle objectPlacement, IFCAnyHandle representation, + string longName, IFCElementComposition compositionType, string predefinedType) { + IFCAnyHandle road = CreateInstance(exporterIFC.GetFile(), IFCEntityType.IfcRoad, null); + IFCAnyHandleUtil.SetAttribute(road, "PredefinedType", predefinedType); + SetFacility(road, null, guid, ownerHistory, name, description, objectType, objectPlacement, representation, longName, compositionType); + return road; + } + /// + /// Creates an IfcRailway, and assigns it to the file. + /// + /// The file. + /// The GUID. + /// The owner history. + /// The name. + /// The description. + /// The object type. + /// The object placement. + /// The representation object. + /// The long name. + /// The composition type. + /// The predefined type of the bridge. + /// The handle. + public static IFCAnyHandle CreateRailway(ExporterIFC exporterIFC, string guid, IFCAnyHandle ownerHistory, + string name, string description, string objectType, IFCAnyHandle objectPlacement, IFCAnyHandle representation, + string longName, IFCElementComposition compositionType, string predefinedType) + { + IFCAnyHandle railway = CreateInstance(exporterIFC.GetFile(), IFCEntityType.IfcRailway, null); + IFCAnyHandleUtil.SetAttribute(railway, "PredefinedType", predefinedType); + SetFacility(railway, null, guid, ownerHistory, name, description, objectType, objectPlacement, representation, longName, compositionType); + return railway; + } + /// + /// Creates an IfcRailway, and assigns it to the file. + /// + /// The file. + /// The GUID. + /// The owner history. + /// The name. + /// The description. + /// The object type. + /// The object placement. + /// The representation object. + /// The long name. + /// The composition type. + /// The predefined type of the bridge. + /// The handle. + public static IFCAnyHandle CreateMarineFacility(ExporterIFC exporterIFC, string guid, IFCAnyHandle ownerHistory, + string name, string description, string objectType, IFCAnyHandle objectPlacement, IFCAnyHandle representation, + string longName, IFCElementComposition compositionType, string predefinedType) + { + IFCAnyHandle marineFacility = CreateInstance(exporterIFC.GetFile(), IFCEntityType.IfcMarineFacility, null); + IFCAnyHandleUtil.SetAttribute(marineFacility, "PredefinedType", predefinedType); + SetFacility(marineFacility, null, guid, ownerHistory, name, description, objectType, objectPlacement, representation, longName, compositionType); + return marineFacility; + } + + /// + /// Creates a handle representing an IfcBuildingStorey and assigns it to the file. + /// + /// The file. + /// The GUID. + /// The owner history. + /// The name. + /// The description. + /// The object type. + /// The object placement. + /// The representation object. + /// The long name. + /// The composition type. + /// The elevation with flooring measurement. + /// The handle. + public static IFCAnyHandle CreateBuildingStorey(ExporterIFC exporterIFC, Level level, IFCAnyHandle ownerHistory, string objectType, IFCAnyHandle objectPlacement, + IFCElementComposition compositionType, double elevation) + { IFCAnyHandle buildingStorey = CreateInstance(exporterIFC.GetFile(), IFCEntityType.IfcBuildingStorey, null); string guid = GUIDUtil.GetLevelGUID(level); string name = NamingUtil.GetNameOverride(buildingStorey, level, level.Name); @@ -1837,10 +1985,120 @@ public static IFCAnyHandle CreateLocalPlacement(IFCFile file, IFCAnyHandle place string longName = NamingUtil.GetLongNameOverride(level, level.Name); IFCAnyHandleUtil.SetAttribute(buildingStorey, "Elevation", elevation); - SetSpatialStructureElement(buildingStorey, level, guid, ownerHistory, name, description, objectType, objectPlacement, null, longName, compositionType); + SetFacilityPart(buildingStorey, level, guid, ownerHistory, name, description, objectType, objectPlacement, null, longName, compositionType); return buildingStorey; } + /// + /// Creates a handle representing an IfcMarinePart and assigns it to the file. + /// + /// The file. + /// The GUID. + /// The owner history. + /// The name. + /// The description. + /// The object type. + /// The object placement. + /// The representation object. + /// The long name. + /// The composition type. + /// The handle. + public static IFCAnyHandle CreateMarinePart(ExporterIFC exporterIFC, Level level, IFCAnyHandle ownerHistory, string objectType, IFCAnyHandle objectPlacement, + IFCElementComposition compositionType) + { + IFCAnyHandle marinePart = CreateInstance(exporterIFC.GetFile(), IFCEntityType.IfcMarinePart, null); + string guid = GUIDUtil.GetLevelGUID(level); + string name = NamingUtil.GetNameOverride(marinePart, level, level.Name); + string description = NamingUtil.GetDescriptionOverride(level, null); + string longName = NamingUtil.GetLongNameOverride(level, level.Name); + + SetFacilityPart(marinePart, level, guid, ownerHistory, name, description, objectType, objectPlacement, null, longName, compositionType); + return marinePart; + } + + /// + /// Creates a handle representing an IfcRoadPart and assigns it to the file. + /// + /// The file. + /// The GUID. + /// The owner history. + /// The name. + /// The description. + /// The object type. + /// The object placement. + /// The representation object. + /// The long name. + /// The composition type. + /// The handle. + public static IFCAnyHandle CreateRoadPart(ExporterIFC exporterIFC, Level level, IFCAnyHandle ownerHistory, string objectType, IFCAnyHandle objectPlacement, + IFCElementComposition compositionType) + { + IFCAnyHandle roadPart = CreateInstance(exporterIFC.GetFile(), IFCEntityType.IfcRoadPart, null); + string guid = GUIDUtil.GetLevelGUID(level); + string name = NamingUtil.GetNameOverride(roadPart, level, level.Name); + string description = NamingUtil.GetDescriptionOverride(level, null); + string longName = NamingUtil.GetLongNameOverride(level, level.Name); + + SetFacilityPart(roadPart, level, guid, ownerHistory, name, description, objectType, objectPlacement, null, longName, compositionType); + return roadPart; + } + + /// + /// Creates a handle representing an IfcRailwayPart and assigns it to the file. + /// + /// The file. + /// The GUID. + /// The owner history. + /// The name. + /// The description. + /// The object type. + /// The object placement. + /// The representation object. + /// The long name. + /// The composition type. + /// The handle. + public static IFCAnyHandle CreateRailwayPart(ExporterIFC exporterIFC, Level level, IFCAnyHandle ownerHistory, string objectType, IFCAnyHandle objectPlacement, + IFCElementComposition compositionType) + { + IFCAnyHandle railwayPart = CreateInstance(exporterIFC.GetFile(), IFCEntityType.IfcRailwayPart, null); + string guid = GUIDUtil.GetLevelGUID(level); + string name = NamingUtil.GetNameOverride(railwayPart, level, level.Name); + string description = NamingUtil.GetDescriptionOverride(level, null); + string longName = NamingUtil.GetLongNameOverride(level, level.Name); + + SetFacilityPart(railwayPart, level, guid, ownerHistory, name, description, objectType, objectPlacement, null, longName, compositionType); + return railwayPart; + } + + /// + /// Creates a handle representing an IfcBridgePart and assigns it to the file. + /// + /// The file. + /// The GUID. + /// The owner history. + /// The name. + /// The description. + /// The object type. + /// The object placement. + /// The representation object. + /// The long name. + /// The composition type. + /// The predefined type of the bridge part. + /// The handle. + public static IFCAnyHandle CreateBridgePart(ExporterIFC exporterIFC, Level level, IFCAnyHandle ownerHistory, string objectType, IFCAnyHandle objectPlacement, + IFCElementComposition compositionType, string predefinedType) + { + IFCAnyHandle bridgePart = CreateInstance(exporterIFC.GetFile(), IFCEntityType.IfcBridgePart, null); + string guid = GUIDUtil.GetLevelGUID(level); + string name = NamingUtil.GetNameOverride(bridgePart, level, level.Name); + string description = NamingUtil.GetDescriptionOverride(level, null); + string longName = NamingUtil.GetLongNameOverride(level, level.Name); + + IFCAnyHandleUtil.SetAttribute(bridgePart, "PredefinedType", predefinedType); + SetFacilityPart(bridgePart, level, guid, ownerHistory, name, description, objectType, objectPlacement, null, longName, compositionType); + return bridgePart; + } + /// /// Creates a handle representing an IfcSpace and assigns it to the file. /// @@ -2768,6 +3026,16 @@ public static IFCAnyHandle CreateUnitAssignment(IFCFile file, HashSet + /// Validate that the parameters for IfcCircle are valid. + /// + /// The radius of the circle. + /// True if the circle is valid. + public static bool ValidateCircle(double radius) + { + return radius >= MathUtil.Eps(); + } + /// /// Creates a handle representing an IfcCircle and assigns it to the file. /// @@ -2777,7 +3045,7 @@ public static IFCAnyHandle CreateUnitAssignment(IFCFile file, HashSetThe handle. public static IFCAnyHandle CreateCircle(IFCFile file, IFCAnyHandle position, double radius) { - if (radius < MathUtil.Eps()) + if (!ValidateCircle(radius)) throw new ArgumentException("Radius is tiny, zero, or negative."); IFCAnyHandle circle = CreateInstance(file, IFCEntityType.IfcCircle, null); @@ -2786,6 +3054,17 @@ public static IFCAnyHandle CreateCircle(IFCFile file, IFCAnyHandle position, dou return circle; } + /// + /// Validate that the parameters for IfcEllipse are valid. + /// + /// The radius in the direction of X in the local coordinate system. + /// The radius in the direction of Y in the local coordinate system. + /// True if the ellipse is valid. + public static bool ValidateEllipse(double semiAxis1, double semiAxis2) + { + return semiAxis1 >= MathUtil.Eps() && semiAxis2 >= MathUtil.Eps(); + } + /// /// Creates a handle representing an IfcEllipse and assigns it to the file. /// @@ -2796,9 +3075,7 @@ public static IFCAnyHandle CreateCircle(IFCFile file, IFCAnyHandle position, dou /// The handle. public static IFCAnyHandle CreateEllipse(IFCFile file, IFCAnyHandle position, double semiAxis1, double semiAxis2) { - if (semiAxis1 < MathUtil.Eps()) - throw new ArgumentException("semiAxis1 is tiny, zero, or negative."); - if (semiAxis2 < MathUtil.Eps()) + if (!ValidateEllipse(semiAxis1, semiAxis2)) throw new ArgumentException("semiAxis2 is tiny, zero, or negative."); IFCAnyHandle ellipse = CreateInstance(file, IFCEntityType.IfcEllipse, null); @@ -3651,9 +3928,6 @@ public static void SetGenericTypeNonOptionalAttributes(IFCAnyHandle handleType, public static void SetPredefinedType(IFCAnyHandle genericIFCEntity, IFCExportInfoPair entityToCreate) { - if (string.IsNullOrEmpty(entityToCreate.ValidatedPredefinedType)) - return; - IFCVersion version = ExporterCacheManager.ExportOptionsCache.FileVersion; IFCEntityType entityType = entityToCreate.ExportInstance; @@ -3666,7 +3940,7 @@ public static void SetGenericTypeNonOptionalAttributes(IFCAnyHandle handleType, if (predefinedTypeAttributeName == null && !MissingAttributeCache.Find(version, entityType)) predefinedTypeAttributeName = "PredefinedType"; if (predefinedTypeAttributeName != null) - IFCAnyHandleUtil.SetAttribute(genericIFCEntity, predefinedTypeAttributeName, entityToCreate.ValidatedPredefinedType, true); + IFCAnyHandleUtil.SetAttribute(genericIFCEntity, predefinedTypeAttributeName, entityToCreate.GetPredefinedTypeOrDefault(), true); } catch { @@ -3755,15 +4029,12 @@ public static void SetGenericTypeNonOptionalAttributes(IFCAnyHandle handleType, IFCAnyHandle genericIFCType = CreateInstance(file, entityTypeToUse, elementType); SetElementType(genericIFCType, elementType, guid, propertySets, representationMaps); - if (!string.IsNullOrEmpty(typeEntityToCreate.ValidatedPredefinedType)) + // Earlier types in IFC2x_ may not have PredefinedType property. Ignore error + try { - // Earlier types in IFC2x_ may not have PredefinedType property. Ignore error - try - { - IFCAnyHandleUtil.SetAttribute(genericIFCType, "PredefinedType", typeEntityToCreate.ValidatedPredefinedType, true); - } - catch { } + IFCAnyHandleUtil.SetAttribute(genericIFCType, "PredefinedType", typeEntityToCreate.GetPredefinedTypeOrDefault(), true); } + catch { } SetGenericTypeNonOptionalAttributes(genericIFCType, typeEntityToCreate.ExportType); @@ -3834,8 +4105,6 @@ public static void SetGenericTypeNonOptionalAttributes(IFCAnyHandle handleType, if (!ExporterCacheManager.ExportOptionsCache.ExportAsOlderThanIFC4) { - if (string.IsNullOrEmpty(predefinedType)) - predefinedType = "NOTDEFINED"; predefinedType = IFCValidateEntry.GetValidIFCPredefinedTypeType(predefinedType, predefinedType, "IFCFurnitureType"); IFCAnyHandleUtil.SetAttribute(furnitureType, "PredefinedType", predefinedType, true); } @@ -3966,8 +4235,7 @@ public static void SetGenericTypeNonOptionalAttributes(IFCAnyHandle handleType, IFCAnyHandle buildingSystem = CreateInstance(file, IFCEntityType.IfcBuildingSystem, null); SetGroup(buildingSystem, guid, ownerHistory, name, description, objectType); - if (!string.IsNullOrEmpty(entityToCreate.ValidatedPredefinedType)) - IFCAnyHandleUtil.SetAttribute(buildingSystem, "PredefinedType", entityToCreate.ValidatedPredefinedType, true); + IFCAnyHandleUtil.SetAttribute(buildingSystem, "PredefinedType", entityToCreate.GetPredefinedTypeOrDefault(), true); if (!string.IsNullOrEmpty(longName)) IFCAnyHandleUtil.SetAttribute(buildingSystem, "LongName", longName, false); @@ -6641,7 +6909,10 @@ public static IFCAnyHandle CreateIndexedColourMap(IFCFile file, IFCAnyHandle map IFCAnyHandle indexedColourMap = CreateInstance(file, IFCEntityType.IfcIndexedColourMap, null); IFCAnyHandleUtil.SetAttribute(indexedColourMap, "MappedTo", mappedTo); if (opacity.HasValue) - IFCAnyHandleUtil.SetAttribute(indexedColourMap, "Opacity", opacity); + { + double inRangeOpacity = Math.Min(Math.Max(opacity.Value, 0.0), 1.0); + IFCAnyHandleUtil.SetAttribute(indexedColourMap, "Opacity", inRangeOpacity); + } IFCAnyHandleUtil.SetAttribute(indexedColourMap, "Colours", colours); IFCAnyHandleUtil.SetAttribute(indexedColourMap, "ColourIndex", colourIndex); return indexedColourMap; diff --git a/Source/Revit.IFC.Export/Toolkit/PlacementSetter.cs b/Source/Revit.IFC.Export/Toolkit/PlacementSetter.cs index 80029180..a3d954ef 100644 --- a/Source/Revit.IFC.Export/Toolkit/PlacementSetter.cs +++ b/Source/Revit.IFC.Export/Toolkit/PlacementSetter.cs @@ -339,7 +339,7 @@ public IFCLevelInfo GetOffsetLevelInfoAndHandle(double offset, double scale, Doc { // the cache contains levels from all the exported documents // if the export is performed for a linked document, filter the levels that are not from this document - if (ExporterCacheManager.ExportOptionsCache.ExportingSeparateLink()) + if (ExporterCacheManager.ExportOptionsCache.ExportLinkedFileAs != LinkedFileExportAs.DontExport) { Element levelElem = document.GetElement(levelInfoPair.Key); if (levelElem == null || !(levelElem is Level)) @@ -491,7 +491,7 @@ private void commonInit(ExporterIFC exporterIFC, Element elem, Transform familyT { // the cache contains levels from all the exported documents // if the export is performed for a linked document, filter the levels that are not from this document - if (ExporterCacheManager.ExportOptionsCache.ExportingSeparateLink()) + if (ExporterCacheManager.ExportOptionsCache.ExportLinkedFileAs != LinkedFileExportAs.DontExport) { Element levelElem = doc.GetElement(levelInfoPair.Key); if (levelElem == null || !(levelElem is Level)) @@ -528,7 +528,7 @@ private void commonInit(ExporterIFC exporterIFC, Element elem, Transform familyT { // the cache contains levels from all the exported documents // if the export is performed for a linked document, filter the levels that are not from this document - if (ExporterCacheManager.ExportOptionsCache.ExportingSeparateLink()) + if (ExporterCacheManager.ExportOptionsCache.ExportLinkedFileAs != LinkedFileExportAs.DontExport) { Element levelElem = doc.GetElement(levelInfoPair.Key); if (levelElem == null || !(levelElem is Level)) diff --git a/Source/Revit.IFC.Export/Utility/CategoryUtil.cs b/Source/Revit.IFC.Export/Utility/CategoryUtil.cs index d69e3108..ab4d9663 100644 --- a/Source/Revit.IFC.Export/Utility/CategoryUtil.cs +++ b/Source/Revit.IFC.Export/Utility/CategoryUtil.cs @@ -48,7 +48,7 @@ public static Category GetSafeCategory(Element element) if (element == null) return null; - // Special cases below - at the moment only one, for ModelCurves. + // Special cases below. if (element is ModelCurve) { CurveElement modelCurve = element as ModelCurve; @@ -56,6 +56,14 @@ public static Category GetSafeCategory(Element element) if (lineStyle != null) return lineStyle.GraphicsStyleCategory; } + else if (element is ModelText) + { + Parameter parameter = element.GetParameter(ParameterTypeId.ModelCategoryIdParam); + if (parameter != null && parameter.HasValue && parameter.StorageType == StorageType.ElementId) + { + return Category.GetCategory(element.Document, parameter.AsElementId()); + } + } return element.Category; } @@ -154,13 +162,6 @@ public static IFCAnyHandle CreateMaterialList(IFCFile file, IList return matList; } - public static IFCAnyHandle CreateMaterialLayerSetUsage(IFCFile file, IFCAnyHandle materialLayerSet, IFCLayerSetDirection direction, - IFCDirectionSense directionSense, double offset) - { - return IFCInstanceExporter.CreateMaterialLayerSetUsage(file, materialLayerSet, - direction, directionSense, offset); - } - public static IFCAnyHandle CreateMaterialProfileSetUsage(IFCFile file, IFCAnyHandle materialProfileSet, int? cardinalPoint) { @@ -257,11 +258,11 @@ private static bool CacheIsElementExternal(ElementId elementId, bool isExternal) /// All other elements are internal. /// /// The element. - /// True if the element is external, false otherwise. - public static bool IsElementExternal(Element element) + /// True if the element is external, false otherwise and null if will not export the value. + public static bool? IsElementExternal(Element element) { - if (element == null) - return false; + if (element == null || element is ElementType) + return null; // Look for a parameter "IsExternal", potentially localized. ElementId elementId = element.Id; @@ -289,10 +290,6 @@ public static bool IsElementExternal(Element element) elementType = element.Document.GetElement(element.GetTypeId()); } - else if (element is ElementType) - { - elementType = element; - } // Many element types have the FUNCTION_PARAM parameter. If this is set, use its value. int elementFunction; @@ -441,9 +438,8 @@ public static void CreateMaterialAssociation(ExporterIFC exporterIFC, IFCAnyHand // in IFC4 we will create IfcConstituentSet instead of MaterialList, create the associated IfcConstituent here from IfcMaterial foreach (ElementId materialId in alreadySeenIds) { - IFCAnyHandle constituentHnd = GetOrCreateMaterialConstituent(exporterIFC, materialId); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(constituentHnd)) - constituentSet.Add(constituentHnd); + constituentSet.AddIfNotNull(GetOrCreateMaterialConstituent(exporterIFC, + materialId)); } GetOrCreateMaterialConstituentSet(file, instanceHandle, constituentSet); @@ -566,9 +562,8 @@ public static void CreateMaterialAssociationWithShapeAspect(ExporterIFC exporter // in IFC4 we will create IfcConstituentSet instead of MaterialList, create the associated IfcConstituent here from IfcMaterial foreach (KeyValuePair> repItemInfoSet in repItemInfoGroup) { - IFCAnyHandle constituentHnd = GetOrCreateMaterialConstituent(exporterIFC, repItemInfoSet.Key); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(constituentHnd)) - constituentSet.Add(constituentHnd); + constituentSet.AddIfNotNull(GetOrCreateMaterialConstituent(exporterIFC, + repItemInfoSet.Key)); RepresentationUtil.CreateRepForShapeAspect(exporterIFC, element, prodRep, repType, repItemInfoSet.Key.ComponentCat, repItemInfoSet.Value); } @@ -759,10 +754,9 @@ public static IFCAnyHandle GetOrCreateMaterialHandle(ExporterIFC exporterIFC, El IFCAnyHandle styledRepItem = null; IFCAnyHandle matStyleHnd = GetOrCreateMaterialStyle(document, file, materialId); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(matStyleHnd) && !ExporterCacheManager.ExportOptionsCache.ExportAs4ReferenceView) + if (!ExporterCacheManager.ExportOptionsCache.ExportAs4ReferenceView && + styles.AddIfNotNull(matStyleHnd)) { - styles.Add(matStyleHnd); - bool supportCutStyles = !ExporterCacheManager.ExportOptionsCache.ExportAsCoordinationView2; if (fillPatternId != ElementId.InvalidElementId && supportCutStyles) { diff --git a/Source/Revit.IFC.Export/Utility/DoorWindowDelayedOpeningCreator.cs b/Source/Revit.IFC.Export/Utility/DoorWindowDelayedOpeningCreator.cs index da7d47dd..a46c5f45 100644 --- a/Source/Revit.IFC.Export/Utility/DoorWindowDelayedOpeningCreator.cs +++ b/Source/Revit.IFC.Export/Utility/DoorWindowDelayedOpeningCreator.cs @@ -17,16 +17,12 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // -using System; using System.Collections.Generic; -using System.Text; -using Autodesk.Revit; using Autodesk.Revit.DB; using Autodesk.Revit.DB.IFC; using Revit.IFC.Common.Enums; using Revit.IFC.Common.Utility; using Revit.IFC.Export.Toolkit; -using Revit.IFC.Export.Exporter; namespace Revit.IFC.Export.Utility { @@ -147,6 +143,49 @@ public void CopyGeometry(DoorWindowDelayedOpeningCreator otherCreator) HasValidGeometry = true; } + /// + /// Adds geometries from another creator. + /// + /// The other creator. + public void AddGeometry(DoorWindowDelayedOpeningCreator otherCreator) + { + // Nothing to add. + if (!otherCreator.HasValidGeometry) + { + return; + } + + bool copyExtrusionData = otherCreator.ExtrusionData != null; + if (ExtrusionData == null && copyExtrusionData) + { + ExtrusionData = otherCreator.ExtrusionData; + copyExtrusionData = false; + } + + bool copySolids = otherCreator.Solids != null; + if (Solids == null && copySolids) + { + Solids = otherCreator.Solids; + copySolids = false; + } + + if (copyExtrusionData) + { + foreach (IFCExtrusionData extrusionData in otherCreator.ExtrusionData) + { + ExtrusionData.Add(extrusionData); + } + } + + if (copySolids) + { + foreach (Solid solid in otherCreator.Solids) + { + Solids.Add(solid); + } + } + } + /// /// Excutes the creator. /// diff --git a/Source/Revit.IFC.Export/Utility/DoorWindowDelayedOpeningCreatorCache.cs b/Source/Revit.IFC.Export/Utility/DoorWindowDelayedOpeningCreatorCache.cs index 62a0b1dd..826d4fe3 100644 --- a/Source/Revit.IFC.Export/Utility/DoorWindowDelayedOpeningCreatorCache.cs +++ b/Source/Revit.IFC.Export/Utility/DoorWindowDelayedOpeningCreatorCache.cs @@ -42,7 +42,7 @@ public class DoorWindowDelayedOpeningCreatorCache /// Adds a new DoorWindowDelayedOpeningCreator. /// /// The creator. - public void Add(DoorWindowDelayedOpeningCreator creator) + public void Add(DoorWindowDelayedOpeningCreator creator, bool extend) { if (creator == null) return; @@ -62,17 +62,11 @@ public void Add(DoorWindowDelayedOpeningCreator creator) // from DoorWindowInfo has higher priority if (oldCreator.CreatedFromDoorWindowInfo) { - if (!oldCreator.HasValidGeometry && creator.HasValidGeometry) - { - oldCreator.CopyGeometry(creator); - } + CopyOrAddGeometry(oldCreator, creator, extend); } else if (creator.CreatedFromDoorWindowInfo) { - if (!creator.HasValidGeometry && oldCreator.HasValidGeometry) - { - creator.CopyGeometry(oldCreator); - } + CopyOrAddGeometry(creator, oldCreator, extend); existingOpenings[levelIdToUse] = creator; } } @@ -91,9 +85,23 @@ public void ExecuteCreators(ExporterIFC exporterIFC, Document doc) { foreach (DoorWindowDelayedOpeningCreator creator in creators.Values) { - creator.Execute(exporterIFC, doc); + //Geometry can become invalid when ExtrusionData or Solids are null or count is 0 + if (creator.HasValidGeometry) + creator.Execute(exporterIFC, doc); } } } + + private void CopyOrAddGeometry(DoorWindowDelayedOpeningCreator oldCreator, DoorWindowDelayedOpeningCreator newCreator, bool extend) + { + if (!oldCreator.HasValidGeometry && newCreator.HasValidGeometry) + { + oldCreator.CopyGeometry(newCreator); + } + else if (oldCreator.HasValidGeometry && newCreator.HasValidGeometry && extend) + { + oldCreator.AddGeometry(newCreator); + } + } } } \ No newline at end of file diff --git a/Source/Revit.IFC.Export/Utility/DoorWindowInfo.cs b/Source/Revit.IFC.Export/Utility/DoorWindowInfo.cs index 82f89ff7..732fffb6 100644 --- a/Source/Revit.IFC.Export/Utility/DoorWindowInfo.cs +++ b/Source/Revit.IFC.Export/Utility/DoorWindowInfo.cs @@ -431,19 +431,13 @@ private void Initialize(bool isDoor, bool isWindow, FamilyInstance famInst, Host ExportingDoor = isDoor; if (isDoor) { - if (exportType.HasUndefinedPredefinedType()) - PreDefinedType = "DOOR"; - else - PreDefinedType = exportType.ValidatedPredefinedType; + PreDefinedType = exportType.GetPredefinedTypeOrDefault("DOOR"); } ExportingWindow = isWindow; if (isWindow) { - if (exportType.HasUndefinedPredefinedType()) - PreDefinedType = "WINDOW"; - else - PreDefinedType = exportType.ValidatedPredefinedType; + PreDefinedType = exportType.GetPredefinedTypeOrDefault("WINDOW"); } FlippedSymbol = false; diff --git a/Source/Revit.IFC.Export/Utility/ElementFilteringUtil.cs b/Source/Revit.IFC.Export/Utility/ElementFilteringUtil.cs index 6857f014..26c0c9f1 100644 --- a/Source/Revit.IFC.Export/Utility/ElementFilteringUtil.cs +++ b/Source/Revit.IFC.Export/Utility/ElementFilteringUtil.cs @@ -146,54 +146,86 @@ private static ElementFilter GetDesignOptionFilter() } /// - /// Checks if element in certain category should be exported. + /// Checks if an element with a given ExportIFCCategoryInfo should be exported. /// - /// The ExporterIFC object. - /// The element. + /// The exporting mapping information for a category. + /// The optional element. /// True if IfcOpeningElement is allowed to be exported. /// True if the element should be exported, false otherwise. - private static bool ShouldCategoryBeExported(ExporterIFC exporterIFC, Element element, bool allowSeparateOpeningExport) + public static bool ShouldExportMappingInfo(ExportIFCCategoryInfo info, Element element, bool allowSeparateOpeningExport) { - IFCExportInfoPair exportType = new IFCExportInfoPair(); - ElementId categoryId; - string ifcClassName = ExporterUtil.GetIFCClassNameFromExportTable(exporterIFC, element, out categoryId); - if (string.IsNullOrEmpty(ifcClassName)) + string entityName = info?.IFCEntityName; + bool? exportFlag = info?.IFCExportFlag; + + // If the element is null, we won't do this check. If the entityName is empty, it is + // likely a sub-category that should defer to its parent to make a final decision. + if (info == null && element != null) { - // Special case: these elements aren't contained in the default export layers mapping table. - // This allows these elements to be exported by default. if (element is AreaScheme || element is Group) - ifcClassName = "IfcGroup"; + entityName = "IfcGroup"; else if (element is ElectricalSystem) - ifcClassName = "IfcSystem"; - else if (element is Grid) - ifcClassName = "IfcGrid"; // In the German template somehow the Grid does not show up in the mapping table + entityName = "IfcSystem"; else return false; - } - bool foundName = string.Compare(ifcClassName, "Default", true) != 0; - if (foundName) - exportType = GetExportTypeFromClassName(ifcClassName); - if (!foundName) - return true; + exportFlag = true; + } - if (exportType.ExportInstance == IFCEntityType.UnKnown) + if (!(exportFlag ?? false)) return false; - // We don't export openings directly, only via the element they are opening, unless flag is set. - if (exportType.ExportInstance == IFCEntityType.IfcOpeningElement && !allowSeparateOpeningExport) + if (!allowSeparateOpeningExport && (string.Compare(entityName, "IfcOpeningElement", true) == 0)) return false; - // Check whether the intended Entity type is inside the export exclusion set - IFCEntityType elementClassTypeEnum; - if (Enum.TryParse(exportType.ExportInstance.ToString(), out elementClassTypeEnum) - || Enum.TryParse(exportType.ExportType.ToString(), out elementClassTypeEnum)) - if (ExporterCacheManager.ExportOptionsCache.IsElementInExcludeList(elementClassTypeEnum)) + if (ExporterCacheManager.ExportOptionsCache.HasExcludeList()) + { + if (ExporterCacheManager.ExportOptionsCache.IsEntityInExcludeList(entityName)) + return false; + + IFCExportInfoPair pair = new IFCExportInfoPair(); + pair.SetByTypeName(entityName); + if (ExporterCacheManager.ExportOptionsCache.IsEntityInExcludeList(pair.ExportType.ToString())) return false; + } + return true; } + /// + /// Checks if element in certain category should be exported. + /// + /// True if IfcOpeningElement is allowed to be exported. + /// True if the element should be exported, false otherwise. + public static bool ShouldCategoryBeExported(Category category, bool allowSeparateOpeningExport) + { + ElementId categoryId = category?.Id ?? ElementId.InvalidElementId; + if (ExporterUtil.GetCategoryInfoById(categoryId, null, out ExportIFCCategoryInfo info)) + return ShouldExportMappingInfo(info, null, allowSeparateOpeningExport); + + ElementId parentCategoryId = category?.Parent?.Id ?? ElementId.InvalidElementId; + if (parentCategoryId != ElementId.InvalidElementId) + { + if (ExporterUtil.GetCategoryInfoById(parentCategoryId, null, out info)) + return ShouldExportMappingInfo(info, null, allowSeparateOpeningExport); + } + + // the category is not in the mapping template + return IsCategoryVisible(category, ExporterCacheManager.ExportOptionsCache.FilterViewForExport); + } + + /// + /// Checks if element in certain category should be exported. + /// + /// The element. + /// True if IfcOpeningElement is allowed to be exported. + /// True if the element should be exported, false otherwise. + private static bool ShouldCategoryBeExported(Element element, bool allowSeparateOpeningExport) + { + ExportIFCCategoryInfo info = ExporterUtil.GetIFCCategoryExportMappingInfo(element); + return ShouldExportMappingInfo(info, element, allowSeparateOpeningExport); + } + /// /// Checks if an element should be exported based on parameter settings. /// @@ -227,13 +259,12 @@ private static bool ShouldCategoryBeExported(ExporterIFC exporterIFC, Element el /// /// Checks if element should be exported using a variety of different checks. /// - /// The ExporterIFC object. /// The element. /// True if IfcOpeningElement is allowed to be exported. /// True if the element should be exported, false otherwise. /// There are some inefficiencies here, as we call GetExportInfoFromParameters /// in other contexts. We should attempt to get the value only once. - public static bool ShouldElementBeExported(ExporterIFC exporterIFC, Element element, bool allowSeparateOpeningExport) + public static bool ShouldElementBeExported(Element element, bool allowSeparateOpeningExport) { // Allow the ExporterStateManager to say that an element should be exported regardless of settings. if (ExporterStateManager.CanExportElementOverride) @@ -249,7 +280,7 @@ public static bool ShouldElementBeExported(ExporterIFC exporterIFC, Element elem // Check to see if the category should be exported if parameters aren't set. // Note that in previous versions, the category override the parameter settings. This is // no longer true. - if (!ShouldCategoryBeExported(exporterIFC, element, allowSeparateOpeningExport)) + if (!ShouldCategoryBeExported(element, allowSeparateOpeningExport)) return false; // Check whether the intended Entity type is inside the export exclusion set @@ -260,13 +291,12 @@ public static bool ShouldElementBeExported(ExporterIFC exporterIFC, Element elem /// /// Determines if the selected element meets extra criteria for export. /// - /// The exporter class. /// The current element to export. /// True if IfcOpeningElement is allowed to be exported. /// True if the element should be exported, false otherwise. - public static bool CanExportElement(ExporterIFC exporterIFC, Autodesk.Revit.DB.Element element, bool allowSeparateOpeningExport) + public static bool CanExportElement(Element element, bool allowSeparateOpeningExport) { - if (!ElementFilteringUtil.ShouldElementBeExported(exporterIFC, element, allowSeparateOpeningExport)) + if (!ShouldElementBeExported(element, allowSeparateOpeningExport)) return false; // if we allow exporting parts as independent building elements, then prevent also exporting the host elements containing the parts. @@ -277,56 +307,14 @@ public static bool CanExportElement(ExporterIFC exporterIFC, Autodesk.Revit.DB.E return true; } - /// - /// Checks if name is equal to base or its type name. - /// - /// The object type name. - /// The IFC base name. - /// True if equal, false otherwise. - private static bool IsEqualToTypeName(String name, String baseName) - { - if (string.Compare(name, baseName, true) == 0) - return true; - - string typeName = IfcSchemaEntityTree.GetTypeNameFromInstanceName(baseName); - return (string.Compare(name, typeName, true) == 0); - } - - /// - /// Compares two strings, ignoring spaces, punctuation and case. - /// - /// The string to compare. - /// String to compare to, all caps, no punctuation or cases. - /// - private static bool CompareAlphaOnly(String name, String baseNameAllCapsNoSpaces) - { - if (string.IsNullOrEmpty(name)) - return string.IsNullOrEmpty(baseNameAllCapsNoSpaces); - string nameToUpper = name.ToUpper(); - int loc = 0; - int maxLen = baseNameAllCapsNoSpaces.Length; - foreach (char c in nameToUpper) - { - if (c >= 'A' && c <= 'Z') - { - if (baseNameAllCapsNoSpaces[loc] != c) - return false; - loc++; - if (loc == maxLen) - return true; - } - } - return false; - } - - static IDictionary PreIFC4Remap = new Dictionary() + static readonly Dictionary PreIFC4Remap = new Dictionary() { { "IFCAUDIOVISUALAPPLIANCE", IFCEntityType.IfcElectricApplianceType }, { "IFCBURNER", IFCEntityType.IfcGasTerminalType }, { "IFCELECTRICDISTRIBUTIONBOARD", IFCEntityType.IfcElectricDistributionPoint } }; - static IDictionary IFC4Remap = new Dictionary() + static readonly Dictionary IFC4Remap = new Dictionary() { { "IFCGASTERMINAL", IFCEntityType.IfcBurnerType }, { "IFCELECTRICDISTRIBUTIONPOINT", IFCEntityType.IfcElectricDistributionBoardType }, @@ -338,7 +326,7 @@ private static bool CompareAlphaOnly(String name, String baseNameAllCapsNoSpaces /// /// The IFC class name. /// The export type. - public static IFCExportInfoPair GetExportTypeFromClassName(String originalIFCClassName) + public static IFCExportInfoPair GetExportTypeFromClassName(string originalIFCClassName) { IFCExportInfoPair exportInfoPair = new IFCExportInfoPair(); @@ -347,60 +335,51 @@ public static IFCExportInfoPair GetExportTypeFromClassName(String originalIFCCla { // Here we try to catch any possible types that are missing above by checking both the class name or the type name // Unless there is any special treatment needed most of the above check can be done here - string clName = cleanIFCClassName.Substring(cleanIFCClassName.Length - 4, 4).Equals("Type", StringComparison.CurrentCultureIgnoreCase) ? - cleanIFCClassName.Substring(0, cleanIFCClassName.Length - 4) : - cleanIFCClassName; - + string clName = cleanIFCClassName.EndsWith("TYPE") ? + cleanIFCClassName.Substring(0, cleanIFCClassName.Length - 4) : cleanIFCClassName; + // Deal with small number of IFC2x3/IFC4 types that have changed in a hardwired way. if (ExporterCacheManager.ExportOptionsCache.ExportAsOlderThanIFC4) { if (PreIFC4Remap.TryGetValue(clName, out IFCEntityType ifcEntityType)) - exportInfoPair.SetValueWithPair(ifcEntityType); + exportInfoPair.SetByType(ifcEntityType); else - exportInfoPair.SetValueWithPair(clName); + exportInfoPair.SetByTypeName(clName); } else { if (IFC4Remap.TryGetValue(clName, out IFCEntityType ifcEntityType)) - exportInfoPair.SetValueWithPair(ifcEntityType); + exportInfoPair.SetByType(ifcEntityType); else - exportInfoPair.SetValueWithPair(clName); + exportInfoPair.SetByTypeName(clName); } if (exportInfoPair.ExportInstance == IFCEntityType.UnKnown) - exportInfoPair.SetValueWithPair(IFCEntityType.IfcBuildingElementProxy); + exportInfoPair.SetByType(IFCEntityType.IfcBuildingElementProxy); } - exportInfoPair.ValidatedPredefinedType = IFCValidateEntry.GetValidIFCPredefinedType("NOTDEFINED", exportInfoPair.ExportType.ToString()); + exportInfoPair.PredefinedType = null; return exportInfoPair; } - static IDictionary s_CategoryToExportType = null; - - static void InitializeCategoryToExportType() - { - if (s_CategoryToExportType != null) - return; - - s_CategoryToExportType = new Dictionary() { - { new ElementId(BuiltInCategory.OST_Cornices), new IFCExportInfoPair(IFCEntityType.IfcBeam, "NOTDEFINED") }, - { new ElementId(BuiltInCategory.OST_Ceilings), new IFCExportInfoPair(IFCEntityType.IfcCovering, "NOTDEFINED") }, - { new ElementId(BuiltInCategory.OST_CurtainWallPanels), new IFCExportInfoPair(IFCEntityType.IfcPlate, "CURTAIN_PANEL") }, - { new ElementId(BuiltInCategory.OST_Furniture), new IFCExportInfoPair(IFCEntityType.IfcFurniture, "NOTDEFINED") }, - { new ElementId(BuiltInCategory.OST_Floors), new IFCExportInfoPair(IFCEntityType.IfcSlab, "FLOOR") }, - { new ElementId(BuiltInCategory.OST_IOSModelGroups), new IFCExportInfoPair(IFCEntityType.IfcGroup, "NOTDEFINED") }, - { new ElementId(BuiltInCategory.OST_Mass), new IFCExportInfoPair(IFCEntityType.IfcBuildingElementProxy, "NOTDEFINED") }, - { new ElementId(BuiltInCategory.OST_CurtainWallMullions), new IFCExportInfoPair(IFCEntityType.IfcMember, "MULLION") }, - { new ElementId(BuiltInCategory.OST_Railings), new IFCExportInfoPair(IFCEntityType.IfcRailing, "NOTDEFINED") }, - { new ElementId(BuiltInCategory.OST_Ramps), new IFCExportInfoPair(IFCEntityType.IfcRamp, "NOTDEFINED") }, - { new ElementId(BuiltInCategory.OST_Roofs), new IFCExportInfoPair(IFCEntityType.IfcRoof, "NOTDEFINED") }, - { new ElementId(BuiltInCategory.OST_Site), new IFCExportInfoPair(IFCEntityType.IfcSite, "NOTDEFINED") }, - { new ElementId(BuiltInCategory.OST_Stairs), new IFCExportInfoPair(IFCEntityType.IfcStair, "NOTDEFINED") }, - { new ElementId(BuiltInCategory.OST_Walls), new IFCExportInfoPair(IFCEntityType.IfcWall, "NOTDEFINED") }, - { new ElementId(BuiltInCategory.OST_Windows), new IFCExportInfoPair(IFCEntityType.IfcWindow, "NOTDEFINED") } - }; - } + static readonly Dictionary CategoryToExportType = new Dictionary() { + { BuiltInCategory.OST_Cornices, (IFCEntityType.IfcBeam, "NOTDEFINED") }, + { BuiltInCategory.OST_Ceilings, (IFCEntityType.IfcCovering, "NOTDEFINED") }, + { BuiltInCategory.OST_CurtainWallPanels, (IFCEntityType.IfcPlate, "CURTAIN_PANEL") }, + { BuiltInCategory.OST_Furniture, (IFCEntityType.IfcFurniture, "NOTDEFINED") }, + { BuiltInCategory.OST_Floors, (IFCEntityType.IfcSlab, "FLOOR") }, + { BuiltInCategory.OST_IOSModelGroups, (IFCEntityType.IfcGroup, "NOTDEFINED") }, + { BuiltInCategory.OST_Mass, (IFCEntityType.IfcBuildingElementProxy, "NOTDEFINED") }, + { BuiltInCategory.OST_CurtainWallMullions, (IFCEntityType.IfcMember, "MULLION") }, + { BuiltInCategory.OST_Railings, (IFCEntityType.IfcRailing, "NOTDEFINED") }, + { BuiltInCategory.OST_Ramps, (IFCEntityType.IfcRamp, "NOTDEFINED") }, + { BuiltInCategory.OST_Roofs, (IFCEntityType.IfcRoof, "NOTDEFINED") }, + { BuiltInCategory.OST_Site, (IFCEntityType.IfcSite, "NOTDEFINED") }, + { BuiltInCategory.OST_Stairs, (IFCEntityType.IfcStair, "NOTDEFINED") }, + { BuiltInCategory.OST_Walls, (IFCEntityType.IfcWall, "NOTDEFINED") }, + { BuiltInCategory.OST_Windows, (IFCEntityType.IfcWindow, "NOTDEFINED") } + }; /// /// Gets export type from category id. @@ -409,10 +388,10 @@ static void InitializeCategoryToExportType() /// The export type. public static IFCExportInfoPair GetExportTypeFromCategoryId(ElementId categoryId) { - InitializeCategoryToExportType(); - IFCExportInfoPair exportInfoPair; - if (s_CategoryToExportType.TryGetValue(categoryId, out exportInfoPair)) - return exportInfoPair; + (IFCEntityType, string) exportInfoPair; + BuiltInCategory builtInCategory = (BuiltInCategory)categoryId.Value; + if (CategoryToExportType.TryGetValue(builtInCategory, out exportInfoPair)) + return new IFCExportInfoPair(exportInfoPair.Item1, exportInfoPair.Item2); return new IFCExportInfoPair(); } @@ -425,11 +404,13 @@ private static ElementFilter GetViewSpecificTypesFilter(ExporterIFC exporter) { ElementFilter ownerViewFilter = GetOwnerViewFilter(exporter); - List viewSpecificTypes = new List(); - viewSpecificTypes.Add(typeof(TextNote)); - viewSpecificTypes.Add(typeof(FilledRegion)); - ElementMulticlassFilter classFilter = new ElementMulticlassFilter(viewSpecificTypes); + List viewSpecificTypes = new List() + { + typeof(TextNote), + typeof(FilledRegion) + }; + ElementMulticlassFilter classFilter = new ElementMulticlassFilter(viewSpecificTypes); LogicalAndFilter viewSpecificTypesFilter = new LogicalAndFilter(ownerViewFilter, classFilter); return viewSpecificTypesFilter; @@ -467,89 +448,102 @@ private static ElementFilter GetClassFilter(bool forSpatialElements) } else { - List excludedTypes = new List(); - - // FamilyInstances are handled in separate filter. - excludedTypes.Add(typeof(FamilyInstance)); - - // Spatial element are exported in a separate pass. - excludedTypes.Add(typeof(SpatialElement)); - - // AreaScheme elements are exported as groups after all Areas have been exported. - excludedTypes.Add(typeof(AreaScheme)); - // FabricArea elements are exported as groups after all FabricSheets have been exported. - excludedTypes.Add(typeof(FabricArea)); - - if (!ExporterCacheManager.ExportOptionsCache.ExportAnnotations) - excludedTypes.Add(typeof(CurveElement)); - - excludedTypes.Add(typeof(ElementType)); - - excludedTypes.Add(typeof(BaseArray)); - - excludedTypes.Add(typeof(FillPatternElement)); - excludedTypes.Add(typeof(LinePatternElement)); - excludedTypes.Add(typeof(Material)); - excludedTypes.Add(typeof(GraphicsStyle)); - excludedTypes.Add(typeof(Family)); - excludedTypes.Add(typeof(SketchPlane)); - excludedTypes.Add(typeof(View)); - excludedTypes.Add(typeof(Autodesk.Revit.DB.Structure.LoadBase)); - - // curtain wall sub-types we are ignoring. - excludedTypes.Add(typeof(CurtainGridLine)); - // excludedTypes.Add(typeof(Mullion)); - - // this will be gotten from the element(s) it cuts. - excludedTypes.Add(typeof(Opening)); - - // 2D types we are ignoring - excludedTypes.Add(typeof(SketchBase)); - excludedTypes.Add(typeof(FaceSplitter)); - - // 2D types covered by the element owner view filter - excludedTypes.Add(typeof(TextNote)); - excludedTypes.Add(typeof(FilledRegion)); - - // exclude levels that are covered in BeginExport - excludedTypes.Add(typeof(Level)); - - // exclude analytical models - excludedTypes.Add(typeof(Autodesk.Revit.DB.Structure.AnalyticalElement)); - ElementFilter excludedClassFilter = new ElementMulticlassFilter(excludedTypes, true); - - List excludedCategories = new List(); - - // Native Revit types without match in API - excludedCategories.Add(BuiltInCategory.OST_ConduitCenterLine); - excludedCategories.Add(BuiltInCategory.OST_ConduitFittingCenterLine); - excludedCategories.Add(BuiltInCategory.OST_DecalElement); - //excludedCategories.Add(BuiltInCategory.OST_Parts); - //excludedCategories.Add(BuiltInCategory.OST_RvtLinks); - excludedCategories.Add(BuiltInCategory.OST_DuctCurvesCenterLine); - excludedCategories.Add(BuiltInCategory.OST_DuctFittingCenterLine); - excludedCategories.Add(BuiltInCategory.OST_FlexDuctCurvesCenterLine); - excludedCategories.Add(BuiltInCategory.OST_FlexPipeCurvesCenterLine); - excludedCategories.Add(BuiltInCategory.OST_IOS_GeoLocations); - excludedCategories.Add(BuiltInCategory.OST_PipeCurvesCenterLine); - excludedCategories.Add(BuiltInCategory.OST_PipeFittingCenterLine); - excludedCategories.Add(BuiltInCategory.OST_Property); - excludedCategories.Add(BuiltInCategory.OST_SiteProperty); - excludedCategories.Add(BuiltInCategory.OST_SitePropertyLineSegment); - excludedCategories.Add(BuiltInCategory.OST_TopographyContours); - excludedCategories.Add(BuiltInCategory.OST_Viewports); - excludedCategories.Add(BuiltInCategory.OST_Views); - - // Exclude elements with no category. - excludedCategories.Add(BuiltInCategory.INVALID); - - ElementMulticategoryFilter excludedCategoryFilter = new ElementMulticategoryFilter(excludedCategories, true); - - LogicalAndFilter exclusionFilter = new LogicalAndFilter(excludedClassFilter, excludedCategoryFilter); - - ElementOwnerViewFilter ownerViewFilter = new ElementOwnerViewFilter(ElementId.InvalidElementId); - - LogicalAndFilter returnedFilter = new LogicalAndFilter(exclusionFilter, ownerViewFilter); + List excludedTypes = new List + { + typeof(AnalyticalElement), + + // AreaScheme elements are exported as groups after all Areas have been + // exported. + typeof(AreaScheme), + + // curtain wall sub-types we are ignoring. + typeof(CurtainGridLine), + // typeof(Mullion), + + typeof(ElevationMarker), + + // FabricArea elements are exported as groups after all FabricSheets have + // been exported. + typeof(FabricArea), + + // exclude levels that are covered in BeginExport + typeof(Level), + + // this will be gotten from the element(s) it cuts. + typeof(Opening), + + // Spatial element are exported in a separate pass. + typeof(SpatialElement), + + // 2D types we are ignoring + typeof(FaceSplitter), + typeof(Revision), + typeof(SketchBase), + + // 2D types covered by the element owner view filter + typeof(FilledRegion), + typeof(TextNote), + + typeof(BaseArray), + typeof(ColorFillScheme), + typeof(ElementType), + typeof(GraphicsStyle), + typeof(Family), + typeof(FamilyInstance), + typeof(FillPatternElement), + typeof(InternalOrigin), + typeof(LinePatternElement), + typeof(LoadBase), + typeof(LoadCase), + typeof(Material), + typeof(Phase), + typeof(SketchPlane), + typeof(SunAndShadowSettings), + typeof(View) + }; + + ElementFilter excludedClassFilter = new ElementMulticlassFilter(excludedTypes, + true); + + List excludedCategories = new List() + { + // Native Revit types without match in API + BuiltInCategory.OST_ConduitCenterLine, + BuiltInCategory.OST_ConduitFittingCenterLine, + BuiltInCategory.OST_DecalElement, + //BuiltInCategory.OST_Parts, + //BuiltInCategory.OST_RvtLinks, + BuiltInCategory.OST_DuctCurvesCenterLine, + BuiltInCategory.OST_DuctFittingCenterLine, + BuiltInCategory.OST_FlexDuctCurvesCenterLine, + BuiltInCategory.OST_FlexPipeCurvesCenterLine, + BuiltInCategory.OST_HVAC_Load_Schedules, + BuiltInCategory.OST_IOS_GeoLocations, + BuiltInCategory.OST_IOSSketchGrid, + BuiltInCategory.OST_PipeCurvesCenterLine, + BuiltInCategory.OST_PipeFittingCenterLine, + BuiltInCategory.OST_Property, + BuiltInCategory.OST_SitePropertyLineSegment, + BuiltInCategory.OST_TopographyContours, + BuiltInCategory.OST_Viewers, + BuiltInCategory.OST_Viewports, + BuiltInCategory.OST_Views, + + // Exclude elements with no category. + BuiltInCategory.INVALID + }; + + ElementMulticategoryFilter excludedCategoryFilter = + new ElementMulticategoryFilter(excludedCategories, true); + + LogicalAndFilter exclusionFilter = new LogicalAndFilter(excludedClassFilter, + excludedCategoryFilter); + + ElementOwnerViewFilter ownerViewFilter = + new ElementOwnerViewFilter(ElementId.InvalidElementId); + + LogicalAndFilter returnedFilter = new LogicalAndFilter(exclusionFilter, + ownerViewFilter); return returnedFilter; } @@ -658,7 +652,7 @@ public static bool IsElementVisible(Element element) if (hidden) return false; - Category category = element.Category; + Category category = CategoryUtil.GetSafeCategory(element); hidden = !IsCategoryVisible(category, filterView); if (hidden) return false; diff --git a/Source/Revit.IFC.Export/Utility/ElementTypeToHandleCache.cs b/Source/Revit.IFC.Export/Utility/ElementTypeToHandleCache.cs index f442c459..053ad503 100644 --- a/Source/Revit.IFC.Export/Utility/ElementTypeToHandleCache.cs +++ b/Source/Revit.IFC.Export/Utility/ElementTypeToHandleCache.cs @@ -1,7 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; using Autodesk.Revit.DB; using Autodesk.Revit.DB.IFC; using Revit.IFC.Common.Enums; @@ -14,7 +12,7 @@ namespace Revit.IFC.Export.Utility /// public sealed class ElementTypeKey : Tuple { - public ElementTypeKey(ElementType elementType, IFCEntityType entType, string preDefinedType) : base(elementType, entType, preDefinedType) { } + public ElementTypeKey(ElementType elementType, IFCExportInfoPair exportAs) : base(elementType, exportAs.ExportType, exportAs.GetPredefinedTypeOrDefault()) { } } /// @@ -89,7 +87,7 @@ public class ElementTypeToHandleCache public IFCAnyHandle Find(ElementType elementType, IFCExportInfoPair exportType) { IFCAnyHandle handle; - var key = new ElementTypeKey(elementType, exportType.ExportType, exportType.ValidatedPredefinedType); + var key = new ElementTypeKey(elementType, exportType); if (m_ElementTypeToHandleDictionary.TryGetValue(key, out handle)) { return handle; @@ -161,7 +159,7 @@ public void RemoveInvalidHandles(ISet keys) /// public void Register(ElementType elementType, IFCExportInfoPair exportType, IFCAnyHandle handle) { - var key = new ElementTypeKey(elementType, exportType.ExportType, exportType.ValidatedPredefinedType); + var key = new ElementTypeKey(elementType, exportType); if (m_ElementTypeToHandleDictionary.ContainsKey(key) || exportType.ExportType == IFCEntityType.UnKnown) return; diff --git a/Source/Revit.IFC.Export/Utility/ExportOptionsCache.cs b/Source/Revit.IFC.Export/Utility/ExportOptionsCache.cs index e2c790ae..7f1e68ff 100644 --- a/Source/Revit.IFC.Export/Utility/ExportOptionsCache.cs +++ b/Source/Revit.IFC.Export/Utility/ExportOptionsCache.cs @@ -73,8 +73,22 @@ public IFCFileHeaderItem FileHeaderItem } } + /// + /// Always export floors and roofs as a single entity unless exporting parts. + /// + public bool ExportHostAsSingleEntity { get; private set; } = false; + + /// + /// If set, set the IfcOwnerHistory LastModified attribute to be the Author in Project Information. + /// + public bool OwnerHistoryLastModified { get; private set; } = false; + public KnownERNames ExchangeRequirement { get; set; } = KnownERNames.NotDefined; + public KnownFacilityTypes FacilityType { get; set; } = KnownFacilityTypes.Building; + + public string FacilityPredefinedType { get; set; } = null; + public string GeoRefCRSName { get; private set; } public string GeoRefCRSDesc { get; private set; } @@ -250,7 +264,8 @@ public static ExportOptionsCache Create(ExporterIFC exporterIFC, View filterView ExportOptionsCache cache = new ExportOptionsCache(); cache.FileVersion = exporterIFC.FileVersion; - cache.FileName = exporterIFC.FileName; + cache.FullFileName = exporterIFC.FileName; + cache.FileNameOnly = Path.GetFileName(cache.FullFileName); cache.ExportBaseQuantities = exporterIFC.ExportBaseQuantities; cache.WallAndColumnSplitting = exporterIFC.WallAndColumnSplitting; cache.SpaceBoundaryLevel = exporterIFC.SpaceBoundaryLevel; @@ -298,6 +313,21 @@ public static ExportOptionsCache Create(ExporterIFC exporterIFC, View filterView bool? exportAdvancedSweptSolids = OptionsUtil.GetNamedBooleanOption(options, "ExportAdvancedSweptSolids"); cache.ExportAdvancedSweptSolids = (exportAdvancedSweptSolids.HasValue) ? exportAdvancedSweptSolids.Value : false; + string exchangeRequirementString = OptionsUtil.GetNamedStringOption(options, "ExchangeRequirement"); + if (Enum.TryParse(exchangeRequirementString, out KnownERNames exchangeRequirment)) + { + cache.ExchangeRequirement = exchangeRequirment; + } + + string facilityTypeString = OptionsUtil.GetNamedStringOption(options, "FacilityType"); + if (Enum.TryParse(facilityTypeString, out KnownFacilityTypes facilityType) && + facilityType != KnownFacilityTypes.NotDefined) + { + cache.FacilityType = facilityType; + } + + cache.FacilityPredefinedType = OptionsUtil.GetNamedStringOption(options, "FacilityPredefinedType"); + // Set GUIDOptions here. { // This option should be rarely used, and is only for consistency with old files. As such, it is set by environment variable only. @@ -329,6 +359,12 @@ public static ExportOptionsCache Create(ExporterIFC exporterIFC, View filterView (useOnlyTypeNameForIfcType != null) && useOnlyTypeNameForIfcType.GetValueOrDefault(); } + bool? exportHostAsSingleEntity = OptionsUtil.GetNamedBooleanOption(options, "ExportHostAsSingleEntity"); + cache.ExportHostAsSingleEntity = exportHostAsSingleEntity.GetValueOrDefault(false); + + bool? ownerHistoryLastModified = OptionsUtil.GetNamedBooleanOption(options, "OwnerHistoryLastModified"); + cache.OwnerHistoryLastModified = ownerHistoryLastModified.GetValueOrDefault(false); + // "SingleElement" export option - useful for debugging - only one input element will be processed for export if (options.TryGetValue("SingleElement", out string singleElementValue)) { @@ -408,7 +444,9 @@ public static ExportOptionsCache Create(ExporterIFC exporterIFC, View filterView if (options.TryGetValue("ActivePhaseId", out activePhaseElementValue)) cache.ActivePhaseId = ParseElementId(activePhaseElementValue); - if ((cache.ActivePhaseId == ElementId.InvalidElementId) && (cache.FilterViewForExport != null)) + // If we have a filter view, the phase to be exported is only the phase of the + // view. So we ignore any phase sent. + if (cache.FilterViewForExport != null) { Parameter currPhase = cache.FilterViewForExport.get_Parameter(BuiltInParameter.VIEW_PHASE); if (currPhase != null) @@ -422,6 +460,8 @@ public static ExportOptionsCache Create(ExporterIFC exporterIFC, View filterView cache.SelectedParametermappingTableName = OptionsUtil.GetNamedStringOption(options, "ExportUserDefinedParameterMappingFileName"); + cache.CategoryMappingTableName = OptionsUtil.GetNamedStringOption(options, "CategoryMapping"); + // This is for the option to export links as part of a federated export. string federatedInfoString = OptionsUtil.GetNamedStringOption(options, "FederatedLinkInfo"); cache.FederatedLinkInfo = ParseFederatedLinkInfo(federatedInfoString); @@ -563,13 +603,13 @@ private static void ParseFileType(IDictionary options, ExportOpt throw new Exception("Option 'FileType' did not match an existing IFCFileFormat value"); } } - else if (!string.IsNullOrEmpty(cache.FileName)) + else if (!string.IsNullOrEmpty(cache.FileNameOnly)) { - if (cache.FileName.EndsWith(".ifcXML")) //localization? + if (cache.FileNameOnly.EndsWith(".ifcXML")) //localization? { cache.IFCFileFormat = IFCFileFormat.IfcXML; } - else if (cache.FileName.EndsWith(".ifcZIP")) + else if (cache.FileNameOnly.EndsWith(".ifcZIP")) { cache.IFCFileFormat = IFCFileFormat.IfcZIP; } @@ -597,13 +637,14 @@ public PropertySetOptions PropertySetOptions public IFCVersion FileVersion { get; set; } /// - /// The file name. + /// The full file name, including path. /// - public string FileName - { - get; - set; - } + public string FullFileName { get; set; } + + /// + /// The file name, not the including path. + /// + public string FileNameOnly { get; set; } /// /// Identifies if the schema version being exported is IFC 2x2. @@ -985,39 +1026,23 @@ public bool Use2DRoomBoundaryForRoomVolumeCreation /// /// Contains options for setting how entity names are generated. /// - public NamingOptions NamingOptions - { - get; - set; - } + public NamingOptions NamingOptions { get; set; } /// /// The file format to export. Not used currently. /// // TODO: Connect this to the output file being written by the client. - public IFCFileFormat IFCFileFormat - { - get; - set; - } + public IFCFileFormat IFCFileFormat { get; set; } /// /// Select export Config Name from the UI /// - public String SelectedConfigName - { - get; - set; - } + public string SelectedConfigName { get; set; } /// /// Select export Config Name from the UI /// - public String SelectedParametermappingTableName - { - get; - set; - } + public string SelectedParametermappingTableName { get; set; } /// /// Allow exporting a mix of extrusions and BReps as a solid model, if possible. @@ -1027,20 +1052,12 @@ public String SelectedParametermappingTableName /// /// Specifies which phase id to export. May be expanded to phases. /// - public ElementId ActivePhaseId - { - get; - protected set; - } + public ElementId ActivePhaseId { get; protected set; } /// /// The phase element corresponding to the phase id. /// - public Phase ActivePhaseElement - { - get; - protected set; - } + public Phase ActivePhaseElement { get; protected set; } /// /// The status of how to handle Revit link instances. @@ -1056,6 +1073,12 @@ public bool ExportingSeparateLink() return ExportLinkedFileAs == LinkedFileExportAs.ExportAsSeparate; } + /// + /// The table that contains Revit class to IFC entity mappings. + /// + public string CategoryMappingTableName { get; set; } = null; + + private IList> LinkInstanceInfos { get; } = new List>(); /// @@ -1128,7 +1151,26 @@ public View ActiveView /// true if the entity found in the set public bool IsElementInExcludeList(IFCEntityType entity) { - return (ExcludeElementSet.Contains(entity.ToString())); + return IsEntityInExcludeList(entity.ToString()); + } + + /// + /// To check whether a specified IFC Entity is listed in the Exclude Filter (from configuration) + /// + /// IFCEntityType enumeration representing the IFC entity concerned + /// true if the entity found in the set + public bool IsEntityInExcludeList(string entityTypeName) + { + return ExcludeElementSet.Contains(entityTypeName); + } + + /// + /// Check whether there is an Exclude Filter (from configuration) + /// + /// True if there are any entities excluded. + public bool HasExcludeList() + { + return ExcludeElementSet.Count > 0; } /// @@ -1148,8 +1190,13 @@ HashSet ExcludeElementSet if (!string.IsNullOrEmpty(ExcludeFilter)) { string[] eList = ExcludeFilter.Split(';'); - foreach (string elem in eList) - exclSet.Add(elem); + foreach (string entityToFilter in eList) + { + if (!string.IsNullOrWhiteSpace(entityToFilter)) + { + exclSet.Add(entityToFilter); + } + } } _excludesElementSet = exclSet; return _excludesElementSet; diff --git a/Source/Revit.IFC.Export/Utility/ExporterCacheManager.cs b/Source/Revit.IFC.Export/Utility/ExporterCacheManager.cs index 168d3442..033718c8 100644 --- a/Source/Revit.IFC.Export/Utility/ExporterCacheManager.cs +++ b/Source/Revit.IFC.Export/Utility/ExporterCacheManager.cs @@ -27,8 +27,6 @@ using Revit.IFC.Export.Toolkit; using Revit.IFC.Export.Exporter.PropertySet; using Revit.IFC.Common.Enums; -using System.Runtime.Remoting.Contexts; -using System.Runtime.InteropServices.WindowsRuntime; using Revit.IFC.Common.Utility; namespace Revit.IFC.Export.Utility @@ -41,7 +39,7 @@ public class ExporterCacheManager /// /// The AllocatedGeometryObjectCache object. /// - static AllocatedGeometryObjectCache m_AllocatedGeometryObjectCache; + static public AllocatedGeometryObjectCache AllocatedGeometryObjectCache { get; protected set; } = new AllocatedGeometryObjectCache(); /// /// The AssemblyInstanceCache object. @@ -60,14 +58,9 @@ public class ExporterCacheManager static IDictionary m_CanExportBeamGeometryAsExtrusionCache; /// - /// Cache the values of the IFC entity class from the IFC Export table by category. + /// Keeps track of the active ifc category mapping template. /// - static Dictionary, string> m_CategoryClassNameCache; - - /// - /// Cache the values of the IFC entity pre-defined type from the IFC Export table by category. - /// - static Dictionary, string> m_CategoryTypeCache; + static IFCCategoryTemplate m_CategoryMappingTemplate = null; /// /// The ClassificationCache object. @@ -105,6 +98,12 @@ public class ExporterCacheManager /// static ElementToHandleCache m_ElementToHandleCache; + /// + /// A mapping of element ids to a material id determined by looking at element parameters. + /// + static public IDictionary ElementIdMaterialParameterCache { get; set; } = + new Dictionary(); + /// /// The ElementTypeToHandleCache cache /// @@ -112,6 +111,11 @@ public class ExporterCacheManager static IDictionary m_FabricParamsCache; + /// + /// The ExporterIFC used to access internal IFC API functions. + /// + static public ExporterIFC ExporterIFC { get; set; } = null; + /// /// The ExportOptions cache. /// @@ -479,6 +483,11 @@ public static IFCAnyHandle Get2DContextHandle(IFCRepresentationIdentifier identi /// static public IDictionary> ComplexPropertyCache { get; set; } = new Dictionary>(); + /// + /// Cache for Base Quantities that require separate calculation. + /// + static public IDictionary> BaseQuantitiesCache { get; set; } = new Dictionary>(); + /// /// Cache for the Project Location that comes from the Selected Site on export option /// @@ -488,19 +497,6 @@ public static IFCAnyHandle Get2DContextHandle(IFCRepresentationIdentifier identi /// static public HashSet<(IFCAnyHandle, string)> QtoSetCreated { get; set; } = new HashSet<(IFCAnyHandle, string)>(); - /// - /// The AllocatedGeometryObjectCache object. - /// - public static AllocatedGeometryObjectCache AllocatedGeometryObjectCache - { - get - { - if (m_AllocatedGeometryObjectCache == null) - m_AllocatedGeometryObjectCache = new AllocatedGeometryObjectCache(); - return m_AllocatedGeometryObjectCache; - } - } - /// /// The AssemblyInstanceCache object. /// @@ -528,29 +524,35 @@ public static AssemblyInstanceCache AssemblyInstanceCache } } - /// - /// The CategoryClassNameCache object. - /// - public static IDictionary, string> CategoryClassNameCache + public static IFCCategoryTemplate CategoryMappingTemplate { get { - if (m_CategoryClassNameCache == null) - m_CategoryClassNameCache = new Dictionary, string>(); - return m_CategoryClassNameCache; - } - } + // TODO: this isn't really correct if we are exporting multiple documents. + if (m_CategoryMappingTemplate == null) + { + try + { + string name = ExportOptionsCache.CategoryMappingTableName; + if (name != null) + { + Document document = ExportOptionsCache.HostDocument ?? Document; + m_CategoryMappingTemplate = IFCCategoryTemplate.FindByName(document, name); + } + } + catch + { + m_CategoryMappingTemplate = null; + } + + if (m_CategoryMappingTemplate == null) + { + m_CategoryMappingTemplate = IFCCategoryTemplate.GetOrCreateInSessionTemplate(Document); + } + m_CategoryMappingTemplate?.UpdateCategoryList(Document); + } - /// - /// The CategoryTypeCache object. - /// - public static IDictionary, string> CategoryTypeCache - { - get - { - if (m_CategoryTypeCache == null) - m_CategoryTypeCache = new Dictionary, string>(); - return m_CategoryTypeCache; + return m_CategoryMappingTemplate; } } @@ -1491,9 +1493,12 @@ public static void Clear(bool fullClear) { if (fullClear) { + m_CategoryMappingTemplate = null; m_CertifiedEntitiesAndPsetCache = null; + ExporterIFC = null; m_ExportOptionsCache = null; m_Global3DOriginHandle = null; + Context2DHandles.Clear(); Context3DHandles.Clear(); GUIDCache.Clear(); OwnerHistoryHandle = null; @@ -1509,24 +1514,21 @@ public static void Clear(bool fullClear) SiteHandle = null; } - if (m_AllocatedGeometryObjectCache != null) - m_AllocatedGeometryObjectCache.DisposeCache(); + AllocatedGeometryObjectCache.DisposeCache(); ParameterUtil.ClearParameterValueCaches(); - m_AllocatedGeometryObjectCache = null; m_AreaSchemeCache = null; m_AssemblyInstanceCache = null; BaseLinkedDocumentGUID = null; m_BeamSystemCache = null; BuildingHandle = null; m_CanExportBeamGeometryAsExtrusionCache = null; - m_CategoryClassNameCache = null; - m_CategoryTypeCache = null; m_CeilingSpaceRelCache = null; m_ClassificationCache = null; m_ClassificationLocationCache = null; ContainmentCache = new ContainmentCache(); ComplexPropertyCache.Clear(); + BaseQuantitiesCache.Clear(); m_CreatedInternalPropertySets = null; m_CreatedSpecialPropertySets = null; m_CurveAnnotationCache = null; @@ -1535,6 +1537,7 @@ public static void Clear(bool fullClear) m_DoorWindowDelayedOpeningCreatorCache = null; m_DummyHostCache = null; m_ElementsInAssembliesCache = null; + ElementIdMaterialParameterCache.Clear(); m_ElementToHandleCache = null; m_ElementTypeToHandleCache = null; m_FabricAreaHandleCache = null; @@ -1549,7 +1552,7 @@ public static void Clear(bool fullClear) m_HostPartsCache = null; m_InternallyCreatedRootHandles = null; m_IsExternalParameterValueCache = null; - LevelInfoCache = new LevelInfoCache(); + LevelInfoCache.Clear(ExporterIFC); m_MaterialIdToStyleHandleCache = null; MaterialSetUsageCache = new MaterialSetUsageCache(); m_MaterialSetCache = null; diff --git a/Source/Revit.IFC.Export/Utility/ExporterUtil.cs b/Source/Revit.IFC.Export/Utility/ExporterUtil.cs index 6c2d4487..f7acf557 100644 --- a/Source/Revit.IFC.Export/Utility/ExporterUtil.cs +++ b/Source/Revit.IFC.Export/Utility/ExporterUtil.cs @@ -26,9 +26,9 @@ using Revit.IFC.Export.Exporter; using Revit.IFC.Export.Exporter.PropertySet; using Revit.IFC.Export.Toolkit; -using Revit.IFC.Export.Utility; using Revit.IFC.Common.Utility; using Revit.IFC.Common.Enums; +using Autodesk.Revit.DB.Structure; namespace Revit.IFC.Export.Utility { @@ -396,9 +396,9 @@ public static IFCAnyHandle CreateCartesianPoint(IFCFile file, IList meas double ceilMeasure = Math.Ceiling(value); double floorMeasure = Math.Floor(value); - if (MathUtil.IsAlmostEqual(value, ceilMeasure)) + if (MathUtil.IsAlmostZero(value - ceilMeasure)) cleanMeasure.Add(ceilMeasure); - else if (MathUtil.IsAlmostEqual(value, floorMeasure)) + else if (MathUtil.IsAlmostZero(value - floorMeasure)) cleanMeasure.Add(floorMeasure); else cleanMeasure.Add(value); @@ -748,71 +748,176 @@ public static IList CopyRepresentations(ExporterIFC exporterIFC, E IFCAnyHandleUtil.GetProductDefinitionShapeDescription(origProductDefinitionShape), representations); } - private static string GetIFCClassNameFromExportTable(ExporterIFC exporterIFC, Element element, ElementId categoryId, int specialClassId) + /// + /// Get the mapping information for a particular category and wall function. + /// + /// The category id. + /// The optional wall function. + /// The mapping information for the category if it exists. + /// + public static bool GetCategoryInfoById(ElementId categoryId, WallFunction? wallFunction, + out ExportIFCCategoryInfo info) { - if (element == null) - return null; + CustomSubCategoryId customSubCategoryId = WallFunctionToCustomSubCategoryId(wallFunction); + info = ExporterCacheManager.CategoryMappingTemplate.GetMappingInfoById(ExporterCacheManager.Document, categoryId, customSubCategoryId); + return !(info?.IsDefault() ?? true); + } - KeyValuePair key = new KeyValuePair(categoryId, specialClassId); - string ifcClassName = null; - if (!ExporterCacheManager.CategoryClassNameCache.TryGetValue(key, out ifcClassName)) + /// + /// Converts WallFunction enum to IFC CustomSubCategoryId + /// + /// Optional wall function. + /// The custom sub-category id. + public static CustomSubCategoryId WallFunctionToCustomSubCategoryId(WallFunction? wallFunction) + { + CustomSubCategoryId specialType = CustomSubCategoryId.None; + if (wallFunction == null) + return specialType; + + switch (wallFunction) { - ifcClassName = ExporterIFCUtils.GetIFCClassName(element, exporterIFC); - ExporterCacheManager.CategoryClassNameCache[key] = ifcClassName; + case WallFunction.Coreshaft: specialType = CustomSubCategoryId.Coreshaft; break; + case WallFunction.Exterior: specialType = CustomSubCategoryId.ExteriorWall; break; + case WallFunction.Foundation: specialType = CustomSubCategoryId.FoundationWall; break; + case WallFunction.Interior: specialType = CustomSubCategoryId.InteriorWall; break; + case WallFunction.Retaining: specialType = CustomSubCategoryId.RetainingWall; break; + case WallFunction.Soffit: specialType = CustomSubCategoryId.Soffit; break; } + return specialType; + } - return ifcClassName; + private static string GetIFCEntityNameFromExportTable(WallFunction wallFunction) + { + if (GetCategoryInfoById(new ElementId(BuiltInCategory.OST_Walls), wallFunction, + out ExportIFCCategoryInfo info)) + { + return info.IFCEntityName; + } + return null; } - private static string GetIFCTypeFromExportTable(ExporterIFC exporterIFC, Element element, ElementId categoryId, int specialClassId) + private static string GetIFCTypeFromExportTable(WallFunction wallFunction) { - if (element == null) - return null; + if (GetCategoryInfoById(new ElementId(BuiltInCategory.OST_Walls), wallFunction, + out ExportIFCCategoryInfo info)) + { + return info.IFCPredefinedType; + } + return null; + } - KeyValuePair key = new KeyValuePair(categoryId, specialClassId); - string ifcType = null; - if (!ExporterCacheManager.CategoryTypeCache.TryGetValue(key, out ifcType)) + /// + /// Get the category and function information for an element. + /// + /// The element. + /// The category and the optional function + public static (Category, WallFunction?) GetMappingKeyInformationForElement(Element element) + { + // TODO: This should really return CustomSubCategoryId, which in theory could apply to more than walls. + Category category = CategoryUtil.GetSafeCategory(element); + WallFunction? wallFunction = null; + + ElementId categoryId = category?.Id ?? ElementId.InvalidElementId; + if (categoryId == new ElementId(BuiltInCategory.OST_Walls) && (element is Wall)) { - ifcType = ExporterIFCUtils.GetIFCType(element, exporterIFC); - ExporterCacheManager.CategoryTypeCache[key] = ifcType; + WallType wallType = element.Document.GetElement(element.GetTypeId()) as WallType; + if (wallType != null) + { + wallFunction = wallType.Function; + } } - return ifcType; + return (category, wallFunction); } /// - /// Get the IFC class name assigned in the export layers table for a category. Cache values to avoid calls to internal code. + /// Get the IFC category mapping information associated with an element. /// - /// The exporterIFC class. - /// The category id. - /// The entity name. - public static string GetIFCClassNameFromExportTable(ExporterIFC exporterIFC, ElementId categoryId) + /// The element. + /// The category mapping information, if found. + public static ExportIFCCategoryInfo GetIFCCategoryExportMappingInfo(Element element) { - if (categoryId == ElementId.InvalidElementId) + (Category category, WallFunction? wallFunction) = GetMappingKeyInformationForElement(element); + + ElementId categoryId = category?.Id ?? ElementId.InvalidElementId; + ExportIFCCategoryInfo info; + if (GetCategoryInfoById(categoryId, wallFunction, out info)) + { + // If the category information is default (i.e., export and ), then we try our luck + // with the parent. If the flag is false, we aren't exporting it anyway. In the future, we may + // get the parent info and override the flag, if there is value to that. + if (!info.IsDefault()) + { + return info; + } + } + + // If the wall function is not null, then the "parent" category id is the original + // category id, minus the wall function. + ElementId parentCategoryId = category?.Parent?.Id ?? (wallFunction == null ? ElementId.InvalidElementId : categoryId); + if (parentCategoryId != ElementId.InvalidElementId) + { + if (GetCategoryInfoById(parentCategoryId, null, out ExportIFCCategoryInfo parentInfo)) + { + return parentInfo; + } + } + + return info; + } + + /// + /// Get the IFC entity name assigned in the category mapping table for a particular categoy. + /// + /// The category. + /// The entity name if found. + public static string GetIFCEntityNameFromExportTable(Category category) + { + if (category == null) + { return null; + } - KeyValuePair key = new KeyValuePair(categoryId, -1); - string ifcClassName = null; - if (!ExporterCacheManager.CategoryClassNameCache.TryGetValue(key, out ifcClassName)) + if (GetCategoryInfoById(category.Id, null, out ExportIFCCategoryInfo info)) { - string ifcClassAndTypeName = ExporterIFCUtils.GetIFCClassNameByCategory(categoryId, exporterIFC); - string ifcTypeName = null; - ExportEntityAndPredefinedType(ifcClassAndTypeName, out ifcClassName, out ifcTypeName); - ExporterCacheManager.CategoryClassNameCache[key] = ifcClassName; + return info.IFCEntityName; + } - // This actually represents an error in the export layers table, where the class name and type name - // or jointly given as a class name. This worked before, though, so for now we'll allow this case - // to continue working. - if (!string.IsNullOrEmpty(ifcTypeName) && - (!ExporterCacheManager.CategoryTypeCache.ContainsKey(key) || - string.IsNullOrEmpty(ExporterCacheManager.CategoryTypeCache[key]))) - ExporterCacheManager.CategoryTypeCache[key] = ifcTypeName; + ElementId parentCategoryId = category.Parent?.Id ?? ElementId.InvalidElementId; + if (parentCategoryId != ElementId.InvalidElementId) + { + if (GetCategoryInfoById(parentCategoryId, null, out info)) + { + return info.IFCEntityName; + } } - return ifcClassName; + return null; } - private static string GetIFCClassNameOrTypeForMass(ExporterIFC exporterIFC, Element element, ElementId categoryId, bool getClassName) + /// + /// Get the IFC predefined type name assigned in the category mapping table for a particular categoy. + /// + /// The category. + /// The predefined type if found. + public static string GetIFCTypeFromExportTable(Category category) + { + if (category == null) + return null; + + if (GetCategoryInfoById(category.Id, null, out ExportIFCCategoryInfo info)) + return info.IFCPredefinedType; + + if (category.Parent != null) + { + if (GetCategoryInfoById(category.Parent.Id, null, out info)) + return info.IFCPredefinedType; + } + + return null; + } + + private static Category GetCategoryForMass(Element element) { Options geomOptions = GeometryUtil.GetIFCExportGeometryOptions(); GeometryElement geomElem = element.get_Geometry(geomOptions); @@ -820,46 +925,58 @@ private static string GetIFCClassNameOrTypeForMass(ExporterIFC exporterIFC, Elem return null; SolidMeshGeometryInfo solidMeshCapsule = GeometryUtil.GetSplitSolidMeshGeometry(geomElem); - IList solidInfos = solidMeshCapsule.GetSolidInfos(); + IList solidInfos = solidMeshCapsule.SolidInfoList; IList meshes = solidMeshCapsule.GetMeshes(); - ElementId overrideCatId = ElementId.InvalidElementId; + Category overrideCategory = null; bool initOverrideCatId = false; Document doc = element.Document; foreach (SolidInfo solidInfo in solidInfos) { - if (!ProcessObjectForGStyle(doc, solidInfo.Solid, ref overrideCatId, ref initOverrideCatId)) + if (!ProcessObjectForGStyle(doc, solidInfo.Solid, ref overrideCategory, ref initOverrideCatId)) return null; } foreach (Mesh mesh in meshes) { - if (!ProcessObjectForGStyle(doc, mesh, ref overrideCatId, ref initOverrideCatId)) + if (!ProcessObjectForGStyle(doc, mesh, ref overrideCategory, ref initOverrideCatId)) return null; } + return overrideCategory; + } + + private static string GetIFCClassNameOrTypeForMass(Element element, bool getClassName) + { + Category overrideCategory = GetCategoryForMass(element); if (getClassName) - return GetIFCClassNameFromExportTable(exporterIFC, overrideCatId); - else - { - // At the moment, we don't have the right API to get the type from a categoryId instead of from an element from the category table. As such, we are - // going to hardwire this. The only one that matters is OST_MassFloor. - if (overrideCatId == new ElementId(BuiltInCategory.OST_MassFloor)) - { - string className = GetIFCClassNameFromExportTable(exporterIFC, overrideCatId); - if (string.Compare(className, "IfcSlab", true) == 0) - return "FLOOR"; - if (string.Compare(className, "IfcCovering", true) == 0) - return "FLOORING"; - } + return GetIFCEntityNameFromExportTable(overrideCategory); - return null; // GetIFCTypeFromExportTable(exporterIFC, overrideCatId); + // At the moment, we don't have the right API to get the type from a categoryId instead of from an element + // from the category table. As such, we are going to hardwire this. The only one that matters is OST_MassFloor. + if ((overrideCategory?.Id.Value ?? -1) == (int) BuiltInCategory.OST_MassFloor) + { + string className = GetIFCEntityNameFromExportTable(overrideCategory); + if (string.Compare(className, "IfcSlab", true) == 0) + return "FLOOR"; + if (string.Compare(className, "IfcCovering", true) == 0) + return "FLOORING"; } + + return null; } - private static string GetIFCClassNameOrTypeForWalls(ExporterIFC exporterIFC, Wall wall, ElementId categoryId, bool getClassName) + private static ExportIFCCategoryInfo GetCategoryInfoForMass(Element element) + { + Category overrideCategory = GetCategoryForMass(element); + ElementId categoryId = overrideCategory?.Id ?? ElementId.InvalidElementId; + GetCategoryInfoById(categoryId, null, out ExportIFCCategoryInfo info); + return info; + } + + private static string GetIFCClassNameOrTypeForWalls(Wall wall, bool getClassName) { WallType wallType = wall.WallType; if (wallType == null) @@ -869,44 +986,46 @@ private static string GetIFCClassNameOrTypeForWalls(ExporterIFC exporterIFC, Wal if (ParameterUtil.GetIntValueFromElement(wallType, BuiltInParameter.FUNCTION_PARAM, out wallFunction) != null) { if (getClassName) - return GetIFCClassNameFromExportTable(exporterIFC, wall, categoryId, wallFunction); + return GetIFCEntityNameFromExportTable((WallFunction) wallFunction); else - return GetIFCTypeFromExportTable(exporterIFC, wall, categoryId, wallFunction); + return GetIFCTypeFromExportTable((WallFunction)wallFunction); } return null; } - private static bool ProcessObjectForGStyle(Document doc, GeometryObject geomObj, ref ElementId overrideCatId, ref bool initOverrideCatId) + private static ExportIFCCategoryInfo GetCategoryInfoForWalls(Wall wall) { - GraphicsStyle gStyle = doc.GetElement(geomObj.GraphicsStyleId) as GraphicsStyle; - if (gStyle == null) - return true; - - if (gStyle.GraphicsStyleCategory == null) - return true; + WallFunction? function = wall?.WallType?.Function; + GetCategoryInfoById(new ElementId(BuiltInCategory.OST_Walls), function, out ExportIFCCategoryInfo info); + return info; + } - ElementId currCatId = gStyle.GraphicsStyleCategory.Id; - if (currCatId == ElementId.InvalidElementId) + private static bool ProcessObjectForGStyle(Document doc, GeometryObject geomObj, + ref Category overrideCategory, ref bool initOverrideCatId) + { + GraphicsStyle gStyle = doc.GetElement(geomObj.GraphicsStyleId) as GraphicsStyle; + Category currCategory = gStyle?.GraphicsStyleCategory; + if (currCategory == null) return true; if (!initOverrideCatId) { initOverrideCatId = true; - overrideCatId = currCatId; + overrideCategory = currCategory; return true; } - if (currCatId != overrideCatId) + if (currCategory.Id != overrideCategory.Id) { - overrideCatId = ElementId.InvalidElementId; + overrideCategory = null; return false; } return true; } - private static string GetIFCClassNameOrTypeFromSpecialEntry(ExporterIFC exporterIFC, Element element, ElementId categoryId, bool getClassName) + private static string GetIFCClassNameOrTypeFromSpecialEntry(Element element, ElementId categoryId, bool getClassName) { if (element == null) return null; @@ -917,57 +1036,169 @@ private static string GetIFCClassNameOrTypeFromSpecialEntry(ExporterIFC exporter if (categoryId == new ElementId(BuiltInCategory.OST_Walls)) { if (element is Wall) - return GetIFCClassNameOrTypeForWalls(exporterIFC, element as Wall, categoryId, getClassName); + return GetIFCClassNameOrTypeForWalls(element as Wall, getClassName); } else if (categoryId == new ElementId(BuiltInCategory.OST_Mass)) { - return GetIFCClassNameOrTypeForMass(exporterIFC, element, categoryId, getClassName); + return GetIFCClassNameOrTypeForMass(element, getClassName); + } + + return null; + } + + private static ExportIFCCategoryInfo GetCategoryInfoForSpecialEntry(Element element, ElementId categoryId) + { + if (element == null) + return null; + + // We do special checks for Wall and Massing categories. + // For walls, we check if it is an interior or exterior wall. + // For massing, we check the geometry. If it is all in the same sub-category, we use that instead. + if (categoryId.Value == (int) BuiltInCategory.OST_Walls) + { + return GetCategoryInfoForWalls(element as Wall); + } + else if (categoryId.Value == (int) BuiltInCategory.OST_Mass) + { + return GetCategoryInfoForMass(element); } return null; } /// - /// Get the IFC class name assigned in the export layers table for a category. Cache values to avoid calls to internal code. + /// Get the category id that will be used for category mapping for this element. + /// + /// The element. + /// The category and element id of the category, if it exists. + public static (Category, ElementId) GetSpecificCategoryForElement(Element element) + { + Category actualCategory = CategoryUtil.GetSafeCategory(element); + + ElementId categoryId = actualCategory?.Id ?? ElementId.InvalidElementId; + if (categoryId == ElementId.InvalidElementId) + return (null, categoryId); + + // Special Case for Beams: if the structural usage is set, use that sub-category. + ElementId actualCategoryId = categoryId; + StructuralInstanceUsage usage = (element as FamilyInstance)?.StructuralUsage ?? StructuralInstanceUsage.Undefined; + switch (usage) + { + case StructuralInstanceUsage.Automatic: + case StructuralInstanceUsage.Column: + case StructuralInstanceUsage.Undefined: + case StructuralInstanceUsage.Wall: + break; + case StructuralInstanceUsage.Brace: + actualCategoryId = new ElementId(BuiltInCategory.OST_VerticalBracing); + break; + case StructuralInstanceUsage.Girder: + actualCategoryId = new ElementId(BuiltInCategory.OST_Girder); + break; + case StructuralInstanceUsage.HorizontalBracing: + actualCategoryId = new ElementId(BuiltInCategory.OST_HorizontalBracing); + break; + case StructuralInstanceUsage.Joist: + actualCategoryId = new ElementId(BuiltInCategory.OST_Joist); + break; + case StructuralInstanceUsage.KickerBracing: + actualCategoryId = new ElementId(BuiltInCategory.OST_KickerBracing); + break; + case StructuralInstanceUsage.Other: + { + if (categoryId == new ElementId(BuiltInCategory.OST_StructuralFraming)) + { + actualCategoryId = new ElementId(BuiltInCategory.OST_StructuralFramingOther); + } + break; + } + case StructuralInstanceUsage.Purlin: + actualCategoryId = new ElementId(BuiltInCategory.OST_Purlin); + break; + case StructuralInstanceUsage.TrussChord: + actualCategoryId = new ElementId(BuiltInCategory.OST_TrussChord); + break; + case StructuralInstanceUsage.TrussWeb: + actualCategoryId = new ElementId(BuiltInCategory.OST_TrussWeb); + break; + } + + if (actualCategoryId != categoryId) + { + actualCategory = Category.GetCategory(element.Document, actualCategoryId); + } + return (actualCategory, actualCategoryId); + } + + /// + /// Get the mapping information assigned in the IFC category table for a category. + /// + /// The element. + /// The returned category id. + /// The entity name. + public static ExportIFCCategoryInfo GetCategoryInfoFromExportTable(Element element, out ElementId categoryId) + { + Category category; + (category, categoryId) = GetSpecificCategoryForElement(element); + if (categoryId == ElementId.InvalidElementId) + return null; + + ExportIFCCategoryInfo info = GetCategoryInfoForSpecialEntry(element, categoryId); + if (!info?.IsDefault() ?? false) + { + return info; + } + + if (!GetCategoryInfoById(categoryId, null, out info)) + { + ElementId parentCategoryId = category.Parent?.Id ?? ElementId.InvalidElementId; + if (parentCategoryId != ElementId.InvalidElementId) + { + GetCategoryInfoById(parentCategoryId, null, out info); + } + } + + return info; + } + + /// + /// Get the IFC entity name assigned in the IFC category table for a category. /// - /// The exporterIFC class. /// The element. /// The returned category id. /// The entity name. - public static string GetIFCClassNameFromExportTable(ExporterIFC exporterIFC, - Element element, out ElementId categoryId) + public static string GetIFCEntityNameFromExportTable(Element element, out ElementId categoryId) { Category category = CategoryUtil.GetSafeCategory(element); categoryId = category?.Id ?? ElementId.InvalidElementId; - if (category == null) + if (categoryId == ElementId.InvalidElementId) return null; - string specialEntry = GetIFCClassNameOrTypeFromSpecialEntry(exporterIFC, element, categoryId, true); + string specialEntry = GetIFCClassNameOrTypeFromSpecialEntry(element, categoryId, true); if (specialEntry != null) return specialEntry; - return GetIFCClassNameFromExportTable(exporterIFC, categoryId); + return GetIFCEntityNameFromExportTable(category); } /// /// Get the IFC predefined type assigned in the export layers table for a category. Cache values to avoid calls to internal code. /// - /// The exporterIFC class. /// The element. /// The predefined type. - public static string GetIFCTypeFromExportTable(ExporterIFC exporterIFC, Element element) + public static string GetIFCTypeFromExportTable(Element element) { Category category = CategoryUtil.GetSafeCategory(element); if (category == null) return null; ElementId categoryId = category.Id; - string specialEntry = GetIFCClassNameOrTypeFromSpecialEntry(exporterIFC, element, categoryId, false); + string specialEntry = GetIFCClassNameOrTypeFromSpecialEntry(element, categoryId, false); if (specialEntry != null) return specialEntry; - return GetIFCTypeFromExportTable(exporterIFC, element, categoryId, -1); + return GetIFCTypeFromExportTable(category); } private class ApplicablePsets where T : Description @@ -1115,7 +1346,7 @@ public bool NeedSearch() else { if (!string.IsNullOrEmpty(currDesc.PredefinedType) - && currDesc.PredefinedType.Equals(exportInfo.ValidatedPredefinedType, StringComparison.InvariantCultureIgnoreCase) + && currDesc.PredefinedType.Equals(exportInfo.PredefinedType, StringComparison.InvariantCultureIgnoreCase) && currDesc.PredefinedType.Equals("USERDEFINED", StringComparison.InvariantCultureIgnoreCase)) userdefinedPdefType = true; } @@ -1145,7 +1376,7 @@ public bool NeedSearch() ByIfcEntityType.ByAltPredefinedType.Add(currDesc); } else if (!string.IsNullOrEmpty(currDesc.PredefinedType) && - currDesc.PredefinedType.Equals(exportInfo.ValidatedPredefinedType, StringComparison.InvariantCultureIgnoreCase)) + currDesc.PredefinedType.Equals(exportInfo.PredefinedType, StringComparison.InvariantCultureIgnoreCase)) { if (addToInstance) ByIfcEntity.ByPredefinedType.Add(currDesc); @@ -1244,7 +1475,7 @@ public static IFCExportInfoPair GetExportInfoForProperties(IFCAnyHandle prodHnd) { IFCEntityType altProdHndType = IFCEntityType.UnKnown; if (Enum.TryParse("IfcFurnitureType", true, out altProdHndType)) - exportInfo.SetValue(prodHndType, altProdHndType, exportInfo.ValidatedPredefinedType); + exportInfo.SetValue(prodHndType, altProdHndType, exportInfo.PredefinedType); } } else if (IFCAnyHandleUtil.IsSubTypeOf(prodHnd, IFCEntityType.IfcTypeObject)) @@ -1253,11 +1484,11 @@ public static IFCExportInfoPair GetExportInfoForProperties(IFCAnyHandle prodHnd) ElementTypeKey etKey = ExporterCacheManager.ElementTypeToHandleCache.Find(prodHnd); if (etKey != null) { - exportInfo.SetValueWithPair(etKey.Item2, etKey.Item3); + exportInfo.SetByTypeAndPredefinedType(etKey.Item2, etKey.Item3); } else { - exportInfo.SetValueWithPair(prodHndType); + exportInfo.SetByType(prodHndType); } // Need to handle backward compatibility for IFC2x3 @@ -1266,7 +1497,7 @@ public static IFCExportInfoPair GetExportInfoForProperties(IFCAnyHandle prodHnd) { IFCEntityType altProdHndType = IFCEntityType.UnKnown; if (Enum.TryParse("IfcFurnishingElement", true, out altProdHndType)) - exportInfo.SetValue(prodHndType, altProdHndType, exportInfo.ValidatedPredefinedType); + exportInfo.SetValue(prodHndType, altProdHndType, exportInfo.PredefinedType); } } else @@ -1337,12 +1568,12 @@ public static IFCExportInfoPair GetExportInfoForProperties(IFCAnyHandle prodHnd) applicablePsets.ByIfcEntityType.ByType = GetCachedValue(processType, cacheToUse, typeEntity, null); - if (!string.IsNullOrEmpty(exportInfo.ValidatedPredefinedType)) + if (!exportInfo.IsPredefinedTypeDefault) { applicablePsets.ByIfcEntity.ByPredefinedType = - GetCachedValue(processInstance, cacheToUse, instanceEntity, exportInfo.ValidatedPredefinedType); + GetCachedValue(processInstance, cacheToUse, instanceEntity, exportInfo.PredefinedType); applicablePsets.ByIfcEntityType.ByPredefinedType = - GetCachedValue(processType, cacheToUse, typeEntity, exportInfo.ValidatedPredefinedType); + GetCachedValue(processType, cacheToUse, typeEntity, exportInfo.PredefinedType); } if (!string.IsNullOrEmpty(objectType)) @@ -1404,11 +1635,8 @@ public static IFCExportInfoPair GetExportInfoForProperties(IFCAnyHandle prodHnd) IFCEntityType typeEntity = (processType && unknownType) ? exportInfo.ExportInstance : exportInfo.ExportType; - currPsets.PopulateCache(exportInfo.ExportInstance, - typeEntity, - exportInfo.ValidatedPredefinedType, - objectType, - cacheToUse); + currPsets.PopulateCache(exportInfo.ExportInstance, typeEntity, exportInfo.PredefinedType, + objectType, cacheToUse); } currPsets.PopulateFromCache(cachedPsets); @@ -1695,6 +1923,16 @@ private static void ExportElementQuantities(ExporterIFC exporterIFC, Element ele } } + if (ExporterCacheManager.BaseQuantitiesCache.TryGetValue(prodHnd, out addQuantity)) + { + foreach (IFCAnyHandle addQty in addQuantity) + { + quantities.Add(addQty); + string addQtyName = IFCAnyHandleUtil.GetStringAttribute(addQty, "Name"); + uniqueQuantityNames.Add(addQtyName); + } + } + IFCExportBodyParams ifcParams = productWrapper.FindExtrusionCreationParameters(prodHnd); HashSet qtyFromInit = currDesc.ProcessEntries(file, exporterIFC, ifcParams, elementToUse, elemTypeToUse); @@ -1782,8 +2020,13 @@ private static void ExportElementClassifications(ExporterIFC exporterIFC, Elemen if (productSet.Count > 1 && prodHnd == productSet.First() && IFCAnyHandleUtil.IsTypeOf(prodHnd, IFCEntityType.IfcElementAssembly)) continue; //Classification for the ELementAssembly should have been created before when processing ElementAssembly + ElementId elementId = ExporterCacheManager.HandleToElementCache.Find(prodHnd); + Element elementToUse = (elementId == ElementId.InvalidElementId) ? element : element?.Document?.GetElement(elementId); + if (elementToUse == null) + continue; + // No need to check the subtype since Classification can be assigned to IfcRoot - ClassificationUtil.CreateClassification(exporterIFC, file, element, prodHnd); + ClassificationUtil.CreateClassification(exporterIFC, file, elementToUse, prodHnd); } transaction.Commit(); } @@ -1880,7 +2123,7 @@ public static string GetExportTypeFromTypeParameter(Element element, Element ele if (!string.IsNullOrEmpty(predefType)) { - exportType.ValidatedPredefinedType = predefType; + exportType.PredefinedType = predefType; } return exportType; @@ -1912,6 +2155,23 @@ private static IFCExportInfoPair GetExportTypeForFurniture(ExporterIFC exporterI return IFCExportInfoPair.UnKnown; } + private static IFCExportInfoPair GetExportTypeForCurtainSystem(Element element, string predefinedType) + { + if (CurtainSystemExporter.IsCurtainSystem(element) || CurtainSystemExporter.IsLegacyCurtainElement(element)) + { + if (element is RoofBase) + { + return new IFCExportInfoPair(IFCEntityType.IfcRoof, predefinedType); + } + else + { + return new IFCExportInfoPair(IFCEntityType.IfcCurtainWall, predefinedType); + } + } + + return IFCExportInfoPair.UnKnown; + } + private static IFCExportInfoPair OverrideExportTypeForStructuralFamilies(Element element, IFCExportInfoPair originalExportInfoPair) { @@ -1924,7 +2184,7 @@ private static IFCExportInfoPair GetExportTypeForFurniture(ExporterIFC exporterI if (familyInstance == null) return originalExportInfoPair; - string enumTypeValue = originalExportInfoPair.ValidatedPredefinedType; + string enumTypeValue = originalExportInfoPair.PredefinedType; switch (familyInstance.StructuralType) { @@ -1968,11 +2228,12 @@ private static IFCExportInfoPair GetExportTypeForFurniture(ExporterIFC exporterI // in the IFC Export Options table. If so, this overrides all other settings. // 2. For the special case of an element in a group, check if it in an IfcFurniture group. // 3. Check the parameters IFC_EXPORT_ELEMENT*_AS. - // 4. Look at class specified by the IFC Export Options table in step 1, if set. - // 5. Check at a pre-defined mapping from Revit category to IFC entity and pre-defined type. - // 6. Check whether the intended Entity type is inside the export exclusion set. - // 7. Check whether we override IfcBuildingElementProxy/Unknown values with structural known values. - // 8. Check to see if we should override the ValidatedPredefinedType from IFC_EXPORT_PREDEFINEDTYPE*. + // 4. If Element is intended to be exported as a curtain systen, find the default export settings for that. + // 5. Look at class specified by the IFC Export Options table in step 1, if set. + // 6. Check at a pre-defined mapping from Revit category to IFC entity and pre-defined type. + // 7. Check whether the intended Entity type is inside the export exclusion set. + // 8. Check whether we override IfcBuildingElementProxy/Unknown values with structural known values. + // 9. Check to see if we should override the ValidatedPredefinedType from IFC_EXPORT_PREDEFINEDTYPE*. // Steps start below. @@ -1983,10 +2244,12 @@ private static IFCExportInfoPair GetExportTypeForFurniture(ExporterIFC exporterI // Note that this means that if the Walls category is not exported, but a wall is set to be // exported as, e.g., an IfcCeilingType, it won't be exported. We may want to reconsider this // in the future based on customer feedback. - ElementId categoryId; - string ifcClassName = GetIFCClassNameFromExportTable(exporterIFC, element, out categoryId); - if (categoryId == ElementId.InvalidElementId) + ElementId categoryId = ElementId.InvalidElementId; + string ifcClassName = null; + ExportIFCCategoryInfo info = GetCategoryInfoFromExportTable(element, out categoryId); + if (info != null && !info.IFCExportFlag) return IFCExportInfoPair.UnKnown; + ifcClassName = info?.IFCEntityName; // 2. If Element is contained within a Group that is exported as IfcFurniture, it should be // exported as an IfcSystemFurnitureElement, regardless of other settings. @@ -2000,48 +2263,58 @@ private static IFCExportInfoPair GetExportTypeForFurniture(ExporterIFC exporterI isExportTypeDefinedInParameters = !exportType.IsUnKnown; } - // 4. Look at class specified by the IFC Export Options table in step 1. + // 4. If Element is intended to be exported as a curtain systen, find the default export + // settings for that. + if (!isExportTypeDefinedInParameters) + { + exportType = GetExportTypeForCurtainSystem(element, exportType.PredefinedType); + } + + // 5. Look at class specified by the IFC Export Options table in step 1. if (exportType.IsUnKnown && !string.IsNullOrEmpty(ifcClassName)) { if (string.IsNullOrEmpty(enumTypeValue)) - enumTypeValue = GetIFCTypeFromExportTable(exporterIFC, element); + { + enumTypeValue = info?.IFCPredefinedType ?? GetIFCTypeFromExportTable(element); + } + // if using name, override category id if match is found. if (!ifcClassName.Equals("Default", StringComparison.OrdinalIgnoreCase)) { exportType = ElementFilteringUtil.GetExportTypeFromClassName(ifcClassName); - exportType.ValidatedPredefinedType = enumTypeValue; + exportType.PredefinedType = enumTypeValue; } } - // 5. Check at a pre-defined mapping from Revit category to IFC entity and pre-defined type. + // 6. Check at a pre-defined mapping from Revit category to IFC entity and pre-defined type. if (exportType.IsUnKnown) { exportType = ElementFilteringUtil.GetExportTypeFromCategoryId(categoryId); if (string.IsNullOrEmpty(enumTypeValue)) - enumTypeValue = exportType.ValidatedPredefinedType; + enumTypeValue = exportType.PredefinedType; } - // 6. Check whether the intended Entity type is inside the export exclusion set. If it is, + // 7. Check whether the intended Entity type is inside the export exclusion set. If it is, // we are done - we won't export it. if (ExporterCacheManager.ExportOptionsCache.IsElementInExcludeList(exportType.ExportInstance)) return IFCExportInfoPair.UnKnown; - // 7. Check whether we override IfcBuildingElementProxy/Unknown values with + // 8. Check whether we override IfcBuildingElementProxy/Unknown values with // structural known values. if (!isExportTypeDefinedInParameters) exportType = OverrideExportTypeForStructuralFamilies(element, exportType); - // 8. Check to see if we should override the ValidatedPredefinedType from + // 9. Check to see if we should override the ValidatedPredefinedType from // IFC_EXPORT_PREDEFINEDTYPE*. string pdefFromParam = GetExportTypeFromTypeParameter(element, null); if (!string.IsNullOrEmpty(pdefFromParam)) enumTypeValue = pdefFromParam; if (!string.IsNullOrEmpty(enumTypeValue)) - exportType.ValidatedPredefinedType = enumTypeValue; + exportType.PredefinedType = enumTypeValue; // Set the out parameter here. - enumTypeValue = exportType.ValidatedPredefinedType; + enumTypeValue = exportType.PredefinedType; if (string.IsNullOrEmpty(enumTypeValue)) enumTypeValue = "NOTDEFINED"; @@ -2858,15 +3131,23 @@ public static ExportPartAs ShouldExportByComponentsOrParts(Element element, int /// /// the element /// whether it can be exported by components or parts - public static ExportPartAs CanExportByComponentsOrParts(Element element) + public static ExportPartAs CanExportByComponentsOrParts(Element element, ref GeometryElement geomElem) { ExportPartAs exportPartAs = ShouldExportByComponentsOrParts(element, PartUtils.GetAssociatedParts(element.Document, element.Id, false, true).Count); - if (PartUtils.HasAssociatedParts(element.Document, element.Id) && (exportPartAs == ExportPartAs.Part || exportPartAs == ExportPartAs.ShapeAspect)) + if (!PartUtils.HasAssociatedParts(element.Document, element.Id)) { - return exportPartAs; + exportPartAs = ExportPartAs.None; } - return ExportPartAs.None; + // We may have previously nuked the geometry because we thought we were going to export parts. However, for + // some reason or other we decided not to, so we need to get the geometry again. An open question is why we + // thought we were exporting parts when we weren't. + if (exportPartAs == ExportPartAs.None && geomElem == null) + { + geomElem = element.get_Geometry(GeometryUtil.GetIFCExportGeometryOptions()); + } + + return exportPartAs; } /// @@ -2877,30 +3158,35 @@ public static ExportPartAs CanExportByComponentsOrParts(Element element) /// true - if parts have been successfully created. false - is creation of parts is not possible. public static bool CreateParts(Element element, int layersCount, ref GeometryElement geometryElement) { + if (!ExporterCacheManager.ExportOptionsCache.ExportAs4ReferenceView) + return false; + ExportPartAs exportPartAs = ShouldExportByComponentsOrParts(element, layersCount); + if (exportPartAs != ExportPartAs.Part && exportPartAs != ExportPartAs.ShapeAspect) + return false; + + Document doc = element.Document; + ICollection ids = new List() { element.Id }; + if (!PartUtils.AreElementsValidForCreateParts(doc, ids)) + return false; + + PartUtils.CreateParts(doc, ids); + doc.Regenerate(); - if (ExporterCacheManager.ExportOptionsCache.ExportAs4ReferenceView && (exportPartAs == ExportPartAs.Part || exportPartAs == ExportPartAs.ShapeAspect)) + //geometryElement is either re-acquired or set to null here because call to PartUtils.CreateParts() and doc.Regenerate() can invalidate its value. + if (exportPartAs == ExportPartAs.ShapeAspect) { - Document doc = element.Document; - ICollection ids = new List() { element.Id }; - if (PartUtils.AreElementsValidForCreateParts(doc, ids)) - { - PartUtils.CreateParts(doc, ids); - doc.Regenerate(); - - //geometryElement is either re-acquired or set to null here because call to PartUtils.CreateParts() and doc.Regenerate() can invalidate its value. - if (exportPartAs == ExportPartAs.ShapeAspect) - //If we export Shape Aspects we will also export original geometry because Shape Aspects reference it. - //When exporting original geometry the code will use GeometryElement so get it now. - geometryElement = element.get_Geometry(GeometryUtil.GetIFCExportGeometryOptions()); - else - //If we export Parts we do not need to export original geometry so set geometryElement to null. - geometryElement = null; - return true; - } + //If we export Shape Aspects we will also export original geometry because Shape Aspects reference it. + //When exporting original geometry the code will use GeometryElement so get it now. + geometryElement = element.get_Geometry(GeometryUtil.GetIFCExportGeometryOptions()); + } + else + { + //If we export Parts we do not need to export original geometry so set geometryElement to null. + geometryElement = null; } - return false; + return true; } /// @@ -2923,6 +3209,11 @@ public static bool ExportingHostModel() (ExporterCacheManager.BaseLinkedDocumentGUID == null); } - + /// + /// Detects if Element is part of Assembly. This is useful during Export. + /// + /// Element to check. + /// True if non-null Element is part of Assembly, false otherwise. + public static bool IsContainedInAssembly(Element element) => ((element?.AssemblyInstanceId ?? ElementId.InvalidElementId) != ElementId.InvalidElementId); } } \ No newline at end of file diff --git a/Source/Revit.IFC.Export/Utility/FamilyExporterUtil.cs b/Source/Revit.IFC.Export/Utility/FamilyExporterUtil.cs index 3626e217..48b9be10 100644 --- a/Source/Revit.IFC.Export/Utility/FamilyExporterUtil.cs +++ b/Source/Revit.IFC.Export/Utility/FamilyExporterUtil.cs @@ -237,8 +237,7 @@ public static bool IsFurnitureSubType(IFCExportInfoPair exportType) IFCAnyHandle localPlacementToUse = useOverridePlacement ? overrideLocalPlacement : setter.LocalPlacement; - bool isChildInContainer = - (familyInstance.AssemblyInstanceId != ElementId.InvalidElementId) || useOverridePlacement; + bool isChildInContainer = ExporterUtil.IsContainedInAssembly(familyInstance) || useOverridePlacement; ElementId roomId = ElementId.InvalidElementId; if (IsRoomRelated(type)) @@ -265,8 +264,7 @@ public static bool IsFurnitureSubType(IFCExportInfoPair exportType) break; } - string preDefinedType = string.IsNullOrWhiteSpace(type.ValidatedPredefinedType) ? - defaultPreDefinedType : type.ValidatedPredefinedType; + string preDefinedType = type.IsPredefinedTypeDefault ? defaultPreDefinedType : type.PredefinedType; IFCAnyHandle instanceHandle = null; switch (type.ExportInstance) @@ -333,7 +331,9 @@ public static bool IsFurnitureSubType(IFCExportInfoPair exportType) } case IFCEntityType.IfcSpace: { - IFCInternalOrExternal internalOrExternal = CategoryUtil.IsElementExternal(familyInstance) ? IFCInternalOrExternal.External : IFCInternalOrExternal.Internal; + IFCInternalOrExternal internalOrExternal = IFCInternalOrExternal.NotDefined; + if(CategoryUtil.IsElementExternal(familyInstance).HasValue) + internalOrExternal = CategoryUtil.IsElementExternal(familyInstance).Value? IFCInternalOrExternal.External : IFCInternalOrExternal.Internal; instanceHandle = IFCInstanceExporter.CreateSpace(exporterIFC, familyInstance, instanceGUID, ownerHistory, localPlacementToUse, productRepresentation, @@ -415,7 +415,6 @@ public static bool IsFurnitureSubType(IFCExportInfoPair exportType) /// If the guid is not provided, it will be generated from the elementType. public static IFCAnyHandle ExportGenericType(ExporterIFC exporterIFC, IFCExportInfoPair type, - string ifcEnumType, HashSet propertySets, IList representationMapList, Element instance, @@ -450,7 +449,7 @@ public static bool IsFurnitureSubType(IFCExportInfoPair exportType) // TODO_GUID: This is just a patch at the moment. We should fix the callers of // this function so that we don't need to do this here. Furthermore, we should // take into account the exportType into the guid generation. - type = AdjustExportTypeForSchema(type, type.ValidatedPredefinedType); + type = AdjustExportTypeForSchema(type); typeHandle = IFCInstanceExporter.CreateGenericIFCType(type, elementType, guid, file, propertySets, representationMapList); @@ -465,23 +464,23 @@ public static bool IsFurnitureSubType(IFCExportInfoPair exportType) return typeHandle; } - public static IFCExportInfoPair AdjustExportTypeForSchema(IFCExportInfoPair exportType, - string ifcEnumType) + public static IFCExportInfoPair AdjustExportTypeForSchema(IFCExportInfoPair exportType) { IFCExportInfoPair exportInfo = exportType; + string ifcEnumType = exportType.PredefinedType; if (ExporterCacheManager.ExportOptionsCache.ExportAsOlderThanIFC4) { // Handle special cases for upward compatibility switch (exportType.ExportType) { case IFCEntityType.IfcBurnerType: - exportInfo.SetValueWithPair(IFCEntityType.IfcGasTerminalType, ifcEnumType); + exportInfo.SetByTypeAndPredefinedType(IFCEntityType.IfcGasTerminalType, ifcEnumType); break; case IFCEntityType.IfcDoorType: - exportInfo.SetValueWithPair(IFCEntityType.IfcDoorStyle, ifcEnumType); + exportInfo.SetByTypeAndPredefinedType(IFCEntityType.IfcDoorStyle, ifcEnumType); break; case IFCEntityType.IfcWindowType: - exportInfo.SetValueWithPair(IFCEntityType.IfcWindowStyle, ifcEnumType); + exportInfo.SetByTypeAndPredefinedType(IFCEntityType.IfcWindowStyle, ifcEnumType); break; case IFCEntityType.UnKnown: { @@ -500,17 +499,17 @@ public static bool IsFurnitureSubType(IFCExportInfoPair exportType) { // For compatibility with IFC2x3 and before. IfcGasTerminalType has been removed and IfcBurnerType replaces it in IFC4 case IFCEntityType.IfcGasTerminalType: - exportInfo.SetValueWithPair(IFCEntityType.IfcBurnerType, ifcEnumType); + exportInfo.SetByTypeAndPredefinedType(IFCEntityType.IfcBurnerType, ifcEnumType); break; // For compatibility with IFC2x3 and before. IfcElectricHeaterType has been removed and IfcSpaceHeaterType replaces it in IFC4 case IFCEntityType.IfcElectricHeaterType: - exportInfo.SetValueWithPair(IFCEntityType.IfcSpaceHeaterType, ifcEnumType); + exportInfo.SetByTypeAndPredefinedType(IFCEntityType.IfcSpaceHeaterType, ifcEnumType); break; case IFCEntityType.UnKnown: { if (exportType.ExportInstance == IFCEntityType.IfcFooting) { - exportInfo.SetValueWithPair(IFCEntityType.IfcFootingType, ifcEnumType); + exportInfo.SetByTypeAndPredefinedType(IFCEntityType.IfcFootingType, ifcEnumType); } break; } @@ -596,37 +595,21 @@ private static bool IsRoomRelated(IFCExportInfoPair exportType) Category graphicsStyleCategory = gStyle.GraphicsStyleCategory; if (graphicsStyleCategory != null) { - // Remove the geometry that is not visible - if (!ElementFilteringUtil.IsCategoryVisible(graphicsStyleCategory, filterView)) + bool removeObject = !ElementFilteringUtil.ShouldCategoryBeExported(graphicsStyleCategory, false); + + if (removeObject) { if (obj is Solid) + { solids.Remove(obj as Solid); + } else if (obj is Mesh) + { meshes.Remove(obj as Mesh); + } continue; } - - ElementId catId = graphicsStyleCategory.Id; - - string ifcClassName = ExporterUtil.GetIFCClassNameFromExportTable(exporterIFC, catId); - if (!string.IsNullOrEmpty(ifcClassName)) - { - bool foundName = String.Compare(ifcClassName, "Default", true) != 0; - if (foundName) - { - IFCExportInfoPair exportType = ElementFilteringUtil.GetExportTypeFromClassName(ifcClassName); - if (exportType.ExportInstance == IFCEntityType.UnKnown) - { - if (obj is Solid) - solids.Remove(obj as Solid); - else if (obj is Mesh) - meshes.Remove(obj as Mesh); - - continue; - } - } - } } } geomObjectsOut.Add(obj); diff --git a/Source/Revit.IFC.Export/Utility/FootPrintInfo.cs b/Source/Revit.IFC.Export/Utility/FootPrintInfo.cs index c52635f4..3098da74 100644 --- a/Source/Revit.IFC.Export/Utility/FootPrintInfo.cs +++ b/Source/Revit.IFC.Export/Utility/FootPrintInfo.cs @@ -119,10 +119,8 @@ public IFCAnyHandle CreateFootprintShapeRepresentation(ExporterIFC exporterIFC) ISet repItems = new HashSet(); foreach (CurveLoop extrusionBoundaryLoop in ExtrusionBaseLoops) { - IFCAnyHandle footprintGeomRepItem = GeometryUtil.CreateIFCCurveFromCurveLoop(exporterIFC, extrusionBoundaryLoop, - ExtrusionBaseLCS, new XYZ(0, 0, 1.0)); - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(footprintGeomRepItem)) - repItems.Add(footprintGeomRepItem); + repItems.AddIfNotNull(GeometryUtil.CreateIFCCurveFromCurveLoop(exporterIFC, + extrusionBoundaryLoop, ExtrusionBaseLCS, XYZ.BasisZ)); } IFCAnyHandle footprintShapeRep = null; diff --git a/Source/Revit.IFC.Export/Utility/GUIDUtil.cs b/Source/Revit.IFC.Export/Utility/GUIDUtil.cs index 8ab615df..f1b49665 100644 --- a/Source/Revit.IFC.Export/Utility/GUIDUtil.cs +++ b/Source/Revit.IFC.Export/Utility/GUIDUtil.cs @@ -294,7 +294,7 @@ public static GUIDString CreateGUIDString(IFCEntityType type, string uniqueKey) { IFCEntityType entityType = useInstanceGeometry ? exportInfoPair.ExportInstance : exportInfoPair.ExportType; - string predefinedType = exportInfoPair.ValidatedPredefinedType ?? string.Empty; + string predefinedType = exportInfoPair.PredefinedType ?? string.Empty; hash += " Entity: " + entityType.ToString() + ":" + predefinedType; } diff --git a/Source/Revit.IFC.Export/Utility/GeometryUtil.cs b/Source/Revit.IFC.Export/Utility/GeometryUtil.cs index 4d1c7044..7450fd51 100644 --- a/Source/Revit.IFC.Export/Utility/GeometryUtil.cs +++ b/Source/Revit.IFC.Export/Utility/GeometryUtil.cs @@ -34,6 +34,16 @@ namespace Revit.IFC.Export.Utility /// public class GeometryUtil { + /// + /// An enum used in CreateIFCCurveFromRevitCurve to determine how to create + /// the Revit curve in IFC entities. + /// + public enum TrimCurvePreference + { + BaseCurve, // Do not trim the curve. + TrimmedCurve, // Use a base curve and trim the curve + UsePolyLineOrTrim, // Use a polyline for a bounded line, otherwise trim. + } /// /// The comparer for comparing XYZ. /// @@ -663,7 +673,7 @@ public static SolidMeshGeometryInfo GetSolidMeshGeometry(GeometryElement geomEle if (geomElemToUse != null) { // call to recursive helper method to obtain all solid and mesh geometry within geomElemToUse - CollectSolidMeshGeometry(geomElemToUse, null, trf, geometryInfo); + geometryInfo.CollectSolidMeshGeometry(geomElemToUse, ExporterCacheManager.AllocatedGeometryObjectCache); } return geometryInfo; } @@ -716,7 +726,7 @@ public static SolidMeshGeometryInfo GetClippedSolidMeshGeometry(GeometryElement public static SolidMeshGeometryInfo GetSplitSolidMeshGeometry(GeometryElement geomElemToUse, Transform trf) { SolidMeshGeometryInfo geometryInfo = GetSolidMeshGeometry(geomElemToUse, Transform.Identity); - geometryInfo.SplitSolidsList(); + SplitSolids(geometryInfo); return geometryInfo; } @@ -752,99 +762,81 @@ public static SolidMeshGeometryInfo GetSplitClippedSolidMeshGeometry(GeometryEle return GetSplitSolidMeshGeometry(geomElemToUse); SolidMeshGeometryInfo geometryInfo = GetClippedSolidMeshGeometry(geomElemToUse, range); - geometryInfo.SplitSolidsList(); + SplitSolids(geometryInfo); return geometryInfo; } /// - /// Transforms a geometry by a given transform. + /// The maximum number of faces in a Solid before we decide not to split it. + /// Larger than this can cause sigificant performance issues. /// - /// The geometry element created by "GetTransformed" is a copy which will have its own allocated - /// membership - this needs to be stored and disposed of (see AllocatedGeometryObjectCache - /// for details) - /// The geometry. - /// The transform. - /// The transformed geometry. - public static GeometryElement GetTransformedGeometry(GeometryElement geomElem, Transform trf) - { - if (geomElem == null) - return null; - - GeometryElement currGeomElem = geomElem.GetTransformed(trf); - ExporterCacheManager.AllocatedGeometryObjectCache.AddGeometryObject(currGeomElem); - return currGeomElem; - } + /// + /// Internal tests show perfectly good behavior at 1044 faces, so setting + /// this value based on that. This may be tweaked over time, or other + /// methods used instead. + /// + public static int MaxFaceCountForSplitVolumes = 2048; /// - /// Collects all solids and meshes within all nested levels of a given GeometryElement. + /// Splits a Solid into distinct volumes. /// - /// - /// This is a private helper method for the GetSolidMeshGeometry type collection methods. - /// - /// The GeometryElement we are collecting solids and meshes from. - /// The element that contains the geomElem. It can be null. - /// The initial Transform applied on the GeometryElement. - /// The SolidMeshGeometryInfo object that contains the lists of collected solids and meshes. - private static void CollectSolidMeshGeometry(GeometryElement geomElem, - Element containingElement, Transform trf, SolidMeshGeometryInfo solidMeshCapsule) + /// The initial solid. + /// The list of volumes. + /// This calls the internal SolidUtils.SplitVolumes routine, but does additional cleanup work to properly dispose of stale data. + public static IList SplitVolumes(Solid solid) { - if (geomElem == null) - return; - - GeometryElement currGeomElem = geomElem; - Transform localTrf = trf; - if (localTrf == null) - localTrf = Transform.Identity; - else if (!localTrf.IsIdentity) - currGeomElem = GetTransformedGeometry(geomElem, localTrf); - - // iterate through the GeometryObjects contained in the GeometryElement - foreach (GeometryObject geomObj in currGeomElem) + IList splitVolumes = null; + try { - // Add try catch here because in a rare cases we find solid that throws exception/invalid solid.Faces - try + if (solid.Faces.Size < GeometryUtil.MaxFaceCountForSplitVolumes) { - Solid solid = geomObj as Solid; - if (solid != null && solid.Faces.Size > 0) - { - solidMeshCapsule.AddSolid(solid, containingElement); - } - else - { - Mesh mesh = geomObj as Mesh; - if (mesh != null) - { - solidMeshCapsule.AddMesh(mesh); - } - else - { - // if the current geomObj is castable as a GeometryInstance, then we perform the same collection on its symbol geometry - GeometryInstance inst = geomObj as GeometryInstance; + splitVolumes = SolidUtils.SplitVolumes(solid); - if (inst != null) - { - try - { - GeometryElement instanceSymbol = inst.GetSymbolGeometry(); - if (instanceSymbol != null && instanceSymbol.Count() != 0) - { - Transform instanceTransform = localTrf.Multiply(inst.Transform); - Element symbol = inst.GetDocument()?.GetElement(inst.GetSymbolGeometryId().SymbolId); - CollectSolidMeshGeometry(instanceSymbol, symbol, - instanceTransform, solidMeshCapsule); - } - } - catch - { - } - } - } + // Fall back to exporting just the original Solid if we got any Solids without volume + if (splitVolumes.Any(x => x.Volume < 0.0 || MathUtil.IsAlmostEqual(x.Volume, 0.0))) + throw new InvalidOperationException(); + + foreach (Solid currSolid in splitVolumes) + { + // The geometry element created by SplitVolumes is a copy which will have its own allocated + // membership - this needs to be stored and disposed of (see AllocatedGeometryObjectCache + // for details) + ExporterCacheManager.AllocatedGeometryObjectCache.AddGeometryObject(currSolid); } } - catch + } + catch + { + splitVolumes = null; + } + + if (splitVolumes == null) + { + // Split volumes can fail; in this case, we'll export the original solid. + splitVolumes = new List() { solid }; + } + + return splitVolumes; + } + + /// + /// Splits any solid volumes which consist of multiple closed bodies into individual solids (and updates the storage accordingly). + /// + public static void SplitSolids(SolidMeshGeometryInfo info) + { + IList splitSolidsList = new List(); + + foreach (SolidInfo solidInfo in info.SolidInfoList) + { + Element element = solidInfo.OwnerElement; + IList splitSolids = GeometryUtil.SplitVolumes(solidInfo.Solid); + foreach (Solid splitSolid in splitSolids) { + splitSolidsList.Add(new SolidInfo(splitSolid, element)); } } + + info.SolidInfoList = splitSolidsList; } /// @@ -1222,8 +1214,6 @@ private static CurveLoop GetOuterFaceBoundary(Face face, XYZ baseLoopOffset, boo int numBoundaries = faceEdges.Size; if (numBoundaries == 0) continue; - if (numBoundaries > 1) - throw new Exception("Can't handle faces with interior boundaries."); // In some cases the native function throws an exception, skip this face if it occurs ICollection generatingElementIds; @@ -2238,6 +2228,12 @@ private static IFCAnyHandle CreateBoundsIfNecessary(IFCFile file, IFCAnyHandle c /// The arc handle. public static IFCAnyHandle CreateArcSegment(ExporterIFC exporterIFC, Arc arc) { + double arcRadius = UnitUtil.ScaleLength(arc.Radius); + if (!IFCInstanceExporter.ValidateCircle(arcRadius)) + { + return null; + } + IFCFile file = exporterIFC.GetFile(); XYZ centerPoint = ExporterIFCUtils.TransformAndScalePoint(exporterIFC, arc.Center); @@ -2247,8 +2243,6 @@ public static IFCAnyHandle CreateArcSegment(ExporterIFC exporterIFC, Arc arc) XYZ xDirection = ExporterIFCUtils.TransformAndScaleVector(exporterIFC, arc.XDirection); IFCAnyHandle axis = ExporterUtil.CreateAxis2Placement3D(file, centerPoint, arc.Normal, xDirection); - double arcRadius = UnitUtil.ScaleLength(arc.Radius); - IFCAnyHandle circle = IFCInstanceExporter.CreateCircle(file, axis, arcRadius); return CreateBoundsIfNecessary(file, circle, arc); } @@ -2261,6 +2255,13 @@ public static IFCAnyHandle CreateArcSegment(ExporterIFC exporterIFC, Arc arc) /// The ellipse handle. public static IFCAnyHandle CreateEllipticalArcSegment(ExporterIFC exporterIFC, Ellipse ellipticalArc) { + double ellipseRadiusX = UnitUtil.ScaleLength(ellipticalArc.RadiusX); + double ellipseRadiusY = UnitUtil.ScaleLength(ellipticalArc.RadiusY); + if (!IFCInstanceExporter.ValidateEllipse(ellipseRadiusX, ellipseRadiusY)) + { + return null; + } + IFCFile file = exporterIFC.GetFile(); XYZ centerPoint = ExporterIFCUtils.TransformAndScalePoint(exporterIFC, ellipticalArc.Center); @@ -2270,9 +2271,6 @@ public static IFCAnyHandle CreateEllipticalArcSegment(ExporterIFC exporterIFC, E XYZ xDirection = ExporterIFCUtils.TransformAndScaleVector(exporterIFC, ellipticalArc.XDirection); IFCAnyHandle axis = ExporterUtil.CreateAxis2Placement3D(file, centerPoint, ellipticalArc.Normal, xDirection); - double ellipseRadiusX = UnitUtil.ScaleLength(ellipticalArc.RadiusX); - double ellipseRadiusY = UnitUtil.ScaleLength(ellipticalArc.RadiusY); - IFCAnyHandle ellipse = IFCInstanceExporter.CreateEllipse(file, axis, ellipseRadiusX, ellipseRadiusY); return CreateBoundsIfNecessary(file, ellipse, ellipticalArc); } @@ -2712,17 +2710,6 @@ public static double ComputePolygonalLoopArea(IList loop, XYZ normal, XYZ r return area / 2.0; } - /// - /// The maximum number of faces in a Solid before we decide not to split it. - /// Larger than this can cause sigificant performance issues. - /// - /// - /// Internal tests show perfectly good behavior at 1044 faces, so setting - /// this value based on that. This may be tweaked over time, or other - /// methods used instead. - /// - public static int MaxFaceCountForSplitVolumes = 2048; - /// /// Gets the volume of a solid, if it is possible. /// @@ -2740,49 +2727,6 @@ public static double ComputePolygonalLoopArea(IList loop, XYZ normal, XYZ r } } - /// - /// Splits a Solid into distinct volumes. - /// - /// The initial solid. - /// The list of volumes. - /// This calls the internal SolidUtils.SplitVolumes routine, but does additional cleanup work to properly dispose of stale data. - public static IList SplitVolumes(Solid solid) - { - IList splitVolumes = null; - try - { - if (solid.Faces.Size < GeometryUtil.MaxFaceCountForSplitVolumes) - { - splitVolumes = SolidUtils.SplitVolumes(solid); - - // Fall back to exporting just the original Solid if we got any Solids without volume - if (splitVolumes.Any(x => x.Volume < 0.0 || MathUtil.IsAlmostEqual(x.Volume, 0.0))) - throw new InvalidOperationException(); - - foreach (Solid currSolid in splitVolumes) - { - // The geometry element created by SplitVolumes is a copy which will have its own allocated - // membership - this needs to be stored and disposed of (see AllocatedGeometryObjectCache - // for details) - ExporterCacheManager.AllocatedGeometryObjectCache.AddGeometryObject(currSolid); - } - } - } - catch - { - splitVolumes = null; - } - - if (splitVolumes == null) - { - // Split volumes can fail; in this case, we'll export the original solid. - splitVolumes = new List(); - splitVolumes.Add(solid); - } - - return splitVolumes; - } - /// /// Creates IFC curve from curve loop. /// @@ -3698,15 +3642,16 @@ private static bool PointInsidePolygon(UV pnt, IList polyNodes) /// The file /// The exporter /// The curve that needs to convert to IFCCurve - /// indicates whether (TRUE) we want to convert "advanced" curve type - /// like Hermite or NURBS to IfcCurve or (FALSE) we want to tessellate them + /// If true, don't tessellate non-lines and non-arcs. /// A map of already created cartesian points, to avoid duplication. - /// True if we are trimming the generated curve, false otherwise. + /// An indication of how to create the curve. /// The handle representing the IFCCurve /// This cartesianPoints map caches certain 3D points computed by this function that are related to the /// curve, such as the start point of a line and the center of an arc. It uses the cached values when possible. - public static IFCAnyHandle CreateIFCCurveFromRevitCurve(IFCFile file, ExporterIFC exporterIFC, Curve curve, bool allowAdvancedCurve, - IDictionary cartesianPoints, bool useTrimmedCurve, Transform additionalTrf = null) + public static IFCAnyHandle CreateIFCCurveFromRevitCurve(IFCFile file, + ExporterIFC exporterIFC, Curve curve, bool allowAdvancedCurve, + IDictionary cartesianPoints, + TrimCurvePreference trimCurvePreference, Transform additionalTrf) { IFCAnyHandle ifcCurve = null; @@ -3733,24 +3678,27 @@ private static bool PointInsidePolygon(UV pnt, IList polyNodes) if (curve.IsBound) { Line curveLine = curve as Line; - //ifcCurve = CreateLineSegment(exporterIFC, curveLine); + if (trimCurvePreference == TrimCurvePreference.UsePolyLineOrTrim) + { + ifcCurve = CreateLineSegment(exporterIFC, curveLine); + } + else + { + // Create line based trimmed curve for Axis + IFCAnyHandle curveOrigin = XYZtoIfcCartesianPoint(exporterIFC, curveLine.Origin, cartesianPoints, additionalTrf); + XYZ dir = (additionalTrf == null) ? curveLine.Direction : additionalTrf.OfVector(curveLine.Direction); + IFCAnyHandle vector = VectorToIfcVector(exporterIFC, dir); + ifcCurve = IFCInstanceExporter.CreateLine(file, curveOrigin, vector); - // Create line based trimmed curve for Axis - IFCAnyHandle curveOrigin = XYZtoIfcCartesianPoint(exporterIFC, curveLine.Origin, cartesianPoints, additionalTrf); - XYZ dir = (additionalTrf == null) ? curveLine.Direction : additionalTrf.OfVector(curveLine.Direction); - IFCAnyHandle vector = VectorToIfcVector(exporterIFC, curveLine.Direction); - ifcCurve = IFCInstanceExporter.CreateLine(file, curveOrigin, vector); + if (trimCurvePreference == TrimCurvePreference.TrimmedCurve) + { + IFCAnyHandle startPoint = XYZtoIfcCartesianPoint(exporterIFC, curveLine.GetEndPoint(0), cartesianPoints, additionalTrf); + HashSet trim1 = new HashSet() { IFCData.CreateIFCAnyHandle(startPoint) }; + IFCAnyHandle endPoint = XYZtoIfcCartesianPoint(exporterIFC, curveLine.GetEndPoint(1), cartesianPoints, additionalTrf); + HashSet trim2 = new HashSet() { IFCData.CreateIFCAnyHandle(endPoint) }; - if (useTrimmedCurve) - { - IFCAnyHandle startPoint = XYZtoIfcCartesianPoint(exporterIFC, curveLine.GetEndPoint(0), cartesianPoints, additionalTrf); - HashSet trim1 = new HashSet(); - trim1.Add(IFCData.CreateIFCAnyHandle(startPoint)); - IFCAnyHandle endPoint = XYZtoIfcCartesianPoint(exporterIFC, curveLine.GetEndPoint(1), cartesianPoints, additionalTrf); - HashSet trim2 = new HashSet(); - trim2.Add(IFCData.CreateIFCAnyHandle(endPoint)); - - ifcCurve = IFCInstanceExporter.CreateTrimmedCurve(file, ifcCurve, trim1, trim2, true, IFCTrimmingPreference.Cartesian); + ifcCurve = IFCInstanceExporter.CreateTrimmedCurve(file, ifcCurve, trim1, trim2, true, IFCTrimmingPreference.Cartesian); + } } } } @@ -3758,6 +3706,12 @@ private static bool PointInsidePolygon(UV pnt, IList polyNodes) else if (curve is Arc) { Arc curveArc = curve as Arc; + double radius = UnitUtil.ScaleLength(curveArc.Radius); + if (!IFCInstanceExporter.ValidateCircle(radius)) + { + return null; + } + // Normal and x direction should be transformed to IFC coordinates before applying additional transform // arc center will be transformed later in XYZtoIfcCartesianPoint XYZ curveArcNormal = ExporterIFCUtils.TransformAndScaleVector(exporterIFC, curveArc.Normal); @@ -3784,17 +3738,15 @@ private static bool PointInsidePolygon(UV pnt, IList polyNodes) IFCAnyHandle refDirection = ExporterUtil.CreateDirection(file, curveArcXDirection); IFCAnyHandle position3D = IFCInstanceExporter.CreateAxis2Placement3D(file, location3D, axis, refDirection); - ifcCurve = IFCInstanceExporter.CreateCircle(file, position3D, UnitUtil.ScaleLength(curveArc.Radius)); + ifcCurve = IFCInstanceExporter.CreateCircle(file, position3D, radius); - if (useTrimmedCurve) + if (trimCurvePreference != TrimCurvePreference.BaseCurve && curve.IsBound) { IFCAnyHandle startPoint = XYZtoIfcCartesianPoint(exporterIFC, curveArc.GetEndPoint(0), cartesianPoints, additionalTrf); - HashSet trim1 = new HashSet(); - trim1.Add(IFCData.CreateIFCAnyHandle(startPoint)); + HashSet trim1 = new HashSet() { IFCData.CreateIFCAnyHandle(startPoint) }; IFCAnyHandle endPoint = XYZtoIfcCartesianPoint(exporterIFC, curveArc.GetEndPoint(1), cartesianPoints, additionalTrf); - HashSet trim2 = new HashSet(); - trim2.Add(IFCData.CreateIFCAnyHandle(endPoint)); + HashSet trim2 = new HashSet() { IFCData.CreateIFCAnyHandle(endPoint) }; ifcCurve = IFCInstanceExporter.CreateTrimmedCurve(file, ifcCurve, trim1, trim2, true, IFCTrimmingPreference.Cartesian); } @@ -3803,6 +3755,13 @@ private static bool PointInsidePolygon(UV pnt, IList polyNodes) else if (curve is Ellipse) { Ellipse curveEllipse = curve as Ellipse; + double semiAxis1 = UnitUtil.ScaleLength(curveEllipse.RadiusX); + double semiAxis2 = UnitUtil.ScaleLength(curveEllipse.RadiusY); + if (!IFCInstanceExporter.ValidateEllipse(semiAxis1, semiAxis2)) + { + return null; + } + // Normal and x direction should be transformed to IFC coordinates before applying additional transform XYZ ellipseNormal = ExporterIFCUtils.TransformAndScaleVector(exporterIFC, curveEllipse.Normal); XYZ ellipseXDirection = ExporterIFCUtils.TransformAndScaleVector(exporterIFC, curveEllipse.XDirection); @@ -3823,17 +3782,15 @@ private static bool PointInsidePolygon(UV pnt, IList polyNodes) IFCAnyHandle position = IFCInstanceExporter.CreateAxis2Placement3D(file, location3D, axis, refDirection); - ifcCurve = IFCInstanceExporter.CreateEllipse(file, position, UnitUtil.ScaleLength(curveEllipse.RadiusX), UnitUtil.ScaleLength(curveEllipse.RadiusY)); + ifcCurve = IFCInstanceExporter.CreateEllipse(file, position, semiAxis1, semiAxis2); - if (useTrimmedCurve) + if (trimCurvePreference != TrimCurvePreference.BaseCurve && curve.IsBound) { IFCAnyHandle startPoint = XYZtoIfcCartesianPoint(exporterIFC, curveEllipse.GetEndPoint(0), cartesianPoints, additionalTrf); - HashSet trim1 = new HashSet(); - trim1.Add(IFCData.CreateIFCAnyHandle(startPoint)); + HashSet trim1 = new HashSet() { IFCData.CreateIFCAnyHandle(startPoint) }; IFCAnyHandle endPoint = XYZtoIfcCartesianPoint(exporterIFC, curveEllipse.GetEndPoint(1), cartesianPoints, additionalTrf); - HashSet trim2 = new HashSet(); - trim2.Add(IFCData.CreateIFCAnyHandle(endPoint)); + HashSet trim2 = new HashSet() { IFCData.CreateIFCAnyHandle(endPoint) }; ifcCurve = IFCInstanceExporter.CreateTrimmedCurve(file, ifcCurve, trim1, trim2, true, IFCTrimmingPreference.Cartesian); } @@ -4614,10 +4571,14 @@ public static Transform GetSiteLocalPlacement(Document doc) using (SubTransaction projLocTr = new SubTransaction(doc)) { + projLocTr.Start(); doc.ActiveProjectLocation = projLocation; BasePoint surveyPoint = BasePoint.GetSurveyPoint(doc); BasePoint projectBasePoint = BasePoint.GetProjectBasePoint(doc); + if (surveyPoint == null || projectBasePoint == null) + return trf; + (double svNorthings, double svEastings, double svElevation, double svAngle, double pbNorthings, double pbEastings, double pbElevation, double pbAngle) = OptionsUtil.ProjectLocationInfo(doc, surveyPoint.Position, projectBasePoint.Position); @@ -4665,6 +4626,7 @@ public static Transform GetSiteLocalPlacement(Document doc) break; } } + projLocTr.RollBack(); } return trf; } diff --git a/Source/Revit.IFC.Export/Utility/IFCExportInfoPair.cs b/Source/Revit.IFC.Export/Utility/IFCExportInfoPair.cs index 392b037d..626a89b8 100644 --- a/Source/Revit.IFC.Export/Utility/IFCExportInfoPair.cs +++ b/Source/Revit.IFC.Export/Utility/IFCExportInfoPair.cs @@ -13,7 +13,13 @@ namespace Revit.IFC.Export.Utility public class IFCExportInfoPair { IFCEntityType m_ExportInstance = IFCEntityType.UnKnown; - + + IFCEntityType m_ExportType = IFCEntityType.UnKnown; + + private string m_PredefinedType = null; + + private string m_UserdefinedType = null; + /// /// The IfcEntity for export /// @@ -26,8 +32,6 @@ public IFCEntityType ExportInstance } } - IFCEntityType m_ExportType = IFCEntityType.UnKnown; - /// /// The type for export /// @@ -40,20 +44,25 @@ public IFCEntityType ExportType } } - private string m_ValidatedPredefinedType; /// /// Validated PredefinedType from IfcExportType (or IfcType for the old param), /// or from IFC_EXPORT_ELEMENT*_AS /// - public string ValidatedPredefinedType + public string PredefinedType { get { - return m_ValidatedPredefinedType; + return m_PredefinedType; } set { - string newValidatedPredefinedType = IFCValidateEntry.GetValidIFCPredefinedTypeType(value, "NOTDEFINED", m_ExportInstance.ToString()); + if (string.IsNullOrWhiteSpace(value)) + { + // always set to null if value is null or empty to make it possible indicate that PredefinedType is default + m_PredefinedType = null; + } + + string newValidatedPredefinedType = IFCValidateEntry.GetValidIFCPredefinedType(value, m_ExportInstance.ToString()); if (ExporterUtil.IsNotDefined(newValidatedPredefinedType)) { // if the ExportType is unknown, i.e. Entity without type (e.g. IfcGrid), @@ -61,29 +70,87 @@ public string ValidatedPredefinedType // there are exceptions. if (m_ExportType == IFCEntityType.UnKnown) { - newValidatedPredefinedType = - IFCValidateEntry.GetValidIFCPredefinedTypeType(value, "NOTDEFINED", - IfcSchemaEntityTree.GetTypeNameFromInstanceName(m_ExportInstance.ToString())); + newValidatedPredefinedType = IFCValidateEntry.GetValidIFCPredefinedType(value, IfcSchemaEntityTree.GetTypeNameFromInstanceName(m_ExportInstance.ToString())); } else { - newValidatedPredefinedType = - IFCValidateEntry.GetValidIFCPredefinedTypeType(value, "NOTDEFINED", - m_ExportType.ToString()); + newValidatedPredefinedType = IFCValidateEntry.GetValidIFCPredefinedType(value, m_ExportType.ToString()); } } - m_ValidatedPredefinedType = newValidatedPredefinedType; + m_PredefinedType = newValidatedPredefinedType; + } + } + + /// + /// Gets a value indicating whether the is default. + /// + public bool IsPredefinedTypeDefault + { + get { return string.IsNullOrWhiteSpace(m_PredefinedType); } + } + + /// + /// Retrieves the current , or the NOTDEFINED value + /// if the is default. + /// + /// + /// The value of the property if set; otherwise the NOTDEFINED value. + /// + public string GetPredefinedTypeOrDefault() + { + return GetPredefinedTypeOrDefault("NOTDEFINED"); + } + + /// + /// Retrieves the current , or the specified default value + /// if the is default. + /// + /// + /// A value to return if the is default, by default "NOTDEFINED". + /// + /// + /// The value of the property if set; + /// otherwise the parameter. + /// + public string GetPredefinedTypeOrDefault(string defaultPredefinedType) + { + if (IsPredefinedTypeDefault) + { + return defaultPredefinedType; + } + + return m_PredefinedType; + } + + /// + /// Set the property if property value is not initialized or "NOTDEFINED". + /// + /// A new predefined type value. + public void SetPredefinedTypeIfNotDefined(string predefinedType) + { + if (ExporterUtil.IsNotDefined(m_PredefinedType)) + { + PredefinedType = predefinedType; } } /// - /// Returns if the PreDefinedType is null or "NOTDEFINED". + /// The user-defined type, if the predefined type is set to USERDEFINED. /// - /// True if the PreDefinedType is null or "NOTDEFINED" (case-insensitive), or false otherwise. - public bool HasUndefinedPredefinedType() + public string UserDefinedType { - return (m_ValidatedPredefinedType == null) || - m_ValidatedPredefinedType.Equals("NOTDEFINED", StringComparison.InvariantCultureIgnoreCase); + get + { + if (string.Compare(PredefinedType, "USERDEFINED", StringComparison.InvariantCultureIgnoreCase) == 0) + { + return m_UserdefinedType; + } + return null; + } + set + { + m_UserdefinedType = value; + } } /// @@ -91,33 +158,33 @@ public bool HasUndefinedPredefinedType() /// public IFCExportInfoPair() { - m_ValidatedPredefinedType = null; } /// - /// Initialize the class with the entity and the type + /// Initialize the class with the entity and the type. /// - /// the entity - /// the type + /// The instance entity class. + /// The type entity class. public IFCExportInfoPair(IFCEntityType instance, IFCEntityType type, string predefinedType) { - instance = ElementFilteringUtil.GetValidIFCEntityType(instance); - m_ExportInstance = instance; - - type = ElementFilteringUtil.GetValidIFCEntityType(type); - m_ExportType = type; - - ValidatedPredefinedType = predefinedType; + SetValue(instance, type, predefinedType); } - public IFCExportInfoPair(IFCEntityType entity, string predefinedType = null) + /// + /// Initialize the class with the entity and optional predefinedType and userDefinedType.. + /// + /// The entity class. + /// The optional predefined type. + /// The optional user defined type. + public IFCExportInfoPair(IFCEntityType entity, string predefinedType = null, string userDefinedType = null) { - if (string.IsNullOrEmpty(predefinedType)) - ValidatedPredefinedType = null; - else - ValidatedPredefinedType = predefinedType; + if (!string.IsNullOrEmpty(predefinedType)) + PredefinedType = predefinedType; + + SetByTypeAndPredefinedType(entity, predefinedType); - SetValueWithPair(entity, predefinedType); + if (!string.IsNullOrEmpty(userDefinedType)) + UserDefinedType = userDefinedType; } /// @@ -125,7 +192,7 @@ public IFCExportInfoPair(IFCEntityType entity, string predefinedType = null) /// public bool IsUnKnown { - get { return (m_ExportInstance == IFCEntityType.UnKnown); } + get { return m_ExportInstance == IFCEntityType.UnKnown; } } /// @@ -149,34 +216,32 @@ public void SetValue(IFCEntityType instance, IFCEntityType type, string predefin type = ElementFilteringUtil.GetValidIFCEntityType(type); m_ExportType = type; - ValidatedPredefinedType = predefinedType; + PredefinedType = predefinedType; } /// - /// Set the pair information using only either the entity or the type + /// Set the export type info by given entity type. /// - /// the entity or type - /// predefinedtype string - public void SetValueWithPair(IFCEntityType entityType, string predefineType = null) + /// The entinty type. + public void SetByType(IFCEntityType entityType) { - SetValueWithPair(entityType.ToString(), predefineType); + SetByTypeName(entityType.ToString()); } /// - /// Set the pair information using only either the entity or the type + /// Set the export type info by given entity type name. /// - /// the entity or type string - /// predefinedtype string - public void SetValueWithPair(string entityTypeStr, string predefineType = null) + /// The entinty type name. + public void SetByTypeName(string entityTypeName) { IFCVersion ifcVersion = ExporterCacheManager.ExportOptionsCache.FileVersion; IfcSchemaEntityTree theTree = IfcSchemaEntityTree.GetEntityDictFor(ifcVersion); int typeLen = 4; - bool isType = entityTypeStr.Substring(entityTypeStr.Length - 4, 4).Equals("Type", StringComparison.CurrentCultureIgnoreCase); + bool isType = entityTypeName.EndsWith("Type", StringComparison.CurrentCultureIgnoreCase); if (!isType) { - if (entityTypeStr.Equals("IfcDoorStyle", StringComparison.InvariantCultureIgnoreCase) - || entityTypeStr.Equals("IfcWindowStyle", StringComparison.InvariantCultureIgnoreCase)) + if (entityTypeName.Equals("IfcDoorStyle", StringComparison.InvariantCultureIgnoreCase) + || entityTypeName.Equals("IfcWindowStyle", StringComparison.InvariantCultureIgnoreCase)) { isType = true; typeLen = 5; @@ -186,7 +251,7 @@ public void SetValueWithPair(string entityTypeStr, string predefineType = null) if (isType) { // Get the instance - string instName = entityTypeStr.Substring(0, entityTypeStr.Length - typeLen); + string instName = entityTypeName.Substring(0, entityTypeName.Length - typeLen); IfcSchemaEntityNode node = theTree.Find(instName); if (node != null && !node.isAbstract) { @@ -207,12 +272,12 @@ public void SetValueWithPair(string entityTypeStr, string predefineType = null) } // set the type - IFCEntityType entityType = ElementFilteringUtil.GetValidIFCEntityType(entityTypeStr); + IFCEntityType entityType = ElementFilteringUtil.GetValidIFCEntityType(entityTypeName); if (entityType != IFCEntityType.UnKnown) m_ExportType = entityType; else { - node = IfcSchemaEntityTree.FindNonAbsInstanceSuperType(ifcVersion, entityTypeStr); + node = IfcSchemaEntityTree.FindNonAbsInstanceSuperType(ifcVersion, entityTypeName); if (node != null) { IFCEntityType instType = IFCEntityType.UnKnown; @@ -224,13 +289,13 @@ public void SetValueWithPair(string entityTypeStr, string predefineType = null) else { // set the instance - IFCEntityType instType = ElementFilteringUtil.GetValidIFCEntityType(entityTypeStr); + IFCEntityType instType = ElementFilteringUtil.GetValidIFCEntityType(entityTypeName); if (instType != IFCEntityType.UnKnown) m_ExportInstance = instType; else { // If not found, try non-abstract supertype derived from the type - IfcSchemaEntityNode node = IfcSchemaEntityTree.FindNonAbsInstanceSuperType(ifcVersion, entityTypeStr); + IfcSchemaEntityNode node = IfcSchemaEntityTree.FindNonAbsInstanceSuperType(ifcVersion, entityTypeName); if (node != null) { instType = IFCEntityType.UnKnown; @@ -240,10 +305,10 @@ public void SetValueWithPair(string entityTypeStr, string predefineType = null) } // set the type pair - string typeName = entityTypeStr; + string typeName = entityTypeName; if (ExporterCacheManager.ExportOptionsCache.ExportAsOlderThanIFC4 && - (entityTypeStr.Equals("IfcDoor", StringComparison.InvariantCultureIgnoreCase) - || entityTypeStr.Equals("IfcWindow", StringComparison.InvariantCultureIgnoreCase))) + (entityTypeName.Equals("IfcDoor", StringComparison.InvariantCultureIgnoreCase) + || entityTypeName.Equals("IfcWindow", StringComparison.InvariantCultureIgnoreCase))) typeName += "Style"; else typeName += "Type"; @@ -256,7 +321,7 @@ public void SetValueWithPair(string entityTypeStr, string predefineType = null) { // If the type name is not found, likely it does not have the pair at this level, // needs to get the supertype of the instance to get the type pair - IList instNodes = IfcSchemaEntityTree.FindAllSuperTypes(ifcVersion, entityTypeStr, "IfcProduct", "IfcGroup"); + IList instNodes = IfcSchemaEntityTree.FindAllSuperTypes(ifcVersion, entityTypeName, "IfcProduct", "IfcGroup"); foreach (IfcSchemaEntityNode instNode in instNodes) { typeName = IfcSchemaEntityTree.GetTypeNameFromInstanceName(instNode.Name); @@ -276,8 +341,18 @@ public void SetValueWithPair(string entityTypeStr, string predefineType = null) } } } + } + + /// + /// Set the export type info by given entity type and predefined type. + /// + /// The entinty type. + /// The PredefinedType attribute value. + public void SetByTypeAndPredefinedType(IFCEntityType entityType, string predefinedTypeName) + { + SetByType(entityType); - ValidatedPredefinedType = predefineType; + PredefinedType = predefinedTypeName; } // Check valid entity and type set according to the MVD used in the export @@ -300,14 +375,14 @@ void CheckValidEntity() IFCEntityType newInst; if (Enum.TryParse(newInstanceName, true, out newInst)) //m_ExportInstance = newInst; - SetValueWithPair(newInst); + SetByType(newInst); } else if (m_ExportInstance.ToString().EndsWith("ElementedCase", StringComparison.InvariantCultureIgnoreCase)) { string newInstanceName = m_ExportInstance.ToString().Remove(m_ExportInstance.ToString().Length - 13); IFCEntityType newInst; if (Enum.TryParse(newInstanceName, true, out newInst)) - SetValueWithPair(newInst); + SetByType(newInst); } } diff --git a/Source/Revit.IFC.Export/Utility/LevelInfoCache.cs b/Source/Revit.IFC.Export/Utility/LevelInfoCache.cs index ef1f04e4..1031840d 100644 --- a/Source/Revit.IFC.Export/Utility/LevelInfoCache.cs +++ b/Source/Revit.IFC.Export/Utility/LevelInfoCache.cs @@ -18,6 +18,8 @@ // using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; using System.Linq; using Autodesk.Revit.DB; using Autodesk.Revit.DB.IFC; @@ -204,6 +206,33 @@ public void AddLevelInfo(ExporterIFC exporterIFC, ElementId levelId, IFCLevelInf exporterIFC.AddBuildingStorey(levelId, info); } + /// + /// Clears all caches. + /// + /// The Exporter object. + public void Clear(ExporterIFC exporterIFC) + { + // Clear data stored externally to LevelInfoCache. + if (exporterIFC != null) + { + ISet uniqueLevels = LevelsByElevation?.ToHashSet(); + if ((uniqueLevels?.Count ?? 0) > 0) + { + foreach (ElementId levelId in uniqueLevels) + { + exporterIFC.RemoveBuildingStorey(levelId); + } + } + } + + // Revert all caches back to original state. + m_ElementIdToLevelHeight?.Clear(); + m_BuildingStoriesByElevation?.Clear(); + m_LevelsByElevation?.Clear(); + m_OrphanedElements?.Clear(); + m_OrphanedSpaces?.Clear(); + FloorSlabEdgeLevels = null; + } /// /// Get the IFCLevelInfo corresponding to a level. diff --git a/Source/Revit.IFC.Export/Utility/MEPCache.cs b/Source/Revit.IFC.Export/Utility/MEPCache.cs index 99ce3283..61ebe448 100644 --- a/Source/Revit.IFC.Export/Utility/MEPCache.cs +++ b/Source/Revit.IFC.Export/Utility/MEPCache.cs @@ -41,9 +41,9 @@ public class MEPCache public List MEPConnectors = new List(); /// - /// A cache of elements (Ducts and Pipes) that may have coverings (Linings and/or Insulations). + /// A cache of elements (Ducts and Pipes) that may have coverings (Linings and/or Insulations) and their categories. /// - public HashSet CoveredElementsCache = new HashSet(); + public IDictionary CoveredElementsCache = new Dictionary(); /// /// A cache of elements (Cable Trays and Conduits) that may be assigned to systems diff --git a/Source/Revit.IFC.Export/Utility/MaterialLayerSetInfo.cs b/Source/Revit.IFC.Export/Utility/MaterialLayerSetInfo.cs index be5a4c9b..f778c624 100644 --- a/Source/Revit.IFC.Export/Utility/MaterialLayerSetInfo.cs +++ b/Source/Revit.IFC.Export/Utility/MaterialLayerSetInfo.cs @@ -15,21 +15,33 @@ public class MaterialInfo { public MaterialInfo(ElementId baseMatId, string layerName, double matWidth, MaterialFunctionAssignment function) { - m_baseMatId = baseMatId; - m_layerName = layerName; - m_matWidth = matWidth; - m_function = function; + BaseMatId = baseMatId; + LayerName = layerName; + ShapeAspectName = layerName; + Width = matWidth; + Function = function; } - public ElementId m_baseMatId; - public string m_layerName; - public double m_matWidth; - public MaterialFunctionAssignment m_function; + + public ElementId BaseMatId { get; private set; } = ElementId.InvalidElementId; + + public string LayerName { get; private set; } = null; + + public string ShapeAspectName { get; set; } = null; + + public double Width { get; set; } = 0.0; + + public MaterialFunctionAssignment Function { get; private set; } = MaterialFunctionAssignment.None; } - ExporterIFC m_ExporterIFC; - Element m_Element; - ProductWrapper m_ProductWrapper; - GeometryElement m_GeometryElement = null; - bool m_needToGenerateIFCObjects = false; + + private ExporterIFC ExporterIFC { get; set; } = null; + + private Element Element { get; set; } = null; + + private ProductWrapper ProductWrapper { get; set; } = null; + + private GeometryElement GeometryElement { get; set; } = null; + + bool NeedToGenerateIFCObjects { get; set; } = false; /// /// Initialize MaterialLayerSetInfo for element @@ -39,10 +51,10 @@ public MaterialInfo(ElementId baseMatId, string layerName, double matWidth, Mate /// the product wrapper public MaterialLayerSetInfo(ExporterIFC exporterIFC, Element element, ProductWrapper productWrapper, GeometryElement geometryElement = null) { - m_Element = element; - m_ExporterIFC = exporterIFC; - m_ProductWrapper = productWrapper; - m_GeometryElement = geometryElement; + Element = element; + ExporterIFC = exporterIFC; + ProductWrapper = productWrapper; + GeometryElement = geometryElement; CollectMaterialLayerSet(); } @@ -50,7 +62,7 @@ public void SingleMaterialOverride (ElementId materialId, double materialWidth) { GenerateIFCObjectsIfNeeded(); - Material material = m_Element.Document.GetElement(materialId) as Material; + Material material = Element.Document.GetElement(materialId) as Material; string layerName = "Layer"; if (material != null) { @@ -59,9 +71,9 @@ public void SingleMaterialOverride (ElementId materialId, double materialWidth) MaterialInfo matInfo = new MaterialInfo(materialId, layerName, materialWidth, MaterialFunctionAssignment.None); MaterialIds.Add(matInfo); - IFCAnyHandle singleMaterialOverrideHnd = IFCInstanceExporter.CreateMaterial(m_ExporterIFC.GetFile(), layerName, null, null); - ExporterCacheManager.MaterialHandleCache.Register(MaterialIds[0].m_baseMatId, singleMaterialOverrideHnd); - m_MaterialLayerSetHandle = singleMaterialOverrideHnd; + IFCAnyHandle singleMaterialOverrideHnd = IFCInstanceExporter.CreateMaterial(ExporterIFC.GetFile(), layerName, null, null); + ExporterCacheManager.MaterialHandleCache.Register(MaterialIds[0].BaseMatId, singleMaterialOverrideHnd); + m_GeneratedMaterialLayerSetHandle = singleMaterialOverrideHnd; } /// @@ -71,7 +83,7 @@ public bool IsEmpty { get { - return (m_MaterialLayerSetHandle == null && MaterialIds.Count == 0); + return (m_GeneratedMaterialLayerSetHandle == null && MaterialIds.Count == 0); } } @@ -85,14 +97,14 @@ public bool IsEmpty /// Warning: Do not call Properties inside class because this can break lazy generation of IFC objects. /// Use private members instead. /// - private IFCAnyHandle m_MaterialLayerSetHandle = null; + private IFCAnyHandle m_GeneratedMaterialLayerSetHandle = null; public IFCAnyHandle MaterialLayerSetHandle { get { GenerateIFCObjectsIfNeeded(); - return m_MaterialLayerSetHandle; + return m_GeneratedMaterialLayerSetHandle; } } @@ -101,14 +113,15 @@ public IFCAnyHandle MaterialLayerSetHandle /// Warning: Do not call Properties inside class because this can break lazy generation of IFC objects. /// Use private members instead. /// - private IFCAnyHandle m_PrimaryMaterialHandle = null; + private IFCAnyHandle m_GeneratedPrimaryMaterialHandle = null; + public IFCAnyHandle PrimaryMaterialHandle { get { GenerateIFCObjectsIfNeeded(); - return m_PrimaryMaterialHandle; + return m_GeneratedPrimaryMaterialHandle; } } @@ -117,14 +130,15 @@ public IFCAnyHandle PrimaryMaterialHandle /// Warning: Do not call Properties inside class because this can break lazy generation of IFC objects. /// Use private members instead. /// - private HashSet m_LayerQuantityWidthHnd = new HashSet(); + private HashSet m_GeneratedLayerQuantityWidthHnd = new HashSet(); + public HashSet LayerQuantityWidthHnd { get { GenerateIFCObjectsIfNeeded(); - return m_LayerQuantityWidthHnd; + return m_GeneratedLayerQuantityWidthHnd; } } @@ -141,10 +155,10 @@ public HashSet LayerQuantityWidthHnd /// private void CollectMaterialLayerSet() { - ElementId typeElemId = m_Element.GetTypeId(); + ElementId typeElemId = Element.GetTypeId(); IFCAnyHandle materialLayerSet = ExporterCacheManager.MaterialSetCache.FindLayerSet(typeElemId); // Roofs with no components are only allowed one material. We will arbitrarily choose the thickest material. - m_PrimaryMaterialHandle = ExporterCacheManager.MaterialSetCache.FindPrimaryMaterialHnd(typeElemId); + m_GeneratedPrimaryMaterialHandle = ExporterCacheManager.MaterialSetCache.FindPrimaryMaterialHnd(typeElemId); bool materialHandleIsNotValid = IFCAnyHandleUtil.IsNullOrHasNoValue(materialLayerSet); if (IFCAnyHandleUtil.IsNullOrHasNoValue(materialLayerSet) || materialHandleIsNotValid) @@ -153,23 +167,23 @@ private void CollectMaterialLayerSet() { UnregisterIFCHandles(); } - m_needToGenerateIFCObjects = true; + NeedToGenerateIFCObjects = true; List widths = new List(); List functions = new List(); - HostObjAttributes hostObjAttr = m_Element.Document.GetElement(typeElemId) as HostObjAttributes; + HostObjAttributes hostObjAttr = Element.Document.GetElement(typeElemId) as HostObjAttributes; if (hostObjAttr == null) { // It does not have the HostObjAttribute (where we will get the compound structure for material layer set. // We will define a single material instead and create the material layer set of this single material if there is enough information (At least Material id and thickness) - FamilyInstance familyInstance = m_Element as FamilyInstance; + FamilyInstance familyInstance = Element as FamilyInstance; if (familyInstance == null) { - if (m_GeometryElement != null) + if (GeometryElement != null) { - ElementId matId = BodyExporter.GetBestMaterialIdFromGeometryOrParameter(m_GeometryElement, m_Element); - CategoryUtil.CreateMaterialAssociation(m_ExporterIFC, m_ProductWrapper.GetAnElement(), matId); + ElementId matId = BodyExporter.GetBestMaterialIdFromGeometryOrParameter(GeometryElement, Element); + CategoryUtil.CreateMaterialAssociation(ExporterIFC, ProductWrapper.GetAnElement(), matId); } return; } @@ -179,8 +193,8 @@ private void CollectMaterialLayerSet() if (famMatIds.Count == 0) { // For some reason Plate type may not return any Material id - ElementId baseMatId = CategoryUtil.GetBaseMaterialIdForElement(m_Element); - Material material = m_Element.Document.GetElement(baseMatId) as Material; + ElementId baseMatId = CategoryUtil.GetBaseMaterialIdForElement(Element); + Material material = Element.Document.GetElement(baseMatId) as Material; if (material == null) return; @@ -209,10 +223,10 @@ private void CollectMaterialLayerSet() continue; widths.Add(matWidth); - ElementId baseMatId = CategoryUtil.GetBaseMaterialIdForElement(m_Element); + ElementId baseMatId = CategoryUtil.GetBaseMaterialIdForElement(Element); if (matid != ElementId.InvalidElementId) { - Material material = m_Element.Document.GetElement(matid) as Material; + Material material = Element.Document.GetElement(matid) as Material; if (material != null) { string layerName = NamingUtil.GetMaterialLayerName(material); @@ -230,13 +244,13 @@ private void CollectMaterialLayerSet() } else { - ElementId baseMatId = CategoryUtil.GetBaseMaterialIdForElement(m_Element); + ElementId baseMatId = CategoryUtil.GetBaseMaterialIdForElement(Element); CompoundStructure cs = hostObjAttr.GetCompoundStructure(); if (cs != null) { double scaledOffset = 0.0, scaledWallWidth = 0.0, wallHeight = 0.0; - Wall wall = m_Element as Wall; + Wall wall = Element as Wall; if (wall != null) { scaledWallWidth = UnitUtil.ScaleLength(wall.Width); @@ -262,7 +276,7 @@ private void CollectMaterialLayerSet() if (matId != ElementId.InvalidElementId) { - Material material = m_Element.Document.GetElement(matId) as Material; + Material material = Element.Document.GetElement(matId) as Material; if (material != null) { string layerName = NamingUtil.GetMaterialLayerName(material); @@ -282,7 +296,7 @@ private void CollectMaterialLayerSet() widths.Add(matWidth); if (baseMatId != ElementId.InvalidElementId) { - Material material = m_Element.Document.GetElement(baseMatId) as Material; + Material material = Element.Document.GetElement(baseMatId) as Material; if (material != null) { string layerName = NamingUtil.GetMaterialLayerName(material); @@ -296,16 +310,16 @@ private void CollectMaterialLayerSet() } else { - m_needToGenerateIFCObjects = false; + NeedToGenerateIFCObjects = false; - m_MaterialLayerSetHandle = materialLayerSet; + m_GeneratedMaterialLayerSetHandle = materialLayerSet; MaterialLayerSetInfo mlsInfo = ExporterCacheManager.MaterialSetCache.FindMaterialLayerSetInfo(typeElemId); if (mlsInfo != null) { MaterialIds = mlsInfo.MaterialIds; - m_PrimaryMaterialHandle = mlsInfo.PrimaryMaterialHandle; - m_LayerQuantityWidthHnd = mlsInfo.LayerQuantityWidthHnd; + m_GeneratedPrimaryMaterialHandle = mlsInfo.PrimaryMaterialHandle; + m_GeneratedLayerQuantityWidthHnd = mlsInfo.LayerQuantityWidthHnd; TotalThickness = mlsInfo.TotalThickness; } } @@ -315,16 +329,16 @@ private void CollectMaterialLayerSet() private void GenerateIFCObjectsIfNeeded() { - if (!m_needToGenerateIFCObjects) + if (!NeedToGenerateIFCObjects) return; - m_needToGenerateIFCObjects = false; + NeedToGenerateIFCObjects = false; IFCAnyHandle materialLayerSet = null; - if (m_ProductWrapper != null && !m_ProductWrapper.ToNative().IsValidObject) - m_ProductWrapper = null; + if (ProductWrapper != null && !ProductWrapper.ToNative().IsValidObject) + ProductWrapper = null; - m_ProductWrapper?.ClearFinishMaterials(); + ProductWrapper?.ClearFinishMaterials(); // We can't create IfcMaterialLayers without creating an IfcMaterialLayerSet. So we will simply collate here. IList materialHnds = new List(); @@ -333,59 +347,61 @@ private void GenerateIFCObjectsIfNeeded() for (int ii = 0; ii < MaterialIds.Count; ++ii) { // Require positive width for IFC2x3 and before, and non-negative width for IFC4. - if (MaterialIds[ii].m_matWidth < -MathUtil.Eps()) + if (MaterialIds[ii].Width < -MathUtil.Eps()) continue; - bool almostZeroWidth = MathUtil.IsAlmostZero(MaterialIds[ii].m_matWidth); + bool almostZeroWidth = MathUtil.IsAlmostZero(MaterialIds[ii].Width); if (ExporterCacheManager.ExportOptionsCache.ExportAsOlderThanIFC4 && almostZeroWidth) continue; if (almostZeroWidth) - MaterialIds[ii].m_matWidth = 0.0; + { + MaterialIds[ii].Width = 0.0; + } - IFCAnyHandle materialHnd = CategoryUtil.GetOrCreateMaterialHandle(m_ExporterIFC, MaterialIds[ii].m_baseMatId); - if (m_PrimaryMaterialHandle == null || (MaterialIds[ii].m_matWidth > thickestLayer)) + IFCAnyHandle materialHnd = CategoryUtil.GetOrCreateMaterialHandle(ExporterIFC, MaterialIds[ii].BaseMatId); + if (m_GeneratedPrimaryMaterialHandle == null || (MaterialIds[ii].Width > thickestLayer)) { - m_PrimaryMaterialHandle = materialHnd; - thickestLayer = MaterialIds[ii].m_matWidth; + m_GeneratedPrimaryMaterialHandle = materialHnd; + thickestLayer = MaterialIds[ii].Width; } widthIndices.Add(ii); materialHnds.Add(materialHnd); - if ((m_ProductWrapper != null) && (MaterialIds[ii].m_function == MaterialFunctionAssignment.Finish1 || MaterialIds[ii].m_function == MaterialFunctionAssignment.Finish2)) + if ((ProductWrapper != null) && (MaterialIds[ii].Function == MaterialFunctionAssignment.Finish1 || MaterialIds[ii].Function == MaterialFunctionAssignment.Finish2)) { - m_ProductWrapper.AddFinishMaterial(materialHnd); + ProductWrapper.AddFinishMaterial(materialHnd); } } int numLayersToCreate = widthIndices.Count; if (numLayersToCreate == 0) { - m_MaterialLayerSetHandle = materialLayerSet; + m_GeneratedMaterialLayerSetHandle = materialLayerSet; return; } // If it is a single material, check single material override (only IfcMaterial without IfcMaterialLayerSet with only 1 member) if (numLayersToCreate == 1 && ExporterCacheManager.ExportOptionsCache.ExportAs4ReferenceView) { - m_MaterialLayerSetHandle = ExporterUtil.GetSingleMaterial(m_ExporterIFC, m_Element, - MaterialIds[0].m_baseMatId) ?? materialHnds[0]; + m_GeneratedMaterialLayerSetHandle = ExporterUtil.GetSingleMaterial(ExporterIFC, Element, + MaterialIds[0].BaseMatId) ?? materialHnds[0]; return; } - IFCFile file = m_ExporterIFC.GetFile(); + IFCFile file = ExporterIFC.GetFile(); Document document = ExporterCacheManager.Document; IList layers = new List(numLayersToCreate); IList> layerWidthQuantities = new List>(); - var uniqueNames = new Dictionary<(string, double), string>(new NameAndWidthComparer()); + HashSet layerNameUsed = new HashSet(); double totalWidth = 0.0; for (int ii = 0; ii < numLayersToCreate; ii++) { int widthIndex = widthIndices[ii]; - double scaledWidth = UnitUtil.ScaleLength(MaterialIds[widthIndex].m_matWidth); + double scaledWidth = UnitUtil.ScaleLength(MaterialIds[widthIndex].Width); string layerName = "Layer"; string description = null; @@ -395,7 +411,7 @@ private void GenerateIFCObjectsIfNeeded() IFCLogical? isVentilated = null; int isVentilatedValue; - Material material = document.GetElement(MaterialIds[ii].m_baseMatId) as Material; + Material material = document.GetElement(MaterialIds[ii].BaseMatId) as Material; if (material != null) { if (ParameterUtil.GetIntValueFromElement(material, "IfcMaterialLayer.IsVentilated", out isVentilatedValue) != null) @@ -408,14 +424,11 @@ private void GenerateIFCObjectsIfNeeded() if (!ExporterCacheManager.ExportOptionsCache.ExportAsOlderThanIFC4) { - layerName = MaterialIds[ii].m_layerName; + layerName = MaterialIds[ii].ShapeAspectName; if (string.IsNullOrEmpty(layerName)) layerName = "Layer"; - // Ensure layer name is unique for different layer widths - layerName = GetUniqueMaterialNameWithWidth(layerName, scaledWidth, uniqueNames); - - description = NamingUtil.GetOverrideStringValue(material, "IfcMaterialLayer.Description", + description = NamingUtil.GetOverrideStringValue(material, "IfcMaterialLayer.Description", IFCAnyHandleUtil.GetStringAttribute(materialHnds[ii], "Description")); category = NamingUtil.GetOverrideStringValue(material, "IfcMaterialLayer.Category", IFCAnyHandleUtil.GetStringAttribute(materialHnds[ii], "Category")); @@ -447,7 +460,7 @@ private void GenerateIFCObjectsIfNeeded() } } - ElementId typeElemId = m_Element.GetTypeId(); + ElementId typeElemId = Element.GetTypeId(); if (layers.Count > 0) { ElementType type = document.GetElement(typeElemId) as ElementType; @@ -458,41 +471,41 @@ private void GenerateIFCObjectsIfNeeded() if (ExporterCacheManager.ExportOptionsCache.ExportAs4ReferenceView) { HashSet constituents = new HashSet(layers); - m_MaterialLayerSetHandle = CategoryUtil.GetOrCreateMaterialConstituentSet(file, + m_GeneratedMaterialLayerSetHandle = CategoryUtil.GetOrCreateMaterialConstituentSet(file, typeElemId, null, constituents, layerSetName, layerSetDesc); foreach (Tuple layerWidthQty in layerWidthQuantities) { - m_LayerQuantityWidthHnd.Add(IFCInstanceExporter.CreatePhysicalComplexQuantity(file, layerWidthQty.Item1, null, + m_GeneratedLayerQuantityWidthHnd.Add(IFCInstanceExporter.CreatePhysicalComplexQuantity(file, layerWidthQty.Item1, null, new HashSet() { layerWidthQty.Item2 }, "Layer", null, null)); } // Finally create the total width as a quantity - m_LayerQuantityWidthHnd.Add(IFCInstanceExporter.CreateQuantityLength(file, "Width", null, null, totalWidth)); + m_GeneratedLayerQuantityWidthHnd.Add(IFCInstanceExporter.CreateQuantityLength(file, "Width", null, null, totalWidth)); } else { - m_MaterialLayerSetHandle = IFCInstanceExporter.CreateMaterialLayerSet(file, layers, layerSetName, layerSetDesc); + m_GeneratedMaterialLayerSetHandle = IFCInstanceExporter.CreateMaterialLayerSet(file, layers, layerSetName, layerSetDesc); } - ExporterCacheManager.MaterialSetCache.RegisterLayerSet(typeElemId, m_MaterialLayerSetHandle, this); + ExporterCacheManager.MaterialSetCache.RegisterLayerSet(typeElemId, m_GeneratedMaterialLayerSetHandle, this); } - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(m_PrimaryMaterialHandle)) - ExporterCacheManager.MaterialSetCache.RegisterPrimaryMaterialHnd(typeElemId, m_PrimaryMaterialHandle); + if (!IFCAnyHandleUtil.IsNullOrHasNoValue(m_GeneratedPrimaryMaterialHandle)) + ExporterCacheManager.MaterialSetCache.RegisterPrimaryMaterialHnd(typeElemId, m_GeneratedPrimaryMaterialHandle); } private void UnregisterIFCHandles() { - ElementId typeElemId = m_Element.GetTypeId(); + ElementId typeElemId = Element.GetTypeId(); ExporterCacheManager.MaterialSetCache.UnregisterLayerSet(typeElemId); ExporterCacheManager.MaterialSetCache.UnregisterPrimaryMaterialHnd(typeElemId); - if (m_ProductWrapper != null) - m_ProductWrapper.ClearFinishMaterials(); + if (ProductWrapper != null) + ProductWrapper.ClearFinishMaterials(); - m_MaterialLayerSetHandle = null; - m_PrimaryMaterialHandle = null; - m_LayerQuantityWidthHnd.Clear(); + m_GeneratedMaterialLayerSetHandle = null; + m_GeneratedPrimaryMaterialHandle = null; + m_GeneratedLayerQuantityWidthHnd.Clear(); } public enum CompareTwoLists @@ -518,41 +531,5 @@ public static CompareTwoLists CompareMaterialInfoList(IList materialL return CompareTwoLists.ListsUnequal; } - - public class NameAndWidthComparer : IEqualityComparer<(string, double)> - { - public bool Equals((string, double) tup1, (string, double) tup2) - { - return tup1.Item1.Equals(tup2.Item1, StringComparison.Ordinal) && MathUtil.IsAlmostEqual(tup1.Item2, tup2.Item2); - } - - public int GetHashCode((string, double) tup) - { - int hashCode = tup.Item1.GetHashCode() ^ tup.Item2.GetHashCode(); - return hashCode; - } - } - - /// - /// Returns the unique name for material name and width pair checking within the given collection. - /// - /// the input name - /// the input width - /// the collection where the name should be search - /// the unique name - public static string GetUniqueMaterialNameWithWidth(string originalName, double width, IDictionary<(string name, double width), string> uniqueNames) - { - if (originalName == null) - return null; - - string uniqueName = string.Empty; - if (uniqueNames.TryGetValue((originalName, width), out uniqueName)) - return uniqueName; - - string newName = NamingUtil.GetUniqueNameWithinSet(originalName, uniqueNames.Values.ToHashSet()); - uniqueNames.Add((originalName, width), newName); - - return newName; - } } } diff --git a/Source/Revit.IFC.Export/Utility/NamingUtil.cs b/Source/Revit.IFC.Export/Utility/NamingUtil.cs index 9a421780..d0b889af 100644 --- a/Source/Revit.IFC.Export/Utility/NamingUtil.cs +++ b/Source/Revit.IFC.Export/Utility/NamingUtil.cs @@ -371,10 +371,11 @@ public static string GetObjectTypeOverride(Element element, string originalValue // ObjectType attribute will be overridden by that value, allowing ObjectType is set according to the Type setting // such as IfcExportType="USERDEFINED" set in the Type which necessitates ObjectType in the instance to be set to // the appropriate value + Element typeOrSymbol = null; if (string.IsNullOrEmpty(overrideValue) || (!string.IsNullOrEmpty(overrideValue) && overrideValue.Equals(originalValue))) { - Element typeOrSymbol = element.Document.GetElement(element.GetTypeId()) as ElementType; ; + typeOrSymbol = element.Document.GetElement(element.GetTypeId()) as ElementType; if (typeOrSymbol == null) { FamilyInstance famInst = element as FamilyInstance; @@ -388,6 +389,21 @@ public static string GetObjectTypeOverride(Element element, string originalValue overrideValue = GetOverrideStringValue(typeOrSymbol, objectTypeOverride, originalValue); } } + + if (string.IsNullOrEmpty(overrideValue) + || (!string.IsNullOrEmpty(overrideValue) && overrideValue.Equals(originalValue))) + { + Document document = element.Document; + ElementId categoryId = element?.Category?.Id ?? ElementId.InvalidElementId; + CustomSubCategoryId customSubCategoryId = + ExporterUtil.WallFunctionToCustomSubCategoryId((typeOrSymbol as WallType)?.Function); + ExportIFCCategoryInfo info = ExporterCacheManager.CategoryMappingTemplate.GetMappingInfoById(document, categoryId, customSubCategoryId); + if (string.Compare(info?.IFCPredefinedType ?? string.Empty, "USERDEFINED", true) == 0) + { + overrideValue = info.IFCUserDefinedType; + } + } + //GetOverrideStringValue will return the override value from the parameter specified, otherwise it will return the originalValue return overrideValue; } diff --git a/Source/Revit.IFC.Export/Utility/OpeningUtil.cs b/Source/Revit.IFC.Export/Utility/OpeningUtil.cs index 9be4c61e..59a65aa1 100644 --- a/Source/Revit.IFC.Export/Utility/OpeningUtil.cs +++ b/Source/Revit.IFC.Export/Utility/OpeningUtil.cs @@ -152,7 +152,7 @@ class OpeningUtil Transform offsetTransform, ExporterIFC exporterIFC, IFCAnyHandle originalPlacement, PlacementSetter setter, ProductWrapper wrapper) { - if (IFCAnyHandleUtil.IsNullOrHasNoValue(elementHandle)) + if (IFCAnyHandleUtil.IsNullOrHasNoValue(elementHandle) || extraParams == null) return; ElementId categoryId = CategoryUtil.GetSafeCategoryId(element); @@ -268,7 +268,7 @@ public static bool NeedToCreateOpenings(IFCAnyHandle elementHandle, IFCExportBod // including a line as part of the elevation profile of the wall. // As such, we will restrict which element types we check for CanExportElement. if ((openingElem is WallSweep) && - (!ElementFilteringUtil.CanExportElement(exporterIFC, openingElem, true))) + (!ElementFilteringUtil.CanExportElement(openingElem, true))) continue; IList extrusionDataList = openingData.GetExtrusionData(); @@ -292,7 +292,7 @@ public static bool NeedToCreateOpenings(IFCAnyHandle elementHandle, IFCExportBod element.Id, parentHandle, setter.LevelId); if (delayedCreator != null) { - ExporterCacheManager.DoorWindowDelayedOpeningCreatorCache.Add(delayedCreator); + ExporterCacheManager.DoorWindowDelayedOpeningCreatorCache.Add(delayedCreator, true); continue; } } diff --git a/Source/Revit.IFC.Export/Utility/ParamExprResolverParser.g4 b/Source/Revit.IFC.Export/Utility/ParamExprResolverParser.g4 index b768ab23..b52c274e 100644 --- a/Source/Revit.IFC.Export/Utility/ParamExprResolverParser.g4 +++ b/Source/Revit.IFC.Export/Utility/ParamExprResolverParser.g4 @@ -5,8 +5,10 @@ grammar ParamExprResolverParser; @lexer::members { - public static int WHITESPACE = 1; - public static int COMMENTS = 2; +// NOTE: If WHITESPACE is changed from 1, please also change the following line below: +// WS: [ \t\n\r]+ -> channel(1) ; +// This is hardwired to 1 (instead of WHITESPACE) to avoid a compiler warning. + public const int WHITESPACE = 1; } /* @@ -20,10 +22,12 @@ expr: value | expr ops expr | '(' expr ')' (power_op)? ; -param_name: NAMEWITHSPECIALCHAR+; +param_name: NAMEWITHSPECIALCHAR_EXPLICIT+; unary_operator: '+' | '-' ; ops: MULTIPLY | DIVIDE | ADDITION | SUBTRACT ; -power_op: '^' ( '-' | '+' )? INT; +INT_DIGIT: INT; +NAMEWITHSPECIALCHAR_EXPLICIT: NAMEWITHSPECIALCHAR; +power_op: '^' ( '-' | '+' )? INT_DIGIT; value: realliteral | stringliteral ; stringliteral: STRING ; realliteral: signed_number ; @@ -42,15 +46,23 @@ DIVIDE: '/'; ADDITION: '+'; SUBTRACT: '-'; -STRING : ( . )*? ; +STRING : (['] (ESC | .)*? [']) + | (["] (ESC | .)*? ["]); NUMBER: INT '.' INT? EXP? // 1.35, 1.35E-9, 0.3 | '.' INT EXP? // .2, .2e-9 | INT EXP? // 1e10 | INT // 45 ; fragment ALPHANUMERIC: [a-zA-Z0-9_] ; -fragment NAMEWITHSPECIALCHAR: [a-zA-Z0-9&*%^$#@!_=+-/.,"']; +fragment NAMEWITHSPECIALCHAR: [a-zA-Z0-9&*%^$#@!_=+,-./"']; + fragment INT: [0] | [0-9] [0-9]* ; -fragment EXP: [Ee] [+\-]? INT ; +fragment EXP: [Ee] [+\-]? INT ; +fragment ESC: '\\' (["\\/bfnrt] | UNICODE) ; +fragment UNICODE : 'u' HEX HEX HEX HEX ; +fragment HEX : [0-9a-fA-F] ; -WS: [ \t\n\r]+ -> channel(WHITESPACE) ; \ No newline at end of file +// NOTE: If WHITESPACE is changed from 1, please also change the following line below: +// WS: [ \t\n\r]+ -> channel(1) ; +// This is hardwired to 1 (instead of WHITESPACE) to avoid a compiler warning. +WS: [ \t\n\r]+ -> channel(1) ; \ No newline at end of file diff --git a/Source/Revit.IFC.Export/Utility/ParameterUtil.cs b/Source/Revit.IFC.Export/Utility/ParameterUtil.cs index 935e61dc..d6c2fc47 100644 --- a/Source/Revit.IFC.Export/Utility/ParameterUtil.cs +++ b/Source/Revit.IFC.Export/Utility/ParameterUtil.cs @@ -27,8 +27,44 @@ namespace Revit.IFC.Export.Utility /// /// Provides static methods for parameter related manipulations. /// - class ParameterUtil + public class ParameterUtil { + /// + /// The information needed to create an IFC property. + /// + public class PropertyDescription + { + /// + /// Create a property description with only a name. + /// + /// The name of the property. + public PropertyDescription(string name) + { + Name = name; + } + + /// + /// Create a property description with only a name. + /// + /// The name of the property. + /// The description of the property. + public PropertyDescription(string name, string description) + { + Name = name; + Description = description; + } + + /// + /// The required name. + /// + public string Name { get; set; } = null; + + /// + /// The optional description. + /// + public string Description { get; set; } = null; + }; + // Cache the parameters for the current Element. private static IDictionary> m_NonIFCParameters = new Dictionary>(); diff --git a/Source/Revit.IFC.Export/Utility/ProductWrapper.cs b/Source/Revit.IFC.Export/Utility/ProductWrapper.cs index 62068357..36819032 100644 --- a/Source/Revit.IFC.Export/Utility/ProductWrapper.cs +++ b/Source/Revit.IFC.Export/Utility/ProductWrapper.cs @@ -73,6 +73,13 @@ protected ProductWrapper(ExporterIFC exporterIFC) ExporterCacheManager.ElementToHandleCache.Register(element.Id, handle, exportType); ExporterCacheManager.HandleToElementCache.Register(handle, element.Id); + + if (SpatialElementExporter.IsZoneCompatible(exportType)) + { + IFCFile file = ExporterIFC.GetFile(); + SpatialElementExporter.CreateZoneInfos(file, element, handle); + SpatialElementExporter.CreateSpaceOccupantInfo(file, element, handle); + } } /// @@ -172,29 +179,6 @@ public bool IsEmpty() return ((CreatedHandles.Count == 0) && (InternalWrapper.Count == 0)); } - /// - /// Gets the first handle of a particular type, or null if none exists. - /// - /// The entity type. - /// The handle, or null. - public IFCAnyHandle GetElementOfType(IFCEntityType type) - { - foreach (IFCAnyHandle handle in CreatedHandles) - { - if (IFCAnyHandleUtil.IsSubTypeOf(handle, type)) - return handle; - } - - ICollection internalObjects = InternalWrapper.GetAllObjects(); - foreach (IFCAnyHandle handle in internalObjects) - { - if (IFCAnyHandleUtil.IsSubTypeOf(handle, type)) - return handle; - } - - return null; - } - /// /// Get all handles in the wrapper. /// @@ -211,8 +195,7 @@ public ISet GetAllObjects() // of disposal of entities, and in general a move to .NET only created entities. foreach (IFCAnyHandle internalObject in internalObjects) { - if (!IFCAnyHandleUtil.IsNullOrHasNoValue(internalObject)) - allObjects.Add(internalObject); + allObjects.AddIfNotNull(internalObject); } allObjects.UnionWith(CreatedHandles); diff --git a/Source/Revit.IFC.Export/Utility/PropertyInfoCache.cs b/Source/Revit.IFC.Export/Utility/PropertyInfoCache.cs index 9c1a7651..32c0e02a 100644 --- a/Source/Revit.IFC.Export/Utility/PropertyInfoCache.cs +++ b/Source/Revit.IFC.Export/Utility/PropertyInfoCache.cs @@ -174,174 +174,20 @@ public StringPropertyInfoCache IdentifierCache } } - /// - /// The DoublePropertyInfoCache object for Real parameter types. - /// - public DoublePropertyInfoCache RealCache - { - get - { - DoublePropertyInfoCache realPropertyInfoCache; - if (!DoubleCacheMap.TryGetValue(PropertyType.Real, out realPropertyInfoCache)) - { - realPropertyInfoCache = new DoublePropertyInfoCache(); - DoubleCacheMap[PropertyType.Real] = realPropertyInfoCache; - } - return realPropertyInfoCache; - } - } - - /// - /// The DoublePropertyInfoCache object for Numeric parameter types. - /// - public DoublePropertyInfoCache NumericCache - { - get - { - DoublePropertyInfoCache numericPropertyInfoCache; - if (!DoubleCacheMap.TryGetValue(PropertyType.Numeric, out numericPropertyInfoCache)) - { - numericPropertyInfoCache = new DoublePropertyInfoCache(); - DoubleCacheMap[PropertyType.Numeric] = numericPropertyInfoCache; - } - return numericPropertyInfoCache; - } - } - - /// - /// The DoublePropertyInfoCache object for LengthMeasure parameter types. - /// - public DoublePropertyInfoCache LengthMeasureCache - { - get - { - DoublePropertyInfoCache lengthMeasurePropertyInfoCache; - if (!DoubleCacheMap.TryGetValue(PropertyType.Length, out lengthMeasurePropertyInfoCache)) - { - lengthMeasurePropertyInfoCache = new DoublePropertyInfoCache(); - DoubleCacheMap[PropertyType.Length] = lengthMeasurePropertyInfoCache; - } - return lengthMeasurePropertyInfoCache; - } - } /// - /// The DoublePropertyInfoCache object for PlaneAngle parameter types. + /// Get DoublePropertyInfoCache object for the particular type /// - public DoublePropertyInfoCache PlaneAngleCache + public DoublePropertyInfoCache GetDoubleChache(PropertyType propertyType) { - get + DoublePropertyInfoCache doublePropertyInfoCache; + if (!DoubleCacheMap.TryGetValue(propertyType, out doublePropertyInfoCache)) { - DoublePropertyInfoCache planeAngleCache; - if (!DoubleCacheMap.TryGetValue(PropertyType.PlaneAngle, out planeAngleCache)) - { - planeAngleCache = new DoublePropertyInfoCache(); - DoubleCacheMap[PropertyType.PlaneAngle] = planeAngleCache; - } - return planeAngleCache; + doublePropertyInfoCache = new DoublePropertyInfoCache(); + DoubleCacheMap[propertyType] = doublePropertyInfoCache; } + return doublePropertyInfoCache; } - /// - /// The DoublePropertyInfoCache object for ElectricalCurrent parameter types. - /// - public DoublePropertyInfoCache ElectricCurrentCache - { - get - { - DoublePropertyInfoCache electricalCurrentCache; - if (!DoubleCacheMap.TryGetValue(PropertyType.ElectricCurrent, out electricalCurrentCache)) - { - electricalCurrentCache = new DoublePropertyInfoCache(); - DoubleCacheMap[PropertyType.ElectricCurrent] = electricalCurrentCache; - } - return electricalCurrentCache; - } - } - - /// - /// The DoublePropertyInfoCache object for ElectricalVoltage parameter types. - /// - public DoublePropertyInfoCache ElectricVoltageCache - { - get - { - DoublePropertyInfoCache electricalVoltageCache; - if (!DoubleCacheMap.TryGetValue(PropertyType.ElectricVoltage, out electricalVoltageCache)) - { - electricalVoltageCache = new DoublePropertyInfoCache(); - DoubleCacheMap[PropertyType.ElectricVoltage] = electricalVoltageCache; - } - return electricalVoltageCache; - } - } - - /// - /// The DoublePropertyInfoCache object for PositivePlaneAngle parameter types. - /// - public DoublePropertyInfoCache PositivePlaneAngleCache - { - get - { - DoublePropertyInfoCache planeAngleCache; - if (!DoubleCacheMap.TryGetValue(PropertyType.PlaneAngle, out planeAngleCache)) - { - planeAngleCache = new DoublePropertyInfoCache(); - DoubleCacheMap[PropertyType.PositivePlaneAngle] = planeAngleCache; - } - return planeAngleCache; - } - } - - /// - /// The DoublePropertyInfoCache object for ThermodynamicTemperature parameter types. - /// - public DoublePropertyInfoCache ThermodynamicTemperatureCache - { - get - { - DoublePropertyInfoCache thermodynamicTemperaturePropertyInfoCache; - if (!DoubleCacheMap.TryGetValue(PropertyType.ThermodynamicTemperature, out thermodynamicTemperaturePropertyInfoCache)) - { - thermodynamicTemperaturePropertyInfoCache = new DoublePropertyInfoCache(); - DoubleCacheMap[PropertyType.ThermodynamicTemperature] = thermodynamicTemperaturePropertyInfoCache; - } - return thermodynamicTemperaturePropertyInfoCache; - } - } - - /// - /// The DoublePropertyInfoCache object for ThermalTransmittance parameter types. - /// - public DoublePropertyInfoCache ThermalTransmittanceCache - { - get - { - DoublePropertyInfoCache thermalTransmittancePropertyInfoCache; - if (!DoubleCacheMap.TryGetValue(PropertyType.ThermalTransmittance, out thermalTransmittancePropertyInfoCache)) - { - thermalTransmittancePropertyInfoCache = new DoublePropertyInfoCache(); - DoubleCacheMap[PropertyType.ThermalTransmittance] = thermalTransmittancePropertyInfoCache; - } - return thermalTransmittancePropertyInfoCache; - } - } - - /// - /// The DoublePropertyInfoCache object for Power parameter types. - /// - public DoublePropertyInfoCache PowerCache - { - get - { - DoublePropertyInfoCache powerPropertyInfoCache; - if (!DoubleCacheMap.TryGetValue(PropertyType.Power, out powerPropertyInfoCache)) - { - powerPropertyInfoCache = new DoublePropertyInfoCache(); - DoubleCacheMap[PropertyType.Power] = powerPropertyInfoCache; - } - return powerPropertyInfoCache; - } - } } } diff --git a/Source/Revit.IFC.Export/Utility/PropertyMap.cs b/Source/Revit.IFC.Export/Utility/PropertyMap.cs index 760c7188..82b45d88 100644 --- a/Source/Revit.IFC.Export/Utility/PropertyMap.cs +++ b/Source/Revit.IFC.Export/Utility/PropertyMap.cs @@ -19,73 +19,248 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; using System.IO; -using System.Text.RegularExpressions; -using GeometryGym.Ifc; +using Autodesk.Revit.DB; +using Revit.IFC.Export.Exporter.PropertySet; namespace Revit.IFC.Export.Utility { /// - /// Type or Instance enum + /// Represents the Revit parameter to get from element. /// - public enum TypeOrInstance + class UserDefinedPropertyRevitParameter { - Type, - Instance - } + /// + /// Gets Revit . + /// + public BuiltInParameter BuiltInParameter { get; private set; } = BuiltInParameter.INVALID; - /// - /// PropertSet definition struct - /// - public struct PropertySetDef - { - public string propertySetName; - public string propertySetDescription; - public IList propertyDefs; - public TypeOrInstance applicableTo; - public IList applicableElements; + /// + /// Gets custom Revit paramater name. + /// + public string RevitParameter { get; private set; } = null; + + /// + /// Gets a value indicating whether the Revit built-in parameter specified. + /// + public bool IsBuiltInParameterDefined { get; private set; } = false; + + /// + /// Creates a new revit parameter representation instance for the given raw definition from the config. + /// + /// Data to parse. + /// A new instance of . + public static UserDefinedPropertyRevitParameter Create(string rawParameter) + { + if (string.IsNullOrWhiteSpace(rawParameter)) + return null; + + UserDefinedPropertyRevitParameter parameter = new UserDefinedPropertyRevitParameter(); + const string prefix = "BuiltInParameter."; + if (rawParameter.StartsWith(prefix, StringComparison.InvariantCultureIgnoreCase)) + { + parameter.IsBuiltInParameterDefined = true; + string builtinParameterName = rawParameter.Substring(prefix.Length); + if (Enum.TryParse(builtinParameterName, out BuiltInParameter builtInParameter) && builtInParameter != BuiltInParameter.INVALID) + { + parameter.BuiltInParameter = builtInParameter; + } + } + else + { + parameter.RevitParameter = rawParameter; + } + + return parameter; + } } /// - /// Property + /// Represents a single property definiton from the config. /// - public class PropertyDef + class UserDefinedProperty { - public string PropertyName; - public string PropertyDataType; - public List ParameterDefinitions = new List(); - //public GeometryGym.Ifc.IfcValue DefaultValue = null; - public PropertyDef(string propertyName) + /// + /// Gets or sets a property name. + /// + public string Name { get; set; } + + /// + /// Gets a list of defined Revit parameters. + /// + public List RevitParameters { get; private set; } = new List(); + + /// + /// Gets a property value type. By default . + /// + public PropertyValueType IfcPropertyValueType { get; private set; } = PropertyValueType.SingleValue; + + /// + /// Gets a list of property types. + /// + public List IfcPropertyTypes { get; private set; } = new List(); + + /// + /// Parses and sets and by given . + /// + /// Property type data to parse. + public void ParseIfcPropertyTypes(string rawIfcPropertyTypes) { - PropertyName = propertyName; - ParameterDefinitions.Add(new PropertyParameterDefinition(propertyName)); + if (string.IsNullOrWhiteSpace(rawIfcPropertyTypes)) + return; + + // format: .//... + IfcPropertyTypes.Clear(); + string dataTypePartToParse = string.Empty; + string[] split = rawIfcPropertyTypes.Split('.') ?? new string[] {}; + if (split.Length == 1) + { + IfcPropertyValueType = PropertyValueType.SingleValue; + dataTypePartToParse = split[0]; + } + else if (split.Length >= 2) + { + const string prefix = "Property"; + bool withPrefix = split[0].StartsWith(prefix, StringComparison.InvariantCultureIgnoreCase); + string rawValueType = withPrefix ? split[0].Substring(prefix.Length) : split[0]; + if (!Enum.TryParse(rawValueType, true, out PropertyValueType propertyValueType)) + { + propertyValueType = PropertyValueType.SingleValue; + } + + IfcPropertyValueType = propertyValueType; + dataTypePartToParse = split[1]; + } + + string[] rawPropertyTypes = dataTypePartToParse.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries); + foreach(string rawPropertyType in rawPropertyTypes) + { + const string prefix = "Ifc"; + bool withPrefix = rawPropertyType.StartsWith(prefix, StringComparison.InvariantCultureIgnoreCase); + IfcPropertyTypes.Add(withPrefix ? rawPropertyType.Substring(prefix.Length) : rawPropertyType); + } } - public PropertyDef(string propertyName, PropertyParameterDefinition definition) + + /// + /// Parses a list of revit parameters by given data from config. + /// + /// Data to parse. + public void ParseRevitParameters(string rawParameters) { - PropertyName = propertyName; - ParameterDefinitions.Add(definition); + if (string.IsNullOrWhiteSpace(rawParameters)) + return; + + RevitParameters.Clear(); + UserDefinedPropertyRevitParameter parameter = UserDefinedPropertyRevitParameter.Create(rawParameters); + if (parameter != null) + RevitParameters.Add(parameter); } - public PropertyDef(string propertyName, IEnumerable definitions) + + /// + /// Gets Revit parameters mapped into list of T type. + /// + /// The type of list elements. + /// Function to initialize T. + /// A list of Revit parameters mapped into list of T type. + public List GetEntryMap(Func mapper) where T : EntryMap, new() { - PropertyName = propertyName; - ParameterDefinitions.AddRange(definitions); + List entryMap = new List(); + foreach (UserDefinedPropertyRevitParameter parameter in RevitParameters) + { + if (parameter.BuiltInParameter != BuiltInParameter.INVALID) + { + entryMap.Add(mapper(Name, parameter.BuiltInParameter)); + } + else if (!parameter.IsBuiltInParameterDefined) + { + entryMap.Add(mapper(parameter.RevitParameter, BuiltInParameter.INVALID)); + } + else + { + // report as error in log when we create log file. + } + } + + return entryMap; } - } - public class PropertyParameterDefinition - { - public string RevitParameterName = ""; - public Autodesk.Revit.DB.BuiltInParameter RevitBuiltInParameter = Autodesk.Revit.DB.BuiltInParameter.INVALID; - public PropertyParameterDefinition(string revitParameterName) + + /// + /// Returns the first element of converted to , + /// or a if contains no elements. + /// + /// The enumeration type to which to convert value. + /// + /// Value to return if the index is out of range, if is empty or + /// first element value is not represented in the . + /// + /// + /// if source is empty; otherwise, the first element in + /// converted to + /// + public TEnum FirstIfcPropertyTypeOrDefault(TEnum defaultValue) where TEnum : struct { - RevitParameterName = revitParameterName; + if ((IfcPropertyTypes?.Count ?? 0) == 0) + return defaultValue; + + if(Enum.TryParse(IfcPropertyTypes[0], true, out TEnum t)) + return t; + + return defaultValue; } - public PropertyParameterDefinition(Autodesk.Revit.DB.BuiltInParameter builtInParameter) + + /// + /// Returns the type at a specified index in converted to + /// or a default value if the index is out of range, if is empty, if value at a specified + /// index is not represented in the . + /// + /// The enumeration type to which to convert value. + /// The zero-based index of the element to retrieve. + /// + /// Value to return if the index is out of range, if is empty, if value at a specified + /// index is not represented in the . + /// + /// + /// if the index is outside the bounds of the source sequence or is empty; + /// otherwise,the element at the specified position in the source sequence. + /// + public TEnum GetIfcPropertyAtOrDefault(int index, TEnum defaultValue) where TEnum : struct { - RevitBuiltInParameter = builtInParameter; + if ((IfcPropertyTypes?.Count ?? 0) == 0 || IfcPropertyTypes.Count >= index || index < 0) + return defaultValue; + + if (Enum.TryParse(IfcPropertyTypes[index], true, out TEnum t)) + return t; + + return defaultValue; } } + + /// + /// Represents a property set from the config. + /// + class UserDefinedPropertySet + { + /// + /// Gets a property set name. + /// + public string Name { get; set; } + + /// + /// Gets a type of elements group for which the property set is defined. + /// + public string Type { get; set; } + + /// + /// Gets a list of IFC entites for which property set is defined. + /// + public string[] IfcEntities { get; set; } + + /// + /// Gets a list of properties in the property set. + /// + public IList Properties { get; set; } = new List(); + } + class PropertyMap { /// @@ -133,7 +308,7 @@ private static void ParseLine(Dictionary, string> paramete { // add the line string[] split = line.Split(new char[] { '\t' }, StringSplitOptions.RemoveEmptyEntries); - if (split.Count() == 3) + if (split.Length == 3) parameterMap.Add(Tuple.Create(split[0], split[1]), split[2]); } } @@ -141,7 +316,7 @@ private static void ParseLine(Dictionary, string> paramete /// /// Load user-defined Property set /// Format: - /// PropertSet: I[nstance]/T[ype] + /// PropertySet: I[nstance]/T[ype] /// Property_name Data_type Revit_Parameter /// ... /// Datatype supported: Text, Integer, Real, Boolean @@ -149,9 +324,9 @@ private static void ParseLine(Dictionary, string> paramete /// #! UserDefinedPset /// /// List of property set definitions - public static IEnumerable LoadUserDefinedPset() + public static IEnumerable LoadUserDefinedPset() { - List userDefinedPsets = new List(); + List userDefinedPropertySets = new List(); try { @@ -162,105 +337,48 @@ public static IEnumerable LoadUserDefinedPset() filename = GetUserDefPsetFilename(); } if (!File.Exists(filename)) - return userDefinedPsets; + return userDefinedPropertySets; - string extension = Path.GetExtension(filename); - if (string.Compare(extension, ".ifcxml", true) == 0 || string.Compare(extension, ".ifcjson", true) == 0 || string.Compare(extension, ".ifc", true) == 0) + // Format: PropertSet: I[nstance]/T[ype] + // Property_name Data_type Revit_Parameter + // ** For now it only works for simple property with single value (datatype supported: Text, Integer, Real and Boolean) + using (StreamReader sr = new StreamReader(filename)) { - DatabaseIfc db = new DatabaseIfc(filename); - IfcContext context = db.Context; - if (context == null) - return userDefinedPsets; - foreach (IfcRelDeclares relDeclares in context.Declares) - { - userDefinedPsets.AddRange(relDeclares.RelatedDefinitions.OfType()); - } - } - else - { - using (StreamReader sr = new StreamReader(filename)) + string line; + + // current property set + UserDefinedPropertySet userDefinedPropertySet = null; + while ((line = sr.ReadLine()) != null) { - string line; + line = line.TrimStart(' ', '\t'); - DatabaseIfc db = new DatabaseIfc(ReleaseVersion.IFC4A2); - IfcPropertySetTemplate userDefinedPset = null; - while ((line = sr.ReadLine()) != null) + if (string.IsNullOrEmpty(line) || line[0] == '#') + continue; + + string[] split = line.Split(new char[] { '\t' }, StringSplitOptions.RemoveEmptyEntries); + if (split.Length >=4 && string.Compare(split[0], "PropertySet:", true) == 0) // Any entry with less than 3 parameters is malformed. { - line.TrimStart(' ', '\t'); + userDefinedPropertySet = new UserDefinedPropertySet() + { + Name = split[1], + Type = split[2], + IfcEntities = split[3].Split(new char[] { ',', ';', ' ' }, StringSplitOptions.RemoveEmptyEntries) + }; - if (String.IsNullOrEmpty(line)) continue; - if (line[0] != '#') + userDefinedPropertySets.Add(userDefinedPropertySet); + } + else if (split.Length >= 2 && userDefinedPropertySet != null) // Skip property definitions outside of property set block. + { + UserDefinedProperty userDefinedProperty = new UserDefinedProperty(); + userDefinedProperty.Name = split[0]; + userDefinedProperty.ParseIfcPropertyTypes(split[1]); + + if (split.Length >= 3) { - // Format: PropertSet: I[nstance]/T[ype] - // Property_name Data_type Revit_Parameter - // ** For now it only works for simple property with single value (datatype supported: Text, Integer, Real and Boolean) - - string[] split = line.Split(new char[] { '\t' }, StringSplitOptions.RemoveEmptyEntries); - if (string.Compare(split[0], "PropertySet:", true) == 0) - { - userDefinedPset = new IfcPropertySetTemplate(db, split.Length > 2 ? split[1] : "Unknown"); - if (split.Count() >= 4) // Any entry with less than 3 par is malformed - { - switch (split[2][0]) - { - case 'T': - userDefinedPset.TemplateType = IfcPropertySetTemplateTypeEnum.PSET_TYPEDRIVENONLY; - break; - case 'I': - userDefinedPset.TemplateType = IfcPropertySetTemplateTypeEnum.PSET_OCCURRENCEDRIVEN; - break; - default: - userDefinedPset.TemplateType = IfcPropertySetTemplateTypeEnum.PSET_OCCURRENCEDRIVEN; - break; - } - userDefinedPset.ApplicableEntity = string.Join(",", split[3].Split(new char[] { ',', ';', ' ' }, StringSplitOptions.RemoveEmptyEntries)); - userDefinedPsets.Add(userDefinedPset); - } - } - else - { - if (split.Count() >= 2) - { - string propertyTemplateName = split[0]; - IfcSimplePropertyTemplate propertyDefUnit = userDefinedPset[propertyTemplateName] as IfcSimplePropertyTemplate; - if(propertyDefUnit == null) - userDefinedPset.AddPropertyTemplate(propertyDefUnit = new IfcSimplePropertyTemplate(db, split[0])); - if (split.Count() >= 3 && !string.IsNullOrEmpty(split[2])) - { - new IfcRelAssociatesClassification(new IfcClassificationReference(db) { Identification = split[2] }, propertyDefUnit); - } - if (!string.IsNullOrEmpty(split[1])) - { - string[] propertyTypeVals = split[1].Split(new char[] {'.'}, StringSplitOptions.RemoveEmptyEntries); - if (propertyTypeVals.Count() > 1) - { - switch (propertyTypeVals[0]) - { - case "PropertyListValue": - propertyDefUnit.TemplateType = IfcSimplePropertyTemplateTypeEnum.P_LISTVALUE; - break; - case "PropertyBoundedValue": - propertyDefUnit.TemplateType = IfcSimplePropertyTemplateTypeEnum.P_BOUNDEDVALUE; - break; - case "PropertyTableValue": - propertyDefUnit.TemplateType = IfcSimplePropertyTemplateTypeEnum.P_TABLEVALUE; - break; - default: - propertyDefUnit.TemplateType = IfcSimplePropertyTemplateTypeEnum.P_SINGLEVALUE; - break; - } - - string[] measureTypes = propertyTypeVals[1].Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries); - propertyDefUnit.PrimaryMeasureType = "Ifc" + measureTypes[0]; - if (measureTypes.Count() == 2) - propertyDefUnit.SecondaryMeasureType = "Ifc" + measureTypes[1]; - } - else - propertyDefUnit.PrimaryMeasureType = "Ifc" + propertyTypeVals[0]; - } - } - } + userDefinedProperty.ParseRevitParameters(split[2]); } + + userDefinedPropertySet.Properties.Add(userDefinedProperty); } } } @@ -271,7 +389,7 @@ public static IEnumerable LoadUserDefinedPset() Console.WriteLine(e.Message); } - return userDefinedPsets; + return userDefinedPropertySets; } /// diff --git a/Source/Revit.IFC.Export/Utility/RepresentationUtil.cs b/Source/Revit.IFC.Export/Utility/RepresentationUtil.cs index 17589f82..270a38be 100644 --- a/Source/Revit.IFC.Export/Utility/RepresentationUtil.cs +++ b/Source/Revit.IFC.Export/Utility/RepresentationUtil.cs @@ -526,12 +526,14 @@ public static IFCAnyHandle CreateBoundingBoxRep(ExporterIFC exporterIFC, IFCAnyH /// The category id. /// The context for which the different subtypes of representation are valid. /// Set of geometric representation items that are defined for this representation. + /// If true, creates a GeometricSet instead of an Annotation2D. /// The handle. public static IFCAnyHandle CreateAnnotationSetRep(ExporterIFC exporterIFC, Element element, ElementId categoryId, - IFCAnyHandle contextOfItems, HashSet bodyItems) + IFCAnyHandle contextOfItems, HashSet bodyItems, bool is3D) { string identifierOpt = "Annotation"; - string repTypeOpt = ShapeRepresentationType.Annotation2D.ToString(); // this is by IFC2x3 convention + string repTypeOpt = is3D ? ShapeRepresentationType.GeometricSet.ToString() : + ShapeRepresentationType.Annotation2D.ToString(); IFCAnyHandle bodyRepresentation = CreateShapeRepresentation(exporterIFC, element, categoryId, contextOfItems, identifierOpt, repTypeOpt, bodyItems); @@ -640,6 +642,8 @@ public static IFCAnyHandle CreateBoundingBoxRep(ExporterIFC exporterIFC, IFCAnyH if (boundingBoxRep != null) bodyReps.Add(boundingBoxRep); + // NOTE: This can create an invalid IfcProductDefinitionShape with no representations. The expectation is + // that these will be created later, but at the moment that is not guaranteed. return IFCInstanceExporter.CreateProductDefinitionShape(exporterIFC.GetFile(), null, null, bodyReps); } diff --git a/Source/Revit.IFC.Export/Utility/SimpleSweptSolidAnalyzer.cs b/Source/Revit.IFC.Export/Utility/SimpleSweptSolidAnalyzer.cs index f4e2145a..67f7e11f 100644 --- a/Source/Revit.IFC.Export/Utility/SimpleSweptSolidAnalyzer.cs +++ b/Source/Revit.IFC.Export/Utility/SimpleSweptSolidAnalyzer.cs @@ -20,7 +20,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; using Autodesk.Revit.DB; using Revit.IFC.Common.Utility; @@ -35,45 +34,20 @@ namespace Revit.IFC.Export.Utility /// class SimpleSweptSolidAnalyzer { - PlanarFace m_ProfileFace; - - Curve m_PathCurve; - - XYZ m_ReferencePlaneNormal; - - List m_UnalignedFaces; - /// /// The face that represents the profile of the swept solid. /// - public PlanarFace ProfileFace - { - get { return m_ProfileFace; } - } + public PlanarFace ProfileFace { get; private set; } = null; /// /// The edge that represents the path of the swept solid. /// - public Curve PathCurve - { - get { return m_PathCurve; } - } - - /// - /// The normal of the reference plane that the path lies on. - /// - public XYZ ReferencePlaneNormal - { - get { return m_ReferencePlaneNormal; } - } + public Curve PathCurve { get; private set; } = null; /// /// The unaligned faces, maybe openings or recesses. /// - public List UnalignedFaces - { - get { return m_UnalignedFaces; } - } + public List UnalignedFaces { get; } = new List(); /// /// Creates a SimpleSweptSolidAnalyzer and computes the swept solid. @@ -110,7 +84,8 @@ public static SimpleSweptSolidAnalyzer Create(ICollection faces, XYZ norma IList> potentialSweptAreaFaces = new List>(); Curve directrix = potentialPathGeom as Curve; - if (potentialPathGeom == null) + // In some cases potentialPathGeom comes as PolyLine + if (directrix == null) return Create(faces, normal); XYZ directrixStartPt = directrix.GetEndPoint(0); @@ -191,14 +166,158 @@ public static SimpleSweptSolidAnalyzer Create(ICollection faces, XYZ norma if (sweptEndStartFace != null) { simpleSweptSolidAnalyzer = new SimpleSweptSolidAnalyzer(); - simpleSweptSolidAnalyzer.m_ProfileFace = sweptEndStartFace; - simpleSweptSolidAnalyzer.m_PathCurve = directrix; - simpleSweptSolidAnalyzer.m_ReferencePlaneNormal = normal; + simpleSweptSolidAnalyzer.ProfileFace = sweptEndStartFace; + simpleSweptSolidAnalyzer.PathCurve = directrix; } return simpleSweptSolidAnalyzer; } + private static Curve GetPathCurve(PlanarFace profileFace1, PlanarFace profileFace2, + IList cylindricalFaces, EdgeArray edgeArray) + { + // Get an arbitrary curve from any of the cylindrical faces that isn't shared with either of + // the provided planar faces. + // Assumptions of this routine: 2 planar faces, all cylindrical faces have 4 edges. + foreach (Edge edge in edgeArray) + { + Face edgeFace0 = edge.GetFace(0); + if (edgeFace0 == profileFace1 || edgeFace0 == profileFace2) + { + continue; + } + + Face edgeFace1 = edge.GetFace(1); + if (edgeFace1 == profileFace1 || edgeFace1 == profileFace2) + { + continue; + } + + Curve curve = edge.AsCurveFollowingFace(cylindricalFaces[0]); + if (curve is Line) + { + // This is a cylinder. + return null; + } + + return curve; + } + + return null; + } + + /// + /// Creates a SimpleSweptSolidAnalyzer and computes the swept solid. + /// + /// The faces of a solid. + /// The analyzer. + /// This is very specialized for a solid made of planes and cylinders. + public static SimpleSweptSolidAnalyzer CreateFromSimpleCylindricalSurfaces(ICollection faces) + { + int numFaces = faces?.Count ?? 0; + if (numFaces < 3) + { + // Invalid faces. + return null; + } + + // Only acceptable cases: planarFaces + at least 1 cylindricalFace, and no other face types. + // All faces have either 4 edges, except for the two end planar faces that have numFaces-2 edges. + IList endPlanarFaces = new List(); + IList cylindricalFaces = new List(); + EdgeArray edgeArray = null; + + int numEndPlanarFaces = 0; + + foreach (Face face in faces) + { + EdgeArrayArray edgeArrayArray = face.EdgeLoops; + if ((edgeArrayArray?.Size ?? 0) != 1) + { + return null; + } + + EdgeArray currEdgeArray = edgeArrayArray.get_Item(0); + int currEdgeArraySize = currEdgeArray.Size; + + if (face is CylindricalFace) + { + if (currEdgeArraySize != 4) + { + return null; + } + if (edgeArray == null) + { + edgeArray = currEdgeArray; + } + + cylindricalFaces.Add(face as CylindricalFace); + continue; + } + else if (face is PlanarFace) + { + PlanarFace planarFace = face as PlanarFace; + if (currEdgeArraySize == 4) + { + continue; + } + + if (numEndPlanarFaces == 2 || currEdgeArraySize != numFaces - 2) + { + return null; + } + + numEndPlanarFaces++; + endPlanarFaces.Add(planarFace); + } + else + { + return null; + } + } + + if (numEndPlanarFaces < 2 || cylindricalFaces.Count == 0) + { + return null; + } + + Curve pathCurve = GetPathCurve(endPlanarFaces[0], endPlanarFaces[1], cylindricalFaces, edgeArray); + if (pathCurve == null || !pathCurve.IsBound) + { + return null; + } + + XYZ firstPlaneNormal = endPlanarFaces[0].FaceNormal; + XYZ firstPlaneOrigin = endPlanarFaces[0].Origin; + if (!MathUtil.IsAlmostZero((pathCurve.GetEndPoint(0) - firstPlaneOrigin).DotProduct(firstPlaneNormal))) + { + pathCurve = pathCurve.CreateReversed(); + // Sanity check. + if (!MathUtil.IsAlmostZero((pathCurve.GetEndPoint(0) - firstPlaneOrigin).DotProduct(firstPlaneNormal))) + { + return null; + } + } + + XYZ startPathCurveNormal = pathCurve.ComputeDerivatives(0, true).BasisX.Normalize(); + XYZ endPathCurveNormal = pathCurve.ComputeDerivatives(1, true).BasisX.Normalize(); + + XYZ secondPlaneNormal = endPlanarFaces[1].FaceNormal; + + if (firstPlaneNormal.IsAlmostEqualTo(-startPathCurveNormal) && + secondPlaneNormal.IsAlmostEqualTo(endPathCurveNormal)) + { + SimpleSweptSolidAnalyzer simpleSweptSolidAnalyzer = new SimpleSweptSolidAnalyzer(); + + simpleSweptSolidAnalyzer.ProfileFace = endPlanarFaces[0]; + simpleSweptSolidAnalyzer.PathCurve = pathCurve; + + return simpleSweptSolidAnalyzer; + } + + return null; + } + /// /// Creates a SimpleSweptSolidAnalyzer and computes the swept solid. /// @@ -208,7 +327,14 @@ public static SimpleSweptSolidAnalyzer Create(ICollection faces, XYZ norma /// This is a simple analyzer, and is not intended to be general - it works in some simple, real-world cases. public static SimpleSweptSolidAnalyzer Create(ICollection faces, XYZ normal) { - if (faces == null || faces.Count < 3) + SimpleSweptSolidAnalyzer simpleSweptSolidAnalyzer = CreateFromSimpleCylindricalSurfaces(faces); + if (simpleSweptSolidAnalyzer != null) + { + return simpleSweptSolidAnalyzer; + } + + int numFaces = faces?.Count ?? 0; + if (numFaces < 3) { // Invalid faces. return null; @@ -324,9 +450,11 @@ public static SimpleSweptSolidAnalyzer Create(ICollection faces, XYZ norma Dictionary> sideFacesWithEdgesDic = new Dictionary>(); foreach (Face sideFace in sideFacesWithCandidateEdges.Keys) { - sideFacesWithEdgesDic[sideFace] = new List(); - sideFacesWithEdgesDic[sideFace].Add(sideFacesWithCandidateEdges[sideFace]); - sideFacesWithEdgesDic[sideFace].Add(sideFacesWithTheOtherCandidateEdges[sideFace]); + sideFacesWithEdgesDic[sideFace] = new List() + { + sideFacesWithCandidateEdges[sideFace], + sideFacesWithTheOtherCandidateEdges[sideFace] + }; } if (!AreFacesSimpleCongruent(sideFacesWithEdgesDic)) @@ -364,8 +492,6 @@ public static SimpleSweptSolidAnalyzer Create(ICollection faces, XYZ norma break; } while (ii < potentialSweepEndFaces.Count); - SimpleSweptSolidAnalyzer simpleSweptSolidAnalyzer = null; - if (foundCandidateFace) { simpleSweptSolidAnalyzer = new SimpleSweptSolidAnalyzer(); @@ -373,9 +499,7 @@ public static SimpleSweptSolidAnalyzer Create(ICollection faces, XYZ norma XYZ endPoint0 = pathCurve.GetEndPoint(0); bool foundProfileFace = false; - List profileFaces = new List(); - profileFaces.Add(candidateProfileFace); - profileFaces.Add(candidateProfileFace2); + List profileFaces = new List() { candidateProfileFace, candidateProfileFace2 }; foreach (PlanarFace profileFace in profileFaces) { @@ -388,7 +512,7 @@ public static SimpleSweptSolidAnalyzer Create(ICollection faces, XYZ norma XYZ intersectPoint = intersectionResult.XYZPoint; if (intersectPoint.IsAlmostEqualTo(endPoint0)) { - simpleSweptSolidAnalyzer.m_ProfileFace = profileFace; + simpleSweptSolidAnalyzer.ProfileFace = profileFace; foundProfileFace = true; break; } @@ -408,19 +532,15 @@ public static SimpleSweptSolidAnalyzer Create(ICollection faces, XYZ norma // TODO: consider one profile face has an opening extrusion inside while the other does not List alignedFaces = FindAlignedFaces(profileFaces.ToList()); - List unalignedFaces = new List(); foreach (Face face in faces) { if (profileFaces.Contains(face) || alignedFaces.Contains(face)) continue; - unalignedFaces.Add(face); + simpleSweptSolidAnalyzer.UnalignedFaces.Add(face); } - simpleSweptSolidAnalyzer.m_UnalignedFaces = unalignedFaces; - - simpleSweptSolidAnalyzer.m_PathCurve = pathCurve; - simpleSweptSolidAnalyzer.m_ReferencePlaneNormal = normal; + simpleSweptSolidAnalyzer.PathCurve = pathCurve; } return simpleSweptSolidAnalyzer; diff --git a/Source/Revit.IFC.Export/Utility/TriangleMergeUtil.cs b/Source/Revit.IFC.Export/Utility/TriangleMergeUtil.cs index fe01630b..968abbad 100644 --- a/Source/Revit.IFC.Export/Utility/TriangleMergeUtil.cs +++ b/Source/Revit.IFC.Export/Utility/TriangleMergeUtil.cs @@ -52,7 +52,7 @@ class IndexFace /// Constructor taking a list of vertex indices (face without hole) /// /// the list of vertex indices (face without hole) - public IndexFace(IList vertxIndices, ref IDictionary meshVertices) + public IndexFace(IList vertxIndices, IDictionary meshVertices) { IndexOuterBoundary = vertxIndices; SetupEdges(IndexOuterBoundary, 0); @@ -69,7 +69,7 @@ public IndexFace(IList vertxIndices, ref IDictionary meshVertices /// Constructor taking in List of List of vertices. The first list will be the outer boundary and the rest are the inner boundaries /// /// List of List of vertices. The first list will be the outer boundary and the rest are the inner boundaries - public IndexFace(IList> vertxIndices, ref IDictionary meshVertices) + public IndexFace(IList> vertxIndices, IDictionary meshVertices) { int vertexCount = vertxIndices?.Count ?? 0; if (vertexCount == 0) @@ -220,7 +220,7 @@ public IndexSegment(int startIndex, int endIndex) /// /// Extent size (length) of the line segment /// - public double Extent(ref IDictionary meshVertices) + public double Extent(IDictionary meshVertices) { return meshVertices[StartPindex].DistanceTo(meshVertices[EndPIndex]); } @@ -305,22 +305,23 @@ public int GetHashCode(XYZ obj) /// public class TriangleMergeUtil { - protected TriangulatedShellComponent m_Geom = null; - protected Mesh m_MeshGeom = null; + protected TriangulatedShellComponent TriangulatedShell { get; set; } = null; + protected Mesh MeshGeom { get; set; } = null; // A Dictionary is created for the mesh vertices due to performance issue for very large mesh if the vertex is accessed via its index - protected IDictionary m_MeshVertices = new Dictionary(); + protected IDictionary MeshVertices { get; set; } = new Dictionary(); - HashSet m_MergedFaceSet = new HashSet(); + HashSet MergedFaceSet { get; set; } = new HashSet(); + List MergedFaceIndices { get; set; } = new List(); - IDictionary m_FacesCollDict = new Dictionary(); - int faceIdxOffset = 0; + IDictionary FacesCollDict { get; set; } = new Dictionary(); + int FaceIdxOffset { get; set; } = 0; // These two must be hand in hand public const int DecimalPrecision = 6; public const double Tolerance = 1e-6; - public bool IsMesh { get { return (m_MeshGeom != null && m_Geom == null); } } + public bool IsMesh { get { return (MeshGeom != null && TriangulatedShell == null); } } /// /// Get the total number of triangles from the Mesh or the TriangulatedShellComponent object. @@ -328,10 +329,10 @@ public class TriangleMergeUtil /// The total number of triangles. public int GetTriangleCount() { - return (IsMesh) ? m_MeshGeom.NumTriangles : m_Geom.TriangleCount; + return (IsMesh) ? MeshGeom.NumTriangles : TriangulatedShell.TriangleCount; } - public List GetVertices() { return m_MeshVertices.Values.ToList(); } + public List GetVertices() { return MeshVertices.Values.ToList(); } /// /// Function called before and after triangle merging. @@ -342,30 +343,29 @@ private int CalculateEulerCharacteristic() { int noVertices = 0; int noHoles = 0; // Stays zero if mesh is triangular - int noFaces; + int noFaces = MergedFaceSet.Count; HashSet edges = new HashSet(new SegmentComparer(true/*compareBothDirections*/)); - if (m_MergedFaceSet.Count != 0) + if (noFaces != 0) { // Merging already occurred, calculate new Euler characteristic - noFaces = m_MergedFaceSet.Count; - foreach (int mergedFace in m_MergedFaceSet) + foreach (int mergedFace in MergedFaceSet) { - m_FacesCollDict[mergedFace].OuterAndInnerBoundaries.ToList().ForEach(vp => edges.Add(vp.Value)); - if (m_FacesCollDict[mergedFace].IndexedInnerBoundaries != null) - noHoles += m_FacesCollDict[mergedFace].IndexedInnerBoundaries.Count; + FacesCollDict[mergedFace].OuterAndInnerBoundaries.ToList().ForEach(vp => edges.Add(vp.Value)); + if (FacesCollDict[mergedFace].IndexedInnerBoundaries != null) + noHoles += FacesCollDict[mergedFace].IndexedInnerBoundaries.Count; } - noVertices = m_MeshVertices.Count; // m_MeshVertices doesn't contain isolated vertices + noVertices = MeshVertices.Count; // m_MeshVertices doesn't contain isolated vertices } else { if (IsMesh) { - noVertices = m_MeshGeom.Vertices.Count; - noFaces = m_MeshGeom.NumTriangles; + noVertices = MeshGeom.Vertices.Count; + noFaces = MeshGeom.NumTriangles; for (int faceIdx = 0; faceIdx < noFaces; faceIdx++) { - MeshTriangle tri = m_MeshGeom.get_Triangle(faceIdx); + MeshTriangle tri = MeshGeom.get_Triangle(faceIdx); edges.Add(new IndexSegment((int)tri.get_Index(0), (int)tri.get_Index(1))); edges.Add(new IndexSegment((int)tri.get_Index(1), (int)tri.get_Index(2))); edges.Add(new IndexSegment((int)tri.get_Index(2), (int)tri.get_Index(0))); @@ -373,11 +373,11 @@ private int CalculateEulerCharacteristic() } else { - noVertices = m_Geom.VertexCount; - noFaces = m_Geom.TriangleCount; + noVertices = TriangulatedShell.VertexCount; + noFaces = TriangulatedShell.TriangleCount; for (int faceIdx = 0; faceIdx < noFaces; faceIdx++) { - TriangleInShellComponent tri = m_Geom.GetTriangle(faceIdx); + TriangleInShellComponent tri = TriangulatedShell.GetTriangle(faceIdx); edges.Add(new IndexSegment(tri.VertexIndex0, tri.VertexIndex1)); edges.Add(new IndexSegment(tri.VertexIndex1, tri.VertexIndex2)); edges.Add(new IndexSegment(tri.VertexIndex2, tri.VertexIndex0)); @@ -395,8 +395,8 @@ private int CalculateEulerCharacteristic() /// public TriangleMergeUtil(TriangulatedShellComponent triangulatedBody) { - m_Geom = triangulatedBody; - m_MeshGeom = null; + TriangulatedShell = triangulatedBody; + MeshGeom = null; Reset(); } @@ -407,8 +407,8 @@ public TriangleMergeUtil(TriangulatedShellComponent triangulatedBody) /// the Mesh public TriangleMergeUtil(Mesh triangulatedMesh) { - m_Geom = null; - m_MeshGeom = triangulatedMesh; + TriangulatedShell = null; + MeshGeom = triangulatedMesh; Reset(); } @@ -417,19 +417,21 @@ public TriangleMergeUtil(Mesh triangulatedMesh) /// public void Reset() { - m_MergedFaceSet.Clear(); - m_FacesCollDict.Clear(); - m_MeshVertices.Clear(); - + MergedFaceSet.Clear(); + MergedFaceIndices.Clear(); + FacesCollDict.Clear(); + MeshVertices.Clear(); + FaceIdxOffset = 0; + IList vertices = null; - if (m_Geom != null) - vertices = m_Geom.GetVertices(); - else if (m_MeshGeom != null) - vertices = m_MeshGeom.Vertices; + if (TriangulatedShell != null) + vertices = TriangulatedShell.GetVertices(); + else if (MeshGeom != null) + vertices = MeshGeom.Vertices; if (vertices != null) for (int idx = 0; idx < vertices.Count; ++idx) - m_MeshVertices.Add(idx, vertices[idx]); + MeshVertices.Add(idx, vertices[idx]); } /// @@ -437,7 +439,7 @@ public void Reset() /// public int NoOfFaces { - get { return m_MergedFaceSet.Count; } + get { return MergedFaceSet.Count; } } /// @@ -447,7 +449,7 @@ public int NoOfFaces /// return index of vertices public IList IndexOuterboundOfFaceAt(int fIdx) { - return m_FacesCollDict[m_MergedFaceSet.ElementAt(fIdx)].IndexOuterBoundary; + return FacesCollDict[MergedFaceIndices[fIdx]].IndexOuterBoundary; } /// @@ -457,7 +459,7 @@ public IList IndexOuterboundOfFaceAt(int fIdx) /// List of list of the inner boundaries public IList> IndexInnerBoundariesOfFaceAt(int fIdx) { - return m_FacesCollDict[m_MergedFaceSet.ElementAt(fIdx)].IndexedInnerBoundaries; + return FacesCollDict[MergedFaceIndices[fIdx]].IndexedInnerBoundaries; } public static XYZ NormalByNewellMethod(IList vertices) @@ -506,9 +508,9 @@ public static XYZ NormalByNewellMethod(IList vertices) /// /// Combine coplanar triangles from the faceted body if they share the edge. From this process, polygonal faces (with or without holes) will be created /// - public void SimplifyAndMergeFaces(bool ignoreMerge = false) + public bool SimplifyAndMergeFaces(bool tryToMerge) { - int eulerBefore = ignoreMerge ? 0 : CalculateEulerCharacteristic(); + int eulerBefore = tryToMerge ? CalculateEulerCharacteristic() : 0; int noTriangle = GetTriangleCount(); IEqualityComparer normalComparer = new VectorCompare(); @@ -520,17 +522,17 @@ public void SimplifyAndMergeFaces(bool ignoreMerge = false) if (IsMesh) { - MeshTriangle f = m_MeshGeom.get_Triangle(ef); + MeshTriangle f = MeshGeom.get_Triangle(ef); vertIndex = new List(3) { (int)f.get_Index(0), (int)f.get_Index(1), (int)f.get_Index(2) }; } else { - TriangleInShellComponent f = m_Geom.GetTriangle(ef); + TriangleInShellComponent f = TriangulatedShell.GetTriangle(ef); vertIndex = new List(3) { f.VertexIndex0, f.VertexIndex1, f.VertexIndex2 }; } - IndexFace intF = new IndexFace(vertIndex, ref m_MeshVertices); - m_FacesCollDict.Add(faceIdxOffset++, intF); // Keep faces in a dictionary and assigns ID + IndexFace intF = new IndexFace(vertIndex, MeshVertices); + FacesCollDict.Add(FaceIdxOffset++, intF); // Keep faces in a dictionary and assigns ID List fIDList; if (!faceSortedByNormal.TryGetValue(intF.Normal, out fIDList)) @@ -549,8 +551,10 @@ public void SimplifyAndMergeFaces(bool ignoreMerge = false) List mergedFaceList = null; if (fListDict.Value.Count > 1) { - if (!ignoreMerge) + if (tryToMerge) + { TryMergeFaces(fListDict.Value, out mergedFaceList); + } else { // keep original face list @@ -561,21 +565,24 @@ public void SimplifyAndMergeFaces(bool ignoreMerge = false) // insert only new face indexes as the mergedlist from different vertices can be duplicated foreach (int fIdx in mergedFaceList) { - if (!m_MergedFaceSet.Contains(fIdx)) - m_MergedFaceSet.Add(fIdx); + if (!MergedFaceSet.Contains(fIdx)) + AddMergedFaceIndex(fIdx); } } } - else if (!m_MergedFaceSet.Contains(fListDict.Value[0])) - m_MergedFaceSet.Add(fListDict.Value[0]); // No pair face, add it into the mergedList + else if (!MergedFaceSet.Contains(fListDict.Value[0])) + { + AddMergedFaceIndex(fListDict.Value[0]); // No pair face, add it into the mergedList + } } // Remove unused vertices CleanVerticesAndUpdateIndexes(); - int eulerAfter = ignoreMerge ? 0 : CalculateEulerCharacteristic(); - if (eulerBefore != eulerAfter) - throw new InvalidOperationException(); // Coplanar merge broke the mesh in some way, so we need to fall back to exporting a triangular mesh + int eulerAfter = tryToMerge ? CalculateEulerCharacteristic() : 0; + // If eulerBefore != eulerAfter, coplanar merge broke the mesh in some way. + // We need to fall back to exporting a triangular mesh. + return (eulerBefore == eulerAfter); } /// @@ -585,31 +592,37 @@ void CleanVerticesAndUpdateIndexes() { List isolatedVertices = new List(); - if (m_MergedFaceSet.Count != 0) + if (MergedFaceSet.Count != 0) { HashSet vertices = new HashSet(); - foreach (int mergedFace in m_MergedFaceSet) + foreach (int mergedFace in MergedFaceSet) { - m_FacesCollDict[mergedFace].IndexOuterBoundary.ToList().ForEach(vt => vertices.Add(vt)); - if (m_FacesCollDict[mergedFace].IndexedInnerBoundaries != null) + FacesCollDict[mergedFace].IndexOuterBoundary.ToList().ForEach(vt => vertices.Add(vt)); + if (FacesCollDict[mergedFace].IndexedInnerBoundaries != null) { - foreach (IList innerB in m_FacesCollDict[mergedFace].IndexedInnerBoundaries) + foreach (IList innerB in FacesCollDict[mergedFace].IndexedInnerBoundaries) + { innerB.ToList().ForEach(vt => vertices.Add(vt)); + } } } - if (vertices.Count < m_MeshVertices.Count) - isolatedVertices = Enumerable.Range(0, m_MeshVertices.Count).Except(vertices).OrderBy(x => x).ToList(); + if (vertices.Count < MeshVertices.Count) + isolatedVertices = Enumerable.Range(0, MeshVertices.Count).Except(vertices).OrderBy(x => x).ToList(); } if (isolatedVertices.Count > 0) { foreach (int vertexInd in isolatedVertices) - m_MeshVertices.Remove(vertexInd); + { + MeshVertices.Remove(vertexInd); + } - foreach (IndexFace face in m_FacesCollDict.Values) + foreach (IndexFace face in FacesCollDict.Values) + { face.UpdateIndexes(isolatedVertices); + } } } /// @@ -621,7 +634,7 @@ void CleanVerticesAndUpdateIndexes() void TryMergeFaces(List inputFaceList, out List outputFaceList) { outputFaceList = new List(); - IndexFace firstF = m_FacesCollDict[inputFaceList[0]]; + IndexFace firstF = FacesCollDict[inputFaceList[0]]; int currProcFace = inputFaceList[0]; inputFaceList.RemoveAt(0); // remove the first face from the list bool merged = false; @@ -664,7 +677,7 @@ void TryMergeFaces(List inputFaceList, out List outputFaceList) else { currFaceIdx = pairedFace.Item1; - currFace = m_FacesCollDict[currFaceIdx]; + currFace = FacesCollDict[currFaceIdx]; // Need to reverse the face boundaries. Remove the entries in the Dict first, reverse the face, and add them back for (int cidx = 0; cidx < currFace.OuterAndInnerBoundaries.Count; ++cidx) @@ -693,7 +706,7 @@ void TryMergeFaces(List inputFaceList, out List outputFaceList) else { currFaceIdx = pairedFace.Item1; - currFace = m_FacesCollDict[currFaceIdx]; + currFace = FacesCollDict[currFaceIdx]; idx = pairedFace.Item2; } @@ -827,15 +840,18 @@ void TryMergeFaces(List inputFaceList, out List outputFaceList) // Find the largest loop and put it in the first position signifying the outer loop and the rest are the inner loops int largestPerimeterIdx = 0; double largestPerimeter = 0.0; - for (int i = 0; i < finalLoops.Count; i++) + for (int ii = 0; ii < finalLoops.Count; ii++) { double loopPerimeter = 0.0; - foreach (IndexSegment line in finalLoops[i]) - loopPerimeter += line.Extent (ref m_MeshVertices); + foreach (IndexSegment line in finalLoops[ii]) + { + loopPerimeter += line.Extent(MeshVertices); + } + if (loopPerimeter > largestPerimeter) { largestPerimeter = loopPerimeter; - largestPerimeterIdx = i; + largestPerimeterIdx = ii; } } // We need to move the largest loop into the head if it is not @@ -869,24 +885,24 @@ void TryMergeFaces(List inputFaceList, out List outputFaceList) } } - mergedFace = new IndexFace(newFaceVertsLoops, ref m_MeshVertices); + mergedFace = new IndexFace(newFaceVertsLoops, MeshVertices); inputFaceList.Remove(currFaceIdx); // Remove the merged face from segmentOfFaceDict - foreach (KeyValuePair idxSeg in m_FacesCollDict[currFaceIdx].OuterAndInnerBoundaries) + foreach (KeyValuePair idxSeg in FacesCollDict[currFaceIdx].OuterAndInnerBoundaries) { segmentOfFaceDict.Remove(idxSeg.Value); } - if (m_FacesCollDict.ContainsKey(currFaceIdx)) - m_FacesCollDict.Remove(currFaceIdx); + if (FacesCollDict.ContainsKey(currFaceIdx)) + FacesCollDict.Remove(currFaceIdx); merged = true; break; // Once there is an edge merged, create a new face and continue the process of merging } - int lastFaceID = faceIdxOffset++; // The new index is always the next one in the collection was inserted based on the seq order + int lastFaceID = FaceIdxOffset++; // The new index is always the next one in the collection was inserted based on the seq order if (mergedFace != null) - m_FacesCollDict.Add(lastFaceID, mergedFace); + FacesCollDict.Add(lastFaceID, mergedFace); if (!merged) { @@ -895,7 +911,7 @@ void TryMergeFaces(List inputFaceList, out List outputFaceList) if (inputFaceList.Count > 0) { - firstF = m_FacesCollDict[inputFaceList[0]]; + firstF = FacesCollDict[inputFaceList[0]]; // Remove the merged face from segmentOfFaceDict foreach (KeyValuePair idxSeg in firstF.OuterAndInnerBoundaries) @@ -918,8 +934,8 @@ void TryMergeFaces(List inputFaceList, out List outputFaceList) outputFaceList.Add(lastFaceID); // Remove merged face from the Dict - if (m_FacesCollDict.ContainsKey(currProcFace)) - m_FacesCollDict.Remove(currProcFace); + if (FacesCollDict.ContainsKey(currProcFace)) + FacesCollDict.Remove(currProcFace); if (inputFaceList.Count > 0) { @@ -944,10 +960,23 @@ void TryMergeFaces(List inputFaceList, out List outputFaceList) } } + /// + /// Appends the merged face index to the HashSet and List + /// We duplicate the indecies to List to have access by index in constant time. + /// The overhead for population and destruction of the list of integers isn't noticeable, + /// comparing to the speedup after removing ElementAt calls for HashSet + /// + /// The merged face index. + void AddMergedFaceIndex(int faceIdx) + { + MergedFaceSet.Add(faceIdx); + MergedFaceIndices.Add(faceIdx); + } + bool SegmentOfFaceToDict(ref IDictionary> segmentOfFaceDict, int indexFace) { IList entriesToRollback = new List(); - IndexFace theFace = m_FacesCollDict[indexFace]; + IndexFace theFace = FacesCollDict[indexFace]; try { int idx = 0; diff --git a/Source/Revit.IFC.Export/Utility/TypeObjectsCache.cs b/Source/Revit.IFC.Export/Utility/TypeObjectsCache.cs index 10fdc17a..fb244f81 100644 --- a/Source/Revit.IFC.Export/Utility/TypeObjectsCache.cs +++ b/Source/Revit.IFC.Export/Utility/TypeObjectsCache.cs @@ -38,7 +38,7 @@ public sealed class TypeObjectKey : Tuple + /// Extracts the unit handles to assign to a project + /// + /// Unit handles set + public static HashSet GetUnitsToAssign() + { + return ExporterCacheManager.UnitsCache.GetUnitsToAssign(); + } + + /// + /// Creates units spesific to ExportAsCOBIE + /// + public static void CreateCobieUnits() + { + if (!ExporterCacheManager.ExportOptionsCache.ExportAsCOBIE) + return; + + IFCFile file = ExporterCacheManager.ExporterIFC?.GetFile(); + if (file == null) + return; + + // Derived imperial mass unit + { + IFCUnit unitType = IFCUnit.MassUnit; + IFCAnyHandle dims = IFCInstanceExporter.CreateDimensionalExponents(file, 0, 1, 0, 0, 0, 0, 0); + double factor = 0.45359237; // --> pound to kilogram + string convName = "pound"; + + IFCAnyHandle kilogramUnit = IFCInstanceExporter.CreateSIUnit(file, IFCUnit.MassUnit, IFCSIPrefix.Kilo, IFCSIUnitName.Gram); + IFCAnyHandle convFactor = IFCInstanceExporter.CreateMeasureWithUnit(file, Toolkit.IFCDataUtil.CreateAsMassMeasure(factor), kilogramUnit); + IFCAnyHandle massUnit = IFCInstanceExporter.CreateConversionBasedUnit(file, dims, unitType, convName, convFactor); + ExporterCacheManager.UnitsCache.RegisterUnitInfo(SpecTypeId.Mass, new UnitInfo(massUnit, factor, 0.0)); + } + + // Air Changes per Hour + { + IFCUnit unitType = IFCUnit.FrequencyUnit; + IFCAnyHandle dims = IFCInstanceExporter.CreateDimensionalExponents(file, 0, 0, -1, 0, 0, 0, 0); + double factor = 1.0 / 3600.0; // --> seconds to hours + string convName = "ACH"; + + IFCAnyHandle secondUnit = IFCInstanceExporter.CreateSIUnit(file, IFCUnit.TimeUnit, null, IFCSIUnitName.Second); + IFCAnyHandle convFactor = IFCInstanceExporter.CreateMeasureWithUnit(file, Toolkit.IFCDataUtil.CreateAsTimeMeasure(factor), secondUnit); + IFCAnyHandle achUnit = IFCInstanceExporter.CreateConversionBasedUnit(file, dims, unitType, convName, convFactor); + ExporterCacheManager.UnitsCache.RegisterUserDefinedUnit("ACH", achUnit); + ExporterCacheManager.UnitsCache.RegisterUnitInfo(SpecTypeId.ElectricalFrequency, new UnitInfo(achUnit, factor, 0.0)); + } + + } + + /// + /// Get mapped Revit data type by ifc measure name + /// + public static ForgeTypeId GetUnitSpecTypeFromString(string measureName) + { + if (IfcToRevitDataTypeMapping.TryGetValue(measureName, out ForgeTypeId forgeTypeId)) + { + return forgeTypeId; + } + return null; + } + + /// + /// Create units for some special cases + /// + /// The file + /// Revit data type + /// Created unit handle + static UnitInfo CreateSpecialCases(IFCFile file, ForgeTypeId specTypeId) + { + UnitInfo unitInfo = null; + + if (specTypeId.Equals(SpecTypeId.Currency)) + { + // Specific IfcMonetaryUnit + unitInfo = CreateCurrencyUnit(file); + } + else if (specTypeId.Equals(SpecTypeId.ColorTemperature)) + { + // Color Temperature is in fact ThermoDynamicTemperature + // Create is specific way to avoid conflict with real HVACTemperature + unitInfo = CreateColorTemperatureUnit(file); + } + else if (specTypeId.Equals(SpecTypeId.MassPerUnitArea)) + { + // A single unit 'since ifc4'. + // Make more generic improvement if there are more than one such unit + if (!ExporterCacheManager.ExportOptionsCache.ExportAsOlderThanIFC4) + { + DerivedAttributes attrib; + if (!DerivedUnitMapping.TryGetValue(specTypeId, out attrib)) + return null; + + attrib.ifcDrivedUnit = Toolkit.IFC4.IFCDerivedUnit.AREADENSITYUNIT; + attrib.userDefinedUnitName = string.Empty; + + return CreateUnitAsDerivedCommon(file, specTypeId, null, attrib); + } + } + + return unitInfo; + } + + /// + /// Creates spesific Currency unit + /// + static UnitInfo CreateCurrencyUnit(IFCFile file) + { + UnitInfo unitInfo = null; + + if (!ExporterCacheManager.ExportOptionsCache.ExportAs2x3CoordinationView2) + { + FormatOptions currencyFormatOptions = ExporterCacheManager.Document.GetUnits().GetFormatOptions(SpecTypeId.Currency); + ForgeTypeId currencySymbol = currencyFormatOptions.GetSymbolTypeId(); + + IFCAnyHandle currencyUnit = null; + + // Some of these are guesses for IFC2x3, since multiple currencies may use the same symbol, + // but no detail is given on which currency is being used. For IFC4, we just use the label. + if (!ExporterCacheManager.ExportOptionsCache.ExportAsOlderThanIFC4) + { + string currencyLabel = null; + try + { + currencyLabel = LabelUtils.GetLabelForSymbol(currencySymbol); + currencyUnit = IFCInstanceExporter.CreateMonetaryUnit4(file, currencyLabel); + } + catch + { + currencyUnit = null; + } + } + else + { + IFCCurrencyType? currencyType = null; + + if (currencySymbol.Equals(SymbolTypeId.UsDollar)) + { + currencyType = IFCCurrencyType.USD; + } + else if (currencySymbol.Equals(SymbolTypeId.EuroPrefix) || + currencySymbol.Equals(SymbolTypeId.EuroSuffix)) + { + currencyType = IFCCurrencyType.EUR; + } + else if (currencySymbol.Equals(SymbolTypeId.UkPound)) + { + currencyType = IFCCurrencyType.GBP; + } + else if (currencySymbol.Equals(SymbolTypeId.ChineseHongKongDollar)) + { + currencyType = IFCCurrencyType.HKD; + } + else if (currencySymbol.Equals(SymbolTypeId.Krone)) + { + currencyType = IFCCurrencyType.NOK; + } + else if (currencySymbol.Equals(SymbolTypeId.Shekel)) + { + currencyType = IFCCurrencyType.ILS; + } + else if (currencySymbol.Equals(SymbolTypeId.Yen)) + { + currencyType = IFCCurrencyType.JPY; + } + else if (currencySymbol.Equals(SymbolTypeId.Won)) + { + currencyType = IFCCurrencyType.KRW; + } + else if (currencySymbol.Equals(SymbolTypeId.Baht)) + { + currencyType = IFCCurrencyType.THB; + } + else if (currencySymbol.Equals(SymbolTypeId.Dong)) + { + currencyType = IFCCurrencyType.VND; + } + + if (currencyType.HasValue) + currencyUnit = IFCInstanceExporter.CreateMonetaryUnit2x3(file, currencyType.Value); + } + + if (currencyUnit != null) + { + unitInfo = new UnitInfo(currencyUnit, 1.0, 0.0); + ExporterCacheManager.UnitsCache.RegisterUserDefinedUnit("CURRENCY", currencyUnit); + } + } + return unitInfo; + } + + /// + /// Creates spesific Color Temperature unit + /// + static UnitInfo CreateColorTemperatureUnit(IFCFile file) + { + UnitInfo unitInfo = null; + if (file == null) + return unitInfo; + + IFCAnyHandle colorTempUnit = IFCInstanceExporter.CreateSIUnit(file, IFCUnit.ThermoDynamicTemperatureUnit, null, IFCSIUnitName.Kelvin); + (double scaleFactor, double offset) = GetScaleFactorAndOffset(UnitTypeId.Kelvin); + ExporterCacheManager.UnitsCache.RegisterUserDefinedUnit("COLORTEMPERATURE", colorTempUnit); + + return new UnitInfo(colorTempUnit, scaleFactor, offset); + } + + + #region Classes to keep units attributes + /// + /// A structure to contain information about IfcSIUnit + /// + class SIUnitInfo + { + public SIUnitInfo(IFCSIUnitName inIfcSIUnitName, IFCSIPrefix? inIfcSIPrefix, bool inIsDefault) + { + ifcSIUnitName = inIfcSIUnitName; + ifcSIPrefix = inIfcSIPrefix; + isDefault = inIsDefault; + } + + readonly public IFCSIUnitName ifcSIUnitName; + readonly public IFCSIPrefix? ifcSIPrefix; + readonly public bool isDefault; + } + + /// + /// A structure to contain information about IfcSIUnit + /// + class SIAttributes + { + public SIAttributes(IFCUnit inIfcUnitType, IDictionary inSIUnitInfoDict) + { + ifcUnitType = inIfcUnitType; + siUnitInfoDict = inSIUnitInfoDict; + } + + readonly public IFCUnit ifcUnitType; + readonly public IDictionary siUnitInfoDict; + }; + + /// + /// A structure to contain information about IfcConversionBasedUnit + /// + class ConversionUnitInfo + { + public ConversionUnitInfo(string inConversionName, bool inIsDefault) + { + conversionName = inConversionName; + isDefault = inIsDefault; + } + + readonly public string conversionName; + readonly public bool isDefault; + } + + /// + /// A structure to contain information about IfcConversionBasedUnit + /// + class ConversionAttributes + { + public ConversionAttributes(IFCUnit inUnit, string inMeasureName, ForgeTypeId inBaseSI, int inLengthExponent, int inMassExponent, + int inTimeExponent, int inElectricCurrentExponent, int inThermodynamicTemperatureExponent, + int inAmountOfSubstanceExponent, int inLuminousIntensityExponent, IDictionary inConversionInfoDict) + { + ifcUnitType = inUnit; + measureName = inMeasureName; + baseSI = inBaseSI; + length = inLengthExponent; + mass = inMassExponent; + time = inTimeExponent; + electricCurrent = inElectricCurrentExponent; + thermodynamicTemperature = inThermodynamicTemperatureExponent; + amountOfSubstance = inAmountOfSubstanceExponent; + luminousIntensity = inLuminousIntensityExponent; + conversionInfoDict = inConversionInfoDict; + } + + readonly public IFCUnit ifcUnitType; + readonly public string measureName; + readonly public ForgeTypeId baseSI; + readonly public int length; + readonly public int mass; + readonly public int time; + readonly public int electricCurrent; + readonly public int thermodynamicTemperature; + readonly public int amountOfSubstance; + readonly public int luminousIntensity; + readonly public IDictionary conversionInfoDict; + + } + + /// + /// A structure to contain information about IfcDerivedUnit + /// + class DerivedInfo + { + public DerivedInfo(double? inExtraScale, bool inIsDefault, IList> inDerivedElements) + { + extraScale = inExtraScale; + isDefault = inIsDefault; + derivedElements = inDerivedElements; + } + + public double? extraScale; + public bool isDefault; + public IList> derivedElements; + } + + /// + /// A structure to contain information about IfcDerivedUnit + /// + class DerivedAttributes + { + public DerivedAttributes(Enum inDerivedUnitType, string inUserDefinedUnitName, IDictionary inDerivedInfoDict) + { + ifcDrivedUnit = inDerivedUnitType; + userDefinedUnitName = inUserDefinedUnitName; + derivedInfoDict = inDerivedInfoDict; + } + + + public Enum ifcDrivedUnit; + public string userDefinedUnitName; + public IDictionary derivedInfoDict; + } + #endregion + + + #region Unit creation methods + /// + /// Creates eighter selected in Revit or default ifc unit of apropriate type. + /// + /// The file + /// Revit data type + /// False - look for the mapping for selected Revit displayed unit. + /// True - look for the default unit in the mappings + /// Created unit handle + static UnitInfo CreateUnitFromMappings(IFCFile file, ForgeTypeId specTypeId, bool byDefault) + { + ForgeTypeId selectedUnitTypeId = null; + + if (!byDefault) + { + FormatOptions formatOptions = ExporterCacheManager.Document.GetUnits().GetFormatOptions(specTypeId); + selectedUnitTypeId = formatOptions.GetUnitTypeId(); + + if (selectedUnitTypeId == null) + return null; + } + + UnitInfo createdUnit = CreateUnitAsSI(file, specTypeId, selectedUnitTypeId); + + if (createdUnit == null) + createdUnit = CreateUnitAsConversionBased(file, specTypeId, selectedUnitTypeId); + + if (createdUnit == null) + createdUnit = CreateUnitAsDerived(file, specTypeId, selectedUnitTypeId); + + return createdUnit; + } + + /// + /// Creates IfcSIUnit handle for input data/unit types if present in mapping. + /// If unit type is null - look for default unit. + /// + /// The file + /// Revit data type + /// Revit unit type + /// Created unit handle + static UnitInfo CreateUnitAsSI(IFCFile file, ForgeTypeId specTypeId, ForgeTypeId selectedUnitTypeId) + { + if (file == null) + return null; + + SIAttributes attrib; + if (!SIUnitMapping.TryGetValue(specTypeId, out attrib)) + return null; + + if (attrib.siUnitInfoDict == null) + return null; + + ForgeTypeId unitTypeId = null; + IFCSIUnitName ifcSIUnitName = 0; + IFCSIPrefix? ifcSIPrefix = null; + IFCUnit ifcUnit; + + if (selectedUnitTypeId == null) + { + // Look for default unit + foreach (var pair in attrib.siUnitInfoDict) + { + if (pair.Value.isDefault) + { + unitTypeId = pair.Key; + ifcSIUnitName = pair.Value.ifcSIUnitName; + ifcSIPrefix = pair.Value.ifcSIPrefix; + break; + } + } + } + else + { + SIUnitInfo siInfo = null; + if (attrib.siUnitInfoDict.TryGetValue(selectedUnitTypeId, out siInfo)) + { + unitTypeId = selectedUnitTypeId; + ifcSIUnitName = siInfo.ifcSIUnitName; + ifcSIPrefix = siInfo.ifcSIPrefix; + } + } + ifcUnit = attrib.ifcUnitType; + + if (unitTypeId == null) + return null; + + + + IFCAnyHandle siUnit = IFCInstanceExporter.CreateSIUnit(file, ifcUnit, ifcSIPrefix, ifcSIUnitName); + (double scaleFactor, double offset) = GetScaleFactorAndOffset(unitTypeId); + return new UnitInfo(siUnit, scaleFactor, offset); + } + + /// + /// Creates IfcConversionBasedUnit handle for input data/unit types if present in mapping. + /// If unit type is null - look for default unit. + /// + /// The file + /// Revit data type + /// Revit unit type + /// Created unit handle + static UnitInfo CreateUnitAsConversionBased(IFCFile file, ForgeTypeId specTypeId, ForgeTypeId selectedUnitTypeId) + { + if (file == null) + return null; + + ConversionAttributes attrib; + if (!ConversionBasedUnitMapping.TryGetValue(specTypeId, out attrib)) + return null; + + if (attrib.conversionInfoDict == null) + return null; + + ForgeTypeId unitTypeId = null; + IFCUnit ifcUnit; + string ifcMeasureName = null; + ForgeTypeId baseTypeId = null; + + int length = 0; + int mass = 0; + int time = 0; + int electricCurrent = 0; + int thermodynamicTemperature = 0; + int amountOfSubstance = 0; + int luminousIntensity = 0; + + string conversionName = null; + + if (selectedUnitTypeId == null) + { + // Look for default unit + foreach (var pair in attrib.conversionInfoDict) + { + if (pair.Value.isDefault) + { + unitTypeId = pair.Key; + conversionName = pair.Value.conversionName; + break; + } + } + } + else + { + ConversionUnitInfo conversionInfo = null; + if (attrib.conversionInfoDict.TryGetValue(selectedUnitTypeId, out conversionInfo)) + { + unitTypeId = selectedUnitTypeId; + conversionName = conversionInfo.conversionName; + } + } + ifcUnit = attrib.ifcUnitType; + ifcMeasureName = attrib.measureName; + baseTypeId = attrib.baseSI; + + length = attrib.length; + mass = attrib.mass; + time = attrib.time; + electricCurrent = attrib.electricCurrent; + thermodynamicTemperature = attrib.thermodynamicTemperature; + amountOfSubstance = attrib.amountOfSubstance; + luminousIntensity = attrib.luminousIntensity; + + conversionName = GetCobieUnitName(conversionName); + + if (unitTypeId == null) + return null; + + IFCAnyHandle siBaseUnit = GetOrCreateAuxiliaryUnit(file, baseTypeId); + if (siBaseUnit == null) + return null; + + double siScaleFactor = UnitUtils.Convert(1.0, unitTypeId, baseTypeId); + IFCAnyHandle dims = IFCInstanceExporter.CreateDimensionalExponents(file, length, mass, time, electricCurrent, thermodynamicTemperature, amountOfSubstance, luminousIntensity); + IFCAnyHandle conversionFactor = IFCInstanceExporter.CreateMeasureWithUnit(file, IFCDataUtil.CreateAsMeasure(siScaleFactor, ifcMeasureName), siBaseUnit); + IFCAnyHandle conversionUnit = IFCInstanceExporter.CreateConversionBasedUnit(file, dims, ifcUnit, conversionName, conversionFactor); + + double scaleFactor = UnitUtils.ConvertFromInternalUnits(1.0, unitTypeId); + return new UnitInfo(conversionUnit, scaleFactor, 0.0); + } + + /// + /// Creates IfcDerivedUnit handle for input data/unit types if present in mapping. + /// If unit type is null - look for default unit. + /// + /// The file + /// Revit data type + /// Revit unit type + /// Created unit handle + static UnitInfo CreateUnitAsDerived(IFCFile file, ForgeTypeId specTypeId, ForgeTypeId selectedUnitTypeId) + { + DerivedAttributes attrib; + if (!DerivedUnitMapping.TryGetValue(specTypeId, out attrib)) + return null; + + if (attrib.derivedInfoDict == null) + return null; + + return CreateUnitAsDerivedCommon(file, specTypeId, selectedUnitTypeId, attrib); + } + + /// + /// Creates IfcDerivedUnit handle for input data/unit types if present in mapping. + /// If unit type is null - look for default unit. + /// + /// The file + /// Revit data type + /// Revit unit type + /// Derived unit attributes + /// Created unit handle + static UnitInfo CreateUnitAsDerivedCommon(IFCFile file, ForgeTypeId specTypeId, ForgeTypeId selectedUnitTypeId, DerivedAttributes attrib) + { + if (file == null) + return null; + + ForgeTypeId unitTypeId = null; + string userDefName = null; + double? extraScale = null; + IList> derivedElements = null; + + if (selectedUnitTypeId == null) + { + // Look for default unit + foreach (var pair in attrib.derivedInfoDict) + { + if (pair.Value.isDefault) + { + unitTypeId = pair.Key; + extraScale = pair.Value.extraScale; + derivedElements = pair.Value.derivedElements; + break; + } + } + } + else + { + DerivedInfo derivedInfo = null; + if (attrib.derivedInfoDict.TryGetValue(selectedUnitTypeId, out derivedInfo)) + { + unitTypeId = selectedUnitTypeId; + extraScale = derivedInfo.extraScale; + derivedElements = derivedInfo.derivedElements; + } + } + Enum ifcDerivedUnit = attrib.ifcDrivedUnit; + userDefName = attrib.userDefinedUnitName; + + if (unitTypeId == null || (derivedElements?.Count ?? 0) == 0) + return null; + + ISet elements = new HashSet(); + foreach (var pair in derivedElements) + { + IFCAnyHandle baseSIUnit = GetOrCreateAuxiliaryUnit(file, pair.Item1); + if (baseSIUnit == null) + return null; + + elements.Add(GetOrCreateDerivedUnitElement(file, baseSIUnit, pair.Item2)); + } + + IFCAnyHandle derivedUnitHnd = IFCInstanceExporter.CreateDerivedUnit(file, elements, ifcDerivedUnit, userDefName); + + if (!string.IsNullOrEmpty(userDefName)) + { + string capitalName = NamingUtil.RemoveSpaces(userDefName.ToUpper()); + ExporterCacheManager.UnitsCache.RegisterUserDefinedUnit(capitalName, derivedUnitHnd); + } + + double scaleFactor = UnitUtils.ConvertFromInternalUnits(1.0, unitTypeId); + scaleFactor *= extraScale.HasValue ? extraScale.Value : 1.0; + return new UnitInfo(derivedUnitHnd, scaleFactor, 0.0); + + } + + /// + /// Creates IfcDerivedUnitElement handle with caching + /// If unit type is null - look for default unit. + /// + /// The file + /// Unit handle + /// The exponent + /// Created IfcDerivedUnitElement handle + static IFCAnyHandle GetOrCreateDerivedUnitElement(IFCFile file, IFCAnyHandle unit, int exponent) + { + Tuple pair = Tuple.Create(unit, exponent); + IFCAnyHandle derivedUnitElement = null; + if (!ExporterCacheManager.UnitsCache.FindDerivedUnitElement(pair, out derivedUnitElement)) + { + derivedUnitElement = IFCInstanceExporter.CreateDerivedUnitElement(file, unit, exponent); + ExporterCacheManager.UnitsCache.RegisterDerivedUnit(pair, derivedUnitElement); + } + return derivedUnitElement; + } + + /// + /// Creates auxiliary unit handle with caching if present in mapping + /// + /// The file + /// Revit unit type + /// Auxiliary unit handle + static IFCAnyHandle GetOrCreateAuxiliaryUnit(IFCFile file, ForgeTypeId unitTypeId) + { + IFCAnyHandle auxiliaryUnit = null; + if (ExporterCacheManager.UnitsCache.FindAuxiliaryUnit(unitTypeId, out auxiliaryUnit)) + return auxiliaryUnit; + + Tuple ifcAuxilaryValue = null; + if (AuxiliaryUnitMapping.TryGetValue(unitTypeId, out ifcAuxilaryValue) == false) + return null; + + auxiliaryUnit = IFCInstanceExporter.CreateSIUnit(file, ifcAuxilaryValue.Item3, ifcAuxilaryValue.Item2, ifcAuxilaryValue.Item1); + + ExporterCacheManager.UnitsCache.RegisterAuxiliaryUnit(unitTypeId, auxiliaryUnit); + + return auxiliaryUnit; + } + + /// + /// Calculates scaling and offset values for conversion + /// from Revit internal units + /// + /// Revit unit type + /// Auxiliary unit handle + static (double offset, double scaleFactor) GetScaleFactorAndOffset(ForgeTypeId unitTypeId) + { + double value0 = UnitUtils.ConvertFromInternalUnits(0.0, unitTypeId); + double value1 = UnitUtils.ConvertFromInternalUnits(1.0, unitTypeId); + + double offset = value0; + double scaleFactor = (value1 - value0); + if (MathUtil.IsAlmostZero(scaleFactor)) + scaleFactor = 1.0; + + return (scaleFactor, offset); + } + + /// + /// Get mapped unit name for ExportAsCOBIE + /// + static string GetCobieUnitName(string unitName) + { + if (!ExporterCacheManager.ExportOptionsCache.ExportAsCOBIE) + return unitName; + + string cobieName; + if (!CobieUnitNameMapping.TryGetValue(unitName, out cobieName)) + cobieName = unitName.ToLower(); + + return cobieName; + } + + #endregion + + + #region Unit Mapping Tables + /// + /// The dictionary contains the information to create auxiliary ifc unit for a Revit unit type + /// These are the auxiliary unit handles that don't go to IfcUnitAssignment + /// + private static readonly Dictionary> AuxiliaryUnitMapping = new Dictionary>() + { + { UnitTypeId.Meters, Tuple.Create(IFCSIUnitName.Metre, null, IFCUnit.LengthUnit) }, + { UnitTypeId.Decimeters, Tuple.Create(IFCSIUnitName.Metre, IFCSIPrefix.Deci, IFCUnit.LengthUnit) }, + { UnitTypeId.SquareMeters, Tuple.Create(IFCSIUnitName.Square_Metre, null, IFCUnit.AreaUnit) }, + { UnitTypeId.CubicMeters, Tuple.Create(IFCSIUnitName.Cubic_Metre, null, IFCUnit.VolumeUnit) }, + { UnitTypeId.Kilograms, Tuple.Create(IFCSIUnitName.Gram, IFCSIPrefix.Kilo, IFCUnit.MassUnit) }, + { UnitTypeId.Seconds, Tuple.Create(IFCSIUnitName.Second, null, IFCUnit.TimeUnit) }, + { UnitTypeId.Amperes, Tuple.Create(IFCSIUnitName.Ampere, null, IFCUnit.ElectricCurrentUnit) }, + { UnitTypeId.Kelvin, Tuple.Create(IFCSIUnitName.Kelvin, null, IFCUnit.ThermoDynamicTemperatureUnit) }, + { UnitTypeId.Candelas, Tuple.Create(IFCSIUnitName.Candela, null, IFCUnit.LuminousIntensityUnit) }, + { UnitTypeId.Radians, Tuple.Create(IFCSIUnitName.Radian, null, IFCUnit.PlaneAngleUnit) }, + { UnitTypeId.Lumens, Tuple.Create(IFCSIUnitName.Lumen, null, IFCUnit.LuminousFluxUnit) }, + { UnitTypeId.Newtons, Tuple.Create(IFCSIUnitName.Newton, null, IFCUnit.ForceUnit) }, + + }; + + + /// + /// The dictionary contains the information to create IfcSIUnit for a Revit data type + /// + private static readonly Dictionary SIUnitMapping = new Dictionary() + { + { SpecTypeId.Mass, new SIAttributes(IFCUnit.MassUnit, new Dictionary() + { + { UnitTypeId.Kilograms, new SIUnitInfo(IFCSIUnitName.Gram, IFCSIPrefix.Kilo, true) } + } ) + }, + { SpecTypeId.Time, new SIAttributes(IFCUnit.TimeUnit, new Dictionary() + { + { UnitTypeId.Seconds, new SIUnitInfo(IFCSIUnitName.Second, null, true) } + } ) + }, + { SpecTypeId.Current, new SIAttributes(IFCUnit.ElectricCurrentUnit, new Dictionary() + { + { UnitTypeId.Amperes, new SIUnitInfo(IFCSIUnitName.Ampere, null, true) } + } ) + }, + { SpecTypeId.LuminousIntensity, new SIAttributes(IFCUnit.LuminousIntensityUnit, new Dictionary() + { + { UnitTypeId.Candelas, new SIUnitInfo(IFCSIUnitName.Candela, null, true) } + } ) + }, + { SpecTypeId.HvacTemperature, new SIAttributes(IFCUnit.ThermoDynamicTemperatureUnit, new Dictionary() + { + { UnitTypeId.Celsius, new SIUnitInfo(IFCSIUnitName.Degree_Celsius, null, true) }, + { UnitTypeId.Kelvin, new SIUnitInfo(IFCSIUnitName.Kelvin, null, false) } + } ) + }, + { SpecTypeId.Length, new SIAttributes(IFCUnit.LengthUnit, new Dictionary() + { + { UnitTypeId.Meters, new SIUnitInfo(IFCSIUnitName.Metre, null, false) }, + { UnitTypeId.MetersCentimeters, new SIUnitInfo(IFCSIUnitName.Metre, null, false) }, + { UnitTypeId.Centimeters, new SIUnitInfo(IFCSIUnitName.Metre, IFCSIPrefix.Centi, false) }, + { UnitTypeId.Millimeters, new SIUnitInfo(IFCSIUnitName.Metre, IFCSIPrefix.Milli, false) } + } ) + }, + { SpecTypeId.Area, new SIAttributes(IFCUnit.AreaUnit, new Dictionary() + { + { UnitTypeId.SquareMeters, new SIUnitInfo(IFCSIUnitName.Square_Metre, null, false) }, + { UnitTypeId.SquareCentimeters, new SIUnitInfo(IFCSIUnitName.Square_Metre, IFCSIPrefix.Centi, false) }, + { UnitTypeId.Millimeters, new SIUnitInfo(IFCSIUnitName.Square_Metre, IFCSIPrefix.Milli, false) } + } ) + }, + { SpecTypeId.Volume, new SIAttributes(IFCUnit.VolumeUnit, new Dictionary() + { + { UnitTypeId.CubicMeters, new SIUnitInfo(IFCSIUnitName.Cubic_Metre, null, false) }, + { UnitTypeId.Liters, new SIUnitInfo(IFCSIUnitName.Cubic_Metre, IFCSIPrefix.Deci, false) }, + { UnitTypeId.CubicCentimeters, new SIUnitInfo(IFCSIUnitName.Cubic_Metre, IFCSIPrefix.Centi, false) }, + { UnitTypeId.CubicMillimeters, new SIUnitInfo(IFCSIUnitName.Cubic_Metre, IFCSIPrefix.Milli, false) } + } ) + }, + { SpecTypeId.Angle, new SIAttributes(IFCUnit.PlaneAngleUnit, new Dictionary() + { + { UnitTypeId.Radians, new SIUnitInfo(IFCSIUnitName.Radian, null, false) } + } ) + }, + { SpecTypeId.Force, new SIAttributes(IFCUnit.ForceUnit, new Dictionary() + { + { UnitTypeId.Newtons, new SIUnitInfo(IFCSIUnitName.Newton, null, true) }, + { UnitTypeId.Dekanewtons, new SIUnitInfo(IFCSIUnitName.Newton, IFCSIPrefix.Deca, false) }, + { UnitTypeId.Kilonewtons, new SIUnitInfo(IFCSIUnitName.Newton, IFCSIPrefix.Kilo, false) }, + { UnitTypeId.Meganewtons, new SIUnitInfo(IFCSIUnitName.Newton, IFCSIPrefix.Mega, false) } + } ) + }, + { SpecTypeId.ElectricalFrequency, new SIAttributes(IFCUnit.FrequencyUnit, new Dictionary() + { + { UnitTypeId.Hertz, new SIUnitInfo(IFCSIUnitName.Hertz, null, true) } + } ) + }, + { SpecTypeId.ElectricalPotential, new SIAttributes(IFCUnit.ElectricVoltageUnit, new Dictionary() + { + { UnitTypeId.Volts, new SIUnitInfo(IFCSIUnitName.Volt, null, true) } + } ) + }, + { SpecTypeId.HvacPower, new SIAttributes(IFCUnit.PowerUnit, new Dictionary() + { + { UnitTypeId.Watts, new SIUnitInfo(IFCSIUnitName.Watt, null, true) } + } ) + }, + { SpecTypeId.Illuminance, new SIAttributes(IFCUnit.IlluminanceUnit, new Dictionary() + { + { UnitTypeId.Lux, new SIUnitInfo(IFCSIUnitName.Lux, null, true) } + } ) + }, + { SpecTypeId.LuminousFlux, new SIAttributes(IFCUnit.LuminousFluxUnit, new Dictionary() + { + { UnitTypeId.Lumens, new SIUnitInfo(IFCSIUnitName.Lumen, null, true) } + } ) + }, + { SpecTypeId.Energy, new SIAttributes(IFCUnit.EnergyUnit, new Dictionary() + { + { UnitTypeId.Joules, new SIUnitInfo(IFCSIUnitName.Joule, null, true) } + } ) + }, + { SpecTypeId.HvacPressure, new SIAttributes(IFCUnit.PressureUnit, new Dictionary() + { + { UnitTypeId.Pascals, new SIUnitInfo(IFCSIUnitName.Pascal, null, true) }, + { UnitTypeId.Kilopascals, new SIUnitInfo(IFCSIUnitName.Pascal, IFCSIPrefix.Kilo, false) }, + { UnitTypeId.Megapascals, new SIUnitInfo(IFCSIUnitName.Pascal, IFCSIPrefix.Mega, false) } + } ) + } + }; + + + /// + /// The dictionary contains the information to create IfcConversionBasedUnit for a Revit data type + /// + private static readonly Dictionary ConversionBasedUnitMapping = new Dictionary() + { + { SpecTypeId.Length, new ConversionAttributes(IFCUnit.LengthUnit, "IfcLengthMeasure", UnitTypeId.Meters, 1, 0, 0, 0, 0, 0, 0, new Dictionary() { + { UnitTypeId.Feet, new ConversionUnitInfo("FOOT", true) }, + { UnitTypeId.FeetFractionalInches, new ConversionUnitInfo("FOOT", false)}, + { UnitTypeId.Inches, new ConversionUnitInfo("INCH", false)}, + { UnitTypeId.FractionalInches, new ConversionUnitInfo("INCH", false) } } ) + }, + { SpecTypeId.Area, new ConversionAttributes(IFCUnit.AreaUnit, "IfcAreaMeasure", UnitTypeId.SquareMeters, 2, 0, 0, 0, 0, 0, 0, new Dictionary() { + { UnitTypeId.SquareFeet, new ConversionUnitInfo("SQUARE FOOT", true) }, + { UnitTypeId.SquareInches, new ConversionUnitInfo("SQUARE INCH", false) } } ) + }, + { SpecTypeId.Volume, new ConversionAttributes(IFCUnit.VolumeUnit, "IfcVolumeMeasure", UnitTypeId.CubicMeters, 3, 0, 0, 0, 0, 0, 0, new Dictionary() { + { UnitTypeId.CubicFeet, new ConversionUnitInfo("CUBIC FOOT", true) }, + { UnitTypeId.CubicInches, new ConversionUnitInfo("CUBIC INCH", false) } } ) + }, + { SpecTypeId.Angle, new ConversionAttributes(IFCUnit.PlaneAngleUnit, "IfcPlaneAngleMeasure", UnitTypeId.Radians, 0, 0, 0, 0, 0, 0, 0, new Dictionary() { + { UnitTypeId.Degrees, new ConversionUnitInfo("DEGREE", true) }, + { UnitTypeId.DegreesMinutes, new ConversionUnitInfo("DEGREE", false) }, + { UnitTypeId.Gradians, new ConversionUnitInfo("GRAD", false) } } ) + }, + { SpecTypeId.Force, new ConversionAttributes(IFCUnit.ForceUnit, "IfcForceMeasure", UnitTypeId.Newtons, 1, 1, -2, 0, 0, 0, 0, new Dictionary() { + { UnitTypeId.KilogramsForce, new ConversionUnitInfo("KILOGRAM-FORCE", false) }, + { UnitTypeId.TonnesForce, new ConversionUnitInfo("TONN-FORCE", false) }, + { UnitTypeId.UsTonnesForce, new ConversionUnitInfo("USTONN-FORCE", false) }, + { UnitTypeId.PoundsForce, new ConversionUnitInfo("POUND-FORCE", false) }, + { UnitTypeId.Kips, new ConversionUnitInfo("CUBIC INCH", false) } } ) + } + }; + + /// + /// The dictionary contains the information to create IfcDerivedUnit for a Revit data type + /// + private static readonly Dictionary DerivedUnitMapping = new Dictionary() + { + { SpecTypeId.MassDensity, new DerivedAttributes(IFCDerivedUnitEnum.MassDensityUnit, null, new Dictionary() { + { UnitTypeId.KilogramsPerCubicMeter, new DerivedInfo(null, true, new List>() { + Tuple.Create(UnitTypeId.Kilograms, 1), + Tuple.Create(UnitTypeId.Meters, -3) } ) + } } ) + }, + { SpecTypeId.PipingDensity, new DerivedAttributes(IFCDerivedUnitEnum.IonConcentrationUnit, null, new Dictionary() { + { UnitTypeId.KilogramsPerCubicMeter, new DerivedInfo(null, true, new List>() { + Tuple.Create(UnitTypeId.Kilograms, 1), + Tuple.Create(UnitTypeId.Meters, -3) } ) + } } ) + }, + { SpecTypeId.MomentOfInertia, new DerivedAttributes(IFCDerivedUnitEnum.MomentOfInertiaUnit, null, new Dictionary() { + { UnitTypeId.MetersToTheFourthPower, new DerivedInfo(null, true, new List>() { + Tuple.Create(UnitTypeId.Meters, 4) } ) + } } ) + }, + { SpecTypeId.HeatTransferCoefficient, new DerivedAttributes(IFCDerivedUnitEnum.ThermalTransmittanceUnit, null, new Dictionary() { + { UnitTypeId.WattsPerSquareMeterKelvin, new DerivedInfo(null, true, new List>() { + Tuple.Create(UnitTypeId.Kilograms, 1), + Tuple.Create(UnitTypeId.Kelvin, -1), + Tuple.Create(UnitTypeId.Seconds, -3) } ) + } } ) + }, + { SpecTypeId.ThermalConductivity, new DerivedAttributes(IFCDerivedUnitEnum.ThermalConductanceUnit, null, new Dictionary() { + { UnitTypeId.WattsPerMeterKelvin, new DerivedInfo(null, true, new List>() { + Tuple.Create(UnitTypeId.Kilograms, 1), + Tuple.Create(UnitTypeId.Meters, 1), + Tuple.Create(UnitTypeId.Kelvin, -1), + Tuple.Create(UnitTypeId.Seconds, -3) } ) + } } ) + }, + { SpecTypeId.AirFlow, new DerivedAttributes(IFCDerivedUnitEnum.VolumetricFlowRateUnit, null, new Dictionary() { + { UnitTypeId.CubicMetersPerSecond, new DerivedInfo(null, true, new List>() { + Tuple.Create(UnitTypeId.Meters, 3), + Tuple.Create(UnitTypeId.Seconds, -1) } ) + }, + { UnitTypeId.LitersPerSecond, new DerivedInfo(null, false, new List>() { + Tuple.Create(UnitTypeId.Decimeters, 3), + Tuple.Create(UnitTypeId.Seconds, -1) } ) + } } ) + }, + { SpecTypeId.PipingMassPerTime, new DerivedAttributes(IFCDerivedUnitEnum.MassFlowRateUnit, null, new Dictionary() { + { UnitTypeId.KilogramsPerSecond, new DerivedInfo(null, true, new List>() { + Tuple.Create(UnitTypeId.Kilograms, 1), + Tuple.Create(UnitTypeId.Seconds, -1) } ) + } } ) + }, + { SpecTypeId.AngularSpeed, new DerivedAttributes(IFCDerivedUnitEnum.RotationalFrequencyUnit, null, new Dictionary() { + { UnitTypeId.RevolutionsPerSecond, new DerivedInfo(null, true, new List>() { + Tuple.Create(UnitTypeId.Seconds, -1) } ) + } } ) + }, + { SpecTypeId.Wattage, new DerivedAttributes(IFCDerivedUnitEnum.SoundPowerUnit, null, new Dictionary() { + { UnitTypeId.Watts, new DerivedInfo(null, true, new List>() { + Tuple.Create(UnitTypeId.Kilograms, 1), + Tuple.Create(UnitTypeId.Meters, 2), + Tuple.Create(UnitTypeId.Seconds, -3) } ) + } } ) + }, + { SpecTypeId.HvacPressure, new DerivedAttributes(IFCDerivedUnitEnum.SoundPressureUnit, null, new Dictionary() { + { UnitTypeId.Pascals, new DerivedInfo(null, true, new List>() { + Tuple.Create(UnitTypeId.Kilograms, 1), + Tuple.Create(UnitTypeId.Meters, -1), + Tuple.Create(UnitTypeId.Seconds, -2) } ) + } } ) + }, + { SpecTypeId.HvacVelocity, new DerivedAttributes(IFCDerivedUnitEnum.LinearVelocityUnit, null, new Dictionary() { + { UnitTypeId.MetersPerSecond, new DerivedInfo(null, true, new List>() { + Tuple.Create(UnitTypeId.Meters, 1), + Tuple.Create(UnitTypeId.Seconds, -1) } ) + } } ) + }, + { SpecTypeId.LinearForce, new DerivedAttributes(IFCDerivedUnitEnum.LinearForceUnit, null, new Dictionary() { + { UnitTypeId.NewtonsPerMeter, new DerivedInfo(null, true, new List>() { + Tuple.Create(UnitTypeId.Kilograms, 1), + Tuple.Create(UnitTypeId.Seconds, -2) } ) + } } ) + }, + { SpecTypeId.AreaForce, new DerivedAttributes(IFCDerivedUnitEnum.PlanarForceUnit, null, new Dictionary() { + { UnitTypeId.NewtonsPerSquareMeter, new DerivedInfo(null, true, new List>() { + Tuple.Create(UnitTypeId.Kilograms, 1), + Tuple.Create(UnitTypeId.Meters, -1), + Tuple.Create(UnitTypeId.Seconds, -2) } ) + } } ) + }, + { SpecTypeId.SpecificHeat, new DerivedAttributes(IFCDerivedUnitEnum.SpecificHeatCapacityUnit, null, new Dictionary() { + { UnitTypeId.JoulesPerKilogramDegreeCelsius, new DerivedInfo(null, true, new List>() { + Tuple.Create(UnitTypeId.Meters, 2), + Tuple.Create(UnitTypeId.Seconds, -2), + Tuple.Create(UnitTypeId.Kelvin, -1) } ) + } } ) + }, + { SpecTypeId.HvacPowerDensity, new DerivedAttributes(IFCDerivedUnitEnum.HeatFluxDensityUnit, null, new Dictionary() { + { UnitTypeId.WattsPerSquareMeter, new DerivedInfo(null, true, new List>() { + Tuple.Create(UnitTypeId.Kilograms, 1), + Tuple.Create(UnitTypeId.Seconds, -3) } ) + } } ) + }, + { SpecTypeId.SpecificHeatOfVaporization, new DerivedAttributes(IFCDerivedUnitEnum.HeatingValueUnit, null, new Dictionary() { + { UnitTypeId.JoulesPerGram, new DerivedInfo(1.0e+3, true, new List>() { + Tuple.Create(UnitTypeId.Meters, 2), + Tuple.Create(UnitTypeId.Seconds, -2) } ) + } } ) + }, + { SpecTypeId.Permeability, new DerivedAttributes(IFCDerivedUnitEnum.VaporPermeabilityUnit, null, new Dictionary() { + { UnitTypeId.NanogramsPerPascalSecondSquareMeter, new DerivedInfo(1.0e-12, true, new List>() { + Tuple.Create(UnitTypeId.Meters, -1), + Tuple.Create(UnitTypeId.Seconds, 1) } ) + } } ) + }, + { SpecTypeId.HvacViscosity, new DerivedAttributes(IFCDerivedUnitEnum.DynamicViscosityUnit, null, new Dictionary() { + { UnitTypeId.KilogramsPerMeterSecond, new DerivedInfo(null, true, new List>() { + Tuple.Create(UnitTypeId.Kilograms, 1), + Tuple.Create(UnitTypeId.Meters, -1), + Tuple.Create(UnitTypeId.Seconds, -1) } ) + } } ) + }, + { SpecTypeId.ThermalExpansionCoefficient, new DerivedAttributes(IFCDerivedUnitEnum.ThermalExpansionCoefficientUnit, null, new Dictionary() { + { UnitTypeId.InverseDegreesCelsius, new DerivedInfo(null, true, new List>() { + Tuple.Create(UnitTypeId.Kelvin, -1) } ) + } } ) + }, + { SpecTypeId.Stress, new DerivedAttributes(IFCDerivedUnitEnum.ModulusOfElasticityUnit, null, new Dictionary() { + { UnitTypeId.Pascals, new DerivedInfo(null, true, new List>() { + Tuple.Create(UnitTypeId.Kilograms, 1), + Tuple.Create(UnitTypeId.Meters, -1), + Tuple.Create(UnitTypeId.Seconds, -2) } ) + } } ) + }, + { SpecTypeId.IsothermalMoistureCapacity, new DerivedAttributes(IFCDerivedUnitEnum.IsothermalMoistureCapacityUnit, null, new Dictionary() { + { UnitTypeId.CubicMetersPerKilogram, new DerivedInfo(null, true, new List>() { + Tuple.Create(UnitTypeId.Kilograms, -1), + Tuple.Create(UnitTypeId.Meters, 3) } ) + } } ) + }, + + { SpecTypeId.Diffusivity, new DerivedAttributes(IFCDerivedUnitEnum.MoistureDiffusivityUnit, null, new Dictionary() { + { UnitTypeId.SquareMetersPerSecond, new DerivedInfo(null, true, new List>() { + Tuple.Create(UnitTypeId.Meters, 2), + Tuple.Create(UnitTypeId.Seconds, -1) } ) + } } ) + }, + { SpecTypeId.MassPerUnitLength, new DerivedAttributes(IFCDerivedUnitEnum.MassPerLengthUnit, null, new Dictionary() { + { UnitTypeId.KilogramsPerMeter, new DerivedInfo(null, true, new List>() { + Tuple.Create(UnitTypeId.Kilograms, 1), + Tuple.Create(UnitTypeId.Meters, -1) } ) + } } ) + }, + { SpecTypeId.ThermalResistance, new DerivedAttributes(IFCDerivedUnitEnum.ThermalResistanceUnit, null, new Dictionary() { + { UnitTypeId.SquareMeterKelvinsPerWatt, new DerivedInfo(null, true, new List>() { + Tuple.Create(UnitTypeId.Kilograms, -1), + Tuple.Create(UnitTypeId.Seconds, 3), + Tuple.Create(UnitTypeId.Kelvin, 1) } ) + } } ) + }, + { SpecTypeId.Acceleration, new DerivedAttributes(IFCDerivedUnitEnum.AccelerationUnit, null, new Dictionary() { + { UnitTypeId.MetersPerSecondSquared, new DerivedInfo(null, true, new List>() { + Tuple.Create(UnitTypeId.Meters, 1), + Tuple.Create(UnitTypeId.Seconds, -2) } ) + } } ) + }, + { SpecTypeId.Pulsation, new DerivedAttributes(IFCDerivedUnitEnum.AngularVelocityUnit, null, new Dictionary() { + { UnitTypeId.RadiansPerSecond, new DerivedInfo(null, true, new List>() { + Tuple.Create(UnitTypeId.Radians, 1), + Tuple.Create(UnitTypeId.Seconds, -1) } ) + } } ) + }, + { SpecTypeId.PointSpringCoefficient, new DerivedAttributes(IFCDerivedUnitEnum.LinearStiffnessUnit, null, new Dictionary() { + { UnitTypeId.NewtonsPerMeter, new DerivedInfo(null, true, new List>() { + Tuple.Create(UnitTypeId.Kilograms, 1), + Tuple.Create(UnitTypeId.Seconds, -2) } ) + } } ) + }, + { SpecTypeId.WarpingConstant, new DerivedAttributes(IFCDerivedUnitEnum.WarpingConstantUnit, null, new Dictionary() { + { UnitTypeId.MetersToTheSixthPower, new DerivedInfo(null, true, new List>() { + Tuple.Create(UnitTypeId.Meters, 6) } ) + } } ) + }, + { SpecTypeId.LinearMoment, new DerivedAttributes(IFCDerivedUnitEnum.LinearMomentUnit, null, new Dictionary() { + { UnitTypeId.NewtonMetersPerMeter, new DerivedInfo(null, true, new List>() { + Tuple.Create(UnitTypeId.Kilograms, 1), + Tuple.Create(UnitTypeId.Meters, 1), + Tuple.Create(UnitTypeId.Seconds, -2) } ) + } } ) + }, + { SpecTypeId.Moment, new DerivedAttributes(IFCDerivedUnitEnum.Torqueunit, null, new Dictionary() { + { UnitTypeId.NewtonMeters, new DerivedInfo(null, true, new List>() { + Tuple.Create(UnitTypeId.Kilograms, 1), + Tuple.Create(UnitTypeId.Meters, 2), + Tuple.Create(UnitTypeId.Seconds, -2) } ) + } } ) + }, + { SpecTypeId.CostPerArea, new DerivedAttributes(IFCDerivedUnitEnum.UserDefined, "Cost Per Area", new Dictionary() { + { UnitTypeId.CurrencyPerSquareMeter, new DerivedInfo(null, true, new List>() { + Tuple.Create(UnitTypeId.Meters, -2) } ) + } } ) + }, + { SpecTypeId.ApparentPowerDensity, new DerivedAttributes(IFCDerivedUnitEnum.UserDefined, "Apparent Power Density", new Dictionary() { + { UnitTypeId.VoltAmperesPerSquareMeter, new DerivedInfo(null, true, new List>() { + Tuple.Create(UnitTypeId.Kilograms, 1), + Tuple.Create(UnitTypeId.Seconds, -3) } ) + } } ) + }, + { SpecTypeId.CostRateEnergy, new DerivedAttributes(IFCDerivedUnitEnum.UserDefined, "Cost Rate Energy", new Dictionary() { + { UnitTypeId.CurrencyPerWattHour, new DerivedInfo(1.0 / 3600.0, true, new List>() { + Tuple.Create(UnitTypeId.Kilograms, -1), + Tuple.Create(UnitTypeId.Meters, -2), + Tuple.Create(UnitTypeId.Seconds, 2) } ) + } } ) + }, + { SpecTypeId.CostRatePower, new DerivedAttributes(IFCDerivedUnitEnum.UserDefined, "Cost Rate Power", new Dictionary() { + { UnitTypeId.CurrencyPerWatt, new DerivedInfo(null, true, new List>() { + Tuple.Create(UnitTypeId.Kilograms, -1), + Tuple.Create(UnitTypeId.Meters, -2), + Tuple.Create(UnitTypeId.Seconds, 3) } ) + } } ) + }, + { SpecTypeId.Efficacy, new DerivedAttributes(IFCDerivedUnitEnum.UserDefined, "Luminous Efficacy", new Dictionary() { + { UnitTypeId.LumensPerWatt, new DerivedInfo(null, true, new List>() { + Tuple.Create(UnitTypeId.Kilograms, -1), + Tuple.Create(UnitTypeId.Meters, -2), + Tuple.Create(UnitTypeId.Seconds, 3), + Tuple.Create(UnitTypeId.Lumens, 1) } ) + } } ) + }, + { SpecTypeId.Luminance, new DerivedAttributes(IFCDerivedUnitEnum.UserDefined, "Luminance", new Dictionary() { + { UnitTypeId.CandelasPerSquareMeter, new DerivedInfo(null, true, new List>() { + Tuple.Create(UnitTypeId.Meters, -2), + Tuple.Create(UnitTypeId.Candelas, 1) } ) + } } ) + }, + { SpecTypeId.ElectricalPowerDensity, new DerivedAttributes(IFCDerivedUnitEnum.UserDefined, "Electrical Power Density", new Dictionary() { + { UnitTypeId.WattsPerSquareMeter, new DerivedInfo(null, true, new List>() { + Tuple.Create(UnitTypeId.Kilograms, 1), + Tuple.Create(UnitTypeId.Seconds, -3) } ) + } } ) + }, + { SpecTypeId.PowerPerLength, new DerivedAttributes(IFCDerivedUnitEnum.UserDefined, "Power Per Length", new Dictionary() { + { UnitTypeId.WattsPerMeter, new DerivedInfo(null, true, new List>() { + Tuple.Create(UnitTypeId.Kilograms, 1), + Tuple.Create(UnitTypeId.Meters, 1), + Tuple.Create(UnitTypeId.Seconds, -3) } ) + } } ) + }, + { SpecTypeId.ElectricalResistivity, new DerivedAttributes(IFCDerivedUnitEnum.UserDefined, "Electrical Resistivity", new Dictionary() { + { UnitTypeId.OhmMeters, new DerivedInfo(null, true, new List>() { + Tuple.Create(UnitTypeId.Kilograms, 1), + Tuple.Create(UnitTypeId.Meters, 3), + Tuple.Create(UnitTypeId.Seconds, -3), + Tuple.Create(UnitTypeId.Amperes, -2) } ) + } } ) + }, + { SpecTypeId.HeatCapacityPerArea, new DerivedAttributes(IFCDerivedUnitEnum.UserDefined, "Heat Capacity Per Area", new Dictionary() { + { UnitTypeId.JoulesPerSquareMeterKelvin, new DerivedInfo(null, true, new List>() { + Tuple.Create(UnitTypeId.Kilograms, 1), + Tuple.Create(UnitTypeId.Seconds, -2), + Tuple.Create(UnitTypeId.Kelvin, -1) } ) + } } ) + }, + { SpecTypeId.ThermalGradientCoefficientForMoistureCapacity, new DerivedAttributes(IFCDerivedUnitEnum.UserDefined, "Thermal Gradient Coefficient For Moisture Capacity", new Dictionary() { + { UnitTypeId.KilogramsPerKilogramKelvin, new DerivedInfo(null, true, new List>() { + Tuple.Create(UnitTypeId.Kelvin, -1) } ) + } } ) + }, + { SpecTypeId.ThermalMass, new DerivedAttributes(IFCDerivedUnitEnum.UserDefined, "Thermal Mass", new Dictionary() { + { UnitTypeId.JoulesPerKelvin, new DerivedInfo(null, true, new List>() { + Tuple.Create(UnitTypeId.Kilograms, 1), + Tuple.Create(UnitTypeId.Meters, 2), + Tuple.Create(UnitTypeId.Seconds, -2), + Tuple.Create(UnitTypeId.Kelvin, -1) } ) + } } ) + }, + { SpecTypeId.AirFlowDensity, new DerivedAttributes(IFCDerivedUnitEnum.UserDefined, "Air Flow Density", new Dictionary() { + { UnitTypeId.CubicMetersPerHourSquareMeter, new DerivedInfo(1.0 / 3600.0, true, new List>() { + Tuple.Create(UnitTypeId.Meters, 1), + Tuple.Create(UnitTypeId.Seconds, -1) } ) + } } ) + }, + { SpecTypeId.AirFlowDividedByCoolingLoad, new DerivedAttributes(IFCDerivedUnitEnum.UserDefined, "Air Flow Divided By Cooling Load", new Dictionary() { + { UnitTypeId.LitersPerSecondKilowatt, new DerivedInfo(1.0e-3 * 1.0e-3, true, new List>() { + Tuple.Create(UnitTypeId.Kilograms, -1), + Tuple.Create(UnitTypeId.Meters, 1), + Tuple.Create(UnitTypeId.Seconds, 2) } ) + } } ) + }, + { SpecTypeId.AirFlowDividedByVolume, new DerivedAttributes(IFCDerivedUnitEnum.UserDefined, "Air Flow Divided By Volume", new Dictionary() { + { UnitTypeId.CubicMetersPerHourCubicMeter, new DerivedInfo(1.0 / 3600.0, true, new List>() { + Tuple.Create(UnitTypeId.Seconds, -1) } ) + } } ) + }, + { SpecTypeId.AreaDividedByCoolingLoad, new DerivedAttributes(IFCDerivedUnitEnum.UserDefined, "Area Divided By Cooling Load", new Dictionary() { + { UnitTypeId.SquareMetersPerKilowatt, new DerivedInfo(1.0e-3, true, new List>() { + Tuple.Create(UnitTypeId.Kilograms, -1), + Tuple.Create(UnitTypeId.Seconds, 3) } ) + } } ) + }, + { SpecTypeId.AreaDividedByHeatingLoad, new DerivedAttributes(IFCDerivedUnitEnum.UserDefined, "Area Divided By Heating Load", new Dictionary() { + { UnitTypeId.SquareMetersPerKilowatt, new DerivedInfo(1.0e-3, true, new List>() { + Tuple.Create(UnitTypeId.Kilograms, -1), + Tuple.Create(UnitTypeId.Seconds, 3) } ) + } } ) + }, + { SpecTypeId.CoolingLoadDividedByArea, new DerivedAttributes(IFCDerivedUnitEnum.UserDefined, "Cooling Load Divided By Area", new Dictionary() { + { UnitTypeId.WattsPerSquareMeter, new DerivedInfo(null, true, new List>() { + Tuple.Create(UnitTypeId.Kilograms, 1), + Tuple.Create(UnitTypeId.Seconds, -3) } ) + } } ) + }, + { SpecTypeId.CoolingLoadDividedByVolume, new DerivedAttributes(IFCDerivedUnitEnum.UserDefined, "Cooling Load Divided By Volume", new Dictionary() { + { UnitTypeId.WattsPerCubicMeter, new DerivedInfo(null, true, new List>() { + Tuple.Create(UnitTypeId.Kilograms, 1), + Tuple.Create(UnitTypeId.Meters, -1), + Tuple.Create(UnitTypeId.Seconds, -3) } ) + } } ) + }, + { SpecTypeId.FlowPerPower, new DerivedAttributes(IFCDerivedUnitEnum.UserDefined, "Flow Per Power", new Dictionary() { + { UnitTypeId.CubicMetersPerWattSecond, new DerivedInfo(null, true, new List>() { + Tuple.Create(UnitTypeId.Kilograms, -1), + Tuple.Create(UnitTypeId.Meters, 1), + Tuple.Create(UnitTypeId.Seconds, 2) } ) + } } ) + }, + { SpecTypeId.HvacFriction, new DerivedAttributes(IFCDerivedUnitEnum.UserDefined, "Friction Loss", new Dictionary() { + { UnitTypeId.PascalsPerMeter, new DerivedInfo(null, true, new List>() { + Tuple.Create(UnitTypeId.Kilograms, 1), + Tuple.Create(UnitTypeId.Meters, -2), + Tuple.Create(UnitTypeId.Seconds, -2) } ) + } } ) + }, + { SpecTypeId.HeatingLoadDividedByArea, new DerivedAttributes(IFCDerivedUnitEnum.UserDefined, "Heating Load Divided By Area", new Dictionary() { + { UnitTypeId.WattsPerSquareMeter, new DerivedInfo(null, true, new List>() { + Tuple.Create(UnitTypeId.Kilograms, 1), + Tuple.Create(UnitTypeId.Seconds, -3) } ) + } } ) + }, + { SpecTypeId.HeatingLoadDividedByVolume, new DerivedAttributes(IFCDerivedUnitEnum.UserDefined, "Heating Load Divided By Volume", new Dictionary() { + { UnitTypeId.WattsPerCubicMeter, new DerivedInfo(null, true, new List>() { + Tuple.Create(UnitTypeId.Kilograms, 1), + Tuple.Create(UnitTypeId.Meters, -1), + Tuple.Create(UnitTypeId.Seconds, -3) } ) + } } ) + }, + { SpecTypeId.PowerPerFlow, new DerivedAttributes(IFCDerivedUnitEnum.UserDefined, "Power Per Flow", new Dictionary() { + { UnitTypeId.WattsPerCubicMeterPerSecond, new DerivedInfo(null, true, new List>() { + Tuple.Create(UnitTypeId.Kilograms, 1), + Tuple.Create(UnitTypeId.Meters, -1), + Tuple.Create(UnitTypeId.Seconds, -2) } ) + } } ) + }, + { SpecTypeId.PipingFriction, new DerivedAttributes(IFCDerivedUnitEnum.UserDefined, "Piping Friction", new Dictionary() { + { UnitTypeId.PascalsPerMeter, new DerivedInfo(null, true, new List>() { + Tuple.Create(UnitTypeId.Kilograms, 1), + Tuple.Create(UnitTypeId.Meters, -2), + Tuple.Create(UnitTypeId.Seconds, -2) } ) + } } ) + }, + { SpecTypeId.AreaSpringCoefficient, new DerivedAttributes(IFCDerivedUnitEnum.UserDefined, "Area Spring Coefficient", new Dictionary() { + { UnitTypeId.PascalsPerMeter, new DerivedInfo(null, true, new List>() { + Tuple.Create(UnitTypeId.Kilograms, 1), + Tuple.Create(UnitTypeId.Meters, -2), + Tuple.Create(UnitTypeId.Seconds, -2) } ) + } } ) + }, + { SpecTypeId.LineSpringCoefficient, new DerivedAttributes(IFCDerivedUnitEnum.UserDefined, "Line Spring Coefficient", new Dictionary() { + { UnitTypeId.Pascals, new DerivedInfo(null, true, new List>() { + Tuple.Create(UnitTypeId.Kilograms, 1), + Tuple.Create(UnitTypeId.Meters, -1), + Tuple.Create(UnitTypeId.Seconds, -2) } ) + } } ) + }, + { SpecTypeId.MassPerUnitArea, new DerivedAttributes(IFCDerivedUnitEnum.UserDefined, "Mass Per Unit Area", new Dictionary() { + { UnitTypeId.KilogramsPerSquareMeter, new DerivedInfo(null, true, new List>() { + Tuple.Create(UnitTypeId.Kilograms, 1), + Tuple.Create(UnitTypeId.Meters, -2) } ) + } } ) + }, + { SpecTypeId.ReinforcementAreaPerUnitLength, new DerivedAttributes(IFCDerivedUnitEnum.UserDefined, "Reinforcement Area Per Unit Length", new Dictionary() { + { UnitTypeId.SquareMetersPerMeter, new DerivedInfo(null, true, new List>() { + Tuple.Create(UnitTypeId.Meters, 2), + Tuple.Create(UnitTypeId.Meters, -1) } ) + } } ) + }, + { SpecTypeId.RotationalLineSpringCoefficient, new DerivedAttributes(IFCDerivedUnitEnum.UserDefined, "Rotational Line Spring Coefficient", new Dictionary() { + { UnitTypeId.KilonewtonMetersPerDegreePerMeter, new DerivedInfo(1.0e+3 * 180.0 / Math.PI, true, new List>() { + Tuple.Create(UnitTypeId.Kilograms, 1), + Tuple.Create(UnitTypeId.Meters, 1), + Tuple.Create(UnitTypeId.Seconds, -2), + Tuple.Create(UnitTypeId.Radians, -1) } ) + } } ) + }, + { SpecTypeId.RotationalPointSpringCoefficient, new DerivedAttributes(IFCDerivedUnitEnum.UserDefined, "Rotational Point Spring Coefficient", new Dictionary() { + { UnitTypeId.KilonewtonMetersPerDegree, new DerivedInfo(1.0e+3 * 180.0 / Math.PI, true, new List>() { + Tuple.Create(UnitTypeId.Kilograms, 1), + Tuple.Create(UnitTypeId.Meters, 2), + Tuple.Create(UnitTypeId.Seconds, -2), + Tuple.Create(UnitTypeId.Radians, -1) } ) + } } ) + }, + { SpecTypeId.UnitWeight, new DerivedAttributes(IFCDerivedUnitEnum.UserDefined, "Unit Weight", new Dictionary() { + { UnitTypeId.KilonewtonsPerCubicMeter, new DerivedInfo(1.0e+3, true, new List>() { + Tuple.Create(UnitTypeId.Kilograms, 1), + Tuple.Create(UnitTypeId.Meters, -2), + Tuple.Create(UnitTypeId.Seconds, -2) } ) + } } ) + } + }; + + /// + /// The dictionary contains specific unit name mapping for ExportAsCOBIE + /// + private static readonly Dictionary CobieUnitNameMapping = new Dictionary() + { + { "SQUARE INCH", "inch" }, + { "SQUARE FOOT", "foot" }, + { "CUBIC INCH", "inch" }, + { "CUBIC FOOT", "foot" } + }; + + /// + /// The dictionary contains Ifc to Revit data type mapping + /// + private static readonly Dictionary IfcToRevitDataTypeMapping = new Dictionary() + { + { "IfcAccelerationMeasure", SpecTypeId.Acceleration }, + { "IfcAngularVelocityMeasure", SpecTypeId.Pulsation }, + { "IfcAreaDensityMeasure", SpecTypeId.MassPerUnitArea }, + { "IfcAreaMeasure", SpecTypeId.Area }, + { "IfcDynamicViscosityMeasure", SpecTypeId.HvacViscosity }, + { "IfcElectricCurrentMeasure", SpecTypeId.Current }, + { "IfcElectricVoltageMeasure", SpecTypeId.ElectricalPotential }, + { "IfcEnergyMeasure", SpecTypeId.Energy }, + { "IfcForceMeasure", SpecTypeId.Force }, + { "IfcFrequencyMeasure", SpecTypeId.ElectricalFrequency }, + { "IfcHeatFluxDensityMeasure", SpecTypeId.HvacPowerDensity }, + { "IfcHeatingValueMeasure", SpecTypeId.SpecificHeatOfVaporization }, + { "IfcIlluminanceMeasure", SpecTypeId.Illuminance }, + { "IfcIonConcentrationMeasure", SpecTypeId.PipingDensity }, + { "IfcIsothermalMoistureCapacityMeasure", SpecTypeId.IsothermalMoistureCapacity }, + { "IfcLengthMeasure", SpecTypeId.Length }, + { "IfcLinearForceMeasure", SpecTypeId.LinearForce }, + { "IfcLinearMomentMeasure", SpecTypeId.LinearMoment }, + { "IfcLinearStiffnessMeasure", SpecTypeId.PointSpringCoefficient }, + { "IfcLinearVelocityMeasure", SpecTypeId.HvacVelocity }, + { "IfcLuminousFluxMeasure", SpecTypeId.LuminousFlux }, + { "IfcLuminousIntensityMeasure", SpecTypeId.LuminousIntensity }, + { "IfcMassDensityMeasure", SpecTypeId.MassDensity }, + { "IfcMassFlowRateMeasure", SpecTypeId.PipingMassPerTime }, + { "IfcMassMeasure", SpecTypeId.Mass }, + { "IfcMassPerLengthMeasure", SpecTypeId.MassPerUnitLength }, + { "IfcModulusOfElasticityMeasure", SpecTypeId.Stress }, + { "IfcMoistureDiffusivityMeasure", SpecTypeId.Diffusivity }, + { "IfcMomentOfInertiaMeasure", SpecTypeId.MomentOfInertia }, + { "IfcPlanarForceMeasure", SpecTypeId.AreaForce }, + { "IfcPlaneAngleMeasure", SpecTypeId.Angle }, + { "IfcPositiveLengthMeasure", SpecTypeId.Length }, + { "IfcPositivePlaneAngleMeasure", SpecTypeId.Angle }, + { "IfcPowerMeasure", SpecTypeId.HvacPower }, + { "IfcPressureMeasure", SpecTypeId.HvacPressure }, + { "IfcRotationalFrequencyMeasure", SpecTypeId.AngularSpeed }, + { "IfcSoundPowerMeasure", SpecTypeId.Wattage }, + { "IfcSoundPressureMeasure", SpecTypeId.HvacPressure }, + { "IfcSpecificHeatCapacityMeasure", SpecTypeId.SpecificHeat }, + { "IfcThermalConductivityMeasure", SpecTypeId.ThermalConductivity }, + { "IfcThermalExpansionCoefficientMeasure", SpecTypeId.ThermalExpansionCoefficient }, + { "IfcThermalResistanceMeasure", SpecTypeId.ThermalResistance }, + { "IfcThermalTransmittanceMeasure", SpecTypeId.HeatTransferCoefficient }, + { "IfcThermodynamicTemperatureMeasure", SpecTypeId.HvacTemperature }, + { "IfcTimeMeasure", SpecTypeId.Time }, + { "IfcTorqueMeasure", SpecTypeId.Moment }, + { "IfcVolumeMeasure", SpecTypeId.Volume }, + { "IfcVolumetricFlowRateMeasure", SpecTypeId.AirFlow }, + { "IfcWarpingConstantMeasure", SpecTypeId.WarpingConstant }, + }; + #endregion + + } +} diff --git a/Source/Revit.IFC.Export/Utility/UnitUtil.cs b/Source/Revit.IFC.Export/Utility/UnitUtil.cs index 818d4279..0436fb57 100644 --- a/Source/Revit.IFC.Export/Utility/UnitUtil.cs +++ b/Source/Revit.IFC.Export/Utility/UnitUtil.cs @@ -29,7 +29,7 @@ namespace Revit.IFC.Export.Utility { /// - /// Utilities to work with ExporterCacheManager.UnitCache. + /// Utilities to work with ExporterCacheManager.UnitsCache. /// public class UnitUtil { @@ -365,6 +365,26 @@ static public double ScaleMassDensity(double unscaledValue) return ScaleDouble(SpecTypeId.MassDensity, unscaledValue); } + /// + /// Converts Mass in Revit internal units to Revit display units. + /// + /// The Mass in Revit internal units. + /// The Mass in Revit display units. + static public double ScaleMass(double unscaledValue) + { + return ScaleDouble(SpecTypeId.Mass, unscaledValue); + } + + /// + /// Converts LinearVelocity in Revit internal units to Revit display units. + /// + /// The LinearVelocity in Revit internal units. + /// The LinearVelocity in Revit display units. + static public double ScaleLinearVelocity(double unscaledValue) + { + return ScaleDouble(SpecTypeId.HvacVelocity, unscaledValue); + } + /// /// Converts ModulusOfElasticity in Revit internal units to Revit display units. /// @@ -495,6 +515,26 @@ static public double ScaleThermalConductivity(double unscaledValue) return ScaleDouble(SpecTypeId.ThermalConductivity, unscaledValue); } + /// + /// Converts RotationalFrequency in Revit internal units to Revit display units. + /// + /// The RotationalFrequency in Revit internal units. + /// The RotationalFrequency in Revit display units. + static public double ScaleRotationalFrequency(double unscaledValue) + { + return ScaleDouble(SpecTypeId.AngularSpeed, unscaledValue); + } + + /// + /// Converts ScaleMassFlowRate in Revit internal units to Revit display units. + /// + /// The ScaleMassFlowRate in Revit internal units. + /// The ScaleMassFlowRate in Revit display units. + static public double ScaleMassFlowRate(double unscaledValue) + { + return ScaleDouble(SpecTypeId.PipingMassPerTime, unscaledValue); + } + /// /// Converts a position in IFC units to Revit internal units. /// diff --git a/Source/Revit.IFC.Export/Utility/UnitsCache.cs b/Source/Revit.IFC.Export/Utility/UnitsCache.cs index 41292ab7..f7125d39 100644 --- a/Source/Revit.IFC.Export/Utility/UnitsCache.cs +++ b/Source/Revit.IFC.Export/Utility/UnitsCache.cs @@ -23,101 +23,215 @@ using System.Text; using Autodesk.Revit.DB; using Autodesk.Revit.DB.IFC; +using Revit.IFC.Common.Utility; using Revit.IFC.Export.Exporter; using Revit.IFC.Export.Toolkit; namespace Revit.IFC.Export.Utility { + /// + /// The class contains information about created and mapped unit. + /// + public class UnitInfo + { + public UnitInfo(IFCAnyHandle handle, double scaleFactor, double offset) + { + Handle = handle; + ScaleFactor = scaleFactor; + Offset = offset; + } + + public IFCAnyHandle Handle { get; private set; } = null; + public double ScaleFactor { get; private set; } = 1.0; + public double Offset { get; private set; } = 0.0; + } + + /// /// Used to keep a cache of the created IfcUnits. /// public class UnitsCache : Dictionary { - Dictionary> m_UnitConversionTable = - new Dictionary>(); + /// + /// The dictionary mapping from Revit data type (SpecTypeId) + /// to created ifc unit handle with convesion values (scale and offset). + /// + Dictionary m_unitInfoTable = + new Dictionary(); + + /// + /// The dictionary mapping from Revit unit (UnitTypeId) to created ifc handle. + /// These are the auxiliary unit handles that don't go to IfcUnitAssignment + /// + Dictionary m_auxiliaryUnitCache = + new Dictionary(); + + /// + /// The dictionary mapping from a unit handle with exponent to IfcDerivedUnitElement handle. + /// + Dictionary, IFCAnyHandle> m_derivedUnitElementCache = + new Dictionary, IFCAnyHandle>(); + + /// + /// Finds UnitInfo in dictionary + /// + public bool FindUnitInfo(ForgeTypeId specTypeId, out UnitInfo unitInfo) + { + return m_unitInfoTable.TryGetValue(specTypeId, out unitInfo); + } + + /// + /// Adds UnitInfo to dictionary + /// + public void RegisterUnitInfo(ForgeTypeId specTypeId, UnitInfo unitInfo) + { + m_unitInfoTable[specTypeId] = unitInfo; + } + /// + /// Extracts the unit handles to assign to a project + /// + /// Unit handles set + public HashSet GetUnitsToAssign() + { + HashSet unitSet = new HashSet(); + foreach (var unitInfo in m_unitInfoTable) + { + // Special case: SpecTypeId.ColorTemperature is mapped to SI IFCUnit.ThermoDynamicTemperatureUnit (Kelvin) + // and mustn't be assigned to project to avoid conflict with ThermoDynamicTemperatureUnit of SpecTypeId.HvacTemperature + if (unitInfo.Key.Equals(SpecTypeId.ColorTemperature)) + continue; + + IFCAnyHandle unitHnd = unitInfo.Value?.Handle; + if (unitHnd != null) + unitSet.Add(unitHnd); + } + return unitSet; + } + + + /// + /// Finds auxiliary unit in dictionary + /// + public bool FindAuxiliaryUnit(ForgeTypeId unitTypeId, out IFCAnyHandle auxiliaryUnit) + { + return m_auxiliaryUnitCache.TryGetValue(unitTypeId, out auxiliaryUnit); + } + + /// + /// Adds auxiliary unit to dictionary + /// + public void RegisterAuxiliaryUnit(ForgeTypeId unitTypeId, IFCAnyHandle auxiliaryUnit) + { + m_auxiliaryUnitCache[unitTypeId] = auxiliaryUnit; + } + + /// + /// Finds derived unit element in dictionary + /// + public bool FindDerivedUnitElement(Tuple unitWithExponent, out IFCAnyHandle derivedUnit) + { + return m_derivedUnitElementCache.TryGetValue(unitWithExponent, out derivedUnit); + } + + /// + /// Adds derived unit element to dictionary + /// + public void RegisterDerivedUnit(Tuple unitWithExponent, IFCAnyHandle derivedUnit) + { + m_derivedUnitElementCache[unitWithExponent] = derivedUnit; + } + + /// + /// Finds user defined unit in dictionary + /// + public IFCAnyHandle FindUserDefinedUnit(string unitName) + { + return this.ContainsKey(unitName) ? this[unitName] : null; + } + + /// + /// Adds user defined unit to dictionary + /// + public void RegisterUserDefinedUnit(string unitName, IFCAnyHandle unitHnd) + { + this[unitName] = unitHnd; + } + + #region Scale/unscale methods /// /// Convert from Revit internal units to Revit display units. /// - /// Identifier of the spec. + /// Revit data type /// The value in Revit internal units. /// The value in Revit display units. public double Scale(ForgeTypeId specTypeId, double unscaledValue) { - Tuple scale; - if (m_UnitConversionTable.TryGetValue(specTypeId, out scale)) - return unscaledValue * scale.Item2 + scale.Item3; + UnitInfo unitInfo = UnitMappingUtil.GetOrCreateUnitInfo(specTypeId); + if (unitInfo != null) + return unscaledValue * unitInfo.ScaleFactor + unitInfo.Offset; return unscaledValue; } /// /// Convert from Revit display units to Revit internal units. /// - /// Identifier of the spec. - /// The value in Revit display units. + /// Revit data type + /// The value in Revit display units. /// The value in Revit internal units. /// Ignores the offset component. public XYZ Unscale(ForgeTypeId specTypeId, XYZ scaledValue) { - Tuple scale; - if (m_UnitConversionTable.TryGetValue(specTypeId, out scale)) - return scaledValue / scale.Item2; + UnitInfo unitInfo = UnitMappingUtil.GetOrCreateUnitInfo(specTypeId); + if (unitInfo != null) + return scaledValue / unitInfo.ScaleFactor; return scaledValue; } /// /// Convert from Revit display units to Revit internal units. /// - /// Identifier of the spec. + /// Revit data type /// The value in Revit display units. /// The value in Revit internal units. public double Unscale(ForgeTypeId specTypeId, double scaledValue) { - Tuple scale; - if (m_UnitConversionTable.TryGetValue(specTypeId, out scale)) - return (scaledValue - scale.Item3) / scale.Item2; + UnitInfo unitInfo = UnitMappingUtil.GetOrCreateUnitInfo(specTypeId); + if (unitInfo != null) + return (scaledValue - unitInfo.Offset) / unitInfo.ScaleFactor; return scaledValue; } /// /// Convert from Revit internal units to Revit display units. /// - /// Identifier of the spec. + /// Revit data type /// The value in Revit internal units. /// The value in Revit display units. /// Ignores the offset component. public UV Scale(ForgeTypeId specTypeId, UV unscaledValue) { - Tuple scale; - if (m_UnitConversionTable.TryGetValue(specTypeId, out scale)) - return unscaledValue * scale.Item2; + UnitInfo unitInfo = UnitMappingUtil.GetOrCreateUnitInfo(specTypeId); + if (unitInfo != null) + return unscaledValue * unitInfo.ScaleFactor; return unscaledValue; } /// /// Convert from Revit internal units to Revit display units. /// - /// Identifier of the spec. + /// Revit data type /// The value in Revit internal units. /// The value in Revit display units. /// Ignores the offset component. public XYZ Scale(ForgeTypeId specTypeId, XYZ unscaledValue) { - Tuple scale; - if (m_UnitConversionTable.TryGetValue(specTypeId, out scale)) - return unscaledValue * scale.Item2; + UnitInfo unitInfo = UnitMappingUtil.GetOrCreateUnitInfo(specTypeId); + if (unitInfo != null) + return unscaledValue * unitInfo.ScaleFactor; return unscaledValue; } - - /// - /// Sets the conversion factors to convert Revit internal units to Revit display units for the specified unit type, and stores the IFC handle. - /// - /// Identifier of the spec. - /// The IFCUnit handle. - /// The scaling factor. - public void AddUnit(ForgeTypeId specTypeId, IFCAnyHandle unitHandle, double scale, double offset) - { - m_UnitConversionTable[specTypeId] = new Tuple(unitHandle, scale, offset); - } + #endregion } + } \ No newline at end of file diff --git a/Source/Revit.IFC.Import.Core/Properties/AssemblyInfo.cs b/Source/Revit.IFC.Import.Core/Properties/AssemblyInfo.cs index 3fea00a5..959c17f2 100644 --- a/Source/Revit.IFC.Import.Core/Properties/AssemblyInfo.cs +++ b/Source/Revit.IFC.Import.Core/Properties/AssemblyInfo.cs @@ -12,9 +12,9 @@ [assembly: AssemblyTitle("Revit.IFC.Import.Core")] [assembly: AssemblyDescription("Revit.IFC.Import.Core")] [assembly: AssemblyCompany("Autodesk")] -[assembly: AssemblyCopyright("@2012-2023 Autodesk, Inc. All rights reserved.")] -[assembly: AssemblyVersion("24.2.0.49")] -[assembly: AssemblyFileVersion("24.2.0.49")] +[assembly: AssemblyCopyright("@2012-2024 Autodesk, Inc. All rights reserved.")] +[assembly: AssemblyVersion("25.2.0.5")] +[assembly: AssemblyFileVersion("25.2.0.5")] // Version information can now be found in Source\Foundation\RevitENU\Version.cs // diff --git a/Source/Revit.IFC.Import.Core/Revit.IFC.Import.Core.csproj b/Source/Revit.IFC.Import.Core/Revit.IFC.Import.Core.csproj index d975ca71..2f14f119 100644 --- a/Source/Revit.IFC.Import.Core/Revit.IFC.Import.Core.csproj +++ b/Source/Revit.IFC.Import.Core/Revit.IFC.Import.Core.csproj @@ -1,64 +1,16 @@ - - - + - Debug - x64 - {B1E159B7-4B12-45A9-B83F-159E37798D44} - Library - Properties Revit.IFC.Import.Core Revit.IFC.Import.Core - v4.8 - 512 - true - - - prompt - x64 - - - prompt - x64 - bin\Releasex64\ - - - prompt - x64 - bin\Debugx64\ + + + + - ..\..\..\..\Program Files\Autodesk\Revit 2024\RevitAPI.dll + ..\..\..\..\Program Files\Autodesk\Revit 2025\RevitAPI.dll - - - - - - - - - - - - - - - - - xcopy "$(TargetPath)" "C:\ProgramData\Autodesk\ApplicationPlugins\IFC 2024.bundle\Contents\2024\" /F /R /Y /I - - - \ No newline at end of file + + diff --git a/Source/Revit.IFC.Import.Core/Revit.IFC.Import.Core.props b/Source/Revit.IFC.Import.Core/Revit.IFC.Import.Core.props deleted file mode 100644 index daf0f829..00000000 --- a/Source/Revit.IFC.Import.Core/Revit.IFC.Import.Core.props +++ /dev/null @@ -1,25 +0,0 @@ - - - - bin\Debugx64\ - DEBUG;TRACE;IFC_OPENSOURCE - true - false - false - 4 - full - prompt - - - bin\Releasex64\ - TRACE;IFC_OPENSOURCE - false - true - false - 4 - none - prompt - - - - diff --git a/Source/Revit.IFC.Import/Data/IFCBlock.cs b/Source/Revit.IFC.Import/Data/IFCBlock.cs index 61d46dcb..ebaaa28e 100644 --- a/Source/Revit.IFC.Import/Data/IFCBlock.cs +++ b/Source/Revit.IFC.Import/Data/IFCBlock.cs @@ -81,10 +81,10 @@ protected IFCBlock() { block = GeometryCreationUtilities.CreateExtrusionGeometry(loops, scaledExtrusionDirection, ZLength, solidOptions); } - catch (Exception ex) + catch (Exception) { if (shapeEditScope.MustCreateSolid()) - throw ex; + throw; Importer.TheLog.LogError(Id, "Block has an invalid definition for a solid; reverting to mesh.", false); diff --git a/Source/Revit.IFC.Import/Data/IFCBooleanOperand.cs b/Source/Revit.IFC.Import/Data/IFCBooleanOperand.cs index 6a27af74..8c5981af 100644 --- a/Source/Revit.IFC.Import/Data/IFCBooleanOperand.cs +++ b/Source/Revit.IFC.Import/Data/IFCBooleanOperand.cs @@ -34,29 +34,14 @@ public static IIFCBooleanOperand ProcessIFCBooleanOperand(IFCAnyHandle ifcBoolea return null; } - // If Hybrid IFC Import is in progress, make sure that the correct RepresentationItem is created. - if (Importer.TheOptions.IsHybridImport && (Importer.TheHybridInfo?.RepresentationsAlreadyCreated ?? false)) - { - // Check for Subtypes that Legacy Import would otherwise process. - if (IFCAnyHandleUtil.IsValidSubTypeOf(ifcBooleanOperand, IFCEntityType.IfcBooleanResult) || - IFCAnyHandleUtil.IsValidSubTypeOf(ifcBooleanOperand, IFCEntityType.IfcHalfSpaceSolid) || - IFCAnyHandleUtil.IsValidSubTypeOf(ifcBooleanOperand, IFCEntityType.IfcSolidModel) || - IFCAnyHandleUtil.IsValidSubTypeOf(ifcBooleanOperand, IFCEntityType.IfcCsgPrimitive3D)) - { - return IFCHybridRepresentationItem.ProcessIFCHybridRepresentationItem(ifcBooleanOperand); - } - } - else - { - if (IFCAnyHandleUtil.IsValidSubTypeOf(ifcBooleanOperand, IFCEntityType.IfcBooleanResult)) - return IFCBooleanResult.ProcessIFCBooleanResult(ifcBooleanOperand); - else if (IFCAnyHandleUtil.IsValidSubTypeOf(ifcBooleanOperand, IFCEntityType.IfcHalfSpaceSolid)) - return IFCHalfSpaceSolid.ProcessIFCHalfSpaceSolid(ifcBooleanOperand); - else if (IFCAnyHandleUtil.IsValidSubTypeOf(ifcBooleanOperand, IFCEntityType.IfcSolidModel)) - return IFCSolidModel.ProcessIFCSolidModel(ifcBooleanOperand); - else if (IFCAnyHandleUtil.IsValidSubTypeOf(ifcBooleanOperand, IFCEntityType.IfcCsgPrimitive3D)) - return IFCCsgPrimitive3D.ProcessIFCCsgPrimitive3D(ifcBooleanOperand); - } + if (IFCAnyHandleUtil.IsValidSubTypeOf(ifcBooleanOperand, IFCEntityType.IfcBooleanResult)) + return IFCBooleanResult.ProcessIFCBooleanResult(ifcBooleanOperand); + if (IFCAnyHandleUtil.IsValidSubTypeOf(ifcBooleanOperand, IFCEntityType.IfcHalfSpaceSolid)) + return IFCHalfSpaceSolid.ProcessIFCHalfSpaceSolid(ifcBooleanOperand); + else if (IFCAnyHandleUtil.IsValidSubTypeOf(ifcBooleanOperand, IFCEntityType.IfcSolidModel)) + return IFCSolidModel.ProcessIFCSolidModel(ifcBooleanOperand); + else if (IFCAnyHandleUtil.IsValidSubTypeOf(ifcBooleanOperand, IFCEntityType.IfcCsgPrimitive3D)) + return IFCCsgPrimitive3D.ProcessIFCCsgPrimitive3D(ifcBooleanOperand); Importer.TheLog.LogUnhandledSubTypeError(ifcBooleanOperand, "IfcBooleanOperand", true); return null; diff --git a/Source/Revit.IFC.Import/Data/IFCBooleanResult.cs b/Source/Revit.IFC.Import/Data/IFCBooleanResult.cs index 012be545..8b670254 100644 --- a/Source/Revit.IFC.Import/Data/IFCBooleanResult.cs +++ b/Source/Revit.IFC.Import/Data/IFCBooleanResult.cs @@ -172,7 +172,7 @@ private IFCStyledItem GetStyledItemFromOperand(IFCRepresentationItem repItem) if (SecondOperand is IFCRepresentationItem) Importer.TheLog.LogError((SecondOperand as IFCRepresentationItem).Id, ex.Message, false); else - throw ex; + throw; secondSolids = null; } } diff --git a/Source/Revit.IFC.Import/Data/IFCBuildingElementProxy.cs b/Source/Revit.IFC.Import/Data/IFCBuildingElementProxy.cs index 3a8354f7..b7b75912 100644 --- a/Source/Revit.IFC.Import/Data/IFCBuildingElementProxy.cs +++ b/Source/Revit.IFC.Import/Data/IFCBuildingElementProxy.cs @@ -136,7 +136,7 @@ protected override void Create(Document doc) IList clonedGeometry = CloneElementGeometry(doc, this, this, false); foreach (IFCSolidInfo solid in clonedGeometry) { - if (CutSolidByVoids(solid)) + if (CutSolidByVoids(solid, null)) geomObjs.Add(solid.GeometryObject); } diff --git a/Source/Revit.IFC.Import/Data/IFCBuildingStorey.cs b/Source/Revit.IFC.Import/Data/IFCBuildingStorey.cs index 1bdaad36..9a06b8e5 100644 --- a/Source/Revit.IFC.Import/Data/IFCBuildingStorey.cs +++ b/Source/Revit.IFC.Import/Data/IFCBuildingStorey.cs @@ -135,7 +135,7 @@ protected override void Create(Document doc) foundLevel = true; double referenceElevation = GetReferenceElevation(); - double totalElevation = (ObjectLocation?.TotalTransform?.Origin.Z ?? 0.0) + referenceElevation; + double totalElevation = (ObjectLocation?.TotalTransformAfterOffset?.Origin.Z ?? 0.0) + referenceElevation; if (level == null) level = Level.Create(doc, totalElevation); diff --git a/Source/Revit.IFC.Import/Data/IFCConnectedFaceSet.cs b/Source/Revit.IFC.Import/Data/IFCConnectedFaceSet.cs index cc447718..b75fbb20 100644 --- a/Source/Revit.IFC.Import/Data/IFCConnectedFaceSet.cs +++ b/Source/Revit.IFC.Import/Data/IFCConnectedFaceSet.cs @@ -71,7 +71,7 @@ override protected void Process(IFCAnyHandle ifcConnectedFaceSet) HashSet ifcCfsFaces = IFCAnyHandleUtil.GetValidAggregateInstanceAttribute>(ifcConnectedFaceSet, "CfsFaces"); - if (ifcCfsFaces?.Count == 0) + if ((ifcCfsFaces?.Count ?? 0) == 0) { Importer.TheLog.LogError(ifcConnectedFaceSet.StepId, "No faces in connected face set, aborting.", false); return; @@ -115,7 +115,7 @@ override protected void Process(IFCAnyHandle ifcConnectedFaceSet) catch (Exception ex) { if (!AllowInvalidFace) - throw ex; + throw; else { shapeEditScope.BuilderScope.AbortCurrentFace(); diff --git a/Source/Revit.IFC.Import/Data/IFCDistributionPort.cs b/Source/Revit.IFC.Import/Data/IFCDistributionPort.cs index e55bcc8a..82ede0f7 100644 --- a/Source/Revit.IFC.Import/Data/IFCDistributionPort.cs +++ b/Source/Revit.IFC.Import/Data/IFCDistributionPort.cs @@ -106,9 +106,16 @@ protected override void Create(Document doc) lcs = ContainedIn?.ObjectLocation?.TotalTransform; } + if (lcs == null) + { lcs = Transform.Identity; + } + // We could use ObjectLocation?.TotalTransformAfterOffset above, but that is + // slightly different behavior, since TotalTransformAfterOffset is never null. + lcs.Origin += (Importer.TheHybridInfo?.LargeCoordinateOriginOffset ?? XYZ.Zero); + // 2016+ only. XYZ origin = lcs.Origin; diff --git a/Source/Revit.IFC.Import/Data/IFCElement.cs b/Source/Revit.IFC.Import/Data/IFCElement.cs index 1a67c0b0..f7ea2fb6 100644 --- a/Source/Revit.IFC.Import/Data/IFCElement.cs +++ b/Source/Revit.IFC.Import/Data/IFCElement.cs @@ -89,13 +89,6 @@ public IFCFeatureElementSubtraction FillsOpening set { m_FillsOpening = value; } } - /// - /// IfcElements are allowed to serve as Containers whose DirectShapes will contain - /// Geometry from other DirectShapes. - /// - /// True. This will always be true for IfcElements. - public override bool IsAllowedToAggregateGeometry() => true; - /// /// Default constructor. /// diff --git a/Source/Revit.IFC.Import/Data/IFCExtrudedAreaSolid.cs b/Source/Revit.IFC.Import/Data/IFCExtrudedAreaSolid.cs index 9766b91a..050c0e67 100644 --- a/Source/Revit.IFC.Import/Data/IFCExtrudedAreaSolid.cs +++ b/Source/Revit.IFC.Import/Data/IFCExtrudedAreaSolid.cs @@ -828,12 +828,12 @@ private IList GetOrientedCurveList(IList loops, Curve axisCurv extrusionObject = GeometryCreationUtilities.CreateExtrusionGeometry(loops, scaledExtrusionDirection, currDepth, solidOptions); } } - catch (Exception ex) + catch (Exception) { extrusionObject = GetMeshBackup(shapeEditScope, loops, scaledExtrusionDirection, currDepth, guid); if (extrusionObject == null) - throw ex; + throw; } if (extrusionObject != null) diff --git a/Source/Revit.IFC.Import/Data/IFCGrid.cs b/Source/Revit.IFC.Import/Data/IFCGrid.cs index 6e2c6cf2..b33677e9 100644 --- a/Source/Revit.IFC.Import/Data/IFCGrid.cs +++ b/Source/Revit.IFC.Import/Data/IFCGrid.cs @@ -499,7 +499,7 @@ public override string GetSharedParameterName(IFCSharedParameters name, bool isT /// The document. protected override void Create(Document doc) { - Transform lcs = (ObjectLocation != null) ? ObjectLocation.TotalTransform : Transform.Identity; + Transform lcs = (ObjectLocation != null) ? ObjectLocation.TotalTransformAfterOffset : Transform.Identity; CreateOneDirection(UAxes, doc, lcs); CreateOneDirection(VAxes, doc, lcs); diff --git a/Source/Revit.IFC.Import/Data/IFCGridAxis.cs b/Source/Revit.IFC.Import/Data/IFCGridAxis.cs index 48061052..ebc18f8b 100644 --- a/Source/Revit.IFC.Import/Data/IFCGridAxis.cs +++ b/Source/Revit.IFC.Import/Data/IFCGridAxis.cs @@ -340,7 +340,7 @@ public Curve GetAxisCurveForGridPlacement() if (ParentGrid != null && ParentGrid.ObjectLocation != null) { - Transform lcs = ParentGrid.ObjectLocation.TotalTransform; + Transform lcs = ParentGrid.ObjectLocation.TotalTransformAfterOffset; axisCurve = axisCurve.CreateTransformed(lcs); } diff --git a/Source/Revit.IFC.Import/Data/IFCHybridRepresentationItem.cs b/Source/Revit.IFC.Import/Data/IFCHybridRepresentationItem.cs deleted file mode 100644 index ea1f5870..00000000 --- a/Source/Revit.IFC.Import/Data/IFCHybridRepresentationItem.cs +++ /dev/null @@ -1,112 +0,0 @@ -// -// Revit IFC Import library: this library works with Autodesk(R) Revit(R) to import IFC files. -// Copyright (C) 2013 Autodesk, Inc. -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; either -// version 2.1 of the License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -// - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Autodesk.Revit.DB; -using Autodesk.Revit.DB.IFC; -using Revit.IFC.Common.Utility; -using Revit.IFC.Common.Enums; -using Revit.IFC.Import.Geometry; -using Revit.IFC.Import.Utility; - -namespace Revit.IFC.Import.Data -{ - // For Hybrid IFC Import, Revit needs something to represent Body Geometry other than null. - // Body geometry for DirectShapes are actually created by AnyCAD, but there are other data within a RepresentationItem - // that must persist. - // IFCHybridRepresentationItem must inherit from IIFCBooleanOperand, since it may need to represent an operand, which (in essence) - // has already been created. - public class IFCHybridRepresentationItem : IFCRepresentationItem, IIFCBooleanOperand - { - protected IFCHybridRepresentationItem() - { - } - - /// - /// Constructor to create a new IFCHybridRepresentationItem. - /// - /// Handle representing IFCRepresentationItem. - protected IFCHybridRepresentationItem(IFCAnyHandle ifcRepresentationItem) - { - Process(ifcRepresentationItem); - } - - /// - /// Process IFCHybridRepresentationItem members. - /// Even though we don't have any members, it exists to maintain a parallel structure of other IFCEntity processing. - /// - /// Handle representing IFCRepresentationItem. - override protected void Process(IFCAnyHandle ifcRepresentationItem) - { - base.Process(ifcRepresentationItem); - } - - /// - /// Create the IFCHybridRepresentationItem. - /// - /// Handle corresponding to the IFCRepresentationItem that AnyCAD already processed. - /// IFCHybridRepresentationItem object. - public static IFCHybridRepresentationItem ProcessIFCHybridRepresentationItem(IFCAnyHandle ifcRepresentationItem) - { - if (IFCAnyHandleUtil.IsNullOrHasNoValue(ifcRepresentationItem)) - { - Importer.TheLog.LogNullError(IFCEntityType.IfcRepresentationItem); - return null; - } - - IFCEntity hybridRepresentationItem; - IFCImportFile.TheFile.EntityMap.TryGetValue(ifcRepresentationItem.StepId, out hybridRepresentationItem); - if (hybridRepresentationItem != null) - return (hybridRepresentationItem as IFCHybridRepresentationItem); - - return new IFCHybridRepresentationItem(ifcRepresentationItem); - } - - /// - /// Return geometry for a particular representation item. - /// In the case of Hybrid, Geometry has already been created, so only process the IfcStyledItem. - /// - /// The geometry creation scope. - /// Local coordinate system for the geometry, including scale, potentially non-uniform. - /// The guid of an element for which represntation is being created. - /// Zero or more created Solids. - public IList CreateGeometry(IFCImportShapeEditScope shapeEditScope, Transform scaledLcs, string guid) - { - if (StyledByItem != null) - StyledByItem.Create(shapeEditScope); - - return null; - } - - /// - /// In case of a Boolean operation failure, provide a recommended direction to shift the geometry in for a second attempt. - /// - /// The local transform for this entity. - /// An XYZ representing a unit direction vector, or null if no direction is suggested. - /// If the 2nd attempt fails, a third attempt will be done with a shift in the opposite direction. - public XYZ GetSuggestedShiftDirection(Transform lcs) - { - return null; - } - - } -} diff --git a/Source/Revit.IFC.Import/Data/IFCImportFile.cs b/Source/Revit.IFC.Import/Data/IFCImportFile.cs index dcccf5ba..8f018a3c 100644 --- a/Source/Revit.IFC.Import/Data/IFCImportFile.cs +++ b/Source/Revit.IFC.Import/Data/IFCImportFile.cs @@ -890,10 +890,10 @@ public static ElementId LinkInFile(string originalIFCFileName, string baseLocalF Importer.PostDelayedLinkErrors(originalDocument); linkTransaction.Commit(); } - catch (Exception ex) + catch (Exception) { linkTransaction.RollBack(); - throw ex; + throw; } } else // reload from @@ -1151,9 +1151,12 @@ static IFCFileModelOptions GetIFCFileModelOptions(string schemaName, out IFCSche } else if (schemaName.Equals("IFC4X3", StringComparison.OrdinalIgnoreCase)) { - //schemaVersion = IFCSchemaVersion.IFC4x3; schemaVersion = IFCSchemaVersion.IFC4x3; } + else if (schemaName.Equals("IFC4X3_ADD2", StringComparison.OrdinalIgnoreCase)) + { + schemaVersion = IFCSchemaVersion.IFC4x3_ADD2; + } else throw new ArgumentException("Invalid or unsupported schema: " + schemaName); diff --git a/Source/Revit.IFC.Import/Data/IFCIndexedPolyCurve.cs b/Source/Revit.IFC.Import/Data/IFCIndexedPolyCurve.cs index 09a2754d..909bcc56 100644 --- a/Source/Revit.IFC.Import/Data/IFCIndexedPolyCurve.cs +++ b/Source/Revit.IFC.Import/Data/IFCIndexedPolyCurve.cs @@ -108,7 +108,7 @@ protected override void Process(IFCAnyHandle ifcCurve) if (IFCImportFile.HasUndefinedAttribute(ex)) IFCImportFile.TheFile.DowngradeIFC4SchemaTo(IFCSchemaVersion.IFC4); else - throw ex; + throw; } IFCCartesianPointList pointList = IFCCartesianPointList.ProcessIFCCartesianPointList(points); diff --git a/Source/Revit.IFC.Import/Data/IFCLocation.cs b/Source/Revit.IFC.Import/Data/IFCLocation.cs index f4f11cb0..1dae731e 100644 --- a/Source/Revit.IFC.Import/Data/IFCLocation.cs +++ b/Source/Revit.IFC.Import/Data/IFCLocation.cs @@ -45,6 +45,19 @@ public Transform TotalTransform get { return RelativeTo != null ? RelativeTo.TotalTransform.Multiply(RelativeTransform) : RelativeTransform; } } + /// + /// The total transform, taking into account any large coordinate offset. + /// + public Transform TotalTransformAfterOffset + { + get + { + Transform totalTransform = TotalTransform ?? Transform.Identity; + totalTransform.Origin += (Importer.TheHybridInfo?.LargeCoordinateOriginOffset ?? XYZ.Zero); + return totalTransform; + } + } + /// /// The relative transform. /// @@ -295,7 +308,7 @@ public static IFCLocation ProcessIFCObjectPlacement(IFCAnyHandle ifcObjectPlacem public static void WarnIfFaraway(IFCProduct product) { - XYZ origin = product?.ObjectLocation?.TotalTransform?.Origin; + XYZ origin = product?.ObjectLocation?.TotalTransformAfterOffset?.Origin; if (origin != null && !XYZ.IsWithinLengthLimits(origin)) { Importer.TheLog.LogWarning(product.Id, "This entity has an origin that is outside of Revit's creation limits. This could result in bad graphical display of geometry.", false); diff --git a/Source/Revit.IFC.Import/Data/IFCMappedItem.cs b/Source/Revit.IFC.Import/Data/IFCMappedItem.cs index eec928ea..24605deb 100644 --- a/Source/Revit.IFC.Import/Data/IFCMappedItem.cs +++ b/Source/Revit.IFC.Import/Data/IFCMappedItem.cs @@ -68,8 +68,6 @@ override protected void Process(IFCAnyHandle item) return; MappingSource = IFCRepresentationMap.ProcessIFCRepresentationMap(mappingSource); - if (MappingSource.IsHybridOnly()) - return; // We will not fail if the transform is not given, but instead assume it to be the identity. IFCAnyHandle mappingTarget = IFCImportHandleUtil.GetRequiredInstanceAttribute(item, "MappingTarget", false); diff --git a/Source/Revit.IFC.Import/Data/IFCMaterial.cs b/Source/Revit.IFC.Import/Data/IFCMaterial.cs index c8762f61..f77325ff 100644 --- a/Source/Revit.IFC.Import/Data/IFCMaterial.cs +++ b/Source/Revit.IFC.Import/Data/IFCMaterial.cs @@ -83,16 +83,19 @@ protected override void Process(IFCAnyHandle ifcMaterial) Name = IFCImportHandleUtil.GetRequiredStringAttribute(ifcMaterial, "Name", true); - List hasRepresentation = null; - if (IFCImportFile.TheFile.SchemaVersionAtLeast(IFCSchemaVersion.IFC2x3)) - hasRepresentation = IFCAnyHandleUtil.GetAggregateInstanceAttribute>(ifcMaterial, "HasRepresentation"); - - if ((hasRepresentation?.Count ?? 0) == 1) + if (!Importer.TheOptions.IsHybridImport) { - if (!IFCAnyHandleUtil.IsSubTypeOf(hasRepresentation[0], IFCEntityType.IfcMaterialDefinitionRepresentation)) - Importer.TheLog.LogUnexpectedTypeError(hasRepresentation[0], IFCEntityType.IfcMaterialDefinitionRepresentation, false); - else - MaterialDefinitionRepresentation = IFCProductRepresentation.ProcessIFCProductRepresentation(hasRepresentation[0]); + List hasRepresentation = null; + if (IFCImportFile.TheFile.SchemaVersionAtLeast(IFCSchemaVersion.IFC2x3)) + hasRepresentation = IFCAnyHandleUtil.GetAggregateInstanceAttribute>(ifcMaterial, "HasRepresentation"); + + if ((hasRepresentation?.Count ?? 0) == 1) + { + if (!IFCAnyHandleUtil.IsSubTypeOf(hasRepresentation[0], IFCEntityType.IfcMaterialDefinitionRepresentation)) + Importer.TheLog.LogUnexpectedTypeError(hasRepresentation[0], IFCEntityType.IfcMaterialDefinitionRepresentation, false); + else + MaterialDefinitionRepresentation = IFCProductRepresentation.ProcessIFCProductRepresentation(hasRepresentation[0]); + } } Importer.TheLog.AddToElementCount(); diff --git a/Source/Revit.IFC.Import/Data/IFCObject.cs b/Source/Revit.IFC.Import/Data/IFCObject.cs index d6b0ce2c..d58f9d56 100644 --- a/Source/Revit.IFC.Import/Data/IFCObject.cs +++ b/Source/Revit.IFC.Import/Data/IFCObject.cs @@ -212,26 +212,27 @@ protected override void Process(IFCAnyHandle ifcObject) else if (IFCAnyHandleUtil.IsSubTypeOf(isDefinedByHandle, IFCEntityType.IfcRelDefinesByType)) { // For Hybrid IFC Import, preprocess IFCRelDefinesByType. - // Need to do this because the TypeObject should have a GlobalId --> DirectShapeType before Revit calls ProcessIFCTypeObject. - // This will add an entry to the HybridMap (IFCTypeObject GlobalId --> DirectShapeType ElementId) so Revit will know later that it doesn't need + // Need to do this because the TypeObject should have a STEP Id --> DirectShapeType before Revit calls ProcessIFCTypeObject. + // This will add an entry to the HybridMap (IFCTypeObject STEP Id --> DirectShapeType ElementId) so Revit will know later that it doesn't need // to create a new DirectShapeType, and which DirectShapeType to use. - if (Importer.TheOptions.IsHybridImport && (Importer.TheHybridInfo?.HybridMap?.ContainsKey(GlobalId) ?? false)) + ElementId ifcObjectElementId = IFCImportHybridInfo.GetHybridMapInformation(ifcObject); + if (IFCImportHybridInfo.IsValidElementId(ifcObjectElementId)) { IFCAnyHandle typeObject = IFCAnyHandleUtil.GetInstanceAttribute(isDefinedByHandle, "RelatingType"); - if (IFCAnyHandleUtil.IsNullOrHasNoValue(typeObject)) { Importer.TheLog.LogNullError(IFCEntityType.IfcTypeObject); + return; } if (!IFCAnyHandleUtil.IsSubTypeOf(typeObject, IFCEntityType.IfcTypeObject)) { Importer.TheLog.LogUnhandledSubTypeError(typeObject, IFCEntityType.IfcTypeObject, false); + return; } - Importer.TheHybridInfo.AddTypeToHybridMap(GlobalId, typeObject); + Importer.TheHybridInfo.AddTypeToHybridMap(ifcObjectElementId, typeObject); } - ProcessIFCRelDefinesByType(isDefinedByHandle); } else diff --git a/Source/Revit.IFC.Import/Data/IFCObjectDefinition.cs b/Source/Revit.IFC.Import/Data/IFCObjectDefinition.cs index ecb3ee2b..64e6d047 100644 --- a/Source/Revit.IFC.Import/Data/IFCObjectDefinition.cs +++ b/Source/Revit.IFC.Import/Data/IFCObjectDefinition.cs @@ -1,4 +1,4 @@ -// +// // Revit IFC Import library: this library works with Autodesk(R) Revit(R) to import IFC files. // Copyright (C) 2013 Autodesk, Inc. // @@ -120,31 +120,6 @@ public virtual bool GroupSubElements() /// public IFCObjectDefinition Decomposes { get; set; } = null; - /// - /// Indicates if this IFCObjectDefinition is allowed to act as a Container whose DirectShape will have Geometry that will contain - /// Geometry from DirectShapes corresponding to other entities. - /// This is defined via an IfcRelAggregates relationship, but it only applies if the "RelatedTo" entity will result in a DirectShape - /// in the Revit Document. - /// This is used to indicate that this entity can act in this capacity. - /// - /// False unless overridden by a derived class. - public virtual bool IsAllowedToAggregateGeometry() => false; - - /// - /// Indicates if this IfcObjectDefinition is acting as a Container whose DirectShape will have Geometry that will contain - /// Geometry from DirectShapes corresponding to other entities. - /// This only applies to Hybrid Import. - /// This is used to indicate that this actually is acting in this capacity. - /// - /// - public bool IsHybridImportContainer() - { - return Importer.TheOptions.IsHybridImport && - IsAllowedToAggregateGeometry() && - ((ComposedObjectDefinitions?.Count ?? 0) > 0) && - (Importer.TheHybridInfo?.ContainerMap?.ContainsKey(Id) ?? false); - } - /// /// Get the reference elevation of this object, located in the containing IFCBuilding. /// @@ -415,7 +390,7 @@ private IList GetOrCloneGeometry(Document doc, IFCObjectDefiniti foreach (IFCSolidInfo solid in clonedGeometry) { - if (CutSolidByVoids(solid)) + if (CutSolidByVoids(solid, null)) geomObjs.Add(solid.GeometryObject); } @@ -426,9 +401,10 @@ private IList GetOrCloneGeometry(Document doc, IFCObjectDefiniti /// Cut a IFCSolidInfo by the voids in this IFCProduct, if any. /// /// The solid information. + /// Extra voids from AnyCAD-created openings. /// False if the return solid is empty; true otherwise. /// Overridden at the IFCProduct level. - protected virtual bool CutSolidByVoids(IFCSolidInfo solidInfo) + protected virtual bool CutSolidByVoids(IFCSolidInfo solidInfo, IList createdVoids) { return true; } @@ -555,9 +531,10 @@ protected override void Process(IFCAnyHandle ifcObjectDefinition) PredefinedType = GetPredefinedType(ifcObjectDefinition); ElementId createdElementId = ElementId.InvalidElementId; - if (Importer.TheOptions.IsHybridImport) + ElementId objectDefinitionElementId = IFCImportHybridInfo.GetHybridMapInformation(Id); + if (objectDefinitionElementId != null) { - Importer.TheHybridInfo?.HybridMap?.TryGetValue(GlobalId, out createdElementId); + createdElementId = objectDefinitionElementId; } // If we aren't importing this category, skip processing. diff --git a/Source/Revit.IFC.Import/Data/IFCProduct.cs b/Source/Revit.IFC.Import/Data/IFCProduct.cs index 326d2785..04fd428a 100644 --- a/Source/Revit.IFC.Import/Data/IFCProduct.cs +++ b/Source/Revit.IFC.Import/Data/IFCProduct.cs @@ -1,4 +1,4 @@ -// +// // Revit IFC Import library: this library works with Autodesk(R) Revit(R) to import IFC files. // Copyright (C) 2013 Autodesk, Inc. // @@ -109,13 +109,16 @@ protected override void Process(IFCAnyHandle ifcProduct) base.Process(ifcProduct); + // Don't even process IfcProductRepresentation if this IfcProduct was imported via Hybrid import. + if (IFCImportHybridInfo.IsValidElementId(IFCImportHybridInfo.GetHybridMapInformation(Id))) + { + return; + } + IFCAnyHandle ifcProductRepresentation = IFCImportHandleUtil.GetOptionalInstanceAttribute(ifcProduct, "Representation"); if (!IFCAnyHandleUtil.IsNullOrHasNoValue(ifcProductRepresentation)) { - using (RepresentationsAlreadyCreatedSetter setter = new RepresentationsAlreadyCreatedSetter(GlobalId)) - { - ProductRepresentation = IFCProductRepresentation.ProcessIFCProductRepresentation(ifcProductRepresentation); - } + ProductRepresentation = IFCProductRepresentation.ProcessIFCProductRepresentation(ifcProductRepresentation); } } @@ -194,16 +197,25 @@ protected override void CreateParametersInternal(Document doc, Element element) /// Private function to determine whether an IFCProduct directly contains valid geometry. /// /// - /// For Hybrid IFC Import, ProductRepresentation should still exist and be valid. + /// For Hybrid IFC Import, ProductRepresentation may not exist, but Geometry is instead within a GeometryInstance. /// /// True if the IFCProduct directly contains valid geometry. private bool HasValidTopLevelGeometry() { + // If this IfcProduct was imported via Hybrid import, ProductRepresentation may not exist. Check for DirectShape instead. + ElementId directShapeId = IFCImportHybridInfo.GetHybridMapInformation(Id); + if (IFCImportHybridInfo.IsValidElementId(directShapeId)) + { + // Or continue the check and require that the directShapeId point to + // a DirectShape (not DirectShapeType) -- unsure! + return Importer.TheHybridInfo.IsValidDirectShape(directShapeId); + } + return (ProductRepresentation != null && ProductRepresentation.IsValid()); } /// - /// Private function to determine whether an IFCProduct contins geometry in a sub-element. + /// Private function to determine whether an IFCProduct contains geometry in a sub-element. /// /// A list of already visited entities, to avoid infinite recursion. /// True if the IFCProduct directly or indirectly contains geometry. @@ -233,8 +245,9 @@ private bool HasValidSubElementGeometry(IList visitedEntities) /// Cut a IFCSolidInfo by the voids in this IFCProduct, if any. /// /// The solid information. + /// Extra voids from AnyCAD-created openings. /// False if the return solid is empty; true otherwise. - protected override bool CutSolidByVoids(IFCSolidInfo solidInfo) + protected override bool CutSolidByVoids(IFCSolidInfo solidInfo, IList createdVoids) { // We only cut "Body" representation items. if (solidInfo.RepresentationIdentifier != IFCRepresentationIdentifier.Body) @@ -267,16 +280,38 @@ protected override bool CutSolidByVoids(IFCSolidInfo solidInfo) return true; } + List> allVoids = new List>(); + + if (createdVoids != null) + { + foreach (Solid createdVoid in createdVoids) + { + if (createdVoid != null) + { + allVoids.Add(Tuple.Create(createdVoid, null, -1)); + } + } + } + foreach (IFCVoidInfo voidInfo in voidsToUse) { Solid voidObject = voidInfo.GeometryObject as Solid; + int voidId = voidInfo.Id; if (voidObject == null) { - Importer.TheLog.LogError(Id, "Can't cut Solid geometry with a Mesh (# " + voidInfo.Id + "), ignoring.", false); + Importer.TheLog.LogError(Id, "Can't cut Solid geometry with a Mesh (# " + voidId + "), ignoring.", false); continue; } Transform voidTransform = voidInfo.TotalTransform; + allVoids.Add(Tuple.Create(voidObject, voidTransform, voidId)); + } + + foreach (Tuple currentVoid in allVoids) + { + Solid voidObject = currentVoid.Item1; + Transform voidTransform = currentVoid.Item2; + int voidId = currentVoid.Item3; if (voidTransform != null) { @@ -288,7 +323,7 @@ protected override bool CutSolidByVoids(IFCSolidInfo solidInfo) } } - solidInfo.GeometryObject = IFCGeometryUtil.ExecuteSafeBooleanOperation(solidInfo.Id, voidInfo.Id, + solidInfo.GeometryObject = IFCGeometryUtil.ExecuteSafeBooleanOperation(solidInfo.Id, voidId, (solidInfo.GeometryObject as Solid), voidObject, BooleanOperationsType.Difference, null); if (solidInfo.GeometryObject == null || (solidInfo.GeometryObject as Solid).Faces.IsEmpty) @@ -303,13 +338,36 @@ private Transform CalculateLocalCoordinateSystem() Transform lcs = IFCImportFile.TheFile.IFCProject.WorldCoordinateSystem; if (lcs == null) return ObjectLocation?.TotalTransform ?? Transform.Identity; - + if (ObjectLocation != null) return lcs.Multiply(ObjectLocation.TotalTransform); return lcs; } + private IList GetCreatedGeometries(Document doc, IFCProduct opening, Options geometryOptions) + { + ElementId createdOpeningId = IFCImportHybridInfo.GetHybridMapInformation(opening.Id); + if (IFCImportHybridInfo.IsValidElementId(createdOpeningId)) + { + DirectShape openingElement = doc.GetElement(createdOpeningId) as DirectShape; + if (openingElement != null) + { + GeometryElement geometryElement = openingElement.get_Geometry(geometryOptions); + + SolidMeshGeometryInfo solidInfo = new SolidMeshGeometryInfo(); + solidInfo.CollectSolidMeshGeometry(geometryElement, Importer.TheCache.AllocatedGeometryObjectCache); + return solidInfo.GetSolids(); + } + else + { + Importer.TheLog.LogError(Id, "Object created in legacy mode missing opening information.", false); + } + } + + return null; + } + /// /// Creates or populates Revit elements based on the information contained in this class. /// @@ -318,13 +376,24 @@ protected override void Create(Document doc) { bool preventInstances = false; IFCElement element = this as IFCElement; + List createdVoids = new List(); + if (element != null) { preventInstances = this is IFCOpeningElement; + + Options geometryOptions = new Options(); + foreach (IFCFeatureElement opening in element.Openings) { try { + IList createdGeometries = GetCreatedGeometries(doc, opening, geometryOptions); + if (createdGeometries != null) + { + createdVoids.AddRange(createdGeometries); + } + preventInstances = true; // Create the actual Revit element based on the IFCFeatureElement here. ElementId openingId = CreateElement(doc, opening); @@ -359,7 +428,7 @@ protected override void Create(Document doc) // If this entity will be a container DirectShape, it may not have any valid top-level geometry at this time. // Detect the situation and allow Element Creation to proceed for Hybrid Import only. - if (HasValidTopLevelGeometry() || IsHybridImportContainer()) + if (HasValidTopLevelGeometry()) { // IFCImportShapeEditScope will not create Body geometry for Hybrid IFC Import, but it may need to create other geometry. using (IFCImportShapeEditScope shapeEditScope = IFCImportShapeEditScope.Create(doc, this)) @@ -367,58 +436,33 @@ protected override void Create(Document doc) Transform lcs = CalculateLocalCoordinateSystem(); shapeEditScope.PreventInstances = preventInstances; - - // Hybrid Import only: An IfcProduct whose DirectShape will be a container might not have a DirectShape created yet. - // If this is the case, create an empty DirectShape for the container. - // If so, create an empty DirectShape to hold container Geometry. - if (Importer.TheOptions.IsHybridImport && (Importer.TheHybridInfo?.HybridMap != null)) - { - if (IsHybridImportContainer() && !Importer.TheHybridInfo.HybridMap.ContainsKey(GlobalId)) - { - CreatedElementId = Importer.TheHybridInfo.CreateEmptyContainer(this); - Importer.TheLog.LogComment(Id, $"Creating Empty Container for {GlobalId}:{CreatedElementId}", false); - } - } - if (HasValidTopLevelGeometry()) + // If we are not applying transforms to the geometry, then pass in the identity matrix. + // Lower down this method we then pass lcs to the consumer element, so that it can apply + // the transform as required. + Transform transformToUse = Importer.TheProcessor.ApplyTransforms ? lcs : Transform.Identity; + bool applyHybridOffset = Importer.TheOptions.IsHybridImport && Importer.TheHybridInfo != null && ObjectLocation?.RelativeTo == null; + if (applyHybridOffset) { - // If we are not applying transforms to the geometry, then pass in the identity matrix. - // Lower down this method we then pass lcs to the consumer element, so that it can apply - // the transform as required. - Transform transformToUse = Importer.TheProcessor.ApplyTransforms ? lcs : Transform.Identity; - if (Importer.TheOptions.IsHybridImport && ( - Importer.TheHybridInfo != null)) - { - transformToUse.Origin += Importer.TheHybridInfo.LargeCoordinateOriginOffset; - } - - // The name can be added as well. but it is usually less useful than 'oid' - string myId = GlobalId; // + "(" + Name + ")"; - - ProductRepresentation.CreateProductRepresentation(shapeEditScope, transformToUse, myId); + transformToUse.Origin += Importer.TheHybridInfo.LargeCoordinateOriginOffset; } - // Everything up to this point needs to be done for Hybrid IFC Import as well. The Product Representation will not contain - // geometry, but it will contain parameters that are needed for the DirectShape imported for Hybrid IFC Import. - if (Importer.TheOptions.IsHybridImport && GlobalId != null) + // If Revit has already created a DirectShape from the IfcProduct, don't try and create a new representation. + ElementId hybridDirectShapeElementId = IFCImportHybridInfo.GetHybridMapInformation(Id); + if (IFCImportHybridInfo.IsValidElementId(hybridDirectShapeElementId)) { - if ((Importer.TheHybridInfo?.HybridMap?.TryGetValue(GlobalId, out ElementId hybridElementId) ?? false) && - hybridElementId != ElementId.InvalidElementId) - { - // "Create" a DirectShape Element. - // This is for those Elements imported via the ATF Pipeline, or for those Elements created above simply to hold an empty container. - CreatedGeometry = Importer.TheHybridInfo.HandleHybridProductCreation(shapeEditScope, this, ref hybridElementId); - CreatedElementId = hybridElementId; - } + CreatedGeometry = Importer.TheHybridInfo.HandleHybridProductCreation(this, hybridDirectShapeElementId); + CreatedElementId = hybridDirectShapeElementId; } + else + { + ProductRepresentation.CreateProductRepresentation(shapeEditScope, transformToUse, GlobalId); - if (CreatedElementId == ElementId.InvalidElementId) - { int numSolids = Solids.Count; // Attempt to cut each solid with each void. for (int solidIdx = 0; solidIdx < numSolids; solidIdx++) { - if (!CutSolidByVoids(Solids[solidIdx])) + if (!CutSolidByVoids(Solids[solidIdx], createdVoids)) { Solids.RemoveAt(solidIdx); solidIdx--; @@ -446,19 +490,19 @@ protected override void Create(Document doc) { // We need to check if the solid created is good enough for DirectShape. If not, warn and use a fallback Mesh. GeometryObject currObject = geometryObject.GeometryObject; - if (currObject is Solid) + + if (currObject != null) { - Solid solid = currObject as Solid; - if (!shape.IsValidGeometry(solid)) + IList adjustedObjects = IFCGeometryUtil.AdjustGeometryObjectsIfNeeded(currObject, shape, Id); + if (adjustedObjects != null) { - Importer.TheLog.LogWarning(Id, "Couldn't create valid solid, reverting to mesh.", false); - directShapeGeometries.AddRange(IFCGeometryUtil.CreateMeshesFromSolid(solid)); - currObject = null; + directShapeGeometries.AddRange(adjustedObjects); + } + else + { + directShapeGeometries.Add(currObject); } } - - if (currObject != null) - directShapeGeometries.Add(currObject); } // We will use the first IfcTypeObject id, if it exists. In general, there should be 0 or 1. @@ -546,9 +590,6 @@ public static IFCProduct ProcessIFCProduct(IFCAnyHandle ifcProduct) if (IFCImportFile.TheFile.EntityMap.TryGetValue(ifcProduct.StepId, out cachedProduct)) return (cachedProduct as IFCProduct); - if (IFCAnyHandleUtil.IsValidSubTypeOf(ifcProduct, IFCEntityType.IfcSpatialStructureElement)) - return IFCSpatialStructureElement.ProcessIFCSpatialStructureElement(ifcProduct); - if (IFCAnyHandleUtil.IsValidSubTypeOf(ifcProduct, IFCEntityType.IfcElement)) return IFCElement.ProcessIFCElement(ifcProduct); @@ -563,6 +604,17 @@ public static IFCProduct ProcessIFCProduct(IFCAnyHandle ifcProduct) if (IFCAnyHandleUtil.IsValidSubTypeOf(ifcProduct, IFCEntityType.IfcAnnotation)) return IFCAnnotation.ProcessIFCAnnotation(ifcProduct); + + if (IFCImportFile.TheFile.SchemaVersionAtLeast(Enums.IFCSchemaVersion.IFC4Obsolete)) + { + if (IFCAnyHandleUtil.IsValidSubTypeOf(ifcProduct, IFCEntityType.IfcSpatialElement)) + return IFCSpatialElement.ProcessIFCSpatialElement(ifcProduct); + } + else + { + if (IFCAnyHandleUtil.IsValidSubTypeOf(ifcProduct, IFCEntityType.IfcSpatialStructureElement)) + return IFCSpatialStructureElement.ProcessIFCSpatialStructureElement(ifcProduct); + } } catch (Exception ex) { diff --git a/Source/Revit.IFC.Import/Data/IFCProfileDef.cs b/Source/Revit.IFC.Import/Data/IFCProfileDef.cs index 6ebf3e81..50f6a1a6 100644 --- a/Source/Revit.IFC.Import/Data/IFCProfileDef.cs +++ b/Source/Revit.IFC.Import/Data/IFCProfileDef.cs @@ -98,7 +98,7 @@ protected bool AppendSegmentsToCurveLoop(CurveLoop curveLoop, IList /// Processes IfcProject attributes. /// @@ -257,13 +276,11 @@ protected override void Process(IFCAnyHandle ifcProjectHandle) } ProjectLocation projectLocation = IFCImportFile.TheFile.Document.ActiveProjectLocation; - ProjectPosition projectPosition; if (projectLocation != null) { if (hasMapConv) { - projectPosition = new ProjectPosition(geoRef.X, geoRef.Y, geoRef.Z, trueNorth); - projectLocation.SetProjectPosition(XYZ.Zero, projectPosition); + UpdateProjectLocation(projectLocation, geoRef, trueNorth); if (!string.IsNullOrEmpty(geoRefName)) { @@ -313,8 +330,7 @@ protected override void Process(IFCAnyHandle ifcProjectHandle) } } - projectPosition = new ProjectPosition(geoRef.X, geoRef.Y, geoRef.Z, trueNorth); - projectLocation.SetProjectPosition(XYZ.Zero, projectPosition); + UpdateProjectLocation(projectLocation, geoRef, trueNorth); } } } diff --git a/Source/Revit.IFC.Import/Data/IFCRepresentation.cs b/Source/Revit.IFC.Import/Data/IFCRepresentation.cs index 78541c92..9a1e3cc6 100644 --- a/Source/Revit.IFC.Import/Data/IFCRepresentation.cs +++ b/Source/Revit.IFC.Import/Data/IFCRepresentation.cs @@ -21,13 +21,17 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using System.Windows.Annotations; +using System.Windows; using Autodesk.Revit.DB; using Autodesk.Revit.DB.IFC; +using Microsoft.VisualBasic.Logging; using Revit.IFC.Common.Enums; using Revit.IFC.Common.Utility; using Revit.IFC.Import.Enums; using Revit.IFC.Import.Geometry; using Revit.IFC.Import.Utility; +using static System.Windows.Forms.VisualStyles.VisualStyleElement.Tab; namespace Revit.IFC.Import.Data { @@ -74,28 +78,21 @@ protected IFCRepresentation() } - /// - /// Determine if the IFCRepresentationMap only has at least 1 IFCHybridInformation. - /// - /// True if the IFCRepresentationMap only has at least 1 IFCHybridInformation. - public bool IsHybridOnly() - { - if ((RepresentationItems?.Count ?? 0) == 0) - return false; - - foreach (IFCRepresentationItem item in RepresentationItems) - { - if (!(item is IFCHybridRepresentationItem)) - return false; - } - - return true; - } - - private IFCRepresentationIdentifier GetRepresentationIdentifier(string identifier, IFCAnyHandle ifcRepresentation) + private IFCRepresentationIdentifier GetRepresentationIdentifier(string identifier, string type, IFCAnyHandle ifcRepresentation) { if (Enum.TryParse(identifier, true, out IFCRepresentationIdentifier ifcRepresentationIdentifier)) { + // Special case for Annotation. + switch (ifcRepresentationIdentifier) + { + case IFCRepresentationIdentifier.Annotation: + if (string.Compare(type, "Annotation2D", true) == 0) + { + return IFCRepresentationIdentifier.FootPrint; + } + return IFCRepresentationIdentifier.Body; + } + return ifcRepresentationIdentifier; } @@ -104,19 +101,31 @@ private IFCRepresentationIdentifier GetRepresentationIdentifier(string identifie // NOTE: This list includes invalid or obsolete identifiers found in real IFC files. if ((string.Compare(identifier, "Facetation", true) == 0) || string.IsNullOrWhiteSpace(identifier)) + { return IFCRepresentationIdentifier.Body; + } + if (string.Compare(identifier, "BoundingBox", true) == 0) + { return IFCRepresentationIdentifier.Box; - if ((string.Compare(identifier, "Annotation", true) == 0) || - (string.Compare(identifier, "Profile", true) == 0) || - (string.Compare(identifier, "Plan", true) == 0)) + } + + if (string.Compare(identifier, "Plan", true) == 0) + { return IFCRepresentationIdentifier.FootPrint; + } + if (IFCAnyHandleUtil.IsSubTypeOf(ifcRepresentation, IFCEntityType.IfcStyledRepresentation)) + { return IFCRepresentationIdentifier.Style; + } + if (string.Compare(identifier, "Body-Fallback", true) == 0) + { return IFCRepresentationIdentifier.BodyFallback; + } - Importer.TheLog.LogWarning(ifcRepresentation.StepId, "Found unknown representation type: " + identifier, false); + Importer.TheLog.LogWarning(ifcRepresentation.StepId, "Found unknown representation identifier: " + identifier, false); return IFCRepresentationIdentifier.Other; } @@ -158,7 +167,10 @@ override protected void Process(IFCAnyHandle ifcRepresentation) base.Process(ifcRepresentation); string identifier = IFCImportHandleUtil.GetOptionalStringAttribute(ifcRepresentation, "RepresentationIdentifier", null); - Identifier = GetRepresentationIdentifier(identifier, ifcRepresentation); + + string type = IFCImportHandleUtil.GetOptionalStringAttribute(ifcRepresentation, "RepresentationType", null); + + Identifier = GetRepresentationIdentifier(identifier, type, ifcRepresentation); LayerAssignment = IFCPresentationLayerAssignment.GetTheLayerAssignment(ifcRepresentation); @@ -184,12 +196,17 @@ override protected void Process(IFCAnyHandle ifcRepresentation) continue; } - // Special processing for bounding boxes - only IfcBoundingBox allowed. if (IFCAnyHandleUtil.IsSubTypeOf(item, IFCEntityType.IfcBoundingBox)) { - // Don't read in Box represenation unless options allow it. + // If Hybrid/Non-legacy, don't process BoundingBox. + if (Importer.TheOptions.IsHybridImport) + continue; + + // Don't read in Box representation unless options allow it. if (IFCImportFile.TheFile.Options.ProcessBoundingBoxGeometry == IFCProcessBBoxOptions.Never) + { Importer.TheLog.LogWarning(item.StepId, "BoundingBox not imported with ProcessBoundingBoxGeometry=Never", false); + } else { if (BoundingBox != null) @@ -201,7 +218,9 @@ override protected void Process(IFCAnyHandle ifcRepresentation) } } else + { repItem = IFCRepresentationItem.ProcessIFCRepresentationItem(item); + } } catch (Exception ex) { @@ -213,10 +232,6 @@ override protected void Process(IFCAnyHandle ifcRepresentation) } } - // If we have a body representation and we have already gone through Hybrid, skip the rest of the work here. - if (Identifier == IFCRepresentationIdentifier.Body && (Importer.TheHybridInfo?.RepresentationsAlreadyCreated ?? false)) - return; - IFCAnyHandle representationContext = IFCImportHandleUtil.GetRequiredInstanceAttribute(ifcRepresentation, "ContextOfItems", false); if (representationContext != null) Context = IFCRepresentationContext.ProcessIFCRepresentationContext(representationContext); @@ -265,7 +280,7 @@ private void CreateBoxShape(IFCImportShapeEditScope shapeEditScope, Transform sc /// /// The geometry creation scope. /// Local coordinate system for the geometry, including scale, potentially non-uniform. - /// The guid of an element for which represntation is being created. + /// The guid of an element for which representation is being created. public void CreateShape(IFCImportShapeEditScope shapeEditScope, Transform scaledLcs, string guid) { // Special handling for Box representation. We may decide to create an IFCBoundingBox class and stop this special treatment. diff --git a/Source/Revit.IFC.Import/Data/IFCRepresentationItem.cs b/Source/Revit.IFC.Import/Data/IFCRepresentationItem.cs index 14ed537a..9750c067 100644 --- a/Source/Revit.IFC.Import/Data/IFCRepresentationItem.cs +++ b/Source/Revit.IFC.Import/Data/IFCRepresentationItem.cs @@ -72,10 +72,6 @@ override protected void Process(IFCAnyHandle item) LayerAssignment = IFCPresentationLayerAssignment.GetTheLayerAssignment(item); - // Don't bother processing styled items we will never use. - if (this is IFCHybridRepresentationItem) - return; - // IFC2x has a different representation for styled items which we don't support. ICollection styledByItems = null; if (Importer.TheCache.StyledByItems.TryGetValue(item, out styledByItems)) @@ -159,66 +155,58 @@ public static IFCRepresentationItem ProcessIFCRepresentationItem(IFCAnyHandle if return null; } - // Skip Body Geometry if doing Hybrid IFC Import and currently processing Hybrid IfcProductRepresentation. - bool skipBodyGeometry = (Importer.TheOptions.IsHybridImport) && (Importer.TheHybridInfo?.RepresentationsAlreadyCreated ?? false); - if (!skipBodyGeometry) + // Legacy. + if (IFCAnyHandleUtil.IsSubTypeOf(ifcRepresentationItem, IFCEntityType.IfcTopologicalRepresentationItem)) + return IFCTopologicalRepresentationItem.ProcessIFCTopologicalRepresentationItem(ifcRepresentationItem); + + // TODO: Move everything below to IFCGeometricRepresentationItem, once it is created. + if (IFCAnyHandleUtil.IsValidSubTypeOf(ifcRepresentationItem, IFCEntityType.IfcFaceBasedSurfaceModel)) + return IFCFaceBasedSurfaceModel.ProcessIFCFaceBasedSurfaceModel(ifcRepresentationItem); + if (IFCAnyHandleUtil.IsValidSubTypeOf(ifcRepresentationItem, IFCEntityType.IfcShellBasedSurfaceModel)) + return IFCShellBasedSurfaceModel.ProcessIFCShellBasedSurfaceModel(ifcRepresentationItem); + if (IFCAnyHandleUtil.IsValidSubTypeOf(ifcRepresentationItem, IFCEntityType.IfcSolidModel)) + return IFCSolidModel.ProcessIFCSolidModel(ifcRepresentationItem); + if (IFCAnyHandleUtil.IsValidSubTypeOf(ifcRepresentationItem, IFCEntityType.IfcCsgPrimitive3D)) + return IFCCsgPrimitive3D.ProcessIFCCsgPrimitive3D(ifcRepresentationItem); + + // TODO: Move the items below to IFCGeometricRepresentationItem->IFCTessellatedItem->IfcTessellatedFaceSet. + if (IFCImportFile.TheFile.SchemaVersionAtLeast(IFCSchemaVersion.IFC4Obsolete) && IFCAnyHandleUtil.IsSubTypeOf(ifcRepresentationItem, IFCEntityType.IfcTriangulatedFaceSet)) + return IFCTriangulatedFaceSet.ProcessIFCTriangulatedFaceSet(ifcRepresentationItem); + // There is no way to actually determine an IFC4Add2 file vs. a "vanilla" IFC4 file, which is + // obsolete. The try/catch here allows us to read these obsolete files without crashing. + try { - if (IFCAnyHandleUtil.IsSubTypeOf(ifcRepresentationItem, IFCEntityType.IfcTopologicalRepresentationItem)) - return IFCTopologicalRepresentationItem.ProcessIFCTopologicalRepresentationItem(ifcRepresentationItem); - - // TODO: Move everything below to IFCGeometricRepresentationItem, once it is created. - if (IFCAnyHandleUtil.IsValidSubTypeOf(ifcRepresentationItem, IFCEntityType.IfcFaceBasedSurfaceModel)) - return IFCFaceBasedSurfaceModel.ProcessIFCFaceBasedSurfaceModel(ifcRepresentationItem); - if (IFCAnyHandleUtil.IsValidSubTypeOf(ifcRepresentationItem, IFCEntityType.IfcShellBasedSurfaceModel)) - return IFCShellBasedSurfaceModel.ProcessIFCShellBasedSurfaceModel(ifcRepresentationItem); - if (IFCAnyHandleUtil.IsValidSubTypeOf(ifcRepresentationItem, IFCEntityType.IfcSolidModel)) - return IFCSolidModel.ProcessIFCSolidModel(ifcRepresentationItem); - if (IFCAnyHandleUtil.IsValidSubTypeOf(ifcRepresentationItem, IFCEntityType.IfcCsgPrimitive3D)) - return IFCCsgPrimitive3D.ProcessIFCCsgPrimitive3D(ifcRepresentationItem); - - // TODO: Move the items below to IFCGeometricRepresentationItem->IFCTessellatedItem->IfcTessellatedFaceSet. - if (IFCImportFile.TheFile.SchemaVersionAtLeast(IFCSchemaVersion.IFC4Obsolete) && IFCAnyHandleUtil.IsSubTypeOf(ifcRepresentationItem, IFCEntityType.IfcTriangulatedFaceSet)) - return IFCTriangulatedFaceSet.ProcessIFCTriangulatedFaceSet(ifcRepresentationItem); - // There is no way to actually determine an IFC4Add2 file vs. a "vanilla" IFC4 file, which is - // obsolete. The try/catch here allows us to read these obsolete files without crashing. - try - { - if (IFCImportFile.TheFile.SchemaVersionAtLeast(IFCSchemaVersion.IFC4) && IFCAnyHandleUtil.IsSubTypeOf(ifcRepresentationItem, IFCEntityType.IfcPolygonalFaceSet)) - return IFCPolygonalFaceSet.ProcessIFCPolygonalFaceSet(ifcRepresentationItem); - } - catch (Exception ex) - { - // Once we fail once, downgrade the schema so we don't try again. - if (IFCImportFile.HasUndefinedAttribute(ex)) - IFCImportFile.TheFile.DowngradeIFC4SchemaTo(IFCSchemaVersion.IFC4Add1Obsolete); - else - throw ex; - } - - if (IFCAnyHandleUtil.IsSubTypeOf(ifcRepresentationItem, IFCEntityType.IfcSurface)) - return IFCSurface.ProcessIFCSurface(ifcRepresentationItem); + if (IFCImportFile.TheFile.SchemaVersionAtLeast(IFCSchemaVersion.IFC4) && IFCAnyHandleUtil.IsSubTypeOf(ifcRepresentationItem, IFCEntityType.IfcPolygonalFaceSet)) + return IFCPolygonalFaceSet.ProcessIFCPolygonalFaceSet(ifcRepresentationItem); + } + catch (Exception ex) + { + // Once we fail once, downgrade the schema so we don't try again. + if (IFCImportFile.HasUndefinedAttribute(ex)) + IFCImportFile.TheFile.DowngradeIFC4SchemaTo(IFCSchemaVersion.IFC4Add1Obsolete); + else + throw; } - // Need to process IFCBooleanResult because the individual operands should become IFCHybridRepresentationItem. + if (IFCAnyHandleUtil.IsSubTypeOf(ifcRepresentationItem, IFCEntityType.IfcSurface)) + return IFCSurface.ProcessIFCSurface(ifcRepresentationItem); + if (IFCAnyHandleUtil.IsValidSubTypeOf(ifcRepresentationItem, IFCEntityType.IfcBooleanResult)) return IFCBooleanResult.ProcessIFCBooleanResult(ifcRepresentationItem); - // Handle IFCStyledItem since IFCHybridRepresentationItem may need to add Materials on import. if (IFCImportFile.TheFile.SchemaVersionAtLeast(IFCSchemaVersion.IFC2x2) && IFCAnyHandleUtil.IsSubTypeOf(ifcRepresentationItem, IFCEntityType.IfcStyledItem)) return IFCStyledItem.ProcessIFCStyledItem(ifcRepresentationItem); if (IFCAnyHandleUtil.IsValidSubTypeOf(ifcRepresentationItem, IFCEntityType.IfcMappedItem)) return IFCMappedItem.ProcessIFCMappedItem(ifcRepresentationItem); + + if (IFCAnyHandleUtil.IsValidSubTypeOf(ifcRepresentationItem, IFCEntityType.IfcGeometricSet)) + return IFCGeometricSet.ProcessIFCGeometricSet(ifcRepresentationItem); + if (IFCAnyHandleUtil.IsValidSubTypeOf(ifcRepresentationItem, IFCEntityType.IfcCurve)) return IFCCurve.ProcessIFCCurve(ifcRepresentationItem); + if (IFCAnyHandleUtil.IsValidSubTypeOf(ifcRepresentationItem, IFCEntityType.IfcPoint)) return IFCPoint.ProcessIFCPoint(ifcRepresentationItem); - if (IFCAnyHandleUtil.IsValidSubTypeOf(ifcRepresentationItem, IFCEntityType.IfcGeometricSet)) - return IFCGeometricSet.ProcessIFCGeometricSet(ifcRepresentationItem); - - if (skipBodyGeometry) - { - return IFCHybridRepresentationItem.ProcessIFCHybridRepresentationItem(ifcRepresentationItem); - } // Everything else is an error. Importer.TheLog.LogUnhandledSubTypeError(ifcRepresentationItem, IFCEntityType.IfcRepresentationItem, false); diff --git a/Source/Revit.IFC.Import/Data/IFCRepresentationMap.cs b/Source/Revit.IFC.Import/Data/IFCRepresentationMap.cs index dfcc3838..e2bd8b7f 100644 --- a/Source/Revit.IFC.Import/Data/IFCRepresentationMap.cs +++ b/Source/Revit.IFC.Import/Data/IFCRepresentationMap.cs @@ -17,15 +17,11 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // -using System; using System.Collections.Generic; -using System.Linq; -using System.Text; using Autodesk.Revit.DB; using Autodesk.Revit.DB.IFC; using Revit.IFC.Common.Enums; using Revit.IFC.Common.Utility; -using Revit.IFC.Import.Enums; using Revit.IFC.Import.Geometry; using Revit.IFC.Import.Utility; @@ -93,15 +89,6 @@ protected IFCRepresentationMap(IFCAnyHandle representationMap) Process(representationMap); } - /// - /// Determine if the IFCRepresentationMap only has at least 1 IFCHybridInformation. - /// - /// True if the IFCRepresentationMap only has at least 1 IFCHybridInformation. - public bool IsHybridOnly() - { - return MappedRepresentation?.IsHybridOnly() ?? false; - } - /// /// Create geometry for a particular representation map. /// @@ -144,10 +131,23 @@ public void CreateShape(IFCImportShapeEditScope shapeEditScope, Transform scaled if ((numExistingSolids != numNewSolids) || (numExistingCurves != numNewCurves)) { - IList mappedSolids = new List(); + List mappedSolids = new List(); for (int ii = numExistingSolids; ii < numNewSolids; ii++) { - mappedSolids.Add(shapeEditScope.Creator.Solids[numExistingSolids].GeometryObject); + GeometryObject originalObject = shapeEditScope.Creator.Solids[numExistingSolids].GeometryObject; + if (originalObject != null) + { + // We only check curves, not solids, here, so we don't pass in a DirectShape. + IList processedList = IFCGeometryUtil.AdjustGeometryObjectsIfNeeded(originalObject, null, Id); + if (processedList != null) + { + mappedSolids.AddRange(processedList); + } + else + { + mappedSolids.Add(originalObject); + } + } shapeEditScope.Creator.Solids.RemoveAt(numExistingSolids); } diff --git a/Source/Revit.IFC.Import/Data/IFCSite.cs b/Source/Revit.IFC.Import/Data/IFCSite.cs index 65c3bbf6..738ba404 100644 --- a/Source/Revit.IFC.Import/Data/IFCSite.cs +++ b/Source/Revit.IFC.Import/Data/IFCSite.cs @@ -387,11 +387,19 @@ public static void ProcessSiteLocations(Document doc, IList sites) } // Register the offset by moving the Shared Coordinates away - ProjectPosition pPos = projectLocation.GetProjectPosition(XYZ.Zero); - pPos.EastWest += BaseSiteOffset.X; - pPos.NorthSouth += BaseSiteOffset.Y; - pPos.Elevation += BaseSiteOffset.Z; - projectLocation.SetProjectPosition(XYZ.Zero, pPos); + if (Importer.TheOptions.IsHybridImport) + { + // If we are in hybrid mode, we've already moved the project. Just change the large offset. + Importer.TheHybridInfo.LargeCoordinateOriginOffset += BaseSiteOffset; + } + else + { + ProjectPosition pPos = projectLocation.GetProjectPosition(XYZ.Zero); + pPos.EastWest += BaseSiteOffset.X; + pPos.NorthSouth += BaseSiteOffset.Y; + pPos.Elevation += BaseSiteOffset.Z; + projectLocation.SetProjectPosition(XYZ.Zero, pPos); + } } else { diff --git a/Source/Revit.IFC.Import/Data/IFCSpatialElement.cs b/Source/Revit.IFC.Import/Data/IFCSpatialElement.cs new file mode 100644 index 00000000..5ce4c846 --- /dev/null +++ b/Source/Revit.IFC.Import/Data/IFCSpatialElement.cs @@ -0,0 +1,86 @@ +// +// Revit IFC Import library: this library works with Autodesk(R) Revit(R) to import IFC files. +// Copyright (C) 2013 Autodesk, Inc. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// + +using Autodesk.Revit.DB.IFC; +using Revit.IFC.Common.Enums; +using Revit.IFC.Common.Utility; +using Revit.IFC.Import.Enums; + +namespace Revit.IFC.Import.Data +{ + /// + /// Represents an IFCSpatialElement. + /// + public class IFCSpatialElement : IFCProduct + { + /// + /// Constructs an IFCSpatialElement from the IfcSpatialElement handle. + /// + /// The IfcSpatialElement handle. + protected IFCSpatialElement(IFCAnyHandle ifcSpatialElement) + { + Process(ifcSpatialElement); + } + + /// + /// Default constructor. + /// + protected IFCSpatialElement() + { + + } + + /// + /// Processes IfcSpatialElement. + /// + /// The IfcSpatialElement handle. + protected override void Process(IFCAnyHandle ifcSpatialElement) + { + base.Process(ifcSpatialElement); + } + + /// + /// Processes IfcSpatialElement handle. + /// + /// The IfcSpatialElement handle. + /// The IFCSpatialElement object. + public static IFCSpatialElement ProcessIFCSpatialElement(IFCAnyHandle ifcSpatialElement) + { + if (IFCAnyHandleUtil.IsNullOrHasNoValue(ifcSpatialElement)) + { + Importer.TheLog.LogNullError(IFCEntityType.IfcSpatialElement); + return null; + } + + IFCEntity spatialElement; + if (IFCImportFile.TheFile.EntityMap.TryGetValue(ifcSpatialElement.StepId, out spatialElement)) + return (spatialElement as IFCSpatialElement); + + if (IFCAnyHandleUtil.IsSubTypeOf(ifcSpatialElement, IFCEntityType.IfcSpatialZone)) + return IFCSpatialZone.ProcessIFCSpatialZone(ifcSpatialElement); + else if (IFCAnyHandleUtil.IsSubTypeOf(ifcSpatialElement, IFCEntityType.IfcSpatialStructureElement)) + return IFCSpatialStructureElement.ProcessIFCSpatialStructureElement(ifcSpatialElement); + + Importer.TheLog.LogUnhandledSubTypeError(ifcSpatialElement, IFCEntityType.IfcProduct, false); + return null; + } + } +} + + diff --git a/Source/Revit.IFC.Import/Data/IFCSpatialStructureElement.cs b/Source/Revit.IFC.Import/Data/IFCSpatialStructureElement.cs index 5076e455..22bc9989 100644 --- a/Source/Revit.IFC.Import/Data/IFCSpatialStructureElement.cs +++ b/Source/Revit.IFC.Import/Data/IFCSpatialStructureElement.cs @@ -33,7 +33,7 @@ namespace Revit.IFC.Import.Data /// /// Represents an IfcSpatialStructureElement. /// - public class IFCSpatialStructureElement : IFCProduct + public class IFCSpatialStructureElement : IFCSpatialElement { HashSet m_IFCProducts = null; @@ -222,6 +222,12 @@ void ProcessIFCRelServicesBuildings(IFCAnyHandle ifcRelHandle) protected void TryToFixFarawayOrigin() { + // If we are using ATF, it should fix coordinate issues for us. + if (Importer.TheOptions.IsHybridImport) + { + return; + } + if (!(ProjectScope?.IsSet ?? false)) return; diff --git a/Source/Revit.IFC.Import/Data/IFCSpatialZone.cs b/Source/Revit.IFC.Import/Data/IFCSpatialZone.cs new file mode 100644 index 00000000..dd648543 --- /dev/null +++ b/Source/Revit.IFC.Import/Data/IFCSpatialZone.cs @@ -0,0 +1,79 @@ +// +// Revit IFC Import library: this library works with Autodesk(R) Revit(R) to import IFC files. +// Copyright (C) 2013 Autodesk, Inc. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// + +using Autodesk.Revit.DB.IFC; +using Revit.IFC.Common.Enums; +using Revit.IFC.Common.Utility; + +namespace Revit.IFC.Import.Data +{ + /// + /// Represents an IFCSpatialZone. + /// + public class IFCSpatialZone : IFCSpatialElement + { + /// + /// Constructs an IFCSpatialZone from the IfcSpatialZone handle. + /// + /// The IfcSpatialZone handle. + protected IFCSpatialZone(IFCAnyHandle ifcSpatialZone) + { + Process(ifcSpatialZone); + } + + /// + /// Default constructor. + /// + protected IFCSpatialZone() + { + + } + + /// + /// Processes IfcSpatialZone. + /// + /// The IfcSpatialZone handle. + protected override void Process(IFCAnyHandle ifcSpatialZone) + { + base.Process(ifcSpatialZone); + } + + /// + /// Processes IfcSpatialZone handle. + /// + /// The IfcSpatialZone handle. + /// The IFCSpatialZone object. + public static IFCSpatialZone ProcessIFCSpatialZone(IFCAnyHandle ifcSpatialZone) + { + if (IFCAnyHandleUtil.IsNullOrHasNoValue(ifcSpatialZone)) + { + Importer.TheLog.LogNullError(IFCEntityType.IfcSpatialZone); + return null; + } + + IFCEntity spatialZone; + if (IFCImportFile.TheFile.EntityMap.TryGetValue(ifcSpatialZone.StepId, out spatialZone)) + return (spatialZone as IFCSpatialZone); + + return new IFCSpatialZone(ifcSpatialZone); + } + } +} + + diff --git a/Source/Revit.IFC.Import/Data/IFCStyledItem.cs b/Source/Revit.IFC.Import/Data/IFCStyledItem.cs index 3098fcbf..daadd2ba 100644 --- a/Source/Revit.IFC.Import/Data/IFCStyledItem.cs +++ b/Source/Revit.IFC.Import/Data/IFCStyledItem.cs @@ -190,11 +190,6 @@ public IFCSurfaceStyle GetSurfaceStyle() /// The document. public void Create(IFCImportShapeEditScope shapeEditScope) { - // If we have an IFCHybridRepresentationItem, there's no reason to create the styled item, because - // we won't use it. - if (Item is IFCHybridRepresentationItem) - return; - // TODO: support cut pattern id and cut pattern color. if (m_CreatedElementId != ElementId.InvalidElementId || !IsValidForCreation) return; diff --git a/Source/Revit.IFC.Import/Data/IFCSweptDiskSolid.cs b/Source/Revit.IFC.Import/Data/IFCSweptDiskSolid.cs index 3f10fdf7..6110ce44 100644 --- a/Source/Revit.IFC.Import/Data/IFCSweptDiskSolid.cs +++ b/Source/Revit.IFC.Import/Data/IFCSweptDiskSolid.cs @@ -290,7 +290,7 @@ private IList CreateProfileCurveLoopsForDirectrix(Curve directrix, ou } else - throw ex; + throw; } } diff --git a/Source/Revit.IFC.Import/Data/IFCTriangulatedFaceSet.cs b/Source/Revit.IFC.Import/Data/IFCTriangulatedFaceSet.cs index 5454496e..81025a0d 100644 --- a/Source/Revit.IFC.Import/Data/IFCTriangulatedFaceSet.cs +++ b/Source/Revit.IFC.Import/Data/IFCTriangulatedFaceSet.cs @@ -129,7 +129,7 @@ protected override void Process(IFCAnyHandle ifcTriangulatedFaceSet) if (IFCImportFile.HasUndefinedAttribute(ex)) IFCImportFile.TheFile.DowngradeIFC4SchemaTo(IFCSchemaVersion.IFC4Add1Obsolete); else - throw ex; + throw; } } diff --git a/Source/Revit.IFC.Import/Data/IFCTypeObject.cs b/Source/Revit.IFC.Import/Data/IFCTypeObject.cs index 5eca2c71..bd6589c0 100644 --- a/Source/Revit.IFC.Import/Data/IFCTypeObject.cs +++ b/Source/Revit.IFC.Import/Data/IFCTypeObject.cs @@ -194,8 +194,8 @@ protected override void Create(Document doc) { // If we're "Creating" an Element from an IFCTypeObject during Hybrid IFC Import, then that will already have been imported as a DirectShapeType // So use that instead of creating a whole new DirectShapeType. - ElementId directShapeTypeId = ElementId.InvalidElementId; - if (Importer.TheOptions.IsHybridImport && (Importer.TheHybridInfo?.HybridMap?.TryGetValue(GlobalId, out directShapeTypeId) ?? false)) + ElementId directShapeTypeId = IFCImportHybridInfo.GetHybridMapInformation(Id); + if (IFCImportHybridInfo.IsValidElementId(directShapeTypeId)) { CreatedElementId = directShapeTypeId; } diff --git a/Source/Revit.IFC.Import/Data/IFCTypeProduct.cs b/Source/Revit.IFC.Import/Data/IFCTypeProduct.cs index 47c37001..71fb1660 100644 --- a/Source/Revit.IFC.Import/Data/IFCTypeProduct.cs +++ b/Source/Revit.IFC.Import/Data/IFCTypeProduct.cs @@ -21,6 +21,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using Autodesk.Revit.DB; using Autodesk.Revit.DB.IFC; using Revit.IFC.Common.Enums; using Revit.IFC.Common.Utility; @@ -116,24 +117,24 @@ protected override void Process(IFCAnyHandle ifcTypeProduct) Tag = IFCAnyHandleUtil.GetStringAttribute(ifcTypeProduct, "Tag"); - using (RepresentationsAlreadyCreatedSetter setter = new RepresentationsAlreadyCreatedSetter(GlobalId)) + if (IFCImportHybridInfo.IsValidElementId(IFCImportHybridInfo.GetHybridMapInformation(Id))) + return; + + IList representationMapsHandle = IFCAnyHandleUtil.GetAggregateInstanceAttribute>(ifcTypeProduct, "RepresentationMaps"); + if (representationMapsHandle?.Count > 0) { - IList representationMapsHandle = IFCAnyHandleUtil.GetAggregateInstanceAttribute>(ifcTypeProduct, "RepresentationMaps"); - if (representationMapsHandle?.Count > 0) + foreach (IFCAnyHandle representationMapHandle in representationMapsHandle) { - foreach (IFCAnyHandle representationMapHandle in representationMapsHandle) + IFCRepresentationMap representationMap = IFCRepresentationMap.ProcessIFCRepresentationMap(representationMapHandle); + if (representationMap != null) { - IFCRepresentationMap representationMap = IFCRepresentationMap.ProcessIFCRepresentationMap(representationMapHandle); - if (representationMap != null) - { - RepresentationMaps.Add(representationMap); - - // Traditionally we would create a "dummy" DirectShapeType for each IfcRepresentationMap. In the case where the IfcRepresentationMap is not used by another other IfcTypeProduct, - // we would like to stop creating the "dummy" DirectShapeType and store the geometry in the DirectShapeType associated with the IfcTypeProduct. However, IfcRepresentationMap - // does not have an INVERSE relationship to its IfcTypeProduct(s), at least in IFC2x3. - // As such, we keep track of the IfcRepresentationMaps that have the relationship described above for future correspondence. - RegisterRepresentationMapWithTypeProject(representationMap, this); - } + RepresentationMaps.Add(representationMap); + + // Traditionally we would create a "dummy" DirectShapeType for each IfcRepresentationMap. In the case where the IfcRepresentationMap is not used by another other IfcTypeProduct, + // we would like to stop creating the "dummy" DirectShapeType and store the geometry in the DirectShapeType associated with the IfcTypeProduct. However, IfcRepresentationMap + // does not have an INVERSE relationship to its IfcTypeProduct(s), at least in IFC2x3. + // As such, we keep track of the IfcRepresentationMaps that have the relationship described above for future correspondence. + RegisterRepresentationMapWithTypeProject(representationMap, this); } } } diff --git a/Source/Revit.IFC.Import/Data/IFCUnit.cs b/Source/Revit.IFC.Import/Data/IFCUnit.cs index 2b082ef9..6058f82f 100644 --- a/Source/Revit.IFC.Import/Data/IFCUnit.cs +++ b/Source/Revit.IFC.Import/Data/IFCUnit.cs @@ -958,7 +958,63 @@ void ProcessIFCDerivedUnit(IFCAnyHandle unitHnd) string userDefinedType = IFCImportHandleUtil.GetOptionalStringAttribute(unitHnd, "UserDefinedType", null); if (!string.IsNullOrWhiteSpace(userDefinedType)) { - if (string.Compare(userDefinedType, "Luminous Efficacy", true) == 0) + if (string.Compare(userDefinedType, "Color Temperature", true) == 0) + { + Spec = SpecTypeId.ColorTemperature; + UnitSystem = UnitSystem.Metric; + + // Support only K. + DerivedUnitExpectedTypes expectedTypes = new DerivedUnitExpectedTypes(UnitTypeId.Kelvin, SymbolTypeId.Kelvin); + expectedTypes.AddExpectedType(1, SpecTypeId.HvacTemperature); + expectedTypesList.Add(expectedTypes); + } + else if (string.Compare(userDefinedType, "Cost Per Area", true) == 0) + { + Spec = SpecTypeId.CostPerArea; + UnitSystem = UnitSystem.Metric; + + // Support only $/m2. + DerivedUnitExpectedTypes expectedTypes = new DerivedUnitExpectedTypes(UnitTypeId.CurrencyPerSquareMeter, SymbolTypeId.DollarPerMSup2); + expectedTypes.AddExpectedType(-2, SpecTypeId.Length); + expectedTypesList.Add(expectedTypes); + } + else if (string.Compare(userDefinedType, "Apparent Power Density", true) == 0) + { + Spec = SpecTypeId.ApparentPowerDensity; + UnitSystem = UnitSystem.Metric; + + // Support only VA / m2 = kg * s−3. + DerivedUnitExpectedTypes expectedTypes = new DerivedUnitExpectedTypes(UnitTypeId.VoltAmperesPerSquareMeter, SymbolTypeId.VAPerMSup2); + expectedTypes.AddExpectedType(1, SpecTypeId.Mass); + expectedTypes.AddCustomExpectedType(-3, "TIMEUNIT"); + expectedTypesList.Add(expectedTypes); + } + else if (string.Compare(userDefinedType, "Cost Rate Energy", true) == 0) + { + Spec = SpecTypeId.CostRateEnergy; + UnitSystem = UnitSystem.Metric; + + // Support only $ / (W * h) = kg-1 * m-2 * s2 / 3600. + DerivedUnitExpectedTypes expectedTypes = new DerivedUnitExpectedTypes(UnitTypeId.CurrencyPerWattHour, SymbolTypeId.DollarPerWH); + expectedTypes.AddExpectedType(-1, SpecTypeId.Mass); + expectedTypes.AddExpectedType(-2, SpecTypeId.Length); + expectedTypes.AddCustomExpectedType(2, "TIMEUNIT"); + // TODO + expectedTypesList.Add(expectedTypes); + } + else if (string.Compare(userDefinedType, "Cost Rate Power", true) == 0) + { + Spec = SpecTypeId.CostRatePower; + UnitSystem = UnitSystem.Metric; + + // Support only $ / W = kg-1 * m-2 * s3. + DerivedUnitExpectedTypes expectedTypes = new DerivedUnitExpectedTypes(UnitTypeId.CurrencyPerWatt, SymbolTypeId.DollarPerW); + expectedTypes.AddExpectedType(-1, SpecTypeId.Mass); + expectedTypes.AddExpectedType(-2, SpecTypeId.Length); + expectedTypes.AddCustomExpectedType(3, "TIMEUNIT"); + expectedTypesList.Add(expectedTypes); + } + else if (string.Compare(userDefinedType, "Luminous Efficacy", true) == 0) { Spec = SpecTypeId.Efficacy; UnitSystem = UnitSystem.Metric; @@ -971,16 +1027,38 @@ void ProcessIFCDerivedUnit(IFCAnyHandle unitHnd) expectedTypes.AddExpectedType(1, SpecTypeId.LuminousFlux); expectedTypesList.Add(expectedTypes); } - else if (string.Compare(userDefinedType, "Friction Loss", true) == 0) + else if (string.Compare(userDefinedType, "Luminance", true) == 0) { - Spec = SpecTypeId.HvacFriction; + Spec = SpecTypeId.Luminance; UnitSystem = UnitSystem.Metric; - // Support only Pa / m. - DerivedUnitExpectedTypes expectedTypes = new DerivedUnitExpectedTypes(UnitTypeId.PascalsPerMeter, SymbolTypeId.PaPerM); + // Support only cd / m2. + DerivedUnitExpectedTypes expectedTypes = new DerivedUnitExpectedTypes(UnitTypeId.CandelasPerSquareMeter, SymbolTypeId.CdPerMSup2); + expectedTypes.AddExpectedType(1, SpecTypeId.LuminousIntensity); // TODO check expectedTypes.AddExpectedType(-2, SpecTypeId.Length); + expectedTypesList.Add(expectedTypes); + } + else if (string.Compare(userDefinedType, "Electrical Power Density", true) == 0) + { + Spec = SpecTypeId.ElectricalPowerDensity; + UnitSystem = UnitSystem.Metric; + + // Support W / m2 = kg * s-3 only + DerivedUnitExpectedTypes expectedTypes = new DerivedUnitExpectedTypes(UnitTypeId.WattsPerSquareMeter, SymbolTypeId.WPerMSup2); expectedTypes.AddExpectedType(1, SpecTypeId.Mass); - expectedTypes.AddCustomExpectedType(-2, "TIMEUNIT"); + expectedTypes.AddCustomExpectedType(-3, "TIMEUNIT"); + expectedTypesList.Add(expectedTypes); + } + else if (string.Compare(userDefinedType, "Power Per Length", true) == 0) + { + Spec = SpecTypeId.PowerPerLength; + UnitSystem = UnitSystem.Metric; + + // Support only W / m = kg * m * s-3. + DerivedUnitExpectedTypes expectedTypes = new DerivedUnitExpectedTypes(UnitTypeId.WattsPerMeter, SymbolTypeId.WPerM); + expectedTypes.AddExpectedType(1, SpecTypeId.Mass); + expectedTypes.AddExpectedType(1, SpecTypeId.Length); + expectedTypes.AddCustomExpectedType(-3, "TIMEUNIT"); expectedTypesList.Add(expectedTypes); } else if (string.Compare(userDefinedType, "Electrical Resistivity", true) == 0) @@ -996,6 +1074,280 @@ void ProcessIFCDerivedUnit(IFCAnyHandle unitHnd) expectedTypes.AddExpectedType(-2, SpecTypeId.Current); expectedTypesList.Add(expectedTypes); } + else if (string.Compare(userDefinedType, "Heat Capacity Per Area", true) == 0) + { + Spec = SpecTypeId.HeatCapacityPerArea; + UnitSystem = UnitSystem.Metric; + + // Support only J / (m2 * K) = kg * s-2 * K-1. + DerivedUnitExpectedTypes expectedTypes = new DerivedUnitExpectedTypes(UnitTypeId.JoulesPerSquareMeterKelvin, SymbolTypeId.JPerMSup2K); + expectedTypes.AddExpectedType(1, SpecTypeId.Mass); + expectedTypes.AddCustomExpectedType(-2, "TIMEUNIT"); + expectedTypes.AddExpectedType(-1, SpecTypeId.HvacTemperature); + expectedTypesList.Add(expectedTypes); + } + else if (string.Compare(userDefinedType, "Thermal Gradient Coefficient For Moisture Capacity", true) == 0) + { + Spec = SpecTypeId.ThermalGradientCoefficientForMoistureCapacity; + UnitSystem = UnitSystem.Metric; + + // Support only kg / (kg * K) = K-1. + DerivedUnitExpectedTypes expectedTypes = new DerivedUnitExpectedTypes(UnitTypeId.KilogramsPerKilogramKelvin, SymbolTypeId.KgPerKgK); + expectedTypes.AddExpectedType(-1, SpecTypeId.HvacTemperature); + expectedTypesList.Add(expectedTypes); + } + else if (string.Compare(userDefinedType, "Thermal Mass", true) == 0) + { + Spec = SpecTypeId.ThermalMass; + UnitSystem = UnitSystem.Metric; + + // Support only J / K = kg * m2 * s-2 * K-1. + DerivedUnitExpectedTypes expectedTypes = new DerivedUnitExpectedTypes(UnitTypeId.JoulesPerKelvin, SymbolTypeId.JPerK); + expectedTypes.AddExpectedType(1, SpecTypeId.Mass); + expectedTypes.AddExpectedType(2, SpecTypeId.Length); + expectedTypes.AddCustomExpectedType(-2, "TIMEUNIT"); + expectedTypes.AddExpectedType(-1, SpecTypeId.HvacTemperature); + expectedTypesList.Add(expectedTypes); + } + else if (string.Compare(userDefinedType, "Air Flow Density", true) == 0) + { + Spec = SpecTypeId.AirFlowDensity; + UnitSystem = UnitSystem.Metric; + + // Support only m3 / (h * m2) = m * s-1 / 3600. + DerivedUnitExpectedTypes expectedTypes = new DerivedUnitExpectedTypes(UnitTypeId.CubicMetersPerHourSquareMeter, SymbolTypeId.MSup3PerHMSup2); + expectedTypes.AddExpectedType(1, SpecTypeId.Length); + expectedTypes.AddCustomExpectedType(-1, "TIMEUNIT"); + // TODO + expectedTypesList.Add(expectedTypes); + } + else if (string.Compare(userDefinedType, "Air Flow Divided By Cooling Load", true) == 0) + { + Spec = SpecTypeId.AirFlowDividedByCoolingLoad; + UnitSystem = UnitSystem.Metric; + + // Support only L / (s * kW) = kg–1 * m * s2 * 10–6. + DerivedUnitExpectedTypes expectedTypes = new DerivedUnitExpectedTypes(UnitTypeId.LitersPerSecondKilowatt, SymbolTypeId.LPerSKw); + expectedTypes.AddExpectedType(-1, SpecTypeId.Mass); + expectedTypes.AddExpectedType(1, SpecTypeId.Length); + expectedTypes.AddCustomExpectedType(2, "TIMEUNIT"); + // TODO + expectedTypesList.Add(expectedTypes); + } + else if (string.Compare(userDefinedType, "Air Flow Divided By Volume", true) == 0) + { + Spec = SpecTypeId.AirFlowDividedByVolume; + UnitSystem = UnitSystem.Metric; + + // Support only m3 / (h*m3) = s-1 / 3600. + DerivedUnitExpectedTypes expectedTypes = new DerivedUnitExpectedTypes(UnitTypeId.CubicMetersPerHourCubicMeter, SymbolTypeId.MSup3PerHMSup3); + expectedTypes.AddCustomExpectedType(-1, "TIMEUNIT"); + // TODO + expectedTypesList.Add(expectedTypes); + } + else if (string.Compare(userDefinedType, "Area Divided By Cooling Load", true) == 0) + { + Spec = SpecTypeId.AreaDividedByCoolingLoad; + UnitSystem = UnitSystem.Metric; + + // Support only m2 / kW = s3 * kg-1 * 10-3. + DerivedUnitExpectedTypes expectedTypes = new DerivedUnitExpectedTypes(UnitTypeId.SquareMetersPerKilowatt, SymbolTypeId.MSup2PerKw); + expectedTypes.AddExpectedType(-1, SpecTypeId.Mass); + expectedTypes.AddCustomExpectedType(3, "TIMEUNIT"); + // TODO + expectedTypesList.Add(expectedTypes); + } + else if (string.Compare(userDefinedType, "Area Divided By Heating Load", true) == 0) + { + Spec = SpecTypeId.AreaDividedByHeatingLoad; + UnitSystem = UnitSystem.Metric; + + // Support only m2 / kW = s3 * kg-1 * 10-3. + DerivedUnitExpectedTypes expectedTypes = new DerivedUnitExpectedTypes(UnitTypeId.SquareMetersPerKilowatt, SymbolTypeId.MSup2PerKw); + expectedTypes.AddExpectedType(-1, SpecTypeId.Mass); + expectedTypes.AddCustomExpectedType(3, "TIMEUNIT"); + expectedTypesList.Add(expectedTypes); + } + else if (string.Compare(userDefinedType, "Cooling Load Divided By Area", true) == 0) + { + Spec = SpecTypeId.CoolingLoadDividedByArea; + UnitSystem = UnitSystem.Metric; + + // Support only W / m2 = kg * s−3. + DerivedUnitExpectedTypes expectedTypes = new DerivedUnitExpectedTypes(UnitTypeId.WattsPerSquareMeter, SymbolTypeId.WPerMSup2); + expectedTypes.AddExpectedType(1, SpecTypeId.Mass); + expectedTypes.AddCustomExpectedType(-3, "TIMEUNIT"); + expectedTypesList.Add(expectedTypes); + } + else if (string.Compare(userDefinedType, "Cooling Load Divided By Volume", true) == 0) + { + Spec = SpecTypeId.CoolingLoadDividedByVolume; + UnitSystem = UnitSystem.Metric; + + // Support only W / m3 = kg * m-1 * s-3. + DerivedUnitExpectedTypes expectedTypes = new DerivedUnitExpectedTypes(UnitTypeId.WattsPerCubicMeter, SymbolTypeId.WPerMSup3); + expectedTypes.AddExpectedType(1, SpecTypeId.Mass); + expectedTypes.AddExpectedType(-1, SpecTypeId.Length); + expectedTypes.AddCustomExpectedType(-3, "TIMEUNIT"); + expectedTypesList.Add(expectedTypes); + } + else if (string.Compare(userDefinedType, "Flow Per Power", true) == 0) + { + Spec = SpecTypeId.FlowPerPower; + UnitSystem = UnitSystem.Metric; + + // Support only m3 / (W * s) = kg-1 * m * s2. + DerivedUnitExpectedTypes expectedTypes = new DerivedUnitExpectedTypes(UnitTypeId.CubicMetersPerWattSecond, SymbolTypeId.MSup3PerWS); + expectedTypes.AddExpectedType(-1, SpecTypeId.Mass); + expectedTypes.AddExpectedType(1, SpecTypeId.Length); + expectedTypes.AddCustomExpectedType(2, "TIMEUNIT"); + expectedTypesList.Add(expectedTypes); + } + else if (string.Compare(userDefinedType, "Friction Loss", true) == 0) + { + Spec = SpecTypeId.HvacFriction; + UnitSystem = UnitSystem.Metric; + + // Support only Pa / m = kg * m-2 * s-2. + DerivedUnitExpectedTypes expectedTypes = new DerivedUnitExpectedTypes(UnitTypeId.PascalsPerMeter, SymbolTypeId.PaPerM); + expectedTypes.AddExpectedType(1, SpecTypeId.Mass); + expectedTypes.AddExpectedType(-2, SpecTypeId.Length); + expectedTypes.AddCustomExpectedType(-2, "TIMEUNIT"); + expectedTypesList.Add(expectedTypes); + } + else if (string.Compare(userDefinedType, "Heating Load Divided By Area", true) == 0) + { + Spec = SpecTypeId.HeatingLoadDividedByArea; + UnitSystem = UnitSystem.Metric; + + // Support only W / m2 = kg * s−3. + DerivedUnitExpectedTypes expectedTypes = new DerivedUnitExpectedTypes(UnitTypeId.WattsPerSquareMeter, SymbolTypeId.WPerMSup2); + expectedTypes.AddExpectedType(1, SpecTypeId.Mass); + expectedTypes.AddCustomExpectedType(-3, "TIMEUNIT"); + expectedTypesList.Add(expectedTypes); + } + else if (string.Compare(userDefinedType, "Heating Load Divided By Volume", true) == 0) + { + Spec = SpecTypeId.HeatingLoadDividedByVolume; + UnitSystem = UnitSystem.Metric; + + // Support only W / m3 = kg * m-1 * s-3. + DerivedUnitExpectedTypes expectedTypes = new DerivedUnitExpectedTypes(UnitTypeId.WattsPerCubicMeter, SymbolTypeId.WPerMSup3); + expectedTypes.AddExpectedType(1, SpecTypeId.Mass); + expectedTypes.AddExpectedType(-1, SpecTypeId.Length); + expectedTypes.AddCustomExpectedType(-3, "TIMEUNIT"); + expectedTypesList.Add(expectedTypes); + } + else if (string.Compare(userDefinedType, "Power Per Flow", true) == 0) + { + Spec = SpecTypeId.PowerPerFlow; + UnitSystem = UnitSystem.Metric; + + // Support only (W * s) / m3 = kg * m-1 * s-2. + DerivedUnitExpectedTypes expectedTypes = new DerivedUnitExpectedTypes(UnitTypeId.WattsPerCubicMeterPerSecond, SymbolTypeId.WSPerMSup3); + expectedTypes.AddExpectedType(1, SpecTypeId.Mass); + expectedTypes.AddExpectedType(-1, SpecTypeId.Length); + expectedTypes.AddCustomExpectedType(-2, "TIMEUNIT"); + expectedTypesList.Add(expectedTypes); + } + else if (string.Compare(userDefinedType, "Piping Friction", true) == 0) + { + Spec = SpecTypeId.PipingFriction; + UnitSystem = UnitSystem.Metric; + + // Support only Pa / m = kg * m-2 * s-2. + DerivedUnitExpectedTypes expectedTypes = new DerivedUnitExpectedTypes(UnitTypeId.PascalsPerMeter, SymbolTypeId.PaPerM); + expectedTypes.AddExpectedType(1, SpecTypeId.Mass); + expectedTypes.AddExpectedType(-2, SpecTypeId.Length); + expectedTypes.AddCustomExpectedType(-2, "TIMEUNIT"); + expectedTypesList.Add(expectedTypes); + } + else if (string.Compare(userDefinedType, "Area Spring Coefficient", true) == 0) + { + Spec = SpecTypeId.AreaSpringCoefficient; + UnitSystem = UnitSystem.Metric; + + // Support only Pa / m = kg * m-2 * s-2. + DerivedUnitExpectedTypes expectedTypes = new DerivedUnitExpectedTypes(UnitTypeId.PascalsPerMeter, SymbolTypeId.PaPerM); + expectedTypes.AddExpectedType(1, SpecTypeId.Mass); + expectedTypes.AddExpectedType(-2, SpecTypeId.Length); + expectedTypes.AddCustomExpectedType(-2, "TIMEUNIT"); + expectedTypesList.Add(expectedTypes); + } + else if (string.Compare(userDefinedType, "Line Spring Coefficient", true) == 0) + { + Spec = SpecTypeId.LineSpringCoefficient; + UnitSystem = UnitSystem.Metric; + + // Support only Pa = kg * m-1 * s-2. + DerivedUnitExpectedTypes expectedTypes = new DerivedUnitExpectedTypes(UnitTypeId.Pascals, SymbolTypeId.Pa); + expectedTypes.AddExpectedType(1, SpecTypeId.Mass); + expectedTypes.AddExpectedType(-1, SpecTypeId.Length); + expectedTypes.AddCustomExpectedType(-2, "TIMEUNIT"); + expectedTypesList.Add(expectedTypes); + } + else if (string.Compare(userDefinedType, "Mass Per Unit Area", true) == 0) + { + Spec = SpecTypeId.MassPerUnitArea; + UnitSystem = UnitSystem.Metric; + + // Support only kg / m2 = kg * m-2. + DerivedUnitExpectedTypes expectedTypes = new DerivedUnitExpectedTypes(UnitTypeId.KilogramsPerSquareMeter, SymbolTypeId.KgPerMSup2); + expectedTypes.AddExpectedType(1, SpecTypeId.Mass); + expectedTypes.AddExpectedType(-2, SpecTypeId.Length); + expectedTypesList.Add(expectedTypes); + } + else if (string.Compare(userDefinedType, "Reinforcement Area Per Unit Length", true) == 0) + { + Spec = SpecTypeId.ReinforcementAreaPerUnitLength; + UnitSystem = UnitSystem.Metric; + + // Support only m2 / m = m. + DerivedUnitExpectedTypes expectedTypes = new DerivedUnitExpectedTypes(UnitTypeId.SquareMetersPerMeter, SymbolTypeId.MSup2PerM); + expectedTypes.AddExpectedType(1, SpecTypeId.Length); + expectedTypesList.Add(expectedTypes); + } + else if (string.Compare(userDefinedType, "Rotational Line Spring Coefficient", true) == 0) + { + Spec = SpecTypeId.RotationalLineSpringCoefficient; + UnitSystem = UnitSystem.Metric; + + // Support only kn-m / (deg/m) = 10+3 kg * m3 * s-2 * rad-1. + DerivedUnitExpectedTypes expectedTypes = new DerivedUnitExpectedTypes(UnitTypeId.KilonewtonMetersPerDegreePerMeter, SymbolTypeId.KNDashMPerDegreePerM); + expectedTypes.AddExpectedType(1, SpecTypeId.Mass); + expectedTypes.AddExpectedType(1, SpecTypeId.Length); + expectedTypes.AddCustomExpectedType(-2, "TIMEUNIT"); + expectedTypes.AddExpectedType(-1, SpecTypeId.Angle); + // TODO + expectedTypesList.Add(expectedTypes); + } + else if (string.Compare(userDefinedType, "Rotational Point Spring Coefficient", true) == 0) + { + Spec = SpecTypeId.RotationalPointSpringCoefficient; + UnitSystem = UnitSystem.Metric; + + // Support only kn-m / deg = 10+3 kg * m2 * s-2 * rad-1. + DerivedUnitExpectedTypes expectedTypes = new DerivedUnitExpectedTypes(UnitTypeId.KilonewtonMetersPerDegree, SymbolTypeId.KNDashMPerDegree); + expectedTypes.AddExpectedType(1, SpecTypeId.Mass); + expectedTypes.AddExpectedType(2, SpecTypeId.Length); + expectedTypes.AddCustomExpectedType(-2, "TIMEUNIT"); + expectedTypes.AddExpectedType(-1, SpecTypeId.Angle); + // TODO + expectedTypesList.Add(expectedTypes); + } + else if (string.Compare(userDefinedType, "Unit Weight", true) == 0) + { + Spec = SpecTypeId.UnitWeight; + UnitSystem = UnitSystem.Metric; + + // Support only kN / m3 = 10+3 kg * m-2 * s-2 * 10-3. + DerivedUnitExpectedTypes expectedTypes = new DerivedUnitExpectedTypes(UnitTypeId.KilonewtonsPerCubicMeter, SymbolTypeId.KNPerMSup3); + expectedTypes.AddExpectedType(1, SpecTypeId.Mass); + expectedTypes.AddExpectedType(-2, SpecTypeId.Length); + expectedTypes.AddCustomExpectedType(-2, "TIMEUNIT"); + // TODO + expectedTypesList.Add(expectedTypes); + } } } diff --git a/Source/Revit.IFC.Import/Data/IFCZone.cs b/Source/Revit.IFC.Import/Data/IFCZone.cs index 3b791bd2..e54d8656 100644 --- a/Source/Revit.IFC.Import/Data/IFCZone.cs +++ b/Source/Revit.IFC.Import/Data/IFCZone.cs @@ -85,7 +85,10 @@ public static IFCZone ProcessIFCZone(IFCAnyHandle ifcZone) /// True if the IFC entity geometry should be duplicated, False otherwise. public override bool ContainerFilteredEntity(IFCEntity entity) { - return (entity is IFCSpace); + if (entity == null) + return false; + + return (entity.EntityType == IFCEntityType.IfcSpace) || (entity.EntityType == IFCEntityType.IfcSpatialZone); } /// diff --git a/Source/Revit.IFC.Import/Enums/IFCSchemaVersion.cs b/Source/Revit.IFC.Import/Enums/IFCSchemaVersion.cs index da2206b0..e026dca2 100644 --- a/Source/Revit.IFC.Import/Enums/IFCSchemaVersion.cs +++ b/Source/Revit.IFC.Import/Enums/IFCSchemaVersion.cs @@ -44,6 +44,7 @@ public enum IFCSchemaVersion IFC4x2, IFC4x3_RC1, IFC4x3_RC4, - IFC4x3 + IFC4x3, + IFC4x3_ADD2 } } \ No newline at end of file diff --git a/Source/Revit.IFC.Import/Geometry/IFCGeometryUtil.cs b/Source/Revit.IFC.Import/Geometry/IFCGeometryUtil.cs index c540e65b..d81498d7 100644 --- a/Source/Revit.IFC.Import/Geometry/IFCGeometryUtil.cs +++ b/Source/Revit.IFC.Import/Geometry/IFCGeometryUtil.cs @@ -622,6 +622,49 @@ private static bool HasSuspiciousNumberOfCurveSegments(int id, IFCCurve ifcCurve return true; } + /// + /// Check that a geometric object is valid for a DirectShape, and adjust if needed. + /// + /// The original GeometryObject. + /// An optional DirectShape uesd to validate solids. + /// The id of the entity being processed. + /// Null if the original object is valid, or a list of GeometryObjects if not. + public static IList AdjustGeometryObjectsIfNeeded(GeometryObject originalObject, DirectShape shape, int id) + { + if (shape != null && originalObject is Solid) + { + Solid solid = originalObject as Solid; + if (!shape.IsValidGeometry(solid)) + { + Importer.TheLog.LogWarning(id, "Couldn't create valid solid, reverting to mesh.", false); + return CreateMeshesFromSolid(solid); + } + } + + Curve curve = originalObject as Curve; + if (!(curve?.IsBound ?? true)) + { + if (curve?.IsCyclic ?? false) + { + double period = curve.Period; + Curve newCurve = curve.Clone(); + + curve.MakeBound(0, period / 2); + newCurve.MakeBound(period / 2, period); + + return new List() { curve, newCurve }; + } + else + { + Importer.TheLog.LogWarning(id, "Found unbounded acyclic curve, ignoring.", false); + return new List(); + } + } + + return null; + } + + /// /// Given a list of curves, finds any unbound cyclic curves and splits them. /// @@ -947,7 +990,7 @@ public static Solid ExecuteSafeBooleanOperation(int id, int secondId, Solid firs // This is the only error that we are trying to catch and fix. // For any other error, we will re-throw. if (!msg.Contains("Failed to perform the Boolean operation for the two solids")) - throw ex; + throw; if (ii < numPasses - 1) continue; diff --git a/Source/Revit.IFC.Import/Importer.cs b/Source/Revit.IFC.Import/Importer.cs index ca366fef..0b503bc0 100644 --- a/Source/Revit.IFC.Import/Importer.cs +++ b/Source/Revit.IFC.Import/Importer.cs @@ -195,7 +195,7 @@ public static Importer CreateImporter(Document originalDocument, string ifcFileN Importer importer = new Importer(); TheImporter = importer; TheCache = IFCImportCache.Create(originalDocument, ifcFileName); - TheOptions = importer.m_ImportOptions = IFCImportOptions.Create(importOptions, ifcFileName, originalDocument); + TheOptions = importer.m_ImportOptions = IFCImportOptions.Create(importOptions); TheLog = IFCImportLog.CreateLog(ifcFileName, "log.html", !TheOptions.DisableLogging); return importer; } @@ -525,42 +525,44 @@ private void LogEndImportDetailed(Document ifcDocument) IList directShapeElements = collector.ToElements(); // This is inefficient, but we need to reliably get the IFCGuids - // HybridMap is for IFC GlobalId --> ElementId. This is used for almost all of the Hybrid IFC Import processing. - // reverseLookup is for ElementId --> IFC GlobalId. This is used to find the ElementId associated with a given IFC GlobalId (for logging purposes). - IDictionary reverseLookup = new Dictionary(); - foreach (KeyValuePair pair in TheHybridInfo.HybridMap) + if (Importer.TheHybridInfo?.HybridMap != null) { - try + // HybridMap is for IFC STEP Id --> ElementId. This is used for almost all of the Hybrid IFC Import processing. + // reverseLookup is for ElementId --> IFC STEP Id. This is used to find the ElementId associated with a given IFC STEP Id (for logging purposes). + IDictionary reverseLookup = new Dictionary(); + foreach (KeyValuePair pair in TheHybridInfo.HybridMap) { - ElementId elementId = pair.Value; - string ifcGuid = pair.Key; - reverseLookup.Add(pair.Value, pair.Key); + try + { + ElementId elementId = pair.Value; + string stepId = pair.Key; + reverseLookup.Add(elementId, stepId); + } + catch (ArgumentException ex) + { + TheLog.LogWarning(-1, $"Duplicate ElementId found when reversing Hybrid Map for logging {ex.Message}", false); + } } - catch (ArgumentException ex) + + // Log (into journal) IFC STEP Ids & ElementIds that were imported by AnyCAD or via Revit alone. + ifcDocument.Application.WriteJournalComment($"Hybrid IFC Import: Count of DirectShapes imported via AnyCAD: {Importer.TheHybridInfo.HybridElements.Count}", false); + foreach (Element element in directShapeElements) { - TheLog.LogWarning(-1, $"Duplicate ElementId found when reversing Hybrid Map for logging {ex.Message}", false); + string stepId; + if (reverseLookup.TryGetValue(element.Id, out stepId)) + { + ifcDocument.Application.WriteJournalComment($"Hybrid IFC Import: AnyCAD DirectShape (IFC STEP Id, ElementId): ({stepId}, {element.Id})", false); + } } - } - // Log (into journal) IFC GlobalIds & ElementIds that were imported by AnyCAD or via Revit alone. - ifcDocument.Application.WriteJournalComment($"Hybrid IFC Import: Count of DirectShapes imported via AnyCAD: {Importer.TheHybridInfo.HybridElements.Count}", false); - foreach (Element element in directShapeElements) - { - string ifcGuid; - if (reverseLookup.TryGetValue(element.Id, out ifcGuid)) + ifcDocument.Application.WriteJournalComment($"Hybrid IFC Import: Count of DirectShapes falling back to Revit: {numDirectShapes - Importer.TheHybridInfo.HybridElements.Count}", false); + foreach (Element element in directShapeElements) { - ifcDocument.Application.WriteJournalComment($"Hybrid IFC Import: AnyCAD DirectShape (IFC GUID, ElementId): ({ifcGuid}, {element.Id})", false); + if (reverseLookup.ContainsKey(element.Id)) + continue; + ifcDocument.Application.WriteJournalComment($"Hybrid IFC Import: Fallback DirectShape ElementId: ({element.Id})", false); } } - - ifcDocument.Application.WriteJournalComment($"Hybrid IFC Import: Count of DirectShapes falling back to Revit: {numDirectShapes - Importer.TheHybridInfo.HybridElements.Count}", false); - foreach (Element element in directShapeElements) - { - if (reverseLookup.ContainsKey(element.Id)) - continue; - string ifcGuid = IFCGUIDUtil.GetGUID(element); - ifcDocument.Application.WriteJournalComment($"Hybrid IFC Import: Fallback DirectShape (IFC GUID, ElementId): ({ifcGuid}, {element.Id})", false); - } } } @@ -696,7 +698,7 @@ public void ImportIFC(ImporterIFC importer) string fullIFCFileName = importer.FullFileName; IDictionary options = importer.GetOptions(); - TheOptions = m_ImportOptions = IFCImportOptions.Create(options, fullIFCFileName, importer.Document); + TheOptions = m_ImportOptions = IFCImportOptions.Create(options); // An early check, based on the options set - if we are allowed to use an up-to-date existing file on disk, use it. try @@ -728,9 +730,9 @@ public void ImportIFC(ImporterIFC importer) finally { TheLog?.Close(); - TheLog = null; IFCImportFile.TheFile?.Close(); TheHybridInfo = null; + TheLog = null; } } diff --git a/Source/Revit.IFC.Import/Properties/AssemblyInfo.cs b/Source/Revit.IFC.Import/Properties/AssemblyInfo.cs index 5bbc2056..f351ccce 100644 --- a/Source/Revit.IFC.Import/Properties/AssemblyInfo.cs +++ b/Source/Revit.IFC.Import/Properties/AssemblyInfo.cs @@ -1,5 +1,4 @@ using System.Reflection; -#if IFC_OPENSOURCE // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. @@ -8,18 +7,12 @@ [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Autodesk")] [assembly: AssemblyProduct("IFC Import for Revit")] -[assembly: AssemblyCopyright("@2012-2022 Autodesk, Inc. All rights reserved.")] +[assembly: AssemblyCopyright("@2012-2024 Autodesk, Inc. All rights reserved.")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] -[assembly: AssemblyVersion("24.2.0.49")] -[assembly: AssemblyFileVersion("24.2.0.49")] -#endif - -#region Using directives - - -#endregion +[assembly: AssemblyVersion("25.2.0.5")] +[assembly: AssemblyFileVersion("25.2.0.5")] diff --git a/Source/Revit.IFC.Import/Revit.IFC.Import.csproj b/Source/Revit.IFC.Import/Revit.IFC.Import.csproj index ef25bbdd..ba11a3e3 100644 --- a/Source/Revit.IFC.Import/Revit.IFC.Import.csproj +++ b/Source/Revit.IFC.Import/Revit.IFC.Import.csproj @@ -1,333 +1,49 @@ - - + - Debug - x86 - 8.0.30703 - 2.0 - {7F987D09-9716-4F50-ADE0-278E4B537101} - Library - Properties Revit.IFC.Import Revit.IFC.Import - v4.8 - - - 512 - {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - 4 - Publish\ - true - Disk - false - Foreground - 7 - Days - false - false - true - 0 - 1.0.0.%2a - false - false - true - Revit.IFC + true + - - - - - false - x64 - C:\ProgramData\Autodesk\ApplicationPlugins\IFC 2024.bundle\Contents\2024\ - - - false - bin\Releasex64\ - x64 - - - - False - ..\ThirdParty\ICSharpCode\ICSharpCode.SharpZipLib.DLL - true - - - False - ..\..\..\..\Program Files\Autodesk\Revit 2024\RevitAPI.dll - - - False - ..\..\..\..\Program Files\Autodesk\Revit 2024\RevitAPIIFC.dll - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + True True Resources.resx - - - - - - - - - - - - - - - - - - - - - - - - - - False - .NET Framework 2.0 %28x86%29 - false - - - False - .NET Framework 3.0 %28x86%29 - true - - - False - .NET Framework 3.5 - false - - - False - Windows Installer 3.1 - true - - {032EA4DC-181F-4453-9F93-E08DE1C07D95} - Revit.IFC.Common False + All + All - {b1e159b7-4b12-45a9-b83f-159e37798d44} - Revit.IFC.Import.Core + False + All + All + ResXFileCodeGenerator Resources.Designer.cs - - - - - - - - - \ No newline at end of file + + + + + + ..\..\..\..\Program Files\Autodesk\Revit 2025\RevitAPI.dll + + + ..\..\..\..\Program Files\Autodesk\Revit 2025\RevitAPIIFC.dll + + + diff --git a/Source/Revit.IFC.Import/Revit.IFC.Import.props b/Source/Revit.IFC.Import/Revit.IFC.Import.props deleted file mode 100644 index 0e84fc8e..00000000 --- a/Source/Revit.IFC.Import/Revit.IFC.Import.props +++ /dev/null @@ -1,25 +0,0 @@ - - - - bin\Debugx64\ - DEBUG;TRACE;IFC_OPENSOURCE - true - false - false - 4 - full - prompt - - - bin\Releasex64\ - TRACE;IFC_OPENSOURCE - false - true - false - 4 - none - prompt - - - - diff --git a/Source/Revit.IFC.Import/Utility/IFCCategoryUtil.cs b/Source/Revit.IFC.Import/Utility/IFCCategoryUtil.cs index 1d86652a..1b7d8a36 100644 --- a/Source/Revit.IFC.Import/Utility/IFCCategoryUtil.cs +++ b/Source/Revit.IFC.Import/Utility/IFCCategoryUtil.cs @@ -557,6 +557,8 @@ private static void InitEntityTypeToCategoryMaps() m_EntityTypeToCategory[IFCEntityType.IfcSpaceType] = BuiltInCategory.OST_GenericModel; m_EntityTypeToCategory[IFCEntityType.IfcSpaceHeater] = BuiltInCategory.OST_MechanicalEquipment; m_EntityTypeToCategory[IFCEntityType.IfcSpaceHeaterType] = BuiltInCategory.OST_MechanicalEquipment; + m_EntityTypeToCategory[IFCEntityType.IfcSpatialZone] = BuiltInCategory.OST_GenericModel; + m_EntityTypeToCategory[IFCEntityType.IfcSpatialZoneType] = BuiltInCategory.OST_GenericModel; m_EntityTypeToCategory[IFCEntityType.IfcStair] = BuiltInCategory.OST_Stairs; m_EntityTypeToCategory[IFCEntityType.IfcStairType] = BuiltInCategory.OST_Stairs; m_EntityTypeToCategory[IFCEntityType.IfcStairFlight] = BuiltInCategory.OST_Stairs; @@ -894,6 +896,63 @@ public static bool CanImport(IFCEntityType type, string predefinedType) return true; } + private static Category GetCategoryFromAcceptableNames(CategoryNameMap subcategories, string subCategoryName) + { + try + { + Category subCategory = subcategories.get_Item(subCategoryName); + if (subCategory != null) + return subCategory; + } + catch + { + } + + if (!Importer.TheOptions.IsHybridImport) + return null; + + // If we are doing hybrid import, try some other acceptable alternatives. + try + { + // First attempt: remove "Type". + int removeTypeLocation = subCategoryName.LastIndexOf("Type."); + if (removeTypeLocation > 0) + { + string altSubCategoryName = subCategoryName.Replace("Type.", "."); + Category subCategory = subcategories.get_Item(altSubCategoryName); + if (subCategory != null) + return subCategory; + } + } + catch + { + } + + try + { + // Second attempt: uppercase predefined type. + int predefinedTypeLocation = subCategoryName.LastIndexOf("."); + if (predefinedTypeLocation > 0) + { + string[] nameAndPredefinedType = subCategoryName.Split('.'); + if (nameAndPredefinedType.Count() == 2) + { + nameAndPredefinedType[1] = nameAndPredefinedType[1].ToUpper(); + string altSubCategoryName = string.Join(".", nameAndPredefinedType); + + Category subCategory = subcategories.get_Item(altSubCategoryName); + if (subCategory != null) + return subCategory; + } + } + } + catch + { + } + + return null; + } + private static Category GetOrCreateSubcategory(Document doc, int id, string subCategoryName, ElementId categoryId) { if (string.IsNullOrWhiteSpace(subCategoryName)) @@ -907,18 +966,8 @@ private static Category GetOrCreateSubcategory(Document doc, int id, string subC IDictionary createdSubcategories = Importer.TheCache.CreatedSubcategories; if (!createdSubcategories.TryGetValue(subCategoryName, out subCategory)) { - // Category may have been created by a previous action (probably a previous import). Look first. - // First check GenericModels. - // - try - { - CategoryNameMap subcategories = Importer.TheCache.GenericModelsCategory.SubCategories; - subCategory = subcategories.get_Item(subCategoryName); - } - catch - { - subCategory = null; - } + subCategory = GetCategoryFromAcceptableNames(Importer.TheCache.GenericModelsCategory?.SubCategories, + subCategoryName); // Then Topography. // diff --git a/Source/Revit.IFC.Import/Utility/IFCImportCache.cs b/Source/Revit.IFC.Import/Utility/IFCImportCache.cs index 62e8d676..3ac5b030 100644 --- a/Source/Revit.IFC.Import/Utility/IFCImportCache.cs +++ b/Source/Revit.IFC.Import/Utility/IFCImportCache.cs @@ -33,6 +33,8 @@ namespace Revit.IFC.Import.Utility /// public class IFCImportCache { + public AllocatedGeometryObjectCache AllocatedGeometryObjectCache { get; set; } = new AllocatedGeometryObjectCache(); + /// /// The ParameterBindings map associated with accessed documents. /// diff --git a/Source/Revit.IFC.Import/Utility/IFCImportHybridInfo.cs b/Source/Revit.IFC.Import/Utility/IFCImportHybridInfo.cs index e6c46d7e..c39541c7 100644 --- a/Source/Revit.IFC.Import/Utility/IFCImportHybridInfo.cs +++ b/Source/Revit.IFC.Import/Utility/IFCImportHybridInfo.cs @@ -31,42 +31,11 @@ namespace Revit.IFC.Import.Utility { + /// - /// A helper class to set and unset RepresentationsAlreadyCreated, with the "using" keyword. + /// Provide methods to perform Hybrid IFC Import. /// - public class RepresentationsAlreadyCreatedSetter : IDisposable - { - /// - /// The constructor. - /// - /// The GUID to check. - public RepresentationsAlreadyCreatedSetter(string guid) - { - // IFCRepresentationItem should know that it is processing something for Hybrid IFC Imports. - // Then it will create IFCHybridRepresentationItems, which are placeholders for body geometry created via AnyCAD. - // This is so data for Representation Item will still exist, even if legacy geometry does not. - if (Importer.TheHybridInfo?.HybridMap?.ContainsKey(guid) ?? false) - { - Importer.TheHybridInfo.RepresentationsAlreadyCreated = true; - } - } - - /// - /// The Dispose method. - /// - public void Dispose() - { - if (Importer.TheHybridInfo != null) - { - Importer.TheHybridInfo.RepresentationsAlreadyCreated = false; - } - } - } - -/// -/// Provide methods to perform Hybrid IFC Import. -/// -public class IFCImportHybridInfo + public class IFCImportHybridInfo { /// /// Keeps track of Elements imported (DirectShape/DirectShapeTypes) by AnyCAD @@ -74,7 +43,7 @@ public class IFCImportHybridInfo public IList HybridElements { get; set; } = new List(); /// - /// Map from IFCGuid --> Revit ElementId so legacy IFC Processing can find Elements. + /// Map from IFC STEP Id --> Revit ElementId so legacy IFC Processing can find Elements. /// public IDictionary HybridMap { get; set; } = new Dictionary(); @@ -83,19 +52,6 @@ public class IFCImportHybridInfo /// public IList ElementsToDelete { get; set; } = new List(); - /// - /// For IFCProject, Revit will still need to process IFCProductRepresentation/IFCRepresentation/IFCRepresentationItem. - /// For IFCProjectType, Revit will still need to process IFCRepresentationMap/IFCRepresentation/IFCRepresentationItem. - /// An example of data that must exist: LayerAssignment. - /// In both cases, body geometry will have been created by AnyCAD during pass one, so new body geometry cannot be created. - /// Communication must be made to IFCRepresentationItem that the IFCProduct/IFCProductType has already had its Representation Created. - /// That is what this flag indicates: - /// True: Representation (Body geometry) already created during pass one. Ignore all RepresentationItems that might create meshes, etc. The only - /// exception to this is points and curves. Instead an IFCHybridRepresentationItem will be created as a placeholder. - /// False: Representation (Body geometry) should be created like normal with Legacy IFC Import. - /// - public bool RepresentationsAlreadyCreated { get; set; } = false; - /// /// Document into which IFC Import occurs. /// @@ -111,13 +67,6 @@ public class IFCImportHybridInfo /// public XYZ LargeCoordinateOriginOffset { get; set; } = XYZ.Zero; - /// - /// Keeps track of one-to-many mapping of entities that will result in container to sub-Element relationships. - /// Key: stepId of the container entity. - /// Value: Set of IfcObjectDefinition entities that result in sub-Elements. - /// - public IDictionary> ContainerMap { get; set; } = null; - /// /// Internal reference to the class that is responsible for doing the actual import and Map creation. /// @@ -149,37 +98,36 @@ public IFCImportHybridInfo(Document ifcDocument, string ifcInputFile, bool doUpd // Import Elements // int? elementsImported = ImportElements(doUpdate); - if (elementsImported == null) + if (!elementsImported.HasValue) { Importer.TheLog.LogError(-1, "Hybrid IFC Import: Unknown Error during Element Import -- aborting", true); return; } - if (elementsImported == 0) + if (elementsImported.Value == 0) { Importer.TheLog.LogError(-1, "Hybrid IFC Import: elementsImportedList empty -- reverting to fallback for entire import.", false); return; } - // Associate Imported Elements with IFC Guids - // - int? associationsPerformed = AssociateElementsWithIFCGuids(); - if (associationsPerformed == null) + HybridMap = HybridImporter.GetIFCStepIdToElementIdMap(); + if (HybridMap == null) { - Importer.TheLog.LogError(-1, "Hybrid IFC Import: Unknown Error during Element / IFC Guid association.", true); + Importer.TheLog.LogError(-1, "Hybrid IFC Import: Unknown Error during IFC STEP Id/ElementId map creation.", true); return; } - // Not an error, but this may hinder the Import Later. - if (associationsPerformed != elementsImported) + if (HybridMap.Count != elementsImported.Value) { - Importer.TheLog.LogWarning(-1, "Hybrid IFC Import: Number of Element / IFC Guid associations do not match number of imported Elements.", false); + // Not an error, but this may hinder the Import Later. + Importer.TheLog.LogComment(-1, "Hybrid IFC Import: Number of IFC STEP Id/ElementIds associations do not match number of imported Elements. Not an error.", true); } + if (Importer.TheOptions.VerboseLogging) { Importer.TheLog.LogComment(-1, "--- Hybrid IFC Import: Start of Logging detailed Information about AnyCAD Import ---", false); - Importer.TheLog.LogComment(-1, "Hybrid IFC Import: If an IfcGuid does not appear in the following list, then it will fallback to Revit processing ---", false); + Importer.TheLog.LogComment(-1, "Hybrid IFC Import: If an IFC STEP Id does not appear in the following list, then it will fallback to Revit processing ---", false); LogImportedElementsDetailed(); LogHybridMapDetailed(); Importer.TheLog.LogComment(-1, "--- Hybrid IFC Import: End of Logging detailed Information about AnyCAD Import ---", false); @@ -197,6 +145,7 @@ public IFCImportHybridInfo(Document ifcDocument, string ifcInputFile, bool doUpd public void LogImportedElementsDetailed() { Importer.TheLog.LogComment(-1, "--- Hybrid IFC Import: Start Imported Element Details. ---", false); + Options options = new Options(); foreach (ElementId elementId in HybridElements) { DirectShape shape = IfcDocument?.GetElement(elementId) as DirectShape; @@ -206,15 +155,27 @@ public void LogImportedElementsDetailed() continue; } - ElementId directShapeType = shape.TypeId; - if ((directShapeType ?? ElementId.InvalidElementId) == ElementId.InvalidElementId) + // GeometryInstance for DirectShape indicates the DirectShapeType for the DirectShape. This DirectShapeType holds Geometry for DirectShape. + // This may differ from DirectShape.TypeId. + ElementId geometricDSTElementId = ElementId.InvalidElementId; + GeometryElement geometryElement = shape.get_Geometry(options); + if (geometryElement != null) { - Importer.TheLog.LogComment(-1, $"Hybrid IFC Import: DirectShape Imported with no DirectShapeType: {elementId}.", false); - } - else - { - Importer.TheLog.LogComment(-1, $"Hybrid IFC Import: (DirectShape, DirectShapeType) Imported: ({elementId}, {directShapeType}).", false); + foreach (GeometryObject geometryObject in geometryElement) + { + GeometryInstance geomInstance = geometryObject as GeometryInstance; + if (geomInstance == null) + { + continue; + } + + geometricDSTElementId = geomInstance.GetSymbolGeometryId().SymbolId; + break; + } } + + // Log Comment for all three -- (DirectShape, GeomInstance DirectShapeType, TypeId) + Importer.TheLog.LogComment(-1, $"Hybrid IFC Import: (DirectShape, Parametric DirectShapeType, Geometric DirectShapeType): ({elementId}, {shape.TypeId}, {geometricDSTElementId})", false); } Importer.TheLog.LogComment(-1, "--- Hybrid IFC Import: End Imported Element Details. ---", false); } @@ -222,7 +183,7 @@ public void LogImportedElementsDetailed() /// /// Log information about the Hybrid IFC Import Association Map (IFC GlobalId --> Revit ElementId). /// - public void LogHybridMapDetailed () + public void LogHybridMapDetailed() { Importer.TheLog.LogComment(-1, "--- Hybrid IFC Import: Start Hybrid Map Details. ---", false); if (HybridMap == null) @@ -239,23 +200,15 @@ public void LogHybridMapDetailed () { foreach (var mapEntry in HybridMap) { - string ifcGuid = mapEntry.Key; + string stepId = mapEntry.Key; ElementId elementId = mapEntry.Value; - if (!string.IsNullOrEmpty(ifcGuid) && ((elementId ?? ElementId.InvalidElementId) != ElementId.InvalidElementId)) - { - Importer.TheLog.LogComment(-1, $"Hybrid IFC Import: Map Entry Created (IFC Guid, ElementId): ({mapEntry.Key}, {mapEntry.Value})", false); - continue; - } - - if (!string.IsNullOrEmpty(ifcGuid)) + if (elementId == ElementId.InvalidElementId) { - Importer.TheLog.LogComment(-1, "Hybrid IFC Import: Hybrid Map entry has no IFC Guid.", false); - continue; + Importer.TheLog.LogComment(-1, $"Hybrid IFC Import: Hybrid Map entry has no ElementId for {stepId}", false); } - - if ((elementId ?? ElementId.InvalidElementId) != ElementId.InvalidElementId) - { - Importer.TheLog.LogComment(-1, $"Hybrid IFC Import: Hybrid Map entry has no ElementId for {ifcGuid}", false); + else + { + Importer.TheLog.LogComment(-1, $"Hybrid IFC Import: Map Entry Created (IFC STEP Id, ElementId): ({stepId}, {elementId})", false); } } } @@ -316,17 +269,7 @@ public void LogElementsToDeleteDetailed() if (update) { - //IFC Extension back - compatibility: - //HybridImporter.UpdateElements method available since Revit 2024.1, handle it for addin usage with the previous Revit versions. - // - try - { - TryToUpdateElements(); - } - catch(MissingMethodException) - { - HybridElements = HybridImporter.ImportElements(IfcDocument, IfcInputFile); - } + HybridElements = HybridImporter.UpdateElements(IfcDocument, IfcInputFile); } else { @@ -336,129 +279,6 @@ public void LogElementsToDeleteDetailed() return HybridElements?.Count; } - private void TryToUpdateElements() - { - HybridElements = HybridImporter.UpdateElements(IfcDocument, IfcInputFile); - } - - /// - /// Associate ElementIds with IFCGuids. In other words, populate the IFCGuid --> ElementId map. - /// - /// Number of entries in the map. - /// - public int? AssociateElementsWithIFCGuids() - { - if (HybridImporter == null) - { - throw new InvalidOperationException("Attempting to associate Elements with IfcGuids with null IFCHybridImporter"); - } - - if (IfcDocument == null) - { - Importer.TheLog.LogError(-1, "No document for Hybrid IFC Import", true); - return null; - } - - // CreateMap actually returns an ElementId-to-String map. This is because of two things: - // 1. We don't know if an external API does a case-sensitive comparison (which is extremely important for IFC GUIDS). - // 2. We do know that System.String uses a case-sensitive comparison. - // And then convert. - IDictionary hybridImportMap = HybridImporter.CreateMap(IfcDocument, HybridElements); - if (hybridImportMap == null) - { - Importer.TheLog.LogError(-1, "Hybrid IFC Import Map set to invalid value.", true); - return null; - } - - // IFCGuidKey exists for the sole purpose of retrieving the map using a well-defined operator< in C++. - foreach (KeyValuePair elementIdGuidPair in hybridImportMap) - { - // Use string for IFC Global from here on out. IFCGuidKey is not generic enough to use as an IFC Global Id elsewhere. - string ifcGuid = elementIdGuidPair.Key.IFCGlobalId; - ElementId elementId = elementIdGuidPair.Value; - if (elementId == ElementId.InvalidElementId) - { - Importer.TheLog.LogError(-1, "Invalid Element ID found during Hybrid IFC Import Map construction.", false); - } - try - { - HybridMap.Add(ifcGuid, elementId); - } - catch (ArgumentException) - { - Importer.TheLog.LogWarning(-1, "Duplicate IFC Global Ids. This will cause some IFC entities to fallback to Revit processing.", false); - } - catch (Exception) - { - Importer.TheLog.LogWarning(-1, "Error in adding items to IFC GUID-to-ElementId map. This will cause some IFC entities to fallback to Revit processing.", false); - } - } - return HybridMap?.Count; - } - - /// - /// Replaces ElementIds in both Hybrid Element list and Hybrid Map (IfcGuid->ElementId) - /// - /// GUID of IFC entity. - /// Old ElementId to replace. - /// New ElementId to replace old ElementId with. - public void ReplaceElementId(string ifcGuid, ElementId oldElementId, ElementId newElementId) - { - if ((oldElementId == ElementId.InvalidElementId) || (newElementId == ElementId.InvalidElementId)) - return; - - // Reassign in HybridElements. - int index = HybridElements.IndexOf(oldElementId); - if (index == -1) - { - Importer.TheLog.LogWarning(-1, $"Unable to replace {ifcGuid} ElementId in list of Hybrid Elements.", true); - return; - } - - HybridElements[index] = newElementId; - - // Reassign in HybridMap if it exists. - if (!HybridMap.ContainsKey(ifcGuid)) - { - Importer.TheLog.LogWarning(-1, $"Unable to replace {ifcGuid} ElementId in Map of IFCGuids to ElementIds.", true); - return; - } - HybridMap[ifcGuid] = newElementId; - } - - /// - /// Creates a DirectShape simply to contain Geometry copied from other Elements. - /// The DirectShape won't have any Geometry at this time, but it will be put into the HybridMap. - /// - /// IfcProduct entity corresponding to empty DirectShape. - /// ElementId of new DirectShape if successful, ElementId.InvalidElementId otherwise. - public ElementId CreateEmptyContainer(IFCProduct ifcProduct) - { - // If HybridMap is null or empty, no other Elements were imported using the ATF Pipeline, so this doesn't need to be created. - if ((ifcProduct == null) || ((HybridMap?.Count ?? 0) == 0)) - { - return ElementId.InvalidElementId; - } - - // If Container is already in HybridMap, DirectShape has already been created. - ElementId containerElementId; - if (HybridMap.TryGetValue(ifcProduct.GlobalId, out containerElementId)) - { - return containerElementId; - } - - DirectShape emptyContainerDS = IFCElementUtil.CreateElement(IfcDocument, ifcProduct.GetCategoryId(IfcDocument), - ifcProduct.GlobalId, null, ifcProduct.Id, ifcProduct.EntityType); - if (emptyContainerDS == null) - { - return ElementId.InvalidElementId; - } - - HybridMap.Add(ifcProduct.GlobalId, emptyContainerDS.Id); - - return emptyContainerDS.Id; - } - /// /// This will create a container DirectShape to represent an IFCGroup, which normally has neither geometry /// nor a Revit Element associated with it. @@ -498,8 +318,8 @@ public ElementId CreateContainer(IFCGroup ifcGroup) { if (ifcGroup.ContainerFilteredEntity(objectDefinition)) { - ElementId objDefId = ElementId.InvalidElementId; - if (HybridMap?.TryGetValue(objectDefinition.GlobalId, out objDefId) ?? false) + ElementId objDefId = IFCImportHybridInfo.GetHybridMapInformation(objectDefinition.Id); + if (IFCImportHybridInfo.IsValidElementId(objDefId)) { elements.Add(objDefId); } @@ -527,7 +347,7 @@ public ElementId CreateContainer(IFCGroup ifcGroup) /// Entity that may exhibit special-case behavior. /// The element id of the existing DirectShape. /// ElementId of new DirectShape if new Element created, ElementId.InvalidElement otherwise. - private void UpdateElementForSpecialCases(IFCObjectDefinition objectDefinition, ref ElementId hybridElementId) + private void UpdateElementForSpecialCases(IFCObjectDefinition objectDefinition, ElementId hybridElementId) { if (objectDefinition == null) { @@ -540,23 +360,7 @@ private void UpdateElementForSpecialCases(IFCObjectDefinition objectDefinition, { if (objectDefinition is IFCProduct architecturalColumn) { - try - { - UpdateStructuralColumnDirectShape(architecturalColumn, hybridElementId); - } - catch (MissingMethodException) - { - ElementId specialCaseElementId = CreateStructuralColumnDirectShape(architecturalColumn, hybridElementId); - - if ((specialCaseElementId != ElementId.InvalidElementId) && (specialCaseElementId != hybridElementId)) - { - ElementId hybridElementIdToDelete = new ElementId(hybridElementId.Value); - // specialCaseElementId has replaced hybridElementId in the HybridMap. - // The hybridElementId will be deleted later. - Importer.TheHybridInfo.ElementsToDelete.Add(hybridElementIdToDelete); - hybridElementId = specialCaseElementId; - } - } + UpdateStructuralColumnDirectShape(architecturalColumn, hybridElementId); } } } @@ -595,11 +399,6 @@ private void UpdateStructuralColumnDirectShape(IFCProduct ifcColumn, ElementId h return; } - //IFC Extension back-compatibility: - //ImporterIFCUtils.UpdateDirectShapeCategory method available since Revit 2024.1, handle it for addin usage with the previous Revit versions. - //Handle this with using CreateStructuralColumnDirectShape instead by catching MissingMethodExseption in UpdateElementForSpecialCases. - // - ImporterIFCUtils.UpdateDirectShapeCategory(directShape, new ElementId(BuiltInCategory.OST_StructuralColumns)); (IList newGeomObjects, ElementId directShapeTypeId) = @@ -630,80 +429,6 @@ private void UpdateStructuralColumnDirectShape(IFCProduct ifcColumn, ElementId h directShape.SetTypeId(directShapeGeomTypeId); } - /// - /// Create a new DirectShape for the new Structural Column. - /// - /// - /// Column that needs a Category change from OST_Column to OST_StructuralColumn. - /// ElementId of new Structural Column for successful creation, ElementId.InvalidElementId otherwise. - private ElementId CreateStructuralColumnDirectShape(IFCProduct ifcColumn, ElementId hybridElementId) - { - if (ifcColumn == null) - { - Importer.TheLog.LogError(-1, "IfcColumn invalid during DirectShape recategorization.", false); - return ElementId.InvalidElementId; - } - - int stepId = ifcColumn.Id; - ElementId ifcColumnCategory = ifcColumn.GetCategoryId(IfcDocument); - if (ifcColumnCategory != new ElementId(BuiltInCategory.OST_StructuralColumns)) - { - Importer.TheLog.LogWarning(stepId, "IfcColumn is not a Structural Column", false); - return ElementId.InvalidElementId; - } - - DirectShape oldDirectShape = IfcDocument.GetElement(hybridElementId) as DirectShape; - ElementId oldDirectShapeCategory = (oldDirectShape?.Category?.Id ?? ElementId.InvalidElementId); - if (oldDirectShapeCategory == ElementId.InvalidElementId) - { - Importer.TheLog.LogWarning(stepId, "Unable to determine Category of DirectShape.", false); - return ElementId.InvalidElementId; - } - - if (oldDirectShapeCategory == ifcColumnCategory) - { - Importer.TheLog.LogComment(stepId, "Category of Column and DirectShape agree. No recategorization needed.", false); - return ElementId.InvalidElementId; - } - - // Perform Deep copy of Geometry and DirectShapeTypes. - (IList newGeomObjects, ElementId directShapeTypeId) - = DuplicateGeometryForDirectShape(oldDirectShape, ifcColumn); - if ((newGeomObjects?.Count ?? 0) == 0) - { - Importer.TheLog.LogError(stepId, "Unable to duplicate Geometry for DirectShape recategorization.", false); - return ElementId.InvalidElementId; - } - - GeometryInstance newDirectShapeGeometryInstance = newGeomObjects.First() as GeometryInstance; ; - if (newDirectShapeGeometryInstance == null) - { - Importer.TheLog.LogWarning(stepId, "Duplicate Geometry is not a GeometryInstance. Using old Geometry.", false); - return ElementId.InvalidElementId; - } - - directShapeTypeId = newDirectShapeGeometryInstance.GetSymbolGeometryId().SymbolId; - if (directShapeTypeId == ElementId.InvalidElementId) - { - Importer.TheLog.LogWarning(stepId, "Even though new DirectShape Geometry created, unable to find DirectShapeType.", false); - return ElementId.InvalidElementId; - } - - IList structuralColumnGeometry = new List() { newDirectShapeGeometryInstance }; - - DirectShape newDirectShape = IFCElementUtil.CreateElement(IfcDocument, ifcColumnCategory, ifcColumn.GlobalId, structuralColumnGeometry, stepId, ifcColumn.EntityType); - if (newDirectShape == null) - { - Importer.TheLog.LogError(stepId, "Unable to create new DirectShape Element for Structural Column.", false); - return ElementId.InvalidElementId; - } - - newDirectShape.SetTypeId(directShapeTypeId); - return newDirectShape.Id; - } - - - /// /// Duplicates Geometry within DirectShape for a new DirectShape creation. /// This is to drive the process where the DirectShape -> GInstance -> DirectShapeType -> etc. will be preserved. @@ -763,7 +488,7 @@ private GeometryInstance GetDirectShapeGeometryInstance(DirectShape directShape) } // Most of the work happens here to Copy DirectShapeTypes. - ElementId newDirectShapeTypeId = DeepCopyDirectShapeType(oldDirectShapeTypeElementId, + ElementId newDirectShapeTypeId = DeepCopyDirectShapeType(oldDirectShapeTypeElementId, oldDirectShapeTypeGeometryElement, ifcProduct.GetCategoryId(IfcDocument), ifcTypeObject); string definitionId = GetDirectShapeTypeDefinitionId(newDirectShapeTypeId); @@ -809,7 +534,7 @@ protected static string GetDirectShapeTypeDefinitionId(ElementId elementId) /// Category that DirectShapeTypes should be. /// IFCTypeObject step that drives this. If null, values from old DirectShapeType will be used. /// ElementId of new DirectShapeType, or ElementId.Invalid if unable to create new DirectShapeType at any step. - protected ElementId DeepCopyDirectShapeType(ElementId oldDirectShapeTypeElementId, GeometryElement oldDirectShapeTypeGeometryElement, + protected ElementId DeepCopyDirectShapeType(ElementId oldDirectShapeTypeElementId, GeometryElement oldDirectShapeTypeGeometryElement, ElementId newCategoryId, IFCTypeObject ifcTypeObject = null) { if ((oldDirectShapeTypeElementId == ElementId.InvalidElementId) || (oldDirectShapeTypeGeometryElement == null)) @@ -955,7 +680,7 @@ public IList DuplicateDirectShapeGeometry(IList direc { continue; } - + foreach (GeometryObject geometryObject in geometryElement) { // For Hybrid IFC Import, it is sufficient to check for GeometryInstances only. @@ -982,16 +707,14 @@ public IList DuplicateDirectShapeGeometry(IList direc /// But there are some data within Representation Items that need to persist to the new DirectShapes, and the only way to get that is to process the ProductRepresentation within the IFCProduct. /// So this populates that data. /// - /// Some data is contained in the ShapeEditScope (but not actual geometry). /// IFCProduct to edit. /// The associated element id. /// The list of created geometries. - public IList HandleHybridProductCreation(IFCImportShapeEditScope shapeEditScope, - IFCProduct ifcProduct, ref ElementId hybridElementId) + public IList HandleHybridProductCreation(IFCProduct ifcProduct, ElementId hybridElementId) { IList createdGeometries = new List(); - if (!Importer.TheOptions.IsHybridImport || (shapeEditScope == null) || (ifcProduct == null) || (hybridElementId == ElementId.InvalidElementId)) + if (!Importer.TheOptions.IsHybridImport || (ifcProduct == null) || (hybridElementId == ElementId.InvalidElementId)) { return createdGeometries; } @@ -1003,59 +726,25 @@ public IList DuplicateDirectShapeGeometry(IList direc return createdGeometries; } - // Get solids for IFCProduct. Only Points and Curves should be contained within "Solids". - if (ifcProduct.Solids?.Count > 0) - { - WireframeBuilder wireframeBuilder = new WireframeBuilder(); - foreach (IFCSolidInfo solidInfo in ifcProduct.Solids) - { - GeometryObject currObject = solidInfo.GeometryObject; - if (currObject is Point) - { - wireframeBuilder.AddPoint(currObject as Point); - } - else if (currObject is Curve) - { - wireframeBuilder.AddCurve(currObject as Curve); - } - else - { - continue; - } - createdGeometries.Add(currObject); - } - - directShape.AppendShape(wireframeBuilder); - } - - // Add Plan View Curves. - if (shapeEditScope.AddPlanViewCurves(ifcProduct.FootprintCurves, ifcProduct.Id)) - { - shapeEditScope.SetPlanViewRep(directShape); - } - - // IFCProduct needs PresentationLayer parameter, which is contained in the RepresentationItem (or IFCHybridRepresentationItem). - ifcProduct.PresentationLayerNames.UnionWith(shapeEditScope.PresentationLayerNames); - // Handle Special Cases: // 1. Possible Category Change for Structural Columns. This requires a whole new DirectShape/DirectShapeType tree creation. // 2. Containers for IfcRelAggregates (e.g., IfcWall & IfcBuildingElementParts). - Importer.TheHybridInfo.UpdateElementForSpecialCases(ifcProduct, ref hybridElementId); + Importer.TheHybridInfo.UpdateElementForSpecialCases(ifcProduct, hybridElementId); return createdGeometries; } /// - /// Adds IFCTypeObject GUID and DirectShapeType ElementId to HybridMap. + /// Adds IFCTypeObject STEP Id and DirectShapeType ElementId to HybridMap. /// /// /// Parameter 1: corresponds to DirectShape/IfcProduct. /// Parameter 2: corresponds to DirectShapeType/IfcTypeProduct. /// - /// Guid of IFCObject corresponding to DirectShape Element. + /// ElementId of DirectShape corresponding to IFCObject corresponding to this IFCTypeObject. /// Handle for IFCTypeObject. - public void AddTypeToHybridMap (string ifcObjectGuid, IFCAnyHandle ifcTypeObject) + public void AddTypeToHybridMap(ElementId directShapeElementId, IFCAnyHandle ifcTypeObject) { - if (string.IsNullOrEmpty(ifcObjectGuid)) + if (directShapeElementId == ElementId.InvalidElementId) return; if (IFCAnyHandleUtil.IsNullOrHasNoValue(ifcTypeObject)) @@ -1064,32 +753,72 @@ public void AddTypeToHybridMap (string ifcObjectGuid, IFCAnyHandle ifcTypeObject return; } - if (HybridMap == null) - { - Importer.TheLog.LogError(ifcTypeObject.Id, "HybridMap is null while trying to process IFCTypeObject for IFCObject. This shouldn't happen", true); + ElementId hybridElementId = IFCImportHybridInfo.GetHybridMapInformation(ifcTypeObject.Id); + if ((hybridElementId == null) || (hybridElementId != ElementId.InvalidElementId)) return; - } - string ifcTypeObjectGuid = IFCImportHandleUtil.GetRequiredStringAttribute(ifcTypeObject, "GlobalId", false); - if ((string.IsNullOrEmpty(ifcTypeObjectGuid)) || HybridMap.ContainsKey(ifcTypeObjectGuid)) - { - Importer.TheLog.LogComment(ifcTypeObject.Id, $"Already added IFC GUID {ifcTypeObjectGuid} to HybridMap. Not an error.", true); + DirectShape directShape = IfcDocument.GetElement(directShapeElementId) as DirectShape; + if (directShape == null) return; + + ElementId directShapeTypeElementId = directShape.TypeId; + if (directShapeTypeElementId != ElementId.InvalidElementId) + { + HybridMap.Add(ifcTypeObject.StepId.ToString(), directShapeTypeElementId); } + } + + /// + /// Indicates if indicated Element is a DirectShape, meaning it has usable Hybrid-imported Geometry. + /// + /// ElementId representing DirectShape. + /// True if a DirectShape, False otherwise. + public bool IsValidDirectShape(ElementId directShapeElementId) + { + return IfcDocument?.GetElement(directShapeElementId) is DirectShape; + } - ElementId directShapeElementId = ElementId.InvalidElementId; - if (HybridMap.TryGetValue(ifcObjectGuid, out directShapeElementId)) + /// + /// Retrieves the ElementId for an IFC entity, based on the STEP Id of the entity. + /// + /// STEP Id of the IFC entity. + /// ElementId of the IFC entity, or ElementId.InvalidElementId if not in the HybridMap, or null if Hybrid IFC Import not running. + public static ElementId GetHybridMapInformation(int stepId) + { + if (!Importer.TheOptions.IsHybridImport || Importer.TheHybridInfo?.HybridMap == null || (stepId <= 0)) + return null; + + string stepIdAsString = stepId.ToString(); + if (Importer.TheHybridInfo.HybridMap.TryGetValue(stepIdAsString, out ElementId hybridElementId)) { - DirectShape directShape = IfcDocument.GetElement(directShapeElementId) as DirectShape; - if (directShape == null) - return; + return hybridElementId; + } - ElementId directShapeTypeElementId = directShape.TypeId; - if (directShapeTypeElementId != ElementId.InvalidElementId) - { - HybridMap.Add(ifcTypeObjectGuid, directShapeTypeElementId); - } + return ElementId.InvalidElementId; + } + + /// + /// Retrieves the ElementId for an IFC entity, based on the STEP Id of the entity. + /// + /// Handle representing IFC entity./param> + /// ElementId of the IFC entity, or ElementId.InvalidElementId if not in the HybridMap, or null if Hybrid IFC Import not running. + + public static ElementId GetHybridMapInformation(IFCAnyHandle ifcEntity) + { + if (IFCAnyHandleUtil.IsNullOrHasNoValue(ifcEntity)) + { + Importer.TheLog.LogError(-1, "Hybrid IFC Import: attempting to retrieve invalid Entity from HybridMap", true); + return null; } + + return GetHybridMapInformation(ifcEntity.StepId); } + + /// + /// Simple helper function to check for Invalid ElementIds. + /// + /// ElementId for comparison. + /// True if elementId is non-null and not ElementId.InvalidElementId. + public static bool IsValidElementId(ElementId elementId) => ((elementId != null) && (elementId != ElementId.InvalidElementId)); } } diff --git a/Source/Revit.IFC.Import/Utility/IFCImportOptions.cs b/Source/Revit.IFC.Import/Utility/IFCImportOptions.cs index 999e7e1d..6a1c0a73 100644 --- a/Source/Revit.IFC.Import/Utility/IFCImportOptions.cs +++ b/Source/Revit.IFC.Import/Utility/IFCImportOptions.cs @@ -209,7 +209,7 @@ protected IFCImportOptions() { } - protected IFCImportOptions(IDictionary options, string ifcFileName, Document doc) + protected IFCImportOptions(IDictionary options) { // "Intent": covers what the import operation is intended to create. // The two options are: @@ -319,22 +319,11 @@ protected IFCImportOptions(IDictionary options, string ifcFileNa ImportIFCOptions importIFCOptions = ImportIFCOptions.GetImportIFCOptions(); string linkProcessor = importIFCOptions.LinkProcessor; - string revitVersion = doc.Application.VersionBuild; - if (revitVersion.StartsWith("24.0")) - { - // For Revit 24.0.x, only use hybrid import if revit.ini has "AnyCAD" set. - IsHybridImport = (string.Compare(linkProcessor, "AnyCAD", true) == 0) && - (ifcFileName.EndsWith(".ifc")) && (Processor is IFCDefaultProcessor); - } - else - { - // For Revit 24.1.x - // Use hybrid import if revit.ini does not have "Legacy" in it and we are using - // the default (Revit) processor. For other processors (e.g., Navis), revert - // to Legacy. - IsHybridImport = (string.Compare(linkProcessor, "Legacy", true) != 0) && - (Processor is IFCDefaultProcessor); - } + // Use hybrid import if revit.ini does not have "Legacy" in it and we are using + // the default (Revit) processor. For other processors (e.g., Navis), revert + // to Legacy. + IsHybridImport = (string.Compare(linkProcessor, "Legacy", true) != 0) && + (Processor is IFCDefaultProcessor); } /// @@ -342,9 +331,9 @@ protected IFCImportOptions(IDictionary options, string ifcFileNa /// /// The user-set options for this import. /// The new IFCImportOptions class. - static public IFCImportOptions Create(IDictionary options, string ifcFileName, Document doc) + static public IFCImportOptions Create(IDictionary options) { - return new IFCImportOptions(options, ifcFileName, doc); + return new IFCImportOptions(options); } } } \ No newline at end of file diff --git a/Source/Revit.IFC.Import/Utility/ParametersToSet.cs b/Source/Revit.IFC.Import/Utility/ParametersToSet.cs index b5d00939..d9712b06 100644 --- a/Source/Revit.IFC.Import/Utility/ParametersToSet.cs +++ b/Source/Revit.IFC.Import/Utility/ParametersToSet.cs @@ -17,57 +17,10 @@ public void Dispose() { if (ParametersToSet != null) { - //IFC Extension back - compatibility: - //Parameter.SetMultiple method available since Revit 2024.1, handle it for addin usage with the previous Revit versions. - // - try - { - TryToSetMultipleParameters(); - } - catch (MissingMethodException) - { - foreach (Tuple parameterAndParameterValue in ParametersToSet.ParameterList) - { - Parameter param = parameterAndParameterValue.Item1; - ParameterValue paramValue = parameterAndParameterValue.Item2; - - if(param != null) - { - StorageType storageType = param.StorageType; - switch (storageType) - { - case StorageType.Integer: - { - param.Set((int)(paramValue as IntegerParameterValue)?.Value); - break; - } - case StorageType.Double: - { - param.Set((double)(paramValue as DoubleParameterValue)?.Value); - break; - } - case StorageType.String: - { - param.Set((string)(paramValue as StringParameterValue)?.Value); - break; - } - case StorageType.ElementId: - { - param.Set((ElementId)(paramValue as ElementIdParameterValue)?.Value); - break; - } - } - } - } - } + Parameter.SetMultiple(ParametersToSet.ParameterList); } } - private void TryToSetMultipleParameters() - { - Parameter.SetMultiple(ParametersToSet.ParameterList); - } - public ParametersToSet ParametersToSet { get; private set; } = null; } diff --git a/Source/Revit.IFC.Import/Utility/ProcessIFCRelation.cs b/Source/Revit.IFC.Import/Utility/ProcessIFCRelation.cs index 9399147e..ddb74409 100644 --- a/Source/Revit.IFC.Import/Utility/ProcessIFCRelation.cs +++ b/Source/Revit.IFC.Import/Utility/ProcessIFCRelation.cs @@ -108,28 +108,6 @@ static public ICollection ProcessRelatedObjects(IFCObjectDe } } - if (Importer.TheOptions.IsHybridImport && Importer.TheHybridInfo != null && - IFCAnyHandleUtil.IsTypeOf(ifcRelAssignsOrAggregates, IFCEntityType.IfcRelAggregates) && - (relatedTo is IFCElement ifcElementRelatedTo)) - { - if (Importer.TheHybridInfo.ContainerMap == null) - { - Importer.TheHybridInfo.ContainerMap = new Dictionary>(); - } - - HashSet containedObjectDefinitions = null; - if (Importer.TheHybridInfo.ContainerMap.TryGetValue(ifcElementRelatedTo.Id, out containedObjectDefinitions)) - { - containedObjectDefinitions.UnionWith(relatedObjectSet); - } - else - { - containedObjectDefinitions = new HashSet(); - containedObjectDefinitions.UnionWith(relatedObjectSet); - Importer.TheHybridInfo.ContainerMap.Add(ifcElementRelatedTo.Id, containedObjectDefinitions); - } - } - return relatedObjectSet; } diff --git a/Source/RevitIFCTools/IFCEntityListWin.xaml.cs b/Source/RevitIFCTools/IFCEntityListWin.xaml.cs index feea2d67..3a7f1216 100644 --- a/Source/RevitIFCTools/IFCEntityListWin.xaml.cs +++ b/Source/RevitIFCTools/IFCEntityListWin.xaml.cs @@ -463,9 +463,9 @@ private void Button_ExportInfoPair_Click(object sender, RoutedEventArgs e) if (!Enum.TryParse(entity, out entType)) return; - exportInfo.SetValueWithPair(entType, predefType); + exportInfo.SetByTypeAndPredefinedType(entType, predefType); textBox_type2.Text = "Instance: " + exportInfo.ExportInstance + "\nType: " + exportInfo.ExportType - + "\r\nPredefinedTpe: " + exportInfo.ValidatedPredefinedType; + + "\r\nPredefinedTpe: " + exportInfo.PredefinedType; } } } diff --git a/Source/RevitIFCTools/MainWindow.xaml.cs b/Source/RevitIFCTools/MainWindow.xaml.cs index 44966fe1..0ab44350 100644 --- a/Source/RevitIFCTools/MainWindow.xaml.cs +++ b/Source/RevitIFCTools/MainWindow.xaml.cs @@ -19,6 +19,7 @@ using System; using System.Windows; +using RevitIFCTools.ParameterExpr; namespace RevitIFCTools { @@ -51,7 +52,7 @@ private void button_Cancel_Click(object sender, RoutedEventArgs e) private void button_paramexpr_Click(object sender, RoutedEventArgs e) { - ParameterExpr.ExprTester exprTest = new ParameterExpr.ExprTester(); + ExprTester exprTest = new ParameterExpr.ExprTester(); exprTest.ShowDialog(); } diff --git a/Source/RevitIFCTools/Properties/AssemblyInfo.cs b/Source/RevitIFCTools/Properties/AssemblyInfo.cs index d4fa2d58..71caca56 100644 --- a/Source/RevitIFCTools/Properties/AssemblyInfo.cs +++ b/Source/RevitIFCTools/Properties/AssemblyInfo.cs @@ -12,7 +12,7 @@ [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Autodesk")] [assembly: AssemblyProduct("RevitIFCTools")] -[assembly: AssemblyCopyright("Copyright © 2012-2022")] +[assembly: AssemblyCopyright("Copyright © 2012-2024")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -51,5 +51,6 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("24.2.0.49")] -[assembly: AssemblyFileVersion("24.2.0.49")] +[assembly: System.Runtime.Versioning.SupportedOSPlatformAttribute("windows7.0")] +[assembly: AssemblyVersion("25.2.0.0")] +[assembly: AssemblyFileVersion("25.2.0.0")] \ No newline at end of file diff --git a/Source/RevitIFCTools/Properties/Resources.Designer.cs b/Source/RevitIFCTools/Properties/Resources.Designer.cs index 2114038e..5a9c312d 100644 --- a/Source/RevitIFCTools/Properties/Resources.Designer.cs +++ b/Source/RevitIFCTools/Properties/Resources.Designer.cs @@ -19,7 +19,7 @@ namespace RevitIFCTools.Properties { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Resources { diff --git a/Source/RevitIFCTools/RevitIFCTools.csproj b/Source/RevitIFCTools/RevitIFCTools.csproj index 633a7d39..e4e19605 100644 --- a/Source/RevitIFCTools/RevitIFCTools.csproj +++ b/Source/RevitIFCTools/RevitIFCTools.csproj @@ -1,228 +1,48 @@ - - - + - Debug - x64 - {FFD592F6-DB2C-4E5D-9C38-9CCC9EABB3EA} - WinExe - Properties RevitIFCTools RevitIFCTools - v4.8 - 512 - {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - 4 - true - publish\ - true - Disk - false - Foreground - 7 - Days - false - false - true - 0 - 1.0.0.%2a - false - false - true - - - - - - true - bin\Debugx64\ - TRACE;DEBUG;OVERRIDE_VERSION;IFC_OPENSOURCE - full - x64 - 7.3 - prompt - - - bin\Releasex64\ - TRACE;IFC_OPENSOURCE - true - pdbonly - x64 - 7.3 - prompt + WinExe + true + false + False + - - ..\..\..\..\Program Files\Autodesk\Revit 2024\RevitAPI.dll - - - - - - - - - - - - - 4.0 - - - - - - - - MSBuild:Compile - Designer - - - - ExprTester.xaml - - - - - - - - - - - - - - - - - - - - MSBuild:Compile - Designer - - - App.xaml - Code - - - IFCEntityListWin.xaml - Code - - - Designer - MSBuild:Compile - - - Designer - MSBuild:Compile - - - Designer - MSBuild:Compile - - - - - GeneratePsetDefWin.xaml - - - MainWindow.xaml - - - Code - - - True - True - Resources.resx - - - True - Settings.settings - True - - - ResXFileCodeGenerator - Resources.Designer.cs - - - PreserveNewest - - - Designer - PreserveNewest - - - PreserveNewest - - - Designer - PreserveNewest - - - PreserveNewest - - - Designer - PreserveNewest - - - PreserveNewest - - - SettingsSingleFileGenerator - Settings.Designer.cs - - + + + + + - - - - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - + + + + + - - + + + + + + + ..\..\..\..\Program Files\Autodesk\Revit 2025\RevitAPI.dll + + + + + 4.6.6 - - - {032ea4dc-181f-4453-9f93-e08de1c07d95} - Revit.IFC.Common - - - {bce5141a-291b-4cd8-a69b-7b9345aa00e9} - Revit.IFC.Export - - - - - xcopy "$(TargetPath)" "C:\ProgramData\Autodesk\ApplicationPlugins\IFC 2024.bundle\Contents\2024\" /F /R /Y /I -xcopy "$(TargetDir)IFC*.xsd" "C:\ProgramData\Autodesk\ApplicationPlugins\IFC 2024.bundle\Contents\2024\" /F /R /Y /I -xcopy "$(TargetDir)IFC*.exp" "C:\ProgramData\Autodesk\ApplicationPlugins\IFC 2024.bundle\Contents\2024\" /F /R /Y /I -xcopy "$(TargetDir)IFC Shared Parameters*.txt" "C:\ProgramData\Autodesk\ApplicationPlugins\IFC 2024.bundle\Contents\2024\" /F /R /Y /I -xcopy "$(TargetDir)IFCCertifiedEntitiesAndPSets.json" "C:\ProgramData\Autodesk\ApplicationPlugins\IFC 2024.bundle\Contents\2024\" /F /R /Y /I - - - + + + + + + + + + \ No newline at end of file diff --git a/Source/ThirdParty/ICSharpCode/ICSharpCode.SharpZipLib.DLL b/Source/ThirdParty/ICSharpCode/ICSharpCode.SharpZipLib.DLL index 8cc48486..08de420a 100644 Binary files a/Source/ThirdParty/ICSharpCode/ICSharpCode.SharpZipLib.DLL and b/Source/ThirdParty/ICSharpCode/ICSharpCode.SharpZipLib.DLL differ diff --git a/VSProps/Revit.CSharp.Sdk.props b/VSProps/Revit.CSharp.Sdk.props new file mode 100644 index 00000000..41952d36 --- /dev/null +++ b/VSProps/Revit.CSharp.Sdk.props @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/VSProps/Revit.CSharp.Sdk.targets b/VSProps/Revit.CSharp.Sdk.targets new file mode 100644 index 00000000..23a97a1b --- /dev/null +++ b/VSProps/Revit.CSharp.Sdk.targets @@ -0,0 +1,21 @@ + + + TRACE;$(DefineConstants) + + + TRACE;$(DefineConstants) + + + DEBUG;TRACE;$(DefineConstants) + + + 7 + + + + + + $(MSBuildAllProjects);$(SolutionDir)VSProps\Revit.CSharp.Sdk.targets + + diff --git a/VSProps/Revit.Common.Sdk.props b/VSProps/Revit.Common.Sdk.props new file mode 100644 index 00000000..97eb17eb --- /dev/null +++ b/VSProps/Revit.Common.Sdk.props @@ -0,0 +1,40 @@ + + + + x64 + Debug;Release;Slow_Debug + Debug + x64 + $(SolutionDir)\Intermediate\$(Configuration)$(Platform)\$(MSBuildProjectName)\ + $(BaseIntermediateOutputPath) + false + false + false + x64 + true + true + true + false + true + true + + + + x64 + x64\Release + Release + + + + x64 + x64\Release + Release + + + + x64 + x64\Debug + Debug + d + + diff --git a/VSProps/Revit.Common.Sdk.targets b/VSProps/Revit.Common.Sdk.targets new file mode 100644 index 00000000..f01f9c6d --- /dev/null +++ b/VSProps/Revit.Common.Sdk.targets @@ -0,0 +1,37 @@ + + + + + Code + + + + + x64 + Airmax2016 + true + full + true + 4 + 1668 + + + false + x64\Release + + + true + x64\Release + + + false + x64\Debug + + + 1607 + + + $(MSBuildAllProjects);$(SolutionDir)VSProps\Revit.Common.Sdk.targets + + + diff --git a/VSProps/Revit.NetVersion.props b/VSProps/Revit.NetVersion.props new file mode 100644 index 00000000..d711542d --- /dev/null +++ b/VSProps/Revit.NetVersion.props @@ -0,0 +1,6 @@ + + + + net8.0-windows + +