netgen/libsrc/occ/occgeom.hpp

643 lines
21 KiB
C++
Raw Normal View History

#ifndef FILE_OCCGEOM
#define FILE_OCCGEOM
/* *************************************************************************/
/* File: occgeom.hpp */
/* Author: Robert Gaisbauer */
/* Date: 26. May 03 */
/* *************************************************************************/
#ifdef OCCGEOMETRY
#include <set>
#include <meshing.hpp>
2021-11-28 20:14:41 +05:00
#include "occ_utils.hpp"
#include "occmeshsurf.hpp"
#include <BOPAlgo_BuilderShape.hxx>
#include <BRepTools_ReShape.hxx>
#include <BRepBuilderAPI_MakeShape.hxx>
#include <BRepBuilderAPI_Sewing.hxx>
2021-11-28 20:14:41 +05:00
#include <Quantity_ColorRGBA.hxx>
#include <STEPCAFControl_Reader.hxx>
#include <StepBasic_MeasureValueMember.hxx>
2021-11-28 20:14:41 +05:00
#include <StepRepr_CompoundRepresentationItem.hxx>
#include <StepRepr_IntegerRepresentationItem.hxx>
#include <StepRepr_ValueRepresentationItem.hxx>
#include <TCollection_HAsciiString.hxx>
#include <TDocStd_Document.hxx>
#include <TopoDS.hxx>
#include <TopoDS_Shape.hxx>
#include <Transfer_FinderProcess.hxx>
2021-07-27 01:50:59 +05:00
#if OCC_VERSION_MAJOR>=7 && OCC_VERSION_MINOR>=4
#define OCC_HAVE_HISTORY
#endif
namespace netgen
{
2019-07-27 22:05:43 +05:00
// extern DLL_HEADER MeshingParameters mparam;
#define PROJECTION_TOLERANCE 1e-10
#define ENTITYISVISIBLE 1
#define ENTITYISHIGHLIGHTED 2
#define ENTITYISDRAWABLE 4
#define OCCGEOMETRYVISUALIZATIONNOCHANGE 0
#define OCCGEOMETRYVISUALIZATIONFULLCHANGE 1 // Compute transformation matrices and redraw
#define OCCGEOMETRYVISUALIZATIONHALFCHANGE 2 // Redraw
bool IsMappedShape(const Transformation<3> & trafo, const TopoDS_Shape & me, const TopoDS_Shape & you);
2019-07-28 23:22:48 +05:00
class EntityVisualizationCode
{
int code;
public:
EntityVisualizationCode()
{ code = ENTITYISVISIBLE + !ENTITYISHIGHLIGHTED + ENTITYISDRAWABLE;}
int IsVisible ()
{ return code & ENTITYISVISIBLE;}
int IsHighlighted ()
{ return code & ENTITYISHIGHLIGHTED;}
int IsDrawable ()
{ return code & ENTITYISDRAWABLE;}
void Show ()
{ code |= ENTITYISVISIBLE;}
void Hide ()
{ code &= ~ENTITYISVISIBLE;}
void Highlight ()
{ code |= ENTITYISHIGHLIGHTED;}
void Lowlight ()
{ code &= ~ENTITYISHIGHLIGHTED;}
void SetDrawable ()
{ code |= ENTITYISDRAWABLE;}
void SetNotDrawable ()
{ code &= ~ENTITYISDRAWABLE;}
};
class Line
{
public:
Point<3> p0, p1;
int layer = 1;
2019-07-28 23:22:48 +05:00
double Dist (Line l);
double Length () { return (p1-p0).Length(); }
};
inline double Det3 (double a00, double a01, double a02,
double a10, double a11, double a12,
double a20, double a21, double a22)
{
return a00*a11*a22 + a01*a12*a20 + a10*a21*a02 - a20*a11*a02 - a10*a01*a22 - a21*a12*a00;
}
2019-10-02 20:20:13 +05:00
class DLL_HEADER OCCParameters
{
public:
2019-07-28 23:22:48 +05:00
/// Factor for meshing close edges, moved to meshingparameters
// double resthcloseedgefac = 2.;
2019-10-02 20:20:13 +05:00
/// Enable / Disable detection of close edges
// int resthcloseedgeenable = true;
2019-10-02 20:20:13 +05:00
/// Minimum edge length to be used for dividing edges to mesh points
// double resthminedgelen = 0.001;
double resthminedgelen = 1e-4;
2019-10-02 20:20:13 +05:00
/// Enable / Disable use of the minimum edge length (by default use 1e-4)
int resthminedgelenenable = false;
2019-07-28 23:22:48 +05:00
2019-10-02 20:20:13 +05:00
/*!
Dump all the OpenCascade specific meshing parameters
to console
*/
void Print (ostream & ost) const;
};
2019-07-28 23:22:48 +05:00
2020-05-17 23:24:22 +05:00
class DLL_HEADER OCCGeometry : public NetgenGeometry
2019-07-28 23:22:48 +05:00
{
Point<3> center;
2019-10-02 20:20:13 +05:00
OCCParameters occparam;
2019-07-28 23:22:48 +05:00
public:
static TopTools_IndexedMapOfShape global_shape_property_indices;
static std::vector<ShapeProperties> global_shape_properties;
static TopTools_IndexedMapOfShape global_identification_indices;
static std::vector<std::vector<OCCIdentification>> global_identifications;
static ShapeProperties& GetProperties(const TopoDS_Shape& shape)
{
auto index = OCCGeometry::global_shape_property_indices.FindIndex(shape);
if(index > 0)
return OCCGeometry::global_shape_properties
[index-1];
OCCGeometry::global_shape_property_indices.Add(shape);
OCCGeometry::global_shape_properties.push_back({});
return OCCGeometry::global_shape_properties.back();
}
static bool HaveProperties(const TopoDS_Shape& shape)
{
return OCCGeometry::global_shape_property_indices.FindIndex(shape) > 0;
}
static std::vector<OCCIdentification>& GetIdentifications(const TopoDS_Shape& shape)
{
auto index = OCCGeometry::global_identification_indices.FindIndex(shape);
if(index > 0)
return OCCGeometry::global_identifications[index-1];
OCCGeometry::global_identification_indices.Add(shape);
OCCGeometry::global_identifications.push_back({});
return OCCGeometry::global_identifications.back();
}
static bool HaveIdentifications(const TopoDS_Shape& shape)
{
return OCCGeometry::global_identification_indices.FindIndex(shape) > 0;
}
2019-07-28 23:22:48 +05:00
TopoDS_Shape shape;
TopTools_IndexedMapOfShape fmap, emap, vmap, somap, shmap, wmap;
2019-07-28 23:22:48 +05:00
NgArray<bool> fsingular, esingular, vsingular;
Box<3> boundingbox;
2021-11-07 04:16:57 +05:00
2019-07-28 23:22:48 +05:00
mutable int changed;
2019-10-28 18:41:31 +05:00
mutable NgArray<int> facemeshstatus;
2019-07-28 23:22:48 +05:00
// Philippose - 15/01/2009
// Maximum mesh size for a given face
// (Used to explicitly define mesh size limits on individual faces)
NgArray<double> face_maxh;
// Philippose - 14/01/2010
// Boolean array to detect whether a face has been explicitly modified
// by the user or not
NgArray<bool> face_maxh_modified;
// Philippose - 15/01/2009
// Indicates which faces have been selected by the user in geometry mode
// (Currently handles only selection of one face at a time, but an array would
// help to extend this to multiple faces)
NgArray<bool> face_sel_status;
NgArray<EntityVisualizationCode> fvispar, evispar, vvispar;
double tolerance;
bool fixsmalledges;
bool fixspotstripfaces;
bool sewfaces;
bool makesolids;
bool splitpartitions;
2019-07-28 23:22:48 +05:00
OCCGeometry()
{
somap.Clear();
shmap.Clear();
fmap.Clear();
wmap.Clear();
emap.Clear();
vmap.Clear();
}
2019-10-02 20:20:13 +05:00
OCCGeometry(const TopoDS_Shape& _shape, int aoccdim = 3, bool copy = false);
2019-10-02 20:20:13 +05:00
Mesh::GEOM_TYPE GetGeomType() const override
{ return Mesh::GEOM_OCC; }
void SetDimension(int dim)
{
dimension = dim;
BuildFMap();
}
2019-10-02 20:20:13 +05:00
void SetOCCParameters(const OCCParameters& par)
{ occparam = par; }
2019-10-02 20:20:13 +05:00
using NetgenGeometry::GetVertex;
using NetgenGeometry::GetEdge;
using NetgenGeometry::GetFace;
GeometryShape & GetShape(const TopoDS_Shape & shape)
{
return const_cast<GeometryShape&>(as_const(*this).GetShape(shape));
}
GeometryVertex & GetVertex(const TopoDS_Shape & shape)
{
return const_cast<GeometryVertex&>(as_const(*this).GetVertex(shape));
}
GeometryEdge & GetEdge(const TopoDS_Shape & shape)
{
return const_cast<GeometryEdge&>(as_const(*this).GetEdge(shape));
}
GeometryFace & GetFace(const TopoDS_Shape & shape)
{
return const_cast<GeometryFace&>(as_const(*this).GetFace(shape));
}
const GeometryShape & GetShape(const TopoDS_Shape & shape) const;
const GeometryVertex & GetVertex(const TopoDS_Shape & shape) const;
const GeometryEdge & GetEdge(const TopoDS_Shape & shape) const;
const GeometryFace & GetFace(const TopoDS_Shape & shape) const;
2019-10-02 20:20:13 +05:00
void Analyse(Mesh& mesh,
2019-10-28 18:41:31 +05:00
const MeshingParameters& mparam) const override;
2021-11-28 20:14:41 +05:00
bool MeshFace(Mesh& mesh, const MeshingParameters& mparam,
int nr, FlatArray<int, PointIndex> glob2loc) const override;
// void OptimizeSurface(Mesh& mesh, const MeshingParameters& mparam) const override {}
2022-02-17 20:52:07 +05:00
void Save (const filesystem::path & filename) const override;
void SaveToMeshFile (ostream & /* ost */) const override;
2019-07-28 23:22:48 +05:00
2019-10-02 20:20:13 +05:00
void DoArchive(Archive& ar) override;
2020-05-17 23:24:22 +05:00
void BuildFMap();
2021-07-24 16:14:21 +05:00
auto GetShape() const { return shape; }
2019-07-28 23:22:48 +05:00
Box<3> GetBoundingBox() const
{ return boundingbox; }
int NrSolids() const
{ return somap.Extent(); }
// Philippose - 17/01/2009
// Total number of faces in the geometry
int NrFaces() const
{ return fmap.Extent(); }
void SetCenter()
{ center = boundingbox.Center(); }
Point<3> Center() const
{ return center; }
OCCSurface GetSurface (int surfi)
{
cout << "OCCGeometry::GetSurface using PLANESPACE" << endl;
return OCCSurface (TopoDS::Face(fmap(surfi)), PLANESPACE);
}
2020-05-17 23:24:22 +05:00
void CalcBoundingBox ();
void BuildVisualizationMesh (double deflection);
2019-07-28 23:22:48 +05:00
void RecursiveTopologyTree (const TopoDS_Shape & sh,
stringstream & str,
TopAbs_ShapeEnum l,
bool free,
const char * lname);
2020-05-17 23:24:22 +05:00
void GetTopologyTree (stringstream & str);
2019-07-28 23:22:48 +05:00
2020-05-17 23:24:22 +05:00
void PrintNrShapes ();
2019-07-28 23:22:48 +05:00
2020-05-17 23:24:22 +05:00
void CheckIrregularEntities (stringstream & str);
2019-07-28 23:22:48 +05:00
2020-05-17 23:24:22 +05:00
void SewFaces();
2019-07-28 23:22:48 +05:00
2020-05-17 23:24:22 +05:00
void MakeSolid();
2019-07-28 23:22:48 +05:00
Array<const GeometryVertex*> GetFaceVertices(const GeometryFace& face) const override;
void FixFaceOrientation();
2020-05-17 23:24:22 +05:00
void HealGeometry();
void GlueGeometry();
2019-07-28 23:22:48 +05:00
// Philippose - 15/01/2009
// Sets the maximum mesh size for a given face
// (Note: Local mesh size limited by the global max mesh size)
void SetFaceMaxH(int facenr, double faceh, const MeshingParameters & mparam)
{
if((facenr> 0) && (facenr <= fmap.Extent()))
{
face_maxh[facenr-1] = min(mparam.maxh,faceh);
2019-07-28 23:22:48 +05:00
// Philippose - 14/01/2010
// If the face maxh is greater than or equal to the
// current global maximum, then identify the face as
// not explicitly controlled by the user any more
if(faceh >= mparam.maxh)
{
2019-07-28 23:22:48 +05:00
face_maxh_modified[facenr-1] = 0;
}
2019-07-28 23:22:48 +05:00
else
{
2019-07-28 23:22:48 +05:00
face_maxh_modified[facenr-1] = 1;
}
2019-07-28 23:22:48 +05:00
}
}
2019-08-13 21:45:27 +05:00
void SetFaceMaxH(size_t facenr, double faceh)
{
if(facenr >= fmap.Extent())
throw RangeException("OCCGeometry faces", facenr, 0, fmap.Extent());
face_maxh[facenr] = faceh;
face_maxh_modified[facenr] = true;
}
2019-07-28 23:22:48 +05:00
// Philippose - 15/01/2009
// Returns the local mesh size of a given face
double GetFaceMaxH(int facenr)
{
if((facenr> 0) && (facenr <= fmap.Extent()))
{
return face_maxh[facenr-1];
}
else
{
return 0.0;
}
}
2019-07-28 23:22:48 +05:00
// Philippose - 14/01/2010
// Returns the flag whether the given face
// has a mesh size controlled by the user or not
bool GetFaceMaxhModified(int facenr)
{
return face_maxh_modified[facenr-1];
}
2019-07-28 23:22:48 +05:00
// Philippose - 17/01/2009
// Returns the index of the currently selected face
int SelectedFace()
{
for(int i = 1; i <= fmap.Extent(); i++)
{
if(face_sel_status[i-1])
{
2019-07-28 23:22:48 +05:00
return i;
}
2019-07-28 23:22:48 +05:00
}
2019-07-28 23:22:48 +05:00
return 0;
}
2019-07-28 23:22:48 +05:00
// Philippose - 17/01/2009
// Sets the currently selected face
void SetSelectedFace(int facenr)
{
face_sel_status = 0;
2019-07-28 23:22:48 +05:00
if((facenr >= 1) && (facenr <= fmap.Extent()))
{
face_sel_status[facenr-1] = 1;
}
}
2019-07-28 23:22:48 +05:00
void LowLightAll()
{
for (int i = 1; i <= fmap.Extent(); i++)
fvispar[i-1].Lowlight();
for (int i = 1; i <= emap.Extent(); i++)
evispar[i-1].Lowlight();
for (int i = 1; i <= vmap.Extent(); i++)
vvispar[i-1].Lowlight();
}
2020-05-17 23:24:22 +05:00
void GetUnmeshedFaceInfo (stringstream & str);
void GetNotDrawableFaces (stringstream & str);
bool ErrorInSurfaceMeshing ();
2019-07-28 23:22:48 +05:00
// void WriteOCC_STL(char * filename);
private:
2021-11-28 20:14:41 +05:00
//bool FastProject (int surfi, Point<3> & ap, double& u, double& v) const;
2019-07-28 23:22:48 +05:00
};
2021-12-16 23:00:10 +05:00
2024-04-19 12:46:02 +05:00
DLL_HEADER void Identify(const ListOfShapes & me, const ListOfShapes & you, string name, Identifications::ID_TYPE type, Transformation<3> trafo);
DLL_HEADER void Identify(const TopoDS_Shape & me, const TopoDS_Shape & you, string name, Identifications::ID_TYPE type, std::optional<std::variant<gp_Trsf, gp_GTrsf>> opt_trafo);
2019-07-28 23:22:48 +05:00
void PrintContents (OCCGeometry * geom);
2022-02-17 20:52:07 +05:00
DLL_HEADER OCCGeometry * LoadOCC_IGES (const filesystem::path & filename);
DLL_HEADER OCCGeometry * LoadOCC_STEP (const filesystem::path & filename);
DLL_HEADER OCCGeometry * LoadOCC_BREP (const filesystem::path & filename);
2019-07-28 23:22:48 +05:00
// Philippose - 31.09.2009
// External access to the mesh generation functions within the OCC
// subsystem (Not sure if this is the best way to implement this....!!)
2019-10-02 20:20:13 +05:00
DLL_HEADER extern void OCCSetLocalMeshSize(const OCCGeometry & geom, Mesh & mesh, const MeshingParameters & mparam,
const OCCParameters& occparam);
2021-11-28 20:14:41 +05:00
DLL_HEADER extern bool OCCMeshFace (const OCCGeometry & geom, Mesh & mesh, FlatArray<int, PointIndex> glob2loc,
const MeshingParameters & mparam, int nr, int projecttype, bool delete_on_failure);
inline auto GetModified(BRepBuilderAPI_MakeShape & builder, TopoDS_Shape shape) { return builder.Modified(shape); }
inline auto GetModified(BRepTools_History & history, TopoDS_Shape shape) { return history.Modified(shape); }
inline auto GetModified(BOPAlgo_BuilderShape & builder, TopoDS_Shape shape) { return builder.Modified(shape); }
inline ArrayMem<TopoDS_Shape, 1> GetModified(BRepBuilderAPI_Sewing& builder, TopoDS_Shape shape) { return {builder.Modified(shape)}; }
inline auto GetModified(BRepTools_ReShape& reshape, TopoDS_Shape shape) {
auto history = reshape.History();
return history->Modified(shape);
}
template <class TBuilder>
void PropagateIdentifications (TBuilder & builder, TopoDS_Shape shape, std::optional<Transformation<3>> trafo = nullopt)
{
TopTools_IndexedMapOfShape mod_indices;
Array<TopTools_IndexedMapOfShape> modifications;
TopTools_MapOfShape shape_handled;
Transformation<3> trafo_inv;
if(trafo)
trafo_inv = trafo->CalcInverse();
for (auto typ : { TopAbs_SOLID, TopAbs_FACE, TopAbs_EDGE, TopAbs_VERTEX })
for (TopExp_Explorer e(shape, typ); e.More(); e.Next())
{
2022-08-19 15:51:39 +05:00
auto s = e.Current();
mod_indices.Add(s);
modifications.Append(TopTools_IndexedMapOfShape());
modifications.Last().Add(s);
}
for (auto typ : { TopAbs_SOLID, TopAbs_FACE, TopAbs_EDGE, TopAbs_VERTEX })
for (TopExp_Explorer e(shape, typ); e.More(); e.Next())
{
2022-08-19 15:51:39 +05:00
auto s = e.Current();
for (auto mods : GetModified(builder, s))
{
auto index = mod_indices.FindIndex(s)-1;
modifications[index].Add(mods);
}
}
for (auto typ : { TopAbs_SOLID, TopAbs_FACE, TopAbs_EDGE, TopAbs_VERTEX })
for (TopExp_Explorer e(shape, typ); e.More(); e.Next())
{
2022-08-19 15:51:39 +05:00
auto s = e.Current();
if(shape_handled.Contains(s))
continue;
shape_handled.Add(s);
if(!OCCGeometry::HaveIdentifications(s))
continue;
auto& identifications = OCCGeometry::GetIdentifications(s);
2023-09-22 17:25:18 +05:00
// auto& shape_mapped = modifications[mod_indices.FindIndex(s)-1];
for(auto ident : identifications)
{
auto i1 = mod_indices.FindIndex(ident.to);
auto i2 = mod_indices.FindIndex(ident.from);
if(i1 == 0 || i2 == 0) // not in geometry
continue;
auto& mods_to = modifications[i1-1];
auto& mods_from = modifications[i2-1];
if(mods_to.Extent()==1 && mods_from.Extent() ==1)
continue;
auto from = ident.from;
auto to = ident.to;
for(auto it = mods_from.cbegin(); it != mods_from.cend(); it++)
for(auto it2 = mods_to.cbegin(); it2 != mods_to.cend(); it2++)
{
auto& from_mapped = it.Iterator().Value();
auto& to_mapped = it2.Iterator().Value();
if(from.IsSame(from_mapped) && to.IsSame(to_mapped))
continue;
Transformation<3> trafo_mapped = ident.trafo;
if(trafo)
{
Transformation<3> trafo_temp;
trafo_temp.Combine(ident.trafo, trafo_inv);
trafo_mapped.Combine(*trafo, trafo_temp);
}
2022-08-19 15:51:39 +05:00
if(!IsMappedShape(trafo_mapped, from_mapped, to_mapped))
continue;
OCCIdentification id_new = ident;
id_new.to = to_mapped;
id_new.from = from_mapped;
id_new.trafo = trafo_mapped;
auto id_owner = from.IsSame(s) ? from_mapped : to_mapped;
OCCGeometry::GetIdentifications(id_owner).push_back(id_new);
}
}
}
}
template <class TBuilder>
void PropagateProperties (TBuilder & builder, TopoDS_Shape shape, std::optional<Transformation<3>> trafo = nullopt)
{
bool have_identifications = false;
2023-06-29 15:23:19 +05:00
for (auto typ : { TopAbs_SOLID, TopAbs_FACE, TopAbs_EDGE, TopAbs_VERTEX })
for (TopExp_Explorer e(shape, typ); e.More(); e.Next())
{
2022-08-19 15:51:39 +05:00
auto s = e.Current();
have_identifications |= OCCGeometry::HaveIdentifications(s);
if(!OCCGeometry::HaveProperties(s))
continue;
auto prop = OCCGeometry::GetProperties(s);
for (auto mods : GetModified(builder, s))
OCCGeometry::GetProperties(mods).Merge(prop);
}
if(have_identifications)
PropagateIdentifications(builder, shape, trafo);
}
namespace step_utils
{
inline Handle(TCollection_HAsciiString) MakeName (string s)
{
return new TCollection_HAsciiString(s.c_str());
};
inline Handle(StepRepr_RepresentationItem) MakeInt (int n, string name = "")
{
Handle(StepRepr_IntegerRepresentationItem) int_obj = new StepRepr_IntegerRepresentationItem;
int_obj->Init(MakeName(name), n);
return int_obj;
}
2021-11-05 01:58:56 +05:00
inline int ReadInt (Handle(StepRepr_RepresentationItem) item)
{
return Handle(StepRepr_IntegerRepresentationItem)::DownCast(item)->Value();
}
inline Handle(StepRepr_RepresentationItem) MakeReal (double val, string name = "")
{
Handle(StepBasic_MeasureValueMember) value_member = new StepBasic_MeasureValueMember;
value_member->SetReal(val);
Handle(StepRepr_ValueRepresentationItem) value_repr = new StepRepr_ValueRepresentationItem;
value_repr->Init(MakeName(name), value_member);
return value_repr;
}
2021-11-05 01:58:56 +05:00
inline double ReadReal (Handle(StepRepr_RepresentationItem) item)
{
return Handle(StepRepr_ValueRepresentationItem)::DownCast(item)
->ValueComponentMember()->Real();
}
inline Handle(StepRepr_RepresentationItem) MakeCompound( FlatArray<Handle(StepRepr_RepresentationItem)> items, string name = "" )
{
Handle(StepRepr_HArray1OfRepresentationItem) array_repr = new StepRepr_HArray1OfRepresentationItem(1,items.Size());
for(auto i : Range(items))
array_repr->SetValue(i+1, items[i]);
Handle(StepRepr_CompoundRepresentationItem) comp = new StepRepr_CompoundRepresentationItem;
comp->Init( MakeName(name), array_repr );
return comp;
}
2021-11-05 01:58:56 +05:00
void WriteIdentifications(const Handle(Interface_InterfaceModel) model, const TopoDS_Shape & shape, const Handle(Transfer_FinderProcess) finder);
void ReadIdentifications(Handle(StepRepr_RepresentationItem) item, Handle(Transfer_TransientProcess) transProc);
inline Quantity_ColorRGBA MakeColor(const Vec<4> & c)
{
return Quantity_ColorRGBA (c[0], c[1], c[2], c[3]);
}
inline Vec<4> ReadColor (const Quantity_ColorRGBA & c)
{
auto rgb = c.GetRGB();
return {rgb.Red(), rgb.Green(), rgb.Blue(), c.Alpha()};
}
void LoadProperties(const TopoDS_Shape & shape,
const STEPCAFControl_Reader & reader,
const Handle(TDocStd_Document) step_doc);
void WriteProperties(const Handle(Interface_InterfaceModel) model, const Handle(Transfer_FinderProcess) finder, const TopoDS_Shape & shape);
2022-02-17 20:52:07 +05:00
void WriteSTEP(const TopoDS_Shape & shape, const filesystem::path & filename);
2022-02-17 20:52:07 +05:00
inline void WriteSTEP(const OCCGeometry & geo, const filesystem::path & filename)
{
WriteSTEP(geo.GetShape(), filename);
}
// deep copy, also ensures consistent shape ordering (face numbers etc.)
TopoDS_Shape WriteAndRead(const TopoDS_Shape shape);
} // namespace step_utils
}
#endif
#endif