diff --git a/resources/data/sla_support.svg b/resources/data/sla_support.svg
new file mode 100644
index 0000000000..d131315194
--- /dev/null
+++ b/resources/data/sla_support.svg
@@ -0,0 +1,361 @@
+
+
+
+
diff --git a/resources/icons/sphere_blueish.svg b/resources/icons/sphere_blueish.svg
new file mode 100644
index 0000000000..39aba38de2
--- /dev/null
+++ b/resources/icons/sphere_blueish.svg
@@ -0,0 +1,12 @@
+
diff --git a/resources/icons/sphere_cyan.svg b/resources/icons/sphere_cyan.svg
new file mode 100644
index 0000000000..5402fc301a
--- /dev/null
+++ b/resources/icons/sphere_cyan.svg
@@ -0,0 +1,12 @@
+
diff --git a/resources/icons/sphere_lightgray.svg b/resources/icons/sphere_lightgray.svg
new file mode 100644
index 0000000000..fcf383f4bf
--- /dev/null
+++ b/resources/icons/sphere_lightgray.svg
@@ -0,0 +1,12 @@
+
diff --git a/resources/icons/sphere_orange.svg b/resources/icons/sphere_orange.svg
new file mode 100644
index 0000000000..2a4382d3f6
--- /dev/null
+++ b/resources/icons/sphere_orange.svg
@@ -0,0 +1,12 @@
+
diff --git a/resources/icons/sphere_redish.svg b/resources/icons/sphere_redish.svg
new file mode 100644
index 0000000000..83a26f4a14
--- /dev/null
+++ b/resources/icons/sphere_redish.svg
@@ -0,0 +1,12 @@
+
diff --git a/resources/icons/support_structure.svg b/resources/icons/support_structure.svg
new file mode 100644
index 0000000000..efa7d455cd
--- /dev/null
+++ b/resources/icons/support_structure.svg
@@ -0,0 +1,68 @@
+
+
diff --git a/resources/icons/support_structure_invisible.svg b/resources/icons/support_structure_invisible.svg
new file mode 100644
index 0000000000..585db7c0ac
--- /dev/null
+++ b/resources/icons/support_structure_invisible.svg
@@ -0,0 +1,228 @@
+
+
diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt
index d8c8d1e069..f7773673ae 100644
--- a/src/libslic3r/CMakeLists.txt
+++ b/src/libslic3r/CMakeLists.txt
@@ -452,7 +452,37 @@ set(SLIC3R_SOURCES
SLA/BranchingTreeSLA.hpp
SLA/BranchingTreeSLA.cpp
SLA/ZCorrection.hpp
- SLA/ZCorrection.cpp
+ SLA/ZCorrection.cpp
+ SLA/SupportIslands/EvaluateNeighbor.cpp
+ SLA/SupportIslands/EvaluateNeighbor.hpp
+ SLA/SupportIslands/ExpandNeighbor.cpp
+ SLA/SupportIslands/ExpandNeighbor.hpp
+ SLA/SupportIslands/IStackFunction.hpp
+ SLA/SupportIslands/LineUtils.cpp
+ SLA/SupportIslands/LineUtils.hpp
+ SLA/SupportIslands/NodeDataWithResult.hpp
+ SLA/SupportIslands/Parabola.hpp
+ SLA/SupportIslands/ParabolaUtils.cpp
+ SLA/SupportIslands/ParabolaUtils.hpp
+ SLA/SupportIslands/PointUtils.cpp
+ SLA/SupportIslands/PointUtils.hpp
+ SLA/SupportIslands/PolygonUtils.cpp
+ SLA/SupportIslands/PolygonUtils.hpp
+ SLA/SupportIslands/PostProcessNeighbor.cpp
+ SLA/SupportIslands/PostProcessNeighbor.hpp
+ SLA/SupportIslands/PostProcessNeighbors.cpp
+ SLA/SupportIslands/PostProcessNeighbors.hpp
+ SLA/SupportIslands/SampleConfig.hpp
+ SLA/SupportIslands/SampleConfigFactory.cpp
+ SLA/SupportIslands/SampleConfigFactory.hpp
+ SLA/SupportIslands/SupportIslandPoint.cpp
+ SLA/SupportIslands/SupportIslandPoint.hpp
+ SLA/SupportIslands/UniformSupportIsland.cpp
+ SLA/SupportIslands/UniformSupportIsland.hpp
+ SLA/SupportIslands/VectorUtils.hpp
+ SLA/SupportIslands/VoronoiGraph.hpp
+ SLA/SupportIslands/VoronoiGraphUtils.cpp
+ SLA/SupportIslands/VoronoiGraphUtils.hpp
BranchingTree/BranchingTree.cpp
BranchingTree/BranchingTree.hpp
BranchingTree/PointCloud.cpp
@@ -524,6 +554,9 @@ foreach(_source IN ITEMS ${SLIC3R_SOURCES})
source_group("${_group_path}" FILES "${_source}")
endforeach()
+# Create the source groups for source tree with root at CMAKE_CURRENT_SOURCE_DIR.
+source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${SOURCE_LIST_SLA})
+
if (SLIC3R_STATIC)
set(CGAL_Boost_USE_STATIC_LIBS ON CACHE BOOL "" FORCE)
endif ()
@@ -541,6 +574,7 @@ add_library(libslic3r_cgal STATIC
MeshBoolean.hpp MeshBoolean.cpp
TryCatchSignal.hpp TryCatchSignal.cpp
Triangulation.hpp Triangulation.cpp
+ SLA/SupportIslands/VoronoiDiagramCGAL.hpp SLA/SupportIslands/VoronoiDiagramCGAL.cpp
)
target_include_directories(libslic3r_cgal PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
target_include_directories(libslic3r_cgal PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/..)
diff --git a/src/libslic3r/ClipperUtils.cpp b/src/libslic3r/ClipperUtils.cpp
index a5ff5a2591..cc2b49be2f 100644
--- a/src/libslic3r/ClipperUtils.cpp
+++ b/src/libslic3r/ClipperUtils.cpp
@@ -726,6 +726,8 @@ Slic3r::Polygons diff(const Slic3r::Surfaces &subject, const Slic3r::Polygons &c
{ return _clipper(ClipperLib::ctDifference, ClipperUtils::SurfacesProvider(subject), ClipperUtils::PolygonsProvider(clip), do_safety_offset); }
Slic3r::Polygons intersection(const Slic3r::Polygon &subject, const Slic3r::Polygon &clip, ApplySafetyOffset do_safety_offset)
{ return _clipper(ClipperLib::ctIntersection, ClipperUtils::SinglePathProvider(subject.points), ClipperUtils::SinglePathProvider(clip.points), do_safety_offset); }
+Slic3r::Polygons intersection(const Slic3r::Polygon &subject, const Slic3r::ExPolygon &clip, ApplySafetyOffset do_safety_offset)
+ { return _clipper(ClipperLib::ctIntersection, ClipperUtils::SinglePathProvider(subject.points), ClipperUtils::ExPolygonProvider(clip), do_safety_offset); }
Slic3r::Polygons intersection_clipped(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset)
{ return intersection(subject, ClipperUtils::clip_clipper_polygons_with_subject_bbox(clip, get_extents(subject).inflated(SCALED_EPSILON)), do_safety_offset); }
Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::ExPolygon &clip, ApplySafetyOffset do_safety_offset)
@@ -778,6 +780,8 @@ Slic3r::ExPolygons diff_ex(const Slic3r::ExPolygon &subject, const Slic3r::Polyg
{ return _clipper_ex(ClipperLib::ctDifference, ClipperUtils::ExPolygonProvider(subject), ClipperUtils::SinglePathProvider(clip.points), do_safety_offset); }
Slic3r::ExPolygons diff_ex(const Slic3r::ExPolygon &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset)
{ return _clipper_ex(ClipperLib::ctDifference, ClipperUtils::ExPolygonProvider(subject), ClipperUtils::PolygonsProvider(clip), do_safety_offset); }
+Slic3r::ExPolygons diff_ex(const Slic3r::ExPolygon &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset)
+ { return _clipper_ex(ClipperLib::ctDifference, ClipperUtils::ExPolygonProvider(subject), ClipperUtils::ExPolygonsProvider(clip), do_safety_offset); }
Slic3r::ExPolygons diff_ex(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset)
{ return _clipper_ex(ClipperLib::ctDifference, ClipperUtils::ExPolygonsProvider(subject), ClipperUtils::PolygonsProvider(clip), do_safety_offset); }
Slic3r::ExPolygons diff_ex(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset)
@@ -1037,8 +1041,7 @@ Polygons union_parallel_reduce(const Polygons &subject)
});
}
-Polygons simplify_polygons(const Polygons &subject)
-{
+Polygons simplify_polygons(const Polygons &subject) {
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
ClipperLib::Paths output;
@@ -1048,27 +1051,9 @@ Polygons simplify_polygons(const Polygons &subject)
c.StrictlySimple(true);
c.AddPaths(ClipperUtils::PolygonsProvider(subject), ClipperLib::ptSubject, true);
c.Execute(ClipperLib::ctUnion, output, ClipperLib::pftNonZero, ClipperLib::pftNonZero);
-
- // convert into Slic3r polygons
return to_polygons(std::move(output));
}
-ExPolygons simplify_polygons_ex(const Polygons &subject, bool preserve_collinear)
-{
- CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
-
- ClipperLib::PolyTree polytree;
- ClipperLib::Clipper c;
-// c.PreserveCollinear(true);
- //FIXME StrictlySimple is very expensive! Is it needed?
- c.StrictlySimple(true);
- c.AddPaths(ClipperUtils::PolygonsProvider(subject), ClipperLib::ptSubject, true);
- c.Execute(ClipperLib::ctUnion, polytree, ClipperLib::pftNonZero, ClipperLib::pftNonZero);
-
- // convert into ExPolygons
- return PolyTreeToExPolygons(std::move(polytree));
-}
-
Polygons top_level_islands(const Slic3r::Polygons &polygons)
{
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
diff --git a/src/libslic3r/ClipperUtils.hpp b/src/libslic3r/ClipperUtils.hpp
index d38bd2aa83..2b6fad550b 100644
--- a/src/libslic3r/ClipperUtils.hpp
+++ b/src/libslic3r/ClipperUtils.hpp
@@ -454,6 +454,7 @@ Slic3r::ExPolygons diff_ex(const Slic3r::Polygons &subject, const Slic3r::Surfac
Slic3r::ExPolygons diff_ex(const Slic3r::Polygon &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::ExPolygons diff_ex(const Slic3r::ExPolygon &subject, const Slic3r::Polygon &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::ExPolygons diff_ex(const Slic3r::ExPolygon &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
+Slic3r::ExPolygons diff_ex(const Slic3r::ExPolygon &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::ExPolygons diff_ex(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::ExPolygons diff_ex(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::ExPolygons diff_ex(const Slic3r::Surfaces &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
@@ -477,6 +478,7 @@ inline Slic3r::Lines diff_ln(const Slic3r::Lines &subject, const Slic3r::Polygon
// Safety offset is applied to the clipping polygons only.
Slic3r::Polygons intersection(const Slic3r::Polygon &subject, const Slic3r::Polygon &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
+Slic3r::Polygons intersection(const Slic3r::Polygon &subject, const Slic3r::ExPolygon &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::ExPolygon &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
// Optimized version clipping the "clipping" polygon using clip_clipper_polygon_with_subject_bbox().
@@ -639,7 +641,6 @@ void traverse_pt(const ClipperLib::PolyNodes &nodes, ExOrJustPolygons *retval)
/* OTHER */
Slic3r::Polygons simplify_polygons(const Slic3r::Polygons &subject);
-Slic3r::ExPolygons simplify_polygons_ex(const Slic3r::Polygons &subject);
Polygons top_level_islands(const Slic3r::Polygons &polygons);
diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp
index 5c608067cb..e8fde1fbb1 100644
--- a/src/libslic3r/Format/3mf.cpp
+++ b/src/libslic3r/Format/3mf.cpp
@@ -1419,21 +1419,31 @@ namespace Slic3r {
std::vector sla_support_points;
if (version == 0) {
+ assert(object_data_points.size() % 3 == 0);
for (unsigned int i=0; isla::SupportPointType{
+ return (std::abs(val - 1.) < EPSILON) ? sla::SupportPointType::island :
+ (std::abs(val - 2.) < EPSILON) ? sla::SupportPointType::manual_add :
+ //(std::abs(val - 3.) < EPSILON) ? sla::SupportPointType::slope :
+ sla::SupportPointType::slope; // default for previous version of store points
+ };
+ assert(object_data_points.size() % 5 == 0);
for (unsigned int i=0; i float {
+ switch (t) {
+ case Slic3r::sla::SupportPointType::manual_add: return 2.f;
+ case Slic3r::sla::SupportPointType::island: return 1.f;
+ case Slic3r::sla::SupportPointType::slope: return 3.f;
+ default: assert(false); return 0.f;
+ }
+ };
// Store the layer height profile as a single space separated list.
for (size_t i = 0; i < sla_support_points.size(); ++i) {
- sprintf(buffer, (i==0 ? "%f %f %f %f %f" : " %f %f %f %f %f"), sla_support_points[i].pos(0), sla_support_points[i].pos(1), sla_support_points[i].pos(2), sla_support_points[i].head_front_radius, (float)sla_support_points[i].is_new_island);
+ sprintf(buffer, (i==0 ? "%f %f %f %f %f" : " %f %f %f %f %f"),
+ sla_support_points[i].pos(0),
+ sla_support_points[i].pos(1),
+ sla_support_points[i].pos(2),
+ sla_support_points[i].head_front_radius,
+ support_point_type_to_float(sla_support_points[i].type));
out += buffer;
}
out += "\n";
diff --git a/src/libslic3r/Format/3mf.hpp b/src/libslic3r/Format/3mf.hpp
index 78f95b7db0..b2e3746fd2 100644
--- a/src/libslic3r/Format/3mf.hpp
+++ b/src/libslic3r/Format/3mf.hpp
@@ -19,7 +19,18 @@ namespace Slic3r {
* version 1 : ThreeMF_support_points_version=1
object_id=1|-12.055421 -2.658771 10.000000 0.4 0.0
object_id=2|-14.051745 -3.570338 5.000000 0.6 1.0
- // introduced header with version number; x,y,z,head_size,is_new_island)
+ // introduced header with version number; x,y,z,head_size,type)
+ // before 2.9.1 fifth float means is_island (bool flag) -> value from 0.9999f to 1.0001f means it is support for island otherwise not. User edited points has always value zero.
+ // since 2.9.1 fifth float means type -> starts show user edited points
+ // type range value meaning
+ // (float is used only for compatibility, string will be better)
+ // from | to | meaning
+ // --------------------------------
+ // 0.9999f | 1.0001 | island (no change)
+ // 1.9999f | 2.0001 | manual edited points loose info about island
+ // 2.9999f | 3.0001 | generated point by slope ration
+ // all other values are readed also as slope type
+
*/
enum {
diff --git a/src/libslic3r/Format/AMF.cpp b/src/libslic3r/Format/AMF.cpp
index 797d083caa..f595e22736 100644
--- a/src/libslic3r/Format/AMF.cpp
+++ b/src/libslic3r/Format/AMF.cpp
@@ -774,7 +774,7 @@ void AMFParserContext::endElement(const char * /* name */)
point(coord_idx) = float(atof(p));
if (++coord_idx == 5) {
- m_object->sla_support_points.push_back(sla::SupportPoint(point));
+ m_object->sla_support_points.push_back(sla::SupportPoint{Vec3f(point[0], point[1], point[2]), point[3]});
coord_idx = 0;
}
if (end == nullptr)
diff --git a/src/libslic3r/Geometry/Circle.hpp b/src/libslic3r/Geometry/Circle.hpp
index 94f756133a..6bce435bb9 100644
--- a/src/libslic3r/Geometry/Circle.hpp
+++ b/src/libslic3r/Geometry/Circle.hpp
@@ -239,7 +239,7 @@ int ray_circle_intersections(T r, T a, T b, T c, std::pair &get_nodes() const { return m_nodes; }
+ // NOTE: Copy constructor cause failing FDM tests but not each run only from time to time.
+ // KDTreeIndirect(const KDTreeIndirect &rhs) : m_nodes(rhs.m_nodes), coordinate(rhs.coordinate) {}
+ KDTreeIndirect get_copy() const { KDTreeIndirect copy(coordinate); copy.m_nodes = m_nodes; return copy; }
void build(size_t num_indices)
{
std::vector indices;
@@ -63,10 +66,10 @@ public:
}
template
- unsigned int descent_mask(const CoordType &point_coord, const CoordType &search_radius, size_t idx, size_t dimension) const
+ unsigned int descent_mask(const CoordType &point_coord, const double &search_radius, size_t idx, size_t dimension) const
{
CoordType dist = point_coord - this->coordinate(idx, dimension);
- return (dist * dist < search_radius + CoordType(EPSILON)) ?
+ return (double(dist) * dist < search_radius + EPSILON) ?
// The plane intersects a hypersphere centered at point_coord of search_radius.
((unsigned int)(VisitorReturnMask::CONTINUE_LEFT) | (unsigned int)(VisitorReturnMask::CONTINUE_RIGHT)) :
// The plane does not intersect the hypersphere.
@@ -213,44 +216,43 @@ std::array find_closest_points(
const Tree &kdtree;
const PointType &point;
const FilterFn filter;
-
- std::array, K> results;
+ struct Result {
+ size_t index;
+ double distance_sq;
+ };
+ std::array results;
Visitor(const Tree &kdtree, const PointType &point, FilterFn filter)
: kdtree(kdtree), point(point), filter(filter)
{
- results.fill(std::make_pair(Tree::npos,
- std::numeric_limits::max()));
+ results.fill(Result{Tree::npos, std::numeric_limits::max()});
}
unsigned int operator()(size_t idx, size_t dimension)
{
if (this->filter(idx)) {
- auto dist = CoordT(0);
+ double distance_sq = 0.;
for (size_t i = 0; i < D; ++i) {
CoordT d = point[i] - kdtree.coordinate(idx, i);
- dist += d * d;
+ distance_sq += double(d) * d;
}
- auto res = std::make_pair(idx, dist);
- auto it = std::lower_bound(results.begin(), results.end(),
- res, [](auto &r1, auto &r2) {
- return r1.second < r2.second;
- });
-
+ Result res{idx, distance_sq};
+ auto lower_distance = [](const Result &r1, const Result &r2) {
+ return r1.distance_sq < r2.distance_sq; };
+ auto it = std::lower_bound(results.begin(), results.end(), res, lower_distance);
if (it != results.end()) {
std::rotate(it, std::prev(results.end()), results.end());
*it = res;
}
}
- return kdtree.descent_mask(point[dimension],
- results.front().second, idx,
- dimension);
+ return kdtree.descent_mask(point[dimension], results.front().distance_sq, idx, dimension);
}
} visitor(kdtree, point, filter);
kdtree.visit(visitor);
std::array ret;
- for (size_t i = 0; i < K; i++) ret[i] = visitor.results[i].first;
+ for (size_t i = 0; i < K; i++)
+ ret[i] = visitor.results[i].index;
return ret;
}
@@ -290,20 +292,20 @@ std::vector find_nearby_points(const KDTreeIndirectType &kdtree, const P
struct Visitor {
const KDTreeIndirectType &kdtree;
const PointType center;
- const CoordType max_distance_squared;
+ const double max_distance_squared;
const FilterFn filter;
std::vector result;
Visitor(const KDTreeIndirectType &kdtree, const PointType& center, const CoordType &max_distance,
FilterFn filter) :
- kdtree(kdtree), center(center), max_distance_squared(max_distance*max_distance), filter(filter) {
+ kdtree(kdtree), center(center), max_distance_squared(double(max_distance)*max_distance), filter(filter) {
}
unsigned int operator()(size_t idx, size_t dimension) {
if (this->filter(idx)) {
- auto dist = CoordType(0);
+ double dist = 0.;
for (size_t i = 0; i < KDTreeIndirectType::NumDimensions; ++i) {
CoordType d = center[i] - kdtree.coordinate(idx, i);
- dist += d * d;
+ dist += double(d) * d;
}
if (dist < max_distance_squared) {
result.push_back(idx);
diff --git a/src/libslic3r/Line.cpp b/src/libslic3r/Line.cpp
index 535fb9b1bc..74f4879c8b 100644
--- a/src/libslic3r/Line.cpp
+++ b/src/libslic3r/Line.cpp
@@ -64,6 +64,18 @@ double Line::perp_distance_to(const Point &point) const
return std::abs(cross2(v, va)) / v.norm();
}
+double Line::perp_signed_distance_to(const Point &point) const {
+ // Sign is dependent on the line orientation.
+ // For CCW oriented polygon is possitive distace into shape and negative outside.
+ // For Line({0,0},{0,2}) and point {1,1} the distance is negative one(-1).
+ const Line &line = *this;
+ const Vec2d v = (line.b - line.a).cast();
+ const Vec2d va = (point - line.a).cast();
+ if (line.a == line.b)
+ return va.norm();
+ return cross2(v, va) / v.norm();
+}
+
double Line::orientation() const
{
double angle = this->atan2_();
diff --git a/src/libslic3r/Line.hpp b/src/libslic3r/Line.hpp
index a32d6eac75..14b77c44d9 100644
--- a/src/libslic3r/Line.hpp
+++ b/src/libslic3r/Line.hpp
@@ -216,6 +216,7 @@ public:
double distance_to(const Point &point) const { return distance_to(point, this->a, this->b); }
double distance_to_infinite_squared(const Point &point, Point *closest_point) const { return line_alg::distance_to_infinite_squared(*this, point, closest_point); }
double perp_distance_to(const Point &point) const;
+ double perp_signed_distance_to(const Point &point) const;
bool parallel_to(double angle) const;
bool parallel_to(const Line& line) const;
bool perpendicular_to(double angle) const;
diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp
index 375da5550a..a34edb5206 100644
--- a/src/libslic3r/Preset.cpp
+++ b/src/libslic3r/Preset.cpp
@@ -604,7 +604,6 @@ static std::vector s_Preset_sla_print_options {
"branchingsupport_object_elevation",
"support_points_density_relative",
- "support_points_minimal_distance",
"slice_closing_radius",
"slicing_mode",
"pad_enable",
diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp
index 2f6836986c..c93eadfadd 100644
--- a/src/libslic3r/PrintConfig.cpp
+++ b/src/libslic3r/PrintConfig.cpp
@@ -4564,14 +4564,6 @@ void PrintConfigDef::init_sla_params()
def->min = 0;
def->set_default_value(new ConfigOptionInt(100));
- def = this->add("support_points_minimal_distance", coFloat);
- def->label = L("Minimal distance of the support points");
- def->category = L("Supports");
- def->tooltip = L("No support points will be placed closer than this threshold.");
- def->sidetext = L("mm");
- def->min = 0;
- def->set_default_value(new ConfigOptionFloat(1.));
-
def = this->add("pad_enable", coBool);
def->label = L("Use pad");
def->category = L("Pad");
@@ -4994,7 +4986,8 @@ static std::set PrintConfigDef_ignore = {
"infill_only_where_needed",
"gcode_binary", // Introduced in 2.7.0-alpha1, removed in 2.7.1 (replaced by binary_gcode).
"wiping_volumes_extruders", // Removed in 2.7.3-alpha1.
- "wipe_tower_x", "wipe_tower_y", "wipe_tower_rotation_angle" // Removed in 2.9.0
+ "wipe_tower_x", "wipe_tower_y", "wipe_tower_rotation_angle", // Removed in 2.9.0
+ "support_points_minimal_distance", // End of the using in 2.9.1 (change algorithm for the support generator)
};
void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &value)
diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp
index 3106819feb..11f8df180b 100644
--- a/src/libslic3r/PrintConfig.hpp
+++ b/src/libslic3r/PrintConfig.hpp
@@ -1143,11 +1143,8 @@ PRINT_CONFIG_CLASS_DEFINE(
// and the model object's bounding box bottom. Units in mm.
((ConfigOptionFloat, branchingsupport_object_elevation))/*= 5.0*/
-
-
/////// Following options influence automatic support points placement:
((ConfigOptionInt, support_points_density_relative))
- ((ConfigOptionFloat, support_points_minimal_distance))
// Now for the base pool (pad) /////////////////////////////////////////////
diff --git a/src/libslic3r/SLA/SupportIslands/EvaluateNeighbor.cpp b/src/libslic3r/SLA/SupportIslands/EvaluateNeighbor.cpp
new file mode 100644
index 0000000000..d6f9fcee2b
--- /dev/null
+++ b/src/libslic3r/SLA/SupportIslands/EvaluateNeighbor.cpp
@@ -0,0 +1,23 @@
+#include "EvaluateNeighbor.hpp"
+#include "ExpandNeighbor.hpp"
+
+using namespace Slic3r::sla;
+
+EvaluateNeighbor::EvaluateNeighbor(VoronoiGraph::ExPath & result,
+ const VoronoiGraph::Node *node,
+ double distance_to_node,
+ const VoronoiGraph::Path &prev_path)
+ : post_process_neighbor(
+ std::make_unique(result,
+ node,
+ distance_to_node,
+ prev_path))
+{}
+
+void EvaluateNeighbor::process(CallStack &call_stack)
+{
+ NodeDataWithResult &data = *post_process_neighbor;
+ call_stack.emplace(std::move(post_process_neighbor));
+ for (const VoronoiGraph::Node::Neighbor &neighbor : data.node->neighbors)
+ call_stack.emplace(std::make_unique(data, neighbor));
+}
\ No newline at end of file
diff --git a/src/libslic3r/SLA/SupportIslands/EvaluateNeighbor.hpp b/src/libslic3r/SLA/SupportIslands/EvaluateNeighbor.hpp
new file mode 100644
index 0000000000..b233ccb612
--- /dev/null
+++ b/src/libslic3r/SLA/SupportIslands/EvaluateNeighbor.hpp
@@ -0,0 +1,36 @@
+#ifndef slic3r_SLA_SuppotstIslands_EvaluateNeighbor_hpp_
+#define slic3r_SLA_SuppotstIslands_EvaluateNeighbor_hpp_
+
+#include
+
+#include "IStackFunction.hpp"
+#include "PostProcessNeighbors.hpp"
+#include "VoronoiGraph.hpp"
+
+namespace Slic3r::sla {
+
+///
+/// create on stack
+/// 1 * PostProcessNeighbors
+/// N * ExpandNode
+///
+class EvaluateNeighbor : public IStackFunction
+{
+ std::unique_ptr post_process_neighbor;
+public:
+ EvaluateNeighbor(
+ VoronoiGraph::ExPath & result,
+ const VoronoiGraph::Node *node,
+ double distance_to_node = 0.,
+ const VoronoiGraph::Path &prev_path = VoronoiGraph::Path({}, 0.));
+
+ ///
+ /// create on stack
+ /// 1 * PostProcessNeighbors
+ /// N * ExpandNode
+ ///
+ virtual void process(CallStack &call_stack);
+};
+
+} // namespace Slic3r::sla
+#endif // slic3r_SLA_SuppotstIslands_EvaluateNeighbor_hpp_
diff --git a/src/libslic3r/SLA/SupportIslands/ExpandNeighbor.cpp b/src/libslic3r/SLA/SupportIslands/ExpandNeighbor.cpp
new file mode 100644
index 0000000000..260ab27436
--- /dev/null
+++ b/src/libslic3r/SLA/SupportIslands/ExpandNeighbor.cpp
@@ -0,0 +1,44 @@
+#include "ExpandNeighbor.hpp"
+#include "VoronoiGraphUtils.hpp"
+
+using namespace Slic3r::sla;
+
+ExpandNeighbor::ExpandNeighbor(
+ NodeDataWithResult & data,
+ const VoronoiGraph::Node::Neighbor &neighbor)
+ : data(data)
+ , neighbor(neighbor)
+{}
+
+void ExpandNeighbor::process(CallStack &call_stack)
+{
+ if (data.skip_nodes.find(neighbor.node) != data.skip_nodes.end()) return;
+
+ // detection of circle
+ auto circle_opt = VoronoiGraphUtils::create_circle(data.act_path,
+ neighbor);
+ if (circle_opt.has_value()) {
+ size_t circle_index = data.result.circles.size();
+ data.circle_indexes.push_back(circle_index);
+ data.result.circles.push_back(*circle_opt);
+ return;
+ }
+
+ // create copy of path(not circles, not side_branches)
+ const VoronoiGraph::Node &next_node = *neighbor.node;
+ // is next node leaf ?
+ if (next_node.neighbors.size() == 1) {
+ VoronoiGraph::Path side_branch({&next_node}, neighbor.length());
+ data.side_branches.push(std::move(side_branch));
+ return;
+ }
+
+ auto post_process_neighbor = std::make_unique(data);
+ VoronoiGraph::ExPath &neighbor_path = post_process_neighbor->neighbor_path;
+
+ call_stack.emplace(std::move(post_process_neighbor));
+ call_stack.emplace(
+ std::make_unique(neighbor_path, neighbor.node,
+ neighbor.length(),
+ data.act_path));
+}
\ No newline at end of file
diff --git a/src/libslic3r/SLA/SupportIslands/ExpandNeighbor.hpp b/src/libslic3r/SLA/SupportIslands/ExpandNeighbor.hpp
new file mode 100644
index 0000000000..061963c46f
--- /dev/null
+++ b/src/libslic3r/SLA/SupportIslands/ExpandNeighbor.hpp
@@ -0,0 +1,35 @@
+#ifndef slic3r_SLA_SuppotstIslands_ExpandNeighbor_hpp_
+#define slic3r_SLA_SuppotstIslands_ExpandNeighbor_hpp_
+
+#include "IStackFunction.hpp"
+#include "VoronoiGraph.hpp"
+#include "PostProcessNeighbor.hpp"
+#include "EvaluateNeighbor.hpp"
+
+namespace Slic3r::sla {
+
+///
+/// Expand neighbor to
+/// - PostProcessNeighbor
+/// - EvaluateNeighbor
+///
+class ExpandNeighbor : public IStackFunction
+{
+ NodeDataWithResult & data;
+ const VoronoiGraph::Node::Neighbor &neighbor;
+
+public:
+ ExpandNeighbor(NodeDataWithResult & data,
+ const VoronoiGraph::Node::Neighbor &neighbor);
+
+ ///
+ /// Expand neighbor to
+ /// - PostProcessNeighbor
+ /// - EvaluateNeighbor
+ ///
+ /// Output callStack
+ virtual void process(CallStack &call_stack);
+};
+
+} // namespace Slic3r::sla
+#endif // slic3r_SLA_SuppotstIslands_ExpandNeighbor_hpp_
diff --git a/src/libslic3r/SLA/SupportIslands/IStackFunction.hpp b/src/libslic3r/SLA/SupportIslands/IStackFunction.hpp
new file mode 100644
index 0000000000..6763116a70
--- /dev/null
+++ b/src/libslic3r/SLA/SupportIslands/IStackFunction.hpp
@@ -0,0 +1,23 @@
+#ifndef slic3r_SLA_SuppotstIslands_IStackFunction_hpp_
+#define slic3r_SLA_SuppotstIslands_IStackFunction_hpp_
+
+#include
+#include
+
+namespace Slic3r::sla {
+
+///
+/// Interface for objects inside of CallStack.
+/// It is way to prevent stack overflow inside recurrent functions.
+///
+class IStackFunction
+{
+public:
+ virtual ~IStackFunction() = default;
+ virtual void process(std::stack> &call_stack) = 0;
+};
+
+using CallStack = std::stack>;
+
+} // namespace Slic3r::sla
+#endif // slic3r_SLA_SuppotstIslands_IStackFunction_hpp_
diff --git a/src/libslic3r/SLA/SupportIslands/LineUtils.cpp b/src/libslic3r/SLA/SupportIslands/LineUtils.cpp
new file mode 100644
index 0000000000..f8f3236273
--- /dev/null
+++ b/src/libslic3r/SLA/SupportIslands/LineUtils.cpp
@@ -0,0 +1,458 @@
+#include "LineUtils.hpp"
+#include
+#include
+#include
+#include "VectorUtils.hpp"
+#include "PointUtils.hpp"
+
+using namespace Slic3r::sla;
+
+// sort counter clock wise lines
+void LineUtils::sort_CCW(Lines &lines, const Point& center)
+{
+ std::function calc = [¢er](const Line &line) {
+ Point p = line.a - center;
+ return std::atan2(p.y(), p.x());
+ };
+ VectorUtils::sort_by(lines, calc);
+}
+
+bool LineUtils::is_parallel_y(const Line &line) {
+ coord_t x_change = line.a.x() - line.b.x();
+ return (x_change == 0);
+}
+bool LineUtils::is_parallel_y(const Linef &line)
+{
+ double x_change = line.a.x() - line.b.x();
+ return (fabs(x_change) < std::numeric_limits::epsilon());
+}
+
+std::optional LineUtils::crop_ray(const Line & ray,
+ const Point ¢er,
+ double radius)
+{
+ if (is_parallel_y(ray)) {
+ coord_t x = ray.a.x();
+ coord_t diff = x - center.x();
+ coord_t abs_diff = abs(diff);
+ if (abs_diff > radius) return {};
+ // create cross points
+ double move_y = sqrt(radius * radius - static_cast(x) * x);
+ coord_t y = static_cast(std::round(move_y));
+ coord_t cy = center.y();
+ Point first(x, cy + y);
+ Point second(x,cy - y);
+ return Line(first, second);
+ } else {
+ Line moved_line(ray.a - center, ray.b - center);
+ double a, b, c;
+ std::tie(a, b, c) = get_param(moved_line);
+ std::pair points;
+ int count = Slic3r::Geometry::ray_circle_intersections(
+ radius, a, b, c, points);
+ if (count != 2) return {};
+ return Line(points.first.cast() + center,
+ points.second.cast() + center);
+ }
+}
+std::optional LineUtils::crop_ray(const Linef &ray,
+ const Point ¢er,
+ double radius)
+{
+ Vec2d center_d = center.cast();
+ if (is_parallel_y(ray)) {
+ double x = ray.a.x();
+ double diff = x - center_d.x();
+ double abs_diff = fabs(diff);
+ if (abs_diff > radius) return {};
+ // create cross points
+ double y = sqrt(radius * radius - x * x);
+ Vec2d first(x, y);
+ Vec2d second(x, -y);
+ return Linef(first + center_d,
+ second + center_d);
+ } else {
+ Linef moved_line(ray.a - center_d, ray.b - center_d);
+ double a, b, c;
+ std::tie(a, b, c) = get_param(moved_line);
+ std::pair points;
+ int count = Slic3r::Geometry::ray_circle_intersections(radius, a, b,
+ c, points);
+ if (count != 2) return {};
+ return Linef(points.first + center_d, points.second + center_d);
+ }
+}
+
+std::optional LineUtils::crop_half_ray(const Line & half_ray,
+ const Point ¢er,
+ double radius)
+{
+ std::optional segment = crop_ray(half_ray, center, radius);
+ if (!segment.has_value()) return {};
+ Point dir = LineUtils::direction(half_ray);
+ using fnc = std::function;
+ fnc use_point_x = [&half_ray, &dir](const Point &p) -> bool {
+ return (p.x() > half_ray.a.x()) == (dir.x() > 0);
+ };
+ fnc use_point_y = [&half_ray, &dir](const Point &p) -> bool {
+ return (p.y() > half_ray.a.y()) == (dir.y() > 0);
+ };
+ bool use_x = PointUtils::is_majorit_x(dir);
+ fnc use_point = (use_x) ? use_point_x : use_point_y;
+ bool use_a = use_point(segment->a);
+ bool use_b = use_point(segment->b);
+ if (!use_a && !use_b) return {};
+ if (use_a && use_b) return segment;
+ return Line(half_ray.a, (use_a)?segment->a : segment->b);
+}
+
+std::optional LineUtils::crop_half_ray(const Linef & half_ray,
+ const Point ¢er,
+ double radius)
+{
+ std::optional segment = crop_ray(half_ray, center, radius);
+ if (!segment.has_value()) return {};
+ Vec2d dir = half_ray.b - half_ray.a;
+ using fnc = std::function;
+ fnc use_point_x = [&half_ray, &dir](const Vec2d &p) -> bool {
+ return (p.x() > half_ray.a.x()) == (dir.x() > 0);
+ };
+ fnc use_point_y = [&half_ray, &dir](const Vec2d &p) -> bool {
+ return (p.y() > half_ray.a.y()) == (dir.y() > 0);
+ };
+ bool use_x = PointUtils::is_majorit_x(dir);
+ fnc use_point = (use_x) ? use_point_x : use_point_y;
+ bool use_a = use_point(segment->a);
+ bool use_b = use_point(segment->b);
+ if (!use_a && !use_b) return {};
+ if (use_a && use_b) return segment;
+ return Linef(half_ray.a, (use_a) ? segment->a : segment->b);
+}
+
+std::optional LineUtils::crop_line(const Line & line,
+ const Point ¢er,
+ double radius)
+{
+ std::optional segment = crop_ray(line, center, radius);
+ if (!segment.has_value()) return {};
+
+ Point dir = line.b - line.a;
+ using fnc = std::function;
+ fnc use_point_x = [&line, &dir](const Point &p) -> bool {
+ return (dir.x() > 0) ? (p.x() > line.a.x()) && (p.x() < line.b.x()) :
+ (p.x() < line.a.x()) && (p.x() > line.b.x());
+ };
+ fnc use_point_y = [&line, &dir](const Point &p) -> bool {
+ return (dir.y() > 0) ? (p.y() > line.a.y()) && (p.y() < line.b.y()) :
+ (p.y() < line.a.y()) && (p.y() > line.b.y());
+ };
+ bool use_x = PointUtils::is_majorit_x(dir);
+ fnc use_point = (use_x) ? use_point_x : use_point_y;
+ bool use_a = use_point(segment->a);
+ bool use_b = use_point(segment->b);
+ if (!use_a && !use_b) return {};
+ if (use_a && use_b) return segment;
+ bool same_dir = (use_x) ?
+ ((dir.x() > 0) == ((segment->b.x() - segment->a.x()) > 0)) :
+ ((dir.y() > 0) == ((segment->b.y() - segment->a.y()) > 0)) ;
+ if (use_a) {
+ if (same_dir)
+ return Line(segment->a, line.b);
+ else
+ return Line(line.a, segment->a);
+ } else { // use b
+ if (same_dir)
+ return Line(line.a, segment->b);
+ else
+ return Line(segment->b, line.b);
+ }
+}
+
+std::optional LineUtils::crop_line(const Linef & line,
+ const Point ¢er,
+ double radius)
+{
+ std::optional segment = crop_ray(line, center, radius);
+ if (!segment.has_value()) return {};
+
+ Vec2d dir = line.b - line.a;
+ using fnc = std::function;
+ fnc use_point_x = [&line, &dir](const Vec2d &p) -> bool {
+ return (dir.x() > 0) ? (p.x() > line.a.x()) && (p.x() < line.b.x()) :
+ (p.x() < line.a.x()) && (p.x() > line.b.x());
+ };
+ fnc use_point_y = [&line, &dir](const Vec2d &p) -> bool {
+ return (dir.y() > 0) ? (p.y() > line.a.y()) && (p.y() < line.b.y()) :
+ (p.y() < line.a.y()) && (p.y() > line.b.y());
+ };
+ bool use_x = PointUtils::is_majorit_x(dir);
+ fnc use_point = (use_x) ? use_point_x : use_point_y;
+ bool use_a = use_point(segment->a);
+ bool use_b = use_point(segment->b);
+ if (!use_a && !use_b) return {};
+ if (use_a && use_b) return segment;
+ bool same_dir = (use_x) ? ((dir.x() > 0) ==
+ ((segment->b.x() - segment->a.x()) > 0)) :
+ ((dir.y() > 0) ==
+ ((segment->b.y() - segment->a.y()) > 0));
+ if (use_a) {
+ if (same_dir)
+ return Linef(segment->a, line.b);
+ else
+ return Linef(line.a, segment->a);
+ } else { // use b
+ if (same_dir)
+ return Linef(line.a, segment->b);
+ else
+ return Linef(segment->b, line.b);
+ }
+}
+
+
+std::tuple LineUtils::get_param(const Line &line) {
+ Vector normal = line.normal();
+ double a = normal.x();
+ double b = normal.y();
+ double c = -a * line.a.x() - b * line.a.y();
+ return {a, b, c};
+}
+
+std::tuple LineUtils::get_param(const Linef &line)
+{
+ Vec2d direction = line.b - line.a;
+ Vec2d normal(-direction.y(), direction.x());
+ double a = normal.x();
+ double b = normal.y();
+ double c = -a * line.a.x() - b * line.a.y();
+ return {a, b, c};
+}
+
+void LineUtils::draw(SVG & svg,
+ const Line &line,
+ const char *color,
+ coordf_t stroke_width,
+ const char *name,
+ bool side_points,
+ const char *color_a,
+ const char *color_b)
+{
+ svg.draw(line, color, stroke_width);
+ bool use_name = name != nullptr;
+ if (use_name) {
+ Point middle = line.a/2 + line.b/2;
+ svg.draw_text(middle, name, color);
+ }
+ if (side_points) {
+ std::string name_a = (use_name) ? "A" : (std::string("A_") + name);
+ std::string name_b = (use_name) ? "B" : (std::string("B_") + name);
+ svg.draw_text(line.a, name_a.c_str(), color_a);
+ svg.draw_text(line.b, name_b.c_str(), color_b);
+ }
+}
+
+double LineUtils::perp_distance(const Linef &line, Vec2d p)
+{
+ Vec2d v = line.b - line.a; // direction
+ Vec2d va = p - line.a;
+ return std::abs(cross2(v, va)) / v.norm();
+}
+
+bool LineUtils::is_parallel(const Line &first, const Line &second)
+{
+ Vec2i64 dir1 = direction(first).cast();
+ Vec2i64 dir2 = direction(second).cast();
+ return Slic3r::cross2(dir1, dir2) == 0;
+}
+
+std::optional LineUtils::intersection(const Line &ray1, const Line &ray2)
+{
+ const Vec2d v1 = direction(ray1).cast();
+ const Vec2d v2 = direction(ray2).cast();
+ double denom = cross2(v1, v2);
+ if (fabs(denom) < std::numeric_limits::epsilon()) return {};
+
+ const Vec2d v12 = (ray1.a - ray2.a).cast();
+ double nume = cross2(v2, v12);
+ double t = nume / denom;
+ return (ray1.a.cast() + t * v1);
+}
+
+bool LineUtils::belongs(const Line &line, const Point &point, double benevolence)
+{
+ const Point &a = line.a;
+ const Point &b = line.b;
+ auto is_in_interval = [](coord_t value, coord_t from, coord_t to) -> bool
+ {
+ if (from < to) {
+ // from < value < to
+ if (from > value || to < value) return false;
+ } else {
+ // to < value < from
+ if (from < value || to > value) return false;
+ }
+ return true;
+ };
+
+ if (!is_in_interval(point.x(), a.x(), b.x()) ||
+ !is_in_interval(point.y(), a.y(), b.y()) )
+ { // out of interval
+ return false;
+ }
+ double distance = line.perp_distance_to(point);
+ if (distance < benevolence) return true;
+ return false;
+}
+
+Slic3r::Point LineUtils::direction(const Line &line)
+{
+ return line.b - line.a;
+}
+
+Slic3r::Point LineUtils::middle(const Line &line) {
+ // division before adding to prevent data type overflow
+ return line.a / 2 + line.b / 2;
+}
+
+double LineUtils::foot(const Line &line, const Point &point)
+{
+ Vec2d a = line.a.cast();
+ Vec2d vec = point.cast() - a;
+ Vec2d b = line.b.cast();
+ Vec2d dir = b - a;
+ double l2 = dir.squaredNorm();
+ return vec.dot(dir) / l2;
+}
+
+LineUtils::LineConnection LineUtils::create_line_connection(
+ const Slic3r::Lines &lines)
+{
+ LineConnection line_connection;
+ static const size_t bad_index = -1;
+ auto insert = [&](size_t line_index, size_t connected, bool connect_by_a){
+ auto item = line_connection.find(line_index);
+ if (item == line_connection.end()) {
+ // create new
+ line_connection[line_index] = (connect_by_a) ?
+ std::pair(connected, bad_index) :
+ std::pair(bad_index, connected);
+ } else {
+ std::pair &pair = item->second;
+ size_t &ref_index = (connect_by_a) ? pair.first : pair.second;
+ assert(ref_index == bad_index);
+ ref_index = connected;
+ }
+ };
+
+ auto inserts = [&](size_t i1, size_t i2)->bool{
+ bool is_l1_a_connect = true; // false => l1_b_connect
+ const Slic3r::Line &l1 = lines[i1];
+ const Slic3r::Line &l2 = lines[i2];
+ if (!PointUtils::is_equal(l1.a, l2.b)) return false;
+ if (!PointUtils::is_equal(l1.b, l2.a)) return false;
+ else is_l1_a_connect = false;
+ insert(i1, i2, is_l1_a_connect);
+ insert(i2, i1, !is_l1_a_connect);
+ return true;
+ };
+
+ std::vector not_finished;
+ size_t prev_index = lines.size() - 1;
+ for (size_t index = 0; index < lines.size(); ++index) {
+ if (!inserts(prev_index, index)) {
+ bool found_index = false;
+ bool found_prev_index = false;
+ not_finished.erase(std::remove_if(not_finished.begin(),
+ not_finished.end(),
+ [&](const size_t ¬_finished_index) {
+ if (!found_index && inserts(index, not_finished_index)) {
+ found_index = true;
+ return true;
+ }
+ if (!found_prev_index && inserts(prev_index, not_finished_index)) {
+ found_prev_index = true;
+ return true;
+ }
+ return false;
+ }),
+ not_finished.end());
+ if (!found_index) not_finished.push_back(index);
+ if (!found_prev_index) not_finished.push_back(prev_index);
+ }
+ prev_index = index;
+ }
+ assert(not_finished.empty());
+ return line_connection;
+}
+
+Slic3r::BoundingBox LineUtils::create_bounding_box(const Lines &lines) {
+ Points pts;
+ pts.reserve(lines.size()*2);
+ for (const Line &line : lines) {
+ pts.push_back(line.a);
+ pts.push_back(line.b);
+ }
+ return BoundingBox(pts);
+}
+
+std::map LineUtils::create_line_connection_over_b(const Lines &lines)
+{
+ std::map line_connection;
+ auto inserts = [&](size_t i1, size_t i2) -> bool {
+ const Line &l1 = lines[i1];
+ const Line &l2 = lines[i2];
+ if (!PointUtils::is_equal(l1.b, l2.a))
+ return false;
+ assert(line_connection.find(i1) == line_connection.end());
+ line_connection[i1] = i2;
+ return true;
+ };
+
+ std::vector not_finished_a;
+ std::vector not_finished_b;
+ size_t prev_index = lines.size() - 1;
+ for (size_t index = 0; index < lines.size(); ++index) {
+ if (!inserts(prev_index, index)) {
+ bool found_b = false;
+ not_finished_b.erase(std::remove_if(not_finished_b.begin(), not_finished_b.end(),
+ [&](const size_t ¬_finished_index) {
+ if (!found_b && inserts(prev_index, not_finished_index)) {
+ found_b = true;
+ return true;
+ }
+ return false;
+ }),not_finished_b.end());
+ if (!found_b) not_finished_a.push_back(prev_index);
+
+ bool found_a = false;
+ not_finished_a.erase(std::remove_if(not_finished_a.begin(), not_finished_a.end(),
+ [&](const size_t ¬_finished_index) {
+ if (!found_a && inserts(not_finished_index, index)) {
+ found_a = true;
+ return true;
+ }
+ return false;
+ }),not_finished_a.end());
+ if (!found_a) not_finished_b.push_back(index);
+ }
+ prev_index = index;
+ }
+ assert(not_finished_a.empty());
+ assert(not_finished_b.empty());
+ return line_connection;
+}
+
+void LineUtils::draw(SVG & svg,
+ const Lines &lines,
+ const char * color,
+ coordf_t stroke_width,
+ bool ord,
+ bool side_points,
+ const char * color_a,
+ const char * color_b)
+{
+ for (const auto &line : lines) {
+ draw(svg, line, color, stroke_width,
+ (ord) ? std::to_string(&line - &lines.front()).c_str() : nullptr,
+ side_points, color_a, color_b);
+ }
+}
\ No newline at end of file
diff --git a/src/libslic3r/SLA/SupportIslands/LineUtils.hpp b/src/libslic3r/SLA/SupportIslands/LineUtils.hpp
new file mode 100644
index 0000000000..d48d988d9c
--- /dev/null
+++ b/src/libslic3r/SLA/SupportIslands/LineUtils.hpp
@@ -0,0 +1,227 @@
+#ifndef slic3r_SLA_SuppotstIslands_LineUtils_hpp_
+#define slic3r_SLA_SuppotstIslands_LineUtils_hpp_
+
+#include
+#include
+#include
+#include