mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-12 06:32:09 +00:00
Merge branch 'master' into v0.6.0
# Conflicts: # src/ifcconvert/IfcConvert.cpp # src/ifcgeom/IfcGeomFilter.h # src/ifcgeom/IfcGeomIteratorImplementation.h # src/ifcgeom/IfcGeomWires.cpp # src/ifcparse/IfcFile.h # src/ifcparse/IfcLogger.cpp # src/ifcparse/IfcLogger.h # src/ifcparse/IfcParse.cpp # src/serializers/SvgSerializer.cpp # src/serializers/schema_dependent/XmlSerializer.cpp
This commit is contained in:
@@ -202,6 +202,8 @@ public:
|
||||
bool approximate_plane_through_wire(const TopoDS_Wire&, gp_Pln&);
|
||||
bool flatten_wire(TopoDS_Wire&);
|
||||
bool triangulate_wire(const TopoDS_Wire&, TopTools_ListOfShape&);
|
||||
bool wire_intersections(const TopoDS_Wire & wire, TopTools_ListOfShape & wires);
|
||||
void select_largest(const TopTools_ListOfShape& shapes, TopoDS_Shape& largest);
|
||||
|
||||
static double shape_volume(const TopoDS_Shape& s);
|
||||
static double face_area(const TopoDS_Face& f);
|
||||
|
||||
@@ -215,7 +215,15 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcFace* l, TopoDS_Shape& face) {
|
||||
process_wire:
|
||||
|
||||
if (face_surface.IsNull()) {
|
||||
mf = new BRepBuilderAPI_MakeFace(wire);
|
||||
if (count(wire, TopAbs_EDGE) > 128) {
|
||||
// tfk: optimization find the underlying surface ourselves since it's going
|
||||
// to be planar in IFC if no explicit surface is given. Should we always do this?
|
||||
gp_Pln pln;
|
||||
approximate_plane_through_wire(wire, pln);
|
||||
mf = new BRepBuilderAPI_MakeFace(pln, wire, true);
|
||||
} else {
|
||||
mf = new BRepBuilderAPI_MakeFace(wire);
|
||||
}
|
||||
} else {
|
||||
/// @todo check necessity of false here
|
||||
mf = new BRepBuilderAPI_MakeFace(face_surface, wire, false);
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
#include "IfcGeom.h"
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/function.hpp>
|
||||
#include <boost/regex.hpp>
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
@@ -90,7 +91,7 @@ namespace IfcGeom
|
||||
void populate(const std::set<std::string>& patterns)
|
||||
{
|
||||
values.clear();
|
||||
foreach(const std::string &pattern, patterns) {
|
||||
BOOST_FOREACH(const std::string &pattern, patterns) {
|
||||
values.insert(wildcard_string_to_regex(pattern));
|
||||
}
|
||||
}
|
||||
@@ -99,7 +100,7 @@ namespace IfcGeom
|
||||
|
||||
static bool match_values(const std::set<boost::regex>& values, const std::string &str)
|
||||
{
|
||||
foreach(const boost::regex& r, values) {
|
||||
BOOST_FOREACH(const boost::regex& r, values) {
|
||||
if (boost::regex_match(str, r)) {
|
||||
return true;
|
||||
}
|
||||
@@ -111,7 +112,7 @@ namespace IfcGeom
|
||||
{
|
||||
// Escape all non-"*?" regex special chars
|
||||
static const std::string special_chars = "\\^.$|()[]+/";
|
||||
foreach(char c, special_chars) {
|
||||
BOOST_FOREACH(char c, special_chars) {
|
||||
std::string char_str(1, c);
|
||||
boost::replace_all(str, char_str, "\\" + char_str);
|
||||
}
|
||||
@@ -184,7 +185,7 @@ namespace IfcGeom
|
||||
|
||||
ss << (traverse ? "traverse " : "") << (include ? "include" : "exclude");
|
||||
std::vector<std::string> patterns;
|
||||
foreach(const boost::regex& r, values) {
|
||||
BOOST_FOREACH(const boost::regex& r, values) {
|
||||
patterns.push_back("\"" + r.str() + "\"");
|
||||
}
|
||||
|
||||
@@ -247,7 +248,7 @@ namespace IfcGeom
|
||||
std::stringstream ss;
|
||||
ss << (traverse ? "traverse " : "") << (include ? "include" : "exclude") << " layers";
|
||||
std::vector<std::string> str_values;
|
||||
foreach(const boost::regex& r, values) {
|
||||
BOOST_FOREACH(const boost::regex& r, values) {
|
||||
str_values.push_back(" \"" + r.str() + "\"");
|
||||
}
|
||||
ss << boost::algorithm::join(str_values, " ");
|
||||
@@ -271,7 +272,7 @@ namespace IfcGeom
|
||||
// TODO
|
||||
#if 0
|
||||
values.clear();
|
||||
foreach(const std::string& type, types) {
|
||||
BOOST_FOREACH(const std::string& type, types) {
|
||||
const IfcParse::declaration* ty;
|
||||
try {
|
||||
ty = IfcSchema::FromString::Class()(boost::to_upper_copy(type));
|
||||
@@ -287,7 +288,7 @@ namespace IfcGeom
|
||||
bool match(IfcSchema::IfcProduct* prod) const
|
||||
{
|
||||
// The set is iterated over to able to filter on subtypes.
|
||||
foreach(const IfcParse::declaration* type, values) {
|
||||
BOOST_FOREACH(const IfcParse::declaration* type, values) {
|
||||
if (prod->declaration().is(*type)) {
|
||||
return true;
|
||||
}
|
||||
@@ -306,7 +307,7 @@ namespace IfcGeom
|
||||
#if 0
|
||||
std::stringstream ss;
|
||||
ss << (traverse ? "traverse " : "") << (include ? "include" : "exclude") << " entities";
|
||||
foreach(IfcSchema::Enum::Class() type, values) {
|
||||
BOOST_FOREACH(IfcSchema::Enum::Class() type, values) {
|
||||
ss << " " << IfcSchema::ToString::Class()(type);
|
||||
}
|
||||
description = ss.str();
|
||||
|
||||
@@ -95,6 +95,7 @@
|
||||
#include <ShapeFix_Solid.hxx>
|
||||
|
||||
#include <ShapeAnalysis_Curve.hxx>
|
||||
#include <ShapeAnalysis_Wire.hxx>
|
||||
#include <ShapeAnalysis_Surface.hxx>
|
||||
#include <ShapeAnalysis_ShapeTolerance.hxx>
|
||||
|
||||
@@ -105,6 +106,7 @@
|
||||
#include <GProp_GProps.hxx>
|
||||
#include <BRepGProp.hxx>
|
||||
|
||||
#include <BRepBuilderAPI_Copy.hxx>
|
||||
#include <BRepBuilderAPI_Transform.hxx>
|
||||
#include <BRepBuilderAPI_GTransform.hxx>
|
||||
|
||||
@@ -131,6 +133,8 @@
|
||||
|
||||
#include <BRepClass3d_SolidClassifier.hxx>
|
||||
|
||||
#include <GeomAPI_ExtremaCurveCurve.hxx>
|
||||
|
||||
#include <Standard_Version.hxx>
|
||||
|
||||
#include "../ifcparse/macros.h"
|
||||
@@ -249,8 +253,10 @@ bool IfcGeom::Kernel::create_solid_from_faces(const TopTools_ListOfShape& face_l
|
||||
}
|
||||
|
||||
if (valid_shell) {
|
||||
|
||||
TopoDS_Shape complete_shape;
|
||||
TopExp_Explorer exp(shape, TopAbs_SHELL);
|
||||
|
||||
for (; exp.More(); exp.Next()) {
|
||||
TopoDS_Shape result_shape = exp.Current();
|
||||
|
||||
@@ -295,12 +301,28 @@ bool IfcGeom::Kernel::create_solid_from_faces(const TopTools_ListOfShape& face_l
|
||||
B.MakeCompound(C);
|
||||
B.Add(C, complete_shape);
|
||||
complete_shape = C;
|
||||
Logger::Message(Logger::LOG_WARNING, "Multiple components in IfcConnectedFaceSet");
|
||||
Logger::Message(Logger::LOG_ERROR, "Multiple components in IfcConnectedFaceSet");
|
||||
}
|
||||
B.Add(complete_shape, result_shape);
|
||||
}
|
||||
}
|
||||
|
||||
TopExp_Explorer loose_faces(shape, TopAbs_FACE, TopAbs_SHELL);
|
||||
|
||||
for (; loose_faces.More(); loose_faces.Next()) {
|
||||
BRep_Builder B;
|
||||
if (complete_shape.ShapeType() != TopAbs_COMPOUND) {
|
||||
TopoDS_Compound C;
|
||||
B.MakeCompound(C);
|
||||
B.Add(C, complete_shape);
|
||||
complete_shape = C;
|
||||
Logger::Message(Logger::LOG_ERROR, "Loose faces in IfcConnectedFaceSet");
|
||||
}
|
||||
B.Add(complete_shape, loose_faces.Current());
|
||||
}
|
||||
|
||||
shape = complete_shape;
|
||||
|
||||
} else {
|
||||
Logger::Message(Logger::LOG_WARNING, "Failed to sew faceset");
|
||||
}
|
||||
@@ -626,9 +648,12 @@ bool IfcGeom::Kernel::convert_openings_fast(const IfcSchema::IfcProduct* entity,
|
||||
}
|
||||
|
||||
for ( unsigned int i = 0; i < opening_shapes.size(); ++ i ) {
|
||||
TopoDS_Shape opening_shape_solid;
|
||||
const TopoDS_Shape& opening_shape_unlocated = ensure_fit_for_subtraction(opening_shapes[i].Shape(), opening_shape_solid);
|
||||
|
||||
gp_GTrsf gtrsf = opening_shapes[i].Placement();
|
||||
gtrsf.PreMultiply(opening_trsf);
|
||||
TopoDS_Shape opening_shape = apply_transformation(opening_shapes[i].Shape(), gtrsf);
|
||||
TopoDS_Shape opening_shape = apply_transformation(opening_shape_unlocated, gtrsf);
|
||||
opening_shapelist.Append(opening_shape);
|
||||
}
|
||||
|
||||
@@ -657,7 +682,15 @@ bool IfcGeom::Kernel::convert_openings_fast(const IfcSchema::IfcProduct* entity,
|
||||
}
|
||||
#endif
|
||||
|
||||
bool IfcGeom::Kernel::convert_wire_to_face(const TopoDS_Wire& wire, TopoDS_Face& face) {
|
||||
bool IfcGeom::Kernel::convert_wire_to_face(const TopoDS_Wire& w, TopoDS_Face& face) {
|
||||
TopoDS_Wire wire = w;
|
||||
|
||||
TopTools_ListOfShape results;
|
||||
if (wire_intersections(wire, results)) {
|
||||
Logger::Error("Self-intersections with " + boost::lexical_cast<std::string>(results.Extent()) + " cycles detected");
|
||||
select_largest(results, wire);
|
||||
}
|
||||
|
||||
ShapeFix_ShapeTolerance FTol;
|
||||
FTol.SetTolerance(wire, getValue(GV_PRECISION), TopAbs_WIRE);
|
||||
|
||||
@@ -2429,7 +2462,11 @@ bool IfcGeom::Kernel::split_solid_by_shell(const TopoDS_Shape& input, const Topo
|
||||
}
|
||||
apply_tolerance(solid, getValue(GV_PRECISION));
|
||||
|
||||
#if OCC_VERSION_HEX >= 0x70300
|
||||
TopTools_ListOfShape shapes;
|
||||
#else
|
||||
BOPCol_ListOfShape shapes;
|
||||
#endif
|
||||
shapes.Append(input);
|
||||
shapes.Append(solid);
|
||||
BOPAlgo_PaveFiller filler(new NCollection_IncAllocator); // TODO: Does this need to be freed?
|
||||
@@ -2774,6 +2811,226 @@ TopoDS_Shape IfcGeom::Kernel::apply_transformation(const TopoDS_Shape& s, const
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
/*
|
||||
* A small helper utility to wrap around a numeric range
|
||||
*/
|
||||
class bounded_int {
|
||||
private:
|
||||
int i;
|
||||
size_t n;
|
||||
public:
|
||||
bounded_int(int i, size_t n) : i(i), n(n) {}
|
||||
|
||||
bounded_int& operator--() {
|
||||
--i;
|
||||
if (i == -1) {
|
||||
i = n - 1;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
bounded_int& operator++() {
|
||||
++i;
|
||||
if (i == (int) n) {
|
||||
i = 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
operator int() { return i; }
|
||||
};
|
||||
|
||||
std::string format_pnt(const gp_Pnt& p) {
|
||||
std::stringstream ss;
|
||||
ss << std::fixed << std::setprecision(4) << p.X() << " " << p.Y() << " " << p.Z();
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string format_edge(const TopoDS_Edge& e) {
|
||||
std::stringstream ss;
|
||||
TopoDS_Vertex v1, v2;
|
||||
TopExp::Vertices(e, v1, v2);
|
||||
gp_Pnt p1 = BRep_Tool::Pnt(v1);
|
||||
gp_Pnt p2 = BRep_Tool::Pnt(v2);
|
||||
ss << "edge " << format_pnt(p1) << " -> " << format_pnt(p2);
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool IfcGeom::Kernel::wire_intersections(const TopoDS_Wire& wire, TopTools_ListOfShape& wires) {
|
||||
if (!wire.Closed()) {
|
||||
wires.Append(wire);
|
||||
return false;
|
||||
}
|
||||
|
||||
int n = count(wire, TopAbs_EDGE);
|
||||
if (n < 3 || n > 128) {
|
||||
if (n > 128) {
|
||||
Logger::Notice("Too many segments for detection of self-intersections");
|
||||
}
|
||||
wires.Append(wire);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Note: initialize empty
|
||||
Handle(ShapeExtend_WireData) wd = new ShapeExtend_WireData();
|
||||
|
||||
// ... to be sure to get consecutive edges
|
||||
BRepTools_WireExplorer exp(wire);
|
||||
for (; exp.More(); exp.Next()) {
|
||||
wd->Add(exp.Current());
|
||||
}
|
||||
|
||||
bool intersected = false;
|
||||
|
||||
// tfk: Extrema on infinite curves proved to be more robust.
|
||||
// TopoDS_Face face = BRepBuilderAPI_MakeFace(wire, true).Face();
|
||||
// ShapeAnalysis_Wire saw(wd, face, getValue(GV_PRECISION));
|
||||
|
||||
for (int i = 2; i < n; ++i) {
|
||||
for (int j = 0; j < i - 1; ++j) {
|
||||
if (i == n - 1 && j == 0) continue;
|
||||
|
||||
bool unbounded_intersects;
|
||||
const double eps = getValue(GV_PRECISION) * 2.;
|
||||
|
||||
double u11, u12, u21, u22, U1, U2;
|
||||
GeomAPI_ExtremaCurveCurve ecc(
|
||||
BRep_Tool::Curve(wd->Edge(i + 1), u11, u12),
|
||||
BRep_Tool::Curve(wd->Edge(j + 1), u21, u22)
|
||||
);
|
||||
|
||||
if ((unbounded_intersects = (ecc.NbExtrema() == 1 && ecc.Distance(1) < eps))) {
|
||||
ecc.Parameters(1, U1, U2);
|
||||
}
|
||||
|
||||
if (u11 > u12) {
|
||||
std::swap(u11, u12);
|
||||
}
|
||||
if (u21 > u22) {
|
||||
std::swap(u21, u22);
|
||||
}
|
||||
|
||||
/// @todo: tfk: probably need different thresholds on non-linear curves
|
||||
u11 -= eps;
|
||||
u12 += eps;
|
||||
u21 -= eps;
|
||||
u22 += eps;
|
||||
|
||||
// tfk: code below is for ShapeAnalysis_Wire::CheckIntersectingEdges()
|
||||
// IntRes2d_SequenceOfIntersectionPoint points2d;
|
||||
// TColgp_SequenceOfPnt points3d;
|
||||
// TColStd_SequenceOfReal errors;
|
||||
// if (saw.CheckIntersectingEdges(i + 1, j + 1, points2d, points3d, errors)) {
|
||||
|
||||
if (unbounded_intersects && u11 < U1 && U1 < u12 && u21 < U2 && U2 < u22) {
|
||||
|
||||
intersected = true;
|
||||
|
||||
// Explore a forward and backward cycle from the intersection point
|
||||
for (int fb = 0; fb <= 1; ++fb) {
|
||||
const bool forward = fb == 0;
|
||||
|
||||
BRepBuilderAPI_MakeWire mw;
|
||||
bool first = true;
|
||||
|
||||
for (bounded_int k(j, n);;) {
|
||||
bool intersecting = k == j || k == i;
|
||||
if (intersecting) {
|
||||
TopoDS_Edge e = wd->Edge(k + 1);
|
||||
|
||||
TopoDS_Vertex v1, v2;
|
||||
TopExp::Vertices(e, v1, v2);
|
||||
const TopoDS_Vertex* v = first == forward ? &v2 : &v1;
|
||||
|
||||
// gp_Pnt p2 = points3d.Value(1);
|
||||
|
||||
gp_Pnt p1 = BRep_Tool::Pnt(*v);
|
||||
gp_Pnt pp1, pp2;
|
||||
ecc.Points(1, pp1, pp2);
|
||||
const gp_Pnt& p2 = k == i ? pp1 : pp2;
|
||||
|
||||
// Substitute with a new edge from/to the intersection point
|
||||
if (p1.Distance(p2) > getValue(GV_PRECISION) * 2) {
|
||||
double _, __;
|
||||
Handle_Geom_Curve crv = BRep_Tool::Curve(e, _, __);
|
||||
BRepBuilderAPI_MakeEdge me(crv, p1, p2);
|
||||
TopoDS_Edge ed = me.Edge();
|
||||
mw.Add(ed);
|
||||
}
|
||||
|
||||
first = false;
|
||||
} else {
|
||||
// Re-use original edge
|
||||
mw.Add(wd->Edge(k+1));
|
||||
}
|
||||
|
||||
if (k == i) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (forward) {
|
||||
++k;
|
||||
} else {
|
||||
--k;
|
||||
}
|
||||
}
|
||||
|
||||
// Recursively process both cuts
|
||||
wire_intersections(mw.Wire(), wires);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No intersections found, append original wire
|
||||
if (!intersected) {
|
||||
wires.Append(wire);
|
||||
}
|
||||
|
||||
return intersected;
|
||||
}
|
||||
|
||||
void IfcGeom::Kernel::select_largest(const TopTools_ListOfShape& shapes, TopoDS_Shape& largest) {
|
||||
double mass = 0.;
|
||||
TopTools_ListIteratorOfListOfShape it(shapes);
|
||||
for (; it.More(); it.Next()) {
|
||||
/*
|
||||
// tfk: bounding box is more efficient probably
|
||||
const TopoDS_Wire& w = TopoDS::Wire(it.Value());
|
||||
TopoDS_Face face = BRepBuilderAPI_MakeFace(w).Face();
|
||||
const double m = face_area(face);
|
||||
*/
|
||||
|
||||
Bnd_Box bb;
|
||||
BRepBndLib::AddClose(it.Value(), bb);
|
||||
double xyz_min[3], xyz_max[3];
|
||||
bb.Get(xyz_min[0], xyz_min[1], xyz_min[2], xyz_max[0], xyz_max[1], xyz_max[2]);
|
||||
const double eps = getValue(GV_PRECISION);
|
||||
|
||||
double m = 1.;
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
if (Precision::IsNegativeInfinite(xyz_min[i])) {
|
||||
xyz_min[i] = 0.;
|
||||
}
|
||||
if (Precision::IsInfinite(xyz_max[i])) {
|
||||
xyz_max[i] = 0.;
|
||||
}
|
||||
m *= (xyz_max[i] + eps) - (xyz_min[i] - eps);
|
||||
}
|
||||
|
||||
if (m > mass) {
|
||||
mass = m;
|
||||
largest = it.Value();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if OCC_VERSION_HEX < 0x60900
|
||||
bool IfcGeom::Kernel::boolean_operation(const TopoDS_Shape& a, const TopTools_ListOfShape& b, BOPAlgo_Operation op, TopoDS_Shape& result) {
|
||||
result = a;
|
||||
@@ -2832,6 +3089,47 @@ bool IfcGeom::Kernel::boolean_operation(const TopoDS_Shape& a, const TopoDS_Shap
|
||||
return succesful;
|
||||
}
|
||||
#else
|
||||
|
||||
namespace {
|
||||
TopTools_ListOfShape copy_operand(const TopTools_ListOfShape& l) {
|
||||
#if OCC_VERSION_HEX < 0x70000
|
||||
TopTools_ListOfShape r;
|
||||
TopTools_ListIteratorOfListOfShape it(l);
|
||||
for (; it.More(); it.Next()) {
|
||||
r.Append(BRepBuilderAPI_Copy(it.Value()));
|
||||
}
|
||||
return r;
|
||||
#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.
|
||||
return 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<double>::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;
|
||||
}
|
||||
}
|
||||
|
||||
bool IfcGeom::Kernel::boolean_operation(const TopoDS_Shape& a, const TopTools_ListOfShape& b, BOPAlgo_Operation op, TopoDS_Shape& result, double fuzziness) {
|
||||
bool success = false;
|
||||
BRepAlgoAPI_BooleanOperation* builder;
|
||||
@@ -2847,17 +3145,27 @@ bool IfcGeom::Kernel::boolean_operation(const TopoDS_Shape& a, const TopTools_Li
|
||||
if (fuzziness < 0.) {
|
||||
fuzziness = getValue(GV_PRECISION);
|
||||
}
|
||||
|
||||
const double min_edge_len = min_edge_length(a);
|
||||
const double fuzz = (std::min)(min_edge_len / 3., fuzziness);
|
||||
|
||||
TopTools_ListOfShape s1s;
|
||||
s1s.Append(a);
|
||||
builder->SetFuzzyValue(fuzziness);
|
||||
s1s.Append(copy_operand(a));
|
||||
#if OCC_VERSION_HEX >= 0x70000
|
||||
builder->SetNonDestructive(true);
|
||||
#endif
|
||||
builder->SetFuzzyValue(fuzz);
|
||||
builder->SetArguments(s1s);
|
||||
builder->SetTools(b);
|
||||
builder->SetTools(copy_operand(b));
|
||||
builder->Build();
|
||||
if (builder->IsDone()) {
|
||||
TopoDS_Shape r = *builder;
|
||||
|
||||
ShapeFix_Shape fix(r);
|
||||
try {
|
||||
fix.SetMinTolerance(fuzz);
|
||||
fix.SetMaxTolerance(fuzz);
|
||||
fix.SetPrecision(fuzz);
|
||||
fix.Perform();
|
||||
r = fix.Shape();
|
||||
} catch (...) {
|
||||
@@ -2873,7 +3181,7 @@ bool IfcGeom::Kernel::boolean_operation(const TopoDS_Shape& a, const TopTools_Li
|
||||
delete builder;
|
||||
if (!success) {
|
||||
const double new_fuzziness = fuzziness * 10.;
|
||||
if (new_fuzziness + 1e-15 <= getValue(GV_PRECISION) * 1000.) {
|
||||
if (new_fuzziness + 1e-15 <= getValue(GV_PRECISION) * 1000. && new_fuzziness < min_edge_len) {
|
||||
return boolean_operation(a, b, op, result, new_fuzziness);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,6 +119,9 @@ namespace IfcGeom {
|
||||
IfcSchema::IfcProduct::list::ptr ifcproducts;
|
||||
IfcSchema::IfcProduct::list::it ifcproduct_iterator;
|
||||
|
||||
|
||||
IfcSchema::IfcRepresentation::list::ptr ok_mapped_representations;
|
||||
|
||||
int done;
|
||||
int total;
|
||||
|
||||
@@ -192,6 +195,7 @@ namespace IfcGeom {
|
||||
bool any_precision_encountered = false;
|
||||
|
||||
representations = IfcSchema::IfcRepresentation::list::ptr(new IfcSchema::IfcRepresentation::list);
|
||||
ok_mapped_representations = IfcSchema::IfcRepresentation::list::ptr(new IfcSchema::IfcRepresentation::list);
|
||||
|
||||
IfcSchema::IfcGeometricRepresentationContext::list::it it;
|
||||
IfcSchema::IfcGeometricRepresentationSubContext::list::it jt;
|
||||
@@ -389,6 +393,7 @@ namespace IfcGeom {
|
||||
|
||||
// Note that this can be a nullptr (!), but the fact that set size should be one still holds
|
||||
associated_single_materials.insert(kernel.get_single_material_association(product));
|
||||
if (associated_single_materials.size() > 1) return false;
|
||||
}
|
||||
|
||||
return associated_single_materials.size() == 1;
|
||||
@@ -412,12 +417,20 @@ namespace IfcGeom {
|
||||
|
||||
geometry_reuse_ok_for_current_representation_ = reuse_ok_(unfiltered_products);
|
||||
|
||||
if (!geometry_reuse_ok_for_current_representation_ && representation->RepresentationMap()->size() == 1) {
|
||||
IfcSchema::IfcRepresentationMap::list::ptr maps = representation->RepresentationMap();
|
||||
|
||||
if (!geometry_reuse_ok_for_current_representation_ && maps->size() == 1) {
|
||||
// unfiltered_products contains products represented by this representation by means of mapped items.
|
||||
// For example because of openings applied to products, reuse might not be acceptable and then the
|
||||
// products will be processed by means of their immediate representation and not the mapped representation.
|
||||
_nextShape();
|
||||
continue;
|
||||
|
||||
// IfcRepresentationMaps are also used for IfcTypeProducts, so an additional check is performed whether the map
|
||||
// is indeed used by IfcMappedItems.
|
||||
IfcSchema::IfcRepresentationMap* map = *maps->begin();
|
||||
if (map->MapUsage()->size() > 0) {
|
||||
_nextShape();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
bool representation_processed_as_mapped_item = false;
|
||||
@@ -425,10 +438,12 @@ namespace IfcGeom {
|
||||
IfcSchema::IfcRepresentation* representation_mapped_to = kernel.representation_mapped_to(representation);
|
||||
if (representation_mapped_to) {
|
||||
// Check if this represenation has (or will be) processed as part its mapped representation
|
||||
representation_processed_as_mapped_item = reuse_ok_(kernel.products_represented_by(representation_mapped_to));
|
||||
representation_processed_as_mapped_item = ok_mapped_representations->contains(representation_mapped_to) ||
|
||||
reuse_ok_(kernel.products_represented_by(representation_mapped_to));
|
||||
}
|
||||
|
||||
if (representation_processed_as_mapped_item) {
|
||||
ok_mapped_representations->push(representation_mapped_to);
|
||||
_nextShape();
|
||||
continue;
|
||||
}
|
||||
@@ -688,7 +703,12 @@ namespace IfcGeom {
|
||||
kernel.setValue(IfcGeom::Kernel::GV_MAX_FACES_TO_SEW, settings.get(IteratorSettings::SEW_SHELLS) ? 1000 : -1);
|
||||
kernel.setValue(IfcGeom::Kernel::GV_DIMENSIONALITY, (settings.get(IteratorSettings::INCLUDE_CURVES)
|
||||
? (settings.get(IteratorSettings::EXCLUDE_SOLIDS_AND_SURFACES) ? -1. : 0.) : +1.));
|
||||
if (settings.get(IteratorSettings::SITE_LOCAL_PLACEMENT)) {
|
||||
if (settings.get(IteratorSettings::BUILDING_LOCAL_PLACEMENT)) {
|
||||
if (settings.get(IteratorSettings::SITE_LOCAL_PLACEMENT)) {
|
||||
Logger::Message(Logger::LOG_WARNING, "building-local-placement takes precedence over site-local-placement");
|
||||
}
|
||||
kernel.set_conversion_placement_rel_to(&IfcSchema::IfcBuilding::Class());
|
||||
} else if (settings.get(IteratorSettings::SITE_LOCAL_PLACEMENT)) {
|
||||
kernel.set_conversion_placement_rel_to(&IfcSchema::IfcSite::Class());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,8 +82,10 @@ namespace IfcGeom
|
||||
SEARCH_FLOOR = 1 << 14,
|
||||
///
|
||||
SITE_LOCAL_PLACEMENT = 1 << 15,
|
||||
/// Number of different setting flags.
|
||||
NUM_SETTINGS = 15
|
||||
///
|
||||
BUILDING_LOCAL_PLACEMENT = 1 << 16,
|
||||
/// Number of different setting flags.
|
||||
NUM_SETTINGS = 16
|
||||
};
|
||||
/// Used to store logical OR combination of setting flags.
|
||||
typedef unsigned SettingField;
|
||||
|
||||
@@ -551,7 +551,12 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcBooleanResult* l, TopoDS_Shape
|
||||
return false;
|
||||
}
|
||||
|
||||
#if OCC_VERSION_HEX < 0x60900
|
||||
bool valid_result = boolean_operation(s1, s2, occ_op, shape);
|
||||
#else
|
||||
const double fuzz = is_halfspace ? getValue(GV_PRECISION) * 10. : -1.;
|
||||
bool valid_result = boolean_operation(s1, s2, occ_op, shape, fuzz);
|
||||
#endif
|
||||
|
||||
if (op == IfcSchema::IfcBooleanOperator::IfcBooleanOperator_DIFFERENCE) {
|
||||
// In case of a subtraction, a check on volume is performed.
|
||||
|
||||
+198
-81
@@ -85,11 +85,169 @@
|
||||
|
||||
#include <Geom_BSplineCurve.hxx>
|
||||
#include <BRepTools_WireExplorer.hxx>
|
||||
#include <ShapeBuild_ReShape.hxx>
|
||||
#include <TopTools_ListOfShape.hxx>
|
||||
#include <TopTools_ListIteratorOfListOfShape.hxx>
|
||||
|
||||
#include "../ifcgeom/IfcGeom.h"
|
||||
|
||||
#define Kernel MAKE_TYPE_NAME(Kernel)
|
||||
|
||||
namespace {
|
||||
// Returns the other vertex of an edge
|
||||
TopoDS_Vertex other(const TopoDS_Edge& e, const TopoDS_Vertex& v) {
|
||||
TopoDS_Vertex a, b;
|
||||
TopExp::Vertices(e, a, b);
|
||||
return v.IsSame(b) ? a : b;
|
||||
}
|
||||
|
||||
TopoDS_Edge first_edge(const TopoDS_Wire& w) {
|
||||
TopoDS_Vertex v1, v2;
|
||||
TopExp::Vertices(w, v1, v2);
|
||||
TopTools_IndexedDataMapOfShapeListOfShape wm;
|
||||
TopExp::MapShapesAndAncestors(w, TopAbs_VERTEX, TopAbs_EDGE, wm);
|
||||
return TopoDS::Edge(wm.FindFromKey(v1).First());
|
||||
}
|
||||
|
||||
// Returns new wire with the edge replaced by a linear edge with the vertex v moved to p
|
||||
TopoDS_Wire adjust(const TopoDS_Wire& w, const TopoDS_Vertex& v, const gp_Pnt& p) {
|
||||
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));
|
||||
}
|
||||
|
||||
// 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 2p, 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::Message(Logger::LOG_ERROR, "Added additional segment to close gap with length " + boost::lexical_cast<std::string>(dist) + " to:", inst_);
|
||||
goto check;
|
||||
}
|
||||
|
||||
{
|
||||
TopTools_IndexedDataMapOfShapeListOfShape wmap1, wmap2;
|
||||
|
||||
// Find edges connected to end- and begin vertex
|
||||
TopExp::MapShapesAndAncestors(w1, TopAbs_VERTEX, TopAbs_EDGE, wmap1);
|
||||
TopExp::MapShapesAndAncestors(w2, TopAbs_VERTEX, TopAbs_EDGE, wmap2);
|
||||
|
||||
const TopTools_ListOfShape& last_edges = wmap1.FindFromKey(w12);
|
||||
const TopTools_ListOfShape& first_edges = wmap2.FindFromKey(w21);
|
||||
|
||||
double _, __;
|
||||
if (last_edges.Extent() == 1 && first_edges.Extent() == 1) {
|
||||
Handle(Geom_Curve) c1 = BRep_Tool::Curve(TopoDS::Edge(last_edges.First()), _, __);
|
||||
Handle(Geom_Curve) c2 = BRep_Tool::Curve(TopoDS::Edge(first_edges.First()), _, __);
|
||||
|
||||
const bool is_line1 = c1->DynamicType() == STANDARD_TYPE(Geom_Line);
|
||||
const bool is_line2 = c2->DynamicType() == STANDARD_TYPE(Geom_Line);
|
||||
|
||||
// Adjust the segment that is linear
|
||||
if (is_line1) {
|
||||
mw_.Add(adjust(w1, w12, p2));
|
||||
Logger::Message(Logger::LOG_ERROR, "Adjusted edge end-point with distance " + boost::lexical_cast<std::string>(dist) + " on:", inst_);
|
||||
} else if (is_line2 && !last) {
|
||||
mw_.Add(w1);
|
||||
override_next_ = true;
|
||||
next_override_ = p1;
|
||||
Logger::Message(Logger::LOG_ERROR, "Adjusted edge end-point with distance " + boost::lexical_cast<std::string>(dist) + " on:", inst_);
|
||||
} else {
|
||||
// If both aren't linear an edge is added
|
||||
mw_.Add(w1);
|
||||
mw_.Add(BRepBuilderAPI_MakeEdge(p1, p2));
|
||||
Logger::Message(Logger::LOG_ERROR, "Added additional segment to close gap with length " + boost::lexical_cast<std::string>(dist) + " to:", inst_);
|
||||
}
|
||||
} else {
|
||||
Logger::Error("Internal error, inconsistent wire segments", inst_);
|
||||
mw_.Add(w1);
|
||||
}
|
||||
}
|
||||
|
||||
check:
|
||||
if (mw_.Error() == BRepBuilderAPI_NonManifoldWire) {
|
||||
Logger::Error("Non-manifold curve segments:", inst_);
|
||||
} else if (mw_.Error() == BRepBuilderAPI_DisconnectedWire) {
|
||||
Logger::Error("Failed to join curve segments:", inst_);
|
||||
}
|
||||
}
|
||||
|
||||
const TopoDS_Wire& wire() { return mw_.Wire(); }
|
||||
};
|
||||
|
||||
template <typename Fn>
|
||||
void shape_pair_enumerate(TopTools_ListIteratorOfListOfShape& it, Fn& fn, bool closed) {
|
||||
bool is_first = true;
|
||||
TopoDS_Shape first, previous, current;
|
||||
for (; it.More(); it.Next(), is_first = false) {
|
||||
current = it.Value();
|
||||
if (is_first) {
|
||||
first = current;
|
||||
} else {
|
||||
fn(previous, current, false);
|
||||
}
|
||||
previous = current;
|
||||
}
|
||||
if (closed) {
|
||||
fn(current, first, true);
|
||||
} else {
|
||||
fn(current);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool IfcGeom::Kernel::convert(const IfcSchema::IfcCompositeCurve* l, TopoDS_Wire& wire) {
|
||||
if ( getValue(GV_PLANEANGLE_UNIT)<0 ) {
|
||||
Logger::Message(Logger::LOG_WARNING,"Creating a composite curve without unit information:",l);
|
||||
@@ -167,105 +325,45 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcCompositeCurve* l, TopoDS_Wire
|
||||
return use_radians || use_degrees;
|
||||
}
|
||||
|
||||
BRepBuilderAPI_MakeWire w;
|
||||
TopoDS_Vertex wire_first_vertex, wire_last_vertex, edge_first_vertex, edge_last_vertex;
|
||||
|
||||
IfcSchema::IfcCompositeCurveSegment::list::ptr segments = l->Segments();
|
||||
|
||||
const double precision_sq_2 = 2 * getValue(GV_PRECISION) * getValue(GV_PRECISION);
|
||||
TopTools_ListOfShape converted_segments;
|
||||
|
||||
for(IfcSchema::IfcCompositeCurveSegment::list::it it = segments->begin(); it != segments->end(); ++it) {
|
||||
|
||||
for (IfcSchema::IfcCompositeCurveSegment::list::it it = segments->begin(); it != segments->end(); ++it) {
|
||||
|
||||
IfcSchema::IfcCurve* curve = (*it)->ParentCurve();
|
||||
TopoDS_Wire segment;
|
||||
|
||||
|
||||
if (!convert_wire(curve, segment)) {
|
||||
Logger::Message(Logger::LOG_ERROR, "Failed to convert curve:", curve);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (!(*it)->SameSense()) {
|
||||
segment.Reverse();
|
||||
}
|
||||
|
||||
|
||||
ShapeFix_ShapeTolerance FTol;
|
||||
FTol.SetTolerance(segment, getValue(GV_PRECISION), TopAbs_WIRE);
|
||||
|
||||
TopExp::Vertices(segment, edge_first_vertex, edge_last_vertex);
|
||||
|
||||
if (it == segments->begin()) {
|
||||
wire_first_vertex = edge_first_vertex;
|
||||
} else {
|
||||
gp_Pnt first = BRep_Tool::Pnt(edge_first_vertex);
|
||||
gp_Pnt last = BRep_Tool::Pnt(wire_last_vertex);
|
||||
converted_segments.Append(segment);
|
||||
|
||||
Standard_Real distance = first.SquareDistance(last);
|
||||
if (distance > precision_sq_2) {
|
||||
w.Add(BRepBuilderAPI_MakeEdge(wire_last_vertex, edge_first_vertex));
|
||||
|
||||
Logger::Message(Logger::LOG_ERROR, "Closed gap on:", l);
|
||||
}
|
||||
}
|
||||
|
||||
w.Add(segment);
|
||||
|
||||
if ( w.Error() != BRepBuilderAPI_WireDone ) {
|
||||
if (w.Error() == BRepBuilderAPI_NonManifoldWire) {
|
||||
|
||||
Logger::Message(Logger::LOG_ERROR, "Non-manifold curve segments:", l);
|
||||
|
||||
} else if (w.Error() == BRepBuilderAPI_DisconnectedWire) {
|
||||
|
||||
Logger::Message(Logger::LOG_ERROR, "Failed to join curve segments:", l);
|
||||
|
||||
gp_Pnt p1, p2;
|
||||
int precision = 4;
|
||||
double d = 0.;
|
||||
|
||||
if (!wire_last_vertex.IsNull()) {
|
||||
p1 = BRep_Tool::Pnt(wire_last_vertex);
|
||||
}
|
||||
if (!edge_first_vertex.IsNull()) {
|
||||
p2 = BRep_Tool::Pnt(edge_first_vertex);
|
||||
}
|
||||
if (!wire_last_vertex.IsNull() && !edge_first_vertex.IsNull()) {
|
||||
d = p1.Distance(p2);
|
||||
precision = ceil(-log10(d)) + 3;
|
||||
}
|
||||
|
||||
if (!wire_last_vertex.IsNull()) {
|
||||
std::stringstream ss;
|
||||
ss << std::setprecision(precision) << "Last vertex at (" << p1.X() << " " << p1.Y() << " " << p1.Z() << ")";
|
||||
Logger::Message(Logger::LOG_NOTICE, ss.str());
|
||||
}
|
||||
|
||||
if (!edge_first_vertex.IsNull()) {
|
||||
std::stringstream ss;
|
||||
ss << std::setprecision(precision) << "Segment starts at (" << p2.X() << " " << p2.Y() << " " << p2.Z() << ")";
|
||||
if (d > 0.) {
|
||||
ss << ", distance " << d << " > precision " << std::fixed << getValue(GV_PRECISION) / 10.;
|
||||
}
|
||||
ss << " for:";
|
||||
Logger::Message(Logger::LOG_NOTICE, ss.str(), (*it));
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
wire_last_vertex = edge_last_vertex;
|
||||
}
|
||||
|
||||
gp_Pnt first = BRep_Tool::Pnt(edge_last_vertex);
|
||||
gp_Pnt last = BRep_Tool::Pnt(wire_first_vertex);
|
||||
BRepBuilderAPI_MakeWire w;
|
||||
TopoDS_Vertex wire_first_vertex, wire_last_vertex, edge_first_vertex, edge_last_vertex;
|
||||
|
||||
Standard_Real distance = first.SquareDistance(last);
|
||||
if (distance > precision_sq_2) {
|
||||
w.Add(BRepBuilderAPI_MakeEdge(edge_last_vertex, wire_first_vertex));
|
||||
const double precision_sq_2 = 2 * getValue(GV_PRECISION) * getValue(GV_PRECISION);
|
||||
|
||||
Logger::Message(Logger::LOG_ERROR, "Closed gap on:", l);
|
||||
}
|
||||
TopTools_ListIteratorOfListOfShape it(converted_segments);
|
||||
|
||||
wire = w.Wire();
|
||||
IfcEntityList::ptr profile = l->data().getInverse(&IfcSchema::IfcProfileDef::Class(), -1);
|
||||
const bool force_close = profile && profile->size() > 0;
|
||||
|
||||
wire_builder bld(getValue(GV_PRECISION), l);
|
||||
shape_pair_enumerate(it, bld, force_close);
|
||||
wire = bld.wire();
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -395,14 +493,25 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcPolyline* l, TopoDS_Wire& resu
|
||||
polygon.Append(pnt);
|
||||
}
|
||||
|
||||
const double eps = getValue(GV_PRECISION) * 10;
|
||||
const bool closed_by_proximity = polygon.Length() >= 2 && 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, false);
|
||||
remove_duplicate_points_from_loop(polygon, closed_by_proximity, eps);
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -426,7 +535,8 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcPolyLoop* l, TopoDS_Wire& resu
|
||||
}
|
||||
|
||||
// Remove points that are too close to one another
|
||||
remove_duplicate_points_from_loop(polygon, true);
|
||||
const double eps = getValue(GV_PRECISION) * 10;
|
||||
remove_duplicate_points_from_loop(polygon, true, eps);
|
||||
|
||||
int count = polygon.Length();
|
||||
if (original_count - count != 0) {
|
||||
@@ -445,7 +555,14 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcPolyLoop* l, TopoDS_Wire& resu
|
||||
}
|
||||
w.Close();
|
||||
|
||||
result = w.Wire();
|
||||
result = w.Wire();
|
||||
|
||||
TopTools_ListOfShape results;
|
||||
if (wire_intersections(result, results)) {
|
||||
Logger::Error("Self-intersections with " + boost::lexical_cast<std::string>(results.Extent()) + " cycles detected", l);
|
||||
select_largest(results, result);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user