diff --git a/src/examples/CMakeLists.txt b/src/examples/CMakeLists.txt index 6e9c53b710..87934ab330 100644 --- a/src/examples/CMakeLists.txt +++ b/src/examples/CMakeLists.txt @@ -24,3 +24,7 @@ set_target_properties(IfcParseExamples PROPERTIES FOLDER Examples) ADD_EXECUTABLE(IfcOpenHouse IfcOpenHouse.cpp) TARGET_LINK_LIBRARIES(IfcOpenHouse ${IFCOPENSHELL_LIBRARIES} ${OPENCASCADE_LIBRARIES}) set_target_properties(IfcOpenHouse PROPERTIES FOLDER Examples) + +ADD_EXECUTABLE(IfcAdvancedHouse IfcAdvancedHouse.cpp) +TARGET_LINK_LIBRARIES(IfcAdvancedHouse ${IFCLIBS} ${OPENCASCADE_LIBRARIES}) +set_target_properties(IfcAdvancedHouse PROPERTIES FOLDER Examples) diff --git a/src/examples/IfcAdvancedHouse.cpp b/src/examples/IfcAdvancedHouse.cpp new file mode 100644 index 0000000000..94a3bfa431 --- /dev/null +++ b/src/examples/IfcAdvancedHouse.cpp @@ -0,0 +1,185 @@ +/******************************************************************************** + * * + * 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 + +#ifdef USE_IFC4 +#include "../ifcparse/Ifc4.h" +#else +#include "../ifcparse/Ifc2x3.h" +#endif + +#include "../ifcparse/IfcUtil.h" +#include "../ifcparse/IfcHierarchyHelper.h" +#include "../ifcgeom/IfcGeom.h" + +#if USE_VLD +#include +#endif + +// Some convenience typedefs and definitions. +typedef std::string S; +typedef IfcParse::IfcGlobalId guid; +typedef std::pair XY; +boost::none_t const null = boost::none; + +// The creation of Nurbs-surface for the IfcSite mesh, to be implemented lateron +void createGroundShape(TopoDS_Shape& shape); + +int main() { + + // The IfcHierarchyHelper is a subclass of the regular IfcFile that provides several + // convenience functions for working with geometry in IFC files. + IfcHierarchyHelper file; + file.header().file_name().name("IfcAdvancedHouse.ifc"); + + IfcSchema::IfcBuilding* building = file.addBuilding(); + // By adding a building, a hierarchy has been automatically created that consists of the following + // structure: IfcProject > IfcSite > IfcBuilding + + // Lateron changing the name of the IfcProject can be done by obtaining a reference to the + // project, which has been created automatically. + file.getSingle()->setName("IfcOpenHouse"); + + // To demonstrate the ability to serialize arbitrary opencascade solids a building envelope is + // constructed by applying boolean operations. Naturally, in IFC, building elements should be + // modeled separately, with rich parametric and relational semantics. Creating geometry in this + // way does not preserve any history and is merely a demonstration of technical capabilities. + TopoDS_Shape outer = BRepPrimAPI_MakeBox(gp_Pnt(-5000., -180., -2000.), gp_Pnt(5000., 5180., 3000.)).Shape(); + TopoDS_Shape inner = BRepPrimAPI_MakeBox(gp_Pnt(-4640., 180., 0.), gp_Pnt(4640., 4820., 3000.)).Shape(); + TopoDS_Shape window1 = BRepPrimAPI_MakeBox(gp_Pnt(-5000., -180., 400.), gp_Pnt( 500., 1180., 2000.)).Shape(); + TopoDS_Shape window2 = BRepPrimAPI_MakeBox(gp_Pnt( 2070., -180., 400.), gp_Pnt(3930., 180., 2000.)).Shape(); + + TopoDS_Shape building_shell = BRepAlgoAPI_Cut( + BRepAlgoAPI_Cut( + BRepAlgoAPI_Cut(outer, inner), + window1 + ), + window2 + ); + + // Since the solid consists only of planar faces and straight edges it can be serialized as an + // IfcFacetedBRep. If it would not be a polyhedron, serialise() can only be successful when linked + // to the IFC4 model and with `advanced` set to `true` which introduces IfcAdvancedFace. It would + // return `0` otherwise. + IfcSchema::IfcProductDefinitionShape* building_shape = IfcGeom::serialise(building_shell, false); + + file.addEntity(building_shape); + IfcSchema::IfcRepresentation* rep = *building_shape->Representations()->begin(); + rep->setContextOfItems(file.getRepresentationContext("model")); + + building->setRepresentation(building_shape); + + // A pale white colour is assigned to the building. + file.setSurfaceColour( + building_shape, 0.75, 0.73, 0.68); + + // For the ground mesh of the IfcSite we will use a Nurbs surface created in Open Cascade. Only + // in IFC4 the surface can be directly serialized. In IFC2X3 the it will have to be tesselated. + TopoDS_Shape shape; + createGroundShape(shape); + + IfcSchema::IfcProductDefinitionShape* ground_representation = IfcGeom::serialise(shape, true); + if (!ground_representation) { + ground_representation = IfcGeom::tesselate(shape, 100.); + } + file.getSingle()->setRepresentation(ground_representation); + + IfcSchema::IfcRepresentation::list::ptr ground_reps = file.getSingle()->Representation()->Representations(); + for (IfcSchema::IfcRepresentation::list::it it = ground_reps->begin(); it != ground_reps->end(); ++it) { + (*it)->setContextOfItems(file.getRepresentationContext("Model")); + } + file.addEntity(ground_representation); + file.setSurfaceColour(ground_representation, 0.15, 0.25, 0.05); + + /* + // Note that IFC lacks elementary surfaces that STEP does have, such as spherical_surface. + // BRepBuilderAPI_NurbsConvert can be used to serialize such surfaces as nurbs surfaces. + TopoDS_Shape sphere = BRepPrimAPI_MakeSphere(gp_Pnt(), 1000.).Shape(); + IfcSchema::IfcProductDefinitionShape* sphere_representation = IfcGeom::serialise(sphere, true); + if (S(IfcSchema::Identifier) == "IFC4") { + sphere = BRepBuilderAPI_NurbsConvert(sphere, true).Shape(); + sphere_representation = IfcGeom::serialise(sphere, true); + } + */ + + // Finally create a file stream for our output and write the IFC file to it. + std::ofstream f("IfcAdvancedHouse.ifc"); + f << file; +} + +void createGroundShape(TopoDS_Shape& shape) { + TColgp_Array2OfPnt cv (0, 4, 0, 4); + cv.SetValue(0, 0, gp_Pnt(-10000, -10000, -4130)); + cv.SetValue(0, 1, gp_Pnt(-10000, -4330, -4130)); + cv.SetValue(0, 2, gp_Pnt(-10000, 0, -5130)); + cv.SetValue(0, 3, gp_Pnt(-10000, 4330, -7130)); + cv.SetValue(0, 4, gp_Pnt(-10000, 10000, -7130)); + cv.SetValue(1, 0, gp_Pnt( -3330, -10000, -5130)); + cv.SetValue(1, 1, gp_Pnt( -7670, -3670, 5000)); + cv.SetValue(1, 2, gp_Pnt( -9000, 0, 1000)); + cv.SetValue(1, 3, gp_Pnt( -7670, 7670, 6000)); + cv.SetValue(1, 4, gp_Pnt( -3330, 10000, -4130)); + cv.SetValue(2, 0, gp_Pnt( 0, -10000, -5530)); + cv.SetValue(2, 1, gp_Pnt( 0, -3670, 3000)); + cv.SetValue(2, 2, gp_Pnt( 0, 0, -12000)); + cv.SetValue(2, 3, gp_Pnt( 0, 7670, 1500)); + cv.SetValue(2, 4, gp_Pnt( 0, 10000, -4130)); + cv.SetValue(3, 0, gp_Pnt( 3330, -10000, -6130)); + cv.SetValue(3, 1, gp_Pnt( 7670, -3670, 6000)); + cv.SetValue(3, 2, gp_Pnt( 9000, 0, 5000)); + cv.SetValue(3, 3, gp_Pnt( 7670, 9000, 7000)); + cv.SetValue(3, 4, gp_Pnt( 3330, 10000, -4130)); + cv.SetValue(4, 0, gp_Pnt( 10000, -10000, -6130)); + cv.SetValue(4, 1, gp_Pnt( 10000, -4330, -5130)); + cv.SetValue(4, 2, gp_Pnt( 10000, 0, -4130)); + cv.SetValue(4, 3, gp_Pnt( 10000, 4330, -4130)); + cv.SetValue(4, 4, gp_Pnt( 10000, 10000, -8130)); + TColStd_Array1OfReal knots(0, 1); + knots(0) = 0; + knots(1) = 1; + TColStd_Array1OfInteger mult(0, 1); + mult(0) = 5; + mult(1) = 5; + Handle(Geom_BSplineSurface) surf = new Geom_BSplineSurface(cv, knots, knots, mult, mult, 4, 4); +#if OCC_VERSION_HEX < 0x60502 + shape = BRepBuilderAPI_MakeFace(surf); +#else + shape = BRepBuilderAPI_MakeFace(surf, Precision::Confusion()); +#endif +} diff --git a/src/examples/IfcOpenHouse.cpp b/src/examples/IfcOpenHouse.cpp index c7414e76e7..88adc574b6 100644 --- a/src/examples/IfcOpenHouse.cpp +++ b/src/examples/IfcOpenHouse.cpp @@ -262,12 +262,11 @@ int main() { // will be tesselated using the deflection specified. TopoDS_Shape shape; createGroundShape(shape); - IfcEntityList::ptr geometrical_entities(new IfcEntityList); - IfcSchema::IfcProductDefinitionShape* ground_representation = IfcGeom::tesselate(shape, 100., geometrical_entities); + IfcSchema::IfcProductDefinitionShape* ground_representation = IfcGeom::tesselate(shape, 100.); file.getSingle()->setRepresentation(ground_representation); - file.addEntities(geometrical_entities); - IfcSchema::IfcShapeRepresentation::list::ptr ground_reps = geometrical_entities->as(); - for (IfcSchema::IfcShapeRepresentation::list::it it = ground_reps->begin(); it != ground_reps->end(); ++it) { + + IfcSchema::IfcRepresentation::list::ptr ground_reps = file.getSingle()->Representation()->Representations(); + for (IfcSchema::IfcRepresentation::list::it it = ground_reps->begin(); it != ground_reps->end(); ++it) { (*it)->setContextOfItems(file.getRepresentationContext("Model")); } file.setSurfaceColour(ground_representation, 0.15, 0.25, 0.05); diff --git a/src/ifcgeom/IfcGeom.h b/src/ifcgeom/IfcGeom.h index 638e410a22..53d21d03be 100644 --- a/src/ifcgeom/IfcGeom.h +++ b/src/ifcgeom/IfcGeom.h @@ -300,7 +300,8 @@ public: }; -IfcSchema::IfcProductDefinitionShape* tesselate(TopoDS_Shape& shape, double deflection, IfcEntityList::ptr es); +IfcSchema::IfcProductDefinitionShape* tesselate(const TopoDS_Shape& shape, double deflection); +IfcSchema::IfcProductDefinitionShape* serialise(const TopoDS_Shape& shape, bool advanced); } #endif diff --git a/src/ifcgeom/IfcGeomCurves.cpp b/src/ifcgeom/IfcGeomCurves.cpp index 117df42f53..76f36f6e15 100644 --- a/src/ifcgeom/IfcGeomCurves.cpp +++ b/src/ifcgeom/IfcGeomCurves.cpp @@ -144,17 +144,32 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcLine* l, Handle(Geom_Curve)& c #ifdef USE_IFC4 bool IfcGeom::Kernel::convert(const IfcSchema::IfcBSplineCurveWithKnots* l, Handle(Geom_Curve)& curve) { + const bool is_rational = l->is(IfcSchema::Type::IfcRationalBSplineCurveWithKnots); + const IfcSchema::IfcCartesianPoint::list::ptr cps = l->ControlPointsList(); const std::vector mults = l->KnotMultiplicities(); const std::vector knots = l->Knots(); TColgp_Array1OfPnt Poles(0, cps->size() - 1); + TColStd_Array1OfReal Weights(0, cps->size() - 1); TColStd_Array1OfReal Knots(0, (int)knots.size() - 1); TColStd_Array1OfInteger Mults(0, (int)mults.size() - 1); Standard_Integer Degree = l->Degree(); Standard_Boolean Periodic = l->ClosedCurve(); + + int i; - int i = 0; + if (is_rational) { + IfcSchema::IfcRationalBSplineCurveWithKnots* rl = (IfcSchema::IfcRationalBSplineCurveWithKnots*)l; + std::vector weights = rl->WeightsData(); + + i = 0; + for (std::vector::const_iterator it = weights.begin(); it != weights.end(); ++it, ++i) { + Weights(i) = *it; + } + } + + i = 0; for (IfcSchema::IfcCartesianPoint::list::it it = cps->begin(); it != cps->end(); ++it, ++i) { gp_Pnt pnt; if (!convert(*it, pnt)) return false; @@ -171,7 +186,11 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcBSplineCurveWithKnots* l, Hand Knots(i) = *it; } - curve = new Geom_BSplineCurve(Poles, Knots, Mults, Degree, Periodic); + if (is_rational) { + curve = new Geom_BSplineCurve(Poles, Weights, Knots, Mults, Degree, Periodic); + } else { + curve = new Geom_BSplineCurve(Poles, Knots, Mults, Degree, Periodic); + } return true; } #endif \ No newline at end of file diff --git a/src/ifcgeom/IfcGeomFaces.cpp b/src/ifcgeom/IfcGeomFaces.cpp index be7539229c..f45042c506 100644 --- a/src/ifcgeom/IfcGeomFaces.cpp +++ b/src/ifcgeom/IfcGeomFaces.cpp @@ -75,6 +75,7 @@ #include +#include #include #include #include @@ -207,7 +208,8 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcFace* l, TopoDS_Shape& face) { if (face_surface.IsNull()) { mf = new BRepBuilderAPI_MakeFace(wire); } else { - mf = new BRepBuilderAPI_MakeFace(face_surface, wire); + /// @todo check necessity of false here + mf = new BRepBuilderAPI_MakeFace(face_surface, wire, false); } /* BRepBuilderAPI_FaceError er = mf->Error(); @@ -221,13 +223,18 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcFace* l, TopoDS_Shape& face) { if (mf->IsDone()) { TopoDS_Face outer_face_bound = mf->Face(); - // BRepCheck_Face might raise exceptions in case of face surfaces. Therefore fix orientation regardless. + // In case of (non-planar) face surface, p-curves need to be computed. + // For planar faces, Open Cascade generates p-curves on the fly. if (!face_surface.IsNull()) { - ShapeFix_Face fix(outer_face_bound); - fix.FixOrientation(); - fix.Perform(); - outer_face_bound = fix.Face(); - } else if (BRepCheck_Face(outer_face_bound).OrientationOfWires() == BRepCheck_BadOrientationOfSubshape) { + TopExp_Explorer exp(outer_face_bound, TopAbs_EDGE); + for (; exp.More(); exp.Next()) { + const TopoDS_Edge& edge = TopoDS::Edge(exp.Current()); + ShapeFix_Edge fix_edge; + fix_edge.FixAddPCurve(edge, outer_face_bound, false, getValue(GV_PRECISION)); + } + } + + if (BRepCheck_Face(outer_face_bound).OrientationOfWires() == BRepCheck_BadOrientationOfSubshape) { wire.Reverse(); same_sense = !same_sense; delete mf; diff --git a/src/ifcgeom/IfcGeomFunctions.cpp b/src/ifcgeom/IfcGeomFunctions.cpp index 14b4bb2ddf..61fe2cb7c6 100644 --- a/src/ifcgeom/IfcGeomFunctions.cpp +++ b/src/ifcgeom/IfcGeomFunctions.cpp @@ -720,70 +720,6 @@ double IfcGeom::Kernel::getValue(GeomValue var) const { return 0; } -IfcSchema::IfcProductDefinitionShape* IfcGeom::tesselate(TopoDS_Shape& shape, double deflection, IfcEntityList::ptr es) { - BRepMesh_IncrementalMesh(shape, deflection); - - IfcSchema::IfcFace::list::ptr faces (new IfcSchema::IfcFace::list); - - for (TopExp_Explorer exp(shape, TopAbs_FACE); exp.More(); exp.Next()) { - const TopoDS_Face& face = TopoDS::Face(exp.Current()); - TopLoc_Location loc; - Handle(Poly_Triangulation) tri = BRep_Tool::Triangulation(face, loc); - - if (! tri.IsNull()) { - const TColgp_Array1OfPnt& nodes = tri->Nodes(); - std::vector vertices; - for (int i = 1; i <= nodes.Length(); ++i) { - gp_Pnt pnt = nodes(i).Transformed(loc); - std::vector xyz; xyz.push_back(pnt.X()); xyz.push_back(pnt.Y()); xyz.push_back(pnt.Z()); - IfcSchema::IfcCartesianPoint* cpnt = new IfcSchema::IfcCartesianPoint(xyz); - vertices.push_back(cpnt); - es->push(cpnt); - } - const Poly_Array1OfTriangle& triangles = tri->Triangles(); - for (int i = 1; i <= triangles.Length(); ++ i) { - int n1, n2, n3; - triangles(i).Get(n1, n2, n3); - IfcSchema::IfcCartesianPoint::list::ptr points (new IfcSchema::IfcCartesianPoint::list); - points->push(vertices[n1-1]); - points->push(vertices[n2-1]); - points->push(vertices[n3-1]); - IfcSchema::IfcPolyLoop* loop = new IfcSchema::IfcPolyLoop(points); - IfcSchema::IfcFaceOuterBound* bound = new IfcSchema::IfcFaceOuterBound(loop, face.Orientation() != TopAbs_REVERSED); - IfcSchema::IfcFaceBound::list::ptr bounds (new IfcSchema::IfcFaceBound::list); - bounds->push(bound); - IfcSchema::IfcFace* face2 = new IfcSchema::IfcFace(bounds); - es->push(loop); - es->push(bound); - es->push(face2); - faces->push(face2); - } - } - } - IfcSchema::IfcOpenShell* shell = new IfcSchema::IfcOpenShell(faces); - IfcSchema::IfcConnectedFaceSet::list::ptr shells (new IfcSchema::IfcConnectedFaceSet::list); - shells->push(shell); - IfcSchema::IfcFaceBasedSurfaceModel* surface_model = new IfcSchema::IfcFaceBasedSurfaceModel(shells); - - IfcSchema::IfcRepresentation::list::ptr reps (new IfcSchema::IfcRepresentation::list); - IfcSchema::IfcRepresentationItem::list::ptr items (new IfcSchema::IfcRepresentationItem::list); - - items->push(surface_model); - - IfcSchema::IfcShapeRepresentation* rep = new IfcSchema::IfcShapeRepresentation( - 0, std::string("Facetation"), std::string("SurfaceModel"), items); - - reps->push(rep); - IfcSchema::IfcProductDefinitionShape* shapedef = new IfcSchema::IfcProductDefinitionShape(boost::none, boost::none, reps); - - es->push(shell); - es->push(surface_model); - es->push(rep); - es->push(shapedef); - - return shapedef; -} - // Returns the vertex part of an TopoDS_Edge edge that is not TopoDS_Vertex vertex TopoDS_Vertex find_other(const TopoDS_Edge& edge, const TopoDS_Vertex& vertex) { TopExp_Explorer exp(edge, TopAbs_VERTEX); diff --git a/src/ifcgeom/IfcGeomRepresentation.h b/src/ifcgeom/IfcGeomRepresentation.h index 4c7d3a0578..472b049813 100644 --- a/src/ifcgeom/IfcGeomRepresentation.h +++ b/src/ifcgeom/IfcGeomRepresentation.h @@ -259,7 +259,8 @@ namespace IfcGeom { if (num_faces == 0) { // Edges are only emitted if there are no faces. A mixed representation of faces // and loose edges is discouraged by the standard. An alternative would be to use - // TopExp::MapShapesAndAncestors() to find edges that do not belong to any face. + // TopExp_Explorer texp(s, TopAbs_EDGE, TopAbs_FACE) to find edges that do not + // belong to any face. for (TopExp_Explorer texp(s, TopAbs_EDGE); texp.More(); texp.Next()) { BRepAdaptor_Curve crv(TopoDS::Edge(texp.Current())); GCPnts_QuasiUniformDeflection tessellater(crv, settings().deflection_tolerance()); @@ -267,6 +268,40 @@ namespace IfcGeom { int start = (int)_verts.size() / 3; for (int i = 1; i <= n; ++i) { gp_XYZ p = tessellater.Value(i).XYZ(); + + /* + // In case you want direction arrows on your edges + double u = tessellater.Parameter(i); + gp_XYZ p2, p3; + gp_Pnt tmp; + gp_Vec tmp2; + crv.D1(u, tmp, tmp2); + gp_Dir d1, d2, d3, d4; + d1 = tmp2; + if (texp.Current().Orientation() == TopAbs_REVERSED) { + d1 = -d1; + } + if (fabs(d1.Z()) < 0.5) { + d2 = d1.Crossed(gp::DZ()); + } else { + d2 = d1.Crossed(gp::DY()); + } + d3 = d1.XYZ() + d2.XYZ(); + d4 = d1.XYZ() - d2.XYZ(); + p2 = p - d3.XYZ() / 10.; + p3 = p - d4.XYZ() / 10.; + trsf.Transforms(p2); + trsf.Transforms(p3); + _material_ids.push_back(surface_style_id); + _material_ids.push_back(surface_style_id); + _verts.push_back(static_cast

(p2.X())); + _verts.push_back(static_cast

(p2.Y())); + _verts.push_back(static_cast

(p2.Z())); + _verts.push_back(static_cast

(p3.X())); + _verts.push_back(static_cast

(p3.Y())); + _verts.push_back(static_cast

(p3.Z())); + */ + trsf.Transforms(p); _material_ids.push_back(surface_style_id); @@ -278,7 +313,14 @@ namespace IfcGeom { if (i > 1) { _edges.push_back(start + i - 2); _edges.push_back(start + i - 1); + // _edges.push_back(start + 3 * (i - 2) + 2); + // _edges.push_back(start + 3 * (i - 1) + 2); } + + // _edges.push_back(start + 3 * (i - 1) + 0); + // _edges.push_back(start + 3 * (i - 1) + 2); + // _edges.push_back(start + 3 * (i - 1) + 1); + // _edges.push_back(start + 3 * (i - 1) + 2); } } } diff --git a/src/ifcgeom/IfcGeomSerialisation.cpp b/src/ifcgeom/IfcGeomSerialisation.cpp new file mode 100644 index 0000000000..e473480455 --- /dev/null +++ b/src/ifcgeom/IfcGeomSerialisation.cpp @@ -0,0 +1,648 @@ +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include "IfcGeom.h" + +template +int convert_to_ifc(const T& t, U*& u, bool /*advanced*/) { + std::vector coords(3); + coords[0] = t.X(); coords[1] = t.Y(); coords[2] = t.Z(); + u = new U(coords); + return 1; +} + +template <> +int convert_to_ifc(const TopoDS_Vertex& v, IfcSchema::IfcCartesianPoint*& p, bool advanced) { + gp_Pnt pnt = BRep_Tool::Pnt(v); + return convert_to_ifc(pnt, p, advanced); +} + +template <> +int convert_to_ifc(const TopoDS_Vertex& v, IfcSchema::IfcVertex*& vertex, bool advanced) { + IfcSchema::IfcCartesianPoint* p; + convert_to_ifc(v, p, advanced); + vertex = new IfcSchema::IfcVertexPoint(p); + return 1; +} + +template <> +int convert_to_ifc(const gp_Ax2& a, IfcSchema::IfcAxis2Placement3D*& ax, bool advanced) { + IfcSchema::IfcCartesianPoint* p; + IfcSchema::IfcDirection *x, *z; + if (!(convert_to_ifc(a.Location(), p, advanced) && convert_to_ifc(a.Direction(), z, advanced) && convert_to_ifc(a.XDirection(), x, advanced))) { + return 0; + } + ax = new IfcSchema::IfcAxis2Placement3D(p, z, x); + return 1; +} + +template +void opencascade_array_to_vector(T& t, std::vector& u) { + u.reserve(t.Length()); + for (int i = t.Lower(); i <= t.Upper(); ++i) { + u.push_back(t.Value(i)); + } +} + +template +void opencascade_array_to_vector2(T& t, std::vector< std::vector >& u) { + u.reserve(t.RowLength()); + for (int j = t.LowerRow(); j <= t.UpperRow(); ++j) { + std::vector v; + v.reserve(t.ColLength()); + for (int i = t.LowerCol(); i <= t.UpperCol(); ++i) { + v.push_back(t.Value(j, i)); + } + u.push_back(v); + } +} + +#ifdef USE_IFC4 +IfcSchema::IfcKnotType::IfcKnotType opencascade_knotspec_to_ifc(GeomAbs_BSplKnotDistribution bspline_knot_spec) { + IfcSchema::IfcKnotType::IfcKnotType knot_spec = IfcSchema::IfcKnotType::IfcKnotType_UNSPECIFIED; + if (bspline_knot_spec == GeomAbs_Uniform) { + knot_spec = IfcSchema::IfcKnotType::IfcKnotType_UNIFORM_KNOTS; + } else if (bspline_knot_spec == GeomAbs_QuasiUniform) { + knot_spec = IfcSchema::IfcKnotType::IfcKnotType_QUASI_UNIFORM_KNOTS; + } else if (bspline_knot_spec == GeomAbs_PiecewiseBezier) { + knot_spec = IfcSchema::IfcKnotType::IfcKnotType_PIECEWISE_BEZIER_KNOTS; + } + return knot_spec; +} +#endif + +template <> +int convert_to_ifc(const Handle_Geom_Curve& c, IfcSchema::IfcCurve*& curve, bool advanced) { + if (c->DynamicType() == STANDARD_TYPE(Geom_Line)) { + IfcSchema::IfcDirection* d; + IfcSchema::IfcCartesianPoint* p; + + Handle_Geom_Line line = Handle_Geom_Line::DownCast(c); + + if (!convert_to_ifc(line->Position().Location(), p, advanced)) { + return 0; + } + if (!convert_to_ifc(line->Position().Direction(), d, advanced)) { + return 0; + } + + IfcSchema::IfcVector* v = new IfcSchema::IfcVector(d, 1.); + curve = new IfcSchema::IfcLine(p, v); + + return 1; + } else if (c->DynamicType() == STANDARD_TYPE(Geom_Circle)) { + IfcSchema::IfcAxis2Placement3D* ax; + + Handle_Geom_Circle circle = Handle_Geom_Circle::DownCast(c); + + convert_to_ifc(circle->Position(), ax, advanced); + curve = new IfcSchema::IfcCircle(ax, circle->Radius()); + + return 1; + } else if (c->DynamicType() == STANDARD_TYPE(Geom_Ellipse)) { + IfcSchema::IfcAxis2Placement3D* ax; + + Handle_Geom_Ellipse ellipse = Handle_Geom_Ellipse::DownCast(c); + + convert_to_ifc(ellipse->Position(), ax, advanced); + curve = new IfcSchema::IfcEllipse(ax, ellipse->MajorRadius(), ellipse->MinorRadius()); + + return 1; + } +#ifdef USE_IFC4 + else if (c->DynamicType() == STANDARD_TYPE(Geom_BSplineCurve)) { + Handle_Geom_BSplineCurve bspline = Handle_Geom_BSplineCurve::DownCast(c); + + IfcSchema::IfcCartesianPoint::list::ptr points(new IfcSchema::IfcCartesianPoint::list); + TColgp_Array1OfPnt poles(1, bspline->NbPoles()); + bspline->Poles(poles); + for (int i = 1; i <= bspline->NbPoles(); ++i) { + IfcSchema::IfcCartesianPoint* p; + if (!convert_to_ifc(poles.Value(i), p, advanced)) { + return 0; + } + points->push(p); + } + IfcSchema::IfcKnotType::IfcKnotType knot_spec = opencascade_knotspec_to_ifc(bspline->KnotDistribution()); + + std::vector mults; + std::vector knots; + std::vector weights; + + TColStd_Array1OfInteger bspline_mults(1, bspline->NbKnots()); + TColStd_Array1OfReal bspline_knots(1, bspline->NbKnots()); + TColStd_Array1OfReal bspline_weights(1, bspline->NbPoles()); + + bspline->Multiplicities(bspline_mults); + bspline->Knots(bspline_knots); + bspline->Weights(bspline_weights); + + opencascade_array_to_vector(bspline_mults, mults); + opencascade_array_to_vector(bspline_knots, knots); + opencascade_array_to_vector(bspline_weights, weights); + + bool rational = false; + for (std::vector::const_iterator it = weights.begin(); it != weights.end(); ++it) { + if ((*it) != 1.) { + rational = true; + break; + } + } + + if (rational) { + curve = new IfcSchema::IfcRationalBSplineCurveWithKnots( + bspline->Degree(), + points, + IfcSchema::IfcBSplineCurveForm::IfcBSplineCurveForm_UNSPECIFIED, + bspline->IsClosed(), + false, + mults, + knots, + knot_spec, + weights + ); + } else { + curve = new IfcSchema::IfcBSplineCurveWithKnots( + bspline->Degree(), + points, + IfcSchema::IfcBSplineCurveForm::IfcBSplineCurveForm_UNSPECIFIED, + bspline->IsClosed(), + false, + mults, + knots, + knot_spec + ); + } + + return 1; + } +#endif + return 0; +} + +template <> +int convert_to_ifc(const Handle_Geom_Surface& s, IfcSchema::IfcSurface*& surface, bool advanced) { + if (s->DynamicType() == STANDARD_TYPE(Geom_Plane)) { + Handle_Geom_Plane plane = Handle_Geom_Plane::DownCast(s); + IfcSchema::IfcAxis2Placement3D* place; + /// @todo: Note that the Ax3 is converted to an Ax2 here + if (!convert_to_ifc(plane->Position().Ax2(), place, advanced)) { + return 0; + } + surface = new IfcSchema::IfcPlane(place); + return 1; + } +#ifdef USE_IFC4 + else if (s->DynamicType() == STANDARD_TYPE(Geom_CylindricalSurface)) { + Handle_Geom_CylindricalSurface cyl = Handle_Geom_CylindricalSurface::DownCast(s); + IfcSchema::IfcAxis2Placement3D* place; + /// @todo: Note that the Ax3 is converted to an Ax2 here + if (!convert_to_ifc(cyl->Position().Ax2(), place, advanced)) { + return 0; + } + surface = new IfcSchema::IfcCylindricalSurface(place, cyl->Radius()); + return 1; + } else if (s->DynamicType() == STANDARD_TYPE(Geom_BSplineSurface)) { + typedef IfcTemplatedEntityListList points_t; + + Handle_Geom_BSplineSurface bspline = Handle_Geom_BSplineSurface::DownCast(s); + points_t::ptr points(new points_t); + + TColgp_Array2OfPnt poles(1, bspline->NbUPoles(), 1, bspline->NbVPoles()); + bspline->Poles(poles); + for (int i = 1; i <= bspline->NbUPoles(); ++i) { + std::vector ps; + ps.reserve(bspline->NbVPoles()); + for (int j = 1; j <= bspline->NbVPoles(); ++j) { + IfcSchema::IfcCartesianPoint* p; + if (!convert_to_ifc(poles.Value(i, j), p, advanced)) { + return 0; + } + ps.push_back(p); + } + points->push(ps); + } + + IfcSchema::IfcKnotType::IfcKnotType knot_spec_u = opencascade_knotspec_to_ifc(bspline->UKnotDistribution()); + IfcSchema::IfcKnotType::IfcKnotType knot_spec_v = opencascade_knotspec_to_ifc(bspline->VKnotDistribution()); + + if (knot_spec_u != knot_spec_v) { + knot_spec_u = IfcSchema::IfcKnotType::IfcKnotType_UNSPECIFIED; + } + + std::vector umults; + std::vector vmults; + std::vector uknots; + std::vector vknots; + std::vector< std::vector > weights; + + TColStd_Array1OfInteger bspline_umults(1, bspline->NbUKnots()); + TColStd_Array1OfInteger bspline_vmults(1, bspline->NbVKnots()); + TColStd_Array1OfReal bspline_uknots(1, bspline->NbUKnots()); + TColStd_Array1OfReal bspline_vknots(1, bspline->NbVKnots()); + TColStd_Array2OfReal bspline_weights(1, bspline->NbUPoles(), 1, bspline->NbVPoles()); + + bspline->UMultiplicities(bspline_umults); + bspline->VMultiplicities(bspline_vmults); + bspline->UKnots(bspline_uknots); + bspline->VKnots(bspline_vknots); + bspline->Weights(bspline_weights); + + opencascade_array_to_vector(bspline_umults, umults); + opencascade_array_to_vector(bspline_vmults, vmults); + opencascade_array_to_vector(bspline_uknots, uknots); + opencascade_array_to_vector(bspline_vknots, vknots); + opencascade_array_to_vector2(bspline_weights, weights); + + bool rational = false; + for (std::vector< std::vector >::const_iterator it = weights.begin(); it != weights.end(); ++it) { + for (std::vector::const_iterator jt = it->begin(); jt != it->end(); ++jt) { + if ((*jt) != 1.) { + rational = true; + break; + } + } + } + + if (rational) { + surface = new IfcSchema::IfcRationalBSplineSurfaceWithKnots( + bspline->UDegree(), + bspline->VDegree(), + points, + IfcSchema::IfcBSplineSurfaceForm::IfcBSplineSurfaceForm_UNSPECIFIED, + bspline->IsUClosed(), + bspline->IsVClosed(), + false, + umults, + vmults, + uknots, + vknots, + knot_spec_u, + weights + ); + } else { + surface = new IfcSchema::IfcBSplineSurfaceWithKnots( + bspline->UDegree(), + bspline->VDegree(), + points, + IfcSchema::IfcBSplineSurfaceForm::IfcBSplineSurfaceForm_UNSPECIFIED, + bspline->IsUClosed(), + bspline->IsVClosed(), + false, + umults, + vmults, + uknots, + vknots, + knot_spec_u + ); + } + + return 1; + } +#endif + return 0; +} + +template <> +int convert_to_ifc(const TopoDS_Edge& e, IfcSchema::IfcCurve*& c, bool advanced) { + double a, b; + IfcSchema::IfcCurve* base; + + Handle_Geom_Curve crv = BRep_Tool::Curve(e, a, b); + if (!convert_to_ifc(crv, base, advanced)) { + return 0; + } + + IfcEntityList::ptr trim1(new IfcEntityList); + IfcEntityList::ptr trim2(new IfcEntityList); + trim1->push(new IfcSchema::IfcParameterValue(a)); + trim2->push(new IfcSchema::IfcParameterValue(b)); + + c = new IfcSchema::IfcTrimmedCurve(base, trim1, trim2, true, IfcSchema::IfcTrimmingPreference::IfcTrimmingPreference_PARAMETER); + + return 1; +} + +template <> +int convert_to_ifc(const TopoDS_Edge& e, IfcSchema::IfcEdge*& edge, bool advanced) { + double a, b; + + TopExp_Explorer exp(e, TopAbs_VERTEX); + if (!exp.More()) return 0; + TopoDS_Vertex v1 = TopoDS::Vertex(exp.Current()); + exp.Next(); + if (!exp.More()) return 0; + TopoDS_Vertex v2 = TopoDS::Vertex(exp.Current()); + + IfcSchema::IfcVertex *vertex1, *vertex2; + if (!(convert_to_ifc(v1, vertex1, advanced) && convert_to_ifc(v2, vertex2, advanced))) { + return 0; + } + + Handle_Geom_Curve crv = BRep_Tool::Curve(e, a, b); + + if (crv.IsNull()) { + return 0; + } + + if (crv->DynamicType() == STANDARD_TYPE(Geom_Line) && !advanced) { + IfcSchema::IfcEdge* edge2 = new IfcSchema::IfcEdge(vertex1, vertex2); + edge = new IfcSchema::IfcOrientedEdge(edge2, true); + return 1; + } else { + IfcSchema::IfcCurve* curve; + if (!convert_to_ifc(crv, curve, advanced)) { + return 0; + } + /// @todo probably not correct + const bool sense = e.Orientation() == TopAbs_FORWARD; + IfcSchema::IfcEdge* edge2 = new IfcSchema::IfcEdgeCurve(vertex1, vertex2, curve, true); + edge = new IfcSchema::IfcOrientedEdge(edge2, sense); + return 1; + } +} + +template <> +int convert_to_ifc(const TopoDS_Wire& wire, IfcSchema::IfcLoop*& loop, bool advanced) { + bool polygonal = true; + for (TopExp_Explorer exp(wire, TopAbs_EDGE); exp.More(); exp.Next()) { + double a, b; + Handle_Geom_Curve crv = BRep_Tool::Curve(TopoDS::Edge(exp.Current()), a, b); + if (crv.IsNull()) { + continue; + } + if (crv->DynamicType() != STANDARD_TYPE(Geom_Line)) { + polygonal = false; + break; + } + } + if (!polygonal && !advanced) { + return 0; + } else if (polygonal && !advanced) { + IfcSchema::IfcCartesianPoint::list::ptr points(new IfcSchema::IfcCartesianPoint::list); + BRepTools_WireExplorer exp(wire); + IfcSchema::IfcCartesianPoint* p; + for (; exp.More(); exp.Next()) { + if (convert_to_ifc(exp.CurrentVertex(), p, advanced)) { + points->push(p); + } else { + return 0; + } + } + loop = new IfcSchema::IfcPolyLoop(points); + return 1; + } else { + IfcSchema::IfcOrientedEdge::list::ptr edges(new IfcSchema::IfcOrientedEdge::list); + BRepTools_WireExplorer exp(wire); + for (; exp.More(); exp.Next()) { + IfcSchema::IfcEdge* edge; + // With advanced set to true convert_to_ifc(TopoDS_Edge&) will always create an IfcOrientedEdge + if (!convert_to_ifc(exp.Current(), edge, true)) { + double a, b; + if (BRep_Tool::Curve(TopoDS::Edge(exp.Current()), a, b).IsNull()) { + continue; + } else { + return 0; + } + } + edges->push(edge->as()); + } + loop = new IfcSchema::IfcEdgeLoop(edges); + return 1; + } +} + +template <> +int convert_to_ifc(const TopoDS_Face& f, IfcSchema::IfcFace*& face, bool advanced) { + Handle_Geom_Surface surf = BRep_Tool::Surface(f); + TopExp_Explorer exp(f, TopAbs_WIRE); + IfcSchema::IfcFaceBound::list::ptr bounds(new IfcSchema::IfcFaceBound::list); + int index = 0; + for (; exp.More(); exp.Next(), ++index) { + IfcSchema::IfcLoop* loop; + if (!convert_to_ifc(TopoDS::Wire(exp.Current()), loop, advanced)) { + return 0; + } + IfcSchema::IfcFaceBound* bnd; + if (index == 0) { + bnd = new IfcSchema::IfcFaceOuterBound(loop, true); + } else { + bnd = new IfcSchema::IfcFaceBound(loop, true); + } + bounds->push(bnd); + } + + const bool is_planar = surf->DynamicType() == STANDARD_TYPE(Geom_Plane); + + if (!is_planar && !advanced) { + return 0; + } + if (is_planar && !advanced) { + face = new IfcSchema::IfcFace(bounds); + return 1; + } else { +#ifdef USE_IFC4 + IfcSchema::IfcSurface* surface; + if (!convert_to_ifc(surf, surface, advanced)) { + return 0; + } + face = new IfcSchema::IfcAdvancedFace(bounds, surface, f.Orientation() == TopAbs_FORWARD); + return 1; +#else + // No IfcAdvancedFace in Ifc2x3 + return 0; +#endif + } +} + +template +int convert_to_ifc(const TopoDS_Shape& s, U*& item, bool advanced) { + IfcSchema::IfcFace::list::ptr faces(new IfcSchema::IfcFace::list); + IfcSchema::IfcFace* f; + for (TopExp_Explorer exp(s, TopAbs_FACE); exp.More(); exp.Next()) { + if (convert_to_ifc(TopoDS::Face(exp.Current()), f, advanced)) { + faces->push(f); + } + } + item = new U(faces); + return faces->size(); +} + +IfcSchema::IfcProductDefinitionShape* IfcGeom::serialise(const TopoDS_Shape& shape, bool advanced) { +#ifndef USE_IFC4 + advanced = false; +#endif + + for (TopExp_Explorer exp(shape, TopAbs_COMPSOLID); exp.More();) { + /// @todo CompSolids are not supported + return 0; + } + + IfcSchema::IfcRepresentation* rep = 0; + IfcSchema::IfcRepresentationItem::list::ptr items(new IfcSchema::IfcRepresentationItem::list); + + // First check if there is a solid with one or more shells + for (TopExp_Explorer exp(shape, TopAbs_SOLID); exp.More(); exp.Next()) { + IfcSchema::IfcClosedShell* outer = 0; + IfcSchema::IfcClosedShell::list::ptr inner(new IfcSchema::IfcClosedShell::list); + for (TopExp_Explorer exp2(exp.Current(), TopAbs_SHELL); exp2.More(); exp2.Next()) { + IfcSchema::IfcClosedShell* shell; + if (!convert_to_ifc(exp2.Current(), shell, advanced)) { + return 0; + } + /// @todo Are shells always in this order or does Orientation() needs to be checked? + if (outer) { + inner->push(shell); + } else { + outer = shell; + } + } + +#ifdef USE_IFC4 + if (advanced) { + if (inner->size()) { + items->push(new IfcSchema::IfcAdvancedBrepWithVoids(outer, inner)); + } else { + items->push(new IfcSchema::IfcAdvancedBrep(outer)); + } + } else +#endif + + /// @todo this is not necessarily correct as the shell is not necessarily facetted. + if (inner->size()) { + items->push(new IfcSchema::IfcFacetedBrepWithVoids(outer, inner)); + } else { + items->push(new IfcSchema::IfcFacetedBrep(outer)); + } + + } + + if (items->size() > 0) { + rep = new IfcSchema::IfcShapeRepresentation(0, std::string("Body"), std::string("Brep"), items); + } else { + + // If not, see if there is a shell + IfcSchema::IfcOpenShell::list::ptr shells(new IfcSchema::IfcOpenShell::list); + for (TopExp_Explorer exp(shape, TopAbs_SHELL); exp.More(); exp.Next()) { + IfcSchema::IfcOpenShell* shell; + if (!convert_to_ifc(exp.Current(), shell, advanced)) { + return 0; + } + shells->push(shell); + } + + if (shells->size() > 0) { + items->push(new IfcSchema::IfcShellBasedSurfaceModel(shells->generalize())); + rep = new IfcSchema::IfcShapeRepresentation(0, std::string("Body"), std::string("Brep"), items); + } else { + + // If not, see if there is are one of more faces. Note that they will be grouped into a shell. + IfcSchema::IfcOpenShell* shell; + int face_count = convert_to_ifc(shape, shell, advanced); + + if (face_count > 0) { + items->push(shell); + rep = new IfcSchema::IfcShapeRepresentation(0, std::string("Body"), std::string("Brep"), items); + } else { + + // If not, see if there are any edges. Note that wires are skipped as + // they are not commonly top-level geometrical descriptions in IFC. + // Also note that edges are written as trimmed curves rather than edges. + + IfcEntityList::ptr edges(new IfcEntityList); + + for (TopExp_Explorer exp(shape, TopAbs_EDGE); exp.More(); exp.Next()) { + IfcSchema::IfcCurve* c; + if (!convert_to_ifc(TopoDS::Edge(exp.Current()), c, advanced)) { + return 0; + } + edges->push(c); + } + + if (edges->size() == 0) { + return 0; + } else if (edges->size() == 1) { + rep = new IfcSchema::IfcShapeRepresentation(0, std::string("Axis"), std::string("Curve2D"), edges->as()); + } else { + // A geometric set is created as that probably (?) makes more sense in IFC + IfcSchema::IfcGeometricCurveSet* curves = new IfcSchema::IfcGeometricCurveSet(edges); + items->push(curves); + rep = new IfcSchema::IfcShapeRepresentation(0, std::string("Axis"), std::string("GeometricCurveSet"), items->as()); + } + + } + } + } + + IfcSchema::IfcRepresentation::list::ptr reps(new IfcSchema::IfcRepresentation::list); + reps->push(rep); + return new IfcSchema::IfcProductDefinitionShape(boost::none, boost::none, reps); +} + +IfcSchema::IfcProductDefinitionShape* IfcGeom::tesselate(const TopoDS_Shape& shape, double deflection) { + BRepMesh_IncrementalMesh(shape, deflection); + + IfcSchema::IfcFace::list::ptr faces(new IfcSchema::IfcFace::list); + + for (TopExp_Explorer exp(shape, TopAbs_FACE); exp.More(); exp.Next()) { + const TopoDS_Face& face = TopoDS::Face(exp.Current()); + TopLoc_Location loc; + Handle(Poly_Triangulation) tri = BRep_Tool::Triangulation(face, loc); + + if (!tri.IsNull()) { + const TColgp_Array1OfPnt& nodes = tri->Nodes(); + std::vector vertices; + for (int i = 1; i <= nodes.Length(); ++i) { + gp_Pnt pnt = nodes(i).Transformed(loc); + std::vector xyz; xyz.push_back(pnt.X()); xyz.push_back(pnt.Y()); xyz.push_back(pnt.Z()); + IfcSchema::IfcCartesianPoint* cpnt = new IfcSchema::IfcCartesianPoint(xyz); + vertices.push_back(cpnt); + } + const Poly_Array1OfTriangle& triangles = tri->Triangles(); + for (int i = 1; i <= triangles.Length(); ++i) { + int n1, n2, n3; + triangles(i).Get(n1, n2, n3); + IfcSchema::IfcCartesianPoint::list::ptr points(new IfcSchema::IfcCartesianPoint::list); + points->push(vertices[n1 - 1]); + points->push(vertices[n2 - 1]); + points->push(vertices[n3 - 1]); + IfcSchema::IfcPolyLoop* loop = new IfcSchema::IfcPolyLoop(points); + IfcSchema::IfcFaceOuterBound* bound = new IfcSchema::IfcFaceOuterBound(loop, face.Orientation() != TopAbs_REVERSED); + IfcSchema::IfcFaceBound::list::ptr bounds(new IfcSchema::IfcFaceBound::list); + bounds->push(bound); + IfcSchema::IfcFace* face2 = new IfcSchema::IfcFace(bounds); + faces->push(face2); + } + } + } + IfcSchema::IfcOpenShell* shell = new IfcSchema::IfcOpenShell(faces); + IfcSchema::IfcConnectedFaceSet::list::ptr shells(new IfcSchema::IfcConnectedFaceSet::list); + shells->push(shell); + IfcSchema::IfcFaceBasedSurfaceModel* surface_model = new IfcSchema::IfcFaceBasedSurfaceModel(shells); + + IfcSchema::IfcRepresentation::list::ptr reps(new IfcSchema::IfcRepresentation::list); + IfcSchema::IfcRepresentationItem::list::ptr items(new IfcSchema::IfcRepresentationItem::list); + + items->push(surface_model); + + IfcSchema::IfcShapeRepresentation* rep = new IfcSchema::IfcShapeRepresentation( + 0, std::string("Facetation"), std::string("SurfaceModel"), items); + + reps->push(rep); + IfcSchema::IfcProductDefinitionShape* shapedef = new IfcSchema::IfcProductDefinitionShape(boost::none, boost::none, reps); + + return shapedef; +} diff --git a/src/ifcgeom/IfcGeomWires.cpp b/src/ifcgeom/IfcGeomWires.cpp index d6e32ecbf0..714244ae96 100644 --- a/src/ifcgeom/IfcGeomWires.cpp +++ b/src/ifcgeom/IfcGeomWires.cpp @@ -83,6 +83,9 @@ #include #include +#include +#include + #include "../ifcgeom/IfcGeom.h" bool IfcGeom::Kernel::convert(const IfcSchema::IfcCompositeCurve* l, TopoDS_Wire& wire) { @@ -390,29 +393,46 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcEdgeCurve* l, TopoDS_Wire& res result = mw; return true; } else if (is_bounded && convert_wire(l->EdgeGeometry(), result)) { - if (!l->SameSense()) std::swap(pnt1, pnt2); - TopExp_Explorer exp(result, TopAbs_EDGE); + 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(); - first = false; + + gp_Pnt a, b; + if (first && last) { - mw.Add(BRepBuilderAPI_MakeEdge(ecrv, p1, p2)); + a = p1; + b = p2; } else if (first) { - gp_Pnt pu; - ecrv->D0(u2, pu); - mw.Add(BRepBuilderAPI_MakeEdge(ecrv, p1, pu)); + a = p1; + ecrv->D0(u2, b); } else if (last) { - gp_Pnt pu; - ecrv->D0(u1, pu); - mw.Add(BRepBuilderAPI_MakeEdge(ecrv, pu, p2)); + ecrv->D0(u1, a); + b = p2; } else { mw.Add(BRepBuilderAPI_MakeEdge(ecrv, u1, u2)); + first = false; + continue; } + + BRep_Builder builder; + TopoDS_Vertex v1, v2; + /// @todo project first and emit warnings accordingly + builder.MakeVertex(v1, a, getValue(GV_PRECISION)); + builder.MakeVertex(v2, b, getValue(GV_PRECISION)); + + mw.Add(BRepBuilderAPI_MakeEdge(ecrv, v1, v2)); + + first = false; } result = mw; return true; @@ -427,13 +447,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcEdgeLoop* l, TopoDS_Wire& resu for (IfcSchema::IfcOrientedEdge::list::it it = li->begin(); it != li->end(); ++it) { TopoDS_Wire w; if (convert_wire(*it, w)) { - if (!(*it)->Orientation()) w.Reverse(); - TopoDS_Iterator topoit(w, false); - for (; topoit.More(); topoit.Next()) { - const TopoDS_Edge& e = TopoDS::Edge(topoit.Value()); - mw.Add(e); - } - // mw.Add(w); + mw.Add(TopoDS::Edge(TopoDS_Iterator(w).Value())); } } result = mw; diff --git a/src/ifcgeom/IfcRegister.h b/src/ifcgeom/IfcRegister.h index c566333a9c..2e6f20297d 100644 --- a/src/ifcgeom/IfcRegister.h +++ b/src/ifcgeom/IfcRegister.h @@ -119,6 +119,7 @@ CURVE(IfcCircle); CURVE(IfcEllipse); CURVE(IfcLine); #ifdef USE_IFC4 +// IfcRationalBSplineCurveWithKnots included CURVE(IfcBSplineCurveWithKnots); #endif diff --git a/src/ifcopenshell-python/ifcopenshell/__init__.py b/src/ifcopenshell-python/ifcopenshell/__init__.py index 1fe59e2b03..bf4b6f43f9 100644 --- a/src/ifcopenshell-python/ifcopenshell/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/__init__.py @@ -17,16 +17,11 @@ # # ############################################################################### +from __future__ import print_function + import os import sys -import numbers import platform -import functools -import itertools - -from functools import reduce - -from . import guid python_distribution = os.path.join(platform.system().lower(), platform.architecture()[0], @@ -37,106 +32,16 @@ sys.path.append(os.path.abspath(os.path.join( try: from . import ifcopenshell_wrapper -except: +except Exception as e: + if int(platform.python_version_tuple()[0]) == 2: + import traceback + traceback.print_exc() + print('-' * 64) raise ImportError("IfcOpenShell not built for '%s'" % python_distribution) - -class entity_instance(object): - def __init__(self, e): - super(entity_instance, self).__setattr__('wrapped_data', e) - def __getattr__(self, name): - INVALID, FORWARD, INVERSE = range(3) - attr_cat = self.wrapped_data.get_attribute_category(name) - if attr_cat == FORWARD: - return entity_instance.wrap_value(self.wrapped_data.get_argument(self.wrapped_data.get_argument_index(name))) - elif attr_cat == INVERSE: - return entity_instance.wrap_value(self.wrapped_data.get_inverse(name)) - else: raise AttributeError("entity instance of type '%s' has no attribute '%s'"%(self.wrapped_data.is_a(), name)) - @staticmethod - def walk(f, g, value): - if isinstance(value, (tuple, list)): return tuple(map(functools.partial(entity_instance.walk, f, g), value)) - elif f(value): return g(value) - else: return value - @staticmethod - def wrap_value(v): - wrap = lambda e: entity_instance(e) - is_instance = lambda e: isinstance(e, ifcopenshell_wrapper.entity_instance) - return entity_instance.walk(is_instance, wrap, v) - @staticmethod - def unwrap_value(v): - unwrap = lambda e: e.wrapped_data - is_instance = lambda e: isinstance(e, entity_instance) - return entity_instance.walk(is_instance, unwrap, v) - def attribute_type(self, attr): - attr_idx = attr if isinstance(attr, numbers.Integral) else self.wrapped_data.get_argument_index(attr) - return self.wrapped_data.get_argument_type(attr_idx) - def attribute_name(self, attr_idx): - return self.wrapped_data.get_argument_name(attr_idx) - def __setattr__(self, key, value): - self[self.wrapped_data.get_argument_index(key)] = value - def __getitem__(self, key): - return entity_instance.wrap_value(self.wrapped_data.get_argument(key)) - def __setitem__(self, idx, value): - if value is None: - self.wrapped_data.setArgumentAsNull(idx) - else: - attr_type = self.attribute_type(idx).title().replace(' ', '') - attr_type = attr_type.replace('Binary', 'String') - attr_type = attr_type.replace('Enumeration', 'String') - try: - if isinstance(value, unicode): value = value.encode("utf-8") - except: pass - getattr(self.wrapped_data, "setArgumentAs%s" % attr_type)(idx, entity_instance.unwrap_value(value)) - return value - def __len__(self): return len(self.wrapped_data) - def __repr__(self): return repr(self.wrapped_data) - def is_a(self, *args): return self.wrapped_data.is_a(*args) - def id(self): return self.wrapped_data.id() - def __eq__(self, other): - if type(self) != type(other): return False - return self.wrapped_data == other.wrapped_data - def __hash__(self): - return hash((self.id(), self.wrapped_data.file_pointer())) - def __dir__(self): - return sorted(set(itertools.chain( - dir(type(self)), - self.wrapped_data.get_attribute_names(), - self.wrapped_data.get_inverse_attribute_names() - ))) - - -class file(object): - def __init__(self, f=None): - self.wrapped_data = f or ifcopenshell_wrapper.file(True) - def create_entity(self,type,*args,**kwargs): - e = entity_instance(ifcopenshell_wrapper.entity_instance(type)) - attrs = list(enumerate(args)) + \ - [(e.wrapped_data.get_argument_index(name), arg) for name, arg in kwargs.items()] - for idx, arg in attrs: e[idx] = arg - self.wrapped_data.add(e.wrapped_data) - e.wrapped_data.this.disown() - return e - def __getattr__(self, attr): - if attr[0:6] == 'create': return functools.partial(self.create_entity,attr[6:]) - else: return getattr(self.wrapped_data, attr) - def __getitem__(self, key): - if isinstance(key, numbers.Integral): - return entity_instance(self.wrapped_data.by_id(key)) - elif isinstance(key, str): - return entity_instance(self.wrapped_data.by_guid(key)) - def by_id(self, id): return self[id] - def by_guid(self, guid): return self[guid] - def add(self, inst): - inst.wrapped_data.this.disown() - return entity_instance(self.wrapped_data.add(inst.wrapped_data)) - def by_type(self, type): - return [entity_instance(e) for e in self.wrapped_data.by_type(type)] - def traverse(self, inst): - return [entity_instance(e) for e in self.wrapped_data.traverse(inst.wrapped_data)] - def remove(self, inst): - return self.wrapped_data.remove(inst.wrapped_data) - def __iter__(self): - return iter(self[id] for id in self.wrapped_data.entity_names()) - + +from . import guid +from .file import file +from .entity_instance import entity_instance def open(fn=None): return file(ifcopenshell_wrapper.open(os.path.abspath(fn))) if fn else file() diff --git a/src/ifcopenshell-python/ifcopenshell/entity_instance.py b/src/ifcopenshell-python/ifcopenshell/entity_instance.py new file mode 100644 index 0000000000..4bb25f026f --- /dev/null +++ b/src/ifcopenshell-python/ifcopenshell/entity_instance.py @@ -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 . # +# # +############################################################################### + +import numbers +import functools +import itertools + +from . import ifcopenshell_wrapper + +class entity_instance(object): + def __init__(self, e): + super(entity_instance, self).__setattr__('wrapped_data', e) + def __getattr__(self, name): + INVALID, FORWARD, INVERSE = range(3) + attr_cat = self.wrapped_data.get_attribute_category(name) + if attr_cat == FORWARD: + return entity_instance.wrap_value(self.wrapped_data.get_argument(self.wrapped_data.get_argument_index(name))) + elif attr_cat == INVERSE: + return entity_instance.wrap_value(self.wrapped_data.get_inverse(name)) + else: raise AttributeError("entity instance of type '%s' has no attribute '%s'"%(self.wrapped_data.is_a(), name)) + @staticmethod + def walk(f, g, value): + if isinstance(value, (tuple, list)): return tuple(map(functools.partial(entity_instance.walk, f, g), value)) + elif f(value): return g(value) + else: return value + @staticmethod + def wrap_value(v): + wrap = lambda e: entity_instance(e) + is_instance = lambda e: isinstance(e, ifcopenshell_wrapper.entity_instance) + return entity_instance.walk(is_instance, wrap, v) + @staticmethod + def unwrap_value(v): + unwrap = lambda e: e.wrapped_data + is_instance = lambda e: isinstance(e, entity_instance) + return entity_instance.walk(is_instance, unwrap, v) + def attribute_type(self, attr): + attr_idx = attr if isinstance(attr, numbers.Integral) else self.wrapped_data.get_argument_index(attr) + return self.wrapped_data.get_argument_type(attr_idx) + def attribute_name(self, attr_idx): + return self.wrapped_data.get_argument_name(attr_idx) + def __setattr__(self, key, value): + self[self.wrapped_data.get_argument_index(key)] = value + def __getitem__(self, key): + return entity_instance.wrap_value(self.wrapped_data.get_argument(key)) + def __setitem__(self, idx, value): + if value is None: + self.wrapped_data.setArgumentAsNull(idx) + else: + attr_type = self.attribute_type(idx).title().replace(' ', '') + attr_type = attr_type.replace('Binary', 'String') + attr_type = attr_type.replace('Enumeration', 'String') + try: + if isinstance(value, unicode): value = value.encode("utf-8") + except: pass + getattr(self.wrapped_data, "setArgumentAs%s" % attr_type)(idx, entity_instance.unwrap_value(value)) + return value + def __len__(self): return len(self.wrapped_data) + def __repr__(self): return repr(self.wrapped_data) + def is_a(self, *args): return self.wrapped_data.is_a(*args) + def id(self): return self.wrapped_data.id() + def __eq__(self, other): + if type(self) != type(other): return False + return self.wrapped_data == other.wrapped_data + def __hash__(self): + return hash((self.id(), self.wrapped_data.file_pointer())) + def __dir__(self): + return sorted(set(itertools.chain( + dir(type(self)), + self.wrapped_data.get_attribute_names(), + self.wrapped_data.get_inverse_attribute_names() + ))) diff --git a/src/ifcopenshell-python/ifcopenshell/file.py b/src/ifcopenshell-python/ifcopenshell/file.py new file mode 100644 index 0000000000..421e8bf34b --- /dev/null +++ b/src/ifcopenshell-python/ifcopenshell/file.py @@ -0,0 +1,57 @@ +############################################################################### +# # +# 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 . # +# # +############################################################################### + +import numbers +import functools + +from . import ifcopenshell_wrapper +from .entity_instance import entity_instance + +class file(object): + def __init__(self, f=None): + self.wrapped_data = f or ifcopenshell_wrapper.file(True) + def create_entity(self,type,*args,**kwargs): + e = entity_instance(ifcopenshell_wrapper.entity_instance(type)) + attrs = list(enumerate(args)) + \ + [(e.wrapped_data.get_argument_index(name), arg) for name, arg in kwargs.items()] + for idx, arg in attrs: e[idx] = arg + self.wrapped_data.add(e.wrapped_data) + e.wrapped_data.this.disown() + return e + def __getattr__(self, attr): + if attr[0:6] == 'create': return functools.partial(self.create_entity,attr[6:]) + else: return getattr(self.wrapped_data, attr) + def __getitem__(self, key): + if isinstance(key, numbers.Integral): + return entity_instance(self.wrapped_data.by_id(key)) + elif isinstance(key, str): + return entity_instance(self.wrapped_data.by_guid(key)) + def by_id(self, id): return self[id] + def by_guid(self, guid): return self[guid] + def add(self, inst): + inst.wrapped_data.this.disown() + return entity_instance(self.wrapped_data.add(inst.wrapped_data)) + def by_type(self, type): + return [entity_instance(e) for e in self.wrapped_data.by_type(type)] + def traverse(self, inst): + return [entity_instance(e) for e in self.wrapped_data.traverse(inst.wrapped_data)] + def remove(self, inst): + return self.wrapped_data.remove(inst.wrapped_data) + def __iter__(self): + return iter(self[id] for id in self.wrapped_data.entity_names()) diff --git a/src/ifcopenshell-python/ifcopenshell/geom/main.py b/src/ifcopenshell-python/ifcopenshell/geom/main.py index b325793df2..28a148a3a5 100644 --- a/src/ifcopenshell-python/ifcopenshell/geom/main.py +++ b/src/ifcopenshell-python/ifcopenshell/geom/main.py @@ -21,6 +21,7 @@ import os import sys from .. import ifcopenshell_wrapper +from ..entity_instance import entity_instance def has_occ(): try: import OCC.BRepTools @@ -82,5 +83,19 @@ def iterate(settings, filename): while True: yield it.get() if not it.next(): break - - + +def make_shape_function(fn): + entity_instance_or_none = lambda e: None if e is None else entity_instance(e) + if has_occ: + import OCC.TopoDS + def _(string_or_shape, *args): + if isinstance(string_or_shape, OCC.TopoDS.TopoDS_Shape): + string_or_shape = utils.serialize_shape(string_or_shape) + return entity_instance_or_none(fn(string_or_shape, *args)) + else: + def _(string, *args): + return entity_instance_or_none(fn(string, *args)) + return _ + +serialise = make_shape_function(ifcopenshell_wrapper.serialise) +tesselate = make_shape_function(ifcopenshell_wrapper.tesselate) diff --git a/src/ifcopenshell-python/ifcopenshell/geom/occ_utils.py b/src/ifcopenshell-python/ifcopenshell/geom/occ_utils.py index 97186735a7..2e2fd35015 100644 --- a/src/ifcopenshell-python/ifcopenshell/geom/occ_utils.py +++ b/src/ifcopenshell-python/ifcopenshell/geom/occ_utils.py @@ -180,7 +180,12 @@ def get_bounding_box_center(bbox): bbmin[0], bbmin[1], bbmin[2], bbmax[0], bbmax[1], bbmax[2] = bbox.Get() return OCC.gp.gp_Pnt(*map(lambda xy: (xy[0]+xy[1])/2., zip(bbmin, bbmax))) - + +def serialize_shape(shape): + shapes = OCC.BRepTools.BRepTools_ShapeSet() + shapes.Add(shape) + return shapes.WriteToString() + def create_shape_from_serialization(brep_object): import OCC.BRepTools diff --git a/src/ifcwrap/IfcGeomWrapper.i b/src/ifcwrap/IfcGeomWrapper.i index 07222a743a..1f66eab9fd 100644 --- a/src/ifcwrap/IfcGeomWrapper.i +++ b/src/ifcwrap/IfcGeomWrapper.i @@ -389,6 +389,34 @@ struct ShapeRTTI : public boost::static_visitor } return boost::variant*, IfcGeom::Representation::Representation*>(); } + + IfcParse::IfcLateBoundEntity* serialise(const std::string& s, bool advanced=true) { + std::stringstream stream(s); + BRepTools_ShapeSet shapes; + shapes.Read(stream); + const TopoDS_Shape& shp = shapes.Shape(shapes.NbShapes()); + + const IfcUtil::IfcBaseClass* e = IfcGeom::serialise(shp, advanced); + if (e) { + return new IfcParse::IfcLateBoundEntity(e->entity); + } else { + return 0; + } + } + + IfcParse::IfcLateBoundEntity* tesselate(const std::string& s, double d) { + std::stringstream stream(s); + BRepTools_ShapeSet shapes; + shapes.Read(stream); + const TopoDS_Shape& shp = shapes.Shape(shapes.NbShapes()); + + const IfcUtil::IfcBaseClass* e = IfcGeom::tesselate(shp, d); + if (e) { + return new IfcParse::IfcLateBoundEntity(e->entity); + } else { + return 0; + } + } %} namespace IfcGeom { diff --git a/src/ifcwrap/IfcPython.i b/src/ifcwrap/IfcPython.i index ef42830a14..419de7e2fd 100644 --- a/src/ifcwrap/IfcPython.i +++ b/src/ifcwrap/IfcPython.i @@ -76,6 +76,8 @@ #else #include "../ifcparse/Ifc2x3-latebound.h" #endif + + #include %} %include "utils/type_conversion.i"