1
/***************************************************************************
2
* Copyright (c) 2002 Jürgen Riegel <juergen.riegel@web.de> *
4
* This file is part of the FreeCAD CAx development system. *
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. *
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. *
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 *
21
***************************************************************************/
23
#include "PreCompiled.h"
30
# include <boost/regex.hpp>
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>
149
# include <BOPAlgo_ArgumentAnalyzer.hxx>
150
# include <BOPAlgo_ListOfCheckResult.hxx>
152
# include <BRepAlgoAPI_Defeaturing.hxx>
154
#if OCC_VERSION_HEX < 0x070600
155
# include <BRepAdaptor_HCurve.hxx>
156
# include <BRepAdaptor_HCompCurve.hxx>
159
# include <boost/algorithm/string/predicate.hpp>
160
# include <boost/core/ignore_unused.hpp>
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>
174
#include "TopoShape.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"
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"
195
FC_LOG_LEVEL_INIT("TopoShape",true,true)
199
const char* BRepBuilderAPI_FaceErrorText(BRepBuilderAPI_FaceError et)
203
case BRepBuilderAPI_FaceDone:
204
return "Construction was successful";
205
case BRepBuilderAPI_NoFace:
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";
214
return "Unknown creation error";
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.
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);
235
// ------------------------------------------------
237
NullShapeException::NullShapeException()
242
NullShapeException::NullShapeException(const char * sMessage)
243
: ValueError(sMessage)
247
NullShapeException::NullShapeException(const std::string& sMessage)
248
: ValueError(sMessage)
252
// ------------------------------------------------
254
BooleanException::BooleanException()
259
BooleanException::BooleanException(const char * sMessage)
260
: CADKernelError(sMessage)
264
BooleanException::BooleanException(const std::string& sMessage)
265
: CADKernelError(sMessage)
269
// ------------------------------------------------
271
TYPESYSTEM_SOURCE(Part::ShapeSegment , Data::Segment)
273
std::string ShapeSegment::getName() const
278
// ------------------------------------------------
280
TYPESYSTEM_SOURCE(Part::TopoShape , Data::ComplexGeoData)
283
TopoShape::~TopoShape() = default;
285
TopoShape::TopoShape(long tag,App::StringHasherRef hasher, const TopoDS_Shape &shape)
286
:_Shape(*this, shape)
292
TopoShape::TopoShape(const TopoDS_Shape &shape, long tag, App::StringHasherRef hasher)
293
: _Shape(*this, shape)
299
TopoShape::TopoShape(const TopoShape& shape)
305
std::pair<std::string, unsigned long> TopoShape::getElementTypeAndIndex(const char* Name)
309
boost::regex ex("^(Face|Edge|Vertex)([1-9][0-9]*)$");
312
if (Name && boost::regex_match(Name, what, ex)) {
313
element = what[1].str();
314
index = std::atoi(what[2].str().c_str());
317
return std::make_pair(element, index);
320
std::vector<const char*> TopoShape::getElementTypes() const
322
static const std::vector<const char*> temp = {"Face","Edge","Vertex"};
326
unsigned long TopoShape::countSubElements(const char* Type) const
328
return countSubShapes(Type);
331
Data::Segment* TopoShape::getSubElement(const char* Type, unsigned long n) const
333
std::stringstream str;
335
std::string temp = str.str();
336
return new ShapeSegment(getSubShape(temp.c_str()));
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 {
343
return s.getSubTopoShape(Type,silent).getShape();
346
TopoDS_Shape TopoShape::getSubShape(TopAbs_ShapeEnum type, int idx, bool silent) const {
349
return s.getSubTopoShape(type,idx,silent).getShape();
353
unsigned long TopoShape::countSubShapes(const char* Type) const
357
if(strcmp(Type,"SubShape")==0)
358
return countSubShapes(TopAbs_SHAPE);
359
auto type = shapeType(Type,true);
360
if(type == TopAbs_SHAPE)
362
return countSubShapes(type);
365
unsigned long TopoShape::countSubShapes(TopAbs_ShapeEnum Type) const
367
if(Type == TopAbs_SHAPE) {
369
for(TopoDS_Iterator it(_Shape);it.More();it.Next())
373
TopTools_IndexedMapOfShape anIndices;
374
TopExp::MapShapes(this->_Shape, Type, anIndices);
375
return anIndices.Extent();
378
bool TopoShape::hasSubShape(TopAbs_ShapeEnum type) const {
379
if(type == TopAbs_SHAPE) {
380
TopoDS_Iterator it(_Shape);
383
TopExp_Explorer exp(_Shape,type);
387
bool TopoShape::hasSubShape(const char *Type) const {
388
auto idx = shapeTypeAndIndex(Type);
389
return idx.second>0 && idx.second<=(int)countSubShapes(idx.first);
393
static inline std::vector<T> _getSubShapes(const TopoDS_Shape &s, TopAbs_ShapeEnum type) {
394
std::vector<T> shapes;
398
if(type == TopAbs_SHAPE) {
399
for(TopoDS_Iterator it(s);it.More();it.Next())
400
shapes.emplace_back(it.Value());
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));
413
static std::array<std::string,TopAbs_SHAPE> _ShapeNames;
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";
428
std::pair<TopAbs_ShapeEnum,int> TopoShape::shapeTypeAndIndex(const char *name) {
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());
438
type = shapeType(name,true);
439
if(type != TopAbs_SHAPE) {
440
std::istringstream iss(name+shapeName(type).size());
448
return std::make_pair(type,idx);
451
std::pair<TopAbs_ShapeEnum, int> TopoShape::shapeTypeAndIndex(const Data::IndexedName& element)
454
return std::make_pair(TopAbs_SHAPE, 0);
456
static const std::string _subshape("SubShape");
457
if (boost::equals(element.getType(), _subshape)) {
458
return std::make_pair(TopAbs_SHAPE, element.getIndex());
460
TopAbs_ShapeEnum shapetype = shapeType(element.getType(), true);
461
if (shapetype == TopAbs_SHAPE) {
462
return std::make_pair(TopAbs_SHAPE, 0);
464
return std::make_pair(shapetype, element.getIndex());
467
TopAbs_ShapeEnum TopoShape::shapeType(const char *type, bool silent) {
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);
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:"?"));
483
TopAbs_ShapeEnum TopoShape::shapeType(char type, bool silent) {
488
return TopAbs_VERTEX;
493
FC_THROWM(Base::CADKernelError, "invalid shape type '" << type << "'");
498
TopAbs_ShapeEnum TopoShape::shapeType(bool silent) const {
501
FC_THROWM(NullShapeException, "Input shape is null");
504
return getShape().ShapeType();
507
const std::string &TopoShape::shapeName(TopAbs_ShapeEnum type, bool silent) {
509
if(type>=0 && type<_ShapeNames.size() && !_ShapeNames[type].empty())
510
return _ShapeNames[type];
512
FC_THROWM(Base::CADKernelError, "invalid shape type '" << type << "'");
513
static std::string ret;
517
const std::string &TopoShape::shapeName(bool silent) const {
518
return shapeName(shapeType(silent),silent);
521
PyObject * TopoShape::getPySubShape(const char* Type, bool silent) const
523
return Py::new_reference_to(shape2pyshape(getSubShape(Type,silent)));
526
PyObject * TopoShape::getPyObject()
528
Base::PyObjectBase* prop = nullptr;
529
if (_Shape.IsNull()) {
530
prop = new TopoShapePy(new TopoShape(*this));
533
TopAbs_ShapeEnum type = _Shape.ShapeType();
536
case TopAbs_COMPOUND:
537
prop = new TopoShapeCompoundPy(new TopoShape(*this));
539
case TopAbs_COMPSOLID:
540
prop = new TopoShapeCompSolidPy(new TopoShape(*this));
543
prop = new TopoShapeSolidPy(new TopoShape(*this));
546
prop = new TopoShapeShellPy(new TopoShape(*this));
549
prop = new TopoShapeFacePy(new TopoShape(*this));
552
prop = new TopoShapeWirePy(new TopoShape(*this));
555
prop = new TopoShapeEdgePy(new TopoShape(*this));
558
prop = new TopoShapeVertexPy(new TopoShape(*this));
562
prop = new TopoShapePy(new TopoShape(*this));
567
prop->setNotTracking(); // TODO: Does this still belong here?
571
void TopoShape::setPyObject(PyObject* obj)
573
if (PyObject_TypeCheck(obj, &TopoShapePy::Type)) {
574
this->_Shape = static_cast<TopoShapePy*>(obj)->getTopoShapePtr()->getShape();
577
std::string error = std::string("type must be 'Shape', not ");
578
error += obj->ob_type->tp_name;
579
throw Base::TypeError(error);
583
//void TopoShape::operator = (const TopoShape& sh)
586
// this->Tag = sh.Tag;
587
// this->_Shape = sh._Shape;
591
void TopoShape::convertTogpTrsf(const Base::Matrix4D& mtrx, gp_Trsf& trsf)
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]);
598
void TopoShape::convertToMatrix(const gp_Trsf& trsf, Base::Matrix4D& mtrx)
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();
605
// set Rotation matrix
624
Base::Matrix4D TopoShape::convert(const gp_Trsf& trsf) {
626
convertToMatrix(trsf,mat);
630
gp_Trsf TopoShape::convert(const Base::Matrix4D& mtrx) {
632
convertTogpTrsf(mtrx,trsf);
636
void TopoShape::setTransform(const Base::Matrix4D& rclTrf)
639
convertTogpTrsf(rclTrf, mov);
640
TopLoc_Location loc(mov);
641
_Shape.Location(loc);
644
Base::Matrix4D TopoShape::getTransform() const
647
gp_Trsf Trf = _Shape.Location().Transformation();
648
Trf.SetScaleFactor(1.0);
649
convertToMatrix(Trf, mtrx);
654
void TopoShape::read(const char *FileName)
656
Base::FileInfo File(FileName);
658
// checking on the file
659
if (!File.isReadable())
660
throw Base::FileException("File to load not existing or not readable", FileName);
662
if (File.hasExtension({"igs", "iges"})) {
664
importIges(File.filePath().c_str());
666
else if (File.hasExtension({"stp", "step"})) {
667
importStep(File.filePath().c_str());
669
else if (File.hasExtension({"brp", "brep"})) {
671
importBrep(File.filePath().c_str());
674
throw Base::FileException("Unknown extension");
679
Example code to get the labels for each face in an IGES file.
681
#include <XSControl_WorkSession.hxx>
682
#include <XSControl_TransferReader.hxx>
683
#include <IGESData_IGESModel.hxx>
684
#include <IGESData_IGESEntity.hxx>
686
IGESControl_Reader aReader;
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();
693
Handle(IGESData_IGESModel) aModel = aReader.IGESModel();
694
Standard_Integer all = aModel->NbEntities();
697
for (ex.Init(this->_Shape, TopAbs_FACE); ex.More(); ex.Next())
699
const TopoDS_Face& aFace = TopoDS::Face(ex.Current());
700
Handle(Standard_Transient) ent = tr->EntityFromShapeResult(aFace, 1);
702
int i = aModel->Number(ent);
704
Handle(IGESData_IGESEntity) ie = aModel->Entity(i);
705
if (ie->HasShortLabel())
706
name = ie->ShortLabel()->ToCString();
712
void TopoShape::importIges(const char *FileName)
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");
724
#if OCC_VERSION_HEX < 0x070500
725
Handle(Message_ProgressIndicator) pi = new ProgressIndicator(100);
726
pi->NewScope(100, "Reading IGES file...");
728
aReader.WS()->MapReader()->SetProgress(pi);
732
aReader.ClearShapes();
733
aReader.TransferRoots();
734
// one shape that contains all subshapes
735
this->_Shape = aReader.OneShape();
736
#if OCC_VERSION_HEX < 0x070500
740
catch (Standard_Failure& e) {
741
throw Base::CADKernelError(e.GetMessageString());
745
void TopoShape::importStep(const char *FileName)
748
STEPControl_Reader aReader;
749
if (aReader.ReadFile(encodeFilename(FileName).c_str()) != IFSelect_RetDone)
750
throw Base::FileException("Error in reading STEP");
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...");
760
aReader.TransferRoots();
761
// one shape that contains all subshapes
762
this->_Shape = aReader.OneShape();
763
#if OCC_VERSION_HEX < 0x070500
767
catch (Standard_Failure& e) {
768
throw Base::CADKernelError(e.GetMessageString());
772
void TopoShape::importBrep(const char *FileName)
776
BRep_Builder aBuilder;
778
#if OCC_VERSION_HEX < 0x070500
779
Handle(Message_ProgressIndicator) pi = new ProgressIndicator(100);
780
pi->NewScope(100, "Reading BREP file...");
782
BRepTools::Read(aShape,encodeFilename(FileName).c_str(),aBuilder,pi);
785
BRepTools::Read(aShape,static_cast<Standard_CString>(FileName),aBuilder);
787
this->_Shape = aShape;
789
catch (Standard_Failure& e) {
790
throw Base::CADKernelError(e.GetMessageString());
794
void TopoShape::importBrep(std::istream& str, int indicator)
798
BRep_Builder aBuilder;
800
#if OCC_VERSION_HEX < 0x070500
802
Handle(Message_ProgressIndicator) pi = new ProgressIndicator(100);
803
pi->NewScope(100, "Reading BREP file...");
805
BRepTools::Read(aShape,str,aBuilder,pi);
809
BRepTools::Read(aShape,str,aBuilder);
813
BRepTools::Read(aShape,str,aBuilder);
815
this->_Shape = aShape;
817
catch (Standard_Failure& e) {
818
throw Base::CADKernelError(e.GetMessageString());
820
catch (const std::exception& e) {
821
throw Base::CADKernelError(e.what());
825
void TopoShape::importBinary(std::istream& str)
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())
834
BinTools::GetInteger(str, locId);
835
BinTools::GetInteger(str, orient);
836
TopAbs_Orientation anOrient = static_cast<TopAbs_Orientation>(orient);
839
this->_Shape = theShapeSet.Shape(shapeId);
840
this->_Shape.Location(theShapeSet.Locations().Location (locId));
841
this->_Shape.Orientation (anOrient);
843
catch (Standard_Failure&) {
844
throw Base::RuntimeError("Failed to read shape from binary stream");
848
void TopoShape::write(const char *FileName) const
850
Base::FileInfo File(FileName);
852
if (File.hasExtension({"igs", "iges"})) {
854
exportIges(File.filePath().c_str());
856
else if (File.hasExtension({"stp", "step"})) {
857
exportStep(File.filePath().c_str());
859
else if (File.hasExtension({"brp", "brep"})) {
861
exportBrep(File.filePath().c_str());
863
else if (File.hasExtension("stl")) {
865
exportStl(File.filePath().c_str(), 0.01);
868
throw Base::FileException("Unknown extension");
872
void TopoShape::exportIges(const char *filename) const
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");
888
catch (Standard_Failure& e) {
889
throw Base::CADKernelError(e.GetMessageString());
893
void TopoShape::exportStep(const char *filename) const
897
// Do not write out any assembly information when using the simplified STEP export
898
Interface::writeStepAssembly(Interface::Assembly::Off);
901
STEPControl_Writer aWriter;
903
const Handle(XSControl_TransferWriter)& hTransferWriter = aWriter.WS()->TransferWriter();
904
Handle(Transfer_FinderProcess) hFinder = hTransferWriter->FinderProcess();
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...");
913
if (aWriter.Transfer(this->_Shape, STEPControl_AsIs) != IFSelect_RetDone)
914
throw Base::FileException("Error in transferring STEP");
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"));
924
if (aWriter.Write(encodeFilename(filename).c_str()) != IFSelect_RetDone)
925
throw Base::FileException("Writing of STEP failed");
926
#if OCC_VERSION_HEX < 0x070500
930
catch (Standard_Failure& e) {
931
throw Base::CADKernelError(e.GetMessageString());
935
void TopoShape::exportBrep(const char *filename) const
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");
941
if (!BRepTools::Write(this->_Shape,encodeFilename(filename).c_str()))
942
throw Base::FileException("Writing of BREP failed");
946
void TopoShape::exportBrep(std::ostream& out) const
948
// See TopTools_FormatVersion of OCCT 7.6
954
BRepTools_ShapeSet SS(Standard_False);
955
SS.SetFormatNb(VERSION_1);
956
SS.Add(this->_Shape);
958
SS.Write(this->_Shape, out);
961
void TopoShape::exportBinary(std::ostream& out) const
963
// See BinTools_FormatVersion of OCCT 7.6
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);
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());
986
theShapeSet.Write(out);
987
BinTools::PutInteger(out, shapeId);
988
BinTools::PutInteger(out, locId);
989
BinTools::PutInteger(out, orient);
993
void TopoShape::dump(std::ostream& out) const
995
BRepTools::Dump(this->_Shape, out);
998
void TopoShape::exportStl(const char *filename, double deflection) const
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());
1009
void TopoShape::exportFaceSet(double dev, double ca,
1010
const std::vector<App::Color>& colors,
1011
std::ostream& str) const
1013
Base::InventorBuilder builder(str);
1014
builder.beginSeparator();
1016
std::size_t numFaces = 0;
1017
for (ex.Init(this->_Shape, TopAbs_FACE); ex.More(); ex.Next()) {
1021
bool supportFaceColors = (numFaces == colors.size());
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))
1037
std::vector<Base::Vector3f> vertices;
1038
std::vector<int> indices;
1039
vertices.resize(points.size());
1040
indices.resize(4 * facets.size());
1042
for (std::size_t i = 0; i < points.size(); i++) {
1043
vertices[i] = Base::convertTo<Base::Vector3f>(points[i]);
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;
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);
1066
Base::Coordinate3Item coords{vertices};
1067
builder.addNode(coords);
1068
Base::IndexedFaceSetItem faceSet{indices};
1069
builder.addNode(faceSet);
1070
builder.endSeparator();
1072
builder.endSeparator();
1075
void TopoShape::exportLineSet(std::ostream& str) const
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);
1083
// build up map edge->face
1084
TopTools_IndexedDataMapOfShapeListOfShape edge2Face;
1085
TopExp::MapShapesAndAncestors(this->_Shape, TopAbs_EDGE, TopAbs_FACE, edge2Face);
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;
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
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))
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));
1107
Base::DrawStyle drawStyle;
1108
drawStyle.lineWidth = 2.0F;
1109
builder.addNode(Base::MultiLineItem{vertices, drawStyle, Base::ColorRGB{0, 0, 0}});
1112
builder.endSeparator();
1115
Base::BoundBox3d TopoShape::getBoundBox() const
1117
Base::BoundBox3d box;
1119
// If the shape is empty an exception may be thrown
1121
BRepBndLib::Add(_Shape, bounds);
1123
Standard_Real xMin, yMin, zMin, xMax, yMax, zMax;
1124
bounds.Get(xMin, yMin, zMin, xMax, yMax, zMax);
1133
catch (Standard_Failure&) {
1140
bool getShapeProperties(const TopoDS_Shape& shape, GProp_GProps& prop)
1142
TopExp_Explorer xpSolid(shape, TopAbs_SOLID);
1143
if (xpSolid.More()) {
1144
BRepGProp::VolumeProperties(shape, prop);
1148
TopExp_Explorer xpFace(shape, TopAbs_FACE);
1149
if (xpFace.More()) {
1150
BRepGProp::SurfaceProperties(shape, prop);
1154
TopExp_Explorer xpEdge(shape, TopAbs_EDGE);
1155
if (xpEdge.More()) {
1156
BRepGProp::LinearProperties(shape, prop);
1160
TopExp_Explorer xpVert(shape, TopAbs_VERTEX);
1161
if (xpVert.More()) {
1164
for (; xpVert.More(); xpVert.Next()) {
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());
1172
pnts.SetX(pnts.X() / count);
1173
pnts.SetY(pnts.Y() / count);
1174
pnts.SetZ(pnts.Z() / count);
1175
prop = GProp_GProps(pnts);
1184
bool TopoShape::getCenterOfGravity(Base::Vector3d& center) const
1186
if (_Shape.IsNull())
1189
// Computing of CentreOfMass
1191
if (getShapeProperties(_Shape, prop)) {
1192
if (prop.Mass() > Precision::Infinite()) {
1195
gp_Pnt pnt = prop.CentreOfMass();
1196
center.Set(pnt.X(), pnt.Y(), pnt.Z());
1203
void TopoShape::Save (Base::Writer &writer ) const
1205
Data::ComplexGeoData::Save(writer);
1208
void TopoShape::Restore(Base::XMLReader &reader)
1210
Data::ComplexGeoData::Restore(reader);
1213
void TopoShape::SaveDocFile (Base::Writer &writer) const
1215
Data::ComplexGeoData::SaveDocFile(writer);
1218
void TopoShape::RestoreDocFile(Base::Reader &reader)
1220
Data::ComplexGeoData::RestoreDocFile(reader);
1224
unsigned int TopoShape_RefCountShapes(const TopoDS_Shape& aShape)
1226
unsigned int size = 1; // this shape
1228
// go through all direct children
1229
for (it.Initialize(aShape, false, false);it.More(); it.Next()) {
1230
size += TopoShape_RefCountShapes(it.Value());
1236
unsigned int TopoShape::getMemSize () const
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);
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);
1250
// add the size of the underlying geometric data
1251
Handle(TopoDS_TShape) tshape = shape.TShape();
1252
memsize += tshape->DynamicType()->Size();
1254
switch (shape.ShapeType())
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;
1264
surface.Initialize(face);
1266
catch (const Standard_Failure&) {
1270
switch (surface.GetType())
1273
memsize += sizeof(Geom_Plane);
1275
case GeomAbs_Cylinder:
1276
memsize += sizeof(Geom_CylindricalSurface);
1279
memsize += sizeof(Geom_ConicalSurface);
1281
case GeomAbs_Sphere:
1282
memsize += sizeof(Geom_SphericalSurface);
1285
memsize += sizeof(Geom_ToroidalSurface);
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);
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);
1298
case GeomAbs_SurfaceOfRevolution:
1299
memsize += sizeof(Geom_SurfaceOfRevolution);
1301
case GeomAbs_SurfaceOfExtrusion:
1302
memsize += sizeof(Geom_SurfaceOfLinearExtrusion);
1304
case GeomAbs_OtherSurface:
1305
// What kind of surface should this be?
1306
memsize += sizeof(Geom_Surface);
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;
1320
curve.Initialize(edge);
1322
catch (const Standard_Failure&) {
1326
switch (curve.GetType())
1329
memsize += sizeof(Geom_Line);
1331
case GeomAbs_Circle:
1332
memsize += sizeof(Geom_Circle);
1334
case GeomAbs_Ellipse:
1335
memsize += sizeof(Geom_Ellipse);
1337
case GeomAbs_Hyperbola:
1338
memsize += sizeof(Geom_Hyperbola);
1340
case GeomAbs_Parabola:
1341
memsize += sizeof(Geom_Parabola);
1343
case GeomAbs_BezierCurve:
1344
memsize += sizeof(Geom_BezierCurve);
1345
memsize += curve.NbPoles() * sizeof(Standard_Real);
1346
memsize += curve.NbPoles() * sizeof(Geom_CartesianPoint);
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);
1354
case GeomAbs_OtherCurve:
1355
// What kind of curve should this be?
1356
memsize += sizeof(Geom_Curve);
1365
memsize += sizeof(Standard_Real);
1366
memsize += sizeof(Geom_CartesianPoint);
1373
// estimated memory usage
1377
// in case the shape is invalid
1378
return sizeof(TopoDS_Shape);
1381
bool TopoShape::isNull() const
1383
return this->_Shape.IsNull() ? true : false;
1386
bool TopoShape::isValid() const
1388
BRepCheck_Analyzer aChecker(this->_Shape);
1389
return aChecker.IsValid() ? true : false;
1393
std::vector<std::string> buildShapeEnumVector()
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
1408
std::vector<std::string> buildBOPCheckResultVector()
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
1427
bool TopoShape::analyze(bool runBopCheck, std::ostream& str) const
1429
if (!this->_Shape.IsNull()) {
1430
BRepCheck_Analyzer aChecker(this->_Shape);
1431
if (!aChecker.IsValid()) {
1432
std::vector<TopoDS_Shape> shapes;
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));
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));
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));
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));
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));
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));
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));
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));
1474
for (const auto & shape : shapes) {
1475
if (!aChecker.IsValid(shape)) {
1476
const Handle(BRepCheck_Result)& result = aChecker.Result(shape);
1477
if (result.IsNull())
1479
const BRepCheck_ListOfStatus& status = result->StatusOnShape(shape);
1481
BRepCheck_ListIteratorOfListOfStatus it(status);
1483
BRepCheck_Status& val = it.Value();
1486
case BRepCheck_NoError:
1487
str << "No error" << std::endl;
1489
case BRepCheck_InvalidPointOnCurve:
1490
str << "Invalid point on curve" << std::endl;
1492
case BRepCheck_InvalidPointOnCurveOnSurface:
1493
str << "Invalid point on curve on surface" << std::endl;
1495
case BRepCheck_InvalidPointOnSurface:
1496
str << "Invalid point on surface" << std::endl;
1498
case BRepCheck_No3DCurve:
1499
str << "No 3D curve" << std::endl;
1501
case BRepCheck_Multiple3DCurve:
1502
str << "Multiple 3D curve" << std::endl;
1504
case BRepCheck_Invalid3DCurve:
1505
str << "Invalid 3D curve" << std::endl;
1507
case BRepCheck_NoCurveOnSurface:
1508
str << "No curve on surface" << std::endl;
1510
case BRepCheck_InvalidCurveOnSurface:
1511
str << "Invalid curve on surface" << std::endl;
1513
case BRepCheck_InvalidCurveOnClosedSurface:
1514
str << "Invalid curve on closed surface" << std::endl;
1516
case BRepCheck_InvalidSameRangeFlag:
1517
str << "Invalid same-range flag" << std::endl;
1519
case BRepCheck_InvalidSameParameterFlag:
1520
str << "Invalid same-parameter flag" << std::endl;
1522
case BRepCheck_InvalidDegeneratedFlag:
1523
str << "Invalid degenerated flag" << std::endl;
1525
case BRepCheck_FreeEdge:
1526
str << "Free edge" << std::endl;
1528
case BRepCheck_InvalidMultiConnexity:
1529
str << "Invalid multi-connexity" << std::endl;
1531
case BRepCheck_InvalidRange:
1532
str << "Invalid range" << std::endl;
1534
case BRepCheck_EmptyWire:
1535
str << "Empty wire" << std::endl;
1537
case BRepCheck_RedundantEdge:
1538
str << "Redundant edge" << std::endl;
1540
case BRepCheck_SelfIntersectingWire:
1541
str << "Self-intersecting wire" << std::endl;
1543
case BRepCheck_NoSurface:
1544
str << "No surface" << std::endl;
1546
case BRepCheck_InvalidWire:
1547
str << "Invalid wires" << std::endl;
1549
case BRepCheck_RedundantWire:
1550
str << "Redundant wires" << std::endl;
1552
case BRepCheck_IntersectingWires:
1553
str << "Intersecting wires" << std::endl;
1555
case BRepCheck_InvalidImbricationOfWires:
1556
str << "Invalid imbrication of wires" << std::endl;
1558
case BRepCheck_EmptyShell:
1559
str << "Empty shell" << std::endl;
1561
case BRepCheck_RedundantFace:
1562
str << "Redundant face" << std::endl;
1564
case BRepCheck_UnorientableShape:
1565
str << "Unorientable shape" << std::endl;
1567
case BRepCheck_NotClosed:
1568
str << "Not closed" << std::endl;
1570
case BRepCheck_NotConnected:
1571
str << "Not connected" << std::endl;
1573
case BRepCheck_SubshapeNotInShape:
1574
str << "Sub-shape not in shape" << std::endl;
1576
case BRepCheck_BadOrientation:
1577
str << "Bad orientation" << std::endl;
1579
case BRepCheck_BadOrientationOfSubshape:
1580
str << "Bad orientation of sub-shape" << std::endl;
1582
case BRepCheck_InvalidToleranceValue:
1583
str << "Invalid tolerance value" << std::endl;
1585
case BRepCheck_CheckFail:
1586
str << "Check failed" << std::endl;
1589
str << "Undetermined error" << std::endl;
1598
return false; // errors detected
1600
else if (runBopCheck) {
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;
1618
if (!BOPCheck.HasFaulty())
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 ¤t = BOPResultsIt.Value();
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;
1643
bool TopoShape::isClosed() const
1645
if (this->_Shape.IsNull())
1647
bool closed = false;
1648
switch (this->_Shape.ShapeType()) {
1652
closed = BRep_Tool::IsClosed(this->_Shape) ? true : false;
1654
case TopAbs_COMPSOLID:
1658
TopExp_Explorer xp(this->_Shape, TopAbs_SHELL);
1660
closed &= BRep_Tool::IsClosed(xp.Current()) ? true : false;
1665
case TopAbs_COMPOUND:
1669
for (xp.Init(this->_Shape, TopAbs_SHELL); xp.More(); xp.Next()) {
1670
closed &= BRep_Tool::IsClosed(xp.Current()) ? true : false;
1672
for (xp.Init(this->_Shape, TopAbs_FACE, TopAbs_SHELL); xp.More(); xp.Next()) {
1673
closed &= BRep_Tool::IsClosed(xp.Current()) ? true : false;
1675
for (xp.Init(this->_Shape, TopAbs_WIRE, TopAbs_FACE); xp.More(); xp.Next()) {
1676
closed &= BRep_Tool::IsClosed(xp.Current()) ? true : false;
1678
for (xp.Init(this->_Shape, TopAbs_EDGE, TopAbs_WIRE); xp.More(); xp.Next()) {
1679
closed &= BRep_Tool::IsClosed(xp.Current()) ? true : false;
1681
for (xp.Init(this->_Shape, TopAbs_VERTEX, TopAbs_EDGE); xp.More(); xp.Next()) {
1682
closed &= BRep_Tool::IsClosed(xp.Current()) ? true : false;
1689
closed = BRep_Tool::IsClosed(this->_Shape) ? true : false;
1695
TopoDS_Shape TopoShape::cut(TopoDS_Shape shape) const
1697
if (this->_Shape.IsNull())
1698
return this->_Shape;
1700
return this->_Shape;
1701
BRepAlgoAPI_Cut mkCut(this->_Shape, shape);
1702
return makeShell(mkCut.Shape());
1705
TopoDS_Shape TopoShape::cut(const std::vector<TopoDS_Shape>& shapes, Standard_Real tolerance) const
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) {
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());
1720
shapeTools.Append(shape);
1723
mkCut.SetArguments(shapeArguments);
1724
mkCut.SetTools(shapeTools);
1725
if (tolerance > 0.0)
1726
mkCut.SetFuzzyValue(tolerance);
1728
if (!mkCut.IsDone())
1729
throw Base::RuntimeError("Multi cut failed");
1731
TopoDS_Shape resShape = mkCut.Shape();
1732
return makeShell(resShape);
1735
TopoDS_Shape TopoShape::common(TopoDS_Shape shape) const
1737
if (this->_Shape.IsNull())
1738
return this->_Shape;
1741
BRepAlgoAPI_Common mkCommon(this->_Shape, shape);
1742
return makeShell(mkCommon.Shape());
1745
TopoDS_Shape TopoShape::common(const std::vector<TopoDS_Shape>& shapes, Standard_Real tolerance) const
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) {
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());
1760
shapeTools.Append(shape);
1763
mkCommon.SetArguments(shapeArguments);
1764
mkCommon.SetTools(shapeTools);
1765
if (tolerance > 0.0)
1766
mkCommon.SetFuzzyValue(tolerance);
1768
if (!mkCommon.IsDone())
1769
throw Base::RuntimeError("Multi common failed");
1771
TopoDS_Shape resShape = mkCommon.Shape();
1772
return makeShell(resShape);
1775
TopoDS_Shape TopoShape::fuse(TopoDS_Shape shape) const
1777
if (this->_Shape.IsNull())
1780
return this->_Shape;
1781
BRepAlgoAPI_Fuse mkFuse(this->_Shape, shape);
1782
return makeShell(mkFuse.Shape());
1785
TopoDS_Shape TopoShape::fuse(const std::vector<TopoDS_Shape>& shapes, Standard_Real tolerance) const
1787
if (this->_Shape.IsNull())
1788
Standard_Failure::Raise("Base shape is null");
1790
BRepAlgoAPI_Fuse mkFuse;
1791
mkFuse.SetRunParallel(true);
1792
TopTools_ListOfShape shapeArguments,shapeTools;
1793
shapeArguments.Append(this->_Shape);
1794
for (const auto & shape : shapes) {
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());
1801
shapeTools.Append(shape);
1803
mkFuse.SetArguments(shapeArguments);
1804
mkFuse.SetTools(shapeTools);
1805
if (tolerance > 0.0)
1806
mkFuse.SetFuzzyValue(tolerance);
1808
if (!mkFuse.IsDone())
1809
throw Base::RuntimeError("Multi fuse failed");
1811
TopoDS_Shape resShape = mkFuse.Shape();
1812
return makeShell(resShape);
1815
TopoDS_Shape TopoShape::oldFuse(TopoDS_Shape shape) const
1817
if (this->_Shape.IsNull())
1818
Standard_Failure::Raise("Base shape is null");
1820
Standard_Failure::Raise("Tool shape is null");
1822
throw Standard_Failure("BRepAlgo_Fuse is deprecated since OCCT 7.3");
1825
TopoDS_Shape TopoShape::section(TopoDS_Shape shape, Standard_Boolean approximate) const
1827
if (this->_Shape.IsNull())
1828
Standard_Failure::Raise("Base shape is null");
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);
1836
if (!mkSection.IsDone())
1837
throw Base::RuntimeError("Section failed");
1838
return mkSection.Shape();
1841
TopoDS_Shape TopoShape::section(const std::vector<TopoDS_Shape>& shapes,
1842
Standard_Real tolerance,
1843
Standard_Boolean approximate) const
1845
if (this->_Shape.IsNull())
1846
Standard_Failure::Raise("Base shape is null");
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) {
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());
1860
shapeTools.Append(shape);
1863
mkSection.SetArguments(shapeArguments);
1864
mkSection.SetTools(shapeTools);
1865
if (tolerance > 0.0)
1866
mkSection.SetFuzzyValue(tolerance);
1868
if (!mkSection.IsDone())
1869
throw Base::RuntimeError("Multi section failed");
1871
TopoDS_Shape resShape = mkSection.Shape();
1875
std::list<TopoDS_Wire> TopoShape::slice(const Base::Vector3d& dir, double d) const
1877
CrossSection cs(dir.x, dir.y, dir.z, this->_Shape);
1881
TopoDS_Compound TopoShape::slices(const Base::Vector3d& dir, const std::vector<double>& d) const
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));
1889
std::vector< std::list<TopoDS_Wire> >::const_iterator ft;
1890
TopoDS_Compound comp;
1891
BRep_Builder builder;
1892
builder.MakeCompound(comp);
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) {
1898
builder.Add(comp, wt);
1905
TopoDS_Shape TopoShape::generalFuse(const std::vector<TopoDS_Shape> &sOthers, Standard_Real tolerance,
1906
std::vector<TopTools_ListOfShape>* mapInOut) const
1908
if (this->_Shape.IsNull())
1909
Standard_Failure::Raise("Base shape is null");
1911
BRepAlgoAPI_BuilderAlgo mkGFA;
1912
mkGFA.SetRunParallel(true);
1913
TopTools_ListOfShape GFAArguments;
1914
GFAArguments.Append(this->_Shape);
1915
for (const TopoDS_Shape &it: sOthers) {
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());
1922
GFAArguments.Append(it);
1924
mkGFA.SetArguments(GFAArguments);
1925
if (tolerance > 0.0)
1926
mkGFA.SetFuzzyValue(tolerance);
1927
mkGFA.SetNonDestructive(Standard_True);
1929
if (!mkGFA.IsDone())
1930
throw BooleanException("MultiFusion failed");
1931
TopoDS_Shape resShape = mkGFA.Shape();
1933
for(TopTools_ListIteratorOfListOfShape it(GFAArguments); it.More(); it.Next()){
1934
mapInOut->push_back(mkGFA.Modified(it.Value()));
1940
TopoDS_Shape TopoShape::makePipe(const TopoDS_Shape& profile) const
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();
1952
TopoDS_Shape TopoShape::makePipeShell(const TopTools_ListOfShape& profiles,
1953
const Standard_Boolean make_solid,
1954
const Standard_Boolean isFrenet,
1955
int transition) const
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");
1962
BRepOffsetAPI_MakePipeShell mkPipeShell(TopoDS::Wire(this->_Shape));
1963
BRepBuilderAPI_TransitionMode transMode;
1964
switch (transition) {
1965
case 1: transMode = BRepBuilderAPI_RightCorner;
1967
case 2: transMode = BRepBuilderAPI_RoundCorner;
1969
default: transMode = BRepBuilderAPI_Transformed;
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()));
1979
if (!mkPipeShell.IsReady())
1980
throw Standard_Failure("shape is not ready to build");
1982
mkPipeShell.Build();
1985
mkPipeShell.MakeSolid();
1987
return mkPipeShell.Shape();
1990
static Handle(Law_Function) CreateBsFunction (const Standard_Real theFirst, const Standard_Real theLast, const Standard_Real 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);
2000
TopoDS_Shape TopoShape::makeTube(double radius, double tol, int cont, int maxdegree, int maxsegm) const
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;
2011
if (this->_Shape.IsNull())
2012
Standard_Failure::Raise("Cannot sweep along empty spine");
2014
#if OCC_VERSION_HEX >= 0x070600
2016
Handle(Adaptor3d_Curve) myPath;
2018
if (this->_Shape.ShapeType() == TopAbs_EDGE) {
2019
const TopoDS_Edge& path_edge = TopoDS::Edge(this->_Shape);
2020
myPath = new BRepAdaptor_Curve(path_edge);
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);
2032
Standard_Failure::Raise("Spine shape is not an edge");
2036
Handle(Geom_Circle) aCirc = new Geom_Circle (gp::XOY(), theRadius);
2037
aCirc->Rotate (gp::OZ(), M_PI/2.);
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);
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();
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()
2056
return mkBuilder.Shape();
2063
TopoDS_Shape TopoShape::makeSweep(const TopoDS_Shape& profile, double tol, int fillMode) const
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");
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");
2076
const TopoDS_Edge& path_edge = TopoDS::Edge(this->_Shape);
2077
const TopoDS_Edge& prof_edge = TopoDS::Edge(profile);
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();
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()));
2089
Standard_Failure::Raise("invalid curve in path edge");
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();
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()));
2100
if (hProfile.IsNull())
2101
Standard_Failure::Raise("invalid curve in profile edge");
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);
2107
const Handle(Geom_Surface)& surf = mkSweep.Surface();
2108
BRepBuilderAPI_MakeFace mkBuilder(surf, umin, umax, vmin, vmax , Precision::Confusion());
2109
return mkBuilder.Face();
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
2116
// https://forum.freecad.org/viewtopic.php?f=3&t=1445
2117
// https://forum.freecad.org/viewtopic.php?f=3&t=52719
2120
circle.SetRadius(radius2);
2121
gp_Pnt pos(radius1,0,0);
2123
circle.SetAxis(gp_Ax1(pos, dir));
2125
BRepBuilderAPI_MakeEdge mkEdge(circle, Base::toRadians<double>(angle1),
2126
Base::toRadians<double>(angle2));
2127
BRepBuilderAPI_MakeWire mkWire;
2128
mkWire.Add(mkEdge.Edge());
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());
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();
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
2149
if (fabs(pitch) < Precision::Confusion())
2150
Standard_Failure::Raise("Pitch of helix too small");
2152
if (fabs(height) < Precision::Confusion())
2153
Standard_Failure::Raise("Height of helix too small");
2155
if ((height > 0 && pitch < 0) || (height < 0 && pitch > 0))
2156
Standard_Failure::Raise("Pitch and height of helix not compatible");
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);
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);
2172
gp_Pnt2d aPnt(0, 0);
2173
gp_Dir2d aDir(2. * M_PI, pitch);
2174
Standard_Real coneDir = 1.0;
2176
aDir.SetCoord(-2. * M_PI, pitch);
2179
gp_Ax2d aAx2d(aPnt, aDir);
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));
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);
2196
Handle(Geom2d_TrimmedCurve) segm = GCE2d_MakeSegment(beg , end);
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));
2205
// makeLongHelix is a workaround for an OCC problem found in helices with more than
2206
// some magic number of turns. See Mantis #0954.
2208
TopoDS_Shape TopoShape::makeLongHelix(Standard_Real pitch, Standard_Real height,
2209
Standard_Real radius, Standard_Real angle,
2210
Standard_Boolean leftHanded) const
2212
if (pitch < Precision::Confusion())
2213
Standard_Failure::Raise("Pitch of helix too small");
2215
if (height < Precision::Confusion())
2216
Standard_Failure::Raise("Height of helix too small");
2218
gp_Ax2 cylAx2(gp_Pnt(0.0,0.0,0.0) , gp::DZ());
2219
Handle(Geom_Surface) surf;
2220
Standard_Boolean isCylinder;
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);
2228
else { // Conical helix
2229
angle = Base::toRadians(angle);
2230
surf = new Geom_ConicalSurface(gp_Ax3(cylAx2), angle, radius);
2234
Standard_Real turns = height/pitch;
2235
unsigned long wholeTurns = floor(turns);
2236
Standard_Real partTurn = turns - wholeTurns;
2238
gp_Pnt2d aPnt(0, 0);
2239
gp_Dir2d aDir(2. * M_PI, pitch);
2240
Standard_Real coneDir = 1.0;
2242
aDir.SetCoord(-2. * M_PI, pitch);
2245
gp_Ax2d aAx2d(aPnt, aDir);
2246
Handle(Geom2d_Line) line = new Geom2d_Line(aAx2d);
2247
gp_Pnt2d beg = line->Value(0);
2250
BRepBuilderAPI_MakeWire mkWire;
2251
Handle(Geom2d_TrimmedCurve) segm;
2252
TopoDS_Edge edgeOnSurf;
2254
for (unsigned long i = 0; i < wholeTurns; i++) {
2256
end = line->Value(sqrt(4.0*M_PI*M_PI+pitch*pitch)*(i+1));
2259
u = coneDir * (i+1) * 2.0 * M_PI;
2260
v = ((i+1) * pitch) / cos(angle);
2261
end = gp_Pnt2d(u, v);
2263
segm = GCE2d_MakeSegment(beg , end);
2264
edgeOnSurf = BRepBuilderAPI_MakeEdge(segm , surf);
2265
mkWire.Add(edgeOnSurf);
2269
if (partTurn > Precision::Confusion()) {
2271
end = line->Value(sqrt(4.0*M_PI*M_PI+pitch*pitch)*turns);
2274
u = coneDir * turns * 2.0 * M_PI;
2275
v = height / cos(angle);
2276
end = gp_Pnt2d(u, v);
2278
segm = GCE2d_MakeSegment(beg , end);
2279
edgeOnSurf = BRepBuilderAPI_MakeEdge(segm , surf);
2280
mkWire.Add(edgeOnSurf);
2283
TopoDS_Wire wire = mkWire.Wire();
2284
BRepLib::BuildCurves3d(wire);
2285
return TopoDS_Shape(std::move(wire));
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
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)
2299
Standard_Failure::Raise("Number of turns must be greater than 0");
2301
Standard_Real nbPeriods = nbturns/breakperiod;
2302
Standard_Real nbFullPeriods = floor(nbPeriods);
2303
Standard_Real partPeriod = nbPeriods - nbFullPeriods;
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);
2311
gp_Ax1 axis(gp_Pnt(0.0,0.0,0.0) , gp::DZ());
2312
Handle(Geom_Surface) surf = new Geom_SurfaceOfRevolution(meridian, axis);
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);
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);
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));
2342
TopoDS_Shape TopoShape::makeThread(Standard_Real pitch,
2343
Standard_Real depth,
2344
Standard_Real height,
2345
Standard_Real radius) const
2347
if (pitch < Precision::Confusion())
2348
Standard_Failure::Raise("Pitch of thread too small");
2350
if (depth < Precision::Confusion())
2351
Standard_Failure::Raise("Depth of thread too small");
2353
if (height < Precision::Confusion())
2354
Standard_Failure::Raise("Height of thread too small");
2356
if (radius < Precision::Confusion())
2357
Standard_Failure::Raise("Radius of thread too small");
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);
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);
2369
Standard_Real aMajor = 2. * M_PI;
2370
Standard_Real aMinor = pitch;
2372
Handle(Geom2d_Ellipse) anEllipse1 = new Geom2d_Ellipse(aAx2d , aMajor , aMinor);
2373
Handle(Geom2d_Ellipse) anEllipse2 = new Geom2d_Ellipse(aAx2d , aMajor , aMinor / 4);
2375
Handle(Geom2d_TrimmedCurve) aArc1 = new Geom2d_TrimmedCurve(anEllipse1 , 0 , M_PI);
2376
Handle(Geom2d_TrimmedCurve) aArc2 = new Geom2d_TrimmedCurve(anEllipse2 , 0 , M_PI);
2378
gp_Pnt2d anEllipsePnt1 = anEllipse1->Value(0);
2379
gp_Pnt2d anEllipsePnt2 = anEllipse1->Value(M_PI);
2381
Handle(Geom2d_TrimmedCurve) aSegment = GCE2d_MakeSegment(anEllipsePnt1 , anEllipsePnt2);
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);
2389
TopoDS_Wire threadingWire1 = BRepBuilderAPI_MakeWire(aEdge1OnSurf1 , aEdge2OnSurf1);
2390
TopoDS_Wire threadingWire2 = BRepBuilderAPI_MakeWire(aEdge1OnSurf2 , aEdge2OnSurf2);
2392
BRepLib::BuildCurves3d(threadingWire1);
2393
BRepLib::BuildCurves3d(threadingWire2);
2395
BRepOffsetAPI_ThruSections aTool(Standard_True);
2397
aTool.AddWire(threadingWire1);
2398
aTool.AddWire(threadingWire2);
2399
aTool.CheckCompatibility(Standard_False);
2401
return aTool.Shape();
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
2410
// http://opencascade.blogspot.com/2010/01/surface-modeling-part5.html
2411
BRepOffsetAPI_ThruSections aGenerator (isSolid,isRuled);
2412
aGenerator.SetMaxDegree(maxDegree);
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));
2422
else if (!item.IsNull() && item.ShapeType() == TopAbs_EDGE) {
2423
BRepBuilderAPI_MakeWire mkWire(TopoDS::Edge(item));
2424
aGenerator.AddWire(mkWire.Wire());
2427
else if (!item.IsNull() && item.ShapeType() == TopAbs_WIRE) {
2428
aGenerator.AddWire(TopoDS::Wire (item));
2433
if (countShapes < 2) {
2434
Standard_Failure::Raise("Need at least two vertices, edges or wires to create loft face");
2437
// close loft by duplicating initial profile as last profile. not perfect.
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");
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));
2454
else if (firstProfile.ShapeType() == TopAbs_EDGE) {
2455
aGenerator.AddWire(TopoDS::Wire (firstProfile));
2458
else if (firstProfile.ShapeType() == TopAbs_WIRE) {
2459
aGenerator.AddWire(TopoDS::Wire (firstProfile));
2466
Standard_Boolean anIsCheck = Standard_True;
2467
aGenerator.CheckCompatibility (anIsCheck); // use BRepFill_CompatibleWires on profiles. force #edges, orientation, "origin" to match.
2469
if (!aGenerator.IsDone())
2470
Standard_Failure::Raise("Failed to create loft face");
2472
//Base::Console().Message("DEBUG: TopoShape::makeLoft returns.\n");
2473
return aGenerator.Shape();
2476
TopoDS_Shape TopoShape::makePrism(const gp_Vec& vec) const
2478
if (this->_Shape.IsNull()) Standard_Failure::Raise("cannot sweep empty shape");
2479
BRepPrimAPI_MakePrism mkPrism(this->_Shape, vec);
2480
return mkPrism.Shape();
2483
TopoDS_Shape TopoShape::revolve(const gp_Ax1& axis, double d, Standard_Boolean isSolid) const
2485
if (this->_Shape.IsNull()) Standard_Failure::Raise("cannot revolve empty shape");
2490
Standard_Boolean convertFailed = false;
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(); }
2500
convertFailed = true; }
2503
w = TopoDS::Wire(base);}
2504
if (!convertFailed) {
2505
BRepBuilderAPI_MakeFace mkFace(w);
2506
if (mkFace.IsDone()) {
2510
convertFailed = true; }
2514
if (convertFailed) {
2515
Base::Console().Message("TopoShape::revolve could not make Solid from Wire/Edge.\n");}
2517
BRepPrimAPI_MakeRevol mkRevol(base, axis,d);
2518
return mkRevol.Shape();
2521
TopoDS_Shape TopoShape::makeOffsetShape(double offset, double tol, bool intersection,
2522
bool selfInter, short offsetMode, short join,
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;
2532
xp.Init(inputShape, TopAbs_VERTEX, TopAbs_SOLID);
2534
xp.Init(inputShape, TopAbs_SOLID);
2536
// If exactly one solid then get it
2537
TopoDS_Shape inputSolid = xp.Current();
2539
if (xp.More() == Standard_False)
2540
inputShape = inputSolid;
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));
2550
if (!mkOffset.IsDone())
2551
Standard_Failure::Raise("BRepOffsetAPI_MakeOffsetShape not done");
2552
const TopoDS_Shape& res = mkOffset.Shape();
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)
2562
Standard_Failure::Raise("no closed bounds");
2565
BRep_Builder builder;
2566
TopoDS_Compound perimeterCompound;
2567
builder.MakeCompound(perimeterCompound);
2568
for (int index = 1; index <= freeCheck.NbClosedFreeBounds(); ++index)
2570
TopoDS_Wire originalWire = freeCheck.ClosedFreeBound(index)->FreeBound();
2571
const BRepAlgo_Image& img = mkOffset.MakeOffset().OffsetEdgesFromShapes();
2573
//build offset wire.
2574
TopoDS_Wire offsetWire;
2575
builder.MakeWire(offsetWire);
2577
for (xp.Init(originalWire, TopAbs_EDGE); xp.More(); xp.Next())
2579
if (!img.HasImage(xp.Current()))
2581
Standard_Failure::Raise("no image for shape");
2583
const TopTools_ListOfShape& currentImage = img.Image(xp.Current());
2584
TopTools_ListIteratorOfListOfShape listIt;
2586
TopoDS_Edge mappedEdge;
2587
for (listIt.Initialize(currentImage); listIt.More(); listIt.Next())
2589
if (listIt.Value().ShapeType() != TopAbs_EDGE)
2592
mappedEdge = TopoDS::Edge(listIt.Value());
2597
std::ostringstream stream;
2598
stream << "wrong edge count: " << edgeCount << std::endl;
2599
Standard_Failure::Raise(stream.str().c_str());
2601
builder.Add(offsetWire, mappedEdge);
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);
2611
if (!aGenerator.IsDone())
2613
Standard_Failure::Raise("ThruSections failed");
2616
builder.Add(perimeterCompound, aGenerator.Shape());
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);
2625
sewTool.Perform(); //Perform Sewing
2627
TopoDS_Shape outputShape = sewTool.SewedShape();
2628
if ((outputShape.ShapeType() == TopAbs_SHELL) && (outputShape.Closed()))
2630
BRepBuilderAPI_MakeSolid solidMaker(TopoDS::Shell(outputShape));
2631
if (solidMaker.IsDone())
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))
2645
TopoDS_Shape TopoShape::makeOffset2D(double offset, short joinType, bool fill, bool allowOpenResult, bool intersection) const
2647
if (_Shape.IsNull())
2648
throw Base::ValueError("makeOffset2D: input shape is null!");
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
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.
2665
std::vector<TopoDS_Shape> shapesToProcess;
2666
std::vector<TopoDS_Shape> shapesToReturn;
2667
bool forceOutputCompound = false;
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;
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;
2688
shapesToProcess.push_back(it.Value());
2694
shapesToProcess.push_back(this->_Shape);
2697
if(!shapesToProcess.empty()) {
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()) {
2710
//convert edge to a wire if necessary...
2711
TopoDS_Wire sourceWire;
2712
if (sh.ShapeType() == TopAbs_WIRE){
2713
sourceWire = TopoDS::Wire(sh);
2715
sourceWire = BRepBuilderAPI_MakeWire(TopoDS::Edge(sh)).Wire();
2717
sourceWires.push_back(sourceWire);
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()));
2729
throw Base::TypeError("makeOffset2D: input shape is not an edge, wire or face or compound of those.");
2733
if (haveWires && haveFaces)
2734
throw Base::TypeError("makeOffset2D: collective offset of a mix of wires and faces is not supported");
2736
allowOpenResult = false;
2739
gp_Pln workingPlane;
2740
TopoDS_Compound compoundSourceWires;
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");
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();
2753
workingPlane = GeomAdaptor_Surface(planefinder.Surface()).Plane();
2758
TopoDS_Shape offsetShape;
2759
BRepOffsetAPI_MakeOffsetFix mkOffset(GeomAbs_JoinType(joinType), allowOpenResult);
2760
for (TopoDS_Wire &w : sourceWires) {
2761
mkOffset.AddWire(w);
2764
if (fabs(offset) > Precision::Confusion()) {
2766
#if defined(__GNUC__) && defined (FC_OS_LINUX)
2767
Base::SignalException se;
2769
mkOffset.Perform(offset);
2771
catch (Standard_Failure &){
2775
throw Base::CADKernelError("BRepOffsetAPI_MakeOffset has crashed! (Unknown exception caught)");
2777
offsetShape = mkOffset.Shape();
2779
// Replace OffsetCurve with B-Spline
2780
if (!offsetShape.IsNull()) {
2781
offsetShape = mkOffset.Replace(GeomAbs_OffsetCurve, offsetShape);
2784
if (offsetShape.IsNull())
2785
throw Base::CADKernelError("makeOffset2D: result of offsetting is null!");
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();
2792
offsetShape = sourceWires.size()>1 ? TopoDS_Shape(compoundSourceWires) : sourceWires[0];
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)));
2804
if (offsetWires.empty())
2805
throw Base::CADKernelError("makeOffset2D: offset result has no wires.");
2807
std::list<TopoDS_Wire> wiresForMakingFaces;
2810
wiresForMakingFaces = offsetWires;
2813
for(TopoDS_Wire &w : offsetWires)
2814
shapesToReturn.push_back(w);
2819
if (fabs(offset) < Precision::Confusion())
2820
throw Base::ValueError("makeOffset2D: offset distance is zero. Can't fill offset.");
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
2826
// 2. source wire is open, but offset wire is closed (if not
2827
// allowOpenResult). -> throw away source wire and make face right from
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
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);
2841
openWires.push_back(w);
2842
for(TopoDS_Wire &w : offsetWires)
2843
if (BRep_Tool::IsClosed(w))
2844
closedWires.push_back(w);
2846
openWires.push_back(w);
2848
wiresForMakingFaces = closedWires;
2849
if (!allowOpenResult || openWires.empty()){
2850
//just ignore all open wires
2853
//We need to connect open wires to form closed wires.
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.");
2859
TopoDS_Wire openWire1 = openWires.front();
2860
TopoDS_Wire openWire2 = openWires.back();
2862
//find open vertices
2863
BRepTools_WireExplorer xp;
2865
TopoDS_Vertex v1 = xp.CurrentVertex();
2866
for(;xp.More();xp.Next()){};
2867
TopoDS_Vertex v2 = xp.CurrentVertex();
2869
//find open vertices
2871
TopoDS_Vertex v3 = xp.CurrentVertex();
2872
for(;xp.More();xp.Next()){};
2873
TopoDS_Vertex v4 = xp.CurrentVertex();
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");
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.
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();
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
2897
throw Base::CADKernelError("makeOffset2D: fill offset: failed to establish open vertex relationship.");
2900
//now directions of open wires are aligned. Finally. make new wire!
2901
BRepBuilderAPI_MakeWire mkWire;
2903
BRepTools_WireExplorer it;
2904
for(it.Init(openWire1); it.More(); it.Next()){
2905
mkWire.Add(it.Current());
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());
2914
//add final joining edge
2915
mkWire.Add(BRepBuilderAPI_MakeEdge(v3,v1).Edge());
2919
wiresForMakingFaces.push_front(mkWire.Wire());
2924
if (!wiresForMakingFaces.empty()){
2925
FaceMakerBullseye mkFace;
2926
mkFace.setPlane(workingPlane);
2927
for(TopoDS_Wire &w : wiresForMakingFaces){
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());
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));
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) {
2953
builder.Add(result, sh);
2956
return TopoDS_Shape(std::move(result));
2959
return shapesToReturn[0];
2963
TopoDS_Shape TopoShape::makeThickSolid(const TopTools_ListOfShape& remFace,
2964
double offset, double tol, bool intersection,
2965
bool selfInter, short offsetMode, short join) const
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();
2975
void TopoShape::transformGeometry(const Base::Matrix4D &rclMat)
2978
if (this->_Shape.IsNull())
2979
Standard_Failure::Raise("Cannot transform null shape");
2981
*this = makeGTransform(rclMat);
2983
catch (const Standard_Failure& e) {
2984
throw Base::CADKernelError(e.GetMessageString());
2988
TopoDS_Shape TopoShape::transformGShape(const Base::Matrix4D& rclTrf, bool copy) const
2990
if (this->_Shape.IsNull())
2991
Standard_Failure::Raise("Cannot transform null shape");
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();
3013
bool TopoShape::transformShape(const Base::Matrix4D& rclTrf, bool copy, bool checkScale)
3015
if (this->_Shape.IsNull())
3016
Standard_Failure::Raise("Cannot transform null shape");
3018
return _makeTransform(TopoShape(*this),rclTrf,nullptr,checkScale,copy);
3021
TopoDS_Shape TopoShape::mirror(const gp_Ax2& ax2) const
3025
BRepBuilderAPI_Transform mkTrf(this->_Shape, mat);
3026
return mkTrf.Shape();
3029
TopoDS_Shape TopoShape::toNurbs() const
3031
if (this->_Shape.IsNull())
3032
Standard_Failure::Raise("Cannot convert null shape to NURBS");
3034
BRepBuilderAPI_NurbsConvert mkNurbs(this->_Shape);
3035
return mkNurbs.Shape();
3038
TopoDS_Shape TopoShape::replaceShape(const std::vector< std::pair<TopoDS_Shape,TopoDS_Shape> >& s) const
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);
3047
TopoDS_Shape TopoShape::removeShape(const std::vector<TopoDS_Shape>& s) const
3049
BRepTools_ReShape reshape;
3050
for (const auto & it : s)
3052
return reshape.Apply(this->_Shape, TopAbs_SHAPE);
3055
void TopoShape::sewShape(double tolerance)
3057
BRepBuilderAPI_Sewing sew(tolerance);
3058
sew.Load(this->_Shape);
3061
this->_Shape = sew.SewedShape();
3064
bool TopoShape::fix()
3066
if (this->_Shape.IsNull()) {
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.
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);
3083
if (fix.Shape().IsSame(copy._Shape)) {
3087
BRepCheck_Analyzer aChecker(fix.Shape());
3088
if (!aChecker.IsValid()) {
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);
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
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});
3112
makeShapeWithElementMap(fix.Shape(), MapperHistory(fix), {copy});
3117
bool TopoShape::fix(double precision, double mintol, double maxtol)
3119
if (this->_Shape.IsNull())
3122
TopAbs_ShapeEnum type = this->_Shape.ShapeType();
3124
ShapeFix_Shape fix(this->_Shape);
3125
fix.SetPrecision(precision);
3126
fix.SetMinTolerance(mintol);
3127
fix.SetMaxTolerance(maxtol);
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();
3139
else if (type == TopAbs_SHELL) {
3140
fix.FixWireTool()->Perform();
3141
fix.FixFaceTool()->Perform();
3142
fix.FixShellTool()->Perform();
3143
this->_Shape = fix.FixShellTool()->Shape();
3145
else if (type == TopAbs_FACE) {
3146
fix.FixWireTool()->Perform();
3147
fix.FixFaceTool()->Perform();
3148
this->_Shape = fix.Shape();
3150
else if (type == TopAbs_WIRE) {
3151
fix.FixWireTool()->Perform();
3152
this->_Shape = fix.Shape();
3155
this->_Shape = fix.Shape();
3161
bool TopoShape::removeInternalWires(double minArea)
3163
ShapeUpgrade_RemoveInternalWires fix(this->_Shape);
3164
fix.MinArea() = minArea;
3165
bool ok = fix.Perform() ? true : false;
3166
this->_Shape = fix.GetResult();
3170
TopoDS_Shape TopoShape::removeSplitter() const
3172
if (_Shape.IsNull())
3173
Standard_Failure::Raise("Cannot remove splitter from empty shape");
3175
if (_Shape.ShapeType() == TopAbs_SOLID) {
3176
const TopoDS_Solid &solid = TopoDS::Solid(_Shape);
3177
BRepBuilderAPI_MakeSolid mkSolid;
3179
for (it.Init(solid, TopAbs_SHELL); it.More(); it.Next()) {
3180
const TopoDS_Shell ¤tShell = 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);
3188
mkSolid.Add(currentShell);
3192
Standard_Failure::Raise("Removing splitter failed");
3196
return mkSolid.Solid();
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();
3205
Standard_Failure::Raise("Removing splitter failed");
3208
else if (_Shape.ShapeType() == TopAbs_COMPOUND) {
3209
BRep_Builder builder;
3210
TopoDS_Compound comp;
3211
builder.MakeCompound(comp);
3215
for (xp.Init(_Shape, TopAbs_SOLID); xp.More(); xp.Next()) {
3216
const TopoDS_Solid &solid = TopoDS::Solid(xp.Current());
3217
BRepTools_ReShape reshape;
3219
for (it.Init(solid, TopAbs_SHELL); it.More(); it.Next()) {
3220
const TopoDS_Shell ¤tShell = 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);
3229
builder.Add(comp, reshape.Apply(solid));
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());
3240
for (xp.Init(_Shape, TopAbs_FACE, TopAbs_SHELL); xp.More(); xp.Next()) {
3241
if (!xp.Current().IsNull())
3242
builder.Add(comp, xp.Current());
3244
for (xp.Init(_Shape, TopAbs_WIRE, TopAbs_FACE); xp.More(); xp.Next()) {
3245
if (!xp.Current().IsNull())
3246
builder.Add(comp, xp.Current());
3248
for (xp.Init(_Shape, TopAbs_EDGE, TopAbs_WIRE); xp.More(); xp.Next()) {
3249
if (!xp.Current().IsNull())
3250
builder.Add(comp, xp.Current());
3252
for (xp.Init(_Shape, TopAbs_VERTEX, TopAbs_EDGE); xp.More(); xp.Next()) {
3253
if (!xp.Current().IsNull())
3254
builder.Add(comp, xp.Current());
3257
return TopoDS_Shape(std::move(comp));
3263
void TopoShape::getDomains(std::vector<Domain>& domains) const
3265
std::size_t countFaces = 0;
3266
for (TopExp_Explorer xp(this->_Shape, TopAbs_FACE); xp.More(); xp.Next()) {
3269
domains.reserve(countFaces);
3271
for (TopExp_Explorer xp(this->_Shape, TopAbs_FACE); xp.More(); xp.Next()) {
3272
TopoDS_Face face = TopoDS::Face(xp.Current());
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
3281
domains.push_back(domain);
3286
domain.points.reserve(points.size());
3287
for (const auto& it : points) {
3288
Standard_Real X, Y, Z;
3290
domain.points.emplace_back(X, Y, Z);
3293
// copy the triangles
3294
domain.facets.reserve(facets.size());
3295
for (const auto& it : facets) {
3296
Standard_Integer N1, N2, N3;
3303
domain.facets.push_back(tria);
3306
domains.push_back(domain);
3311
void TopoShape::getFacesFromDomains(const std::vector<Domain>& domains,
3312
std::vector<Base::Vector3d>& points,
3313
std::vector<Facet>& faces) const
3316
mesh.getFacesFromDomains(domains, points, faces);
3319
double TopoShape::getAccuracy() const
3321
double deviation = 0.2;
3322
Base::BoundBox3d bbox = getBoundBox();
3324
return ((bbox.LengthX() + bbox.LengthY() + bbox.LengthZ())/300.0 * deviation);
3325
return ComplexGeoData::getAccuracy();
3328
void TopoShape::getFaces(std::vector<Base::Vector3d> &aPoints,
3329
std::vector<Facet> &aTopo,
3330
double accuracy, uint16_t /*flags*/) const
3332
if (this->_Shape.IsNull())
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);
3346
void TopoShape::setFaces(const std::vector<Base::Vector3d> &Points,
3347
const std::vector<Facet> &Topo, double tolerance)
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;
3358
TopoDS_Compound aComp;
3359
BRep_Builder BuildTool;
3360
BuildTool.MakeCompound(aComp);
3362
uint32_t ctPoints = Points.size();
3363
Vertexes.resize(ctPoints);
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);
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]);
3375
if (it.I2 < ctPoints) {
3376
if (Vertexes[it.I2].IsNull())
3377
Vertexes[it.I2] = CreateVertex(Points[it.I2]);
3379
if (it.I3 < ctPoints) {
3380
if (Vertexes[it.I3].IsNull())
3381
Vertexes[it.I3] = CreateVertex(Points[it.I3]);
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;
3398
BRepBuilderAPI_MakeEdge mkEdge(Vertexes[p1], Vertexes[p2]);
3399
if (mkEdge.IsDone())
3400
Edges[key2] = mkEdge.Edge();
3403
auto GetEdge = [&Edges](uint32_t p1, uint32_t p2) {
3404
auto key = std::make_pair(p1, p2);
3407
for (const auto& it : Topo) {
3408
CreateEdge(it.I1, it.I2);
3409
CreateEdge(it.I2, it.I3);
3410
CreateEdge(it.I3, it.I1);
3413
for (const auto& it : Topo) {
3414
if (it.I1 >= ctPoints || it.I2 >= ctPoints || it.I3 >= ctPoints)
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;
3420
p1.SetCoord(x1,y1,z1);
3421
p2.SetCoord(x2,y2,z2);
3422
p3.SetCoord(x3,y3,z3);
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())
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);
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);
3453
#if OCC_VERSION_HEX < 0x070500
3454
Handle(Message_ProgressIndicator) pi = new ProgressIndicator(100);
3455
pi->NewScope(100, "Create shape from mesh...");
3458
aSewingTool.Perform(pi);
3460
aSewingTool.Perform();
3463
_Shape = aSewingTool.SewedShape();
3464
#if OCC_VERSION_HEX < 0x070500
3467
if (_Shape.IsNull())
3471
void TopoShape::getLinesFromSubShape(const TopoDS_Shape& shape,
3472
std::vector<Base::Vector3d> &vertices,
3473
std::vector<Line> &lines) const
3478
// build up map edge->face
3479
TopTools_IndexedDataMapOfShapeListOfShape edge2Face;
3480
TopExp::MapShapesAndAncestors(this->_Shape, TopAbs_EDGE, TopAbs_FACE, edge2Face);
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;
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
3490
// Look for one face in our map (it doesn't care which one we take)
3491
int index = edge2Face.FindIndex(aEdge);
3495
const auto &faces = edge2Face.FindFromIndex(index);
3496
if (faces.IsEmpty())
3499
const TopoDS_Face& aFace = TopoDS::Face(faces.First());
3500
if (!Part::Tools::getPolygonOnTriangulation(aEdge, aFace, points))
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));
3510
if (line_start+1 < vertices.size()) {
3511
lines.emplace_back();
3512
lines.back().I1 = line_start;
3513
lines.back().I2 = vertices.size()-1;
3518
void TopoShape::getLines(std::vector<Base::Vector3d> &vertices,
3519
std::vector<TopoShape::Line> &lines,
3520
double /*Accuracy*/, uint16_t /*flags*/) const
3522
getLinesFromSubShape(_Shape, vertices, lines);
3525
void TopoShape::getPoints(std::vector<Base::Vector3d> &Points,
3526
std::vector<Base::Vector3d> &Normals,
3527
double Accuracy, uint16_t /*flags*/) const
3529
if (_Shape.IsNull())
3532
const int minPointsPerEdge = 30;
3533
const double lateralDistance = Accuracy;
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);
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);
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()) {
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);
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;
3574
// get geometrical length and width of the surface
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);
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);
3587
fLengthU += p1.Distance(p2);
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);
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);
3599
fLengthV += p1.Distance(p2);
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);
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;
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;
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));
3621
if (GeomLib::NormEstim(aSurf, p2d, Precision::Confusion(), normal) <= 1) {
3622
if (face.Orientation() == TopAbs_REVERSED)
3624
Normals.push_back(Base::convertTo<Base::Vector3d>(normal));
3627
Normals.emplace_back(0,0,0);
3634
// if no faces are found then the normals can be cleared
3639
void TopoShape::getLinesFromSubElement(const Data::Segment* element,
3640
std::vector<Base::Vector3d> &vertices,
3641
std::vector<Line> &lines) const
3643
if (element->is<ShapeSegment>()) {
3644
const TopoDS_Shape& shape = static_cast<const ShapeSegment*>(element)->Shape;
3648
getLinesFromSubShape(shape, vertices, lines);
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
3657
if (element->is<ShapeSegment>()) {
3658
const TopoDS_Shape& shape = static_cast<const ShapeSegment*>(element)->Shape;
3659
if (shape.IsNull() || shape.ShapeType() != TopAbs_FACE)
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);
3667
(void)pointNormals; // leave this empty
3671
TopoDS_Shape TopoShape::defeaturing(const std::vector<TopoDS_Shape>& s) const
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);
3681
if (!defeat.IsDone()) {
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);
3689
return defeat.Shape();
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
3700
TopoDS_Shape TopoShape::makeShell(const TopoDS_Shape& input) const
3702
// For comparison see also:
3703
// GEOMImpl_BooleanDriver::makeCompoundShellFromFaces
3706
if (input.ShapeType() != TopAbs_COMPOUND)
3709
// we need a compound that consists of only faces
3712
it.Init(input, TopAbs_SHELL);
3716
// no wires outside a face
3717
it.Init(input, TopAbs_WIRE, TopAbs_FACE);
3721
// no edges outside a wire
3722
it.Init(input, TopAbs_EDGE, TopAbs_WIRE);
3726
// no vertexes outside an edge
3727
it.Init(input, TopAbs_VERTEX, TopAbs_EDGE);
3731
BRep_Builder builder;
3734
builder.MakeShell(shell);
3737
for (it.Init(input, TopAbs_FACE); it.More(); it.Next()) {
3738
if (!it.Current().IsNull())
3739
builder.Add(shell, it.Current());
3743
BRepCheck_Analyzer check(shell);
3744
if (!check.IsValid()) {
3745
ShapeUpgrade_ShellSewing sewShell;
3746
shape = sewShell.ApplySewing(shell);
3752
if (shape.ShapeType() != TopAbs_SHELL)
3755
return shape; // success
3757
catch (Standard_Failure&) {
3762
#define _HANDLE_NULL_SHAPE(_msg,_throw) do {\
3764
FC_THROWM(NullShapeException,_msg);\
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)
3773
TopoShape &TopoShape::makeWires(const TopoShape &shape, const char *op, bool fix, double tol)
3780
if(tol<Precision::Confusion()) tol = Precision::Confusion();
3784
std::vector<TopoShape> edges;
3785
std::list<TopoShape> edge_list;
3786
std::vector<TopoShape> wires;
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));
3793
edges.reserve(edge_list.size());
3794
wires.reserve(edge_list.size());
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());
3805
TopoDS_Wire new_wire = mkWire.Wire(); // current new wire
3807
// try to connect each edge to the wire, the wire is complete if no more edges are connectible
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
3816
edges.push_back(*it);
3817
edges.back().setShape(mkWire.Edge());
3818
edge_list.erase(it);
3819
new_wire = mkWire.Wire();
3825
// Fix any topological issues of the wire
3827
aFix.SetPrecision(tol);
3828
aFix.Load(new_wire);
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());
3836
aFix.FixConnected();
3838
// Now retrieve the shape and set it without touching element map
3839
wires.back().setShape(aFix.Wire());
3841
return makeCompound(wires,nullptr,false);
3844
TopoShape &TopoShape::makeCompound(const std::vector<TopoShape> &shapes, const char *op, bool force)
3852
if(!force && shapes.size()==1) {
3857
BRep_Builder builder;
3858
TopoDS_Compound comp;
3859
builder.MakeCompound(comp);
3861
for(auto &s : shapes) {
3866
builder.Add(comp,s.getShape());
3875
TopoShape &TopoShape::makeFace(const TopoShape &shape, const char *op, const char *maker)
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());
3882
shapes.push_back(shape);
3883
return makeFace(shapes,op,maker);
3886
TopoShape &TopoShape::makeFace(const std::vector<TopoShape> &shapes, const char *op, const char *maker)
3891
if(!maker || !maker[0])
3892
maker = "Part::FaceMakerBullseye";
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());
3902
_Shape = mkFace->Shape();
3906
TopoShape &TopoShape::makeRefine(const TopoShape &shape, const char *op, RefineFail no_fail)
3911
if(shape.isNull()) {
3912
if (no_fail == RefineFail::throwException) {
3918
BRepBuilderAPI_RefineModel mkRefine(shape.getShape());
3919
_Shape = mkRefine.Shape();
3921
}catch (Standard_Failure &) {
3922
if(no_fail == RefineFail::throwException ) {
3930
bool TopoShape::findPlane(gp_Pln& pln, double tol, double atol) const
3932
if (_Shape.IsNull()) {
3936
tol = Precision::Confusion();
3939
atol = Precision::Angular();
3942
if (countSubShapes(TopAbs_EDGE) == 1) {
3943
// To deal with OCCT bug of wrong edge transformation
3944
shape = BRepBuilderAPI_Copy(_Shape).Shape();
3951
// BRepLib_FindSurface only really works on edges. We'll deal face first
3952
for (auto& shape : getSubShapes(TopAbs_FACE)) {
3954
auto face = TopoDS::Face(shape);
3955
BRepAdaptor_Surface adapt(face);
3956
if (adapt.GetType() == GeomAbs_Plane) {
3957
plane = adapt.Plane();
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();
3974
else if (!pln.Position().IsCoplanar(plane.Position(), tol, atol)) {
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()) {
3989
pln = GeomAdaptor_Surface(finder.Surface()).Plane();
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) {
4002
BRepBuilderAPI_MakeEdge(TopoDS::Vertex(vertexes[i]),
4003
TopoDS::Vertex(vertexes[i + 1]))
4006
BRepLib_FindSurface finder(comp, tol, Standard_True);
4007
if (!finder.Found()) {
4010
pln = GeomAdaptor_Surface(finder.Surface()).Plane();
4014
double tt = tol * tol;
4015
for (auto& v : vertexes) {
4016
if (pln.SquareDistance(BRep_Tool::Pnt(TopoDS::Vertex(v))) > tt) {
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));
4028
adapt.FirstUParameter() + (adapt.LastUParameter() - adapt.FirstUParameter()) / 2.;
4030
adapt.FirstVParameter() + (adapt.LastVParameter() - adapt.FirstVParameter()) / 2.;
4031
BRepLProp_SLProps prop(adapt, u, v, 2, Precision::Confusion());
4032
if (prop.IsNormalDefined()) {
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));
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());
4052
bool TopoShape::isInfinite() const
4054
if (_Shape.IsNull())
4058
// If the shape is empty an exception may be thrown
4060
BRepBndLib::Add(_Shape, bounds);
4062
Standard_Real xMin, yMin, zMin, xMax, yMax, zMax;
4063
bounds.Get(xMin, yMin, zMin, xMax, yMax, zMax);
4065
if (Precision::IsInfinite(xMax - xMin))
4067
if (Precision::IsInfinite(yMax - yMin))
4069
if (Precision::IsInfinite(zMax - zMin))
4074
catch (Standard_Failure&) {
4079
bool TopoShape::isPlanar(double tol) const
4081
if (_Shape.IsNull() || _Shape.ShapeType() != TopAbs_FACE) {
4085
BRepAdaptor_Surface adapt(TopoDS::Face(_Shape));
4086
if (adapt.GetType() == GeomAbs_Plane) {
4090
TopLoc_Location loc;
4091
Handle(Geom_Surface) surf = BRep_Tool::Surface(TopoDS::Face(_Shape), loc);
4092
if (surf.IsNull()) {
4096
GeomLib_IsPlanarSurface check(surf, tol);
4097
return check.IsPlanar();
4100
bool TopoShape::isCoplanar(const TopoShape &other, double tol) const {
4101
if(isNull() || other.isNull())
4103
if(_Shape.IsEqual(other._Shape))
4106
if(!findPlane(pln1,tol) || !other.findPlane(pln2,tol))
4109
tol = Precision::Confusion();
4110
return pln1.Position().IsCoplanar(pln2.Position(),tol,tol);
4113
bool TopoShape::_makeTransform(const TopoShape &shape,
4114
const Base::Matrix4D &rclTrf, const char *op, bool checkScale, bool copy)
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);
4125
catch (const Standard_Failure& e) {
4126
Base::Console().Warning("TopoShape::makeGTransform failed: %s\n", e.GetMessageString());
4129
makeTransform(shape,convert(rclTrf),op,copy);
4133
TopoShape &TopoShape::makeTransform(const TopoShape &shape, const gp_Trsf &trsf, const char *op, bool copy) {
4134
// resetElementMap();
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();
4141
TopoShape tmp(shape);
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());
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);
4162
TopoShape &TopoShape::makeGTransform(const TopoShape &shape, const Base::Matrix4D &rclTrf, const char *op, bool copy)
4164
boost::ignore_unused(op);
4165
_Shape = shape.transformGShape(rclTrf, copy);