OrcaSlicer latest
OrcaSlicer
Loading...
Searching...
No Matches
Slic3r::Geometry::MedialAxis Class Reference

#include </home/runner/work/OrcaSlicer/OrcaSlicer/src/libslic3r/Geometry/MedialAxis.hpp>

Collaboration diagram for Slic3r::Geometry::MedialAxis:

Classes

struct  EdgeData

Public Member Functions

 MedialAxis (double min_width, double max_width, const ExPolygon &expolygon)
void build (ThickPolylines *polylines)
void build (Polylines *polylines)

Private Types

using VD = VoronoiDiagram

Private Member Functions

std::pair< EdgeData &, bool > edge_data (const VD::edge_type &edge)
void process_edge_neighbors (const VD::edge_type *edge, ThickPolyline *polyline)
bool validate_edge (const VD::edge_type *edge)

Private Attributes

const ExPolygon & m_expolygon
Lines m_lines
double m_min_width
double m_max_width
VD m_vd
std::vector< EdgeData > m_edge_data

Detailed Description


Class Documentation

◆ Slic3r::Geometry::MedialAxis::EdgeData

struct Slic3r::Geometry::MedialAxis::EdgeData
Class Members
bool active { false }
double width_end { 0 }
double width_start { 0 }

Member Typedef Documentation

◆ VD

Constructor & Destructor Documentation

◆ MedialAxis()

Slic3r::Geometry::MedialAxis::MedialAxis ( double min_width,
double max_width,
const ExPolygon & expolygon )
457 :
458 m_expolygon(expolygon), m_lines(expolygon.lines()), m_min_width(min_width), m_max_width(max_width)
459{}
const ExPolygon & m_expolygon
Definition MedialAxis.hpp:24
Lines m_lines
Definition MedialAxis.hpp:25
double m_min_width
Definition MedialAxis.hpp:27
double m_max_width
Definition MedialAxis.hpp:28

References m_expolygon, m_lines, m_max_width, and m_min_width.

Member Function Documentation

◆ build() [1/2]

void Slic3r::Geometry::MedialAxis::build ( Polylines * polylines)
560{
562 this->build(&tp);
563 polylines->reserve(polylines->size() + tp.size());
564 for (auto &pl : tp)
565 polylines->emplace_back(pl.points);
566}
void build(ThickPolylines *polylines)
Definition MedialAxis.cpp:461
std::vector< ThickPolyline > ThickPolylines
Definition Polyline.hpp:23

References build().

Here is the call graph for this function:

◆ build() [2/2]

void Slic3r::Geometry::MedialAxis::build ( ThickPolylines * polylines)
462{
463 m_vd.construct_voronoi(m_lines.begin(), m_lines.end());
464
465 // For several ExPolygons in SPE-1729, an invalid Voronoi diagram was produced that wasn't fixable by rotating input data.
466 // Those ExPolygons contain very thin lines and holes formed by very close (1-5nm) vertices that are on the edge of our resolution.
467 // Those thin lines and holes are both unprintable and cause the Voronoi diagram to be invalid.
468 // So we filter out such thin lines and holes and try to compute the Voronoi diagram again.
469 if (!m_vd.is_valid()) {
471 m_vd.construct_voronoi(m_lines.begin(), m_lines.end());
472
473 if (!m_vd.is_valid())
474 BOOST_LOG_TRIVIAL(error) << "MedialAxis - Invalid Voronoi diagram even after morphological closing.";
475 }
476
478// static constexpr double threshold_alpha = M_PI / 12.; // 30 degrees
479// std::vector<Vec2d> skeleton_edges = Slic3r::Voronoi::skeleton_edges_rough(vd, lines, threshold_alpha);
480
481 /*
482 // DEBUG: dump all Voronoi edges
483 {
484 for (VD::const_edge_iterator edge = m_vd.edges().begin(); edge != m_vd.edges().end(); ++edge) {
485 if (edge->is_infinite()) continue;
486
487 ThickPolyline polyline;
488 polyline.points.push_back(Point( edge->vertex0()->x(), edge->vertex0()->y() ));
489 polyline.points.push_back(Point( edge->vertex1()->x(), edge->vertex1()->y() ));
490 polylines->push_back(polyline);
491 }
492 return;
493 }
494 */
495
496 // collect valid edges (i.e. prune those not belonging to MAT)
497 // note: this keeps twins, so it inserts twice the number of the valid edges
498 m_edge_data.assign(m_vd.edges().size() / 2, EdgeData{});
499 for (VD::const_edge_iterator edge = m_vd.edges().begin(); edge != m_vd.edges().end(); edge += 2)
500 if (edge->is_primary() && edge->is_finite() &&
503 this->validate_edge(&*edge)) {
504 // Valid skeleton edge.
505 this->edge_data(*edge).first.active = true;
506 }
507
508 // iterate through the valid edges to build polylines
509 ThickPolyline reverse_polyline;
510 for (VD::const_edge_iterator seed_edge = m_vd.edges().begin(); seed_edge != m_vd.edges().end(); seed_edge += 2)
511 if (EdgeData &seed_edge_data = this->edge_data(*seed_edge).first; seed_edge_data.active) {
512 // Mark this edge as visited.
513 seed_edge_data.active = false;
514
515 // Start a polyline.
516 ThickPolyline polyline;
517 polyline.points.emplace_back(seed_edge->vertex0()->x(), seed_edge->vertex0()->y());
518 polyline.points.emplace_back(seed_edge->vertex1()->x(), seed_edge->vertex1()->y());
519 polyline.width.emplace_back(seed_edge_data.width_start);
520 polyline.width.emplace_back(seed_edge_data.width_end);
521 // Grow the polyline in a forward direction.
522 this->process_edge_neighbors(&*seed_edge, &polyline);
523 assert(polyline.width.size() == polyline.points.size() * 2 - 2);
524
525 // Grow the polyline in a backward direction.
526 reverse_polyline.clear();
527 this->process_edge_neighbors(seed_edge->twin(), &reverse_polyline);
528 polyline.points.insert(polyline.points.begin(), reverse_polyline.points.rbegin(), reverse_polyline.points.rend());
529 polyline.width.insert(polyline.width.begin(), reverse_polyline.width.rbegin(), reverse_polyline.width.rend());
530 polyline.endpoints.first = reverse_polyline.endpoints.second;
531 assert(polyline.width.size() == polyline.points.size() * 2 - 2);
532
533 // Prevent loop endpoints from being extended.
534 if (polyline.first_point() == polyline.last_point()) {
535 polyline.endpoints.first = false;
536 polyline.endpoints.second = false;
537 }
538
539 // Append polyline to result.
540 polylines->emplace_back(std::move(polyline));
541 }
542
543 #ifdef SLIC3R_DEBUG
544 {
545 static int iRun = 0;
546 dump_voronoi_to_svg(m_lines, m_vd, polylines, debug_out_path("MedialAxis-%d.svg", iRun ++).c_str());
547 printf("Thick lines: ");
548 for (ThickPolylines::const_iterator it = polylines->begin(); it != polylines->end(); ++ it) {
549 ThickLines lines = it->thicklines();
550 for (ThickLines::const_iterator it2 = lines.begin(); it2 != lines.end(); ++ it2) {
551 printf("%f,%f ", it2->a_width, it2->b_width);
552 }
553 }
554 printf("\n");
555 }
556 #endif /* SLIC3R_DEBUG */
557}
std::vector< EdgeData > m_edge_data
Definition MedialAxis.hpp:45
void process_edge_neighbors(const VD::edge_type *edge, ThickPolyline *polyline)
Definition MedialAxis.cpp:568
bool active
Definition MedialAxis.hpp:36
std::pair< EdgeData &, bool > edge_data(const VD::edge_type &edge)
Definition MedialAxis.hpp:41
VD m_vd
Definition MedialAxis.hpp:32
Definition MedialAxis.hpp:35
voronoi_diagram_type::const_edge_iterator const_edge_iterator
Definition Voronoi.hpp:45
#define SCALED_EPSILON
Definition libslic3r.h:109
VertexCategory vertex_category(const VD::vertex_type &v)
Definition VoronoiOffset.hpp:77
@ Inside
Definition VoronoiOffset.hpp:42
void annotate_inside_outside(VD &vd, const Lines &lines)
Definition VoronoiOffset.cpp:660
std::string debug_out_path(const char *name,...)
Definition utils.cpp:382
Lines to_lines(const ExPolygon &src)
Definition ExPolygon.hpp:124
static void dump_voronoi_to_svg(const char *path, const Geometry::VoronoiDiagram &vd, const Points &points, const Lines &lines, const Polygons &offset_curves=Polygons(), const Lines &helper_lines=Lines(), double scale=0)
Definition VoronoiVisualUtils.hpp:311
std::vector< ThickLine > ThickLines
Definition Line.hpp:22
Slic3r::ExPolygons closing_ex(const Slic3r::Polygons &polygons, const float delta1, const float delta2, JoinType joinType, double miterLimit)
Definition ClipperUtils.cpp:678

References Slic3r::Geometry::MedialAxis::EdgeData::active, Slic3r::Voronoi::annotate_inside_outside(), Slic3r::ThickPolyline::clear(), Slic3r::closing_ex(), Slic3r::debug_out_path(), Slic3r::dump_voronoi_to_svg(), edge_data(), Slic3r::ThickPolyline::endpoints, Slic3r::MultiPoint::first_point(), Slic3r::Voronoi::Inside, Slic3r::Polyline::last_point(), m_edge_data, m_expolygon, m_lines, m_vd, Slic3r::MultiPoint::points, process_edge_neighbors(), SCALED_EPSILON, Slic3r::to_lines(), Slic3r::Voronoi::vertex_category(), and Slic3r::ThickPolyline::width.

Referenced by build(), and Slic3r::ExPolygon::medial_axis().

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

◆ edge_data()

std::pair< EdgeData &, bool > Slic3r::Geometry::MedialAxis::edge_data ( const VD::edge_type & edge)
inlineprivate
41 {
42 size_t edge_id = &edge - &m_vd.edges().front();
43 return { m_edge_data[edge_id / 2], (edge_id & 1) != 0 };
44 }

References m_edge_data, and m_vd.

Referenced by build(), process_edge_neighbors(), and validate_edge().

Here is the caller graph for this function:

◆ process_edge_neighbors()

void Slic3r::Geometry::MedialAxis::process_edge_neighbors ( const VD::edge_type * edge,
ThickPolyline * polyline )
private
569{
570 for (;;) {
571 // Since rot_next() works on the edge starting point but we want
572 // to find neighbors on the ending point, we just swap edge with
573 // its twin.
574 const VD::edge_type *twin = edge->twin();
575
576 // count neighbors for this edge
577 size_t num_neighbors = 0;
578 const VD::edge_type *first_neighbor = nullptr;
579 for (const VD::edge_type *neighbor = twin->rot_next(); neighbor != twin; neighbor = neighbor->rot_next())
580 if (this->edge_data(*neighbor).first.active) {
581 if (num_neighbors == 0)
582 first_neighbor = neighbor;
583 ++ num_neighbors;
584 }
585
586 // if we have a single neighbor then we can continue recursively
587 if (num_neighbors == 1) {
588 if (std::pair<EdgeData&, bool> neighbor_data = this->edge_data(*first_neighbor);
589 neighbor_data.first.active) {
590 neighbor_data.first.active = false;
591 polyline->points.emplace_back(first_neighbor->vertex1()->x(), first_neighbor->vertex1()->y());
592 if (neighbor_data.second) {
593 polyline->width.push_back(neighbor_data.first.width_end);
594 polyline->width.push_back(neighbor_data.first.width_start);
595 } else {
596 polyline->width.push_back(neighbor_data.first.width_start);
597 polyline->width.push_back(neighbor_data.first.width_end);
598 }
599 edge = first_neighbor;
600 // Continue chaining.
601 continue;
602 }
603 } else if (num_neighbors == 0) {
604 polyline->endpoints.second = true;
605 } else {
606 // T-shaped or star-shaped joint
607 }
608 // Stop chaining.
609 break;
610 }
611}
voronoi_diagram_type::edge_type edge_type
Definition Voronoi.hpp:41

References edge_data(), Slic3r::ThickPolyline::endpoints, Slic3r::MultiPoint::points, and Slic3r::ThickPolyline::width.

Referenced by build().

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

◆ validate_edge()

bool Slic3r::Geometry::MedialAxis::validate_edge ( const VD::edge_type * edge)
private
614{
615 auto retrieve_segment = [this](const VD::cell_type* cell) -> const Line& { return m_lines[cell->source_index()]; };
616 auto retrieve_endpoint = [retrieve_segment](const VD::cell_type* cell) -> const Point& {
617 const Line &line = retrieve_segment(cell);
618 return cell->source_category() == boost::polygon::SOURCE_CATEGORY_SEGMENT_START_POINT ? line.a : line.b;
619 };
620
621 // prevent overflows and detect almost-infinite edges
622// #ifndef CLIPPERLIB_INT32
623// if (std::abs(edge->vertex0()->x()) > double(CLIPPER_MAX_COORD_UNSCALED) ||
624// std::abs(edge->vertex0()->y()) > double(CLIPPER_MAX_COORD_UNSCALED) ||
625// std::abs(edge->vertex1()->x()) > double(CLIPPER_MAX_COORD_UNSCALED) ||
626// std::abs(edge->vertex1()->y()) > double(CLIPPER_MAX_COORD_UNSCALED))
627// return false;
628// #endif // CLIPPERLIB_INT32
629
630 // construct the line representing this edge of the Voronoi diagram
631 const Line line({ edge->vertex0()->x(), edge->vertex0()->y() },
632 { edge->vertex1()->x(), edge->vertex1()->y() });
633
634 // retrieve the original line segments which generated the edge we're checking
635 const VD::cell_type* cell_l = edge->cell();
636 const VD::cell_type* cell_r = edge->twin()->cell();
637 const Line &segment_l = retrieve_segment(cell_l);
638 const Line &segment_r = retrieve_segment(cell_r);
639
640 /*
641 SVG svg("edge.svg");
642 svg.draw(m_expolygon);
643 svg.draw(line);
644 svg.draw(segment_l, "red");
645 svg.draw(segment_r, "blue");
646 svg.Close();
647 */
648
649 /* Calculate thickness of the cross-section at both the endpoints of this edge.
650 Our Voronoi edge is part of a CCW sequence going around its Voronoi cell
651 located on the left side. (segment_l).
652 This edge's twin goes around segment_r. Thus, segment_r is
653 oriented in the same direction as our main edge, and segment_l is oriented
654 in the same direction as our twin edge.
655 We used to only consider the (half-)distances to segment_r, and that works
656 whenever segment_l and segment_r are almost specular and facing. However,
657 at curves they are staggered and they only face for a very little length
658 (our very short edge represents such visibility).
659 Both w0 and w1 can be calculated either towards cell_l or cell_r with equal
660 results by Voronoi definition.
661 When cell_l or cell_r don't refer to the segment but only to an endpoint, we
662 calculate the distance to that endpoint instead. */
663
664 coordf_t w0 = cell_r->contains_segment()
665 ? segment_r.distance_to(line.a)*2
666 : (retrieve_endpoint(cell_r) - line.a).cast<double>().norm()*2;
667
668 coordf_t w1 = cell_l->contains_segment()
669 ? segment_l.distance_to(line.b)*2
670 : (retrieve_endpoint(cell_l) - line.b).cast<double>().norm()*2;
671
672 if (cell_l->contains_segment() && cell_r->contains_segment()) {
673 // calculate the relative angle between the two boundary segments
674 double angle = fabs(segment_r.orientation() - segment_l.orientation());
675 if (angle > PI)
676 angle = 2. * PI - angle;
677 assert(angle >= 0 && angle <= PI);
678
679 // fabs(angle) ranges from 0 (collinear, same direction) to PI (collinear, opposite direction)
680 // we're interested only in segments close to the second case (facing segments)
681 // so we allow some tolerance.
682 // this filter ensures that we're dealing with a narrow/oriented area (longer than thick)
683 // we don't run it on edges not generated by two segments (thus generated by one segment
684 // and the endpoint of another segment), since their orientation would not be meaningful
685 if (PI - angle > PI / 8.) {
686 // angle is not narrow enough
687 // only apply this filter to segments that are not too short otherwise their
688 // angle could possibly be not meaningful
689 if (w0 < SCALED_EPSILON || w1 < SCALED_EPSILON || line.length() >= m_min_width)
690 return false;
691 }
692 } else {
693 if (w0 < SCALED_EPSILON || w1 < SCALED_EPSILON)
694 return false;
695 }
696
697 if ((w0 >= m_min_width || w1 >= m_min_width) &&
698 (w0 <= m_max_width || w1 <= m_max_width)) {
699 std::pair<EdgeData&, bool> ed = this->edge_data(*edge);
700 if (ed.second)
701 std::swap(w0, w1);
702 ed.first.width_start = w0;
703 ed.first.width_end = w1;
704 return true;
705 }
706
707 return false;
708}
voronoi_diagram_type::cell_type cell_type
Definition Voronoi.hpp:42
static constexpr double PI
Definition libslic3r.h:81
double coordf_t
Definition GUI_ObjectList.hpp:46
double angle(const Eigen::MatrixBase< Derived > &v1, const Eigen::MatrixBase< Derived2 > &v2)
Definition Point.hpp:138
auto cast(const BoundingBoxBase< Tin > &b)
Definition BoundingBox.hpp:299

References Slic3r::Line::a, Slic3r::angle(), Slic3r::Line::b, Slic3r::cast(), Slic3r::Line::distance_to(), edge_data(), m_lines, m_max_width, m_min_width, Slic3r::Line::orientation(), PI, and SCALED_EPSILON.

Here is the call graph for this function:

Member Data Documentation

◆ m_edge_data

std::vector<EdgeData> Slic3r::Geometry::MedialAxis::m_edge_data
private

Referenced by build(), and edge_data().

◆ m_expolygon

const ExPolygon& Slic3r::Geometry::MedialAxis::m_expolygon
private

Referenced by MedialAxis(), and build().

◆ m_lines

Lines Slic3r::Geometry::MedialAxis::m_lines
private

Referenced by MedialAxis(), build(), and validate_edge().

◆ m_max_width

double Slic3r::Geometry::MedialAxis::m_max_width
private

Referenced by MedialAxis(), and validate_edge().

◆ m_min_width

double Slic3r::Geometry::MedialAxis::m_min_width
private

Referenced by MedialAxis(), and validate_edge().

◆ m_vd

VD Slic3r::Geometry::MedialAxis::m_vd
private

Referenced by build(), and edge_data().


The documentation for this class was generated from the following files:
  • /home/runner/work/OrcaSlicer/OrcaSlicer/src/libslic3r/Geometry/MedialAxis.hpp
  • /home/runner/work/OrcaSlicer/OrcaSlicer/src/libslic3r/Geometry/MedialAxis.cpp