2012-07-19 18:26:47 +00: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/>. *
* *
********************************************************************************/
/********************************************************************************
* *
* Implementations of the various conversion functions defined in IfcGeom.h *
* *
********************************************************************************/
Add support for IfcBooleanResult, IfcBlock, IfcRectangularPyramid, IfcRightCircularCylinder, IfcRightCircularCone, IfcSphere, IfcCsgSolid, IfcCurveBoundedPlane, IfcRectangularTrimmedSurface, IfcSurfaceCurveSweptAreaSolid, IfcCylindricalSurface.
2014-03-22 10:31:51 +00:00
# include <set>
2012-06-25 15:08:58 +00:00
# include <cassert>
2014-07-24 11:14:54 +00:00
# include <algorithm>
2019-08-21 11:59:30 +02:00
# include <numeric>
2012-06-25 15:08:58 +00:00
2018-12-19 16:30:05 +01:00
# include <Standard_Version.hxx>
2011-07-25 14:56:40 +00:00
# include <gp_Pnt.hxx>
# include <gp_Vec.hxx>
# include <gp_Dir.hxx>
# include <gp_Pnt2d.hxx>
# include <gp_Vec2d.hxx>
# include <gp_Dir2d.hxx>
2011-10-23 17:11:06 +00:00
# include <gp_Mat.hxx>
# include <gp_Mat2d.hxx>
# include <gp_GTrsf.hxx>
# include <gp_GTrsf2d.hxx>
2011-07-25 14:56:40 +00:00
# include <gp_Trsf.hxx>
# include <gp_Trsf2d.hxx>
# include <gp_Ax3.hxx>
# include <gp_Ax2d.hxx>
# include <gp_Pln.hxx>
# include <gp_Circ.hxx>
2018-09-12 13:26:11 +02:00
# include <boost/range/irange.hpp>
# include <boost/range/algorithm_ext/push_back.hpp>
2011-07-25 14:56:40 +00:00
# include <TColgp_Array1OfPnt.hxx>
# include <TColgp_Array1OfPnt2d.hxx>
# include <TColStd_Array1OfReal.hxx>
# include <TColStd_Array1OfInteger.hxx>
2015-10-07 17:33:30 +02:00
2011-07-25 14:56:40 +00:00
# include <Geom_Line.hxx>
# include <Geom_Circle.hxx>
# include <Geom_Ellipse.hxx>
# include <Geom_TrimmedCurve.hxx>
2015-10-07 17:33:30 +02:00
# include <Geom_Plane.hxx>
# include <Geom_OffsetCurve.hxx>
# include <Geom_OffsetSurface.hxx>
# include <Geom_CylindricalSurface.hxx>
# include <Geom_SurfaceOfLinearExtrusion.hxx>
# include <GeomAPI_IntCS.hxx>
# include <GeomAPI_IntSS.hxx>
# include <BRepBndLib.hxx>
2011-07-25 14:56:40 +00:00
# include <BRepOffsetAPI_Sewing.hxx>
# include <BRepBuilderAPI_MakeFace.hxx>
# include <BRepBuilderAPI_MakeEdge.hxx>
# include <BRepBuilderAPI_MakeWire.hxx>
# include <BRepBuilderAPI_MakePolygon.hxx>
# include <BRepBuilderAPI_MakeVertex.hxx>
# include <TopoDS.hxx>
# include <TopoDS_Wire.hxx>
# include <TopoDS_Face.hxx>
2016-02-18 13:17:15 +01:00
# include <TopoDS_CompSolid.hxx>
# include <TopExp.hxx>
2011-07-25 14:56:40 +00:00
# include <TopExp_Explorer.hxx>
# include <BRepPrimAPI_MakePrism.hxx>
# include <BRepBuilderAPI_MakeShell.hxx>
# include <BRepBuilderAPI_MakeSolid.hxx>
# include <BRepPrimAPI_MakeHalfSpace.hxx>
# include <BRepAlgoAPI_Cut.hxx>
2014-07-06 14:21:56 +00:00
# include <BRepAlgoAPI_Fuse.hxx>
2015-10-07 17:33:30 +02:00
# include <BRepAlgoAPI_Common.hxx>
2017-02-16 15:54:34 +01:00
# include <BRepAlgoAPI_BooleanOperation.hxx>
2018-12-19 16:10:33 +01:00
# if OCC_VERSION_HEX >= 0x70200
# include <BRepAlgoAPI_Splitter.hxx>
# endif
2011-07-25 14:56:40 +00:00
2016-04-16 14:47:09 +02:00
# include <BRepAlgo_NormalProjection.hxx>
2011-07-25 14:56:40 +00:00
# include <ShapeFix_Shape.hxx>
# include <ShapeFix_ShapeTolerance.hxx>
# include <ShapeFix_Solid.hxx>
2019-02-12 13:59:49 +01:00
# include <ShapeFix_Shell.hxx>
2011-07-25 14:56:40 +00:00
2015-10-07 17:33:30 +02:00
# include <ShapeAnalysis_Curve.hxx>
2018-04-18 11:33:57 +02:00
# include <ShapeAnalysis_Wire.hxx>
2015-10-07 17:33:30 +02:00
# include <ShapeAnalysis_Surface.hxx>
2017-02-16 15:54:34 +01:00
# include <ShapeAnalysis_ShapeTolerance.hxx>
2015-10-07 17:33:30 +02:00
2019-04-24 16:32:06 +02:00
# include <ShapeUpgrade_UnifySameDomain.hxx>
2011-07-25 14:56:40 +00:00
# include <BRepFilletAPI_MakeFillet2d.hxx>
# include <TopLoc_Location.hxx>
2011-10-12 16:43:29 +00:00
# include <GProp_GProps.hxx>
# include <BRepGProp.hxx>
2018-05-05 16:09:12 +02:00
# include <BRepBuilderAPI_Copy.hxx>
2016-05-03 11:01:47 +02:00
# include <BRepBuilderAPI_Transform.hxx>
2011-10-23 17:11:06 +00:00
# include <BRepBuilderAPI_GTransform.hxx>
2012-03-13 11:56:52 +00:00
# include <BRepCheck_Analyzer.hxx>
# include <BRepGProp_Face.hxx>
2015-01-19 16:02:34 +00:00
# include <BRepMesh_IncrementalMesh.hxx>
2012-11-04 16:26:33 +00:00
# include <BRepTools.hxx>
2016-04-16 14:47:09 +02:00
# include <BRepTools_WireExplorer.hxx>
2012-11-04 16:26:33 +00:00
# include <Poly_Triangulation.hxx>
# include <Poly_Array1OfTriangle.hxx>
Add support for IfcBooleanResult, IfcBlock, IfcRectangularPyramid, IfcRightCircularCylinder, IfcRightCircularCone, IfcSphere, IfcCsgSolid, IfcCurveBoundedPlane, IfcRectangularTrimmedSurface, IfcSurfaceCurveSweptAreaSolid, IfcCylindricalSurface.
2014-03-22 10:31:51 +00:00
# include <TopTools_IndexedMapOfShape.hxx>
# include <TopTools_IndexedDataMapOfShapeListOfShape.hxx>
# include <TopTools_ListIteratorOfListOfShape.hxx>
2017-04-03 14:55:34 +02:00
# include <TopTools_HSequenceOfShape.hxx>
Add support for IfcBooleanResult, IfcBlock, IfcRectangularPyramid, IfcRightCircularCylinder, IfcRightCircularCone, IfcSphere, IfcCsgSolid, IfcCurveBoundedPlane, IfcRectangularTrimmedSurface, IfcSurfaceCurveSweptAreaSolid, IfcCylindricalSurface.
2014-03-22 10:31:51 +00:00
2015-10-07 17:33:30 +02:00
# include <BOPAlgo_PaveFiller.hxx>
# include <BOPAlgo_BOP.hxx>
# include <GCPnts_AbscissaPoint.hxx>
2019-04-24 16:32:06 +02:00
# include <BRepTopAdaptor_FClass2d.hxx>
2016-12-09 16:40:14 +01:00
# include <BRepClass3d_SolidClassifier.hxx>
2018-04-18 11:33:57 +02:00
# include <GeomAPI_ExtremaCurveCurve.hxx>
2019-08-16 10:42:48 +02:00
# include <Extrema_ExtCS.hxx>
2018-08-20 11:46:26 +02:00
# include <Extrema_ExtPC.hxx>
# include <BRepAdaptor_Curve.hxx>
2019-09-26 15:14:23 +02:00
# include <ShapeAnalysis_Edge.hxx>
# include <BRepExtrema_DistShapeShape.hxx>
2018-03-16 16:04:48 +01:00
# include "../ifcparse/macros.h"
2015-02-17 19:48:41 +00:00
# include "../ifcparse/IfcSIPrefix.h"
2015-10-07 17:33:30 +02:00
# include "../ifcparse/IfcFile.h"
2011-08-24 12:21:07 +00:00
# include "../ifcgeom/IfcGeom.h"
2018-09-12 13:26:11 +02:00
# include "../ifcgeom/IfcGeomTree.h"
2011-07-25 14:56:40 +00:00
2018-12-10 10:45:58 +01:00
# include <memory>
2016-01-25 13:30:33 +01:00
# if OCC_VERSION_HEX < 0x60900
2016-02-18 13:17:15 +01:00
# ifdef _MSC_VER
2016-01-25 13:30:33 +01:00
# pragma message("warning: You are linking against Open CASCADE version " OCC_VERSION_COMPLETE ". Version 6.9.0 introduces various improvements with relation to boolean operations. You are advised to upgrade.")
2016-02-18 13:17:15 +01:00
# else
2021-03-17 22:19:19 +02:00
# warning "You are linking against an older version of Open CASCADE. Version 6.9.0 introduces various improvements with relation to boolean operations. You are advised to upgrade."
2016-02-18 13:17:15 +01:00
# endif
2016-01-25 13:30:33 +01:00
# endif
2017-12-28 16:33:07 +01:00
namespace {
2018-09-05 14:42:59 +02:00
struct MAKE_TYPE_NAME ( factory_t ) {
2017-12-22 13:51:11 +01:00
IfcGeom : : Kernel * operator ( ) ( IfcParse : : IfcFile * file ) const {
IfcGeom : : MAKE_TYPE_NAME ( Kernel ) * k = new IfcGeom : : MAKE_TYPE_NAME ( Kernel ) ;
if ( file ) {
double unit_magnitude = 1. ;
// Set unit information from file
IfcSchema : : IfcProject : : list : : ptr projects = file - > instances_by_type < IfcSchema : : IfcProject > ( ) ;
if ( projects - > size ( ) = = 1 ) {
IfcSchema : : IfcProject * project = * projects - > begin ( ) ;
std : : pair < std : : string , double > unit_info = k - > initializeUnits ( project - > UnitsInContext ( ) ) ;
unit_magnitude = unit_info . second ;
2018-09-08 08:27:45 +02:00
} else {
2018-12-29 12:34:32 +01:00
Logger : : Warning ( " A single IfcProject is expected (encountered " + boost : : lexical_cast < std : : string > ( projects - > size ( ) ) + " ); unable to read unit information. " ) ;
2017-12-22 13:51:11 +01:00
}
// Set precision from file
double lowest_precision_encountered = std : : numeric_limits < double > : : infinity ( ) ;
bool any_precision_encountered = false ;
IfcSchema : : IfcGeometricRepresentationContext : : list : : it it ;
IfcSchema : : IfcGeometricRepresentationContext : : list : : ptr contexts =
2018-01-01 11:22:12 +01:00
file - > instances_by_type_excl_subtypes < IfcSchema : : IfcGeometricRepresentationContext > ( ) ;
2017-12-22 13:51:11 +01:00
for ( it = contexts - > begin ( ) ; it ! = contexts - > end ( ) ; + + it ) {
IfcSchema : : IfcGeometricRepresentationContext * context = * it ;
if ( context - > hasPrecision ( ) & & context - > Precision ( ) < lowest_precision_encountered ) {
// Some arbitrary factor that has proven to work better for the models in the set of test files.
lowest_precision_encountered = context - > Precision ( ) * unit_magnitude * 10. ;
any_precision_encountered = true ;
}
}
double precision_to_set = 1.e-5 ;
if ( any_precision_encountered ) {
if ( lowest_precision_encountered < 1.e-7 ) {
Logger : : Message ( Logger : : LOG_WARNING , " Precision lower than 0.0000001 meter not enforced " ) ;
precision_to_set = 1.e-7 ;
} else {
precision_to_set = lowest_precision_encountered ;
}
}
k - > setValue ( IfcGeom : : Kernel : : GV_PRECISION , precision_to_set ) ;
}
return k ;
}
2017-12-28 16:33:07 +01:00
} ;
}
2017-12-22 13:51:11 +01:00
2017-12-28 16:33:07 +01:00
void MAKE_INIT_FN ( KernelImplementation_ ) ( IfcGeom : : impl : : KernelFactoryImplementation * mapping ) {
static const std : : string schema_name = STRINGIFY ( IfcSchema ) ;
2018-09-05 14:42:59 +02:00
MAKE_TYPE_NAME ( factory_t ) factory ;
2017-12-22 13:51:11 +01:00
mapping - > bind ( schema_name , factory ) ;
}
2017-12-20 16:38:55 +01:00
# define Kernel MAKE_TYPE_NAME(Kernel)
2018-09-21 10:22:24 +02:00
namespace {
2018-09-21 11:21:51 +02:00
void copy_operand ( const TopTools_ListOfShape & l , TopTools_ListOfShape & r ) {
2018-09-21 10:22:24 +02:00
# if OCC_VERSION_HEX < 0x70000
TopTools_ListIteratorOfListOfShape it ( l ) ;
for ( ; it . More ( ) ; it . Next ( ) ) {
r . Append ( BRepBuilderAPI_Copy ( it . Value ( ) ) ) ;
}
# else
// On OCCT 7.0 and higher BRepAlgoAPI_BuilderAlgo::SetNonDestructive(true) is
// called. Not entirely sure on the behaviour before 7.0, so overcautiously
// create copies.
2018-09-21 11:21:51 +02:00
r . Assign ( l ) ;
2018-09-21 10:22:24 +02:00
# endif
}
TopoDS_Shape copy_operand ( const TopoDS_Shape & s ) {
# if OCC_VERSION_HEX < 0x70000
return BRepBuilderAPI_Copy ( s ) ;
# else
return s ;
# endif
}
double min_edge_length ( const TopoDS_Shape & a ) {
double min_edge_len = std : : numeric_limits < double > : : infinity ( ) ;
TopExp_Explorer exp ( a , TopAbs_EDGE ) ;
for ( ; exp . More ( ) ; exp . Next ( ) ) {
GProp_GProps prop ;
BRepGProp : : LinearProperties ( exp . Current ( ) , prop ) ;
double l = prop . Mass ( ) ;
if ( l < min_edge_len ) {
min_edge_len = l ;
}
}
return min_edge_len ;
}
2018-12-19 16:39:54 +01:00
double min_vertex_edge_distance ( const TopoDS_Shape & a , double min_search , double max_search ) {
2018-09-21 10:22:24 +02:00
double M = std : : numeric_limits < double > : : infinity ( ) ;
2018-12-19 16:39:54 +01:00
TopTools_IndexedMapOfShape vertices , edges ;
2018-09-21 10:22:24 +02:00
2018-12-19 16:39:54 +01:00
TopExp : : MapShapes ( a , TopAbs_VERTEX , vertices ) ;
TopExp : : MapShapes ( a , TopAbs_EDGE , edges ) ;
2018-09-21 10:22:24 +02:00
2018-12-19 16:39:54 +01:00
IfcGeom : : impl : : tree < int > tree ;
// Add edges to tree
for ( int i = 1 ; i < = edges . Extent ( ) ; + + i ) {
tree . add ( i , edges ( i ) ) ;
}
for ( int j = 1 ; j < = vertices . Extent ( ) ; + + j ) {
const TopoDS_Vertex & v = TopoDS : : Vertex ( vertices ( j ) ) ;
2018-09-21 10:22:24 +02:00
gp_Pnt p = BRep_Tool : : Pnt ( v ) ;
2018-12-19 16:39:54 +01:00
Bnd_Box b ;
b . Add ( p ) ;
b . Enlarge ( max_search ) ;
std : : vector < int > edge_idxs = tree . select_box ( b , false ) ;
std : : vector < int > : : const_iterator it = edge_idxs . begin ( ) ;
for ( ; it ! = edge_idxs . end ( ) ; + + it ) {
const TopoDS_Edge & e = TopoDS : : Edge ( edges ( * it ) ) ;
2018-09-21 10:22:24 +02:00
TopoDS_Vertex v1 , v2 ;
TopExp : : Vertices ( e , v1 , v2 ) ;
if ( v . IsSame ( v1 ) | | v . IsSame ( v2 ) ) {
continue ;
}
BRepAdaptor_Curve crv ( e ) ;
Extrema_ExtPC ext ( p , crv ) ;
if ( ! ext . IsDone ( ) ) {
continue ;
}
for ( int i = 1 ; i < = ext . NbExt ( ) ; + + i ) {
const double m = sqrt ( ext . SquareDistance ( i ) ) ;
2018-12-19 16:39:54 +01:00
if ( m < M & & m > min_search ) {
2018-09-21 10:22:24 +02:00
M = m ;
}
}
}
}
return M ;
}
2019-04-24 16:32:06 +02:00
class points_on_planar_face_generator {
private :
const TopoDS_Face & f_ ;
Handle ( Geom_Surface ) plane_ ;
BRepTopAdaptor_FClass2d cls_ ;
double u0 , u1 , v0 , v1 ;
int i , j ;
2019-09-26 15:14:23 +02:00
bool inset_ ;
2019-04-24 16:32:06 +02:00
static const int N = 10 ;
public :
2019-09-26 15:14:23 +02:00
points_on_planar_face_generator ( const TopoDS_Face & f , bool inset = false )
2019-04-24 16:32:06 +02:00
: f_ ( f )
, plane_ ( BRep_Tool : : Surface ( f_ ) )
, cls_ ( f_ , BRep_Tool : : Tolerance ( f_ ) )
2019-09-26 15:14:23 +02:00
, i ( ( int ) inset ) , j ( ( int ) inset )
, inset_ ( inset )
2019-04-24 16:32:06 +02:00
{
BRepTools : : UVBounds ( f_ , u0 , u1 , v0 , v1 ) ;
}
void reset ( ) {
2019-09-26 15:14:23 +02:00
i = j = ( int ) inset_ ;
2019-04-24 16:32:06 +02:00
}
bool operator ( ) ( gp_Pnt & p ) {
while ( j < N ) {
double u = u0 + ( u1 - u0 ) * i / N ;
double v = v0 + ( v1 - v0 ) * j / N ;
i + + ;
if ( i = = N ) {
i = 0 ;
j + + ;
}
// Specifically does not consider ON
if ( cls_ . Perform ( gp_Pnt2d ( u , v ) ) = = TopAbs_IN ) {
plane_ - > D0 ( u , v , p ) ;
return true ;
}
}
return false ;
}
} ;
2019-09-26 15:14:23 +02:00
bool faces_overlap ( const TopoDS_Face & f , const TopoDS_Face & g ) {
points_on_planar_face_generator pgen ( f ) ;
BRep_Builder B ;
gp_Pnt test ;
double eps = BRep_Tool : : Tolerance ( f ) + BRep_Tool : : Tolerance ( g ) ;
BRepExtrema_DistShapeShape x ;
x . LoadS1 ( g ) ;
while ( pgen ( test ) ) {
TopoDS_Vertex V ;
B . MakeVertex ( V , test , Precision : : Confusion ( ) ) ;
x . LoadS2 ( V ) ;
x . Perform ( ) ;
if ( x . IsDone ( ) & & x . NbSolution ( ) = = 1 ) {
if ( x . Value ( ) > eps ) {
return false ;
}
}
}
return true ;
}
2019-04-24 16:32:06 +02:00
double min_face_face_distance ( const TopoDS_Shape & a , double max_search ) {
/*
NB: This is currently only implemented for planar surfaces.
*/
double M = std : : numeric_limits < double > : : infinity ( ) ;
TopTools_IndexedMapOfShape faces ;
TopExp : : MapShapes ( a , TopAbs_FACE , faces ) ;
IfcGeom : : impl : : tree < int > tree ;
2019-05-24 17:16:31 +02:00
// Add faces to tree
2019-04-24 16:32:06 +02:00
for ( int i = 1 ; i < = faces . Extent ( ) ; + + i ) {
if ( BRep_Tool : : Surface ( TopoDS : : Face ( faces ( i ) ) ) - > DynamicType ( ) = = STANDARD_TYPE ( Geom_Plane ) ) {
tree . add ( i , faces ( i ) ) ;
}
}
for ( int j = 1 ; j < = faces . Extent ( ) ; + + j ) {
const TopoDS_Face & f = TopoDS : : Face ( faces ( j ) ) ;
const Handle ( Geom_Surface ) & fs = BRep_Tool : : Surface ( f ) ;
if ( fs - > DynamicType ( ) ! = STANDARD_TYPE ( Geom_Plane ) ) {
continue ;
}
points_on_planar_face_generator pgen ( f ) ;
Bnd_Box b ;
BRepBndLib : : AddClose ( f , b ) ;
b . Enlarge ( max_search ) ;
2019-05-24 17:16:31 +02:00
std : : vector < int > face_idxs = tree . select_box ( b , false ) ;
std : : vector < int > : : const_iterator it = face_idxs . begin ( ) ;
for ( ; it ! = face_idxs . end ( ) ; + + it ) {
2019-04-24 16:32:06 +02:00
if ( * it = = j ) {
continue ;
}
const TopoDS_Face & g = TopoDS : : Face ( faces ( * it ) ) ;
const Handle ( Geom_Surface ) & gs = BRep_Tool : : Surface ( g ) ;
auto p0 = Handle ( Geom_Plane ) : : DownCast ( fs ) ;
auto p1 = Handle ( Geom_Plane ) : : DownCast ( gs ) ;
if ( p0 - > Position ( ) . IsCoplanar ( p1 - > Position ( ) , max_search , asin ( max_search ) ) ) {
pgen . reset ( ) ;
BRepTopAdaptor_FClass2d cls ( g , BRep_Tool : : Tolerance ( g ) ) ;
gp_Pnt test ;
while ( pgen ( test ) ) {
gp_Vec d = test . XYZ ( ) - p1 - > Position ( ) . Location ( ) . XYZ ( ) ;
double u = d . Dot ( p1 - > Position ( ) . XDirection ( ) ) ;
double v = d . Dot ( p1 - > Position ( ) . YDirection ( ) ) ;
2019-05-24 17:16:31 +02:00
// nb: TopAbs_ON is explicitly not considered to prevent matching adjacent faces
// with similar orientations.
2019-04-24 16:32:06 +02:00
if ( cls . Perform ( gp_Pnt2d ( u , v ) ) = = TopAbs_IN ) {
gp_Pnt test2 ;
p1 - > D0 ( u , v , test2 ) ;
2019-10-15 14:14:50 +02:00
double w = std : : abs ( gp_Vec ( p1 - > Position ( ) . Direction ( ) . XYZ ( ) ) . Dot ( test2 . XYZ ( ) - test . XYZ ( ) ) ) ;
2019-04-24 16:32:06 +02:00
if ( w < M ) {
M = w ;
}
}
}
}
}
}
return M ;
}
2018-09-21 10:22:24 +02:00
void bounding_box_overlap ( double p , const TopoDS_Shape & a , const TopTools_ListOfShape & b , TopTools_ListOfShape & c ) {
Bnd_Box A ;
BRepBndLib : : Add ( a , A ) ;
2018-09-25 12:47:06 +02:00
if ( A . IsVoid ( ) ) {
return ;
}
2018-09-21 10:22:24 +02:00
TopTools_ListIteratorOfListOfShape it ( b ) ;
for ( ; it . More ( ) ; it . Next ( ) ) {
Bnd_Box B ;
BRepBndLib : : Add ( it . Value ( ) , B ) ;
2018-09-25 12:47:06 +02:00
if ( B . IsVoid ( ) ) {
continue ;
}
2018-09-21 10:22:24 +02:00
if ( A . Distance ( B ) < p ) {
c . Append ( it . Value ( ) ) ;
}
}
}
2019-04-24 16:32:06 +02:00
2019-05-24 17:16:31 +02:00
TopoDS_Shape unify ( const TopoDS_Shape & s , double tolerance ) {
2019-06-04 14:24:19 +02:00
tolerance = ( std : : min ) ( min_edge_length ( s ) / 2. , tolerance ) ;
2019-04-24 16:32:06 +02:00
ShapeUpgrade_UnifySameDomain usd ( s ) ;
2019-09-28 23:14:08 +10:00
# if OCC_VERSION_HEX >= 0x70200
2019-05-24 17:16:31 +02:00
usd . SetSafeInputMode ( true ) ;
2019-09-28 23:14:08 +10:00
# endif
# if OCC_VERSION_HEX >= 0x70100
2019-05-24 17:16:31 +02:00
usd . SetLinearTolerance ( tolerance ) ;
2019-06-04 14:24:19 +02:00
usd . SetAngularTolerance ( 1.e-3 ) ;
2019-09-28 23:14:08 +10:00
# endif
2019-04-24 16:32:06 +02:00
usd . Build ( ) ;
return usd . Shape ( ) ;
}
2020-01-31 15:55:04 +01:00
gp_Trsf combine_offset_and_rotation ( const gp_Vec & offset , const gp_Quaternion & rotation ) {
auto offset_transform = gp_Trsf { } ;
offset_transform . SetTranslation ( offset ) ;
auto rotation_transform = gp_Trsf { } ;
rotation_transform . SetRotation ( rotation ) ;
return rotation_transform * offset_transform ;
}
}
void IfcGeom : : Kernel : : set_offset ( const std : : array < double , 3 > & p_offset ) {
offset = gp_Vec ( p_offset [ 0 ] , p_offset [ 1 ] , p_offset [ 2 ] ) ;
offset_and_rotation = combine_offset_and_rotation ( offset , rotation ) ;
}
void IfcGeom : : Kernel : : set_rotation ( const std : : array < double , 4 > & p_rotation ) {
rotation = gp_Quaternion ( p_rotation [ 0 ] , p_rotation [ 1 ] , p_rotation [ 2 ] , p_rotation [ 3 ] ) ;
offset_and_rotation = combine_offset_and_rotation ( offset , rotation ) ;
2018-09-21 10:22:24 +02:00
}
2021-04-24 20:47:25 +02:00
bool IfcGeom : : Kernel : : shape_to_face_list ( const TopoDS_Shape & s , TopTools_ListOfShape & li ) {
TopExp_Explorer exp ( s , TopAbs_FACE ) ;
2016-12-09 16:40:14 +01:00
for ( ; exp . More ( ) ; exp . Next ( ) ) {
TopoDS_Face face = TopoDS : : Face ( exp . Current ( ) ) ;
2021-04-24 20:47:25 +02:00
li . Append ( face ) ;
2016-12-09 16:40:14 +01:00
}
2021-04-24 20:47:25 +02:00
return true ;
}
2016-12-09 16:40:14 +01:00
2021-04-24 20:47:25 +02:00
bool IfcGeom : : Kernel : : create_solid_from_compound ( const TopoDS_Shape & compound , TopoDS_Shape & shape ) {
TopTools_ListOfShape face_list ;
shape_to_face_list ( compound , face_list ) ;
2016-12-09 16:40:14 +01:00
if ( face_list . Extent ( ) = = 0 ) {
return false ;
}
return create_solid_from_faces ( face_list , shape ) ;
}
2021-04-24 20:47:25 +02:00
bool IfcGeom : : Kernel : : create_solid_from_faces ( const TopTools_ListOfShape & face_list , TopoDS_Shape & shape , bool force_sewing ) {
2016-12-09 16:40:14 +01:00
bool valid_shell = false ;
2018-12-17 13:25:28 +01:00
if ( face_list . Extent ( ) = = 1 ) {
shape = face_list . First ( ) ;
// A bit dubious what to return here.
return true ;
} else if ( face_list . Extent ( ) = = 0 ) {
return false ;
}
2018-09-12 16:44:30 +02:00
2019-02-12 13:59:49 +01:00
TopTools_ListIteratorOfListOfShape face_iterator ;
2016-12-09 16:40:14 +01:00
2019-02-12 13:59:49 +01:00
bool has_shared_edges = false ;
TopTools_MapOfShape edge_set ;
2019-04-24 17:15:18 +02:00
// In case there are wire interesections or failures in non-planar wire triangulations
// the idea is to let occt do an exhaustive search of edge partners. But we have not
// found a case where this actually improves boolean ops later on.
// if (!faceset_helper_ || !faceset_helper_->non_manifold()) {
2021-04-24 20:47:25 +02:00
for ( face_iterator . Initialize ( face_list ) ; ! force_sewing & & face_iterator . More ( ) ; face_iterator . Next ( ) ) {
2019-04-24 17:15:18 +02:00
// As soon as is detected one of the edges is shared, the assumption is made no
// additional sewing is necessary.
if ( ! has_shared_edges ) {
TopExp_Explorer exp ( face_iterator . Value ( ) , TopAbs_EDGE ) ;
for ( ; exp . More ( ) ; exp . Next ( ) ) {
if ( edge_set . Contains ( exp . Current ( ) ) ) {
has_shared_edges = true ;
break ;
2019-02-12 13:59:49 +01:00
}
2019-04-24 17:15:18 +02:00
edge_set . Add ( exp . Current ( ) ) ;
2019-02-12 13:59:49 +01:00
}
}
}
BRepOffsetAPI_Sewing sewing_builder ;
sewing_builder . SetTolerance ( getValue ( GV_PRECISION ) ) ;
sewing_builder . SetMaxTolerance ( getValue ( GV_PRECISION ) ) ;
sewing_builder . SetMinTolerance ( getValue ( GV_PRECISION ) ) ;
BRep_Builder builder ;
TopoDS_Shell shell ;
builder . MakeShell ( shell ) ;
2018-09-12 16:44:30 +02:00
2016-12-09 16:40:14 +01:00
for ( face_iterator . Initialize ( face_list ) ; face_iterator . More ( ) ; face_iterator . Next ( ) ) {
2019-02-12 13:59:49 +01:00
if ( has_shared_edges ) {
builder . Add ( shell , face_iterator . Value ( ) ) ;
} else {
sewing_builder . Add ( face_iterator . Value ( ) ) ;
}
2012-03-01 21:17:06 +00:00
}
2016-12-09 16:40:14 +01:00
try {
2019-02-12 13:59:49 +01:00
if ( has_shared_edges ) {
ShapeFix_Shell fix ;
fix . FixFaceOrientation ( shell ) ;
shape = fix . Shape ( ) ;
} else {
sewing_builder . Perform ( ) ;
shape = sewing_builder . SewedShape ( ) ;
2018-10-30 09:05:18 +01:00
}
2019-02-12 13:59:49 +01:00
2018-10-30 09:05:18 +01:00
BRepCheck_Analyzer ana ( shape ) ;
2019-02-12 13:59:49 +01:00
valid_shell = ana . IsValid ( ) ;
if ( ! valid_shell ) {
ShapeFix_Shape sfs ( shape ) ;
sfs . Perform ( ) ;
shape = sfs . Shape ( ) ;
BRepCheck_Analyzer reana ( shape ) ;
valid_shell = reana . IsValid ( ) ;
}
valid_shell & = count ( shape , TopAbs_SHELL ) > 0 ;
2017-08-01 16:45:11 +02:00
} catch ( const Standard_Failure & e ) {
if ( e . GetMessageString ( ) & & strlen ( e . GetMessageString ( ) ) ) {
Logger : : Error ( e . GetMessageString ( ) ) ;
} else {
Logger : : Error ( " Unknown error sewing shell " ) ;
}
} catch ( . . . ) {
Logger : : Error ( " Unknown error sewing shell " ) ;
}
2016-12-09 16:40:14 +01:00
if ( valid_shell ) {
2018-04-11 12:26:01 +02:00
2016-12-09 16:40:14 +01:00
TopoDS_Shape complete_shape ;
TopExp_Explorer exp ( shape , TopAbs_SHELL ) ;
2018-04-11 12:26:01 +02:00
2016-12-09 16:40:14 +01:00
for ( ; exp . More ( ) ; exp . Next ( ) ) {
TopoDS_Shape result_shape = exp . Current ( ) ;
try {
ShapeFix_Solid solid ;
2017-11-27 16:27:36 +01:00
solid . SetMaxTolerance ( getValue ( GV_PRECISION ) ) ;
2016-12-09 16:40:14 +01:00
TopoDS_Solid solid_shape = solid . SolidFromShell ( TopoDS : : Shell ( exp . Current ( ) ) ) ;
2018-09-24 12:56:07 +02:00
// @todo: BRepClass3d_SolidClassifier::PerformInfinitePoint() is done by SolidFromShell
// and this is done again, to be able to catch errors during this process.
// This is double work that should be avoided.
2016-12-09 16:40:14 +01:00
if ( ! solid_shape . IsNull ( ) ) {
try {
BRepClass3d_SolidClassifier classifier ( solid_shape ) ;
result_shape = solid_shape ;
classifier . PerformInfinitePoint ( getValue ( GV_PRECISION ) ) ;
if ( classifier . State ( ) = = TopAbs_IN ) {
shape . Reverse ( ) ;
}
2017-08-01 16:45:11 +02:00
} catch ( const Standard_Failure & e ) {
if ( e . GetMessageString ( ) & & strlen ( e . GetMessageString ( ) ) ) {
Logger : : Error ( e . GetMessageString ( ) ) ;
} else {
Logger : : Error ( " Unknown error classifying solid " ) ;
}
} catch ( . . . ) {
Logger : : Error ( " Unknown error classifying solid " ) ;
}
}
} catch ( const Standard_Failure & e ) {
if ( e . GetMessageString ( ) & & strlen ( e . GetMessageString ( ) ) ) {
Logger : : Error ( e . GetMessageString ( ) ) ;
} else {
Logger : : Error ( " Unknown error creating solid " ) ;
2016-12-09 16:40:14 +01:00
}
2017-08-01 16:45:11 +02:00
} catch ( . . . ) {
Logger : : Error ( " Unknown error creating solid " ) ;
}
2016-12-09 16:40:14 +01:00
if ( complete_shape . IsNull ( ) ) {
complete_shape = result_shape ;
} else {
BRep_Builder B ;
if ( complete_shape . ShapeType ( ) ! = TopAbs_COMPOUND ) {
TopoDS_Compound C ;
B . MakeCompound ( C ) ;
B . Add ( C , complete_shape ) ;
complete_shape = C ;
2018-12-29 12:34:32 +01:00
Logger : : Warning ( " Multiple components in IfcConnectedFaceSet " ) ;
2016-12-09 16:40:14 +01:00
}
B . Add ( complete_shape , result_shape ) ;
}
}
2018-04-11 12:26:01 +02:00
TopExp_Explorer loose_faces ( shape , TopAbs_FACE , TopAbs_SHELL ) ;
for ( ; loose_faces . More ( ) ; loose_faces . Next ( ) ) {
BRep_Builder B ;
if ( complete_shape . ShapeType ( ) ! = TopAbs_COMPOUND ) {
TopoDS_Compound C ;
B . MakeCompound ( C ) ;
B . Add ( C , complete_shape ) ;
complete_shape = C ;
2018-12-29 12:34:32 +01:00
Logger : : Warning ( " Loose faces in IfcConnectedFaceSet " ) ;
2018-04-11 12:26:01 +02:00
}
B . Add ( complete_shape , loose_faces . Current ( ) ) ;
}
2016-12-09 16:40:14 +01:00
shape = complete_shape ;
2018-04-11 12:26:01 +02:00
2016-12-09 16:40:14 +01:00
} else {
2018-12-29 12:34:32 +01:00
Logger : : Error ( " Failed to sew faceset " ) ;
2016-03-18 11:23:25 +01:00
}
2016-12-09 16:40:14 +01:00
return valid_shell ;
2012-03-01 21:17:06 +00:00
}
2014-12-19 12:14:36 +00:00
bool IfcGeom : : Kernel : : is_compound ( const TopoDS_Shape & shape ) {
2012-03-13 11:56:52 +00:00
bool has_solids = TopExp_Explorer ( shape , TopAbs_SOLID ) . More ( ) ! = 0 ;
2012-03-01 21:17:06 +00:00
bool has_shells = TopExp_Explorer ( shape , TopAbs_SHELL ) . More ( ) ! = 0 ;
bool has_compounds = TopExp_Explorer ( shape , TopAbs_COMPOUND ) . More ( ) ! = 0 ;
bool has_faces = TopExp_Explorer ( shape , TopAbs_FACE ) . More ( ) ! = 0 ;
return has_compounds & & has_faces & & ! has_solids & & ! has_shells ;
}
2014-12-19 12:14:36 +00:00
const TopoDS_Shape & IfcGeom : : Kernel : : ensure_fit_for_subtraction ( const TopoDS_Shape & shape , TopoDS_Shape & solid ) {
const bool is_comp = is_compound ( shape ) ;
2015-06-21 16:14:37 +00:00
if ( ! is_comp ) {
return solid = shape ;
}
2016-12-09 16:40:14 +01:00
if ( ! create_solid_from_compound ( shape , solid ) ) {
return solid = shape ;
}
2014-06-09 12:56:47 +00:00
// If the SEW_SHELLS option had been set this precision had been applied
2014-12-19 12:14:36 +00:00
// at the end of the generic convert_shape() call.
const double precision = getValue ( GV_PRECISION ) ;
apply_tolerance ( solid , precision ) ;
2014-06-09 12:56:47 +00:00
2012-03-01 21:17:06 +00:00
return solid ;
}
2021-05-12 09:58:11 +02:00
// @nb this function is only in use on older versions of occt.
2014-12-19 12:14:36 +00:00
bool IfcGeom : : Kernel : : convert_openings ( const IfcSchema : : IfcProduct * entity , const IfcSchema : : IfcRelVoidsElement : : list : : ptr & openings ,
const IfcGeom : : IfcRepresentationShapeItems & entity_shapes , const gp_Trsf & entity_trsf , IfcGeom : : IfcRepresentationShapeItems & cut_shapes ) {
2015-05-22 15:41:56 +00:00
// TODO: Refactor convert_openings() convert_openings_fast() and convert(IfcBooleanResult) to use
// the same code base and conform to the same checks and logging messages.
2011-10-23 17:11:06 +00:00
// Iterate over IfcOpeningElements
2013-05-05 08:49:25 +00:00
IfcGeom : : IfcRepresentationShapeItems opening_shapes ;
2011-10-23 17:11:06 +00:00
unsigned int last_size = 0 ;
2014-05-04 14:42:55 +00:00
for ( IfcSchema : : IfcRelVoidsElement : : list : : it it = openings - > begin ( ) ; it ! = openings - > end ( ) ; + + it ) {
IfcSchema : : IfcRelVoidsElement * v = * it ;
IfcSchema : : IfcFeatureElementSubtraction * fes = v - > RelatedOpeningElement ( ) ;
2017-12-18 15:25:09 +01:00
if ( fes - > declaration ( ) . is ( IfcSchema : : IfcOpeningElement : : Class ( ) ) ) {
2015-12-05 17:14:10 +01:00
if ( ! fes - > hasRepresentation ( ) ) continue ;
2011-10-23 17:11:06 +00:00
// Convert the IfcRepresentation of the IfcOpeningElement
gp_Trsf opening_trsf ;
2015-12-05 17:14:10 +01:00
if ( fes - > hasObjectPlacement ( ) ) {
try {
convert ( fes - > ObjectPlacement ( ) , opening_trsf ) ;
2017-08-01 16:45:11 +02:00
} catch ( const std : : exception & e ) {
Logger : : Error ( e ) ;
} catch ( . . . ) {
Logger : : Error ( " Failed to construct placement " ) ;
}
2015-12-05 17:14:10 +01:00
}
2011-10-23 17:11:06 +00:00
// Move the opening into the coordinate system of the IfcProduct
opening_trsf . PreMultiply ( entity_trsf . Inverted ( ) ) ;
2014-05-04 14:42:55 +00:00
IfcSchema : : IfcProductRepresentation * prodrep = fes - > Representation ( ) ;
IfcSchema : : IfcRepresentation : : list : : ptr reps = prodrep - > Representations ( ) ;
2011-10-23 17:11:06 +00:00
2014-05-04 14:42:55 +00:00
for ( IfcSchema : : IfcRepresentation : : list : : it it2 = reps - > begin ( ) ; it2 ! = reps - > end ( ) ; + + it2 ) {
2014-12-19 12:14:36 +00:00
convert_shapes ( * it2 , opening_shapes ) ;
2011-10-23 17:11:06 +00:00
}
2021-03-18 08:41:12 +02:00
const unsigned int current_size = opening_shapes . size ( ) ;
2011-10-23 17:11:06 +00:00
for ( unsigned int i = last_size ; i < current_size ; + + i ) {
2013-07-20 22:12:40 +00:00
opening_shapes [ i ] . prepend ( opening_trsf ) ;
2011-07-25 14:56:40 +00:00
}
2011-10-23 17:11:06 +00:00
last_size = current_size ;
2011-07-25 14:56:40 +00:00
}
}
2011-10-23 17:11:06 +00:00
// Iterate over the shapes of the IfcProduct
2013-05-05 08:49:25 +00:00
for ( IfcGeom : : IfcRepresentationShapeItems : : const_iterator it3 = entity_shapes . begin ( ) ; it3 ! = entity_shapes . end ( ) ; + + it3 ) {
2012-03-01 21:17:06 +00:00
TopoDS_Shape entity_shape_solid ;
2014-12-19 12:14:36 +00:00
const TopoDS_Shape & entity_shape_unlocated = ensure_fit_for_subtraction ( it3 - > Shape ( ) , entity_shape_solid ) ;
2013-05-05 08:49:25 +00:00
const gp_GTrsf & entity_shape_gtrsf = it3 - > Placement ( ) ;
2011-10-23 17:11:06 +00:00
if ( entity_shape_gtrsf . Form ( ) = = gp_Other ) {
2017-12-11 15:07:40 +01:00
Logger : : Message ( Logger : : LOG_WARNING , " Applying non uniform transformation to: " , entity ) ;
2011-10-23 17:11:06 +00:00
}
2016-05-03 11:01:47 +02:00
TopoDS_Shape entity_shape = apply_transformation ( entity_shape_unlocated , entity_shape_gtrsf ) ;
2011-10-23 17:11:06 +00:00
// Iterate over the shapes of the IfcOpeningElements
2013-05-05 08:49:25 +00:00
for ( IfcGeom : : IfcRepresentationShapeItems : : const_iterator it4 = opening_shapes . begin ( ) ; it4 ! = opening_shapes . end ( ) ; + + it4 ) {
2012-03-01 21:17:06 +00:00
TopoDS_Shape opening_shape_solid ;
2014-12-19 12:14:36 +00:00
const TopoDS_Shape & opening_shape_unlocated = ensure_fit_for_subtraction ( it4 - > Shape ( ) , opening_shape_solid ) ;
2013-05-05 08:49:25 +00:00
const gp_GTrsf & opening_shape_gtrsf = it4 - > Placement ( ) ;
2011-10-23 17:11:06 +00:00
if ( opening_shape_gtrsf . Form ( ) = = gp_Other ) {
2017-12-11 15:07:40 +01:00
Logger : : Message ( Logger : : LOG_WARNING , " Applying non uniform transformation to opening of: " , entity ) ;
2011-10-23 17:11:06 +00:00
}
2016-05-03 11:01:47 +02:00
TopoDS_Shape opening_shape = apply_transformation ( opening_shape_unlocated , opening_shape_gtrsf ) ;
2011-10-23 17:11:06 +00:00
2015-11-21 23:05:28 +02:00
double opening_volume ;
2017-06-05 13:08:07 +02:00
if ( Logger : : LOG_WARNING > = Logger : : Verbosity ( ) ) {
2012-06-07 14:38:21 +00:00
opening_volume = shape_volume ( opening_shape ) ;
if ( opening_volume < = ALMOST_ZERO )
2017-12-11 15:07:40 +01:00
Logger : : Message ( Logger : : LOG_WARNING , " Empty opening for: " , entity ) ;
2012-06-07 14:38:21 +00:00
}
2012-03-01 21:17:06 +00:00
2014-07-24 11:22:31 +00:00
if ( entity_shape . ShapeType ( ) = = TopAbs_COMPSOLID ) {
// For compound solids process the subtraction for the constituent
// solids individually and write the result back as a compound solid.
TopoDS_CompSolid compound ;
BRep_Builder builder ;
builder . MakeCompSolid ( compound ) ;
TopExp_Explorer exp ( entity_shape , TopAbs_SOLID ) ;
for ( ; exp . More ( ) ; exp . Next ( ) ) {
2016-01-22 17:27:28 +01:00
# if OCC_VERSION_HEX < 0x60900
2014-07-24 11:22:31 +00:00
BRepAlgoAPI_Cut brep_cut ( exp . Current ( ) , opening_shape ) ;
2016-01-22 17:27:28 +01:00
# else
BRepAlgoAPI_Cut brep_cut ;
TopTools_ListOfShape s1s ;
s1s . Append ( exp . Current ( ) ) ;
TopTools_ListOfShape s2s ;
s2s . Append ( opening_shape ) ;
brep_cut . SetFuzzyValue ( getValue ( GV_PRECISION ) ) ;
brep_cut . SetArguments ( s1s ) ;
brep_cut . SetTools ( s2s ) ;
brep_cut . Build ( ) ;
# endif
2014-07-24 11:22:31 +00:00
bool added = false ;
if ( brep_cut . IsDone ( ) ) {
TopoDS_Shape brep_cut_result = brep_cut ;
BRepCheck_Analyzer analyser ( brep_cut_result ) ;
bool is_valid = analyser . IsValid ( ) ! = 0 ;
if ( is_valid ) {
2015-11-21 23:05:28 +02:00
TopExp_Explorer exp2 ( brep_cut_result , TopAbs_SOLID ) ;
for ( ; exp2 . More ( ) ; exp2 . Next ( ) ) {
builder . Add ( compound , exp2 . Current ( ) ) ;
2014-07-24 11:22:31 +00:00
added = true ;
}
}
}
if ( ! added ) {
// Add the original in case subtraction fails
builder . Add ( compound , exp . Current ( ) ) ;
} else {
2017-12-11 15:07:40 +01:00
Logger : : Message ( Logger : : LOG_ERROR , " Failed to process subtraction: " , entity ) ;
2014-07-24 11:22:31 +00:00
}
}
entity_shape = compound ;
} else {
2016-01-22 17:27:28 +01:00
# if OCC_VERSION_HEX < 0x60900
2014-07-24 11:22:31 +00:00
BRepAlgoAPI_Cut brep_cut ( entity_shape , opening_shape ) ;
2016-01-22 17:27:28 +01:00
# else
BRepAlgoAPI_Cut brep_cut ;
TopTools_ListOfShape s1s ;
s1s . Append ( entity_shape ) ;
TopTools_ListOfShape s2s ;
s2s . Append ( opening_shape ) ;
brep_cut . SetFuzzyValue ( getValue ( GV_PRECISION ) ) ;
brep_cut . SetArguments ( s1s ) ;
brep_cut . SetTools ( s2s ) ;
brep_cut . Build ( ) ;
# endif
2014-07-24 11:22:31 +00:00
if ( brep_cut . IsDone ( ) ) {
TopoDS_Shape brep_cut_result = brep_cut ;
2015-05-22 15:41:56 +00:00
ShapeFix_Shape fix ( brep_cut_result ) ;
try {
fix . Perform ( ) ;
brep_cut_result = fix . Shape ( ) ;
} catch ( . . . ) {
2018-12-29 12:34:32 +01:00
Logger : : Error ( " Shape healing failed on opening subtraction result " , entity ) ;
2015-05-22 15:41:56 +00:00
}
2014-07-24 11:22:31 +00:00
BRepCheck_Analyzer analyser ( brep_cut_result ) ;
bool is_valid = analyser . IsValid ( ) ! = 0 ;
if ( is_valid ) {
2015-05-22 15:41:56 +00:00
entity_shape = brep_cut_result ;
2017-06-05 13:08:07 +02:00
if ( Logger : : LOG_WARNING > = Logger : : Verbosity ( ) ) {
2014-07-24 11:22:31 +00:00
const double volume_after_subtraction = shape_volume ( entity_shape ) ;
2015-11-21 23:05:28 +02:00
double original_shape_volume = shape_volume ( entity_shape ) ;
2014-07-24 11:22:31 +00:00
if ( ALMOST_THE_SAME ( original_shape_volume , volume_after_subtraction ) )
2017-12-11 15:07:40 +01:00
Logger : : Message ( Logger : : LOG_WARNING , " Subtraction yields unchanged volume: " , entity ) ;
2014-07-24 11:22:31 +00:00
}
} else {
2017-12-11 15:07:40 +01:00
Logger : : Message ( Logger : : LOG_ERROR , " Invalid result from subtraction: " , entity ) ;
2012-06-07 14:38:21 +00:00
}
2012-03-13 11:56:52 +00:00
} else {
2017-12-11 15:07:40 +01:00
Logger : : Message ( Logger : : LOG_ERROR , " Failed to process subtraction: " , entity ) ;
2012-03-13 11:56:52 +00:00
}
2012-03-01 21:17:06 +00:00
}
2012-03-13 11:56:52 +00:00
2011-10-23 17:11:06 +00:00
}
2018-12-10 11:33:54 +01:00
cut_shapes . push_back ( IfcGeom : : IfcRepresentationShapeItem ( it3 - > ItemId ( ) , it3 - > Placement ( ) , entity_shape , & it3 - > Style ( ) ) ) ;
2011-10-23 17:11:06 +00:00
}
2011-07-25 14:56:40 +00:00
return true ;
}
2012-06-07 14:38:21 +00:00
2016-01-25 13:30:33 +01:00
# if OCC_VERSION_HEX < 0x60900
2014-12-19 12:14:36 +00:00
bool IfcGeom : : Kernel : : convert_openings_fast ( const IfcSchema : : IfcProduct * entity , const IfcSchema : : IfcRelVoidsElement : : list : : ptr & openings ,
const IfcGeom : : IfcRepresentationShapeItems & entity_shapes , const gp_Trsf & entity_trsf , IfcGeom : : IfcRepresentationShapeItems & cut_shapes ) {
2012-06-07 14:38:21 +00:00
// Create a compound of all opening shapes in order to speed up the boolean operations
TopoDS_Compound opening_compound ;
BRep_Builder builder ;
builder . MakeCompound ( opening_compound ) ;
2014-05-04 14:42:55 +00:00
for ( IfcSchema : : IfcRelVoidsElement : : list : : it it = openings - > begin ( ) ; it ! = openings - > end ( ) ; + + it ) {
IfcSchema : : IfcRelVoidsElement * v = * it ;
IfcSchema : : IfcFeatureElementSubtraction * fes = v - > RelatedOpeningElement ( ) ;
2017-12-18 15:25:09 +01:00
if ( fes - > declaration ( ) . is ( IfcSchema : : IfcOpeningElement : : Class ( ) ) ) {
2015-12-05 17:14:10 +01:00
if ( ! fes - > hasRepresentation ( ) ) continue ;
2012-06-07 14:38:21 +00:00
// Convert the IfcRepresentation of the IfcOpeningElement
gp_Trsf opening_trsf ;
2015-12-05 17:14:10 +01:00
if ( fes - > hasObjectPlacement ( ) ) {
try {
convert ( fes - > ObjectPlacement ( ) , opening_trsf ) ;
2017-08-01 16:45:11 +02:00
} catch ( const std : : exception & e ) {
Logger : : Error ( e ) ;
} catch ( . . . ) {
Logger : : Error ( " Failed to construct placement " ) ;
}
2015-12-05 17:14:10 +01:00
}
2012-06-07 14:38:21 +00:00
// Move the opening into the coordinate system of the IfcProduct
opening_trsf . PreMultiply ( entity_trsf . Inverted ( ) ) ;
2014-05-04 14:42:55 +00:00
IfcSchema : : IfcProductRepresentation * prodrep = fes - > Representation ( ) ;
IfcSchema : : IfcRepresentation : : list : : ptr reps = prodrep - > Representations ( ) ;
2012-06-07 14:38:21 +00:00
2013-05-05 08:49:25 +00:00
IfcGeom : : IfcRepresentationShapeItems opening_shapes ;
2012-06-07 14:38:21 +00:00
2014-05-04 14:42:55 +00:00
for ( IfcSchema : : IfcRepresentation : : list : : it it2 = reps - > begin ( ) ; it2 ! = reps - > end ( ) ; + + it2 ) {
2014-12-19 12:14:36 +00:00
convert_shapes ( * it2 , opening_shapes ) ;
2012-06-07 14:38:21 +00:00
}
for ( unsigned int i = 0 ; i < opening_shapes . size ( ) ; + + i ) {
2013-05-05 08:49:25 +00:00
gp_GTrsf gtrsf = opening_shapes [ i ] . Placement ( ) ;
2012-06-07 14:38:21 +00:00
gtrsf . PreMultiply ( opening_trsf ) ;
2016-05-03 11:01:47 +02:00
TopoDS_Shape opening_shape = apply_transformation ( opening_shapes [ i ] . Shape ( ) , gtrsf ) ;
builder . Add ( opening_compound , opening_shape ) ;
2012-06-07 14:38:21 +00:00
}
}
}
// Iterate over the shapes of the IfcProduct
2013-05-05 08:49:25 +00:00
for ( IfcGeom : : IfcRepresentationShapeItems : : const_iterator it3 = entity_shapes . begin ( ) ; it3 ! = entity_shapes . end ( ) ; + + it3 ) {
2012-06-07 14:38:21 +00:00
TopoDS_Shape entity_shape_solid ;
2014-12-19 12:14:36 +00:00
const TopoDS_Shape & entity_shape_unlocated = ensure_fit_for_subtraction ( it3 - > Shape ( ) , entity_shape_solid ) ;
2013-05-05 08:49:25 +00:00
const gp_GTrsf & entity_shape_gtrsf = it3 - > Placement ( ) ;
2016-05-03 11:01:47 +02:00
if ( entity_shape_gtrsf . Form ( ) = = gp_Other ) {
2017-12-11 15:07:40 +01:00
Logger : : Message ( Logger : : LOG_WARNING , " Applying non uniform transformation to: " , entity ) ;
2012-06-07 14:38:21 +00:00
}
2016-05-03 11:01:47 +02:00
TopoDS_Shape entity_shape = apply_transformation ( entity_shape_unlocated , entity_shape_gtrsf ) ;
2012-06-07 14:38:21 +00:00
BRepAlgoAPI_Cut brep_cut ( entity_shape , opening_compound ) ;
2016-01-25 13:30:33 +01:00
2012-06-07 14:38:21 +00:00
bool is_valid = false ;
if ( brep_cut . IsDone ( ) ) {
TopoDS_Shape brep_cut_result = brep_cut ;
BRepCheck_Analyzer analyser ( brep_cut_result ) ;
is_valid = analyser . IsValid ( ) ! = 0 ;
if ( is_valid ) {
2018-12-10 11:33:54 +01:00
cut_shapes . push_back ( IfcGeom : : IfcRepresentationShapeItem ( it3 - > ItemId ( ) , brep_cut_result , & it3 - > Style ( ) ) ) ;
2012-06-07 14:38:21 +00:00
}
}
if ( ! is_valid ) {
// Apparently processing the boolean operation failed or resulted in an invalid result
// in which case the original shape without the subtractions is returned instead
// we try convert the openings in the original way, one by one.
2017-12-11 15:07:40 +01:00
Logger : : Message ( Logger : : LOG_WARNING , " Subtracting combined openings compound failed: " , entity ) ;
2012-06-07 14:38:21 +00:00
return false ;
}
}
return true ;
}
2016-01-22 17:27:28 +01:00
# else
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 ;
}
} ;
}
bool IfcGeom : : Kernel : : convert_openings_fast ( const IfcSchema : : IfcProduct * entity , const IfcSchema : : IfcRelVoidsElement : : list : : ptr & openings ,
const IfcGeom : : IfcRepresentationShapeItems & entity_shapes , const gp_Trsf & entity_trsf , IfcGeom : : IfcRepresentationShapeItems & cut_shapes ) {
std : : vector < std : : pair < double , TopoDS_Shape > > opening_vector ;
for ( IfcSchema : : IfcRelVoidsElement : : list : : it it = openings - > begin ( ) ; it ! = openings - > end ( ) ; + + it ) {
2016-01-25 13:30:33 +01:00
IfcSchema : : IfcRelVoidsElement * v = * it ;
IfcSchema : : IfcFeatureElementSubtraction * fes = v - > RelatedOpeningElement ( ) ;
2018-11-28 14:18:58 +01:00
if ( fes - > declaration ( ) . is ( IfcSchema : : IfcOpeningElement : : Class ( ) ) ) {
2016-01-25 13:30:33 +01:00
if ( ! fes - > hasRepresentation ( ) ) continue ;
// Convert the IfcRepresentation of the IfcOpeningElement
gp_Trsf opening_trsf ;
if ( fes - > hasObjectPlacement ( ) ) {
try {
2018-11-23 11:45:23 +01:00
convert ( fes - > ObjectPlacement ( ) , opening_trsf ) ;
2017-08-01 16:45:11 +02:00
} catch ( const std : : exception & e ) {
Logger : : Error ( e ) ;
} catch ( . . . ) {
Logger : : Error ( " Failed to construct placement " ) ;
}
2016-01-25 13:30:33 +01:00
}
// Move the opening into the coordinate system of the IfcProduct
opening_trsf . PreMultiply ( entity_trsf . Inverted ( ) ) ;
IfcSchema : : IfcProductRepresentation * prodrep = fes - > Representation ( ) ;
IfcSchema : : IfcRepresentation : : list : : ptr reps = prodrep - > Representations ( ) ;
IfcGeom : : IfcRepresentationShapeItems opening_shapes ;
2018-11-23 11:45:23 +01:00
for ( IfcSchema : : IfcRepresentation : : list : : it it2 = reps - > begin ( ) ; it2 ! = reps - > end ( ) ; + + it2 ) {
convert_shapes ( * it2 , opening_shapes ) ;
2016-01-25 13:30:33 +01:00
}
2018-11-23 11:45:23 +01:00
for ( unsigned int i = 0 ; i < opening_shapes . size ( ) ; + + i ) {
2018-05-05 16:09:12 +02:00
TopoDS_Shape opening_shape_solid ;
const TopoDS_Shape & opening_shape_unlocated = ensure_fit_for_subtraction ( opening_shapes [ i ] . Shape ( ) , opening_shape_solid ) ;
2016-01-25 13:30:33 +01:00
gp_GTrsf gtrsf = opening_shapes [ i ] . Placement ( ) ;
gtrsf . PreMultiply ( opening_trsf ) ;
2018-05-05 16:09:12 +02:00
TopoDS_Shape opening_shape = apply_transformation ( opening_shape_unlocated , gtrsf ) ;
2018-11-23 11:45:23 +01:00
opening_vector . push_back ( std : : make_pair ( min_edge_length ( opening_shape ) , opening_shape ) ) ;
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
for ( IfcGeom : : IfcRepresentationShapeItems : : const_iterator it3 = entity_shapes . begin ( ) ; it3 ! = entity_shapes . end ( ) ; + + it3 ) {
2021-05-23 16:26:34 +02:00
bool is_manifold = Kernel : : is_manifold ( it3 - > Shape ( ) ) ;
if ( ! is_manifold ) {
Logger : : Warning ( " Non-manifold first operand " ) ;
2016-01-25 13:30:33 +01:00
}
2016-01-22 17:27:28 +01:00
2021-05-23 16:26:34 +02:00
for ( int as_shell = 0 ; as_shell < 2 ; + + as_shell ) {
2018-11-23 11:45:23 +01:00
2021-05-23 16:26:34 +02:00
TopoDS_Shape entity_shape_solid ;
TopoDS_Shape entity_shape_unlocated ;
if ( as_shell ) {
entity_shape_unlocated = it3 - > Shape ( ) ;
} else {
entity_shape_unlocated = ensure_fit_for_subtraction ( it3 - > Shape ( ) , entity_shape_solid ) ;
}
const gp_GTrsf & entity_shape_gtrsf = it3 - > Placement ( ) ;
if ( entity_shape_gtrsf . Form ( ) = = gp_Other ) {
Logger : : Message ( Logger : : LOG_WARNING , " Applying non uniform transformation to: " , entity ) ;
}
TopoDS_Shape entity_shape = apply_transformation ( entity_shape_unlocated , entity_shape_gtrsf ) ;
2018-11-23 11:45:23 +01:00
2021-05-23 16:26:34 +02:00
TopoDS_Shape result = entity_shape ;
2018-11-23 11:45:23 +01:00
2021-05-23 16:26:34 +02:00
auto it = opening_vector . begin ( ) ;
auto jt = it ;
for ( ; ; + + it ) {
if ( it = = opening_vector . end ( ) | | jt - > first / it - > first > 10. ) {
TopTools_ListOfShape opening_list ;
for ( auto kt = jt ; kt < it ; + + kt ) {
opening_list . Append ( kt - > second ) ;
}
TopoDS_Shape intermediate_result ;
if ( boolean_operation ( result , opening_list , BOPAlgo_CUT , intermediate_result ) ) {
result = intermediate_result ;
}
else {
Logger : : Message ( Logger : : LOG_ERROR , " Opening subtraction failed for " + boost : : lexical_cast < std : : string > ( std : : distance ( jt , it ) ) + " openings " , entity ) ;
}
jt = it ;
2018-11-23 11:45:23 +01:00
}
2021-05-23 16:26:34 +02:00
if ( it = = opening_vector . end ( ) ) {
break ;
}
2018-11-23 11:45:23 +01:00
}
2021-05-23 16:26:34 +02:00
int result_n_faces = count ( result , TopAbs_FACE ) ;
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 ) {
Logger : : Warning ( " Retrying boolean operation on individual faces " ) ;
}
continue ;
2018-11-23 11:45:23 +01:00
}
2021-05-23 16:26:34 +02:00
cut_shapes . push_back ( IfcGeom : : IfcRepresentationShapeItem ( it3 - > ItemId ( ) , result , & it3 - > Style ( ) ) ) ;
// For manifold first operands we're not even going to try if processing
// as loose faces gives a better result.
break ;
}
2012-06-07 14:38:21 +00:00
}
return true ;
}
2016-01-25 13:30:33 +01:00
# endif
2012-06-07 14:38:21 +00:00
2018-04-18 11:33:57 +02:00
bool IfcGeom : : Kernel : : convert_wire_to_face ( const TopoDS_Wire & w , TopoDS_Face & face ) {
TopoDS_Wire wire = w ;
TopTools_ListOfShape results ;
if ( wire_intersections ( wire , results ) ) {
2018-12-29 12:34:32 +01:00
Logger : : Warning ( " Self-intersections with " + boost : : lexical_cast < std : : string > ( results . Extent ( ) ) + " cycles detected " ) ;
2018-04-18 11:33:57 +02:00
select_largest ( results , wire ) ;
}
2018-08-20 11:46:26 +02:00
bool is_2d = true ;
TopExp_Explorer exp ( wire , TopAbs_EDGE ) ;
for ( ; exp . More ( ) ; exp . Next ( ) ) {
double a , b ;
Handle ( Geom_Curve ) crv = BRep_Tool : : Curve ( TopoDS : : Edge ( exp . Current ( ) ) , a , b ) ;
if ( crv - > DynamicType ( ) ! = STANDARD_TYPE ( Geom_Line ) ) {
is_2d = false ;
break ;
}
Handle ( Geom_Line ) line = Handle ( Geom_Line ) : : DownCast ( crv ) ;
if ( line - > Lin ( ) . Direction ( ) . Z ( ) > ALMOST_ZERO ) {
is_2d = false ;
break ;
}
}
if ( ! is_2d ) {
// For 2d wires (e.g. profiles) a higher tolerance for plane fitting is never required.
ShapeFix_ShapeTolerance FTol ;
FTol . SetTolerance ( wire , getValue ( GV_PRECISION ) , TopAbs_WIRE ) ;
}
2017-11-08 13:27:45 +01:00
2011-07-25 14:56:40 +00:00
BRepBuilderAPI_MakeFace mf ( wire , false ) ;
BRepBuilderAPI_FaceError er = mf . Error ( ) ;
2017-11-08 13:27:45 +01:00
if ( er ! = BRepBuilderAPI_FaceDone ) {
Logger : : Error ( " Failed to create face. " ) ;
return false ;
2011-07-25 14:56:40 +00:00
}
face = mf . Face ( ) ;
2017-11-08 13:27:45 +01:00
2011-07-25 14:56:40 +00:00
return true ;
}
2014-03-15 11:51:23 +00:00
2020-08-25 13:51:59 +02:00
bool IfcGeom : : Kernel : : convert_wire_to_faces ( const TopoDS_Wire & w , TopoDS_Compound & faces ) {
bool is_2d = true ;
TopExp_Explorer exp ( w , TopAbs_EDGE ) ;
for ( ; exp . More ( ) ; exp . Next ( ) ) {
double a , b ;
Handle ( Geom_Curve ) crv = BRep_Tool : : Curve ( TopoDS : : Edge ( exp . Current ( ) ) , a , b ) ;
if ( crv - > DynamicType ( ) ! = STANDARD_TYPE ( Geom_Line ) ) {
is_2d = false ;
break ;
}
Handle ( Geom_Line ) line = Handle ( Geom_Line ) : : DownCast ( crv ) ;
if ( line - > Lin ( ) . Direction ( ) . Z ( ) > ALMOST_ZERO ) {
is_2d = false ;
break ;
}
}
TopTools_ListOfShape results ;
2021-02-06 09:38:38 -06:00
if ( wire_intersections ( w , results ) ) {
2021-02-05 14:00:23 -06:00
Logger : : Warning ( " Self-intersections with " + boost : : lexical_cast < std : : string > ( results . Extent ( ) ) + " cycles detected " ) ;
} else {
results . Clear ( ) ;
results . Append ( w ) ;
}
2020-08-25 13:51:59 +02:00
TopoDS_Compound C ;
BRep_Builder B ;
B . MakeCompound ( faces ) ;
std : : list < std : : pair < double , TopoDS_Face > > face_list ;
double max_area = 0. ;
TopTools_ListIteratorOfListOfShape it ( results ) ;
for ( ; it . More ( ) ; it . Next ( ) ) {
const TopoDS_Wire & wire = TopoDS : : Wire ( it . Value ( ) ) ;
if ( ! is_2d ) {
// For 2d wires (e.g. profiles) a higher tolerance for plane fitting is never required.
ShapeFix_ShapeTolerance FTol ;
FTol . SetTolerance ( wire , getValue ( GV_PRECISION ) , TopAbs_WIRE ) ;
}
BRepBuilderAPI_MakeFace mf ( wire , false ) ;
BRepBuilderAPI_FaceError er = mf . Error ( ) ;
if ( er ! = BRepBuilderAPI_FaceDone ) {
Logger : : Error ( " Failed to create face. " ) ;
continue ;
}
TopoDS_Face face = mf . Face ( ) ;
const double m = face_area ( face ) ;
face_list . push_back ( { m , face } ) ;
if ( m > max_area ) {
max_area = m ;
}
}
for ( auto & p : face_list ) {
if ( p . first > = max_area / 10. ) {
B . Add ( faces , p . second ) ;
} else {
Logger : : Warning ( " Ignoring self-intersection loop with area " + boost : : lexical_cast < std : : string > ( p . first ) ) ;
}
}
return true ;
}
2019-01-16 12:28:58 +01:00
void IfcGeom : : Kernel : : assert_closed_wire ( TopoDS_Wire & wire ) {
if ( wire . Closed ( ) = = 0 ) {
TopoDS_Vertex v0 , v1 ;
TopExp : : Vertices ( wire , v0 , v1 ) ;
gp_Pnt p1 = BRep_Tool : : Pnt ( v0 ) ;
gp_Pnt p2 = BRep_Tool : : Pnt ( v1 ) ;
if ( p1 . Distance ( p2 ) > getValue ( GV_PRECISION ) ) {
BRepBuilderAPI_MakeWire mw ;
mw . Add ( wire ) ;
mw . Add ( BRepBuilderAPI_MakeEdge ( v0 , v1 ) . Edge ( ) ) ;
wire = mw . Wire ( ) ;
}
Logger : : Warning ( " Wire not closed: " ) ;
}
}
2014-12-19 12:14:36 +00:00
bool IfcGeom : : Kernel : : convert_curve_to_wire ( const Handle ( Geom_Curve ) & curve , TopoDS_Wire & wire ) {
2015-04-22 09:15:48 +00:00
try {
wire = BRepBuilderAPI_MakeWire ( BRepBuilderAPI_MakeEdge ( curve ) ) ;
2017-08-01 16:45:11 +02:00
return true ;
} catch ( const Standard_Failure & e ) {
if ( e . GetMessageString ( ) & & strlen ( e . GetMessageString ( ) ) ) {
Logger : : Error ( e . GetMessageString ( ) ) ;
} else {
2018-09-06 12:23:07 -04:00
Logger : : Error ( " Unknown error converting curve to wire " ) ;
2017-08-01 16:45:11 +02:00
}
} catch ( . . . ) {
2018-09-06 12:23:07 -04:00
Logger : : Error ( " Unknown error converting curve to wire " ) ;
2017-08-01 16:45:11 +02:00
}
return false ;
2014-03-15 11:51:23 +00:00
}
2014-12-19 12:14:36 +00:00
bool IfcGeom : : Kernel : : profile_helper ( int numVerts , double * verts , int numFillets , int * filletIndices , double * filletRadii , gp_Trsf2d trsf , TopoDS_Shape & face_shape ) {
2011-07-25 14:56:40 +00:00
TopoDS_Vertex * vertices = new TopoDS_Vertex [ numVerts ] ;
for ( int i = 0 ; i < numVerts ; i + + ) {
gp_XY xy ( verts [ 2 * i ] , verts [ 2 * i + 1 ] ) ;
trsf . Transforms ( xy ) ;
vertices [ i ] = BRepBuilderAPI_MakeVertex ( gp_Pnt ( xy . X ( ) , xy . Y ( ) , 0.0f ) ) ;
}
BRepBuilderAPI_MakeWire w ;
for ( int i = 0 ; i < numVerts ; i + + )
w . Add ( BRepBuilderAPI_MakeEdge ( vertices [ i ] , vertices [ ( i + 1 ) % numVerts ] ) ) ;
2014-03-16 13:34:01 +00:00
TopoDS_Face face ;
2014-12-19 12:14:36 +00:00
convert_wire_to_face ( w . Wire ( ) , face ) ;
2011-07-25 14:56:40 +00:00
2014-07-24 11:14:54 +00:00
if ( numFillets & & * std : : max_element ( filletRadii , filletRadii + numFillets ) > ALMOST_ZERO ) {
2011-07-25 14:56:40 +00:00
BRepFilletAPI_MakeFillet2d fillet ( face ) ;
for ( int i = 0 ; i < numFillets ; i + + ) {
2012-03-13 11:56:52 +00:00
const double radius = filletRadii [ i ] ;
2014-07-24 11:14:54 +00:00
if ( radius < = ALMOST_ZERO ) continue ;
2011-07-25 14:56:40 +00:00
fillet . AddFillet ( vertices [ filletIndices [ i ] ] , radius ) ;
}
fillet . Build ( ) ;
2014-07-24 11:14:54 +00:00
if ( fillet . IsDone ( ) ) {
face = TopoDS : : Face ( fillet . Shape ( ) ) ;
} else {
2018-12-29 12:34:32 +01:00
Logger : : Error ( " Failed to process profile fillets " ) ;
2014-07-24 11:14:54 +00:00
}
2011-07-25 14:56:40 +00:00
}
2014-03-16 13:34:01 +00:00
face_shape = face ;
2011-07-25 14:56:40 +00:00
delete [ ] vertices ;
return true ;
2011-10-12 16:43:29 +00:00
}
2014-12-19 12:14:36 +00:00
double IfcGeom : : Kernel : : shape_volume ( const TopoDS_Shape & s ) {
2012-04-07 14:34:44 +00:00
GProp_GProps prop ;
BRepGProp : : VolumeProperties ( s , prop ) ;
return prop . Mass ( ) ;
}
2014-12-19 12:14:36 +00:00
double IfcGeom : : Kernel : : face_area ( const TopoDS_Face & f ) {
2012-04-07 14:34:44 +00:00
GProp_GProps prop ;
BRepGProp : : SurfaceProperties ( f , prop ) ;
return prop . Mass ( ) ;
2012-03-13 11:56:52 +00:00
}
2014-12-19 12:14:36 +00:00
bool IfcGeom : : Kernel : : is_convex ( const TopoDS_Wire & wire ) {
2012-03-13 11:56:52 +00:00
for ( TopExp_Explorer exp1 ( wire , TopAbs_VERTEX ) ; exp1 . More ( ) ; exp1 . Next ( ) ) {
TopoDS_Vertex V1 = TopoDS : : Vertex ( exp1 . Current ( ) ) ;
gp_Pnt P1 = BRep_Tool : : Pnt ( V1 ) ;
// Store the neighboring points
std : : vector < gp_Pnt > neighbors ;
for ( TopExp_Explorer exp3 ( wire , TopAbs_EDGE ) ; exp3 . More ( ) ; exp3 . Next ( ) ) {
TopoDS_Edge edge = TopoDS : : Edge ( exp3 . Current ( ) ) ;
std : : vector < gp_Pnt > edge_points ;
for ( TopExp_Explorer exp2 ( edge , TopAbs_VERTEX ) ; exp2 . More ( ) ; exp2 . Next ( ) ) {
TopoDS_Vertex V2 = TopoDS : : Vertex ( exp2 . Current ( ) ) ;
gp_Pnt P2 = BRep_Tool : : Pnt ( V2 ) ;
edge_points . push_back ( P2 ) ;
}
if ( edge_points . size ( ) ! = 2 ) continue ;
2014-12-19 12:14:36 +00:00
if ( edge_points [ 0 ] . IsEqual ( P1 , getValue ( GV_POINT_EQUALITY_TOLERANCE ) ) ) neighbors . push_back ( edge_points [ 1 ] ) ;
else if ( edge_points [ 1 ] . IsEqual ( P1 , getValue ( GV_POINT_EQUALITY_TOLERANCE ) ) ) neighbors . push_back ( edge_points [ 0 ] ) ;
2012-03-13 11:56:52 +00:00
}
// There should be two of these
if ( neighbors . size ( ) ! = 2 ) return false ;
// Now find the non neighboring points
std : : vector < gp_Pnt > non_neighbors ;
for ( TopExp_Explorer exp2 ( wire , TopAbs_VERTEX ) ; exp2 . More ( ) ; exp2 . Next ( ) ) {
TopoDS_Vertex V2 = TopoDS : : Vertex ( exp2 . Current ( ) ) ;
gp_Pnt P2 = BRep_Tool : : Pnt ( V2 ) ;
2014-12-19 12:14:36 +00:00
if ( P1 . IsEqual ( P2 , getValue ( GV_POINT_EQUALITY_TOLERANCE ) ) ) continue ;
2012-03-13 11:56:52 +00:00
bool found = false ;
for ( std : : vector < gp_Pnt > : : const_iterator it = neighbors . begin ( ) ; it ! = neighbors . end ( ) ; + + it ) {
2014-12-19 12:14:36 +00:00
if ( ( * it ) . IsEqual ( P2 , getValue ( GV_POINT_EQUALITY_TOLERANCE ) ) ) { found = true ; break ; }
2012-03-13 11:56:52 +00:00
}
if ( ! found ) non_neighbors . push_back ( P2 ) ;
}
// Calculate the angle between the two edges of the vertex
gp_Dir dir1 ( neighbors [ 0 ] . XYZ ( ) - P1 . XYZ ( ) ) ;
gp_Dir dir2 ( neighbors [ 1 ] . XYZ ( ) - P1 . XYZ ( ) ) ;
const double angle = acos ( dir1 . Dot ( dir2 ) ) + 0.0001 ;
// Now for the non-neighbors see whether a greater angle can be found with one of the edges
for ( std : : vector < gp_Pnt > : : const_iterator it = non_neighbors . begin ( ) ; it ! = non_neighbors . end ( ) ; + + it ) {
gp_Dir dir3 ( ( * it ) . XYZ ( ) - P1 . XYZ ( ) ) ;
const double angle2 = acos ( dir3 . Dot ( dir1 ) ) ;
const double angle3 = acos ( dir3 . Dot ( dir2 ) ) ;
if ( angle2 > angle | | angle3 > angle ) return false ;
}
}
return true ;
}
2014-12-19 12:14:36 +00:00
TopoDS_Shape IfcGeom : : Kernel : : halfspace_from_plane ( const gp_Pln & pln , const gp_Pnt & cent ) {
2012-03-13 11:56:52 +00:00
TopoDS_Face face = BRepBuilderAPI_MakeFace ( pln ) . Face ( ) ;
return BRepPrimAPI_MakeHalfSpace ( face , cent ) . Solid ( ) ;
}
2014-12-19 12:14:36 +00:00
gp_Pln IfcGeom : : Kernel : : plane_from_face ( const TopoDS_Face & face ) {
2012-03-13 11:56:52 +00:00
BRepGProp_Face prop ( face ) ;
Standard_Real u1 , u2 , v1 , v2 ;
prop . Bounds ( u1 , u2 , v1 , v2 ) ;
Standard_Real u = ( u1 + u2 ) / 2.0 ;
Standard_Real v = ( v1 + v2 ) / 2.0 ;
gp_Pnt p ;
gp_Vec n ;
prop . Normal ( u , v , p , n ) ;
return gp_Pln ( p , n ) ;
}
2014-12-19 12:14:36 +00:00
gp_Pnt IfcGeom : : Kernel : : point_above_plane ( const gp_Pln & pln , bool agree ) {
2012-03-13 11:56:52 +00:00
if ( agree ) {
return pln . Location ( ) . Translated ( pln . Axis ( ) . Direction ( ) ) ;
} else {
return pln . Location ( ) . Translated ( - pln . Axis ( ) . Direction ( ) ) ;
}
2012-06-25 15:08:58 +00:00
}
2014-12-19 12:14:36 +00:00
void IfcGeom : : Kernel : : apply_tolerance ( TopoDS_Shape & s , double t ) {
2017-08-09 17:00:41 +02:00
/*
// This does not result in actionable error messages and has been disabled.
2017-04-03 14:55:34 +02:00
ShapeAnalysis_ShapeTolerance toler;
2017-06-05 13:08:07 +02:00
if (Logger::LOG_WARNING >= Logger::Verbosity()) {
2017-08-09 17:00:41 +02:00
if (toler.Tolerance(s, 0) > t * 10.) {
Handle_TopTools_HSequenceOfShape shapes = toler.OverTolerance(s, t * 10.);
2017-06-05 13:08:07 +02:00
for (int i = 1; i <= shapes->Length(); ++i) {
const TopoDS_Shape& sub = shapes->Value(i);
std::stringstream ss;
TopAbs::Print(sub.ShapeType(), ss);
Logger::Warning("Tolerance of " + boost::lexical_cast<std::string>(toler.Tolerance(sub, 0)) + " on " + ss.str());
}
2017-04-03 14:55:34 +02:00
}
}
2017-08-09 17:00:41 +02:00
*/
2017-11-13 12:41:37 +01:00
# if OCC_VERSION_HEX < 0x60900
// This tolerance hack is not required as the boolean ops use a fuzziness value
2013-11-24 15:44:27 +00:00
ShapeFix_ShapeTolerance tol ;
2017-04-03 14:55:34 +02:00
tol . LimitTolerance ( s , t ) ;
2017-11-13 12:41:37 +01:00
# else
( void ) s ;
( void ) t ;
# endif
2013-11-24 15:44:27 +00:00
}
2014-12-19 12:14:36 +00:00
void IfcGeom : : Kernel : : setValue ( GeomValue var , double value ) {
2012-06-25 15:08:58 +00:00
switch ( var ) {
case GV_DEFLECTION_TOLERANCE :
deflection_tolerance = value ;
2012-06-27 15:59:09 +00:00
break ;
2012-08-11 13:24:08 +00:00
case GV_LENGTH_UNIT :
ifc_length_unit = value ;
break ;
case GV_PLANEANGLE_UNIT :
ifc_planeangle_unit = value ;
break ;
2013-11-24 15:44:27 +00:00
case GV_PRECISION :
modelling_precision = value ;
break ;
2015-04-22 09:15:48 +00:00
case GV_DIMENSIONALITY :
dimensionality = value ;
break ;
2019-05-24 14:29:21 +02:00
case GV_MAX_FACES_TO_ORIENT :
max_faces_to_orient = value ;
break ;
2020-06-07 19:46:10 +02:00
case GV_LAYERSET_FIRST :
layerset_first = value ;
break ;
2020-11-24 16:58:05 +01:00
case GV_DISABLE_BOOLEAN_RESULT :
disable_boolean_result = value ;
break ;
2021-02-05 14:00:23 -06:00
case GV_NO_WIRE_INTERSECTION_CHECK :
no_wire_intersection_check = value ;
break ;
2021-02-06 10:26:17 -06:00
case GV_PRECISION_FACTOR :
precision_factor = value ;
break ;
2021-02-08 14:06:05 -06:00
case GV_NO_WIRE_INTERSECTION_TOLERANCE :
no_wire_intersection_tolerance = value ;
break ;
2012-06-27 15:59:09 +00:00
default :
2019-05-24 14:29:21 +02:00
throw std : : runtime_error ( " Invalid setting " ) ;
2012-06-25 15:08:58 +00:00
}
}
2015-10-07 16:13:25 +02:00
double IfcGeom : : Kernel : : getValue ( GeomValue var ) const {
2012-06-25 15:08:58 +00:00
switch ( var ) {
case GV_DEFLECTION_TOLERANCE :
return deflection_tolerance ;
case GV_MINIMAL_FACE_AREA :
2016-05-24 12:50:05 +02:00
// Considering a right-angled triangle, this about the smallest
// area you can obtain without the vertices being confused.
2019-05-24 14:29:21 +02:00
return modelling_precision * modelling_precision / 20. ;
2012-06-25 15:08:58 +00:00
case GV_POINT_EQUALITY_TOLERANCE :
2019-05-24 14:29:21 +02:00
return modelling_precision ;
2012-08-11 13:24:08 +00:00
case GV_LENGTH_UNIT :
return ifc_length_unit ;
case GV_PLANEANGLE_UNIT :
return ifc_planeangle_unit ;
2013-11-24 15:44:27 +00:00
case GV_PRECISION :
return modelling_precision ;
2015-04-22 09:15:48 +00:00
case GV_DIMENSIONALITY :
return dimensionality ;
2019-05-24 14:29:21 +02:00
case GV_MAX_FACES_TO_ORIENT :
return max_faces_to_orient ;
2020-06-07 19:46:10 +02:00
case GV_LAYERSET_FIRST :
return layerset_first ;
2020-11-24 16:58:05 +01:00
case GV_DISABLE_BOOLEAN_RESULT :
return disable_boolean_result ;
2021-02-05 14:00:23 -06:00
case GV_NO_WIRE_INTERSECTION_CHECK :
return no_wire_intersection_check ;
2021-02-06 10:26:17 -06:00
case GV_PRECISION_FACTOR :
return precision_factor ;
2021-02-08 14:06:05 -06:00
case GV_NO_WIRE_INTERSECTION_TOLERANCE :
return no_wire_intersection_tolerance ;
2012-06-25 15:08:58 +00:00
}
2019-05-24 14:29:21 +02:00
throw std : : runtime_error ( " Invalid setting " ) ;
2012-11-04 16:26:33 +00:00
}
2017-12-20 16:38:55 +01:00
namespace {
// Returns the vertex part of an TopoDS_Edge edge that is not TopoDS_Vertex vertex
TopoDS_Vertex find_other ( const TopoDS_Edge & edge , const TopoDS_Vertex & vertex ) {
TopExp_Explorer exp ( edge , TopAbs_VERTEX ) ;
while ( exp . More ( ) ) {
if ( ! exp . Current ( ) . IsSame ( vertex ) ) {
return TopoDS : : Vertex ( exp . Current ( ) ) ;
}
exp . Next ( ) ;
Add support for IfcBooleanResult, IfcBlock, IfcRectangularPyramid, IfcRightCircularCylinder, IfcRightCircularCone, IfcSphere, IfcCsgSolid, IfcCurveBoundedPlane, IfcRectangularTrimmedSurface, IfcSurfaceCurveSweptAreaSolid, IfcCylindricalSurface.
2014-03-22 10:31:51 +00:00
}
2017-12-20 16:38:55 +01:00
return TopoDS_Vertex ( ) ;
Add support for IfcBooleanResult, IfcBlock, IfcRectangularPyramid, IfcRightCircularCylinder, IfcRightCircularCone, IfcSphere, IfcCsgSolid, IfcCurveBoundedPlane, IfcRectangularTrimmedSurface, IfcSurfaceCurveSweptAreaSolid, IfcCylindricalSurface.
2014-03-22 10:31:51 +00:00
}
2017-12-20 16:38:55 +01:00
TopoDS_Edge find_next ( const TopTools_IndexedMapOfShape & edge_set , const TopTools_IndexedDataMapOfShapeListOfShape & vertex_to_edges , const TopoDS_Vertex & current , const TopoDS_Edge & previous_edge ) {
const TopTools_ListOfShape & edges = vertex_to_edges . FindFromKey ( current ) ;
TopTools_ListIteratorOfListOfShape eit ;
for ( eit . Initialize ( edges ) ; eit . More ( ) ; eit . Next ( ) ) {
const TopoDS_Edge & edge = TopoDS : : Edge ( eit . Value ( ) ) ;
if ( edge . IsSame ( previous_edge ) ) continue ;
if ( edge_set . Contains ( edge ) ) {
return edge ;
}
Add support for IfcBooleanResult, IfcBlock, IfcRectangularPyramid, IfcRightCircularCylinder, IfcRightCircularCone, IfcSphere, IfcCsgSolid, IfcCurveBoundedPlane, IfcRectangularTrimmedSurface, IfcSurfaceCurveSweptAreaSolid, IfcCylindricalSurface.
2014-03-22 10:31:51 +00:00
}
2017-12-20 16:38:55 +01:00
return TopoDS_Edge ( ) ;
Add support for IfcBooleanResult, IfcBlock, IfcRectangularPyramid, IfcRightCircularCylinder, IfcRightCircularCone, IfcSphere, IfcCsgSolid, IfcCurveBoundedPlane, IfcRectangularTrimmedSurface, IfcSurfaceCurveSweptAreaSolid, IfcCylindricalSurface.
2014-03-22 10:31:51 +00:00
}
2017-12-20 16:38:55 +01:00
Add support for IfcBooleanResult, IfcBlock, IfcRectangularPyramid, IfcRightCircularCylinder, IfcRightCircularCone, IfcSphere, IfcCsgSolid, IfcCurveBoundedPlane, IfcRectangularTrimmedSurface, IfcSurfaceCurveSweptAreaSolid, IfcCylindricalSurface.
2014-03-22 10:31:51 +00:00
}
2014-12-19 12:14:36 +00:00
bool IfcGeom : : Kernel : : fill_nonmanifold_wires_with_planar_faces ( TopoDS_Shape & shape ) {
Add support for IfcBooleanResult, IfcBlock, IfcRectangularPyramid, IfcRightCircularCylinder, IfcRightCircularCone, IfcSphere, IfcCsgSolid, IfcCurveBoundedPlane, IfcRectangularTrimmedSurface, IfcSurfaceCurveSweptAreaSolid, IfcCylindricalSurface.
2014-03-22 10:31:51 +00:00
BRepOffsetAPI_Sewing sew ;
sew . Add ( shape ) ;
TopTools_IndexedDataMapOfShapeListOfShape edge_to_faces ;
TopTools_IndexedDataMapOfShapeListOfShape vertex_to_edges ;
std : : set < int > visited ;
TopTools_IndexedMapOfShape edge_set ;
TopExp : : MapShapesAndAncestors ( shape , TopAbs_EDGE , TopAbs_FACE , edge_to_faces ) ;
const int num_edges = edge_to_faces . Extent ( ) ;
for ( int i = 1 ; i < = num_edges ; + + i ) {
const TopTools_ListOfShape & faces = edge_to_faces . FindFromIndex ( i ) ;
const int count = faces . Extent ( ) ;
// Find only the non-manifold edges: Edges that are only part of a
// single face and therefore part of the wire(s) we want to fill.
if ( count = = 1 ) {
const TopoDS_Shape & edge = edge_to_faces . FindKey ( i ) ;
TopExp : : MapShapesAndAncestors ( edge , TopAbs_VERTEX , TopAbs_EDGE , vertex_to_edges ) ;
edge_set . Add ( edge ) ;
}
}
const int num_verts = vertex_to_edges . Extent ( ) ;
TopoDS_Vertex first , current ;
TopoDS_Edge previous_edge ;
// Now loop over all the vertices that are part of the wire(s) to be filled
for ( int i = 1 ; i < = num_verts ; + + i ) {
first = current = TopoDS : : Vertex ( vertex_to_edges . FindKey ( i ) ) ;
// We keep track of the vertices we already used
if ( visited . find ( vertex_to_edges . FindIndex ( current ) ) ! = visited . end ( ) ) {
continue ;
}
// Given these vertices, try to find closed loops and create new
// wires out of them.
BRepBuilderAPI_MakeWire w ;
2015-11-21 23:05:28 +02:00
for ( ; ; ) {
Add support for IfcBooleanResult, IfcBlock, IfcRectangularPyramid, IfcRightCircularCylinder, IfcRightCircularCone, IfcSphere, IfcCsgSolid, IfcCurveBoundedPlane, IfcRectangularTrimmedSurface, IfcSurfaceCurveSweptAreaSolid, IfcCylindricalSurface.
2014-03-22 10:31:51 +00:00
visited . insert ( vertex_to_edges . FindIndex ( current ) ) ;
// Find the edge that the current vertex is part of and points
// away from the previous vertex (null for the first vertex).
TopoDS_Edge edge = find_next ( edge_set , vertex_to_edges , current , previous_edge ) ;
if ( edge . IsNull ( ) ) {
return false ;
}
TopoDS_Vertex other = find_other ( edge , current ) ;
2014-03-31 14:00:02 +00:00
if ( other . IsNull ( ) ) {
// Dealing with a conical edge probably, for some reason
// this works better than adding the edge directly.
double u1 , u2 ;
Handle ( Geom_Curve ) crv = BRep_Tool : : Curve ( edge , u1 , u2 ) ;
w . Add ( BRepBuilderAPI_MakeEdge ( crv , u1 , u2 ) ) ;
break ;
} else {
w . Add ( edge ) ;
}
Add support for IfcBooleanResult, IfcBlock, IfcRectangularPyramid, IfcRightCircularCylinder, IfcRightCircularCone, IfcSphere, IfcCsgSolid, IfcCurveBoundedPlane, IfcRectangularTrimmedSurface, IfcSurfaceCurveSweptAreaSolid, IfcCylindricalSurface.
2014-03-22 10:31:51 +00:00
// See if the starting point of this loop has been reached. Note that
// additional wires after this one potentially will be created.
if ( other . IsSame ( first ) ) {
break ;
}
previous_edge = edge ;
current = other ;
}
sew . Add ( BRepBuilderAPI_MakeFace ( w ) ) ;
previous_edge . Nullify ( ) ;
}
sew . Perform ( ) ;
shape = sew . SewedShape ( ) ;
try {
ShapeFix_Solid solid ;
2014-12-19 12:14:36 +00:00
solid . LimitTolerance ( getValue ( GV_POINT_EQUALITY_TOLERANCE ) ) ;
Add support for IfcBooleanResult, IfcBlock, IfcRectangularPyramid, IfcRightCircularCylinder, IfcRightCircularCone, IfcSphere, IfcCsgSolid, IfcCurveBoundedPlane, IfcRectangularTrimmedSurface, IfcSurfaceCurveSweptAreaSolid, IfcCylindricalSurface.
2014-03-22 10:31:51 +00:00
shape = solid . SolidFromShell ( TopoDS : : Shell ( shape ) ) ;
2017-08-01 16:45:11 +02:00
} catch ( const Standard_Failure & e ) {
if ( e . GetMessageString ( ) & & strlen ( e . GetMessageString ( ) ) ) {
Logger : : Error ( e . GetMessageString ( ) ) ;
} else {
Logger : : Error ( " Unknown error creating solid " ) ;
}
} catch ( . . . ) {
Logger : : Error ( " Unknown error creating solid " ) ;
}
Add support for IfcBooleanResult, IfcBlock, IfcRectangularPyramid, IfcRightCircularCylinder, IfcRightCircularCone, IfcSphere, IfcCsgSolid, IfcCurveBoundedPlane, IfcRectangularTrimmedSurface, IfcSurfaceCurveSweptAreaSolid, IfcCylindricalSurface.
2014-03-22 10:31:51 +00:00
return true ;
2014-07-06 14:21:56 +00:00
}
2014-12-19 12:14:36 +00:00
bool IfcGeom : : Kernel : : flatten_shape_list ( const IfcGeom : : IfcRepresentationShapeItems & shapes , TopoDS_Shape & result , bool fuse ) {
2014-07-06 14:21:56 +00:00
TopoDS_Compound compound ;
BRep_Builder builder ;
builder . MakeCompound ( compound ) ;
result = TopoDS_Shape ( ) ;
for ( IfcGeom : : IfcRepresentationShapeItems : : const_iterator it = shapes . begin ( ) ; it ! = shapes . end ( ) ; + + it ) {
TopoDS_Shape merged ;
const TopoDS_Shape & s = it - > Shape ( ) ;
if ( fuse ) {
2014-12-19 12:14:36 +00:00
ensure_fit_for_subtraction ( s , merged ) ;
2014-07-06 14:21:56 +00:00
} else {
merged = s ;
}
const gp_GTrsf & trsf = it - > Placement ( ) ;
2016-05-03 11:01:47 +02:00
const TopoDS_Shape moved_shape = apply_transformation ( merged , trsf ) ;
2014-07-06 14:21:56 +00:00
if ( shapes . size ( ) = = 1 ) {
result = moved_shape ;
2015-02-06 17:14:05 +00:00
const double precision = getValue ( GV_PRECISION ) ;
apply_tolerance ( result , precision ) ;
2014-07-06 14:21:56 +00:00
return true ;
}
if ( fuse ) {
if ( result . IsNull ( ) ) {
result = moved_shape ;
} else {
BRepAlgoAPI_Fuse brep_fuse ( result , moved_shape ) ;
if ( brep_fuse . IsDone ( ) ) {
TopoDS_Shape fused = brep_fuse ;
ShapeFix_Shape fix ( result ) ;
fix . Perform ( ) ;
result = fix . Shape ( ) ;
bool is_valid = BRepCheck_Analyzer ( result ) . IsValid ( ) ! = 0 ;
if ( is_valid ) {
result = fused ;
}
}
}
} else {
builder . Add ( compound , moved_shape ) ;
}
}
2015-10-07 16:14:34 +02:00
if ( ! fuse ) {
result = compound ;
}
2015-02-06 17:14:05 +00:00
const bool success = ! result . IsNull ( ) ;
if ( success ) {
const double precision = getValue ( GV_PRECISION ) ;
apply_tolerance ( result , precision ) ;
}
return success ;
2014-07-06 14:21:56 +00:00
}
2014-07-28 20:03:53 +00:00
2016-01-22 17:27:28 +01:00
void IfcGeom : : Kernel : : remove_duplicate_points_from_loop ( TColgp_SequenceOfPnt & polygon , bool closed , double tol ) {
2016-12-10 14:11:01 +01:00
if ( tol < = 0. ) tol = getValue ( GV_PRECISION ) ;
2014-07-28 20:03:53 +00:00
tol * = tol ;
2015-11-21 23:05:28 +02:00
for ( ; ; ) {
2014-07-28 20:03:53 +00:00
bool removed = false ;
int n = polygon . Length ( ) - ( closed ? 0 : 1 ) ;
for ( int i = 1 ; i < = n ; + + i ) {
// wrap around to the first point in case of a closed loop
int j = ( i % polygon . Length ( ) ) + 1 ;
double dist = polygon . Value ( i ) . SquareDistance ( polygon . Value ( j ) ) ;
if ( dist < tol ) {
// do not remove the first or last point to
// maintain connectivity with other wires
if ( ( closed & & j = = 1 ) | | ( ! closed & & j = = n ) ) polygon . Remove ( i ) ;
else polygon . Remove ( j ) ;
removed = true ;
break ;
}
}
if ( ! removed ) break ;
}
2015-01-05 14:11:42 +00:00
}
2016-01-22 17:27:28 +01:00
void IfcGeom : : Kernel : : remove_collinear_points_from_loop ( TColgp_SequenceOfPnt & polygon , bool closed , double tol ) {
2016-12-10 14:11:01 +01:00
if ( tol < = 0. ) tol = getValue ( GV_PRECISION ) ;
2016-01-22 17:27:28 +01:00
const int start = closed ? 1 : 2 ;
const int end = polygon . Length ( ) - ( closed ? 0 : 1 ) ;
std : : vector < bool > to_remove ( polygon . Length ( ) , false ) ;
for ( int i = start ; i < = end ; + + i ) {
const gp_Pnt & a = polygon . Value ( ( ( i - 2 + polygon . Length ( ) ) % polygon . Length ( ) ) + 1 ) ;
const gp_Pnt & b = polygon . Value ( i ) ;
const gp_Pnt & c = polygon . Value ( ( i % polygon . Length ( ) ) + 1 ) ;
const gp_Vec d1 = c . XYZ ( ) - a . XYZ ( ) ;
const gp_Vec d2 = b . XYZ ( ) - a . XYZ ( ) ;
const double dt = d2 . Dot ( d1 ) / d1 . Dot ( d1 ) ;
const gp_Vec d3 = d1 . Scaled ( dt ) ;
const gp_Pnt b2 = a . XYZ ( ) + d3 . XYZ ( ) ;
if ( b . Distance ( b2 ) < tol ) {
to_remove [ i - 1 ] = true ;
}
}
2016-01-25 13:30:33 +01:00
for ( int i = ( int ) to_remove . size ( ) - 1 ; i > = 0 ; - - i ) {
2016-01-22 17:27:28 +01:00
if ( to_remove [ i ] ) {
polygon . Remove ( i + 1 ) ;
}
}
}
bool IfcGeom : : Kernel : : wire_to_sequence_of_point ( const TopoDS_Wire & w , TColgp_SequenceOfPnt & p ) {
TopExp_Explorer exp ( w , TopAbs_EDGE ) ;
for ( ; exp . More ( ) ; exp . Next ( ) ) {
double a , b ;
Handle_Geom_Curve crv = BRep_Tool : : Curve ( TopoDS : : Edge ( exp . Current ( ) ) , a , b ) ;
if ( crv - > DynamicType ( ) ! = STANDARD_TYPE ( Geom_Line ) ) {
return false ;
}
}
exp . ReInit ( ) ;
int i = 0 ;
for ( ; exp . More ( ) ; exp . Next ( ) , + + i ) {
TopoDS_Vertex v1 , v2 ;
TopExp : : Vertices ( TopoDS : : Edge ( exp . Current ( ) ) , v1 , v2 , true ) ;
if ( exp . More ( ) ) {
if ( i = = 0 ) {
p . Append ( BRep_Tool : : Pnt ( v1 ) ) ;
}
p . Append ( BRep_Tool : : Pnt ( v2 ) ) ;
}
}
return true ;
}
void IfcGeom : : Kernel : : sequence_of_point_to_wire ( const TColgp_SequenceOfPnt & p , TopoDS_Wire & w , bool close ) {
BRepBuilderAPI_MakePolygon builder ;
for ( int i = 1 ; i < = p . Length ( ) ; + + i ) {
builder . Add ( p . Value ( i ) ) ;
}
if ( close ) {
builder . Close ( ) ;
}
w = builder . Wire ( ) ;
}
2016-03-18 11:23:25 +01:00
IfcSchema : : IfcRelVoidsElement : : list : : ptr IfcGeom : : Kernel : : find_openings ( IfcSchema : : IfcProduct * product ) {
2020-07-13 13:19:28 +02:00
std : : vector < IfcSchema : : IfcRelVoidsElement * > rs ;
2017-12-18 15:25:09 +01:00
if ( product - > declaration ( ) . is ( IfcSchema : : IfcElement : : Class ( ) ) & & ! product - > declaration ( ) . is ( IfcSchema : : IfcOpeningElement : : Class ( ) ) ) {
2016-03-18 11:23:25 +01:00
IfcSchema : : IfcElement * element = ( IfcSchema : : IfcElement * ) product ;
2020-07-13 13:19:28 +02:00
auto rels = element - > HasOpenings ( ) ;
rs . insert ( rs . end ( ) , rels - > begin ( ) , rels - > end ( ) ) ;
2016-03-18 11:23:25 +01:00
}
// Is the IfcElement a decomposition of an IfcElement with any IfcOpeningElements?
IfcSchema : : IfcObjectDefinition * obdef = product - > as < IfcSchema : : IfcObjectDefinition > ( ) ;
for ( ; ; ) {
2019-06-26 13:50:46 +02:00
auto decomposes = obdef - > Decomposes ( ) ;
2016-05-18 17:15:44 +02:00
if ( decomposes - > size ( ) ! = 1 ) break ;
2016-03-18 11:23:25 +01:00
IfcSchema : : IfcObjectDefinition * rel_obdef = ( * decomposes - > begin ( ) ) - > RelatingObject ( ) ;
2017-12-18 15:25:09 +01:00
if ( rel_obdef - > declaration ( ) . is ( IfcSchema : : IfcElement : : Class ( ) ) & & ! rel_obdef - > declaration ( ) . is ( IfcSchema : : IfcOpeningElement : : Class ( ) ) ) {
2016-03-18 11:23:25 +01:00
IfcSchema : : IfcElement * element = ( IfcSchema : : IfcElement * ) rel_obdef ;
2020-07-13 13:19:28 +02:00
auto rels = element - > HasOpenings ( ) ;
rs . insert ( rs . end ( ) , rels - > begin ( ) , rels - > end ( ) ) ;
2016-03-18 11:23:25 +01:00
}
obdef = rel_obdef ;
}
2020-07-13 13:19:28 +02:00
// Filter openings in Reference view, solely marked as Reference.
IfcSchema : : IfcRelVoidsElement : : list : : ptr openings ( new IfcSchema : : IfcRelVoidsElement : : list ) ;
std : : for_each ( rs . begin ( ) , rs . end ( ) , [ & openings ] ( IfcSchema : : IfcRelVoidsElement * rel ) {
2020-11-05 13:37:21 +01:00
if ( rel - > RelatedOpeningElement ( ) - > hasObjectPlacement ( ) & & rel - > RelatedOpeningElement ( ) - > hasRepresentation ( ) ) {
auto reps = rel - > RelatedOpeningElement ( ) - > Representation ( ) - > Representations ( ) ;
if ( ! ( reps - > size ( ) = = 1 & & ( * reps - > begin ( ) ) - > RepresentationIdentifier ( ) = = " Reference " ) ) {
openings - > push ( rel ) ;
}
2020-07-13 13:19:28 +02:00
}
} ) ;
2016-03-18 11:23:25 +01:00
return openings ;
}
2017-10-06 15:36:51 +02:00
const IfcSchema : : IfcMaterial * IfcGeom : : Kernel : : get_single_material_association ( const IfcSchema : : IfcProduct * product ) {
IfcSchema : : IfcMaterial * single_material = 0 ;
IfcSchema : : IfcRelAssociatesMaterial : : list : : ptr associated_materials = product - > HasAssociations ( ) - > as < IfcSchema : : IfcRelAssociatesMaterial > ( ) ;
if ( associated_materials - > size ( ) = = 1 ) {
IfcSchema : : IfcMaterialSelect * associated_material = ( * associated_materials - > begin ( ) ) - > RelatingMaterial ( ) ;
single_material = associated_material - > as < IfcSchema : : IfcMaterial > ( ) ;
2018-12-19 16:54:02 +01:00
2020-02-26 10:42:23 +02:00
// NB: IfcMaterialLayerSets are also considered, regardless of --enable-layerset-slicing. Picking
// the first material (in accordance with other viewers) when layerset-slicing is disabled.
2017-10-06 15:36:51 +02:00
if ( ! single_material & & associated_material - > as < IfcSchema : : IfcMaterialLayerSetUsage > ( ) ) {
IfcSchema : : IfcMaterialLayerSet * layerset = associated_material - > as < IfcSchema : : IfcMaterialLayerSetUsage > ( ) - > ForLayerSet ( ) ;
2020-06-07 19:46:10 +02:00
if ( getValue ( GV_LAYERSET_FIRST ) > 0.0 ? layerset - > MaterialLayers ( ) - > size ( ) > = 1 : layerset - > MaterialLayers ( ) - > size ( ) = = 1 ) {
2017-10-06 15:36:51 +02:00
IfcSchema : : IfcMaterialLayer * layer = ( * layerset - > MaterialLayers ( ) - > begin ( ) ) ;
if ( layer - > hasMaterial ( ) ) {
single_material = layer - > Material ( ) ;
}
}
}
}
return single_material ;
}
2018-07-07 21:14:27 +02:00
template < typename P , typename PP >
IfcGeom : : BRepElement < P , PP > * IfcGeom : : Kernel : : create_brep_for_representation_and_product (
2016-07-11 21:36:12 +03:00
const IteratorSettings & settings , IfcSchema : : IfcRepresentation * representation , IfcSchema : : IfcProduct * product )
{
2017-06-28 15:24:49 +02:00
std : : stringstream representation_id_builder ;
2017-12-11 15:07:40 +01:00
representation_id_builder < < representation - > data ( ) . id ( ) ;
2017-06-28 15:24:49 +02:00
2015-01-05 14:11:42 +00:00
IfcGeom : : Representation : : BRep * shape ;
2015-10-07 17:33:30 +02:00
IfcGeom : : IfcRepresentationShapeItems shapes , shapes2 ;
2015-01-05 14:11:42 +00:00
2015-10-07 17:33:30 +02:00
if ( ! convert_shapes ( representation , shapes ) ) {
2015-01-05 14:11:42 +00:00
return 0 ;
}
2016-05-02 15:13:30 +02:00
if ( settings . get ( IteratorSettings : : APPLY_LAYERSETS ) ) {
2015-10-07 17:33:30 +02:00
TopoDS_Shape merge ;
if ( flatten_shape_list ( shapes , merge , false ) ) {
if ( count ( merge , TopAbs_FACE ) > 0 ) {
std : : vector < double > thickness ;
std : : vector < Handle_Geom_Surface > layers ;
std : : vector < std : : vector < Handle_Geom_Surface > > folded_layers ;
std : : vector < const SurfaceStyle * > styles ;
if ( convert_layerset ( product , layers , styles , thickness ) ) {
2017-06-28 15:24:49 +02:00
IfcSchema : : IfcRelAssociates : : list : : ptr associations = product - > HasAssociations ( ) ;
for ( IfcSchema : : IfcRelAssociates : : list : : it it = associations - > begin ( ) ; it ! = associations - > end ( ) ; + + it ) {
IfcSchema : : IfcRelAssociatesMaterial * associates_material = ( * * it ) . as < IfcSchema : : IfcRelAssociatesMaterial > ( ) ;
if ( associates_material ) {
2017-12-11 15:07:40 +01:00
unsigned layerset_id = associates_material - > RelatingMaterial ( ) - > data ( ) . id ( ) ;
2017-06-28 15:24:49 +02:00
representation_id_builder < < " -layerset- " < < layerset_id ;
break ;
}
}
2018-12-19 15:15:28 +01:00
if ( styles . size ( ) > 1 ) {
// If there's only a single layer there is no need to manipulate geometries.
bool success = true ;
if ( product - > as < IfcSchema : : IfcWall > ( ) & & fold_layers ( product - > as < IfcSchema : : IfcWall > ( ) , shapes , layers , thickness , folded_layers ) ) {
if ( apply_folded_layerset ( shapes , folded_layers , styles , shapes2 ) ) {
std : : swap ( shapes , shapes2 ) ;
success = true ;
}
} else {
if ( apply_layerset ( shapes , layers , styles , shapes2 ) ) {
std : : swap ( shapes , shapes2 ) ;
success = true ;
}
2015-10-07 17:33:30 +02:00
}
2018-12-19 15:15:28 +01:00
if ( ! success ) {
Logger : : Error ( " Failed processing layerset " ) ;
2015-10-07 17:33:30 +02:00
}
}
}
}
}
}
2017-10-06 15:36:51 +02:00
bool material_style_applied = false ;
const IfcSchema : : IfcMaterial * single_material = get_single_material_association ( product ) ;
if ( single_material ) {
const IfcGeom : : SurfaceStyle * s = get_style ( single_material ) ;
2017-10-06 16:01:18 +02:00
for ( IfcGeom : : IfcRepresentationShapeItems : : iterator it = shapes . begin ( ) ; it ! = shapes . end ( ) ; + + it ) {
2017-10-06 15:36:51 +02:00
if ( ! it - > hasStyle ( ) & & s ) {
it - > setStyle ( s ) ;
material_style_applied = true ;
}
}
2018-12-19 16:54:02 +01:00
} else {
bool some_items_without_style = false ;
for ( IfcGeom : : IfcRepresentationShapeItems : : iterator it = shapes . begin ( ) ; it ! = shapes . end ( ) ; + + it ) {
2020-08-16 11:41:07 +02:00
if ( ! it - > hasStyle ( ) & & count ( it - > Shape ( ) , TopAbs_FACE ) ) {
2018-12-19 16:54:02 +01:00
some_items_without_style = true ;
break ;
}
}
if ( some_items_without_style ) {
2018-12-21 11:53:36 +01:00
Logger : : Warning ( " No material and surface styles for: " , product ) ;
2018-12-19 16:54:02 +01:00
}
2018-03-28 17:33:46 +03:00
}
2017-10-06 15:36:51 +02:00
if ( material_style_applied ) {
2017-12-11 15:07:40 +01:00
representation_id_builder < < " -material- " < < single_material - > data ( ) . id ( ) ;
2017-10-06 15:36:51 +02:00
}
2020-09-27 11:51:00 +02:00
if ( settings . force_space_transparency ( ) > = 0. & & product - > declaration ( ) . is ( " IfcSpace " ) ) {
for ( auto & s : shapes ) {
if ( s . hasStyle ( ) ) {
for ( auto & p : style_cache ) {
if ( & p . second = = & s . Style ( ) ) {
p . second . Transparency ( ) = settings . force_space_transparency ( ) ;
}
}
}
}
}
2015-01-05 14:11:42 +00:00
int parent_id = - 1 ;
try {
2019-01-11 16:22:53 +01:00
IfcUtil : : IfcBaseEntity * parent_object = get_decomposing_entity ( product ) ;
if ( parent_object & & parent_object - > as < IfcSchema : : IfcObjectDefinition > ( ) ) {
2017-12-11 15:07:40 +01:00
parent_id = parent_object - > data ( ) . id ( ) ;
2015-01-05 14:11:42 +00:00
}
2017-08-01 16:45:11 +02:00
} catch ( const std : : exception & e ) {
Logger : : Error ( e ) ;
}
2015-01-05 14:11:42 +00:00
const std : : string name = product - > hasName ( ) ? product - > Name ( ) : " " ;
const std : : string guid = product - > GlobalId ( ) ;
gp_Trsf trsf ;
try {
convert ( product - > ObjectPlacement ( ) , trsf ) ;
2017-08-01 16:45:11 +02:00
} catch ( const std : : exception & e ) {
Logger : : Error ( e ) ;
} catch ( . . . ) {
Logger : : Error ( " Failed to construct placement " ) ;
}
2015-01-05 14:11:42 +00:00
// Does the IfcElement have any IfcOpenings?
// Note that openings for IfcOpeningElements are not processed
2016-03-18 11:23:25 +01:00
IfcSchema : : IfcRelVoidsElement : : list : : ptr openings = find_openings ( product ) ;
2015-01-05 14:11:42 +00:00
2017-12-18 15:25:09 +01:00
const std : : string product_type = product - > declaration ( ) . name ( ) ;
2015-01-05 14:11:42 +00:00
ElementSettings element_settings ( settings , getValue ( GV_LENGTH_UNIT ) , product_type ) ;
2016-03-07 22:37:36 +02:00
if ( ! settings . get ( IfcGeom : : IteratorSettings : : DISABLE_OPENING_SUBTRACTIONS ) & & openings & & openings - > size ( ) ) {
2017-06-28 15:24:49 +02:00
representation_id_builder < < " -openings " ;
for ( IfcSchema : : IfcRelVoidsElement : : list : : it it = openings - > begin ( ) ; it ! = openings - > end ( ) ; + + it ) {
2017-12-11 15:07:40 +01:00
representation_id_builder < < " - " < < ( * it ) - > data ( ) . id ( ) ;
2017-06-28 15:24:49 +02:00
}
2015-01-05 14:11:42 +00:00
IfcGeom : : IfcRepresentationShapeItems opened_shapes ;
2018-09-17 14:11:03 +02:00
bool caught_error = false ;
2015-01-05 14:11:42 +00:00
try {
2016-01-25 13:30:33 +01:00
# if OCC_VERSION_HEX < 0x60900
2016-03-07 22:37:36 +02:00
const bool faster_booleans = settings . get ( IteratorSettings : : FASTER_BOOLEANS ) ;
2016-01-25 13:30:33 +01:00
# else
const bool faster_booleans = true ;
# endif
if ( faster_booleans ) {
2017-11-13 12:41:37 +01:00
bool success = convert_openings_fast ( product , openings , shapes , trsf , opened_shapes ) ;
# if OCC_VERSION_HEX < 0x60900
2017-11-13 13:01:59 +01:00
if ( ! success ) {
2015-01-05 14:11:42 +00:00
opened_shapes . clear ( ) ;
convert_openings ( product , openings , shapes , trsf , opened_shapes ) ;
}
2017-11-13 12:41:37 +01:00
# else
( void ) success ;
# endif
2015-01-05 14:11:42 +00:00
} else {
convert_openings ( product , openings , shapes , trsf , opened_shapes ) ;
}
2018-09-12 16:44:30 +02:00
} catch ( const std : : exception & e ) {
2018-09-17 17:46:13 +02:00
Logger : : Message ( Logger : : LOG_ERROR , std : : string ( " Error processing openings for: " ) + e . what ( ) + " : " , product ) ;
2018-09-17 14:11:03 +02:00
caught_error = true ;
2015-01-05 14:11:42 +00:00
} catch ( . . . ) {
2017-12-11 15:07:40 +01:00
Logger : : Message ( Logger : : LOG_ERROR , " Error processing openings for: " , product ) ;
2015-01-05 14:11:42 +00:00
}
2018-09-17 14:11:03 +02:00
if ( caught_error & & opened_shapes . size ( ) < shapes . size ( ) ) {
opened_shapes = shapes ;
}
2016-03-07 22:37:36 +02:00
if ( settings . get ( IteratorSettings : : USE_WORLD_COORDS ) ) {
2015-01-05 14:11:42 +00:00
for ( IfcGeom : : IfcRepresentationShapeItems : : iterator it = opened_shapes . begin ( ) ; it ! = opened_shapes . end ( ) ; + + it ) {
it - > prepend ( trsf ) ;
}
trsf = gp_Trsf ( ) ;
2017-06-28 15:24:49 +02:00
representation_id_builder < < " -world-coords " ;
2015-01-05 14:11:42 +00:00
}
2017-06-28 15:24:49 +02:00
shape = new IfcGeom : : Representation : : BRep ( element_settings , representation_id_builder . str ( ) , opened_shapes ) ;
2016-03-07 22:37:36 +02:00
} else if ( settings . get ( IteratorSettings : : USE_WORLD_COORDS ) ) {
2015-01-05 14:11:42 +00:00
for ( IfcGeom : : IfcRepresentationShapeItems : : iterator it = shapes . begin ( ) ; it ! = shapes . end ( ) ; + + it ) {
it - > prepend ( trsf ) ;
}
trsf = gp_Trsf ( ) ;
2017-06-28 15:24:49 +02:00
representation_id_builder < < " -world-coords " ;
shape = new IfcGeom : : Representation : : BRep ( element_settings , representation_id_builder . str ( ) , shapes ) ;
2015-01-05 14:11:42 +00:00
} else {
2017-06-28 15:24:49 +02:00
shape = new IfcGeom : : Representation : : BRep ( element_settings , representation_id_builder . str ( ) , shapes ) ;
2015-01-05 14:11:42 +00:00
}
2015-04-22 09:15:48 +00:00
std : : string context_string = " " ;
if ( representation - > hasRepresentationIdentifier ( ) ) {
context_string = representation - > RepresentationIdentifier ( ) ;
} else if ( representation - > ContextOfItems ( ) - > hasContextType ( ) ) {
context_string = representation - > ContextOfItems ( ) - > ContextType ( ) ;
}
2018-12-12 12:33:09 +01:00
auto elem = new BRepElement < P , PP > (
2017-12-11 15:07:40 +01:00
product - > data ( ) . id ( ) ,
2015-01-05 14:11:42 +00:00
parent_id ,
2018-12-12 12:33:09 +01:00
name ,
2015-01-05 14:11:42 +00:00
product_type ,
guid ,
2015-04-22 09:15:48 +00:00
context_string ,
2015-01-05 14:11:42 +00:00
trsf ,
2017-02-07 17:34:23 +02:00
boost : : shared_ptr < IfcGeom : : Representation : : BRep > ( shape ) ,
2018-12-12 12:33:09 +01:00
product
2016-03-18 11:23:25 +01:00
) ;
2018-12-12 12:33:09 +01:00
if ( settings . get ( IteratorSettings : : VALIDATE_QUANTITIES ) ) {
auto rels = product - > IsDefinedBy ( ) ;
for ( auto & rel : * rels ) {
if ( rel - > as < IfcSchema : : IfcRelDefinesByProperties > ( ) ) {
auto pdef = rel - > as < IfcSchema : : IfcRelDefinesByProperties > ( ) - > RelatingPropertyDefinition ( ) ;
if ( pdef - > as < IfcSchema : : IfcElementQuantity > ( ) ) {
2018-12-30 12:55:17 +01:00
std : : string organization_name ;
try {
// A couple of files are not according to the schema here.
organization_name = pdef - > as < IfcSchema : : IfcElementQuantity > ( ) - > OwnerHistory ( ) - > OwningApplication ( ) - > ApplicationDeveloper ( ) - > Name ( ) ;
} catch ( . . . ) { }
if ( organization_name = = " IfcOpenShell " ) {
2018-12-12 12:33:09 +01:00
auto qs = pdef - > as < IfcSchema : : IfcElementQuantity > ( ) - > Quantities ( ) ;
for ( auto & q : * qs ) {
if ( q - > as < IfcSchema : : IfcQuantityArea > ( ) & & q - > Name ( ) = = " Total Surface Area " ) {
double a_calc ;
double a_file = q - > as < IfcSchema : : IfcQuantityArea > ( ) - > AreaValue ( ) ;
if ( elem - > geometry ( ) . calculate_surface_area ( a_calc ) ) {
double diff = std : : abs ( a_calc - a_file ) ;
if ( diff / std : : sqrt ( a_file ) > getValue ( GV_PRECISION ) ) {
Logger : : Error ( " Validation of surface area failed for: " , product ) ;
} else {
Logger : : Notice ( " Validation of surface area succeeded for: " , product ) ;
}
} else {
Logger : : Error ( " Validation of surface area failed for: " , product ) ;
}
} else if ( q - > as < IfcSchema : : IfcQuantityVolume > ( ) & & q - > Name ( ) = = " Volume " ) {
double v_calc ;
double v_file = q - > as < IfcSchema : : IfcQuantityVolume > ( ) - > VolumeValue ( ) ;
if ( elem - > geometry ( ) . calculate_volume ( v_calc ) ) {
double diff = std : : abs ( v_calc - v_file ) ;
if ( diff / std : : sqrt ( v_file ) > getValue ( GV_PRECISION ) ) {
Logger : : Error ( " Validation of volume failed for: " , product ) ;
} else {
Logger : : Notice ( " Validation of volume succeeded for: " , product ) ;
}
} else {
Logger : : Error ( " Validation of volume failed for: " , product ) ;
}
} else if ( q - > as < IfcSchema : : IfcPhysicalComplexQuantity > ( ) & & q - > Name ( ) = = " Shape Validation Properties " ) {
auto qs2 = q - > as < IfcSchema : : IfcPhysicalComplexQuantity > ( ) - > HasQuantities ( ) ;
bool all_succeeded = qs2 - > size ( ) > 0 ;
for ( auto & q2 : * qs2 ) {
if ( q2 - > as < IfcSchema : : IfcQuantityCount > ( ) & & q2 - > Name ( ) = = " Surface Genus " & & q2 - > hasDescription ( ) ) {
int item_id = boost : : lexical_cast < int > ( q2 - > Description ( ) . substr ( 1 ) ) ;
int genus = q2 - > as < IfcSchema : : IfcQuantityCount > ( ) - > CountValue ( ) ;
for ( auto & part : elem - > geometry ( ) ) {
if ( part . ItemId ( ) = = item_id ) {
if ( surface_genus ( part . Shape ( ) ) ! = genus ) {
all_succeeded = false ;
}
}
}
}
}
if ( ! all_succeeded ) {
Logger : : Error ( " Validation of surface genus failed for: " , product ) ;
} else {
Logger : : Notice ( " Validation of surface genus succeeded for: " , product ) ;
}
}
}
}
}
}
}
}
return elem ;
2016-03-18 11:23:25 +01:00
}
2017-10-06 15:36:51 +02:00
IfcSchema : : IfcRepresentation * IfcGeom : : Kernel : : representation_mapped_to ( const IfcSchema : : IfcRepresentation * representation ) {
IfcSchema : : IfcRepresentation * representation_mapped_to = 0 ;
2020-05-20 11:55:59 +02:00
try {
IfcSchema : : IfcRepresentationItem : : list : : ptr items = representation - > Items ( ) ;
if ( items - > size ( ) = = 1 ) {
IfcSchema : : IfcRepresentationItem * item = * items - > begin ( ) ;
if ( item - > declaration ( ) . is ( IfcSchema : : IfcMappedItem : : Class ( ) ) ) {
if ( item - > StyledByItem ( ) - > size ( ) = = 0 ) {
IfcSchema : : IfcMappedItem * mapped_item = item - > as < IfcSchema : : IfcMappedItem > ( ) ;
if ( is_identity_transform ( mapped_item - > MappingTarget ( ) ) ) {
IfcSchema : : IfcRepresentationMap * map = mapped_item - > MappingSource ( ) ;
if ( is_identity_transform ( map - > MappingOrigin ( ) ) ) {
representation_mapped_to = map - > MappedRepresentation ( ) ;
}
2017-10-06 15:36:51 +02:00
}
}
}
}
2020-05-20 11:55:59 +02:00
} catch ( const IfcParse : : IfcException & e ) {
Logger : : Error ( e ) ;
// @todo reset representation_mapped_to to zero?
2017-10-06 15:36:51 +02:00
}
return representation_mapped_to ;
}
IfcSchema : : IfcProduct : : list : : ptr IfcGeom : : Kernel : : products_represented_by ( const IfcSchema : : IfcRepresentation * representation ) {
IfcSchema : : IfcProduct : : list : : ptr products ( new IfcSchema : : IfcProduct : : list ) ;
IfcSchema : : IfcProductRepresentation : : list : : ptr prodreps = representation - > OfProductRepresentation ( ) ;
for ( IfcSchema : : IfcProductRepresentation : : list : : it it = prodreps - > begin ( ) ; it ! = prodreps - > end ( ) ; + + it ) {
// http://buildingsmart-tech.org/ifc/IFC2x3/TC1/html/ifcrepresentationresource/lexical/ifcproductrepresentation.htm
// IFC2x Edition 3 NOTE Users should not instantiate the entity IfcProductRepresentation from IFC2x Edition 3 onwards.
// It will be changed into an ABSTRACT supertype in future releases of IFC.
// IfcProductRepresentation also lacks the INVERSE relation to IfcProduct
// Let's find the IfcProducts that reference the IfcProductRepresentation anyway
2017-12-18 15:25:09 +01:00
products - > push ( ( * it ) - > data ( ) . getInverse ( ( & IfcSchema : : IfcProduct : : Class ( ) ) , - 1 ) - > as < IfcSchema : : IfcProduct > ( ) ) ;
2017-10-06 15:36:51 +02:00
}
IfcSchema : : IfcRepresentationMap : : list : : ptr maps = representation - > RepresentationMap ( ) ;
2019-11-17 14:07:32 +01:00
if ( products - > size ( ) & & maps - > size ( ) ) {
Logger : : Warning ( " Representation used by IfcRepresentationMap and IfcProductDefinitionShape " , representation ) ;
}
2019-11-18 12:59:48 +01:00
if ( prodreps - > size ( ) > 1 ) {
Logger : : Warning ( " Multiple IfcProductDefinitionShapes for representation " , representation ) ;
}
if ( maps - > size ( ) > 1 ) {
Logger : : Warning ( " Multiple IfcRepresentationMaps for representation " , representation ) ;
}
2017-10-06 15:36:51 +02:00
if ( maps - > size ( ) = = 1 ) {
IfcSchema : : IfcRepresentationMap * map = * maps - > begin ( ) ;
if ( is_identity_transform ( map - > MappingOrigin ( ) ) ) {
IfcSchema : : IfcMappedItem : : list : : ptr items = map - > MapUsage ( ) ;
for ( IfcSchema : : IfcMappedItem : : list : : it it = items - > begin ( ) ; it ! = items - > end ( ) ; + + it ) {
IfcSchema : : IfcMappedItem * item = * it ;
if ( item - > StyledByItem ( ) - > size ( ) ! = 0 ) continue ;
if ( ! is_identity_transform ( item - > MappingTarget ( ) ) ) {
continue ;
}
2017-12-18 15:25:09 +01:00
IfcSchema : : IfcRepresentation : : list : : ptr reps = item - > data ( ) . getInverse ( ( & IfcSchema : : IfcRepresentation : : Class ( ) ) , - 1 ) - > as < IfcSchema : : IfcRepresentation > ( ) ;
2017-10-06 15:36:51 +02:00
for ( IfcSchema : : IfcRepresentation : : list : : it jt = reps - > begin ( ) ; jt ! = reps - > end ( ) ; + + jt ) {
IfcSchema : : IfcRepresentation * rep = * jt ;
if ( rep - > Items ( ) - > size ( ) ! = 1 ) continue ;
IfcSchema : : IfcProductRepresentation : : list : : ptr prodreps_mapped = rep - > OfProductRepresentation ( ) ;
for ( IfcSchema : : IfcProductRepresentation : : list : : it kt = prodreps_mapped - > begin ( ) ; kt ! = prodreps_mapped - > end ( ) ; + + kt ) {
2017-12-18 15:25:09 +01:00
IfcSchema : : IfcProduct : : list : : ptr ps = ( * kt ) - > data ( ) . getInverse ( ( & IfcSchema : : IfcProduct : : Class ( ) ) , - 1 ) - > as < IfcSchema : : IfcProduct > ( ) ;
2017-10-06 15:36:51 +02:00
products - > push ( ps ) ;
}
}
}
}
}
return products ;
}
2018-07-07 21:14:27 +02:00
template < typename P , typename PP >
IfcGeom : : BRepElement < P , PP > * IfcGeom : : Kernel : : create_brep_for_processed_representation (
2016-07-11 21:36:12 +03:00
const IteratorSettings & /*settings*/ , IfcSchema : : IfcRepresentation * representation , IfcSchema : : IfcProduct * product ,
2018-07-07 21:14:27 +02:00
IfcGeom : : BRepElement < P , PP > * brep )
2016-07-11 21:36:12 +03:00
{
2016-03-18 11:23:25 +01:00
int parent_id = - 1 ;
try {
2019-01-11 16:22:53 +01:00
IfcUtil : : IfcBaseEntity * parent_object = get_decomposing_entity ( product ) ;
if ( parent_object & & parent_object - > as < IfcSchema : : IfcObjectDefinition > ( ) ) {
2017-12-11 15:07:40 +01:00
parent_id = parent_object - > data ( ) . id ( ) ;
2016-03-18 11:23:25 +01:00
}
2017-08-01 16:45:11 +02:00
} catch ( const std : : exception & e ) {
Logger : : Error ( e ) ;
}
2016-03-18 11:23:25 +01:00
const std : : string name = product - > hasName ( ) ? product - > Name ( ) : " " ;
const std : : string guid = product - > GlobalId ( ) ;
gp_Trsf trsf ;
try {
convert ( product - > ObjectPlacement ( ) , trsf ) ;
2017-08-01 16:45:11 +02:00
} catch ( const std : : exception & e ) {
Logger : : Error ( e ) ;
} catch ( . . . ) {
Logger : : Error ( " Failed to construct placement " ) ;
}
2016-03-18 11:23:25 +01:00
std : : string context_string = " " ;
if ( representation - > hasRepresentationIdentifier ( ) ) {
context_string = representation - > RepresentationIdentifier ( ) ;
} else if ( representation - > ContextOfItems ( ) - > hasContextType ( ) ) {
context_string = representation - > ContextOfItems ( ) - > ContextType ( ) ;
}
2017-12-18 15:25:09 +01:00
const std : : string product_type = product - > declaration ( ) . name ( ) ;
2016-03-18 11:23:25 +01:00
2018-07-07 21:14:27 +02:00
return new BRepElement < P , PP > (
2017-12-11 15:07:40 +01:00
product - > data ( ) . id ( ) ,
2016-03-18 11:23:25 +01:00
parent_id ,
name ,
product_type ,
guid ,
context_string ,
trsf ,
2017-02-07 17:34:23 +02:00
brep - > geometry_pointer ( ) ,
product
2015-01-05 14:11:42 +00:00
) ;
}
2018-07-07 21:14:27 +02:00
template IFC_GEOM_API IfcGeom : : BRepElement < float , float > * IfcGeom : : Kernel : : create_brep_for_representation_and_product < float , float > (
2016-07-11 21:36:12 +03:00
const IteratorSettings & settings , IfcSchema : : IfcRepresentation * representation , IfcSchema : : IfcProduct * product ) ;
2018-07-07 21:14:27 +02:00
template IFC_GEOM_API IfcGeom : : BRepElement < float , double > * IfcGeom : : Kernel : : create_brep_for_representation_and_product < float , double > (
2016-07-11 21:36:12 +03:00
const IteratorSettings & settings , IfcSchema : : IfcRepresentation * representation , IfcSchema : : IfcProduct * product ) ;
2018-07-07 21:14:27 +02:00
template IFC_GEOM_API IfcGeom : : BRepElement < double , double > * IfcGeom : : Kernel : : create_brep_for_representation_and_product < double , double > (
const IteratorSettings & settings , IfcSchema : : IfcRepresentation * representation , IfcSchema : : IfcProduct * product ) ;
template IFC_GEOM_API IfcGeom : : BRepElement < float , float > * IfcGeom : : Kernel : : create_brep_for_processed_representation < float , float > (
const IteratorSettings & settings , IfcSchema : : IfcRepresentation * representation , IfcSchema : : IfcProduct * product , IfcGeom : : BRepElement < float , float > * brep ) ;
template IFC_GEOM_API IfcGeom : : BRepElement < float , double > * IfcGeom : : Kernel : : create_brep_for_processed_representation < float , double > (
const IteratorSettings & settings , IfcSchema : : IfcRepresentation * representation , IfcSchema : : IfcProduct * product , IfcGeom : : BRepElement < float , double > * brep ) ;
template IFC_GEOM_API IfcGeom : : BRepElement < double , double > * IfcGeom : : Kernel : : create_brep_for_processed_representation < double , double > (
const IteratorSettings & settings , IfcSchema : : IfcRepresentation * representation , IfcSchema : : IfcProduct * product , IfcGeom : : BRepElement < double , double > * brep ) ;
2016-03-18 11:23:25 +01:00
2015-01-05 14:11:42 +00:00
std : : pair < std : : string , double > IfcGeom : : Kernel : : initializeUnits ( IfcSchema : : IfcUnitAssignment * unit_assignment ) {
// Set default units, set length to meters, angles to undefined
setValue ( IfcGeom : : Kernel : : GV_LENGTH_UNIT , 1.0 ) ;
setValue ( IfcGeom : : Kernel : : GV_PLANEANGLE_UNIT , - 1.0 ) ;
std : : string unit_name = " METER " ;
double unit_magnitude = 1. ;
2018-09-07 14:25:46 +02:00
bool length_unit_encountered = false , angle_unit_encountered = false ;
2015-01-05 14:11:42 +00:00
try {
IfcEntityList : : ptr units = unit_assignment - > Units ( ) ;
2015-01-10 22:13:52 +00:00
if ( ! units | | ! units - > size ( ) ) {
2018-12-29 12:34:32 +01:00
Logger : : Warning ( " No unit information found " ) ;
2015-01-05 14:11:42 +00:00
} else {
2016-05-24 13:50:50 +02:00
for ( IfcEntityList : : it it = units - > begin ( ) ; it ! = units - > end ( ) ; + + it ) {
2015-01-05 14:11:42 +00:00
IfcUtil : : IfcBaseClass * base = * it ;
2017-12-18 15:25:09 +01:00
if ( base - > declaration ( ) . is ( IfcSchema : : IfcNamedUnit : : Class ( ) ) ) {
2016-05-24 13:50:50 +02:00
IfcSchema : : IfcNamedUnit * named_unit = base - > as < IfcSchema : : IfcNamedUnit > ( ) ;
if ( named_unit - > UnitType ( ) = = IfcSchema : : IfcUnitEnum : : IfcUnit_LENGTHUNIT | |
named_unit - > UnitType ( ) = = IfcSchema : : IfcUnitEnum : : IfcUnit_PLANEANGLEUNIT )
{
std : : string current_unit_name ;
2017-12-18 15:25:09 +01:00
const double current_unit_magnitude = IfcParse : : get_SI_equivalent < IfcSchema > ( named_unit ) ;
2016-05-24 13:50:50 +02:00
if ( current_unit_magnitude ! = 0. ) {
2017-12-18 15:25:09 +01:00
if ( named_unit - > declaration ( ) . is ( IfcSchema : : IfcConversionBasedUnit : : Class ( ) ) ) {
2016-05-24 13:50:50 +02:00
IfcSchema : : IfcConversionBasedUnit * u = ( IfcSchema : : IfcConversionBasedUnit * ) base ;
current_unit_name = u - > Name ( ) ;
2017-12-18 15:25:09 +01:00
} else if ( named_unit - > declaration ( ) . is ( IfcSchema : : IfcSIUnit : : Class ( ) ) ) {
2016-05-24 13:50:50 +02:00
IfcSchema : : IfcSIUnit * si_unit = named_unit - > as < IfcSchema : : IfcSIUnit > ( ) ;
if ( si_unit - > hasPrefix ( ) ) {
current_unit_name = IfcSchema : : IfcSIPrefix : : ToString ( si_unit - > Prefix ( ) ) + unit_name ;
}
current_unit_name + = IfcSchema : : IfcSIUnitName : : ToString ( si_unit - > Name ( ) ) ;
}
if ( named_unit - > UnitType ( ) = = IfcSchema : : IfcUnitEnum : : IfcUnit_LENGTHUNIT ) {
unit_name = current_unit_name ;
unit_magnitude = current_unit_magnitude ;
setValue ( IfcGeom : : Kernel : : GV_LENGTH_UNIT , current_unit_magnitude ) ;
2018-09-07 14:25:46 +02:00
length_unit_encountered = true ;
2016-05-24 13:50:50 +02:00
} else {
setValue ( IfcGeom : : Kernel : : GV_PLANEANGLE_UNIT , current_unit_magnitude ) ;
2018-09-07 14:25:46 +02:00
angle_unit_encountered = true ;
2015-01-05 14:11:42 +00:00
}
}
}
}
}
}
} catch ( const IfcParse : : IfcException & ex ) {
std : : stringstream ss ;
ss < < " Failed to determine unit information ' " < < ex . what ( ) < < " ' " ;
Logger : : Message ( Logger : : LOG_ERROR , ss . str ( ) ) ;
}
2018-09-07 14:25:46 +02:00
if ( ! length_unit_encountered ) {
2018-12-29 12:34:32 +01:00
Logger : : Warning ( " No length unit encountered " ) ;
2018-09-07 14:25:46 +02:00
}
if ( ! angle_unit_encountered ) {
2018-12-29 12:34:32 +01:00
Logger : : Warning ( " No plane angle unit encountered " ) ;
2018-09-07 14:25:46 +02:00
}
2015-01-05 14:11:42 +00:00
return std : : pair < std : : string , double > ( unit_name , unit_magnitude ) ;
2015-10-07 17:33:30 +02:00
}
bool IfcGeom : : Kernel : : convert_layerset ( const IfcSchema : : IfcProduct * product , std : : vector < Handle_Geom_Surface > & surfaces , std : : vector < const SurfaceStyle * > & styles , std : : vector < double > & thicknesses ) {
IfcSchema : : IfcMaterialLayerSetUsage * usage = 0 ;
Handle_Geom_Surface reference_surface ;
IfcSchema : : IfcRelAssociates : : list : : ptr associations = product - > HasAssociations ( ) ;
for ( IfcSchema : : IfcRelAssociates : : list : : it it = associations - > begin ( ) ; it ! = associations - > end ( ) ; + + it ) {
IfcSchema : : IfcRelAssociatesMaterial * associates_material = ( * * it ) . as < IfcSchema : : IfcRelAssociatesMaterial > ( ) ;
if ( associates_material ) {
usage = associates_material - > RelatingMaterial ( ) - > as < IfcSchema : : IfcMaterialLayerSetUsage > ( ) ;
break ;
}
}
if ( ! usage ) {
return false ;
}
IfcSchema : : IfcRepresentation * body_representation = find_representation ( product , " Body " ) ;
2018-11-20 12:10:44 +01:00
if ( ! body_representation ) {
2018-11-28 14:18:58 +01:00
Logger : : Warning ( " No body representation for product " , product ) ;
2018-11-20 12:10:44 +01:00
return false ;
}
2015-10-07 17:33:30 +02:00
2017-12-18 15:25:09 +01:00
if ( product - > declaration ( ) . is ( IfcSchema : : IfcWall : : Class ( ) ) ) {
2018-11-20 12:10:44 +01:00
IfcSchema : : IfcRepresentation * axis_representation = find_representation ( product , " Axis " ) ;
2015-10-07 17:33:30 +02:00
if ( ! axis_representation ) {
2017-12-11 15:07:40 +01:00
Logger : : Message ( Logger : : LOG_WARNING , " No axis representation for: " , product ) ;
2015-10-07 17:33:30 +02:00
return false ;
}
IfcRepresentationShapeItems axis_items ;
{
Kernel temp = * this ;
temp . setValue ( GV_DIMENSIONALITY , - 1. ) ;
temp . convert_shapes ( axis_representation , axis_items ) ;
}
TopoDS_Shape axis_shape ;
flatten_shape_list ( axis_items , axis_shape , false ) ;
TopExp_Explorer exp ( axis_shape , TopAbs_EDGE ) ;
TopoDS_Edge axis_edge ;
int edge_count = 0 ;
2016-05-02 15:56:08 +02:00
if ( exp . More ( ) ) {
2015-10-07 17:33:30 +02:00
axis_edge = TopoDS : : Edge ( exp . Current ( ) ) ;
+ + edge_count ;
2016-05-02 15:56:08 +02:00
} else {
2017-12-11 15:07:40 +01:00
Logger : : Message ( Logger : : LOG_WARNING , " No edge found in axis representation: " , product ) ;
2015-10-07 17:33:30 +02:00
return false ;
}
double u1 , u2 ;
Handle_Geom_Curve axis_curve = BRep_Tool : : Curve ( axis_edge , u1 , u2 ) ;
2016-05-30 13:44:03 +03:00
if ( true ) { /**< @todo Why always true? */
2015-10-07 17:33:30 +02:00
if ( axis_curve - > DynamicType ( ) = = STANDARD_TYPE ( Geom_Line ) ) {
Handle_Geom_Line axis_line = Handle_Geom_Line : : DownCast ( axis_curve ) ;
2018-12-21 14:40:58 +01:00
// @todo note that this creates an offset into the wrong order, the cross product arguments should be
// reversed. This causes some inversions later on, e.g. if(positive) { reverse(); }
2015-10-07 17:33:30 +02:00
reference_surface = new Geom_Plane ( axis_line - > Lin ( ) . Location ( ) , axis_line - > Lin ( ) . Direction ( ) ^ gp : : DZ ( ) ) ;
} else if ( axis_curve - > DynamicType ( ) = = STANDARD_TYPE ( Geom_Circle ) ) {
2018-12-21 14:40:58 +01:00
// @todo note that in this branch this inversion does not seem to take place.
2015-10-07 17:33:30 +02:00
Handle_Geom_Circle axis_line = Handle_Geom_Circle : : DownCast ( axis_curve ) ;
reference_surface = new Geom_CylindricalSurface ( axis_line - > Position ( ) , axis_line - > Radius ( ) ) ;
} else {
2017-12-11 15:07:40 +01:00
Logger : : Message ( Logger : : LOG_ERROR , " Unsupported underlying curve of Axis representation: " , product ) ;
2015-10-07 17:33:30 +02:00
return false ;
}
} else {
// Unfortunately this does not work when its intersection
// is calculated later on when the layerset is applied.
reference_surface = new Geom_SurfaceOfLinearExtrusion ( axis_curve , gp : : DZ ( ) ) ;
}
} else {
2017-12-11 15:07:40 +01:00
IfcSchema : : IfcExtrudedAreaSolid : : list : : ptr extrusions = IfcParse : : traverse ( body_representation ) - > as < IfcSchema : : IfcExtrudedAreaSolid > ( ) ;
2015-10-07 17:33:30 +02:00
if ( extrusions - > size ( ) ! = 1 ) {
2017-12-11 15:07:40 +01:00
Logger : : Message ( Logger : : LOG_WARNING , " No single extrusion found in body representation for: " , product ) ;
2015-10-07 17:33:30 +02:00
return false ;
}
IfcSchema : : IfcExtrudedAreaSolid * extrusion = * extrusions - > begin ( ) ;
gp_Trsf extrusion_position ;
2017-08-09 17:01:10 +02:00
bool has_position = true ;
2019-06-26 13:50:46 +02:00
# ifdef SCHEMA_IfcSweptAreaSolid_Position_IS_OPTIONAL
2017-08-09 17:01:10 +02:00
has_position = extrusion - > hasPosition ( ) ;
# endif
if ( has_position ) {
if ( ! convert ( extrusion - > Position ( ) , extrusion_position ) ) {
2017-12-11 15:07:40 +01:00
Logger : : Message ( Logger : : LOG_ERROR , " Failed to convert placement for extrusion of: " , product ) ;
2017-08-09 17:01:10 +02:00
return false ;
}
2015-10-07 17:33:30 +02:00
}
gp_Dir extrusion_direction ;
if ( ! convert ( extrusion - > ExtrudedDirection ( ) , extrusion_direction ) ) {
2017-12-11 15:07:40 +01:00
Logger : : Message ( Logger : : LOG_ERROR , " Failed to convert direction for extrusion of: " , product ) ;
2015-10-07 17:33:30 +02:00
return false ;
}
reference_surface = new Geom_Plane ( extrusion_position . TranslationPart ( ) , extrusion_direction ) ;
}
const IfcSchema : : IfcMaterialLayerSet * layerset = usage - > ForLayerSet ( ) ;
const bool positive = usage - > DirectionSense ( ) = = IfcSchema : : IfcDirectionSenseEnum : : IfcDirectionSense_POSITIVE ;
double offset = usage - > OffsetFromReferenceLine ( ) * getValue ( GV_LENGTH_UNIT ) ;
IfcSchema : : IfcMaterialLayer : : list : : ptr material_layers = layerset - > MaterialLayers ( ) ;
surfaces . push_back ( new Geom_OffsetSurface ( reference_surface , - offset ) ) ;
for ( IfcSchema : : IfcMaterialLayer : : list : : it it = material_layers - > begin ( ) ; it ! = material_layers - > end ( ) ; + + it ) {
styles . push_back ( get_style ( ( * it ) - > Material ( ) ) ) ;
double thickness = ( * it ) - > LayerThickness ( ) * getValue ( GV_LENGTH_UNIT ) ;
thicknesses . push_back ( thickness ) ;
if ( ! positive ) {
thickness * = - 1 ;
}
offset + = thickness ;
if ( fabs ( offset ) < 1.e-7 ) {
surfaces . push_back ( reference_surface ) ;
} else {
surfaces . push_back ( new Geom_OffsetSurface ( reference_surface , - offset ) ) ;
}
}
if ( positive ) {
2019-06-06 09:09:36 +03:00
std : : reverse ( thicknesses . begin ( ) , thicknesses . end ( ) ) ;
2019-05-24 14:57:29 +03:00
std : : reverse ( styles . begin ( ) , styles . end ( ) ) ;
2015-10-07 17:33:30 +02:00
std : : reverse ( surfaces . begin ( ) , surfaces . end ( ) ) ;
}
return true ;
}
const Handle_Geom_Curve IfcGeom : : Kernel : : intersect ( const Handle_Geom_Surface & a , const Handle_Geom_Surface & b ) {
GeomAPI_IntSS x ( a , b , 1.e-7 ) ;
if ( x . IsDone ( ) & & x . NbLines ( ) = = 1 ) {
return x . Line ( 1 ) ;
} else {
return Handle_Geom_Curve ( ) ;
}
}
const Handle_Geom_Curve IfcGeom : : Kernel : : intersect ( const Handle_Geom_Surface & a , const TopoDS_Face & b ) {
return intersect ( a , BRep_Tool : : Surface ( b ) ) ;
}
const Handle_Geom_Curve IfcGeom : : Kernel : : intersect ( const TopoDS_Face & a , const Handle_Geom_Surface & b ) {
return intersect ( BRep_Tool : : Surface ( a ) , b ) ;
}
2016-05-02 15:56:08 +02:00
bool IfcGeom : : Kernel : : intersect ( const Handle_Geom_Curve & a , const Handle_Geom_Surface & b , gp_Pnt & p ) {
2015-10-07 17:33:30 +02:00
GeomAPI_IntCS x ( a , b ) ;
if ( x . IsDone ( ) & & x . NbPoints ( ) = = 1 ) {
p = x . Point ( 1 ) ;
return true ;
} else {
return false ;
}
}
bool IfcGeom : : Kernel : : intersect ( const Handle_Geom_Curve & a , const TopoDS_Face & b , gp_Pnt & c ) {
2016-05-02 15:56:08 +02:00
return intersect ( a , BRep_Tool : : Surface ( b ) , c ) ;
2015-10-07 17:33:30 +02:00
}
bool IfcGeom : : Kernel : : intersect ( const Handle_Geom_Curve & a , const TopoDS_Shape & b , std : : vector < gp_Pnt > & out ) {
TopExp_Explorer exp ( b , TopAbs_FACE ) ;
gp_Pnt p ;
for ( ; exp . More ( ) ; exp . Next ( ) ) {
if ( intersect ( a , TopoDS : : Face ( exp . Current ( ) ) , p ) ) {
out . push_back ( p ) ;
}
}
return ! out . empty ( ) ;
}
bool IfcGeom : : Kernel : : intersect ( const Handle_Geom_Surface & a , const TopoDS_Shape & b , std : : vector < std : : pair < Handle_Geom_Surface , Handle_Geom_Curve > > & out ) {
TopExp_Explorer exp ( b , TopAbs_FACE ) ;
for ( ; exp . More ( ) ; exp . Next ( ) ) {
const TopoDS_Face & f = TopoDS : : Face ( exp . Current ( ) ) ;
const Handle_Geom_Surface & s = BRep_Tool : : Surface ( f ) ;
Handle_Geom_Curve crv = intersect ( a , s ) ;
if ( ! crv . IsNull ( ) ) {
out . push_back ( std : : make_pair ( s , crv ) ) ;
}
}
return ! out . empty ( ) ;
}
bool IfcGeom : : Kernel : : closest ( const gp_Pnt & a , const std : : vector < gp_Pnt > & b , gp_Pnt & c ) {
double minimal_distance = std : : numeric_limits < double > : : infinity ( ) ;
for ( std : : vector < gp_Pnt > : : const_iterator it = b . begin ( ) ; it ! = b . end ( ) ; + + it ) {
const double d = a . Distance ( * it ) ;
if ( d < minimal_distance ) {
minimal_distance = d ;
c = * it ;
}
}
return minimal_distance ! = std : : numeric_limits < double > : : infinity ( ) ;
}
bool IfcGeom : : Kernel : : project ( const Handle_Geom_Curve & crv , const gp_Pnt & pt , gp_Pnt & p , double & u , double & d ) {
ShapeAnalysis_Curve sac ;
sac . Project ( crv , pt , 1e-3 , p , u , false ) ;
d = pt . Distance ( p ) ;
return true ;
}
bool IfcGeom : : Kernel : : find_wall_end_points ( const IfcSchema : : IfcWall * wall , gp_Pnt & start , gp_Pnt & end ) {
IfcSchema : : IfcRepresentation * axis_representation = find_representation ( wall , " Axis " ) ;
if ( ! axis_representation ) {
return false ;
}
IfcRepresentationShapeItems items ;
{
Kernel temp = * this ;
temp . setValue ( GV_DIMENSIONALITY , - 1. ) ;
temp . convert_shapes ( axis_representation , items ) ;
}
TopoDS_Vertex a , b ;
for ( IfcRepresentationShapeItems : : const_iterator it = items . begin ( ) ; it ! = items . end ( ) ; + + it ) {
TopExp_Explorer exp ( it - > Shape ( ) , TopAbs_VERTEX ) ;
for ( ; exp . More ( ) ; exp . Next ( ) ) {
b = TopoDS : : Vertex ( exp . Current ( ) ) ;
if ( a . IsNull ( ) ) {
a = b ;
}
}
}
if ( a . IsNull ( ) | | b . IsNull ( ) ) {
return false ;
}
start = BRep_Tool : : Pnt ( a ) ;
end = BRep_Tool : : Pnt ( b ) ;
return true ;
}
bool IfcGeom : : Kernel : : fold_layers ( const IfcSchema : : IfcWall * wall , const IfcRepresentationShapeItems & items , const std : : vector < Handle_Geom_Surface > & surfaces , const std : : vector < double > & thicknesses , std : : vector < std : : vector < Handle_Geom_Surface > > & result ) {
2019-08-16 10:42:48 +02:00
/*
* @todo isn't it easier to do this based on the non-folded surfaces of
* the connected walls and fold both pairs of layersets simultaneously?
*/
2015-10-07 17:33:30 +02:00
bool folds_made = false ;
IfcSchema : : IfcRelConnectsPathElements : : list : : ptr connections ( new IfcSchema : : IfcRelConnectsPathElements : : list ) ;
connections - > push ( wall - > ConnectedFrom ( ) - > as < IfcSchema : : IfcRelConnectsPathElements > ( ) ) ;
connections - > push ( wall - > ConnectedTo ( ) - > as < IfcSchema : : IfcRelConnectsPathElements > ( ) ) ;
typedef std : : vector < Handle_Geom_Surface > surfaces_t ;
typedef std : : pair < Handle_Geom_Surface , Handle_Geom_Curve > curve_on_surface ;
typedef std : : vector < curve_on_surface > curves_on_surfaces_t ;
2017-12-18 15:25:09 +01:00
typedef std : : vector < std : : pair < std : : pair < IfcSchema : : IfcConnectionTypeEnum : : Value , IfcSchema : : IfcConnectionTypeEnum : : Value > , const IfcSchema : : IfcProduct * > > endpoint_connections_t ;
2015-10-07 17:33:30 +02:00
typedef std : : vector < std : : vector < Handle_Geom_Surface > > result_t ;
endpoint_connections_t endpoint_connections ;
2019-08-16 10:42:48 +02:00
// Find the semantic connections ot other wall elements when they are not connected 'AT_PATH' because
// in that latter case no folds need to be made.
2015-10-07 17:33:30 +02:00
for ( IfcSchema : : IfcRelConnectsPathElements : : list : : it it = connections - > begin ( ) ; it ! = connections - > end ( ) ; + + it ) {
IfcSchema : : IfcRelConnectsPathElements * connection = * it ;
2017-12-18 15:25:09 +01:00
IfcSchema : : IfcConnectionTypeEnum : : Value own_type = connection - > RelatedElement ( ) = = wall
2015-10-07 17:33:30 +02:00
? connection - > RelatedConnectionType ( )
: connection - > RelatingConnectionType ( ) ;
2017-12-18 15:25:09 +01:00
IfcSchema : : IfcConnectionTypeEnum : : Value other_type = connection - > RelatedElement ( ) = = wall
2015-10-07 17:33:30 +02:00
? connection - > RelatingConnectionType ( )
: connection - > RelatedConnectionType ( ) ;
if ( other_type ! = IfcSchema : : IfcConnectionTypeEnum : : IfcConnectionType_ATPATH & &
( own_type = = IfcSchema : : IfcConnectionTypeEnum : : IfcConnectionType_ATEND | |
own_type = = IfcSchema : : IfcConnectionTypeEnum : : IfcConnectionType_ATSTART ) )
{
IfcSchema : : IfcElement * other = connection - > RelatedElement ( ) = = wall
? connection - > RelatingElement ( )
: connection - > RelatedElement ( ) ;
if ( other - > as < IfcSchema : : IfcWall > ( ) ) {
endpoint_connections . push_back ( std : : make_pair ( std : : make_pair ( own_type , other_type ) , other ) ) ;
}
}
}
if ( endpoint_connections . size ( ) = = 0 ) {
return false ;
}
2019-08-16 10:42:48 +02:00
// Count how many connections are made AT_START and AT_END respectively
2015-10-07 17:33:30 +02:00
int connection_type_count [ 2 ] = { 0 , 0 } ;
2016-04-09 17:54:23 +02:00
for ( endpoint_connections_t : : const_iterator it = endpoint_connections . begin ( ) ; it ! = endpoint_connections . end ( ) ; + + it ) {
2015-10-07 17:33:30 +02:00
const int idx = it - > first . first = = IfcSchema : : IfcConnectionTypeEnum : : IfcConnectionType_ATSTART ;
connection_type_count [ idx ] + + ;
}
gp_Trsf local ;
if ( ! convert ( wall - > ObjectPlacement ( ) , local ) ) {
return false ;
}
local . Invert ( ) ;
{
// Copy the unfolded surfaces
result . resize ( surfaces . size ( ) ) ;
2016-04-09 17:54:23 +02:00
std : : vector < std : : vector < Handle_Geom_Surface > > : : iterator result_it = result . begin ( ) + 1 ;
std : : vector < Handle_Geom_Surface > : : const_iterator input_it = surfaces . begin ( ) + 1 ;
2015-10-07 17:33:30 +02:00
for ( ; input_it ! = surfaces . end ( ) - 1 ; + + result_it , + + input_it ) {
result_it - > push_back ( * input_it ) ;
}
}
2021-03-13 10:05:33 +01:00
const double total_thickness = std : : accumulate ( thicknesses . begin ( ) , thicknesses . end ( ) , 0. ) ;
2015-10-07 17:33:30 +02:00
gp_Pnt own_axis_start , own_axis_end ;
find_wall_end_points ( wall , own_axis_start , own_axis_end ) ;
2019-08-16 10:42:48 +02:00
// Sometimes duplicate IfcRelConnectsPathElements exist. These are detected
// and the counts of connections are decremented accordingly.
2015-10-07 17:33:30 +02:00
for ( int idx = 0 ; idx < 2 ; + + idx ) {
if ( connection_type_count [ idx ] < = 1 ) {
continue ;
}
2019-08-21 11:59:30 +02:00
2016-05-02 15:56:08 +02:00
/*
2017-12-18 15:25:09 +01:00
IfcSchema::IfcConnectionTypeEnum::Value connection_type = idx == 1
2015-10-07 17:33:30 +02:00
? IfcSchema::IfcConnectionTypeEnum::IfcConnectionType_ATSTART
: IfcSchema::IfcConnectionTypeEnum::IfcConnectionType_ATEND;
2016-05-02 15:56:08 +02:00
*/
2015-10-07 17:33:30 +02:00
std : : set < const IfcSchema : : IfcProduct * > others ;
2016-04-09 17:54:23 +02:00
endpoint_connections_t : : iterator it = endpoint_connections . begin ( ) ;
2015-10-07 17:33:30 +02:00
while ( it ! = endpoint_connections . end ( ) ) {
const IfcSchema : : IfcProduct * other = it - > second ;
if ( others . find ( other ) ! = others . end ( ) ) {
it = endpoint_connections . erase ( it ) ;
2019-08-21 11:59:30 +02:00
- - connection_type_count [ idx ] ;
2015-10-07 17:33:30 +02:00
} else {
others . insert ( other ) ;
+ + it ;
}
}
2019-08-21 11:59:30 +02:00
}
2015-10-07 17:33:30 +02:00
2019-08-21 11:59:30 +02:00
// Check whether the end points are of the wall are really ~1 LayerThickness away from each other
/*
for (endpoint_connections_t::const_iterator it = endpoint_connections.begin(); it != endpoint_connections.end(); ++it) {
IfcSchema::IfcConnectionTypeEnum::Value own_type = it->first.first;
IfcSchema::IfcConnectionTypeEnum::Value other_type = it->first.second;
2015-10-07 17:33:30 +02:00
2019-08-21 11:59:30 +02:00
gp_Pnt other_axis_start, other_axis_end;
find_wall_end_points(it->second->as<IfcSchema::IfcWall>(), other_axis_start, other_axis_end);
2015-10-07 17:33:30 +02:00
2019-08-21 11:59:30 +02:00
gp_Trsf other;
if (!convert(it->second->ObjectPlacement(), other)) {
continue;
}
2015-10-07 17:33:30 +02:00
2019-08-21 11:59:30 +02:00
other.Transforms(other_axis_start.ChangeCoord());
local.Transforms(other_axis_start.ChangeCoord());
other.Transforms(other_axis_end.ChangeCoord());
local.Transforms(other_axis_end.ChangeCoord());
2015-10-07 17:33:30 +02:00
2019-08-21 11:59:30 +02:00
const gp_Pnt& a = own_type == IfcSchema::IfcConnectionTypeEnum::IfcConnectionType_ATSTART
? own_axis_start
: own_axis_end;
2015-10-07 17:33:30 +02:00
2019-08-21 11:59:30 +02:00
const gp_Pnt& b = other_type == IfcSchema::IfcConnectionTypeEnum::IfcConnectionType_ATSTART
? other_axis_start
: other_axis_end;
2015-10-07 17:33:30 +02:00
2019-08-21 11:59:30 +02:00
const double d = a.Distance(b);
2015-10-07 17:33:30 +02:00
}
2019-08-21 11:59:30 +02:00
*/
2015-10-07 17:33:30 +02:00
2021-03-13 10:05:33 +01:00
const double length_required = endpoint_connections . size ( ) * total_thickness ;
// @todo this is not precisely the distance in case of curved walls. Also, it's safer
// to first reproject the body onto the axis to get the precise curve parametrization
// range. It's only a safeguard though, so can probably be approximated.
const double axis_length = own_axis_start . Distance ( own_axis_end ) ;
if ( length_required > axis_length ) {
Logger : : Warning ( " The wall axis is not long enough to accomodate the fold points " ) ;
return false ;
}
2015-10-07 17:33:30 +02:00
for ( endpoint_connections_t : : const_iterator it = endpoint_connections . begin ( ) ; it ! = endpoint_connections . end ( ) ; + + it ) {
2017-12-18 15:25:09 +01:00
IfcSchema : : IfcConnectionTypeEnum : : Value connection_type = it - > first . first ;
2015-10-07 17:33:30 +02:00
// If more than one wall connects to this start/end -point assume layers do not need to be folded
const int idx = connection_type = = IfcSchema : : IfcConnectionTypeEnum : : IfcConnectionType_ATSTART ;
if ( connection_type_count [ idx ] > 1 ) continue ;
2019-08-16 10:42:48 +02:00
// Pick the corresponding point from the axis
2015-10-07 17:33:30 +02:00
const gp_Pnt & own_end_point = connection_type = = IfcSchema : : IfcConnectionTypeEnum : : IfcConnectionType_ATEND
? own_axis_end
: own_axis_start ;
const IfcSchema : : IfcProduct * other_wall = it - > second ;
gp_Trsf other ;
if ( ! convert ( other_wall - > ObjectPlacement ( ) , other ) ) {
2018-11-28 14:18:58 +01:00
Logger : : Error ( " Failed to convert placement " , other_wall ) ;
2015-10-07 17:33:30 +02:00
continue ;
}
IfcSchema : : IfcRepresentation * axis_representation = find_representation ( other_wall , " Axis " ) ;
2018-11-20 12:10:44 +01:00
if ( ! axis_representation ) {
2018-11-28 14:18:58 +01:00
Logger : : Warning ( " Joined wall has no axis representation " , other_wall ) ;
2018-11-20 12:10:44 +01:00
continue ;
}
2015-10-07 17:33:30 +02:00
IfcRepresentationShapeItems axis_items ;
{
Kernel temp = * this ;
temp . setValue ( GV_DIMENSIONALITY , - 1. ) ;
temp . convert_shapes ( axis_representation , axis_items ) ;
}
TopoDS_Shape axis_shape ;
flatten_shape_list ( axis_items , axis_shape , false ) ;
2016-05-03 11:01:47 +02:00
// local and other are IfcLocalPlacements and therefore have a unit
// scale factor that can be applied by means of TopoDS_Shape::Move()
2015-10-07 17:33:30 +02:00
axis_shape . Move ( other ) ;
axis_shape . Move ( local ) ;
TopoDS_Shape body_shape ;
flatten_shape_list ( items , body_shape , false ) ;
2019-08-16 10:42:48 +02:00
// Create a single paremetric range over a single curve
// that represents the entire 1d domain of the other wall
// Sometimes there are multiple edges in the Axis shape
// but it is assumed these are colinear.
Handle_Geom_Curve other_axis_curve ;
2015-10-07 17:33:30 +02:00
double axis_u1 , axis_u2 ;
{
TopExp_Explorer exp ( axis_shape , TopAbs_EDGE ) ;
if ( ! exp . More ( ) ) {
return false ;
}
TopoDS_Edge axis_edge = TopoDS : : Edge ( exp . Current ( ) ) ;
2019-08-16 10:42:48 +02:00
other_axis_curve = BRep_Tool : : Curve ( axis_edge , axis_u1 , axis_u2 ) ;
2015-10-07 17:33:30 +02:00
gp_Pnt other_a_1 , other_a_2 ;
2019-08-16 10:42:48 +02:00
other_axis_curve - > D0 ( axis_u1 , other_a_1 ) ;
other_axis_curve - > D0 ( axis_u2 , other_a_2 ) ;
2015-10-07 17:33:30 +02:00
if ( axis_u2 < axis_u1 ) {
std : : swap ( axis_u1 , axis_u2 ) ;
}
exp . Next ( ) ;
for ( ; exp . More ( ) ; exp . Next ( ) ) {
2016-05-02 15:56:08 +02:00
TopoDS_Edge axis_edge2 = TopoDS : : Edge ( exp . Current ( ) ) ;
TopExp_Explorer exp2 ( axis_edge2 , TopAbs_VERTEX ) ;
2015-10-07 17:33:30 +02:00
for ( ; exp2 . More ( ) ; exp2 . Next ( ) ) {
gp_Pnt p = BRep_Tool : : Pnt ( TopoDS : : Vertex ( exp2 . Current ( ) ) ) ;
gp_Pnt pp ;
double u , d ;
2019-08-16 10:42:48 +02:00
if ( project ( other_axis_curve , p , pp , u , d ) ) {
2015-10-07 17:33:30 +02:00
if ( u < axis_u1 ) axis_u1 = u ;
if ( u > axis_u2 ) axis_u2 = u ;
}
}
}
}
double layer_offset = 0 ;
2019-08-21 11:59:30 +02:00
2015-10-07 17:33:30 +02:00
std : : vector < double > : : const_iterator thickness = thicknesses . begin ( ) ;
result_t : : iterator result_vector = result . begin ( ) + 1 ;
2019-08-16 10:42:48 +02:00
// nb The first layer is never folded, because it corresponds
// to one of the longitudonal faces of the wall. Hence the +1
2015-10-07 17:33:30 +02:00
for ( surfaces_t : : const_iterator jt = surfaces . begin ( ) + 1 ; jt ! = surfaces . end ( ) - 1 ; + + jt , + + result_vector ) {
layer_offset + = * thickness + + ;
2020-11-25 11:02:57 +01:00
bool found_intersection = false , parallel = false ;
2015-10-07 17:33:30 +02:00
boost : : optional < gp_Pnt > point_outside_param_range ;
const Handle_Geom_Surface & surface = * jt ;
2019-08-16 10:42:48 +02:00
// Find the intersection point between the layerset surface
// and the other axis curve. If it's within the parametric
// range of the other wall it means the walls are connected
// with an angle.
GeomAPI_IntCS intersections ( other_axis_curve , surface ) ;
2015-10-07 17:33:30 +02:00
if ( intersections . IsDone ( ) & & intersections . NbPoints ( ) = = 1 ) {
const gp_Pnt & p = intersections . Point ( 1 ) ;
2020-11-25 11:02:57 +01:00
2015-10-07 17:33:30 +02:00
double u , v , w ;
intersections . Parameters ( 1 , u , v , w ) ;
2020-11-25 11:02:57 +01:00
gp_Pnt Pc , Ps ;
gp_Vec Vc , Vs1 , Vs2 ;
other_axis_curve - > D1 ( w , Pc , Vc ) ;
surface - > D1 ( u , v , Ps , Vs1 , Vs2 ) ;
Vs1 . Cross ( Vs2 ) ;
2021-03-13 10:05:33 +01:00
if ( Vs1 . IsNormal ( Vc , 1.e-5 ) ) {
2020-11-25 11:02:57 +01:00
Logger : : Warning ( " Connected walls are parallel " ) ;
parallel = true ;
} else if ( w < axis_u1 | | w > axis_u2 ) {
2015-10-07 17:33:30 +02:00
point_outside_param_range = p ;
} else {
// Found an intersection. Layer end point is covered by connecting wall
found_intersection = true ;
break ;
}
}
2020-11-25 11:02:57 +01:00
if ( ! parallel & & ! found_intersection & & point_outside_param_range ) {
2015-10-07 17:33:30 +02:00
/*
Is there a bug in Open Cascade related to the intersection
of offset surfaces constructed from linear extrusions?
Handle_Geom_Surface xy = new Geom_Plane(gp::Origin(), gp::DZ());
// Handle_Geom_Surface yz = new Geom_Plane(gp::Origin(), gp::DX());
// Handle_Geom_Surface yz2 = new Geom_OffsetSurface(yz, 1.);
Handle_Geom_Curve ln = new Geom_Line(gp::Origin(), gp::DX());
Handle_Geom_Surface yz = new Geom_SurfaceOfLinearExtrusion(ln, gp::DZ());
Handle_Geom_Surface yz2 = new Geom_OffsetSurface(yz, 1.);
intersect(xy, yz2);
*/
Handle_Geom_Surface plane = new Geom_Plane ( * point_outside_param_range , gp : : DZ ( ) ) ;
2019-08-16 10:42:48 +02:00
// vertical edges at wall end point face.
2015-10-07 17:33:30 +02:00
curves_on_surfaces_t layer_ends ;
intersect ( surface , body_shape , layer_ends ) ;
Handle_Geom_Curve layer_body_intersection ;
Handle_Geom_Surface body_surface ;
double mind = std : : numeric_limits < double > : : infinity ( ) ;
2016-05-02 15:56:08 +02:00
for ( curves_on_surfaces_t : : const_iterator kt = layer_ends . begin ( ) ; kt ! = layer_ends . end ( ) ; + + kt ) {
2015-10-07 17:33:30 +02:00
gp_Pnt p ;
gp_Vec v ;
double u , d ;
2016-05-02 15:56:08 +02:00
kt - > second - > D1 ( 0. , p , v ) ;
2015-10-07 17:33:30 +02:00
if ( ALMOST_THE_SAME ( 0. , v . Dot ( gp : : DZ ( ) ) ) ) {
// Filter horizontal curves
continue ;
}
2019-08-16 10:42:48 +02:00
// Find vertical wall end point edge closest to end point associated with semantic connection
2016-05-02 15:56:08 +02:00
if ( project ( kt - > second , own_end_point , p , u , d ) ) {
2019-08-21 11:59:30 +02:00
// In addition to closest, there is a length threshold based on thickness.
// @todo ideally, first, the point closest to end-point is selected, and
// after that the parallel check is performed. But threshold probably
// functions good enough.
if ( d < total_thickness * 3 & & d < mind ) {
2019-08-16 10:42:48 +02:00
GeomAdaptor_Curve GAC ( other_axis_curve ) ;
GeomAdaptor_Surface GAS ( kt - > first ) ;
Extrema_ExtCS x ( GAC , GAS , getValue ( GV_PRECISION ) , getValue ( GV_PRECISION ) ) ;
if ( x . IsParallel ( ) ) {
body_surface = kt - > first ;
layer_body_intersection = kt - > second ;
mind = d ;
}
2015-10-07 17:33:30 +02:00
}
}
}
2019-08-16 10:42:48 +02:00
if ( body_surface . IsNull ( ) ) {
continue ;
}
// Intersect vertical edge with ground plane for point.
2015-10-07 17:33:30 +02:00
GeomAPI_IntCS intersection2 ( layer_body_intersection , plane ) ;
if ( intersection2 . IsDone ( ) & & intersection2 . NbPoints ( ) = = 1 ) {
const gp_Pnt & layer_end_point = intersection2 . Point ( 1 ) ;
2019-08-16 10:42:48 +02:00
// Intersect layerset surface with ground plane
2015-10-07 17:33:30 +02:00
GeomAPI_IntSS intersection3 ( surface , plane , 1.e-7 ) ;
if ( intersection3 . IsDone ( ) & & intersection3 . NbLines ( ) = = 1 ) {
Handle_Geom_Curve layer_line = intersection3 . Line ( 1 ) ;
GeomAdaptor_Curve layer_line_adaptor ( layer_line ) ;
ShapeAnalysis_Curve sac ;
gp_Pnt layer_end_point_projected ; double layer_end_point_param ;
sac . Project ( layer_line , layer_end_point , 1e-3 , layer_end_point_projected , layer_end_point_param , false ) ;
2019-08-16 10:42:48 +02:00
// Move point inwards by distance from other layerset
2015-10-07 17:33:30 +02:00
GCPnts_AbscissaPoint dst ( layer_line_adaptor , layer_offset , layer_end_point_param ) ;
if ( dst . IsDone ( ) ) {
2019-08-16 10:42:48 +02:00
// Convert parameter to point
2015-10-07 17:33:30 +02:00
gp_Pnt layer_fold_point ;
layer_line - > D0 ( dst . Parameter ( ) , layer_fold_point ) ;
GeomAPI_IntSS intersection4 ( body_surface , plane , 1.e-7 ) ;
if ( intersection4 . IsDone ( ) & & intersection4 . NbLines ( ) = = 1 ) {
Handle_Geom_Curve body_trim_curve = intersection4 . Line ( 1 ) ;
ShapeAnalysis_Curve sac2 ;
gp_Pnt layer_fold_point_projected ; double layer_fold_point_param ;
sac2 . Project ( body_trim_curve , layer_fold_point , 1.e-7 , layer_fold_point_projected , layer_fold_point_param , false ) ;
Handle_Geom_Curve fold_curve = new Geom_OffsetCurve ( body_trim_curve - > Reversed ( ) , layer_fold_point_projected . Distance ( layer_fold_point ) , gp : : DZ ( ) ) ;
Handle_Geom_Surface fold_surface = new Geom_SurfaceOfLinearExtrusion ( fold_curve , gp : : DZ ( ) ) ;
result_vector - > push_back ( fold_surface ) ;
folds_made = true ;
}
}
}
}
}
}
}
return folds_made ;
}
2018-12-19 16:10:33 +01:00
namespace {
2020-09-20 11:12:57 +02:00
void subshapes ( const TopoDS_Shape & in , std : : list < TopoDS_Shape > & out ) {
TopoDS_Iterator sit ( in ) ;
for ( ; sit . More ( ) ; sit . Next ( ) ) {
out . push_back ( sit . Value ( ) ) ;
}
}
2018-12-19 16:10:33 +01:00
# if OCC_VERSION_HEX >= 0x70200
bool split ( IfcGeom : : Kernel & , const TopoDS_Shape & input , const TopTools_ListOfShape & operands , double eps , std : : vector < TopoDS_Shape > & slices ) {
if ( operands . Extent ( ) < 2 ) {
// Needs to have at least two cutting surfaces for the ordering based on surface containment to work.
return false ;
}
BRepAlgoAPI_Splitter split ;
TopTools_ListOfShape input_list ;
input_list . Append ( input ) ;
split . SetArguments ( input_list ) ;
split . SetTools ( operands ) ;
split . SetNonDestructive ( true ) ;
split . SetFuzzyValue ( eps ) ;
split . Build ( ) ;
if ( ! split . IsDone ( ) ) {
return false ;
} else {
std : : map < Geom_Surface * , int > surfaces ;
// NB 1, since first surface has been excluded
int i = 1 ;
for ( TopTools_ListIteratorOfListOfShape it ( operands ) ; it . More ( ) ; it . Next ( ) , + + i ) {
TopExp_Explorer exp ( it . Value ( ) , TopAbs_FACE ) ;
for ( ; exp . More ( ) ; exp . Next ( ) ) {
surfaces . insert ( std : : make_pair ( BRep_Tool : : Surface ( TopoDS : : Face ( exp . Current ( ) ) ) . get ( ) , i ) ) ;
}
}
2020-09-20 11:12:57 +02:00
auto result_shape = split . Shape ( ) ;
std : : list < TopoDS_Shape > subs ;
subshapes ( result_shape , subs ) ;
2020-11-02 16:03:50 +01:00
if ( subs . size ( ) = = 1 & & operands . Size ( ) - 2 > ( int ) subs . size ( ) & & ( subs . front ( ) . ShapeType ( ) = = TopAbs_COMPSOLID | | subs . front ( ) . ShapeType ( ) = = TopAbs_COMPOUND ) ) {
2020-09-20 11:12:57 +02:00
auto s = subs . front ( ) ;
subs . clear ( ) ;
subshapes ( s , subs ) ;
2018-12-19 16:10:33 +01:00
}
// Initialize storage
2020-09-20 11:12:57 +02:00
slices . resize ( subs . size ( ) ) ;
2018-12-19 16:10:33 +01:00
2020-09-20 11:12:57 +02:00
for ( auto & s : subs ) {
2018-12-19 16:10:33 +01:00
// Iterate over the faces of solid to find correspondence to original
// splitting surfaces. For the outmost slices, there will be a single
// corresponding surface, because the outmost surfaces that align with
// the body geometry have not been added as operands. For intermediate
// slices, two surface indices should be find that should be next to
// each other in the array of input surfaces.
2020-09-20 11:12:57 +02:00
TopExp_Explorer exp ( s , TopAbs_FACE ) ;
2018-12-19 16:10:33 +01:00
int min = std : : numeric_limits < int > : : max ( ) ;
int max = std : : numeric_limits < int > : : min ( ) ;
for ( ; exp . More ( ) ; exp . Next ( ) ) {
auto ssrf = BRep_Tool : : Surface ( TopoDS : : Face ( exp . Current ( ) ) ) ;
auto it = surfaces . find ( ssrf . get ( ) ) ;
if ( it ! = surfaces . end ( ) ) {
if ( it - > second < min ) {
min = it - > second ;
}
if ( it - > second > max ) {
max = it - > second ;
}
}
}
int idx = std : : numeric_limits < int > : : max ( ) ;
if ( min ! = std : : numeric_limits < int > : : max ( ) ) {
if ( min = = 1 & & max = = 1 ) {
idx = 0 ;
} else if ( min + 1 = = max | | min = = max ) {
idx = min ;
}
}
2018-12-30 12:50:04 +01:00
if ( idx < ( int ) slices . size ( ) ) {
2018-12-19 16:10:33 +01:00
if ( slices [ idx ] . IsNull ( ) ) {
2020-09-20 11:12:57 +02:00
slices [ idx ] = s ;
2018-12-19 16:10:33 +01:00
continue ;
}
}
Logger : : Error ( " Unable to map layer geometry to material index " ) ;
return false ;
}
}
return true ;
}
# else
bool split ( IfcGeom : : Kernel & k , const TopoDS_Shape & input , const TopTools_ListOfShape & operands , double , std : : vector < TopoDS_Shape > & slices ) {
TopTools_ListIteratorOfListOfShape it ( operands ) ;
TopoDS_Shape i = input ;
for ( ; it . More ( ) ; it . Next ( ) ) {
const TopoDS_Shape & s = it . Value ( ) ;
TopoDS_Shape a , b ;
Handle ( Geom_Surface ) surf ;
if ( s . ShapeType ( ) = = TopAbs_FACE ) {
surf = BRep_Tool : : Surface ( TopoDS : : Face ( s ) ) ;
}
if ( ( s . ShapeType ( ) = = TopAbs_FACE & & k . split_solid_by_surface ( i , surf , a , b ) ) | |
( s . ShapeType ( ) = = TopAbs_SHELL & & k . split_solid_by_shell ( i , s , a , b ) ) )
{
slices . push_back ( b ) ;
i = a ;
} else {
return false ;
}
}
slices . push_back ( i ) ;
return true ;
}
# endif
}
2016-05-02 15:56:08 +02:00
bool IfcGeom : : Kernel : : apply_folded_layerset ( const IfcRepresentationShapeItems & items , const std : : vector < std : : vector < Handle_Geom_Surface > > & surfaces , const std : : vector < const SurfaceStyle * > & styles , IfcRepresentationShapeItems & result ) {
2015-10-07 17:33:30 +02:00
Bnd_Box bb ;
TopoDS_Shape input ;
flatten_shape_list ( items , input , false ) ;
typedef std : : vector < std : : vector < Handle_Geom_Surface > > folded_surfaces_t ;
typedef std : : vector < std : : pair < TopoDS_Face , std : : pair < gp_Pnt , gp_Pnt > > > faces_with_mass_t ;
2018-12-19 16:10:33 +01:00
TopTools_ListOfShape shells ;
2015-10-07 17:33:30 +02:00
for ( folded_surfaces_t : : const_iterator it = surfaces . begin ( ) ; it ! = surfaces . end ( ) ; + + it ) {
if ( it - > empty ( ) ) {
continue ;
} else if ( it - > size ( ) = = 1 ) {
const Handle_Geom_Surface & surface = ( * it ) [ 0 ] ;
double u1 , v1 , u2 , v2 ;
if ( ! project ( surface , input , u1 , v1 , u2 , v2 ) ) {
continue ;
}
2018-12-19 16:10:33 +01:00
shells . Append ( BRepBuilderAPI_MakeShell ( surface , u1 , v1 , u2 , v2 ) . Shell ( ) ) ;
2015-10-07 17:33:30 +02:00
} else {
faces_with_mass_t solids ;
for ( folded_surfaces_t : : value_type : : const_iterator jt = it - > begin ( ) ; jt ! = it - > end ( ) ; + + jt ) {
const Handle_Geom_Surface & surface = * jt ;
double u1 , v1 , u2 , v2 ;
if ( ! project ( surface , input , u1 , v1 , u2 , v2 ) ) {
continue ;
}
TopoDS_Face face = BRepBuilderAPI_MakeFace ( surface , u1 , u2 , v1 , v2 , 1.e-7 ) . Face ( ) ;
gp_Pnt p , p1 , p2 ; gp_Vec vu , vv , n ;
surface - > D1 ( ( u1 + u2 ) / 2. , ( v1 + v2 ) / 2. , p , vu , vv ) ;
n = vu ^ vv ;
p1 = p . Translated ( n ) ;
p2 = p . Translated ( - n ) ;
solids . push_back ( std : : make_pair ( face , std : : make_pair ( p1 , p2 ) ) ) ;
}
if ( solids . empty ( ) ) {
continue ;
}
faces_with_mass_t : : iterator jt = solids . begin ( ) ;
TopoDS_Face & A = jt - > first ;
TopoDS_Shape An = BRepPrimAPI_MakeHalfSpace ( A , jt - > second . second ) . Solid ( ) ;
for ( + + jt ; jt ! = solids . end ( ) ; + + jt ) {
TopoDS_Face & B = jt - > first ;
TopoDS_Shape Bn = BRepPrimAPI_MakeHalfSpace ( B , jt - > second . second ) . Solid ( ) ;
TopoDS_Shape a = BRepAlgoAPI_Cut ( A , Bn ) ;
if ( count ( a , TopAbs_FACE ) = = 1 ) {
A = TopoDS : : Face ( TopExp_Explorer ( a , TopAbs_FACE ) . Current ( ) ) ;
}
TopoDS_Shape b = BRepAlgoAPI_Cut ( B , An ) ;
if ( count ( b , TopAbs_FACE ) = = 1 ) {
B = TopoDS : : Face ( TopExp_Explorer ( b , TopAbs_FACE ) . Current ( ) ) ;
}
}
BRepOffsetAPI_Sewing builder ;
2016-05-02 15:56:08 +02:00
for ( faces_with_mass_t : : const_iterator kt = solids . begin ( ) ; kt ! = solids . end ( ) ; + + kt ) {
builder . Add ( kt - > first ) ;
2015-10-07 17:33:30 +02:00
}
builder . Perform ( ) ;
2020-11-25 11:02:57 +01:00
TopoDS_Shape s = builder . SewedShape ( ) ;
if ( s . ShapeType ( ) = = TopAbs_SHELL ) {
shells . Append ( TopoDS : : Shell ( s ) ) ;
} else {
Logger : : Error ( " Expected shell type in layerset processing " ) ;
return false ;
}
2015-10-07 17:33:30 +02:00
}
}
2018-12-19 16:10:33 +01:00
if ( shells . Extent ( ) = = 0 ) {
2015-10-07 17:33:30 +02:00
return false ;
2018-12-19 16:10:33 +01:00
} else if ( shells . Extent ( ) = = 1 ) {
2015-10-07 17:33:30 +02:00
for ( IfcRepresentationShapeItems : : const_iterator it = items . begin ( ) ; it ! = items . end ( ) ; + + it ) {
TopoDS_Shape a , b ;
2018-12-19 16:10:33 +01:00
if ( split_solid_by_shell ( it - > Shape ( ) , shells . First ( ) , a , b ) ) {
2018-12-10 11:33:54 +01:00
result . push_back ( IfcRepresentationShapeItem ( it - > ItemId ( ) , it - > Placement ( ) , b , styles [ 0 ] ? styles [ 0 ] : & it - > Style ( ) ) ) ;
result . push_back ( IfcRepresentationShapeItem ( it - > ItemId ( ) , it - > Placement ( ) , a , styles [ 1 ] ? styles [ 1 ] : & it - > Style ( ) ) ) ;
2015-10-07 17:33:30 +02:00
} else {
continue ;
}
}
return true ;
} else {
for ( IfcRepresentationShapeItems : : const_iterator it = items . begin ( ) ; it ! = items . end ( ) ; + + it ) {
2018-12-19 16:10:33 +01:00
2015-10-07 17:33:30 +02:00
const TopoDS_Shape & s = it - > Shape ( ) ;
TopoDS_Solid sld ;
ensure_fit_for_subtraction ( s , sld ) ;
2018-12-19 16:10:33 +01:00
std : : vector < TopoDS_Shape > slices ;
if ( split ( * this , it - > Shape ( ) , shells , getValue ( GV_PRECISION ) , slices ) & & slices . size ( ) = = styles . size ( ) ) {
for ( size_t i = 0 ; i < slices . size ( ) ; + + i ) {
2018-12-21 11:53:36 +01:00
result . push_back ( IfcRepresentationShapeItem ( it - > ItemId ( ) , it - > Placement ( ) , slices [ i ] , styles [ i ] ? styles [ i ] : & it - > Style ( ) ) ) ;
2015-10-07 17:33:30 +02:00
}
2018-12-19 16:10:33 +01:00
} else {
return false ;
2015-10-07 17:33:30 +02:00
}
}
return true ;
}
}
2016-05-02 15:56:08 +02:00
bool IfcGeom : : Kernel : : apply_layerset ( const IfcRepresentationShapeItems & items , const std : : vector < Handle_Geom_Surface > & surfaces , const std : : vector < const SurfaceStyle * > & styles , IfcRepresentationShapeItems & result ) {
2015-10-07 17:33:30 +02:00
if ( surfaces . size ( ) < 3 ) {
return false ;
} else if ( surfaces . size ( ) = = 3 ) {
for ( IfcRepresentationShapeItems : : const_iterator it = items . begin ( ) ; it ! = items . end ( ) ; + + it ) {
TopoDS_Shape a , b ;
if ( split_solid_by_surface ( it - > Shape ( ) , surfaces [ 1 ] , a , b ) ) {
2018-12-10 11:33:54 +01:00
result . push_back ( IfcRepresentationShapeItem ( it - > ItemId ( ) , it - > Placement ( ) , b , styles [ 0 ] ? styles [ 0 ] : & it - > Style ( ) ) ) ;
result . push_back ( IfcRepresentationShapeItem ( it - > ItemId ( ) , it - > Placement ( ) , a , styles [ 1 ] ? styles [ 1 ] : & it - > Style ( ) ) ) ;
2015-10-07 17:33:30 +02:00
} else {
continue ;
}
}
return true ;
} else {
/*
// Determine whether sequence of surfaces is consistent with surface normal, so that
// layer operations are applied in the correct order. This seems to be always the case.
Bnd_Box bb;
for (IfcRepresentationShapeItems::const_iterator it = items.begin(); it != items.end(); ++it) {
BRepBndLib::Add(it->Shape(), bb);
}
double x1, y1, z1, x2, y2, z2;
bb.Get(x1, y1, z1, x2, y2, z2);
gp_Pnt p1(x1, y1, z1);
gp_Pnt p2(x2, y2, z2);
gp_Pnt avg = (p1.XYZ() + p2.XYZ()) / 2.;
ShapeAnalysis_Surface sas1(surfaces[0]);
ShapeAnalysis_Surface sas2(surfaces[1]);
const gp_Pnt2d uv = sas1.ValueOfUV(avg, 1e-3);
gp_Pnt ps1, ps2, mass;
gp_Vec du1, dv1, du2, dv2;
surfaces[0]->D1(uv.X(), uv.Y(), ps1, du1, dv1);
const gp_Vec n1 = dv1.XYZ() ^ du1.XYZ();
const bool reversed = gp_Dir(ps2.XYZ() - ps1.XYZ()).Dot(n1) < 0.;
surfaces[surfaces.size() - 1]->D0(uv.X(), uv.Y(), mass);
mass.ChangeCoord() += n1.XYZ();
*/
for ( IfcRepresentationShapeItems : : const_iterator it = items . begin ( ) ; it ! = items . end ( ) ; + + it ) {
2018-12-19 16:10:33 +01:00
2015-10-07 17:33:30 +02:00
const TopoDS_Shape & s = it - > Shape ( ) ;
TopoDS_Solid sld ;
ensure_fit_for_subtraction ( s , sld ) ;
2018-12-19 16:10:33 +01:00
TopTools_ListOfShape operands ;
for ( unsigned i = 1 ; i < surfaces . size ( ) - 1 ; + + i ) {
double u1 , v1 , u2 , v2 ;
if ( ! project ( surfaces [ i ] , sld , u1 , v1 , u2 , v2 ) ) {
return false ;
2015-10-07 17:33:30 +02:00
}
2018-12-19 16:10:33 +01:00
TopoDS_Face face = BRepBuilderAPI_MakeFace ( surfaces [ i ] , u1 , u2 , v1 , v2 , 1.e-7 ) . Face ( ) ;
operands . Append ( face ) ;
2015-10-07 17:33:30 +02:00
}
2018-12-19 16:10:33 +01:00
std : : vector < TopoDS_Shape > slices ;
if ( split ( * this , it - > Shape ( ) , operands , getValue ( GV_PRECISION ) , slices ) & & slices . size ( ) = = styles . size ( ) ) {
for ( size_t i = 0 ; i < slices . size ( ) ; + + i ) {
2018-12-21 11:53:36 +01:00
result . push_back ( IfcRepresentationShapeItem ( it - > ItemId ( ) , it - > Placement ( ) , slices [ i ] , styles [ i ] ? styles [ i ] : & it - > Style ( ) ) ) ;
2018-12-19 16:10:33 +01:00
}
} else {
return false ;
2015-10-07 17:33:30 +02:00
}
}
return true ;
}
}
IfcSchema : : IfcRepresentation * IfcGeom : : Kernel : : find_representation ( const IfcSchema : : IfcProduct * product , const std : : string & identifier ) {
if ( ! product - > hasRepresentation ( ) ) return 0 ;
IfcSchema : : IfcProductRepresentation * prod_rep = product - > Representation ( ) ;
IfcSchema : : IfcRepresentation : : list : : ptr reps = prod_rep - > Representations ( ) ;
for ( IfcSchema : : IfcRepresentation : : list : : it it = reps - > begin ( ) ; it ! = reps - > end ( ) ; + + it ) {
if ( ( * * it ) . hasRepresentationIdentifier ( ) & & ( * * it ) . RepresentationIdentifier ( ) = = identifier ) {
return * it ;
}
}
return 0 ;
}
bool IfcGeom : : Kernel : : split_solid_by_surface ( const TopoDS_Shape & input , const Handle_Geom_Surface & surface , TopoDS_Shape & front , TopoDS_Shape & back ) {
// Use an unbounded surface, that isolate part of the input shape,
// to split this shape into two parts. Make sure that the addition
// of the two result volumes matches that of the input.
double u1 , v1 , u2 , v2 ;
if ( ! project ( surface , input , u1 , v1 , u2 , v2 ) ) {
return false ;
}
TopoDS_Face face = BRepBuilderAPI_MakeFace ( surface , u1 , u2 , v1 , v2 , 1.e-7 ) . Face ( ) ;
gp_Pnt p , p1 , p2 ; gp_Vec vu , vv , n ;
surface - > D1 ( ( u1 + u2 ) / 2. , ( v1 + v2 ) / 2. , p , vu , vv ) ;
n = vu ^ vv ;
p1 = p . Translated ( - n ) ;
TopoDS_Solid solid = BRepPrimAPI_MakeHalfSpace ( face , p1 ) . Solid ( ) ;
const bool b = split_solid_by_shell ( input , solid , front , back ) ;
return b ;
}
bool IfcGeom : : Kernel : : split_solid_by_shell ( const TopoDS_Shape & input , const TopoDS_Shape & shell , TopoDS_Shape & front , TopoDS_Shape & back ) {
// Use a shell, typically one or more connected faces, that isolate part
// of the input shape, to split this shape into two parts. Make sure that
// the addition of the two result volumes matches that of the input.
TopoDS_Solid solid ;
if ( shell . ShapeType ( ) = = TopAbs_SHELL ) {
solid = BRepBuilderAPI_MakeSolid ( TopoDS : : Shell ( shell ) ) . Solid ( ) ;
} else if ( shell . ShapeType ( ) = = TopAbs_SOLID ) {
solid = TopoDS : : Solid ( shell ) ;
} else {
return false ;
}
2016-09-01 15:13:48 +02:00
apply_tolerance ( solid , getValue ( GV_PRECISION ) ) ;
2015-10-07 17:33:30 +02:00
2018-05-16 10:11:00 -03:00
# if OCC_VERSION_HEX >= 0x70300
2018-05-14 13:08:27 -03:00
TopTools_ListOfShape shapes ;
2018-05-16 10:11:00 -03:00
# else
2015-10-07 17:33:30 +02:00
BOPCol_ListOfShape shapes ;
2018-05-16 10:11:00 -03:00
# endif
2015-10-07 17:33:30 +02:00
shapes . Append ( input ) ;
shapes . Append ( solid ) ;
BOPAlgo_PaveFiller filler ( new NCollection_IncAllocator ) ; // TODO: Does this need to be freed?
filler . SetArguments ( shapes ) ;
filler . Perform ( ) ;
front = BRepAlgoAPI_Cut ( input , solid , filler ) ;
back = BRepAlgoAPI_Common ( input , solid , filler ) ;
2016-09-01 15:13:48 +02:00
bool is_null [ 2 ] ;
2015-10-07 17:33:30 +02:00
for ( int i = 0 ; i < 2 ; + + i ) {
TopoDS_Shape & shape = i = = 0 ? front : back ;
2016-10-09 13:39:47 +03:00
const bool result_is_null = is_null [ i ] = shape . IsNull ( ) ! = 0 ;
2016-09-01 15:13:48 +02:00
if ( result_is_null ) {
continue ;
}
2015-10-07 17:33:30 +02:00
try {
ShapeFix_Shape fix ( shape ) ;
if ( fix . Perform ( ) ) {
shape = fix . Shape ( ) ;
}
2017-08-01 16:45:11 +02:00
} catch ( const Standard_Failure & e ) {
if ( e . GetMessageString ( ) & & strlen ( e . GetMessageString ( ) ) ) {
Logger : : Error ( e . GetMessageString ( ) ) ;
} else {
Logger : : Error ( " Unknown error performing fixes " ) ;
}
} catch ( . . . ) {
Logger : : Error ( " Unknown error performing fixes " ) ;
}
2015-10-07 17:33:30 +02:00
BRepCheck_Analyzer analyser ( shape ) ;
bool is_valid = analyser . IsValid ( ) ! = 0 ;
if ( ! is_valid ) {
return false ;
}
}
2016-09-01 15:13:48 +02:00
if ( is_null [ 0 ] | | is_null [ 1 ] ) {
Logger : : Message ( Logger : : LOG_ERROR , " Null result obtained from layerset slicing " ) ;
if ( is_null [ 0 ] & & is_null [ 1 ] ) {
return false ;
}
}
2015-10-07 17:33:30 +02:00
const double ab = shape_volume ( input ) ;
const double a = shape_volume ( front ) ;
const double b = shape_volume ( back ) ;
return ALMOST_THE_SAME ( ab , a + b , 1.e-3 ) ;
}
bool IfcGeom : : Kernel : : project ( const Handle_Geom_Surface & srf , const TopoDS_Shape & shp , double & u1 , double & v1 , double & u2 , double & v2 , double widen ) {
2018-12-19 15:15:00 +01:00
// @todo std::unique_ptr for C++11
ShapeAnalysis_Surface * sas = 0 ;
Handle ( Geom_Plane ) pln ;
if ( srf - > DynamicType ( ) = = STANDARD_TYPE ( Geom_Plane ) ) {
// Optimize projection for specific cases
pln = Handle ( Geom_Plane ) : : DownCast ( srf ) ;
} else if ( srf - > DynamicType ( ) = = STANDARD_TYPE ( Geom_OffsetSurface ) & & Handle ( Geom_OffsetSurface ) : : DownCast ( srf ) - > BasisSurface ( ) - > DynamicType ( ) = = STANDARD_TYPE ( Geom_Plane ) ) {
// For an offset planar surface the projected UV coords are the same as the basis surface
pln = Handle ( Geom_Plane ) : : DownCast ( Handle ( Geom_OffsetSurface ) : : DownCast ( srf ) - > BasisSurface ( ) ) ;
} else {
sas = new ShapeAnalysis_Surface ( srf ) ;
}
2015-10-07 17:33:30 +02:00
u1 = v1 = + std : : numeric_limits < double > : : infinity ( ) ;
u2 = v2 = - std : : numeric_limits < double > : : infinity ( ) ;
gp_Pnt median ;
int vertex_count = 0 ;
for ( TopExp_Explorer exp ( shp , TopAbs_VERTEX ) ; exp . More ( ) ; exp . Next ( ) , + + vertex_count ) {
gp_Pnt p = BRep_Tool : : Pnt ( TopoDS : : Vertex ( exp . Current ( ) ) ) ;
median . ChangeCoord ( ) + = p . XYZ ( ) ;
2018-12-19 15:15:00 +01:00
gp_Pnt2d uv ;
if ( sas ) {
uv = sas - > ValueOfUV ( p , 1e-3 ) ;
} else {
gp_Vec d = p . XYZ ( ) - pln - > Position ( ) . Location ( ) . XYZ ( ) ;
uv . SetX ( d . Dot ( pln - > Position ( ) . XDirection ( ) ) ) ;
uv . SetY ( d . Dot ( pln - > Position ( ) . YDirection ( ) ) ) ;
}
2015-10-07 17:33:30 +02:00
if ( uv . X ( ) < u1 ) u1 = uv . X ( ) ;
if ( uv . Y ( ) < v1 ) v1 = uv . Y ( ) ;
if ( uv . X ( ) > u2 ) u2 = uv . X ( ) ;
if ( uv . Y ( ) > v2 ) v2 = uv . Y ( ) ;
}
2018-12-19 15:15:00 +01:00
if ( vertex_count > 0 ) {
2015-10-07 17:33:30 +02:00
2018-12-19 15:15:00 +01:00
// Add a little bit of resolution so that the median is shifted towards the mass
// of the curve. This helps to find the parameter ordering for conic surfaces.
for ( TopExp_Explorer exp ( shp , TopAbs_EDGE ) ; exp . More ( ) ; exp . Next ( ) , + + vertex_count ) {
const TopoDS_Edge & e = TopoDS : : Edge ( exp . Current ( ) ) ;
double a , b ;
Handle_Geom_Curve crv = BRep_Tool : : Curve ( e , a , b ) ;
gp_Pnt p ;
crv - > D0 ( ( a + b ) / 2. , p ) ;
median . ChangeCoord ( ) + = p . XYZ ( ) ;
}
median . ChangeCoord ( ) . Divide ( vertex_count ) ;
gp_Pnt2d uv ;
if ( sas ) {
uv = sas - > ValueOfUV ( median , 1e-3 ) ;
} else {
gp_Vec d = median . XYZ ( ) - pln - > Position ( ) . Location ( ) . XYZ ( ) ;
uv . SetX ( d . Dot ( pln - > Position ( ) . XDirection ( ) ) ) ;
uv . SetY ( d . Dot ( pln - > Position ( ) . YDirection ( ) ) ) ;
}
if ( uv . X ( ) < u1 | | uv . X ( ) > u2 ) {
std : : swap ( u1 , u2 ) ;
}
u1 - = widen ;
u2 + = widen ;
v1 - = widen ;
v2 + = widen ;
2015-10-07 17:33:30 +02:00
}
2018-12-19 15:15:00 +01:00
delete sas ;
return vertex_count > 0 ;
2016-05-02 15:13:30 +02:00
}
2016-01-22 17:27:28 +01:00
const IfcSchema : : IfcRepresentationItem * IfcGeom : : Kernel : : find_item_carrying_style ( const IfcSchema : : IfcRepresentationItem * item ) {
2016-02-03 22:34:50 +02:00
if ( item - > StyledByItem ( ) - > size ( ) ) {
2016-01-22 17:27:28 +01:00
return item ;
}
2020-01-15 11:50:44 +01:00
while ( item - > declaration ( ) . is ( IfcSchema : : IfcBooleanResult : : Class ( ) ) ) {
2016-01-22 17:27:28 +01:00
// All instantiations of IfcBooleanOperand (type of FirstOperand) are subtypes of
// IfcGeometricRepresentationItem
2020-01-15 11:50:44 +01:00
item = ( IfcSchema : : IfcGeometricRepresentationItem * ) ( ( IfcSchema : : IfcBooleanResult * ) item ) - > FirstOperand ( ) ;
2016-02-03 22:34:50 +02:00
if ( item - > StyledByItem ( ) - > size ( ) ) {
2016-01-22 17:27:28 +01:00
return item ;
}
}
// TODO: Ideally this would be done for other entities (such as IfcCsgSolid) as well.
// But neither are these very prevalent, nor does the current IfcOpenShell style
// mechanism enable to conveniently style subshapes, which would be necessary for
// distinctly styled union operands.
return item ;
2016-02-03 22:34:50 +02:00
}
2016-03-18 11:23:25 +01:00
bool IfcGeom : : Kernel : : is_identity_transform ( IfcUtil : : IfcBaseClass * l ) {
IfcSchema : : IfcAxis2Placement2D * ax2d ;
IfcSchema : : IfcAxis2Placement3D * ax3d ;
IfcSchema : : IfcCartesianTransformationOperator2D * op2d ;
IfcSchema : : IfcCartesianTransformationOperator3D * op3d ;
IfcSchema : : IfcCartesianTransformationOperator2DnonUniform * op2dnonu ;
IfcSchema : : IfcCartesianTransformationOperator3DnonUniform * op3dnonu ;
if ( ( op2dnonu = l - > as < IfcSchema : : IfcCartesianTransformationOperator2DnonUniform > ( ) ) ! = 0 ) {
gp_GTrsf2d gtrsf2d ;
convert ( op2dnonu , gtrsf2d ) ;
return gtrsf2d . Form ( ) = = gp_Identity ;
} else if ( ( op2d = l - > as < IfcSchema : : IfcCartesianTransformationOperator2D > ( ) ) ! = 0 ) {
gp_Trsf2d trsf2d ;
convert ( op2d , trsf2d ) ;
return trsf2d . Form ( ) = = gp_Identity ;
} else if ( ( op3dnonu = l - > as < IfcSchema : : IfcCartesianTransformationOperator3DnonUniform > ( ) ) ! = 0 ) {
gp_GTrsf gtrsf ;
convert ( op3dnonu , gtrsf ) ;
return gtrsf . Form ( ) = = gp_Identity ;
} else if ( ( op3d = l - > as < IfcSchema : : IfcCartesianTransformationOperator3D > ( ) ) ! = 0 ) {
gp_Trsf trsf ;
convert ( op3d , trsf ) ;
return trsf . Form ( ) = = gp_Identity ;
} else if ( ( ax2d = l - > as < IfcSchema : : IfcAxis2Placement2D > ( ) ) ! = 0 ) {
gp_Trsf2d trsf2d ;
convert ( ax2d , trsf2d ) ;
return trsf2d . Form ( ) = = gp_Identity ;
} else if ( ( ax3d = l - > as < IfcSchema : : IfcAxis2Placement3D > ( ) ) ! = 0 ) {
gp_Trsf trsf ;
convert ( ax3d , trsf ) ;
return trsf . Form ( ) = = gp_Identity ;
} else {
throw IfcParse : : IfcException ( " Invalid valuation for IfcAxis2Placement / IfcCartesianTransformationOperator " ) ;
}
}
2019-04-09 17:03:24 +02:00
bool IfcGeom : : Kernel : : approximate_plane_through_wire ( const TopoDS_Wire & wire , gp_Pln & plane , double eps ) {
2016-04-16 14:47:09 +02:00
// Newell's Method is used for the normal calculation
// as a simple edge cross product can give opposite results
// for a concave face boundary.
// Reference: Graphics Gems III p. 231
2019-04-09 17:03:24 +02:00
const double eps_ = eps < 1. ? getValue ( GV_PRECISION ) : eps ;
const double eps2 = eps_ * eps_ ;
2016-04-16 14:47:09 +02:00
double x = 0 , y = 0 , z = 0 ;
gp_Pnt current , previous , first ;
gp_XYZ center ;
int n = 0 ;
BRepTools_WireExplorer exp ( wire ) ;
for ( ; ; exp . Next ( ) ) {
2016-09-01 12:28:17 +03:00
const bool has_more = exp . More ( ) ! = 0 ;
2016-04-16 14:47:09 +02:00
if ( has_more ) {
const TopoDS_Vertex & v = exp . CurrentVertex ( ) ;
current = BRep_Tool : : Pnt ( v ) ;
center + = current . XYZ ( ) ;
} else {
current = first ;
}
if ( n ) {
const double & xn = previous . X ( ) ;
const double & yn = previous . Y ( ) ;
const double & zn = previous . Z ( ) ;
const double & xn1 = current . X ( ) ;
const double & yn1 = current . Y ( ) ;
const double & zn1 = current . Z ( ) ;
x + = ( yn - yn1 ) * ( zn + zn1 ) ;
y + = ( xn + xn1 ) * ( zn - zn1 ) ;
z + = ( xn - xn1 ) * ( yn + yn1 ) ;
} else {
first = current ;
}
if ( ! has_more ) {
break ;
}
previous = current ;
+ + n ;
}
if ( n < 3 ) {
return false ;
}
2019-04-09 17:03:24 +02:00
2016-04-16 14:47:09 +02:00
plane = gp_Pln ( center / n , gp_Dir ( x , y , z ) ) ;
2019-04-09 17:03:24 +02:00
exp . Init ( wire ) ;
for ( ; exp . More ( ) ; exp . Next ( ) ) {
const TopoDS_Vertex & v = exp . CurrentVertex ( ) ;
current = BRep_Tool : : Pnt ( v ) ;
if ( plane . SquareDistance ( current ) > eps2 ) {
return false ;
}
}
2016-04-16 14:47:09 +02:00
return true ;
}
bool IfcGeom : : Kernel : : flatten_wire ( TopoDS_Wire & wire ) {
gp_Pln pln ;
if ( ! approximate_plane_through_wire ( wire , pln ) ) {
return false ;
}
TopoDS_Face face = BRepBuilderAPI_MakeFace ( pln ) . Face ( ) ;
BRepAlgo_NormalProjection proj ( face ) ;
proj . Add ( wire ) ;
proj . Build ( ) ;
if ( ! proj . IsDone ( ) ) {
return false ;
}
TopTools_ListOfShape list ;
proj . BuildWire ( list ) ;
if ( list . Extent ( ) ! = 1 ) {
return false ;
}
wire = TopoDS : : Wire ( list . First ( ) ) ;
return true ;
2016-04-24 15:22:10 +02:00
}
2016-05-03 11:01:47 +02:00
2019-05-17 12:31:23 +02:00
bool IfcGeom : : Kernel : : triangulate_wire ( const std : : vector < TopoDS_Wire > & wires , TopTools_ListOfShape & faces ) {
2017-11-27 16:27:36 +01:00
// This is a bit of a precarious approach, but seems to work for the
// versions of OCCT tested for. OCCT has a Delaunay triangulation function
// BRepMesh_Delaun, but it is notoriously hard to interpret the results
// (due to the Bowyer-Watson super triangle perhaps?). Therefore
// alternatively we use the regular OCCT incremental mesher on a new face
// created from the UV coordinates of the original wire. Pray to our gods
// that the vertex coordinates are unaffected by the meshing algorithm and
2019-04-24 16:32:06 +02:00
// map them back to 3d coordinates when iterating over the mesh triangles.
// In addition, to maintain a manifold shell, we need to make sure that
// every edge from the input wire is used exactly once in the list of
// resulting faces. And that other internal edges are used twice.
2017-11-27 16:27:36 +01:00
typedef std : : pair < double , double > uv_node ;
gp_Pln pln ;
2019-05-17 12:31:23 +02:00
if ( ! approximate_plane_through_wire ( wires . front ( ) , pln , std : : numeric_limits < double > : : infinity ( ) ) ) {
2017-11-27 16:27:36 +01:00
return false ;
}
const gp_XYZ & udir = pln . Position ( ) . XDirection ( ) . XYZ ( ) ;
const gp_XYZ & vdir = pln . Position ( ) . YDirection ( ) . XYZ ( ) ;
const gp_XYZ & pnt = pln . Position ( ) . Location ( ) . XYZ ( ) ;
2019-05-17 12:31:23 +02:00
2019-04-24 16:32:06 +02:00
std : : map < uv_node , TopoDS_Vertex > mapping ;
std : : map < std : : pair < uv_node , uv_node > , TopoDS_Edge > existing_edges , new_edges ;
2017-11-27 16:27:36 +01:00
2019-05-17 12:31:23 +02:00
std : : unique_ptr < BRepBuilderAPI_MakeFace > mf ;
for ( auto it = wires . begin ( ) ; it ! = wires . end ( ) ; + + it ) {
const TopoDS_Wire & wire = * it ;
BRepTools_WireExplorer exp ( wire ) ;
BRepBuilderAPI_MakePolygon mp ;
// Add UV coordinates to a newly created polygon
for ( ; exp . More ( ) ; exp . Next ( ) ) {
// Project onto plane
const TopoDS_Vertex & V = exp . CurrentVertex ( ) ;
gp_Pnt p = BRep_Tool : : Pnt ( V ) ;
double u = ( p . XYZ ( ) - pnt ) . Dot ( udir ) ;
double v = ( p . XYZ ( ) - pnt ) . Dot ( vdir ) ;
mp . Add ( gp_Pnt ( u , v , 0. ) ) ;
mapping . insert ( std : : make_pair ( std : : make_pair ( u , v ) , V ) ) ;
// Store existing edges in a map so that triangles can
// actually reference the preexisting edges.
const TopoDS_Edge & e = exp . Current ( ) ;
TopoDS_Vertex V0 , V1 ;
TopExp : : Vertices ( e , V0 , V1 , true ) ;
gp_Pnt p0 = BRep_Tool : : Pnt ( V0 ) ;
gp_Pnt p1 = BRep_Tool : : Pnt ( V1 ) ;
double u0 = ( p0 . XYZ ( ) - pnt ) . Dot ( udir ) ;
double v0 = ( p0 . XYZ ( ) - pnt ) . Dot ( vdir ) ;
double u1 = ( p1 . XYZ ( ) - pnt ) . Dot ( udir ) ;
double v1 = ( p1 . XYZ ( ) - pnt ) . Dot ( vdir ) ;
uv_node uv0 = std : : make_pair ( u0 , v0 ) ;
uv_node uv1 = std : : make_pair ( u1 , v1 ) ;
existing_edges . insert ( std : : make_pair ( std : : make_pair ( uv0 , uv1 ) , e ) ) ;
existing_edges . insert ( std : : make_pair ( std : : make_pair ( uv1 , uv0 ) , TopoDS : : Edge ( e . Reversed ( ) ) ) ) ;
}
// Not closed by default
mp . Close ( ) ;
if ( mf ) {
if ( it - 1 = = wires . begin ( ) ) {
// @todo is this necessary?
TopoDS_Face f = mf - > Face ( ) ;
mf - > Init ( f ) ;
}
mf - > Add ( mp . Wire ( ) ) ;
} else {
mf . reset ( new BRepBuilderAPI_MakeFace ( mp . Wire ( ) ) ) ;
}
}
const TopoDS_Face & face = mf - > Face ( ) ;
// Create a triangular mesh from the face
2017-11-27 16:27:36 +01:00
BRepMesh_IncrementalMesh ( face , Precision : : Confusion ( ) ) ;
int n123 [ 3 ] ;
TopLoc_Location loc ;
Handle_Poly_Triangulation tri = BRep_Tool : : Triangulation ( face , loc ) ;
2019-04-24 16:32:06 +02:00
2017-11-27 16:27:36 +01:00
if ( ! tri . IsNull ( ) ) {
const TColgp_Array1OfPnt & nodes = tri - > Nodes ( ) ;
const Poly_Array1OfTriangle & triangles = tri - > Triangles ( ) ;
for ( int i = 1 ; i < = triangles . Length ( ) ; + + i ) {
if ( face . Orientation ( ) = = TopAbs_REVERSED )
triangles ( i ) . Get ( n123 [ 2 ] , n123 [ 1 ] , n123 [ 0 ] ) ;
else triangles ( i ) . Get ( n123 [ 0 ] , n123 [ 1 ] , n123 [ 2 ] ) ;
// Create polygons from the mesh vertices
2019-04-24 16:32:06 +02:00
BRepBuilderAPI_MakeWire mp2 ;
2017-11-27 16:27:36 +01:00
for ( int j = 0 ; j < 3 ; + + j ) {
2019-04-24 16:32:06 +02:00
uv_node uvnodes [ 2 ] ;
TopoDS_Vertex vs [ 2 ] ;
2017-11-27 16:27:36 +01:00
2019-04-24 16:32:06 +02:00
for ( int k = 0 ; k < 2 ; + + k ) {
const gp_Pnt & uv = nodes . Value ( n123 [ ( j + k ) % 3 ] ) ;
uvnodes [ k ] = std : : make_pair ( uv . X ( ) , uv . Y ( ) ) ;
auto it = mapping . find ( uvnodes [ k ] ) ;
if ( it = = mapping . end ( ) ) {
Logger : : Error ( " Internal error: unable to unproject uv-mesh " ) ;
return false ;
}
vs [ k ] = it - > second ;
2017-11-27 16:27:36 +01:00
}
2019-04-24 16:32:06 +02:00
auto it = existing_edges . find ( std : : make_pair ( uvnodes [ 0 ] , uvnodes [ 1 ] ) ) ;
if ( it ! = existing_edges . end ( ) ) {
// This is a boundary edge, reuse existing edge from wire
mp2 . Add ( it - > second ) ;
} else {
auto jt = new_edges . find ( std : : make_pair ( uvnodes [ 0 ] , uvnodes [ 1 ] ) ) ;
if ( jt ! = new_edges . end ( ) ) {
// We have already added the reverse as part of another
// triangle, reuse this edge.
mp2 . Add ( TopoDS : : Edge ( jt - > second ) ) ;
} else {
// This is a new internal edge. Register the reverse
// for reuse later. We need to be sure to reuse vertices
// for the edge construction because otherwise the wire
// builder will use geometrical proximity for vertex
// connections in which case the edge will be copied
// and no longer partner with other edges from the shell.
TopoDS_Edge ne = BRepBuilderAPI_MakeEdge ( vs [ 0 ] , vs [ 1 ] ) ;
mp2 . Add ( ne ) ;
// Store the reverse to be picked up later.
new_edges . insert ( std : : make_pair ( std : : make_pair ( uvnodes [ 1 ] , uvnodes [ 0 ] ) , TopoDS : : Edge ( ne . Reversed ( ) ) ) ) ;
}
}
2017-11-27 16:27:36 +01:00
}
2019-05-17 12:31:23 +02:00
BRepBuilderAPI_MakeFace mft ( mp2 . Wire ( ) ) ;
if ( mft . IsDone ( ) ) {
TopoDS_Face triangle_face = mft . Face ( ) ;
2017-11-27 16:27:36 +01:00
TopoDS_Iterator jt ( triangle_face , false ) ;
for ( ; jt . More ( ) ; jt . Next ( ) ) {
const TopoDS_Wire & w = TopoDS : : Wire ( jt . Value ( ) ) ;
2019-05-17 12:31:23 +02:00
if ( w . Orientation ( ) ! = wires . front ( ) . Orientation ( ) ) {
2017-11-27 16:27:36 +01:00
triangle_face . Reverse ( ) ;
}
}
faces . Append ( triangle_face ) ;
2019-04-24 16:32:06 +02:00
} else {
Logger : : Error ( " Internal error: missing face " ) ;
return false ;
}
}
}
TopTools_IndexedDataMapOfShapeListOfShape mape , mapn ;
2019-05-17 12:31:23 +02:00
for ( auto & wire : wires ) {
TopExp : : MapShapesAndAncestors ( wire , TopAbs_EDGE , TopAbs_WIRE , mape ) ;
}
2019-04-24 16:32:06 +02:00
TopTools_ListIteratorOfListOfShape it ( faces ) ;
for ( ; it . More ( ) ; it . Next ( ) ) {
TopExp : : MapShapesAndAncestors ( it . Value ( ) , TopAbs_EDGE , TopAbs_WIRE , mapn ) ;
}
// Validation
for ( int i = 1 ; i < = mape . Extent ( ) ; + + i ) {
2019-04-30 11:54:56 +02:00
# if OCC_VERSION_HEX >= 0x70000
2019-04-24 16:32:06 +02:00
TopTools_ListOfShape val ;
if ( ! mapn . FindFromKey ( mape . FindKey ( i ) , val ) ) {
2019-04-30 11:54:56 +02:00
# else
bool contains = false ;
try {
TopTools_ListOfShape val = mapn . FindFromKey ( mape . FindKey ( i ) ) ;
contains = true ;
} catch ( Standard_NoSuchObject & ) { }
if ( ! contains ) {
# endif
2019-04-24 16:32:06 +02:00
// All existing edges need to exist in the new faces
Logger : : Error ( " Internal error, missing edge from triangulation " ) ;
if ( faceset_helper_ ! = nullptr ) {
faceset_helper_ - > non_manifold ( ) = true ;
}
}
}
for ( int i = 1 ; i < = mapn . Extent ( ) ; + + i ) {
const TopoDS_Shape & v = mapn . FindKey ( i ) ;
int n = mapn . FindFromIndex ( i ) . Extent ( ) ;
// Existing edges are boundaries with use 1
// New edges are internal with use 2
if ( n ! = ( mape . Contains ( v ) ? 1 : 2 ) ) {
Logger : : Error ( " Internal error, non-manifold result from triangulation " ) ;
if ( faceset_helper_ ! = nullptr ) {
faceset_helper_ - > non_manifold ( ) = true ;
2017-11-27 16:27:36 +01:00
}
}
}
return true ;
}
2016-05-03 11:01:47 +02:00
TopoDS_Shape IfcGeom : : Kernel : : apply_transformation ( const TopoDS_Shape & s , const gp_Trsf & t ) {
if ( t . Form ( ) = = gp_Identity ) {
return s ;
} else {
/// @todo set to 1. and exactly 1. or use epsilon?
if ( t . ScaleFactor ( ) ! = 1. ) {
return BRepBuilderAPI_Transform ( s , t , true ) ;
} else {
return s . Moved ( t ) ;
}
}
}
TopoDS_Shape IfcGeom : : Kernel : : apply_transformation ( const TopoDS_Shape & s , const gp_GTrsf & t ) {
if ( t . Form ( ) = = gp_Other ) {
return BRepBuilderAPI_GTransform ( s , t , true ) ;
} else {
return apply_transformation ( s , t . Trsf ( ) ) ;
}
}
2018-04-18 11:33:57 +02:00
namespace {
/*
* A small helper utility to wrap around a numeric range
*/
class bounded_int {
private :
int i ;
size_t n ;
public :
bounded_int ( int i , size_t n ) : i ( i ) , n ( n ) { }
bounded_int & operator - - ( ) {
- - i ;
if ( i = = - 1 ) {
i = n - 1 ;
}
return * this ;
}
bounded_int & operator + + ( ) {
+ + i ;
2018-05-05 16:09:12 +02:00
if ( i = = ( int ) n ) {
2018-04-18 11:33:57 +02:00
i = 0 ;
}
return * this ;
}
operator int ( ) { return i ; }
} ;
}
bool IfcGeom : : Kernel : : wire_intersections ( const TopoDS_Wire & wire , TopTools_ListOfShape & wires ) {
2021-02-06 09:38:38 -06:00
if ( getValue ( GV_NO_WIRE_INTERSECTION_CHECK ) > 0. ) {
return false ;
}
2018-04-18 11:33:57 +02:00
if ( ! wire . Closed ( ) ) {
wires . Append ( wire ) ;
return false ;
}
int n = count ( wire , TopAbs_EDGE ) ;
2018-09-12 13:26:11 +02:00
if ( n < 3 ) {
2018-04-18 11:33:57 +02:00
wires . Append ( wire ) ;
return false ;
}
// Note: initialize empty
Handle ( ShapeExtend_WireData ) wd = new ShapeExtend_WireData ( ) ;
// ... to be sure to get consecutive edges
BRepTools_WireExplorer exp ( wire ) ;
2018-09-12 13:26:11 +02:00
IfcGeom : : impl : : tree < int > tree ;
int edge_idx = 0 ;
2018-04-18 11:33:57 +02:00
for ( ; exp . More ( ) ; exp . Next ( ) ) {
wd - > Add ( exp . Current ( ) ) ;
2018-09-12 13:26:11 +02:00
if ( n > 64 ) {
// tfk: indices in tree are 0-based vd 1-based in wiredata
tree . add ( edge_idx + + , exp . Current ( ) ) ;
}
2018-04-18 11:33:57 +02:00
}
2018-09-17 14:59:15 +02:00
if ( wd - > NbEdges ( ) ! = n ) {
// If the number of edges differs, BRepTools_WireExplorer did not
// reach every edge, probably due to loops exactly at vertex locations.
// This is not supported by this algorithm which only elimates loops
// due to edge crossings.
throw geometry_exception ( " Invalid loop " ) ;
2018-04-18 11:33:57 +02:00
}
bool intersected = false ;
// tfk: Extrema on infinite curves proved to be more robust.
// TopoDS_Face face = BRepBuilderAPI_MakeFace(wire, true).Face();
// ShapeAnalysis_Wire saw(wd, face, getValue(GV_PRECISION));
2018-09-12 13:26:11 +02:00
2021-02-08 14:06:05 -06:00
double eps = 0 ;
if ( getValue ( GV_NO_WIRE_INTERSECTION_TOLERANCE ) < 0. ) {
eps = faceset_helper_
// eps is added to both ends of the parametric domain, so 3. is chosen to be on the safe side here.
? ( faceset_helper_ - > epsilon ( ) / 3. )
// @todo re-evaluate 2. here for the reasons above:
: ( std : : min ) ( min_edge_length ( wire ) / 2. , getValue ( GV_PRECISION ) * 10. ) ;
}
2018-09-12 13:26:11 +02:00
2018-04-18 11:33:57 +02:00
for ( int i = 2 ; i < n ; + + i ) {
2018-09-12 13:26:11 +02:00
std : : vector < int > js ;
if ( n > 64 ) {
Bnd_Box b ;
BRepBndLib : : Add ( wd - > Edge ( i + 1 ) , b ) ;
b . Enlarge ( eps ) ;
js = tree . select_box ( b , false ) ;
} else {
boost : : push_back ( js , boost : : irange ( 0 , i - 1 ) ) ;
}
for ( std : : vector < int > : : const_iterator it = js . begin ( ) ; it ! = js . end ( ) ; + + it ) {
int j = * it ;
if ( n > 64 ) {
if ( j > i ) {
continue ;
}
if ( ( std : : max ) ( i , j ) - ( std : : min ) ( i , j ) < = 1 ) {
continue ;
}
}
// Only check non-consecutive edges
if ( i = = n - 1 & & j = = 0 ) continue ;
2018-04-18 11:33:57 +02:00
double u11 , u12 , u21 , u22 , U1 , U2 ;
GeomAPI_ExtremaCurveCurve ecc (
BRep_Tool : : Curve ( wd - > Edge ( i + 1 ) , u11 , u12 ) ,
BRep_Tool : : Curve ( wd - > Edge ( j + 1 ) , u21 , u22 )
) ;
2018-09-21 10:22:24 +02:00
// @todo: extend this to work in case of multiple extrema and curved segments.
2020-03-27 13:26:34 +01:00
const bool unbounded_intersects = ( ! ecc . Extrema ( ) . IsParallel ( ) & & ecc . NbExtrema ( ) = = 1 & & ecc . Distance ( 1 ) < eps ) ;
2018-11-02 16:10:06 +01:00
if ( unbounded_intersects ) {
2018-04-18 11:33:57 +02:00
ecc . Parameters ( 1 , U1 , U2 ) ;
2018-11-02 16:10:06 +01:00
if ( u11 > u12 ) {
std : : swap ( u11 , u12 ) ;
}
if ( u21 > u22 ) {
std : : swap ( u21 , u22 ) ;
}
2018-04-18 11:33:57 +02:00
2018-11-02 16:10:06 +01:00
/// @todo: tfk: probably need different thresholds on non-linear curves
u11 - = eps ;
u12 + = eps ;
u21 - = eps ;
u22 + = eps ;
2018-04-18 11:33:57 +02:00
2018-11-02 16:10:06 +01:00
// tfk: code below is for ShapeAnalysis_Wire::CheckIntersectingEdges()
// IntRes2d_SequenceOfIntersectionPoint points2d;
// TColgp_SequenceOfPnt points3d;
// TColStd_SequenceOfReal errors;
// if (saw.CheckIntersectingEdges(i + 1, j + 1, points2d, points3d, errors)) {
2018-04-18 11:33:57 +02:00
2018-11-02 16:10:06 +01:00
if ( u11 < U1 & & U1 < u12 & & u21 < U2 & & U2 < u22 ) {
2018-04-18 11:33:57 +02:00
2018-11-02 16:10:06 +01:00
intersected = true ;
2018-04-18 11:33:57 +02:00
2018-11-02 16:10:06 +01:00
// Explore a forward and backward cycle from the intersection point
for ( int fb = 0 ; fb < = 1 ; + + fb ) {
const bool forward = fb = = 0 ;
2018-04-18 11:33:57 +02:00
2018-11-02 16:10:06 +01:00
BRepBuilderAPI_MakeWire mw ;
bool first = true ;
2018-04-18 11:33:57 +02:00
2018-11-02 16:10:06 +01:00
for ( bounded_int k ( j , n ) ; ; ) {
bool intersecting = k = = j | | k = = i ;
if ( intersecting ) {
TopoDS_Edge e = wd - > Edge ( k + 1 ) ;
2018-04-18 11:33:57 +02:00
2018-11-02 16:10:06 +01:00
TopoDS_Vertex v1 , v2 ;
2018-12-24 16:03:20 +01:00
TopExp : : Vertices ( e , v1 , v2 , true ) ;
2018-11-02 16:10:06 +01:00
const TopoDS_Vertex * v = first = = forward ? & v2 : & v1 ;
// gp_Pnt p2 = points3d.Value(1);
gp_Pnt p1 = BRep_Tool : : Pnt ( * v ) ;
gp_Pnt pp1 , pp2 ;
ecc . Points ( 1 , pp1 , pp2 ) ;
const gp_Pnt & p2 = k = = i ? pp1 : pp2 ;
// Substitute with a new edge from/to the intersection point
if ( p1 . Distance ( p2 ) > getValue ( GV_PRECISION ) * 2 ) {
double _ , __ ;
Handle_Geom_Curve crv = BRep_Tool : : Curve ( e , _ , __ ) ;
BRepBuilderAPI_MakeEdge me ( crv , p1 , p2 ) ;
TopoDS_Edge ed = me . Edge ( ) ;
mw . Add ( ed ) ;
}
first = false ;
} else {
// Re-use original edge
mw . Add ( wd - > Edge ( k + 1 ) ) ;
2018-04-18 11:33:57 +02:00
}
2018-11-02 16:10:06 +01:00
if ( k = = i ) {
break ;
}
2018-04-18 11:33:57 +02:00
2018-11-02 16:10:06 +01:00
if ( forward ) {
+ + k ;
} else {
- - k ;
}
2018-04-18 11:33:57 +02:00
}
2018-11-02 16:10:06 +01:00
// Recursively process both cuts
wire_intersections ( mw . Wire ( ) , wires ) ;
2018-04-18 11:33:57 +02:00
}
2018-11-02 16:10:06 +01:00
return true ;
2018-04-18 11:33:57 +02:00
}
}
}
}
// No intersections found, append original wire
if ( ! intersected ) {
wires . Append ( wire ) ;
}
return intersected ;
}
void IfcGeom : : Kernel : : select_largest ( const TopTools_ListOfShape & shapes , TopoDS_Shape & largest ) {
double mass = 0. ;
TopTools_ListIteratorOfListOfShape it ( shapes ) ;
for ( ; it . More ( ) ; it . Next ( ) ) {
/*
// tfk: bounding box is more efficient probably
const TopoDS_Wire& w = TopoDS::Wire(it.Value());
TopoDS_Face face = BRepBuilderAPI_MakeFace(w).Face();
const double m = face_area(face);
*/
Bnd_Box bb ;
BRepBndLib : : AddClose ( it . Value ( ) , bb ) ;
double xyz_min [ 3 ] , xyz_max [ 3 ] ;
bb . Get ( xyz_min [ 0 ] , xyz_min [ 1 ] , xyz_min [ 2 ] , xyz_max [ 0 ] , xyz_max [ 1 ] , xyz_max [ 2 ] ) ;
const double eps = getValue ( GV_PRECISION ) ;
double m = 1. ;
for ( int i = 0 ; i < 3 ; + + i ) {
if ( Precision : : IsNegativeInfinite ( xyz_min [ i ] ) ) {
xyz_min [ i ] = 0. ;
}
if ( Precision : : IsInfinite ( xyz_max [ i ] ) ) {
xyz_max [ i ] = 0. ;
}
m * = ( xyz_max [ i ] + eps ) - ( xyz_min [ i ] - eps ) ;
}
if ( m > mass ) {
mass = m ;
largest = it . Value ( ) ;
}
}
}
2018-08-20 11:46:26 +02:00
bool IfcGeom : : Kernel : : fit_halfspace ( const TopoDS_Shape & a , const TopoDS_Shape & b , TopoDS_Shape & box , double & height ) {
TopExp_Explorer exp ( b , TopAbs_FACE ) ;
if ( ! exp . More ( ) ) {
return false ;
}
TopoDS_Face face = TopoDS : : Face ( exp . Current ( ) ) ;
exp . Next ( ) ;
if ( exp . More ( ) ) {
return false ;
}
Handle ( Geom_Surface ) surf = BRep_Tool : : Surface ( face ) ;
// const gp_XYZ xyz = a.Location().Transformation().TranslationPart();
// std::cout << "dz " << xyz.Z() << std::endl;
if ( surf - > DynamicType ( ) ! = STANDARD_TYPE ( Geom_Plane ) ) {
return false ;
}
Bnd_Box bb ;
BRepBndLib : : Add ( a , bb ) ;
2018-09-17 15:21:12 +02:00
if ( bb . IsVoid ( ) ) {
return false ;
}
2018-08-20 11:46:26 +02:00
double xs [ 2 ] , ys [ 2 ] , zs [ 2 ] ;
bb . Get ( xs [ 0 ] , ys [ 0 ] , zs [ 0 ] , xs [ 1 ] , ys [ 1 ] , zs [ 1 ] ) ;
gp_Pln pln = Handle ( Geom_Plane ) : : DownCast ( surf ) - > Pln ( ) ;
gp_Pnt P = pln . Position ( ) . Location ( ) ;
gp_Vec z = pln . Position ( ) . Direction ( ) ;
gp_Vec x = pln . Position ( ) . XDirection ( ) ;
gp_Vec y = pln . Position ( ) . YDirection ( ) ;
if ( face . Orientation ( ) ! = TopAbs_REVERSED ) {
z . Reverse ( ) ;
}
double D , Umin , Umax , Vmin , Vmax ;
D = 0. ;
Umin = Vmin = + std : : numeric_limits < double > : : infinity ( ) ;
Umax = Vmax = - std : : numeric_limits < double > : : infinity ( ) ;
for ( int i = 0 ; i < 2 ; + + i ) {
for ( int j = 0 ; j < 2 ; + + j ) {
for ( int k = 0 ; k < 2 ; + + k ) {
gp_Pnt p ( xs [ i ] , ys [ j ] , zs [ k ] ) ;
gp_Vec d = p . XYZ ( ) - P . XYZ ( ) ;
const double u = d . Dot ( x ) ;
const double v = d . Dot ( y ) ;
const double w = d . Dot ( z ) ;
if ( w > D ) {
D = w ;
}
if ( u < Umin ) {
Umin = u ;
}
if ( u > Umax ) {
Umax = u ;
}
if ( v < Vmin ) {
Vmin = v ;
}
if ( v > Vmax ) {
Vmax = v ;
}
}
}
}
2018-11-21 11:28:22 +01:00
const double eps = getValue ( GV_PRECISION ) * 1000. ;
2018-08-20 11:46:26 +02:00
BRepBuilderAPI_MakePolygon poly ;
2018-11-21 11:28:22 +01:00
poly . Add ( P . XYZ ( ) + x . XYZ ( ) * ( Umin - eps ) + y . XYZ ( ) * ( Vmin - eps ) ) ;
poly . Add ( P . XYZ ( ) + x . XYZ ( ) * ( Umax + eps ) + y . XYZ ( ) * ( Vmin - eps ) ) ;
2018-08-20 11:46:26 +02:00
poly . Add ( P . XYZ ( ) + x . XYZ ( ) * ( Umax + eps ) + y . XYZ ( ) * ( Vmax + eps ) ) ;
2018-11-21 11:28:22 +01:00
poly . Add ( P . XYZ ( ) + x . XYZ ( ) * ( Umin - eps ) + y . XYZ ( ) * ( Vmax + eps ) ) ;
2018-08-20 11:46:26 +02:00
poly . Close ( ) ;
BRepBuilderAPI_MakeFace mf ( surf , poly . Wire ( ) , true ) ;
gp_Vec vec = gp_Vec ( z . XYZ ( ) * ( D + eps ) ) ;
BRepPrimAPI_MakePrism mp ( mf . Face ( ) , vec ) ;
box = mp . Shape ( ) ;
height = D ;
return true ;
}
2017-02-16 15:54:34 +01:00
# if OCC_VERSION_HEX < 0x60900
bool IfcGeom : : Kernel : : boolean_operation ( const TopoDS_Shape & a , const TopTools_ListOfShape & b , BOPAlgo_Operation op , TopoDS_Shape & result ) {
result = a ;
TopTools_ListIteratorOfListOfShape it ( b ) ;
for ( ; it . More ( ) ; it . Next ( ) ) {
TopoDS_Shape r ;
if ( ! boolean_operation ( result , it . Value ( ) , op , r ) ) {
return false ;
}
result = r ;
}
return true ;
}
bool IfcGeom : : Kernel : : boolean_operation ( const TopoDS_Shape & a , const TopoDS_Shape & b , BOPAlgo_Operation op , TopoDS_Shape & result ) {
bool succesful = true ;
BRepAlgoAPI_BooleanOperation * builder ;
if ( op = = BOPAlgo_CUT ) {
builder = new BRepAlgoAPI_Cut ( a , b ) ;
} else if ( op = = BOPAlgo_COMMON ) {
builder = new BRepAlgoAPI_Common ( a , b ) ;
} else if ( op = = BOPAlgo_FUSE ) {
builder = new BRepAlgoAPI_Fuse ( a , b ) ;
} else {
return false ;
}
if ( builder - > IsDone ( ) ) {
TopoDS_Shape r = * builder ;
succesful = BRepCheck_Analyzer ( r ) . IsValid ( ) ! = 0 ;
if ( succesful ) {
result = r ;
ShapeFix_Shape fix ( result ) ;
try {
fix . Perform ( ) ;
result = fix . Shape ( ) ;
} catch ( . . . ) {
2018-12-29 12:34:32 +01:00
Logger : : Error ( " Shape healing failed on boolean result " ) ;
2017-02-16 15:54:34 +01:00
}
} else {
// Increase tolerance max 3 times until succesful
TopoDS_Shape a2 = a ;
TopoDS_Shape b2 = b ;
ShapeAnalysis_ShapeTolerance tolerance ;
const double t1 = tolerance . Tolerance ( a , 1 ) * 10. ;
const double t2 = tolerance . Tolerance ( b , 1 ) * 10. ;
if ( ( ( std : : max ) ( t1 , t2 ) + 1e-15 ) > getValue ( GV_PRECISION ) * 1000. ) {
return false ;
}
apply_tolerance ( a2 , t1 ) ;
apply_tolerance ( b2 , t2 ) ;
succesful = boolean_operation ( a2 , b2 , op , result ) ;
}
}
delete builder ;
return succesful ;
}
# else
2019-04-24 16:32:06 +02:00
bool IfcGeom : : Kernel : : boolean_operation ( const TopoDS_Shape & a_ , const TopTools_ListOfShape & b__ , BOPAlgo_Operation op , TopoDS_Shape & result , double fuzziness ) {
2019-05-24 17:16:31 +02:00
if ( fuzziness < 0. ) {
2019-06-05 16:58:08 +02:00
fuzziness = getValue ( GV_PRECISION ) / 10. ;
2019-05-24 17:16:31 +02:00
}
// @todo, it does seem a bit odd, we first triangulate non-planar faces
// to later unify them again. Can we make this a bit more intelligent?
TopoDS_Shape a = unify ( a_ , fuzziness ) ;
2019-04-24 16:32:06 +02:00
TopTools_ListOfShape b_ ;
{
TopTools_ListIteratorOfListOfShape it ( b__ ) ;
for ( ; it . More ( ) ; it . Next ( ) ) {
2019-05-24 17:16:31 +02:00
b_ . Append ( unify ( it . Value ( ) , fuzziness ) ) ;
2019-04-24 16:32:06 +02:00
}
}
2019-05-24 17:16:31 +02:00
2017-02-16 15:54:34 +01:00
bool success = false ;
BRepAlgoAPI_BooleanOperation * builder ;
2018-09-21 11:21:51 +02:00
TopTools_ListOfShape B , b ;
2017-02-16 15:54:34 +01:00
if ( op = = BOPAlgo_CUT ) {
builder = new BRepAlgoAPI_Cut ( ) ;
2018-09-19 16:42:34 +02:00
bounding_box_overlap ( getValue ( GV_PRECISION ) , a , b_ , b ) ;
2017-02-16 15:54:34 +01:00
} else if ( op = = BOPAlgo_COMMON ) {
builder = new BRepAlgoAPI_Common ( ) ;
2018-09-21 14:53:39 +02:00
b = b_ ;
2017-02-16 15:54:34 +01:00
} else if ( op = = BOPAlgo_FUSE ) {
builder = new BRepAlgoAPI_Fuse ( ) ;
2018-09-19 16:42:34 +02:00
b = b_ ;
2017-02-16 15:54:34 +01:00
} else {
return false ;
}
2018-09-21 14:53:39 +02:00
if ( b . Extent ( ) = = 0 ) {
result = a ;
return true ;
}
2018-12-19 16:39:54 +01:00
// Find a sensible value for the fuzziness, based on precision
// and limited by edge lengths and vertex-edge distances.
2019-05-24 17:16:31 +02:00
const double len_a = min_edge_length ( a_ ) ;
double min_length_orig = ( std : : min ) ( len_a , min_vertex_edge_distance ( a_ , getValue ( GV_PRECISION ) , len_a ) ) ;
TopTools_ListIteratorOfListOfShape it ( b__ ) ;
2018-08-20 11:46:26 +02:00
for ( ; it . More ( ) ; it . Next ( ) ) {
double d = min_edge_length ( it . Value ( ) ) ;
2018-12-28 15:15:41 +01:00
if ( d < min_length_orig ) {
min_length_orig = d ;
2018-08-20 11:46:26 +02:00
}
2018-12-19 16:39:54 +01:00
d = min_vertex_edge_distance ( it . Value ( ) , getValue ( GV_PRECISION ) , d ) ;
2018-12-28 15:15:41 +01:00
if ( d < min_length_orig ) {
min_length_orig = d ;
2018-08-20 11:46:26 +02:00
}
}
2019-05-17 14:25:28 +02:00
const double fuzz = ( std : : min ) ( min_length_orig / 3. , fuzziness ) ;
2018-05-13 09:15:06 +02:00
2017-02-16 15:54:34 +01:00
TopTools_ListOfShape s1s ;
2018-06-15 12:39:29 +02:00
s1s . Append ( copy_operand ( a ) ) ;
# if OCC_VERSION_HEX >= 0x70000
2018-06-12 16:15:12 +02:00
builder - > SetNonDestructive ( true ) ;
2018-06-15 12:39:29 +02:00
# endif
2018-06-08 14:12:01 +02:00
builder - > SetFuzzyValue ( fuzz ) ;
2017-02-16 15:54:34 +01:00
builder - > SetArguments ( s1s ) ;
2018-09-21 11:21:51 +02:00
copy_operand ( b , B ) ;
builder - > SetTools ( B ) ;
2017-02-16 15:54:34 +01:00
builder - > Build ( ) ;
if ( builder - > IsDone ( ) ) {
TopoDS_Shape r = * builder ;
2017-11-13 12:41:37 +01:00
ShapeFix_Shape fix ( r ) ;
try {
2018-06-08 14:12:01 +02:00
fix . SetMinTolerance ( fuzz ) ;
fix . SetMaxTolerance ( fuzz ) ;
fix . SetPrecision ( fuzz ) ;
2017-11-13 12:41:37 +01:00
fix . Perform ( ) ;
r = fix . Shape ( ) ;
} catch ( . . . ) {
2018-12-29 12:34:32 +01:00
Logger : : Error ( " Shape healing failed on boolean result " ) ;
2017-11-13 12:41:37 +01:00
}
2017-02-16 15:54:34 +01:00
success = BRepCheck_Analyzer ( r ) . IsValid ( ) ! = 0 ;
2017-11-13 12:41:37 +01:00
2017-02-16 15:54:34 +01:00
if ( success ) {
2018-09-17 12:00:49 +02:00
success = ! is_manifold ( a ) | | is_manifold ( r ) ;
2019-09-26 15:14:23 +02:00
if ( ! success ) {
// An excemption for the requirement to be manifold: When the cut operands have overlapping edge belonging to faces that do not overlap.
bool operands_nonmanifold = false ;
if ( op = = BOPAlgo_CUT ) {
TopTools_IndexedMapOfShape edges ;
TopTools_IndexedDataMapOfShapeListOfShape map ;
2019-09-28 23:14:08 +10:00
for ( TopTools_ListIteratorOfListOfShape it ( B ) ; it . More ( ) ; it . Next ( ) ) {
auto & bb = it . Value ( ) ;
2019-09-26 15:14:23 +02:00
TopExp : : MapShapes ( bb , TopAbs_EDGE , edges ) ;
TopExp : : MapShapesAndAncestors ( bb , TopAbs_EDGE , TopAbs_FACE , map ) ;
}
IfcGeom : : impl : : tree < int > tree ;
for ( int i = 1 ; i < = edges . Extent ( ) ; + + i ) {
tree . add ( i , edges . FindKey ( i ) ) ;
}
for ( int i = 1 ; i < = edges . Extent ( ) ; + + i ) {
const TopoDS_Edge & ei = TopoDS : : Edge ( edges . FindKey ( i ) ) ;
Bnd_Box b ;
BRepBndLib : : Add ( ei , b ) ;
b . Enlarge ( fuzziness ) ;
auto ii = tree . select_box ( b , false ) ;
for ( int j : ii ) {
if ( j ! = i ) {
const TopoDS_Edge & ej = TopoDS : : Edge ( edges . FindKey ( j ) ) ;
ShapeAnalysis_Edge sae ;
double f = fuzziness ;
bool edges_overlapping = sae . CheckOverlapping ( ei , ej , f , 0. ) | |
sae . CheckOverlapping ( ej , ei , f , 0. ) ;
if ( edges_overlapping ) {
auto faces_i = map . FindFromKey ( edges . FindKey ( i ) ) ;
auto faces_j = map . FindFromKey ( edges . FindKey ( j ) ) ;
bool overlap = false ;
2019-09-28 23:14:08 +10:00
for ( TopTools_ListIteratorOfListOfShape it ( faces_i ) ; it . More ( ) ; it . Next ( ) ) {
auto & fi = it . Value ( ) ;
for ( TopTools_ListIteratorOfListOfShape it2 ( faces_j ) ; it2 . More ( ) ; it2 . Next ( ) ) {
auto & fj = it2 . Value ( ) ;
2019-09-26 15:14:23 +02:00
if ( faces_overlap ( TopoDS : : Face ( fi ) , TopoDS : : Face ( fj ) ) ) {
overlap = true ;
}
}
if ( overlap ) {
break ;
}
}
operands_nonmanifold = ! overlap ;
break ;
}
}
}
if ( operands_nonmanifold ) {
break ;
}
}
}
success = operands_nonmanifold ;
}
2018-09-17 12:00:49 +02:00
if ( success ) {
2021-05-23 16:26:34 +02:00
TopTools_IndexedMapOfShape faces ;
TopExp : : MapShapes ( r , TopAbs_FACE , faces ) ;
bool all_faces_included_in_result = true ;
for ( TopExp_Explorer exp ( a , TopAbs_FACE ) ; exp . More ( ) ; exp . Next ( ) ) {
auto & f = TopoDS : : Face ( exp . Current ( ) ) ;
if ( ! faces . Contains ( f ) ) {
all_faces_included_in_result = false ;
break ;
}
}
int result_n_faces = count ( r , TopAbs_FACE ) ;
int first_op_n_faces = count ( a , TopAbs_FACE ) ;
if ( all_faces_included_in_result & & result_n_faces > first_op_n_faces ) {
2019-04-24 16:32:06 +02:00
success = false ;
2021-05-23 16:26:34 +02:00
Logger : : Notice ( " Boolean result discarded because subtractions results in only the addition of faces " ) ;
} else {
// when there are edges or vertex-edge distances close to the used fuzziness, the
// output is not trusted and the operation is attempted with a higher fuzziness.
int reason = 0 ;
double v ;
if ( ( v = min_edge_length ( r ) ) < fuzziness * 3. ) {
reason = 0 ;
success = false ;
}
else if ( ( v = min_vertex_edge_distance ( r , getValue ( GV_PRECISION ) , fuzziness * 3. ) ) < fuzziness * 3. ) {
reason = 1 ;
success = false ;
}
else if ( ( v = min_face_face_distance ( r , 1.e-4 ) ) < 1.e-4 ) {
reason = 2 ;
success = false ;
}
if ( ! success ) {
static const char * const reason_strings [ ] = { " edge length " , " vertex-edge " , " face-face " } ;
std : : stringstream str ;
str < < " Boolean operation result failing " < < reason_strings [ reason ] < < " interference check, with fuzziness " < < fuzziness < < " with length " < < v ;
Logger : : Notice ( str . str ( ) ) ;
}
2019-04-24 16:32:06 +02:00
}
2018-09-17 12:02:41 +02:00
if ( success ) {
result = r ;
}
2021-05-23 16:26:34 +02:00
2018-12-28 15:15:41 +01:00
} else {
Logger : : Notice ( " Boolean operation yields non-manifold result " ) ;
2018-09-17 12:00:49 +02:00
}
2018-12-28 15:15:41 +01:00
} else {
Logger : : Notice ( " Boolean operation yields invalid result " ) ;
2017-02-16 15:54:34 +01:00
}
2018-12-28 15:15:41 +01:00
} else {
std : : stringstream str ;
2019-01-02 07:27:09 +01:00
# if OCC_VERSION_HEX >= 0x70000
2018-12-28 15:15:41 +01:00
builder - > DumpErrors ( str ) ;
2019-01-02 07:27:09 +01:00
# else
2019-04-24 17:01:02 +02:00
str < < " Error code: " < < builder - > ErrorStatus ( ) ;
2019-01-02 07:27:09 +01:00
# endif
2019-04-24 17:01:02 +02:00
std : : string str_str = str . str ( ) ;
if ( str_str . size ( ) ) {
Logger : : Notice ( str_str ) ;
}
2017-02-16 15:54:34 +01:00
}
delete builder ;
if ( ! success ) {
2017-04-25 12:43:29 +02:00
const double new_fuzziness = fuzziness * 10. ;
2019-04-24 16:32:06 +02:00
if ( new_fuzziness - 1e-15 < = getValue ( GV_PRECISION ) * 10000. & & new_fuzziness < min_length_orig ) {
2017-04-25 12:43:29 +02:00
return boolean_operation ( a , b , op , result , new_fuzziness ) ;
2019-04-24 16:32:06 +02:00
} else {
Logger : : Notice ( " No longer attempting boolean operation with higher fuzziness " ) ;
}
2017-02-16 15:54:34 +01:00
}
return success ;
}
bool IfcGeom : : Kernel : : boolean_operation ( const TopoDS_Shape & a , const TopoDS_Shape & b , BOPAlgo_Operation op , TopoDS_Shape & result , double fuzziness ) {
TopTools_ListOfShape bs ;
bs . Append ( b ) ;
return boolean_operation ( a , bs , op , result , fuzziness ) ;
}
2017-11-13 13:01:59 +01:00
# endif
2018-11-02 16:10:06 +01:00
namespace {
2018-12-10 10:45:58 +01:00
void find_neighbours ( IfcGeom : : impl : : tree < int > & tree , std : : vector < std : : unique_ptr < gp_Pnt > > & pnts , std : : set < int > & visited , int p , double eps ) {
2018-11-02 16:10:06 +01:00
visited . insert ( p ) ;
Bnd_Box b ;
2018-12-10 10:45:58 +01:00
b . Set ( * pnts [ p ] . get ( ) ) ;
2018-11-02 16:10:06 +01:00
b . Enlarge ( eps ) ;
std : : vector < int > js = tree . select_box ( b , false ) ;
for ( int j : js ) {
2018-12-10 10:45:58 +01:00
visited . insert ( j ) ;
# ifdef FACESET_HELPER_RECURSIVE
2018-11-02 16:10:06 +01:00
if ( visited . find ( j ) = = visited . end ( ) ) {
2018-11-30 11:08:19 +01:00
// @todo, making this recursive removes the dependence on the initial ordering, but will
// likely result in empty results when all vertices are within 1 eps from another point.
2018-11-02 16:10:06 +01:00
find_neighbours ( tree , pnts , visited , j , eps ) ;
}
2018-12-10 10:45:58 +01:00
# endif
2018-11-02 16:10:06 +01:00
}
}
}
2021-05-11 22:35:46 +02:00
template < typename CP , typename LP >
IfcGeom : : Kernel : : faceset_helper < CP , LP > : : ~ faceset_helper ( ) {
// @todo this is super ugly, but how else can we be notified that the unique_ptr goes out of scope?
// Perhaps just supply a custom std::deleter?
2018-11-02 16:10:06 +01:00
kernel_ - > faceset_helper_ = nullptr ;
}
2021-05-11 22:35:46 +02:00
template < typename CP , typename LP >
bool IfcGeom : : Kernel : : faceset_helper < CP , LP > : : construct ( const IfcSchema : : IfcCartesianPoint * cp , gp_Pnt * l ) {
return kernel_ - > convert ( cp , * l ) ;
}
template < typename CP , typename LP >
bool IfcGeom : : Kernel : : faceset_helper < CP , LP > : : construct ( const std : : vector < double > & cp , gp_Pnt * l ) {
if ( cp . size ( ) ! = 3 ) {
return false ;
}
auto LU = kernel_ - > getValue ( GV_LENGTH_UNIT ) ;
l - > SetCoord ( cp [ 0 ] * LU , cp [ 1 ] * LU , cp [ 2 ] * LU ) ;
2021-05-13 10:09:09 +02:00
return true ;
2021-05-11 22:35:46 +02:00
}
/*
template <typename CP, typename LP>
IfcGeom::Kernel::faceset_helper<CP, LP>::faceset_helper(Kernel* kernel, const IfcSchema::IfcConnectedFaceSet* l)
2018-11-02 16:10:06 +01:00
: kernel_(kernel)
2019-04-24 16:32:06 +02:00
, non_manifold_(false)
2018-11-02 16:10:06 +01:00
{
kernel->faceset_helper_ = this;
IfcSchema::IfcCartesianPoint::list::ptr points = IfcParse::traverse((IfcUtil::IfcBaseClass*) l)->as<IfcSchema::IfcCartesianPoint>();
2018-12-10 10:45:58 +01:00
std::vector<std::unique_ptr<gp_Pnt>> pnts(std::distance(points->begin(), points->end()));
2018-11-02 16:10:06 +01:00
std::vector<TopoDS_Vertex> vertices(pnts.size());
2018-12-10 10:45:58 +01:00
IfcGeom::impl::tree<int> tree;
2018-11-02 16:10:06 +01:00
BRep_Builder B;
2018-12-10 10:45:58 +01:00
Bnd_Box box;
for (size_t i = 0; i < points->size(); ++i) {
gp_Pnt* p = new gp_Pnt();
if (kernel->convert(*(points->begin() + i), *p)) {
pnts[i].reset(p);
B.MakeVertex(vertices[i], *p, Precision::Confusion());
tree.add(i, vertices[i]);
box.Add(*p);
} else {
delete p;
}
2018-11-02 16:10:06 +01:00
}
2019-04-24 16:32:06 +02:00
// Use the bbox diagonal to influence local epsilon
// double bdiff = std::sqrt(box.SquareExtent());
2019-08-09 17:48:56 +02:00
// @todo the bounding box diagonal is not used (see above)
// because we're explicitly interested in the miminal
// dimension of the element to limit the tolerance (for sheet-
// like elements for example). But the way below is very
// dependent on orientation due to the usage of the
// axis-aligned bounding box. Use PCA to find three non-aligned
// set of dimensions and use the one with the smallest eigenvalue.
2018-12-14 17:14:55 +01:00
// Find the minimal bounding box edge
double bmin[3], bmax[3];
box.Get(bmin[0], bmin[1], bmin[2], bmax[0], bmax[1], bmax[2]);
double bdiff = std::numeric_limits<double>::infinity();
for (size_t i = 0; i < 3; ++i) {
const double d = bmax[i] - bmin[i];
if (d > kernel->getValue(GV_PRECISION) * 10. && d < bdiff) {
bdiff = d;
}
}
2018-12-21 15:58:21 +01:00
eps_ = kernel->getValue(GV_PRECISION) * 10. * (std::min)(1.0, bdiff);
2018-11-02 16:10:06 +01:00
2019-06-14 11:32:59 +02:00
// @todo, there a tiny possibility that the duplicate faces are triggered
// for an internal boundary, that is also present as an external boundary.
// This will result in non-manifold configuration then, but this is deemed
// such as corner-case that it is not considered.
2018-11-02 16:10:06 +01:00
IfcSchema::IfcPolyLoop::list::ptr loops = IfcParse::traverse((IfcUtil::IfcBaseClass*)l)->as<IfcSchema::IfcPolyLoop>();
2019-08-09 17:48:56 +02:00
size_t loops_removed, non_manifold, duplicate_faces;
2019-06-14 11:32:59 +02:00
2019-08-09 17:48:56 +02:00
std::map<std::pair<int, int>, int> edge_use;
2018-11-30 11:08:19 +01:00
2019-08-09 17:48:56 +02:00
for (int i = 0; i < 3; ++i) {
// Some times files, have large tolerance values specified collapsing too many vertices.
// This case we detect below and re-run the loop with smaller epsilon. Normally
// the body of this loop would only be executed once.
2018-11-30 11:08:19 +01:00
2019-08-09 17:48:56 +02:00
loops_removed = 0;
non_manifold = 0;
duplicate_faces = 0;
2018-11-30 11:08:19 +01:00
2019-08-09 17:48:56 +02:00
vertex_mapping_.clear();
duplicates_.clear();
2018-11-30 11:08:19 +01:00
2019-08-09 17:48:56 +02:00
edge_use.clear();
if (eps_ < Precision::Confusion()) {
// occt uses some hard coded precision values, don't go smaller than that.
// @todo, can be reset though with BRepLib::Precision(double)
eps_ = Precision::Confusion();
}
2019-11-01 16:11:26 +01:00
for (int pnt_i = 0; pnt_i < (int)pnts.size(); ++pnt_i) {
if (pnts[pnt_i]) {
2019-08-09 17:48:56 +02:00
std::set<int> vs;
2019-11-01 16:11:26 +01:00
find_neighbours(tree, pnts, vs, pnt_i, eps_);
2019-06-14 11:32:59 +02:00
2019-08-09 17:48:56 +02:00
for (int v : vs) {
auto pt = *(points->begin() + v);
// NB: insert() ignores duplicate keys
2021-05-11 22:35:46 +02:00
vertex_mapping_.insert({ get_idx(pt), pnt_i });
2019-08-09 17:48:56 +02:00
}
}
}
typedef std::array<int, 2> edge_t;
typedef std::set<edge_t> edge_set_t;
std::set<edge_set_t> edge_sets;
for (auto& loop : *loops) {
auto ps = loop->Polygon();
std::vector<std::pair<int, int> > segments;
edge_set_t segment_set;
loop_(ps, [&segments, &segment_set](int C, int D, bool) {
2020-06-15 11:56:04 +02:00
segment_set.insert(edge_t{C,D});
segments.push_back(std::make_pair(C, D));
2019-08-09 17:48:56 +02:00
});
if (edge_sets.find(segment_set) != edge_sets.end()) {
duplicate_faces++;
duplicates_.insert(loop);
continue;
}
edge_sets.insert(segment_set);
if (segments.size() >= 3) {
for (auto& p : segments) {
edge_use[p] ++;
}
} else {
loops_removed += 1;
2018-11-30 11:08:19 +01:00
}
2019-08-09 17:48:56 +02:00
}
if (edge_use.size() != 0) {
break;
2018-11-30 11:08:19 +01:00
} else {
2019-08-09 17:48:56 +02:00
eps_ /= 10.;
2018-11-30 11:08:19 +01:00
}
2018-11-02 16:10:06 +01:00
}
for (auto& p : edge_use) {
int a, b;
std::tie(a, b) = p.first;
edges_[p.first] = BRepBuilderAPI_MakeEdge(vertices[a], vertices[b]);
2018-11-30 11:08:19 +01:00
if (p.second != 2) {
non_manifold += 1;
}
}
2018-12-17 13:25:28 +01:00
if (loops_removed || (non_manifold && l->declaration().is(IfcSchema::IfcClosedShell::Class()))) {
2019-06-14 11:32:59 +02:00
Logger::Warning(boost::lexical_cast<std::string>(duplicate_faces) + " duplicate faces removed, " + boost::lexical_cast<std::string>(loops_removed) + " loops removed and " + boost::lexical_cast<std::string>(non_manifold) + " non-manifold edges for:", l);
2018-11-02 16:10:06 +01:00
}
2021-03-17 22:04:27 +02:00
}
2021-05-11 22:35:46 +02:00
*/
namespace {
const std : : vector < std : : vector < double > > * store_cache ( const std : : vector < std : : vector < double > > & p ) {
return & p ;
}
const std : : vector < std : : vector < double > > * store_cache ( const std : : vector < const IfcSchema : : IfcCartesianPoint * > & p ) {
return nullptr ;
}
}
template < typename CP , typename LP >
IfcGeom : : Kernel : : faceset_helper < CP , LP > : : faceset_helper (
Kernel * kernel ,
const std : : vector < CP > & points ,
const std : : vector < LP > & indices ,
bool should_be_closed
)
: kernel_ ( kernel )
, non_manifold_ ( false )
, points_ ( store_cache ( points ) )
{
std : : vector < std : : unique_ptr < gp_Pnt > > pnts ( std : : distance ( points . begin ( ) , points . end ( ) ) ) ;
std : : vector < TopoDS_Vertex > vertices ( pnts . size ( ) ) ;
auto LU = kernel_ - > getValue ( GV_LENGTH_UNIT ) ;
IfcGeom : : impl : : tree < int > tree ;
BRep_Builder B ;
Bnd_Box box ;
for ( size_t i = 0 ; i < points . size ( ) ; + + i ) {
gp_Pnt * p = new gp_Pnt ;
if ( construct ( points [ i ] , p ) ) {
pnts [ i ] . reset ( p ) ;
B . MakeVertex ( vertices [ i ] , * p , Precision : : Confusion ( ) ) ;
tree . add ( i , vertices [ i ] ) ;
box . Add ( * p ) ;
} else {
delete p ;
}
}
// Use the bbox diagonal to influence local epsilon
// double bdiff = std::sqrt(box.SquareExtent());
// @todo the bounding box diagonal is not used (see above)
// because we're explicitly interested in the miminal
// dimension of the element to limit the tolerance (for sheet-
// like elements for example). But the way below is very
// dependent on orientation due to the usage of the
// axis-aligned bounding box. Use PCA to find three non-aligned
// set of dimensions and use the one with the smallest eigenvalue.
// Find the minimal bounding box edge
double bmin [ 3 ] , bmax [ 3 ] ;
box . Get ( bmin [ 0 ] , bmin [ 1 ] , bmin [ 2 ] , bmax [ 0 ] , bmax [ 1 ] , bmax [ 2 ] ) ;
double bdiff = std : : numeric_limits < double > : : infinity ( ) ;
for ( size_t i = 0 ; i < 3 ; + + i ) {
const double d = bmax [ i ] - bmin [ i ] ;
if ( d > kernel - > getValue ( GV_PRECISION ) * 10. & & d < bdiff ) {
bdiff = d ;
}
}
eps_ = kernel - > getValue ( GV_PRECISION ) * 10. * ( std : : min ) ( 1.0 , bdiff ) ;
size_t loops_removed , non_manifold , duplicate_faces ;
std : : map < std : : pair < int , int > , int > edge_use ;
for ( int i = 0 ; i < 3 ; + + i ) {
// Some times files, have large tolerance values specified collapsing too many vertices.
// This case we detect below and re-run the loop with smaller epsilon. Normally
// the body of this loop would only be executed once.
loops_removed = 0 ;
non_manifold = 0 ;
duplicate_faces = 0 ;
vertex_mapping_ . clear ( ) ;
duplicates_ . clear ( ) ;
edge_use . clear ( ) ;
if ( eps_ < Precision : : Confusion ( ) ) {
// occt uses some hard coded precision values, don't go smaller than that.
// @todo, can be reset though with BRepLib::Precision(double)
eps_ = Precision : : Confusion ( ) ;
}
for ( int pnt_i = 0 ; pnt_i < ( int ) pnts . size ( ) ; + + pnt_i ) {
if ( pnts [ pnt_i ] ) {
std : : set < int > vs ;
find_neighbours ( tree , pnts , vs , pnt_i , eps_ ) ;
for ( int v : vs ) {
// NB: insert() ignores duplicate keys
// v-1?
vertex_mapping_ . insert ( { get_idx ( points [ v ] ) , pnt_i } ) ;
}
}
}
typedef std : : array < int , 2 > edge_t ;
typedef std : : set < edge_t > edge_set_t ;
std : : set < edge_set_t > edge_sets ;
for ( auto ps = indices . begin ( ) ; ps ! = indices . end ( ) ; + + ps ) {
std : : vector < std : : pair < int , int > > segments ;
edge_set_t segment_set ;
loop_ ( * ps , [ & segments , & segment_set ] ( int C , int D , bool ) {
segment_set . insert ( edge_t { C , D } ) ;
segments . push_back ( std : : make_pair ( C , D ) ) ;
} ) ;
if ( edge_sets . find ( segment_set ) ! = edge_sets . end ( ) ) {
duplicate_faces + + ;
duplicates_ . insert ( * ps ) ;
continue ;
}
edge_sets . insert ( segment_set ) ;
if ( segments . size ( ) > = 3 ) {
for ( auto & p : segments ) {
edge_use [ p ] + + ;
}
}
else {
loops_removed + = 1 ;
}
}
if ( edge_use . size ( ) ! = 0 ) {
break ;
}
else {
eps_ / = 10. ;
}
}
for ( auto & p : edge_use ) {
int a , b ;
std : : tie ( a , b ) = p . first ;
edges_ [ p . first ] = BRepBuilderAPI_MakeEdge ( vertices [ a ] , vertices [ b ] ) ;
if ( p . second ! = 2 ) {
non_manifold + = 1 ;
}
}
if ( loops_removed | | ( non_manifold & & should_be_closed ) ) {
Logger : : Warning ( boost : : lexical_cast < std : : string > ( duplicate_faces ) + " duplicate faces removed, " + boost : : lexical_cast < std : : string > ( loops_removed ) + " loops removed and " + boost : : lexical_cast < std : : string > ( non_manifold ) + " non-manifold edges " ) ;
}
}
template class IfcGeom : : Kernel : : faceset_helper < const IfcSchema : : IfcCartesianPoint * , const IfcSchema : : IfcPolyLoop * > ;
template class IfcGeom : : Kernel : : faceset_helper < std : : vector < double > , std : : vector < int > > ;