Files
create/src/Mod/Part/Gui/DlgRevolution.cpp
2016-08-21 18:46:40 +02:00

508 lines
17 KiB
C++

/***************************************************************************
* Copyright (c) 2009 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 <gp_Dir.hxx>
# include <gp_Lin.hxx>
# include <gp_Pnt.hxx>
# include <BRepAdaptor_Curve.hxx>
# include <TopExp_Explorer.hxx>
# include <TopoDS.hxx>
# include <TopoDS_Edge.hxx>
# include <Inventor/system/inttypes.h>
# include <Precision.hxx>
#endif
#include "ui_DlgRevolution.h"
#include "DlgRevolution.h"
#include "../App/PartFeature.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/Utilities.h>
#include <Gui/ViewProvider.h>
#include <Gui/WaitCursor.h>
#include <Mod/Part/App/Tools.h>
#include <Mod/Part/App/FeatureRevolution.h>
#include <Base/Console.h>
#include <Base/UnitsApi.h>
using namespace PartGui;
class DlgRevolution::EdgeSelection : public Gui::SelectionFilterGate
{
public:
bool canSelect;
EdgeSelection()
: Gui::SelectionFilterGate((Gui::SelectionFilter*)0)
{
canSelect = false;
}
bool allow(App::Document*pDoc, App::DocumentObject*pObj, const char*sSubName)
{
this->canSelect = false;
if (!pObj->isDerivedFrom(Part::Feature::getClassTypeId()))
return false;
if (!sSubName || sSubName[0] == '\0')
return false;
std::string element(sSubName);
if (element.substr(0,4) != "Edge")
return false;
Part::Feature* fea = static_cast<Part::Feature*>(pObj);
try {
TopoDS_Shape sub = fea->Shape.getShape().getSubShape(sSubName);
if (!sub.IsNull() && sub.ShapeType() == TopAbs_EDGE) {
const TopoDS_Edge& edge = TopoDS::Edge(sub);
BRepAdaptor_Curve adapt(edge);
if (adapt.GetType() == GeomAbs_Line || adapt.GetType() == GeomAbs_Circle) {
this->canSelect = true;
return true;
}
}
}
catch (...) {
}
return false;
}
};
DlgRevolution::DlgRevolution(QWidget* parent, Qt::WindowFlags fl)
: QDialog(parent, fl), filter(0)
{
ui = new Ui_DlgRevolution();
ui->setupUi(this);
ui->xPos->setRange(-DBL_MAX,DBL_MAX);
ui->yPos->setRange(-DBL_MAX,DBL_MAX);
ui->zPos->setRange(-DBL_MAX,DBL_MAX);
ui->xPos->setUnit(Base::Unit::Length);
ui->yPos->setUnit(Base::Unit::Length);
ui->zPos->setUnit(Base::Unit::Length);
ui->xDir->setRange(-DBL_MAX,DBL_MAX);
ui->yDir->setRange(-DBL_MAX,DBL_MAX);
ui->zDir->setRange(-DBL_MAX,DBL_MAX);
ui->xDir->setUnit(Base::Unit());
ui->yDir->setUnit(Base::Unit());
ui->zDir->setUnit(Base::Unit());
ui->zDir->setValue(1.0);
ui->angle->setUnit(Base::Unit::Angle);
ui->angle->setValue(360.0);
findShapes();
Gui::ItemViewSelection sel(ui->treeWidget);
sel.applyFrom(Gui::Selection().getObjectsOfType(Part::Feature::getClassTypeId()));
connect(ui->txtAxisLink, SIGNAL(textChanged(QString)), this, SLOT(on_txtAxisLink_textChanged(QString)));
}
/*
* Destroys the object and frees any allocated resources
*/
DlgRevolution::~DlgRevolution()
{
// no need to delete child widgets, Qt does it all for us
Gui::Selection().rmvSelectionGate();
delete ui;
}
Base::Vector3d DlgRevolution::getDirection() const
{
return Base::Vector3d(
ui->xDir->value().getValue(),
ui->yDir->value().getValue(),
ui->zDir->value().getValue());
}
Base::Vector3d DlgRevolution::getPosition() const
{
return Base::Vector3d(
ui->xPos->value().getValueAs(Base::Quantity::MilliMetre),
ui->yPos->value().getValueAs(Base::Quantity::MilliMetre),
ui->zPos->value().getValueAs(Base::Quantity::MilliMetre));
}
void DlgRevolution::getAxisLink(App::PropertyLinkSub &lnk) const
{
QString text = ui->txtAxisLink->text();
if (text.length() == 0) {
lnk.setValue(nullptr);
} else {
QStringList parts = text.split(QChar::fromLatin1(':'));
App::DocumentObject* obj = App::GetApplication().getActiveDocument()->getObject(parts[0].toLatin1());
if(!obj){
throw Base::ValueError(tr("Object not found: %1").arg(parts[0]).toUtf8().constData());
}
lnk.setValue(obj);
if (parts.size() == 1) {
return;
} else if (parts.size() == 2) {
std::vector<std::string> subs;
subs.push_back(std::string(parts[1].toLatin1().constData()));
lnk.setValue(obj,subs);
}
}
}
double DlgRevolution::getAngle() const
{
return ui->angle->value().getValueAs(Base::Quantity::Degree);
}
void DlgRevolution::setDirection(Base::Vector3d dir)
{
ui->xDir->setValue(dir.x);
ui->yDir->setValue(dir.y);
ui->zDir->setValue(dir.z);
}
void DlgRevolution::setPosition(Base::Vector3d pos)
{
ui->xPos->setValue(pos.x);
ui->yPos->setValue(pos.y);
ui->zPos->setValue(pos.z);
}
void DlgRevolution::setAxisLink(const App::PropertyLinkSub& lnk)
{
if (!lnk.getValue()){
ui->txtAxisLink->clear();
return;
}
if (lnk.getSubValues().size() == 1){
this->setAxisLink(lnk.getValue()->getNameInDocument(), lnk.getSubValues()[0].c_str());
} else {
this->setAxisLink(lnk.getValue()->getNameInDocument(), "");
}
}
void DlgRevolution::setAxisLink(const char* objname, const char* subname)
{
if(objname && strlen(objname) > 0){
QString txt = QString::fromLatin1(objname);
if (subname && strlen(subname) > 0){
txt = txt + QString::fromLatin1(":") + QString::fromLatin1(subname);
}
ui->txtAxisLink->setText(txt);
} else {
ui->txtAxisLink->clear();
}
}
bool DlgRevolution::validate()
{
//check source shapes
if (ui->treeWidget->selectedItems().isEmpty()) {
QMessageBox::critical(this, windowTitle(),
tr("Select a shape for revolution, first."));
return false;
}
//check axis link
bool axisLinkIsValid = false;
bool axisLinkHasAngle = false;
try{
App::PropertyLinkSub lnk;
this->getAxisLink(lnk);
double angle_edge = 1e100;
Base::Vector3d axis, center;
axisLinkIsValid = Part::Revolution::fetchAxisLink(lnk, center, axis, angle_edge);
axisLinkHasAngle = angle_edge != 1e100;
} catch(Base::Exception &err) {
QMessageBox::critical(this, windowTitle(),
tr("Revolution axis link is invalid.\n\n%1").arg(QString::fromUtf8(err.what())));
ui->txtAxisLink->setFocus();
return false;
} catch(Standard_Failure &err) {
QMessageBox::critical(this, windowTitle(),
tr("Revolution axis link is invalid.\n\n%1").arg(QString::fromLocal8Bit(err.GetMessageString())));
ui->txtAxisLink->setFocus();
return false;
} catch(...) {
QMessageBox::critical(this, windowTitle(),
tr("Revolution axis link is invalid.\n\n%1").arg(QString::fromUtf8("Unknown error")));
ui->txtAxisLink->setFocus();
return false;
}
//check axis dir
if (!axisLinkIsValid){
if(this->getDirection().Length() < Precision::Confusion()){
QMessageBox::critical(this, windowTitle(),
tr("Revolution axis direction is zero-length. It must be non-zero."));
ui->xDir->setFocus();
return false;
}
}
//check angle
if (!axisLinkHasAngle){
if (fabs(this->getAngle() / 180.0 * M_PI) < Precision::Angular()){
QMessageBox::critical(this, windowTitle(),
tr("Revolution angle span is zero. It must be non-zero."));
ui->angle->setFocus();
return false;
}
}
return true;
}
void DlgRevolution::changeEvent(QEvent *e)
{
if (e->type() == QEvent::LanguageChange) {
ui->retranslateUi(this);
}
else {
QDialog::changeEvent(e);
}
}
void DlgRevolution::findShapes()
{
App::Document* activeDoc = App::GetApplication().getActiveDocument();
if (!activeDoc) return;
Gui::Document* activeGui = Gui::Application::Instance->getDocument(activeDoc);
std::vector<App::DocumentObject*> objs = activeDoc->getObjectsOfType
(Part::Feature::getClassTypeId());
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()) continue;
TopExp_Explorer xp;
xp.Init(shape,TopAbs_SOLID);
if (xp.More()) continue; // solids not allowed
xp.Init(shape,TopAbs_COMPSOLID);
if (xp.More()) continue; // compound solids not allowed
// So allowed are: vertex, edge, wire, face, shell and compound
QTreeWidgetItem* item = new QTreeWidgetItem(ui->treeWidget);
item->setText(0, QString::fromUtf8((*it)->Label.getValue()));
item->setData(0, Qt::UserRole, QString::fromLatin1((*it)->getNameInDocument()));
Gui::ViewProvider* vp = activeGui->getViewProvider(*it);
if (vp) item->setIcon(0, vp->getIcon());
}
}
void DlgRevolution::accept()
{
if (!this->validate())
return;
Gui::WaitCursor wc;
App::Document* activeDoc = App::GetApplication().getActiveDocument();
activeDoc->openTransaction("Revolve");
try{
QString shape, type, name, solid;
QList<QTreeWidgetItem *> items = ui->treeWidget->selectedItems();
if (ui->checkSolid->isChecked()) {
solid = QString::fromLatin1("True");}
else {
solid = QString::fromLatin1("False");}
App::PropertyLinkSub axisLink;
this->getAxisLink(axisLink);
QString strAxisLink;
if (axisLink.getValue()){
strAxisLink = QString::fromLatin1("(App.ActiveDocument.%1, %2)")
.arg(QString::fromLatin1(axisLink.getValue()->getNameInDocument()))
.arg(axisLink.getSubValues().size() == 1 ?
QString::fromLatin1("\"%1\"").arg(QString::fromLatin1(axisLink.getSubValues()[0].c_str()))
: QString() );
} else {
strAxisLink = QString::fromLatin1("None");
}
QString symmetric;
if (ui->checkSymmetric->isChecked()) {
symmetric = QString::fromLatin1("True");}
else {
symmetric = QString::fromLatin1("False");}
for (QList<QTreeWidgetItem *>::iterator it = items.begin(); it != items.end(); ++it) {
shape = (*it)->data(0, Qt::UserRole).toString();
type = QString::fromLatin1("Part::Revolution");
name = QString::fromLatin1(activeDoc->getUniqueObjectName("Revolve").c_str());
Base::Vector3d axis = this->getDirection();
Base::Vector3d pos = this->getPosition();
QString code = QString::fromLatin1(
"FreeCAD.ActiveDocument.addObject(\"%1\",\"%2\")\n"
"FreeCAD.ActiveDocument.%2.Source = FreeCAD.ActiveDocument.%3\n"
"FreeCAD.ActiveDocument.%2.Axis = (%4,%5,%6)\n"
"FreeCAD.ActiveDocument.%2.Base = (%7,%8,%9)\n"
"FreeCAD.ActiveDocument.%2.Angle = %10\n"
"FreeCAD.ActiveDocument.%2.Solid = %11\n"
"FreeCAD.ActiveDocument.%2.AxisLink = %12\n"
"FreeCAD.ActiveDocument.%2.Symmetric = %13\n"
"FreeCADGui.ActiveDocument.%3.Visibility = False\n")
.arg(type).arg(name).arg(shape) //%1, 2, 3
.arg(axis.x,0,'f',15) //%4
.arg(axis.y,0,'f',15) //%5
.arg(axis.z,0,'f',15) //%6
.arg(pos.x, 0,'f',15) //%7
.arg(pos.y, 0,'f',15) //%8
.arg(pos.z, 0,'f',15) //%9
.arg(getAngle(),0,'f',15) //%10
.arg(solid) //%11
.arg(strAxisLink) //%12
.arg(symmetric) //13
;
Gui::Application::Instance->runPythonCode((const char*)code.toLatin1());
QByteArray to = name.toLatin1();
QByteArray from = shape.toLatin1();
Gui::Command::copyVisual(to, "ShapeColor", from);
Gui::Command::copyVisual(to, "LineColor", from);
Gui::Command::copyVisual(to, "PointColor", from);
}
activeDoc->commitTransaction();
activeDoc->recompute();
} catch (Base::Exception &err) {
QMessageBox::critical(this, windowTitle(),
tr("Creating Revolve failed.\n\n%1").arg(QString::fromUtf8(err.what())));
return;
} catch (...){
QMessageBox::critical(this, windowTitle(),
tr("Creating Revolve failed.\n\n%1").arg(QString::fromUtf8("Unknown error")));
return;
}
QDialog::accept();
}
void DlgRevolution::on_selectLine_clicked()
{
if (!filter) {
filter = new EdgeSelection();
Gui::Selection().addSelectionGate(filter);
ui->selectLine->setText(tr("Selecting... (line or arc)"));
} else {
Gui::Selection().rmvSelectionGate();
filter = nullptr;
ui->selectLine->setText(tr("Select reference"));
}
}
void DlgRevolution::on_btnX_clicked()
{
setDirection(Base::Vector3d(1,0,0));
if (!ui->xDir->isEnabled())
ui->txtAxisLink->clear();
}
void DlgRevolution::on_btnY_clicked()
{
setDirection(Base::Vector3d(0,1,0));
if (!ui->xDir->isEnabled())
ui->txtAxisLink->clear();
}
void DlgRevolution::on_btnZ_clicked()
{
setDirection(Base::Vector3d(0,0,1));
if (!ui->xDir->isEnabled())
ui->txtAxisLink->clear();
}
void DlgRevolution::on_txtAxisLink_textChanged(QString)
{
bool en = true;
try{
Base::Vector3d pos, dir;
double angle_edge = 1e100;
App::PropertyLinkSub lnk; this->getAxisLink(lnk);
bool fetched = Part::Revolution::fetchAxisLink(lnk, pos, dir, angle_edge);
if (fetched){
this->setDirection(dir);
this->setPosition(pos);
if (angle_edge != 1e100){
ui->angle->setValue(0.0);
} else if (fabs(ui->angle->value().getValue()) < 1e-12) {
ui->angle->setValue(360.0);
}
en = false;
}
} catch (Base::Exception &){
} catch (...){
}
ui->xDir->setEnabled(en);
ui->yDir->setEnabled(en);
ui->zDir->setEnabled(en);
ui->xPos->setEnabled(en);
ui->yPos->setEnabled(en);
ui->zPos->setEnabled(en);
}
void DlgRevolution::onSelectionChanged(const Gui::SelectionChanges& msg)
{
if (msg.Type == Gui::SelectionChanges::AddSelection) {
if (filter && filter->canSelect) {
this->setAxisLink(msg.pObjectName, msg.pSubName);
}
}
}
// ---------------------------------------
TaskRevolution::TaskRevolution()
{
widget = new DlgRevolution();
taskbox = new Gui::TaskView::TaskBox(
Gui::BitmapFactory().pixmap("Part_Revolve"),
widget->windowTitle(), true, 0);
taskbox->groupLayout()->addWidget(widget);
Content.push_back(taskbox);
}
TaskRevolution::~TaskRevolution()
{
// automatically deleted in the sub-class
}
bool TaskRevolution::accept()
{
widget->accept();
return (widget->result() == QDialog::Accepted);
}
#include "moc_DlgRevolution.cpp"