/*************************************************************************** * Copyright (c) 2013 Werner Mayer * * * * 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 "TaskSketcherValidation.h" #include "ui_TaskSketcherValidation.h" using namespace SketcherGui; using namespace Gui::TaskView; /* TRANSLATOR SketcherGui::SketcherValidation */ SketcherValidation::SketcherValidation(Sketcher::SketchObject* Obj, QWidget* parent) : QWidget(parent) , ui(new Ui_TaskSketcherValidation()) , sketch(Obj) , sketchAnalyser(Obj) , coincidenceRoot(nullptr) { ui->setupUi(this); setupConnections(); ui->fixButton->setEnabled(false); ui->fixConstraint->setEnabled(false); ui->fixDegenerated->setEnabled(false); ui->swapReversed->setEnabled(false); ui->checkBoxIgnoreConstruction->setEnabled(true); std::array tolerances = { // NOLINTBEGIN Precision::Confusion() / 100.0, Precision::Confusion() / 10.0, Precision::Confusion(), Precision::Confusion() * 10.0, Precision::Confusion() * 100.0, Precision::Confusion() * 1000.0, Precision::Confusion() * 10000.0, Precision::Confusion() * 100000.0 // NOLINTEND }; QLocale loc; for (double it : tolerances) { ui->comboBoxTolerance->addItem(loc.toString(it), QVariant(it)); } ui->comboBoxTolerance->setCurrentIndex(5); ui->comboBoxTolerance->setEditable(true); const double bottom = 0.0; const double top = 10.0; const int decimals = 10; ui->comboBoxTolerance->setValidator(new QDoubleValidator(bottom, top, decimals, this)); } SketcherValidation::~SketcherValidation() { hidePoints(); } void SketcherValidation::setupConnections() { // clang-format off connect(ui->findButton, &QPushButton::clicked, this, &SketcherValidation::onFindButtonClicked); connect(ui->fixButton, &QPushButton::clicked, this, &SketcherValidation::onFixButtonClicked); connect(ui->highlightButton, &QPushButton::clicked, this, &SketcherValidation::onHighlightButtonClicked); connect(ui->findConstraint, &QPushButton::clicked, this, &SketcherValidation::onFindConstraintClicked); connect(ui->fixConstraint, &QPushButton::clicked, this, &SketcherValidation::onFixConstraintClicked); connect(ui->findReversed, &QPushButton::clicked, this, &SketcherValidation::onFindReversedClicked); connect(ui->swapReversed, &QPushButton::clicked, this, &SketcherValidation::onSwapReversedClicked); connect(ui->orientLockEnable, &QPushButton::clicked, this, &SketcherValidation::onOrientLockEnableClicked); connect(ui->orientLockDisable, &QPushButton::clicked, this, &SketcherValidation::onOrientLockDisableClicked); connect(ui->delConstrExtr, &QPushButton::clicked, this, &SketcherValidation::onDelConstrExtrClicked); connect(ui->findDegenerated, &QPushButton::clicked, this, &SketcherValidation::onFindDegeneratedClicked); connect(ui->fixDegenerated, &QPushButton::clicked, this, &SketcherValidation::onFixDegeneratedClicked); // clang-format on } void SketcherValidation::changeEvent(QEvent* e) { if (e->type() == QEvent::LanguageChange) { ui->retranslateUi(this); } QWidget::changeEvent(e); } void SketcherValidation::onFindButtonClicked() { if (sketch.expired()) { return; } double prec = Precision::Confusion(); bool ok {}; double conv {}; conv = QLocale::system().toDouble(ui->comboBoxTolerance->currentText(), &ok); if (ok) { prec = conv; } else { QVariant v = ui->comboBoxTolerance->itemData(ui->comboBoxTolerance->currentIndex()); if (v.isValid()) { prec = v.toDouble(); } } sketchAnalyser.detectMissingPointOnPointConstraints( prec, !ui->checkBoxIgnoreConstruction->isChecked()); std::vector& vertexConstraints = sketchAnalyser.getMissingPointOnPointConstraints(); std::vector points; points.reserve(vertexConstraints.size()); for (auto vc : vertexConstraints) { points.push_back(vc.v); } hidePoints(); if (vertexConstraints.empty()) { Gui::TranslatedNotification(*sketch, tr("No missing coincidences"), tr("No missing coincidences found")); ui->fixButton->setEnabled(false); } else { showPoints(points); Gui::TranslatedUserWarning( *sketch, tr("Missing coincidences"), tr("%1 missing coincidences found").arg(vertexConstraints.size())); ui->fixButton->setEnabled(true); } } void SketcherValidation::onFixButtonClicked() { if (sketch.expired()) { return; } // undo command open App::Document* doc = sketch->getDocument(); doc->openTransaction("Add coincident constraint"); sketchAnalyser.makeMissingPointOnPointCoincident(); ui->fixButton->setEnabled(false); hidePoints(); // finish the transaction and update Gui::WaitCursor wc; doc->commitTransaction(); doc->recompute(); } void SketcherValidation::onHighlightButtonClicked() { if (sketch.expired()) { return; } std::vector points; points = sketchAnalyser.getOpenVertices(); hidePoints(); if (!points.empty()) { showPoints(points); } } void SketcherValidation::onFindConstraintClicked() { if (sketch.expired()) { return; } if (sketch->evaluateConstraints()) { Gui::TranslatedNotification(*sketch, tr("No invalid constraints"), tr("No invalid constraints found")); ui->fixConstraint->setEnabled(false); } else { Gui::TranslatedUserError(*sketch, tr("Invalid constraints"), tr("Invalid constraints found")); ui->fixConstraint->setEnabled(true); } } void SketcherValidation::onFixConstraintClicked() { if (sketch.expired()) { return; } sketch->validateConstraints(); ui->fixConstraint->setEnabled(false); } void SketcherValidation::onFindReversedClicked() { if (sketch.expired()) { return; } std::vector points; const std::vector& geom = sketch->getExternalGeometry(); for (const auto geo : geom) { // only arcs of circles need to be repaired. Arcs of ellipse were so broken there should be // nothing to repair from. if (const auto segm = dynamic_cast(geo)) { if (segm->isReversed()) { points.push_back(segm->getStartPoint(/*emulateCCWXY=*/true)); points.push_back(segm->getEndPoint(/*emulateCCWXY=*/true)); } } } hidePoints(); if (!points.empty()) { int nc = sketch->port_reversedExternalArcs(/*justAnalyze=*/true); showPoints(points); if (nc > 0) { Gui::TranslatedUserWarning( *sketch, tr("Reversed external geometry"), tr("%1 reversed external-geometry arcs were found. Their endpoints are" " encircled in 3d view.\n\n" "%2 constraints are linking to the endpoints. The constraints have" " been listed in Report view (menu View -> Panels -> Report view).\n\n" "Click \"Swap endpoints in constraints\" button to reassign endpoints." " Do this only once to sketches created in FreeCAD older than v0.15") .arg(points.size() / 2) .arg(nc)); ui->swapReversed->setEnabled(true); } else { Gui::TranslatedUserWarning( *sketch, tr("Reversed external geometry"), tr("%1 reversed external-geometry arcs were found. Their endpoints are " "encircled in 3d view.\n\n" "However, no constraints linking to the endpoints were found.") .arg(points.size() / 2)); ui->swapReversed->setEnabled(false); } } else { Gui::TranslatedNotification(*sketch, tr("Reversed external geometry"), tr("No reversed external-geometry arcs were found.")); } } void SketcherValidation::onSwapReversedClicked() { if (sketch.expired()) { return; } App::Document* doc = sketch->getDocument(); doc->openTransaction("Sketch porting"); int n = sketch->port_reversedExternalArcs(/*justAnalyze=*/false); Gui::TranslatedNotification( *sketch, tr("Reversed external geometry"), tr("%1 changes were made to constraints linking to endpoints of reversed arcs.").arg(n)); hidePoints(); ui->swapReversed->setEnabled(false); doc->commitTransaction(); } void SketcherValidation::onOrientLockEnableClicked() { if (sketch.expired()) { return; } App::Document* doc = sketch->getDocument(); doc->openTransaction("Constraint orientation lock"); int n = sketch->changeConstraintsLocking(/*bLock=*/true); Gui::TranslatedNotification( *sketch, tr("Constraint orientation locking"), tr("Orientation locking was enabled and recomputed for %1 constraints. The" " constraints have been listed in Report view (menu View -> Panels ->" " Report view).") .arg(n)); doc->commitTransaction(); } void SketcherValidation::onOrientLockDisableClicked() { if (sketch.expired()) { return; } App::Document* doc = sketch->getDocument(); doc->openTransaction("Constraint orientation unlock"); int n = sketch->changeConstraintsLocking(/*bLock=*/false); Gui::TranslatedNotification( *sketch, tr("Constraint orientation locking"), tr("Orientation locking was disabled for %1 constraints. The" " constraints have been listed in Report view (menu View -> Panels ->" " Report view). Note that for all future constraints, the locking still" " defaults to ON.") .arg(n)); doc->commitTransaction(); } void SketcherValidation::onDelConstrExtrClicked() { if (sketch.expired()) { return; } int reply = QMessageBox::question( this, tr("Delete constraints to external geom."), tr("You are about to delete ALL constraints that deal with external geometry. This is " "useful to rescue a sketch with broken/changed links to external geometry. Are you sure " "you want to delete the constraints?"), QMessageBox::No | QMessageBox::Yes, QMessageBox::No); if (reply != QMessageBox::Yes) { return; } App::Document* doc = sketch->getDocument(); doc->openTransaction("Delete constraints"); sketch->delConstraintsToExternal(); doc->commitTransaction(); Gui::TranslatedNotification( *sketch, tr("Delete constraints to external geom."), tr("All constraints that deal with external geometry were deleted.")); } void SketcherValidation::showPoints(const std::vector& pts) { auto coords = new SoCoordinate3(); auto drawStyle = new SoDrawStyle(); drawStyle->pointSize = 6; auto pcPoints = new SoPointSet(); coincidenceRoot = new SoGroup(); coincidenceRoot->addChild(drawStyle); auto pointsep = new SoSeparator(); auto basecol = new SoBaseColor(); basecol->rgb.setValue(1.0F, 0.5F, 0.0F); pointsep->addChild(basecol); pointsep->addChild(coords); pointsep->addChild(pcPoints); coincidenceRoot->addChild(pointsep); // Draw markers auto markcol = new SoBaseColor(); markcol->rgb.setValue(1.0F, 1.0F, 0.0F); auto marker = new SoMarkerSet(); long markerSize = App::GetApplication() .GetParameterGroupByPath("User parameter:BaseApp/Preferences/View") ->GetInt("MarkerSize", 9); marker->markerIndex = Gui::Inventor::MarkerBitmaps::getMarkerIndex("PLUS", int(markerSize)); pointsep->addChild(markcol); pointsep->addChild(marker); int pts_size = (int)pts.size(); coords->point.setNum(pts_size); SbVec3f* c = coords->point.startEditing(); for (int i = 0; i < pts_size; i++) { const Base::Vector3d& v = pts[i]; c[i].setValue((float)v.x, (float)v.y, (float)v.z); } coords->point.finishEditing(); if (!sketch.expired()) { Gui::ViewProvider* vp = Gui::Application::Instance->getViewProvider(sketch.get()); vp->getRoot()->addChild(coincidenceRoot); } } void SketcherValidation::hidePoints() { if (coincidenceRoot) { if (!sketch.expired()) { Gui::ViewProvider* vp = Gui::Application::Instance->getViewProvider(sketch.get()); vp->getRoot()->removeChild(coincidenceRoot); } coincidenceRoot = nullptr; } } void SketcherValidation::onFindDegeneratedClicked() { if (sketch.expired()) { return; } double prec = Precision::Confusion(); int count = sketchAnalyser.detectDegeneratedGeometries(prec); if (count == 0) { Gui::TranslatedNotification(*sketch, tr("No degenerated geometry"), tr("No degenerated geometry found")); ui->fixDegenerated->setEnabled(false); } else { Gui::TranslatedUserWarning(*sketch, tr("Degenerated geometry"), tr("%1 degenerated geometry found").arg(count)); ui->fixDegenerated->setEnabled(true); } } void SketcherValidation::onFixDegeneratedClicked() { if (sketch.expired()) { return; } // undo command open App::Document* doc = sketch->getDocument(); doc->openTransaction("Remove degenerated geometry"); double prec = Precision::Confusion(); sketchAnalyser.removeDegeneratedGeometries(prec); ui->fixButton->setEnabled(false); hidePoints(); // finish the transaction and update Gui::WaitCursor wc; doc->commitTransaction(); doc->recompute(); } // ----------------------------------------------- TaskSketcherValidation::TaskSketcherValidation(Sketcher::SketchObject* Obj) { QWidget* widget = new SketcherValidation(Obj); auto taskbox = new Gui::TaskView::TaskBox(QPixmap(), widget->windowTitle(), true, nullptr); taskbox->groupLayout()->addWidget(widget); Content.push_back(taskbox); } TaskSketcherValidation::~TaskSketcherValidation() = default; #include "moc_TaskSketcherValidation.cpp"