mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-16 10:34:31 +00:00
7ae6bf4374
Apply the rename manifest, normalize serializer filenames to the classes they define, and update includes and CMake source lists. Generated with the assistance of an AI coding tool.
228 lines
7.9 KiB
C++
228 lines
7.9 KiB
C++
#include "wire_builder.h"
|
|
|
|
#include "../../../ifcparse/logger.h"
|
|
#include "../../../ifcgeom/conversion_settings.h"
|
|
|
|
#include <TopExp.hxx>
|
|
#include <TopoDS.hxx>
|
|
#include <BRep_Tool.hxx>
|
|
#include <BRep_Builder.hxx>
|
|
#include <ShapeBuild_ReShape.hxx>
|
|
#include <GC_MakeCircle.hxx>
|
|
#include <BRepBuilderAPI_MakeEdge.hxx>
|
|
#include <Geom_Line.hxx>
|
|
#include <Geom_Circle.hxx>
|
|
#include <GeomAdaptor_Curve.hxx>
|
|
|
|
// Returns the first edge of a wire
|
|
TopoDS_Edge ifcopenshell::geom::util::first_edge(const TopoDS_Wire & w) {
|
|
TopoDS_Vertex v1, v2;
|
|
TopExp::Vertices(w, v1, v2);
|
|
NCollection_IndexedDataMap<TopoDS_Shape, NCollection_List<TopoDS_Shape>, TopTools_ShapeMapHasher> 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 ifcopenshell::geom::util::adjust(const TopoDS_Wire & w, const TopoDS_Vertex & v, const gp_Pnt & p) {
|
|
NCollection_IndexedDataMap<TopoDS_Shape, NCollection_List<TopoDS_Shape>, TopTools_ShapeMapHasher> map;
|
|
TopExp::MapShapesAndAncestors(w, TopAbs_VERTEX, TopAbs_EDGE, map);
|
|
|
|
bool all_linear = true, single_circle = false, first = true;
|
|
|
|
const NCollection_List<TopoDS_Shape>& edges = map.FindFromKey(v);
|
|
|
|
NCollection_List<TopoDS_Shape>::Iterator it(edges);
|
|
for (; it.More(); it.Next()) {
|
|
const TopoDS_Edge& e = TopoDS::Edge(it.Value());
|
|
double _, __;
|
|
Handle(Geom_Curve) crv = BRep_Tool::Curve(e, _, __);
|
|
const bool is_line = crv->DynamicType() == STANDARD_TYPE(Geom_Line);
|
|
const bool is_circle = crv->DynamicType() == STANDARD_TYPE(Geom_Circle);
|
|
all_linear = all_linear && is_line;
|
|
single_circle = first && is_circle;
|
|
}
|
|
|
|
if (all_linear) {
|
|
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));
|
|
} else if (single_circle) {
|
|
TopoDS_Vertex v1, v2;
|
|
TopExp::Vertices(w, v1, v2);
|
|
|
|
gp_Pnt p1, p2, p3;
|
|
p1 = v.IsEqual(v1) ? p : BRep_Tool::Pnt(v1);
|
|
p3 = v.IsEqual(v2) ? p : BRep_Tool::Pnt(v2);
|
|
|
|
double a, b;
|
|
Handle(Geom_Curve) crv = BRep_Tool::Curve(TopoDS::Edge(edges.First()), a, b);
|
|
crv->D0((a + b) / 2., p2);
|
|
|
|
GC_MakeCircle mc(p1, p2, p3);
|
|
if (!mc.IsDone()) {
|
|
throw ifcopenshell::geom::geometry_exception("Failed to adjust circle");
|
|
}
|
|
|
|
TopoDS_Edge edge = BRepBuilderAPI_MakeEdge(mc.Value(), p1, p3).Edge();
|
|
BRepBuilderAPI_MakeWire builder;
|
|
builder.Add(edge);
|
|
return builder.Wire();
|
|
} else {
|
|
throw ifcopenshell::geom::geometry_exception("Unexpected wire to adjust");
|
|
}
|
|
}
|
|
|
|
double ifcopenshell::geom::util::deflection_for_approximating_circle(double radius, double param) {
|
|
return -radius * std::cos(1. / 2. * param) * std::cos(param) - radius * std::sin(1. / 2. * param) * std::sin(param) + radius;
|
|
}
|
|
|
|
bool ifcopenshell::geom::util::create_edge_over_curve_with_log_messages(const opencascade::handle<Geom_Curve>& crv, const double eps, const gp_Pnt& p1, const gp_Pnt& p2, TopoDS_Edge& result) {
|
|
if (crv->IsClosed() && p1.Distance(p2) <= eps) {
|
|
BRepBuilderAPI_MakeEdge me(crv);
|
|
if (me.IsDone()) {
|
|
result = me.Edge();
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
BRep_Builder builder;
|
|
TopoDS_Vertex v1, v2;
|
|
/// @todo project first and emit warnings accordingly
|
|
builder.MakeVertex(v1, p1, eps);
|
|
builder.MakeVertex(v2, p2, eps);
|
|
|
|
BRepBuilderAPI_MakeEdge me(crv, v1, v2);
|
|
if (!me.IsDone()) {
|
|
const double eps2 = eps * eps;
|
|
if (me.Error() == BRepBuilderAPI_PointProjectionFailed) {
|
|
GeomAdaptor_Curve GAC(crv);
|
|
const gp_Pnt* ps[2] = { &p1, &p2 };
|
|
for (int i = 0; i < 2; ++i) {
|
|
Extrema_ExtPC extrema(*ps[i], GAC);
|
|
if (extrema.IsDone()) {
|
|
int n = extrema.NbExt();
|
|
double dmin = std::numeric_limits<double>::infinity();
|
|
for (int j = 1; j <= n; j++) {
|
|
const double d = extrema.SquareDistance(j);
|
|
if (d < dmin) {
|
|
dmin = d;
|
|
}
|
|
}
|
|
if (dmin == std::numeric_limits<double>::infinity()) {
|
|
ifcopenshell::logger::root().error("GEO", 205, "No extrema for point");
|
|
} else if (dmin > eps2) {
|
|
ifcopenshell::logger::root().error("GEO", 206, "Distance of " + boost::lexical_cast<std::string>(std::sqrt(dmin)) + " exceeds tolerance");
|
|
}
|
|
} else {
|
|
ifcopenshell::logger::root().error("GEO", 207, "Failed to calculate extrema for point");
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
result = me.Edge();
|
|
return true;
|
|
}
|
|
|
|
void ifcopenshell::geom::util::wire_builder::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 ifcopenshell::geom::util::wire_builder::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 tolerance, 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));
|
|
ifcopenshell::logger::root().warning("GEO", 208, "Added additional segment to close gap with length " + boost::lexical_cast<std::string>(dist) + " to:", inst_);
|
|
goto check;
|
|
}
|
|
|
|
{
|
|
NCollection_IndexedDataMap<TopoDS_Shape, NCollection_List<TopoDS_Shape>, TopTools_ShapeMapHasher> 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 NCollection_List<TopoDS_Shape>& last_edges = wmap1.FindFromKey(w12);
|
|
const NCollection_List<TopoDS_Shape>& 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);
|
|
|
|
const bool is_circle1 = c1->DynamicType() == STANDARD_TYPE(Geom_Circle);
|
|
const bool is_circle2 = c2->DynamicType() == STANDARD_TYPE(Geom_Circle);
|
|
|
|
// Preferably adjust the segment that is linear
|
|
if (is_line1 || (is_circle1 && !is_line2)) {
|
|
mw_.Add(adjust(w1, w12, p2));
|
|
ifcopenshell::logger::root().notice("GEO", 209, "Adjusted edge end-point with distance " + boost::lexical_cast<std::string>(dist) + " on:", inst_);
|
|
} else if ((is_line2 || is_circle2) && !last) {
|
|
mw_.Add(w1);
|
|
override_next_ = true;
|
|
next_override_ = p1;
|
|
ifcopenshell::logger::root().notice("GEO", 210, "Adjusted edge end-point with distance " + boost::lexical_cast<std::string>(dist) + " on:", inst_);
|
|
} else {
|
|
// In all other cases an edge is added
|
|
mw_.Add(w1);
|
|
mw_.Add(BRepBuilderAPI_MakeEdge(p1, p2));
|
|
ifcopenshell::logger::root().warning("GEO", 211, "Added additional segment to close gap with length " + boost::lexical_cast<std::string>(dist) + " to:", inst_);
|
|
}
|
|
} else {
|
|
ifcopenshell::logger::root().error("GEO", 212, "Internal error, inconsistent wire segments", inst_);
|
|
mw_.Add(w1);
|
|
}
|
|
}
|
|
|
|
check:
|
|
if (mw_.Error() == BRepBuilderAPI_NonManifoldWire) {
|
|
ifcopenshell::logger::root().error("GEO", 213, "Non-manifold curve segments:", inst_);
|
|
} else if (mw_.Error() == BRepBuilderAPI_DisconnectedWire) {
|
|
ifcopenshell::logger::root().error("GEO", 214, "Failed to join curve segments:", inst_);
|
|
}
|
|
}
|