Skip to content

Commit aebb9e3

Browse files
authored
Geometry serialization (IfcOpenShell#71)
Serialize geometry to IFC instances directly from a TopoDS_Shape
1 parent 90ad3a4 commit aebb9e3

18 files changed

+1161
-206
lines changed

src/examples/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,7 @@ set_target_properties(IfcParseExamples PROPERTIES FOLDER Examples)
2424
ADD_EXECUTABLE(IfcOpenHouse IfcOpenHouse.cpp)
2525
TARGET_LINK_LIBRARIES(IfcOpenHouse ${IFCOPENSHELL_LIBRARIES} ${OPENCASCADE_LIBRARIES})
2626
set_target_properties(IfcOpenHouse PROPERTIES FOLDER Examples)
27+
28+
ADD_EXECUTABLE(IfcAdvancedHouse IfcAdvancedHouse.cpp)
29+
TARGET_LINK_LIBRARIES(IfcAdvancedHouse ${IFCLIBS} ${OPENCASCADE_LIBRARIES})
30+
set_target_properties(IfcAdvancedHouse PROPERTIES FOLDER Examples)

src/examples/IfcAdvancedHouse.cpp

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
/********************************************************************************
2+
* *
3+
* This file is part of IfcOpenShell. *
4+
* *
5+
* IfcOpenShell is free software: you can redistribute it and/or modify *
6+
* it under the terms of the Lesser GNU General Public License as published by *
7+
* the Free Software Foundation, either version 3.0 of the License, or *
8+
* (at your option) any later version. *
9+
* *
10+
* IfcOpenShell is distributed in the hope that it will be useful, *
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13+
* Lesser GNU General Public License for more details. *
14+
* *
15+
* You should have received a copy of the Lesser GNU General Public License *
16+
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
17+
* *
18+
********************************************************************************/
19+
20+
#include <string>
21+
#include <iostream>
22+
#include <fstream>
23+
24+
#include <TColgp_Array2OfPnt.hxx>
25+
#include <TColgp_Array1OfPnt.hxx>
26+
#include <TColStd_Array1OfReal.hxx>
27+
#include <TColStd_Array1OfInteger.hxx>
28+
29+
#include <Geom_BSplineSurface.hxx>
30+
31+
#include <BRepBuilderAPI_MakeFace.hxx>
32+
#include <BRepBuilderAPI_NurbsConvert.hxx>
33+
34+
#include <BRepPrimAPI_MakeBox.hxx>
35+
#include <BRepPrimAPI_MakeSphere.hxx>
36+
37+
#include <BRepAlgoAPI_Cut.hxx>
38+
39+
#include <Standard_Version.hxx>
40+
41+
#ifdef USE_IFC4
42+
#include "../ifcparse/Ifc4.h"
43+
#else
44+
#include "../ifcparse/Ifc2x3.h"
45+
#endif
46+
47+
#include "../ifcparse/IfcUtil.h"
48+
#include "../ifcparse/IfcHierarchyHelper.h"
49+
#include "../ifcgeom/IfcGeom.h"
50+
51+
#if USE_VLD
52+
#include <vld.h>
53+
#endif
54+
55+
// Some convenience typedefs and definitions.
56+
typedef std::string S;
57+
typedef IfcParse::IfcGlobalId guid;
58+
typedef std::pair<double, double> XY;
59+
boost::none_t const null = boost::none;
60+
61+
// The creation of Nurbs-surface for the IfcSite mesh, to be implemented lateron
62+
void createGroundShape(TopoDS_Shape& shape);
63+
64+
int main() {
65+
66+
// The IfcHierarchyHelper is a subclass of the regular IfcFile that provides several
67+
// convenience functions for working with geometry in IFC files.
68+
IfcHierarchyHelper file;
69+
file.header().file_name().name("IfcAdvancedHouse.ifc");
70+
71+
IfcSchema::IfcBuilding* building = file.addBuilding();
72+
// By adding a building, a hierarchy has been automatically created that consists of the following
73+
// structure: IfcProject > IfcSite > IfcBuilding
74+
75+
// Lateron changing the name of the IfcProject can be done by obtaining a reference to the
76+
// project, which has been created automatically.
77+
file.getSingle<IfcSchema::IfcProject>()->setName("IfcOpenHouse");
78+
79+
// To demonstrate the ability to serialize arbitrary opencascade solids a building envelope is
80+
// constructed by applying boolean operations. Naturally, in IFC, building elements should be
81+
// modeled separately, with rich parametric and relational semantics. Creating geometry in this
82+
// way does not preserve any history and is merely a demonstration of technical capabilities.
83+
TopoDS_Shape outer = BRepPrimAPI_MakeBox(gp_Pnt(-5000., -180., -2000.), gp_Pnt(5000., 5180., 3000.)).Shape();
84+
TopoDS_Shape inner = BRepPrimAPI_MakeBox(gp_Pnt(-4640., 180., 0.), gp_Pnt(4640., 4820., 3000.)).Shape();
85+
TopoDS_Shape window1 = BRepPrimAPI_MakeBox(gp_Pnt(-5000., -180., 400.), gp_Pnt( 500., 1180., 2000.)).Shape();
86+
TopoDS_Shape window2 = BRepPrimAPI_MakeBox(gp_Pnt( 2070., -180., 400.), gp_Pnt(3930., 180., 2000.)).Shape();
87+
88+
TopoDS_Shape building_shell = BRepAlgoAPI_Cut(
89+
BRepAlgoAPI_Cut(
90+
BRepAlgoAPI_Cut(outer, inner),
91+
window1
92+
),
93+
window2
94+
);
95+
96+
// Since the solid consists only of planar faces and straight edges it can be serialized as an
97+
// IfcFacetedBRep. If it would not be a polyhedron, serialise() can only be successful when linked
98+
// to the IFC4 model and with `advanced` set to `true` which introduces IfcAdvancedFace. It would
99+
// return `0` otherwise.
100+
IfcSchema::IfcProductDefinitionShape* building_shape = IfcGeom::serialise(building_shell, false);
101+
102+
file.addEntity(building_shape);
103+
IfcSchema::IfcRepresentation* rep = *building_shape->Representations()->begin();
104+
rep->setContextOfItems(file.getRepresentationContext("model"));
105+
106+
building->setRepresentation(building_shape);
107+
108+
// A pale white colour is assigned to the building.
109+
file.setSurfaceColour(
110+
building_shape, 0.75, 0.73, 0.68);
111+
112+
// For the ground mesh of the IfcSite we will use a Nurbs surface created in Open Cascade. Only
113+
// in IFC4 the surface can be directly serialized. In IFC2X3 the it will have to be tesselated.
114+
TopoDS_Shape shape;
115+
createGroundShape(shape);
116+
117+
IfcSchema::IfcProductDefinitionShape* ground_representation = IfcGeom::serialise(shape, true);
118+
if (!ground_representation) {
119+
ground_representation = IfcGeom::tesselate(shape, 100.);
120+
}
121+
file.getSingle<IfcSchema::IfcSite>()->setRepresentation(ground_representation);
122+
123+
IfcSchema::IfcRepresentation::list::ptr ground_reps = file.getSingle<IfcSchema::IfcSite>()->Representation()->Representations();
124+
for (IfcSchema::IfcRepresentation::list::it it = ground_reps->begin(); it != ground_reps->end(); ++it) {
125+
(*it)->setContextOfItems(file.getRepresentationContext("Model"));
126+
}
127+
file.addEntity(ground_representation);
128+
file.setSurfaceColour(ground_representation, 0.15, 0.25, 0.05);
129+
130+
/*
131+
// Note that IFC lacks elementary surfaces that STEP does have, such as spherical_surface.
132+
// BRepBuilderAPI_NurbsConvert can be used to serialize such surfaces as nurbs surfaces.
133+
TopoDS_Shape sphere = BRepPrimAPI_MakeSphere(gp_Pnt(), 1000.).Shape();
134+
IfcSchema::IfcProductDefinitionShape* sphere_representation = IfcGeom::serialise(sphere, true);
135+
if (S(IfcSchema::Identifier) == "IFC4") {
136+
sphere = BRepBuilderAPI_NurbsConvert(sphere, true).Shape();
137+
sphere_representation = IfcGeom::serialise(sphere, true);
138+
}
139+
*/
140+
141+
// Finally create a file stream for our output and write the IFC file to it.
142+
std::ofstream f("IfcAdvancedHouse.ifc");
143+
f << file;
144+
}
145+
146+
void createGroundShape(TopoDS_Shape& shape) {
147+
TColgp_Array2OfPnt cv (0, 4, 0, 4);
148+
cv.SetValue(0, 0, gp_Pnt(-10000, -10000, -4130));
149+
cv.SetValue(0, 1, gp_Pnt(-10000, -4330, -4130));
150+
cv.SetValue(0, 2, gp_Pnt(-10000, 0, -5130));
151+
cv.SetValue(0, 3, gp_Pnt(-10000, 4330, -7130));
152+
cv.SetValue(0, 4, gp_Pnt(-10000, 10000, -7130));
153+
cv.SetValue(1, 0, gp_Pnt( -3330, -10000, -5130));
154+
cv.SetValue(1, 1, gp_Pnt( -7670, -3670, 5000));
155+
cv.SetValue(1, 2, gp_Pnt( -9000, 0, 1000));
156+
cv.SetValue(1, 3, gp_Pnt( -7670, 7670, 6000));
157+
cv.SetValue(1, 4, gp_Pnt( -3330, 10000, -4130));
158+
cv.SetValue(2, 0, gp_Pnt( 0, -10000, -5530));
159+
cv.SetValue(2, 1, gp_Pnt( 0, -3670, 3000));
160+
cv.SetValue(2, 2, gp_Pnt( 0, 0, -12000));
161+
cv.SetValue(2, 3, gp_Pnt( 0, 7670, 1500));
162+
cv.SetValue(2, 4, gp_Pnt( 0, 10000, -4130));
163+
cv.SetValue(3, 0, gp_Pnt( 3330, -10000, -6130));
164+
cv.SetValue(3, 1, gp_Pnt( 7670, -3670, 6000));
165+
cv.SetValue(3, 2, gp_Pnt( 9000, 0, 5000));
166+
cv.SetValue(3, 3, gp_Pnt( 7670, 9000, 7000));
167+
cv.SetValue(3, 4, gp_Pnt( 3330, 10000, -4130));
168+
cv.SetValue(4, 0, gp_Pnt( 10000, -10000, -6130));
169+
cv.SetValue(4, 1, gp_Pnt( 10000, -4330, -5130));
170+
cv.SetValue(4, 2, gp_Pnt( 10000, 0, -4130));
171+
cv.SetValue(4, 3, gp_Pnt( 10000, 4330, -4130));
172+
cv.SetValue(4, 4, gp_Pnt( 10000, 10000, -8130));
173+
TColStd_Array1OfReal knots(0, 1);
174+
knots(0) = 0;
175+
knots(1) = 1;
176+
TColStd_Array1OfInteger mult(0, 1);
177+
mult(0) = 5;
178+
mult(1) = 5;
179+
Handle(Geom_BSplineSurface) surf = new Geom_BSplineSurface(cv, knots, knots, mult, mult, 4, 4);
180+
#if OCC_VERSION_HEX < 0x60502
181+
shape = BRepBuilderAPI_MakeFace(surf);
182+
#else
183+
shape = BRepBuilderAPI_MakeFace(surf, Precision::Confusion());
184+
#endif
185+
}

src/examples/IfcOpenHouse.cpp

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -262,12 +262,11 @@ int main() {
262262
// will be tesselated using the deflection specified.
263263
TopoDS_Shape shape;
264264
createGroundShape(shape);
265-
IfcEntityList::ptr geometrical_entities(new IfcEntityList);
266-
IfcSchema::IfcProductDefinitionShape* ground_representation = IfcGeom::tesselate(shape, 100., geometrical_entities);
265+
IfcSchema::IfcProductDefinitionShape* ground_representation = IfcGeom::tesselate(shape, 100.);
267266
file.getSingle<IfcSchema::IfcSite>()->setRepresentation(ground_representation);
268-
file.addEntities(geometrical_entities);
269-
IfcSchema::IfcShapeRepresentation::list::ptr ground_reps = geometrical_entities->as<IfcSchema::IfcShapeRepresentation>();
270-
for (IfcSchema::IfcShapeRepresentation::list::it it = ground_reps->begin(); it != ground_reps->end(); ++it) {
267+
268+
IfcSchema::IfcRepresentation::list::ptr ground_reps = file.getSingle<IfcSchema::IfcSite>()->Representation()->Representations();
269+
for (IfcSchema::IfcRepresentation::list::it it = ground_reps->begin(); it != ground_reps->end(); ++it) {
271270
(*it)->setContextOfItems(file.getRepresentationContext("Model"));
272271
}
273272
file.setSurfaceColour(ground_representation, 0.15, 0.25, 0.05);

src/ifcgeom/IfcGeom.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,8 @@ class Kernel {
300300

301301
};
302302

303-
IfcSchema::IfcProductDefinitionShape* tesselate(TopoDS_Shape& shape, double deflection, IfcEntityList::ptr es);
303+
IfcSchema::IfcProductDefinitionShape* tesselate(const TopoDS_Shape& shape, double deflection);
304+
IfcSchema::IfcProductDefinitionShape* serialise(const TopoDS_Shape& shape, bool advanced);
304305

305306
}
306307
#endif

src/ifcgeom/IfcGeomCurves.cpp

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,17 +144,32 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcLine* l, Handle(Geom_Curve)& c
144144
#ifdef USE_IFC4
145145
bool IfcGeom::Kernel::convert(const IfcSchema::IfcBSplineCurveWithKnots* l, Handle(Geom_Curve)& curve) {
146146

147+
const bool is_rational = l->is(IfcSchema::Type::IfcRationalBSplineCurveWithKnots);
148+
147149
const IfcSchema::IfcCartesianPoint::list::ptr cps = l->ControlPointsList();
148150
const std::vector<int> mults = l->KnotMultiplicities();
149151
const std::vector<double> knots = l->Knots();
150152

151153
TColgp_Array1OfPnt Poles(0, cps->size() - 1);
154+
TColStd_Array1OfReal Weights(0, cps->size() - 1);
152155
TColStd_Array1OfReal Knots(0, (int)knots.size() - 1);
153156
TColStd_Array1OfInteger Mults(0, (int)mults.size() - 1);
154157
Standard_Integer Degree = l->Degree();
155158
Standard_Boolean Periodic = l->ClosedCurve();
159+
160+
int i;
161+
162+
if (is_rational) {
163+
IfcSchema::IfcRationalBSplineCurveWithKnots* rl = (IfcSchema::IfcRationalBSplineCurveWithKnots*)l;
164+
std::vector<double> weights = rl->WeightsData();
165+
166+
i = 0;
167+
for (std::vector<double>::const_iterator it = weights.begin(); it != weights.end(); ++it, ++i) {
168+
Weights(i) = *it;
169+
}
170+
}
156171

157-
int i = 0;
172+
i = 0;
158173
for (IfcSchema::IfcCartesianPoint::list::it it = cps->begin(); it != cps->end(); ++it, ++i) {
159174
gp_Pnt pnt;
160175
if (!convert(*it, pnt)) return false;
@@ -171,7 +186,11 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcBSplineCurveWithKnots* l, Hand
171186
Knots(i) = *it;
172187
}
173188

174-
curve = new Geom_BSplineCurve(Poles, Knots, Mults, Degree, Periodic);
189+
if (is_rational) {
190+
curve = new Geom_BSplineCurve(Poles, Weights, Knots, Mults, Degree, Periodic);
191+
} else {
192+
curve = new Geom_BSplineCurve(Poles, Knots, Mults, Degree, Periodic);
193+
}
175194
return true;
176195
}
177196
#endif

src/ifcgeom/IfcGeomFaces.cpp

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575

7676
#include <BRepAlgoAPI_Cut.hxx>
7777

78+
#include <ShapeFix_Edge.hxx>
7879
#include <ShapeFix_Shape.hxx>
7980
#include <ShapeFix_ShapeTolerance.hxx>
8081
#include <ShapeFix_Solid.hxx>
@@ -207,7 +208,8 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcFace* l, TopoDS_Shape& face) {
207208
if (face_surface.IsNull()) {
208209
mf = new BRepBuilderAPI_MakeFace(wire);
209210
} else {
210-
mf = new BRepBuilderAPI_MakeFace(face_surface, wire);
211+
/// @todo check necessity of false here
212+
mf = new BRepBuilderAPI_MakeFace(face_surface, wire, false);
211213
}
212214

213215
/* BRepBuilderAPI_FaceError er = mf->Error();
@@ -221,13 +223,18 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcFace* l, TopoDS_Shape& face) {
221223
if (mf->IsDone()) {
222224
TopoDS_Face outer_face_bound = mf->Face();
223225

224-
// BRepCheck_Face might raise exceptions in case of face surfaces. Therefore fix orientation regardless.
226+
// In case of (non-planar) face surface, p-curves need to be computed.
227+
// For planar faces, Open Cascade generates p-curves on the fly.
225228
if (!face_surface.IsNull()) {
226-
ShapeFix_Face fix(outer_face_bound);
227-
fix.FixOrientation();
228-
fix.Perform();
229-
outer_face_bound = fix.Face();
230-
} else if (BRepCheck_Face(outer_face_bound).OrientationOfWires() == BRepCheck_BadOrientationOfSubshape) {
229+
TopExp_Explorer exp(outer_face_bound, TopAbs_EDGE);
230+
for (; exp.More(); exp.Next()) {
231+
const TopoDS_Edge& edge = TopoDS::Edge(exp.Current());
232+
ShapeFix_Edge fix_edge;
233+
fix_edge.FixAddPCurve(edge, outer_face_bound, false, getValue(GV_PRECISION));
234+
}
235+
}
236+
237+
if (BRepCheck_Face(outer_face_bound).OrientationOfWires() == BRepCheck_BadOrientationOfSubshape) {
231238
wire.Reverse();
232239
same_sense = !same_sense;
233240
delete mf;

src/ifcgeom/IfcGeomFunctions.cpp

Lines changed: 0 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -720,70 +720,6 @@ double IfcGeom::Kernel::getValue(GeomValue var) const {
720720
return 0;
721721
}
722722

723-
IfcSchema::IfcProductDefinitionShape* IfcGeom::tesselate(TopoDS_Shape& shape, double deflection, IfcEntityList::ptr es) {
724-
BRepMesh_IncrementalMesh(shape, deflection);
725-
726-
IfcSchema::IfcFace::list::ptr faces (new IfcSchema::IfcFace::list);
727-
728-
for (TopExp_Explorer exp(shape, TopAbs_FACE); exp.More(); exp.Next()) {
729-
const TopoDS_Face& face = TopoDS::Face(exp.Current());
730-
TopLoc_Location loc;
731-
Handle(Poly_Triangulation) tri = BRep_Tool::Triangulation(face, loc);
732-
733-
if (! tri.IsNull()) {
734-
const TColgp_Array1OfPnt& nodes = tri->Nodes();
735-
std::vector<IfcSchema::IfcCartesianPoint*> vertices;
736-
for (int i = 1; i <= nodes.Length(); ++i) {
737-
gp_Pnt pnt = nodes(i).Transformed(loc);
738-
std::vector<double> xyz; xyz.push_back(pnt.X()); xyz.push_back(pnt.Y()); xyz.push_back(pnt.Z());
739-
IfcSchema::IfcCartesianPoint* cpnt = new IfcSchema::IfcCartesianPoint(xyz);
740-
vertices.push_back(cpnt);
741-
es->push(cpnt);
742-
}
743-
const Poly_Array1OfTriangle& triangles = tri->Triangles();
744-
for (int i = 1; i <= triangles.Length(); ++ i) {
745-
int n1, n2, n3;
746-
triangles(i).Get(n1, n2, n3);
747-
IfcSchema::IfcCartesianPoint::list::ptr points (new IfcSchema::IfcCartesianPoint::list);
748-
points->push(vertices[n1-1]);
749-
points->push(vertices[n2-1]);
750-
points->push(vertices[n3-1]);
751-
IfcSchema::IfcPolyLoop* loop = new IfcSchema::IfcPolyLoop(points);
752-
IfcSchema::IfcFaceOuterBound* bound = new IfcSchema::IfcFaceOuterBound(loop, face.Orientation() != TopAbs_REVERSED);
753-
IfcSchema::IfcFaceBound::list::ptr bounds (new IfcSchema::IfcFaceBound::list);
754-
bounds->push(bound);
755-
IfcSchema::IfcFace* face2 = new IfcSchema::IfcFace(bounds);
756-
es->push(loop);
757-
es->push(bound);
758-
es->push(face2);
759-
faces->push(face2);
760-
}
761-
}
762-
}
763-
IfcSchema::IfcOpenShell* shell = new IfcSchema::IfcOpenShell(faces);
764-
IfcSchema::IfcConnectedFaceSet::list::ptr shells (new IfcSchema::IfcConnectedFaceSet::list);
765-
shells->push(shell);
766-
IfcSchema::IfcFaceBasedSurfaceModel* surface_model = new IfcSchema::IfcFaceBasedSurfaceModel(shells);
767-
768-
IfcSchema::IfcRepresentation::list::ptr reps (new IfcSchema::IfcRepresentation::list);
769-
IfcSchema::IfcRepresentationItem::list::ptr items (new IfcSchema::IfcRepresentationItem::list);
770-
771-
items->push(surface_model);
772-
773-
IfcSchema::IfcShapeRepresentation* rep = new IfcSchema::IfcShapeRepresentation(
774-
0, std::string("Facetation"), std::string("SurfaceModel"), items);
775-
776-
reps->push(rep);
777-
IfcSchema::IfcProductDefinitionShape* shapedef = new IfcSchema::IfcProductDefinitionShape(boost::none, boost::none, reps);
778-
779-
es->push(shell);
780-
es->push(surface_model);
781-
es->push(rep);
782-
es->push(shapedef);
783-
784-
return shapedef;
785-
}
786-
787723
// Returns the vertex part of an TopoDS_Edge edge that is not TopoDS_Vertex vertex
788724
TopoDS_Vertex find_other(const TopoDS_Edge& edge, const TopoDS_Vertex& vertex) {
789725
TopExp_Explorer exp(edge, TopAbs_VERTEX);

0 commit comments

Comments
 (0)