| |
|
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| |
|
| | #include <QMessageBox>
|
| | #include <QTreeWidget>
|
| | #include <TopExp_Explorer.hxx>
|
| | #include <TopoDS_Shape.hxx>
|
| |
|
| |
|
| | #include <Base/Exception.h>
|
| | #include <Base/Tools.h>
|
| | #include <App/Application.h>
|
| | #include <App/Document.h>
|
| | #include <App/DocumentObject.h>
|
| | #include <App/DocumentObjectGroup.h>
|
| | #include <Gui/Application.h>
|
| | #include <Gui/BitmapFactory.h>
|
| | #include <Gui/Command.h>
|
| | #include <Gui/Document.h>
|
| | #include <Gui/Selection/Selection.h>
|
| | #include <Gui/ViewProvider.h>
|
| | #include <Gui/WaitCursor.h>
|
| | #include <Mod/Part/App/PartFeature.h>
|
| |
|
| | #include "DlgBooleanOperation.h"
|
| | #include "ui_DlgBooleanOperation.h"
|
| |
|
| |
|
| | using namespace PartGui;
|
| | namespace sp = std::placeholders;
|
| |
|
| | namespace PartGui
|
| | {
|
| | class BooleanOperationItem: public QTreeWidgetItem
|
| | {
|
| | public:
|
| | explicit BooleanOperationItem(int type = Type)
|
| | : QTreeWidgetItem(type)
|
| | {}
|
| | void setData(int column, int role, const QVariant& value) override
|
| | {
|
| | QTreeWidgetItem::setData(column, role, value);
|
| | if (role == Qt::CheckStateRole && value.toBool()) {
|
| | QTreeWidget* tree = this->treeWidget();
|
| | if (!tree) {
|
| | return;
|
| | }
|
| | int numChild = tree->topLevelItemCount();
|
| | for (int i = 0; i < numChild; i++) {
|
| | QTreeWidgetItem* item = tree->topLevelItem(i);
|
| | for (int j = 0; j < item->childCount(); j++) {
|
| | QTreeWidgetItem* child = item->child(j);
|
| | if (child && child->checkState(column) & Qt::Checked) {
|
| | if (child != this) {
|
| | child->setCheckState(column, Qt::Unchecked);
|
| | }
|
| | }
|
| | }
|
| | }
|
| | }
|
| | }
|
| | };
|
| | }
|
| |
|
| |
|
| |
|
| | DlgBooleanOperation::DlgBooleanOperation(QWidget* parent)
|
| | : QWidget(parent)
|
| | , ui(new Ui_DlgBooleanOperation)
|
| | {
|
| | ui->setupUi(this);
|
| | connect(ui->swapButton, &QPushButton::clicked, this, &DlgBooleanOperation::onSwapButtonClicked);
|
| | connect(
|
| | ui->firstShape,
|
| | &QTreeWidget::currentItemChanged,
|
| | this,
|
| | &DlgBooleanOperation::currentItemChanged
|
| | );
|
| | connect(
|
| | ui->secondShape,
|
| | &QTreeWidget::currentItemChanged,
|
| | this,
|
| | &DlgBooleanOperation::currentItemChanged
|
| | );
|
| |
|
| | this->connectNewObject = App::GetApplication().signalNewObject.connect(
|
| | std::bind(&DlgBooleanOperation::slotCreatedObject, this, sp::_1)
|
| | );
|
| | this->connectModObject = App::GetApplication().signalChangedObject.connect(
|
| | std::bind(&DlgBooleanOperation::slotChangedObject, this, sp::_1, sp::_2)
|
| | );
|
| |
|
| | findShapes();
|
| | }
|
| |
|
| | |
| | |
| |
|
| | DlgBooleanOperation::~DlgBooleanOperation()
|
| | {
|
| |
|
| | this->connectNewObject.disconnect();
|
| | this->connectModObject.disconnect();
|
| | }
|
| |
|
| | void DlgBooleanOperation::changeEvent(QEvent* e)
|
| | {
|
| | if (e->type() == QEvent::LanguageChange) {
|
| | ui->retranslateUi(this);
|
| | }
|
| | QWidget::changeEvent(e);
|
| | }
|
| |
|
| | void DlgBooleanOperation::slotCreatedObject(const App::DocumentObject& obj)
|
| | {
|
| | App::Document* activeDoc = App::GetApplication().getActiveDocument();
|
| | if (!activeDoc) {
|
| | return;
|
| | }
|
| | App::Document* doc = obj.getDocument();
|
| | if (activeDoc == doc && obj.isDerivedFrom<Part::Feature>()) {
|
| | observe.push_back(&obj);
|
| | }
|
| | }
|
| |
|
| | void DlgBooleanOperation::slotChangedObject(const App::DocumentObject& obj, const App::Property& prop)
|
| | {
|
| | if (const auto it = std::ranges::find(observe, &obj);
|
| | it != observe.end() && prop.is<Part::PropertyPartShape>()) {
|
| | const TopoDS_Shape& shape = static_cast<const Part::PropertyPartShape&>(prop).getValue();
|
| | if (!shape.IsNull()) {
|
| | Gui::Document* activeGui = Gui::Application::Instance->getDocument(obj.getDocument());
|
| | QString label = QString::fromUtf8(obj.Label.getValue());
|
| | QString name = QString::fromLatin1(obj.getNameInDocument());
|
| |
|
| | QTreeWidgetItem* child = new BooleanOperationItem();
|
| | child->setCheckState(0, Qt::Unchecked);
|
| | child->setText(0, label);
|
| | child->setToolTip(0, label);
|
| | child->setData(0, Qt::UserRole, name);
|
| | Gui::ViewProvider* vp = activeGui->getViewProvider(&obj);
|
| | if (vp) {
|
| | child->setIcon(0, vp->getIcon());
|
| | }
|
| |
|
| | QTreeWidgetItem* copy = new BooleanOperationItem();
|
| | copy->setCheckState(0, Qt::Unchecked);
|
| | copy->setText(0, label);
|
| | copy->setToolTip(0, label);
|
| | copy->setData(0, Qt::UserRole, name);
|
| | if (vp) {
|
| | copy->setIcon(0, vp->getIcon());
|
| | }
|
| |
|
| | TopAbs_ShapeEnum type = shape.ShapeType();
|
| | if (type == TopAbs_SOLID) {
|
| | ui->firstShape->topLevelItem(0)->addChild(child);
|
| | ui->secondShape->topLevelItem(0)->addChild(copy);
|
| | ui->firstShape->topLevelItem(0)->setExpanded(true);
|
| | ui->secondShape->topLevelItem(0)->setExpanded(true);
|
| | }
|
| | else if (type == TopAbs_SHELL) {
|
| | ui->firstShape->topLevelItem(1)->addChild(child);
|
| | ui->secondShape->topLevelItem(1)->addChild(copy);
|
| | ui->firstShape->topLevelItem(1)->setExpanded(true);
|
| | ui->secondShape->topLevelItem(1)->setExpanded(true);
|
| | }
|
| | else if (type == TopAbs_COMPOUND || type == TopAbs_COMPSOLID) {
|
| | ui->firstShape->topLevelItem(2)->addChild(child);
|
| | ui->secondShape->topLevelItem(2)->addChild(copy);
|
| | ui->firstShape->topLevelItem(2)->setExpanded(true);
|
| | ui->secondShape->topLevelItem(2)->setExpanded(true);
|
| | }
|
| | else if (type == TopAbs_FACE) {
|
| | ui->firstShape->topLevelItem(3)->addChild(child);
|
| | ui->secondShape->topLevelItem(3)->addChild(copy);
|
| | ui->firstShape->topLevelItem(3)->setExpanded(true);
|
| | ui->secondShape->topLevelItem(3)->setExpanded(true);
|
| | }
|
| | else {
|
| | delete child;
|
| | child = nullptr;
|
| | delete copy;
|
| | copy = nullptr;
|
| | }
|
| |
|
| |
|
| | observe.erase(it);
|
| | }
|
| | }
|
| | }
|
| |
|
| | bool DlgBooleanOperation::hasSolids(const App::DocumentObject* obj) const
|
| | {
|
| | if (obj->isDerivedFrom<Part::Feature>()) {
|
| | const TopoDS_Shape& shape = static_cast<const Part::Feature*>(obj)->Shape.getValue();
|
| | TopExp_Explorer anExp(shape, TopAbs_SOLID);
|
| | if (anExp.More()) {
|
| | return true;
|
| | }
|
| | }
|
| |
|
| | return false;
|
| | }
|
| |
|
| | void DlgBooleanOperation::findShapes()
|
| | {
|
| | App::Document* activeDoc = App::GetApplication().getActiveDocument();
|
| | if (!activeDoc) {
|
| | return;
|
| | }
|
| | Gui::Document* activeGui = Gui::Application::Instance->getDocument(activeDoc);
|
| | if (!activeGui) {
|
| | return;
|
| | }
|
| |
|
| | std::vector<App::DocumentObject*> objs = activeDoc->getObjectsOfType(
|
| | Part::Feature::getClassTypeId()
|
| | );
|
| |
|
| | QTreeWidgetItem *item_left = nullptr, *item_right = nullptr;
|
| | for (auto obj : objs) {
|
| | const TopoDS_Shape& shape = static_cast<Part::Feature*>(obj)->Shape.getValue();
|
| | if (!shape.IsNull()) {
|
| | QString label = QString::fromUtf8(obj->Label.getValue());
|
| | QString name = QString::fromLatin1(obj->getNameInDocument());
|
| |
|
| | QTreeWidgetItem* child = new BooleanOperationItem();
|
| | child->setCheckState(0, Qt::Unchecked);
|
| | child->setText(0, label);
|
| | child->setToolTip(0, label);
|
| | child->setData(0, Qt::UserRole, name);
|
| | Gui::ViewProvider* vp = activeGui->getViewProvider(obj);
|
| | if (vp) {
|
| | child->setIcon(0, vp->getIcon());
|
| | }
|
| |
|
| | QTreeWidgetItem* copy = new BooleanOperationItem();
|
| | copy->setCheckState(0, Qt::Unchecked);
|
| | copy->setText(0, label);
|
| | copy->setToolTip(0, label);
|
| | copy->setData(0, Qt::UserRole, name);
|
| | if (vp) {
|
| | copy->setIcon(0, vp->getIcon());
|
| | }
|
| |
|
| | TopAbs_ShapeEnum type = shape.ShapeType();
|
| | if (type == TopAbs_SOLID) {
|
| | ui->firstShape->topLevelItem(0)->addChild(child);
|
| | ui->secondShape->topLevelItem(0)->addChild(copy);
|
| | }
|
| | else if (type == TopAbs_SHELL) {
|
| | ui->firstShape->topLevelItem(1)->addChild(child);
|
| | ui->secondShape->topLevelItem(1)->addChild(copy);
|
| | }
|
| | else if (type == TopAbs_COMPOUND || type == TopAbs_COMPSOLID) {
|
| | ui->firstShape->topLevelItem(2)->addChild(child);
|
| | ui->secondShape->topLevelItem(2)->addChild(copy);
|
| | }
|
| | else if (type == TopAbs_FACE) {
|
| | ui->firstShape->topLevelItem(3)->addChild(child);
|
| | ui->secondShape->topLevelItem(3)->addChild(copy);
|
| | }
|
| | else {
|
| | delete child;
|
| | child = nullptr;
|
| | delete copy;
|
| | copy = nullptr;
|
| | }
|
| |
|
| | if (!item_left || !item_right) {
|
| | bool selected = Gui::Selection().isSelected(obj);
|
| | if (!item_left && selected) {
|
| | item_left = child;
|
| | }
|
| | else if (!item_right && selected) {
|
| | item_right = copy;
|
| | }
|
| | }
|
| | }
|
| | }
|
| |
|
| | if (item_left) {
|
| | item_left->setCheckState(0, Qt::Checked);
|
| | ui->firstShape->setCurrentItem(item_left);
|
| | }
|
| | if (item_right) {
|
| | item_right->setCheckState(0, Qt::Checked);
|
| | ui->secondShape->setCurrentItem(item_right);
|
| | }
|
| | for (int i = 0; i < ui->firstShape->topLevelItemCount(); i++) {
|
| | QTreeWidgetItem* group = ui->firstShape->topLevelItem(i);
|
| | group->setFlags(Qt::ItemIsEnabled);
|
| | if (group->childCount() > 0) {
|
| | group->setExpanded(true);
|
| | }
|
| | }
|
| | for (int i = 0; i < ui->secondShape->topLevelItemCount(); i++) {
|
| | QTreeWidgetItem* group = ui->secondShape->topLevelItem(i);
|
| | group->setFlags(Qt::ItemIsEnabled);
|
| | if (group->childCount() > 0) {
|
| | group->setExpanded(true);
|
| | }
|
| | }
|
| | }
|
| |
|
| | bool DlgBooleanOperation::indexOfCurrentItem(QTreeWidgetItem* item, int& top_ind, int& child_ind) const
|
| | {
|
| | QTreeWidgetItem* parent = item->parent();
|
| | if (parent) {
|
| | top_ind = parent->treeWidget()->indexOfTopLevelItem(parent);
|
| | child_ind = parent->indexOfChild(item);
|
| | return true;
|
| | }
|
| |
|
| | return false;
|
| | }
|
| |
|
| | void DlgBooleanOperation::currentItemChanged(QTreeWidgetItem* current, QTreeWidgetItem* previous)
|
| | {
|
| | Q_UNUSED(current);
|
| | Q_UNUSED(previous);
|
| |
|
| |
|
| |
|
| |
|
| | }
|
| |
|
| | void DlgBooleanOperation::onSwapButtonClicked()
|
| | {
|
| | QTreeWidgetItem* lChild = ui->firstShape->currentItem();
|
| | bool lsel = (lChild && (lChild->checkState(0) & Qt::Checked));
|
| | QTreeWidgetItem* rChild = ui->secondShape->currentItem();
|
| | bool rsel = (rChild && (rChild->checkState(0) & Qt::Checked));
|
| |
|
| | if (rsel) {
|
| | int top_index, child_ind;
|
| | if (indexOfCurrentItem(rChild, top_index, child_ind)) {
|
| | QTreeWidgetItem* child = ui->firstShape->topLevelItem(top_index)->child(child_ind);
|
| | child->setCheckState(0, Qt::Checked);
|
| | ui->firstShape->setCurrentItem(child);
|
| | }
|
| | }
|
| | if (lsel) {
|
| | int top_index, child_ind;
|
| | if (indexOfCurrentItem(lChild, top_index, child_ind)) {
|
| | QTreeWidgetItem* child = ui->secondShape->topLevelItem(top_index)->child(child_ind);
|
| | child->setCheckState(0, Qt::Checked);
|
| | ui->secondShape->setCurrentItem(child);
|
| | }
|
| | }
|
| | }
|
| |
|
| | void DlgBooleanOperation::accept()
|
| | {
|
| | int ltop, lchild, rtop, rchild;
|
| |
|
| | QTreeWidgetItem* litem = nullptr;
|
| | int numLChild = ui->firstShape->topLevelItemCount();
|
| | for (int i = 0; i < numLChild; i++) {
|
| | QTreeWidgetItem* item = ui->firstShape->topLevelItem(i);
|
| | for (int j = 0; j < item->childCount(); j++) {
|
| | QTreeWidgetItem* child = item->child(j);
|
| | if (child && child->checkState(0) & Qt::Checked) {
|
| | litem = child;
|
| | break;
|
| | }
|
| | }
|
| |
|
| | if (litem) {
|
| | break;
|
| | }
|
| | }
|
| |
|
| | QTreeWidgetItem* ritem = nullptr;
|
| | int numRChild = ui->secondShape->topLevelItemCount();
|
| | for (int i = 0; i < numRChild; i++) {
|
| | QTreeWidgetItem* item = ui->secondShape->topLevelItem(i);
|
| | for (int j = 0; j < item->childCount(); j++) {
|
| | QTreeWidgetItem* child = item->child(j);
|
| | if (child && child->checkState(0) & Qt::Checked) {
|
| | ritem = child;
|
| | break;
|
| | }
|
| | }
|
| |
|
| | if (ritem) {
|
| | break;
|
| | }
|
| | }
|
| |
|
| | if (!litem || !indexOfCurrentItem(litem, ltop, lchild)) {
|
| | QMessageBox::critical(this, windowTitle(), tr("First, select a shape on the left side"));
|
| | return;
|
| | }
|
| | if (!ritem || !indexOfCurrentItem(ritem, rtop, rchild)) {
|
| | QMessageBox::critical(this, windowTitle(), tr("First, select a shape on the right side"));
|
| | return;
|
| | }
|
| | if (ltop == rtop && lchild == rchild) {
|
| | QMessageBox::critical(
|
| | this,
|
| | windowTitle(),
|
| | tr("Cannot perform a boolean operation with the same shape")
|
| | );
|
| | return;
|
| | }
|
| |
|
| | std::string shapeOne, shapeTwo;
|
| | shapeOne = (const char*)litem->data(0, Qt::UserRole).toByteArray();
|
| | shapeTwo = (const char*)ritem->data(0, Qt::UserRole).toByteArray();
|
| | App::Document* activeDoc = App::GetApplication().getActiveDocument();
|
| | if (!activeDoc) {
|
| | QMessageBox::critical(this, windowTitle(), tr("No active document available"));
|
| | return;
|
| | }
|
| |
|
| | std::string method;
|
| | App::DocumentObject* obj1 = activeDoc->getObject(shapeOne.c_str());
|
| | App::DocumentObject* obj2 = activeDoc->getObject(shapeTwo.c_str());
|
| | if (!obj1 || !obj2) {
|
| |
|
| | QMessageBox::critical(
|
| | this,
|
| | windowTitle(),
|
| | tr("One of the selected objects does not exist anymore")
|
| | );
|
| | return;
|
| | }
|
| |
|
| | if (ui->unionButton->isChecked()) {
|
| | if (!hasSolids(obj1) || !hasSolids(obj2)) {
|
| | QMessageBox::critical(
|
| | this,
|
| | windowTitle(),
|
| | tr("Performing union on non-solids is not possible")
|
| | );
|
| | return;
|
| | }
|
| | method = "make_fuse";
|
| | }
|
| | else if (ui->interButton->isChecked()) {
|
| | if (!hasSolids(obj1) || !hasSolids(obj2)) {
|
| | QMessageBox::critical(
|
| | this,
|
| | windowTitle(),
|
| | tr("Performing intersection on non-solids is not possible")
|
| | );
|
| | return;
|
| | }
|
| | method = "make_common";
|
| | }
|
| | else if (ui->diffButton->isChecked()) {
|
| | if (!hasSolids(obj1) || !hasSolids(obj2)) {
|
| | QMessageBox::critical(
|
| | this,
|
| | windowTitle(),
|
| | tr("Performing difference on non-solids is not possible")
|
| | );
|
| | return;
|
| | }
|
| | method = "make_cut";
|
| | }
|
| | else if (ui->sectionButton->isChecked()) {
|
| | method = "make_section";
|
| | }
|
| |
|
| | try {
|
| | Gui::WaitCursor wc;
|
| | activeDoc->openTransaction("Boolean operation");
|
| | std::vector<std::string> names;
|
| | names.push_back(Base::Tools::quoted(shapeOne.c_str()));
|
| | names.push_back(Base::Tools::quoted(shapeTwo.c_str()));
|
| | Gui::Command::doCommand(Gui::Command::Doc, "from BOPTools import BOPFeatures");
|
| | Gui::Command::doCommand(Gui::Command::Doc, "bp = BOPFeatures.BOPFeatures(App.activeDocument())");
|
| | Gui::Command::doCommand(
|
| | Gui::Command::Doc,
|
| | "bp.%s([%s])",
|
| | method.c_str(),
|
| | Base::Tools::joinList(names).c_str()
|
| | );
|
| | activeDoc->commitTransaction();
|
| | activeDoc->recompute();
|
| | }
|
| | catch (const Base::Exception& e) {
|
| | e.reportException();
|
| | }
|
| | }
|
| |
|
| |
|
| |
|
| | TaskBooleanOperation::TaskBooleanOperation()
|
| | {
|
| | widget = new DlgBooleanOperation();
|
| | addTaskBox(Gui::BitmapFactory().pixmap("Part_Booleans"), widget, false);
|
| | }
|
| |
|
| | void TaskBooleanOperation::clicked(int id)
|
| | {
|
| | if (id == QDialogButtonBox::Apply) {
|
| | widget->accept();
|
| | }
|
| | }
|
| |
|
| | #include "moc_DlgBooleanOperation.cpp"
|
| |
|