23
#include "PreCompiled.h"
28
#include <QDoubleSpinBox>
34
#include <App/Application.h>
35
#include <App/Document.h>
36
#include <App/DocumentObjectGroup.h>
37
#include <Gui/Command.h>
38
#include <Gui/SelectionObject.h>
39
#include <Mod/Mesh/App/Core/Approximation.h>
40
#include <Mod/Mesh/App/Core/Segmentation.h>
41
#include <Mod/Mesh/App/MeshFeature.h>
43
#include "SegmentationBestFit.h"
44
#include "ui_SegmentationBestFit.h"
47
using namespace MeshGui;
51
class PlaneFitParameter: public FitParameter
54
PlaneFitParameter() = default;
55
std::vector<float> getParameter(FitParameter::Points pts) const override
57
std::vector<float> values;
58
MeshCore::PlaneFit fit;
59
fit.AddPoints(pts.points);
60
if (fit.Fit() < FLOAT_MAX) {
61
Base::Vector3f base = fit.GetBase();
62
Base::Vector3f axis = fit.GetNormal();
63
values.push_back(base.x);
64
values.push_back(base.y);
65
values.push_back(base.z);
66
values.push_back(axis.x);
67
values.push_back(axis.y);
68
values.push_back(axis.z);
74
class CylinderFitParameter: public FitParameter
77
CylinderFitParameter() = default;
78
std::vector<float> getParameter(FitParameter::Points pts) const override
80
std::vector<float> values;
81
MeshCore::CylinderFit fit;
82
fit.AddPoints(pts.points);
83
if (!pts.normals.empty()) {
84
Base::Vector3f base = fit.GetGravity();
85
Base::Vector3f axis = fit.GetInitialAxisFromNormals(pts.normals);
86
fit.SetInitialValues(base, axis);
89
Base::Console().Message("Initial axis: (%f, %f, %f)\n", axis.x, axis.y, axis.z);
93
if (fit.Fit() < FLOAT_MAX) {
94
Base::Vector3f base, top;
95
fit.GetBounding(base, top);
96
Base::Vector3f axis = fit.GetAxis();
97
float radius = fit.GetRadius();
98
values.push_back(base.x);
99
values.push_back(base.y);
100
values.push_back(base.z);
101
values.push_back(axis.x);
102
values.push_back(axis.y);
103
values.push_back(axis.z);
104
values.push_back(radius);
109
float height = Base::Distance(base, top);
110
Gui::Command::doCommand(
112
"cyl = App.ActiveDocument.addObject('Part::Cylinder', 'Cylinder')\n"
115
"cyl.Placement = App.Placement(App.Vector(%f,%f,%f), "
116
"App.Rotation(App.Vector(0,0,1), App.Vector(%f,%f,%f)))\n",
126
Gui::Command::doCommand(
128
"axis = cyl.Placement.Rotation.multVec(App.Vector(0,0,1))\n"
129
"print('Final axis: ({}, {}, {})'.format(axis.x, axis.y, axis.z))\n");
139
class SphereFitParameter: public FitParameter
142
SphereFitParameter() = default;
143
std::vector<float> getParameter(FitParameter::Points pts) const override
145
std::vector<float> values;
146
MeshCore::SphereFit fit;
147
fit.AddPoints(pts.points);
148
if (fit.Fit() < FLOAT_MAX) {
149
Base::Vector3f base = fit.GetCenter();
150
float radius = fit.GetRadius();
151
values.push_back(base.x);
152
values.push_back(base.y);
153
values.push_back(base.z);
154
values.push_back(radius);
161
ParametersDialog::ParametersDialog(std::vector<float>& val,
162
FitParameter* fitPar,
168
, fitParameter(fitPar)
169
, parameter(std::move(par))
172
this->setWindowTitle(tr("Surface fit"));
174
QGridLayout* gridLayout {};
175
gridLayout = new QGridLayout(this);
177
QGroupBox* groupBox {};
178
groupBox = new QGroupBox(this);
179
groupBox->setTitle(tr("Parameters"));
180
gridLayout->addWidget(groupBox, 0, 0, 1, 1);
182
QGroupBox* selectBox {};
183
selectBox = new QGroupBox(this);
184
selectBox->setTitle(tr("Selection"));
185
gridLayout->addWidget(selectBox, 1, 0, 1, 1);
187
QVBoxLayout* selectLayout {};
188
selectLayout = new QVBoxLayout(selectBox);
190
QPushButton* regionButton {};
191
regionButton = new QPushButton(this);
192
regionButton->setText(tr("Region"));
193
regionButton->setObjectName(QString::fromLatin1("region"));
194
selectLayout->addWidget(regionButton);
196
QPushButton* singleButton {};
197
singleButton = new QPushButton(this);
198
singleButton->setText(tr("Triangle"));
199
singleButton->setObjectName(QString::fromLatin1("single"));
200
selectLayout->addWidget(singleButton);
202
QPushButton* clearButton {};
203
clearButton = new QPushButton(this);
204
clearButton->setText(tr("Clear"));
205
clearButton->setObjectName(QString::fromLatin1("clear"));
206
selectLayout->addWidget(clearButton);
208
QPushButton* computeButton {};
209
computeButton = new QPushButton(this);
210
computeButton->setText(tr("Compute"));
211
computeButton->setObjectName(QString::fromLatin1("compute"));
212
gridLayout->addWidget(computeButton, 2, 0, 1, 1);
214
QDialogButtonBox* buttonBox {};
215
buttonBox = new QDialogButtonBox(this);
216
buttonBox->setOrientation(Qt::Horizontal);
217
buttonBox->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Ok);
218
gridLayout->addWidget(buttonBox, 3, 0, 1, 1);
221
QGridLayout* layout {};
222
layout = new QGridLayout(groupBox);
223
groupBox->setLayout(layout);
224
for (const auto& it : parameter) {
225
QLabel* label = new QLabel(groupBox);
226
label->setText(it.first);
227
layout->addWidget(label, index, 0, 1, 1);
229
QDoubleSpinBox* doubleSpinBox = new QDoubleSpinBox(groupBox);
230
doubleSpinBox->setObjectName(it.first);
231
doubleSpinBox->setRange(-INT_MAX, INT_MAX);
232
doubleSpinBox->setValue(it.second);
233
layout->addWidget(doubleSpinBox, index, 1, 1, 1);
234
spinBoxes.push_back(doubleSpinBox);
239
connect(buttonBox, &QDialogButtonBox::accepted, this, &ParametersDialog::accept);
240
connect(buttonBox, &QDialogButtonBox::rejected, this, &ParametersDialog::reject);
241
connect(regionButton, &QPushButton::clicked, this, &ParametersDialog::onRegionClicked);
242
connect(singleButton, &QPushButton::clicked, this, &ParametersDialog::onSingleClicked);
243
connect(clearButton, &QPushButton::clicked, this, &ParametersDialog::onClearClicked);
244
connect(computeButton, &QPushButton::clicked, this, &ParametersDialog::onComputeClicked);
247
Gui::SelectionObject obj(mesh);
248
std::vector<Gui::SelectionObject> sel;
250
Gui::Selection().clearSelection();
251
meshSel.setObjects(sel);
252
meshSel.setCheckOnlyPointToUserTriangles(true);
253
meshSel.setCheckOnlyVisibleTriangles(true);
254
meshSel.setEnabledViewerSelection(false);
257
ParametersDialog::~ParametersDialog()
259
meshSel.clearSelection();
260
meshSel.setEnabledViewerSelection(true);
264
void ParametersDialog::onRegionClicked()
266
meshSel.startSelection();
269
void ParametersDialog::onSingleClicked()
271
meshSel.selectTriangle();
274
void ParametersDialog::onClearClicked()
276
meshSel.clearSelection();
279
void ParametersDialog::onComputeClicked()
281
const Mesh::MeshObject& kernel = myMesh->Mesh.getValue();
282
if (kernel.hasSelectedFacets()) {
283
FitParameter::Points fitpts;
284
std::vector<Mesh::ElementIndex> facets, points;
285
kernel.getFacetsFromSelection(facets);
286
points = kernel.getPointsFromFacets(facets);
287
MeshCore::MeshPointArray coords = kernel.getKernel().GetPoints(points);
288
fitpts.normals = kernel.getKernel().GetFacetNormals(facets);
291
fitpts.points.insert(fitpts.points.end(), coords.begin(), coords.end());
294
values = fitParameter->getParameter(fitpts);
295
if (values.size() == spinBoxes.size()) {
296
for (std::size_t i = 0; i < values.size(); i++) {
297
spinBoxes[i]->setValue(values[i]);
300
meshSel.stopSelection();
301
meshSel.clearSelection();
304
QMessageBox::warning(this,
306
tr("Before fitting the surface select an area."));
310
void ParametersDialog::accept()
312
std::vector<float> v;
313
for (auto it : spinBoxes) {
314
v.push_back(it->value());
320
void ParametersDialog::reject()
330
SegmentationBestFit::SegmentationBestFit(Mesh::Feature* mesh, QWidget* parent, Qt::WindowFlags fl)
331
: QWidget(parent, fl)
332
, ui(new Ui_SegmentationBestFit)
338
ui->numPln->setRange(1, INT_MAX);
339
ui->numPln->setValue(100);
340
ui->numCyl->setRange(1, INT_MAX);
341
ui->numCyl->setValue(100);
342
ui->numSph->setRange(1, INT_MAX);
343
ui->numSph->setValue(100);
345
Gui::SelectionObject obj(myMesh);
346
std::vector<Gui::SelectionObject> sel;
348
meshSel.setObjects(sel);
351
SegmentationBestFit::~SegmentationBestFit()
357
void SegmentationBestFit::setupConnections()
360
connect(ui->planeParameters, &QPushButton::clicked,
361
this, &SegmentationBestFit::onPlaneParametersClicked);
362
connect(ui->cylinderParameters, &QPushButton::clicked,
363
this, &SegmentationBestFit::onCylinderParametersClicked);
364
connect(ui->sphereParameters, &QPushButton::clicked,
365
this, &SegmentationBestFit::onSphereParametersClicked);
369
void SegmentationBestFit::onPlaneParametersClicked()
372
std::vector<float> p = planeParameter;
374
QString base = tr("Base");
375
QString axis = tr("Normal");
376
QString x = QString::fromLatin1(" x");
377
QString y = QString::fromLatin1(" y");
378
QString z = QString::fromLatin1(" z");
379
list.push_back(std::make_pair(base + x, p[0]));
380
list.push_back(std::make_pair(base + y, p[1]));
381
list.push_back(std::make_pair(base + z, p[2]));
382
list.push_back(std::make_pair(axis + x, p[3]));
383
list.push_back(std::make_pair(axis + y, p[4]));
384
list.push_back(std::make_pair(axis + z, p[5]));
386
static QPointer<QDialog> dialog = nullptr;
388
dialog = new ParametersDialog(planeParameter, new PlaneFitParameter, list, myMesh, this);
390
dialog->setAttribute(Qt::WA_DeleteOnClose);
394
void SegmentationBestFit::onCylinderParametersClicked()
397
std::vector<float> p = cylinderParameter;
399
QString base = tr("Base");
400
QString axis = tr("Axis");
401
QString radius = tr("Radius");
402
QString x = QString::fromLatin1(" x");
403
QString y = QString::fromLatin1(" y");
404
QString z = QString::fromLatin1(" z");
405
list.push_back(std::make_pair(base + x, p[0]));
406
list.push_back(std::make_pair(base + y, p[1]));
407
list.push_back(std::make_pair(base + z, p[2]));
408
list.push_back(std::make_pair(axis + x, p[3]));
409
list.push_back(std::make_pair(axis + y, p[4]));
410
list.push_back(std::make_pair(axis + z, p[5]));
411
list.push_back(std::make_pair(radius, p[6]));
413
static QPointer<QDialog> dialog = nullptr;
416
new ParametersDialog(cylinderParameter, new CylinderFitParameter, list, myMesh, this);
418
dialog->setAttribute(Qt::WA_DeleteOnClose);
422
void SegmentationBestFit::onSphereParametersClicked()
425
std::vector<float> p = sphereParameter;
427
QString base = tr("Center");
428
QString radius = tr("Radius");
429
QString x = QString::fromLatin1(" x");
430
QString y = QString::fromLatin1(" y");
431
QString z = QString::fromLatin1(" z");
432
list.push_back(std::make_pair(base + x, p[0]));
433
list.push_back(std::make_pair(base + y, p[1]));
434
list.push_back(std::make_pair(base + z, p[2]));
435
list.push_back(std::make_pair(radius, p[3]));
437
static QPointer<QDialog> dialog = nullptr;
439
dialog = new ParametersDialog(sphereParameter, new SphereFitParameter, list, myMesh, this);
441
dialog->setAttribute(Qt::WA_DeleteOnClose);
445
void SegmentationBestFit::accept()
447
const Mesh::MeshObject* mesh = myMesh->Mesh.getValuePtr();
448
const MeshCore::MeshKernel& kernel = mesh->getKernel();
450
MeshCore::MeshSegmentAlgorithm finder(kernel);
452
std::vector<MeshCore::MeshSurfaceSegmentPtr> segm;
453
if (ui->groupBoxCyl->isChecked()) {
454
MeshCore::AbstractSurfaceFit* fitter {};
455
if (cylinderParameter.size() == 7) {
456
std::vector<float>& p = cylinderParameter;
457
fitter = new MeshCore::CylinderSurfaceFit(Base::Vector3f(p[0], p[1], p[2]),
458
Base::Vector3f(p[3], p[4], p[5]),
462
fitter = new MeshCore::CylinderSurfaceFit;
465
std::make_shared<MeshCore::MeshDistanceGenericSurfaceFitSegment>(fitter,
468
ui->tolCyl->value()));
470
if (ui->groupBoxSph->isChecked()) {
471
MeshCore::AbstractSurfaceFit* fitter {};
472
if (sphereParameter.size() == 4) {
473
std::vector<float>& p = sphereParameter;
474
fitter = new MeshCore::SphereSurfaceFit(Base::Vector3f(p[0], p[1], p[2]), p[3]);
477
fitter = new MeshCore::SphereSurfaceFit;
480
std::make_shared<MeshCore::MeshDistanceGenericSurfaceFitSegment>(fitter,
483
ui->tolSph->value()));
485
if (ui->groupBoxPln->isChecked()) {
486
MeshCore::AbstractSurfaceFit* fitter {};
487
if (planeParameter.size() == 6) {
488
std::vector<float>& p = planeParameter;
489
fitter = new MeshCore::PlaneSurfaceFit(Base::Vector3f(p[0], p[1], p[2]),
490
Base::Vector3f(p[3], p[4], p[5]));
493
fitter = new MeshCore::PlaneSurfaceFit;
496
std::make_shared<MeshCore::MeshDistanceGenericSurfaceFitSegment>(fitter,
499
ui->tolPln->value()));
501
finder.FindSegments(segm);
503
App::Document* document = App::GetApplication().getActiveDocument();
504
document->openTransaction("Segmentation");
506
std::string internalname = "Segments_";
507
internalname += myMesh->getNameInDocument();
508
App::DocumentObjectGroup* group = static_cast<App::DocumentObjectGroup*>(
509
document->addObject("App::DocumentObjectGroup", internalname.c_str()));
510
std::string labelname = "Segments ";
511
labelname += myMesh->Label.getValue();
512
group->Label.setValue(labelname);
513
for (const auto& it : segm) {
514
const std::vector<MeshCore::MeshSegment>& data = it->GetSegments();
515
for (const auto& jt : data) {
516
Mesh::MeshObject* segment = mesh->meshFromSegment(jt);
517
Mesh::Feature* feaSegm =
518
static_cast<Mesh::Feature*>(group->addObject("Mesh::Feature", "Segment"));
519
Mesh::MeshObject* feaMesh = feaSegm->Mesh.startEditing();
520
feaMesh->swap(*segment);
521
feaSegm->Mesh.finishEditing();
524
std::stringstream label;
525
label << feaSegm->Label.getValue() << " (" << it->GetType() << ")";
526
feaSegm->Label.setValue(label.str());
529
document->commitTransaction();
532
void SegmentationBestFit::changeEvent(QEvent* e)
534
if (e->type() == QEvent::LanguageChange) {
535
ui->retranslateUi(this);
537
QWidget::changeEvent(e);
544
TaskSegmentationBestFit::TaskSegmentationBestFit(Mesh::Feature* mesh)
546
widget = new SegmentationBestFit(mesh);
547
addTaskBox(widget, false);
550
bool TaskSegmentationBestFit::accept()
556
#include "moc_SegmentationBestFit.cpp"