Compare commits

...

330 Commits

Author SHA1 Message Date
Thomas Krijnen 4dc3dc37a5 Refactoring 2022-11-14 10:31:00 +01:00
Thomas Krijnen b5b73fd079 Work on modularizing files 2022-11-11 13:20:10 +01:00
Thomas Krijnen 4a2465674e Merge branch 'cgal' of https://github.com/tudelft3d/IfcOpenShell into v0.7.0-rewrite 2021-02-21 17:26:16 +01:00
Thomas Krijnen 46d24f112c Remove static keywords 2021-02-21 17:25:50 +01:00
aothms 5b13db129d Merge branch 'cgal' of https://github.com/tudelft3d/IfcOpenShell into cgal 2021-02-21 16:38:53 +01:00
aothms a57f08a9da Fix map invalidation reported by valgrind 2021-02-21 16:37:33 +01:00
Thomas Krijnen 4ec24e93a6 Don't store precision cube (multi-threading) 2021-02-21 16:34:51 +01:00
Thomas Krijnen 7812ecb189 C++17 compat 2021-02-21 16:34:34 +01:00
Thomas Krijnen 7852c06ab7 Fix issue with iterator invalidation 2021-02-02 23:40:43 +01:00
aothms db44eaf221 Temporarily disable serialization 2021-01-24 15:49:46 +01:00
aothms 1064e2fec8 CGAL v5.2 2021-01-24 15:10:02 +01:00
Thomas Krijnen a55187600e Update to CGAL 5.2 2021-01-24 12:14:43 +01:00
Thomas Krijnen 059de764d6 Begin proper memory management on taxonomy 2020-11-04 13:46:25 +01:00
Thomas Krijnen e39c7e7195 ~Iterator() 2020-10-29 16:06:37 +01:00
Thomas Krijnen fb59cd4004 ~collection() 2020-10-29 16:06:26 +01:00
Thomas Krijnen 7bc411ac9b debugging tip 2020-10-29 16:06:11 +01:00
Thomas Krijnen d181d03a21 Change density constraints 2020-10-29 16:05:56 +01:00
Thomas Krijnen 625c757326 incorporate halfspace orientation 2020-10-04 13:08:46 +02:00
Thomas Krijnen df1225ce95 Include cgal patch for nary union 2020-10-04 10:42:05 +02:00
Thomas Krijnen 4881238ce6 full circle trim fixes 2020-10-03 17:15:38 +02:00
Thomas Krijnen 336b628be2 virtual dtor 2020-10-03 17:13:25 +02:00
aothms 575f9e015b virtual dtor and profile helper assignment fix 2020-09-30 16:06:35 +02:00
Thomas Krijnen b3ee2c6511 fail on empty poly 2020-09-30 13:53:46 +02:00
Thomas Krijnen bf4bf3d327 fix density based box substitution 2020-09-30 13:53:30 +02:00
Thomas Krijnen 00e4a24153 only fetch style for some set of kinds 2020-09-30 13:53:15 +02:00
Thomas Krijnen 7b26d6d541 try to reduce memory footprint 2020-09-30 13:52:57 +02:00
Thomas Krijnen 3fb822b07b disable validation code to reduce compilation time 2020-09-30 13:45:37 +02:00
Thomas Krijnen b09fc12868 Traverse collections for styles when processing boolean as arrangement 2020-09-28 15:46:19 +02:00
Thomas Krijnen 04ae29759b Re-enable are_holes_and_boundary_pairwise_disjoint() check 2020-09-28 15:38:19 +02:00
Thomas Krijnen 2905900d5c retain transformations on collection nodes during 2d boolean op handling 2020-09-28 15:26:03 +02:00
Thomas Krijnen e45ad15d91 Correct nesting 2020-09-28 15:25:24 +02:00
Thomas Krijnen 8ef971cfb0 use polygon_soup_to_polygon_mesh instead of incremental builder 2020-09-28 14:21:33 +02:00
Thomas Krijnen 630c742a77 more verbose logging 2020-09-28 14:21:07 +02:00
Thomas Krijnen 2885cd74e3 infinity as 1e9 2020-09-28 14:16:48 +02:00
Thomas Krijnen 286a76ddda fix boolean exp 2020-09-28 12:22:38 +02:00
Thomas Krijnen aa3ccf5cb5 const lvalue things 2020-09-20 12:19:43 +02:00
Thomas Krijnen c9ce6a6df9 Eigen case on nix 2020-09-20 12:17:22 +02:00
Thomas Krijnen 1cfb15f060 halfspaces in cgal kernel 2020-09-19 15:40:18 +02:00
Thomas Krijnen 104896b3e2 Don't fail on self-intersections in polygon with holes after triangulation 2020-09-18 09:56:14 +02:00
Thomas Krijnen b8c90eaa5e Fix mapping of booleanresult and begin implementing handling of arbitrary polyhedra operands in 2d 2020-09-17 14:52:21 +02:00
Thomas Krijnen 8bd91113a0 Substitution with bounding box based on density; Reorientation from soup; 2D Arrangement in extrusion and shell 2020-09-16 15:22:07 +02:00
Thomas Krijnen 52942f8a3e Use map in builder; stitch borders optional; fix positioning of 2d arrangement 2020-09-14 13:55:14 +02:00
Thomas Krijnen c40f9fdd97 process boolean subtraction in 2d if coplanar extrusion along z 2020-09-13 17:15:46 +02:00
Ken Arroyo Ohori a1c33af488 skip linker groups on Mac 2020-09-03 21:40:22 -05:00
Ken Arroyo Ohori a72223acfd eigen from gitlab repo 2020-09-03 21:39:55 -05:00
Thomas Krijnen ed9718af2a Use constructor to initialize intersection_collector 2020-09-02 15:16:37 +02:00
Thomas Krijnen e15e5a2d20 Correct 730p3 patch 2020-09-01 16:54:45 +02:00
Thomas Krijnen 51dc0e506c Propagate static runtime flag to voxel dependency 2020-09-01 16:25:11 +02:00
Thomas Krijnen feac80aa9d Assign material name to taxonomy::style 2020-09-01 12:22:23 +02:00
Thomas Krijnen 842dcb7579 Check loop self intersection in cgal 2020-09-01 12:22:10 +02:00
Thomas Krijnen 6685aa16d3 compare style on collection 2020-09-01 12:21:39 +02:00
Thomas Krijnen 91517a9dd1 consider extrusion matrix in taxonomy::item::less 2020-08-06 16:02:24 +02:00
Thomas Krijnen 891732d22f check for get_surface_style returning nullptr 2020-08-06 15:29:28 +02:00
Thomas Krijnen 2cf06c0d6a skip over nullptr from map() 2020-08-06 14:09:30 +02:00
Thomas Krijnen 5dd146f357 remove unused header 2020-08-06 14:09:15 +02:00
Thomas Krijnen cfc4a03312 Merge branch 'cgal' of https://github.com/tudelft3d/IfcOpenShell into v0.7.0-rewrite 2020-07-29 12:23:15 +02:00
Thomas Krijnen 9346947476 catch exceptions during orientation 2020-07-29 12:21:30 +02:00
Thomas Krijnen d4a5b33b8f CGAL_VERSION 4.14.3 2020-07-29 12:04:43 +02:00
Thomas Krijnen 9ba2266d0c CGAL_VERSION 4.14.3 2020-07-29 12:04:00 +02:00
Thomas Krijnen 75ce57c203 More elaborate validation 2020-07-29 12:01:19 +02:00
Thomas Krijnen 23a05c47d4 Fix cgal loop handling 2020-07-29 11:14:45 +02:00
Thomas Krijnen 606dbf7226 material colours 2020-07-28 13:39:14 +02:00
Thomas Krijnen c01db8a86a cgal reversed curve fixes 2020-07-26 17:17:01 +02:00
Thomas Krijnen c7e2fc5551 Fix for cgal point projection 2020-07-26 15:35:10 +02:00
Thomas Krijnen 63638a4d26 improve trimmed curve handling in both kernels 2020-07-25 09:12:39 +02:00
Thomas Krijnen badac24d6c Merge branch 'cgal' of https://github.com/tudelft3d/IfcOpenShell into v0.7.0-rewrite 2020-07-23 12:14:37 +02:00
Thomas Krijnen 549cc76a2c is_valid level 3 2020-07-23 12:14:20 +02:00
Ubuntu 9a94a5ed5a iterator progress 2020-07-15 14:09:50 +00:00
Ubuntu 7fe9c3ccb1 fixes for clang 2020-07-15 13:48:42 +00:00
Thomas Krijnen 6d59e6ea90 Fixes for transform and mapped item reuse in taxonomy 2020-07-09 22:09:54 +02:00
Thomas Krijnen 0e2bac5e3b Shape reuse based on std::less 2020-07-08 12:07:10 +02:00
Thomas Krijnen 6aafff2c40 taxonomy printing and different trimmed curve orientation handling 2020-06-21 15:15:49 +02:00
Thomas Krijnen eacea738f9 move dispatch curve creation 2020-06-11 20:51:22 +02:00
Thomas Krijnen 7f60ea9c52 cgal curve implementations 2020-06-11 20:51:08 +02:00
Thomas Krijnen b85e03374a Workaround singularity issues in gp_GTrsf 2020-06-11 20:50:50 +02:00
Thomas Krijnen 939d1a6c4f implement triangulation transformation in eigen 2020-06-11 20:50:13 +02:00
Thomas Krijnen 018f1ea1d4 fix radius 2020-06-11 20:49:53 +02:00
Thomas Krijnen 52a3d627e2 mapped item implementation 2020-06-11 20:49:44 +02:00
Thomas Krijnen 109131961f heap alloc for eigen types for alignment issues 2020-06-11 20:48:35 +02:00
Thomas Krijnen 185b4ef9fa shell based surface model 2020-06-11 20:44:29 +02:00
Thomas Krijnen 0fa05f7339 Note on segfault 2020-04-29 10:06:23 +02:00
Thomas Krijnen 0b659ce4f6 Update CMakeLists.txt 2020-04-28 13:54:41 +02:00
aothms 55024e1e8a Update cmake install directives 2020-04-05 09:41:20 +00:00
aothms 53477cc8e2 Small fixes for gcc 2020-04-03 15:05:50 +00:00
aothms bba7295edf Small fixes for gcc 2020-04-03 14:56:46 +00:00
Thomas Krijnen 9b391d3d47 Remove auto from lambda parameter declaration 2020-04-03 16:24:07 +02:00
Thomas Krijnen 5d21d26f24 Fix glTF transformation 2020-04-03 16:21:16 +02:00
Thomas Krijnen 47b2da6082 Fix gltf serializer compilation 2020-04-03 14:23:23 +02:00
aothms 63b9b7baa7 Nullptr return types 2020-04-03 12:01:58 +00:00
aothms 33e092c8d5 Update occt tree 2020-04-03 12:01:30 +00:00
aothms 4d23a442be Eigen in nix build 2020-04-03 12:00:48 +00:00
Thomas Krijnen 3113976ee5 Implement fillets and a couple of profiles 2020-03-30 14:31:59 +02:00
Thomas Krijnen 048ee84ae6 Propagate failure on representation to product 2020-03-27 14:06:43 +01:00
Thomas Krijnen 4aef5dbd10 Small fixes 2020-03-23 09:20:01 +01:00
Thomas Krijnen 60b160461e Update README 2020-02-01 11:23:01 +01:00
Thomas Krijnen df6852d2ac space boundaries 2020-02-01 11:16:09 +01:00
Thomas Krijnen 6b250b3ccf manifold solid brep 2020-02-01 11:15:40 +01:00
Thomas Krijnen bca78e086d Storey containment fuzziness 2020-01-31 11:58:14 +01:00
Thomas Krijnen cbf147b9f9 area overlap 2020-01-31 11:57:52 +01:00
Thomas Krijnen a805496895 Disable polyline point removal 2020-01-29 17:29:33 +01:00
Thomas Krijnen b91a34c8c6 Track time; fixes for wall connectivity check 2020-01-29 14:57:49 +01:00
Thomas Krijnen e448312d41 Reporting for wall connectivity 2020-01-27 10:42:01 +01:00
Thomas Krijnen 1d6f20c48d Fix wall connectivity 2020-01-26 17:11:14 +01:00
Thomas Krijnen 7a8a5e2ebb Dont close polylines automatically 2020-01-26 17:11:02 +01:00
Thomas Krijnen b6a7ba1e1c Fix for open polyhedra 2020-01-26 13:58:02 +01:00
Thomas Krijnen 060c35939a Wall connectivity 2020-01-26 13:12:19 +01:00
Thomas Krijnen 030ea06aac Fix placement on containment case 2020-01-25 14:24:18 +01:00
Thomas Krijnen 3575aded90 containment check 2020-01-21 16:26:00 +01:00
Thomas Krijnen 487be4b65e Correct projection to middle surface 2019-12-14 16:17:36 +01:00
Thomas Krijnen 612fb8d8d5 Average space boundary construction in Nef polyhedra 2019-12-14 14:50:20 +01:00
Thomas Krijnen a729f489ee Fix double free 2019-12-14 14:41:41 +01:00
Thomas Krijnen 59b04c7857 Validation work using Nef 2019-12-14 13:25:30 +01:00
Thomas Krijnen a5de17228a Work towards space boundaries fix 2019-10-06 19:03:31 +02:00
Thomas Krijnen f391b425ba dilate as boolean 2019-10-06 09:11:44 +02:00
Thomas Krijnen 389964c9f4 cgal boolean result and minkowski sum for dilation 2019-09-25 15:30:11 +02:00
Thomas Krijnen 11ad1b2cc5 cgal extrusion 2019-09-24 15:46:50 +02:00
Thomas Krijnen d91ac7363f Mark loops from profile_helper as external 2019-09-22 16:02:48 +02:00
Thomas Krijnen 0085393884 Take into account vertex welding 2019-09-22 16:02:22 +02:00
Thomas Krijnen 61403e878e Try catch face normal computation 2019-09-22 16:02:05 +02:00
Thomas Krijnen a1671448db Default static runtime to off. 2019-09-22 15:05:14 +02:00
Thomas Krijnen 474786c4c5 IfcArbitraryProfileDefWithVoids 2019-09-22 10:32:03 +02:00
Thomas Krijnen 1f4bc28e2b Product placements and openings 2019-09-19 10:11:01 +02:00
Thomas Krijnen 3cde411e1b halfspaces and boolean ops 2019-09-18 16:35:26 +02:00
Thomas Krijnen a447dc6208 Work towards trimmed curves 2019-09-16 19:30:11 +02:00
Thomas Krijnen 3731c95728 Mapped item 2019-09-16 17:40:55 +02:00
Thomas Krijnen d59f3e9932 Polyline 2019-09-16 15:40:13 +02:00
Thomas Krijnen 04bce57e80 Fix placements 2019-09-16 10:25:39 +02:00
Thomas Krijnen e3041219b5 Further work on profiles 2019-09-15 15:59:05 +02:00
Thomas Krijnen 0c54bdcd54 Cleanup 2019-09-04 09:10:57 +02:00
Thomas Krijnen bed9c44883 mark bounds as external based on entity type 2019-09-01 18:46:11 +02:00
Thomas Krijnen 8efb86420e Compilation errors 2019-09-01 16:48:50 +02:00
Thomas Krijnen 7f8bed9922 Begin reenable CGAL kernel 2019-09-01 16:29:00 +02:00
Thomas Krijnen 816c0d3618 Fix placements 2019-09-01 13:36:46 +02:00
Thomas Krijnen 2e7c43f590 Work on placements 2019-09-01 10:36:49 +02:00
Thomas Krijnen ca2a4f243d initialize units in mapping 2019-09-01 09:18:29 +02:00
Thomas Krijnen 64ee3e9af3 closed shell; placements; faceset_helper 2019-08-28 09:54:20 +02:00
Thomas Krijnen 3bfe3b9ab5 Seperate map() and map_impl() for automatic taxonomy item <> ifc instance mapping 2019-08-28 09:24:00 +02:00
Thomas Krijnen 640b08159f Work on shells 2019-08-27 18:59:19 +02:00
Thomas Krijnen f17036313d Work on face boundaries 2019-08-26 17:19:06 +02:00
Thomas Krijnen 8b4900b0e1 Work on shell conversino 2019-08-25 16:07:10 +02:00
Thomas Krijnen 41d7fb32da Provide collection implementation in abstract_kernel 2019-08-25 12:00:04 +02:00
Thomas Krijnen 8ec2019003 Fix enumeration order 2019-08-25 11:50:40 +02:00
Thomas Krijnen 26d7af1450 Implement additional mappings 2019-08-24 18:32:30 +02:00
Thomas Krijnen 2f1861db7b Declare conversions for reuse detection 2019-08-24 17:36:17 +02:00
Thomas Krijnen 94aa7dfab4 Close to working executable 2019-08-18 15:53:26 +02:00
Thomas Krijnen 8f88c601c6 IfcConvert links on windows 2019-08-18 14:31:22 +02:00
Thomas Krijnen 85171bb5df Remove ConversionResultPlacement, fix some errors 2019-08-18 08:37:59 +02:00
Thomas Krijnen 585b89be87 Fix compilation of serializers and convert 2019-08-17 09:47:07 +02:00
Thomas Krijnen ddeaf5a375 More work 2019-08-16 17:40:13 +02:00
Thomas Krijnen dae35f59ce Implement mapping of IfcExtrudedAreaSolid 2019-08-04 11:27:10 +02:00
Thomas Krijnen 467ebb68a3 First step at reorganizing 2019-08-04 09:36:31 +02:00
Thomas Krijnen c8def0474e Update examples 2019-08-03 16:11:02 +02:00
Thomas Krijnen f6aa7c8930 Additional merge issues 2019-08-03 15:43:14 +02:00
Thomas Krijnen 92f7d26835 Work on Python wrapper 2019-08-03 15:09:50 +02:00
Thomas Krijnen 5ccc7213a0 Merge remote-tracking branch 'origin/v0.6.0' into v0.7.0 2019-08-03 14:50:08 +02:00
Thomas Krijnen fd9c57b77d Merge branch 'v0.6.0' into v0.7.0 2019-08-02 17:56:45 +02:00
Thomas Krijnen b08e52bd9b Merge remote-tracking branch 'origin/v0.6.0' into v0.7.0 2019-08-02 13:23:41 +02:00
Thomas Krijnen 0cb2c81227 Rough draft of a client application for the C++ IfcGeomServer binary 2019-05-15 17:13:29 +02:00
Thomas Krijnen bbf271a10e Fix volume calculation with voxels in geom server 2019-05-15 17:07:16 +02:00
Thomas Krijnen d45d843173 Fix CMake error 2019-05-15 14:27:31 +02:00
Thomas Krijnen 4fa283fb3c Write voxelization logic in geom server 2019-05-15 13:57:50 +02:00
Thomas Krijnen 4400a6ea4f Make use of cgal configurable 2019-05-15 11:13:58 +02:00
Thomas Krijnen 9cb321e73d Move include up for M_PI def on MSVC 2019-05-10 14:38:42 +02:00
Thomas Krijnen e566af84c2 Fix geom server for schema agnosticism 2019-05-10 14:35:42 +02:00
Thomas Krijnen 310013dfe4 Fix compilation error from merge 2019-05-10 14:35:20 +02:00
Thomas Krijnen de3633e742 Cmake find voxels 2019-05-10 14:35:00 +02:00
Thomas Krijnen a24ad023a5 CGAL win build script 2019-05-10 13:46:13 +02:00
Thomas Krijnen adc0e92528 Fix remaining conflict 2019-05-10 13:42:16 +02:00
Thomas Krijnen 46ba5661d6 Merge branch 'v0.6.0' into v0.7.0 2019-05-10 13:37:42 +02:00
Thomas Krijnen 33bcf786fd MPIR and MPFR in win build script 2019-05-03 10:46:59 +02:00
Thomas Krijnen 8e6a6ec56d Add voxelization_toolkit to win build script 2019-04-30 14:36:55 +02:00
Thomas Krijnen b862f633db Fix voxel library in nix build script 2019-04-30 13:24:28 +02:00
Thomas Krijnen f9e839817d Add opensourceBIM/voxel to nix build script 2019-04-27 14:37:31 +02:00
Thomas Krijnen a0c01ff756 Merge branch 'v0.6.0' into v0.7.0
# Conflicts:
#	cmake/CMakeLists.txt
#	src/ifcconvert/IfcConvert.cpp
#	src/ifcgeom/IfcGeomRepresentation.h
#	src/ifcgeom/IfcRepresentationShapeItem.h
#	src/ifcgeom/kernels/opencascade/IfcGeomFunctions.cpp
#	src/ifcgeom/schema_agnostic/IfcGeomRepresentation.cpp
#	src/ifcgeom/schema_agnostic/Kernel.cpp
#	src/ifcgeom/schema_agnostic/Kernel.h
#	src/ifcgeomserver/IfcGeomServer.cpp
#	src/serializers/schema_dependent/XmlSerializer.cpp
2019-04-26 15:07:50 +02:00
Thomas Krijnen 5b066c9f95 Don't convert to Nef if there are no voids 2019-01-25 15:19:52 +01:00
Thomas Krijnen 3afb9169b1 Implement missing virtual function in CgalKernel 2019-01-25 14:25:34 +01:00
Thomas Krijnen 24b552a822 Merge fixes 2019-01-23 17:27:13 +01:00
Thomas Krijnen 884064f213 Merge remote-tracking branch 'cgal/cgal' into v0.7.0 2019-01-23 15:44:19 +01:00
Thomas Krijnen 4d39dbca2a Build static cgal, fix null pointer access 2019-01-23 15:10:20 +01:00
Thomas Krijnen 30479ca0a6 Fixes for compilation of cgal kernel 2019-01-23 12:34:21 +01:00
Thomas Krijnen a7a1ad129e Merge commit '6b47e9ca1d18df4b1cd2c66e488ad91f9730ecdf' into v0.7.0
# Conflicts:
#	cmake/CMakeLists.txt
#	nix/build-all.py
#	test/input
2019-01-22 13:00:49 +01:00
Thomas Krijnen bea627d01e Merge commit '6b47e9ca1d18df4b1cd2c66e488ad91f9730ecdf' into v0.7.0 2019-01-22 12:47:21 +01:00
Thomas Krijnen 059a1cb14b Merge commit '4d12bf7e8df4bd144526eeb5994802d308b40297' into v0.7.0 2019-01-22 12:38:02 +01:00
Thomas Krijnen 8c5349feaa Introduce AbstractKernel 2019-01-20 13:16:42 +01:00
Thomas Krijnen fa0a33f3c0 Option to select Kernel from IfcConvert 2019-01-19 12:39:47 +01:00
Thomas Krijnen 5a7c7ed048 Shuffle project structure 2019-01-18 16:43:38 +01:00
Thomas Krijnen 16d6420352 More work on enabling cgal kernel 2019-01-18 15:19:00 +01:00
Thomas Krijnen d804575974 Cgal kernel skeleton 2019-01-18 12:10:18 +01:00
Thomas Krijnen ad24b6be0f Isolate (most of the) geometry processing code into separate opencascade kernel 2019-01-18 11:23:19 +01:00
Thomas Krijnen 0072e1f247 CMake CGAL version of IfcGeom 2019-01-16 14:44:43 +01:00
Thomas Krijnen 116b80cf91 Fix MPFR download location 2019-01-16 14:41:34 +01:00
Thomas Krijnen 208f6d4d74 Small fixes to build script 2019-01-16 14:18:36 +01:00
Thomas Krijnen a0dfdc78ef Add cgal to IfcGeom deps 2019-01-16 14:06:17 +01:00
Thomas Krijnen cf2dd2aa8d Add GMP MPFR CGAL to build script 2019-01-16 14:04:05 +01:00
Thomas Krijnen 6737b54457 Add GMP MPFR CGAL to build script 2019-01-16 13:59:10 +01:00
Thomas Krijnen 9ae55ab303 Small fixes to transformations 2017-06-06 17:12:46 +02:00
Ken Arroyo Ohori 8baf6197d5 Merge pull request #16 from kenohori/cgal
Validation code to catch more cases of invalid objects
2017-04-25 10:22:33 -05:00
Ken Arroyo Ohori 42f23a786f Output all errors as separate files 2017-04-25 13:45:18 +02:00
Ken Arroyo Ohori 8b539d2b93 Requirement for self-intersection tests 2017-04-25 13:44:57 +02:00
Ken Arroyo Ohori b732759388 Comprehensive validation code for opening subtractions 2017-04-25 13:44:34 +02:00
Ken Arroyo Ohori 02244400ab Remove old debug code 2017-04-25 13:43:55 +02:00
Ken Arroyo Ohori 85a36738d2 Comment out triangulation code 2017-04-25 13:42:38 +02:00
Ken Arroyo Ohori 164e67a50b Catching some more CGAL errors, allow non-closed meshes, start of new code to triangulate faces 2017-04-21 15:43:19 +02:00
Ken Arroyo Ohori de83633891 Merge pull request #15 from kenohori/cgal
Catching some errors during conversion to/from Nef, tapered extrusions
2017-04-12 23:10:46 -05:00
Ken Arroyo Ohori 9f2a617ad7 Catch a few nasty cases in triangulation, more efficient to only compute face normals 2017-04-10 18:54:30 -05:00
Ken Arroyo Ohori 26bd03e407 Ditto 2017-04-10 18:53:52 -05:00
Ken Arroyo Ohori 248c73c2ab Catch failures to convert Nef to Polyhedron_3 2017-04-10 18:53:28 -05:00
Thomas Krijnen 8d9acfa677 Attempt to fix travis build w/ CGAL 2017-04-06 15:23:45 +02:00
Thomas Krijnen 7b7f9b4852 Attempt to fix travis build w/ CGAL 2017-04-06 14:57:10 +02:00
Thomas Krijnen 7189219aa4 Attempt to fix travis build w/ CGAL 2017-04-06 14:44:57 +02:00
Ken Arroyo Ohori f03a228280 Merge pull request #3 from aothms/cgal
Thomas fixed the transformations
2017-04-05 18:07:32 -05:00
Thomas Krijnen 91e5b337d3 Reverse matrix multiplication order 2017-04-05 16:06:07 +02:00
Thomas Krijnen 009f50abe1 Fix compilation on MSVC 2017-04-05 15:57:21 +02:00
Ken Arroyo Ohori c9267d7ace Fixed orientation bug in tapered extrusions 2017-03-30 21:11:05 -06:00
Ken Arroyo Ohori 4a340027bf IfcExtrudedAreaSolidTapered (with problems?) 2017-03-30 20:12:23 -06:00
Ken Arroyo Ohori 612f8f36db Merge pull request #14 from kenohori/cgal
Bugfixes + some validation code when creating New polyhedra
2017-03-30 19:16:48 -06:00
Ken Arroyo Ohori c1887f3f1f And the rest 2017-03-30 19:16:11 -06:00
Ken Arroyo Ohori 71ad7dd4bc Forgot one line 2017-03-30 19:06:47 -06:00
Ken Arroyo Ohori ed9676ed38 Squashed some bugs 2017-03-30 19:04:39 -06:00
Ken Arroyo Ohori a6195290f5 Merge pull request #13 from kenohori/cgal
Move shapes back to Polyhedron_3, more IFC entities supported, bugfixes
2017-03-23 20:33:52 -06:00
Ken Arroyo Ohori 583c334802 Removed sphere radius at some point 2017-03-23 20:31:41 -06:00
Ken Arroyo Ohori f8f4db72ec IfcDerivedProfileDef 2017-03-23 20:25:54 -06:00
Ken Arroyo Ohori dd206a2a52 A few more simple IFC classes to fill in things 2017-03-23 20:20:43 -06:00
Ken Arroyo Ohori 12967790ba Switched back to Polyhedron_3 for shapes. Should be checked. 2017-03-23 19:49:15 -06:00
Ken Arroyo Ohori d0b71a2e57 Merge pull request #12 from kenohori/cgal
Reorganisation, export of non-simple New polyhedra, (very slow) solution to create open meshes
2017-03-20 20:59:19 -06:00
Ken Arroyo Ohori 4061f93560 Export non-simple Nef too 2017-03-20 20:57:17 -06:00
Ken Arroyo Ohori 71d7bbd6ba Ordering things… 2017-03-20 20:06:48 -06:00
Ken Arroyo Ohori 62ca6f6a7d Checked shapes with styles. Found bug? 2017-03-20 16:45:27 -06:00
Ken Arroyo Ohori 8090bb4c00 Shapes with styles in separate file 2017-03-20 16:35:14 -06:00
Ken Arroyo Ohori cf6bd5a629 Enabled missing Cartesian transformations 2017-03-20 16:31:06 -06:00
Ken Arroyo Ohori cf9605f108 Missing Shapes, support for voids 2017-03-20 15:56:31 -06:00
Ken Arroyo Ohori 507d4778fa Merge pull request #11 from kenohori/cgal
Bugfixes in placements, half space solids and validation code
2017-03-17 19:27:34 -06:00
Ken Arroyo Ohori 95e6c14f39 Hack to solve issues with IfcHalfSpaceSolid. Not ideal. 2017-03-17 19:24:51 -06:00
Ken Arroyo Ohori 2bbe80fd28 Problems with extended kernel experiment, but this should be incorporated in any case 2017-03-17 16:12:24 -06:00
Ken Arroyo Ohori 0ffa633a6d Better validation 2017-03-16 19:30:39 -06:00
Ken Arroyo Ohori 30e88210b2 Simplified code by moving Nef creation outside 2017-03-16 19:20:41 -06:00
Ken Arroyo Ohori 98ce04ed64 Fix logic of transformations 2017-03-16 18:44:25 -06:00
Ken Arroyo Ohori d2aef0fcbd 2D Cartesian transformations (untested) 2017-03-13 22:32:12 -06:00
Ken Arroyo Ohori 38cbdbc0f4 Correct way to create trimmed parametric curves? 2017-03-13 20:22:19 -06:00
Ken Arroyo Ohori 83689a1cec Merge pull request #10 from kenohori/cgal
Basic support for (trimmed) curves
2017-03-13 19:29:20 -06:00
Ken Arroyo Ohori 81cb7f379e Ignore output files in /test/ 2017-03-13 19:26:54 -06:00
Ken Arroyo Ohori 7d16b725a3 IfcTrimmedCurve. Needs projection to closest point in curve? 2017-03-13 19:08:50 -06:00
Ken Arroyo Ohori 54331062c3 Basic curve types 2017-03-13 19:08:23 -06:00
Ken Arroyo Ohori 4419331710 Merge pull request #9 from kenohori/cgal
Fix issue with opening placements
2017-03-10 18:29:01 -06:00
Ken Arroyo Ohori 81a33309f3 Merge pull request #2 from aothms/cgal
Merge pull request #8 from kenohori/cgal
2017-03-10 18:28:17 -06:00
Ken Arroyo Ohori d02918070d Remove debug code 2017-03-10 18:26:15 -06:00
Ken Arroyo Ohori ac7099ab39 Fixed bug with opening placements 2017-03-10 18:21:15 -06:00
Ken Arroyo Ohori b5e79b2558 Debug code 2017-03-10 17:44:19 -06:00
Ken Arroyo Ohori b1df91ffd9 Merge pull request #8 from kenohori/cgal
More profiles (T, U, Z) and support for openings
2017-03-09 18:45:34 -06:00
Ken Arroyo Ohori 287fb9f0ed Convert openings and subtract them 2017-03-09 18:42:54 -06:00
Ken Arroyo Ohori 06c10dc4da Correct plane creation? 2017-03-09 17:04:37 -06:00
Ken Arroyo Ohori 3972727f9d Z profiles 2017-03-09 16:55:33 -06:00
Ken Arroyo Ohori 3e918180d2 U profiles 2017-03-09 16:46:09 -06:00
Ken Arroyo Ohori 0fbadc2985 T profiles 2017-03-09 16:29:14 -06:00
Ken Arroyo Ohori 8f89498a32 Merge pull request #1 from aothms/cgal
Merging back Thomas' latest changes
2017-03-09 09:46:48 -06:00
Thomas Krijnen 7bcd0d0a8d Apply transformation to IfcExtrudedAreaSolid 2017-03-09 15:35:29 +01:00
Thomas Krijnen 0596827b9d Fix null pointer access in tesselation of opencascade shape 2017-03-09 15:04:54 +01:00
Thomas Krijnen 619b4fbed5 Fix CGAL library linking on windows, conditional IfcTriangulatedFaceSet based on schema 2017-03-09 12:14:00 +01:00
Ken Arroyo Ohori deea0f6558 Merge pull request #7 from kenohori/cgal
Composite curves, some profiles (C, I, L)
2017-03-08 20:13:33 -06:00
Ken Arroyo Ohori 15e4e1952f I profiles (untested) 2017-03-08 20:11:45 -06:00
Ken Arroyo Ohori e922212357 L profiles 2017-03-08 19:55:17 -06:00
Ken Arroyo Ohori 0d36693780 C profiles 2017-03-08 19:38:25 -06:00
Ken Arroyo Ohori cb7ed51fc6 IfcCartesianTransformationOperator3DnonUniform (untested) 2017-03-07 20:00:32 -06:00
Ken Arroyo Ohori a3df0556aa Composite curves 2017-03-07 19:39:59 -06:00
Ken Arroyo Ohori 3f94d4af37 Skeleton for planes+halfspaces. Might be wrong. 2017-03-07 14:30:48 -06:00
Ken Arroyo Ohori 7f96b3a523 Merge pull request #6 from kenohori/cgal
Added several types of shapes and profiles
2017-03-07 13:55:15 -06:00
Ken Arroyo Ohori 27d8e86024 Hollow rectangle profiles, all tested now 2017-03-07 13:52:29 -06:00
Ken Arroyo Ohori 66048610e2 Rounded rectangles work now, def must be before rectangles 2017-03-07 13:41:04 -06:00
Ken Arroyo Ohori 31d4de228a Extrusions with holes (Nef) 2017-03-07 13:18:49 -06:00
Ken Arroyo Ohori 29021d8b06 Hollow circles 2017-03-07 13:04:22 -06:00
Ken Arroyo Ohori 1fb76a9749 Switched to normals per vertex per face 2017-03-07 12:50:06 -06:00
Ken Arroyo Ohori b299c27473 Switched to Nef_polyhedron_3. Some problems... 2017-03-06 19:28:12 -06:00
Ken Arroyo Ohori 64e385c361 Skeleton for IfcEdgeLoop and IfcOrientedEdge 2017-03-06 17:45:55 -06:00
Ken Arroyo Ohori 27b5fd0fc5 Debug code 2017-03-06 17:29:20 -06:00
Ken Arroyo Ohori 4f5ecee81e Removing duplicate points, IfcCartesianTransformationOperator3D with problems 2017-03-06 16:03:29 -06:00
Ken Arroyo Ohori ca19d447cc Zero-radius rounded rectangles 2017-03-06 15:03:57 -06:00
Ken Arroyo Ohori 484cea968e IfcTriangulatedFaceSet 2017-03-06 11:41:58 -06:00
Ken Arroyo Ohori 033a2e7482 IfcFaceBasedSurfaceModel 2017-03-06 11:28:57 -06:00
Ken Arroyo Ohori 1d3732aa62 IfcEllipseProfileDef 2017-03-06 11:23:37 -06:00
Ken Arroyo Ohori 584b2e5584 IfcTrapeziumProfileDef 2017-03-06 11:18:51 -06:00
Ken Arroyo Ohori 1bcc369c7a Rounded rectangles 2017-03-06 11:12:06 -06:00
Ken Arroyo Ohori 64bc8e9d4f Circular profiles, added missing transformation for rectangles 2017-03-06 10:52:57 -06:00
Ken Arroyo Ohori b9828b029c Right way to output more than one object 2017-03-03 15:25:45 -06:00
Ken Arroyo Ohori 749071601e Merge pull request #5 from aothms/ken_first_steps
Reorganised code, Boolean ops seem to work
2017-03-03 13:59:58 -06:00
Ken Arroyo Ohori bbe0b74f83 Boolean ops working? 2017-03-03 13:56:08 -06:00
Ken Arroyo Ohori efb8bce25a Didn’t save sphere output 2017-03-03 13:20:47 -06:00
Ken Arroyo Ohori c59ae03cb3 Fixed bug in cylinders/cones 2017-03-03 13:12:05 -06:00
Ken Arroyo Ohori 7145b1ae85 Some debugging code, checking transformations 2017-03-03 12:58:52 -06:00
Ken Arroyo Ohori 596c4f8b77 Adding some validation code 2017-03-03 09:58:17 -06:00
Ken Arroyo Ohori 302e7b2db8 IfcRightCircularCone 2017-03-02 18:42:10 -06:00
Ken Arroyo Ohori bece7053a0 IfcRightCircularCylinder 2017-03-02 18:39:18 -06:00
Ken Arroyo Ohori 82cd4056b7 IfcRectangularPyramid 2017-03-02 18:22:34 -06:00
Ken Arroyo Ohori 6297ff74f1 Boolean ops using Nef polyhedra (untested) 2017-03-02 18:11:11 -06:00
Ken Arroyo Ohori c9633273a6 Several more classes, needs testing 2017-03-02 17:11:58 -06:00
Ken Arroyo Ohori 2cb53a860d Entities for basic CSG 2017-03-02 15:09:05 -06:00
Ken Arroyo Ohori db85bebc1a Merge branch 'cgal' into ken_first_steps 2017-03-01 17:48:37 -06:00
Ken Arroyo Ohori 57fae25de8 Putting functions into files per geometric type 2017-03-01 17:48:19 -06:00
Thomas Krijnen 451ccd4e21 Merge pull request #4 from aothms/ken_first_steps
IfcManifoldSolidBrep and IfcConnectedFaceSet
2017-02-23 13:53:02 +01:00
Ken Arroyo Ohori b893f5a3f0 Take into account extrusion height 2017-02-22 18:15:27 -06:00
Ken Arroyo Ohori 64eb707996 Fixed issue with transformations? 2017-02-21 18:40:27 -06:00
Ken Arroyo Ohori c0732f5197 Trying to find out why placements don’t arrive at Triangulate() 2017-02-21 15:37:38 -06:00
Ken Arroyo Ohori e47d128b18 Brep and swept solid working now 2017-02-20 20:14:49 -06:00
Ken Arroyo Ohori 9d4463b73a Check for NULL placement 2017-02-13 15:26:06 -06:00
Ken Arroyo Ohori d9d5f17725 Should be working now 2017-02-13 11:45:17 -06:00
Ken Arroyo Ohori f4274e4b46 Skeleton for IfcExtrudedAreaSolid 2017-02-13 11:12:04 -06:00
Ken Arroyo Ohori b463dfe88a Changed --kernel parameter to --opencascade. Was conflicting with positional options for input. 2017-02-09 14:46:02 -06:00
Ken Arroyo Ohori 44634eb42d Remove implemented throws, add faces and materials 2017-02-08 17:12:56 -06:00
Ken Arroyo Ohori 1755752ab7 IfcAxis2Placement2D 2017-02-08 16:56:59 -06:00
Ken Arroyo Ohori 7051104bc7 Basic code to output triangulation, something goes wrong when getting materials... 2017-02-08 16:12:19 -06:00
Ken Arroyo Ohori 2894b0cb92 Nothing is a pointer now. Initialisation is easier… 2017-02-07 20:37:47 -06:00
Ken Arroyo Ohori 42b512960b Filling in most of the placement code. To check. 2017-02-07 20:25:04 -06:00
Ken Arroyo Ohori 96e9c8ecc1 Skeleton for IfcObjectPlacement and IfcAxis2Placement3D 2017-02-07 19:51:16 -06:00
Ken Arroyo Ohori bae84b6753 Directions as CGAL Vector_3, more robust points 2017-02-07 19:50:51 -06:00
Ken Arroyo Ohori e1702fc0cf Return value for transformation, still needs to be initialised somewhere… 2017-02-06 16:22:54 -06:00
Ken Arroyo Ohori 6aff3d0f3d Conversion result for breps 2017-02-06 16:22:36 -06:00
Thomas Krijnen 6b47e9ca1d Add GMP MPFR and CGAL include/libs to cmake and build script 2017-02-04 16:38:57 +01:00
Ken Arroyo Ohori 1525cf6bfc Polyhedra built with the incremental builder, IfcConvert crashes 2017-02-02 14:29:26 -06:00
Ken Arroyo Ohori 5724e1ac34 Face from IfcFace 2017-02-01 17:53:12 -06:00
Ken Arroyo Ohori f2a45b7057 Fixed pointer bug, wires seem okay now 2017-02-01 15:57:25 -06:00
Ken Arroyo Ohori 233aaeaca2 Points and wires 2017-01-31 19:03:15 -06:00
Ken Arroyo Ohori 9c21e05184 Skeleton reaching all the way up to points, to be filled in 2017-01-31 18:53:00 -06:00
Ken Arroyo Ohori 0821dd4702 Skeleton for IfcManifoldSolidBrep and IfcConnectedFaceSet 2017-01-27 18:40:38 -06:00
Ken Arroyo Ohori cac9c91c56 Mac metadata 2017-01-27 15:31:37 -06:00
Ken Arroyo Ohori a4264f3143 Replaced macros, added basic CGAL definitions 2017-01-27 15:31:22 -06:00
Thomas Krijnen 4d12bf7e8d Cgal kernel skeleton (#3)
Cgal kernel skeleton
2017-01-16 14:04:31 +01:00
Thomas Krijnen 3958204c1c Don't build Python wrapper on Travis 2017-01-13 20:51:12 +01:00
Thomas Krijnen d17f714dc5 Isolate geometry processing code into separate opencascade kernel 2017-01-13 20:51:12 +01:00
Thomas Krijnen 4fa7f293d6 cd to correct repository folder 2017-01-13 18:11:05 +01:00
Thomas Krijnen 7e3f96e410 Add GMP MPFR CGAL to build script 2017-01-09 17:14:49 +01:00
Thomas Krijnen 8018c9cc44 Add GMP MPFR CGAL to build script 2017-01-09 17:14:49 +01:00
177 changed files with 17430 additions and 12865 deletions
+2
View File
@@ -12,3 +12,5 @@ __pycache__
.vscode
# PyCharm files
.idea
# OSX files
.DS_Store
+7
View File
@@ -1,3 +1,10 @@
About this branch
=================
This version splits the geometry interpretation process into two steps (a) map IFC to a smaller set of schema-agnostic definitions (`ifcopenshell::geometry::taxonomy`) (b) convert these into explicit breps or polyhedra with Open CASCADE or CGAL.
Three validation options are added to IfcConvert for (a) storey containment (b) wall connectivity (c) space boundaries.
IfcOpenShell
============
IfcOpenShell is an open source ([LGPL]) software library for working with the Industry Foundation Classes ([IFC])
+168 -51
View File
@@ -18,7 +18,7 @@
################################################################################
cmake_minimum_required(VERSION 3.1.3)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON) # not necessary, but encouraged
project (IfcOpenShell)
@@ -42,6 +42,9 @@ OPTION(BUILD_GEOMSERVER "Build IfcGeomServer executable." ON)
OPTION(BUILD_CONVERT "Build IfcConvert executable." ON)
OPTION(USE_VLD "Use Visual Leak Detector for debugging memory leaks, MSVC-only." OFF)
OPTION(USE_MMAP "Adds a command line options to parse IFC files from memory mapped files using Boost.Iostreams" OFF)
OPTION(USE_VOXELS "Use voxelized geometries as a fallback mechanism to calculate quantities in IfcGeomServer" OFF)
OPTION(USE_CGAL "Use CGAL as an alternative geometry kernel implementation" OFF)
OPTION(USE_STATIC_MSVC_RUNTIME "Link to the static runtime on MSVC." OFF)
OPTION(BUILD_SHARED_LIBS "Build IfcParse and IfcGeom as shared libs (SO/DLL)." OFF)
if (${HAS_MAX})
OPTION(BUILD_IFCMAX "Build IfcMax, a 3ds Max plug-in, Windows-only." ON)
@@ -111,6 +114,15 @@ UNIFY_ENVVARS_AND_CACHE(LIBXML2_INCLUDE_DIR)
UNIFY_ENVVARS_AND_CACHE(LIBXML2_LIBRARIES)
UNIFY_ENVVARS_AND_CACHE(PCRE_LIBRARY_DIR)
UNIFY_ENVVARS_AND_CACHE(PYTHON_EXECUTABLE)
UNIFY_ENVVARS_AND_CACHE(CGAL_INCLUDE_DIR)
UNIFY_ENVVARS_AND_CACHE(CGAL_LIBRARY_DIR)
UNIFY_ENVVARS_AND_CACHE(GMP_INCLUDE_DIR)
UNIFY_ENVVARS_AND_CACHE(GMP_LIBRARY_DIR)
UNIFY_ENVVARS_AND_CACHE(MPFR_INCLUDE_DIR)
UNIFY_ENVVARS_AND_CACHE(MPFR_LIBRARY_DIR)
UNIFY_ENVVARS_AND_CACHE(VOXEL_INCLUDE_DIR)
UNIFY_ENVVARS_AND_CACHE(VOXEL_LIBRARY_DIR)
UNIFY_ENVVARS_AND_CACHE(EIGEN_DIR)
if (GLTF_SUPPORT AND BUILD_CONVERT)
UNIFY_ENVVARS_AND_CACHE(JSON_INCLUDE_DIR)
@@ -140,8 +152,10 @@ ENDMACRO()
# runtime, when doing running conda-build we pick what conda prepared for us.
IF(WIN32 AND ("$ENV{CONDA_BUILD}" STREQUAL ""))
SET(Boost_USE_STATIC_LIBS ON)
SET(Boost_USE_STATIC_RUNTIME ON)
SET(Boost_USE_MULTITHREADED ON)
if (USE_STATIC_MSVC_RUNTIME)
SET(Boost_USE_STATIC_RUNTIME ON)
endif()
ELSE()
# Disable Boost's autolinking as the libraries to be linked to are supplied
# already by CMake, and it's going to conflict if there are multiple, as is
@@ -154,20 +168,29 @@ ELSE()
ENDIF()
set(BOOST_COMPONENTS system program_options regex thread date_time)
if(USE_MMAP)
if(USE_MMAP OR USE_VOXELS)
if(MSVC)
# filesystem is necessary for the utf-16 wpath
set(BOOST_COMPONENTS ${BOOST_COMPONENTS} iostreams filesystem)
else()
set(BOOST_COMPONENTS ${BOOST_COMPONENTS} iostreams)
endif()
add_definitions(-DUSE_MMAP)
if(USE_MMAP)
add_definitions(-DUSE_MMAP)
endif()
endif()
if (IFCXML_SUPPORT)
add_definitions(-DWITH_IFCXML)
endif()
if (USE_VOXELS)
FIND_LIBRARY(libvoxel NAMES voxel libvoxel PATHS ${VOXEL_LIBRARY_DIR} NO_DEFAULT_PATH)
FIND_LIBRARY(libvoxec NAMES voxec libvoxec PATHS ${VOXEL_LIBRARY_DIR} NO_DEFAULT_PATH)
set(VOXEL_LIBRARIES ${libvoxel} ${libvoxec})
ADD_DEFINITIONS("-DUSE_VOXELS")
endif()
FIND_PACKAGE(Boost REQUIRED COMPONENTS ${BOOST_COMPONENTS})
MESSAGE(STATUS "Boost include files found in ${Boost_INCLUDE_DIRS}")
MESSAGE(STATUS "Boost libraries found in ${Boost_LIBRARY_DIRS}")
@@ -255,6 +278,61 @@ foreach(lib ${OPENCASCADE_LIBRARY_NAMES})
list(APPEND OPENCASCADE_LIBRARIES "${lib_path}")
endforeach()
list(APPEND GEOMETRY_KERNELS opencascade)
if (USE_CGAL)
add_definitions(-DIFOPSH_USE_CGAL)
list(APPEND GEOMETRY_KERNELS cgal)
SET(CGAL_LIBRARY_NAMES libCGAL_Core libCGAL_ImageIO libCGAL)
# Find CGAL
IF("${CGAL_INCLUDE_DIR}" STREQUAL "")
SET(CGAL_INCLUDE_DIR "/usr/include/" CACHE FILEPATH "CGAL header files")
MESSAGE(STATUS "Looking for CGAL include files in: ${CGAL_INCLUDE_DIR}")
MESSAGE(STATUS "Use CGAL_INCLUDE_DIR to specify another directory")
ELSE()
SET(CGAL_INCLUDE_DIR ${CGAL_INCLUDE_DIR} CACHE FILEPATH "CGAL header files")
MESSAGE(STATUS "Looking for CGAL include files in: ${CGAL_INCLUDE_DIR}")
ENDIF()
IF("${CGAL_LIBRARY_DIR}" STREQUAL "")
SET(CGAL_LIBRARY_DIR "/usr/lib/" CACHE FILEPATH "CGAL library files")
MESSAGE(STATUS "Looking for CGAL library files in: ${CGAL_LIBRARY_DIR}")
MESSAGE(STATUS "Use CGAL_LIBRARY_DIR to specify another directory")
ELSE()
SET(CGAL_LIBRARY_DIR ${CGAL_LIBRARY_DIR} CACHE FILEPATH "CGAL library files")
MESSAGE(STATUS "Looking for CGAL library files in: ${CGAL_LIBRARY_DIR}")
ENDIF()
FIND_LIBRARY(libCGAL NAMES CGAL PATHS ${CGAL_LIBRARY_DIR} NO_DEFAULT_PATH)
IF(libCGAL)
MESSAGE(STATUS "CGAL library files found")
foreach(lib ${CGAL_LIBRARY_NAMES})
string(REPLACE libCGAL "${lib}" lib_path "${libCGAL}")
list(APPEND CGAL_LIBRARIES "${lib_path}")
endforeach()
ELSE()
FILE(GLOB CGAL_LIBRARIES ${CGAL_LIBRARY_DIR}/*CGAL*.lib)
message(STATUS CGAL_LIBRARIES ${CGAL_LIBRARIES})
LIST(LENGTH CGAL_LIBRARY_NAMES num_cgal_library_names)
LIST(LENGTH CGAL_LIBRARIES num_cgal_libraries)
message(STATUS ${num_cgal_library_names} ${num_cgal_libraries})
LINK_DIRECTORIES("${CGAL_LIBRARY_DIR}")
if(NOT "${num_cgal_library_names}" STREQUAL "${num_cgal_libraries}")
MESSAGE(FATAL_ERROR "Unable to find CGAL library files, aborting")
endif()
MESSAGE(STATUS "CGAL library files found")
ENDIF()
FIND_LIBRARY(libGMP NAMES gmp mpir PATHS ${GMP_LIBRARY_DIR} NO_DEFAULT_PATH)
FIND_LIBRARY(libMPFR NAMES mpfr PATHS ${MPFR_LIBRARY_DIR} NO_DEFAULT_PATH)
IF(NOT libGMP)
MESSAGE(FATAL_ERROR "Unable to find GMP library files, aborting")
ENDIF()
IF(NOT libMPFR)
MESSAGE(FATAL_ERROR "Unable to find MPFR library files, aborting")
ENDIF()
list(APPEND CGAL_LIBRARIES "${libMPFR}")
list(APPEND CGAL_LIBRARIES "${libGMP}")
endif()
if(MSVC)
add_definitions(-DHAVE_NO_DLL)
add_debug_variants(OPENCASCADE_LIBRARIES "${OPENCASCADE_LIBRARIES}" d)
@@ -274,7 +352,9 @@ endif()
if(OCCT_STATIC)
find_package(Threads)
# OPENCASCADE_LIBRARIES repeated three times below in order to fix cyclic dependencies - use --start-group ... --end-group instead?
set(OPENCASCADE_LIBRARIES ${OPENCASCADE_LIBRARIES} ${OPENCASCADE_LIBRARIES} ${OPENCASCADE_LIBRARIES} ${OPENCASCADE_LIBRARIES} ${OPENCASCADE_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
if(NOT APPLE)
set(OPENCASCADE_LIBRARIES -Wl,--start-group ${OPENCASCADE_LIBRARIES} -Wl,--end-group ${CMAKE_THREAD_LIBS_INIT})
endif()
if (NOT APPLE AND NOT WIN32)
set(OPENCASCADE_LIBRARIES ${OPENCASCADE_LIBRARIES} "rt")
endif()
@@ -412,22 +492,25 @@ IF(MSVC)
ENDIF()
# Enforce standards-conformance on VS > 2015, older Boost versions fail to compile with this
if (MSVC_VERSION GREATER 1900 AND (Boost_MAJOR_VERSION GREATER 1 OR Boost_MINOR_VERSION GREATER 66))
add_definitions(-permissive-)
# @todo currently fails
# add_definitions(-permissive-)
endif()
if(USE_STATIC_MSVC_RUNTIME)
# Link against the static VC runtime
IF("$ENV{CONDA_BUILD}" STREQUAL "")
FOREACH(flag CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS_MINSIZEREL
CMAKE_CXX_FLAGS_RELWITHDEBINFO CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE
CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO)
IF(${flag} MATCHES "/MD")
STRING(REGEX REPLACE "/MD" "/MT" ${flag} "${${flag}}")
ENDIF()
IF(${flag} MATCHES "/MDd")
STRING(REGEX REPLACE "/MDd" "/MTd" ${flag} "${${flag}}")
ENDIF()
ENDFOREACH()
ENDIF()
endif()
# Link against the static VC runtime
# TODO Make this configurable
IF("$ENV{CONDA_BUILD}" STREQUAL "")
FOREACH(flag CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS_MINSIZEREL
CMAKE_CXX_FLAGS_RELWITHDEBINFO CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE
CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO)
IF(${flag} MATCHES "/MD")
STRING(REGEX REPLACE "/MD" "/MT" ${flag} "${${flag}}")
ENDIF()
IF(${flag} MATCHES "/MDd")
STRING(REGEX REPLACE "/MDd" "/MTd" ${flag} "${${flag}}")
ENDIF()
ENDFOREACH()
ENDIF()
ElSE()
add_definitions(-Wall -Wextra)
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
@@ -447,6 +530,7 @@ endif()
INCLUDE_DIRECTORIES(${INCLUDE_DIRECTORIES} ${OCC_INCLUDE_DIR} ${OPENCOLLADA_INCLUDE_DIRS}
${Boost_INCLUDE_DIRS} ${LIBXML2_INCLUDE_DIR} ${JSON_INCLUDE_DIR}
${CGAL_INCLUDE_DIR} ${GMP_INCLUDE_DIR} ${MPFR_INCLUDE_DIR} ${VOXEL_INCLUDE_DIR} ${EIGEN_DIR}
)
function(files_for_ifc_version IFC_VERSION RESULT_NAME)
@@ -459,7 +543,7 @@ function(files_for_ifc_version IFC_VERSION RESULT_NAME)
)
endfunction()
set(SCHEMA_VERSIONS "2x3" "4" "4x1" "4x2")
set(SCHEMA_VERSIONS "2x3" "4") # "4x1" "4x2")
if(COMPILE_SCHEMA)
# @todo, this appears to be untested at the moment
@@ -526,15 +610,15 @@ endif()
set(IFCOPENSHELL_LIBRARIES IfcParse)
if (BUILD_IFCGEOM)
foreach(s ${SCHEMA_VERSIONS})
set(IFCGEOM_SCHEMA_LIBRARIES ${IFCGEOM_SCHEMA_LIBRARIES} IfcGeom_ifc${s})
set(IFCGEOM_SCHEMA_LIBRARIES ${IFCGEOM_SCHEMA_LIBRARIES} geometry_mapping_ifc${s})
endforeach()
set(IFCOPENSHELL_LIBRARIES ${IFCOPENSHELL_LIBRARIES} IfcGeom ${IFCGEOM_SCHEMA_LIBRARIES} IfcGeom ${IFCGEOM_SCHEMA_LIBRARIES})
set(IFCOPENSHELL_LIBRARIES ${IFCOPENSHELL_LIBRARIES} IfcGeom geometry_mappings ${IFCGEOM_SCHEMA_LIBRARIES})
endif()
if (BUILD_CONVERT)
foreach(s ${SCHEMA_VERSIONS})
set(SERIALIZER_SCHEMA_LIBRARIES ${SERIALIZER_SCHEMA_LIBRARIES} Serializers_ifc${s})
set(SERIALIZER_SCHEMA_LIBRARIES ${SERIALIZER_SCHEMA_LIBRARIES} serializers_ifc${s})
endforeach()
set(IFCOPENSHELL_LIBRARIES ${IFCOPENSHELL_LIBRARIES} Serializers ${SERIALIZER_SCHEMA_LIBRARIES})
set(IFCOPENSHELL_LIBRARIES ${IFCOPENSHELL_LIBRARIES} serializers ${SERIALIZER_SCHEMA_LIBRARIES})
endif()
# IfcParse
@@ -549,36 +633,67 @@ TARGET_LINK_LIBRARIES(IfcParse ${Boost_LIBRARIES} ${BCRYPT_LIBRARIES} ${LIBXML2_
if (BUILD_IFCGEOM)
# IfcGeom
file(GLOB IFCGEOM_H_FILES ../src/ifcgeom/*.h)
file(GLOB IFCGEOM_CPP_FILES ../src/ifcgeom/*.cpp)
foreach(kernel ${GEOMETRY_KERNELS})
string(TOUPPER ${kernel} KERNEL_UPPER)
file(GLOB IFCGEOM_H_FILES ../src/ifcgeom/kernels/${kernel}/*.h)
file(GLOB IFCGEOM_CPP_FILES ../src/ifcgeom/kernels/${kernel}/*.cpp)
set(IFCGEOM_FILES ${IFCGEOM_CPP_FILES} ${IFCGEOM_H_FILES})
foreach(s ${SCHEMA_VERSIONS})
add_library(IfcGeom_ifc${s} STATIC ${IFCGEOM_FILES})
set_target_properties(IfcGeom_ifc${s} PROPERTIES COMPILE_FLAGS "-DIFC_GEOM_EXPORTS -DIfcSchema=Ifc${s}")
TARGET_LINK_LIBRARIES(IfcGeom_ifc${s} IfcParse ${OPENCASCADE_LIBRARIES})
add_library(geometry_kernel_${kernel} STATIC ${IFCGEOM_FILES})
set_target_properties(geometry_kernel_${kernel} PROPERTIES COMPILE_FLAGS "-DIFC_GEOM_EXPORTS")
target_link_libraries(geometry_kernel_${kernel} ${${KERNEL_UPPER}_LIBRARIES})
list(APPEND kernel_libraries geometry_kernel_${kernel})
endforeach()
# IfcGeom (schema agnostic)
file(GLOB SCHEMA_AGNOSTIC_H_FILES ../src/ifcgeom_schema_agnostic/*.h)
file(GLOB SCHEMA_AGNOSTIC_CPP_FILES ../src/ifcgeom_schema_agnostic/*.cpp)
file(GLOB SCHEMA_AGNOSTIC_H_FILES ../src/ifcgeom/kernel_agnostic/*.h)
file(GLOB SCHEMA_AGNOSTIC_CPP_FILES ../src/ifcgeom/kernel_agnostic/*.cpp)
set(SCHEMA_AGNOSTIC_FILES ${SCHEMA_AGNOSTIC_H_FILES} ${SCHEMA_AGNOSTIC_CPP_FILES})
add_library(IfcGeom ${SCHEMA_AGNOSTIC_FILES})
set_target_properties(IfcGeom PROPERTIES COMPILE_FLAGS -DIFC_GEOM_EXPORTS)
add_library(geometry_kernels ${SCHEMA_AGNOSTIC_FILES})
set_target_properties(geometry_kernels PROPERTIES COMPILE_FLAGS -DIFC_GEOM_EXPORTS)
target_link_libraries(geometry_kernels ${kernel_libraries})
foreach(schema ${SCHEMA_VERSIONS})
file(GLOB IFCGEOM_I_FILES ../src/ifcgeom/schema/*.i)
file(GLOB IFCGEOM_H_FILES ../src/ifcgeom/schema/*.h)
file(GLOB IFCGEOM_CPP_FILES ../src/ifcgeom/schema/*.cpp)
set(IFCGEOM_FILES ${IFCGEOM_CPP_FILES} ${IFCGEOM_H_FILES} ${IFCGEOM_I_FILES})
add_library(geometry_mapping_ifc${schema} STATIC ${IFCGEOM_FILES})
set_target_properties(geometry_mapping_ifc${schema} PROPERTIES COMPILE_FLAGS "-DIFC_GEOM_EXPORTS -DIfcSchema=Ifc${schema}")
target_link_libraries(geometry_mapping_ifc${schema} IfcParse)
list(APPEND mapping_libraries geometry_mapping_ifc${schema})
endforeach()
file(GLOB SCHEMA_AGNOSTIC_H_FILES ../src/ifcgeom/*.h)
file(GLOB SCHEMA_AGNOSTIC_CPP_FILES ../src/ifcgeom/*.cpp)
set(SCHEMA_AGNOSTIC_FILES ${SCHEMA_AGNOSTIC_H_FILES} ${SCHEMA_AGNOSTIC_CPP_FILES})
add_library(geometry_mappings ${SCHEMA_AGNOSTIC_FILES})
set_target_properties(geometry_mappings PROPERTIES COMPILE_FLAGS -DIFC_GEOM_EXPORTS)
target_link_libraries(geometry_mappings ${mapping_libraries})
if (UNIX)
find_package(Threads)
endif()
TARGET_LINK_LIBRARIES(IfcGeom ${IFCGEOM_SCHEMA_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
file(GLOB SCHEMA_AGNOSTIC_H_FILES ../src/ifcgeom/schema_agnostic/*.h)
file(GLOB SCHEMA_AGNOSTIC_CPP_FILES ../src/ifcgeom/schema_agnostic/*.cpp)
set(SCHEMA_AGNOSTIC_FILES ${SCHEMA_AGNOSTIC_H_FILES} ${SCHEMA_AGNOSTIC_CPP_FILES})
add_library(IfcGeom ${SCHEMA_AGNOSTIC_FILES})
set_target_properties(IfcGeom PROPERTIES COMPILE_FLAGS -DIFC_GEOM_EXPORTS)
target_link_libraries(IfcGeom geometry_mappings geometry_kernels ${CMAKE_THREAD_LIBS_INIT})
endif(BUILD_IFCGEOM)
if (BUILD_CONVERT)
# Serializers
# serializers
file(GLOB SERIALIZERS_H_FILES ../src/serializers/*.h)
file(GLOB SERIALIZERS_CPP_FILES ../src/serializers/*.cpp)
set(SERIALIZERS_FILES ${SERIALIZERS_H_FILES} ${SERIALIZERS_CPP_FILES})
@@ -587,19 +702,19 @@ file(GLOB SERIALIZERS_S_CPP_FILES ../src/serializers/schema_dependent/*.cpp)
set(SERIALIZERS_S_FILES ${SERIALIZERS_S_H_FILES} ${SERIALIZERS_S_CPP_FILES})
foreach(s ${SCHEMA_VERSIONS})
add_library(Serializers_ifc${s} STATIC ${SERIALIZERS_S_FILES})
set_target_properties(Serializers_ifc${s} PROPERTIES COMPILE_FLAGS "-DIFC_GEOM_EXPORTS -DIfcSchema=Ifc${s} ${CONVERT_PRECISION}")
TARGET_LINK_LIBRARIES(Serializers_ifc${s} IfcGeom ${OPENCASCADE_LIBRARIES})
add_library(serializers_ifc${s} STATIC ${SERIALIZERS_S_FILES})
set_target_properties(serializers_ifc${s} PROPERTIES COMPILE_FLAGS "-DIFC_GEOM_EXPORTS -DIfcSchema=Ifc${s} ${CONVERT_PRECISION}")
TARGET_LINK_LIBRARIES(serializers_ifc${s} IfcGeom ${OPENCASCADE_LIBRARIES})
endforeach()
add_library(Serializers ${SERIALIZERS_FILES})
set_target_properties(Serializers PROPERTIES COMPILE_FLAGS "-DIFC_GEOM_EXPORTS ${CONVERT_PRECISION}")
add_library(serializers ${SERIALIZERS_FILES})
set_target_properties(serializers PROPERTIES COMPILE_FLAGS "-DIFC_GEOM_EXPORTS ${CONVERT_PRECISION}")
TARGET_LINK_LIBRARIES(Serializers ${SERIALIZER_SCHEMA_LIBRARIES})
TARGET_LINK_LIBRARIES(serializers ${SERIALIZER_SCHEMA_LIBRARIES})
# IfcConvert
file(GLOB IFCCONVERT_CPP_FILES ../src/ifcconvert/*.cpp)
file(GLOB IFCCONVERT_H_FILES ../src/ifcconvert/*.h)
file(GLOB IFCCONVERT_CPP_FILES ../src/ifcconvert/IfcConvert.cpp)
file(GLOB IFCCONVERT_H_FILES ../src/ifcconvert/skip-for-now.h)
set(IFCCONVERT_FILES ${IFCCONVERT_CPP_FILES} ${IFCCONVERT_H_FILES})
ADD_EXECUTABLE(IfcConvert ${IFCCONVERT_FILES})
set_target_properties(IfcConvert PROPERTIES COMPILE_FLAGS "${CONVERT_PRECISION}")
@@ -628,7 +743,7 @@ file(GLOB CPP_FILES ../src/ifcgeomserver/*.cpp)
file(GLOB H_FILES ../src/ifcgeomserver/*.h)
set(SOURCE_FILES ${CPP_FILES} ${H_FILES})
ADD_EXECUTABLE(IfcGeomServer ${SOURCE_FILES})
TARGET_LINK_LIBRARIES(IfcGeomServer ${IFCOPENSHELL_LIBRARIES} ${OPENCASCADE_LIBRARIES} ${Boost_LIBRARIES})
TARGET_LINK_LIBRARIES(IfcGeomServer ${IFCOPENSHELL_LIBRARIES} ${OPENCASCADE_LIBRARIES} ${Boost_LIBRARIES} ${VOXEL_LIBRARIES})
if ((NOT WIN32) AND BUILD_SHARED_LIBS)
SET_INSTALL_RPATHS(IfcGeomServer "${IFCOPENSHELL_LIBARY_DIR};${OCC_LIBRARY_DIR};${Boost_LIBRARY_DIRS}")
@@ -671,10 +786,12 @@ INSTALL(FILES ${IFCGEOM_H_FILES}
)
INSTALL(FILES ${SCHEMA_AGNOSTIC_H_FILES}
DESTINATION ${INCLUDEDIR}/ifcgeom_schema_agnostic
DESTINATION ${INCLUDEDIR}/ifcgeom/schema_agnostic
)
INSTALL(TARGETS ${IFCGEOM_SCHEMA_LIBRARIES} IfcGeom
INSTALL(TARGETS IfcGeom ${IfcGeom_libraries}
geometry_mappings ${mapping_libraries}
geometry_kernels ${kernel_libraries}
ARCHIVE DESTINATION ${LIBDIR}
LIBRARY DESTINATION ${LIBDIR}
RUNTIME DESTINATION ${BINDIR}
@@ -682,7 +799,7 @@ INSTALL(TARGETS ${IFCGEOM_SCHEMA_LIBRARIES} IfcGeom
endif()
if(BUILD_CONVERT)
INSTALL(TARGETS Serializers ${SERIALIZER_SCHEMA_LIBRARIES}
INSTALL(TARGETS serializers ${SERIALIZER_SCHEMA_LIBRARIES}
ARCHIVE DESTINATION ${LIBDIR}
LIBRARY DESTINATION ${LIBDIR}
RUNTIME DESTINATION ${BINDIR}
+92 -46
View File
@@ -84,7 +84,7 @@ OCE_VERSION="0.18"
# OCCT_VERSION="7.2.0"
# OCCT_HASH="88af392"
OCCT_VERSION="7.3.0p3"
BOOST_VERSION="1.59.0"
BOOST_VERSION="1.69.0"
#PCRE_VERSION="8.39"
PCRE_VERSION="8.41"
#LIBXML2_VERSION="2.9.3"
@@ -95,8 +95,9 @@ SWIG_VERSION="3.0.12"
#SWIG_VERSION="4.0.0"
#OPENCOLLADA_VERSION="v1.6.63"
OPENCOLLADA_VERSION="v1.6.68"
GMP_VERSION="6.1.2"
MPFR_VERSION="3.1.5"
CGAL_VERSION="5.2"
# binaries
cp="cp"
@@ -216,24 +217,19 @@ cecho(""" - How many compiler processes may be run in parallel.
dependency_tree = {
'IfcParse': ('boost', 'libxml2'),
'IfcGeom': ('IfcParse', 'occ'),
'IfcGeom': ('IfcParse', 'occ', 'cgal', 'voxel', 'eigen'),
'IfcConvert': ('IfcGeom', 'OpenCOLLADA', 'json'),
'OpenCOLLADA': ('libxml2', 'pcre'),
'IfcGeomServer': ('IfcGeom',),
'IfcGeomServer': ('IfcGeom', ),
'IfcOpenShell-Python': ('python', 'swig', 'IfcGeom'),
'voxel': ('occ',),
'swig': ('pcre',),
'boost': (),
'libxml2': (),
'python': (),
'swig': (),
'occ': (),
'pcre': (),
'json': ()
}
def v(dep):
yield dep
for d in dependency_tree[dep]:
for d in dependency_tree.get(dep, []):
for x in v(d):
yield x
@@ -251,13 +247,13 @@ PIC = "-fPIC" if BUILD_STATIC else ""
if len(tgts):
targets = set(sum((list(v(target)) for target in tgts), []))
else:
targets = set(dependency_tree.keys())
targets = set(tuple(dependency_tree.keys()) + sum(dependency_tree.values(), ()))
print("Building:", *sorted(targets, key=lambda t: len(list(v(t)))))
# Check that required tools are in PATH
for cmd in [git, bunzip2, tar, cc, cplusplus, autoconf, automake, yacc, make, "patch"]:
for cmd in [git, bunzip2, tar, cc, cplusplus, autoconf, automake, yacc, make, "patch", "m4", "cmake"]:
if which(cmd) is None:
raise ValueError("Required tool '%s' not installed or not added to PATH" % (cmd,))
@@ -310,17 +306,22 @@ BOOST_LOCATION="http://downloads.sourceforge.net/project/boost/boost/%s/boost_%s
def run_autoconf(arg1, configure_args, cwd):
configure_path = os.path.realpath(os.path.join(cwd, "..", "configure"))
install_dir = os.path.realpath("%s/install/%s" % (DEPS_DIR, arg1))
if not os.path.exists(install_dir):
# Some (MPFR) need to have prefix dir manually created
os.makedirs(install_dir)
if not os.path.exists(configure_path):
run([bash, "./autogen.sh"], cwd=os.path.realpath(os.path.join(cwd, ".."))) # only run autogen.sh in the directory it is located and use cwd to achieve that in order to not mess up things
# Using `sh` over `bash` fixes issues with building swig
run(["/bin/sh", "../configure"]+configure_args+["--prefix=%s" % (os.path.realpath("%s/install/%s" % (DEPS_DIR, arg1)),)], cwd=cwd)
run(["/bin/sh", "../configure"]+configure_args+["--prefix=%s" % install_dir], cwd=cwd)
def run_cmake(arg1, cmake_args, cmake_dir=None, cwd=None):
if cmake_dir is None:
P=".."
else:
P=cmake_dir
cmake_path= os.path.join(DEPS_DIR, "install", "cmake-%s" % (CMAKE_VERSION,), "bin", "cmake")
# @todo this is already in v0.6
cmake_path = "cmake"
run([cmake_path, P]+cmake_args+["-DCMAKE_BUILD_TYPE=%s" % (BUILD_CFG,)], cwd=cwd)
def git_clone_or_pull_repository(clone_url, target_dir, revision=None):
@@ -428,7 +429,7 @@ def build_dependency(name, mode, build_tool_args, download_url, download_name, d
logger.info( "\rInstalled %s \n" % (name,))
else:
logger.info( "\rConfiguring %s..." % (name,))
run([bash, "./bootstrap.sh"], cwd=extract_dir)
run([bash, "./bootstrap.sh"] + (['--with-toolset=clang'] if "clang" in os.environ.get('CXX', '') else []), cwd=extract_dir)
logger.info("\rBuilding %s... " % (name,))
run(["./b2", "-j%s" % (IFCOS_NUM_BUILD_PROCS,)]+build_tool_args, cwd=extract_dir)
logger.info("\rInstalling %s... " % (name,))
@@ -481,27 +482,6 @@ os.environ["CXXFLAGS"] = CXXFLAGS
os.environ["CFLAGS"] = CFLAGS
os.environ["LDFLAGS"] = LDFLAGS
# Some dependencies need a more recent CMake version than most distros provide
build_dependency(name="cmake-%s" % (CMAKE_VERSION,), mode="autoconf", build_tool_args=[], download_url="https://cmake.org/files/v%s" % (CMAKE_VERSION_2,), download_name="cmake-%s.tar.gz" % (CMAKE_VERSION,))
# Extract compiler flags from CMake to harmonize settings with other autoconf dependencies
CMAKE_FLAG_EXTRACT_DIR="ifcopenshell_cmake_test_%s" % (time.time(),)
# was sp.check_output([bash, "-c", "cat /dev/urandom | env LC_CTYPE=C tr -dc 'a-zA-Z0-9' | head -c 32"]), in bash script, unclear what the exact required format is and whether it's needed
if os.path.exists(CMAKE_FLAG_EXTRACT_DIR):
shutil.rmtree(CMAKE_FLAG_EXTRACT_DIR)
os.makedirs(CMAKE_FLAG_EXTRACT_DIR)
BUILD_CFG_UPPER=BUILD_CFG.upper()
for FL in ["C", "CXX"]:
run([bash, "-c", """echo "
message(\"\${CMAKE_%s_FLAGS_%s}\")
" > CMakeLists.txt""" % (FL, BUILD_CFG_UPPER)], cwd=CMAKE_FLAG_EXTRACT_DIR)
FL="%sFLAGS" % (FL,)
FLM="%sFLAGS_MINIMAL" % (FL,)
# @TODO: bash code unclear
# exec("%sFLAGS=%s" % (FL, sp.check_output([os.path.join(DEPS_DIR, "install", "cmake-%s" % (CMAKE_VERSION,), "bin", "cmake"), "."
# declare ${FL}FLAGS_MINIMAL="`$DEPS_DIR/install/cmake-$CMAKE_VERSION/bin/cmake . 2>&1 >/dev/null` ${!FLM}"
shutil.rmtree(CMAKE_FLAG_EXTRACT_DIR)
if "json" in targets:
json_url = "https://github.com/nlohmann/json/releases/download/{JSON_VERSION}/json.hpp".format(**locals())
json_install_path = "{DEPS_DIR}/install/json/nlohmann/json.hpp".format(**locals())
@@ -509,6 +489,9 @@ if "json" in targets:
os.makedirs(os.path.dirname(json_install_path))
if not os.path.exists(json_install_path):
urlretrieve(json_url, json_install_path)
if "eigen" in targets:
git_clone_or_pull_repository("https://gitlab.com/libeigen/eigen.git", "{DEPS_DIR}/install/eigen-3.3.7".format(**locals()), revision="3.3.7")
if "pcre" in targets:
build_dependency(
@@ -547,6 +530,8 @@ if USE_OCCT and "occ" in targets:
patch="./patches/occt/enable-exception-handling.patch",
revision="V" + OCCT_VERSION.replace('.', '_')
)
occ_include_dir = "{DEPS_DIR}/install/occt-{OCCT_VERSION}/include/opencascade".format(**locals())
occ_library_dir = "{DEPS_DIR}/install/occt-{OCCT_VERSION}/lib".format(**locals())
elif "occ" in targets:
build_dependency(
name="oce-{OCE_VERSION}".format(**locals()),
@@ -563,6 +548,9 @@ elif "occ" in targets:
download_url="https://github.com/tpaviot/oce/archive/",
download_name="OCE-{OCE_VERSION}.tar.gz".format(**locals())
)
occ_include_dir = "{DEPS_DIR}/install/oce-{OCE_VERSION}/include/oce".format(**locals())
occ_library_dir = "{DEPS_DIR}/install/oce-{OCE_VERSION}/lib"
if "libxml2" in targets:
build_dependency(
@@ -646,16 +634,64 @@ if "boost" in targets:
"--with-thread",
"--with-date_time",
"--with-iostreams",
"link={LINK_TYPE}".format(**locals())
"link={LINK_TYPE}".format(**locals()),
] + \
BOOST_ADDRESS_MODEL + \
list(map(str_concat("cxxflags"), CXXFLAGS.strip().split(' '))) + \
list(map(str_concat("linkflags"), LDFLAGS.strip().split(' '))) + \
(["toolset=clang"] if "clang" in os.environ.get('CXX', '') else []) +\
["stage", "-s", "NO_BZIP2=1"],
download_url="http://downloads.sourceforge.net/project/boost/boost/{BOOST_VERSION}/".format(**locals()),
download_name="boost_{BOOST_VERSION_UNDERSCORE}.tar.bz2".format(**locals())
)
if "cgal" in targets:
build_dependency(name="gmp-%s" % (GMP_VERSION,), mode="autoconf", build_tool_args=["--disable-shared", "--with-pic"], download_url="https://ftp.gnu.org/gnu/gmp/", download_name="gmp-%s.tar.bz2" % (GMP_VERSION,))
build_dependency(name="mpfr-%s" % (MPFR_VERSION,), mode="autoconf", build_tool_args=["--disable-shared", "--with-gmp=%s/install/gmp-%s" % (DEPS_DIR, GMP_VERSION)], download_url="http://www.mpfr.org/mpfr-%s/" % (MPFR_VERSION,), download_name="mpfr-%s.tar.bz2" % (MPFR_VERSION,))
OLD_BUILD_CFG = BUILD_CFG
if BUILD_CFG != "Debug":
# CGAL only supports Debug and Release for CMAKE_BUILD_TYPE
BUILD_CFG = "Release"
build_dependency(
name="cgal-{CGAL_VERSION}".format(**locals()),
mode="cmake",
# applied upstream
# patch="./patches/cgal/cgal_4768.patch",
build_tool_args=[
"-DGMP_LIBRARIES=%s/install/gmp-%s/lib/libgmp.a" % (DEPS_DIR, GMP_VERSION),
"-DGMP_INCLUDE_DIR=%s/install/gmp-%s/include" % (DEPS_DIR, GMP_VERSION),
"-DMPFR_LIBRARIES=%s/install/mpfr-%s/lib/libmpfr.a" % (DEPS_DIR, MPFR_VERSION),
"-DMPFR_INCLUDE_DIR=%s/install/mpfr-%s/include" % (DEPS_DIR, MPFR_VERSION),
"-DBoost_INCLUDE_DIR=%s/install/boost-%s" % (DEPS_DIR, BOOST_VERSION),
"-DCMAKE_INSTALL_PREFIX=%s/install/cgal-%s/" % (DEPS_DIR, CGAL_VERSION),
# for now as the IfOpSh CMakeL expects libraries, we can probably safely turn this off though
"-DCGAL_HEADER_ONLY=OFF",
"-DBUILD_SHARED_LIBS=Off"],
download_url="https://github.com/CGAL/cgal.git",
download_name="cgal",
download_tool=download_tool_git,
revision="v{CGAL_VERSION}".format(**locals())
)
BUILD_CFG = OLD_BUILD_CFG
if "voxel" in targets:
build_dependency(
"voxel",
"cmake",
build_tool_args=[
"-DIFC_SUPPORT=Off",
"-DOCC_INCLUDE_DIR=" +occ_include_dir,
"-DOCC_LIBRARY_DIR=" +occ_library_dir,
"-DCMAKE_INSTALL_PREFIX={DEPS_DIR}/install/voxel".format(**locals()),
"-DBOOST_ROOT=" "{DEPS_DIR}/install/boost-{BOOST_VERSION}".format(**locals())
],
download_url="https://github.com/opensourceBIM/voxel.git",
download_name="voxel",
download_tool=download_tool_git,
revision="master"
)
cecho("Building IfcOpenShell:", GREEN)
IFCOS_DIR=os.path.join(DEPS_DIR, "build", "ifcopenshell")
@@ -682,24 +718,32 @@ cmake_args=[
"-DCMAKE_INSTALL_PREFIX=" "{DEPS_DIR}/install/ifcopenshell".format(**locals()),
"-DBOOST_ROOT=" "{DEPS_DIR}/install/boost-{BOOST_VERSION}".format(**locals()),
"-DGLTF_SUPPORT=" "ON",
"-DJSON_INCLUDE_DIR=" "{DEPS_DIR}/install/json".format(**locals())
"-DJSON_INCLUDE_DIR=" "{DEPS_DIR}/install/json".format(**locals()),
"-DEIGEN_DIR=" "{DEPS_DIR}/install/eigen-3.3.7".format(**locals())
]
if "occ" in targets and USE_OCCT:
occ_include_dir = "{DEPS_DIR}/install/occt-{OCCT_VERSION}/include/opencascade".format(**locals())
occ_library_dir = "{DEPS_DIR}/install/occt-{OCCT_VERSION}/lib".format(**locals())
if "occ" in targets:
cmake_args.extend([
"-DOCC_INCLUDE_DIR=" +occ_include_dir,
"-DOCC_LIBRARY_DIR=" +occ_library_dir
])
elif "occ" in targets:
occ_include_dir = "{DEPS_DIR}/install/oce-{OCE_VERSION}/include/oce".format(**locals())
occ_library_dir = "{DEPS_DIR}/install/oce-{OCE_VERSION}/lib"
cmake_args.extend([
"-DOCC_INCLUDE_DIR=" +occ_include_dir,
"-DOCC_LIBRARY_DIR=" +occ_library_dir
])
if "cgal" in targets:
cmake_args.extend([
"-DCGAL_INCLUDE_DIR=" "{DEPS_DIR}/install/cgal-{CGAL_VERSION}/include".format(**locals()),
"-DCGAL_LIBRARY_DIR=" "{DEPS_DIR}/install/cgal-{CGAL_VERSION}/lib".format(**locals()),
"-DGMP_INCLUDE_DIR=" "{DEPS_DIR}/install/gmp-{GMP_VERSION}/include".format(**locals()),
"-DGMP_LIBRARY_DIR=" "{DEPS_DIR}/install/gmp-{GMP_VERSION}/lib".format(**locals()),
"-DMPFR_INCLUDE_DIR=" "{DEPS_DIR}/install/mpfr-{MPFR_VERSION}/include".format(**locals()),
"-DMPFR_LIBRARY_DIR=" "{DEPS_DIR}/install/mpfr-{MPFR_VERSION}/lib".format(**locals()),
"-DUSE_CGAL=ON"
])
if "OpenCOLLADA" in targets:
cmake_args.extend([
"-DOPENCOLLADA_INCLUDE_DIR=" "{DEPS_DIR}/install/OpenCOLLADA/include/opencollada".format(**locals()),
@@ -756,6 +800,8 @@ if "IfcOpenShell-Python" in targets:
"-DPYTHON_LIBRARY=" +PYTHON_LIBRARY,
"-DPYTHON_EXECUTABLE=" +PYTHON_EXECUTABLE,
"-DPYTHON_INCLUDE_DIR=" +PYTHON_INCLUDE,
"-DUSE_CGAL=ON",
"-DEIGEN_DIR=" "{DEPS_DIR}/install/eigen-3.3.7".format(**locals()),
"-DSWIG_EXECUTABLE=" "{DEPS_DIR}/install/swig/bin/swig".format(**locals()),
"-DCMAKE_INSTALL_PREFIX=" "{DEPS_DIR}/install/ifcopenshell/tmp".format(**locals()),
"-DLIBXML2_INCLUDE_DIR=" "{DEPS_DIR}/install/libxml2-{LIBXML2_VERSION}/include/libxml2".format(**locals()),
+38
View File
@@ -0,0 +1,38 @@
From a86712d1aae9a1a6d2004db3fc8fcdac6ef6b782 Mon Sep 17 00:00:00 2001
From: Thomas Krijnen <t.krijnen@gmail.com>
Date: Sun, 7 Jun 2020 15:25:18 +0200
Subject: [PATCH] Don't segfault on empty Nef_nary_x
---
Nef_3/include/CGAL/Nef_nary_intersection_3.h | 3 ++-
Nef_3/include/CGAL/Nef_nary_union_3.h | 3 ++-
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/Nef_3/include/CGAL/Nef_nary_intersection_3.h b/Nef_3/include/CGAL/Nef_nary_intersection_3.h
index 70fc5791fae..5178705dc2f 100644
--- a/Nef_3/include/CGAL/Nef_nary_intersection_3.h
+++ b/Nef_3/include/CGAL/Nef_nary_intersection_3.h
@@ -52,7 +52,8 @@ class Nef_nary_intersection_3 {
}
Polyhedron get_intersection() {
-
+ if (queue.empty())
+ return empty;
while(queue.size() > 1)
intersect();
inserted = 0;
diff --git a/Nef_3/include/CGAL/Nef_nary_union_3.h b/Nef_3/include/CGAL/Nef_nary_union_3.h
index ecbbae2db59..c8663bb911b 100644
--- a/Nef_3/include/CGAL/Nef_nary_union_3.h
+++ b/Nef_3/include/CGAL/Nef_nary_union_3.h
@@ -52,7 +52,8 @@ class Nef_nary_union_3 {
}
Polyhedron get_union() {
-
+ if (queue.empty())
+ return empty;
while(queue.size() > 1)
unite();
inserted = 0;
+4 -9
View File
@@ -38,18 +38,13 @@
#include <Standard_Version.hxx>
#ifdef USE_IFC4
#include "../ifcparse/Ifc4.h"
#define IfcSchema Ifc4
#else
#include "../ifcparse/Ifc2x3.h"
#define IfcSchema Ifc2x3
#endif
#include "../ifcparse/macros.h"
#include "../ifcparse/Ifc2x3.h"
#include "../ifcparse/IfcBaseClass.h"
#include "../ifcparse/IfcHierarchyHelper.h"
#include "../ifcgeom/IfcGeom.h"
#include "../ifcgeom_schema_agnostic/Serialization.h"
#include "../ifcgeom/schema_agnostic/Serialization.h"
#if USE_VLD
#include <vld.h>
+6 -9
View File
@@ -33,18 +33,15 @@
#include <BRepGProp.hxx>
#include <GProp_GProps.hxx>
#ifdef USE_IFC4
#include "../ifcparse/Ifc4.h"
#define IfcSchema Ifc4
#else
#include "../ifcparse/Ifc2x3.h"
#define IfcSchema Ifc2x3
#endif
#include <Precision.hxx>
#define IfcSchema Ifc2x3
#include "../ifcparse/macros.h"
#include "../ifcparse/Ifc2x3.h"
#include "../ifcparse/IfcBaseClass.h"
#include "../ifcparse/IfcHierarchyHelper.h"
#include "../ifcgeom/IfcGeom.h"
#include "../ifcgeom_schema_agnostic/Serialization.h"
#include "../ifcgeom/schema_agnostic/Serialization.h"
#if USE_VLD
#include <vld.h>
+73 -42
View File
@@ -34,9 +34,9 @@
#include "../serializers/XmlSerializer.h"
#include "../serializers/SvgSerializer.h"
#include "../ifcgeom_schema_agnostic/IfcGeomFilter.h"
#include "../ifcgeom_schema_agnostic/IfcGeomIterator.h"
#include "../ifcgeom_schema_agnostic/IfcGeomRenderStyles.h"
#include "../ifcgeom/schema_agnostic/IfcGeomFilter.h"
#include "../ifcgeom/schema_agnostic/IfcGeomIterator.h"
#include "../ifcgeom/schema_agnostic/IfcGeomRenderStyles.h"
#include "../ifcparse/utils.h"
@@ -142,9 +142,17 @@ bool file_exists(const std::string& filename) {
static std::basic_stringstream<path_t::value_type> log_stream;
void write_log(bool);
/*
void fix_quantities(IfcParse::IfcFile&, bool, bool, bool);
void fix_spaceboundaries(IfcParse::IfcFile&, bool, bool, bool);
void fix_storeycontainment(IfcParse::IfcFile&, bool, bool, bool);
void fix_wallconnectivity(IfcParse::IfcFile&, bool, bool, bool);
*/
std::string format_duration(time_t start, time_t end);
/// @todo make the filters non-global
IfcGeom::entity_filter entity_filter; // Entity filter is used always by default.
IfcGeom::layer_filter layer_filter;
@@ -194,6 +202,7 @@ int main(int argc, char** argv) {
exclusion_traverse_filter exclude_traverse_filter;
path_t filter_filename;
path_t default_material_filename;
std::string geometry_kernel;
std::string log_format;
po::options_description generic_options("Command line options");
@@ -214,16 +223,22 @@ int main(int argc, char** argv) {
#endif
("input-file", new po::typed_value<path_t, char_t>(0), "input IFC file")
("output-file", new po::typed_value<path_t, char_t>(0), "output geometry file");
po::options_description ifc_options("IFC options");
ifc_options.add_options()
("calculate-quantities", "Calculate or fix the physical quantity definitions "
"based on an interpretation of the geometry when exporting IFC");
"based on an interpretation of the geometry when exporting IFC")
("fix-space-boundaries", "Calculate or fix space boundary geometries "
"when exporting IFC")
("fix-storey-containment", "Calculate or fix containment in building storeys")
("fix-wall-connectivity", "Calculate or fix wall connectivity relationships");
int num_threads;
po::options_description geom_options("Geometry options");
geom_options.add_options()
("kernel", po::value<std::string>(&geometry_kernel)->default_value("opencascade"),
"Geometry kernel to use (opencascade or cgal).")
("threads,j", po::value<int>(&num_threads)->default_value(1),
"Number of parallel processing threads for geometry interpretation.")
("plan",
@@ -571,9 +586,20 @@ int main(int argc, char** argv) {
time(&start);
std::ofstream fs(output_filename.c_str());
if (fs.is_open()) {
/*
if (vmap.count("calculate-quantities")) {
fix_quantities(*ifc_file, no_progress, quiet, stderr_progress);
}
if (vmap.count("fix-space-boundaries")) {
fix_spaceboundaries(*ifc_file, no_progress, quiet, stderr_progress);
}
if (vmap.count("fix-storey-containment")) {
fix_storeycontainment(*ifc_file, no_progress, quiet, stderr_progress);
}
if (vmap.count("fix-wall-connectivity")) {
fix_wallconnectivity(*ifc_file, no_progress, quiet, stderr_progress);
}
*/
fs << *ifc_file;
exit_code = EXIT_SUCCESS;
} else {
@@ -630,24 +656,24 @@ int main(int argc, char** argv) {
SerializerSettings settings;
/// @todo Make APPLY_DEFAULT_MATERIALS configurable? Quickly tested setting this to false and using obj exporter caused the program to crash and burn.
settings.set(IfcGeom::IteratorSettings::APPLY_DEFAULT_MATERIALS, true);
settings.set(IfcGeom::IteratorSettings::USE_WORLD_COORDS, use_world_coords || output_extension == SVG || output_extension == OBJ);
settings.set(IfcGeom::IteratorSettings::WELD_VERTICES, weld_vertices);
settings.set(IfcGeom::IteratorSettings::SEW_SHELLS, orient_shells);
settings.set(IfcGeom::IteratorSettings::CONVERT_BACK_UNITS, convert_back_units);
settings.set(ifcopenshell::geometry::settings::APPLY_DEFAULT_MATERIALS, true);
settings.set(ifcopenshell::geometry::settings::USE_WORLD_COORDS, use_world_coords || output_extension == SVG || output_extension == OBJ);
settings.set(ifcopenshell::geometry::settings::WELD_VERTICES, weld_vertices);
settings.set(ifcopenshell::geometry::settings::SEW_SHELLS, orient_shells);
settings.set(ifcopenshell::geometry::settings::CONVERT_BACK_UNITS, convert_back_units);
#if OCC_VERSION_HEX < 0x60900
settings.set(IfcGeom::IteratorSettings::FASTER_BOOLEANS, merge_boolean_operands);
settings.set(ifcopenshell::geometry::settings::FASTER_BOOLEANS, merge_boolean_operands);
#endif
settings.set(IfcGeom::IteratorSettings::DISABLE_OPENING_SUBTRACTIONS, disable_opening_subtractions);
settings.set(IfcGeom::IteratorSettings::INCLUDE_CURVES, include_plan);
settings.set(IfcGeom::IteratorSettings::EXCLUDE_SOLIDS_AND_SURFACES, !include_model);
settings.set(IfcGeom::IteratorSettings::APPLY_LAYERSETS, enable_layerset_slicing);
settings.set(IfcGeom::IteratorSettings::NO_NORMALS, no_normals);
settings.set(IfcGeom::IteratorSettings::GENERATE_UVS, generate_uvs);
settings.set(IfcGeom::IteratorSettings::SEARCH_FLOOR, use_element_hierarchy || output_extension == SVG);
settings.set(IfcGeom::IteratorSettings::SITE_LOCAL_PLACEMENT, site_local_placement);
settings.set(IfcGeom::IteratorSettings::BUILDING_LOCAL_PLACEMENT, building_local_placement);
settings.set(IfcGeom::IteratorSettings::VALIDATE_QUANTITIES, validate);
settings.set(ifcopenshell::geometry::settings::DISABLE_OPENING_SUBTRACTIONS, disable_opening_subtractions);
settings.set(ifcopenshell::geometry::settings::INCLUDE_CURVES, include_plan);
settings.set(ifcopenshell::geometry::settings::EXCLUDE_SOLIDS_AND_SURFACES, !include_model);
settings.set(ifcopenshell::geometry::settings::APPLY_LAYERSETS, enable_layerset_slicing);
settings.set(ifcopenshell::geometry::settings::NO_NORMALS, no_normals);
settings.set(ifcopenshell::geometry::settings::GENERATE_UVS, generate_uvs);
settings.set(ifcopenshell::geometry::settings::SEARCH_FLOOR, use_element_hierarchy || output_extension == SVG);
settings.set(ifcopenshell::geometry::settings::SITE_LOCAL_PLACEMENT, site_local_placement);
settings.set(ifcopenshell::geometry::settings::BUILDING_LOCAL_PLACEMENT, building_local_placement);
settings.set(ifcopenshell::geometry::settings::VALIDATE_QUANTITIES, validate);
settings.set(SerializerSettings::USE_ELEMENT_NAMES, use_element_names);
settings.set(SerializerSettings::USE_ELEMENT_GUIDS, use_element_guids);
@@ -679,7 +705,7 @@ int main(int argc, char** argv) {
#endif
serializer = boost::make_shared<IgesSerializer>(IfcUtil::path::to_utf8(output_temp_filename), settings);
} else if (output_extension == SVG) {
settings.set(IfcGeom::IteratorSettings::DISABLE_TRIANGULATION, true);
settings.set(ifcopenshell::geometry::settings::DISABLE_TRIANGULATION, true);
serializer = boost::make_shared<SvgSerializer>(IfcUtil::path::to_utf8(output_temp_filename), settings);
if (vmap.count("section-height") != 0) {
Logger::Notice("Overriding section height");
@@ -716,7 +742,7 @@ int main(int argc, char** argv) {
Logger::Notice("Centering/offsetting model setting ignored when writing non-tesselated output");
}
settings.set(IfcGeom::IteratorSettings::DISABLE_TRIANGULATION, true);
settings.set(ifcopenshell::geometry::settings::DISABLE_TRIANGULATION, true);
}
if (!serializer->ready()) {
@@ -746,7 +772,7 @@ int main(int argc, char** argv) {
Logger::SetOutput(quiet ? nullptr : &cout_, &log_stream);
IfcGeom::Iterator<real_t> context_iterator(settings, ifc_file, filter_funcs, num_threads);
ifcopenshell::geometry::Iterator context_iterator(geometry_kernel, settings, ifc_file, filter_funcs, num_threads);
if (!context_iterator.initialize()) {
/// @todo It would be nice to know and print separate error prints for a case where we found no entities
/// and for a case we found no entities that satisfy our filtering criteria.
@@ -810,7 +836,7 @@ int main(int argc, char** argv) {
// The functions IfcGeom::Iterator::get() and IfcGeom::Iterator::next()
// wrap an iterator of all geometrical products in the Ifc file.
// IfcGeom::Iterator::get() returns an IfcGeom::TriangulationElement or
// -BRepElement pointer, based on current settings. (see IfcGeomIterator.h
// -NativeElement pointer, based on current settings. (see IfcGeomIterator.h
// for definition) IfcGeom::Iterator::next() is used to poll whether more
// geometrical entities are available. None of these functions throw
// exceptions, neither for parsing errors or geometrical errors. Upon
@@ -820,15 +846,15 @@ int main(int argc, char** argv) {
size_t num_created = 0;
do {
IfcGeom::Element<real_t> *geom_object = context_iterator.get();
ifcopenshell::geometry::Element* geom_object = context_iterator.get();
if (is_tesselated)
{
serializer->write(static_cast<const IfcGeom::TriangulationElement<real_t>*>(geom_object));
serializer->write(static_cast<const ifcopenshell::geometry::TriangulationElement*>(geom_object));
}
else
{
serializer->write(static_cast<const IfcGeom::BRepElement<real_t>*>(geom_object));
serializer->write(static_cast<const ifcopenshell::geometry::NativeElement*>(geom_object));
}
if (!no_progress) {
@@ -925,14 +951,14 @@ void write_log(bool header) {
}
#include <boost/algorithm/string/predicate.hpp>
#include <chrono>
bool init_input_file(const std::string& filename, IfcParse::IfcFile*& ifc_file, bool no_progress, bool mmap) {
time_t start, end;
std::clock_t c_start = std::clock();
// Prevent IfcFile::Init() prints by setting output to null temporarily
if (no_progress) { Logger::SetOutput(NULL, &log_stream); }
time(&start);
#ifdef USE_MMAP
ifc_file = new IfcParse::IfcFile(filename, mmap);
#else
@@ -949,10 +975,15 @@ bool init_input_file(const std::string& filename, IfcParse::IfcFile*& ifc_file,
Logger::Error("Unable to parse input file '" + filename + "'");
return false;
}
time(&end);
std::clock_t c_end = std::clock();
if (no_progress) { Logger::SetOutput(&cout_, &log_stream); }
else { Logger::Status("Parsing input file took " + format_duration(start, end)); }
else {
std::stringstream ss;
ss << std::setprecision(14) << (c_end - c_start) / (double)CLOCKS_PER_SEC;
Logger::Status("total_ifc_parse_time " + ss.str());
}
return true;
@@ -1216,14 +1247,14 @@ void fix_quantities(IfcParse::IfcFile& f, bool no_progress, bool quiet, bool std
}
}
IfcGeom::IteratorSettings settings;
settings.set(IfcGeom::IteratorSettings::USE_WORLD_COORDS, false);
settings.set(IfcGeom::IteratorSettings::WELD_VERTICES, false);
settings.set(IfcGeom::IteratorSettings::SEW_SHELLS, true);
settings.set(IfcGeom::IteratorSettings::CONVERT_BACK_UNITS, true);
settings.set(IfcGeom::IteratorSettings::DISABLE_TRIANGULATION, true);
ifcopenshell::geometry::settings settings;
settings.set(ifcopenshell::geometry::settings::USE_WORLD_COORDS, false);
settings.set(ifcopenshell::geometry::settings::WELD_VERTICES, false);
settings.set(ifcopenshell::geometry::settings::SEW_SHELLS, true);
settings.set(ifcopenshell::geometry::settings::CONVERT_BACK_UNITS, true);
settings.set(ifcopenshell::geometry::settings::DISABLE_TRIANGULATION, true);
IfcGeom::Iterator<double> context_iterator(settings, &f);
ifcopenshell::geometry::Iterator context_iterator(settings, &f);
if (!context_iterator.initialize()) {
return;
@@ -1257,14 +1288,14 @@ void fix_quantities(IfcParse::IfcFile& f, bool no_progress, bool quiet, bool std
IfcUtil::IfcBaseClass* quantity = nullptr;
IfcEntityList::ptr objects;
boost::shared_ptr<IfcGeom::Representation::BRep> previous_geometry_pointer;
boost::shared_ptr<ifcopenshell::geometry::Representation::BRep> previous_geometry_pointer;
for (;; ++num_created) {
bool has_more = true;
if (num_created) {
has_more = context_iterator.next();
}
IfcGeom::BRepElement<double>* geom_object = nullptr;
ifcopenshell::geometry::NativeElement* geom_object = nullptr;
if (has_more) {
geom_object = context_iterator.get_native();
}
@@ -1317,7 +1348,7 @@ void fix_quantities(IfcParse::IfcFile& f, bool no_progress, bool quiet, bool std
auto quantity_count = latebound_access::create(f, "IfcQuantityCount");
latebound_access::set(quantity_count, "Name", std::string("Surface Genus"));
latebound_access::set(quantity_count, "Description", '#' + boost::lexical_cast<std::string>(part.ItemId()));
latebound_access::set(quantity_count, "CountValue", IfcGeom::Kernel::surface_genus(part.Shape()));
latebound_access::set(quantity_count, "CountValue", part.Shape()->surface_genus());
quantities_2->push(quantity_count);
}
@@ -0,0 +1,185 @@
#include "validation_utils.h"
using namespace ifcopenshell::geometry;
#include <CGAL/AABB_tree.h>
#include <CGAL/AABB_traits.h>
#include <CGAL/Polyhedron_3.h>
#include <CGAL/AABB_face_graph_triangle_primitive.h>
typedef Kernel_::FT FT;
typedef Kernel_::Point_3 Point;
typedef Kernel_::Segment_3 Segment;
typedef CGAL::Polyhedron_3<Kernel_> Polyhedron;
typedef CGAL::AABB_face_graph_triangle_primitive<Polyhedron> Primitive;
typedef CGAL::AABB_traits<Kernel_, Primitive> Traits;
typedef CGAL::AABB_tree<Traits> Tree;
typedef Tree::Point_and_primitive_id Point_and_primitive_id;
void fix_spaceboundaries(IfcParse::IfcFile& f, bool no_progress, bool quiet, bool stderr_progress) {
intersection_validator v(f, { "IfcWall", "IfcSpace", "IfcSlab", "IfcCovering" }, 1.e-5, no_progress, quiet, stderr_progress);
auto rels = f.instances_by_type("IfcRelSpaceBoundary");
std::map<std::pair<const IfcUtil::IfcBaseClass*, const IfcUtil::IfcBaseClass*>, const IfcUtil::IfcBaseClass*> rel_by_space_elem;
if (rels) {
std::for_each(rels->begin(), rels->end(), [&rel_by_space_elem](const IfcUtil::IfcBaseClass* rel) {
auto x = ((IfcUtil::IfcBaseEntity*)rel)->get_value<IfcUtil::IfcBaseClass*>("RelatingSpace");
try {
auto y = ((IfcUtil::IfcBaseEntity*)rel)->get_value<IfcUtil::IfcBaseClass*>("RelatedBuildingElement");
rel_by_space_elem.insert({ { x,y }, rel });
} catch (IfcParse::IfcException&) {
// RelatedBuildingElement can be NULL
}
});
}
std::set<const IfcUtil::IfcBaseClass*> rels_encounted;
IfcParse::IfcFile f2("boundaries-triangulated.ifc");
if (!f2.good()) {
return;
}
ifcopenshell::geometry::settings settings;
settings.set(ifcopenshell::geometry::settings::USE_WORLD_COORDS, false);
settings.set(ifcopenshell::geometry::settings::WELD_VERTICES, false);
settings.set(ifcopenshell::geometry::settings::SEW_SHELLS, true);
settings.set(ifcopenshell::geometry::settings::CONVERT_BACK_UNITS, true);
settings.set(ifcopenshell::geometry::settings::DISABLE_TRIANGULATION, true);
settings.set(ifcopenshell::geometry::settings::DISABLE_OPENING_SUBTRACTIONS, true);
ifcopenshell::geometry::Converter c("cgal", &f2, settings);
std::map<std::set<std::string>, std::vector<Kernel_::Point_3>> elem_to_space_boundary_coords;
for (auto& i : *f2.instances_by_type("IfcProduct")) {
auto n = ((IfcUtil::IfcBaseEntity*)i)->get_value<std::string>("Name");
auto g1 = n.substr(0, 22);
auto g2 = n.substr(23);
auto item = c.mapping()->map(i);
if (((ifcopenshell::geometry::taxonomy::collection*) item)->children[0] == nullptr) {
continue;
}
auto shell = (taxonomy::shell*) ((taxonomy::collection*)((taxonomy::collection*) item)->children[0])->children[0];
for (auto& f : shell->children) {
auto face = (taxonomy::face*) f;
for (auto& w : face->children) {
auto wire = (taxonomy::loop*) w;
for (auto& e : wire->children) {
auto edge = (taxonomy::edge*) e;
auto p3 = boost::get<taxonomy::point3>(edge->start);
auto p4 = ((taxonomy::geom_item*)item)->matrix.ccomponents() * p3.ccomponents().homogeneous();
Kernel_::Point_3 P(p4(0), p4(1), p4(2));
elem_to_space_boundary_coords[{g1, g2}].emplace_back(P);
}
}
}
}
std::set< std::set<std::string> > guid_pairs_visited;
v([&rel_by_space_elem, &elem_to_space_boundary_coords, &guid_pairs_visited](const intersection_validator::Box& a, const intersection_validator::Box& b) {
std::ostringstream ss;
// ss << id_map[a.id()]->first->data().toString() << "x" << id_map[b.id()]->first->data().toString() << std::endl;
// auto x = id_map[a.id()]->second * id_map[b.id()]->second;
auto A = a.handle()->first;
auto B = b.handle()->first;
auto Aguid = A->get_value<std::string>("GlobalId");
auto Bguid = B->get_value<std::string>("GlobalId");
int space_count = 0;
if (A->declaration().name() == "IfcSpace") {
space_count += 1;
}
if (B->declaration().name() == "IfcSpace") {
space_count += 1;
}
if (space_count != 1) {
return;
}
ss << a.handle()->first->data().toString() << "x" << a.handle()->first->data().toString() << std::endl;
auto x = a.handle()->second * b.handle()->second;
if (x.is_empty()) {
return;
}
guid_pairs_visited.insert({ Aguid, Bguid });
cgal_shape_t x_poly;
x.convert_to_polyhedron(x_poly);
{
std::string fn = "computed_boundaries_" + Aguid + "_" + Bguid + ".off";
std::ofstream computed_boundaries(fn.c_str());
computed_boundaries.precision(17);
computed_boundaries << x_poly;
}
Tree tree(faces(x_poly).first, faces(x_poly).second, x_poly);
tree.accelerate_distance_queries();
auto itelem = elem_to_space_boundary_coords.find({ Aguid, Bguid });
if (itelem == elem_to_space_boundary_coords.end()) {
Logger::Error("Missing space boundary relationship " + Aguid + " " + Bguid);
return;
}
const auto& coords = itelem->second;
std::vector<double> distances;
std::transform(coords.begin(), coords.end(), std::back_inserter(distances), [&tree](const Kernel_::Point_3& p) {
return std::sqrt(CGAL::to_double(tree.squared_distance(p)));
});
bool valid = *std::max_element(distances.begin(), distances.end()) < 0.4;
if (!valid) {
Logger::Error("Wrong connection geometry " + Aguid + " " + Bguid);
}
/*{
remove_thickness r(x_poly);
std::string fn = "thin_computed_boundaries_" + Aguid + "_" + Bguid + ".off";
std::ofstream computed_boundaries(fn.c_str());
computed_boundaries.precision(17);
computed_boundaries << r.flattened;
}*/
/*
{
auto FN = s0 + "-" + s1 + "-" + std::to_string(i0) + "-" + std::to_string(i1) + "-sides-sb.off";
std::ofstream os(FN.c_str());
os.precision(17);
os << r.polyhedron2;
}
{
auto FN = s0 + "-" + s1 + "-" + std::to_string(i0) + "-" + std::to_string(i1) + "-flat-sb.off";
std::ofstream os(FN.c_str());
os.precision(17);
os << r.flattened;
}
*/
});
auto is_wall_space_or_slab = [&f](const std::string& g) {
auto decl = f.instance_by_guid(g)->declaration();
return decl.is("IfcWall") || decl.is("IfcSpace") || decl.is("IfcSlab");
};
for (auto& i : *f2.instances_by_type("IfcProduct")) {
auto n = ((IfcUtil::IfcBaseEntity*)i)->get_value<std::string>("Name");
auto g1 = n.substr(0, 22);
auto g2 = n.substr(23);
if (is_wall_space_or_slab(g1) && is_wall_space_or_slab(g2) && guid_pairs_visited.find({ g1, g2 }) == guid_pairs_visited.end()) {
Logger::Error("Space boundary for non-bounding geometry " + g1 + " " + g2);
}
}
}
@@ -0,0 +1,233 @@
#include "../ifcgeom/kernels/cgal/CgalKernel.h"
#include "../ifcgeom/schema_agnostic/IfcGeomFilter.h"
#include "../ifcgeom/schema_agnostic/IfcGeomIterator.h"
#include <CGAL/Polygon_mesh_processing/measure.h>
#include <CGAL/Polygon_mesh_processing/bbox.h>
#include <algorithm>
void fix_storeycontainment(IfcParse::IfcFile& f, bool no_progress, bool quiet, bool stderr_progress) {
ifcopenshell::geometry::settings settings;
settings.set(ifcopenshell::geometry::settings::USE_WORLD_COORDS, false);
settings.set(ifcopenshell::geometry::settings::WELD_VERTICES, false);
settings.set(ifcopenshell::geometry::settings::SEW_SHELLS, true);
settings.set(ifcopenshell::geometry::settings::CONVERT_BACK_UNITS, true);
settings.set(ifcopenshell::geometry::settings::DISABLE_TRIANGULATION, true);
settings.set(ifcopenshell::geometry::settings::DISABLE_OPENING_SUBTRACTIONS, true);
std::vector<ifcopenshell::geometry::filter_t> no_openings_and_spaces = {
IfcGeom::entity_filter(false, false, {"IfcOpeningElement", "IfcSpace"})
};
ifcopenshell::geometry::Iterator context_iterator("cgal", settings, &f, no_openings_and_spaces);
auto get_elevation = [](IfcUtil::IfcBaseClass* a) {
return ((IfcUtil::IfcBaseEntity*)a)->get_value_or<double>("Elevation", 0.);
};
// latebound inverse attribute lookup not working
auto rels = f.instances_by_type("IfcRelContainedInSpatialStructure");
std::map<IfcUtil::IfcBaseClass*, IfcUtil::IfcBaseClass*> elem_to_storey;
std::for_each(rels->begin(), rels->end(), [&elem_to_storey](IfcUtil::IfcBaseClass* r) {
auto elems = ((IfcUtil::IfcBaseEntity*)r)->get_value<IfcEntityList::ptr>("RelatedElements");
auto storey = ((IfcUtil::IfcBaseEntity*)r)->get_value<IfcUtil::IfcBaseClass*>("RelatingStructure");
if (storey->declaration().name() == "IfcBuildingStorey") {
for (auto it = elems->begin(); it != elems->end(); ++it) {
elem_to_storey[*it] = storey;
}
}
});
auto storeys = f.instances_by_type("IfcBuildingStorey");
std::vector<IfcUtil::IfcBaseClass*> storeys_sorted(storeys->begin(), storeys->end());
std::sort(storeys_sorted.begin(), storeys_sorted.end(), [&get_elevation](IfcUtil::IfcBaseClass* a, IfcUtil::IfcBaseClass* b) {
return get_elevation(a) < get_elevation(b);
});
/*
std::wcout << "Storeys ";
for (auto& s : storeys_sorted) {
auto n = ((IfcUtil::IfcBaseEntity*)s)->get_value<std::string>("Name");
std::wcout << n.c_str() << " ";
}
std::wcout << std::endl;
*/
std::vector<double> elevations;
std::transform(storeys_sorted.begin(), storeys_sorted.end(), std::back_inserter(elevations), get_elevation);
double LARGE = 1e4;
std::vector<std::pair<double, double>> elevation_slices;
for (size_t i = 0; i < elevations.size(); ++i) {
elevation_slices.push_back({
i == 0 ? -LARGE : elevations[i],
i + 1 == elevations.size() ? LARGE : elevations[i + 1]
});
}
std::for_each(elevation_slices.begin(), elevation_slices.end(), [](std::pair<double, double>& p) {
p.first -= 0.3;
p.second += 0.3;
});
std::vector<CGAL::Nef_polyhedron_3<Kernel_>> nefs;
std::transform(elevation_slices.begin(), elevation_slices.end(), std::back_inserter(nefs), [&LARGE](const std::pair<double, double>& p) {
// std::wcout << p.first << " - " << p.second << std::endl;
Kernel_::Point_3 p1(-LARGE, -LARGE, p.first);
Kernel_::Point_3 p2(+LARGE, +LARGE, p.second);
auto poly = ifcopenshell::geometry::utils::create_cube(p1, p2);
return ifcopenshell::geometry::utils::create_nef_polyhedron(poly);
});
/*
for (auto& n : nefs) {
auto poly = ifcopenshell::geometry::utils::create_polyhedron(n);
auto bounds = CGAL::Polygon_mesh_processing::bbox_3(poly);
for (int i = 0; i < 3; ++i) {
std::wcout << bounds.min(i) << std::endl;
}
for (int i = 0; i < 3; ++i) {
std::wcout << bounds.max(i) << std::endl;
}
std::wcout << "---" << std::endl;
}
*/
if (!context_iterator.initialize()) {
return;
}
size_t num_created = 0;
int old_progress = quiet ? 0 : -1;
for (;; ++num_created) {
bool has_more = true;
if (num_created) {
has_more = context_iterator.next();
}
ifcopenshell::geometry::NativeElement* geom_object = nullptr;
if (has_more) {
geom_object = context_iterator.get_native();
}
if (!geom_object) {
break;
}
/*
std::stringstream ss;
ss << geom_object->product()->data().toString();
auto sss = ss.str();
std::wcout << sss.c_str() << std::endl;
*/
if (elem_to_storey.find(geom_object->product()) == elem_to_storey.end()) {
// std::wcout << "not associated to storey" << std::endl;
continue;
}
std::vector<double> intersection_volumes(nefs.size());
for (auto& g : geom_object->geometry()) {
auto s = ((ifcopenshell::geometry::CgalShape*) g.Shape())->shape();
const auto& m = g.Placement().ccomponents();
const auto& n = geom_object->transformation().data().ccomponents();
const cgal_placement_t trsf(
m(0, 0), m(0, 1), m(0, 2), m(0, 3),
m(1, 0), m(1, 1), m(1, 2), m(1, 3),
m(2, 0), m(2, 1), m(2, 2), m(2, 3));
const cgal_placement_t trsf2(
n(0, 0), n(0, 1), n(0, 2), n(0, 3),
n(1, 0), n(1, 1), n(1, 2), n(1, 3),
n(2, 0), n(2, 1), n(2, 2), n(2, 3));
// Apply transformation
for (auto &vertex : vertices(s)) {
vertex->point() = vertex->point().transform(trsf).transform(trsf2);
}
/*
{
auto bounds = CGAL::Polygon_mesh_processing::bbox_3(s);
for (int i = 0; i < 3; ++i) {
std::wcout << bounds.min(i) << std::endl;
}
for (int i = 0; i < 3; ++i) {
std::wcout << bounds.max(i) << std::endl;
}
std::wcout << "---" << std::endl;
}
*/
CGAL::Nef_polyhedron_3<Kernel_> part_nef = ifcopenshell::geometry::utils::create_nef_polyhedron(s);
if (!part_nef.is_simple()) {
// std::wcout << "not simple" << std::endl;
continue;
}
std::vector<double>::iterator accumulator = intersection_volumes.begin();
std::for_each(nefs.begin(), nefs.end(), [&accumulator, &part_nef](const CGAL::Nef_polyhedron_3<Kernel_>& storey_nef) {
auto poly = ifcopenshell::geometry::utils::create_polyhedron(part_nef * storey_nef);
CGAL::Polygon_mesh_processing::triangulate_faces(poly);
*accumulator += CGAL::to_double(CGAL::Polygon_mesh_processing::volume(poly));
accumulator++;
});
}
/*
std::wcout << "volumes: ";
for (auto& v : intersection_volumes) {
std::wcout << v << " ";
}
std::wcout << std::endl;
*/
auto calc_idx = std::max_element(intersection_volumes.begin(), intersection_volumes.end()) - intersection_volumes.begin();
auto calc_overlap = intersection_volumes[calc_idx];
auto assigned_idx = std::distance(storeys_sorted.begin(), std::find(storeys_sorted.begin(), storeys_sorted.end(), elem_to_storey[geom_object->product()]));
auto assigned_overlap = intersection_volumes[assigned_idx];
if (calc_overlap > 0 && assigned_overlap < calc_overlap * 0.9) {
auto s = geom_object->product()->get_value<std::string>("GlobalId");
auto s1 = ((IfcUtil::IfcBaseEntity*)storeys_sorted[calc_idx])->get_value<std::string>("GlobalId");
auto s2 = ((IfcUtil::IfcBaseEntity*)elem_to_storey[geom_object->product()])->get_value<std::string>("GlobalId");
Logger::Error("Element " + s + " contained in " + s2 + " located on " + s1);
}
if (!no_progress) {
if (quiet) {
const int progress = context_iterator.progress();
for (; old_progress < progress; ++old_progress) {
std::cout << ".";
if (stderr_progress)
std::cerr << ".";
}
std::cout << std::flush;
if (stderr_progress)
std::cerr << std::flush;
} else {
const int progress = context_iterator.progress() / 2;
if (old_progress != progress) Logger::ProgressBar(progress);
old_progress = progress;
}
}
}
if (!no_progress && quiet) {
for (; old_progress < 100; ++old_progress) {
std::cout << ".";
if (stderr_progress)
std::cerr << ".";
}
std::cout << std::flush;
if (stderr_progress)
std::cerr << std::flush;
} else {
Logger::Status("\rDone fixing space boundaries for " + boost::lexical_cast<std::string>(num_created) +
" objects ");
}
}
@@ -0,0 +1,198 @@
#include "validation_utils.h"
#include <CGAL/Polygon_mesh_processing/bbox.h>
#include <CGAL/Polygon_mesh_processing/measure.h>
#include <algorithm>
using namespace ifcopenshell::geometry;
void fix_wallconnectivity(IfcParse::IfcFile& f, bool no_progress, bool quiet, bool stderr_progress) {
intersection_validator v(f, { "IfcWall" }, 1.e-3, no_progress, quiet, stderr_progress);
ifcopenshell::geometry::settings settings;
settings.set(ifcopenshell::geometry::settings::USE_WORLD_COORDS, false);
settings.set(ifcopenshell::geometry::settings::WELD_VERTICES, false);
settings.set(ifcopenshell::geometry::settings::SEW_SHELLS, true);
settings.set(ifcopenshell::geometry::settings::CONVERT_BACK_UNITS, true);
settings.set(ifcopenshell::geometry::settings::DISABLE_TRIANGULATION, true);
settings.set(ifcopenshell::geometry::settings::DISABLE_OPENING_SUBTRACTIONS, true);
settings.set(ifcopenshell::geometry::settings::INCLUDE_CURVES, true);
settings.set(ifcopenshell::geometry::settings::EXCLUDE_SOLIDS_AND_SURFACES, true);
ifcopenshell::geometry::Converter c("cgal", &f, settings);
auto rels = f.instances_by_type("IfcRelConnectsPathElements");
std::map<std::set<const IfcUtil::IfcBaseClass*>, const IfcUtil::IfcBaseClass*> rel_by_elem;
std::for_each(rels->begin(), rels->end(), [&rel_by_elem](const IfcUtil::IfcBaseClass* rel) {
auto x = ((IfcUtil::IfcBaseEntity*)rel)->get_value<IfcUtil::IfcBaseClass*>("RelatingElement");
auto y = ((IfcUtil::IfcBaseEntity*)rel)->get_value<IfcUtil::IfcBaseClass*>("RelatedElement");
rel_by_elem.insert({{ x,y }, rel});
});
std::set<const IfcUtil::IfcBaseClass*> rels_encounted;
double total_nef_intersection_time = 0.;
double conversion_to_poly = 0.;
v([&c, &rel_by_elem, &rels_encounted, &total_nef_intersection_time, &conversion_to_poly](const intersection_validator::Box& a, const intersection_validator::Box& b) {
auto A = a.handle()->first;
auto B = b.handle()->first;
const IfcUtil::IfcBaseClass* rel = nullptr;
std::string a_type, b_type;
auto rit = rel_by_elem.find({ A, B });
if (rit != rel_by_elem.end()) {
rel = rit->second;
const bool a_is_relating = A == ((IfcUtil::IfcBaseEntity*)rel)->get_value<IfcUtil::IfcBaseClass*>("RelatingElement");
a_type = ((IfcUtil::IfcBaseEntity*)rel)->get_value<std::string>("RelatingConnectionType");
b_type = ((IfcUtil::IfcBaseEntity*)rel)->get_value<std::string>("RelatedConnectionType");
if (!a_is_relating) {
std::swap(a_type, b_type);
}
}
#if 0
auto a_poly = ifcopenshell::geometry::utils::create_polyhedron(a.handle()->second);
auto b_poly = ifcopenshell::geometry::utils::create_polyhedron(b.handle()->second);
std::wcout << "a" << std::endl;
for (auto& v : vertices(a_poly)) {
for (int i = 0; i < 3; ++i) {
std::wcout << CGAL::to_double(v->point().cartesian(i)) << " ";
}
std::wcout << std::endl;
}
std::wcout << "b" << std::endl;
for (auto& v : vertices(b_poly)) {
for (int i = 0; i < 3; ++i) {
std::wcout << CGAL::to_double(v->point().cartesian(i)) << " ";
}
std::wcout << std::endl;
}
#endif
std::ostringstream ss;
ss << A->data().toString() << "x" << B->data().toString() << std::endl;
std::clock_t intersection_begin = std::clock();
auto x = a.handle()->second * b.handle()->second;
std::clock_t intersection_end = std::clock();
total_nef_intersection_time += (intersection_end - intersection_begin) / (double) CLOCKS_PER_SEC;
if (x.is_empty()) {
return;
}
std::clock_t poly_begin = std::clock();
cgal_shape_t x_poly;
x.convert_to_polyhedron(x_poly);
std::clock_t poly_end = std::clock();
conversion_to_poly += (poly_end - poly_begin) / (double)CLOCKS_PER_SEC;
auto dza = a.bbox().zmax() - a.bbox().zmin();
auto dzb = b.bbox().zmax() - b.bbox().zmin();
auto bb = CGAL::Polygon_mesh_processing::bbox_3(x_poly);
if (bb.zmax() - bb.zmin() < std::min(dza, dzb) / 3.) {
return;
}
CGAL::Polygon_mesh_processing::triangulate_faces(x_poly);
if (CGAL::Polygon_mesh_processing::area(x_poly) > 4.0) {
return;
}
auto get_axis_parameter_min_max = [&c, &x_poly](IfcUtil::IfcBaseEntity* inst) {
auto item = c.mapping()->map(inst);
auto shaperep = ((taxonomy::collection*) item)->children[0];
auto loop = ((taxonomy::collection*) shaperep)->children[0];
if (loop->kind() != taxonomy::LOOP) {
// std::wcout << "no suitable axis" << std::endl;
} else {
auto first_vertex = ((taxonomy::edge*) ((taxonomy::loop*) loop)->children.front())->start;
auto last_vertex = ((taxonomy::edge*) ((taxonomy::loop*) loop)->children.back())->end;
if (first_vertex.which() != 0 || last_vertex.which() != 0) {
// std::wcout << "trims not supported" << std::endl;
} else {
auto p0 = boost::get<taxonomy::point3>(first_vertex);
auto p1 = boost::get<taxonomy::point3>(last_vertex);
auto v0 = ((taxonomy::geom_item*)item)->matrix.ccomponents() * p0.ccomponents().homogeneous();
auto v1 = ((taxonomy::geom_item*)item)->matrix.ccomponents() * p1.ccomponents().homogeneous();
auto P0 = Kernel_::Point_3(v0(0), v0(1), v0(2));
auto P1 = Kernel_::Point_3(v1(0), v1(1), v1(2));
auto D = P1 - P0;
auto len = std::sqrt(CGAL::to_double(D.squared_length()));
D /= len;
std::vector<Kernel_::FT> parameters;
std::transform(vertices(x_poly).begin(), vertices(x_poly).end(), std::back_inserter(parameters), [&P0, D](cgal_vertex_descriptor_t& v) {
return (v->point() - P0) * D;
});
auto pit = std::minmax_element(parameters.begin(), parameters.end());
return std::make_pair(len, std::make_pair(CGAL::to_double(*pit.first), CGAL::to_double(*pit.second)));
}
}
const auto& nan = std::numeric_limits<double>::quiet_NaN();
return std::make_pair(nan, std::make_pair(nan, nan));
};
auto qualify_connection_type = [](double l, const std::pair<double, double>& p) {
if (p.first < 1.e-3) {
return "ATSTART";
} else if (p.second > l - 1.e-3) {
return "ATEND";
} else {
return "ATPATH";
}
};
auto alu0u1 = get_axis_parameter_min_max(A);
auto blu0u1 = get_axis_parameter_min_max(B);
auto atype_computed = qualify_connection_type(alu0u1.first, alu0u1.second);
auto btype_computed = qualify_connection_type(blu0u1.first, blu0u1.second);
rels_encounted.insert(rel);
if (a_type != atype_computed || b_type != btype_computed) {
if (rel) {
Logger::Error(std::string("Connection type ") + atype_computed + " " + btype_computed + " for:", rel);
} else {
auto A_str = A->get_value<std::string>("GlobalId");
auto B_str = B->get_value<std::string>("GlobalId");
Logger::Error("No connection for adjacent " + A_str + " " + B_str);
}
}
});
std::for_each(rels->begin(), rels->end(), [&rels_encounted, &v](const IfcUtil::IfcBaseClass* rel) {
if (rels_encounted.find(rel) == rels_encounted.end()) {
auto x = (IfcUtil::IfcBaseEntity*)((IfcUtil::IfcBaseEntity*)rel)->get_value<IfcUtil::IfcBaseClass*>("RelatingElement");
auto y = (IfcUtil::IfcBaseEntity*)((IfcUtil::IfcBaseEntity*)rel)->get_value<IfcUtil::IfcBaseClass*>("RelatedElement");
if (v.succesfully_processed.find(x) != v.succesfully_processed.end() && v.succesfully_processed.find(y) != v.succesfully_processed.end()) {
Logger::Error("Connection for non-adjacent walls", rel);
}
}
});
std::wcout << std::setprecision(14);
std::wcout << "total_map_time " << v.total_map_time << std::endl;
std::wcout << "total_geom_time " << v.total_geom_time << std::endl;
std::wcout << "total_nef_time " << v.total_nef_time << std::endl;
std::wcout << "total_minkowsky_time " << v.total_minkowsky_time << std::endl;
std::wcout << "total_box_time " << v.total_box_time << std::endl;
std::wcout << "total_nef_intersection_time " << total_nef_intersection_time << std::endl;
std::wcout << "total_conversion_to_poly_time " << conversion_to_poly << std::endl;
}
+28
View File
@@ -0,0 +1,28 @@
#include "validation_utils.h"
double facet_area(const cgal_shape_t::Facet_handle& f) {
auto p0 = f->facet_begin()->vertex()->point();
auto p1 = f->facet_begin()->next()->vertex()->point();
auto p2 = f->facet_begin()->next()->next()->vertex()->point();
return std::sqrt(CGAL::to_double(CGAL::cross_product(p0 - p1, p2 - p1).squared_length()));
}
void dump_facet(const cgal_shape_t::Facet_handle& f) {
auto p0 = f->facet_begin()->vertex()->point();
auto p1 = f->facet_begin()->next()->vertex()->point();
auto p2 = f->facet_begin()->next()->next()->vertex()->point();
auto V = CGAL::cross_product(p0 - p1, p2 - p1);
auto d = std::sqrt(CGAL::to_double(V.squared_length()));
if (d > 1.e-20) {
V /= d;
}
std::ostringstream oss;
oss.precision(8);
oss << "Facet with area " << facet_area(f) << " and normal ("
<< CGAL::to_double(V.cartesian(0)) << " " << CGAL::to_double(V.cartesian(1)) << " "
<< CGAL::to_double(V.cartesian(2)) << ")";
auto osss = oss.str();
std::wcout << osss.c_str() << std::endl;
}
+581
View File
@@ -0,0 +1,581 @@
#include "../ifcgeom/kernels/cgal/CgalKernel.h"
#include "../ifcgeom/schema_agnostic/IfcGeomFilter.h"
#include "../ifcgeom/schema_agnostic/IfcGeomIterator.h"
#include <CGAL/box_intersection_d.h>
#include <CGAL/minkowski_sum_3.h>
#include <CGAL/AABB_tree.h>
#include <CGAL/AABB_traits.h>
#include <CGAL/Polyhedron_3.h>
#include <CGAL/AABB_face_graph_triangle_primitive.h>
#include <fstream>
#include <iostream>
template <typename T>
T enlarge(const T& t, double d = 1.e-5) {
typename T::NT min[3];
typename T::NT max[3];
for (int i = 0; i < t.dimension(); ++i) {
min[i] = t.min_coord(i) - d;
max[i] = t.max_coord(i) + d;
}
return T(min, max, t.handle());
}
template <class HDS>
struct Build_Offset : public CGAL::Modifier_base<HDS> {
std::list<cgal_shape_t::Facet_handle> input;
void operator()(HDS& hds) {
// Postcondition: hds is a valid polyhedral surface.
CGAL::Polyhedron_incremental_builder_3<HDS> B(hds);
int Nv = 0, Nf = 0;
for (auto& f : input) {
Nv += 3;
Nf += 1;
}
B.begin_surface(Nv, Nf);
for (auto& f : input) {
auto p0 = f->facet_begin()->vertex()->point();
auto p1 = f->facet_begin()->next()->vertex()->point();
auto p2 = f->facet_begin()->next()->next()->vertex()->point();
auto O = CGAL::centroid(p0, p1, p2);
Kernel_::Point_3* p012[3] = { &p0, &p1, &p2 };
for (int i = 0; i < 3; ++i) {
*p012[i] = CGAL::ORIGIN + (((*(p012[i])) - CGAL::ORIGIN) + ((*(p012[i])) - O));
B.add_vertex(*p012[i]);
}
}
Nv = 0;
for (int i = 0; i < Nf; ++i) {
B.begin_facet();
B.add_vertex_to_facet(Nv++);
B.add_vertex_to_facet(Nv++);
B.add_vertex_to_facet(Nv++);
B.end_facet();
}
B.end_surface();
}
};
template <typename Ts>
std::list<cgal_shape_t::Facet_handle> connected_faces(cgal_shape_t::Facet_handle f, const Ts& excluded) {
std::set<cgal_shape_t::Facet_handle> fs = { f };
std::function<void(cgal_shape_t::Facet_handle& f)> process;
process = [&fs, &process, &excluded](cgal_shape_t::Facet_handle& f) {
cgal_shape_t::Halfedge_around_facet_circulator circ = f->facet_begin(), end(circ);
do {
auto ff = circ->opposite()->facet();
if (excluded.find(ff) == excluded.end()) {
auto p = fs.insert(ff);
if (p.second) {
process(ff);
}
}
} while (++circ != end);
};
process(f);
return std::list<cgal_shape_t::Facet_handle>(fs.begin(), fs.end());
}
template <class HDS>
struct Builder_With_Map : public CGAL::Modifier_base<HDS> {
std::list<cgal_shape_t::Facet_handle> input;
std::map<Kernel_::Point_3, Kernel_::Point_3> mapping;
void operator()(HDS& hds) {
// Postcondition: hds is a valid polyhedral surface.
CGAL::Polyhedron_incremental_builder_3<HDS> B(hds);
std::set<Kernel_::Point_3> used_points;
for (auto& f : input) {
cgal_shape_t::Halfedge_around_facet_circulator circ = f->facet_begin(), end(circ);
do {
auto P = circ->vertex()->point();
auto it = mapping.find(P);
if (it == mapping.end()) {
std::wcout << "WARNING unprojected point :(" << std::endl;
} else {
P = it->second;
}
used_points.insert(P);
} while (++circ != end);
}
B.begin_surface(used_points.size(), input.size());
for (auto& p : used_points) {
B.add_vertex(p);
}
for (auto& f : input) {
B.begin_facet();
cgal_shape_t::Halfedge_around_facet_circulator circ = f->facet_begin(), end(circ);
do {
auto P = circ->vertex()->point();
auto it = mapping.find(P);
if (it == mapping.end()) {
std::wcout << "WARNING unprojected point :(" << std::endl;
} else {
P = it->second;
}
auto jt = used_points.find(P);
if (jt == used_points.end()) {
throw std::runtime_error("Unable to map point");
}
size_t idx = std::distance(used_points.begin(), jt);
std::wcout << "idx " << idx << std::endl;
B.add_vertex_to_facet(idx);
} while (++circ != end);
B.end_facet();
}
B.end_surface();
}
};
double facet_area(const cgal_shape_t::Facet_handle& f);
void dump_facet(const cgal_shape_t::Facet_handle& f);
struct remove_thickness {
typedef Kernel_::Point_3 Point;
typedef Kernel_::Plane_3 Plane;
typedef Kernel_::Vector_3 Vector;
typedef Kernel_::Segment_3 Segment;
typedef Kernel_::Ray_3 Ray;
typedef CGAL::Polyhedron_3<Kernel_> Polyhedron;
typedef CGAL::AABB_face_graph_triangle_primitive<Polyhedron> Primitive;
typedef CGAL::AABB_traits<Kernel_, Primitive> Traits;
typedef CGAL::AABB_tree<Traits> Tree;
typedef boost::optional<Tree::Intersection_and_primitive_id<Ray>::Type> Ray_intersection;
cgal_shape_t polyhedron, polyhedron2, flattened;
remove_thickness(const cgal_shape_t& p)
// edge_collapse(p) still does not work :(
: polyhedron(p)
, polyhedron2(p) {
CGAL::Polygon_mesh_processing::triangulate_faces(polyhedron);
CGAL::Polygon_mesh_processing::triangulate_faces(polyhedron2);
std::list<cgal_shape_t::Facet_handle> non_degenerate, degenerate, longitudonal;
std::set<cgal_shape_t::Facet_iterator> thin_sides;
std::wcout << "ALL FACES:" << std::endl;
for (auto& f : faces(polyhedron)) {
dump_facet(f);
if (facet_area(f) > 1.e-20) {
non_degenerate.push_back(f);
} else {
degenerate.push_front(f);
std::wcout << "Degenerate, area: " << facet_area(f) << std::endl;
}
}
std::wcout << "NON DEGENERATE:" << std::endl;
for (auto& f : non_degenerate) {
dump_facet(f);
}
cgal_shape_t enlarged_non_degenerate_triangles;
Build_Offset<cgal_shape_t::HDS> bo;
bo.input = non_degenerate;
enlarged_non_degenerate_triangles.delegate(bo);
// @todo, first on non-enlarged faces, then on enlarged; to fix projection on concave surfaces where the enlarging operation shortens projection distances.
Tree tree(faces(enlarged_non_degenerate_triangles).first, faces(enlarged_non_degenerate_triangles).second, enlarged_non_degenerate_triangles);
std::map<cgal_face_descriptor_t, Kernel_::Vector_3> face_normals;
boost::associative_property_map<std::map<cgal_face_descriptor_t, Kernel_::Vector_3>> face_normals_map(face_normals);
CGAL::Polygon_mesh_processing::compute_face_normals(polyhedron, face_normals_map);
for (auto& f : non_degenerate) {
auto O = CGAL::centroid(
f->facet_begin()->vertex()->point(),
f->facet_begin()->next()->vertex()->point(),
f->facet_begin()->next()->next()->vertex()->point()
);
Ray ray(O, -face_normals_map[f]);
std::list<Ray_intersection> intersections;
tree.all_intersections(ray, std::back_inserter(intersections));
double N = std::numeric_limits<double>::infinity();
Point P;
for (auto& intersection : intersections) {
if (boost::get<Point>(&(intersection->first))) {
const Point* p = boost::get<Point>(&(intersection->first));
const double d = std::sqrt(CGAL::to_double((*p - O).squared_length()));
if (d > 1.e-20 && d < N) {
N = d;
}
}
}
if (N != std::numeric_limits<double>::infinity() && N > 1.e-4) {
thin_sides.insert(f);
}
}
std::wcout << "THIN SIDES:" << std::endl;
for (auto& f : thin_sides) {
dump_facet(f);
}
for (auto& f : non_degenerate) {
if (thin_sides.find(f) == thin_sides.end()) {
longitudonal.push_back(f);
}
}
std::wcout << "LONGITUDONAL:" << std::endl;
for (auto& f : longitudonal) {
dump_facet(f);
}
std::wcout << "faces " << faces(polyhedron).size() << "long " << longitudonal.size() << "thin " << thin_sides.size() << "non-degen " << non_degenerate.size() << std::endl;
cgal_shape_t enlarged_indiv_triangles;
Build_Offset<cgal_shape_t::HDS> bo2;
bo2.input = longitudonal;
enlarged_indiv_triangles.delegate(bo2);
{
std::ofstream ofs("enlarged.off");
ofs.precision(17);
ofs << enlarged_indiv_triangles;
}
Tree tree2(faces(enlarged_indiv_triangles).begin(), faces(enlarged_indiv_triangles).end(), enlarged_indiv_triangles);
std::map<Kernel_::Point_3, Kernel_::Point_3> new_points;
for (Polyhedron::Facet_iterator fit = polyhedron.facets_begin();
fit != polyhedron.facets_end();
++fit) {
if (CGAL::collinear(
fit->halfedge()->vertex()->point(),
fit->halfedge()->next()->vertex()->point(),
fit->halfedge()->opposite()->vertex()->point())) {
std::wcout << "degenerate triangle" << std::endl;
}
}
for (auto& v : vertices(polyhedron)) {
auto O = v->point();
Kernel_::Vector_3 norm;
Kernel_::Vector_3 accum;
int count = 0;
CGAL::Face_around_target_circulator<cgal_shape_t> it(v->halfedge(), polyhedron), end(it);
do {
cgal_shape_t::Facet_handle fh = (*it)->halfedge()->facet();
auto jt = std::find(non_degenerate.begin(), non_degenerate.end(), fh);
std::wcout << "non degen: " << (jt != non_degenerate.end()) << std::endl;
auto kt = std::find(thin_sides.begin(), thin_sides.end(), fh);
std::wcout << "thin side: " << (kt != thin_sides.end()) << std::endl;
if (jt != non_degenerate.end() && kt == thin_sides.end()) {
// else degenerate, prevent div by zero, do not incorporate in vnorm.
// or else part of thin side
auto p0 = (*it)->facet_begin()->vertex()->point();
auto p1 = (*it)->facet_begin()->next()->vertex()->point();
auto p2 = (*it)->facet_begin()->next()->next()->vertex()->point();
{
std::ostringstream oss;
oss.precision(8);
oss << "p0 " << p0.cartesian(0) << " " << p0.cartesian(1) << " " << p0.cartesian(2) << "\n";
oss << "p1 " << p1.cartesian(0) << " " << p1.cartesian(1) << " " << p1.cartesian(2) << "\n";
oss << "p2 " << p2.cartesian(0) << " " << p2.cartesian(1) << " " << p2.cartesian(2) << "\n";
auto osss = oss.str();
std::wcout << osss.c_str() << std::endl;
}
auto fnorm = CGAL::cross_product(p0 - p1, p2 - p1);
fnorm /= std::sqrt(CGAL::to_double(fnorm.squared_length()));
// const auto& fnorm = face_normals_map_2[*it];
std::ostringstream oss;
oss.precision(8);
oss << fnorm.cartesian(0) << " " << fnorm.cartesian(1) << " " << fnorm.cartesian(2);
auto osss = oss.str();
std::wcout << osss.c_str() << std::endl;
accum += fnorm;
++count;
}
++it;
} while (it != end);
norm = accum / count;
std::wcout << "count " << count << std::endl;
if (count == 0) {
// part of only degenerate or only thin sides
continue;
}
// v->vertex_begin();
Ray ray(O, norm);
std::ostringstream oss;
oss.precision(8);
oss << O << " -> " << norm;
auto osss = oss.str();
std::wcout << osss.c_str() << std::endl;
std::list<Ray_intersection> intersections;
tree2.all_intersections(ray, std::back_inserter(intersections));
double N = std::numeric_limits<double>::infinity();
Point P;
bool used_intersection = false;
if (intersections.size()) {
for (auto& intersection : intersections) {
if (boost::get<Point>(&(intersection->first))) {
const Point* p = boost::get<Point>(&(intersection->first));
const double d = std::sqrt(CGAL::to_double((*p - O).squared_length()));
if (d < N && d > 1.e-20) {
N = d;
P = *p;
std::wcout << "intersection @ " << d << std::endl;
}
}
}
std::wcout << "-----------" << std::endl;
// average the new point
new_points[O] = CGAL::ORIGIN + (((O - CGAL::ORIGIN) + (P - CGAL::ORIGIN))) / 2;
used_intersection = true;
}
if (!used_intersection) {
std::wcout << "no intersection :(" << std::endl;
}
}
auto thin_sides_degenerate = thin_sides;
thin_sides_degenerate.insert(degenerate.begin(), degenerate.end());
// @todo choose connected / connected_opposing based on largest combined area of facets?
if (longitudonal.size() == 0) {
std::wcout << "no longitudonal faces detected :(" << std::endl;
return;
}
auto connected = connected_faces(*longitudonal.begin(), thin_sides_degenerate);
decltype(connected) connected_opposing;
for (auto& f : longitudonal) {
if (std::find(connected.begin(), connected.end(), f) == connected.end()) {
connected_opposing = connected_faces(f, thin_sides_degenerate);
std::set<cgal_shape_t::Facet_handle> longi(longitudonal.begin(), longitudonal.end());
std::set<cgal_shape_t::Facet_handle> both_sides(connected.begin(), connected.end());
both_sides.insert(connected_opposing.begin(), connected_opposing.end());
if (longi == both_sides) {
std::wcout << "Facet connection functioning properly" << std::endl;
} else {
std::wcout << "Facet connection functioning incorrectly" << std::endl;
}
break;
}
}
Builder_With_Map<cgal_shape_t::HDS> b2;
b2.input = connected;
b2.mapping = new_points;
flattened.delegate(b2);
}
};
struct intersection_validator {
typedef std::list<std::pair<IfcUtil::IfcBaseEntity*, CGAL::Nef_polyhedron_3<Kernel_>> > nefs_t;
typedef CGAL::Box_intersection_d::Box_with_handle_d<double, 3, nefs_t::value_type*> Box;
std::vector<Box> boxes;
nefs_t nefs;
double total_map_time = 0.;
double total_geom_time = 0.;
double total_nef_time = 0.;
double total_minkowsky_time = 0.;
double total_box_time = 0.;
std::set<IfcUtil::IfcBaseEntity*> succesfully_processed;
intersection_validator(IfcParse::IfcFile& f, std::initializer_list<std::string> entities, double eps, bool no_progress, bool quiet, bool stderr_progress) {
ifcopenshell::geometry::settings settings;
settings.set(ifcopenshell::geometry::settings::USE_WORLD_COORDS, false);
settings.set(ifcopenshell::geometry::settings::WELD_VERTICES, false);
settings.set(ifcopenshell::geometry::settings::SEW_SHELLS, true);
settings.set(ifcopenshell::geometry::settings::CONVERT_BACK_UNITS, true);
settings.set(ifcopenshell::geometry::settings::DISABLE_TRIANGULATION, true);
settings.set(ifcopenshell::geometry::settings::DISABLE_OPENING_SUBTRACTIONS, true);
std::vector<ifcopenshell::geometry::filter_t> spaces_and_walls = {
IfcGeom::entity_filter(true, false, entities)
};
ifcopenshell::geometry::Iterator context_iterator("cgal", settings, &f, spaces_and_walls);
if (!context_iterator.initialize()) {
return;
}
auto polycube = ifcopenshell::geometry::utils::create_cube(eps);
auto cube = ifcopenshell::geometry::utils::create_nef_polyhedron(polycube);
size_t num_created = 0;
int old_progress = quiet ? 0 : -1;
for (;; ++num_created) {
bool has_more = true;
if (num_created) {
has_more = context_iterator.next();
}
ifcopenshell::geometry::NativeElement* geom_object = nullptr;
if (has_more) {
geom_object = context_iterator.get_native();
}
if (!geom_object) {
break;
}
std::stringstream ss;
ss << geom_object->product()->data().toString();
auto sss = ss.str();
std::wcout << sss.c_str() << std::endl;
for (auto& g : geom_object->geometry()) {
auto s = ((ifcopenshell::geometry::CgalShape*) g.Shape())->shape();
const auto& m = g.Placement().ccomponents();
const auto& n = geom_object->transformation().data().ccomponents();
const cgal_placement_t trsf(
m(0, 0), m(0, 1), m(0, 2), m(0, 3),
m(1, 0), m(1, 1), m(1, 2), m(1, 3),
m(2, 0), m(2, 1), m(2, 2), m(2, 3));
const cgal_placement_t trsf2(
n(0, 0), n(0, 1), n(0, 2), n(0, 3),
n(1, 0), n(1, 1), n(1, 2), n(1, 3),
n(2, 0), n(2, 1), n(2, 2), n(2, 3));
// Apply transformation
for (auto &vertex : vertices(s)) {
vertex->point() = vertex->point().transform(trsf).transform(trsf2);
}
std::clock_t nef_begin = std::clock();
CGAL::Nef_polyhedron_3<Kernel_> nef = ifcopenshell::geometry::utils::create_nef_polyhedron(s);
std::clock_t nef_end = std::clock();
total_nef_time += (nef_end - nef_begin) / (double) CLOCKS_PER_SEC;
if (nef.is_empty()) {
std::wcout << "Failed to create nef" << std::endl;
continue;
}
succesfully_processed.insert(geom_object->product());
nef = CGAL::minkowski_sum_3(nef, cube);
std::clock_t minkowski_end = std::clock();
total_minkowsky_time += (minkowski_end - nef_end) / (double) CLOCKS_PER_SEC;
std::wcout << "product: " << geom_object->product() << std::endl;
nefs.push_back({ geom_object->product(), nef });
Box b(&*(nefs.rbegin()));
// id_map[b.id()] = ;
for (auto &vertex : vertices(s)) {
double p[3] = {
CGAL::to_double(vertex->point().cartesian(0)),
CGAL::to_double(vertex->point().cartesian(1)),
CGAL::to_double(vertex->point().cartesian(2))
};
b.extend(p);
}
boxes.push_back(enlarge(b));
/*
std::ostringstream ss;
ss << geom_object->product()->data().toString() << std::endl << b.min_coord(0) << " - " << b.max_coord(0) << std::endl;
auto sss = ss.str();
std::wcout << sss.c_str();
*/
}
if (!no_progress) {
if (quiet) {
const int progress = context_iterator.progress();
for (; old_progress < progress; ++old_progress) {
std::cout << ".";
if (stderr_progress)
std::cerr << ".";
}
std::cout << std::flush;
if (stderr_progress)
std::cerr << std::flush;
} else {
const int progress = context_iterator.progress() / 2;
if (old_progress != progress) Logger::ProgressBar(progress);
old_progress = progress;
}
}
}
if (!no_progress && quiet) {
for (; old_progress < 100; ++old_progress) {
std::cout << ".";
if (stderr_progress)
std::cerr << ".";
}
std::cout << std::flush;
if (stderr_progress)
std::cerr << std::flush;
} else {
Logger::Status("\rDone fixing space boundaries for " + boost::lexical_cast<std::string>(num_created) +
" objects ");
}
total_geom_time = context_iterator.converter().total_geom_time;
total_map_time = context_iterator.converter().total_map_time;
}
template <typename Fn>
void operator()(Fn fn) {
std::clock_t box_overlap_begin = std::clock();
CGAL::box_self_intersection_d(boxes.begin(), boxes.end(), [](Box& x, Box& y) {});
std::clock_t box_overlap_end = std::clock();
total_box_time += (box_overlap_end - box_overlap_begin) / (double) CLOCKS_PER_SEC;
CGAL::box_self_intersection_d(boxes.begin(), boxes.end(), fn);
}
};
+9
View File
@@ -0,0 +1,9 @@
#include "ConversionSettings.h"
void ifcopenshell::geometry::ConversionSettings::setValue(GeomValue var, double value) {
values_[var] = value;
}
double ifcopenshell::geometry::ConversionSettings::getValue(GeomValue var) const {
return values_[var];
}
+61
View File
@@ -0,0 +1,61 @@
#ifndef CONVERSIONSETTINGS_H
#define CONVERSIONSETTINGS_H
#include <array>
namespace ifcopenshell { namespace geometry {
class NativeElement;
class ConversionSettings {
public:
// Tolerances and settings for various geometrical operations:
enum GeomValue {
// Specifies the deflection of the mesher
// Default: 0.001m / 1mm
GV_DEFLECTION_TOLERANCE,
// Specifies the minimal area of a face to be included in an IfcConnectedFaceset
// Read-only
GV_MINIMAL_FACE_AREA,
// Specifies the threshold distance under which cartesian points are deemed equal
// Read-only
GV_POINT_EQUALITY_TOLERANCE,
// Specifies maximum number of faces for a shell to be reoriented.
// Default: -1
GV_MAX_FACES_TO_ORIENT,
// The length unit used the creation of TopoDS_Shapes, primarily affects the
// interpretation of IfcCartesianPoints and IfcVector magnitudes
// DefaultL 1.0
GV_LENGTH_UNIT,
// The plane angle unit used for the creation of TopoDS_Shapes, primarily affects
// the interpretation of IfcParamaterValues of IfcTrimmedCurves
// Default: -1.0 (= not set, fist try degrees, then radians)
GV_PLANEANGLE_UNIT,
// The precision used in boolean operations, setting this value too low results
// in artefacts and potentially modelling failures
// Default: 0.00001 (obtained from IfcGeometricRepresentationContext if available)
GV_PRECISION,
// Whether to process shapes of type Face or higher (1) Wire or lower (-1) or all (0)
GV_DIMENSIONALITY
};
void setValue(GeomValue var, double value);
double getValue(GeomValue var) const;
private:
std::array<double, 8> values_ = {
/* deflection_tolerance = */ 0.001,
/* wire_creation_tolerance = */ 0.0001,
/* point_equality_tolerance = */ 0.00001,
/* max_faces_to_sew = */ -1.0,
/* ifc_length_unit = */ 1.0,
/* ifc_planeangle_unit = */ -1.0,
/* modelling_precision = */ 0.00001,
/* dimensionality = */ 1.,
};
};
} }
#endif
-457
View File
@@ -1,457 +0,0 @@
/********************************************************************************
* *
* 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/>. *
* *
********************************************************************************/
#ifndef IFCGEOM_H
#define IFCGEOM_H
#include <cmath>
static const double ALMOST_ZERO = 1.e-9;
template <typename T>
inline static bool ALMOST_THE_SAME(const T& a, const T& b, double tolerance=ALMOST_ZERO) {
return fabs(a-b) < tolerance;
}
#include <gp_Pnt.hxx>
#include <gp_Vec.hxx>
#include <gp_Mat.hxx>
#include <gp_Mat2d.hxx>
#include <gp_GTrsf.hxx>
#include <gp_GTrsf2d.hxx>
#include <gp_Trsf.hxx>
#include <gp_Trsf2d.hxx>
#include <TopoDS.hxx>
#include <TopoDS_Wire.hxx>
#include <TopoDS_Face.hxx>
#include <Geom_Curve.hxx>
#include <gp_Pln.hxx>
#include <TColgp_SequenceOfPnt.hxx>
#include <TopTools_ListOfShape.hxx>
#include <BOPAlgo_Operation.hxx>
#include <BRep_Builder.hxx>
#include <BRepBuilderAPI_MakeEdge.hxx>
#include "../ifcparse/macros.h"
#include "../ifcparse/IfcParse.h"
#include "../ifcparse/IfcBaseClass.h"
#include "../ifcgeom/IfcGeomElement.h"
#include "../ifcgeom/IfcGeomRepresentation.h"
#include "../ifcgeom/IfcRepresentationShapeItem.h"
#include "../ifcgeom/IfcGeomShapeType.h"
#include "../ifcgeom_schema_agnostic/Kernel.h"
#include "ifc_geom_api.h"
// Define this in case you want to conserve memory usage at all cost. This has been
// benchmarked extensively: https://github.com/IfcOpenShell/IfcOpenShell/pull/47
// #define NO_CACHE
#ifdef NO_CACHE
#define IN_CACHE(T,E,t,e)
#define CACHE(T,E,e)
#else
#define IN_CACHE(T,E,t,e) std::map<int,t>::const_iterator it = cache.T.find(E->data().id());\
if ( it != cache.T.end() ) { e = it->second; return true; }
#define CACHE(T,E,e) cache.T[E->data().id()] = e;
#endif
#define INCLUDE_PARENT_DIR(x) STRINGIFY(../ifcparse/x.h)
#include INCLUDE_PARENT_DIR(IfcSchema)
#undef INCLUDE_PARENT_DIR
#define INCLUDE_PARENT_DIR(x) STRINGIFY(../ifcparse/x-definitions.h)
#include INCLUDE_PARENT_DIR(IfcSchema)
namespace IfcGeom {
class IFC_GEOM_API geometry_exception : public std::exception {
protected:
std::string message;
public:
geometry_exception(const std::string& m)
: message(m) {}
virtual ~geometry_exception() throw () {}
virtual const char* what() const throw() {
return message.c_str();
}
};
class IFC_GEOM_API too_many_faces_exception : public geometry_exception {
public:
too_many_faces_exception()
: geometry_exception("Too many faces for operation") {}
};
class IFC_GEOM_API MAKE_TYPE_NAME(Cache) {
public:
#include "IfcRegisterCreateCache.h"
std::map<int, TopoDS_Shape> Shape;
};
class IFC_GEOM_API MAKE_TYPE_NAME(Kernel) : public IfcGeom::Kernel {
private:
/*
faceset_helper traverses the forward instance references of IfcConnectedFaceSet and then provides a mapping
M of (IfcCartesianPoint, IfcCartesianPoint) -> TopoDS_Edge, where M(a, b) is a partner of M(b, a), ie share
the same underlying edge but with orientation reversed. This then later speeds op the process of creating a
manifold Shell / Solid from this set of faces. Only IfcPolyLoop instances are used. Points within the tolerance
threshiold are merged, so consider points a, b, c, distance(a, b) < eps then M(a, b) = Null, M(a, b) = M(a, c).
*/
class faceset_helper {
private:
MAKE_TYPE_NAME(Kernel)* kernel_;
std::set<const IfcSchema::IfcPolyLoop*> duplicates_;
std::map<int, int> vertex_mapping_;
std::map<std::pair<int, int>, TopoDS_Edge> edges_;
double eps_;
bool non_manifold_;
template <typename Fn>
void loop_(IfcSchema::IfcCartesianPoint::list::ptr& ps, const Fn& callback) {
if (ps->size() < 3) {
return;
}
auto a = *(ps->end() - 1);
auto A = a->data().id();
for (auto& b : *ps) {
auto B = b->data().id();
auto C = vertex_mapping_[A], D = vertex_mapping_[B];
bool fwd = C < D;
if (!fwd) {
std::swap(C, D);
}
if (C != D) {
callback(C, D, fwd);
A = B;
}
}
}
public:
faceset_helper(MAKE_TYPE_NAME(Kernel)* kernel, const IfcSchema::IfcConnectedFaceSet* l);
~faceset_helper();
bool non_manifold() const { return non_manifold_; }
bool& non_manifold() { return non_manifold_; }
bool edge(const IfcSchema::IfcCartesianPoint* a, const IfcSchema::IfcCartesianPoint* b, TopoDS_Edge& e) {
int A = vertex_mapping_[a->data().id()];
int B = vertex_mapping_[b->data().id()];
if (A == B) {
return false;
}
return edge(A, B, e);
}
bool edge(int A, int B, TopoDS_Edge& e) {
auto it = edges_.find({A, B});
if (it == edges_.end()) {
return false;
}
e = it->second;
return true;
}
bool wire(const IfcSchema::IfcPolyLoop* loop, TopoDS_Wire& wire) {
if (duplicates_.find(loop) != duplicates_.end()) {
return false;
}
BRep_Builder builder;
builder.MakeWire(wire);
int count = 0;
auto ps = loop->Polygon();
loop_(ps, [this, &builder, &wire, &count](int A, int B, bool fwd) {
TopoDS_Edge e;
if (edge(A, B, e)) {
if (!fwd) {
e.Reverse();
}
builder.Add(wire, e);
count += 1;
}
});
if (count >= 3) {
wire.Closed(true);
TopTools_ListOfShape results;
if (kernel_->wire_intersections(wire, results)) {
Logger::Warning("Self-intersections with " + boost::lexical_cast<std::string>(results.Extent()) + " cycles detected", loop);
kernel_->select_largest(results, wire);
non_manifold_ = true;
}
return true;
} else {
return false;
}
}
double epsilon() const {
return eps_;
}
};
double deflection_tolerance;
double ifc_length_unit;
double ifc_planeangle_unit;
double modelling_precision;
double dimensionality;
double max_faces_to_orient;
#ifndef NO_CACHE
MAKE_TYPE_NAME(Cache) cache;
#endif
std::map<int, SurfaceStyle> style_cache;
const SurfaceStyle* internalize_surface_style(const std::pair<IfcUtil::IfcBaseClass*, IfcUtil::IfcBaseClass*>& shading_style);
// For stopping PlacementRelTo recursion in convert(const IfcSchema::IfcObjectPlacement* l, gp_Trsf& trsf)
const IfcParse::declaration* placement_rel_to;
faceset_helper* faceset_helper_;
public:
MAKE_TYPE_NAME(Kernel)()
: IfcGeom::Kernel(0)
, deflection_tolerance(0.001)
, max_faces_to_orient(-1.0)
, ifc_length_unit(1.0)
, ifc_planeangle_unit(-1.0)
, modelling_precision(0.00001)
, dimensionality(1.)
, placement_rel_to(0)
, faceset_helper_(nullptr)
{}
MAKE_TYPE_NAME(Kernel)(const MAKE_TYPE_NAME(Kernel)& other) : IfcGeom::Kernel(0) {
*this = other;
}
MAKE_TYPE_NAME(Kernel)& operator=(const MAKE_TYPE_NAME(Kernel)& other) {
setValue(GV_DEFLECTION_TOLERANCE, other.getValue(GV_DEFLECTION_TOLERANCE));
setValue(GV_MAX_FACES_TO_ORIENT, other.getValue(GV_MAX_FACES_TO_ORIENT));
setValue(GV_LENGTH_UNIT, other.getValue(GV_LENGTH_UNIT));
setValue(GV_PLANEANGLE_UNIT, other.getValue(GV_PLANEANGLE_UNIT));
setValue(GV_PRECISION, other.getValue(GV_PRECISION));
setValue(GV_DIMENSIONALITY, other.getValue(GV_DIMENSIONALITY));
setValue(GV_DEFLECTION_TOLERANCE, other.getValue(GV_DEFLECTION_TOLERANCE));
return *this;
}
bool convert_wire_to_face(const TopoDS_Wire& wire, TopoDS_Face& face);
bool convert_curve_to_wire(const Handle(Geom_Curve)& curve, TopoDS_Wire& wire);
bool convert_shapes(const IfcUtil::IfcBaseClass* L, IfcRepresentationShapeItems& result);
IfcGeom::ShapeType shape_type(const IfcUtil::IfcBaseClass* L);
bool convert_shape(const IfcUtil::IfcBaseClass* L, TopoDS_Shape& result);
bool flatten_shape_list(const IfcGeom::IfcRepresentationShapeItems& shapes, TopoDS_Shape& result, bool fuse);
bool convert_wire(const IfcUtil::IfcBaseClass* L, TopoDS_Wire& result);
bool convert_curve(const IfcUtil::IfcBaseClass* L, Handle(Geom_Curve)& result);
bool convert_face(const IfcUtil::IfcBaseClass* L, TopoDS_Shape& result);
bool convert_openings(const IfcSchema::IfcProduct* entity, const IfcSchema::IfcRelVoidsElement::list::ptr& openings, const IfcRepresentationShapeItems& entity_shapes, const gp_Trsf& entity_trsf, IfcRepresentationShapeItems& cut_shapes);
bool convert_openings_fast(const IfcSchema::IfcProduct* entity, const IfcSchema::IfcRelVoidsElement::list::ptr& openings, const IfcRepresentationShapeItems& entity_shapes, const gp_Trsf& entity_trsf, IfcRepresentationShapeItems& cut_shapes);
void assert_closed_wire(TopoDS_Wire& wire);
bool convert_layerset(const IfcSchema::IfcProduct*, std::vector<Handle_Geom_Surface>&, std::vector<const SurfaceStyle*>&, std::vector<double>&);
bool apply_layerset(const IfcRepresentationShapeItems&, const std::vector<Handle_Geom_Surface>&, const std::vector<const SurfaceStyle*>&, IfcRepresentationShapeItems&);
bool apply_folded_layerset(const IfcRepresentationShapeItems&, const std::vector< std::vector<Handle_Geom_Surface> >&, const std::vector<const SurfaceStyle*>&, IfcRepresentationShapeItems&);
bool fold_layers(const IfcSchema::IfcWall*, const IfcRepresentationShapeItems&, const std::vector<Handle_Geom_Surface>&, const std::vector<double>&, std::vector< std::vector<Handle_Geom_Surface> >&);
bool split_solid_by_surface(const TopoDS_Shape&, const Handle_Geom_Surface&, TopoDS_Shape&, TopoDS_Shape&);
bool split_solid_by_shell(const TopoDS_Shape&, const TopoDS_Shape& s, TopoDS_Shape&, TopoDS_Shape&);
#if OCC_VERSION_HEX < 0x60900
bool boolean_operation(const TopoDS_Shape&, const TopTools_ListOfShape&, BOPAlgo_Operation, TopoDS_Shape&);
bool boolean_operation(const TopoDS_Shape&, const TopoDS_Shape&, BOPAlgo_Operation, TopoDS_Shape&);
#else
bool boolean_operation(const TopoDS_Shape&, const TopTools_ListOfShape&, BOPAlgo_Operation, TopoDS_Shape&, double fuzziness = -1.);
bool boolean_operation(const TopoDS_Shape&, const TopoDS_Shape&, BOPAlgo_Operation, TopoDS_Shape&, double fuzziness = -1.);
#endif
bool fit_halfspace(const TopoDS_Shape& a, const TopoDS_Shape& b, TopoDS_Shape& box, double& height);
const Handle_Geom_Curve intersect(const Handle_Geom_Surface&, const Handle_Geom_Surface&);
const Handle_Geom_Curve intersect(const Handle_Geom_Surface&, const TopoDS_Face&);
const Handle_Geom_Curve intersect(const TopoDS_Face&, const Handle_Geom_Surface&);
bool intersect(const Handle_Geom_Curve&, const Handle_Geom_Surface&, gp_Pnt&);
bool intersect(const Handle_Geom_Curve&, const TopoDS_Face&, gp_Pnt&);
bool intersect(const Handle_Geom_Curve&, const TopoDS_Shape&, std::vector<gp_Pnt>&);
bool intersect(const Handle_Geom_Surface&, const TopoDS_Shape&, std::vector< std::pair<Handle_Geom_Surface, Handle_Geom_Curve> >&);
bool closest(const gp_Pnt&, const std::vector<gp_Pnt>&, gp_Pnt&);
bool project(const Handle_Geom_Curve&, const gp_Pnt&, gp_Pnt& p, double& u, double& d);
bool project(const Handle_Geom_Surface&, const TopoDS_Shape&, double& u1, double& v1, double& u2, double& v2, double widen=0.1);
bool find_wall_end_points(const IfcSchema::IfcWall*, gp_Pnt& start, gp_Pnt& end);
IfcSchema::IfcSurfaceStyleShading* get_surface_style(IfcSchema::IfcRepresentationItem* item);
const IfcSchema::IfcRepresentationItem* find_item_carrying_style(const IfcSchema::IfcRepresentationItem* item);
bool create_solid_from_compound(const TopoDS_Shape& compound, TopoDS_Shape& solid);
bool create_solid_from_faces(const TopTools_ListOfShape& face_list, TopoDS_Shape& solid);
bool is_compound(const TopoDS_Shape& shape);
bool is_convex(const TopoDS_Wire& wire);
TopoDS_Shape halfspace_from_plane(const gp_Pln& pln,const gp_Pnt& cent);
gp_Pln plane_from_face(const TopoDS_Face& face);
gp_Pnt point_above_plane(const gp_Pln& pln, bool agree=true);
const TopoDS_Shape& ensure_fit_for_subtraction(const TopoDS_Shape& shape, TopoDS_Shape& solid);
bool profile_helper(int numVerts, double* verts, int numFillets, int* filletIndices, double* filletRadii, gp_Trsf2d trsf, TopoDS_Shape& face);
void apply_tolerance(TopoDS_Shape& s, double t);
bool fill_nonmanifold_wires_with_planar_faces(TopoDS_Shape& shape);
void remove_duplicate_points_from_loop(TColgp_SequenceOfPnt& polygon, bool closed, double tol=-1.);
void remove_collinear_points_from_loop(TColgp_SequenceOfPnt& polygon, bool closed, double tol=-1.);
bool wire_to_sequence_of_point(const TopoDS_Wire&, TColgp_SequenceOfPnt&);
void sequence_of_point_to_wire(const TColgp_SequenceOfPnt&, TopoDS_Wire&, bool closed);
bool approximate_plane_through_wire(const TopoDS_Wire&, gp_Pln&, double eps=-1.);
bool flatten_wire(TopoDS_Wire&);
/// Triangulate the set of wires. The firstmost wire is assumed to be the outer wire.
bool triangulate_wire(const std::vector<TopoDS_Wire>&, TopTools_ListOfShape&);
bool wire_intersections(const TopoDS_Wire & wire, TopTools_ListOfShape & wires);
void select_largest(const TopTools_ListOfShape& shapes, TopoDS_Shape& largest);
static double shape_volume(const TopoDS_Shape& s);
static double face_area(const TopoDS_Face& f);
static TopoDS_Shape apply_transformation(const TopoDS_Shape&, const gp_Trsf&);
static TopoDS_Shape apply_transformation(const TopoDS_Shape&, const gp_GTrsf&);
bool is_identity_transform(IfcUtil::IfcBaseClass*);
IfcSchema::IfcRelVoidsElement::list::ptr find_openings(IfcSchema::IfcProduct* product);
IfcSchema::IfcRepresentation* find_representation(const IfcSchema::IfcProduct*, const std::string&);
std::pair<std::string, double> initializeUnits(IfcSchema::IfcUnitAssignment*);
template <typename P, typename PP>
IfcGeom::BRepElement<P, PP>* create_brep_for_representation_and_product(
const IteratorSettings&, IfcSchema::IfcRepresentation*, IfcSchema::IfcProduct*);
template <typename P, typename PP>
IfcGeom::BRepElement<P, PP>* create_brep_for_processed_representation(
const IteratorSettings&, IfcSchema::IfcRepresentation*, IfcSchema::IfcProduct*, IfcGeom::BRepElement<P, PP>*);
const IfcSchema::IfcMaterial* get_single_material_association(const IfcSchema::IfcProduct*);
IfcSchema::IfcRepresentation* representation_mapped_to(const IfcSchema::IfcRepresentation* representation);
IfcSchema::IfcProduct::list::ptr products_represented_by(const IfcSchema::IfcRepresentation*);
const SurfaceStyle* get_style(const IfcSchema::IfcRepresentationItem*);
const SurfaceStyle* get_style(const IfcSchema::IfcMaterial*);
template <typename T> std::pair<IfcSchema::IfcSurfaceStyle*, T*> _get_surface_style(const IfcSchema::IfcStyledItem* si) {
#ifdef SCHEMA_HAS_IfcStyleAssignmentSelect
IfcEntityList::ptr style_assignments = si->Styles();
for (IfcEntityList::it kt = style_assignments->begin(); kt != style_assignments->end(); ++kt) {
if (!(*kt)->declaration().is(IfcSchema::IfcPresentationStyleAssignment::Class())) {
continue;
}
IfcSchema::IfcPresentationStyleAssignment* style_assignment = (IfcSchema::IfcPresentationStyleAssignment*) *kt;
#else
IfcSchema::IfcPresentationStyleAssignment::list::ptr style_assignments = si->Styles();
for (IfcSchema::IfcPresentationStyleAssignment::list::it kt = style_assignments->begin(); kt != style_assignments->end(); ++kt) {
IfcSchema::IfcPresentationStyleAssignment* style_assignment = *kt;
#endif
IfcEntityList::ptr styles = style_assignment->Styles();
for (IfcEntityList::it lt = styles->begin(); lt != styles->end(); ++lt) {
IfcUtil::IfcBaseClass* style = *lt;
if (style->declaration().is(IfcSchema::IfcSurfaceStyle::Class())) {
IfcSchema::IfcSurfaceStyle* surface_style = (IfcSchema::IfcSurfaceStyle*) style;
if (surface_style->Side() != IfcSchema::IfcSurfaceSide::IfcSurfaceSide_NEGATIVE) {
IfcEntityList::ptr styles_elements = surface_style->Styles();
for (IfcEntityList::it mt = styles_elements->begin(); mt != styles_elements->end(); ++mt) {
if ((*mt)->declaration().is(T::Class())) {
return std::make_pair(surface_style, (T*) *mt);
}
}
}
}
}
}
return std::make_pair<IfcSchema::IfcSurfaceStyle*, T*>(0,0);
}
template <typename T> std::pair<IfcSchema::IfcSurfaceStyle*, T*> get_surface_style(const IfcSchema::IfcRepresentationItem* representation_item) {
// For certain representation items, most notably boolean operands,
// a style definition might reside on one of its operands.
representation_item = find_item_carrying_style(representation_item);
if (representation_item->as<IfcSchema::IfcStyledItem>()) {
return _get_surface_style<T>(representation_item->as<IfcSchema::IfcStyledItem>());
}
IfcSchema::IfcStyledItem::list::ptr styled_items = representation_item->StyledByItem();
if (styled_items->size()) {
// StyledByItem is a SET [0:1] OF IfcStyledItem, so we return after the first IfcStyledItem:
return _get_surface_style<T>(*styled_items->begin());
}
return std::make_pair<IfcSchema::IfcSurfaceStyle*, T*>(0,0);
}
void purge_cache() {
// Rather hack-ish, but a stopgap solution to keep memory under control
// for large files. SurfaceStyles need to be kept at all costs, as they
// are read later on when serializing Collada files.
#ifndef NO_CACHE
cache = MAKE_TYPE_NAME(Cache)();
#endif
}
void set_conversion_placement_rel_to(const IfcParse::declaration* type);
#include "IfcRegisterGeomHeader.h"
virtual void setValue(GeomValue var, double value);
virtual double getValue(GeomValue var) const;
virtual IfcGeom::BRepElement<double>* convert(
const IteratorSettings& settings, IfcUtil::IfcBaseClass* representation,
IfcUtil::IfcBaseClass* product)
{
return create_brep_for_representation_and_product<double, double>(settings, (IfcSchema::IfcRepresentation*) representation, (IfcSchema::IfcProduct*) product);
}
virtual IfcRepresentationShapeItems convert(IfcUtil::IfcBaseClass* item) {
IfcRepresentationShapeItems items;
bool success = convert_shapes(item, items);
if (!success) {
throw IfcParse::IfcException("Failed to process representation item");
}
return items;
}
virtual bool convert_placement(IfcUtil::IfcBaseClass* item, gp_Trsf& trsf) {
if (item->as<IfcSchema::IfcObjectPlacement>()) {
return convert(item->as<IfcSchema::IfcObjectPlacement>(), trsf);
} else {
return false;
}
}
};
IfcUtil::IfcBaseClass* MAKE_TYPE_NAME(tesselate_)(const TopoDS_Shape& shape, double deflection);
IfcUtil::IfcBaseClass* MAKE_TYPE_NAME(serialise_)(const TopoDS_Shape& shape, bool advanced);
}
#endif
-198
View File
@@ -1,198 +0,0 @@
/********************************************************************************
* *
* 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 IfcRegister.h *
* *
********************************************************************************/
#include <algorithm>
#include <gp_Pnt.hxx>
#include <gp_Vec.hxx>
#include <gp_Dir.hxx>
#include <gp_Pnt2d.hxx>
#include <gp_Vec2d.hxx>
#include <gp_Dir2d.hxx>
#include <gp_Mat.hxx>
#include <gp_Mat2d.hxx>
#include <gp_GTrsf.hxx>
#include <gp_GTrsf2d.hxx>
#include <gp_Trsf.hxx>
#include <gp_Trsf2d.hxx>
#include <gp_Ax3.hxx>
#include <gp_Ax2d.hxx>
#include <gp_Pln.hxx>
#include <gp_Circ.hxx>
#include <TColgp_Array1OfPnt.hxx>
#include <TColgp_Array1OfPnt2d.hxx>
#include <TColStd_Array1OfReal.hxx>
#include <TColStd_Array1OfInteger.hxx>
#include <Geom_Line.hxx>
#include <Geom_Circle.hxx>
#include <Geom_Ellipse.hxx>
#include <Geom_TrimmedCurve.hxx>
#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>
#include <TopExp_Explorer.hxx>
#include <BRepPrimAPI_MakePrism.hxx>
#include <BRepBuilderAPI_MakeShell.hxx>
#include <BRepBuilderAPI_MakeSolid.hxx>
#include <BRepPrimAPI_MakeHalfSpace.hxx>
#include <BRepAlgoAPI_Cut.hxx>
#include <ShapeFix_Shape.hxx>
#include <ShapeFix_ShapeTolerance.hxx>
#include <ShapeFix_Solid.hxx>
#include <BRepFilletAPI_MakeFillet2d.hxx>
#include <TopLoc_Location.hxx>
#include "../ifcgeom/IfcGeom.h"
#ifdef SCHEMA_HAS_IfcBSplineCurveWithKnots
#include <Geom_BSplineCurve.hxx>
#endif
#define Kernel MAKE_TYPE_NAME(Kernel)
bool IfcGeom::Kernel::convert(const IfcSchema::IfcCircle* l, Handle(Geom_Curve)& curve) {
const double r = l->Radius() * getValue(GV_LENGTH_UNIT);
if ( r < ALMOST_ZERO ) {
Logger::Message(Logger::LOG_ERROR, "Radius not greater than zero for:", l);
return false;
}
gp_Trsf trsf;
IfcSchema::IfcAxis2Placement* placement = l->Position();
if (placement->declaration().is(IfcSchema::IfcAxis2Placement3D::Class())) {
IfcGeom::Kernel::convert((IfcSchema::IfcAxis2Placement3D*)placement,trsf);
} else {
gp_Trsf2d trsf2d;
IfcGeom::Kernel::convert((IfcSchema::IfcAxis2Placement2D*)placement,trsf2d);
trsf = trsf2d;
}
gp_Ax2 ax = gp_Ax2().Transformed(trsf);
curve = new Geom_Circle(ax, r);
return true;
}
bool IfcGeom::Kernel::convert(const IfcSchema::IfcEllipse* l, Handle(Geom_Curve)& curve) {
double x = l->SemiAxis1() * getValue(GV_LENGTH_UNIT);
double y = l->SemiAxis2() * getValue(GV_LENGTH_UNIT);
if (x < ALMOST_ZERO || y < ALMOST_ZERO) {
Logger::Message(Logger::LOG_ERROR, "Radius not greater than zero for:", l);
return false;
}
// Open Cascade does not allow ellipses of which the minor radius
// is greater than the major radius. Hence, in this case, the
// ellipse is rotated. Note that special care needs to be taken
// when creating a trimmed curve off of an ellipse like this.
const bool rotated = y > x;
gp_Trsf trsf;
IfcSchema::IfcAxis2Placement* placement = l->Position();
if (placement->declaration().is(IfcSchema::IfcAxis2Placement3D::Class())) {
convert((IfcSchema::IfcAxis2Placement3D*)placement,trsf);
} else {
gp_Trsf2d trsf2d;
convert((IfcSchema::IfcAxis2Placement2D*)placement,trsf2d);
trsf = trsf2d;
}
gp_Ax2 ax = gp_Ax2();
if (rotated) {
ax.Rotate(ax.Axis(), M_PI / 2.);
std::swap(x, y);
}
ax.Transform(trsf);
curve = new Geom_Ellipse(ax, x, y);
return true;
}
bool IfcGeom::Kernel::convert(const IfcSchema::IfcLine* l, Handle(Geom_Curve)& curve) {
gp_Pnt pnt;gp_Vec vec;
convert(l->Pnt(),pnt);
convert(l->Dir(),vec);
// See note at IfcGeomWires.cpp:237
curve = new Geom_Line(pnt,vec);
return true;
}
#ifdef SCHEMA_HAS_IfcBSplineCurveWithKnots
bool IfcGeom::Kernel::convert(const IfcSchema::IfcBSplineCurveWithKnots* l, Handle(Geom_Curve)& curve) {
const bool is_rational = l->declaration().is(IfcSchema::IfcRationalBSplineCurveWithKnots::Class());
const IfcSchema::IfcCartesianPoint::list::ptr cps = l->ControlPointsList();
const std::vector<int> mults = l->KnotMultiplicities();
const std::vector<double> knots = l->Knots();
TColgp_Array1OfPnt Poles(0, cps->size() - 1);
TColStd_Array1OfReal Weights(0, cps->size() - 1);
TColStd_Array1OfReal Knots(0, (int)knots.size() - 1);
TColStd_Array1OfInteger Mults(0, (int)mults.size() - 1);
Standard_Integer Degree = l->Degree();
Standard_Boolean Periodic = l->ClosedCurve();
int i;
if (is_rational) {
IfcSchema::IfcRationalBSplineCurveWithKnots* rl = (IfcSchema::IfcRationalBSplineCurveWithKnots*)l;
std::vector<double> weights = rl->WeightsData();
i = 0;
for (std::vector<double>::const_iterator it = weights.begin(); it != weights.end(); ++it, ++i) {
Weights(i) = *it;
}
}
i = 0;
for (IfcSchema::IfcCartesianPoint::list::it it = cps->begin(); it != cps->end(); ++it, ++i) {
gp_Pnt pnt;
if (!convert(*it, pnt)) return false;
Poles(i) = pnt;
}
i = 0;
for (std::vector<int>::const_iterator it = mults.begin(); it != mults.end(); ++it, ++i) {
Mults(i) = *it;
}
i = 0;
for (std::vector<double>::const_iterator it = knots.begin(); it != knots.end(); ++it, ++i) {
Knots(i) = *it;
}
if (is_rational) {
curve = new Geom_BSplineCurve(Poles, Weights, Knots, Mults, Degree, Periodic);
} else {
curve = new Geom_BSplineCurve(Poles, Knots, Mults, Degree, Periodic);
}
return true;
}
#endif
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
-423
View File
@@ -1,423 +0,0 @@
/********************************************************************************
* *
* 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 IfcRegister.h *
* *
********************************************************************************/
#include <gp_Pnt.hxx>
#include <gp_Vec.hxx>
#include <gp_Dir.hxx>
#include <gp_Pnt2d.hxx>
#include <gp_Vec2d.hxx>
#include <gp_Dir2d.hxx>
#include <gp_Mat.hxx>
#include <gp_Mat2d.hxx>
#include <gp_GTrsf.hxx>
#include <gp_GTrsf2d.hxx>
#include <gp_Trsf.hxx>
#include <gp_Trsf2d.hxx>
#include <gp_Ax3.hxx>
#include <gp_Ax2d.hxx>
#include <gp_Pln.hxx>
#include <gp_Circ.hxx>
#include <TColgp_Array1OfPnt.hxx>
#include <TColgp_Array1OfPnt2d.hxx>
#include <TColStd_Array1OfReal.hxx>
#include <TColStd_Array1OfInteger.hxx>
#include <Geom_Line.hxx>
#include <Geom_Circle.hxx>
#include <Geom_Ellipse.hxx>
#include <Geom_TrimmedCurve.hxx>
#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>
#include <TopExp_Explorer.hxx>
#include <BRepPrimAPI_MakePrism.hxx>
#include <BRepBuilderAPI_MakeShell.hxx>
#include <BRepBuilderAPI_MakeSolid.hxx>
#include <BRepPrimAPI_MakeHalfSpace.hxx>
#include <BRepAlgoAPI_Cut.hxx>
#include <ShapeFix_Shape.hxx>
#include <ShapeFix_ShapeTolerance.hxx>
#include <ShapeFix_Solid.hxx>
#include <BRepFilletAPI_MakeFillet2d.hxx>
#include <TopLoc_Location.hxx>
#include "../ifcgeom/IfcGeom.h"
#define Kernel MAKE_TYPE_NAME(Kernel)
namespace {
// Helper functions (re)set gp_(G)Trsf(2d) forms explicitly to 'Identity'
// so that it can be easily identified in the IfcMappedItem processing
// For axis placements detect equality early in order for the
// relatively computionaly expensive gp_Trsf calculation to be skipped
template <typename T>
bool axis_equal(const T& a, const T& b, double tolerance);
template <>
bool axis_equal(const gp_Ax3& a, const gp_Ax3& b, double tolerance) {
if (!a.Location().IsEqual(b.Location(), tolerance)) return false;
// Note that the tolerance below is angular, above is linear. Since architectural
// objects are about 1m'ish in scale, it should be somewhat equivalent. Besides,
// this is mostly a filter for NULL or default values in the placements.
if (!a.Direction().IsEqual(b.Direction(), tolerance)) return false;
if (!a.XDirection().IsEqual(b.XDirection(), tolerance)) return false;
if (!a.YDirection().IsEqual(b.YDirection(), tolerance)) return false;
return true;
}
bool axis_equal(const gp_Ax2d& a, const gp_Ax2d& b, double tolerance) {
if (!a.Location().IsEqual(b.Location(), tolerance)) return false;
if (!a.Direction().IsEqual(b.Direction(), tolerance)) return false;
return true;
}
template <typename T> struct dimension_count {};
template <> struct dimension_count <gp_Trsf2d > { static const int n = 2; };
template <> struct dimension_count <gp_GTrsf2d> { static const int n = 2; };
template <> struct dimension_count < gp_Trsf > { static const int n = 3; };
template <> struct dimension_count < gp_GTrsf > { static const int n = 3; };
template <typename T>
bool is_identity(const T& t, double tolerance) {
// Note the {1, n+1} range due to Open Cascade's 1-based indexing
// Note the {1, n+2} range due to the translation part of the matrix
for (int i = 1; i < dimension_count<T>::n + 2; ++i) {
for (int j = 1; j < dimension_count<T>::n + 1; ++j) {
const double iden_value = i == j ? 1. : 0.;
const double trsf_value = t.Value(j, i);
if (fabs(trsf_value - iden_value) > tolerance) {
return false;
}
}
}
return true;
}
}
bool IfcGeom::Kernel::convert(const IfcSchema::IfcCartesianPoint* l, gp_Pnt& point) {
IN_CACHE(IfcCartesianPoint,l,gp_Pnt,point)
std::vector<double> xyz = l->Coordinates();
point = gp_Pnt(
xyz.size() ? (xyz[0]*getValue(GV_LENGTH_UNIT)) : 0.0f,
xyz.size() > 1 ? (xyz[1]*getValue(GV_LENGTH_UNIT)) : 0.0f,
xyz.size() > 2 ? (xyz[2]*getValue(GV_LENGTH_UNIT)) : 0.0f
);
CACHE(IfcCartesianPoint,l,point)
return true;
}
bool IfcGeom::Kernel::convert(const IfcSchema::IfcDirection* l, gp_Dir& dir) {
IN_CACHE(IfcDirection,l,gp_Dir,dir)
std::vector<double> xyz = l->DirectionRatios();
dir = gp_Dir(
xyz.size() ? xyz[0] : 0.0f,
xyz.size() > 1 ? xyz[1] : 0.0f,
xyz.size() > 2 ? xyz[2] : 0.0f
);
CACHE(IfcDirection,l,dir)
return true;
}
bool IfcGeom::Kernel::convert(const IfcSchema::IfcVector* l, gp_Vec& v) {
IN_CACHE(IfcVector,l,gp_Vec,v)
gp_Dir d;
IfcGeom::Kernel::convert(l->Orientation(),d);
v = l->Magnitude() * getValue(GV_LENGTH_UNIT) * d;
CACHE(IfcVector,l,v)
return true;
}
bool IfcGeom::Kernel::convert(const IfcSchema::IfcAxis2Placement3D* l, gp_Trsf& trsf) {
IN_CACHE(IfcAxis2Placement3D, l, gp_Trsf, trsf)
gp_Pnt o;
gp_Dir axis(0, 0, 1);
gp_Dir refDirection;
IfcGeom::Kernel::convert(l->Location(), o);
const bool hasAxis = l->hasAxis();
const bool hasRef = l->hasRefDirection();
if (hasAxis != hasRef) {
Logger::Warning("Axis and RefDirection should be specified together", l);
}
if (hasAxis) {
IfcGeom::Kernel::convert(l->Axis(), axis);
}
if (hasRef) {
IfcGeom::Kernel::convert(l->RefDirection(), refDirection);
} else {
if (!axis.IsParallel(gp::DX(), 1.e-5)) {
refDirection = gp::DX();
} else {
refDirection = gp::DZ();
}
gp_Vec Xvec = axis.Dot(refDirection) * axis;
gp_Vec Xaxis = refDirection.XYZ() - Xvec.XYZ();
refDirection = Xaxis;
}
gp_Ax3 ax3(o, axis, refDirection);
if (!axis_equal(ax3, (gp_Ax3) gp::XOY(), getValue(GV_PRECISION))) {
trsf.SetTransformation(ax3, gp::XOY());
}
CACHE(IfcAxis2Placement3D,l,trsf)
return true;
}
bool IfcGeom::Kernel::convert(const IfcSchema::IfcAxis1Placement* l, gp_Ax1& ax) {
IN_CACHE(IfcAxis1Placement,l,gp_Ax1,ax)
gp_Pnt o;gp_Dir axis = gp_Dir(0,0,1);
IfcGeom::Kernel::convert(l->Location(),o);
if ( l->hasAxis() ) IfcGeom::Kernel::convert(l->Axis(), axis);
ax = gp_Ax1(o, axis);
CACHE(IfcAxis1Placement,l,ax)
return true;
}
bool IfcGeom::Kernel::convert(const IfcSchema::IfcCartesianTransformationOperator3D* l, gp_Trsf& trsf) {
IN_CACHE(IfcCartesianTransformationOperator3D,l,gp_Trsf,trsf)
gp_Pnt origin;
IfcGeom::Kernel::convert(l->LocalOrigin(),origin);
gp_Dir axis1 (1.,0.,0.);
gp_Dir axis2 (0.,1.,0.);
gp_Dir axis3 (0.,0.,1.);
if ( l->hasAxis1() ) IfcGeom::Kernel::convert(l->Axis1(),axis1);
if ( l->hasAxis2() ) IfcGeom::Kernel::convert(l->Axis2(),axis2);
if ( l->hasAxis3() ) IfcGeom::Kernel::convert(l->Axis3(),axis3);
gp_Ax3 ax3 (origin,axis3,axis1);
if ( axis2.Dot(ax3.YDirection()) < 0 ) ax3.YReverse();
if (!axis_equal(ax3, (gp_Ax3) gp::XOY(), getValue(GV_PRECISION))) {
trsf.SetTransformation(ax3);
trsf.Invert();
}
if (l->hasScale() && !ALMOST_THE_SAME(l->Scale(), 1.)) {
trsf.SetScaleFactor(l->Scale());
}
CACHE(IfcCartesianTransformationOperator3D,l,trsf)
return true;
}
bool IfcGeom::Kernel::convert(const IfcSchema::IfcCartesianTransformationOperator2D* l, gp_Trsf2d& trsf) {
IN_CACHE(IfcCartesianTransformationOperator2D,l,gp_Trsf2d,trsf)
gp_Pnt origin;
gp_Dir axis1 (1.,0.,0.);
gp_Dir axis2 (0.,1.,0.);
IfcGeom::Kernel::convert(l->LocalOrigin(),origin);
if ( l->hasAxis1() ) IfcGeom::Kernel::convert(l->Axis1(),axis1);
if ( l->hasAxis2() ) IfcGeom::Kernel::convert(l->Axis2(),axis2);
const gp_Pnt2d origin2d(origin.X(), origin.Y());
const gp_Dir2d axis12d(axis1.X(), axis1.Y());
const gp_Dir2d axis22d(axis2.X(), axis2.Y());
// A better match to represent the IfcCartesianTransformationOperator2D would
// be the gp_Ax22d, but to my knowledge no easy way exists to convert it into
// a gp_Trsf2d. Easiest would probably be to simply update the underlying
// gp_Mat2d directly.
const gp_Ax2d ax2d (origin2d, axis12d);
trsf.SetTransformation(ax2d);
if ( ax2d.Direction().Rotated(M_PI / 2.).Dot(axis22d) < 0. ) {
gp_Trsf2d mirror; mirror.SetMirror(ax2d);
trsf.Multiply(mirror);
}
trsf.Invert();
if ( l->hasScale() && !ALMOST_THE_SAME(l->Scale(), 1.) ) trsf.SetScaleFactor(l->Scale());
if (is_identity(trsf, getValue(GV_PRECISION))) {
trsf = gp_Trsf2d();
}
CACHE(IfcCartesianTransformationOperator2D,l,trsf)
return true;
}
bool IfcGeom::Kernel::convert(const IfcSchema::IfcCartesianTransformationOperator3DnonUniform* l, gp_GTrsf& gtrsf) {
IN_CACHE(IfcCartesianTransformationOperator3DnonUniform,l,gp_GTrsf,gtrsf)
gp_Trsf trsf;
gp_Pnt origin;
IfcGeom::Kernel::convert(l->LocalOrigin(),origin);
gp_Dir axis1 (1.,0.,0.);
gp_Dir axis2 (0.,1.,0.);
gp_Dir axis3 (0.,0.,1.);
if ( l->hasAxis1() ) IfcGeom::Kernel::convert(l->Axis1(),axis1);
if ( l->hasAxis2() ) IfcGeom::Kernel::convert(l->Axis2(),axis2);
if ( l->hasAxis3() ) IfcGeom::Kernel::convert(l->Axis3(),axis3);
gp_Ax3 ax3 (origin,axis3,axis1);
if ( axis2.Dot(ax3.YDirection()) < 0 ) ax3.YReverse();
trsf.SetTransformation(ax3);
trsf.Invert();
const double scale1 = l->hasScale() ? l->Scale() : 1.0f;
const double scale2 = l->hasScale2() ? l->Scale2() : scale1;
const double scale3 = l->hasScale3() ? l->Scale3() : scale1;
gtrsf = gp_GTrsf();
gtrsf.SetValue(1,1,scale1);
gtrsf.SetValue(2,2,scale2);
gtrsf.SetValue(3,3,scale3);
gtrsf.PreMultiply(trsf);
if (is_identity(gtrsf, getValue(GV_PRECISION))) {
gtrsf = gp_GTrsf();
}
CACHE(IfcCartesianTransformationOperator3DnonUniform,l,gtrsf)
return true;
}
bool IfcGeom::Kernel::convert(const IfcSchema::IfcCartesianTransformationOperator2DnonUniform* l, gp_GTrsf2d& gtrsf) {
IN_CACHE(IfcCartesianTransformationOperator2DnonUniform,l,gp_GTrsf2d,gtrsf)
gp_Trsf2d trsf;
gp_Pnt origin;
gp_Dir axis1 (1.,0.,0.);
gp_Dir axis2 (0.,1.,0.);
IfcGeom::Kernel::convert(l->LocalOrigin(),origin);
if ( l->hasAxis1() ) IfcGeom::Kernel::convert(l->Axis1(),axis1);
if ( l->hasAxis2() ) IfcGeom::Kernel::convert(l->Axis2(),axis2);
const gp_Pnt2d origin2d(origin.X(), origin.Y());
const gp_Dir2d axis12d(axis1.X(), axis1.Y());
const gp_Dir2d axis22d(axis2.X(), axis2.Y());
const gp_Ax2d ax2d (origin2d, axis12d);
trsf.SetTransformation(ax2d);
if ( ax2d.Direction().Rotated(M_PI / 2.).Dot(axis22d) < 0. ) {
gp_Trsf2d mirror; mirror.SetMirror(ax2d);
trsf.Multiply(mirror);
}
trsf.Invert();
const double scale1 = l->hasScale() ? l->Scale() : 1.0f;
const double scale2 = l->hasScale2() ? l->Scale2() : scale1;
gtrsf = gp_GTrsf2d();
gtrsf.SetValue(1,1,scale1);
gtrsf.SetValue(2,2,scale2);
gtrsf.Multiply(trsf);
if (is_identity(gtrsf, getValue(GV_PRECISION))) {
gtrsf = gp_GTrsf2d();
}
CACHE(IfcCartesianTransformationOperator2DnonUniform,l,gtrsf)
return true;
}
bool IfcGeom::Kernel::convert(const IfcSchema::IfcPlane* pln, gp_Pln& plane) {
IN_CACHE(IfcPlane,pln,gp_Pln,plane)
IfcSchema::IfcAxis2Placement3D* l = pln->Position();
gp_Pnt o;gp_Dir axis = gp_Dir(0,0,1);gp_Dir refDirection;
IfcGeom::Kernel::convert(l->Location(),o);
bool hasRef = l->hasRefDirection();
if ( l->hasAxis() ) IfcGeom::Kernel::convert(l->Axis(),axis);
if ( hasRef ) IfcGeom::Kernel::convert(l->RefDirection(),refDirection);
gp_Ax3 ax3;
if ( hasRef ) ax3 = gp_Ax3(o,axis,refDirection);
else ax3 = gp_Ax3(o,axis);
plane = gp_Pln(ax3);
CACHE(IfcPlane,pln,plane)
return true;
}
bool IfcGeom::Kernel::convert(const IfcSchema::IfcAxis2Placement2D* l, gp_Trsf2d& trsf) {
IN_CACHE(IfcAxis2Placement2D,l,gp_Trsf2d,trsf)
gp_Pnt P; gp_Dir V (1,0,0);
IfcGeom::Kernel::convert(l->Location(),P);
if ( l->hasRefDirection() )
IfcGeom::Kernel::convert(l->RefDirection(),V);
gp_Ax2d axis(gp_Pnt2d(P.X(),P.Y()), gp_Dir2d(V.X(),V.Y()));
if (!axis_equal(axis, gp_Ax2d(), getValue(GV_PRECISION))) {
trsf.SetTransformation(axis, gp_Ax2d());
}
CACHE(IfcAxis2Placement2D,l,trsf)
return true;
}
void IfcGeom::Kernel::set_conversion_placement_rel_to(const IfcParse::declaration* type) {
placement_rel_to = type;
}
bool IfcGeom::Kernel::convert(const IfcSchema::IfcObjectPlacement* l, gp_Trsf& trsf) {
IN_CACHE(IfcObjectPlacement,l,gp_Trsf,trsf)
if ( ! l->declaration().is(IfcSchema::IfcLocalPlacement::Class()) ) {
Logger::Message(Logger::LOG_ERROR, "Unsupported IfcObjectPlacement:", l);
return false;
}
IfcSchema::IfcLocalPlacement* current = (IfcSchema::IfcLocalPlacement*)l;
for (;;) {
gp_Trsf trsf2;
IfcSchema::IfcAxis2Placement* relplacement = current->RelativePlacement();
if ( relplacement->declaration().is(IfcSchema::IfcAxis2Placement3D::Class()) ) {
IfcGeom::Kernel::convert((IfcSchema::IfcAxis2Placement3D*)relplacement,trsf2);
trsf.PreMultiply(trsf2);
}
if ( current->hasPlacementRelTo() ) {
IfcSchema::IfcObjectPlacement* parent = current->PlacementRelTo();
IfcSchema::IfcProduct::list::ptr parentPlaces = parent->PlacesObject();
bool parentPlacesType = false;
for ( IfcSchema::IfcProduct::list::it iter = parentPlaces->begin();
iter != parentPlaces->end(); ++iter) {
if ( (*iter)->declaration().is(*placement_rel_to) ) parentPlacesType = true;
}
if ( parentPlacesType ) break;
else if ( parent->declaration().is(IfcSchema::IfcLocalPlacement::Class()) )
current = (IfcSchema::IfcLocalPlacement*)current->PlacementRelTo();
else break;
} else break;
}
CACHE(IfcObjectPlacement,l,trsf)
return true;
}
@@ -1,32 +0,0 @@
#include "IfcGeomIteratorImplementation.h"
#include "../ifcgeom_schema_agnostic/IteratorImplementation.h"
namespace IfcGeom {
template class MAKE_TYPE_NAME(IteratorImplementation_)<float, float>;
template class MAKE_TYPE_NAME(IteratorImplementation_)<float, double>;
template class MAKE_TYPE_NAME(IteratorImplementation_)<double, double>;
}
#define MAKE_INIT_FN__(a, b) init_ ## a ## b
#define MAKE_INIT_FN_(a, b) MAKE_INIT_FN__(a, b)
#define MAKE_INIT_FN(t) MAKE_INIT_FN_(t, IfcSchema)
namespace {
template <typename P, typename PP>
struct MAKE_TYPE_NAME(factory_t) {
IfcGeom::IteratorImplementation<P, PP>* operator()(const IfcGeom::IteratorSettings& settings, IfcParse::IfcFile* file, const std::vector<IfcGeom::filter_t>& filters, int num_threads) const {
return new IfcGeom::MAKE_TYPE_NAME(IteratorImplementation_)<P, PP>(settings, file, filters, num_threads);
}
};
}
template <typename P, typename PP>
void MAKE_INIT_FN(IteratorImplementation_)(IteratorFactoryImplementation<P, PP>* mapping) {
static const std::string schema_name = STRINGIFY(IfcSchema);
MAKE_TYPE_NAME(factory_t)<P, PP> factory;
mapping->bind(schema_name, factory);
}
template void MAKE_INIT_FN(IteratorImplementation_)<float, float>(IteratorFactoryImplementation<float, float>*);
template void MAKE_INIT_FN(IteratorImplementation_)<float, double>(IteratorFactoryImplementation<float, double>*);
template void MAKE_INIT_FN(IteratorImplementation_)<double, double>(IteratorFactoryImplementation<double, double>*);
-988
View File
@@ -1,988 +0,0 @@
/********************************************************************************
* *
* 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/>. *
* *
********************************************************************************/
/********************************************************************************
* *
* Geometrical data in an IFC file consists of shapes (IfcShapeRepresentation) *
* and instances (SUBTYPE OF IfcBuildingElement e.g. IfcWindow). *
* *
* IfcGeom::Representation::Triangulation is a class that represents a *
* triangulated IfcShapeRepresentation. *
* Triangulation.verts is a 1 dimensional vector of float defining the *
* cartesian coordinates of the vertices of the triangulated shape in the *
* format of [x1,y1,z1,..,xn,yn,zn] *
* Triangulation.faces is a 1 dimensional vector of int containing the *
* indices of the triangles referencing positions in Triangulation.verts *
* Triangulation.edges is a 1 dimensional vector of int in {0,1} that dictates*
* the visibility of the edges that span the faces in Triangulation.faces *
* *
* IfcGeom::Element represents the actual IfcBuildingElements. *
* IfcGeomObject.name is the GUID of the element *
* IfcGeomObject.type is the datatype of the element e.g. IfcWindow *
* IfcGeomObject.mesh is a pointer to an IfcMesh *
* IfcGeomObject.transformation.matrix is a 4x3 matrix that defines the *
* orientation and translation of the mesh in relation to the world origin *
* *
* IfcGeom::Iterator::initialize() *
* finds the most suitable representation contexts. Returns true iff *
* at least a single representation will process successfully *
* *
* IfcGeom::Iterator::get() *
* returns a pointer to the current IfcGeom::Element *
* *
* IfcGeom::Iterator::next() *
* returns true iff a following entity is available for a successive call to *
* IfcGeom::Iterator::get() *
* *
* IfcGeom::Iterator::progress() *
* returns an int in [0..100] that indicates the overall progress *
* *
********************************************************************************/
#ifndef IFCGEOMITERATOR_H
#define IFCGEOMITERATOR_H
#include <map>
#include <set>
#include <vector>
#include <limits>
#include <algorithm>
#include <atomic>
#include <future>
#include <thread>
#include <chrono>
#include <boost/algorithm/string.hpp>
#include <gp_Mat.hxx>
#include <gp_Mat2d.hxx>
#include <gp_GTrsf.hxx>
#include <gp_GTrsf2d.hxx>
#include <gp_Trsf.hxx>
#include <gp_Trsf2d.hxx>
#include "../ifcparse/IfcFile.h"
#include "../ifcgeom/IfcGeom.h"
#include "../ifcgeom/IfcGeomElement.h"
#include "../ifcgeom_schema_agnostic/IfcGeomMaterial.h"
#include "../ifcgeom/IfcGeomIteratorSettings.h"
#include "../ifcgeom/IfcRepresentationShapeItem.h"
#include "../ifcgeom_schema_agnostic/IfcGeomFilter.h"
#include "../ifcgeom_schema_agnostic/IteratorImplementation.h"
#include <atomic>
// The infamous min & max Win32 #defines can leak here from OCE depending on the build configuration
#ifdef min
#undef min
#endif
#ifdef max
#undef max
#endif
namespace {
template <typename P, typename PP=P>
struct geometry_conversion_task {
int index;
IfcSchema::IfcRepresentation *representation;
IfcSchema::IfcProduct::list::ptr products;
std::vector<IfcGeom::BRepElement<P, PP>*> breps;
std::vector<IfcGeom::Element<P, PP>*> elements;
};
template <typename P, typename PP=P>
IfcGeom::Element<P, PP>* process_based_on_settings(
const IfcGeom::IteratorSettings& settings,
IfcGeom::BRepElement<P, PP>* elem,
IfcGeom::TriangulationElement<P, PP>* previous=nullptr)
{
if (settings.get(IfcGeom::IteratorSettings::USE_BREP_DATA)) {
try {
return new IfcGeom::SerializedElement<P, PP>(*elem);
} catch (...) {
Logger::Message(Logger::LOG_ERROR, "Getting a serialized element from model failed.");
return nullptr;
}
} else if (!settings.get(IfcGeom::IteratorSettings::DISABLE_TRIANGULATION)) {
try {
if (!previous) {
return new IfcGeom::TriangulationElement<P, PP>(*elem);
} else {
return new IfcGeom::TriangulationElement<P, PP>(*elem, previous->geometry_pointer());
}
} catch (...) {
Logger::Message(Logger::LOG_ERROR, "Getting a triangulation element from model failed.");
return nullptr;
}
} else {
return elem;
}
}
template <typename P, typename PP = P>
void create_element(
IfcGeom::MAKE_TYPE_NAME(Kernel)* kernel,
const IfcGeom::IteratorSettings& settings,
geometry_conversion_task<P, PP>* rep)
{
IfcSchema::IfcRepresentation *representation = rep->representation;
IfcSchema::IfcProduct *product = *rep->products->begin();
auto brep = kernel->create_brep_for_representation_and_product<P, PP>(settings, representation, product);
if (!brep) {
return;
}
auto elem = process_based_on_settings(settings, brep);
if (!elem) {
return;
}
rep->breps = { brep };
rep->elements = { elem };
for (auto it = rep->products->begin() + 1; it != rep->products->end(); ++it) {
auto brep2 = kernel->create_brep_for_processed_representation<P, PP>(settings, representation, *it, brep);
if (brep2) {
auto elem2 = process_based_on_settings(settings, brep, dynamic_cast<IfcGeom::TriangulationElement<P, PP>*>(elem));
if (elem2) {
rep->breps.push_back(brep2);
rep->elements.push_back(elem2);
}
}
}
}
}
namespace IfcGeom {
template <typename P, typename PP>
class MAKE_TYPE_NAME(IteratorImplementation_) : public IteratorImplementation<P, PP> {
private:
int num_threads_;
std::atomic<int> progress_;
std::vector<geometry_conversion_task<P, PP>> tasks_;
std::vector<IfcGeom::Element<P, PP>*> all_processed_elements_;
std::vector<IfcGeom::BRepElement<P, PP>*> all_processed_native_elements_;
typename std::vector<IfcGeom::Element<P, PP>*>::const_iterator task_result_iterator_;
typename std::vector<IfcGeom::BRepElement<P, PP>*>::const_iterator native_task_result_iterator_;
MAKE_TYPE_NAME(IteratorImplementation_)(const MAKE_TYPE_NAME(IteratorImplementation_)&); // N/I
MAKE_TYPE_NAME(IteratorImplementation_)& operator=(const MAKE_TYPE_NAME(IteratorImplementation_)&); // N/I
MAKE_TYPE_NAME(Kernel) kernel;
IteratorSettings settings;
IfcParse::IfcFile* ifc_file;
// A container and iterator for IfcRepresentations
IfcSchema::IfcRepresentation::list::ptr representations;
IfcSchema::IfcRepresentation::list::it representation_iterator;
// The object is fetched beforehand to be sure that get() returns a valid element
TriangulationElement<P, PP>* current_triangulation;
BRepElement<P, PP>* current_shape_model;
SerializedElement<P, PP>* current_serialization;
// A container and iterator for IfcBuildingElements for the current IfcRepresentation referenced by *representation_iterator
IfcSchema::IfcProduct::list::ptr ifcproducts;
IfcSchema::IfcProduct::list::it ifcproduct_iterator;
IfcSchema::IfcRepresentation::list::ptr ok_mapped_representations;
int done;
int total;
std::string unit_name;
double unit_magnitude;
gp_XYZ bounds_min_;
gp_XYZ bounds_max_;
std::vector<filter_t> filters_;
struct filter_match
{
filter_match(IfcSchema::IfcProduct *prod) : product(prod) {}
bool operator()(const filter_t& filter) const { return filter(product); }
IfcSchema::IfcProduct* product;
};
void initUnits() {
IfcSchema::IfcProject::list::ptr projects = ifc_file->instances_by_type<IfcSchema::IfcProject>();
if (projects->size() == 1) {
IfcSchema::IfcProject* project = *projects->begin();
std::pair<std::string, double> length_unit = kernel.initializeUnits(project->UnitsInContext());
unit_name = length_unit.first;
unit_magnitude = length_unit.second;
} else {
Logger::Warning("A single IfcProject is expected (encountered " + boost::lexical_cast<std::string>(projects->size()) + "); unable to read unit information.");
}
}
/// @todo public/private sections all over the place: move all public to the beginning of the class
public:
typedef P Precision;
typedef PP PlacementPrecision;
bool initialize() {
try {
initUnits();
} catch (const std::exception& e) {
Logger::Error(e);
}
std::set<std::string> allowed_context_types;
allowed_context_types.insert("model");
allowed_context_types.insert("plan");
allowed_context_types.insert("notdefined");
std::set<std::string> context_types;
if (!settings.get(IteratorSettings::EXCLUDE_SOLIDS_AND_SURFACES)) {
// Really this should only be 'Model', as per
// the standard 'Design' is deprecated. So,
// just for backwards compatibility:
context_types.insert("model");
context_types.insert("design");
// Some earlier (?) versions DDS-CAD output their own ContextTypes
context_types.insert("model view");
context_types.insert("detail view");
}
if (settings.get(IteratorSettings::INCLUDE_CURVES)) {
context_types.insert("plan");
}
double lowest_precision_encountered = std::numeric_limits<double>::infinity();
bool any_precision_encountered = false;
representations = IfcSchema::IfcRepresentation::list::ptr(new IfcSchema::IfcRepresentation::list);
ok_mapped_representations = IfcSchema::IfcRepresentation::list::ptr(new IfcSchema::IfcRepresentation::list);
IfcSchema::IfcGeometricRepresentationContext::list::it it;
IfcSchema::IfcGeometricRepresentationSubContext::list::it jt;
IfcSchema::IfcGeometricRepresentationContext::list::ptr contexts =
ifc_file->instances_by_type<IfcSchema::IfcGeometricRepresentationContext>();
IfcSchema::IfcGeometricRepresentationContext::list::ptr filtered_contexts (new IfcSchema::IfcGeometricRepresentationContext::list);
for (it = contexts->begin(); it != contexts->end(); ++it) {
IfcSchema::IfcGeometricRepresentationContext* context = *it;
if (context->declaration().is(IfcSchema::IfcGeometricRepresentationSubContext::Class())) {
// Continue, as the list of subcontexts will be considered
// by the parent's context inverse attributes.
continue;
}
try {
if (context->hasContextType()) {
std::string context_type = context->ContextType();
boost::to_lower(context_type);
if (allowed_context_types.find(context_type) == allowed_context_types.end()) {
Logger::Warning(std::string("ContextType '") + context->ContextType() + "' not allowed:", context);
}
if (context_types.find(context_type) != context_types.end()) {
filtered_contexts->push(context);
}
}
} catch (const std::exception& e) {
Logger::Error(e);
}
}
// In case no contexts are identified based on their ContextType, all contexts are
// considered. Note that sub contexts are excluded as they are considered later on.
if (filtered_contexts->size() == 0) {
for (it = contexts->begin(); it != contexts->end(); ++it) {
IfcSchema::IfcGeometricRepresentationContext* context = *it;
if (!context->declaration().is(IfcSchema::IfcGeometricRepresentationSubContext::Class())) {
filtered_contexts->push(context);
}
}
}
for (it = filtered_contexts->begin(); it != filtered_contexts->end(); ++it) {
IfcSchema::IfcGeometricRepresentationContext* context = *it;
representations->push(context->RepresentationsInContext());
try {
if (context->hasPrecision() && context->Precision() < lowest_precision_encountered) {
lowest_precision_encountered = context->Precision();
any_precision_encountered = true;
}
} catch (const std::exception& e) {
Logger::Error(e);
}
IfcSchema::IfcGeometricRepresentationSubContext::list::ptr sub_contexts = context->HasSubContexts();
for (jt = sub_contexts->begin(); jt != sub_contexts->end(); ++jt) {
representations->push((*jt)->RepresentationsInContext());
}
// There is no need for full recursion as the following is governed by the schema:
// WR31: The parent context shall not be another geometric representation sub context.
}
if (any_precision_encountered) {
// Some arbitrary factor that has proven to work better for the models in the set of test files.
lowest_precision_encountered *= 10.;
lowest_precision_encountered *= unit_magnitude;
if (lowest_precision_encountered < 1.e-7) {
Logger::Message(Logger::LOG_WARNING, "Precision lower than 0.0000001 meter not enforced");
kernel.setValue(IfcGeom::Kernel::GV_PRECISION, 1.e-7);
} else {
kernel.setValue(IfcGeom::Kernel::GV_PRECISION, lowest_precision_encountered);
}
} else {
kernel.setValue(IfcGeom::Kernel::GV_PRECISION, 1.e-5);
}
if (representations->size() == 0) {
Logger::Warning("No representations encountered in relevant contexts, using all");
representations = ifc_file->instances_by_type<IfcSchema::IfcRepresentation>();
}
if (representations->size() == 0) {
Logger::Warning("No representations encountered, aborting");
return false;
}
representation_iterator = representations->begin();
ifcproducts.reset();
done = 0;
total = representations->size();
if (num_threads_ != 1) {
collect();
process_concurrently();
} else {
if (!create()) {
return false;
}
}
return true;
}
void collect() {
int i = 0;
IfcSchema::IfcProduct::list* previous = nullptr;
while (auto rp = get_next_task()) {
// Note that get_next_task() mutates the state of the iterator
// we use that capture all products that can be processed as
// part of this representation and then keep iterating until
// the underlying list of products changes.
if (ifcproducts.get() != previous) {
previous = ifcproducts.get();
geometry_conversion_task<P, PP> t;
t.index = i++;
t.representation = *representation_iterator;
t.products = ifcproducts;
tasks_.emplace_back(t);
}
_nextShape();
}
}
void process_concurrently() {
size_t conc_threads = num_threads_;
if (conc_threads > tasks_.size()) {
conc_threads = tasks_.size();
}
std::vector<MAKE_TYPE_NAME(Kernel)*> kernel_pool;
kernel_pool.reserve(conc_threads);
for (unsigned i = 0; i < conc_threads; ++i) {
kernel_pool.push_back(new MAKE_TYPE_NAME(Kernel)(kernel));
}
std::vector<std::future<void>> threadpool;
int old_progress = -1;
int processed = 0;
Logger::ProgressBar(0);
for (auto& rep : tasks_) {
MAKE_TYPE_NAME(Kernel)* K = nullptr;
if (threadpool.size() < kernel_pool.size()) {
K = kernel_pool[threadpool.size()];
}
while (threadpool.size() == conc_threads) {
for (int i = 0; i < (int)threadpool.size(); i++) {
std::future<void> &fu = threadpool[i];
std::future_status status;
status = fu.wait_for(std::chrono::seconds(0));
if (status == std::future_status::ready) {
fu.get();
processed += 1;
progress_ = processed * 50 / tasks_.size();
if (progress_ != old_progress) {
Logger::ProgressBar(progress_);
old_progress = progress_;
}
std::swap(threadpool[i], threadpool.back());
threadpool.pop_back();
std::swap(kernel_pool[i], kernel_pool.back());
K = kernel_pool.back();
break;
} // if
} // for
} // while
std::future<void> fu = std::async(std::launch::async, create_element<P, PP>, K, std::ref(settings), &rep);
threadpool.emplace_back(std::move(fu));
}
for (std::future<void> &fu : threadpool) {
fu.get();
processed += 1;
progress_ = processed * 50 / tasks_.size();
if (progress_ != old_progress) {
Logger::ProgressBar(progress_);
old_progress = progress_;
}
}
for (auto& rep : tasks_) {
all_processed_elements_.insert(all_processed_elements_.end(), rep.elements.begin(), rep.elements.end());
all_processed_native_elements_.insert(all_processed_native_elements_.end(), rep.breps.begin(), rep.breps.end());
}
task_result_iterator_ = all_processed_elements_.begin();
native_task_result_iterator_ = all_processed_native_elements_.begin();
Logger::Status("\rDone creating geometry (" + boost::lexical_cast<std::string>(all_processed_elements_.size()) +
" objects) ");
}
/// Computes model's bounding box (bounds_min and bounds_max).
/// @note Can take several minutes for large files.
void compute_bounds()
{
for (int i = 1; i < 4; ++i) {
bounds_min_.SetCoord(i, std::numeric_limits<double>::infinity());
bounds_max_.SetCoord(i, -std::numeric_limits<double>::infinity());
}
IfcSchema::IfcProduct::list::ptr products = ifc_file->instances_by_type<IfcSchema::IfcProduct>();
for (IfcSchema::IfcProduct::list::it iter = products->begin(); iter != products->end(); ++iter) {
IfcSchema::IfcProduct* product = *iter;
if (product->hasObjectPlacement()) {
// Use a fresh trsf every time in order to prevent the result to be concatenated
gp_Trsf trsf;
bool success = false;
try {
success = kernel.convert(product->ObjectPlacement(), trsf);
} catch (const std::exception& e) {
Logger::Error(e);
} catch (...) {
Logger::Error("Failed to construct placement");
}
if (!success) {
continue;
}
const gp_XYZ& pos = trsf.TranslationPart();
bounds_min_.SetX(std::min(bounds_min_.X(), pos.X()));
bounds_min_.SetY(std::min(bounds_min_.Y(), pos.Y()));
bounds_min_.SetZ(std::min(bounds_min_.Z(), pos.Z()));
bounds_max_.SetX(std::max(bounds_max_.X(), pos.X()));
bounds_max_.SetY(std::max(bounds_max_.Y(), pos.Y()));
bounds_max_.SetZ(std::max(bounds_max_.Z(), pos.Z()));
}
}
}
int progress() const {
if (num_threads_ == 1) {
return 100 * done / total;
} else {
return progress_;
}
}
const std::string& getUnitName() const { return unit_name; }
/// @note Double always as per IFC specification.
double getUnitMagnitude() const { return unit_magnitude; }
std::string getLog() const { return Logger::GetLog(); }
IfcParse::IfcFile* file() const { return ifc_file; }
const std::vector<IfcGeom::filter_t>& filters() const { return filters_; }
std::vector<IfcGeom::filter_t>& filters() { return filters_; }
const gp_XYZ& bounds_min() const { return bounds_min_; }
const gp_XYZ& bounds_max() const { return bounds_max_; }
private:
// Move to the next IfcRepresentation
void _nextShape() {
// In order to conserve memory and reduce cache insertion times, the cache is
// cleared after an arbitrary number of processed representations. This has been
// benchmarked extensively: https://github.com/IfcOpenShell/IfcOpenShell/pull/47
static const int clear_interval = 64;
if (done % clear_interval == clear_interval - 1) {
kernel.purge_cache();
}
ifcproducts.reset();
++ representation_iterator;
++ done;
}
bool geometry_reuse_ok_for_current_representation_;
bool reuse_ok_(const IfcSchema::IfcProduct::list::ptr& products) {
// With world coords enabled, object transformations are directly applied to
// the BRep. There is no way to re-use the geometry for multiple products.
if (settings.get(IteratorSettings::USE_WORLD_COORDS)) {
return false;
}
std::set<const IfcSchema::IfcMaterial*> associated_single_materials;
for (IfcSchema::IfcProduct::list::it it = products->begin(); it != products->end(); ++it) {
IfcSchema::IfcProduct* product = *it;
if (!settings.get(IteratorSettings::DISABLE_OPENING_SUBTRACTIONS) && kernel.find_openings(product)->size()) {
return false;
}
if (settings.get(IteratorSettings::APPLY_LAYERSETS)) {
IfcSchema::IfcRelAssociates::list::ptr associations = product->HasAssociations();
for (IfcSchema::IfcRelAssociates::list::it jt = associations->begin(); jt != associations->end(); ++jt) {
IfcSchema::IfcRelAssociatesMaterial* assoc = (*jt)->as<IfcSchema::IfcRelAssociatesMaterial>();
if (assoc) {
if (assoc->RelatingMaterial()->declaration().is(IfcSchema::IfcMaterialLayerSetUsage::Class())) {
// TODO: Check whether single layer?
return false;
}
}
}
}
// Note that this can be a nullptr (!), but the fact that set size should be one still holds
associated_single_materials.insert(kernel.get_single_material_association(product));
if (associated_single_materials.size() > 1) return false;
}
return associated_single_materials.size() == 1;
}
boost::optional<std::pair<IfcSchema::IfcRepresentation*, IfcSchema::IfcProduct*>> get_next_task() {
for (;;) {
IfcSchema::IfcRepresentation* representation;
if (representation_iterator == representations->end()) {
representations.reset();
return boost::none; // reached the end of our list of representations
}
representation = *representation_iterator;
if (!ifcproducts) {
// Init. the list of filtered IfcProducts for this representation
ifcproducts = IfcSchema::IfcProduct::list::ptr(new IfcSchema::IfcProduct::list);
IfcSchema::IfcProduct::list::ptr unfiltered_products = kernel.products_represented_by(representation);
// Include only the desired products for processing.
for (IfcSchema::IfcProduct::list::it jt = unfiltered_products->begin(); jt != unfiltered_products->end(); ++jt) {
IfcSchema::IfcProduct* prod = *jt;
if (boost::all(filters_, filter_match(prod))) {
ifcproducts->push(prod);
}
}
if (ifcproducts->size() == 0) {
_nextShape();
continue;
}
geometry_reuse_ok_for_current_representation_ = reuse_ok_(ifcproducts);
IfcSchema::IfcRepresentationMap::list::ptr maps = representation->RepresentationMap();
if (!geometry_reuse_ok_for_current_representation_ && maps->size() == 1) {
// unfiltered_products contains products represented by this representation by means of mapped items.
// For example because of openings applied to products, reuse might not be acceptable and then the
// products will be processed by means of their immediate representation and not the mapped representation.
// IfcRepresentationMaps are also used for IfcTypeProducts, so an additional check is performed whether the map
// is indeed used by IfcMappedItems.
IfcSchema::IfcRepresentationMap* map = *maps->begin();
if (map->MapUsage()->size() > 0) {
_nextShape();
continue;
}
}
// Check if this represenation has (or will be) processed as part its mapped representation
bool representation_processed_as_mapped_item = false;
IfcSchema::IfcRepresentation* representation_mapped_to = kernel.representation_mapped_to(representation);
if (representation_mapped_to) {
representation_processed_as_mapped_item = geometry_reuse_ok_for_current_representation_ && (
ok_mapped_representations->contains(representation_mapped_to) || reuse_ok_(kernel.products_represented_by(representation_mapped_to)));
}
if (representation_processed_as_mapped_item) {
ok_mapped_representations->push(representation_mapped_to);
_nextShape();
continue;
}
ifcproduct_iterator = ifcproducts->begin();
}
// Have we reached the end of our list of IfcProducts?
if (ifcproduct_iterator == ifcproducts->end()) {
_nextShape();
continue;
}
IfcSchema::IfcProduct* product = *ifcproduct_iterator;
return std::make_pair(representation, product);
}
}
BRepElement<P, PP>* create_shape_model_for_next_entity() {
for (;;) {
auto rp = get_next_task();
if (!rp) {
return nullptr;
}
auto representation = rp->first;
auto product = rp->second;
Logger::SetProduct(product);
BRepElement<P, PP>* element;
if (ifcproduct_iterator == ifcproducts->begin() || !geometry_reuse_ok_for_current_representation_) {
element = kernel.create_brep_for_representation_and_product<P, PP>(settings, representation, product);
} else {
element = kernel.create_brep_for_processed_representation(settings, representation, product, current_shape_model);
}
Logger::SetProduct(boost::none);
if (!element) {
_nextShape();
continue;
}
return element;
}
}
void free_shapes() {
// Free all possible representations of the current geometrical entity
delete current_triangulation;
current_triangulation = 0;
delete current_serialization;
current_serialization = 0;
delete current_shape_model;
current_shape_model = 0;
}
public:
/// Returns what would be the product for the next shape representation
/// @todo Double-check and test the impl.
//IfcSchema::IfcProduct* peek_next() const
//{
// if (ifcproducts && ifcproduct_iterator + 1 != ifcproducts->end()){
// return *(ifcproduct_iterator + 1);
// } else {
// return 0;
// }
//}
/// @todo Would this be as simple as the following code?
//void skip_next() { if (ifcproducts) { ++ifcproduct_iterator; } }
/// Moves to the next shape representation, create its geometry, and returns the associated product.
/// Use get() to retrieve the created geometry.
IfcUtil::IfcBaseClass* next() {
if (num_threads_ != 1) {
task_result_iterator_++;
native_task_result_iterator_++;
if (task_result_iterator_ == all_processed_elements_.end()) {
return nullptr;
} else {
return (*task_result_iterator_)->product();
}
} else {
// Increment the iterator over the list of products using the current
// shape representation
if (ifcproducts) {
++ifcproduct_iterator;
}
return create();
}
}
/// Gets the representation of the current geometrical entity.
Element<P, PP>* get()
{
// TODO: Test settings and throw
Element<P, PP>* ret = 0;
if (num_threads_ != 1) {
ret = *task_result_iterator_;
} else {
if (current_triangulation) {
ret = current_triangulation;
} else if (current_serialization) {
ret = current_serialization;
} else if (current_shape_model) {
ret = current_shape_model;
}
}
// If we want to organize the element considering their hierarchy
if (settings.get(IteratorSettings::SEARCH_FLOOR))
{
// We are going to build a vector with the element parents.
// First, create the parent vector
std::vector<const IfcGeom::Element<P, PP>*> parents;
// if the element has a parent
if (ret->parent_id() != -1)
{
const IfcGeom::Element<P, PP>* parent_object = NULL;
bool hasParent = true;
// get the parent
try {
parent_object = get_object(ret->parent_id());
} catch (const std::exception& e) {
Logger::Error(e);
hasParent = false;
}
// Add the previously found parent to the vector
if (hasParent) parents.insert(parents.begin(), parent_object);
// We need to find all the parents
while (parent_object != NULL && hasParent && parent_object->parent_id() != -1)
{
// Find the next parent
try {
parent_object = get_object(parent_object->parent_id());
} catch (const std::exception& e) {
Logger::Error(e);
hasParent = false;
}
// Add the previously found parent to the vector
if (hasParent) parents.insert(parents.begin(), parent_object);
hasParent = hasParent && parent_object->parent_id() != -1;
}
// when done push the parent list in the Element object
ret->SetParents(parents);
}
}
return ret;
}
/// Gets the native (Open Cascade) representation of the current geometrical entity.
BRepElement<P, PP>* get_native()
{
// TODO: Test settings and throw
if (num_threads_ != 1) {
return *native_task_result_iterator_;
} else {
return current_shape_model;
}
}
const Element<P, PP>* get_object(int id) {
gp_Trsf trsf;
int parent_id = -1;
std::string instance_type, product_name, product_guid;
IfcSchema::IfcProduct* ifc_product = 0;
try {
IfcUtil::IfcBaseClass* ifc_entity = ifc_file->instance_by_id(id);
instance_type = ifc_entity->declaration().name();
if (ifc_entity->declaration().is(IfcSchema::IfcRoot::Class())) {
IfcSchema::IfcRoot* ifc_root = ifc_entity->as<IfcSchema::IfcRoot>();
product_guid = ifc_root->GlobalId();
product_name = ifc_root->hasName() ? ifc_root->Name() : "";
}
if (ifc_entity->declaration().is(IfcSchema::IfcProduct::Class())) {
ifc_product = ifc_entity->as<IfcSchema::IfcProduct>();
parent_id = -1;
try {
IfcSchema::IfcObjectDefinition* parent_object = kernel.get_decomposing_entity(ifc_product)->template as<IfcSchema::IfcObjectDefinition>();
if (parent_object) {
parent_id = parent_object->data().id();
}
} catch (const std::exception& e) {
Logger::Error(e);
} catch (...) {
Logger::Error("Failed to find decomposing entity");
}
try {
kernel.convert(ifc_product->ObjectPlacement(), trsf);
} catch (const std::exception& e) {
Logger::Error(e);
} catch (...) {
Logger::Error("Failed to construct placement");
}
}
} catch (const std::exception& e) {
Logger::Error(e);
} catch (const Standard_Failure& e) {
if (e.GetMessageString() && strlen(e.GetMessageString())) {
Logger::Error(e.GetMessageString());
} else {
Logger::Error("Unknown error returning product");
}
} catch (...) {
Logger::Error("Unknown error returning product");
}
ElementSettings element_settings(settings, unit_magnitude, instance_type);
Element<P, PP>* ifc_object = new Element<P, PP>(element_settings, id, parent_id, product_name, instance_type, product_guid, "", trsf, ifc_product);
return ifc_object;
}
IfcUtil::IfcBaseClass* create() {
IfcGeom::BRepElement<P, PP>* next_shape_model = 0;
IfcGeom::SerializedElement<P, PP>* next_serialization = 0;
IfcGeom::TriangulationElement<P, PP>* next_triangulation = 0;
try {
next_shape_model = create_shape_model_for_next_entity();
} catch (const std::exception& e) {
Logger::Error(e);
} catch (const Standard_Failure& e) {
if (e.GetMessageString() && strlen(e.GetMessageString())) {
Logger::Error(e.GetMessageString());
} else {
Logger::Error("Unknown error creating geometry");
}
} catch (...) {
Logger::Error("Unknown error creating geometry");
}
if (next_shape_model) {
if (settings.get(IteratorSettings::USE_BREP_DATA)) {
try {
next_serialization = new SerializedElement<P, PP>(*next_shape_model);
} catch (...) {
Logger::Message(Logger::LOG_ERROR, "Getting a serialized element from model failed.");
}
} else if (!settings.get(IteratorSettings::DISABLE_TRIANGULATION)) {
try {
if (ifcproduct_iterator == ifcproducts->begin() || !geometry_reuse_ok_for_current_representation_) {
next_triangulation = new TriangulationElement<P, PP>(*next_shape_model);
} else {
next_triangulation = new TriangulationElement<P, PP>(*next_shape_model, current_triangulation->geometry_pointer());
}
} catch (...) {
Logger::Message(Logger::LOG_ERROR, "Getting a triangulation element from model failed.");
}
}
}
free_shapes();
current_shape_model = next_shape_model;
current_serialization = next_serialization;
current_triangulation = next_triangulation;
return next_shape_model ? next_shape_model->product() : 0;
}
private:
void _initialize() {
current_triangulation = 0;
current_shape_model = 0;
current_serialization = 0;
unit_name = "METER";
unit_magnitude = 1.f;
kernel.setValue(IfcGeom::Kernel::GV_MAX_FACES_TO_ORIENT, settings.get(IteratorSettings::SEW_SHELLS) ? std::numeric_limits<double>::infinity() : -1);
kernel.setValue(IfcGeom::Kernel::GV_DIMENSIONALITY, (settings.get(IteratorSettings::INCLUDE_CURVES)
? (settings.get(IteratorSettings::EXCLUDE_SOLIDS_AND_SURFACES) ? -1. : 0.) : +1.));
if (settings.get(IteratorSettings::BUILDING_LOCAL_PLACEMENT)) {
if (settings.get(IteratorSettings::SITE_LOCAL_PLACEMENT)) {
Logger::Message(Logger::LOG_WARNING, "building-local-placement takes precedence over site-local-placement");
}
kernel.set_conversion_placement_rel_to(&IfcSchema::IfcBuilding::Class());
} else if (settings.get(IteratorSettings::SITE_LOCAL_PLACEMENT)) {
kernel.set_conversion_placement_rel_to(&IfcSchema::IfcSite::Class());
}
}
bool owns_ifc_file;
public:
MAKE_TYPE_NAME(IteratorImplementation_)(const IteratorSettings& settings, IfcParse::IfcFile* file, const std::vector<IfcGeom::filter_t>& filters, int num_threads)
: settings(settings)
, ifc_file(file)
, filters_(filters)
, owns_ifc_file(false)
, num_threads_(num_threads)
{
_initialize();
}
~MAKE_TYPE_NAME(IteratorImplementation_)() {
if (owns_ifc_file) {
delete ifc_file;
}
if (settings.get(IfcGeom::IteratorSettings::DISABLE_TRIANGULATION)) {
for (auto& p : all_processed_native_elements_) {
delete p;
}
}
for (auto& p : all_processed_elements_) {
delete p;
}
free_shapes();
}
};
}
#endif
-140
View File
@@ -1,140 +0,0 @@
/********************************************************************************
* *
* 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/>. *
* *
********************************************************************************/
#include <map>
#include "IfcGeom.h"
namespace {
bool process_colour(IfcSchema::IfcColourRgb* colour, double* rgb) {
if (colour != 0) {
rgb[0] = colour->Red();
rgb[1] = colour->Green();
rgb[2] = colour->Blue();
}
return colour != 0;
}
bool process_colour(IfcSchema::IfcNormalisedRatioMeasure* factor, double* rgb) {
if (factor != 0) {
const double f = *factor;
rgb[0] = rgb[1] = rgb[2] = f;
}
return factor != 0;
}
bool process_colour(IfcSchema::IfcColourOrFactor* colour_or_factor, double* rgb) {
if (colour_or_factor == 0) {
return false;
} else if (colour_or_factor->declaration().is(IfcSchema::IfcColourRgb::Class())) {
return process_colour(static_cast<IfcSchema::IfcColourRgb*>(colour_or_factor), rgb);
} else if (colour_or_factor->declaration().is(IfcSchema::IfcNormalisedRatioMeasure::Class())) {
return process_colour(static_cast<IfcSchema::IfcNormalisedRatioMeasure*>(colour_or_factor), rgb);
} else {
return false;
}
}
}
#define Kernel MAKE_TYPE_NAME(Kernel)
const IfcGeom::SurfaceStyle* IfcGeom::Kernel::internalize_surface_style(const std::pair<IfcUtil::IfcBaseClass*, IfcUtil::IfcBaseClass*>& shading_styles) {
if (shading_styles.second == 0) {
return 0;
}
int surface_style_id = shading_styles.first->data().id();
std::map<int,SurfaceStyle>::const_iterator it = style_cache.find(surface_style_id);
if (it != style_cache.end()) {
return &(it->second);
}
SurfaceStyle surface_style;
IfcSchema::IfcSurfaceStyle* style = shading_styles.first->as<IfcSchema::IfcSurfaceStyle>();
IfcSchema::IfcSurfaceStyleShading* shading = shading_styles.second->as<IfcSchema::IfcSurfaceStyleShading>();
if (style->hasName()) {
surface_style = SurfaceStyle(surface_style_id, style->Name());
} else {
surface_style = SurfaceStyle(surface_style_id);
}
double rgb[3];
if (process_colour(shading->SurfaceColour(), rgb)) {
surface_style.Diffuse().reset(SurfaceStyle::ColorComponent(rgb[0], rgb[1], rgb[2]));
}
if (shading_styles.second->declaration().is(IfcSchema::IfcSurfaceStyleRendering::Class())) {
IfcSchema::IfcSurfaceStyleRendering* rendering_style = static_cast<IfcSchema::IfcSurfaceStyleRendering*>(shading_styles.second);
if (rendering_style->hasDiffuseColour() && process_colour(rendering_style->DiffuseColour(), rgb)) {
SurfaceStyle::ColorComponent diffuse = surface_style.Diffuse().get_value_or(SurfaceStyle::ColorComponent(1,1,1));
surface_style.Diffuse().reset(SurfaceStyle::ColorComponent(diffuse.R() * rgb[0], diffuse.G() * rgb[1], diffuse.B() * rgb[2]));
}
if (rendering_style->hasDiffuseTransmissionColour()) {
// Not supported
}
if (rendering_style->hasReflectionColour()) {
// Not supported
}
if (rendering_style->hasSpecularColour() && process_colour(rendering_style->SpecularColour(), rgb)) {
surface_style.Specular().reset(SurfaceStyle::ColorComponent(rgb[0], rgb[1], rgb[2]));
}
if (rendering_style->hasSpecularHighlight()) {
IfcSchema::IfcSpecularHighlightSelect* highlight = rendering_style->SpecularHighlight();
if (highlight->declaration().is(IfcSchema::IfcSpecularRoughness::Class())) {
double roughness = *((IfcSchema::IfcSpecularRoughness*)highlight);
if (roughness >= 1e-9) {
surface_style.Specularity().reset(1.0 / roughness);
}
} else if (highlight->declaration().is(IfcSchema::IfcSpecularExponent::Class())) {
surface_style.Specularity().reset(*((IfcSchema::IfcSpecularExponent*)highlight));
}
}
if (rendering_style->hasTransmissionColour()) {
// Not supported
}
if (rendering_style->hasTransparency()) {
const double d = rendering_style->Transparency();
surface_style.Transparency().reset(d);
}
}
return &(style_cache[surface_style_id] = surface_style);
}
const IfcGeom::SurfaceStyle* IfcGeom::Kernel::get_style(const IfcSchema::IfcRepresentationItem* item) {
return internalize_surface_style(get_surface_style<IfcSchema::IfcSurfaceStyleShading>(item));
}
const IfcGeom::SurfaceStyle* IfcGeom::Kernel::get_style(const IfcSchema::IfcMaterial* material) {
IfcSchema::IfcMaterialDefinitionRepresentation::list::ptr defs = material->HasRepresentation();
for (IfcSchema::IfcMaterialDefinitionRepresentation::list::it jt = defs->begin(); jt != defs->end(); ++jt) {
IfcSchema::IfcRepresentation::list::ptr reps = (*jt)->Representations();
IfcSchema::IfcStyledItem::list::ptr styles(new IfcSchema::IfcStyledItem::list);
for (IfcSchema::IfcRepresentation::list::it it = reps->begin(); it != reps->end(); ++it) {
styles->push((**it).Items()->as<IfcSchema::IfcStyledItem>());
}
for (IfcSchema::IfcStyledItem::list::it it = styles->begin(); it != styles->end(); ++it) {
const std::pair<IfcSchema::IfcSurfaceStyle*, IfcSchema::IfcSurfaceStyleShading*> ss = get_surface_style<IfcSchema::IfcSurfaceStyleShading>(*it);
if (ss.second) {
return internalize_surface_style(ss);
}
}
}
IfcGeom::SurfaceStyle material_style = IfcGeom::SurfaceStyle(material->data().id(), material->Name());
return &(style_cache[material->data().id()] = material_style);
}
-415
View File
@@ -1,415 +0,0 @@
/********************************************************************************
* *
* 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/>. *
* *
********************************************************************************/
#ifndef IFCGEOMREPRESENTATION_H
#define IFCGEOMREPRESENTATION_H
#include <BRepMesh_IncrementalMesh.hxx>
#include <BRepGProp_Face.hxx>
#include <Poly_Triangulation.hxx>
#include <TColgp_Array1OfPnt.hxx>
#include <TColgp_Array1OfPnt2d.hxx>
#include <TopoDS.hxx>
#include <BRepTools.hxx>
#include <TopExp_Explorer.hxx>
#include <BRepAdaptor_Curve.hxx>
#include <GCPnts_QuasiUniformDeflection.hxx>
#include <Geom_SphericalSurface.hxx>
#include "../ifcgeom/IfcGeomIteratorSettings.h"
#include "../ifcgeom_schema_agnostic/IfcGeomMaterial.h"
#include "../ifcgeom/IfcRepresentationShapeItem.h"
#include <TopoDS_Compound.hxx>
namespace IfcGeom {
namespace Representation {
class IFC_GEOM_API Representation {
Representation(const Representation&); //N/A
Representation& operator =(const Representation&); //N/A
protected:
const ElementSettings settings_;
public:
explicit Representation(const ElementSettings& settings)
: settings_(settings)
{}
const ElementSettings& settings() const { return settings_; }
virtual ~Representation() {}
};
class IFC_GEOM_API BRep : public Representation {
private:
std::string id_;
const IfcGeom::IfcRepresentationShapeItems shapes_;
BRep(const BRep& other);
BRep& operator=(const BRep& other);
public:
BRep(const ElementSettings& settings, const std::string& id, const IfcGeom::IfcRepresentationShapeItems& shapes)
: Representation(settings)
, id_(id)
, shapes_(shapes)
{}
virtual ~BRep() {}
IfcGeom::IfcRepresentationShapeItems::const_iterator begin() const { return shapes_.begin(); }
IfcGeom::IfcRepresentationShapeItems::const_iterator end() const { return shapes_.end(); }
const IfcGeom::IfcRepresentationShapeItems& shapes() const { return shapes_; }
const std::string& id() const { return id_; }
TopoDS_Compound as_compound(bool force_meters = false) const;
bool calculate_volume(double&) const;
bool calculate_surface_area(double&) const;
bool calculate_projected_surface_area(const gp_Ax3& ax, double& along_x, double& along_y, double& along_z) const;
};
class IFC_GEOM_API Serialization : public Representation {
private:
std::string id_;
std::string brep_data_;
std::vector<double> surface_styles_;
public:
const std::string& brep_data() const { return brep_data_; }
const std::vector<double>& surface_styles() const { return surface_styles_; }
Serialization(const BRep& brep);
virtual ~Serialization() {}
const std::string& id() const { return id_; }
private:
Serialization();
Serialization(const Serialization&);
Serialization& operator=(const Serialization&);
};
template <typename P>
class Triangulation : public Representation {
private:
// A nested pair of floats and a material index to be able to store an XYZ coordinate in a map.
// TODO: Make this a std::tuple when compilers add support for that.
typedef typename std::pair<P, std::pair<P, P> > Coordinate;
typedef typename std::pair<int, Coordinate> VertexKey;
typedef std::map<VertexKey, int> VertexKeyMap;
typedef std::pair<int, int> Edge;
std::string id_;
std::vector<P> _verts;
std::vector<int> _faces;
std::vector<int> _edges;
std::vector<P> _normals;
std::vector<P> uvs_;
std::vector<int> _material_ids;
std::vector<Material> _materials;
VertexKeyMap welds;
public:
const std::string& id() const { return id_; }
const std::vector<P>& verts() const { return _verts; }
const std::vector<int>& faces() const { return _faces; }
const std::vector<int>& edges() const { return _edges; }
const std::vector<P>& normals() const { return _normals; }
const std::vector<P>& uvs() const { return uvs_; }
const std::vector<int>& material_ids() const { return _material_ids; }
const std::vector<Material>& materials() const { return _materials; }
Triangulation(const BRep& shape_model)
: Representation(shape_model.settings())
, id_(shape_model.id())
{
for ( IfcGeom::IfcRepresentationShapeItems::const_iterator iit = shape_model.begin(); iit != shape_model.end(); ++ iit ) {
int surface_style_id = -1;
if (iit->hasStyle()) {
Material adapter(&iit->Style());
std::vector<Material>::const_iterator jt = std::find(_materials.begin(), _materials.end(), adapter);
if (jt == _materials.end()) {
surface_style_id = (int)_materials.size();
_materials.push_back(adapter);
} else {
surface_style_id = (int)(jt - _materials.begin());
}
}
if (settings().get(IteratorSettings::APPLY_DEFAULT_MATERIALS) && surface_style_id == -1) {
Material material(IfcGeom::get_default_style(settings().element_type()));
std::vector<Material>::const_iterator mit = std::find(_materials.begin(), _materials.end(), material);
if (mit == _materials.end()) {
surface_style_id = (int)_materials.size();
_materials.push_back(material);
} else {
surface_style_id = (int)(mit - _materials.begin());
}
}
const TopoDS_Shape& s = iit->Shape();
const gp_GTrsf& trsf = iit->Placement();
// Triangulate the shape
try {
BRepMesh_IncrementalMesh(s, settings().deflection_tolerance());
} catch(...) {
Logger::Message(Logger::LOG_ERROR, "Failed to triangulate shape");
continue;
}
// Iterates over the faces of the shape
int num_faces = 0;
TopExp_Explorer exp;
for ( exp.Init(s,TopAbs_FACE); exp.More(); exp.Next(), ++num_faces ) {
TopoDS_Face face = TopoDS::Face(exp.Current());
TopLoc_Location loc;
Handle_Poly_Triangulation tri = BRep_Tool::Triangulation(face,loc);
if ( ! tri.IsNull() ) {
// A 3x3 matrix to rotate the vertex normals
const gp_Mat rotation_matrix = trsf.VectorialPart();
// Keep track of the number of times an edge is used
// Manifold edges (i.e. edges used twice) are deemed invisible
std::map<std::pair<int,int>,int> edgecount;
std::vector<std::pair<int,int> > edges_temp;
const TColgp_Array1OfPnt& nodes = tri->Nodes();
const TColgp_Array1OfPnt2d& uvs = tri->UVNodes();
std::vector<gp_XYZ> coords;
BRepGProp_Face prop(face);
std::map<int,int> dict;
// Vertex normals are only calculated if vertices are not welded and calculation is not disable explicitly.
const bool calculate_normals = !settings().get(IteratorSettings::WELD_VERTICES) &&
!settings().get(IteratorSettings::NO_NORMALS);
for( int i = 1; i <= nodes.Length(); ++ i ) {
coords.push_back(nodes(i).Transformed(loc).XYZ());
trsf.Transforms(*coords.rbegin());
dict[i] = addVertex(surface_style_id, *coords.rbegin());
if ( calculate_normals ) {
const gp_Pnt2d& uv = uvs(i);
gp_Pnt p;
gp_Vec normal_direction;
prop.Normal(uv.X(),uv.Y(),p,normal_direction);
gp_Vec normal(0., 0., 0.);
if (normal_direction.Magnitude() > 1.e-9) {
normal = gp_Dir(normal_direction.XYZ() * rotation_matrix);
} else {
Handle_Geom_Surface surf = BRep_Tool::Surface(face);
// Special case the normal at the poles of a spherical surface
if (surf->DynamicType() == STANDARD_TYPE(Geom_SphericalSurface)) {
if (fabs(fabs(uv.Y()) - M_PI / 2.) < 1.e-9) {
const bool is_top = uv.Y() > 0;
const bool is_forward = face.Orientation() == TopAbs_FORWARD;
const double z = (is_top == is_forward) ? 1. : -1.;
normal = gp_Dir(gp_XYZ(0, 0, z) * rotation_matrix);
}
}
// TODO: Do the same for conical surfaces, but they are rare in IFC.
}
_normals.push_back(static_cast<P>(normal.X()));
_normals.push_back(static_cast<P>(normal.Y()));
_normals.push_back(static_cast<P>(normal.Z()));
}
}
const Poly_Array1OfTriangle& triangles = tri->Triangles();
for( int i = 1; i <= triangles.Length(); ++ i ) {
int n1,n2,n3;
if ( face.Orientation() == TopAbs_REVERSED )
triangles(i).Get(n3,n2,n1);
else triangles(i).Get(n1,n2,n3);
/* An alternative would be to calculate normals based
* on the coordinates of the mesh vertices */
/*
const gp_XYZ pt1 = coords[n1-1];
const gp_XYZ pt2 = coords[n2-1];
const gp_XYZ pt3 = coords[n3-1];
const gp_XYZ v1 = pt2-pt1;
const gp_XYZ v2 = pt3-pt2;
gp_Dir normal = gp_Dir(v1^v2);
_normals.push_back((float)normal.X());
_normals.push_back((float)normal.Y());
_normals.push_back((float)normal.Z());
*/
_faces.push_back(dict[n1]);
_faces.push_back(dict[n2]);
_faces.push_back(dict[n3]);
_material_ids.push_back(surface_style_id);
addEdge(dict[n1], dict[n2], edgecount, edges_temp);
addEdge(dict[n2], dict[n3], edgecount, edges_temp);
addEdge(dict[n3], dict[n1], edgecount, edges_temp);
}
for ( std::vector<std::pair<int,int> >::const_iterator jt = edges_temp.begin(); jt != edges_temp.end(); ++jt ) {
if (edgecount[*jt] == 1) {
// non manifold edge, face boundary
_edges.push_back(jt->first);
_edges.push_back(jt->second);
}
}
}
}
if (!_normals.empty() && settings().get(IfcGeom::IteratorSettings::GENERATE_UVS)) {
uvs_ = box_project_uvs(_verts, _normals);
}
if (num_faces == 0) {
// Edges are only emitted if there are no faces. A mixed representation of faces
// and loose edges is discouraged by the standard. An alternative would be to use
// TopExp_Explorer texp(s, TopAbs_EDGE, TopAbs_FACE) to find edges that do not
// belong to any face.
for (TopExp_Explorer texp(s, TopAbs_EDGE); texp.More(); texp.Next()) {
BRepAdaptor_Curve crv(TopoDS::Edge(texp.Current()));
GCPnts_QuasiUniformDeflection tessellater(crv, settings().deflection_tolerance());
int n = tessellater.NbPoints();
int start = (int)_verts.size() / 3;
for (int i = 1; i <= n; ++i) {
gp_XYZ p = tessellater.Value(i).XYZ();
/*
// In case you want direction arrows on your edges
double u = tessellater.Parameter(i);
gp_XYZ p2, p3;
gp_Pnt tmp;
gp_Vec tmp2;
crv.D1(u, tmp, tmp2);
gp_Dir d1, d2, d3, d4;
d1 = tmp2;
if (texp.Current().Orientation() == TopAbs_REVERSED) {
d1 = -d1;
}
if (fabs(d1.Z()) < 0.5) {
d2 = d1.Crossed(gp::DZ());
} else {
d2 = d1.Crossed(gp::DY());
}
d3 = d1.XYZ() + d2.XYZ();
d4 = d1.XYZ() - d2.XYZ();
p2 = p - d3.XYZ() / 10.;
p3 = p - d4.XYZ() / 10.;
trsf.Transforms(p2);
trsf.Transforms(p3);
_material_ids.push_back(surface_style_id);
_material_ids.push_back(surface_style_id);
_verts.push_back(static_cast<P>(p2.X()));
_verts.push_back(static_cast<P>(p2.Y()));
_verts.push_back(static_cast<P>(p2.Z()));
_verts.push_back(static_cast<P>(p3.X()));
_verts.push_back(static_cast<P>(p3.Y()));
_verts.push_back(static_cast<P>(p3.Z()));
*/
trsf.Transforms(p);
_material_ids.push_back(surface_style_id);
_verts.push_back(static_cast<P>(p.X()));
_verts.push_back(static_cast<P>(p.Y()));
_verts.push_back(static_cast<P>(p.Z()));
if (i > 1) {
_edges.push_back(start + i - 2);
_edges.push_back(start + i - 1);
// _edges.push_back(start + 3 * (i - 2) + 2);
// _edges.push_back(start + 3 * (i - 1) + 2);
}
// _edges.push_back(start + 3 * (i - 1) + 0);
// _edges.push_back(start + 3 * (i - 1) + 2);
// _edges.push_back(start + 3 * (i - 1) + 1);
// _edges.push_back(start + 3 * (i - 1) + 2);
}
}
}
BRepTools::Clean(s);
}
}
virtual ~Triangulation() {}
/// Generates UVs for a single mesh using box projection.
/// @todo Very simple impl. Assumes that input vertices and normals match 1:1.
static std::vector<P> box_project_uvs(const std::vector<P> &vertices, const std::vector<P> &normals)
{
std::vector<P> uvs;
uvs.resize(vertices.size() / 3 * 2);
for (size_t uv_idx = 0, v_idx = 0;
uv_idx < uvs.size() && v_idx < vertices.size() && v_idx < normals.size();
uv_idx += 2, v_idx += 3) {
P n_x = normals[v_idx], n_y = normals[v_idx + 1], n_z = normals[v_idx + 2];
P v_x = vertices[v_idx], v_y = vertices[v_idx + 1], v_z = vertices[v_idx + 2];
if (std::abs(n_x) > std::abs(n_y) && std::abs(n_x) > std::abs(n_z)) {
uvs[uv_idx] = v_z;
uvs[uv_idx + 1] = v_y;
}
if (std::abs(n_y) > std::abs(n_x) && std::abs(n_y) > std::abs(n_z)) {
uvs[uv_idx] = v_x;
uvs[uv_idx + 1] = v_z;
}
if (std::abs(n_z) > std::abs(n_x) && std::abs(n_z) > std::abs(n_y)) {
uvs[uv_idx] = v_x;
uvs[uv_idx + 1] = v_y;
}
}
return uvs;
}
private:
// Welds vertices that belong to different faces
int addVertex(int material_index, const gp_XYZ& p) {
const bool convert = settings().get(IteratorSettings::CONVERT_BACK_UNITS);
const P X = static_cast<P>(convert ? (p.X() / settings().unit_magnitude()) : p.X());
const P Y = static_cast<P>(convert ? (p.Y() / settings().unit_magnitude()) : p.Y());
const P Z = static_cast<P>(convert ? (p.Z() / settings().unit_magnitude()) : p.Z());
int i = (int) _verts.size() / 3;
if (settings().get(IteratorSettings::WELD_VERTICES)) {
const VertexKey key = std::make_pair(material_index, std::make_pair(X, std::make_pair(Y, Z)));
typename VertexKeyMap::const_iterator it = welds.find(key);
if ( it != welds.end() ) return it->second;
i = (int) welds.size();
welds[key] = i;
}
_verts.push_back(X);
_verts.push_back(Y);
_verts.push_back(Z);
return i;
}
inline void addEdge(int n1, int n2, std::map<std::pair<int,int>,int>& edgecount, std::vector<std::pair<int,int> >& edges_temp) {
const Edge e = Edge( (std::min)(n1,n2),(std::max)(n1,n2) );
if ( edgecount.find(e) == edgecount.end() ) edgecount[e] = 1;
else edgecount[e] ++;
edges_temp.push_back(e);
}
Triangulation();
Triangulation(const Triangulation&);
Triangulation& operator=(const Triangulation&);
};
}
}
#endif
-660
View File
@@ -1,660 +0,0 @@
#include <Geom_Line.hxx>
#include <Geom_Circle.hxx>
#include <Geom_Ellipse.hxx>
#include <Geom_BSplineCurve.hxx>
#include <Geom_Plane.hxx>
#include <Geom_BSplineSurface.hxx>
#include <Geom_CylindricalSurface.hxx>
#include <BRepTools_WireExplorer.hxx>
#include <TColgp_Array2OfPnt.hxx>
#include <TColStd_Array1OfReal.hxx>
#include <TColStd_Array2OfReal.hxx>
#include <TColStd_Array1OfInteger.hxx>
#include "IfcGeom.h"
template <typename T, typename U>
int convert_to_ifc(const T& t, U*& u, bool /*advanced*/) {
std::vector<double> coords(3);
coords[0] = t.X(); coords[1] = t.Y(); coords[2] = t.Z();
u = new U(coords);
return 1;
}
template <>
int convert_to_ifc(const TopoDS_Vertex& v, IfcSchema::IfcCartesianPoint*& p, bool advanced) {
gp_Pnt pnt = BRep_Tool::Pnt(v);
return convert_to_ifc(pnt, p, advanced);
}
template <>
int convert_to_ifc(const TopoDS_Vertex& v, IfcSchema::IfcVertex*& vertex, bool advanced) {
IfcSchema::IfcCartesianPoint* p;
convert_to_ifc(v, p, advanced);
vertex = new IfcSchema::IfcVertexPoint(p);
return 1;
}
template <>
int convert_to_ifc(const gp_Ax2& a, IfcSchema::IfcAxis2Placement3D*& ax, bool advanced) {
IfcSchema::IfcCartesianPoint* p;
IfcSchema::IfcDirection *x, *z;
if (!(convert_to_ifc(a.Location(), p, advanced) && convert_to_ifc(a.Direction(), z, advanced) && convert_to_ifc(a.XDirection(), x, advanced))) {
ax = 0;
return 0;
}
ax = new IfcSchema::IfcAxis2Placement3D(p, z, x);
return 1;
}
template <typename T, typename U>
void opencascade_array_to_vector(T& t, std::vector<U>& u) {
u.reserve(t.Length());
for (int i = t.Lower(); i <= t.Upper(); ++i) {
u.push_back(t.Value(i));
}
}
template <typename T, typename U>
void opencascade_array_to_vector2(T& t, std::vector< std::vector<U> >& u) {
u.reserve(t.RowLength());
for (int j = t.LowerRow(); j <= t.UpperRow(); ++j) {
std::vector<U> v;
v.reserve(t.ColLength());
for (int i = t.LowerCol(); i <= t.UpperCol(); ++i) {
v.push_back(t.Value(j, i));
}
u.push_back(v);
}
}
#ifdef USE_IFC4
IfcSchema::IfcKnotType::Value opencascade_knotspec_to_ifc(GeomAbs_BSplKnotDistribution bspline_knot_spec) {
IfcSchema::IfcKnotType::Value knot_spec = IfcSchema::IfcKnotType::IfcKnotType_UNSPECIFIED;
if (bspline_knot_spec == GeomAbs_Uniform) {
knot_spec = IfcSchema::IfcKnotType::IfcKnotType_UNIFORM_KNOTS;
} else if (bspline_knot_spec == GeomAbs_QuasiUniform) {
knot_spec = IfcSchema::IfcKnotType::IfcKnotType_QUASI_UNIFORM_KNOTS;
} else if (bspline_knot_spec == GeomAbs_PiecewiseBezier) {
knot_spec = IfcSchema::IfcKnotType::IfcKnotType_PIECEWISE_BEZIER_KNOTS;
}
return knot_spec;
}
#endif
template <>
int convert_to_ifc(const Handle_Geom_Curve& c, IfcSchema::IfcCurve*& curve, bool advanced) {
if (c->DynamicType() == STANDARD_TYPE(Geom_Line)) {
IfcSchema::IfcDirection* d;
IfcSchema::IfcCartesianPoint* p;
Handle_Geom_Line line = Handle_Geom_Line::DownCast(c);
if (!convert_to_ifc(line->Position().Location(), p, advanced)) {
return 0;
}
if (!convert_to_ifc(line->Position().Direction(), d, advanced)) {
return 0;
}
IfcSchema::IfcVector* v = new IfcSchema::IfcVector(d, 1.);
curve = new IfcSchema::IfcLine(p, v);
return 1;
} else if (c->DynamicType() == STANDARD_TYPE(Geom_Circle)) {
IfcSchema::IfcAxis2Placement3D* ax;
Handle_Geom_Circle circle = Handle_Geom_Circle::DownCast(c);
convert_to_ifc(circle->Position(), ax, advanced);
curve = new IfcSchema::IfcCircle(ax, circle->Radius());
return 1;
} else if (c->DynamicType() == STANDARD_TYPE(Geom_Ellipse)) {
IfcSchema::IfcAxis2Placement3D* ax;
Handle_Geom_Ellipse ellipse = Handle_Geom_Ellipse::DownCast(c);
convert_to_ifc(ellipse->Position(), ax, advanced);
curve = new IfcSchema::IfcEllipse(ax, ellipse->MajorRadius(), ellipse->MinorRadius());
return 1;
}
#ifdef USE_IFC4
else if (c->DynamicType() == STANDARD_TYPE(Geom_BSplineCurve)) {
Handle_Geom_BSplineCurve bspline = Handle_Geom_BSplineCurve::DownCast(c);
IfcSchema::IfcCartesianPoint::list::ptr points(new IfcSchema::IfcCartesianPoint::list);
TColgp_Array1OfPnt poles(1, bspline->NbPoles());
bspline->Poles(poles);
for (int i = 1; i <= bspline->NbPoles(); ++i) {
IfcSchema::IfcCartesianPoint* p;
if (!convert_to_ifc(poles.Value(i), p, advanced)) {
return 0;
}
points->push(p);
}
IfcSchema::IfcKnotType::Value knot_spec = opencascade_knotspec_to_ifc(bspline->KnotDistribution());
std::vector<int> mults;
std::vector<double> knots;
std::vector<double> weights;
TColStd_Array1OfInteger bspline_mults(1, bspline->NbKnots());
TColStd_Array1OfReal bspline_knots(1, bspline->NbKnots());
TColStd_Array1OfReal bspline_weights(1, bspline->NbPoles());
bspline->Multiplicities(bspline_mults);
bspline->Knots(bspline_knots);
bspline->Weights(bspline_weights);
opencascade_array_to_vector(bspline_mults, mults);
opencascade_array_to_vector(bspline_knots, knots);
opencascade_array_to_vector(bspline_weights, weights);
bool rational = false;
for (std::vector<double>::const_iterator it = weights.begin(); it != weights.end(); ++it) {
if ((*it) != 1.) {
rational = true;
break;
}
}
if (rational) {
curve = new IfcSchema::IfcRationalBSplineCurveWithKnots(
bspline->Degree(),
points,
IfcSchema::IfcBSplineCurveForm::IfcBSplineCurveForm_UNSPECIFIED,
bspline->IsClosed() != 0,
false,
mults,
knots,
knot_spec,
weights
);
} else {
curve = new IfcSchema::IfcBSplineCurveWithKnots(
bspline->Degree(),
points,
IfcSchema::IfcBSplineCurveForm::IfcBSplineCurveForm_UNSPECIFIED,
bspline->IsClosed() != 0,
false,
mults,
knots,
knot_spec
);
}
return 1;
}
#endif
return 0;
}
template <>
int convert_to_ifc(const Handle_Geom_Surface& s, IfcSchema::IfcSurface*& surface, bool advanced) {
if (s->DynamicType() == STANDARD_TYPE(Geom_Plane)) {
Handle_Geom_Plane plane = Handle_Geom_Plane::DownCast(s);
IfcSchema::IfcAxis2Placement3D* place;
/// @todo: Note that the Ax3 is converted to an Ax2 here
if (!convert_to_ifc(plane->Position().Ax2(), place, advanced)) {
return 0;
}
surface = new IfcSchema::IfcPlane(place);
return 1;
}
#ifdef USE_IFC4
else if (s->DynamicType() == STANDARD_TYPE(Geom_CylindricalSurface)) {
Handle_Geom_CylindricalSurface cyl = Handle_Geom_CylindricalSurface::DownCast(s);
IfcSchema::IfcAxis2Placement3D* place;
/// @todo: Note that the Ax3 is converted to an Ax2 here
if (!convert_to_ifc(cyl->Position().Ax2(), place, advanced)) {
return 0;
}
surface = new IfcSchema::IfcCylindricalSurface(place, cyl->Radius());
return 1;
} else if (s->DynamicType() == STANDARD_TYPE(Geom_BSplineSurface)) {
typedef IfcTemplatedEntityListList<IfcSchema::IfcCartesianPoint> points_t;
Handle_Geom_BSplineSurface bspline = Handle_Geom_BSplineSurface::DownCast(s);
points_t::ptr points(new points_t);
TColgp_Array2OfPnt poles(1, bspline->NbUPoles(), 1, bspline->NbVPoles());
bspline->Poles(poles);
for (int i = 1; i <= bspline->NbUPoles(); ++i) {
std::vector<IfcSchema::IfcCartesianPoint*> ps;
ps.reserve(bspline->NbVPoles());
for (int j = 1; j <= bspline->NbVPoles(); ++j) {
IfcSchema::IfcCartesianPoint* p;
if (!convert_to_ifc(poles.Value(i, j), p, advanced)) {
return 0;
}
ps.push_back(p);
}
points->push(ps);
}
IfcSchema::IfcKnotType::Value knot_spec_u = opencascade_knotspec_to_ifc(bspline->UKnotDistribution());
IfcSchema::IfcKnotType::Value knot_spec_v = opencascade_knotspec_to_ifc(bspline->VKnotDistribution());
if (knot_spec_u != knot_spec_v) {
knot_spec_u = IfcSchema::IfcKnotType::IfcKnotType_UNSPECIFIED;
}
std::vector<int> umults;
std::vector<int> vmults;
std::vector<double> uknots;
std::vector<double> vknots;
std::vector< std::vector<double> > weights;
TColStd_Array1OfInteger bspline_umults(1, bspline->NbUKnots());
TColStd_Array1OfInteger bspline_vmults(1, bspline->NbVKnots());
TColStd_Array1OfReal bspline_uknots(1, bspline->NbUKnots());
TColStd_Array1OfReal bspline_vknots(1, bspline->NbVKnots());
TColStd_Array2OfReal bspline_weights(1, bspline->NbUPoles(), 1, bspline->NbVPoles());
bspline->UMultiplicities(bspline_umults);
bspline->VMultiplicities(bspline_vmults);
bspline->UKnots(bspline_uknots);
bspline->VKnots(bspline_vknots);
bspline->Weights(bspline_weights);
opencascade_array_to_vector(bspline_umults, umults);
opencascade_array_to_vector(bspline_vmults, vmults);
opencascade_array_to_vector(bspline_uknots, uknots);
opencascade_array_to_vector(bspline_vknots, vknots);
opencascade_array_to_vector2(bspline_weights, weights);
bool rational = false;
for (std::vector< std::vector<double> >::const_iterator it = weights.begin(); it != weights.end(); ++it) {
for (std::vector<double>::const_iterator jt = it->begin(); jt != it->end(); ++jt) {
if ((*jt) != 1.) {
rational = true;
break;
}
}
}
if (rational) {
surface = new IfcSchema::IfcRationalBSplineSurfaceWithKnots(
bspline->UDegree(),
bspline->VDegree(),
points,
IfcSchema::IfcBSplineSurfaceForm::IfcBSplineSurfaceForm_UNSPECIFIED,
bspline->IsUClosed() != 0,
bspline->IsVClosed() != 0,
false,
umults,
vmults,
uknots,
vknots,
knot_spec_u,
weights
);
} else {
surface = new IfcSchema::IfcBSplineSurfaceWithKnots(
bspline->UDegree(),
bspline->VDegree(),
points,
IfcSchema::IfcBSplineSurfaceForm::IfcBSplineSurfaceForm_UNSPECIFIED,
bspline->IsUClosed() != 0,
bspline->IsVClosed() != 0,
false,
umults,
vmults,
uknots,
vknots,
knot_spec_u
);
}
return 1;
}
#endif
return 0;
}
template <>
int convert_to_ifc(const TopoDS_Edge& e, IfcSchema::IfcCurve*& c, bool advanced) {
double a, b;
IfcSchema::IfcCurve* base;
Handle_Geom_Curve crv = BRep_Tool::Curve(e, a, b);
if (!convert_to_ifc(crv, base, advanced)) {
return 0;
}
IfcEntityList::ptr trim1(new IfcEntityList);
IfcEntityList::ptr trim2(new IfcEntityList);
trim1->push(new IfcSchema::IfcParameterValue(a));
trim2->push(new IfcSchema::IfcParameterValue(b));
c = new IfcSchema::IfcTrimmedCurve(base, trim1, trim2, true, IfcSchema::IfcTrimmingPreference::IfcTrimmingPreference_PARAMETER);
return 1;
}
template <>
int convert_to_ifc(const TopoDS_Edge& e, IfcSchema::IfcEdge*& edge, bool advanced) {
double a, b;
TopExp_Explorer exp(e, TopAbs_VERTEX);
if (!exp.More()) return 0;
TopoDS_Vertex v1 = TopoDS::Vertex(exp.Current());
exp.Next();
if (!exp.More()) return 0;
TopoDS_Vertex v2 = TopoDS::Vertex(exp.Current());
IfcSchema::IfcVertex *vertex1, *vertex2;
if (!(convert_to_ifc(v1, vertex1, advanced) && convert_to_ifc(v2, vertex2, advanced))) {
return 0;
}
Handle_Geom_Curve crv = BRep_Tool::Curve(e, a, b);
if (crv.IsNull()) {
return 0;
}
if (crv->DynamicType() == STANDARD_TYPE(Geom_Line) && !advanced) {
IfcSchema::IfcEdge* edge2 = new IfcSchema::IfcEdge(vertex1, vertex2);
edge = new IfcSchema::IfcOrientedEdge(edge2, true);
return 1;
} else {
IfcSchema::IfcCurve* curve;
if (!convert_to_ifc(crv, curve, advanced)) {
return 0;
}
/// @todo probably not correct
const bool sense = e.Orientation() == TopAbs_FORWARD;
IfcSchema::IfcEdge* edge2 = new IfcSchema::IfcEdgeCurve(vertex1, vertex2, curve, true);
edge = new IfcSchema::IfcOrientedEdge(edge2, sense);
return 1;
}
}
template <>
int convert_to_ifc(const TopoDS_Wire& wire, IfcSchema::IfcLoop*& loop, bool advanced) {
bool polygonal = true;
for (TopExp_Explorer exp(wire, TopAbs_EDGE); exp.More(); exp.Next()) {
double a, b;
Handle_Geom_Curve crv = BRep_Tool::Curve(TopoDS::Edge(exp.Current()), a, b);
if (crv.IsNull()) {
continue;
}
if (crv->DynamicType() != STANDARD_TYPE(Geom_Line)) {
polygonal = false;
break;
}
}
if (!polygonal && !advanced) {
return 0;
} else if (polygonal && !advanced) {
IfcSchema::IfcCartesianPoint::list::ptr points(new IfcSchema::IfcCartesianPoint::list);
BRepTools_WireExplorer exp(wire);
IfcSchema::IfcCartesianPoint* p;
for (; exp.More(); exp.Next()) {
if (convert_to_ifc(exp.CurrentVertex(), p, advanced)) {
points->push(p);
} else {
return 0;
}
}
loop = new IfcSchema::IfcPolyLoop(points);
return 1;
} else {
IfcSchema::IfcOrientedEdge::list::ptr edges(new IfcSchema::IfcOrientedEdge::list);
BRepTools_WireExplorer exp(wire);
for (; exp.More(); exp.Next()) {
IfcSchema::IfcEdge* edge;
// With advanced set to true convert_to_ifc(TopoDS_Edge&) will always create an IfcOrientedEdge
if (!convert_to_ifc(exp.Current(), edge, true)) {
double a, b;
if (BRep_Tool::Curve(TopoDS::Edge(exp.Current()), a, b).IsNull()) {
continue;
} else {
return 0;
}
}
edges->push(edge->as<IfcSchema::IfcOrientedEdge>());
}
loop = new IfcSchema::IfcEdgeLoop(edges);
return 1;
}
}
template <>
int convert_to_ifc(const TopoDS_Face& f, IfcSchema::IfcFace*& face, bool advanced) {
Handle_Geom_Surface surf = BRep_Tool::Surface(f);
TopExp_Explorer exp(f, TopAbs_WIRE);
IfcSchema::IfcFaceBound::list::ptr bounds(new IfcSchema::IfcFaceBound::list);
int index = 0;
for (; exp.More(); exp.Next(), ++index) {
IfcSchema::IfcLoop* loop;
if (!convert_to_ifc(TopoDS::Wire(exp.Current()), loop, advanced)) {
return 0;
}
IfcSchema::IfcFaceBound* bnd;
if (index == 0) {
bnd = new IfcSchema::IfcFaceOuterBound(loop, true);
} else {
bnd = new IfcSchema::IfcFaceBound(loop, true);
}
bounds->push(bnd);
}
const bool is_planar = surf->DynamicType() == STANDARD_TYPE(Geom_Plane);
if (!is_planar && !advanced) {
return 0;
}
if (is_planar && !advanced) {
face = new IfcSchema::IfcFace(bounds);
return 1;
} else {
#ifdef USE_IFC4
IfcSchema::IfcSurface* surface;
if (!convert_to_ifc(surf, surface, advanced)) {
return 0;
}
face = new IfcSchema::IfcAdvancedFace(bounds, surface, f.Orientation() == TopAbs_FORWARD);
return 1;
#else
// No IfcAdvancedFace in Ifc2x3
return 0;
#endif
}
}
template <typename U>
int convert_to_ifc(const TopoDS_Shape& s, U*& item, bool advanced) {
IfcSchema::IfcFace::list::ptr faces(new IfcSchema::IfcFace::list);
IfcSchema::IfcFace* f;
for (TopExp_Explorer exp(s, TopAbs_FACE); exp.More(); exp.Next()) {
if (convert_to_ifc(TopoDS::Face(exp.Current()), f, advanced)) {
faces->push(f);
} else {
/// Cleanup:
for (IfcSchema::IfcFace::list::it it = faces->begin(); it != faces->end(); ++it) {
IfcEntityList::ptr data = IfcParse::traverse(*it)->unique();
for (IfcEntityList::it jt = data->begin(); jt != data->end(); ++jt) {
delete *jt;
}
}
return 0;
}
}
item = new U(faces);
return faces->size();
}
IfcUtil::IfcBaseClass* IfcGeom::MAKE_TYPE_NAME(serialise_)(const TopoDS_Shape& shape, bool advanced) {
#ifndef USE_IFC4
advanced = false;
#endif
for (TopExp_Explorer exp(shape, TopAbs_COMPSOLID); exp.More();) {
/// @todo CompSolids are not supported
return 0;
}
IfcSchema::IfcRepresentation* rep = 0;
IfcSchema::IfcRepresentationItem::list::ptr items(new IfcSchema::IfcRepresentationItem::list);
// First check if there is a solid with one or more shells
for (TopExp_Explorer exp(shape, TopAbs_SOLID); exp.More(); exp.Next()) {
IfcSchema::IfcClosedShell* outer = 0;
IfcSchema::IfcClosedShell::list::ptr inner(new IfcSchema::IfcClosedShell::list);
for (TopExp_Explorer exp2(exp.Current(), TopAbs_SHELL); exp2.More(); exp2.Next()) {
IfcSchema::IfcClosedShell* shell;
if (!convert_to_ifc(exp2.Current(), shell, advanced)) {
return 0;
}
/// @todo Are shells always in this order or does Orientation() needs to be checked?
if (outer) {
inner->push(shell);
} else {
outer = shell;
}
}
#ifdef USE_IFC4
if (advanced) {
if (inner->size()) {
items->push(new IfcSchema::IfcAdvancedBrepWithVoids(outer, inner));
} else {
items->push(new IfcSchema::IfcAdvancedBrep(outer));
}
} else
#endif
/// @todo this is not necessarily correct as the shell is not necessarily facetted.
if (inner->size()) {
items->push(new IfcSchema::IfcFacetedBrepWithVoids(outer, inner));
} else {
items->push(new IfcSchema::IfcFacetedBrep(outer));
}
}
if (items->size() > 0) {
rep = new IfcSchema::IfcShapeRepresentation(0, std::string("Body"), std::string("Brep"), items);
} else {
// If not, see if there is a shell
IfcSchema::IfcOpenShell::list::ptr shells(new IfcSchema::IfcOpenShell::list);
for (TopExp_Explorer exp(shape, TopAbs_SHELL); exp.More(); exp.Next()) {
IfcSchema::IfcOpenShell* shell;
if (!convert_to_ifc(exp.Current(), shell, advanced)) {
return 0;
}
shells->push(shell);
}
if (shells->size() > 0) {
items->push(new IfcSchema::IfcShellBasedSurfaceModel(shells->generalize()));
rep = new IfcSchema::IfcShapeRepresentation(0, std::string("Body"), std::string("Brep"), items);
} else {
// If not, see if there is are one of more faces. Note that they will be grouped into a shell.
IfcSchema::IfcOpenShell* shell;
int face_count = convert_to_ifc(shape, shell, advanced);
if (face_count > 0) {
items->push(shell);
rep = new IfcSchema::IfcShapeRepresentation(0, std::string("Body"), std::string("Brep"), items);
} else {
// If not, see if there are any edges. Note that wires are skipped as
// they are not commonly top-level geometrical descriptions in IFC.
// Also note that edges are written as trimmed curves rather than edges.
IfcEntityList::ptr edges(new IfcEntityList);
for (TopExp_Explorer exp(shape, TopAbs_EDGE); exp.More(); exp.Next()) {
IfcSchema::IfcCurve* c;
if (!convert_to_ifc(TopoDS::Edge(exp.Current()), c, advanced)) {
return 0;
}
edges->push(c);
}
if (edges->size() == 0) {
return 0;
} else if (edges->size() == 1) {
rep = new IfcSchema::IfcShapeRepresentation(0, std::string("Axis"), std::string("Curve2D"), edges->as<IfcSchema::IfcRepresentationItem>());
} else {
// A geometric set is created as that probably (?) makes more sense in IFC
IfcSchema::IfcGeometricCurveSet* curves = new IfcSchema::IfcGeometricCurveSet(edges);
items->push(curves);
rep = new IfcSchema::IfcShapeRepresentation(0, std::string("Axis"), std::string("GeometricCurveSet"), items->as<IfcSchema::IfcRepresentationItem>());
}
}
}
}
IfcSchema::IfcRepresentation::list::ptr reps(new IfcSchema::IfcRepresentation::list);
reps->push(rep);
return new IfcSchema::IfcProductDefinitionShape(boost::none, boost::none, reps);
}
IfcUtil::IfcBaseClass* IfcGeom::MAKE_TYPE_NAME(tesselate_)(const TopoDS_Shape& shape, double deflection) {
BRepMesh_IncrementalMesh(shape, deflection);
IfcSchema::IfcFace::list::ptr faces(new IfcSchema::IfcFace::list);
for (TopExp_Explorer exp(shape, TopAbs_FACE); exp.More(); exp.Next()) {
const TopoDS_Face& face = TopoDS::Face(exp.Current());
TopLoc_Location loc;
Handle(Poly_Triangulation) tri = BRep_Tool::Triangulation(face, loc);
if (!tri.IsNull()) {
const TColgp_Array1OfPnt& nodes = tri->Nodes();
std::vector<IfcSchema::IfcCartesianPoint*> vertices;
for (int i = 1; i <= nodes.Length(); ++i) {
gp_Pnt pnt = nodes(i).Transformed(loc);
std::vector<double> xyz; xyz.push_back(pnt.X()); xyz.push_back(pnt.Y()); xyz.push_back(pnt.Z());
IfcSchema::IfcCartesianPoint* cpnt = new IfcSchema::IfcCartesianPoint(xyz);
vertices.push_back(cpnt);
}
const Poly_Array1OfTriangle& triangles = tri->Triangles();
for (int i = 1; i <= triangles.Length(); ++i) {
int n1, n2, n3;
triangles(i).Get(n1, n2, n3);
IfcSchema::IfcCartesianPoint::list::ptr points(new IfcSchema::IfcCartesianPoint::list);
points->push(vertices[n1 - 1]);
points->push(vertices[n2 - 1]);
points->push(vertices[n3 - 1]);
IfcSchema::IfcPolyLoop* loop = new IfcSchema::IfcPolyLoop(points);
IfcSchema::IfcFaceOuterBound* bound = new IfcSchema::IfcFaceOuterBound(loop, face.Orientation() != TopAbs_REVERSED);
IfcSchema::IfcFaceBound::list::ptr bounds(new IfcSchema::IfcFaceBound::list);
bounds->push(bound);
IfcSchema::IfcFace* face2 = new IfcSchema::IfcFace(bounds);
faces->push(face2);
}
}
}
IfcSchema::IfcOpenShell* shell = new IfcSchema::IfcOpenShell(faces);
IfcSchema::IfcConnectedFaceSet::list::ptr shells(new IfcSchema::IfcConnectedFaceSet::list);
shells->push(shell);
IfcSchema::IfcFaceBasedSurfaceModel* surface_model = new IfcSchema::IfcFaceBasedSurfaceModel(shells);
IfcSchema::IfcRepresentation::list::ptr reps(new IfcSchema::IfcRepresentation::list);
IfcSchema::IfcRepresentationItem::list::ptr items(new IfcSchema::IfcRepresentationItem::list);
items->push(surface_model);
IfcSchema::IfcShapeRepresentation* rep = new IfcSchema::IfcShapeRepresentation(
0, std::string("Facetation"), std::string("SurfaceModel"), items);
reps->push(rep);
IfcSchema::IfcProductDefinitionShape* shapedef = new IfcSchema::IfcProductDefinitionShape(boost::none, boost::none, reps);
return shapedef;
}
-38
View File
@@ -1,38 +0,0 @@
/********************************************************************************
* *
* 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/>. *
* *
********************************************************************************/
#ifndef IFCGEOMSHAPETYPE_H
#define IFCGEOMSHAPETYPE_H
namespace IfcGeom {
enum ShapeType {
ST_SHAPELIST,
ST_SHAPE,
ST_FACE,
ST_WIRE,
ST_CURVE,
ST_EDGE,
ST_VERTEX,
ST_OTHER
};
}
#endif
File diff suppressed because it is too large Load Diff
-929
View File
@@ -1,929 +0,0 @@
/********************************************************************************
* *
* 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 IfcRegister.h *
* *
********************************************************************************/
#define _USE_MATH_DEFINES
#include <cmath>
#include <gp_Pnt.hxx>
#include <gp_Vec.hxx>
#include <gp_Dir.hxx>
#include <gp_Pnt2d.hxx>
#include <gp_Vec2d.hxx>
#include <gp_Dir2d.hxx>
#include <gp_Mat.hxx>
#include <gp_Mat2d.hxx>
#include <gp_GTrsf.hxx>
#include <gp_GTrsf2d.hxx>
#include <gp_Trsf.hxx>
#include <gp_Trsf2d.hxx>
#include <gp_Ax3.hxx>
#include <gp_Ax2d.hxx>
#include <gp_Pln.hxx>
#include <gp_Circ.hxx>
#include <GC_MakeCircle.hxx>
#include <TColgp_Array1OfPnt.hxx>
#include <TColgp_Array1OfPnt2d.hxx>
#include <TColStd_Array1OfReal.hxx>
#include <TColStd_Array1OfInteger.hxx>
#include <Geom_Line.hxx>
#include <Geom_Circle.hxx>
#include <Geom_Ellipse.hxx>
#include <Geom_TrimmedCurve.hxx>
#include <BRepBuilderAPI_MakeVertex.hxx>
#include <BRepBuilderAPI_MakeFace.hxx>
#include <BRepBuilderAPI_MakeEdge.hxx>
#include <BRepBuilderAPI_MakeWire.hxx>
#include <BRepBuilderAPI_MakeShell.hxx>
#include <BRepBuilderAPI_MakeSolid.hxx>
#include <BRepBuilderAPI_MakePolygon.hxx>
#include <BRepBuilderAPI_MakeVertex.hxx>
#include <TopoDS.hxx>
#include <TopoDS_Wire.hxx>
#include <TopoDS_Face.hxx>
#include <TopExp.hxx>
#include <TopExp_Explorer.hxx>
#include <TopLoc_Location.hxx>
#include <TopTools_ListOfShape.hxx>
#include <BRepAlgoAPI_Cut.hxx>
#include <BRepOffsetAPI_Sewing.hxx>
#include <BRepPrimAPI_MakePrism.hxx>
#include <BRepPrimAPI_MakeHalfSpace.hxx>
#include <BRepFilletAPI_MakeFillet2d.hxx>
#include <BRep_Tool.hxx>
#include <ShapeFix_Shape.hxx>
#include <ShapeFix_ShapeTolerance.hxx>
#include <ShapeFix_Solid.hxx>
#include <Geom_BSplineCurve.hxx>
#include <BRepTools_WireExplorer.hxx>
#include <ShapeBuild_ReShape.hxx>
#include <TopTools_ListOfShape.hxx>
#include <TopTools_ListIteratorOfListOfShape.hxx>
#include <BRepAdaptor_CompCurve.hxx>
#include <BRepAdaptor_HCompCurve.hxx>
#include <Approx_Curve3d.hxx>
#include "../ifcgeom/IfcGeom.h"
#define Kernel MAKE_TYPE_NAME(Kernel)
namespace {
// Returns the other vertex of an edge
TopoDS_Vertex other(const TopoDS_Edge& e, const TopoDS_Vertex& v) {
TopoDS_Vertex a, b;
TopExp::Vertices(e, a, b);
return v.IsSame(b) ? a : b;
}
TopoDS_Edge first_edge(const TopoDS_Wire& w) {
TopoDS_Vertex v1, v2;
TopExp::Vertices(w, v1, v2);
TopTools_IndexedDataMapOfShapeListOfShape wm;
TopExp::MapShapesAndAncestors(w, TopAbs_VERTEX, TopAbs_EDGE, wm);
return TopoDS::Edge(wm.FindFromKey(v1).First());
}
// Returns new wire with the edge replaced by a linear edge with the vertex v moved to p
TopoDS_Wire adjust(const TopoDS_Wire& w, const TopoDS_Vertex& v, const gp_Pnt& p) {
TopTools_IndexedDataMapOfShapeListOfShape map;
TopExp::MapShapesAndAncestors(w, TopAbs_VERTEX, TopAbs_EDGE, map);
bool all_linear = true, single_circle = false, first = true;
const TopTools_ListOfShape& edges = map.FindFromKey(v);
TopTools_ListIteratorOfListOfShape it(edges);
for (; it.More(); it.Next()) {
const TopoDS_Edge& e = TopoDS::Edge(it.Value());
double _, __;
Handle(Geom_Curve) crv = BRep_Tool::Curve(e, _, __);
const bool is_line = crv->DynamicType() == STANDARD_TYPE(Geom_Line);
const bool is_circle = crv->DynamicType() == STANDARD_TYPE(Geom_Circle);
all_linear = all_linear && is_line;
single_circle = first && is_circle;
}
if (all_linear) {
BRep_Builder b;
TopoDS_Vertex v2;
b.MakeVertex(v2, p, BRep_Tool::Tolerance(v));
ShapeBuild_ReShape reshape;
reshape.Replace(v.Oriented(TopAbs_FORWARD), v2);
return TopoDS::Wire(reshape.Apply(w));
} else if (single_circle) {
TopoDS_Vertex v1, v2;
TopExp::Vertices(w, v1, v2);
gp_Pnt p1, p2, p3;
p1 = v.IsEqual(v1) ? p : BRep_Tool::Pnt(v1);
p3 = v.IsEqual(v2) ? p : BRep_Tool::Pnt(v2);
double a, b;
Handle(Geom_Curve) crv = BRep_Tool::Curve(TopoDS::Edge(edges.First()), a, b);
crv->D0((a + b) / 2., p2);
GC_MakeCircle mc(p1, p2, p3);
if (!mc.IsDone()) {
throw IfcGeom::geometry_exception("Failed to adjust circle");
}
TopoDS_Edge edge = BRepBuilderAPI_MakeEdge(mc.Value(), p1, p3).Edge();
BRepBuilderAPI_MakeWire builder;
builder.Add(edge);
return builder.Wire();
} else {
throw IfcGeom::geometry_exception("Unexpected wire to adjust");
}
}
// A wrapper around BRepBuilderAPI_MakeWire that makes sure segments are connected either by moving end points or by adding intermediate segments
class wire_builder {
private:
BRepBuilderAPI_MakeWire mw_;
double p_;
bool override_next_;
gp_Pnt next_override_;
const IfcUtil::IfcBaseClass* inst_;
public:
wire_builder(double p, const IfcUtil::IfcBaseClass* inst = 0) : p_(p), override_next_(false), inst_(inst) {}
void operator()(const TopoDS_Shape& a) {
const TopoDS_Wire& w = TopoDS::Wire(a);
if (override_next_) {
override_next_ = false;
TopoDS_Edge e = first_edge(w);
mw_.Add(adjust(w, TopExp::FirstVertex(e, true), next_override_));
} else {
mw_.Add(w);
}
}
void operator()(const TopoDS_Shape& a, const TopoDS_Shape& b, bool last) {
TopoDS_Wire w1 = TopoDS::Wire(a);
const TopoDS_Wire& w2 = TopoDS::Wire(b);
if (override_next_) {
override_next_ = false;
TopoDS_Edge e = first_edge(w1);
w1 = adjust(w1, TopExp::FirstVertex(e, true), next_override_);
}
TopoDS_Vertex w11, w12, w21, w22;
TopExp::Vertices(w1, w11, w12);
TopExp::Vertices(w2, w21, w22);
gp_Pnt p1 = BRep_Tool::Pnt(w12);
gp_Pnt p2 = BRep_Tool::Pnt(w21);
double dist = p1.Distance(p2);
// Distance is within tolerance, this is fine
if (dist < p_) {
mw_.Add(w1);
goto check;
}
// Distance is too large for attempting to move end points, add intermediate edge
if (dist > 1000. * p_) {
mw_.Add(w1);
mw_.Add(BRepBuilderAPI_MakeEdge(p1, p2));
Logger::Warning("Added additional segment to close gap with length " + boost::lexical_cast<std::string>(dist) + " to:", inst_);
goto check;
}
{
TopTools_IndexedDataMapOfShapeListOfShape wmap1, wmap2;
// Find edges connected to end- and begin vertex
TopExp::MapShapesAndAncestors(w1, TopAbs_VERTEX, TopAbs_EDGE, wmap1);
TopExp::MapShapesAndAncestors(w2, TopAbs_VERTEX, TopAbs_EDGE, wmap2);
const TopTools_ListOfShape& last_edges = wmap1.FindFromKey(w12);
const TopTools_ListOfShape& first_edges = wmap2.FindFromKey(w21);
double _, __;
if (last_edges.Extent() == 1 && first_edges.Extent() == 1) {
Handle(Geom_Curve) c1 = BRep_Tool::Curve(TopoDS::Edge(last_edges.First()), _, __);
Handle(Geom_Curve) c2 = BRep_Tool::Curve(TopoDS::Edge(first_edges.First()), _, __);
const bool is_line1 = c1->DynamicType() == STANDARD_TYPE(Geom_Line);
const bool is_line2 = c2->DynamicType() == STANDARD_TYPE(Geom_Line);
const bool is_circle1 = c1->DynamicType() == STANDARD_TYPE(Geom_Circle);
const bool is_circle2 = c2->DynamicType() == STANDARD_TYPE(Geom_Circle);
// Preferably adjust the segment that is linear
if (is_line1 || (is_circle1 && !is_line2)) {
mw_.Add(adjust(w1, w12, p2));
Logger::Notice("Adjusted edge end-point with distance " + boost::lexical_cast<std::string>(dist) + " on:", inst_);
} else if ((is_line2 || is_circle2) && !last) {
mw_.Add(w1);
override_next_ = true;
next_override_ = p1;
Logger::Notice("Adjusted edge end-point with distance " + boost::lexical_cast<std::string>(dist) + " on:", inst_);
} else {
// In all other cases an edge is added
mw_.Add(w1);
mw_.Add(BRepBuilderAPI_MakeEdge(p1, p2));
Logger::Warning("Added additional segment to close gap with length " + boost::lexical_cast<std::string>(dist) + " to:", inst_);
}
} else {
Logger::Error("Internal error, inconsistent wire segments", inst_);
mw_.Add(w1);
}
}
check:
if (mw_.Error() == BRepBuilderAPI_NonManifoldWire) {
Logger::Error("Non-manifold curve segments:", inst_);
} else if (mw_.Error() == BRepBuilderAPI_DisconnectedWire) {
Logger::Error("Failed to join curve segments:", inst_);
}
}
const TopoDS_Wire& wire() { return mw_.Wire(); }
};
template <typename Fn>
void shape_pair_enumerate(TopTools_ListIteratorOfListOfShape& it, Fn& fn, bool closed) {
bool is_first = true;
TopoDS_Shape first, previous, current;
for (; it.More(); it.Next(), is_first = false) {
current = it.Value();
if (is_first) {
first = current;
} else {
fn(previous, current, false);
}
previous = current;
}
if (closed) {
fn(current, first, true);
} else {
fn(current);
}
}
}
bool IfcGeom::Kernel::convert(const IfcSchema::IfcCompositeCurve* l, TopoDS_Wire& wire) {
if ( getValue(GV_PLANEANGLE_UNIT)<0 ) {
Logger::Message(Logger::LOG_WARNING,"Creating a composite curve without unit information:",l);
// Temporarily pretend we do have unit information
setValue(GV_PLANEANGLE_UNIT,1.0);
bool succes_radians = false;
bool succes_degrees = false;
bool use_radians = false;
bool use_degrees = false;
// First try radians
TopoDS_Wire wire_radians, wire_degrees;
try {
succes_radians = IfcGeom::Kernel::convert(l,wire_radians);
} catch (const std::exception& e) {
Logger::Notice(e);
} catch (const Standard_Failure& e) {
if (e.GetMessageString() && strlen(e.GetMessageString())) {
Logger::Notice(e.GetMessageString());
} else {
Logger::Notice("Unknown error using radians");
}
} catch (...) {
Logger::Notice("Unknown error using radians");
}
// Now try degrees
setValue(GV_PLANEANGLE_UNIT,0.0174532925199433);
try {
succes_degrees = IfcGeom::Kernel::convert(l,wire_degrees);
} catch (const std::exception& e) {
Logger::Notice(e);
} catch (const Standard_Failure& e) {
if (e.GetMessageString() && strlen(e.GetMessageString())) {
Logger::Notice(e.GetMessageString());
} else {
Logger::Notice("Unknown error using degrees");
}
} catch (...) {
Logger::Notice("Unknown error using degrees");
}
// Restore to unknown unit state
setValue(GV_PLANEANGLE_UNIT,-1.0);
if ( succes_degrees && ! succes_radians ) {
use_degrees = true;
} else if ( succes_radians && ! succes_degrees ) {
use_radians = true;
} else if ( succes_radians && succes_degrees ) {
if ( wire_degrees.Closed() && ! wire_radians.Closed() ) {
use_degrees = true;
} else if ( wire_radians.Closed() && ! wire_degrees.Closed() ) {
use_radians = true;
} else {
// No heuristic left to prefer the one over the other,
// apparently both variants are equally successful.
// The curve might be composed of only straight segments.
// Let's go with the wire created using radians as that
// at least is a SI unit.
use_radians = true;
}
}
if ( use_radians ) {
Logger::Message(Logger::LOG_NOTICE,"Used radians to create composite curve");
wire = wire_radians;
} else if ( use_degrees ) {
Logger::Message(Logger::LOG_NOTICE,"Used degrees to create composite curve");
wire = wire_degrees;
}
return use_radians || use_degrees;
}
IfcSchema::IfcCompositeCurveSegment::list::ptr segments = l->Segments();
TopTools_ListOfShape converted_segments;
for (IfcSchema::IfcCompositeCurveSegment::list::it it = segments->begin(); it != segments->end(); ++it) {
IfcSchema::IfcCurve* curve = (*it)->ParentCurve();
TopoDS_Wire segment;
if (!convert_wire(curve, segment)) {
Logger::Message(Logger::LOG_ERROR, "Failed to convert curve:", curve);
continue;
}
if (!(*it)->SameSense()) {
segment.Reverse();
}
ShapeFix_ShapeTolerance FTol;
FTol.SetTolerance(segment, getValue(GV_PRECISION), TopAbs_WIRE);
converted_segments.Append(segment);
}
if (converted_segments.Extent() == 0) {
Logger::Message(Logger::LOG_ERROR, "No segment succesfully converted:", l);
return false;
}
BRepBuilderAPI_MakeWire w;
TopoDS_Vertex wire_first_vertex, wire_last_vertex, edge_first_vertex, edge_last_vertex;
TopTools_ListIteratorOfListOfShape it(converted_segments);
IfcEntityList::ptr profile = l->data().getInverse(&IfcSchema::IfcProfileDef::Class(), -1);
const bool force_close = profile && profile->size() > 0;
wire_builder bld(getValue(GV_PRECISION), l);
shape_pair_enumerate(it, bld, force_close);
wire = bld.wire();
return true;
}
namespace {
/*
Below is code to deduce the formula below in SageMath
| R, b = var('R b')
|
| Bxy = R * cos(b), R * sin(b)
| Cxy = R * cos(b/2), R * sin(b/2)
|
| def dot(v, w):
| return v[0] * w[0] + v[1] * w[1]
|
| def norm(v):
| l = sqrt(v[0]^2 + v[1]^2)
| return v[0] / l, v[1] / l
|
| (R - R*dot(norm(Cxy), norm(Bxy))).full_simplify()
*/
double deflection_for_approximating_circle(double radius, double param) {
return -radius * std::cos(1. / 2. * param) * std::cos(param) - radius * std::sin(1. / 2. * param) * std::sin(param) + radius;
}
}
bool IfcGeom::Kernel::convert(const IfcSchema::IfcTrimmedCurve* l, TopoDS_Wire& wire) {
IfcSchema::IfcCurve* basis_curve = l->BasisCurve();
bool isConic = basis_curve->declaration().is(IfcSchema::IfcConic::Class());
double parameterFactor = isConic ? getValue(GV_PLANEANGLE_UNIT) : getValue(GV_LENGTH_UNIT);
Handle(Geom_Curve) curve;
if (shape_type(basis_curve) == ST_CURVE) {
if (!convert_curve(basis_curve, curve)) return false;
} else if (shape_type(basis_curve) == ST_WIRE) {
Logger::Warning("Approximating BasisCurve due to possible discontinuities", l);
TopoDS_Wire w;
if (!convert_wire(basis_curve, w)) return false;
BRepAdaptor_CompCurve cc(w, true);
Handle(Adaptor3d_HCurve) hcc = Handle(Adaptor3d_HCurve)(new BRepAdaptor_HCompCurve(cc));
// @todo, arbitrary numbers here, note they cannot be too high as contiguous memory is allocated based on them.
Approx_Curve3d approx(hcc, getValue(GV_PRECISION), GeomAbs_C0, 10, 10);
curve = approx.Curve();
} else {
Logger::Error("Unknown BasisCurve", l);
return false;
}
bool trim_cartesian = l->MasterRepresentation() != IfcSchema::IfcTrimmingPreference::IfcTrimmingPreference_PARAMETER;
IfcEntityList::ptr trims1 = l->Trim1();
IfcEntityList::ptr trims2 = l->Trim2();
unsigned sense_agreement = l->SenseAgreement() ? 0 : 1;
double flts[2];
gp_Pnt pnts[2];
bool has_flts[2] = {false,false};
bool has_pnts[2] = {false,false};
TopoDS_Edge e;
for ( IfcEntityList::it it = trims1->begin(); it != trims1->end(); it ++ ) {
IfcUtil::IfcBaseClass* i = *it;
if ( i->declaration().is(IfcSchema::IfcCartesianPoint::Class()) ) {
IfcGeom::Kernel::convert((IfcSchema::IfcCartesianPoint*)i, pnts[sense_agreement] );
has_pnts[sense_agreement] = true;
} else if ( i->declaration().is(IfcSchema::IfcParameterValue::Class()) ) {
const double value = *((IfcSchema::IfcParameterValue*)i);
flts[sense_agreement] = value * parameterFactor;
has_flts[sense_agreement] = true;
}
}
for ( IfcEntityList::it it = trims2->begin(); it != trims2->end(); it ++ ) {
IfcUtil::IfcBaseClass* i = *it;
if ( i->declaration().is(IfcSchema::IfcCartesianPoint::Class()) ) {
IfcGeom::Kernel::convert((IfcSchema::IfcCartesianPoint*)i, pnts[1-sense_agreement] );
has_pnts[1-sense_agreement] = true;
} else if ( i->declaration().is(IfcSchema::IfcParameterValue::Class()) ) {
const double value = *((IfcSchema::IfcParameterValue*)i);
flts[1-sense_agreement] = value * parameterFactor;
has_flts[1-sense_agreement] = true;
}
}
trim_cartesian &= has_pnts[0] && has_pnts[1];
bool trim_cartesian_failed = !trim_cartesian;
if ( trim_cartesian ) {
if ( pnts[0].Distance(pnts[1]) < 2 * getValue(GV_PRECISION) ) {
Logger::Message(Logger::LOG_WARNING,"Skipping segment with length below tolerance level:",l);
return false;
}
ShapeFix_ShapeTolerance FTol;
TopoDS_Vertex v1 = BRepBuilderAPI_MakeVertex(pnts[0]);
TopoDS_Vertex v2 = BRepBuilderAPI_MakeVertex(pnts[1]);
FTol.SetTolerance(v1, getValue(GV_PRECISION), TopAbs_VERTEX);
FTol.SetTolerance(v2, getValue(GV_PRECISION), TopAbs_VERTEX);
BRepBuilderAPI_MakeEdge me (curve,v1,v2);
if (!me.IsDone()) {
BRepBuilderAPI_EdgeError err = me.Error();
if ( err == BRepBuilderAPI_PointProjectionFailed ) {
Logger::Message(Logger::LOG_WARNING,"Point projection failed for:",l);
trim_cartesian_failed = true;
}
} else {
e = me.Edge();
}
}
if ( (!trim_cartesian || trim_cartesian_failed) && (has_flts[0] && has_flts[1]) ) {
// The Geom_Line is constructed from a gp_Pnt and gp_Dir, whereas the IfcLine
// is defined by an IfcCartesianPoint and an IfcVector with Magnitude. Because
// the vector is normalised when passed to Geom_Line constructor the magnitude
// needs to be factored in with the IfcParameterValue here.
if ( basis_curve->declaration().is(IfcSchema::IfcLine::Class()) ) {
IfcSchema::IfcLine* line = static_cast<IfcSchema::IfcLine*>(basis_curve);
const double magnitude = line->Dir()->Magnitude();
flts[0] *= magnitude; flts[1] *= magnitude;
}
if ( basis_curve->declaration().is(IfcSchema::IfcEllipse::Class()) ) {
IfcSchema::IfcEllipse* ellipse = static_cast<IfcSchema::IfcEllipse*>(basis_curve);
double x = ellipse->SemiAxis1() * getValue(GV_LENGTH_UNIT);
double y = ellipse->SemiAxis2() * getValue(GV_LENGTH_UNIT);
const bool rotated = y > x;
if (rotated) {
flts[0] -= M_PI / 2.;
flts[1] -= M_PI / 2.;
}
}
if ( isConic && ALMOST_THE_SAME(fmod(flts[1]-flts[0],M_PI*2.),0.) ) {
e = BRepBuilderAPI_MakeEdge(curve).Edge();
} else {
BRepBuilderAPI_MakeEdge me (curve,flts[0],flts[1]);
e = me.Edge();
}
} else if ( trim_cartesian_failed && (has_pnts[0] && has_pnts[1]) ) {
e = BRepBuilderAPI_MakeEdge(pnts[0], pnts[1]).Edge();
}
if (isConic) {
// Tiny circle segnments can cause issues later on, for example
// when the comp curve is used as the sweeping directrix.
double a, b;
Handle(Geom_Curve) crv = BRep_Tool::Curve(e, a, b);
double radius = -1.;
if (crv->DynamicType() == STANDARD_TYPE(Geom_Circle)) {
radius = Handle(Geom_Circle)::DownCast(crv)->Radius();
} else if (crv->DynamicType() == STANDARD_TYPE(Geom_Ellipse)) {
// The formula above is for circles, but probably good enough
radius = Handle(Geom_Ellipse)::DownCast(crv)->MajorRadius();
}
if (radius > 0. && deflection_for_approximating_circle(radius, b - a) < getValue(GV_PRECISION)) {
TopoDS_Vertex v0, v1;
TopExp::Vertices(e, v0, v1);
e = TopoDS::Edge(BRepBuilderAPI_MakeEdge(v0, v1).Edge().Oriented(e.Orientation()));
Logger::Warning("Subsituted edge with linear approximation", l);
}
}
BRepBuilderAPI_MakeWire w;
w.Add(e);
if (w.IsDone()) {
wire = w.Wire();
// When SenseAgreement == .F. the vertices above have been reversed to
// comply with the direction of conical curves. The ordering of the
// vertices then still needs to be reversed in order to have begin and
// end vertex consistent with IFC.
if (sense_agreement != 0) { // .F.
wire.Reverse();
}
return true;
} else {
return false;
}
}
bool IfcGeom::Kernel::convert(const IfcSchema::IfcPolyline* l, TopoDS_Wire& result) {
IfcSchema::IfcCartesianPoint::list::ptr points = l->Points();
// Parse and store the points in a sequence
TColgp_SequenceOfPnt polygon;
for(IfcSchema::IfcCartesianPoint::list::it it = points->begin(); it != points->end(); ++ it) {
gp_Pnt pnt;
IfcGeom::Kernel::convert(*it, pnt);
polygon.Append(pnt);
}
const double eps = getValue(GV_PRECISION) * 10;
const bool closed_by_proximity = polygon.Length() >= 3 && polygon.First().Distance(polygon.Last()) < eps;
if (closed_by_proximity) {
// tfk: note 1-based
polygon.Remove(polygon.Length());
}
// Remove points that are too close to one another
remove_duplicate_points_from_loop(polygon, closed_by_proximity, eps);
if (polygon.Length() < 2) {
return false;
}
BRepBuilderAPI_MakePolygon w;
for (int i = 1; i <= polygon.Length(); ++i) {
w.Add(polygon.Value(i));
}
if (closed_by_proximity) {
w.Close();
}
result = w.Wire();
return true;
}
bool IfcGeom::Kernel::convert(const IfcSchema::IfcPolyLoop* l, TopoDS_Wire& result) {
IfcSchema::IfcCartesianPoint::list::ptr points = l->Polygon();
// Parse and store the points in a sequence
TColgp_SequenceOfPnt polygon;
for(IfcSchema::IfcCartesianPoint::list::it it = points->begin(); it != points->end(); ++ it) {
gp_Pnt pnt;
IfcGeom::Kernel::convert(*it, pnt);
polygon.Append(pnt);
}
// A loop should consist of at least three vertices
int original_count = polygon.Length();
if (original_count < 3) {
Logger::Message(Logger::LOG_ERROR, "Not enough edges for:", l);
return false;
}
// Remove points that are too close to one another
const double eps = getValue(GV_PRECISION) * 10;
remove_duplicate_points_from_loop(polygon, true, eps);
int count = polygon.Length();
if (original_count - count != 0) {
std::stringstream ss; ss << (original_count - count) << " edges removed for:";
Logger::Message(Logger::LOG_WARNING, ss.str(), l);
}
if (count < 3) {
Logger::Message(Logger::LOG_ERROR, "Not enough edges for:", l);
return false;
}
BRepBuilderAPI_MakePolygon w;
for (int i = 1; i <= polygon.Length(); ++i) {
w.Add(polygon.Value(i));
}
w.Close();
result = w.Wire();
TopTools_ListOfShape results;
if (wire_intersections(result, results)) {
Logger::Error("Self-intersections with " + boost::lexical_cast<std::string>(results.Extent()) + " cycles detected", l);
select_largest(results, result);
}
return true;
}
bool IfcGeom::Kernel::convert(const IfcSchema::IfcArbitraryOpenProfileDef* l, TopoDS_Wire& result) {
return convert_wire(l->Curve(), result);
}
bool IfcGeom::Kernel::convert(const IfcSchema::IfcEdgeCurve* l, TopoDS_Wire& result) {
IfcSchema::IfcPoint* pnt1 = ((IfcSchema::IfcVertexPoint*) l->EdgeStart())->VertexGeometry();
IfcSchema::IfcPoint* pnt2 = ((IfcSchema::IfcVertexPoint*) l->EdgeEnd())->VertexGeometry();
if (!pnt1->declaration().is(IfcSchema::IfcCartesianPoint::Class()) || !pnt2->declaration().is(IfcSchema::IfcCartesianPoint::Class())) {
Logger::Message(Logger::LOG_ERROR, "Only IfcCartesianPoints are supported for VertexGeometry", l);
return false;
}
gp_Pnt p1, p2;
if (!IfcGeom::Kernel::convert(((IfcSchema::IfcCartesianPoint*)pnt1), p1) ||
!IfcGeom::Kernel::convert(((IfcSchema::IfcCartesianPoint*)pnt2), p2))
{
return false;
}
BRepBuilderAPI_MakeWire mw;
Handle_Geom_Curve crv;
// The lack of a clear separation between topological and geometrical entities
// is starting to get problematic. If the underlying curve is bounded it is
// assumed that a topological wire can be crafted from it. After which an
// attempt is made to reconstruct it from the individual curves and the vertices
// of the IfcEdgeCurve.
const bool is_bounded = l->EdgeGeometry()->declaration().is(IfcSchema::IfcBoundedCurve::Class());
if (!is_bounded && convert_curve(l->EdgeGeometry(), crv)) {
BRepBuilderAPI_MakeEdge me(crv, p1, p2);
if (!me.IsDone()) {
return false;
}
mw.Add(me.Edge());
result = mw;
return true;
} else if (is_bounded && convert_wire(l->EdgeGeometry(), result)) {
if (!l->SameSense()) {
result.Reverse();
}
bool first = true;
TopExp_Explorer exp(result, TopAbs_EDGE);
while (exp.More()) {
const TopoDS_Edge& ed = TopoDS::Edge(exp.Current());
Standard_Real u1, u2;
Handle(Geom_Curve) ecrv = BRep_Tool::Curve(ed, u1, u2);
exp.Next();
const bool last = !exp.More();
gp_Pnt a, b;
if (first && last) {
a = p1;
b = p2;
} else if (first) {
a = p1;
ecrv->D0(u2, b);
} else if (last) {
ecrv->D0(u1, a);
b = p2;
} else {
BRepBuilderAPI_MakeEdge me(ecrv, u1, u2);
if (!me.IsDone()) {
return false;
}
mw.Add(me.Edge());
first = false;
continue;
}
BRep_Builder builder;
TopoDS_Vertex v1, v2;
/// @todo project first and emit warnings accordingly
builder.MakeVertex(v1, a, getValue(GV_PRECISION));
builder.MakeVertex(v2, b, getValue(GV_PRECISION));
mw.Add(BRepBuilderAPI_MakeEdge(ecrv, v1, v2));
first = false;
}
result = mw;
return true;
} else {
return false;
}
}
bool IfcGeom::Kernel::convert(const IfcSchema::IfcEdgeLoop* l, TopoDS_Wire& result) {
IfcSchema::IfcOrientedEdge::list::ptr li = l->EdgeList();
BRepBuilderAPI_MakeWire mw;
for (IfcSchema::IfcOrientedEdge::list::it it = li->begin(); it != li->end(); ++it) {
TopoDS_Wire w;
if (convert_wire(*it, w)) {
mw.Add(TopoDS::Edge(TopoDS_Iterator(w).Value()));
}
}
if (!mw.IsDone()) {
return false;
}
result = mw.Wire();
return true;
}
bool IfcGeom::Kernel::convert(const IfcSchema::IfcEdge* l, TopoDS_Wire& result) {
if (!l->EdgeStart()->declaration().is(IfcSchema::IfcVertexPoint::Class()) || !l->EdgeEnd()->declaration().is(IfcSchema::IfcVertexPoint::Class())) {
Logger::Message(Logger::LOG_ERROR, "Only IfcVertexPoints are supported for EdgeStart and -End", l);
return false;
}
IfcSchema::IfcPoint* pnt1 = ((IfcSchema::IfcVertexPoint*) l->EdgeStart())->VertexGeometry();
IfcSchema::IfcPoint* pnt2 = ((IfcSchema::IfcVertexPoint*) l->EdgeEnd())->VertexGeometry();
if (!pnt1->declaration().is(IfcSchema::IfcCartesianPoint::Class()) || !pnt2->declaration().is(IfcSchema::IfcCartesianPoint::Class())) {
Logger::Message(Logger::LOG_ERROR, "Only IfcCartesianPoints are supported for VertexGeometry", l);
return false;
}
gp_Pnt p1, p2;
if (!convert(((IfcSchema::IfcCartesianPoint*)pnt1), p1) ||
!convert(((IfcSchema::IfcCartesianPoint*)pnt2), p2))
{
return false;
}
BRepBuilderAPI_MakeWire mw;
mw.Add(BRepBuilderAPI_MakeEdge(p1, p2));
result = mw.Wire();
return true;
}
bool IfcGeom::Kernel::convert(const IfcSchema::IfcOrientedEdge* l, TopoDS_Wire& result) {
if (convert_wire(l->EdgeElement(), result)) {
if (!l->Orientation()) {
result.Reverse();
}
return true;
} else {
return false;
}
}
bool IfcGeom::Kernel::convert(const IfcSchema::IfcSubedge* l, TopoDS_Wire& result) {
TopoDS_Wire temp;
if (convert_wire(l->ParentEdge(), result) && convert((IfcSchema::IfcEdge*) l, temp)) {
TopExp_Explorer exp(result, TopAbs_EDGE);
TopoDS_Edge edge = TopoDS::Edge(exp.Current());
Standard_Real u1, u2;
Handle(Geom_Curve) crv = BRep_Tool::Curve(edge, u1, u2);
TopoDS_Vertex v1, v2;
TopExp::Vertices(temp, v1, v2);
BRepBuilderAPI_MakeWire mw;
mw.Add(BRepBuilderAPI_MakeEdge(crv, v1, v2));
result = mw.Wire();
return true;
} else {
return false;
}
}
#ifdef SCHEMA_HAS_IfcIndexedPolyCurve
bool IfcGeom::Kernel::convert(const IfcSchema::IfcIndexedPolyCurve* l, TopoDS_Wire& result) {
IfcSchema::IfcCartesianPointList* point_list = l->Points();
std::vector< std::vector<double> > coordinates;
if (point_list->as<IfcSchema::IfcCartesianPointList2D>()) {
coordinates = point_list->as<IfcSchema::IfcCartesianPointList2D>()->CoordList();
} else if (point_list->as<IfcSchema::IfcCartesianPointList3D>()) {
coordinates = point_list->as<IfcSchema::IfcCartesianPointList3D>()->CoordList();
}
std::vector<gp_Pnt> points;
points.reserve(coordinates.size());
for (std::vector< std::vector<double> >::const_iterator it = coordinates.begin(); it != coordinates.end(); ++it) {
const std::vector<double>& coords = *it;
points.push_back(gp_Pnt(
coords.size() < 1 ? 0. : coords[0] * getValue(GV_LENGTH_UNIT),
coords.size() < 2 ? 0. : coords[1] * getValue(GV_LENGTH_UNIT),
coords.size() < 3 ? 0. : coords[2] * getValue(GV_LENGTH_UNIT)));
}
int max_index = points.size();
BRepBuilderAPI_MakeWire w;
if(l->hasSegments()) {
IfcEntityList::ptr segments = l->Segments();
for (IfcEntityList::it it = segments->begin(); it != segments->end(); ++it) {
IfcUtil::IfcBaseClass* segment = *it;
if (segment->declaration().is(IfcSchema::IfcLineIndex::Class())) {
IfcSchema::IfcLineIndex* line = (IfcSchema::IfcLineIndex*) segment;
std::vector<int> indices = *line;
gp_Pnt previous;
for (std::vector<int>::const_iterator jt = indices.begin(); jt != indices.end(); ++jt) {
if (*jt < 1 || *jt > max_index) {
throw IfcParse::IfcException("IfcIndexedPolyCurve index out of bounds for index " + boost::lexical_cast<std::string>(*jt));
}
const gp_Pnt& current = points[*jt - 1];
if (jt != indices.begin()) {
w.Add(BRepBuilderAPI_MakeEdge(previous, current));
}
previous = current;
}
} else if (segment->declaration().is(IfcSchema::IfcArcIndex::Class())) {
IfcSchema::IfcArcIndex* arc = (IfcSchema::IfcArcIndex*) segment;
std::vector<int> indices = *arc;
if (indices.size() != 3) {
throw IfcParse::IfcException("Invalid IfcArcIndex encountered");
}
for (int i = 0; i < 3; ++i) {
const int& idx = indices[i];
if (idx < 1 || idx > max_index) {
throw IfcParse::IfcException("IfcIndexedPolyCurve index out of bounds for index " + boost::lexical_cast<std::string>(idx));
}
}
const gp_Pnt& a = points[indices[0] - 1];
const gp_Pnt& b = points[indices[1] - 1];
const gp_Pnt& c = points[indices[2] - 1];
Handle(Geom_Circle) circ = GC_MakeCircle(a, b, c).Value();
w.Add(BRepBuilderAPI_MakeEdge(circ, a, c));
} else {
throw IfcParse::IfcException("Unexpected IfcIndexedPolyCurve segment of type " + segment->declaration().name());
}
}
} else if (points.begin() < points.end()) {
std::vector<gp_Pnt>::const_iterator previous = points.begin();
for (std::vector<gp_Pnt>::const_iterator current = previous+1; current < points.end(); ++current){
w.Add(BRepBuilderAPI_MakeEdge(*previous, *current));
previous = current;
}
}
result = w.Wire();
return true;
}
#endif
-123
View File
@@ -1,123 +0,0 @@
/********************************************************************************
* *
* 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/>. *
* *
********************************************************************************/
#include "IfcGeom.h"
#include "IfcGeomShapeType.h"
#define Kernel MAKE_TYPE_NAME(Kernel)
using namespace IfcUtil;
bool IfcGeom::Kernel::convert_shapes(const IfcBaseClass* l, IfcRepresentationShapeItems& r) {
if (shape_type(l) != ST_SHAPELIST) {
TopoDS_Shape shp;
if (convert_shape(l, shp)) {
r.push_back(IfcGeom::IfcRepresentationShapeItem(l->data().id(), shp, get_style(l->as<IfcSchema::IfcRepresentationItem>())));
return true;
}
return false;
}
#include "IfcRegisterConvertShapes.h"
Logger::Message(Logger::LOG_ERROR,"No operation defined for:",l);
return false;
}
IfcGeom::ShapeType IfcGeom::Kernel::shape_type(const IfcBaseClass* l) {
#include "IfcRegisterShapeType.h"
return ST_OTHER;
}
bool IfcGeom::Kernel::convert_shape(const IfcBaseClass* l, TopoDS_Shape& r) {
const unsigned int id = l->data().id();
bool success = false;
bool processed = false;
bool ignored = false;
#ifndef NO_CACHE
std::map<int,TopoDS_Shape>::const_iterator it = cache.Shape.find(id);
if ( it != cache.Shape.end() ) { r = it->second; return true; }
#endif
const bool include_curves = getValue(GV_DIMENSIONALITY) != +1;
const bool include_solids_and_surfaces = getValue(GV_DIMENSIONALITY) != -1;
IfcGeom::ShapeType st = shape_type(l);
ignored = (!include_solids_and_surfaces && (st == ST_SHAPE || st == ST_FACE)) || (!include_curves && (st == ST_WIRE || st == ST_CURVE));
if (st == ST_SHAPELIST) {
processed = true;
IfcRepresentationShapeItems items;
success = convert_shapes(l, items) && flatten_shape_list(items, r, false);
} else if (st == ST_SHAPE && include_solids_and_surfaces) {
#include "IfcRegisterConvertShape.h"
} else if (st == ST_FACE && include_solids_and_surfaces) {
processed = true;
success = convert_face(l, r);
} else if (st == ST_WIRE && include_curves) {
processed = true;
TopoDS_Wire w;
success = convert_wire(l, w);
if (success) {
r = w;
}
} else if (st == ST_CURVE && include_curves) {
processed = true;
Handle(Geom_Curve) crv;
TopoDS_Wire w;
success = convert_curve(l, crv) && convert_curve_to_wire(crv, w);
if (success) {
r = w;
}
}
if ( processed && success ) {
const double precision = getValue(GV_PRECISION);
apply_tolerance(r, precision);
#ifndef NO_CACHE
cache.Shape[id] = r;
#endif
} else if (!ignored) {
const char* const msg = processed
? "Failed to convert:"
: "No operation defined for:";
Logger::Message(Logger::LOG_ERROR, msg, l);
}
return success;
}
bool IfcGeom::Kernel::convert_wire(const IfcBaseClass* l, TopoDS_Wire& r) {
#include "IfcRegisterConvertWire.h"
Handle(Geom_Curve) curve;
if (IfcGeom::Kernel::convert_curve(l, curve)) {
return IfcGeom::Kernel::convert_curve_to_wire(curve, r);
}
Logger::Message(Logger::LOG_ERROR,"No operation defined for:",l);
return false;
}
bool IfcGeom::Kernel::convert_face(const IfcBaseClass* l, TopoDS_Shape& r) {
#include "IfcRegisterConvertFace.h"
Logger::Message(Logger::LOG_ERROR,"No operation defined for:",l);
return false;
}
bool IfcGeom::Kernel::convert_curve(const IfcBaseClass* l, Handle(Geom_Curve)& r) {
#include "IfcRegisterConvertCurve.h"
Logger::Message(Logger::LOG_ERROR,"No operation defined for:",l);
return false;
}
-148
View File
@@ -1,148 +0,0 @@
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* Lesser GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
/********************************************************************************
* *
* This file registers function prototypes for all supported IFC geometrical *
* entities. For entities of type CLASS an std::map is also created to cache *
* the output of the conversion functions *
* *
********************************************************************************/
#include <TopoDS_Shape.hxx>
#include <TopoDS_Wire.hxx>
#include <TopoDS_Face.hxx>
#include <gp_Pnt.hxx>
#include <gp_Pln.hxx>
#include <gp_Dir.hxx>
#include <gp_Mat.hxx>
#include <gp_Mat2d.hxx>
#include <gp_GTrsf.hxx>
#include <gp_GTrsf2d.hxx>
#include <gp_Trsf.hxx>
#include <gp_Trsf2d.hxx>
#include "../ifcparse/IfcBaseClass.h"
#include "../ifcparse/IfcParse.h"
SHAPES(IfcShellBasedSurfaceModel);
SHAPES(IfcFaceBasedSurfaceModel);
SHAPES(IfcRepresentation);
SHAPES(IfcMappedItem);
// IfcFacetedBrep included
// IfcAdvancedBrep included
// IfcFacetedBrepWithVoids included
// IfcAdvancedBrepWithVoids included
SHAPES(IfcManifoldSolidBrep);
SHAPES(IfcGeometricSet);
#ifdef SCHEMA_HAS_IfcCylindricalSurface
SHAPE(IfcCylindricalSurface);
#endif
#ifdef SCHEMA_HAS_IfcAdvancedBrep
SHAPE(IfcAdvancedBrep);
#endif
// FIXME: Surfaces should have a shape type of their own
#ifdef SCHEMA_HAS_IfcBSplineSurfaceWithKnots
SHAPE(IfcBSplineSurfaceWithKnots);
#endif
#ifdef SCHEMA_HAS_IfcTriangulatedFaceSet
SHAPE(IfcTriangulatedFaceSet);
#endif
#ifdef SCHEMA_HAS_IfcExtrudedAreaSolidTapered
SHAPE(IfcExtrudedAreaSolidTapered);
#endif
SHAPE(IfcPlane);
SHAPE(IfcExtrudedAreaSolid);
SHAPE(IfcRevolvedAreaSolid);
SHAPE(IfcConnectedFaceSet);
SHAPE(IfcBooleanResult);
SHAPE(IfcPolygonalBoundedHalfSpace);
SHAPE(IfcHalfSpaceSolid);
// FIXME: Surfaces should have a shape type of their own
SHAPE(IfcSurfaceOfLinearExtrusion);
SHAPE(IfcSurfaceOfRevolution);
SHAPE(IfcBlock);
SHAPE(IfcRectangularPyramid);
SHAPE(IfcRightCircularCylinder);
SHAPE(IfcRightCircularCone);
SHAPE(IfcSphere);
SHAPE(IfcCsgSolid);
SHAPE(IfcCurveBoundedPlane);
SHAPE(IfcRectangularTrimmedSurface);
SHAPE(IfcSurfaceCurveSweptAreaSolid);
SHAPE(IfcSweptDiskSolid);
FACE(IfcArbitraryProfileDefWithVoids);
FACE(IfcArbitraryClosedProfileDef);
FACE(IfcRoundedRectangleProfileDef);
FACE(IfcRectangleHollowProfileDef);
FACE(IfcRectangleProfileDef);
FACE(IfcTrapeziumProfileDef)
FACE(IfcCShapeProfileDef);
// IfcAsymmetricIShapeProfileDef included
FACE(IfcIShapeProfileDef);
FACE(IfcLShapeProfileDef);
FACE(IfcTShapeProfileDef);
FACE(IfcUShapeProfileDef);
FACE(IfcZShapeProfileDef);
FACE(IfcCircleHollowProfileDef);
FACE(IfcCircleProfileDef);
FACE(IfcEllipseProfileDef);
FACE(IfcCenterLineProfileDef);
FACE(IfcCompositeProfileDef);
FACE(IfcDerivedProfileDef);
// IfcFaceSurface included
// IfcAdvancedFace included in case of IFC4
FACE(IfcFace);
WIRE(IfcEdgeCurve);
WIRE(IfcSubedge);
WIRE(IfcOrientedEdge);
WIRE(IfcEdge);
WIRE(IfcEdgeLoop);
WIRE(IfcPolyline);
WIRE(IfcPolyLoop);
WIRE(IfcCompositeCurve);
WIRE(IfcTrimmedCurve);
WIRE(IfcArbitraryOpenProfileDef);
#ifdef SCHEMA_HAS_IfcIndexedPolyCurve
WIRE(IfcIndexedPolyCurve)
#endif
CURVE(IfcCircle);
CURVE(IfcEllipse);
CURVE(IfcLine);
#ifdef SCHEMA_HAS_IfcBSplineCurveWithKnots
// IfcRationalBSplineCurveWithKnots included
CURVE(IfcBSplineCurveWithKnots);
#endif
CLASS(IfcCartesianPoint,gp_Pnt);
CLASS(IfcDirection,gp_Dir);
CLASS(IfcAxis2Placement2D,gp_Trsf2d);
CLASS(IfcAxis2Placement3D,gp_Trsf);
CLASS(IfcAxis1Placement,gp_Ax1);
CLASS(IfcCartesianTransformationOperator2DnonUniform,gp_GTrsf2d);
CLASS(IfcCartesianTransformationOperator3DnonUniform,gp_GTrsf);
CLASS(IfcCartesianTransformationOperator2D,gp_Trsf2d);
CLASS(IfcCartesianTransformationOperator3D,gp_Trsf);
CLASS(IfcObjectPlacement,gp_Trsf);
CLASS(IfcVector,gp_Vec);
CLASS(IfcPlane,gp_Pln);
-6
View File
@@ -1,6 +0,0 @@
#include "IfcRegisterUndef.h"
#define CURVE(T) \
if ( l->declaration().is(IfcSchema::T::Class()) ) return convert((IfcSchema::T*)l,r);
#include "IfcRegisterDef.h"
#include "IfcRegister.h"
-6
View File
@@ -1,6 +0,0 @@
#include "IfcRegisterUndef.h"
#define FACE(T) \
if ( l->declaration().is(IfcSchema::T::Class()) ) return convert((IfcSchema::T*)l,r);
#include "IfcRegisterDef.h"
#include "IfcRegister.h"
-26
View File
@@ -1,26 +0,0 @@
#include "IfcRegisterUndef.h"
#define SHAPE(T) \
if ( !processed && l->declaration().is(IfcSchema::T::Class()) ) { \
processed = true; \
try { \
if ( convert((IfcSchema::T*)l,r) ) { \
success = true; \
} \
} catch (const std::exception& e) { \
Logger::Message(Logger::LOG_ERROR, std::string(e.what()) + "\nFailed to convert:", l); \
return false; \
} catch (const Standard_Failure& f) { \
if (f.GetMessageString() && strlen(f.GetMessageString())) \
Logger::Message(Logger::LOG_ERROR, std::string("Error in: ") + f.GetMessageString() + "\nFailed to convert:", l); \
else \
Logger::Message(Logger::LOG_ERROR, "Failed to convert:", l); \
return false; \
} \
if (!success) { \
Logger::Message(Logger::LOG_ERROR,"Failed to convert:",l); \
return false; \
} \
}
#include "IfcRegisterDef.h"
#include "IfcRegister.h"
-18
View File
@@ -1,18 +0,0 @@
#include "IfcRegisterUndef.h"
#define SHAPES(T) \
if ( l->declaration().is(IfcSchema::T::Class()) ) { \
try { \
return convert((IfcSchema::T*)l,r); \
} catch (const std::exception& e) { \
Logger::Message(Logger::LOG_ERROR, std::string(e.what()) + "\nFailed to convert:", l); \
} catch (const Standard_Failure& f) { \
if (f.GetMessageString()) \
Logger::Message(Logger::LOG_ERROR, std::string("Error in: ") + f.GetMessageString() + "\nFailed to convert:", l); \
else \
Logger::Message(Logger::LOG_ERROR, "Failed to convert:", l); \
} \
return false; \
}
#include "IfcRegisterDef.h"
#include "IfcRegister.h"
-6
View File
@@ -1,6 +0,0 @@
#include "IfcRegisterUndef.h"
#define WIRE(T) \
if ( l->declaration().is(IfcSchema::T::Class()) ) return convert((IfcSchema::T*)l,r);
#include "IfcRegisterDef.h"
#include "IfcRegister.h"
-6
View File
@@ -1,6 +0,0 @@
#include "IfcRegisterUndef.h"
#define CLASS(T,V) \
std::map<int,V> T;
#include "IfcRegisterDef.h"
#include "IfcRegister.h"
-18
View File
@@ -1,18 +0,0 @@
#ifndef SHAPES
#define SHAPES(T)
#endif
#ifndef SHAPE
#define SHAPE(T)
#endif
#ifndef WIRE
#define WIRE(T)
#endif
#ifndef FACE
#define FACE(T)
#endif
#ifndef CURVE
#define CURVE(T)
#endif
#ifndef CLASS
#define CLASS(T,V)
#endif
-10
View File
@@ -1,10 +0,0 @@
#include "IfcRegisterUndef.h"
#define CLASS(T,V) bool convert(const IfcSchema::T* L, V& r);
#define SHAPES(T) CLASS(T,IfcRepresentationShapeItems)
#define SHAPE(T) CLASS(T,TopoDS_Shape)
#define WIRE(T) CLASS(T,TopoDS_Wire)
#define FACE(T) CLASS(T,TopoDS_Shape)
#define CURVE(T) CLASS(T,Handle(Geom_Curve))
#include "IfcRegisterDef.h"
#include "IfcRegister.h"
-6
View File
@@ -1,6 +0,0 @@
#include "IfcRegisterUndef.h"
#define CLASS(T,V) \
T.clear();
#include "IfcRegisterDef.h"
#include "IfcRegister.h"
-14
View File
@@ -1,14 +0,0 @@
#include "IfcRegisterUndef.h"
#define SHAPES(T) \
if ( l->declaration().is(IfcSchema::T::Class()) ) return ST_SHAPELIST;
#define SHAPE(T) \
if ( l->declaration().is(IfcSchema::T::Class()) ) return ST_SHAPE;
#define WIRE(T) \
if ( l->declaration().is(IfcSchema::T::Class()) ) return ST_WIRE;
#define FACE(T) \
if ( l->declaration().is(IfcSchema::T::Class()) ) return ST_FACE;
#define CURVE(T) \
if ( l->declaration().is(IfcSchema::T::Class()) ) return ST_CURVE;
#include "IfcRegisterDef.h"
#include "IfcRegister.h"
-18
View File
@@ -1,18 +0,0 @@
#ifdef SHAPES
#undef SHAPES
#endif
#ifdef SHAPE
#undef SHAPE
#endif
#ifdef WIRE
#undef WIRE
#endif
#ifdef FACE
#undef FACE
#endif
#ifdef CURVE
#undef CURVE
#endif
#ifdef CLASS
#undef CLASS
#endif
-55
View File
@@ -1,55 +0,0 @@
/********************************************************************************
* *
* 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/>. *
* *
********************************************************************************/
#ifndef IFCSHAPELIST_H
#define IFCSHAPELIST_H
#include <gp_GTrsf.hxx>
#include <TopoDS_Shape.hxx>
#include "../ifcgeom_schema_agnostic/IfcGeomRenderStyles.h"
namespace IfcGeom {
class IFC_GEOM_API IfcRepresentationShapeItem {
private:
int id;
gp_GTrsf placement;
TopoDS_Shape shape;
const SurfaceStyle* style;
public:
IfcRepresentationShapeItem(int id, const gp_GTrsf& placement, const TopoDS_Shape& shape, const SurfaceStyle* style)
: id(id), placement(placement), shape(shape), style(style) {}
IfcRepresentationShapeItem(int id, const gp_GTrsf& placement, const TopoDS_Shape& shape)
: id(id), placement(placement), shape(shape), style(0) {}
IfcRepresentationShapeItem(int id, const TopoDS_Shape& shape, const SurfaceStyle* style)
: id(id), shape(shape), style(style) {}
IfcRepresentationShapeItem(int id, const TopoDS_Shape& shape)
: id(id), shape(shape), style(0) {}
void append(const gp_GTrsf& trsf) { placement.Multiply(trsf); }
void prepend(const gp_GTrsf& trsf) { placement.PreMultiply(trsf); }
const TopoDS_Shape& Shape() const { return shape; }
const gp_GTrsf& Placement() const { return placement; }
bool hasStyle() const { return style != 0; }
const SurfaceStyle& Style() const { return *style; }
void setStyle(const SurfaceStyle* newStyle) { style = newStyle; }
int ItemId() const { return id; }
};
typedef std::vector<IfcRepresentationShapeItem> IfcRepresentationShapeItems;
}
#endif
+56
View File
@@ -0,0 +1,56 @@
#include "abstract_mapping.h"
#include "../ifcparse/IfcFile.h"
ifcopenshell::geometry::impl::MappingFactoryImplementation& ifcopenshell::geometry::impl::mapping_implementations() {
static MappingFactoryImplementation impl;
return impl;
}
extern void init_MappingImplementation_Ifc2x3(ifcopenshell::geometry::impl::MappingFactoryImplementation*);
extern void init_MappingImplementation_Ifc4(ifcopenshell::geometry::impl::MappingFactoryImplementation*);
// extern void init_MappingImplementation_Ifc4x1(ifcopenshell::geometry::impl::MappingFactoryImplementation*);
// extern void init_MappingImplementation_Ifc4x2(ifcopenshell::geometry::impl::MappingFactoryImplementation*);
ifcopenshell::geometry::impl::MappingFactoryImplementation::MappingFactoryImplementation() {
init_MappingImplementation_Ifc2x3(this);
init_MappingImplementation_Ifc4(this);
// init_MappingImplementation_Ifc4x1(this);
// init_MappingImplementation_Ifc4x2(this);
}
void ifcopenshell::geometry::impl::MappingFactoryImplementation::bind(const std::string& schema_name, ifcopenshell::geometry::impl::mapping_fn fn) {
const std::string schema_name_lower = boost::to_lower_copy(schema_name);
this->insert(std::make_pair(schema_name_lower, fn));
}
ifcopenshell::geometry::abstract_mapping* ifcopenshell::geometry::impl::MappingFactoryImplementation::construct(IfcParse::IfcFile* file, settings& s) {
const std::string schema_name_lower = boost::to_lower_copy(file->schema()->name());
std::map<std::string, ifcopenshell::geometry::impl::mapping_fn>::const_iterator it;
it = this->find(schema_name_lower);
if (it == end()) {
throw IfcParse::IfcException("No geometry mapping registered for " + schema_name_lower);
}
return it->second(file, s);
}
void ifcopenshell::geometry::remove_duplicate_points_from_loop(std::vector<taxonomy::point3>& polygon, bool closed, double tol) {
for (;;) {
bool removed = false;
int n = polygon.size() - (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.size()) + 1;
double dist = (polygon.at(i - 1).components() - polygon.at(j - 1).components()).squaredNorm();
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.erase(polygon.begin() + i - 1);
else polygon.erase(polygon.begin() + j - 1);
removed = true;
break;
}
}
if (!removed) break;
}
}
+56
View File
@@ -0,0 +1,56 @@
#ifndef ABSTRACT_MAPPING_H
#define ABSTRACT_MAPPING_H
#include "../ifcparse/IfcBaseClass.h"
#include "../ifcparse/IfcEntityList.h"
#include "../ifcgeom/taxonomy.h"
#include "../ifcgeom/settings.h"
#include <boost/function.hpp>
#include <map>
#include <string>
namespace ifcopenshell {
namespace geometry {
struct geometry_conversion_task {
int index;
IfcUtil::IfcBaseEntity* representation;
IfcEntityList::ptr products;
};
typedef boost::function<bool(IfcUtil::IfcBaseEntity*)> filter_t;
class abstract_mapping {
protected:
settings settings_;
public:
abstract_mapping(settings& s) : settings_(s) {}
virtual ifcopenshell::geometry::taxonomy::item* map(const IfcUtil::IfcBaseClass*) = 0;
virtual void get_representations(std::vector<geometry_conversion_task>& tasks, std::vector<filter_t>& filters, settings& s) = 0;
virtual IfcUtil::IfcBaseEntity* get_decomposing_entity(IfcUtil::IfcBaseEntity* product, bool include_openings = true) = 0;
virtual std::map<std::string, IfcUtil::IfcBaseEntity*> get_layers(IfcUtil::IfcBaseEntity*) = 0;
};
namespace impl {
typedef boost::function2<abstract_mapping*, IfcParse::IfcFile*, settings&> mapping_fn;
class MappingFactoryImplementation : public std::map<std::string, mapping_fn> {
public:
MappingFactoryImplementation();
void bind(const std::string& schema_name, mapping_fn);
abstract_mapping* construct(IfcParse::IfcFile*, settings&);
};
MappingFactoryImplementation& mapping_implementations();
}
void remove_duplicate_points_from_loop(std::vector<taxonomy::point3>& polygon, bool closed, double tol);
}
}
#endif
+29
View File
@@ -0,0 +1,29 @@
#pragma once
#include <string>
#include <exception>
#include "schema_agnostic/ifc_geom_api.h"
namespace ifcopenshell {
namespace geometry {
class IFC_GEOM_API geometry_exception : public std::exception {
protected:
std::string message;
public:
geometry_exception(const std::string& m)
: message(m) {}
virtual ~geometry_exception() throw () {}
virtual const char* what() const throw() {
return message.c_str();
}
};
class IFC_GEOM_API too_many_faces_exception : public geometry_exception {
public:
too_many_faces_exception()
: geometry_exception("Too many faces for operation") {}
};
}
}
@@ -0,0 +1,42 @@
#include "AbstractKernel.h"
#include "../../ifcgeom/schema_agnostic/IfcGeomElement.h"
#include "../../ifcgeom/kernels/opencascade/OpenCascadeKernel.h"
#undef Handle
#include "../../ifcgeom/kernels/cgal/CgalKernel.h"
bool ifcopenshell::geometry::kernels::AbstractKernel::convert(const taxonomy::item* item, ifcopenshell::geometry::ConversionResults& results) {
try {
return dispatch_conversion<0>::dispatch(this, item, results);
} catch (std::exception& e) {
Logger::Error(e, item->instance);
return false;
}
}
ifcopenshell::geometry::kernels::AbstractKernel* ifcopenshell::geometry::kernels::construct(const std::string& geometry_library, IfcParse::IfcFile* file) {
const std::string geometry_library_lower = boost::to_lower_copy(geometry_library);
if (geometry_library_lower == "opencascade") {
return new OpenCascadeKernel(settings);
} else if (geometry_library_lower == "cgal") {
return new CgalKernel(settings);
} else {
throw IfcParse::IfcException("No geometry kernel registered for " + geometry_library);
}
}
bool ifcopenshell::geometry::kernels::AbstractKernel::convert_impl(const taxonomy::collection* collection, ifcopenshell::geometry::ConversionResults& r) {
auto s = r.size();
for (auto& c : collection->children) {
convert(c, r);
}
for (auto i = s; i < r.size(); ++i) {
r[i].prepend(collection->matrix);
if (!r[i].hasStyle()) {
r[i].setStyle(collection->surface_style);
}
}
return r.size() > s;
}
@@ -0,0 +1,106 @@
#ifndef ABSTRACT_KERNEL_H
#define ABSTRACT_KERNEL_H
#include "../../ifcparse/macros.h"
#include "../../ifcgeom/schema_agnostic/ifc_geom_api.h"
#include "../../ifcgeom/schema_agnostic/IfcGeomRepresentation.h"
#include "../../ifcgeom/taxonomy.h"
#include "../../ifcgeom/ConversionSettings.h"
static const double ALMOST_ZERO = 1.e-9;
template <typename T>
inline static bool ALMOST_THE_SAME(const T& a, const T& b, double tolerance = ALMOST_ZERO) {
return fabs(a - b) < tolerance;
}
namespace ifcopenshell { namespace geometry { namespace kernels {
class IFC_GEOM_API AbstractKernel {
protected:
// For stopping PlacementRelTo recursion in convert(const IfcSchema::IfcObjectPlacement* l, gp_Trsf& trsf)
const IfcParse::declaration* placement_rel_to = nullptr;
std::string geometry_library_;
ConversionSettings settings_;
public:
AbstractKernel(const std::string& geometry_library, const ConversionSettings& settings)
: geometry_library_(geometry_library)
, settings_(settings)
{}
bool convert(const taxonomy::item*, ifcopenshell::geometry::ConversionResults&);
virtual bool convert_impl(const taxonomy::matrix4*, ifcopenshell::geometry::ConversionResults&) { throw std::runtime_error("Not implemented"); }
virtual bool convert_impl(const taxonomy::point3*, ifcopenshell::geometry::ConversionResults&) { throw std::runtime_error("Not implemented"); }
virtual bool convert_impl(const taxonomy::direction3*, ifcopenshell::geometry::ConversionResults&) { throw std::runtime_error("Not implemented"); }
virtual bool convert_impl(const taxonomy::line*, ifcopenshell::geometry::ConversionResults&) { throw std::runtime_error("Not implemented"); }
virtual bool convert_impl(const taxonomy::circle*, ifcopenshell::geometry::ConversionResults&) { throw std::runtime_error("Not implemented"); }
virtual bool convert_impl(const taxonomy::ellipse*, ifcopenshell::geometry::ConversionResults&) { throw std::runtime_error("Not implemented"); }
virtual bool convert_impl(const taxonomy::bspline_curve*, ifcopenshell::geometry::ConversionResults&) { throw std::runtime_error("Not implemented"); }
virtual bool convert_impl(const taxonomy::edge*, ifcopenshell::geometry::ConversionResults&) { throw std::runtime_error("Not implemented"); }
virtual bool convert_impl(const taxonomy::loop*, ifcopenshell::geometry::ConversionResults&) { throw std::runtime_error("Not implemented"); }
virtual bool convert_impl(const taxonomy::shell*, ifcopenshell::geometry::ConversionResults&) { throw std::runtime_error("Not implemented"); }
virtual bool convert_impl(const taxonomy::face*, ifcopenshell::geometry::ConversionResults&) { throw std::runtime_error("Not implemented"); }
virtual bool convert_impl(const taxonomy::extrusion*, ifcopenshell::geometry::ConversionResults&) { throw std::runtime_error("Not implemented"); }
virtual bool convert_impl(const taxonomy::node*, ifcopenshell::geometry::ConversionResults&) { throw std::runtime_error("Not implemented"); }
virtual bool convert_impl(const taxonomy::colour*, ifcopenshell::geometry::ConversionResults&) { throw std::runtime_error("Not implemented"); }
virtual bool convert_impl(const taxonomy::boolean_result*, ifcopenshell::geometry::ConversionResults&) { throw std::runtime_error("Not implemented"); }
virtual bool convert_impl(const taxonomy::plane*, ifcopenshell::geometry::ConversionResults&) { throw std::runtime_error("Not implemented"); }
virtual bool convert_impl(const taxonomy::collection*, ifcopenshell::geometry::ConversionResults&);
};
AbstractKernel* construct(const std::string& geometry_library, IfcParse::IfcFile*);
}
}
}
namespace {
/* A compile-time for loop over the taxonomy kinds */
template <size_t N>
struct dispatch_conversion {
static bool dispatch(ifcopenshell::geometry::kernels::AbstractKernel* kernel, const ifcopenshell::geometry::taxonomy::item* item, ifcopenshell::geometry::ConversionResults& results) {
if (N == item->kind()) {
auto concrete_item = static_cast<const ifcopenshell::geometry::taxonomy::type_by_kind::type<N>*>(item);
return kernel->convert_impl(concrete_item, results);
} else {
return dispatch_conversion<N + 1>::dispatch(kernel, item, results);
}
}
};
template <>
struct dispatch_conversion<ifcopenshell::geometry::taxonomy::type_by_kind::max> {
static bool dispatch(ifcopenshell::geometry::kernels::AbstractKernel*, const ifcopenshell::geometry::taxonomy::item* item, ifcopenshell::geometry::ConversionResults&) {
Logger::Error("No conversion for " + std::to_string(item->kind()));
return false;
}
};
/* A compile-time for loop over the curve kinds */
template <typename T, size_t N = 0>
struct dispatch_curve_creation {
static bool dispatch(const ifcopenshell::geometry::taxonomy::item* item, T& visitor) {
// @todo it should be possible to eliminate this dynamic_cast when there is a static equivalent to kind()
const ifcopenshell::geometry::taxonomy::curves::type<N>* v = dynamic_cast<const ifcopenshell::geometry::taxonomy::curves::type<N>*>(item);
if (v) {
visitor(*v);
return true;
} else {
return dispatch_curve_creation<T, N + 1>::dispatch(item, visitor);
}
}
};
template <typename T>
struct dispatch_curve_creation<T, ifcopenshell::geometry::taxonomy::curves::max> {
static bool dispatch(const ifcopenshell::geometry::taxonomy::item* item, T&) {
Logger::Error("No conversion for " + std::to_string(item->kind()));
return false;
}
};
}
#endif
@@ -0,0 +1,118 @@
#include "CgalKernel.h"
#include "../../../ifcgeom/schema_agnostic/cgal/CgalConversionResult.h"
#define CgalKernel MAKE_TYPE_NAME(CgalKernel)
bool IfcGeom::CgalKernel::convert_wire_to_face(const cgal_wire_t& wire, cgal_face_t& face) {
face.outer = wire;
return true;
}
void IfcGeom::CgalKernel::remove_duplicate_points_from_loop(cgal_wire_t& polygon) {
std::set<cgal_point_t> points;
for (int i = 0; i < polygon.size(); ++i) {
if (points.count(polygon[i])) {
polygon.erase(polygon.begin()+i);
--i;
} else points.insert(polygon[i]);
}
}
CGAL::Polyhedron_3<Kernel_> IfcGeom::CgalKernel::create_polyhedron(std::list<cgal_face_t> &face_list) {
// Naive creation
CGAL::Polyhedron_3<Kernel_> polyhedron;
PolyhedronBuilder builder(&face_list);
polyhedron.delegate(builder);
// Stitch edges
// std::cout << "Before: " << polyhedron.size_of_vertices() << " vertices and " << polyhedron.size_of_facets() << " facets" << std::endl;
CGAL::Polygon_mesh_processing::stitch_borders(polyhedron);
if (!polyhedron.is_valid()) {
Logger::Message(Logger::LOG_ERROR, "create_polyhedron: Polyhedron not valid!");
// std::ofstream fresult;
// fresult.open("/Users/ken/Desktop/invalid.off");
// fresult << polyhedron << std::endl;
// fresult.close();
return CGAL::Polyhedron_3<Kernel_>();
} if (polyhedron.is_closed()) {
if (!CGAL::Polygon_mesh_processing::is_outward_oriented(polyhedron)) {
CGAL::Polygon_mesh_processing::reverse_face_orientations(polyhedron);
}
}
// std::cout << "After: " << polyhedron.size_of_vertices() << " vertices and " << polyhedron.size_of_facets() << " facets" << std::endl;
return polyhedron;
}
CGAL::Polyhedron_3<Kernel_> IfcGeom::CgalKernel::create_polyhedron(CGAL::Nef_polyhedron_3<Kernel_> &nef_polyhedron) {
if (nef_polyhedron.is_simple()) {
try {
CGAL::Polyhedron_3<Kernel_> polyhedron;
nef_polyhedron.convert_to_polyhedron(polyhedron);
return polyhedron;
} catch (...) {
Logger::Message(Logger::LOG_ERROR, "Conversion from Nef to polyhedron failed!");
return CGAL::Polyhedron_3<Kernel_>();
}
} else {
Logger::Message(Logger::LOG_ERROR, "Nef polyhedron not simple: cannot create polyhedron!");
return CGAL::Polyhedron_3<Kernel_>();
}
}
CGAL::Nef_polyhedron_3<Kernel_> IfcGeom::CgalKernel::create_nef_polyhedron(std::list<cgal_face_t> &face_list) {
CGAL::Polyhedron_3<Kernel_> polyhedron = create_polyhedron(face_list);
CGAL::Polygon_mesh_processing::triangulate_faces(polyhedron);
CGAL::Nef_polyhedron_3<Kernel_> nef_polyhedron;
try {
nef_polyhedron = CGAL::Nef_polyhedron_3<Kernel_>(polyhedron);
} catch (...) {
Logger::Message(Logger::LOG_ERROR, "Conversion to Nef polyhedron failed!");
return nef_polyhedron;
} return nef_polyhedron;
}
CGAL::Nef_polyhedron_3<Kernel_> IfcGeom::CgalKernel::create_nef_polyhedron(CGAL::Polyhedron_3<Kernel_> &polyhedron) {
if (polyhedron.is_valid()) {
CGAL::Polygon_mesh_processing::triangulate_faces(polyhedron);
CGAL::Nef_polyhedron_3<Kernel_> nef_polyhedron;
try {
nef_polyhedron = CGAL::Nef_polyhedron_3<Kernel_>(polyhedron);
} catch (...) {
Logger::Message(Logger::LOG_ERROR, "Conversion to Nef polyhedron failed!");
return nef_polyhedron;
} return nef_polyhedron;
} else {
Logger::Message(Logger::LOG_ERROR, "Polyhedron not valid: cannot create Nef polyhedron!");
return CGAL::Nef_polyhedron_3<Kernel_>();
}
}
//CGAL::Polyhedron_3<Kernel_> IfcGeom::CgalKernel::triangulate_faces(CGAL::Polyhedron_3<Kernel_> &polyhedron) {
// std::list<cgal_face_t> face_list;
//
// for (CGAL::Polyhedron_3<Kernel_>::Facet_const_iterator current_facet = polyhedron.facets_begin();
// current_facet != polyhedron.facets_end();
// ++current_facet) {
//
// // Triangle
// if (current_facet->is_triangle()) {
// face_list.push_back(cgal_face_t());
// CGAL::Polyhedron_3<Kernel_>::Halfedge_around_facet_const_circulator current_halfedge = current_facet->facet_begin();
// do {
// face_list.back().outer.push_back(current_halfedge->vertex()->point());
// ++current_halfedge;
// } while (current_halfedge != current_facet->facet_begin());
// }
//
// // Polygon
// else {
// std::list<Kernel_::Point_3> points_in_polygon;
//
// }
// }
//
// return create_polyhedron(face_list);
//}
@@ -0,0 +1,125 @@
#include "CgalConversionResult.h"
#include "CgalKernel.h"
#include "../../../ifcparse/IfcLogger.h"
#include "../../../ifcgeom/schema_agnostic/IfcGeomRepresentation.h"
void ifcopenshell::geometry::CgalShape::Triangulate(const settings& settings, const ifcopenshell::geometry::taxonomy::matrix4& place, Representation::Triangulation* t, int surface_style_id) const {
// Copy is made because triangulate_faces() does not accept a const argument
cgal_shape_t s = shape_;
if (!place.is_identity()) {
const auto& m = place.ccomponents();
// @todo check
const cgal_placement_t trsf(
m(0, 0), m(0, 1), m(0, 2), m(0, 3),
m(1, 0), m(1, 1), m(1, 2), m(1, 3),
m(2, 0), m(2, 1), m(2, 2), m(2, 3));
// Apply transformation
for (auto &vertex : vertices(s)) {
vertex->point() = vertex->point().transform(trsf);
}
}
if (!s.is_valid()) {
Logger::Message(Logger::LOG_ERROR, "Invalid Polyhedron_3 in object (before triangulation)");
return;
}
// Triangulate the shape and compute the normals
// std::map<cgal_vertex_descriptor_t, Kernel_::Vector_3> vertex_normals;
// boost::associative_property_map<std::map<cgal_vertex_descriptor_t, Kernel_::Vector_3>> vertex_normals_map(vertex_normals);
std::map<cgal_face_descriptor_t, Kernel_::Vector_3> face_normals;
boost::associative_property_map<std::map<cgal_face_descriptor_t, Kernel_::Vector_3>> face_normals_map(face_normals);
bool success = false;
try {
success = CGAL::Polygon_mesh_processing::triangulate_faces(s);
} catch (...) {
Logger::Message(Logger::LOG_ERROR, "Triangulation crashed");
return;
}
if (!success) {
Logger::Message(Logger::LOG_ERROR, "Triangulation failed");
return;
}
// std::cout << "Triangulated model: " << s.size_of_facets() << " facets and " << s.size_of_vertices() << " vertices" << std::endl;
if (!s.is_valid()) {
Logger::Message(Logger::LOG_ERROR, "Invalid Polyhedron_3 in object (after triangulation)");
return;
}
// CGAL::Polygon_mesh_processing::compute_normals(s, vertex_normals_map, face_normals_map);
try {
CGAL::Polygon_mesh_processing::compute_face_normals(s, face_normals_map);
} catch (...) {
Logger::Message(Logger::LOG_ERROR, "Face normal calculation failed");
return;
}
int num_faces = 0, num_vertices = 0;
for (auto &face: faces(s)) {
if (!face->is_triangle()) {
std::cout << "Warning: non-triangular face!" << std::endl;
continue;
}
CGAL::Polyhedron_3<Kernel_>::Halfedge_around_facet_const_circulator current_halfedge = face->facet_begin();
int vertexidx[3];
int i = 0;
do {
vertexidx[i++] = t->addVertex(surface_style_id,
CGAL::to_double(current_halfedge->vertex()->point().cartesian(0)),
CGAL::to_double(current_halfedge->vertex()->point().cartesian(1)),
CGAL::to_double(current_halfedge->vertex()->point().cartesian(2)));
double nx = 0.;
double ny = 0.;
double nz = 1.;
// @todo normal calculation throws divide by zero?
// try {
if (false) {
nx = CGAL::to_double(face_normals_map[face].cartesian(0));
ny = CGAL::to_double(face_normals_map[face].cartesian(1));
nz = CGAL::to_double(face_normals_map[face].cartesian(2));
}
// catch (...) {
// Logger::Error("Error during normal calculation");
// }
t->addNormal(nx, ny, nz);
++num_vertices;
++current_halfedge;
} while (current_halfedge != face->facet_begin());
t->addFace(surface_style_id, vertexidx[0], vertexidx[1], vertexidx[2]);
++num_faces;
}
}
#include <CGAL/Polygon_mesh_processing/bbox.h>
double ifcopenshell::geometry::CgalShape::bounding_box(void *& b) const {
if (b == nullptr) {
b = new CGAL::Bbox_3;
}
auto& bb = (*((CGAL::Bbox_3*)b));
bb += CGAL::Polygon_mesh_processing::bbox(shape_);
return (bb.xmax() - bb.xmin()) * (bb.ymax() - bb.ymin()) * (bb.zmax() - bb.zmin());
}
int ifcopenshell::geometry::CgalShape::num_vertices() const {
return shape_.size_of_vertices();
}
void ifcopenshell::geometry::CgalShape::set_box(void * b) {
auto& bb = (*((CGAL::Bbox_3*)b));
Kernel_::Point_3 lower(bb.xmin(), bb.ymin(), bb.zmin());
Kernel_::Point_3 upper(bb.xmax(), bb.ymax(), bb.zmax());
shape_ = ifcopenshell::geometry::utils::create_cube(lower, upper);
}
@@ -0,0 +1,97 @@
/********************************************************************************
* *
* 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/>. *
* *
********************************************************************************/
#ifndef CGALCONVERSIONRESULT_H
#define CGALCONVERSIONRESULT_H
#include "../../../ifcgeom/schema_agnostic/IfcGeomElement.h"
#include <boost/property_map/property_map.hpp>
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include <CGAL/Polyhedron_3.h>
#include <CGAL/boost/graph/graph_traits_Polyhedron_3.h>
#include <CGAL/Polygon_mesh_processing/stitch_borders.h>
#include <CGAL/Polygon_mesh_processing/orientation.h>
#include <CGAL/Polygon_mesh_processing/triangulate_faces.h>
#include <CGAL/Polygon_mesh_processing/compute_normal.h>
#include <CGAL/Polygon_mesh_processing/self_intersections.h>
#include <CGAL/Nef_polyhedron_3.h>
typedef CGAL::Exact_predicates_exact_constructions_kernel Kernel_;
typedef Kernel_::Aff_transformation_3 cgal_placement_t;
typedef Kernel_::Point_3 cgal_point_t;
typedef Kernel_::Vector_3 cgal_direction_t;
typedef Kernel_::Vector_3 cgal_vector_t;
typedef Kernel_::Plane_3 cgal_plane_t;
typedef std::vector<Kernel_::Point_3> cgal_curve_t;
typedef std::vector<Kernel_::Point_3> cgal_wire_t;
struct cgal_face_t {
cgal_wire_t outer;
std::vector<cgal_wire_t> inner;
};
typedef CGAL::Polyhedron_3<Kernel_> cgal_shape_t;
typedef boost::graph_traits<CGAL::Polyhedron_3<Kernel_>>::vertex_descriptor cgal_vertex_descriptor_t;
typedef boost::graph_traits<CGAL::Polyhedron_3<Kernel_>>::face_descriptor cgal_face_descriptor_t;
#include "../../../ifcgeom/schema_agnostic/ConversionResult.h"
namespace ifcopenshell { namespace geometry {
class CgalShape : public ConversionResultShape {
public:
CgalShape(const cgal_shape_t& shape)
: shape_(shape)
{}
const cgal_shape_t& shape() const { return shape_; }
operator const cgal_shape_t& () { return shape_; }
virtual void Triangulate(const settings& settings, const ifcopenshell::geometry::taxonomy::matrix4& place, Representation::Triangulation* t, int surface_style_id) const;
virtual void Serialize(std::string&) const {
throw std::runtime_error("Not implemented");
}
virtual ConversionResultShape* clone() const {
return new CgalShape(shape_);
}
virtual double bounding_box(void*& b) const;
virtual int num_vertices() const;
virtual void set_box(void* b);
virtual bool is_manifold() const {
throw std::runtime_error("Not implemented");
}
virtual int surface_genus() const {
throw std::runtime_error("Not implemented");
}
private:
cgal_shape_t shape_;
};
}}
#endif
@@ -0,0 +1,78 @@
#include "CgalKernel.h"
#include "../../../ifcgeom/schema_agnostic/cgal/CgalConversionResult.h"
#define CgalKernel MAKE_TYPE_NAME(CgalKernel)
bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcCircle* l, cgal_curve_t& curve) {
const double r = l->Radius() * getValue(GV_LENGTH_UNIT);
if ( r < ALMOST_ZERO ) {
Logger::Message(Logger::LOG_ERROR, "Radius not greater than zero for:", l);
return false;
}
cgal_placement_t trsf;
IfcSchema::IfcAxis2Placement* placement = l->Position();
if (placement->as<IfcSchema::IfcAxis2Placement3D>()) {
IfcGeom::CgalKernel::convert((IfcSchema::IfcAxis2Placement3D*)placement,trsf);
} else {
cgal_placement_t trsf2d;
IfcGeom::CgalKernel::convert((IfcSchema::IfcAxis2Placement2D*)placement,trsf2d);
trsf = trsf2d;
}
const int segments = 12;
curve = cgal_curve_t();
for (int current_segment = 0; current_segment < segments; ++current_segment) {
double current_angle = current_segment*2.0*3.141592653589793/((double)segments);
curve.push_back(Kernel_::Point_3(r*cos(current_angle), r*sin(current_angle), 0));
}
for (auto &vertex: curve) {
vertex = vertex.transform(trsf);
}
return true;
}
bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcEllipse* l, cgal_curve_t& curve) {
double x = l->SemiAxis1() * getValue(GV_LENGTH_UNIT);
double y = l->SemiAxis2() * getValue(GV_LENGTH_UNIT);
if (x < ALMOST_ZERO || y < ALMOST_ZERO) {
Logger::Message(Logger::LOG_ERROR, "Radius not greater than zero for:", l);
return false;
}
cgal_placement_t trsf;
IfcSchema::IfcAxis2Placement* placement = l->Position();
if (placement->as<IfcSchema::IfcAxis2Placement3D>()) {
convert((IfcSchema::IfcAxis2Placement3D*)placement,trsf);
} else {
cgal_placement_t trsf2d;
convert((IfcSchema::IfcAxis2Placement2D*)placement,trsf2d);
trsf = trsf2d;
}
const int segments = 12;
curve = cgal_curve_t();
for (int current_segment = 0; current_segment < segments; ++current_segment) {
double current_angle = current_segment*2.0*3.141592653589793/((double)segments);
curve.push_back(Kernel_::Point_3(x*cos(current_angle), y*sin(current_angle), 0));
}
for (auto &vertex: curve) {
vertex = vertex.transform(trsf);
}
return true;
}
bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcLine* l, cgal_curve_t& curve) {
cgal_point_t pnt;
cgal_direction_t vec;
convert(l->Pnt(),pnt);
convert(l->Dir(),vec);
curve = cgal_curve_t();
curve.push_back(pnt);
curve.push_back(pnt+vec);
return true;
}
@@ -0,0 +1,991 @@
#include "CgalKernel.h"
#include "../../../ifcgeom/schema_agnostic/cgal/CgalConversionResult.h"
#define CgalKernel MAKE_TYPE_NAME(CgalKernel)
bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcArbitraryClosedProfileDef* l, cgal_face_t& face) {
cgal_wire_t wire;
if ( ! convert_wire(l->OuterCurve(),wire) ) return false;
cgal_face_t f;
bool success = convert_wire_to_face(wire, f);
if (success) face = f;
return success;
}
bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcArbitraryProfileDefWithVoids* l, cgal_face_t& face) {
cgal_wire_t profile;
if ( ! convert_wire(l->OuterCurve(),profile) ) return false;
cgal_face_t mf;
mf.outer = profile;
IfcSchema::IfcCurve::list::ptr voids = l->InnerCurves();
for( IfcSchema::IfcCurve::list::it it = voids->begin(); it != voids->end(); ++ it ) {
cgal_wire_t hole;
if ( convert_wire(*it,hole) ) {
mf.inner.push_back(hole);
}
} face = mf;
return true;
}
bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcRectangleProfileDef* l, cgal_face_t& face) {
const double x = l->XDim() / 2.0f * getValue(GV_LENGTH_UNIT);
const double y = l->YDim() / 2.0f * getValue(GV_LENGTH_UNIT);
if ( x < ALMOST_ZERO || y < ALMOST_ZERO ) {
Logger::Message(Logger::LOG_NOTICE,"Skipping zero sized profile:",l);
return false;
}
cgal_placement_t trsf2d;
bool has_position = true;
#ifdef USE_IFC4
has_position = l->hasPosition();
#endif
face = cgal_face_t();
face.outer.push_back(Kernel_::Point_3(-x, -y, 0.0));
face.outer.push_back(Kernel_::Point_3( x, -y, 0.0));
face.outer.push_back(Kernel_::Point_3( x, y, 0.0));
face.outer.push_back(Kernel_::Point_3(-x, y, 0.0));
if (has_position) {
IfcGeom::CgalKernel::convert(l->Position(), trsf2d);
for (auto &vertex: face.outer) {
vertex = vertex.transform(trsf2d);
}
}
return true;
}
bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcRoundedRectangleProfileDef* l, cgal_face_t& face) {
const double x = l->XDim() / 2.0f * getValue(GV_LENGTH_UNIT);
const double y = l->YDim() / 2.0f * getValue(GV_LENGTH_UNIT);
const double r = l->RoundingRadius() * getValue(GV_LENGTH_UNIT);
if ( x < ALMOST_ZERO || y < ALMOST_ZERO || r < ALMOST_ZERO ) {
Logger::Message(Logger::LOG_NOTICE,"Skipping zero sized profile:",l);
return false;
}
cgal_placement_t trsf2d;
bool has_position = true;
#ifdef USE_IFC4
has_position = l->hasPosition();
#endif
const int segments = 3;
if (r == 0.0) {
face = cgal_face_t();
face.outer.push_back(Kernel_::Point_3(-x, -y, 0.0));
face.outer.push_back(Kernel_::Point_3( x, -y, 0.0));
face.outer.push_back(Kernel_::Point_3( x, y, 0.0));
face.outer.push_back(Kernel_::Point_3(-x, y, 0.0));
}
else {
face = cgal_face_t();
for (int current_segment = 0; current_segment <= segments; ++current_segment) {
double current_angle = current_segment*0.5*3.141592653589793/((double)segments);
face.outer.push_back(Kernel_::Point_3(x-r+r*cos(current_angle), y-r+r*sin(current_angle), 0));
}
for (int current_segment = 0; current_segment <= segments; ++current_segment) {
double current_angle = 0.5*3.141592653589793+current_segment*0.5*3.141592653589793/((double)segments);
face.outer.push_back(Kernel_::Point_3(-x+r+r*cos(current_angle), y-r+r*sin(current_angle), 0));
}
for (int current_segment = 0; current_segment <= segments; ++current_segment) {
double current_angle = 1.0*3.141592653589793+current_segment*0.5*3.141592653589793/((double)segments);
face.outer.push_back(Kernel_::Point_3(-x+r+r*cos(current_angle), -y+r+r*sin(current_angle), 0));
}
for (int current_segment = 0; current_segment <= segments; ++current_segment) {
double current_angle = 1.5*3.141592653589793+current_segment*0.5*3.141592653589793/((double)segments);
face.outer.push_back(Kernel_::Point_3(x-r+r*cos(current_angle), -y+r+r*sin(current_angle), 0));
}
}
if (has_position) {
IfcGeom::CgalKernel::convert(l->Position(), trsf2d);
for (auto &vertex: face.outer) {
vertex = vertex.transform(trsf2d);
}
}
return true;
}
bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcRectangleHollowProfileDef* l, cgal_face_t& face) {
const double x = l->XDim() / 2.0f * getValue(GV_LENGTH_UNIT);
const double y = l->YDim() / 2.0f * getValue(GV_LENGTH_UNIT);
const double d = l->WallThickness() * getValue(GV_LENGTH_UNIT);
const bool fr1 = l->hasOuterFilletRadius();
const bool fr2 = l->hasInnerFilletRadius();
const double r1 = fr1 ? l->OuterFilletRadius() * getValue(GV_LENGTH_UNIT) : 0.;
const double r2 = fr2 ? l->InnerFilletRadius() * getValue(GV_LENGTH_UNIT) : 0.;
if ( x < ALMOST_ZERO || y < ALMOST_ZERO ) {
Logger::Message(Logger::LOG_NOTICE,"Skipping zero sized profile:",l);
return false;
}
cgal_placement_t trsf2d;
bool has_position = true;
#ifdef USE_IFC4
has_position = l->hasPosition();
#endif
const int segments = 3;
if (!fr1 || r1 == 0.0) {
face = cgal_face_t();
face.outer.push_back(Kernel_::Point_3(-x, -y, 0.0));
face.outer.push_back(Kernel_::Point_3( x, -y, 0.0));
face.outer.push_back(Kernel_::Point_3( x, y, 0.0));
face.outer.push_back(Kernel_::Point_3(-x, y, 0.0));
}
else {
face = cgal_face_t();
for (int current_segment = 0; current_segment <= segments; ++current_segment) {
double current_angle = current_segment*0.5*3.141592653589793/((double)segments);
face.outer.push_back(Kernel_::Point_3(x-r1+r1*cos(current_angle), y-r1+r1*sin(current_angle), 0));
}
for (int current_segment = 0; current_segment <= segments; ++current_segment) {
double current_angle = 0.5*3.141592653589793+current_segment*0.5*3.141592653589793/((double)segments);
face.outer.push_back(Kernel_::Point_3(-x+r1+r1*cos(current_angle), y-r1+r1*sin(current_angle), 0));
}
for (int current_segment = 0; current_segment <= segments; ++current_segment) {
double current_angle = 1.0*3.141592653589793+current_segment*0.5*3.141592653589793/((double)segments);
face.outer.push_back(Kernel_::Point_3(-x+r1+r1*cos(current_angle), -y+r1+r1*sin(current_angle), 0));
}
for (int current_segment = 0; current_segment <= segments; ++current_segment) {
double current_angle = 1.5*3.141592653589793+current_segment*0.5*3.141592653589793/((double)segments);
face.outer.push_back(Kernel_::Point_3(x-r1+r1*cos(current_angle), -y+r1+r1*sin(current_angle), 0));
}
}
if (!fr2 || r2 == 0.0) {
face.inner.push_back(cgal_wire_t());
face.inner.back().push_back(Kernel_::Point_3(-x+d, -y+d, 0.0));
face.inner.back().push_back(Kernel_::Point_3( x-d, -y+d, 0.0));
face.inner.back().push_back(Kernel_::Point_3( x-d, y-d, 0.0));
face.inner.back().push_back(Kernel_::Point_3(-x+d, y-d, 0.0));
}
else {
face.inner.push_back(cgal_wire_t());
for (int current_segment = 0; current_segment <= segments; ++current_segment) {
double current_angle = current_segment*0.5*3.141592653589793/((double)segments);
face.inner.back().push_back(Kernel_::Point_3(x-d-r1+r1*cos(current_angle), y-d-r1+r1*sin(current_angle), 0));
}
for (int current_segment = 0; current_segment <= segments; ++current_segment) {
double current_angle = 0.5*3.141592653589793+current_segment*0.5*3.141592653589793/((double)segments);
face.inner.back().push_back(Kernel_::Point_3(-x+d+r1+r1*cos(current_angle), y-d-r1+r1*sin(current_angle), 0));
}
for (int current_segment = 0; current_segment <= segments; ++current_segment) {
double current_angle = 1.0*3.141592653589793+current_segment*0.5*3.141592653589793/((double)segments);
face.inner.back().push_back(Kernel_::Point_3(-x+d+r1+r1*cos(current_angle), -y+d+r1+r1*sin(current_angle), 0));
}
for (int current_segment = 0; current_segment <= segments; ++current_segment) {
double current_angle = 1.5*3.141592653589793+current_segment*0.5*3.141592653589793/((double)segments);
face.inner.back().push_back(Kernel_::Point_3(x-d-r1+r1*cos(current_angle), -y+d+r1+r1*sin(current_angle), 0));
}
}
if (has_position) {
IfcGeom::CgalKernel::convert(l->Position(), trsf2d);
for (auto &vertex: face.outer) {
vertex = vertex.transform(trsf2d);
} for (auto &inner: face.inner) {
for (auto &vertex: inner) {
vertex = vertex.transform(trsf2d);
}
}
}
return true;
}
bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcTrapeziumProfileDef* l, cgal_face_t& face) {
const double x1 = l->BottomXDim() / 2.0f * getValue(GV_LENGTH_UNIT);
const double w = l->TopXDim() * getValue(GV_LENGTH_UNIT);
const double dx = l->TopXOffset() * getValue(GV_LENGTH_UNIT);
const double y = l->YDim() / 2.0f * getValue(GV_LENGTH_UNIT);
if ( x1 < ALMOST_ZERO || w < ALMOST_ZERO || y < ALMOST_ZERO ) {
Logger::Message(Logger::LOG_NOTICE,"Skipping zero sized profile:",l);
return false;
}
cgal_placement_t trsf2d;
bool has_position = true;
#ifdef USE_IFC4
has_position = l->hasPosition();
#endif
face = cgal_face_t();
face.outer.push_back(Kernel_::Point_3(-x1, -y, 0.0));
face.outer.push_back(Kernel_::Point_3(x1, -y, 0.0));
face.outer.push_back(Kernel_::Point_3(dx+w-x1, y, 0.0));
face.outer.push_back(Kernel_::Point_3(dx-x1, y, 0.0));
if (has_position) {
IfcGeom::CgalKernel::convert(l->Position(), trsf2d);
for (auto &vertex: face.outer) {
vertex = vertex.transform(trsf2d);
}
}
return true;
}
bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcCircleProfileDef* l, cgal_face_t& face) {
const double r = l->Radius() * getValue(GV_LENGTH_UNIT);
if ( r == 0.0f ) {
Logger::Message(Logger::LOG_NOTICE,"Skipping zero sized profile:",l);
return false;
}
cgal_placement_t trsf2d;
bool has_position = true;
#ifdef USE_IFC4
has_position = l->hasPosition();
#endif
const int segments = 12;
face = cgal_face_t();
for (int current_segment = 0; current_segment < segments; ++current_segment) {
double current_angle = current_segment*2.0*3.141592653589793/((double)segments);
face.outer.push_back(Kernel_::Point_3(r*cos(current_angle), r*sin(current_angle), 0));
}
if (has_position) {
IfcGeom::CgalKernel::convert(l->Position(), trsf2d);
for (auto &vertex: face.outer) {
vertex = vertex.transform(trsf2d);
}
}
return true;
}
bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcCircleHollowProfileDef* l, cgal_face_t& face) {
const double r = l->Radius() * getValue(GV_LENGTH_UNIT);
const double t = l->WallThickness() * getValue(GV_LENGTH_UNIT);
if ( r == 0.0f || t == 0.0f ) {
Logger::Message(Logger::LOG_NOTICE,"Skipping zero sized profile:",l);
return false;
}
cgal_placement_t trsf2d;
bool has_position = true;
#ifdef USE_IFC4
has_position = l->hasPosition();
#endif
const int segments = 12;
face = cgal_face_t();
for (int current_segment = 0; current_segment < segments; ++current_segment) {
double current_angle = current_segment*2.0*3.141592653589793/((double)segments);
face.outer.push_back(Kernel_::Point_3(r*cos(current_angle), r*sin(current_angle), 0));
}
face.inner.push_back(cgal_wire_t());
for (int current_segment = 0; current_segment < segments; ++current_segment) {
double current_angle = current_segment*2.0*3.141592653589793/((double)segments);
face.inner.back().push_back(Kernel_::Point_3((r-t)*cos(current_angle), (r-t)*sin(current_angle), 0));
}
if (has_position) {
IfcGeom::CgalKernel::convert(l->Position(), trsf2d);
for (auto &vertex: face.outer) {
vertex = vertex.transform(trsf2d);
} for (auto &inner: face.inner) {
for (auto &vertex: inner) {
vertex = vertex.transform(trsf2d);
}
}
}
return true;
}
bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcEllipseProfileDef* l, cgal_face_t& face) {
double rx = l->SemiAxis1() * getValue(GV_LENGTH_UNIT);
double ry = l->SemiAxis2() * getValue(GV_LENGTH_UNIT);
if ( rx < ALMOST_ZERO || ry < ALMOST_ZERO ) {
Logger::Message(Logger::LOG_NOTICE,"Skipping zero sized profile:",l);
return false;
}
cgal_placement_t trsf2d;
bool has_position = true;
#ifdef USE_IFC4
has_position = l->hasPosition();
#endif
const int segments = 12;
face = cgal_face_t();
for (int current_segment = 0; current_segment < segments; ++current_segment) {
double current_angle = current_segment*2.0*3.141592653589793/((double)segments);
face.outer.push_back(Kernel_::Point_3(rx*cos(current_angle), ry*sin(current_angle), 0));
}
if (has_position) {
IfcGeom::CgalKernel::convert(l->Position(), trsf2d);
for (auto &vertex: face.outer) {
vertex = vertex.transform(trsf2d);
}
}
return true;
}
bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcFace* l, cgal_face_t& face) {
IfcSchema::IfcFaceBound::list::ptr bounds = l->Bounds();
int num_outer_bounds = 0;
for (IfcSchema::IfcFaceBound::list::it it = bounds->begin(); it != bounds->end(); ++it) {
IfcSchema::IfcFaceBound* bound = *it;
if (bound->as<IfcSchema::IfcFaceOuterBound>()) num_outer_bounds ++;
}
if (num_outer_bounds != 1) {
Logger::Message(Logger::LOG_ERROR, "Invalid configuration of boundaries for:", l);
return false;
}
cgal_face_t mf;
for (IfcSchema::IfcFaceBound::list::it it = bounds->begin(); it != bounds->end(); ++it) {
IfcSchema::IfcFaceBound* bound = *it;
IfcSchema::IfcLoop* loop = bound->Bound();
const bool is_interior = !bound->as<IfcSchema::IfcFaceOuterBound>();
cgal_wire_t wire;
if (!convert_wire(loop, wire)) {
Logger::Message(Logger::LOG_ERROR, "Failed to process face boundary loop", loop);
return false;
}
if (!is_interior) {
mf.outer = wire;
} else {
mf.inner.push_back(wire);
}
}
face = mf;
// std::cout << "Face: " << std::endl;
// for (auto &point: face.outer) {
// std::cout << "\tPoint(" << point << ")" << std::endl;
// }
return true;
}
bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcCShapeProfileDef* l, cgal_face_t& face) {
const double y = l->Depth() / 2.0f * getValue(GV_LENGTH_UNIT);
const double x = l->Width() / 2.0f * getValue(GV_LENGTH_UNIT);
const double d1 = l->WallThickness() * getValue(GV_LENGTH_UNIT);
const double d2 = l->Girth() * getValue(GV_LENGTH_UNIT);
bool doFillet = l->hasInternalFilletRadius();
double f1 = 0;
double f2 = 0;
if ( doFillet ) {
f1 = l->InternalFilletRadius() * getValue(GV_LENGTH_UNIT);
f2 = f1 + d1;
}
if ( x < ALMOST_ZERO || y < ALMOST_ZERO || d1 < ALMOST_ZERO || d2 < ALMOST_ZERO ) {
Logger::Message(Logger::LOG_NOTICE,"Skipping zero sized profile:",l);
return false;
}
cgal_placement_t trsf2d;
bool has_position = true;
#ifdef USE_IFC4
has_position = l->hasPosition();
#endif
const int segments = 3;
if (!doFillet || f1 == 0.0) {
face = cgal_face_t();
face.outer.push_back(Kernel_::Point_3(-x, -y, 0.0));
face.outer.push_back(Kernel_::Point_3(x, -y, 0.0));
face.outer.push_back(Kernel_::Point_3(x, -y+d2, 0.0));
face.outer.push_back(Kernel_::Point_3(x-d1, -y+d2, 0.0));
face.outer.push_back(Kernel_::Point_3(x-d1, -y+d1, 0.0));
face.outer.push_back(Kernel_::Point_3(-x+d1, -y+d1, 0.0));
face.outer.push_back(Kernel_::Point_3(-x+d1, y-d1, 0.0));
face.outer.push_back(Kernel_::Point_3(x-d1, y-d1, 0.0));
face.outer.push_back(Kernel_::Point_3(x-d1, y-d2, 0.0));
face.outer.push_back(Kernel_::Point_3(x, y-d2, 0.0));
face.outer.push_back(Kernel_::Point_3(x, y, 0.0));
face.outer.push_back(Kernel_::Point_3(-x, y, 0.0));
}
else {
face = cgal_face_t();
for (int current_segment = 0; current_segment <= segments; ++current_segment) {
double current_angle = 1.0*3.141592653589793+current_segment*0.5*3.141592653589793/((double)segments);
face.outer.push_back(Kernel_::Point_3(-x+f2+f2*cos(current_angle), -y+f2+f2*sin(current_angle), 0));
}
for (int current_segment = 0; current_segment <= segments; ++current_segment) {
double current_angle = 1.5*3.141592653589793+current_segment*0.5*3.141592653589793/((double)segments);
face.outer.push_back(Kernel_::Point_3(x-f2+f2*cos(current_angle), -y+f2+f2*sin(current_angle), 0));
}
face.outer.push_back(Kernel_::Point_3(x, -y+d2, 0.0));
face.outer.push_back(Kernel_::Point_3(x-d1, -y+d2, 0.0));
for (int current_segment = segments; current_segment >= 0; --current_segment) {
double current_angle = 1.5*3.141592653589793+current_segment*0.5*3.141592653589793/((double)segments);
face.outer.push_back(Kernel_::Point_3(x-f2+f1*cos(current_angle), -y+f2+f1*sin(current_angle), 0));
}
for (int current_segment = segments; current_segment >= 0; --current_segment) {
double current_angle = 1.0*3.141592653589793+current_segment*0.5*3.141592653589793/((double)segments);
face.outer.push_back(Kernel_::Point_3(-x+f2+f1*cos(current_angle), -y+f2+f1*sin(current_angle), 0));
}
for (int current_segment = segments; current_segment >= 0; --current_segment) {
double current_angle = 0.5*3.141592653589793+current_segment*0.5*3.141592653589793/((double)segments);
face.outer.push_back(Kernel_::Point_3(-x+f2+f1*cos(current_angle), y-f2+f1*sin(current_angle), 0));
}
for (int current_segment = segments; current_segment >= 0; --current_segment) {
double current_angle = current_segment*0.5*3.141592653589793/((double)segments);
face.outer.push_back(Kernel_::Point_3(x-f2+f1*cos(current_angle), y-f2+f1*sin(current_angle), 0));
}
face.outer.push_back(Kernel_::Point_3(x-d1, y-d2, 0.0));
face.outer.push_back(Kernel_::Point_3(x, y-d2, 0.0));
for (int current_segment = 0; current_segment <= segments; ++current_segment) {
double current_angle = current_segment*0.5*3.141592653589793/((double)segments);
face.outer.push_back(Kernel_::Point_3(x-f2+f2*cos(current_angle), y-f2+f2*sin(current_angle), 0));
}
for (int current_segment = 0; current_segment <= segments; ++current_segment) {
double current_angle = 0.5*3.141592653589793+current_segment*0.5*3.141592653589793/((double)segments);
face.outer.push_back(Kernel_::Point_3(-x+f2+f2*cos(current_angle), y-f2+f2*sin(current_angle), 0));
}
}
if (has_position) {
IfcGeom::CgalKernel::convert(l->Position(), trsf2d);
for (auto &vertex: face.outer) {
vertex = vertex.transform(trsf2d);
}
}
return true;
}
bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcLShapeProfileDef* l, cgal_face_t& face) {
const bool hasSlope = l->hasLegSlope();
const bool doEdgeFillet = l->hasEdgeRadius();
const bool doFillet = l->hasFilletRadius();
const double y = l->Depth() / 2.0f * getValue(GV_LENGTH_UNIT);
const double x = (l->hasWidth() ? l->Width() : l->Depth()) / 2.0f * getValue(GV_LENGTH_UNIT);
const double d = l->Thickness() * getValue(GV_LENGTH_UNIT);
const double slope = hasSlope ? (l->LegSlope() * getValue(GV_PLANEANGLE_UNIT)) : 0.;
double f1 = 0.0f;
double f2 = 0.0f;
if (doFillet) {
f1 = l->FilletRadius() * getValue(GV_LENGTH_UNIT);
}
if ( doEdgeFillet) {
f2 = l->EdgeRadius() * getValue(GV_LENGTH_UNIT);
}
if ( x < ALMOST_ZERO || y < ALMOST_ZERO || d < ALMOST_ZERO ) {
Logger::Message(Logger::LOG_NOTICE,"Skipping zero sized profile:",l);
return false;
}
double xx = -x+d;
double xy = -y+d;
double dy1 = 0.;
double dy2 = 0.;
double dx1 = 0.;
double dx2 = 0.;
if (hasSlope) {
dy1 = tan(slope) * x;
dy2 = tan(slope) * (x - d);
dx1 = tan(slope) * y;
dx2 = tan(slope) * (y - d);
const double x1s = x; const double y1s = -y + d - dy1;
const double x1e = -x + d; const double y1e = -y + d + dy2;
const double x2s = -x + d - dx1; const double y2s = y;
const double x2e = -x + d + dx2; const double y2e = -y + d;
const double a1 = y1e - y1s;
const double b1 = x1s - x1e;
const double c1 = a1*x1s + b1*y1s;
const double a2 = y2e - y2s;
const double b2 = x2s - x2e;
const double c2 = a2*x2s + b2*y2s;
const double det = a1*b2 - a2*b1;
if (ALMOST_THE_SAME(det, 0.)) {
Logger::Message(Logger::LOG_NOTICE, "Legs do not intersect for:",l);
return false;
}
xx = (b2*c1 - b1*c2) / det;
xy = (a1*c2 - a2*c1) / det;
}
cgal_placement_t trsf2d;
bool has_position = true;
#ifdef USE_IFC4
has_position = l->hasPosition();
#endif
const int segments = 3;
face = cgal_face_t();
face.outer.push_back(Kernel_::Point_3(-x, -y, 0.0));
face.outer.push_back(Kernel_::Point_3(x, -y, 0.0));
if (f2 == 0.0) {
face.outer.push_back(Kernel_::Point_3(x, -y+d-dy1, 0.0));
} else {
for (int current_segment = 0; current_segment <= segments; ++current_segment) {
double current_angle = current_segment*0.5*3.141592653589793/((double)segments);
face.outer.push_back(Kernel_::Point_3(x-f2+f2*cos(current_angle), -y+d-dy1-f2+f2*sin(current_angle), 0));
}
} if (f1 == 0.0) {
face.outer.push_back(Kernel_::Point_3(xx, xy, 0.0));
} else {
for (int current_segment = segments; current_segment >= 0; --current_segment) {
double current_angle = 1.0*3.141592653589793+current_segment*0.5*3.141592653589793/((double)segments);
face.outer.push_back(Kernel_::Point_3(xx+f1+f1*cos(current_angle), xy+f1+f1*sin(current_angle), 0));
}
} if (f2 == 0.0) {
face.outer.push_back(Kernel_::Point_3(-x+d-dx1, y, 0.0));
} else {
for (int current_segment = 0; current_segment <= segments; ++current_segment) {
double current_angle = current_segment*0.5*3.141592653589793/((double)segments);
face.outer.push_back(Kernel_::Point_3(-x+d-dx1-f2+f2*cos(current_angle), y-f2+f2*sin(current_angle), 0));
}
} face.outer.push_back(Kernel_::Point_3(-x, y, 0.0));
if (has_position) {
IfcGeom::CgalKernel::convert(l->Position(), trsf2d);
for (auto &vertex: face.outer) {
vertex = vertex.transform(trsf2d);
}
}
return true;
}
// TODO: Untested
bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcIShapeProfileDef* l, cgal_face_t& face) {
const double x1 = l->OverallWidth() / 2.0f * getValue(GV_LENGTH_UNIT);
const double y = l->OverallDepth() / 2.0f * getValue(GV_LENGTH_UNIT);
const double d1 = l->WebThickness() / 2.0f * getValue(GV_LENGTH_UNIT);
const double dy1 = l->FlangeThickness() * getValue(GV_LENGTH_UNIT);
bool doFillet1 = l->hasFilletRadius();
double f1 = 0.;
if ( doFillet1 ) {
f1 = l->FilletRadius() * getValue(GV_LENGTH_UNIT);
}
bool doFillet2 = doFillet1;
double x2 = x1, dy2 = dy1, f2 = f1;
if (l->as<IfcSchema::IfcAsymmetricIShapeProfileDef>()) {
IfcSchema::IfcAsymmetricIShapeProfileDef* assym = (IfcSchema::IfcAsymmetricIShapeProfileDef*) l;
x2 = assym->TopFlangeWidth() / 2. * getValue(GV_LENGTH_UNIT);
doFillet2 = assym->hasTopFlangeFilletRadius();
if (doFillet2) {
f2 = assym->TopFlangeFilletRadius() * getValue(GV_LENGTH_UNIT);
}
if (assym->hasTopFlangeThickness()) {
dy2 = assym->TopFlangeThickness() * getValue(GV_LENGTH_UNIT);
}
}
if ( x1 < ALMOST_ZERO || x2 < ALMOST_ZERO || y < ALMOST_ZERO || d1 < ALMOST_ZERO || dy1 < ALMOST_ZERO || dy2 < ALMOST_ZERO ) {
Logger::Message(Logger::LOG_NOTICE,"Skipping zero sized profile:",l);
return false;
}
cgal_placement_t trsf2d;
bool has_position = true;
#ifdef USE_IFC4
has_position = l->hasPosition();
#endif
const int segments = 3;
face = cgal_face_t();
face.outer.push_back(Kernel_::Point_3(-x1, -y, 0.0));
face.outer.push_back(Kernel_::Point_3(x1, -y, 0.0));
face.outer.push_back(Kernel_::Point_3(x1, -y+dy1, 0.0));
if (f1 == 0.0) {
face.outer.push_back(Kernel_::Point_3(d1, -y+dy1, 0.0));
face.outer.push_back(Kernel_::Point_3(d1, y-dy2, 0.0));
} else {
for (int current_segment = segments; current_segment >= 0; --current_segment) {
double current_angle = 1.0*3.141592653589793+current_segment*0.5*3.141592653589793/((double)segments);
face.outer.push_back(Kernel_::Point_3(d1+f1+f1*cos(current_angle), -y+dy1+f1+f1*sin(current_angle), 0));
} for (int current_segment = segments; current_segment >= 0; --current_segment) {
double current_angle = 0.5*3.141592653589793+current_segment*0.5*3.141592653589793/((double)segments);
face.outer.push_back(Kernel_::Point_3(d1+f1+f1*cos(current_angle), y-dy2-f1+f1*sin(current_angle), 0));
}
} face.outer.push_back(Kernel_::Point_3(x2, y-dy2, 0.0));
face.outer.push_back(Kernel_::Point_3(x2, y, 0.0));
face.outer.push_back(Kernel_::Point_3(-x2, y, 0.0));
face.outer.push_back(Kernel_::Point_3(-x2, y-dy2, 0.0));
if (f2 == 0.0) {
face.outer.push_back(Kernel_::Point_3(-d1, y-dy2, 0.0));
face.outer.push_back(Kernel_::Point_3(-d1, -y+dy1, 0.0));
} else {
for (int current_segment = segments; current_segment >= 0; --current_segment) {
double current_angle = current_segment*0.5*3.141592653589793/((double)segments);
face.outer.push_back(Kernel_::Point_3(-d1-f2+f2*cos(current_angle), y-dy2-f2+f2*sin(current_angle), 0));
} for (int current_segment = segments; current_segment >= 0; --current_segment) {
double current_angle = 1.5*3.141592653589793+current_segment*0.5*3.141592653589793/((double)segments);
face.outer.push_back(Kernel_::Point_3(-d1-f2+f2*cos(current_angle), -y+dy1+f2+f2*sin(current_angle), 0));
}
} face.outer.push_back(Kernel_::Point_3(-x1, -y+dy1, 0.0));
if (has_position) {
IfcGeom::CgalKernel::convert(l->Position(), trsf2d);
for (auto &vertex: face.outer) {
vertex = vertex.transform(trsf2d);
}
}
return true;
}
bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcTShapeProfileDef* l, cgal_face_t& face) {
const bool doFlangeEdgeFillet = l->hasFlangeEdgeRadius();
const bool doWebEdgeFillet = l->hasWebEdgeRadius();
const bool doFillet = l->hasFilletRadius();
const bool hasFlangeSlope = l->hasFlangeSlope();
const bool hasWebSlope = l->hasWebSlope();
const double y = l->Depth() / 2.0f * getValue(GV_LENGTH_UNIT);
const double x = l->FlangeWidth() / 2.0f * getValue(GV_LENGTH_UNIT);
const double d1 = l->WebThickness() * getValue(GV_LENGTH_UNIT);
const double d2 = l->FlangeThickness() * getValue(GV_LENGTH_UNIT);
const double flangeSlope = hasFlangeSlope ? (l->FlangeSlope() * getValue(GV_PLANEANGLE_UNIT)) : 0.;
const double webSlope = hasWebSlope ? (l->WebSlope() * getValue(GV_PLANEANGLE_UNIT)) : 0.;
if ( x < ALMOST_ZERO || y < ALMOST_ZERO || d1 < ALMOST_ZERO || d2 < ALMOST_ZERO ) {
Logger::Message(Logger::LOG_NOTICE,"Skipping zero sized profile:",l);
return false;
}
double dy1 = 0.0f;
double dy2 = 0.0f;
double dx1 = 0.0f;
double dx2 = 0.0f;
double f1 = 0.0f;
double f2 = 0.0f;
double f3 = 0.0f;
if (doFillet) {
f1 = l->FilletRadius() * getValue(GV_LENGTH_UNIT);
}
if (doWebEdgeFillet) {
f2 = l->WebEdgeRadius() * getValue(GV_LENGTH_UNIT);
}
if (doFlangeEdgeFillet) {
f3 = l->FlangeEdgeRadius() * getValue(GV_LENGTH_UNIT);
}
double xx, xy;
if (hasFlangeSlope) {
dy1 = (x / 2. - d1) * tan(flangeSlope);
dy2 = x / 2. * tan(flangeSlope);
}
if (hasWebSlope) {
dx1 = (y - d2) * tan(webSlope);
dx2 = y * tan(webSlope);
}
if (hasWebSlope || hasFlangeSlope) {
const double x1s = d1/2. - dx2; const double y1s = -y;
const double x1e = d1/2. + dx1; const double y1e = y - d2;
const double x2s = x; const double y2s = y - d2 + dy2;
const double x2e = d1/2.; const double y2e = y - d2 - dy1;
const double a1 = y1e - y1s;
const double b1 = x1s - x1e;
const double c1 = a1*x1s + b1*y1s;
const double a2 = y2e - y2s;
const double b2 = x2s - x2e;
const double c2 = a2*x2s + b2*y2s;
const double det = a1*b2 - a2*b1;
if (ALMOST_THE_SAME(det, 0.)) {
Logger::Message(Logger::LOG_NOTICE, "Web and flange do not intersect for:",l);
return false;
}
xx = (b2*c1 - b1*c2) / det;
xy = (a1*c2 - a2*c1) / det;
} else {
xx = d1 / 2;
xy = y - d2;
}
cgal_placement_t trsf2d;
bool has_position = true;
#ifdef USE_IFC4
has_position = l->hasPosition();
#endif
const int segments = 3;
face = cgal_face_t();
if (f2 == 0.0) {
face.outer.push_back(Kernel_::Point_3(d1/2.-dx2, -y, 0.0));
} else {
for (int current_segment = 0; current_segment <= segments; ++current_segment) {
double current_angle = 1.5*3.141592653589793+current_segment*0.5*3.141592653589793/((double)segments);
face.outer.push_back(Kernel_::Point_3(d1/2.-dx2-f2+f2*cos(current_angle), -y+f2+f2*sin(current_angle), 0));
}
} if (f1 == 0.0) {
face.outer.push_back(Kernel_::Point_3(xx, xy, 0.0));
} else {
for (int current_segment = segments; current_segment >= 0; --current_segment) {
double current_angle = 0.5*3.141592653589793+current_segment*0.5*3.141592653589793/((double)segments);
face.outer.push_back(Kernel_::Point_3(xx+f1+f1*cos(current_angle), xy-f1+f1*sin(current_angle), 0));
}
} if (f3 == 0.0) {
face.outer.push_back(Kernel_::Point_3(x, y-d2+dy2, 0.0));
} else {
for (int current_segment = 0; current_segment <= segments; ++current_segment) {
double current_angle = 1.5*3.141592653589793+current_segment*0.5*3.141592653589793/((double)segments);
face.outer.push_back(Kernel_::Point_3(x-f3+f3*cos(current_angle), y-d2+dy2+f3+f3*sin(current_angle), 0));
}
} face.outer.push_back(Kernel_::Point_3(x, y, 0.0));
face.outer.push_back(Kernel_::Point_3(-x, y, 0.0));
if (f3 == 0.0) {
face.outer.push_back(Kernel_::Point_3(-x, y-d2+dy2, 0.0));
} else {
for (int current_segment = 0; current_segment <= segments; ++current_segment) {
double current_angle = 1.0*3.141592653589793+current_segment*0.5*3.141592653589793/((double)segments);
face.outer.push_back(Kernel_::Point_3(-x+f3+f3*cos(current_angle), y-d2+dy2+f3+f3*sin(current_angle), 0));
}
} if (f1 == 0.0) {
face.outer.push_back(Kernel_::Point_3(-xx, xy, 0.0));
} else {
for (int current_segment = segments; current_segment >= 0; --current_segment) {
double current_angle = current_segment*0.5*3.141592653589793/((double)segments);
face.outer.push_back(Kernel_::Point_3(-xx-f1+f1*cos(current_angle), xy-f1+f1*sin(current_angle), 0));
}
} if (f2 == 0.0) {
face.outer.push_back(Kernel_::Point_3(-d1/2.+dx2, -y, 0.0));
} else {
for (int current_segment = 0; current_segment <= segments; ++current_segment) {
double current_angle = 1.0*3.141592653589793+current_segment*0.5*3.141592653589793/((double)segments);
face.outer.push_back(Kernel_::Point_3(-d1/2.+dx2+f2+f2*cos(current_angle), -y+f2+f2*sin(current_angle), 0));
}
}
if (has_position) {
IfcGeom::CgalKernel::convert(l->Position(), trsf2d);
for (auto &vertex: face.outer) {
vertex = vertex.transform(trsf2d);
}
}
return true;
}
bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcUShapeProfileDef* l, cgal_face_t& face) {
const bool doEdgeFillet = l->hasEdgeRadius();
const bool doFillet = l->hasFilletRadius();
const bool hasSlope = l->hasFlangeSlope();
const double y = l->Depth() / 2.0f * getValue(GV_LENGTH_UNIT);
const double x = l->FlangeWidth() / 2.0f * getValue(GV_LENGTH_UNIT);
const double d1 = l->WebThickness() * getValue(GV_LENGTH_UNIT);
const double d2 = l->FlangeThickness() * getValue(GV_LENGTH_UNIT);
const double slope = hasSlope ? (l->FlangeSlope() * getValue(GV_PLANEANGLE_UNIT)) : 0.;
double dy1 = 0.0f;
double dy2 = 0.0f;
double f1 = 0.0f;
double f2 = 0.0f;
if (doFillet) {
f1 = l->FilletRadius() * getValue(GV_LENGTH_UNIT);
}
if (doEdgeFillet) {
f2 = l->EdgeRadius() * getValue(GV_LENGTH_UNIT);
}
if (hasSlope) {
dy1 = (x - d1) * tan(slope);
dy2 = x * tan(slope);
}
if ( x < ALMOST_ZERO || y < ALMOST_ZERO || d1 < ALMOST_ZERO || d2 < ALMOST_ZERO ) {
Logger::Message(Logger::LOG_NOTICE,"Skipping zero sized profile:",l);
return false;
}
cgal_placement_t trsf2d;
bool has_position = true;
#ifdef USE_IFC4
has_position = l->hasPosition();
#endif
const int segments = 3;
face = cgal_face_t();
face.outer.push_back(Kernel_::Point_3(-x, -y, 0.0));
face.outer.push_back(Kernel_::Point_3(x, -y, 0.0));
if (f2 == 0.0) {
face.outer.push_back(Kernel_::Point_3(x, -y+d2-dy2, 0.0));
} else {
for (int current_segment = 0; current_segment <= segments; ++current_segment) {
double current_angle = current_segment*0.5*3.141592653589793/((double)segments);
face.outer.push_back(Kernel_::Point_3(x-f2+f2*cos(current_angle), -y+d2-dy2-f2+f2*sin(current_angle), 0));
}
} if (f1 == 0.0) {
face.outer.push_back(Kernel_::Point_3(-x+d1, -y+d2+dy1, 0.0));
} else {
for (int current_segment = segments; current_segment >= 0; --current_segment) {
double current_angle = 1.0*3.141592653589793+current_segment*0.5*3.141592653589793/((double)segments);
face.outer.push_back(Kernel_::Point_3(-x+d1+f1+f1*cos(current_angle), -y+d2+dy1+f1+f1*sin(current_angle), 0));
}
} if (f1 == 0.0) {
face.outer.push_back(Kernel_::Point_3(-x+d1, y-d2-dy1, 0.0));
} else {
for (int current_segment = segments; current_segment >= 0; --current_segment) {
double current_angle = 0.5*3.141592653589793+current_segment*0.5*3.141592653589793/((double)segments);
face.outer.push_back(Kernel_::Point_3(-x+d1+f1+f1*cos(current_angle), y-d2-dy1-f1+f1*sin(current_angle), 0));
}
} if (f2 == 0.0) {
face.outer.push_back(Kernel_::Point_3(x,y-d2+dy2, 0.0));
} else {
for (int current_segment = 0; current_segment <= segments; ++current_segment) {
double current_angle = 1.5*3.141592653589793+current_segment*0.5*3.141592653589793/((double)segments);
face.outer.push_back(Kernel_::Point_3(x-f2+f2*cos(current_angle), y-d2+dy2+f2+f2*sin(current_angle), 0));
}
} face.outer.push_back(Kernel_::Point_3(x,y, 0.0));
face.outer.push_back(Kernel_::Point_3(-x,y, 0.0));
if (has_position) {
IfcGeom::CgalKernel::convert(l->Position(), trsf2d);
for (auto &vertex: face.outer) {
vertex = vertex.transform(trsf2d);
}
}
return true;
}
bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcZShapeProfileDef* l, cgal_face_t& face) {
const double x = l->FlangeWidth() * getValue(GV_LENGTH_UNIT);
const double y = l->Depth() / 2.0f * getValue(GV_LENGTH_UNIT);
const double dx = l->WebThickness() / 2.0f * getValue(GV_LENGTH_UNIT);
const double dy = l->FlangeThickness() * getValue(GV_LENGTH_UNIT);
bool doFillet = l->hasFilletRadius();
bool doEdgeFillet = l->hasEdgeRadius();
double f1 = 0.;
double f2 = 0.;
if ( doFillet ) {
f1 = l->FilletRadius() * getValue(GV_LENGTH_UNIT);
}
if ( doEdgeFillet ) {
f2 = l->EdgeRadius() * getValue(GV_LENGTH_UNIT);
}
if ( x == 0.0f || y == 0.0f || dx == 0.0f || dy == 0.0f ) {
Logger::Message(Logger::LOG_NOTICE,"Skipping zero sized profile:",l);
return false;
}
cgal_placement_t trsf2d;
bool has_position = true;
#ifdef USE_IFC4
has_position = l->hasPosition();
#endif
const int segments = 3;
face = cgal_face_t();
face.outer.push_back(Kernel_::Point_3(-dx, -y, 0.0));
face.outer.push_back(Kernel_::Point_3(x, -y, 0.0));
if (f2 == 0.0) {
face.outer.push_back(Kernel_::Point_3(x, -y+dy, 0.0));
} else {
for (int current_segment = 0; current_segment <= segments; ++current_segment) {
double current_angle = current_segment*0.5*3.141592653589793/((double)segments);
face.outer.push_back(Kernel_::Point_3(x-f2+f2*cos(current_angle), -y+dy-f2+f2*sin(current_angle), 0));
}
} if (f1 == 0.0) {
face.outer.push_back(Kernel_::Point_3(dx, -y+dy, 0.0));
} else {
for (int current_segment = segments; current_segment >= 0; --current_segment) {
double current_angle = 1.0*3.141592653589793+current_segment*0.5*3.141592653589793/((double)segments);
face.outer.push_back(Kernel_::Point_3(dx+f1+f1*cos(current_angle), -y+dy+f1+f1*sin(current_angle), 0));
}
} face.outer.push_back(Kernel_::Point_3(dx, y, 0.0));
face.outer.push_back(Kernel_::Point_3(-x, y, 0.0));
if (f2 == 0.0) {
face.outer.push_back(Kernel_::Point_3(-x, y-dy, 0.0));
} else {
for (int current_segment = 0; current_segment <= segments; ++current_segment) {
double current_angle = 1.0*3.141592653589793+current_segment*0.5*3.141592653589793/((double)segments);
face.outer.push_back(Kernel_::Point_3(-x+f2+f2*cos(current_angle), y-dy+f2+f2*sin(current_angle), 0));
}
} if (f1 == 0.0) {
face.outer.push_back(Kernel_::Point_3(-dx, y-dy, 0.0));
} else {
for (int current_segment = segments; current_segment >= 0; --current_segment) {
double current_angle = current_segment*0.5*3.141592653589793/((double)segments);
face.outer.push_back(Kernel_::Point_3(-dx-f1+f1*cos(current_angle), y-dy-f1+f1*sin(current_angle), 0));
}
}
if (has_position) {
IfcGeom::CgalKernel::convert(l->Position(), trsf2d);
for (auto &vertex: face.outer) {
vertex = vertex.transform(trsf2d);
}
}
return true;
}
bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcDerivedProfileDef* l, cgal_face_t& face) {
cgal_face_t f;
cgal_placement_t trsf2d;
if (convert_face(l->ParentProfile(), f) && IfcGeom::CgalKernel::convert(l->Operator(), trsf2d)) {
cgal_placement_t trsf = trsf2d;
for (auto &vertex: f.outer) vertex = vertex.transform(trsf);
for (auto &ring: f.inner) {
for (auto &vertex: ring) vertex = vertex.transform(trsf);
} face = f;
return true;
} else {
return false;
}
}
@@ -0,0 +1,323 @@
#include "CgalKernel.h"
#include "../../../ifcgeom/schema_agnostic/cgal/CgalConversionResult.h"
#define CgalKernel MAKE_TYPE_NAME(CgalKernel)
bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcCartesianPoint* l, cgal_point_t& point) {
std::vector<double> xyz = l->Coordinates();
point = Kernel_::Point_3(xyz.size() ? (xyz[0]*getValue(GV_LENGTH_UNIT)) : 0.0f,
xyz.size() > 1 ? (xyz[1]*getValue(GV_LENGTH_UNIT)) : 0.0f,
xyz.size() > 2 ? (xyz[2]*getValue(GV_LENGTH_UNIT)) : 0.0f);
// std::cout << "Converted Point(" << point << ")" << std::endl;
return true;
}
bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcDirection* l, cgal_direction_t& dir) {
// IN_CACHE(IfcDirection,l,cgal_direction_t,dir)
std::vector<double> xyz = l->DirectionRatios();
dir = Kernel_::Vector_3(xyz.size() ? xyz[0] : 0.0f,
xyz.size() > 1 ? xyz[1] : 0.0f,
xyz.size() > 2 ? xyz[2] : 0.0f);
// CACHE(IfcDirection,l,dir)
return true;
}
bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcVector* l, cgal_vector_t& v) {
// IN_CACHE(IfcVector,l,cgal_vector_t,v)
cgal_direction_t d;
IfcGeom::CgalKernel::convert(l->Orientation(),d);
v = l->Magnitude() * getValue(GV_LENGTH_UNIT) * d;
// CACHE(IfcVector,l,v)
return true;
}
bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcPlane* pln, cgal_plane_t& plane) {
// IN_CACHE(IfcPlane,pln,gp_Pln,plane)
IfcSchema::IfcAxis2Placement3D* l = pln->Position();
cgal_point_t o;
cgal_direction_t axis = Kernel_::Vector_3(0,0,1);
cgal_direction_t refDirection = Kernel_::Vector_3(1,0,0);
IfcGeom::CgalKernel::convert(l->Location(),o);
bool hasRef = l->hasRefDirection();
if ( l->hasAxis() ) IfcGeom::CgalKernel::convert(l->Axis(),axis);
if ( hasRef ) IfcGeom::CgalKernel::convert(l->RefDirection(),refDirection);
Kernel_::Vector_3 y = CGAL::cross_product(axis, refDirection);
Kernel_::Vector_3 x = CGAL::cross_product(y, axis);
cgal_plane_t ax3;
if ( hasRef ) ax3 = Kernel_::Plane_3(o,o+x,o+y);
else ax3 = Kernel_::Plane_3(o,axis);
plane = ax3;
// std::cout << "IfcPlane C = " << o << std::endl;
// std::cout << "IfcPlane z (axis, exact) = " << axis << std::endl;
// std::cout << "IfcPlane x (refDirection, approximate) = " << refDirection << std::endl;
// std::cout << "IfcPlane y (computed, exact) = " << y << std::endl;
// std::cout << "IfcPlane x (computed, exact) = " << x << std::endl;
//
// std::cout << "Plane_3 o = " << o << std::endl;
// std::cout << "Plane_3 o+x = " << o+x << std::endl;
// std::cout << "Plane_3 o+y = " << o+y << std::endl;
// ax + by + cz + d = 0
// std::cout << "Plane: a = " << plane.a() << ", b = " << plane.b() << ", c = " << plane.c() << ", d = " << plane.d() << std::endl;
// std::ofstream fresult;
// fresult.open("/Users/ken/Desktop/plane.obj");
// // x = -5, y = -5, z = (5a +5b -d)/c
// fresult << "v -5 -5 " << (5.0*CGAL::to_double(plane.a())+5.0*CGAL::to_double(plane.b())-CGAL::to_double(plane.d()))/CGAL::to_double(plane.c()) << std::endl;
// // x = -5, y = +5, z = (5a -5b -d)/c
// fresult << "v -5 5 " << (5.0*CGAL::to_double(plane.a())-5.0*CGAL::to_double(plane.b())-CGAL::to_double(plane.d()))/CGAL::to_double(plane.c()) << std::endl;
// // x = 5, y = -5, z = (-5a +5b -d)/c
// fresult << "v 5 -5 " << (-5.0*CGAL::to_double(plane.a())+5.0*CGAL::to_double(plane.b())-CGAL::to_double(plane.d()))/CGAL::to_double(plane.c()) << std::endl;
// // x = 5, y = +5, z = (-5a -5b -d)/c
// fresult << "v 5 5 " << (-5.0*CGAL::to_double(plane.a())-5.0*CGAL::to_double(plane.b())-CGAL::to_double(plane.d()))/CGAL::to_double(plane.c()) << std::endl;
// fresult << "f 1 2 3" << std::endl;
// fresult << "f 4 3 2" << std::endl;
// fresult.close();
// CACHE(IfcPlane,pln,plane)
return true;
}
bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcAxis2Placement2D* l, cgal_placement_t& trsf) {
// IN_CACHE(IfcAxis2Placement3D,l,gp_Trsf,trsf)
cgal_point_t o;
cgal_direction_t refDirection = Kernel_::Vector_3(1,0,0);
IfcGeom::CgalKernel::convert(l->Location(),o);
bool hasRef = l->hasRefDirection();
if ( hasRef ) IfcGeom::CgalKernel::convert(l->RefDirection(),refDirection);
cgal_direction_t y = Kernel_::Vector_3(-refDirection.y(), refDirection.x(), 0.0);
const double tolerance = 0.01;
if (refDirection.squared_length() < 1.0-tolerance || refDirection.squared_length() > 1.0+tolerance ||
y.squared_length() < 1.0-tolerance || y.squared_length() > 1.0+tolerance) {
std::cout << "Ref direction (x): " << refDirection << " squared length: " << refDirection.squared_length() << std::endl;
std::cout << "y: " << y << " squared length: " << y.squared_length() << std::endl;
std::cout << "Origin: " << o << std::endl;
}
// TODO: Should be checked.
trsf = Kernel_::Aff_transformation_3(refDirection.cartesian(0), y.cartesian(0), 0.0, o.cartesian(0),
refDirection.cartesian(1), y.cartesian(1), 0.0, o.cartesian(1),
0.0, 0.0, 1.0, 0.0);
// CACHE(IfcAxis2Placement3D,l,trsf)
return true;
}
bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcAxis2Placement3D* l, cgal_placement_t& trsf) {
// IN_CACHE(IfcAxis2Placement3D,l,gp_Trsf,trsf)
cgal_point_t o;
cgal_direction_t axis = Kernel_::Vector_3(0,0,1);
cgal_direction_t refDirection = Kernel_::Vector_3(1,0,0);
IfcGeom::CgalKernel::convert(l->Location(),o);
bool hasRef = l->hasRefDirection();
if ( l->hasAxis() ) IfcGeom::CgalKernel::convert(l->Axis(),axis);
if ( hasRef ) IfcGeom::CgalKernel::convert(l->RefDirection(),refDirection);
Kernel_::Vector_3 y = CGAL::cross_product(axis, refDirection);
Kernel_::Vector_3 x = CGAL::cross_product(y, axis);
const double tolerance = 0.01;
if (x.squared_length() < 1.0-tolerance || x.squared_length() > 1.0+tolerance ||
y.squared_length() < 1.0-tolerance || y.squared_length() > 1.0+tolerance ||
axis.squared_length() < 1.0-tolerance || axis.squared_length() > 1.0+tolerance) {
std::cout << "Ref direction: " << refDirection << " squared length: " << refDirection.squared_length() << std::endl;
std::cout << "Axis (z): " << axis << " squared length: " << axis.squared_length() << std::endl;
std::cout << "y: " << y << " squared length: " << y.squared_length() << std::endl;
std::cout << "x: " << x << " squared length: " << x.squared_length() << std::endl;
std::cout << "Origin: " << o << std::endl;
}
// TODO: Should be checked.
trsf = Kernel_::Aff_transformation_3(x.cartesian(0), y.cartesian(0), axis.cartesian(0), o.cartesian(0),
x.cartesian(1), y.cartesian(1), axis.cartesian(1), o.cartesian(1),
x.cartesian(2), y.cartesian(2), axis.cartesian(2), o.cartesian(2));
// for (int i = 0; i < 3; ++i) {
// for (int j = 0; j < 4; ++j) {
// std::cout << trsf.cartesian(i, j) << " ";
// } std::cout << std::endl;
// }
// CACHE(IfcAxis2Placement3D,l,trsf)
return true;
}
bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcAxis1Placement* l, cgal_placement_t& ax) {
// IN_CACHE(IfcAxis1Placement,l,gp_Ax1,ax)
cgal_point_t o;
cgal_direction_t axis = Kernel_::Vector_3(0,0,1);
IfcGeom::CgalKernel::convert(l->Location(),o);
if ( l->hasAxis() ) IfcGeom::CgalKernel::convert(l->Axis(), axis);
const double tolerance = 0.01;
if (axis.squared_length() < 1.0-tolerance || axis.squared_length() > 1.0+tolerance) {
std::cout << "Axis (z): " << axis << " squared length: " << axis.squared_length() << std::endl;
std::cout << "Origin: " << o << std::endl;
}
// TODO: Should be checked.
ax = Kernel_::Aff_transformation_3(1.0, 0.0, axis.cartesian(0), o.cartesian(0),
0.0, 1.0, axis.cartesian(1), o.cartesian(1),
0.0, 0.0, axis.cartesian(2), o.cartesian(2));
// CACHE(IfcAxis1Placement,l,ax)
return true;
}
bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcObjectPlacement* l, cgal_placement_t& trsf) {
// IN_CACHE(IfcObjectPlacement,l,cgal_placement_t,trsf)
if ( ! l->as<IfcSchema::IfcLocalPlacement>() ) {
Logger::Message(Logger::LOG_ERROR, "Unsupported IfcObjectPlacement:", l);
return false;
}
// std::cout << "initial trsf (identity?)" << std::endl;
// for (int i = 0; i < 3; ++i) {
// for (int j = 0; j < 4; ++j) {
// std::cout << trsf.cartesian(i, j) << " ";
// } std::cout << std::endl;
// }
IfcSchema::IfcLocalPlacement* current = (IfcSchema::IfcLocalPlacement*)l;
for (;;) {
cgal_placement_t trsf2;
IfcSchema::IfcAxis2Placement* relplacement = current->RelativePlacement();
if ( relplacement->as<IfcSchema::IfcAxis2Placement3D>() ) {
IfcGeom::CgalKernel::convert((IfcSchema::IfcAxis2Placement3D*)relplacement,trsf2);
// std::cout << "trsf2" << std::endl;
// for (int i = 0; i < 3; ++i) {
// for (int j = 0; j < 4; ++j) {
// std::cout << trsf2.cartesian(i, j) << " ";
// } std::cout << std::endl;
// }
trsf = trsf2 * trsf;
// std::cout << "trsf (after multiplication)" << std::endl;
// for (int i = 0; i < 3; ++i) {
// for (int j = 0; j < 4; ++j) {
// std::cout << trsf.cartesian(i, j) << " ";
// } std::cout << std::endl;
// }
}
if ( current->hasPlacementRelTo() ) {
IfcSchema::IfcObjectPlacement* relto = current->PlacementRelTo();
if ( relto->as<IfcSchema::IfcLocalPlacement>() )
current = (IfcSchema::IfcLocalPlacement*)current->PlacementRelTo();
else break;
} else break;
}
// CACHE(IfcObjectPlacement,l,trsf)
return true;
}
bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcCartesianTransformationOperator2D* l, cgal_placement_t& trsf) {
// IN_CACHE(IfcCartesianTransformationOperator2D,l,cgal_placement_t,trsf)
cgal_point_t origin;
cgal_direction_t axis1 (1.,0.,0.);
cgal_direction_t axis2 (0.,1.,0.);
IfcGeom::CgalKernel::convert(l->LocalOrigin(),origin);
if ( l->hasAxis1() ) IfcGeom::CgalKernel::convert(l->Axis1(),axis1);
if ( l->hasAxis2() ) IfcGeom::CgalKernel::convert(l->Axis2(),axis2);
double scale = 1.0;
if (l->hasScale()) {
scale = l->Scale();
}
// TODO: Untested
trsf = Kernel_::Aff_transformation_3(scale*axis1.cartesian(0), axis2.cartesian(0), 0.0, origin.cartesian(0),
axis1.cartesian(1), scale*axis2.cartesian(1), 0.0, origin.cartesian(1),
0.0, 0.0, 1.0, 0.0);
// CACHE(IfcCartesianTransformationOperator2D,l,trsf)
return true;
}
bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcCartesianTransformationOperator2DnonUniform* l, cgal_placement_t& trsf) {
// IN_CACHE(IfcCartesianTransformationOperator2DnonUniform,l,cgal_placement_t,gtrsf)
cgal_point_t origin;
cgal_direction_t axis1 (1.,0.,0.);
cgal_direction_t axis2 (0.,1.,0.);
IfcGeom::CgalKernel::convert(l->LocalOrigin(),origin);
if ( l->hasAxis1() ) IfcGeom::CgalKernel::convert(l->Axis1(),axis1);
if ( l->hasAxis2() ) IfcGeom::CgalKernel::convert(l->Axis2(),axis2);
const double scale1 = l->hasScale() ? l->Scale() : 1.0f;
const double scale2 = l->hasScale2() ? l->Scale2() : scale1;
// TODO: Untested
trsf = Kernel_::Aff_transformation_3(scale1*axis1.cartesian(0), axis2.cartesian(0), 0.0, origin.cartesian(0),
axis1.cartesian(1), scale2*axis2.cartesian(1), 0.0, origin.cartesian(1),
0.0, 0.0, 1.0, 0.0);
// CACHE(IfcCartesianTransformationOperator2DnonUniform,l,gtrsf)
return true;
}
bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcCartesianTransformationOperator3D* l, cgal_placement_t& trsf) {
// IN_CACHE(IfcCartesianTransformationOperator3D,l,gp_Trsf,trsf)
cgal_point_t origin;
IfcGeom::CgalKernel::convert(l->LocalOrigin(),origin);
cgal_direction_t axis1 (1.,0.,0.);
cgal_direction_t axis2 (0.,1.,0.);
cgal_direction_t axis3 (0.,0.,1.);
if ( l->hasAxis1() ) IfcGeom::CgalKernel::convert(l->Axis1(),axis1);
if ( l->hasAxis2() ) IfcGeom::CgalKernel::convert(l->Axis2(),axis2);
if ( l->hasAxis3() ) IfcGeom::CgalKernel::convert(l->Axis3(),axis3);
double scale = 1.0;
if (l->hasScale()) {
scale = l->Scale();
}
// TODO: Untested
trsf = Kernel_::Aff_transformation_3(scale*axis1.cartesian(0), axis2.cartesian(0), axis3.cartesian(0), origin.cartesian(0),
axis1.cartesian(1), scale*axis2.cartesian(1), axis3.cartesian(1), origin.cartesian(1),
axis1.cartesian(2), axis2.cartesian(2), scale*axis3.cartesian(2), origin.cartesian(2));
// std::cout << std::endl;
// for (int i = 0; i < 3; ++i) {
// for (int j = 0; j < 4; ++j) {
// std::cout << trsf.cartesian(i, j) << " ";
// } std::cout << std::endl;
// }
// CACHE(IfcCartesianTransformationOperator3D,l,trsf)
return true;
}
bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcCartesianTransformationOperator3DnonUniform* l, cgal_placement_t& gtrsf) {
// IN_CACHE(IfcCartesianTransformationOperator3DnonUniform,l,gp_GTrsf,gtrsf)
cgal_point_t origin;
IfcGeom::CgalKernel::convert(l->LocalOrigin(),origin);
cgal_direction_t axis1 (1.,0.,0.);
cgal_direction_t axis2 (0.,1.,0.);
cgal_direction_t axis3 (0.,0.,1.);
if ( l->hasAxis1() ) IfcGeom::CgalKernel::convert(l->Axis1(),axis1);
if ( l->hasAxis2() ) IfcGeom::CgalKernel::convert(l->Axis2(),axis2);
if ( l->hasAxis3() ) IfcGeom::CgalKernel::convert(l->Axis3(),axis3);
const double scale1 = l->hasScale() ? l->Scale() : 1.0f;
const double scale2 = l->hasScale2() ? l->Scale2() : scale1;
const double scale3 = l->hasScale3() ? l->Scale3() : scale1;
// TODO: Untested
gtrsf = Kernel_::Aff_transformation_3(scale1*axis1.cartesian(0), axis2.cartesian(0), axis3.cartesian(0), origin.cartesian(0),
axis1.cartesian(1), scale2*axis2.cartesian(1), axis3.cartesian(1), origin.cartesian(1),
axis1.cartesian(2), axis2.cartesian(2), scale3*axis3.cartesian(2), origin.cartesian(2));
// for (int i = 0; i < 3; ++i) {
// for (int j = 0; j < 4; ++j) {
// std::cout << gtrsf.cartesian(i, j) << " ";
// } std::cout << std::endl;
// }
// CACHE(IfcCartesianTransformationOperator3DnonUniform,l,gtrsf)
return true;
}
@@ -0,0 +1,919 @@
#include "CgalKernel.h"
#include "../../../ifcgeom/schema_agnostic/cgal/CgalConversionResult.h"
#define CgalKernel MAKE_TYPE_NAME(CgalKernel)
bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcExtrudedAreaSolid *l, cgal_shape_t &shape) {
const double height = l->Depth() * getValue(GV_LENGTH_UNIT);
if (height < getValue(GV_PRECISION)) {
Logger::Message(Logger::LOG_ERROR, "Non-positive extrusion height encountered for:", l);
return false;
}
// Outer
cgal_face_t bottom_face;
if ( !convert_face(l->SweptArea(),bottom_face) ) return false;
// std::cout << "Face vertices: " << face.outer.size() << std::endl;
cgal_placement_t trsf;
bool has_position = true;
#ifdef USE_IFC4
has_position = l->hasPosition();
#endif
if (has_position) {
IfcGeom::CgalKernel::convert(l->Position(), trsf);
}
cgal_direction_t dir;
convert(l->ExtrudedDirection(),dir);
// std::cout << "Direction: " << dir << std::endl;
std::list<cgal_face_t> face_list;
face_list.push_back(bottom_face);
for (std::vector<Kernel_::Point_3>::const_iterator current_vertex = bottom_face.outer.begin();
current_vertex != bottom_face.outer.end();
++current_vertex) {
std::vector<Kernel_::Point_3>::const_iterator next_vertex = current_vertex;
++next_vertex;
if (next_vertex == bottom_face.outer.end()) {
next_vertex = bottom_face.outer.begin();
} cgal_face_t side_face;
side_face.outer.push_back(*next_vertex);
side_face.outer.push_back(*current_vertex);
side_face.outer.push_back(*current_vertex+height*dir);
side_face.outer.push_back(*next_vertex+height*dir);
face_list.push_back(side_face);
}
cgal_face_t top_face;
for (std::vector<Kernel_::Point_3>::const_reverse_iterator vertex = bottom_face.outer.rbegin();
vertex != bottom_face.outer.rend();
++vertex) {
top_face.outer.push_back(*vertex+height*dir);
} face_list.push_back(top_face);
if (bottom_face.inner.empty()) {
shape = create_polyhedron(face_list);
if (has_position) for (auto &vertex: vertices(shape)) vertex->point() = vertex->point().transform(trsf);
return true;
}
CGAL::Nef_polyhedron_3<Kernel_> nef_shape = create_nef_polyhedron(face_list);
// Inner
// TODO: Would be faster to triangulate top/bottom face template rather than use Nef polyhedra for subtraction
for (auto &inner: bottom_face.inner) {
// std::cout << "Inner wire" << std::endl;
face_list.clear();
cgal_face_t hole_bottom_face;
hole_bottom_face.outer = inner;
remove_duplicate_points_from_loop(hole_bottom_face.outer);
face_list.push_back(hole_bottom_face);
for (std::vector<Kernel_::Point_3>::const_iterator current_vertex = inner.begin();
current_vertex != inner.end();
++current_vertex) {
std::vector<Kernel_::Point_3>::const_iterator next_vertex = current_vertex;
++next_vertex;
if (next_vertex == inner.end()) {
next_vertex = inner.begin();
} cgal_face_t hole_side_face;
hole_side_face.outer.push_back(*next_vertex);
hole_side_face.outer.push_back(*current_vertex);
hole_side_face.outer.push_back(*current_vertex+height*dir);
hole_side_face.outer.push_back(*next_vertex+height*dir);
face_list.push_back(hole_side_face);
}
cgal_face_t hole_top_face;
for (std::vector<Kernel_::Point_3>::const_reverse_iterator vertex = inner.rbegin();
vertex != inner.rend();
++vertex) {
hole_top_face.outer.push_back(*vertex+height*dir);
} face_list.push_back(hole_top_face);
try {
nef_shape -= create_nef_polyhedron(face_list);
} catch (...) {
Logger::Message(Logger::LOG_ERROR, "IfcExtrudedAreaSolid: cannot subtract opening for:", l);
return false;
}
}
if (has_position) {
// IfcSweptAreaSolid.Position (trsf) is an IfcAxis2Placement3D
// and therefore has a unit scale factor
nef_shape.transform(trsf);
}
try {
nef_shape.convert_to_polyhedron(shape);
return true;
} catch (...) {
Logger::Message(Logger::LOG_ERROR, "IfcExtrudedAreaSolid: cannot convert Nef to polyhedron for:", l);
return false;
}
}
#ifdef USE_IFC4
bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcExtrudedAreaSolidTapered* l, cgal_shape_t& shape) {
const double height = l->Depth() * getValue(GV_LENGTH_UNIT);
if (height < getValue(GV_PRECISION)) {
Logger::Message(Logger::LOG_ERROR, "Non-positive extrusion height encountered for:", l);
return false;
}
cgal_face_t face1, face2;
if (!convert_face(l->SweptArea(), face1)) return false;
if (!convert_face(l->EndSweptArea(), face2)) return false;
cgal_placement_t trsf;
bool has_position = true;
#ifdef USE_IFC4
has_position = l->hasPosition();
#endif
if (has_position) {
IfcGeom::CgalKernel::convert(l->Position(), trsf);
}
cgal_direction_t dir;
convert(l->ExtrudedDirection(), dir);
for (auto &vertex: face2.outer) vertex = vertex + height*dir;
for (auto &ring: face2.inner) {
for (auto &vertex: ring) vertex = vertex + height*dir;
}
// Outer
std::list<cgal_face_t> face_list;
face_list.push_back(face1);
std::vector<Kernel_::Point_3>::const_iterator current_face1_vertex = face1.outer.begin();
std::vector<Kernel_::Point_3>::const_iterator current_face2_vertex = face2.outer.begin();
while (current_face1_vertex != face1.outer.end() &&
current_face2_vertex != face2.outer.end()) {
std::vector<Kernel_::Point_3>::const_iterator next_face1_vertex = current_face1_vertex;
std::vector<Kernel_::Point_3>::const_iterator next_face2_vertex = current_face2_vertex;
++next_face1_vertex;
++next_face2_vertex;
if (next_face1_vertex == face1.outer.end()) next_face1_vertex = face1.outer.begin();
if (next_face2_vertex == face2.outer.end()) next_face2_vertex = face2.outer.begin();
cgal_face_t side_face;
side_face.outer.push_back(*next_face1_vertex);
side_face.outer.push_back(*current_face1_vertex);
side_face.outer.push_back(*current_face2_vertex);
side_face.outer.push_back(*next_face2_vertex);
face_list.push_back(side_face);
++current_face1_vertex;
++current_face2_vertex;
}
cgal_face_t top_face;
for (std::vector<Kernel_::Point_3>::const_reverse_iterator vertex = face2.outer.rbegin();
vertex != face2.outer.rend();
++vertex) {
top_face.outer.push_back(*vertex);
} face_list.push_back(top_face);
if (face1.inner.empty() || face2.inner.empty()) {
shape = create_polyhedron(face_list);
if (has_position) for (auto &vertex: vertices(shape)) vertex->point() = vertex->point().transform(trsf);
return true;
}
// std::ofstream f1;
// CGAL::Polyhedron_3<Kernel_> outer_polyhedron;
// PolyhedronBuilder builder(&face_list);
// outer_polyhedron.delegate(builder);
// f1.open("/Users/ken/Desktop/outer.off");
// f1 << outer_polyhedron << std::endl;
// f1.close();
CGAL::Nef_polyhedron_3<Kernel_> nef_shape = create_nef_polyhedron(face_list);
// Inner
// TODO: Would be faster to triangulate top/bottom face template rather than use Nef polyhedra for subtraction
std::vector<cgal_wire_t>::iterator inner_face1 = face1.inner.begin();
std::vector<cgal_wire_t>::iterator inner_face2 = face2.inner.begin();
while (inner_face1 != face1.inner.end() &&
inner_face2 != face2.inner.end()) {
face_list.clear();
cgal_face_t hole_face1;
hole_face1.outer = *inner_face1;
remove_duplicate_points_from_loop(hole_face1.outer);
face_list.push_back(hole_face1);
cgal_face_t hole_face2;
hole_face2.outer = *inner_face2;
remove_duplicate_points_from_loop(hole_face2.outer);
current_face1_vertex = hole_face1.outer.begin();
current_face2_vertex = hole_face2.outer.begin();
while (current_face1_vertex != hole_face1.outer.end() &&
current_face2_vertex != hole_face2.outer.end()) {
std::vector<Kernel_::Point_3>::const_iterator next_face1_vertex = current_face1_vertex;
std::vector<Kernel_::Point_3>::const_iterator next_face2_vertex = current_face2_vertex;
++next_face1_vertex;
++next_face2_vertex;
if (next_face1_vertex == hole_face1.outer.end()) next_face1_vertex = hole_face1.outer.begin();
if (next_face2_vertex == hole_face2.outer.end()) next_face2_vertex = hole_face2.outer.begin();
cgal_face_t side_face;
side_face.outer.push_back(*next_face1_vertex);
side_face.outer.push_back(*current_face1_vertex);
side_face.outer.push_back(*current_face2_vertex);
side_face.outer.push_back(*next_face2_vertex);
face_list.push_back(side_face);
++current_face1_vertex;
++current_face2_vertex;
}
cgal_face_t top_hole_face;
for (std::vector<Kernel_::Point_3>::const_reverse_iterator vertex = hole_face2.outer.rbegin();
vertex != hole_face2.outer.rend();
++vertex) {
top_hole_face.outer.push_back(*vertex);
} face_list.push_back(top_hole_face);
// std::ofstream f2;
// CGAL::Polyhedron_3<Kernel_> inner_polyhedron;
// PolyhedronBuilder builder(&face_list);
// inner_polyhedron.delegate(builder);
// f2.open("/Users/ken/Desktop/inner.off");
// f2 << inner_polyhedron << std::endl;
// f2.close();
try {
nef_shape -= create_nef_polyhedron(face_list);
} catch (...) {
std::cout << "IfcExtrudedAreaSolidTapered: cannot subtract opening for:" << std::endl;
return false;
}
++inner_face1;
++inner_face2;
}
if (has_position) {
// IfcSweptAreaSolid.Position (trsf) is an IfcAxis2Placement3D
// and therefore has a unit scale factor
nef_shape.transform(trsf);
}
try {
nef_shape.convert_to_polyhedron(shape);
return true;
} catch (...) {
std::cout << "IfcExtrudedAreaSolidTapered: cannot convert Nef to polyhedron!" << std::endl;
return false;
}
}
#endif
bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcConnectedFaceSet* l, cgal_shape_t& shape) {
IfcSchema::IfcFace::list::ptr faces = l->CfsFaces();
std::list<cgal_face_t> face_list;
for (IfcSchema::IfcFace::list::it it = faces->begin(); it != faces->end(); ++it) {
bool success = false;
cgal_face_t face;
try {
success = convert_face(*it, face);
} catch (...) {}
if (!success) {
Logger::Message(Logger::LOG_WARNING, "Failed to convert face:", (*it));
continue;
}
// std::cout << "Face in ConnectedFaceSet: " << std::endl;
// for (auto &point: face.outer) {
// std::cout << "\tPoint(" << point << ")" << std::endl;
// }
face_list.push_back(face);
}
shape = create_polyhedron(face_list);
return true;
}
bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcCsgSolid* l, cgal_shape_t& shape) {
return convert_shape(l->TreeRootExpression(), shape);
}
bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcBlock* l, cgal_shape_t& shape) {
const double dx = l->XLength() * getValue(GV_LENGTH_UNIT);
const double dy = l->YLength() * getValue(GV_LENGTH_UNIT);
const double dz = l->ZLength() * getValue(GV_LENGTH_UNIT);
std::list<cgal_face_t> face_list;
// x = 0
face_list.push_back(cgal_face_t());
face_list.back().outer.push_back(Kernel_::Point_3(0, 0, 0));
face_list.back().outer.push_back(Kernel_::Point_3(0, dy, 0));
face_list.back().outer.push_back(Kernel_::Point_3(0, dy, dz));
face_list.back().outer.push_back(Kernel_::Point_3(0, 0, dz));
// x = dx
face_list.push_back(cgal_face_t());
face_list.back().outer.push_back(Kernel_::Point_3(dx, 0, 0));
face_list.back().outer.push_back(Kernel_::Point_3(dx, 0, dz));
face_list.back().outer.push_back(Kernel_::Point_3(dx, dy, dz));
face_list.back().outer.push_back(Kernel_::Point_3(dx, dy, 0));
// y = 0
face_list.push_back(cgal_face_t());
face_list.back().outer.push_back(Kernel_::Point_3(0, 0, 0));
face_list.back().outer.push_back(Kernel_::Point_3(0, 0, dz));
face_list.back().outer.push_back(Kernel_::Point_3(dx, 0, dz));
face_list.back().outer.push_back(Kernel_::Point_3(dx, 0, 0));
// y = dy
face_list.push_back(cgal_face_t());
face_list.back().outer.push_back(Kernel_::Point_3(0, dy, 0));
face_list.back().outer.push_back(Kernel_::Point_3(dx, dy, 0));
face_list.back().outer.push_back(Kernel_::Point_3(dx, dy, dz));
face_list.back().outer.push_back(Kernel_::Point_3(0, dy, dz));
// z = 0
face_list.push_back(cgal_face_t());
face_list.back().outer.push_back(Kernel_::Point_3(0, 0, 0));
face_list.back().outer.push_back(Kernel_::Point_3(dx, 0, 0));
face_list.back().outer.push_back(Kernel_::Point_3(dx, dy, 0));
face_list.back().outer.push_back(Kernel_::Point_3(0, dy, 0));
// z = dz
face_list.push_back(cgal_face_t());
face_list.back().outer.push_back(Kernel_::Point_3(0, 0, dz));
face_list.back().outer.push_back(Kernel_::Point_3(0, dy, dz));
face_list.back().outer.push_back(Kernel_::Point_3(dx, dy, dz));
face_list.back().outer.push_back(Kernel_::Point_3(dx, 0, dz));
cgal_placement_t trsf;
IfcGeom::CgalKernel::convert(l->Position(),trsf);
shape = create_polyhedron(face_list);
for (auto &vertex: vertices(shape)) vertex->point() = vertex->point().transform(trsf);
return true;
}
bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcBooleanResult* l, cgal_shape_t& shape) {
cgal_shape_t s1, s2;
ConversionResults items1, items2;
cgal_wire_t boundary_wire;
IfcSchema::IfcBooleanOperand* operand1 = l->FirstOperand();
IfcSchema::IfcBooleanOperand* operand2 = l->SecondOperand();
bool is_halfspace = operand2->as<IfcSchema::IfcHalfSpaceSolid>();
if ( shape_type(operand1) == ST_SHAPELIST ) {
Logger::Message(Logger::LOG_ERROR, "s1: ST_SHAPELIST Unsupported", operand1);
// if (!(convert_shapes(operand1, items1) && flatten_shape_list(items1, s1, true))) {
return false;
// }
} else if ( shape_type(operand1) == ST_SHAPE ) {
if (!convert_shape(operand1, s1) ) {
return false;
}
} else {
Logger::Message(Logger::LOG_ERROR, "s1: Invalid representation item for boolean operation", operand1);
return false;
}
// const double first_operand_volume = shape_volume(s1);
// if ( first_operand_volume <= ALMOST_ZERO )
// Logger::Message(Logger::LOG_WARNING,"Empty solid for:",l->FirstOperand());
bool shape2_processed = false;
if ( shape_type(operand2) == ST_SHAPELIST ) {
Logger::Message(Logger::LOG_ERROR, "s2: ST_SHAPELIST Unsupported", operand1);
// shape2_processed = convert_shapes(operand2, items2) && flatten_shape_list(items2, s2, true);
} else if ( shape_type(operand2) == ST_SHAPE ) {
shape2_processed = convert_shape(operand2,s2);
} else {
Logger::Message(Logger::LOG_ERROR, "s2: Invalid representation item for boolean operation", operand2);
}
if (!shape2_processed) {
shape = s1;
Logger::Message(Logger::LOG_ERROR,"Failed to convert SecondOperand of:",l);
return true;
}
// if (!is_halfspace) {
// const double second_operand_volume = shape_volume(s2);
// if ( second_operand_volume <= ALMOST_ZERO )
// Logger::Message(Logger::LOG_WARNING,"Empty solid for:",operand2);
// }
const IfcSchema::IfcBooleanOperator::Value op = l->Operator();
if (!s1.is_valid()) {
Logger::Message(Logger::LOG_ERROR, "s1: Not valid?", operand1);
return false;
} else {
// std::ofstream f1;
// CGAL::Polyhedron_3<Kernel_> p1;
// s1.convert_to_Polyhedron(p1);
// f1.open("/Users/ken/Desktop/s1.off");
// f1 << p1 << std::endl;
// f1.close();
}
bool is_plane = false;
cgal_plane_t plane;
if (!s2.is_valid()) {
Logger::Message(Logger::LOG_ERROR, "s2: Not valid?", operand2);
return false;
} else if (is_halfspace) {
// std::cout << "s2: halfspace" << std::endl;
IfcSchema::IfcHalfSpaceSolid *hss = static_cast<IfcSchema::IfcHalfSpaceSolid *>(operand2);
IfcSchema::IfcSurface* surface = hss->BaseSurface();
if (surface->as<IfcSchema::IfcPlane>() ) {
is_plane = true;
IfcGeom::CgalKernel::convert((IfcSchema::IfcPlane *)surface, plane);
if (hss->AgreementFlag()) plane = plane.opposite();
// std::ofstream fresult;
// fresult.open("/Users/ken/Desktop/s2.off");
// fresult << "OFF" << std::endl << "4 2 4" << std::endl;
// // x = -5, y = -5, z = (5a +5b -d)/c
// fresult << "-5 -5 " << (5.0*CGAL::to_double(plane.a())+5.0*CGAL::to_double(plane.b())-CGAL::to_double(plane.d()))/CGAL::to_double(plane.c()) << std::endl;
// // x = -5, y = +5, z = (5a -5b -d)/c
// fresult << "-5 5 " << (5.0*CGAL::to_double(plane.a())-5.0*CGAL::to_double(plane.b())-CGAL::to_double(plane.d()))/CGAL::to_double(plane.c()) << std::endl;
// // x = 5, y = -5, z = (-5a +5b -d)/c
// fresult << "5 -5 " << (-5.0*CGAL::to_double(plane.a())+5.0*CGAL::to_double(plane.b())-CGAL::to_double(plane.d()))/CGAL::to_double(plane.c()) << std::endl;
// // x = 5, y = +5, z = (-5a -5b -d)/c
// fresult << "5 5 " << (-5.0*CGAL::to_double(plane.a())-5.0*CGAL::to_double(plane.b())-CGAL::to_double(plane.d()))/CGAL::to_double(plane.c()) << std::endl;
// fresult << "3 0 1 2" << std::endl;
// fresult << "3 3 2 1" << std::endl;
// fresult.close();
}
} else {
// std::ofstream f2;
// CGAL::Polyhedron_3<Kernel_> p2;
// s2.convert_to_Polyhedron(p2);
// f2.open("/Users/ken/Desktop/s2.off");
// f2 << p2 << std::endl;
// f2.close();
}
if (op == IfcSchema::IfcBooleanOperator::IfcBooleanOperator_DIFFERENCE) {
// std::cout << "Difference" << std::endl;
CGAL::Nef_polyhedron_3<Kernel_> nef_result;
try {
nef_result = CGAL::Nef_polyhedron_3<Kernel_>(s1);
} catch (...) {
Logger::Message(Logger::LOG_ERROR, "s1: cannot convert to Nef?", operand1);
return false;
} if (is_halfspace) {
if (is_plane) nef_result = nef_result.intersection(plane, CGAL::Nef_polyhedron_3<Kernel_>::Intersection_mode::CLOSED_HALFSPACE);
} else {
CGAL::Nef_polyhedron_3<Kernel_> nef_s2;
try {
nef_s2 = CGAL::Nef_polyhedron_3<Kernel_>(s2);
} catch (...) {
Logger::Message(Logger::LOG_ERROR, "s2: cannot convert to Nef?", operand2);
} nef_result -= nef_s2;
}
if (!nef_result.is_simple()) {
Logger::Message(Logger::LOG_ERROR, "s2: not simple?", operand2);
return false;
} else {
// CGAL::Polyhedron_3<Kernel_> result;
// nef_result.convert_to_polyhedron(result);
// std::ofstream fresult;
// fresult.open("/Users/ken/Desktop/result.off");
// fresult << result << std::endl;
// fresult.close();
} try {
nef_result.convert_to_polyhedron(shape);
return true;
} catch (...) {
std::cout << "IfcBooleanResult: cannot convert Nef to polyhedron!" << std::endl;
return false;
}
} else if (op == IfcSchema::IfcBooleanOperator::IfcBooleanOperator_UNION) {
// std::cout << "Union" << std::endl;
CGAL::Nef_polyhedron_3<Kernel_> nef_result = CGAL::Nef_polyhedron_3<Kernel_>(s1)+CGAL::Nef_polyhedron_3<Kernel_>(s2);
if (!nef_result.is_simple()) {
std::cout << "Not simple: " << nef_result.number_of_volumes() << " volumes" << std::endl;
return false;
} else {
// CGAL::Polyhedron_3<Kernel_> result;
// nef_result.convert_to_polyhedron(result);
// std::ofstream fresult;
// fresult.open("/Users/ken/Desktop/result.off");
// fresult << result << std::endl;
// fresult.close();
} try {
nef_result.convert_to_polyhedron(shape);
return true;
} catch (...) {
std::cout << "IfcBooleanResult: cannot convert Nef to polyhedron!" << std::endl;
return false;
}
} else if (op == IfcSchema::IfcBooleanOperator::IfcBooleanOperator_INTERSECTION) {
// std::cout << "Intersection" << std::endl;
CGAL::Nef_polyhedron_3<Kernel_> nef_result = CGAL::Nef_polyhedron_3<Kernel_>(s1)*CGAL::Nef_polyhedron_3<Kernel_>(s2);
if (!nef_result.is_simple()) {
std::cout << "Not simple: " << nef_result.number_of_volumes() << " volumes" << std::endl;
return false;
} else {
// CGAL::Polyhedron_3<Kernel_> result;
// nef_result.convert_to_polyhedron(result);
// std::ofstream fresult;
// fresult.open("/Users/ken/Desktop/result.off");
// fresult << result << std::endl;
// fresult.close();
} try {
nef_result.convert_to_polyhedron(shape);
return true;
} catch (...) {
std::cout << "IfcBooleanResult: cannot convert Nef to polyhedron!" << std::endl;
return false;
}
} return false;
}
bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcSphere* l, cgal_shape_t& shape) {
const double r = l->Radius() * getValue(GV_LENGTH_UNIT);
// Make icosahedron
float golden_ratio = (1.0+sqrtf(5.0))/2.0;
float normalising_factor = sqrtf(golden_ratio*golden_ratio+1.0);
std::vector<Kernel_::Point_3> icosahedron_vertices;
icosahedron_vertices.push_back(Kernel_::Point_3(-1.0/normalising_factor, golden_ratio/normalising_factor, 0.0));
icosahedron_vertices.push_back(Kernel_::Point_3( 1.0/normalising_factor, golden_ratio/normalising_factor, 0.0));
icosahedron_vertices.push_back(Kernel_::Point_3(-1.0/normalising_factor, -golden_ratio/normalising_factor, 0.0));
icosahedron_vertices.push_back(Kernel_::Point_3( 1.0/normalising_factor, -golden_ratio/normalising_factor, 0.0));
icosahedron_vertices.push_back(Kernel_::Point_3(0.0, -1.0/normalising_factor, golden_ratio/normalising_factor));
icosahedron_vertices.push_back(Kernel_::Point_3(0.0, 1.0/normalising_factor, golden_ratio/normalising_factor));
icosahedron_vertices.push_back(Kernel_::Point_3(0.0, -1.0/normalising_factor, -golden_ratio/normalising_factor));
icosahedron_vertices.push_back(Kernel_::Point_3(0.0, 1.0/normalising_factor, -golden_ratio/normalising_factor));
icosahedron_vertices.push_back(Kernel_::Point_3( golden_ratio/normalising_factor, 0.0, -1.0/normalising_factor));
icosahedron_vertices.push_back(Kernel_::Point_3( golden_ratio/normalising_factor, 0.0, 1.0/normalising_factor));
icosahedron_vertices.push_back(Kernel_::Point_3(-golden_ratio/normalising_factor, 0.0, -1.0/normalising_factor));
icosahedron_vertices.push_back(Kernel_::Point_3(-golden_ratio/normalising_factor, 0.0, 1.0/normalising_factor));
std::list<cgal_face_t> face_list;
face_list.push_back(cgal_face_t());
face_list.back().outer.push_back(icosahedron_vertices[0]);
face_list.back().outer.push_back(icosahedron_vertices[11]);
face_list.back().outer.push_back(icosahedron_vertices[5]);
face_list.push_back(cgal_face_t());
face_list.back().outer.push_back(icosahedron_vertices[0]);
face_list.back().outer.push_back(icosahedron_vertices[5]);
face_list.back().outer.push_back(icosahedron_vertices[1]);
face_list.push_back(cgal_face_t());
face_list.back().outer.push_back(icosahedron_vertices[0]);
face_list.back().outer.push_back(icosahedron_vertices[1]);
face_list.back().outer.push_back(icosahedron_vertices[7]);
face_list.push_back(cgal_face_t());
face_list.back().outer.push_back(icosahedron_vertices[0]);
face_list.back().outer.push_back(icosahedron_vertices[7]);
face_list.back().outer.push_back(icosahedron_vertices[10]);
face_list.push_back(cgal_face_t());
face_list.back().outer.push_back(icosahedron_vertices[0]);
face_list.back().outer.push_back(icosahedron_vertices[10]);
face_list.back().outer.push_back(icosahedron_vertices[11]);
face_list.push_back(cgal_face_t());
face_list.back().outer.push_back(icosahedron_vertices[1]);
face_list.back().outer.push_back(icosahedron_vertices[5]);
face_list.back().outer.push_back(icosahedron_vertices[9]);
face_list.push_back(cgal_face_t());
face_list.back().outer.push_back(icosahedron_vertices[5]);
face_list.back().outer.push_back(icosahedron_vertices[11]);
face_list.back().outer.push_back(icosahedron_vertices[4]);
face_list.push_back(cgal_face_t());
face_list.back().outer.push_back(icosahedron_vertices[11]);
face_list.back().outer.push_back(icosahedron_vertices[10]);
face_list.back().outer.push_back(icosahedron_vertices[2]);
face_list.push_back(cgal_face_t());
face_list.back().outer.push_back(icosahedron_vertices[10]);
face_list.back().outer.push_back(icosahedron_vertices[7]);
face_list.back().outer.push_back(icosahedron_vertices[6]);
face_list.push_back(cgal_face_t());
face_list.back().outer.push_back(icosahedron_vertices[7]);
face_list.back().outer.push_back(icosahedron_vertices[1]);
face_list.back().outer.push_back(icosahedron_vertices[8]);
face_list.push_back(cgal_face_t());
face_list.back().outer.push_back(icosahedron_vertices[3]);
face_list.back().outer.push_back(icosahedron_vertices[9]);
face_list.back().outer.push_back(icosahedron_vertices[4]);
face_list.push_back(cgal_face_t());
face_list.back().outer.push_back(icosahedron_vertices[3]);
face_list.back().outer.push_back(icosahedron_vertices[4]);
face_list.back().outer.push_back(icosahedron_vertices[2]);
face_list.push_back(cgal_face_t());
face_list.back().outer.push_back(icosahedron_vertices[3]);
face_list.back().outer.push_back(icosahedron_vertices[2]);
face_list.back().outer.push_back(icosahedron_vertices[6]);
face_list.push_back(cgal_face_t());
face_list.back().outer.push_back(icosahedron_vertices[3]);
face_list.back().outer.push_back(icosahedron_vertices[6]);
face_list.back().outer.push_back(icosahedron_vertices[8]);
face_list.push_back(cgal_face_t());
face_list.back().outer.push_back(icosahedron_vertices[3]);
face_list.back().outer.push_back(icosahedron_vertices[8]);
face_list.back().outer.push_back(icosahedron_vertices[9]);
face_list.push_back(cgal_face_t());
face_list.back().outer.push_back(icosahedron_vertices[4]);
face_list.back().outer.push_back(icosahedron_vertices[9]);
face_list.back().outer.push_back(icosahedron_vertices[5]);
face_list.push_back(cgal_face_t());
face_list.back().outer.push_back(icosahedron_vertices[2]);
face_list.back().outer.push_back(icosahedron_vertices[4]);
face_list.back().outer.push_back(icosahedron_vertices[11]);
face_list.push_back(cgal_face_t());
face_list.back().outer.push_back(icosahedron_vertices[6]);
face_list.back().outer.push_back(icosahedron_vertices[2]);
face_list.back().outer.push_back(icosahedron_vertices[10]);
face_list.push_back(cgal_face_t());
face_list.back().outer.push_back(icosahedron_vertices[8]);
face_list.back().outer.push_back(icosahedron_vertices[6]);
face_list.back().outer.push_back(icosahedron_vertices[7]);
face_list.push_back(cgal_face_t());
face_list.back().outer.push_back(icosahedron_vertices[9]);
face_list.back().outer.push_back(icosahedron_vertices[8]);
face_list.back().outer.push_back(icosahedron_vertices[1]);
const unsigned int refinements = 2;
for (unsigned int current_refinement = 0; current_refinement < refinements; ++current_refinement) {
std::list<cgal_face_t> refined_face_list;
for (auto &face: face_list) {
Kernel_::Point_3 vertex0 = face.outer[0];
Kernel_::Point_3 vertex1 = face.outer[1];
Kernel_::Point_3 vertex2 = face.outer[2];
Kernel_::Point_3 midpoint01 = CGAL::midpoint(vertex0, vertex1);
Kernel_::Point_3 midpoint12 = CGAL::midpoint(vertex1, vertex2);
Kernel_::Point_3 midpoint20 = CGAL::midpoint(vertex2, vertex0);
double midpoint01_distance_to_origin = sqrt(CGAL::to_double(CGAL::squared_distance(midpoint01, Kernel_::Point_3(0, 0, 0))));
midpoint01 = Kernel_::Point_3(midpoint01.x()/midpoint01_distance_to_origin,
midpoint01.y()/midpoint01_distance_to_origin,
midpoint01.z()/midpoint01_distance_to_origin);
double midpoint12_distance_to_origin = sqrt(CGAL::to_double(CGAL::squared_distance(midpoint12, Kernel_::Point_3(0, 0, 0))));
midpoint12 = Kernel_::Point_3(midpoint12.x()/midpoint12_distance_to_origin,
midpoint12.y()/midpoint12_distance_to_origin,
midpoint12.z()/midpoint12_distance_to_origin);
double midpoint20_distance_to_origin = sqrt(CGAL::to_double(CGAL::squared_distance(midpoint20, Kernel_::Point_3(0, 0, 0))));
midpoint20 = Kernel_::Point_3(midpoint20.x()/midpoint20_distance_to_origin,
midpoint20.y()/midpoint20_distance_to_origin,
midpoint20.z()/midpoint20_distance_to_origin);
refined_face_list.push_back(cgal_face_t());
refined_face_list.back().outer.push_back(vertex0);
refined_face_list.back().outer.push_back(midpoint01);
refined_face_list.back().outer.push_back(midpoint20);
refined_face_list.push_back(cgal_face_t());
refined_face_list.back().outer.push_back(vertex1);
refined_face_list.back().outer.push_back(midpoint12);
refined_face_list.back().outer.push_back(midpoint01);
refined_face_list.push_back(cgal_face_t());
refined_face_list.back().outer.push_back(vertex2);
refined_face_list.back().outer.push_back(midpoint20);
refined_face_list.back().outer.push_back(midpoint12);
refined_face_list.push_back(cgal_face_t());
refined_face_list.back().outer.push_back(midpoint01);
refined_face_list.back().outer.push_back(midpoint12);
refined_face_list.back().outer.push_back(midpoint20);
} face_list = refined_face_list;
}
cgal_placement_t trsf;
IfcGeom::CgalKernel::convert(l->Position(),trsf);
shape = create_polyhedron(face_list);
for (auto &vertex: vertices(shape)) {
vertex->point() = Kernel_::Point_3(r*vertex->point().x(),
r*vertex->point().y(),
r*vertex->point().z());
vertex->point() = vertex->point().transform(trsf);
}
return true;
}
bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcRectangularPyramid* l, cgal_shape_t& shape) {
const double dx = l->XLength() * getValue(GV_LENGTH_UNIT);
const double dy = l->YLength() * getValue(GV_LENGTH_UNIT);
const double dz = l->Height() * getValue(GV_LENGTH_UNIT);
std::list<cgal_face_t> face_list;
// Base
face_list.push_back(cgal_face_t());
face_list.back().outer.push_back(Kernel_::Point_3(0, 0, 0));
face_list.back().outer.push_back(Kernel_::Point_3(dx, 0, 0));
face_list.back().outer.push_back(Kernel_::Point_3(dx, dy, 0));
face_list.back().outer.push_back(Kernel_::Point_3(0, dy, 0));
// Lateral faces
face_list.push_back(cgal_face_t());
face_list.back().outer.push_back(Kernel_::Point_3(0, 0, 0));
face_list.back().outer.push_back(Kernel_::Point_3(0, dy, 0));
face_list.back().outer.push_back(Kernel_::Point_3(0.5*dx, 0.5*dy, dz));
face_list.push_back(cgal_face_t());
face_list.back().outer.push_back(Kernel_::Point_3(0, dy, 0));
face_list.back().outer.push_back(Kernel_::Point_3(dx, dy, 0));
face_list.back().outer.push_back(Kernel_::Point_3(0.5*dx, 0.5*dy, dz));
face_list.push_back(cgal_face_t());
face_list.back().outer.push_back(Kernel_::Point_3(dx, dy, 0));
face_list.back().outer.push_back(Kernel_::Point_3(dx, 0, 0));
face_list.back().outer.push_back(Kernel_::Point_3(0.5*dx, 0.5*dy, dz));
face_list.push_back(cgal_face_t());
face_list.back().outer.push_back(Kernel_::Point_3(dx, 0, 0));
face_list.back().outer.push_back(Kernel_::Point_3(0, 0, 0));
face_list.back().outer.push_back(Kernel_::Point_3(0.5*dx, 0.5*dy, dz));
cgal_placement_t trsf;
IfcGeom::CgalKernel::convert(l->Position(),trsf);
shape = create_polyhedron(face_list);
for (auto &vertex: vertices(shape)) vertex->point() = vertex->point().transform(trsf);
return true;
}
bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcRightCircularCylinder* l, cgal_shape_t& shape) {
const double r = l->Radius() * getValue(GV_LENGTH_UNIT);
const double h = l->Height() * getValue(GV_LENGTH_UNIT);
std::list<cgal_face_t> face_list;
const int segments = 12;
// Base
face_list.push_back(cgal_face_t());
for (int current_segment = 0; current_segment < segments; ++current_segment) {
double current_angle = current_segment*2.0*3.141592653589793/((double)segments);
face_list.back().outer.push_back(Kernel_::Point_3(r*cos(current_angle), r*sin(current_angle), 0));
}
// Side faces
for (int current_segment = 0; current_segment < segments; ++current_segment) {
double current_angle = current_segment*2.0*3.141592653589793/((double)segments);
int next_segment = (current_segment+1)%segments;
double next_angle = next_segment*2.0*3.141592653589793/((double)segments);
face_list.push_back(cgal_face_t());
face_list.back().outer.push_back(Kernel_::Point_3(r*cos(next_angle), r*sin(next_angle), 0));
face_list.back().outer.push_back(Kernel_::Point_3(r*cos(current_angle), r*sin(current_angle), 0));
face_list.back().outer.push_back(Kernel_::Point_3(r*cos(current_angle), r*sin(current_angle), h));
face_list.back().outer.push_back(Kernel_::Point_3(r*cos(next_angle), r*sin(next_angle), h));
}
// Top
face_list.push_back(cgal_face_t());
for (int current_segment = segments-1; current_segment >= 0; --current_segment) {
double current_angle = current_segment*2.0*3.141592653589793/((double)segments);
face_list.back().outer.push_back(Kernel_::Point_3(r*cos(current_angle), r*sin(current_angle), h));
}
cgal_placement_t trsf;
IfcGeom::CgalKernel::convert(l->Position(),trsf);
shape = create_polyhedron(face_list);
for (auto &vertex: vertices(shape)) vertex->point() = vertex->point().transform(trsf);
return true;
}
bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcRightCircularCone* l, cgal_shape_t& shape) {
const double r = l->BottomRadius() * getValue(GV_LENGTH_UNIT);
const double h = l->Height() * getValue(GV_LENGTH_UNIT);
std::list<cgal_face_t> face_list;
const int segments = 12;
// Base
face_list.push_back(cgal_face_t());
for (int current_segment = 0; current_segment < segments; ++current_segment) {
double current_angle = current_segment*2.0*3.141592653589793/((double)segments);
face_list.back().outer.push_back(Kernel_::Point_3(r*cos(current_angle), r*sin(current_angle), 0));
}
// Side faces
for (int current_segment = 0; current_segment < segments; ++current_segment) {
double current_angle = current_segment*2.0*3.141592653589793/((double)segments);
int next_segment = (current_segment+1)%segments;
double next_angle = next_segment*2.0*3.141592653589793/((double)segments);
face_list.push_back(cgal_face_t());
face_list.back().outer.push_back(Kernel_::Point_3(r*cos(next_angle), r*sin(next_angle), 0));
face_list.back().outer.push_back(Kernel_::Point_3(r*cos(current_angle), r*sin(current_angle), 0));
face_list.back().outer.push_back(Kernel_::Point_3(0, 0, h));
}
cgal_placement_t trsf;
IfcGeom::CgalKernel::convert(l->Position(),trsf);
shape = create_polyhedron(face_list);
for (auto &vertex: vertices(shape)) vertex->point() = vertex->point().transform(trsf);
return true;
}
#ifdef USE_IFC4
bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcTriangulatedFaceSet* l, cgal_shape_t& shape) {
IfcSchema::IfcCartesianPointList3D* point_list = l->Coordinates();
const std::vector< std::vector<double> > coordinates = point_list->CoordList();
std::vector<cgal_point_t> points;
points.reserve(coordinates.size());
for (std::vector< std::vector<double> >::const_iterator it = coordinates.begin(); it != coordinates.end(); ++it) {
const std::vector<double>& coords = *it;
if (coords.size() != 3) {
Logger::Message(Logger::LOG_ERROR, "Invalid dimensions encountered on Coordinates", l);
return false;
}
points.push_back(Kernel_::Point_3(coords[0] * getValue(GV_LENGTH_UNIT),
coords[1] * getValue(GV_LENGTH_UNIT),
coords[2] * getValue(GV_LENGTH_UNIT)));
}
std::vector< std::vector<int> > indices = l->CoordIndex();
std::list<cgal_face_t> face_list;
for(std::vector< std::vector<int> >::const_iterator it = indices.begin(); it != indices.end(); ++ it) {
const std::vector<int>& tri = *it;
if (tri.size() != 3) {
Logger::Message(Logger::LOG_ERROR, "Invalid dimensions encountered on CoordIndex", l);
return false;
}
const int min_index = *std::min_element(tri.begin(), tri.end());
const int max_index = *std::max_element(tri.begin(), tri.end());
if (min_index < 1 || max_index > (int) points.size()) {
Logger::Message(Logger::LOG_ERROR, "Contents of CoordIndex out of bounds", l);
return false;
}
const Kernel_::Point_3& a = points[tri[0] - 1]; // account for zero- vs
const Kernel_::Point_3& b = points[tri[1] - 1]; // one-based indices in
const Kernel_::Point_3& c = points[tri[2] - 1]; // c++ and express
face_list.push_back(cgal_face_t());
face_list.back().outer.push_back(a);
face_list.back().outer.push_back(b);
face_list.back().outer.push_back(c);
}
shape = create_polyhedron(face_list);
return true;
}
#endif
bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcHalfSpaceSolid* l, cgal_shape_t& shape) {
IfcSchema::IfcSurface* surface = l->BaseSurface();
if ( ! surface->as<IfcSchema::IfcPlane>() ) {
Logger::Message(Logger::LOG_ERROR, "Unsupported BaseSurface:", surface);
return false;
}
cgal_plane_t pln;
IfcGeom::CgalKernel::convert((IfcSchema::IfcPlane*)surface,pln);
// TODO: Don't fully understand the logic here. Might be incorrect.
if (l->AgreementFlag()) pln = pln.opposite();
// const gp_Pnt pnt = pln.Location().Translated( l->AgreementFlag() ? -pln.Axis().Direction() : pln.Axis().Direction());
// shape = BRepPrimAPI_MakeHalfSpace(BRepBuilderAPI_MakeFace(pln),pnt).Solid();
// TODO: For now we do nothing and process halfspaces in IfcBooleanResult, which likely doesn't capture all cases.
// Find a better solution later (with an abstract shape class?)
shape = CGAL::Polyhedron_3<Kernel_>();
return true;
}
@@ -0,0 +1,166 @@
#include "CgalKernel.h"
#include "../../../ifcgeom/schema_agnostic/cgal/CgalConversionResult.h"
#define CgalKernel MAKE_TYPE_NAME(CgalKernel)
bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcRepresentation* l, ConversionResults& shapes) {
IfcSchema::IfcRepresentationItem::list::ptr items = l->Items();
bool part_succes = false;
if (items->size()) {
for (IfcSchema::IfcRepresentationItem::list::it it = items->begin(); it != items->end(); ++it) {
IfcSchema::IfcRepresentationItem* representation_item = *it;
if (shape_type(representation_item) == ST_SHAPELIST) {
part_succes |= convert_shapes(*it, shapes);
} else {
cgal_shape_t s;
if (convert_shape(representation_item, s)) {
shapes.push_back(ConversionResult(representation_item->data().id(), new CgalShape(s), get_style(representation_item)));
part_succes |= true;
}
}
}
}
return part_succes;
}
bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcGeometricSet* l, ConversionResults& shapes) {
IfcEntityList::ptr elements = l->Elements();
if ( !elements->size() ) return false;
bool part_succes = false;
const IfcGeom::SurfaceStyle* parent_style = get_style(l);
for ( IfcEntityList::it it = elements->begin(); it != elements->end(); ++ it ) {
IfcSchema::IfcGeometricSetSelect* element = *it;
cgal_shape_t s;
if (convert_shape(element, s)) {
part_succes = true;
const IfcGeom::SurfaceStyle* style = 0;
if (element->as<IfcSchema::IfcPoint>()) {
style = get_style((IfcSchema::IfcPoint*) element);
} else if (element->as<IfcSchema::IfcCurve>()) {
style = get_style((IfcSchema::IfcCurve*) element);
} else if (element->as<IfcSchema::IfcSurface>()) {
style = get_style((IfcSchema::IfcSurface*) element);
}
shapes.push_back(ConversionResult(element->data().id(), new CgalShape(s), style ? style : parent_style));
}
}
return part_succes;
}
bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcShellBasedSurfaceModel* l, ConversionResults& shapes) {
IfcEntityList::ptr shells = l->SbsmBoundary();
const SurfaceStyle* collective_style = get_style(l);
for( IfcEntityList::it it = shells->begin(); it != shells->end(); ++ it ) {
cgal_shape_t s;
const SurfaceStyle* shell_style = 0;
if ((*it)->as<IfcSchema::IfcRepresentationItem>()) {
shell_style = get_style((IfcSchema::IfcRepresentationItem*)*it);
}
if (convert_shape(*it,s)) {
shapes.push_back(ConversionResult((*it)->data().id(), new CgalShape(s), shell_style ? shell_style : collective_style));
}
}
return true;
}
bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcManifoldSolidBrep* l, ConversionResults& shape) {
cgal_shape_t s;
const SurfaceStyle* collective_style = get_style(l);
const SurfaceStyle* indiv_style = get_style(l->Outer());
if (convert_shape(l->Outer(),s) ) {
IfcSchema::IfcClosedShell::list::ptr voids(new IfcSchema::IfcClosedShell::list);
if (l->as<IfcSchema::IfcFacetedBrepWithVoids>()) {
voids = l->as<IfcSchema::IfcFacetedBrepWithVoids>()->Voids();
}
#ifdef USE_IFC4
if (l->as<IfcSchema::IfcAdvancedBrepWithVoids>()) {
voids = l->as<IfcSchema::IfcAdvancedBrepWithVoids>()->Voids();
}
#endif
if (voids->size()) {
CGAL::Nef_polyhedron_3<Kernel_> nef_s = create_nef_polyhedron(s);
for (IfcSchema::IfcClosedShell::list::it it = voids->begin(); it != voids->end(); ++it) {
cgal_shape_t s2;
if (convert_shape(*it, s2)) {
nef_s -= CGAL::Nef_polyhedron_3<Kernel_>(s2);
}
}
s = create_polyhedron(nef_s);
}
shape.push_back(ConversionResult(l->data().id(), new CgalShape(s), indiv_style ? indiv_style : collective_style));
return true;
}
return false;
}
bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcMappedItem* l, ConversionResults& shapes) {
cgal_placement_t gtrsf;
IfcSchema::IfcCartesianTransformationOperator* transform = l->MappingTarget();
if ( transform->as<IfcSchema::IfcCartesianTransformationOperator3DnonUniform>() ) {
IfcGeom::CgalKernel::convert((IfcSchema::IfcCartesianTransformationOperator3DnonUniform*)transform,gtrsf);
} else if ( transform->as<IfcSchema::IfcCartesianTransformationOperator2DnonUniform>() ) {
IfcGeom::CgalKernel::convert((IfcSchema::IfcCartesianTransformationOperator2DnonUniform*)transform,gtrsf);
} else if ( transform->as<IfcSchema::IfcCartesianTransformationOperator3D>() ) {
IfcGeom::CgalKernel::convert((IfcSchema::IfcCartesianTransformationOperator3D*)transform,gtrsf);
} else if ( transform->as<IfcSchema::IfcCartesianTransformationOperator2D>() ) {
IfcGeom::CgalKernel::convert((IfcSchema::IfcCartesianTransformationOperator2D*)transform,gtrsf);
}
IfcSchema::IfcRepresentationMap* map = l->MappingSource();
IfcSchema::IfcAxis2Placement* placement = map->MappingOrigin();
cgal_placement_t trsf;
if (placement->as<IfcSchema::IfcAxis2Placement3D>()) {
IfcGeom::CgalKernel::convert((IfcSchema::IfcAxis2Placement3D*)placement,trsf);
} else {
cgal_placement_t trsf_2d;
IfcGeom::CgalKernel::convert((IfcSchema::IfcAxis2Placement2D*)placement,trsf_2d);
trsf = trsf_2d;
}
// TODO: Check
gtrsf = trsf * gtrsf;
// std::cout << std::endl;
// for (int i = 0; i < 3; ++i) {
// for (int j = 0; j < 4; ++j) {
// std::cout << gtrsf.cartesian(i, j) << " ";
// } std::cout << std::endl;
// }
const IfcGeom::SurfaceStyle* mapped_item_style = get_style(l);
const size_t previous_size = shapes.size();
bool b = convert_shapes(map->MappedRepresentation(), shapes);
for (size_t i = previous_size; i < shapes.size(); ++ i ) {
IfcGeom::CgalPlacement place(gtrsf);
shapes[i].prepend(&place);
// Apply styles assigned to the mapped item only if on
// a more granular level no styles have been applied
if (!shapes[i].hasStyle()) {
shapes[i].setStyle(mapped_item_style);
}
}
return b;
}
bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcFaceBasedSurfaceModel* l, ConversionResults& shapes) {
bool part_success = false;
IfcSchema::IfcConnectedFaceSet::list::ptr facesets = l->FbsmFaces();
const SurfaceStyle* collective_style = get_style(l);
for( IfcSchema::IfcConnectedFaceSet::list::it it = facesets->begin(); it != facesets->end(); ++ it ) {
cgal_shape_t s;
const SurfaceStyle* shell_style = get_style(*it);
if (convert_shape(*it,s)) {
shapes.push_back(ConversionResult((*it)->data().id(), new CgalShape(s), shell_style ? shell_style : collective_style));
part_success |= true;
}
}
return part_success;
}
@@ -0,0 +1,336 @@
// For MSVC to have M_PI
#define _USE_MATH_DEFINES
#include <cmath>
#include "CgalKernel.h"
#include "../../../ifcgeom/schema_agnostic/cgal/CgalConversionResult.h"
#define CgalKernel MAKE_TYPE_NAME(CgalKernel)
bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcPolyLoop* l, cgal_wire_t& result) {
IfcSchema::IfcCartesianPoint::list::ptr points = l->Polygon();
// Parse and store the points in a sequence
cgal_wire_t polygon = std::vector<Kernel_::Point_3>();
for(IfcSchema::IfcCartesianPoint::list::it it = points->begin(); it != points->end(); ++ it) {
cgal_point_t pnt;
IfcGeom::CgalKernel::convert(*it, pnt);
polygon.push_back(pnt);
}
// A loop should consist of at least three vertices
std::size_t original_count = polygon.size();
if (original_count < 3) {
Logger::Message(Logger::LOG_ERROR, "Not enough edges for:", l);
return false;
}
// Remove points that are too close to one another
remove_duplicate_points_from_loop(polygon);
std::size_t count = polygon.size();
if (original_count - count != 0) {
std::stringstream ss; ss << (original_count - count) << " edges removed for:";
Logger::Message(Logger::LOG_WARNING, ss.str(), l);
}
if (count < 3) {
Logger::Message(Logger::LOG_ERROR, "Not enough edges for:", l);
return false;
}
result = polygon;
// std::cout << "PolyLoop: " << std::endl;
// for (auto &point: polygon) {
// std::cout << "\tPoint(" << point << ")" << std::endl;
// }
return true;
}
bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcPolyline* l, cgal_wire_t& result) {
IfcSchema::IfcCartesianPoint::list::ptr points = l->Points();
// Parse and store the points in a sequence
cgal_wire_t polygon = std::vector<Kernel_::Point_3>();
for(IfcSchema::IfcCartesianPoint::list::it it = points->begin(); it != points->end(); ++ it) {
cgal_point_t pnt;
IfcGeom::CgalKernel::convert(*it, pnt);
polygon.push_back(pnt);
}
// Remove points that are too close to one another
remove_duplicate_points_from_loop(polygon);
result = polygon;
return true;
}
bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcEdgeLoop* l, cgal_wire_t& result) {
IfcSchema::IfcOrientedEdge::list::ptr li = l->EdgeList();
cgal_wire_t mw;
for (IfcSchema::IfcOrientedEdge::list::it it = li->begin(); it != li->end(); ++it) {
cgal_wire_t w;
if (convert_wire(*it, w)) {
// TODO: What to do here? Add some points only?
// mw.Add(TopoDS::Edge(TopoDS_Iterator(w).Value()));
return false;
}
}
result = mw;
return true;
}
bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcOrientedEdge* l, cgal_wire_t& result) {
if (convert_wire(l->EdgeElement(), result)) {
if (!l->Orientation()) {
std::reverse(result.begin(),result.end());
}
return true;
} else {
return false;
}
}
bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcEdge* l, cgal_wire_t& result) {
if (!l->EdgeStart()->as<IfcSchema::IfcVertexPoint>() || !l->EdgeEnd()->as<IfcSchema::IfcVertexPoint>()) {
Logger::Message(Logger::LOG_ERROR, "Only IfcVertexPoints are supported for EdgeStart and -End", l);
return false;
}
IfcSchema::IfcPoint* pnt1 = ((IfcSchema::IfcVertexPoint*) l->EdgeStart())->VertexGeometry();
IfcSchema::IfcPoint* pnt2 = ((IfcSchema::IfcVertexPoint*) l->EdgeEnd())->VertexGeometry();
if (!pnt1->as<IfcSchema::IfcCartesianPoint>() || !pnt2->as<IfcSchema::IfcCartesianPoint>()) {
Logger::Message(Logger::LOG_ERROR, "Only IfcCartesianPoints are supported for VertexGeometry", l);
return false;
}
cgal_point_t p1, p2;
if (!convert(((IfcSchema::IfcCartesianPoint*)pnt1), p1) ||
!convert(((IfcSchema::IfcCartesianPoint*)pnt2), p2))
{
return false;
}
cgal_wire_t mw;
mw.push_back(p1);
mw.push_back(p2);
result = mw;
return true;
}
bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcCompositeCurve* l, cgal_wire_t& wire) {
if ( getValue(GV_PLANEANGLE_UNIT)<0 ) {
Logger::Message(Logger::LOG_WARNING,"Creating a composite curve without unit information:",l);
// Temporarily pretend we do have unit information
setValue(GV_PLANEANGLE_UNIT,1.0);
bool succes_radians = false;
bool succes_degrees = false;
bool use_radians = false;
bool use_degrees = false;
// First try radians
cgal_wire_t wire_radians, wire_degrees;
try {
succes_radians = IfcGeom::CgalKernel::convert(l,wire_radians);
} catch (...) {}
// Now try degrees
setValue(GV_PLANEANGLE_UNIT,0.0174532925199433);
try {
succes_degrees = IfcGeom::CgalKernel::convert(l,wire_degrees);
} catch (...) {}
// Restore to unknown unit state
setValue(GV_PLANEANGLE_UNIT,-1.0);
if ( succes_degrees && ! succes_radians ) {
use_degrees = true;
} else if ( succes_radians && ! succes_degrees ) {
use_radians = true;
} else if ( succes_radians && succes_degrees ) {
if ( wire_degrees.back() == wire_degrees.front() && wire_radians.back() != wire_radians.front() ) {
use_degrees = true;
} else if ( wire_radians.back() == wire_radians.front() && wire_degrees.back() != wire_degrees.front() ) {
use_radians = true;
} else {
// No heuristic left to prefer the one over the other,
// apparently both variants are equally succesful.
// The curve might be composed of only straight segments.
// Let's go with the wire created using radians as that
// at least is a SI unit.
use_radians = true;
}
}
if ( use_radians ) {
Logger::Message(Logger::LOG_NOTICE,"Used radians to create composite curve");
wire = wire_radians;
} else if ( use_degrees ) {
Logger::Message(Logger::LOG_NOTICE,"Used degrees to create composite curve");
wire = wire_degrees;
}
return use_radians || use_degrees;
}
IfcSchema::IfcCompositeCurveSegment::list::ptr segments = l->Segments();
cgal_wire_t w;
//TopoDS_Vertex last_vertex;
for( IfcSchema::IfcCompositeCurveSegment::list::it it = segments->begin(); it != segments->end(); ++ it ) {
IfcSchema::IfcCurve* curve = (*it)->ParentCurve();
cgal_wire_t wire2;
if ( !convert_wire(curve,wire2) ) {
Logger::Message(Logger::LOG_ERROR,"Failed to convert curve:",curve);
continue;
}
if ( ! (*it)->SameSense() ) std::reverse(wire2.begin(),wire2.end());
if (wire2.empty()) {
continue;
} else if (w.empty()) {
w = wire2;
} else if (w.back() == w.front()) {
std::vector<Kernel_::Point_3>::const_iterator vertex = wire2.begin();
++vertex;
while (vertex != wire2.end()) {
w.push_back(*vertex);
++vertex;
}
} else {
for (auto &vertex: wire2) w.push_back(vertex);
}
}
remove_duplicate_points_from_loop(w);
wire = w;
return true;
}
bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcTrimmedCurve* l, cgal_wire_t& wire) {
IfcSchema::IfcCurve* basis_curve = l->BasisCurve();
bool isConic = basis_curve->as<IfcSchema::IfcConic>();
double parameterFactor = isConic ? getValue(GV_PLANEANGLE_UNIT) : getValue(GV_LENGTH_UNIT);
cgal_curve_t curve;
if ( !convert_curve(basis_curve,curve) ) return false;
bool trim_cartesian = l->MasterRepresentation() == IfcSchema::IfcTrimmingPreference::IfcTrimmingPreference_CARTESIAN;
IfcEntityList::ptr trims1 = l->Trim1();
IfcEntityList::ptr trims2 = l->Trim2();
unsigned sense_agreement = l->SenseAgreement() ? 0 : 1;
double flts[2];
cgal_point_t pnts[2];
bool has_flts[2] = {false,false};
bool has_pnts[2] = {false,false};
cgal_wire_t w;
for ( IfcEntityList::it it = trims1->begin(); it != trims1->end(); it ++ ) {
IfcUtil::IfcBaseClass* i = *it;
if ( i->as<IfcSchema::IfcCartesianPoint>() ) {
IfcGeom::CgalKernel::convert((IfcSchema::IfcCartesianPoint*)i, pnts[sense_agreement] );
has_pnts[sense_agreement] = true;
} else if ( i->as<IfcSchema::IfcParameterValue>() ) {
const double value = *((IfcSchema::IfcParameterValue*)i);
flts[sense_agreement] = value * parameterFactor;
has_flts[sense_agreement] = true;
}
}
for ( IfcEntityList::it it = trims2->begin(); it != trims2->end(); it ++ ) {
IfcUtil::IfcBaseClass* i = *it;
if ( i->as<IfcSchema::IfcCartesianPoint>() ) {
IfcGeom::CgalKernel::convert((IfcSchema::IfcCartesianPoint*)i, pnts[1-sense_agreement] );
has_pnts[1-sense_agreement] = true;
} else if ( i->as<IfcSchema::IfcParameterValue>() ) {
const double value = *((IfcSchema::IfcParameterValue*)i);
flts[1-sense_agreement] = value * parameterFactor;
has_flts[1-sense_agreement] = true;
}
}
trim_cartesian &= has_pnts[0] && has_pnts[1];
bool trim_cartesian_failed = !trim_cartesian;
if ( trim_cartesian ) {
// TODO: Project points to closest point in curve?
if ( CGAL::squared_distance(pnts[0], pnts[1]) < getValue(GV_WIRE_CREATION_TOLERANCE)*getValue(GV_WIRE_CREATION_TOLERANCE) ) {
Logger::Message(Logger::LOG_WARNING,"Skipping segment with length below tolerance level:",l);
return false;
}
if (l->SenseAgreement()) {
bool found = false;
int loops_to_go = 2;
std::vector<Kernel_::Point_3>::const_iterator point = curve.begin();
do {
if (!found) {
if (CGAL::squared_distance(*point, pnts[0]) < getValue(GV_WIRE_CREATION_TOLERANCE)*getValue(GV_WIRE_CREATION_TOLERANCE)) {
found = true;
w.push_back(*point);
}
} else {
w.push_back(*point);
if (CGAL::squared_distance(*point, pnts[1]) < getValue(GV_WIRE_CREATION_TOLERANCE)*getValue(GV_WIRE_CREATION_TOLERANCE)) {
break;
}
} ++point;
if (point == curve.end()) {
point = curve.begin();
--loops_to_go;
}
} while (point != curve.begin() && loops_to_go > 0);
} else {
bool found = false;
int loops_to_go = 2;
std::vector<Kernel_::Point_3>::const_reverse_iterator point = curve.rbegin();
do {
if (!found) {
if (CGAL::squared_distance(*point, pnts[0]) < getValue(GV_WIRE_CREATION_TOLERANCE)*getValue(GV_WIRE_CREATION_TOLERANCE)) {
found = true;
w.push_back(*point);
}
} else {
w.push_back(*point);
if (CGAL::squared_distance(*point, pnts[1]) < getValue(GV_WIRE_CREATION_TOLERANCE)*getValue(GV_WIRE_CREATION_TOLERANCE)) {
break;
}
} ++point;
if (point == curve.rend() && loops_to_go > 0) point = curve.rbegin();
} while (point != curve.rbegin());
}
}
if ( (!trim_cartesian || trim_cartesian_failed) && (has_flts[0] && has_flts[1]) ) {
// The Geom_Line is constructed from a gp_Pnt and gp_Dir, whereas the IfcLine
// is defined by an IfcCartesianPoint and an IfcVector with Magnitude. Because
// the vector is normalised when passed to Geom_Line constructor the magnitude
// needs to be factored in with the IfcParameterValue here.
if ( basis_curve->as<IfcSchema::IfcLine>() ) {
IfcSchema::IfcLine* line = static_cast<IfcSchema::IfcLine*>(basis_curve);
const double magnitude = line->Dir()->Magnitude();
flts[0] *= magnitude; flts[1] *= magnitude;
}
if ( isConic && ALMOST_THE_SAME(fmod(flts[1]-flts[0],M_PI*2.),0.) ) {
for (auto &point: curve) w.push_back(point);
} else {
const int segments_of_full_curve = 12;
double segment_angle = 2.0*3.141592653589793/segments_of_full_curve;
if ( basis_curve->as<IfcSchema::IfcEllipse>() ) {
IfcSchema::IfcEllipse* ellipse = static_cast<IfcSchema::IfcEllipse*>(basis_curve);
double x = ellipse->SemiAxis1() * getValue(GV_LENGTH_UNIT);
double y = ellipse->SemiAxis2() * getValue(GV_LENGTH_UNIT);
for (double current_angle = flts[0]; current_angle < flts[1]; current_angle += segment_angle) {
w.push_back(Kernel_::Point_3(x*cos(current_angle), y*sin(current_angle), 0));
} w.push_back(Kernel_::Point_3(x*cos(flts[1]), y*sin(flts[1]), 0));
} if ( basis_curve->as<IfcSchema::IfcCircle>() ) {
IfcSchema::IfcCircle* circle = static_cast<IfcSchema::IfcCircle*>(basis_curve);
double r = circle->Radius() * getValue(GV_LENGTH_UNIT);
for (double current_angle = flts[0]; current_angle < flts[1]; current_angle += segment_angle) {
w.push_back(Kernel_::Point_3(r*cos(current_angle), r*sin(current_angle), 0));
} w.push_back(Kernel_::Point_3(r*cos(flts[1]), r*sin(flts[1]), 0));
}
}
} else if ( trim_cartesian_failed && (has_pnts[0] && has_pnts[1]) ) {
w.push_back(pnts[0]);
w.push_back(pnts[1]);
}
wire = w;
return true;
}
File diff suppressed because it is too large Load Diff
+267
View File
@@ -0,0 +1,267 @@
/********************************************************************************
* *
* 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/>. *
* *
********************************************************************************/
#include "CgalKernel.h"
namespace {
struct MAKE_TYPE_NAME(factory_t) {
IfcGeom::Kernel* operator()(IfcParse::IfcFile* file) const {
IfcGeom::MAKE_TYPE_NAME(CgalKernel)* k = new IfcGeom::MAKE_TYPE_NAME(CgalKernel);
return k;
}
};
}
void MAKE_INIT_FN(KernelImplementation_cgal_)(IfcGeom::impl::KernelFactoryImplementation* mapping) {
static const std::string schema_name = STRINGIFY(IfcSchema);
MAKE_TYPE_NAME(factory_t) factory;
mapping->bind(schema_name, "cgal", factory);
}
#define CgalKernel MAKE_TYPE_NAME(CgalKernel)
bool IfcGeom::CgalKernel::is_identity_transform(const IfcUtil::IfcBaseClass* l) {
Logger::Message(Logger::LOG_ERROR, "Not implemented is_identity_transform()");
return false;
/*
// OpenCascade kernel code below
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");
}
*/
}
bool IfcGeom::CgalKernel::apply_layerset(const IfcSchema::IfcProduct* product, IfcGeom::ConversionResults& shapes) {
throw std::runtime_error("not implemented");
}
bool IfcGeom::CgalKernel::validate_quantities(const IfcSchema::IfcProduct* product, const IfcGeom::Representation::BRep& brep) {
throw std::runtime_error("not implemented");
}
bool IfcGeom::CgalKernel::convert_placement(IfcUtil::IfcBaseClass* item, ConversionResultPlacement*& trsf) {
if (item->as<IfcSchema::IfcObjectPlacement>()) {
cgal_placement_t cgal_trsf;
if (convert(item->as<IfcSchema::IfcObjectPlacement>(), cgal_trsf)) {
trsf = new CgalPlacement(cgal_trsf);
return true;
}
}
return false;
}
bool IfcGeom::CgalKernel::convert_openings(const IfcSchema::IfcProduct* product, const IfcSchema::IfcRelVoidsElement::list::ptr& openings, const IfcGeom::ConversionResults& entity_shapes, const IfcGeom::ConversionResultPlacement* trsf, IfcGeom::ConversionResults& opened_shapes) {
const cgal_placement_t& entity_trsf = ((CgalPlacement*) trsf)->trsf();
std::list<cgal_shape_t> opening_shapelist;
for ( IfcSchema::IfcRelVoidsElement::list::it it = openings->begin(); it != openings->end(); ++ it ) {
IfcSchema::IfcRelVoidsElement* v = *it;
IfcSchema::IfcFeatureElementSubtraction* fes = v->RelatedOpeningElement();
if ( fes->as<IfcSchema::IfcOpeningElement>() ) {
if (!fes->hasRepresentation()) continue;
// Convert the IfcRepresentation of the IfcOpeningElement
cgal_placement_t opening_trsf;
if (fes->hasObjectPlacement()) {
try {
convert(fes->ObjectPlacement(),opening_trsf);
} catch (...) {}
}
// Move the opening into the coordinate system of the IfcProduct
opening_trsf = entity_trsf.inverse() * opening_trsf;
IfcSchema::IfcProductRepresentation* prodrep = fes->Representation();
IfcSchema::IfcRepresentation::list::ptr reps = prodrep->Representations();
IfcGeom::ConversionResults opening_shapes;
for ( IfcSchema::IfcRepresentation::list::it it2 = reps->begin(); it2 != reps->end(); ++ it2 ) {
convert_shapes(*it2,opening_shapes);
}
for ( unsigned int i = 0; i < opening_shapes.size(); ++ i ) {
cgal_placement_t gtrsf;
if (opening_shapes[i].Placement()) {
gtrsf = *(CgalPlacement*)opening_shapes[i].Placement();
}
gtrsf = opening_trsf * gtrsf;
cgal_shape_t opening_shape(((CgalShape*)opening_shapes[i].Shape())->shape());
for (auto &vertex: vertices(opening_shape)) vertex->point() = vertex->point().transform(gtrsf);
opening_shapelist.push_back(opening_shape);
}
}
}
// Iterate over the shapes of the IfcProduct
for ( IfcGeom::ConversionResults::const_iterator it3 = entity_shapes.begin(); it3 != entity_shapes.end(); ++ it3 ) {
const cgal_shape_t& entity_shape_unlocated(((CgalShape*)it3->Shape())->shape());
cgal_shape_t entity_shape(entity_shape_unlocated);
if (it3->Placement()) {
const cgal_placement_t& entity_shape_gtrsf = *(CgalPlacement*)it3->Placement();
for (auto &vertex: vertices(entity_shape)) vertex->point() = vertex->point().transform(entity_shape_gtrsf);
}
cgal_shape_t original_entity_shape(entity_shape);
if (!entity_shape.is_valid()) {
Logger::Message(Logger::LOG_ERROR, "Conversion to Nef will fail. Invalid geometry:", product);
return false;
}
if (!entity_shape.is_closed()) {
// TODO: There can be substractions to remove parts of non-volumetric objects. Maybe iterate over all faces of an entity and put them in a Nef_polyhedron_3 through Boolean union? Highly inefficient but maybe desirable...
Logger::Message(Logger::LOG_ERROR, "Subtraction of openings not supported for non-closed geometry:", product);
return false;
}
bool success = false;
try {
success = CGAL::Polygon_mesh_processing::triangulate_faces(entity_shape);
} catch (...) {
Logger::Message(Logger::LOG_ERROR, "Triangulation of geometry crashed:", product);
return false;
}
if (!success) {
Logger::Message(Logger::LOG_ERROR, "Triangulation of geometry failed:", product);
return false;
}
if (CGAL::Polygon_mesh_processing::does_self_intersect(entity_shape)) {
Logger::Message(Logger::LOG_ERROR, "Conversion to Nef will fail. Self-intersecting geometry:", product);
return false;
}
CGAL::Nef_polyhedron_3<Kernel_> nef_brep_cut_result;
try {
nef_brep_cut_result = CGAL::Nef_polyhedron_3<Kernel_>(entity_shape);
} catch (...) {
Logger::Message(Logger::LOG_ERROR, "Could not convert geometry to Nef:", product);
return false;
}
try {
cgal_shape_t brep_cut_result;
nef_brep_cut_result.convert_to_polyhedron(brep_cut_result);
} catch (...) {
Logger::Message(Logger::LOG_WARNING, "Final conversion will likely fail. Could not convert geometry from Nef:", product);
}
for (auto &opening: opening_shapelist) {
cgal_shape_t original_opening_shape(opening);
if (!opening.is_valid()) {
Logger::Message(Logger::LOG_ERROR, "Conversion to Nef will fail. Invalid opening in geometry:", product);
return false;
} if (!opening.is_closed()) {
Logger::Message(Logger::LOG_ERROR, "Subtraction of opening makes no sense. Not closed opening in geometry:", product);
return false;
}
success = false;
try {
success = CGAL::Polygon_mesh_processing::triangulate_faces(opening);
} catch (...) {
Logger::Message(Logger::LOG_ERROR, "Triangulation of opening of geometry crashed:", product);
return false;
}
if (!success) {
Logger::Message(Logger::LOG_ERROR, "Triangulation of opening of geometry failed:", product);
return false;
}
if (CGAL::Polygon_mesh_processing::does_self_intersect(entity_shape)) {
Logger::Message(Logger::LOG_ERROR, "Conversion to Nef will fail. Self-intersecting opening of geometry:", product);
}
CGAL::Nef_polyhedron_3<Kernel_> nef_opening;
try {
nef_opening = CGAL::Nef_polyhedron_3<Kernel_>(opening);
} catch (...) {
Logger::Message(Logger::LOG_ERROR, "Could not convert opening of geometry to Nef:", product);
return false;
}
try {
cgal_shape_t opening_shape;
nef_opening.convert_to_polyhedron(opening_shape);
} catch (...) {
Logger::Message(Logger::LOG_WARNING, "Final conversion will likely fail. Could not convert opening of geometry from Nef:", product);
// return false;
}
try {
nef_brep_cut_result -= nef_opening;
} catch (...) {
Logger::Message(Logger::LOG_ERROR, "Could not subtract Nef opening of geometry:", product);
return false;
}
}
try {
nef_brep_cut_result.convert_to_polyhedron(entity_shape);
} catch (...) {
Logger::Message(Logger::LOG_ERROR, "Could not convert geometry with openings from Nef:", product);
return false;
}
opened_shapes.push_back(IfcGeom::ConversionResult(it3->ItemId(), new CgalShape(entity_shape), &it3->Style()));
} return true;
}
+111
View File
@@ -0,0 +1,111 @@
/********************************************************************************
* *
* 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/>. *
* *
********************************************************************************/
#ifndef CGAL_KERNEL_H
#define CGAL_KERNEL_H
/*
#ifdef NO_CACHE
#define IN_CACHE(T,E,t,e)
#define CACHE(T,E,e)
#else
#define IN_CACHE(T,E,t,e) std::map<int,t>::const_iterator it = cache.T.find(E->entity->id());\
if ( it != cache.T.end() ) { e = it->second; return true; }
#define CACHE(T,E,e) cache.T[E->entity->id()] = e;
#endif
*/
#include <cmath>
#include "../../../ifcparse/macros.h"
#include "../../../ifcgeom/kernel_agnostic/AbstractKernel.h"
#include "../../../ifcgeom/schema_agnostic/IfcGeomElement.h"
#include "../../../ifcgeom/kernels/cgal/CgalConversionResult.h"
struct PolyhedronBuilder : public CGAL::Modifier_base<CGAL::Polyhedron_3<Kernel_>::HalfedgeDS> {
private:
std::list<cgal_face_t> *face_list;
public:
boost::optional<cgal_shape_t> from_soup;
PolyhedronBuilder(std::list<cgal_face_t> *face_list);
void operator()(CGAL::Polyhedron_3<Kernel_>::HalfedgeDS &hds);
};
namespace ifcopenshell {
namespace geometry {
namespace utils {
IFC_GEOM_API CGAL::Polyhedron_3<Kernel_> create_cube(double d);
IFC_GEOM_API CGAL::Polyhedron_3<Kernel_> create_cube(const Kernel_::Point_3& lower, const Kernel_::Point_3& upper);
IFC_GEOM_API CGAL::Polyhedron_3<Kernel_> create_polyhedron(std::list<cgal_face_t> &face_list, bool stitch_borders=false);
IFC_GEOM_API CGAL::Polyhedron_3<Kernel_> create_polyhedron(const CGAL::Nef_polyhedron_3<Kernel_> &nef_polyhedron);
IFC_GEOM_API CGAL::Nef_polyhedron_3<Kernel_> create_nef_polyhedron(std::list<cgal_face_t> &face_list);
IFC_GEOM_API CGAL::Nef_polyhedron_3<Kernel_> create_nef_polyhedron(CGAL::Polyhedron_3<Kernel_> &polyhedron);
}
namespace kernels {
class IFC_GEOM_API CgalKernel : public AbstractKernel {
private:
double precision_;
size_t circle_segments_;
// CGAL::Nef_polyhedron_3<Kernel_> precision_cube_;
bool preprocess_boolean_operand(const IfcUtil::IfcBaseClass* log_reference, const cgal_shape_t& shape_const, CGAL::Nef_polyhedron_3<Kernel_>& result, bool dilate);
bool thin_solid(const CGAL::Nef_polyhedron_3<Kernel_>& a, CGAL::Nef_polyhedron_3<Kernel_>& result);
CGAL::Nef_polyhedron_3<Kernel_> create_precision_cube_() const {
auto cc = utils::create_cube(precision_);
return CGAL::Nef_polyhedron_3<Kernel_>(cc);
}
public:
CgalKernel(const ConversionSettings& settings)
: AbstractKernel("cgal", settings)
, circle_segments_(16)
{}
void remove_duplicate_points_from_loop(cgal_wire_t& polygon);
bool convert(const taxonomy::extrusion*, cgal_shape_t&);
bool convert(const taxonomy::face*, cgal_face_t&);
bool convert(const taxonomy::loop*, cgal_wire_t&);
// bool convert(const taxonomy::matrix4*, cgal_placement_t&);
bool convert(const taxonomy::shell*, cgal_shape_t&);
bool process_extrusion(const cgal_face_t& bottom_face, const taxonomy::direction3& direction, double height, cgal_shape_t& shape);
bool process_as_2d_polygon(const taxonomy::boolean_result* br, std::list<CGAL::Polygon_2<Kernel_>>& loops, double& z0, double& z1);
bool process_as_2d_polygon(const std::list<std::list<std::pair<const IfcUtil::IfcBaseClass*, cgal_shape_t>>>& operands, std::list<CGAL::Polygon_2<Kernel_>>& loops, double& z0, double& z1);
virtual bool convert_impl(const taxonomy::shell*, ifcopenshell::geometry::ConversionResults&);
virtual bool convert_impl(const taxonomy::extrusion*, ifcopenshell::geometry::ConversionResults&);
virtual bool convert_impl(const taxonomy::boolean_result*, ifcopenshell::geometry::ConversionResults&);
CGAL::Nef_polyhedron_3<Kernel_> precision_cube() const { return create_precision_cube_(); }
};
}
}
}
#endif
@@ -0,0 +1,718 @@
/********************************************************************************
* *
* 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 IfcRegister.h *
* *
********************************************************************************/
#include <gp_Pnt.hxx>
#include <gp_Vec.hxx>
#include <gp_Dir.hxx>
#include <gp_Pnt2d.hxx>
#include <gp_Vec2d.hxx>
#include <gp_Dir2d.hxx>
#include <gp_Mat.hxx>
#include <gp_Mat2d.hxx>
#include <gp_GTrsf.hxx>
#include <gp_GTrsf2d.hxx>
#include <gp_Trsf.hxx>
#include <gp_Trsf2d.hxx>
#include <gp_Ax1.hxx>
#include <gp_Ax3.hxx>
#include <gp_Ax2d.hxx>
#include <gp_Pln.hxx>
#include <gp_Circ.hxx>
#include <TColgp_Array1OfPnt.hxx>
#include <TColgp_Array1OfPnt2d.hxx>
#include <TColStd_Array1OfReal.hxx>
#include <TColStd_Array1OfInteger.hxx>
#include <Geom_Line.hxx>
#include <Geom_Circle.hxx>
#include <Geom_Ellipse.hxx>
#include <Geom_TrimmedCurve.hxx>
#include <Geom_CylindricalSurface.hxx>
#include <BRepOffsetAPI_Sewing.hxx>
#include <BRepOffsetAPI_MakePipe.hxx>
#include <BRepOffsetAPI_MakePipeShell.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>
#include <TopoDS_CompSolid.hxx>
#include <TopExp.hxx>
#include <TopExp_Explorer.hxx>
#include <BRepPrimAPI_MakePrism.hxx>
#include <BRepPrimAPI_MakeRevol.hxx>
#include <BRepPrimAPI_MakeBox.hxx>
#include <BRepPrimAPI_MakeCone.hxx>
#include <BRepPrimAPI_MakeCylinder.hxx>
#include <BRepPrimAPI_MakeSphere.hxx>
#include <BRepPrimAPI_MakeWedge.hxx>
#include <BRepBuilderAPI_MakePolygon.hxx>
#include <BRepBuilderAPI_Transform.hxx>
#include <BRepBuilderAPI_MakeShell.hxx>
#include <BRepBuilderAPI_MakeSolid.hxx>
#include <BRepPrimAPI_MakeHalfSpace.hxx>
#include <BRepAlgoAPI_Cut.hxx>
#include <BRepAlgoAPI_Fuse.hxx>
#include <BRepAlgoAPI_Common.hxx>
#include <ShapeFix_Shape.hxx>
#include <ShapeFix_ShapeTolerance.hxx>
#include <ShapeFix_Solid.hxx>
#include <TopLoc_Location.hxx>
#include <BRepCheck_Analyzer.hxx>
#include <BRepClass3d_SolidClassifier.hxx>
#include <Standard_Version.hxx>
#include <TopTools_ListIteratorOfListOfShape.hxx>
#include "OpenCascadeKernel.h"
#include <memory>
#include "../../../ifcparse/IfcLogger.h"
#include "../../../ifcgeom/kernels/opencascade/OpenCascadeConversionResult.h"
#include "IfcGeomTree.h"
using namespace ifcopenshell::geometry;
using namespace ifcopenshell::geometry::kernels;
#include <TopTools_DataMapOfShapeInteger.hxx>
#include <Geom_Plane.hxx>
#include <BRepLib_FindSurface.hxx>
#include <ShapeFix_Edge.hxx>
#include <BRepBuilderAPI_GTransform.hxx>
#include <Geom_Curve.hxx>
#include <Geom_Line.hxx>
#include <Approx_Curve3d.hxx>
#include <BRepAdaptor_CompCurve.hxx>
#include <BRepAdaptor_HCompCurve.hxx>
#include <Approx_Curve3d.hxx>
#include <ShapeBuild_ReShape.hxx>
#include <GC_MakeCircle.hxx>
#include <BRepTools_WireExplorer.hxx>
#include <BRepGProp.hxx>
#include <GProp_GProps.hxx>
double OpenCascadeKernel::shape_volume(const TopoDS_Shape& s) {
GProp_GProps prop;
BRepGProp::VolumeProperties(s, prop);
return prop.Mass();
}
double OpenCascadeKernel::face_area(const TopoDS_Face& f) {
GProp_GProps prop;
BRepGProp::SurfaceProperties(f, prop);
return prop.Mass();
}
bool OpenCascadeKernel::create_solid_from_compound(const TopoDS_Shape& compound, TopoDS_Shape& shape) {
TopTools_ListOfShape face_list;
TopExp_Explorer exp(compound, TopAbs_FACE);
for (; exp.More(); exp.Next()) {
TopoDS_Face face = TopoDS::Face(exp.Current());
face_list.Append(face);
}
if (face_list.Extent() == 0) {
return false;
}
return create_solid_from_faces(face_list, shape);
}
bool OpenCascadeKernel::create_solid_from_faces(const TopTools_ListOfShape& face_list, TopoDS_Shape& shape) {
bool valid_shell = false;
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;
}
TopTools_ListIteratorOfListOfShape face_iterator;
bool has_shared_edges = false;
TopTools_MapOfShape edge_set;
// 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()) {
for (face_iterator.Initialize(face_list); face_iterator.More(); face_iterator.Next()) {
// 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;
}
edge_set.Add(exp.Current());
}
}
}
BRepOffsetAPI_Sewing sewing_builder;
sewing_builder.SetTolerance(settings_.getValue(ConversionSettings::GV_PRECISION));
sewing_builder.SetMaxTolerance(settings_.getValue(ConversionSettings::GV_PRECISION));
sewing_builder.SetMinTolerance(settings_.getValue(ConversionSettings::GV_PRECISION));
BRep_Builder builder;
TopoDS_Shell shell;
builder.MakeShell(shell);
for (face_iterator.Initialize(face_list); face_iterator.More(); face_iterator.Next()) {
if (has_shared_edges) {
builder.Add(shell, face_iterator.Value());
} else {
sewing_builder.Add(face_iterator.Value());
}
}
try {
if (has_shared_edges) {
ShapeFix_Shell fix;
fix.FixFaceOrientation(shell);
shape = fix.Shape();
} else {
sewing_builder.Perform();
shape = sewing_builder.SewedShape();
}
BRepCheck_Analyzer ana(shape);
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;
} 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");
}
if (valid_shell) {
TopoDS_Shape complete_shape;
TopExp_Explorer exp(shape, TopAbs_SHELL);
for (; exp.More(); exp.Next()) {
TopoDS_Shape result_shape = exp.Current();
try {
ShapeFix_Solid solid;
solid.SetMaxTolerance(settings_.getValue(ConversionSettings::GV_PRECISION));
TopoDS_Solid solid_shape = solid.SolidFromShell(TopoDS::Shell(exp.Current()));
// @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.
if (!solid_shape.IsNull()) {
try {
BRepClass3d_SolidClassifier classifier(solid_shape);
result_shape = solid_shape;
classifier.PerformInfinitePoint(settings_.getValue(ConversionSettings::GV_PRECISION));
if (classifier.State() == TopAbs_IN) {
shape.Reverse();
}
} 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");
}
} catch (...) {
Logger::Error("Unknown error creating solid");
}
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;
Logger::Warning("Multiple components in IfcConnectedFaceSet");
}
B.Add(complete_shape, result_shape);
}
}
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;
Logger::Warning("Loose faces in IfcConnectedFaceSet");
}
B.Add(complete_shape, loose_faces.Current());
}
shape = complete_shape;
} else {
Logger::Error("Failed to sew faceset");
}
return valid_shell;
}
int OpenCascadeKernel::count(const TopoDS_Shape& s, TopAbs_ShapeEnum t, bool unique) {
if (unique) {
TopTools_IndexedMapOfShape map;
TopExp::MapShapes(s, t, map);
return map.Extent();
} else {
int i = 0;
TopExp_Explorer exp(s, t);
for (; exp.More(); exp.Next()) {
++i;
}
return i;
}
}
bool is_manifold_occt(const TopoDS_Shape& a) {
if (a.ShapeType() == TopAbs_COMPOUND || a.ShapeType() == TopAbs_SOLID) {
TopoDS_Iterator it(a);
for (; it.More(); it.Next()) {
if (!is_manifold_occt(it.Value())) {
return false;
}
}
return true;
} else {
TopTools_IndexedDataMapOfShapeListOfShape map;
TopExp::MapShapesAndAncestors(a, TopAbs_EDGE, TopAbs_FACE, map);
for (int i = 1; i <= map.Extent(); ++i) {
if (map.FindFromIndex(i).Extent() != 2) {
return false;
}
}
return true;
}
}
bool OpenCascadeKernel::boolean_operation(const TopoDS_Shape& a_, const TopTools_ListOfShape& b__, BOPAlgo_Operation op, TopoDS_Shape& result, double fuzziness) {
if (fuzziness < 0.) {
fuzziness = settings_.getValue(ConversionSettings::GV_PRECISION);
}
// @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);
TopTools_ListOfShape b_;
{
TopTools_ListIteratorOfListOfShape it(b__);
for (; it.More(); it.Next()) {
b_.Append(unify(it.Value(), fuzziness));
}
}
bool success = false;
BRepAlgoAPI_BooleanOperation* builder;
TopTools_ListOfShape B, b;
if (op == BOPAlgo_CUT) {
builder = new BRepAlgoAPI_Cut();
bounding_box_overlap(settings_.getValue(ConversionSettings::GV_PRECISION), a, b_, b);
} else if (op == BOPAlgo_COMMON) {
builder = new BRepAlgoAPI_Common();
b = b_;
} else if (op == BOPAlgo_FUSE) {
builder = new BRepAlgoAPI_Fuse();
b = b_;
} else {
return false;
}
if (b.Extent() == 0) {
result = a;
return true;
}
// Find a sensible value for the fuzziness, based on precision
// and limited by edge lengths and vertex-edge distances.
const double len_a = min_edge_length(a_);
double min_length_orig = (std::min)(len_a, min_vertex_edge_distance(a_, settings_.getValue(ConversionSettings::GV_PRECISION), len_a));
TopTools_ListIteratorOfListOfShape it(b__);
for (; it.More(); it.Next()) {
double d = min_edge_length(it.Value());
if (d < min_length_orig) {
min_length_orig = d;
}
d = min_vertex_edge_distance(it.Value(), settings_.getValue(ConversionSettings::GV_PRECISION), d);
if (d < min_length_orig) {
min_length_orig = d;
}
}
const double fuzz = (std::min)(min_length_orig / 3., fuzziness);
TopTools_ListOfShape s1s;
s1s.Append(copy_operand(a));
#if OCC_VERSION_HEX >= 0x70000
builder->SetNonDestructive(true);
#endif
builder->SetFuzzyValue(fuzz);
builder->SetArguments(s1s);
copy_operand(b, B);
builder->SetTools(B);
builder->Build();
if (builder->IsDone()) {
TopoDS_Shape r = *builder;
ShapeFix_Shape fix(r);
try {
fix.SetMinTolerance(fuzz);
fix.SetMaxTolerance(fuzz);
fix.SetPrecision(fuzz);
fix.Perform();
r = fix.Shape();
} catch (...) {
Logger::Error("Shape healing failed on boolean result");
}
success = BRepCheck_Analyzer(r).IsValid() != 0;
if (success) {
success = !is_manifold_occt(a) || is_manifold_occt(r);
if (success) {
// 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, settings_.getValue(ConversionSettings::GV_PRECISION), fuzziness * 3.)) < fuzziness * 3.) {
reason = 1;
success = false;
} else if ((v = min_face_face_distance(r, fuzziness * 3.)) < fuzziness * 3.) {
reason = 2;
success = false;
}
if (success) {
result = r;
} else {
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());
}
} else {
Logger::Notice("Boolean operation yields non-manifold result");
}
} else {
Logger::Notice("Boolean operation yields invalid result");
}
} else {
std::stringstream str;
#if OCC_VERSION_HEX >= 0x70000
builder->DumpErrors(str);
#else
str << "Error code: " << builder->ErrorStatus();
#endif
std::string str_str = str.str();
if (str_str.size()) {
Logger::Notice(str_str);
}
}
delete builder;
if (!success) {
const double new_fuzziness = fuzziness * 10.;
if (new_fuzziness - 1e-15 <= settings_.getValue(ConversionSettings::GV_PRECISION) * 10000. && new_fuzziness < min_length_orig) {
return boolean_operation(a, b, op, result, new_fuzziness);
} else {
Logger::Notice("No longer attempting boolean operation with higher fuzziness");
}
}
return success;
}
namespace {
BOPAlgo_Operation op_to_occt(taxonomy::boolean_result::operation_t t) {
switch (t) {
case taxonomy::boolean_result::UNION: return BOPAlgo_FUSE;
case taxonomy::boolean_result::INTERSECTION: return BOPAlgo_COMMON;
case taxonomy::boolean_result::SUBTRACTION: return BOPAlgo_CUT;
}
}
}
bool OpenCascadeKernel::convert_impl(const taxonomy::boolean_result* br, ifcopenshell::geometry::ConversionResults& results) {
bool first = true;
TopoDS_Shape a;
TopTools_ListOfShape b;
taxonomy::style* first_item_style = nullptr;
for (auto& c : br->children) {
// AbstractKernel::convert(c, results);
// continue;
ifcopenshell::geometry::ConversionResults cr;
// @todo half-space detection
AbstractKernel::convert(c, cr);
if (first && br->operation == taxonomy::boolean_result::SUBTRACTION) {
// @todo A will be null on union/intersection, intended?
flatten_shape_list(cr, a, false);
first_item_style = ((taxonomy::geom_item*)c)->surface_style;
if (!first_item_style && c->kind() == taxonomy::COLLECTION) {
// @todo recursively right?
first_item_style = ((taxonomy::geom_item*) ((taxonomy::collection*)c)->children[0])->surface_style;
}
} else {
for (auto& r : cr) {
auto S = ((OpenCascadeShape*)r.Shape())->shape();
gp_GTrsf trsf;
convert(&r.Placement(), trsf);
// @todo it really confuses me why I cannot use Moved() here instead
S.Location(S.Location() * trsf.Trsf());
b.Append(S);
/*results.emplace_back(ConversionResult(
r.ItemId(),
ifcopenshell::geometry::taxonomy::matrix4(),
new OpenCascadeShape(S),
r.Style()
));*/
}
}
first = false;
}
TopoDS_Shape r;
if (!boolean_operation(a, b, op_to_occt(br->operation), r)) {
return false;
}
/*
TopoDS_Compound r;
BRep_Builder B;
B.MakeCompound(r);
B.Add(r, a);
for (auto& bb : b) {
B.Add(r, bb);
}
*/
results.emplace_back(ConversionResult(
br->instance->data().id(),
br->matrix,
new OpenCascadeShape(r),
br->surface_style ? br->surface_style : first_item_style
));
return true;
}
bool OpenCascadeKernel::is_compound(const TopoDS_Shape& shape) {
bool has_solids = TopExp_Explorer(shape, TopAbs_SOLID).More() != 0;
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;
}
const TopoDS_Shape& OpenCascadeKernel::ensure_fit_for_subtraction(const TopoDS_Shape& shape, TopoDS_Shape& solid) {
const bool is_comp = is_compound(shape);
if (!is_comp) {
return solid = shape;
}
if (!create_solid_from_compound(shape, solid)) {
return solid = shape;
}
return solid;
}
bool OpenCascadeKernel::flatten_shape_list(const ifcopenshell::geometry::ConversionResults& shapes, TopoDS_Shape& result, bool fuse) {
TopoDS_Compound compound;
BRep_Builder builder;
builder.MakeCompound(compound);
result = TopoDS_Shape();
for (ifcopenshell::geometry::ConversionResults::const_iterator it = shapes.begin(); it != shapes.end(); ++it) {
TopoDS_Shape merged;
const TopoDS_Shape& s = *(OpenCascadeShape*)it->Shape();
if (fuse) {
ensure_fit_for_subtraction(s, merged);
} else {
merged = s;
}
const TopoDS_Shape moved_shape = apply_transformation(merged, it->Placement());
if (shapes.size() == 1) {
result = moved_shape;
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);
}
}
if (!fuse) {
result = compound;
}
const bool success = !result.IsNull();
return success;
}
TopoDS_Shape OpenCascadeKernel::apply_transformation(const TopoDS_Shape& s, const taxonomy::matrix4& t) {
if (t.is_identity()) {
return s;
} else {
gp_GTrsf trsf;
convert(&t, trsf);
return apply_transformation(s, trsf);
}
}
TopoDS_Shape OpenCascadeKernel::apply_transformation(const TopoDS_Shape& s, const gp_GTrsf& t) {
if (t.Form() == gp_Other) {
Logger::Message(Logger::LOG_WARNING, "Applying non uniform transformation");
return BRepBuilderAPI_GTransform(s, t, true);
} else {
return apply_transformation(s, t.Trsf());
}
}
TopoDS_Shape OpenCascadeKernel::apply_transformation(const TopoDS_Shape& s, const gp_Trsf& t) {
/// @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);
}
}
bool OpenCascadeKernel::convert_impl(const taxonomy::face* face, ifcopenshell::geometry::ConversionResults& results) {
// Root level faces are only encountered in case of half spaces
if (face->basis == nullptr) {
Logger::Error("Half space without underlying surface:", face->instance);
return false;
}
if (face->basis->kind() != taxonomy::PLANE) {
Logger::Message(Logger::LOG_ERROR, "Unsupported BaseSurface:", face->basis->instance);
return false;
}
// @todo boundary
const auto& m = ((taxonomy::geom_item*)face->basis)->matrix.ccomponents();
gp_Pln pln(convert_xyz2<gp_Pnt>(m.col(3)), convert_xyz2<gp_Dir>(m.col(2)));
const gp_Pnt pnt = pln.Location().Translated(face->orientation.get_value_or(false) ? -pln.Axis().Direction() : pln.Axis().Direction());
TopoDS_Shape shape = BRepPrimAPI_MakeHalfSpace(BRepBuilderAPI_MakeFace(pln), pnt).Solid();
results.emplace_back(ConversionResult(
face->instance->data().id(),
new OpenCascadeShape(shape),
face->surface_style
));
return true;
}
@@ -20,10 +20,11 @@
#ifndef IFCGEOMTREE_H
#define IFCGEOMTREE_H
#include "../ifcparse/IfcFile.h"
#include "../ifcgeom/IfcGeomElement.h"
#include "../ifcgeom_schema_agnostic/IfcGeomIterator.h"
#include "../ifcgeom_schema_agnostic/Kernel.h"
#include "../../../ifcparse/IfcFile.h"
#include "../../../ifcgeom/schema_agnostic/IfcGeomElement.h"
#include "../../../ifcgeom/schema_agnostic/IfcGeomIterator.h"
#include "../../../ifcgeom/schema_agnostic/Converter.h"
#include "../../../ifcgeom/kernels/opencascade/OpenCascadeKernel.h"
#include <NCollection_UBTree.hxx>
#include <BRepBndLib.hxx>
@@ -32,7 +33,7 @@
#include <BRepAlgoAPI_Cut.hxx>
#include <BRepClass3d_SolidClassifier.hxx>
namespace IfcGeom {
namespace ifcopenshell { namespace geometry {
namespace impl {
template <typename T>
@@ -113,7 +114,7 @@ namespace IfcGeom {
std::vector<T> ts_filtered;
const TopoDS_Shape& A = shapes_.find(t)->second;
if (IfcGeom::Kernel::count(A, TopAbs_SHELL) == 0) {
if (kernels::OpenCascadeKernel::count(A, TopAbs_SHELL) == 0) {
return ts_filtered;
}
@@ -122,21 +123,21 @@ namespace IfcGeom {
typename std::vector<T>::const_iterator it = ts.begin();
for (it = ts.begin(); it != ts.end(); ++it) {
const TopoDS_Shape& B = shapes_.find(*it)->second;
if (IfcGeom::Kernel::count(B, TopAbs_SHELL) == 0) {
if (kernels::OpenCascadeKernel::count(B, TopAbs_SHELL) == 0) {
continue;
}
if (completely_within) {
BRepAlgoAPI_Cut cut(B, A);
if (cut.IsDone()) {
if (IfcGeom::Kernel::count(cut.Shape(), TopAbs_SHELL) == 0) {
if (kernels::OpenCascadeKernel::count(cut.Shape(), TopAbs_SHELL) == 0) {
ts_filtered.push_back(*it);
}
}
} else {
BRepAlgoAPI_Common common(A, B);
if (common.IsDone()) {
if (IfcGeom::Kernel::count(common.Shape(), TopAbs_SHELL) > 0) {
if (kernels::OpenCascadeKernel::count(common.Shape(), TopAbs_SHELL) > 0) {
ts_filtered.push_back(*it);
}
}
@@ -152,7 +153,7 @@ namespace IfcGeom {
std::vector<T> ts;
if (IfcGeom::Kernel::count(s, TopAbs_SHELL) == 0) {
if (kernels::OpenCascadeKernel::count(s, TopAbs_SHELL) == 0) {
return ts;
}
@@ -169,13 +170,13 @@ namespace IfcGeom {
for (it = ts.begin(); it != ts.end(); ++it) {
const TopoDS_Shape& B = shapes_.find(*it)->second;
if (IfcGeom::Kernel::count(B, TopAbs_SHELL) == 0) {
if (kernels::OpenCascadeKernel::count(B, TopAbs_SHELL) == 0) {
continue;
}
BRepAlgoAPI_Common common(s, B);
if (common.IsDone()) {
if (IfcGeom::Kernel::count(common.Shape(), TopAbs_SHELL) > 0) {
if (kernels::OpenCascadeKernel::count(common.Shape(), TopAbs_SHELL) > 0) {
ts_filtered.push_back(*it);
}
}
@@ -251,30 +252,30 @@ namespace IfcGeom {
tree() {};
tree(IfcParse::IfcFile& f) {
add_file(f, IfcGeom::IteratorSettings());
add_file(f, ifcopenshell::geometry::settings());
}
tree(IfcParse::IfcFile& f, const IfcGeom::IteratorSettings& settings) {
tree(IfcParse::IfcFile& f, const ifcopenshell::geometry::settings& settings) {
add_file(f, settings);
}
void add_file(IfcParse::IfcFile& f, const IfcGeom::IteratorSettings& settings) {
IfcGeom::IteratorSettings settings_ = settings;
settings_.set(IfcGeom::IteratorSettings::DISABLE_TRIANGULATION, true);
settings_.set(IfcGeom::IteratorSettings::USE_WORLD_COORDS, true);
settings_.set(IfcGeom::IteratorSettings::SEW_SHELLS, true);
void add_file(IfcParse::IfcFile& f, const ifcopenshell::geometry::settings& settings) {
ifcopenshell::geometry::settings settings_ = settings;
settings_.set(ifcopenshell::geometry::settings::DISABLE_TRIANGULATION, true);
settings_.set(ifcopenshell::geometry::settings::USE_WORLD_COORDS, true);
settings_.set(ifcopenshell::geometry::settings::SEW_SHELLS, true);
IfcGeom::Iterator<double> it(settings_, &f);
Iterator it(settings_, &f);
if (it.initialize()) {
do {
IfcGeom::BRepElement<double>* elem = (IfcGeom::BRepElement<double>*)it.get();
add((IfcUtil::IfcBaseEntity*)f.instance_by_id(elem->id()), elem->geometry().as_compound());
NativeElement* elem = (NativeElement*)it.get();
add((IfcUtil::IfcBaseEntity*)f.instance_by_id(elem->id()), ((OpenCascadeShape*)elem->geometry().as_compound())->shape());
} while (it.next());
}
}
};
}
}}
#endif
@@ -0,0 +1,219 @@
#include "OpenCascadeConversionResult.h"
#include "../../../ifcparse/IfcLogger.h"
#include "../../../ifcgeom/schema_agnostic/IfcGeomRepresentation.h"
#include <TopoDS.hxx>
#include <map>
namespace {
// We bypass the conversion to gp_GTrsf, because it does not work
void taxonomy_transform(const Eigen::Matrix4d* m, gp_XYZ& xyz) {
if (m) {
Eigen::Vector4d v(xyz.X(), xyz.Y(), xyz.Z(), 1.0);
auto v2 = (*m * v).eval();
xyz.ChangeData()[0] = v2(0);
xyz.ChangeData()[1] = v2(1);
xyz.ChangeData()[2] = v2(2);
}
}
}
void ifcopenshell::geometry::OpenCascadeShape::Triangulate(const settings& settings, const ifcopenshell::geometry::taxonomy::matrix4& place, Representation::Triangulation* t, int surface_style_id) const {
// @todo remove duplication with OpenCascadeKernel::convert(const taxonomy::matrix4* matrix, gp_GTrsf& trsf);
// above can be static?
// A 3x3 matrix to rotate the vertex normals
boost::optional<gp_Mat> rotation_matrix;
if (place.components_) {
const auto& m = *place.components_;
rotation_matrix.emplace(
m(0, 0), m(0, 1), m(0, 2),
m(1, 0), m(1, 1), m(1, 2),
m(2, 0), m(2, 1), m(2, 2)
);
}
// Triangulate the shape
try {
BRepMesh_IncrementalMesh(shape_, settings.deflection_tolerance());
} catch (...) {
// TODO: Catch outside
// Logger::Message(Logger::LOG_ERROR,"Failed to triangulate shape:",ifc_file->entityById(_id)->entity);
Logger::Message(Logger::LOG_ERROR, "Failed to triangulate shape");
return;
}
// Iterates over the faces of the shape
int num_faces = 0;
TopExp_Explorer exp;
for (exp.Init(shape_, TopAbs_FACE); exp.More(); exp.Next(), ++num_faces) {
TopoDS_Face face = TopoDS::Face(exp.Current());
TopLoc_Location loc;
Handle_Poly_Triangulation tri = BRep_Tool::Triangulation(face, loc);
if (!tri.IsNull()) {
// Keep track of the number of times an edge is used
// Manifold edges (i.e. edges used twice) are deemed invisible
std::map<std::pair<int, int>, int> edgecount;
std::vector<std::pair<int, int> > edges_temp;
const TColgp_Array1OfPnt& nodes = tri->Nodes();
const TColgp_Array1OfPnt2d& uvs = tri->UVNodes();
std::vector<gp_XYZ> coords;
BRepGProp_Face prop(face);
std::map<int, int> dict;
// Vertex normals are only calculated if vertices are not welded and calculation is not disable explicitly.
const bool calculate_normals = !settings.get(ifcopenshell::geometry::settings::WELD_VERTICES) &&
!settings.get(ifcopenshell::geometry::settings::NO_NORMALS);
for (int i = 1; i <= nodes.Length(); ++i) {
coords.push_back(nodes(i).Transformed(loc).XYZ());
taxonomy_transform(place.components_, *coords.rbegin());
const gp_XYZ& last = *coords.rbegin();
dict[i] = t->addVertex(surface_style_id, last.X(), last.Y(), last.Z());
if (calculate_normals) {
const gp_Pnt2d& uv = uvs(i);
gp_Pnt p;
gp_Vec normal_direction;
prop.Normal(uv.X(), uv.Y(), p, normal_direction);
gp_Vec normal(0., 0., 0.);
if (normal_direction.Magnitude() > 1.e-9) {
if (rotation_matrix) {
normal = gp_Dir(normal_direction.XYZ() * *rotation_matrix);
} else {
normal = normal_direction;
}
}
t->addNormal(normal.X(), normal.Y(), normal.Z());
}
}
const Poly_Array1OfTriangle& triangles = tri->Triangles();
for (int i = 1; i <= triangles.Length(); ++i) {
int n1, n2, n3;
if (face.Orientation() == TopAbs_REVERSED)
triangles(i).Get(n3, n2, n1);
else triangles(i).Get(n1, n2, n3);
/* An alternative would be to calculate normals based
* on the coordinates of the mesh vertices */
/*
const gp_XYZ pt1 = coords[n1-1];
const gp_XYZ pt2 = coords[n2-1];
const gp_XYZ pt3 = coords[n3-1];
const gp_XYZ v1 = pt2-pt1;
const gp_XYZ v2 = pt3-pt2;
gp_Dir normal = gp_Dir(v1^v2);
_normals.push_back((float)normal.X());
_normals.push_back((float)normal.Y());
_normals.push_back((float)normal.Z());
*/
t->addFace(surface_style_id, dict[n1], dict[n2], dict[n3]);
t->addEdge(dict[n1], dict[n2], edgecount, edges_temp);
t->addEdge(dict[n2], dict[n3], edgecount, edges_temp);
t->addEdge(dict[n3], dict[n1], edgecount, edges_temp);
}
for (std::vector<std::pair<int, int> >::const_iterator jt = edges_temp.begin(); jt != edges_temp.end(); ++jt) {
if (edgecount[*jt] == 1) {
// non manifold edge, face boundary
t->registerEdge(jt->first, jt->second);
}
}
}
}
/*
TODO: Unimplemented
if (!t.normals().empty() && settings().get(IfcGeom::IteratorSettings::GENERATE_UVS)) {
t.uvs() = box_project_uvs(t.verts(), t.normals());
}
if (num_faces == 0) {
// Edges are only emitted if there are no faces. A mixed representation of faces
// and loose edges is discouraged by the standard. An alternative would be to use
// TopExp_Explorer texp(s, TopAbs_EDGE, TopAbs_FACE) to find edges that do not
// belong to any face.
for (TopExp_Explorer texp(s, TopAbs_EDGE); texp.More(); texp.Next()) {
BRepAdaptor_Curve crv(TopoDS::Edge(texp.Current()));
GCPnts_QuasiUniformDeflection tessellater(crv, settings.deflection_tolerance());
int n = tessellater.NbPoints();
int start = (int)t->verts().size() / 3;
for (int i = 1; i <= n; ++i) {
gp_XYZ p = tessellater.Value(i).XYZ();
// // In case you want direction arrows on your edges
// double u = tessellater.Parameter(i);
// gp_XYZ p2, p3;
// gp_Pnt tmp;
// gp_Vec tmp2;
// crv.D1(u, tmp, tmp2);
// gp_Dir d1, d2, d3, d4;
// d1 = tmp2;
// if (texp.Current().Orientation() == TopAbs_REVERSED) {
// d1 = -d1;
// }
// if (fabs(d1.Z()) < 0.5) {
// d2 = d1.Crossed(gp::DZ());
// } else {
// d2 = d1.Crossed(gp::DY());
// }
// d3 = d1.XYZ() + d2.XYZ();
// d4 = d1.XYZ() - d2.XYZ();
// p2 = p - d3.XYZ() / 10.;
// p3 = p - d4.XYZ() / 10.;
// trsf.Transforms(p2);
// trsf.Transforms(p3);
// _material_ids.push_back(surface_style_id);
// _material_ids.push_back(surface_style_id);
// _verts.push_back(static_cast<P>(p2.X()));
// _verts.push_back(static_cast<P>(p2.Y()));
// _verts.push_back(static_cast<P>(p2.Z()));
// _verts.push_back(static_cast<P>(p3.X()));
// _verts.push_back(static_cast<P>(p3.Y()));
// _verts.push_back(static_cast<P>(p3.Z()));
trsf.Transforms(p);
t->material_ids().push_back(surface_style_id);
t->verts().push_back(static_cast<double>(p.X()));
t->verts().push_back(static_cast<double>(p.Y()));
t->verts().push_back(static_cast<double>(p.Z()));
if (i > 1) {
t->edges().push_back(start + i - 2);
t->edges().push_back(start + i - 1);
// _edges.push_back(start + 3 * (i - 2) + 2);
// _edges.push_back(start + 3 * (i - 1) + 2);
}
// _edges.push_back(start + 3 * (i - 1) + 0);
// _edges.push_back(start + 3 * (i - 1) + 2);
// _edges.push_back(start + 3 * (i - 1) + 1);
// _edges.push_back(start + 3 * (i - 1) + 2);
}
}
}
*/
BRepTools::Clean(shape_);
}
int ifcopenshell::geometry::OpenCascadeShape::surface_genus() const {
throw std::runtime_error("Not implemented");
}
bool ifcopenshell::geometry::OpenCascadeShape::is_manifold() const {
throw std::runtime_error("Not implemented");
}
@@ -0,0 +1,82 @@
/********************************************************************************
* *
* 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/>. *
* *
********************************************************************************/
#ifndef IFCGEOMOPENCASCADEREPRESENTATION_H
#define IFCGEOMOPENCASCADEREPRESENTATION_H
#include <BRepMesh_IncrementalMesh.hxx>
#include <BRepGProp_Face.hxx>
#include <Poly_Triangulation.hxx>
#include <TColgp_Array1OfPnt.hxx>
#include <TColgp_Array1OfPnt2d.hxx>
#include <TopExp_Explorer.hxx>
#include <BRepTools.hxx>
#include <gp_GTrsf.hxx>
#include <BRepAdaptor_Curve.hxx>
#include <GCPnts_QuasiUniformDeflection.hxx>
#include "../../../ifcgeom/schema_agnostic/ConversionResult.h"
namespace ifcopenshell {
namespace geometry {
class OpenCascadeShape : public ConversionResultShape {
public:
OpenCascadeShape(const TopoDS_Shape& shape)
: shape_(shape) {}
const TopoDS_Shape& shape() const { return shape_; }
operator const TopoDS_Shape& () { return shape_; }
virtual void Triangulate(const settings& settings, const ifcopenshell::geometry::taxonomy::matrix4& place, Representation::Triangulation* t, int surface_style_id) const;
virtual void Serialize(std::string&) const {
throw std::runtime_error("Not implemented");
}
virtual ConversionResultShape* clone() const {
return new OpenCascadeShape(shape_);
}
virtual bool is_manifold() const;
virtual double bounding_box(void*&) const {
throw std::runtime_error("Not implemented");
}
virtual int num_vertices() const {
throw std::runtime_error("Not implemented");
}
virtual void set_box(void*) {
throw std::runtime_error("Not implemented");
}
virtual int surface_genus() const;
private:
TopoDS_Shape shape_;
};
}
}
#endif
@@ -0,0 +1,191 @@
/********************************************************************************
* *
* 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/>. *
* *
********************************************************************************/
#ifndef OPENCASCADEKERNEL_H
#define OPENCASCADEKERNEL_H
#include <cmath>
#include <gp_Pnt.hxx>
#include <gp_Vec.hxx>
#include <gp_Mat.hxx>
#include <gp_Mat2d.hxx>
#include <gp_GTrsf.hxx>
#include <gp_GTrsf2d.hxx>
#include <gp_Trsf.hxx>
#include <gp_Trsf2d.hxx>
#include <TopoDS.hxx>
#include <TopoDS_Wire.hxx>
#include <TopoDS_Face.hxx>
#include <Geom_Curve.hxx>
#include <gp_Pln.hxx>
#include <TColgp_SequenceOfPnt.hxx>
#include <TopTools_ListOfShape.hxx>
#include <BOPAlgo_Operation.hxx>
#include <BRep_Builder.hxx>
#include <BRepBuilderAPI_MakeEdge.hxx>
#include "../../../ifcgeom/kernel_agnostic/AbstractKernel.h"
#include "../../../ifcgeom/schema_agnostic/IfcGeomElement.h"
#include "../../../ifcgeom/schema_agnostic/IfcGeomRepresentation.h"
#include "../../../ifcgeom/schema_agnostic/ConversionResult.h"
#include "../../../ifcgeom/kernels/opencascade/OpenCascadeConversionResult.h"
#include "../../../ifcgeom/schema_agnostic/ifc_geom_api.h"
#include "../../../ifcgeom/taxonomy.h"
#include "../../../ifcgeom/ConversionSettings.h"
// Define this in case you want to conserve memory usage at all cost. This has been
// benchmarked extensively: https://github.com/IfcOpenShell/IfcOpenShell/pull/47
// #define NO_CACHE
#ifdef NO_CACHE
#define IN_CACHE(T,E,t,e)
#define CACHE(T,E,e)
#else
#define IN_CACHE(T,E,t,e) std::map<int,t>::const_iterator it = cache.T.find(E->data().id());\
if ( it != cache.T.end() ) { e = it->second; return true; }
#define CACHE(T,E,e) cache.T[E->data().id()] = e;
#endif
namespace ifcopenshell {
namespace geometry {
namespace kernels {
/*
class IFC_GEOM_API POSTFIX_SCHEMA(Cache) {
public:
#include "IfcRegisterCreateCache.h"
std::map<int, TopoDS_Shape> Shape;
};
*/
class IFC_GEOM_API OpenCascadeKernel : public AbstractKernel {
private:
// faceset_helper traverses the forward instance references of IfcConnectedFaceSet and then provides a mapping
// M of (IfcCartesianPoint, IfcCartesianPoint) -> TopoDS_Edge, where M(a, b) is a partner of M(b, a), ie share
// the same underlying edge but with orientation reversed. This then later speeds op the process of creating a
// manifold Shell / Solid from this set of faces. Only IfcPolyLoop instances are used. Points within the tolerance
// threshiold are merged, so consider points a, b, c, distance(a, b) < eps then M(a, b) = Null, M(a, b) = M(a, c).
class faceset_helper {
private:
OpenCascadeKernel* kernel_;
std::set<int> duplicates_;
std::map<int, int> vertex_mapping_;
std::map<std::pair<int, int>, TopoDS_Edge> edges_;
double eps_;
bool non_manifold_;
void loop_(const taxonomy::loop* ps, const std::function<void(int, int, bool)>& callback);
public:
faceset_helper(OpenCascadeKernel* kernel, const taxonomy::shell* l);
~faceset_helper();
bool non_manifold() const { return non_manifold_; }
bool& non_manifold() { return non_manifold_; }
bool edge(int A, int B, TopoDS_Edge& e);
bool wire(const taxonomy::loop* loop, TopoDS_Wire& wire);
bool wires(const taxonomy::loop* loop, TopTools_ListOfShape& wires);
double epsilon() const {
return eps_;
}
};
/*
#ifndef NO_CACHE
POSTFIX_SCHEMA(Cache) cache;
#endif
*/
faceset_helper* faceset_helper_;
public:
OpenCascadeKernel(ConversionSettings& settings)
: AbstractKernel("opencascade", settings)
, faceset_helper_(nullptr)
{}
OpenCascadeKernel(const OpenCascadeKernel& other)
: AbstractKernel("opencascade", other.settings_) {
*this = other;
}
static double shape_volume(const TopoDS_Shape&);
static double face_area(const TopoDS_Face&);
static int count(const TopoDS_Shape& s, TopAbs_ShapeEnum t, bool unique = false);
bool create_solid_from_compound(const TopoDS_Shape& compound, TopoDS_Shape& shape);
bool create_solid_from_faces(const TopTools_ListOfShape& face_list, TopoDS_Shape& shape);
bool convert(const taxonomy::extrusion*, TopoDS_Shape&);
bool convert(const taxonomy::face*, TopoDS_Shape&);
bool convert(const taxonomy::loop*, TopoDS_Wire&);
bool convert(const taxonomy::matrix4*, gp_GTrsf&);
bool convert(const taxonomy::shell*, TopoDS_Shape&);
bool boolean_operation(const TopoDS_Shape& a_, const TopTools_ListOfShape& b__, BOPAlgo_Operation op, TopoDS_Shape& result, double fuzziness = -1.);
const TopoDS_Shape& ensure_fit_for_subtraction(const TopoDS_Shape& shape, TopoDS_Shape& solid);
bool flatten_shape_list(const ifcopenshell::geometry::ConversionResults& shapes, TopoDS_Shape& result, bool fuse);
bool is_compound(const TopoDS_Shape& shape);
TopoDS_Shape apply_transformation(const TopoDS_Shape& s, const taxonomy::matrix4& t);
TopoDS_Shape apply_transformation(const TopoDS_Shape& s, const gp_GTrsf& t);
TopoDS_Shape apply_transformation(const TopoDS_Shape& s, const gp_Trsf& t);
virtual bool convert_impl(const taxonomy::face*, ifcopenshell::geometry::ConversionResults&);
virtual bool convert_impl(const taxonomy::shell*, ifcopenshell::geometry::ConversionResults&);
virtual bool convert_impl(const taxonomy::extrusion*, ifcopenshell::geometry::ConversionResults&);
virtual bool convert_impl(const taxonomy::boolean_result*, ifcopenshell::geometry::ConversionResults&);
};
/*
IfcUtil::IfcBaseClass* POSTFIX_SCHEMA(tesselate_)(const TopoDS_Shape& shape, double deflection);
IfcUtil::IfcBaseClass* POSTFIX_SCHEMA(serialise_)(const TopoDS_Shape& shape, bool advanced);
*/
template <typename T, typename U>
T convert_xyz(const U& u) {
const auto& vs = u.ccomponents();
return T(vs(0), vs(1), vs(2));
}
// @todo eliminate
template <typename T, typename U>
T convert_xyz2(const U& vs) {
return T(vs(0), vs(1), vs(2));
}
}
}
}
#endif
@@ -0,0 +1,804 @@
#include "boolean_utils.h"
#include "IfcGeomTree.h"
#include <BRepBuilderAPI_Copy.hxx>
#include <TopExp_Explorer.hxx>
#include <GProp_GProps.hxx>
#include <BRepGProp.hxx>
#include <TopExp.hxx>
#include <TopoDS.hxx>
#include <Bnd_Box.hxx>
#include <Extrema_ExtPC.hxx>
#include <Geom_Plane.hxx>
#include <Geom_BSplineCurve.hxx>
#include <ShapeUpgrade_UnifySameDomain.hxx>
#include <GeomAPI_ExtremaCurveCurve.hxx>
#include <ShapeAnalysis_Surface.hxx>
#include <BRepBuilderAPI_MakeFace.hxx>
#include <Standard_Version.hxx>
#include <BRepAdaptor_Curve.hxx>
#include <BRepExtrema_DistShapeShape.hxx>
#include <BRepBndLib.hxx>
#include <vector>
void ifcopenshell::geometry::util::copy_operand(const TopTools_ListOfShape & l, TopTools_ListOfShape & r) {
#if OCC_VERSION_HEX < 0x70000
r.Clear();
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.
r.Assign(l);
#endif
}
TopoDS_Shape ifcopenshell::geometry::util::copy_operand(const TopoDS_Shape & s) {
#if OCC_VERSION_HEX < 0x70000
return BRepBuilderAPI_Copy(s);
#else
return s;
#endif
}
double ifcopenshell::geometry::util::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()) {
const TopoDS_Edge& e = TopoDS::Edge(exp.Current());
TopoDS_Vertex v0, v1;
TopExp::Vertices(e, v0, v1);
if (!v0.IsNull() && !v1.IsNull() && v0.IsSame(v1)) {
// Don't consider a 3d-degenerate edge (for example cone apex)
// in calculating overall shape min edge length.
continue;
}
GProp_GProps prop;
BRepGProp::LinearProperties(e, prop);
double l = prop.Mass();
if (l < min_edge_len) {
min_edge_len = l;
}
}
return min_edge_len;
}
double ifcopenshell::geometry::util::min_vertex_edge_distance(const TopoDS_Shape & a, double min_search, double max_search) {
double M = std::numeric_limits<double>::infinity();
TopTools_IndexedMapOfShape vertices, edges;
TopExp::MapShapes(a, TopAbs_VERTEX, vertices);
TopExp::MapShapes(a, TopAbs_EDGE, edges);
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));
gp_Pnt p = BRep_Tool::Pnt(v);
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));
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));
if (m < M && m > min_search) {
M = m;
}
}
}
}
return M;
}
bool ifcopenshell::geometry::util::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;
}
double ifcopenshell::geometry::util::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);
impl::tree<int> tree;
// Add faces to tree
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);
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) {
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());
// nb: TopAbs_ON is explicitly not considered to prevent matching adjacent faces
// with similar orientations.
if (cls.Perform(gp_Pnt2d(u, v)) == TopAbs_IN) {
gp_Pnt test2;
p1->D0(u, v, test2);
double w = std::abs(gp_Vec(p1->Position().Direction().XYZ()).Dot(test2.XYZ() - test.XYZ()));
if (w < M) {
M = w;
}
}
}
}
}
}
return M;
}
int ifcopenshell::geometry::util::bounding_box_overlap(double p, const TopoDS_Shape & a, const TopTools_ListOfShape & b, TopTools_ListOfShape & c) {
int N = 0;
Bnd_Box A;
BRepBndLib::Add(a, A);
if (A.IsVoid()) {
return 0;
}
TopTools_ListIteratorOfListOfShape it(b);
for (; it.More(); it.Next()) {
Bnd_Box B;
BRepBndLib::Add(it.Value(), B);
if (B.IsVoid()) {
continue;
}
if (A.Distance(B) < p) {
c.Append(it.Value());
} else {
++N;
}
}
return N;
}
bool ifcopenshell::geometry::util::get_edge_axis(const TopoDS_Edge & e, gp_Ax1 & ax) {
double _, __;
auto crv = BRep_Tool::Curve(e, _, __);
auto line = Handle_Geom_Line::DownCast(crv);
auto bsple = Handle_Geom_BSplineCurve::DownCast(crv);
if (line) {
ax = line->Position();
return true;
} else if (bsple) {
if (bsple->NbPoles() == 2 && bsple->Degree() == 1) {
gp_Dir V(bsple->Poles().Last().XYZ() - bsple->Poles().First().XYZ());
ax = gp_Ax1(bsple->Poles().First(), V);
return true;
}
}
return false;
}
bool ifcopenshell::geometry::util::is_subset(const TopTools_IndexedMapOfShape & lhs, const TopTools_IndexedMapOfShape & rhs) {
if (rhs.Extent() < lhs.Extent()) {
return false;
}
for (int i = 1; i < lhs.Extent(); ++i) {
auto& s = lhs.FindKey(i);
if (!rhs.Contains(s)) {
return false;
}
}
return true;
}
bool ifcopenshell::geometry::util::is_extrusion(const gp_Vec & v, const TopoDS_Shape & s, TopoDS_Face & base, std::pair<double, double>& interval) {
// This assumes UnifySameDomain has been processed on s, so that
// the extrusion top and bottom are a single face.
TopTools_IndexedDataMapOfShapeListOfShape mapping;
TopExp::MapShapesAndAncestors(s, TopAbs_EDGE, TopAbs_FACE, mapping);
TopExp::MapShapesAndAncestors(s, TopAbs_VERTEX, TopAbs_FACE, mapping);
TopTools_ListOfShape parallel;
TopTools_IndexedMapOfShape curved_orthogonal;
gp_Ax1 ax;
gp_Ax1 V(gp::Origin(), v);
// Segment edges in parallel to extrusion direction, and orthogonal or curved,
// where the latter two categories have to make the edges part of the base or
// top face. When neither of these categories the shape is not a extrusion
// or the extrusion direction is not orthogonal to its basis.
for (int i = 1; i < mapping.Extent(); ++i) {
auto& s = mapping.FindKey(i);
if (s.ShapeType() != TopAbs_EDGE) {
continue;
}
// @todo use a linear tolernace and the face extrimities, see #2218
const TopoDS_Edge& e = TopoDS::Edge(s);
if (!get_edge_axis(e, ax)) {
// curved
curved_orthogonal.Add(e);
} else if (ax.IsParallel(V, 1.e-7)) {
parallel.Append(e);
} else if (ax.IsNormal(V, 1.e-7)) {
// ortho
curved_orthogonal.Add(e);
} else {
return false;
}
}
// Select the two faces for which their edges are subsets
// of the ortho/curved edges
TopTools_IndexedMapOfShape ortho_faces;
for (TopExp_Explorer exp(s, TopAbs_FACE); exp.More(); exp.Next()) {
TopTools_IndexedMapOfShape face_edges;
TopExp::MapShapes(exp.Current(), TopAbs_EDGE, face_edges);
if (is_subset(face_edges, curved_orthogonal)) {
ortho_faces.Add(exp.Current());
}
}
// There should be a basis and top face
if (ortho_faces.Extent() != 2) {
return false;
}
// For the parallel edges assert that its two vertices are part
// of both the basis and the top face.
for (TopTools_ListIteratorOfListOfShape it(parallel);
it.More(); it.Next()) {
TopoDS_Vertex v01[2];
TopExp::Vertices(TopoDS::Edge(it.Value()), v01[0], v01[1]);
TopTools_IndexedMapOfShape v_ortho_faces;
int nb_ortho_faces[2] = { 0,0 };
for (int i = 0; i < 2; ++i) {
auto& faces = mapping.FindFromKey(v01[i]);
for (TopTools_ListIteratorOfListOfShape jt(faces);
jt.More(); jt.Next()) {
if (ortho_faces.Contains(jt.Value())) {
nb_ortho_faces[i] ++;
v_ortho_faces.Add(jt.Value());
}
}
}
bool sets_equal = v_ortho_faces.Size() == ortho_faces.Size() && is_subset(v_ortho_faces, ortho_faces);
if (!sets_equal) {
return false;
}
}
// Assert the base/top faces are planar and get the interval
// (dot products along axis) for which the extrusion is defined
// If necessary swap the two faces so that the basis face has
// the smallest dot product along the axis.
auto f0 = TopoDS::Face(ortho_faces.FindKey(1));
auto f1 = TopoDS::Face(ortho_faces.FindKey(2));
const Handle(Geom_Surface)& f0_s = BRep_Tool::Surface(f0);
const Handle(Geom_Surface)& f1_s = BRep_Tool::Surface(f1);
auto p0 = Handle(Geom_Plane)::DownCast(f0_s);
auto p1 = Handle(Geom_Plane)::DownCast(f1_s);
if (p0.IsNull() || p1.IsNull()) {
return false;
}
auto dot0 = p0->Location().XYZ().Dot(v.XYZ());
auto dot1 = p1->Location().XYZ().Dot(v.XYZ());
if (dot0 > dot1) {
std::swap(dot0, dot1);
std::swap(f0, f1);
}
base = f0;
interval = { dot0, dot1 };
return true;
}
int ifcopenshell::geometry::util::eliminate_touching_operands(double prec, const TopoDS_Shape & a, const TopTools_ListOfShape & bs, TopTools_ListOfShape & c) {
TopTools_IndexedMapOfShape a_faces;
TopExp::MapShapes(a, TopAbs_FACE, a_faces);
// Check if any of the faces in a are non-planar, which is
// not supported by this quick check.
for (int i = 1; i <= a_faces.Extent(); ++i) {
auto surf = BRep_Tool::Surface(TopoDS::Face(a_faces(i)));
if (surf->DynamicType() != STANDARD_TYPE(Geom_Plane)) {
return 0;
}
}
TopTools_IndexedMapOfShape a_vertices;
TopExp::MapShapes(a, TopAbs_VERTEX, a_vertices);
ifcopenshell::geometry::impl::tree<int> tree;
// Add faces to tree
for (int i = 1; i <= a_faces.Extent(); ++i) {
tree.add(i, a_faces(i));
}
int N = 0;
TopTools_ListIteratorOfListOfShape it(bs);
for (; it.More(); it.Next()) {
bool is_touching = false;
auto& b = it.Value();
TopTools_IndexedMapOfShape b_faces;
TopExp::MapShapes(b, TopAbs_FACE, b_faces);
// Check if any of the faces in b are non-planar, which is
// not supported by this quick check.
for (int i = 1; i <= b_faces.Extent(); ++i) {
auto surf = BRep_Tool::Surface(TopoDS::Face(b_faces(i)));
if (surf->DynamicType() != STANDARD_TYPE(Geom_Plane)) {
continue;
}
}
TopTools_IndexedMapOfShape b_vertices;
TopExp::MapShapes(b, TopAbs_VERTEX, b_vertices);
for (int k = 1; k <= b_faces.Extent(); ++k) {
const TopoDS_Face& f_b = TopoDS::Face(b_faces(k));
Bnd_Box B;
BRepBndLib::Add(f_b, B);
// Query tree using b_face bounding box
for (auto& i : tree.select_box(B, false)) {
const TopoDS_Face& f_a = TopoDS::Face(a_faces(i));
TopTools_IndexedMapOfShape f_a_vertices;
TopExp::MapShapes(f_a, TopAbs_VERTEX, f_a_vertices);
BRepGProp_Face prop_a(f_a);
BRepGProp_Face prop_b(f_b);
gp_Pnt p_a, p_b;
gp_Vec v_a, v_b;
double u0, u1, v0, v1;
prop_a.Bounds(u0, u1, v0, v1);
prop_a.Normal((u0 + u1) / 2., (u0 + u1) / 2., p_a, v_a);
prop_b.Bounds(u0, u1, v0, v1);
prop_b.Normal((u0 + u1) / 2., (u0 + u1) / 2., p_b, v_b);
bool all_vertices_behind_f_a = true;
// Check if all 'other' vertices in a are pointing
// away from the face in a, so that there is no geometry
// from a in front of the face that could participate
// in the boolean subtraction.
for (int j = 1; j <= a_vertices.Extent(); ++j) {
if (!f_a_vertices.Contains(a_vertices(j))) {
auto p = BRep_Tool::Pnt(TopoDS::Vertex(a_vertices(j)));
if ((p.XYZ() - p_a.XYZ()).Dot(v_a.XYZ()) > prec) {
all_vertices_behind_f_a = false;
break;
}
}
}
if (!all_vertices_behind_f_a) {
continue;
}
// Check if surface normals are opposite
if (v_a.IsOpposite(v_b, 1.e-5)) {
// Check if faces are co-planar
if ((p_b.XYZ() - p_a.XYZ()).Dot(v_a.XYZ()) <= prec) {
TopTools_IndexedMapOfShape f_b_vertices;
TopExp::MapShapes(f_b, TopAbs_VERTEX, f_b_vertices);
bool all_vertices_behind_f_b = true;
// Check if all 'other' vertices in b are pointing
// away from the face in a. So that a boolean subtraction
// would not alter a.
for (int j = 1; j <= b_vertices.Extent(); ++j) {
if (!f_b_vertices.Contains(b_vertices(j))) {
auto p = BRep_Tool::Pnt(TopoDS::Vertex(b_vertices(j)));
if ((p.XYZ() - p_a.XYZ()).Dot(v_a.XYZ()) < prec * 10.) {
all_vertices_behind_f_b = false;
break;
}
}
}
if (all_vertices_behind_f_b) {
is_touching = true;
break;
}
}
}
}
if (is_touching) {
break;
}
}
if (!is_touching) {
c.Append(it.Value());
} else {
++N;
}
}
return N;
}
TopoDS_Shape ifcopenshell::geometry::util::unify(const TopoDS_Shape & s, double tolerance) {
tolerance = (std::min)(min_edge_length(s) / 2., tolerance);
ShapeUpgrade_UnifySameDomain usd(s);
#if OCC_VERSION_HEX >= 0x70200
usd.SetSafeInputMode(true);
#endif
#if OCC_VERSION_HEX >= 0x70100
usd.SetLinearTolerance(tolerance);
usd.SetAngularTolerance(1.e-3);
#endif
usd.Build();
return usd.Shape();
}
bool ifcopenshell::geometry::util::boolean_subtraction_2d_using_builder(const TopoDS_Shape & a_input, const TopTools_ListOfShape & b_input, TopoDS_Shape & result, double eps) {
ifcopenshell::geometry::impl::tree<int> edge_tree;
TopTools_ListOfShape ab_input = b_input;
ab_input.Prepend(a_input);
TopTools_ListIteratorOfListOfShape it(ab_input);
int shape_index = 0;
int edge_index = 0;
std::map<int, int> edge_index_to_shape_index;
std::vector<TopoDS_Shape> shapes;
std::vector<std::pair<size_t, TopoDS_Edge>> edges;
// First is the outer wire
std::vector<TopoDS_Wire> wires;
for (; it.More(); it.Next(), ++shape_index) {
if (it.Value().ShapeType() != TopAbs_FACE) {
return false;
}
const TopoDS_Face& f = TopoDS::Face(it.Value());
TopoDS_Wire outer_wire;
if (shape_index == 0) {
outer_wire = BRepTools::OuterWire(f);
wires.push_back(outer_wire);
}
size_t num_wires = 0;
TopoDS_Iterator it2(it.Value());
for (; it2.More(); it2.Next()) {
++num_wires;
if (outer_wire.IsNull() || !it2.Value().IsSame(outer_wire)) {
wires.push_back(TopoDS::Wire(it2.Value()));
if (shape_index == 0 && num_wires > 0) {
// An inner wire on the first operand face: reverse, because
// MakeFace expects inner boundaries to be added as bounded
// areas.
wires.back().Reverse();
}
}
}
if (num_wires > 1 && shape_index != 0) {
// The first operand can have inner wires, but the others
// can't because a inner wire would result in an additional
// outer wire for the result.
return false;
}
shapes.push_back(it.Value());
TopExp_Explorer exp(it.Value(), TopAbs_EDGE);
for (; exp.More(); exp.Next(), ++edge_index) {
edge_tree.add(edge_index, exp.Current());
edge_index_to_shape_index[edge_index] = shape_index;
edges.push_back({ shape_index, TopoDS::Edge(exp.Current()) });
}
}
{
TopoDS_Compound C;
BRep_Builder BB;
BB.MakeCompound(C);
for (auto& w : wires) {
BB.Add(C, w);
}
BRepTools::Write(C, "debug.brep");
}
shape_index = 0;
edge_index = 0;
it.Initialize(ab_input);
for (; it.More(); it.Next(), ++shape_index) {
TopExp_Explorer exp(it.Value(), TopAbs_EDGE);
for (; exp.More(); exp.Next(), ++edge_index) {
Bnd_Box b;
BRepBndLib::Add(exp.Current(), b);
b.Enlarge(eps);
for (auto& i : edge_tree.select_box(b)) {
if (i == edge_index) {
// Skip self-selection
continue;
}
if (edges[i].first == shape_index) {
// Skip edges of the same operand
continue;
}
const TopoDS_Edge& e0 = TopoDS::Edge(exp.Current());
const TopoDS_Edge& e1 = edges[i].second;
double u11, u12, u21, u22, U1, U2;
GeomAPI_ExtremaCurveCurve ecc(
BRep_Tool::Curve(e0, u11, u12),
BRep_Tool::Curve(e1, u21, u22)
);
// @todo: extend this to work in case of multiple extrema and curved segments.
const bool unbounded_intersects = (!ecc.Extrema().IsParallel() && ecc.NbExtrema() == 1 && ecc.Distance(1) < eps);
if (unbounded_intersects) {
ecc.Parameters(1, U1, U2);
if (u11 > u12) {
std::swap(u11, u12);
}
if (u21 > u22) {
std::swap(u21, u22);
}
/// @todo: tfk: probably need different thresholds on non-linear curves
u11 -= eps;
u12 += eps;
u21 -= eps;
u22 += eps;
if (u11 < U1 && U1 < u12 && u21 < U2 && U2 < u22) {
// Edge curves belonging to different operands intersect, don't process
// using builder.
Logger::Notice("Intersecting boundaries");
return false;
}
}
}
}
}
// Only inner wires are considered that are directly contained in the outer wire
// Redundant subtractions are eliminated.
std::vector<bool> redundant(wires.size(), false);
std::vector<TopoDS_Face> wire_faces;
wire_faces.reserve(wires.size());
std::vector<BRepTopAdaptor_FClass2d> wire_clss;
wire_clss.reserve(wires.size());
std::vector<std::unique_ptr<ShapeAnalysis_Surface>> sass;
sass.reserve(wires.size());
for (auto& w : wires) {
wire_faces.push_back(BRepBuilderAPI_MakeFace(w).Face());
wire_clss.emplace_back(wire_faces.back(), eps);
sass.push_back(std::make_unique<ShapeAnalysis_Surface>(BRep_Tool::Surface(wire_faces.back())));
}
// First check for containment in outer wire
for (auto it = ++wires.begin(); it != wires.end(); ++it) {
// Considering a single vertex is sufficient because we have already
// guaranteed that the edges of different operands do not cross.
TopoDS_Iterator it_ed(*it);
auto& ed = it_ed.Value();
TopoDS_Iterator it_v(ed);
auto& v = TopoDS::Vertex(it_v.Value());
auto pnt = BRep_Tool::Pnt(v);
auto p2d = sass[0]->ValueOfUV(pnt, eps);
if (wire_clss[0].Perform(p2d) != TopAbs_IN) {
// A wire is not contained in the outer wire, it's a subtraction without
// any effect and marked as redundant. Feeding it to the builder algo
// will likely cause problems.
redundant[std::distance(wires.begin(), it)] = true;
Logger::Notice("Subtraction operand outside of outer bound");
}
}
// Now build a tree to find inner wires contained in other inner wires
// NB first wire is *not* in this tree
ifcopenshell::geometry::impl::tree<int> wire_tree;
for (size_t wire_index = 1; wire_index < wires.size(); ++wire_index) {
wire_tree.add(wire_index, wires[wire_index]);
}
for (size_t wire_index = 1; wire_index < wires.size(); ++wire_index) {
Bnd_Box b;
BRepBndLib::Add(wires[wire_index], b);
b.Enlarge(eps);
// We're only selecting operands completely within b because we
// have already guaranteed they do not intersect. So they are
// either fully in or out. Selecting with complete_within=true
// will filter out some unnecessary cases. It also means we need
// that due this asymmetry we need to process all pairs of wire
// indices and not just the pairs where the first element is less
// than the second element.
for (auto& other_index : wire_tree.select_box(b, true)) {
// other_index is fully contained in wire_index
if (wire_index == other_index) {
continue;
}
TopoDS_Iterator it_ed(wires[other_index]);
auto& ed = it_ed.Value();
TopoDS_Iterator it_v(ed);
auto& v = TopoDS::Vertex(it_v.Value());
auto pnt = BRep_Tool::Pnt(v);
auto p2d = sass[wire_index]->ValueOfUV(pnt, eps);
if (wire_clss[wire_index].Perform(p2d) == TopAbs_IN) {
// A wire is contained within another operand
redundant[other_index] = true;
Logger::Notice("Subtraction operand contained in other");
}
}
}
BRepBuilderAPI_MakeFace mf(wire_faces[0]);
for (size_t wire_index = 1; wire_index < wires.size(); ++wire_index) {
if (!redundant[wire_index]) {
mf.Add(TopoDS::Wire(wires[wire_index].Reversed()));
}
}
result = mf.Face();
return true;
}
void ifcopenshell::geometry::util::points_on_planar_face_generator::reset() {
i = j = (int)inset_;
}
bool ifcopenshell::geometry::util::points_on_planar_face_generator::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;
}
@@ -0,0 +1,91 @@
/********************************************************************************
* *
* 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/>. *
* *
********************************************************************************/
#ifndef BOOLEAN_UTILS_H
#define BOOLEAN_UTILS_H
#include <TopoDS_Shape.hxx>
#include <TopTools_ListOfShape.hxx>
#include <Geom_Surface.hxx>
#include <TopoDS_Face.hxx>
#include <BRepTopAdaptor_FClass2d.hxx>
#include <BRep_Tool.hxx>
#include <BRepTools.hxx>
#include <TopTools_IndexedMapOfShape.hxx>
namespace ifcopenshell { namespace geometry {
namespace util {
void copy_operand(const TopTools_ListOfShape& l, TopTools_ListOfShape& r);
TopoDS_Shape copy_operand(const TopoDS_Shape& s);
double min_edge_length(const TopoDS_Shape& a);
double min_vertex_edge_distance(const TopoDS_Shape& a, double min_search, double max_search);
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;
bool inset_;
static const int N = 10;
public:
points_on_planar_face_generator(const TopoDS_Face& f, bool inset = false)
: f_(f)
, plane_(BRep_Tool::Surface(f_))
, cls_(f_, BRep_Tool::Tolerance(f_))
, i((int)inset), j((int)inset)
, inset_(inset)
{
BRepTools::UVBounds(f_, u0, u1, v0, v1);
}
void reset();
bool operator()(gp_Pnt& p);
};
bool faces_overlap(const TopoDS_Face& f, const TopoDS_Face& g);
double min_face_face_distance(const TopoDS_Shape& a, double max_search);
int bounding_box_overlap(double p, const TopoDS_Shape& a, const TopTools_ListOfShape& b, TopTools_ListOfShape& c);
bool get_edge_axis(const TopoDS_Edge& e, gp_Ax1& ax);
bool is_subset(const TopTools_IndexedMapOfShape& lhs, const TopTools_IndexedMapOfShape& rhs);
bool is_extrusion(const gp_Vec& v, const TopoDS_Shape& s, TopoDS_Face& base, std::pair<double, double>& interval);
int eliminate_touching_operands(double prec, const TopoDS_Shape& a, const TopTools_ListOfShape& bs, TopTools_ListOfShape& c);
TopoDS_Shape unify(const TopoDS_Shape& s, double tolerance);
bool boolean_subtraction_2d_using_builder(const TopoDS_Shape& a_input, const TopTools_ListOfShape& b_input, TopoDS_Shape& result, double eps);
}
} }
#endif
@@ -0,0 +1,85 @@
#include "OpenCascadeKernel.h"
#include <BRepPrimAPI_MakePrism.hxx>
using namespace ifcopenshell::geometry;
using namespace ifcopenshell::geometry::kernels;
bool OpenCascadeKernel::convert(const taxonomy::extrusion* extrusion, TopoDS_Shape& shape) {
const double& height = extrusion->depth;
if (height < precision_) {
Logger::Error("Non-positive extrusion height encountered for:", extrusion->instance);
return false;
}
TopoDS_Shape face;
if (!convert(&extrusion->basis, face)) {
return false;
}
/*
// @todo we need to decide whether the matrix is kept on the taxonomy node or
// move the TopoDS_Shape, but obviously not both.
gp_GTrsf gtrsf;
if (!convert(&extrusion->matrix, gtrsf)) {
Logger::Error("Unable to move extrusion");
}
auto trsf = gtrsf.Trsf();
*/
const auto& fs = extrusion->direction.ccomponents();
gp_Dir dir(fs(0), fs(1), fs(2));
shape.Nullify();
if (face.ShapeType() == TopAbs_COMPOUND) {
// For compounds (most likely the result of a IfcCompositeProfileDef)
// create a compound solid shape.
TopExp_Explorer exp(face, TopAbs_FACE);
TopoDS_CompSolid compound;
BRep_Builder builder;
builder.MakeCompSolid(compound);
int num_faces_extruded = 0;
for (; exp.More(); exp.Next(), ++num_faces_extruded) {
builder.Add(compound, BRepPrimAPI_MakePrism(exp.Current(), height*dir));
}
if (num_faces_extruded) {
shape = compound;
}
}
if (shape.IsNull()) {
shape = BRepPrimAPI_MakePrism(face, height*dir);
}
/*
if (!shape.IsNull()) {
// IfcSweptAreaSolid.Position (trsf) is an IfcAxis2Placement3D
// and therefore has a unit scale factor
shape.Move(trsf);
}
*/
return !shape.IsNull();
}
bool OpenCascadeKernel::convert_impl(const taxonomy::extrusion* extrusion, ifcopenshell::geometry::ConversionResults& results) {
TopoDS_Shape shape;
if (!convert(extrusion, shape)) {
return false;
}
results.emplace_back(ConversionResult(
extrusion->instance->data().id(),
extrusion->matrix,
new OpenCascadeShape(shape),
extrusion->surface_style
));
return true;
}
+298
View File
@@ -0,0 +1,298 @@
/********************************************************************************
* *
* 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/>. *
* *
********************************************************************************/
#include <gp_Vec.hxx>
#include <gp_Dir.hxx>
#include <gp_Pln.hxx>
#include <Geom_Line.hxx>
#include <Geom_Plane.hxx>
#include <BRepBuilderAPI_MakeFace.hxx>
#include <TopoDS.hxx>
#include <TopoDS_Wire.hxx>
#include <TopoDS_Face.hxx>
#include <TopExp_Explorer.hxx>
#include <TopoDS_Iterator.hxx>
#include <ShapeFix_Shape.hxx>
#include <ShapeFix_ShapeTolerance.hxx>
#include <BRep_Tool.hxx>
#include <TopTools_DataMapOfShapeInteger.hxx>
#include <BRepLib_FindSurface.hxx>
#include <ShapeExtend_MsgRegistrator.hxx>
#include <Message_Msg.hxx>
#include <ShapeFix_Edge.hxx>
#include "OpenCascadeKernel.h"
#include "face_definition.h"
using namespace ifcopenshell::geometry;
using namespace ifcopenshell::geometry::util;
using namespace ifcopenshell::geometry::kernels;
bool OpenCascadeKernel::convert(const taxonomy::face* face, TopoDS_Shape& result) {
auto bounds = face->children_as<taxonomy::loop>();
face_definition fd;
const bool is_face_surface = false; /* todo */
/*
if (is_face_surface) {
IfcSchema::IfcFaceSurface* fs = (IfcSchema::IfcFaceSurface*) l;
fs->FaceSurface();
// FIXME: Surfaces are interpreted as a TopoDS_Shape
TopoDS_Shape surface_shape;
if (!convert_shape(fs->FaceSurface(), surface_shape)) return false;
// FIXME: Assert this obtaines the only face
TopExp_Explorer exp(surface_shape, TopAbs_FACE);
if (!exp.More()) return false;
TopoDS_Face surface = TopoDS::Face(exp.Current());
fd.surface() = BRep_Tool::Surface(surface);
}
*/
const int num_bounds = bounds.size();
int num_outer_bounds = 0;
for (auto& bound : bounds) {
if (bound->external.get_value_or(false)) {
num_outer_bounds++;
}
}
// The number of outer bounds should be one according to the schema. Also Open Cascade
// expects this, but it is not strictly checked. Regardless, if the number is greater,
// the face will still be processed as long as there are no holes. A compound of faces
// is returned in that case.
if (num_bounds > 1 && num_outer_bounds > 1 && num_bounds != num_outer_bounds) {
Logger::Message(Logger::LOG_ERROR, "Invalid configuration of boundaries for:", face->instance);
return false;
}
if (num_outer_bounds > 1) {
Logger::Message(Logger::LOG_WARNING, "Multiple outer boundaries for:", face->instance);
fd.all_outer() = true;
}
TopTools_DataMapOfShapeInteger wire_senses;
for (int process_interior = 0; process_interior <= 1; ++process_interior) {
for (auto& bound : bounds) {
bool same_sense = true; /* todo bound->Orientation(); */
const bool is_interior =
!bound->external.get_value_or(false) &&
(num_bounds > 1) &&
(num_outer_bounds < num_bounds);
// The exterior face boundary is processed first
if (is_interior == !process_interior) continue;
TopoDS_Wire wire;
if (faceset_helper_ && is_polyhedron(bound)) {
if (!faceset_helper_->wire(bound, wire)) {
Logger::Message(Logger::LOG_WARNING, "Face boundary loop not included", bound->instance);
continue;
}
} else if (!convert(bound, wire)) {
Logger::Message(Logger::LOG_ERROR, "Failed to process face boundary loop", bound->instance);
return false;
}
if (!same_sense) {
wire.Reverse();
}
wire_senses.Bind(wire.Oriented(TopAbs_FORWARD), same_sense ? TopAbs_FORWARD : TopAbs_REVERSED);
fd.wires().emplace_back(wire);
}
}
if (fd.wires().empty()) {
Logger::Warning("Face with no boundaries", face->instance);
return false;
}
if (fd.surface().IsNull()) {
// Use the first wire to find a plane manually for polygonal wires
const TopoDS_Wire& wire = fd.wires().front();
if (is_polyhedron(wire)) {
TopExp_Explorer exp(wire, TopAbs_EDGE);
int count = 0;
TopoDS_Edge edges[2];
for (; exp.More(); exp.Next(), count++) {
if (count < 2) {
edges[count] = TopoDS::Edge(exp.Current());
}
}
if (count == 3) {
// Help Open Cascade by finding the plane more efficiently
double _, __;
Handle(Geom_Line) c1 = Handle(Geom_Line)::DownCast(BRep_Tool::Curve(edges[0], _, __));
Handle(Geom_Line) c2 = Handle(Geom_Line)::DownCast(BRep_Tool::Curve(edges[1], _, __));
const gp_Vec ab = c1->Position().Direction();
const gp_Vec ac = c2->Position().Direction();
const gp_Vec cross = ab.Crossed(ac);
if (cross.SquareMagnitude() > ALMOST_ZERO) {
const gp_Dir n = cross;
fd.surface() = new Geom_Plane(c1->Position().Location(), n);
}
} else {
gp_Pln pln;
if (approximate_plane_through_wire(wire, pln)) {
fd.surface() = new Geom_Plane(pln);
}
}
}
}
if (fd.surface().IsNull()) {
// BRepLib_FindSurface is used in case no surface is found or provided
const TopoDS_Wire& wire = fd.wires().front();
BRepLib_FindSurface fs(wire, precision_, true, true);
if (fs.Found()) {
fd.surface() = fs.Surface();
ShapeFix_ShapeTolerance ftol;
ftol.SetTolerance(wire, fs.ToleranceReached(), TopAbs_WIRE);
}
}
TopTools_ListOfShape face_list;
if (fd.surface().IsNull()) {
// The set of wires is triangulated in case no surface can be found
Logger::Message(Logger::LOG_WARNING, "Triangulating face boundaries for face", face->instance);
if (fd.all_outer()) {
for (const auto& w : fd.wires()) {
TopTools_ListOfShape fl;
triangulate_wire({ w }, fl);
face_list.Append(fl);
}
} else {
triangulate_wire(fd.wires(), face_list);
}
} else if (!fd.all_outer()) {
BRepBuilderAPI_MakeFace mf(fd.surface(), fd.outer_wire());
if (mf.IsDone()) {
// Is this necessary
TopoDS_Face f = mf.Face();
mf.Init(f);
for (auto it = fd.inner_wires().first; it != fd.inner_wires().second; ++it) {
mf.Add(*it);
}
face_list.Append(mf.Face());
}
} else {
for (const auto& w : fd.wires()) {
BRepBuilderAPI_MakeFace mf(fd.surface(), w);
if (mf.IsDone()) {
face_list.Append(mf.Face());
}
}
}
if (!fd.surface().IsNull()) {
// Some fixes for orientation and p-curves. If we have no surface, it
// means the face has been triangulated in which case none of these
// fixes are necessary.
if (fd.surface()->DynamicType() != STANDARD_TYPE(Geom_Plane)) {
// In case of (non-planar) face surface, p-curves need to be computed.
// For planar faces, Open Cascade generates p-curves on the fly.
for (TopTools_ListIteratorOfListOfShape it(face_list); it.More(); it.Next()) {
// Small chance there are multiple faces
const TopoDS_Face& occ_face = TopoDS::Face(it.Value());
for (TopExp_Explorer exp2(occ_face, TopAbs_EDGE); exp2.More(); exp2.Next()) {
const TopoDS_Edge& edge = TopoDS::Edge(exp2.Current());
ShapeFix_Edge fix_edge;
fix_edge.FixAddPCurve(edge, occ_face, false, precision_);
}
}
}
for (TopTools_ListIteratorOfListOfShape it(face_list); it.More(); it.Next()) {
const TopoDS_Face& occ_face = TopoDS::Face(it.Value());
ShapeFix_Face sfs(TopoDS::Face(occ_face));
TopTools_DataMapOfShapeListOfShape wire_map;
sfs.FixOrientation(wire_map);
TopoDS_Iterator jt(occ_face, false);
for (; jt.More(); jt.Next()) {
const TopoDS_Wire& w = TopoDS::Wire(jt.Value());
// tfk: @todo if wire_map contains w, I would assume wire_senses also contains w,
// this is not the case in github issue #405.
if (wire_map.IsBound(w) && wire_senses.IsBound(w)) {
const TopTools_ListOfShape& shapes = wire_map.Find(w);
TopTools_ListIteratorOfListOfShape kt(shapes);
for (; kt.More(); kt.Next()) {
// Apparently the wire got reversed, so register it with opposite orientation in the map
wire_senses.Bind(kt.Value(), wire_senses.Find(w) == TopAbs_FORWARD ? TopAbs_REVERSED : TopAbs_FORWARD);
}
}
}
it.Value() = sfs.Face();
}
for (TopTools_ListIteratorOfListOfShape it(face_list); it.More(); it.Next()) {
TopoDS_Face& occ_face = TopoDS::Face(it.Value());
bool all_reversed = true;
TopoDS_Iterator jt(occ_face, false);
for (; jt.More(); jt.Next()) {
const TopoDS_Wire& w = TopoDS::Wire(jt.Value());
if (!wire_senses.IsBound(w.Oriented(TopAbs_FORWARD)) || (w.Orientation() == wire_senses.Find(w.Oriented(TopAbs_FORWARD)))) {
all_reversed = false;
}
}
if (all_reversed) {
occ_face.Reverse();
}
}
}
if (face_list.Extent() > 1) {
TopoDS_Compound compound;
BRep_Builder builder;
builder.MakeCompound(compound);
for (TopTools_ListIteratorOfListOfShape it(face_list); it.More(); it.Next()) {
TopoDS_Face& occ_face = TopoDS::Face(it.Value());
builder.Add(compound, occ_face);
}
result = compound;
} else {
result = face_list.First();
}
return true;
}
@@ -0,0 +1,22 @@
#include "face_definition.h"
#include <TopoDS.hxx>
#include <Geom_Line.hxx>
#include <BRep_Tool.hxx>
#include <TopoDS_Iterator.hxx>
/* Returns whether wire conforms to a polyhedron, i.e. only edges with linear curves*/
bool IfcGeom::util::is_polyhedron(const TopoDS_Wire & wire) {
double a, b;
TopLoc_Location l;
TopoDS_Iterator it(wire, false, false);
for (; it.More(); it.Next()) {
auto crv = BRep_Tool::Curve(TopoDS::Edge(it.Value()), l, a, b);
if (!crv || crv->DynamicType() != STANDARD_TYPE(Geom_Line)) {
return false;
}
}
return true;
}
@@ -1,4 +1,4 @@
/********************************************************************************
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* *
@@ -17,19 +17,62 @@
* *
********************************************************************************/
#include "IfcGeomMaterial.h"
#ifndef FACE_DEFINITION_H
#define FACE_DEFINITION_H
static double black[3] = {0.,0.,0.};
#include <TopoDS_Wire.hxx>
#include <Geom_Surface.hxx>
IfcGeom::Material::Material(const IfcGeom::SurfaceStyle* style) : style(style) {}
bool IfcGeom::Material::hasDiffuse() const { return style->Diffuse() ? true : false; }
bool IfcGeom::Material::hasSpecular() const { return style->Specular() ? true : false; }
bool IfcGeom::Material::hasTransparency() const { return style->Transparency() ? true : false; }
bool IfcGeom::Material::hasSpecularity() const { return style->Specularity() ? true : false; }
const double* IfcGeom::Material::diffuse() const { if (hasDiffuse()) return &((*style->Diffuse()).R()); else return black; }
const double* IfcGeom::Material::specular() const { if (hasSpecular()) return &((*style->Specular()).R()); else return black; }
double IfcGeom::Material::transparency() const { if (hasTransparency()) return *style->Transparency(); else return 0; }
double IfcGeom::Material::specularity() const { if (hasSpecularity()) return *style->Specularity(); else return 0; }
const std::string &IfcGeom::Material::name() const { return style->Name(); }
const std::string &IfcGeom::Material::original_name() const { return style->original_name(); }
bool IfcGeom::Material::operator==(const IfcGeom::Material& other) const { return style == other.style; }
#include <map>
#include <vector>
namespace IfcGeom {
namespace util {
/* Returns whether wire conforms to a polyhedron, i.e. only edges with linear curves*/
bool is_polyhedron(const TopoDS_Wire& wire);
/* A temporary structure to store the intermediate data for the face conversion */
class face_definition {
private:
Handle(Geom_Surface) surface_;
std::vector<TopoDS_Wire> wires_;
bool all_outer_;
public:
face_definition() : surface_(), all_outer_(false) {}
typedef std::vector<TopoDS_Wire>::const_iterator wire_it;
bool& all_outer() {
return all_outer_;
}
bool all_outer() const {
return all_outer_;
}
Handle(Geom_Surface)& surface() {
return surface_;
}
const Handle(Geom_Surface)& surface() const {
return surface_;
}
std::vector<TopoDS_Wire>& wires() {
return wires_;
}
const TopoDS_Wire& outer_wire() const {
return wires_.front();
}
std::pair<wire_it, wire_it> inner_wires() const {
return { wires_.begin() + 1, wires_.end() };
}
};
}
}
#endif
@@ -0,0 +1,280 @@
#include "OpenCascadeKernel.h"
#include "IfcGeomTree.h"
#include "wire_utils.h"
using namespace ifcopenshell::geometry;
using namespace ifcopenshell::geometry::kernels;
namespace {
void find_neighbours(ifcopenshell::geometry::impl::tree<int>& tree, std::vector<std::unique_ptr<gp_Pnt>>& pnts, std::set<int>& visited, int p, double eps) {
visited.insert(p);
Bnd_Box b;
b.Set(*pnts[p].get());
b.Enlarge(eps);
std::vector<int> js = tree.select_box(b, false);
for (int j : js) {
visited.insert(j);
#ifdef FACESET_HELPER_RECURSIVE
if (visited.find(j) == visited.end()) {
// @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.
find_neighbours(tree, pnts, visited, j, eps);
}
#endif
}
}
}
OpenCascadeKernel::faceset_helper::faceset_helper(OpenCascadeKernel* kernel, const taxonomy::shell* shell)
: kernel_(kernel)
, non_manifold_(false)
{
// @todo use pointers?
std::vector<taxonomy::point3> points;
std::vector<taxonomy::loop*> loops;
for (auto& f : shell->children_as<taxonomy::face>()) {
for (auto& l : f->children_as<taxonomy::loop>()) {
loops.push_back(l);
for (auto& e : l->children_as<taxonomy::edge>()) {
// @todo make sure only cartesian points are provided here
points.push_back(boost::get<taxonomy::point3>(e->start));
}
}
}
std::vector<std::unique_ptr<gp_Pnt>> pnts(points.size());
std::vector<TopoDS_Vertex> vertices(pnts.size());
// @todo
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(convert_xyz<gp_Pnt>(points[i]));
pnts[i].reset(p);
B.MakeVertex(vertices[i], *p, Precision::Confusion());
tree.add(i, vertices[i]);
box.Add(*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->settings_.getValue(ConversionSettings::GV_PRECISION) * 10. && d < bdiff) {
bdiff = d;
}
}
eps_ = kernel->settings_.getValue(ConversionSettings::GV_PRECISION) * 10. * (std::min)(1.0, bdiff);
// @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.
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) {
auto& pt = points[v];
// NB: insert() ignores duplicate keys
vertex_mapping_.insert({ pt.instance->data().id() , i });
}
}
}
std::set<std::tuple<double, double, double>> unique;
for (int pnt_i = 0; pnt_i < (int)pnts.size(); ++pnt_i) {
if (pnts[pnt_i]) {
unique.insert(std::make_tuple(
(*pnts[pnt_i]).X(),
(*pnts[pnt_i]).Y(),
(*pnts[pnt_i]).Z()
));
}
}
if (unique.size() != vertex_mapping_.size()) {
Logger::Notice("Collapsed vertices from " + std::to_string(pnts.size()) + " (" + std::to_string(unique.size()) + " unique) to " + std::to_string(vertex_mapping_.size()));
}
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) {
std::vector<std::pair<int, int> > segments;
edge_set_t segment_set;
loop_(loop, [&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++;
// @todo does this work with tesselated face sets, will they have an associated instance? Guess not.
duplicates_.insert(loop->instance->data().id());
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 (duplicates_.size() || loops_removed || (non_manifold && shell->closed.get_value_or(false))) {
Logger::Warning(boost::lexical_cast<std::string>(duplicate_faces) + " duplicate faces removed, " + boost::lexical_cast<std::string>(loops_removed) + " degenerate loops eliminated and " + boost::lexical_cast<std::string>(non_manifold) + " non-manifold edges");
}
}
void OpenCascadeKernel::faceset_helper::loop_(const taxonomy::loop* ps, const std::function<void(int, int, bool)>& callback) {
if (ps->children.size() < 3) {
return;
}
auto a = boost::get<taxonomy::point3>(((taxonomy::edge*) ps->children.back())->start).instance;
auto A = a->data().id();
for (auto& b : ps->children) {
auto B = boost::get<taxonomy::point3>(((taxonomy::edge*) b)->start).instance->data().id();
auto C = vertex_mapping_[A], D = vertex_mapping_[B];
bool fwd = C < D;
if (!fwd) {
std::swap(C, D);
}
if (C != D) {
callback(C, D, fwd);
A = B;
}
}
}
bool OpenCascadeKernel::faceset_helper::edge(int A, int B, TopoDS_Edge& e) {
auto it = edges_.find({ A, B });
if (it == edges_.end()) {
return false;
}
e = it->second;
return true;
}
bool OpenCascadeKernel::faceset_helper::wire(const taxonomy::loop* loop, TopoDS_Wire& w) {
TopTools_ListOfShape ws;
if (!wires(loop, ws)) {
return false;
}
util::select_largest(ws, w);
return true;
}
bool OpenCascadeKernel::faceset_helper::wires(const taxonomy::loop* loop, TopTools_ListOfShape& wires) {
if (duplicates_.find(loop->instance->data().id()) != duplicates_.end()) {
return false;
}
TopoDS_Wire wire;
BRep_Builder builder;
builder.MakeWire(wire);
int count = 0;
loop_(loop, [this, &builder, &wire, &count](int A, int B, bool fwd) {
TopoDS_Edge e;
if (edge(A, B, e)) {
if (!fwd) {
e.Reverse();
}
builder.Add(wire, e);
count += 1;
}
});
if (count >= 3) {
wire.Closed(true);
TopTools_ListOfShape results;
/* todo kernel_->getValue(GV_NO_WIRE_INTERSECTION_CHECK) < 0. && */
/* todo kernel_->get_wire_intersection_tolerance(wire) */
if (util::wire_intersections(wire, results, kernel_->settings_.getValue(ConversionSettings::GV_PRECISION), kernel_->settings_.getValue(ConversionSettings::GV_PRECISION))) {
Logger::Warning("Self-intersections with " + boost::lexical_cast<std::string>(results.Extent()) + " cycles detected");
non_manifold_ = true;
wires = results;
} else {
wires.Append(wire);
}
return true;
} else {
return false;
}
}
OpenCascadeKernel::faceset_helper::~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?
kernel_->faceset_helper_ = nullptr;
}
+186
View File
@@ -0,0 +1,186 @@
#include "OpenCascadeKernel.h"
#include "wire_builder.h"
#include <Geom_Line.hxx>
#include <Geom_Circle.hxx>
#include <Geom_Ellipse.hxx>
#include <BRepAdaptor_CompCurve.hxx>
#include <BRepAdaptor_HCompCurve.hxx>
#include <Approx_Curve3d.hxx>
#include <ShapeFix_ShapeTolerance.hxx>
#include <BRepBuilderAPI_MakeWire.hxx>
using namespace ifcopenshell::geometry;
using namespace ifcopenshell::geometry::kernels;
using namespace IfcGeom::util;
namespace {
typedef boost::variant<Handle(Geom_Curve), TopoDS_Wire> curve_creation_visitor_result_type;
curve_creation_visitor_result_type convert_curve(OpenCascadeKernel* kernel, const taxonomy::item* curve);
struct curve_creation_visitor {
OpenCascadeKernel* kernel;
curve_creation_visitor_result_type result;
curve_creation_visitor_result_type operator()(const taxonomy::bspline_curve&) {
throw std::runtime_error("Not implemented");
}
curve_creation_visitor_result_type operator()(const taxonomy::line& l) {
const auto& m = l.matrix.ccomponents();
return result = Handle(Geom_Curve)(new Geom_Line(convert_xyz2<gp_Pnt>(m.col(3)), convert_xyz2<gp_Dir>(m.col(0))));
}
curve_creation_visitor_result_type operator()(const taxonomy::circle& c) {
const auto& m = c.matrix.ccomponents();
return result = Handle(Geom_Curve)(new Geom_Circle(gp_Ax2(convert_xyz2<gp_Pnt>(m.col(3)), convert_xyz2<gp_Dir>(m.col(2)), convert_xyz2<gp_Dir>(m.col(0))), c.radius));
}
curve_creation_visitor_result_type operator()(const taxonomy::ellipse& e) {
const auto& m = e.matrix.ccomponents();
return result = Handle(Geom_Curve)(new Geom_Ellipse(gp_Ax2(convert_xyz2<gp_Pnt>(m.col(3)), convert_xyz2<gp_Dir>(m.col(2)), convert_xyz2<gp_Dir>(m.col(0))), e.radius, e.radius2));
}
curve_creation_visitor_result_type operator()(const taxonomy::loop& l) {
TopoDS_Wire wire;
kernel->convert(&l, wire);
return result = wire;
}
curve_creation_visitor_result_type operator()(const taxonomy::edge& e) {
// @todo for polyloops/-lines we should probably construct edges based on correct oriented TopoDS_Vertex instead.
if (e.start.which() != e.end.which()) {
throw std::runtime_error("Different trim types not supported");
}
TopoDS_Edge E;
if (e.basis) {
auto crv_or_wire = convert_curve(kernel, e.basis);
Handle(Geom_Curve) curve;
if (crv_or_wire.which() == 0) {
curve = boost::get<Handle(Geom_Curve)>(crv_or_wire);
} else {
// @todo
const double precision_ = 1.e-5;
Logger::Warning("Approximating BasisCurve due to possible discontinuities", e.instance);
BRepAdaptor_CompCurve cc(boost::get<TopoDS_Wire>(crv_or_wire), true);
Handle(Adaptor3d_HCurve) hcc = Handle(Adaptor3d_HCurve)(new BRepAdaptor_HCompCurve(cc));
// @todo, arbitrary numbers here, note they cannot be too high as contiguous memory is allocated based on them.
Approx_Curve3d approx(hcc, precision_, GeomAbs_C0, 10, 10);
curve = approx.Curve();
}
const bool reversed = !((taxonomy::geom_item*)e.basis)->orientation.get_value_or(true);
const bool is_conic = e.basis->kind() == taxonomy::ELLIPSE || e.basis->kind() == taxonomy::CIRCLE;
// @todo, copy over logic from previous IfcTrimmedCurve handling
if (e.start.which() == 0) {
auto p1 = convert_xyz<gp_Pnt>(boost::get<taxonomy::point3>(e.start));
auto p2 = convert_xyz<gp_Pnt>(boost::get<taxonomy::point3>(e.end));
if (reversed) {
std::swap(p1, p2);
}
E = BRepBuilderAPI_MakeEdge(curve, p1, p2).Edge();
} else {
auto v1 = boost::get<double>(e.start);
auto v2 = boost::get<double>(e.end);
if (reversed) {
std::swap(v1, v2);
}
if (is_conic && ALMOST_THE_SAME(fmod(v2 - v1, M_PI*2.), 0.)) {
E = BRepBuilderAPI_MakeEdge(curve).Edge();
} else {
E = BRepBuilderAPI_MakeEdge(curve, v1, v2).Edge();
}
}
if (reversed) {
E.Reverse();
}
} else {
if (e.start.which() != 0) {
throw std::runtime_error("Non-cartesian trim on edge without curve");
}
auto p1 = convert_xyz<gp_Pnt>(boost::get<taxonomy::point3>(e.start));
auto p2 = convert_xyz<gp_Pnt>(boost::get<taxonomy::point3>(e.end));
E = BRepBuilderAPI_MakeEdge(p1, p2).Edge();
}
BRep_Builder B;
TopoDS_Wire W;
B.MakeWire(W);
B.Add(W, E);
return result = W;
}
};
curve_creation_visitor_result_type convert_curve(OpenCascadeKernel* kernel, const taxonomy::item* curve) {
curve_creation_visitor v{ kernel };
if (dispatch_curve_creation<curve_creation_visitor, 0>::dispatch(curve, v)) {
return v.result;
} else {
throw std::runtime_error("No curve created");
}
}
}
bool OpenCascadeKernel::convert(const taxonomy::loop* loop, TopoDS_Wire& wire) {
auto segments = loop->children_as<taxonomy::edge>();
TopTools_ListOfShape converted_segments;
for (auto& segment : segments) {
auto segment_wire = boost::get<TopoDS_Wire>(convert_curve(this, segment));
#ifdef IFOPSH_DEBUG
std::ostringstream o;
segment->print(o);
TopoDS_Vertex v0, v1;
TopExp::Vertices(segment_wire, v0, v1);
gp_Pnt p0 = BRep_Tool::Pnt(v0);
gp_Pnt p1 = BRep_Tool::Pnt(v1);
o << "p0 " << p0.X() << " " << p0.Y() << " " << p0.Z() << std::endl;
o << "p1 " << p1.X() << " " << p1.Y() << " " << p1.Z() << std::endl;
auto o_str = o.str();
std::wcout << o_str.c_str() << std::endl;
#endif
if (!segment->orientation_2.get_value_or(true)) {
segment_wire.Reverse();
}
ShapeFix_ShapeTolerance FTol;
FTol.SetTolerance(segment_wire, precision_, TopAbs_WIRE);
converted_segments.Append(segment_wire);
}
if (converted_segments.Extent() == 0) {
Logger::Message(Logger::LOG_ERROR, "No segment succesfully converted:", loop->instance);
return false;
}
BRepBuilderAPI_MakeWire w;
TopoDS_Vertex wire_first_vertex, wire_last_vertex, edge_first_vertex, edge_last_vertex;
TopTools_ListIteratorOfListOfShape it(converted_segments);
/*
@todo
IfcEntityList::ptr profile = l->data().getInverse(&IfcSchema::IfcProfileDef::Class(), -1);
const bool force_close = profile && profile->size() > 0;
*/
const bool force_close = false;
wire_builder bld(precision_, loop->instance);
shape_pair_enumerate(it, bld, force_close);
wire = bld.wire();
return true;
}
@@ -0,0 +1,44 @@
#include "OpenCascadeKernel.h"
using namespace ifcopenshell::geometry;
using namespace ifcopenshell::geometry::kernels;
bool OpenCascadeKernel::convert(const taxonomy::matrix4* matrix, gp_GTrsf& trsf) {
// @todo check
const auto& m = matrix->ccomponents();
gp_Mat mat(
m(0, 0), m(0, 1), m(0, 2),
m(1, 0), m(1, 1), m(1, 2),
m(2, 0), m(2, 1), m(2, 2)
);
if (matrix->instance && matrix->instance->declaration().name() == "IfcCartesianTransformationOperator3DnonUniform") {
std::wcout << "non uniform" << std::endl;
}
// @nb SetVectorialPart() sets gp_GTrsf.scale to 0.0, causing an non-invertable
// matrix later on which cannot be in TopLoc_Location.
std::array<double, 3> ms{ {
mat.Column(1).Modulus(),
mat.Column(2).Modulus(),
mat.Column(3).Modulus()
} };
std::sort(ms.begin(), ms.end());
if (std::fabs(ms.front() - ms.back()) < 1.e-7) {
gp_Trsf tr;
tr.SetValues(
m(0, 0), m(0, 1), m(0, 2), m(0, 3),
m(1, 0), m(1, 1), m(1, 2), m(1, 3),
m(2, 0), m(2, 1), m(2, 2), m(2, 3)
);
trsf = tr;
} else {
trsf.SetVectorialPart(mat);
trsf.SetTranslationPart(gp_XYZ(m(0, 3), m(1, 3), m(2, 3)));
trsf.SetForm();
}
return true;
}
+99
View File
@@ -0,0 +1,99 @@
#include "OpenCascadeKernel.h"
using namespace ifcopenshell::geometry;
using namespace ifcopenshell::geometry::kernels;
bool OpenCascadeKernel::convert(const taxonomy::shell* l, TopoDS_Shape& shape) {
std::unique_ptr<faceset_helper> helper_scope;
helper_scope.reset(new faceset_helper(this, l));
faceset_helper_ = helper_scope.get();
auto faces = l->children_as<taxonomy::face>();
double minimal_face_area = precision_ * precision_ * 0.5;
double min_face_area = faceset_helper_
? (faceset_helper_->epsilon() * faceset_helper_->epsilon() / 20.)
: minimal_face_area;
TopTools_ListOfShape face_list;
for (auto& face : faces) {
bool success = false;
TopoDS_Face occ_face;
try {
success = convert(face, occ_face);
} catch (const std::exception& e) {
Logger::Error(e);
} catch (const Standard_Failure& e) {
if (e.GetMessageString() && strlen(e.GetMessageString())) {
Logger::Error(e.GetMessageString());
} else {
Logger::Error("Unknown error creating face");
}
} catch (...) {
Logger::Error("Unknown error creating face");
}
if (!success) {
Logger::Message(Logger::LOG_WARNING, "Failed to convert face:", face->instance);
continue;
}
if (occ_face.ShapeType() == TopAbs_COMPOUND) {
TopoDS_Iterator face_it(occ_face, false);
for (; face_it.More(); face_it.Next()) {
if (face_it.Value().ShapeType() == TopAbs_FACE) {
// This should really be the case. This is not asserted.
const TopoDS_Face& triangle = TopoDS::Face(face_it.Value());
if (face_area(triangle) > min_face_area) {
face_list.Append(triangle);
} else {
Logger::Message(Logger::LOG_WARNING, "Degenerate face:", face->instance);
}
}
}
} else {
if (face_area(occ_face) > min_face_area) {
face_list.Append(occ_face);
} else {
Logger::Message(Logger::LOG_WARNING, "Degenerate face:", face->instance);
}
}
}
if (face_list.Extent() == 0) {
return false;
}
// @todo
/* face_list.Extent() > getValue(GV_MAX_FACES_TO_ORIENT) || */
if (!create_solid_from_faces(face_list, shape)) {
TopoDS_Compound compound;
BRep_Builder builder;
builder.MakeCompound(compound);
TopTools_ListIteratorOfListOfShape face_iterator;
for (face_iterator.Initialize(face_list); face_iterator.More(); face_iterator.Next()) {
builder.Add(compound, face_iterator.Value());
}
shape = compound;
}
return true;
}
bool OpenCascadeKernel::convert_impl(const taxonomy::shell *shell, ifcopenshell::geometry::ConversionResults& results) {
TopoDS_Shape shape;
if (!convert(shell, shape)) {
return false;
}
results.emplace_back(ConversionResult(
shell->instance->data().id(),
shell->matrix,
new OpenCascadeShape(shape),
shell->surface_style
));
return true;
}
@@ -0,0 +1,356 @@
#include "sweep_utils.h"
#include "../ifcparse/IfcLogger.h"
#include "../ifcgeom_schema_agnostic/Kernel.h"
#include <gp_Ax2.hxx>
#include <Geom_Line.hxx>
#include <Geom_Circle.hxx>
#include <TopExp.hxx>
#include <TopExp_Explorer.hxx>
#include <TopoDS.hxx>
#include <TopoDS_Vertex.hxx>
#include <TopoDS_Compound.hxx>
#include <BRep_Tool.hxx>
#include <BRep_Builder.hxx>
#include <BRepPrimAPI_MakePrism.hxx>
#include <BRepPrimAPI_MakeRevol.hxx>
#include <BRepBuilderAPI_MakeEdge.hxx>
#include <BRepBuilderAPI_MakeWire.hxx>
#include <BRepBuilderAPI_MakeFace.hxx>
#include <BRepOffsetAPI_MakePipeShell.hxx>
bool IfcGeom::util::wire_is_c1_continuous(const TopoDS_Wire & w, double tol) {
// NB Note that c0 continuity is NOT checked!
TopTools_IndexedDataMapOfShapeListOfShape map;
TopExp::MapShapesAndAncestors(w, TopAbs_VERTEX, TopAbs_EDGE, map);
for (int i = 1; i <= map.Extent(); ++i) {
const auto& li = map.FindFromIndex(i);
if (li.Extent() == 2) {
const TopoDS_Vertex& v = TopoDS::Vertex(map.FindKey(i));
const TopoDS_Edge& e0 = TopoDS::Edge(li.First());
const TopoDS_Edge& e1 = TopoDS::Edge(li.Last());
double u0 = BRep_Tool::Parameter(v, e0);
double u1 = BRep_Tool::Parameter(v, e1);
double _, __;
Handle(Geom_Curve) c0 = BRep_Tool::Curve(e0, _, __);
Handle(Geom_Curve) c1 = BRep_Tool::Curve(e1, _, __);
gp_Pnt p;
gp_Vec v0, v1;
c0->D1(u0, p, v0);
c1->D1(u1, p, v1);
if (1. - std::abs(v0.Normalized().Dot(v1.Normalized())) > tol) {
return false;
}
}
}
return true;
}
bool IfcGeom::util::wire_to_ax(const TopoDS_Wire & wire, gp_Ax2 & directrix) {
gp_Pnt directrix_origin;
gp_Vec directrix_tangent;
TopoDS_Edge edge;
// Find first edge
TopoDS_Vertex v0, v1;
TopExp::Vertices(wire, v0, v1);
TopTools_IndexedDataMapOfShapeListOfShape map;
TopExp::MapShapesAndAncestors(wire, TopAbs_VERTEX, TopAbs_EDGE, map);
if (v0.IsSame(v1) && map.Contains(v0) && map.FindFromKey(v0).Extent() == 2) {
// Closed wire, with more than 1 edges
auto es = map.FindFromKey(v0);
auto e1 = TopoDS::Edge(es.First());
auto e2 = TopoDS::Edge(es.Last());
double u0, u1;
gp_Vec accum;
Handle(Geom_Curve) crv = BRep_Tool::Curve(e1, u0, u1);
crv->D1(TopExp::FirstVertex(e1).IsSame(v0) ? u0 : u1, directrix_origin, directrix_tangent);
accum += directrix_tangent;
crv = BRep_Tool::Curve(e2, u0, u1);
crv->D1(TopExp::FirstVertex(e2).IsSame(v0) ? u0 : u1, directrix_origin, directrix_tangent);
accum += directrix_tangent;
directrix_tangent = accum;
} else if (map.Contains(v0) && map.FindFromKey(v0).Extent() == 1) {
edge = TopoDS::Edge(map.FindFromKey(v0).First());
double u0, u1;
Handle(Geom_Curve) crv = BRep_Tool::Curve(edge, u0, u1);
crv->D1(u0, directrix_origin, directrix_tangent);
} else {
Logger::Error("Unable to locate first edge");
return false;
}
directrix = gp_Ax2(directrix_origin, directrix_tangent);
return true;
}
bool IfcGeom::util::is_single_linear_edge(const TopoDS_Wire & wire) {
TopExp_Explorer exp(wire, TopAbs_EDGE);
if (!exp.More()) {
return false;
}
TopoDS_Edge e = TopoDS::Edge(exp.Current());
exp.Next();
if (exp.More()) {
return false;
}
double u, v;
Handle_Geom_Curve crv = BRep_Tool::Curve(e, u, v);
return crv->DynamicType() == STANDARD_TYPE(Geom_Line);
}
bool IfcGeom::util::is_single_circular_edge(const TopoDS_Wire & wire) {
TopExp_Explorer exp(wire, TopAbs_EDGE);
if (!exp.More()) {
return false;
}
TopoDS_Edge e = TopoDS::Edge(exp.Current());
exp.Next();
if (exp.More()) {
return false;
}
double u, v;
Handle_Geom_Curve crv = BRep_Tool::Curve(e, u, v);
return crv->DynamicType() == STANDARD_TYPE(Geom_Circle);
}
void IfcGeom::util::process_sweep_as_extrusion(const TopoDS_Wire & wire, const TopoDS_Wire & section, TopoDS_Shape & result) {
TopExp_Explorer exp(wire, TopAbs_EDGE);
TopoDS_Edge e = TopoDS::Edge(exp.Current());
double u, v;
Handle_Geom_Curve crv = BRep_Tool::Curve(e, u, v);
const auto& dir = Handle(Geom_Line)::DownCast(crv)->Position().Direction();
// OCCT line is normalized so diff in parametric coords equals length
const double depth = std::abs(u - v);
// @todo we could be extruding the wire only when we know this is an intermediate edge.
TopoDS_Face face = BRepBuilderAPI_MakeFace(section).Face();
result = BRepPrimAPI_MakePrism(face, depth*dir).Shape();
}
void IfcGeom::util::process_sweep_as_revolution(const TopoDS_Wire & wire, const TopoDS_Wire & section, TopoDS_Shape & result) {
TopExp_Explorer exp(wire, TopAbs_EDGE);
TopoDS_Edge e = TopoDS::Edge(exp.Current());
double u, v;
Handle_Geom_Curve crv = BRep_Tool::Curve(e, u, v);
auto circ = Handle(Geom_Circle)::DownCast(crv);
// @todo we could be extruding the wire only when we know this is an intermediate edge.
const double depth = std::abs(u - v);
TopoDS_Face face = BRepBuilderAPI_MakeFace(section).Face();
result = BRepPrimAPI_MakeRevol(face, circ->Axis(), depth).Shape();
}
void IfcGeom::util::process_sweep_as_pipe(const TopoDS_Wire & wire, const TopoDS_Wire & section, TopoDS_Shape & result, bool force_transformed) {
// This tolerance is fairly high due to the linear edge substitution for small (or large radii) conical curves.
const bool is_continuous = wire_is_c1_continuous(wire, 1.e-2);
BRepOffsetAPI_MakePipeShell builder(wire);
builder.Add(section);
builder.SetTransitionMode(is_continuous || force_transformed ? BRepBuilderAPI_Transformed : BRepBuilderAPI_RightCorner);
try {
builder.Build();
} catch (Standard_Failure& e) {
// We fallback to BRepBuilderAPI_Transformed, but likely with visual artefacts.
if (!(is_continuous || force_transformed)) {
return process_sweep_as_pipe(wire, section, result, true);
} else {
throw e;
}
}
builder.MakeSolid();
result = builder.Shape();
}
void IfcGeom::util::sort_edges(const TopoDS_Wire & wire, std::vector<TopoDS_Edge>& sorted_edges) {
TopTools_IndexedDataMapOfShapeListOfShape map;
TopExp::MapShapesAndAncestors(wire, TopAbs_VERTEX, TopAbs_EDGE, map);
for (int i = 1; i <= map.Extent(); ++i) {
if (map.FindFromIndex(i).Extent() > 2) {
Logger::Warning("Self-intersecting Directrix");
}
}
std::set<TopoDS_TShape*> seen;
auto num_edges = IfcGeom::Kernel::count(wire, TopAbs_EDGE);
TopoDS_Vertex v0, v1;
// @todo this creates the ancestor map twice
TopExp::Vertices(wire, v0, v1);
bool ignore_first_equality_because_closed = v0.IsSame(v1);
// @todo this probably still does not work on a closed wire consisting of one (circular) edge.
while ((int)sorted_edges.size() < num_edges &&
(!v0.IsSame(v1) || ignore_first_equality_because_closed)) {
ignore_first_equality_because_closed = false;
if (!map.Contains(v0)) {
throw std::runtime_error("Disconnected vertex");
}
const TopTools_ListOfShape& es = map.FindFromKey(v0);
TopoDS_Vertex ve0, ve1;
TopTools_ListIteratorOfListOfShape it(es);
bool added = false;
for (; it.More(); it.Next()) {
const TopoDS_Edge& e = TopoDS::Edge(it.Value());
TopExp::Vertices(e, ve0, ve1, true);
if (ve0.IsSame(v0) && seen.find(&*e.TShape()) == seen.end()) {
sorted_edges.push_back(e);
v0 = ve1;
added = true;
seen.insert(&*e.TShape());
break;
}
}
if (!added) {
throw std::runtime_error("Disconnected edge");
}
}
}
// #939: a closed loop causes failed triangulation in 7.3 and artefacts
// in 7.4 so we break up a closed wire into two equal parts.
void IfcGeom::util::break_closed(const TopoDS_Wire & wire, std::vector<TopoDS_Wire>& wires) {
std::vector<TopoDS_Edge> sorted_edges;
sort_edges(wire, sorted_edges);
if (sorted_edges.size() == 1) {
wires.push_back(wire);
return;
}
BRep_Builder B;
wires.emplace_back();
B.MakeWire(wires.back());
for (size_t i = 0; i < sorted_edges.size(); ++i) {
if (i == sorted_edges.size() / 2) {
wires.emplace_back();
B.MakeWire(wires.back());
}
const auto& e = sorted_edges[i];
B.Add(wires.back(), e);
}
}
void IfcGeom::util::segment_adjacent_non_linear(const TopoDS_Wire & wire, std::vector<TopoDS_Wire>& wires) {
std::vector<TopoDS_Edge> sorted_edges;
sort_edges(wire, sorted_edges);
BRep_Builder B;
double u, v;
wires.emplace_back();
B.MakeWire(wires.back());
for (int i = 0; i < (int)sorted_edges.size() - 1; ++i) {
const auto& e = sorted_edges[i];
Handle_Geom_Curve crv = BRep_Tool::Curve(e, u, v);
const bool is_linear = crv->DynamicType() == STANDARD_TYPE(Geom_Line);
const auto& f = sorted_edges[i + 1];
crv = BRep_Tool::Curve(f, u, v);
const bool next_is_linear = crv->DynamicType() == STANDARD_TYPE(Geom_Line);
B.Add(wires.back(), e);
if (!is_linear && !next_is_linear) {
wires.emplace_back();
B.MakeWire(wires.back());
}
}
if (!sorted_edges.empty()) {
B.Add(wires.back(), sorted_edges.back());
}
}
// @todo make this generic for other sweeps not just swept disk
void IfcGeom::util::process_sweep(const TopoDS_Wire & wire, double radius, TopoDS_Shape & result) {
std::vector<TopoDS_Wire> wires, wires_tmp;
segment_adjacent_non_linear(wire, wires_tmp);
for (auto& w : wires_tmp) {
break_closed(w, wires);
}
TopoDS_Compound C;
BRep_Builder B;
if (wires.size() > 1) {
B.MakeCompound(C);
}
for (auto& w : wires) {
TopoDS_Shape part;
gp_Ax2 directrix;
if (!wire_to_ax(w, directrix)) {
continue;
}
Handle(Geom_Circle) circle = new Geom_Circle(directrix, radius);
TopoDS_Wire section = BRepBuilderAPI_MakeWire(BRepBuilderAPI_MakeEdge(circle));
if (is_single_circular_edge(w)) {
process_sweep_as_revolution(w, section, part);
} else if (is_single_linear_edge(w)) {
process_sweep_as_extrusion(w, section, part);
} else {
process_sweep_as_pipe(w, section, part);
}
if (wires.size() > 1) {
B.Add(C, part);
} else {
result = part;
}
}
if (wires.size() > 1) {
result = C;
}
/*
// Eliminate Swept Surfaces?
result = ShapeCustom::SweptToElementary(result);
// Eliminate Trimmed Surfaces?
ShapeBuild_ReShape sbrs;
BRep_Builder b;
TopExp_Explorer exp(result, TopAbs_FACE);
for (; exp.More(); exp.Next()) {
const TopoDS_Face& f = TopoDS::Face(exp.Current());
auto S = BRep_Tool::Surface(f);
if (S->IsKind(STANDARD_TYPE(Geom_RectangularTrimmedSurface))) {
auto RTS = Handle(Geom_RectangularTrimmedSurface)::DownCast(S);
auto B = RTS->BasisSurface();
TopoDS_Shape newf = f.EmptyCopied();
// @todo Is it ok to assume no location?
b.MakeFace(TopoDS::Face(newf), B, BRep_Tool::Tolerance(f));
sbrs.Replace(f, newf);
}
}
result = sbrs.Apply(result);
*/
}
@@ -0,0 +1,59 @@
/********************************************************************************
* *
* 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/>. *
* *
********************************************************************************/
#ifndef SWEEP_UTILS_H
#define SWEEP_UTILS_H
#include <TopoDS_Wire.hxx>
#include <TopoDS_Edge.hxx>
#include <vector>
namespace IfcGeom {
namespace util {
bool wire_is_c1_continuous(const TopoDS_Wire& w, double tol);
bool wire_to_ax(const TopoDS_Wire& wire, gp_Ax2& directrix);
bool is_single_linear_edge(const TopoDS_Wire& wire);
bool is_single_circular_edge(const TopoDS_Wire& wire);
void process_sweep_as_extrusion(const TopoDS_Wire& wire, const TopoDS_Wire& section, TopoDS_Shape& result);
void process_sweep_as_revolution(const TopoDS_Wire& wire, const TopoDS_Wire& section, TopoDS_Shape& result);
void process_sweep_as_pipe(const TopoDS_Wire& wire, const TopoDS_Wire& section, TopoDS_Shape& result, bool force_transformed = false);
void sort_edges(const TopoDS_Wire& wire, std::vector<TopoDS_Edge>& sorted_edges);
// #939: a closed loop causes failed triangulation in 7.3 and artefacts
// in 7.4 so we break up a closed wire into two equal parts.
void break_closed(const TopoDS_Wire& wire, std::vector<TopoDS_Wire>& wires);
void segment_adjacent_non_linear(const TopoDS_Wire& wire, std::vector<TopoDS_Wire>& wires);
// @todo make this generic for other sweeps not just swept disk
void process_sweep(const TopoDS_Wire& wire, double radius, TopoDS_Shape& result);
}
}
#endif
@@ -0,0 +1,226 @@
#include "wire_builder.h"
#include "../ifcparse/IfcLogger.h"
#include "../ifcgeom_schema_agnostic/Kernel.h"
#include <TopExp.hxx>
#include <TopoDS.hxx>
#include <BRep_Tool.hxx>
#include <BRep_Builder.hxx>
#include <ShapeBuild_ReShape.hxx>
#include <GC_MakeCircle.hxx>
#include <BRepBuilderAPI_MakeEdge.hxx>
#include <Geom_Line.hxx>
#include <Geom_Circle.hxx>
#include <GeomAdaptor_Curve.hxx>
// Returns the first edge of a wire
TopoDS_Edge IfcGeom::util::first_edge(const TopoDS_Wire & w) {
TopoDS_Vertex v1, v2;
TopExp::Vertices(w, v1, v2);
TopTools_IndexedDataMapOfShapeListOfShape wm;
TopExp::MapShapesAndAncestors(w, TopAbs_VERTEX, TopAbs_EDGE, wm);
return TopoDS::Edge(wm.FindFromKey(v1).First());
}
// Returns new wire with the edge replaced by a linear edge with the vertex v moved to p
TopoDS_Wire IfcGeom::util::adjust(const TopoDS_Wire & w, const TopoDS_Vertex & v, const gp_Pnt & p) {
TopTools_IndexedDataMapOfShapeListOfShape map;
TopExp::MapShapesAndAncestors(w, TopAbs_VERTEX, TopAbs_EDGE, map);
bool all_linear = true, single_circle = false, first = true;
const TopTools_ListOfShape& edges = map.FindFromKey(v);
TopTools_ListIteratorOfListOfShape it(edges);
for (; it.More(); it.Next()) {
const TopoDS_Edge& e = TopoDS::Edge(it.Value());
double _, __;
Handle(Geom_Curve) crv = BRep_Tool::Curve(e, _, __);
const bool is_line = crv->DynamicType() == STANDARD_TYPE(Geom_Line);
const bool is_circle = crv->DynamicType() == STANDARD_TYPE(Geom_Circle);
all_linear = all_linear && is_line;
single_circle = first && is_circle;
}
if (all_linear) {
BRep_Builder b;
TopoDS_Vertex v2;
b.MakeVertex(v2, p, BRep_Tool::Tolerance(v));
ShapeBuild_ReShape reshape;
reshape.Replace(v.Oriented(TopAbs_FORWARD), v2);
return TopoDS::Wire(reshape.Apply(w));
} else if (single_circle) {
TopoDS_Vertex v1, v2;
TopExp::Vertices(w, v1, v2);
gp_Pnt p1, p2, p3;
p1 = v.IsEqual(v1) ? p : BRep_Tool::Pnt(v1);
p3 = v.IsEqual(v2) ? p : BRep_Tool::Pnt(v2);
double a, b;
Handle(Geom_Curve) crv = BRep_Tool::Curve(TopoDS::Edge(edges.First()), a, b);
crv->D0((a + b) / 2., p2);
GC_MakeCircle mc(p1, p2, p3);
if (!mc.IsDone()) {
throw IfcGeom::geometry_exception("Failed to adjust circle");
}
TopoDS_Edge edge = BRepBuilderAPI_MakeEdge(mc.Value(), p1, p3).Edge();
BRepBuilderAPI_MakeWire builder;
builder.Add(edge);
return builder.Wire();
} else {
throw IfcGeom::geometry_exception("Unexpected wire to adjust");
}
}
double IfcGeom::util::deflection_for_approximating_circle(double radius, double param) {
return -radius * std::cos(1. / 2. * param) * std::cos(param) - radius * std::sin(1. / 2. * param) * std::sin(param) + radius;
}
bool IfcGeom::util::create_edge_over_curve_with_log_messages(const Handle_Geom_Curve & crv, const double eps, const gp_Pnt & p1, const gp_Pnt & p2, TopoDS_Edge & result) {
if (crv->IsClosed() && p1.Distance(p2) <= eps) {
BRepBuilderAPI_MakeEdge me(crv);
if (me.IsDone()) {
result = me.Edge();
return true;
} else {
return false;
}
}
BRep_Builder builder;
TopoDS_Vertex v1, v2;
/// @todo project first and emit warnings accordingly
builder.MakeVertex(v1, p1, eps);
builder.MakeVertex(v2, p2, eps);
BRepBuilderAPI_MakeEdge me(crv, v1, v2);
if (!me.IsDone()) {
const double eps2 = eps * eps;
if (me.Error() == BRepBuilderAPI_PointProjectionFailed) {
GeomAdaptor_Curve GAC(crv);
const gp_Pnt* ps[2] = { &p1, &p2 };
for (int i = 0; i < 2; ++i) {
Extrema_ExtPC extrema(*ps[i], GAC);
if (extrema.IsDone()) {
int n = extrema.NbExt();
double dmin = std::numeric_limits<double>::infinity();
for (int j = 1; j <= n; j++) {
const double d = extrema.SquareDistance(j);
if (d < dmin) {
dmin = d;
}
}
if (dmin == std::numeric_limits<double>::infinity()) {
Logger::Error("No extrema for point");
} else if (dmin > eps2) {
Logger::Error("Distance of " + boost::lexical_cast<std::string>(std::sqrt(dmin)) + " exceeds tolerance");
}
} else {
Logger::Error("Failed to calculate extrema for point");
}
}
}
return false;
}
result = me.Edge();
return true;
}
void IfcGeom::util::wire_builder::operator()(const TopoDS_Shape& a) {
const TopoDS_Wire& w = TopoDS::Wire(a);
if (override_next_) {
override_next_ = false;
TopoDS_Edge e = first_edge(w);
mw_.Add(adjust(w, TopExp::FirstVertex(e, true), next_override_));
} else {
mw_.Add(w);
}
}
void IfcGeom::util::wire_builder::operator()(const TopoDS_Shape& a, const TopoDS_Shape& b, bool last) {
TopoDS_Wire w1 = TopoDS::Wire(a);
const TopoDS_Wire& w2 = TopoDS::Wire(b);
if (override_next_) {
override_next_ = false;
TopoDS_Edge e = first_edge(w1);
w1 = adjust(w1, TopExp::FirstVertex(e, true), next_override_);
}
TopoDS_Vertex w11, w12, w21, w22;
TopExp::Vertices(w1, w11, w12);
TopExp::Vertices(w2, w21, w22);
gp_Pnt p1 = BRep_Tool::Pnt(w12);
gp_Pnt p2 = BRep_Tool::Pnt(w21);
double dist = p1.Distance(p2);
// Distance is within tolerance, this is fine
if (dist < p_) {
mw_.Add(w1);
goto check;
}
// Distance is too large for attempting to move end points, add intermediate edge
if (dist > 1000. * p_) {
mw_.Add(w1);
mw_.Add(BRepBuilderAPI_MakeEdge(p1, p2));
Logger::Warning("Added additional segment to close gap with length " + boost::lexical_cast<std::string>(dist) + " to:", inst_);
goto check;
}
{
TopTools_IndexedDataMapOfShapeListOfShape wmap1, wmap2;
// Find edges connected to end- and begin vertex
TopExp::MapShapesAndAncestors(w1, TopAbs_VERTEX, TopAbs_EDGE, wmap1);
TopExp::MapShapesAndAncestors(w2, TopAbs_VERTEX, TopAbs_EDGE, wmap2);
const TopTools_ListOfShape& last_edges = wmap1.FindFromKey(w12);
const TopTools_ListOfShape& first_edges = wmap2.FindFromKey(w21);
double _, __;
if (last_edges.Extent() == 1 && first_edges.Extent() == 1) {
Handle(Geom_Curve) c1 = BRep_Tool::Curve(TopoDS::Edge(last_edges.First()), _, __);
Handle(Geom_Curve) c2 = BRep_Tool::Curve(TopoDS::Edge(first_edges.First()), _, __);
const bool is_line1 = c1->DynamicType() == STANDARD_TYPE(Geom_Line);
const bool is_line2 = c2->DynamicType() == STANDARD_TYPE(Geom_Line);
const bool is_circle1 = c1->DynamicType() == STANDARD_TYPE(Geom_Circle);
const bool is_circle2 = c2->DynamicType() == STANDARD_TYPE(Geom_Circle);
// Preferably adjust the segment that is linear
if (is_line1 || (is_circle1 && !is_line2)) {
mw_.Add(adjust(w1, w12, p2));
Logger::Notice("Adjusted edge end-point with distance " + boost::lexical_cast<std::string>(dist) + " on:", inst_);
} else if ((is_line2 || is_circle2) && !last) {
mw_.Add(w1);
override_next_ = true;
next_override_ = p1;
Logger::Notice("Adjusted edge end-point with distance " + boost::lexical_cast<std::string>(dist) + " on:", inst_);
} else {
// In all other cases an edge is added
mw_.Add(w1);
mw_.Add(BRepBuilderAPI_MakeEdge(p1, p2));
Logger::Warning("Added additional segment to close gap with length " + boost::lexical_cast<std::string>(dist) + " to:", inst_);
}
} else {
Logger::Error("Internal error, inconsistent wire segments", inst_);
mw_.Add(w1);
}
}
check:
if (mw_.Error() == BRepBuilderAPI_NonManifoldWire) {
Logger::Error("Non-manifold curve segments:", inst_);
} else if (mw_.Error() == BRepBuilderAPI_DisconnectedWire) {
Logger::Error("Failed to join curve segments:", inst_);
}
}
@@ -0,0 +1,105 @@
/********************************************************************************
* *
* 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/>. *
* *
********************************************************************************/
#ifndef WIRE_BUILDER_H
#define WIRE_BUILDER_H
#include "../ifcparse/IfcBaseClass.h"
#include <Geom_Curve.hxx>
#include <TopoDS_Vertex.hxx>
#include <TopoDS_Edge.hxx>
#include <TopoDS_Wire.hxx>
#include <Extrema_ExtPC.hxx>
#include <BRepBuilderAPI_MakeWire.hxx>
namespace IfcGeom {
namespace util {
// Returns the first edge of a wire
TopoDS_Edge first_edge(const TopoDS_Wire& w);
// Returns new wire with the edge replaced by a linear edge with the vertex v moved to p
TopoDS_Wire adjust(const TopoDS_Wire& w, const TopoDS_Vertex& v, const gp_Pnt& p);
// A wrapper around BRepBuilderAPI_MakeWire that makes sure segments are connected either by moving end points or by adding intermediate segments
class wire_builder {
private:
BRepBuilderAPI_MakeWire mw_;
double p_;
bool override_next_;
gp_Pnt next_override_;
const IfcUtil::IfcBaseClass* inst_;
public:
wire_builder(double p, const IfcUtil::IfcBaseClass* inst = 0) : p_(p), override_next_(false), inst_(inst) {}
void operator()(const TopoDS_Shape& a);
void operator()(const TopoDS_Shape& a, const TopoDS_Shape& b, bool last);
const TopoDS_Wire& wire() { return mw_.Wire(); }
};
template <typename Fn>
void shape_pair_enumerate(TopTools_ListIteratorOfListOfShape& it, Fn& fn, bool closed) {
bool is_first = true;
TopoDS_Shape first, previous, current;
for (; it.More(); it.Next(), is_first = false) {
current = it.Value();
if (is_first) {
first = current;
} else {
fn(previous, current, false);
}
previous = current;
}
if (closed) {
fn(current, first, true);
} else {
fn(current);
}
}
/*
Below is code to deduce the formula below in SageMath
| R, b = var('R b')
|
| Bxy = R * cos(b), R * sin(b)
| Cxy = R * cos(b/2), R * sin(b/2)
|
| def dot(v, w):
| return v[0] * w[0] + v[1] * w[1]
|
| def norm(v):
| l = sqrt(v[0]^2 + v[1]^2)
| return v[0] / l, v[1] / l
|
| (R - R*dot(norm(Cxy), norm(Bxy))).full_simplify()
*/
double deflection_for_approximating_circle(double radius, double param);
bool create_edge_over_curve_with_log_messages(const Handle_Geom_Curve& crv, const double eps, const gp_Pnt& p1, const gp_Pnt& p2, TopoDS_Edge& result);
}
}
#endif
@@ -0,0 +1,569 @@
#include "wire_utils.h"
#include "../ifcparse/IfcLogger.h"
#include "../ifcgeom_schema_agnostic/Kernel.h"
#include "../ifcgeom_schema_agnostic/IfcGeomTree.h"
#include <TopExp.hxx>
#include <TopoDS.hxx>
#include <TopoDS_Iterator.hxx>
#include <ShapeFix_Wire.hxx>
#include <BRep_Tool.hxx>
#include <BRepTools_WireExplorer.hxx>
#include <BRepBuilderAPI_MakeFace.hxx>
#include <BRepAlgo_NormalProjection.hxx>
#include <BRepMesh_IncrementalMesh.hxx>
#include <BRepBuilderAPI_MakePolygon.hxx>
#include <BRepBuilderAPI_MakeWire.hxx>
#include <BRepBuilderAPI_MakeEdge.hxx>
#include <TopTools_ListOfShape.hxx>
#include <ShapeExtend_WireData.hxx>
#include <Standard_Version.hxx>
#include <GeomAPI_ExtremaCurveCurve.hxx>
#include <boost/range/irange.hpp>
#include <boost/range/algorithm_ext/push_back.hpp>
#include <map>
bool IfcGeom::util::approximate_plane_through_wire(const TopoDS_Wire& wire, gp_Pln& plane, double eps_) {
// 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
const double eps2 = eps_ * eps_;
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()) {
const bool has_more = exp.More() != 0;
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;
}
gp_Vec v(x, y, z);
if (v.SquareMagnitude() < eps_ * eps_) {
Logger::Warning("Degenerate face boundary in normal estimation");
return false;
}
plane = gp_Pln(center / n, v);
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;
}
}
return true;
}
bool IfcGeom::util::flatten_wire(TopoDS_Wire& wire, double eps) {
gp_Pln pln;
if (!approximate_plane_through_wire(wire, pln, eps)) {
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;
}
IfcGeom::util::triangulate_wire_result IfcGeom::util::triangulate_wire(const std::vector<TopoDS_Wire>& wires, TopTools_ListOfShape& faces) {
// 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
// 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.
typedef std::pair<double, double> uv_node;
gp_Pln pln;
if (!approximate_plane_through_wire(wires.front(), pln, std::numeric_limits<double>::infinity())) {
return TRIANGULATE_WIRE_FAIL;
}
const gp_XYZ& udir = pln.Position().XDirection().XYZ();
const gp_XYZ& vdir = pln.Position().YDirection().XYZ();
const gp_XYZ& pnt = pln.Position().Location().XYZ();
std::map<uv_node, TopoDS_Vertex> mapping;
std::map<std::pair<uv_node, uv_node>, TopoDS_Edge> existing_edges, new_edges;
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
BRepMesh_IncrementalMesh(face, Precision::Confusion());
int n123[3];
TopLoc_Location loc;
Handle_Poly_Triangulation tri = BRep_Tool::Triangulation(face, loc);
if (!tri.IsNull()) {
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
BRepBuilderAPI_MakeWire mp2;
for (int j = 0; j < 3; ++j) {
uv_node uvnodes[2];
TopoDS_Vertex vs[2];
for (int k = 0; k < 2; ++k) {
const gp_Pnt& uv = tri->Node(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 TRIANGULATE_WIRE_FAIL;
}
vs[k] = it->second;
}
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())));
}
}
}
BRepBuilderAPI_MakeFace mft(mp2.Wire());
if (mft.IsDone()) {
TopoDS_Face triangle_face = mft.Face();
TopoDS_Iterator jt(triangle_face, false);
for (; jt.More(); jt.Next()) {
const TopoDS_Wire& w = TopoDS::Wire(jt.Value());
if (w.Orientation() != wires.front().Orientation()) {
triangle_face.Reverse();
}
}
faces.Append(triangle_face);
} else {
Logger::Error("Internal error: missing face");
return TRIANGULATE_WIRE_FAIL;
}
}
}
TopTools_IndexedDataMapOfShapeListOfShape mape, mapn;
for (auto& wire : wires) {
TopExp::MapShapesAndAncestors(wire, TopAbs_EDGE, TopAbs_WIRE, mape);
}
TopTools_ListIteratorOfListOfShape it(faces);
for (; it.More(); it.Next()) {
TopExp::MapShapesAndAncestors(it.Value(), TopAbs_EDGE, TopAbs_WIRE, mapn);
}
// Validation
bool non_manifold = false;
for (int i = 1; i <= mape.Extent(); ++i) {
#if OCC_VERSION_HEX >= 0x70000
TopTools_ListOfShape val;
if (!mapn.FindFromKey(mape.FindKey(i), val)) {
#else
bool contains = false;
try {
TopTools_ListOfShape val = mapn.FindFromKey(mape.FindKey(i));
contains = true;
} catch (Standard_NoSuchObject&) {}
if (!contains) {
#endif
// All existing edges need to exist in the new faces
Logger::Error("Internal error, missing edge from triangulation");
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");
non_manifold = true;
}
}
return non_manifold ? TRIANGULATE_WIRE_NON_MANIFOLD : TRIANGULATE_WIRE_OK;
}
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 = (int)n - 1;
}
return *this;
}
bounded_int& operator++() {
++i;
if (i == (int)n) {
i = 0;
}
return *this;
}
operator int() { return i; }
};
}
bool IfcGeom::util::wire_intersections(const TopoDS_Wire& wire, TopTools_ListOfShape& wires, double eps, double eps_real) {
if (!wire.Closed()) {
wires.Append(wire);
return false;
}
int n = IfcGeom::Kernel::count(wire, TopAbs_EDGE);
if (n < 3) {
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);
IfcGeom::impl::tree<int> tree;
int edge_idx = 0;
for (; exp.More(); exp.Next()) {
wd->Add(exp.Current());
if (n > 64) {
// tfk: indices in tree are 0-based vd 1-based in wiredata
tree.add(edge_idx++, exp.Current());
}
}
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");
}
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));
// @todo: should this start from 0 in case of n > 64?
for (int i = 2; i < n; ++i) {
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;
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)
);
// @todo: extend this to work in case of multiple extrema and curved segments.
const bool unbounded_intersects = (!ecc.Extrema().IsParallel() && ecc.NbExtrema() == 1 && ecc.Distance(1) < eps);
if (unbounded_intersects) {
ecc.Parameters(1, U1, U2);
if (u11 > u12) {
std::swap(u11, u12);
}
if (u21 > u22) {
std::swap(u21, u22);
}
/// @todo: tfk: probably need different thresholds on non-linear curves
u11 -= eps;
u12 += eps;
u21 -= eps;
u22 += eps;
// 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)) {
if (u11 < U1 && U1 < u12 && u21 < U2 && U2 < u22) {
intersected = true;
// Explore a forward and backward cycle from the intersection point
for (int fb = 0; fb <= 1; ++fb) {
const bool forward = fb == 0;
BRepBuilderAPI_MakeWire mw;
bool first = true;
for (bounded_int k(j, n);;) {
bool intersecting = k == j || k == i;
if (intersecting) {
TopoDS_Edge e = wd->Edge(k + 1);
TopoDS_Vertex v1, v2;
TopExp::Vertices(e, v1, v2, true);
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) > eps_real * 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));
}
if (k == i) {
break;
}
if (forward) {
++k;
} else {
--k;
}
}
ShapeFix_Wire sfw;
sfw.Load(mw.Wire());
sfw.Perform();
// Recursively process both cuts
// @todo this is a change in behaviour with eps precomputed from the kernel
// instead of adaptively calculated for the successive iterations.
wire_intersections(sfw.Wire(), wires, eps, eps_real);
}
return true;
}
}
}
}
// No intersections found, append original wire
if (!intersected) {
wires.Append(wire);
}
return intersected;
}
void IfcGeom::util::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]);
// @todo hard coded precision.
// @todo this is a really strange measure for wire size. Why not use newell's
// method to project to plane and then calculate size of the 2d bbox?
const double eps = 1.e-5;
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();
}
}
}
@@ -0,0 +1,28 @@
#include <gp_Pln.hxx>
#include <TopoDS_Wire.hxx>
#include <TopTools_ListOfShape.hxx>
#include <vector>
namespace ifcopenshell { namespace geometry {
namespace util {
bool approximate_plane_through_wire(const TopoDS_Wire& wire, gp_Pln& plane, double eps);
bool flatten_wire(TopoDS_Wire& wire, double eps);
enum triangulate_wire_result {
TRIANGULATE_WIRE_FAIL,
TRIANGULATE_WIRE_OK,
TRIANGULATE_WIRE_NON_MANIFOLD,
};
/// Triangulate the set of wires. The firstmost wire is assumed to be the outer wire.
triangulate_wire_result triangulate_wire(const std::vector<TopoDS_Wire>& wires, TopTools_ListOfShape& faces);
// eps: tolerance added to wire intersection checks, can be zero
// eps_real: tolerance used to construct new edge geometry around intersection points, cannot be zero
bool wire_intersections(const TopoDS_Wire& wire, TopTools_ListOfShape& wires, double eps, double eps_real);
void select_largest(const TopTools_ListOfShape& shapes, TopoDS_Shape& largest);
}
} }
+122
View File
@@ -0,0 +1,122 @@
#include "profile_helper.h"
using namespace ifcopenshell::geometry;
taxonomy::loop* ifcopenshell::geometry::polygon_from_points(const std::vector<taxonomy::point3>& ps, bool external) {
auto loop = new taxonomy::loop();
loop->external = external;
boost::optional<taxonomy::point3> previous;
for (auto& p : ps) {
if (previous) {
auto e = new taxonomy::edge;
e->start = *previous;
e->end = p;
loop->children.push_back(e);
}
previous = p;
}
return loop;
}
taxonomy::loop* ifcopenshell::geometry::profile_helper(Eigen::Matrix4d& m4, const std::vector<profile_point>& points) {
/* 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]));
TopoDS_Face face;
convert_wire_to_face(w.Wire(), face);
if (numFillets && *std::max_element(filletRadii, filletRadii + numFillets) > ALMOST_ZERO) {
BRepFilletAPI_MakeFillet2d fillet(face);
for (int i = 0; i < numFillets; i++) {
const double radius = filletRadii[i];
if (radius <= ALMOST_ZERO) continue;
fillet.AddFillet(vertices[filletIndices[i]], radius);
}
fillet.Build();
if (fillet.IsDone()) {
face = TopoDS::Face(fillet.Shape());
} else {
Logger::Error("Failed to process profile fillets");
}
}
*/
const bool has_position = !m4.isIdentity();
// @todo precision
std::vector<taxonomy::point3> ps;
ps.reserve(points.size() + 1);
std::transform(points.begin(), points.end(), std::back_inserter(ps), [&has_position, &m4](const profile_point& p) {
if (has_position) {
Eigen::Vector4d v(p.xy[0], p.xy[1], 0., 1.);
v = m4 * v;
return taxonomy::point3(v(0), v(1), 0.);
} else {
return taxonomy::point3(p.xy[0], p.xy[1], 0.);
}
});
ps.push_back(ps.front());
auto loop = polygon_from_points(ps);
std::vector<profile_point_with_edges> pps(points.size());
for (int b = 0; b < points.size(); ++b) {
int c = (b - 1) % points.size();
pps[b] = { Eigen::Vector2d(points[b].xy[0], points[b].xy[1]), points[b].radius, (taxonomy::edge*) loop->children[c], (taxonomy::edge*) loop->children[b] };
}
size_t i = pps.size();
while (i--) {
const auto& p = pps[i];
if (p.radius && *p.radius > 0.) {
// Position is a IfcAxis2Placement2D, so should remain 2d points
auto p0 = boost::get<taxonomy::point3>(p.previous->start).components_->head<2>();
auto p1a = boost::get<taxonomy::point3>(p.previous->end).components_->head<2>();
auto p2 = boost::get<taxonomy::point3>(p.next->end).components_->head<2>();
auto p1b = boost::get<taxonomy::point3>(p.next->start).components_->head<2>();
auto ba_ = p0 - p1a;
auto bc_ = p2 - p1b;
auto ba = ba_.normalized();
auto bc = bc_.normalized();
const double angle = std::acos(ba.dot(bc));
const double inset = *p.radius / std::tan(angle / 2.);
boost::get<taxonomy::point3>(p.previous->end).components_->head<2>() += ba * inset;
boost::get<taxonomy::point3>(p.next->start).components_->head<2>() += bc * inset;
auto e = new taxonomy::edge;
e->start = p.previous->end;
e->end = p.next->start;
auto ab = Eigen::Vector3d(-ba(1), +ba(0), 0.);
double sign = ab.head<2>().dot(bc) > 0 ? 1. : -1.;
auto O = boost::get<taxonomy::point3>(p.previous->end).ccomponents().head<3>() + ab * *p.radius * sign;
auto c = new taxonomy::circle;
c->matrix.components_ = new Eigen::Matrix4d(Eigen::Affine3d(Eigen::Translation3d(O)).matrix());
c->radius = *p.radius;
e->basis = c;
c->orientation.reset(sign == -1.);
loop->children.insert(std::find(loop->children.begin(), loop->children.end(), p.next), e);
}
};
return loop;
}
+24
View File
@@ -0,0 +1,24 @@
#include "taxonomy.h"
namespace ifcopenshell {
namespace geometry {
struct profile_point {
std::array<double, 2> xy;
boost::optional<double> radius;
};
struct profile_point_with_edges {
Eigen::Vector2d xy;
boost::optional<double> radius;
taxonomy::edge *previous, *next;
};
taxonomy::loop* polygon_from_points(const std::vector<taxonomy::point3>& ps, bool external = true);
taxonomy::loop* profile_helper(Eigen::Matrix4d& m4, const std::vector<profile_point>& points);
}
}
@@ -0,0 +1,26 @@
#include "mapping.h"
#define mapping POSTFIX_SCHEMA(mapping)
using namespace ifcopenshell::geometry;
taxonomy::item* mapping::map_impl(const IfcSchema::IfcArbitraryClosedProfileDef* inst) {
auto loop = map(inst->OuterCurve());
if (loop) {
auto face = new taxonomy::face;
((taxonomy::loop*)loop)->external = true;
face->children = { loop };
if (inst->as<IfcSchema::IfcArbitraryProfileDefWithVoids>()) {
auto with_voids = inst->as<IfcSchema::IfcArbitraryProfileDefWithVoids>();
auto voids = with_voids->InnerCurves();
for (auto& v : *voids) {
auto inner_loop = map(v);
if (inner_loop) {
((taxonomy::loop*)inner_loop)->external = false;
face->children.push_back(inner_loop);
}
}
}
return face;
} else {
return nullptr;
}
}
@@ -0,0 +1,17 @@
#include "mapping.h"
#define mapping POSTFIX_SCHEMA(mapping)
using namespace ifcopenshell::geometry;
taxonomy::item* mapping::map_impl(const IfcSchema::IfcAxis2Placement2D* inst) {
Eigen::Vector3d P, axis(0, 0, 1), V(1, 0, 0);
{
taxonomy::point3 v = as<taxonomy::point3>(map(inst->Location()));
P = *v.components_;
}
const bool hasRef = inst->hasRefDirection();
if (hasRef) {
taxonomy::direction3 v = as<taxonomy::direction3>(map(inst->RefDirection()));
V = *v.components_;
}
return new taxonomy::matrix4(P, axis, V);
}
@@ -0,0 +1,37 @@
#include "mapping.h"
#define mapping POSTFIX_SCHEMA(mapping)
using namespace ifcopenshell::geometry;
taxonomy::item* mapping::map_impl(const IfcSchema::IfcAxis2Placement3D* inst) {
Eigen::Vector3d o, axis(0, 0, 1), refDirection, X(1, 0, 0);
{
taxonomy::point3 v = as<taxonomy::point3>(map(inst->Location()));
o = *v.components_;
}
const bool hasAxis = inst->hasAxis();
const bool hasRef = inst->hasRefDirection();
if (hasAxis != hasRef) {
Logger::Warning("Axis and RefDirection should be specified together", inst);
}
if (hasAxis) {
taxonomy::direction3 v = as<taxonomy::direction3>(map(inst->Axis()));
axis = *v.components_;
}
if (hasRef) {
taxonomy::direction3 v = as<taxonomy::direction3>(map(inst->RefDirection()));
refDirection = *v.components_;
} else {
if (acos(axis.dot(X)) > 1.e-5) {
refDirection = { 1., 0., 0. };
} else {
refDirection = { 0., 0., 1. };
}
auto Xvec = axis.dot(refDirection) * axis;
auto Xaxis = refDirection - Xvec;
refDirection = Xaxis;
}
return new taxonomy::matrix4(o, axis, refDirection);
}
+59
View File
@@ -0,0 +1,59 @@
#include "mapping.h"
#define mapping POSTFIX_SCHEMA(mapping)
using namespace ifcopenshell::geometry;
namespace {
taxonomy::boolean_result::operation_t boolean_op_type(IfcSchema::IfcBooleanOperator::Value op) {
if (op == IfcSchema::IfcBooleanOperator::IfcBooleanOperator_DIFFERENCE) {
return taxonomy::boolean_result::SUBTRACTION;
} else if (op == IfcSchema::IfcBooleanOperator::IfcBooleanOperator_INTERSECTION) {
return taxonomy::boolean_result::INTERSECTION;
} else if (op == IfcSchema::IfcBooleanOperator::IfcBooleanOperator_UNION) {
return taxonomy::boolean_result::UNION;
} else {
throw taxonomy::topology_error("Unknown boolean operation");
}
}
}
taxonomy::item* mapping::map_impl(const IfcSchema::IfcBooleanResult* inst) {
IfcSchema::IfcBooleanOperand* operand1 = inst->FirstOperand();
IfcSchema::IfcBooleanOperand* operand2 = inst->SecondOperand();
std::vector<IfcUtil::IfcBaseClass*> operands = { operand2 };
auto op = boolean_op_type(inst->Operator());
bool process_as_list = true;
while (true) {
auto res1 = operand1->as<IfcSchema::IfcBooleanResult>();
if (res1) {
if (boolean_op_type(res1->Operator()) == op) {
operand1 = res1->FirstOperand();
operands.push_back(res1->SecondOperand());
} else {
process_as_list = false;
break;
}
} else {
operands.push_back(operand1);
break;
}
}
if (process_as_list) {
std::reverse(operands.begin(), operands.end());
} else {
operand1 = inst->FirstOperand();
operands.clear();
operands.push_back(operand1);
operands.push_back(operand2);
}
auto br = map_to_collection<taxonomy::boolean_result>(this, &operands);
if (br) {
br->operation = op;
}
return br;
}
+12
View File
@@ -0,0 +1,12 @@
#include "mapping.h"
#define mapping POSTFIX_SCHEMA(mapping)
using namespace ifcopenshell::geometry;
taxonomy::item* mapping::map_impl(const IfcSchema::IfcCartesianPoint* inst) {
auto coords = inst->Coordinates();
return new taxonomy::point3(
coords.size() >= 1 ? coords[0] * length_unit_ : 0.,
coords.size() >= 2 ? coords[1] * length_unit_ : 0.,
coords.size() >= 3 ? coords[2] * length_unit_ : 0.
);
}
@@ -0,0 +1,42 @@
#include "mapping.h"
#define mapping POSTFIX_SCHEMA(mapping)
using namespace ifcopenshell::geometry;
taxonomy::item* mapping::map_impl(const IfcSchema::IfcCartesianTransformationOperator2D* inst) {
auto m = new taxonomy::matrix4;
Eigen::Vector4d origin, axis1(1.0, 0.0, 0.0, 0.0), axis2(0.0, 1.0, 0.0, 0.0), axis3(0.0, 0.0, 1.0, 0.0);
taxonomy::point3 O = as<taxonomy::point3>(map(inst->LocalOrigin()));
origin << *O.components_, 1.0;
if (inst->hasAxis1()) {
taxonomy::direction3 ax1 = as<taxonomy::direction3>(map(inst->Axis1()));
axis1 << *ax1.components_, 0.0;
}
if (inst->hasAxis2()) {
taxonomy::direction3 ax2 = as<taxonomy::direction3>(map(inst->Axis1()));
axis2 << *ax2.components_, 0.0;
}
double scale1, scale2;
scale1 = scale2 = 1.0;
if (inst->hasScale()) {
scale1 = inst->Scale();
}
if (inst->as<IfcSchema::IfcCartesianTransformationOperator2DnonUniform>()) {
auto nu = inst->as<IfcSchema::IfcCartesianTransformationOperator2DnonUniform>();
scale2 = nu->hasScale2() ? nu->Scale2() : scale1;
}
m->components() <<
axis1 * scale1,
axis2 * scale2,
axis3,
origin;
m->components().transposeInPlace();
return m;
}
@@ -0,0 +1,54 @@
#include "mapping.h"
#define mapping POSTFIX_SCHEMA(mapping)
using namespace ifcopenshell::geometry;
taxonomy::item* mapping::map_impl(const IfcSchema::IfcCartesianTransformationOperator3D* inst) {
auto m = new taxonomy::matrix4;
Eigen::Vector4d origin;
Eigen::Vector4d axis1(1., 0., 0., 0.);
Eigen::Vector4d axis2(0., 1., 0., 0.);
Eigen::Vector4d axis3(0., 0., 1., 0.);
taxonomy::point3 O = as<taxonomy::point3>(map(inst->LocalOrigin()));
origin << *O.components_, 1.0;
if (inst->hasAxis1()) {
taxonomy::direction3 ax1 = as<taxonomy::direction3>(map(inst->Axis1()));
axis1 << *ax1.components_, 0.0;
}
if (inst->hasAxis2()) {
taxonomy::direction3 ax2 = as<taxonomy::direction3>(map(inst->Axis2()));
axis2 << *ax2.components_, 0.0;
}
if (inst->hasAxis3()) {
taxonomy::direction3 ax3 = as<taxonomy::direction3>(map(inst->Axis3()));
axis3 << *ax3.components_, 0.0;
}
double scale1, scale2, scale3;
scale1 = scale2 = scale3 = 1.;
if (inst->hasScale()) {
scale1 = inst->Scale();
}
if (inst->as<IfcSchema::IfcCartesianTransformationOperator3DnonUniform>()) {
auto nu = inst->as<IfcSchema::IfcCartesianTransformationOperator3DnonUniform>();
scale2 = nu->hasScale2() ? nu->Scale2() : scale1;
scale3 = nu->hasScale3() ? nu->Scale3() : scale1;
}
Eigen::Matrix4d tmp;
tmp <<
axis1 * scale1,
axis2 * scale2,
axis3 * scale3,
origin;
m->components() = tmp.inverse();
m->components().transposeInPlace();
// @todo tag identity?
return m;
}
+10
View File
@@ -0,0 +1,10 @@
#include "mapping.h"
#define mapping POSTFIX_SCHEMA(mapping)
using namespace ifcopenshell::geometry;
taxonomy::item* mapping::map_impl(const IfcSchema::IfcCircle* inst) {
auto c = new taxonomy::circle;
c->matrix = as<taxonomy::matrix4>(map(inst->Position()));
c->radius = inst->Radius() * length_unit_;
return c;
}
@@ -0,0 +1,48 @@
#include "mapping.h"
#define mapping POSTFIX_SCHEMA(mapping)
using namespace ifcopenshell::geometry;
#include <boost/math/constants/constants.hpp>
taxonomy::item* mapping::map_impl(const IfcSchema::IfcCircleProfileDef* inst) {
std::vector<double> radii = { inst->Radius() * length_unit_ };
if (inst->as<IfcSchema::IfcCircleHollowProfileDef>()) {
double t = inst->as<IfcSchema::IfcCircleHollowProfileDef>()->WallThickness() * length_unit_;
radii.push_back(radii.front() - t);
}
auto f = new taxonomy::face;
for (auto it = radii.begin(); it != radii.end(); ++it) {
const double r = *it;
const bool exterior = it == radii.begin();
auto c = new taxonomy::circle;
c->radius = r;
bool has_position = true;
#ifdef SCHEMA_IfcParameterizedProfileDef_Position_IS_OPTIONAL
has_position = inst->hasPosition();
#endif
if (has_position) {
taxonomy::matrix4 m = as<taxonomy::matrix4>(map(inst->Position()));
if (m.components_) {
c->matrix = *m.components_;
}
}
auto e = new taxonomy::edge;
e->basis = c;
e->start = 0.;
e->end = 2 * boost::math::constants::pi<double>();
auto l = new taxonomy::loop;
l->children = { e };
l->external = exterior;
f->children.push_back(l);
}
return f;
}
+30
View File
@@ -0,0 +1,30 @@
#include "mapping.h"
#define mapping POSTFIX_SCHEMA(mapping)
using namespace ifcopenshell::geometry;
taxonomy::item* mapping::map_impl(const IfcSchema::IfcCompositeCurve* inst) {
auto loop = new taxonomy::loop;
auto segments = inst->Segments();
for (auto& segment : *segments) {
auto crv = map(segment->ParentCurve());
if (crv) {
if (crv->kind() == taxonomy::EDGE) {
((taxonomy::edge*)crv)->orientation_2.reset(segment->SameSense());
loop->children.push_back(crv);
} else if (crv->kind() == taxonomy::LOOP) {
if (!segment->SameSense()) {
crv->reverse();
}
auto curve_segments = ((taxonomy::loop*)crv)->children_as<taxonomy::edge>();
for (auto& s : curve_segments) {
loop->children.push_back(s);
}
// @todo delete crv without children
}
}
}
IfcEntityList::ptr profile = inst->data().getInverse(&IfcSchema::IfcProfileDef::Class(), -1);
const bool force_close = profile && profile->size() > 0;
loop->closed = force_close;
return loop;
}
@@ -0,0 +1,12 @@
#include "mapping.h"
#define mapping POSTFIX_SCHEMA(mapping)
using namespace ifcopenshell::geometry;
taxonomy::item* mapping::map_impl(const IfcSchema::IfcConnectedFaceSet* inst) {
auto shell = map_to_collection<taxonomy::shell>(this, inst->CfsFaces());
if (shell == nullptr) {
return nullptr;
}
shell->closed = inst->declaration().is(IfcSchema::IfcClosedShell::Class());
return shell;
}
+12
View File
@@ -0,0 +1,12 @@
#include "mapping.h"
#define mapping POSTFIX_SCHEMA(mapping)
using namespace ifcopenshell::geometry;
taxonomy::item* mapping::map_impl(const IfcSchema::IfcDirection* inst) {
auto coords = inst->DirectionRatios();
return new taxonomy::direction3(
coords.size() >= 1 ? coords[0] : 0.,
coords.size() >= 2 ? coords[1] : 0.,
coords.size() >= 3 ? coords[2] : 0.
);
}
@@ -0,0 +1,12 @@
#include "mapping.h"
#define mapping POSTFIX_SCHEMA(mapping)
using namespace ifcopenshell::geometry;
taxonomy::item* mapping::map_impl(const IfcSchema::IfcExtrudedAreaSolid* inst) {
return new taxonomy::extrusion(
as<taxonomy::matrix4>(map(inst->Position())),
as<taxonomy::face>(map(inst->SweptArea())),
as<taxonomy::direction3>(map(inst->ExtrudedDirection())),
inst->Depth() * length_unit_
);
}
+31
View File
@@ -0,0 +1,31 @@
#include "mapping.h"
#define mapping POSTFIX_SCHEMA(mapping)
using namespace ifcopenshell::geometry;
taxonomy::item* mapping::map_impl(const IfcSchema::IfcFace* inst) {
taxonomy::face* face = new taxonomy::face;
auto bounds = inst->Bounds();
for (auto& bound : *bounds) {
if (auto r = map(bound->Bound())) {
if (!bound->Orientation()) {
r->reverse();
}
if (bound->declaration().is(IfcSchema::IfcFaceOuterBound::Class())) {
((taxonomy::loop*)r)->external = true;
/*
// Make a copy in case we need immutability later for e.g. caching
auto s = r->clone();
((taxonomy::loop*)s)->external = true;
delete r;
r = s;
*/
}
face->children.push_back(r);
}
}
if (face->children.empty()) {
delete face;
return nullptr;
}
return face;
}
@@ -0,0 +1,7 @@
#include "mapping.h"
#define mapping POSTFIX_SCHEMA(mapping)
using namespace ifcopenshell::geometry;
taxonomy::item* mapping::map_impl(const IfcSchema::IfcFaceBasedSurfaceModel* inst) {
return map_to_collection(this, inst->FbsmFaces());
}
+7
View File
@@ -0,0 +1,7 @@
#include "mapping.h"
#define mapping POSTFIX_SCHEMA(mapping)
using namespace ifcopenshell::geometry;
taxonomy::item* mapping::map_impl(const IfcSchema::IfcGeometricSet* inst) {
return map_to_collection(this, inst->Elements());
}
+17
View File
@@ -0,0 +1,17 @@
#include "mapping.h"
#define mapping POSTFIX_SCHEMA(mapping)
using namespace ifcopenshell::geometry;
taxonomy::item* mapping::map_impl(const IfcSchema::IfcHalfSpaceSolid* inst) {
IfcSchema::IfcSurface* surface = inst->BaseSurface();
if (!surface->declaration().is(IfcSchema::IfcPlane::Class())) {
Logger::Message(Logger::LOG_ERROR, "Unsupported BaseSurface:", surface);
return nullptr;
}
auto p = new taxonomy::plane;
p->matrix = as<taxonomy::matrix4>(map(((IfcSchema::IfcPlane*)surface)->Position()));
p->orientation.reset(!inst->AgreementFlag());
auto f = new taxonomy::face;
f->basis = p;
return f;
}
@@ -0,0 +1,67 @@
#include "mapping.h"
#define mapping POSTFIX_SCHEMA(mapping)
using namespace ifcopenshell::geometry;
#include "../profile_helper.h"
taxonomy::item* mapping::map_impl(const IfcSchema::IfcIShapeProfileDef* inst) {
const double x1 = inst->OverallWidth() / 2.0f * length_unit_;
const double y = inst->OverallDepth() / 2.0f * length_unit_;
const double d1 = inst->WebThickness() / 2.0f * length_unit_;
const double dy1 = inst->FlangeThickness() * length_unit_;
bool doFillet1 = inst->hasFilletRadius();
double f1 = 0.;
if (doFillet1) {
f1 = inst->FilletRadius() * length_unit_;
}
bool doFillet2 = doFillet1;
double x2 = x1, dy2 = dy1, f2 = f1;
if (inst->declaration().is(IfcSchema::IfcAsymmetricIShapeProfileDef::Class())) {
IfcSchema::IfcAsymmetricIShapeProfileDef* assym = (IfcSchema::IfcAsymmetricIShapeProfileDef*) inst;
x2 = assym->TopFlangeWidth() / 2. * length_unit_;
doFillet2 = assym->hasTopFlangeFilletRadius();
if (doFillet2) {
f2 = assym->TopFlangeFilletRadius() * length_unit_;
}
if (assym->hasTopFlangeThickness()) {
dy2 = assym->TopFlangeThickness() * length_unit_;
}
}
// @todo
const double precision_ = 1.e-5;
if (x1 < precision_ || x2 < precision_ || y < precision_ || d1 < precision_ || dy1 < precision_ || dy2 < precision_) {
Logger::Message(Logger::LOG_NOTICE, "Skipping zero sized profile:", inst);
return nullptr;
}
Eigen::Matrix4d m4;
bool has_position = true;
#ifdef SCHEMA_IfcParameterizedProfileDef_Position_IS_OPTIONAL
has_position = inst->hasPosition();
#endif
if (has_position) {
taxonomy::matrix4 m = as<taxonomy::matrix4>(map(inst->Position()));
m4 = m.ccomponents();
}
return profile_helper(m4, {
{{-x1,-y}},
{{x1,-y}},
{{x1,-y + dy1}},
{{d1,-y + dy1}, f1},
{{d1,y - dy2}, f2},
{{x2,y - dy2}},
{{x2,y}},
{{-x2,y}},
{{-x2,y - dy2}},
{{-d1,y - dy2}, f2},
{{-d1,-y + dy1}, f1},
{{-x1,-y + dy1}}
});
}

Some files were not shown because too many files have changed in this diff Show More