Break up sweeps along adjacent non-linear segments.

This commit is contained in:
Thomas Krijnen
2019-11-01 15:06:04 +01:00
parent 2d0d0ed786
commit 159ce931b4
+214 -47
View File
@@ -62,6 +62,9 @@
#include <BRepBuilderAPI_MakePolygon.hxx> #include <BRepBuilderAPI_MakePolygon.hxx>
#include <BRepBuilderAPI_MakeVertex.hxx> #include <BRepBuilderAPI_MakeVertex.hxx>
#include <BRepGProp.hxx>
#include <GProp_GProps.hxx>
#include <TopoDS.hxx> #include <TopoDS.hxx>
#include <TopoDS_Wire.hxx> #include <TopoDS_Wire.hxx>
#include <TopoDS_Face.hxx> #include <TopoDS_Face.hxx>
@@ -1067,17 +1070,8 @@ namespace {
} }
} }
bool IfcGeom::Kernel::convert(const IfcSchema::IfcSweptDiskSolid* l, TopoDS_Shape& shape) { namespace {
TopoDS_Wire wire, section1, section2; bool wire_to_ax(const TopoDS_Wire& wire, gp_Ax2& directrix) {
bool hasInnerRadius = l->hasInnerRadius();
if (!convert_wire(l->Directrix(), wire)) {
return false;
}
gp_Ax2 directrix;
{
gp_Pnt directrix_origin; gp_Pnt directrix_origin;
gp_Vec directrix_tangent; gp_Vec directrix_tangent;
@@ -1088,10 +1082,10 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcSweptDiskSolid* l, TopoDS_Shap
TopExp::Vertices(wire, v0, v1); TopExp::Vertices(wire, v0, v1);
TopTools_IndexedDataMapOfShapeListOfShape map; TopTools_IndexedDataMapOfShapeListOfShape map;
TopExp::MapShapesAndAncestors(wire, TopAbs_VERTEX, TopAbs_EDGE, map); TopExp::MapShapesAndAncestors(wire, TopAbs_VERTEX, TopAbs_EDGE, map);
if (map.Contains(v0) && map.FindFromKey(v0).Extent() == 1) { if (map.Contains(v0) && map.FindFromKey(v0).Extent() == 1) {
edge = TopoDS::Edge(map.FindFromKey(v0).First()); edge = TopoDS::Edge(map.FindFromKey(v0).First());
} else { } else {
Logger::Error("Unable to locate first edge of:", l->Directrix()); Logger::Error("Unable to locate first edge");
return false; return false;
} }
@@ -1099,56 +1093,229 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcSweptDiskSolid* l, TopoDS_Shap
Handle(Geom_Curve) crv = BRep_Tool::Curve(edge, u0, u1); Handle(Geom_Curve) crv = BRep_Tool::Curve(edge, u0, u1);
crv->D1(u0, directrix_origin, directrix_tangent); crv->D1(u0, directrix_origin, directrix_tangent);
directrix = gp_Ax2(directrix_origin, directrix_tangent); directrix = gp_Ax2(directrix_origin, directrix_tangent);
return true;
} }
const double r1 = l->Radius() * getValue(GV_LENGTH_UNIT); bool is_single_linear_edge(const TopoDS_Wire& wire) {
Handle(Geom_Circle) circle = new Geom_Circle(directrix, r1); TopExp_Explorer exp(wire, TopAbs_EDGE);
section1 = BRepBuilderAPI_MakeWire(BRepBuilderAPI_MakeEdge(circle)); if (!exp.More()) {
return false;
}
TopoDS_Edge e = TopoDS::Edge(exp.Current());
exp.Next();
if (exp.More()) {
return false;
}
double u, v;
Handle_Geom_Curve crv = BRep_Tool::Curve(e, u, v);
return crv->DynamicType() == STANDARD_TYPE(Geom_Line);
}
if (hasInnerRadius) { void process_sweep_as_extrusion(const TopoDS_Wire& wire, const TopoDS_Wire& section, TopoDS_Shape& result) {
const double r2 = l->InnerRadius() * getValue(GV_LENGTH_UNIT); TopExp_Explorer exp(wire, TopAbs_EDGE);
if (r2 < getValue(GV_PRECISION)) { TopoDS_Edge e = TopoDS::Edge(exp.Current());
// Subtraction of pipes with small radii is unstable. double u, v;
hasInnerRadius = false; Handle_Geom_Curve crv = BRep_Tool::Curve(e, u, v);
} else { const auto& dir = Handle(Geom_Line)::DownCast(crv)->Position().Direction();
Handle(Geom_Circle) circle2 = new Geom_Circle(directrix, r2); // OCCT line is normalized so diff in parametric coords equals length
section2 = BRepBuilderAPI_MakeWire(BRepBuilderAPI_MakeEdge(circle2)); const double depth = std::abs(u - v);
// @todo we could be extruding the wire only when we know this is an intermediate edge.
TopoDS_Face face = BRepBuilderAPI_MakeFace(section).Face();
result = BRepPrimAPI_MakePrism(face, depth*dir).Shape();
}
void process_sweep_as_pipe(const TopoDS_Wire& wire, const TopoDS_Wire& section, TopoDS_Shape& result) {
BRepOffsetAPI_MakePipeShell builder(wire);
builder.Add(section);
builder.SetTransitionMode(BRepBuilderAPI_RightCorner);
builder.Build();
builder.MakeSolid();
result = builder.Shape();
}
void sort_edges(const TopoDS_Wire& wire, std::vector<TopoDS_Edge>& sorted_edges) {
TopTools_IndexedDataMapOfShapeListOfShape map;
TopExp::MapShapesAndAncestors(wire, TopAbs_VERTEX, TopAbs_EDGE, map);
TopoDS_Vertex v0, v1;
// @todo this creates the ancestor map twice
TopExp::Vertices(wire, v0, v1);
TopTools_ListOfShape es;
while (!v0.IsSame(v1)) {
if (!map.FindFromKey(v0, es)) {
throw std::runtime_error("Disconnected vertex");
}
TopoDS_Vertex ve0, ve1;
TopTools_ListIteratorOfListOfShape it(es);
bool added = false;
for (; it.More(); it.Next()) {
const TopoDS_Edge& e = TopoDS::Edge(it.Value());
TopExp::Vertices(e, ve0, ve1, true);
if (ve0.IsSame(v0)) {
sorted_edges.push_back(e);
v0 = ve1;
added = true;
break;
}
}
if (!added) {
throw std::runtime_error("Disconnected edge");
}
} }
} }
// This is not used anymore, BRepBuilderAPI_RightCorner is always used now. void segment_tiny_edges(const TopoDS_Wire& wire, std::vector<TopoDS_Wire>& wires, double eps) {
// const bool is_continuous = wire_is_c1_continuous(wire, 1.e-3); std::vector<TopoDS_Edge> sorted_edges;
sort_edges(wire, sorted_edges);
bool segment_next = true;
BRep_Builder B;
for (const auto& e : sorted_edges) {
GProp_GProps prop;
BRepGProp::LinearProperties(e, prop);
const double l = prop.Mass();
if (l < eps || segment_next) {
wires.emplace_back();
B.MakeWire(wires.back());
segment_next = l < eps;
}
B.Add(wires.back(), e);
}
}
void segment_adjacent_non_linear(const TopoDS_Wire& wire, std::vector<TopoDS_Wire>& wires, double eps) {
std::vector<TopoDS_Edge> sorted_edges;
sort_edges(wire, sorted_edges);
bool segment_next = true;
BRep_Builder B;
double u, v;
wires.emplace_back();
B.MakeWire(wires.back());
for (size_t i = 0; i < sorted_edges.size() - 1; ++i) {
const auto& e = sorted_edges[i];
Handle_Geom_Curve crv = BRep_Tool::Curve(e, u, v);
const bool is_linear = crv->DynamicType() == STANDARD_TYPE(Geom_Line);
const auto& f = sorted_edges[i+1];
crv = BRep_Tool::Curve(f, u, v);
const bool next_is_linear = crv->DynamicType() == STANDARD_TYPE(Geom_Line);
B.Add(wires.back(), e);
if (!is_linear && !next_is_linear) {
wires.emplace_back();
B.MakeWire(wires.back());
}
}
B.Add(wires.back(), sorted_edges.back());
}
// @todo make this generic for other sweeps not just swept disk
void process_sweep(const TopoDS_Wire& wire, double radius, TopoDS_Shape& result) {
std::vector<TopoDS_Wire> wires;
segment_adjacent_non_linear(wire, wires, 1.e-2);
TopoDS_Compound C;
BRep_Builder B;
if (wires.size() > 1) {
B.MakeCompound(C);
}
for (auto& w : wires) {
TopoDS_Shape part;
gp_Ax2 directrix;
if (!wire_to_ax(w, directrix)) {
continue;
}
Handle(Geom_Circle) circle = new Geom_Circle(directrix, radius);
TopoDS_Wire section = BRepBuilderAPI_MakeWire(BRepBuilderAPI_MakeEdge(circle));
if (is_single_linear_edge(w)) {
process_sweep_as_extrusion(w, section, part);
} else {
process_sweep_as_pipe(w, section, part);
}
if (wires.size() > 1) {
B.Add(C, part);
} else {
result = part;
}
}
if (wires.size() > 1) {
result = C;
}
}
}
bool IfcGeom::Kernel::convert(const IfcSchema::IfcSweptDiskSolid* l, TopoDS_Shape& shape) {
TopoDS_Wire wire, section1, section2;
bool hasInnerRadius = l->hasInnerRadius();
if (!convert_wire(l->Directrix(), wire)) {
return false;
}
BRepTools::Write(wire, "debug-wire.brep");
{
std::ofstream fs("debug-wire.txt");
BRepTools::Dump(wire, fs);
}
// NB: Note that StartParam and EndParam param are ignored and the assumption is // NB: Note that StartParam and EndParam param are ignored and the assumption is
// made that the parametric range over which to be swept matches the IfcCurve in // made that the parametric range over which to be swept matches the IfcCurve in
// its entirety. // its entirety.
{ BRepOffsetAPI_MakePipeShell builder(wire);
builder.Add(section1); // This is not used anymore, BRepBuilderAPI_RightCorner is always used now.
builder.SetTransitionMode(BRepBuilderAPI_RightCorner); // const bool is_continuous = wire_is_c1_continuous(wire, 1.e-3);
builder.Build();
builder.MakeSolid(); process_sweep(wire, l->Radius() * getValue(GV_LENGTH_UNIT), shape);
shape = builder.Shape(); }
double r2 = 0.;
if (hasInnerRadius) { if (hasInnerRadius) {
BRepOffsetAPI_MakePipeShell builder(wire); // Subtraction of pipes with small radii is unstable.
builder.Add(section2); const double r2 = l->InnerRadius() * getValue(GV_LENGTH_UNIT);
builder.SetTransitionMode(BRepBuilderAPI_RightCorner); }
builder.Build();
builder.MakeSolid(); if (r2 > getValue(GV_PRECISION) * 10.) {
TopoDS_Shape inner = builder.Shape(); TopoDS_Shape inner;
process_sweep(wire, r2, inner);
BRepAlgoAPI_Cut brep_cut(shape, inner);
bool is_valid = false; bool is_valid = false;
if (brep_cut.IsDone()) {
TopoDS_Shape result = brep_cut;
ShapeFix_Shape fix(result); // Boolean op on the compound of separately processed sweeps
fix.Perform(); // is not attempted.
result = fix.Shape(); // @todo iterate over compound subshapes and process boolean
// separately.
// @todo don't process as boolean op at all, since we know
// only the start and end faces intersect and we know they
// are co-planar and we know they are circles.
if (shape.ShapeType() != TopAbs_COMPOUND) {
BRepAlgoAPI_Cut brep_cut(shape, inner);
if (brep_cut.IsDone()) {
TopoDS_Shape result = brep_cut;
is_valid = BRepCheck_Analyzer(result).IsValid() != 0; ShapeFix_Shape fix(result);
if (is_valid) { fix.Perform();
shape = result; result = fix.Shape();
is_valid = BRepCheck_Analyzer(result).IsValid() != 0;
if (is_valid) {
shape = result;
}
} }
} }