diff --git a/cmake/modules/FindFlann.cmake b/cmake/modules/FindFlann.cmake new file mode 100644 index 000000000..98674d230 --- /dev/null +++ b/cmake/modules/FindFlann.cmake @@ -0,0 +1,28 @@ +############################################################################### +# Find Flann +# +# This sets the following variables: +# FLANN_FOUND - True if FLANN was found. +# FLANN_INCLUDE_DIRS - Directories containing the FLANN include files. +# FLANN_LIBRARIES - Libraries needed to use FLANN. +# FLANN_DEFINITIONS - Compiler flags for FLANN. + +find_package(PkgConfig) +pkg_check_modules(PC_FLANN flann) +set(FLANN_DEFINITIONS ${PC_FLANN_CFLAGS_OTHER}) + +find_path(FLANN_INCLUDE_DIR flann/flann.hpp + HINTS ${PC_FLANN_INCLUDEDIR} ${PC_FLANN_INCLUDE_DIRS}) + +find_library(FLANN_LIBRARY flann_cpp + HINTS ${PC_FLANN_LIBDIR} ${PC_FLANN_LIBRARY_DIRS}) + +set(FLANN_INCLUDE_DIRS ${FLANN_INCLUDE_DIR}) +set(FLANN_LIBRARIES ${FLANN_LIBRARY}) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Flann DEFAULT_MSG + FLANN_LIBRARY FLANN_INCLUDE_DIR) + +mark_as_advanced(FLANN_LIBRARY FLANN_INCLUDE_DIR) + diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt index b6e39ec8e..0536eeacc 100644 --- a/xs/CMakeLists.txt +++ b/xs/CMakeLists.txt @@ -179,13 +179,21 @@ add_library(libslic3r STATIC ${LIBDIR}/libslic3r/SVG.hpp ${LIBDIR}/libslic3r/TriangleMesh.cpp ${LIBDIR}/libslic3r/TriangleMesh.hpp - ${LIBDIR}/libslic3r/SLASupportPool.hpp - ${LIBDIR}/libslic3r/SLASupportPool.cpp + ${LIBDIR}/libslic3r/SLABasePool.hpp + ${LIBDIR}/libslic3r/SLABasePool.cpp + ${LIBDIR}/libslic3r/ConcaveHull.hpp + ${LIBDIR}/libslic3r/ConcaveHull.cpp # ${LIBDIR}/libslic3r/utils.cpp ${LIBDIR}/libslic3r/Utils.hpp ) +find_package(Flann REQUIRED) +if(FLANN_FOUND) +message(STATUS "FLANN found: ${FLANN_LIBRARIES}") +target_link_libraries(libslic3r ${FLANN_LIBRARIES}) +endif() + add_library(libslic3r_gui STATIC ${LIBDIR}/slic3r/GUI/AboutDialog.cpp ${LIBDIR}/slic3r/GUI/AboutDialog.hpp diff --git a/xs/src/libslic3r/ConcaveHull.cpp b/xs/src/libslic3r/ConcaveHull.cpp new file mode 100644 index 000000000..8c8462267 --- /dev/null +++ b/xs/src/libslic3r/ConcaveHull.cpp @@ -0,0 +1,487 @@ +#include "ConcaveHull.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#pragma warning(push, 0) +#include +#pragma warning(pop) + +namespace Slic3r { namespace concavehull { + +const size_t stride = 24; // size in bytes of x, y, id + +namespace { + +// Floating point comparisons +auto Equal(double a, double b) -> bool; +auto Zero(double a) -> bool; +auto LessThan(double a, double b) -> bool; +auto LessThanOrEqual(double a, double b) -> bool; +auto GreaterThan(double a, double b) -> bool; + +// Algorithm-specific +auto NearestNeighboursFlann(flann::Index> &index, const Point &p, size_t k) -> PointValueVector; +auto SortByAngle(PointValueVector &values, const Point &p, double prevAngle) -> PointVector; +auto AddPoint(PointVector &points, const Point &p) -> void; + +// General maths +auto PointsEqual(const Point &a, const Point &b) -> bool; +auto Angle(const Point &a, const Point &b) -> double; +auto NormaliseAngle(double radians) -> double; +auto PointInPolygon(const Point &p, const PointVector &list) -> bool; +auto Intersects(const LineSegment &a, const LineSegment &b) -> bool; + +// Point list utilities +auto FindMinYPoint(const PointVector &points) -> Point; +auto RemoveDuplicates(PointVector &points) -> void; +auto IdentifyPoints(PointVector &points) -> void; +auto RemoveHull(PointVector &points, const PointVector &hull) -> PointVector::iterator; +auto MultiplePointInPolygon(PointVector::iterator begin, PointVector::iterator end, const PointVector &hull) -> bool; + +// Compare a and b for equality +auto Equal(double a, double b) -> bool +{ + return fabs(a - b) <= DBL_EPSILON; +} + +// Compare value to zero +auto Zero(double a) -> bool +{ + return fabs(a) <= DBL_EPSILON; +} + +// Compare for a < b +auto LessThan(double a, double b) -> bool +{ + return a < (b - DBL_EPSILON); +} + +// Compare for a <= b +auto LessThanOrEqual(double a, double b) -> bool +{ + return a <= (b + DBL_EPSILON); +} + +// Compare for a > b +auto GreaterThan(double a, double b) -> bool +{ + return a > (b + DBL_EPSILON); +} + +// Compare whether two points have the same x and y +auto PointsEqual(const Point &a, const Point &b) -> bool +{ + return Equal(a.x, b.x) && Equal(a.y, b.y); +} + +// Remove duplicates in a list of points +auto RemoveDuplicates(PointVector &points) -> void +{ + sort(begin(points), end(points), [](const Point & a, const Point & b) + { + if (Equal(a.x, b.x)) + return LessThan(a.y, b.y); + else + return LessThan(a.x, b.x); + }); + + auto newEnd = unique(begin(points), end(points), [](const Point & a, const Point & b) + { + return PointsEqual(a, b); + }); + + points.erase(newEnd, end(points)); +} + +// Uniquely id the points for binary searching +auto IdentifyPoints(PointVector &points) -> void +{ + uint64_t id = 0; + + for (auto itr = begin(points); itr != end(points); ++itr, ++id) + { + itr->id = id; + } +} + +// Find the point having the smallest y-value +auto FindMinYPoint(const PointVector &points) -> Point +{ + assert(!points.empty()); + + auto itr = min_element(begin(points), end(points), [](const Point & a, const Point & b) + { + if (Equal(a.y, b.y)) + return GreaterThan(a.x, b.x); + else + return LessThan(a.y, b.y); + }); + + return *itr; +} + +// Lookup by ID and remove a point from a list of points +auto RemovePoint(PointVector &list, const Point &p) -> void +{ + auto itr = std::lower_bound(begin(list), end(list), p, [](const Point & a, const Point & b) + { + return a.id < b.id; + }); + + assert(itr != end(list) && itr->id == p.id); + + if (itr != end(list)) + list.erase(itr); +} + +// Add a point to a list of points +auto AddPoint(PointVector &points, const Point &p) -> void +{ + points.push_back(p); +} + +// Return the k-nearest points in a list of points from the given point p (uses Flann library). +auto NearestNeighboursFlann(flann::Index> &index, const Point &p, size_t k) -> PointValueVector +{ + std::vector vIndices(k); + std::vector vDists(k); + double test[] = { p.x, p.y }; + + flann::Matrix query(test, 1, 2); + flann::Matrix mIndices(vIndices.data(), 1, static_cast(vIndices.size())); + flann::Matrix mDists(vDists.data(), 1, static_cast(vDists.size())); + + int count_ = index.knnSearch(query, mIndices, mDists, k, flann::SearchParams(128)); + size_t count = static_cast(count_); + + PointValueVector result(count); + + for (size_t i = 0; i < count; ++i) + { + int id = vIndices[i]; + const double *point = index.getPoint(id); + result[i].point.x = point[0]; + result[i].point.y = point[1]; + result[i].point.id = id; + result[i].distance = vDists[i]; + } + + return result; +} + +// Returns a list of points sorted in descending order of clockwise angle +auto SortByAngle(PointValueVector &values, const Point &from, double prevAngle) -> PointVector +{ + for_each(begin(values), end(values), [from, prevAngle](PointValue & to) + { + to.angle = NormaliseAngle(Angle(from, to.point) - prevAngle); + }); + + sort(begin(values), end(values), [](const PointValue & a, const PointValue & b) + { + return GreaterThan(a.angle, b.angle); + }); + + PointVector angled(values.size()); + + transform(begin(values), end(values), begin(angled), [](const PointValue & pv) + { + return pv.point; + }); + + return angled; +} + +// Get the angle in radians measured clockwise from +'ve x-axis +auto Angle(const Point &a, const Point &b) -> double +{ + double angle = -atan2(b.y - a.y, b.x - a.x); + + return NormaliseAngle(angle); +} + +// Return angle in range: 0 <= angle < 2PI +auto NormaliseAngle(double radians) -> double +{ + if (radians < 0.0) + return radians + M_PI + M_PI; + else + return radians; +} + +// Return the new logical end after removing points from dataset having ids belonging to hull +auto RemoveHull(PointVector &points, const PointVector &hull) -> PointVector::iterator +{ + std::vector ids(hull.size()); + + transform(begin(hull), end(hull), begin(ids), [](const Point & p) + { + return p.id; + }); + + sort(begin(ids), end(ids)); + + return remove_if(begin(points), end(points), [&ids](const Point & p) + { + return binary_search(begin(ids), end(ids), p.id); + }); +} + +//// Uses OpenMP to determine whether a condition exists in the specified range of elements. https://msdn.microsoft.com/en-us/library/ff521445.aspx +//template +//bool omp_parallel_any_of(InIt first, InIt last, const Predicate &pr) +//{ +// typedef typename std::iterator_traits::value_type item_type; + +// // A flag that indicates that the condition exists. +// bool found = false; + +// #pragma omp parallel for +// for (int i = 0; i < static_cast(last - first); ++i) +// { +// if (!found) +// { +// item_type &cur = *(first + i); + +// // If the element satisfies the condition, set the flag to cancel the operation. +// if (pr(cur)) +// { +// found = true; +// } +// } +// } + +// return found; +//} + +// Check whether all points in a begin/end range are inside hull. +auto MultiplePointInPolygon(PointVector::iterator begin, PointVector::iterator end, const PointVector &hull) -> bool +{ + auto test = [&hull](const Point & p) + { + return !PointInPolygon(p, hull); + }; + + bool anyOutside = true; + +#if defined USE_OPENMP + + anyOutside = omp_parallel_any_of(begin, end, test); // multi-threaded + +#else + + anyOutside = std::any_of(begin, end, test); // single-threaded + +#endif + + return !anyOutside; +} + +// Point-in-polygon test +auto PointInPolygon(const Point &p, const PointVector &list) -> bool +{ + if (list.size() <= 2) + return false; + + const double &x = p.x; + const double &y = p.y; + + int inout = 0; + auto v0 = list.begin(); + auto v1 = v0 + 1; + + while (v1 != list.end()) + { + if ((LessThanOrEqual(v0->y, y) && LessThan(y, v1->y)) || (LessThanOrEqual(v1->y, y) && LessThan(y, v0->y))) + { + if (!Zero(v1->y - v0->y)) + { + double tdbl1 = (y - v0->y) / (v1->y - v0->y); + double tdbl2 = v1->x - v0->x; + + if (LessThan(x, v0->x + (tdbl2 * tdbl1))) + inout++; + } + } + + v0 = v1; + v1++; + } + + if (inout == 0) + return false; + else if (inout % 2 == 0) + return false; + else + return true; +} + +// Test whether two line segments intersect each other +auto Intersects(const LineSegment &a, const LineSegment &b) -> bool +{ + // https://www.topcoder.com/community/data-science/data-science-tutorials/geometry-concepts-line-intersection-and-its-applications/ + + const double &ax1 = a.first.x; + const double &ay1 = a.first.y; + const double &ax2 = a.second.x; + const double &ay2 = a.second.y; + const double &bx1 = b.first.x; + const double &by1 = b.first.y; + const double &bx2 = b.second.x; + const double &by2 = b.second.y; + + double a1 = ay2 - ay1; + double b1 = ax1 - ax2; + double c1 = a1 * ax1 + b1 * ay1; + double a2 = by2 - by1; + double b2 = bx1 - bx2; + double c2 = a2 * bx1 + b2 * by1; + double det = a1 * b2 - a2 * b1; + + if (Zero(det)) + { + return false; + } + else + { + double x = (b2 * c1 - b1 * c2) / det; + double y = (a1 * c2 - a2 * c1) / det; + + bool on_both = true; + on_both = on_both && LessThanOrEqual(std::min(ax1, ax2), x) && LessThanOrEqual(x, std::max(ax1, ax2)); + on_both = on_both && LessThanOrEqual(std::min(ay1, ay2), y) && LessThanOrEqual(y, std::max(ay1, ay2)); + on_both = on_both && LessThanOrEqual(std::min(bx1, bx2), x) && LessThanOrEqual(x, std::max(bx1, bx2)); + on_both = on_both && LessThanOrEqual(std::min(by1, by2), y) && LessThanOrEqual(y, std::max(by1, by2)); + return on_both; + } +} + +} + +// The main algorithm from the Moreira-Santos paper. +auto ConcaveHull(PointVector &pointList, size_t k, PointVector &hull) -> bool +{ + hull.clear(); + + if (pointList.size() < 3) + { + return true; + } + if (pointList.size() == 3) + { + hull = pointList; + return true; + } + + // construct a randomized kd-tree index using 4 kd-trees + // 2 columns, but stride = 24 bytes in width (x, y, ignoring id) + flann::Matrix matrix(&(pointList.front().x), pointList.size(), 2, stride); + flann::Index> flannIndex(matrix, flann::KDTreeIndexParams(4)); + flannIndex.buildIndex(); + + std::cout << "\rFinal 'k' : " << k; + + // Initialise hull with the min-y point + Point firstPoint = FindMinYPoint(pointList); + AddPoint(hull, firstPoint); + + // Until the hull is of size > 3 we want to ignore the first point from nearest neighbour searches + Point currentPoint = firstPoint; + flannIndex.removePoint(firstPoint.id); + + double prevAngle = 0.0; + int step = 1; + + // Iterate until we reach the start, or until there's no points left to process + while ((!PointsEqual(currentPoint, firstPoint) || step == 1) && hull.size() != pointList.size()) + { + if (step == 4) + { + // Put back the first point into the dataset and into the flann index + firstPoint.id = pointList.size(); + flann::Matrix firstPointMatrix(&firstPoint.x, 1, 2, stride); + flannIndex.addPoints(firstPointMatrix); + } + + PointValueVector kNearestNeighbours = NearestNeighboursFlann(flannIndex, currentPoint, k); + PointVector cPoints = SortByAngle(kNearestNeighbours, currentPoint, prevAngle); + + bool its = true; + size_t i = 0; + + while (its && i < cPoints.size()) + { + size_t lastPoint = 0; + if (PointsEqual(cPoints[i], firstPoint)) + lastPoint = 1; + + size_t j = 2; + its = false; + + while (!its && j < hull.size() - lastPoint) + { + auto line1 = std::make_pair(hull[step - 1], cPoints[i]); + auto line2 = std::make_pair(hull[step - j - 1], hull[step - j]); + its = Intersects(line1, line2); + j++; + } + + if (its) + i++; + } + + if (its) + return false; + + currentPoint = cPoints[i]; + + AddPoint(hull, currentPoint); + + prevAngle = Angle(hull[step], hull[step - 1]); + + flannIndex.removePoint(currentPoint.id); + + step++; + } + + // The original points less the points belonging to the hull need to be fully enclosed by the hull in order to return true. + PointVector dataset = pointList; + + auto newEnd = RemoveHull(dataset, hull); + bool allEnclosed = MultiplePointInPolygon(begin(dataset), newEnd, hull); + + return allEnclosed; +} + +// Iteratively call the main algorithm with an increasing k until success +auto ConcaveHull(PointVector &dataset, size_t k, bool iterate) -> PointVector +{ + while (k < dataset.size()) + { + PointVector hull; + if (ConcaveHull(dataset, k, hull) || !iterate) + { + return hull; + } + k++; + } + + return{}; +} + +Point::Point(const Pointf & sp): x(sp.x), y(sp.y) {} + +} +} diff --git a/xs/src/libslic3r/ConcaveHull.hpp b/xs/src/libslic3r/ConcaveHull.hpp new file mode 100644 index 000000000..52e59e269 --- /dev/null +++ b/xs/src/libslic3r/ConcaveHull.hpp @@ -0,0 +1,49 @@ +#ifndef CONCAVEHULL_HPP +#define CONCAVEHULL_HPP + +#include +#include + +namespace Slic3r { + +class Pointf; + +namespace concavehull { + +using std::uint64_t; + +struct Point +{ + double x = 0.0; + double y = 0.0; + std::uint64_t id = 0; + + Point() = default; + + Point(double x, double y) + : x(x) + , y(y) + {} + + explicit Point(const Slic3r::Pointf&); +}; + +struct PointValue +{ + Point point; + double distance = 0.0; + double angle = 0.0; +}; + +extern const size_t stride; // size in bytes of x, y, id + +using PointVector = std::vector; +using PointValueVector = std::vector; +using LineSegment = std::pair; + +auto ConcaveHull(PointVector &dataset, size_t k, bool iterate) -> PointVector; + +} +} + +#endif // CONCAVEHULL_HPP diff --git a/xs/src/libslic3r/SLABasePool.cpp b/xs/src/libslic3r/SLABasePool.cpp new file mode 100644 index 000000000..6d498c715 --- /dev/null +++ b/xs/src/libslic3r/SLABasePool.cpp @@ -0,0 +1,333 @@ +#include + +#include "SLABasePool.hpp" +#include "ExPolygon.hpp" +#include "TriangleMesh.hpp" +#include "libnest2d/clipper_backend/clipper_backend.hpp" +#include "ClipperUtils.hpp" + +#include "ConcaveHull.hpp" + +using BoostPolygon = libnest2d::PolygonImpl; +using BoostPolygons = std::vector; + +namespace Slic3r { namespace sla { + +namespace { + +using coord_t = Point::coord_type; + +void reverse(Polygon& p) { + std::reverse(p.points.begin(), p.points.end()); +} + +inline BoostPolygon convert(const ExPolygon& exp) { + auto&& ctour = Slic3rMultiPoint_to_ClipperPath(exp.contour); + auto&& holes = Slic3rMultiPoints_to_ClipperPaths(exp.holes); + return {ctour, holes}; +} + +inline BoostPolygons convert(const ExPolygons& exps) { + BoostPolygons ret; + ret.reserve(exps.size()); + std::for_each(exps.begin(), exps.end(), [&ret](const ExPolygon p) { + ret.emplace_back(convert(p)); + }); + return ret; +} + +inline ExPolygon convert(const BoostPolygon& p) { + ExPolygon ret; + + auto&& ctour = ClipperPath_to_Slic3rPolygon(p.Contour); + ctour.points.pop_back(); + + auto&& holes = ClipperPaths_to_Slic3rPolygons(p.Holes); + for(auto&& h : holes) h.points.pop_back(); + + ret.contour = ctour; + ret.holes = holes; + return ret; +} + +struct Contour3D { + Pointf3s points; + std::vector indices; + + void merge(const Contour3D& ctour) { + auto s3 = coord_t(points.size()); + auto s = coord_t(indices.size()); + + points.insert(points.end(), ctour.points.begin(), ctour.points.end()); + indices.insert(indices.end(), ctour.indices.begin(), ctour.indices.end()); + + for(auto n = s; n < indices.size(); n++) { + auto& idx = indices[n]; idx.x += s3; idx.y += s3; idx.z += s3; + } + } +}; + +inline Contour3D convert(const Polygons& triangles, coord_t z, bool dir) { + + Pointf3s points; + points.reserve(3*triangles.size()); + std::vector indices; + indices.reserve(points.size()); + + for(auto& tr : triangles) { + auto c = coord_t(points.size()), b = c++, a = c++; + if(dir) indices.emplace_back(a, b, c); + else indices.emplace_back(c, b, a); + for(auto& p : tr.points) { + points.emplace_back(Pointf3::new_unscale(p.x, p.y, z)); + } + } + + return {points, indices}; +} + +inline Contour3D roofs(const ExPolygon& poly, coord_t z_distance) { + Polygons triangles; + poly.triangulate_pp(&triangles); + + auto lower = convert(triangles, 0, true); + auto upper = convert(triangles, z_distance, false); + lower.merge(upper); + return lower; +} + +inline Contour3D inner_bed(const ExPolygon& poly, coord_t depth) { + Polygons triangles; + poly.triangulate_pp(&triangles); + + auto bottom = convert(triangles, -depth, false); + auto lines = poly.lines(); + + // Generate outer walls + auto fp = [](const Point& p, Point::coord_type z) { + return Pointf3::new_unscale(p.x, p.y, z); + }; + + for(auto& l : lines) { + auto s = bottom.points.size(); + + bottom.points.emplace_back(fp(l.a, -depth)); + bottom.points.emplace_back(fp(l.b, -depth)); + bottom.points.emplace_back(fp(l.a, 0)); + bottom.points.emplace_back(fp(l.b, 0)); + + bottom.indices.emplace_back(s, s + 1, s + 3); + bottom.indices.emplace_back(s, s + 3, s + 2); + } + + return bottom; +} + +inline TriangleMesh mesh(const Contour3D& ctour) { + return {ctour.points, ctour.indices}; +} + +inline TriangleMesh mesh(Contour3D&& ctour) { + return {std::move(ctour.points), std::move(ctour.indices)}; +} + +inline void offset(BoostPolygon& sh, Point::coord_type distance) { + using ClipperLib::ClipperOffset; + using ClipperLib::jtRound; + using ClipperLib::etClosedPolygon; + using ClipperLib::Paths; + using namespace libnest2d; + + // If the input is not at least a triangle, we can not do this algorithm + if(sh.Contour.size() <= 3 || + std::any_of(sh.Holes.begin(), sh.Holes.end(), + [](const PathImpl& p) { return p.size() <= 3; }) + ) throw GeometryException(GeomErr::OFFSET); + + ClipperOffset offs; + Paths result; + offs.AddPath(sh.Contour, jtRound, etClosedPolygon); + offs.AddPaths(sh.Holes, jtRound, etClosedPolygon); + offs.Execute(result, static_cast(distance)); + + // Offsetting reverts the orientation and also removes the last vertex + // so boost will not have a closed polygon. + + bool found_the_contour = false; + for(auto& r : result) { + if(ClipperLib::Orientation(r)) { + // We don't like if the offsetting generates more than one contour + // but throwing would be an overkill. Instead, we should warn the + // caller about the inability to create correct geometries + if(!found_the_contour) { + sh.Contour = r; + ClipperLib::ReversePath(sh.Contour); + sh.Contour.push_back(sh.Contour.front()); + found_the_contour = true; + } else { + dout() << "Warning: offsetting result is invalid!"; + /* TODO warning */ + } + } else { + // TODO If there are multiple contours we can't be sure which hole + // belongs to the first contour. (But in this case the situation is + // bad enough to let it go...) + sh.Holes.push_back(r); + ClipperLib::ReversePath(sh.Holes.back()); + sh.Holes.back().push_back(sh.Holes.back().front()); + } + } +} + + +inline ExPolygon concave_hull(const ExPolygons& polys) { + concavehull::PointVector pv; + size_t s = 0; + + for(auto& ep : polys) s += ep.contour.points.size(); + pv.reserve(s); + + std::cout << polys.size() << std::endl; + +// for(const ExPolygon& ep : polys) { + auto& ep = polys.front(); + + for(auto& v : ep.contour.points) + pv.emplace_back(Pointf::new_unscale(v.x, v.y)); + + std::reverse(pv.begin(), pv.end()); + +// auto frontpoint = ep.contour.points.front(); +// pv.emplace_back(Pointf::new_unscale(frontpoint)); +// } + + auto result = concavehull::ConcaveHull(pv, 3, true); + + if(result.empty()) std::cout << "Empty concave hull!!!" << std::endl; + std::cout << "result size " << result.size() << std::endl; + + ExPolygon ret; + ret.contour.points.reserve(result.size() + 1); + + std::reverse(result.begin(), result.end()); + + for(auto& p : result) { + std::cout << p.x << " " << p.y << std::endl; + ret.contour.points.emplace_back(Point::new_scale(p.x, p.y)); + } + + reverse(ret.contour); + +// ret.contour.points.emplace_back(ret.contour.points.front()); + + return ret; +} + +} + +void ground_layer(const TriangleMesh &mesh, ExPolygons &output, float h) +{ + TriangleMesh m = mesh; + TriangleMeshSlicer slicer(&m); + + std::vector tmp; + + slicer.slice({h}, &tmp); + + output = tmp.front(); +} + +void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out) +{ + using libnest2d::PolygonImpl; + using boost::geometry::convex_hull; + using boost::geometry::is_valid; + + static const Point::coord_type INNER_OFFSET_DIST = 2000000; + static const Point::coord_type OFFSET_DIST = 5000000; + static const Point::coord_type HEIGHT = 10000000; + + // 1: Offset the ground layer + auto concaveh = ground_layer.front(); //concave_hull(ground_layer); + concaveh.holes.clear(); + +// BoostPolygon chull_boost; +// convex_hull(convert(ground_layer), chull_boost); +// auto concaveh = convert(chull_boost); + +// auto pool = roofs(concaveh, HEIGHT); + +// // Generate outer walls +// auto fp = [](const Point& p, Point::coord_type z) { +// return Pointf3::new_unscale(p.x, p.y, z); +// }; + +// auto lines = concaveh.lines(); +// std::cout << "lines: " << lines.size() << std::endl; +// for(auto& l : lines) { +// auto s = pool.points.size(); + +// pool.points.emplace_back(fp(l.a, 0)); +// pool.points.emplace_back(fp(l.b, 0)); +// pool.points.emplace_back(fp(l.a, HEIGHT)); +// pool.points.emplace_back(fp(l.b, HEIGHT)); + +// pool.indices.emplace_back(s, s + 3, s + 1); +// pool.indices.emplace_back(s, s + 2, s + 3); +// } + +// out = mesh(pool); + + BoostPolygon chull_boost = convert(concaveh); +// convex_hull(convert(ground_layer), chull_boost); + + offset(chull_boost, INNER_OFFSET_DIST); + auto chull_outer_boost = chull_boost; + offset(chull_outer_boost, OFFSET_DIST); + + + // Convert back to Slic3r format + ExPolygon chull_inner = convert(chull_boost); + ExPolygon chull_outer = convert(chull_outer_boost); + + ExPolygon top_poly; + top_poly.contour = chull_outer.contour; + top_poly.holes.emplace_back(chull_inner.contour); + reverse(top_poly.holes.back()); + + Contour3D pool; + + Polygons top_triangles, bottom_triangles; + top_poly.triangulate_pp(&top_triangles); + chull_outer.triangulate_pp(&bottom_triangles); + auto top_plate = convert(top_triangles, 0, false); + auto bottom_plate = convert(bottom_triangles, -HEIGHT, true); + auto innerbed = inner_bed(chull_inner, HEIGHT/2); + + // Generate outer walls + auto fp = [](const Point& p, Point::coord_type z) { + return Pointf3::new_unscale(p.x, p.y, z); + }; + + auto lines = chull_outer.lines(); + for(auto& l : lines) { + auto s = pool.points.size(); + + pool.points.emplace_back(fp(l.a, -HEIGHT)); + pool.points.emplace_back(fp(l.b, -HEIGHT)); + pool.points.emplace_back(fp(l.a, 0)); + pool.points.emplace_back(fp(l.b, 0)); + + pool.indices.emplace_back(s, s + 3, s + 1); + pool.indices.emplace_back(s, s + 2, s + 3); + } + + pool.merge(top_plate); + pool.merge(bottom_plate); + pool.merge(innerbed); + + out = mesh(pool); +} + +} +} diff --git a/xs/src/libslic3r/SLASupportPool.hpp b/xs/src/libslic3r/SLABasePool.hpp similarity index 54% rename from xs/src/libslic3r/SLASupportPool.hpp rename to xs/src/libslic3r/SLABasePool.hpp index c23949384..47bc0569d 100644 --- a/xs/src/libslic3r/SLASupportPool.hpp +++ b/xs/src/libslic3r/SLABasePool.hpp @@ -10,14 +10,16 @@ class TriangleMesh; namespace sla { -using Mesh3D = TriangleMesh; -using GroundLayer = std::vector; +using ExPolygons = std::vector; /// Calculate the polygon representing the slice of the lowest layer of mesh -void ground_layer(const Mesh3D& mesh, GroundLayer& output); +void ground_layer(const TriangleMesh& mesh, + ExPolygons& output, + float height = .1f); /// Calculate the pool for the mesh for SLA printing -void create_base_pool(const GroundLayer& ground_layer, Mesh3D& output_mesh); +void create_base_pool(const ExPolygons& ground_layer, + TriangleMesh& output_mesh); } diff --git a/xs/src/libslic3r/SLASupportPool.cpp b/xs/src/libslic3r/SLASupportPool.cpp deleted file mode 100644 index 12dae72d4..000000000 --- a/xs/src/libslic3r/SLASupportPool.cpp +++ /dev/null @@ -1,33 +0,0 @@ - -#include "SLASupportPool.hpp" -#include "ExPolygon.hpp" -#include "TriangleMesh.hpp" -#include "boost/geometry.hpp" - -namespace Slic3r { namespace sla { - -void ground_layer(const Mesh3D &mesh, GroundLayer &output) -{ - Mesh3D m = mesh; - TriangleMeshSlicer slicer(&m); - - std::vector tmp; - - slicer.slice({0.1f}, &tmp); - - output = tmp.front(); -} - -void create_base_pool(const GroundLayer &ground_layer, Mesh3D& out) -{ - // 1: Offset the ground layer - ExPolygon in; - ExPolygon chull; - boost::geometry::convex_hull(in, chull); - - // 2: triangulate the ground layer - -} - -} -} diff --git a/xs/src/slic3r/AppController.cpp b/xs/src/slic3r/AppController.cpp index a0b07dbc7..9246affb4 100644 --- a/xs/src/slic3r/AppController.cpp +++ b/xs/src/slic3r/AppController.cpp @@ -17,6 +17,7 @@ #include #include #include +#include namespace Slic3r { @@ -259,109 +260,124 @@ void PrintController::slice() void PrintController::slice_to_png() { - auto exd = query_png_export_data(); - - if(exd.zippath.empty()) return; - - auto presetbundle = GUI::get_preset_bundle(); - - assert(presetbundle); - - auto conf = presetbundle->full_config(); - - conf.validate(); - - try { - print_->apply_config(conf); - print_->validate(); - } catch(std::exception& e) { - report_issue(IssueType::ERR, e.what(), "Error"); - return; + std::vector ground_layers; + ground_layers.reserve(print_->objects.size()); + for(auto o : print_->objects) { + ModelObject *mo = o->model_object(); + ground_layers.emplace_back(); + sla::ground_layer(mo->mesh(), ground_layers.back()); } - // TODO: copy the model and work with the copy only - bool correction = false; - if(exd.corr_x != 1.0 || exd.corr_y != 1.0 || exd.corr_z != 1.0) { - correction = true; - print_->invalidate_all_steps(); - - for(auto po : print_->objects) { - po->model_object()->scale( - Pointf3(exd.corr_x, exd.corr_y, exd.corr_z) - ); - po->model_object()->invalidate_bounding_box(); - po->reload_model_instances(); - po->invalidate_all_steps(); - } + for(auto& lyr : ground_layers) { + TriangleMesh mesh; + sla::create_base_pool(lyr, mesh); + mesh.write_ascii("out.stl"); } - // Turn back the correction scaling on the model. - auto scale_back = [this, correction, exd]() { - if(correction) { // scale the model back - print_->invalidate_all_steps(); - for(auto po : print_->objects) { - po->model_object()->scale( - Pointf3(1.0/exd.corr_x, 1.0/exd.corr_y, 1.0/exd.corr_z) - ); - po->model_object()->invalidate_bounding_box(); - po->reload_model_instances(); - po->invalidate_all_steps(); - } - } - }; - auto print_bb = print_->bounding_box(); +// auto exd = query_png_export_data(); - // If the print does not fit into the print area we should cry about it. - if(unscale(print_bb.size().x) > exd.width_mm || - unscale(print_bb.size().y) > exd.height_mm) { - std::stringstream ss; +// if(exd.zippath.empty()) return; - ss << _(L("Print will not fit and will be truncated!")) << "\n" - << _(L("Width needed: ")) << unscale(print_bb.size().x) << " mm\n" - << _(L("Height needed: ")) << unscale(print_bb.size().y) << " mm\n"; +// auto presetbundle = GUI::get_preset_bundle(); - if(!report_issue(IssueType::WARN_Q, ss.str(), _(L("Warning")))) { - scale_back(); - return; - } - } +// assert(presetbundle); -// std::async(supports_asynch()? std::launch::async : std::launch::deferred, -// [this, exd, scale_back]() -// { +// auto conf = presetbundle->full_config(); - auto pri = create_progress_indicator( - 200, _(L("Slicing to zipped png files..."))); +// conf.validate(); - try { - pri->update(0, _(L("Slicing..."))); - slice(pri); - } catch (std::exception& e) { - pri->cancel(); - report_issue(IssueType::ERR, e.what(), _(L("Exception occured"))); - scale_back(); - return; - } +// try { +// print_->apply_config(conf); +// print_->validate(); +// } catch(std::exception& e) { +// report_issue(IssueType::ERR, e.what(), "Error"); +// return; +// } - auto pbak = print_->progressindicator; - print_->progressindicator = pri; +// // TODO: copy the model and work with the copy only +// bool correction = false; +// if(exd.corr_x != 1.0 || exd.corr_y != 1.0 || exd.corr_z != 1.0) { +// correction = true; +// print_->invalidate_all_steps(); - try { - print_to( *print_, exd.zippath, - exd.width_mm, exd.height_mm, - exd.width_px, exd.height_px, - exd.exp_time_s, exd.exp_time_first_s); +// for(auto po : print_->objects) { +// po->model_object()->scale( +// Pointf3(exd.corr_x, exd.corr_y, exd.corr_z) +// ); +// po->model_object()->invalidate_bounding_box(); +// po->reload_model_instances(); +// po->invalidate_all_steps(); +// } +// } - } catch (std::exception& e) { - pri->cancel(); - report_issue(IssueType::ERR, e.what(), _(L("Exception occured"))); - } +// // Turn back the correction scaling on the model. +// auto scale_back = [this, correction, exd]() { +// if(correction) { // scale the model back +// print_->invalidate_all_steps(); +// for(auto po : print_->objects) { +// po->model_object()->scale( +// Pointf3(1.0/exd.corr_x, 1.0/exd.corr_y, 1.0/exd.corr_z) +// ); +// po->model_object()->invalidate_bounding_box(); +// po->reload_model_instances(); +// po->invalidate_all_steps(); +// } +// } +// }; - print_->progressindicator = pbak; - scale_back(); +// auto print_bb = print_->bounding_box(); -// }); +// // If the print does not fit into the print area we should cry about it. +// if(unscale(print_bb.size().x) > exd.width_mm || +// unscale(print_bb.size().y) > exd.height_mm) { +// std::stringstream ss; + +// ss << _(L("Print will not fit and will be truncated!")) << "\n" +// << _(L("Width needed: ")) << unscale(print_bb.size().x) << " mm\n" +// << _(L("Height needed: ")) << unscale(print_bb.size().y) << " mm\n"; + +// if(!report_issue(IssueType::WARN_Q, ss.str(), _(L("Warning")))) { +// scale_back(); +// return; +// } +// } + +//// std::async(supports_asynch()? std::launch::async : std::launch::deferred, +//// [this, exd, scale_back]() +//// { + +// auto pri = create_progress_indicator( +// 200, _(L("Slicing to zipped png files..."))); + +// try { +// pri->update(0, _(L("Slicing..."))); +// slice(pri); +// } catch (std::exception& e) { +// pri->cancel(); +// report_issue(IssueType::ERR, e.what(), _(L("Exception occured"))); +// scale_back(); +// return; +// } + +// auto pbak = print_->progressindicator; +// print_->progressindicator = pri; + +// try { +// print_to( *print_, exd.zippath, +// exd.width_mm, exd.height_mm, +// exd.width_px, exd.height_px, +// exd.exp_time_s, exd.exp_time_first_s); + +// } catch (std::exception& e) { +// pri->cancel(); +// report_issue(IssueType::ERR, e.what(), _(L("Exception occured"))); +// } + +// print_->progressindicator = pbak; +// scale_back(); + +//// }); } void IProgressIndicator::message_fmt(