netgen/libsrc/stlgeom/python_stl.cpp
2024-12-03 18:58:12 +01:00

297 lines
10 KiB
C++

#ifdef NG_PYTHON
#include <../general/ngpython.hpp>
#include <core/python_ngcore.hpp>
#include <stlgeom.hpp>
#include "../meshing/python_mesh.hpp"
using namespace netgen;
namespace netgen
{
extern shared_ptr<Mesh> mesh;
extern shared_ptr<NetgenGeometry> ng_geometry;
}
static string stlparameter_description = R"delimiter(
STL Specific Meshing Parameters
-------------------------------
yangle: float = 30.
Angle for edge detection
contyangle: float = 20.
Edges continue if angle > contyangle
edgecornerangle: float = 60.
Angle of geometry edge at which the mesher should set a point.
closeedgefac: Optional[float] = 1.
Factor for meshing close edges, if None it is disabled.
minedgelen: Optional[float] = 0.001
Minimum edge length to be used for dividing edges to mesh points. If
None this is disabled.
)delimiter";
void CreateSTLParametersFromKwargs(STLParameters& stlparam, py::dict kwargs)
{
if(kwargs.contains("yangle"))
stlparam.yangle = py::cast<double>(kwargs.attr("pop")("yangle"));
if(kwargs.contains("contyangle"))
stlparam.contyangle = py::cast<double>(kwargs.attr("pop")("contyangle"));
if(kwargs.contains("edgecornerangle"))
stlparam.edgecornerangle = py::cast<double>(kwargs.attr("pop")("edgecornerangle"));
if(kwargs.contains("chartangle"))
stlparam.chartangle = py::cast<double>(kwargs.attr("pop")("chartangle"));
if(kwargs.contains("outerchartangle"))
stlparam.outerchartangle = py::cast<double>(kwargs.attr("pop")("outerchartangle"));
if(kwargs.contains("usesearchtree"))
stlparam.usesearchtree = py::cast<int>(kwargs.attr("pop")("usesearchtree"));
if(kwargs.contains("atlasfac"))
{
auto val = kwargs.attr("pop")("resthatlasfac");
if(val.is_none())
stlparam.resthatlasenable = false;
else
{
stlparam.resthatlasenable = true;
stlparam.resthatlasfac = py::cast<double>(val);
}
}
if(kwargs.contains("atlasminh"))
stlparam.atlasminh = py::cast<double>(kwargs.attr("pop")("atlasminh"));
if(kwargs.contains("surfcurvfac"))
{
auto val = kwargs.attr("pop")("surfcurvfac");
if(val.is_none())
stlparam.resthsurfcurvenable = false;
else
{
stlparam.resthsurfcurvenable = true;
stlparam.resthsurfcurvfac = py::cast<double>(val);
}
}
if(kwargs.contains("chartdistfac"))
{
auto val = kwargs.attr("pop")("chartdistfac");
if(val.is_none())
stlparam.resthchartdistenable = false;
else
{
stlparam.resthchartdistenable = true;
stlparam.resthchartdistfac = py::cast<double>(val);
}
}
if(kwargs.contains("edgeanglefac"))
{
auto val = kwargs.attr("pop")("edgeanglefac");
if(val.is_none())
stlparam.resthedgeangleenable = false;
else
{
stlparam.resthedgeangleenable = true;
stlparam.resthedgeanglefac = py::cast<double>(val);
}
}
if(kwargs.contains("surfmeshcurvfac"))
{
auto val = kwargs.attr("pop")("surfmeshcurvfac");
if(val.is_none())
stlparam.resthsurfmeshcurvenable = false;
else
{
stlparam.resthsurfmeshcurvenable = true;
stlparam.resthsurfmeshcurvfac = py::cast<double>(val);
}
}
if(kwargs.contains("linelengthfac"))
{
auto val = kwargs.attr("pop")("linelengthfac");
if(val.is_none())
stlparam.resthlinelengthenable = false;
else
{
stlparam.resthlinelengthenable = true;
stlparam.resthlinelengthfac = py::cast<double>(val);
}
}
if(kwargs.contains("recalc_h_opt"))
stlparam.recalc_h_opt = py::cast<bool>(kwargs.attr("pop")("recalc_h_opt"));
}
NGCORE_API_EXPORT void ExportSTL(py::module & m)
{
py::class_<STLGeometry,shared_ptr<STLGeometry>, NetgenGeometry> (m,"STLGeometry")
.def(py::init<>())
.def(py::init<>([](const string& filename, bool surface)
{
ifstream ist(filename);
return shared_ptr<STLGeometry>(STLGeometry::Load(ist,
surface));
}), py::arg("filename"), py::arg("surface")=false,
py::call_guard<py::gil_scoped_release>())
.def(NGSPickle<STLGeometry>())
.def("_visualizationData", [](shared_ptr<STLGeometry> stl_geo)
{
std::vector<float> vertices;
std::vector<int> trigs;
std::vector<float> normals;
std::vector<float> min = {std::numeric_limits<float>::max(),
std::numeric_limits<float>::max(),
std::numeric_limits<float>::max()};
std::vector<float> max = {std::numeric_limits<float>::lowest(),
std::numeric_limits<float>::lowest(),
std::numeric_limits<float>::lowest()};
std::vector<string> surfnames;
surfnames.push_back("stl");
vertices.reserve(stl_geo->GetNT()*3*3);
trigs.reserve(stl_geo->GetNT()*4);
normals.reserve(stl_geo->GetNT()*3*3);
size_t ii = 0;
for(int i = 0; i < stl_geo->GetNT(); i++)
{
auto& trig = stl_geo->GetTriangle(i+1);
for(int k = 0; k < 3; k++)
{
trigs.push_back(ii++);
auto& pnt = stl_geo->GetPoint(trig[k]);
for (int l = 0; l < 3; l++)
{
float val = pnt[l];
vertices.push_back(val);
min[l] = min2(min[l], val);
max[l] = max2(max[l], val);
normals.push_back(trig.Normal()[l]);
}
}
trigs.push_back(0);
}
py::gil_scoped_acquire ac;
py::dict res;
py::list snames;
for(auto name : surfnames)
snames.append(py::cast(name));
res["vertices"] = MoveToNumpy(vertices);
res["triangles"] = MoveToNumpy(trigs);
res["normals"] = MoveToNumpy(normals);
res["surfnames"] = snames;
res["min"] = MoveToNumpy(min);
res["max"] = MoveToNumpy(max);
return res;
}, py::call_guard<py::gil_scoped_release>())
.def("GenerateMesh", [] (shared_ptr<STLGeometry> geo,
MeshingParameters* pars,
shared_ptr<Mesh> mesh, py::kwargs kwargs)
{
MeshingParameters mp;
STLParameters stlparam;
if(pars)
{
auto mp_flags = pars->geometrySpecificParameters;
auto mp_kwargs = CreateDictFromFlags(mp_flags);
CreateSTLParametersFromKwargs(stlparam, mp_kwargs);
mp = *pars;
}
CreateSTLParametersFromKwargs(stlparam, kwargs);
CreateMPfromKwargs(mp, kwargs); // this will throw if any kwargs are not passed
py::gil_scoped_release gil_release;
if(!mesh)
{
mesh = make_shared<Mesh>();
}
mesh->SetGeometry(geo);
ng_geometry = geo;
SetGlobalMesh(mesh);
auto result = STLMeshingDummy(geo.get(), mesh, mp, stlparam);
if(result != 0)
{
netgen::mesh = mesh;
throw Exception("Meshing failed!");
}
return mesh;
}, py::arg("mp") = nullptr, py::arg("mesh") = nullptr,
(meshingparameter_description + stlparameter_description).c_str())
.def("Draw", FunctionPointer
([] (shared_ptr<STLGeometry> self)
{
ng_geometry = self;
})
)
.def("GetVicinity", [] (shared_ptr<STLGeometry> self, int node, int size, string type) {
NgArray<int> vic;
int trig=-1;
if(type == "trig")
trig = node;
if(type == "point")
trig = self->TrigPerPoint(node, 1);
self->GetVicinity(trig, size, vic);
auto geo = make_shared<STLGeometry>();
NgArray<STLReadTriangle> trigs;
for(auto i : Range(vic.Size())) {
int trigi = vic[i];
STLReadTriangle t;
Vec<3> normal = self->GetTriangle(trigi).Normal();
Point<3> pts[3];
auto trig = self->GetTriangle(trigi);
for(auto pi : Range(3))
pts[pi] = self->GetPoint(trig[pi]);
trigs.Append(STLReadTriangle(pts, normal));
}
geo->SetSurfaceSTL(true);
geo->InitSTLGeometry(trigs);
return geo;
}, py::arg("node"), py::arg("size"), py::arg("node_type") = "trig")
.def("SmoothDirtyTrigs", [] (shared_ptr<STLGeometry> self, py::kwargs kwargs) {
STLParameters stlparam;
CreateSTLParametersFromKwargs(stlparam, kwargs);
self->SmoothDirtyTrigs(stlparam);
})
.def("GetDirtyTrigs", [] (shared_ptr<STLGeometry> self, py::kwargs kwargs) {
STLParameters stlparam;
CreateSTLParametersFromKwargs(stlparam, kwargs);
self->MarkDirtyTrigs(stlparam);
py::list dirty;
for(auto i : Range(self->GetNT()))
if(self->IsMarkedTrig(i+1))
dirty.append(i);
})
.def("MovePointToMiddle", [] (shared_ptr<STLGeometry> self, int node, int count) {
auto trignr = self->TrigPerPoint(node, 1);
auto trig = self->GetTriangle(trignr);
int point_in_trig = -1;
for(auto i : Range(3))
if(trig[i] == node)
point_in_trig = i;
if(point_in_trig == -1)
throw Exception("Point not found in triangle");
self->SetSelectTrig(trignr);
self->SetNodeOfSelTrig(point_in_trig);
for([[maybe_unused]] auto i : Range(count))
self->MoveSelectedPointToMiddle();
})
.def("Save", &STLGeometry::Save)
;
m.def("LoadSTLGeometry", [] (const string & filename)
{
cout << "WARNING: LoadSTLGeometry is deprecated, use the STLGeometry(filename) constructor instead!" << endl;
ifstream ist(filename);
return shared_ptr<STLGeometry>(STLGeometry::Load(ist));
},py::call_guard<py::gil_scoped_release>());
}
PYBIND11_MODULE(libstl, m) {
ExportSTL(m);
}
#endif