FreeCAD

Форк
0
/
TopoShape.cpp 
4167 строк · 149.5 Кб
1
/***************************************************************************
2
 *   Copyright (c) 2002 Jürgen Riegel <juergen.riegel@web.de>              *
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

25
#ifndef _PreComp_
26
# include <array>
27
# include <cmath>
28
# include <cstdlib>
29
# include <sstream>
30
# include <boost/regex.hpp>
31

32
# include <APIHeaderSection_MakeHeader.hxx>
33
# include <BinTools.hxx>
34
# include <BinTools_ShapeSet.hxx>
35
# include <Bnd_Box.hxx>
36
# include <BRep_Builder.hxx>
37
# include <BRep_Tool.hxx>
38
# include <BRepAdaptor_Surface.hxx>
39
# include <BRepAlgoAPI_Common.hxx>
40
# include <BRepAdaptor_CompCurve.hxx>
41
# include <BRepAdaptor_Curve.hxx>
42
# include <BRepAlgoAPI_Cut.hxx>
43
# include <BRepAlgoAPI_Fuse.hxx>
44
# include <BRepAlgoAPI_Section.hxx>
45
# include <BRepBndLib.hxx>
46
# include <BRepBuilderAPI_Copy.hxx>
47
# include <BRepBuilderAPI_FaceError.hxx>
48
# include <BRepBuilderAPI_GTransform.hxx>
49
# include <BRepBuilderAPI_MakeEdge.hxx>
50
# include <BRepBuilderAPI_MakeFace.hxx>
51
# include <BRepBuilderAPI_MakeSolid.hxx>
52
# include <BRepBuilderAPI_MakeVertex.hxx>
53
# include <BRepBuilderAPI_MakeWire.hxx>
54
# include <BRepBuilderAPI_NurbsConvert.hxx>
55
# include <BRepBuilderAPI_Sewing.hxx>
56
# include <BRepBuilderAPI_Transform.hxx>
57
# include <BRepCheck_Analyzer.hxx>
58
# include <BRepClass_FaceClassifier.hxx>
59
# include <BRepCheck_ListIteratorOfListOfStatus.hxx>
60
# include <BRepCheck_Result.hxx>
61
# include <BRepFill_CompatibleWires.hxx>
62
# include <BRepGProp.hxx>
63
# include <BRepGProp_Face.hxx>
64
# include <BRepLib.hxx>
65
# include <BRepLib_FindSurface.hxx>
66
# include <BRepLProp_SLProps.hxx>
67
# include <BRepMesh_IncrementalMesh.hxx>
68
# include <BRepOffsetAPI_MakeOffset.hxx>
69
# include <BRepOffsetAPI_MakeOffsetShape.hxx>
70
# include <BRepOffsetAPI_MakePipe.hxx>
71
# include <BRepOffsetAPI_MakePipeShell.hxx>
72
# include <BRepOffsetAPI_ThruSections.hxx>
73
# include <BRepOffsetAPI_MakeThickSolid.hxx>
74
# include <BRepPrimAPI_MakePrism.hxx>
75
# include <BRepPrimAPI_MakeRevol.hxx>
76
# include <BRepPrimAPI_MakeTorus.hxx>
77
# include <BRepTools.hxx>
78
# include <BRepTools_ReShape.hxx>
79
# include <BRepTools_ShapeSet.hxx>
80
# include <BRepTools_WireExplorer.hxx>
81
# include <BSplCLib.hxx>
82
# include <GCE2d_MakeSegment.hxx>
83
# include <GCPnts_UniformAbscissa.hxx>
84
# include <Geom_BezierCurve.hxx>
85
# include <Geom_BezierSurface.hxx>
86
# include <Geom_BSplineCurve.hxx>
87
# include <Geom_BSplineSurface.hxx>
88
# include <Geom_CartesianPoint.hxx>
89
# include <Geom_Circle.hxx>
90
# include <Geom_ConicalSurface.hxx>
91
# include <Geom_CylindricalSurface.hxx>
92
# include <Geom_Ellipse.hxx>
93
# include <Geom_Hyperbola.hxx>
94
# include <Geom_Line.hxx>
95
# include <Geom_Parabola.hxx>
96
# include <Geom_Plane.hxx>
97
# include <Geom_SphericalSurface.hxx>
98
# include <Geom_SurfaceOfLinearExtrusion.hxx>
99
# include <Geom_SurfaceOfRevolution.hxx>
100
# include <Geom_ToroidalSurface.hxx>
101
# include <Geom2d_Ellipse.hxx>
102
# include <Geom2d_Line.hxx>
103
# include <Geom2d_TrimmedCurve.hxx>
104
# include <GeomFill_CorrectedFrenet.hxx>
105
# include <GeomFill_CurveAndTrihedron.hxx>
106
# include <GeomFill_EvolvedSection.hxx>
107
# include <GeomFill_Pipe.hxx>
108
# include <GeomFill_SectionLaw.hxx>
109
# include <GeomFill_Sweep.hxx>
110
# include <GeomLib.hxx>
111
# include <GeomLib_IsPlanarSurface.hxx>
112
# include <gp_Circ.hxx>
113
# include <gp_Pln.hxx>
114
# include <GProp_GProps.hxx>
115
# include <IGESControl_Controller.hxx>
116
# include <IGESControl_Reader.hxx>
117
# include <IGESControl_Writer.hxx>
118
# include <IGESData_GlobalSection.hxx>
119
# include <IGESData_IGESModel.hxx>
120
# include <Interface_Static.hxx>
121
# include <Law_BSpline.hxx>
122
# include <Law_BSpFunc.hxx>
123
# include <Law_Constant.hxx>
124
# include <ShapeAnalysis_FreeBoundsProperties.hxx>
125
# include <ShapeExtend_Explorer.hxx>
126
# include <ShapeFix_Shape.hxx>
127
# include <ShapeUpgrade_RemoveInternalWires.hxx>
128
# include <ShapeUpgrade_ShellSewing.hxx>
129
# include <Standard_Failure.hxx>
130
# include <Standard_Version.hxx>
131
# include <STEPControl_Reader.hxx>
132
# include <STEPControl_Writer.hxx>
133
# include <StlAPI_Writer.hxx>
134
# include <TopoDS.hxx>
135
# include <TopoDS_Compound.hxx>
136
# include <TopoDS_Iterator.hxx>
137
# include <TopoDS_Solid.hxx>
138
# include <TopoDS_Vertex.hxx>
139
# include <TopExp.hxx>
140
# include <TopExp_Explorer.hxx>
141
# include <TopTools_ListIteratorOfListOfShape.hxx>
142
# include <TopTools_HSequenceOfShape.hxx>
143
# include <Transfer_FinderProcess.hxx>
144
# include <Transfer_TransientProcess.hxx>
145
# include <XSControl_TransferWriter.hxx>
146
# include <XSControl_WorkSession.hxx>
147

148

149
# include <BOPAlgo_ArgumentAnalyzer.hxx>
150
# include <BOPAlgo_ListOfCheckResult.hxx>
151

152
# include <BRepAlgoAPI_Defeaturing.hxx>
153

154
#if OCC_VERSION_HEX < 0x070600
155
# include <BRepAdaptor_HCurve.hxx>
156
# include <BRepAdaptor_HCompCurve.hxx>
157
#endif
158

159
# include <boost/algorithm/string/predicate.hpp>
160
# include <boost/core/ignore_unused.hpp>
161
#endif // _PreComp_
162

163
#include <App/Material.h>
164
#include <App/ElementNamingUtils.h>
165
#include <Base/BoundBox.h>
166
#include <Base/Builder3D.h>
167
#include <Base/Console.h>
168
#include <Base/Exception.h>
169
#include <Base/Placement.h>
170
#include <Base/Tools.h>
171
#include <Base/Reader.h>
172
#include <Base/Writer.h>
173

174
#include "TopoShape.h"
175
#include "BRepMesh.h"
176
#include "BRepOffsetAPI_MakeOffsetFix.h"
177
#include "CrossSection.h"
178
#include "encodeFilename.h"
179
#include "FaceMakerBullseye.h"
180
#include "Interface.h"
181
#include "modelRefine.h"
182
#include "PartPyCXX.h"
183
#include "ProgressIndicator.h"
184
#include "Tools.h"
185
#include "TopoShapeCompoundPy.h"
186
#include "TopoShapeCompSolidPy.h"
187
#include "TopoShapeEdgePy.h"
188
#include "TopoShapeFacePy.h"
189
#include "TopoShapeShellPy.h"
190
#include "TopoShapeSolidPy.h"
191
#include "TopoShapeVertexPy.h"
192
#include "TopoShapeWirePy.h"
193

194

195
FC_LOG_LEVEL_INIT("TopoShape",true,true)
196

197
using namespace Part;
198

199
const char* BRepBuilderAPI_FaceErrorText(BRepBuilderAPI_FaceError et)
200
{
201
    switch (et)
202
    {
203
    case BRepBuilderAPI_FaceDone:
204
        return "Construction was successful";
205
    case BRepBuilderAPI_NoFace:
206
        return "No face";
207
    case BRepBuilderAPI_NotPlanar:
208
        return "Face is not planar";
209
    case BRepBuilderAPI_CurveProjectionFailed:
210
        return "Curve projection failed";
211
    case BRepBuilderAPI_ParametersOutOfRange:
212
        return "Parameters out of range";
213
    default:
214
        return "Unknown creation error";
215
    }
216
}
217

218
/**
219
 * Derive a roughly proportional default angular deflection from a linear
220
 * tolerance. This is a work-around for unreliable linear tolerance enforcement
221
 * in OCC, especially on nurbs surfaces. The intention is to provide sane
222
 * baseline (default) behavior with linear tolerances only, until OCC is fixed.
223
 * If needed for specific use cases, explicit angular deflection parameters can
224
 * still be exposed separately.
225
 */
226
inline double defaultAngularDeflection(double linearTolerance) {
227
    // Default OCC angular deflection is 0.5 radians, or about 28.6 degrees.
228
    // That is a bit coarser than necessary for performance, so we default to at
229
    // most 0.1 radians, or 5.7 degrees. We also do not go finer than 0.005, or
230
    // roughly 0.28 degree angular resolution, to avoid performance tanking
231
    // completely at very fine resolutions.
232
    return std::min(0.1, linearTolerance * 5 + 0.005);
233
}
234

235
// ------------------------------------------------
236

237
NullShapeException::NullShapeException()
238
  : ValueError()
239
{
240
}
241

242
NullShapeException::NullShapeException(const char * sMessage)
243
  : ValueError(sMessage)
244
{
245
}
246

247
NullShapeException::NullShapeException(const std::string& sMessage)
248
  : ValueError(sMessage)
249
{
250
}
251

252
// ------------------------------------------------
253

254
BooleanException::BooleanException()
255
  : CADKernelError()
256
{
257
}
258

259
BooleanException::BooleanException(const char * sMessage)
260
  : CADKernelError(sMessage)
261
{
262
}
263

264
BooleanException::BooleanException(const std::string& sMessage)
265
  : CADKernelError(sMessage)
266
{
267
}
268

269
// ------------------------------------------------
270

271
TYPESYSTEM_SOURCE(Part::ShapeSegment , Data::Segment)
272

273
std::string ShapeSegment::getName() const
274
{
275
    return {};
276
}
277

278
// ------------------------------------------------
279

280
TYPESYSTEM_SOURCE(Part::TopoShape , Data::ComplexGeoData)
281

282

283
TopoShape::~TopoShape() = default;
284

285
TopoShape::TopoShape(long tag,App::StringHasherRef hasher, const TopoDS_Shape &shape)
286
    :_Shape(*this, shape)
287
{
288
    Tag = tag;
289
    Hasher = hasher;
290
}
291

292
TopoShape::TopoShape(const TopoDS_Shape &shape, long tag, App::StringHasherRef hasher)
293
    : _Shape(*this, shape)
294
{
295
    Tag = tag;
296
    Hasher = hasher;
297
}
298

299
TopoShape::TopoShape(const TopoShape& shape)
300
    : _Shape(*this)
301
{
302
    *this = shape;
303
}
304

305
std::pair<std::string, unsigned long> TopoShape::getElementTypeAndIndex(const char* Name)
306
{
307
    int index = 0;
308
    std::string element;
309
    boost::regex ex("^(Face|Edge|Vertex)([1-9][0-9]*)$");
310
    boost::cmatch what;
311

312
    if (Name && boost::regex_match(Name, what, ex)) {
313
        element = what[1].str();
314
        index = std::atoi(what[2].str().c_str());
315
    }
316

317
    return std::make_pair(element, index);
318
}
319

320
std::vector<const char*> TopoShape::getElementTypes() const
321
{
322
    static const std::vector<const char*> temp = {"Face","Edge","Vertex"};
323
    return temp;
324
}
325

326
unsigned long TopoShape::countSubElements(const char* Type) const
327
{
328
    return countSubShapes(Type);
329
}
330

331
Data::Segment* TopoShape::getSubElement(const char* Type, unsigned long n) const
332
{
333
    std::stringstream str;
334
    str << Type << n;
335
    std::string temp = str.str();
336
    return new ShapeSegment(getSubShape(temp.c_str()));
337
}
338

339
// Type can be (should be?) a subshape name, not a type, E.G. Edge3
340
TopoDS_Shape TopoShape::getSubShape(const char* Type, bool silent) const {
341
    TopoShape s(*this);
342
    s.Tag = 0;
343
    return s.getSubTopoShape(Type,silent).getShape();
344
}
345

346
TopoDS_Shape TopoShape::getSubShape(TopAbs_ShapeEnum type, int idx, bool silent) const {
347
    TopoShape s(*this);
348
    s.Tag = 0;
349
    return s.getSubTopoShape(type,idx,silent).getShape();
350
}
351

352

353
unsigned long TopoShape::countSubShapes(const char* Type) const
354
{
355
    if(!Type)
356
        return 0;
357
    if(strcmp(Type,"SubShape")==0)
358
        return countSubShapes(TopAbs_SHAPE);
359
    auto type = shapeType(Type,true);
360
    if(type == TopAbs_SHAPE)
361
        return 0;
362
    return countSubShapes(type);
363
}
364

365
unsigned long TopoShape::countSubShapes(TopAbs_ShapeEnum Type) const
366
{
367
    if(Type == TopAbs_SHAPE) {
368
        int count = 0;
369
        for(TopoDS_Iterator it(_Shape);it.More();it.Next())
370
            ++count;
371
        return count;
372
    }
373
    TopTools_IndexedMapOfShape anIndices;
374
    TopExp::MapShapes(this->_Shape, Type, anIndices);
375
    return anIndices.Extent();
376
}
377

378
bool TopoShape::hasSubShape(TopAbs_ShapeEnum type) const {
379
    if(type == TopAbs_SHAPE) {
380
        TopoDS_Iterator it(_Shape);
381
        return !!it.More();
382
    }
383
    TopExp_Explorer exp(_Shape,type);
384
    return !!exp.More();
385
}
386

387
bool TopoShape::hasSubShape(const char *Type) const {
388
    auto idx = shapeTypeAndIndex(Type);
389
    return idx.second>0 && idx.second<=(int)countSubShapes(idx.first);
390
}
391

392
template<class T>
393
static inline std::vector<T> _getSubShapes(const TopoDS_Shape &s, TopAbs_ShapeEnum type) {
394
    std::vector<T> shapes;
395
    if(s.IsNull())
396
        return shapes;
397

398
    if(type == TopAbs_SHAPE) {
399
        for(TopoDS_Iterator it(s);it.More();it.Next())
400
            shapes.emplace_back(it.Value());
401
        return shapes;
402
    }
403

404
    TopTools_IndexedMapOfShape anIndices;
405
    TopExp::MapShapes(s, type, anIndices);
406
    int count = anIndices.Extent();
407
    shapes.reserve(count);
408
    for(int i=1;i<=count;++i)
409
        shapes.emplace_back(anIndices.FindKey(i));
410
    return shapes;
411
}
412

413
static std::array<std::string,TopAbs_SHAPE> _ShapeNames;
414

415
static void initShapeNameMap() {
416
    if(_ShapeNames[TopAbs_VERTEX].empty()) {
417
        _ShapeNames[TopAbs_VERTEX] = "Vertex";
418
        _ShapeNames[TopAbs_EDGE] = "Edge";
419
        _ShapeNames[TopAbs_FACE] = "Face";
420
        _ShapeNames[TopAbs_WIRE] = "Wire";
421
        _ShapeNames[TopAbs_SHELL] = "Shell";
422
        _ShapeNames[TopAbs_SOLID] = "Solid";
423
        _ShapeNames[TopAbs_COMPOUND] = "Compound";
424
        _ShapeNames[TopAbs_COMPSOLID] = "CompSolid";
425
    }
426
}
427

428
std::pair<TopAbs_ShapeEnum,int> TopoShape::shapeTypeAndIndex(const char *name) {
429
    int idx = 0;
430
    TopAbs_ShapeEnum type = TopAbs_SHAPE;
431
    static const std::string _subshape("SubShape");
432
    if(boost::starts_with(name,_subshape)) {
433
        std::istringstream iss(name+_subshape.size());
434
        iss >> idx;
435
        if(!iss.eof())
436
            idx = 0;
437
    } else {
438
        type = shapeType(name,true);
439
        if(type != TopAbs_SHAPE) {
440
            std::istringstream iss(name+shapeName(type).size());
441
            iss >> idx;
442
            if(!iss.eof()) {
443
                idx = 0;
444
                type = TopAbs_SHAPE;
445
            }
446
        }
447
    }
448
    return std::make_pair(type,idx);
449
}
450

451
std::pair<TopAbs_ShapeEnum, int> TopoShape::shapeTypeAndIndex(const Data::IndexedName& element)
452
{
453
    if (!element) {
454
        return std::make_pair(TopAbs_SHAPE, 0);
455
    }
456
    static const std::string _subshape("SubShape");
457
    if (boost::equals(element.getType(), _subshape)) {
458
        return std::make_pair(TopAbs_SHAPE, element.getIndex());
459
    }
460
    TopAbs_ShapeEnum shapetype = shapeType(element.getType(), true);
461
    if (shapetype == TopAbs_SHAPE) {
462
        return std::make_pair(TopAbs_SHAPE, 0);
463
    }
464
    return std::make_pair(shapetype, element.getIndex());
465
}
466

467
TopAbs_ShapeEnum TopoShape::shapeType(const char *type, bool silent) {
468
    if(type) {
469
        initShapeNameMap();
470
        for(size_t idx=0;idx<_ShapeNames.size();++idx) {
471
            if(!_ShapeNames[idx].empty() && boost::starts_with(type,_ShapeNames[idx]))
472
                return static_cast<TopAbs_ShapeEnum>(idx);
473
        }
474
    }
475
    if(!silent) {
476
        if(Data::hasMissingElement(type))
477
            FC_THROWM(Base::CADKernelError,"missing shape element: " << (type?type:"?"));
478
        FC_THROWM(Base::CADKernelError,"invalid shape type: " << (type?type:"?"));
479
    }
480
    return TopAbs_SHAPE;
481
}
482

483
TopAbs_ShapeEnum TopoShape::shapeType(char type, bool silent) {
484
    switch(type) {
485
    case 'E':
486
        return TopAbs_EDGE;
487
    case 'V':
488
        return TopAbs_VERTEX;
489
    case 'F':
490
        return TopAbs_FACE;
491
    default:
492
        if(!silent)
493
            FC_THROWM(Base::CADKernelError, "invalid shape type '" << type << "'");
494
        return TopAbs_SHAPE;
495
    }
496
}
497

498
TopAbs_ShapeEnum TopoShape::shapeType(bool silent) const {
499
    if(isNull()) {
500
        if(!silent)
501
            FC_THROWM(NullShapeException, "Input shape is null");
502
        return TopAbs_SHAPE;
503
    }
504
    return getShape().ShapeType();
505
}
506

507
const std::string &TopoShape::shapeName(TopAbs_ShapeEnum type, bool silent) {
508
    initShapeNameMap();
509
    if(type>=0 && type<_ShapeNames.size() && !_ShapeNames[type].empty())
510
        return _ShapeNames[type];
511
    if(!silent)
512
        FC_THROWM(Base::CADKernelError, "invalid shape type '" << type << "'");
513
    static std::string ret;
514
    return ret;
515
}
516

517
const std::string &TopoShape::shapeName(bool silent) const {
518
    return shapeName(shapeType(silent),silent);
519
}
520

521
PyObject * TopoShape::getPySubShape(const char* Type, bool silent) const
522
{
523
    return Py::new_reference_to(shape2pyshape(getSubShape(Type,silent)));
524
}
525

526
PyObject * TopoShape::getPyObject()
527
{
528
    Base::PyObjectBase* prop = nullptr;
529
    if (_Shape.IsNull()) {
530
        prop = new TopoShapePy(new TopoShape(*this));
531
    }
532
    else {
533
        TopAbs_ShapeEnum type = _Shape.ShapeType();
534
        switch (type)
535
        {
536
        case TopAbs_COMPOUND:
537
            prop = new TopoShapeCompoundPy(new TopoShape(*this));
538
            break;
539
        case TopAbs_COMPSOLID:
540
            prop = new TopoShapeCompSolidPy(new TopoShape(*this));
541
            break;
542
        case TopAbs_SOLID:
543
            prop = new TopoShapeSolidPy(new TopoShape(*this));
544
            break;
545
        case TopAbs_SHELL:
546
            prop = new TopoShapeShellPy(new TopoShape(*this));
547
            break;
548
        case TopAbs_FACE:
549
            prop = new TopoShapeFacePy(new TopoShape(*this));
550
            break;
551
        case TopAbs_WIRE:
552
            prop = new TopoShapeWirePy(new TopoShape(*this));
553
            break;
554
        case TopAbs_EDGE:
555
            prop = new TopoShapeEdgePy(new TopoShape(*this));
556
            break;
557
        case TopAbs_VERTEX:
558
            prop = new TopoShapeVertexPy(new TopoShape(*this));
559
            break;
560
        case TopAbs_SHAPE:
561
        default:
562
            prop = new TopoShapePy(new TopoShape(*this));
563
            break;
564
        }
565
    }
566

567
    prop->setNotTracking(); // TODO: Does this still belong here?
568
    return prop;
569
}
570

571
void TopoShape::setPyObject(PyObject* obj)
572
{
573
    if (PyObject_TypeCheck(obj, &TopoShapePy::Type)) {
574
        this->_Shape = static_cast<TopoShapePy*>(obj)->getTopoShapePtr()->getShape();
575
    }
576
    else {
577
        std::string error = std::string("type must be 'Shape', not ");
578
        error += obj->ob_type->tp_name;
579
        throw Base::TypeError(error);
580
    }
581
}
582

583
//void TopoShape::operator = (const TopoShape& sh)
584
//{
585
//    if (this != &sh) {
586
//        this->Tag = sh.Tag;
587
//        this->_Shape = sh._Shape;
588
//    }
589
//}
590

591
void TopoShape::convertTogpTrsf(const Base::Matrix4D& mtrx, gp_Trsf& trsf)
592
{
593
    trsf.SetValues(mtrx[0][0],mtrx[0][1],mtrx[0][2],mtrx[0][3],
594
                   mtrx[1][0],mtrx[1][1],mtrx[1][2],mtrx[1][3],
595
                   mtrx[2][0],mtrx[2][1],mtrx[2][2],mtrx[2][3]);
596
}
597

598
void TopoShape::convertToMatrix(const gp_Trsf& trsf, Base::Matrix4D& mtrx)
599
{
600
    // https://www.opencascade.com/doc/occt-7.0.0/refman/html/classgp___trsf.html
601
    // VectorialPart() already includes the scale factor
602
    gp_Mat m = trsf.VectorialPart();
603
    gp_XYZ p = trsf.TranslationPart();
604

605
    // set Rotation matrix
606
    mtrx[0][0] = m(1,1);
607
    mtrx[0][1] = m(1,2);
608
    mtrx[0][2] = m(1,3);
609

610
    mtrx[1][0] = m(2,1);
611
    mtrx[1][1] = m(2,2);
612
    mtrx[1][2] = m(2,3);
613

614
    mtrx[2][0] = m(3,1);
615
    mtrx[2][1] = m(3,2);
616
    mtrx[2][2] = m(3,3);
617

618
    // set pos vector
619
    mtrx[0][3] = p.X();
620
    mtrx[1][3] = p.Y();
621
    mtrx[2][3] = p.Z();
622
}
623

624
Base::Matrix4D TopoShape::convert(const gp_Trsf& trsf) {
625
    Base::Matrix4D mat;
626
    convertToMatrix(trsf,mat);
627
    return mat;
628
}
629

630
gp_Trsf TopoShape::convert(const Base::Matrix4D& mtrx) {
631
    gp_Trsf trsf;
632
    convertTogpTrsf(mtrx,trsf);
633
    return trsf;
634
}
635

636
void TopoShape::setTransform(const Base::Matrix4D& rclTrf)
637
{
638
    gp_Trsf mov;
639
    convertTogpTrsf(rclTrf, mov);
640
    TopLoc_Location loc(mov);
641
    _Shape.Location(loc);
642
}
643

644
Base::Matrix4D TopoShape::getTransform() const
645
{
646
    Base::Matrix4D mtrx;
647
    gp_Trsf Trf = _Shape.Location().Transformation();
648
    Trf.SetScaleFactor(1.0);
649
    convertToMatrix(Trf, mtrx);
650
    return mtrx;
651
}
652

653

654
void TopoShape::read(const char *FileName)
655
{
656
    Base::FileInfo File(FileName);
657

658
    // checking on the file
659
    if (!File.isReadable())
660
        throw Base::FileException("File to load not existing or not readable", FileName);
661

662
    if (File.hasExtension({"igs", "iges"})) {
663
        // read iges file
664
        importIges(File.filePath().c_str());
665
    }
666
    else if (File.hasExtension({"stp", "step"})) {
667
        importStep(File.filePath().c_str());
668
    }
669
    else if (File.hasExtension({"brp", "brep"})) {
670
        // read brep-file
671
        importBrep(File.filePath().c_str());
672
    }
673
    else{
674
        throw Base::FileException("Unknown extension");
675
    }
676
}
677

678
/*!
679
 Example code to get the labels for each face in an IGES file.
680
 \code
681
#include <XSControl_WorkSession.hxx>
682
#include <XSControl_TransferReader.hxx>
683
#include <IGESData_IGESModel.hxx>
684
#include <IGESData_IGESEntity.hxx>
685

686
IGESControl_Reader aReader;
687
...
688
// Gets the labels of all face items if defined in the IGES file
689
Handle(XSControl_WorkSession) ws = aReader.WS();
690
Handle(XSControl_TransferReader) tr = ws->TransferReader();
691

692
std::string name;
693
Handle(IGESData_IGESModel) aModel = aReader.IGESModel();
694
Standard_Integer all = aModel->NbEntities();
695

696
TopExp_Explorer ex;
697
for (ex.Init(this->_Shape, TopAbs_FACE); ex.More(); ex.Next())
698
{
699
    const TopoDS_Face& aFace = TopoDS::Face(ex.Current());
700
    Handle(Standard_Transient) ent = tr->EntityFromShapeResult(aFace, 1);
701
    if (!ent.IsNull()) {
702
        int i = aModel->Number(ent);
703
        if (i > 0) {
704
            Handle(IGESData_IGESEntity) ie = aModel->Entity(i);
705
            if (ie->HasShortLabel())
706
                name = ie->ShortLabel()->ToCString();
707
        }
708
    }
709
}
710
\endcode
711
*/
712
void TopoShape::importIges(const char *FileName)
713
{
714
    try {
715
        // read iges file
716
        IGESControl_Controller::Init();
717
        IGESControl_Reader aReader;
718
        // Ignore construction elements
719
        // http://www.opencascade.org/org/forum/thread_20603/?forum=3
720
        aReader.SetReadVisible(Standard_True);
721
        if (aReader.ReadFile(encodeFilename(FileName).c_str()) != IFSelect_RetDone)
722
            throw Base::FileException("Error in reading IGES");
723

724
#if OCC_VERSION_HEX < 0x070500
725
        Handle(Message_ProgressIndicator) pi = new ProgressIndicator(100);
726
        pi->NewScope(100, "Reading IGES file...");
727
        pi->Show();
728
        aReader.WS()->MapReader()->SetProgress(pi);
729
#endif
730

731
        // make brep
732
        aReader.ClearShapes();
733
        aReader.TransferRoots();
734
        // one shape that contains all subshapes
735
        this->_Shape = aReader.OneShape();
736
#if OCC_VERSION_HEX < 0x070500
737
        pi->EndScope();
738
#endif
739
    }
740
    catch (Standard_Failure& e) {
741
        throw Base::CADKernelError(e.GetMessageString());
742
    }
743
}
744

745
void TopoShape::importStep(const char *FileName)
746
{
747
    try {
748
        STEPControl_Reader aReader;
749
        if (aReader.ReadFile(encodeFilename(FileName).c_str()) != IFSelect_RetDone)
750
            throw Base::FileException("Error in reading STEP");
751

752
#if OCC_VERSION_HEX < 0x070500
753
        Handle(Message_ProgressIndicator) pi = new ProgressIndicator(100);
754
        aReader.WS()->MapReader()->SetProgress(pi);
755
        pi->NewScope(100, "Reading STEP file...");
756
        pi->Show();
757
#endif
758

759
        // Root transfers
760
        aReader.TransferRoots();
761
        // one shape that contains all subshapes
762
        this->_Shape = aReader.OneShape();
763
#if OCC_VERSION_HEX < 0x070500
764
        pi->EndScope();
765
#endif
766
    }
767
    catch (Standard_Failure& e) {
768
        throw Base::CADKernelError(e.GetMessageString());
769
    }
770
}
771

772
void TopoShape::importBrep(const char *FileName)
773
{
774
    try {
775
        // read brep-file
776
        BRep_Builder aBuilder;
777
        TopoDS_Shape aShape;
778
#if OCC_VERSION_HEX < 0x070500
779
        Handle(Message_ProgressIndicator) pi = new ProgressIndicator(100);
780
        pi->NewScope(100, "Reading BREP file...");
781
        pi->Show();
782
        BRepTools::Read(aShape,encodeFilename(FileName).c_str(),aBuilder,pi);
783
        pi->EndScope();
784
#else
785
        BRepTools::Read(aShape,static_cast<Standard_CString>(FileName),aBuilder);
786
#endif
787
        this->_Shape = aShape;
788
    }
789
    catch (Standard_Failure& e) {
790
        throw Base::CADKernelError(e.GetMessageString());
791
    }
792
}
793

794
void TopoShape::importBrep(std::istream& str, int indicator)
795
{
796
    try {
797
        // read brep-file
798
        BRep_Builder aBuilder;
799
        TopoDS_Shape aShape;
800
#if OCC_VERSION_HEX < 0x070500
801
        if (indicator) {
802
            Handle(Message_ProgressIndicator) pi = new ProgressIndicator(100);
803
            pi->NewScope(100, "Reading BREP file...");
804
            pi->Show();
805
            BRepTools::Read(aShape,str,aBuilder,pi);
806
            pi->EndScope();
807
        }
808
        else {
809
            BRepTools::Read(aShape,str,aBuilder);
810
        }
811
#else
812
        (void)indicator;
813
        BRepTools::Read(aShape,str,aBuilder);
814
#endif
815
        this->_Shape = aShape;
816
    }
817
    catch (Standard_Failure& e) {
818
        throw Base::CADKernelError(e.GetMessageString());
819
    }
820
    catch (const std::exception& e) {
821
        throw Base::CADKernelError(e.what());
822
    }
823
}
824

825
void TopoShape::importBinary(std::istream& str)
826
{
827
    BinTools_ShapeSet theShapeSet;
828
    theShapeSet.Read(str);
829
    Standard_Integer shapeId=0, locId=0, orient=0;
830
    BinTools::GetInteger(str, shapeId);
831
    if (shapeId <= 0 || shapeId > theShapeSet.NbShapes())
832
        return;
833

834
    BinTools::GetInteger(str, locId);
835
    BinTools::GetInteger(str, orient);
836
    TopAbs_Orientation anOrient = static_cast<TopAbs_Orientation>(orient);
837

838
    try {
839
        this->_Shape = theShapeSet.Shape(shapeId);
840
        this->_Shape.Location(theShapeSet.Locations().Location (locId));
841
        this->_Shape.Orientation (anOrient);
842
    }
843
    catch (Standard_Failure&) {
844
        throw Base::RuntimeError("Failed to read shape from binary stream");
845
    }
846
}
847

848
void TopoShape::write(const char *FileName) const
849
{
850
    Base::FileInfo File(FileName);
851

852
    if (File.hasExtension({"igs", "iges"})) {
853
        // write iges file
854
        exportIges(File.filePath().c_str());
855
    }
856
    else if (File.hasExtension({"stp", "step"})) {
857
        exportStep(File.filePath().c_str());
858
    }
859
    else if (File.hasExtension({"brp", "brep"})) {
860
        // read brep-file
861
        exportBrep(File.filePath().c_str());
862
    }
863
    else if (File.hasExtension("stl")) {
864
        // read brep-file
865
        exportStl(File.filePath().c_str(), 0.01);
866
    }
867
    else{
868
        throw Base::FileException("Unknown extension");
869
    }
870
}
871

872
void TopoShape::exportIges(const char *filename) const
873
{
874
    try {
875
        // write iges file
876
        IGESControl_Controller::Init();
877
        IGESControl_Writer aWriter;
878
        IGESData_GlobalSection header = aWriter.Model()->GlobalSection();
879
        header.SetAuthorName(new TCollection_HAsciiString(Interface::writeIgesHeaderAuthor()));
880
        header.SetCompanyName(new TCollection_HAsciiString(Interface::writeIgesHeaderCompany()));
881
        header.SetSendName(new TCollection_HAsciiString(Interface::writeIgesHeaderProduct()));
882
        aWriter.Model()->SetGlobalSection(header);
883
        aWriter.AddShape(this->_Shape);
884
        aWriter.ComputeModel();
885
        if (aWriter.Write(encodeFilename(filename).c_str()) != IFSelect_RetDone)
886
            throw Base::FileException("Writing of IGES failed");
887
    }
888
    catch (Standard_Failure& e) {
889
        throw Base::CADKernelError(e.GetMessageString());
890
    }
891
}
892

893
void TopoShape::exportStep(const char *filename) const
894
{
895
    try {
896
        // Fixes issue #6282
897
        // Do not write out any assembly information when using the simplified STEP export
898
        Interface::writeStepAssembly(Interface::Assembly::Off);
899

900
        // write step file
901
        STEPControl_Writer aWriter;
902

903
        const Handle(XSControl_TransferWriter)& hTransferWriter = aWriter.WS()->TransferWriter();
904
        Handle(Transfer_FinderProcess) hFinder = hTransferWriter->FinderProcess();
905

906
#if OCC_VERSION_HEX < 0x070500
907
        Handle(Message_ProgressIndicator) pi = new ProgressIndicator(100);
908
        hFinder->SetProgress(pi);
909
        pi->NewScope(100, "Writing STEP file...");
910
        pi->Show();
911
#endif
912

913
        if (aWriter.Transfer(this->_Shape, STEPControl_AsIs) != IFSelect_RetDone)
914
            throw Base::FileException("Error in transferring STEP");
915

916
        APIHeaderSection_MakeHeader makeHeader(aWriter.Model());
917
        // Don't set name because STEP doesn't support UTF-8
918
        // https://forum.freecad.org/viewtopic.php?f=8&t=52967
919
        makeHeader.SetAuthorValue (1, new TCollection_HAsciiString("FreeCAD"));
920
        makeHeader.SetOrganizationValue (1, new TCollection_HAsciiString("FreeCAD"));
921
        makeHeader.SetOriginatingSystem(new TCollection_HAsciiString("FreeCAD"));
922
        makeHeader.SetDescriptionValue(1, new TCollection_HAsciiString("FreeCAD Model"));
923

924
        if (aWriter.Write(encodeFilename(filename).c_str()) != IFSelect_RetDone)
925
            throw Base::FileException("Writing of STEP failed");
926
#if OCC_VERSION_HEX < 0x070500
927
        pi->EndScope();
928
#endif
929
    }
930
    catch (Standard_Failure& e) {
931
        throw Base::CADKernelError(e.GetMessageString());
932
    }
933
}
934

935
void TopoShape::exportBrep(const char *filename) const
936
{
937
#if OCC_VERSION_HEX >= 0x070600
938
    if (!BRepTools::Write(this->_Shape,encodeFilename(filename).c_str(), Standard_False, Standard_False, TopTools_FormatVersion_VERSION_1))
939
        throw Base::FileException("Writing of BREP failed");
940
#else
941
    if (!BRepTools::Write(this->_Shape,encodeFilename(filename).c_str()))
942
        throw Base::FileException("Writing of BREP failed");
943
#endif
944
}
945

946
void TopoShape::exportBrep(std::ostream& out) const
947
{
948
    // See TopTools_FormatVersion of OCCT 7.6
949
    enum {
950
        VERSION_1 = 1,
951
        VERSION_2 = 2,
952
        VERSION_3 = 3
953
    };
954
    BRepTools_ShapeSet SS(Standard_False);
955
    SS.SetFormatNb(VERSION_1);
956
    SS.Add(this->_Shape);
957
    SS.Write(out);
958
    SS.Write(this->_Shape, out);
959
}
960

961
void TopoShape::exportBinary(std::ostream& out) const
962
{
963
    // See BinTools_FormatVersion of OCCT 7.6
964
    enum {
965
        VERSION_1 = 1,
966
        VERSION_2 = 2,
967
        VERSION_3 = 3,
968
        VERSION_4 = 4
969
    };
970

971
    // An example how to use BinTools_ShapeSet can be found in BinMNaming_NamedShapeDriver.cxx
972
    BinTools_ShapeSet theShapeSet;
973
    theShapeSet.SetFormatNb(VERSION_3);
974
    if (this->_Shape.IsNull()) {
975
        theShapeSet.Add(this->_Shape);
976
        theShapeSet.Write(out);
977
        BinTools::PutInteger(out, -1);
978
        BinTools::PutInteger(out, -1);
979
        BinTools::PutInteger(out, -1);
980
    }
981
    else {
982
        Standard_Integer shapeId = theShapeSet.Add(this->_Shape);
983
        Standard_Integer locId = theShapeSet.Locations().Index(this->_Shape.Location());
984
        Standard_Integer orient = static_cast<int>(this->_Shape.Orientation());
985

986
        theShapeSet.Write(out);
987
        BinTools::PutInteger(out, shapeId);
988
        BinTools::PutInteger(out, locId);
989
        BinTools::PutInteger(out, orient);
990
    }
991
}
992

993
void TopoShape::dump(std::ostream& out) const
994
{
995
    BRepTools::Dump(this->_Shape, out);
996
}
997

998
void TopoShape::exportStl(const char *filename, double deflection) const
999
{
1000
    StlAPI_Writer writer;
1001
    BRepMesh_IncrementalMesh aMesh(this->_Shape, deflection,
1002
                                   /*isRelative*/ Standard_False,
1003
                                   /*theAngDeflection*/
1004
                                   defaultAngularDeflection(deflection),
1005
                                   /*isInParallel*/ true);
1006
    writer.Write(this->_Shape,encodeFilename(filename).c_str());
1007
}
1008

1009
void TopoShape::exportFaceSet(double dev, double ca,
1010
                              const std::vector<App::Color>& colors,
1011
                              std::ostream& str) const
1012
{
1013
    Base::InventorBuilder builder(str);
1014
    builder.beginSeparator();
1015
    TopExp_Explorer ex;
1016
    std::size_t numFaces = 0;
1017
    for (ex.Init(this->_Shape, TopAbs_FACE); ex.More(); ex.Next()) {
1018
        numFaces++;
1019
    }
1020

1021
    bool supportFaceColors = (numFaces == colors.size());
1022

1023
    std::size_t index=0;
1024
    BRepMesh_IncrementalMesh MESH(this->_Shape, dev,
1025
                                  /*isRelative*/ Standard_False,
1026
                                  /*theAngDeflection*/
1027
                                  defaultAngularDeflection(dev),
1028
                                  /*isInParallel*/ true);
1029
    for (ex.Init(this->_Shape, TopAbs_FACE); ex.More(); ex.Next(), index++) {
1030
        // get the shape and mesh it
1031
        const TopoDS_Face& aFace = TopoDS::Face(ex.Current());
1032
        std::vector<gp_Pnt> points;
1033
        std::vector<Poly_Triangle> facets;
1034
        if (!Tools::getTriangulation(aFace, points, facets))
1035
            continue;
1036

1037
        std::vector<Base::Vector3f> vertices;
1038
        std::vector<int> indices;
1039
        vertices.resize(points.size());
1040
        indices.resize(4 * facets.size());
1041

1042
        for (std::size_t i = 0; i < points.size(); i++) {
1043
            vertices[i] = Base::convertTo<Base::Vector3f>(points[i]);
1044
        }
1045

1046
        for (std::size_t i = 0; i < facets.size(); i++) {
1047
            Standard_Integer n1,n2,n3;
1048
            facets[i].Get(n1, n2, n3);
1049
            indices[4 * i    ] = n1;
1050
            indices[4 * i + 1] = n2;
1051
            indices[4 * i + 2] = n3;
1052
            indices[4 * i + 3] = -1;
1053
        }
1054

1055
        builder.beginSeparator();
1056
        Base::ShapeHintsItem shapeHints{static_cast<float>(ca)};
1057
        builder.addNode(shapeHints);
1058
        if (supportFaceColors) {
1059
            App::Color c = colors[index];
1060
            Base::MaterialItem material;
1061
            material.setDiffuseColor({Base::ColorRGB{c.r, c.g, c.b}});
1062
            material.setTransparency({c.a});
1063
            builder.addNode(material);
1064
        }
1065

1066
        Base::Coordinate3Item coords{vertices};
1067
        builder.addNode(coords);
1068
        Base::IndexedFaceSetItem faceSet{indices};
1069
        builder.addNode(faceSet);
1070
        builder.endSeparator();
1071
    }
1072
    builder.endSeparator();
1073
}
1074

1075
void TopoShape::exportLineSet(std::ostream& str) const
1076
{
1077
    Base::InventorBuilder builder(str);
1078
    builder.beginSeparator();
1079
    // get a indexed map of edges
1080
    TopTools_IndexedMapOfShape M;
1081
    TopExp::MapShapes(this->_Shape, TopAbs_EDGE, M);
1082

1083
    // build up map edge->face
1084
    TopTools_IndexedDataMapOfShapeListOfShape edge2Face;
1085
    TopExp::MapShapesAndAncestors(this->_Shape, TopAbs_EDGE, TopAbs_FACE, edge2Face);
1086

1087
    for (int i=0; i<M.Extent(); i++) {
1088
        const TopoDS_Edge& aEdge = TopoDS::Edge(M(i+1));
1089
        std::vector<gp_Pnt> points;
1090

1091
        if (!Tools::getPolygon3D(aEdge, points)) {
1092
            // the edge has not its own triangulation, but then a face the edge is attached to
1093
            // must provide this triangulation
1094

1095
            // Look for one face in our map (it doesn't care which one we take)
1096
            const TopoDS_Face& aFace = TopoDS::Face(edge2Face.FindFromKey(aEdge).First());
1097
            if (!Tools::getPolygonOnTriangulation(aEdge, aFace, points))
1098
                continue;
1099
        }
1100

1101
        std::vector<Base::Vector3f> vertices;
1102
        vertices.reserve(points.size());
1103
        std::for_each(points.begin(), points.end(), [&vertices](const gp_Pnt& p) {
1104
            vertices.push_back(Base::convertTo<Base::Vector3f>(p));
1105
        });
1106

1107
        Base::DrawStyle drawStyle;
1108
        drawStyle.lineWidth = 2.0F;
1109
        builder.addNode(Base::MultiLineItem{vertices, drawStyle, Base::ColorRGB{0, 0, 0}});
1110
    }
1111

1112
    builder.endSeparator();
1113
}
1114

1115
Base::BoundBox3d TopoShape::getBoundBox() const
1116
{
1117
    Base::BoundBox3d box;
1118
    try {
1119
        // If the shape is empty an exception may be thrown
1120
        Bnd_Box bounds;
1121
        BRepBndLib::Add(_Shape, bounds);
1122
        bounds.SetGap(0.0);
1123
        Standard_Real xMin, yMin, zMin, xMax, yMax, zMax;
1124
        bounds.Get(xMin, yMin, zMin, xMax, yMax, zMax);
1125

1126
        box.MinX = xMin;
1127
        box.MaxX = xMax;
1128
        box.MinY = yMin;
1129
        box.MaxY = yMax;
1130
        box.MinZ = zMin;
1131
        box.MaxZ = zMax;
1132
    }
1133
    catch (Standard_Failure&) {
1134
    }
1135

1136
    return box;
1137
}
1138

1139
namespace {
1140
bool getShapeProperties(const TopoDS_Shape& shape, GProp_GProps& prop)
1141
{
1142
    TopExp_Explorer xpSolid(shape, TopAbs_SOLID);
1143
    if (xpSolid.More()) {
1144
        BRepGProp::VolumeProperties(shape, prop);
1145
        return true;
1146
    }
1147

1148
    TopExp_Explorer xpFace(shape, TopAbs_FACE);
1149
    if (xpFace.More()) {
1150
        BRepGProp::SurfaceProperties(shape, prop);
1151
        return true;
1152
    }
1153

1154
    TopExp_Explorer xpEdge(shape, TopAbs_EDGE);
1155
    if (xpEdge.More()) {
1156
        BRepGProp::LinearProperties(shape, prop);
1157
        return true;
1158
    }
1159

1160
    TopExp_Explorer xpVert(shape, TopAbs_VERTEX);
1161
    if (xpVert.More()) {
1162
        gp_Pnt pnts;
1163
        int count = 0;
1164
        for (; xpVert.More(); xpVert.Next()) {
1165
            count++;
1166
            gp_Pnt pnt = BRep_Tool::Pnt(TopoDS::Vertex(xpVert.Current()));
1167
            pnts.SetX(pnts.X() + pnt.X());
1168
            pnts.SetY(pnts.Y() + pnt.Y());
1169
            pnts.SetZ(pnts.Z() + pnt.Z());
1170
        }
1171

1172
        pnts.SetX(pnts.X() / count);
1173
        pnts.SetY(pnts.Y() / count);
1174
        pnts.SetZ(pnts.Z() / count);
1175
        prop = GProp_GProps(pnts);
1176

1177
        return true;
1178
    }
1179

1180
    return false;
1181
}
1182
}
1183

1184
bool TopoShape::getCenterOfGravity(Base::Vector3d& center) const
1185
{
1186
    if (_Shape.IsNull())
1187
        return false;
1188

1189
    // Computing of CentreOfMass
1190
    GProp_GProps prop;
1191
    if (getShapeProperties(_Shape, prop)) {
1192
        if (prop.Mass() > Precision::Infinite()) {
1193
            return false;
1194
        }
1195
        gp_Pnt pnt = prop.CentreOfMass();
1196
        center.Set(pnt.X(), pnt.Y(), pnt.Z());
1197
        return true;
1198
    }
1199

1200
    return false;
1201
}
1202

1203
void TopoShape::Save (Base::Writer &writer ) const
1204
{
1205
    Data::ComplexGeoData::Save(writer);
1206
}
1207

1208
void TopoShape::Restore(Base::XMLReader &reader)
1209
{
1210
    Data::ComplexGeoData::Restore(reader);
1211
}
1212

1213
void TopoShape::SaveDocFile (Base::Writer &writer) const
1214
{
1215
    Data::ComplexGeoData::SaveDocFile(writer);
1216
}
1217

1218
void TopoShape::RestoreDocFile(Base::Reader &reader)
1219
{
1220
    Data::ComplexGeoData::RestoreDocFile(reader);
1221
}
1222

1223

1224
unsigned int TopoShape_RefCountShapes(const TopoDS_Shape& aShape)
1225
{
1226
    unsigned int size = 1; // this shape
1227
    TopoDS_Iterator it;
1228
    // go through all direct children
1229
    for (it.Initialize(aShape, false, false);it.More(); it.Next()) {
1230
        size += TopoShape_RefCountShapes(it.Value());
1231
    }
1232

1233
    return size;
1234
}
1235

1236
unsigned int TopoShape::getMemSize () const
1237
{
1238
    if (!_Shape.IsNull()) {
1239
        // Count total amount of references of TopoDS_Shape objects
1240
        unsigned int memsize = (sizeof(TopoDS_Shape)+sizeof(TopoDS_TShape)) * TopoShape_RefCountShapes(_Shape);
1241

1242
        // Now get a map of TopoDS_Shape objects without duplicates
1243
        TopTools_IndexedMapOfShape M;
1244
        TopExp::MapShapes(_Shape, M);
1245
        for (int i=0; i<M.Extent(); i++) {
1246
            const TopoDS_Shape& shape = M(i+1);
1247
            if (shape.IsNull())
1248
                continue;
1249

1250
            // add the size of the underlying geometric data
1251
            Handle(TopoDS_TShape) tshape = shape.TShape();
1252
            memsize += tshape->DynamicType()->Size();
1253

1254
            switch (shape.ShapeType())
1255
            {
1256
            case TopAbs_FACE:
1257
                {
1258
                    // first, last, tolerance
1259
                    memsize += 5*sizeof(Standard_Real);
1260
                    const TopoDS_Face& face = TopoDS::Face(shape);
1261
                    // if no geometry is attached to a face an exception is raised
1262
                    BRepAdaptor_Surface surface;
1263
                    try {
1264
                        surface.Initialize(face);
1265
                    }
1266
                    catch (const Standard_Failure&) {
1267
                        continue;
1268
                    }
1269

1270
                    switch (surface.GetType())
1271
                    {
1272
                    case GeomAbs_Plane:
1273
                        memsize += sizeof(Geom_Plane);
1274
                        break;
1275
                    case GeomAbs_Cylinder:
1276
                        memsize += sizeof(Geom_CylindricalSurface);
1277
                        break;
1278
                    case GeomAbs_Cone:
1279
                        memsize += sizeof(Geom_ConicalSurface);
1280
                        break;
1281
                    case GeomAbs_Sphere:
1282
                        memsize += sizeof(Geom_SphericalSurface);
1283
                        break;
1284
                    case GeomAbs_Torus:
1285
                        memsize += sizeof(Geom_ToroidalSurface);
1286
                        break;
1287
                    case GeomAbs_BezierSurface:
1288
                        memsize += sizeof(Geom_BezierSurface);
1289
                        memsize += (surface.NbUPoles()*surface.NbVPoles()) * sizeof(Standard_Real);
1290
                        memsize += (surface.NbUPoles()*surface.NbVPoles()) * sizeof(Geom_CartesianPoint);
1291
                        break;
1292
                    case GeomAbs_BSplineSurface:
1293
                        memsize += sizeof(Geom_BSplineSurface);
1294
                        memsize += (surface.NbUKnots()+surface.NbVKnots()) * sizeof(Standard_Real);
1295
                        memsize += (surface.NbUPoles()*surface.NbVPoles()) * sizeof(Standard_Real);
1296
                        memsize += (surface.NbUPoles()*surface.NbVPoles()) * sizeof(Geom_CartesianPoint);
1297
                        break;
1298
                    case GeomAbs_SurfaceOfRevolution:
1299
                        memsize += sizeof(Geom_SurfaceOfRevolution);
1300
                        break;
1301
                    case GeomAbs_SurfaceOfExtrusion:
1302
                        memsize += sizeof(Geom_SurfaceOfLinearExtrusion);
1303
                        break;
1304
                    case GeomAbs_OtherSurface:
1305
                        // What kind of surface should this be?
1306
                        memsize += sizeof(Geom_Surface);
1307
                        break;
1308
                    default:
1309
                        break;
1310
                    }
1311
                } break;
1312
            case TopAbs_EDGE:
1313
                {
1314
                    // first, last, tolerance
1315
                    memsize += 3*sizeof(Standard_Real);
1316
                    const TopoDS_Edge& edge = TopoDS::Edge(shape);
1317
                    // if no geometry is attached to an edge an exception is raised
1318
                    BRepAdaptor_Curve curve;
1319
                    try {
1320
                        curve.Initialize(edge);
1321
                    }
1322
                    catch (const Standard_Failure&) {
1323
                        continue;
1324
                    }
1325

1326
                    switch (curve.GetType())
1327
                    {
1328
                    case GeomAbs_Line:
1329
                        memsize += sizeof(Geom_Line);
1330
                        break;
1331
                    case GeomAbs_Circle:
1332
                        memsize += sizeof(Geom_Circle);
1333
                        break;
1334
                    case GeomAbs_Ellipse:
1335
                        memsize += sizeof(Geom_Ellipse);
1336
                        break;
1337
                    case GeomAbs_Hyperbola:
1338
                        memsize += sizeof(Geom_Hyperbola);
1339
                        break;
1340
                    case GeomAbs_Parabola:
1341
                        memsize += sizeof(Geom_Parabola);
1342
                        break;
1343
                    case GeomAbs_BezierCurve:
1344
                        memsize += sizeof(Geom_BezierCurve);
1345
                        memsize += curve.NbPoles() * sizeof(Standard_Real);
1346
                        memsize += curve.NbPoles() * sizeof(Geom_CartesianPoint);
1347
                        break;
1348
                    case GeomAbs_BSplineCurve:
1349
                        memsize += sizeof(Geom_BSplineCurve);
1350
                        memsize += curve.NbKnots() * sizeof(Standard_Real);
1351
                        memsize += curve.NbPoles() * sizeof(Standard_Real);
1352
                        memsize += curve.NbPoles() * sizeof(Geom_CartesianPoint);
1353
                        break;
1354
                    case GeomAbs_OtherCurve:
1355
                        // What kind of curve should this be?
1356
                        memsize += sizeof(Geom_Curve);
1357
                        break;
1358
                    default:
1359
                        break;
1360
                    }
1361
                } break;
1362
            case TopAbs_VERTEX:
1363
                {
1364
                    // tolerance
1365
                    memsize += sizeof(Standard_Real);
1366
                    memsize += sizeof(Geom_CartesianPoint);
1367
                } break;
1368
            default:
1369
                break;
1370
            }
1371
        }
1372

1373
        // estimated memory usage
1374
        return memsize;
1375
    }
1376

1377
    // in case the shape is invalid
1378
    return sizeof(TopoDS_Shape);
1379
}
1380

1381
bool TopoShape::isNull() const
1382
{
1383
    return this->_Shape.IsNull() ? true : false;
1384
}
1385

1386
bool TopoShape::isValid() const
1387
{
1388
    BRepCheck_Analyzer aChecker(this->_Shape);
1389
    return aChecker.IsValid() ? true : false;
1390
}
1391

1392
namespace Part {
1393
std::vector<std::string> buildShapeEnumVector()
1394
{
1395
   std::vector<std::string> names;
1396
   names.emplace_back("Compound");             //TopAbs_COMPOUND
1397
   names.emplace_back("Compound Solid");       //TopAbs_COMPSOLID
1398
   names.emplace_back("Solid");                //TopAbs_SOLID
1399
   names.emplace_back("Shell");                //TopAbs_SHELL
1400
   names.emplace_back("Face");                 //TopAbs_FACE
1401
   names.emplace_back("Wire");                 //TopAbs_WIRE
1402
   names.emplace_back("Edge");                 //TopAbs_EDGE
1403
   names.emplace_back("Vertex");               //TopAbs_VERTEX
1404
   names.emplace_back("Shape");                //TopAbs_SHAPE
1405
   return names;
1406
}
1407

1408
std::vector<std::string> buildBOPCheckResultVector()
1409
{
1410
  std::vector<std::string> results;
1411
  results.emplace_back("BOPAlgo CheckUnknown");               //BOPAlgo_CheckUnknown
1412
  results.emplace_back("BOPAlgo BadType");                    //BOPAlgo_BadType
1413
  results.emplace_back("BOPAlgo SelfIntersect");              //BOPAlgo_SelfIntersect
1414
  results.emplace_back("BOPAlgo TooSmallEdge");               //BOPAlgo_TooSmallEdge
1415
  results.emplace_back("BOPAlgo NonRecoverableFace");         //BOPAlgo_NonRecoverableFace
1416
  results.emplace_back("BOPAlgo IncompatibilityOfVertex");    //BOPAlgo_IncompatibilityOfVertex
1417
  results.emplace_back("BOPAlgo IncompatibilityOfEdge");      //BOPAlgo_IncompatibilityOfEdge
1418
  results.emplace_back("BOPAlgo IncompatibilityOfFace");      //BOPAlgo_IncompatibilityOfFace
1419
  results.emplace_back("BOPAlgo OperationAborted");           //BOPAlgo_OperationAborted
1420
  results.emplace_back("BOPAlgo GeomAbs_C0");                 //BOPAlgo_GeomAbs_C0
1421
  results.emplace_back("BOPAlgo_InvalidCurveOnSurface");      //BOPAlgo_InvalidCurveOnSurface
1422
  results.emplace_back("BOPAlgo NotValid");                   //BOPAlgo_NotValid
1423
  return results;
1424
}
1425
}
1426

1427
bool TopoShape::analyze(bool runBopCheck, std::ostream& str) const
1428
{
1429
    if (!this->_Shape.IsNull()) {
1430
        BRepCheck_Analyzer aChecker(this->_Shape);
1431
        if (!aChecker.IsValid()) {
1432
            std::vector<TopoDS_Shape> shapes;
1433

1434
            TopTools_IndexedMapOfShape vertexOfShape;
1435
            TopExp::MapShapes(this->_Shape, TopAbs_VERTEX, vertexOfShape);
1436
            for (int i = 1; i <= vertexOfShape.Extent();++i)
1437
                shapes.push_back(vertexOfShape(i));
1438

1439
            TopTools_IndexedMapOfShape edgeOfShape;
1440
            TopExp::MapShapes(this->_Shape, TopAbs_EDGE, edgeOfShape);
1441
            for (int i = 1; i <= edgeOfShape.Extent();++i)
1442
                shapes.push_back(edgeOfShape(i));
1443

1444
            TopTools_IndexedMapOfShape wireOfShape;
1445
            TopExp::MapShapes(this->_Shape, TopAbs_WIRE, wireOfShape);
1446
            for (int i = 1; i <= wireOfShape.Extent();++i)
1447
                shapes.push_back(wireOfShape(i));
1448

1449
            TopTools_IndexedMapOfShape faceOfShape;
1450
            TopExp::MapShapes(this->_Shape, TopAbs_FACE, faceOfShape);
1451
            for (int i = 1; i <= faceOfShape.Extent();++i)
1452
                shapes.push_back(faceOfShape(i));
1453

1454
            TopTools_IndexedMapOfShape shellOfShape;
1455
            TopExp::MapShapes(this->_Shape, TopAbs_SHELL, shellOfShape);
1456
            for (int i = 1; i <= shellOfShape.Extent();++i)
1457
                shapes.push_back(shellOfShape(i));
1458

1459
            TopTools_IndexedMapOfShape solidOfShape;
1460
            TopExp::MapShapes(this->_Shape, TopAbs_SOLID, solidOfShape);
1461
            for (int i = 1; i <= solidOfShape.Extent();++i)
1462
                shapes.push_back(solidOfShape(i));
1463

1464
            TopTools_IndexedMapOfShape compOfShape;
1465
            TopExp::MapShapes(this->_Shape, TopAbs_COMPOUND, compOfShape);
1466
            for (int i = 1; i <= compOfShape.Extent();++i)
1467
                shapes.push_back(compOfShape(i));
1468

1469
            TopTools_IndexedMapOfShape compsOfShape;
1470
            TopExp::MapShapes(this->_Shape, TopAbs_COMPSOLID, compsOfShape);
1471
            for (int i = 1; i <= compsOfShape.Extent();++i)
1472
                shapes.push_back(compsOfShape(i));
1473

1474
            for (const auto & shape : shapes) {
1475
                if (!aChecker.IsValid(shape)) {
1476
                    const Handle(BRepCheck_Result)& result = aChecker.Result(shape);
1477
                    if (result.IsNull())
1478
                        continue;
1479
                    const BRepCheck_ListOfStatus& status = result->StatusOnShape(shape);
1480

1481
                    BRepCheck_ListIteratorOfListOfStatus it(status);
1482
                    while (it.More()) {
1483
                        BRepCheck_Status& val = it.Value();
1484
                        switch (val)
1485
                        {
1486
                        case BRepCheck_NoError:
1487
                            str << "No error" << std::endl;
1488
                            break;
1489
                        case BRepCheck_InvalidPointOnCurve:
1490
                            str << "Invalid point on curve" << std::endl;
1491
                            break;
1492
                        case BRepCheck_InvalidPointOnCurveOnSurface:
1493
                            str << "Invalid point on curve on surface" << std::endl;
1494
                            break;
1495
                        case BRepCheck_InvalidPointOnSurface:
1496
                            str << "Invalid point on surface" << std::endl;
1497
                            break;
1498
                        case BRepCheck_No3DCurve:
1499
                            str << "No 3D curve" << std::endl;
1500
                            break;
1501
                        case BRepCheck_Multiple3DCurve:
1502
                            str << "Multiple 3D curve" << std::endl;
1503
                            break;
1504
                        case BRepCheck_Invalid3DCurve:
1505
                            str << "Invalid 3D curve" << std::endl;
1506
                            break;
1507
                        case BRepCheck_NoCurveOnSurface:
1508
                            str << "No curve on surface" << std::endl;
1509
                            break;
1510
                        case BRepCheck_InvalidCurveOnSurface:
1511
                            str << "Invalid curve on surface" << std::endl;
1512
                            break;
1513
                        case BRepCheck_InvalidCurveOnClosedSurface:
1514
                            str << "Invalid curve on closed surface" << std::endl;
1515
                            break;
1516
                        case BRepCheck_InvalidSameRangeFlag:
1517
                            str << "Invalid same-range flag" << std::endl;
1518
                            break;
1519
                        case BRepCheck_InvalidSameParameterFlag:
1520
                            str << "Invalid same-parameter flag" << std::endl;
1521
                            break;
1522
                        case BRepCheck_InvalidDegeneratedFlag:
1523
                            str << "Invalid degenerated flag" << std::endl;
1524
                            break;
1525
                        case BRepCheck_FreeEdge:
1526
                            str << "Free edge" << std::endl;
1527
                            break;
1528
                        case BRepCheck_InvalidMultiConnexity:
1529
                            str << "Invalid multi-connexity" << std::endl;
1530
                            break;
1531
                        case BRepCheck_InvalidRange:
1532
                            str << "Invalid range" << std::endl;
1533
                            break;
1534
                        case BRepCheck_EmptyWire:
1535
                            str << "Empty wire" << std::endl;
1536
                            break;
1537
                        case BRepCheck_RedundantEdge:
1538
                            str << "Redundant edge" << std::endl;
1539
                            break;
1540
                        case BRepCheck_SelfIntersectingWire:
1541
                            str << "Self-intersecting wire" << std::endl;
1542
                            break;
1543
                        case BRepCheck_NoSurface:
1544
                            str << "No surface" << std::endl;
1545
                            break;
1546
                        case BRepCheck_InvalidWire:
1547
                            str << "Invalid wires" << std::endl;
1548
                            break;
1549
                        case BRepCheck_RedundantWire:
1550
                            str << "Redundant wires" << std::endl;
1551
                            break;
1552
                        case BRepCheck_IntersectingWires:
1553
                            str << "Intersecting wires" << std::endl;
1554
                            break;
1555
                        case BRepCheck_InvalidImbricationOfWires:
1556
                            str << "Invalid imbrication of wires" << std::endl;
1557
                            break;
1558
                        case BRepCheck_EmptyShell:
1559
                            str << "Empty shell" << std::endl;
1560
                            break;
1561
                        case BRepCheck_RedundantFace:
1562
                            str << "Redundant face" << std::endl;
1563
                            break;
1564
                        case BRepCheck_UnorientableShape:
1565
                            str << "Unorientable shape" << std::endl;
1566
                            break;
1567
                        case BRepCheck_NotClosed:
1568
                            str << "Not closed" << std::endl;
1569
                            break;
1570
                        case BRepCheck_NotConnected:
1571
                            str << "Not connected" << std::endl;
1572
                            break;
1573
                        case BRepCheck_SubshapeNotInShape:
1574
                            str << "Sub-shape not in shape" << std::endl;
1575
                            break;
1576
                        case BRepCheck_BadOrientation:
1577
                            str << "Bad orientation" << std::endl;
1578
                            break;
1579
                        case BRepCheck_BadOrientationOfSubshape:
1580
                            str << "Bad orientation of sub-shape" << std::endl;
1581
                            break;
1582
                        case BRepCheck_InvalidToleranceValue:
1583
                            str << "Invalid tolerance value" << std::endl;
1584
                            break;
1585
                        case BRepCheck_CheckFail:
1586
                            str << "Check failed" << std::endl;
1587
                            break;
1588
                        default:
1589
                            str << "Undetermined error" << std::endl;
1590
                            break;
1591
                        }
1592

1593
                        it.Next();
1594
                    }
1595
                }
1596
            }
1597

1598
            return false; // errors detected
1599
        }
1600
        else if (runBopCheck) {
1601

1602
            TopoDS_Shape BOPCopy = BRepBuilderAPI_Copy(this->_Shape).Shape();
1603
            BOPAlgo_ArgumentAnalyzer BOPCheck;
1604
            BOPCheck.SetShape1(BOPCopy);
1605
            //all settings are false by default. so only turn on what we want.
1606
            BOPCheck.ArgumentTypeMode() = true;
1607
            BOPCheck.SelfInterMode() = true;
1608
            BOPCheck.SmallEdgeMode() = true;
1609
            BOPCheck.RebuildFaceMode() = true;
1610
            BOPCheck.ContinuityMode() = true;
1611
            BOPCheck.SetParallelMode(true); //this doesn't help for speed right now(occt 6.9.1).
1612
            BOPCheck.SetRunParallel(true); //performance boost, use all available cores
1613
            BOPCheck.TangentMode() = true; //these 4 new tests add about 5% processing time.
1614
            BOPCheck.MergeVertexMode() = true;
1615
            BOPCheck.CurveOnSurfaceMode() = true;
1616
            BOPCheck.MergeEdgeMode() = true;
1617
            BOPCheck.Perform();
1618
            if (!BOPCheck.HasFaulty())
1619
                return true;
1620

1621
            str << "BOP check found the following errors:" << std::endl;
1622
            static std::vector<std::string> shapeEnumToString = buildShapeEnumVector();
1623
            static std::vector<std::string> bopEnumToString = buildBOPCheckResultVector();
1624
            const BOPAlgo_ListOfCheckResult &BOPResults = BOPCheck.GetCheckResult();
1625
            BOPAlgo_ListIteratorOfListOfCheckResult BOPResultsIt(BOPResults);
1626
            for (; BOPResultsIt.More(); BOPResultsIt.Next()) {
1627
                const BOPAlgo_CheckResult &current = BOPResultsIt.Value();
1628

1629
                const TopTools_ListOfShape &faultyShapes1 = current.GetFaultyShapes1();
1630
                TopTools_ListIteratorOfListOfShape faultyShapes1It(faultyShapes1);
1631
                for (;faultyShapes1It.More(); faultyShapes1It.Next()) {
1632
                    const TopoDS_Shape &faultyShape = faultyShapes1It.Value();
1633
                    str << "Error in " << shapeEnumToString[faultyShape.ShapeType()] << ": ";
1634
                    str << bopEnumToString[current.GetCheckStatus()] << std::endl;
1635
                }
1636
            }
1637
            return false;
1638
        }
1639
    }
1640
    return true;
1641
}
1642

1643
bool TopoShape::isClosed() const
1644
{
1645
    if (this->_Shape.IsNull())
1646
        return false;
1647
    bool closed = false;
1648
    switch (this->_Shape.ShapeType()) {
1649
    case TopAbs_SHELL:
1650
    case TopAbs_WIRE:
1651
    case TopAbs_EDGE:
1652
        closed = BRep_Tool::IsClosed(this->_Shape) ? true : false;
1653
        break;
1654
    case TopAbs_COMPSOLID:
1655
    case TopAbs_SOLID:
1656
        {
1657
            closed = true;
1658
            TopExp_Explorer xp(this->_Shape, TopAbs_SHELL);
1659
            while (xp.More()) {
1660
                closed &= BRep_Tool::IsClosed(xp.Current()) ? true : false;
1661
                xp.Next();
1662
            }
1663
        }
1664
        break;
1665
    case TopAbs_COMPOUND:
1666
        {
1667
            closed = true;
1668
            TopExp_Explorer xp;
1669
            for (xp.Init(this->_Shape, TopAbs_SHELL); xp.More(); xp.Next()) {
1670
                closed &= BRep_Tool::IsClosed(xp.Current()) ? true : false;
1671
            }
1672
            for (xp.Init(this->_Shape, TopAbs_FACE, TopAbs_SHELL); xp.More(); xp.Next()) {
1673
                closed &= BRep_Tool::IsClosed(xp.Current()) ? true : false;
1674
            }
1675
            for (xp.Init(this->_Shape, TopAbs_WIRE, TopAbs_FACE); xp.More(); xp.Next()) {
1676
                closed &= BRep_Tool::IsClosed(xp.Current()) ? true : false;
1677
            }
1678
            for (xp.Init(this->_Shape, TopAbs_EDGE, TopAbs_WIRE); xp.More(); xp.Next()) {
1679
                closed &= BRep_Tool::IsClosed(xp.Current()) ? true : false;
1680
            }
1681
            for (xp.Init(this->_Shape, TopAbs_VERTEX, TopAbs_EDGE); xp.More(); xp.Next()) {
1682
                closed &= BRep_Tool::IsClosed(xp.Current()) ? true : false;
1683
            }
1684
        }
1685
        break;
1686
    case TopAbs_FACE:
1687
    case TopAbs_VERTEX:
1688
    case TopAbs_SHAPE:
1689
        closed = BRep_Tool::IsClosed(this->_Shape) ? true : false;
1690
        break;
1691
    }
1692
    return closed;
1693
}
1694

1695
TopoDS_Shape TopoShape::cut(TopoDS_Shape shape) const
1696
{
1697
    if (this->_Shape.IsNull())
1698
        return this->_Shape;
1699
    if (shape.IsNull())
1700
        return this->_Shape;
1701
    BRepAlgoAPI_Cut mkCut(this->_Shape, shape);
1702
    return makeShell(mkCut.Shape());
1703
}
1704

1705
TopoDS_Shape TopoShape::cut(const std::vector<TopoDS_Shape>& shapes, Standard_Real tolerance) const
1706
{
1707
    if (this->_Shape.IsNull())
1708
        return this->_Shape;
1709
    BRepAlgoAPI_Cut mkCut;
1710
    mkCut.SetRunParallel(true);
1711
    TopTools_ListOfShape shapeArguments,shapeTools;
1712
    shapeArguments.Append(this->_Shape);
1713
    for (const auto & shape : shapes) {
1714
        if (shape.IsNull())
1715
            throw Base::ValueError("Tool shape is null");
1716
        if (tolerance > 0.0)
1717
            // workaround for http://dev.opencascade.org/index.php?q=node/1056#comment-520
1718
            shapeTools.Append(BRepBuilderAPI_Copy(shape).Shape());
1719
        else
1720
            shapeTools.Append(shape);
1721
    }
1722

1723
    mkCut.SetArguments(shapeArguments);
1724
    mkCut.SetTools(shapeTools);
1725
    if (tolerance > 0.0)
1726
        mkCut.SetFuzzyValue(tolerance);
1727
    mkCut.Build();
1728
    if (!mkCut.IsDone())
1729
        throw Base::RuntimeError("Multi cut failed");
1730

1731
    TopoDS_Shape resShape = mkCut.Shape();
1732
    return makeShell(resShape);
1733
}
1734

1735
TopoDS_Shape TopoShape::common(TopoDS_Shape shape) const
1736
{
1737
    if (this->_Shape.IsNull())
1738
        return this->_Shape;
1739
    if (shape.IsNull())
1740
        return shape;
1741
    BRepAlgoAPI_Common mkCommon(this->_Shape, shape);
1742
    return makeShell(mkCommon.Shape());
1743
}
1744

1745
TopoDS_Shape TopoShape::common(const std::vector<TopoDS_Shape>& shapes, Standard_Real tolerance) const
1746
{
1747
    if (this->_Shape.IsNull())
1748
        return this->_Shape;
1749
    BRepAlgoAPI_Common mkCommon;
1750
    mkCommon.SetRunParallel(true);
1751
    TopTools_ListOfShape shapeArguments,shapeTools;
1752
    shapeArguments.Append(this->_Shape);
1753
    for (const auto & shape : shapes) {
1754
        if (shape.IsNull())
1755
            throw Base::ValueError("Tool shape is null");
1756
        if (tolerance > 0.0)
1757
            // workaround for http://dev.opencascade.org/index.php?q=node/1056#comment-520
1758
            shapeTools.Append(BRepBuilderAPI_Copy(shape).Shape());
1759
        else
1760
            shapeTools.Append(shape);
1761
    }
1762

1763
    mkCommon.SetArguments(shapeArguments);
1764
    mkCommon.SetTools(shapeTools);
1765
    if (tolerance > 0.0)
1766
        mkCommon.SetFuzzyValue(tolerance);
1767
    mkCommon.Build();
1768
    if (!mkCommon.IsDone())
1769
        throw Base::RuntimeError("Multi common failed");
1770

1771
    TopoDS_Shape resShape = mkCommon.Shape();
1772
    return makeShell(resShape);
1773
}
1774

1775
TopoDS_Shape TopoShape::fuse(TopoDS_Shape shape) const
1776
{
1777
    if (this->_Shape.IsNull())
1778
        return shape;
1779
    if (shape.IsNull())
1780
        return this->_Shape;
1781
    BRepAlgoAPI_Fuse mkFuse(this->_Shape, shape);
1782
    return makeShell(mkFuse.Shape());
1783
}
1784

1785
TopoDS_Shape TopoShape::fuse(const std::vector<TopoDS_Shape>& shapes, Standard_Real tolerance) const
1786
{
1787
    if (this->_Shape.IsNull())
1788
        Standard_Failure::Raise("Base shape is null");
1789

1790
    BRepAlgoAPI_Fuse mkFuse;
1791
    mkFuse.SetRunParallel(true);
1792
    TopTools_ListOfShape shapeArguments,shapeTools;
1793
    shapeArguments.Append(this->_Shape);
1794
    for (const auto & shape : shapes) {
1795
        if (shape.IsNull())
1796
            throw NullShapeException("Tool shape is null");
1797
        if (tolerance > 0.0)
1798
            // workaround for http://dev.opencascade.org/index.php?q=node/1056#comment-520
1799
            shapeTools.Append(BRepBuilderAPI_Copy(shape).Shape());
1800
        else
1801
            shapeTools.Append(shape);
1802
    }
1803
    mkFuse.SetArguments(shapeArguments);
1804
    mkFuse.SetTools(shapeTools);
1805
    if (tolerance > 0.0)
1806
        mkFuse.SetFuzzyValue(tolerance);
1807
    mkFuse.Build();
1808
    if (!mkFuse.IsDone())
1809
        throw Base::RuntimeError("Multi fuse failed");
1810

1811
    TopoDS_Shape resShape = mkFuse.Shape();
1812
    return makeShell(resShape);
1813
}
1814

1815
TopoDS_Shape TopoShape::oldFuse(TopoDS_Shape shape) const
1816
{
1817
    if (this->_Shape.IsNull())
1818
        Standard_Failure::Raise("Base shape is null");
1819
    if (shape.IsNull())
1820
        Standard_Failure::Raise("Tool shape is null");
1821

1822
    throw Standard_Failure("BRepAlgo_Fuse is deprecated since OCCT 7.3");
1823
}
1824

1825
TopoDS_Shape TopoShape::section(TopoDS_Shape shape, Standard_Boolean approximate) const
1826
{
1827
    if (this->_Shape.IsNull())
1828
        Standard_Failure::Raise("Base shape is null");
1829
    if (shape.IsNull())
1830
        Standard_Failure::Raise("Tool shape is null");
1831
    BRepAlgoAPI_Section mkSection;
1832
    mkSection.Init1(this->_Shape);
1833
    mkSection.Init2(shape);
1834
    mkSection.Approximation(approximate);
1835
    mkSection.Build();
1836
    if (!mkSection.IsDone())
1837
        throw Base::RuntimeError("Section failed");
1838
    return mkSection.Shape();
1839
}
1840

1841
TopoDS_Shape TopoShape::section(const std::vector<TopoDS_Shape>& shapes,
1842
                                Standard_Real tolerance,
1843
                                Standard_Boolean approximate) const
1844
{
1845
    if (this->_Shape.IsNull())
1846
        Standard_Failure::Raise("Base shape is null");
1847

1848
    BRepAlgoAPI_Section mkSection;
1849
    mkSection.SetRunParallel(true);
1850
    mkSection.Approximation(approximate);
1851
    TopTools_ListOfShape shapeArguments,shapeTools;
1852
    shapeArguments.Append(this->_Shape);
1853
    for (const auto & shape : shapes) {
1854
        if (shape.IsNull())
1855
            throw Base::ValueError("Tool shape is null");
1856
        if (tolerance > 0.0)
1857
            // workaround for http://dev.opencascade.org/index.php?q=node/1056#comment-520
1858
            shapeTools.Append(BRepBuilderAPI_Copy(shape).Shape());
1859
        else
1860
            shapeTools.Append(shape);
1861
    }
1862

1863
    mkSection.SetArguments(shapeArguments);
1864
    mkSection.SetTools(shapeTools);
1865
    if (tolerance > 0.0)
1866
        mkSection.SetFuzzyValue(tolerance);
1867
    mkSection.Build();
1868
    if (!mkSection.IsDone())
1869
        throw Base::RuntimeError("Multi section failed");
1870

1871
    TopoDS_Shape resShape = mkSection.Shape();
1872
    return resShape;
1873
}
1874

1875
std::list<TopoDS_Wire> TopoShape::slice(const Base::Vector3d& dir, double d) const
1876
{
1877
    CrossSection cs(dir.x, dir.y, dir.z, this->_Shape);
1878
    return cs.slice(d);
1879
}
1880

1881
TopoDS_Compound TopoShape::slices(const Base::Vector3d& dir, const std::vector<double>& d) const
1882
{
1883
    std::vector< std::list<TopoDS_Wire> > wire_list;
1884
    CrossSection cs(dir.x, dir.y, dir.z, this->_Shape);
1885
    for (double jt : d) {
1886
        wire_list.push_back(cs.slice(jt));
1887
    }
1888

1889
    std::vector< std::list<TopoDS_Wire> >::const_iterator ft;
1890
    TopoDS_Compound comp;
1891
    BRep_Builder builder;
1892
    builder.MakeCompound(comp);
1893

1894
    for (ft = wire_list.begin(); ft != wire_list.end(); ++ft) {
1895
        const std::list<TopoDS_Wire>& w = *ft;
1896
        for (const auto & wt : w) {
1897
            if (!wt.IsNull())
1898
                builder.Add(comp, wt);
1899
        }
1900
    }
1901

1902
    return comp;
1903
}
1904

1905
TopoDS_Shape TopoShape::generalFuse(const std::vector<TopoDS_Shape> &sOthers, Standard_Real tolerance,
1906
                                    std::vector<TopTools_ListOfShape>* mapInOut) const
1907
{
1908
    if (this->_Shape.IsNull())
1909
        Standard_Failure::Raise("Base shape is null");
1910

1911
    BRepAlgoAPI_BuilderAlgo mkGFA;
1912
    mkGFA.SetRunParallel(true);
1913
    TopTools_ListOfShape GFAArguments;
1914
    GFAArguments.Append(this->_Shape);
1915
    for (const TopoDS_Shape &it: sOthers) {
1916
        if (it.IsNull())
1917
            throw NullShapeException("Tool shape is null");
1918
        if (tolerance > 0.0)
1919
            // workaround for http://dev.opencascade.org/index.php?q=node/1056#comment-520
1920
            GFAArguments.Append(BRepBuilderAPI_Copy(it).Shape());
1921
        else
1922
            GFAArguments.Append(it);
1923
    }
1924
    mkGFA.SetArguments(GFAArguments);
1925
    if (tolerance > 0.0)
1926
        mkGFA.SetFuzzyValue(tolerance);
1927
    mkGFA.SetNonDestructive(Standard_True);
1928
    mkGFA.Build();
1929
    if (!mkGFA.IsDone())
1930
        throw BooleanException("MultiFusion failed");
1931
    TopoDS_Shape resShape = mkGFA.Shape();
1932
    if (mapInOut){
1933
        for(TopTools_ListIteratorOfListOfShape it(GFAArguments); it.More(); it.Next()){
1934
            mapInOut->push_back(mkGFA.Modified(it.Value()));
1935
        }
1936
    }
1937
    return resShape;
1938
}
1939

1940
TopoDS_Shape TopoShape::makePipe(const TopoDS_Shape& profile) const
1941
{
1942
    if (this->_Shape.IsNull())
1943
        Standard_Failure::Raise("Cannot sweep along empty spine");
1944
    if (this->_Shape.ShapeType() != TopAbs_WIRE)
1945
        Standard_Failure::Raise("Spine shape is not a wire");
1946
    if (profile.IsNull())
1947
        Standard_Failure::Raise("Cannot sweep empty profile");
1948
    BRepOffsetAPI_MakePipe mkPipe(TopoDS::Wire(this->_Shape), profile);
1949
    return mkPipe.Shape();
1950
}
1951

1952
TopoDS_Shape TopoShape::makePipeShell(const TopTools_ListOfShape& profiles,
1953
                                      const Standard_Boolean make_solid,
1954
                                      const Standard_Boolean isFrenet,
1955
                                      int transition) const
1956
{
1957
    if (this->_Shape.IsNull())
1958
        Standard_Failure::Raise("Cannot sweep along empty spine");
1959
    if (this->_Shape.ShapeType() != TopAbs_WIRE)
1960
        Standard_Failure::Raise("Spine shape is not a wire");
1961

1962
    BRepOffsetAPI_MakePipeShell mkPipeShell(TopoDS::Wire(this->_Shape));
1963
    BRepBuilderAPI_TransitionMode transMode;
1964
    switch (transition) {
1965
        case 1: transMode = BRepBuilderAPI_RightCorner;
1966
            break;
1967
        case 2: transMode = BRepBuilderAPI_RoundCorner;
1968
            break;
1969
        default: transMode = BRepBuilderAPI_Transformed;
1970
            break;
1971
    }
1972
    mkPipeShell.SetMode(isFrenet);
1973
    mkPipeShell.SetTransitionMode(transMode);
1974
    TopTools_ListIteratorOfListOfShape it;
1975
    for (it.Initialize(profiles); it.More(); it.Next()) {
1976
        mkPipeShell.Add(TopoDS_Shape(it.Value()));
1977
    }
1978

1979
    if (!mkPipeShell.IsReady())
1980
        throw Standard_Failure("shape is not ready to build");
1981

1982
    mkPipeShell.Build();
1983

1984
    if (make_solid)
1985
        mkPipeShell.MakeSolid();
1986

1987
    return mkPipeShell.Shape();
1988
}
1989

1990
static Handle(Law_Function) CreateBsFunction (const Standard_Real theFirst, const Standard_Real theLast, const Standard_Real theRadius)
1991
{
1992
    (void)theRadius;
1993
    //Handle(Law_BSpline) aBs;
1994
    //Handle(Law_BSpFunc) aFunc = new Law_BSpFunc (aBs, theFirst, theLast);
1995
    Handle(Law_Constant) aFunc = new Law_Constant();
1996
    aFunc->Set(1, theFirst, theLast);
1997
    return aFunc;
1998
}
1999

2000
TopoDS_Shape TopoShape::makeTube(double radius, double tol, int cont, int maxdegree, int maxsegm) const
2001
{
2002
    // http://opencascade.blogspot.com/2009/11/surface-modeling-part3.html
2003
    Standard_Real theTol = tol;
2004
    Standard_Real theRadius = radius;
2005
    //Standard_Boolean theIsPolynomial = Standard_True;
2006
    Standard_Boolean myIsElem = Standard_True;
2007
    GeomAbs_Shape theContinuity = GeomAbs_Shape(cont);
2008
    Standard_Integer theMaxDegree = maxdegree;
2009
    Standard_Integer theMaxSegment = maxsegm;
2010

2011
    if (this->_Shape.IsNull())
2012
        Standard_Failure::Raise("Cannot sweep along empty spine");
2013

2014
#if OCC_VERSION_HEX >= 0x070600
2015

2016
    Handle(Adaptor3d_Curve) myPath;
2017

2018
    if (this->_Shape.ShapeType() == TopAbs_EDGE) {
2019
        const TopoDS_Edge& path_edge = TopoDS::Edge(this->_Shape);
2020
        myPath = new BRepAdaptor_Curve(path_edge);
2021
    }
2022
#else
2023
    Handle(Adaptor3d_HCurve) myPath;
2024
    if (this->_Shape.ShapeType() == TopAbs_EDGE) {
2025
        const TopoDS_Edge& path_edge = TopoDS::Edge(this->_Shape);
2026
        BRepAdaptor_Curve path_adapt(path_edge);
2027
        myPath = new BRepAdaptor_HCurve(path_adapt);
2028
    }
2029
#endif
2030

2031
    else {
2032
        Standard_Failure::Raise("Spine shape is not an edge");
2033
    }
2034

2035
    //circular profile
2036
    Handle(Geom_Circle) aCirc = new Geom_Circle (gp::XOY(), theRadius);
2037
    aCirc->Rotate (gp::OZ(), M_PI/2.);
2038

2039
    //perpendicular section
2040
    Handle(Law_Function) myEvol = ::CreateBsFunction (myPath->FirstParameter(), myPath->LastParameter(), theRadius);
2041
    Handle(GeomFill_SectionLaw) aSec = new GeomFill_EvolvedSection(aCirc, myEvol);
2042
    Handle(GeomFill_LocationLaw) aLoc = new GeomFill_CurveAndTrihedron(new GeomFill_CorrectedFrenet);
2043
    aLoc->SetCurve (myPath);
2044

2045
    GeomFill_Sweep mkSweep (aLoc, myIsElem);
2046
    mkSweep.SetTolerance (theTol);
2047
    mkSweep.Build (aSec, GeomFill_Location, theContinuity, theMaxDegree, theMaxSegment);
2048
    if (mkSweep.IsDone()) {
2049
        Handle(Geom_Surface) mySurface = mkSweep.Surface();
2050
        //Standard_Real myError = mkSweep.ErrorOnSurface();
2051

2052
        Standard_Real u1,u2,v1,v2;
2053
        mySurface->Bounds(u1,u2,v1,v2);
2054
        BRepBuilderAPI_MakeFace mkBuilder(mySurface, u1, u2, v1, v2 , Precision::Confusion()
2055
        );
2056
        return mkBuilder.Shape();
2057
    }
2058

2059
    return {};
2060
}
2061

2062

2063
TopoDS_Shape TopoShape::makeSweep(const TopoDS_Shape& profile, double tol, int fillMode) const
2064
{
2065
    // http://opencascade.blogspot.com/2009/10/surface-modeling-part2.html
2066
    if (this->_Shape.IsNull())
2067
        Standard_Failure::Raise("Cannot sweep along empty spine");
2068
    if (this->_Shape.ShapeType() != TopAbs_EDGE)
2069
        Standard_Failure::Raise("Spine shape is not an edge");
2070

2071
    if (profile.IsNull())
2072
        Standard_Failure::Raise("Cannot sweep with empty profile");
2073
    if (profile.ShapeType() != TopAbs_EDGE)
2074
        Standard_Failure::Raise("Profile shape is not an edge");
2075

2076
    const TopoDS_Edge& path_edge = TopoDS::Edge(this->_Shape);
2077
    const TopoDS_Edge& prof_edge = TopoDS::Edge(profile);
2078

2079
    BRepAdaptor_Curve path_adapt(path_edge);
2080
    double umin = path_adapt.FirstParameter();
2081
    double umax = path_adapt.LastParameter();
2082
    Handle(Geom_Curve) hPath = path_adapt.Curve().Curve();
2083

2084
    // Apply placement of the shape to the curve
2085
    TopLoc_Location loc1 = path_edge.Location();
2086
    hPath = Handle(Geom_Curve)::DownCast(hPath->Transformed(loc1.Transformation()));
2087

2088
    if (hPath.IsNull())
2089
        Standard_Failure::Raise("invalid curve in path edge");
2090

2091
    BRepAdaptor_Curve prof_adapt(prof_edge);
2092
    double vmin = prof_adapt.FirstParameter();
2093
    double vmax = prof_adapt.LastParameter();
2094
    Handle(Geom_Curve) hProfile = prof_adapt.Curve().Curve();
2095

2096
    // Apply placement of the shape to the curve
2097
    TopLoc_Location loc2 = prof_edge.Location();
2098
    hProfile = Handle(Geom_Curve)::DownCast(hProfile->Transformed(loc2.Transformation()));
2099

2100
    if (hProfile.IsNull())
2101
        Standard_Failure::Raise("invalid curve in profile edge");
2102

2103
    GeomFill_Pipe mkSweep(hPath, hProfile, static_cast<GeomFill_Trihedron>(fillMode));
2104
    mkSweep.GenerateParticularCase(Standard_True);
2105
    mkSweep.Perform(tol, Standard_False, GeomAbs_C1, BSplCLib::MaxDegree(), 1000);
2106

2107
    const Handle(Geom_Surface)& surf = mkSweep.Surface();
2108
    BRepBuilderAPI_MakeFace mkBuilder(surf, umin, umax, vmin, vmax , Precision::Confusion());
2109
    return mkBuilder.Face();
2110
}
2111

2112
TopoDS_Shape TopoShape::makeTorus(Standard_Real radius1, Standard_Real radius2,
2113
                                  Standard_Real angle1, Standard_Real angle2,
2114
                                  Standard_Real angle3, Standard_Boolean isSolid) const
2115
{
2116
    // https://forum.freecad.org/viewtopic.php?f=3&t=1445
2117
    // https://forum.freecad.org/viewtopic.php?f=3&t=52719
2118
    // Build a torus
2119
    gp_Circ circle;
2120
    circle.SetRadius(radius2);
2121
    gp_Pnt pos(radius1,0,0);
2122
    gp_Dir dir(0,1,0);
2123
    circle.SetAxis(gp_Ax1(pos, dir));
2124

2125
    BRepBuilderAPI_MakeEdge mkEdge(circle, Base::toRadians<double>(angle1),
2126
                                           Base::toRadians<double>(angle2));
2127
    BRepBuilderAPI_MakeWire mkWire;
2128
    mkWire.Add(mkEdge.Edge());
2129

2130
    if ((angle1 > -180.0 || angle2 < 180.0) && isSolid) {
2131
        BRepBuilderAPI_MakeVertex mkVertex(pos);
2132
        BRepBuilderAPI_MakeEdge mkEdge1(mkVertex.Vertex(), mkEdge.Vertex1());
2133
        BRepBuilderAPI_MakeEdge mkEdge2(mkVertex.Vertex(), mkEdge.Vertex2());
2134
        mkWire.Add(mkEdge1.Edge());
2135
        mkWire.Add(mkEdge2.Edge());
2136
    }
2137

2138
    BRepBuilderAPI_MakeFace mkFace(mkWire.Wire());
2139
    BRepPrimAPI_MakeRevol mkRevol(mkFace.Face(), gp_Ax1(gp_Pnt(0,0,0), gp_Dir(0,0,1)),
2140
        Base::toRadians<double>(angle3), Standard_True);
2141
    return mkRevol.Shape();
2142
}
2143

2144
TopoDS_Shape TopoShape::makeHelix(Standard_Real pitch, Standard_Real height,
2145
                                  Standard_Real radius, Standard_Real angle,
2146
                                  Standard_Boolean leftHanded,
2147
                                  Standard_Boolean newStyle) const
2148
{
2149
    if (fabs(pitch) < Precision::Confusion())
2150
        Standard_Failure::Raise("Pitch of helix too small");
2151

2152
    if (fabs(height) < Precision::Confusion())
2153
        Standard_Failure::Raise("Height of helix too small");
2154

2155
    if ((height > 0 && pitch < 0) || (height < 0 && pitch > 0))
2156
        Standard_Failure::Raise("Pitch and height of helix not compatible");
2157

2158
    gp_Ax2 cylAx2(gp_Pnt(0.0,0.0,0.0) , gp::DZ());
2159
    Handle(Geom_Surface) surf;
2160
    if (angle < Precision::Confusion()) {
2161
        if (radius < Precision::Confusion())
2162
            Standard_Failure::Raise("Radius of helix too small");
2163
        surf = new Geom_CylindricalSurface(cylAx2, radius);
2164
    }
2165
    else {
2166
        angle = Base::toRadians(angle);
2167
        if (angle < Precision::Confusion())
2168
            Standard_Failure::Raise("Angle of helix too small");
2169
        surf = new Geom_ConicalSurface(gp_Ax3(cylAx2), angle, radius);
2170
    }
2171

2172
    gp_Pnt2d aPnt(0, 0);
2173
    gp_Dir2d aDir(2. * M_PI, pitch);
2174
    Standard_Real coneDir = 1.0;
2175
    if (leftHanded) {
2176
        aDir.SetCoord(-2. * M_PI, pitch);
2177
        coneDir = -1.0;
2178
    }
2179
    gp_Ax2d aAx2d(aPnt, aDir);
2180

2181
    Handle(Geom2d_Line) line = new Geom2d_Line(aAx2d);
2182
    gp_Pnt2d beg = line->Value(0);
2183
    gp_Pnt2d end = line->Value(sqrt(4.0*M_PI*M_PI+pitch*pitch)*(height/pitch));
2184

2185
    if (newStyle) {
2186
        // See discussion at 0001247: Part Conical Helix Height/Pitch Incorrect
2187
        if (angle >= Precision::Confusion()) {
2188
            // calculate end point for conical helix
2189
            Standard_Real v = height / cos(angle);
2190
            Standard_Real u = coneDir * (height/pitch) * 2.0 * M_PI;
2191
            gp_Pnt2d cend(u, v);
2192
            end = cend;
2193
        }
2194
    }
2195

2196
    Handle(Geom2d_TrimmedCurve) segm = GCE2d_MakeSegment(beg , end);
2197

2198
    TopoDS_Edge edgeOnSurf = BRepBuilderAPI_MakeEdge(segm , surf);
2199
    TopoDS_Wire wire = BRepBuilderAPI_MakeWire(edgeOnSurf);
2200
    BRepLib::BuildCurves3d(wire);
2201
    return TopoDS_Shape(std::move(wire));
2202
}
2203

2204
//***********
2205
// makeLongHelix is a workaround for an OCC problem found in helices with more than
2206
// some magic number of turns.  See Mantis #0954.
2207
//***********
2208
TopoDS_Shape TopoShape::makeLongHelix(Standard_Real pitch, Standard_Real height,
2209
                                      Standard_Real radius, Standard_Real angle,
2210
                                      Standard_Boolean leftHanded) const
2211
{
2212
    if (pitch < Precision::Confusion())
2213
        Standard_Failure::Raise("Pitch of helix too small");
2214

2215
    if (height < Precision::Confusion())
2216
        Standard_Failure::Raise("Height of helix too small");
2217

2218
    gp_Ax2 cylAx2(gp_Pnt(0.0,0.0,0.0) , gp::DZ());
2219
    Handle(Geom_Surface) surf;
2220
    Standard_Boolean isCylinder;
2221

2222
    if (std::fabs(angle) < Precision::Confusion()) {                           // Cylindrical helix
2223
        if (radius < Precision::Confusion())
2224
            Standard_Failure::Raise("Radius of helix too small");
2225
        surf= new Geom_CylindricalSurface(cylAx2, radius);
2226
        isCylinder = true;
2227
    }
2228
    else {                                                                     // Conical helix
2229
        angle = Base::toRadians(angle);
2230
        surf = new Geom_ConicalSurface(gp_Ax3(cylAx2), angle, radius);
2231
        isCylinder = false;
2232
    }
2233

2234
    Standard_Real turns = height/pitch;
2235
    unsigned long wholeTurns = floor(turns);
2236
    Standard_Real partTurn = turns - wholeTurns;
2237

2238
    gp_Pnt2d aPnt(0, 0);
2239
    gp_Dir2d aDir(2. * M_PI, pitch);
2240
    Standard_Real coneDir = 1.0;
2241
    if (leftHanded) {
2242
        aDir.SetCoord(-2. * M_PI, pitch);
2243
        coneDir = -1.0;
2244
    }
2245
    gp_Ax2d aAx2d(aPnt, aDir);
2246
    Handle(Geom2d_Line) line = new Geom2d_Line(aAx2d);
2247
    gp_Pnt2d beg = line->Value(0);
2248
    gp_Pnt2d end;
2249
    Standard_Real u,v;
2250
    BRepBuilderAPI_MakeWire mkWire;
2251
    Handle(Geom2d_TrimmedCurve) segm;
2252
    TopoDS_Edge edgeOnSurf;
2253

2254
    for (unsigned long i = 0; i < wholeTurns; i++) {
2255
        if (isCylinder) {
2256
            end = line->Value(sqrt(4.0*M_PI*M_PI+pitch*pitch)*(i+1));
2257
        }
2258
        else {
2259
            u = coneDir * (i+1) * 2.0 * M_PI;
2260
            v = ((i+1) * pitch) / cos(angle);
2261
            end = gp_Pnt2d(u, v);
2262
        }
2263
        segm = GCE2d_MakeSegment(beg , end);
2264
        edgeOnSurf = BRepBuilderAPI_MakeEdge(segm , surf);
2265
        mkWire.Add(edgeOnSurf);
2266
        beg = end;
2267
    }
2268

2269
    if (partTurn > Precision::Confusion()) {
2270
        if (isCylinder) {
2271
            end = line->Value(sqrt(4.0*M_PI*M_PI+pitch*pitch)*turns);
2272
        }
2273
        else {
2274
            u = coneDir * turns * 2.0 * M_PI;
2275
            v = height / cos(angle);
2276
            end = gp_Pnt2d(u, v);
2277
        }
2278
        segm = GCE2d_MakeSegment(beg , end);
2279
        edgeOnSurf = BRepBuilderAPI_MakeEdge(segm , surf);
2280
        mkWire.Add(edgeOnSurf);
2281
    }
2282

2283
    TopoDS_Wire wire = mkWire.Wire();
2284
    BRepLib::BuildCurves3d(wire);
2285
    return TopoDS_Shape(std::move(wire));
2286
}
2287

2288
TopoDS_Shape TopoShape::makeSpiralHelix(Standard_Real radiusbottom, Standard_Real radiustop,
2289
                                  Standard_Real height, Standard_Real nbturns,
2290
                                  Standard_Real breakperiod, Standard_Boolean leftHanded) const
2291
{
2292
    // 1000 periods is an OCCT limit. The 3D curve gets truncated
2293
    // if the 2D curve spans beyond this limit.
2294
    if ((breakperiod < 0) || (breakperiod > 1000))
2295
        Standard_Failure::Raise("Break period must be in [0, 1000]");
2296
    if (breakperiod == 0)
2297
        breakperiod = 1000;
2298
    if (nbturns <= 0)
2299
        Standard_Failure::Raise("Number of turns must be greater than 0");
2300

2301
    Standard_Real nbPeriods = nbturns/breakperiod;
2302
    Standard_Real nbFullPeriods = floor(nbPeriods);
2303
    Standard_Real partPeriod = nbPeriods - nbFullPeriods;
2304

2305
    // A Bezier curve is used below, to get a periodic surface also for spirals.
2306
    TColgp_Array1OfPnt poles(1,2);
2307
    poles(1) = gp_Pnt(radiusbottom, 0, 0);
2308
    poles(2) = gp_Pnt(radiustop, 0, height);
2309
    Handle(Geom_BezierCurve) meridian = new Geom_BezierCurve(poles);
2310

2311
    gp_Ax1 axis(gp_Pnt(0.0,0.0,0.0) , gp::DZ());
2312
    Handle(Geom_Surface) surf = new Geom_SurfaceOfRevolution(meridian, axis);
2313

2314
    gp_Pnt2d beg(0, 0);
2315
    gp_Pnt2d end(0, 0);
2316
    gp_Vec2d dir(breakperiod * 2.0 * M_PI, 1 / nbPeriods);
2317
    if (leftHanded == Standard_True)
2318
        dir = gp_Vec2d(-breakperiod * 2.0 * M_PI, 1 / nbPeriods);
2319
    Handle(Geom2d_TrimmedCurve) segm;
2320
    TopoDS_Edge edgeOnSurf;
2321
    BRepBuilderAPI_MakeWire mkWire;
2322
    for (unsigned long i = 0; i < nbFullPeriods; i++) {
2323
        end = beg.Translated(dir);
2324
        segm = GCE2d_MakeSegment(beg , end);
2325
        edgeOnSurf = BRepBuilderAPI_MakeEdge(segm , surf);
2326
        mkWire.Add(edgeOnSurf);
2327
        beg = end;
2328
    }
2329
    if (partPeriod > Precision::Confusion()) {
2330
        dir.Scale(partPeriod);
2331
        end = beg.Translated(dir);
2332
        segm = GCE2d_MakeSegment(beg , end);
2333
        edgeOnSurf = BRepBuilderAPI_MakeEdge(segm , surf);
2334
        mkWire.Add(edgeOnSurf);
2335
    }
2336

2337
    TopoDS_Wire wire = mkWire.Wire();
2338
    BRepLib::BuildCurves3d(wire, Precision::Confusion(), GeomAbs_Shape::GeomAbs_C1, 14, 10000);
2339
    return TopoDS_Shape(std::move(wire));
2340
}
2341

2342
TopoDS_Shape TopoShape::makeThread(Standard_Real pitch,
2343
                                   Standard_Real depth,
2344
                                   Standard_Real height,
2345
                                   Standard_Real radius) const
2346
{
2347
    if (pitch < Precision::Confusion())
2348
        Standard_Failure::Raise("Pitch of thread too small");
2349

2350
    if (depth < Precision::Confusion())
2351
        Standard_Failure::Raise("Depth of thread too small");
2352

2353
    if (height < Precision::Confusion())
2354
        Standard_Failure::Raise("Height of thread too small");
2355

2356
    if (radius < Precision::Confusion())
2357
        Standard_Failure::Raise("Radius of thread too small");
2358

2359
    //Threading : Create Surfaces
2360
    gp_Ax2 cylAx2(gp_Pnt(0.0,0.0,0.0) , gp::DZ());
2361
    Handle(Geom_CylindricalSurface) aCyl1 = new Geom_CylindricalSurface(cylAx2 , radius);
2362
    Handle(Geom_CylindricalSurface) aCyl2 = new Geom_CylindricalSurface(cylAx2 , radius+depth);
2363

2364
    //Threading : Define 2D Curves
2365
    gp_Pnt2d aPnt(2. * M_PI , height / 2.);
2366
    gp_Dir2d aDir(2. * M_PI , height / 4.);
2367
    gp_Ax2d aAx2d(aPnt , aDir);
2368

2369
    Standard_Real aMajor = 2. * M_PI;
2370
    Standard_Real aMinor = pitch;
2371

2372
    Handle(Geom2d_Ellipse) anEllipse1 = new Geom2d_Ellipse(aAx2d , aMajor , aMinor);
2373
    Handle(Geom2d_Ellipse) anEllipse2 = new Geom2d_Ellipse(aAx2d , aMajor , aMinor / 4);
2374

2375
    Handle(Geom2d_TrimmedCurve) aArc1 = new Geom2d_TrimmedCurve(anEllipse1 , 0 , M_PI);
2376
    Handle(Geom2d_TrimmedCurve) aArc2 = new Geom2d_TrimmedCurve(anEllipse2 , 0 , M_PI);
2377

2378
    gp_Pnt2d anEllipsePnt1 = anEllipse1->Value(0);
2379
    gp_Pnt2d anEllipsePnt2 = anEllipse1->Value(M_PI);
2380

2381
    Handle(Geom2d_TrimmedCurve) aSegment = GCE2d_MakeSegment(anEllipsePnt1 , anEllipsePnt2);
2382

2383
    //Threading : Build Edges and Wires
2384
    TopoDS_Edge aEdge1OnSurf1 = BRepBuilderAPI_MakeEdge(aArc1 , aCyl1);
2385
    TopoDS_Edge aEdge2OnSurf1 = BRepBuilderAPI_MakeEdge(aSegment , aCyl1);
2386
    TopoDS_Edge aEdge1OnSurf2 = BRepBuilderAPI_MakeEdge(aArc2 , aCyl2);
2387
    TopoDS_Edge aEdge2OnSurf2 = BRepBuilderAPI_MakeEdge(aSegment , aCyl2);
2388

2389
    TopoDS_Wire threadingWire1 = BRepBuilderAPI_MakeWire(aEdge1OnSurf1 , aEdge2OnSurf1);
2390
    TopoDS_Wire threadingWire2 = BRepBuilderAPI_MakeWire(aEdge1OnSurf2 , aEdge2OnSurf2);
2391

2392
    BRepLib::BuildCurves3d(threadingWire1);
2393
    BRepLib::BuildCurves3d(threadingWire2);
2394

2395
    BRepOffsetAPI_ThruSections aTool(Standard_True);
2396

2397
    aTool.AddWire(threadingWire1);
2398
    aTool.AddWire(threadingWire2);
2399
    aTool.CheckCompatibility(Standard_False);
2400

2401
    return aTool.Shape();
2402
}
2403

2404
TopoDS_Shape TopoShape::makeLoft(const TopTools_ListOfShape& profiles,
2405
                                 Standard_Boolean isSolid,
2406
                                 Standard_Boolean isRuled,
2407
                                 Standard_Boolean isClosed,
2408
                                 Standard_Integer maxDegree) const
2409
{
2410
    // http://opencascade.blogspot.com/2010/01/surface-modeling-part5.html
2411
    BRepOffsetAPI_ThruSections aGenerator (isSolid,isRuled);
2412
    aGenerator.SetMaxDegree(maxDegree);
2413

2414
    TopTools_ListIteratorOfListOfShape it;
2415
    int countShapes = 0;
2416
    for (it.Initialize(profiles); it.More(); it.Next()) {
2417
        const TopoDS_Shape& item = it.Value();
2418
        if (!item.IsNull() && item.ShapeType() == TopAbs_VERTEX) {
2419
            aGenerator.AddVertex(TopoDS::Vertex (item));
2420
            countShapes++;
2421
        }
2422
        else if (!item.IsNull() && item.ShapeType() == TopAbs_EDGE) {
2423
            BRepBuilderAPI_MakeWire mkWire(TopoDS::Edge(item));
2424
            aGenerator.AddWire(mkWire.Wire());
2425
            countShapes++;
2426
        }
2427
        else if (!item.IsNull() && item.ShapeType() == TopAbs_WIRE) {
2428
            aGenerator.AddWire(TopoDS::Wire (item));
2429
            countShapes++;
2430
        }
2431
    }
2432

2433
    if (countShapes < 2) {
2434
        Standard_Failure::Raise("Need at least two vertices, edges or wires to create loft face");
2435
    }
2436
    else {
2437
        // close loft by duplicating initial profile as last profile.  not perfect.
2438
        if (isClosed) {
2439
        /* can only close loft in certain combinations of Vertex/Wire(Edge):
2440
            - V1-W1-W2-W3-V2  ==> V1-W1-W2-W3-V2-V1  invalid closed
2441
            - V1-W1-W2-W3     ==> V1-W1-W2-W3-V1     valid closed
2442
            - W1-W2-W3-V1     ==> W1-W2-W3-V1-W1     invalid closed
2443
            - W1-W2-W3        ==> W1-W2-W3-W1        valid closed*/
2444
            if (profiles.Last().ShapeType() == TopAbs_VERTEX) {
2445
                Base::Console().Message("TopoShape::makeLoft: can't close Loft with Vertex as last profile. 'Closed' ignored.\n");
2446
            }
2447
            else {
2448
                // repeat Add logic above for first profile
2449
                const TopoDS_Shape& firstProfile = profiles.First();
2450
                if (firstProfile.ShapeType() == TopAbs_VERTEX)  {
2451
                    aGenerator.AddVertex(TopoDS::Vertex (firstProfile));
2452
                    countShapes++;
2453
                }
2454
                else if (firstProfile.ShapeType() == TopAbs_EDGE)  {
2455
                    aGenerator.AddWire(TopoDS::Wire (firstProfile));
2456
                    countShapes++;
2457
                }
2458
                else if (firstProfile.ShapeType() == TopAbs_WIRE)  {
2459
                    aGenerator.AddWire(TopoDS::Wire (firstProfile));
2460
                    countShapes++;
2461
                }
2462
            }
2463
        }
2464
    }
2465

2466
    Standard_Boolean anIsCheck = Standard_True;
2467
    aGenerator.CheckCompatibility (anIsCheck);   // use BRepFill_CompatibleWires on profiles. force #edges, orientation, "origin" to match.
2468
    aGenerator.Build();
2469
    if (!aGenerator.IsDone())
2470
        Standard_Failure::Raise("Failed to create loft face");
2471

2472
    //Base::Console().Message("DEBUG: TopoShape::makeLoft returns.\n");
2473
    return aGenerator.Shape();
2474
}
2475

2476
TopoDS_Shape TopoShape::makePrism(const gp_Vec& vec) const
2477
{
2478
    if (this->_Shape.IsNull()) Standard_Failure::Raise("cannot sweep empty shape");
2479
    BRepPrimAPI_MakePrism mkPrism(this->_Shape, vec);
2480
    return mkPrism.Shape();
2481
}
2482

2483
TopoDS_Shape TopoShape::revolve(const gp_Ax1& axis, double d, Standard_Boolean isSolid) const
2484
{
2485
    if (this->_Shape.IsNull()) Standard_Failure::Raise("cannot revolve empty shape");
2486

2487
    TopoDS_Face f;
2488
    TopoDS_Wire w;
2489
    TopoDS_Edge e;
2490
    Standard_Boolean convertFailed = false;
2491

2492
    TopoDS_Shape base = this->_Shape;
2493
    if ((isSolid) && (BRep_Tool::IsClosed(base)) &&
2494
        ((base.ShapeType() == TopAbs_EDGE) || (base.ShapeType() == TopAbs_WIRE))) {
2495
        if (base.ShapeType() == TopAbs_EDGE) {
2496
            BRepBuilderAPI_MakeWire mkWire(TopoDS::Edge(base));
2497
            if (mkWire.IsDone()) {
2498
                w = mkWire.Wire(); }
2499
            else {
2500
                convertFailed = true; }
2501
        }
2502
        else {
2503
             w = TopoDS::Wire(base);}
2504
        if (!convertFailed) {
2505
            BRepBuilderAPI_MakeFace mkFace(w);
2506
            if (mkFace.IsDone()) {
2507
                f = mkFace.Face();
2508
                base = f; }
2509
            else {
2510
                convertFailed = true; }
2511
        }
2512
    }
2513

2514
    if (convertFailed) {
2515
        Base::Console().Message("TopoShape::revolve could not make Solid from Wire/Edge.\n");}
2516

2517
    BRepPrimAPI_MakeRevol mkRevol(base, axis,d);
2518
    return mkRevol.Shape();
2519
}
2520

2521
TopoDS_Shape TopoShape::makeOffsetShape(double offset, double tol, bool intersection,
2522
                                        bool selfInter, short offsetMode, short join,
2523
                                        bool fill) const
2524
{
2525
    // If the input shape is a compound with a single solid then the offset
2526
    // algorithm creates only a shell instead of a solid which causes errors
2527
    // when using it e.g. for boolean operations. (#0003571)
2528
    // But when extracting the solid and passing it to the algorithm the output
2529
    // shape is a solid.
2530
    TopoDS_Shape inputShape = this->_Shape;
2531
    TopExp_Explorer xp;
2532
    xp.Init(inputShape, TopAbs_VERTEX, TopAbs_SOLID);
2533
    if (!xp.More()) {
2534
        xp.Init(inputShape, TopAbs_SOLID);
2535
        if (xp.More()) {
2536
            // If exactly one solid then get it
2537
            TopoDS_Shape inputSolid = xp.Current();
2538
            xp.Next();
2539
            if (xp.More() == Standard_False)
2540
                inputShape = inputSolid;
2541
        }
2542
    }
2543

2544
    BRepOffsetAPI_MakeOffsetShape mkOffset;
2545
    mkOffset.PerformByJoin(inputShape, offset, tol, BRepOffset_Mode(offsetMode),
2546
                           intersection ? Standard_True : Standard_False,
2547
                           selfInter ? Standard_True : Standard_False,
2548
                           GeomAbs_JoinType(join));
2549

2550
    if (!mkOffset.IsDone())
2551
        Standard_Failure::Raise("BRepOffsetAPI_MakeOffsetShape not done");
2552
    const TopoDS_Shape& res = mkOffset.Shape();
2553
    if (!fill)
2554
        return res;
2555

2556
    //get perimeter wire of original shape.
2557
    //Wires returned seem to have edges in connection order.
2558
    ShapeAnalysis_FreeBoundsProperties freeCheck(this->_Shape);
2559
    freeCheck.Perform();
2560
    if (freeCheck.NbClosedFreeBounds() < 1)
2561
    {
2562
        Standard_Failure::Raise("no closed bounds");
2563
    }
2564

2565
    BRep_Builder builder;
2566
    TopoDS_Compound perimeterCompound;
2567
    builder.MakeCompound(perimeterCompound);
2568
    for (int index = 1; index <= freeCheck.NbClosedFreeBounds(); ++index)
2569
    {
2570
        TopoDS_Wire originalWire = freeCheck.ClosedFreeBound(index)->FreeBound();
2571
        const BRepAlgo_Image& img = mkOffset.MakeOffset().OffsetEdgesFromShapes();
2572

2573
        //build offset wire.
2574
        TopoDS_Wire offsetWire;
2575
        builder.MakeWire(offsetWire);
2576
        TopExp_Explorer xp;
2577
        for (xp.Init(originalWire, TopAbs_EDGE); xp.More(); xp.Next())
2578
        {
2579
            if (!img.HasImage(xp.Current()))
2580
            {
2581
                Standard_Failure::Raise("no image for shape");
2582
            }
2583
            const TopTools_ListOfShape& currentImage = img.Image(xp.Current());
2584
            TopTools_ListIteratorOfListOfShape listIt;
2585
            int edgeCount(0);
2586
            TopoDS_Edge mappedEdge;
2587
            for (listIt.Initialize(currentImage); listIt.More(); listIt.Next())
2588
            {
2589
                if (listIt.Value().ShapeType() != TopAbs_EDGE)
2590
                    continue;
2591
                edgeCount++;
2592
                mappedEdge = TopoDS::Edge(listIt.Value());
2593
            }
2594

2595
            if (edgeCount != 1)
2596
            {
2597
                std::ostringstream stream;
2598
                stream << "wrong edge count: " << edgeCount << std::endl;
2599
                Standard_Failure::Raise(stream.str().c_str());
2600
            }
2601
            builder.Add(offsetWire, mappedEdge);
2602
        }
2603

2604
        //It would be nice if we could get thruSections to build planar faces
2605
        //in all areas possible, so we could run through refine. I tried setting
2606
        //ruled to standard_true, but that didn't have the desired affect.
2607
        BRepOffsetAPI_ThruSections aGenerator;
2608
        aGenerator.AddWire(originalWire);
2609
        aGenerator.AddWire(offsetWire);
2610
        aGenerator.Build();
2611
        if (!aGenerator.IsDone())
2612
        {
2613
            Standard_Failure::Raise("ThruSections failed");
2614
        }
2615

2616
        builder.Add(perimeterCompound, aGenerator.Shape());
2617
    }
2618

2619
    //still had to sew. not using the passed in parameter for sew.
2620
    //Sew has it's own default tolerance. Opinions?
2621
    BRepBuilderAPI_Sewing sewTool;
2622
    sewTool.Add(this->_Shape);
2623
    sewTool.Add(perimeterCompound);
2624
    sewTool.Add(res);
2625
    sewTool.Perform(); //Perform Sewing
2626

2627
    TopoDS_Shape outputShape = sewTool.SewedShape();
2628
    if ((outputShape.ShapeType() == TopAbs_SHELL) && (outputShape.Closed()))
2629
    {
2630
        BRepBuilderAPI_MakeSolid solidMaker(TopoDS::Shell(outputShape));
2631
        if (solidMaker.IsDone())
2632
        {
2633
            TopoDS_Solid temp = solidMaker.Solid();
2634
            //contrary to the occ docs the return value OrientCloseSolid doesn't
2635
            //indicate whether the shell was open or not. It returns true with an
2636
            //open shell and we end up with an invalid solid.
2637
            if (BRepLib::OrientClosedSolid(temp))
2638
                outputShape = temp;
2639
        }
2640
    }
2641

2642
    return outputShape;
2643
}
2644

2645
TopoDS_Shape TopoShape::makeOffset2D(double offset, short joinType, bool fill, bool allowOpenResult, bool intersection) const
2646
{
2647
    if (_Shape.IsNull())
2648
        throw Base::ValueError("makeOffset2D: input shape is null!");
2649

2650
    // OUTLINE OF MAKEOFFSET2D
2651
    // * Prepare shapes to process
2652
    // ** if _Shape is a compound, recursively call this routine for all subcompounds
2653
    // ** if intrsection, dump all non-compound children into shapes to process; otherwise call this routine recursively for all children
2654
    // ** if _shape isn't a compound, dump it straight to shapes to process
2655
    // * Test for shape types, and convert them all to wires
2656
    // * find plane
2657
    // * OCC call (BRepBuilderAPI_MakeOffset)
2658
    // * postprocessing (facemaking):
2659
    // ** convert offset result back to faces, if inputs were faces
2660
    // ** OR do offset filling:
2661
    // *** for closed wires, simply feed source wires + offset wires to smart facemaker
2662
    // *** for open wires, try to connect source anf offset result by creating new edges (incomplete implementation)
2663
    // ** actual call to FaceMakerBullseye, unified for all facemaking.
2664

2665
    std::vector<TopoDS_Shape> shapesToProcess;
2666
    std::vector<TopoDS_Shape> shapesToReturn;
2667
    bool forceOutputCompound = false;
2668

2669
    if (this->_Shape.ShapeType() == TopAbs_COMPOUND) {
2670
        if (!intersection) {
2671
            //simply recursively process the children, independently
2672
            TopoDS_Iterator it(_Shape);
2673
            for( ; it.More() ; it.Next()) {
2674
                shapesToReturn.push_back( TopoShape(it.Value()).makeOffset2D(offset, joinType, fill, allowOpenResult, intersection) );
2675
                forceOutputCompound = true;
2676
            }
2677
        }
2678
        else {
2679
            //collect non-compounds from this compound for collective offset. Process other shapes independently.
2680
            TopoDS_Iterator it(_Shape);
2681
            for( ; it.More() ; it.Next()) {
2682
                if(it.Value().ShapeType() == TopAbs_COMPOUND) {
2683
                    //recursively process subcompounds
2684
                    shapesToReturn.push_back( TopoShape(it.Value()).makeOffset2D(offset, joinType, fill, allowOpenResult, intersection) );
2685
                    forceOutputCompound = true;
2686
                }
2687
                else {
2688
                    shapesToProcess.push_back(it.Value());
2689
                }
2690
            }
2691
        }
2692
    }
2693
    else {
2694
        shapesToProcess.push_back(this->_Shape);
2695
    }
2696

2697
    if(!shapesToProcess.empty()) {
2698

2699
        //although 2d offset supports offsetting a face directly, it seems there is
2700
        //no way to do a collective offset of multiple faces. So, we are doing it
2701
        //by getting all wires from the faces, and applying offsets to them, and
2702
        //reassembling the faces later.
2703
        std::vector<TopoDS_Wire> sourceWires;
2704
        bool haveWires = false;
2705
        bool haveFaces = false;
2706
        for(TopoDS_Shape &sh : shapesToProcess){
2707
            switch (sh.ShapeType()) {
2708
                case TopAbs_EDGE:
2709
                case TopAbs_WIRE:{
2710
                    //convert edge to a wire if necessary...
2711
                    TopoDS_Wire sourceWire;
2712
                    if (sh.ShapeType() == TopAbs_WIRE){
2713
                        sourceWire = TopoDS::Wire(sh);
2714
                    } else { //edge
2715
                        sourceWire = BRepBuilderAPI_MakeWire(TopoDS::Edge(sh)).Wire();
2716
                    }
2717
                    sourceWires.push_back(sourceWire);
2718
                    haveWires = true;
2719
                }break;
2720
                case TopAbs_FACE:{
2721
                    //get all wires of the face
2722
                    TopoDS_Iterator it(sh);
2723
                    for(; it.More(); it.Next()){
2724
                        sourceWires.push_back(TopoDS::Wire(it.Value()));
2725
                    }
2726
                    haveFaces = true;
2727
                }break;
2728
                default:
2729
                    throw Base::TypeError("makeOffset2D: input shape is not an edge, wire or face or compound of those.");
2730
                break;
2731
            }
2732
        }
2733
        if (haveWires && haveFaces)
2734
            throw Base::TypeError("makeOffset2D: collective offset of a mix of wires and faces is not supported");
2735
        if (haveFaces)
2736
            allowOpenResult = false;
2737

2738
        //find plane.
2739
        gp_Pln workingPlane;
2740
        TopoDS_Compound compoundSourceWires;
2741
        {
2742
            BRep_Builder builder;
2743
            builder.MakeCompound(compoundSourceWires);
2744
            for(TopoDS_Wire &w : sourceWires)
2745
                builder.Add(compoundSourceWires, w);
2746
            BRepLib_FindSurface planefinder(compoundSourceWires, -1, Standard_True);
2747
            if (!planefinder.Found())
2748
                throw Base::CADKernelError("makeOffset2D: wires are nonplanar or noncoplanar");
2749
            if (haveFaces){
2750
                //extract plane from first face (useful for preserving the plane of face precisely if dealing with only one face)
2751
                workingPlane = BRepAdaptor_Surface(TopoDS::Face(shapesToProcess[0])).Plane();
2752
            } else {
2753
                workingPlane = GeomAdaptor_Surface(planefinder.Surface()).Plane();
2754
            }
2755
        }
2756

2757
        //do the offset..
2758
        TopoDS_Shape offsetShape;
2759
        BRepOffsetAPI_MakeOffsetFix mkOffset(GeomAbs_JoinType(joinType), allowOpenResult);
2760
        for (TopoDS_Wire &w : sourceWires) {
2761
            mkOffset.AddWire(w);
2762
        }
2763

2764
        if (fabs(offset) > Precision::Confusion()) {
2765
            try {
2766
    #if defined(__GNUC__) && defined (FC_OS_LINUX)
2767
                Base::SignalException se;
2768
    #endif
2769
                mkOffset.Perform(offset);
2770
            }
2771
            catch (Standard_Failure &){
2772
                throw;
2773
            }
2774
            catch (...) {
2775
                throw Base::CADKernelError("BRepOffsetAPI_MakeOffset has crashed! (Unknown exception caught)");
2776
            }
2777
            offsetShape = mkOffset.Shape();
2778

2779
            // Replace OffsetCurve with B-Spline
2780
            if (!offsetShape.IsNull()) {
2781
                offsetShape = mkOffset.Replace(GeomAbs_OffsetCurve, offsetShape);
2782
            }
2783

2784
            if (offsetShape.IsNull())
2785
                throw Base::CADKernelError("makeOffset2D: result of offsetting is null!");
2786

2787
            //Copying shape to fix strange orientation behavior, OCC7.0.0. See bug #2699
2788
            // https://www.freecad.org/tracker/view.php?id=2699
2789
            offsetShape = BRepBuilderAPI_Copy(offsetShape).Shape();
2790
        }
2791
        else {
2792
            offsetShape = sourceWires.size()>1 ? TopoDS_Shape(compoundSourceWires) : sourceWires[0];
2793
        }
2794

2795
        std::list<TopoDS_Wire> offsetWires;
2796
        //interestingly, if wires are removed, empty compounds are returned by MakeOffset (as of OCC 7.0.0)
2797
        //so, we just extract all nesting
2798
        Handle(TopTools_HSequenceOfShape) seq = ShapeExtend_Explorer().SeqFromCompound(offsetShape, Standard_True);
2799
        TopoDS_Iterator it(offsetShape);
2800
        for(int i = 0; i < seq->Length(); ++i){
2801
            offsetWires.push_back(TopoDS::Wire(seq->Value(i+1)));
2802
        }
2803

2804
        if (offsetWires.empty())
2805
            throw Base::CADKernelError("makeOffset2D: offset result has no wires.");
2806

2807
        std::list<TopoDS_Wire> wiresForMakingFaces;
2808
        if (!fill){
2809
            if (haveFaces){
2810
                wiresForMakingFaces = offsetWires;
2811
            }
2812
            else {
2813
                for(TopoDS_Wire &w : offsetWires)
2814
                    shapesToReturn.push_back(w);
2815
            }
2816
        }
2817
        else {
2818
            //fill offset
2819
            if (fabs(offset) < Precision::Confusion())
2820
                throw Base::ValueError("makeOffset2D: offset distance is zero. Can't fill offset.");
2821

2822
            //filling offset. There are three major cases to consider:
2823
            // 1. source wires and result wires are closed (simplest) -> make face
2824
            // from source wire + offset wire
2825
            //
2826
            // 2. source wire is open, but offset wire is closed (if not
2827
            // allowOpenResult). -> throw away source wire and make face right from
2828
            // offset result.
2829
            //
2830
            // 3. source and offset wire are both open (note that there may be
2831
            // closed islands in offset result) -> need connecting offset result to
2832
            // source wire with new edges
2833

2834
            //first, lets split apart closed and open wires.
2835
            std::list<TopoDS_Wire> closedWires;
2836
            std::list<TopoDS_Wire> openWires;
2837
            for(TopoDS_Wire &w : sourceWires)
2838
                if (BRep_Tool::IsClosed(w))
2839
                    closedWires.push_back(w);
2840
                else
2841
                    openWires.push_back(w);
2842
            for(TopoDS_Wire &w : offsetWires)
2843
                if (BRep_Tool::IsClosed(w))
2844
                    closedWires.push_back(w);
2845
                else
2846
                    openWires.push_back(w);
2847

2848
            wiresForMakingFaces = closedWires;
2849
            if (!allowOpenResult || openWires.empty()){
2850
                //just ignore all open wires
2851
            }
2852
            else {
2853
                //We need to connect open wires to form closed wires.
2854

2855
                //for now, only support offsetting one open wire -> there should be exactly two open wires for connecting
2856
                if (openWires.size() != 2)
2857
                    throw Base::CADKernelError("makeOffset2D: collective offset with filling of multiple wires is not supported yet.");
2858

2859
                TopoDS_Wire openWire1 = openWires.front();
2860
                TopoDS_Wire openWire2 = openWires.back();
2861

2862
                //find open vertices
2863
                BRepTools_WireExplorer xp;
2864
                xp.Init(openWire1);
2865
                TopoDS_Vertex v1 = xp.CurrentVertex();
2866
                for(;xp.More();xp.Next()){};
2867
                TopoDS_Vertex v2 = xp.CurrentVertex();
2868

2869
                //find open vertices
2870
                xp.Init(openWire2);
2871
                TopoDS_Vertex v3 = xp.CurrentVertex();
2872
                for(;xp.More();xp.Next()){};
2873
                TopoDS_Vertex v4 = xp.CurrentVertex();
2874

2875
                //check
2876
                if (v1.IsNull())  throw NullShapeException("v1 is null");
2877
                if (v2.IsNull())  throw NullShapeException("v2 is null");
2878
                if (v3.IsNull())  throw NullShapeException("v3 is null");
2879
                if (v4.IsNull())  throw NullShapeException("v4 is null");
2880

2881
                //assemble new wire
2882

2883
                //we want the connection order to be
2884
                //v1 -> openWire1 -> v2 -> (new edge) -> v4 -> openWire2(rev) -> v3 -> (new edge) -> v1
2885
                //let's check if it's the case. If not, we reverse one wire and swap its endpoints.
2886

2887
                if (fabs(gp_Vec(BRep_Tool::Pnt(v2), BRep_Tool::Pnt(v3)).Magnitude() - fabs(offset)) <= BRep_Tool::Tolerance(v2) + BRep_Tool::Tolerance(v3)){
2888
                    openWire2.Reverse();
2889
                    std::swap(v3, v4);
2890
                    v3.Reverse();
2891
                    v4.Reverse();
2892
                }
2893
                else if ((fabs(gp_Vec(BRep_Tool::Pnt(v2), BRep_Tool::Pnt(v4)).Magnitude() - fabs(offset)) <= BRep_Tool::Tolerance(v2) + BRep_Tool::Tolerance(v4))){
2894
                    //orientation is as expected, nothing to do
2895
                }
2896
                else {
2897
                    throw Base::CADKernelError("makeOffset2D: fill offset: failed to establish open vertex relationship.");
2898
                }
2899

2900
                //now directions of open wires are aligned. Finally. make new wire!
2901
                BRepBuilderAPI_MakeWire mkWire;
2902
                //add openWire1
2903
                BRepTools_WireExplorer it;
2904
                for(it.Init(openWire1); it.More(); it.Next()){
2905
                    mkWire.Add(it.Current());
2906
                }
2907
                //add first joining edge
2908
                mkWire.Add(BRepBuilderAPI_MakeEdge(v2,v4).Edge());
2909
                //add openWire2, in reverse order
2910
                openWire2.Reverse();
2911
                for(it.Init(TopoDS::Wire(openWire2)); it.More(); it.Next()){
2912
                    mkWire.Add(it.Current());
2913
                }
2914
                //add final joining edge
2915
                mkWire.Add(BRepBuilderAPI_MakeEdge(v3,v1).Edge());
2916

2917
                mkWire.Build();
2918

2919
                wiresForMakingFaces.push_front(mkWire.Wire());
2920
            }
2921
        }
2922

2923
        //make faces
2924
        if (!wiresForMakingFaces.empty()){
2925
            FaceMakerBullseye mkFace;
2926
            mkFace.setPlane(workingPlane);
2927
            for(TopoDS_Wire &w : wiresForMakingFaces){
2928
                mkFace.addWire(w);
2929
            }
2930
            mkFace.Build();
2931
            if (mkFace.Shape().IsNull())
2932
                throw Base::CADKernelError("makeOffset2D: making face failed (null shape returned).");
2933
            TopoDS_Shape result = mkFace.Shape();
2934
            if (haveFaces && shapesToProcess.size() == 1)
2935
                result.Orientation(shapesToProcess[0].Orientation());
2936

2937
            ShapeExtend_Explorer xp;
2938
            Handle(TopTools_HSequenceOfShape) result_leaves = xp.SeqFromCompound(result, Standard_True);
2939
            for(int i = 0; i < result_leaves->Length(); ++i)
2940
                shapesToReturn.push_back(result_leaves->Value(i+1));
2941
        }
2942
    }
2943

2944
    //assemble output compound
2945
    if (shapesToReturn.empty())
2946
        return {}; //failure
2947
    if (shapesToReturn.size() > 1 || forceOutputCompound){
2948
        TopoDS_Compound result;
2949
        BRep_Builder builder;
2950
        builder.MakeCompound(result);
2951
        for(TopoDS_Shape &sh : shapesToReturn) {
2952
            if (!sh.IsNull()) {
2953
                builder.Add(result, sh);
2954
            }
2955
        }
2956
        return TopoDS_Shape(std::move(result));
2957
    }
2958
    else {
2959
        return shapesToReturn[0];
2960
    }
2961
}
2962

2963
TopoDS_Shape TopoShape::makeThickSolid(const TopTools_ListOfShape& remFace,
2964
                                       double offset, double tol, bool intersection,
2965
                                       bool selfInter, short offsetMode, short join) const
2966
{
2967
    BRepOffsetAPI_MakeThickSolid mkThick;
2968
    mkThick.MakeThickSolidByJoin(this->_Shape, remFace, offset, tol, BRepOffset_Mode(offsetMode),
2969
        intersection ? Standard_True : Standard_False,
2970
        selfInter ? Standard_True : Standard_False,
2971
        GeomAbs_JoinType(join));
2972
    return mkThick.Shape();
2973
}
2974

2975
void TopoShape::transformGeometry(const Base::Matrix4D &rclMat)
2976
{
2977
    try {
2978
        if (this->_Shape.IsNull())
2979
            Standard_Failure::Raise("Cannot transform null shape");
2980

2981
        *this = makeGTransform(rclMat);
2982
    }
2983
    catch (const Standard_Failure& e) {
2984
        throw Base::CADKernelError(e.GetMessageString());
2985
    }
2986
}
2987

2988
TopoDS_Shape TopoShape::transformGShape(const Base::Matrix4D& rclTrf, bool copy) const
2989
{
2990
    if (this->_Shape.IsNull())
2991
        Standard_Failure::Raise("Cannot transform null shape");
2992

2993
    gp_GTrsf mat;
2994
    mat.SetValue(1,1,rclTrf[0][0]);
2995
    mat.SetValue(2,1,rclTrf[1][0]);
2996
    mat.SetValue(3,1,rclTrf[2][0]);
2997
    mat.SetValue(1,2,rclTrf[0][1]);
2998
    mat.SetValue(2,2,rclTrf[1][1]);
2999
    mat.SetValue(3,2,rclTrf[2][1]);
3000
    mat.SetValue(1,3,rclTrf[0][2]);
3001
    mat.SetValue(2,3,rclTrf[1][2]);
3002
    mat.SetValue(3,3,rclTrf[2][2]);
3003
    mat.SetValue(1,4,rclTrf[0][3]);
3004
    mat.SetValue(2,4,rclTrf[1][3]);
3005
    mat.SetValue(3,4,rclTrf[2][3]);
3006
    // this copy step seems to eliminate Part.OCCError: gp_GTrsf::Trsf() - non-orthogonal GTrsf
3007
    BRepBuilderAPI_Copy copier(this->_Shape);
3008
    // geometric transformation
3009
    BRepBuilderAPI_GTransform mkTrf(copier.Shape(), mat, copy);
3010
    return mkTrf.Shape();
3011
}
3012

3013
bool TopoShape::transformShape(const Base::Matrix4D& rclTrf, bool copy, bool checkScale)
3014
{
3015
    if (this->_Shape.IsNull())
3016
        Standard_Failure::Raise("Cannot transform null shape");
3017

3018
    return _makeTransform(TopoShape(*this),rclTrf,nullptr,checkScale,copy);
3019
}
3020

3021
TopoDS_Shape TopoShape::mirror(const gp_Ax2& ax2) const
3022
{
3023
    gp_Trsf mat;
3024
    mat.SetMirror(ax2);
3025
    BRepBuilderAPI_Transform mkTrf(this->_Shape, mat);
3026
    return mkTrf.Shape();
3027
}
3028

3029
TopoDS_Shape TopoShape::toNurbs() const
3030
{
3031
    if (this->_Shape.IsNull())
3032
        Standard_Failure::Raise("Cannot convert null shape to NURBS");
3033

3034
    BRepBuilderAPI_NurbsConvert mkNurbs(this->_Shape);
3035
    return mkNurbs.Shape();
3036
}
3037

3038
TopoDS_Shape TopoShape::replaceShape(const std::vector< std::pair<TopoDS_Shape,TopoDS_Shape> >& s) const
3039
{
3040
    BRepTools_ReShape reshape;
3041
    std::vector< std::pair<TopoDS_Shape,TopoDS_Shape> >::const_iterator it;
3042
    for (it = s.begin(); it != s.end(); ++it)
3043
        reshape.Replace(it->first, it->second);
3044
    return reshape.Apply(this->_Shape, TopAbs_SHAPE);
3045
}
3046

3047
TopoDS_Shape TopoShape::removeShape(const std::vector<TopoDS_Shape>& s) const
3048
{
3049
    BRepTools_ReShape reshape;
3050
    for (const auto & it : s)
3051
        reshape.Remove(it);
3052
    return reshape.Apply(this->_Shape, TopAbs_SHAPE);
3053
}
3054

3055
void TopoShape::sewShape(double tolerance)
3056
{
3057
    BRepBuilderAPI_Sewing sew(tolerance);
3058
    sew.Load(this->_Shape);
3059
    sew.Perform();
3060

3061
    this->_Shape = sew.SewedShape();
3062
}
3063

3064
bool TopoShape::fix()
3065
{
3066
    if (this->_Shape.IsNull()) {
3067
        return false;
3068
    }
3069

3070
    // First, we do fix regardless if the current shape is valid or not,
3071
    // because not all problems that are handled by ShapeFix_Shape can be
3072
    // recognized by BRepCheck_Analyzer.
3073
    //
3074
    // Second, for some reason, a failed fix (i.e. a fix that produces invalid shape)
3075
    // will affect the input shape. (See // https://github.com/realthunder/FreeCAD/issues/585,
3076
    // BTW, the file attached in the issue also shows that ShapeFix_Shape may
3077
    // actually make a valid input shape invalid). So, it actually change the
3078
    // underlying shape data. Therefore, we try with a copy first.
3079
    auto copy = makeElementCopy();
3080
    ShapeFix_Shape fix(copy._Shape);
3081
    fix.Perform();
3082

3083
    if (fix.Shape().IsSame(copy._Shape)) {
3084
        return false;
3085
    }
3086

3087
    BRepCheck_Analyzer aChecker(fix.Shape());
3088
    if (!aChecker.IsValid()) {
3089
        return false;
3090
    }
3091

3092
    // If the above fix produces a valid shape, then we fix the original shape,
3093
    // because BRepBuilderAPI_Copy has some undesired side effect (e.g. flatten
3094
    // underlying shape, and thus break internal shape sharing).
3095
    ShapeFix_Shape fixThis(this->_Shape);
3096
    fixThis.Perform();
3097

3098
    aChecker.Init(fixThis.Shape());
3099
    if (aChecker.IsValid()) {
3100
        // Must call makESHAPE() (which calls mapSubElement()) to remap element
3101
        // names because ShapeFix_Shape may delete (e.g. small edges) or modify
3102
        // the input shape.
3103
        //
3104
        // See https://github.com/realthunder/FreeCAD/issues/595. Sketch001
3105
        // has small edges. Simply recompute the sketch to trigger call of fix()
3106
        // through makEWires(), and it will remove those edges. Without
3107
        // remapping, there will be invalid index jumpping in reference in
3108
        // Sketch002.ExternalEdge5.
3109
        makeShapeWithElementMap(fixThis.Shape(), MapperHistory(fixThis), {*this});
3110
    }
3111
    else {
3112
        makeShapeWithElementMap(fix.Shape(), MapperHistory(fix), {copy});
3113
    }
3114
    return true;
3115
}
3116

3117
bool TopoShape::fix(double precision, double mintol, double maxtol)
3118
{
3119
    if (this->_Shape.IsNull())
3120
        return false;
3121

3122
    TopAbs_ShapeEnum type = this->_Shape.ShapeType();
3123

3124
    ShapeFix_Shape fix(this->_Shape);
3125
    fix.SetPrecision(precision);
3126
    fix.SetMinTolerance(mintol);
3127
    fix.SetMaxTolerance(maxtol);
3128

3129
    fix.Perform();
3130

3131
    if (type == TopAbs_SOLID) {
3132
        //fix.FixEdgeTool();
3133
        fix.FixWireTool()->Perform();
3134
        fix.FixFaceTool()->Perform();
3135
        fix.FixShellTool()->Perform();
3136
        fix.FixSolidTool()->Perform();
3137
        this->_Shape = fix.FixSolidTool()->Shape();
3138
    }
3139
    else if (type == TopAbs_SHELL) {
3140
        fix.FixWireTool()->Perform();
3141
        fix.FixFaceTool()->Perform();
3142
        fix.FixShellTool()->Perform();
3143
        this->_Shape = fix.FixShellTool()->Shape();
3144
    }
3145
    else if (type == TopAbs_FACE) {
3146
        fix.FixWireTool()->Perform();
3147
        fix.FixFaceTool()->Perform();
3148
        this->_Shape = fix.Shape();
3149
    }
3150
    else if (type == TopAbs_WIRE) {
3151
        fix.FixWireTool()->Perform();
3152
        this->_Shape = fix.Shape();
3153
    }
3154
    else {
3155
        this->_Shape = fix.Shape();
3156
    }
3157

3158
    return isValid();
3159
}
3160

3161
bool TopoShape::removeInternalWires(double minArea)
3162
{
3163
    ShapeUpgrade_RemoveInternalWires fix(this->_Shape);
3164
    fix.MinArea() = minArea;
3165
    bool ok = fix.Perform() ? true : false;
3166
    this->_Shape = fix.GetResult();
3167
    return ok;
3168
}
3169

3170
TopoDS_Shape TopoShape::removeSplitter() const
3171
{
3172
    if (_Shape.IsNull())
3173
        Standard_Failure::Raise("Cannot remove splitter from empty shape");
3174

3175
    if (_Shape.ShapeType() == TopAbs_SOLID) {
3176
        const TopoDS_Solid &solid = TopoDS::Solid(_Shape);
3177
        BRepBuilderAPI_MakeSolid mkSolid;
3178
        TopExp_Explorer it;
3179
        for (it.Init(solid, TopAbs_SHELL); it.More(); it.Next()) {
3180
            const TopoDS_Shell &currentShell = TopoDS::Shell(it.Current());
3181
            ModelRefine::FaceUniter uniter(currentShell);
3182
            if (uniter.process()) {
3183
                if (uniter.isModified()) {
3184
                    const TopoDS_Shell &newShell = uniter.getShell();
3185
                    mkSolid.Add(newShell);
3186
                }
3187
                else {
3188
                    mkSolid.Add(currentShell);
3189
                }
3190
            }
3191
            else {
3192
                Standard_Failure::Raise("Removing splitter failed");
3193
                return _Shape;
3194
            }
3195
        }
3196
        return mkSolid.Solid();
3197
    }
3198
    else if (_Shape.ShapeType() == TopAbs_SHELL) {
3199
        const TopoDS_Shell& shell = TopoDS::Shell(_Shape);
3200
        ModelRefine::FaceUniter uniter(shell);
3201
        if (uniter.process()) {
3202
            return uniter.getShell();
3203
        }
3204
        else {
3205
            Standard_Failure::Raise("Removing splitter failed");
3206
        }
3207
    }
3208
    else if (_Shape.ShapeType() == TopAbs_COMPOUND) {
3209
        BRep_Builder builder;
3210
        TopoDS_Compound comp;
3211
        builder.MakeCompound(comp);
3212

3213
        TopExp_Explorer xp;
3214
        // solids
3215
        for (xp.Init(_Shape, TopAbs_SOLID); xp.More(); xp.Next()) {
3216
            const TopoDS_Solid &solid = TopoDS::Solid(xp.Current());
3217
            BRepTools_ReShape reshape;
3218
            TopExp_Explorer it;
3219
            for (it.Init(solid, TopAbs_SHELL); it.More(); it.Next()) {
3220
                const TopoDS_Shell &currentShell = TopoDS::Shell(it.Current());
3221
                ModelRefine::FaceUniter uniter(currentShell);
3222
                if (uniter.process()) {
3223
                    if (uniter.isModified()) {
3224
                        const TopoDS_Shell &newShell = uniter.getShell();
3225
                        reshape.Replace(currentShell, newShell);
3226
                    }
3227
                }
3228
            }
3229
            builder.Add(comp, reshape.Apply(solid));
3230
        }
3231
        // free shells
3232
        for (xp.Init(_Shape, TopAbs_SHELL, TopAbs_SOLID); xp.More(); xp.Next()) {
3233
            const TopoDS_Shell& shell = TopoDS::Shell(xp.Current());
3234
            ModelRefine::FaceUniter uniter(shell);
3235
            if (uniter.process()) {
3236
                builder.Add(comp, uniter.getShell());
3237
            }
3238
        }
3239
        // the rest
3240
        for (xp.Init(_Shape, TopAbs_FACE, TopAbs_SHELL); xp.More(); xp.Next()) {
3241
            if (!xp.Current().IsNull())
3242
                builder.Add(comp, xp.Current());
3243
        }
3244
        for (xp.Init(_Shape, TopAbs_WIRE, TopAbs_FACE); xp.More(); xp.Next()) {
3245
            if (!xp.Current().IsNull())
3246
                builder.Add(comp, xp.Current());
3247
        }
3248
        for (xp.Init(_Shape, TopAbs_EDGE, TopAbs_WIRE); xp.More(); xp.Next()) {
3249
            if (!xp.Current().IsNull())
3250
                builder.Add(comp, xp.Current());
3251
        }
3252
        for (xp.Init(_Shape, TopAbs_VERTEX, TopAbs_EDGE); xp.More(); xp.Next()) {
3253
            if (!xp.Current().IsNull())
3254
                builder.Add(comp, xp.Current());
3255
        }
3256

3257
        return TopoDS_Shape(std::move(comp));
3258
    }
3259

3260
    return _Shape;
3261
}
3262

3263
void TopoShape::getDomains(std::vector<Domain>& domains) const
3264
{
3265
    std::size_t countFaces = 0;
3266
    for (TopExp_Explorer xp(this->_Shape, TopAbs_FACE); xp.More(); xp.Next()) {
3267
        ++countFaces;
3268
    }
3269
    domains.reserve(countFaces);
3270

3271
    for (TopExp_Explorer xp(this->_Shape, TopAbs_FACE); xp.More(); xp.Next()) {
3272
        TopoDS_Face face = TopoDS::Face(xp.Current());
3273

3274
        std::vector<gp_Pnt> points;
3275
        std::vector<Poly_Triangle> facets;
3276
        if (!Tools::getTriangulation(face, points, facets)) {
3277
            // For a face that cannot be meshed append an empty domain.
3278
            // It's important for some algorithms (e.g. color mapping) that the numbers of
3279
            // faces and domains match
3280
            Domain domain;
3281
            domains.push_back(domain);
3282
        }
3283
        else {
3284
            Domain domain;
3285
            // copy the points
3286
            domain.points.reserve(points.size());
3287
            for (const auto& it : points) {
3288
                Standard_Real X, Y, Z;
3289
                it.Coord (X, Y, Z);
3290
                domain.points.emplace_back(X, Y, Z);
3291
            }
3292

3293
            // copy the triangles
3294
            domain.facets.reserve(facets.size());
3295
            for (const auto& it : facets) {
3296
                Standard_Integer N1, N2, N3;
3297
                it.Get(N1, N2, N3);
3298

3299
                Facet tria;
3300
                tria.I1 = N1;
3301
                tria.I2 = N2;
3302
                tria.I3 = N3;
3303
                domain.facets.push_back(tria);
3304
            }
3305

3306
            domains.push_back(domain);
3307
        }
3308
    }
3309
}
3310

3311
void TopoShape::getFacesFromDomains(const std::vector<Domain>& domains,
3312
                                    std::vector<Base::Vector3d>& points,
3313
                                    std::vector<Facet>& faces) const
3314
{
3315
    BRepMesh mesh;
3316
    mesh.getFacesFromDomains(domains, points, faces);
3317
}
3318

3319
double TopoShape::getAccuracy() const
3320
{
3321
    double deviation = 0.2;
3322
    Base::BoundBox3d bbox = getBoundBox();
3323
    if (bbox.IsValid())
3324
        return ((bbox.LengthX() + bbox.LengthY() + bbox.LengthZ())/300.0 * deviation);
3325
    return ComplexGeoData::getAccuracy();
3326
}
3327

3328
void TopoShape::getFaces(std::vector<Base::Vector3d> &aPoints,
3329
                         std::vector<Facet> &aTopo,
3330
                         double accuracy, uint16_t /*flags*/) const
3331
{
3332
    if (this->_Shape.IsNull())
3333
        return;
3334

3335
    // get the meshes of all faces and then merge them
3336
    BRepMesh_IncrementalMesh aMesh(this->_Shape, accuracy,
3337
                                   /*isRelative*/ Standard_False,
3338
                                   /*theAngDeflection*/
3339
                                   defaultAngularDeflection(accuracy),
3340
                                   /*isInParallel*/ true);
3341
    std::vector<Domain> domains;
3342
    getDomains(domains);
3343
    getFacesFromDomains(domains, aPoints, aTopo);
3344
}
3345

3346
void TopoShape::setFaces(const std::vector<Base::Vector3d> &Points,
3347
                         const std::vector<Facet> &Topo, double tolerance)
3348
{
3349
    gp_XYZ p1, p2, p3;
3350
    std::vector<TopoDS_Vertex> Vertexes;
3351
    std::map<std::pair<uint32_t, uint32_t>, TopoDS_Edge> Edges;
3352
    TopoDS_Face newFace;
3353
    TopoDS_Wire newWire;
3354
    Standard_Real x1, y1, z1;
3355
    Standard_Real x2, y2, z2;
3356
    Standard_Real x3, y3, z3;
3357

3358
    TopoDS_Compound aComp;
3359
    BRep_Builder BuildTool;
3360
    BuildTool.MakeCompound(aComp);
3361

3362
    uint32_t ctPoints = Points.size();
3363
    Vertexes.resize(ctPoints);
3364

3365
    // Create array of vertexes
3366
    auto CreateVertex = [](const Base::Vector3d& v) {
3367
        gp_XYZ p(v.x, v.y, v.z);
3368
        return BRepBuilderAPI_MakeVertex(p);
3369
    };
3370
    for (const auto& it : Topo) {
3371
        if (it.I1 < ctPoints) {
3372
            if (Vertexes[it.I1].IsNull())
3373
                Vertexes[it.I1] = CreateVertex(Points[it.I1]);
3374
        }
3375
        if (it.I2 < ctPoints) {
3376
            if (Vertexes[it.I2].IsNull())
3377
                Vertexes[it.I2] = CreateVertex(Points[it.I2]);
3378
        }
3379
        if (it.I3 < ctPoints) {
3380
            if (Vertexes[it.I3].IsNull())
3381
                Vertexes[it.I3] = CreateVertex(Points[it.I3]);
3382
        }
3383
    }
3384

3385
    // Create map of edges
3386
    auto CreateEdge = [&Vertexes, &Edges](uint32_t p1, uint32_t p2) {
3387
        // First check if the edge of a neighbour facet already exists
3388
        // The point indices must be flipped.
3389
        auto key1 = std::make_pair(p2, p1);
3390
        auto key2 = std::make_pair(p1, p2);
3391
        auto it = Edges.find(key1);
3392
        if (it != Edges.end()) {
3393
            TopoDS_Edge edge = it->second;
3394
            edge.Reverse();
3395
            Edges[key2] = edge;
3396
        }
3397
        else {
3398
            BRepBuilderAPI_MakeEdge mkEdge(Vertexes[p1], Vertexes[p2]);
3399
            if (mkEdge.IsDone())
3400
                Edges[key2] = mkEdge.Edge();
3401
        }
3402
    };
3403
    auto GetEdge = [&Edges](uint32_t p1, uint32_t p2) {
3404
        auto key = std::make_pair(p1, p2);
3405
        return Edges[key];
3406
    };
3407
    for (const auto& it : Topo) {
3408
        CreateEdge(it.I1, it.I2);
3409
        CreateEdge(it.I2, it.I3);
3410
        CreateEdge(it.I3, it.I1);
3411
    }
3412

3413
    for (const auto& it : Topo) {
3414
        if (it.I1 >= ctPoints || it.I2 >= ctPoints || it.I3 >= ctPoints)
3415
            continue;
3416
        x1 = Points[it.I1].x; y1 = Points[it.I1].y; z1 = Points[it.I1].z;
3417
        x2 = Points[it.I2].x; y2 = Points[it.I2].y; z2 = Points[it.I2].z;
3418
        x3 = Points[it.I3].x; y3 = Points[it.I3].y; z3 = Points[it.I3].z;
3419

3420
        p1.SetCoord(x1,y1,z1);
3421
        p2.SetCoord(x2,y2,z2);
3422
        p3.SetCoord(x3,y3,z3);
3423

3424
        // Avoid very tiny edges as this may result into broken faces. The tolerance is Approximation
3425
        // because Confusion might be too tight.
3426
        if ((!(p1.IsEqual(p2, Precision::Approximation()))) && (!(p1.IsEqual(p3, Precision::Approximation())))) {
3427
            const TopoDS_Edge& e1 = GetEdge(it.I1, it.I2);
3428
            const TopoDS_Edge& e2 = GetEdge(it.I2, it.I3);
3429
            const TopoDS_Edge& e3 = GetEdge(it.I3, it.I1);
3430
            if (e1.IsNull() || e2.IsNull() || e3.IsNull())
3431
                continue;
3432

3433
            newWire = BRepBuilderAPI_MakeWire(e1, e2, e3);
3434
            if (!newWire.IsNull()) {
3435
                newFace = BRepBuilderAPI_MakeFace(newWire);
3436
                if (!newFace.IsNull())
3437
                    BuildTool.Add(aComp, newFace);
3438
            }
3439
        }
3440
    }
3441

3442
    // If performSewing is true BRepBuilderAPI_Sewing creates a compound of
3443
    // shells. Since the resulting shape isn't very usable in most use cases
3444
    // it's fine to set it to false so the algorithm only performs some control
3445
    // checks and creates a compound of faces.
3446
    // However, the computing time can be reduced by 90%.
3447
    // If a shell is needed then the sewShape() function should be called explicitly.
3448
    BRepBuilderAPI_Sewing aSewingTool;
3449
    Standard_Boolean performSewing = Standard_False;
3450
    aSewingTool.Init(tolerance, performSewing);
3451
    aSewingTool.Load(aComp);
3452

3453
#if OCC_VERSION_HEX < 0x070500
3454
    Handle(Message_ProgressIndicator) pi = new ProgressIndicator(100);
3455
    pi->NewScope(100, "Create shape from mesh...");
3456
    pi->Show();
3457

3458
    aSewingTool.Perform(pi);
3459
#else
3460
    aSewingTool.Perform();
3461
#endif
3462

3463
    _Shape = aSewingTool.SewedShape();
3464
#if OCC_VERSION_HEX < 0x070500
3465
    pi->EndScope();
3466
#endif
3467
    if (_Shape.IsNull())
3468
        _Shape = aComp;
3469
}
3470

3471
void TopoShape::getLinesFromSubShape(const TopoDS_Shape& shape,
3472
                                     std::vector<Base::Vector3d> &vertices,
3473
                                     std::vector<Line> &lines) const
3474
{
3475
    if (shape.IsNull())
3476
        return;
3477

3478
    // build up map edge->face
3479
    TopTools_IndexedDataMapOfShapeListOfShape edge2Face;
3480
    TopExp::MapShapesAndAncestors(this->_Shape, TopAbs_EDGE, TopAbs_FACE, edge2Face);
3481

3482
    for (TopExp_Explorer exp(shape, TopAbs_EDGE); exp.More(); exp.Next()) {
3483
        TopoDS_Edge aEdge = TopoDS::Edge(exp.Current());
3484
        std::vector<gp_Pnt> points;
3485

3486
        if (!Tools::getPolygon3D(aEdge, points)) {
3487
            // the edge has not its own triangulation, but then a face the edge is attached to
3488
            // must provide this triangulation
3489

3490
            // Look for one face in our map (it doesn't care which one we take)
3491
            int index = edge2Face.FindIndex(aEdge);
3492
            if (index < 1)
3493
                continue;
3494

3495
            const auto &faces = edge2Face.FindFromIndex(index);
3496
            if (faces.IsEmpty())
3497
                continue;
3498

3499
            const TopoDS_Face& aFace = TopoDS::Face(faces.First());
3500
            if (!Part::Tools::getPolygonOnTriangulation(aEdge, aFace, points))
3501
                continue;
3502
        }
3503

3504
        auto line_start = vertices.size();
3505
        vertices.reserve(vertices.size() + points.size());
3506
        std::for_each(points.begin(), points.end(), [&vertices](const gp_Pnt& p) {
3507
            vertices.push_back(Base::convertTo<Base::Vector3d>(p));
3508
        });
3509

3510
        if (line_start+1 < vertices.size()) {
3511
            lines.emplace_back();
3512
            lines.back().I1 = line_start;
3513
            lines.back().I2 = vertices.size()-1;
3514
        }
3515
    }
3516
}
3517

3518
void TopoShape::getLines(std::vector<Base::Vector3d> &vertices,
3519
                         std::vector<TopoShape::Line> &lines,
3520
                         double /*Accuracy*/, uint16_t /*flags*/) const
3521
{
3522
    getLinesFromSubShape(_Shape, vertices, lines);
3523
}
3524

3525
void TopoShape::getPoints(std::vector<Base::Vector3d> &Points,
3526
                          std::vector<Base::Vector3d> &Normals,
3527
                          double Accuracy, uint16_t /*flags*/) const
3528
{
3529
    if (_Shape.IsNull())
3530
        return;
3531

3532
    const int minPointsPerEdge = 30;
3533
    const double lateralDistance = Accuracy;
3534

3535
    // get all 3d points from free vertices
3536
    for (TopExp_Explorer xp(_Shape, TopAbs_VERTEX, TopAbs_EDGE); xp.More(); xp.Next()) {
3537
        gp_Pnt p = BRep_Tool::Pnt(TopoDS::Vertex(xp.Current()));
3538
        Points.push_back(Base::convertTo<Base::Vector3d>(p));
3539
        Normals.emplace_back(0,0,0);
3540
    }
3541

3542
    // sample inner points of all free edges
3543
    for (TopExp_Explorer xp(_Shape, TopAbs_EDGE, TopAbs_FACE); xp.More(); xp.Next()) {
3544
        BRepAdaptor_Curve curve(TopoDS::Edge(xp.Current()));
3545
        GCPnts_UniformAbscissa discretizer(curve, lateralDistance, curve.FirstParameter(), curve.LastParameter());
3546
        if (discretizer.IsDone () && discretizer.NbPoints () > 0) {
3547
            int nbPoints = discretizer.NbPoints();
3548
            for (int i=1; i<=nbPoints; i++) {
3549
                gp_Pnt p = curve.Value (discretizer.Parameter(i));
3550
                Points.push_back(Base::convertTo<Base::Vector3d>(p));
3551
                Normals.emplace_back(0,0,0);
3552
            }
3553
        }
3554
    }
3555

3556
    // sample inner points of all faces
3557
    BRepClass_FaceClassifier classifier;
3558
    bool hasFaces = false;
3559
    for (TopExp_Explorer xp(_Shape, TopAbs_FACE); xp.More(); xp.Next()) {
3560
        hasFaces = true;
3561
        int pointsPerEdge = minPointsPerEdge;
3562
        TopoDS_Face face = TopoDS::Face(xp.Current());
3563
        BRepAdaptor_Surface surface(face);
3564
        Handle(Geom_Surface) aSurf = BRep_Tool::Surface(face);
3565

3566
        // parameter ranges
3567
        Standard_Real uFirst = surface.FirstUParameter();
3568
        Standard_Real uLast = surface.LastUParameter();
3569
        Standard_Real uMid = (uFirst+uLast)/2;
3570
        Standard_Real vFirst = surface.FirstVParameter();
3571
        Standard_Real vLast = surface.LastVParameter();
3572
        Standard_Real vMid = (vFirst+vLast)/2;
3573

3574
        // get geometrical length and width of the surface
3575
        //
3576
        gp_Pnt p1, p2;
3577
        Standard_Real fLengthU = 0.0, fLengthV = 0.0;
3578
        for (int i = 1; i <= pointsPerEdge; i++) {
3579
            double u1 = static_cast<double>(i-1)/static_cast<double>(pointsPerEdge);
3580
            double s1 = (1.0-u1)*uFirst + u1*uLast;
3581
            p1 = surface.Value(s1,vMid);
3582

3583
            double u2 = static_cast<double>(i)/static_cast<double>(pointsPerEdge);
3584
            double s2 = (1.0-u2)*uFirst + u2*uLast;
3585
            p2 = surface.Value(s2,vMid);
3586

3587
            fLengthU += p1.Distance(p2);
3588
        }
3589

3590
        for (int i = 1; i <= pointsPerEdge; i++) {
3591
            double v1 = static_cast<double>(i-1)/static_cast<double>(pointsPerEdge);
3592
            double t1 = (1.0-v1)*vFirst + v1*vLast;
3593
            p1 = surface.Value(uMid,t1);
3594

3595
            double v2 = static_cast<double>(i)/static_cast<double>(pointsPerEdge);
3596
            double t2 = (1.0-v2)*vFirst + v2*vLast;
3597
            p2 = surface.Value(uMid,t2);
3598

3599
            fLengthV += p1.Distance(p2);
3600
        }
3601

3602
        int uPointsPerEdge = static_cast<int>(fLengthU / lateralDistance);
3603
        int vPointsPerEdge = static_cast<int>(fLengthV / lateralDistance);
3604
        uPointsPerEdge = std::max(uPointsPerEdge, 1);
3605
        vPointsPerEdge = std::max(vPointsPerEdge, 1);
3606

3607
        for (int i = 0; i <= uPointsPerEdge; i++) {
3608
            double u = static_cast<double>(i)/static_cast<double>(uPointsPerEdge);
3609
            double s = (1.0-u)*uFirst + u*uLast;
3610

3611
            for (int j = 0; j <= vPointsPerEdge; j++) {
3612
                double v = static_cast<double>(j)/static_cast<double>(vPointsPerEdge);
3613
                double t = (1.0-v)*vFirst + v*vLast;
3614

3615
                gp_Pnt2d p2d(s,t);
3616
                classifier.Perform(face,p2d,1.0e-4);
3617
                if (classifier.State() == TopAbs_IN || classifier.State() == TopAbs_ON) {
3618
                    gp_Pnt p = surface.Value(s,t);
3619
                    Points.push_back(Base::convertTo<Base::Vector3d>(p));
3620
                    gp_Dir normal;
3621
                    if (GeomLib::NormEstim(aSurf, p2d, Precision::Confusion(), normal) <= 1) {
3622
                        if (face.Orientation() == TopAbs_REVERSED)
3623
                            normal.Reverse();
3624
                        Normals.push_back(Base::convertTo<Base::Vector3d>(normal));
3625
                    }
3626
                    else {
3627
                        Normals.emplace_back(0,0,0);
3628
                    }
3629
                }
3630
            }
3631
        }
3632
    }
3633

3634
    // if no faces are found then the normals can be cleared
3635
    if (!hasFaces)
3636
        Normals.clear();
3637
}
3638

3639
void TopoShape::getLinesFromSubElement(const Data::Segment* element,
3640
                                       std::vector<Base::Vector3d> &vertices,
3641
                                       std::vector<Line> &lines) const
3642
{
3643
    if (element->is<ShapeSegment>()) {
3644
        const TopoDS_Shape& shape = static_cast<const ShapeSegment*>(element)->Shape;
3645
        if (shape.IsNull())
3646
            return;
3647

3648
        getLinesFromSubShape(shape, vertices, lines);
3649
    }
3650
}
3651

3652
void TopoShape::getFacesFromSubElement(const Data::Segment* element,
3653
                                       std::vector<Base::Vector3d> &points,
3654
                                       std::vector<Base::Vector3d> &pointNormals,
3655
                                       std::vector<Facet> &faces) const
3656
{
3657
    if (element->is<ShapeSegment>()) {
3658
        const TopoDS_Shape& shape = static_cast<const ShapeSegment*>(element)->Shape;
3659
        if (shape.IsNull() || shape.ShapeType() != TopAbs_FACE)
3660
            return;
3661

3662
        // get the meshes of all faces and then merge them
3663
        std::vector<Domain> domains;
3664
        TopoShape(shape).getDomains(domains);
3665
        getFacesFromDomains(domains, points, faces);
3666

3667
        (void)pointNormals; // leave this empty
3668
    }
3669
}
3670

3671
TopoDS_Shape TopoShape::defeaturing(const std::vector<TopoDS_Shape>& s) const
3672
{
3673
    if (this->_Shape.IsNull())
3674
        Standard_Failure::Raise("Base shape is null");
3675
    BRepAlgoAPI_Defeaturing defeat;
3676
    defeat.SetRunParallel(true);
3677
    defeat.SetShape(this->_Shape);
3678
    for (const auto & it : s)
3679
        defeat.AddFaceToRemove(it);
3680
    defeat.Build();
3681
    if (!defeat.IsDone()) {
3682
        // error treatment
3683
        Standard_SStream aSStream;
3684
        defeat.DumpErrors(aSStream);
3685
        const std::string& resultstr = aSStream.str();
3686
        const char* cstr2 = resultstr.c_str();
3687
        throw Base::RuntimeError(cstr2);
3688
    }
3689
    return defeat.Shape();
3690
}
3691

3692
/**
3693
 * @brief TopoShape::makeShell
3694
 * If the input shape is a compound with faces not being part of a shell
3695
 * it tries to make a shell.
3696
 * If this operation fails or if the input shape is not a compound or a compound
3697
 * with not only faces the input shape is returned.
3698
 * @return Shell or passed shape
3699
 */
3700
TopoDS_Shape TopoShape::makeShell(const TopoDS_Shape& input) const
3701
{
3702
    // For comparison see also:
3703
    // GEOMImpl_BooleanDriver::makeCompoundShellFromFaces
3704
    if (input.IsNull())
3705
        return input;
3706
    if (input.ShapeType() != TopAbs_COMPOUND)
3707
        return input;
3708

3709
    // we need a compound that consists of only faces
3710
    TopExp_Explorer it;
3711
    // no shells
3712
    it.Init(input, TopAbs_SHELL);
3713
    if (it.More())
3714
        return input;
3715

3716
    // no wires outside a face
3717
    it.Init(input, TopAbs_WIRE, TopAbs_FACE);
3718
    if (it.More())
3719
        return input;
3720

3721
    // no edges outside a wire
3722
    it.Init(input, TopAbs_EDGE, TopAbs_WIRE);
3723
    if (it.More())
3724
        return input;
3725

3726
    // no vertexes outside an edge
3727
    it.Init(input, TopAbs_VERTEX, TopAbs_EDGE);
3728
    if (it.More())
3729
        return input;
3730

3731
    BRep_Builder builder;
3732
    TopoDS_Shape shape;
3733
    TopoDS_Shell shell;
3734
    builder.MakeShell(shell);
3735

3736
    try {
3737
        for (it.Init(input, TopAbs_FACE); it.More(); it.Next()) {
3738
            if (!it.Current().IsNull())
3739
                builder.Add(shell, it.Current());
3740
        }
3741

3742
        shape = shell;
3743
        BRepCheck_Analyzer check(shell);
3744
        if (!check.IsValid()) {
3745
            ShapeUpgrade_ShellSewing sewShell;
3746
            shape = sewShell.ApplySewing(shell);
3747
        }
3748

3749
        if (shape.IsNull())
3750
            return input;
3751

3752
        if (shape.ShapeType() != TopAbs_SHELL)
3753
            return input;
3754

3755
        return shape; // success
3756
    }
3757
    catch (Standard_Failure&) {
3758
        return input;
3759
    }
3760
}
3761

3762
#define _HANDLE_NULL_SHAPE(_msg,_throw) do {\
3763
    if(_throw) {\
3764
        FC_THROWM(NullShapeException,_msg);\
3765
    }\
3766
    FC_WARN(_msg);\
3767
}while(0)
3768

3769
#define HANDLE_NULL_SHAPE _HANDLE_NULL_SHAPE("Null shape",true)
3770
#define HANDLE_NULL_INPUT _HANDLE_NULL_SHAPE("Null input shape",true)
3771
#define WARN_NULL_INPUT _HANDLE_NULL_SHAPE("Null input shape",false)
3772

3773
TopoShape &TopoShape::makeWires(const TopoShape &shape, const char *op, bool fix, double tol)
3774
{
3775
    _Shape.Nullify();
3776

3777
    if(shape.isNull())
3778
        HANDLE_NULL_INPUT;
3779

3780
    if(tol<Precision::Confusion()) tol = Precision::Confusion();
3781

3782
    (void)op;
3783
    (void)fix;
3784
    std::vector<TopoShape> edges;
3785
    std::list<TopoShape> edge_list;
3786
    std::vector<TopoShape> wires;
3787

3788
    TopTools_IndexedMapOfShape anIndices;
3789
    TopExp::MapShapes(shape.getShape(), TopAbs_EDGE, anIndices);
3790
    for(int i=1;i<=anIndices.Extent();++i)
3791
        edge_list.emplace_back(anIndices.FindKey(i));
3792

3793
    edges.reserve(edge_list.size());
3794
    wires.reserve(edge_list.size());
3795

3796
    // sort them together to wires
3797
    while (!edge_list.empty()) {
3798
        BRepBuilderAPI_MakeWire mkWire;
3799
        // add and erase first edge
3800
        edges.push_back(edge_list.front());
3801
        edge_list.pop_front();
3802
        mkWire.Add(TopoDS::Edge(edges.back().getShape()));
3803
        edges.back().setShape(mkWire.Edge());
3804

3805
        TopoDS_Wire new_wire = mkWire.Wire(); // current new wire
3806

3807
        // try to connect each edge to the wire, the wire is complete if no more edges are connectible
3808
        bool found = false;
3809
        do {
3810
            found = false;
3811
            for (auto it=edge_list.begin();it!=edge_list.end();++it) {
3812
                mkWire.Add(TopoDS::Edge(it->getShape()));
3813
                if (mkWire.Error() != BRepBuilderAPI_DisconnectedWire) {
3814
                    // edge added ==> remove it from list
3815
                    found = true;
3816
                    edges.push_back(*it);
3817
                    edges.back().setShape(mkWire.Edge());
3818
                    edge_list.erase(it);
3819
                    new_wire = mkWire.Wire();
3820
                    break;
3821
                }
3822
            }
3823
        } while (found);
3824

3825
        // Fix any topological issues of the wire
3826
        ShapeFix_Wire aFix;
3827
        aFix.SetPrecision(tol);
3828
        aFix.Load(new_wire);
3829

3830
        aFix.FixReorder();
3831
        // Assuming FixReorder() just reorder and don't change the underlying
3832
        // edges, we get the wire and do a name mapping now, as the following
3833
        // two operations (FixConnected and FixClosed) may change the edges.
3834
        wires.emplace_back(aFix.Wire());
3835

3836
        aFix.FixConnected();
3837
        aFix.FixClosed();
3838
        // Now retrieve the shape and set it without touching element map
3839
        wires.back().setShape(aFix.Wire());
3840
    }
3841
    return makeCompound(wires,nullptr,false);
3842
}
3843

3844
TopoShape &TopoShape::makeCompound(const std::vector<TopoShape> &shapes, const char *op, bool force)
3845
{
3846
    (void)op;
3847
    _Shape.Nullify();
3848

3849
    if(shapes.empty())
3850
        HANDLE_NULL_INPUT;
3851

3852
    if(!force && shapes.size()==1) {
3853
        *this = shapes[0];
3854
        return *this;
3855
    }
3856

3857
    BRep_Builder builder;
3858
    TopoDS_Compound comp;
3859
    builder.MakeCompound(comp);
3860
    int count = 0;
3861
    for(auto &s : shapes) {
3862
        if(s.isNull()) {
3863
            WARN_NULL_INPUT;
3864
            continue;
3865
        }
3866
        builder.Add(comp,s.getShape());
3867
        ++count;
3868
    }
3869
    if(!count)
3870
        HANDLE_NULL_SHAPE;
3871
    _Shape = comp;
3872
    return *this;
3873
}
3874

3875
TopoShape &TopoShape::makeFace(const TopoShape &shape, const char *op, const char *maker)
3876
{
3877
    std::vector<TopoShape> shapes;
3878
    if(shape.shapeType() == TopAbs_COMPOUND) {
3879
        for(TopoDS_Iterator it(shape.getShape());it.More();it.Next())
3880
            shapes.emplace_back(it.Value());
3881
    } else
3882
        shapes.push_back(shape);
3883
    return makeFace(shapes,op,maker);
3884
}
3885

3886
TopoShape &TopoShape::makeFace(const std::vector<TopoShape> &shapes, const char *op, const char *maker)
3887
{
3888
    (void)op;
3889
    _Shape.Nullify();
3890

3891
    if(!maker || !maker[0])
3892
        maker = "Part::FaceMakerBullseye";
3893

3894
    std::unique_ptr<FaceMaker> mkFace = FaceMaker::ConstructFromType(maker);
3895
    for(auto &s : shapes) {
3896
        if (s.getShape().ShapeType() == TopAbs_COMPOUND)
3897
            mkFace->useCompound(TopoDS::Compound(s.getShape()));
3898
        else if (s.getShape().ShapeType() != TopAbs_VERTEX)
3899
            mkFace->addShape(s.getShape());
3900
    }
3901
    mkFace->Build();
3902
    _Shape = mkFace->Shape();
3903
    return *this;
3904
}
3905

3906
TopoShape &TopoShape::makeRefine(const TopoShape &shape, const char *op, RefineFail no_fail)
3907
{
3908
    (void)op;
3909
    _Shape.Nullify();
3910

3911
    if(shape.isNull()) {
3912
        if (no_fail == RefineFail::throwException) {
3913
            HANDLE_NULL_SHAPE;
3914
        }
3915
        return *this;
3916
    }
3917
    try {
3918
        BRepBuilderAPI_RefineModel mkRefine(shape.getShape());
3919
        _Shape = mkRefine.Shape();
3920
        return *this;
3921
    }catch (Standard_Failure &) {
3922
        if(no_fail == RefineFail::throwException ) {
3923
            throw;
3924
        }
3925
    }
3926
    *this = shape;
3927
    return *this;
3928
}
3929

3930
bool TopoShape::findPlane(gp_Pln& pln, double tol, double atol) const
3931
{
3932
    if (_Shape.IsNull()) {
3933
        return false;
3934
    }
3935
    if (tol < 0.0) {
3936
        tol = Precision::Confusion();
3937
    }
3938
    if (atol < 0.0) {
3939
        atol = Precision::Angular();
3940
    }
3941
    TopoDS_Shape shape;
3942
    if (countSubShapes(TopAbs_EDGE) == 1) {
3943
        // To deal with OCCT bug of wrong edge transformation
3944
        shape = BRepBuilderAPI_Copy(_Shape).Shape();
3945
    }
3946
    else {
3947
        shape = _Shape;
3948
    }
3949
    try {
3950
        bool found = false;
3951
        // BRepLib_FindSurface only really works on edges. We'll deal face first
3952
        for (auto& shape : getSubShapes(TopAbs_FACE)) {
3953
            gp_Pln plane;
3954
            auto face = TopoDS::Face(shape);
3955
            BRepAdaptor_Surface adapt(face);
3956
            if (adapt.GetType() == GeomAbs_Plane) {
3957
                plane = adapt.Plane();
3958
            }
3959
            else {
3960
                TopLoc_Location loc;
3961
                Handle(Geom_Surface) surf = BRep_Tool::Surface(face, loc);
3962
                GeomLib_IsPlanarSurface check(surf);
3963
                if (check.IsPlanar()) {
3964
                    plane = check.Plan();
3965
                }
3966
                else {
3967
                    return false;
3968
                }
3969
            }
3970
            if (!found) {
3971
                found = true;
3972
                pln = plane;
3973
            }
3974
            else if (!pln.Position().IsCoplanar(plane.Position(), tol, atol)) {
3975
                return false;
3976
            }
3977
        }
3978

3979
        // Check if there is free edges (i.e. edges does not belong to any face)
3980
        if (TopExp_Explorer(getShape(), TopAbs_EDGE, TopAbs_FACE).More()) {
3981
            // Copy shape to work around OCC transformation bug, that is, if
3982
            // edge has transformation, but underlying geometry does not (or the
3983
            // other way round), BRepLib_FindSurface returns a plane with the
3984
            // wrong transformation
3985
            BRepLib_FindSurface finder(BRepBuilderAPI_Copy(shape).Shape(), tol, Standard_True);
3986
            if (!finder.Found()) {
3987
                return false;
3988
            }
3989
            pln = GeomAdaptor_Surface(finder.Surface()).Plane();
3990
            found = true;
3991
        }
3992

3993
        // Check for free vertexes
3994
        auto vertexes = getSubShapes(TopAbs_VERTEX, TopAbs_EDGE);
3995
        if (vertexes.size()) {
3996
            if (!found && vertexes.size() > 2) {
3997
                BRep_Builder builder;
3998
                TopoDS_Compound comp;
3999
                builder.MakeCompound(comp);
4000
                for (int i = 0, c = (int)vertexes.size() - 1; i < c; ++i) {
4001
                    builder.Add(comp,
4002
                                BRepBuilderAPI_MakeEdge(TopoDS::Vertex(vertexes[i]),
4003
                                                        TopoDS::Vertex(vertexes[i + 1]))
4004
                                    .Edge());
4005
                }
4006
                BRepLib_FindSurface finder(comp, tol, Standard_True);
4007
                if (!finder.Found()) {
4008
                    return false;
4009
                }
4010
                pln = GeomAdaptor_Surface(finder.Surface()).Plane();
4011
                return true;
4012
            }
4013

4014
            double tt = tol * tol;
4015
            for (auto& v : vertexes) {
4016
                if (pln.SquareDistance(BRep_Tool::Pnt(TopoDS::Vertex(v))) > tt) {
4017
                    return false;
4018
                }
4019
            }
4020
        }
4021

4022
        // To make the returned plane normal more stable, if the shape has any
4023
        // face, use the normal of the first face.
4024
        if (hasSubShape(TopAbs_FACE)) {
4025
            shape = getSubShape(TopAbs_FACE, 1);
4026
            BRepAdaptor_Surface adapt(TopoDS::Face(shape));
4027
            double u =
4028
                adapt.FirstUParameter() + (adapt.LastUParameter() - adapt.FirstUParameter()) / 2.;
4029
            double v =
4030
                adapt.FirstVParameter() + (adapt.LastVParameter() - adapt.FirstVParameter()) / 2.;
4031
            BRepLProp_SLProps prop(adapt, u, v, 2, Precision::Confusion());
4032
            if (prop.IsNormalDefined()) {
4033
                gp_Pnt pnt;
4034
                gp_Vec vec;
4035
                // handles the orientation state of the shape
4036
                BRepGProp_Face(TopoDS::Face(shape)).Normal(u, v, pnt, vec);
4037
                pln = gp_Pln(pnt, gp_Dir(vec));
4038
            }
4039
        }
4040
        return true;
4041
    }
4042
    catch (Standard_Failure& e) {
4043
        // For some reason the above BRepBuilderAPI_Copy failed to copy
4044
        // the geometry of some edge, causing exception with message
4045
        // BRepAdaptor_Curve::No geometry. However, without the above
4046
        // copy, circular edges often have the wrong transformation!
4047
        FC_LOG("failed to find surface: " << e.GetMessageString());
4048
        return false;
4049
    }
4050
}
4051

4052
bool TopoShape::isInfinite() const
4053
{
4054
    if (_Shape.IsNull())
4055
        return false;
4056

4057
    try {
4058
        // If the shape is empty an exception may be thrown
4059
        Bnd_Box bounds;
4060
        BRepBndLib::Add(_Shape, bounds);
4061
        bounds.SetGap(0.0);
4062
        Standard_Real xMin, yMin, zMin, xMax, yMax, zMax;
4063
        bounds.Get(xMin, yMin, zMin, xMax, yMax, zMax);
4064

4065
        if (Precision::IsInfinite(xMax - xMin))
4066
            return true;
4067
        if (Precision::IsInfinite(yMax - yMin))
4068
            return true;
4069
        if (Precision::IsInfinite(zMax - zMin))
4070
            return true;
4071

4072
        return false;
4073
    }
4074
    catch (Standard_Failure&) {
4075
        return false;
4076
    }
4077
}
4078

4079
bool TopoShape::isPlanar(double tol) const
4080
{
4081
    if (_Shape.IsNull() || _Shape.ShapeType() != TopAbs_FACE) {
4082
        return false;
4083
    }
4084

4085
    BRepAdaptor_Surface adapt(TopoDS::Face(_Shape));
4086
    if (adapt.GetType() == GeomAbs_Plane) {
4087
        return true;
4088
    }
4089

4090
    TopLoc_Location loc;
4091
    Handle(Geom_Surface) surf = BRep_Tool::Surface(TopoDS::Face(_Shape), loc);
4092
    if (surf.IsNull()) {
4093
        return false;
4094
    }
4095

4096
    GeomLib_IsPlanarSurface check(surf, tol);
4097
    return check.IsPlanar();
4098
}
4099

4100
bool TopoShape::isCoplanar(const TopoShape &other, double tol) const {
4101
    if(isNull() || other.isNull())
4102
        return false;
4103
    if(_Shape.IsEqual(other._Shape))
4104
        return true;
4105
    gp_Pln pln1,pln2;
4106
    if(!findPlane(pln1,tol) || !other.findPlane(pln2,tol))
4107
        return false;
4108
    if(tol<0.0)
4109
        tol = Precision::Confusion();
4110
    return pln1.Position().IsCoplanar(pln2.Position(),tol,tol);
4111
}
4112

4113
bool TopoShape::_makeTransform(const TopoShape &shape,
4114
        const Base::Matrix4D &rclTrf, const char *op, bool checkScale, bool copy)
4115
{
4116
    if(checkScale) {
4117
        try {
4118
            auto type = rclTrf.hasScale();
4119
            if ((type != Base::ScaleType::Uniform && type != Base::ScaleType::NoScaling)
4120
                || (type == Base::ScaleType::Uniform && rclTrf.determinant3() == 0.0)) {
4121
                makeGTransform(shape,rclTrf,op,copy);
4122
                return true;
4123
            }
4124
        }
4125
        catch (const Standard_Failure& e) {
4126
            Base::Console().Warning("TopoShape::makeGTransform failed: %s\n", e.GetMessageString());
4127
        }
4128
    }
4129
    makeTransform(shape,convert(rclTrf),op,copy);
4130
    return false;
4131
}
4132

4133
TopoShape &TopoShape::makeTransform(const TopoShape &shape, const gp_Trsf &trsf, const char *op, bool copy) {
4134
    // resetElementMap();
4135

4136
    if(!copy) {
4137
        // OCCT checks the ScaleFactor against gp::Resolution() which is DBL_MIN!!!
4138
        copy = trsf.ScaleFactor()*trsf.HVectorialPart().Determinant() < 0. ||
4139
               Abs(Abs(trsf.ScaleFactor()) - 1) > Precision::Confusion();
4140
    }
4141
    TopoShape tmp(shape);
4142
    if(copy) {
4143
        BRepBuilderAPI_Transform mkTrf(shape.getShape(), trsf, Standard_True);
4144
        // TODO: calling Moved() is to make sure the shape has some Location,
4145
        // which is necessary for STEP export to work. However, if we reach
4146
        // here, it porabably means BRepBuilderAPI_Transform has modified
4147
        // underlying shapes (because of scaling), it will break compound child
4148
        // parent relationship anyway. In short, STEP import/export will most
4149
        // likely break badly if there is any scaling involved
4150
        tmp._Shape = mkTrf.Shape().Moved(gp_Trsf());
4151
    }else
4152
        tmp._Shape.Move(trsf);
4153
    if(op || (shape.Tag && shape.Tag!=Tag)) {
4154
        _Shape = tmp._Shape;
4155
        // tmp.initCache(1);
4156
        // mapSubElement(tmp,op);
4157
    } else
4158
        *this = tmp;
4159
    return *this;
4160
}
4161

4162
TopoShape &TopoShape::makeGTransform(const TopoShape &shape, const Base::Matrix4D &rclTrf, const char *op, bool copy)
4163
{
4164
    boost::ignore_unused(op);
4165
    _Shape = shape.transformGShape(rclTrf, copy);
4166
    return *this;
4167
}
4168

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

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

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

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