Skip to content

Commit b4e1b15

Browse files
author
aothms
committed
Add support for IfcBooleanResult, IfcBlock, IfcRectangularPyramid, IfcRightCircularCylinder, IfcRightCircularCone, IfcSphere, IfcCsgSolid, IfcCurveBoundedPlane, IfcRectangularTrimmedSurface, IfcSurfaceCurveSweptAreaSolid, IfcCylindricalSurface. Add CSG example.
1 parent a2b2add commit b4e1b15

File tree

6 files changed

+574
-27
lines changed

6 files changed

+574
-27
lines changed

src/examples/csg_primitive.cpp

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
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+
/********************************************************************************
21+
* *
22+
* Example that generates a Constructive Solid Geometry example *
23+
* *
24+
********************************************************************************/
25+
26+
#include <string>
27+
#include <iostream>
28+
#include <fstream>
29+
30+
#include "../ifcparse/Ifc2x3.h"
31+
#include "../ifcparse/IfcUtil.h"
32+
#include "../ifcparse/IfcHierarchyHelper.h"
33+
34+
typedef std::string S;
35+
typedef IfcWrite::IfcGuidHelper guid;
36+
boost::none_t const null = (static_cast<boost::none_t>(0));
37+
38+
class Node {
39+
private:
40+
typedef enum {
41+
OP_ADD, OP_SUBTRACT, OP_INTERSECT, OP_TERMINAL
42+
} Op;
43+
typedef enum {
44+
PRIM_BOX, PRIM_CONE, PRIM_CYLINDER, PRIM_PYRAMID, PRIM_SPHERE
45+
} Prim;
46+
47+
double x,y,z, zx,zy,zz, xx,xy,xz, a,b,c;
48+
const Node *left, *right;
49+
50+
Op op;
51+
Prim prim;
52+
53+
Node& operate(Op op, const Node& p) {
54+
left = new Node(*this);
55+
right = new Node(p);
56+
this->op = op;
57+
return *this;
58+
}
59+
60+
Node(Prim p, double la, double lb=0., double lc=0.)
61+
: prim(p), op(OP_TERMINAL),
62+
x(0.), y(0.), z(0.),
63+
zx(0.), zy(0.), zz(1.),
64+
xx(1.), xy(0.), xz(0.),
65+
a(la), b(lb), c(lc) {}
66+
public:
67+
static Node Sphere(double r) {
68+
return Node(PRIM_SPHERE, r);
69+
}
70+
static Node Box(double dx, double dy, double dz) {
71+
return Node(PRIM_BOX, dx, dy, dz);
72+
}
73+
static Node Pyramid(double dx, double dy, double dz) {
74+
return Node(PRIM_PYRAMID, dx, dy, dz);
75+
}
76+
static Node Cylinder(double r, double h) {
77+
return Node(PRIM_CYLINDER, r, h);
78+
}
79+
static Node Cone(double r, double h) {
80+
return Node(PRIM_CONE, r, h);
81+
}
82+
83+
Node& move(
84+
double px = 0., double py = 0., double pz = 0.,
85+
double zx = 0., double zy = 0., double zz = 1.,
86+
double xx = 1., double xy = 0., double xz = 0.)
87+
{
88+
this->x = px; this->y = py; this->z = pz;
89+
this->zx = zx; this->zy = zy; this->zz = zz;
90+
this->xx = xx; this->xy = xy; this->xz = xz;
91+
return *this;
92+
}
93+
94+
Node& add(const Node& p) {
95+
return operate(OP_ADD, p);
96+
}
97+
Node& subtract(const Node& p) {
98+
return operate(OP_SUBTRACT, p);
99+
}
100+
Node& intersect(const Node& p) {
101+
return operate(OP_INTERSECT, p);
102+
}
103+
104+
IfcSchema::IfcRepresentationItem* serialize(IfcHierarchyHelper& file) const {
105+
IfcSchema::IfcRepresentationItem* my;
106+
if (op == OP_TERMINAL) {
107+
IfcSchema::IfcAxis2Placement3D* place = file.addPlacement3d(x,y,z,zx,zy,zz,xx,xy,xz);
108+
if (prim == PRIM_SPHERE) {
109+
my = new IfcSchema::IfcSphere(place, a);
110+
} else if (prim == PRIM_BOX) {
111+
my = new IfcSchema::IfcBlock(place, a, b, c);
112+
} else if (prim == PRIM_PYRAMID) {
113+
my = new IfcSchema::IfcRectangularPyramid(place, a, b, c);
114+
} else if (prim == PRIM_CYLINDER) {
115+
my = new IfcSchema::IfcRightCircularCylinder(place, b, a);
116+
} else if (prim == PRIM_CONE) {
117+
my = new IfcSchema::IfcRightCircularCone(place, b, a);
118+
}
119+
} else {
120+
IfcSchema::IfcBooleanOperator::IfcBooleanOperator o;
121+
if (op == OP_ADD) {
122+
o = IfcSchema::IfcBooleanOperator::IfcBooleanOperator_UNION;
123+
} else if (op == OP_SUBTRACT) {
124+
o = IfcSchema::IfcBooleanOperator::IfcBooleanOperator_DIFFERENCE;
125+
} else if (op == OP_INTERSECT) {
126+
o = IfcSchema::IfcBooleanOperator::IfcBooleanOperator_INTERSECTION;
127+
}
128+
my = new IfcSchema::IfcBooleanResult(o, left->serialize(file), right->serialize(file));
129+
}
130+
file.AddEntity(my);
131+
return my;
132+
}
133+
};
134+
135+
int main(int argc, char** argv) {
136+
const char filename[] = "IfcCsgPrimitive.ifc";
137+
IfcHierarchyHelper file;
138+
file.filename(filename);
139+
140+
IfcSchema::IfcRepresentationItem* csg1 = Node::Box(8000.,6000.,3000.).subtract(
141+
Node::Box(7600.,5600.,2800.).move(200.,200.,200.)
142+
).add(
143+
Node::Pyramid(8000.,6000.,3000.).move(0,0,3000.).add(
144+
Node::Cylinder(1000.,4000.).move(4000.,1000.,4000., 0.,1.,0.)
145+
).subtract(
146+
Node::Pyramid(7600.,5600.,2800.).move(200.,200.,3000.)
147+
).subtract(
148+
Node::Cylinder(900.,4000.).move(4000.,1000.,4000., 0.,1.,0.).intersect(
149+
Node::Box(2000.,4000.,1000.).move(3000.,1000.,4000.)
150+
)
151+
)
152+
).serialize(file);
153+
154+
const double x = 1000.; const double y = -4000.;
155+
156+
IfcSchema::IfcRepresentationItem* csg2 = Node::Sphere(5000.).move(x,y,-4500.).intersect(
157+
Node::Box(6000., 6000., 6000.).move(x-3000., y-3000., 0.)
158+
).add(
159+
Node::Cone(500., 3000.).move(x,y).add(
160+
Node::Cone(1500., 1000.).move(x,y, 900.).add(
161+
Node::Cone(1100., 1000.).move(x,y, 1800.).add(
162+
Node::Cone(750., 600.).move(x,y, 2700.)
163+
)))).serialize(file);
164+
165+
IfcSchema::IfcBuildingElementProxy* product = new IfcSchema::IfcBuildingElementProxy(
166+
guid(), 0, S("IfcCsgPrimitive"), null, null, 0, 0, null, null);
167+
168+
file.addBuildingProduct(product);
169+
170+
product->setOwnerHistory(file.getSingle<IfcSchema::IfcOwnerHistory>());
171+
172+
product->setObjectPlacement(file.addLocalPlacement());
173+
174+
IfcSchema::IfcRepresentation::list reps (new IfcTemplatedEntityList<IfcSchema::IfcRepresentation>());
175+
IfcSchema::IfcRepresentationItem::list items (new IfcTemplatedEntityList<IfcSchema::IfcRepresentationItem>());
176+
177+
items->push(csg1);
178+
items->push(csg2);
179+
IfcSchema::IfcShapeRepresentation* rep = new IfcSchema::IfcShapeRepresentation(
180+
file.getSingle<IfcSchema::IfcRepresentationContext>(), S("Body"), S("CSG"), items);
181+
reps->push(rep);
182+
183+
IfcSchema::IfcProductDefinitionShape* shape = new IfcSchema::IfcProductDefinitionShape(0, 0, reps);
184+
file.AddEntity(rep);
185+
file.AddEntity(shape);
186+
187+
product->setRepresentation(shape);
188+
189+
file.getSingle<IfcSchema::IfcProject>()->setName("IfcCompositeProfileDef");
190+
191+
std::ofstream f(filename);
192+
f << file;
193+
}

src/ifcgeom/IfcGeom.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ namespace IfcGeom {
104104
void SetValue(GeomValue var, double value);
105105
double GetValue(GeomValue var);
106106
std::string create_brep_data(Ifc2x3::IfcProduct* s);
107+
bool fill_nonmanifold_wires_with_planar_faces(TopoDS_Shape& shape);
107108
IfcSchema::IfcProductDefinitionShape* tesselate(TopoDS_Shape& shape, double deflection, IfcEntities es);
108109

109110

src/ifcgeom/IfcGeomFunctions.cpp

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
* *
2424
********************************************************************************/
2525

26+
#include <set>
2627
#include <cassert>
2728

2829
#include <gp_Pnt.hxx>
@@ -94,6 +95,11 @@
9495

9596
#include <BRepTools.hxx>
9697

98+
#include <TopExp.hxx>
99+
#include <TopTools_IndexedMapOfShape.hxx>
100+
#include <TopTools_IndexedDataMapOfShapeListOfShape.hxx>
101+
#include <TopTools_ListIteratorOfListOfShape.hxx>
102+
97103
#include "../ifcgeom/IfcGeom.h"
98104

99105
bool IfcGeom::create_solid_from_compound(const TopoDS_Shape& compound, TopoDS_Shape& shape) {
@@ -576,6 +582,102 @@ IfcSchema::IfcProductDefinitionShape* IfcGeom::tesselate(TopoDS_Shape& shape, do
576582
return shapedef;
577583
}
578584

585+
// Returns the vertex part of an TopoDS_Edge edge that is not TopoDS_Vertex vertex
586+
TopoDS_Vertex find_other(const TopoDS_Edge& edge, const TopoDS_Vertex& vertex) {
587+
TopExp_Explorer exp(edge, TopAbs_VERTEX);
588+
while (exp.More()) {
589+
if (!exp.Current().IsSame(vertex)) {
590+
return TopoDS::Vertex(exp.Current());
591+
}
592+
exp.Next();
593+
}
594+
}
595+
596+
TopoDS_Edge find_next(const TopTools_IndexedMapOfShape& edge_set, const TopTools_IndexedDataMapOfShapeListOfShape& vertex_to_edges, const TopoDS_Vertex& current, const TopoDS_Edge& previous_edge) {
597+
const TopTools_ListOfShape& edges = vertex_to_edges.FindFromKey(current);
598+
TopTools_ListIteratorOfListOfShape eit;
599+
for (eit.Initialize(edges); eit.More(); eit.Next()) {
600+
const TopoDS_Edge& edge = TopoDS::Edge(eit.Value());
601+
if (edge.IsSame(previous_edge)) continue;
602+
if (edge_set.Contains(edge)) {
603+
return edge;
604+
}
605+
}
606+
}
607+
608+
bool IfcGeom::fill_nonmanifold_wires_with_planar_faces(TopoDS_Shape& shape) {
609+
BRepOffsetAPI_Sewing sew;
610+
sew.Add(shape);
611+
612+
TopTools_IndexedDataMapOfShapeListOfShape edge_to_faces;
613+
TopTools_IndexedDataMapOfShapeListOfShape vertex_to_edges;
614+
std::set<int> visited;
615+
TopTools_IndexedMapOfShape edge_set;
616+
617+
TopExp::MapShapesAndAncestors (shape, TopAbs_EDGE, TopAbs_FACE, edge_to_faces);
618+
619+
const int num_edges = edge_to_faces.Extent();
620+
for (int i = 1; i <= num_edges; ++i) {
621+
const TopTools_ListOfShape& faces = edge_to_faces.FindFromIndex(i);
622+
const int count = faces.Extent();
623+
// Find only the non-manifold edges: Edges that are only part of a
624+
// single face and therefore part of the wire(s) we want to fill.
625+
if (count == 1) {
626+
const TopoDS_Shape& edge = edge_to_faces.FindKey(i);
627+
TopExp::MapShapesAndAncestors (edge, TopAbs_VERTEX, TopAbs_EDGE, vertex_to_edges);
628+
edge_set.Add(edge);
629+
}
630+
}
631+
632+
const int num_verts = vertex_to_edges.Extent();
633+
TopoDS_Vertex first, current;
634+
TopoDS_Edge previous_edge;
635+
636+
// Now loop over all the vertices that are part of the wire(s) to be filled
637+
for (int i = 1; i <= num_verts; ++i) {
638+
first = current = TopoDS::Vertex(vertex_to_edges.FindKey(i));
639+
const bool isSame = first.IsSame(current);
640+
// We keep track of the vertices we already used
641+
if (visited.find(vertex_to_edges.FindIndex(current)) != visited.end()) {
642+
continue;
643+
}
644+
// Given these vertices, try to find closed loops and create new
645+
// wires out of them.
646+
BRepBuilderAPI_MakeWire w;
647+
while (true) {
648+
visited.insert(vertex_to_edges.FindIndex(current));
649+
// Find the edge that the current vertex is part of and points
650+
// away from the previous vertex (null for the first vertex).
651+
TopoDS_Edge edge = find_next(edge_set, vertex_to_edges, current, previous_edge);
652+
if (edge.IsNull()) {
653+
return false;
654+
}
655+
TopoDS_Vertex other = find_other(edge, current);
656+
w.Add(edge);
657+
// See if the starting point of this loop has been reached. Note that
658+
// additional wires after this one potentially will be created.
659+
if (other.IsSame(first)) {
660+
break;
661+
}
662+
previous_edge = edge;
663+
current = other;
664+
}
665+
sew.Add(BRepBuilderAPI_MakeFace(w));
666+
previous_edge.Nullify();
667+
}
668+
669+
sew.Perform();
670+
shape = sew.SewedShape();
671+
672+
try {
673+
ShapeFix_Solid solid;
674+
solid.LimitTolerance(GetValue(GV_POINT_EQUALITY_TOLERANCE));
675+
shape = solid.SolidFromShell(TopoDS::Shell(shape));
676+
} catch(...) {}
677+
678+
return true;
679+
}
680+
579681
std::string IfcGeom::create_brep_data(Ifc2x3::IfcProduct* ifc_product) {
580682
if (!ifc_product->hasRepresentation()) return "";
581683

src/ifcgeom/IfcGeomObjects.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,8 +176,11 @@ IfcGeomObjects::IfcRepresentationTriangulation::IfcRepresentationTriangulation(c
176176
const gp_Pnt2d& uv = uvs(i);
177177
gp_Pnt p;
178178
gp_Vec normal_direction;
179-
prop.Normal(uv.X(),uv.Y(),p,normal_direction);
180-
gp_Dir normal = gp_Dir(normal_direction.XYZ() * rotation_matrix);
179+
prop.Normal(uv.X(),uv.Y(),p,normal_direction);
180+
gp_Vec normal(0., 0., 0.);
181+
if (normal_direction.Magnitude() > ALMOST_ZERO) {
182+
normal = gp_Dir(normal_direction.XYZ() * rotation_matrix);
183+
}
181184
_normals.push_back((float)normal.X());
182185
_normals.push_back((float)normal.Y());
183186
_normals.push_back((float)normal.Z());

0 commit comments

Comments
 (0)