Make PartDesign::Boolean work with new Link structure
This is the first feature that used GeoFeatureGroupExtension and required links to the groups inside as well as to things on the same level. Hence a few modifications to link scopes have been nesseccary.
This commit is contained in:
@@ -41,6 +41,8 @@ PROPERTY_SOURCE_WITH_EXTENSIONS(Part::BodyBase, Part::Feature)
|
||||
BodyBase::BodyBase()
|
||||
{
|
||||
ADD_PROPERTY(Tip , (0) );
|
||||
Tip.setScope(App::LinkScope::Child);
|
||||
|
||||
ADD_PROPERTY(BaseFeature , (0) );
|
||||
|
||||
App::OriginGroupExtension::initExtension(this);
|
||||
|
||||
@@ -55,7 +55,6 @@ using namespace PartDesign;
|
||||
PROPERTY_SOURCE(PartDesign::Body, Part::BodyBase)
|
||||
|
||||
Body::Body() {
|
||||
ADD_PROPERTY_TYPE (Origin, (0), 0, App::Prop_Hidden, "Origin linked to the body" );
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -46,7 +46,7 @@ using namespace PartDesign;
|
||||
|
||||
namespace PartDesign {
|
||||
|
||||
PROPERTY_SOURCE(PartDesign::Boolean, PartDesign::Feature)
|
||||
PROPERTY_SOURCE_WITH_EXTENSIONS(PartDesign::Boolean, PartDesign::Feature)
|
||||
|
||||
const char* Boolean::TypeEnums[]= {"Fuse","Cut","Common","Section",NULL};
|
||||
|
||||
@@ -54,13 +54,13 @@ Boolean::Boolean()
|
||||
{
|
||||
ADD_PROPERTY(Type,((long)0));
|
||||
Type.setEnums(TypeEnums);
|
||||
ADD_PROPERTY(Bodies,(0));
|
||||
Bodies.setSize(0);
|
||||
|
||||
initExtension(this);
|
||||
}
|
||||
|
||||
short Boolean::mustExecute() const
|
||||
{
|
||||
if (Bodies.isTouched())
|
||||
if (Group.isTouched())
|
||||
return 1;
|
||||
return PartDesign::Feature::mustExecute();
|
||||
}
|
||||
@@ -74,8 +74,8 @@ App::DocumentObjectExecReturn *Boolean::execute(void)
|
||||
return new App::DocumentObjectExecReturn("Cannot do boolean operation with invalid BaseFeature");
|
||||
}
|
||||
|
||||
std::vector<App::DocumentObject*> bodies = Bodies.getValues();
|
||||
if (bodies.empty())
|
||||
std::vector<App::DocumentObject*> tools = Group.getValues();
|
||||
if (tools.empty())
|
||||
return App::DocumentObject::StdReturn;
|
||||
|
||||
// Get the base shape to operate on
|
||||
@@ -89,84 +89,66 @@ App::DocumentObjectExecReturn *Boolean::execute(void)
|
||||
if(!baseBody)
|
||||
return new App::DocumentObjectExecReturn("Cannot do boolean on feature which is not in a body");
|
||||
|
||||
// TODO: Why is Feature::getLocation() protected?
|
||||
Base::Placement place = baseBody->Placement.getValue();
|
||||
Base::Rotation rot(place.getRotation());
|
||||
Base::Vector3d axis;
|
||||
double angle;
|
||||
rot.getValue(axis, angle);
|
||||
gp_Trsf trf;
|
||||
trf.SetRotation(gp_Ax1(gp_Pnt(), gp_Dir(axis.x, axis.y, axis.z)), angle);
|
||||
trf.SetTranslationPart(gp_Vec(place.getPosition().x,place.getPosition().y,place.getPosition().z));
|
||||
TopLoc_Location objLoc(trf);
|
||||
|
||||
TopoDS_Shape result = baseTopShape.getShape();
|
||||
result.Move(objLoc);
|
||||
|
||||
// Get the operation type
|
||||
std::string type = Type.getValueAsString();
|
||||
|
||||
for (std::vector<App::DocumentObject*>::const_iterator b = bodies.begin(); b != bodies.end(); b++)
|
||||
for (auto tool : tools)
|
||||
{
|
||||
// Extract the body shape. Its important to get the actual feature that provides the last solid in the body
|
||||
// so that the placement will be right
|
||||
PartDesign::Body* body = static_cast<PartDesign::Body*>(*b);
|
||||
if(!tool->isDerivedFrom(Part::Feature::getClassTypeId()))
|
||||
return new App::DocumentObjectExecReturn("Cannot do boolean with anything but Part::Feature and its derivatives");
|
||||
|
||||
TopoDS_Shape shape = body->Shape.getValue();
|
||||
|
||||
// Move the shape to the location of the base shape in the other body
|
||||
Base::Placement pl = body->Placement.getValue();
|
||||
// TODO: Why is Feature::getLocation() protected?
|
||||
Base::Rotation rot(pl.getRotation());
|
||||
Base::Vector3d axis;
|
||||
double angle;
|
||||
rot.getValue(axis, angle);
|
||||
gp_Trsf trf;
|
||||
trf.SetRotation(gp_Ax1(gp_Pnt(), gp_Dir(axis.x, axis.y, axis.z)), angle);
|
||||
trf.SetTranslationPart(gp_Vec(pl.getPosition().x,pl.getPosition().y,pl.getPosition().z));
|
||||
TopLoc_Location bLoc(trf);
|
||||
shape.Move(bLoc);
|
||||
|
||||
TopoDS_Shape shape = static_cast<Part::Feature*>(tool)->Shape.getValue();
|
||||
TopoDS_Shape boolOp;
|
||||
|
||||
if (type == "Fuse") {
|
||||
BRepAlgoAPI_Fuse mkFuse(result, shape);
|
||||
if (!mkFuse.IsDone())
|
||||
return new App::DocumentObjectExecReturn("Fusion of bodies failed", *b);
|
||||
return new App::DocumentObjectExecReturn("Fusion of tools failed");
|
||||
// we have to get the solids (fuse sometimes creates compounds)
|
||||
boolOp = this->getSolid(mkFuse.Shape());
|
||||
// lets check if the result is a solid
|
||||
if (boolOp.IsNull())
|
||||
return new App::DocumentObjectExecReturn("Resulting shape is not a solid", *b);
|
||||
return new App::DocumentObjectExecReturn("Resulting shape is not a solid");
|
||||
} else if (type == "Cut") {
|
||||
BRepAlgoAPI_Cut mkCut(result, shape);
|
||||
if (!mkCut.IsDone())
|
||||
return new App::DocumentObjectExecReturn("Cut out of first body failed", *b);
|
||||
return new App::DocumentObjectExecReturn("Cut out failed");
|
||||
boolOp = mkCut.Shape();
|
||||
} else if (type == "Common") {
|
||||
BRepAlgoAPI_Common mkCommon(result, shape);
|
||||
if (!mkCommon.IsDone())
|
||||
return new App::DocumentObjectExecReturn("Common operation with first body failed", *b);
|
||||
return new App::DocumentObjectExecReturn("Common operation failed");
|
||||
boolOp = mkCommon.Shape();
|
||||
} else if (type == "Section") {
|
||||
BRepAlgoAPI_Section mkSection(result, shape);
|
||||
if (!mkSection.IsDone())
|
||||
return new App::DocumentObjectExecReturn("Section out of first body failed", *b);
|
||||
return new App::DocumentObjectExecReturn("Section failed");
|
||||
// we have to get the solids
|
||||
boolOp = this->getSolid(mkSection.Shape());
|
||||
// lets check if the result is a solid
|
||||
if (boolOp.IsNull())
|
||||
return new App::DocumentObjectExecReturn("Resulting shape is not a solid", *b);
|
||||
return new App::DocumentObjectExecReturn("Resulting shape is not a solid");
|
||||
}
|
||||
|
||||
result = boolOp; // Use result of this operation for fuse/cut of next body
|
||||
//bring the result geometry into the correct coordinance of the body the boolean belongs to
|
||||
BRepBuilderAPI_GTransform mkTrf(result, objLoc.Inverted().Transformation());
|
||||
result = mkTrf.Shape();
|
||||
}
|
||||
|
||||
this->Shape.setValue(getSolid(result));
|
||||
return App::DocumentObject::StdReturn;
|
||||
}
|
||||
|
||||
void Boolean::onChanged(const App::Property* prop) {
|
||||
|
||||
if(strcmp(prop->getName(), "Group") == 0)
|
||||
touch();
|
||||
|
||||
PartDesign::Feature::onChanged(prop);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#define PARTDESIGN_FeatureBoolean_H
|
||||
|
||||
#include <App/PropertyStandard.h>
|
||||
#include <App/GeoFeatureGroupExtension.h>
|
||||
#include "Feature.h"
|
||||
|
||||
|
||||
@@ -35,17 +36,15 @@ namespace PartDesign
|
||||
* Abstract superclass of all features that are created by transformation of another feature
|
||||
* Transformations are translation, rotation and mirroring
|
||||
*/
|
||||
class PartDesignExport Boolean : public PartDesign::Feature
|
||||
class PartDesignExport Boolean : public PartDesign::Feature, public App::GeoFeatureGroupExtension
|
||||
{
|
||||
PROPERTY_HEADER(PartDesign::Boolean);
|
||||
PROPERTY_HEADER_WITH_EXTENSIONS(PartDesign::Boolean);
|
||||
|
||||
public:
|
||||
Boolean();
|
||||
|
||||
/// The type of the boolean operation
|
||||
App::PropertyEnumeration Type;
|
||||
/// The bodies for the operation
|
||||
App::PropertyLinkList Bodies;
|
||||
|
||||
/** @name methods override feature */
|
||||
//@{
|
||||
@@ -56,6 +55,7 @@ public:
|
||||
const char* getViewProviderName(void) const {
|
||||
return "PartDesignGui::ViewProviderBoolean";
|
||||
}
|
||||
virtual void onChanged(const App::Property* prop);
|
||||
//@}
|
||||
|
||||
private:
|
||||
|
||||
@@ -2136,13 +2136,13 @@ void CmdPartDesignBoolean::activated(int iMsg)
|
||||
PartDesign::Body *pcActiveBody = PartDesignGui::getBody(/*messageIfNot = */true);
|
||||
if (!pcActiveBody) return;
|
||||
|
||||
Gui::SelectionFilter BodyFilter("SELECT PartDesign::Body COUNT 1..");
|
||||
Gui::SelectionFilter BodyFilter("SELECT Part::Feature COUNT 1..");
|
||||
|
||||
openCommand("Create Boolean");
|
||||
std::string FeatName = getUniqueObjectName("Boolean");
|
||||
doCommand(Doc,"App.activeDocument().addObject('PartDesign::Boolean','%s')",FeatName.c_str());
|
||||
|
||||
if (BodyFilter.match()) {
|
||||
if (BodyFilter.match() && !BodyFilter.Result.empty()) {
|
||||
std::vector<App::DocumentObject*> bodies;
|
||||
std::vector<std::vector<Gui::SelectionObject> >::iterator i = BodyFilter.Result.begin();
|
||||
for (; i != BodyFilter.Result.end(); i++) {
|
||||
@@ -2152,7 +2152,7 @@ void CmdPartDesignBoolean::activated(int iMsg)
|
||||
}
|
||||
}
|
||||
std::string bodyString = PartDesignGui::buildLinkListPythonStr(bodies);
|
||||
doCommand(Doc,"App.activeDocument().%s.Bodies = %s",FeatName.c_str(),bodyString.c_str());
|
||||
doCommand(Doc,"App.activeDocument().%s.addObjects(%s)",FeatName.c_str(),bodyString.c_str());
|
||||
}
|
||||
|
||||
finishFeature(this, FeatName, nullptr, false);
|
||||
|
||||
@@ -71,7 +71,7 @@ TaskBooleanParameters::TaskBooleanParameters(ViewProviderBoolean *BooleanView,QW
|
||||
this->groupLayout()->addWidget(proxy);
|
||||
|
||||
PartDesign::Boolean* pcBoolean = static_cast<PartDesign::Boolean*>(BooleanView->getObject());
|
||||
std::vector<App::DocumentObject*> bodies = pcBoolean->Bodies.getValues();
|
||||
std::vector<App::DocumentObject*> bodies = pcBoolean->Group.getValues();
|
||||
for (std::vector<App::DocumentObject*>::const_iterator b = bodies.begin(); b != bodies.end(); b++)
|
||||
{
|
||||
ui->listWidgetBodies->insertItem(0, QString::fromLatin1((*b)->getNameInDocument()));
|
||||
@@ -108,12 +108,13 @@ void TaskBooleanParameters::onSelectionChanged(const Gui::SelectionChanges& msg)
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<App::DocumentObject*> bodies = pcBoolean->Bodies.getValues();
|
||||
std::vector<App::DocumentObject*> bodies = pcBoolean->Group.getValues();
|
||||
|
||||
if (selectionMode == bodyAdd) {
|
||||
if (std::find(bodies.begin(), bodies.end(), pcBody) == bodies.end()) {
|
||||
bodies.push_back(pcBody);
|
||||
pcBoolean->Bodies.setValues(bodies);
|
||||
pcBoolean->Group.setValues(std::vector<App::DocumentObject*>());
|
||||
pcBoolean->addObjects(bodies);
|
||||
ui->listWidgetBodies->insertItem(ui->listWidgetBodies->count(),
|
||||
QString::fromStdString(pcBody->getNameInDocument()));
|
||||
|
||||
@@ -145,7 +146,7 @@ void TaskBooleanParameters::onSelectionChanged(const Gui::SelectionChanges& msg)
|
||||
std::vector<App::DocumentObject*>::iterator b = std::find(bodies.begin(), bodies.end(), pcBody);
|
||||
if (b != bodies.end()) {
|
||||
bodies.erase(b);
|
||||
pcBoolean->Bodies.setValues(bodies);
|
||||
pcBoolean->setObjects(bodies);
|
||||
QList<QListWidgetItem*> items = ui->listWidgetBodies->findItems(QString::fromStdString(body), Qt::MatchExactly);
|
||||
if (!items.empty()) {
|
||||
for (QList<QListWidgetItem*>::const_iterator i = items.begin(); i != items.end(); i++) {
|
||||
@@ -180,7 +181,7 @@ void TaskBooleanParameters::onButtonBodyAdd(bool checked)
|
||||
PartDesign::Boolean* pcBoolean = static_cast<PartDesign::Boolean*>(BooleanView->getObject());
|
||||
Gui::Document* doc = BooleanView->getDocument();
|
||||
BooleanView->hide();
|
||||
if (pcBoolean->Bodies.getValues().empty() && pcBoolean->BaseFeature.getValue())
|
||||
if (pcBoolean->Group.getValues().empty() && pcBoolean->BaseFeature.getValue())
|
||||
doc->setHide(pcBoolean->BaseFeature.getValue()->getNameInDocument());
|
||||
selectionMode = bodyAdd;
|
||||
Gui::Selection().clearSelection();
|
||||
@@ -233,13 +234,13 @@ int TaskBooleanParameters::getType(void) const
|
||||
void TaskBooleanParameters::onBodyDeleted(void)
|
||||
{
|
||||
PartDesign::Boolean* pcBoolean = static_cast<PartDesign::Boolean*>(BooleanView->getObject());
|
||||
std::vector<App::DocumentObject*> bodies = pcBoolean->Bodies.getValues();
|
||||
std::vector<App::DocumentObject*> bodies = pcBoolean->Group.getValues();
|
||||
int index = ui->listWidgetBodies->currentRow();
|
||||
if (index < 0 && (size_t) index > bodies.size())
|
||||
return;
|
||||
App::DocumentObject* body = bodies[index];
|
||||
bodies.erase(bodies.begin() + ui->listWidgetBodies->currentRow());
|
||||
pcBoolean->Bodies.setValues(bodies);
|
||||
pcBoolean->setObjects(bodies);
|
||||
ui->listWidgetBodies->model()->removeRow(ui->listWidgetBodies->currentRow());
|
||||
pcBoolean->getDocument()->recomputeFeature(pcBoolean);
|
||||
|
||||
@@ -323,10 +324,10 @@ bool TaskDlgBooleanParameters::accept()
|
||||
try {
|
||||
std::vector<std::string> bodies = parameter->getBodies();
|
||||
std::stringstream str;
|
||||
str << "App.ActiveDocument." << name.c_str() << ".Bodies = [";
|
||||
str << "App.ActiveDocument." << name.c_str() << ".setObjects( [";
|
||||
for (std::vector<std::string>::const_iterator it = bodies.begin(); it != bodies.end(); ++it)
|
||||
str << "App.ActiveDocument." << *it << ",";
|
||||
str << "]";
|
||||
str << "])";
|
||||
Gui::Command::runCommand(Gui::Command::Doc,str.str().c_str());
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
@@ -350,7 +351,7 @@ bool TaskDlgBooleanParameters::reject()
|
||||
if (doc != NULL) {
|
||||
if (obj->BaseFeature.getValue() != NULL) {
|
||||
doc->setShow(obj->BaseFeature.getValue()->getNameInDocument());
|
||||
std::vector<App::DocumentObject*> bodies = obj->Bodies.getValues();
|
||||
std::vector<App::DocumentObject*> bodies = obj->Group.getValues();
|
||||
for (std::vector<App::DocumentObject*>::const_iterator b = bodies.begin(); b != bodies.end(); b++)
|
||||
doc->setShow((*b)->getNameInDocument());
|
||||
}
|
||||
|
||||
@@ -40,11 +40,12 @@
|
||||
|
||||
using namespace PartDesignGui;
|
||||
|
||||
PROPERTY_SOURCE(PartDesignGui::ViewProviderBoolean,PartDesignGui::ViewProvider)
|
||||
PROPERTY_SOURCE_WITH_EXTENSIONS(PartDesignGui::ViewProviderBoolean,PartDesignGui::ViewProvider)
|
||||
|
||||
ViewProviderBoolean::ViewProviderBoolean()
|
||||
{
|
||||
sPixmap = "PartDesign_Boolean.svg";
|
||||
initExtension(this);
|
||||
}
|
||||
|
||||
ViewProviderBoolean::~ViewProviderBoolean()
|
||||
@@ -101,17 +102,12 @@ bool ViewProviderBoolean::setEdit(int ModNum)
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<App::DocumentObject*> ViewProviderBoolean::claimChildren(void)const
|
||||
{
|
||||
return static_cast<PartDesign::Boolean*>(getObject())->Bodies.getValues();
|
||||
}
|
||||
|
||||
bool ViewProviderBoolean::onDelete(const std::vector<std::string> &s)
|
||||
{
|
||||
PartDesign::Boolean* pcBoolean = static_cast<PartDesign::Boolean*>(getObject());
|
||||
|
||||
// if abort command deleted the object the bodies are visible again
|
||||
std::vector<App::DocumentObject*> bodies = pcBoolean->Bodies.getValues();
|
||||
std::vector<App::DocumentObject*> bodies = pcBoolean->Group.getValues();
|
||||
for (std::vector<App::DocumentObject*>::const_iterator b = bodies.begin(); b != bodies.end(); b++) {
|
||||
if (*b && Gui::Application::Instance->getViewProvider(*b))
|
||||
Gui::Application::Instance->getViewProvider(*b)->show();
|
||||
|
||||
@@ -25,13 +25,15 @@
|
||||
#define PARTGUI_ViewProviderBoolean_H
|
||||
|
||||
#include "ViewProvider.h"
|
||||
#include <Gui/ViewProviderGeoFeatureGroupExtension.h>
|
||||
|
||||
|
||||
namespace PartDesignGui {
|
||||
|
||||
class PartDesignGuiExport ViewProviderBoolean : public ViewProvider
|
||||
class PartDesignGuiExport ViewProviderBoolean : public ViewProvider,
|
||||
public Gui::ViewProviderGeoFeatureGroupExtension
|
||||
{
|
||||
PROPERTY_HEADER(PartDesignGui::ViewProviderBoolean);
|
||||
PROPERTY_HEADER_WITH_EXTENSIONS(PartDesignGui::ViewProviderBoolean);
|
||||
|
||||
public:
|
||||
/// constructor
|
||||
@@ -41,7 +43,6 @@ public:
|
||||
|
||||
/// grouping handling
|
||||
void setupContextMenu(QMenu*, QObject*, const char*);
|
||||
std::vector<App::DocumentObject*> claimChildren(void)const;
|
||||
|
||||
virtual bool onDelete(const std::vector<std::string> &);
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ class TestBoolean(unittest.TestCase):
|
||||
self.BooleanFuse = self.Doc.addObject('PartDesign::Boolean','BooleanFuse')
|
||||
self.Body001.addObject(self.BooleanFuse)
|
||||
self.Doc.recompute()
|
||||
self.BooleanFuse.Bodies = [self.Body,]
|
||||
self.BooleanFuse.setObjects([self.Body,])
|
||||
self.BooleanFuse.Type = 0
|
||||
self.Doc.recompute()
|
||||
self.assertAlmostEqual(self.BooleanFuse.Shape.Volume, 1500)
|
||||
@@ -71,7 +71,7 @@ class TestBoolean(unittest.TestCase):
|
||||
self.BooleanCut = self.Doc.addObject('PartDesign::Boolean','BooleanCut')
|
||||
self.Body001.addObject(self.BooleanCut)
|
||||
self.Doc.recompute()
|
||||
self.BooleanCut.Bodies = [self.Body,]
|
||||
self.BooleanCut.setObjects([self.Body,])
|
||||
self.BooleanCut.Type = 1
|
||||
self.Doc.recompute()
|
||||
self.assertAlmostEqual(self.BooleanCut.Shape.Volume, 500)
|
||||
@@ -95,7 +95,7 @@ class TestBoolean(unittest.TestCase):
|
||||
self.BooleanCommon = self.Doc.addObject('PartDesign::Boolean','BooleanCommon')
|
||||
self.Body001.addObject(self.BooleanCommon)
|
||||
self.Doc.recompute()
|
||||
self.BooleanCommon.Bodies = [self.Body,]
|
||||
self.BooleanCommon.setObjects([self.Body,])
|
||||
self.BooleanCommon.Type = 2
|
||||
self.Doc.recompute()
|
||||
self.assertAlmostEqual(self.BooleanCommon.Shape.Volume, 500)
|
||||
|
||||
Reference in New Issue
Block a user