492 lines
19 KiB
C++
492 lines
19 KiB
C++
/***************************************************************************
|
|
* Copyright (c) 2007 Werner Mayer <wmayer[at]users.sourceforge.net> *
|
|
* *
|
|
* This file is part of the FreeCAD CAx development system. *
|
|
* *
|
|
* This library is free software; you can redistribute it and/or *
|
|
* modify it under the terms of the GNU Library General Public *
|
|
* License as published by the Free Software Foundation; either *
|
|
* version 2 of the License, or (at your option) any later version. *
|
|
* *
|
|
* This library is distributed in the hope that it will be useful, *
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
* GNU Library General Public License for more details. *
|
|
* *
|
|
* You should have received a copy of the GNU Library General Public *
|
|
* License along with this library; see the file COPYING.LIB. If not, *
|
|
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
|
* Suite 330, Boston, MA 02111-1307, USA *
|
|
* *
|
|
***************************************************************************/
|
|
|
|
|
|
#include "PreCompiled.h"
|
|
#ifndef _PreComp_
|
|
# include <QMessageBox>
|
|
# include <QTreeWidget>
|
|
# include <TopoDS_Shape.hxx>
|
|
# include <TopExp_Explorer.hxx>
|
|
# include <boost/bind.hpp>
|
|
#endif
|
|
|
|
#include "DlgBooleanOperation.h"
|
|
#include "ui_DlgBooleanOperation.h"
|
|
#include "../App/PartFeature.h"
|
|
#include <Base/Exception.h>
|
|
#include <App/Application.h>
|
|
#include <App/Document.h>
|
|
#include <App/DocumentObject.h>
|
|
#include <Gui/Application.h>
|
|
#include <Gui/BitmapFactory.h>
|
|
#include <Gui/Command.h>
|
|
#include <Gui/Document.h>
|
|
#include <Gui/Selection.h>
|
|
#include <Gui/ViewProvider.h>
|
|
#include <Gui/WaitCursor.h>
|
|
|
|
using namespace PartGui;
|
|
|
|
namespace PartGui {
|
|
class BooleanOperationItem : public QTreeWidgetItem
|
|
{
|
|
public:
|
|
BooleanOperationItem(int type = Type)
|
|
: QTreeWidgetItem(type)
|
|
{
|
|
}
|
|
void setData (int column, int role, const QVariant & value)
|
|
{
|
|
QTreeWidgetItem::setData(column, role, value);
|
|
if (role == Qt::CheckStateRole && value.toBool() == true) {
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
/* TRANSLATOR PartGui::DlgBooleanOperation */
|
|
|
|
DlgBooleanOperation::DlgBooleanOperation(QWidget* parent)
|
|
: QWidget(parent), ui(new Ui_DlgBooleanOperation)
|
|
{
|
|
ui->setupUi(this);
|
|
connect(ui->firstShape, SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)),
|
|
this, SLOT(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)));
|
|
connect(ui->secondShape, SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)),
|
|
this, SLOT(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)));
|
|
this->connectNewObject = App::GetApplication().signalNewObject.connect(boost::bind
|
|
(&DlgBooleanOperation::slotCreatedObject, this, _1));
|
|
this->connectModObject = App::GetApplication().signalChangedObject.connect(boost::bind
|
|
(&DlgBooleanOperation::slotChangedObject, this, _1, _2));
|
|
findShapes();
|
|
}
|
|
|
|
/*
|
|
* Destroys the object and frees any allocated resources
|
|
*/
|
|
DlgBooleanOperation::~DlgBooleanOperation()
|
|
{
|
|
// no need to delete child widgets, Qt does it all for us
|
|
delete ui;
|
|
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.getTypeId().isDerivedFrom(Part::Feature::getClassTypeId())) {
|
|
observe.push_back(&obj);
|
|
}
|
|
}
|
|
|
|
void DlgBooleanOperation::slotChangedObject(const App::DocumentObject& obj,
|
|
const App::Property& prop)
|
|
{
|
|
std::list<const App::DocumentObject*>::iterator it;
|
|
it = std::find(observe.begin(), observe.end(), &obj);
|
|
if (it != observe.end() && prop.getTypeId() == Part::PropertyPartShape::getClassTypeId()) {
|
|
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::fromAscii(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 { // belongs to none of these groups
|
|
delete child; child = 0;
|
|
delete copy; copy = 0;
|
|
}
|
|
|
|
// remove the watched object because now it is added to the tree
|
|
observe.erase(it);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool DlgBooleanOperation::hasSolids(const App::DocumentObject* obj) const
|
|
{
|
|
if (obj->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId())) {
|
|
const TopoDS_Shape& shape = static_cast<const Part::Feature*>(obj)->Shape.getValue();
|
|
TopExp_Explorer anExp (shape, TopAbs_SOLID);
|
|
for (; anExp.More(); anExp.Next()) {
|
|
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=0, *item_right=0;
|
|
for (std::vector<App::DocumentObject*>::iterator it = objs.begin(); it!=objs.end(); ++it) {
|
|
const TopoDS_Shape& shape = static_cast<Part::Feature*>(*it)->Shape.getValue();
|
|
if (!shape.IsNull()) {
|
|
QString label = QString::fromUtf8((*it)->Label.getValue());
|
|
QString name = QString::fromAscii((*it)->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(*it);
|
|
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 { // belongs to none of these groups
|
|
delete child; child = 0;
|
|
delete copy; copy = 0;
|
|
}
|
|
|
|
if (!item_left || !item_right) {
|
|
bool selected = Gui::Selection().isSelected(*it);
|
|
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)
|
|
{
|
|
// if (current && current->flags() & Qt::ItemIsUserCheckable)
|
|
// current->setCheckState(0, Qt::Checked);
|
|
//if (previous && previous->flags() & Qt::ItemIsUserCheckable)
|
|
// previous->setCheckState(0, Qt::Unchecked);
|
|
}
|
|
|
|
void DlgBooleanOperation::on_swapButton_clicked()
|
|
{
|
|
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 = 0;
|
|
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 = 0;
|
|
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("Select a shape on the left side, first"));
|
|
return;
|
|
}
|
|
if (!ritem || !indexOfCurrentItem(ritem,rtop,rchild)) {
|
|
QMessageBox::critical(this, windowTitle(),
|
|
tr("Select a shape on the right side, first"));
|
|
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 type, objName;
|
|
App::DocumentObject* obj1 = activeDoc->getObject(shapeOne.c_str());
|
|
App::DocumentObject* obj2 = activeDoc->getObject(shapeTwo.c_str());
|
|
if (!obj1 || !obj2) {
|
|
// objects don't exists (anymore)
|
|
QMessageBox::critical(this, windowTitle(),
|
|
tr("One of the selected objects doesn't 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;
|
|
}
|
|
type = "Part::Fuse";
|
|
objName = activeDoc->getUniqueObjectName("Fusion");
|
|
}
|
|
else if (ui->interButton->isChecked()) {
|
|
if (!hasSolids(obj1) || !hasSolids(obj2)) {
|
|
QMessageBox::critical(this, windowTitle(),
|
|
tr("Performing intersection on non-solids is not possible"));
|
|
return;
|
|
}
|
|
type = "Part::Common";
|
|
objName = activeDoc->getUniqueObjectName("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;
|
|
}
|
|
type = "Part::Cut";
|
|
objName = activeDoc->getUniqueObjectName("Cut");
|
|
}
|
|
else if (ui->sectionButton->isChecked()) {
|
|
type = "Part::Section";
|
|
objName = activeDoc->getUniqueObjectName("Section");
|
|
}
|
|
|
|
try {
|
|
Gui::WaitCursor wc;
|
|
activeDoc->openTransaction("Boolean operation");
|
|
Gui::Command::doCommand(Gui::Command::Doc,
|
|
"App.activeDocument().addObject(\"%s\",\"%s\")",
|
|
type.c_str(), objName.c_str());
|
|
Gui::Command::doCommand(Gui::Command::Doc,
|
|
"App.activeDocument().%s.Base = App.activeDocument().%s",
|
|
objName.c_str(),shapeOne.c_str());
|
|
Gui::Command::doCommand(Gui::Command::Doc,
|
|
"App.activeDocument().%s.Tool = App.activeDocument().%s",
|
|
objName.c_str(),shapeTwo.c_str());
|
|
Gui::Command::doCommand(Gui::Command::Gui,
|
|
"Gui.activeDocument().hide(\"%s\")",shapeOne.c_str());
|
|
Gui::Command::doCommand(Gui::Command::Gui,
|
|
"Gui.activeDocument().hide(\"%s\")",shapeTwo.c_str());
|
|
Gui::Command::copyVisual(objName.c_str(), "ShapeColor", shapeOne.c_str());
|
|
Gui::Command::copyVisual(objName.c_str(), "DisplayMode", shapeOne.c_str());
|
|
activeDoc->commitTransaction();
|
|
activeDoc->recompute();
|
|
}
|
|
catch (const Base::Exception& e) {
|
|
e.ReportException();
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------
|
|
|
|
TaskBooleanOperation::TaskBooleanOperation()
|
|
{
|
|
widget = new DlgBooleanOperation();
|
|
taskbox = new Gui::TaskView::TaskBox(
|
|
Gui::BitmapFactory().pixmap("Part_Booleans"),
|
|
widget->windowTitle(), false, 0);
|
|
taskbox->groupLayout()->addWidget(widget);
|
|
Content.push_back(taskbox);
|
|
}
|
|
|
|
TaskBooleanOperation::~TaskBooleanOperation()
|
|
{
|
|
// automatically deleted in the sub-class
|
|
}
|
|
|
|
void TaskBooleanOperation::clicked(int id)
|
|
{
|
|
if (id == QDialogButtonBox::Apply) {
|
|
widget->accept();
|
|
}
|
|
}
|
|
|
|
#include "moc_DlgBooleanOperation.cpp"
|