diff --git a/src/Mod/Part/App/AppPart.cpp b/src/Mod/Part/App/AppPart.cpp index c4db9845ab..de78cddefe 100644 --- a/src/Mod/Part/App/AppPart.cpp +++ b/src/Mod/Part/App/AppPart.cpp @@ -66,6 +66,7 @@ #include "FeatureChamfer.h" #include "FeatureCompound.h" #include "FeatureExtrusion.h" +#include "FeatureScale.h" #include "FeatureFace.h" #include "FeatureFillet.h" #include "FeatureGeometrySet.h" @@ -440,6 +441,7 @@ PyMOD_INIT_FUNC(Part) Part::Compound ::init(); Part::Compound2 ::init(); Part::Extrusion ::init(); + Part::Scale ::init(); Part::Revolution ::init(); Part::Mirroring ::init(); Part::ImportStep ::init(); diff --git a/src/Mod/Part/App/CMakeLists.txt b/src/Mod/Part/App/CMakeLists.txt index 65dbd06f56..822bb388a0 100644 --- a/src/Mod/Part/App/CMakeLists.txt +++ b/src/Mod/Part/App/CMakeLists.txt @@ -189,6 +189,8 @@ SET(Features_SRCS FeatureCompound.h FeatureExtrusion.cpp FeatureExtrusion.h + FeatureScale.cpp + FeatureScale.h FeatureFace.cpp FeatureFace.h FeatureFillet.cpp diff --git a/src/Mod/Part/App/FeatureScale.cpp b/src/Mod/Part/App/FeatureScale.cpp new file mode 100644 index 0000000000..24ca43dfc4 --- /dev/null +++ b/src/Mod/Part/App/FeatureScale.cpp @@ -0,0 +1,162 @@ +/*************************************************************************** + * Copyright (c) 2023 Wanderer Fan * + * * + * 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 +#include +#include +#include +#include +#include +#include +#endif + +#include + +#include "FeatureScale.h" + +using namespace Part; + +PROPERTY_SOURCE(Part::Scale, Part::Feature) + +Scale::Scale() +{ + ADD_PROPERTY_TYPE(Base, (nullptr), "Scale", App::Prop_None, "Shape to scale"); + ADD_PROPERTY_TYPE(Uniform, (true), "Scale", App::Prop_None, "If true, scale equally in all directions"); + ADD_PROPERTY_TYPE(UniformScale, (1.0), "Scale", App::Prop_None, "Uniform scale factor - 1.0 means no scaling"); + ADD_PROPERTY_TYPE(XScale, (1.0), "Scale", App::Prop_None, "Scale factor in X direction - 1.0 means no scaling"); + ADD_PROPERTY_TYPE(YScale, (1.0), "Scale", App::Prop_None, "Scale factor in Y direction - 1.0 means no scaling"); + ADD_PROPERTY_TYPE(ZScale, (1.0), "Scale", App::Prop_None, "Scale factor in Z direction - 1.0 means no scaling"); +} + +short Scale::mustExecute() const +{ + if (Base.isTouched() || + Uniform.isTouched() || + UniformScale.isTouched() || + XScale.isTouched() || + YScale.isTouched() || + ZScale.isTouched()) { + return 1; + } + return 0; +} + +Scale::ScaleParameters Scale::computeFinalParameters() +{ + Scale::ScaleParameters result; + result.uniform = Uniform.getValue(); + result.uniformScale = UniformScale.getValue(); + result.XScale = XScale.getValue(); + result.YScale = YScale.getValue(); + result.ZScale = ZScale.getValue(); + + return result; +} + +TopoShape Scale::scaleShape(const TopoShape& source, const Scale::ScaleParameters& params) +{ + TopoShape result; + + if (params.uniform) { + result = uniformScale(source, params.uniformScale); + return result; + } + + return nonuniformScale(source, params); +} + +TopoShape Scale::uniformScale(const TopoShape& source, const double& factor) +{ +// Base::Console().Message("FS::uniformScale()\n"); + TopoDS_Shape transShape; + TopoShape transTopo; + try { + gp_Trsf scaleTransform; + scaleTransform.SetScale(gp_Pnt(0, 0, 0), factor); + + BRepBuilderAPI_Transform mkTrf(source.getShape(), scaleTransform); + transShape = mkTrf.Shape(); + } + catch (...) { + return transTopo; + } + transTopo.setShape(transShape); + return transTopo; +} + +TopoShape Scale::nonuniformScale(const TopoShape& source, const Scale::ScaleParameters& params) +{ +// Base::Console().Message("FS::nonuniformScale()\n"); + Base::Matrix4D matScale; + matScale.scale(params.XScale, params.YScale, params.ZScale); + + // copy the FreeCAD matrix cell values to an OCC matrix + gp_GTrsf mat; + mat.SetValue(1,1,matScale[0][0]); + mat.SetValue(2,1,matScale[1][0]); + mat.SetValue(3,1,matScale[2][0]); + mat.SetValue(1,2,matScale[0][1]); + mat.SetValue(2,2,matScale[1][1]); + mat.SetValue(3,2,matScale[2][1]); + mat.SetValue(1,3,matScale[0][2]); + mat.SetValue(2,3,matScale[1][2]); + mat.SetValue(3,3,matScale[2][2]); + mat.SetValue(1,4,matScale[0][3]); + mat.SetValue(2,4,matScale[1][3]); + mat.SetValue(3,4,matScale[2][3]); + + // this copy step seems to eliminate Part.OCCError: gp_GTrsf::Trsf() - non-orthogonal GTrsf + // which may to be related to the tesselation of the input shape. See Github issue #9651 + // for more detail. + BRepBuilderAPI_Copy copier(source.getShape()); + TopoShape transTopo; + try { + BRepBuilderAPI_GTransform mkTrf(copier.Shape(), mat, true); + transTopo.setShape(mkTrf.Shape()); + } + catch (...) { + Base::Console().Warning("FeatureScale failed on nonuniform scale\n"); + return transTopo; + } + return transTopo; +} + +App::DocumentObjectExecReturn* Scale::execute() +{ +// Base::Console().Message("FS::execute()\n"); + App::DocumentObject* link = Base.getValue(); + if (!link) + return new App::DocumentObjectExecReturn("No object linked"); + + try { + Scale::ScaleParameters params = computeFinalParameters(); + TopoShape result = scaleShape(Feature::getShape(link), params); + this->Shape.setValue(result); + return App::DocumentObject::StdReturn; + } + catch (Standard_Failure& e) { + return new App::DocumentObjectExecReturn(e.GetMessageString()); + } +} + diff --git a/src/Mod/Part/App/FeatureScale.h b/src/Mod/Part/App/FeatureScale.h new file mode 100644 index 0000000000..f0efb175e7 --- /dev/null +++ b/src/Mod/Part/App/FeatureScale.h @@ -0,0 +1,90 @@ +/*************************************************************************** + * Copyright (c) 2023 Wanderer Fan * + * * + * 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 * + * * + ***************************************************************************/ + +#ifndef PART_FEATURESCALE_H +#define PART_FEATURESCALE_H + +#include +#include + +#include "FaceMakerCheese.h" +#include "PartFeature.h" + + +namespace Part +{ + +class PartExport Scale : public Part::Feature +{ + PROPERTY_HEADER_WITH_OVERRIDE(Part::Scale); + +public: + Scale(); + + App::PropertyLink Base; + App::PropertyBool Uniform; + App::PropertyFloat UniformScale; + App::PropertyFloat XScale; + App::PropertyFloat YScale; + App::PropertyFloat ZScale; + + /** + * @brief The ScaleParameters struct is supposed to be filled with final + * scale parameters, after resolving links, applying mode logic, + * reversing, etc., and be passed to scaleShape. + */ + struct ScaleParameters { + bool uniform; + double uniformScale{1.0}; + double XScale{1.0}; + double YScale{1.0}; + double ZScale{1.0}; + }; + + /** @name methods override feature */ + //@{ + /// recalculate the feature + App::DocumentObjectExecReturn *execute() override; + short mustExecute() const override; + /// returns the type name of the view provider + const char* getViewProviderName() const override { + return "PartGui::ViewProviderScale"; + } + //@} + Scale::ScaleParameters computeFinalParameters(); + + /** + * @brief scaleShape powers the extrusion feature. + * @param source: the shape to be scaled + * @param params: scale parameters + * @return result of scaling + */ + static TopoShape scaleShape(const TopoShape& source, const ScaleParameters& params); + static TopoShape uniformScale(const TopoShape& source, const double& factor); + static TopoShape nonuniformScale(const TopoShape& source, const Scale::ScaleParameters& params); +}; + +} //namespace Part + + +#endif // PART_FEATURESCALE_H + diff --git a/src/Mod/Part/Gui/AppPartGui.cpp b/src/Mod/Part/Gui/AppPartGui.cpp index 6077cc884f..fbaef82d4b 100644 --- a/src/Mod/Part/Gui/AppPartGui.cpp +++ b/src/Mod/Part/Gui/AppPartGui.cpp @@ -61,6 +61,7 @@ #include "ViewProviderEllipseParametric.h" #include "ViewProviderExt.h" #include "ViewProviderExtrusion.h" +#include "ViewProviderScale.h" #include "ViewProviderHelixParametric.h" #include "ViewProviderPrimitive.h" #include "ViewProviderPython.h" @@ -171,6 +172,7 @@ PyMOD_INIT_FUNC(PartGui) PartGui::ViewProviderImport ::init(); PartGui::ViewProviderCurveNet ::init(); PartGui::ViewProviderExtrusion ::init(); + PartGui::ViewProviderScale ::init(); PartGui::ViewProvider2DObject ::init(); PartGui::ViewProvider2DObjectPython ::init(); PartGui::ViewProvider2DObjectGrid ::init(); diff --git a/src/Mod/Part/Gui/CMakeLists.txt b/src/Mod/Part/Gui/CMakeLists.txt index 9167357d17..75c3206929 100644 --- a/src/Mod/Part/Gui/CMakeLists.txt +++ b/src/Mod/Part/Gui/CMakeLists.txt @@ -49,6 +49,7 @@ set(PartGui_UIC_SRCS DlgExportStep.ui DlgExportHeaderStep.ui DlgExtrusion.ui + DlgScale.ui DlgImportStep.ui DlgFilletEdges.ui DlgImportExportIges.ui @@ -101,6 +102,9 @@ SET(PartGui_SRCS DlgExtrusion.cpp DlgExtrusion.h DlgExtrusion.ui + DlgScale.cpp + DlgScale.h + DlgScale.ui DlgFilletEdges.cpp DlgFilletEdges.h DlgFilletEdges.ui @@ -204,6 +208,8 @@ SET(PartGui_SRCS ViewProviderImport.h ViewProviderExtrusion.cpp ViewProviderExtrusion.h + ViewProviderScale.cpp + ViewProviderScale.h ViewProvider2DObject.cpp ViewProvider2DObject.h ViewProviderMirror.cpp diff --git a/src/Mod/Part/Gui/Command.cpp b/src/Mod/Part/Gui/Command.cpp index 983045064f..5773bac1b3 100644 --- a/src/Mod/Part/Gui/Command.cpp +++ b/src/Mod/Part/Gui/Command.cpp @@ -56,6 +56,7 @@ #include "CrossSections.h" #include "DlgBooleanOperation.h" #include "DlgExtrusion.h" +#include "DlgScale.h" #include "DlgFilletEdges.h" #include "DlgPrimitives.h" #include "DlgProjectionOnSurface.h" @@ -1294,6 +1295,35 @@ bool CmdPartExtrude::isActive() return (hasActiveDocument() && !Gui::Control().activeDialog()); } +//=========================================================================== +// Part_Scale +//=========================================================================== +DEF_STD_CMD_A(CmdPartScale) + +CmdPartScale::CmdPartScale() + :Command("Part_Scale") +{ + sAppModule = "Part"; + sGroup = QT_TR_NOOP("Part"); + sMenuText = QT_TR_NOOP("Scale..."); + sToolTipText = QT_TR_NOOP("Scale a selected shape"); + sWhatsThis = "Part_Scale"; + sStatusTip = sToolTipText; + sPixmap = "Part_Scale"; +} + +void CmdPartScale::activated(int iMsg) +{ + Q_UNUSED(iMsg); + + Gui::Control().showDialog(new PartGui::TaskScale()); +} + +bool CmdPartScale::isActive() +{ + return (hasActiveDocument() && !Gui::Control().activeDialog()); +} + //=========================================================================== // Part_MakeFace //=========================================================================== @@ -2425,6 +2455,7 @@ void CreatePartCommands() rcCmdMgr.addCommand(new CmdPartReverseShape()); rcCmdMgr.addCommand(new CmdPartBoolean()); rcCmdMgr.addCommand(new CmdPartExtrude()); + rcCmdMgr.addCommand(new CmdPartScale()); rcCmdMgr.addCommand(new CmdPartMakeFace()); rcCmdMgr.addCommand(new CmdPartMirror()); rcCmdMgr.addCommand(new CmdPartRevolve()); diff --git a/src/Mod/Part/Gui/DlgScale.cpp b/src/Mod/Part/Gui/DlgScale.cpp new file mode 100644 index 0000000000..6334b779eb --- /dev/null +++ b/src/Mod/Part/Gui/DlgScale.cpp @@ -0,0 +1,363 @@ +/*************************************************************************** + * Copyright (c) 2023 Wanderer Fan * + * * + * 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 +# include +# include +# include +# include +# include +# include +# include +# include +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ui_DlgScale.h" +#include "DlgScale.h" + + +FC_LOG_LEVEL_INIT("Part",true,true) + +using namespace PartGui; + +DlgScale::DlgScale(QWidget* parent, Qt::WindowFlags fl) + : QDialog(parent, fl), ui(new Ui_DlgScale) +{ + ui->setupUi(this); + setupConnections(); + + ui->dsbUniformScale->setDecimals(Base::UnitsApi::getDecimals()); + ui->dsbXScale->setDecimals(Base::UnitsApi::getDecimals()); + ui->dsbYScale->setDecimals(Base::UnitsApi::getDecimals()); + ui->dsbZScale->setDecimals(Base::UnitsApi::getDecimals()); + findShapes(); +} + +/* + * Destroys the object and frees any allocated resources + */ +DlgScale::~DlgScale() +{ + // no need to delete child widgets, Qt does it all for us +} + +void DlgScale::setupConnections() +{ + connect(ui->rbUniform, &QRadioButton::toggled, + this, &DlgScale::onUniformScaleToggled); +} + +void DlgScale::changeEvent(QEvent *e) +{ + if (e->type() == QEvent::LanguageChange) { + ui->retranslateUi(this); + } + QDialog::changeEvent(e); +} + +void DlgScale::onUniformScaleToggled(bool state) +{ +// Base::Console().Message("DS::onUniformScaleToggled()\n"); + if (state) { + // this is uniform scaling, so hide the non-uniform input fields + ui->dsbUniformScale->setEnabled(true); + ui->dsbXScale->setEnabled(false); + ui->dsbYScale->setEnabled(false); + ui->dsbZScale->setEnabled(false); + } else { + // this is non-uniform scaling, so hide the uniform input fields + ui->dsbUniformScale->setEnabled(false); + ui->dsbXScale->setEnabled(true); + ui->dsbYScale->setEnabled(true); + ui->dsbZScale->setEnabled(true); + } +} + +App::DocumentObject& DlgScale::getShapeToScale() const +{ +// Base::Console().Message("DS::getShapeToScale()\n"); + std::vector objs = this->getShapesToScale(); + if (objs.empty()) + throw Base::ValueError("No shapes selected"); + return *(objs[0]); +} + +void DlgScale::findShapes() +{ +// Base::Console().Message("DS::findShapes()\n"); + App::Document* activeDoc = App::GetApplication().getActiveDocument(); + if (!activeDoc) + return; + Gui::Document* activeGui = Gui::Application::Instance->getDocument(activeDoc); + m_document = activeDoc->getName(); + m_label = activeDoc->Label.getValue(); + + std::vector objs = activeDoc->getObjectsOfType(); + + for (auto obj : objs) { + Part::TopoShape topoShape = Part::Feature::getTopoShape(obj); + if (topoShape.isNull()) { + continue; + } + TopoDS_Shape shape = topoShape.getShape(); + if (shape.IsNull()) continue; + if (canScale(shape)) { + QTreeWidgetItem* item = new QTreeWidgetItem(ui->treeWidget); + item->setText(0, QString::fromUtf8(obj->Label.getValue())); + item->setData(0, Qt::UserRole, QString::fromLatin1(obj->getNameInDocument())); + Gui::ViewProvider* vp = activeGui->getViewProvider(obj); + if (vp) + item->setIcon(0, vp->getIcon()); + } + } +} + +bool DlgScale::canScale(const TopoDS_Shape& shape) const +{ + if (shape.IsNull()) { + return false; + } + // if the shape is a solid or a compound containing shapes, then we can scale it + TopAbs_ShapeEnum type = shape.ShapeType(); + + if (type == TopAbs_VERTEX) { + return false; + } + + if (type == TopAbs_COMPOUND || + type == TopAbs_COMPSOLID) { + TopExp_Explorer xp; + xp.Init(shape, TopAbs_SHAPE); + for ( ; xp.More() ; xp.Next()) { + // there is at least 1 sub shape inside the compound + if (!xp.Current().IsNull()) { + // found a non-null shape + return true; + } + } + // did not find a non-null shape + return false; + } else { + // not a Vertex, Compound or CompSolid, must be one of Edge, Wire, Face, Shell or + // Solid, all of which we can scale. + return true; + } + + return false; +} + +void DlgScale::accept() +{ +// Base::Console().Message("DS::accept()\n"); + try{ + apply(); + QDialog::accept(); + } catch (Base::AbortException&){ + Base::Console().Message("DS::accept - apply failed!\n"); + }; +} + +void DlgScale::apply() +{ +// Base::Console().Message("DS::apply()\n"); + try{ + if (!validate()) { + QMessageBox::critical(this, windowTitle(), + tr("No scalable shapes selected")); + return; + } + + Gui::WaitCursor wc; + App::Document* activeDoc = App::GetApplication().getDocument(m_document.c_str()); + if (!activeDoc) { + QMessageBox::critical(this, windowTitle(), + tr("The document '%1' doesn't exist.").arg(QString::fromUtf8(m_label.c_str()))); + return; + } + activeDoc->openTransaction("Scale"); + + Base::Reference hGrp = App::GetApplication().GetUserParameter() + .GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod/Part"); + bool addBaseName = hGrp->GetBool("AddBaseObjectName", false); + + std::vector objects = this->getShapesToScale(); + for (App::DocumentObject* sourceObj: objects) { + assert(sourceObj); + + if (Part::Feature::getTopoShape(sourceObj).isNull()){ + FC_ERR("Object " << sourceObj->getFullName() + << " is not Part object (has no OCC shape). Can't scale it."); + continue; + } + + std::string name; + name = sourceObj->getDocument()->getUniqueObjectName("Scale").c_str(); + if (addBaseName) { + //FIXME: implement + //QString baseName = QString::fromLatin1("Scale_%1").arg(sourceObjectName); + //label = QString::fromLatin1("%1_Scale").arg((*it)->text(0)); + } + + FCMD_OBJ_DOC_CMD(sourceObj,"addObject('Part::Scale','" << name << "')"); + auto newObj = sourceObj->getDocument()->getObject(name.c_str()); + + this->writeParametersToFeature(*newObj, sourceObj); + + Gui::Command::copyVisual(newObj, "ShapeColor", sourceObj); + Gui::Command::copyVisual(newObj, "LineColor", sourceObj); + Gui::Command::copyVisual(newObj, "PointColor", sourceObj); + + FCMD_OBJ_HIDE(sourceObj); + } + + activeDoc->commitTransaction(); + Gui::Command::updateActive(); + } + catch (Base::AbortException&){ + throw; + } + catch (Base::Exception &err){ + QMessageBox::critical(this, + windowTitle(), + tr("Creating Scale failed.\n%1") + .arg(QCoreApplication::translate("Exception", err.what()))); + return; + } + catch(...) { + QMessageBox::critical(this, windowTitle(), + tr("Creating Scale failed.\n%1").arg(QString::fromUtf8("Unknown error"))); + return; + } +} + +void DlgScale::reject() +{ + QDialog::reject(); +} + +std::vector DlgScale::getShapesToScale() const +{ +// Base::Console().Message("DS::getShapesToScale()\n"); + QList items = ui->treeWidget->selectedItems(); + App::Document* doc = App::GetApplication().getDocument(m_document.c_str()); + if (!doc) + throw Base::RuntimeError("Document lost"); + + std::vector objects; + for (auto item : items) { + App::DocumentObject* obj = doc->getObject(item->data(0, Qt::UserRole).toString().toLatin1()); + if (!obj) + throw Base::RuntimeError("Object not found"); + objects.push_back(obj); + } + return objects; +} + +bool DlgScale::validate() +{ + QList items = ui->treeWidget->selectedItems(); + App::Document* doc = App::GetApplication().getDocument(m_document.c_str()); + if (!doc) + throw Base::RuntimeError("Document lost"); + + std::vector objects; + for (auto item : items) { + App::DocumentObject* obj = doc->getObject(item->data(0, Qt::UserRole).toString().toLatin1()); + if (!obj) + throw Base::RuntimeError("Object not found"); + objects.push_back(obj); + } + return !objects.empty(); +} + +void DlgScale::writeParametersToFeature(App::DocumentObject &feature, App::DocumentObject* base) const +{ +// Base::Console().Message("DS::writeParametersToFeature()\n"); + Gui::Command::doCommand(Gui::Command::Doc,"f = App.getDocument('%s').getObject('%s')", feature.getDocument()->getName(), feature.getNameInDocument()); + + if (!base) { + return; + } + + Gui::Command::doCommand(Gui::Command::Doc,"f.Base = App.getDocument('%s').getObject('%s')", base->getDocument()->getName(), base->getNameInDocument()); + + Gui::Command::doCommand(Gui::Command::Doc,"f.Uniform = %s", ui->rbUniform->isChecked() ? "True" : "False"); + Gui::Command::doCommand(Gui::Command::Doc,"f.UniformScale = %.7f", ui->dsbUniformScale->value()); + Gui::Command::doCommand(Gui::Command::Doc,"f.XScale = %.7f", ui->dsbXScale->value()); + Gui::Command::doCommand(Gui::Command::Doc,"f.YScale = %.7f", ui->dsbYScale->value()); + Gui::Command::doCommand(Gui::Command::Doc,"f.ZScale = %.7f", ui->dsbZScale->value()); +} + +// --------------------------------------- + +TaskScale::TaskScale() +{ + widget = new DlgScale(); + taskbox = new Gui::TaskView::TaskBox( + Gui::BitmapFactory().pixmap("Part_Scale"), + widget->windowTitle(), true, nullptr); + taskbox->groupLayout()->addWidget(widget); + Content.push_back(taskbox); +} + +bool TaskScale::accept() +{ + widget->accept(); + return (widget->result() == QDialog::Accepted); +} + +bool TaskScale::reject() +{ + widget->reject(); + return true; +} + +void TaskScale::clicked(int id) +{ + if (id == QDialogButtonBox::Apply) { + try{ + widget->apply(); + } catch (Base::AbortException&){ + + }; + } +} + +#include "moc_DlgScale.cpp" diff --git a/src/Mod/Part/Gui/DlgScale.h b/src/Mod/Part/Gui/DlgScale.h new file mode 100644 index 0000000000..a8eb9132f2 --- /dev/null +++ b/src/Mod/Part/Gui/DlgScale.h @@ -0,0 +1,94 @@ +/*************************************************************************** + * Copyright (c) 2023 Wanderer Fan * + * * + * 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 * + * * + ***************************************************************************/ + +#ifndef PARTGUI_DLGSCALE_H +#define PARTGUI_DLGSCALE_H + +#include +#include + +#include +#include +#include + +class TopoDS_Shape; + +namespace PartGui { + +class Ui_DlgScale; +class DlgScale : public QDialog +{ + Q_OBJECT + +public: + DlgScale(QWidget* parent = nullptr, Qt::WindowFlags fl = Qt::WindowFlags()); + ~DlgScale() override; + void accept() override; + void apply(); + void reject() override; + + std::vector getShapesToScale() const; + + bool validate(); + + void writeParametersToFeature(App::DocumentObject& feature, App::DocumentObject* base) const; + +protected: + void findShapes(); + bool canScale(const TopoDS_Shape&) const; + void changeEvent(QEvent *e) override; + +private: + void setupConnections(); + void onUniformScaleToggled(bool on); + +private: + ///returns link to any of selected source shapes. Throws if nothing is selected for scaling. + App::DocumentObject& getShapeToScale() const; + + std::unique_ptr ui; + std::string m_document, m_label; +}; + +class TaskScale : public Gui::TaskView::TaskDialog +{ + Q_OBJECT + +public: + TaskScale(); + +public: + bool accept() override; + bool reject() override; + void clicked(int) override; + + QDialogButtonBox::StandardButtons getStandardButtons() const override + { return QDialogButtonBox::Ok | QDialogButtonBox::Apply | QDialogButtonBox::Close; } + +private: + DlgScale* widget; + Gui::TaskView::TaskBox* taskbox; +}; + +} // namespace PartGui + +#endif // PARTGUI_DLGSCALE_H diff --git a/src/Mod/Part/Gui/DlgScale.ui b/src/Mod/Part/Gui/DlgScale.ui new file mode 100644 index 0000000000..c2c0547c9f --- /dev/null +++ b/src/Mod/Part/Gui/DlgScale.ui @@ -0,0 +1,231 @@ + + + PartGui::DlgScale + + + + 0 + 0 + 319 + 360 + + + + Scale + + + + + + + + X Factor + + + + + + + Factor + + + + + + + false + + + + 0 + 0 + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 4 + + + 0.000000000000000 + + + 2147480000.000000000000000 + + + 1.000000000000000 + + + + + + + Z Factor + + + + + + + Scale the object by a single factor in all directions. + + + Uniform Scaling + + + true + + + + + + + false + + + + 0 + 0 + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 4 + + + 0.000000000000000 + + + 2147480000.000000000000000 + + + 1.000000000000000 + + + + + + + Y Factor + + + + + + + Specify a different scale factor for each cardinal direction. + + + Non-Uniform Scaling + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 4 + + + 0.000000000000000 + + + 2147480000.000000000000000 + + + 1.000000000000000 + + + + + + + false + + + + 0 + 0 + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + false + + + 4 + + + 0.000000000000000 + + + 2147480000.000000000000000 + + + 1.000000000000000 + + + + + + + + + + + + + + + + Qt::Horizontal + + + + + + + Select shape(s) that should be scaled + + + + Shape + + + + + + + + false + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + diff --git a/src/Mod/Part/Gui/Resources/Part.qrc b/src/Mod/Part/Gui/Resources/Part.qrc index 21ddc8ff45..8ac1e588c5 100644 --- a/src/Mod/Part/Gui/Resources/Part.qrc +++ b/src/Mod/Part/Gui/Resources/Part.qrc @@ -65,6 +65,7 @@ icons/tools/Part_ColorFace.svg icons/tools/Part_Element_Copy.svg icons/tools/Part_Extrude.svg + icons/tools/Part_Scale.svg icons/tools/Part_Fillet.svg icons/tools/Part_Loft.svg icons/tools/Part_MakeFace.svg diff --git a/src/Mod/Part/Gui/Resources/icons/tools/Part_Scale.svg b/src/Mod/Part/Gui/Resources/icons/tools/Part_Scale.svg new file mode 100644 index 0000000000..d0d4070403 --- /dev/null +++ b/src/Mod/Part/Gui/Resources/icons/tools/Part_Scale.svg @@ -0,0 +1,243 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + Mon Oct 10 13:44:52 2011 +0000 + + + [wmayer] + + + + + FreeCAD LGPL2+ + + + + + FreeCAD + + + FreeCAD/src/Mod/Draft/Resources/icons/Draft_Scale.svg + http://www.freecad.org/wiki/index.php?title=Artwork + + + [agryson] Alexander Gryson + + + + + square + arrow + dotted line + + + + A small square in the bottom left corner of a large dotted box with an arrow pointing from the top left corner of the inner box to the top left corner of the outer box + + + + diff --git a/src/Mod/Part/Gui/ViewProviderScale.cpp b/src/Mod/Part/Gui/ViewProviderScale.cpp new file mode 100644 index 0000000000..fe0bb05ebc --- /dev/null +++ b/src/Mod/Part/Gui/ViewProviderScale.cpp @@ -0,0 +1,47 @@ +/*************************************************************************** + * Copyright (c) 2023 Wanderer Fan * + * * + * 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" + +#include "ViewProviderScale.h" +#include + + +using namespace PartGui; + +PROPERTY_SOURCE(PartGui::ViewProviderScale,PartGui::ViewProviderPart) + +ViewProviderScale::ViewProviderScale() +{ + sPixmap = "Part_Scale.svg"; +} + +ViewProviderScale::~ViewProviderScale() = default; + +std::vector ViewProviderScale::claimChildren()const +{ + std::vector temp; + temp.push_back(static_cast(getObject())->Base.getValue()); + + return temp; +} diff --git a/src/Mod/Part/Gui/ViewProviderScale.h b/src/Mod/Part/Gui/ViewProviderScale.h new file mode 100644 index 0000000000..84261ab07b --- /dev/null +++ b/src/Mod/Part/Gui/ViewProviderScale.h @@ -0,0 +1,49 @@ +/*************************************************************************** + * Copyright (c) 2023 Wanderer Fan * + * * + * 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 * + * * + ***************************************************************************/ + + +#ifndef PARTGUI_VIEWPROVIDERSCALE_H +#define PARTGUI_VIEWPROVIDERSCALE_H + +#include "ViewProvider.h" + + +namespace PartGui { + +class PartGuiExport ViewProviderScale : public ViewProviderPart +{ + PROPERTY_HEADER_WITH_OVERRIDE(PartGui::ViewProviderScale); + +public: + /// constructor + ViewProviderScale(); + /// destructor + ~ViewProviderScale() override; + + /// grouping handling + std::vector claimChildren() const override; +}; + +} // namespace PartGui + + +#endif // PARTGUI_VIEWPROVIDERSCALE_H diff --git a/src/Mod/Part/Gui/Workbench.cpp b/src/Mod/Part/Gui/Workbench.cpp index 27abd8a302..7a314f44e4 100644 --- a/src/Mod/Part/Gui/Workbench.cpp +++ b/src/Mod/Part/Gui/Workbench.cpp @@ -122,6 +122,7 @@ Gui::MenuItem* Workbench::setupMenuBar() const << bop << join << split << compound << "Separator" << "Part_Extrude" + << "Part_Scale" << "Part_Revolve" << "Part_Mirror" << "Part_Fillet" @@ -181,6 +182,7 @@ Gui::ToolBarItem* Workbench::setupToolBars() const Gui::ToolBarItem* tool = new Gui::ToolBarItem(root); tool->setCommand("Part tools"); *tool << "Part_Extrude" + << "Part_Scale" << "Part_Revolve" << "Part_Mirror" << "Part_Fillet"