2021-11-05 16:54:16 +01:00
/********************************************************************************
2011-07-25 14:56:40 +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/>. *
* *
********************************************************************************/
2023-03-21 20:15:01 +01:00
/********************************************************************************
* *
* Implementations of the various conversion functions defined in IfcGeom.h *
* *
********************************************************************************/
2023-03-22 08:45:30 +01:00
# include "OpenCascadeKernel.h"
2023-03-21 20:15:01 +01:00
# include "boolean_utils.h"
# include "base_utils.h"
2024-10-07 20:47:01 +02:00
# include <BRepPrimAPI_MakeRevol.hxx>
2026-02-01 21:08:11 +01:00
# include <BOPAlgo_MakerVolume.hxx>
2018-09-21 10:22:24 +02:00
2018-11-23 11:45:23 +01:00
namespace {
struct opening_sorter {
bool operator ( ) ( const std : : pair < double , TopoDS_Shape > & a , const std : : pair < double , TopoDS_Shape > & b ) const {
return a . first > b . first ;
}
} ;
}
2023-11-15 10:32:20 +01:00
using namespace ifcopenshell : : geometry ;
2023-07-27 16:42:06 +08:00
bool IfcGeom : : OpenCascadeKernel : : convert_openings ( const IfcUtil : : IfcBaseEntity * entity , const std : : vector < std : : pair < taxonomy : : ptr , ifcopenshell : : geometry : : taxonomy : : matrix4 > > & openings ,
2023-03-21 20:15:01 +01:00
const IfcGeom : : ConversionResults & entity_shapes , const ifcopenshell : : geometry : : taxonomy : : matrix4 & entity_trsf , IfcGeom : : ConversionResults & cut_shapes ) {
2018-11-23 11:45:23 +01:00
2022-11-13 11:04:16 +01:00
util : : boolean_settings bst ;
2023-11-15 10:32:20 +01:00
bst . attempt_2d = settings_ . get < settings : : BooleanAttempt2d > ( ) . get ( ) ;
bst . debug = settings_ . get < settings : : DebugBooleanOperations > ( ) . get ( ) ;
bst . precision = settings_ . get < settings : : Precision > ( ) . get ( ) ;
2022-11-13 11:04:16 +01:00
2018-11-23 11:45:23 +01:00
std : : vector < std : : pair < double , TopoDS_Shape > > opening_vector ;
2023-03-21 20:15:01 +01:00
for ( auto & op : openings ) {
/*
// Not yet implemented and tested, process opening placement up to parent wall
// placement so that the matrix inverse can be eliminated.
// @todo property check and handle the decomposition into parts (where element
// carying geom and opening are in different branches).
// @todo properly check whether opening correctly references wall placement
// and fallback to matrix inverse when not the case.
auto relative = entity;
{
auto ds = relative->Decomposes();
if (ds->size() == 1) {
relative = (*ds->begin())->RelatingObject()->as<IfcSchema::IfcProduct>();
2016-01-25 13:30:33 +01:00
}
2023-03-21 20:15:01 +01:00
}
set_conversion_placement_rel_to_instance(relative);
*/
2016-01-25 13:30:33 +01:00
2023-03-21 20:15:01 +01:00
// Convert the IfcRepresentation of the IfcOpeningElement
auto opening_trsf = op . second ;
// set_conversion_placement_rel_to_instance(nullptr);
// Move the opening into the coordinate system of the IfcProduct
// @todo
Eigen : : Matrix4d relative = entity_trsf . ccomponents ( ) . inverse ( ) * opening_trsf . ccomponents ( ) ;
// opening_trsf = relative;
IfcGeom : : ConversionResults opening_shapes ;
// @todo
AbstractKernel : : convert ( op . first , opening_shapes ) ;
for ( unsigned int i = 0 ; i < opening_shapes . size ( ) ; + + i ) {
2023-11-02 09:04:34 +01:00
auto opening_shape_i = std : : static_pointer_cast < OpenCascadeShape > ( opening_shapes [ i ] . Shape ( ) ) - > shape ( ) ;
2024-06-18 21:30:00 +02:00
const TopoDS_Shape & opening_shape_unlocated = util : : ensure_fit_for_subtraction ( opening_shape_i , settings_ . get < settings : : Precision > ( ) . get ( ) ) ;
2023-03-21 20:15:01 +01:00
auto gtrsf = opening_shapes [ i ] . Placement ( ) ;
// @todo check
2023-07-27 16:42:06 +08:00
Eigen : : Matrix4d m = relative * gtrsf - > ccomponents ( ) ;
2023-03-21 20:15:01 +01:00
gp_Trsf trsf ;
trsf . SetValues (
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 )
) ;
TopoDS_Shape opening_shape = util : : apply_transformation ( opening_shape_unlocated , trsf ) ;
opening_vector . push_back ( std : : make_pair ( util : : min_edge_length ( opening_shape ) , opening_shape ) ) ;
2016-01-25 13:30:33 +01:00
}
2023-03-21 20:15:01 +01:00
2016-01-25 13:30:33 +01:00
}
2018-11-23 11:45:23 +01:00
std : : sort ( opening_vector . begin ( ) , opening_vector . end ( ) , opening_sorter ( ) ) ;
2016-01-25 13:30:33 +01:00
// Iterate over the shapes of the IfcProduct
2023-03-21 20:15:01 +01:00
for ( IfcGeom : : ConversionResults : : const_iterator it3 = entity_shapes . begin ( ) ; it3 ! = entity_shapes . end ( ) ; + + it3 ) {
2022-01-13 01:24:57 +01:00
2022-06-11 15:48:04 +02:00
TopoDS_Compound C ;
BRep_Builder B ;
B . MakeCompound ( C ) ;
TopoDS_Shape combined_result ;
2021-05-23 16:26:34 +02:00
2022-06-11 15:48:04 +02:00
std : : list < TopoDS_Shape > parts ;
2016-01-22 17:27:28 +01:00
2023-11-02 09:04:34 +01:00
auto it3_shape = std : : static_pointer_cast < OpenCascadeShape > ( it3 - > Shape ( ) ) - > shape ( ) ;
2024-06-11 11:20:55 +02:00
if ( it3_shape . IsNull ( ) ) {
2026-06-10 18:30:54 +02:00
Logger : : Root ( ) . Error ( " GEO " , 187 , " Null operand " ) ;
2024-06-11 11:20:55 +02:00
continue ;
}
2023-03-21 20:15:01 +01:00
bool is_multiple = it3_shape . ShapeType ( ) = = TopAbs_COMPOUND & & TopoDS_Iterator ( it3_shape ) . More ( ) & & util : : is_nested_compound_of_solid ( it3_shape ) ;
2018-11-23 11:45:23 +01:00
2022-06-11 15:48:04 +02:00
if ( is_multiple ) {
2023-03-21 20:15:01 +01:00
TopoDS_Iterator sit ( it3_shape ) ;
2022-06-11 15:48:04 +02:00
for ( ; sit . More ( ) ; sit . Next ( ) ) {
parts . push_back ( sit . Value ( ) ) ;
2021-05-23 16:26:34 +02:00
}
2022-06-11 15:48:04 +02:00
} else {
2023-03-21 20:15:01 +01:00
parts . push_back ( it3_shape ) ;
2022-06-11 15:48:04 +02:00
}
2026-02-01 21:08:11 +01:00
for ( auto entity_part : parts ) {
2022-11-13 11:04:16 +01:00
bool is_manifold = util : : is_manifold ( entity_part ) ;
2022-06-11 15:48:04 +02:00
2026-06-04 21:37:41 +02:00
if ( ! is_manifold ) {
// force sewing, edge identity might have been mudied by FixAdvFace.FixOrientation.MSG5 to fix interior loop winding order
TopTools_ListOfShape list ;
IfcGeom : : util : : shape_to_face_list ( entity_part , list ) ;
IfcGeom : : util : : create_solid_from_faces ( list , entity_part , settings_ . get < settings : : Precision > ( ) . get ( ) , true ) ;
is_manifold = util : : is_manifold ( entity_part ) ;
if ( is_manifold ) {
2026-06-10 18:30:54 +02:00
Logger : : Root ( ) . Warning ( " GEO " , 188 , " Successfully sewed non-manifold first operand " ) ;
2026-06-04 21:37:41 +02:00
}
}
2022-06-11 15:48:04 +02:00
if ( ! is_manifold ) {
2026-02-01 21:08:11 +01:00
if ( settings_ . get < settings : : MakeVolume > ( ) . get ( ) ) {
BOPAlgo_MakerVolume mv ;
mv . AddArgument ( entity_part ) ;
mv . SetAvoidInternalShapes ( true ) ;
2026-06-04 21:41:06 +02:00
// mv.SetFuzzyValue(settings_.get<settings::Precision>().get());
std : : optional < std : : string > failure ;
2026-02-01 21:08:11 +01:00
try {
mv . Perform ( ) ;
2026-06-04 21:41:06 +02:00
auto entity_part_2 = mv . Shape ( ) ;
if ( IfcGeom : : util : : count ( entity_part_2 , TopAbs_FACE ) = = 0 ) {
failure = " Empty result (no faces) for BOPAlgo_MakerVolume; original was " + std : : to_string ( IfcGeom : : util : : count ( entity_part , TopAbs_FACE ) ) ;
} else {
is_manifold = util : : is_manifold ( entity_part_2 ) ;
2026-06-10 18:30:54 +02:00
Logger : : Root ( ) . Warning ( " GEO " , 189 , std : : string ( " Sucessfully detected exterior volume to non-manifold first operand; shape is now " ) + ( is_manifold ? std : : string ( " manifold " ) : std : : string ( " non-manifold " ) ) ) ;
2026-06-04 21:41:06 +02:00
entity_part = entity_part_2 ;
}
2026-02-01 21:08:11 +01:00
} catch ( const Standard_Failure & e ) {
2026-06-04 21:41:06 +02:00
failure . emplace ( e . GetMessageString ( ) ) ;
}
if ( failure ) {
2026-06-10 18:30:54 +02:00
Logger : : Root ( ) . Warning ( " GEO " , 190 , " MakeVolume failed: " + * failure , entity ) ;
2026-02-01 21:08:11 +01:00
}
} else {
2026-06-10 18:30:54 +02:00
Logger : : Root ( ) . Warning ( " GEO " , 191 , " Non-manifold first operand, use --make-volume to try and make manifold " ) ;
2026-02-01 21:08:11 +01:00
}
2021-05-23 16:26:34 +02:00
}
2018-11-23 11:45:23 +01:00
2023-03-21 20:15:01 +01:00
TopoDS_Shape entity_part_result ;
2022-06-11 15:48:04 +02:00
for ( int as_shell = 0 ; as_shell < 2 ; + + as_shell ) {
TopoDS_Shape entity_shape_unlocated ;
if ( as_shell ) {
entity_shape_unlocated = entity_part ;
} else {
2024-06-18 21:30:00 +02:00
entity_shape_unlocated = util : : ensure_fit_for_subtraction ( entity_part , settings_ . get < settings : : Precision > ( ) . get ( ) ) ;
2022-06-11 15:48:04 +02:00
}
2023-07-27 16:42:06 +08:00
const auto & m = it3 - > Placement ( ) - > ccomponents ( ) ;
2023-03-21 20:15:01 +01:00
// @todo
// if (entity_shape_gtrsf.Form() == gp_Other) {
// Logger::Message(Logger::LOG_WARNING, "Applying non uniform transformation to:", entity);
// }
gp_Trsf entity_shape_gtrsf ;
entity_shape_gtrsf . SetValues (
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 )
) ;
2022-11-14 09:23:59 +01:00
TopoDS_Shape entity_shape = util : : apply_transformation ( entity_shape_unlocated , entity_shape_gtrsf ) ;
2018-11-23 11:45:23 +01:00
2022-06-11 15:48:04 +02:00
TopoDS_Shape result = entity_shape ;
2021-05-23 16:26:34 +02:00
2022-06-11 15:48:04 +02:00
auto it = opening_vector . begin ( ) ;
auto jt = it ;
2021-05-23 16:26:34 +02:00
2022-06-11 15:48:04 +02:00
for ( ; ; + + it ) {
if ( it = = opening_vector . end ( ) | | jt - > first / it - > first > 10. ) {
2021-05-23 16:26:34 +02:00
2022-06-11 15:48:04 +02:00
TopTools_ListOfShape opening_list ;
for ( auto kt = jt ; kt < it ; + + kt ) {
opening_list . Append ( kt - > second ) ;
}
TopoDS_Shape intermediate_result ;
2022-11-13 11:04:16 +01:00
if ( util : : boolean_operation ( bst , result , opening_list , BOPAlgo_CUT , intermediate_result ) ) {
2022-06-11 15:48:04 +02:00
result = intermediate_result ;
} else {
2026-06-10 18:30:54 +02:00
Logger : : Root ( ) . Message ( Logger : : LOG_ERROR , " GEO " , 192 , " Opening subtraction failed for " + boost : : lexical_cast < std : : string > ( std : : distance ( jt , it ) ) + " openings " , entity ) ;
2022-06-11 15:48:04 +02:00
}
jt = it ;
2021-05-23 16:26:34 +02:00
}
2022-06-11 15:48:04 +02:00
if ( it = = opening_vector . end ( ) ) {
break ;
}
2018-11-23 11:45:23 +01:00
}
2022-11-13 11:04:16 +01:00
int result_n_faces = util : : count ( result , TopAbs_FACE ) ;
2022-06-11 15:48:04 +02:00
if ( ! is_manifold & & as_shell = = 0 & & result_n_faces = = 0 ) {
// If we have a non-manifold first operand and our first attempt
// on a Solid-Solid subtraction yielded a empty result (no faces)
// or a strange result, a larger number of faces with the original input
// included. Then retry (another iteration on the for-loop on as-shell)
// where we keep the first operand as is (a compound of faces probably,
// unless --orient-shells was activated in which case we're already lost).
if ( ! is_manifold ) {
2026-06-10 18:30:54 +02:00
Logger : : Root ( ) . Warning ( " GEO " , 193 , " Retrying boolean operation on individual faces " ) ;
2022-06-11 15:48:04 +02:00
}
continue ;
2021-05-23 16:26:34 +02:00
}
2022-06-11 15:48:04 +02:00
entity_part_result = result ;
// For manifold first operands we're not even going to try if processing
// as loose faces gives a better result.
break ;
2018-11-23 11:45:23 +01:00
}
2022-06-11 15:48:04 +02:00
if ( is_multiple ) {
B . Add ( C , entity_part_result ) ;
} else {
combined_result = entity_part_result ;
2018-11-23 11:45:23 +01:00
}
2022-06-11 15:48:04 +02:00
}
2021-05-23 16:26:34 +02:00
2022-06-11 15:48:04 +02:00
if ( is_multiple ) {
combined_result = C ;
2021-05-23 16:26:34 +02:00
}
2022-06-11 15:48:04 +02:00
2023-07-27 16:42:06 +08:00
cut_shapes . push_back ( IfcGeom : : ConversionResult ( it3 - > ItemId ( ) , new OpenCascadeShape ( combined_result ) , it3 - > StylePtr ( ) ) ) ;
2012-06-07 14:38:21 +00:00
}
return true ;
}
2024-10-07 20:47:01 +02:00
bool IfcGeom : : OpenCascadeKernel : : unify_shapes ( const IfcGeom : : ConversionResults & input , IfcGeom : : ConversionResults & output ) {
std : : transform ( input . begin ( ) , input . end ( ) , std : : back_inserter ( output ) , [ this ] ( auto v ) {
auto & s = std : : static_pointer_cast < OpenCascadeShape > ( v . Shape ( ) ) - > shape ( ) ;
2024-10-09 13:16:58 +02:00
return IfcGeom : : ConversionResult (
v . ItemId ( ) ,
v . Placement ( ) ,
new OpenCascadeShape ( util : : unify ( s , settings_ . get < ifcopenshell : : geometry : : settings : : Precision > ( ) . get ( ) ) ) ,
v . StylePtr ( ) ) ;
2024-10-07 20:47:01 +02:00
} ) ;
return true ;
}
2024-08-01 07:48:42 +02:00
bool IfcGeom : : OpenCascadeKernel : : convert_impl ( const taxonomy : : revolve : : ptr r , IfcGeom : : ConversionResults & results ) {
2026-03-14 14:44:47 +01:00
return handle_occt_exception ( [ & ] ( ) - > bool {
gp_Ax1 ax (
convert_xyz < gp_Pnt > ( * r - > axis_origin ) ,
convert_xyz < gp_Dir > ( * r - > direction ) ) ;
TopoDS_Shape face ;
if ( ! convert ( taxonomy : : cast < taxonomy : : face > ( r - > basis ) , face ) ) {
return false ;
}
TopoDS_Shape shape ;
if ( r - > angle ) {
shape = BRepPrimAPI_MakeRevol ( face , ax , * r - > angle ) ;
} else {
shape = BRepPrimAPI_MakeRevol ( face , ax ) ;
}
results . emplace_back ( ConversionResult (
r - > instance - > as < IfcUtil : : IfcBaseEntity > ( ) - > id ( ) ,
r - > matrix ,
new OpenCascadeShape ( shape ) ,
r - > surface_style ) ) ;
return true ;
} ) ;
2024-08-01 07:48:42 +02:00
}