Rest of the path chaining has been replaced with the new algorithm.
PolylineCollection.cpp/hpp was removed, use Polylines instead. Various first_point() / last_point() now return references, not copies.
This commit is contained in:
parent
4b35ebe6e5
commit
331c187b39
29 changed files with 266 additions and 364 deletions
|
@ -131,8 +131,6 @@ add_library(libslic3r STATIC
|
|||
PolygonTrimmer.hpp
|
||||
Polyline.cpp
|
||||
Polyline.hpp
|
||||
PolylineCollection.cpp
|
||||
PolylineCollection.hpp
|
||||
Print.cpp
|
||||
Print.hpp
|
||||
PrintBase.cpp
|
||||
|
|
|
@ -81,8 +81,8 @@ public:
|
|||
virtual ExtrusionEntity* clone_move() = 0;
|
||||
virtual ~ExtrusionEntity() {}
|
||||
virtual void reverse() = 0;
|
||||
virtual Point first_point() const = 0;
|
||||
virtual Point last_point() const = 0;
|
||||
virtual const Point& first_point() const = 0;
|
||||
virtual const Point& last_point() const = 0;
|
||||
// Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width.
|
||||
// Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps.
|
||||
virtual void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const = 0;
|
||||
|
@ -139,8 +139,8 @@ public:
|
|||
// Create a new object, initialize it with this object using the move semantics.
|
||||
ExtrusionEntity* clone_move() override { return new ExtrusionPath(std::move(*this)); }
|
||||
void reverse() override { this->polyline.reverse(); }
|
||||
Point first_point() const override { return this->polyline.points.front(); }
|
||||
Point last_point() const override { return this->polyline.points.back(); }
|
||||
const Point& first_point() const override { return this->polyline.points.front(); }
|
||||
const Point& last_point() const override { return this->polyline.points.back(); }
|
||||
size_t size() const { return this->polyline.size(); }
|
||||
bool empty() const { return this->polyline.empty(); }
|
||||
bool is_closed() const { return ! this->empty() && this->polyline.points.front() == this->polyline.points.back(); }
|
||||
|
@ -200,8 +200,8 @@ public:
|
|||
// Create a new object, initialize it with this object using the move semantics.
|
||||
ExtrusionEntity* clone_move() override { return new ExtrusionMultiPath(std::move(*this)); }
|
||||
void reverse() override;
|
||||
Point first_point() const override { return this->paths.front().polyline.points.front(); }
|
||||
Point last_point() const override { return this->paths.back().polyline.points.back(); }
|
||||
const Point& first_point() const override { return this->paths.front().polyline.points.front(); }
|
||||
const Point& last_point() const override { return this->paths.back().polyline.points.back(); }
|
||||
double length() const override;
|
||||
ExtrusionRole role() const override { return this->paths.empty() ? erNone : this->paths.front().role(); }
|
||||
// Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width.
|
||||
|
@ -243,8 +243,8 @@ public:
|
|||
bool make_clockwise();
|
||||
bool make_counter_clockwise();
|
||||
void reverse() override;
|
||||
Point first_point() const override { return this->paths.front().polyline.points.front(); }
|
||||
Point last_point() const override { assert(first_point() == this->paths.back().polyline.points.back()); return first_point(); }
|
||||
const Point& first_point() const override { return this->paths.front().polyline.points.front(); }
|
||||
const Point& last_point() const override { assert(this->first_point() == this->paths.back().polyline.points.back()); return this->first_point(); }
|
||||
Polygon polygon() const;
|
||||
double length() const override;
|
||||
bool split_at_vertex(const Point &point);
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "ExtrusionEntityCollection.hpp"
|
||||
#include "ShortestPath.hpp"
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <map>
|
||||
|
@ -73,78 +74,31 @@ void ExtrusionEntityCollection::remove(size_t i)
|
|||
this->entities.erase(this->entities.begin() + i);
|
||||
}
|
||||
|
||||
ExtrusionEntityCollection ExtrusionEntityCollection::chained_path(bool no_reverse, ExtrusionRole role) const
|
||||
ExtrusionEntityCollection ExtrusionEntityCollection::chained_path_from(const Point &start_near, ExtrusionRole role) const
|
||||
{
|
||||
ExtrusionEntityCollection coll;
|
||||
this->chained_path(&coll, no_reverse, role);
|
||||
return coll;
|
||||
}
|
||||
|
||||
void ExtrusionEntityCollection::chained_path(ExtrusionEntityCollection* retval, bool no_reverse, ExtrusionRole role) const
|
||||
{
|
||||
if (this->entities.empty()) return;
|
||||
this->chained_path_from(this->entities.front()->first_point(), retval, no_reverse, role);
|
||||
}
|
||||
|
||||
ExtrusionEntityCollection ExtrusionEntityCollection::chained_path_from(Point start_near, bool no_reverse, ExtrusionRole role) const
|
||||
{
|
||||
ExtrusionEntityCollection coll;
|
||||
this->chained_path_from(start_near, &coll, no_reverse, role);
|
||||
return coll;
|
||||
}
|
||||
|
||||
void ExtrusionEntityCollection::chained_path_from(Point start_near, ExtrusionEntityCollection* retval, bool no_reverse, ExtrusionRole role) const
|
||||
{
|
||||
if (this->no_sort) {
|
||||
*retval = *this;
|
||||
return;
|
||||
}
|
||||
|
||||
retval->entities.reserve(this->entities.size());
|
||||
|
||||
// if we're asked to return the original indices, build a map
|
||||
std::map<ExtrusionEntity*,size_t> indices_map;
|
||||
|
||||
ExtrusionEntitiesPtr my_paths;
|
||||
for (ExtrusionEntity * const &entity_src : this->entities) {
|
||||
if (role != erMixed) {
|
||||
// The caller wants only paths with a specific extrusion role.
|
||||
auto role2 = entity_src->role();
|
||||
if (role != role2) {
|
||||
// This extrusion entity does not match the role asked.
|
||||
assert(role2 != erMixed);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
ExtrusionEntity *entity = entity_src->clone();
|
||||
my_paths.push_back(entity);
|
||||
// if (orig_indices != nullptr)
|
||||
// indices_map[entity] = &entity_src - &this->entities.front();
|
||||
}
|
||||
|
||||
Points endpoints;
|
||||
for (const ExtrusionEntity *entity : my_paths) {
|
||||
endpoints.push_back(entity->first_point());
|
||||
endpoints.push_back((no_reverse || ! entity->can_reverse()) ?
|
||||
entity->first_point() : entity->last_point());
|
||||
}
|
||||
|
||||
while (! my_paths.empty()) {
|
||||
// find nearest point
|
||||
int start_index = start_near.nearest_point_index(endpoints);
|
||||
int path_index = start_index/2;
|
||||
ExtrusionEntity* entity = my_paths.at(path_index);
|
||||
// never reverse loops, since it's pointless for chained path and callers might depend on orientation
|
||||
if (start_index % 2 && !no_reverse && entity->can_reverse())
|
||||
entity->reverse();
|
||||
retval->entities.push_back(my_paths.at(path_index));
|
||||
// if (orig_indices != nullptr)
|
||||
// orig_indices->push_back(indices_map[entity]);
|
||||
my_paths.erase(my_paths.begin() + path_index);
|
||||
endpoints.erase(endpoints.begin() + 2*path_index, endpoints.begin() + 2*path_index + 2);
|
||||
start_near = retval->entities.back()->last_point();
|
||||
}
|
||||
ExtrusionEntityCollection out;
|
||||
if (this->no_sort) {
|
||||
out = *this;
|
||||
} else {
|
||||
if (role == erMixed)
|
||||
out = *this;
|
||||
else {
|
||||
for (const ExtrusionEntity *ee : this->entities) {
|
||||
if (role != erMixed) {
|
||||
// The caller wants only paths with a specific extrusion role.
|
||||
auto role2 = ee->role();
|
||||
if (role != role2) {
|
||||
// This extrusion entity does not match the role asked.
|
||||
assert(role2 != erMixed);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
out.entities.emplace_back(ee->clone());
|
||||
}
|
||||
}
|
||||
chain_and_reorder_extrusion_entities(out.entities, &start_near);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
void ExtrusionEntityCollection::polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const
|
||||
|
|
|
@ -65,13 +65,10 @@ public:
|
|||
}
|
||||
void replace(size_t i, const ExtrusionEntity &entity);
|
||||
void remove(size_t i);
|
||||
ExtrusionEntityCollection chained_path(bool no_reverse = false, ExtrusionRole role = erMixed) const;
|
||||
void chained_path(ExtrusionEntityCollection* retval, bool no_reverse = false, ExtrusionRole role = erMixed) const;
|
||||
ExtrusionEntityCollection chained_path_from(Point start_near, bool no_reverse = false, ExtrusionRole role = erMixed) const;
|
||||
void chained_path_from(Point start_near, ExtrusionEntityCollection* retval, bool no_reverse = false, ExtrusionRole role = erMixed) const;
|
||||
ExtrusionEntityCollection chained_path_from(const Point &start_near, ExtrusionRole role = erMixed) const;
|
||||
void reverse();
|
||||
Point first_point() const { return this->entities.front()->first_point(); }
|
||||
Point last_point() const { return this->entities.back()->last_point(); }
|
||||
const Point& first_point() const { return this->entities.front()->first_point(); }
|
||||
const Point& last_point() const { return this->entities.back()->last_point(); }
|
||||
// Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width.
|
||||
// Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps.
|
||||
void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const override;
|
||||
|
|
|
@ -176,7 +176,7 @@ void Fill3DHoneycomb::_fill_surface_single(
|
|||
}
|
||||
}
|
||||
bool first = true;
|
||||
for (Polyline &polyline : chain_infill_polylines(std::move(polylines))) {
|
||||
for (Polyline &polyline : chain_polylines(std::move(polylines))) {
|
||||
if (! first) {
|
||||
// Try to connect the lines.
|
||||
Points &pts_end = polylines_out.back().points;
|
||||
|
|
|
@ -167,7 +167,7 @@ void FillGyroid::_fill_surface_single(
|
|||
}
|
||||
}
|
||||
bool first = true;
|
||||
for (Polyline &polyline : chain_infill_polylines(std::move(polylines))) {
|
||||
for (Polyline &polyline : chain_polylines(std::move(polylines))) {
|
||||
if (! first) {
|
||||
// Try to connect the lines.
|
||||
Points &pts_end = polylines_out.back().points;
|
||||
|
|
|
@ -93,7 +93,7 @@ void FillHoneycomb::_fill_surface_single(
|
|||
|
||||
// connect paths
|
||||
if (! paths.empty()) { // prevent calling leftmost_point() on empty collections
|
||||
Polylines chained = chain_infill_polylines(std::move(paths));
|
||||
Polylines chained = chain_polylines(std::move(paths));
|
||||
assert(paths.empty());
|
||||
paths.clear();
|
||||
for (Polyline &path : chained) {
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
#include "../ClipperUtils.hpp"
|
||||
#include "../PolylineCollection.hpp"
|
||||
#include "../Surface.hpp"
|
||||
|
||||
#include "FillPlanePath.hpp"
|
||||
|
|
|
@ -93,7 +93,7 @@ void FillRectilinear::_fill_surface_single(
|
|||
}
|
||||
}
|
||||
bool first = true;
|
||||
for (Polyline &polyline : chain_infill_polylines(std::move(polylines))) {
|
||||
for (Polyline &polyline : chain_polylines(std::move(polylines))) {
|
||||
if (! first) {
|
||||
// Try to connect the lines.
|
||||
Points &pts_end = polylines_out.back().points;
|
||||
|
|
|
@ -1969,7 +1969,7 @@ void GCode::process_layer(
|
|||
m_layer = layers[instance_to_print.layer_id].support_layer;
|
||||
gcode += this->extrude_support(
|
||||
// support_extrusion_role is erSupportMaterial, erSupportMaterialInterface or erMixed for all extrusion paths.
|
||||
instance_to_print.object_by_extruder.support->chained_path_from(m_last_pos, false, instance_to_print.object_by_extruder.support_extrusion_role));
|
||||
instance_to_print.object_by_extruder.support->chained_path_from(m_last_pos, instance_to_print.object_by_extruder.support_extrusion_role));
|
||||
m_layer = layers[instance_to_print.layer_id].layer();
|
||||
}
|
||||
for (ObjectByExtruder::Island &island : instance_to_print.object_by_extruder.islands) {
|
||||
|
@ -2588,10 +2588,10 @@ std::string GCode::extrude_infill(const Print &print, const std::vector<ObjectBy
|
|||
std::string gcode;
|
||||
for (const ObjectByExtruder::Island::Region ®ion : by_region) {
|
||||
m_config.apply(print.regions()[®ion - &by_region.front()]->config());
|
||||
for (ExtrusionEntity *fill : region.infills.chained_path_from(m_last_pos, false).entities) {
|
||||
for (ExtrusionEntity *fill : region.infills.chained_path_from(m_last_pos).entities) {
|
||||
auto *eec = dynamic_cast<ExtrusionEntityCollection*>(fill);
|
||||
if (eec) {
|
||||
for (ExtrusionEntity *ee : eec->chained_path_from(m_last_pos, false).entities)
|
||||
for (ExtrusionEntity *ee : eec->chained_path_from(m_last_pos).entities)
|
||||
gcode += this->extrude_entity(*ee, "infill");
|
||||
} else
|
||||
gcode += this->extrude_entity(*fill, "infill");
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
#include "ClipperUtils.hpp"
|
||||
#include "ExPolygon.hpp"
|
||||
#include "Line.hpp"
|
||||
#include "PolylineCollection.hpp"
|
||||
#include "clipper.hpp"
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
|
|
|
@ -61,8 +61,10 @@ public:
|
|||
{
|
||||
CoordType dist = point_coord - this->coordinate(idx, dimension);
|
||||
return (dist * dist < search_radius + CoordType(EPSILON)) ?
|
||||
// The plane intersects a hypersphere centered at point_coord of search_radius.
|
||||
((unsigned int)(VisitorReturnMask::CONTINUE_LEFT) | (unsigned int)(VisitorReturnMask::CONTINUE_RIGHT)) :
|
||||
(dist < CoordType(0)) ? (unsigned int)(VisitorReturnMask::CONTINUE_RIGHT) : (unsigned int)(VisitorReturnMask::CONTINUE_LEFT);
|
||||
// The plane does not intersect the hypersphere.
|
||||
(dist > CoordType(0)) ? (unsigned int)(VisitorReturnMask::CONTINUE_RIGHT) : (unsigned int)(VisitorReturnMask::CONTINUE_LEFT);
|
||||
}
|
||||
|
||||
// Visitor is supposed to return a bit mask of VisitorReturnMask.
|
||||
|
|
|
@ -6,8 +6,6 @@
|
|||
#include "SurfaceCollection.hpp"
|
||||
#include "ExtrusionEntityCollection.hpp"
|
||||
#include "ExPolygonCollection.hpp"
|
||||
#include "PolylineCollection.hpp"
|
||||
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
@ -48,7 +46,7 @@ public:
|
|||
Polygons bridged;
|
||||
|
||||
// collection of polylines representing the unsupported bridge edges
|
||||
PolylineCollection unsupported_bridge_edges;
|
||||
Polylines unsupported_bridge_edges;
|
||||
|
||||
// ordered collection of extrusion paths/loops to build all perimeters
|
||||
// (this collection contains only ExtrusionEntityCollection objects)
|
||||
|
|
|
@ -272,7 +272,7 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly
|
|||
bridges[idx_last].bridge_angle = bd.angle;
|
||||
if (this->layer()->object()->config().support_material) {
|
||||
polygons_append(this->bridged, bd.coverage());
|
||||
this->unsupported_bridge_edges.append(bd.unsupported_edges());
|
||||
append(this->unsupported_bridge_edges, bd.unsupported_edges());
|
||||
}
|
||||
} else if (custom_angle > 0) {
|
||||
// Bridge was not detected (likely it is only supported at one side). Still it is a surface filled in
|
||||
|
|
|
@ -34,7 +34,7 @@ public:
|
|||
void rotate(double angle, const Point ¢er);
|
||||
void reverse();
|
||||
Point first_point() const;
|
||||
virtual Point last_point() const = 0;
|
||||
virtual const Point& last_point() const = 0;
|
||||
virtual Lines lines() const = 0;
|
||||
size_t size() const { return points.size(); }
|
||||
bool empty() const { return points.empty(); }
|
||||
|
|
|
@ -175,10 +175,9 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime
|
|||
perimeter_generator.overhang_flow.width,
|
||||
perimeter_generator.overhang_flow.height);
|
||||
|
||||
// reapply the nearest point search for starting point
|
||||
// We allow polyline reversal because Clipper may have randomly
|
||||
// reversed polylines during clipping.
|
||||
paths = (ExtrusionPaths)ExtrusionEntityCollection(paths).chained_path();
|
||||
// Reapply the nearest point search for starting point.
|
||||
// We allow polyline reversal because Clipper may have randomly reversed polylines during clipping.
|
||||
chain_and_reorder_extrusion_paths(paths, &paths.front().first_point());
|
||||
} else {
|
||||
ExtrusionPath path(role);
|
||||
path.polyline = loop.polygon.split_at_first_point();
|
||||
|
|
|
@ -5,43 +5,12 @@
|
|||
|
||||
namespace Slic3r {
|
||||
|
||||
Polygon::operator Polygons() const
|
||||
{
|
||||
Polygons pp;
|
||||
pp.push_back(*this);
|
||||
return pp;
|
||||
}
|
||||
|
||||
Polygon::operator Polyline() const
|
||||
{
|
||||
return this->split_at_first_point();
|
||||
}
|
||||
|
||||
Point&
|
||||
Polygon::operator[](Points::size_type idx)
|
||||
{
|
||||
return this->points[idx];
|
||||
}
|
||||
|
||||
const Point&
|
||||
Polygon::operator[](Points::size_type idx) const
|
||||
{
|
||||
return this->points[idx];
|
||||
}
|
||||
|
||||
Point
|
||||
Polygon::last_point() const
|
||||
{
|
||||
return this->points.front(); // last point == first point for polygons
|
||||
}
|
||||
|
||||
Lines Polygon::lines() const
|
||||
{
|
||||
return to_lines(*this);
|
||||
}
|
||||
|
||||
Polyline
|
||||
Polygon::split_at_vertex(const Point &point) const
|
||||
Polyline Polygon::split_at_vertex(const Point &point) const
|
||||
{
|
||||
// find index of point
|
||||
for (const Point &pt : this->points)
|
||||
|
@ -52,8 +21,7 @@ Polygon::split_at_vertex(const Point &point) const
|
|||
}
|
||||
|
||||
// Split a closed polygon into an open polyline, with the split point duplicated at both ends.
|
||||
Polyline
|
||||
Polygon::split_at_index(int index) const
|
||||
Polyline Polygon::split_at_index(int index) const
|
||||
{
|
||||
Polyline polyline;
|
||||
polyline.points.reserve(this->points.size() + 1);
|
||||
|
@ -64,19 +32,6 @@ Polygon::split_at_index(int index) const
|
|||
return polyline;
|
||||
}
|
||||
|
||||
// Split a closed polygon into an open polyline, with the split point duplicated at both ends.
|
||||
Polyline
|
||||
Polygon::split_at_first_point() const
|
||||
{
|
||||
return this->split_at_index(0);
|
||||
}
|
||||
|
||||
Points
|
||||
Polygon::equally_spaced_points(double distance) const
|
||||
{
|
||||
return this->split_at_first_point().equally_spaced_points(distance);
|
||||
}
|
||||
|
||||
/*
|
||||
int64_t Polygon::area2x() const
|
||||
{
|
||||
|
@ -107,20 +62,17 @@ double Polygon::area() const
|
|||
return 0.5 * a;
|
||||
}
|
||||
|
||||
bool
|
||||
Polygon::is_counter_clockwise() const
|
||||
bool Polygon::is_counter_clockwise() const
|
||||
{
|
||||
return ClipperLib::Orientation(Slic3rMultiPoint_to_ClipperPath(*this));
|
||||
}
|
||||
|
||||
bool
|
||||
Polygon::is_clockwise() const
|
||||
bool Polygon::is_clockwise() const
|
||||
{
|
||||
return !this->is_counter_clockwise();
|
||||
}
|
||||
|
||||
bool
|
||||
Polygon::make_counter_clockwise()
|
||||
bool Polygon::make_counter_clockwise()
|
||||
{
|
||||
if (!this->is_counter_clockwise()) {
|
||||
this->reverse();
|
||||
|
@ -129,8 +81,7 @@ Polygon::make_counter_clockwise()
|
|||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
Polygon::make_clockwise()
|
||||
bool Polygon::make_clockwise()
|
||||
{
|
||||
if (this->is_counter_clockwise()) {
|
||||
this->reverse();
|
||||
|
@ -139,16 +90,9 @@ Polygon::make_clockwise()
|
|||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
Polygon::is_valid() const
|
||||
{
|
||||
return this->points.size() >= 3;
|
||||
}
|
||||
|
||||
// Does an unoriented polygon contain a point?
|
||||
// Tested by counting intersections along a horizontal line.
|
||||
bool
|
||||
Polygon::contains(const Point &point) const
|
||||
bool Polygon::contains(const Point &point) const
|
||||
{
|
||||
// http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
|
||||
bool result = false;
|
||||
|
@ -174,8 +118,7 @@ Polygon::contains(const Point &point) const
|
|||
}
|
||||
|
||||
// this only works on CCW polygons as CW will be ripped out by Clipper's simplify_polygons()
|
||||
Polygons
|
||||
Polygon::simplify(double tolerance) const
|
||||
Polygons Polygon::simplify(double tolerance) const
|
||||
{
|
||||
// repeat first point at the end in order to apply Douglas-Peucker
|
||||
// on the whole polygon
|
||||
|
@ -189,8 +132,7 @@ Polygon::simplify(double tolerance) const
|
|||
return simplify_polygons(pp);
|
||||
}
|
||||
|
||||
void
|
||||
Polygon::simplify(double tolerance, Polygons &polygons) const
|
||||
void Polygon::simplify(double tolerance, Polygons &polygons) const
|
||||
{
|
||||
Polygons pp = this->simplify(tolerance);
|
||||
polygons.reserve(polygons.size() + pp.size());
|
||||
|
@ -198,8 +140,7 @@ Polygon::simplify(double tolerance, Polygons &polygons) const
|
|||
}
|
||||
|
||||
// Only call this on convex polygons or it will return invalid results
|
||||
void
|
||||
Polygon::triangulate_convex(Polygons* polygons) const
|
||||
void Polygon::triangulate_convex(Polygons* polygons) const
|
||||
{
|
||||
for (Points::const_iterator it = this->points.begin() + 2; it != this->points.end(); ++it) {
|
||||
Polygon p;
|
||||
|
@ -214,8 +155,7 @@ Polygon::triangulate_convex(Polygons* polygons) const
|
|||
}
|
||||
|
||||
// center of mass
|
||||
Point
|
||||
Polygon::centroid() const
|
||||
Point Polygon::centroid() const
|
||||
{
|
||||
double area_temp = this->area();
|
||||
double x_temp = 0;
|
||||
|
@ -232,8 +172,7 @@ Polygon::centroid() const
|
|||
|
||||
// find all concave vertices (i.e. having an internal angle greater than the supplied angle)
|
||||
// (external = right side, thus we consider ccw orientation)
|
||||
Points
|
||||
Polygon::concave_points(double angle) const
|
||||
Points Polygon::concave_points(double angle) const
|
||||
{
|
||||
Points points;
|
||||
angle = 2*PI - angle;
|
||||
|
@ -256,8 +195,7 @@ Polygon::concave_points(double angle) const
|
|||
|
||||
// find all convex vertices (i.e. having an internal angle smaller than the supplied angle)
|
||||
// (external = right side, thus we consider ccw orientation)
|
||||
Points
|
||||
Polygon::convex_points(double angle) const
|
||||
Points Polygon::convex_points(double angle) const
|
||||
{
|
||||
Points points;
|
||||
angle = 2*PI - angle;
|
||||
|
|
|
@ -13,13 +13,14 @@ namespace Slic3r {
|
|||
class Polygon;
|
||||
typedef std::vector<Polygon> Polygons;
|
||||
|
||||
class Polygon : public MultiPoint {
|
||||
class Polygon : public MultiPoint
|
||||
{
|
||||
public:
|
||||
operator Polygons() const;
|
||||
operator Polyline() const;
|
||||
Point& operator[](Points::size_type idx);
|
||||
const Point& operator[](Points::size_type idx) const;
|
||||
|
||||
operator Polygons() const { Polygons pp; pp.push_back(*this); return pp; }
|
||||
operator Polyline() const { return this->split_at_first_point(); }
|
||||
Point& operator[](Points::size_type idx) { return this->points[idx]; }
|
||||
const Point& operator[](Points::size_type idx) const { return this->points[idx]; }
|
||||
|
||||
Polygon() {}
|
||||
explicit Polygon(const Points &points): MultiPoint(points) {}
|
||||
Polygon(const Polygon &other) : MultiPoint(other.points) {}
|
||||
|
@ -34,20 +35,24 @@ public:
|
|||
Polygon& operator=(const Polygon &other) { points = other.points; return *this; }
|
||||
Polygon& operator=(Polygon &&other) { points = std::move(other.points); return *this; }
|
||||
|
||||
Point last_point() const;
|
||||
// last point == first point for polygons
|
||||
const Point& last_point() const override { return this->points.front(); }
|
||||
|
||||
virtual Lines lines() const;
|
||||
Polyline split_at_vertex(const Point &point) const;
|
||||
// Split a closed polygon into an open polyline, with the split point duplicated at both ends.
|
||||
Polyline split_at_index(int index) const;
|
||||
// Split a closed polygon into an open polyline, with the split point duplicated at both ends.
|
||||
Polyline split_at_first_point() const;
|
||||
Points equally_spaced_points(double distance) const;
|
||||
Polyline split_at_first_point() const { return this->split_at_index(0); }
|
||||
Points equally_spaced_points(double distance) const { return this->split_at_first_point().equally_spaced_points(distance); }
|
||||
|
||||
double area() const;
|
||||
bool is_counter_clockwise() const;
|
||||
bool is_clockwise() const;
|
||||
bool make_counter_clockwise();
|
||||
bool make_clockwise();
|
||||
bool is_valid() const;
|
||||
bool is_valid() const { return this->points.size() >= 3; }
|
||||
|
||||
// Does an unoriented polygon contain a point?
|
||||
// Tested by counting intersections along a horizontal line.
|
||||
bool contains(const Point &point) const;
|
||||
|
|
|
@ -23,18 +23,17 @@ Polyline::operator Line() const
|
|||
return Line(this->points.front(), this->points.back());
|
||||
}
|
||||
|
||||
Point
|
||||
Polyline::leftmost_point() const
|
||||
const Point& Polyline::leftmost_point() const
|
||||
{
|
||||
Point p = this->points.front();
|
||||
for (Points::const_iterator it = this->points.begin() + 1; it != this->points.end(); ++it) {
|
||||
if ((*it)(0) < p(0)) p = *it;
|
||||
const Point *p = &this->points.front();
|
||||
for (Points::const_iterator it = this->points.begin() + 1; it != this->points.end(); ++ it) {
|
||||
if (it->x() < p->x())
|
||||
p = &(*it);
|
||||
}
|
||||
return p;
|
||||
return *p;
|
||||
}
|
||||
|
||||
Lines
|
||||
Polyline::lines() const
|
||||
Lines Polyline::lines() const
|
||||
{
|
||||
Lines lines;
|
||||
if (this->points.size() >= 2) {
|
||||
|
@ -205,6 +204,20 @@ BoundingBox get_extents(const Polylines &polylines)
|
|||
return bb;
|
||||
}
|
||||
|
||||
const Point& leftmost_point(const Polylines &polylines)
|
||||
{
|
||||
if (polylines.empty())
|
||||
throw std::invalid_argument("leftmost_point() called on empty PolylineCollection");
|
||||
Polylines::const_iterator it = polylines.begin();
|
||||
const Point *p = &it->leftmost_point();
|
||||
for (++ it; it != polylines.end(); ++it) {
|
||||
const Point *p2 = &it->leftmost_point();
|
||||
if (p2->x() < p->x())
|
||||
p = p2;
|
||||
}
|
||||
return *p;
|
||||
}
|
||||
|
||||
bool remove_degenerate(Polylines &polylines)
|
||||
{
|
||||
bool modified = false;
|
||||
|
|
|
@ -62,9 +62,9 @@ public:
|
|||
|
||||
operator Polylines() const;
|
||||
operator Line() const;
|
||||
Point last_point() const override { return this->points.back(); }
|
||||
const Point& last_point() const override { return this->points.back(); }
|
||||
|
||||
Point leftmost_point() const;
|
||||
const Point& leftmost_point() const;
|
||||
virtual Lines lines() const;
|
||||
void clip_end(double distance);
|
||||
void clip_start(double distance);
|
||||
|
@ -77,6 +77,15 @@ public:
|
|||
bool is_straight() const;
|
||||
};
|
||||
|
||||
// Don't use this class in production code, it is used exclusively by the Perl binding for unit tests!
|
||||
#ifdef PERL_UCHAR_MIN
|
||||
class PolylineCollection
|
||||
{
|
||||
public:
|
||||
Polylines polylines;
|
||||
};
|
||||
#endif /* PERL_UCHAR_MIN */
|
||||
|
||||
extern BoundingBox get_extents(const Polyline &polyline);
|
||||
extern BoundingBox get_extents(const Polylines &polylines);
|
||||
|
||||
|
@ -129,6 +138,8 @@ inline void polylines_append(Polylines &dst, Polylines &&src)
|
|||
}
|
||||
}
|
||||
|
||||
const Point& leftmost_point(const Polylines &polylines);
|
||||
|
||||
bool remove_degenerate(Polylines &polylines);
|
||||
|
||||
class ThickPolyline : public Polyline {
|
||||
|
|
|
@ -1,92 +0,0 @@
|
|||
#include "PolylineCollection.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
struct Chaining
|
||||
{
|
||||
Point first;
|
||||
Point last;
|
||||
size_t idx;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
inline int nearest_point_index(const std::vector<Chaining> &pairs, const Point &start_near, bool no_reverse)
|
||||
{
|
||||
T dmin = std::numeric_limits<T>::max();
|
||||
int idx = 0;
|
||||
for (std::vector<Chaining>::const_iterator it = pairs.begin(); it != pairs.end(); ++it) {
|
||||
T d = sqr(T(start_near(0) - it->first(0)));
|
||||
if (d <= dmin) {
|
||||
d += sqr(T(start_near(1) - it->first(1)));
|
||||
if (d < dmin) {
|
||||
idx = (it - pairs.begin()) * 2;
|
||||
dmin = d;
|
||||
if (dmin < EPSILON)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (! no_reverse) {
|
||||
d = sqr(T(start_near(0) - it->last(0)));
|
||||
if (d <= dmin) {
|
||||
d += sqr(T(start_near(1) - it->last(1)));
|
||||
if (d < dmin) {
|
||||
idx = (it - pairs.begin()) * 2 + 1;
|
||||
dmin = d;
|
||||
if (dmin < EPSILON)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return idx;
|
||||
}
|
||||
|
||||
Polylines PolylineCollection::_chained_path_from(
|
||||
const Polylines &src,
|
||||
Point start_near,
|
||||
bool no_reverse,
|
||||
bool move_from_src)
|
||||
{
|
||||
std::vector<Chaining> endpoints;
|
||||
endpoints.reserve(src.size());
|
||||
for (size_t i = 0; i < src.size(); ++ i) {
|
||||
Chaining c;
|
||||
c.first = src[i].first_point();
|
||||
if (! no_reverse)
|
||||
c.last = src[i].last_point();
|
||||
c.idx = i;
|
||||
endpoints.push_back(c);
|
||||
}
|
||||
Polylines retval;
|
||||
while (! endpoints.empty()) {
|
||||
// find nearest point
|
||||
int endpoint_index = nearest_point_index<double>(endpoints, start_near, no_reverse);
|
||||
assert(endpoint_index >= 0 && size_t(endpoint_index) < endpoints.size() * 2);
|
||||
if (move_from_src) {
|
||||
retval.push_back(std::move(src[endpoints[endpoint_index/2].idx]));
|
||||
} else {
|
||||
retval.push_back(src[endpoints[endpoint_index/2].idx]);
|
||||
}
|
||||
if (endpoint_index & 1)
|
||||
retval.back().reverse();
|
||||
endpoints.erase(endpoints.begin() + endpoint_index/2);
|
||||
start_near = retval.back().last_point();
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
Point PolylineCollection::leftmost_point(const Polylines &polylines)
|
||||
{
|
||||
if (polylines.empty())
|
||||
throw std::invalid_argument("leftmost_point() called on empty PolylineCollection");
|
||||
Polylines::const_iterator it = polylines.begin();
|
||||
Point p = it->leftmost_point();
|
||||
for (++ it; it != polylines.end(); ++it) {
|
||||
Point p2 = it->leftmost_point();
|
||||
if (p2(0) < p(0))
|
||||
p = p2;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
|
@ -1,47 +0,0 @@
|
|||
#ifndef slic3r_PolylineCollection_hpp_
|
||||
#define slic3r_PolylineCollection_hpp_
|
||||
|
||||
#include "libslic3r.h"
|
||||
#include "Polyline.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class PolylineCollection
|
||||
{
|
||||
static Polylines _chained_path_from(
|
||||
const Polylines &src,
|
||||
Point start_near,
|
||||
bool no_reverse,
|
||||
bool move_from_src);
|
||||
|
||||
public:
|
||||
Polylines polylines;
|
||||
void chained_path(PolylineCollection* retval, bool no_reverse = false) const
|
||||
{ retval->polylines = chained_path(this->polylines, no_reverse); }
|
||||
void chained_path_from(Point start_near, PolylineCollection* retval, bool no_reverse = false) const
|
||||
{ retval->polylines = chained_path_from(this->polylines, start_near, no_reverse); }
|
||||
Point leftmost_point() const
|
||||
{ return leftmost_point(polylines); }
|
||||
void append(const Polylines &polylines)
|
||||
{ this->polylines.insert(this->polylines.end(), polylines.begin(), polylines.end()); }
|
||||
|
||||
static Point leftmost_point(const Polylines &polylines);
|
||||
static Polylines chained_path(Polylines &&src, bool no_reverse = false) {
|
||||
return (src.empty() || src.front().points.empty()) ?
|
||||
Polylines() :
|
||||
_chained_path_from(src, src.front().first_point(), no_reverse, true);
|
||||
}
|
||||
static Polylines chained_path_from(Polylines &&src, Point start_near, bool no_reverse = false)
|
||||
{ return _chained_path_from(src, start_near, no_reverse, true); }
|
||||
static Polylines chained_path(const Polylines &src, bool no_reverse = false) {
|
||||
return (src.empty() || src.front().points.empty()) ?
|
||||
Polylines() :
|
||||
_chained_path_from(src, src.front().first_point(), no_reverse, false);
|
||||
}
|
||||
static Polylines chained_path_from(const Polylines &src, Point start_near, bool no_reverse = false)
|
||||
{ return _chained_path_from(src, start_near, no_reverse, false); }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -14,6 +14,44 @@
|
|||
|
||||
namespace Slic3r {
|
||||
|
||||
// Naive implementation of the Traveling Salesman Problem, it works by always taking the next closest neighbor.
|
||||
// This implementation will always produce valid result even if some segments cannot reverse.
|
||||
template<typename EndPointType, typename KDTreeType, typename CouldReverseFunc>
|
||||
std::vector<std::pair<size_t, bool>> chain_segments_closest_point(std::vector<EndPointType> &end_points, KDTreeType &kdtree, CouldReverseFunc &could_reverse_func, EndPointType &first_point)
|
||||
{
|
||||
assert((end_points.size() & 1) == 0);
|
||||
size_t num_segments = end_points.size() / 2;
|
||||
assert(num_segments >= 2);
|
||||
for (EndPointType &ep : end_points)
|
||||
ep.chain_id = 0;
|
||||
std::vector<std::pair<size_t, bool>> out;
|
||||
out.reserve(num_segments);
|
||||
size_t first_point_idx = &first_point - end_points.data();
|
||||
out.emplace_back(first_point_idx / 2, (first_point_idx & 1) != 0);
|
||||
first_point.chain_id = 1;
|
||||
size_t this_idx = first_point_idx ^ 1;
|
||||
for (int iter = (int)num_segments - 2; iter >= 0; -- iter) {
|
||||
EndPointType &this_point = end_points[this_idx];
|
||||
this_point.chain_id = 1;
|
||||
// Find the closest point to this end_point, which lies on a different extrusion path (filtered by the lambda).
|
||||
// Ignore the starting point as the starting point is considered to be occupied, no end point coud connect to it.
|
||||
size_t next_idx = find_closest_point(kdtree, this_point.pos,
|
||||
[this_idx, &end_points, &could_reverse_func](size_t idx) {
|
||||
return (idx ^ this_idx) > 1 && end_points[idx].chain_id == 0 && ((idx ^ 1) == 0 || could_reverse_func(idx >> 1));
|
||||
});
|
||||
assert(next_idx < end_points.size());
|
||||
EndPointType &end_point = end_points[next_idx];
|
||||
end_point.chain_id = 1;
|
||||
this_idx = next_idx ^ 1;
|
||||
}
|
||||
#ifndef _NDEBUG
|
||||
assert(end_points[this_idx].chain_id == 0);
|
||||
for (EndPointType &ep : end_points)
|
||||
assert(&ep == &end_points[this_idx] || ep.chain_id == 1);
|
||||
#endif /* _NDEBUG */
|
||||
return out;
|
||||
}
|
||||
|
||||
// Chain perimeters (always closed) and thin fills (closed or open) using a greedy algorithm.
|
||||
// Solving a Traveling Salesman Problem (TSP) with the modification, that the sites are not always points, but points and segments.
|
||||
// Solving using a greedy algorithm, where a shortest edge is added to the solution if it does not produce a bifurcation or a cycle.
|
||||
|
@ -22,8 +60,8 @@ namespace Slic3r {
|
|||
// The algorithm builds a tour for the traveling salesman one edge at a time and thus maintains multiple tour fragments, each of which
|
||||
// is a simple path in the complete graph of cities. At each stage, the algorithm selects the edge of minimal cost that either creates
|
||||
// a new fragment, extends one of the existing paths or creates a cycle of length equal to the number of cities.
|
||||
template<typename PointType, typename SegmentEndPointFunc>
|
||||
std::vector<std::pair<size_t, bool>> chain_segments(SegmentEndPointFunc end_point_func, size_t num_segments, const PointType *start_near)
|
||||
template<typename PointType, typename SegmentEndPointFunc, bool REVERSE_COULD_FAIL, typename CouldReverseFunc>
|
||||
std::vector<std::pair<size_t, bool>> chain_segments_greedy_constrained_reversals_(SegmentEndPointFunc end_point_func, CouldReverseFunc could_reverse_func, size_t num_segments, const PointType *start_near)
|
||||
{
|
||||
std::vector<std::pair<size_t, bool>> out;
|
||||
|
||||
|
@ -132,6 +170,8 @@ std::vector<std::pair<size_t, bool>> chain_segments(SegmentEndPointFunc end_poin
|
|||
first_point->chain_id = equivalent_chain.next();
|
||||
first_point_idx = idx;
|
||||
}
|
||||
EndPoint *initial_point = first_point;
|
||||
EndPoint *last_point = nullptr;
|
||||
|
||||
// Assign the closest point and distance to the end points.
|
||||
for (EndPoint &end_point : end_points) {
|
||||
|
@ -240,12 +280,15 @@ std::vector<std::pair<size_t, bool>> chain_segments(SegmentEndPointFunc end_poin
|
|||
if (iter == 0) {
|
||||
// Last iteration. There shall be exactly one or two end points waiting to be connected.
|
||||
assert(queue.size() == ((first_point == nullptr) ? 2 : 1));
|
||||
if (first_point == nullptr)
|
||||
if (first_point == nullptr) {
|
||||
first_point = queue.top();
|
||||
while (! queue.empty()) {
|
||||
queue.top()->edge_out = nullptr;
|
||||
queue.pop();
|
||||
first_point->edge_out = nullptr;
|
||||
}
|
||||
last_point = queue.top();
|
||||
last_point->edge_out = nullptr;
|
||||
queue.pop();
|
||||
assert(queue.empty());
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
|
@ -280,24 +323,77 @@ std::vector<std::pair<size_t, bool>> chain_segments(SegmentEndPointFunc end_poin
|
|||
|
||||
// Now interconnect pairs of segments into a chain.
|
||||
assert(first_point != nullptr);
|
||||
out.reserve(num_segments);
|
||||
bool failed = false;
|
||||
do {
|
||||
assert(out.size() < num_segments);
|
||||
size_t first_point_id = first_point - &end_points.front();
|
||||
size_t segment_id = first_point_id >> 1;
|
||||
bool reverse = (first_point_id & 1) != 0;
|
||||
EndPoint *second_point = &end_points[first_point_id ^ 1];
|
||||
out.emplace_back(segment_id, (first_point_id & 1) != 0);
|
||||
if (REVERSE_COULD_FAIL) {
|
||||
if (reverse && ! could_reverse_func(segment_id)) {
|
||||
failed = true;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
assert(! reverse || could_reverse_func(segment_id));
|
||||
}
|
||||
out.emplace_back(segment_id, reverse);
|
||||
first_point = second_point->edge_out;
|
||||
} while (first_point != nullptr);
|
||||
if (REVERSE_COULD_FAIL) {
|
||||
if (failed) {
|
||||
if (start_near == nullptr) {
|
||||
// We may try the reverse order.
|
||||
out.clear();
|
||||
first_point = last_point;
|
||||
failed = false;
|
||||
do {
|
||||
assert(out.size() < num_segments);
|
||||
size_t first_point_id = first_point - &end_points.front();
|
||||
size_t segment_id = first_point_id >> 1;
|
||||
bool reverse = (first_point_id & 1) != 0;
|
||||
EndPoint *second_point = &end_points[first_point_id ^ 1];
|
||||
if (reverse && ! could_reverse_func(segment_id)) {
|
||||
failed = true;
|
||||
break;
|
||||
}
|
||||
out.emplace_back(segment_id, reverse);
|
||||
first_point = second_point->edge_out;
|
||||
} while (first_point != nullptr);
|
||||
}
|
||||
}
|
||||
if (failed)
|
||||
// As a last resort, try a dumb algorithm, which is not sensitive to edge reversal constraints.
|
||||
out = chain_segments_closest_point<EndPoint, decltype(kdtree), CouldReverseFunc>(end_points, kdtree, could_reverse_func, (initial_point != nullptr) ? *initial_point : end_points.front());
|
||||
} else {
|
||||
assert(! failed);
|
||||
}
|
||||
}
|
||||
|
||||
assert(out.size() == num_segments);
|
||||
return out;
|
||||
}
|
||||
|
||||
template<typename PointType, typename SegmentEndPointFunc, typename CouldReverseFunc>
|
||||
std::vector<std::pair<size_t, bool>> chain_segments_greedy_constrained_reversals(SegmentEndPointFunc end_point_func, CouldReverseFunc could_reverse_func, size_t num_segments, const PointType *start_near)
|
||||
{
|
||||
return chain_segments_greedy_constrained_reversals_<PointType, SegmentEndPointFunc, true, CouldReverseFunc>(end_point_func, could_reverse_func, num_segments, start_near);
|
||||
}
|
||||
|
||||
template<typename PointType, typename SegmentEndPointFunc>
|
||||
std::vector<std::pair<size_t, bool>> chain_segments_greedy(SegmentEndPointFunc end_point_func, size_t num_segments, const PointType *start_near)
|
||||
{
|
||||
auto could_reverse_func = [](size_t /* idx */) -> bool { return true; };
|
||||
return chain_segments_greedy_constrained_reversals_<PointType, SegmentEndPointFunc, false, decltype(could_reverse_func)>(end_point_func, could_reverse_func, num_segments, start_near);
|
||||
}
|
||||
|
||||
std::vector<std::pair<size_t, bool>> chain_extrusion_entities(std::vector<ExtrusionEntity*> &entities, const Point *start_near)
|
||||
{
|
||||
auto segment_end_point = [&entities](size_t idx, bool first_point) -> const Point& { return first_point ? entities[idx]->first_point() : entities[idx]->last_point(); };
|
||||
std::vector<std::pair<size_t, bool>> out = chain_segments<Point, decltype(segment_end_point)>(segment_end_point, entities.size(), start_near);
|
||||
auto could_reverse = [&entities](size_t idx) { const ExtrusionEntity *ee = entities[idx]; return ee->is_loop() || ee->can_reverse(); };
|
||||
std::vector<std::pair<size_t, bool>> out = chain_segments_greedy_constrained_reversals<Point, decltype(segment_end_point), decltype(could_reverse)>(segment_end_point, could_reverse, entities.size(), start_near);
|
||||
for (size_t i = 0; i < entities.size(); ++ i) {
|
||||
ExtrusionEntity *ee = entities[i];
|
||||
if (ee->is_loop())
|
||||
|
@ -328,10 +424,34 @@ void chain_and_reorder_extrusion_entities(std::vector<ExtrusionEntity*> &entitie
|
|||
reorder_extrusion_entities(entities, chain_extrusion_entities(entities, start_near));
|
||||
}
|
||||
|
||||
std::vector<std::pair<size_t, bool>> chain_extrusion_paths(std::vector<ExtrusionPath> &extrusion_paths, const Point *start_near)
|
||||
{
|
||||
auto segment_end_point = [&extrusion_paths](size_t idx, bool first_point) -> const Point& { return first_point ? extrusion_paths[idx].first_point() : extrusion_paths[idx].last_point(); };
|
||||
return chain_segments_greedy<Point, decltype(segment_end_point)>(segment_end_point, extrusion_paths.size(), start_near);
|
||||
}
|
||||
|
||||
void reorder_extrusion_paths(std::vector<ExtrusionPath> &extrusion_paths, std::vector<std::pair<size_t, bool>> &chain)
|
||||
{
|
||||
assert(extrusion_paths.size() == chain.size());
|
||||
std::vector<ExtrusionPath> out;
|
||||
out.reserve(extrusion_paths.size());
|
||||
for (const std::pair<size_t, bool> &idx : chain) {
|
||||
out.emplace_back(std::move(extrusion_paths[idx.first]));
|
||||
if (idx.second)
|
||||
out.back().reverse();
|
||||
}
|
||||
extrusion_paths.swap(out);
|
||||
}
|
||||
|
||||
void chain_and_reorder_extrusion_paths(std::vector<ExtrusionPath> &extrusion_paths, const Point *start_near)
|
||||
{
|
||||
reorder_extrusion_paths(extrusion_paths, chain_extrusion_paths(extrusion_paths, start_near));
|
||||
}
|
||||
|
||||
std::vector<size_t> chain_points(const Points &points, Point *start_near)
|
||||
{
|
||||
auto segment_end_point = [&points](size_t idx, bool /* first_point */) -> const Point& { return points[idx]; };
|
||||
std::vector<std::pair<size_t, bool>> ordered = chain_segments<Point, decltype(segment_end_point)>(segment_end_point, points.size(), start_near);
|
||||
std::vector<std::pair<size_t, bool>> ordered = chain_segments_greedy<Point, decltype(segment_end_point)>(segment_end_point, points.size(), start_near);
|
||||
std::vector<size_t> out;
|
||||
out.reserve(ordered.size());
|
||||
for (auto &segment_and_reversal : ordered)
|
||||
|
@ -339,12 +459,12 @@ std::vector<size_t> chain_points(const Points &points, Point *start_near)
|
|||
return out;
|
||||
}
|
||||
|
||||
Polylines chain_infill_polylines(Polylines &polylines)
|
||||
Polylines chain_polylines(Polylines &polylines, const Point *start_near)
|
||||
{
|
||||
auto segment_end_point = [&polylines](size_t idx, bool first_point) -> const Point& { return first_point ? polylines[idx].first_point() : polylines[idx].last_point(); };
|
||||
std::vector<std::pair<size_t, bool>> ordered = chain_segments<Point, decltype(segment_end_point)>(segment_end_point, polylines.size(), nullptr);
|
||||
std::vector<std::pair<size_t, bool>> ordered = chain_segments_greedy<Point, decltype(segment_end_point)>(segment_end_point, polylines.size(), start_near);
|
||||
Polylines out;
|
||||
out.reserve(polylines.size());
|
||||
out.reserve(polylines.size());
|
||||
for (auto &segment_and_reversal : ordered) {
|
||||
out.emplace_back(std::move(polylines[segment_and_reversal.first]));
|
||||
if (segment_and_reversal.second)
|
||||
|
@ -356,7 +476,7 @@ Polylines chain_infill_polylines(Polylines &polylines)
|
|||
template<class T> static inline T chain_path_items(const Points &points, const T &items)
|
||||
{
|
||||
auto segment_end_point = [&points](size_t idx, bool /* first_point */) -> const Point& { return points[idx]; };
|
||||
std::vector<std::pair<size_t, bool>> ordered = chain_segments<Point, decltype(segment_end_point)>(segment_end_point, points.size(), nullptr);
|
||||
std::vector<std::pair<size_t, bool>> ordered = chain_segments_greedy<Point, decltype(segment_end_point)>(segment_end_point, points.size(), nullptr);
|
||||
T out;
|
||||
out.reserve(items.size());
|
||||
for (auto &segment_and_reversal : ordered)
|
||||
|
@ -382,7 +502,7 @@ std::vector<std::pair<size_t, size_t>> chain_print_object_instances(const Print
|
|||
}
|
||||
}
|
||||
auto segment_end_point = [&object_reference_points](size_t idx, bool /* first_point */) -> const Point& { return object_reference_points[idx]; };
|
||||
std::vector<std::pair<size_t, bool>> ordered = chain_segments<Point, decltype(segment_end_point)>(segment_end_point, instances.size(), nullptr);
|
||||
std::vector<std::pair<size_t, bool>> ordered = chain_segments_greedy<Point, decltype(segment_end_point)>(segment_end_point, instances.size(), nullptr);
|
||||
std::vector<std::pair<size_t, size_t>> out;
|
||||
out.reserve(instances.size());
|
||||
for (auto &segment_and_reversal : ordered)
|
||||
|
|
|
@ -18,7 +18,11 @@ std::vector<std::pair<size_t, bool>> chain_extrusion_entities(std::vector<Extrus
|
|||
void reorder_extrusion_entities(std::vector<ExtrusionEntity*> &entities, std::vector<std::pair<size_t, bool>> &chain);
|
||||
void chain_and_reorder_extrusion_entities(std::vector<ExtrusionEntity*> &entities, const Point *start_near = nullptr);
|
||||
|
||||
Polylines chain_infill_polylines(Polylines &src);
|
||||
std::vector<std::pair<size_t, bool>> chain_extrusion_paths(std::vector<ExtrusionPath> &extrusion_paths, const Point *start_near = nullptr);
|
||||
void reorder_extrusion_paths(std::vector<ExtrusionPath> &extrusion_paths, std::vector<std::pair<size_t, bool>> &chain);
|
||||
void chain_and_reorder_extrusion_paths(std::vector<ExtrusionPath> &extrusion_paths, const Point *start_near = nullptr);
|
||||
|
||||
Polylines chain_polylines(Polylines &src, const Point *start_near = nullptr);
|
||||
|
||||
std::vector<ClipperLib::PolyNode*> chain_clipper_polynodes(const Points &points, const std::vector<ClipperLib::PolyNode*> &items);
|
||||
|
||||
|
|
|
@ -923,7 +923,7 @@ namespace SupportMaterialInternal {
|
|||
//FIXME add supports at regular intervals to support long bridges!
|
||||
bridges = diff(bridges,
|
||||
// Offset unsupported edges into polygons.
|
||||
offset(layerm->unsupported_bridge_edges.polylines, scale_(SUPPORT_MATERIAL_MARGIN), SUPPORT_SURFACES_OFFSET_PARAMETERS));
|
||||
offset(layerm->unsupported_bridge_edges, scale_(SUPPORT_MATERIAL_MARGIN), SUPPORT_SURFACES_OFFSET_PARAMETERS));
|
||||
// Remove bridged areas from the supported areas.
|
||||
contact_polygons = diff(contact_polygons, bridges, true);
|
||||
}
|
||||
|
|
|
@ -14,13 +14,17 @@
|
|||
void clear();
|
||||
ExtrusionEntityCollection* chained_path(bool no_reverse, ExtrusionRole role = erMixed)
|
||||
%code{%
|
||||
if (no_reverse)
|
||||
croak("no_reverse must be false");
|
||||
RETVAL = new ExtrusionEntityCollection();
|
||||
THIS->chained_path(RETVAL, no_reverse, role);
|
||||
*RETVAL = THIS->chained_path_from(THIS->entities.front()->first_point());
|
||||
%};
|
||||
ExtrusionEntityCollection* chained_path_from(Point* start_near, bool no_reverse, ExtrusionRole role = erMixed)
|
||||
%code{%
|
||||
if (no_reverse)
|
||||
croak("no_reverse must be false");
|
||||
RETVAL = new ExtrusionEntityCollection();
|
||||
THIS->chained_path_from(*start_near, RETVAL, no_reverse, role);
|
||||
*RETVAL = THIS->chained_path_from(*start_near, role);
|
||||
%};
|
||||
Clone<Point> first_point();
|
||||
Clone<Point> last_point();
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
%{
|
||||
#include <xsinit.h>
|
||||
#include "libslic3r/Fill/Fill.hpp"
|
||||
#include "libslic3r/PolylineCollection.hpp"
|
||||
#include "libslic3r/ExtrusionEntity.hpp"
|
||||
#include "libslic3r/ExtrusionEntityCollection.hpp"
|
||||
%}
|
||||
|
|
|
@ -19,8 +19,6 @@
|
|||
%code%{ RETVAL = &THIS->fill_surfaces; %};
|
||||
Polygons bridged()
|
||||
%code%{ RETVAL = THIS->bridged; %};
|
||||
Ref<PolylineCollection> unsupported_bridge_edges()
|
||||
%code%{ RETVAL = &THIS->unsupported_bridge_edges; %};
|
||||
Ref<ExtrusionEntityCollection> perimeters()
|
||||
%code%{ RETVAL = &THIS->perimeters; %};
|
||||
Ref<ExtrusionEntityCollection> fills()
|
||||
|
|
|
@ -2,7 +2,11 @@
|
|||
|
||||
%{
|
||||
#include <xsinit.h>
|
||||
#include "libslic3r/PolylineCollection.hpp"
|
||||
|
||||
#include "libslic3r.h"
|
||||
#include "Polyline.hpp"
|
||||
#include "ShortestPath.hpp"
|
||||
|
||||
%}
|
||||
|
||||
%name{Slic3r::Polyline::Collection} class PolylineCollection {
|
||||
|
@ -14,16 +18,15 @@
|
|||
PolylineCollection* chained_path(bool no_reverse)
|
||||
%code{%
|
||||
RETVAL = new PolylineCollection();
|
||||
THIS->chained_path(RETVAL, no_reverse);
|
||||
RETVAL->polylines = chain_polylines(THIS->polylines, &THIS->polylines.front().first_point());
|
||||
%};
|
||||
PolylineCollection* chained_path_from(Point* start_near, bool no_reverse)
|
||||
%code{%
|
||||
RETVAL = new PolylineCollection();
|
||||
THIS->chained_path_from(*start_near, RETVAL, no_reverse);
|
||||
RETVAL->polylines = chain_polylines(THIS->polylines, start_near);
|
||||
%};
|
||||
int count()
|
||||
%code{% RETVAL = THIS->polylines.size(); %};
|
||||
Clone<Point> leftmost_point();
|
||||
%{
|
||||
|
||||
PolylineCollection*
|
||||
|
|
Loading…
Reference in a new issue