Merge remote-tracking branch 'origin/master' into ys_color_print_extension
This commit is contained in:
commit
c564f693e9
89 changed files with 5205 additions and 1103 deletions
|
@ -375,7 +375,7 @@ public:
|
|||
|
||||
for(unsigned idx = 0; idx < fixeditems.size(); ++idx) {
|
||||
Item& itm = fixeditems[idx];
|
||||
itm.markAsFixedInBin(0);
|
||||
itm.markAsFixedInBin(itm.binId());
|
||||
}
|
||||
|
||||
m_pck.configure(m_pconf);
|
||||
|
|
|
@ -22,6 +22,8 @@ add_library(libslic3r STATIC
|
|||
Config.hpp
|
||||
EdgeGrid.cpp
|
||||
EdgeGrid.hpp
|
||||
ElephantFootCompensation.cpp
|
||||
ElephantFootCompensation.hpp
|
||||
ExPolygon.cpp
|
||||
ExPolygon.hpp
|
||||
ExPolygonCollection.cpp
|
||||
|
@ -222,6 +224,7 @@ target_link_libraries(libslic3r
|
|||
qhull
|
||||
semver
|
||||
TBB::tbb
|
||||
# OpenVDB::openvdb
|
||||
${CMAKE_DL_LIBS}
|
||||
)
|
||||
|
||||
|
|
|
@ -107,8 +107,7 @@ void AddOuterPolyNodeToExPolygons(ClipperLib::PolyNode& polynode, ExPolygons* ex
|
|||
}
|
||||
}
|
||||
|
||||
ExPolygons
|
||||
PolyTreeToExPolygons(ClipperLib::PolyTree& polytree)
|
||||
ExPolygons PolyTreeToExPolygons(ClipperLib::PolyTree& polytree)
|
||||
{
|
||||
ExPolygons retval;
|
||||
for (int i = 0; i < polytree.ChildCount(); ++i)
|
||||
|
@ -151,8 +150,7 @@ Slic3r::Polylines ClipperPaths_to_Slic3rPolylines(const ClipperLib::Paths &input
|
|||
return retval;
|
||||
}
|
||||
|
||||
ExPolygons
|
||||
ClipperPaths_to_Slic3rExPolygons(const ClipperLib::Paths &input)
|
||||
ExPolygons ClipperPaths_to_Slic3rExPolygons(const ClipperLib::Paths &input)
|
||||
{
|
||||
// init Clipper
|
||||
ClipperLib::Clipper clipper;
|
||||
|
@ -167,8 +165,7 @@ ClipperPaths_to_Slic3rExPolygons(const ClipperLib::Paths &input)
|
|||
return PolyTreeToExPolygons(polytree);
|
||||
}
|
||||
|
||||
ClipperLib::Path
|
||||
Slic3rMultiPoint_to_ClipperPath(const MultiPoint &input)
|
||||
ClipperLib::Path Slic3rMultiPoint_to_ClipperPath(const MultiPoint &input)
|
||||
{
|
||||
ClipperLib::Path retval;
|
||||
for (Points::const_iterator pit = input.points.begin(); pit != input.points.end(); ++pit)
|
||||
|
@ -176,8 +173,7 @@ Slic3rMultiPoint_to_ClipperPath(const MultiPoint &input)
|
|||
return retval;
|
||||
}
|
||||
|
||||
ClipperLib::Path
|
||||
Slic3rMultiPoint_to_ClipperPath_reversed(const Slic3r::MultiPoint &input)
|
||||
ClipperLib::Path Slic3rMultiPoint_to_ClipperPath_reversed(const Slic3r::MultiPoint &input)
|
||||
{
|
||||
ClipperLib::Path output;
|
||||
output.reserve(input.points.size());
|
||||
|
@ -521,7 +517,7 @@ T _clipper_do(const ClipperLib::ClipType clipType,
|
|||
|
||||
// Fix of #117: A large fractal pyramid takes ages to slice
|
||||
// The Clipper library has difficulties processing overlapping polygons.
|
||||
// Namely, the function Clipper::JoinCommonEdges() has potentially a terrible time complexity if the output
|
||||
// Namely, the function ClipperLib::JoinCommonEdges() has potentially a terrible time complexity if the output
|
||||
// of the operation is of the PolyTree type.
|
||||
// This function implmenets a following workaround:
|
||||
// 1) Peform the Clipper operation with the output to Paths. This method handles overlaps in a reasonable time.
|
||||
|
@ -918,4 +914,320 @@ Polygons top_level_islands(const Slic3r::Polygons &polygons)
|
|||
return out;
|
||||
}
|
||||
|
||||
// Outer offset shall not split the input contour into multiples. It is expected, that the solution will be non empty and it will contain just a single polygon.
|
||||
ClipperLib::Paths fix_after_outer_offset(const ClipperLib::Path &input, ClipperLib::PolyFillType filltype, bool reverse_result)
|
||||
{
|
||||
ClipperLib::Paths solution;
|
||||
if (! input.empty()) {
|
||||
ClipperLib::Clipper clipper;
|
||||
clipper.AddPath(input, ClipperLib::ptSubject, true);
|
||||
clipper.ReverseSolution(reverse_result);
|
||||
clipper.Execute(ClipperLib::ctUnion, solution, filltype, filltype);
|
||||
}
|
||||
return solution;
|
||||
}
|
||||
|
||||
// Inner offset may split the source contour into multiple contours, but one shall not be inside the other.
|
||||
ClipperLib::Paths fix_after_inner_offset(const ClipperLib::Path &input, ClipperLib::PolyFillType filltype, bool reverse_result)
|
||||
{
|
||||
ClipperLib::Paths solution;
|
||||
if (! input.empty()) {
|
||||
ClipperLib::Clipper clipper;
|
||||
clipper.AddPath(input, ClipperLib::ptSubject, true);
|
||||
ClipperLib::IntRect r = clipper.GetBounds();
|
||||
r.left -= 10; r.top -= 10; r.right += 10; r.bottom += 10;
|
||||
if (filltype == ClipperLib::pftPositive)
|
||||
clipper.AddPath({ ClipperLib::IntPoint(r.left, r.bottom), ClipperLib::IntPoint(r.left, r.top), ClipperLib::IntPoint(r.right, r.top), ClipperLib::IntPoint(r.right, r.bottom) }, ClipperLib::ptSubject, true);
|
||||
else
|
||||
clipper.AddPath({ ClipperLib::IntPoint(r.left, r.bottom), ClipperLib::IntPoint(r.right, r.bottom), ClipperLib::IntPoint(r.right, r.top), ClipperLib::IntPoint(r.left, r.top) }, ClipperLib::ptSubject, true);
|
||||
clipper.ReverseSolution(reverse_result);
|
||||
clipper.Execute(ClipperLib::ctUnion, solution, filltype, filltype);
|
||||
if (! solution.empty())
|
||||
solution.erase(solution.begin());
|
||||
}
|
||||
return solution;
|
||||
}
|
||||
|
||||
ClipperLib::Path mittered_offset_path_scaled(const Points &contour, const std::vector<float> &deltas, double miter_limit)
|
||||
{
|
||||
assert(contour.size() == deltas.size());
|
||||
#ifndef NDEBUG
|
||||
// Verify that the deltas are either all positive, or all negative.
|
||||
bool positive = false;
|
||||
bool negative = false;
|
||||
for (float delta : deltas)
|
||||
if (delta < 0.f)
|
||||
negative = true;
|
||||
else if (delta > 0.f)
|
||||
positive = true;
|
||||
assert(! (negative && positive));
|
||||
#endif /* NDEBUG */
|
||||
|
||||
ClipperLib::Path out;
|
||||
|
||||
if (deltas.size() > 2)
|
||||
{
|
||||
out.reserve(contour.size() * 2);
|
||||
|
||||
// Clamp miter limit to 2.
|
||||
miter_limit = (miter_limit > 2.) ? 2. / (miter_limit * miter_limit) : 0.5;
|
||||
|
||||
// perpenduclar vector
|
||||
auto perp = [](const Vec2d &v) -> Vec2d { return Vec2d(v.y(), - v.x()); };
|
||||
|
||||
// Add a new point to the output, scale by CLIPPER_OFFSET_SCALE and round to ClipperLib::cInt.
|
||||
auto add_offset_point = [&out](Vec2d pt) {
|
||||
pt *= double(CLIPPER_OFFSET_SCALE);
|
||||
pt += Vec2d(0.5 - (pt.x() < 0), 0.5 - (pt.y() < 0));
|
||||
out.emplace_back(ClipperLib::cInt(pt.x()), ClipperLib::cInt(pt.y()));
|
||||
};
|
||||
|
||||
// Minimum edge length, squared.
|
||||
double lmin = *std::max_element(deltas.begin(), deltas.end()) * CLIPPER_OFFSET_SHORTEST_EDGE_FACTOR;
|
||||
double l2min = lmin * lmin;
|
||||
// Minimum angle to consider two edges to be parallel.
|
||||
double sin_min_parallel = EPSILON + 1. / double(CLIPPER_OFFSET_SCALE);
|
||||
|
||||
// Find the last point further from pt by l2min.
|
||||
Vec2d pt = contour.front().cast<double>();
|
||||
size_t iprev = contour.size() - 1;
|
||||
Vec2d ptprev;
|
||||
for (; iprev > 0; -- iprev) {
|
||||
ptprev = contour[iprev].cast<double>();
|
||||
if ((ptprev - pt).squaredNorm() > l2min)
|
||||
break;
|
||||
}
|
||||
|
||||
if (iprev != 0) {
|
||||
size_t ilast = iprev;
|
||||
// Normal to the (pt - ptprev) segment.
|
||||
Vec2d nprev = perp(pt - ptprev).normalized();
|
||||
for (size_t i = 0; ; ) {
|
||||
// Find the next point further from pt by l2min.
|
||||
size_t j = i + 1;
|
||||
Vec2d ptnext;
|
||||
for (; j <= ilast; ++ j) {
|
||||
ptnext = contour[j].cast<double>();
|
||||
double l2 = (ptnext - pt).squaredNorm();
|
||||
if (l2 > l2min)
|
||||
break;
|
||||
}
|
||||
if (j > ilast)
|
||||
ptnext = contour.front().cast<double>();
|
||||
|
||||
// Normal to the (ptnext - pt) segment.
|
||||
Vec2d nnext = perp(ptnext - pt).normalized();
|
||||
|
||||
double delta = deltas[i];
|
||||
double sin_a = clamp(-1., 1., cross2(nprev, nnext));
|
||||
double convex = sin_a * delta;
|
||||
if (convex <= - sin_min_parallel) {
|
||||
// Concave corner.
|
||||
add_offset_point(pt + nprev * delta);
|
||||
add_offset_point(pt);
|
||||
add_offset_point(pt + nnext * delta);
|
||||
} else if (convex < sin_min_parallel) {
|
||||
// Nearly parallel.
|
||||
add_offset_point((nprev.dot(nnext) > 0.) ? (pt + nprev * delta) : pt);
|
||||
} else {
|
||||
// Convex corner
|
||||
double dot = nprev.dot(nnext);
|
||||
double r = 1. + dot;
|
||||
if (r >= miter_limit)
|
||||
add_offset_point(pt + (nprev + nnext) * (delta / r));
|
||||
else {
|
||||
double dx = std::tan(std::atan2(sin_a, dot) / 4.);
|
||||
Vec2d newpt1 = pt + (nprev - perp(nprev) * dx) * delta;
|
||||
Vec2d newpt2 = pt + (nnext + perp(nnext) * dx) * delta;
|
||||
#ifndef NDEBUG
|
||||
Vec2d vedge = 0.5 * (newpt1 + newpt2) - pt;
|
||||
double dist_norm = vedge.norm();
|
||||
assert(std::abs(dist_norm - delta) < EPSILON);
|
||||
#endif /* NDEBUG */
|
||||
add_offset_point(newpt1);
|
||||
add_offset_point(newpt2);
|
||||
}
|
||||
}
|
||||
|
||||
if (i == ilast)
|
||||
break;
|
||||
|
||||
ptprev = pt;
|
||||
nprev = nnext;
|
||||
pt = ptnext;
|
||||
i = j;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
{
|
||||
ClipperLib::Path polytmp(out);
|
||||
unscaleClipperPolygon(polytmp);
|
||||
Slic3r::Polygon offsetted = ClipperPath_to_Slic3rPolygon(polytmp);
|
||||
BoundingBox bbox = get_extents(contour);
|
||||
bbox.merge(get_extents(offsetted));
|
||||
static int iRun = 0;
|
||||
SVG svg(debug_out_path("mittered_offset_path_scaled-%d.svg", iRun ++).c_str(), bbox);
|
||||
svg.draw_outline(Polygon(contour), "blue", scale_(0.01));
|
||||
svg.draw_outline(offsetted, "red", scale_(0.01));
|
||||
svg.draw(contour, "blue", scale_(0.03));
|
||||
svg.draw((Points)offsetted, "blue", scale_(0.03));
|
||||
}
|
||||
#endif
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
Polygons variable_offset_inner(const ExPolygon &expoly, const std::vector<std::vector<float>> &deltas, double miter_limit)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
// Verify that the deltas are all non positive.
|
||||
for (const std::vector<float> &ds : deltas)
|
||||
for (float delta : ds)
|
||||
assert(delta <= 0.);
|
||||
assert(expoly.holes.size() + 1 == deltas.size());
|
||||
#endif /* NDEBUG */
|
||||
|
||||
// 1) Offset the outer contour.
|
||||
ClipperLib::Paths contours = fix_after_inner_offset(mittered_offset_path_scaled(expoly.contour.points, deltas.front(), miter_limit), ClipperLib::pftNegative, true);
|
||||
|
||||
// 2) Offset the holes one by one, collect the results.
|
||||
ClipperLib::Paths holes;
|
||||
holes.reserve(expoly.holes.size());
|
||||
for (const Polygon& hole : expoly.holes)
|
||||
append(holes, fix_after_outer_offset(mittered_offset_path_scaled(hole, deltas[1 + &hole - expoly.holes.data()], miter_limit), ClipperLib::pftPositive, false));
|
||||
|
||||
// 3) Subtract holes from the contours.
|
||||
ClipperLib::Paths output;
|
||||
if (holes.empty())
|
||||
output = std::move(contours);
|
||||
else {
|
||||
ClipperLib::Clipper clipper;
|
||||
clipper.Clear();
|
||||
clipper.AddPaths(contours, ClipperLib::ptSubject, true);
|
||||
clipper.AddPaths(holes, ClipperLib::ptClip, true);
|
||||
clipper.Execute(ClipperLib::ctDifference, output, ClipperLib::pftNonZero, ClipperLib::pftNonZero);
|
||||
}
|
||||
|
||||
// 4) Unscale the output.
|
||||
unscaleClipperPolygons(output);
|
||||
return ClipperPaths_to_Slic3rPolygons(output);
|
||||
}
|
||||
|
||||
Polygons variable_offset_outer(const ExPolygon &expoly, const std::vector<std::vector<float>> &deltas, double miter_limit)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
// Verify that the deltas are all non positive.
|
||||
for (const std::vector<float>& ds : deltas)
|
||||
for (float delta : ds)
|
||||
assert(delta >= 0.);
|
||||
assert(expoly.holes.size() + 1 == deltas.size());
|
||||
#endif /* NDEBUG */
|
||||
|
||||
// 1) Offset the outer contour.
|
||||
ClipperLib::Paths contours = fix_after_outer_offset(mittered_offset_path_scaled(expoly.contour.points, deltas.front(), miter_limit), ClipperLib::pftPositive, false);
|
||||
|
||||
// 2) Offset the holes one by one, collect the results.
|
||||
ClipperLib::Paths holes;
|
||||
holes.reserve(expoly.holes.size());
|
||||
for (const Polygon& hole : expoly.holes)
|
||||
append(holes, fix_after_inner_offset(mittered_offset_path_scaled(hole, deltas[1 + &hole - expoly.holes.data()], miter_limit), ClipperLib::pftPositive, true));
|
||||
|
||||
// 3) Subtract holes from the contours.
|
||||
ClipperLib::Paths output;
|
||||
if (holes.empty())
|
||||
output = std::move(contours);
|
||||
else {
|
||||
ClipperLib::Clipper clipper;
|
||||
clipper.Clear();
|
||||
clipper.AddPaths(contours, ClipperLib::ptSubject, true);
|
||||
clipper.AddPaths(holes, ClipperLib::ptClip, true);
|
||||
clipper.Execute(ClipperLib::ctDifference, output, ClipperLib::pftNonZero, ClipperLib::pftNonZero);
|
||||
}
|
||||
|
||||
// 4) Unscale the output.
|
||||
unscaleClipperPolygons(output);
|
||||
return ClipperPaths_to_Slic3rPolygons(output);
|
||||
}
|
||||
|
||||
ExPolygons variable_offset_outer_ex(const ExPolygon &expoly, const std::vector<std::vector<float>> &deltas, double miter_limit)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
// Verify that the deltas are all non positive.
|
||||
for (const std::vector<float>& ds : deltas)
|
||||
for (float delta : ds)
|
||||
assert(delta >= 0.);
|
||||
assert(expoly.holes.size() + 1 == deltas.size());
|
||||
#endif /* NDEBUG */
|
||||
|
||||
// 1) Offset the outer contour.
|
||||
ClipperLib::Paths contours = fix_after_outer_offset(mittered_offset_path_scaled(expoly.contour.points, deltas.front(), miter_limit), ClipperLib::pftPositive, false);
|
||||
|
||||
// 2) Offset the holes one by one, collect the results.
|
||||
ClipperLib::Paths holes;
|
||||
holes.reserve(expoly.holes.size());
|
||||
for (const Polygon& hole : expoly.holes)
|
||||
append(holes, fix_after_inner_offset(mittered_offset_path_scaled(hole, deltas[1 + &hole - expoly.holes.data()], miter_limit), ClipperLib::pftPositive, true));
|
||||
|
||||
// 3) Subtract holes from the contours.
|
||||
unscaleClipperPolygons(contours);
|
||||
ExPolygons output;
|
||||
if (holes.empty()) {
|
||||
output.reserve(contours.size());
|
||||
for (ClipperLib::Path &path : contours)
|
||||
output.emplace_back(ClipperPath_to_Slic3rPolygon(path));
|
||||
} else {
|
||||
ClipperLib::Clipper clipper;
|
||||
unscaleClipperPolygons(holes);
|
||||
clipper.AddPaths(contours, ClipperLib::ptSubject, true);
|
||||
clipper.AddPaths(holes, ClipperLib::ptClip, true);
|
||||
ClipperLib::PolyTree polytree;
|
||||
clipper.Execute(ClipperLib::ctDifference, polytree, ClipperLib::pftNonZero, ClipperLib::pftNonZero);
|
||||
output = PolyTreeToExPolygons(polytree);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
|
||||
ExPolygons variable_offset_inner_ex(const ExPolygon &expoly, const std::vector<std::vector<float>> &deltas, double miter_limit)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
// Verify that the deltas are all non positive.
|
||||
for (const std::vector<float>& ds : deltas)
|
||||
for (float delta : ds)
|
||||
assert(delta <= 0.);
|
||||
assert(expoly.holes.size() + 1 == deltas.size());
|
||||
#endif /* NDEBUG */
|
||||
|
||||
// 1) Offset the outer contour.
|
||||
ClipperLib::Paths contours = fix_after_inner_offset(mittered_offset_path_scaled(expoly.contour.points, deltas.front(), miter_limit), ClipperLib::pftNegative, false);
|
||||
|
||||
// 2) Offset the holes one by one, collect the results.
|
||||
ClipperLib::Paths holes;
|
||||
holes.reserve(expoly.holes.size());
|
||||
for (const Polygon& hole : expoly.holes)
|
||||
append(holes, fix_after_outer_offset(mittered_offset_path_scaled(hole, deltas[1 + &hole - expoly.holes.data()], miter_limit), ClipperLib::pftNegative, true));
|
||||
|
||||
// 3) Subtract holes from the contours.
|
||||
unscaleClipperPolygons(contours);
|
||||
ExPolygons output;
|
||||
if (holes.empty()) {
|
||||
output.reserve(contours.size());
|
||||
for (ClipperLib::Path &path : contours)
|
||||
output.emplace_back(ClipperPath_to_Slic3rPolygon(path));
|
||||
} else {
|
||||
ClipperLib::Clipper clipper;
|
||||
unscaleClipperPolygons(holes);
|
||||
clipper.AddPaths(contours, ClipperLib::ptSubject, true);
|
||||
clipper.AddPaths(holes, ClipperLib::ptClip, true);
|
||||
ClipperLib::PolyTree polytree;
|
||||
clipper.Execute(ClipperLib::ctDifference, polytree, ClipperLib::pftNonZero, ClipperLib::pftNonZero);
|
||||
output = PolyTreeToExPolygons(polytree);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -238,6 +238,11 @@ void safety_offset(ClipperLib::Paths* paths);
|
|||
|
||||
Polygons top_level_islands(const Slic3r::Polygons &polygons);
|
||||
|
||||
Polygons variable_offset_inner(const ExPolygon &expoly, const std::vector<std::vector<float>> &deltas, double miter_limit = 2.);
|
||||
Polygons variable_offset_outer(const ExPolygon &expoly, const std::vector<std::vector<float>> &deltas, double miter_limit = 2.);
|
||||
ExPolygons variable_offset_outer_ex(const ExPolygon &expoly, const std::vector<std::vector<float>> &deltas, double miter_limit = 2.);
|
||||
ExPolygons variable_offset_inner_ex(const ExPolygon &expoly, const std::vector<std::vector<float>> &deltas, double miter_limit = 2.);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -113,6 +113,7 @@ void EdgeGrid::Grid::create(const ExPolygonCollection &expolygons, coord_t resol
|
|||
// m_contours has been initialized. Now fill in the edge grid.
|
||||
void EdgeGrid::Grid::create_from_m_contours(coord_t resolution)
|
||||
{
|
||||
assert(resolution > 0);
|
||||
// 1) Measure the bounding box.
|
||||
for (size_t i = 0; i < m_contours.size(); ++ i) {
|
||||
const Slic3r::Points &pts = *m_contours[i];
|
||||
|
@ -281,7 +282,11 @@ void EdgeGrid::Grid::create_from_m_contours(coord_t resolution)
|
|||
Visitor(std::vector<std::pair<size_t, size_t>> &cell_data, std::vector<Cell> &cells, size_t cols) :
|
||||
cell_data(cell_data), cells(cells), cols(cols), i(0), j(0) {}
|
||||
|
||||
void operator()(coord_t iy, coord_t ix) { cell_data[cells[iy*cols + ix].end++] = std::pair<size_t, size_t>(i, j); }
|
||||
inline bool operator()(coord_t iy, coord_t ix) {
|
||||
cell_data[cells[iy*cols + ix].end++] = std::pair<size_t, size_t>(i, j);
|
||||
// Continue traversing the grid along the edge.
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<std::pair<size_t, size_t>> &cell_data;
|
||||
std::vector<Cell> &cells;
|
||||
|
@ -1017,8 +1022,139 @@ float EdgeGrid::Grid::signed_distance_bilinear(const Point &pt) const
|
|||
|
||||
return f;
|
||||
}
|
||||
|
||||
bool EdgeGrid::Grid::signed_distance_edges(const Point &pt, coord_t search_radius, coordf_t &result_min_dist, bool *pon_segment) const {
|
||||
|
||||
EdgeGrid::Grid::ClosestPointResult EdgeGrid::Grid::closest_point(const Point &pt, coord_t search_radius) const
|
||||
{
|
||||
BoundingBox bbox;
|
||||
bbox.min = bbox.max = Point(pt(0) - m_bbox.min(0), pt(1) - m_bbox.min(1));
|
||||
bbox.defined = true;
|
||||
// Upper boundary, round to grid and test validity.
|
||||
bbox.max(0) += search_radius;
|
||||
bbox.max(1) += search_radius;
|
||||
ClosestPointResult result;
|
||||
if (bbox.max(0) < 0 || bbox.max(1) < 0)
|
||||
return result;
|
||||
bbox.max(0) /= m_resolution;
|
||||
bbox.max(1) /= m_resolution;
|
||||
if ((size_t)bbox.max(0) >= m_cols)
|
||||
bbox.max(0) = m_cols - 1;
|
||||
if ((size_t)bbox.max(1) >= m_rows)
|
||||
bbox.max(1) = m_rows - 1;
|
||||
// Lower boundary, round to grid and test validity.
|
||||
bbox.min(0) -= search_radius;
|
||||
bbox.min(1) -= search_radius;
|
||||
if (bbox.min(0) < 0)
|
||||
bbox.min(0) = 0;
|
||||
if (bbox.min(1) < 0)
|
||||
bbox.min(1) = 0;
|
||||
bbox.min(0) /= m_resolution;
|
||||
bbox.min(1) /= m_resolution;
|
||||
// Is the interval empty?
|
||||
if (bbox.min(0) > bbox.max(0) ||
|
||||
bbox.min(1) > bbox.max(1))
|
||||
return result;
|
||||
// Traverse all cells in the bounding box.
|
||||
double d_min = double(search_radius);
|
||||
// Signum of the distance field at pt.
|
||||
int sign_min = 0;
|
||||
double l2_seg_min = 1.;
|
||||
for (int r = bbox.min(1); r <= bbox.max(1); ++ r) {
|
||||
for (int c = bbox.min(0); c <= bbox.max(0); ++ c) {
|
||||
const Cell &cell = m_cells[r * m_cols + c];
|
||||
for (size_t i = cell.begin; i < cell.end; ++ i) {
|
||||
const size_t contour_idx = m_cell_data[i].first;
|
||||
const Slic3r::Points &pts = *m_contours[contour_idx];
|
||||
size_t ipt = m_cell_data[i].second;
|
||||
// End points of the line segment.
|
||||
const Slic3r::Point &p1 = pts[ipt];
|
||||
const Slic3r::Point &p2 = pts[(ipt + 1 == pts.size()) ? 0 : ipt + 1];
|
||||
const Slic3r::Point v_seg = p2 - p1;
|
||||
const Slic3r::Point v_pt = pt - p1;
|
||||
// dot(p2-p1, pt-p1)
|
||||
int64_t t_pt = int64_t(v_seg(0)) * int64_t(v_pt(0)) + int64_t(v_seg(1)) * int64_t(v_pt(1));
|
||||
// l2 of seg
|
||||
int64_t l2_seg = int64_t(v_seg(0)) * int64_t(v_seg(0)) + int64_t(v_seg(1)) * int64_t(v_seg(1));
|
||||
if (t_pt < 0) {
|
||||
// Closest to p1.
|
||||
double dabs = sqrt(int64_t(v_pt(0)) * int64_t(v_pt(0)) + int64_t(v_pt(1)) * int64_t(v_pt(1)));
|
||||
if (dabs < d_min) {
|
||||
// Previous point.
|
||||
const Slic3r::Point &p0 = pts[(ipt == 0) ? (pts.size() - 1) : ipt - 1];
|
||||
Slic3r::Point v_seg_prev = p1 - p0;
|
||||
int64_t t2_pt = int64_t(v_seg_prev(0)) * int64_t(v_pt(0)) + int64_t(v_seg_prev(1)) * int64_t(v_pt(1));
|
||||
if (t2_pt > 0) {
|
||||
// Inside the wedge between the previous and the next segment.
|
||||
d_min = dabs;
|
||||
// Set the signum depending on whether the vertex is convex or reflex.
|
||||
int64_t det = int64_t(v_seg_prev(0)) * int64_t(v_seg(1)) - int64_t(v_seg_prev(1)) * int64_t(v_seg(0));
|
||||
assert(det != 0);
|
||||
sign_min = (det > 0) ? 1 : -1;
|
||||
result.contour_idx = contour_idx;
|
||||
result.start_point_idx = ipt;
|
||||
result.t = 0.;
|
||||
#ifndef NDEBUG
|
||||
Vec2d vfoot = (p1 - pt).cast<double>();
|
||||
double dist_foot = vfoot.norm();
|
||||
double dist_foot_err = dist_foot - d_min;
|
||||
assert(std::abs(dist_foot_err) < 1e-7 * d_min);
|
||||
#endif /* NDEBUG */
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (t_pt > l2_seg) {
|
||||
// Closest to p2. Then p2 is the starting point of another segment, which shall be discovered in the same cell.
|
||||
continue;
|
||||
} else {
|
||||
// Closest to the segment.
|
||||
assert(t_pt >= 0 && t_pt <= l2_seg);
|
||||
int64_t d_seg = int64_t(v_seg(1)) * int64_t(v_pt(0)) - int64_t(v_seg(0)) * int64_t(v_pt(1));
|
||||
double d = double(d_seg) / sqrt(double(l2_seg));
|
||||
double dabs = std::abs(d);
|
||||
if (dabs < d_min) {
|
||||
d_min = dabs;
|
||||
sign_min = (d_seg < 0) ? -1 : ((d_seg == 0) ? 0 : 1);
|
||||
l2_seg_min = l2_seg;
|
||||
result.contour_idx = contour_idx;
|
||||
result.start_point_idx = ipt;
|
||||
result.t = t_pt;
|
||||
#ifndef NDEBUG
|
||||
Vec2d foot = p1.cast<double>() * (1. - result.t / l2_seg_min) + p2.cast<double>() * (result.t / l2_seg_min);
|
||||
Vec2d vfoot = foot - pt.cast<double>();
|
||||
double dist_foot = vfoot.norm();
|
||||
double dist_foot_err = dist_foot - d_min;
|
||||
assert(std::abs(dist_foot_err) < 1e-7 * d_min);
|
||||
#endif /* NDEBUG */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (result.contour_idx != -1 && d_min <= double(search_radius)) {
|
||||
result.distance = d_min * sign_min;
|
||||
result.t /= l2_seg_min;
|
||||
assert(result.t >= 0. && result.t < 1.);
|
||||
#ifndef NDEBUG
|
||||
{
|
||||
const Slic3r::Points &pts = *m_contours[result.contour_idx];
|
||||
const Slic3r::Point &p1 = pts[result.start_point_idx];
|
||||
const Slic3r::Point &p2 = pts[(result.start_point_idx + 1 == pts.size()) ? 0 : result.start_point_idx + 1];
|
||||
Vec2d vfoot;
|
||||
if (result.t == 0)
|
||||
vfoot = p1.cast<double>() - pt.cast<double>();
|
||||
else
|
||||
vfoot = p1.cast<double>() * (1. - result.t) + p2.cast<double>() * result.t - pt.cast<double>();
|
||||
double dist_foot = vfoot.norm();
|
||||
double dist_foot_err = dist_foot - std::abs(result.distance);
|
||||
assert(std::abs(dist_foot_err) < 1e-7 * std::abs(result.distance));
|
||||
}
|
||||
#endif /* NDEBUG */
|
||||
} else
|
||||
result = ClosestPointResult();
|
||||
return result;
|
||||
}
|
||||
|
||||
bool EdgeGrid::Grid::signed_distance_edges(const Point &pt, coord_t search_radius, coordf_t &result_min_dist, bool *pon_segment) const
|
||||
{
|
||||
BoundingBox bbox;
|
||||
bbox.min = bbox.max = Point(pt(0) - m_bbox.min(0), pt(1) - m_bbox.min(1));
|
||||
bbox.defined = true;
|
||||
|
@ -1047,7 +1183,7 @@ bool EdgeGrid::Grid::signed_distance_edges(const Point &pt, coord_t search_radiu
|
|||
bbox.min(1) > bbox.max(1))
|
||||
return false;
|
||||
// Traverse all cells in the bounding box.
|
||||
float d_min = search_radius;
|
||||
double d_min = double(search_radius);
|
||||
// Signum of the distance field at pt.
|
||||
int sign_min = 0;
|
||||
bool on_segment = false;
|
||||
|
|
|
@ -25,6 +25,8 @@ public:
|
|||
void create(const ExPolygons &expolygons, coord_t resolution);
|
||||
void create(const ExPolygonCollection &expolygons, coord_t resolution);
|
||||
|
||||
const std::vector<const Slic3r::Points*>& contours() const { return m_contours; }
|
||||
|
||||
#if 0
|
||||
// Test, whether the edges inside the grid intersect with the polygons provided.
|
||||
bool intersect(const MultiPoint &polyline, bool closed);
|
||||
|
@ -46,7 +48,19 @@ public:
|
|||
float signed_distance_bilinear(const Point &pt) const;
|
||||
|
||||
// Calculate a signed distance to the contours in search_radius from the point.
|
||||
bool signed_distance_edges(const Point &pt, coord_t search_radius, coordf_t &result_min_dist, bool *pon_segment = NULL) const;
|
||||
struct ClosestPointResult {
|
||||
size_t contour_idx = size_t(-1);
|
||||
size_t start_point_idx = size_t(-1);
|
||||
// Signed distance to the closest point.
|
||||
double distance = std::numeric_limits<double>::max();
|
||||
// Parameter of the closest point on edge starting with start_point_idx <0, 1)
|
||||
double t = 0.;
|
||||
|
||||
bool valid() const { return contour_idx != size_t(-1); }
|
||||
};
|
||||
ClosestPointResult closest_point(const Point &pt, coord_t search_radius) const;
|
||||
|
||||
bool signed_distance_edges(const Point &pt, coord_t search_radius, coordf_t &result_min_dist, bool *pon_segment = nullptr) const;
|
||||
|
||||
// Calculate a signed distance to the contours in search_radius from the point. If no edge is found in search_radius,
|
||||
// return an interpolated value from m_signed_distance_field, if it exists.
|
||||
|
@ -65,7 +79,7 @@ public:
|
|||
std::vector<std::pair<ContourEdge, ContourEdge>> intersecting_edges() const;
|
||||
bool has_intersecting_edges() const;
|
||||
|
||||
template<typename FUNCTION> void visit_cells_intersecting_line(Slic3r::Point p1, Slic3r::Point p2, FUNCTION func) const
|
||||
template<typename VISITOR> void visit_cells_intersecting_line(Slic3r::Point p1, Slic3r::Point p2, VISITOR &visitor) const
|
||||
{
|
||||
// End points of the line segment.
|
||||
p1(0) -= m_bbox.min(0);
|
||||
|
@ -82,8 +96,7 @@ public:
|
|||
assert(ixb >= 0 && size_t(ixb) < m_cols);
|
||||
assert(iyb >= 0 && size_t(iyb) < m_rows);
|
||||
// Account for the end points.
|
||||
func(iy, ix);
|
||||
if (ix == ixb && iy == iyb)
|
||||
if (! visitor(iy, ix) || (ix == ixb && iy == iyb))
|
||||
// Both ends fall into the same cell.
|
||||
return;
|
||||
// Raster the centeral part of the line.
|
||||
|
@ -113,7 +126,8 @@ public:
|
|||
ey = int64_t(dx) * m_resolution;
|
||||
iy += 1;
|
||||
}
|
||||
func(iy, ix);
|
||||
if (! visitor(iy, ix))
|
||||
return;
|
||||
} while (ix != ixb || iy != iyb);
|
||||
}
|
||||
else {
|
||||
|
@ -131,7 +145,8 @@ public:
|
|||
ey = int64_t(dx) * m_resolution;
|
||||
iy -= 1;
|
||||
}
|
||||
func(iy, ix);
|
||||
if (! visitor(iy, ix))
|
||||
return;
|
||||
} while (ix != ixb || iy != iyb);
|
||||
}
|
||||
}
|
||||
|
@ -153,7 +168,8 @@ public:
|
|||
ey = int64_t(dx) * m_resolution;
|
||||
iy += 1;
|
||||
}
|
||||
func(iy, ix);
|
||||
if (! visitor(iy, ix))
|
||||
return;
|
||||
} while (ix != ixb || iy != iyb);
|
||||
}
|
||||
else {
|
||||
|
@ -185,7 +201,8 @@ public:
|
|||
ey = int64_t(dx) * m_resolution;
|
||||
iy -= 1;
|
||||
}
|
||||
func(iy, ix);
|
||||
if (! visitor(iy, ix))
|
||||
return;
|
||||
} while (ix != ixb || iy != iyb);
|
||||
}
|
||||
}
|
||||
|
|
321
src/libslic3r/ElephantFootCompensation.cpp
Normal file
321
src/libslic3r/ElephantFootCompensation.cpp
Normal file
|
@ -0,0 +1,321 @@
|
|||
#include "clipper/clipper_z.hpp"
|
||||
|
||||
#include "libslic3r.h"
|
||||
#include "ClipperUtils.hpp"
|
||||
#include "EdgeGrid.hpp"
|
||||
#include "ExPolygon.hpp"
|
||||
#include "ElephantFootCompensation.hpp"
|
||||
#include "Flow.hpp"
|
||||
#include "Geometry.hpp"
|
||||
#include "SVG.hpp"
|
||||
|
||||
#include <cmath>
|
||||
#include <cassert>
|
||||
|
||||
// #define CONTOUR_DISTANCE_DEBUG_SVG
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
struct ResampledPoint {
|
||||
ResampledPoint(size_t idx_src, bool interpolated, double curve_parameter) : idx_src(idx_src), interpolated(interpolated), curve_parameter(curve_parameter) {}
|
||||
|
||||
size_t idx_src;
|
||||
// Is this point interpolated or initial?
|
||||
bool interpolated;
|
||||
// Euclidean distance along the curve from the 0th point.
|
||||
double curve_parameter;
|
||||
};
|
||||
|
||||
std::vector<float> contour_distance(const EdgeGrid::Grid &grid, const size_t idx_contour, const Slic3r::Points &contour, const std::vector<ResampledPoint> &resampled_point_parameters, double search_radius)
|
||||
{
|
||||
assert(! contour.empty());
|
||||
assert(contour.size() >= 2);
|
||||
|
||||
std::vector<float> out;
|
||||
|
||||
if (contour.size() > 2)
|
||||
{
|
||||
#ifdef CONTOUR_DISTANCE_DEBUG_SVG
|
||||
static int iRun = 0;
|
||||
++ iRun;
|
||||
BoundingBox bbox = get_extents(contour);
|
||||
bbox.merge(grid.bbox());
|
||||
ExPolygon expoly_grid;
|
||||
expoly_grid.contour = Polygon(*grid.contours().front());
|
||||
for (size_t i = 1; i < grid.contours().size(); ++ i)
|
||||
expoly_grid.holes.emplace_back(Polygon(*grid.contours()[i]));
|
||||
#endif
|
||||
struct Visitor {
|
||||
Visitor(const EdgeGrid::Grid &grid, const size_t idx_contour, const std::vector<ResampledPoint> &resampled_point_parameters, double dist_same_contour_reject) :
|
||||
grid(grid), idx_contour(idx_contour), resampled_point_parameters(resampled_point_parameters), dist_same_contour_reject(dist_same_contour_reject) {}
|
||||
|
||||
void init(const size_t aidx_point_start, const Point &apt_start, Vec2d dir, const double radius) {
|
||||
this->idx_point_start = aidx_point_start;
|
||||
this->pt = apt_start.cast<double>() + SCALED_EPSILON * dir;
|
||||
dir *= radius;
|
||||
this->pt_start = this->pt.cast<coord_t>();
|
||||
// Trim the vector by the grid's bounding box.
|
||||
const BoundingBox &bbox = this->grid.bbox();
|
||||
double t = 1.;
|
||||
for (size_t axis = 0; axis < 2; ++ axis) {
|
||||
double dx = std::abs(dir(axis));
|
||||
if (dx >= EPSILON) {
|
||||
double tedge = (dir(axis) > 0) ? (double(bbox.max(axis)) - EPSILON - this->pt(axis)) : (this->pt(axis) - double(bbox.min(axis)) - EPSILON);
|
||||
if (tedge < dx)
|
||||
t = tedge / dx;
|
||||
}
|
||||
}
|
||||
this->dir = dir;
|
||||
if (t < 1.)
|
||||
dir *= t;
|
||||
this->pt_end = (this->pt + dir).cast<coord_t>();
|
||||
this->t_min = 1.;
|
||||
}
|
||||
|
||||
bool operator()(coord_t iy, coord_t ix) {
|
||||
// Called with a row and colum of the grid cell, which is intersected by a line.
|
||||
auto cell_data_range = this->grid.cell_data_range(iy, ix);
|
||||
bool valid = true;
|
||||
for (auto it_contour_and_segment = cell_data_range.first; it_contour_and_segment != cell_data_range.second; ++ it_contour_and_segment) {
|
||||
// End points of the line segment and their vector.
|
||||
auto segment = this->grid.segment(*it_contour_and_segment);
|
||||
if (Geometry::segments_intersect(segment.first, segment.second, this->pt_start, this->pt_end)) {
|
||||
// The two segments intersect. Calculate the intersection.
|
||||
Vec2d pt2 = segment.first.cast<double>();
|
||||
Vec2d dir2 = segment.second.cast<double>() - pt2;
|
||||
Vec2d vptpt2 = pt - pt2;
|
||||
double denom = dir(0) * dir2(1) - dir2(0) * dir(1);
|
||||
|
||||
if (std::abs(denom) >= EPSILON) {
|
||||
double t = cross2(dir2, vptpt2) / denom;
|
||||
assert(t > 0. && t <= 1.);
|
||||
bool this_valid = true;
|
||||
if (it_contour_and_segment->first == idx_contour) {
|
||||
// The intersected segment originates from the same contour as the starting point.
|
||||
// Reject the intersection if it is close to the starting point.
|
||||
// Find the start and end points of this segment
|
||||
double param_lo = resampled_point_parameters[idx_point_start].curve_parameter;
|
||||
double param_hi;
|
||||
double param_end = resampled_point_parameters.back().curve_parameter;
|
||||
{
|
||||
const Slic3r::Points &ipts = *grid.contours()[it_contour_and_segment->first];
|
||||
size_t ipt = it_contour_and_segment->second;
|
||||
ResampledPoint key(ipt, false, 0.);
|
||||
auto lower = [](const ResampledPoint& l, const ResampledPoint r) { return l.idx_src < r.idx_src || (l.idx_src == r.idx_src && int(l.interpolated) > int(r.interpolated)); };
|
||||
auto it = std::lower_bound(resampled_point_parameters.begin(), resampled_point_parameters.end(), key, lower);
|
||||
assert(it != resampled_point_parameters.end() && it->idx_src == ipt && ! it->interpolated);
|
||||
double t2 = cross2(dir, vptpt2) / denom;
|
||||
assert(t2 >= 0. && t2 <= 1.);
|
||||
if (++ ipt == ipts.size())
|
||||
param_hi = t2 * dir2.norm();
|
||||
else
|
||||
param_hi = it->curve_parameter + t2 * dir2.norm();
|
||||
}
|
||||
if (param_lo > param_hi)
|
||||
std::swap(param_lo, param_hi);
|
||||
assert(param_lo >= 0. && param_lo <= param_end);
|
||||
assert(param_hi >= 0. && param_hi <= param_end);
|
||||
this_valid = param_hi > param_lo + dist_same_contour_reject && param_hi - param_end < param_lo - dist_same_contour_reject;
|
||||
}
|
||||
if (t < this->t_min) {
|
||||
this->t_min = t;
|
||||
valid = this_valid;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (! valid)
|
||||
this->t_min = 1.;
|
||||
}
|
||||
// Continue traversing the grid along the edge.
|
||||
return true;
|
||||
}
|
||||
|
||||
const EdgeGrid::Grid &grid;
|
||||
const size_t idx_contour;
|
||||
const std::vector<ResampledPoint> &resampled_point_parameters;
|
||||
const double dist_same_contour_reject;
|
||||
|
||||
size_t idx_point_start;
|
||||
Point pt_start;
|
||||
Point pt_end;
|
||||
Vec2d pt;
|
||||
Vec2d dir;
|
||||
// Minium parameter along the vector (pt_end - pt_start).
|
||||
double t_min;
|
||||
} visitor(grid, idx_contour, resampled_point_parameters, search_radius);
|
||||
|
||||
const Point *pt_this = &contour.back();
|
||||
size_t idx_pt_this = contour.size() - 1;
|
||||
const Point *pt_prev = pt_this - 1;
|
||||
// perpenduclar vector
|
||||
auto perp = [](const Vec2d& v) -> Vec2d { return Vec2d(v.y(), -v.x()); };
|
||||
Vec2d vprev = (*pt_this - *pt_prev).cast<double>().normalized();
|
||||
out.reserve(contour.size() + 1);
|
||||
for (const Point &pt_next : contour) {
|
||||
Vec2d vnext = (pt_next - *pt_this).cast<double>().normalized();
|
||||
Vec2d dir = - (perp(vprev) + perp(vnext)).normalized();
|
||||
Vec2d dir_perp = perp(dir);
|
||||
double cross = cross2(vprev, vnext);
|
||||
double dot = vprev.dot(vnext);
|
||||
double a = (cross < 0 || dot > 0.5) ? (M_PI / 3.) : (0.48 * acos(std::min(1., - dot)));
|
||||
// Throw rays, collect distances.
|
||||
std::vector<double> distances;
|
||||
int num_rays = 15;
|
||||
|
||||
#ifdef CONTOUR_DISTANCE_DEBUG_SVG
|
||||
SVG svg(debug_out_path("contour_distance_raycasted-%d-%d.svg", iRun, &pt_next - contour.data()).c_str(), bbox);
|
||||
svg.draw(expoly_grid);
|
||||
svg.draw_outline(Polygon(contour), "blue", scale_(0.01));
|
||||
svg.draw(*pt_this, "red", scale_(0.1));
|
||||
#endif /* CONTOUR_DISTANCE_DEBUG_SVG */
|
||||
|
||||
for (int i = - num_rays + 1; i < num_rays; ++ i) {
|
||||
double angle = a * i / (int)num_rays;
|
||||
double c = cos(angle);
|
||||
double s = sin(angle);
|
||||
Vec2d v = c * dir + s * dir_perp;
|
||||
visitor.init(idx_pt_this, *pt_this, v, search_radius);
|
||||
grid.visit_cells_intersecting_line(visitor.pt_start, visitor.pt_end, visitor);
|
||||
distances.emplace_back(visitor.t_min);
|
||||
#ifdef CONTOUR_DISTANCE_DEBUG_SVG
|
||||
svg.draw(Line(visitor.pt_start, visitor.pt_end), "yellow", scale_(0.01));
|
||||
if (visitor.t_min < 1.) {
|
||||
Vec2d pt = visitor.pt + visitor.dir * visitor.t_min;
|
||||
svg.draw(Point(pt), "red", scale_(0.1));
|
||||
}
|
||||
#endif /* CONTOUR_DISTANCE_DEBUG_SVG */
|
||||
}
|
||||
#ifdef CONTOUR_DISTANCE_DEBUG_SVG
|
||||
svg.Close();
|
||||
#endif /* CONTOUR_DISTANCE_DEBUG_SVG */
|
||||
std::sort(distances.begin(), distances.end());
|
||||
#if 0
|
||||
double median = distances[distances.size() / 2];
|
||||
double standard_deviation = 0;
|
||||
for (double d : distances)
|
||||
standard_deviation += (d - median) * (d - median);
|
||||
standard_deviation = sqrt(standard_deviation / (distances.size() - 1));
|
||||
double avg = 0;
|
||||
size_t cnt = 0;
|
||||
for (double d : distances)
|
||||
if (d > median - standard_deviation - EPSILON && d < median + standard_deviation + EPSILON) {
|
||||
avg += d;
|
||||
++ cnt;
|
||||
}
|
||||
avg /= double(cnt);
|
||||
out.emplace_back(float(avg * search_radius));
|
||||
#else
|
||||
out.emplace_back(float(distances.front() * search_radius));
|
||||
#endif
|
||||
#ifdef CONTOUR_DISTANCE_DEBUG_SVG
|
||||
printf("contour_distance_raycasted-%d-%d.svg - distance %lf\n", iRun, &pt_next - contour.data(), unscale<double>(out.back()));
|
||||
#endif /* CONTOUR_DISTANCE_DEBUG_SVG */
|
||||
pt_this = &pt_next;
|
||||
idx_pt_this = &pt_next - contour.data();
|
||||
vprev = vnext;
|
||||
}
|
||||
// Rotate the vector by one item.
|
||||
out.emplace_back(out.front());
|
||||
out.erase(out.begin());
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
Points resample_polygon(const Points &contour, double dist, std::vector<ResampledPoint> &resampled_point_parameters)
|
||||
{
|
||||
Points out;
|
||||
out.reserve(contour.size());
|
||||
resampled_point_parameters.reserve(contour.size());
|
||||
if (contour.size() > 2) {
|
||||
Vec2d pt_prev = contour.back().cast<double>();
|
||||
for (const Point &pt : contour) {
|
||||
size_t idx_this = &pt - contour.data();
|
||||
const Vec2d pt_this = pt.cast<double>();
|
||||
const Vec2d v = pt_this - pt_prev;
|
||||
const double l = v.norm();
|
||||
const size_t n = size_t(ceil(l / dist));
|
||||
const double l_step = l / n;
|
||||
for (size_t i = 1; i < n; ++ i) {
|
||||
double interpolation_parameter = double(i) / n;
|
||||
Vec2d new_pt = pt_prev + v * interpolation_parameter;
|
||||
out.emplace_back(new_pt.cast<coord_t>());
|
||||
resampled_point_parameters.emplace_back(idx_this, true, l_step);
|
||||
}
|
||||
out.emplace_back(pt);
|
||||
resampled_point_parameters.emplace_back(idx_this, false, l_step);
|
||||
pt_prev = pt_this;
|
||||
}
|
||||
for (size_t i = 1; i < resampled_point_parameters.size(); ++i)
|
||||
resampled_point_parameters[i].curve_parameter += resampled_point_parameters[i - 1].curve_parameter;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
static inline void smooth_compensation(std::vector<float> &compensation, float strength, size_t num_iterations)
|
||||
{
|
||||
std::vector<float> out(compensation);
|
||||
for (size_t iter = 0; iter < num_iterations; ++ iter) {
|
||||
for (size_t i = 0; i < compensation.size(); ++ i) {
|
||||
float prev = (i == 0) ? compensation.back() : compensation[i - 1];
|
||||
float next = (i + 1 == compensation.size()) ? compensation.front() : compensation[i + 1];
|
||||
float laplacian = compensation[i] * (1.f - strength) + 0.5f * strength * (prev + next);
|
||||
// Compensations are negative. Only apply the laplacian if it leads to lower compensation.
|
||||
out[i] = std::max(laplacian, compensation[i]);
|
||||
}
|
||||
out.swap(compensation);
|
||||
}
|
||||
}
|
||||
|
||||
ExPolygon elephant_foot_compensation(const ExPolygon &input_expoly, const Flow &external_perimeter_flow, const double compensation)
|
||||
{
|
||||
// The contour shall be wide enough to apply the external perimeter plus compensation on both sides.
|
||||
double min_contour_width = double(external_perimeter_flow.scaled_width() + external_perimeter_flow.scaled_spacing());
|
||||
double scaled_compensation = scale_(compensation);
|
||||
double min_contour_width_compensated = min_contour_width + 2. * scaled_compensation;
|
||||
// Make the search radius a bit larger for the averaging in contour_distance over a fan of rays to work.
|
||||
double search_radius = min_contour_width_compensated + min_contour_width * 0.5;
|
||||
|
||||
EdgeGrid::Grid grid;
|
||||
ExPolygon simplified = input_expoly.simplify(SCALED_EPSILON).front();
|
||||
BoundingBox bbox = get_extents(simplified.contour);
|
||||
bbox.offset(SCALED_EPSILON);
|
||||
grid.set_bbox(bbox);
|
||||
grid.create(simplified, coord_t(0.7 * search_radius));
|
||||
std::vector<std::vector<float>> deltas;
|
||||
deltas.reserve(simplified.holes.size() + 1);
|
||||
ExPolygon resampled(simplified);
|
||||
for (size_t idx_contour = 0; idx_contour <= simplified.holes.size(); ++ idx_contour) {
|
||||
Polygon &poly = (idx_contour == 0) ? resampled.contour : resampled.holes[idx_contour - 1];
|
||||
std::vector<ResampledPoint> resampled_point_parameters;
|
||||
poly.points = resample_polygon(poly.points, scale_(0.5), resampled_point_parameters);
|
||||
std::vector<float> dists = contour_distance(grid, idx_contour, poly.points, resampled_point_parameters, search_radius);
|
||||
for (float &d : dists) {
|
||||
// printf("Point %d, Distance: %lf\n", int(&d - dists.data()), unscale<double>(d));
|
||||
// Convert contour width to available compensation distance.
|
||||
if (d < min_contour_width)
|
||||
d = 0.f;
|
||||
else if (d > min_contour_width_compensated)
|
||||
d = - float(scaled_compensation);
|
||||
else
|
||||
d = - (d - float(min_contour_width)) / 2.f;
|
||||
assert(d >= - float(scaled_compensation) && d <= 0.f);
|
||||
}
|
||||
smooth_compensation(dists, 0.4f, 10);
|
||||
deltas.emplace_back(dists);
|
||||
}
|
||||
|
||||
ExPolygons out = variable_offset_inner_ex(resampled, deltas, 2.);
|
||||
return out.front();
|
||||
}
|
||||
|
||||
ExPolygons elephant_foot_compensation(const ExPolygons &input, const Flow &external_perimeter_flow, const double compensation)
|
||||
{
|
||||
ExPolygons out;
|
||||
out.reserve(input.size());
|
||||
for (const ExPolygon &expoly : input)
|
||||
out.emplace_back(elephant_foot_compensation(expoly, external_perimeter_flow, compensation));
|
||||
return out;
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
16
src/libslic3r/ElephantFootCompensation.hpp
Normal file
16
src/libslic3r/ElephantFootCompensation.hpp
Normal file
|
@ -0,0 +1,16 @@
|
|||
#ifndef slic3r_ElephantFootCompensation_hpp_
|
||||
#define slic3r_ElephantFootCompensation_hpp_
|
||||
|
||||
#include "libslic3r.h"
|
||||
#include <vector>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class Flow;
|
||||
|
||||
ExPolygon elephant_foot_compensation(const ExPolygon &input, const Flow &external_perimeter_flow, const double compensation);
|
||||
ExPolygons elephant_foot_compensation(const ExPolygons &input, const Flow &external_perimeter_flow, const double compensation);
|
||||
|
||||
} // Slic3r
|
||||
|
||||
#endif /* slic3r_ElephantFootCompensation_hpp_ */
|
|
@ -28,6 +28,8 @@ public:
|
|||
explicit ExPolygon(Polygon &&contour, Polygon &&hole) : contour(std::move(contour)) { holes.emplace_back(std::move(hole)); }
|
||||
explicit ExPolygon(const Points &contour, const Points &hole) : contour(contour) { holes.emplace_back(hole); }
|
||||
explicit ExPolygon(Points &&contour, Polygon &&hole) : contour(std::move(contour)) { holes.emplace_back(std::move(hole)); }
|
||||
ExPolygon(std::initializer_list<Point> contour) : contour(contour) {}
|
||||
ExPolygon(std::initializer_list<Point> contour, std::initializer_list<Point> hole) : contour(contour), holes({ hole }) {}
|
||||
|
||||
ExPolygon& operator=(const ExPolygon &other) { contour = other.contour; holes = other.holes; return *this; }
|
||||
ExPolygon& operator=(ExPolygon &&other) { contour = std::move(other.contour); holes = std::move(other.holes); return *this; }
|
||||
|
@ -77,6 +79,9 @@ public:
|
|||
Lines lines() const;
|
||||
};
|
||||
|
||||
inline bool operator==(const ExPolygon &lhs, const ExPolygon &rhs) { return lhs.contour == rhs.contour && lhs.holes == rhs.holes; }
|
||||
inline bool operator!=(const ExPolygon &lhs, const ExPolygon &rhs) { return lhs.contour != rhs.contour || lhs.holes != rhs.holes; }
|
||||
|
||||
// Count a nuber of polygons stored inside the vector of expolygons.
|
||||
// Useful for allocating space for polygons when converting expolygons to polygons.
|
||||
inline size_t number_polygons(const ExPolygons &expolys)
|
||||
|
@ -301,6 +306,15 @@ inline bool expolygons_contain(ExPolygons &expolys, const Point &pt)
|
|||
return false;
|
||||
}
|
||||
|
||||
inline ExPolygons expolygons_simplify(const ExPolygons &expolys, double tolerance)
|
||||
{
|
||||
ExPolygons out;
|
||||
out.reserve(expolys.size());
|
||||
for (const ExPolygon &exp : expolys)
|
||||
exp.simplify(tolerance, &out);
|
||||
return out;
|
||||
}
|
||||
|
||||
extern BoundingBox get_extents(const ExPolygon &expolygon);
|
||||
extern BoundingBox get_extents(const ExPolygons &expolygons);
|
||||
extern BoundingBox get_extents_rotated(const ExPolygon &poly, double angle);
|
||||
|
|
|
@ -584,8 +584,16 @@ void AMFParserContext::endElement(const char * /* name */)
|
|||
stl_get_size(&stl);
|
||||
mesh.repair();
|
||||
m_volume->set_mesh(std::move(mesh));
|
||||
// pass false if the mesh offset has been already taken from the data
|
||||
m_volume->center_geometry_after_creation(m_volume->source.input_file.empty());
|
||||
if (m_volume->source.input_file.empty() && (m_volume->type() == ModelVolumeType::MODEL_PART))
|
||||
{
|
||||
m_volume->source.object_idx = (int)m_model.objects.size() - 1;
|
||||
m_volume->source.volume_idx = (int)m_model.objects.back()->volumes.size() - 1;
|
||||
m_volume->center_geometry_after_creation();
|
||||
}
|
||||
else
|
||||
// pass false if the mesh offset has been already taken from the data
|
||||
m_volume->center_geometry_after_creation(m_volume->source.input_file.empty());
|
||||
|
||||
m_volume->calculate_convex_hull();
|
||||
m_volume_facets.clear();
|
||||
m_volume = nullptr;
|
||||
|
@ -799,6 +807,15 @@ bool load_amf_file(const char *path, DynamicPrintConfig *config, Model *model)
|
|||
if (result)
|
||||
ctx.endDocument();
|
||||
|
||||
for (ModelObject* o : model->objects)
|
||||
{
|
||||
for (ModelVolume* v : o->volumes)
|
||||
{
|
||||
if (v->source.input_file.empty() && (v->type() == ModelVolumeType::MODEL_PART))
|
||||
v->source.input_file = path;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -543,7 +543,7 @@ std::vector<GCode::LayerToPrint> GCode::collect_layers_to_print(const PrintObjec
|
|||
//FIXME should we use the printing extruders instead?
|
||||
double gap_over_supports = object.config().support_material_contact_distance;
|
||||
// FIXME should we test object.config().support_material_synchronize_layers ? Currently the support layers are synchronized with object layers iff soluble supports.
|
||||
assert(gap_over_supports != 0. || object.config().support_material_synchronize_layers);
|
||||
assert(! object.config().support_material || gap_over_supports != 0. || object.config().support_material_synchronize_layers);
|
||||
if (gap_over_supports != 0.) {
|
||||
gap_over_supports = std::max(0., gap_over_supports);
|
||||
// Not a soluble support,
|
||||
|
@ -778,22 +778,26 @@ void GCode::_do_export(Print &print, FILE *file)
|
|||
{
|
||||
m_silent_time_estimator.reset();
|
||||
m_silent_time_estimator.set_dialect(print.config().gcode_flavor);
|
||||
m_silent_time_estimator.set_max_acceleration((float)print.config().machine_max_acceleration_extruding.values[1]);
|
||||
m_silent_time_estimator.set_retract_acceleration((float)print.config().machine_max_acceleration_retracting.values[1]);
|
||||
m_silent_time_estimator.set_minimum_feedrate((float)print.config().machine_min_extruding_rate.values[1]);
|
||||
m_silent_time_estimator.set_minimum_travel_feedrate((float)print.config().machine_min_travel_rate.values[1]);
|
||||
m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::X, (float)print.config().machine_max_acceleration_x.values[1]);
|
||||
m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Y, (float)print.config().machine_max_acceleration_y.values[1]);
|
||||
m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Z, (float)print.config().machine_max_acceleration_z.values[1]);
|
||||
m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::E, (float)print.config().machine_max_acceleration_e.values[1]);
|
||||
m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::X, (float)print.config().machine_max_feedrate_x.values[1]);
|
||||
m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Y, (float)print.config().machine_max_feedrate_y.values[1]);
|
||||
m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Z, (float)print.config().machine_max_feedrate_z.values[1]);
|
||||
m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::E, (float)print.config().machine_max_feedrate_e.values[1]);
|
||||
m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::X, (float)print.config().machine_max_jerk_x.values[1]);
|
||||
m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Y, (float)print.config().machine_max_jerk_y.values[1]);
|
||||
m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Z, (float)print.config().machine_max_jerk_z.values[1]);
|
||||
m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::E, (float)print.config().machine_max_jerk_e.values[1]);
|
||||
/* "Stealth mode" values can be just a copy of "normal mode" values
|
||||
* (when they aren't input for a printer preset).
|
||||
* Thus, use back value from values, instead of second one, which could be absent
|
||||
*/
|
||||
m_silent_time_estimator.set_max_acceleration((float)print.config().machine_max_acceleration_extruding.values.back());
|
||||
m_silent_time_estimator.set_retract_acceleration((float)print.config().machine_max_acceleration_retracting.values.back());
|
||||
m_silent_time_estimator.set_minimum_feedrate((float)print.config().machine_min_extruding_rate.values.back());
|
||||
m_silent_time_estimator.set_minimum_travel_feedrate((float)print.config().machine_min_travel_rate.values.back());
|
||||
m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::X, (float)print.config().machine_max_acceleration_x.values.back());
|
||||
m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Y, (float)print.config().machine_max_acceleration_y.values.back());
|
||||
m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Z, (float)print.config().machine_max_acceleration_z.values.back());
|
||||
m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::E, (float)print.config().machine_max_acceleration_e.values.back());
|
||||
m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::X, (float)print.config().machine_max_feedrate_x.values.back());
|
||||
m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Y, (float)print.config().machine_max_feedrate_y.values.back());
|
||||
m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Z, (float)print.config().machine_max_feedrate_z.values.back());
|
||||
m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::E, (float)print.config().machine_max_feedrate_e.values.back());
|
||||
m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::X, (float)print.config().machine_max_jerk_x.values.back());
|
||||
m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Y, (float)print.config().machine_max_jerk_y.values.back());
|
||||
m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Z, (float)print.config().machine_max_jerk_z.values.back());
|
||||
m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::E, (float)print.config().machine_max_jerk_e.values.back());
|
||||
if (print.config().single_extruder_multi_material) {
|
||||
// As of now the fields are shown at the UI dialog in the same combo box as the ramming values, so they
|
||||
// are considered to be active for the single extruder multi-material printers only.
|
||||
|
|
|
@ -285,6 +285,11 @@ void GCodeAnalyzer::_process_gcode_line(GCodeReader&, const GCodeReader::GCodeLi
|
|||
_processM108orM135(line);
|
||||
break;
|
||||
}
|
||||
case 132: // Recall stored home offsets
|
||||
{
|
||||
_processM132(line);
|
||||
break;
|
||||
}
|
||||
case 401: // Repetier: Store x, y and z position
|
||||
{
|
||||
_processM401(line);
|
||||
|
@ -504,6 +509,25 @@ void GCodeAnalyzer::_processM108orM135(const GCodeReader::GCodeLine& line)
|
|||
}
|
||||
}
|
||||
|
||||
void GCodeAnalyzer::_processM132(const GCodeReader::GCodeLine& line)
|
||||
{
|
||||
// This command is used by Makerbot to load the current home position from EEPROM
|
||||
// see: https://github.com/makerbot/s3g/blob/master/doc/GCodeProtocol.md
|
||||
// Using this command to reset the axis origin to zero helps in fixing: https://github.com/prusa3d/PrusaSlicer/issues/3082
|
||||
|
||||
if (line.has_x())
|
||||
_set_axis_origin(X, 0.0f);
|
||||
|
||||
if (line.has_y())
|
||||
_set_axis_origin(Y, 0.0f);
|
||||
|
||||
if (line.has_z())
|
||||
_set_axis_origin(Z, 0.0f);
|
||||
|
||||
if (line.has_e())
|
||||
_set_axis_origin(E, 0.0f);
|
||||
}
|
||||
|
||||
void GCodeAnalyzer::_processM401(const GCodeReader::GCodeLine& line)
|
||||
{
|
||||
if (m_gcode_flavor != gcfRepetier)
|
||||
|
|
|
@ -182,6 +182,9 @@ private:
|
|||
// Set tool (MakerWare and Sailfish flavor)
|
||||
void _processM108orM135(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Recall stored home offsets
|
||||
void _processM132(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Repetier: Store x, y and z position
|
||||
void _processM401(const GCodeReader::GCodeLine& line);
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
#ifndef slic3r_SpiralVase_hpp_
|
||||
#define slic3r_SpiralVase_hpp_
|
||||
|
||||
#include "libslic3r.h"
|
||||
#include "GCodeReader.hpp"
|
||||
#include "../libslic3r.h"
|
||||
#include "../GCodeReader.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
|
|
@ -331,15 +331,18 @@ public:
|
|||
|
||||
// Let the firmware back up the active speed override value.
|
||||
WipeTowerWriter& speed_override_backup()
|
||||
{
|
||||
m_gcode += "M220 B\n";
|
||||
{
|
||||
// This is only supported by Prusa at this point (https://github.com/prusa3d/PrusaSlicer/issues/3114)
|
||||
if (m_gcode_flavor == gcfMarlin)
|
||||
m_gcode += "M220 B\n";
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Let the firmware restore the active speed override value.
|
||||
WipeTowerWriter& speed_override_restore()
|
||||
{
|
||||
m_gcode += "M220 R\n";
|
||||
if (m_gcode_flavor == gcfMarlin)
|
||||
m_gcode += "M220 R\n";
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
|
|
@ -663,7 +663,6 @@ namespace Voronoi { namespace Internal {
|
|||
typedef boost::polygon::point_data<coordinate_type> point_type;
|
||||
typedef boost::polygon::segment_data<coordinate_type> segment_type;
|
||||
typedef boost::polygon::rectangle_data<coordinate_type> rect_type;
|
||||
// typedef voronoi_builder<int> VB;
|
||||
typedef boost::polygon::voronoi_diagram<coordinate_type> VD;
|
||||
typedef VD::cell_type cell_type;
|
||||
typedef VD::cell_type::source_index_type source_index_type;
|
||||
|
@ -710,15 +709,15 @@ namespace Voronoi { namespace Internal {
|
|||
if (cell1.contains_point() && cell2.contains_point()) {
|
||||
point_type p1 = retrieve_point(segments, cell1);
|
||||
point_type p2 = retrieve_point(segments, cell2);
|
||||
origin.x((p1(0) + p2(0)) * 0.5);
|
||||
origin.y((p1(1) + p2(1)) * 0.5);
|
||||
direction.x(p1(1) - p2(1));
|
||||
direction.y(p2(0) - p1(0));
|
||||
origin.x((p1.x() + p2.x()) * 0.5);
|
||||
origin.y((p1.y() + p2.y()) * 0.5);
|
||||
direction.x(p1.y() - p2.y());
|
||||
direction.y(p2.x() - p1.x());
|
||||
} else {
|
||||
origin = cell1.contains_segment() ? retrieve_point(segments, cell2) : retrieve_point(segments, cell1);
|
||||
segment_type segment = cell1.contains_segment() ? segments[cell1.source_index()] : segments[cell2.source_index()];
|
||||
coordinate_type dx = high(segment)(0) - low(segment)(0);
|
||||
coordinate_type dy = high(segment)(1) - low(segment)(1);
|
||||
coordinate_type dx = high(segment).x() - low(segment).x();
|
||||
coordinate_type dy = high(segment).y() - low(segment).y();
|
||||
if ((low(segment) == origin) ^ cell1.contains_point()) {
|
||||
direction.x(dy);
|
||||
direction.y(-dx);
|
||||
|
@ -727,19 +726,19 @@ namespace Voronoi { namespace Internal {
|
|||
direction.y(dx);
|
||||
}
|
||||
}
|
||||
coordinate_type koef = bbox_max_size / (std::max)(fabs(direction(0)), fabs(direction(1)));
|
||||
coordinate_type koef = bbox_max_size / (std::max)(fabs(direction.x()), fabs(direction.y()));
|
||||
if (edge.vertex0() == NULL) {
|
||||
clipped_edge->push_back(point_type(
|
||||
origin(0) - direction(0) * koef,
|
||||
origin(1) - direction(1) * koef));
|
||||
origin.x() - direction.x() * koef,
|
||||
origin.y() - direction.y() * koef));
|
||||
} else {
|
||||
clipped_edge->push_back(
|
||||
point_type(edge.vertex0()->x(), edge.vertex0()->y()));
|
||||
}
|
||||
if (edge.vertex1() == NULL) {
|
||||
clipped_edge->push_back(point_type(
|
||||
origin(0) + direction(0) * koef,
|
||||
origin(1) + direction(1) * koef));
|
||||
origin.x() + direction.x() * koef,
|
||||
origin.y() + direction.y() * koef));
|
||||
} else {
|
||||
clipped_edge->push_back(
|
||||
point_type(edge.vertex1()->x(), edge.vertex1()->y()));
|
||||
|
@ -759,7 +758,7 @@ namespace Voronoi { namespace Internal {
|
|||
|
||||
} /* namespace Internal */ } // namespace Voronoi
|
||||
|
||||
static inline void dump_voronoi_to_svg(const Lines &lines, /* const */ voronoi_diagram<double> &vd, const ThickPolylines *polylines, const char *path)
|
||||
static inline void dump_voronoi_to_svg(const Lines &lines, /* const */ boost::polygon::voronoi_diagram<double> &vd, const ThickPolylines *polylines, const char *path)
|
||||
{
|
||||
const double scale = 0.2;
|
||||
const std::string inputSegmentPointColor = "lightseagreen";
|
||||
|
@ -803,7 +802,7 @@ static inline void dump_voronoi_to_svg(const Lines &lines, /* const */ voronoi_d
|
|||
Voronoi::Internal::point_type(double(it->b(0)), double(it->b(1)))));
|
||||
|
||||
// Color exterior edges.
|
||||
for (voronoi_diagram<double>::const_edge_iterator it = vd.edges().begin(); it != vd.edges().end(); ++it)
|
||||
for (boost::polygon::voronoi_diagram<double>::const_edge_iterator it = vd.edges().begin(); it != vd.edges().end(); ++it)
|
||||
if (!it->is_finite())
|
||||
Voronoi::Internal::color_exterior(&(*it));
|
||||
|
||||
|
@ -818,11 +817,11 @@ static inline void dump_voronoi_to_svg(const Lines &lines, /* const */ voronoi_d
|
|||
|
||||
#if 1
|
||||
// Draw voronoi vertices.
|
||||
for (voronoi_diagram<double>::const_vertex_iterator it = vd.vertices().begin(); it != vd.vertices().end(); ++it)
|
||||
for (boost::polygon::voronoi_diagram<double>::const_vertex_iterator it = vd.vertices().begin(); it != vd.vertices().end(); ++it)
|
||||
if (! internalEdgesOnly || it->color() != Voronoi::Internal::EXTERNAL_COLOR)
|
||||
svg.draw(Point(coord_t((*it)(0)), coord_t((*it)(1))), voronoiPointColor, voronoiPointRadius);
|
||||
svg.draw(Point(coord_t(it->x()), coord_t(it->y())), voronoiPointColor, voronoiPointRadius);
|
||||
|
||||
for (voronoi_diagram<double>::const_edge_iterator it = vd.edges().begin(); it != vd.edges().end(); ++it) {
|
||||
for (boost::polygon::voronoi_diagram<double>::const_edge_iterator it = vd.edges().begin(); it != vd.edges().end(); ++it) {
|
||||
if (primaryEdgesOnly && !it->is_primary())
|
||||
continue;
|
||||
if (internalEdgesOnly && (it->color() == Voronoi::Internal::EXTERNAL_COLOR))
|
||||
|
@ -845,7 +844,7 @@ static inline void dump_voronoi_to_svg(const Lines &lines, /* const */ voronoi_d
|
|||
color = voronoiLineColorSecondary;
|
||||
}
|
||||
for (std::size_t i = 0; i + 1 < samples.size(); ++i)
|
||||
svg.draw(Line(Point(coord_t(samples[i](0)), coord_t(samples[i](1))), Point(coord_t(samples[i+1](0)), coord_t(samples[i+1](1)))), color, voronoiLineWidth);
|
||||
svg.draw(Line(Point(coord_t(samples[i].x()), coord_t(samples[i].y())), Point(coord_t(samples[i+1].x()), coord_t(samples[i+1].y()))), color, voronoiLineWidth);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -11,8 +11,6 @@
|
|||
#include <cereal/access.hpp>
|
||||
|
||||
#include "boost/polygon/voronoi.hpp"
|
||||
using boost::polygon::voronoi_builder;
|
||||
using boost::polygon::voronoi_diagram;
|
||||
|
||||
namespace ClipperLib {
|
||||
class PolyNode;
|
||||
|
@ -192,7 +190,7 @@ class MedialAxis {
|
|||
void build(Polylines* polylines);
|
||||
|
||||
private:
|
||||
class VD : public voronoi_diagram<double> {
|
||||
class VD : public boost::polygon::voronoi_diagram<double> {
|
||||
public:
|
||||
typedef double coord_type;
|
||||
typedef boost::polygon::point_data<coordinate_type> point_type;
|
||||
|
|
|
@ -88,8 +88,12 @@ ExPolygons Layer::merged(float offset_scaled) const
|
|||
offset_scaled2 = float(- EPSILON);
|
||||
}
|
||||
Polygons polygons;
|
||||
for (LayerRegion *layerm : m_regions)
|
||||
append(polygons, offset(to_expolygons(layerm->slices.surfaces), offset_scaled));
|
||||
for (LayerRegion *layerm : m_regions) {
|
||||
const PrintRegionConfig &config = layerm->region()->config();
|
||||
// Our users learned to bend Slic3r to produce empty volumes to act as subtracters. Only add the region if it is non-empty.
|
||||
if (config.bottom_solid_layers > 0 || config.top_solid_layers > 0 || config.fill_density > 0. || config.perimeters > 0)
|
||||
append(polygons, offset(to_expolygons(layerm->slices.surfaces), offset_scaled));
|
||||
}
|
||||
ExPolygons out = union_ex(polygons);
|
||||
if (offset_scaled2 != 0.f)
|
||||
out = offset_ex(out, offset_scaled2);
|
||||
|
|
|
@ -88,7 +88,6 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollec
|
|||
|
||||
void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Polygons *lower_layer_covered)
|
||||
{
|
||||
const Surfaces &surfaces = this->fill_surfaces.surfaces;
|
||||
const bool has_infill = this->region()->config().fill_density.value > 0.;
|
||||
const float margin = float(scale_(EXTERNAL_INFILL_MARGIN));
|
||||
|
||||
|
|
|
@ -254,6 +254,11 @@ Point Polygon::point_projection(const Point &point) const
|
|||
return proj;
|
||||
}
|
||||
|
||||
BoundingBox get_extents(const Points &points)
|
||||
{
|
||||
return BoundingBox(points);
|
||||
}
|
||||
|
||||
BoundingBox get_extents(const Polygon &poly)
|
||||
{
|
||||
return poly.bounding_box();
|
||||
|
|
|
@ -22,7 +22,8 @@ public:
|
|||
const Point& operator[](Points::size_type idx) const { return this->points[idx]; }
|
||||
|
||||
Polygon() {}
|
||||
explicit Polygon(const Points &points): MultiPoint(points) {}
|
||||
explicit Polygon(const Points &points) : MultiPoint(points) {}
|
||||
Polygon(std::initializer_list<Point> points) : MultiPoint(points) {}
|
||||
Polygon(const Polygon &other) : MultiPoint(other.points) {}
|
||||
Polygon(Polygon &&other) : MultiPoint(std::move(other.points)) {}
|
||||
static Polygon new_scale(const std::vector<Vec2d> &points) {
|
||||
|
@ -66,6 +67,10 @@ public:
|
|||
Point point_projection(const Point &point) const;
|
||||
};
|
||||
|
||||
inline bool operator==(const Polygon &lhs, const Polygon &rhs) { return lhs.points == rhs.points; }
|
||||
inline bool operator!=(const Polygon &lhs, const Polygon &rhs) { return lhs.points != rhs.points; }
|
||||
|
||||
extern BoundingBox get_extents(const Points &points);
|
||||
extern BoundingBox get_extents(const Polygon &poly);
|
||||
extern BoundingBox get_extents(const Polygons &polygons);
|
||||
extern BoundingBox get_extents_rotated(const Polygon &poly, double angle);
|
||||
|
@ -102,6 +107,15 @@ inline void polygons_append(Polygons &dst, Polygons &&src)
|
|||
}
|
||||
}
|
||||
|
||||
inline Polygons polygons_simplify(const Polygons &polys, double tolerance)
|
||||
{
|
||||
Polygons out;
|
||||
out.reserve(polys.size());
|
||||
for (const Polygon &p : polys)
|
||||
polygons_append(out, p.simplify(tolerance));
|
||||
return out;
|
||||
}
|
||||
|
||||
inline void polygons_rotate(Polygons &polys, double angle)
|
||||
{
|
||||
const double cos_angle = cos(angle);
|
||||
|
|
|
@ -12,12 +12,11 @@ TrimmedLoop trim_loop(const Polygon &loop, const EdgeGrid::Grid &grid)
|
|||
TrimmedLoop out;
|
||||
|
||||
if (loop.size() >= 2) {
|
||||
size_t cnt = loop.points.size();
|
||||
|
||||
struct Visitor {
|
||||
Visitor(const EdgeGrid::Grid &grid, const Slic3r::Point *pt_prev, const Slic3r::Point *pt_this) : grid(grid), pt_prev(pt_prev), pt_this(pt_this) {}
|
||||
|
||||
void operator()(coord_t iy, coord_t ix) {
|
||||
bool operator()(coord_t iy, coord_t ix) {
|
||||
// Called with a row and colum of the grid cell, which is intersected by a line.
|
||||
auto cell_data_range = grid.cell_data_range(iy, ix);
|
||||
for (auto it_contour_and_segment = cell_data_range.first; it_contour_and_segment != cell_data_range.second; ++ it_contour_and_segment) {
|
||||
|
@ -27,6 +26,8 @@ TrimmedLoop trim_loop(const Polygon &loop, const EdgeGrid::Grid &grid)
|
|||
// The two segments intersect. Add them to the output.
|
||||
}
|
||||
}
|
||||
// Continue traversing the grid along the edge.
|
||||
return true;
|
||||
}
|
||||
|
||||
const EdgeGrid::Grid &grid;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "Print.hpp"
|
||||
#include "BoundingBox.hpp"
|
||||
#include "ClipperUtils.hpp"
|
||||
#include "ElephantFootCompensation.hpp"
|
||||
#include "Geometry.hpp"
|
||||
#include "I18N.hpp"
|
||||
#include "SupportMaterial.hpp"
|
||||
|
@ -1769,6 +1770,7 @@ end:
|
|||
Layer *layer = m_layers[layer_id];
|
||||
// Apply size compensation and perform clipping of multi-part objects.
|
||||
float delta = float(scale_(m_config.xy_size_compensation.value));
|
||||
//FIXME only apply the compensation if no raft is enabled.
|
||||
float elephant_foot_compensation = 0.f;
|
||||
if (layer_id == 0)
|
||||
elephant_foot_compensation = float(scale_(m_config.elefant_foot_compensation.value));
|
||||
|
@ -1789,19 +1791,8 @@ end:
|
|||
to_expolygons(std::move(layerm->slices.surfaces)) :
|
||||
offset_ex(to_expolygons(std::move(layerm->slices.surfaces)), delta);
|
||||
// Apply the elephant foot compensation.
|
||||
if (elephant_foot_compensation > 0) {
|
||||
float elephant_foot_spacing = float(layerm->flow(frExternalPerimeter).scaled_elephant_foot_spacing());
|
||||
float external_perimeter_nozzle = float(scale_(this->print()->config().nozzle_diameter.get_at(layerm->region()->config().perimeter_extruder.value - 1)));
|
||||
// Apply the elephant foot compensation by steps of 1/10 nozzle diameter.
|
||||
float steps = std::ceil(elephant_foot_compensation / (0.1f * external_perimeter_nozzle));
|
||||
size_t nsteps = size_t(steps);
|
||||
float step = elephant_foot_compensation / steps;
|
||||
for (size_t i = 0; i < nsteps; ++ i) {
|
||||
Polygons tmp = offset(expolygons, - step);
|
||||
append(tmp, diff(to_polygons(expolygons), offset(offset_ex(expolygons, -elephant_foot_spacing - step), elephant_foot_spacing + step)));
|
||||
expolygons = union_ex(tmp);
|
||||
}
|
||||
}
|
||||
if (elephant_foot_compensation > 0)
|
||||
expolygons = union_ex(Slic3r::elephant_foot_compensation(expolygons, layerm->flow(frExternalPerimeter), unscale<double>(elephant_foot_compensation)));
|
||||
layerm->slices.set(std::move(expolygons), stInternal);
|
||||
}
|
||||
} else {
|
||||
|
@ -1825,33 +1816,17 @@ end:
|
|||
layerm->slices.set(std::move(slices), stInternal);
|
||||
}
|
||||
}
|
||||
if (delta < 0.f) {
|
||||
if (delta < 0.f || elephant_foot_compensation > 0.f) {
|
||||
// Apply the negative XY compensation.
|
||||
Polygons trimming = offset(layer->merged(float(EPSILON)), delta - float(EPSILON));
|
||||
Polygons trimming;
|
||||
if (elephant_foot_compensation > 0.f) {
|
||||
trimming = to_polygons(Slic3r::elephant_foot_compensation(offset_ex(layer->merged(float(EPSILON)), std::min(delta, 0.f) - float(EPSILON)),
|
||||
layer->m_regions.front()->flow(frExternalPerimeter), unscale<double>(elephant_foot_compensation)));
|
||||
} else
|
||||
trimming = offset(layer->merged(float(EPSILON)), delta - float(EPSILON));
|
||||
for (size_t region_id = 0; region_id < layer->m_regions.size(); ++ region_id)
|
||||
layer->m_regions[region_id]->trim_surfaces(trimming);
|
||||
}
|
||||
if (elephant_foot_compensation > 0.f) {
|
||||
// Apply the elephant foot compensation.
|
||||
std::vector<float> elephant_foot_spacing;
|
||||
elephant_foot_spacing.reserve(layer->m_regions.size());
|
||||
float external_perimeter_nozzle = 0.f;
|
||||
for (size_t region_id = 0; region_id < layer->m_regions.size(); ++ region_id) {
|
||||
LayerRegion *layerm = layer->m_regions[region_id];
|
||||
elephant_foot_spacing.emplace_back(float(layerm->flow(frExternalPerimeter).scaled_elephant_foot_spacing()));
|
||||
external_perimeter_nozzle += float(scale_(this->print()->config().nozzle_diameter.get_at(layerm->region()->config().perimeter_extruder.value - 1)));
|
||||
}
|
||||
external_perimeter_nozzle /= (float)layer->m_regions.size();
|
||||
// Apply the elephant foot compensation by steps of 1/10 nozzle diameter.
|
||||
float steps = std::ceil(elephant_foot_compensation / (0.1f * external_perimeter_nozzle));
|
||||
size_t nsteps = size_t(steps);
|
||||
float step = elephant_foot_compensation / steps;
|
||||
for (size_t i = 0; i < nsteps; ++ i) {
|
||||
Polygons trimming_polygons = offset(layer->merged(float(EPSILON)), - step - float(EPSILON));
|
||||
for (size_t region_id = 0; region_id < layer->m_regions.size(); ++ region_id)
|
||||
layer->m_regions[region_id]->elephant_foot_compensation_step(elephant_foot_spacing[region_id] + step, trimming_polygons);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Merge all regions' slices to get islands, chain them by a shortest path.
|
||||
layer->make_slices();
|
||||
|
|
|
@ -368,6 +368,10 @@ void SVG::export_expolygons(const char *path, const std::vector<std::pair<Slic3r
|
|||
color_holes = color_contour;
|
||||
svg.draw_outline(exp_with_attr.first, color_contour, color_holes, exp_with_attr.second.outline_width);
|
||||
}
|
||||
for (const auto &exp_with_attr : expolygons_with_attributes)
|
||||
if (exp_with_attr.second.radius_points > 0)
|
||||
for (const ExPolygon &expoly : exp_with_attr.first)
|
||||
svg.draw((Points)expoly, exp_with_attr.second.color_points, exp_with_attr.second.radius_points);
|
||||
svg.Close();
|
||||
}
|
||||
|
||||
|
|
|
@ -105,19 +105,25 @@ public:
|
|||
const std::string &color_contour,
|
||||
const std::string &color_holes,
|
||||
const coord_t outline_width = scale_(0.05),
|
||||
const float fill_opacity = 0.5f) :
|
||||
const float fill_opacity = 0.5f,
|
||||
const std::string &color_points = "black",
|
||||
const coord_t radius_points = 0) :
|
||||
color_fill (color_fill),
|
||||
color_contour (color_contour),
|
||||
color_holes (color_holes),
|
||||
outline_width (outline_width),
|
||||
fill_opacity (fill_opacity)
|
||||
fill_opacity (fill_opacity),
|
||||
color_points (color_points),
|
||||
radius_points (radius_points)
|
||||
{}
|
||||
|
||||
std::string color_fill;
|
||||
std::string color_contour;
|
||||
std::string color_holes;
|
||||
std::string color_points;
|
||||
coord_t outline_width;
|
||||
float fill_opacity;
|
||||
coord_t radius_points;
|
||||
};
|
||||
|
||||
static void export_expolygons(const char *path, const std::vector<std::pair<Slic3r::ExPolygons, ExPolygonAttributes>> &expolygons_with_attributes);
|
||||
|
|
|
@ -424,14 +424,19 @@ int copy_file(const std::string &from, const std::string &to)
|
|||
static const auto perms = boost::filesystem::owner_read | boost::filesystem::owner_write | boost::filesystem::group_read | boost::filesystem::others_read; // aka 644
|
||||
|
||||
// Make sure the file has correct permission both before and after we copy over it.
|
||||
try {
|
||||
if (boost::filesystem::exists(target))
|
||||
boost::filesystem::permissions(target, perms);
|
||||
boost::filesystem::copy_file(source, target, boost::filesystem::copy_option::overwrite_if_exists);
|
||||
boost::filesystem::permissions(target, perms);
|
||||
} catch (std::exception & /* ex */) {
|
||||
// NOTE: error_code variants are used here to supress expception throwing.
|
||||
// Error code of permission() calls is ignored on purpose - if they fail,
|
||||
// the copy_file() function will fail appropriately and we don't want the permission()
|
||||
// calls to cause needless failures on permissionless filesystems (ie. FATs on SD cards etc.)
|
||||
// or when the target file doesn't exist.
|
||||
boost::system::error_code ec;
|
||||
boost::filesystem::permissions(target, perms, ec);
|
||||
boost::filesystem::copy_file(source, target, boost::filesystem::copy_option::overwrite_if_exists, ec);
|
||||
if (ec) {
|
||||
return -1;
|
||||
}
|
||||
boost::filesystem::permissions(target, perms, ec);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -18,11 +18,13 @@ if(Qhull_FOUND)
|
|||
|
||||
message(STATUS "Using qhull from system.")
|
||||
if(SLIC3R_STATIC)
|
||||
slic3r_remap_configs("Qhull::qhullcpp;Qhull::qhullstatic_r" RelWithDebInfo Release)
|
||||
target_link_libraries(qhull INTERFACE Qhull::qhullcpp Qhull::qhullstatic_r)
|
||||
else()
|
||||
slic3r_remap_configs("Qhull::qhullcpp;Qhull::qhull_r" RelWithDebInfo Release)
|
||||
target_link_libraries(qhull INTERFACE Qhull::qhullcpp Qhull::qhull_r)
|
||||
endif()
|
||||
|
||||
|
||||
else(Qhull_FOUND)
|
||||
|
||||
project(qhull)
|
||||
|
|
|
@ -235,9 +235,9 @@ size_t Index::load(const boost::filesystem::path &path)
|
|||
value = left_trim(value + 1);
|
||||
*key_end = 0;
|
||||
boost::optional<Semver> semver;
|
||||
if (maybe_semver)
|
||||
if (maybe_semver)
|
||||
semver = Semver::parse(key);
|
||||
if (key_value_pair) {
|
||||
if (key_value_pair) {
|
||||
if (semver)
|
||||
throw file_parser_error("Key cannot be a semantic version", path, idx_line);\
|
||||
// Verify validity of the key / value pair.
|
||||
|
@ -288,7 +288,6 @@ Index::const_iterator Index::find(const Semver &ver) const
|
|||
|
||||
Index::const_iterator Index::recommended() const
|
||||
{
|
||||
int idx = -1;
|
||||
const_iterator highest = this->end();
|
||||
for (const_iterator it = this->begin(); it != this->end(); ++ it)
|
||||
if (it->is_current_slic3r_supported() &&
|
||||
|
|
|
@ -55,6 +55,7 @@ bool BackgroundSlicingProcess::select_technology(PrinterTechnology tech)
|
|||
switch (tech) {
|
||||
case ptFFF: m_print = m_fff_print; break;
|
||||
case ptSLA: m_print = m_sla_print; break;
|
||||
default: assert(false); break;
|
||||
}
|
||||
changed = true;
|
||||
}
|
||||
|
|
|
@ -150,7 +150,13 @@ void Field::get_value_by_opt_type(wxString& str, const bool check_value/* = true
|
|||
case coFloat:{
|
||||
if (m_opt.type == coPercent && !str.IsEmpty() && str.Last() == '%')
|
||||
str.RemoveLast();
|
||||
else if (check_value && !str.IsEmpty() && str.Last() == '%') {
|
||||
else if (!str.IsEmpty() && str.Last() == '%')
|
||||
{
|
||||
if (!check_value) {
|
||||
m_value.clear();
|
||||
break;
|
||||
}
|
||||
|
||||
wxString label = m_Label->GetLabel();
|
||||
if (label.Last() == '\n') label.RemoveLast();
|
||||
while (label.Last() == ' ') label.RemoveLast();
|
||||
|
@ -169,13 +175,21 @@ void Field::get_value_by_opt_type(wxString& str, const bool check_value/* = true
|
|||
{
|
||||
if (m_opt.nullable && str == na_value())
|
||||
val = ConfigOptionFloatsNullable::nil_value();
|
||||
else if (check_value && !str.ToCDouble(&val))
|
||||
else if (!str.ToCDouble(&val))
|
||||
{
|
||||
if (!check_value) {
|
||||
m_value.clear();
|
||||
break;
|
||||
}
|
||||
show_error(m_parent, _(L("Invalid numeric input.")));
|
||||
set_value(double_to_string(val), true);
|
||||
}
|
||||
if (check_value && (m_opt.min > val || val > m_opt.max))
|
||||
if (m_opt.min > val || val > m_opt.max)
|
||||
{
|
||||
if (!check_value) {
|
||||
m_value.clear();
|
||||
break;
|
||||
}
|
||||
show_error(m_parent, _(L("Input value is out of range")));
|
||||
if (m_opt.min > val) val = m_opt.min;
|
||||
if (val > m_opt.max) val = m_opt.max;
|
||||
|
@ -192,15 +206,24 @@ void Field::get_value_by_opt_type(wxString& str, const bool check_value/* = true
|
|||
double val = 0.;
|
||||
// Replace the first occurence of comma in decimal number.
|
||||
str.Replace(",", ".", false);
|
||||
if (check_value && !str.ToCDouble(&val))
|
||||
if (!str.ToCDouble(&val))
|
||||
{
|
||||
if (!check_value) {
|
||||
m_value.clear();
|
||||
break;
|
||||
}
|
||||
show_error(m_parent, _(L("Invalid numeric input.")));
|
||||
set_value(double_to_string(val), true);
|
||||
}
|
||||
else if (check_value && ((m_opt.sidetext.rfind("mm/s") != std::string::npos && val > m_opt.max) ||
|
||||
else if (((m_opt.sidetext.rfind("mm/s") != std::string::npos && val > m_opt.max) ||
|
||||
(m_opt.sidetext.rfind("mm ") != std::string::npos && val > 1)) &&
|
||||
(m_value.empty() || std::string(str.ToUTF8().data()) != boost::any_cast<std::string>(m_value)))
|
||||
{
|
||||
if (!check_value) {
|
||||
m_value.clear();
|
||||
break;
|
||||
}
|
||||
|
||||
const std::string sidetext = m_opt.sidetext.rfind("mm/s") != std::string::npos ? "mm/s" : "mm";
|
||||
const wxString stVal = double_to_string(val, 2);
|
||||
const wxString msg_text = wxString::Format(_(L("Do you mean %s%% instead of %s %s?\n"
|
||||
|
@ -351,6 +374,7 @@ bool TextCtrl::value_was_changed()
|
|||
boost::any val = m_value;
|
||||
wxString ret_str = static_cast<wxTextCtrl*>(window)->GetValue();
|
||||
// update m_value!
|
||||
// ret_str might be changed inside get_value_by_opt_type
|
||||
get_value_by_opt_type(ret_str);
|
||||
|
||||
switch (m_opt.type) {
|
||||
|
@ -396,8 +420,10 @@ void TextCtrl::set_value(const boost::any& value, bool change_event/* = false*/)
|
|||
|
||||
if (!change_event) {
|
||||
wxString ret_str = static_cast<wxTextCtrl*>(window)->GetValue();
|
||||
// update m_value to correct work of next value_was_changed(),
|
||||
// but don't check/change inputed value and don't show a warning message
|
||||
/* Update m_value to correct work of next value_was_changed().
|
||||
* But after checking of entered value, don't fix the "incorrect" value and don't show a warning message,
|
||||
* just clear m_value in this case.
|
||||
*/
|
||||
get_value_by_opt_type(ret_str, false);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1949,7 +1949,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
|
|||
if (it->new_geometry()) {
|
||||
// New volume.
|
||||
unsigned int old_id = find_old_volume_id(it->composite_id);
|
||||
if (old_id != -1)
|
||||
if (old_id != (unsigned int)-1)
|
||||
map_glvolume_old_to_new[old_id] = m_volumes.volumes.size();
|
||||
m_volumes.load_object_volume(&model_object, obj_idx, volume_idx, instance_idx, m_color_by, m_initialized);
|
||||
m_volumes.volumes.back()->geometry_id = key.geometry_id;
|
||||
|
|
|
@ -87,7 +87,7 @@ class GUI_App : public wxApp
|
|||
wxFont m_bold_font;
|
||||
wxFont m_normal_font;
|
||||
|
||||
size_t m_em_unit; // width of a "m"-symbol in pixels for current system font
|
||||
int m_em_unit; // width of a "m"-symbol in pixels for current system font
|
||||
// Note: for 100% Scale m_em_unit = 10 -> it's a good enough coefficient for a size setting of controls
|
||||
|
||||
std::unique_ptr<wxLocale> m_wxLocale;
|
||||
|
@ -105,7 +105,7 @@ public:
|
|||
bool initialized() const { return m_initialized; }
|
||||
|
||||
GUI_App();
|
||||
~GUI_App();
|
||||
~GUI_App() override;
|
||||
|
||||
static unsigned get_colour_approx_luma(const wxColour &colour);
|
||||
static bool dark_mode();
|
||||
|
@ -124,8 +124,7 @@ public:
|
|||
const wxFont& small_font() { return m_small_font; }
|
||||
const wxFont& bold_font() { return m_bold_font; }
|
||||
const wxFont& normal_font() { return m_normal_font; }
|
||||
size_t em_unit() const { return m_em_unit; }
|
||||
void set_em_unit(const size_t em_unit) { m_em_unit = em_unit; }
|
||||
int em_unit() const { return m_em_unit; }
|
||||
float toolbar_icon_scale(const bool is_limited = false) const;
|
||||
|
||||
void recreate_GUI();
|
||||
|
@ -155,7 +154,7 @@ public:
|
|||
// Translate the language code to a code, for which Prusa Research maintains translations. Defaults to "en_US".
|
||||
wxString current_language_code_safe() const;
|
||||
|
||||
virtual bool OnExceptionInMainLoop();
|
||||
virtual bool OnExceptionInMainLoop() override;
|
||||
|
||||
#ifdef __APPLE__
|
||||
// wxWidgets override to get an event on open files.
|
||||
|
|
|
@ -445,7 +445,7 @@ void ObjectList::update_extruder_values_for_items(const size_t max_extruder)
|
|||
auto object = (*m_objects)[i];
|
||||
wxString extruder;
|
||||
if (!object->config.has("extruder") ||
|
||||
object->config.option<ConfigOptionInt>("extruder")->value > max_extruder)
|
||||
size_t(object->config.option<ConfigOptionInt>("extruder")->value) > max_extruder)
|
||||
extruder = _(L("default"));
|
||||
else
|
||||
extruder = wxString::Format("%d", object->config.option<ConfigOptionInt>("extruder")->value);
|
||||
|
@ -457,7 +457,7 @@ void ObjectList::update_extruder_values_for_items(const size_t max_extruder)
|
|||
item = m_objects_model->GetItemByVolumeId(i, id);
|
||||
if (!item) continue;
|
||||
if (!object->volumes[id]->config.has("extruder") ||
|
||||
object->volumes[id]->config.option<ConfigOptionInt>("extruder")->value > max_extruder)
|
||||
size_t(object->volumes[id]->config.option<ConfigOptionInt>("extruder")->value) > max_extruder)
|
||||
extruder = _(L("default"));
|
||||
else
|
||||
extruder = wxString::Format("%d", object->volumes[id]->config.option<ConfigOptionInt>("extruder")->value);
|
||||
|
|
|
@ -634,7 +634,11 @@ void ObjectManipulation::update_reset_buttons_visibility()
|
|||
show_drop_to_bed = (std::abs(min_z) > EPSILON);
|
||||
}
|
||||
|
||||
wxGetApp().CallAfter([this, show_rotation, show_scale, show_drop_to_bed]{
|
||||
wxGetApp().CallAfter([this, show_rotation, show_scale, show_drop_to_bed] {
|
||||
// There is a case (under OSX), when this function is called after the Manipulation panel is hidden
|
||||
// So, let check if Manipulation panel is still shown for this moment
|
||||
if (!this->IsShown())
|
||||
return;
|
||||
m_reset_rotation_button->Show(show_rotation);
|
||||
m_reset_scale_button->Show(show_scale);
|
||||
m_drop_to_bed_button->Show(show_drop_to_bed);
|
||||
|
|
|
@ -375,6 +375,8 @@ void Preview::load_print(bool keep_z_range)
|
|||
load_print_as_fff(keep_z_range);
|
||||
else if (tech == ptSLA)
|
||||
load_print_as_sla();
|
||||
|
||||
Layout();
|
||||
}
|
||||
|
||||
void Preview::reload_print(bool keep_volumes)
|
||||
|
|
|
@ -114,8 +114,17 @@ public:
|
|||
|
||||
m_serializing = true;
|
||||
|
||||
// Following is needed to know which to be turn on, but not actually modify
|
||||
// m_current prematurely, so activate_gizmo is not confused.
|
||||
EType old_current = m_current;
|
||||
ar(m_current);
|
||||
EType new_current = m_current;
|
||||
m_current = old_current;
|
||||
|
||||
// activate_gizmo call sets m_current and calls set_state for the gizmo
|
||||
// it does nothing in case the gizmo is already activated
|
||||
// it can safely be called for Undefined gizmo
|
||||
activate_gizmo(new_current);
|
||||
if (m_current != Undefined)
|
||||
m_gizmos[m_current]->load(ar);
|
||||
}
|
||||
|
|
|
@ -233,7 +233,7 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** full_Label/* = n
|
|||
|
||||
add_undo_buttuns_to_sizer(sizer, field);
|
||||
if (is_window_field(field))
|
||||
sizer->Add(field->getWindow(), option.opt.full_width ? 1 : 0, //(option.opt.full_width ? wxEXPAND : 0) |
|
||||
sizer->Add(field->getWindow(), option.opt.full_width ? 1 : 0, //(option.opt.full_width ? wxEXPAND : 0) |
|
||||
wxBOTTOM | wxTOP | (option.opt.full_width ? wxEXPAND : wxALIGN_CENTER_VERTICAL), (wxOSX || !staticbox) ? 0 : 2);
|
||||
if (is_sizer_field(field))
|
||||
sizer->Add(field->getSizer(), 1, /*(*/option.opt.full_width ? wxEXPAND : /*0) |*/ wxALIGN_CENTER_VERTICAL, 0);
|
||||
|
|
|
@ -4794,7 +4794,7 @@ bool Plater::undo_redo_string_getter(const bool is_undo, int idx, const char** o
|
|||
const std::vector<UndoRedo::Snapshot>& ss_stack = p->undo_redo_stack().snapshots();
|
||||
const int idx_in_ss_stack = p->get_active_snapshot_index() + (is_undo ? -(++idx) : idx);
|
||||
|
||||
if (0 < idx_in_ss_stack && idx_in_ss_stack < ss_stack.size() - 1) {
|
||||
if (0 < idx_in_ss_stack && (size_t)idx_in_ss_stack < ss_stack.size() - 1) {
|
||||
*out_text = ss_stack[idx_in_ss_stack].name.c_str();
|
||||
return true;
|
||||
}
|
||||
|
@ -4807,7 +4807,7 @@ void Plater::undo_redo_topmost_string_getter(const bool is_undo, std::string& ou
|
|||
const std::vector<UndoRedo::Snapshot>& ss_stack = p->undo_redo_stack().snapshots();
|
||||
const int idx_in_ss_stack = p->get_active_snapshot_index() + (is_undo ? -1 : 0);
|
||||
|
||||
if (0 < idx_in_ss_stack && idx_in_ss_stack < ss_stack.size() - 1) {
|
||||
if (0 < idx_in_ss_stack && (size_t)idx_in_ss_stack < ss_stack.size() - 1) {
|
||||
out_text = ss_stack[idx_in_ss_stack].name;
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -1061,7 +1061,7 @@ wxDataViewItem ObjectDataViewModel::Delete(const wxDataViewItem &item)
|
|||
node_parent->GetChildren().Remove(node);
|
||||
|
||||
if (id > 0) {
|
||||
if(id == node_parent->GetChildCount()) id--;
|
||||
if (size_t(id) == node_parent->GetChildCount()) id--;
|
||||
ret_item = wxDataViewItem(node_parent->GetChildren().Item(id));
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue