#ifdef NG_PYTHON #include <../general/ngpython.hpp> #include #include #include "../meshing/python_mesh.hpp" using namespace netgen; namespace netgen { extern shared_ptr mesh; extern shared_ptr 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(kwargs.attr("pop")("yangle")); if(kwargs.contains("contyangle")) stlparam.contyangle = py::cast(kwargs.attr("pop")("contyangle")); if(kwargs.contains("edgecornerangle")) stlparam.edgecornerangle = py::cast(kwargs.attr("pop")("edgecornerangle")); if(kwargs.contains("chartangle")) stlparam.chartangle = py::cast(kwargs.attr("pop")("chartangle")); if(kwargs.contains("outerchartangle")) stlparam.outerchartangle = py::cast(kwargs.attr("pop")("outerchartangle")); if(kwargs.contains("usesearchtree")) stlparam.usesearchtree = py::cast(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(val); } } if(kwargs.contains("atlasminh")) stlparam.atlasminh = py::cast(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(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(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(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(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(val); } } if(kwargs.contains("recalc_h_opt")) stlparam.recalc_h_opt = py::cast(kwargs.attr("pop")("recalc_h_opt")); } NGCORE_API_EXPORT void ExportSTL(py::module & m) { py::class_, NetgenGeometry> (m,"STLGeometry") .def(py::init<>()) .def(py::init<>([](const string& filename, bool surface) { ifstream ist(filename); return shared_ptr(STLGeometry::Load(ist, surface)); }), py::arg("filename"), py::arg("surface")=false, py::call_guard()) .def(NGSPickle()) .def("_visualizationData", [](shared_ptr stl_geo) { std::vector vertices; std::vector trigs; std::vector normals; std::vector min = {std::numeric_limits::max(), std::numeric_limits::max(), std::numeric_limits::max()}; std::vector max = {std::numeric_limits::lowest(), std::numeric_limits::lowest(), std::numeric_limits::lowest()}; std::vector 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()) .def("GenerateMesh", [] (shared_ptr geo, MeshingParameters* pars, shared_ptr 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->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 self) { ng_geometry = self; }) ) .def("GetVicinity", [] (shared_ptr self, int node, int size, string type) { NgArray vic; int trig; if(type == "trig") trig = node; if(type == "point") trig = self->TrigPerPoint(node, 1); self->GetVicinity(trig, size, vic); auto geo = make_shared(); NgArray 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 self, py::kwargs kwargs) { STLParameters stlparam; CreateSTLParametersFromKwargs(stlparam, kwargs); self->SmoothDirtyTrigs(stlparam); }) .def("GetDirtyTrigs", [] (shared_ptr 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 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::Load(ist)); },py::call_guard()); } PYBIND11_MODULE(libstl, m) { ExportSTL(m); } #endif