FreeCAD

Форк
0
/
TaskCheckGeometry.cpp 
1425 строк · 55.7 Кб
1
/***************************************************************************
2
 *   Copyright (c) 2012 Thomas Anderson <blobfish[at]gmx.com>              *
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 <QCheckBox>
26
# include <QCoreApplication>
27
# include <QHeaderView>
28
# include <QPushButton>
29
# include <QScrollBar>
30
# include <QTextEdit>
31
# include <QTextStream>
32
# include <QThread>
33
# include <QTreeView>
34
# include <Standard_Version.hxx>
35
# include <Bnd_Box.hxx>
36
# include <BOPAlgo_ArgumentAnalyzer.hxx>
37
# include <BOPAlgo_ListOfCheckResult.hxx>
38
# include <BRepBndLib.hxx>
39
# include <BRepBuilderAPI_Copy.hxx>
40
# include <BRepCheck_Analyzer.hxx>
41
# include <BRepCheck_ListIteratorOfListOfStatus.hxx>
42
# include <BRepCheck_Result.hxx>
43
# include <BRepTools_ShapeSet.hxx>
44
# include <ShapeAnalysis_FreeBounds.hxx>
45
# include <TopoDS.hxx>
46
# include <TopoDS_Compound.hxx>
47
# include <TopTools_IndexedMapOfShape.hxx>
48
# include <TopExp.hxx>
49
# include <TopExp_Explorer.hxx>
50
# include <Inventor/nodes/SoCube.h>
51
# include <Inventor/nodes/SoDrawStyle.h>
52
# include <Inventor/nodes/SoMaterial.h>
53
# include <Inventor/nodes/SoResetTransform.h>
54
# include <Inventor/nodes/SoSeparator.h>
55
# include <Inventor/nodes/SoSwitch.h>
56
# include <Inventor/nodes/SoTransform.h>
57
#endif //_PreComp_
58

59
#include <Base/Interpreter.h>
60
#include <Gui/Application.h>
61
#include <Gui/BitmapFactory.h>
62
#include <Gui/Document.h>
63
#include <Gui/MainWindow.h>
64
#include <Gui/Selection.h>
65
#include <Gui/ViewProvider.h>
66
#include <Gui/WaitCursor.h>
67
#include <Mod/Part/App/PartFeature.h>
68

69
#include "TaskCheckGeometry.h"
70

71

72
using namespace PartGui;
73

74
QVector<QString> buildShapeEnumVector()
75
{
76
   QVector<QString>names;
77
   names.push_back(QObject::tr("Compound"));             //TopAbs_COMPOUND
78
   names.push_back(QObject::tr("Compound Solid"));       //TopAbs_COMPSOLID
79
   names.push_back(QObject::tr("Solid"));                //TopAbs_SOLID
80
   names.push_back(QObject::tr("Shell"));                //TopAbs_SHELL
81
   names.push_back(QObject::tr("Face"));                 //TopAbs_FACE
82
   names.push_back(QObject::tr("Wire"));                 //TopAbs_WIRE
83
   names.push_back(QObject::tr("Edge"));                 //TopAbs_EDGE
84
   names.push_back(QObject::tr("Vertex"));               //TopAbs_VERTEX
85
   names.push_back(QObject::tr("Shape"));                //TopAbs_SHAPE
86
   return names;
87
}
88

89
QString shapeEnumToString(const int &index)
90
{
91
    static QVector<QString> names = buildShapeEnumVector();
92
    if (index < 0 || index > TopAbs_SHAPE)
93
        return names.at(8);
94
    return names.at(index);
95
}
96

97
QVector<QString> buildCheckStatusStringVector()
98
{
99
    QVector<QString>names;
100
    names.push_back(QObject::tr("No Error"));                           //    BRepCheck_NoError
101
    names.push_back(QObject::tr("Invalid Point On Curve"));             //    BRepCheck_InvalidPointOnCurve
102
    names.push_back(QObject::tr("Invalid Point On Curve On Surface"));  //    BRepCheck_InvalidPointOnCurveOnSurface
103
    names.push_back(QObject::tr("Invalid Point On Surface"));           //    BRepCheck_InvalidPointOnSurface
104
    names.push_back(QObject::tr("No 3D Curve"));                        //    BRepCheck_No3DCurve
105
    names.push_back(QObject::tr("Multiple 3D Curve"));                  //    BRepCheck_Multiple3DCurve
106
    names.push_back(QObject::tr("Invalid 3D Curve"));                   //    BRepCheck_Invalid3DCurve
107
    names.push_back(QObject::tr("No Curve On Surface"));                //    BRepCheck_NoCurveOnSurface
108
    names.push_back(QObject::tr("Invalid Curve On Surface"));           //    BRepCheck_InvalidCurveOnSurface
109
    names.push_back(QObject::tr("Invalid Curve On Closed Surface"));    //    BRepCheck_InvalidCurveOnClosedSurface
110
    names.push_back(QObject::tr("Invalid Same Range Flag"));            //    BRepCheck_InvalidSameRangeFlag
111
    names.push_back(QObject::tr("Invalid Same Parameter Flag"));        //    BRepCheck_InvalidSameParameterFlag
112
    names.push_back(QObject::tr("Invalid Degenerated Flag"));           //    BRepCheck_InvalidDegeneratedFlag
113
    names.push_back(QObject::tr("Free Edge"));                          //    BRepCheck_FreeEdge
114
    names.push_back(QObject::tr("Invalid MultiConnexity"));             //    BRepCheck_InvalidMultiConnexity
115
    names.push_back(QObject::tr("Invalid Range"));                      //    BRepCheck_InvalidRange
116
    names.push_back(QObject::tr("Empty Wire"));                         //    BRepCheck_EmptyWire
117
    names.push_back(QObject::tr("Redundant Edge"));                     //    BRepCheck_RedundantEdge
118
    names.push_back(QObject::tr("Self Intersecting Wire"));             //    BRepCheck_SelfIntersectingWire
119
    names.push_back(QObject::tr("No Surface"));                         //    BRepCheck_NoSurface
120
    names.push_back(QObject::tr("Invalid Wire"));                       //    BRepCheck_InvalidWire
121
    names.push_back(QObject::tr("Redundant Wire"));                     //    BRepCheck_RedundantWire
122
    names.push_back(QObject::tr("Intersecting Wires"));                 //    BRepCheck_IntersectingWires
123
    names.push_back(QObject::tr("Invalid Imbrication Of Wires"));       //    BRepCheck_InvalidImbricationOfWires
124
    names.push_back(QObject::tr("Empty Shell"));                        //    BRepCheck_EmptyShell
125
    names.push_back(QObject::tr("Redundant Face"));                     //    BRepCheck_RedundantFace
126
    names.push_back(QObject::tr("Unorientable Shape"));                 //    BRepCheck_UnorientableShape
127
    names.push_back(QObject::tr("Not Closed"));                         //    BRepCheck_NotClosed
128
    names.push_back(QObject::tr("Not Connected"));                      //    BRepCheck_NotConnected
129
    names.push_back(QObject::tr("Sub Shape Not In Shape"));             //    BRepCheck_SubshapeNotInShape
130
    names.push_back(QObject::tr("Bad Orientation"));                    //    BRepCheck_BadOrientation
131
    names.push_back(QObject::tr("Bad Orientation Of Sub Shape"));       //    BRepCheck_BadOrientationOfSubshape
132
    names.push_back(QObject::tr("Invalid Tolerance Value"));            //    BRepCheck_InvalidToleranceValue
133
    names.push_back(QObject::tr("Check Failed"));                       //    BRepCheck_CheckFail
134

135
    return names;
136
}
137

138
QString checkStatusToString(const int &index)
139
{
140
    static QVector<QString> names = buildCheckStatusStringVector();
141
    if (index == -1)
142
    {
143
        return QString(QObject::tr("No Result"));
144
    }
145
    if (index > 33 || index < 0)
146
    {
147
        QString message(QObject::tr("Out Of Enum Range:") + QStringLiteral(" "));
148
        QString number;
149
        number.setNum(index);
150
        message += number;
151
        return message;
152
    }
153
    return names.at(index);
154
}
155

156
QVector<QString> buildBOPCheckResultVector()
157
{
158
  QVector<QString> results;
159
  results.push_back(QObject::tr("Boolean operation: Unknown check"));               //BOPAlgo_CheckUnknown
160
  results.push_back(QObject::tr("Boolean operation: Bad type"));                    //BOPAlgo_BadType
161
  results.push_back(QObject::tr("Boolean operation: Self-intersection found"));     //BOPAlgo_SelfIntersect
162
  results.push_back(QObject::tr("Boolean operation: Edge too small"));              //BOPAlgo_TooSmallEdge
163
  results.push_back(QObject::tr("Boolean operation: Non-recoverable face"));        //BOPAlgo_NonRecoverableFace
164
  results.push_back(QObject::tr("Boolean operation: Incompatibility of vertex"));   //BOPAlgo_IncompatibilityOfVertex
165
  results.push_back(QObject::tr("Boolean operation: Incompatibility of edge"));     //BOPAlgo_IncompatibilityOfEdge
166
  results.push_back(QObject::tr("Boolean operation: Incompatibility of face"));     //BOPAlgo_IncompatibilityOfFace
167
  results.push_back(QObject::tr("Boolean operation: Aborted"));                     //BOPAlgo_OperationAborted
168
  results.push_back(QObject::tr("Boolean operation: GeomAbs_C0"));                  //BOPAlgo_GeomAbs_C0
169
  results.push_back(QObject::tr("Boolean operation: Invalid curve on surface"));    //BOPAlgo_InvalidCurveOnSurface
170
  results.push_back(QObject::tr("Boolean operation: Not valid"));                   //BOPAlgo_NotValid
171

172
  return results;
173
}
174

175
QString getBOPCheckString(const BOPAlgo_CheckStatus &status)
176
{
177
  static QVector<QString> strings = buildBOPCheckResultVector();
178
  int index = static_cast<int>(status);
179
  if (index < 0 || index > strings.size())
180
    index = 0;
181
  return strings.at(index);
182
}
183

184
ResultEntry::ResultEntry()
185
{
186
    viewProviderRoot = nullptr;
187
    boxSep = nullptr;
188
    boxSwitch = nullptr;
189
    parent = nullptr;
190
    children.clear();
191
    selectionStrings.clear();
192
}
193

194
ResultEntry::~ResultEntry()
195
{
196
    if (boxSep && viewProviderRoot)
197
        viewProviderRoot->removeChild(boxSep);
198
    if (viewProviderRoot)
199
        viewProviderRoot->unref();
200
    qDeleteAll(children);
201
}
202

203
void ResultEntry::buildEntryName()
204
{
205
    ResultEntry* parentEntry = this;
206
    while (parentEntry->parent) {
207
        ResultEntry* temp = parentEntry->parent;
208
        if (!temp->parent)
209
            break;
210
        parentEntry = parentEntry->parent;
211
    }
212

213
  QString stringOut;
214
  QTextStream stream(&stringOut);
215
  TopTools_IndexedMapOfShape shapeMap;
216
  int index(-1);
217

218
  switch (this->shape.ShapeType())
219
  {
220
  case TopAbs_COMPOUND:
221
      TopExp::MapShapes(parentEntry->shape, TopAbs_COMPOUND, shapeMap);
222
      stream << "Compound";
223
      break;
224
  case TopAbs_COMPSOLID:
225
      TopExp::MapShapes(parentEntry->shape, TopAbs_COMPSOLID, shapeMap);
226
      stream << "CompSolid";
227
      break;
228
  case TopAbs_SOLID:
229
      TopExp::MapShapes(parentEntry->shape, TopAbs_SOLID, shapeMap);
230
      stream << "Solid";
231
      break;
232
  case TopAbs_SHELL:
233
      TopExp::MapShapes(parentEntry->shape, TopAbs_SHELL, shapeMap);
234
      stream << "Shell";
235
      break;
236
  case TopAbs_WIRE:
237
      TopExp::MapShapes(parentEntry->shape, TopAbs_WIRE, shapeMap);
238
      stream << "Wire";
239
      break;
240
  case TopAbs_FACE:
241
      TopExp::MapShapes(parentEntry->shape, TopAbs_FACE, shapeMap);
242
      stream << "Face";
243
      break;
244
  case TopAbs_EDGE:
245
      TopExp::MapShapes(parentEntry->shape, TopAbs_EDGE, shapeMap);
246
      stream << "Edge";
247
      break;
248
  case TopAbs_VERTEX:
249
      TopExp::MapShapes(parentEntry->shape, TopAbs_VERTEX, shapeMap);
250
      stream << "Vertex";
251
      break;
252
  default:
253
      stream << "Unexpected shape type";
254
      break;
255
  }
256

257
  index = shapeMap.FindIndex(this->shape);
258
  stream << index;
259
  this->name = stringOut;
260
}
261

262
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
263

264
ResultModel::ResultModel(QObject *parent) : QAbstractItemModel(parent)
265
{
266
    root = nullptr;
267
}
268

269
ResultModel::~ResultModel()
270
{
271
    if (root)
272
        delete root;
273
}
274

275
QModelIndex ResultModel::index(int row, int column, const QModelIndex &parent) const
276
{
277
    if (!root)
278
        return {};
279
    ResultEntry *parentNode = nodeFromIndex(parent);
280
    if (!parentNode)
281
        return {};
282
    return createIndex(row, column, parentNode->children.at(row));
283
}
284

285
QModelIndex ResultModel::parent(const QModelIndex &child) const
286
{
287
    ResultEntry *childNode = nodeFromIndex(child);
288
    if (!childNode)
289
        return {};
290
    ResultEntry *parentNode = childNode->parent;
291
    if (!parentNode)
292
        return {};
293
    ResultEntry *grandParentNode = parentNode->parent;
294
    if (!grandParentNode)
295
        return {};
296
    int row = grandParentNode->children.indexOf(parentNode);
297
    return createIndex(row, 0, parentNode);
298
}
299

300
int ResultModel::rowCount(const QModelIndex &parent) const
301
{
302
    ResultEntry *parentNode = nodeFromIndex(parent);
303
    if (!parentNode)
304
        return 0;
305
    return parentNode->children.size();
306
}
307

308
int ResultModel::columnCount(const QModelIndex &parent) const
309
{
310
    Q_UNUSED(parent);
311
    return 3;
312
}
313

314
QVariant ResultModel::data(const QModelIndex &index, int role) const
315
{
316
    if (role != Qt::DisplayRole)
317
        return {};
318
    ResultEntry *node = nodeFromIndex(index);
319
    if (!node)
320
        return {};
321
    switch (index.column())
322
    {
323
    case 0:
324
        return QVariant(node->name);
325
    case 1:
326
        return QVariant(node->type);
327
    case 2:
328
        return QVariant(node->error);
329
    }
330
    return {};
331
}
332

333
QVariant ResultModel::headerData(int section, Qt::Orientation orientation, int role) const
334
{
335
    if (orientation != Qt::Horizontal || role != Qt::DisplayRole)
336
        return {};
337
    switch (section)
338
    {
339
    case 0:
340
        return QVariant(QString(tr("Name")));
341
    case 1:
342
        return QVariant(QString(tr("Type")));
343
    case 2:
344
        return QVariant(QString(tr("Error")));
345
    }
346
    return {};
347
}
348

349
void ResultModel::setResults(ResultEntry *resultsIn)
350
{
351
    this->beginResetModel();
352
    if (root)
353
        delete root;
354
    root = resultsIn;
355
    this->endResetModel();
356
}
357

358
ResultEntry* ResultModel::getEntry(const QModelIndex &index)
359
{
360
    return nodeFromIndex(index);
361
}
362

363
ResultEntry* ResultModel::nodeFromIndex(const QModelIndex &index) const
364
{
365
    if (index.isValid())
366
        return static_cast<ResultEntry *>(index.internalPointer());
367
    else
368
        return root;
369
}
370

371
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
372

373
TaskCheckGeometryResults::TaskCheckGeometryResults(QWidget *parent) : QWidget(parent)
374
{
375
    this->setWindowTitle(tr("Check Geometry Results"));
376
    setupInterface();
377
    setupFunctionMap();
378
}
379

380
TaskCheckGeometryResults::~TaskCheckGeometryResults()
381
{
382
    try {
383
        Gui::Selection().clearSelection();
384
    }
385
    catch (const Py::Exception&) {
386
        Base::PyException e; // extract the Python error text
387
        e.ReportException();
388
    }
389
}
390

391
void TaskCheckGeometryResults::setupInterface()
392
{
393
    message = new QLabel(this);
394
    message->setText(tr("Check is running..."));
395
    model = new ResultModel(this);
396
    treeView = new QTreeView(this);
397
    treeView->setModel(model);
398
    treeView->setSelectionMode(QAbstractItemView::SingleSelection);
399
    treeView->setSelectionBehavior(QAbstractItemView::SelectRows);
400
    connect(treeView->selectionModel(), &QItemSelectionModel::currentRowChanged,
401
            this, &TaskCheckGeometryResults::currentRowChanged);
402

403
    QVBoxLayout *layout = new QVBoxLayout();
404
    layout->addWidget(message);
405
    layout->addWidget(treeView);
406
    this->setLayout(layout);
407
}
408

409
void TaskCheckGeometryResults::goCheck()
410
{
411
    Gui::WaitCursor wc;
412
    auto selection = Gui::Selection().getSelection();
413

414
    int selectedCount(0), checkedCount(0), invalidShapes(0);
415
    ResultEntry *theRoot = new ResultEntry();
416

417
    std::string scopeName {tr("Boolean operation check...").toStdString()};
418
#if OCC_VERSION_HEX < 0x070500
419
    Handle(Message_ProgressIndicator) theProgress = new BOPProgressIndicator(tr("Check geometry"),
420
                                                                             Gui::getMainWindow());
421
    theProgress->NewScope(scopeName.c_str());
422
    theProgress->Show();
423
#else
424
    Handle(Message_ProgressIndicator) theProgress = new BOPProgressIndicator(tr("Check geometry"),
425
                                                                             Gui::getMainWindow());
426
    Message_ProgressRange theRange(theProgress->Start());
427
    Message_ProgressScope theScope(theRange,
428
                                   TCollection_AsciiString(scopeName.c_str()),
429
                                   selection.size());
430
    theScope.Show();
431
#endif // 0x070500
432

433
    for(const auto &sel :  selection) {
434
        selectedCount++;
435
        TopoDS_Shape shape = Part::Feature::getShape(sel.pObject,sel.SubName,true);
436
        if (shape.IsNull())
437
            continue;
438
        currentSeparator = Gui::Application::Instance->getViewProvider(sel.pObject)->getRoot();
439
        if (!currentSeparator)
440
            continue;
441
        QString baseName;
442
        QTextStream baseStream(&baseName);
443
        baseStream << sel.DocName;
444
        baseStream << "." << sel.FeatName;
445
        checkedCount++;
446
        checkedMap.Clear();
447

448
        buildShapeContent(sel.pObject, baseName, shape);
449

450
        BRepCheck_Analyzer shapeCheck(shape);
451
        if (!shapeCheck.IsValid())
452
        {
453
            invalidShapes++;
454
            ResultEntry *entry = new ResultEntry();
455
            entry->parent = theRoot;
456
            entry->shape = shape;
457
            entry->name = baseName;
458
            entry->type = shapeEnumToString(shape.ShapeType());
459
            entry->error = tr("Invalid");
460
            entry->viewProviderRoot = currentSeparator;
461
            entry->viewProviderRoot->ref();
462
            goSetupResultBoundingBox(entry);
463
            theRoot->children.push_back(entry);
464
            recursiveCheck(shapeCheck, shape, entry);
465
            continue; //don't run BOPAlgo_ArgumentAnalyzer if BRepCheck_Analyzer finds something.
466
        }
467
        else
468
        {
469
          //BOPAlgo_ArgumentAnalyzer can be really slow!
470
          //so only run it when the shape seems valid to BRepCheck_Analyzer And
471
          //when the option is set.
472

473
          ParameterGrp::handle group = App::GetApplication().GetUserParameter().
474
          GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod")->GetGroup("Part")->GetGroup("CheckGeometry");
475
          bool runSignal = group->GetBool("RunBOPCheck", false);
476
          group->SetBool("RunBOPCheck", runSignal);
477
          if (runSignal) {
478
            std::string label = tr("Checking").toStdString() + " ";
479
            label += sel.pObject->Label.getStrValue();
480
            label += "...";
481
#if OCC_VERSION_HEX < 0x070500
482
            theProgress->NewScope(label.c_str());
483
            invalidShapes += goBOPSingleCheck(shape, theRoot, baseName, theProgress);
484
            theProgress->EndScope();
485
            if (theProgress->UserBreak())
486
              break;
487
#else
488
            Message_ProgressScope theInnerScope(theScope.Next(), TCollection_AsciiString(label.c_str()), 1);
489
            theInnerScope.Show();
490
            invalidShapes += goBOPSingleCheck(shape, theRoot, baseName, theInnerScope);
491
            theInnerScope.Close();
492
            if (theScope.UserBreak())
493
              break;
494
#endif
495
          }
496
        }
497
    }
498
    model->setResults(theRoot);
499
    treeView->expandAll();
500
    treeView->header()->resizeSections(QHeaderView::ResizeToContents);
501
    QString aMessage {tr("%1 processed out of %2 selected").arg(checkedCount).arg(selectedCount)};
502
    aMessage += QLatin1String("\n ") + tr("%n invalid shapes.", "", invalidShapes);
503
    message->setText(aMessage);
504
}
505

506
void TaskCheckGeometryResults::recursiveCheck(const BRepCheck_Analyzer &shapeCheck, const TopoDS_Shape &shape,
507
                                              ResultEntry *parent)
508
{
509
    ResultEntry *branchNode = parent;
510
    BRepCheck_ListIteratorOfListOfStatus listIt;
511
    if (!shapeCheck.Result(shape).IsNull() && !checkedMap.Contains(shape))
512
    {
513
        listIt.Initialize(shapeCheck.Result(shape)->Status());
514
        if (listIt.Value() != BRepCheck_NoError)
515
        {
516
            ResultEntry *entry = new ResultEntry();
517
            entry->parent = parent;
518
            entry->shape = shape;
519
            entry->buildEntryName();
520
            entry->type = shapeEnumToString(shape.ShapeType());
521
            entry->error = checkStatusToString(listIt.Value());
522
            entry->viewProviderRoot = currentSeparator;
523
            entry->viewProviderRoot->ref();
524
            dispatchError(entry, listIt.Value());
525
            parent->children.push_back(entry);
526
            branchNode = entry;
527
        }
528
    }
529
    checkedMap.Add(shape);
530

531
    if (shape.ShapeType() == TopAbs_SOLID)
532
        checkSub(shapeCheck, shape, TopAbs_SHELL, branchNode);
533
    if (shape.ShapeType() == TopAbs_EDGE)
534
        checkSub(shapeCheck, shape, TopAbs_VERTEX, branchNode);
535
    if (shape.ShapeType() == TopAbs_FACE)
536
    {
537
        checkSub(shapeCheck, shape, TopAbs_WIRE, branchNode);
538
        checkSub(shapeCheck, shape, TopAbs_EDGE, branchNode);
539
        checkSub(shapeCheck, shape, TopAbs_VERTEX, branchNode);
540
    }
541

542
    for (TopoDS_Iterator it(shape); it.More(); it.Next())
543
        recursiveCheck(shapeCheck, it.Value(), branchNode);
544
}
545

546
void TaskCheckGeometryResults::checkSub(const BRepCheck_Analyzer &shapeCheck, const TopoDS_Shape &shape,
547
                                        const TopAbs_ShapeEnum subType, ResultEntry *parent)
548
{
549
    BRepCheck_ListIteratorOfListOfStatus itl;
550
    TopExp_Explorer exp;
551
    for (exp.Init(shape,subType); exp.More(); exp.Next())
552
    {
553
        const Handle(BRepCheck_Result)& res = shapeCheck.Result(exp.Current());
554
        const TopoDS_Shape& sub = exp.Current();
555
        for (res->InitContextIterator(); res->MoreShapeInContext(); res->NextShapeInContext())
556
        {
557
            if (res->ContextualShape().IsSame(shape))
558
            {
559
                for (itl.Initialize(res->StatusOnShape()); itl.More(); itl.Next())
560
                {
561
                     if (itl.Value() == BRepCheck_NoError)
562
                         break;
563
                     checkedMap.Add(sub);
564
                     ResultEntry *entry = new ResultEntry();
565
                     entry->parent = parent;
566
                     entry->shape = sub;
567
                     entry->buildEntryName();
568
                     entry->type = shapeEnumToString(sub.ShapeType());
569
                     entry->error = checkStatusToString(itl.Value());
570
                     entry->viewProviderRoot = currentSeparator;
571
                     entry->viewProviderRoot->ref();
572
                     dispatchError(entry, itl.Value());
573
                     parent->children.push_back(entry);
574
                }
575
            }
576
        }
577
    }
578
}
579

580
void TaskCheckGeometryResults::buildShapeContent(App::DocumentObject *pObject, const QString &baseName, const TopoDS_Shape &shape)
581
{
582

583
    bool advancedShapeContent = App::GetApplication().GetUserParameter().GetGroup("BaseApp")->GetGroup("Preferences")->
584
            GetGroup("Mod")->GetGroup("Part")->GetGroup("CheckGeometry")->GetBool("AdvancedShapeContent", true);
585
    int decimals = App::GetApplication().GetUserParameter().
586
            GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Units")->GetInt("Decimals", 2);
587
    std::ostringstream stream;
588
    if (!shapeContentString.empty())
589
        stream << std::endl << std::endl;
590
    stream << tr("Checked object").toStdString() << ": ";
591
    Base::PyGILStateLocker lock;
592
    try {
593
        PyObject* module = PyImport_ImportModule("BasicShapes.ShapeContent");
594
        if (!module) {
595
            throw Py::Exception();
596
        }
597
        Py::Tuple args(3);
598
        args.setItem(0, Py::asObject(pObject->getPyObject()));
599
        args.setItem(1, Py::Long(decimals));
600
        args.setItem(2, Py::Boolean(advancedShapeContent));
601
        Py::Module shapecontent(module, true);
602
        Py::String result(shapecontent.callMemberFunction("buildShapeContent", args));
603
        stream << result.as_std_string("utf-8");
604
    }
605
    catch (Py::Exception&) {
606
        Base::PyException e;
607
        e.ReportException();
608
        stream << baseName.toLatin1().data() << std::endl;
609
        BRepTools_ShapeSet set;
610
        set.Add(shape);
611
        set.DumpExtent(stream);
612
    }
613
    shapeContentString += stream.str();
614
}
615

616
QString TaskCheckGeometryResults::getShapeContentString()
617
{
618
  return QString::fromStdString(shapeContentString);
619
}
620

621
#if OCC_VERSION_HEX < 0x070500
622
int TaskCheckGeometryResults::goBOPSingleCheck(const TopoDS_Shape& shapeIn, ResultEntry *theRoot, const QString &baseName,
623
                                               const Handle(Message_ProgressIndicator)& theProgress)
624
#else
625
int TaskCheckGeometryResults::goBOPSingleCheck(const TopoDS_Shape& shapeIn, ResultEntry *theRoot, const QString &baseName,
626
                                               const Message_ProgressScope& theScope)
627
#endif
628
{
629
    ParameterGrp::handle group = App::GetApplication().GetUserParameter().
630
    GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod")->GetGroup("Part")->GetGroup("CheckGeometry");
631
    bool runSingleThreaded = group->GetBool("RunBOPCheckSingleThreaded", false);
632
    bool logErrors = group->GetBool("LogErrors", true);
633
    bool argumentTypeMode = group->GetBool("ArgumentTypeMode", true);
634
    bool selfInterMode = group->GetBool("SelfInterMode", true);
635
    bool smallEdgeMode = group->GetBool("SmallEdgeMode", true);
636
    bool rebuildFaceMode = group->GetBool("RebuildFaceMode", true);
637
    bool continuityMode = group->GetBool("ContinuityMode", true);
638
    bool tangentMode = group->GetBool("TangentMode", true);
639
    bool mergeVertexMode = group->GetBool("MergeVertexMode", true);
640
    bool mergeEdgeMode = group->GetBool("MergeEdgeMode", true);
641
    bool curveOnSurfaceMode = group->GetBool("CurveOnSurfaceMode", true);
642

643
  //Reference use: src/BOPTest/BOPTest_CheckCommands.cxx
644

645
  //I don't why we need to make a copy, but it doesn't work without it.
646
  //BRepAlgoAPI_Check also makes a copy of the shape.
647

648
  //didn't use BRepAlgoAPI_Check because it calls BRepCheck_Analyzer itself and
649
  //doesn't give us access to it. so I didn't want to run BRepCheck_Analyzer twice to get invalid results.
650

651
  //BOPAlgo_ArgumentAnalyzer can check 2 objects with respect to a boolean op.
652
  //this is left for another time.
653
  TopoDS_Shape BOPCopy = BRepBuilderAPI_Copy(shapeIn).Shape();
654
  BOPAlgo_ArgumentAnalyzer BOPCheck;
655

656
#if OCC_VERSION_HEX < 0x070500
657
  BOPCheck.SetProgressIndicator(theProgress);
658
#elif OCC_VERSION_HEX < 0x070600
659
  BOPCheck.SetProgressIndicator(theScope);
660
#else
661
  Q_UNUSED(theScope)
662
#endif // 0x070500
663

664

665
  BOPCheck.SetShape1(BOPCopy);
666
  //all settings are false by default. so only turn on what we want.
667
  BOPCheck.ArgumentTypeMode() = argumentTypeMode;
668
  BOPCheck.SelfInterMode() = selfInterMode;
669
  BOPCheck.SmallEdgeMode() = smallEdgeMode;
670
  BOPCheck.RebuildFaceMode() = rebuildFaceMode;
671
  BOPCheck.ContinuityMode() = continuityMode;
672

673
  BOPCheck.SetParallelMode(!runSingleThreaded); //this doesn't help for speed right now(occt 6.9.1).
674
  BOPCheck.SetRunParallel(!runSingleThreaded); //performance boost, use all available cores
675
  BOPCheck.TangentMode() = tangentMode; //these 4 new tests add about 5% processing time.
676
  BOPCheck.MergeVertexMode() = mergeVertexMode;
677
  BOPCheck.MergeEdgeMode() = mergeEdgeMode;
678
  BOPCheck.CurveOnSurfaceMode() = curveOnSurfaceMode;
679

680
#ifdef FC_DEBUG
681
  Base::TimeElapsed start_time;
682
#endif
683

684
  BOPCheck.Perform();
685

686
#ifdef FC_DEBUG
687
  float bopAlgoTime = Base::TimeElapsed::diffTimeF(start_time, Base::TimeElapsed());
688
  std::cout << std::endl << "BopAlgo check time is: " << bopAlgoTime << std::endl << std::endl;
689
#endif
690

691
  if (!BOPCheck.HasFaulty())
692
      return 0;
693

694
  ResultEntry *entry = new ResultEntry();
695
  entry->parent = theRoot;
696
  entry->shape = BOPCopy; //this will cause a problem, with existing entry. i.e. entry is true.
697
  entry->name = baseName;
698
  entry->type = shapeEnumToString(shapeIn.ShapeType());
699
  entry->error = QObject::tr("Invalid");
700
  entry->viewProviderRoot = currentSeparator;
701
  entry->viewProviderRoot->ref();
702
  goSetupResultBoundingBox(entry);
703
  theRoot->children.push_back(entry);
704

705
  const BOPAlgo_ListOfCheckResult &BOPResults = BOPCheck.GetCheckResult();
706
  BOPAlgo_ListIteratorOfListOfCheckResult BOPResultsIt(BOPResults);
707
  for (; BOPResultsIt.More(); BOPResultsIt.Next())
708
  {
709
    const BOPAlgo_CheckResult &current = BOPResultsIt.Value();
710
    const TopTools_ListOfShape &faultyShapes1 = current.GetFaultyShapes1();
711
    TopTools_ListIteratorOfListOfShape faultyShapes1It(faultyShapes1);
712

713
    for (;faultyShapes1It.More(); faultyShapes1It.Next())
714
    {
715
      const TopoDS_Shape &faultyShape = faultyShapes1It.Value();
716
      ResultEntry *faultyEntry = new ResultEntry();
717
      faultyEntry->parent = entry;
718
      faultyEntry->shape = faultyShape;
719
      faultyEntry->buildEntryName();
720
      faultyEntry->type = shapeEnumToString(faultyShape.ShapeType());
721
      faultyEntry->error = getBOPCheckString(current.GetCheckStatus());
722
      faultyEntry->viewProviderRoot = currentSeparator;
723
      entry->viewProviderRoot->ref();
724
      goSetupResultBoundingBox(faultyEntry);
725

726
      if (faultyShape.ShapeType() == TopAbs_FACE)
727
      {
728
        goSetupResultTypedSelection(faultyEntry, faultyShape, TopAbs_FACE);
729
      }
730
      else if (faultyShape.ShapeType() == TopAbs_EDGE)
731
      {
732
        goSetupResultTypedSelection(faultyEntry, faultyShape, TopAbs_EDGE);
733
      }
734
      else if (faultyShape.ShapeType() == TopAbs_VERTEX)
735
      {
736
        goSetupResultTypedSelection(faultyEntry, faultyShape, TopAbs_VERTEX);
737
      }
738
      entry->children.push_back(faultyEntry);
739

740
      /*log BOPCheck errors to report view*/
741
      if (logErrors){
742
          std::clog << faultyEntry->parent->name.toStdString().c_str() << " : "
743
                    << faultyEntry->name.toStdString().c_str() << " : "
744
                    << faultyEntry->type.toStdString().c_str() << " : "
745
                    << faultyEntry->error.toStdString().c_str()
746
                    << std::endl;
747
      }
748
    }
749
  }
750
  return 1;
751
}
752

753

754
void TaskCheckGeometryResults::dispatchError(ResultEntry *entry, const BRepCheck_Status &stat)
755
{
756
    std::vector<FunctionMapType>::iterator mapIt;
757
    for (mapIt = functionMap.begin(); mapIt != functionMap.end(); ++mapIt)
758
    {
759
        if (std::get<0>(*mapIt) == entry->shape.ShapeType() && std::get<1>(*mapIt) == stat)
760
        {
761
            (std::get<2>(*mapIt))(entry);
762
            return;
763
        }
764
    }
765
    goSetupResultBoundingBox(entry);
766
    ParameterGrp::handle group = App::GetApplication().GetUserParameter().
767
    GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod")->GetGroup("Part")->GetGroup("CheckGeometry");
768
    bool logErrors = group->GetBool("LogErrors", true); //log errors to report view
769

770
    /*log BRepCheck errors to report view*/
771
    if (logErrors){
772
        std::clog << entry->parent->name.toStdString().c_str() << " : "
773
                  << entry->name.toStdString().c_str() << " : "
774
                  << entry->type.toStdString().c_str() << " : "
775
                  << entry->error.toStdString().c_str() << " (BRepCheck)"
776
                  << std::endl;
777
    }
778
}
779

780
void TaskCheckGeometryResults::setupFunctionMap()
781
{
782
    functionMap.emplace_back(TopAbs_SHELL, BRepCheck_NotClosed, goSetupResultShellNotClosed);
783
    functionMap.emplace_back(TopAbs_WIRE, BRepCheck_NotClosed, goSetupResultWireNotClosed);
784
    functionMap.emplace_back(TopAbs_VERTEX, BRepCheck_InvalidPointOnCurve, goSetupResultInvalidPointCurve);
785
    functionMap.emplace_back(TopAbs_FACE, BRepCheck_IntersectingWires, goSetupResultIntersectingWires);
786
    functionMap.emplace_back(TopAbs_EDGE, BRepCheck_InvalidCurveOnSurface, goSetupResultInvalidCurveSurface);
787
    functionMap.emplace_back(TopAbs_EDGE, BRepCheck_InvalidSameParameterFlag, goSetupResultInvalidSameParameterFlag);
788
    functionMap.emplace_back(TopAbs_FACE, BRepCheck_UnorientableShape, goSetupResultUnorientableShapeFace);
789
}
790

791
void TaskCheckGeometryResults::currentRowChanged (const QModelIndex &current, const QModelIndex &previous)
792
{
793
    Gui::Selection().clearSelection();
794
    if (previous.isValid())
795
    {
796
        ResultEntry *entry = model->getEntry(previous);
797
        if (entry)
798
        {
799
            if (entry->boxSwitch)
800
                entry->boxSwitch->whichChild.setValue(SO_SWITCH_NONE);
801
        }
802
    }
803
    if (current.isValid())
804
    {
805
        ResultEntry *entry = model->getEntry(current);
806
        if (entry)
807
        {
808
            if (entry->boxSwitch)
809
                entry->boxSwitch->whichChild.setValue(0);
810
            QStringList::Iterator stringIt;
811
            for (stringIt = entry->selectionStrings.begin(); stringIt != entry->selectionStrings.end(); ++stringIt)
812
            {
813
                //need unique delimiter.
814
                QString doc, object, sub;
815
                if (!this->split((*stringIt), doc, object, sub))
816
                    continue;
817
                Gui::Selection().addSelection(doc.toLatin1(), object.toLatin1(), sub.toLatin1());
818
            }
819
        }
820
    }
821
}
822

823
bool TaskCheckGeometryResults::split(QString &input, QString &doc, QString &object, QString &sub)
824
{
825
    QStringList strings = input.split(QString::fromLatin1("."));
826
    if (strings.size() != 3)
827
        return false;
828
    doc = strings.at(0);
829
    object = strings.at(1);
830
    sub = strings.at(2);
831
    return true;
832
}
833

834
////////////////////////////////////////////////////////////////////////////////////////////////
835

836
QString PartGui::buildSelectionName(const ResultEntry *entry, const TopoDS_Shape &shape)
837
{
838
    const ResultEntry *parentEntry = entry;
839
    while(parentEntry->parent)
840
    {
841
        ResultEntry *temp = parentEntry->parent;
842
        if (!temp->parent)
843
          break;
844
        parentEntry = parentEntry->parent;
845
    }
846

847
    QString stringOut;
848
    QTextStream stream(&stringOut);
849
    stream << parentEntry->name;
850
    stream << '.';
851
    TopTools_IndexedMapOfShape shapeMap;
852
    int index(-1);
853

854
    switch (shape.ShapeType())
855
    {
856
    case TopAbs_FACE:
857
        TopExp::MapShapes(parentEntry->shape, TopAbs_FACE, shapeMap);
858
        stream << "Face";
859
        break;
860
    case TopAbs_EDGE:
861
        TopExp::MapShapes(parentEntry->shape, TopAbs_EDGE, shapeMap);
862
        stream << "Edge";
863
        break;
864
    case TopAbs_VERTEX:
865
        TopExp::MapShapes(parentEntry->shape, TopAbs_VERTEX, shapeMap);
866
        stream << "Vertex";
867
        break;
868
    default:
869
        stream << "Unexpected shape type";
870
        break;
871
    }
872

873
    index = shapeMap.FindIndex(shape);
874
    stream << index;
875
    return stringOut;
876
}
877

878
void PartGui::goSetupResultTypedSelection(ResultEntry* entry, const TopoDS_Shape& shape, TopAbs_ShapeEnum type)
879
{
880
    TopExp_Explorer it;
881
    for (it.Init(shape, type); it.More(); it.Next())
882
    {
883
        QString name = buildSelectionName(entry, (it.Current()));
884
        if (!name.isEmpty())
885
            entry->selectionStrings.append(name);
886
    }
887
}
888

889
void PartGui::goSetupResultBoundingBox(ResultEntry *entry)
890
{
891
    //empty compound throws bounding box error. Mantis #0001426
892
    try
893
    {
894
      Bnd_Box boundingBox;
895
      BRepBndLib::Add(entry->shape, boundingBox);
896
      Standard_Real xmin, ymin, zmin, xmax, ymax, zmax;
897
      boundingBox.Get(xmin, ymin, zmin, xmax, ymax, zmax);
898
      SbVec3f boundCenter((xmax - xmin)/2 + xmin, (ymax - ymin)/2 + ymin, (zmax - zmin)/2 + zmin);
899

900
      entry->boxSep = new SoSeparator();
901
      entry->viewProviderRoot->addChild(entry->boxSep);
902
      entry->boxSwitch = new SoSwitch();
903
      entry->boxSep->addChild(entry->boxSwitch);
904
      SoGroup *group = new SoGroup();
905
      entry->boxSwitch->addChild(group);
906
      entry->boxSwitch->whichChild.setValue(SO_SWITCH_NONE);
907
      SoDrawStyle *dStyle = new SoDrawStyle();
908
      dStyle->style.setValue(SoDrawStyle::LINES);
909
      dStyle->linePattern.setValue(0xc0c0);
910
      group->addChild(dStyle);
911
      SoMaterial *material = new SoMaterial();
912
      material->diffuseColor.setValue(255.0, 255.0, 255.0);
913
      material->ambientColor.setValue(255.0, 255.0, 255.0);
914
      group->addChild(material);
915

916
      SoResetTransform *reset = new SoResetTransform();
917
      group->addChild(reset);
918

919
      SoTransform *position = new SoTransform();
920
      position->translation.setValue(boundCenter);
921
      group->addChild(position);
922

923
      SoCube *cube = new SoCube();
924
      cube->width.setValue(xmax - xmin);
925
      cube->height.setValue(ymax - ymin);
926
      cube->depth.setValue(zmax - zmin);
927
      group->addChild(cube);
928
    }
929
    catch (const Standard_Failure &){}
930
}
931

932
void PartGui::goSetupResultShellNotClosed(ResultEntry *entry)
933
{
934
    ShapeAnalysis_FreeBounds shellCheck(entry->shape);
935
    TopoDS_Compound closedWires = shellCheck.GetClosedWires();
936
    TopoDS_Compound openWires = shellCheck.GetOpenWires();
937

938
    goSetupResultTypedSelection(entry, closedWires, TopAbs_EDGE);
939
    goSetupResultTypedSelection(entry, openWires, TopAbs_EDGE);
940

941
    goSetupResultBoundingBox(entry);
942
}
943

944
void PartGui::goSetupResultWireNotClosed(ResultEntry *entry)
945
{
946
    goSetupResultTypedSelection(entry, entry->shape, TopAbs_EDGE);
947
    goSetupResultBoundingBox(entry);
948
}
949

950
void PartGui::goSetupResultInvalidPointCurve(ResultEntry *entry)
951
{
952
    goSetupResultTypedSelection(entry, entry->shape, TopAbs_VERTEX);
953
    goSetupResultBoundingBox(entry);
954
}
955

956
void PartGui::goSetupResultIntersectingWires(ResultEntry *entry)
957
{
958
    goSetupResultTypedSelection(entry, entry->shape, TopAbs_FACE);
959
    goSetupResultBoundingBox(entry);
960
}
961

962
void PartGui::goSetupResultInvalidCurveSurface(ResultEntry *entry)
963
{
964
    goSetupResultTypedSelection(entry, entry->shape, TopAbs_EDGE);
965
    goSetupResultBoundingBox(entry);
966
}
967

968
void PartGui::goSetupResultInvalidSameParameterFlag(ResultEntry *entry)
969
{
970
    goSetupResultTypedSelection(entry, entry->shape, TopAbs_EDGE);
971
    goSetupResultBoundingBox(entry);
972
}
973

974
void PartGui::goSetupResultUnorientableShapeFace(ResultEntry *entry)
975
{
976
    goSetupResultTypedSelection(entry, entry->shape, TopAbs_FACE);
977
    goSetupResultBoundingBox(entry);
978
}
979

980
////////////////////////////////////////////////////////////////////////////////////////////////
981

982
TaskCheckGeometryDialog::TaskCheckGeometryDialog()
983
    : widget(nullptr), contentLabel(nullptr), okBtn(nullptr), settingsBtn(nullptr), resultsBtn(nullptr)
984
{
985
    ParameterGrp::handle group = App::GetApplication().GetUserParameter().
986
    GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod")->GetGroup("Part")->GetGroup("CheckGeometry");
987
    bool expandShapeContent = group->GetBool("ExpandShapeContent", false);
988

989
    this->setButtonPosition(TaskDialog::South);
990
    widget = new TaskCheckGeometryResults();
991

992
    taskbox = new Gui::TaskView::TaskBox(
993
        Gui::BitmapFactory().pixmap("Part_CheckGeometry"),
994
        widget->windowTitle(), true, nullptr);
995
    taskbox->groupLayout()->addWidget(widget);
996
    Content.push_back(taskbox);
997

998
    contentLabel = new QTextEdit();
999
    contentLabel->setText(widget->getShapeContentString());
1000
    shapeContentBox = new Gui::TaskView::TaskBox(Gui::BitmapFactory().pixmap("Part_CheckGeometry"),
1001
        tr("Shape Content"), true, nullptr);
1002
    shapeContentBox->groupLayout()->addWidget(contentLabel);
1003
    if (!expandShapeContent){
1004
        shapeContentBox->hideGroupBox();
1005
    }
1006
    Content.push_back(shapeContentBox);
1007

1008
    settingsBox = new Gui::TaskView::TaskBox(Gui::BitmapFactory().pixmap("Part_CheckGeometry"),
1009
        tr("Settings"), true, nullptr);
1010
    Content.push_back(settingsBox);
1011

1012
    autoRunCheckBox = new QCheckBox();
1013
    autoRunCheckBox->setText(tr("Skip this settings page"));
1014
    autoRunCheckBox->setToolTip(
1015
        tr("Skip this settings page and run the geometry check automatically.")
1016
        + QStringLiteral("\n")
1017
        + tr("Default: false"));
1018
    autoRunCheckBox->setChecked(group->GetBool("AutoRun", false));
1019
    connect(autoRunCheckBox, &QCheckBox::toggled,
1020
            this, &TaskCheckGeometryDialog::onAutoRunCheckBoxToggled);
1021
    settingsBox->groupLayout()->addWidget(autoRunCheckBox);
1022

1023
    runBOPCheckBox = new QCheckBox();
1024
    runBOPCheckBox->setText(tr("Run boolean operation check"));
1025
    runBOPCheckBox->setToolTip(tr(
1026
        "Extra boolean operations check that can sometimes find errors that\n"
1027
        "the standard BRep geometry check misses. These errors do not always\n"
1028
        "mean the checked object is unusable.  Default: false"));
1029
    runBOPCheckBox->setChecked(group->GetBool("RunBOPCheck", false));
1030
    connect(runBOPCheckBox, &QCheckBox::toggled,
1031
            this, &TaskCheckGeometryDialog::onRunBOPCheckBoxToggled);
1032
    settingsBox->groupLayout()->addWidget(runBOPCheckBox);
1033

1034
    runSingleThreadedCheckBox = new QCheckBox();
1035
    runSingleThreadedCheckBox->setText(tr("Single-threaded"));
1036
    runSingleThreadedCheckBox->setToolTip(tr(
1037
        "Run the geometry check in a single thread.  This is slower,\n"
1038
        "but more stable.  Default: false"));
1039
    runSingleThreadedCheckBox->setChecked(group->GetBool("RunSingleThreaded", false));
1040
    connect(runSingleThreadedCheckBox, &QCheckBox::toggled,
1041
            this, &TaskCheckGeometryDialog::onRunSingleThreadedCheckBoxToggled);
1042
    settingsBox->groupLayout()->addWidget(runSingleThreadedCheckBox);
1043

1044
    logErrorsCheckBox = new QCheckBox();
1045
    logErrorsCheckBox->setText(tr("Log errors"));
1046
    logErrorsCheckBox->setToolTip(tr("Log errors to report view.  Default: true"));
1047
    logErrorsCheckBox->setChecked(group->GetBool("LogErrors", true));
1048
    connect(logErrorsCheckBox, &QCheckBox::toggled,
1049
            this, &TaskCheckGeometryDialog::onLogErrorsCheckBoxToggled);
1050
    settingsBox->groupLayout()->addWidget(logErrorsCheckBox);
1051

1052
    expandShapeContentCheckBox = new QCheckBox();
1053
    expandShapeContentCheckBox->setText(tr("Expand shape content"));
1054
    expandShapeContentCheckBox->setToolTip(tr(
1055
        "Expand shape content.  Changes will take effect next time you use \n"
1056
        "the check geometry tool.  Default: false"));
1057
    expandShapeContentCheckBox->setChecked(group->GetBool("ExpandShapeContent", false));
1058
    connect(expandShapeContentCheckBox, &QCheckBox::toggled,
1059
            this, &TaskCheckGeometryDialog::onExpandShapeContentCheckBoxToggled);
1060
    settingsBox->groupLayout()->addWidget(expandShapeContentCheckBox);
1061

1062
    advancedShapeContentCheckBox = new QCheckBox();
1063
    advancedShapeContentCheckBox->setText(tr("Advanced shape content"));
1064
    advancedShapeContentCheckBox->setToolTip(tr(
1065
        "Show advanced shape content.  Changes will take effect next time you use \n"
1066
        "the check geometry tool.  Default: false"));
1067
    advancedShapeContentCheckBox->setChecked(group->GetBool("AdvancedShapeContent", true));
1068
    connect(advancedShapeContentCheckBox, &QCheckBox::toggled,
1069
            this, &TaskCheckGeometryDialog::onAdvancedShapeContentCheckBoxToggled);
1070
    settingsBox->groupLayout()->addWidget(advancedShapeContentCheckBox);
1071

1072
    settingsBox->groupLayout()->addWidget(new QLabel(tr("\nIndividual boolean operation checks:")));
1073

1074
    argumentTypeModeCheckBox = new QCheckBox();
1075
    argumentTypeModeCheckBox->setText(QStringLiteral("  ") + tr("Bad type"));
1076
    argumentTypeModeCheckBox->setToolTip(tr("Check for bad argument types.  Default: true"));
1077
    argumentTypeModeCheckBox->setChecked(group->GetBool("ArgumentTypeMode", true));
1078
    connect(argumentTypeModeCheckBox, &QCheckBox::toggled,
1079
            this, &TaskCheckGeometryDialog::onArgumentTypeModeCheckBoxToggled);
1080
    settingsBox->groupLayout()->addWidget(argumentTypeModeCheckBox);
1081

1082
    selfInterModeCheckBox = new QCheckBox();
1083
    selfInterModeCheckBox->setText(QStringLiteral("  ") + tr("Self-intersect"));
1084
    selfInterModeCheckBox->setToolTip(tr("Check for self-intersections.  Default: true"));
1085
    selfInterModeCheckBox->setChecked(group->GetBool("SelfInterMode", true));
1086
    connect(selfInterModeCheckBox, &QCheckBox::toggled,
1087
            this, &TaskCheckGeometryDialog::onSelfInterModeCheckBoxToggled);
1088
    settingsBox->groupLayout()->addWidget(selfInterModeCheckBox);
1089

1090
    smallEdgeModeCheckBox = new QCheckBox();
1091
    smallEdgeModeCheckBox->setText(QStringLiteral("  ") + tr("Too small edge"));
1092
    smallEdgeModeCheckBox->setToolTip(tr("Check for edges that are too small.  Default: true"));
1093
    smallEdgeModeCheckBox->setChecked(group->GetBool("SmallEdgeMode", true));
1094
    connect(smallEdgeModeCheckBox, &QCheckBox::toggled,
1095
            this, &TaskCheckGeometryDialog::onSmallEdgeModeCheckBoxToggled);
1096
    settingsBox->groupLayout()->addWidget(smallEdgeModeCheckBox);
1097

1098
    rebuildFaceModeCheckBox = new QCheckBox();
1099
    rebuildFaceModeCheckBox->setText(QStringLiteral("  ") + tr("Nonrecoverable face"));
1100
    rebuildFaceModeCheckBox->setToolTip(tr("Check for nonrecoverable faces.  Default: true"));
1101
    rebuildFaceModeCheckBox->setChecked(group->GetBool("RebuildFaceMode", true));
1102
    connect(rebuildFaceModeCheckBox, &QCheckBox::toggled,
1103
            this, &TaskCheckGeometryDialog::onRebuildFaceModeCheckBoxToggled);
1104
    settingsBox->groupLayout()->addWidget(rebuildFaceModeCheckBox);
1105

1106
    continuityModeCheckBox = new QCheckBox();
1107
    continuityModeCheckBox->setText(QStringLiteral("  ") + tr("Continuity"));
1108
    continuityModeCheckBox->setToolTip(tr("Check for continuity.  Default: true"));
1109
    continuityModeCheckBox->setChecked(group->GetBool("ContinuityMode", true));
1110
    connect(continuityModeCheckBox, &QCheckBox::toggled,
1111
            this, &TaskCheckGeometryDialog::onContinuityModeCheckBoxToggled);
1112
    settingsBox->groupLayout()->addWidget(continuityModeCheckBox);
1113

1114
    tangentModeCheckBox = new QCheckBox();
1115
    tangentModeCheckBox->setText(QStringLiteral("  ") + tr("Incompatibility of face"));
1116
    tangentModeCheckBox->setToolTip(tr("Check for incompatible faces.  Default: true"));
1117
    tangentModeCheckBox->setChecked(group->GetBool("TangentMode", true));
1118
    connect(tangentModeCheckBox, &QCheckBox::toggled,
1119
            this, &TaskCheckGeometryDialog::onTangentModeCheckBoxToggled);
1120
    settingsBox->groupLayout()->addWidget(tangentModeCheckBox);
1121

1122
    mergeVertexModeCheckBox = new QCheckBox();
1123
    mergeVertexModeCheckBox->setText(QStringLiteral("  ") + tr("Incompatibility of vertex"));
1124
    mergeVertexModeCheckBox->setToolTip(tr("Check for incompatible vertices.  Default: true"));
1125
    mergeVertexModeCheckBox->setChecked(group->GetBool("MergeVertexMode", true));
1126
    connect(mergeVertexModeCheckBox, &QCheckBox::toggled,
1127
            this, &TaskCheckGeometryDialog::onMergeVertexModeCheckBoxToggled);
1128
    settingsBox->groupLayout()->addWidget(mergeVertexModeCheckBox);
1129

1130
    mergeEdgeModeCheckBox = new QCheckBox();
1131
    mergeEdgeModeCheckBox->setText(QStringLiteral("  ") + tr("Incompatibility of edge"));
1132
    mergeEdgeModeCheckBox->setToolTip(tr("Check for incompatible edges.  Default: true"));
1133
    mergeEdgeModeCheckBox->setChecked(group->GetBool("MergeEdgeMode", true));
1134
    connect(mergeEdgeModeCheckBox, &QCheckBox::toggled,
1135
            this, &TaskCheckGeometryDialog::onMergeEdgeModeCheckBoxToggled);
1136
    settingsBox->groupLayout()->addWidget(mergeEdgeModeCheckBox);
1137

1138
    curveOnSurfaceModeCheckBox = new QCheckBox();
1139
    curveOnSurfaceModeCheckBox->setText(QStringLiteral("  ") + tr("Invalid curve on surface"));
1140
    curveOnSurfaceModeCheckBox->setToolTip(tr("Check for invalid curves on surfaces.  Default: true"));
1141
    curveOnSurfaceModeCheckBox->setChecked(group->GetBool("CurveOnSurfaceMode", true));
1142
    connect(curveOnSurfaceModeCheckBox, &QCheckBox::toggled,
1143
            this, &TaskCheckGeometryDialog::onCurveOnSurfaceModeCheckBoxToggled);
1144
    settingsBox->groupLayout()->addWidget(curveOnSurfaceModeCheckBox);
1145
    if (group->GetBool("AutoRun",false)){
1146
        settingsBox->hide();
1147
        widget->goCheck();
1148
        contentLabel->setText(widget->getShapeContentString());
1149
    } else {
1150
        taskbox->hide();
1151
        shapeContentBox->hide();
1152
    }
1153
}
1154

1155
bool TaskCheckGeometryDialog::accept()
1156
{
1157
    settingsBtn->setEnabled(true);
1158
    settingsBox->hide();
1159
    shapeContentBox->show();
1160
    taskbox->show();
1161
    widget->goCheck();
1162
    QScrollBar *v = contentLabel->verticalScrollBar();
1163
    v->setValue(v->maximum()); //scroll to bottom
1164
    int curval = v->value(); //save position
1165
    contentLabel->setText(widget->getShapeContentString());
1166
    v->setValue(curval+(v->maximum()-curval)/5);
1167
    return false;
1168
}
1169

1170
bool TaskCheckGeometryDialog::reject()
1171
{
1172
    return true;
1173
}
1174

1175
void TaskCheckGeometryDialog::onClicked(QAbstractButton *btn)
1176
{
1177
    /** when ok (run check) is clicked or when close is clicked
1178
     *  the appropriate accept() or reject() is called already
1179
     *  all we need to do here is enable / disable / show / hide
1180
     *  ui elements
1181
     */
1182

1183
    if(btn == okBtn){
1184
        settingsBtn->setEnabled(true);
1185
    } else if (btn == settingsBtn){
1186
        settingsBtn->setEnabled(false);
1187
        taskbox->hide();
1188
        shapeContentBox->hide();
1189
        settingsBox->show();
1190
        resultsBtn->setEnabled(true);
1191
    } else if (btn == resultsBtn){
1192
        settingsBtn->setEnabled(true);
1193
        taskbox->show();
1194
        shapeContentBox->show();
1195
        settingsBox->hide();
1196
        resultsBtn->setEnabled(false);
1197
    }
1198
}
1199

1200
void TaskCheckGeometryDialog::modifyStandardButtons(QDialogButtonBox* box)
1201
{
1202
    okBtn = box->button(QDialogButtonBox::Ok);
1203
    okBtn->setText(tr("Run check"));
1204
    settingsBtn = box->addButton(tr("Settings"),QDialogButtonBox::ActionRole);
1205
    ParameterGrp::handle group = App::GetApplication().GetUserParameter().
1206
    GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod")->GetGroup("Part")->GetGroup("CheckGeometry");
1207
    if(!group->GetBool("AutoRun",false))
1208
        settingsBtn->setEnabled(false);
1209
    resultsBtn = box->addButton(tr("Results"),QDialogButtonBox::ActionRole);
1210
    resultsBtn->setEnabled(false);
1211
    connect(box, &QDialogButtonBox::clicked, this, &TaskCheckGeometryDialog::onClicked);
1212
}
1213

1214
void TaskCheckGeometryDialog::onAutoRunCheckBoxToggled(bool isOn)
1215
{
1216
    ParameterGrp::handle group = App::GetApplication().GetUserParameter().
1217
    GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod")->GetGroup("Part")->GetGroup("CheckGeometry");
1218
    group->SetBool("AutoRun", isOn);
1219
}
1220

1221
void TaskCheckGeometryDialog::onRunBOPCheckBoxToggled(bool isOn)
1222
{
1223
    ParameterGrp::handle group = App::GetApplication().GetUserParameter().
1224
    GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod")->GetGroup("Part")->GetGroup("CheckGeometry");
1225
    group->SetBool("RunBOPCheck", isOn);
1226
}
1227

1228
void TaskCheckGeometryDialog::onRunSingleThreadedCheckBoxToggled(bool isOn)
1229
{
1230
    ParameterGrp::handle group = App::GetApplication().GetUserParameter().
1231
    GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod")->GetGroup("Part")->GetGroup("CheckGeometry");
1232
    group->SetBool("RunSingleThreaded", isOn);
1233
}
1234

1235
void TaskCheckGeometryDialog::onLogErrorsCheckBoxToggled(bool isOn)
1236
{
1237
    ParameterGrp::handle group = App::GetApplication().GetUserParameter().
1238
    GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod")->GetGroup("Part")->GetGroup("CheckGeometry");
1239
    group->SetBool("LogErrors", isOn);
1240
}
1241

1242
void TaskCheckGeometryDialog::onArgumentTypeModeCheckBoxToggled(bool isOn)
1243
{
1244
    ParameterGrp::handle group = App::GetApplication().GetUserParameter().
1245
    GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod")->GetGroup("Part")->GetGroup("CheckGeometry");
1246
    group->SetBool("ArgumentTypeMode", isOn);
1247
}
1248

1249
void TaskCheckGeometryDialog::onExpandShapeContentCheckBoxToggled(bool isOn)
1250
{
1251
    ParameterGrp::handle group = App::GetApplication().GetUserParameter().
1252
    GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod")->GetGroup("Part")->GetGroup("CheckGeometry");
1253
    group->SetBool("ExpandShapeContent", isOn);
1254
}
1255

1256
void TaskCheckGeometryDialog::onAdvancedShapeContentCheckBoxToggled(bool isOn)
1257
{
1258
    ParameterGrp::handle group = App::GetApplication().GetUserParameter().
1259
    GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod")->GetGroup("Part")->GetGroup("CheckGeometry");
1260
    group->SetBool("AdvancedShapeContent", isOn);
1261
}
1262

1263
void TaskCheckGeometryDialog::onSelfInterModeCheckBoxToggled(bool isOn)
1264
{
1265
    ParameterGrp::handle group = App::GetApplication().GetUserParameter().
1266
    GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod")->GetGroup("Part")->GetGroup("CheckGeometry");
1267
    group->SetBool("SelfInterMode", isOn);
1268
}
1269

1270
void TaskCheckGeometryDialog::onSmallEdgeModeCheckBoxToggled(bool isOn)
1271
{
1272
    ParameterGrp::handle group = App::GetApplication().GetUserParameter().
1273
    GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod")->GetGroup("Part")->GetGroup("CheckGeometry");
1274
    group->SetBool("SmallEdgeMode", isOn);
1275
}
1276

1277
void TaskCheckGeometryDialog::onRebuildFaceModeCheckBoxToggled(bool isOn)
1278
{
1279
    ParameterGrp::handle group = App::GetApplication().GetUserParameter().
1280
    GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod")->GetGroup("Part")->GetGroup("CheckGeometry");
1281
    group->SetBool("RebuildFaceMode", isOn);
1282
}
1283

1284
void TaskCheckGeometryDialog::onContinuityModeCheckBoxToggled(bool isOn)
1285
{
1286
    ParameterGrp::handle group = App::GetApplication().GetUserParameter().
1287
    GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod")->GetGroup("Part")->GetGroup("CheckGeometry");
1288
    group->SetBool("ContinuityMode", isOn);
1289
}
1290

1291
void TaskCheckGeometryDialog::onTangentModeCheckBoxToggled(bool isOn)
1292
{
1293
    ParameterGrp::handle group = App::GetApplication().GetUserParameter().
1294
    GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod")->GetGroup("Part")->GetGroup("CheckGeometry");
1295
    group->SetBool("TangentMode", isOn);
1296
}
1297

1298
void TaskCheckGeometryDialog::onMergeVertexModeCheckBoxToggled(bool isOn)
1299
{
1300
    ParameterGrp::handle group = App::GetApplication().GetUserParameter().
1301
    GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod")->GetGroup("Part")->GetGroup("CheckGeometry");
1302
    group->SetBool("MergeVertexMode", isOn);
1303
}
1304

1305
void TaskCheckGeometryDialog::onMergeEdgeModeCheckBoxToggled(bool isOn)
1306
{
1307
    ParameterGrp::handle group = App::GetApplication().GetUserParameter().
1308
    GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod")->GetGroup("Part")->GetGroup("CheckGeometry");
1309
    group->SetBool("MergeEdgeMode", isOn);
1310
}
1311

1312
void TaskCheckGeometryDialog::onCurveOnSurfaceModeCheckBoxToggled(bool isOn)
1313
{
1314
    ParameterGrp::handle group = App::GetApplication().GetUserParameter().
1315
    GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod")->GetGroup("Part")->GetGroup("CheckGeometry");
1316
    group->SetBool("CurveOnSurfaceMode", isOn);
1317
}
1318

1319
TaskCheckGeometryDialog::~TaskCheckGeometryDialog()
1320
{
1321
  if (widget)
1322
  {
1323
    delete widget;
1324
    widget = nullptr;
1325
  }
1326
  if (contentLabel)
1327
  {
1328
    delete contentLabel;
1329
    contentLabel = nullptr;
1330
  }
1331
}
1332

1333
////////////////////////////////////////////////////////////////////////////////////////////////
1334

1335
BOPProgressIndicator::BOPProgressIndicator (const QString& title, QWidget* parent)
1336
{
1337
    steps = 0;
1338
    canceled = false;
1339

1340
    myProgress = new QProgressDialog(parent);
1341
    myProgress->setWindowTitle(title);
1342
    myProgress->setAttribute(Qt::WA_DeleteOnClose);
1343
}
1344

1345
BOPProgressIndicator::~BOPProgressIndicator ()
1346
{
1347
    myProgress->close();
1348
}
1349

1350
#if OCC_VERSION_HEX < 0x070500
1351
Standard_Boolean BOPProgressIndicator::Show (const Standard_Boolean theForce)
1352
{
1353
    if (theForce) {
1354
        steps = 0;
1355
        canceled = false;
1356

1357
        time.start();
1358
        myProgress->show();
1359

1360
        myProgress->setRange(0, 0);
1361
        myProgress->setValue(0);
1362
    }
1363
    else {
1364
        Handle(TCollection_HAsciiString) aName = GetScope(1).GetName(); //current step
1365
        if (!aName.IsNull())
1366
            myProgress->setLabelText (QString::fromUtf8(aName->ToCString()));
1367
    }
1368

1369
    return Standard_True;
1370
}
1371
#else
1372
void BOPProgressIndicator::Show (const Message_ProgressScope& theScope,
1373
                                 const Standard_Boolean isForce)
1374
{
1375
    Standard_CString aName = theScope.Name(); //current step
1376
    myProgress->setLabelText (QString::fromUtf8(aName));
1377

1378
    if (isForce) {
1379
        myProgress->show();
1380
    }
1381

1382
    QCoreApplication::processEvents();
1383
}
1384

1385
void BOPProgressIndicator::Reset()
1386
{
1387
    steps = 0;
1388
    canceled = false;
1389

1390
    time.start();
1391

1392
    myProgress->setRange(0, 0);
1393
    myProgress->setValue(0);
1394
}
1395
#endif
1396

1397
Standard_Boolean BOPProgressIndicator::UserBreak()
1398
{
1399
    QThread *currentThread = QThread::currentThread();
1400
    if (currentThread == myProgress->thread()) {
1401
        // this is needed to check the status outside BOPAlgo_ArgumentAnalyzer
1402
        //
1403
        // Hint: We must make sure to do this only when calling from the GUI
1404
        // thread because when calling it from a worker thread the thrown
1405
        // exception isn't handled anywhere and thus std::terminate is called
1406
        if (canceled)
1407
            return Standard_True;
1408

1409
        // it suffices to update only every second
1410
        // to avoid to unnecessarily process events
1411
        steps++;
1412
        myProgress->setValue(steps);
1413
        if (time.elapsed() > 1000) {
1414
            time.restart();
1415
            QCoreApplication::processEvents();
1416

1417
            canceled = myProgress->wasCanceled();
1418
            return canceled;
1419
        }
1420
    }
1421

1422
    return Standard_False;
1423
}
1424

1425
#include "moc_TaskCheckGeometry.cpp"
1426

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

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

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

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