OrcaSlicer latest
OrcaSlicer
Loading...
Searching...
No Matches
Slic3r::GeometryEngine Class Reference

#include </home/runner/work/OrcaSlicer/OrcaSlicer/src/libslic3r/CAD/GeometryEngine.hpp>

Classes

struct  MeshBrepStats
struct  MassProps
struct  Deviation
struct  CylinderFace

Static Public Member Functions

static std::vector< TopoDS_Shape > read_step_solids (const std::string &path, std::string &err)
static TopoDS_Shape mesh_to_brep (const indexed_triangle_set &its, double tolerance, double merge_angle_deg, MeshBrepStats &stats)
static MassProps mass_properties (const TopoDS_Shape &shape)
static Deviation surface_deviation (const TopoDS_Shape &candidate, const TopoDS_Shape &reference, double linear_deflection=0.5)
static TopoDS_Shape apply_fillet (const TopoDS_Shape &solid, double radius, FaceGroup faces=FaceGroup::All)
static TopoDS_Shape apply_fillet (const TopoDS_Shape &solid, double radius, int edge_id)
static TopoDS_Shape apply_chamfer (const TopoDS_Shape &solid, double distance, FaceGroup faces=FaceGroup::All)
static TopoDS_Shape apply_chamfer (const TopoDS_Shape &solid, double distance, int edge_id)
static TopoDS_Shape apply_fillet (const TopoDS_Shape &solid, double radius, const std::vector< int > &edge_ids)
static TopoDS_Shape apply_chamfer (const TopoDS_Shape &solid, double distance, const std::vector< int > &edge_ids)
static TriangleMesh tessellate (const TopoDS_Shape &shape, double linear_deflection=0.003, double angular_deflection=0.5)
static TopoDS_Face face_by_index (const TopoDS_Shape &shape, int index)
static int face_count (const TopoDS_Shape &shape)
static std::vector< TopoDS_Face > faces_of (const TopoDS_Shape &shape)
static std::vector< TopoDS_Edge > edges_of (const TopoDS_Shape &shape)
static std::vector< TopoDS_Edge > edges_of_face (const TopoDS_Face &face)
static Vec3d face_centroid_world (const TopoDS_Face &face)
static Vec3d face_normal_world (const TopoDS_Face &face)
static std::vector< Vec3d > sample_edge_world (const TopoDS_Edge &edge, double chord_tol=0.05)
static std::vector< std::vector< Vec3d > > display_edges (const TopoDS_Shape &shape, double chord_tol)
static int edge_count (const TopoDS_Shape &shape)
static TopoDS_Edge edge_by_index (const TopoDS_Shape &shape, int index)
static int edge_index_of (const TopoDS_Shape &shape, const TopoDS_Edge &edge)
static CylinderFace cylinder_of_face (const TopoDS_Face &face)
static CylinderFace circle_of_edge (const TopoDS_Edge &edge)
static bool face_plane_bounds (const TopoDS_Face &face, const Vec3d &origin, const Vec3d &x_axis, const Vec3d &y_axis, double &umin, double &umax, double &vmin, double &vmax)

Static Private Member Functions

static std::vector< TopoDS_Edge > collect_edges (const TopoDS_Shape &solid, FaceGroup faces)
static FaceGroup classify_face (const TopoDS_Face &face, const TopoDS_Shape &solid)

Detailed Description


Class Documentation

◆ Slic3r::GeometryEngine::MeshBrepStats

struct Slic3r::GeometryEngine::MeshBrepStats
Class Members
int boundary_edges {0}
int degenerate_collapsed {0}
int degenerate_sliver {0}
int faces_built {0}
int faces_failed {0}
int faces_final {0}
int input_tris {0}
bool is_solid {false}
int kept_tris {0}
int nonmanifold_edges {0}
int unique_edges {0}
double volume {0.0}
bool watertight {false}

◆ Slic3r::GeometryEngine::MassProps

struct Slic3r::GeometryEngine::MassProps
Collaboration diagram for Slic3r::GeometryEngine::MassProps:
Class Members
Vec3d center_of_mass {Vec3d::Zero()}
array< double, 9 > inertia {}
bool is_solid {false}
double surface_area {0.0}
bool valid {false}
double volume {0.0}

◆ Slic3r::GeometryEngine::Deviation

struct Slic3r::GeometryEngine::Deviation
Class Members
double max_mm {0}
double mean_mm {0}
double rms_mm {0}
int sample_count {0}

◆ Slic3r::GeometryEngine::CylinderFace

struct Slic3r::GeometryEngine::CylinderFace
Class Members
Vec3d axis {0, 0, 1}
Vec3d base {0, 0, 0}
double height {0}
bool internal {false}
bool ok {false}
double radius {0}

Member Function Documentation

◆ apply_chamfer() [1/3]

TopoDS_Shape Slic3r::GeometryEngine::apply_chamfer ( const TopoDS_Shape & solid,
double distance,
const std::vector< int > & edge_ids )
static
389{
390 if (distance <= 0.001) throw std::runtime_error(_u8L("the chamfer distance must be greater than 0"));
391 if (edge_ids.empty()) throw std::runtime_error("apply_chamfer: no edge picked");
392
393 BRepFilletAPI_MakeChamfer mk(solid);
394 for (int id : edge_ids) {
395 TopoDS_Edge edge = edge_by_index(solid, id);
396 if (edge.IsNull()) throw std::runtime_error("apply_chamfer: invalid edge id");
397 mk.Add(distance, edge);
398 }
399 mk.Build();
400
401 if (!mk.IsDone()) throw std::runtime_error(_u8L("apply_chamfer: OCCT chamfer failed"));
402 return mk.Shape();
403}
static TopoDS_Edge edge_by_index(const TopoDS_Shape &shape, int index)
Definition GeometryEngine.cpp:717
#define _u8L(s)
Definition I18N.hpp:11

References _u8L, and edge_by_index().

Here is the call graph for this function:

◆ apply_chamfer() [2/3]

TopoDS_Shape Slic3r::GeometryEngine::apply_chamfer ( const TopoDS_Shape & solid,
double distance,
FaceGroup faces = FaceGroup::All )
static
346{
347 if (distance <= 0.001) throw std::runtime_error(_u8L("the chamfer distance must be greater than 0"));
348
349 std::vector<TopoDS_Edge> edges = collect_edges(solid, faces);
350 if (edges.empty()) throw std::runtime_error(_u8L("no edges to chamfer in that group"));
351
352 BRepFilletAPI_MakeChamfer chamfer(solid);
353 for (const auto& edge : edges)
354 chamfer.Add(distance, edge); // symmetric chamfer
355 chamfer.Build();
356
357 if (!chamfer.IsDone()) throw std::runtime_error(_u8L("chamfer distance too large for this geometry"));
358 return chamfer.Shape();
359}
static std::vector< TopoDS_Edge > collect_edges(const TopoDS_Shape &solid, FaceGroup faces)
Definition GeometryEngine.cpp:272

References _u8L, and collect_edges().

Referenced by apply_chamfer(), and Slic3r::CadDocument::apply_feature().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ apply_chamfer() [3/3]

TopoDS_Shape Slic3r::GeometryEngine::apply_chamfer ( const TopoDS_Shape & solid,
double distance,
int edge_id )
static
367{
368 return apply_chamfer(solid, distance, std::vector<int>{ edge_id });
369}
static TopoDS_Shape apply_chamfer(const TopoDS_Shape &solid, double distance, FaceGroup faces=FaceGroup::All)
Definition GeometryEngine.cpp:345

References apply_chamfer().

Here is the call graph for this function:

◆ apply_fillet() [1/3]

TopoDS_Shape Slic3r::GeometryEngine::apply_fillet ( const TopoDS_Shape & solid,
double radius,
const std::vector< int > & edge_ids )
static
372{
373 if (radius <= 0.001) throw std::runtime_error(_u8L("the fillet radius must be greater than 0"));
374 if (edge_ids.empty()) throw std::runtime_error("apply_fillet: no edge picked");
375
376 BRepFilletAPI_MakeFillet mk(solid);
377 for (int id : edge_ids) {
378 TopoDS_Edge edge = edge_by_index(solid, id);
379 if (edge.IsNull()) throw std::runtime_error("apply_fillet: invalid edge id");
380 mk.Add(radius, edge);
381 }
382 mk.Build();
383
384 if (!mk.IsDone()) throw std::runtime_error(_u8L("apply_fillet: OCCT fillet failed"));
385 return mk.Shape();
386}

References _u8L, and edge_by_index().

Here is the call graph for this function:

◆ apply_fillet() [2/3]

TopoDS_Shape Slic3r::GeometryEngine::apply_fillet ( const TopoDS_Shape & solid,
double radius,
FaceGroup faces = FaceGroup::All )
static
327{
328 if (radius <= 0.001) throw std::runtime_error(_u8L("the fillet radius must be greater than 0"));
329
330 std::vector<TopoDS_Edge> edges = collect_edges(solid, faces);
331 if (edges.empty()) throw std::runtime_error(_u8L("no edges to fillet in that group"));
332
333 BRepFilletAPI_MakeFillet fillet(solid);
334 for (const auto& edge : edges)
335 fillet.Add(radius, edge);
336 fillet.Build();
337
338 // A too-large radius (e.g. >= half the smallest spanned dimension) makes the
339 // operation degenerate; OCCT leaves IsDone() false. Report it instead of
340 // silently returning the unfilleted solid (which reads as a false success).
341 if (!fillet.IsDone()) throw std::runtime_error(_u8L("fillet radius too large for this geometry"));
342 return fillet.Shape();
343}

References _u8L, and collect_edges().

Referenced by Slic3r::CadDocument::apply_feature(), and apply_fillet().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ apply_fillet() [3/3]

TopoDS_Shape Slic3r::GeometryEngine::apply_fillet ( const TopoDS_Shape & solid,
double radius,
int edge_id )
static
362{
363 return apply_fillet(solid, radius, std::vector<int>{ edge_id });
364}
static TopoDS_Shape apply_fillet(const TopoDS_Shape &solid, double radius, FaceGroup faces=FaceGroup::All)
Definition GeometryEngine.cpp:326

References apply_fillet().

Here is the call graph for this function:

◆ circle_of_edge()

GeometryEngine::CylinderFace Slic3r::GeometryEngine::circle_of_edge ( const TopoDS_Edge & edge)
static
676{
677 CylinderFace cf;
678 if (edge.IsNull()) return cf;
679 BRepAdaptor_Curve curve(edge);
680 if (curve.GetType() != GeomAbs_Circle) return cf;
681 const gp_Circ c = curve.Circle();
682 const gp_Ax1 ax = c.Axis();
683 cf.base = Vec3d(c.Location().X(), c.Location().Y(), c.Location().Z());
684 cf.axis = Vec3d(ax.Direction().X(), ax.Direction().Y(), ax.Direction().Z());
685 cf.radius = c.Radius();
686 cf.height = 0.0; // an edge carries no axial extent; the card keeps the current length
687 cf.internal = false; // ambiguous from an edge alone — default external, user can toggle
688 cf.ok = true;
689 return cf;
690}
Definition GeometryEngine.hpp:127
Eigen::Matrix< double, 3, 1, Eigen::DontAlign > Vec3d
Definition Point.hpp:68

References Slic3r::GeometryEngine::CylinderFace::axis, Slic3r::GeometryEngine::CylinderFace::base, Slic3r::GeometryEngine::CylinderFace::height, Slic3r::GeometryEngine::CylinderFace::internal, Slic3r::GeometryEngine::CylinderFace::ok, and Slic3r::GeometryEngine::CylinderFace::radius.

Referenced by Slic3r::GUI::DesignPanel::DesignPanel(), Slic3r::GUI::DesignPanel::offer_selection_kind(), and Slic3r::GUI::anonymous_namespace{McpControl.cpp}::query_topology().

Here is the caller graph for this function:

◆ classify_face()

FaceGroup Slic3r::GeometryEngine::classify_face ( const TopoDS_Face & face,
const TopoDS_Shape & solid )
staticprivate
250{
251 try {
252 BRepAdaptor_Surface surf(face);
253 if (surf.GetType() == GeomAbs_Plane) {
254 // Sample normal at center UV
255 double u = (surf.FirstUParameter() + surf.LastUParameter()) / 2.0;
256 double v = (surf.FirstVParameter() + surf.LastVParameter()) / 2.0;
257 gp_Pnt pt; gp_Vec du, dv;
258 surf.D1(u, v, pt, du, dv);
259 gp_Dir n = du.Crossed(dv);
260 if (face.Orientation() == TopAbs_REVERSED) n.Reverse();
261
262 if (n.Z() > 0.7) return FaceGroup::Top;
263 if (n.Z() < -0.7) return FaceGroup::Bottom;
264 return FaceGroup::Lateral;
265 }
266 } catch (...) {}
267 return FaceGroup::Lateral;
268}
@ Lateral
Definition GeometryEngine.hpp:16
@ Bottom
Definition GeometryEngine.hpp:16
@ Top
Definition GeometryEngine.hpp:16

References Slic3r::Bottom, Slic3r::Lateral, and Slic3r::Top.

Referenced by collect_edges().

Here is the caller graph for this function:

◆ collect_edges()

std::vector< TopoDS_Edge > Slic3r::GeometryEngine::collect_edges ( const TopoDS_Shape & solid,
FaceGroup faces )
staticprivate
273{
274 // Each edge ONCE. An explorer visits a shared edge from both of its faces, so walking one
275 // handed every edge to the fillet twice; the de-duplicated map is also what edge_by_index
276 // numbers edges by.
277 std::vector<TopoDS_Edge> result;
278 NCollection_IndexedDataMap<TopoDS_Shape, NCollection_List<TopoDS_Shape>, TopTools_ShapeMapHasher> edgeFaceMap;
279 TopExp::MapShapesAndAncestors(solid, TopAbs_EDGE, TopAbs_FACE, edgeFaceMap);
280 if (target == FaceGroup::All) {
281 for (int i = 1; i <= edgeFaceMap.Extent(); ++i)
282 result.push_back(TopoDS::Edge(edgeFaceMap.FindKey(i)));
283 return result;
284 }
285
286 for (int ei = 1; ei <= edgeFaceMap.Extent(); ++ei) {
287 const TopoDS_Edge& edge = TopoDS::Edge(edgeFaceMap.FindKey(ei));
288 const NCollection_List<TopoDS_Shape>& faces = edgeFaceMap.FindFromIndex(ei);
289
290 bool include = false;
291 for (auto it = faces.begin(); it != faces.end(); ++it) {
292 FaceGroup fg = classify_face(TopoDS::Face(*it), solid);
293 if (target == FaceGroup::Top && fg == FaceGroup::Top) { include = true; break; }
294 if (target == FaceGroup::Bottom && fg == FaceGroup::Bottom) { include = true; break; }
295 if (target == FaceGroup::Lateral && fg == FaceGroup::Lateral) { include = true; break; }
296 }
297
298 if (!include && target == FaceGroup::Top) {
299 for (auto it = faces.begin(); it != faces.end(); ++it) {
300 if (classify_face(TopoDS::Face(*it), solid) == FaceGroup::Top) { include = true; break; }
301 }
302 }
303 if (!include && target == FaceGroup::Bottom) {
304 for (auto it = faces.begin(); it != faces.end(); ++it) {
305 if (classify_face(TopoDS::Face(*it), solid) == FaceGroup::Bottom) { include = true; break; }
306 }
307 }
308 if (target == FaceGroup::Lateral && !include) {
309 int lateralCount = 0;
310 for (auto it = faces.begin(); it != faces.end(); ++it) {
311 if (classify_face(TopoDS::Face(*it), solid) == FaceGroup::Lateral) ++lateralCount;
312 }
313 if (lateralCount >= 2) include = true;
314 }
315
316 if (include) result.push_back(edge);
317 }
318
319 return result;
320}
static FaceGroup classify_face(const TopoDS_Face &face, const TopoDS_Shape &solid)
Definition GeometryEngine.cpp:249
FaceGroup
Definition GeometryEngine.hpp:16
@ All
Definition GeometryEngine.hpp:16

References Slic3r::All, Slic3r::Bottom, classify_face(), Slic3r::Lateral, and Slic3r::Top.

Referenced by apply_chamfer(), and apply_fillet().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ cylinder_of_face()

GeometryEngine::CylinderFace Slic3r::GeometryEngine::cylinder_of_face ( const TopoDS_Face & face)
static
640{
641 CylinderFace cf;
642 if (face.IsNull()) return cf;
643 BRepAdaptor_Surface surf(face);
644 if (surf.GetType() != GeomAbs_Cylinder) return cf;
645
646 const gp_Cylinder cyl = surf.Cylinder();
647 const gp_Ax1 ax = cyl.Axis();
648 const Vec3d axis(ax.Direction().X(), ax.Direction().Y(), ax.Direction().Z());
649 const Vec3d apt (ax.Location().X(), ax.Location().Y(), ax.Location().Z());
650 cf.radius = cyl.Radius();
651
652 // Axial extent: V is the axial parameter on a cylinder; bound the face's two ends and
653 // order them so `axis` points base -> top.
654 const double umid = 0.5 * (surf.FirstUParameter() + surf.LastUParameter());
655 const gp_Pnt e0 = surf.Value(umid, surf.FirstVParameter());
656 const gp_Pnt e1 = surf.Value(umid, surf.LastVParameter());
657 double t0 = (Vec3d(e0.X(), e0.Y(), e0.Z()) - apt).dot(axis);
658 double t1 = (Vec3d(e1.X(), e1.Y(), e1.Z()) - apt).dot(axis);
659 if (t1 < t0) std::swap(t0, t1);
660 cf.base = apt + axis * t0;
661 cf.axis = axis;
662 cf.height = t1 - t0;
663
664 // Internal (bore) vs external: compare the face's outward normal at its centre to the
665 // outward radial direction. A bore's normal points toward the axis (dot < 0).
666 const gp_Pnt sp = surf.Value(umid, 0.5 * (surf.FirstVParameter() + surf.LastVParameter()));
667 const Vec3d S(sp.X(), sp.Y(), sp.Z());
668 const Vec3d axpt = cf.base + axis * (S - cf.base).dot(axis);
669 const Vec3d radial = (S - axpt).normalized();
670 cf.internal = face_normal_world(face).dot(radial) < 0.0;
671 cf.ok = true;
672 return cf;
673}
static Vec3d face_normal_world(const TopoDS_Face &face)
Definition GeometryEngine.cpp:627
T dot(const boost::geometry::model::d2::point_xy< T > &v1, const boost::geometry::model::d2::point_xy< T > &v2)
Definition ExtrusionSimulator.cpp:154

References Slic3r::GeometryEngine::CylinderFace::axis, Slic3r::GeometryEngine::CylinderFace::base, Slic3r::dot(), face_normal_world(), Slic3r::GeometryEngine::CylinderFace::height, Slic3r::GeometryEngine::CylinderFace::internal, Slic3r::GeometryEngine::CylinderFace::ok, and Slic3r::GeometryEngine::CylinderFace::radius.

Referenced by Slic3r::GUI::DesignPanel::DesignPanel(), Slic3r::GUI::DesignPanel::offer_selection_kind(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::query_topology(), Slic3r::CadDocument::resolve_datum_axes(), and Slic3r::CadDocument::resolve_datum_planes().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ display_edges()

std::vector< std::vector< Vec3d > > Slic3r::GeometryEngine::display_edges ( const TopoDS_Shape & shape,
double chord_tol )
static
599{
600 NCollection_IndexedDataMap<TopoDS_Shape, NCollection_List<TopoDS_Shape>, TopTools_ShapeMapHasher> faces_of_edge;
601 TopExp::MapShapesAndAncestors(shape, TopAbs_EDGE, TopAbs_FACE, faces_of_edge);
602 std::vector<std::vector<Vec3d>> out;
603 for (int i = 1; i <= faces_of_edge.Extent(); ++i) {
604 const TopoDS_Edge& edge = TopoDS::Edge(faces_of_edge.FindKey(i));
605 if (BRep_Tool::Degenerated(edge))
606 continue;
607 bool seam = false;
608 for (NCollection_List<TopoDS_Shape>::Iterator it(faces_of_edge.FindFromIndex(i)); it.More() && !seam; it.Next())
609 seam = BRep_Tool::IsClosed(edge, TopoDS::Face(it.Value()));
610 if (seam)
611 continue;
612 std::vector<Vec3d> pts = sample_edge_world(edge, chord_tol);
613 if (pts.size() >= 2)
614 out.push_back(std::move(pts));
615 }
616 return out;
617}
static std::vector< Vec3d > sample_edge_world(const TopoDS_Edge &edge, double chord_tol=0.05)
Definition GeometryEngine.cpp:575

References sample_edge_world().

Referenced by Slic3r::GUI::DesignSketchTool::make_face_highlight(), and Slic3r::GUI::DesignSketchTool::refresh_body_edges().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ edge_by_index()

TopoDS_Edge Slic3r::GeometryEngine::edge_by_index ( const TopoDS_Shape & shape,
int index )
static
718{
719 ShapeIndexMap map;
720 TopExp::MapShapes(shape, TopAbs_EDGE, map);
721 if (index < 0 || index >= map.Extent())
722 return TopoDS_Edge();
723 return TopoDS::Edge(map(index + 1));
724}
NCollection_IndexedMap< TopoDS_Shape, TopTools_ShapeMapHasher > ShapeIndexMap
Definition GeometryEngine.cpp:67

Referenced by Slic3r::GUI::DesignPanel::DesignPanel(), apply_chamfer(), apply_fillet(), Slic3r::CadDocument::apply_project(), Slic3r::CadDocument::datum_frame(), Slic3r::GUI::DesignPanel::offer_selection_kind(), Slic3r::CadDocument::project_edges_into_sketch(), Slic3r::CadDocument::resolve_datum_axes(), Slic3r::CadDocument::resolve_datum_planes(), and Slic3r::GUI::anonymous_namespace{McpControl.cpp}::resolve_ref().

Here is the caller graph for this function:

◆ edge_count()

int Slic3r::GeometryEngine::edge_count ( const TopoDS_Shape & shape)
static
711{
712 ShapeIndexMap map;
713 TopExp::MapShapes(shape, TopAbs_EDGE, map);
714 return map.Extent();
715}

◆ edge_index_of()

int Slic3r::GeometryEngine::edge_index_of ( const TopoDS_Shape & shape,
const TopoDS_Edge & edge )
static
727{
728 ShapeIndexMap map;
729 TopExp::MapShapes(shape, TopAbs_EDGE, map);
730 int idx = map.FindIndex(edge);
731 return (idx > 0) ? (idx - 1) : -1;
732}

Referenced by Slic3r::GUI::DesignSketchTool::resolve_solid_pick().

Here is the caller graph for this function:

◆ edges_of()

std::vector< TopoDS_Edge > Slic3r::GeometryEngine::edges_of ( const TopoDS_Shape & shape)
static
555{
556 ShapeIndexMap map;
557 TopExp::MapShapes(shape, TopAbs_EDGE, map); // same order as edge_by_index
558 std::vector<TopoDS_Edge> out;
559 out.reserve(map.Extent());
560 for (int i = 1; i <= map.Extent(); ++i)
561 out.push_back(TopoDS::Edge(map(i)));
562 return out;
563}

Referenced by Slic3r::CadDocument::apply_project(), Slic3r::CadDocument::project_edges_into_sketch(), and Slic3r::GUI::anonymous_namespace{McpControl.cpp}::query_topology().

Here is the caller graph for this function:

◆ edges_of_face()

std::vector< TopoDS_Edge > Slic3r::GeometryEngine::edges_of_face ( const TopoDS_Face & face)
static
566{
567 std::vector<TopoDS_Edge> result;
568 ShapeIndexMap map;
569 TopExp::MapShapes(face, TopAbs_EDGE, map);
570 for (int i = 1; i <= map.Extent(); ++i)
571 result.push_back(TopoDS::Edge(map(i)));
572 return result;
573}

Referenced by Slic3r::CadDocument::apply_project(), Slic3r::CadDocument::datum_frame(), Slic3r::GUI::face_plane_inward(), Slic3r::CadDocument::project_edges_into_sketch(), Slic3r::CadDocument::recompute(), and Slic3r::GUI::DesignSketchTool::resolve_solid_pick().

Here is the caller graph for this function:

◆ face_by_index()

◆ face_centroid_world()

Vec3d Slic3r::GeometryEngine::face_centroid_world ( const TopoDS_Face & face)
static

◆ face_count()

int Slic3r::GeometryEngine::face_count ( const TopoDS_Shape & shape)
static
527{
528 int n = 0;
529 for (TopExp_Explorer e(shape, TopAbs_FACE); e.More(); e.Next())
530 ++n;
531 return n;
532}

Referenced by Slic3r::CadDocument::faces_made_by(), mesh_to_brep(), and Slic3r::GUI::DesignSketchTool::picked_faces().

Here is the caller graph for this function:

◆ face_normal_world()

Vec3d Slic3r::GeometryEngine::face_normal_world ( const TopoDS_Face & face)
static
628{
629 BRepAdaptor_Surface surf(face);
630 const double u = 0.5 * (surf.FirstUParameter() + surf.LastUParameter());
631 const double v = 0.5 * (surf.FirstVParameter() + surf.LastVParameter());
632 BRepLProp_SLProps props(surf, u, v, 1, 1e-6);
633 gp_Dir n(0.0, 0.0, 1.0);
634 if (props.IsNormalDefined()) n = props.Normal();
635 if (face.Orientation() == TopAbs_REVERSED) n.Reverse(); // outward (account for face winding)
636 return Vec3d(n.X(), n.Y(), n.Z());
637}

Referenced by cylinder_of_face(), Slic3r::CadDocument::datum_frame(), Slic3r::GUI::face_plane_inward(), Slic3r::GUI::DesignPanel::place_on_face(), Slic3r::CadDocument::plane_of_face(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::query_topology(), Slic3r::CadDocument::resolve_datum_axes(), Slic3r::CadDocument::resolve_datum_planes(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::resolve_ref(), Slic3r::GUI::DesignPanel::update_draft_gizmo(), Slic3r::GUI::DesignPanel::update_extrude_gizmo(), and Slic3r::GUI::DesignPanel::update_shell_gizmo().

Here is the caller graph for this function:

◆ face_plane_bounds()

bool Slic3r::GeometryEngine::face_plane_bounds ( const TopoDS_Face & face,
const Vec3d & origin,
const Vec3d & x_axis,
const Vec3d & y_axis,
double & umin,
double & umax,
double & vmin,
double & vmax )
static
695{
696 umin = vmin = 1e30; umax = vmax = -1e30;
697 bool any = false;
698 for (TopExp_Explorer ex(face, TopAbs_VERTEX); ex.More(); ex.Next()) {
699 const gp_Pnt p = BRep_Tool::Pnt(TopoDS::Vertex(ex.Current()));
700 const Vec3d P(p.X(), p.Y(), p.Z());
701 const double u = (P - origin).dot(x_axis);
702 const double v = (P - origin).dot(y_axis);
703 umin = std::min(umin, u); umax = std::max(umax, u);
704 vmin = std::min(vmin, v); vmax = std::max(vmax, v);
705 any = true;
706 }
707 return any;
708}
@ P
Definition libslic3r.h:149

References Slic3r::dot(), and Slic3r::P.

Referenced by Slic3r::GUI::DesignPanel::DesignPanel().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ faces_of()

std::vector< TopoDS_Face > Slic3r::GeometryEngine::faces_of ( const TopoDS_Shape & shape)
static
547{
548 std::vector<TopoDS_Face> out;
549 for (TopExp_Explorer e(shape, TopAbs_FACE); e.More(); e.Next())
550 out.push_back(TopoDS::Face(e.Current())); // same order as face_by_index
551 return out;
552}

Referenced by Slic3r::GUI::DesignSketchTool::make_face_highlight(), and Slic3r::GUI::anonymous_namespace{McpControl.cpp}::query_topology().

Here is the caller graph for this function:

◆ mass_properties()

GeometryEngine::MassProps Slic3r::GeometryEngine::mass_properties ( const TopoDS_Shape & shape)
static
483{
484 MassProps p;
485 if (shape.IsNull()) return p;
486 try {
487 // A sheet body (open shell, no solid) encloses nothing, and BRepGProp::VolumeProperties
488 // integrates the divergence theorem over whatever faces exist — on an open shell that is
489 // not a volume at all. It came back as 96000 with an inertia diagonal of
490 // [-4.2e7, -4.2e7, -6.9e7] for a 60x60x40 four-walled box: negative principal moments,
491 // which no real body can have. The old code then hid the only obvious tell by taking
492 // std::abs() of the mass. Report the honest answer instead — surface area is still
493 // meaningful, so this is not a failure, just not a solid.
494 p.is_solid = TopExp_Explorer(shape, TopAbs_SOLID).More();
495 if (!p.is_solid) {
496 GProp_GProps sonly;
497 BRepGProp::SurfaceProperties(shape, sonly);
498 p.surface_area = sonly.Mass();
499 p.valid = true; // the area IS trustworthy; volume/inertia stay zero
500 return p;
501 }
502 GProp_GProps vprops;
503 BRepGProp::VolumeProperties(shape, vprops);
504 double mass = vprops.Mass();
505 if (std::abs(mass) < 1e-30) return p;
506 p.volume = std::abs(mass);
507 p.center_of_mass = Vec3d(vprops.CentreOfMass().X(), vprops.CentreOfMass().Y(), vprops.CentreOfMass().Z());
508 gp_Mat mat = vprops.MatrixOfInertia();
509 p.inertia = {{
510 mat(1,1), mat(1,2), mat(1,3),
511 mat(2,1), mat(2,2), mat(2,3),
512 mat(3,1), mat(3,2), mat(3,3),
513 }};
514 GProp_GProps sprops;
515 BRepGProp::SurfaceProperties(shape, sprops);
516 p.surface_area = sprops.Mass();
517 p.valid = true;
518 } catch (const Standard_Failure&) {
519 // leave valid = false
520 }
521 return p;
522}
Definition GeometryEngine.hpp:61

References Slic3r::GeometryEngine::MassProps::center_of_mass, Slic3r::GeometryEngine::MassProps::inertia, Slic3r::GeometryEngine::MassProps::is_solid, Slic3r::GeometryEngine::MassProps::surface_area, Slic3r::GeometryEngine::MassProps::valid, and Slic3r::GeometryEngine::MassProps::volume.

Referenced by Slic3r::CadDocument::body_mass_properties(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::mass_properties(), and Slic3r::GUI::DesignPanel::on_mass_properties().

Here is the caller graph for this function:

◆ mesh_to_brep()

TopoDS_Shape Slic3r::GeometryEngine::mesh_to_brep ( const indexed_triangle_set & its,
double tolerance,
double merge_angle_deg,
MeshBrepStats & stats )
static
113{
114 stats = MeshBrepStats{};
115 stats.input_tris = int(its.indices.size());
116 if (tolerance <= 0.0)
117 throw std::runtime_error("mesh_to_brep: tolerance must be > 0");
118 if (its.indices.empty())
119 throw std::runtime_error(_u8L("mesh_to_brep: mesh has no triangles"));
120
121 // 1. Tolerance-quantized vertex dedup. A merged vertex keeps the exact coordinates of the
122 // first input occurrence — vertices are grouped by a cell, never snapped onto its grid.
123 std::map<std::array<long long, 3>, int> cell_to_new;
124 std::vector<int> old_to_new(its.vertices.size(), -1);
125 std::vector<Vec3d> verts;
126 verts.reserve(its.vertices.size());
127 for (size_t i = 0; i < its.vertices.size(); ++i) {
128 const Vec3d p = its.vertices[i].cast<double>();
129 const std::array<long long, 3> cell{ (long long) std::llround(p.x() / tolerance),
130 (long long) std::llround(p.y() / tolerance),
131 (long long) std::llround(p.z() / tolerance) };
132 auto ins = cell_to_new.emplace(cell, int(verts.size()));
133 if (ins.second)
134 verts.push_back(p);
135 old_to_new[i] = ins.first->second;
136 }
137
138 // 2. Reject degenerate triangles (see the two-part rule in the comment above).
139 std::vector<Vec3i32> tris;
140 tris.reserve(its.indices.size());
141 for (const Vec3i32& t : its.indices) {
142 const int a = old_to_new[t(0)], b = old_to_new[t(1)], c = old_to_new[t(2)];
143 if (a == b || b == c || a == c) { ++stats.degenerate_collapsed; continue; }
144 const Vec3d& pa = verts[a]; const Vec3d& pb = verts[b]; const Vec3d& pc = verts[c];
145 const double e0 = (pb - pa).norm(), e1 = (pc - pb).norm(), e2 = (pa - pc).norm();
146 const double longest = std::max(e0, std::max(e1, e2));
147 if (longest < tolerance) { ++stats.degenerate_collapsed; continue; }
148 const double area = 0.5 * (pb - pa).cross(pc - pa).norm();
149 if (area < 1e-9 * longest * longest) { ++stats.degenerate_sliver; continue; }
150 tris.emplace_back(a, b, c);
151 }
152 stats.kept_tris = int(tris.size());
153 if (tris.empty())
154 throw std::runtime_error(_u8L("mesh_to_brep: every triangle was rejected as degenerate "
155 "(try a smaller tolerance)"));
156
157 // 3. One face per triangle, sharing vertices and edges through the caches.
158 std::vector<TopoDS_Vertex> vertex_cache(verts.size());
159 std::vector<bool> vertex_made(verts.size(), false);
160 auto get_vertex = [&](int i) -> const TopoDS_Vertex& {
161 if (!vertex_made[i]) {
162 const Vec3d& p = verts[i];
163 vertex_cache[i] = BRepBuilderAPI_MakeVertex(gp_Pnt(p.x(), p.y(), p.z())).Vertex();
164 vertex_made[i] = true;
165 }
166 return vertex_cache[i];
167 };
168
169 std::map<std::pair<int, int>, TopoDS_Edge> edge_cache;
170 std::map<std::pair<int, int>, int> edge_usage;
171 auto get_edge = [&](int i, int j) -> TopoDS_Edge {
172 const std::pair<int, int> key = (i < j) ? std::make_pair(i, j) : std::make_pair(j, i);
173 ++edge_usage[key];
174 auto it = edge_cache.find(key);
175 if (it == edge_cache.end())
176 it = edge_cache.emplace(key,
177 BRepBuilderAPI_MakeEdge(get_vertex(key.first), get_vertex(key.second)).Edge()).first;
178 return (i > j) ? TopoDS::Edge(it->second.Reversed()) : it->second;
179 };
180
181 BRep_Builder builder;
182 TopoDS_Shell shell;
183 builder.MakeShell(shell);
184
185 for (const Vec3i32& t : tris) {
186 try {
187 BRepBuilderAPI_MakeWire mk_wire(get_edge(t(0), t(1)), get_edge(t(1), t(2)), get_edge(t(2), t(0)));
188 if (!mk_wire.IsDone()) { ++stats.faces_failed; continue; }
189 BRepBuilderAPI_MakeFace mk_face(mk_wire.Wire());
190 if (!mk_face.IsDone()) { ++stats.faces_failed; continue; }
191 builder.Add(shell, mk_face.Face());
192 ++stats.faces_built;
193 } catch (const Standard_Failure&) {
194 ++stats.faces_failed;
195 }
196 }
197
198 // 4. Watertightness falls straight out of the usage counts the cache already gathered.
199 for (const auto& kv : edge_usage) {
200 if (kv.second == 1) ++stats.boundary_edges;
201 else if (kv.second >= 3) ++stats.nonmanifold_edges;
202 }
203 stats.unique_edges = int(edge_usage.size());
204 stats.watertight = stats.boundary_edges == 0 && stats.nonmanifold_edges == 0 && stats.unique_edges > 0;
205
206 TopoDS_Shape shape = shell;
207 if (stats.watertight && stats.faces_built > 0) {
208 BRepBuilderAPI_MakeSolid mk_solid(shell);
209 if (mk_solid.IsDone()) {
210 TopoDS_Solid solid = mk_solid.Solid();
211 GProp_GProps props;
212 BRepGProp::VolumeProperties(solid, props);
213 double vol = props.Mass();
214 if (vol < 0.0) { // inward-wound input
215 solid = TopoDS::Solid(solid.Reversed());
216 vol = -vol;
217 }
218 if (vol > 0.0) {
219 shape = solid;
220 stats.is_solid = true;
221 stats.volume = vol;
222 }
223 }
224 }
225
226 // 5. Optional coplanar merge. Faceted output is one planar face per triangle — exact, but
227 // you cannot meaningfully fillet or extrude a face that IS a single triangle. Merging
228 // coplanar neighbours is what turns the import into something the face/edge tools can
229 // actually operate on (a 12-triangle cube collapses to its 6 real faces).
230 if (merge_angle_deg > 0.0) {
231 try {
232 ShapeUpgrade_UnifySameDomain unifier(shape, true, true, true);
233 unifier.SetAngularTolerance(merge_angle_deg * M_PI / 180.0);
234 unifier.SetLinearTolerance(tolerance);
235 unifier.Build();
236 const TopoDS_Shape merged = unifier.Shape();
237 if (!merged.IsNull())
238 shape = merged;
239 } catch (const Standard_Failure&) {
240 // Merging is an optimisation, not a correctness step: keep the exact faceted shape.
241 }
242 }
243 stats.faces_final = face_count(shape);
244 return shape;
245}
#define M_PI
Definition ExtrusionSimulator.cpp:31
static int face_count(const TopoDS_Shape &shape)
Definition GeometryEngine.cpp:526
int input_tris
Definition GeometryEngine.hpp:35
Definition GeometryEngine.hpp:34
double area(const ExPolygon &poly)
Definition ExPolygon.hpp:511
T cross(const boost::geometry::model::d2::point_xy< T > &v1, const boost::geometry::model::d2::point_xy< T > &v2)
Definition ExtrusionSimulator.cpp:168
Eigen::Matrix< int32_t, 3, 1, Eigen::DontAlign > Vec3i32
Definition Point.hpp:59

References _u8L, Slic3r::area(), Slic3r::GeometryEngine::MeshBrepStats::boundary_edges, Slic3r::cross(), Slic3r::GeometryEngine::MeshBrepStats::degenerate_collapsed, Slic3r::GeometryEngine::MeshBrepStats::degenerate_sliver, face_count(), Slic3r::GeometryEngine::MeshBrepStats::faces_built, Slic3r::GeometryEngine::MeshBrepStats::faces_failed, Slic3r::GeometryEngine::MeshBrepStats::faces_final, Slic3r::GeometryEngine::MeshBrepStats::input_tris, Slic3r::GeometryEngine::MeshBrepStats::is_solid, Slic3r::GeometryEngine::MeshBrepStats::kept_tris, M_PI, Slic3r::GeometryEngine::MeshBrepStats::nonmanifold_edges, Slic3r::GeometryEngine::MeshBrepStats::unique_edges, Slic3r::GeometryEngine::MeshBrepStats::volume, and Slic3r::GeometryEngine::MeshBrepStats::watertight.

Referenced by Slic3r::GUI::anonymous_namespace{McpControl.cpp}::import_mesh(), and Slic3r::GUI::DesignPanel::on_import_mesh().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ read_step_solids()

std::vector< TopoDS_Shape > Slic3r::GeometryEngine::read_step_solids ( const std::string & path,
std::string & err )
static
71{
72 err.clear();
73 std::vector<TopoDS_Shape> out;
74 try {
75 STEPControl_Reader reader;
76 if (reader.ReadFile(path.c_str()) != IFSelect_RetDone) {
77 err = _u8L("cannot read STEP file");
78 return out;
79 }
80 reader.TransferRoots();
81 const TopoDS_Shape shape = reader.OneShape();
82 if (shape.IsNull()) { err = _u8L("STEP file has no geometry"); return out; }
83 // One body per top-level solid; fall back to the whole shape (shells/faces) if none.
84 for (TopExp_Explorer ex(shape, TopAbs_SOLID); ex.More(); ex.Next())
85 out.push_back(ex.Current());
86 if (out.empty())
87 out.push_back(shape);
88 } catch (const Standard_Failure& e) {
89 err = *e.what() ? e.what() : _u8L("OCCT failed to read STEP");
90 out.clear();
91 }
92 return out;
93}

References _u8L.

Referenced by Slic3r::GUI::anonymous_namespace{McpControl.cpp}::import_step(), Slic3r::GUI::DesignPanel::on_import_step(), and Slic3r::GUI::anonymous_namespace{McpControl.cpp}::validate_against().

Here is the caller graph for this function:

◆ sample_edge_world()

std::vector< Vec3d > Slic3r::GeometryEngine::sample_edge_world ( const TopoDS_Edge & edge,
double chord_tol = 0.05 )
static
576{
577 if (BRep_Tool::Degenerated(edge))
578 return {};
579
580 BRepAdaptor_Curve curve(edge);
581 GCPnts_TangentialDeflection disc(curve, 0.1, chord_tol);
582
583 std::vector<Vec3d> pts;
584 if (disc.NbPoints() >= 2) {
585 for (int i = 1; i <= disc.NbPoints(); ++i) {
586 gp_Pnt p = disc.Value(i);
587 pts.emplace_back(p.X(), p.Y(), p.Z());
588 }
589 } else {
590 gp_Pnt p0 = curve.Value(curve.FirstParameter());
591 gp_Pnt p1 = curve.Value(curve.LastParameter());
592 pts.emplace_back(p0.X(), p0.Y(), p0.Z());
593 pts.emplace_back(p1.X(), p1.Y(), p1.Z());
594 }
595 return pts;
596}

Referenced by Slic3r::CadDocument::datum_frame(), display_edges(), Slic3r::GUI::face_plane_inward(), Slic3r::project_edges_to_entities(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::query_topology(), Slic3r::CadDocument::resolve_datum_axes(), Slic3r::CadDocument::resolve_datum_planes(), Slic3r::GUI::anonymous_namespace{McpControl.cpp}::resolve_ref(), Slic3r::GUI::DesignSketchTool::resolve_solid_pick(), and Slic3r::GUI::anonymous_namespace{McpControl.cpp}::slice_body().

Here is the caller graph for this function:

◆ surface_deviation()

GeometryEngine::Deviation Slic3r::GeometryEngine::surface_deviation ( const TopoDS_Shape & candidate,
const TopoDS_Shape & reference,
double linear_deflection = 0.5 )
static
461{
462 Deviation d;
463 if (candidate.IsNull() || reference.IsNull()) return d;
464 TriangleMesh mesh = tessellate(candidate, linear_deflection, 0.5);
465 const auto& verts = mesh.its.vertices;
466 if (verts.empty()) return d;
467 double sum = 0.0, sumsq = 0.0;
468 int n = 0;
469 for (const auto& v : verts) {
470 gp_Pnt p(v.x(), v.y(), v.z());
471 BRepExtrema_DistShapeShape dss(BRepBuilderAPI_MakeVertex(p).Vertex(), reference);
472 if (!dss.IsDone() || dss.NbSolution() < 1) continue;
473 double dist = dss.Value();
474 d.max_mm = std::max(d.max_mm, dist);
475 sum += dist; sumsq += dist * dist; ++n;
476 }
477 d.sample_count = n;
478 if (n > 0) { d.mean_mm = sum / n; d.rms_mm = std::sqrt(sumsq / n); }
479 return d;
480}
static TriangleMesh tessellate(const TopoDS_Shape &shape, double linear_deflection=0.003, double angular_deflection=0.5)
Definition GeometryEngine.cpp:407
Definition GeometryEngine.hpp:73
T dist(const boost::polygon::point_data< T > &p1, const boost::polygon::point_data< T > &p2)
Definition Geometry.cpp:298
Mesh::Vertex_index Vertex
Definition MeshRepair.cpp:84

References Slic3r::Geometry::dist(), Slic3r::TriangleMesh::its, Slic3r::GeometryEngine::Deviation::max_mm, Slic3r::GeometryEngine::Deviation::mean_mm, Slic3r::GeometryEngine::Deviation::rms_mm, Slic3r::GeometryEngine::Deviation::sample_count, and tessellate().

Referenced by Slic3r::GUI::anonymous_namespace{McpControl.cpp}::validate_against().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ tessellate()

TriangleMesh Slic3r::GeometryEngine::tessellate ( const TopoDS_Shape & shape,
double linear_deflection = 0.003,
double angular_deflection = 0.5 )
static
410{
411 BRepMesh_IncrementalMesh mesh(shape, linear_deflection, false, angular_deflection, true);
412
413 int nbNodes = 0, nbTri = 0;
414 for (TopExp_Explorer exp(shape, TopAbs_FACE); exp.More(); exp.Next()) {
415 TopLoc_Location loc;
416 Handle(Poly_Triangulation) tri = BRep_Tool::Triangulation(TopoDS::Face(exp.Current()), loc);
417 if (!tri.IsNull()) { nbNodes += tri->NbNodes(); nbTri += tri->NbTriangles(); }
418 }
419 if (nbTri == 0 || nbNodes == 0) return TriangleMesh{};
420
421 stl_file stl;
422 stl.stats.type = inmemory;
423 stl.stats.number_of_facets = (uint32_t)nbTri;
424 stl.stats.original_num_facets = stl.stats.number_of_facets;
425 stl_allocate(&stl);
426
427 std::vector<Vec3f> pts; pts.reserve(nbNodes);
428 int ndOff = 0, trOff = 0;
429 for (TopExp_Explorer exp(shape, TopAbs_FACE); exp.More(); exp.Next()) {
430 const TopoDS_Shape& F = exp.Current();
431 TopLoc_Location loc;
432 Handle(Poly_Triangulation) tri = BRep_Tool::Triangulation(TopoDS::Face(F), loc);
433 if (tri.IsNull()) continue;
434 gp_Trsf T = loc.Transformation();
435 for (int i = 1; i <= tri->NbNodes(); ++i) {
436 gp_Pnt p = tri->Node(i); p.Transform(T);
437 pts.emplace_back(Vec3f(p.X(), p.Y(), p.Z()));
438 }
439 auto orient = exp.Current().Orientation();
440 int ids[3];
441 for (int i = 1; i <= tri->NbTriangles(); ++i) {
442 Poly_Triangle t = tri->Triangle(i); t.Get(ids[0], ids[1], ids[2]);
443 if (orient == TopAbs_REVERSED) std::swap(ids[1], ids[2]);
444 stl_facet f;
445 f.vertex[0] = pts[ids[0]+ndOff-1].cast<float>();
446 f.vertex[1] = pts[ids[1]+ndOff-1].cast<float>();
447 f.vertex[2] = pts[ids[2]+ndOff-1].cast<float>();
448 f.extra[0]=0; f.extra[1]=0;
449 stl_normal n; stl_calculate_normal(n,&f); stl_normalize_vector(n);
450 f.normal=n; stl.facet_start[trOff+i-1]=f;
451 }
452 ndOff += tri->NbNodes(); trOff += tri->NbTriangles();
453 }
454 TriangleMesh result; result.from_stl(stl); return result;
455}
static Orientation orient(const Point &a, const Point &b, const Point &c)
Definition Geometry.hpp:44
Eigen::Matrix< float, 3, 1, Eigen::DontAlign > Vec3f
Definition Point.hpp:65
static double f(double x, double z_sin, double z_cos, bool vertical, bool flip)
Definition FillGyroid.cpp:124
@ F
Definition libslic3r.h:145

References Slic3r::F, Slic3r::f(), Slic3r::TriangleMesh::from_stl(), Slic3r::TriangleMeshStats::number_of_facets, Slic3r::Geometry::orient(), and Slic3r::TriangleMesh::stats().

Referenced by surface_deviation().

Here is the call graph for this function:
Here is the caller graph for this function:

The documentation for this class was generated from the following files: