| |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| #include <QMessageBox> |
| #include <algorithm> |
|
|
|
|
| #include <App/ComplexGeoData.h> |
| #include <App/Document.h> |
| #include <App/Placement.h> |
| #include <Base/Converter.h> |
| #include <Base/CoordinateSystem.h> |
| #include <Gui/BitmapFactory.h> |
| #include <Gui/Command.h> |
| #include <Gui/Selection/Selection.h> |
| #include <Gui/WaitCursor.h> |
| #include <Mod/Mesh/App/Core/Approximation.h> |
|
|
| #include "FitBSplineSurface.h" |
| #include "ui_FitBSplineSurface.h" |
|
|
|
|
| using namespace ReenGui; |
|
|
| class FitBSplineSurfaceWidget::Private |
| { |
| public: |
| Ui_FitBSplineSurface ui; |
| App::DocumentObjectT obj; |
| Private() = default; |
| ~Private() = default; |
| }; |
|
|
| |
|
|
| FitBSplineSurfaceWidget::FitBSplineSurfaceWidget(const App::DocumentObjectT& obj, QWidget* parent) |
| : d(new Private()) |
| { |
| Q_UNUSED(parent); |
| d->ui.setupUi(this); |
| connect( |
| d->ui.makePlacement, |
| &QPushButton::clicked, |
| this, |
| &FitBSplineSurfaceWidget::onMakePlacementClicked |
| ); |
| d->obj = obj; |
| restoreSettings(); |
| } |
|
|
| FitBSplineSurfaceWidget::~FitBSplineSurfaceWidget() |
| { |
| saveSettings(); |
| delete d; |
| } |
|
|
| void FitBSplineSurfaceWidget::restoreSettings() |
| { |
| d->ui.degreeU->onRestore(); |
| d->ui.polesU->onRestore(); |
| d->ui.degreeV->onRestore(); |
| d->ui.polesV->onRestore(); |
| d->ui.iterations->onRestore(); |
| d->ui.sizeFactor->onRestore(); |
| d->ui.totalWeight->onRestore(); |
| d->ui.gradient->onRestore(); |
| d->ui.bending->onRestore(); |
| d->ui.curvature->onRestore(); |
| d->ui.uvdir->onRestore(); |
| } |
|
|
| void FitBSplineSurfaceWidget::saveSettings() |
| { |
| d->ui.degreeU->onSave(); |
| d->ui.polesU->onSave(); |
| d->ui.degreeV->onSave(); |
| d->ui.polesV->onSave(); |
| d->ui.iterations->onSave(); |
| d->ui.sizeFactor->onSave(); |
| d->ui.totalWeight->onSave(); |
| d->ui.gradient->onSave(); |
| d->ui.bending->onSave(); |
| d->ui.curvature->onSave(); |
| d->ui.uvdir->onSave(); |
| } |
|
|
| void FitBSplineSurfaceWidget::onMakePlacementClicked() |
| { |
| try { |
| App::GeoFeature* geo = d->obj.getObjectAs<App::GeoFeature>(); |
| if (geo) { |
| const App::PropertyComplexGeoData* geom = geo->getPropertyOfGeometry(); |
| if (geom) { |
| std::vector<Base::Vector3d> points, normals; |
| geom->getComplexData()->getPoints(points, normals, 0.001); |
|
|
| std::vector<Base::Vector3f> data; |
| std::transform( |
| points.begin(), |
| points.end(), |
| std::back_inserter(data), |
| [](const Base::Vector3d& v) { return Base::convertTo<Base::Vector3f>(v); } |
| ); |
| MeshCore::PlaneFit fit; |
| fit.AddPoints(data); |
| if (fit.Fit() < std::numeric_limits<float>::max()) { |
| Base::Vector3f base = fit.GetBase(); |
| Base::Vector3f dirU = fit.GetDirU(); |
| Base::Vector3f norm = fit.GetNormal(); |
|
|
| Base::CoordinateSystem cs; |
| cs.setPosition(Base::convertTo<Base::Vector3d>(base)); |
| cs.setAxes( |
| Base::convertTo<Base::Vector3d>(norm), |
| Base::convertTo<Base::Vector3d>(dirU) |
| ); |
| Base::Placement pm = Base::CoordinateSystem().displacement(cs); |
| double q0, q1, q2, q3; |
| pm.getRotation().getValue(q0, q1, q2, q3); |
|
|
| QString argument = QStringLiteral( |
| "Base.Placement(Base.Vector(%1, %2, " |
| "%3), Base.Rotation(%4, %5, %6, %7))" |
| ) |
| .arg(base.x) |
| .arg(base.y) |
| .arg(base.z) |
| .arg(q0) |
| .arg(q1) |
| .arg(q2) |
| .arg(q3); |
|
|
| QString document = QString::fromStdString(d->obj.getDocumentPython()); |
| QString command = QStringLiteral( |
| R"(%1.addObject("App::Placement", "Placement").Placement = %2)" |
| ) |
| .arg(document, argument); |
|
|
| Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Placement")); |
| Gui::Command::runCommand(Gui::Command::Doc, "from FreeCAD import Base"); |
| Gui::Command::runCommand(Gui::Command::Doc, command.toLatin1()); |
| Gui::Command::commitCommand(); |
| Gui::Command::updateActive(); |
| } |
| } |
| } |
| } |
| catch (const Base::Exception& e) { |
| Gui::Command::abortCommand(); |
| QMessageBox::warning(this, tr("Input error"), QString::fromLatin1(e.what())); |
| } |
| } |
|
|
| bool FitBSplineSurfaceWidget::accept() |
| { |
| try { |
| QString document = QString::fromStdString(d->obj.getDocumentPython()); |
| QString object = QString::fromStdString(d->obj.getObjectPython()); |
|
|
| QString argument = QStringLiteral( |
| "Points=getattr(%1, %1.getPropertyNameOfGeometry()), " |
| "UDegree=%2, VDegree=%3, " |
| "NbUPoles=%4, NbVPoles=%5, " |
| "Smooth=%6, " |
| "Weight=%7, " |
| "Grad=%8, " |
| "Bend=%9, " |
| "Curv=%10, " |
| "Iterations=%11, " |
| "PatchFactor=%12, " |
| "Correction=True" |
| ) |
| .arg(object) |
| .arg(d->ui.degreeU->value()) |
| .arg(d->ui.degreeV->value()) |
| .arg(d->ui.polesU->value()) |
| .arg(d->ui.polesV->value()) |
| .arg( |
| d->ui.groupBoxSmooth->isChecked() ? QLatin1String("True") |
| : QLatin1String("False") |
| ) |
| .arg(d->ui.totalWeight->value()) |
| .arg(d->ui.gradient->value()) |
| .arg(d->ui.bending->value()) |
| .arg(d->ui.curvature->value()) |
| .arg(d->ui.iterations->value()) |
| .arg(d->ui.sizeFactor->value()); |
| if (d->ui.uvdir->isChecked()) { |
| std::vector<App::Placement*> selection |
| = Gui::Selection().getObjectsOfType<App::Placement>(); |
| if (selection.size() != 1) { |
| QMessageBox::warning( |
| this, |
| tr("Wrong selection"), |
| tr("Select a single placement object to get the local orientation.") |
| ); |
| return false; |
| } |
|
|
| Base::Rotation rot = selection.front()->GeoFeature::Placement.getValue().getRotation(); |
| Base::Vector3d u(1, 0, 0); |
| Base::Vector3d v(0, 1, 0); |
| rot.multVec(u, u); |
| rot.multVec(v, v); |
| argument += QStringLiteral(", UVDirs=(FreeCAD.Vector(%1,%2,%3), FreeCAD.Vector(%4,%5,%6))") |
| .arg(u.x) |
| .arg(u.y) |
| .arg(u.z) |
| .arg(v.x) |
| .arg(v.y) |
| .arg(v.z); |
| } |
| QString command = QStringLiteral( |
| "%1.addObject(\"Part::Spline\", \"Spline\").Shape = " |
| "ReverseEngineering.approxSurface(%2).toShape()" |
| ) |
| .arg(document, argument); |
|
|
| Gui::WaitCursor wc; |
| Gui::Command::addModule(Gui::Command::App, "ReverseEngineering"); |
| Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Fit B-spline")); |
| Gui::Command::runCommand(Gui::Command::Doc, command.toLatin1()); |
| Gui::Command::commitCommand(); |
| Gui::Command::updateActive(); |
| } |
| catch (const Base::Exception& e) { |
| Gui::Command::abortCommand(); |
| QMessageBox::warning(this, tr("Input error"), QString::fromLatin1(e.what())); |
| return false; |
| } |
|
|
| return true; |
| } |
|
|
| void FitBSplineSurfaceWidget::changeEvent(QEvent* e) |
| { |
| QWidget::changeEvent(e); |
| if (e->type() == QEvent::LanguageChange) { |
| d->ui.retranslateUi(this); |
| } |
| } |
|
|
|
|
| |
|
|
| TaskFitBSplineSurface::TaskFitBSplineSurface(const App::DocumentObjectT& obj) |
| { |
| widget = new FitBSplineSurfaceWidget(obj); |
| addTaskBox(Gui::BitmapFactory().pixmap("actions/FitSurface"), widget); |
| } |
|
|
| void TaskFitBSplineSurface::open() |
| {} |
|
|
| bool TaskFitBSplineSurface::accept() |
| { |
| return widget->accept(); |
| } |
|
|
| #include "moc_FitBSplineSurface.cpp" |
|
|