FreeCAD

Форк
0
/
SegmentationBestFit.cpp 
556 строк · 19.2 Кб
1
/***************************************************************************
2
 *   Copyright (c) 2018 Werner Mayer <wmayer[at]users.sourceforge.net>     *
3
 *                                                                         *
4
 *   This file is part of the FreeCAD CAx development system.              *
5
 *                                                                         *
6
 *   This library is free software; you can redistribute it and/or         *
7
 *   modify it under the terms of the GNU Library General Public           *
8
 *   License as published by the Free Software Foundation; either          *
9
 *   version 2 of the License, or (at your option) any later version.      *
10
 *                                                                         *
11
 *   This library  is distributed in the hope that it will be useful,      *
12
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
13
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
14
 *   GNU Library General Public License for more details.                  *
15
 *                                                                         *
16
 *   You should have received a copy of the GNU Library General Public     *
17
 *   License along with this library; see the file COPYING.LIB. If not,    *
18
 *   write to the Free Software Foundation, Inc., 59 Temple Place,         *
19
 *   Suite 330, Boston, MA  02111-1307, USA                                *
20
 *                                                                         *
21
 ***************************************************************************/
22

23
#include "PreCompiled.h"
24
#ifndef _PreComp_
25
#include <sstream>
26

27
#include <QDialog>
28
#include <QDoubleSpinBox>
29
#include <QMessageBox>
30
#include <QPointer>
31
#include <QVBoxLayout>
32
#endif
33

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

43
#include "SegmentationBestFit.h"
44
#include "ui_SegmentationBestFit.h"
45

46

47
using namespace MeshGui;
48

49
namespace MeshGui
50
{
51
class PlaneFitParameter: public FitParameter
52
{
53
public:
54
    PlaneFitParameter() = default;
55
    std::vector<float> getParameter(FitParameter::Points pts) const override
56
    {
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);
69
        }
70
        return values;
71
    }
72
};
73

74
class CylinderFitParameter: public FitParameter
75
{
76
public:
77
    CylinderFitParameter() = default;
78
    std::vector<float> getParameter(FitParameter::Points pts) const override
79
    {
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);
87

88
#if defined(FC_DEBUG)
89
            Base::Console().Message("Initial axis: (%f, %f, %f)\n", axis.x, axis.y, axis.z);
90
#endif
91
        }
92

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);
105

106
#if defined(FC_DEBUG)
107
            // Only for testing purposes
108
            try {
109
                float height = Base::Distance(base, top);
110
                Gui::Command::doCommand(
111
                    Gui::Command::App,
112
                    "cyl = App.ActiveDocument.addObject('Part::Cylinder', 'Cylinder')\n"
113
                    "cyl.Radius = %f\n"
114
                    "cyl.Height = %f\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",
117
                    radius,
118
                    height,
119
                    base.x,
120
                    base.y,
121
                    base.z,
122
                    axis.x,
123
                    axis.y,
124
                    axis.z);
125

126
                Gui::Command::doCommand(
127
                    Gui::Command::App,
128
                    "axis = cyl.Placement.Rotation.multVec(App.Vector(0,0,1))\n"
129
                    "print('Final axis: ({}, {}, {})'.format(axis.x, axis.y, axis.z))\n");
130
            }
131
            catch (...) {
132
            }
133
#endif
134
        }
135
        return values;
136
    }
137
};
138

139
class SphereFitParameter: public FitParameter
140
{
141
public:
142
    SphereFitParameter() = default;
143
    std::vector<float> getParameter(FitParameter::Points pts) const override
144
    {
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);
155
        }
156
        return values;
157
    }
158
};
159
}  // namespace MeshGui
160

161
ParametersDialog::ParametersDialog(std::vector<float>& val,
162
                                   FitParameter* fitPar,
163
                                   ParameterList par,
164
                                   Mesh::Feature* mesh,
165
                                   QWidget* parent)
166
    : QDialog(parent)
167
    , values(val)
168
    , fitParameter(fitPar)
169
    , parameter(std::move(par))
170
    , myMesh(mesh)
171
{
172
    this->setWindowTitle(tr("Surface fit"));
173

174
    QGridLayout* gridLayout {};
175
    gridLayout = new QGridLayout(this);
176

177
    QGroupBox* groupBox {};
178
    groupBox = new QGroupBox(this);
179
    groupBox->setTitle(tr("Parameters"));
180
    gridLayout->addWidget(groupBox, 0, 0, 1, 1);
181

182
    QGroupBox* selectBox {};
183
    selectBox = new QGroupBox(this);
184
    selectBox->setTitle(tr("Selection"));
185
    gridLayout->addWidget(selectBox, 1, 0, 1, 1);
186

187
    QVBoxLayout* selectLayout {};
188
    selectLayout = new QVBoxLayout(selectBox);
189

190
    QPushButton* regionButton {};
191
    regionButton = new QPushButton(this);
192
    regionButton->setText(tr("Region"));
193
    regionButton->setObjectName(QString::fromLatin1("region"));
194
    selectLayout->addWidget(regionButton);
195

196
    QPushButton* singleButton {};
197
    singleButton = new QPushButton(this);
198
    singleButton->setText(tr("Triangle"));
199
    singleButton->setObjectName(QString::fromLatin1("single"));
200
    selectLayout->addWidget(singleButton);
201

202
    QPushButton* clearButton {};
203
    clearButton = new QPushButton(this);
204
    clearButton->setText(tr("Clear"));
205
    clearButton->setObjectName(QString::fromLatin1("clear"));
206
    selectLayout->addWidget(clearButton);
207

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);
213

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);
219

220
    int index = 0;
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);
228

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);
235
        ++index;
236
    }
237

238
    // clang-format off
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);
245
    // clang-format on
246

247
    Gui::SelectionObject obj(mesh);
248
    std::vector<Gui::SelectionObject> sel;
249
    sel.push_back(obj);
250
    Gui::Selection().clearSelection();
251
    meshSel.setObjects(sel);
252
    meshSel.setCheckOnlyPointToUserTriangles(true);
253
    meshSel.setCheckOnlyVisibleTriangles(true);
254
    meshSel.setEnabledViewerSelection(false);
255
}
256

257
ParametersDialog::~ParametersDialog()
258
{
259
    meshSel.clearSelection();
260
    meshSel.setEnabledViewerSelection(true);
261
    delete fitParameter;
262
}
263

264
void ParametersDialog::onRegionClicked()
265
{
266
    meshSel.startSelection();
267
}
268

269
void ParametersDialog::onSingleClicked()
270
{
271
    meshSel.selectTriangle();
272
}
273

274
void ParametersDialog::onClearClicked()
275
{
276
    meshSel.clearSelection();
277
}
278

279
void ParametersDialog::onComputeClicked()
280
{
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);
289

290
        // Copy points into right format
291
        fitpts.points.insert(fitpts.points.end(), coords.begin(), coords.end());
292
        coords.clear();
293

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]);
298
            }
299
        }
300
        meshSel.stopSelection();
301
        meshSel.clearSelection();
302
    }
303
    else {
304
        QMessageBox::warning(this,
305
                             tr("No selection"),
306
                             tr("Before fitting the surface select an area."));
307
    }
308
}
309

310
void ParametersDialog::accept()
311
{
312
    std::vector<float> v;
313
    for (auto it : spinBoxes) {
314
        v.push_back(it->value());
315
    }
316
    values = v;
317
    QDialog::accept();
318
}
319

320
void ParametersDialog::reject()
321
{
322
    values.clear();
323
    QDialog::reject();
324
}
325

326
// ----------------------------------------------------------------------------
327

328
/* TRANSLATOR MeshGui::SegmentationBestFit */
329

330
SegmentationBestFit::SegmentationBestFit(Mesh::Feature* mesh, QWidget* parent, Qt::WindowFlags fl)
331
    : QWidget(parent, fl)
332
    , ui(new Ui_SegmentationBestFit)
333
    , myMesh(mesh)
334
{
335
    ui->setupUi(this);
336
    setupConnections();
337

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);
344

345
    Gui::SelectionObject obj(myMesh);
346
    std::vector<Gui::SelectionObject> sel;
347
    sel.push_back(obj);
348
    meshSel.setObjects(sel);
349
}
350

351
SegmentationBestFit::~SegmentationBestFit()
352
{
353
    // no need to delete child widgets, Qt does it all for us
354
    delete ui;
355
}
356

357
void SegmentationBestFit::setupConnections()
358
{
359
    // clang-format off
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);
366
    // clang-format on
367
}
368

369
void SegmentationBestFit::onPlaneParametersClicked()
370
{
371
    ParameterList list;
372
    std::vector<float> p = planeParameter;
373
    p.resize(6);
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]));
385

386
    static QPointer<QDialog> dialog = nullptr;
387
    if (!dialog) {
388
        dialog = new ParametersDialog(planeParameter, new PlaneFitParameter, list, myMesh, this);
389
    }
390
    dialog->setAttribute(Qt::WA_DeleteOnClose);
391
    dialog->show();
392
}
393

394
void SegmentationBestFit::onCylinderParametersClicked()
395
{
396
    ParameterList list;
397
    std::vector<float> p = cylinderParameter;
398
    p.resize(7);
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]));
412

413
    static QPointer<QDialog> dialog = nullptr;
414
    if (!dialog) {
415
        dialog =
416
            new ParametersDialog(cylinderParameter, new CylinderFitParameter, list, myMesh, this);
417
    }
418
    dialog->setAttribute(Qt::WA_DeleteOnClose);
419
    dialog->show();
420
}
421

422
void SegmentationBestFit::onSphereParametersClicked()
423
{
424
    ParameterList list;
425
    std::vector<float> p = sphereParameter;
426
    p.resize(4);
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]));
436

437
    static QPointer<QDialog> dialog = nullptr;
438
    if (!dialog) {
439
        dialog = new ParametersDialog(sphereParameter, new SphereFitParameter, list, myMesh, this);
440
    }
441
    dialog->setAttribute(Qt::WA_DeleteOnClose);
442
    dialog->show();
443
}
444

445
void SegmentationBestFit::accept()
446
{
447
    const Mesh::MeshObject* mesh = myMesh->Mesh.getValuePtr();
448
    const MeshCore::MeshKernel& kernel = mesh->getKernel();
449

450
    MeshCore::MeshSegmentAlgorithm finder(kernel);
451

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]),
459
                                                      p[6]);
460
        }
461
        else {
462
            fitter = new MeshCore::CylinderSurfaceFit;
463
        }
464
        segm.emplace_back(
465
            std::make_shared<MeshCore::MeshDistanceGenericSurfaceFitSegment>(fitter,
466
                                                                             kernel,
467
                                                                             ui->numCyl->value(),
468
                                                                             ui->tolCyl->value()));
469
    }
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]);
475
        }
476
        else {
477
            fitter = new MeshCore::SphereSurfaceFit;
478
        }
479
        segm.emplace_back(
480
            std::make_shared<MeshCore::MeshDistanceGenericSurfaceFitSegment>(fitter,
481
                                                                             kernel,
482
                                                                             ui->numSph->value(),
483
                                                                             ui->tolSph->value()));
484
    }
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]));
491
        }
492
        else {
493
            fitter = new MeshCore::PlaneSurfaceFit;
494
        }
495
        segm.emplace_back(
496
            std::make_shared<MeshCore::MeshDistanceGenericSurfaceFitSegment>(fitter,
497
                                                                             kernel,
498
                                                                             ui->numPln->value(),
499
                                                                             ui->tolPln->value()));
500
    }
501
    finder.FindSegments(segm);
502

503
    App::Document* document = App::GetApplication().getActiveDocument();
504
    document->openTransaction("Segmentation");
505

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();
522
            delete segment;
523

524
            std::stringstream label;
525
            label << feaSegm->Label.getValue() << " (" << it->GetType() << ")";
526
            feaSegm->Label.setValue(label.str());
527
        }
528
    }
529
    document->commitTransaction();
530
}
531

532
void SegmentationBestFit::changeEvent(QEvent* e)
533
{
534
    if (e->type() == QEvent::LanguageChange) {
535
        ui->retranslateUi(this);
536
    }
537
    QWidget::changeEvent(e);
538
}
539

540
// ---------------------------------------
541

542
/* TRANSLATOR MeshGui::TaskSegmentationBestFit */
543

544
TaskSegmentationBestFit::TaskSegmentationBestFit(Mesh::Feature* mesh)
545
{
546
    widget = new SegmentationBestFit(mesh);  // NOLINT
547
    addTaskBox(widget, false);
548
}
549

550
bool TaskSegmentationBestFit::accept()
551
{
552
    widget->accept();
553
    return true;
554
}
555

556
#include "moc_SegmentationBestFit.cpp"
557

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.