2013-03-24 10:48:39 +00:00
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell 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 *
* Lesser GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
/********************************************************************************
* *
2022-08-27 07:53:17 -04:00
* This started as a brief example of how IfcOpenShell can be interfaced from *
2013-03-24 10:48:39 +00:00
* within a C++ context, it has since then evolved into a fullfledged command *
* line application that is able to convert geometry in an IFC files into *
2022-08-27 07:53:17 -04:00
* several tessellated and topological output formats. *
2013-03-24 10:48:39 +00:00
* *
********************************************************************************/
2024-06-10 20:10:53 +02:00
// windows stuff: defines max as a macro when including windows.h
// error C2589: '(': illegal token on right side of '::'
# define NOMINMAX
2018-03-16 13:49:11 +01:00
# include "../serializers/ColladaSerializer.h"
2019-05-07 17:06:45 +02:00
# include "../serializers/GltfSerializer.h"
2021-02-12 17:32:43 +01:00
# include "../serializers/HdfSerializer.h"
2018-03-16 13:49:11 +01:00
# include "../serializers/IgesSerializer.h"
# include "../serializers/StepSerializer.h"
# include "../serializers/WavefrontObjSerializer.h"
2018-03-16 16:04:48 +01:00
# include "../serializers/XmlSerializer.h"
2018-03-16 13:49:11 +01:00
# include "../serializers/SvgSerializer.h"
2023-05-04 20:30:03 +02:00
# include "../serializers/USDSerializer.h"
2024-09-27 16:32:41 +02:00
# include "../serializers/TtlWktSerializer.h"
2025-02-25 21:29:54 +01:00
# include "../serializers/RocksDbSerializer.h"
2025-11-17 14:06:39 +01:00
# include "../serializers/JsonSerializer.h"
2015-07-26 14:26:13 +00:00
2023-03-21 20:15:01 +01:00
# include "../ifcgeom/IfcGeomFilter.h"
# include "../ifcgeom/Iterator.h"
# include "../ifcgeom/IfcGeomRenderStyles.h"
2025-09-26 14:24:49 +02:00
# include "../ifcgeom/hybrid_kernel.h"
2013-03-24 10:48:39 +00:00
2019-02-08 15:18:55 +01:00
# include "../ifcparse/utils.h"
2023-03-22 11:17:14 +01:00
# ifdef IFOPSH_WITH_OPENCASCADE
2016-01-25 13:30:33 +01:00
# include <Standard_Version.hxx>
2015-11-23 15:52:12 +02:00
2018-07-08 11:08:07 +02:00
# if OCC_VERSION_HEX < 0x60900
# include <IGESControl_Controller.hxx>
# endif
2023-03-22 11:17:14 +01:00
# endif
2016-03-07 22:37:36 +02:00
# include <boost/program_options.hpp>
2018-10-08 16:36:48 +03:00
# include <boost/make_shared.hpp>
2016-03-07 22:37:36 +02:00
# include <fstream>
# include <sstream>
# include <set>
# include <time.h>
2023-03-22 11:17:14 +01:00
# include <iomanip>
2016-03-07 22:37:36 +02:00
2016-02-07 12:28:05 +02:00
# if USE_VLD
2016-02-07 00:30:08 +02:00
# include <vld.h>
# endif
2019-02-08 15:18:55 +01:00
# ifdef _MSC_VER
# include <io.h>
# include <fcntl.h>
2019-05-21 12:03:44 +01:00
# endif
2019-07-09 16:00:58 +02:00
2019-02-08 15:18:55 +01:00
# include <random>
2019-07-09 16:00:58 +02:00
# include <thread>
2019-02-08 15:18:55 +01:00
# if defined(_MSC_VER) && defined(_UNICODE)
typedef std : : wstring path_t ;
2020-08-16 16:17:38 +02:00
typedef std : : wofstream ofstream_t ;
2019-02-08 15:18:55 +01:00
static std : : wostream & cout_ = std : : wcout ;
static std : : wostream & cerr_ = std : : wcerr ;
# else
typedef std : : string path_t ;
2020-08-16 16:17:38 +02:00
typedef std : : ofstream ofstream_t ;
2019-02-08 15:18:55 +01:00
static std : : ostream & cout_ = std : : cout ;
static std : : ostream & cerr_ = std : : cerr ;
# endif
2019-05-28 12:48:40 +02:00
const std : : string DEFAULT_EXTENSION = " .obj " ;
2016-03-20 12:48:20 +02:00
const std : : string TEMP_FILE_EXTENSION = " .tmp " ;
2013-03-24 10:48:39 +00:00
2016-12-16 13:51:07 +02:00
namespace po = boost : : program_options ;
2016-03-07 22:37:36 +02:00
void print_version ( )
{
2023-03-22 11:17:14 +01:00
cout_ < < " IfcOpenShell IfcConvert " < < IFCOPENSHELL_VERSION ;
# ifdef IFOPSH_WITH_OPENCASCADE
cout_ < < " (OCC " < < OCC_VERSION_STRING_EXT < < " ) " ;
# endif
cout_ < < " \n " ;
2013-03-24 10:48:39 +00:00
}
2016-03-11 10:05:33 +02:00
void print_usage ( bool suggest_help = true )
2016-03-07 22:37:36 +02:00
{
2019-02-08 15:18:55 +01:00
cout_ < < " Usage: IfcConvert [options] <input.ifc> [<output>] \n "
2016-03-07 22:37:36 +02:00
< < " \n "
2018-11-02 16:10:06 +01:00
< < " Converts (the geometry in) an IFC file into one of the following formats: \n "
2017-02-21 16:04:05 +02:00
< < " .obj WaveFront OBJ (a .mtl file is also created) \n "
2016-03-07 22:37:36 +02:00
# ifdef WITH_OPENCOLLADA
2017-02-21 16:04:05 +02:00
< < " .dae Collada Digital Assets Exchange \n "
2019-05-07 17:06:45 +02:00
# endif
# ifdef WITH_GLTF
< < " .glb glTF Binary glTF v2.0 \n "
2023-05-04 20:30:03 +02:00
# endif
# ifdef WITH_USD
< < " .usd USD Universal Scene Description \n "
2013-11-22 08:23:11 +00:00
# endif
2017-02-21 16:04:05 +02:00
< < " .stp STEP Standard for the Exchange of Product Data \n "
< < " .igs IGES Initial Graphics Exchange Specification \n "
2018-03-16 16:04:48 +01:00
< < " .xml XML Property definitions and decomposition tree \n "
2025-11-17 14:06:39 +01:00
# ifdef WITH_GLTF
< < " .json JSON Property definitions and decomposition tree in xeokit json format \n "
# endif
< < " .rdb RocksDB RocksDB Key-Value store serialization of IFC data \n "
2025-02-25 21:29:54 +01:00
< < " .svg SVG Scalable Vector Graphics (2D floor plan) \n "
2021-08-31 11:03:28 +02:00
# ifdef WITH_HDF5
2021-02-19 17:12:58 +01:00
< < " .h5 HDF Hierarchical Data Format storing positions, normals and indices \n "
2021-08-31 11:03:28 +02:00
# endif
2024-09-27 16:32:41 +02:00
< < " .ttl TTL/WKT RDF Turtle with Well-Known-Text geometry \n "
2018-11-02 16:10:06 +01:00
< < " .ifc IFC-SPF Industry Foundation Classes \n "
< < " \n "
2019-05-28 12:48:40 +02:00
< < " If no output filename given, <input> " < < IfcUtil : : path : : from_utf8 ( DEFAULT_EXTENSION ) < < " will be used as the output file. \n " ;
2016-03-11 10:05:33 +02:00
if ( suggest_help ) {
2019-02-08 15:18:55 +01:00
cout_ < < " \n Run 'IfcConvert --help' for more information. " ;
2016-03-11 10:05:33 +02:00
}
2019-02-08 15:18:55 +01:00
cout_ < < std : : endl ;
2016-03-07 22:37:36 +02:00
}
2017-02-21 16:04:05 +02:00
/// @todo Add help for single option
2016-12-16 13:51:07 +02:00
void print_options ( const po : : options_description & options )
2016-03-07 22:37:36 +02:00
{
2019-02-08 15:18:55 +01:00
# if defined(_MSC_VER) && defined(_UNICODE)
// See issue https://svn.boost.org/trac10/ticket/10952
std : : ostringstream temp ;
temp < < options ;
cout_ < < " \n " < < temp . str ( ) . c_str ( ) ;
# else
cout_ < < " \n " < < options ;
# endif
cout_ < < std : : endl ;
2013-03-24 10:48:39 +00:00
}
2019-02-08 15:18:55 +01:00
template < typename T >
T change_extension ( const T & fn , const T & ext ) {
typename T : : size_type dot = fn . find_last_of ( ' . ' ) ;
if ( dot ! = T : : npos ) {
return fn . substr ( 0 , dot ) + ext ;
2013-03-24 10:48:39 +00:00
} else {
2019-02-08 15:18:55 +01:00
return fn + ext ;
2013-03-24 10:48:39 +00:00
}
}
2019-02-08 15:18:55 +01:00
bool file_exists ( const std : : string & filename ) {
std : : ifstream file ( IfcUtil : : path : : from_utf8 ( filename ) . c_str ( ) ) ;
2016-03-18 22:14:05 +02:00
return file . good ( ) ;
}
2019-02-08 15:18:55 +01:00
static std : : basic_stringstream < path_t : : value_type > log_stream ;
2018-05-12 20:12:25 +02:00
void write_log ( bool ) ;
2026-06-10 18:30:54 +02:00
void fix_quantities ( IfcParse : : IfcFile & , bool , bool , bool , Logger & logger = Logger : : Root ( ) ) ;
2018-10-01 13:19:31 +03:00
std : : string format_duration ( time_t start , time_t end ) ;
2013-12-31 14:10:38 +00:00
2017-02-24 16:46:14 +02:00
/// @todo make the filters non-global
2017-02-20 19:41:52 +02:00
IfcGeom : : entity_filter entity_filter ; // Entity filter is used always by default.
IfcGeom : : layer_filter layer_filter ;
2018-09-07 14:08:56 +02:00
IfcGeom : : attribute_filter attribute_filter ;
2017-02-20 19:41:52 +02:00
2016-12-16 13:51:07 +02:00
struct geom_filter
{
2017-03-01 23:51:57 +02:00
geom_filter ( bool include , bool traverse ) : type ( UNUSED ) , include ( include ) , traverse ( traverse ) { }
geom_filter ( ) : type ( UNUSED ) , include ( false ) , traverse ( false ) { }
2017-02-16 19:49:46 +02:00
enum filter_type { UNUSED , ENTITY_TYPE , LAYER_NAME , ENTITY_ARG } ;
2016-12-16 13:51:07 +02:00
filter_type type ;
2017-03-01 23:51:57 +02:00
bool include ;
bool traverse ;
2017-02-16 14:28:18 +02:00
std : : string arg ;
2016-12-16 13:51:07 +02:00
std : : set < std : : string > values ;
} ;
// Specialized classes for knowing which type of filter we are validating within validate().
// Could not figure out easily how else to know it if using single type for both.
2017-03-01 23:51:57 +02:00
struct inclusion_filter : public geom_filter { inclusion_filter ( ) : geom_filter ( true , false ) { } } ;
struct inclusion_traverse_filter : public geom_filter { inclusion_traverse_filter ( ) : geom_filter ( true , true ) { } } ;
struct exclusion_filter : public geom_filter { exclusion_filter ( ) : geom_filter ( false , false ) { } } ;
struct exclusion_traverse_filter : public geom_filter { exclusion_traverse_filter ( ) : geom_filter ( false , true ) { } } ;
2016-12-16 13:51:07 +02:00
2017-05-19 18:10:20 +03:00
size_t read_filters_from_file ( const std : : string & , inclusion_filter & , inclusion_traverse_filter & , exclusion_filter & , exclusion_traverse_filter & ) ;
void parse_filter ( geom_filter & , const std : : vector < std : : string > & ) ;
2017-03-01 23:51:57 +02:00
std : : vector < IfcGeom : : filter_t > setup_filters ( const std : : vector < geom_filter > & , const std : : string & ) ;
2017-02-20 19:41:52 +02:00
2026-06-10 18:30:54 +02:00
bool init_input_file ( const std : : string & filename , IfcParse : : IfcFile * & ifc_file , bool no_progress , bool mmap , bool bypass_properties = false , Logger & logger = Logger : : Root ( ) ) ;
2017-06-15 22:07:56 +03:00
2020-08-16 13:37:58 +02:00
// from https://stackoverflow.com/questions/31696328/boost-program-options-using-zero-parameter-options-multiple-times
struct verbosity_counter {
2020-08-17 18:02:59 +02:00
int count ;
verbosity_counter ( int c = 0 ) {
count = c ;
}
2020-08-16 13:37:58 +02:00
} ;
2019-02-08 15:18:55 +01:00
# if defined(_MSC_VER) && defined(_UNICODE)
int wmain ( int argc , wchar_t * * argv ) {
typedef po : : wcommand_line_parser command_line_parser ;
typedef wchar_t char_t ;
_setmode ( _fileno ( stdout ) , _O_U16TEXT ) ;
_setmode ( _fileno ( stderr ) , _O_U16TEXT ) ;
# else
int main ( int argc , char * * argv ) {
typedef po : : command_line_parser command_line_parser ;
typedef char char_t ;
# endif
2026-06-10 18:30:54 +02:00
Logger logger ;
2019-02-08 15:18:55 +01:00
inclusion_filter include_filter ;
inclusion_traverse_filter include_traverse_filter ;
exclusion_filter exclude_filter ;
exclusion_traverse_filter exclude_traverse_filter ;
path_t filter_filename ;
path_t default_material_filename ;
2020-08-16 16:17:38 +02:00
path_t log_file ;
2021-10-08 15:38:46 +02:00
path_t cache_file ;
2018-05-12 20:12:25 +02:00
std : : string log_format ;
2023-03-21 20:15:01 +01:00
std : : string geometry_kernel ;
2019-02-08 15:18:55 +01:00
2016-12-16 13:51:07 +02:00
po : : options_description generic_options ( " Command line options " ) ;
2020-08-16 13:37:58 +02:00
verbosity_counter vcounter ;
2013-03-24 10:48:39 +00:00
generic_options . add_options ( )
2016-03-07 22:37:36 +02:00
( " help,h " , " display usage information " )
2013-03-24 10:48:39 +00:00
( " version " , " display version information " )
2021-11-26 11:33:00 -03:00
( " verbose,v " , po : : value ( & vcounter ) - > zero_tokens ( ) , " more verbose log messages. Use twice (-vv) for debugging level. " )
2018-05-12 20:12:25 +02:00
( " quiet,q " , " less status and progress output " )
2021-10-08 15:38:46 +02:00
# ifdef WITH_HDF5
( " cache " , " cache geometry creation. Use --cache-file to specify cache file path. " )
# endif
2018-08-13 12:54:52 +02:00
( " stderr-progress " , " output progress to stderr stream " )
2018-05-12 20:12:25 +02:00
( " yes,y " , " answer 'yes' automatically to possible confirmation queries (e.g. overwriting an existing output file) " )
( " no-progress " , " suppress possible progress bar type of prints that use carriage return " )
2026-07-10 20:38:15 +03:00
( " fail-on-error " , " return a non-zero exit code when one or more errors were logged during "
" geometry conversion (e.g. an element failed to convert). By default IfcConvert exits "
" successfully as long as an output file could be written, even if some elements were "
" silently dropped. Enable this flag so scripts and CI can detect partial conversions. " )
2020-08-16 16:17:38 +02:00
( " log-format " , po : : value < std : : string > ( & log_format ) , " log format: plain or json " )
( " log-file " , new po : : typed_value < path_t , char_t > ( & log_file ) , " redirect log output to file " ) ;
2013-03-24 10:48:39 +00:00
2016-12-16 13:51:07 +02:00
po : : options_description fileio_options ;
2013-03-24 10:48:39 +00:00
fileio_options . add_options ( )
2017-07-23 14:52:04 +02:00
# ifdef USE_MMAP
( " mmap " , " use memory-mapped file for input " )
# endif
2019-02-08 15:18:55 +01:00
( " input-file " , new po : : typed_value < path_t , char_t > ( 0 ) , " input IFC file " )
2021-10-08 15:38:46 +02:00
( " output-file " , new po : : typed_value < path_t , char_t > ( 0 ) , " output geometry file " )
# ifdef WITH_HDF5
( " cache-file " , new po : : typed_value < path_t , char_t > ( & cache_file ) , " geometry cache file " )
# endif
2025-08-25 12:45:10 +02:00
( " stream " , " Use streaming conversion (currently supported with conversion to RocksDB) " )
2021-10-08 15:38:46 +02:00
;
2018-11-02 16:10:06 +01:00
po : : options_description ifc_options ( " IFC options " ) ;
ifc_options . add_options ( )
( " calculate-quantities " , " Calculate or fix the physical quantity definitions "
" based on an interpretation of the geometry when exporting IFC " ) ;
2019-07-03 12:10:27 +02:00
2019-07-19 15:55:22 +02:00
int num_threads ;
2020-02-13 19:23:28 +01:00
std : : string offset_str , rotation_str ;
2023-03-21 20:15:01 +01:00
std : : string default_kernel ;
# ifdef IFOPSH_WITH_CGAL
default_kernel = " cgal " ;
# endif
# ifdef IFOPSH_WITH_OPENCASCADE
default_kernel = " opencascade " ;
# endif
2023-10-06 13:27:47 +02:00
// none, convex-decomposition, minkowski-triangles or halfspace-snapping
std : : string exterior_only_algo ;
2023-11-15 10:32:20 +01:00
ifcopenshell : : geometry : : Settings geometry_settings ;
2018-11-02 16:10:06 +01:00
po : : options_description geom_options ( " Geometry options " ) ;
2013-03-24 10:48:39 +00:00
geom_options . add_options ( )
2023-03-21 20:15:01 +01:00
( " kernel " , po : : value < std : : string > ( & geometry_kernel ) - > default_value ( default_kernel ) ,
2025-03-28 20:43:17 +01:00
" Geometry kernel to use (opencascade, cgal, cgal-simple, hybrid-cgal-simple-opencascade). " )
2019-08-02 13:27:33 +02:00
( " threads,j " , po : : value < int > ( & num_threads ) - > default_value ( 1 ) ,
2019-07-03 12:10:27 +02:00
" Number of parallel processing threads for geometry interpretation. " )
2020-02-13 19:23:28 +01:00
( " center-model " ,
" Centers the elements by applying the center point of all placements as an offset. "
" Can take several minutes on large models. " )
2020-02-12 11:38:08 +01:00
( " center-model-geometry " ,
" Centers the elements by applying the center point of all mesh vertices as an offset. " )
2025-03-19 11:42:11 +01:00
( " model-offset " , po : : value < std : : string > ( & offset_str ) ,
" Applies an arbitrary offset of form 'x;y;z' to all placements. " )
( " model-rotation " , po : : value < std : : string > ( & rotation_str ) ,
" Applies an arbitrary quaternion rotation of form 'x;y;z;w' to all placements. " )
2017-12-20 12:40:15 +01:00
( " include " , po : : value < inclusion_filter > ( & include_filter ) - > multitoken ( ) ,
2018-09-07 14:08:56 +02:00
" Specifies that the instances that match a specific filtering criteria are to be included in the geometrical output: \n "
2017-12-20 12:40:15 +01:00
" 1) 'entities': the following list of types should be included. SVG output defaults "
" to IfcSpace to be included. The entity names are handled case-insensitively. \n "
2018-09-07 14:08:56 +02:00
" 2) 'layers': the instances that are assigned to presentation layers of which names "
2017-12-20 12:40:15 +01:00
" match the given values should be included. \n "
2018-09-07 14:08:56 +02:00
" 3) 'attribute <AttributeName>': products whose value for <AttributeName> should be included \n . "
2017-12-20 12:40:15 +01:00
" Currently supported arguments are GlobalId, Name, Description, and Tag. \n \n "
" The values for 'layers' and 'arg' are handled case-sensitively (wildcards supported). "
" --include and --exclude cannot be placed right before input file argument and "
" only single of each argument supported for now. See also --exclude. " )
( " include+ " , po : : value < inclusion_traverse_filter > ( & include_traverse_filter ) - > multitoken ( ) ,
" Same as --include but applies filtering also to the decomposition and/or containment (IsDecomposedBy, "
" HasOpenings, FillsVoid, ContainedInStructure) of the filtered entity, e.g. --include+=arg Name \" Level 1 \" "
" includes entity with name \" Level 1 \" and all of its children. See --include for more information. " )
( " exclude " , po : : value < exclusion_filter > ( & exclude_filter ) - > multitoken ( ) ,
" Specifies that the entities that match a specific filtering criteria are to be excluded in the geometrical output. "
" See --include for syntax and more details. The default value is '--exclude=entities IfcOpeningElement IfcSpace'. " )
( " exclude+ " , po : : value < exclusion_traverse_filter > ( & exclude_traverse_filter ) - > multitoken ( ) ,
" Same as --exclude but applies filtering also to the decomposition and/or containment "
" of the filtered entity. See --include+ for more details. " )
2019-03-08 11:40:16 +01:00
( " filter-file " , new po : : typed_value < path_t , char_t > ( & filter_filename ) ,
2017-12-20 12:40:15 +01:00
" Specifies a filter file that describes the used filtering criteria. Supported formats "
2018-09-07 14:42:15 +02:00
" are '--include=arg GlobalId ...' and 'include arg GlobalId ...'. Spaces and tabs can be used as delimiters. "
2017-12-20 12:40:15 +01:00
" Multiple filters of same type with different values can be inserted on their own lines. "
2018-09-07 14:08:56 +02:00
" See --include, --include+, --exclude, and --exclude+ for more details. " )
2019-02-08 15:18:55 +01:00
( " default-material-file " , new po : : typed_value < path_t , char_t > ( & default_material_filename ) ,
2018-09-20 15:58:24 +02:00
" Specifies a material file that describes the material object types will have "
2018-12-10 11:33:54 +01:00
" if an object does not have any specified material in the IFC file. " )
2023-10-06 13:27:47 +02:00
( " exterior-only " ,
po : : value < std : : string > ( & exterior_only_algo ) - > default_value ( " none " ) - > implicit_value ( " minkowski-triangles " ) ,
" Export only the exterior shell of the building found by geometric analysis. convex-decomposition, minkowski-triangles or halfspace-snapping " )
2024-04-18 21:04:02 +02:00
( " plan " , " Specifies whether to include curves in the output result. Typically "
" these are representations of type Plan or Axis. Excluded by default. " )
( " model " , " Specifies whether to include surfaces and solids in the output result. "
" Typically these are representations of type Body or Facetation. " )
2023-11-15 10:32:20 +01:00
;
geometry_settings . define_options ( geom_options ) ;
2016-03-07 22:37:36 +02:00
2020-02-13 19:23:28 +01:00
std : : string bounds ;
2017-01-13 12:10:50 +02:00
# ifdef HAVE_ICU
std : : string unicode_mode ;
# endif
2017-01-11 16:36:33 +02:00
short precision ;
2017-10-10 20:42:38 +02:00
double section_height ;
2020-09-27 14:29:27 +02:00
std : : string svg_scale , svg_center ;
2021-08-08 18:06:58 +02:00
std : : string section_ref , elevation_ref , elevation_ref_guid ;
2021-03-09 16:11:42 +01:00
// "none", "full" or "left"
std : : string storey_height_display ;
2023-03-22 11:17:14 +01:00
# ifdef IFOPSH_WITH_OPENCASCADE
2021-03-09 16:11:42 +01:00
SvgSerializer : : storey_height_display_types svg_storey_height_display = SvgSerializer : : SH_NONE ;
2023-03-22 11:17:14 +01:00
# endif
2020-07-11 13:39:09 +02:00
2023-11-15 10:32:20 +01:00
ifcopenshell : : geometry : : SerializerSettings serializer_settings ;
2016-12-16 13:51:07 +02:00
po : : options_description serializer_options ( " Serialization options " ) ;
2016-03-07 22:37:36 +02:00
serializer_options . add_options ( )
2017-01-13 12:10:50 +02:00
# ifdef HAVE_ICU
2016-12-16 13:51:07 +02:00
( " unicode " , po : : value < std : : string > ( & unicode_mode ) ,
2017-01-13 12:10:50 +02:00
" Specifies the Unicode handling behavior when parsing the IFC file. "
" Accepted values 'utf8' (the default) and 'escape'. " )
# endif
2016-12-16 13:51:07 +02:00
( " bounds " , po : : value < std : : string > ( & bounds ) ,
2016-03-07 22:37:36 +02:00
" Specifies the bounding rectangle, for example 512x512, to which the "
" output will be scaled. Only used when converting to SVG. " )
2020-08-25 15:01:07 +02:00
( " scale " , po : : value < std : : string > ( & svg_scale ) ,
" Interprets SVG bounds in mm, centers layout and draw elements to scale. "
" Only used when converting to SVG. Example 1:100. " )
2020-09-27 14:29:27 +02:00
( " center " , po : : value < std : : string > ( & svg_center ) ,
" When using --scale, specifies the location in the range [0 1]x[0 1] around which "
" to center the drawings. Example 0.5x0.5 (default). " )
2020-09-20 15:08:52 +02:00
( " section-ref " , po : : value < std : : string > ( & section_ref ) ,
2021-08-08 18:06:58 +02:00
" Element at which cross sections should be created " )
2020-09-20 15:08:52 +02:00
( " elevation-ref " , po : : value < std : : string > ( & elevation_ref ) ,
2021-08-08 18:06:58 +02:00
" Element at which drawings should be created " )
( " elevation-ref-guid " , po : : value < std : : string > ( & elevation_ref_guid ) ,
" Element guids at which drawings should be created " )
2020-12-30 14:25:08 +01:00
( " auto-section " ,
" Creates SVG cross section drawings automatically based on model extents " )
( " auto-elevation " ,
" Creates SVG elevation drawings automatically based on model extents " )
2021-03-05 21:50:32 +01:00
( " draw-storey-heights " ,
2021-03-09 16:11:42 +01:00
po : : value < std : : string > ( & storey_height_display ) - > default_value ( " none " ) - > implicit_value ( " full " ) ,
2021-03-05 21:50:32 +01:00
" Draws a horizontal line at the height of building storeys in vertical drawings " )
2021-03-28 21:38:29 +02:00
( " storey-height-line-length " , po : : value < double > ( ) ,
" Length of the line when --draw-storey-heights=left " )
2020-12-30 14:44:43 +01:00
( " svg-xmlns " ,
" Stores name and guid in a separate namespace as opposed to data-name, data-guid " )
2021-01-02 12:34:10 +01:00
( " svg-poly " ,
" Uses the polygonal algorithm for hidden line rendering " )
2023-06-29 13:08:22 +02:00
( " svg-prefilter " ,
" Prefilter faces and shapes before feeding to HLR algorithm " )
2023-08-30 10:57:33 +02:00
( " svg-segment-projection " ,
" Segment result of projection wrt original products " )
2021-04-15 10:36:37 +02:00
( " svg-write-poly " ,
" Approximate every curve as polygonal in SVG output " )
2021-01-27 22:34:19 +01:00
( " svg-project " ,
" Always enable hidden line rendering instead of only on elevations " )
2021-04-18 15:03:28 +02:00
( " svg-without-storeys " , " Don't emit drawings for building storeys " )
2021-08-08 20:40:15 +02:00
( " svg-no-css " , " Don't emit CSS style declarations " )
2020-07-11 23:26:21 +02:00
( " door-arcs " , " Draw door openings arcs for IfcDoor elements " )
2017-10-10 20:42:38 +02:00
( " section-height " , po : : value < double > ( & section_height ) ,
" Specifies the cut section height for SVG 2D geometry. " )
2021-11-15 20:15:55 +00:00
( " section-height-from-storeys " , " Derives section height from storey elevation. Use --section-height to override default offset of 1.2 " )
2020-05-10 11:10:30 +02:00
( " print-space-names " , " Prints IfcSpace LongName and Name in the geometry output. Applicable for SVG output " )
2020-06-15 16:24:16 +02:00
( " print-space-areas " , " Prints calculated IfcSpace areas in square meters. Applicable for SVG output " )
2021-03-28 22:16:58 +02:00
( " space-name-transform " , po : : value < std : : string > ( ) ,
" Additional transform to the space labels in SVG " )
2020-06-15 16:24:16 +02:00
;
2016-03-07 22:37:36 +02:00
2023-11-15 10:32:20 +01:00
serializer_settings . define_options ( serializer_options ) ;
2016-12-16 13:51:07 +02:00
po : : options_description cmdline_options ;
2018-11-02 16:24:32 +01:00
cmdline_options . add ( generic_options ) . add ( fileio_options ) . add ( geom_options ) . add ( ifc_options ) . add ( serializer_options ) ;
2013-03-24 10:48:39 +00:00
2016-12-16 13:51:07 +02:00
po : : positional_options_description positional_options ;
2013-03-24 10:48:39 +00:00
positional_options . add ( " input-file " , 1 ) ;
positional_options . add ( " output-file " , 1 ) ;
2016-12-16 13:51:07 +02:00
po : : variables_map vmap ;
try {
2019-02-08 15:18:55 +01:00
po : : store ( command_line_parser ( argc , argv ) .
2016-12-16 13:51:07 +02:00
options ( cmdline_options ) . positional ( positional_options ) . run ( ) , vmap ) ;
} catch ( const po : : unknown_option & e ) {
2019-02-08 15:18:55 +01:00
cerr_ < < " [Error] Unknown option ' " < < e . get_option_name ( ) . c_str ( ) < < " ' \n \n " ;
2016-03-07 22:37:36 +02:00
print_usage ( ) ;
2016-12-16 13:51:07 +02:00
return EXIT_FAILURE ;
} catch ( const po : : error_with_option_name & e ) {
2019-02-08 15:18:55 +01:00
cerr_ < < " [Error] Invalid usage of ' " < < e . get_option_name ( ) . c_str ( ) < < " ': " < < e . what ( ) < < " \n \n " ;
2016-12-16 13:51:07 +02:00
return EXIT_FAILURE ;
2017-01-11 16:36:33 +02:00
} catch ( const std : : exception & e ) {
2019-02-08 15:18:55 +01:00
cerr_ < < " [Error] " < < e . what ( ) < < " \n \n " ;
2017-01-11 16:36:33 +02:00
print_usage ( ) ;
2016-12-16 13:51:07 +02:00
return EXIT_FAILURE ;
2017-02-20 19:41:52 +02:00
} catch ( . . . ) {
2019-02-08 15:18:55 +01:00
cerr_ < < " [Error] Unknown error parsing command line options \n \n " ;
2016-03-07 22:37:36 +02:00
print_usage ( ) ;
2017-02-02 14:58:58 +02:00
return EXIT_FAILURE ;
2017-02-22 17:39:32 +02:00
}
2016-12-16 13:51:07 +02:00
po : : notify ( vmap ) ;
2013-03-24 10:48:39 +00:00
2017-07-23 14:52:04 +02:00
const bool mmap = vmap . count ( " mmap " ) ! = 0 ;
2017-05-23 20:06:35 +03:00
const bool no_progress = vmap . count ( " no-progress " ) ! = 0 ;
2026-07-10 20:38:15 +03:00
const bool fail_on_error = vmap . count ( " fail-on-error " ) ! = 0 ;
2018-05-12 20:12:25 +02:00
const bool quiet = vmap . count ( " quiet " ) ! = 0 ;
2018-08-13 12:54:52 +02:00
const bool stderr_progress = vmap . count ( " stderr-progress " ) ! = 0 ;
2023-11-15 10:32:20 +01:00
2018-05-12 20:12:25 +02:00
const bool center_model = vmap . count ( " center-model " ) ! = 0 ;
2020-02-12 11:38:08 +01:00
const bool center_model_geometry = vmap . count ( " center-model-geometry " ) ! = 0 ;
2025-03-19 11:42:11 +01:00
const bool model_offset = vmap . count ( " model-offset " ) ! = 0 ;
const bool model_rotation = vmap . count ( " model-rotation " ) ! = 0 ;
2018-05-12 20:12:25 +02:00
2019-02-08 15:18:55 +01:00
if ( ! quiet | | vmap . count ( " version " ) ) {
2018-05-12 20:12:25 +02:00
print_version ( ) ;
}
2019-02-08 15:18:55 +01:00
if ( vmap . count ( " version " ) ) {
2018-05-12 20:12:25 +02:00
return EXIT_SUCCESS ;
} else if ( vmap . count ( " help " ) ) {
print_usage ( false ) ;
print_options ( generic_options . add ( geom_options ) . add ( serializer_options ) ) ;
return EXIT_SUCCESS ;
} else if ( ! vmap . count ( " input-file " ) ) {
2020-07-21 23:23:39 +02:00
cerr_ < < " [Error] Input file not specified " < < std : : endl ;
2018-05-12 20:12:25 +02:00
print_usage ( ) ;
return EXIT_FAILURE ;
}
2021-03-09 16:11:42 +01:00
2023-03-22 11:17:14 +01:00
# ifdef IFOPSH_WITH_OPENCASCADE
2021-03-09 16:11:42 +01:00
if ( vmap . count ( " draw-storey-heights " ) ) {
boost : : to_lower ( storey_height_display ) ;
if ( storey_height_display = = " none " ) {
svg_storey_height_display = SvgSerializer : : SH_NONE ;
} else if ( storey_height_display = = " full " ) {
svg_storey_height_display = SvgSerializer : : SH_FULL ;
} else if ( storey_height_display = = " left " ) {
svg_storey_height_display = SvgSerializer : : SH_LEFT ;
} else {
cerr_ < < " [Error] --draw-storey-heights should be none|full|left " < < std : : endl ;
print_usage ( ) ;
return EXIT_FAILURE ;
}
}
2023-03-22 11:17:14 +01:00
# endif
2023-09-13 22:02:52 +02:00
if ( num_threads < = 0 ) {
num_threads = std : : thread : : hardware_concurrency ( ) ;
2026-06-10 18:30:54 +02:00
logger . Notice ( " SYS " , 7 , " Using " + std : : to_string ( num_threads ) + " threads " ) ;
2023-09-13 22:02:52 +02:00
}
2019-02-08 15:18:55 +01:00
if ( vmap . count ( " log-format " ) = = 1 ) {
boost : : to_lower ( log_format ) ;
if ( log_format = = " plain " ) {
2026-06-10 18:30:54 +02:00
logger . OutputFormat ( Logger : : FMT_PLAIN ) ;
2019-02-08 15:18:55 +01:00
} else if ( log_format = = " json " ) {
2026-06-10 18:30:54 +02:00
logger . OutputFormat ( Logger : : FMT_JSON ) ;
2019-02-08 15:18:55 +01:00
} else {
2020-07-21 23:23:39 +02:00
cerr_ < < " [Error] --log-format should be either plain or json " < < std : : endl ;
2019-02-08 15:18:55 +01:00
print_usage ( ) ;
return EXIT_FAILURE ;
}
}
if ( ! filter_filename . empty ( ) ) {
size_t num_filters = read_filters_from_file ( IfcUtil : : path : : to_utf8 ( filter_filename ) , include_filter , include_traverse_filter , exclude_filter , exclude_traverse_filter ) ;
if ( num_filters ) {
2026-06-10 18:30:54 +02:00
logger . Notice ( " SYS " , 8 , boost : : lexical_cast < std : : string > ( num_filters ) + " filters read from specifified file. " ) ;
2019-02-08 15:18:55 +01:00
} else {
2020-07-21 23:23:39 +02:00
cerr_ < < " [Error] No filters read from specifified file. \n " ;
2019-02-08 15:18:55 +01:00
return EXIT_FAILURE ;
}
}
2015-10-07 17:33:30 +02:00
2017-01-13 12:10:50 +02:00
# ifdef HAVE_ICU
if ( ! unicode_mode . empty ( ) ) {
if ( unicode_mode = = " utf8 " ) {
IfcParse : : IfcCharacterDecoder : : mode = IfcParse : : IfcCharacterDecoder : : UTF8 ;
} else if ( unicode_mode = = " escape " ) {
IfcParse : : IfcCharacterDecoder : : mode = IfcParse : : IfcCharacterDecoder : : JSON ;
} else {
2019-02-08 15:18:55 +01:00
cerr_ < < " [Error] Invalid value for --unicode " < < std : : endl ;
2017-01-13 12:10:50 +02:00
print_options ( serializer_options ) ;
return 1 ;
}
}
# endif
2019-02-08 15:18:55 +01:00
if ( ! default_material_filename . empty ( ) ) {
try {
IfcGeom : : set_default_style_file ( IfcUtil : : path : : to_utf8 ( default_material_filename ) ) ;
} catch ( const std : : exception & e ) {
2020-07-21 23:23:39 +02:00
cerr_ < < " [Error] Could not read default material file: " < < std : : endl ;
cerr_ < < e . what ( ) < < std : : endl ;
2019-02-08 15:18:55 +01:00
return EXIT_FAILURE ;
}
}
2020-09-27 14:29:27 +02:00
boost : : optional < double > bounding_width , bounding_height , relative_center_x , relative_center_y ;
2015-07-26 14:26:13 +00:00
if ( vmap . count ( " bounds " ) = = 1 ) {
int w , h ;
2016-07-15 06:49:32 +03:00
if ( sscanf ( bounds . c_str ( ) , " %ux%u " , & w , & h ) = = 2 & & w > 0 & & h > 0 ) {
2015-07-26 14:26:13 +00:00
bounding_width = w ;
bounding_height = h ;
} else {
2019-02-08 15:18:55 +01:00
cerr_ < < " [Error] Invalid use of --bounds " < < std : : endl ;
2016-03-11 10:05:33 +02:00
print_options ( serializer_options ) ;
2017-02-20 19:41:52 +02:00
return EXIT_FAILURE ;
2015-07-26 14:26:13 +00:00
}
}
2013-03-24 10:48:39 +00:00
2020-09-27 14:29:27 +02:00
if ( vmap . count ( " center " ) = = 1 ) {
double cx , cy ;
if ( sscanf ( svg_center . c_str ( ) , " %lfx%lf " , & cx , & cy ) = = 2 & & cx > = 0. & & cy > = 0. & & cx < = 1. & & cy < = 1. ) {
relative_center_x = cx ;
relative_center_y = cy ;
} else {
cerr_ < < " [Error] Invalid use of --bounds " < < std : : endl ;
print_options ( serializer_options ) ;
return EXIT_FAILURE ;
}
}
2019-02-08 15:18:55 +01:00
const path_t input_filename = vmap [ " input-file " ] . as < path_t > ( ) ;
2025-02-27 22:07:31 +01:00
/*
// todo also allow rocksdb dir
if (!file_exists(IfcUtil::path::to_utf8(input_filename))) {
2019-02-08 15:18:55 +01:00
cerr_ << "[Error] Input file '" << input_filename << "' does not exist" << std::endl;
2017-02-20 19:41:52 +02:00
return EXIT_FAILURE;
2025-02-27 22:07:31 +01:00
}*/
2016-03-18 22:33:03 +02:00
2013-03-24 10:48:39 +00:00
// If no output filename is specified a Wavefront OBJ file will be output
// to maintain backwards compatibility with the obsolete IfcObj executable.
2019-02-08 15:18:55 +01:00
const path_t output_filename = vmap . count ( " output-file " ) = = 1
? vmap [ " output-file " ] . as < path_t > ( )
: change_extension ( input_filename , IfcUtil : : path : : from_utf8 ( DEFAULT_EXTENSION ) ) ;
2013-03-24 10:48:39 +00:00
if ( output_filename . size ( ) < 5 ) {
2019-02-08 15:18:55 +01:00
cerr_ < < " [Error] Invalid or unsupported output file ' " < < output_filename < < " ' given " < < std : : endl ;
2016-03-07 22:37:36 +02:00
print_usage ( ) ;
2017-02-20 19:41:52 +02:00
return EXIT_FAILURE ;
2013-03-24 10:48:39 +00:00
}
2019-02-08 15:18:55 +01:00
if ( file_exists ( IfcUtil : : path : : to_utf8 ( output_filename ) ) & & ! vmap . count ( " yes " ) ) {
2016-03-18 22:14:05 +02:00
std : : string answer ;
2025-08-12 17:30:35 +05:00
cout_ < < " A file ' " < < output_filename < < " ' already exists. Overwrite the existing file? y/n " < < std : : endl ;
2016-03-18 22:14:05 +02:00
std : : cin > > answer ;
if ( ! boost : : iequals ( answer , " yes " ) & & ! boost : : iequals ( answer , " y " ) ) {
2017-02-20 19:41:52 +02:00
return EXIT_SUCCESS ;
2016-03-18 22:14:05 +02:00
}
}
2020-08-16 16:17:38 +02:00
ofstream_t log_fs ;
if ( vmap . count ( " log-file " ) ) {
log_fs . open ( log_file . c_str ( ) , std : : ios : : app ) ;
2026-06-10 18:30:54 +02:00
logger . SetOutput ( quiet ? nullptr : & cout_ , & log_fs ) ;
2020-08-16 16:17:38 +02:00
} else {
2026-06-10 18:30:54 +02:00
logger . SetOutput ( quiet ? nullptr : & cout_ , vcounter . count > 1 ? & cout_ : & log_stream ) ;
2020-08-16 16:17:38 +02:00
}
2022-01-07 15:12:32 +01:00
switch ( vcounter . count ) {
case 0 :
2026-06-10 18:30:54 +02:00
logger . Verbosity ( Logger : : LOG_ERROR ) ;
2022-01-07 15:12:32 +01:00
break ;
case 1 :
2026-06-10 18:30:54 +02:00
logger . Verbosity ( Logger : : LOG_NOTICE ) ;
2022-01-07 15:12:32 +01:00
break ;
case 2 :
2026-06-10 18:30:54 +02:00
logger . Verbosity ( Logger : : LOG_DEBUG ) ;
2022-01-07 15:12:32 +01:00
break ;
case 3 :
2026-06-10 18:30:54 +02:00
logger . Verbosity ( Logger : : LOG_PERF ) ;
2022-01-07 15:12:32 +01:00
break ;
case 4 :
2026-06-10 18:30:54 +02:00
logger . Verbosity ( Logger : : LOG_PERF ) ;
logger . PrintPerformanceStatsOnElement ( true ) ;
2022-01-07 15:12:32 +01:00
break ;
}
2015-02-06 13:07:50 +00:00
2019-02-08 15:18:55 +01:00
path_t output_temp_filename = output_filename + IfcUtil : : path : : from_utf8 ( TEMP_FILE_EXTENSION ) ;
2021-02-17 17:20:03 +01:00
2021-02-20 15:35:33 +01:00
std : : vector < path_t > tokens ;
2021-02-17 17:20:03 +01:00
split ( tokens , output_filename , boost : : is_any_of ( " . " ) ) ;
2023-05-17 16:18:50 +02:00
std : : vector < path_t > : : iterator tok_iter ;
2021-02-20 15:35:33 +01:00
path_t ext = * ( tokens . end ( ) - 1 ) ;
path_t dot ;
2021-02-17 17:20:03 +01:00
dot = ' . ' ;
path_t output_extension = dot + ext ;
2017-02-03 19:15:37 +02:00
2019-02-08 15:18:55 +01:00
boost : : to_lower ( output_extension ) ;
2018-05-12 20:12:25 +02:00
2017-12-20 16:38:55 +01:00
IfcParse : : IfcFile * ifc_file = 0 ;
2023-09-13 22:02:52 +02:00
boost : : optional < std : : list < IfcGeom : : Element * > > elems_from_adaptor ;
2019-03-08 11:40:16 +01:00
const path_t OBJ = IfcUtil : : path : : from_utf8 ( " .obj " ) ,
2019-02-08 15:18:55 +01:00
MTL = IfcUtil : : path : : from_utf8 ( " .mtl " ) ,
DAE = IfcUtil : : path : : from_utf8 ( " .dae " ) ,
2019-05-07 17:06:45 +02:00
GLB = IfcUtil : : path : : from_utf8 ( " .glb " ) ,
2019-02-08 15:18:55 +01:00
STP = IfcUtil : : path : : from_utf8 ( " .stp " ) ,
IGS = IfcUtil : : path : : from_utf8 ( " .igs " ) ,
SVG = IfcUtil : : path : : from_utf8 ( " .svg " ) ,
2021-10-08 15:38:46 +02:00
CACHE = IfcUtil : : path : : from_utf8 ( " .cache " ) ,
2021-02-17 17:20:03 +01:00
HDF = IfcUtil : : path : : from_utf8 ( " .h5 " ) ,
2019-03-08 17:22:05 +01:00
XML = IfcUtil : : path : : from_utf8 ( " .xml " ) ,
2025-11-17 14:06:39 +01:00
JSON = IfcUtil : : path : : from_utf8 ( " .json " ) ,
// @todo this is just temporary as it doesn't make sense to require an extension for a DB
2025-02-25 21:29:54 +01:00
RDB = IfcUtil : : path : : from_utf8 ( " .rdb " ) ,
2023-05-07 11:45:47 +02:00
IFC = IfcUtil : : path : : from_utf8 ( " .ifc " ) ,
2023-05-07 22:46:05 +02:00
USD = IfcUtil : : path : : from_utf8 ( " .usd " ) ,
2023-05-17 14:24:09 +02:00
USDA = IfcUtil : : path : : from_utf8 ( " .usda " ) ,
2024-09-27 16:32:41 +02:00
USDC = IfcUtil : : path : : from_utf8 ( " .usdc " ) ,
TTL = IfcUtil : : path : : from_utf8 ( " .ttl " ) ;
2017-03-02 14:55:21 +02:00
2018-11-02 16:10:06 +01:00
// @todo clean up serializer selection
// @todo detect program options that conflict with the chosen serializer
2025-11-17 14:06:39 +01:00
if ( output_extension = = XML | | output_extension = = JSON ) {
2018-11-02 16:10:06 +01:00
int exit_code = EXIT_FAILURE ;
try {
2026-06-10 18:30:54 +02:00
if ( init_input_file ( IfcUtil : : path : : to_utf8 ( input_filename ) , ifc_file , no_progress | | quiet , mmap , false , logger ) ) {
2019-02-06 15:39:43 +01:00
time_t start , end ;
time ( & start ) ;
2025-11-17 14:06:39 +01:00
if ( output_extension = = XML ) {
2026-06-10 18:30:54 +02:00
XmlSerializer s ( ifc_file , IfcUtil : : path : : to_utf8 ( output_temp_filename ) , logger ) ;
logger . Status ( " Writing XML output... " ) ;
2025-11-17 14:06:39 +01:00
s . finalize ( ) ;
} else {
# ifdef WITH_GLTF
2026-06-10 18:30:54 +02:00
JsonSerializer s ( ifc_file , IfcUtil : : path : : to_utf8 ( output_temp_filename ) , JsonSerializer : : JSON_DIALECT_CREOOX , logger ) ;
logger . Status ( " Writing JSON output... " ) ;
2025-11-17 14:06:39 +01:00
s . finalize ( ) ;
# endif
}
2019-02-06 15:39:43 +01:00
time ( & end ) ;
2026-06-10 18:30:54 +02:00
logger . Status ( " Done! Conversion took " + format_duration ( start , end ) ) ;
2019-02-06 15:39:43 +01:00
2019-03-08 11:40:16 +01:00
IfcUtil : : path : : rename_file ( IfcUtil : : path : : to_utf8 ( output_temp_filename ) , IfcUtil : : path : : to_utf8 ( output_filename ) ) ;
2018-11-02 16:10:06 +01:00
exit_code = EXIT_SUCCESS ;
}
} catch ( const std : : exception & e ) {
2026-06-10 18:30:54 +02:00
logger . Error ( " SYS " , 9 , e ) ;
2018-03-16 16:04:48 +01:00
}
2018-11-02 16:10:06 +01:00
write_log ( ! quiet ) ;
return exit_code ;
2019-03-08 11:40:16 +01:00
} else if ( output_extension = = IFC ) {
2018-11-02 16:10:06 +01:00
int exit_code = EXIT_FAILURE ;
try {
2026-06-10 18:30:54 +02:00
if ( init_input_file ( IfcUtil : : path : : to_utf8 ( input_filename ) , ifc_file , no_progress | | quiet , mmap , false , logger ) ) {
2018-10-01 13:19:31 +03:00
time_t start , end ;
2019-03-08 11:40:16 +01:00
time ( & start ) ;
2018-11-02 16:10:06 +01:00
std : : ofstream fs ( output_filename . c_str ( ) ) ;
if ( fs . is_open ( ) ) {
if ( vmap . count ( " calculate-quantities " ) ) {
2026-06-10 18:30:54 +02:00
fix_quantities ( * ifc_file , no_progress , quiet , stderr_progress , logger ) ;
2018-11-02 16:10:06 +01:00
}
fs < < * ifc_file ;
2018-12-10 11:33:54 +01:00
exit_code = EXIT_SUCCESS ;
2018-11-02 16:10:06 +01:00
} else {
2026-06-10 18:30:54 +02:00
logger . Error ( " SYS " , 10 , " Unable to open output file for writing " ) ;
2018-11-02 16:10:06 +01:00
}
2018-10-01 13:19:31 +03:00
time ( & end ) ;
2026-06-10 18:30:54 +02:00
logger . Status ( " Done! Writing IFC took " + format_duration ( start , end ) ) ;
2018-11-02 16:10:06 +01:00
}
} catch ( const std : : exception & e ) {
2026-06-10 18:30:54 +02:00
logger . Error ( " SYS " , 11 , e ) ;
2018-11-02 16:10:06 +01:00
}
write_log ( ! quiet ) ;
return exit_code ;
}
2025-02-25 21:29:54 +01:00
# ifdef WITH_ROCKSDB
else if ( output_extension = = RDB ) {
int exit_code = EXIT_FAILURE ;
try {
2025-08-25 12:45:10 +02:00
if ( vmap . count ( " stream " ) ) {
2025-02-25 21:29:54 +01:00
time_t start , end ;
time ( & start ) ;
2026-06-10 18:30:54 +02:00
RocksDbSerializer s ( IfcUtil : : path : : to_utf8 ( input_filename ) , IfcUtil : : path : : to_utf8 ( output_filename ) , true , logger ) ;
logger . Status ( " Populating RocksDB Key-Value store... " ) ;
2025-02-25 21:29:54 +01:00
s . finalize ( ) ;
time ( & end ) ;
2026-06-10 18:30:54 +02:00
logger . Status ( " Done! Conversion took " + format_duration ( start , end ) ) ;
2025-02-25 21:29:54 +01:00
exit_code = EXIT_SUCCESS ;
2025-08-25 12:45:10 +02:00
} else {
2026-06-10 18:30:54 +02:00
if ( init_input_file ( IfcUtil : : path : : to_utf8 ( input_filename ) , ifc_file , no_progress | | quiet , mmap , false , logger ) ) {
2025-08-25 12:45:10 +02:00
time_t start , end ;
time ( & start ) ;
2026-06-10 18:30:54 +02:00
RocksDbSerializer s ( ifc_file , IfcUtil : : path : : to_utf8 ( output_filename ) , logger ) ;
logger . Status ( " Populating RocksDB Key-Value store... " ) ;
2025-08-25 12:45:10 +02:00
s . finalize ( ) ;
time ( & end ) ;
2026-06-10 18:30:54 +02:00
logger . Status ( " Done! Conversion took " + format_duration ( start , end ) ) ;
2025-08-25 12:45:10 +02:00
exit_code = EXIT_SUCCESS ;
}
}
2025-02-25 21:29:54 +01:00
} catch ( const std : : exception & e ) {
2026-06-10 18:30:54 +02:00
logger . Error ( " SYS " , 12 , e ) ;
2025-02-25 21:29:54 +01:00
}
write_log ( ! quiet ) ;
return exit_code ;
}
# endif
2018-03-16 16:04:48 +01:00
2017-03-01 23:51:57 +02:00
/// @todo Clean up this filter code further.
std : : vector < geom_filter > used_filters ;
if ( include_filter . type ! = geom_filter : : UNUSED ) { used_filters . push_back ( include_filter ) ; }
if ( include_traverse_filter . type ! = geom_filter : : UNUSED ) { used_filters . push_back ( include_traverse_filter ) ; }
if ( exclude_filter . type ! = geom_filter : : UNUSED ) { used_filters . push_back ( exclude_filter ) ; }
if ( exclude_traverse_filter . type ! = geom_filter : : UNUSED ) { used_filters . push_back ( exclude_traverse_filter ) ; }
2019-02-08 15:18:55 +01:00
std : : vector < IfcGeom : : filter_t > filter_funcs = setup_filters ( used_filters , IfcUtil : : path : : to_utf8 ( output_extension ) ) ;
2017-03-01 23:51:57 +02:00
if ( filter_funcs . empty ( ) ) {
2019-02-08 15:18:55 +01:00
cerr_ < < " [Error] Failed to set up geometry filters \n " ;
2017-02-20 19:41:52 +02:00
return EXIT_FAILURE ;
2017-02-07 17:34:23 +02:00
}
2026-06-10 18:30:54 +02:00
if ( ! entity_filter . entity_names . empty ( ) ) { entity_filter . update_description ( ) ; logger . Notice ( " SYS " , 13 , entity_filter . description ) ; }
if ( ! layer_filter . values . empty ( ) ) { layer_filter . update_description ( ) ; logger . Notice ( " SYS " , 14 , layer_filter . description ) ; }
if ( ! attribute_filter . attribute_name . empty ( ) ) { attribute_filter . update_description ( ) ; logger . Notice ( " SYS " , 15 , attribute_filter . description ) ; }
2017-02-16 19:49:46 +02:00
2019-02-08 15:18:55 +01:00
# ifdef _MSC_VER
if ( output_extension = = DAE | | output_extension = = STP | | output_extension = = IGS ) {
2019-05-17 16:52:15 +02:00
# else
if ( output_extension = = DAE ) {
# endif
// These serializers do not support opening unicode paths. Therefore
2019-02-08 15:18:55 +01:00
// a random temp file is generated using only ASCII characters instead.
std : : random_device rng ;
2019-05-18 11:01:17 +02:00
std : : uniform_int_distribution < int > index_dist ( ' A ' , ' Z ' ) ;
{
std : : string v = " .ifcopenshell. " ;
2021-07-17 09:06:03 +02:00
output_temp_filename = path_t ( v . begin ( ) , v . end ( ) ) ;
2019-05-18 11:01:17 +02:00
}
2019-02-08 15:18:55 +01:00
for ( int i = 0 ; i < 8 ; + + i ) {
2019-05-18 11:01:17 +02:00
output_temp_filename . push_back ( static_cast < path_t : : value_type > ( index_dist ( rng ) ) ) ;
}
{
2020-08-19 12:56:43 +02:00
std : : string v = " .tmp " ;
2019-05-18 11:01:17 +02:00
output_temp_filename + = path_t ( v . begin ( ) , v . end ( ) ) ;
2019-02-08 15:18:55 +01:00
}
}
2025-05-18 22:14:22 +02:00
// The OS will clean up for us if there is a leak
geometry_settings . get < ifcopenshell : : geometry : : settings : : OcctNoCleanTriangulation > ( ) . value = true ;
if ( geometry_settings . get < ifcopenshell : : geometry : : settings : : PermissiveShapeReuse > ( ) . get ( ) ) {
geometry_settings . get < ifcopenshell : : geometry : : settings : : NoParallelMapping > ( ) . value = true ;
}
2025-11-17 14:05:59 +01:00
if ( geometry_settings . get < ifcopenshell : : geometry : : settings : : UseElementHierarchy > ( ) . get ( ) & & output_extension ! = DAE & & output_extension ! = USD & & output_extension ! = USDA & & output_extension ! = USDC & & output_extension ! = GLB ) {
cerr_ < < " [Error] --use-element-hierarchy can be used only with .dae or .usd or .glb output. \n " ;
2024-12-01 11:20:37 +01:00
/// @todo Lots of duplicate error-and-exit code.
write_log ( ! quiet ) ;
print_usage ( ) ;
IfcUtil : : path : : delete_file ( IfcUtil : : path : : to_utf8 ( output_temp_filename ) ) ;
return EXIT_FAILURE ;
}
2024-04-29 20:49:11 +02:00
if ( vmap [ ifcopenshell : : geometry : : settings : : WeldVertices : : name ] . defaulted ( ) ) {
2023-11-15 10:32:20 +01:00
geometry_settings . get < ifcopenshell : : geometry : : settings : : WeldVertices > ( ) . value = false ;
}
if ( geometry_settings . get < ifcopenshell : : geometry : : settings : : ForceSpaceTransparency > ( ) . has ( ) ) {
2024-06-10 21:30:26 +02:00
IfcGeom : : update_default_style ( " IfcSpace " ) - > transparency = geometry_settings . get < ifcopenshell : : geometry : : settings : : ForceSpaceTransparency > ( ) . get ( ) ;
2020-09-27 10:43:11 +02:00
}
2023-12-05 21:10:42 +01:00
if ( output_extension = = OBJ | | output_extension = = STP | | output_extension = = IGS ) {
geometry_settings . get < ifcopenshell : : geometry : : settings : : UseWorldCoords > ( ) . value = true ;
}
2024-09-27 16:32:41 +02:00
if ( output_extension = = TTL ) {
geometry_settings . get < ifcopenshell : : geometry : : settings : : TriangulationType > ( ) . value = ifcopenshell : : geometry : : settings : : TriangulationMethod : : POLYHEDRON_WITH_HOLES ;
}
2024-12-01 11:20:37 +01:00
if ( output_extension = = SVG ) {
// SVG serialiazation depends on element hierarchy now to look up the parent
geometry_settings . get < ifcopenshell : : geometry : : settings : : UseElementHierarchy > ( ) . value = true ;
}
2018-10-08 16:36:48 +03:00
boost : : shared_ptr < GeometrySerializer > serializer ; /**< @todo use std::unique_ptr when possible */
2019-02-08 15:18:55 +01:00
if ( output_extension = = OBJ ) {
2016-03-28 11:32:29 +03:00
// Do not use temp file for MTL as it's such a small file.
2019-02-08 15:18:55 +01:00
const path_t mtl_filename = change_extension ( output_filename , MTL ) ;
2026-06-10 18:30:54 +02:00
serializer = boost : : make_shared < WaveFrontOBJSerializer > ( IfcUtil : : path : : to_utf8 ( output_temp_filename ) , IfcUtil : : path : : to_utf8 ( mtl_filename ) , geometry_settings , serializer_settings , logger ) ;
2013-11-22 08:23:11 +00:00
# ifdef WITH_OPENCOLLADA
2019-02-08 15:18:55 +01:00
} else if ( output_extension = = DAE ) {
2026-06-10 18:30:54 +02:00
serializer = boost : : make_shared < ColladaSerializer > ( IfcUtil : : path : : to_utf8 ( output_temp_filename ) , geometry_settings , serializer_settings , logger ) ;
2019-05-07 17:06:45 +02:00
# endif
# ifdef WITH_GLTF
} else if ( output_extension = = GLB ) {
2026-06-10 18:30:54 +02:00
serializer = boost : : make_shared < GltfSerializer > ( IfcUtil : : path : : to_utf8 ( output_temp_filename ) , geometry_settings , serializer_settings , logger ) ;
2013-11-22 08:23:11 +00:00
# endif
2023-05-04 20:30:03 +02:00
# ifdef WITH_USD
2023-05-17 14:24:09 +02:00
} else if ( output_extension = = USD | | output_extension = = USDA | | output_extension = = USDC ) {
2026-06-10 18:30:54 +02:00
serializer = boost : : make_shared < USDSerializer > ( IfcUtil : : path : : to_utf8 ( output_filename ) , geometry_settings , serializer_settings , logger ) ;
2023-05-04 20:30:03 +02:00
# endif
2023-03-22 11:17:14 +01:00
# ifdef IFOPSH_WITH_OPENCASCADE
2019-02-08 15:18:55 +01:00
} else if ( output_extension = = STP ) {
2026-06-10 18:30:54 +02:00
serializer = boost : : make_shared < StepSerializer > ( IfcUtil : : path : : to_utf8 ( output_temp_filename ) , geometry_settings , serializer_settings , logger ) ;
2019-02-08 15:18:55 +01:00
} else if ( output_extension = = IGS ) {
2018-07-08 11:08:07 +02:00
# if OCC_VERSION_HEX < 0x60900
// According to https://tracker.dev.opencascade.org/view.php?id=25689 something has been fixed in 6.9.0
2016-03-10 16:46:20 +02:00
IGESControl_Controller : : Init ( ) ; // work around Open Cascade bug
2018-07-08 11:08:07 +02:00
# endif
2026-06-10 18:30:54 +02:00
serializer = boost : : make_shared < IgesSerializer > ( IfcUtil : : path : : to_utf8 ( output_temp_filename ) , geometry_settings , serializer_settings , logger ) ;
2019-02-08 15:18:55 +01:00
} else if ( output_extension = = SVG ) {
2023-11-15 10:32:20 +01:00
geometry_settings . get < ifcopenshell : : geometry : : settings : : IteratorOutput > ( ) . value = ifcopenshell : : geometry : : settings : : NATIVE ;
2026-06-10 18:30:54 +02:00
serializer = boost : : make_shared < SvgSerializer > ( IfcUtil : : path : : to_utf8 ( output_temp_filename ) , geometry_settings , serializer_settings , logger ) ;
2021-08-31 11:03:28 +02:00
# ifdef WITH_HDF5
2023-03-22 11:17:14 +01:00
} else if ( output_extension = = HDF ) {
2023-11-15 10:32:20 +01:00
geometry_settings . get < ifcopenshell : : geometry : : settings : : IteratorOutput > ( ) . value = ifcopenshell : : geometry : : settings : : NATIVE ;
2026-06-10 18:30:54 +02:00
serializer = boost : : make_shared < HdfSerializer > ( IfcUtil : : path : : to_utf8 ( output_temp_filename ) , geometry_settings , serializer_settings , false , logger ) ;
2021-08-31 11:03:28 +02:00
# endif
2023-03-22 11:17:14 +01:00
# endif
2024-09-27 16:32:41 +02:00
} else if ( output_extension = = TTL ) {
2026-06-10 18:30:54 +02:00
serializer = boost : : make_shared < TtlWktSerializer > ( IfcUtil : : path : : to_utf8 ( output_temp_filename ) , geometry_settings , serializer_settings , logger ) ;
2023-03-22 11:17:14 +01:00
} else {
2019-02-08 15:18:55 +01:00
cerr_ < < " [Error] Unknown output filename extension ' " < < output_extension < < " ' \n " ;
2018-05-12 20:12:25 +02:00
write_log ( ! quiet ) ;
2016-03-07 22:37:36 +02:00
print_usage ( ) ;
2017-02-20 19:41:52 +02:00
return EXIT_FAILURE ;
2013-03-24 10:48:39 +00:00
}
2016-03-17 23:25:53 +02:00
const bool is_tesselated = serializer - > isTesselated ( ) ; // isTesselated() doesn't change at run-time
if ( ! is_tesselated ) {
2023-11-15 10:32:20 +01:00
if ( geometry_settings . get < ifcopenshell : : geometry : : settings : : WeldVertices > ( ) . get ( ) ) {
2026-06-10 18:30:54 +02:00
logger . Notice ( " SYS " , 16 , " Weld vertices setting ignored when writing non-tesselated output " ) ;
2015-02-23 22:01:21 +00:00
}
2023-11-15 10:32:20 +01:00
if ( geometry_settings . get < ifcopenshell : : geometry : : settings : : GenerateUvs > ( ) . get ( ) ) {
2026-06-10 18:30:54 +02:00
logger . Notice ( " SYS " , 17 , " Generate UVs setting ignored when writing non-tesselated output " ) ;
2016-03-17 00:25:33 +02:00
}
2024-11-29 13:03:56 +01:00
if ( center_model | | center_model_geometry ) {
2026-06-10 18:30:54 +02:00
logger . Notice ( " SYS " , 18 , " Centering/offsetting model setting ignored when writing non-tesselated output " ) ;
2016-03-17 23:25:53 +02:00
}
2023-11-15 10:32:20 +01:00
geometry_settings . get < ifcopenshell : : geometry : : settings : : IteratorOutput > ( ) . value = ifcopenshell : : geometry : : settings : : NATIVE ;
2015-02-23 22:01:21 +00:00
}
2013-03-24 10:48:39 +00:00
if ( ! serializer - > ready ( ) ) {
2026-07-10 15:37:44 +03:00
logger . Error ( " SYS " , 25 , " Unable to open output file ' " + IfcUtil : : path : : to_utf8 ( output_filename ) + " ' for writing; check that the directory exists and is writable " ) ;
2019-02-08 15:18:55 +01:00
IfcUtil : : path : : delete_file ( IfcUtil : : path : : to_utf8 ( output_temp_filename ) ) ;
2018-05-12 20:12:25 +02:00
write_log ( ! quiet ) ;
2017-02-20 19:41:52 +02:00
return EXIT_FAILURE ;
2013-03-24 10:48:39 +00:00
}
2014-03-31 14:03:06 +00:00
time_t start , end ;
time ( & start ) ;
2017-04-11 17:26:30 +02:00
2025-10-24 12:06:59 +02:00
// @nb last argument true -> bypass_properties which are not read by any of the geometry serializers
// XML, RocksDB, IFC are already special-cased above
// SVG requires properties for IfcAnnotation/DRAWING properties
2026-06-10 18:30:54 +02:00
if ( ! init_input_file ( IfcUtil : : path : : to_utf8 ( input_filename ) , ifc_file , no_progress | | quiet , mmap , output_extension ! = SVG , logger ) ) {
2018-10-08 10:39:27 +03:00
write_log ( ! quiet ) ;
2019-06-04 17:06:13 +02:00
serializer . reset ( ) ;
2019-02-08 15:18:55 +01:00
IfcUtil : : path : : delete_file ( IfcUtil : : path : : to_utf8 ( output_temp_filename ) ) ; /**< @todo Windows Unicode support */
2017-06-15 22:07:56 +03:00
return EXIT_FAILURE ;
}
2020-08-16 16:17:38 +02:00
if ( vmap . count ( " log-file " ) ) {
2026-06-10 18:30:54 +02:00
logger . SetOutput ( quiet ? nullptr : & cout_ , & log_fs ) ;
2020-08-16 16:17:38 +02:00
} else {
2026-06-10 18:30:54 +02:00
logger . SetOutput ( quiet ? nullptr : & cout_ , vcounter . count > 1 ? & cout_ : & log_stream ) ;
2020-08-16 16:17:38 +02:00
}
2025-03-19 11:42:11 +01:00
if ( model_rotation ) {
std : : vector < double > rotation ( 4 ) ;
int n = 0 ;
if ( sscanf ( rotation_str . c_str ( ) , " %lf;%lf;%lf;%lf %n " , & rotation [ 0 ] , & rotation [ 1 ] , & rotation [ 2 ] , & rotation [ 3 ] , & n ) ! = 4 | | n ! = rotation_str . size ( ) ) {
cerr_ < < " [Error] Invalid use of --model-rotation \n " ;
IfcUtil : : path : : delete_file ( IfcUtil : : path : : to_utf8 ( output_temp_filename ) ) ;
print_options ( serializer_options ) ;
return EXIT_FAILURE ;
}
std : : stringstream msg ;
msg < < " Using model rotation ( " < < rotation [ 0 ] < < " , " < < rotation [ 1 ] < < " , " < < rotation [ 2 ] < < " , " < < rotation [ 3 ] < < " ) " ;
2026-06-10 18:30:54 +02:00
logger . Notice ( " SYS " , 19 , msg . str ( ) ) ;
2025-03-19 11:42:11 +01:00
geometry_settings . get < ifcopenshell : : geometry : : settings : : ModelRotation > ( ) . value = rotation ;
}
if ( model_offset & & ( center_model | | center_model_geometry ) ) {
2026-06-10 18:30:54 +02:00
logger . Notice ( " GEO " , 22 , " --model-offset ignored with --center-model or --center-model-geometry " ) ;
2025-03-19 11:42:11 +01:00
}
if ( model_offset & & ! ( center_model | | center_model_geometry ) ) {
std : : vector < double > offset ( 3 ) ;
int n = 0 ;
if ( sscanf ( offset_str . c_str ( ) , " %lf;%lf;%lf %n " , & offset [ 0 ] , & offset [ 1 ] , & offset [ 2 ] , & n ) ! = 3 | | n ! = offset_str . size ( ) ) {
cerr_ < < " [Error] Invalid use of --model-offset \n " ;
IfcUtil : : path : : delete_file ( IfcUtil : : path : : to_utf8 ( output_temp_filename ) ) ;
print_options ( serializer_options ) ;
return EXIT_FAILURE ;
}
std : : stringstream msg ;
msg < < std : : setprecision ( std : : numeric_limits < double > : : max_digits10 ) < < " Using model offset ( " < < offset [ 0 ] < < " , " < < offset [ 1 ] < < " , " < < offset [ 2 ] < < " ) " ;
2026-06-10 18:30:54 +02:00
logger . Notice ( " SYS " , 20 , msg . str ( ) ) ;
2025-03-19 11:42:11 +01:00
geometry_settings . get < ifcopenshell : : geometry : : settings : : ModelOffset > ( ) . value = offset ;
}
2020-01-31 15:55:04 +01:00
2024-11-29 13:03:56 +01:00
if ( is_tesselated & & ( center_model | | center_model_geometry ) ) {
std : : vector < double > offset ( 3 ) ;
2018-08-20 14:36:53 +03:00
2026-06-10 18:30:54 +02:00
IfcGeom : : Iterator tmp_context_iterator ( ifcopenshell : : geometry : : kernels : : construct ( ifc_file , geometry_kernel , geometry_settings , logger ) , geometry_settings , ifc_file , filter_funcs , num_threads , logger ) ;
2020-02-12 11:38:08 +01:00
2024-11-29 13:03:56 +01:00
time_t start , end ;
time ( & start ) ;
2026-06-10 18:30:54 +02:00
if ( ! quiet ) logger . Status ( " Computing bounds... " ) ;
2024-11-29 13:03:56 +01:00
if ( center_model_geometry ) {
if ( ! tmp_context_iterator . initialize ( ) ) {
/// @todo It would be nice to know and print separate error prints for a case where we found no entities
/// and for a case we found no entities that satisfy our filtering criteria.
2026-06-10 18:30:54 +02:00
logger . Notice ( " GEO " , 23 , " No geometrical elements found or none successfully converted " ) ;
2024-11-29 13:03:56 +01:00
serializer . reset ( ) ;
IfcUtil : : path : : delete_file ( IfcUtil : : path : : to_utf8 ( output_temp_filename ) ) ;
write_log ( ! quiet ) ;
return EXIT_FAILURE ;
2020-02-12 11:38:08 +01:00
}
2024-11-29 13:03:56 +01:00
}
2020-02-12 11:38:08 +01:00
2024-11-29 13:03:56 +01:00
tmp_context_iterator . compute_bounds ( center_model_geometry ) ;
2020-01-31 15:55:04 +01:00
2024-11-29 13:03:56 +01:00
time ( & end ) ;
2026-06-10 18:30:54 +02:00
if ( ! quiet ) logger . Status ( " Done ! Bounds computed in " + format_duration ( start , end ) ) ;
2018-08-20 14:36:53 +03:00
2024-11-29 13:03:56 +01:00
auto center = ( tmp_context_iterator . bounds_min ( ) . ccomponents ( ) + tmp_context_iterator . bounds_max ( ) . ccomponents ( ) ) * 0.5 ;
offset [ 0 ] = - center ( 0 ) ;
offset [ 1 ] = - center ( 1 ) ;
offset [ 2 ] = - center ( 2 ) ;
2017-01-12 15:47:44 +02:00
std : : stringstream msg ;
2025-03-19 11:42:11 +01:00
msg < < std : : setprecision ( std : : numeric_limits < double > : : max_digits10 ) < < " Using model offset ( " < < offset [ 0 ] < < " , " < < offset [ 1 ] < < " , " < < offset [ 2 ] < < " ) " ;
2026-06-10 18:30:54 +02:00
logger . Notice ( " SYS " , 21 , msg . str ( ) ) ;
2024-11-29 13:03:56 +01:00
geometry_settings . get < ifcopenshell : : geometry : : settings : : ModelOffset > ( ) . value = offset ;
2017-01-12 15:47:44 +02:00
}
2024-04-18 21:04:02 +02:00
// backwards compatibility
if ( vmap . count ( " plan " ) & & vmap . count ( " model " ) ) {
geometry_settings . get < ifcopenshell : : geometry : : settings : : OutputDimensionality > ( ) . value = ifcopenshell : : geometry : : settings : : CURVES_SURFACES_AND_SOLIDS ;
} else if ( vmap . count ( " model " ) ) {
geometry_settings . get < ifcopenshell : : geometry : : settings : : OutputDimensionality > ( ) . value = ifcopenshell : : geometry : : settings : : SURFACES_AND_SOLIDS ;
} else if ( vmap . count ( " plan " ) ) {
geometry_settings . get < ifcopenshell : : geometry : : settings : : OutputDimensionality > ( ) . value = ifcopenshell : : geometry : : settings : : CURVES ;
}
2023-09-13 22:02:52 +02:00
std : : unique_ptr < IfcGeom : : Iterator > context_iterator ;
if ( ! elems_from_adaptor ) {
2026-06-10 18:30:54 +02:00
context_iterator . reset ( new IfcGeom : : Iterator ( ifcopenshell : : geometry : : kernels : : construct ( ifc_file , geometry_kernel , geometry_settings , logger ) , geometry_settings , ifc_file , filter_funcs , num_threads , logger ) ) ;
2023-09-13 22:02:52 +02:00
}
2021-10-08 15:38:46 +02:00
2023-03-22 11:17:14 +01:00
# if defined(WITH_HDF5) && defined(IFOPSH_WITH_OPENCASCADE)
2021-10-08 15:38:46 +02:00
std : : unique_ptr < HdfSerializer > cache ;
2023-09-13 22:02:52 +02:00
if ( context_iterator & & vmap . count ( " cache-file " ) | | vmap . count ( " cache " ) ) {
2021-10-08 15:38:46 +02:00
if ( ! vmap . count ( " cache-file " ) ) {
cache_file = input_filename + CACHE + HDF ;
}
2026-06-10 18:30:54 +02:00
cache . reset ( new HdfSerializer ( IfcUtil : : path : : to_utf8 ( cache_file ) , geometry_settings , serializer_settings , false , logger ) ) ;
2023-09-13 22:02:52 +02:00
context_iterator - > set_cache ( cache . get ( ) ) ;
2021-10-08 15:38:46 +02:00
}
# endif
2026-06-10 18:30:54 +02:00
logger . Message ( Logger : : LOG_PERF , " GEO " , 24 , " file geometry conversion " ) ;
2022-01-09 14:30:56 +01:00
2023-09-13 22:02:52 +02:00
if ( context_iterator & & ! context_iterator - > initialize ( ) ) {
2020-01-31 15:55:04 +01:00
/// @todo It would be nice to know and print separate error prints for a case where we found no entities
/// and for a case we found no entities that satisfy our filtering criteria.
2026-06-10 18:30:54 +02:00
logger . Notice ( " GEO " , 25 , " No geometrical elements found or none successfully converted " ) ;
2020-01-31 15:55:04 +01:00
serializer . reset ( ) ;
IfcUtil : : path : : delete_file ( IfcUtil : : path : : to_utf8 ( output_temp_filename ) ) ;
write_log ( ! quiet ) ;
return EXIT_FAILURE ;
}
2023-09-13 22:02:52 +02:00
serializer - > setFile ( ifc_file ) ;
2020-07-11 13:39:09 +02:00
2023-03-22 11:17:14 +01:00
# ifdef IFOPSH_WITH_OPENCASCADE
2020-07-11 13:39:09 +02:00
if ( output_extension = = SVG ) {
2024-10-08 20:57:27 +02:00
// @todo turn these all into proper settings
2020-07-11 13:39:09 +02:00
if ( vmap . count ( " section-height-from-storeys " ) ! = 0 ) {
if ( vmap . count ( " section-height " ) ) {
static_cast < SvgSerializer * > ( serializer . get ( ) ) - > setSectionHeightsFromStoreys ( section_height ) ;
} else {
static_cast < SvgSerializer * > ( serializer . get ( ) ) - > setSectionHeightsFromStoreys ( ) ;
}
} else if ( vmap . count ( " section-height " ) ! = 0 ) {
2026-06-10 18:30:54 +02:00
logger . Notice ( " SYS " , 22 , " Overriding section height " ) ;
2020-07-11 13:39:09 +02:00
static_cast < SvgSerializer * > ( serializer . get ( ) ) - > setSectionHeight ( section_height ) ;
}
if ( vmap . count ( " print-space-names " ) ! = 0 ) {
static_cast < SvgSerializer * > ( serializer . get ( ) ) - > setPrintSpaceNames ( true ) ;
}
if ( vmap . count ( " print-space-areas " ) ! = 0 ) {
static_cast < SvgSerializer * > ( serializer . get ( ) ) - > setPrintSpaceAreas ( true ) ;
}
2021-03-05 21:50:32 +01:00
if ( vmap . count ( " draw-storey-heights " ) ! = 0 ) {
2021-03-09 16:11:42 +01:00
static_cast < SvgSerializer * > ( serializer . get ( ) ) - > setDrawStoreyHeights ( svg_storey_height_display ) ;
2021-03-05 21:50:32 +01:00
}
2020-07-11 13:39:09 +02:00
if ( bounding_width . is_initialized ( ) & & bounding_height . is_initialized ( ) ) {
static_cast < SvgSerializer * > ( serializer . get ( ) ) - > setBoundingRectangle ( bounding_width . get ( ) , bounding_height . get ( ) ) ;
}
2020-07-11 23:26:21 +02:00
if ( vmap . count ( " door-arcs " ) ) {
static_cast < SvgSerializer * > ( serializer . get ( ) ) - > setDrawDoorArcs ( true ) ;
}
2020-08-25 15:01:07 +02:00
if ( vmap . count ( " scale " ) ) {
int s0 , s1 ;
if ( sscanf ( svg_scale . c_str ( ) , " %u:%u " , & s0 , & s1 ) = = 2 & & s0 > 0 & & s1 > 0 ) {
static_cast < SvgSerializer * > ( serializer . get ( ) ) - > setScale ( ( double ) s0 / s1 ) ;
} else {
cerr_ < < " [Error] Invalid use of --scale " < < std : : endl ;
print_options ( serializer_options ) ;
return EXIT_FAILURE ;
}
}
2020-09-20 15:08:52 +02:00
if ( vmap . count ( " section-ref " ) ) {
static_cast < SvgSerializer * > ( serializer . get ( ) ) - > setSectionRef ( section_ref ) ;
}
if ( vmap . count ( " elevation-ref " ) ) {
static_cast < SvgSerializer * > ( serializer . get ( ) ) - > setElevationRef ( elevation_ref ) ;
}
2021-08-08 18:06:58 +02:00
if ( vmap . count ( " elevation-ref-guid " ) ) {
static_cast < SvgSerializer * > ( serializer . get ( ) ) - > setElevationRefGuid ( elevation_ref_guid ) ;
}
2020-12-30 14:25:08 +01:00
if ( vmap . count ( " auto-section " ) ) {
static_cast < SvgSerializer * > ( serializer . get ( ) ) - > setAutoSection ( true ) ;
}
if ( vmap . count ( " auto-elevation " ) ) {
static_cast < SvgSerializer * > ( serializer . get ( ) ) - > setAutoElevation ( true ) ;
}
2020-12-30 14:44:43 +01:00
static_cast < SvgSerializer * > ( serializer . get ( ) ) - > setUseNamespace ( vmap . count ( " svg-xmlns " ) > 0 ) ;
2021-01-02 12:34:10 +01:00
static_cast < SvgSerializer * > ( serializer . get ( ) ) - > setUseHlrPoly ( vmap . count ( " svg-poly " ) > 0 ) ;
2023-06-29 13:08:22 +02:00
static_cast < SvgSerializer * > ( serializer . get ( ) ) - > setUsePrefiltering ( vmap . count ( " svg-prefilter " ) > 0 ) ;
2023-08-30 10:57:33 +02:00
static_cast < SvgSerializer * > ( serializer . get ( ) ) - > setSegmentProjection ( vmap . count ( " svg-segment-projection " ) > 0 ) ;
2021-04-15 10:36:37 +02:00
static_cast < SvgSerializer * > ( serializer . get ( ) ) - > setPolygonal ( vmap . count ( " svg-write-poly " ) > 0 ) ;
2021-01-27 22:34:19 +01:00
static_cast < SvgSerializer * > ( serializer . get ( ) ) - > setAlwaysProject ( vmap . count ( " svg-project " ) > 0 ) ;
2021-04-18 15:03:28 +02:00
static_cast < SvgSerializer * > ( serializer . get ( ) ) - > setWithoutStoreys ( vmap . count ( " svg-without-storeys " ) > 0 ) ;
2021-08-08 20:40:15 +02:00
static_cast < SvgSerializer * > ( serializer . get ( ) ) - > setNoCSS ( vmap . count ( " svg-no-css " ) > 0 ) ;
2020-09-27 14:29:27 +02:00
if ( relative_center_x & & relative_center_y ) {
static_cast < SvgSerializer * > ( serializer . get ( ) ) - > setDrawingCenter ( * relative_center_x , * relative_center_y ) ;
}
2021-03-28 21:38:29 +02:00
if ( vmap . count ( " storey-height-line-length " ) ) {
static_cast < SvgSerializer * > ( serializer . get ( ) ) - > setStoreyHeightLineLength (
vmap [ " storey-height-line-length " ] . as < double > ( )
) ;
}
2021-03-28 22:16:58 +02:00
if ( vmap . count ( " space-name-transform " ) ) {
static_cast < SvgSerializer * > ( serializer . get ( ) ) - > setSpaceNameTransform (
vmap [ " space-name-transform " ] . as < std : : string > ( )
) ;
}
2020-07-11 13:39:09 +02:00
}
2023-03-22 11:17:14 +01:00
# endif
2020-01-31 15:55:04 +01:00
2023-11-15 10:32:20 +01:00
if ( context_iterator & & geometry_settings . get < ifcopenshell : : geometry : : settings : : ConvertBackUnits > ( ) . get ( ) ) {
2023-09-13 22:02:52 +02:00
serializer - > setUnitNameAndMagnitude ( context_iterator - > unit_name ( ) , static_cast < float > ( context_iterator - > unit_magnitude ( ) ) ) ;
2020-01-31 15:55:04 +01:00
} else {
serializer - > setUnitNameAndMagnitude ( " METER " , 1.0f ) ;
}
serializer - > writeHeader ( ) ;
int old_progress = quiet ? 0 : - 1 ;
2018-05-12 20:12:25 +02:00
if ( ! quiet ) {
2026-06-10 18:30:54 +02:00
logger . Status ( " Creating geometry... " ) ;
2018-05-12 20:12:25 +02:00
}
2013-03-24 10:48:39 +00:00
2015-02-06 13:07:50 +00:00
// The functions IfcGeom::Iterator::get() and IfcGeom::Iterator::next()
// wrap an iterator of all geometrical products in the Ifc file.
// IfcGeom::Iterator::get() returns an IfcGeom::TriangulationElement or
2023-03-21 20:15:01 +01:00
// -BRepElement pointer, based on current settings. (see Iterator.h
2015-02-06 13:07:50 +00:00
// for definition) IfcGeom::Iterator::next() is used to poll whether more
// geometrical entities are available. None of these functions throw
// exceptions, neither for parsing errors or geometrical errors. Upon
// calling next() the entity to be returned has already been processed, a
2017-02-03 19:15:37 +02:00
// non-null return value guarantees that a successfully processed product is
2015-02-06 13:07:50 +00:00
// available.
2016-03-27 21:17:51 +02:00
size_t num_created = 0 ;
2023-09-13 22:02:52 +02:00
std : : list < IfcGeom : : Element * > : : const_iterator elems_from_adaptor_it ;
if ( elems_from_adaptor ) {
elems_from_adaptor_it = elems_from_adaptor - > begin ( ) ;
}
2016-03-27 21:17:51 +02:00
2023-09-13 22:02:52 +02:00
while ( true ) {
2021-02-14 12:23:43 +01:00
2023-09-13 22:02:52 +02:00
IfcGeom : : Element * geom_object = elems_from_adaptor ? * elems_from_adaptor_it : context_iterator - > get ( ) ;
2017-04-20 15:06:49 +02:00
2017-04-21 15:49:34 +02:00
if ( is_tesselated )
2017-04-11 17:26:30 +02:00
{
2021-07-11 15:00:59 +02:00
serializer - > write ( static_cast < const IfcGeom : : TriangulationElement * > ( geom_object ) ) ;
2017-04-11 17:26:30 +02:00
}
2017-04-12 14:38:54 +02:00
else
2017-04-11 17:26:30 +02:00
{
2021-07-11 15:00:59 +02:00
serializer - > write ( static_cast < const IfcGeom : : BRepElement * > ( geom_object ) ) ;
2013-03-24 10:48:39 +00:00
}
2017-05-23 20:06:35 +03:00
if ( ! no_progress ) {
2023-09-13 22:02:52 +02:00
int progress = context_iterator ? context_iterator - > progress ( ) : ( int ) std : : distance ( elems_from_adaptor - > cbegin ( ) , elems_from_adaptor_it ) * 100 / elems_from_adaptor - > size ( ) ;
2018-05-12 20:12:25 +02:00
if ( quiet ) {
for ( ; old_progress < progress ; + + old_progress ) {
2019-07-09 16:00:58 +02:00
cout_ < < " . " ;
2018-08-13 12:54:52 +02:00
if ( stderr_progress )
2019-07-09 16:00:58 +02:00
cerr_ < < " . " ;
2018-05-12 20:12:25 +02:00
}
2019-07-09 16:00:58 +02:00
cout_ < < std : : flush ;
2018-08-13 12:54:52 +02:00
if ( stderr_progress )
2019-07-09 16:00:58 +02:00
cerr_ < < std : : flush ;
2020-08-16 13:37:58 +02:00
} else if ( vcounter . count = = 2 ) {
2026-06-10 18:30:54 +02:00
logger . Message ( Logger : : LOG_DEBUG , " SYS " , 23 , " Progress " + boost : : lexical_cast < std : : string > ( progress ) ) ;
2018-05-12 20:12:25 +02:00
} else {
2023-09-13 22:02:52 +02:00
progress = progress / 2 ;
2026-06-10 18:30:54 +02:00
if ( old_progress ! = progress ) logger . ProgressBar ( progress ) ;
2018-05-12 20:12:25 +02:00
old_progress = progress ;
}
2017-05-23 20:06:35 +03:00
}
2013-03-24 10:48:39 +00:00
2023-09-13 22:02:52 +02:00
+ + num_created ;
if ( context_iterator ) {
if ( ! context_iterator - > next ( ) ) {
break ;
}
} else {
+ + elems_from_adaptor_it ;
if ( elems_from_adaptor_it = = elems_from_adaptor - > end ( ) ) {
break ;
}
}
}
2018-05-12 20:12:25 +02:00
if ( ! no_progress & & quiet ) {
for ( ; old_progress < 100 ; + + old_progress ) {
2019-07-09 16:00:58 +02:00
cout_ < < " . " ;
2018-08-13 12:54:52 +02:00
if ( stderr_progress )
2019-07-09 16:00:58 +02:00
cerr_ < < " . " ;
}
cout_ < < std : : flush ;
if ( stderr_progress ) {
cerr_ < < std : : flush ;
2018-05-12 20:12:25 +02:00
}
} else {
2019-07-09 16:00:58 +02:00
const std : : string task = ( ( num_threads = = 1 ) ? " creating " : " writing " ) ;
2026-06-10 18:30:54 +02:00
logger . Status ( " \r Done " + task + " geometry ( " + boost : : lexical_cast < std : : string > ( num_created ) +
2018-05-12 20:12:25 +02:00
" objects) " ) ;
}
2016-03-07 22:37:36 +02:00
2016-03-27 21:17:51 +02:00
serializer - > finalize ( ) ;
2018-10-08 16:36:48 +03:00
// Make sure the dtor is explicitly run here (e.g. output files are closed before renaming them).
serializer . reset ( ) ;
2013-03-24 10:48:39 +00:00
2026-06-10 18:30:54 +02:00
logger . Message ( Logger : : LOG_PERF , " GEO " , 26 , " done file geometry conversion " ) ;
2022-01-09 15:15:50 +01:00
2023-05-17 14:24:09 +02:00
bool successful ;
if ( output_extension = = USD | | output_extension = = USDC | | output_extension = = USDA ) {
// No need to rename the file
successful = true ;
}
else {
// Renaming might fail (e.g. maybe the existing file was open in a viewer application)
// Do not remove the temp file as user can salvage the conversion result from it.
successful = IfcUtil : : path : : rename_file ( IfcUtil : : path : : to_utf8 ( output_temp_filename ) , IfcUtil : : path : : to_utf8 ( output_filename ) ) ;
}
2023-05-07 22:46:05 +02:00
2016-03-28 11:47:02 +03:00
if ( ! successful ) {
2019-02-08 15:18:55 +01:00
cerr_ < < " Unable to write output file ' " < < output_filename < < " ', see ' " < <
output_temp_filename < < " ' for the conversion result. " ;
2016-03-20 12:48:20 +02:00
}
2013-03-24 10:48:39 +00:00
2026-06-10 18:30:54 +02:00
if ( geometry_settings . get < ifcopenshell : : geometry : : settings : : ValidateQuantities > ( ) . get ( ) & & logger . MaxSeverity ( ) > = Logger : : LOG_ERROR ) {
logger . Error ( " SYS " , 24 , " Errors encountered during processing. " ) ;
2018-12-12 12:33:09 +01:00
successful = false ;
}
2026-07-10 20:38:15 +03:00
if ( fail_on_error & & logger . MaxSeverity ( ) > = Logger : : LOG_ERROR ) {
logger . Error ( " SYS " , 26 , " Errors encountered during processing, failing due to --fail-on-error. " ) ;
successful = false ;
}
2026-06-10 18:30:54 +02:00
if ( logger . Verbosity ( ) = = Logger : : LOG_PERF ) {
logger . PrintPerformanceStats ( ) ;
2022-01-09 15:15:50 +01:00
}
2018-05-12 20:12:25 +02:00
write_log ( ! quiet ) ;
2013-03-24 10:48:39 +00:00
time ( & end ) ;
2015-02-06 13:07:50 +00:00
2018-10-01 13:19:31 +03:00
if ( ! quiet ) {
2026-06-10 18:30:54 +02:00
logger . Status ( " \n Conversion took " + format_duration ( start , end ) ) ;
2018-10-01 13:19:31 +03:00
}
2015-02-06 13:07:50 +00:00
2017-02-22 16:34:09 +02:00
return successful ? EXIT_SUCCESS : EXIT_FAILURE ;
2013-12-31 14:10:38 +00:00
}
2018-10-01 13:19:31 +03:00
std : : string format_duration ( time_t start , time_t end )
{
int seconds = ( int ) difftime ( end , start ) ;
std : : stringstream ss ;
int minutes = seconds / 60 ;
seconds = seconds % 60 ;
if ( minutes > 0 ) {
ss < < minutes < < " minute " ;
if ( minutes = = 0 | | minutes > 1 ) {
ss < < " s " ;
}
ss < < " " ;
}
ss < < seconds < < " second " ;
if ( seconds = = 0 | | seconds > 1 ) {
ss < < " s " ;
}
return ss . str ( ) ;
}
2018-05-12 20:12:25 +02:00
void write_log ( bool header ) {
2019-02-08 15:18:55 +01:00
path_t log = log_stream . str ( ) ;
2013-12-31 14:10:38 +00:00
if ( ! log . empty ( ) ) {
2019-02-08 15:18:55 +01:00
if ( header ) {
cout_ < < " \n Log: \n " ;
}
cout_ < < log < < std : : endl ;
2013-12-31 14:10:38 +00:00
}
2016-03-07 22:37:36 +02:00
}
2016-12-16 13:51:07 +02:00
2018-08-29 12:54:25 +02:00
# include <boost/algorithm/string/predicate.hpp>
2026-06-10 18:30:54 +02:00
bool init_input_file ( const std : : string & filename , IfcParse : : IfcFile * & ifc_file , bool no_progress , bool mmap , bool bypass_properties , Logger & logger ) {
2018-10-01 13:19:31 +03:00
time_t start , end ;
2017-12-20 12:40:15 +01:00
2017-06-15 22:07:56 +03:00
// Prevent IfcFile::Init() prints by setting output to null temporarily
2026-06-10 18:30:54 +02:00
if ( no_progress ) { logger . SetOutput ( NULL , & log_stream ) ; }
2017-06-15 22:07:56 +03:00
2018-10-01 13:19:31 +03:00
time ( & start ) ;
2018-08-29 12:54:25 +02:00
2025-10-24 12:06:59 +02:00
bool requires_init = false ;
2018-08-29 12:54:25 +02:00
# ifdef WITH_IFCXML
if ( boost : : ends_with ( boost : : to_lower_copy ( filename ) , " .ifcxml " ) ) {
2026-06-11 21:04:44 +02:00
ifc_file = IfcParse : : parse_ifcxml ( filename , logger ) ;
2025-10-24 12:06:59 +02:00
} else
2018-08-29 12:54:25 +02:00
# endif
2025-10-24 12:06:59 +02:00
{
2026-06-11 21:04:44 +02:00
ifc_file = new IfcParse : : IfcFile ( IfcParse : : uninitialized_tag { } , logger ) ;
2025-10-24 12:06:59 +02:00
requires_init = true ;
}
2021-09-04 15:38:07 +02:00
2025-11-14 19:12:39 +01:00
if ( bypass_properties ) {
ifc_file - > bypass_type ( " IfcRelDefinesByProperties " ) ;
ifc_file - > bypass_type ( " IfcPropertySetDefinition " ) ;
ifc_file - > bypass_type ( " IfcProperty " ) ;
ifc_file - > bypass_type ( " IfcMaterialProperties " ) ;
ifc_file - > bypass_type ( " IfcProfileProperties " ) ;
ifc_file - > bypass_type ( " IfcPhysicalQuantity " ) ;
}
2025-10-24 12:06:59 +02:00
2021-09-04 15:38:07 +02:00
# ifdef USE_MMAP
2025-10-24 12:06:59 +02:00
if ( mmap ) {
ifc_file - > initialize ( filename , mmap ) ;
requires_init = false ;
}
2021-09-04 15:38:07 +02:00
# else
2025-10-24 12:06:59 +02:00
( void ) mmap ;
2017-07-23 14:52:04 +02:00
# endif
2025-10-24 12:06:59 +02:00
if ( requires_init ) {
ifc_file - > initialize ( filename ) ;
}
2021-09-04 15:38:07 +02:00
if ( ! ifc_file | | ! ifc_file - > good ( ) ) {
2026-06-10 18:30:54 +02:00
logger . Error ( " SYN " , 1 , " Unable to parse input file ' " + filename + " ' " ) ;
2017-06-15 22:07:56 +03:00
return false ;
}
2018-10-01 13:19:31 +03:00
time ( & end ) ;
2017-06-15 22:07:56 +03:00
2026-06-10 18:30:54 +02:00
if ( no_progress ) { logger . SetOutput ( & cout_ , & log_stream ) ; }
else { logger . Status ( " Parsing input file took " + format_duration ( start , end ) ) ; }
2017-06-15 22:07:56 +03:00
return true ;
2017-12-20 12:40:15 +01:00
2017-06-15 22:07:56 +03:00
}
2017-05-19 18:10:20 +03:00
bool append_filter ( const std : : string & type , const std : : vector < std : : string > & values , geom_filter & filter )
{
geom_filter temp ;
parse_filter ( temp , values ) ;
// Merge values only if type and arg match.
if ( ( filter . type ! = geom_filter : : UNUSED & & filter . type ! = temp . type ) | | ( ! filter . arg . empty ( ) & & filter . arg ! = temp . arg ) ) {
2019-02-08 15:18:55 +01:00
cerr_ < < " [Error] Multiple ' " < < type . c_str ( ) < < " ' filters specified with different criteria \n " ;
2017-05-19 18:10:20 +03:00
return false ;
}
filter . type = temp . type ;
filter . values . insert ( temp . values . begin ( ) , temp . values . end ( ) ) ;
filter . arg = temp . arg ;
return true ;
}
size_t read_filters_from_file (
const std : : string & filename ,
inclusion_filter & include_filter ,
inclusion_traverse_filter & include_traverse_filter ,
exclusion_filter & exclude_filter ,
exclusion_traverse_filter & exclude_traverse_filter )
{
2019-02-08 15:18:55 +01:00
std : : ifstream filter_file ( IfcUtil : : path : : from_utf8 ( filename ) . c_str ( ) ) ;
2017-05-19 18:10:20 +03:00
if ( ! filter_file . is_open ( ) ) {
2019-02-08 15:18:55 +01:00
cerr_ < < " [Error] Unable to open filter file ' " < < IfcUtil : : path : : from_utf8 ( filename ) < < " ' or the file does not exist. \n " ;
2017-05-19 18:10:20 +03:00
return 0 ;
}
size_t line_number = 1 , num_filters = 0 ;
for ( std : : string line ; std : : getline ( filter_file , line ) ; + + line_number ) {
boost : : trim ( line ) ;
if ( line . empty ( ) ) {
continue ;
}
std : : vector < std : : string > values ;
boost : : split ( values , line , boost : : is_any_of ( " \t " ) , boost : : token_compress_on ) ;
if ( values . empty ( ) ) {
continue ;
}
std : : string type = values . front ( ) ;
values . erase ( values . begin ( ) ) ;
// Support both "--include=arg GlobalId 1VQ5n5$RrEbPk8le4ZCI81" and "include arg GlobalId 1VQ5n5$RrEbPk8le4ZCI81"
// and tolerate extraneous whitespace.
boost : : trim_left_if ( type , boost : : is_any_of ( " - " ) ) ;
size_t equal_pos = type . find ( ' = ' ) ;
if ( equal_pos ! = std : : string : : npos ) {
std : : string value = type . substr ( equal_pos + 1 ) ;
type = type . substr ( 0 , equal_pos ) ;
values . insert ( values . begin ( ) , value ) ;
}
try {
if ( type = = " include " ) { if ( append_filter ( " include " , values , include_filter ) ) { + + num_filters ; } }
else if ( type = = " include+ " ) { if ( append_filter ( " include+ " , values , include_traverse_filter ) ) { + + num_filters ; } }
else if ( type = = " exclude " ) { if ( append_filter ( " exclude " , values , exclude_filter ) ) { + + num_filters ; } }
else if ( type = = " exclude+ " ) { if ( append_filter ( " exclude+ " , values , exclude_traverse_filter ) ) { + + num_filters ; } }
else {
2019-02-08 15:18:55 +01:00
cerr_ < < " [Error] Invalid filtering type at line " < < boost : : lexical_cast < path_t > ( line_number ) < < " \n " ;
2017-05-19 18:10:20 +03:00
return 0 ;
}
} catch ( . . . ) {
2019-02-08 15:18:55 +01:00
cerr_ < < " [Error] Unable to parse filter at line " < < boost : : lexical_cast < path_t > ( line_number ) < < " . \n " ;
2017-05-19 18:10:20 +03:00
return 0 ;
}
}
return num_filters ;
}
2017-02-16 14:28:18 +02:00
void parse_filter ( geom_filter & filter , const std : : vector < std : : string > & values )
2016-12-16 13:51:07 +02:00
{
if ( values . size ( ) = = 0 ) {
throw po : : validation_error ( po : : validation_error : : at_least_one_value_required ) ;
}
std : : string type = * values . begin ( ) ;
if ( type = = " entities " ) {
2017-02-02 14:58:58 +02:00
filter . type = geom_filter : : ENTITY_TYPE ;
2017-02-03 11:02:24 +02:00
} else if ( type = = " layers " ) {
filter . type = geom_filter : : LAYER_NAME ;
2019-10-13 07:45:19 +02:00
} else if ( type = = " attribute " | | type = = " arg " ) {
2017-02-16 14:28:18 +02:00
filter . type = geom_filter : : ENTITY_ARG ;
filter . arg = * ( values . begin ( ) + 1 ) ;
2016-12-16 13:51:07 +02:00
} else {
throw po : : validation_error ( po : : validation_error : : invalid_option_value ) ;
}
2017-02-16 14:28:18 +02:00
filter . values . insert ( values . begin ( ) + ( filter . type = = geom_filter : : ENTITY_ARG ? 2 : 1 ) , values . end ( ) ) ;
}
2020-08-16 13:37:58 +02:00
void validate ( boost : : any & v , const std : : vector < std : : string > & values , verbosity_counter * , long ) {
if ( v . empty ( ) ) v = verbosity_counter { 1 } ;
else + + boost : : any_cast < verbosity_counter & > ( v ) . count ;
}
2017-02-16 14:28:18 +02:00
void validate ( boost : : any & v , const std : : vector < std : : string > & values , inclusion_filter * , int )
{
2017-05-19 18:10:20 +03:00
/// @todo For now only single --include, --include+, --exclude, or --exclude+ supported. Support having multiple.
2017-02-16 14:28:18 +02:00
po : : validators : : check_first_occurrence ( v ) ;
inclusion_filter filter ;
parse_filter ( filter , values ) ;
2016-12-16 13:51:07 +02:00
v = filter ;
}
2017-03-01 23:51:57 +02:00
void validate ( boost : : any & v , const std : : vector < std : : string > & values , inclusion_traverse_filter * , int )
{
po : : validators : : check_first_occurrence ( v ) ;
inclusion_traverse_filter filter ;
parse_filter ( filter , values ) ;
v = filter ;
}
2016-12-16 13:51:07 +02:00
void validate ( boost : : any & v , const std : : vector < std : : string > & values , exclusion_filter * , int )
{
po : : validators : : check_first_occurrence ( v ) ;
exclusion_filter filter ;
2017-02-16 14:28:18 +02:00
parse_filter ( filter , values ) ;
2016-12-16 13:51:07 +02:00
v = filter ;
}
2017-02-20 19:41:52 +02:00
2017-03-01 23:51:57 +02:00
void validate ( boost : : any & v , const std : : vector < std : : string > & values , exclusion_traverse_filter * , int )
{
po : : validators : : check_first_occurrence ( v ) ;
exclusion_traverse_filter filter ;
parse_filter ( filter , values ) ;
v = filter ;
}
2017-02-21 16:04:05 +02:00
/// @todo Clean up this filter initialization code further.
2017-03-01 23:51:57 +02:00
/// @return References to the used filter functors, if none an error occurred.
std : : vector < IfcGeom : : filter_t > setup_filters ( const std : : vector < geom_filter > & filters , const std : : string & output_extension )
2017-02-20 19:41:52 +02:00
{
2017-03-01 23:51:57 +02:00
std : : vector < IfcGeom : : filter_t > filter_funcs ;
2018-09-07 14:08:56 +02:00
for ( auto & f : filters ) {
2017-03-01 23:51:57 +02:00
if ( f . type = = geom_filter : : ENTITY_TYPE ) {
entity_filter . include = f . include ;
entity_filter . traverse = f . traverse ;
2018-09-07 14:08:56 +02:00
entity_filter . entity_names = f . values ;
2017-03-01 23:51:57 +02:00
} else if ( f . type = = geom_filter : : LAYER_NAME ) {
layer_filter . include = f . include ;
layer_filter . traverse = f . traverse ;
layer_filter . populate ( f . values ) ;
} else if ( f . type = = geom_filter : : ENTITY_ARG ) {
2018-09-07 14:08:56 +02:00
attribute_filter . include = f . include ;
2024-07-21 02:41:43 +02:00
attribute_filter . traverse = attribute_filter . traverse_openings = f . traverse ;
2018-09-07 14:08:56 +02:00
attribute_filter . attribute_name = f . arg ;
attribute_filter . populate ( f . values ) ;
2017-02-20 19:41:52 +02:00
}
2017-03-01 23:51:57 +02:00
}
// If no entity names are specified these are the defaults to skip from output
2018-09-07 14:08:56 +02:00
if ( entity_filter . entity_names . empty ( ) ) {
std : : set < std : : string > entities ;
entities . insert ( " IfcSpace " ) ;
if ( output_extension = = " .svg " ) {
entity_filter . include = true ;
} else {
entities . insert ( " IfcOpeningElement " ) ;
2017-02-20 19:41:52 +02:00
}
2018-09-07 14:08:56 +02:00
entity_filter . entity_names = entities ;
2017-02-20 19:41:52 +02:00
}
2017-03-01 23:51:57 +02:00
if ( ! layer_filter . values . empty ( ) ) { filter_funcs . push_back ( boost : : ref ( layer_filter ) ) ; }
2018-09-07 14:08:56 +02:00
if ( ! entity_filter . entity_names . empty ( ) ) { filter_funcs . push_back ( boost : : ref ( entity_filter ) ) ; }
if ( ! attribute_filter . values . empty ( ) ) { filter_funcs . push_back ( boost : : ref ( attribute_filter ) ) ; }
2017-02-20 19:41:52 +02:00
2017-03-01 23:51:57 +02:00
return filter_funcs ;
2017-02-20 19:41:52 +02:00
}
2018-11-02 16:10:06 +01:00
namespace latebound_access {
template < typename T >
void set ( IfcUtil : : IfcBaseClass * inst , const std : : string & attr , T t ) ;
template < typename T >
void set_enumeration ( IfcUtil : : IfcBaseClass * , const std : : string & , const IfcParse : : enumeration_type * , T ) { }
template < >
void set_enumeration ( IfcUtil : : IfcBaseClass * inst , const std : : string & attr , const IfcParse : : enumeration_type * enum_type , std : : string t ) {
std : : vector < std : : string > : : const_iterator it = std : : find (
enum_type - > enumeration_items ( ) . begin ( ) ,
enum_type - > enumeration_items ( ) . end ( ) ,
t ) ;
2024-08-23 20:29:07 +02:00
return set ( inst , attr , EnumerationReference ( enum_type , it - enum_type - > enumeration_items ( ) . begin ( ) ) ) ;
2018-11-02 16:10:06 +01:00
}
template < typename T >
void set ( IfcUtil : : IfcBaseClass * inst , const std : : string & attr , T t ) {
auto decl = inst - > declaration ( ) . as_entity ( ) ;
auto i = decl - > attribute_index ( attr ) ;
auto attr_type = decl - > attribute_by_index ( i ) - > type_of_attribute ( ) ;
2024-08-23 20:29:07 +02:00
if ( attr_type - > as_named_type ( ) & & attr_type - > as_named_type ( ) - > declared_type ( ) - > as_enumeration_type ( ) & & ! std : : is_same < T , EnumerationReference > : : value ) {
2018-11-02 16:10:06 +01:00
set_enumeration ( inst , attr , attr_type - > as_named_type ( ) - > declared_type ( ) - > as_enumeration_type ( ) , t ) ;
2019-06-18 14:28:57 +02:00
} else {
2024-08-23 20:29:07 +02:00
inst - > set_attribute_value ( i , t ) ;
2018-11-02 16:10:06 +01:00
}
}
IfcUtil : : IfcBaseClass * create ( IfcParse : : IfcFile & f , const std : : string & entity ) {
auto decl = f . schema ( ) - > declaration_by_name ( entity ) ;
2025-02-21 16:14:34 +01:00
auto data = IfcEntityInstanceData ( in_memory_attribute_storage ( decl - > as_entity ( ) - > attribute_count ( ) ) ) ;
2024-08-23 20:29:07 +02:00
auto inst = f . schema ( ) - > instantiate ( decl , std : : move ( data ) ) ;
2018-11-02 16:10:06 +01:00
if ( decl - > is ( " IfcRoot " ) ) {
IfcParse : : IfcGlobalId guid ;
latebound_access : : set ( inst , " GlobalId " , ( std : : string ) guid ) ;
}
return f . addEntity ( inst ) ;
}
}
2026-06-10 18:30:54 +02:00
void fix_quantities ( IfcParse : : IfcFile & f , bool no_progress , bool quiet , bool stderr_progress , Logger & logger ) {
2018-11-02 16:10:06 +01:00
{
2021-07-29 14:54:51 +02:00
auto delete_reversed = [ & f ] ( const aggregate_of_instance : : ptr & insts ) {
2018-11-02 16:10:06 +01:00
if ( ! insts ) {
return ;
}
// Lists are traversed back to front as the list may be mutated when
// instances are removed from the grouping by type.
for ( auto it = insts - > end ( ) - 1 ; it > = insts - > begin ( ) ; - - it ) {
IfcUtil : : IfcBaseClass * const inst = * it ;
f . removeEntity ( inst ) ;
}
} ;
// Delete quantities
auto quantities = f . instances_by_type ( " IfcPhysicalQuantity " ) ;
if ( quantities ) {
quantities = quantities - > filtered ( { f . schema ( ) - > declaration_by_name ( " IfcPhysicalComplexQuantity " ) } ) ;
delete_reversed ( quantities ) ;
}
// Delete complexes
delete_reversed ( f . instances_by_type ( " IfcPhysicalComplexQuantity " ) ) ;
auto element_quantities = f . instances_by_type ( " IfcElementQuantity " ) ;
// Capture relationship nodes
std : : vector < IfcUtil : : IfcBaseClass * > relationships ;
auto IfcRelDefinesByProperties = f . schema ( ) - > declaration_by_name ( " IfcRelDefinesByProperties " ) ;
2018-12-10 11:33:54 +01:00
if ( element_quantities ) {
for ( auto & eq : * element_quantities ) {
2024-08-23 20:29:07 +02:00
auto rels = eq - > file_ - > getInverse ( eq - > id ( ) , IfcRelDefinesByProperties , - 1 ) ;
2018-12-10 11:33:54 +01:00
for ( auto & rel : * rels ) {
relationships . push_back ( rel ) ;
}
2018-11-02 16:10:06 +01:00
}
2018-12-10 11:33:54 +01:00
// Delete element quantities
delete_reversed ( element_quantities ) ;
2018-11-02 16:10:06 +01:00
}
// Delete relationship nodes
for ( auto & rel : relationships ) {
f . removeEntity ( rel ) ;
}
}
2023-11-15 10:32:20 +01:00
ifcopenshell : : geometry : : Settings settings ;
settings . get < ifcopenshell : : geometry : : settings : : UseWorldCoords > ( ) . value = false ;
settings . get < ifcopenshell : : geometry : : settings : : WeldVertices > ( ) . value = false ;
settings . get < ifcopenshell : : geometry : : settings : : ReorientShells > ( ) . value = true ;
settings . get < ifcopenshell : : geometry : : settings : : ConvertBackUnits > ( ) . value = true ;
settings . get < ifcopenshell : : geometry : : settings : : IteratorOutput > ( ) . value = ifcopenshell : : geometry : : settings : : NATIVE ;
2018-11-02 16:10:06 +01:00
2026-06-10 18:30:54 +02:00
IfcGeom : : Iterator context_iterator ( ifcopenshell : : geometry : : kernels : : construct ( & f , " opencascade " , settings , logger ) , settings , & f , { } , 1 , logger ) ;
2018-11-02 16:10:06 +01:00
if ( ! context_iterator . initialize ( ) ) {
return ;
}
size_t num_created = 0 ;
int old_progress = quiet ? 0 : - 1 ;
auto person = latebound_access : : create ( f , " IfcPerson " ) ;
latebound_access : : set ( person , " FamilyName " , std : : string ( " IfcOpenShell " ) ) ;
latebound_access : : set ( person , " GivenName " , std : : string ( " IfcOpenShell " ) ) ;
auto org = latebound_access : : create ( f , " IfcOrganization " ) ;
latebound_access : : set ( org , " Name " , std : : string ( " IfcOpenShell " ) ) ;
auto pando = latebound_access : : create ( f , " IfcPersonAndOrganization " ) ;
latebound_access : : set ( pando , " ThePerson " , person ) ;
latebound_access : : set ( pando , " TheOrganization " , org ) ;
auto application = latebound_access : : create ( f , " IfcApplication " ) ;
latebound_access : : set ( application , " ApplicationDeveloper " , org ) ;
latebound_access : : set ( application , " Version " , std : : string ( IFCOPENSHELL_VERSION ) ) ;
latebound_access : : set ( application , " ApplicationFullName " , std : : string ( " IfcConvert " ) ) ;
2025-09-30 18:11:33 +05:00
latebound_access : : set ( application , " ApplicationIdentifier " , std : : string ( " IfcConvert " ) + IFCOPENSHELL_VERSION ) ;
2018-11-02 16:10:06 +01:00
auto ownerhist = latebound_access : : create ( f , " IfcOwnerHistory " ) ;
latebound_access : : set ( ownerhist , " OwningUser " , pando ) ;
latebound_access : : set ( ownerhist , " OwningApplication " , application ) ;
latebound_access : : set ( ownerhist , " ChangeAction " , std : : string ( " MODIFIED " ) ) ;
latebound_access : : set ( ownerhist , " CreationDate " , ( int ) time ( 0 ) ) ;
IfcUtil : : IfcBaseClass * quantity = nullptr ;
2021-07-29 14:54:51 +02:00
aggregate_of_instance : : ptr objects ;
2018-11-02 16:10:06 +01:00
boost : : shared_ptr < IfcGeom : : Representation : : BRep > previous_geometry_pointer ;
2018-12-12 12:33:09 +01:00
for ( ; ; + + num_created ) {
bool has_more = true ;
if ( num_created ) {
has_more = context_iterator . next ( ) ;
}
2021-07-11 15:00:59 +02:00
IfcGeom : : BRepElement * geom_object = nullptr ;
2018-12-12 12:33:09 +01:00
if ( has_more ) {
geom_object = context_iterator . get_native ( ) ;
}
2018-11-02 16:10:06 +01:00
2018-12-12 12:33:09 +01:00
if ( geom_object & & geom_object - > geometry_pointer ( ) = = previous_geometry_pointer ) {
2023-03-21 20:15:01 +01:00
// @todo
objects - > push ( const_cast < IfcUtil : : IfcBaseEntity * > ( geom_object - > product ( ) ) ) ;
2018-11-02 16:10:06 +01:00
} else {
if ( quantity ) {
auto rel = latebound_access : : create ( f , " IfcRelDefinesByProperties " ) ;
latebound_access : : set ( rel , " OwnerHistory " , ownerhist ) ;
latebound_access : : set ( rel , " RelatedObjects " , objects ) ;
latebound_access : : set ( rel , " RelatingPropertyDefinition " , quantity ) ;
}
2018-12-12 12:33:09 +01:00
if ( ! geom_object ) {
break ;
}
2021-07-29 14:54:51 +02:00
aggregate_of_instance : : ptr quantities ( new aggregate_of_instance ) ;
2018-11-02 16:10:06 +01:00
double a , b , c ;
if ( geom_object - > geometry ( ) . calculate_surface_area ( a ) ) {
auto quantity_area = latebound_access : : create ( f , " IfcQuantityArea " ) ;
latebound_access : : set ( quantity_area , " Name " , std : : string ( " Total Surface Area " ) ) ;
latebound_access : : set ( quantity_area , " AreaValue " , a ) ;
quantities - > push ( quantity_area ) ;
}
if ( geom_object - > geometry ( ) . calculate_volume ( a ) ) {
auto quantity_volume = latebound_access : : create ( f , " IfcQuantityVolume " ) ;
latebound_access : : set ( quantity_volume , " Name " , std : : string ( " Volume " ) ) ;
latebound_access : : set ( quantity_volume , " VolumeValue " , a ) ;
quantities - > push ( quantity_volume ) ;
}
if ( geom_object - > calculate_projected_surface_area ( a , b , c ) ) {
auto quantity_area = latebound_access : : create ( f , " IfcQuantityArea " ) ;
latebound_access : : set ( quantity_area , " Name " , std : : string ( " Footprint Area " ) ) ;
latebound_access : : set ( quantity_area , " AreaValue " , c ) ;
quantities - > push ( quantity_area ) ;
}
2018-12-12 12:33:09 +01:00
auto quantity_complex = latebound_access : : create ( f , " IfcPhysicalComplexQuantity " ) ;
latebound_access : : set ( quantity_complex , " Name " , std : : string ( " Shape Validation Properties " ) ) ;
quantities - > push ( quantity_complex ) ;
2018-12-10 11:33:54 +01:00
2021-07-29 14:54:51 +02:00
aggregate_of_instance : : ptr quantities_2 ( new aggregate_of_instance ) ;
2018-12-10 11:33:54 +01:00
2018-12-12 12:33:09 +01:00
for ( auto & part : geom_object - > geometry ( ) ) {
auto quantity_count = latebound_access : : create ( f , " IfcQuantityCount " ) ;
latebound_access : : set ( quantity_count , " Name " , std : : string ( " Surface Genus " ) ) ;
latebound_access : : set ( quantity_count , " Description " , ' # ' + boost : : lexical_cast < std : : string > ( part . ItemId ( ) ) ) ;
2023-03-21 20:15:01 +01:00
latebound_access : : set ( quantity_count , " CountValue " , part . Shape ( ) - > surface_genus ( ) ) ;
2018-12-10 11:33:54 +01:00
2018-12-12 12:33:09 +01:00
quantities_2 - > push ( quantity_count ) ;
2018-12-10 11:33:54 +01:00
}
2018-12-12 12:33:09 +01:00
latebound_access : : set ( quantity_complex , " HasQuantities " , quantities_2 ) ;
2018-11-02 16:10:06 +01:00
if ( quantities - > size ( ) ) {
quantity = latebound_access : : create ( f , " IfcElementQuantity " ) ;
latebound_access : : set ( quantity , " OwnerHistory " , ownerhist ) ;
latebound_access : : set ( quantity , " Quantities " , quantities ) ;
}
2021-07-29 14:54:51 +02:00
objects . reset ( new aggregate_of_instance ) ;
2023-03-21 20:15:01 +01:00
// @todo
objects - > push ( const_cast < IfcUtil : : IfcBaseEntity * > ( geom_object - > product ( ) ) ) ;
2018-11-02 16:10:06 +01:00
}
previous_geometry_pointer = geom_object - > geometry_pointer ( ) ;
if ( ! no_progress ) {
if ( quiet ) {
const int progress = context_iterator . progress ( ) ;
for ( ; old_progress < progress ; + + old_progress ) {
std : : cout < < " . " ;
if ( stderr_progress )
2020-07-21 23:23:39 +02:00
cerr_ < < " . " ;
2018-11-02 16:10:06 +01:00
}
std : : cout < < std : : flush ;
if ( stderr_progress )
2020-07-21 23:23:39 +02:00
cerr_ < < std : : flush ;
2018-11-02 16:10:06 +01:00
} else {
const int progress = context_iterator . progress ( ) / 2 ;
2026-06-10 18:30:54 +02:00
if ( old_progress ! = progress ) logger . ProgressBar ( progress ) ;
2018-11-02 16:10:06 +01:00
old_progress = progress ;
}
}
2018-12-12 12:33:09 +01:00
}
2018-12-10 11:33:54 +01:00
if ( ! no_progress & & quiet ) {
for ( ; old_progress < 100 ; + + old_progress ) {
std : : cout < < " . " ;
if ( stderr_progress )
2020-07-21 23:23:39 +02:00
cerr_ < < " . " ;
2018-12-10 11:33:54 +01:00
}
std : : cout < < std : : flush ;
if ( stderr_progress )
2020-07-21 23:23:39 +02:00
cerr_ < < std : : flush ;
2018-12-10 11:33:54 +01:00
} else {
2026-06-10 18:30:54 +02:00
logger . Status ( " \r Done writing quantities for " + boost : : lexical_cast < std : : string > ( num_created ) +
2018-12-10 11:33:54 +01:00
" objects " ) ;
}
2018-11-02 16:10:06 +01:00
}