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/>. *
* *
********************************************************************************/
/********************************************************************************
* *
* This started as a brief example of how IfcOpenShell can be interfaced from *
* 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 *
* several tesselated and topological output formats. *
* *
********************************************************************************/
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"
2015-07-26 14:26:13 +00:00
2018-09-07 14:08:56 +02:00
# include "../ifcgeom_schema_agnostic/IfcGeomFilter.h"
2017-12-20 17:09:42 +01:00
# include "../ifcgeom_schema_agnostic/IfcGeomIterator.h"
2018-10-30 14:32:28 +01:00
# include "../ifcgeom_schema_agnostic/IfcGeomRenderStyles.h"
2013-03-24 10:48:39 +00:00
2019-02-08 15:18:55 +01:00
# include "../ifcparse/utils.h"
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
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>
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 ( )
{
2019-03-08 11:40:16 +01:00
cout_ < < " IfcOpenShell IfcConvert " < < IFCOPENSHELL_VERSION < < " (OCC " < < OCC_VERSION_STRING_EXT < < " ) \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 "
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 "
2017-02-21 16:04:05 +02: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
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 ) ;
2018-11-02 16:10:06 +01:00
void fix_quantities ( IfcParse : : IfcFile & , bool , bool , bool ) ;
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
2017-12-21 14:14:04 +01:00
bool init_input_file ( const std : : string & filename , IfcParse : : IfcFile * & ifc_file , bool no_progress , bool mmap ) ;
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
2020-09-27 10:43:11 +02:00
double deflection_tolerance , angular_tolerance , force_space_transparency ;
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 ;
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 " )
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
;
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 ;
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 ( )
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. " )
2015-04-22 09:15:48 +00:00
( " plan " ,
" Specifies whether to include curves in the output result. Typically "
" these are representations of type Plan or Axis. Excluded by default. " )
( " model " ,
2015-07-26 17:58:08 +00:00
" Specifies whether to include surfaces and solids in the output result. "
2015-04-22 09:15:48 +00:00
" Typically these are representations of type Body or Facetation. "
" Included by default. " )
2015-02-05 11:55:25 +00:00
( " weld-vertices " ,
" Specifies whether vertices are welded, meaning that the coordinates "
" vector will only contain unique xyz-triplets. This results in a "
" manifold mesh which is useful for modelling applications, but might "
" result in unwanted shading artefacts in rendering applications. " )
2017-12-20 12:40:15 +01:00
( " use-world-coords " ,
2015-02-05 11:55:25 +00:00
" Specifies whether to apply the local placements of building elements "
" directly to the coordinates of the representation mesh rather than "
" to represent the local placement in the 4x3 matrix, which will in that "
" case be the identity matrix. " )
( " convert-back-units " ,
" Specifies whether to convert back geometrical output back to the "
" unit of measure in which it is defined in the IFC file. Default is "
" to use meters. " )
2019-05-24 14:29:21 +02:00
( " orient-shells " ,
" Specifies whether to orient the faces of IfcConnectedFaceSets. "
" This is a potentially time consuming operation, but guarantees a "
" consistent orientation of surface normals, even if the faces are not "
" properly oriented in the IFC file. " )
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. " )
2020-02-13 19:23:28 +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. " )
2016-01-25 13:30:33 +01:00
# if OCC_VERSION_HEX < 0x60900
// In Open CASCADE version prior to 6.9.0 boolean operations with multiple
// arguments where not introduced yet and a work-around was implemented to
// subtract multiple openings as a single compound. This hack is obsolete
// for newer versions of Open CASCADE.
2017-12-20 12:40:15 +01:00
( " merge-boolean-operands " ,
2015-02-05 11:55:25 +00:00
" Specifies whether to merge all IfcOpeningElement operands into a single "
" operand before applying the subtraction operation. This may "
" introduce a performance improvement at the risk of failing, in "
" which case the subtraction is applied one-by-one. " )
2016-01-25 13:30:33 +01:00
# endif
2017-12-20 12:40:15 +01:00
( " disable-opening-subtractions " ,
2015-02-05 11:55:25 +00:00
" Specifies whether to disable the boolean subtraction of "
" IfcOpeningElement Representations from their RelatingElements. " )
2020-11-24 16:58:05 +01:00
( " disable-boolean-results " ,
" Specifies whether to disable the boolean operation within representations "
" such as clippings by means of IfcBooleanResult and subtypes " )
2017-12-20 12:40:15 +01:00
( " enable-layerset-slicing " ,
2015-10-07 17:33:30 +02:00
" Specifies whether to enable the slicing of products according "
" to their associated IfcMaterialLayerSet. " )
2020-06-07 19:46:10 +02:00
( " layerset-first " , " Assigns the first layer material of the layerset "
" to the complete product. " )
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. " )
2017-12-20 12:40:15 +01:00
( " no-normals " ,
" Disables computation of normals. Saves time and file size and is useful "
" in instances where you're going to recompute normals for the exported "
" model in other modelling application in any case. " )
( " deflection-tolerance " , po : : value < double > ( & deflection_tolerance ) - > default_value ( 1e-3 ) ,
" Sets the deflection tolerance of the mesher, 1e-3 by default if not specified. " )
2020-09-27 10:43:11 +02:00
( " force-space-transparency " , po : : value < double > ( & force_space_transparency ) ,
" Overrides transparency of spaces in geometry output. " )
2020-09-14 14:03:35 +02:00
( " angular-tolerance " , po : : value < double > ( & angular_tolerance ) - > default_value ( 0.5 ) ,
" Sets the angular tolerance of the mesher in radians 0.5 by default if not specified. " )
2017-12-20 12:40:15 +01:00
( " generate-uvs " ,
" Generates UVs (texture coordinates) by using simple box projection. Requires normals. "
2018-10-30 14:32:28 +01:00
" Not guaranteed to work properly if used with --weld-vertices. " )
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. " )
2021-02-05 14:00:23 -06:00
( " validate " , " Checks whether geometrical output conforms to the included explicit quantities. " )
2021-02-08 14:06:05 -06:00
( " no-wire-intersection-check " , " Skip wire intersection check. " )
( " no-wire-intersection-tolerance " , " Set wire intersection tolerance to 0. " )
2021-02-06 21:44:13 -06:00
( " strict-tolerance " , " Use exact tolerance from model. Default is a 10 "
2021-02-08 14:06:05 -06:00
" times increase for more permissive edge curves and fewer artifacts after "
" boolean operations at the expense of geometric detail "
" due to vertex collapsing and wire intersection fuzziness. " ) ;
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 ;
SvgSerializer : : storey_height_display_types svg_storey_height_display = SvgSerializer : : SH_NONE ;
2020-07-11 13:39:09 +02:00
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 " )
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-07-11 13:39:09 +02:00
( " use-element-names " ,
2020-08-16 14:35:39 +02:00
" Use entity instance IfcRoot.Name instead of unique IDs for naming elements upon serialization. "
2016-03-10 12:01:04 +02:00
" Applicable for OBJ, DAE, and SVG output. " )
( " use-element-guids " ,
2020-08-16 14:35:39 +02:00
" Use entity instance IfcRoot.GlobalId instead of unique IDs for naming elements upon serialization. "
2016-03-10 12:01:04 +02:00
" Applicable for OBJ, DAE, and SVG output. " )
2020-08-16 14:35:39 +02:00
( " use-element-numeric-ids " , " Use the numeric step identifier (entity instance name) for naming elements upon serialization. "
" Applicable for OBJ, DAE, and SVG output. " )
2016-03-10 12:01:04 +02:00
( " use-material-names " ,
" Use material names instead of unique IDs for naming materials upon serialization. "
" Applicable for OBJ and DAE output. " )
2017-04-19 12:07:03 +02:00
( " use-element-types " ,
2017-04-19 16:38:35 +02:00
" Use element types instead of unique IDs for naming elements upon serialization. "
2017-04-19 12:07:03 +02:00
" Applicable for DAE output. " )
( " use-element-hierarchy " ,
" Order the elements using their IfcBuildingStorey parent. "
" Applicable for DAE output. " )
2017-08-19 17:21:10 +02:00
( " site-local-placement " ,
" Place elements locally in the IfcSite coordinate system, instead of placing "
" them in the IFC global coords. Applicable for OBJ and DAE output. " )
2021-02-14 17:20:55 +01:00
( " y-up " , " Change the 'up' axis to positive Y, default is Z UP, Applicable for OBJ output. " )
2018-02-14 15:17:37 +01:00
( " building-local-placement " ,
" Similar to --site-local-placement, but placing elements in locally in the parent IfcBuilding coord system " )
2016-12-16 13:51:07 +02:00
( " precision " , po : : value < short > ( & precision ) - > default_value ( SerializerSettings : : DEFAULT_PRECISION ) ,
2017-01-12 15:47:44 +02:00
" Sets the precision to be used to format floating-point values, 15 by default. "
2017-01-11 16:36:33 +02:00
" Use a negative value to use the system's default precision (should be 6 typically). "
" Applicable for OBJ and DAE output. For DAE output, value >= 15 means that up to 16 decimals are used, "
2020-04-01 12:19:56 +02:00
" and any other value means that 6 or 7 decimals are used. " )
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
( " edge-arrows " , " Adds arrow heads to edge segments to signify edge direction " )
;
2016-03-07 22:37:36 +02:00
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 ;
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 ;
2013-03-24 10:48:39 +00:00
const bool weld_vertices = vmap . count ( " weld-vertices " ) ! = 0 ;
const bool use_world_coords = vmap . count ( " use-world-coords " ) ! = 0 ;
const bool convert_back_units = vmap . count ( " convert-back-units " ) ! = 0 ;
2019-05-24 14:29:21 +02:00
const bool orient_shells = vmap . count ( " orient-shells " ) ! = 0 ;
2016-01-25 13:30:33 +01:00
# if OCC_VERSION_HEX < 0x60900
2013-03-24 10:48:39 +00:00
const bool merge_boolean_operands = vmap . count ( " merge-boolean-operands " ) ! = 0 ;
2016-01-25 13:30:33 +01:00
# endif
2013-12-27 09:37:51 +00:00
const bool disable_opening_subtractions = vmap . count ( " disable-opening-subtractions " ) ! = 0 ;
2020-11-24 16:58:05 +01:00
const bool disable_boolean_results = vmap . count ( " disable-boolean-results " ) ! = 0 ;
2015-04-22 09:15:48 +00:00
const bool include_plan = vmap . count ( " plan " ) ! = 0 ;
const bool include_model = vmap . count ( " model " ) ! = 0 | | ( ! include_plan ) ;
2015-10-07 17:33:30 +02:00
const bool enable_layerset_slicing = vmap . count ( " enable-layerset-slicing " ) ! = 0 ;
2020-06-07 19:46:10 +02:00
const bool layerset_first = vmap . count ( " layerset-first " ) ! = 0 ;
2018-05-12 20:12:25 +02:00
const bool use_element_names = vmap . count ( " use-element-names " ) ! = 0 ;
const bool use_element_guids = vmap . count ( " use-element-guids " ) ! = 0 ;
2020-08-16 14:35:39 +02:00
const bool use_element_stepids = vmap . count ( " use-element-numeric-ids " ) ! = 0 ;
2018-05-12 20:12:25 +02:00
const bool use_material_names = vmap . count ( " use-material-names " ) ! = 0 ;
2017-04-19 12:07:03 +02:00
const bool use_element_types = vmap . count ( " use-element-types " ) ! = 0 ;
const bool use_element_hierarchy = vmap . count ( " use-element-hierarchy " ) ! = 0 ;
2021-01-09 15:08:30 +01:00
const bool use_y_up = vmap . count ( " y-up " ) ! = 0 ;
2018-05-12 20:12:25 +02:00
const bool no_normals = vmap . count ( " no-normals " ) ! = 0 ;
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 ;
2018-05-12 20:12:25 +02:00
const bool model_offset = vmap . count ( " model-offset " ) ! = 0 ;
2020-01-31 15:55:04 +01:00
const bool model_rotation = vmap . count ( " model-rotation " ) ! = 0 ;
2018-05-12 20:12:25 +02:00
const bool site_local_placement = vmap . count ( " site-local-placement " ) ! = 0 ;
const bool building_local_placement = vmap . count ( " building-local-placement " ) ! = 0 ;
const bool generate_uvs = vmap . count ( " generate-uvs " ) ! = 0 ;
2018-12-12 12:33:09 +01:00
const bool validate = vmap . count ( " validate " ) ! = 0 ;
2020-06-15 16:24:16 +02:00
const bool edge_arrows = vmap . count ( " edge-arrows " ) ! = 0 ;
2021-02-05 14:00:23 -06:00
const bool no_wire_intersection_check = vmap . count ( " no-wire-intersection-check " ) ! = 0 ;
2021-02-08 14:06:05 -06:00
const bool no_wire_intersection_tolerance = vmap . count ( " no-wire-intersection-tolerance " ) ! = 0 ;
2021-02-06 10:26:17 -06:00
const bool strict_tolerance = vmap . count ( " strict-tolerance " ) ! = 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
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 ;
}
}
2019-02-08 15:18:55 +01:00
if ( vmap . count ( " log-format " ) = = 1 ) {
boost : : to_lower ( log_format ) ;
if ( log_format = = " plain " ) {
Logger : : OutputFormat ( Logger : : FMT_PLAIN ) ;
} else if ( log_format = = " json " ) {
Logger : : OutputFormat ( Logger : : FMT_JSON ) ;
} 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 ) {
Logger : : Notice ( boost : : lexical_cast < std : : string > ( num_filters ) + " filters read from specifified file. " ) ;
} 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 > ( ) ;
if ( ! file_exists ( IfcUtil : : path : : to_utf8 ( input_filename ) ) ) {
cerr_ < < " [Error] Input file ' " < < input_filename < < " ' does not exist " < < std : : endl ;
2017-02-20 19:41:52 +02:00
return EXIT_FAILURE ;
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 ;
2019-02-08 15:18:55 +01:00
cout_ < < " A file ' " < < output_filename < < " ' already exists. Overwrite the existing file? " < < 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 ) ;
Logger : : SetOutput ( quiet ? nullptr : & cout_ , & log_fs ) ;
} else {
Logger : : SetOutput ( quiet ? nullptr : & cout_ , vcounter . count > 1 ? & cout_ : & log_stream ) ;
}
2020-08-16 13:37:58 +02:00
Logger : : Verbosity ( vcounter . count
? ( vcounter . count > 1
? Logger : : LOG_DEBUG
: Logger : : LOG_NOTICE )
: Logger : : LOG_ERROR
) ;
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 ( " . " ) ) ;
2021-02-20 15:35:33 +01:00
std : : vector < path_t > : : iterator tok_iter ;
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 ;
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 " ) ,
2019-03-08 11:40:16 +01:00
IFC = IfcUtil : : path : : from_utf8 ( " .ifc " ) ;
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
2019-03-08 11:40:16 +01:00
if ( output_extension = = XML ) {
2018-11-02 16:10:06 +01:00
int exit_code = EXIT_FAILURE ;
try {
2019-03-08 11:40:16 +01:00
if ( init_input_file ( IfcUtil : : path : : to_utf8 ( input_filename ) , ifc_file , no_progress | | quiet , mmap ) ) {
2019-02-06 15:39:43 +01:00
time_t start , end ;
time ( & start ) ;
2019-03-08 11:40:16 +01:00
XmlSerializer s ( ifc_file , IfcUtil : : path : : to_utf8 ( output_temp_filename ) ) ;
2018-11-02 16:10:06 +01:00
Logger : : Status ( " Writing XML output... " ) ;
s . finalize ( ) ;
2019-02-06 15:39:43 +01:00
time ( & end ) ;
Logger : : Status ( " Done! Conversion took " + format_duration ( start , end ) ) ;
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 ) {
2018-03-16 16:04:48 +01:00
Logger : : Error ( e ) ;
}
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 {
2019-03-08 17:22:05 +01:00
if ( init_input_file ( IfcUtil : : path : : to_utf8 ( input_filename ) , ifc_file , no_progress | | quiet , mmap ) ) {
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 " ) ) {
fix_quantities ( * ifc_file , no_progress , quiet , stderr_progress ) ;
}
fs < < * ifc_file ;
2018-12-10 11:33:54 +01:00
exit_code = EXIT_SUCCESS ;
2018-11-02 16:10:06 +01:00
} else {
Logger : : Error ( " Unable to open output file for writing " ) ;
}
2018-10-01 13:19:31 +03:00
time ( & end ) ;
2019-03-08 11:40:16 +01:00
Logger : : Status ( " Done! Writing IFC took " + format_duration ( start , end ) ) ;
2018-11-02 16:10:06 +01:00
}
} catch ( const std : : exception & e ) {
Logger : : Error ( e ) ;
}
write_log ( ! quiet ) ;
return exit_code ;
}
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
}
2018-09-07 14:08:56 +02:00
if ( ! entity_filter . entity_names . empty ( ) ) { entity_filter . update_description ( ) ; Logger : : Notice ( entity_filter . description ) ; }
2017-02-24 15:01:54 +02:00
if ( ! layer_filter . values . empty ( ) ) { layer_filter . update_description ( ) ; Logger : : Notice ( layer_filter . description ) ; }
2019-11-05 14:43:37 +01:00
if ( ! attribute_filter . attribute_name . empty ( ) ) { attribute_filter . update_description ( ) ; Logger : : Notice ( 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
}
}
2017-02-22 17:39:32 +02:00
SerializerSettings settings ;
2016-03-07 22:37:36 +02:00
/// @todo Make APPLY_DEFAULT_MATERIALS configurable? Quickly tested setting this to false and using obj exporter caused the program to crash and burn.
2014-12-19 12:14:36 +00:00
settings . set ( IfcGeom : : IteratorSettings : : APPLY_DEFAULT_MATERIALS , true ) ;
2020-07-11 23:26:21 +02:00
settings . set ( IfcGeom : : IteratorSettings : : USE_WORLD_COORDS , use_world_coords | | output_extension = = OBJ ) ;
2014-12-19 12:14:36 +00:00
settings . set ( IfcGeom : : IteratorSettings : : WELD_VERTICES , weld_vertices ) ;
2021-11-05 11:07:27 +01:00
settings . set ( IfcGeom : : IteratorSettings : : SEW_SHELLS , orient_shells | | output_extension = = SVG ) ; // svg depends on correct solids for boolean subtractions for hlr
2014-12-19 12:14:36 +00:00
settings . set ( IfcGeom : : IteratorSettings : : CONVERT_BACK_UNITS , convert_back_units ) ;
settings . set ( IfcGeom : : IteratorSettings : : DISABLE_OPENING_SUBTRACTIONS , disable_opening_subtractions ) ;
2020-11-24 16:58:05 +01:00
settings . set ( IfcGeom : : IteratorSettings : : DISABLE_BOOLEAN_RESULT , disable_boolean_results ) ;
2015-04-22 09:15:48 +00:00
settings . set ( IfcGeom : : IteratorSettings : : INCLUDE_CURVES , include_plan ) ;
settings . set ( IfcGeom : : IteratorSettings : : EXCLUDE_SOLIDS_AND_SURFACES , ! include_model ) ;
2015-10-07 17:33:30 +02:00
settings . set ( IfcGeom : : IteratorSettings : : APPLY_LAYERSETS , enable_layerset_slicing ) ;
2020-06-07 19:46:10 +02:00
settings . set ( IfcGeom : : IteratorSettings : : LAYERSET_FIRST , layerset_first ) ;
2016-03-10 13:29:22 +02:00
settings . set ( IfcGeom : : IteratorSettings : : NO_NORMALS , no_normals ) ;
2016-03-10 16:46:20 +02:00
settings . set ( IfcGeom : : IteratorSettings : : GENERATE_UVS , generate_uvs ) ;
2020-06-15 16:24:16 +02:00
settings . set ( IfcGeom : : IteratorSettings : : EDGE_ARROWS , edge_arrows ) ;
2021-12-18 16:16:44 +01:00
settings . set ( IfcGeom : : IteratorSettings : : ELEMENT_HIERARCHY , use_element_hierarchy | | output_extension = = SVG ) ;
2017-08-19 17:21:10 +02:00
settings . set ( IfcGeom : : IteratorSettings : : SITE_LOCAL_PLACEMENT , site_local_placement ) ;
2018-02-14 15:17:37 +01:00
settings . set ( IfcGeom : : IteratorSettings : : BUILDING_LOCAL_PLACEMENT , building_local_placement ) ;
2018-12-12 12:33:09 +01:00
settings . set ( IfcGeom : : IteratorSettings : : VALIDATE_QUANTITIES , validate ) ;
2021-02-05 14:00:23 -06:00
settings . set ( IfcGeom : : IteratorSettings : : NO_WIRE_INTERSECTION_CHECK , no_wire_intersection_check ) ;
2021-02-08 14:06:05 -06:00
settings . set ( IfcGeom : : IteratorSettings : : NO_WIRE_INTERSECTION_TOLERANCE , no_wire_intersection_tolerance ) ;
2021-02-06 10:26:17 -06:00
settings . set ( IfcGeom : : IteratorSettings : : STRICT_TOLERANCE , strict_tolerance ) ;
2017-01-11 16:36:33 +02:00
2017-01-11 12:00:51 +02:00
settings . set ( SerializerSettings : : USE_ELEMENT_NAMES , use_element_names ) ;
settings . set ( SerializerSettings : : USE_ELEMENT_GUIDS , use_element_guids ) ;
2021-01-09 15:08:30 +01:00
settings . set ( SerializerSettings : : USE_Y_UP , use_y_up ) ;
2020-08-16 14:35:39 +02:00
settings . set ( SerializerSettings : : USE_ELEMENT_STEPIDS , use_element_stepids ) ;
settings . set ( SerializerSettings : : USE_MATERIAL_NAMES , use_material_names ) ;
2017-04-19 12:07:03 +02:00
settings . set ( SerializerSettings : : USE_ELEMENT_TYPES , use_element_types ) ;
settings . set ( SerializerSettings : : USE_ELEMENT_HIERARCHY , use_element_hierarchy ) ;
2017-02-22 17:39:32 +02:00
settings . set_deflection_tolerance ( deflection_tolerance ) ;
2020-09-14 14:03:35 +02:00
settings . set_angular_tolerance ( angular_tolerance ) ;
settings . precision = precision ;
2013-03-24 10:48:39 +00:00
2020-09-27 10:43:11 +02:00
if ( vmap . count ( " force-space-transparency " ) ) {
settings . force_space_transparency ( force_space_transparency ) ;
2020-09-27 16:01:59 +02:00
IfcGeom : : update_default_style ( " IfcSpace " ) . Transparency ( ) . reset ( force_space_transparency ) ;
2020-09-27 10:43:11 +02:00
}
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 ) ;
serializer = boost : : make_shared < WaveFrontOBJSerializer > ( IfcUtil : : path : : to_utf8 ( output_temp_filename ) , IfcUtil : : path : : to_utf8 ( mtl_filename ) , settings ) ;
2013-11-22 08:23:11 +00:00
# ifdef WITH_OPENCOLLADA
2019-02-08 15:18:55 +01:00
} else if ( output_extension = = DAE ) {
serializer = boost : : make_shared < ColladaSerializer > ( IfcUtil : : path : : to_utf8 ( output_temp_filename ) , settings ) ;
2019-05-07 17:06:45 +02:00
# endif
# ifdef WITH_GLTF
} else if ( output_extension = = GLB ) {
serializer = boost : : make_shared < GltfSerializer > ( IfcUtil : : path : : to_utf8 ( output_temp_filename ) , settings ) ;
2013-11-22 08:23:11 +00:00
# endif
2019-02-08 15:18:55 +01:00
} else if ( output_extension = = STP ) {
serializer = boost : : make_shared < StepSerializer > ( IfcUtil : : path : : to_utf8 ( output_temp_filename ) , settings ) ;
} 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
2019-02-08 15:18:55 +01:00
serializer = boost : : make_shared < IgesSerializer > ( IfcUtil : : path : : to_utf8 ( output_temp_filename ) , settings ) ;
} else if ( output_extension = = SVG ) {
2015-07-26 14:26:13 +00:00
settings . set ( IfcGeom : : IteratorSettings : : DISABLE_TRIANGULATION , true ) ;
2019-02-08 15:18:55 +01:00
serializer = boost : : make_shared < SvgSerializer > ( IfcUtil : : path : : to_utf8 ( output_temp_filename ) , settings ) ;
2021-02-12 17:32:43 +01:00
}
2021-08-31 11:03:28 +02:00
# ifdef WITH_HDF5
2021-02-12 17:32:43 +01:00
else if ( output_extension = = HDF ) {
2021-02-19 11:52:32 +01:00
settings . set ( IfcGeom : : IteratorSettings : : DISABLE_TRIANGULATION , true ) ;
2021-02-19 17:03:41 +01:00
serializer = boost : : make_shared < HdfSerializer > ( IfcUtil : : path : : to_utf8 ( output_temp_filename ) , settings ) ;
2021-02-12 17:32:43 +01:00
}
2021-08-31 11:03:28 +02:00
# endif
2021-02-12 17:32:43 +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
}
2019-02-08 15:18:55 +01:00
if ( use_element_hierarchy & & output_extension ! = DAE ) {
cerr_ < < " [Error] --use-element-hierarchy can be used only with .dae output. \n " ;
2018-10-08 10:39:27 +03:00
/// @todo Lots of duplicate error-and-exit code.
2018-05-12 20:12:25 +02:00
write_log ( ! quiet ) ;
2017-04-25 10:13:18 +02:00
print_usage ( ) ;
2019-02-08 15:18:55 +01:00
IfcUtil : : path : : delete_file ( IfcUtil : : path : : to_utf8 ( output_temp_filename ) ) ;
2017-04-25 10:13:18 +02:00
return EXIT_FAILURE ;
}
2016-03-17 23:25:53 +02:00
const bool is_tesselated = serializer - > isTesselated ( ) ; // isTesselated() doesn't change at run-time
if ( ! is_tesselated ) {
2015-02-23 22:01:21 +00:00
if ( weld_vertices ) {
2017-02-24 15:01:54 +02:00
Logger : : Notice ( " Weld vertices setting ignored when writing non-tesselated output " ) ;
2015-02-23 22:01:21 +00:00
}
2016-03-17 00:25:33 +02:00
if ( generate_uvs ) {
2017-02-24 15:01:54 +02:00
Logger : : Notice ( " Generate UVs setting ignored when writing non-tesselated output " ) ;
2016-03-17 00:25:33 +02:00
}
2020-02-12 11:38:08 +01:00
if ( center_model | | center_model_geometry | | model_offset ) {
2017-02-24 15:01:54 +02:00
Logger : : Notice ( " Centering/offsetting model setting ignored when writing non-tesselated output " ) ;
2016-03-17 23:25:53 +02:00
}
2016-03-07 22:37:36 +02:00
settings . set ( IfcGeom : : IteratorSettings : : DISABLE_TRIANGULATION , true ) ;
2015-02-23 22:01:21 +00:00
}
2013-03-24 10:48:39 +00:00
if ( ! serializer - > ready ( ) ) {
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
2019-02-08 15:18:55 +01:00
if ( ! init_input_file ( IfcUtil : : path : : to_utf8 ( input_filename ) , ifc_file , no_progress | | quiet , mmap ) ) {
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 ;
}
2019-07-19 15:55:22 +02:00
if ( num_threads < = 0 ) {
2019-07-09 16:00:58 +02:00
num_threads = std : : thread : : hardware_concurrency ( ) ;
Logger : : Notice ( " Using " + std : : to_string ( num_threads ) + " threads " ) ;
}
2020-08-16 16:17:38 +02:00
if ( vmap . count ( " log-file " ) ) {
Logger : : SetOutput ( quiet ? nullptr : & cout_ , & log_fs ) ;
} else {
Logger : : SetOutput ( quiet ? nullptr : & cout_ , vcounter . count > 1 ? & cout_ : & log_stream ) ;
}
2019-07-09 16:00:58 +02:00
2020-01-31 15:55:04 +01:00
if ( model_rotation ) {
std : : array < double , 4 > & rotation = settings . rotation ;
if ( sscanf ( rotation_str . c_str ( ) , " %lf;%lf;%lf;%lf " , & rotation [ 0 ] , & rotation [ 1 ] , & rotation [ 2 ] , & rotation [ 3 ] ) ! = 4 ) {
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 ;
}
2015-07-26 14:26:13 +00:00
2020-01-31 15:55:04 +01:00
std : : stringstream msg ;
msg < < " Using model rotation ( " < < rotation [ 0 ] < < " , " < < rotation [ 1 ] < < " , " < < rotation [ 2 ] < < " , " < < rotation [ 3 ] < < " ) " ;
Logger : : Notice ( msg . str ( ) ) ;
2013-12-31 14:10:38 +00:00
}
2020-01-31 15:55:04 +01:00
2020-02-12 11:38:08 +01:00
if ( is_tesselated & & ( center_model | | center_model_geometry | | model_offset ) ) {
2020-01-31 15:55:04 +01:00
std : : array < double , 3 > & offset = settings . offset ;
2020-02-12 11:38:08 +01:00
if ( center_model | | center_model_geometry ) {
2018-02-14 15:17:37 +01:00
if ( site_local_placement | | building_local_placement ) {
2020-02-12 11:38:08 +01:00
Logger : : Error ( " Cannot use --center-model or --center-model-geometry together with --{site,building}-local-placement " ) ;
2017-08-19 17:21:10 +02:00
return EXIT_FAILURE ;
}
2018-08-20 14:36:53 +03:00
2021-07-11 15:00:59 +02:00
IfcGeom : : Iterator tmp_context_iterator ( settings , ifc_file , filter_funcs , num_threads ) ;
2020-02-12 11:38:08 +01:00
time_t start , end ;
time ( & start ) ;
if ( ! quiet ) Logger : : Status ( " Computing bounds... " ) ;
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.
Logger : : Notice ( " No geometrical elements found or none succesfully converted " ) ;
serializer . reset ( ) ;
IfcUtil : : path : : delete_file ( IfcUtil : : path : : to_utf8 ( output_temp_filename ) ) ;
write_log ( ! quiet ) ;
return EXIT_FAILURE ;
}
}
tmp_context_iterator . compute_bounds ( center_model_geometry ) ;
2020-01-31 15:55:04 +01:00
2020-02-12 11:38:08 +01:00
time ( & end ) ;
if ( ! quiet ) Logger : : Status ( " Done ! Bounds computed in " + format_duration ( start , end ) ) ;
2018-08-20 14:36:53 +03:00
2020-01-31 15:55:04 +01:00
gp_XYZ center = ( tmp_context_iterator . bounds_min ( ) + tmp_context_iterator . bounds_max ( ) ) * 0.5 ;
2017-01-12 15:47:44 +02:00
offset [ 0 ] = - center . X ( ) ;
offset [ 1 ] = - center . Y ( ) ;
offset [ 2 ] = - center . Z ( ) ;
} else {
if ( sscanf ( offset_str . c_str ( ) , " %lf;%lf;%lf " , & offset [ 0 ] , & offset [ 1 ] , & offset [ 2 ] ) ! = 3 ) {
2019-02-08 15:18:55 +01:00
cerr_ < < " [Error] Invalid use of --model-offset \n " ;
IfcUtil : : path : : delete_file ( IfcUtil : : path : : to_utf8 ( output_temp_filename ) ) ;
2017-01-12 15:47:44 +02:00
print_options ( serializer_options ) ;
2017-06-15 21:24:13 +03:00
return EXIT_FAILURE ;
2017-01-12 15:47:44 +02:00
}
}
std : : stringstream msg ;
2021-06-21 21:37:53 +02:00
msg < < std : : setprecision ( std : : numeric_limits < double > : : max_digits10 ) < < " Using model offset ( " < < offset [ 0 ] < < " , " < < offset [ 1 ] < < " , " < < offset [ 2 ] < < " ) " ;
2017-02-24 15:01:54 +02:00
Logger : : Notice ( msg . str ( ) ) ;
2017-01-12 15:47:44 +02:00
}
2016-03-27 21:17:51 +02:00
2021-07-11 15:00:59 +02:00
IfcGeom : : Iterator context_iterator ( settings , ifc_file , filter_funcs , num_threads ) ;
2021-10-08 15:38:46 +02:00
# ifdef WITH_HDF5
std : : unique_ptr < HdfSerializer > cache ;
if ( vmap . count ( " cache-file " ) | | vmap . count ( " cache " ) ) {
if ( ! vmap . count ( " cache-file " ) ) {
cache_file = input_filename + CACHE + HDF ;
}
cache . reset ( new HdfSerializer ( IfcUtil : : path : : to_utf8 ( cache_file ) , settings ) ) ;
context_iterator . set_cache ( cache . get ( ) ) ;
}
# endif
2020-01-31 15:55:04 +01:00
if ( ! 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.
Logger : : Notice ( " No geometrical elements found or none succesfully converted " ) ;
serializer . reset ( ) ;
IfcUtil : : path : : delete_file ( IfcUtil : : path : : to_utf8 ( output_temp_filename ) ) ;
write_log ( ! quiet ) ;
return EXIT_FAILURE ;
}
2020-07-11 13:39:09 +02:00
serializer - > setFile ( context_iterator . file ( ) ) ;
if ( output_extension = = SVG ) {
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 ) {
Logger : : Notice ( " Overriding section height " ) ;
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 ) ;
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
}
2020-01-31 15:55:04 +01:00
2020-07-11 13:39:09 +02:00
if ( convert_back_units ) {
2020-01-31 15:55:04 +01:00
serializer - > setUnitNameAndMagnitude ( context_iterator . unit_name ( ) , static_cast < float > ( context_iterator . unit_magnitude ( ) ) ) ;
} else {
serializer - > setUnitNameAndMagnitude ( " METER " , 1.0f ) ;
}
serializer - > writeHeader ( ) ;
int old_progress = quiet ? 0 : - 1 ;
2018-05-12 20:12:25 +02:00
if ( ! quiet ) {
2021-11-12 09:54:08 +01: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
// -BRepElement pointer, based on current settings. (see IfcGeomIterator.h
// 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 ;
2013-03-24 10:48:39 +00:00
do {
2021-02-14 12:23:43 +01:00
2021-07-11 15:00:59 +02:00
IfcGeom : : Element * geom_object = 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 ) {
2018-05-12 20:12:25 +02:00
if ( quiet ) {
const int progress = context_iterator . progress ( ) ;
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 ) {
const int progress = context_iterator . progress ( ) ;
Logger : : Message ( Logger : : LOG_DEBUG , " Progress " + boost : : lexical_cast < std : : string > ( progress ) ) ;
2018-05-12 20:12:25 +02:00
} else {
const int progress = context_iterator . progress ( ) / 2 ;
if ( old_progress ! = progress ) Logger : : ProgressBar ( progress ) ;
old_progress = progress ;
}
2017-05-23 20:06:35 +03:00
}
2016-03-27 21:17:51 +02:00
} while ( + + num_created , context_iterator . next ( ) ) ;
2013-03-24 10:48:39 +00:00
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 " ) ;
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
2016-03-28 11:47:02 +03:00
// 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.
2019-02-08 15:18:55 +01:00
bool successful = IfcUtil : : path : : rename_file ( IfcUtil : : path : : to_utf8 ( output_temp_filename ) , IfcUtil : : path : : to_utf8 ( output_filename ) ) ;
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
2018-12-12 12:33:09 +01:00
if ( validate & & Logger : : MaxSeverity ( ) > = Logger : : LOG_ERROR ) {
2019-06-04 16:34:57 +02:00
Logger : : Error ( " Errors encountered during processing. " ) ;
2018-12-12 12:33:09 +01:00
successful = false ;
}
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 ) {
Logger : : Status ( " \n Conversion took " + format_duration ( start , end ) ) ;
}
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>
2017-12-21 14:14:04 +01:00
bool init_input_file ( const std : : string & filename , IfcParse : : IfcFile * & ifc_file , bool no_progress , bool mmap ) {
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
if ( no_progress ) { Logger : : SetOutput ( NULL , & log_stream ) ; }
2018-10-01 13:19:31 +03:00
time ( & start ) ;
2018-08-29 12:54:25 +02:00
# ifdef WITH_IFCXML
if ( boost : : ends_with ( boost : : to_lower_copy ( filename ) , " .ifcxml " ) ) {
ifc_file = IfcParse : : parse_ifcxml ( filename ) ;
} else
# endif
2021-09-04 15:38:07 +02:00
{
# ifdef USE_MMAP
ifc_file = new IfcParse : : IfcFile ( filename , mmap ) ;
# else
( void ) mmap ;
ifc_file = new IfcParse : : IfcFile ( filename ) ;
2017-07-23 14:52:04 +02:00
# endif
2021-09-04 15:38:07 +02:00
}
if ( ! ifc_file | | ! ifc_file - > good ( ) ) {
2017-06-15 22:07:56 +03:00
Logger : : Error ( " Unable to parse input file ' " + filename + " ' " ) ;
return false ;
}
2018-10-01 13:19:31 +03:00
time ( & end ) ;
2017-06-15 22:07:56 +03:00
2019-02-08 15:18:55 +01:00
if ( no_progress ) { Logger : : SetOutput ( & cout_ , & log_stream ) ; }
2018-10-01 13:19:31 +03:00
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 ;
attribute_filter . traverse = f . traverse ;
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 ) ;
return set ( inst , attr , IfcWrite : : IfcWriteArgument : : EnumerationReference ( it - enum_type - > enumeration_items ( ) . begin ( ) , it - > c_str ( ) ) ) ;
}
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 ( ) ;
2019-06-18 14:28:57 +02:00
if ( attr_type - > as_named_type ( ) & & attr_type - > as_named_type ( ) - > declared_type ( ) - > as_enumeration_type ( ) & & ! std : : is_same < T , IfcWrite : : IfcWriteArgument : : 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 {
IfcWrite : : IfcWriteArgument * a = new IfcWrite : : IfcWriteArgument ;
a - > set ( t ) ;
inst - > data ( ) . attributes ( ) [ i ] = a ;
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 ) ;
auto data = new IfcEntityInstanceData ( decl ) ;
auto inst = f . schema ( ) - > instantiate ( data ) ;
if ( decl - > is ( " IfcRoot " ) ) {
IfcParse : : IfcGlobalId guid ;
latebound_access : : set ( inst , " GlobalId " , ( std : : string ) guid ) ;
}
return f . addEntity ( inst ) ;
}
}
void fix_quantities ( IfcParse : : IfcFile & f , bool no_progress , bool quiet , bool stderr_progress ) {
{
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 ) {
auto rels = eq - > data ( ) . getInverse ( IfcRelDefinesByProperties , - 1 ) ;
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 ) ;
}
}
IfcGeom : : IteratorSettings settings ;
settings . set ( IfcGeom : : IteratorSettings : : USE_WORLD_COORDS , false ) ;
settings . set ( IfcGeom : : IteratorSettings : : WELD_VERTICES , false ) ;
2019-05-24 14:29:21 +02:00
settings . set ( IfcGeom : : IteratorSettings : : SEW_SHELLS , true ) ;
2018-11-02 16:10:06 +01:00
settings . set ( IfcGeom : : IteratorSettings : : CONVERT_BACK_UNITS , true ) ;
settings . set ( IfcGeom : : IteratorSettings : : DISABLE_TRIANGULATION , true ) ;
2021-07-11 15:00:59 +02:00
IfcGeom : : Iterator context_iterator ( settings , & f ) ;
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 " ) ) ;
latebound_access : : set ( application , " ApplicationIdentifier " , std : : string ( " IfcConvert " IFCOPENSHELL_VERSION ) ) ;
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 ) {
2018-11-02 16:10:06 +01:00
objects - > push ( geom_object - > product ( ) ) ;
} 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 ( ) ) ) ;
latebound_access : : set ( quantity_count , " CountValue " , IfcGeom : : Kernel : : surface_genus ( part . Shape ( ) ) ) ;
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 ) ;
2018-11-02 16:10:06 +01:00
objects - > push ( geom_object - > product ( ) ) ;
}
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 ;
if ( old_progress ! = progress ) Logger : : ProgressBar ( progress ) ;
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 {
Logger : : Status ( " \r Done writing quantities for " + boost : : lexical_cast < std : : string > ( num_created ) +
" objects " ) ;
}
2018-11-02 16:10:06 +01:00
}