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"
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
2019-01-18 16:43:38 +01:00
# include "../ifcgeom/schema_agnostic/IfcGeomFilter.h"
# include "../ifcgeom/schema_agnostic/IfcGeomIterator.h"
# 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 ;
static std : : wostream & cout_ = std : : wcout ;
static std : : wostream & cerr_ = std : : wcerr ;
# else
typedef std : : string path_t ;
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 "
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 ) ;
2019-10-06 19:03:31 +02:00
void fix_spaceboundaries ( 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
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
double deflection_tolerance ;
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 ;
2019-05-10 14:35:20 +02:00
std : : string geometry_kernel ;
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 " ) ;
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 " )
2018-05-12 20:12:25 +02:00
( " verbose,v " , " more verbose log messages " )
( " quiet,q " , " less status and progress output " )
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 " )
( " log-format " , po : : value < std : : string > ( & log_format ) , " log format: plain or json " ) ;
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 " )
( " output-file " , new po : : typed_value < path_t , char_t > ( 0 ) , " output geometry file " ) ;
2017-07-23 14:52:04 +02:00
2018-11-02 16:10:06 +01:00
po : : options_description ifc_options ( " IFC options " ) ;
ifc_options . add_options ( )
( " calculate-quantities " , " Calculate or fix the physical quantity definitions "
2019-10-06 19:03:31 +02:00
" based on an interpretation of the geometry when exporting IFC " )
( " fix-space-boundaries " , " Calculate or fix space boundary geometries "
" when exporting IFC " ) ;
2019-07-03 12:10:27 +02:00
2019-07-19 15:55:22 +02:00
int num_threads ;
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-01-20 13:16:42 +01:00
( " kernel " , po : : value < std : : string > ( & geometry_kernel ) - > default_value ( " opencascade " ) ,
" Geometry kernel to use (opencascade or cgal). " )
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. " )
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. " )
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. " )
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. " )
( " 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. " )
( " validate " , " Checks whether geometrical output conforms to the included explicit quantities. " ) ;
2016-03-07 22:37:36 +02:00
2017-01-12 15:47:44 +02:00
std : : string bounds , offset_str ;
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 ;
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. " )
2017-10-10 20:42:38 +02:00
( " section-height " , po : : value < double > ( & section_height ) ,
" Specifies the cut section height for SVG 2D geometry. " )
2016-03-10 12:01:04 +02:00
( " use-element-names " ,
" Use entity names instead of unique IDs for naming elements upon serialization. "
" Applicable for OBJ, DAE, and SVG output. " )
( " use-element-guids " ,
" Use entity GUIDs instead of unique IDs for naming elements upon serialization. "
" Applicable for OBJ, DAE, and SVG output. " )
( " 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. " )
2016-03-10 14:39:42 +02:00
( " center-model " ,
2016-03-17 23:25:53 +02:00
" Centers the elements upon serialization by applying the center point of "
2018-08-20 14:36:53 +03:00
" all placements as an offset. Applicable for OBJ and DAE output. Can take several minutes on large models. " )
2017-02-02 14:58:58 +02:00
( " model-offset " , po : : value < std : : string > ( & offset_str ) ,
2017-01-12 15:47:44 +02:00
" Applies an arbitrary offset of form 'x;y;z' to all placements. Applicable for OBJ and 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. " )
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, "
" and any other value means that 6 or 7 decimals are used. " ) ;
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 ;
2013-03-24 10:48:39 +00:00
const bool verbose = vmap . count ( " verbose " ) ! = 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 ;
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 ;
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 ;
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 ;
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 ;
const bool model_offset = vmap . count ( " model-offset " ) ! = 0 ;
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 ;
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 " ) ) {
std : : cerr < < " [Error] Input file not specified " < < 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 {
std : : cerr < < " [Error] --log-format should be either plain or json " < < std : : endl ;
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 {
std : : cerr < < " [Error] No filters read from specifified file. \n " ;
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 ) {
std : : cerr < < " [Error] Could not read default material file: " < < std : : endl ;
std : : cerr < < e . what ( ) < < std : : endl ;
return EXIT_FAILURE ;
}
}
2017-09-28 11:09:37 +02:00
boost : : optional < double > bounding_width ;
boost : : optional < double > bounding_height ;
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
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
}
}
2019-07-09 16:00:58 +02:00
Logger : : SetOutput ( quiet ? nullptr : & cout_ , & log_stream ) ;
2019-02-08 15:18:55 +01:00
Logger : : Verbosity ( verbose ? Logger : : LOG_NOTICE : Logger : : LOG_ERROR ) ;
2016-03-20 12:48:20 +02:00
2019-02-08 15:18:55 +01:00
path_t output_temp_filename = output_filename + IfcUtil : : path : : from_utf8 ( TEMP_FILE_EXTENSION ) ;
2015-02-06 13:07:50 +00:00
2019-02-08 15:18:55 +01:00
path_t output_extension = output_filename . substr ( output_filename . size ( ) - 4 ) ;
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 " ) ,
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 ) ;
}
2019-10-06 19:03:31 +02:00
if ( vmap . count ( " fix-space-boundaries " ) ) {
fix_spaceboundaries ( * ifc_file , no_progress , quiet , stderr_progress ) ;
}
2018-11-02 16:10:06 +01:00
fs < < * ifc_file ;
2018-12-10 11:33:54 +01:00
exit_code = EXIT_SUCCESS ;
2018-11-02 16:10:06 +01:00
} else {
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 ) ; }
2018-09-07 14:08:56 +02:00
if ( ! attribute_filter . attribute_name . empty ( ) ) { attribute_filter . update_description ( ) ; Logger : : Notice ( layer_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. " ;
output_temp_filename + = path_t ( v . begin ( ) , v . end ( ) ) ;
}
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 ) ) ) ;
}
{
std : : string v = " .tmp. " ;
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.
2019-08-17 09:47:07 +02:00
settings . set ( ifcopenshell : : geometry : : settings : : APPLY_DEFAULT_MATERIALS , true ) ;
settings . set ( ifcopenshell : : geometry : : settings : : USE_WORLD_COORDS , use_world_coords | | output_extension = = SVG | | output_extension = = OBJ ) ;
settings . set ( ifcopenshell : : geometry : : settings : : WELD_VERTICES , weld_vertices ) ;
settings . set ( ifcopenshell : : geometry : : settings : : SEW_SHELLS , orient_shells ) ;
settings . set ( ifcopenshell : : geometry : : settings : : CONVERT_BACK_UNITS , convert_back_units ) ;
2016-01-25 13:30:33 +01:00
# if OCC_VERSION_HEX < 0x60900
2019-08-17 09:47:07 +02:00
settings . set ( ifcopenshell : : geometry : : settings : : FASTER_BOOLEANS , merge_boolean_operands ) ;
2016-01-25 13:30:33 +01:00
# endif
2019-08-17 09:47:07 +02:00
settings . set ( ifcopenshell : : geometry : : settings : : DISABLE_OPENING_SUBTRACTIONS , disable_opening_subtractions ) ;
settings . set ( ifcopenshell : : geometry : : settings : : INCLUDE_CURVES , include_plan ) ;
settings . set ( ifcopenshell : : geometry : : settings : : EXCLUDE_SOLIDS_AND_SURFACES , ! include_model ) ;
settings . set ( ifcopenshell : : geometry : : settings : : APPLY_LAYERSETS , enable_layerset_slicing ) ;
settings . set ( ifcopenshell : : geometry : : settings : : NO_NORMALS , no_normals ) ;
settings . set ( ifcopenshell : : geometry : : settings : : GENERATE_UVS , generate_uvs ) ;
settings . set ( ifcopenshell : : geometry : : settings : : SEARCH_FLOOR , use_element_hierarchy | | output_extension = = SVG ) ;
settings . set ( ifcopenshell : : geometry : : settings : : SITE_LOCAL_PLACEMENT , site_local_placement ) ;
settings . set ( ifcopenshell : : geometry : : settings : : BUILDING_LOCAL_PLACEMENT , building_local_placement ) ;
settings . set ( ifcopenshell : : geometry : : settings : : VALIDATE_QUANTITIES , validate ) ;
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 ) ;
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 ) ;
2017-01-11 16:36:33 +02:00
settings . precision = precision ;
2013-03-24 10:48:39 +00: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 ) {
2019-08-17 09:47:07 +02:00
settings . set ( ifcopenshell : : geometry : : settings : : DISABLE_TRIANGULATION , true ) ;
2019-02-08 15:18:55 +01:00
serializer = boost : : make_shared < SvgSerializer > ( IfcUtil : : path : : to_utf8 ( output_temp_filename ) , settings ) ;
2017-10-10 20:42:38 +02:00
if ( vmap . count ( " section-height " ) ! = 0 ) {
Logger : : Notice ( " Overriding section height " ) ;
2018-10-08 16:36:48 +03:00
static_cast < SvgSerializer * > ( serializer . get ( ) ) - > setSectionHeight ( section_height ) ;
2017-10-10 20:42:38 +02:00
}
2017-09-28 11:09:37 +02:00
if ( bounding_width . is_initialized ( ) & & bounding_height . is_initialized ( ) ) {
2018-10-08 16:36:48 +03:00
static_cast < SvgSerializer * > ( serializer . get ( ) ) - > setBoundingRectangle ( bounding_width . get ( ) , bounding_height . get ( ) ) ;
2015-07-26 14:26:13 +00:00
}
2013-03-24 10:48:39 +00: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
}
2017-01-12 15:47:44 +02:00
if ( center_model | | 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
}
2019-08-17 09:47:07 +02:00
settings . set ( ifcopenshell : : geometry : : settings : : 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 " ) ;
}
if ( ! quiet & & num_threads > 1 ) {
Logger : : Status ( " Creating geometry... " ) ;
}
Logger : : SetOutput ( quiet ? nullptr : & cout_ , & log_stream ) ;
2019-08-17 09:47:07 +02:00
ifcopenshell : : geometry : : Iterator context_iterator ( geometry_kernel , settings , ifc_file , filter_funcs , num_threads ) ;
2017-03-02 14:55:21 +02: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.
2018-12-29 12:34:32 +01:00
Logger : : Notice ( " No geometrical elements found or none succesfully converted " ) ;
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 ) ) ;
2018-05-12 20:12:25 +02:00
write_log ( ! quiet ) ;
2017-03-02 14:55:21 +02:00
return EXIT_FAILURE ;
}
2013-03-24 10:48:39 +00:00
2017-12-20 12:40:15 +01:00
serializer - > setFile ( context_iterator . file ( ) ) ;
2015-07-26 14:26:13 +00:00
2013-12-31 14:10:38 +00:00
if ( convert_back_units ) {
2017-12-20 12:40:15 +01:00
serializer - > setUnitNameAndMagnitude ( context_iterator . unit_name ( ) , static_cast < float > ( context_iterator . unit_magnitude ( ) ) ) ;
2013-12-31 14:10:38 +00:00
} else {
serializer - > setUnitNameAndMagnitude ( " METER " , 1.0f ) ;
}
serializer - > writeHeader ( ) ;
2018-05-12 20:12:25 +02:00
int old_progress = quiet ? 0 : - 1 ;
2016-03-27 21:17:51 +02:00
2018-08-20 14:36:53 +03:00
if ( is_tesselated & & ( center_model | | model_offset ) ) {
2017-01-12 15:47:44 +02:00
double * offset = serializer - > settings ( ) . offset ;
if ( center_model ) {
2018-02-14 15:17:37 +01:00
if ( site_local_placement | | building_local_placement ) {
Logger : : Error ( " Cannot use --center-model 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
if ( ! quiet ) Logger : : Status ( " Computing bounds... " ) ;
context_iterator . compute_bounds ( ) ;
if ( ! quiet ) Logger : : Status ( " Done! " ) ;
2017-01-12 15:47:44 +02:00
gp_XYZ center = ( context_iterator . bounds_min ( ) + context_iterator . bounds_max ( ) ) * 0.5 ;
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 ;
msg < < " 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
2018-05-12 20:12:25 +02:00
if ( ! quiet ) {
2019-07-09 16:00:58 +02:00
if ( num_threads = = 1 ) {
Logger : : Status ( " Creating geometry... " ) ;
} else {
Logger : : Status ( " Writing 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
2019-01-18 11:23:19 +01:00
// -NativeElement pointer, based on current settings. (see IfcGeomIterator.h
2015-02-06 13:07:50 +00:00
// for definition) IfcGeom::Iterator::next() is used to poll whether more
// geometrical entities are available. None of these functions throw
// exceptions, neither for parsing errors or geometrical errors. Upon
// calling next() the entity to be returned has already been processed, a
2017-02-03 19:15:37 +02:00
// non-null return value guarantees that a successfully processed product is
2015-02-06 13:07:50 +00:00
// available.
2016-03-27 21:17:51 +02:00
size_t num_created = 0 ;
2013-03-24 10:48:39 +00:00
do {
2019-08-17 09:47:07 +02:00
ifcopenshell : : geometry : : 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
{
2019-08-17 09:47:07 +02:00
serializer - > write ( static_cast < const ifcopenshell : : geometry : : 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
{
2019-08-17 09:47:07 +02:00
serializer - > write ( static_cast < const ifcopenshell : : geometry : : NativeElement * > ( 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 ;
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 ) ;
2017-07-23 14:52:04 +02:00
# ifdef USE_MMAP
2017-12-20 12:40:15 +01:00
ifc_file = new IfcParse : : IfcFile ( filename , mmap ) ;
2017-07-23 14:52:04 +02:00
# else
2017-08-09 12:55:03 +02:00
( void ) mmap ;
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
2017-12-20 12:40:15 +01:00
ifc_file = new IfcParse : : IfcFile ( filename ) ;
if ( ! ifc_file - > good ( ) ) {
2017-07-23 14:52:04 +02:00
# endif
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 ;
2017-02-16 14:28:18 +02:00
} else if ( type = = " arg " ) {
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 ( ) ) ;
}
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 ) ;
}
}
2019-10-06 19:03:31 +02:00
# undef Handle
# include "../ifcgeom/kernels/cgal/CgalKernel.h"
# include <CGAL/box_intersection_d.h>
# include <CGAL/minkowski_sum_3.h>
2019-12-14 13:25:30 +01:00
# include <CGAL/Surface_mesh.h>
# include <CGAL/Surface_mesh_simplification/edge_collapse.h>
# include <CGAL/Surface_mesh_simplification/Policies/Edge_collapse/Edge_length_stop_predicate.h>
# include <CGAL/Surface_mesh_simplification/Policies/Edge_collapse/Edge_length_cost.h>
# include <CGAL/Surface_mesh_simplification/Edge_collapse_visitor_base.h>
namespace SMS = CGAL : : Surface_mesh_simplification ;
2019-10-06 19:03:31 +02:00
template < typename T >
T enlarge ( const T & t , double d = 1.e-5 ) {
T : : NT min [ 3 ] ;
T : : NT max [ 3 ] ;
for ( int i = 0 ; i < t . dimension ( ) ; + + i ) {
min [ i ] = t . min_coord ( i ) - d ;
max [ i ] = t . max_coord ( i ) + d ;
}
return T ( min , max , t . handle ( ) ) ;
2019-12-14 13:25:30 +01:00
// return T(min, max);
2019-10-06 19:03:31 +02:00
}
int convert_to_nef ( cgal_shape_t & shape , CGAL : : Nef_polyhedron_3 < Kernel_ > & result ) {
if ( ! shape . is_valid ( ) ) {
return 1 ;
}
if ( ! shape . is_closed ( ) ) {
return 2 ;
}
bool success = false ;
try {
success = CGAL : : Polygon_mesh_processing : : triangulate_faces ( shape ) ;
} catch ( . . . ) {
return 3 ;
}
if ( ! success ) {
return 4 ;
}
if ( CGAL : : Polygon_mesh_processing : : does_self_intersect ( shape ) ) {
return 5 ;
}
try {
result = CGAL : : Nef_polyhedron_3 < Kernel_ > ( shape ) ;
} catch ( . . . ) {
return 6 ;
}
return 0 ;
}
2019-12-14 13:25:30 +01:00
namespace {
// Can be used to convert polyhedron from exact to inexact and vice-versa
template < class Polyhedron_input ,
class Polyhedron_output >
struct Copy_polyhedron_to
: public CGAL : : Modifier_base < typename Polyhedron_output : : HalfedgeDS > {
Copy_polyhedron_to ( const Polyhedron_input & in_poly )
: in_poly ( in_poly ) { }
void operator ( ) ( typename Polyhedron_output : : HalfedgeDS & out_hds ) {
typedef typename Polyhedron_output : : HalfedgeDS Output_HDS ;
typedef typename Polyhedron_input : : HalfedgeDS Input_HDS ;
CGAL : : Polyhedron_incremental_builder_3 < Output_HDS > builder ( out_hds ) ;
typedef typename Polyhedron_input : : Vertex_const_iterator Vertex_const_iterator ;
typedef typename Polyhedron_input : : Facet_const_iterator Facet_const_iterator ;
typedef typename Polyhedron_input : : Halfedge_around_facet_const_circulator HFCC ;
builder . begin_surface ( in_poly . size_of_vertices ( ) ,
in_poly . size_of_facets ( ) ,
in_poly . size_of_halfedges ( ) ) ;
for ( Vertex_const_iterator
vi = in_poly . vertices_begin ( ) , end = in_poly . vertices_end ( ) ;
vi ! = end ; + + vi ) {
typename Polyhedron_output : : Point_3 p ( : : CGAL : : to_double ( vi - > point ( ) . x ( ) ) ,
: : CGAL : : to_double ( vi - > point ( ) . y ( ) ) ,
: : CGAL : : to_double ( vi - > point ( ) . z ( ) ) ) ;
builder . add_vertex ( p ) ;
}
typedef CGAL : : Inverse_index < Vertex_const_iterator > Index ;
Index index ( in_poly . vertices_begin ( ) , in_poly . vertices_end ( ) ) ;
for ( Facet_const_iterator
fi = in_poly . facets_begin ( ) , end = in_poly . facets_end ( ) ;
fi ! = end ; + + fi ) {
HFCC hc = fi - > facet_begin ( ) ;
HFCC hc_end = hc ;
builder . begin_facet ( ) ;
do {
builder . add_vertex_to_facet ( index [ hc - > vertex ( ) ] ) ;
+ + hc ;
} while ( hc ! = hc_end ) ;
builder . end_facet ( ) ;
}
builder . end_surface ( ) ;
} // end operator()(..)
private :
const Polyhedron_input & in_poly ;
} ; // end Copy_polyhedron_to<>
template < class Poly_B , class Poly_A >
void poly_copy ( Poly_B & poly_b , const Poly_A & poly_a ) {
poly_b . clear ( ) ;
Copy_polyhedron_to < Poly_A , Poly_B > modifier ( poly_a ) ;
poly_b . delegate ( modifier ) ;
}
}
namespace {
// The following is a Visitor that keeps track of the simplification process.
// In this example the progress is printed real-time and a few statistics are
// recorded (and printed in the end).
//
struct Stats {
Stats ( )
: collected ( 0 )
, processed ( 0 )
, collapsed ( 0 )
, non_collapsable ( 0 )
, cost_uncomputable ( 0 )
, placement_uncomputable ( 0 ) { }
std : : size_t collected ;
std : : size_t processed ;
std : : size_t collapsed ;
std : : size_t non_collapsable ;
std : : size_t cost_uncomputable ;
std : : size_t placement_uncomputable ;
} ;
struct My_visitor : SMS : : Edge_collapse_visitor_base < CGAL : : Polyhedron_3 < CGAL : : Simple_cartesian < double > > > {
My_visitor ( Stats * s ) : stats ( s ) { }
// Called during the collecting phase for each edge collected.
void OnCollected ( Profile const & , boost : : optional < double > const & ) {
+ + stats - > collected ;
std : : wcerr < < " \r Edges collected: " < < stats - > collected < < std : : flush ;
}
// Called during the processing phase for each edge selected.
// If cost is absent the edge won't be collapsed.
void OnSelected ( Profile const &
, boost : : optional < double > cost
, std : : size_t initial
, std : : size_t current
) {
+ + stats - > processed ;
if ( ! cost )
+ + stats - > cost_uncomputable ;
if ( current = = initial )
std : : wcerr < < " \n " < < std : : flush ;
std : : wcerr < < " \r " < < current < < std : : flush ;
}
// Called during the processing phase for each edge being collapsed.
// If placement is absent the edge is left uncollapsed.
void OnCollapsing ( Profile const &
, boost : : optional < Point > placement
) {
if ( ! placement )
+ + stats - > placement_uncomputable ;
}
// Called for each edge which failed the so called link-condition,
// that is, which cannot be collapsed because doing so would
// turn the surface mesh into a non-manifold.
void OnNonCollapsable ( Profile const & ) {
+ + stats - > non_collapsable ;
}
// Called after each edge has been collapsed
void OnCollapsed ( Profile const & , vertex_descriptor ) {
+ + stats - > collapsed ;
}
Stats * stats ;
} ;
}
namespace {
template < typename T >
T approx_normalized ( const T & t ) {
return t * ( 1. / Kernel_ : : FT ( CGAL : : sqrt ( CGAL : : to_double ( t . squared_length ( ) ) ) ) ) ;
}
}
# include <CGAL/AABB_tree.h>
# include <CGAL/AABB_traits.h>
# include <CGAL/Polyhedron_3.h>
# include <CGAL/AABB_face_graph_triangle_primitive.h>
namespace {
template < class HDS >
struct Build_Offset : public CGAL : : Modifier_base < HDS > {
std : : list < cgal_shape_t : : Facet_handle > input ;
void operator ( ) ( HDS & hds ) {
// Postcondition: hds is a valid polyhedral surface.
CGAL : : Polyhedron_incremental_builder_3 < HDS > B ( hds ) ;
int Nv = 0 , Nf = 0 ;
for ( auto & f : input ) {
Nv + = 3 ;
Nf + = 1 ;
}
B . begin_surface ( Nv , Nf ) ;
for ( auto & f : input ) {
auto p0 = f - > facet_begin ( ) - > vertex ( ) - > point ( ) ;
auto p1 = f - > facet_begin ( ) - > next ( ) - > vertex ( ) - > point ( ) ;
auto p2 = f - > facet_begin ( ) - > next ( ) - > next ( ) - > vertex ( ) - > point ( ) ;
auto O = CGAL : : centroid ( p0 , p1 , p2 ) ;
Kernel_ : : Point_3 * p012 [ 3 ] = { & p0 , & p1 , & p2 } ;
for ( int i = 0 ; i < 3 ; + + i ) {
* p012 [ i ] = CGAL : : ORIGIN + ( ( ( * ( p012 [ i ] ) ) - CGAL : : ORIGIN ) + ( ( * ( p012 [ i ] ) ) - O ) ) ;
B . add_vertex ( * p012 [ i ] ) ;
}
}
Nv = 0 ;
for ( int i = 0 ; i < Nf ; + + i ) {
B . begin_facet ( ) ;
B . add_vertex_to_facet ( Nv + + ) ;
B . add_vertex_to_facet ( Nv + + ) ;
B . add_vertex_to_facet ( Nv + + ) ;
B . end_facet ( ) ;
}
B . end_surface ( ) ;
}
} ;
template < typename Ts >
std : : list < cgal_shape_t : : Facet_handle > connected_faces ( cgal_shape_t : : Facet_handle & f , const Ts & excluded ) {
std : : set < cgal_shape_t : : Facet_handle > fs = { f } ;
std : : function < void ( cgal_shape_t : : Facet_handle & f ) > process ;
process = [ & fs , & process , & excluded ] ( cgal_shape_t : : Facet_handle & f ) {
cgal_shape_t : : Halfedge_around_facet_circulator circ = f - > facet_begin ( ) , end ( circ ) ;
do {
auto ff = circ - > opposite ( ) - > facet ( ) ;
if ( excluded . find ( ff ) = = excluded . end ( ) ) {
auto p = fs . insert ( ff ) ;
if ( p . second ) {
process ( ff ) ;
}
}
} while ( + + circ ! = end ) ;
} ;
process ( f ) ;
return std : : list < cgal_shape_t : : Facet_handle > ( fs . begin ( ) , fs . end ( ) ) ;
}
template < class HDS >
struct Builder_With_Map : public CGAL : : Modifier_base < HDS > {
std : : list < cgal_shape_t : : Facet_handle > input ;
std : : map < Kernel_ : : Point_3 , Kernel_ : : Point_3 > mapping ;
void operator ( ) ( HDS & hds ) {
// Postcondition: hds is a valid polyhedral surface.
CGAL : : Polyhedron_incremental_builder_3 < HDS > B ( hds ) ;
std : : set < Kernel_ : : Point_3 > used_points ;
for ( auto & f : input ) {
cgal_shape_t : : Halfedge_around_facet_circulator circ = f - > facet_begin ( ) , end ( circ ) ;
do {
auto P = circ - > vertex ( ) - > point ( ) ;
auto it = mapping . find ( P ) ;
if ( it = = mapping . end ( ) ) {
std : : wcout < < " WARNING unprojected point :( " < < std : : endl ;
} else {
P = it - > second ;
}
used_points . insert ( P ) ;
} while ( + + circ ! = end ) ;
}
B . begin_surface ( used_points . size ( ) , input . size ( ) ) ;
for ( auto & p : used_points ) {
B . add_vertex ( p ) ;
}
for ( auto & f : input ) {
B . begin_facet ( ) ;
cgal_shape_t : : Halfedge_around_facet_circulator circ = f - > facet_begin ( ) , end ( circ ) ;
do {
2019-12-14 14:50:20 +01:00
auto P = circ - > vertex ( ) - > point ( ) ;
auto it = mapping . find ( P ) ;
if ( it = = mapping . end ( ) ) {
std : : wcout < < " WARNING unprojected point :( " < < std : : endl ;
} else {
P = it - > second ;
}
auto jt = used_points . find ( P ) ;
if ( jt = = used_points . end ( ) ) {
throw std : : runtime_error ( " Unable to map point " ) ;
}
size_t idx = std : : distance ( used_points . begin ( ) , jt ) ;
std : : wcout < < " idx " < < idx < < std : : endl ;
B . add_vertex_to_facet ( idx ) ;
2019-12-14 13:25:30 +01:00
} while ( + + circ ! = end ) ;
B . end_facet ( ) ;
}
B . end_surface ( ) ;
}
} ;
}
namespace {
template < typename T >
T edge_collapse ( T polyhedron ) {
typedef CGAL : : Simple_cartesian < double > simple ;
CGAL : : Polyhedron_3 < simple > simple_poly ;
poly_copy ( simple_poly , polyhedron ) ;
// flattening from a thin box to a plane is not valid in edge_collapse()
Stats stats ;
My_visitor vis ( & stats ) ;
SMS : : Edge_length_cost < double > elc ;
SMS : : Edge_length_stop_predicate < double > stop ( 1.e-3 ) ;
int r = SMS : : edge_collapse ( simple_poly , stop ,
CGAL : : parameters : : vertex_index_map ( get ( CGAL : : vertex_external_index , simple_poly ) )
. halfedge_index_map ( get ( CGAL : : halfedge_external_index , simple_poly ) )
. visitor ( vis )
. get_cost ( elc )
) ;
std : : wcout < < " Removed: " < < r < < std : : endl ;
T result ;
poly_copy ( result , simple_poly ) ;
return result ;
}
double facet_area ( const cgal_shape_t : : Facet_handle & f ) {
auto p0 = f - > facet_begin ( ) - > vertex ( ) - > point ( ) ;
auto p1 = f - > facet_begin ( ) - > next ( ) - > vertex ( ) - > point ( ) ;
auto p2 = f - > facet_begin ( ) - > next ( ) - > next ( ) - > vertex ( ) - > point ( ) ;
return std : : sqrt ( CGAL : : to_double ( CGAL : : cross_product ( p0 - p1 , p2 - p1 ) . squared_length ( ) ) ) ;
}
void dump_facet ( const cgal_shape_t : : Facet_handle & f ) {
auto p0 = f - > facet_begin ( ) - > vertex ( ) - > point ( ) ;
auto p1 = f - > facet_begin ( ) - > next ( ) - > vertex ( ) - > point ( ) ;
auto p2 = f - > facet_begin ( ) - > next ( ) - > next ( ) - > vertex ( ) - > point ( ) ;
auto V = CGAL : : cross_product ( p0 - p1 , p2 - p1 ) ;
auto d = std : : sqrt ( CGAL : : to_double ( V . squared_length ( ) ) ) ;
if ( d > 1.e-20 ) {
V / = d ;
}
std : : ostringstream oss ;
oss . precision ( 8 ) ;
oss < < " Facet with area " < < facet_area ( f ) < < " and normal ( "
< < CGAL : : to_double ( V . cartesian ( 0 ) ) < < " " < < CGAL : : to_double ( V . cartesian ( 1 ) ) < < " "
< < CGAL : : to_double ( V . cartesian ( 2 ) ) < < " ) " ;
auto osss = oss . str ( ) ;
std : : wcout < < osss . c_str ( ) < < std : : endl ;
}
struct remove_thickness {
typedef Kernel_ : : Point_3 Point ;
typedef Kernel_ : : Plane_3 Plane ;
typedef Kernel_ : : Vector_3 Vector ;
typedef Kernel_ : : Segment_3 Segment ;
typedef Kernel_ : : Ray_3 Ray ;
typedef CGAL : : Polyhedron_3 < Kernel_ > Polyhedron ;
typedef CGAL : : AABB_face_graph_triangle_primitive < Polyhedron > Primitive ;
typedef CGAL : : AABB_traits < Kernel_ , Primitive > Traits ;
typedef CGAL : : AABB_tree < Traits > Tree ;
typedef boost : : optional < Tree : : Intersection_and_primitive_id < Ray > : : Type > Ray_intersection ;
cgal_shape_t polyhedron , polyhedron2 , flattened ;
2019-12-14 16:17:36 +01:00
remove_thickness ( const cgal_shape_t & p )
2019-12-14 13:25:30 +01:00
// edge_collapse(p) still does not work :(
: polyhedron ( p )
2019-12-14 16:17:36 +01:00
, polyhedron2 ( p ) {
2019-12-14 13:25:30 +01:00
CGAL : : Polygon_mesh_processing : : triangulate_faces ( polyhedron ) ;
CGAL : : Polygon_mesh_processing : : triangulate_faces ( polyhedron2 ) ;
2019-12-14 16:17:36 +01:00
std : : list < cgal_shape_t : : Facet_handle > non_degenerate , degenerate , longitudonal ;
2019-12-14 13:25:30 +01:00
std : : set < cgal_shape_t : : Facet_iterator > thin_sides ;
std : : wcout < < " ALL FACES: " < < std : : endl ;
for ( auto & f : faces ( polyhedron ) ) {
dump_facet ( f ) ;
if ( facet_area ( f ) > 1.e-20 ) {
non_degenerate . push_back ( f ) ;
2019-12-14 16:17:36 +01:00
} else {
degenerate . push_front ( f ) ;
std : : wcout < < " Degenerate, area: " < < facet_area ( f ) < < std : : endl ;
2019-12-14 13:25:30 +01:00
}
}
std : : wcout < < " NON DEGENERATE: " < < std : : endl ;
for ( auto & f : non_degenerate ) {
dump_facet ( f ) ;
}
cgal_shape_t enlarged_non_degenerate_triangles ;
Build_Offset < cgal_shape_t : : HDS > bo ;
bo . input = non_degenerate ;
enlarged_non_degenerate_triangles . delegate ( bo ) ;
2019-12-14 16:17:36 +01:00
// @todo, first on non-enlarged faces, then on enlarged; to fix projection on concave surfaces where the enlarging operation shortens projection distances.
Tree tree ( faces ( enlarged_non_degenerate_triangles ) . first , faces ( enlarged_non_degenerate_triangles ) . second , enlarged_non_degenerate_triangles ) ;
2019-12-14 13:25:30 +01:00
std : : map < cgal_face_descriptor_t , Kernel_ : : Vector_3 > face_normals ;
boost : : associative_property_map < std : : map < cgal_face_descriptor_t , Kernel_ : : Vector_3 > > face_normals_map ( face_normals ) ;
CGAL : : Polygon_mesh_processing : : compute_face_normals ( polyhedron , face_normals_map ) ;
2019-12-14 16:17:36 +01:00
2019-12-14 13:25:30 +01:00
for ( auto & f : non_degenerate ) {
auto O = CGAL : : centroid (
f - > facet_begin ( ) - > vertex ( ) - > point ( ) ,
f - > facet_begin ( ) - > next ( ) - > vertex ( ) - > point ( ) ,
f - > facet_begin ( ) - > next ( ) - > next ( ) - > vertex ( ) - > point ( )
2019-12-14 16:17:36 +01:00
) ;
2019-12-14 13:25:30 +01:00
Ray ray ( O , - face_normals_map [ f ] ) ;
2019-12-14 16:17:36 +01:00
std : : list < Ray_intersection > intersections ;
tree . all_intersections ( ray , std : : back_inserter ( intersections ) ) ;
double N = std : : numeric_limits < double > : : infinity ( ) ;
Point P ;
for ( auto & intersection : intersections ) {
if ( boost : : get < Point > ( & ( intersection - > first ) ) ) {
const Point * p = boost : : get < Point > ( & ( intersection - > first ) ) ;
const double d = std : : sqrt ( CGAL : : to_double ( ( * p - O ) . squared_length ( ) ) ) ;
if ( d > 1.e-20 & & d < N ) {
N = d ;
}
}
}
if ( N ! = std : : numeric_limits < double > : : infinity ( ) & & N > 1.e-4 ) {
thin_sides . insert ( f ) ;
}
/*
2019-12-14 13:25:30 +01:00
Ray_intersection intersection = tree.first_intersection(ray, [f](const cgal_shape_t::Facet_handle& p) {
return p == f;
});
if (intersection) {
if (boost::get<Point>(&(intersection->first))) {
const Point* p = boost::get<Point>(&(intersection->first));
const double d = std::sqrt(CGAL::to_double((*p - O).squared_length()));
if (d > 1.e-4) {
thin_sides.insert(f);
}
}
} else {
std::wcout << "No intersection :((!!!" << std::endl;
}
2019-12-14 16:17:36 +01:00
*/
2019-12-14 13:25:30 +01:00
}
std : : wcout < < " THIN SIDES: " < < std : : endl ;
for ( auto & f : thin_sides ) {
dump_facet ( f ) ;
}
for ( auto & f : non_degenerate ) {
if ( thin_sides . find ( f ) = = thin_sides . end ( ) ) {
longitudonal . push_back ( f ) ;
}
}
std : : wcout < < " LONGITUDONAL: " < < std : : endl ;
for ( auto & f : longitudonal ) {
dump_facet ( f ) ;
}
2019-12-14 16:17:36 +01:00
std : : wcout < < " faces " < < faces ( polyhedron ) . size ( ) < < " long " < < longitudonal . size ( ) < < " thin " < < thin_sides . size ( ) < < " non-degen " < < non_degenerate . size ( ) < < std : : endl ;
2019-12-14 13:25:30 +01:00
cgal_shape_t enlarged_indiv_triangles ;
2019-12-14 14:50:20 +01:00
Build_Offset < cgal_shape_t : : HDS > bo2 ;
bo2 . input = longitudonal ;
enlarged_indiv_triangles . delegate ( bo2 ) ;
2019-12-14 13:25:30 +01:00
{
std : : ofstream ofs ( " enlarged.off " ) ;
ofs . precision ( 17 ) ;
ofs < < enlarged_indiv_triangles ;
}
Tree tree2 ( faces ( enlarged_indiv_triangles ) . begin ( ) , faces ( enlarged_indiv_triangles ) . end ( ) , enlarged_indiv_triangles ) ;
// std::map<cgal_face_descriptor_t, Kernel_::Vector_3> face_normals_2;
// boost::associative_property_map<std::map<cgal_face_descriptor_t, Kernel_::Vector_3>> face_normals_map_2(face_normals_2);
// CGAL::Polygon_mesh_processing::compute_face_normals(polyhedron, face_normals_map_2);
// below does not seem to work? Do manually?
// std::map<cgal_vertex_descriptor_t, Kernel_::Vector_3> vertex_normals;
// boost::associative_property_map<std::map<cgal_vertex_descriptor_t, Kernel_::Vector_3>> vertex_normals_map(vertex_normals);
// CGAL::Polygon_mesh_processing::compute_normals(polyhedron, vertex_normals_map, face_normals_map_2);
std : : map < Kernel_ : : Point_3 , Kernel_ : : Point_3 > new_points ;
for ( Polyhedron : : Facet_iterator fit = polyhedron . facets_begin ( ) ;
fit ! = polyhedron . facets_end ( ) ;
+ + fit ) {
if ( CGAL : : collinear (
fit - > halfedge ( ) - > vertex ( ) - > point ( ) ,
fit - > halfedge ( ) - > next ( ) - > vertex ( ) - > point ( ) ,
fit - > halfedge ( ) - > opposite ( ) - > vertex ( ) - > point ( ) ) ) {
std : : wcout < < " degenerate triangle " < < std : : endl ;
}
}
/*
std::list<cgal_shape_t::Vertex_handle> vertices;
for (auto& f : non_degenerate) {
CGAL::Face_around_target_circulator<cgal_shape_t> it(f->halfedge(), polyhedron), end(it);
do {
vertices.push_back((*it)->halfedge()->vertex());
++it;
} while (it != end);
}*/
for ( auto & v : vertices ( polyhedron ) ) {
auto O = v - > point ( ) ;
2019-12-14 16:17:36 +01:00
2019-12-14 13:25:30 +01:00
Kernel_ : : Vector_3 norm ;
Kernel_ : : Vector_3 accum ;
int count = 0 ;
CGAL : : Face_around_target_circulator < cgal_shape_t > it ( v - > halfedge ( ) , polyhedron ) , end ( it ) ;
do {
cgal_shape_t : : Facet_handle fh = ( * it ) - > halfedge ( ) - > facet ( ) ;
auto jt = std : : find ( non_degenerate . begin ( ) , non_degenerate . end ( ) , fh ) ;
std : : wcout < < " non degen: " < < ( jt ! = non_degenerate . end ( ) ) < < std : : endl ;
auto kt = std : : find ( thin_sides . begin ( ) , thin_sides . end ( ) , fh ) ;
std : : wcout < < " thin side: " < < ( kt ! = thin_sides . end ( ) ) < < std : : endl ;
if ( jt ! = non_degenerate . end ( ) & & kt = = thin_sides . end ( ) ) {
// else degenerate, prevent div by zero, do not incorporate in vnorm.
// or else part of thin side
auto p0 = ( * it ) - > facet_begin ( ) - > vertex ( ) - > point ( ) ;
auto p1 = ( * it ) - > facet_begin ( ) - > next ( ) - > vertex ( ) - > point ( ) ;
auto p2 = ( * it ) - > facet_begin ( ) - > next ( ) - > next ( ) - > vertex ( ) - > point ( ) ;
{
std : : ostringstream oss ;
oss . precision ( 8 ) ;
oss < < " p0 " < < p0 . cartesian ( 0 ) < < " " < < p0 . cartesian ( 1 ) < < " " < < p0 . cartesian ( 2 ) < < " \n " ;
oss < < " p1 " < < p1 . cartesian ( 0 ) < < " " < < p1 . cartesian ( 1 ) < < " " < < p1 . cartesian ( 2 ) < < " \n " ;
oss < < " p2 " < < p2 . cartesian ( 0 ) < < " " < < p2 . cartesian ( 1 ) < < " " < < p2 . cartesian ( 2 ) < < " \n " ;
auto osss = oss . str ( ) ;
std : : wcout < < osss . c_str ( ) < < std : : endl ;
}
auto fnorm = CGAL : : cross_product ( p0 - p1 , p2 - p1 ) ;
fnorm / = std : : sqrt ( CGAL : : to_double ( fnorm . squared_length ( ) ) ) ;
// const auto& fnorm = face_normals_map_2[*it];
std : : ostringstream oss ;
oss . precision ( 8 ) ;
oss < < fnorm . cartesian ( 0 ) < < " " < < fnorm . cartesian ( 1 ) < < " " < < fnorm . cartesian ( 2 ) ;
auto osss = oss . str ( ) ;
std : : wcout < < osss . c_str ( ) < < std : : endl ;
accum + = fnorm ;
+ + count ;
}
+ + it ;
} while ( it ! = end ) ;
norm = accum / count ;
std : : wcout < < " count " < < count < < std : : endl ;
if ( count = = 0 ) {
2019-12-14 16:17:36 +01:00
// part of only degenerate or only thin sides
2019-12-14 13:25:30 +01:00
continue ;
}
// v->vertex_begin();
Ray ray ( O , norm ) ;
std : : ostringstream oss ;
oss . precision ( 8 ) ;
oss < < O < < " -> " < < norm ;
auto osss = oss . str ( ) ;
std : : wcout < < osss . c_str ( ) < < std : : endl ;
2019-12-14 16:17:36 +01:00
2019-12-14 13:25:30 +01:00
//// skip does not work anymore because we have offset the facets
// auto skip = [this, &v](const cgal_shape_t::Facet_handle& p) {
// CGAL::Face_around_target_circulator<cgal_shape_t> it(v->halfedge(), polyhedron), end(it);
// do {
// if ((*it)->facet_begin()->facet() == p) {
// return true;
// }
// } while (++it != end);
// return false;
// };
2019-12-14 16:17:36 +01:00
2019-12-14 13:25:30 +01:00
std : : list < Ray_intersection > intersections ;
tree2 . all_intersections ( ray , std : : back_inserter ( intersections ) ) ;
double N = std : : numeric_limits < double > : : infinity ( ) ;
Point P ;
2019-12-14 16:17:36 +01:00
bool used_intersection = false ;
2019-12-14 13:25:30 +01:00
if ( intersections . size ( ) ) {
for ( auto & intersection : intersections ) {
if ( boost : : get < Point > ( & ( intersection - > first ) ) ) {
const Point * p = boost : : get < Point > ( & ( intersection - > first ) ) ;
const double d = std : : sqrt ( CGAL : : to_double ( ( * p - O ) . squared_length ( ) ) ) ;
2019-12-14 14:50:20 +01:00
if ( d < N & & d > 1.e-20 ) {
2019-12-14 13:25:30 +01:00
N = d ;
P = * p ;
2019-12-14 16:17:36 +01:00
std : : wcout < < " intersection @ " < < d < < std : : endl ;
2019-12-14 13:25:30 +01:00
}
}
}
std : : wcout < < " ----------- " < < std : : endl ;
2019-12-14 14:50:20 +01:00
// average the new point
new_points [ O ] = CGAL : : ORIGIN + ( ( ( O - CGAL : : ORIGIN ) + ( P - CGAL : : ORIGIN ) ) ) / 2 ;
2019-12-14 16:17:36 +01:00
used_intersection = true ;
}
if ( ! used_intersection ) {
2019-12-14 13:25:30 +01:00
std : : wcout < < " no intersection :( " < < std : : endl ;
}
}
/*
for (auto& fi : thin_sides) {
auto f_circ = fi->facet_begin();
polyhedron2.erase_facet(f_circ);
}
*/
2019-12-14 16:17:36 +01:00
auto thin_sides_degenerate = thin_sides ;
thin_sides_degenerate . insert ( degenerate . begin ( ) , degenerate . end ( ) ) ;
// @todo choose connected / connected_opposing based on largest combined area of facets?
auto connected = connected_faces ( * longitudonal . begin ( ) , thin_sides_degenerate ) ;
decltype ( connected ) connected_opposing ;
for ( auto & f : longitudonal ) {
if ( std : : find ( connected . begin ( ) , connected . end ( ) , f ) = = connected . end ( ) ) {
connected_opposing = connected_faces ( f , thin_sides_degenerate ) ;
std : : set < cgal_shape_t : : Facet_handle > longi ( longitudonal . begin ( ) , longitudonal . end ( ) ) ;
std : : set < cgal_shape_t : : Facet_handle > both_sides ( connected . begin ( ) , connected . end ( ) ) ;
both_sides . insert ( connected_opposing . begin ( ) , connected_opposing . end ( ) ) ;
if ( longi = = both_sides ) {
std : : wcout < < " Facet connection functioning properly " < < std : : endl ;
} else {
std : : wcout < < " Facet connection functioning incorrectly " < < std : : endl ;
}
break ;
}
}
2019-12-14 13:25:30 +01:00
Builder_With_Map < cgal_shape_t : : HDS > b2 ;
b2 . input = connected ;
b2 . mapping = new_points ;
flattened . delegate ( b2 ) ;
}
} ;
}
2019-10-06 19:03:31 +02:00
void fix_spaceboundaries ( IfcParse : : IfcFile & f , bool no_progress , bool quiet , bool stderr_progress ) {
2019-12-14 13:25:30 +01:00
typedef std : : list < std : : pair < IfcUtil : : IfcBaseEntity * , CGAL : : Nef_polyhedron_3 < Kernel_ > > > nefs_t ;
typedef CGAL : : Box_intersection_d : : Box_with_handle_d < double , 3 , nefs_t : : value_type * > Box ;
// typedef CGAL::Box_intersection_d::Box_d<double, 3, CGAL::Box_intersection_d::ID_EXPLICIT> Box;
// std::map<size_t, nefs_t::value_type*> id_map;
2019-10-06 19:03:31 +02:00
ifcopenshell : : geometry : : settings settings ;
settings . set ( ifcopenshell : : geometry : : settings : : USE_WORLD_COORDS , false ) ;
settings . set ( ifcopenshell : : geometry : : settings : : WELD_VERTICES , false ) ;
settings . set ( ifcopenshell : : geometry : : settings : : SEW_SHELLS , true ) ;
settings . set ( ifcopenshell : : geometry : : settings : : CONVERT_BACK_UNITS , true ) ;
settings . set ( ifcopenshell : : geometry : : settings : : DISABLE_TRIANGULATION , true ) ;
settings . set ( ifcopenshell : : geometry : : settings : : DISABLE_OPENING_SUBTRACTIONS , true ) ;
std : : vector < ifcopenshell : : geometry : : filter_t > spaces_and_walls = {
IfcGeom : : entity_filter ( true , false , { " IfcWall " , " IfcSpace " } )
} ;
ifcopenshell : : geometry : : Iterator context_iterator ( " cgal " , settings , & f , spaces_and_walls ) ;
if ( ! context_iterator . initialize ( ) ) {
return ;
}
auto kernel = ( ifcopenshell : : geometry : : kernels : : CgalKernel * ) context_iterator . converter ( ) . kernel ( ) ;
auto cube = kernel - > precision_cube ( ) ;
size_t num_created = 0 ;
int old_progress = quiet ? 0 : - 1 ;
std : : vector < Box > boxes ;
nefs_t nefs ;
for ( ; ; + + num_created ) {
bool has_more = true ;
if ( num_created ) {
has_more = context_iterator . next ( ) ;
}
ifcopenshell : : geometry : : NativeElement * geom_object = nullptr ;
if ( has_more ) {
geom_object = context_iterator . get_native ( ) ;
}
if ( ! geom_object ) {
break ;
}
std : : stringstream ss ;
ss < < geom_object - > product ( ) - > data ( ) . toString ( ) ;
auto sss = ss . str ( ) ;
std : : wcout < < sss . c_str ( ) < < std : : endl ;
for ( auto & g : geom_object - > geometry ( ) ) {
auto s = ( ( ifcopenshell : : geometry : : CgalShape * ) g . Shape ( ) ) - > shape ( ) ;
const auto & m = g . Placement ( ) . components ;
const auto & n = geom_object - > transformation ( ) . data ( ) . components ;
if ( true | | ! m . isIdentity ( ) ) {
const cgal_placement_t trsf (
m ( 0 , 0 ) , m ( 0 , 1 ) , m ( 0 , 2 ) , m ( 0 , 3 ) ,
m ( 1 , 0 ) , m ( 1 , 1 ) , m ( 1 , 2 ) , m ( 1 , 3 ) ,
m ( 2 , 0 ) , m ( 2 , 1 ) , m ( 2 , 2 ) , m ( 2 , 3 ) ) ;
const cgal_placement_t trsf2 (
n ( 0 , 0 ) , n ( 0 , 1 ) , n ( 0 , 2 ) , n ( 0 , 3 ) ,
n ( 1 , 0 ) , n ( 1 , 1 ) , n ( 1 , 2 ) , n ( 1 , 3 ) ,
n ( 2 , 0 ) , n ( 2 , 1 ) , n ( 2 , 2 ) , n ( 2 , 3 ) ) ;
// Apply transformation
for ( auto & vertex : vertices ( s ) ) {
vertex - > point ( ) = vertex - > point ( ) . transform ( trsf ) . transform ( trsf2 ) ;
std : : ostringstream ss ;
ss < < vertex - > point ( ) . cartesian ( 0 ) ;
auto sss = ss . str ( ) ;
std : : wcout < < sss . c_str ( ) < < std : : endl ;
}
}
CGAL : : Nef_polyhedron_3 < Kernel_ > nef ;
auto c = convert_to_nef ( s , nef ) ;
if ( c ! = 0 ) {
std : : wcout < < " Error " < < c < < std : : endl ;
continue ;
}
nef = CGAL : : minkowski_sum_3 ( nef , cube ) ;
2019-12-14 13:25:30 +01:00
std : : wcout < < " product: " < < geom_object - > product ( ) < < std : : endl ;
2019-10-06 19:03:31 +02:00
nefs . push_back ( { geom_object - > product ( ) , nef } ) ;
2019-12-14 13:25:30 +01:00
Box b ( & * ( nefs . rbegin ( ) ) ) ;
// id_map[b.id()] = ;
2019-10-06 19:03:31 +02:00
for ( auto & vertex : vertices ( s ) ) {
2019-12-14 13:25:30 +01:00
double p [ 3 ] = {
CGAL : : to_double ( vertex - > point ( ) . cartesian ( 0 ) ) ,
CGAL : : to_double ( vertex - > point ( ) . cartesian ( 1 ) ) ,
CGAL : : to_double ( vertex - > point ( ) . cartesian ( 2 ) )
2019-10-06 19:03:31 +02:00
} ;
b . extend ( p ) ;
}
boxes . push_back ( enlarge ( b ) ) ;
/*
std::ostringstream ss;
ss << geom_object->product()->data().toString() << std::endl << b.min_coord(0) << " - " << b.max_coord(0) << std::endl;
auto sss = ss.str();
std::wcout << sss.c_str();
*/
}
if ( ! no_progress ) {
if ( quiet ) {
const int progress = context_iterator . progress ( ) ;
for ( ; old_progress < progress ; + + old_progress ) {
std : : cout < < " . " ;
if ( stderr_progress )
std : : cerr < < " . " ;
}
std : : cout < < std : : flush ;
if ( stderr_progress )
std : : cerr < < std : : flush ;
} else {
const int progress = context_iterator . progress ( ) / 2 ;
if ( old_progress ! = progress ) Logger : : ProgressBar ( progress ) ;
old_progress = progress ;
}
}
}
CGAL : : box_self_intersection_d ( boxes . begin ( ) , boxes . end ( ) , [ ] ( const Box & a , const Box & b ) {
std : : ostringstream ss ;
2019-12-14 13:25:30 +01:00
// ss << id_map[a.id()]->first->data().toString() << "x" << id_map[b.id()]->first->data().toString() << std::endl;
// auto x = id_map[a.id()]->second * id_map[b.id()]->second;
ss < < a . handle ( ) - > first - > data ( ) . toString ( ) < < " x " < < a . handle ( ) - > first - > data ( ) . toString ( ) < < std : : endl ;
2019-10-06 19:03:31 +02:00
auto x = a . handle ( ) - > second * b . handle ( ) - > second ;
cgal_shape_t x_poly ;
x . convert_to_polyhedron ( x_poly ) ;
2019-12-14 13:25:30 +01:00
CGAL : : Polygon_mesh_processing : : triangulate_faces ( x_poly ) ;
auto vs = vertices ( x_poly ) ;
if ( std : : distance ( vs . begin ( ) , vs . end ( ) ) = = 0 ) {
return ;
}
auto s0 = a . handle ( ) - > first - > declaration ( ) . name ( ) ;
auto s1 = b . handle ( ) - > first - > declaration ( ) . name ( ) ;
auto i0 = a . handle ( ) - > first - > data ( ) . id ( ) ;
auto i1 = b . handle ( ) - > first - > data ( ) . id ( ) ;
if ( s0 < s1 ) {
std : : swap ( s1 , s0 ) ;
std : : swap ( i0 , i1 ) ;
}
{
auto FN = s0 + " - " + s1 + " - " + std : : to_string ( i0 ) + " - " + std : : to_string ( i1 ) + " sb.off " ;
std : : ofstream os ( FN . c_str ( ) ) ;
os . precision ( 17 ) ;
os < < x_poly ;
}
remove_thickness r ( x_poly ) ;
{
auto FN = s0 + " - " + s1 + " - " + std : : to_string ( i0 ) + " - " + std : : to_string ( i1 ) + " -sides-sb.off " ;
std : : ofstream os ( FN . c_str ( ) ) ;
os . precision ( 17 ) ;
os < < r . polyhedron2 ;
}
{
auto FN = s0 + " - " + s1 + " - " + std : : to_string ( i0 ) + " - " + std : : to_string ( i1 ) + " -flat-sb.off " ;
std : : ofstream os ( FN . c_str ( ) ) ;
os . precision ( 17 ) ;
os < < r . flattened ;
}
// r();
/*
std::map<cgal_shape_t::Halfedge_const_handle, cgal_shape_t::Point_3> collapsed;
std::map<cgal_shape_t::Vertex_const_handle, cgal_shape_t::Point_3> collapsed_v;
for (auto it = x_poly.edges_begin(); it != x_poly.edges_end(); ++it) {
auto& e = *it;
cgal_shape_t::Vertex_iterator v0 = e.vertex();
cgal_shape_t::Vertex_iterator v1 = e.prev()->vertex();
auto p0 = v0->point();
auto p1 = v1->point();
auto l = std::sqrt(CGAL::to_double((p1 - p0).squared_length()));
std::wcout << "edge w/ length " << l << std::endl;
cgal_shape_t::Plane_3 plane(it->vertex()->point(),
it->next()->vertex()->point(),
it->next()->next()->vertex()->point());
auto d0 = plane.to_2d(e.prev()->vertex()->point()) - plane.to_2d(e.prev()->prev()->vertex()->point());
auto d1 = plane.to_2d(e.vertex()->point()) - plane.to_2d(e.prev()->vertex()->point());
auto d2 = plane.to_2d(e.next()->vertex()->point()) - plane.to_2d(e.vertex()->point());
auto a0 = std::atan2(CGAL::to_double(d0.cartesian(1)), CGAL::to_double(d0.cartesian(0)));
auto a1 = std::atan2(CGAL::to_double(d1.cartesian(1)), CGAL::to_double(d1.cartesian(0)));
auto a2 = std::atan2(CGAL::to_double(d2.cartesian(1)), CGAL::to_double(d2.cartesian(0)));
auto a10 = a1 - a0;
auto a21 = a2 - a1;
if (a10 < 0.) {
a10 += 2 * M_PI;
}
if (a21 < 0.) {
a21 += 2 * M_PI;
}
const bool is_convex = a10 < M_PI && a21 < M_PI;
cgal_shape_t::Plane_3 opposite_plane(it->opposite()->vertex()->point(),
it->opposite()->next()->vertex()->point(),
it->opposite()->next()->next()->vertex()->point()
);
{
std::ostringstream oss;
oss << plane << " vs " << opposite_plane << "\n";
oss << plane.orthogonal_vector() << " vs " << opposite_plane.orthogonal_vector();
auto osss = oss.str();
std::wcout << osss.c_str() << std::endl;
}
bool is_internal = false;
if (std::sqrt(CGAL::to_double(plane.orthogonal_vector().squared_length())) < 1.e-15 ||
std::sqrt(CGAL::to_double(opposite_plane.orthogonal_vector().squared_length())) < 1.e-15
) {
is_internal = true;
std::wcout << "Degenerate" << std::endl;
} else {
const double face_normal_dot = CGAL::to_double(approx_normalized(plane.orthogonal_vector()) * approx_normalized(opposite_plane.orthogonal_vector()));
std::wcout << "Face normal dot " << face_normal_dot << std::endl;
is_internal = face_normal_dot > 0.9;
}
std::wcout << "Angles " << a0 << " " << a1 << " " << a2 << std::endl;
if (l < 4.e-5 && (is_convex || is_internal)) {
auto p2 = CGAL::ORIGIN + ((p0 - CGAL::ORIGIN) + (p1 - CGAL::ORIGIN)) / 2;
std::wcout << "(a) " << CGAL::to_double(p0.cartesian(0)) << " " << CGAL::to_double(p0.cartesian(1)) << " " << CGAL::to_double(p0.cartesian(2)) << "\n";
std::wcout << "(b) " << CGAL::to_double(p1.cartesian(0)) << " " << CGAL::to_double(p1.cartesian(1)) << " " << CGAL::to_double(p1.cartesian(2)) << "\n";
std::wcout << "(c) " << CGAL::to_double(p2.cartesian(0)) << " " << CGAL::to_double(p2.cartesian(1)) << " " << CGAL::to_double(p2.cartesian(2)) << "\n";
// collapsed.insert({ v0, p2 });
// collapsed.insert({ v1, p2 });
collapsed.insert({ it, p2 });
// Edges includes only half of the halfedges
collapsed.insert({ it->opposite(), p2 });
collapsed_v.insert({ v0, p2 });
collapsed_v.insert({ v1, p2 });
}
}
{
auto FN = s0 + "-" + s1 + "-" + std::to_string(i0) + "-" + std::to_string(i1) + "sb.obj";
std::ofstream ofs(FN.c_str());
ofs.precision(17);
int N = 1;
std::set<std::set<Kernel_::Point_3> > faces_emitted;
for (auto& f : faces(x_poly)) {
std::ostringstream oss;
auto start = f->facet_begin();
bool part_collapsed = false;
CGAL::Polyhedron_3<Kernel_>::Halfedge_around_facet_const_circulator e = f->facet_begin();
do {
auto it = collapsed.find(e);
if (it != collapsed.end()) {
part_collapsed = true;
break;
}
++e;
} while (e != f->facet_begin());
decltype(faces_emitted)::key_type vss;
std::list<cgal_shape_t::Point_3> points;
if (!part_collapsed) {
e = f->facet_begin();
do {
cgal_shape_t::Vertex_const_handle v = e->vertex();
auto it = collapsed_v.find(v);
if (it == collapsed_v.end()) {
std::wcout << "Unexpected "
<< CGAL::to_double(v->point().cartesian(0)) << " "
<< CGAL::to_double(v->point().cartesian(1)) << " "
<< CGAL::to_double(v->point().cartesian(2)) << std::endl;
} else {
points.push_back(it->second);
vss.insert(it->second);
}
++e;
} while (e != f->facet_begin());
if (faces_emitted.find(vss) != faces_emitted.end()) {
std::wcout << "Emitted" << std::endl;
} else {
faces_emitted.insert(vss);
for (auto& p : points) {
ofs << "v " << CGAL::to_double(p.cartesian(0)) << " " << CGAL::to_double(p.cartesian(1)) << " " << CGAL::to_double(p.cartesian(2)) << "\n";
}
ofs << "f ";
for (auto i = 0; i < points.size(); ++i) {
if (i) {
ofs << " ";
}
ofs << i + N;
}
ofs << "\n";
N += points.size();
}
}
}
}
*/
/*
// edge collapse does not work on the rational number types
typedef CGAL::Simple_cartesian<double> simple;
CGAL::Polyhedron_3<simple> x_simple;
poly_copy(x_simple, x_poly);
// flattening from a thin box to a plane is not valid in edge_collapse()
Stats stats;
My_visitor vis(&stats);
SMS::Edge_length_cost<double> elc;
SMS::Edge_length_stop_predicate<double> stop(1.e-3);
int r = SMS::edge_collapse(x_simple, stop,
CGAL::parameters::vertex_index_map(get(CGAL::vertex_external_index, x_simple))
.halfedge_index_map(get(CGAL::halfedge_external_index, x_simple))
.visitor(vis)
.get_cost(elc)
);
std::wcout << "Removed: " << r << std::endl;
*/
/*
2019-10-06 19:03:31 +02:00
for (auto& v : vertices(x_poly)) {
auto p = v->point();
for (int i = 0; i < 3; ++i) {
ss << p.cartesian(i) << " ";
}
ss << std::endl;
}
ss << "---" << std::endl;
auto sss = ss.str();
std::wcout << sss.c_str();
2019-12-14 13:25:30 +01:00
*/
2019-10-06 19:03:31 +02:00
} ) ;
if ( ! no_progress & & quiet ) {
for ( ; old_progress < 100 ; + + old_progress ) {
std : : cout < < " . " ;
if ( stderr_progress )
std : : cerr < < " . " ;
}
std : : cout < < std : : flush ;
if ( stderr_progress )
std : : cerr < < std : : flush ;
} else {
Logger : : Status ( " \r Done fixing space boundaries for " + boost : : lexical_cast < std : : string > ( num_created ) +
" objects " ) ;
}
}
2018-11-02 16:10:06 +01:00
void fix_quantities ( IfcParse : : IfcFile & f , bool no_progress , bool quiet , bool stderr_progress ) {
{
auto delete_reversed = [ & f ] ( const IfcEntityList : : ptr & insts ) {
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 ) ;
}
}
2019-08-17 09:47:07 +02:00
ifcopenshell : : geometry : : settings settings ;
settings . set ( ifcopenshell : : geometry : : settings : : USE_WORLD_COORDS , false ) ;
settings . set ( ifcopenshell : : geometry : : settings : : WELD_VERTICES , false ) ;
settings . set ( ifcopenshell : : geometry : : settings : : SEW_SHELLS , true ) ;
settings . set ( ifcopenshell : : geometry : : settings : : CONVERT_BACK_UNITS , true ) ;
settings . set ( ifcopenshell : : geometry : : settings : : DISABLE_TRIANGULATION , true ) ;
2018-11-02 16:10:06 +01:00
2019-08-17 09:47:07 +02:00
ifcopenshell : : geometry : : 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 ;
IfcEntityList : : ptr objects ;
2019-08-17 09:47:07 +02:00
boost : : shared_ptr < ifcopenshell : : geometry : : Representation : : BRep > previous_geometry_pointer ;
2018-11-02 16:10:06 +01:00
2018-12-12 12:33:09 +01:00
for ( ; ; + + num_created ) {
bool has_more = true ;
if ( num_created ) {
has_more = context_iterator . next ( ) ;
}
2019-08-17 09:47:07 +02:00
ifcopenshell : : geometry : : NativeElement * 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 ;
}
2018-11-02 16:10:06 +01:00
IfcEntityList : : ptr quantities ( new IfcEntityList ) ;
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
2018-12-12 12:33:09 +01:00
IfcEntityList : : ptr quantities_2 ( new IfcEntityList ) ;
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 ( ) ) ) ;
2019-01-18 11:23:19 +01:00
latebound_access : : set ( quantity_count , " CountValue " , part . Shape ( ) - > surface_genus ( ) ) ;
2018-12-10 11:33:54 +01:00
2018-12-12 12:33:09 +01:00
quantities_2 - > push ( quantity_count ) ;
2018-12-10 11:33:54 +01:00
}
2018-12-12 12:33:09 +01:00
latebound_access : : set ( quantity_complex , " HasQuantities " , quantities_2 ) ;
2018-11-02 16:10:06 +01:00
if ( quantities - > size ( ) ) {
quantity = latebound_access : : create ( f , " IfcElementQuantity " ) ;
latebound_access : : set ( quantity , " OwnerHistory " , ownerhist ) ;
latebound_access : : set ( quantity , " Quantities " , quantities ) ;
}
objects . reset ( new IfcEntityList ) ;
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 )
std : : cerr < < " . " ;
}
std : : cout < < std : : flush ;
if ( stderr_progress )
std : : cerr < < std : : flush ;
} 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 )
std : : cerr < < " . " ;
}
std : : cout < < std : : flush ;
if ( stderr_progress )
std : : cerr < < std : : flush ;
} else {
Logger : : Status ( " \r Done writing quantities for " + boost : : lexical_cast < std : : string > ( num_created ) +
" objects " ) ;
}
2018-11-02 16:10:06 +01:00
}