Part: Face: use facemakers
For old documents, default to FaceMakerCheese. For newly created objects, use FaceMakerBullseye
This commit is contained in:
parent
12d33e841f
commit
e898789cba
|
@ -44,6 +44,7 @@
|
|||
#include <Base/Placement.h>
|
||||
|
||||
#include "FeatureFace.h"
|
||||
#include "FaceMaker.h"
|
||||
#include <Mod/Part/App/Part2DObject.h>
|
||||
|
||||
|
||||
|
@ -55,26 +56,37 @@ PROPERTY_SOURCE(Part::Face, Part::Feature)
|
|||
Face::Face()
|
||||
{
|
||||
ADD_PROPERTY(Sources,(0));
|
||||
ADD_PROPERTY(FaceMakerClass,("Part::FaceMakerCheese"));//default value here is for legacy documents. Default for new objects is set in setupObject.
|
||||
Sources.setSize(0);
|
||||
}
|
||||
|
||||
short Face::mustExecute() const
|
||||
{
|
||||
if (FaceMakerClass.isTouched())
|
||||
return 1;
|
||||
if (Sources.isTouched())
|
||||
return 1;
|
||||
return Part::Feature::mustExecute();
|
||||
}
|
||||
|
||||
void Face::setupObject()
|
||||
{
|
||||
this->FaceMakerClass.setValue("Part::FaceMakerBullseye");
|
||||
Feature::setupObject();
|
||||
}
|
||||
|
||||
App::DocumentObjectExecReturn *Face::execute(void)
|
||||
{
|
||||
std::vector<App::DocumentObject*> links = Sources.getValues();
|
||||
if (links.empty())
|
||||
return new App::DocumentObjectExecReturn("No shapes linked");
|
||||
|
||||
std::vector<TopoDS_Wire> wires;
|
||||
std::unique_ptr<FaceMaker> fm_instance = FaceMaker::ConstructFromType(this->FaceMakerClass.getValue());
|
||||
FaceMaker* facemaker = &(*(fm_instance));
|
||||
|
||||
for (std::vector<App::DocumentObject*>::iterator it = links.begin(); it != links.end(); ++it) {
|
||||
if (!(*it && (*it)->isDerivedFrom(Part::Part2DObject::getClassTypeId())))
|
||||
return new App::DocumentObjectExecReturn("Linked object is not a Sketch or Part2DObject");
|
||||
if (!(*it && (*it)->isDerivedFrom(Part::Feature::getClassTypeId())))
|
||||
return new App::DocumentObjectExecReturn("Linked object is not a Part object (has no Shape).");
|
||||
TopoDS_Shape shape = static_cast<Part::Part2DObject*>(*it)->Shape.getShape().getShape();
|
||||
if (shape.IsNull())
|
||||
return new App::DocumentObjectExecReturn("Linked shape object is empty");
|
||||
|
@ -82,139 +94,26 @@ App::DocumentObjectExecReturn *Face::execute(void)
|
|||
// this is a workaround for an obscure OCC bug which leads to empty tessellations
|
||||
// for some faces. Making an explicit copy of the linked shape seems to fix it.
|
||||
// The error only happens when re-computing the shape.
|
||||
if (!this->Shape.getValue().IsNull()) {
|
||||
/*if (!this->Shape.getValue().IsNull()) {
|
||||
BRepBuilderAPI_Copy copy(shape);
|
||||
shape = copy.Shape();
|
||||
if (shape.IsNull())
|
||||
return new App::DocumentObjectExecReturn("Linked shape object is empty");
|
||||
}
|
||||
}*/
|
||||
|
||||
TopExp_Explorer ex;
|
||||
for (ex.Init(shape, TopAbs_WIRE); ex.More(); ex.Next()) {
|
||||
wires.push_back(TopoDS::Wire(ex.Current()));
|
||||
}
|
||||
if(links.size() == 1 && shape.ShapeType() == TopAbs_COMPOUND)
|
||||
facemaker->useCompound(TopoDS::Compound(shape));
|
||||
else
|
||||
facemaker->addShape(shape);
|
||||
}
|
||||
|
||||
if (wires.empty()) // there can be several wires
|
||||
return new App::DocumentObjectExecReturn("Linked shape object is not a wire");
|
||||
facemaker->Build();
|
||||
|
||||
TopoDS_Shape aFace = makeFace(wires);
|
||||
TopoDS_Shape aFace = facemaker->Shape();
|
||||
if (aFace.IsNull())
|
||||
return new App::DocumentObjectExecReturn("Creating a face from sketch failed");
|
||||
return new App::DocumentObjectExecReturn("Creating face failed (null shape result)");
|
||||
this->Shape.setValue(aFace);
|
||||
|
||||
return App::DocumentObject::StdReturn;
|
||||
}
|
||||
|
||||
// sort bounding boxes according to diagonal length
|
||||
class Face::Wire_Compare {
|
||||
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();
|
||||
}
|
||||
};
|
||||
|
||||
TopoDS_Shape Face::makeFace(std::list<TopoDS_Wire>& 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<TopoDS_Wire>::iterator it = wires.begin(); it != wires.end(); ++it) {
|
||||
BRepBuilderAPI_MakeFace mkInnerFace(*it);
|
||||
const TopoDS_Face& inner_face = mkInnerFace.Face();
|
||||
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 mkFace.Face();
|
||||
}
|
||||
|
||||
TopoDS_Shape Face::makeFace(const std::vector<TopoDS_Wire>& 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<TopoDS_Wire> wires = w;
|
||||
std::sort(wires.begin(), wires.end(), Wire_Compare());
|
||||
std::list<TopoDS_Wire> wire_list;
|
||||
wire_list.insert(wire_list.begin(), wires.rbegin(), wires.rend());
|
||||
|
||||
// separate the wires into several independent faces
|
||||
std::list< std::list<TopoDS_Wire> > sep_wire_list;
|
||||
while (!wire_list.empty()) {
|
||||
std::list<TopoDS_Wire> sep_list;
|
||||
TopoDS_Wire wire = wire_list.front();
|
||||
wire_list.pop_front();
|
||||
sep_list.push_back(wire);
|
||||
|
||||
Bnd_Box box;
|
||||
BRepBndLib::Add(wire, box);
|
||||
box.SetGap(0.0);
|
||||
|
||||
std::list<TopoDS_Wire>::iterator it = wire_list.begin();
|
||||
while (it != wire_list.end()) {
|
||||
Bnd_Box box2;
|
||||
BRepBndLib::Add(*it, box2);
|
||||
box2.SetGap(0.0);
|
||||
if (!box.IsOut(box2)) {
|
||||
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<TopoDS_Wire>& 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<TopoDS_Wire> >::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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@ public:
|
|||
Face();
|
||||
|
||||
App::PropertyLinkList Sources;
|
||||
App::PropertyString FaceMakerClass;
|
||||
|
||||
/** @name methods override feature */
|
||||
//@{
|
||||
|
@ -47,14 +48,8 @@ public:
|
|||
const char* getViewProviderName(void) const {
|
||||
return "PartGui::ViewProviderFace";
|
||||
}
|
||||
void setupObject() override;
|
||||
//@}
|
||||
|
||||
protected:
|
||||
TopoDS_Shape makeFace(const std::vector<TopoDS_Wire>&) const;
|
||||
TopoDS_Shape makeFace(std::list<TopoDS_Wire>&) const; // for internal use only
|
||||
|
||||
private:
|
||||
class Wire_Compare;
|
||||
};
|
||||
|
||||
} //namespace Part
|
||||
|
|
Loading…
Reference in New Issue
Block a user