mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-21 14:23:53 +00:00
This commit is contained in:
+171
-83
@@ -85,9 +85,161 @@
|
|||||||
|
|
||||||
#include <Geom_BSplineCurve.hxx>
|
#include <Geom_BSplineCurve.hxx>
|
||||||
#include <BRepTools_WireExplorer.hxx>
|
#include <BRepTools_WireExplorer.hxx>
|
||||||
|
#include <ShapeBuild_ReShape.hxx>
|
||||||
|
|
||||||
|
|
||||||
#include "../ifcgeom/IfcGeom.h"
|
#include "../ifcgeom/IfcGeom.h"
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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_Edge& e, const TopoDS_Vertex& v, const gp_Pnt& p) {
|
||||||
|
gp_Pnt p1 = p;
|
||||||
|
gp_Pnt p2 = BRep_Tool::Pnt(other(e, v));
|
||||||
|
|
||||||
|
if (e.Orientation() == TopAbs_REVERSED) {
|
||||||
|
std::swap(p1, p2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Already asserted this is a replacement for a linear edge
|
||||||
|
TopoDS_Edge new_edge = BRepBuilderAPI_MakeEdge(p1, p2).Edge();
|
||||||
|
new_edge.Orientation(e.Orientation());
|
||||||
|
|
||||||
|
ShapeBuild_ReShape reshape;
|
||||||
|
reshape.Replace(e, new_edge);
|
||||||
|
|
||||||
|
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 skip_next_;
|
||||||
|
const IfcUtil::IfcBaseClass* inst_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
wire_builder(double p, const IfcUtil::IfcBaseClass* inst = 0) : p_(p), skip_next_(false), inst_(inst) {}
|
||||||
|
|
||||||
|
void operator()(const TopoDS_Shape& a) {
|
||||||
|
if (skip_next_) {
|
||||||
|
// tfk: not ideal, adjusting both start and end points not supported now.
|
||||||
|
skip_next_ = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mw_.Add(TopoDS::Wire(a));
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(const TopoDS_Shape& a, const TopoDS_Shape& b, bool last) {
|
||||||
|
if (skip_next_) {
|
||||||
|
// tfk: not ideal, adjusting both start and end points not supported now.
|
||||||
|
skip_next_ = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TopoDS_Wire& w1 = TopoDS::Wire(a);
|
||||||
|
const TopoDS_Wire& w2 = TopoDS::Wire(b);
|
||||||
|
|
||||||
|
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);
|
||||||
|
TopTools_IndexedDataMapOfShapeListOfShape wmap1, wmap2;
|
||||||
|
|
||||||
|
// Distance is within 2p, this is fine
|
||||||
|
if (dist < 2. * 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_->entity);
|
||||||
|
goto check;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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, TopoDS::Edge(last_edges.First()), w12, p2));
|
||||||
|
Logger::Message(Logger::LOG_ERROR, "Adjusted edge end-point with distance " + boost::lexical_cast<std::string>(dist) + " on:", inst_->entity);
|
||||||
|
} else if (is_line2 && !last) {
|
||||||
|
// tfk: not ideal, begin point of first edge cannot be adjusted now for cyclic wires
|
||||||
|
mw_.Add(w1);
|
||||||
|
mw_.Add(adjust(w1, TopoDS::Edge(last_edges.First()), w12, p2));
|
||||||
|
skip_next_ = true;
|
||||||
|
Logger::Message(Logger::LOG_ERROR, "Adjusted edge end-point with distance " + boost::lexical_cast<std::string>(dist) + " on:", inst_->entity);
|
||||||
|
} 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_->entity);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Logger::Error("Internal error, inconsistent wire segments", inst_->entity);
|
||||||
|
mw_.Add(w1);
|
||||||
|
}
|
||||||
|
|
||||||
|
check:
|
||||||
|
if (mw_.Error() == BRepBuilderAPI_NonManifoldWire) {
|
||||||
|
Logger::Error("Non-manifold curve segments:", inst_->entity);
|
||||||
|
} else if (mw_.Error() == BRepBuilderAPI_DisconnectedWire) {
|
||||||
|
Logger::Error("Failed to join curve segments:", inst_->entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
bool IfcGeom::Kernel::convert(const IfcSchema::IfcCompositeCurve* l, TopoDS_Wire& wire) {
|
||||||
if ( getValue(GV_PLANEANGLE_UNIT)<0 ) {
|
if ( getValue(GV_PLANEANGLE_UNIT)<0 ) {
|
||||||
Logger::Message(Logger::LOG_WARNING,"Creating a composite curve without unit information:",l->entity);
|
Logger::Message(Logger::LOG_WARNING,"Creating a composite curve without unit information:",l->entity);
|
||||||
@@ -165,109 +317,45 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcCompositeCurve* l, TopoDS_Wire
|
|||||||
return use_radians || use_degrees;
|
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();
|
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();
|
IfcSchema::IfcCurve* curve = (*it)->ParentCurve();
|
||||||
TopoDS_Wire segment;
|
TopoDS_Wire segment;
|
||||||
|
|
||||||
if (!convert_wire(curve, segment)) {
|
if (!convert_wire(curve, segment)) {
|
||||||
Logger::Message(Logger::LOG_ERROR, "Failed to convert curve:", curve->entity);
|
Logger::Message(Logger::LOG_ERROR, "Failed to convert curve:", curve->entity);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(*it)->SameSense()) {
|
if (!(*it)->SameSense()) {
|
||||||
segment.Reverse();
|
segment.Reverse();
|
||||||
}
|
}
|
||||||
|
|
||||||
ShapeFix_ShapeTolerance FTol;
|
ShapeFix_ShapeTolerance FTol;
|
||||||
FTol.SetTolerance(segment, getValue(GV_PRECISION), TopAbs_WIRE);
|
FTol.SetTolerance(segment, getValue(GV_PRECISION), TopAbs_WIRE);
|
||||||
|
|
||||||
TopExp::Vertices(segment, edge_first_vertex, edge_last_vertex);
|
|
||||||
|
|
||||||
if (it == segments->begin()) {
|
converted_segments.Append(segment);
|
||||||
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);
|
|
||||||
|
|
||||||
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->entity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
w.Add(segment);
|
|
||||||
|
|
||||||
if ( w.Error() != BRepBuilderAPI_WireDone ) {
|
|
||||||
if (w.Error() == BRepBuilderAPI_NonManifoldWire) {
|
|
||||||
|
|
||||||
Logger::Message(Logger::LOG_ERROR, "Non-manifold curve segments:", l->entity);
|
|
||||||
|
|
||||||
} else if (w.Error() == BRepBuilderAPI_DisconnectedWire) {
|
|
||||||
|
|
||||||
Logger::Message(Logger::LOG_ERROR, "Failed to join curve segments:", l->entity);
|
|
||||||
|
|
||||||
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)->entity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
wire_last_vertex = edge_last_vertex;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BRepBuilderAPI_MakeWire w;
|
||||||
|
TopoDS_Vertex wire_first_vertex, wire_last_vertex, edge_first_vertex, edge_last_vertex;
|
||||||
|
|
||||||
|
const double precision_sq_2 = 2 * getValue(GV_PRECISION) * getValue(GV_PRECISION);
|
||||||
|
|
||||||
|
TopTools_ListIteratorOfListOfShape it(converted_segments);
|
||||||
|
|
||||||
IfcEntityList::ptr profile = l->entity->getInverse(IfcSchema::Type::IfcProfileDef, -1);
|
IfcEntityList::ptr profile = l->entity->getInverse(IfcSchema::Type::IfcProfileDef, -1);
|
||||||
|
const bool force_close = profile && profile->size() > 0;
|
||||||
|
|
||||||
if (profile && profile->size() > 0) {
|
wire_builder bld(getValue(GV_PRECISION), l);
|
||||||
gp_Pnt first = BRep_Tool::Pnt(edge_last_vertex);
|
shape_pair_enumerate(it, bld, force_close);
|
||||||
gp_Pnt last = BRep_Tool::Pnt(wire_first_vertex);
|
wire = bld.wire();
|
||||||
|
|
||||||
Standard_Real distance = first.SquareDistance(last);
|
|
||||||
if (distance > precision_sq_2) {
|
|
||||||
w.Add(BRepBuilderAPI_MakeEdge(edge_last_vertex, wire_first_vertex));
|
|
||||||
|
|
||||||
Logger::Message(Logger::LOG_ERROR, "Closed gap on:", l->entity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
wire = w.Wire();
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user