From d6f8448c835b64d1b66dccd3d9a1deb2a0c065e3 Mon Sep 17 00:00:00 2001 From: Thomas Krijnen Date: Tue, 14 Jun 2022 15:02:54 +0200 Subject: [PATCH] IfcGeom refactoring pt. 1 --- src/ifcgeom/IfcAdvancedBrep.cpp | 99 + src/ifcgeom/IfcAnnotationFillArea.cpp | 119 ++ src/ifcgeom/IfcArbitraryClosedProfileDef.cpp | 106 + src/ifcgeom/IfcArbitraryOpenProfileDef.cpp | 87 + .../IfcArbitraryProfileDefWithVoids.cpp | 117 ++ src/ifcgeom/IfcAxis1Placement.cpp | 83 + src/ifcgeom/IfcAxis2Placement2D.cpp | 91 + src/ifcgeom/IfcAxis2Placement3D.cpp | 114 + ...urves.cpp => IfcBSplineCurveWithKnots.cpp} | 86 - src/ifcgeom/IfcBSplineSurfaceWithKnots.cpp | 146 ++ src/ifcgeom/IfcBlock.cpp | 106 + src/ifcgeom/IfcBooleanResult.cpp | 298 +++ src/ifcgeom/IfcBoundingBox.cpp | 105 + src/ifcgeom/IfcCShapeProfileDef.cpp | 123 ++ src/ifcgeom/IfcCartesianPoint.cpp | 78 + .../IfcCartesianTransformationOperator2D.cpp | 107 + ...sianTransformationOperator2DnonUniform.cpp | 107 + .../IfcCartesianTransformationOperator3D.cpp | 92 + ...sianTransformationOperator3DnonUniform.cpp | 98 + src/ifcgeom/IfcCenterLineProfileDef.cpp | 139 ++ src/ifcgeom/IfcCircle.cpp | 88 + src/ifcgeom/IfcCircleHollowProfileDef.cpp | 126 ++ src/ifcgeom/IfcCircleProfileDef.cpp | 120 ++ src/ifcgeom/IfcCompositeCurve.cpp | 238 +++ src/ifcgeom/IfcCompositeProfileDef.cpp | 120 ++ src/ifcgeom/IfcConnectedFaceSet.cpp | 180 ++ src/ifcgeom/IfcCraneRailAShapeProfileDef.cpp | 133 ++ src/ifcgeom/IfcCsgSolid.cpp | 95 + src/ifcgeom/IfcCurveBoundedPlane.cpp | 130 ++ src/ifcgeom/IfcCylindricalSurface.cpp | 104 + src/ifcgeom/IfcDerivedProfileDef.cpp | 102 + src/ifcgeom/IfcDirection.cpp | 78 + src/ifcgeom/IfcEdge.cpp | 110 + src/ifcgeom/IfcEdgeCurve.cpp | 171 ++ src/ifcgeom/IfcEdgeLoop.cpp | 99 + src/ifcgeom/IfcEllipse.cpp | 101 + src/ifcgeom/IfcEllipseProfileDef.cpp | 128 ++ src/ifcgeom/IfcExtrudedAreaSolid.cpp | 165 ++ src/ifcgeom/IfcExtrudedAreaSolidTapered.cpp | 195 ++ src/ifcgeom/IfcFace.cpp | 359 ++++ src/ifcgeom/IfcFaceBasedSurfaceModel.cpp | 106 + .../{IfcGeomFunctions.cpp => IfcGeom.cpp} | 841 +------- src/ifcgeom/IfcGeom.h | 19 +- src/ifcgeom/IfcGeomFaces.cpp | 1284 ------------ src/ifcgeom/IfcGeomHelpers.cpp | 476 ----- src/ifcgeom/IfcGeomShapes.cpp | 1864 ----------------- src/ifcgeom/IfcGeomWires.cpp | 1086 ---------- src/ifcgeom/IfcGeometricSet.cpp | 140 ++ src/ifcgeom/IfcHalfSpaceSolid.cpp | 104 + src/ifcgeom/IfcIShapeProfileDef.cpp | 138 ++ src/ifcgeom/IfcIndexedPolyCurve.cpp | 178 ++ src/ifcgeom/IfcLShapeProfileDef.cpp | 165 ++ src/ifcgeom/IfcLine.cpp | 77 + src/ifcgeom/IfcManifoldSolidBrep.cpp | 122 ++ src/ifcgeom/IfcMappedItem.cpp | 138 ++ src/ifcgeom/IfcObjectPlacement.cpp | 118 ++ src/ifcgeom/IfcOrientedEdge.cpp | 94 + src/ifcgeom/IfcPlane.cpp | 126 ++ src/ifcgeom/IfcPolyLoop.cpp | 133 ++ src/ifcgeom/IfcPolygonalBoundedHalfSpace.cpp | 125 ++ src/ifcgeom/IfcPolygonalFaceSet.cpp | 209 ++ src/ifcgeom/IfcPolyline.cpp | 125 ++ src/ifcgeom/IfcRectangleHollowProfileDef.cpp | 144 ++ src/ifcgeom/IfcRectangleProfileDef.cpp | 112 + src/ifcgeom/IfcRectangularPyramid.cpp | 113 + src/ifcgeom/IfcRectangularTrimmedSurface.cpp | 106 + src/ifcgeom/IfcRepresentation.cpp | 118 ++ src/ifcgeom/IfcRevolvedAreaSolid.cpp | 163 ++ src/ifcgeom/IfcRightCircularCone.cpp | 105 + src/ifcgeom/IfcRightCircularCylinder.cpp | 105 + src/ifcgeom/IfcRoundedRectangleProfileDef.cpp | 115 + src/ifcgeom/IfcShellBasedSurfaceModel.cpp | 107 + src/ifcgeom/IfcSphere.cpp | 104 + src/ifcgeom/IfcSphericalSurface.cpp | 104 + src/ifcgeom/IfcSubedge.cpp | 101 + src/ifcgeom/IfcSurfaceCurve.cpp | 75 + src/ifcgeom/IfcSurfaceCurveSweptAreaSolid.cpp | 209 ++ src/ifcgeom/IfcSurfaceOfLinearExtrusion.cpp | 124 ++ src/ifcgeom/IfcSurfaceOfRevolution.cpp | 123 ++ src/ifcgeom/IfcSweptDiskSolid.cpp | 189 ++ src/ifcgeom/IfcTShapeProfileDef.cpp | 179 ++ src/ifcgeom/IfcToroidalSurface.cpp | 104 + src/ifcgeom/IfcTrapeziumProfileDef.cpp | 123 ++ src/ifcgeom/IfcTriangulatedFaceSet.cpp | 132 ++ src/ifcgeom/IfcTrimmedCurve.cpp | 292 +++ src/ifcgeom/IfcUShapeProfileDef.cpp | 138 ++ src/ifcgeom/IfcVector.cpp | 75 + src/ifcgeom/IfcZShapeProfileDef.cpp | 129 ++ src/ifcgeom_schema_agnostic/Kernel.cpp | 50 + src/ifcgeom_schema_agnostic/Kernel.h | 50 + 90 files changed, 10675 insertions(+), 5625 deletions(-) create mode 100644 src/ifcgeom/IfcAdvancedBrep.cpp create mode 100644 src/ifcgeom/IfcAnnotationFillArea.cpp create mode 100644 src/ifcgeom/IfcArbitraryClosedProfileDef.cpp create mode 100644 src/ifcgeom/IfcArbitraryOpenProfileDef.cpp create mode 100644 src/ifcgeom/IfcArbitraryProfileDefWithVoids.cpp create mode 100644 src/ifcgeom/IfcAxis1Placement.cpp create mode 100644 src/ifcgeom/IfcAxis2Placement2D.cpp create mode 100644 src/ifcgeom/IfcAxis2Placement3D.cpp rename src/ifcgeom/{IfcGeomCurves.cpp => IfcBSplineCurveWithKnots.cpp} (63%) create mode 100644 src/ifcgeom/IfcBSplineSurfaceWithKnots.cpp create mode 100644 src/ifcgeom/IfcBlock.cpp create mode 100644 src/ifcgeom/IfcBooleanResult.cpp create mode 100644 src/ifcgeom/IfcBoundingBox.cpp create mode 100644 src/ifcgeom/IfcCShapeProfileDef.cpp create mode 100644 src/ifcgeom/IfcCartesianPoint.cpp create mode 100644 src/ifcgeom/IfcCartesianTransformationOperator2D.cpp create mode 100644 src/ifcgeom/IfcCartesianTransformationOperator2DnonUniform.cpp create mode 100644 src/ifcgeom/IfcCartesianTransformationOperator3D.cpp create mode 100644 src/ifcgeom/IfcCartesianTransformationOperator3DnonUniform.cpp create mode 100644 src/ifcgeom/IfcCenterLineProfileDef.cpp create mode 100644 src/ifcgeom/IfcCircle.cpp create mode 100644 src/ifcgeom/IfcCircleHollowProfileDef.cpp create mode 100644 src/ifcgeom/IfcCircleProfileDef.cpp create mode 100644 src/ifcgeom/IfcCompositeCurve.cpp create mode 100644 src/ifcgeom/IfcCompositeProfileDef.cpp create mode 100644 src/ifcgeom/IfcConnectedFaceSet.cpp create mode 100644 src/ifcgeom/IfcCraneRailAShapeProfileDef.cpp create mode 100644 src/ifcgeom/IfcCsgSolid.cpp create mode 100644 src/ifcgeom/IfcCurveBoundedPlane.cpp create mode 100644 src/ifcgeom/IfcCylindricalSurface.cpp create mode 100644 src/ifcgeom/IfcDerivedProfileDef.cpp create mode 100644 src/ifcgeom/IfcDirection.cpp create mode 100644 src/ifcgeom/IfcEdge.cpp create mode 100644 src/ifcgeom/IfcEdgeCurve.cpp create mode 100644 src/ifcgeom/IfcEdgeLoop.cpp create mode 100644 src/ifcgeom/IfcEllipse.cpp create mode 100644 src/ifcgeom/IfcEllipseProfileDef.cpp create mode 100644 src/ifcgeom/IfcExtrudedAreaSolid.cpp create mode 100644 src/ifcgeom/IfcExtrudedAreaSolidTapered.cpp create mode 100644 src/ifcgeom/IfcFace.cpp create mode 100644 src/ifcgeom/IfcFaceBasedSurfaceModel.cpp rename src/ifcgeom/{IfcGeomFunctions.cpp => IfcGeom.cpp} (87%) delete mode 100644 src/ifcgeom/IfcGeomFaces.cpp delete mode 100644 src/ifcgeom/IfcGeomHelpers.cpp delete mode 100644 src/ifcgeom/IfcGeomShapes.cpp delete mode 100644 src/ifcgeom/IfcGeomWires.cpp create mode 100644 src/ifcgeom/IfcGeometricSet.cpp create mode 100644 src/ifcgeom/IfcHalfSpaceSolid.cpp create mode 100644 src/ifcgeom/IfcIShapeProfileDef.cpp create mode 100644 src/ifcgeom/IfcIndexedPolyCurve.cpp create mode 100644 src/ifcgeom/IfcLShapeProfileDef.cpp create mode 100644 src/ifcgeom/IfcLine.cpp create mode 100644 src/ifcgeom/IfcManifoldSolidBrep.cpp create mode 100644 src/ifcgeom/IfcMappedItem.cpp create mode 100644 src/ifcgeom/IfcObjectPlacement.cpp create mode 100644 src/ifcgeom/IfcOrientedEdge.cpp create mode 100644 src/ifcgeom/IfcPlane.cpp create mode 100644 src/ifcgeom/IfcPolyLoop.cpp create mode 100644 src/ifcgeom/IfcPolygonalBoundedHalfSpace.cpp create mode 100644 src/ifcgeom/IfcPolygonalFaceSet.cpp create mode 100644 src/ifcgeom/IfcPolyline.cpp create mode 100644 src/ifcgeom/IfcRectangleHollowProfileDef.cpp create mode 100644 src/ifcgeom/IfcRectangleProfileDef.cpp create mode 100644 src/ifcgeom/IfcRectangularPyramid.cpp create mode 100644 src/ifcgeom/IfcRectangularTrimmedSurface.cpp create mode 100644 src/ifcgeom/IfcRepresentation.cpp create mode 100644 src/ifcgeom/IfcRevolvedAreaSolid.cpp create mode 100644 src/ifcgeom/IfcRightCircularCone.cpp create mode 100644 src/ifcgeom/IfcRightCircularCylinder.cpp create mode 100644 src/ifcgeom/IfcRoundedRectangleProfileDef.cpp create mode 100644 src/ifcgeom/IfcShellBasedSurfaceModel.cpp create mode 100644 src/ifcgeom/IfcSphere.cpp create mode 100644 src/ifcgeom/IfcSphericalSurface.cpp create mode 100644 src/ifcgeom/IfcSubedge.cpp create mode 100644 src/ifcgeom/IfcSurfaceCurve.cpp create mode 100644 src/ifcgeom/IfcSurfaceCurveSweptAreaSolid.cpp create mode 100644 src/ifcgeom/IfcSurfaceOfLinearExtrusion.cpp create mode 100644 src/ifcgeom/IfcSurfaceOfRevolution.cpp create mode 100644 src/ifcgeom/IfcSweptDiskSolid.cpp create mode 100644 src/ifcgeom/IfcTShapeProfileDef.cpp create mode 100644 src/ifcgeom/IfcToroidalSurface.cpp create mode 100644 src/ifcgeom/IfcTrapeziumProfileDef.cpp create mode 100644 src/ifcgeom/IfcTriangulatedFaceSet.cpp create mode 100644 src/ifcgeom/IfcTrimmedCurve.cpp create mode 100644 src/ifcgeom/IfcUShapeProfileDef.cpp create mode 100644 src/ifcgeom/IfcVector.cpp create mode 100644 src/ifcgeom/IfcZShapeProfileDef.cpp diff --git a/src/ifcgeom/IfcAdvancedBrep.cpp b/src/ifcgeom/IfcAdvancedBrep.cpp new file mode 100644 index 0000000000..50f5ff6fe1 --- /dev/null +++ b/src/ifcgeom/IfcAdvancedBrep.cpp @@ -0,0 +1,99 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include +#include + +#define Kernel MAKE_TYPE_NAME(Kernel) + +#ifdef SCHEMA_HAS_IfcAdvancedBrep + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcAdvancedBrep* l, TopoDS_Shape& shape) { + return convert(l->Outer(), shape); +} + +#endif diff --git a/src/ifcgeom/IfcAnnotationFillArea.cpp b/src/ifcgeom/IfcAnnotationFillArea.cpp new file mode 100644 index 0000000000..1f51c569a7 --- /dev/null +++ b/src/ifcgeom/IfcAnnotationFillArea.cpp @@ -0,0 +1,119 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include +#include +#include +#include + +#define Kernel MAKE_TYPE_NAME(Kernel) + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcAnnotationFillArea* l, TopoDS_Shape& face) { + TopoDS_Wire outer_boundary; + if (!convert_wire(l->OuterBoundary(), outer_boundary)) { + return false; + } + + assert_closed_wire(outer_boundary); + + BRepBuilderAPI_MakeFace mf(outer_boundary); + + if (l->InnerBoundaries()) { + IfcSchema::IfcCurve::list::ptr inner_boundaries = *l->InnerBoundaries(); + + for(IfcSchema::IfcCurve::list::it it = inner_boundaries->begin(); it != inner_boundaries->end(); ++it) { + TopoDS_Wire hole; + if (convert_wire(*it, hole)) { + assert_closed_wire(hole); + mf.Add(hole); + } + } + } + + ShapeFix_Shape sfs(mf.Face()); + sfs.Perform(); + face = sfs.Shape(); + + return true; +} diff --git a/src/ifcgeom/IfcArbitraryClosedProfileDef.cpp b/src/ifcgeom/IfcArbitraryClosedProfileDef.cpp new file mode 100644 index 0000000000..cdf21ae827 --- /dev/null +++ b/src/ifcgeom/IfcArbitraryClosedProfileDef.cpp @@ -0,0 +1,106 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include +#include +#include +#include + +#define Kernel MAKE_TYPE_NAME(Kernel) + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcArbitraryClosedProfileDef* l, TopoDS_Shape& face) { + TopoDS_Wire wire; + if (!convert_wire(l->OuterCurve(), wire)) { + return false; + } + + assert_closed_wire(wire); + + TopoDS_Compound f; + bool success = convert_wire_to_faces(wire, f); + if (success) { + face = f; + } + return success; +} diff --git a/src/ifcgeom/IfcArbitraryOpenProfileDef.cpp b/src/ifcgeom/IfcArbitraryOpenProfileDef.cpp new file mode 100644 index 0000000000..3ee45d751e --- /dev/null +++ b/src/ifcgeom/IfcArbitraryOpenProfileDef.cpp @@ -0,0 +1,87 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include + +#define _USE_MATH_DEFINES +#define Kernel MAKE_TYPE_NAME(Kernel) + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcArbitraryOpenProfileDef* l, TopoDS_Wire& result) { + return convert_wire(l->Curve(), result); +} diff --git a/src/ifcgeom/IfcArbitraryProfileDefWithVoids.cpp b/src/ifcgeom/IfcArbitraryProfileDefWithVoids.cpp new file mode 100644 index 0000000000..8b771de210 --- /dev/null +++ b/src/ifcgeom/IfcArbitraryProfileDefWithVoids.cpp @@ -0,0 +1,117 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include +#include +#include +#include + +#define Kernel MAKE_TYPE_NAME(Kernel) + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcArbitraryProfileDefWithVoids* l, TopoDS_Shape& face) { + TopoDS_Wire profile; + if (!convert_wire(l->OuterCurve(), profile)) { + return false; + } + + assert_closed_wire(profile); + + BRepBuilderAPI_MakeFace mf(profile); + + IfcSchema::IfcCurve::list::ptr voids = l->InnerCurves(); + + for(IfcSchema::IfcCurve::list::it it = voids->begin(); it != voids->end(); ++it) { + TopoDS_Wire hole; + if (convert_wire(*it, hole)) { + assert_closed_wire(hole); + mf.Add(hole); + } + } + + ShapeFix_Shape sfs(mf.Face()); + sfs.Perform(); + face = sfs.Shape(); + + return true; +} diff --git a/src/ifcgeom/IfcAxis1Placement.cpp b/src/ifcgeom/IfcAxis1Placement.cpp new file mode 100644 index 0000000000..7d02d0fbad --- /dev/null +++ b/src/ifcgeom/IfcAxis1Placement.cpp @@ -0,0 +1,83 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" + +#define Kernel MAKE_TYPE_NAME(Kernel) + +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); + + if (!l->Location()->declaration().is("IfcCartesianPoint")) { + // only applicable to 4x3 rc2 + Logger::Error("Not implemented", l->Location()); + return false; + } + + IfcGeom::Kernel::convert((const IfcSchema::IfcCartesianPoint*) l->Location(),o); + if ( l->Axis() ) IfcGeom::Kernel::convert(l->Axis(), axis); + ax = gp_Ax1(o, axis); + CACHE(IfcAxis1Placement,l,ax) + return true; +} diff --git a/src/ifcgeom/IfcAxis2Placement2D.cpp b/src/ifcgeom/IfcAxis2Placement2D.cpp new file mode 100644 index 0000000000..0eb968d287 --- /dev/null +++ b/src/ifcgeom/IfcAxis2Placement2D.cpp @@ -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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" + +#define Kernel MAKE_TYPE_NAME(Kernel) + +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); + + if (!l->Location()->declaration().is("IfcCartesianPoint")) { + // only applicable to 4x3 rc2 + Logger::Error("Not implemented", l->Location()); + return false; + } + + IfcGeom::Kernel::convert((const IfcSchema::IfcCartesianPoint*) l->Location(),P); + if (l->RefDirection()) { + 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; +} diff --git a/src/ifcgeom/IfcAxis2Placement3D.cpp b/src/ifcgeom/IfcAxis2Placement3D.cpp new file mode 100644 index 0000000000..fd9abd2779 --- /dev/null +++ b/src/ifcgeom/IfcAxis2Placement3D.cpp @@ -0,0 +1,114 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" + +#define Kernel MAKE_TYPE_NAME(Kernel) + +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; + + if (!l->Location()->declaration().is("IfcCartesianPoint")) { + // only applicable to 4x3 rc2 + Logger::Error("Not implemented", l->Location()); + return false; + } + + IfcGeom::Kernel::convert((const IfcSchema::IfcCartesianPoint*) l->Location(), o); + const bool hasAxis = !!l->Axis(); + const bool hasRef = !!l->RefDirection(); + + 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; +} diff --git a/src/ifcgeom/IfcGeomCurves.cpp b/src/ifcgeom/IfcBSplineCurveWithKnots.cpp similarity index 63% rename from src/ifcgeom/IfcGeomCurves.cpp rename to src/ifcgeom/IfcBSplineCurveWithKnots.cpp index 92c14ce31c..aacbdcc484 100644 --- a/src/ifcgeom/IfcGeomCurves.cpp +++ b/src/ifcgeom/IfcBSplineCurveWithKnots.cpp @@ -17,14 +17,7 @@ * * ********************************************************************************/ -/******************************************************************************** - * * - * Implementations of the various conversion functions defined in mapping.i * - * * - ********************************************************************************/ - #include - #include #include #include @@ -41,7 +34,6 @@ #include #include #include - #include #include #include @@ -50,109 +42,31 @@ #include #include #include - #include #include #include #include #include #include - #include #include #include #include - #include #include #include #include #include - #include #include #include - #include - #include - #include "../ifcgeom/IfcGeom.h" - -#ifdef SCHEMA_HAS_IfcBSplineCurveWithKnots #include -#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->as()) { - IfcGeom::Kernel::convert(placement->as(),trsf); - } else { - gp_Trsf2d trsf2d; - IfcGeom::Kernel::convert(placement->as(),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->as()) { - IfcGeom::Kernel::convert(placement->as(), trsf); - } else { - gp_Trsf2d trsf2d; - IfcGeom::Kernel::convert(placement->as(), 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; -} - -#ifdef SCHEMA_HAS_IfcSurfaceCurve -bool IfcGeom::Kernel::convert(const IfcSchema::IfcSurfaceCurve* sc, Handle(Geom_Curve)& curve) { - // @todo take into account PCurves. - return convert_curve(sc->Curve3D(), curve); -} -#endif - -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) { diff --git a/src/ifcgeom/IfcBSplineSurfaceWithKnots.cpp b/src/ifcgeom/IfcBSplineSurfaceWithKnots.cpp new file mode 100644 index 0000000000..70db779d46 --- /dev/null +++ b/src/ifcgeom/IfcBSplineSurfaceWithKnots.cpp @@ -0,0 +1,146 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include +#include +#include +#include + +#define Kernel MAKE_TYPE_NAME(Kernel) + +#ifdef SCHEMA_HAS_IfcBSplineSurfaceWithKnots + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcBSplineSurfaceWithKnots* l, TopoDS_Shape& face) { + boost::shared_ptr< aggregate_of_aggregate_of > cps = l->ControlPointsList(); + std::vector uknots = l->UKnots(); + std::vector vknots = l->VKnots(); + std::vector umults = l->UMultiplicities(); + std::vector vmults = l->VMultiplicities(); + + TColgp_Array2OfPnt Poles (0, (int)cps->size() - 1, 0, (int)(*cps->begin()).size() - 1); + TColStd_Array1OfReal UKnots(0, (int)uknots.size() - 1); + TColStd_Array1OfReal VKnots(0, (int)vknots.size() - 1); + TColStd_Array1OfInteger UMults(0, (int)umults.size() - 1); + TColStd_Array1OfInteger VMults(0, (int)vmults.size() - 1); + Standard_Integer UDegree = l->UDegree(); + Standard_Integer VDegree = l->VDegree(); + + int i = 0, j; + for (aggregate_of_aggregate_of::outer_it it = cps->begin(); it != cps->end(); ++it, ++i) { + j = 0; + for (aggregate_of_aggregate_of::inner_it jt = (*it).begin(); jt != (*it).end(); ++jt, ++j) { + IfcSchema::IfcCartesianPoint* p = *jt; + gp_Pnt pnt; + if (!convert(p, pnt)) return false; + Poles(i, j) = pnt; + } + } + i = 0; + for (std::vector::const_iterator it = uknots.begin(); it != uknots.end(); ++it, ++i) { + UKnots(i) = *it; + } + i = 0; + for (std::vector::const_iterator it = vknots.begin(); it != vknots.end(); ++it, ++i) { + VKnots(i) = *it; + } + i = 0; + for (std::vector::const_iterator it = umults.begin(); it != umults.end(); ++it, ++i) { + UMults(i) = *it; + } + i = 0; + for (std::vector::const_iterator it = vmults.begin(); it != vmults.end(); ++it, ++i) { + VMults(i) = *it; + } + Handle_Geom_Surface surf = new Geom_BSplineSurface(Poles, UKnots, VKnots, UMults, VMults, UDegree, VDegree); + +#if OCC_VERSION_HEX < 0x60502 + face = BRepBuilderAPI_MakeFace(surf); +#else + face = BRepBuilderAPI_MakeFace(surf, getValue(GV_PRECISION)); +#endif + + return true; +} + +#endif diff --git a/src/ifcgeom/IfcBlock.cpp b/src/ifcgeom/IfcBlock.cpp new file mode 100644 index 0000000000..5a546fe9d5 --- /dev/null +++ b/src/ifcgeom/IfcBlock.cpp @@ -0,0 +1,106 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include +#include + +#define Kernel MAKE_TYPE_NAME(Kernel) + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcBlock* l, TopoDS_Shape& 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); + + BRepPrimAPI_MakeBox builder(dx, dy, dz); + gp_Trsf trsf; + IfcGeom::Kernel::convert(l->Position(),trsf); + + // IfcCsgPrimitive3D.Position has unit scale factor + shape = builder.Solid().Moved(trsf); + + return true; +} diff --git a/src/ifcgeom/IfcBooleanResult.cpp b/src/ifcgeom/IfcBooleanResult.cpp new file mode 100644 index 0000000000..72351e6c24 --- /dev/null +++ b/src/ifcgeom/IfcBooleanResult.cpp @@ -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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include +#include + +#define Kernel MAKE_TYPE_NAME(Kernel) + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcBooleanResult* l, TopoDS_Shape& shape) { + + TopoDS_Shape s1; + IfcRepresentationShapeItems items1; + TopoDS_Wire boundary_wire; + IfcSchema::IfcBooleanOperand* operand1 = l->FirstOperand(); + IfcSchema::IfcBooleanOperand* operand2 = l->SecondOperand(); + bool has_halfspace_operand = false; + + BOPAlgo_Operation occ_op; + + const IfcSchema::IfcBooleanOperator::Value op = l->Operator(); + if (op == IfcSchema::IfcBooleanOperator::IfcBooleanOperator_DIFFERENCE) { + occ_op = BOPAlgo_CUT; + } else if (op == IfcSchema::IfcBooleanOperator::IfcBooleanOperator_INTERSECTION) { + occ_op = BOPAlgo_COMMON; + } else if (op == IfcSchema::IfcBooleanOperator::IfcBooleanOperator_UNION) { + occ_op = BOPAlgo_FUSE; + } else { + return false; + } + + std::vector second_operands; + second_operands.push_back(operand2); + + if (occ_op == BOPAlgo_CUT) { + int n_half_space_operands = 0; + bool process_as_list = true; + while (true) { + auto res1 = operand1->as(); + if (res1 && res1->SecondOperand()->as() && ++n_half_space_operands > 8) { + // There is something peculiar about many half space subtraction operands that OCCT does not like. + // Often these are used to create a semi-curved arch, as is the case in 693. Supplying all these + // operands at once apparently leads to too many edge-edge interference checks. + process_as_list = false; + break; + } + if (res1) { + if (res1->Operator() == op) { + operand1 = res1->FirstOperand(); + second_operands.push_back(res1->SecondOperand()); + } else { + process_as_list = false; + break; + } + } else { + break; + } + } + + if (!process_as_list) { + operand1 = l->FirstOperand(); + second_operands = { operand2 }; + } + } + + if ( shape_type(operand1) == ST_SHAPELIST ) { + 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; + } + { TopoDS_Solid temp_solid; + s1 = ensure_fit_for_subtraction(s1, temp_solid); } + } else { + Logger::Message(Logger::LOG_ERROR, "Invalid representation item for boolean operation", operand1); + return false; + } + + if (getValue(GV_DISABLE_BOOLEAN_RESULT) > 0.0) { + shape = s1; + return true; + } + + const double first_operand_volume = shape_volume(s1); + if (first_operand_volume <= ALMOST_ZERO) { + Logger::Message(Logger::LOG_WARNING, "Empty solid for:", l->FirstOperand()); + } + + TopTools_ListOfShape second_operand_shapes; + + for (auto& op2 : second_operands) { + TopoDS_Shape s2; + + bool shape2_processed = false; + + bool is_halfspace = op2->declaration().is(IfcSchema::IfcHalfSpaceSolid::Class()); + bool is_unbounded_halfspace = is_halfspace && !op2->declaration().is(IfcSchema::IfcPolygonalBoundedHalfSpace::Class()); + has_halfspace_operand |= is_halfspace; + + { + if (shape_type(op2) == ST_SHAPELIST) { + IfcRepresentationShapeItems items2; + shape2_processed = convert_shapes(op2, items2) && flatten_shape_list(items2, s2, true); + } else if (shape_type(op2) == ST_SHAPE) { + shape2_processed = convert_shape(op2, s2); + if (shape2_processed) { + TopoDS_Solid temp_solid; + s2 = ensure_fit_for_subtraction(s2, temp_solid); + } + } else { + Logger::Message(Logger::LOG_ERROR, "Invalid representation item for boolean operation", op2); + } + } + + if (is_unbounded_halfspace) { + TopoDS_Shape temp; + double d; + if (fit_halfspace(s1, s2, temp, d)) { + if (d < getValue(GV_PRECISION)) { + Logger::Message(Logger::LOG_WARNING, "Halfspace subtraction yields unchanged volume:", l); + continue; + } else { + s2 = temp; + } + } + } + + if (!shape2_processed) { + Logger::Message(Logger::LOG_ERROR, "Failed to convert SecondOperand:", op2); + continue; + } + + if (op2->declaration().is(IfcSchema::IfcHalfSpaceSolid::Class())) { + const double second_operand_volume = shape_volume(s2); + if (second_operand_volume <= ALMOST_ZERO) { + Logger::Message(Logger::LOG_WARNING, "Empty solid for:", op2); + } + } + + second_operand_shapes.Append(s2); + } + + /* + // TK: A little debugging trick to output both operands for visual inspection + + BRep_Builder builder; + TopoDS_Compound compound; + builder.MakeCompound(compound); + builder.Add(compound, s1); + for (const auto& s2 : second_operand_shapes) { + builder.Add(compound, s2); + } + shape = compound; + return true; + */ + +#if OCC_VERSION_HEX < 0x60900 + // @todo: this currently does not compile anymore, do we still need this? + bool valid_result = boolean_operation(s1, s2, occ_op, shape); +#else + + bool valid_result; + + if (s1.ShapeType() == TopAbs_COMPOUND && TopoDS_Iterator(s1).More() && util::is_nested_compound_of_solid(s1)) { + TopoDS_Compound C; + BRep_Builder B; + B.MakeCompound(C); + TopoDS_Iterator it(s1); + valid_result = true; + for (; it.More(); it.Next()) { + TopoDS_Shape part; + if (boolean_operation(it.Value(), second_operand_shapes, occ_op, part)) { + B.Add(C, part); + } else { + valid_result = false; + } + } + shape = C; + } else { + valid_result = boolean_operation(s1, second_operand_shapes, occ_op, shape); + } + +#endif + + if (op == IfcSchema::IfcBooleanOperator::IfcBooleanOperator_DIFFERENCE) { + // In case of a subtraction, a check on volume is performed. + if (valid_result) { + const double volume_after_subtraction = shape_volume(shape); + if ( ALMOST_THE_SAME(first_operand_volume,volume_after_subtraction) ) + Logger::Message(Logger::LOG_WARNING,"Subtraction yields unchanged volume:",l); + } else { + Logger::Message(Logger::LOG_ERROR,"Failed to process subtraction:",l); + shape = s1; + } + // NB: After issuing error the first operand is returned! + return true; + } else if (op == IfcSchema::IfcBooleanOperator::IfcBooleanOperator_UNION && !valid_result) { + BRep_Builder B; + TopoDS_Compound C; + B.MakeCompound(C); + B.Add(C, s1); + TopTools_ListIteratorOfListOfShape it(second_operand_shapes); + for (; it.More(); it.Next()) { + B.Add(C, it.Value()); + } + Logger::Message(Logger::LOG_ERROR, "Failed to process union, creating compound:", l); + shape = C; + return true; + } else { + return valid_result; + } + return false; +} diff --git a/src/ifcgeom/IfcBoundingBox.cpp b/src/ifcgeom/IfcBoundingBox.cpp new file mode 100644 index 0000000000..435a450560 --- /dev/null +++ b/src/ifcgeom/IfcBoundingBox.cpp @@ -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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include +#include + +#define Kernel MAKE_TYPE_NAME(Kernel) + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcBoundingBox* l, TopoDS_Shape& shape) { + const double dx = l->XDim() * getValue(GV_LENGTH_UNIT); + const double dy = l->YDim() * getValue(GV_LENGTH_UNIT); + const double dz = l->ZDim() * getValue(GV_LENGTH_UNIT); + + gp_Pnt corner; + IfcGeom::Kernel::convert(l->Corner(), corner); + BRepPrimAPI_MakeBox builder(corner, dx, dy, dz); + + shape = builder.Solid(); + + return true; +} diff --git a/src/ifcgeom/IfcCShapeProfileDef.cpp b/src/ifcgeom/IfcCShapeProfileDef.cpp new file mode 100644 index 0000000000..e0c0a43207 --- /dev/null +++ b/src/ifcgeom/IfcCShapeProfileDef.cpp @@ -0,0 +1,123 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include +#include +#include +#include + +#define Kernel MAKE_TYPE_NAME(Kernel) + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcCShapeProfileDef* l, TopoDS_Shape& 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->InternalFilletRadius(); + 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; + } + + gp_Trsf2d trsf2d; + bool has_position = true; +#ifdef SCHEMA_IfcParameterizedProfileDef_Position_IS_OPTIONAL + has_position = l->Position() != nullptr; +#endif + if (has_position) { + IfcGeom::Kernel::convert(l->Position(), trsf2d); + } + + double coords[24] = {-x,-y,x,-y,x,-y+d2,x-d1,-y+d2,x-d1,-y+d1,-x+d1,-y+d1,-x+d1,y-d1,x-d1,y-d1,x-d1,y-d2,x,y-d2,x,y,-x,y}; + int fillets[8] = {0,1,4,5,6,7,10,11}; + double radii[8] = {f2,f2,f1,f1,f1,f1,f2,f2}; + return profile_helper(12,coords,doFillet ? 8 : 0,fillets,radii,trsf2d,face); +} diff --git a/src/ifcgeom/IfcCartesianPoint.cpp b/src/ifcgeom/IfcCartesianPoint.cpp new file mode 100644 index 0000000000..a571752b0c --- /dev/null +++ b/src/ifcgeom/IfcCartesianPoint.cpp @@ -0,0 +1,78 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" + +#define Kernel MAKE_TYPE_NAME(Kernel) + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcCartesianPoint* l, gp_Pnt& point) { + IN_CACHE(IfcCartesianPoint,l,gp_Pnt,point) + std::vector 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; +} diff --git a/src/ifcgeom/IfcCartesianTransformationOperator2D.cpp b/src/ifcgeom/IfcCartesianTransformationOperator2D.cpp new file mode 100644 index 0000000000..5056701f7c --- /dev/null +++ b/src/ifcgeom/IfcCartesianTransformationOperator2D.cpp @@ -0,0 +1,107 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" + +#define Kernel MAKE_TYPE_NAME(Kernel) + +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->Axis1() ) IfcGeom::Kernel::convert(l->Axis1(),axis1); + if ( l->Axis2() ) 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->Scale() && !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; +} diff --git a/src/ifcgeom/IfcCartesianTransformationOperator2DnonUniform.cpp b/src/ifcgeom/IfcCartesianTransformationOperator2DnonUniform.cpp new file mode 100644 index 0000000000..c2716a831c --- /dev/null +++ b/src/ifcgeom/IfcCartesianTransformationOperator2DnonUniform.cpp @@ -0,0 +1,107 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" + +#define Kernel MAKE_TYPE_NAME(Kernel) + +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->Axis1() ) IfcGeom::Kernel::convert(l->Axis1(),axis1); + if ( l->Axis2() ) 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->Scale().get_value_or(1.); + const double scale2 = l->Scale2().get_value_or(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; +} diff --git a/src/ifcgeom/IfcCartesianTransformationOperator3D.cpp b/src/ifcgeom/IfcCartesianTransformationOperator3D.cpp new file mode 100644 index 0000000000..8319a7090f --- /dev/null +++ b/src/ifcgeom/IfcCartesianTransformationOperator3D.cpp @@ -0,0 +1,92 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" + +#define Kernel MAKE_TYPE_NAME(Kernel) + +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->Axis1() ) IfcGeom::Kernel::convert(l->Axis1(),axis1); + if ( l->Axis2() ) IfcGeom::Kernel::convert(l->Axis2(),axis2); + if ( l->Axis3() ) 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->Scale() && !ALMOST_THE_SAME(*l->Scale(), 1.)) { + trsf.SetScaleFactor(*l->Scale()); + } + + CACHE(IfcCartesianTransformationOperator3D,l,trsf) + return true; +} diff --git a/src/ifcgeom/IfcCartesianTransformationOperator3DnonUniform.cpp b/src/ifcgeom/IfcCartesianTransformationOperator3DnonUniform.cpp new file mode 100644 index 0000000000..75e8f9f28d --- /dev/null +++ b/src/ifcgeom/IfcCartesianTransformationOperator3DnonUniform.cpp @@ -0,0 +1,98 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" + +#define Kernel MAKE_TYPE_NAME(Kernel) + +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->Axis1() ) IfcGeom::Kernel::convert(l->Axis1(),axis1); + if ( l->Axis2() ) IfcGeom::Kernel::convert(l->Axis2(),axis2); + if ( l->Axis3() ) 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->Scale().get_value_or(1.); + const double scale2 = l->Scale2().get_value_or(scale1); + const double scale3 = l->Scale3().get_value_or(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; +} diff --git a/src/ifcgeom/IfcCenterLineProfileDef.cpp b/src/ifcgeom/IfcCenterLineProfileDef.cpp new file mode 100644 index 0000000000..688137d268 --- /dev/null +++ b/src/ifcgeom/IfcCenterLineProfileDef.cpp @@ -0,0 +1,139 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include +#include +#include +#include + +#define Kernel MAKE_TYPE_NAME(Kernel) + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcCenterLineProfileDef* l, TopoDS_Shape& face) { + const double d = l->Thickness() * getValue(GV_LENGTH_UNIT) / 2.; + + TopoDS_Wire wire; + if (!convert_wire(l->Curve(), wire)) return false; + + // BRepOffsetAPI_MakeOffset insists on creating circular arc + // segments for joining the curves that constitute the center + // line. This is probably not in accordance with the IFC spec. + // Although it does not specify a method to join segments + // explicitly, it does dictate 'a constant thickness along the + // curve'. Therefore for simple singular wires a quick + // alternative is provided that uses a straight join. + + TopExp_Explorer exp(wire, TopAbs_EDGE); + TopoDS_Edge edge = TopoDS::Edge(exp.Current()); + exp.Next(); + + if (!exp.More()) { + double u1, u2; + Handle(Geom_Curve) curve = BRep_Tool::Curve(edge, u1, u2); + + Handle(Geom_TrimmedCurve) trim = new Geom_TrimmedCurve(curve, u1, u2); + + Handle(Geom_OffsetCurve) c1 = new Geom_OffsetCurve(trim, d, gp::DZ()); + Handle(Geom_OffsetCurve) c2 = new Geom_OffsetCurve(trim, -d, gp::DZ()); + + gp_Pnt c1a, c1b, c2a, c2b; + c1->D0(c1->FirstParameter(), c1a); + c1->D0(c1->LastParameter(), c1b); + c2->D0(c2->FirstParameter(), c2a); + c2->D0(c2->LastParameter(), c2b); + + BRepBuilderAPI_MakeWire mw; + mw.Add(BRepBuilderAPI_MakeEdge(c1)); + mw.Add(BRepBuilderAPI_MakeEdge(c1a, c2a)); + mw.Add(BRepBuilderAPI_MakeEdge(c2)); + mw.Add(BRepBuilderAPI_MakeEdge(c2b, c1b)); + + face = BRepBuilderAPI_MakeFace(mw.Wire()); + } else { + BRepOffsetAPI_MakeOffset offset(BRepBuilderAPI_MakeFace(gp_Pln(gp::Origin(), gp::DZ()))); + offset.AddWire(wire); + offset.Perform(d); + face = BRepBuilderAPI_MakeFace(TopoDS::Wire(offset)); + } + return true; +} diff --git a/src/ifcgeom/IfcCircle.cpp b/src/ifcgeom/IfcCircle.cpp new file mode 100644 index 0000000000..fdb933e30a --- /dev/null +++ b/src/ifcgeom/IfcCircle.cpp @@ -0,0 +1,88 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include + +#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->as()) { + IfcGeom::Kernel::convert(placement->as(),trsf); + } else { + gp_Trsf2d trsf2d; + IfcGeom::Kernel::convert(placement->as(),trsf2d); + trsf = trsf2d; + } + gp_Ax2 ax = gp_Ax2().Transformed(trsf); + curve = new Geom_Circle(ax, r); + return true; +} diff --git a/src/ifcgeom/IfcCircleHollowProfileDef.cpp b/src/ifcgeom/IfcCircleHollowProfileDef.cpp new file mode 100644 index 0000000000..b093d47801 --- /dev/null +++ b/src/ifcgeom/IfcCircleHollowProfileDef.cpp @@ -0,0 +1,126 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include +#include +#include +#include + +#define Kernel MAKE_TYPE_NAME(Kernel) + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcCircleHollowProfileDef* l, TopoDS_Shape& 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; + } + + gp_Trsf2d trsf2d; + bool has_position = true; +#ifdef SCHEMA_IfcParameterizedProfileDef_Position_IS_OPTIONAL + has_position = l->Position() != nullptr; +#endif + if (has_position) { + IfcGeom::Kernel::convert(l->Position(), trsf2d); + } + + gp_Ax2 ax = gp_Ax2().Transformed(trsf2d); + + BRepBuilderAPI_MakeWire outer; + Handle(Geom_Circle) outerCircle = new Geom_Circle(ax, r); + outer.Add(BRepBuilderAPI_MakeEdge(outerCircle)); + BRepBuilderAPI_MakeFace mf(outer.Wire(), false); + + BRepBuilderAPI_MakeWire inner; + Handle(Geom_Circle) innerCirlce = new Geom_Circle(ax, r-t); + inner.Add(BRepBuilderAPI_MakeEdge(innerCirlce)); + mf.Add(inner); + + ShapeFix_Shape sfs(mf.Face()); + sfs.Perform(); + face = TopoDS::Face(sfs.Shape()); + return true; +} diff --git a/src/ifcgeom/IfcCircleProfileDef.cpp b/src/ifcgeom/IfcCircleProfileDef.cpp new file mode 100644 index 0000000000..c1d415f49e --- /dev/null +++ b/src/ifcgeom/IfcCircleProfileDef.cpp @@ -0,0 +1,120 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include +#include +#include +#include + +#define Kernel MAKE_TYPE_NAME(Kernel) + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcCircleProfileDef* l, TopoDS_Shape& 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; + } + + gp_Trsf2d trsf2d; + bool has_position = true; +#ifdef SCHEMA_IfcParameterizedProfileDef_Position_IS_OPTIONAL + has_position = l->Position() != nullptr; +#endif + if (has_position) { + IfcGeom::Kernel::convert(l->Position(), trsf2d); + } + gp_Ax2 ax = gp_Ax2().Transformed(trsf2d); + + + Handle(Geom_Circle) circle = new Geom_Circle(ax, r); + TopoDS_Edge edge = BRepBuilderAPI_MakeEdge(circle); + + BRepBuilderAPI_MakeWire w; + w.Add(edge); + + TopoDS_Face f; + bool success = convert_wire_to_face(w, f); + if (success) face = f; + return success; +} diff --git a/src/ifcgeom/IfcCompositeCurve.cpp b/src/ifcgeom/IfcCompositeCurve.cpp new file mode 100644 index 0000000000..432e6dcda4 --- /dev/null +++ b/src/ifcgeom/IfcCompositeCurve.cpp @@ -0,0 +1,238 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include + +#include "../ifcgeom_schema_agnostic/wire_builder.h" + +#define _USE_MATH_DEFINES +#define Kernel MAKE_TYPE_NAME(Kernel) + +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; + } + +#ifdef SCHEMA_HAS_IfcSegment + // 4x3 + IfcSchema::IfcSegment::list::ptr segments = l->Segments(); +#else + IfcSchema::IfcCompositeCurveSegment::list::ptr segments = l->Segments(); +#endif + + TopTools_ListOfShape converted_segments; + + for (auto it = segments->begin(); it != segments->end(); ++it) { + + if (!(*it)->declaration().is(IfcSchema::IfcCompositeCurveSegment::Class())) { + Logger::Error("Not implemented", *it); + return false; + } + + IfcSchema::IfcCurve* curve = ((IfcSchema::IfcCompositeCurveSegment*)(*it))->ParentCurve(); + + // The type of ParentCurve is IfcCurve, but the documentation says: + // ParentCurve: The *bounded curve* which defines the geometry of the segment. + // At least let's exclude IfcLine as an infinite linear segment + // definitely does not make any sense. + TopoDS_Wire segment; + + if (curve->as()) { + Logger::Notice("Infinite IfcLine used as ParentCurve of segment, treating as a segment", *it); + Handle_Geom_Curve handle; + convert_curve(curve, handle); + double u0 = 0.0; + double u1 = curve->as()->Dir()->Magnitude() * getValue(GV_LENGTH_UNIT); + if (u1 < getValue(GV_PRECISION)) { + Logger::Warning("Segment length below tolerance", *it); + } + BRepBuilderAPI_MakeEdge me(handle, u0, u1); + if (me.IsDone()) { + BRep_Builder B; + B.MakeWire(segment); + B.Add(segment, me.Edge()); + } + } else if (!convert_wire(curve, segment)) { + const bool failed_on_purpose = curve->as() && !segment.IsNull(); + Logger::Message(failed_on_purpose ? Logger::LOG_WARNING : Logger::LOG_ERROR, "Failed to convert curve:", curve); + continue; + } + + if (!((IfcSchema::IfcCompositeCurveSegment*)(*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); + + aggregate_of_instance::ptr profile = l->data().getInverse(&IfcSchema::IfcProfileDef::Class(), -1); + const bool force_close = profile && profile->size() > 0; + + util::wire_builder bld(getValue(GV_PRECISION), l); + util::shape_pair_enumerate(it, bld, force_close); + wire = bld.wire(); + + return true; +} diff --git a/src/ifcgeom/IfcCompositeProfileDef.cpp b/src/ifcgeom/IfcCompositeProfileDef.cpp new file mode 100644 index 0000000000..3e4ba1de71 --- /dev/null +++ b/src/ifcgeom/IfcCompositeProfileDef.cpp @@ -0,0 +1,120 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include +#include +#include +#include + +#define Kernel MAKE_TYPE_NAME(Kernel) + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcCompositeProfileDef* l, TopoDS_Shape& face) { + // BRepBuilderAPI_MakeFace mf; + + TopoDS_Compound compound; + BRep_Builder builder; + builder.MakeCompound(compound); + + IfcSchema::IfcProfileDef::list::ptr profiles = l->Profiles(); + //bool first = true; + for (IfcSchema::IfcProfileDef::list::it it = profiles->begin(); it != profiles->end(); ++it) { + TopoDS_Face f; + if (convert_face(*it, f)) { + builder.Add(compound, f); + /* TopExp_Explorer exp(f, TopAbs_WIRE); + for (; exp.More(); exp.Next()) { + const TopoDS_Wire& wire = TopoDS::Wire(exp.Current()); + if (first) { + mf.Init(BRepBuilderAPI_MakeFace(wire)); + } else { + mf.Add(wire); + } + first = false; + } */ + } + } + + face = compound; + return !face.IsNull(); +} diff --git a/src/ifcgeom/IfcConnectedFaceSet.cpp b/src/ifcgeom/IfcConnectedFaceSet.cpp new file mode 100644 index 0000000000..2b4c450683 --- /dev/null +++ b/src/ifcgeom/IfcConnectedFaceSet.cpp @@ -0,0 +1,180 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include +#include + +#define Kernel MAKE_TYPE_NAME(Kernel) + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcConnectedFaceSet* l, TopoDS_Shape& shape) { + std::unique_ptr> helper_scope; + + IfcSchema::IfcCartesianPoint::list::ptr points = IfcParse::traverse((IfcUtil::IfcBaseClass*) l)->as(); + std::vector points_(points->begin(), points->end()); + + IfcSchema::IfcPolyLoop::list::ptr loops = IfcParse::traverse((IfcUtil::IfcBaseClass*)l)->as(); + std::vector loops_(loops->begin(), loops->end()); + + helper_scope.reset(new faceset_helper<>( + this, + points_, + loops_, + l->declaration().is(IfcSchema::IfcClosedShell::Class()) + )); + + faceset_helper_ = helper_scope.get(); + + IfcSchema::IfcFace::list::ptr faces = l->CfsFaces(); + + double min_face_area = faceset_helper_ + ? (faceset_helper_->epsilon() * faceset_helper_->epsilon() / 20.) + : getValue(GV_MINIMAL_FACE_AREA); + + TopTools_ListOfShape face_list; + for (IfcSchema::IfcFace::list::it it = faces->begin(); it != faces->end(); ++it) { + bool success = false; + TopoDS_Face face; + + try { + success = convert_face(*it, 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:", (*it)); + continue; + } + + if (face.ShapeType() == TopAbs_COMPOUND) { + TopoDS_Iterator face_it(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:", (*it)); + } + } + } + } else { + if (face_area(face) > min_face_area) { + face_list.Append(face); + } else { + Logger::Message(Logger::LOG_WARNING, "Degenerate face:", (*it)); + } + } + } + + if (face_list.Extent() == 0) { + return false; + } + + if (face_list.Extent() > getValue(GV_MAX_FACES_TO_ORIENT) || !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; +} diff --git a/src/ifcgeom/IfcCraneRailAShapeProfileDef.cpp b/src/ifcgeom/IfcCraneRailAShapeProfileDef.cpp new file mode 100644 index 0000000000..b9b87b0497 --- /dev/null +++ b/src/ifcgeom/IfcCraneRailAShapeProfileDef.cpp @@ -0,0 +1,133 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include +#include +#include +#include + +#define Kernel MAKE_TYPE_NAME(Kernel) + +#ifdef SCHEMA_HAS_IfcCraneRailAShapeProfileDef +bool IfcGeom::Kernel::convert(const IfcSchema::IfcCraneRailAShapeProfileDef* l, TopoDS_Shape& face) { + double oh = l->OverallHeight() * getValue(GV_LENGTH_UNIT); + double bw2 = l->BaseWidth2() * getValue(GV_LENGTH_UNIT); + double hw = l->HeadWidth() * getValue(GV_LENGTH_UNIT); + double hd2 = l->HeadDepth2() * getValue(GV_LENGTH_UNIT); + double hd3 = l->HeadDepth3() * getValue(GV_LENGTH_UNIT); + double wt = l->WebThickness() * getValue(GV_LENGTH_UNIT); + double bw4 = l->BaseWidth4() * getValue(GV_LENGTH_UNIT); + double bd1 = l->BaseDepth1() * getValue(GV_LENGTH_UNIT); + double bd2 = l->BaseDepth2() * getValue(GV_LENGTH_UNIT); + double bd3 = l->BaseDepth3() * getValue(GV_LENGTH_UNIT); + + gp_Trsf2d trsf2d; + bool has_position = true; +#ifdef SCHEMA_IfcParameterizedProfileDef_Position_IS_OPTIONAL + has_position = l->Position() != nullptr; +#endif + if (has_position) { + IfcGeom::Kernel::convert(l->Position(), trsf2d); + } + + double coords[28] = { + -hw / 2., +oh / 2., + -hw / 2., +oh / 2. - hd3, + -wt / 2., +oh / 2. - hd2, + -wt / 2., -oh / 2. + bd2, + -bw4 / 2., -oh / 2. + bd3, + -bw2 / 2., -oh / 2. + bd1, + -bw2 / 2., -oh / 2., + +bw2 / 2., -oh / 2., + +bw2 / 2., -oh / 2. + bd1, + +bw4 / 2., -oh / 2. + bd3, + +wt / 2., -oh / 2. + bd2, + +wt / 2., +oh / 2. - hd2, + +hw / 2., +oh / 2. - hd3, + +hw / 2., +oh / 2. + }; + + return profile_helper(14, coords, 0, 0, 0, trsf2d, face); +} +#endif diff --git a/src/ifcgeom/IfcCsgSolid.cpp b/src/ifcgeom/IfcCsgSolid.cpp new file mode 100644 index 0000000000..614bcb4e3d --- /dev/null +++ b/src/ifcgeom/IfcCsgSolid.cpp @@ -0,0 +1,95 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include +#include + +#define Kernel MAKE_TYPE_NAME(Kernel) + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcCsgSolid* l, TopoDS_Shape& shape) { + return convert_shape(l->TreeRootExpression(), shape); +} diff --git a/src/ifcgeom/IfcCurveBoundedPlane.cpp b/src/ifcgeom/IfcCurveBoundedPlane.cpp new file mode 100644 index 0000000000..c6858f6edc --- /dev/null +++ b/src/ifcgeom/IfcCurveBoundedPlane.cpp @@ -0,0 +1,130 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include +#include + +#define Kernel MAKE_TYPE_NAME(Kernel) + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcCurveBoundedPlane* l, TopoDS_Shape& face) { + gp_Pln pln; + if (!IfcGeom::Kernel::convert(l->BasisSurface(), pln)) { + return false; + } + + gp_Trsf trsf; + trsf.SetTransformation(pln.Position(), gp::XOY()); + + TopoDS_Wire outer; + if (!convert_wire(l->OuterBoundary(), outer)) { + return false; + } + + BRepBuilderAPI_MakeFace mf(outer); + + if (!mf.IsDone() || mf.Shape().IsNull()) { + Logger::Error("Invalid outer boundary:", l->OuterBoundary()); + return false; + } + + IfcSchema::IfcCurve::list::ptr boundaries = l->InnerBoundaries(); + + for (IfcSchema::IfcCurve::list::it it = boundaries->begin(); it != boundaries->end(); ++it) { + TopoDS_Wire inner; + if (convert_wire(*it, inner)) { + mf.Add(inner); + } + } + + ShapeFix_Shape sfs(mf.Face()); + sfs.Perform(); + + // `trsf` consitutes the placement of the plane and therefore has unit scale factor + face = TopoDS::Face(sfs.Shape()).Moved(trsf); + + return true; +} diff --git a/src/ifcgeom/IfcCylindricalSurface.cpp b/src/ifcgeom/IfcCylindricalSurface.cpp new file mode 100644 index 0000000000..8e1dc716a5 --- /dev/null +++ b/src/ifcgeom/IfcCylindricalSurface.cpp @@ -0,0 +1,104 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include +#include + +#define Kernel MAKE_TYPE_NAME(Kernel) + +#ifdef SCHEMA_HAS_IfcCylindricalSurface + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcCylindricalSurface* l, TopoDS_Shape& face) { + gp_Trsf trsf; + IfcGeom::Kernel::convert(l->Position(),trsf); + + // IfcElementarySurface.Position has unit scale factor + face = BRepBuilderAPI_MakeFace(new Geom_CylindricalSurface(gp::XOY(), l->Radius() * getValue(GV_LENGTH_UNIT)), getValue(GV_PRECISION)).Face().Moved(trsf); + return true; +} + +#endif diff --git a/src/ifcgeom/IfcDerivedProfileDef.cpp b/src/ifcgeom/IfcDerivedProfileDef.cpp new file mode 100644 index 0000000000..036e51b482 --- /dev/null +++ b/src/ifcgeom/IfcDerivedProfileDef.cpp @@ -0,0 +1,102 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include +#include +#include +#include + +#define Kernel MAKE_TYPE_NAME(Kernel) + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcDerivedProfileDef* l, TopoDS_Shape& face) { + TopoDS_Face f; + gp_Trsf2d trsf2d; + if (convert_face(l->ParentProfile(), f) && IfcGeom::Kernel::convert(l->Operator(), trsf2d)) { + gp_Trsf trsf = trsf2d; + face = BRepBuilderAPI_Transform(f, trsf).Shape(); + return true; + } else { + return false; + } +} diff --git a/src/ifcgeom/IfcDirection.cpp b/src/ifcgeom/IfcDirection.cpp new file mode 100644 index 0000000000..31fae2b16b --- /dev/null +++ b/src/ifcgeom/IfcDirection.cpp @@ -0,0 +1,78 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" + +#define Kernel MAKE_TYPE_NAME(Kernel) + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcDirection* l, gp_Dir& dir) { + IN_CACHE(IfcDirection,l,gp_Dir,dir) + std::vector 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; +} diff --git a/src/ifcgeom/IfcEdge.cpp b/src/ifcgeom/IfcEdge.cpp new file mode 100644 index 0000000000..a2be514d4a --- /dev/null +++ b/src/ifcgeom/IfcEdge.cpp @@ -0,0 +1,110 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include + +#define _USE_MATH_DEFINES +#define Kernel MAKE_TYPE_NAME(Kernel) + +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; +} diff --git a/src/ifcgeom/IfcEdgeCurve.cpp b/src/ifcgeom/IfcEdgeCurve.cpp new file mode 100644 index 0000000000..01fefa8d0b --- /dev/null +++ b/src/ifcgeom/IfcEdgeCurve.cpp @@ -0,0 +1,171 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include + +#include "../ifcgeom_schema_agnostic/wire_builder.h" + +#define _USE_MATH_DEFINES +#define Kernel MAKE_TYPE_NAME(Kernel) + +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)) { + TopoDS_Edge e; + if (util::create_edge_over_curve_with_log_messages(crv, getValue(GV_PRECISION), p1, p2, e)) { + mw.Add(e); + result = mw; + return true; + } else { + return false; + } + } 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; + } + + TopoDS_Edge e; + if (util::create_edge_over_curve_with_log_messages(ecrv, getValue(GV_PRECISION), a, b, e)) { + mw.Add(e); + } else { + return false; + } + + first = false; + } + result = mw; + return true; + } else { + return false; + } +} diff --git a/src/ifcgeom/IfcEdgeLoop.cpp b/src/ifcgeom/IfcEdgeLoop.cpp new file mode 100644 index 0000000000..30efd11aa3 --- /dev/null +++ b/src/ifcgeom/IfcEdgeLoop.cpp @@ -0,0 +1,99 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include + +#define _USE_MATH_DEFINES +#define Kernel MAKE_TYPE_NAME(Kernel) + +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; +} diff --git a/src/ifcgeom/IfcEllipse.cpp b/src/ifcgeom/IfcEllipse.cpp new file mode 100644 index 0000000000..1a9433c738 --- /dev/null +++ b/src/ifcgeom/IfcEllipse.cpp @@ -0,0 +1,101 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include + +#define Kernel MAKE_TYPE_NAME(Kernel) + +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->as()) { + IfcGeom::Kernel::convert(placement->as(), trsf); + } else { + gp_Trsf2d trsf2d; + IfcGeom::Kernel::convert(placement->as(), 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; +} diff --git a/src/ifcgeom/IfcEllipseProfileDef.cpp b/src/ifcgeom/IfcEllipseProfileDef.cpp new file mode 100644 index 0000000000..52805e8b99 --- /dev/null +++ b/src/ifcgeom/IfcEllipseProfileDef.cpp @@ -0,0 +1,128 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include +#include +#include +#include + +#define Kernel MAKE_TYPE_NAME(Kernel) + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcEllipseProfileDef* l, TopoDS_Shape& 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; + } + + const bool rotated = ry > rx; + + gp_Trsf2d trsf2d; + bool has_position = true; +#ifdef SCHEMA_IfcParameterizedProfileDef_Position_IS_OPTIONAL + has_position = l->Position() != nullptr; +#endif + if (has_position) { + IfcGeom::Kernel::convert(l->Position(), trsf2d); + } + + gp_Ax2 ax = gp_Ax2(); + if (rotated) { + ax.Rotate(ax.Axis(), M_PI / 2.); + std::swap(rx, ry); + } + ax.Transform(trsf2d); + + BRepBuilderAPI_MakeWire w; + Handle(Geom_Ellipse) ellipse = new Geom_Ellipse(ax, rx, ry); + TopoDS_Edge edge = BRepBuilderAPI_MakeEdge(ellipse); + w.Add(edge); + + TopoDS_Face f; + bool success = convert_wire_to_face(w, f); + if (success) face = f; + return success; +} diff --git a/src/ifcgeom/IfcExtrudedAreaSolid.cpp b/src/ifcgeom/IfcExtrudedAreaSolid.cpp new file mode 100644 index 0000000000..3f9f1f51e8 --- /dev/null +++ b/src/ifcgeom/IfcExtrudedAreaSolid.cpp @@ -0,0 +1,165 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include +#include + +// uncomment if you'd like negative or close to zero extrusion depths to succeed +// #define PERMISSIVE_EXTRUSION + +#define Kernel MAKE_TYPE_NAME(Kernel) + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcExtrudedAreaSolid* l, TopoDS_Shape& 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); +#ifndef PERMISSIVE_EXTRUSION + return false; +#endif + } + + TopoDS_Shape face; + if ( !convert_face(l->SweptArea(),face) ) return false; + +#ifdef PERMISSIVE_EXTRUSION + if (abs(height) < getValue(GV_PRECISION)) { + shape = face; + return true; + } +#endif + + gp_Trsf trsf; + bool has_position = true; +#ifdef SCHEMA_IfcSweptAreaSolid_Position_IS_OPTIONAL + has_position = l->Position() != nullptr; +#endif + if (has_position) { + IfcGeom::Kernel::convert(l->Position(), trsf); + } + + gp_Dir dir; + convert(l->ExtrudedDirection(),dir); + + shape.Nullify(); + + /* + // @nb This logic was probably flawed always as this does not by itself result in a valid compsolid... + 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 (has_position && !shape.IsNull()) { + // IfcSweptAreaSolid.Position (trsf) is an IfcAxis2Placement3D + // and therefore has a unit scale factor + shape.Move(trsf); + } + + return !shape.IsNull(); +} diff --git a/src/ifcgeom/IfcExtrudedAreaSolidTapered.cpp b/src/ifcgeom/IfcExtrudedAreaSolidTapered.cpp new file mode 100644 index 0000000000..b2729650ed --- /dev/null +++ b/src/ifcgeom/IfcExtrudedAreaSolidTapered.cpp @@ -0,0 +1,195 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include +#include + +#define Kernel MAKE_TYPE_NAME(Kernel) + +#ifdef SCHEMA_HAS_IfcExtrudedAreaSolidTapered +bool IfcGeom::Kernel::convert(const IfcSchema::IfcExtrudedAreaSolidTapered* l, TopoDS_Shape& 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; + } + + TopoDS_Shape face1, face2; + if (!convert_face(l->SweptArea(), face1)) return false; + if (!convert_face(l->EndSweptArea(), face2)) return false; + + gp_Trsf trsf; + bool has_position = true; +#ifdef SCHEMA_IfcSweptAreaSolid_Position_IS_OPTIONAL + has_position = l->Position() != nullptr; +#endif + if (has_position) { + IfcGeom::Kernel::convert(l->Position(), trsf); + } + + gp_Dir dir; + convert(l->ExtrudedDirection(), dir); + + gp_Trsf end_profile; + end_profile.SetTranslation(height * dir); + + TopoDS_Edge spine_edge = BRepBuilderAPI_MakeEdge(gp_Pnt(), gp_Pnt((height * dir).XYZ())).Edge(); + TopoDS_Wire wire = BRepBuilderAPI_MakeWire(spine_edge).Wire(); + + shape.Nullify(); + + TopExp_Explorer exp1(face1, TopAbs_WIRE); + TopExp_Explorer exp2(face2, TopAbs_WIRE); + + TopoDS_Vertex v1, v2; + TopExp::Vertices(wire, v1, v2); + + TopoDS_Shape shell; + TopoDS_Compound compound; + BRep_Builder compound_builder; + + for (; exp1.More() && exp2.More(); exp1.Next(), exp2.Next()) { + const TopoDS_Wire& w1 = TopoDS::Wire(exp1.Current()); + const TopoDS_Wire& w2 = TopoDS::Wire(exp2.Current()); + + BRepOffsetAPI_MakePipeShell builder(wire); + builder.Add(w1, v1); + builder.Add(w2.Moved(end_profile), v2); + + TopoDS_Shape result = builder.Shape(); + + TopTools_ListOfShape li; + shape_to_face_list(result, li); + li.Append(BRepBuilderAPI_MakeFace(w1).Face().Reversed()); + li.Append(BRepBuilderAPI_MakeFace(w2).Face().Moved(end_profile)); + + create_solid_from_faces(li, result, true); + + // @todo ugly hack + + // The reason for this distinction is that at this point of the loop we're not sure anymore + // whether this was constructed from an inner or outer bound. So rather than iterating over + // wires of `face1` and `face2` we should iterate over the faces and then properly check with + // BRepTools::OuterBound(). + // Currently this distinction happens based on profile type which is not robust and probably + // not complete. + if (shell.IsNull()) { + shell = result; + } else if (l->SweptArea()->declaration().is(IfcSchema::IfcCircleHollowProfileDef::Class()) || + l->SweptArea()->declaration().is(IfcSchema::IfcRectangleHollowProfileDef::Class()) || + l->SweptArea()->declaration().is(IfcSchema::IfcArbitraryProfileDefWithVoids::Class())) + { + // @todo properly check for failure and all. + shell = BRepAlgoAPI_Cut(shell, result).Shape(); + } else { + if (compound.IsNull()) { + compound_builder.MakeCompound(compound); + compound_builder.Add(compound, shell); + } + compound_builder.Add(compound, result); + } + } + + if (!compound.IsNull()) { + shell = compound; + } + + shape = shell; + + if (exp1.More() != exp2.More()) { + Logger::Message(Logger::LOG_ERROR, "Inconsistent profiles encountered for:", l); + } + + if (has_position && !shape.IsNull()) { + // IfcSweptAreaSolid.Position (trsf) is an IfcAxis2Placement3D + // and therefore has a unit scale factor + shape.Move(trsf); + } + + return !shape.IsNull(); +} +#endif diff --git a/src/ifcgeom/IfcFace.cpp b/src/ifcgeom/IfcFace.cpp new file mode 100644 index 0000000000..959132fe6b --- /dev/null +++ b/src/ifcgeom/IfcFace.cpp @@ -0,0 +1,359 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include +#include +#include +#include + +#include "../ifcgeom_schema_agnostic/face_definition.h" + +#define Kernel MAKE_TYPE_NAME(Kernel) + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcFace* l, TopoDS_Shape& result) { + IfcSchema::IfcFaceBound::list::ptr bounds = l->Bounds(); + + util::face_definition fd; + + const bool is_face_surface = l->declaration().is(IfcSchema::IfcFaceSurface::Class()); + + 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 (IfcSchema::IfcFaceBound::list::it it = bounds->begin(); it != bounds->end(); ++it) { + IfcSchema::IfcFaceBound* bound = *it; + if (bound->declaration().is(IfcSchema::IfcFaceOuterBound::Class())) 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:", l); + return false; + } + + if (num_outer_bounds > 1) { + Logger::Message(Logger::LOG_WARNING, "Multiple outer boundaries for:", l); + fd.all_outer() = true; + } + + TopTools_DataMapOfShapeInteger wire_senses; + + for (int process_interior = 0; process_interior <= 1; ++process_interior) { + for (IfcSchema::IfcFaceBound::list::it it = bounds->begin(); it != bounds->end(); ++it) { + IfcSchema::IfcFaceBound* bound = *it; + IfcSchema::IfcLoop* loop = bound->Bound(); + + bool same_sense = bound->Orientation(); + const bool is_interior = + !bound->declaration().is(IfcSchema::IfcFaceOuterBound::Class()) && + (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_ && loop->as()) { + if (!faceset_helper_->wire(loop->as(), wire)) { + Logger::Message(Logger::LOG_WARNING, "Face boundary loop not included", loop); + continue; + } + } else if (!convert_wire(loop, wire)) { + Logger::Message(Logger::LOG_ERROR, "Failed to process face boundary loop", loop); + 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", l); + 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 (util::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, getValue(GV_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", l); + + 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()); + TopoDS_Face f = mf.Face(); + + if (mf.IsDone()) { + if (std::distance(fd.inner_wires().first, fd.inner_wires().second)) { + 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 { + face_list.Append(f); + } + } + } 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()) { + ShapeFix_Shape sfs(it.Value()); + + Handle(ShapeExtend_MsgRegistrator) msg; + msg = new ShapeExtend_MsgRegistrator; + sfs.SetMsgRegistrator(msg); + + sfs.Perform(); + it.Value() = sfs.Shape(); + + ShapeExtend_DataMapIteratorOfDataMapOfShapeListOfMsg jt(msg->MapShape()); + for (; jt.More(); jt.Next()) { + Message_ListIteratorOfListOfMsg kt(jt.Value()); + for (; kt.More(); kt.Next()) { + char* c = new char[kt.Value().Value().LengthOfCString() + 1]; + kt.Value().Value().ToUTF8CString(c); + Logger::Notice(c, l); + delete[] c; + } + } + } + } + + for (TopTools_ListIteratorOfListOfShape it(face_list); it.More(); it.Next()) { + const TopoDS_Face& face = TopoDS::Face(it.Value()); + + ShapeFix_Face sfs(TopoDS::Face(face)); + TopTools_DataMapOfShapeListOfShape wire_map; + sfs.FixOrientation(wire_map); + + TopoDS_Iterator jt(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& face = TopoDS::Face(it.Value()); + + bool all_reversed = true; + TopoDS_Iterator jt(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) { + 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& face = TopoDS::Face(it.Value()); + builder.Add(compound, face); + } + result = compound; + } else { + result = face_list.First(); + } + + return true; +} diff --git a/src/ifcgeom/IfcFaceBasedSurfaceModel.cpp b/src/ifcgeom/IfcFaceBasedSurfaceModel.cpp new file mode 100644 index 0000000000..71ae4d2068 --- /dev/null +++ b/src/ifcgeom/IfcFaceBasedSurfaceModel.cpp @@ -0,0 +1,106 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include +#include + +#define Kernel MAKE_TYPE_NAME(Kernel) + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcFaceBasedSurfaceModel* l, IfcRepresentationShapeItems& shapes) { + bool part_success = false; + IfcSchema::IfcConnectedFaceSet::list::ptr facesets = l->FbsmFaces(); + auto collective_style = get_style(l); + for( IfcSchema::IfcConnectedFaceSet::list::it it = facesets->begin(); it != facesets->end(); ++ it ) { + TopoDS_Shape s; + auto shell_style = get_style(*it); + if (convert_shape(*it,s)) { + shapes.push_back(IfcRepresentationShapeItem(l->data().id(), s, shell_style ? shell_style : collective_style)); + part_success |= true; + } + } + return part_success; +} diff --git a/src/ifcgeom/IfcGeomFunctions.cpp b/src/ifcgeom/IfcGeom.cpp similarity index 87% rename from src/ifcgeom/IfcGeomFunctions.cpp rename to src/ifcgeom/IfcGeom.cpp index 119075745b..ecd901eb1e 100644 --- a/src/ifcgeom/IfcGeomFunctions.cpp +++ b/src/ifcgeom/IfcGeom.cpp @@ -164,6 +164,7 @@ #include "../ifcparse/IfcFile.h" #include "../ifcgeom/IfcGeom.h" #include "../ifcgeom/IfcGeomTree.h" +#include "../ifcgeom_schema_agnostic/boolean_utils.h" #include #include @@ -239,578 +240,7 @@ void MAKE_INIT_FN(KernelImplementation_)(IfcGeom::impl::KernelFactoryImplementat #define Kernel MAKE_TYPE_NAME(Kernel) namespace { - void copy_operand(const TopTools_ListOfShape& l, TopTools_ListOfShape& r) { -#if OCC_VERSION_HEX < 0x70000 - TopTools_ListIteratorOfListOfShape it(l); - for (; it.More(); it.Next()) { - r.Append(BRepBuilderAPI_Copy(it.Value())); - } -#else - // On OCCT 7.0 and higher BRepAlgoAPI_BuilderAlgo::SetNonDestructive(true) is - // called. Not entirely sure on the behaviour before 7.0, so overcautiously - // create copies. - r.Assign(l); -#endif - } - - TopoDS_Shape copy_operand(const TopoDS_Shape& s) { -#if OCC_VERSION_HEX < 0x70000 - return BRepBuilderAPI_Copy(s); -#else - return s; -#endif - } - - double min_edge_length(const TopoDS_Shape& a) { - double min_edge_len = std::numeric_limits::infinity(); - TopExp_Explorer exp(a, TopAbs_EDGE); - for (; exp.More(); exp.Next()) { - GProp_GProps prop; - BRepGProp::LinearProperties(exp.Current(), prop); - double l = prop.Mass(); - if (l < min_edge_len) { - min_edge_len = l; - } - } - return min_edge_len; - } - - double min_vertex_edge_distance(const TopoDS_Shape& a, double min_search, double max_search) { - double M = std::numeric_limits::infinity(); - - TopTools_IndexedMapOfShape vertices, edges; - - TopExp::MapShapes(a, TopAbs_VERTEX, vertices); - TopExp::MapShapes(a, TopAbs_EDGE, edges); - - IfcGeom::impl::tree 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 edge_idxs = tree.select_box(b, false); - std::vector::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; - } - - 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() { - i = j = (int)inset_; - } - - bool operator()(gp_Pnt& p) { - while (j < N) { - double u = u0 + (u1 - u0) * i / N; - double v = v0 + (v1 - v0) * j / N; - - i++; - if (i == N) { - i = 0; - j++; - } - - // Specifically does not consider ON - if (cls_.Perform(gp_Pnt2d(u, v)) == TopAbs_IN) { - plane_->D0(u, v, p); - return true; - } - } - - return false; - } - }; - - bool faces_overlap(const TopoDS_Face& f, const TopoDS_Face& g) { - points_on_planar_face_generator pgen(f); - - BRep_Builder B; - gp_Pnt test; - double eps = BRep_Tool::Tolerance(f) + BRep_Tool::Tolerance(g); - - BRepExtrema_DistShapeShape x; - x.LoadS1(g); - - while (pgen(test)) { - TopoDS_Vertex V; - B.MakeVertex(V, test, Precision::Confusion()); - x.LoadS2(V); - x.Perform(); - if (x.IsDone() && x.NbSolution() == 1) { - if (x.Value() > eps) { - return false; - } - } - } - - return true; - } - - double min_face_face_distance(const TopoDS_Shape& a, double max_search) { - /* - NB: This is currently only implemented for planar surfaces. - */ - double M = std::numeric_limits::infinity(); - - TopTools_IndexedMapOfShape faces; - - TopExp::MapShapes(a, TopAbs_FACE, faces); - - IfcGeom::impl::tree 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 face_idxs = tree.select_box(b, false); - std::vector::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 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 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 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 is_extrusion(const gp_Vec& v, const TopoDS_Shape& s, TopoDS_Face& base, std::pair& 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 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); - - IfcGeom::impl::tree 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 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(); - } - - gp_Trsf combine_offset_and_rotation(const gp_Vec &offset, const gp_Quaternion& rotation) { - auto offset_transform = gp_Trsf{}; - offset_transform.SetTranslation(offset); - - auto rotation_transform = gp_Trsf{}; - rotation_transform.SetRotation(rotation); - - return rotation_transform * offset_transform; - } + } void IfcGeom::Kernel::set_offset(const std::array &p_offset) { @@ -1366,7 +796,7 @@ bool IfcGeom::Kernel::convert_openings_fast(const IfcSchema::IfcProduct* entity, gp_GTrsf gtrsf = opening_shapes[i].Placement(); gtrsf.PreMultiply(opening_trsf); TopoDS_Shape opening_shape = apply_transformation(opening_shape_unlocated, gtrsf); - opening_vector.push_back(std::make_pair(min_edge_length(opening_shape), opening_shape)); + opening_vector.push_back(std::make_pair(util::min_edge_length(opening_shape), opening_shape)); } } @@ -4275,7 +3705,7 @@ bool IfcGeom::Kernel::wire_intersections(const TopoDS_Wire& wire, TopTools_ListO // eps is added to both ends of the parametric domain, so 3. is chosen to be on the safe side here. ? (faceset_helper_->epsilon() / 3.) // @todo re-evaluate 2. here for the reasons above: - : (std::min)(min_edge_length(wire) / 2., getValue(GV_PRECISION) * 10.); + : (std::min)(util::min_edge_length(wire) / 2., getValue(GV_PRECISION) * 10.); } // @todo: should this start from 0 in case of n > 64? @@ -4599,225 +4029,6 @@ bool IfcGeom::Kernel::boolean_operation(const TopoDS_Shape& a, const TopoDS_Shap } #else -namespace { - - bool boolean_subtraction_2d_using_builder(const TopoDS_Shape& a_input, const TopTools_ListOfShape& b_input, TopoDS_Shape& result, double eps) { - IfcGeom::impl::tree 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 edge_index_to_shape_index; - - std::vector shapes; - std::vector> edges; - // First is the outer wire - std::vector 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()) }); - } - } - - 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 redundant(wires.size(), false); - - std::vector wire_faces; - wire_faces.reserve(wires.size()); - - std::vector wire_clss; - wire_clss.reserve(wires.size()); - - std::vector> 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(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 - IfcGeom::impl::tree 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 assymetry 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; - } -} - bool IfcGeom::Kernel::boolean_operation(const TopoDS_Shape& a_input, const TopTools_ListOfShape& b_input, BOPAlgo_Operation op, TopoDS_Shape& result, double fuzziness) { using namespace std::string_literals; @@ -4847,7 +4058,7 @@ bool IfcGeom::Kernel::boolean_operation(const TopoDS_Shape& a_input, const TopTo if (do_unify) { PERF("boolean operation: unifying operands"); - a = unify(a_input, fuzziness * 1000.); + a = util::unify(a_input, fuzziness * 1000.); Logger::Notice( "Simplified operand A from "s + @@ -4859,7 +4070,7 @@ bool IfcGeom::Kernel::boolean_operation(const TopoDS_Shape& a_input, const TopTo { TopTools_ListIteratorOfListOfShape it(b_input); for (; it.More(); it.Next()) { - b.Append(unify(it.Value(), fuzziness)); + b.Append(util::unify(it.Value(), fuzziness)); Logger::Notice( "Simplified operand B from "s + std::to_string(count(it.Value(), TopAbs_FACE)) + @@ -4883,7 +4094,7 @@ bool IfcGeom::Kernel::boolean_operation(const TopoDS_Shape& a_input, const TopTo if (do_subtraction_eliminate_disjoint_bbox) { PERF("boolean subtraction: eliminate disjoint bbox"); - auto N = bounding_box_overlap(fuzziness, a, b, b_tmp); + auto N = util::bounding_box_overlap(fuzziness, a, b, b_tmp); if (N) { Logger::Notice("Eliminated " + std::to_string(N) + " disjoint operands"); std::swap(b, b_tmp); @@ -4894,7 +4105,7 @@ bool IfcGeom::Kernel::boolean_operation(const TopoDS_Shape& a_input, const TopTo PERF("boolean subtraction: eliminate touching"); b_tmp.Clear(); - auto N = eliminate_touching_operands(fuzziness, a, b, b_tmp); + auto N = util::eliminate_touching_operands(fuzziness, a, b, b_tmp); if (N) { Logger::Notice("Eliminated " + std::to_string(N) + " touching operands"); std::swap(b, b_tmp); @@ -4922,10 +4133,10 @@ bool IfcGeom::Kernel::boolean_operation(const TopoDS_Shape& a_input, const TopTo { PERF("boolean operation: min edge length"); - min_length_orig = min_edge_length(a); + min_length_orig = util::min_edge_length(a); TopTools_ListIteratorOfListOfShape it(b); for (; it.More(); it.Next()) { - double d = min_edge_length(it.Value()); + double d = util::min_edge_length(it.Value()); if (d < min_length_orig) { min_length_orig = d; } @@ -4935,14 +4146,14 @@ bool IfcGeom::Kernel::boolean_operation(const TopoDS_Shape& a_input, const TopTo { PERF("boolean operation: min vertex-edge dist"); - double d = min_vertex_edge_distance(a, getValue(GV_PRECISION), min_length_orig); + double d = util::min_vertex_edge_distance(a, getValue(GV_PRECISION), min_length_orig); if (d < min_length_orig) { min_length_orig = d; } TopTools_ListIteratorOfListOfShape it(b); for (; it.More(); it.Next()) { - d = min_vertex_edge_distance(it.Value(), getValue(GV_PRECISION), min_length_orig); + d = util::min_vertex_edge_distance(it.Value(), getValue(GV_PRECISION), min_length_orig); if (d < min_length_orig) { min_length_orig = d; } @@ -4954,7 +4165,7 @@ bool IfcGeom::Kernel::boolean_operation(const TopoDS_Shape& a_input, const TopTo Logger::Notice("Used fuzziness: " + std::to_string(fuzz)); TopTools_ListOfShape s1s; - s1s.Append(copy_operand(a)); + s1s.Append(util::copy_operand(a)); if (debug) { TopTools_ListOfShape* lists[2] = { &s1s, &b }; @@ -4978,7 +4189,7 @@ bool IfcGeom::Kernel::boolean_operation(const TopoDS_Shape& a_input, const TopTo if (do_attempt_2d_boolean) { PERF("boolean subtraction: extrusion check"); - is_extrusion_a = is_extrusion(gp::DY(), a, a_face, a_interval); + is_extrusion_a = util::is_extrusion(gp::DY(), a, a_face, a_interval); } if (is_extrusion_a) { @@ -4994,7 +4205,7 @@ bool IfcGeom::Kernel::boolean_operation(const TopoDS_Shape& a_input, const TopTo { PERF("boolean subtraction: extrusion check"); - is_extrusion_b = is_extrusion(gp::DY(), it.Value(), b_face, b_interval); + is_extrusion_b = util::is_extrusion(gp::DY(), it.Value(), b_face, b_interval); } if (is_extrusion_b) { @@ -5025,7 +4236,7 @@ bool IfcGeom::Kernel::boolean_operation(const TopoDS_Shape& a_input, const TopTo PERF("boolean operation: 2d builder"); // First try using face builder - boolean_op_2d_success = boolean_subtraction_2d_using_builder(a_face, b_faces, face_result, fuzziness); + boolean_op_2d_success = util::boolean_subtraction_2d_using_builder(a_face, b_faces, face_result, fuzziness); } if (!boolean_op_2d_success) { @@ -5067,7 +4278,7 @@ bool IfcGeom::Kernel::boolean_operation(const TopoDS_Shape& a_input, const TopTo #endif builder->SetFuzzyValue(fuzz); builder->SetArguments(s1s); - copy_operand(b, b_tmp); + util::copy_operand(b, b_tmp); std::swap(b, b_tmp); builder->SetTools(b); { @@ -5183,7 +4394,7 @@ bool IfcGeom::Kernel::boolean_operation(const TopoDS_Shape& a_input, const TopTo auto& fi = it4.Value(); for (TopTools_ListIteratorOfListOfShape it2(faces_j); it2.More(); it2.Next()) { auto& fj = it2.Value(); - if (faces_overlap(TopoDS::Face(fi), TopoDS::Face(fj))) { + if (util::faces_overlap(TopoDS::Face(fi), TopoDS::Face(fj))) { overlap = true; } } @@ -5262,7 +4473,7 @@ bool IfcGeom::Kernel::boolean_operation(const TopoDS_Shape& a_input, const TopTo { PERF("boolean operation: result min edge length check"); - if ((v = min_edge_length(r)) < fuzziness * 3.) { + if ((v = util::min_edge_length(r)) < fuzziness * 3.) { reason = 0; success = false; @@ -5273,7 +4484,7 @@ bool IfcGeom::Kernel::boolean_operation(const TopoDS_Shape& a_input, const TopTo { PERF("boolean operation: result min vertex-edge dist check"); - if ((v = min_vertex_edge_distance(r, getValue(GV_PRECISION), fuzziness * 3.)) < fuzziness * 3.) { + if ((v = util::min_vertex_edge_distance(r, getValue(GV_PRECISION), fuzziness * 3.)) < fuzziness * 3.) { reason = 1; success = false; @@ -5284,9 +4495,9 @@ bool IfcGeom::Kernel::boolean_operation(const TopoDS_Shape& a_input, const TopTo { PERF("boolean operation: result min face-face dist check"); - if ((v = min_face_face_distance(r, 1.e-4)) < 1.e-4) { + if ((v = util::min_face_face_distance(r, 1.e-4)) < 1.e-4) { // #2095 Check if this distance wasn't already realized in the input first operand. - if (v < min_face_face_distance(a, 1.e-4)) { + if (v < util::min_face_face_distance(a, 1.e-4)) { reason = 2; success = false; } @@ -5706,3 +4917,11 @@ IfcGeom::Kernel::faceset_helper::faceset_helper( template class IfcGeom::Kernel::faceset_helper; template class IfcGeom::Kernel::faceset_helper, std::vector>; + +void IfcGeom::Kernel::set_conversion_placement_rel_to_type(const IfcParse::declaration* type) { + placement_rel_to_type_ = type; +} + +void IfcGeom::Kernel::set_conversion_placement_rel_to_instance(const IfcUtil::IfcBaseEntity* instance) { + placement_rel_to_instance_ = instance; +} diff --git a/src/ifcgeom/IfcGeom.h b/src/ifcgeom/IfcGeom.h index 69a04f1339..99f6a87c13 100644 --- a/src/ifcgeom/IfcGeom.h +++ b/src/ifcgeom/IfcGeom.h @@ -87,24 +87,7 @@ if ( it != cache.T.end() ) { e = it->second; return true; } #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 "mapping_cache.i" diff --git a/src/ifcgeom/IfcGeomFaces.cpp b/src/ifcgeom/IfcGeomFaces.cpp deleted file mode 100644 index 7d2d339282..0000000000 --- a/src/ifcgeom/IfcGeomFaces.cpp +++ /dev/null @@ -1,1284 +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 . * - * * - ********************************************************************************/ - -/******************************************************************************** - * * - * Implementations of the various conversion functions defined in mapping.i * - * * - ********************************************************************************/ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include - -#include -#include - -#include - -#include -#include -#include - -#include - -#include -#include - -#include - -#include -#include -#include -#include -#include - -#include "../ifcgeom/IfcGeom.h" - -#ifdef SCHEMA_HAS_IfcBSplineSurfaceWithKnots -#include -#include -#include -#include -#endif - -#define Kernel MAKE_TYPE_NAME(Kernel) - -namespace { - /* Returns whether wire conforms to a polyhedron, i.e. only edges with linear curves*/ - bool 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; - } - - /* A temporary structure to store the intermediate data for the face conversion */ - class face_definition { - private: - Handle(Geom_Surface) surface_; - std::vector wires_; - bool all_outer_; - public: - face_definition() : surface_(), all_outer_(false) {} - - typedef std::vector::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& wires() { - return wires_; - } - - const TopoDS_Wire& outer_wire() const { - return wires_.front(); - } - - std::pair inner_wires() const { - return { wires_.begin() + 1, wires_.end() }; - } - }; -} - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcFace* l, TopoDS_Shape& result) { - IfcSchema::IfcFaceBound::list::ptr bounds = l->Bounds(); - - face_definition fd; - - const bool is_face_surface = l->declaration().is(IfcSchema::IfcFaceSurface::Class()); - - 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 (IfcSchema::IfcFaceBound::list::it it = bounds->begin(); it != bounds->end(); ++it) { - IfcSchema::IfcFaceBound* bound = *it; - if (bound->declaration().is(IfcSchema::IfcFaceOuterBound::Class())) 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:", l); - return false; - } - - if (num_outer_bounds > 1) { - Logger::Message(Logger::LOG_WARNING, "Multiple outer boundaries for:", l); - fd.all_outer() = true; - } - - TopTools_DataMapOfShapeInteger wire_senses; - - for (int process_interior = 0; process_interior <= 1; ++process_interior) { - for (IfcSchema::IfcFaceBound::list::it it = bounds->begin(); it != bounds->end(); ++it) { - IfcSchema::IfcFaceBound* bound = *it; - IfcSchema::IfcLoop* loop = bound->Bound(); - - bool same_sense = bound->Orientation(); - const bool is_interior = - !bound->declaration().is(IfcSchema::IfcFaceOuterBound::Class()) && - (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_ && loop->as()) { - if (!faceset_helper_->wire(loop->as(), wire)) { - Logger::Message(Logger::LOG_WARNING, "Face boundary loop not included", loop); - continue; - } - } else if (!convert_wire(loop, wire)) { - Logger::Message(Logger::LOG_ERROR, "Failed to process face boundary loop", loop); - 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", l); - 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, getValue(GV_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", l); - - 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()); - TopoDS_Face f = mf.Face(); - - if (mf.IsDone()) { - if (std::distance(fd.inner_wires().first, fd.inner_wires().second)) { - 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 { - face_list.Append(f); - } - } - } 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()) { - ShapeFix_Shape sfs(it.Value()); - - Handle(ShapeExtend_MsgRegistrator) msg; - msg = new ShapeExtend_MsgRegistrator; - sfs.SetMsgRegistrator(msg); - - sfs.Perform(); - it.Value() = sfs.Shape(); - - ShapeExtend_DataMapIteratorOfDataMapOfShapeListOfMsg jt(msg->MapShape()); - for (; jt.More(); jt.Next()) { - Message_ListIteratorOfListOfMsg kt(jt.Value()); - for (; kt.More(); kt.Next()) { - char* c = new char[kt.Value().Value().LengthOfCString() + 1]; - kt.Value().Value().ToUTF8CString(c); - Logger::Notice(c, l); - delete[] c; - } - } - } - } - - for (TopTools_ListIteratorOfListOfShape it(face_list); it.More(); it.Next()) { - const TopoDS_Face& face = TopoDS::Face(it.Value()); - - ShapeFix_Face sfs(TopoDS::Face(face)); - TopTools_DataMapOfShapeListOfShape wire_map; - sfs.FixOrientation(wire_map); - - TopoDS_Iterator jt(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& face = TopoDS::Face(it.Value()); - - bool all_reversed = true; - TopoDS_Iterator jt(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) { - 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& face = TopoDS::Face(it.Value()); - builder.Add(compound, face); - } - result = compound; - } else { - result = face_list.First(); - } - - return true; -} - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcArbitraryClosedProfileDef* l, TopoDS_Shape& face) { - TopoDS_Wire wire; - if (!convert_wire(l->OuterCurve(), wire)) { - return false; - } - - assert_closed_wire(wire); - - TopoDS_Compound f; - bool success = convert_wire_to_faces(wire, f); - if (success) { - face = f; - } - return success; -} - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcAnnotationFillArea* l, TopoDS_Shape& face) { - TopoDS_Wire outer_boundary; - if (!convert_wire(l->OuterBoundary(), outer_boundary)) { - return false; - } - - assert_closed_wire(outer_boundary); - - BRepBuilderAPI_MakeFace mf(outer_boundary); - - if (l->InnerBoundaries()) { - IfcSchema::IfcCurve::list::ptr inner_boundaries = *l->InnerBoundaries(); - - for(IfcSchema::IfcCurve::list::it it = inner_boundaries->begin(); it != inner_boundaries->end(); ++it) { - TopoDS_Wire hole; - if (convert_wire(*it, hole)) { - assert_closed_wire(hole); - mf.Add(hole); - } - } - } - - ShapeFix_Shape sfs(mf.Face()); - sfs.Perform(); - face = sfs.Shape(); - - return true; -} - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcArbitraryProfileDefWithVoids* l, TopoDS_Shape& face) { - TopoDS_Wire profile; - if (!convert_wire(l->OuterCurve(), profile)) { - return false; - } - - assert_closed_wire(profile); - - BRepBuilderAPI_MakeFace mf(profile); - - IfcSchema::IfcCurve::list::ptr voids = l->InnerCurves(); - - for(IfcSchema::IfcCurve::list::it it = voids->begin(); it != voids->end(); ++it) { - TopoDS_Wire hole; - if (convert_wire(*it, hole)) { - assert_closed_wire(hole); - mf.Add(hole); - } - } - - ShapeFix_Shape sfs(mf.Face()); - sfs.Perform(); - face = sfs.Shape(); - - return true; -} - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcRectangleProfileDef* l, TopoDS_Shape& 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; - } - - gp_Trsf2d trsf2d; - bool has_position = true; -#ifdef SCHEMA_IfcParameterizedProfileDef_Position_IS_OPTIONAL - has_position = l->Position() != nullptr; -#endif - if (has_position) { - IfcGeom::Kernel::convert(l->Position(), trsf2d); - } - - double coords[8] = {-x,-y,x,-y,x,y,-x,y}; - return profile_helper(4,coords,0,0,0,trsf2d,face); -} - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcRoundedRectangleProfileDef* l, TopoDS_Shape& 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; - } - - gp_Trsf2d trsf2d; - bool has_position = true; -#ifdef SCHEMA_IfcParameterizedProfileDef_Position_IS_OPTIONAL - has_position = l->Position() != nullptr; -#endif - if (has_position) { - IfcGeom::Kernel::convert(l->Position(), trsf2d); - } - - double coords[8] = {-x,-y, x,-y, x,y, -x,y}; - int fillets[4] = {0,1,2,3}; - double radii[4] = {r,r,r,r}; - return profile_helper(4,coords,4,fillets,radii,trsf2d,face); -} - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcRectangleHollowProfileDef* l, TopoDS_Shape& 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->OuterFilletRadius(); - const bool fr2 = !!l->InnerFilletRadius(); - - 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; - } - - TopoDS_Face f1; - TopoDS_Face f2; - - gp_Trsf2d trsf2d; - bool has_position = true; -#ifdef SCHEMA_IfcParameterizedProfileDef_Position_IS_OPTIONAL - has_position = l->Position() != nullptr; -#endif - if (has_position) { - IfcGeom::Kernel::convert(l->Position(), trsf2d); - } - - double coords1[8] = {-x ,-y, x ,-y, x, y, -x, y }; - double coords2[8] = {-x+d,-y+d, x-d,-y+d, x-d,y-d, -x+d,y-d}; - double radii1[4] = {r1,r1,r1,r1}; - double radii2[4] = {r2,r2,r2,r2}; - int fillets[4] = {0,1,2,3}; - - bool s1 = profile_helper(4,coords1,fr1 ? 4 : 0,fillets,radii1,trsf2d,f1); - bool s2 = profile_helper(4,coords2,fr2 ? 4 : 0,fillets,radii2,trsf2d,f2); - - if (!s1 || !s2) return false; - - TopExp_Explorer exp1(f1, TopAbs_WIRE); - TopExp_Explorer exp2(f2, TopAbs_WIRE); - - TopoDS_Wire w1 = TopoDS::Wire(exp1.Current()); - TopoDS_Wire w2 = TopoDS::Wire(exp2.Current()); - - BRepBuilderAPI_MakeFace mf(w1, false); - mf.Add(w2); - - ShapeFix_Shape sfs(mf.Face()); - sfs.Perform(); - face = TopoDS::Face(sfs.Shape()); - return true; -} - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcTrapeziumProfileDef* l, TopoDS_Shape& face) { - const double x1 = l->BottomXDim() / 2. * 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. * getValue(GV_LENGTH_UNIT); - - // See: https://forums.buildingsmart.org/t/how-are-the-sides-of-ifctrapeziumprofiledefs-bounding-box-calculated-in-most-implementations/2945/8 - // The trapezium x center should not be midway of BottomXDim but rather at the center of the overall bounding box. - const double x_offset = ((std::min(dx, 0.) + std::max(w + dx, x1 * 2.)) / 2.) - x1; - - if ( x1 < ALMOST_ZERO || w < ALMOST_ZERO || y < ALMOST_ZERO ) { - Logger::Message(Logger::LOG_NOTICE,"Skipping zero sized profile:",l); - return false; - } - - gp_Trsf2d trsf2d; - bool has_position = true; -#ifdef SCHEMA_IfcParameterizedProfileDef_Position_IS_OPTIONAL - has_position = l->Position() != nullptr; -#endif - if (has_position) { - IfcGeom::Kernel::convert(l->Position(), trsf2d); - } - - double coords[8] = { - -x1 - x_offset, -y, - +x1 - x_offset, -y, - -x1 + dx + w - x_offset, y, - -x1 + dx - x_offset,y - }; - return profile_helper(4,coords,0,0,0,trsf2d,face); -} - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcIShapeProfileDef* l, TopoDS_Shape& 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->FilletRadius(); - double f1 = 0.; - if ( doFillet1 ) { - f1 = *l->FilletRadius() * getValue(GV_LENGTH_UNIT); - } - - bool doFillet2 = doFillet1; - double x2 = x1, dy2 = dy1, f2 = f1; - - // @todo in IFC4 a IfcAsymmetricIShapeProfileDef is not a subtype anymore of IfcIShapeProfileDef! - if (l->declaration().is(IfcSchema::IfcAsymmetricIShapeProfileDef::Class())) { - IfcSchema::IfcAsymmetricIShapeProfileDef* assym = (IfcSchema::IfcAsymmetricIShapeProfileDef*) l; - x2 = assym->TopFlangeWidth() / 2. * getValue(GV_LENGTH_UNIT); - doFillet2 = !!assym->TopFlangeFilletRadius(); - if (doFillet2) { - f2 = *assym->TopFlangeFilletRadius() * getValue(GV_LENGTH_UNIT); - } - if (assym->TopFlangeThickness()) { - 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; - } - - gp_Trsf2d trsf2d; - bool has_position = true; -#ifdef SCHEMA_IfcParameterizedProfileDef_Position_IS_OPTIONAL - has_position = l->Position() != nullptr; -#endif - if (has_position) { - IfcGeom::Kernel::convert(l->Position(), trsf2d); - } - - double coords[24] = {-x1,-y, x1,-y, x1,-y+dy1, d1,-y+dy1, d1,y-dy2, x2,y-dy2, x2,y, -x2,y, -x2,y-dy2, -d1,y-dy2, -d1,-y+dy1, -x1,-y+dy1}; - int fillets[4] = {3,4,9,10}; - double radii[4] = {f1,f2,f2,f1}; - return profile_helper(12,coords,(doFillet1||doFillet2) ? 4 : 0,fillets,radii,trsf2d,face); -} - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcZShapeProfileDef* l, TopoDS_Shape& 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->FilletRadius(); - bool doEdgeFillet = !!l->EdgeRadius(); - - 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; - } - - gp_Trsf2d trsf2d; - bool has_position = true; -#ifdef SCHEMA_IfcParameterizedProfileDef_Position_IS_OPTIONAL - has_position = l->Position() != nullptr; -#endif - if (has_position) { - IfcGeom::Kernel::convert(l->Position(), trsf2d); - } - - double coords[16] = {-dx,-y, x,-y, x,-y+dy, dx,-y+dy, dx,y, -x,y, -x,y-dy, -dx,y-dy}; - int fillets[4] = {2,3,6,7}; - double radii[4] = {f2,f1,f2,f1}; - return profile_helper(8,coords,(doFillet || doEdgeFillet) ? 4 : 0,fillets,radii,trsf2d,face); -} - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcCShapeProfileDef* l, TopoDS_Shape& 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->InternalFilletRadius(); - 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; - } - - gp_Trsf2d trsf2d; - bool has_position = true; -#ifdef SCHEMA_IfcParameterizedProfileDef_Position_IS_OPTIONAL - has_position = l->Position() != nullptr; -#endif - if (has_position) { - IfcGeom::Kernel::convert(l->Position(), trsf2d); - } - - double coords[24] = {-x,-y,x,-y,x,-y+d2,x-d1,-y+d2,x-d1,-y+d1,-x+d1,-y+d1,-x+d1,y-d1,x-d1,y-d1,x-d1,y-d2,x,y-d2,x,y,-x,y}; - int fillets[8] = {0,1,4,5,6,7,10,11}; - double radii[8] = {f2,f2,f1,f1,f1,f1,f2,f2}; - return profile_helper(12,coords,doFillet ? 8 : 0,fillets,radii,trsf2d,face); -} - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcLShapeProfileDef* l, TopoDS_Shape& face) { - const bool hasSlope = !!l->LegSlope(); - const bool doEdgeFillet = !!l->EdgeRadius(); - const bool doFillet = !!l->FilletRadius(); - - const double y = l->Depth() / 2.0f * getValue(GV_LENGTH_UNIT); - const double x = l->Width().get_value_or(l->Depth()) / 2.0f * getValue(GV_LENGTH_UNIT); - const double d = l->Thickness() * getValue(GV_LENGTH_UNIT); - const double slope = l->LegSlope().get_value_or(0.) * getValue(GV_PLANEANGLE_UNIT); - - 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; - } - - gp_Trsf2d trsf2d; - bool has_position = true; -#ifdef SCHEMA_IfcParameterizedProfileDef_Position_IS_OPTIONAL - has_position = l->Position() != nullptr; -#endif - if (has_position) { - IfcGeom::Kernel::convert(l->Position(), trsf2d); - } - - double coords[12] = {-x,-y, x,-y, x,-y+d-dy1, xx, xy, -x+d-dx1,y, -x,y}; - int fillets[3] = {2,3,4}; - double radii[3] = {f2,f1,f2}; - return profile_helper(6,coords,doFillet ? 3 : 0,fillets,radii,trsf2d,face); -} - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcUShapeProfileDef* l, TopoDS_Shape& face) { - const bool doEdgeFillet = !!l->EdgeRadius(); - const bool doFillet = !!l->FilletRadius(); - const bool hasSlope = !!l->FlangeSlope(); - - 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 = l->FlangeSlope().get_value_or(0.) * getValue(GV_PLANEANGLE_UNIT); - - 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; - } - - gp_Trsf2d trsf2d; - bool has_position = true; -#ifdef SCHEMA_IfcParameterizedProfileDef_Position_IS_OPTIONAL - has_position = l->Position() != nullptr; -#endif - if (has_position) { - IfcGeom::Kernel::convert(l->Position(), trsf2d); - } - - double coords[16] = {-x,-y, x,-y, x,-y+d2-dy2, -x+d1,-y+d2+dy1, -x+d1,y-d2-dy1, x,y-d2+dy2, x,y, -x,y}; - int fillets[4] = {2,3,4,5}; - double radii[4] = {f2,f1,f1,f2}; - return profile_helper(8, coords, (doFillet || doEdgeFillet) ? 4 : 0, fillets, radii, trsf2d, face); -} - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcTShapeProfileDef* l, TopoDS_Shape& face) { - const bool doFlangeEdgeFillet = !!l->FlangeEdgeRadius(); - const bool doWebEdgeFillet = !!l->WebEdgeRadius(); - const bool doFillet = !!l->FilletRadius(); - const bool hasFlangeSlope = !!l->FlangeSlope(); - const bool hasWebSlope = !!l->WebSlope(); - - 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; - } - - gp_Trsf2d trsf2d; - bool has_position = true; -#ifdef SCHEMA_IfcParameterizedProfileDef_Position_IS_OPTIONAL - has_position = l->Position() != nullptr; -#endif - if (has_position) { - IfcGeom::Kernel::convert(l->Position(), trsf2d); - } - - double coords[16] = {d1/2.-dx2,-y, xx,xy, x,y-d2+dy2, x,y, -x,y, -x,y-d2+dy2, -xx,xy, -d1/2.+dx2,-y}; - int fillets[6] = {0,1,2,5,6,7}; - double radii[6] = {f2,f1,f3,f3,f1,f2}; - return profile_helper(8, coords, (doFillet || doWebEdgeFillet || doFlangeEdgeFillet) ? 6 : 0, fillets, radii, trsf2d, face); -} - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcCircleProfileDef* l, TopoDS_Shape& 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; - } - - gp_Trsf2d trsf2d; - bool has_position = true; -#ifdef SCHEMA_IfcParameterizedProfileDef_Position_IS_OPTIONAL - has_position = l->Position() != nullptr; -#endif - if (has_position) { - IfcGeom::Kernel::convert(l->Position(), trsf2d); - } - gp_Ax2 ax = gp_Ax2().Transformed(trsf2d); - - - Handle(Geom_Circle) circle = new Geom_Circle(ax, r); - TopoDS_Edge edge = BRepBuilderAPI_MakeEdge(circle); - - BRepBuilderAPI_MakeWire w; - w.Add(edge); - - TopoDS_Face f; - bool success = convert_wire_to_face(w, f); - if (success) face = f; - return success; -} - -#ifdef SCHEMA_HAS_IfcCraneRailAShapeProfileDef -bool IfcGeom::Kernel::convert(const IfcSchema::IfcCraneRailAShapeProfileDef* l, TopoDS_Shape& face) { - double oh = l->OverallHeight() * getValue(GV_LENGTH_UNIT); - double bw2 = l->BaseWidth2() * getValue(GV_LENGTH_UNIT); - double hw = l->HeadWidth() * getValue(GV_LENGTH_UNIT); - double hd2 = l->HeadDepth2() * getValue(GV_LENGTH_UNIT); - double hd3 = l->HeadDepth3() * getValue(GV_LENGTH_UNIT); - double wt = l->WebThickness() * getValue(GV_LENGTH_UNIT); - double bw4 = l->BaseWidth4() * getValue(GV_LENGTH_UNIT); - double bd1 = l->BaseDepth1() * getValue(GV_LENGTH_UNIT); - double bd2 = l->BaseDepth2() * getValue(GV_LENGTH_UNIT); - double bd3 = l->BaseDepth3() * getValue(GV_LENGTH_UNIT); - - gp_Trsf2d trsf2d; - bool has_position = true; -#ifdef SCHEMA_IfcParameterizedProfileDef_Position_IS_OPTIONAL - has_position = l->Position() != nullptr; -#endif - if (has_position) { - IfcGeom::Kernel::convert(l->Position(), trsf2d); - } - - double coords[28] = { - -hw / 2., +oh / 2., - -hw / 2., +oh / 2. - hd3, - -wt / 2., +oh / 2. - hd2, - -wt / 2., -oh / 2. + bd2, - -bw4 / 2., -oh / 2. + bd3, - -bw2 / 2., -oh / 2. + bd1, - -bw2 / 2., -oh / 2., - +bw2 / 2., -oh / 2., - +bw2 / 2., -oh / 2. + bd1, - +bw4 / 2., -oh / 2. + bd3, - +wt / 2., -oh / 2. + bd2, - +wt / 2., +oh / 2. - hd2, - +hw / 2., +oh / 2. - hd3, - +hw / 2., +oh / 2. - }; - - return profile_helper(14, coords, 0, 0, 0, trsf2d, face); -} -#endif - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcCircleHollowProfileDef* l, TopoDS_Shape& 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; - } - - gp_Trsf2d trsf2d; - bool has_position = true; -#ifdef SCHEMA_IfcParameterizedProfileDef_Position_IS_OPTIONAL - has_position = l->Position() != nullptr; -#endif - if (has_position) { - IfcGeom::Kernel::convert(l->Position(), trsf2d); - } - - gp_Ax2 ax = gp_Ax2().Transformed(trsf2d); - - BRepBuilderAPI_MakeWire outer; - Handle(Geom_Circle) outerCircle = new Geom_Circle(ax, r); - outer.Add(BRepBuilderAPI_MakeEdge(outerCircle)); - BRepBuilderAPI_MakeFace mf(outer.Wire(), false); - - BRepBuilderAPI_MakeWire inner; - Handle(Geom_Circle) innerCirlce = new Geom_Circle(ax, r-t); - inner.Add(BRepBuilderAPI_MakeEdge(innerCirlce)); - mf.Add(inner); - - ShapeFix_Shape sfs(mf.Face()); - sfs.Perform(); - face = TopoDS::Face(sfs.Shape()); - return true; -} - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcEllipseProfileDef* l, TopoDS_Shape& 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; - } - - const bool rotated = ry > rx; - - gp_Trsf2d trsf2d; - bool has_position = true; -#ifdef SCHEMA_IfcParameterizedProfileDef_Position_IS_OPTIONAL - has_position = l->Position() != nullptr; -#endif - if (has_position) { - IfcGeom::Kernel::convert(l->Position(), trsf2d); - } - - gp_Ax2 ax = gp_Ax2(); - if (rotated) { - ax.Rotate(ax.Axis(), M_PI / 2.); - std::swap(rx, ry); - } - ax.Transform(trsf2d); - - BRepBuilderAPI_MakeWire w; - Handle(Geom_Ellipse) ellipse = new Geom_Ellipse(ax, rx, ry); - TopoDS_Edge edge = BRepBuilderAPI_MakeEdge(ellipse); - w.Add(edge); - - TopoDS_Face f; - bool success = convert_wire_to_face(w, f); - if (success) face = f; - return success; -} - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcCenterLineProfileDef* l, TopoDS_Shape& face) { - const double d = l->Thickness() * getValue(GV_LENGTH_UNIT) / 2.; - - TopoDS_Wire wire; - if (!convert_wire(l->Curve(), wire)) return false; - - // BRepOffsetAPI_MakeOffset insists on creating circular arc - // segments for joining the curves that constitute the center - // line. This is probably not in accordance with the IFC spec. - // Although it does not specify a method to join segments - // explicitly, it does dictate 'a constant thickness along the - // curve'. Therefore for simple singular wires a quick - // alternative is provided that uses a straight join. - - TopExp_Explorer exp(wire, TopAbs_EDGE); - TopoDS_Edge edge = TopoDS::Edge(exp.Current()); - exp.Next(); - - if (!exp.More()) { - double u1, u2; - Handle(Geom_Curve) curve = BRep_Tool::Curve(edge, u1, u2); - - Handle(Geom_TrimmedCurve) trim = new Geom_TrimmedCurve(curve, u1, u2); - - Handle(Geom_OffsetCurve) c1 = new Geom_OffsetCurve(trim, d, gp::DZ()); - Handle(Geom_OffsetCurve) c2 = new Geom_OffsetCurve(trim, -d, gp::DZ()); - - gp_Pnt c1a, c1b, c2a, c2b; - c1->D0(c1->FirstParameter(), c1a); - c1->D0(c1->LastParameter(), c1b); - c2->D0(c2->FirstParameter(), c2a); - c2->D0(c2->LastParameter(), c2b); - - BRepBuilderAPI_MakeWire mw; - mw.Add(BRepBuilderAPI_MakeEdge(c1)); - mw.Add(BRepBuilderAPI_MakeEdge(c1a, c2a)); - mw.Add(BRepBuilderAPI_MakeEdge(c2)); - mw.Add(BRepBuilderAPI_MakeEdge(c2b, c1b)); - - face = BRepBuilderAPI_MakeFace(mw.Wire()); - } else { - BRepOffsetAPI_MakeOffset offset(BRepBuilderAPI_MakeFace(gp_Pln(gp::Origin(), gp::DZ()))); - offset.AddWire(wire); - offset.Perform(d); - face = BRepBuilderAPI_MakeFace(TopoDS::Wire(offset)); - } - return true; -} - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcCompositeProfileDef* l, TopoDS_Shape& face) { - // BRepBuilderAPI_MakeFace mf; - - TopoDS_Compound compound; - BRep_Builder builder; - builder.MakeCompound(compound); - - IfcSchema::IfcProfileDef::list::ptr profiles = l->Profiles(); - //bool first = true; - for (IfcSchema::IfcProfileDef::list::it it = profiles->begin(); it != profiles->end(); ++it) { - TopoDS_Face f; - if (convert_face(*it, f)) { - builder.Add(compound, f); - /* TopExp_Explorer exp(f, TopAbs_WIRE); - for (; exp.More(); exp.Next()) { - const TopoDS_Wire& wire = TopoDS::Wire(exp.Current()); - if (first) { - mf.Init(BRepBuilderAPI_MakeFace(wire)); - } else { - mf.Add(wire); - } - first = false; - } */ - } - } - - face = compound; - return !face.IsNull(); -} - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcDerivedProfileDef* l, TopoDS_Shape& face) { - TopoDS_Face f; - gp_Trsf2d trsf2d; - if (convert_face(l->ParentProfile(), f) && IfcGeom::Kernel::convert(l->Operator(), trsf2d)) { - gp_Trsf trsf = trsf2d; - face = BRepBuilderAPI_Transform(f, trsf).Shape(); - return true; - } else { - return false; - } -} - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcPlane* l, TopoDS_Shape& face) { - gp_Pln pln; - convert(l, pln); - Handle_Geom_Surface surf = new Geom_Plane(pln); -#if OCC_VERSION_HEX < 0x60502 - face = BRepBuilderAPI_MakeFace(surf); -#else - face = BRepBuilderAPI_MakeFace(surf, getValue(GV_PRECISION)); -#endif - return true; -} - -#ifdef SCHEMA_HAS_IfcBSplineSurfaceWithKnots - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcBSplineSurfaceWithKnots* l, TopoDS_Shape& face) { - boost::shared_ptr< aggregate_of_aggregate_of > cps = l->ControlPointsList(); - std::vector uknots = l->UKnots(); - std::vector vknots = l->VKnots(); - std::vector umults = l->UMultiplicities(); - std::vector vmults = l->VMultiplicities(); - - TColgp_Array2OfPnt Poles (0, (int)cps->size() - 1, 0, (int)(*cps->begin()).size() - 1); - TColStd_Array1OfReal UKnots(0, (int)uknots.size() - 1); - TColStd_Array1OfReal VKnots(0, (int)vknots.size() - 1); - TColStd_Array1OfInteger UMults(0, (int)umults.size() - 1); - TColStd_Array1OfInteger VMults(0, (int)vmults.size() - 1); - Standard_Integer UDegree = l->UDegree(); - Standard_Integer VDegree = l->VDegree(); - - int i = 0, j; - for (aggregate_of_aggregate_of::outer_it it = cps->begin(); it != cps->end(); ++it, ++i) { - j = 0; - for (aggregate_of_aggregate_of::inner_it jt = (*it).begin(); jt != (*it).end(); ++jt, ++j) { - IfcSchema::IfcCartesianPoint* p = *jt; - gp_Pnt pnt; - if (!convert(p, pnt)) return false; - Poles(i, j) = pnt; - } - } - i = 0; - for (std::vector::const_iterator it = uknots.begin(); it != uknots.end(); ++it, ++i) { - UKnots(i) = *it; - } - i = 0; - for (std::vector::const_iterator it = vknots.begin(); it != vknots.end(); ++it, ++i) { - VKnots(i) = *it; - } - i = 0; - for (std::vector::const_iterator it = umults.begin(); it != umults.end(); ++it, ++i) { - UMults(i) = *it; - } - i = 0; - for (std::vector::const_iterator it = vmults.begin(); it != vmults.end(); ++it, ++i) { - VMults(i) = *it; - } - Handle_Geom_Surface surf = new Geom_BSplineSurface(Poles, UKnots, VKnots, UMults, VMults, UDegree, VDegree); - -#if OCC_VERSION_HEX < 0x60502 - face = BRepBuilderAPI_MakeFace(surf); -#else - face = BRepBuilderAPI_MakeFace(surf, getValue(GV_PRECISION)); -#endif - - return true; -} - -#endif diff --git a/src/ifcgeom/IfcGeomHelpers.cpp b/src/ifcgeom/IfcGeomHelpers.cpp deleted file mode 100644 index ee2918a1f6..0000000000 --- a/src/ifcgeom/IfcGeomHelpers.cpp +++ /dev/null @@ -1,476 +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 . * - * * - ********************************************************************************/ - -/******************************************************************************** - * * - * Implementations of the various conversion functions defined in mapping.i * - * * - ********************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include - -#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 - 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 struct dimension_count {}; - template <> struct dimension_count { static const int n = 2; }; - template <> struct dimension_count { 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 - 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::n + 2; ++i) { - for (int j = 1; j < dimension_count::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 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 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; - - if (!l->Location()->declaration().is("IfcCartesianPoint")) { - // only applicable to 4x3 rc2 - Logger::Error("Not implemented", l->Location()); - return false; - } - - IfcGeom::Kernel::convert((const IfcSchema::IfcCartesianPoint*) l->Location(), o); - const bool hasAxis = !!l->Axis(); - const bool hasRef = !!l->RefDirection(); - - 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); - - if (!l->Location()->declaration().is("IfcCartesianPoint")) { - // only applicable to 4x3 rc2 - Logger::Error("Not implemented", l->Location()); - return false; - } - - IfcGeom::Kernel::convert((const IfcSchema::IfcCartesianPoint*) l->Location(),o); - if ( l->Axis() ) 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->Axis1() ) IfcGeom::Kernel::convert(l->Axis1(),axis1); - if ( l->Axis2() ) IfcGeom::Kernel::convert(l->Axis2(),axis2); - if ( l->Axis3() ) 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->Scale() && !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->Axis1() ) IfcGeom::Kernel::convert(l->Axis1(),axis1); - if ( l->Axis2() ) 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->Scale() && !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->Axis1() ) IfcGeom::Kernel::convert(l->Axis1(),axis1); - if ( l->Axis2() ) IfcGeom::Kernel::convert(l->Axis2(),axis2); - if ( l->Axis3() ) 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->Scale().get_value_or(1.); - const double scale2 = l->Scale2().get_value_or(scale1); - const double scale3 = l->Scale3().get_value_or(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->Axis1() ) IfcGeom::Kernel::convert(l->Axis1(),axis1); - if ( l->Axis2() ) 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->Scale().get_value_or(1.); - const double scale2 = l->Scale2().get_value_or(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; - - if (!l->Location()->declaration().is("IfcCartesianPoint")) { - // only applicable to 4x3 rc2 - Logger::Error("Not implemented", l->Location()); - return false; - } - - IfcGeom::Kernel::convert((const IfcSchema::IfcCartesianPoint*) l->Location(),o); - bool hasRef = !!l->RefDirection(); - if ( l->Axis() ) 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); - - if (!l->Location()->declaration().is("IfcCartesianPoint")) { - // only applicable to 4x3 rc2 - Logger::Error("Not implemented", l->Location()); - return false; - } - - IfcGeom::Kernel::convert((const IfcSchema::IfcCartesianPoint*) l->Location(),P); - if (l->RefDirection()) { - 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_type(const IfcParse::declaration* type) { - placement_rel_to_type_ = type; -} - -void IfcGeom::Kernel::set_conversion_placement_rel_to_instance(const IfcUtil::IfcBaseEntity* instance) { - placement_rel_to_instance_ = instance; -} - -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->as()) { - IfcGeom::Kernel::convert(relplacement->as(),trsf2); - trsf.PreMultiply(trsf2); - } - - if (current->PlacementRelTo()) { - IfcSchema::IfcObjectPlacement* parent = current->PlacementRelTo(); - - bool parent_placement_ignored = false; - if (placement_rel_to_type_ || placement_rel_to_instance_) { - IfcSchema::IfcProduct::list::ptr parent_places = parent->PlacesObject(); - for (auto iter = parent_places->begin(); iter != parent_places->end(); ++iter) { - if ((placement_rel_to_type_ && (*iter)->declaration().is(*placement_rel_to_type_)) || - (placement_rel_to_instance_ && (*iter)->as() == placement_rel_to_instance_)) { - parent_placement_ignored = true; - } - } - } - - if (parent_placement_ignored) { - // The parent placement of the current is a placement for a type that is - // being ignored (Site or Building) or it is the host element of an opening. - break; - } else if (parent->declaration().is(IfcSchema::IfcLocalPlacement::Class())) { - // Keep processing parent placements - current = current->PlacementRelTo()->as(); - } else { - // This is the root placement (typically Site). - break; - } - } else { - break; - } - } - - trsf.PreMultiply(offset_and_rotation); - - CACHE(IfcObjectPlacement,l,trsf) - return true; -} diff --git a/src/ifcgeom/IfcGeomShapes.cpp b/src/ifcgeom/IfcGeomShapes.cpp deleted file mode 100644 index 73ba45f0b8..0000000000 --- a/src/ifcgeom/IfcGeomShapes.cpp +++ /dev/null @@ -1,1864 +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 . * - * * - ********************************************************************************/ - -/******************************************************************************** - * * - * Implementations of the various conversion functions defined in mapping.i * - * * - ********************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include - -#include - -#include -#include - -#include - -#include - -#include - -#include "../ifcgeom/IfcGeom.h" - -#include - -#ifdef SCHEMA_HAS_IfcToroidalSurface -#include -#endif - -#define Kernel MAKE_TYPE_NAME(Kernel) - -// uncomment if you'd like negative or close to zero extrusion depths to succeed -// #define PERMISSIVE_EXTRUSION - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcExtrudedAreaSolid* l, TopoDS_Shape& 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); -#ifndef PERMISSIVE_EXTRUSION - return false; -#endif - } - - TopoDS_Shape face; - if ( !convert_face(l->SweptArea(),face) ) return false; - -#ifdef PERMISSIVE_EXTRUSION - if (abs(height) < getValue(GV_PRECISION)) { - shape = face; - return true; - } -#endif - - gp_Trsf trsf; - bool has_position = true; -#ifdef SCHEMA_IfcSweptAreaSolid_Position_IS_OPTIONAL - has_position = l->Position() != nullptr; -#endif - if (has_position) { - IfcGeom::Kernel::convert(l->Position(), trsf); - } - - gp_Dir dir; - convert(l->ExtrudedDirection(),dir); - - shape.Nullify(); - - /* - // @nb This logic was probably flawed always as this does not by itself result in a valid compsolid... - 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 (has_position && !shape.IsNull()) { - // IfcSweptAreaSolid.Position (trsf) is an IfcAxis2Placement3D - // and therefore has a unit scale factor - shape.Move(trsf); - } - - return !shape.IsNull(); -} - -#ifdef SCHEMA_HAS_IfcExtrudedAreaSolidTapered -bool IfcGeom::Kernel::convert(const IfcSchema::IfcExtrudedAreaSolidTapered* l, TopoDS_Shape& 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; - } - - TopoDS_Shape face1, face2; - if (!convert_face(l->SweptArea(), face1)) return false; - if (!convert_face(l->EndSweptArea(), face2)) return false; - - gp_Trsf trsf; - bool has_position = true; -#ifdef SCHEMA_IfcSweptAreaSolid_Position_IS_OPTIONAL - has_position = l->Position() != nullptr; -#endif - if (has_position) { - IfcGeom::Kernel::convert(l->Position(), trsf); - } - - gp_Dir dir; - convert(l->ExtrudedDirection(), dir); - - gp_Trsf end_profile; - end_profile.SetTranslation(height * dir); - - TopoDS_Edge spine_edge = BRepBuilderAPI_MakeEdge(gp_Pnt(), gp_Pnt((height * dir).XYZ())).Edge(); - TopoDS_Wire wire = BRepBuilderAPI_MakeWire(spine_edge).Wire(); - - shape.Nullify(); - - TopExp_Explorer exp1(face1, TopAbs_WIRE); - TopExp_Explorer exp2(face2, TopAbs_WIRE); - - TopoDS_Vertex v1, v2; - TopExp::Vertices(wire, v1, v2); - - TopoDS_Shape shell; - TopoDS_Compound compound; - BRep_Builder compound_builder; - - for (; exp1.More() && exp2.More(); exp1.Next(), exp2.Next()) { - const TopoDS_Wire& w1 = TopoDS::Wire(exp1.Current()); - const TopoDS_Wire& w2 = TopoDS::Wire(exp2.Current()); - - BRepOffsetAPI_MakePipeShell builder(wire); - builder.Add(w1, v1); - builder.Add(w2.Moved(end_profile), v2); - - TopoDS_Shape result = builder.Shape(); - - TopTools_ListOfShape li; - shape_to_face_list(result, li); - li.Append(BRepBuilderAPI_MakeFace(w1).Face().Reversed()); - li.Append(BRepBuilderAPI_MakeFace(w2).Face().Moved(end_profile)); - - create_solid_from_faces(li, result, true); - - // @todo ugly hack - - // The reason for this distinction is that at this point of the loop we're not sure anymore - // whether this was constructed from an inner or outer bound. So rather than iterating over - // wires of `face1` and `face2` we should iterate over the faces and then properly check with - // BRepTools::OuterBound(). - // Currently this distinction happens based on profile type which is not robust and probably - // not complete. - if (shell.IsNull()) { - shell = result; - } else if (l->SweptArea()->declaration().is(IfcSchema::IfcCircleHollowProfileDef::Class()) || - l->SweptArea()->declaration().is(IfcSchema::IfcRectangleHollowProfileDef::Class()) || - l->SweptArea()->declaration().is(IfcSchema::IfcArbitraryProfileDefWithVoids::Class())) - { - // @todo properly check for failure and all. - shell = BRepAlgoAPI_Cut(shell, result).Shape(); - } else { - if (compound.IsNull()) { - compound_builder.MakeCompound(compound); - compound_builder.Add(compound, shell); - } - compound_builder.Add(compound, result); - } - } - - if (!compound.IsNull()) { - shell = compound; - } - - shape = shell; - - if (exp1.More() != exp2.More()) { - Logger::Message(Logger::LOG_ERROR, "Inconsistent profiles encountered for:", l); - } - - if (has_position && !shape.IsNull()) { - // IfcSweptAreaSolid.Position (trsf) is an IfcAxis2Placement3D - // and therefore has a unit scale factor - shape.Move(trsf); - } - - return !shape.IsNull(); -} -#endif - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcSurfaceOfLinearExtrusion* l, TopoDS_Shape& shape) { - TopoDS_Wire wire; - if ( !convert_wire(l->SweptCurve(), wire) ) { - TopoDS_Face face; - if ( !convert_face(l->SweptCurve(),face) ) return false; - TopExp_Explorer exp(face, TopAbs_WIRE); - wire = TopoDS::Wire(exp.Current()); - } - const double height = l->Depth() * getValue(GV_LENGTH_UNIT); - - gp_Trsf trsf; - bool has_position = true; -#ifdef SCHEMA_IfcSweptSurface_Position_IS_OPTIONAL - has_position = l->Position() != nullptr; -#endif - if (has_position) { - IfcGeom::Kernel::convert(l->Position(), trsf); - } - - gp_Dir dir; - convert(l->ExtrudedDirection(),dir); - - shape = BRepPrimAPI_MakePrism(wire, height*dir); - - if (has_position) { - // IfcSweptSurface.Position (trsf) is an IfcAxis2Placement3D - // and therefore has a unit scale factor - shape.Move(trsf); - } - - return !shape.IsNull(); -} - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcSurfaceOfRevolution* l, TopoDS_Shape& shape) { - TopoDS_Wire wire; - if ( !convert_wire(l->SweptCurve(), wire) ) { - TopoDS_Face face; - if ( !convert_face(l->SweptCurve(),face) ) return false; - TopExp_Explorer exp(face, TopAbs_WIRE); - wire = TopoDS::Wire(exp.Current()); - } - - gp_Ax1 ax1; - IfcGeom::Kernel::convert(l->AxisPosition(), ax1); - - gp_Trsf trsf; - bool has_position = true; -#ifdef SCHEMA_IfcSweptSurface_Position_IS_OPTIONAL - has_position = l->Position() != nullptr; -#endif - if (has_position) { - IfcGeom::Kernel::convert(l->Position(), trsf); - } - - shape = BRepPrimAPI_MakeRevol(wire, ax1); - - if (has_position) { - // IfcSweptSurface.Position (trsf) is an IfcAxis2Placement3D - // and therefore has a unit scale factor - shape.Move(trsf); - } - - return !shape.IsNull(); -} - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcRevolvedAreaSolid* l, TopoDS_Shape& shape) { - const double ang = l->Angle() * getValue(GV_PLANEANGLE_UNIT); - - TopoDS_Shape face; - if ( ! convert_face(l->SweptArea(),face) ) return false; - - gp_Ax1 ax1; - IfcGeom::Kernel::convert(l->Axis(), ax1); - - gp_Trsf trsf; - bool has_position = true; -#ifdef SCHEMA_IfcSweptAreaSolid_Position_IS_OPTIONAL - has_position = l->Position() != nullptr; -#endif - if (has_position) { - IfcGeom::Kernel::convert(l->Position(), trsf); - } - - - { - // https://github.com/IfcOpenShell/IfcOpenShell/issues/1030 - // Check whether Axis does not intersect SweptArea - - double min_dot = +std::numeric_limits::infinity(); - double max_dot = -std::numeric_limits::infinity(); - - gp_Ax2 ax(ax1.Location(), gp::DZ(), ax1.Direction()); - - TopExp_Explorer exp(face, TopAbs_EDGE); - for (; exp.More(); exp.Next()) { - BRepAdaptor_Curve crv(TopoDS::Edge(exp.Current())); - GCPnts_QuasiUniformDeflection tessellater(crv, getValue(GV_PRECISION)); - - int n = tessellater.NbPoints(); - for (int i = 1; i <= n; ++i) { - double d = ax.YDirection().XYZ().Dot(tessellater.Value(i).XYZ()); - if (d < min_dot) { - min_dot = d; - } - if (d > max_dot) { - max_dot = d; - } - } - } - - bool intersecting; - if (std::abs(min_dot) > std::abs(max_dot)) { - intersecting = max_dot > + getValue(GV_PRECISION); - } else { - intersecting = min_dot < - getValue(GV_PRECISION); - } - - if (intersecting) { - Logger::Warning("Warning Axis and SweptArea intersecting", l); - } - } - - if (ang >= M_PI * 2. - ALMOST_ZERO) { - shape = BRepPrimAPI_MakeRevol(face, ax1); - } else { - shape = BRepPrimAPI_MakeRevol(face, ax1, ang); - } - - if (has_position) { - // IfcSweptAreaSolid.Position (trsf) is an IfcAxis2Placement3D - // and therefore has a unit scale factor - shape.Move(trsf); - } - - return !shape.IsNull(); -} - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcManifoldSolidBrep* l, IfcRepresentationShapeItems& shape) { - TopoDS_Shape s; - auto collective_style = get_style(l); - if (convert_shape(l->Outer(),s) ) { - auto indiv_style = get_style(l->Outer()); - - IfcSchema::IfcClosedShell::list::ptr voids(new IfcSchema::IfcClosedShell::list); - if (l->declaration().is(IfcSchema::IfcFacetedBrepWithVoids::Class())) { - voids = l->as()->Voids(); - } -#ifdef SCHEMA_HAS_IfcAdvancedBrepWithVoids - if (l->declaration().is(IfcSchema::IfcAdvancedBrepWithVoids::Class())) { - voids = l->as()->Voids(); - } -#endif - - for (IfcSchema::IfcClosedShell::list::it it = voids->begin(); it != voids->end(); ++it) { - TopoDS_Shape s2; - /// @todo No extensive shapefixing since shells should be disjoint. - /// @todo Awaiting generalized boolean ops module with appropriate checking - if (convert_shape(l->Outer(), s2)) { - s = BRepAlgoAPI_Cut(s, s2).Shape(); - } - } - - shape.push_back(IfcRepresentationShapeItem(l->data().id(), s, indiv_style ? indiv_style : collective_style)); - return true; - } - return false; -} - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcFaceBasedSurfaceModel* l, IfcRepresentationShapeItems& shapes) { - bool part_success = false; - IfcSchema::IfcConnectedFaceSet::list::ptr facesets = l->FbsmFaces(); - auto collective_style = get_style(l); - for( IfcSchema::IfcConnectedFaceSet::list::it it = facesets->begin(); it != facesets->end(); ++ it ) { - TopoDS_Shape s; - auto shell_style = get_style(*it); - if (convert_shape(*it,s)) { - shapes.push_back(IfcRepresentationShapeItem(l->data().id(), s, shell_style ? shell_style : collective_style)); - part_success |= true; - } - } - return part_success; -} - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcHalfSpaceSolid* l, TopoDS_Shape& shape) { - IfcSchema::IfcSurface* surface = l->BaseSurface(); - if ( ! surface->declaration().is(IfcSchema::IfcPlane::Class()) ) { - Logger::Message(Logger::LOG_ERROR, "Unsupported BaseSurface:", surface); - return false; - } - gp_Pln pln; - IfcGeom::Kernel::convert((IfcSchema::IfcPlane*)surface,pln); - const gp_Pnt pnt = pln.Location().Translated( l->AgreementFlag() ? -pln.Axis().Direction() : pln.Axis().Direction()); - shape = BRepPrimAPI_MakeHalfSpace(BRepBuilderAPI_MakeFace(pln),pnt).Solid(); - return true; -} - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcPolygonalBoundedHalfSpace* l, TopoDS_Shape& shape) { - TopoDS_Shape halfspace; - if ( ! IfcGeom::Kernel::convert((IfcSchema::IfcHalfSpaceSolid*)l,halfspace) ) return false; - - TopoDS_Wire wire; - if ( ! convert_wire(l->PolygonalBoundary(),wire) || ! wire.Closed() ) return false; - - gp_Trsf trsf; - if ( ! convert(l->Position(),trsf) ) return false; - - TColgp_SequenceOfPnt points; - if (wire_to_sequence_of_point(wire, points)) { - // Boolean subtractions not very robust for narrow operands, - // increase minimal point spacing to eliminate such shapes. - const double t = getValue(GV_PRECISION) * 10.; - remove_duplicate_points_from_loop(points, wire.Closed() != 0, t); // Note: wire always closed, as per if statement above - remove_collinear_points_from_loop(points, wire.Closed() != 0, t); - if (points.Length() < 3) { - Logger::Message(Logger::LOG_ERROR, "Not enough points retained from:", l->PolygonalBoundary()); - return false; - } - sequence_of_point_to_wire(points, wire, wire.Closed() != 0); - } - - TopoDS_Shape prism = BRepPrimAPI_MakePrism(BRepBuilderAPI_MakeFace(wire),gp_Vec(0,0,200)); - gp_Trsf down; down.SetTranslation(gp_Vec(0,0,-100.0)); - - // `trsf` and `down` both have a unit scale factor - prism.Move(trsf*down); - - shape = BRepAlgoAPI_Common(halfspace,prism); - return true; -} - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcShellBasedSurfaceModel* l, IfcRepresentationShapeItems& shapes) { - aggregate_of_instance::ptr shells = l->SbsmBoundary(); - auto collective_style = get_style(l); - for( aggregate_of_instance::it it = shells->begin(); it != shells->end(); ++ it ) { - TopoDS_Shape s; - decltype(collective_style) shell_style; - if ((*it)->declaration().is(IfcSchema::IfcRepresentationItem::Class())) { - shell_style = get_style((IfcSchema::IfcRepresentationItem*)*it); - } - if (convert_shape(*it,s)) { - shapes.push_back(IfcRepresentationShapeItem(l->data().id(), s, shell_style ? shell_style : collective_style)); - } - } - return true; -} - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcBooleanResult* l, TopoDS_Shape& shape) { - - TopoDS_Shape s1; - IfcRepresentationShapeItems items1; - TopoDS_Wire boundary_wire; - IfcSchema::IfcBooleanOperand* operand1 = l->FirstOperand(); - IfcSchema::IfcBooleanOperand* operand2 = l->SecondOperand(); - bool has_halfspace_operand = false; - - BOPAlgo_Operation occ_op; - - const IfcSchema::IfcBooleanOperator::Value op = l->Operator(); - if (op == IfcSchema::IfcBooleanOperator::IfcBooleanOperator_DIFFERENCE) { - occ_op = BOPAlgo_CUT; - } else if (op == IfcSchema::IfcBooleanOperator::IfcBooleanOperator_INTERSECTION) { - occ_op = BOPAlgo_COMMON; - } else if (op == IfcSchema::IfcBooleanOperator::IfcBooleanOperator_UNION) { - occ_op = BOPAlgo_FUSE; - } else { - return false; - } - - std::vector second_operands; - second_operands.push_back(operand2); - - if (occ_op == BOPAlgo_CUT) { - int n_half_space_operands = 0; - bool process_as_list = true; - while (true) { - auto res1 = operand1->as(); - if (res1 && res1->SecondOperand()->as() && ++n_half_space_operands > 8) { - // There is something peculiar about many half space subtraction operands that OCCT does not like. - // Often these are used to create a semi-curved arch, as is the case in 693. Supplying all these - // operands at once apparently leads to too many edge-edge interference checks. - process_as_list = false; - break; - } - if (res1) { - if (res1->Operator() == op) { - operand1 = res1->FirstOperand(); - second_operands.push_back(res1->SecondOperand()); - } else { - process_as_list = false; - break; - } - } else { - break; - } - } - - if (!process_as_list) { - operand1 = l->FirstOperand(); - second_operands = { operand2 }; - } - } - - if ( shape_type(operand1) == ST_SHAPELIST ) { - 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; - } - { TopoDS_Solid temp_solid; - s1 = ensure_fit_for_subtraction(s1, temp_solid); } - } else { - Logger::Message(Logger::LOG_ERROR, "Invalid representation item for boolean operation", operand1); - return false; - } - - if (getValue(GV_DISABLE_BOOLEAN_RESULT) > 0.0) { - shape = s1; - return true; - } - - const double first_operand_volume = shape_volume(s1); - if (first_operand_volume <= ALMOST_ZERO) { - Logger::Message(Logger::LOG_WARNING, "Empty solid for:", l->FirstOperand()); - } - - TopTools_ListOfShape second_operand_shapes; - - for (auto& op2 : second_operands) { - TopoDS_Shape s2; - - bool shape2_processed = false; - - bool is_halfspace = op2->declaration().is(IfcSchema::IfcHalfSpaceSolid::Class()); - bool is_unbounded_halfspace = is_halfspace && !op2->declaration().is(IfcSchema::IfcPolygonalBoundedHalfSpace::Class()); - has_halfspace_operand |= is_halfspace; - - { - if (shape_type(op2) == ST_SHAPELIST) { - IfcRepresentationShapeItems items2; - shape2_processed = convert_shapes(op2, items2) && flatten_shape_list(items2, s2, true); - } else if (shape_type(op2) == ST_SHAPE) { - shape2_processed = convert_shape(op2, s2); - if (shape2_processed) { - TopoDS_Solid temp_solid; - s2 = ensure_fit_for_subtraction(s2, temp_solid); - } - } else { - Logger::Message(Logger::LOG_ERROR, "Invalid representation item for boolean operation", op2); - } - } - - if (is_unbounded_halfspace) { - TopoDS_Shape temp; - double d; - if (fit_halfspace(s1, s2, temp, d)) { - if (d < getValue(GV_PRECISION)) { - Logger::Message(Logger::LOG_WARNING, "Halfspace subtraction yields unchanged volume:", l); - continue; - } else { - s2 = temp; - } - } - } - - if (!shape2_processed) { - Logger::Message(Logger::LOG_ERROR, "Failed to convert SecondOperand:", op2); - continue; - } - - if (op2->declaration().is(IfcSchema::IfcHalfSpaceSolid::Class())) { - const double second_operand_volume = shape_volume(s2); - if (second_operand_volume <= ALMOST_ZERO) { - Logger::Message(Logger::LOG_WARNING, "Empty solid for:", op2); - } - } - - second_operand_shapes.Append(s2); - } - - /* - // TK: A little debugging trick to output both operands for visual inspection - - BRep_Builder builder; - TopoDS_Compound compound; - builder.MakeCompound(compound); - builder.Add(compound, s1); - for (const auto& s2 : second_operand_shapes) { - builder.Add(compound, s2); - } - shape = compound; - return true; - */ - -#if OCC_VERSION_HEX < 0x60900 - // @todo: this currently does not compile anymore, do we still need this? - bool valid_result = boolean_operation(s1, s2, occ_op, shape); -#else - - bool valid_result; - - if (s1.ShapeType() == TopAbs_COMPOUND && TopoDS_Iterator(s1).More() && util::is_nested_compound_of_solid(s1)) { - TopoDS_Compound C; - BRep_Builder B; - B.MakeCompound(C); - TopoDS_Iterator it(s1); - valid_result = true; - for (; it.More(); it.Next()) { - TopoDS_Shape part; - if (boolean_operation(it.Value(), second_operand_shapes, occ_op, part)) { - B.Add(C, part); - } else { - valid_result = false; - } - } - shape = C; - } else { - valid_result = boolean_operation(s1, second_operand_shapes, occ_op, shape); - } - -#endif - - if (op == IfcSchema::IfcBooleanOperator::IfcBooleanOperator_DIFFERENCE) { - // In case of a subtraction, a check on volume is performed. - if (valid_result) { - const double volume_after_subtraction = shape_volume(shape); - if ( ALMOST_THE_SAME(first_operand_volume,volume_after_subtraction) ) - Logger::Message(Logger::LOG_WARNING,"Subtraction yields unchanged volume:",l); - } else { - Logger::Message(Logger::LOG_ERROR,"Failed to process subtraction:",l); - shape = s1; - } - // NB: After issuing error the first operand is returned! - return true; - } else if (op == IfcSchema::IfcBooleanOperator::IfcBooleanOperator_UNION && !valid_result) { - BRep_Builder B; - TopoDS_Compound C; - B.MakeCompound(C); - B.Add(C, s1); - TopTools_ListIteratorOfListOfShape it(second_operand_shapes); - for (; it.More(); it.Next()) { - B.Add(C, it.Value()); - } - Logger::Message(Logger::LOG_ERROR, "Failed to process union, creating compound:", l); - shape = C; - return true; - } else { - return valid_result; - } - return false; -} - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcConnectedFaceSet* l, TopoDS_Shape& shape) { - std::unique_ptr> helper_scope; - - IfcSchema::IfcCartesianPoint::list::ptr points = IfcParse::traverse((IfcUtil::IfcBaseClass*) l)->as(); - std::vector points_(points->begin(), points->end()); - - IfcSchema::IfcPolyLoop::list::ptr loops = IfcParse::traverse((IfcUtil::IfcBaseClass*)l)->as(); - std::vector loops_(loops->begin(), loops->end()); - - helper_scope.reset(new faceset_helper<>( - this, - points_, - loops_, - l->declaration().is(IfcSchema::IfcClosedShell::Class()) - )); - - faceset_helper_ = helper_scope.get(); - - IfcSchema::IfcFace::list::ptr faces = l->CfsFaces(); - - double min_face_area = faceset_helper_ - ? (faceset_helper_->epsilon() * faceset_helper_->epsilon() / 20.) - : getValue(GV_MINIMAL_FACE_AREA); - - TopTools_ListOfShape face_list; - for (IfcSchema::IfcFace::list::it it = faces->begin(); it != faces->end(); ++it) { - bool success = false; - TopoDS_Face face; - - try { - success = convert_face(*it, 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:", (*it)); - continue; - } - - if (face.ShapeType() == TopAbs_COMPOUND) { - TopoDS_Iterator face_it(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:", (*it)); - } - } - } - } else { - if (face_area(face) > min_face_area) { - face_list.Append(face); - } else { - Logger::Message(Logger::LOG_WARNING, "Degenerate face:", (*it)); - } - } - } - - if (face_list.Extent() == 0) { - return false; - } - - if (face_list.Extent() > getValue(GV_MAX_FACES_TO_ORIENT) || !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 IfcGeom::Kernel::convert(const IfcSchema::IfcMappedItem* l, IfcRepresentationShapeItems& shapes) { - gp_GTrsf gtrsf; - IfcSchema::IfcCartesianTransformationOperator* transform = l->MappingTarget(); - if ( transform->declaration().is(IfcSchema::IfcCartesianTransformationOperator3DnonUniform::Class()) ) { - IfcGeom::Kernel::convert((IfcSchema::IfcCartesianTransformationOperator3DnonUniform*)transform,gtrsf); - } else if ( transform->declaration().is(IfcSchema::IfcCartesianTransformationOperator2DnonUniform::Class()) ) { - Logger::Message(Logger::LOG_ERROR, "Unsupported MappingTarget:", transform); - return false; - } else if ( transform->declaration().is(IfcSchema::IfcCartesianTransformationOperator3D::Class()) ) { - gp_Trsf trsf; - IfcGeom::Kernel::convert((IfcSchema::IfcCartesianTransformationOperator3D*)transform,trsf); - gtrsf = trsf; - } else if ( transform->declaration().is(IfcSchema::IfcCartesianTransformationOperator2D::Class()) ) { - gp_Trsf2d trsf_2d; - IfcGeom::Kernel::convert((IfcSchema::IfcCartesianTransformationOperator2D*)transform,trsf_2d); - gtrsf = (gp_Trsf) trsf_2d; - } - IfcSchema::IfcRepresentationMap* map = l->MappingSource(); - IfcSchema::IfcAxis2Placement* placement = map->MappingOrigin(); - gp_Trsf trsf; - if (placement->declaration().is(IfcSchema::IfcAxis2Placement3D::Class())) { - IfcGeom::Kernel::convert((IfcSchema::IfcAxis2Placement3D*)placement,trsf); - } else { - gp_Trsf2d trsf_2d; - IfcGeom::Kernel::convert((IfcSchema::IfcAxis2Placement2D*)placement,trsf_2d); - trsf = trsf_2d; - } - gtrsf.Multiply(trsf); - - auto 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 ) { - shapes[i].prepend(gtrsf); - - // 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::Kernel::convert(const IfcSchema::IfcRepresentation* l, IfcRepresentationShapeItems& 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 { - TopoDS_Shape s; - if (convert_shape(representation_item, s)) { - if (s.ShapeType() == TopAbs_COMPOUND && TopoDS_Iterator(s).More() && TopoDS_Iterator(s).Value().ShapeType() == TopAbs_SOLID) { - TopoDS_Iterator it(s); - for (; it.More(); it.Next()) { - shapes.push_back(IfcRepresentationShapeItem(representation_item->data().id(), it.Value(), get_style(representation_item))); - } - } else { - shapes.push_back(IfcRepresentationShapeItem(representation_item->data().id(), s, get_style(representation_item))); - } - part_succes |= true; - } - } - } - } - return part_succes; -} - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcGeometricSet* l, IfcRepresentationShapeItems& shapes) { - // @nb the selection is partly duplicated from convert_curves() but it's needed as a - // geometric set by it's static class definition does not inform us of the type of elements. - // @todo handle this better so that this doesn't log an error. - const bool include_curves = getValue(GV_DIMENSIONALITY) != +1; - const bool include_solids_and_surfaces = getValue(GV_DIMENSIONALITY) != -1; - - aggregate_of_instance::ptr elements = l->Elements(); - if ( !elements->size() ) return false; - bool part_succes = false; - auto parent_style = get_style(l); - for (aggregate_of_instance::it it = elements->begin(); it != elements->end(); ++it) { - auto element = *it; - TopoDS_Shape s; - if (shape_type(element) == ST_SHAPELIST) { - IfcRepresentationShapeItems items; - if (!(convert_shapes(element, items) && flatten_shape_list(items, s, false))) { - continue; - } - } else if (shape_type(element) == ST_SHAPE && include_solids_and_surfaces) { - if (!convert_shape(element, s)) { - continue; - } - } else if (shape_type(element) == ST_WIRE && include_curves) { - TopoDS_Wire w; - if (!convert_wire(element, w)) { - continue; - } - s = w; - } else { - continue; - } - - part_succes = true; - decltype(parent_style) style = 0; - if (element->declaration().is(IfcSchema::IfcPoint::Class())) { - style = get_style((IfcSchema::IfcPoint*) element); - } - else if (element->declaration().is(IfcSchema::IfcCurve::Class())) { - style = get_style((IfcSchema::IfcCurve*) element); - } - else if (element->declaration().is(IfcSchema::IfcSurface::Class())) { - style = get_style((IfcSchema::IfcSurface*) element); - } - shapes.push_back(IfcRepresentationShapeItem(l->data().id(), s, style ? style : parent_style)); - } - return part_succes; -} - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcBlock* l, TopoDS_Shape& 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); - - BRepPrimAPI_MakeBox builder(dx, dy, dz); - gp_Trsf trsf; - IfcGeom::Kernel::convert(l->Position(),trsf); - - // IfcCsgPrimitive3D.Position has unit scale factor - shape = builder.Solid().Moved(trsf); - - return true; -} - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcBoundingBox* l, TopoDS_Shape& shape) { - const double dx = l->XDim() * getValue(GV_LENGTH_UNIT); - const double dy = l->YDim() * getValue(GV_LENGTH_UNIT); - const double dz = l->ZDim() * getValue(GV_LENGTH_UNIT); - - gp_Pnt corner; - IfcGeom::Kernel::convert(l->Corner(), corner); - BRepPrimAPI_MakeBox builder(corner, dx, dy, dz); - - shape = builder.Solid(); - - return true; -} - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcRectangularPyramid* l, TopoDS_Shape& 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); - - BRepPrimAPI_MakeWedge builder(dx, dz, dy, dx / 2., dy / 2., dx / 2., dy / 2.); - - gp_Trsf trsf1, trsf2; - trsf2.SetValues( - 1, 0, 0, 0, - 0, 0, 1, 0, - 0, 1, 0, 0 -#if OCC_VERSION_HEX < 0x60800 - , Precision::Angular(), Precision::Confusion() -#endif - ); - - IfcGeom::Kernel::convert(l->Position(), trsf1); - shape = BRepBuilderAPI_Transform(builder.Solid(), trsf1 * trsf2); - return true; -} - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcRightCircularCylinder* l, TopoDS_Shape& shape) { - const double r = l->Radius() * getValue(GV_LENGTH_UNIT); - const double h = l->Height() * getValue(GV_LENGTH_UNIT); - - BRepPrimAPI_MakeCylinder builder(r, h); - gp_Trsf trsf; - IfcGeom::Kernel::convert(l->Position(),trsf); - - // IfcCsgPrimitive3D.Position has unit scale factor - shape = builder.Solid().Moved(trsf); - - return true; -} - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcRightCircularCone* l, TopoDS_Shape& shape) { - const double r = l->BottomRadius() * getValue(GV_LENGTH_UNIT); - const double h = l->Height() * getValue(GV_LENGTH_UNIT); - - BRepPrimAPI_MakeCone builder(r, 0., h); - gp_Trsf trsf; - IfcGeom::Kernel::convert(l->Position(),trsf); - - // IfcCsgPrimitive3D.Position has unit scale factor - shape = builder.Solid().Moved(trsf); - - return true; -} - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcSphere* l, TopoDS_Shape& shape) { - const double r = l->Radius() * getValue(GV_LENGTH_UNIT); - - BRepPrimAPI_MakeSphere builder(r); - gp_Trsf trsf; - IfcGeom::Kernel::convert(l->Position(),trsf); - - // IfcCsgPrimitive3D.Position has unit scale factor - shape = builder.Solid().Moved(trsf); - - return true; -} - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcCsgSolid* l, TopoDS_Shape& shape) { - return convert_shape(l->TreeRootExpression(), shape); -} - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcCurveBoundedPlane* l, TopoDS_Shape& face) { - gp_Pln pln; - if (!IfcGeom::Kernel::convert(l->BasisSurface(), pln)) { - return false; - } - - gp_Trsf trsf; - trsf.SetTransformation(pln.Position(), gp::XOY()); - - TopoDS_Wire outer; - if (!convert_wire(l->OuterBoundary(), outer)) { - return false; - } - - BRepBuilderAPI_MakeFace mf(outer); - - if (!mf.IsDone() || mf.Shape().IsNull()) { - Logger::Error("Invalid outer boundary:", l->OuterBoundary()); - return false; - } - - IfcSchema::IfcCurve::list::ptr boundaries = l->InnerBoundaries(); - - for (IfcSchema::IfcCurve::list::it it = boundaries->begin(); it != boundaries->end(); ++it) { - TopoDS_Wire inner; - if (convert_wire(*it, inner)) { - mf.Add(inner); - } - } - - ShapeFix_Shape sfs(mf.Face()); - sfs.Perform(); - - // `trsf` consitutes the placement of the plane and therefore has unit scale factor - face = TopoDS::Face(sfs.Shape()).Moved(trsf); - - return true; -} - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcRectangularTrimmedSurface* l, TopoDS_Shape& face) { - if (!l->BasisSurface()->declaration().is(IfcSchema::IfcPlane::Class())) { - Logger::Message(Logger::LOG_ERROR, "Unsupported BasisSurface:", l->BasisSurface()); - return false; - } - gp_Pln pln; - IfcGeom::Kernel::convert((IfcSchema::IfcPlane*) l->BasisSurface(), pln); - - BRepBuilderAPI_MakeFace mf(pln, l->U1(), l->U2(), l->V1(), l->V2()); - - face = mf.Face(); - - return true; -} - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcSurfaceCurveSweptAreaSolid* l, TopoDS_Shape& shape) { - gp_Trsf directrix; - TopoDS_Shape face; - TopoDS_Face surface_face; - TopoDS_Wire wire, section; - - const bool is_plane = l->ReferenceSurface()->declaration().is(IfcSchema::IfcPlane::Class()); - - if (!is_plane) { - TopoDS_Shape surface_shell; - if (!convert_shape(l->ReferenceSurface(), surface_shell)) { - Logger::Error("Failed to convert reference surface", l); - return false; - } - if (count(surface_shell, TopAbs_FACE) != 1) { - Logger::Error("Non-continuous reference surface", l); - return false; - } - surface_face = TopoDS::Face(TopExp_Explorer(surface_shell, TopAbs_FACE).Current()); - } - - gp_Trsf trsf; - bool has_position = true; -#ifdef SCHEMA_IfcSweptAreaSolid_Position_IS_OPTIONAL - has_position = l->Position() != nullptr; -#endif - if (has_position) { - IfcGeom::Kernel::convert(l->Position(), trsf); - } - - if (!convert_face(l->SweptArea(), face) || - !convert_wire(l->Directrix(), wire) ) { - return false; - } - - gp_Pln pln; - gp_Pnt directrix_origin; - gp_Vec directrix_tangent; - bool directrix_on_plane = is_plane; - - if (is_plane) { - IfcGeom::Kernel::convert((IfcSchema::IfcPlane*) l->ReferenceSurface(), pln); - - // As per Informal propositions 2: The Directrix shall lie on the ReferenceSurface. - // This is not always the case with the test files in the repository. I am not sure - // how to deal with this and whether my interpretation of the propositions is - // correct. However, if it has been asserted that the vertices of the directrix do - // not conform to the ReferenceSurface, the ReferenceSurface is ignored. - { - for (TopExp_Explorer exp(wire, TopAbs_VERTEX); exp.More(); exp.Next()) { - if (pln.Distance(BRep_Tool::Pnt(TopoDS::Vertex(exp.Current()))) > ALMOST_ZERO) { - directrix_on_plane = false; - Logger::Message(Logger::LOG_WARNING, "The Directrix does not lie on the ReferenceSurface", l); - break; - } - } - } - } - - { - TopExp_Explorer exp(wire, TopAbs_EDGE); - TopoDS_Edge edge = TopoDS::Edge(exp.Current()); - double u0, u1; - Handle(Geom_Curve) crv = BRep_Tool::Curve(edge, u0, u1); - crv->D1(u0, directrix_origin, directrix_tangent); - } - - if (is_plane && pln.Axis().Direction().IsNormal(directrix_tangent, Precision::Approximation()) && directrix_on_plane) { - directrix.SetTransformation(gp_Ax3(directrix_origin, directrix_tangent, pln.Axis().Direction()), gp::XOY()); - } else if (!is_plane) { - ShapeAnalysis_Surface sas(BRep_Tool::Surface(surface_face)); - auto pnt2d = sas.ValueOfUV(directrix_origin, getValue(GV_PRECISION) * 10.); - BRepGProp_Face prop(surface_face); - gp_Pnt _; - gp_Vec surface_normal; - prop.Normal(pnt2d.X(), pnt2d.Y(), _, surface_normal); - directrix.SetTransformation(gp_Ax3(directrix_origin, directrix_tangent, surface_normal), gp::XOY()); - } else { - directrix.SetTransformation(gp_Ax3(directrix_origin, directrix_tangent), gp::XOY()); - } - face = BRepBuilderAPI_Transform(face, directrix); - - if (!is_plane) { - TopExp_Explorer exp(wire, TopAbs_EDGE); - for (; exp.More(); exp.Next()) { - ShapeFix_Edge sfe; - sfe.FixAddPCurve(TopoDS::Edge(exp.Current()), surface_face, false, getValue(GV_PRECISION)); - } - } - - // NB: Note that StartParam and EndParam param are ignored and the assumption is - // made that the parametric range over which to be swept matches the IfcCurve in - // its entirety. - BRepOffsetAPI_MakePipeShell builder(wire); - - { TopExp_Explorer exp(face, TopAbs_WIRE); - section = TopoDS::Wire(exp.Current()); } - - builder.Add(section); - builder.SetTransitionMode(BRepBuilderAPI_RightCorner); - if (directrix_on_plane) { - builder.SetMode(pln.Axis().Direction()); - } else if (!is_plane) { - builder.SetMode(surface_face); - } - builder.Build(); - builder.MakeSolid(); - shape = builder.Shape(); - - if (has_position) { - // IfcSweptAreaSolid.Position (trsf) is an IfcAxis2Placement3D - // and therefore has a unit scale factor - shape.Move(trsf); - } - - return true; -} - -namespace { - bool 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; - } -} - -namespace { - bool 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 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 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 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 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 process_sweep_as_pipe(const TopoDS_Wire& wire, const TopoDS_Wire& section, TopoDS_Shape& result, bool force_transformed=false) { - // 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 sort_edges(const TopoDS_Wire& wire, std::vector& 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 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 break_closed(const TopoDS_Wire& wire, std::vector& wires) { - std::vector 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 segment_adjacent_non_linear(const TopoDS_Wire& wire, std::vector& wires) { - std::vector 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 process_sweep(const TopoDS_Wire& wire, double radius, TopoDS_Shape& result) { - std::vector 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); - */ - } -} - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcSweptDiskSolid* l, TopoDS_Shape& shape) { - TopoDS_Wire wire, section1, section2; - - bool hasInnerRadius = !!l->InnerRadius(); - - if (!convert_wire(l->Directrix(), wire)) { - return false; - } - - // Start- EndParam became optional in IFC4 -#ifdef SCHEMA_IfcSweptDiskSolid_StartParam_IS_OPTIONAL - auto sp = l->StartParam(); - auto ep = l->EndParam(); -#else - boost::optional sp, ep; - sp = l->StartParam(); - ep = l->EndParam(); -#endif - - if (count(wire, TopAbs_EDGE) == 1 && sp && ep) { - TopoDS_Vertex v0, v1; - TopExp::Vertices(wire, v0, v1); - if (v0.IsSame(v1)) { - TopExp_Explorer exp(wire, TopAbs_EDGE); - auto& e = TopoDS::Edge(exp.Current()); - double a, b; - auto crv = BRep_Tool::Curve(e, a, b); - if ((crv->DynamicType() == STANDARD_TYPE(Geom_Circle)) || - (crv->DynamicType() == STANDARD_TYPE(Geom_Ellipse))) - { - BRepBuilderAPI_MakeEdge me(crv, *sp, *ep); - if (me.IsDone()) { - auto e2 = me.Edge(); - BRep_Builder B; - wire.Nullify(); - B.MakeWire(wire); - B.Add(wire, e2); - } - } - } - } - - // NB: Note that StartParam and EndParam param are ignored and the assumption is - // made that the parametric range over which to be swept matches the IfcCurve in - // its entirety. - - process_sweep(wire, l->Radius() * getValue(GV_LENGTH_UNIT), shape); - - if (shape.IsNull()) { - return false; - } - - double r2 = 0.; - - if (hasInnerRadius) { - // Subtraction of pipes with small radii is unstable. - r2 = *l->InnerRadius() * getValue(GV_LENGTH_UNIT); - } - - if (r2 > getValue(GV_PRECISION) * 10.) { - TopoDS_Shape inner; - process_sweep(wire, r2, inner); - - bool is_valid = false; - - // Boolean op on the compound of separately processed sweeps - // is not attempted. - // @todo iterate over compound subshapes and process boolean - // separately. - // @todo don't process as boolean op at all, since we know - // only the start and end faces intersect and we know they - // are co-planar and we know they are circles. - if (shape.ShapeType() != TopAbs_COMPOUND) { - BRepAlgoAPI_Cut brep_cut(shape, inner); - if (brep_cut.IsDone()) { - TopoDS_Shape result = brep_cut; - - ShapeFix_Shape fix(result); - fix.Perform(); - result = fix.Shape(); - - is_valid = BRepCheck_Analyzer(result).IsValid() != 0; - if (is_valid) { - shape = result; - } - } - } - - if (!is_valid) { - Logger::Message(Logger::LOG_WARNING, "Failed to subtract inner radius void for:", l); - } - } - - return true; -} - -#ifdef SCHEMA_HAS_IfcCylindricalSurface - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcCylindricalSurface* l, TopoDS_Shape& face) { - gp_Trsf trsf; - IfcGeom::Kernel::convert(l->Position(),trsf); - - // IfcElementarySurface.Position has unit scale factor - face = BRepBuilderAPI_MakeFace(new Geom_CylindricalSurface(gp::XOY(), l->Radius() * getValue(GV_LENGTH_UNIT)), getValue(GV_PRECISION)).Face().Moved(trsf); - return true; -} - -#endif - -#ifdef SCHEMA_HAS_IfcSphericalSurface - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcSphericalSurface* l, TopoDS_Shape& face) { - gp_Trsf trsf; - IfcGeom::Kernel::convert(l->Position(), trsf); - - // IfcElementarySurface.Position has unit scale factor - face = BRepBuilderAPI_MakeFace(new Geom_SphericalSurface(gp::XOY(), l->Radius() * getValue(GV_LENGTH_UNIT)), getValue(GV_PRECISION)).Face().Moved(trsf); - return true; -} - -#endif - -#ifdef SCHEMA_HAS_IfcToroidalSurface - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcToroidalSurface* l, TopoDS_Shape& face) { - gp_Trsf trsf; - IfcGeom::Kernel::convert(l->Position(), trsf); - - // IfcElementarySurface.Position has unit scale factor - face = BRepBuilderAPI_MakeFace(new Geom_ToroidalSurface(gp::XOY(), l->MajorRadius() * getValue(GV_LENGTH_UNIT), l->MinorRadius() * getValue(GV_LENGTH_UNIT)), getValue(GV_PRECISION)).Face().Moved(trsf); - return true; -} - -#endif - -#ifdef SCHEMA_HAS_IfcAdvancedBrep - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcAdvancedBrep* l, TopoDS_Shape& shape) { - return convert(l->Outer(), shape); -} - -#endif - -#ifdef SCHEMA_HAS_IfcTriangulatedFaceSet - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcTriangulatedFaceSet* l, TopoDS_Shape& shape) { - IfcSchema::IfcCartesianPointList3D* point_list = l->Coordinates(); - auto coord_list = point_list->CoordList(); - std::vector> indices = l->CoordIndex(); - - faceset_helper< - std::vector, - std::vector - > helper(this, coord_list, indices, l->Closed().get_value_or(false)); - - TopTools_ListOfShape faces; - - for (auto it = indices.begin(); it != indices.end(); ++it) { - TopoDS_Wire w; - if (helper.wire(*it, w)) { - BRepBuilderAPI_MakeFace mf(w); - if (mf.IsDone()) { - faces.Append(mf.Face()); - } - } - } - - if (faces.Extent() > getValue(GV_MAX_FACES_TO_ORIENT) || !create_solid_from_faces(faces, shape)) { - TopoDS_Compound compound; - BRep_Builder builder; - builder.MakeCompound(compound); - - TopTools_ListIteratorOfListOfShape face_iterator; - for (face_iterator.Initialize(faces); face_iterator.More(); face_iterator.Next()) { - builder.Add(compound, face_iterator.Value()); - } - shape = compound; - } - - return true; -} - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcPolygonalFaceSet* pfs, TopoDS_Shape& shape) { - IfcSchema::IfcCartesianPointList3D* point_list = pfs->Coordinates(); - auto coord_list = point_list->CoordList(); - auto polygonal_faces = pfs->Faces(); - - std::vector> indices; - indices.reserve(polygonal_faces->size() * 2); - - std::vector> loop_grouping; - loop_grouping.reserve(polygonal_faces->size()); - - for (auto& f : *polygonal_faces) { - loop_grouping.emplace_back(); - loop_grouping.back().push_back(indices.size()); - indices.push_back(f->CoordIndex()); - if (f->as()) { - auto inner_coordinates = f->as()->InnerCoordIndices(); - for (auto& x : inner_coordinates) { - loop_grouping.back().push_back(indices.size()); - indices.push_back(x); - } - } - } - - faceset_helper< - std::vector, - std::vector - > helper(this, coord_list, indices, pfs->Closed().get_value_or(false)); - - TopTools_ListOfShape faces; - - for (auto& f : loop_grouping) { - bool not_planar = false; - - TopoDS_Wire w; - if (!helper.wire(indices[f[0]], w)) { - continue; - } - - TopoDS_Face face; - std::vector ws = { w }; - - // @todo triangulate - BRepBuilderAPI_MakeFace mf(w); - if (mf.Error() == BRepBuilderAPI_NotPlanar) { - not_planar = true; - } else if (mf.IsDone()) { - face = mf.Face(); - } else { - // todo log - continue; - } - - if (f.size() > 1) { - - if (not_planar) { - for (auto it = f.begin() + 1; it != f.end(); ++it) { - TopoDS_Wire w2; - if (helper.wire(indices[*it], w2)) { - ws.push_back(w2); - } - } - } else { - BRepBuilderAPI_MakeFace mf2(face); - for (auto it = f.begin() + 1; it != f.end(); ++it) { - TopoDS_Wire w2; - if (helper.wire(indices[*it], w2)) { - mf2.Add(w2); - ws.push_back(w2); - } - - } - - if (mf2.Error() == BRepBuilderAPI_NotPlanar) { - not_planar = true; - } else if (mf2.IsDone()) { - face = mf2.Face(); - } - } - - } - - if (not_planar) { - TopTools_ListOfShape fs; - if (triangulate_wire(ws, fs)) { - Logger::Warning("Triangulated face boundary:", pfs); - TopTools_ListIteratorOfListOfShape it(fs); - for (; it.More(); it.Next()) { - const TopoDS_Face& tri = TopoDS::Face(it.Value()); - if (face_area(tri) > getValue(GV_MINIMAL_FACE_AREA)) { - faces.Append(tri); - } - } - } - } else { - faces.Append(face); - } - } - - if (faces.Extent() > getValue(GV_MAX_FACES_TO_ORIENT) || !create_solid_from_faces(faces, shape)) { - TopoDS_Compound compound; - BRep_Builder builder; - builder.MakeCompound(compound); - - TopTools_ListIteratorOfListOfShape face_iterator; - for (face_iterator.Initialize(faces); face_iterator.More(); face_iterator.Next()) { - builder.Add(compound, face_iterator.Value()); - } - shape = compound; - } - - return true; -} - -#endif diff --git a/src/ifcgeom/IfcGeomWires.cpp b/src/ifcgeom/IfcGeomWires.cpp deleted file mode 100644 index c1bb274c1b..0000000000 --- a/src/ifcgeom/IfcGeomWires.cpp +++ /dev/null @@ -1,1086 +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 . * - * * - ********************************************************************************/ - -/******************************************************************************** - * * - * Implementations of the various conversion functions defined in mapping.i * - * * - ********************************************************************************/ - -#define _USE_MATH_DEFINES -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include - -#include -#if OCC_VERSION_HEX < 0x70600 -#include -#endif - -#include - -#include "../ifcgeom/IfcGeom.h" - -#define Kernel MAKE_TYPE_NAME(Kernel) - -namespace { - // Returns the first edge of a wire - 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(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(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(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(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 - 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; - } - -#ifdef SCHEMA_HAS_IfcSegment - // 4x3 - IfcSchema::IfcSegment::list::ptr segments = l->Segments(); -#else - IfcSchema::IfcCompositeCurveSegment::list::ptr segments = l->Segments(); -#endif - - TopTools_ListOfShape converted_segments; - - for (auto it = segments->begin(); it != segments->end(); ++it) { - - if (!(*it)->declaration().is(IfcSchema::IfcCompositeCurveSegment::Class())) { - Logger::Error("Not implemented", *it); - return false; - } - - IfcSchema::IfcCurve* curve = ((IfcSchema::IfcCompositeCurveSegment*)(*it))->ParentCurve(); - - // The type of ParentCurve is IfcCurve, but the documentation says: - // ParentCurve: The *bounded curve* which defines the geometry of the segment. - // At least let's exclude IfcLine as an infinite linear segment - // definitely does not make any sense. - TopoDS_Wire segment; - - if (curve->as()) { - Logger::Notice("Infinite IfcLine used as ParentCurve of segment, treating as a segment", *it); - Handle_Geom_Curve handle; - convert_curve(curve, handle); - double u0 = 0.0; - double u1 = curve->as()->Dir()->Magnitude() * getValue(GV_LENGTH_UNIT); - if (u1 < getValue(GV_PRECISION)) { - Logger::Warning("Segment length below tolerance", *it); - } - BRepBuilderAPI_MakeEdge me(handle, u0, u1); - if (me.IsDone()) { - BRep_Builder B; - B.MakeWire(segment); - B.Add(segment, me.Edge()); - } - } else if (!convert_wire(curve, segment)) { - const bool failed_on_purpose = curve->as() && !segment.IsNull(); - Logger::Message(failed_on_purpose ? Logger::LOG_WARNING : Logger::LOG_ERROR, "Failed to convert curve:", curve); - continue; - } - - if (!((IfcSchema::IfcCompositeCurveSegment*)(*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); - - aggregate_of_instance::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; -#if OCC_VERSION_HEX < 0x70600 - BRepAdaptor_CompCurve cc(w, true); - Handle(Adaptor3d_HCurve) hcc = Handle(Adaptor3d_HCurve)(new BRepAdaptor_HCompCurve(cc)); -#else - auto hcc = new BRepAdaptor_CompCurve(w, true); -#endif - // @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; - aggregate_of_instance::ptr trims1 = l->Trim1(); - aggregate_of_instance::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 ( aggregate_of_instance::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 ( aggregate_of_instance::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(); - // BRepBuilderAPI_MakeEdge swaps v1 and v2 if the parameter value of v2 is - // smaller than that of v1. In that case the edge has to be reversed so that - // the vertex order is consistent with Trim1 and Trim2. Otherwise the - // IfcOpenShell wire builder will create intermediate edges automatically. - // The alternative would be to reverse the underlying curve instead. - if (!TopExp::FirstVertex(e, true).IsSame(v1)) { - e.Reverse(); - } - } - } - - 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(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(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.; - } - } - - double radius = 1.0; - if (curve->DynamicType() == STANDARD_TYPE(Geom_Circle)) { - auto circle_curve = Handle_Geom_Circle::DownCast(curve); - radius = circle_curve->Radius(); - } else if (curve->DynamicType() == STANDARD_TYPE(Geom_Ellipse)) { - auto circle_curve = Handle_Geom_Ellipse::DownCast(curve); - radius = (circle_curve->MajorRadius() + circle_curve->MinorRadius()) / 2.; - } - - // Fix from @sanderboer to compare using model tolerance, see #744 - // Made dependent on radius, see #928 - - // A good critereon for determining whether to take full curve - // or trimmed segment would be whether there are other curve segments or this - // is the only one. - boost::optional num_segments; - auto segment = l->data().getInverse(&IfcSchema::IfcCompositeCurveSegment::Class(), -1); - if (segment->size() == 1) { - auto comp = (*segment->begin())->data().getInverse(&IfcSchema::IfcCompositeCurve::Class(), -1); - if (comp->size() == 1) { - num_segments = (*comp->begin())->as()->Segments()->size(); - } - } - - // @todo is 100. not too much? Check with the original issue. - const double precision_markup = getValue(IfcGeom::Kernel::GV_PRECISION_FACTOR) == 1. ? 1. : 100.; - - if (isConic && ALMOST_THE_SAME(fmod(flts[1]-flts[0],M_PI*2.), 0., precision_markup * getValue(GV_PRECISION) / (2 * M_PI * radius))) { - e = BRepBuilderAPI_MakeEdge(curve).Edge(); - } else { - BRepBuilderAPI_MakeEdge me (curve,flts[0],flts[1]); - e = me.Edge(); - } - - if (num_segments && *num_segments > 1) { - TopoDS_Vertex v0, v1; - TopExp::Vertices(e, v0, v1); - if (v0.IsSame(v1)) { - Logger::Warning("Skipping degenerate segment", l); - return false; - } - } - - } else if ( trim_cartesian_failed && (has_pnts[0] && has_pnts[1]) ) { - e = BRepBuilderAPI_MakeEdge(pnts[0], pnts[1]).Edge(); - } - - if (e.IsNull()) { - return false; - } - - 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 in deflection_for_approximating_circle() is for circles, but probably good enough - radius = Handle(Geom_Ellipse)::DownCast(crv)->MajorRadius(); - } - if (radius > 0. && deflection_for_approximating_circle(radius, b - a) < 100 * getValue(GV_PRECISION) && std::abs(b-a) < M_PI/4.) { - 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); - } - - // @todo the strict tolerance should also govern these arbitrary precision increases - 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) { - // We somehow need to signal we fail this curve on purpose not to trigger an error. - BRep_Builder B; - B.MakeWire(result); - 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(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); -} - -#include - -namespace { - 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) { - 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::infinity(); - for (int j = 1; j <= n; j++) { - const double d = extrema.SquareDistance(j); - if (d < dmin) { - dmin = d; - } - } - if (dmin == std::numeric_limits::infinity()) { - Logger::Error("No extrema for point"); - } else if (dmin > eps2) { - Logger::Error("Distance of " + boost::lexical_cast(std::sqrt(dmin)) + " exceeds tolerance"); - } - } else { - Logger::Error("Failed to calculate extrema for point"); - } - } - } - return false; - } - result = me.Edge(); - return true; - } -} - -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)) { - TopoDS_Edge e; - if (create_edge_over_curve_with_log_messages(crv, getValue(GV_PRECISION), p1, p2, e)) { - mw.Add(e); - result = mw; - return true; - } else { - return false; - } - } 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; - } - - TopoDS_Edge e; - if (create_edge_over_curve_with_log_messages(ecrv, getValue(GV_PRECISION), a, b, e)) { - mw.Add(e); - } else { - return false; - } - - 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 > coordinates; - if (point_list->as()) { - coordinates = point_list->as()->CoordList(); - } else if (point_list->as()) { - coordinates = point_list->as()->CoordList(); - } - - std::vector points; - points.reserve(coordinates.size()); - for (std::vector< std::vector >::const_iterator it = coordinates.begin(); it != coordinates.end(); ++it) { - const std::vector& 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; - - // ignored, just for capturing the curve parameters on the null check - double u, v; - - if(l->Segments()) { - aggregate_of_instance::ptr segments = *l->Segments(); - for (aggregate_of_instance::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 indices = *line; - gp_Pnt previous; - for (std::vector::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(*jt)); - } - const gp_Pnt& current = points[*jt - 1]; - if (jt != indices.begin()) { - BRepBuilderAPI_MakeEdge me(previous, current); - if (me.IsDone() && !BRep_Tool::Curve(me.Edge(), u, v).IsNull()) { - w.Add(me.Edge()); - } else { - Logger::Warning("Ignoring segment on", l); - } - } - previous = current; - } - } else if (segment->declaration().is(IfcSchema::IfcArcIndex::Class())) { - IfcSchema::IfcArcIndex* arc = (IfcSchema::IfcArcIndex*) segment; - std::vector 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(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(); - BRepBuilderAPI_MakeEdge me(circ, a, c); - if (me.IsDone() && !BRep_Tool::Curve(me.Edge(), u, v).IsNull()) { - w.Add(me.Edge()); - } else { - Logger::Warning("Ignoring segment on", l); - } - } else { - throw IfcParse::IfcException("Unexpected IfcIndexedPolyCurve segment of type " + segment->declaration().name()); - } - } - } else if (points.begin() < points.end()) { - std::vector::const_iterator previous = points.begin(); - for (std::vector::const_iterator current = previous+1; current < points.end(); ++current){ - BRepBuilderAPI_MakeEdge me(*previous, *current); - if (me.IsDone() && !BRep_Tool::Curve(me.Edge(), u, v).IsNull()) { - w.Add(me.Edge()); - previous = current; - } - } - } - - result = w.Wire(); - return true; -} - -#endif diff --git a/src/ifcgeom/IfcGeometricSet.cpp b/src/ifcgeom/IfcGeometricSet.cpp new file mode 100644 index 0000000000..5e4c56270f --- /dev/null +++ b/src/ifcgeom/IfcGeometricSet.cpp @@ -0,0 +1,140 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include +#include + +#define Kernel MAKE_TYPE_NAME(Kernel) + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcGeometricSet* l, IfcRepresentationShapeItems& shapes) { + // @nb the selection is partly duplicated from convert_curves() but it's needed as a + // geometric set by it's static class definition does not inform us of the type of elements. + // @todo handle this better so that this doesn't log an error. + const bool include_curves = getValue(GV_DIMENSIONALITY) != +1; + const bool include_solids_and_surfaces = getValue(GV_DIMENSIONALITY) != -1; + + aggregate_of_instance::ptr elements = l->Elements(); + if ( !elements->size() ) return false; + bool part_succes = false; + auto parent_style = get_style(l); + for (aggregate_of_instance::it it = elements->begin(); it != elements->end(); ++it) { + auto element = *it; + TopoDS_Shape s; + if (shape_type(element) == ST_SHAPELIST) { + IfcRepresentationShapeItems items; + if (!(convert_shapes(element, items) && flatten_shape_list(items, s, false))) { + continue; + } + } else if (shape_type(element) == ST_SHAPE && include_solids_and_surfaces) { + if (!convert_shape(element, s)) { + continue; + } + } else if (shape_type(element) == ST_WIRE && include_curves) { + TopoDS_Wire w; + if (!convert_wire(element, w)) { + continue; + } + s = w; + } else { + continue; + } + + part_succes = true; + decltype(parent_style) style = 0; + if (element->declaration().is(IfcSchema::IfcPoint::Class())) { + style = get_style((IfcSchema::IfcPoint*) element); + } + else if (element->declaration().is(IfcSchema::IfcCurve::Class())) { + style = get_style((IfcSchema::IfcCurve*) element); + } + else if (element->declaration().is(IfcSchema::IfcSurface::Class())) { + style = get_style((IfcSchema::IfcSurface*) element); + } + shapes.push_back(IfcRepresentationShapeItem(l->data().id(), s, style ? style : parent_style)); + } + return part_succes; +} diff --git a/src/ifcgeom/IfcHalfSpaceSolid.cpp b/src/ifcgeom/IfcHalfSpaceSolid.cpp new file mode 100644 index 0000000000..d196b37d4c --- /dev/null +++ b/src/ifcgeom/IfcHalfSpaceSolid.cpp @@ -0,0 +1,104 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include +#include + +#define Kernel MAKE_TYPE_NAME(Kernel) + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcHalfSpaceSolid* l, TopoDS_Shape& shape) { + IfcSchema::IfcSurface* surface = l->BaseSurface(); + if ( ! surface->declaration().is(IfcSchema::IfcPlane::Class()) ) { + Logger::Message(Logger::LOG_ERROR, "Unsupported BaseSurface:", surface); + return false; + } + gp_Pln pln; + IfcGeom::Kernel::convert((IfcSchema::IfcPlane*)surface,pln); + const gp_Pnt pnt = pln.Location().Translated( l->AgreementFlag() ? -pln.Axis().Direction() : pln.Axis().Direction()); + shape = BRepPrimAPI_MakeHalfSpace(BRepBuilderAPI_MakeFace(pln),pnt).Solid(); + return true; +} diff --git a/src/ifcgeom/IfcIShapeProfileDef.cpp b/src/ifcgeom/IfcIShapeProfileDef.cpp new file mode 100644 index 0000000000..93b61a9387 --- /dev/null +++ b/src/ifcgeom/IfcIShapeProfileDef.cpp @@ -0,0 +1,138 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include +#include +#include +#include + +#define Kernel MAKE_TYPE_NAME(Kernel) + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcIShapeProfileDef* l, TopoDS_Shape& 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->FilletRadius(); + double f1 = 0.; + if ( doFillet1 ) { + f1 = *l->FilletRadius() * getValue(GV_LENGTH_UNIT); + } + + bool doFillet2 = doFillet1; + double x2 = x1, dy2 = dy1, f2 = f1; + + // @todo in IFC4 a IfcAsymmetricIShapeProfileDef is not a subtype anymore of IfcIShapeProfileDef! + if (l->declaration().is(IfcSchema::IfcAsymmetricIShapeProfileDef::Class())) { + IfcSchema::IfcAsymmetricIShapeProfileDef* assym = (IfcSchema::IfcAsymmetricIShapeProfileDef*) l; + x2 = assym->TopFlangeWidth() / 2. * getValue(GV_LENGTH_UNIT); + doFillet2 = !!assym->TopFlangeFilletRadius(); + if (doFillet2) { + f2 = *assym->TopFlangeFilletRadius() * getValue(GV_LENGTH_UNIT); + } + if (assym->TopFlangeThickness()) { + 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; + } + + gp_Trsf2d trsf2d; + bool has_position = true; +#ifdef SCHEMA_IfcParameterizedProfileDef_Position_IS_OPTIONAL + has_position = l->Position() != nullptr; +#endif + if (has_position) { + IfcGeom::Kernel::convert(l->Position(), trsf2d); + } + + double coords[24] = {-x1,-y, x1,-y, x1,-y+dy1, d1,-y+dy1, d1,y-dy2, x2,y-dy2, x2,y, -x2,y, -x2,y-dy2, -d1,y-dy2, -d1,-y+dy1, -x1,-y+dy1}; + int fillets[4] = {3,4,9,10}; + double radii[4] = {f1,f2,f2,f1}; + return profile_helper(12,coords,(doFillet1||doFillet2) ? 4 : 0,fillets,radii,trsf2d,face); +} diff --git a/src/ifcgeom/IfcIndexedPolyCurve.cpp b/src/ifcgeom/IfcIndexedPolyCurve.cpp new file mode 100644 index 0000000000..889b1e0ed4 --- /dev/null +++ b/src/ifcgeom/IfcIndexedPolyCurve.cpp @@ -0,0 +1,178 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include + +#define _USE_MATH_DEFINES +#define Kernel MAKE_TYPE_NAME(Kernel) + +#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 > coordinates; + if (point_list->as()) { + coordinates = point_list->as()->CoordList(); + } else if (point_list->as()) { + coordinates = point_list->as()->CoordList(); + } + + std::vector points; + points.reserve(coordinates.size()); + for (std::vector< std::vector >::const_iterator it = coordinates.begin(); it != coordinates.end(); ++it) { + const std::vector& 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; + + // ignored, just for capturing the curve parameters on the null check + double u, v; + + if(l->Segments()) { + aggregate_of_instance::ptr segments = *l->Segments(); + for (aggregate_of_instance::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 indices = *line; + gp_Pnt previous; + for (std::vector::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(*jt)); + } + const gp_Pnt& current = points[*jt - 1]; + if (jt != indices.begin()) { + BRepBuilderAPI_MakeEdge me(previous, current); + if (me.IsDone() && !BRep_Tool::Curve(me.Edge(), u, v).IsNull()) { + w.Add(me.Edge()); + } else { + Logger::Warning("Ignoring segment on", l); + } + } + previous = current; + } + } else if (segment->declaration().is(IfcSchema::IfcArcIndex::Class())) { + IfcSchema::IfcArcIndex* arc = (IfcSchema::IfcArcIndex*) segment; + std::vector 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(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(); + BRepBuilderAPI_MakeEdge me(circ, a, c); + if (me.IsDone() && !BRep_Tool::Curve(me.Edge(), u, v).IsNull()) { + w.Add(me.Edge()); + } else { + Logger::Warning("Ignoring segment on", l); + } + } else { + throw IfcParse::IfcException("Unexpected IfcIndexedPolyCurve segment of type " + segment->declaration().name()); + } + } + } else if (points.begin() < points.end()) { + std::vector::const_iterator previous = points.begin(); + for (std::vector::const_iterator current = previous+1; current < points.end(); ++current){ + BRepBuilderAPI_MakeEdge me(*previous, *current); + if (me.IsDone() && !BRep_Tool::Curve(me.Edge(), u, v).IsNull()) { + w.Add(me.Edge()); + previous = current; + } + } + } + + result = w.Wire(); + return true; +} + +#endif diff --git a/src/ifcgeom/IfcLShapeProfileDef.cpp b/src/ifcgeom/IfcLShapeProfileDef.cpp new file mode 100644 index 0000000000..edaaf1551a --- /dev/null +++ b/src/ifcgeom/IfcLShapeProfileDef.cpp @@ -0,0 +1,165 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include +#include +#include +#include + +#define Kernel MAKE_TYPE_NAME(Kernel) + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcLShapeProfileDef* l, TopoDS_Shape& face) { + const bool hasSlope = !!l->LegSlope(); + const bool doEdgeFillet = !!l->EdgeRadius(); + const bool doFillet = !!l->FilletRadius(); + + const double y = l->Depth() / 2.0f * getValue(GV_LENGTH_UNIT); + const double x = l->Width().get_value_or(l->Depth()) / 2.0f * getValue(GV_LENGTH_UNIT); + const double d = l->Thickness() * getValue(GV_LENGTH_UNIT); + const double slope = l->LegSlope().get_value_or(0.) * getValue(GV_PLANEANGLE_UNIT); + + 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; + } + + gp_Trsf2d trsf2d; + bool has_position = true; +#ifdef SCHEMA_IfcParameterizedProfileDef_Position_IS_OPTIONAL + has_position = l->Position() != nullptr; +#endif + if (has_position) { + IfcGeom::Kernel::convert(l->Position(), trsf2d); + } + + double coords[12] = {-x,-y, x,-y, x,-y+d-dy1, xx, xy, -x+d-dx1,y, -x,y}; + int fillets[3] = {2,3,4}; + double radii[3] = {f2,f1,f2}; + return profile_helper(6,coords,doFillet ? 3 : 0,fillets,radii,trsf2d,face); +} diff --git a/src/ifcgeom/IfcLine.cpp b/src/ifcgeom/IfcLine.cpp new file mode 100644 index 0000000000..53aedc57c2 --- /dev/null +++ b/src/ifcgeom/IfcLine.cpp @@ -0,0 +1,77 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include + +#define Kernel MAKE_TYPE_NAME(Kernel) + +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; +} diff --git a/src/ifcgeom/IfcManifoldSolidBrep.cpp b/src/ifcgeom/IfcManifoldSolidBrep.cpp new file mode 100644 index 0000000000..5e623b8c8b --- /dev/null +++ b/src/ifcgeom/IfcManifoldSolidBrep.cpp @@ -0,0 +1,122 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include +#include + +#define Kernel MAKE_TYPE_NAME(Kernel) + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcManifoldSolidBrep* l, IfcRepresentationShapeItems& shape) { + TopoDS_Shape s; + auto collective_style = get_style(l); + if (convert_shape(l->Outer(),s) ) { + auto indiv_style = get_style(l->Outer()); + + IfcSchema::IfcClosedShell::list::ptr voids(new IfcSchema::IfcClosedShell::list); + if (l->declaration().is(IfcSchema::IfcFacetedBrepWithVoids::Class())) { + voids = l->as()->Voids(); + } +#ifdef SCHEMA_HAS_IfcAdvancedBrepWithVoids + if (l->declaration().is(IfcSchema::IfcAdvancedBrepWithVoids::Class())) { + voids = l->as()->Voids(); + } +#endif + + for (IfcSchema::IfcClosedShell::list::it it = voids->begin(); it != voids->end(); ++it) { + TopoDS_Shape s2; + /// @todo No extensive shapefixing since shells should be disjoint. + /// @todo Awaiting generalized boolean ops module with appropriate checking + if (convert_shape(l->Outer(), s2)) { + s = BRepAlgoAPI_Cut(s, s2).Shape(); + } + } + + shape.push_back(IfcRepresentationShapeItem(l->data().id(), s, indiv_style ? indiv_style : collective_style)); + return true; + } + return false; +} diff --git a/src/ifcgeom/IfcMappedItem.cpp b/src/ifcgeom/IfcMappedItem.cpp new file mode 100644 index 0000000000..554f8df596 --- /dev/null +++ b/src/ifcgeom/IfcMappedItem.cpp @@ -0,0 +1,138 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include +#include + +#define Kernel MAKE_TYPE_NAME(Kernel) + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcMappedItem* l, IfcRepresentationShapeItems& shapes) { + gp_GTrsf gtrsf; + IfcSchema::IfcCartesianTransformationOperator* transform = l->MappingTarget(); + if ( transform->declaration().is(IfcSchema::IfcCartesianTransformationOperator3DnonUniform::Class()) ) { + IfcGeom::Kernel::convert((IfcSchema::IfcCartesianTransformationOperator3DnonUniform*)transform,gtrsf); + } else if ( transform->declaration().is(IfcSchema::IfcCartesianTransformationOperator2DnonUniform::Class()) ) { + Logger::Message(Logger::LOG_ERROR, "Unsupported MappingTarget:", transform); + return false; + } else if ( transform->declaration().is(IfcSchema::IfcCartesianTransformationOperator3D::Class()) ) { + gp_Trsf trsf; + IfcGeom::Kernel::convert((IfcSchema::IfcCartesianTransformationOperator3D*)transform,trsf); + gtrsf = trsf; + } else if ( transform->declaration().is(IfcSchema::IfcCartesianTransformationOperator2D::Class()) ) { + gp_Trsf2d trsf_2d; + IfcGeom::Kernel::convert((IfcSchema::IfcCartesianTransformationOperator2D*)transform,trsf_2d); + gtrsf = (gp_Trsf) trsf_2d; + } + IfcSchema::IfcRepresentationMap* map = l->MappingSource(); + IfcSchema::IfcAxis2Placement* placement = map->MappingOrigin(); + gp_Trsf trsf; + if (placement->declaration().is(IfcSchema::IfcAxis2Placement3D::Class())) { + IfcGeom::Kernel::convert((IfcSchema::IfcAxis2Placement3D*)placement,trsf); + } else { + gp_Trsf2d trsf_2d; + IfcGeom::Kernel::convert((IfcSchema::IfcAxis2Placement2D*)placement,trsf_2d); + trsf = trsf_2d; + } + gtrsf.Multiply(trsf); + + auto 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 ) { + shapes[i].prepend(gtrsf); + + // 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; +} diff --git a/src/ifcgeom/IfcObjectPlacement.cpp b/src/ifcgeom/IfcObjectPlacement.cpp new file mode 100644 index 0000000000..096b79419b --- /dev/null +++ b/src/ifcgeom/IfcObjectPlacement.cpp @@ -0,0 +1,118 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" + +#define Kernel MAKE_TYPE_NAME(Kernel) + +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->as()) { + IfcGeom::Kernel::convert(relplacement->as(),trsf2); + trsf.PreMultiply(trsf2); + } + + if (current->PlacementRelTo()) { + IfcSchema::IfcObjectPlacement* parent = current->PlacementRelTo(); + + bool parent_placement_ignored = false; + if (placement_rel_to_type_ || placement_rel_to_instance_) { + IfcSchema::IfcProduct::list::ptr parent_places = parent->PlacesObject(); + for (auto iter = parent_places->begin(); iter != parent_places->end(); ++iter) { + if ((placement_rel_to_type_ && (*iter)->declaration().is(*placement_rel_to_type_)) || + (placement_rel_to_instance_ && (*iter)->as() == placement_rel_to_instance_)) { + parent_placement_ignored = true; + } + } + } + + if (parent_placement_ignored) { + // The parent placement of the current is a placement for a type that is + // being ignored (Site or Building) or it is the host element of an opening. + break; + } else if (parent->declaration().is(IfcSchema::IfcLocalPlacement::Class())) { + // Keep processing parent placements + current = current->PlacementRelTo()->as(); + } else { + // This is the root placement (typically Site). + break; + } + } else { + break; + } + } + + trsf.PreMultiply(offset_and_rotation); + + CACHE(IfcObjectPlacement,l,trsf) + return true; +} diff --git a/src/ifcgeom/IfcOrientedEdge.cpp b/src/ifcgeom/IfcOrientedEdge.cpp new file mode 100644 index 0000000000..ffa3b9d0b6 --- /dev/null +++ b/src/ifcgeom/IfcOrientedEdge.cpp @@ -0,0 +1,94 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include + +#define _USE_MATH_DEFINES +#define Kernel MAKE_TYPE_NAME(Kernel) + +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; + } +} diff --git a/src/ifcgeom/IfcPlane.cpp b/src/ifcgeom/IfcPlane.cpp new file mode 100644 index 0000000000..dde9f45893 --- /dev/null +++ b/src/ifcgeom/IfcPlane.cpp @@ -0,0 +1,126 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include +#include +#include +#include +#include + +#define Kernel MAKE_TYPE_NAME(Kernel) +#define Kernel MAKE_TYPE_NAME(Kernel) + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcPlane* l, TopoDS_Shape& face) { + gp_Pln pln; + convert(l, pln); + Handle_Geom_Surface surf = new Geom_Plane(pln); +#if OCC_VERSION_HEX < 0x60502 + face = BRepBuilderAPI_MakeFace(surf); +#else + face = BRepBuilderAPI_MakeFace(surf, getValue(GV_PRECISION)); +#endif + 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; + + if (!l->Location()->declaration().is("IfcCartesianPoint")) { + // only applicable to 4x3 rc2 + Logger::Error("Not implemented", l->Location()); + return false; + } + + IfcGeom::Kernel::convert((const IfcSchema::IfcCartesianPoint*) l->Location(),o); + bool hasRef = !!l->RefDirection(); + if ( l->Axis() ) 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; +} diff --git a/src/ifcgeom/IfcPolyLoop.cpp b/src/ifcgeom/IfcPolyLoop.cpp new file mode 100644 index 0000000000..a8320998f7 --- /dev/null +++ b/src/ifcgeom/IfcPolyLoop.cpp @@ -0,0 +1,133 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include + +#define _USE_MATH_DEFINES +#define Kernel MAKE_TYPE_NAME(Kernel) + +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(results.Extent()) + " cycles detected", l); + select_largest(results, result); + } + + return true; +} diff --git a/src/ifcgeom/IfcPolygonalBoundedHalfSpace.cpp b/src/ifcgeom/IfcPolygonalBoundedHalfSpace.cpp new file mode 100644 index 0000000000..5af0aa011b --- /dev/null +++ b/src/ifcgeom/IfcPolygonalBoundedHalfSpace.cpp @@ -0,0 +1,125 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include +#include + +#define Kernel MAKE_TYPE_NAME(Kernel) + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcPolygonalBoundedHalfSpace* l, TopoDS_Shape& shape) { + TopoDS_Shape halfspace; + if ( ! IfcGeom::Kernel::convert((IfcSchema::IfcHalfSpaceSolid*)l,halfspace) ) return false; + + TopoDS_Wire wire; + if ( ! convert_wire(l->PolygonalBoundary(),wire) || ! wire.Closed() ) return false; + + gp_Trsf trsf; + if ( ! convert(l->Position(),trsf) ) return false; + + TColgp_SequenceOfPnt points; + if (wire_to_sequence_of_point(wire, points)) { + // Boolean subtractions not very robust for narrow operands, + // increase minimal point spacing to eliminate such shapes. + const double t = getValue(GV_PRECISION) * 10.; + remove_duplicate_points_from_loop(points, wire.Closed() != 0, t); // Note: wire always closed, as per if statement above + remove_collinear_points_from_loop(points, wire.Closed() != 0, t); + if (points.Length() < 3) { + Logger::Message(Logger::LOG_ERROR, "Not enough points retained from:", l->PolygonalBoundary()); + return false; + } + sequence_of_point_to_wire(points, wire, wire.Closed() != 0); + } + + TopoDS_Shape prism = BRepPrimAPI_MakePrism(BRepBuilderAPI_MakeFace(wire),gp_Vec(0,0,200)); + gp_Trsf down; down.SetTranslation(gp_Vec(0,0,-100.0)); + + // `trsf` and `down` both have a unit scale factor + prism.Move(trsf*down); + + shape = BRepAlgoAPI_Common(halfspace,prism); + return true; +} diff --git a/src/ifcgeom/IfcPolygonalFaceSet.cpp b/src/ifcgeom/IfcPolygonalFaceSet.cpp new file mode 100644 index 0000000000..80f55e3214 --- /dev/null +++ b/src/ifcgeom/IfcPolygonalFaceSet.cpp @@ -0,0 +1,209 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include +#include + +#define Kernel MAKE_TYPE_NAME(Kernel) + +#ifdef SCHEMA_HAS_IfcPolygonalFaceSet + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcPolygonalFaceSet* pfs, TopoDS_Shape& shape) { + IfcSchema::IfcCartesianPointList3D* point_list = pfs->Coordinates(); + auto coord_list = point_list->CoordList(); + auto polygonal_faces = pfs->Faces(); + + std::vector> indices; + indices.reserve(polygonal_faces->size() * 2); + + std::vector> loop_grouping; + loop_grouping.reserve(polygonal_faces->size()); + + for (auto& f : *polygonal_faces) { + loop_grouping.emplace_back(); + loop_grouping.back().push_back(indices.size()); + indices.push_back(f->CoordIndex()); + if (f->as()) { + auto inner_coordinates = f->as()->InnerCoordIndices(); + for (auto& x : inner_coordinates) { + loop_grouping.back().push_back(indices.size()); + indices.push_back(x); + } + } + } + + faceset_helper< + std::vector, + std::vector + > helper(this, coord_list, indices, pfs->Closed().get_value_or(false)); + + TopTools_ListOfShape faces; + + for (auto& f : loop_grouping) { + bool not_planar = false; + + TopoDS_Wire w; + if (!helper.wire(indices[f[0]], w)) { + continue; + } + + TopoDS_Face face; + std::vector ws = { w }; + + // @todo triangulate + BRepBuilderAPI_MakeFace mf(w); + if (mf.Error() == BRepBuilderAPI_NotPlanar) { + not_planar = true; + } else if (mf.IsDone()) { + face = mf.Face(); + } else { + // todo log + continue; + } + + if (f.size() > 1) { + + if (not_planar) { + for (auto it = f.begin() + 1; it != f.end(); ++it) { + TopoDS_Wire w2; + if (helper.wire(indices[*it], w2)) { + ws.push_back(w2); + } + } + } else { + BRepBuilderAPI_MakeFace mf2(face); + for (auto it = f.begin() + 1; it != f.end(); ++it) { + TopoDS_Wire w2; + if (helper.wire(indices[*it], w2)) { + mf2.Add(w2); + ws.push_back(w2); + } + + } + + if (mf2.Error() == BRepBuilderAPI_NotPlanar) { + not_planar = true; + } else if (mf2.IsDone()) { + face = mf2.Face(); + } + } + + } + + if (not_planar) { + TopTools_ListOfShape fs; + if (triangulate_wire(ws, fs)) { + Logger::Warning("Triangulated face boundary:", pfs); + TopTools_ListIteratorOfListOfShape it(fs); + for (; it.More(); it.Next()) { + const TopoDS_Face& tri = TopoDS::Face(it.Value()); + if (face_area(tri) > getValue(GV_MINIMAL_FACE_AREA)) { + faces.Append(tri); + } + } + } + } else { + faces.Append(face); + } + } + + if (faces.Extent() > getValue(GV_MAX_FACES_TO_ORIENT) || !create_solid_from_faces(faces, shape)) { + TopoDS_Compound compound; + BRep_Builder builder; + builder.MakeCompound(compound); + + TopTools_ListIteratorOfListOfShape face_iterator; + for (face_iterator.Initialize(faces); face_iterator.More(); face_iterator.Next()) { + builder.Add(compound, face_iterator.Value()); + } + shape = compound; + } + + return true; +} + +#endif diff --git a/src/ifcgeom/IfcPolyline.cpp b/src/ifcgeom/IfcPolyline.cpp new file mode 100644 index 0000000000..bcb57a1c47 --- /dev/null +++ b/src/ifcgeom/IfcPolyline.cpp @@ -0,0 +1,125 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include + +#define _USE_MATH_DEFINES +#define Kernel MAKE_TYPE_NAME(Kernel) + +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); + } + + // @todo the strict tolerance should also govern these arbitrary precision increases + 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) { + // We somehow need to signal we fail this curve on purpose not to trigger an error. + BRep_Builder B; + B.MakeWire(result); + 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; +} diff --git a/src/ifcgeom/IfcRectangleHollowProfileDef.cpp b/src/ifcgeom/IfcRectangleHollowProfileDef.cpp new file mode 100644 index 0000000000..95dba8fc22 --- /dev/null +++ b/src/ifcgeom/IfcRectangleHollowProfileDef.cpp @@ -0,0 +1,144 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include +#include +#include +#include + +#define Kernel MAKE_TYPE_NAME(Kernel) + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcRectangleHollowProfileDef* l, TopoDS_Shape& 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->OuterFilletRadius(); + const bool fr2 = !!l->InnerFilletRadius(); + + 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; + } + + TopoDS_Face f1; + TopoDS_Face f2; + + gp_Trsf2d trsf2d; + bool has_position = true; +#ifdef SCHEMA_IfcParameterizedProfileDef_Position_IS_OPTIONAL + has_position = l->Position() != nullptr; +#endif + if (has_position) { + IfcGeom::Kernel::convert(l->Position(), trsf2d); + } + + double coords1[8] = {-x ,-y, x ,-y, x, y, -x, y }; + double coords2[8] = {-x+d,-y+d, x-d,-y+d, x-d,y-d, -x+d,y-d}; + double radii1[4] = {r1,r1,r1,r1}; + double radii2[4] = {r2,r2,r2,r2}; + int fillets[4] = {0,1,2,3}; + + bool s1 = profile_helper(4,coords1,fr1 ? 4 : 0,fillets,radii1,trsf2d,f1); + bool s2 = profile_helper(4,coords2,fr2 ? 4 : 0,fillets,radii2,trsf2d,f2); + + if (!s1 || !s2) return false; + + TopExp_Explorer exp1(f1, TopAbs_WIRE); + TopExp_Explorer exp2(f2, TopAbs_WIRE); + + TopoDS_Wire w1 = TopoDS::Wire(exp1.Current()); + TopoDS_Wire w2 = TopoDS::Wire(exp2.Current()); + + BRepBuilderAPI_MakeFace mf(w1, false); + mf.Add(w2); + + ShapeFix_Shape sfs(mf.Face()); + sfs.Perform(); + face = TopoDS::Face(sfs.Shape()); + return true; +} diff --git a/src/ifcgeom/IfcRectangleProfileDef.cpp b/src/ifcgeom/IfcRectangleProfileDef.cpp new file mode 100644 index 0000000000..fe352089a8 --- /dev/null +++ b/src/ifcgeom/IfcRectangleProfileDef.cpp @@ -0,0 +1,112 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include +#include +#include +#include + +#define Kernel MAKE_TYPE_NAME(Kernel) + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcRectangleProfileDef* l, TopoDS_Shape& 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; + } + + gp_Trsf2d trsf2d; + bool has_position = true; +#ifdef SCHEMA_IfcParameterizedProfileDef_Position_IS_OPTIONAL + has_position = l->Position() != nullptr; +#endif + if (has_position) { + IfcGeom::Kernel::convert(l->Position(), trsf2d); + } + + double coords[8] = {-x,-y,x,-y,x,y,-x,y}; + return profile_helper(4,coords,0,0,0,trsf2d,face); +} diff --git a/src/ifcgeom/IfcRectangularPyramid.cpp b/src/ifcgeom/IfcRectangularPyramid.cpp new file mode 100644 index 0000000000..b1dce67d1b --- /dev/null +++ b/src/ifcgeom/IfcRectangularPyramid.cpp @@ -0,0 +1,113 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include +#include + +#define Kernel MAKE_TYPE_NAME(Kernel) + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcRectangularPyramid* l, TopoDS_Shape& 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); + + BRepPrimAPI_MakeWedge builder(dx, dz, dy, dx / 2., dy / 2., dx / 2., dy / 2.); + + gp_Trsf trsf1, trsf2; + trsf2.SetValues( + 1, 0, 0, 0, + 0, 0, 1, 0, + 0, 1, 0, 0 +#if OCC_VERSION_HEX < 0x60800 + , Precision::Angular(), Precision::Confusion() +#endif + ); + + IfcGeom::Kernel::convert(l->Position(), trsf1); + shape = BRepBuilderAPI_Transform(builder.Solid(), trsf1 * trsf2); + return true; +} diff --git a/src/ifcgeom/IfcRectangularTrimmedSurface.cpp b/src/ifcgeom/IfcRectangularTrimmedSurface.cpp new file mode 100644 index 0000000000..760b8cf924 --- /dev/null +++ b/src/ifcgeom/IfcRectangularTrimmedSurface.cpp @@ -0,0 +1,106 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include +#include + +#define Kernel MAKE_TYPE_NAME(Kernel) + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcRectangularTrimmedSurface* l, TopoDS_Shape& face) { + if (!l->BasisSurface()->declaration().is(IfcSchema::IfcPlane::Class())) { + Logger::Message(Logger::LOG_ERROR, "Unsupported BasisSurface:", l->BasisSurface()); + return false; + } + gp_Pln pln; + IfcGeom::Kernel::convert((IfcSchema::IfcPlane*) l->BasisSurface(), pln); + + BRepBuilderAPI_MakeFace mf(pln, l->U1(), l->U2(), l->V1(), l->V2()); + + face = mf.Face(); + + return true; +} diff --git a/src/ifcgeom/IfcRepresentation.cpp b/src/ifcgeom/IfcRepresentation.cpp new file mode 100644 index 0000000000..e29d0b413d --- /dev/null +++ b/src/ifcgeom/IfcRepresentation.cpp @@ -0,0 +1,118 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include +#include + +#define Kernel MAKE_TYPE_NAME(Kernel) + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcRepresentation* l, IfcRepresentationShapeItems& 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 { + TopoDS_Shape s; + if (convert_shape(representation_item, s)) { + if (s.ShapeType() == TopAbs_COMPOUND && TopoDS_Iterator(s).More() && TopoDS_Iterator(s).Value().ShapeType() == TopAbs_SOLID) { + TopoDS_Iterator it(s); + for (; it.More(); it.Next()) { + shapes.push_back(IfcRepresentationShapeItem(representation_item->data().id(), it.Value(), get_style(representation_item))); + } + } else { + shapes.push_back(IfcRepresentationShapeItem(representation_item->data().id(), s, get_style(representation_item))); + } + part_succes |= true; + } + } + } + } + return part_succes; +} diff --git a/src/ifcgeom/IfcRevolvedAreaSolid.cpp b/src/ifcgeom/IfcRevolvedAreaSolid.cpp new file mode 100644 index 0000000000..16606276b7 --- /dev/null +++ b/src/ifcgeom/IfcRevolvedAreaSolid.cpp @@ -0,0 +1,163 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include +#include + +#define Kernel MAKE_TYPE_NAME(Kernel) + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcRevolvedAreaSolid* l, TopoDS_Shape& shape) { + const double ang = l->Angle() * getValue(GV_PLANEANGLE_UNIT); + + TopoDS_Shape face; + if ( ! convert_face(l->SweptArea(),face) ) return false; + + gp_Ax1 ax1; + IfcGeom::Kernel::convert(l->Axis(), ax1); + + gp_Trsf trsf; + bool has_position = true; +#ifdef SCHEMA_IfcSweptAreaSolid_Position_IS_OPTIONAL + has_position = l->Position() != nullptr; +#endif + if (has_position) { + IfcGeom::Kernel::convert(l->Position(), trsf); + } + + + { + // https://github.com/IfcOpenShell/IfcOpenShell/issues/1030 + // Check whether Axis does not intersect SweptArea + + double min_dot = +std::numeric_limits::infinity(); + double max_dot = -std::numeric_limits::infinity(); + + gp_Ax2 ax(ax1.Location(), gp::DZ(), ax1.Direction()); + + TopExp_Explorer exp(face, TopAbs_EDGE); + for (; exp.More(); exp.Next()) { + BRepAdaptor_Curve crv(TopoDS::Edge(exp.Current())); + GCPnts_QuasiUniformDeflection tessellater(crv, getValue(GV_PRECISION)); + + int n = tessellater.NbPoints(); + for (int i = 1; i <= n; ++i) { + double d = ax.YDirection().XYZ().Dot(tessellater.Value(i).XYZ()); + if (d < min_dot) { + min_dot = d; + } + if (d > max_dot) { + max_dot = d; + } + } + } + + bool intersecting; + if (std::abs(min_dot) > std::abs(max_dot)) { + intersecting = max_dot > + getValue(GV_PRECISION); + } else { + intersecting = min_dot < - getValue(GV_PRECISION); + } + + if (intersecting) { + Logger::Warning("Warning Axis and SweptArea intersecting", l); + } + } + + if (ang >= M_PI * 2. - ALMOST_ZERO) { + shape = BRepPrimAPI_MakeRevol(face, ax1); + } else { + shape = BRepPrimAPI_MakeRevol(face, ax1, ang); + } + + if (has_position) { + // IfcSweptAreaSolid.Position (trsf) is an IfcAxis2Placement3D + // and therefore has a unit scale factor + shape.Move(trsf); + } + + return !shape.IsNull(); +} diff --git a/src/ifcgeom/IfcRightCircularCone.cpp b/src/ifcgeom/IfcRightCircularCone.cpp new file mode 100644 index 0000000000..690d28d205 --- /dev/null +++ b/src/ifcgeom/IfcRightCircularCone.cpp @@ -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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include +#include + +#define Kernel MAKE_TYPE_NAME(Kernel) + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcRightCircularCone* l, TopoDS_Shape& shape) { + const double r = l->BottomRadius() * getValue(GV_LENGTH_UNIT); + const double h = l->Height() * getValue(GV_LENGTH_UNIT); + + BRepPrimAPI_MakeCone builder(r, 0., h); + gp_Trsf trsf; + IfcGeom::Kernel::convert(l->Position(),trsf); + + // IfcCsgPrimitive3D.Position has unit scale factor + shape = builder.Solid().Moved(trsf); + + return true; +} diff --git a/src/ifcgeom/IfcRightCircularCylinder.cpp b/src/ifcgeom/IfcRightCircularCylinder.cpp new file mode 100644 index 0000000000..307327abec --- /dev/null +++ b/src/ifcgeom/IfcRightCircularCylinder.cpp @@ -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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include +#include + +#define Kernel MAKE_TYPE_NAME(Kernel) + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcRightCircularCylinder* l, TopoDS_Shape& shape) { + const double r = l->Radius() * getValue(GV_LENGTH_UNIT); + const double h = l->Height() * getValue(GV_LENGTH_UNIT); + + BRepPrimAPI_MakeCylinder builder(r, h); + gp_Trsf trsf; + IfcGeom::Kernel::convert(l->Position(),trsf); + + // IfcCsgPrimitive3D.Position has unit scale factor + shape = builder.Solid().Moved(trsf); + + return true; +} diff --git a/src/ifcgeom/IfcRoundedRectangleProfileDef.cpp b/src/ifcgeom/IfcRoundedRectangleProfileDef.cpp new file mode 100644 index 0000000000..ecb71e38f4 --- /dev/null +++ b/src/ifcgeom/IfcRoundedRectangleProfileDef.cpp @@ -0,0 +1,115 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include +#include +#include +#include + +#define Kernel MAKE_TYPE_NAME(Kernel) + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcRoundedRectangleProfileDef* l, TopoDS_Shape& 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; + } + + gp_Trsf2d trsf2d; + bool has_position = true; +#ifdef SCHEMA_IfcParameterizedProfileDef_Position_IS_OPTIONAL + has_position = l->Position() != nullptr; +#endif + if (has_position) { + IfcGeom::Kernel::convert(l->Position(), trsf2d); + } + + double coords[8] = {-x,-y, x,-y, x,y, -x,y}; + int fillets[4] = {0,1,2,3}; + double radii[4] = {r,r,r,r}; + return profile_helper(4,coords,4,fillets,radii,trsf2d,face); +} diff --git a/src/ifcgeom/IfcShellBasedSurfaceModel.cpp b/src/ifcgeom/IfcShellBasedSurfaceModel.cpp new file mode 100644 index 0000000000..bfc970b8f4 --- /dev/null +++ b/src/ifcgeom/IfcShellBasedSurfaceModel.cpp @@ -0,0 +1,107 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include +#include + +#define Kernel MAKE_TYPE_NAME(Kernel) + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcShellBasedSurfaceModel* l, IfcRepresentationShapeItems& shapes) { + aggregate_of_instance::ptr shells = l->SbsmBoundary(); + auto collective_style = get_style(l); + for( aggregate_of_instance::it it = shells->begin(); it != shells->end(); ++ it ) { + TopoDS_Shape s; + decltype(collective_style) shell_style; + if ((*it)->declaration().is(IfcSchema::IfcRepresentationItem::Class())) { + shell_style = get_style((IfcSchema::IfcRepresentationItem*)*it); + } + if (convert_shape(*it,s)) { + shapes.push_back(IfcRepresentationShapeItem(l->data().id(), s, shell_style ? shell_style : collective_style)); + } + } + return true; +} diff --git a/src/ifcgeom/IfcSphere.cpp b/src/ifcgeom/IfcSphere.cpp new file mode 100644 index 0000000000..7e9bde719a --- /dev/null +++ b/src/ifcgeom/IfcSphere.cpp @@ -0,0 +1,104 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include +#include + +#define Kernel MAKE_TYPE_NAME(Kernel) + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcSphere* l, TopoDS_Shape& shape) { + const double r = l->Radius() * getValue(GV_LENGTH_UNIT); + + BRepPrimAPI_MakeSphere builder(r); + gp_Trsf trsf; + IfcGeom::Kernel::convert(l->Position(),trsf); + + // IfcCsgPrimitive3D.Position has unit scale factor + shape = builder.Solid().Moved(trsf); + + return true; +} diff --git a/src/ifcgeom/IfcSphericalSurface.cpp b/src/ifcgeom/IfcSphericalSurface.cpp new file mode 100644 index 0000000000..a93b90acd6 --- /dev/null +++ b/src/ifcgeom/IfcSphericalSurface.cpp @@ -0,0 +1,104 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include +#include + +#define Kernel MAKE_TYPE_NAME(Kernel) + +#ifdef SCHEMA_HAS_IfcSphericalSurface + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcSphericalSurface* l, TopoDS_Shape& face) { + gp_Trsf trsf; + IfcGeom::Kernel::convert(l->Position(), trsf); + + // IfcElementarySurface.Position has unit scale factor + face = BRepBuilderAPI_MakeFace(new Geom_SphericalSurface(gp::XOY(), l->Radius() * getValue(GV_LENGTH_UNIT)), getValue(GV_PRECISION)).Face().Moved(trsf); + return true; +} + +#endif diff --git a/src/ifcgeom/IfcSubedge.cpp b/src/ifcgeom/IfcSubedge.cpp new file mode 100644 index 0000000000..be3459fcb1 --- /dev/null +++ b/src/ifcgeom/IfcSubedge.cpp @@ -0,0 +1,101 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include + +#define _USE_MATH_DEFINES +#define Kernel MAKE_TYPE_NAME(Kernel) + +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; + } +} diff --git a/src/ifcgeom/IfcSurfaceCurve.cpp b/src/ifcgeom/IfcSurfaceCurve.cpp new file mode 100644 index 0000000000..b444aa2eae --- /dev/null +++ b/src/ifcgeom/IfcSurfaceCurve.cpp @@ -0,0 +1,75 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include + +#define Kernel MAKE_TYPE_NAME(Kernel) + +#ifdef SCHEMA_HAS_IfcSurfaceCurve +bool IfcGeom::Kernel::convert(const IfcSchema::IfcSurfaceCurve* sc, Handle(Geom_Curve)& curve) { + // @todo take into account PCurves. + return convert_curve(sc->Curve3D(), curve); +} +#endif diff --git a/src/ifcgeom/IfcSurfaceCurveSweptAreaSolid.cpp b/src/ifcgeom/IfcSurfaceCurveSweptAreaSolid.cpp new file mode 100644 index 0000000000..ca7c321e1a --- /dev/null +++ b/src/ifcgeom/IfcSurfaceCurveSweptAreaSolid.cpp @@ -0,0 +1,209 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include +#include + +#define Kernel MAKE_TYPE_NAME(Kernel) + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcSurfaceCurveSweptAreaSolid* l, TopoDS_Shape& shape) { + gp_Trsf directrix; + TopoDS_Shape face; + TopoDS_Face surface_face; + TopoDS_Wire wire, section; + + const bool is_plane = l->ReferenceSurface()->declaration().is(IfcSchema::IfcPlane::Class()); + + if (!is_plane) { + TopoDS_Shape surface_shell; + if (!convert_shape(l->ReferenceSurface(), surface_shell)) { + Logger::Error("Failed to convert reference surface", l); + return false; + } + if (count(surface_shell, TopAbs_FACE) != 1) { + Logger::Error("Non-continuous reference surface", l); + return false; + } + surface_face = TopoDS::Face(TopExp_Explorer(surface_shell, TopAbs_FACE).Current()); + } + + gp_Trsf trsf; + bool has_position = true; +#ifdef SCHEMA_IfcSweptAreaSolid_Position_IS_OPTIONAL + has_position = l->Position() != nullptr; +#endif + if (has_position) { + IfcGeom::Kernel::convert(l->Position(), trsf); + } + + if (!convert_face(l->SweptArea(), face) || + !convert_wire(l->Directrix(), wire) ) { + return false; + } + + gp_Pln pln; + gp_Pnt directrix_origin; + gp_Vec directrix_tangent; + bool directrix_on_plane = is_plane; + + if (is_plane) { + IfcGeom::Kernel::convert((IfcSchema::IfcPlane*) l->ReferenceSurface(), pln); + + // As per Informal propositions 2: The Directrix shall lie on the ReferenceSurface. + // This is not always the case with the test files in the repository. I am not sure + // how to deal with this and whether my interpretation of the propositions is + // correct. However, if it has been asserted that the vertices of the directrix do + // not conform to the ReferenceSurface, the ReferenceSurface is ignored. + { + for (TopExp_Explorer exp(wire, TopAbs_VERTEX); exp.More(); exp.Next()) { + if (pln.Distance(BRep_Tool::Pnt(TopoDS::Vertex(exp.Current()))) > ALMOST_ZERO) { + directrix_on_plane = false; + Logger::Message(Logger::LOG_WARNING, "The Directrix does not lie on the ReferenceSurface", l); + break; + } + } + } + } + + { + TopExp_Explorer exp(wire, TopAbs_EDGE); + TopoDS_Edge edge = TopoDS::Edge(exp.Current()); + double u0, u1; + Handle(Geom_Curve) crv = BRep_Tool::Curve(edge, u0, u1); + crv->D1(u0, directrix_origin, directrix_tangent); + } + + if (is_plane && pln.Axis().Direction().IsNormal(directrix_tangent, Precision::Approximation()) && directrix_on_plane) { + directrix.SetTransformation(gp_Ax3(directrix_origin, directrix_tangent, pln.Axis().Direction()), gp::XOY()); + } else if (!is_plane) { + ShapeAnalysis_Surface sas(BRep_Tool::Surface(surface_face)); + auto pnt2d = sas.ValueOfUV(directrix_origin, getValue(GV_PRECISION) * 10.); + BRepGProp_Face prop(surface_face); + gp_Pnt _; + gp_Vec surface_normal; + prop.Normal(pnt2d.X(), pnt2d.Y(), _, surface_normal); + directrix.SetTransformation(gp_Ax3(directrix_origin, directrix_tangent, surface_normal), gp::XOY()); + } else { + directrix.SetTransformation(gp_Ax3(directrix_origin, directrix_tangent), gp::XOY()); + } + face = BRepBuilderAPI_Transform(face, directrix); + + if (!is_plane) { + TopExp_Explorer exp(wire, TopAbs_EDGE); + for (; exp.More(); exp.Next()) { + ShapeFix_Edge sfe; + sfe.FixAddPCurve(TopoDS::Edge(exp.Current()), surface_face, false, getValue(GV_PRECISION)); + } + } + + // NB: Note that StartParam and EndParam param are ignored and the assumption is + // made that the parametric range over which to be swept matches the IfcCurve in + // its entirety. + BRepOffsetAPI_MakePipeShell builder(wire); + + { TopExp_Explorer exp(face, TopAbs_WIRE); + section = TopoDS::Wire(exp.Current()); } + + builder.Add(section); + builder.SetTransitionMode(BRepBuilderAPI_RightCorner); + if (directrix_on_plane) { + builder.SetMode(pln.Axis().Direction()); + } else if (!is_plane) { + builder.SetMode(surface_face); + } + builder.Build(); + builder.MakeSolid(); + shape = builder.Shape(); + + if (has_position) { + // IfcSweptAreaSolid.Position (trsf) is an IfcAxis2Placement3D + // and therefore has a unit scale factor + shape.Move(trsf); + } + + return true; +} diff --git a/src/ifcgeom/IfcSurfaceOfLinearExtrusion.cpp b/src/ifcgeom/IfcSurfaceOfLinearExtrusion.cpp new file mode 100644 index 0000000000..9efcf31919 --- /dev/null +++ b/src/ifcgeom/IfcSurfaceOfLinearExtrusion.cpp @@ -0,0 +1,124 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include +#include + +#define Kernel MAKE_TYPE_NAME(Kernel) + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcSurfaceOfLinearExtrusion* l, TopoDS_Shape& shape) { + TopoDS_Wire wire; + if ( !convert_wire(l->SweptCurve(), wire) ) { + TopoDS_Face face; + if ( !convert_face(l->SweptCurve(),face) ) return false; + TopExp_Explorer exp(face, TopAbs_WIRE); + wire = TopoDS::Wire(exp.Current()); + } + const double height = l->Depth() * getValue(GV_LENGTH_UNIT); + + gp_Trsf trsf; + bool has_position = true; +#ifdef SCHEMA_IfcSweptSurface_Position_IS_OPTIONAL + has_position = l->Position() != nullptr; +#endif + if (has_position) { + IfcGeom::Kernel::convert(l->Position(), trsf); + } + + gp_Dir dir; + convert(l->ExtrudedDirection(),dir); + + shape = BRepPrimAPI_MakePrism(wire, height*dir); + + if (has_position) { + // IfcSweptSurface.Position (trsf) is an IfcAxis2Placement3D + // and therefore has a unit scale factor + shape.Move(trsf); + } + + return !shape.IsNull(); +} diff --git a/src/ifcgeom/IfcSurfaceOfRevolution.cpp b/src/ifcgeom/IfcSurfaceOfRevolution.cpp new file mode 100644 index 0000000000..d06c04e11a --- /dev/null +++ b/src/ifcgeom/IfcSurfaceOfRevolution.cpp @@ -0,0 +1,123 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include +#include + +#define Kernel MAKE_TYPE_NAME(Kernel) + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcSurfaceOfRevolution* l, TopoDS_Shape& shape) { + TopoDS_Wire wire; + if ( !convert_wire(l->SweptCurve(), wire) ) { + TopoDS_Face face; + if ( !convert_face(l->SweptCurve(),face) ) return false; + TopExp_Explorer exp(face, TopAbs_WIRE); + wire = TopoDS::Wire(exp.Current()); + } + + gp_Ax1 ax1; + IfcGeom::Kernel::convert(l->AxisPosition(), ax1); + + gp_Trsf trsf; + bool has_position = true; +#ifdef SCHEMA_IfcSweptSurface_Position_IS_OPTIONAL + has_position = l->Position() != nullptr; +#endif + if (has_position) { + IfcGeom::Kernel::convert(l->Position(), trsf); + } + + shape = BRepPrimAPI_MakeRevol(wire, ax1); + + if (has_position) { + // IfcSweptSurface.Position (trsf) is an IfcAxis2Placement3D + // and therefore has a unit scale factor + shape.Move(trsf); + } + + return !shape.IsNull(); +} diff --git a/src/ifcgeom/IfcSweptDiskSolid.cpp b/src/ifcgeom/IfcSweptDiskSolid.cpp new file mode 100644 index 0000000000..520d3c6c38 --- /dev/null +++ b/src/ifcgeom/IfcSweptDiskSolid.cpp @@ -0,0 +1,189 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include +#include + +#include "../ifcgeom_schema_agnostic/sweep_utils.h" + +#define Kernel MAKE_TYPE_NAME(Kernel) + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcSweptDiskSolid* l, TopoDS_Shape& shape) { + TopoDS_Wire wire, section1, section2; + + bool hasInnerRadius = !!l->InnerRadius(); + + if (!convert_wire(l->Directrix(), wire)) { + return false; + } + + // Start- EndParam became optional in IFC4 +#ifdef SCHEMA_IfcSweptDiskSolid_StartParam_IS_OPTIONAL + auto sp = l->StartParam(); + auto ep = l->EndParam(); +#else + boost::optional sp, ep; + sp = l->StartParam(); + ep = l->EndParam(); +#endif + + if (count(wire, TopAbs_EDGE) == 1 && sp && ep) { + TopoDS_Vertex v0, v1; + TopExp::Vertices(wire, v0, v1); + if (v0.IsSame(v1)) { + TopExp_Explorer exp(wire, TopAbs_EDGE); + auto& e = TopoDS::Edge(exp.Current()); + double a, b; + auto crv = BRep_Tool::Curve(e, a, b); + if ((crv->DynamicType() == STANDARD_TYPE(Geom_Circle)) || + (crv->DynamicType() == STANDARD_TYPE(Geom_Ellipse))) + { + BRepBuilderAPI_MakeEdge me(crv, *sp, *ep); + if (me.IsDone()) { + auto e2 = me.Edge(); + BRep_Builder B; + wire.Nullify(); + B.MakeWire(wire); + B.Add(wire, e2); + } + } + } + } + + // NB: Note that StartParam and EndParam param are ignored and the assumption is + // made that the parametric range over which to be swept matches the IfcCurve in + // its entirety. + + util::process_sweep(wire, l->Radius() * getValue(GV_LENGTH_UNIT), shape); + + if (shape.IsNull()) { + return false; + } + + double r2 = 0.; + + if (hasInnerRadius) { + // Subtraction of pipes with small radii is unstable. + r2 = *l->InnerRadius() * getValue(GV_LENGTH_UNIT); + } + + if (r2 > getValue(GV_PRECISION) * 10.) { + TopoDS_Shape inner; + util::process_sweep(wire, r2, inner); + + bool is_valid = false; + + // Boolean op on the compound of separately processed sweeps + // is not attempted. + // @todo iterate over compound subshapes and process boolean + // separately. + // @todo don't process as boolean op at all, since we know + // only the start and end faces intersect and we know they + // are co-planar and we know they are circles. + if (shape.ShapeType() != TopAbs_COMPOUND) { + BRepAlgoAPI_Cut brep_cut(shape, inner); + if (brep_cut.IsDone()) { + TopoDS_Shape result = brep_cut; + + ShapeFix_Shape fix(result); + fix.Perform(); + result = fix.Shape(); + + is_valid = BRepCheck_Analyzer(result).IsValid() != 0; + if (is_valid) { + shape = result; + } + } + } + + if (!is_valid) { + Logger::Message(Logger::LOG_WARNING, "Failed to subtract inner radius void for:", l); + } + } + + return true; +} diff --git a/src/ifcgeom/IfcTShapeProfileDef.cpp b/src/ifcgeom/IfcTShapeProfileDef.cpp new file mode 100644 index 0000000000..a4b752d55a --- /dev/null +++ b/src/ifcgeom/IfcTShapeProfileDef.cpp @@ -0,0 +1,179 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include +#include +#include +#include + +#define Kernel MAKE_TYPE_NAME(Kernel) + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcTShapeProfileDef* l, TopoDS_Shape& face) { + const bool doFlangeEdgeFillet = !!l->FlangeEdgeRadius(); + const bool doWebEdgeFillet = !!l->WebEdgeRadius(); + const bool doFillet = !!l->FilletRadius(); + const bool hasFlangeSlope = !!l->FlangeSlope(); + const bool hasWebSlope = !!l->WebSlope(); + + 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; + } + + gp_Trsf2d trsf2d; + bool has_position = true; +#ifdef SCHEMA_IfcParameterizedProfileDef_Position_IS_OPTIONAL + has_position = l->Position() != nullptr; +#endif + if (has_position) { + IfcGeom::Kernel::convert(l->Position(), trsf2d); + } + + double coords[16] = {d1/2.-dx2,-y, xx,xy, x,y-d2+dy2, x,y, -x,y, -x,y-d2+dy2, -xx,xy, -d1/2.+dx2,-y}; + int fillets[6] = {0,1,2,5,6,7}; + double radii[6] = {f2,f1,f3,f3,f1,f2}; + return profile_helper(8, coords, (doFillet || doWebEdgeFillet || doFlangeEdgeFillet) ? 6 : 0, fillets, radii, trsf2d, face); +} diff --git a/src/ifcgeom/IfcToroidalSurface.cpp b/src/ifcgeom/IfcToroidalSurface.cpp new file mode 100644 index 0000000000..eaa55f91f7 --- /dev/null +++ b/src/ifcgeom/IfcToroidalSurface.cpp @@ -0,0 +1,104 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include +#include + +#define Kernel MAKE_TYPE_NAME(Kernel) + +#ifdef SCHEMA_HAS_IfcToroidalSurface + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcToroidalSurface* l, TopoDS_Shape& face) { + gp_Trsf trsf; + IfcGeom::Kernel::convert(l->Position(), trsf); + + // IfcElementarySurface.Position has unit scale factor + face = BRepBuilderAPI_MakeFace(new Geom_ToroidalSurface(gp::XOY(), l->MajorRadius() * getValue(GV_LENGTH_UNIT), l->MinorRadius() * getValue(GV_LENGTH_UNIT)), getValue(GV_PRECISION)).Face().Moved(trsf); + return true; +} + +#endif diff --git a/src/ifcgeom/IfcTrapeziumProfileDef.cpp b/src/ifcgeom/IfcTrapeziumProfileDef.cpp new file mode 100644 index 0000000000..4960f31e60 --- /dev/null +++ b/src/ifcgeom/IfcTrapeziumProfileDef.cpp @@ -0,0 +1,123 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include +#include +#include +#include + +#define Kernel MAKE_TYPE_NAME(Kernel) + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcTrapeziumProfileDef* l, TopoDS_Shape& face) { + const double x1 = l->BottomXDim() / 2. * 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. * getValue(GV_LENGTH_UNIT); + + // See: https://forums.buildingsmart.org/t/how-are-the-sides-of-ifctrapeziumprofiledefs-bounding-box-calculated-in-most-implementations/2945/8 + // The trapezium x center should not be midway of BottomXDim but rather at the center of the overall bounding box. + const double x_offset = ((std::min(dx, 0.) + std::max(w + dx, x1 * 2.)) / 2.) - x1; + + if ( x1 < ALMOST_ZERO || w < ALMOST_ZERO || y < ALMOST_ZERO ) { + Logger::Message(Logger::LOG_NOTICE,"Skipping zero sized profile:",l); + return false; + } + + gp_Trsf2d trsf2d; + bool has_position = true; +#ifdef SCHEMA_IfcParameterizedProfileDef_Position_IS_OPTIONAL + has_position = l->Position() != nullptr; +#endif + if (has_position) { + IfcGeom::Kernel::convert(l->Position(), trsf2d); + } + + double coords[8] = { + -x1 - x_offset, -y, + +x1 - x_offset, -y, + -x1 + dx + w - x_offset, y, + -x1 + dx - x_offset,y + }; + return profile_helper(4,coords,0,0,0,trsf2d,face); +} diff --git a/src/ifcgeom/IfcTriangulatedFaceSet.cpp b/src/ifcgeom/IfcTriangulatedFaceSet.cpp new file mode 100644 index 0000000000..5935695b76 --- /dev/null +++ b/src/ifcgeom/IfcTriangulatedFaceSet.cpp @@ -0,0 +1,132 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include +#include + +#define Kernel MAKE_TYPE_NAME(Kernel) + +#ifdef SCHEMA_HAS_IfcTriangulatedFaceSet + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcTriangulatedFaceSet* l, TopoDS_Shape& shape) { + IfcSchema::IfcCartesianPointList3D* point_list = l->Coordinates(); + auto coord_list = point_list->CoordList(); + std::vector> indices = l->CoordIndex(); + + faceset_helper< + std::vector, + std::vector + > helper(this, coord_list, indices, l->Closed().get_value_or(false)); + + TopTools_ListOfShape faces; + + for (auto it = indices.begin(); it != indices.end(); ++it) { + TopoDS_Wire w; + if (helper.wire(*it, w)) { + BRepBuilderAPI_MakeFace mf(w); + if (mf.IsDone()) { + faces.Append(mf.Face()); + } + } + } + + if (faces.Extent() > getValue(GV_MAX_FACES_TO_ORIENT) || !create_solid_from_faces(faces, shape)) { + TopoDS_Compound compound; + BRep_Builder builder; + builder.MakeCompound(compound); + + TopTools_ListIteratorOfListOfShape face_iterator; + for (face_iterator.Initialize(faces); face_iterator.More(); face_iterator.Next()) { + builder.Add(compound, face_iterator.Value()); + } + shape = compound; + } + + return true; +} + +#endif \ No newline at end of file diff --git a/src/ifcgeom/IfcTrimmedCurve.cpp b/src/ifcgeom/IfcTrimmedCurve.cpp new file mode 100644 index 0000000000..73b56f4d75 --- /dev/null +++ b/src/ifcgeom/IfcTrimmedCurve.cpp @@ -0,0 +1,292 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include + +#include "../ifcgeom_schema_agnostic/wire_builder.h" + +#define _USE_MATH_DEFINES + +#define Kernel MAKE_TYPE_NAME(Kernel) + +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; +#if OCC_VERSION_HEX < 0x70600 + BRepAdaptor_CompCurve cc(w, true); + Handle(Adaptor3d_HCurve) hcc = Handle(Adaptor3d_HCurve)(new BRepAdaptor_HCompCurve(cc)); +#else + auto hcc = new BRepAdaptor_CompCurve(w, true); +#endif + // @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; + aggregate_of_instance::ptr trims1 = l->Trim1(); + aggregate_of_instance::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 ( aggregate_of_instance::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 ( aggregate_of_instance::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(); + // BRepBuilderAPI_MakeEdge swaps v1 and v2 if the parameter value of v2 is + // smaller than that of v1. In that case the edge has to be reversed so that + // the vertex order is consistent with Trim1 and Trim2. Otherwise the + // IfcOpenShell wire builder will create intermediate edges automatically. + // The alternative would be to reverse the underlying curve instead. + if (!TopExp::FirstVertex(e, true).IsSame(v1)) { + e.Reverse(); + } + } + } + + 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(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(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.; + } + } + + double radius = 1.0; + if (curve->DynamicType() == STANDARD_TYPE(Geom_Circle)) { + auto circle_curve = Handle_Geom_Circle::DownCast(curve); + radius = circle_curve->Radius(); + } else if (curve->DynamicType() == STANDARD_TYPE(Geom_Ellipse)) { + auto circle_curve = Handle_Geom_Ellipse::DownCast(curve); + radius = (circle_curve->MajorRadius() + circle_curve->MinorRadius()) / 2.; + } + + // Fix from @sanderboer to compare using model tolerance, see #744 + // Made dependent on radius, see #928 + + // A good critereon for determining whether to take full curve + // or trimmed segment would be whether there are other curve segments or this + // is the only one. + boost::optional num_segments; + auto segment = l->data().getInverse(&IfcSchema::IfcCompositeCurveSegment::Class(), -1); + if (segment->size() == 1) { + auto comp = (*segment->begin())->data().getInverse(&IfcSchema::IfcCompositeCurve::Class(), -1); + if (comp->size() == 1) { + num_segments = (*comp->begin())->as()->Segments()->size(); + } + } + + // @todo is 100. not too much? Check with the original issue. + const double precision_markup = getValue(IfcGeom::Kernel::GV_PRECISION_FACTOR) == 1. ? 1. : 100.; + + if (isConic && ALMOST_THE_SAME(fmod(flts[1]-flts[0],M_PI*2.), 0., precision_markup * getValue(GV_PRECISION) / (2 * M_PI * radius))) { + e = BRepBuilderAPI_MakeEdge(curve).Edge(); + } else { + BRepBuilderAPI_MakeEdge me (curve,flts[0],flts[1]); + e = me.Edge(); + } + + if (num_segments && *num_segments > 1) { + TopoDS_Vertex v0, v1; + TopExp::Vertices(e, v0, v1); + if (v0.IsSame(v1)) { + Logger::Warning("Skipping degenerate segment", l); + return false; + } + } + + } else if ( trim_cartesian_failed && (has_pnts[0] && has_pnts[1]) ) { + e = BRepBuilderAPI_MakeEdge(pnts[0], pnts[1]).Edge(); + } + + if (e.IsNull()) { + return false; + } + + 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 in deflection_for_approximating_circle() is for circles, but probably good enough + radius = Handle(Geom_Ellipse)::DownCast(crv)->MajorRadius(); + } + if (radius > 0. && util::deflection_for_approximating_circle(radius, b - a) < 100 * getValue(GV_PRECISION) && std::abs(b-a) < M_PI/4.) { + 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; + } +} diff --git a/src/ifcgeom/IfcUShapeProfileDef.cpp b/src/ifcgeom/IfcUShapeProfileDef.cpp new file mode 100644 index 0000000000..68baa93a16 --- /dev/null +++ b/src/ifcgeom/IfcUShapeProfileDef.cpp @@ -0,0 +1,138 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include +#include +#include +#include + +#define Kernel MAKE_TYPE_NAME(Kernel) + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcUShapeProfileDef* l, TopoDS_Shape& face) { + const bool doEdgeFillet = !!l->EdgeRadius(); + const bool doFillet = !!l->FilletRadius(); + const bool hasSlope = !!l->FlangeSlope(); + + 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 = l->FlangeSlope().get_value_or(0.) * getValue(GV_PLANEANGLE_UNIT); + + 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; + } + + gp_Trsf2d trsf2d; + bool has_position = true; +#ifdef SCHEMA_IfcParameterizedProfileDef_Position_IS_OPTIONAL + has_position = l->Position() != nullptr; +#endif + if (has_position) { + IfcGeom::Kernel::convert(l->Position(), trsf2d); + } + + double coords[16] = {-x,-y, x,-y, x,-y+d2-dy2, -x+d1,-y+d2+dy1, -x+d1,y-d2-dy1, x,y-d2+dy2, x,y, -x,y}; + int fillets[4] = {2,3,4,5}; + double radii[4] = {f2,f1,f1,f2}; + return profile_helper(8, coords, (doFillet || doEdgeFillet) ? 4 : 0, fillets, radii, trsf2d, face); +} diff --git a/src/ifcgeom/IfcVector.cpp b/src/ifcgeom/IfcVector.cpp new file mode 100644 index 0000000000..585659f67f --- /dev/null +++ b/src/ifcgeom/IfcVector.cpp @@ -0,0 +1,75 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" + +#define Kernel MAKE_TYPE_NAME(Kernel) + +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; +} diff --git a/src/ifcgeom/IfcZShapeProfileDef.cpp b/src/ifcgeom/IfcZShapeProfileDef.cpp new file mode 100644 index 0000000000..2b66344919 --- /dev/null +++ b/src/ifcgeom/IfcZShapeProfileDef.cpp @@ -0,0 +1,129 @@ +/******************************************************************************** + * * + * 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 . * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ifcgeom/IfcGeom.h" +#include +#include +#include +#include + +#define Kernel MAKE_TYPE_NAME(Kernel) + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcZShapeProfileDef* l, TopoDS_Shape& 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->FilletRadius(); + bool doEdgeFillet = !!l->EdgeRadius(); + + 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; + } + + gp_Trsf2d trsf2d; + bool has_position = true; +#ifdef SCHEMA_IfcParameterizedProfileDef_Position_IS_OPTIONAL + has_position = l->Position() != nullptr; +#endif + if (has_position) { + IfcGeom::Kernel::convert(l->Position(), trsf2d); + } + + double coords[16] = {-dx,-y, x,-y, x,-y+dy, dx,-y+dy, dx,y, -x,y, -x,y-dy, -dx,y-dy}; + int fillets[4] = {2,3,6,7}; + double radii[4] = {f2,f1,f2,f1}; + return profile_helper(8,coords,(doFillet || doEdgeFillet) ? 4 : 0,fillets,radii,trsf2d,face); +} diff --git a/src/ifcgeom_schema_agnostic/Kernel.cpp b/src/ifcgeom_schema_agnostic/Kernel.cpp index 57a27be97c..924cd7bde0 100644 --- a/src/ifcgeom_schema_agnostic/Kernel.cpp +++ b/src/ifcgeom_schema_agnostic/Kernel.cpp @@ -370,3 +370,53 @@ bool IfcGeom::util::is_nested_compound_of_solid(const TopoDS_Shape& s, int depth return false; } } + +namespace { + template struct dimension_count {}; + template <> struct dimension_count { static const int n = 2; }; + template <> struct dimension_count { 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 + bool is_identity_helper(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::n + 2; ++i) { + for (int j = 1; j < dimension_count::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::is_identity(const gp_Trsf2d& t, double tolerance) { + return is_identity_helper(t, tolerance); +} + +bool IfcGeom::Kernel::is_identity(const gp_GTrsf2d& t, double tolerance) { + return is_identity_helper(t, tolerance); +} + +bool IfcGeom::Kernel::is_identity(const gp_Trsf& t, double tolerance) { + return is_identity_helper(t, tolerance); +} + +bool IfcGeom::Kernel::is_identity(const gp_GTrsf& t, double tolerance) { + return is_identity_helper(t, tolerance); +} + +inline IFC_PARSE_API gp_Trsf IfcGeom::Kernel::combine_offset_and_rotation(const gp_Vec & offset, const gp_Quaternion & rotation) { + auto offset_transform = gp_Trsf{}; + offset_transform.SetTranslation(offset); + + auto rotation_transform = gp_Trsf{}; + rotation_transform.SetRotation(rotation); + + return rotation_transform * offset_transform; +} diff --git a/src/ifcgeom_schema_agnostic/Kernel.h b/src/ifcgeom_schema_agnostic/Kernel.h index 4485143a89..17fec0b1eb 100644 --- a/src/ifcgeom_schema_agnostic/Kernel.h +++ b/src/ifcgeom_schema_agnostic/Kernel.h @@ -33,6 +33,12 @@ #include #include +#include +#include +#include +#include +#include +#include namespace IfcGeom { @@ -116,6 +122,32 @@ namespace IfcGeom { IFC_PARSE_API static bool is_manifold(const TopoDS_Shape& a); IFC_PARSE_API static IfcUtil::IfcBaseEntity* get_decomposing_entity(IfcUtil::IfcBaseEntity*, bool include_openings = true); IFC_PARSE_API static std::map get_layers(IfcUtil::IfcBaseEntity*); + + // For axis placements detect equality early in order for the + // relatively computionaly expensive gp_Trsf calculation to be skipped + IFC_PARSE_API static 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; + } + + IFC_PARSE_API static 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; + } + + IFC_PARSE_API static bool is_identity(const gp_Trsf2d& t, double tolerance); + IFC_PARSE_API static bool is_identity(const gp_GTrsf2d& t, double tolerance); + IFC_PARSE_API static bool is_identity(const gp_Trsf& t, double tolerance); + IFC_PARSE_API static bool is_identity(const gp_GTrsf& t, double tolerance); + + IFC_PARSE_API static gp_Trsf combine_offset_and_rotation(const gp_Vec &offset, const gp_Quaternion& rotation); }; namespace impl { @@ -134,6 +166,24 @@ namespace IfcGeom { namespace util { bool is_nested_compound_of_solid(const TopoDS_Shape& s, int depth = 0); } + + 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") {} + }; } #endif \ No newline at end of file