Prusa Slicer 2.6.0
Loading...
Searching...
No Matches
Slic3r::Geometry::MedialAxis Class Reference

#include <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 ExPolygonm_expolygon
 
Lines m_lines
 
double m_min_width
 
double m_max_width
 
VD m_vd
 
std::vector< EdgeDatam_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 
)
445 :
446 m_expolygon(expolygon), m_lines(expolygon.lines()), m_min_width(min_width), m_max_width(max_width)
447{
448 (void)m_expolygon; // supress unused variable warning
449}
const ExPolygon & m_expolygon
Definition MedialAxis.hpp:17
Lines m_lines
Definition MedialAxis.hpp:18
double m_min_width
Definition MedialAxis.hpp:20
double m_max_width
Definition MedialAxis.hpp:21
typedef void(GLAPIENTRYP _GLUfuncptr)(void)

References m_expolygon, and void().

+ Here is the call graph for this function:

Member Function Documentation

◆ build() [1/2]

void Slic3r::Geometry::MedialAxis::build ( Polylines polylines)
537{
539 this->build(&tp);
540 polylines->reserve(polylines->size() + tp.size());
541 for (auto &pl : tp)
542 polylines->emplace_back(pl.points);
543}
void build(ThickPolylines *polylines)
Definition MedialAxis.cpp:451
std::vector< ThickPolyline > ThickPolylines
Definition Polyline.hpp:15

References build().

+ Here is the call graph for this function:

◆ build() [2/2]

void Slic3r::Geometry::MedialAxis::build ( ThickPolylines polylines)
452{
453 construct_voronoi(m_lines.begin(), m_lines.end(), &m_vd);
455// static constexpr double threshold_alpha = M_PI / 12.; // 30 degrees
456// std::vector<Vec2d> skeleton_edges = Slic3r::Voronoi::skeleton_edges_rough(vd, lines, threshold_alpha);
457
458 /*
459 // DEBUG: dump all Voronoi edges
460 {
461 for (VD::const_edge_iterator edge = m_vd.edges().begin(); edge != m_vd.edges().end(); ++edge) {
462 if (edge->is_infinite()) continue;
463
464 ThickPolyline polyline;
465 polyline.points.push_back(Point( edge->vertex0()->x(), edge->vertex0()->y() ));
466 polyline.points.push_back(Point( edge->vertex1()->x(), edge->vertex1()->y() ));
467 polylines->push_back(polyline);
468 }
469 return;
470 }
471 */
472
473 // collect valid edges (i.e. prune those not belonging to MAT)
474 // note: this keeps twins, so it inserts twice the number of the valid edges
475 m_edge_data.assign(m_vd.edges().size() / 2, EdgeData{});
476 for (VD::const_edge_iterator edge = m_vd.edges().begin(); edge != m_vd.edges().end(); edge += 2)
477 if (edge->is_primary() && edge->is_finite() &&
480 this->validate_edge(&*edge)) {
481 // Valid skeleton edge.
482 this->edge_data(*edge).first.active = true;
483 }
484
485 // iterate through the valid edges to build polylines
486 ThickPolyline reverse_polyline;
487 for (VD::const_edge_iterator seed_edge = m_vd.edges().begin(); seed_edge != m_vd.edges().end(); seed_edge += 2)
488 if (EdgeData &seed_edge_data = this->edge_data(*seed_edge).first; seed_edge_data.active) {
489 // Mark this edge as visited.
490 seed_edge_data.active = false;
491
492 // Start a polyline.
493 ThickPolyline polyline;
494 polyline.points.emplace_back(seed_edge->vertex0()->x(), seed_edge->vertex0()->y());
495 polyline.points.emplace_back(seed_edge->vertex1()->x(), seed_edge->vertex1()->y());
496 polyline.width.emplace_back(seed_edge_data.width_start);
497 polyline.width.emplace_back(seed_edge_data.width_end);
498 // Grow the polyline in a forward direction.
499 this->process_edge_neighbors(&*seed_edge, &polyline);
500 assert(polyline.width.size() == polyline.points.size() * 2 - 2);
501
502 // Grow the polyline in a backward direction.
503 reverse_polyline.clear();
504 this->process_edge_neighbors(seed_edge->twin(), &reverse_polyline);
505 polyline.points.insert(polyline.points.begin(), reverse_polyline.points.rbegin(), reverse_polyline.points.rend());
506 polyline.width.insert(polyline.width.begin(), reverse_polyline.width.rbegin(), reverse_polyline.width.rend());
507 polyline.endpoints.first = reverse_polyline.endpoints.second;
508 assert(polyline.width.size() == polyline.points.size() * 2 - 2);
509
510 // Prevent loop endpoints from being extended.
511 if (polyline.first_point() == polyline.last_point()) {
512 polyline.endpoints.first = false;
513 polyline.endpoints.second = false;
514 }
515
516 // Append polyline to result.
517 polylines->emplace_back(std::move(polyline));
518 }
519
520 #ifdef SLIC3R_DEBUG
521 {
522 static int iRun = 0;
523 dump_voronoi_to_svg(m_lines, m_vd, polylines, debug_out_path("MedialAxis-%d.svg", iRun ++).c_str());
524 printf("Thick lines: ");
525 for (ThickPolylines::const_iterator it = polylines->begin(); it != polylines->end(); ++ it) {
526 ThickLines lines = it->thicklines();
527 for (ThickLines::const_iterator it2 = lines.begin(); it2 != lines.end(); ++ it2) {
528 printf("%f,%f ", it2->a_width, it2->b_width);
529 }
530 }
531 printf("\n");
532 }
533 #endif /* SLIC3R_DEBUG */
534}
std::vector< EdgeData > m_edge_data
Definition MedialAxis.hpp:38
void process_edge_neighbors(const VD::edge_type *edge, ThickPolyline *polyline)
Definition MedialAxis.cpp:545
std::pair< EdgeData &, bool > edge_data(const VD::edge_type &edge)
Definition MedialAxis.hpp:34
bool validate_edge(const VD::edge_type *edge)
Definition MedialAxis.cpp:590
VD m_vd
Definition MedialAxis.hpp:25
VertexCategory vertex_category(const VD::vertex_type &v)
Definition VoronoiOffset.hpp:72
void annotate_inside_outside(VD &vd, const Lines &lines)
Definition VoronoiOffset.cpp:650
std::string debug_out_path(const char *name,...)
Definition utils.cpp:218
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:296
std::vector< ThickLine > ThickLines
Definition Line.hpp:19

References Slic3r::Voronoi::annotate_inside_outside(), Slic3r::ThickPolyline::clear(), Slic3r::debug_out_path(), Slic3r::dump_voronoi_to_svg(), edge_data(), Slic3r::ThickPolyline::endpoints, Slic3r::ThickPolyline::first_point(), Slic3r::Voronoi::Inside, Slic3r::ThickPolyline::last_point(), m_edge_data, m_lines, m_vd, Slic3r::ThickPolyline::points, process_edge_neighbors(), validate_edge(), 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
34 {
35 size_t edge_id = &edge - &m_vd.edges().front();
36 return { m_edge_data[edge_id / 2], (edge_id & 1) != 0 };
37 }

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
546{
547 for (;;) {
548 // Since rot_next() works on the edge starting point but we want
549 // to find neighbors on the ending point, we just swap edge with
550 // its twin.
551 const VD::edge_type *twin = edge->twin();
552
553 // count neighbors for this edge
554 size_t num_neighbors = 0;
555 const VD::edge_type *first_neighbor = nullptr;
556 for (const VD::edge_type *neighbor = twin->rot_next(); neighbor != twin; neighbor = neighbor->rot_next())
557 if (this->edge_data(*neighbor).first.active) {
558 if (num_neighbors == 0)
559 first_neighbor = neighbor;
560 ++ num_neighbors;
561 }
562
563 // if we have a single neighbor then we can continue recursively
564 if (num_neighbors == 1) {
565 if (std::pair<EdgeData&, bool> neighbor_data = this->edge_data(*first_neighbor);
566 neighbor_data.first.active) {
567 neighbor_data.first.active = false;
568 polyline->points.emplace_back(first_neighbor->vertex1()->x(), first_neighbor->vertex1()->y());
569 if (neighbor_data.second) {
570 polyline->width.push_back(neighbor_data.first.width_end);
571 polyline->width.push_back(neighbor_data.first.width_start);
572 } else {
573 polyline->width.push_back(neighbor_data.first.width_start);
574 polyline->width.push_back(neighbor_data.first.width_end);
575 }
576 edge = first_neighbor;
577 // Continue chaining.
578 continue;
579 }
580 } else if (num_neighbors == 0) {
581 polyline->endpoints.second = true;
582 } else {
583 // T-shaped or star-shaped joint
584 }
585 // Stop chaining.
586 break;
587 }
588}

References edge_data(), Slic3r::ThickPolyline::endpoints, Slic3r::ThickPolyline::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
591{
592 auto retrieve_segment = [this](const VD::cell_type* cell) -> const Line& { return m_lines[cell->source_index()]; };
593 auto retrieve_endpoint = [retrieve_segment](const VD::cell_type* cell) -> const Point& {
594 const Line &line = retrieve_segment(cell);
595 return cell->source_category() == boost::polygon::SOURCE_CATEGORY_SEGMENT_START_POINT ? line.a : line.b;
596 };
597
598 // prevent overflows and detect almost-infinite edges
599#ifndef CLIPPERLIB_INT32
600 if (std::abs(edge->vertex0()->x()) > double(CLIPPER_MAX_COORD_UNSCALED) ||
601 std::abs(edge->vertex0()->y()) > double(CLIPPER_MAX_COORD_UNSCALED) ||
602 std::abs(edge->vertex1()->x()) > double(CLIPPER_MAX_COORD_UNSCALED) ||
603 std::abs(edge->vertex1()->y()) > double(CLIPPER_MAX_COORD_UNSCALED))
604 return false;
605#endif // CLIPPERLIB_INT32
606
607 // construct the line representing this edge of the Voronoi diagram
608 const Line line({ edge->vertex0()->x(), edge->vertex0()->y() },
609 { edge->vertex1()->x(), edge->vertex1()->y() });
610
611 // retrieve the original line segments which generated the edge we're checking
612 const VD::cell_type* cell_l = edge->cell();
613 const VD::cell_type* cell_r = edge->twin()->cell();
614 const Line &segment_l = retrieve_segment(cell_l);
615 const Line &segment_r = retrieve_segment(cell_r);
616
617 /*
618 SVG svg("edge.svg");
619 svg.draw(m_expolygon);
620 svg.draw(line);
621 svg.draw(segment_l, "red");
622 svg.draw(segment_r, "blue");
623 svg.Close();
624 */
625
626 /* Calculate thickness of the cross-section at both the endpoints of this edge.
627 Our Voronoi edge is part of a CCW sequence going around its Voronoi cell
628 located on the left side. (segment_l).
629 This edge's twin goes around segment_r. Thus, segment_r is
630 oriented in the same direction as our main edge, and segment_l is oriented
631 in the same direction as our twin edge.
632 We used to only consider the (half-)distances to segment_r, and that works
633 whenever segment_l and segment_r are almost specular and facing. However,
634 at curves they are staggered and they only face for a very little length
635 (our very short edge represents such visibility).
636 Both w0 and w1 can be calculated either towards cell_l or cell_r with equal
637 results by Voronoi definition.
638 When cell_l or cell_r don't refer to the segment but only to an endpoint, we
639 calculate the distance to that endpoint instead. */
640
641 coordf_t w0 = cell_r->contains_segment()
642 ? segment_r.distance_to(line.a)*2
643 : (retrieve_endpoint(cell_r) - line.a).cast<double>().norm()*2;
644
645 coordf_t w1 = cell_l->contains_segment()
646 ? segment_l.distance_to(line.b)*2
647 : (retrieve_endpoint(cell_l) - line.b).cast<double>().norm()*2;
648
649 if (cell_l->contains_segment() && cell_r->contains_segment()) {
650 // calculate the relative angle between the two boundary segments
651 double angle = fabs(segment_r.orientation() - segment_l.orientation());
652 if (angle > PI)
653 angle = 2. * PI - angle;
654 assert(angle >= 0 && angle <= PI);
655
656 // fabs(angle) ranges from 0 (collinear, same direction) to PI (collinear, opposite direction)
657 // we're interested only in segments close to the second case (facing segments)
658 // so we allow some tolerance.
659 // this filter ensures that we're dealing with a narrow/oriented area (longer than thick)
660 // we don't run it on edges not generated by two segments (thus generated by one segment
661 // and the endpoint of another segment), since their orientation would not be meaningful
662 if (PI - angle > PI / 8.) {
663 // angle is not narrow enough
664 // only apply this filter to segments that are not too short otherwise their
665 // angle could possibly be not meaningful
666 if (w0 < SCALED_EPSILON || w1 < SCALED_EPSILON || line.length() >= m_min_width)
667 return false;
668 }
669 } else {
670 if (w0 < SCALED_EPSILON || w1 < SCALED_EPSILON)
671 return false;
672 }
673
674 if ((w0 >= m_min_width || w1 >= m_min_width) &&
675 (w0 <= m_max_width || w1 <= m_max_width)) {
676 std::pair<EdgeData&, bool> ed = this->edge_data(*edge);
677 if (ed.second)
678 std::swap(w0, w1);
679 ed.first.width_start = w0;
680 ed.first.width_end = w1;
681 return true;
682 }
683
684 return false;
685}
static constexpr double PI
Definition libslic3r.h:58
#define SCALED_EPSILON
Definition libslic3r.h:71
double coordf_t
Definition libslic3r.h:45
double angle(const Eigen::MatrixBase< Derived > &v1, const Eigen::MatrixBase< Derived2 > &v2)
Definition Point.hpp:112
TCoord< P > x(const P &p)
Definition geometry_traits.hpp:297
Kernel::Point_2 Point
Definition point_areas.cpp:20

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

Referenced by build().

+ Here is the call graph for this function:
+ Here is the caller 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().

◆ m_lines

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

Referenced by build(), and validate_edge().

◆ m_max_width

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

Referenced by validate_edge().

◆ m_min_width

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

Referenced by 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: