[Part]add scale command & feature

This commit is contained in:
wandererfan
2023-09-04 13:52:29 -04:00
committed by wwmayer
parent 2cbe037ce8
commit 90aef60f32
15 changed files with 1325 additions and 0 deletions

View File

@@ -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();

View File

@@ -189,6 +189,8 @@ SET(Features_SRCS
FeatureCompound.h
FeatureExtrusion.cpp
FeatureExtrusion.h
FeatureScale.cpp
FeatureScale.h
FeatureFace.cpp
FeatureFace.h
FeatureFillet.cpp

View File

@@ -0,0 +1,162 @@
/***************************************************************************
* Copyright (c) 2023 Wanderer Fan <wandererfan@gmail.com> *
* *
* 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 <BRepBuilderAPI_GTransform.hxx>
#include <BRepBuilderAPI_Transform.hxx>
#include <BRepBuilderAPI_Copy.hxx>
#include <gp_Pnt.hxx>
#include <gp_GTrsf.hxx>
#include <gp_Trsf.hxx>
#include <Precision.hxx>
#endif
#include <Base/Exception.h>
#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());
}
}

View File

@@ -0,0 +1,90 @@
/***************************************************************************
* Copyright (c) 2023 Wanderer Fan <wandererfan@gmail.com> *
* *
* 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 <App/PropertyStandard.h>
#include <App/PropertyUnits.h>
#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

View File

@@ -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();

View File

@@ -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

View File

@@ -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());

View File

@@ -0,0 +1,363 @@
/***************************************************************************
* Copyright (c) 2023 Wanderer Fan <wandererfan@gmail.com> *
* *
* 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 <BRepAdaptor_Curve.hxx>
# include <BRep_Tool.hxx>
# include <Precision.hxx>
# include <ShapeExtend_Explorer.hxx>
# include <TopExp_Explorer.hxx>
# include <TopoDS.hxx>
# include <TopTools_HSequenceOfShape.hxx>
# include <QKeyEvent>
# include <QMessageBox>
#include <QTreeWidget>
#include <QTreeWidgetItem>
#endif
#include <App/Application.h>
#include <App/Document.h>
#include <App/DocumentObject.h>
#include <App/Link.h>
#include <App/Part.h>
#include <Base/UnitsApi.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 "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<App::DocumentObject*> 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<App::DocumentObject*> objs = activeDoc->getObjectsOfType<App::DocumentObject>();
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<ParameterGrp> hGrp = App::GetApplication().GetUserParameter()
.GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod/Part");
bool addBaseName = hGrp->GetBool("AddBaseObjectName", false);
std::vector<App::DocumentObject*> 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<App::DocumentObject*> DlgScale::getShapesToScale() const
{
// Base::Console().Message("DS::getShapesToScale()\n");
QList<QTreeWidgetItem *> items = ui->treeWidget->selectedItems();
App::Document* doc = App::GetApplication().getDocument(m_document.c_str());
if (!doc)
throw Base::RuntimeError("Document lost");
std::vector<App::DocumentObject*> 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<QTreeWidgetItem *> items = ui->treeWidget->selectedItems();
App::Document* doc = App::GetApplication().getDocument(m_document.c_str());
if (!doc)
throw Base::RuntimeError("Document lost");
std::vector<App::DocumentObject*> 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"

View File

@@ -0,0 +1,94 @@
/***************************************************************************
* Copyright (c) 2023 Wanderer Fan <wandererfan@gmail.com> *
* *
* 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 <QDialog>
#include <string>
#include <Gui/TaskView/TaskDialog.h>
#include <Gui/TaskView/TaskView.h>
#include <Mod/Part/App/FeatureScale.h>
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<App::DocumentObject*> 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_DlgScale> 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

View File

@@ -0,0 +1,231 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PartGui::DlgScale</class>
<widget class="QDialog" name="PartGui::DlgScale">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>319</width>
<height>360</height>
</rect>
</property>
<property name="windowTitle">
<string>Scale</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QGridLayout" name="gridLayout_4">
<item row="3" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>X Factor</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Factor</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QDoubleSpinBox" name="dsbYScale">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="decimals">
<number>4</number>
</property>
<property name="minimum">
<double>0.000000000000000</double>
</property>
<property name="maximum">
<double>2147480000.000000000000000</double>
</property>
<property name="value">
<double>1.000000000000000</double>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Z Factor</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QRadioButton" name="rbUniform">
<property name="toolTip">
<string>Scale the object by a single factor in all directions.</string>
</property>
<property name="text">
<string>Uniform Scaling</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QDoubleSpinBox" name="dsbXScale">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="decimals">
<number>4</number>
</property>
<property name="minimum">
<double>0.000000000000000</double>
</property>
<property name="maximum">
<double>2147480000.000000000000000</double>
</property>
<property name="value">
<double>1.000000000000000</double>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Y Factor</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QRadioButton" name="rbNonUniform">
<property name="toolTip">
<string>Specify a different scale factor for each cardinal direction.</string>
</property>
<property name="text">
<string>Non-Uniform Scaling</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QDoubleSpinBox" name="dsbUniformScale">
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="decimals">
<number>4</number>
</property>
<property name="minimum">
<double>0.000000000000000</double>
</property>
<property name="maximum">
<double>2147480000.000000000000000</double>
</property>
<property name="value">
<double>1.000000000000000</double>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QDoubleSpinBox" name="dsbZScale">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="keyboardTracking">
<bool>false</bool>
</property>
<property name="decimals">
<number>4</number>
</property>
<property name="minimum">
<double>0.000000000000000</double>
</property>
<property name="maximum">
<double>2147480000.000000000000000</double>
</property>
<property name="value">
<double>1.000000000000000</double>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QTreeWidget" name="treeWidget">
<property name="toolTip">
<string>Select shape(s) that should be scaled</string>
</property>
<column>
<property name="text">
<string>Shape</string>
</property>
</column>
</widget>
</item>
<item>
<widget class="QLabel" name="statusLabel">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -65,6 +65,7 @@
<file>icons/tools/Part_ColorFace.svg</file>
<file>icons/tools/Part_Element_Copy.svg</file>
<file>icons/tools/Part_Extrude.svg</file>
<file>icons/tools/Part_Scale.svg</file>
<file>icons/tools/Part_Fillet.svg</file>
<file>icons/tools/Part_Loft.svg</file>
<file>icons/tools/Part_MakeFace.svg</file>

View File

@@ -0,0 +1,243 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="64px"
height="64px"
id="svg2766"
version="1.1">
<defs
id="defs2768">
<linearGradient
id="linearGradient3856">
<stop
style="stop-color:#3465a4;stop-opacity:1"
offset="0"
id="stop3858" />
<stop
style="stop-color:#729fcf;stop-opacity:1"
offset="1"
id="stop3860" />
</linearGradient>
<linearGradient
id="linearGradient3824">
<stop
style="stop-color:#729fcf;stop-opacity:1"
offset="0"
id="stop3826" />
<stop
style="stop-color:#204a87;stop-opacity:1"
offset="1"
id="stop3828" />
</linearGradient>
<linearGradient
id="linearGradient3787">
<stop
id="stop3789"
offset="0"
style="stop-color:#0619c0;stop-opacity:1;" />
<stop
id="stop3791"
offset="1"
style="stop-color:#379cfb;stop-opacity:1;" />
</linearGradient>
<linearGradient
id="linearGradient3864">
<stop
id="stop3866"
offset="0"
style="stop-color:#0619c0;stop-opacity:1;" />
<stop
id="stop3868"
offset="1"
style="stop-color:#379cfb;stop-opacity:1;" />
</linearGradient>
<linearGradient
gradientTransform="matrix(0,-1.4500001,1.4705882,0,-15.05882,91.45)"
y2="36.079998"
x2="21.689653"
y1="29.279999"
x1="56.172409"
gradientUnits="userSpaceOnUse"
id="linearGradient3036"
xlink:href="#linearGradient3895" />
<linearGradient
id="linearGradient3895">
<stop
style="stop-color:#729fcf;stop-opacity:1;"
offset="0"
id="stop3897" />
<stop
style="stop-color:#204a87;stop-opacity:1;"
offset="1"
id="stop3899" />
</linearGradient>
<linearGradient
y2="43.940212"
x2="32.329041"
y1="18.865765"
x1="44.058071"
gradientTransform="matrix(0.38366341,-0.38366341,0.41875298,0.41875298,12.196434,25.003375)"
gradientUnits="userSpaceOnUse"
id="linearGradient3012"
xlink:href="#linearGradient3895" />
<linearGradient
xlink:href="#linearGradient3824"
id="linearGradient3830"
x1="591.59064"
y1="103.1946"
x2="599.1272"
y2="128.1946"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(-0.9420628,1.000004)" />
<linearGradient
xlink:href="#linearGradient3856"
id="linearGradient3862"
x1="613.25824"
y1="135.1946"
x2="595.35895"
y2="68.194603"
gradientUnits="userSpaceOnUse" />
</defs>
<g
id="layer1">
<g
id="g3518"
transform="matrix(1.0614931,0,0,1,-612.96941,-69.194602)">
<path
id="rect3446"
d="m 580.28581,72.194602 3e-5,14 5.65241,0 0,-8 1.88414,0 0,-6 z m 11.30486,0 0,6 7.53655,0 0,-6 z m 11.30483,0 0,6 7.53655,0 0,-6 z m 11.30483,0 0,6 7.53655,0 0,-6 z m 11.30483,0 0,6 3.76828,0 0,4 5.65241,0 0,-10 z m 3.76828,14 0,8 5.65241,0 0,-8 z m -48.9876,4 0,8 5.65241,0 0,-8 z m 48.9876,8 0,7.999998 5.65241,0 0,-7.999998 z m 0,11.999998 0,8 5.65241,0 0,-8 z m 0,12 0,2 -3.76828,0 0,6 9.42069,0 0,-8 z m -18.84139,2 0,6 11.30483,0 0,-6 z"
style="fill:url(#linearGradient3862);fill-opacity:1;fill-rule:nonzero;stroke:#204a87;stroke-width:1.94120501999999990;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<path
style="fill:none;stroke:#0b1521;stroke-width:7.7648201;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"
d="m 583.11204,105.1946 0,22 20.72553,0 0,-22 z"
id="path3018" />
<path
style="fill:none;stroke:url(#linearGradient3830);stroke-width:3.88241005;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"
d="m 583.11204,105.1946 0,19.93618 0,2.06382 20.72553,0 0,-22 z"
id="path3018-1" />
<path
style="fill:none;stroke:#729fcf;stroke-width:1.94120502;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"
d="m 586.88032,126.1946 16.01518,0 0,-17 m 1.88413,-5 -22.60966,0 0,24"
id="path3018-1-7" />
<path
style="fill:none;stroke:#729fcf;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 36,57 10,0"
id="path3832"
transform="matrix(0.94206924,0,0,1,577.45963,69.194602)" />
<path
style="fill:none;stroke:#729fcf;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1"
d="m 52,57 5,0 0,-2 3,0"
id="path3834"
transform="matrix(0.94206924,0,0,1,577.45963,69.194602)" />
<path
style="fill:none;stroke:#729fcf;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 57,48 0,-6"
id="path3836"
transform="matrix(0.94206924,0,0,1,577.45963,69.194602)" />
<path
style="fill:none;stroke:#729fcf;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 57,36 0,-6"
id="path3838"
transform="matrix(0.94206924,0,0,1,577.45963,69.194602)" />
<path
style="fill:none;stroke:#729fcf;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 57,24 0,-6"
id="path3840"
transform="matrix(0.94206924,0,0,1,577.45963,69.194602)" />
<path
style="fill:none;stroke:#729fcf;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 53,8 0,-3 7,0"
id="path3842"
transform="matrix(0.94206924,0,0,1,577.45963,69.194602)" />
<path
style="fill:none;stroke:#729fcf;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 46,5 40,5"
id="path3844"
transform="matrix(0.94206924,0,0,1,577.45963,69.194602)" />
<path
style="fill:none;stroke:#729fcf;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 34,5 28,5"
id="path3846"
transform="matrix(0.94206924,0,0,1,577.45963,69.194602)" />
<path
style="fill:none;stroke:#729fcf;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 22,5 16,5"
id="path3848"
transform="matrix(0.94206924,0,0,1,577.45963,69.194602)" />
<path
style="fill:none;stroke:#729fcf;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 10,5 5,5 5,16"
id="path3850"
transform="matrix(0.94206924,0,0,1,577.45963,69.194602)" />
<path
style="fill:none;stroke:#729fcf;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 5,22 0,6"
id="path3852"
transform="matrix(0.94206924,0,0,1,577.45963,69.194602)" />
<path
style="fill:none;stroke:#729fcf;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 57,12 57,9"
id="path3854"
transform="matrix(0.94206924,0,0,1,577.45963,69.194602)" />
</g>
<path
style="fill:url(#linearGradient3012);fill-opacity:1;fill-rule:evenodd;stroke:#0b1521;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="M 35,15 39.062816,19.37832 31,27 l 6,6 7.71967,-7.964826 4.24264,4.242641 L 49,15 z"
id="path3343" />
<path
style="fill:none;stroke:#729fcf;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 39.550023,17 2.32515,2.37832 -7.987667,7.653821 3.080353,3.112494 7.751811,-7.937888 2.221642,2.240472 L 47.000002,17 z"
id="path3343-2" />
</g>
<metadata
id="metadata5370">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
<cc:license
rdf:resource="" />
<dc:date>Mon Oct 10 13:44:52 2011 +0000</dc:date>
<dc:creator>
<cc:Agent>
<dc:title>[wmayer]</dc:title>
</cc:Agent>
</dc:creator>
<dc:rights>
<cc:Agent>
<dc:title>FreeCAD LGPL2+</dc:title>
</cc:Agent>
</dc:rights>
<dc:publisher>
<cc:Agent>
<dc:title>FreeCAD</dc:title>
</cc:Agent>
</dc:publisher>
<dc:identifier>FreeCAD/src/Mod/Draft/Resources/icons/Draft_Scale.svg</dc:identifier>
<dc:relation>http://www.freecad.org/wiki/index.php?title=Artwork</dc:relation>
<dc:contributor>
<cc:Agent>
<dc:title>[agryson] Alexander Gryson</dc:title>
</cc:Agent>
</dc:contributor>
<dc:subject>
<rdf:Bag>
<rdf:li>square</rdf:li>
<rdf:li>arrow</rdf:li>
<rdf:li>dotted line</rdf:li>
<rdf:li />
</rdf:Bag>
</dc:subject>
<dc:description>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</dc:description>
</cc:Work>
</rdf:RDF>
</metadata>
</svg>

After

Width:  |  Height:  |  Size: 9.7 KiB

View File

@@ -0,0 +1,47 @@
/***************************************************************************
* Copyright (c) 2023 Wanderer Fan <wandererfan@gmail.com> *
* *
* 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 <Mod/Part/App/FeatureScale.h>
using namespace PartGui;
PROPERTY_SOURCE(PartGui::ViewProviderScale,PartGui::ViewProviderPart)
ViewProviderScale::ViewProviderScale()
{
sPixmap = "Part_Scale.svg";
}
ViewProviderScale::~ViewProviderScale() = default;
std::vector<App::DocumentObject*> ViewProviderScale::claimChildren()const
{
std::vector<App::DocumentObject*> temp;
temp.push_back(static_cast<Part::Scale*>(getObject())->Base.getValue());
return temp;
}

View File

@@ -0,0 +1,49 @@
/***************************************************************************
* Copyright (c) 2023 Wanderer Fan <wandererfan@gmail.com> *
* *
* 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<App::DocumentObject*> claimChildren() const override;
};
} // namespace PartGui
#endif // PARTGUI_VIEWPROVIDERSCALE_H

View File

@@ -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"