diff --git a/src/Mod/Part/App/FeatureExtrusion.cpp b/src/Mod/Part/App/FeatureExtrusion.cpp index 7c4605a8a..0755bb5d1 100644 --- a/src/Mod/Part/App/FeatureExtrusion.cpp +++ b/src/Mod/Part/App/FeatureExtrusion.cpp @@ -24,7 +24,11 @@ #include "PreCompiled.h" #ifndef _PreComp_ # include +# include +# include +# include # include +# include # include # include # include @@ -33,12 +37,17 @@ # include # include # include +# include +# include # include # include +# include # include # include # include +# include # include +# include #endif @@ -161,9 +170,24 @@ App::DocumentObjectExecReturn *Extrusion::execute(void) Standard_Failure::Raise("Cannot extrude empty shape"); // #0000910: Circles Extrude Only Surfaces, thus use BRepBuilderAPI_Copy myShape = BRepBuilderAPI_Copy(myShape).Shape(); - if (makeSolid && myShape.ShapeType() == TopAbs_WIRE) { - BRepBuilderAPI_MakeFace mkFace(TopoDS::Wire(myShape)); - myShape = validateFace(mkFace.Face()); + if (makeSolid && myShape.ShapeType() != TopAbs_FACE) { + TopTools_IndexedMapOfShape mapOfWires; + TopExp::MapShapes(myShape, TopAbs_WIRE, mapOfWires); + if (!mapOfWires.IsEmpty()) { + try { + std::vector wires; + wires.reserve(mapOfWires.Extent()); + for (int i=1; i<=mapOfWires.Extent(); i++) { + wires.push_back(TopoDS::Wire(mapOfWires.FindKey(i))); + } + + TopoDS_Shape res = makeFace(wires); + if (!res.IsNull()) + myShape = res; + } + catch (...) { + } + } } BRepPrimAPI_MakePrism mkPrism(myShape, vec); TopoDS_Shape swept = mkPrism.Shape(); @@ -211,10 +235,163 @@ TopoDS_Face Extrusion::validateFace(const TopoDS_Face& face) const fix.Perform(); fix.FixWireTool()->Perform(); fix.FixFaceTool()->Perform(); - return TopoDS::Face(fix.Shape()); + TopoDS_Face fixedFace = TopoDS::Face(fix.Shape()); + aChecker.Init(fixedFace); + if (!aChecker.IsValid()) + Standard_Failure::Raise("Failed to validate broken face"); + return fixedFace; } return mkFace.Face(); } return face; } + +// sort bounding boxes according to diagonal length +class Extrusion::Wire_Compare : public std::binary_function { +public: + bool operator() (const TopoDS_Wire& w1, const TopoDS_Wire& w2) + { + Bnd_Box box1, box2; + if (!w1.IsNull()) { + BRepBndLib::Add(w1, box1); + box1.SetGap(0.0); + } + + if (!w2.IsNull()) { + BRepBndLib::Add(w2, box2); + box2.SetGap(0.0); + } + + return box1.SquareExtent() < box2.SquareExtent(); + } +}; + +bool Extrusion::isInside(const TopoDS_Wire& wire1, const TopoDS_Wire& wire2) const +{ + Bnd_Box box1; + BRepBndLib::Add(wire1, box1); + box1.SetGap(0.0); + + Bnd_Box box2; + BRepBndLib::Add(wire2, box2); + box2.SetGap(0.0); + + if (box1.IsOut(box2)) + return false; + + double prec = Precision::Confusion(); + + BRepBuilderAPI_MakeFace mkFace(wire1); + if (!mkFace.IsDone()) + Standard_Failure::Raise("Failed to create a face from wire in sketch"); + TopoDS_Face face = validateFace(mkFace.Face()); + BRepAdaptor_Surface adapt(face); + IntTools_FClass2d class2d(face, prec); + Handle_Geom_Surface surf = new Geom_Plane(adapt.Plane()); + ShapeAnalysis_Surface as(surf); + + TopExp_Explorer xp(wire2,TopAbs_VERTEX); + while (xp.More()) { + TopoDS_Vertex v = TopoDS::Vertex(xp.Current()); + gp_Pnt p = BRep_Tool::Pnt(v); + gp_Pnt2d uv = as.ValueOfUV(p, prec); + if (class2d.Perform(uv) == TopAbs_IN) + return true; + // TODO: We can make a check to see if all points are inside or all outside + // because otherwise we have some intersections which is not allowed + else + return false; + xp.Next(); + } + + return false; +} + +TopoDS_Shape Extrusion::makeFace(std::list& wires) const +{ + BRepBuilderAPI_MakeFace mkFace(wires.front()); + const TopoDS_Face& face = mkFace.Face(); + if (face.IsNull()) + return face; + gp_Dir axis(0,0,1); + BRepAdaptor_Surface adapt(face); + if (adapt.GetType() == GeomAbs_Plane) { + axis = adapt.Plane().Axis().Direction(); + } + + wires.pop_front(); + for (std::list::iterator it = wires.begin(); it != wires.end(); ++it) { + BRepBuilderAPI_MakeFace mkInnerFace(*it); + const TopoDS_Face& inner_face = mkInnerFace.Face(); + if (inner_face.IsNull()) + return inner_face; // failure + gp_Dir inner_axis(0,0,1); + BRepAdaptor_Surface adapt(inner_face); + if (adapt.GetType() == GeomAbs_Plane) { + inner_axis = adapt.Plane().Axis().Direction(); + } + // It seems that orientation is always 'Forward' and we only have to reverse + // if the underlying plane have opposite normals. + if (axis.Dot(inner_axis) < 0) + it->Reverse(); + mkFace.Add(*it); + } + return validateFace(mkFace.Face()); +} + +TopoDS_Shape Extrusion::makeFace(const std::vector& w) const +{ + if (w.empty()) + return TopoDS_Shape(); + + //FIXME: Need a safe method to sort wire that the outermost one comes last + // Currently it's done with the diagonal lengths of the bounding boxes + std::vector wires = w; + std::sort(wires.begin(), wires.end(), Wire_Compare()); + std::list wire_list; + wire_list.insert(wire_list.begin(), wires.rbegin(), wires.rend()); + + // separate the wires into several independent faces + std::list< std::list > sep_wire_list; + while (!wire_list.empty()) { + std::list sep_list; + TopoDS_Wire wire = wire_list.front(); + wire_list.pop_front(); + sep_list.push_back(wire); + + std::list::iterator it = wire_list.begin(); + while (it != wire_list.end()) { + if (isInside(wire, *it)) { + sep_list.push_back(*it); + it = wire_list.erase(it); + } + else { + ++it; + } + } + + sep_wire_list.push_back(sep_list); + } + + if (sep_wire_list.size() == 1) { + std::list& wires = sep_wire_list.front(); + return makeFace(wires); + } + else if (sep_wire_list.size() > 1) { + TopoDS_Compound comp; + BRep_Builder builder; + builder.MakeCompound(comp); + for (std::list< std::list >::iterator it = sep_wire_list.begin(); it != sep_wire_list.end(); ++it) { + TopoDS_Shape aFace = makeFace(*it); + if (!aFace.IsNull()) + builder.Add(comp, aFace); + } + + return comp; + } + else { + return TopoDS_Shape(); // error + } +} diff --git a/src/Mod/Part/App/FeatureExtrusion.h b/src/Mod/Part/App/FeatureExtrusion.h index 4b0597f3c..f41b2fbb5 100644 --- a/src/Mod/Part/App/FeatureExtrusion.h +++ b/src/Mod/Part/App/FeatureExtrusion.h @@ -56,7 +56,13 @@ public: //@} private: + bool isInside(const TopoDS_Wire&, const TopoDS_Wire&) const; TopoDS_Face validateFace(const TopoDS_Face&) const; + TopoDS_Shape makeFace(const std::vector&) const; + TopoDS_Shape makeFace(std::list&) const; // for internal use only + +private: + class Wire_Compare; }; } //namespace Part