mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-13 09:47:58 -06:00
Fixed conflicts after merge with master
This commit is contained in:
commit
f0aa4de20a
39 changed files with 467 additions and 402 deletions
|
@ -479,13 +479,18 @@ class _NofitPolyPlacer: public PlacerBoilerplate<_NofitPolyPlacer<RawShape, TBin
|
||||||
|
|
||||||
using MaxNfpLevel = nfp::MaxNfpLevel<RawShape>;
|
using MaxNfpLevel = nfp::MaxNfpLevel<RawShape>;
|
||||||
|
|
||||||
// Norming factor for the optimization function
|
|
||||||
const double norm_;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
using Pile = nfp::Shapes<RawShape>;
|
using Pile = nfp::Shapes<RawShape>;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
// Norming factor for the optimization function
|
||||||
|
const double norm_;
|
||||||
|
Pile merged_pile_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
inline explicit _NofitPolyPlacer(const BinType& bin):
|
inline explicit _NofitPolyPlacer(const BinType& bin):
|
||||||
Base(bin),
|
Base(bin),
|
||||||
norm_(std::sqrt(sl::area(bin)))
|
norm_(std::sqrt(sl::area(bin)))
|
||||||
|
@ -616,135 +621,9 @@ private:
|
||||||
template<class Level>
|
template<class Level>
|
||||||
Shapes calcnfp(const Item &trsh, Level)
|
Shapes calcnfp(const Item &trsh, Level)
|
||||||
{ // Function for arbitrary level of nfp implementation
|
{ // Function for arbitrary level of nfp implementation
|
||||||
using namespace nfp;
|
|
||||||
|
|
||||||
Shapes nfps;
|
// TODO: implement
|
||||||
|
return {};
|
||||||
auto& orb = trsh.transformedShape();
|
|
||||||
bool orbconvex = trsh.isContourConvex();
|
|
||||||
|
|
||||||
for(Item& sh : items_) {
|
|
||||||
nfp::NfpResult<RawShape> subnfp;
|
|
||||||
auto& stat = sh.transformedShape();
|
|
||||||
|
|
||||||
if(sh.isContourConvex() && orbconvex)
|
|
||||||
subnfp = nfp::noFitPolygon<NfpLevel::CONVEX_ONLY>(stat, orb);
|
|
||||||
else if(orbconvex)
|
|
||||||
subnfp = nfp::noFitPolygon<NfpLevel::ONE_CONVEX>(stat, orb);
|
|
||||||
else
|
|
||||||
subnfp = nfp::noFitPolygon<Level::value>(stat, orb);
|
|
||||||
|
|
||||||
correctNfpPosition(subnfp, sh, trsh);
|
|
||||||
|
|
||||||
nfps = nfp::merge(nfps, subnfp.first);
|
|
||||||
}
|
|
||||||
|
|
||||||
return nfps;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Very much experimental
|
|
||||||
void repack(Item& item, PackResult& result) {
|
|
||||||
|
|
||||||
if((sl::area(bin_) - this->filledArea()) >= item.area()) {
|
|
||||||
auto prev_func = config_.object_function;
|
|
||||||
|
|
||||||
unsigned iter = 0;
|
|
||||||
ItemGroup backup_rf = items_;
|
|
||||||
std::vector<Item> backup_cpy;
|
|
||||||
for(Item& itm : items_) backup_cpy.emplace_back(itm);
|
|
||||||
|
|
||||||
auto ofn = [this, &item, &result, &iter, &backup_cpy, &backup_rf]
|
|
||||||
(double ratio)
|
|
||||||
{
|
|
||||||
auto& bin = bin_;
|
|
||||||
iter++;
|
|
||||||
config_.object_function = [bin, ratio](
|
|
||||||
nfp::Shapes<RawShape>& pile,
|
|
||||||
const Item& item,
|
|
||||||
const ItemGroup& /*remaining*/)
|
|
||||||
{
|
|
||||||
pile.emplace_back(item.transformedShape());
|
|
||||||
auto ch = sl::convexHull(pile);
|
|
||||||
auto pbb = sl::boundingBox(pile);
|
|
||||||
pile.pop_back();
|
|
||||||
|
|
||||||
double parea = 0.5*(sl::area(ch) + sl::area(pbb));
|
|
||||||
|
|
||||||
double pile_area = std::accumulate(
|
|
||||||
pile.begin(), pile.end(), item.area(),
|
|
||||||
[](double sum, const RawShape& sh){
|
|
||||||
return sum + sl::area(sh);
|
|
||||||
});
|
|
||||||
|
|
||||||
// The pack ratio -- how much is the convex hull occupied
|
|
||||||
double pack_rate = (pile_area)/parea;
|
|
||||||
|
|
||||||
// ratio of waste
|
|
||||||
double waste = 1.0 - pack_rate;
|
|
||||||
|
|
||||||
// Score is the square root of waste. This will extend the
|
|
||||||
// range of good (lower) values and shrink the range of bad
|
|
||||||
// (larger) values.
|
|
||||||
auto wscore = std::sqrt(waste);
|
|
||||||
|
|
||||||
|
|
||||||
auto ibb = item.boundingBox();
|
|
||||||
auto bbb = sl::boundingBox(bin);
|
|
||||||
auto c = ibb.center();
|
|
||||||
double norm = 0.5*pl::distance(bbb.minCorner(),
|
|
||||||
bbb.maxCorner());
|
|
||||||
|
|
||||||
double dscore = pl::distance(c, pbb.center()) / norm;
|
|
||||||
|
|
||||||
return ratio*wscore + (1.0 - ratio) * dscore;
|
|
||||||
};
|
|
||||||
|
|
||||||
auto bb = sl::boundingBox(bin);
|
|
||||||
double norm = bb.width() + bb.height();
|
|
||||||
|
|
||||||
auto items = items_;
|
|
||||||
clearItems();
|
|
||||||
auto it = items.begin();
|
|
||||||
while(auto pr = _trypack(*it++)) {
|
|
||||||
this->accept(pr); if(it == items.end()) break;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto count_diff = items.size() - items_.size();
|
|
||||||
double score = count_diff;
|
|
||||||
|
|
||||||
if(count_diff == 0) {
|
|
||||||
result = _trypack(item);
|
|
||||||
|
|
||||||
if(result) {
|
|
||||||
std::cout << "Success" << std::endl;
|
|
||||||
score = 0.0;
|
|
||||||
} else {
|
|
||||||
score += result.overfit() / norm;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
result = PackResult();
|
|
||||||
items_ = backup_rf;
|
|
||||||
for(unsigned i = 0; i < items_.size(); i++) {
|
|
||||||
items_[i].get() = backup_cpy[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << iter << " repack result: " << score << " "
|
|
||||||
<< ratio << " " << count_diff << std::endl;
|
|
||||||
|
|
||||||
return score;
|
|
||||||
};
|
|
||||||
|
|
||||||
opt::StopCriteria stopcr;
|
|
||||||
stopcr.max_iterations = 30;
|
|
||||||
stopcr.stop_score = 1e-20;
|
|
||||||
opt::TOptimizer<opt::Method::L_SUBPLEX> solver(stopcr);
|
|
||||||
solver.optimize_min(ofn, opt::initvals(0.5),
|
|
||||||
opt::bound(0.0, 1.0));
|
|
||||||
|
|
||||||
// optimize
|
|
||||||
config_.object_function = prev_func;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Optimum {
|
struct Optimum {
|
||||||
|
@ -798,42 +677,9 @@ private:
|
||||||
Radians final_rot = initial_rot;
|
Radians final_rot = initial_rot;
|
||||||
Shapes nfps;
|
Shapes nfps;
|
||||||
|
|
||||||
for(auto rot : config_.rotations) {
|
|
||||||
|
|
||||||
item.translation(initial_tr);
|
|
||||||
item.rotation(initial_rot + rot);
|
|
||||||
item.boundingBox(); // fill the bb cache
|
|
||||||
|
|
||||||
// place the new item outside of the print bed to make sure
|
|
||||||
// it is disjunct from the current merged pile
|
|
||||||
placeOutsideOfBin(item);
|
|
||||||
|
|
||||||
nfps = calcnfp(item, Lvl<MaxNfpLevel::value>());
|
|
||||||
|
|
||||||
auto iv = item.referenceVertex();
|
|
||||||
|
|
||||||
auto startpos = item.translation();
|
|
||||||
|
|
||||||
std::vector<Edges> ecache;
|
|
||||||
ecache.reserve(nfps.size());
|
|
||||||
|
|
||||||
for(auto& nfp : nfps ) {
|
|
||||||
ecache.emplace_back(nfp);
|
|
||||||
ecache.back().accuracy(config_.accuracy);
|
|
||||||
}
|
|
||||||
|
|
||||||
Shapes pile;
|
|
||||||
pile.reserve(items_.size()+1);
|
|
||||||
// double pile_area = 0;
|
|
||||||
for(Item& mitem : items_) {
|
|
||||||
pile.emplace_back(mitem.transformedShape());
|
|
||||||
// pile_area += mitem.area();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto merged_pile = nfp::merge(pile);
|
|
||||||
auto& bin = bin_;
|
auto& bin = bin_;
|
||||||
double norm = norm_;
|
double norm = norm_;
|
||||||
auto pbb = sl::boundingBox(merged_pile);
|
auto pbb = sl::boundingBox(merged_pile_);
|
||||||
auto binbb = sl::boundingBox(bin);
|
auto binbb = sl::boundingBox(bin);
|
||||||
|
|
||||||
// This is the kernel part of the object function that is
|
// This is the kernel part of the object function that is
|
||||||
|
@ -873,6 +719,32 @@ private:
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Pile merged_pile = merged_pile_;
|
||||||
|
|
||||||
|
for(auto rot : config_.rotations) {
|
||||||
|
|
||||||
|
item.translation(initial_tr);
|
||||||
|
item.rotation(initial_rot + rot);
|
||||||
|
item.boundingBox(); // fill the bb cache
|
||||||
|
|
||||||
|
// place the new item outside of the print bed to make sure
|
||||||
|
// it is disjunct from the current merged pile
|
||||||
|
placeOutsideOfBin(item);
|
||||||
|
|
||||||
|
nfps = calcnfp(item, Lvl<MaxNfpLevel::value>());
|
||||||
|
|
||||||
|
auto iv = item.referenceVertex();
|
||||||
|
|
||||||
|
auto startpos = item.translation();
|
||||||
|
|
||||||
|
std::vector<Edges> ecache;
|
||||||
|
ecache.reserve(nfps.size());
|
||||||
|
|
||||||
|
for(auto& nfp : nfps ) {
|
||||||
|
ecache.emplace_back(nfp);
|
||||||
|
ecache.back().accuracy(config_.accuracy);
|
||||||
|
}
|
||||||
|
|
||||||
// Our object function for placement
|
// Our object function for placement
|
||||||
auto rawobjfunc = [_objfunc, iv, startpos]
|
auto rawobjfunc = [_objfunc, iv, startpos]
|
||||||
(Vertex v, Item& itm)
|
(Vertex v, Item& itm)
|
||||||
|
@ -1045,6 +917,7 @@ private:
|
||||||
|
|
||||||
if(can_pack) {
|
if(can_pack) {
|
||||||
ret = PackResult(item);
|
ret = PackResult(item);
|
||||||
|
merged_pile_ = nfp::merge(merged_pile_, item.transformedShape());
|
||||||
} else {
|
} else {
|
||||||
ret = PackResult(best_overfit);
|
ret = PackResult(best_overfit);
|
||||||
}
|
}
|
||||||
|
|
|
@ -309,7 +309,7 @@ protected:
|
||||||
public:
|
public:
|
||||||
AutoArranger(const TBin & bin,
|
AutoArranger(const TBin & bin,
|
||||||
const ArrangeParams ¶ms,
|
const ArrangeParams ¶ms,
|
||||||
std::function<void(unsigned)> progressind,
|
std::function<void(unsigned, unsigned /*bins*/)> progressind,
|
||||||
std::function<bool(void)> stopcond)
|
std::function<bool(void)> stopcond)
|
||||||
: m_pck(bin, params.min_obj_distance)
|
: m_pck(bin, params.min_obj_distance)
|
||||||
, m_bin(bin)
|
, m_bin(bin)
|
||||||
|
@ -348,7 +348,9 @@ public:
|
||||||
|
|
||||||
m_pconf.object_function = get_objfn();
|
m_pconf.object_function = get_objfn();
|
||||||
|
|
||||||
if (progressind) m_pck.progressIndicator(progressind);
|
if (progressind) m_pck.progressIndicator([this, &progressind](unsigned rem) {
|
||||||
|
progressind(rem, m_pck.lastResult().size() - 1);
|
||||||
|
});
|
||||||
if (stopcond) m_pck.stopCondition(stopcond);
|
if (stopcond) m_pck.stopCondition(stopcond);
|
||||||
|
|
||||||
m_pck.configure(m_pconf);
|
m_pck.configure(m_pconf);
|
||||||
|
@ -462,7 +464,7 @@ void _arrange(
|
||||||
std::vector<Item> & excludes,
|
std::vector<Item> & excludes,
|
||||||
const BinT & bin,
|
const BinT & bin,
|
||||||
const ArrangeParams ¶ms,
|
const ArrangeParams ¶ms,
|
||||||
std::function<void(unsigned)> progressfn,
|
std::function<void(unsigned, unsigned)> progressfn,
|
||||||
std::function<bool()> stopfn)
|
std::function<bool()> stopfn)
|
||||||
{
|
{
|
||||||
// Integer ceiling the min distance from the bed perimeters
|
// Integer ceiling the min distance from the bed perimeters
|
||||||
|
|
|
@ -74,7 +74,7 @@ struct ArrangeParams {
|
||||||
|
|
||||||
/// The accuracy of optimization.
|
/// The accuracy of optimization.
|
||||||
/// Goes from 0.0 to 1.0 and scales performance as well
|
/// Goes from 0.0 to 1.0 and scales performance as well
|
||||||
float accuracy = 0.65f;
|
float accuracy = 1.f;
|
||||||
|
|
||||||
/// Allow parallel execution.
|
/// Allow parallel execution.
|
||||||
bool parallel = true;
|
bool parallel = true;
|
||||||
|
@ -83,7 +83,8 @@ struct ArrangeParams {
|
||||||
|
|
||||||
/// Progress indicator callback called when an object gets packed.
|
/// Progress indicator callback called when an object gets packed.
|
||||||
/// The unsigned argument is the number of items remaining to pack.
|
/// The unsigned argument is the number of items remaining to pack.
|
||||||
std::function<void(unsigned)> progressind;
|
/// Second is the current bed idx being filled.
|
||||||
|
std::function<void(unsigned, unsigned /*bed_idx*/)> progressind;
|
||||||
|
|
||||||
/// A predicate returning true if abort is needed.
|
/// A predicate returning true if abort is needed.
|
||||||
std::function<bool(void)> stopcondition;
|
std::function<bool(void)> stopcondition;
|
||||||
|
|
|
@ -1069,7 +1069,7 @@ Polygons variable_offset_inner(const ExPolygon &expoly, const std::vector<std::v
|
||||||
ClipperLib::Paths holes;
|
ClipperLib::Paths holes;
|
||||||
holes.reserve(expoly.holes.size());
|
holes.reserve(expoly.holes.size());
|
||||||
for (const Polygon& hole : expoly.holes)
|
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, false));
|
append(holes, fix_after_outer_offset(mittered_offset_path_scaled(hole.points, deltas[1 + &hole - expoly.holes.data()], miter_limit), ClipperLib::pftNegative, false));
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
for (auto &c : holes)
|
for (auto &c : holes)
|
||||||
assert(ClipperLib::Area(c) > 0.);
|
assert(ClipperLib::Area(c) > 0.);
|
||||||
|
@ -1113,7 +1113,7 @@ for (const std::vector<float>& ds : deltas)
|
||||||
ClipperLib::Paths holes;
|
ClipperLib::Paths holes;
|
||||||
holes.reserve(expoly.holes.size());
|
holes.reserve(expoly.holes.size());
|
||||||
for (const Polygon& hole : expoly.holes)
|
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));
|
append(holes, fix_after_inner_offset(mittered_offset_path_scaled(hole.points, deltas[1 + &hole - expoly.holes.data()], miter_limit), ClipperLib::pftPositive, true));
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
for (auto &c : holes)
|
for (auto &c : holes)
|
||||||
assert(ClipperLib::Area(c) > 0.);
|
assert(ClipperLib::Area(c) > 0.);
|
||||||
|
@ -1157,7 +1157,7 @@ for (const std::vector<float>& ds : deltas)
|
||||||
ClipperLib::Paths holes;
|
ClipperLib::Paths holes;
|
||||||
holes.reserve(expoly.holes.size());
|
holes.reserve(expoly.holes.size());
|
||||||
for (const Polygon& hole : expoly.holes)
|
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));
|
append(holes, fix_after_inner_offset(mittered_offset_path_scaled(hole.points, deltas[1 + &hole - expoly.holes.data()], miter_limit), ClipperLib::pftPositive, true));
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
for (auto &c : holes)
|
for (auto &c : holes)
|
||||||
assert(ClipperLib::Area(c) > 0.);
|
assert(ClipperLib::Area(c) > 0.);
|
||||||
|
@ -1205,7 +1205,7 @@ ExPolygons variable_offset_inner_ex(const ExPolygon &expoly, const std::vector<s
|
||||||
ClipperLib::Paths holes;
|
ClipperLib::Paths holes;
|
||||||
holes.reserve(expoly.holes.size());
|
holes.reserve(expoly.holes.size());
|
||||||
for (const Polygon& hole : expoly.holes)
|
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, false));
|
append(holes, fix_after_outer_offset(mittered_offset_path_scaled(hole.points, deltas[1 + &hole - expoly.holes.data()], miter_limit), ClipperLib::pftNegative, false));
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
for (auto &c : holes)
|
for (auto &c : holes)
|
||||||
assert(ClipperLib::Area(c) > 0.);
|
assert(ClipperLib::Area(c) > 0.);
|
||||||
|
|
|
@ -350,23 +350,10 @@ void ExPolygon::get_trapezoids2(Polygons* polygons) const
|
||||||
// find trapezoids by looping from first to next-to-last coordinate
|
// find trapezoids by looping from first to next-to-last coordinate
|
||||||
for (std::vector<coord_t>::const_iterator x = xx.begin(); x != xx.end()-1; ++x) {
|
for (std::vector<coord_t>::const_iterator x = xx.begin(); x != xx.end()-1; ++x) {
|
||||||
coord_t next_x = *(x + 1);
|
coord_t next_x = *(x + 1);
|
||||||
if (*x == next_x) continue;
|
if (*x != next_x)
|
||||||
|
// intersect with rectangle
|
||||||
// build rectangle
|
|
||||||
Polygon poly;
|
|
||||||
poly.points.resize(4);
|
|
||||||
poly[0](0) = *x;
|
|
||||||
poly[0](1) = bb.min(1);
|
|
||||||
poly[1](0) = next_x;
|
|
||||||
poly[1](1) = bb.min(1);
|
|
||||||
poly[2](0) = next_x;
|
|
||||||
poly[2](1) = bb.max(1);
|
|
||||||
poly[3](0) = *x;
|
|
||||||
poly[3](1) = bb.max(1);
|
|
||||||
|
|
||||||
// intersect with this expolygon
|
|
||||||
// append results to return value
|
// append results to return value
|
||||||
polygons_append(*polygons, intersection(poly, to_polygons(*this)));
|
polygons_append(*polygons, intersection({ { { *x, bb.min.y() }, { next_x, bb.min.y() }, { next_x, bb.max.y() }, { *x, bb.max.y() } } }, to_polygons(*this)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,12 +14,12 @@ namespace Slic3r {
|
||||||
|
|
||||||
void ExtrusionPath::intersect_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const
|
void ExtrusionPath::intersect_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const
|
||||||
{
|
{
|
||||||
this->_inflate_collection(intersection_pl(this->polyline, (Polygons)collection), retval);
|
this->_inflate_collection(intersection_pl((Polylines)polyline, to_polygons(collection.expolygons)), retval);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExtrusionPath::subtract_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const
|
void ExtrusionPath::subtract_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const
|
||||||
{
|
{
|
||||||
this->_inflate_collection(diff_pl(this->polyline, (Polygons)collection), retval);
|
this->_inflate_collection(diff_pl((Polylines)this->polyline, to_polygons(collection.expolygons)), retval);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExtrusionPath::clip_end(double distance)
|
void ExtrusionPath::clip_end(double distance)
|
||||||
|
|
|
@ -38,6 +38,7 @@ struct SurfaceFillParams
|
||||||
// Length of the infill anchor along the perimeter line.
|
// Length of the infill anchor along the perimeter line.
|
||||||
// 1000mm is roughly the maximum length line that fits into a 32bit coord_t.
|
// 1000mm is roughly the maximum length line that fits into a 32bit coord_t.
|
||||||
float anchor_length = 1000.f;
|
float anchor_length = 1000.f;
|
||||||
|
float anchor_length_max = 1000.f;
|
||||||
|
|
||||||
// width, height of extrusion, nozzle diameter, is bridge
|
// width, height of extrusion, nozzle diameter, is bridge
|
||||||
// For the output, for fill generator.
|
// For the output, for fill generator.
|
||||||
|
@ -68,6 +69,7 @@ struct SurfaceFillParams
|
||||||
RETURN_COMPARE_NON_EQUAL(density);
|
RETURN_COMPARE_NON_EQUAL(density);
|
||||||
RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, dont_adjust);
|
RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, dont_adjust);
|
||||||
RETURN_COMPARE_NON_EQUAL(anchor_length);
|
RETURN_COMPARE_NON_EQUAL(anchor_length);
|
||||||
|
RETURN_COMPARE_NON_EQUAL(anchor_length_max);
|
||||||
RETURN_COMPARE_NON_EQUAL(flow.width);
|
RETURN_COMPARE_NON_EQUAL(flow.width);
|
||||||
RETURN_COMPARE_NON_EQUAL(flow.height);
|
RETURN_COMPARE_NON_EQUAL(flow.height);
|
||||||
RETURN_COMPARE_NON_EQUAL(flow.nozzle_diameter);
|
RETURN_COMPARE_NON_EQUAL(flow.nozzle_diameter);
|
||||||
|
@ -86,6 +88,7 @@ struct SurfaceFillParams
|
||||||
this->density == rhs.density &&
|
this->density == rhs.density &&
|
||||||
this->dont_adjust == rhs.dont_adjust &&
|
this->dont_adjust == rhs.dont_adjust &&
|
||||||
this->anchor_length == rhs.anchor_length &&
|
this->anchor_length == rhs.anchor_length &&
|
||||||
|
this->anchor_length_max == rhs.anchor_length_max &&
|
||||||
this->flow == rhs.flow &&
|
this->flow == rhs.flow &&
|
||||||
this->extrusion_role == rhs.extrusion_role;
|
this->extrusion_role == rhs.extrusion_role;
|
||||||
}
|
}
|
||||||
|
@ -171,8 +174,12 @@ std::vector<SurfaceFill> group_fills(const Layer &layer)
|
||||||
// Anchor a sparse infill to inner perimeters with the following anchor length:
|
// Anchor a sparse infill to inner perimeters with the following anchor length:
|
||||||
params.anchor_length = float(region_config.infill_anchor);
|
params.anchor_length = float(region_config.infill_anchor);
|
||||||
if (region_config.infill_anchor.percent)
|
if (region_config.infill_anchor.percent)
|
||||||
params.anchor_length *= 0.01 * params.spacing;
|
params.anchor_length = float(params.anchor_length * 0.01 * params.spacing);
|
||||||
|
params.anchor_length_max = float(region_config.infill_anchor_max);
|
||||||
|
if (region_config.infill_anchor_max.percent)
|
||||||
|
params.anchor_length_max = float(params.anchor_length_max * 0.01 * params.spacing);
|
||||||
}
|
}
|
||||||
|
params.anchor_length = std::min(params.anchor_length, params.anchor_length_max);
|
||||||
|
|
||||||
auto it_params = set_surface_params.find(params);
|
auto it_params = set_surface_params.find(params);
|
||||||
if (it_params == set_surface_params.end())
|
if (it_params == set_surface_params.end())
|
||||||
|
@ -379,6 +386,7 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
|
||||||
params.density = float(0.01 * surface_fill.params.density);
|
params.density = float(0.01 * surface_fill.params.density);
|
||||||
params.dont_adjust = surface_fill.params.dont_adjust; // false
|
params.dont_adjust = surface_fill.params.dont_adjust; // false
|
||||||
params.anchor_length = surface_fill.params.anchor_length;
|
params.anchor_length = surface_fill.params.anchor_length;
|
||||||
|
params.anchor_length_max = surface_fill.params.anchor_length_max;
|
||||||
|
|
||||||
for (ExPolygon &expoly : surface_fill.expolygons) {
|
for (ExPolygon &expoly : surface_fill.expolygons) {
|
||||||
// Spacing is modified by the filler to indicate adjustments. Reset it for each expolygon.
|
// Spacing is modified by the filler to indicate adjustments. Reset it for each expolygon.
|
||||||
|
|
|
@ -667,9 +667,26 @@ static inline rtree_segment_t mk_rtree_seg(const Line &l) {
|
||||||
// Create a hook based on hook_line and append it to the begin or end of the polyline in the intersection
|
// Create a hook based on hook_line and append it to the begin or end of the polyline in the intersection
|
||||||
static void add_hook(
|
static void add_hook(
|
||||||
const Intersection &intersection, const double scaled_offset,
|
const Intersection &intersection, const double scaled_offset,
|
||||||
const int hook_length, double scaled_trim_distance,
|
const coordf_t hook_length, double scaled_trim_distance,
|
||||||
const rtree_t &rtree, const Lines &lines_src)
|
const rtree_t &rtree, const Lines &lines_src)
|
||||||
{
|
{
|
||||||
|
if (hook_length < SCALED_EPSILON)
|
||||||
|
// Ignore open hooks.
|
||||||
|
return;
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
{
|
||||||
|
const Vec2d v = (intersection.closest_line->b - intersection.closest_line->a).cast<double>();
|
||||||
|
const Vec2d va = (intersection.intersect_point - intersection.closest_line->a).cast<double>();
|
||||||
|
const double l2 = v.squaredNorm(); // avoid a sqrt
|
||||||
|
assert(l2 > 0.);
|
||||||
|
const double t = va.dot(v) / l2;
|
||||||
|
assert(t > 0. && t < 1.);
|
||||||
|
const double d = (t * v - va).norm();
|
||||||
|
assert(d < 1000.);
|
||||||
|
}
|
||||||
|
#endif // NDEBUG
|
||||||
|
|
||||||
// Trim the hook start by the infill line it will connect to.
|
// Trim the hook start by the infill line it will connect to.
|
||||||
Point hook_start;
|
Point hook_start;
|
||||||
bool intersection_found = intersection.intersect_line->intersection(
|
bool intersection_found = intersection.intersect_line->intersection(
|
||||||
|
@ -700,7 +717,7 @@ static void add_hook(
|
||||||
const std::vector<std::pair<rtree_segment_t, size_t>> &hook_intersections,
|
const std::vector<std::pair<rtree_segment_t, size_t>> &hook_intersections,
|
||||||
bool self_intersection, const std::optional<Line> &self_intersection_line, const Point &self_intersection_point) {
|
bool self_intersection, const std::optional<Line> &self_intersection_line, const Point &self_intersection_point) {
|
||||||
// No hook is longer than hook_length, there shouldn't be any intersection closer than that.
|
// No hook is longer than hook_length, there shouldn't be any intersection closer than that.
|
||||||
auto max_length = double(hook_length);
|
auto max_length = hook_length;
|
||||||
auto update_max_length = [&max_length](double d) {
|
auto update_max_length = [&max_length](double d) {
|
||||||
if (d < max_length)
|
if (d < max_length)
|
||||||
max_length = d;
|
max_length = d;
|
||||||
|
@ -757,15 +774,32 @@ static void add_hook(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Polylines connect_lines_using_hooks(Polylines &&lines, const ExPolygon &boundary, const double spacing, const int hook_length)
|
#ifndef NDEBUG
|
||||||
|
bool validate_intersection_t_joint(const Intersection &intersection)
|
||||||
|
{
|
||||||
|
const Vec2d v = (intersection.closest_line->b - intersection.closest_line->a).cast<double>();
|
||||||
|
const Vec2d va = (intersection.intersect_point - intersection.closest_line->a).cast<double>();
|
||||||
|
const double l2 = v.squaredNorm(); // avoid a sqrt
|
||||||
|
assert(l2 > 0.);
|
||||||
|
const double t = va.dot(v);
|
||||||
|
assert(t > SCALED_EPSILON && t < l2 - SCALED_EPSILON);
|
||||||
|
const double d = ((t / l2) * v - va).norm();
|
||||||
|
assert(d < 1000.);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool validate_intersections(const std::vector<Intersection> &intersections)
|
||||||
|
{
|
||||||
|
for (const Intersection& intersection : intersections)
|
||||||
|
assert(validate_intersection_t_joint(intersection));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif // NDEBUG
|
||||||
|
|
||||||
|
static Polylines connect_lines_using_hooks(Polylines &&lines, const ExPolygon &boundary, const double spacing, const coordf_t hook_length, const coordf_t hook_length_max)
|
||||||
{
|
{
|
||||||
rtree_t rtree;
|
rtree_t rtree;
|
||||||
size_t poly_idx = 0;
|
size_t poly_idx = 0;
|
||||||
|
|
||||||
Lines lines_src;
|
|
||||||
lines_src.reserve(lines.size());
|
|
||||||
std::transform(lines.begin(), lines.end(), std::back_inserter(lines_src), [](const Line& l) { return Polyline{ l.a, l.b }; });
|
|
||||||
|
|
||||||
// 19% overlap, slightly lower than the allowed overlap in Fill::connect_infill()
|
// 19% overlap, slightly lower than the allowed overlap in Fill::connect_infill()
|
||||||
const float scaled_offset = float(scale_(spacing) * 0.81);
|
const float scaled_offset = float(scale_(spacing) * 0.81);
|
||||||
// 25% overlap
|
// 25% overlap
|
||||||
|
@ -815,15 +849,18 @@ static Polylines connect_lines_using_hooks(Polylines &&lines, const ExPolygon &b
|
||||||
return std::make_pair(static_cast<Polyline*>(nullptr), false);
|
return std::make_pair(static_cast<Polyline*>(nullptr), false);
|
||||||
};
|
};
|
||||||
auto collinear_front = collinear_segment(poly.points.front(), poly.points.back(), &poly);
|
auto collinear_front = collinear_segment(poly.points.front(), poly.points.back(), &poly);
|
||||||
|
auto collinear_back = collinear_segment(poly.points.back(), poly.points.front(), &poly);
|
||||||
|
assert(! collinear_front.first || ! collinear_back.first || collinear_front.first != collinear_back.first);
|
||||||
if (collinear_front.first) {
|
if (collinear_front.first) {
|
||||||
Polyline &other = *collinear_front.first;
|
Polyline &other = *collinear_front.first;
|
||||||
|
assert(&other != &poly);
|
||||||
poly.points.front() = collinear_front.second ? other.points.back() : other.points.front();
|
poly.points.front() = collinear_front.second ? other.points.back() : other.points.front();
|
||||||
other.points.clear();
|
other.points.clear();
|
||||||
}
|
}
|
||||||
auto collinear_back = collinear_segment(poly.points.back(), poly.points.front(), &poly);
|
|
||||||
if (collinear_back.first) {
|
if (collinear_back.first) {
|
||||||
Polyline &other = *collinear_front.first;
|
Polyline &other = *collinear_back.first;
|
||||||
poly.points.back() = collinear_front.second ? other.points.back() : other.points.front();
|
assert(&other != &poly);
|
||||||
|
poly.points.back() = collinear_back.second ? other.points.back() : other.points.front();
|
||||||
other.points.clear();
|
other.points.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -831,6 +868,12 @@ static Polylines connect_lines_using_hooks(Polylines &&lines, const ExPolygon &b
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Convert input polylines to lines_src after the colinear segments were merged.
|
||||||
|
Lines lines_src;
|
||||||
|
lines_src.reserve(lines.size());
|
||||||
|
std::transform(lines.begin(), lines.end(), std::back_inserter(lines_src), [](const Polyline &pl) {
|
||||||
|
return pl.empty() ? Line(Point(0, 0), Point(0, 0)) : Line(pl.points.front(), pl.points.back()); });
|
||||||
|
|
||||||
sort_remove_duplicates(lines_touching_at_endpoints);
|
sort_remove_duplicates(lines_touching_at_endpoints);
|
||||||
|
|
||||||
std::vector<Intersection> intersections;
|
std::vector<Intersection> intersections;
|
||||||
|
@ -854,12 +897,26 @@ static Polylines connect_lines_using_hooks(Polylines &&lines, const ExPolygon &b
|
||||||
// Find the nearest line from the start point of the line.
|
// Find the nearest line from the start point of the line.
|
||||||
std::optional<size_t> tjoint_front, tjoint_back;
|
std::optional<size_t> tjoint_front, tjoint_back;
|
||||||
{
|
{
|
||||||
auto has_tjoint = [&closest, line_idx, &rtree, &lines](const Point &pt) {
|
auto has_tjoint = [&closest, line_idx, &rtree, &lines, &lines_src](const Point &pt) {
|
||||||
auto filter_itself = [line_idx](const auto &item) { return item.second != line_idx; };
|
auto filter_t_joint = [line_idx, &lines_src, pt](const auto &item) {
|
||||||
|
if (item.second != line_idx) {
|
||||||
|
// Verify that the point projects onto the line.
|
||||||
|
const Line &line = lines_src[item.second];
|
||||||
|
const Vec2d v = (line.b - line.a).cast<double>();
|
||||||
|
const Vec2d va = (pt - line.a).cast<double>();
|
||||||
|
const double l2 = v.squaredNorm(); // avoid a sqrt
|
||||||
|
if (l2 > 0.) {
|
||||||
|
const double t = va.dot(v);
|
||||||
|
return t > SCALED_EPSILON && t < l2 - SCALED_EPSILON;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
closest.clear();
|
closest.clear();
|
||||||
rtree.query(bgi::nearest(mk_rtree_point(pt), 1) && bgi::satisfies(filter_itself), std::back_inserter(closest));
|
rtree.query(bgi::nearest(mk_rtree_point(pt), 1) && bgi::satisfies(filter_t_joint), std::back_inserter(closest));
|
||||||
const Polyline &pl = lines[closest.front().second];
|
|
||||||
std::optional<size_t> out;
|
std::optional<size_t> out;
|
||||||
|
if (! closest.empty()) {
|
||||||
|
const Polyline &pl = lines[closest.front().second];
|
||||||
if (pl.points.empty()) {
|
if (pl.points.empty()) {
|
||||||
// The closest infill line was already dropped as it was too short.
|
// The closest infill line was already dropped as it was too short.
|
||||||
// Such an infill line should not make a T-joint anyways.
|
// Such an infill line should not make a T-joint anyways.
|
||||||
|
@ -871,6 +928,7 @@ static Polylines connect_lines_using_hooks(Polylines &&lines, const ExPolygon &b
|
||||||
#endif // NDEBUG
|
#endif // NDEBUG
|
||||||
} else if (((Line)pl).distance_to_squared(pt) <= 1000 * 1000)
|
} else if (((Line)pl).distance_to_squared(pt) <= 1000 * 1000)
|
||||||
out = closest.front().second;
|
out = closest.front().second;
|
||||||
|
}
|
||||||
return out;
|
return out;
|
||||||
};
|
};
|
||||||
// Refuse to create a T-joint if the infill lines touch at their ends.
|
// Refuse to create a T-joint if the infill lines touch at their ends.
|
||||||
|
@ -912,12 +970,16 @@ static Polylines connect_lines_using_hooks(Polylines &&lines, const ExPolygon &b
|
||||||
// A shorter line than spacing could produce a degenerate polyline.
|
// A shorter line than spacing could produce a degenerate polyline.
|
||||||
line.points.clear();
|
line.points.clear();
|
||||||
} else if (anchor) {
|
} else if (anchor) {
|
||||||
if (tjoint_front)
|
if (tjoint_front) {
|
||||||
// T-joint of line's front point with the 'closest' line.
|
// T-joint of line's front point with the 'closest' line.
|
||||||
intersections.emplace_back(lines_src[*tjoint_front], lines_src[line_idx], &line, front_point, true);
|
intersections.emplace_back(lines_src[*tjoint_front], lines_src[line_idx], &line, front_point, true);
|
||||||
if (tjoint_back)
|
assert(validate_intersection_t_joint(intersections.back()));
|
||||||
|
}
|
||||||
|
if (tjoint_back) {
|
||||||
// T-joint of line's back point with the 'closest' line.
|
// T-joint of line's back point with the 'closest' line.
|
||||||
intersections.emplace_back(lines_src[*tjoint_back], lines_src[line_idx], &line, back_point, false);
|
intersections.emplace_back(lines_src[*tjoint_back], lines_src[line_idx], &line, back_point, false);
|
||||||
|
assert(validate_intersection_t_joint(intersections.back()));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (tjoint_front)
|
if (tjoint_front)
|
||||||
// T joint at the front at a 60 degree angle, the line is very short.
|
// T joint at the front at a 60 degree angle, the line is very short.
|
||||||
|
@ -940,6 +1002,7 @@ static Polylines connect_lines_using_hooks(Polylines &&lines, const ExPolygon &b
|
||||||
++ it;
|
++ it;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
assert(validate_intersections(intersections));
|
||||||
|
|
||||||
#ifdef ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
|
#ifdef ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
|
||||||
static int iRun = 0;
|
static int iRun = 0;
|
||||||
|
@ -1106,7 +1169,7 @@ static Polylines connect_lines_using_hooks(Polylines &&lines, const ExPolygon &b
|
||||||
}
|
}
|
||||||
Points &first_points = first_i.intersect_pl->points;
|
Points &first_points = first_i.intersect_pl->points;
|
||||||
Points &second_points = nearest_i.intersect_pl->points;
|
Points &second_points = nearest_i.intersect_pl->points;
|
||||||
could_connect &= (nearest_i_point - first_i_point).cast<double>().squaredNorm() <= Slic3r::sqr(3. * hook_length);
|
could_connect &= (nearest_i_point - first_i_point).cast<double>().squaredNorm() <= Slic3r::sqr(hook_length_max);
|
||||||
if (could_connect) {
|
if (could_connect) {
|
||||||
// Both intersections are so close that their polylines can be connected.
|
// Both intersections are so close that their polylines can be connected.
|
||||||
// Verify that no other infill line intersects this anchor line.
|
// Verify that no other infill line intersects this anchor line.
|
||||||
|
@ -1219,7 +1282,7 @@ bool has_no_collinear_lines(const Polylines &polylines)
|
||||||
const Point* operator()(const LineEnd &pt) const { return &pt.point(); }
|
const Point* operator()(const LineEnd &pt) const { return &pt.point(); }
|
||||||
};
|
};
|
||||||
typedef ClosestPointInRadiusLookup<LineEnd, LineEndAccessor> ClosestPointLookupType;
|
typedef ClosestPointInRadiusLookup<LineEnd, LineEndAccessor> ClosestPointLookupType;
|
||||||
ClosestPointLookupType closest_end_point_lookup(1001. * sqrt(2.));
|
ClosestPointLookupType closest_end_point_lookup(coord_t(1001. * sqrt(2.)));
|
||||||
for (const Polyline& pl : polylines) {
|
for (const Polyline& pl : polylines) {
|
||||||
// assert(pl.points.size() == 2);
|
// assert(pl.points.size() == 2);
|
||||||
auto line_start = LineEnd(&pl, true);
|
auto line_start = LineEnd(&pl, true);
|
||||||
|
@ -1321,9 +1384,10 @@ void Filler::_fill_surface_single(
|
||||||
}
|
}
|
||||||
#endif /* ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT */
|
#endif /* ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT */
|
||||||
|
|
||||||
const auto hook_length = coord_t(std::min(scale_(this->spacing * 5), scale_(params.anchor_length)));
|
const auto hook_length = coordf_t(std::min<float>(std::numeric_limits<coord_t>::max(), scale_(params.anchor_length)));
|
||||||
|
const auto hook_length_max = coordf_t(std::min<float>(std::numeric_limits<coord_t>::max(), scale_(params.anchor_length_max)));
|
||||||
|
|
||||||
Polylines all_polylines_with_hooks = all_polylines.size() > 1 ? connect_lines_using_hooks(std::move(all_polylines), expolygon, this->spacing, hook_length) : std::move(all_polylines);
|
Polylines all_polylines_with_hooks = all_polylines.size() > 1 ? connect_lines_using_hooks(std::move(all_polylines), expolygon, this->spacing, hook_length, hook_length_max) : std::move(all_polylines);
|
||||||
|
|
||||||
#ifdef ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
|
#ifdef ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
|
||||||
{
|
{
|
||||||
|
|
|
@ -200,10 +200,10 @@ struct ContourIntersectionPoint {
|
||||||
|
|
||||||
// Could extrude a complete segment from this to this->prev_on_contour.
|
// Could extrude a complete segment from this to this->prev_on_contour.
|
||||||
bool could_connect_prev() const throw()
|
bool could_connect_prev() const throw()
|
||||||
{ return ! this->consumed && this->prev_on_contour && ! this->prev_on_contour->consumed && ! this->prev_trimmed && ! this->prev_on_contour->next_trimmed; }
|
{ return ! this->consumed && this->prev_on_contour != this && ! this->prev_on_contour->consumed && ! this->prev_trimmed && ! this->prev_on_contour->next_trimmed; }
|
||||||
// Could extrude a complete segment from this to this->next_on_contour.
|
// Could extrude a complete segment from this to this->next_on_contour.
|
||||||
bool could_connect_next() const throw()
|
bool could_connect_next() const throw()
|
||||||
{ return ! this->consumed && this->next_on_contour && ! this->next_on_contour->consumed && ! this->next_trimmed && ! this->next_on_contour->prev_trimmed; }
|
{ return ! this->consumed && this->next_on_contour != this && ! this->next_on_contour->consumed && ! this->next_trimmed && ! this->next_on_contour->prev_trimmed; }
|
||||||
};
|
};
|
||||||
|
|
||||||
// Distance from param1 to param2 when going counter-clockwise.
|
// Distance from param1 to param2 when going counter-clockwise.
|
||||||
|
@ -390,7 +390,12 @@ static void take(Polyline &pl1, const Polyline &pl2, const Points &contour, size
|
||||||
|
|
||||||
static void take(Polyline &pl1, const Polyline &pl2, const Points &contour, ContourIntersectionPoint *cp_start, ContourIntersectionPoint *cp_end, bool clockwise)
|
static void take(Polyline &pl1, const Polyline &pl2, const Points &contour, ContourIntersectionPoint *cp_start, ContourIntersectionPoint *cp_end, bool clockwise)
|
||||||
{
|
{
|
||||||
|
assert(cp_start->prev_on_contour != nullptr);
|
||||||
|
assert(cp_start->next_on_contour != nullptr);
|
||||||
|
assert(cp_end ->prev_on_contour != nullptr);
|
||||||
|
assert(cp_end ->next_on_contour != nullptr);
|
||||||
assert(cp_start != cp_end);
|
assert(cp_start != cp_end);
|
||||||
|
|
||||||
take(pl1, pl2, contour, cp_start->point_idx, cp_end->point_idx, clockwise);
|
take(pl1, pl2, contour, cp_start->point_idx, cp_end->point_idx, clockwise);
|
||||||
|
|
||||||
// Mark the contour segments in between cp_start and cp_end as consumed.
|
// Mark the contour segments in between cp_start and cp_end as consumed.
|
||||||
|
@ -410,7 +415,12 @@ static void take_limited(
|
||||||
ContourIntersectionPoint *cp_start, ContourIntersectionPoint *cp_end, bool clockwise, float take_max_length, float line_half_width)
|
ContourIntersectionPoint *cp_start, ContourIntersectionPoint *cp_end, bool clockwise, float take_max_length, float line_half_width)
|
||||||
{
|
{
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
assert(cp_start != cp_end);
|
// This is a valid case, where a single infill line connect to two different contours (outer contour + hole or two holes).
|
||||||
|
// assert(cp_start != cp_end);
|
||||||
|
assert(cp_start->prev_on_contour != nullptr);
|
||||||
|
assert(cp_start->next_on_contour != nullptr);
|
||||||
|
assert(cp_end ->prev_on_contour != nullptr);
|
||||||
|
assert(cp_end ->next_on_contour != nullptr);
|
||||||
assert(pl1.size() >= 2);
|
assert(pl1.size() >= 2);
|
||||||
assert(contour.size() + 1 == params.size());
|
assert(contour.size() + 1 == params.size());
|
||||||
#endif /* NDEBUG */
|
#endif /* NDEBUG */
|
||||||
|
@ -438,8 +448,18 @@ static void take_limited(
|
||||||
float length = params.back();
|
float length = params.back();
|
||||||
float length_to_go = take_max_length;
|
float length_to_go = take_max_length;
|
||||||
cp_start->consumed = true;
|
cp_start->consumed = true;
|
||||||
if (clockwise) {
|
if (cp_start == cp_end) {
|
||||||
|
length_to_go = std::max(0.f, std::min(length_to_go, length - line_half_width));
|
||||||
|
length_to_go = std::min(length_to_go, clockwise ? cp_start->contour_not_taken_length_prev : cp_start->contour_not_taken_length_next);
|
||||||
|
cp_start->consume_prev();
|
||||||
|
cp_start->consume_next();
|
||||||
|
if (length_to_go > SCALED_EPSILON)
|
||||||
|
clockwise ?
|
||||||
|
take_cw_limited (pl1, contour, params, cp_start->point_idx, cp_start->point_idx, length_to_go) :
|
||||||
|
take_ccw_limited(pl1, contour, params, cp_start->point_idx, cp_start->point_idx, length_to_go);
|
||||||
|
} else if (clockwise) {
|
||||||
// Going clockwise from cp_start to cp_end.
|
// Going clockwise from cp_start to cp_end.
|
||||||
|
assert(cp_start != cp_end);
|
||||||
for (ContourIntersectionPoint *cp = cp_start; cp != cp_end; cp = cp->prev_on_contour) {
|
for (ContourIntersectionPoint *cp = cp_start; cp != cp_end; cp = cp->prev_on_contour) {
|
||||||
// Length of the segment from cp to cp->prev_on_contour.
|
// Length of the segment from cp to cp->prev_on_contour.
|
||||||
float l = closed_contour_distance_cw(cp->param, cp->prev_on_contour->param, length);
|
float l = closed_contour_distance_cw(cp->param, cp->prev_on_contour->param, length);
|
||||||
|
@ -461,6 +481,7 @@ static void take_limited(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
assert(cp_start != cp_end);
|
||||||
for (ContourIntersectionPoint *cp = cp_start; cp != cp_end; cp = cp->next_on_contour) {
|
for (ContourIntersectionPoint *cp = cp_start; cp != cp_end; cp = cp->next_on_contour) {
|
||||||
float l = closed_contour_distance_ccw(cp->param, cp->next_on_contour->param, length);
|
float l = closed_contour_distance_ccw(cp->param, cp->next_on_contour->param, length);
|
||||||
length_to_go = std::min(length_to_go, cp->contour_not_taken_length_next);
|
length_to_go = std::min(length_to_go, cp->contour_not_taken_length_next);
|
||||||
|
@ -869,6 +890,10 @@ void mark_boundary_segments_touching_infill(
|
||||||
for (auto it_contour_and_segment = cell_data_range.first; it_contour_and_segment != cell_data_range.second; ++ it_contour_and_segment) {
|
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.
|
// End points of the line segment and their vector.
|
||||||
auto segment = this->grid.segment(*it_contour_and_segment);
|
auto segment = this->grid.segment(*it_contour_and_segment);
|
||||||
|
std::vector<ContourIntersectionPoint*> &intersections = boundary_intersections[it_contour_and_segment->first];
|
||||||
|
if (intersections.empty())
|
||||||
|
// There is no infil line touching this contour, thus effort will be saved to calculate overlap with other infill lines.
|
||||||
|
continue;
|
||||||
const Vec2d seg_pt1 = segment.first.cast<double>();
|
const Vec2d seg_pt1 = segment.first.cast<double>();
|
||||||
const Vec2d seg_pt2 = segment.second.cast<double>();
|
const Vec2d seg_pt2 = segment.second.cast<double>();
|
||||||
std::pair<double, double> interval;
|
std::pair<double, double> interval;
|
||||||
|
@ -892,20 +917,23 @@ void mark_boundary_segments_touching_infill(
|
||||||
const float param_overlap1 = param_seg_pt1 + interval.first;
|
const float param_overlap1 = param_seg_pt1 + interval.first;
|
||||||
const float param_overlap2 = param_seg_pt1 + interval.second;
|
const float param_overlap2 = param_seg_pt1 + interval.second;
|
||||||
// 2) Find the ContourIntersectionPoints before param_overlap1 and after param_overlap2.
|
// 2) Find the ContourIntersectionPoints before param_overlap1 and after param_overlap2.
|
||||||
std::vector<ContourIntersectionPoint*> &intersections = boundary_intersections[it_contour_and_segment->first];
|
|
||||||
// Find the span of ContourIntersectionPoints, that is trimmed by the interval (param_overlap1, param_overlap2).
|
// Find the span of ContourIntersectionPoints, that is trimmed by the interval (param_overlap1, param_overlap2).
|
||||||
ContourIntersectionPoint *ip_low, *ip_high;
|
ContourIntersectionPoint *ip_low, *ip_high;
|
||||||
{
|
if (intersections.size() == 1) {
|
||||||
|
// Only a single infill line touches this contour.
|
||||||
|
ip_low = ip_high = intersections.front();
|
||||||
|
} else {
|
||||||
|
assert(intersections.size() > 1);
|
||||||
auto it_low = Slic3r::lower_bound_by_predicate(intersections.begin(), intersections.end(), [param_overlap1](const ContourIntersectionPoint *l) { return l->param < param_overlap1; });
|
auto it_low = Slic3r::lower_bound_by_predicate(intersections.begin(), intersections.end(), [param_overlap1](const ContourIntersectionPoint *l) { return l->param < param_overlap1; });
|
||||||
auto it_high = Slic3r::lower_bound_by_predicate(intersections.begin(), intersections.end(), [param_overlap2](const ContourIntersectionPoint *l) { return l->param < param_overlap2; });
|
auto it_high = Slic3r::lower_bound_by_predicate(intersections.begin(), intersections.end(), [param_overlap2](const ContourIntersectionPoint *l) { return l->param < param_overlap2; });
|
||||||
ip_low = it_low == intersections.end() ? intersections.front() : *it_low;
|
ip_low = it_low == intersections.end() ? intersections.front() : *it_low;
|
||||||
ip_high = it_high == intersections.end() ? intersections.front() : *it_high;
|
ip_high = it_high == intersections.end() ? intersections.front() : *it_high;
|
||||||
if (ip_low->param != param_overlap1)
|
if (ip_low->param != param_overlap1)
|
||||||
ip_low = ip_low->prev_on_contour;
|
ip_low = ip_low->prev_on_contour;
|
||||||
}
|
|
||||||
assert(ip_low != ip_high);
|
assert(ip_low != ip_high);
|
||||||
// Verify that the interval (param_overlap1, param_overlap2) is inside the interval (ip_low->param, ip_high->param).
|
// Verify that the interval (param_overlap1, param_overlap2) is inside the interval (ip_low->param, ip_high->param).
|
||||||
assert(cyclic_interval_inside_interval(ip_low->param, ip_high->param, param_overlap1, param_overlap2, contour_length));
|
assert(cyclic_interval_inside_interval(ip_low->param, ip_high->param, param_overlap1, param_overlap2, contour_length));
|
||||||
|
}
|
||||||
assert(validate_boundary_intersections(boundary_intersections));
|
assert(validate_boundary_intersections(boundary_intersections));
|
||||||
// Mark all ContourIntersectionPoints between ip_low and ip_high as consumed.
|
// Mark all ContourIntersectionPoints between ip_low and ip_high as consumed.
|
||||||
if (ip_low->next_on_contour != ip_high)
|
if (ip_low->next_on_contour != ip_high)
|
||||||
|
@ -1068,8 +1096,11 @@ void Fill::connect_infill(Polylines &&infill_ordered, const Polygons &boundary_s
|
||||||
void Fill::connect_infill(Polylines &&infill_ordered, const std::vector<const Polygon*> &boundary_src, const BoundingBox &bbox, Polylines &polylines_out, const double spacing, const FillParams ¶ms)
|
void Fill::connect_infill(Polylines &&infill_ordered, const std::vector<const Polygon*> &boundary_src, const BoundingBox &bbox, Polylines &polylines_out, const double spacing, const FillParams ¶ms)
|
||||||
{
|
{
|
||||||
assert(! infill_ordered.empty());
|
assert(! infill_ordered.empty());
|
||||||
assert(params.anchor_length >= 0.01f);
|
assert(params.anchor_length >= 0.f);
|
||||||
|
assert(params.anchor_length_max >= 0.01f);
|
||||||
|
assert(params.anchor_length_max >= params.anchor_length);
|
||||||
const auto anchor_length = float(scale_(params.anchor_length));
|
const auto anchor_length = float(scale_(params.anchor_length));
|
||||||
|
const auto anchor_length_max = float(scale_(params.anchor_length_max));
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
append(polylines_out, infill_ordered);
|
append(polylines_out, infill_ordered);
|
||||||
|
@ -1097,7 +1128,7 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vector<const Po
|
||||||
EdgeGrid::Grid::ClosestPointResult cp = grid.closest_point(*pt, SCALED_EPSILON);
|
EdgeGrid::Grid::ClosestPointResult cp = grid.closest_point(*pt, SCALED_EPSILON);
|
||||||
if (cp.valid()) {
|
if (cp.valid()) {
|
||||||
// The infill end point shall lie on the contour.
|
// The infill end point shall lie on the contour.
|
||||||
assert(cp.distance < 2.);
|
assert(cp.distance <= 3.);
|
||||||
intersection_points.emplace_back(cp, (&pl - infill_ordered.data()) * 2 + (pt == &pl.points.front() ? 0 : 1));
|
intersection_points.emplace_back(cp, (&pl - infill_ordered.data()) * 2 + (pt == &pl.points.front() ? 0 : 1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1154,7 +1185,7 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vector<const Po
|
||||||
//add new point here
|
//add new point here
|
||||||
contour_dst.emplace_back(pt);
|
contour_dst.emplace_back(pt);
|
||||||
}
|
}
|
||||||
if (pprev != pfirst) {
|
if (pfirst) {
|
||||||
pprev->next_on_contour = pfirst;
|
pprev->next_on_contour = pfirst;
|
||||||
pfirst->prev_on_contour = pprev;
|
pfirst->prev_on_contour = pprev;
|
||||||
}
|
}
|
||||||
|
@ -1170,7 +1201,12 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vector<const Po
|
||||||
ip->param = contour_params[ip->point_idx];
|
ip->param = contour_params[ip->point_idx];
|
||||||
// and measure distance to the previous and next intersection point.
|
// and measure distance to the previous and next intersection point.
|
||||||
const float contour_length = contour_params.back();
|
const float contour_length = contour_params.back();
|
||||||
for (ContourIntersectionPoint *ip : contour_intersection_points) {
|
for (ContourIntersectionPoint *ip : contour_intersection_points)
|
||||||
|
if (ip->next_on_contour == ip) {
|
||||||
|
assert(ip->prev_on_contour == ip);
|
||||||
|
ip->contour_not_taken_length_prev = ip->contour_not_taken_length_next = contour_length;
|
||||||
|
} else {
|
||||||
|
assert(ip->prev_on_contour != ip);
|
||||||
ip->contour_not_taken_length_prev = closed_contour_distance_ccw(ip->prev_on_contour->param, ip->param, contour_length);
|
ip->contour_not_taken_length_prev = closed_contour_distance_ccw(ip->prev_on_contour->param, ip->param, contour_length);
|
||||||
ip->contour_not_taken_length_next = closed_contour_distance_ccw(ip->param, ip->next_on_contour->param, contour_length);
|
ip->contour_not_taken_length_next = closed_contour_distance_ccw(ip->param, ip->next_on_contour->param, contour_length);
|
||||||
}
|
}
|
||||||
|
@ -1277,7 +1313,7 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vector<const Po
|
||||||
idx_first = get_and_update_merged_with(idx_first);
|
idx_first = get_and_update_merged_with(idx_first);
|
||||||
assert(idx_first < idx_second);
|
assert(idx_first < idx_second);
|
||||||
assert(idx_second == merged_with[idx_second]);
|
assert(idx_second == merged_with[idx_second]);
|
||||||
if (could_connect && length < anchor_length * 2.5) {
|
if (could_connect && length < anchor_length_max) {
|
||||||
// Take the complete contour.
|
// Take the complete contour.
|
||||||
// Connect the two polygons using the boundary contour.
|
// Connect the two polygons using the boundary contour.
|
||||||
take(infill_ordered[idx_first], infill_ordered[idx_second], boundary[cp1->contour_idx], cp1, cp2, connection_cost.reversed);
|
take(infill_ordered[idx_first], infill_ordered[idx_second], boundary[cp1->contour_idx], cp1, cp2, connection_cost.reversed);
|
||||||
|
@ -1299,10 +1335,11 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vector<const Po
|
||||||
std::vector<Arc> arches;
|
std::vector<Arc> arches;
|
||||||
arches.reserve(map_infill_end_point_to_boundary.size());
|
arches.reserve(map_infill_end_point_to_boundary.size());
|
||||||
for (ContourIntersectionPoint &cp : map_infill_end_point_to_boundary)
|
for (ContourIntersectionPoint &cp : map_infill_end_point_to_boundary)
|
||||||
if (! cp.contour_idx != boundary_idx_unconnected && cp.next_on_contour != &cp && cp.could_connect_next())
|
if (cp.contour_idx != boundary_idx_unconnected && cp.next_on_contour != &cp && cp.could_connect_next())
|
||||||
arches.push_back({ &cp, path_length_along_contour_ccw(&cp, cp.next_on_contour, boundary_params[cp.contour_idx].back()) });
|
arches.push_back({ &cp, path_length_along_contour_ccw(&cp, cp.next_on_contour, boundary_params[cp.contour_idx].back()) });
|
||||||
std::sort(arches.begin(), arches.end(), [](const auto &l, const auto &r) { return l.arc_length < r.arc_length; });
|
std::sort(arches.begin(), arches.end(), [](const auto &l, const auto &r) { return l.arc_length < r.arc_length; });
|
||||||
|
|
||||||
|
//FIXME improve the Traveling Salesman problem with 2-opt and 3-opt local optimization.
|
||||||
for (Arc &arc : arches)
|
for (Arc &arc : arches)
|
||||||
if (! arc.intersection->consumed && ! arc.intersection->next_on_contour->consumed) {
|
if (! arc.intersection->consumed && ! arc.intersection->next_on_contour->consumed) {
|
||||||
// Indices of the polylines to be connected by a perimeter segment.
|
// Indices of the polylines to be connected by a perimeter segment.
|
||||||
|
@ -1315,7 +1352,7 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vector<const Po
|
||||||
if (polyline_idx1 != polyline_idx2) {
|
if (polyline_idx1 != polyline_idx2) {
|
||||||
Polyline &polyline1 = infill_ordered[polyline_idx1];
|
Polyline &polyline1 = infill_ordered[polyline_idx1];
|
||||||
Polyline &polyline2 = infill_ordered[polyline_idx2];
|
Polyline &polyline2 = infill_ordered[polyline_idx2];
|
||||||
if (arc.arc_length < anchor_length * 2.5) {
|
if (arc.arc_length < anchor_length_max) {
|
||||||
// Not closing a loop, connecting the lines.
|
// Not closing a loop, connecting the lines.
|
||||||
assert(contour[cp1->point_idx] == polyline1.points.front() || contour[cp1->point_idx] == polyline1.points.back());
|
assert(contour[cp1->point_idx] == polyline1.points.front() || contour[cp1->point_idx] == polyline1.points.back());
|
||||||
if (contour[cp1->point_idx] == polyline1.points.front())
|
if (contour[cp1->point_idx] == polyline1.points.front())
|
||||||
|
@ -1333,7 +1370,7 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vector<const Po
|
||||||
polyline2.points.clear();
|
polyline2.points.clear();
|
||||||
merged_with[polyline_idx2] = merged_with[polyline_idx1];
|
merged_with[polyline_idx2] = merged_with[polyline_idx1];
|
||||||
}
|
}
|
||||||
} else {
|
} else if (anchor_length > SCALED_EPSILON) {
|
||||||
// Move along the perimeter, but don't take the whole arc.
|
// Move along the perimeter, but don't take the whole arc.
|
||||||
take_limited(polyline1, contour, contour_params, cp1, cp2, false, anchor_length, line_half_width);
|
take_limited(polyline1, contour, contour_params, cp1, cp2, false, anchor_length, line_half_width);
|
||||||
take_limited(polyline2, contour, contour_params, cp2, cp1, true, anchor_length, line_half_width);
|
take_limited(polyline2, contour, contour_params, cp2, cp1, true, anchor_length, line_half_width);
|
||||||
|
@ -1360,7 +1397,7 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vector<const Po
|
||||||
assert(contour[contour_point.point_idx] == polyline.points.front() || contour[contour_point.point_idx] == polyline.points.back());
|
assert(contour[contour_point.point_idx] == polyline.points.front() || contour[contour_point.point_idx] == polyline.points.back());
|
||||||
bool connected = false;
|
bool connected = false;
|
||||||
for (float l : { std::min(lprev, lnext), std::max(lprev, lnext) }) {
|
for (float l : { std::min(lprev, lnext), std::max(lprev, lnext) }) {
|
||||||
if (l == std::numeric_limits<float>::max() || l > anchor_length * 2.5)
|
if (l == std::numeric_limits<float>::max() || l > anchor_length_max)
|
||||||
break;
|
break;
|
||||||
// Take the complete contour.
|
// Take the complete contour.
|
||||||
bool reversed = l == lprev;
|
bool reversed = l == lprev;
|
||||||
|
@ -1392,7 +1429,7 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vector<const Po
|
||||||
connected = true;
|
connected = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (! connected) {
|
if (! connected && anchor_length > SCALED_EPSILON) {
|
||||||
// Which to take? One could optimize for:
|
// Which to take? One could optimize for:
|
||||||
// 1) Shortest path
|
// 1) Shortest path
|
||||||
// 2) Hook length
|
// 2) Hook length
|
||||||
|
|
|
@ -34,7 +34,7 @@ struct FillParams
|
||||||
{
|
{
|
||||||
bool full_infill() const { return density > 0.9999f; }
|
bool full_infill() const { return density > 0.9999f; }
|
||||||
// Don't connect the fill lines around the inner perimeter.
|
// Don't connect the fill lines around the inner perimeter.
|
||||||
bool dont_connect() const { return anchor_length < 0.05f; }
|
bool dont_connect() const { return anchor_length_max < 0.05f; }
|
||||||
|
|
||||||
// Fill density, fraction in <0, 1>
|
// Fill density, fraction in <0, 1>
|
||||||
float density { 0.f };
|
float density { 0.f };
|
||||||
|
@ -42,6 +42,7 @@ struct FillParams
|
||||||
// Length of an infill anchor along the perimeter.
|
// Length of an infill anchor along the perimeter.
|
||||||
// 1000mm is roughly the maximum length line that fits into a 32bit coord_t.
|
// 1000mm is roughly the maximum length line that fits into a 32bit coord_t.
|
||||||
float anchor_length { 1000.f };
|
float anchor_length { 1000.f };
|
||||||
|
float anchor_length_max { 1000.f };
|
||||||
|
|
||||||
// Don't adjust spacing to fill the space evenly.
|
// Don't adjust spacing to fill the space evenly.
|
||||||
bool dont_adjust { true };
|
bool dont_adjust { true };
|
||||||
|
|
|
@ -39,7 +39,7 @@ void FillConcentric::_fill_surface_single(
|
||||||
size_t iPathFirst = polylines_out.size();
|
size_t iPathFirst = polylines_out.size();
|
||||||
Point last_pos(0, 0);
|
Point last_pos(0, 0);
|
||||||
for (const Polygon &loop : loops) {
|
for (const Polygon &loop : loops) {
|
||||||
polylines_out.push_back(loop.split_at_index(last_pos.nearest_point_index(loop)));
|
polylines_out.push_back(loop.split_at_index(last_pos.nearest_point_index(loop.points)));
|
||||||
last_pos = polylines_out.back().last_point();
|
last_pos = polylines_out.back().last_point();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -49,7 +49,7 @@ Slic3r::arrangement::ArrangePolygon get_arrange_poly(const Model &model)
|
||||||
std::copy(pts.begin(), pts.end(), std::back_inserter(apts));
|
std::copy(pts.begin(), pts.end(), std::back_inserter(apts));
|
||||||
}
|
}
|
||||||
|
|
||||||
apts = Geometry::convex_hull(apts);
|
apts = std::move(Geometry::convex_hull(apts).points);
|
||||||
return ap;
|
return ap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -264,7 +264,7 @@ Point MotionPlannerEnv::nearest_env_point(const Point &from, const Point &to) co
|
||||||
for (const ExPolygon &ex : m_env.expolygons) {
|
for (const ExPolygon &ex : m_env.expolygons) {
|
||||||
for (const Polygon &hole : ex.holes)
|
for (const Polygon &hole : ex.holes)
|
||||||
if (hole.contains(from))
|
if (hole.contains(from))
|
||||||
pp = hole;
|
pp = hole.points;
|
||||||
if (! pp.empty())
|
if (! pp.empty())
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,8 +17,6 @@ class MultiPoint
|
||||||
public:
|
public:
|
||||||
Points points;
|
Points points;
|
||||||
|
|
||||||
operator Points() const { return this->points; }
|
|
||||||
|
|
||||||
MultiPoint() {}
|
MultiPoint() {}
|
||||||
MultiPoint(const MultiPoint &other) : points(other.points) {}
|
MultiPoint(const MultiPoint &other) : points(other.points) {}
|
||||||
MultiPoint(MultiPoint &&other) : points(std::move(other.points)) {}
|
MultiPoint(MultiPoint &&other) : points(std::move(other.points)) {}
|
||||||
|
|
|
@ -158,7 +158,7 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime
|
||||||
// get non-overhang paths by intersecting this loop with the grown lower slices
|
// get non-overhang paths by intersecting this loop with the grown lower slices
|
||||||
extrusion_paths_append(
|
extrusion_paths_append(
|
||||||
paths,
|
paths,
|
||||||
intersection_pl(loop.polygon, perimeter_generator.lower_slices_polygons()),
|
intersection_pl((Polygons)loop.polygon, perimeter_generator.lower_slices_polygons()),
|
||||||
role,
|
role,
|
||||||
is_external ? perimeter_generator.ext_mm3_per_mm() : perimeter_generator.mm3_per_mm(),
|
is_external ? perimeter_generator.ext_mm3_per_mm() : perimeter_generator.mm3_per_mm(),
|
||||||
is_external ? perimeter_generator.ext_perimeter_flow.width : perimeter_generator.perimeter_flow.width,
|
is_external ? perimeter_generator.ext_perimeter_flow.width : perimeter_generator.perimeter_flow.width,
|
||||||
|
@ -169,7 +169,7 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime
|
||||||
// the loop centerline and original lower slices is >= half nozzle diameter
|
// the loop centerline and original lower slices is >= half nozzle diameter
|
||||||
extrusion_paths_append(
|
extrusion_paths_append(
|
||||||
paths,
|
paths,
|
||||||
diff_pl(loop.polygon, perimeter_generator.lower_slices_polygons()),
|
diff_pl((Polygons)loop.polygon, perimeter_generator.lower_slices_polygons()),
|
||||||
erOverhangPerimeter,
|
erOverhangPerimeter,
|
||||||
perimeter_generator.mm3_per_mm_overhang(),
|
perimeter_generator.mm3_per_mm_overhang(),
|
||||||
perimeter_generator.overhang_flow.width,
|
perimeter_generator.overhang_flow.width,
|
||||||
|
|
|
@ -16,8 +16,8 @@ typedef std::vector<Polygon> Polygons;
|
||||||
class Polygon : public MultiPoint
|
class Polygon : public MultiPoint
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
operator Polygons() const { Polygons pp; pp.push_back(*this); return pp; }
|
explicit operator Polygons() const { Polygons pp; pp.push_back(*this); return pp; }
|
||||||
operator Polyline() const { return this->split_at_first_point(); }
|
explicit operator Polyline() const { return this->split_at_first_point(); }
|
||||||
Point& operator[](Points::size_type idx) { return this->points[idx]; }
|
Point& operator[](Points::size_type idx) { return this->points[idx]; }
|
||||||
const Point& operator[](Points::size_type idx) const { return this->points[idx]; }
|
const Point& operator[](Points::size_type idx) const { return this->points[idx]; }
|
||||||
|
|
||||||
|
|
|
@ -200,7 +200,7 @@ BoundingBox get_extents(const Polylines &polylines)
|
||||||
if (! polylines.empty()) {
|
if (! polylines.empty()) {
|
||||||
bb = polylines.front().bounding_box();
|
bb = polylines.front().bounding_box();
|
||||||
for (size_t i = 1; i < polylines.size(); ++ i)
|
for (size_t i = 1; i < polylines.size(); ++ i)
|
||||||
bb.merge(polylines[i]);
|
bb.merge(polylines[i].points);
|
||||||
}
|
}
|
||||||
return bb;
|
return bb;
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,8 +60,8 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
operator Polylines() const;
|
explicit operator Polylines() const;
|
||||||
operator Line() const;
|
explicit operator Line() const;
|
||||||
const Point& last_point() const override { return this->points.back(); }
|
const Point& last_point() const override { return this->points.back(); }
|
||||||
|
|
||||||
const Point& leftmost_point() const;
|
const Point& leftmost_point() const;
|
||||||
|
|
|
@ -427,7 +427,7 @@ const std::vector<std::string>& Preset::print_options()
|
||||||
"infill_extruder", "solid_infill_extruder", "support_material_extruder", "support_material_interface_extruder",
|
"infill_extruder", "solid_infill_extruder", "support_material_extruder", "support_material_interface_extruder",
|
||||||
"ooze_prevention", "standby_temperature_delta", "interface_shells", "extrusion_width", "first_layer_extrusion_width",
|
"ooze_prevention", "standby_temperature_delta", "interface_shells", "extrusion_width", "first_layer_extrusion_width",
|
||||||
"perimeter_extrusion_width", "external_perimeter_extrusion_width", "infill_extrusion_width", "solid_infill_extrusion_width",
|
"perimeter_extrusion_width", "external_perimeter_extrusion_width", "infill_extrusion_width", "solid_infill_extrusion_width",
|
||||||
"top_infill_extrusion_width", "support_material_extrusion_width", "infill_overlap", "infill_anchor", "bridge_flow_ratio", "clip_multipart_objects",
|
"top_infill_extrusion_width", "support_material_extrusion_width", "infill_overlap", "infill_anchor", "infill_anchor_max", "bridge_flow_ratio", "clip_multipart_objects",
|
||||||
"elefant_foot_compensation", "xy_size_compensation", "threads", "resolution", "wipe_tower", "wipe_tower_x", "wipe_tower_y",
|
"elefant_foot_compensation", "xy_size_compensation", "threads", "resolution", "wipe_tower", "wipe_tower_x", "wipe_tower_y",
|
||||||
"wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_bridging", "single_extruder_multi_material_priming",
|
"wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_bridging", "single_extruder_multi_material_priming",
|
||||||
"wipe_tower_no_sparse_layers", "compatible_printers", "compatible_printers_condition", "inherits"
|
"wipe_tower_no_sparse_layers", "compatible_printers", "compatible_printers_condition", "inherits"
|
||||||
|
|
|
@ -1220,9 +1220,9 @@ static inline bool sequential_print_horizontal_clearance_valid(const Print &prin
|
||||||
// instance.shift is a position of a centered object, while model object may not be centered.
|
// instance.shift is a position of a centered object, while model object may not be centered.
|
||||||
// Conver the shift from the PrintObject's coordinates into ModelObject's coordinates by removing the centering offset.
|
// Conver the shift from the PrintObject's coordinates into ModelObject's coordinates by removing the centering offset.
|
||||||
convex_hull.translate(instance.shift - print_object->center_offset());
|
convex_hull.translate(instance.shift - print_object->center_offset());
|
||||||
if (! intersection(convex_hulls_other, convex_hull).empty())
|
if (! intersection(convex_hulls_other, (Polygons)convex_hull).empty())
|
||||||
return false;
|
return false;
|
||||||
polygons_append(convex_hulls_other, convex_hull);
|
convex_hulls_other.emplace_back(std::move(convex_hull));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -1064,11 +1064,15 @@ void PrintConfigDef::init_fff_params()
|
||||||
def->mode = comAdvanced;
|
def->mode = comAdvanced;
|
||||||
def->set_default_value(new ConfigOptionInt(1));
|
def->set_default_value(new ConfigOptionInt(1));
|
||||||
|
|
||||||
def = this->add("infill_anchor", coFloatOrPercent);
|
auto def_infill_anchor_min = def = this->add("infill_anchor", coFloatOrPercent);
|
||||||
def->label = L("Length of the infill anchor");
|
def->label = L("Length of the infill anchor");
|
||||||
def->category = L("Advanced");
|
def->category = L("Advanced");
|
||||||
def->tooltip = L("Connect an infill line to an internal perimeter with a short segment of an additional perimeter. "
|
def->tooltip = L("Connect an infill line to an internal perimeter with a short segment of an additional perimeter. "
|
||||||
"If expressed as percentage (example: 15%) it is calculated over infill extrusion width.");
|
"If expressed as percentage (example: 15%) it is calculated over infill extrusion width. "
|
||||||
|
"PrusaSlicer tries to connect two close infill lines to a short perimeter segment. If no such perimeter segment "
|
||||||
|
"shorter than infill_anchor_max is found, the infill line is connected to a perimeter segment at just one side "
|
||||||
|
"and the length of the perimeter segment taken is limited to this parameter, but no longer than anchor_length_max. "
|
||||||
|
"Set this parameter to zero to disable anchoring perimeters connected to a single infill line.");
|
||||||
def->sidetext = L("mm or %");
|
def->sidetext = L("mm or %");
|
||||||
def->ratio_over = "infill_extrusion_width";
|
def->ratio_over = "infill_extrusion_width";
|
||||||
def->gui_type = "f_enum_open";
|
def->gui_type = "f_enum_open";
|
||||||
|
@ -1078,15 +1082,36 @@ void PrintConfigDef::init_fff_params()
|
||||||
def->enum_values.push_back("5");
|
def->enum_values.push_back("5");
|
||||||
def->enum_values.push_back("10");
|
def->enum_values.push_back("10");
|
||||||
def->enum_values.push_back("1000");
|
def->enum_values.push_back("1000");
|
||||||
def->enum_labels.push_back(L("0 (not anchored)"));
|
def->enum_labels.push_back(L("0 (no open anchors)"));
|
||||||
def->enum_labels.push_back("1 mm");
|
def->enum_labels.push_back("1 mm");
|
||||||
def->enum_labels.push_back("2 mm");
|
def->enum_labels.push_back("2 mm");
|
||||||
def->enum_labels.push_back("5 mm");
|
def->enum_labels.push_back("5 mm");
|
||||||
def->enum_labels.push_back("10 mm");
|
def->enum_labels.push_back("10 mm");
|
||||||
def->enum_labels.push_back(L("1000 (unlimited)"));
|
def->enum_labels.push_back(L("1000 (unlimited)"));
|
||||||
def->mode = comAdvanced;
|
def->mode = comAdvanced;
|
||||||
// def->set_default_value(new ConfigOptionFloatOrPercent(300, true));
|
def->set_default_value(new ConfigOptionFloatOrPercent(600, true));
|
||||||
def->set_default_value(new ConfigOptionFloatOrPercent(1000, false));
|
|
||||||
|
def = this->add("infill_anchor_max", coFloatOrPercent);
|
||||||
|
def->label = L("Maximum length of the infill anchor");
|
||||||
|
def->category = def_infill_anchor_min->category;
|
||||||
|
def->tooltip = L("Connect an infill line to an internal perimeter with a short segment of an additional perimeter. "
|
||||||
|
"If expressed as percentage (example: 15%) it is calculated over infill extrusion width. "
|
||||||
|
"PrusaSlicer tries to connect two close infill lines to a short perimeter segment. If no such perimeter segment "
|
||||||
|
"shorter than this parameter is found, the infill line is connected to a perimeter segment at just one side "
|
||||||
|
"and the length of the perimeter segment taken is limited to infill_anchor, but no longer than this parameter. "
|
||||||
|
"Set this parameter to zero to disable anchoring.");
|
||||||
|
def->sidetext = def_infill_anchor_min->sidetext;
|
||||||
|
def->ratio_over = def_infill_anchor_min->ratio_over;
|
||||||
|
def->gui_type = def_infill_anchor_min->gui_type;
|
||||||
|
def->enum_values = def_infill_anchor_min->enum_values;
|
||||||
|
def->enum_labels.push_back(L("0 (not anchored)"));
|
||||||
|
def->enum_labels.push_back("1 mm");
|
||||||
|
def->enum_labels.push_back("2 mm");
|
||||||
|
def->enum_labels.push_back("5 mm");
|
||||||
|
def->enum_labels.push_back("10 mm");
|
||||||
|
def->enum_labels.push_back(L("1000 (unlimited)"));
|
||||||
|
def->mode = def_infill_anchor_min->mode;
|
||||||
|
def->set_default_value(new ConfigOptionFloatOrPercent(50, false));
|
||||||
|
|
||||||
def = this->add("infill_extruder", coInt);
|
def = this->add("infill_extruder", coInt);
|
||||||
def->label = L("Infill extruder");
|
def->label = L("Infill extruder");
|
||||||
|
|
|
@ -532,6 +532,7 @@ public:
|
||||||
ConfigOptionEnum<InfillPattern> fill_pattern;
|
ConfigOptionEnum<InfillPattern> fill_pattern;
|
||||||
ConfigOptionFloat gap_fill_speed;
|
ConfigOptionFloat gap_fill_speed;
|
||||||
ConfigOptionFloatOrPercent infill_anchor;
|
ConfigOptionFloatOrPercent infill_anchor;
|
||||||
|
ConfigOptionFloatOrPercent infill_anchor_max;
|
||||||
ConfigOptionInt infill_extruder;
|
ConfigOptionInt infill_extruder;
|
||||||
ConfigOptionFloatOrPercent infill_extrusion_width;
|
ConfigOptionFloatOrPercent infill_extrusion_width;
|
||||||
ConfigOptionInt infill_every_layers;
|
ConfigOptionInt infill_every_layers;
|
||||||
|
@ -584,6 +585,7 @@ protected:
|
||||||
OPT_PTR(fill_pattern);
|
OPT_PTR(fill_pattern);
|
||||||
OPT_PTR(gap_fill_speed);
|
OPT_PTR(gap_fill_speed);
|
||||||
OPT_PTR(infill_anchor);
|
OPT_PTR(infill_anchor);
|
||||||
|
OPT_PTR(infill_anchor_max);
|
||||||
OPT_PTR(infill_extruder);
|
OPT_PTR(infill_extruder);
|
||||||
OPT_PTR(infill_extrusion_width);
|
OPT_PTR(infill_extrusion_width);
|
||||||
OPT_PTR(infill_every_layers);
|
OPT_PTR(infill_every_layers);
|
||||||
|
|
|
@ -591,6 +591,7 @@ bool PrintObject::invalidate_state_by_config_options(const std::vector<t_config_
|
||||||
|| opt_key == "fill_angle"
|
|| opt_key == "fill_angle"
|
||||||
|| opt_key == "fill_pattern"
|
|| opt_key == "fill_pattern"
|
||||||
|| opt_key == "infill_anchor"
|
|| opt_key == "infill_anchor"
|
||||||
|
|| opt_key == "infill_anchor_max"
|
||||||
|| opt_key == "top_infill_extrusion_width"
|
|| opt_key == "top_infill_extrusion_width"
|
||||||
|| opt_key == "first_layer_extrusion_width") {
|
|| opt_key == "first_layer_extrusion_width") {
|
||||||
steps.emplace_back(posInfill);
|
steps.emplace_back(posInfill);
|
||||||
|
|
|
@ -369,7 +369,7 @@ bool add_cavity(Contour3D &pad, ExPolygon &top_poly, const PadConfig3D &cfg,
|
||||||
|
|
||||||
if (inner_base.empty() || middle_base.empty()) { logerr(); return false; }
|
if (inner_base.empty() || middle_base.empty()) { logerr(); return false; }
|
||||||
|
|
||||||
ExPolygons pdiff = diff_ex(top_poly, middle_base.contour);
|
ExPolygons pdiff = diff_ex((Polygons)top_poly, (Polygons)middle_base.contour);
|
||||||
|
|
||||||
if (pdiff.size() != 1) { logerr(); return false; }
|
if (pdiff.size() != 1) { logerr(); return false; }
|
||||||
|
|
||||||
|
|
|
@ -2513,7 +2513,7 @@ void LoopInterfaceProcessor::generate(MyLayerExtruded &top_contact_layer, const
|
||||||
Polygon &contour = (i_contour == 0) ? it_contact_expoly->contour : it_contact_expoly->holes[i_contour - 1];
|
Polygon &contour = (i_contour == 0) ? it_contact_expoly->contour : it_contact_expoly->holes[i_contour - 1];
|
||||||
const Point *seg_current_pt = nullptr;
|
const Point *seg_current_pt = nullptr;
|
||||||
coordf_t seg_current_t = 0.;
|
coordf_t seg_current_t = 0.;
|
||||||
if (! intersection_pl(contour.split_at_first_point(), overhang_with_margin).empty()) {
|
if (! intersection_pl((Polylines)contour.split_at_first_point(), overhang_with_margin).empty()) {
|
||||||
// The contour is below the overhang at least to some extent.
|
// The contour is below the overhang at least to some extent.
|
||||||
//FIXME ideally one would place the circles below the overhang only.
|
//FIXME ideally one would place the circles below the overhang only.
|
||||||
// Walk around the contour and place circles so their centers are not closer than circle_distance from each other.
|
// Walk around the contour and place circles so their centers are not closer than circle_distance from each other.
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
#ifndef _prusaslicer_technologies_h_
|
#ifndef _prusaslicer_technologies_h_
|
||||||
#define _prusaslicer_technologies_h_
|
#define _prusaslicer_technologies_h_
|
||||||
|
|
||||||
//============
|
//=============
|
||||||
// debug techs
|
// debug techs
|
||||||
//============
|
//=============
|
||||||
|
|
||||||
// Shows camera target in the 3D scene
|
// Shows camera target in the 3D scene
|
||||||
#define ENABLE_SHOW_CAMERA_TARGET 0
|
#define ENABLE_SHOW_CAMERA_TARGET 0
|
||||||
|
@ -23,20 +23,24 @@
|
||||||
#define DISABLE_INSTANCES_SYNCH 0
|
#define DISABLE_INSTANCES_SYNCH 0
|
||||||
// Use wxDataViewRender instead of wxDataViewCustomRenderer
|
// Use wxDataViewRender instead of wxDataViewCustomRenderer
|
||||||
#define ENABLE_NONCUSTOM_DATA_VIEW_RENDERING 0
|
#define ENABLE_NONCUSTOM_DATA_VIEW_RENDERING 0
|
||||||
|
// Enable G-Code viewer statistics imgui dialog
|
||||||
|
#define ENABLE_GCODE_VIEWER_STATISTICS 0
|
||||||
|
// Enable G-Code viewer comparison between toolpaths height and width detected from gcode and calculated at gcode generation
|
||||||
|
#define ENABLE_GCODE_VIEWER_DATA_CHECKING 0
|
||||||
|
|
||||||
|
|
||||||
//================
|
//=================
|
||||||
// 2.2.0.rc1 techs
|
// 2.2.0.rc1 techs
|
||||||
//================
|
//=================
|
||||||
#define ENABLE_2_2_0_RC1 1
|
#define ENABLE_2_2_0_RC1 1
|
||||||
|
|
||||||
// Enable hack to remove crash when closing on OSX 10.9.5
|
// Enable hack to remove crash when closing on OSX 10.9.5
|
||||||
#define ENABLE_HACK_CLOSING_ON_OSX_10_9_5 (1 && ENABLE_2_2_0_RC1)
|
#define ENABLE_HACK_CLOSING_ON_OSX_10_9_5 (1 && ENABLE_2_2_0_RC1)
|
||||||
|
|
||||||
|
|
||||||
//===================
|
//====================
|
||||||
// 2.3.0.alpha1 techs
|
// 2.3.0.alpha1 techs
|
||||||
//===================
|
//====================
|
||||||
#define ENABLE_2_3_0_ALPHA1 1
|
#define ENABLE_2_3_0_ALPHA1 1
|
||||||
|
|
||||||
// Enable rendering of objects using environment map
|
// Enable rendering of objects using environment map
|
||||||
|
@ -51,27 +55,22 @@
|
||||||
// Enable built-in DPI changed event handler of wxWidgets 3.1.3
|
// Enable built-in DPI changed event handler of wxWidgets 3.1.3
|
||||||
#define ENABLE_WX_3_1_3_DPI_CHANGED_EVENT (1 && ENABLE_2_3_0_ALPHA1)
|
#define ENABLE_WX_3_1_3_DPI_CHANGED_EVENT (1 && ENABLE_2_3_0_ALPHA1)
|
||||||
|
|
||||||
// Enable G-Code viewer
|
|
||||||
#define ENABLE_GCODE_VIEWER (1 && ENABLE_2_3_0_ALPHA1)
|
|
||||||
#define ENABLE_GCODE_VIEWER_STATISTICS (0 && ENABLE_GCODE_VIEWER)
|
|
||||||
#define ENABLE_GCODE_VIEWER_DATA_CHECKING (0 && ENABLE_GCODE_VIEWER)
|
|
||||||
|
|
||||||
|
//====================
|
||||||
//===================
|
|
||||||
// 2.3.0.alpha3 techs
|
// 2.3.0.alpha3 techs
|
||||||
//===================
|
//====================
|
||||||
#define ENABLE_2_3_0_ALPHA3 1
|
#define ENABLE_2_3_0_ALPHA3 1
|
||||||
|
|
||||||
#define ENABLE_CTRL_M_ON_WINDOWS (0 && ENABLE_2_3_0_ALPHA3)
|
#define ENABLE_CTRL_M_ON_WINDOWS (0 && ENABLE_2_3_0_ALPHA3)
|
||||||
|
|
||||||
|
|
||||||
//===================
|
//====================
|
||||||
// 2.3.0.alpha4 techs
|
// 2.3.0.alpha4 techs
|
||||||
//===================
|
//====================
|
||||||
#define ENABLE_2_3_0_ALPHA4 1
|
#define ENABLE_2_3_0_ALPHA4 1
|
||||||
|
|
||||||
#define ENABLE_FIXED_SCREEN_SIZE_POINT_MARKERS (1 && ENABLE_GCODE_VIEWER && ENABLE_2_3_0_ALPHA4)
|
#define ENABLE_FIXED_SCREEN_SIZE_POINT_MARKERS (1 && ENABLE_2_3_0_ALPHA4)
|
||||||
#define ENABLE_SHOW_OPTION_POINT_LAYERS (1 && ENABLE_GCODE_VIEWER && ENABLE_2_3_0_ALPHA4)
|
#define ENABLE_SHOW_OPTION_POINT_LAYERS (1 && ENABLE_2_3_0_ALPHA4)
|
||||||
|
|
||||||
|
|
||||||
//===================
|
//===================
|
||||||
|
@ -79,7 +78,8 @@
|
||||||
//===================
|
//===================
|
||||||
#define ENABLE_2_3_0_BETA1 1
|
#define ENABLE_2_3_0_BETA1 1
|
||||||
|
|
||||||
#define ENABLE_SHOW_WIPE_MOVES (1 && ENABLE_GCODE_VIEWER && ENABLE_2_3_0_BETA1)
|
#define ENABLE_SHOW_WIPE_MOVES (1 && ENABLE_2_3_0_BETA1)
|
||||||
#define ENABLE_DRAG_AND_DROP_FIX (1 && ENABLE_2_3_0_BETA1)
|
#define ENABLE_DRAG_AND_DROP_FIX (1 && ENABLE_2_3_0_BETA1)
|
||||||
|
|
||||||
|
|
||||||
#endif // _prusaslicer_technologies_h_
|
#endif // _prusaslicer_technologies_h_
|
||||||
|
|
|
@ -87,7 +87,7 @@ void Bed_2D::repaint(const std::vector<Vec2d>& shape)
|
||||||
for (auto y = bb.min(1) - fmod(bb.min(1), step) + step; y < bb.max(1); y += step) {
|
for (auto y = bb.min(1) - fmod(bb.min(1), step) + step; y < bb.max(1); y += step) {
|
||||||
polylines.push_back(Polyline::new_scale({ Vec2d(bb.min(0), y), Vec2d(bb.max(0), y) }));
|
polylines.push_back(Polyline::new_scale({ Vec2d(bb.min(0), y), Vec2d(bb.max(0), y) }));
|
||||||
}
|
}
|
||||||
polylines = intersection_pl(polylines, bed_polygon);
|
polylines = intersection_pl(polylines, (Polygons)bed_polygon);
|
||||||
|
|
||||||
dc.SetPen(wxPen(wxColour(230, 230, 230), 1, wxPENSTYLE_SOLID));
|
dc.SetPen(wxPen(wxColour(230, 230, 230), 1, wxPENSTYLE_SOLID));
|
||||||
for (auto pl : polylines)
|
for (auto pl : polylines)
|
||||||
|
|
|
@ -237,8 +237,11 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config)
|
||||||
bool have_infill = config->option<ConfigOptionPercent>("fill_density")->value > 0;
|
bool have_infill = config->option<ConfigOptionPercent>("fill_density")->value > 0;
|
||||||
// infill_extruder uses the same logic as in Print::extruders()
|
// infill_extruder uses the same logic as in Print::extruders()
|
||||||
for (auto el : { "fill_pattern", "infill_every_layers", "infill_only_where_needed",
|
for (auto el : { "fill_pattern", "infill_every_layers", "infill_only_where_needed",
|
||||||
"solid_infill_every_layers", "solid_infill_below_area", "infill_extruder" })
|
"solid_infill_every_layers", "solid_infill_below_area", "infill_extruder", "infill_anchor_max" })
|
||||||
toggle_field(el, have_infill);
|
toggle_field(el, have_infill);
|
||||||
|
// Only allow configuration of open anchors if the anchoring is enabled.
|
||||||
|
bool has_infill_anchors = have_infill && config->option<ConfigOptionFloatOrPercent>("infill_anchor_max")->value > 0;
|
||||||
|
toggle_field("infill_anchor", has_infill_anchors);
|
||||||
|
|
||||||
bool has_spiral_vase = config->opt_bool("spiral_vase");
|
bool has_spiral_vase = config->opt_bool("spiral_vase");
|
||||||
bool has_top_solid_infill = config->opt_int("top_solid_layers") > 0;
|
bool has_top_solid_infill = config->opt_int("top_solid_layers") > 0;
|
||||||
|
|
|
@ -569,7 +569,7 @@ void Control::draw_tick_on_mouse_position(wxDC& dc)
|
||||||
|
|
||||||
if (m_extra_style & wxSL_VALUE_LABEL) {
|
if (m_extra_style & wxSL_VALUE_LABEL) {
|
||||||
wxColour old_clr = dc.GetTextForeground();
|
wxColour old_clr = dc.GetTextForeground();
|
||||||
dc.SetTextForeground(LIGHT_GREY_PEN.GetColour());
|
dc.SetTextForeground(GREY_PEN.GetColour());
|
||||||
draw_tick_text(dc, pos, tick, ltEstimatedTime, false);
|
draw_tick_text(dc, pos, tick, ltEstimatedTime, false);
|
||||||
dc.SetTextForeground(old_clr);
|
dc.SetTextForeground(old_clr);
|
||||||
}
|
}
|
||||||
|
@ -893,6 +893,11 @@ void Control::Ruler::update(wxWindow* win, const std::vector<double>& values, do
|
||||||
int DPI = GUI::get_dpi_for_window(win);
|
int DPI = GUI::get_dpi_for_window(win);
|
||||||
int pixels_per_sm = lround((double)(DPI) * 5.0/25.4);
|
int pixels_per_sm = lround((double)(DPI) * 5.0/25.4);
|
||||||
|
|
||||||
|
if (lround(scroll_step) > pixels_per_sm) {
|
||||||
|
long_step = -1.0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
int pow = -2;
|
int pow = -2;
|
||||||
int step = 0;
|
int step = 0;
|
||||||
auto end_it = count == 1 ? values.end() : values.begin() + lround(values.size() / count);
|
auto end_it = count == 1 ? values.end() : values.begin() + lround(values.size() / count);
|
||||||
|
@ -907,15 +912,15 @@ void Control::Ruler::update(wxWindow* win, const std::vector<double>& values, do
|
||||||
break;
|
break;
|
||||||
int tick = val_it - values.begin();
|
int tick = val_it - values.begin();
|
||||||
|
|
||||||
if (lround(tick * scroll_step) > pixels_per_sm) {
|
|
||||||
step = istep;
|
|
||||||
|
|
||||||
// find next tick with istep
|
// find next tick with istep
|
||||||
val *= 2;
|
val *= 2;
|
||||||
val_it = std::lower_bound(values.begin(), end_it, val - epsilon());
|
val_it = std::lower_bound(values.begin(), end_it, val - epsilon());
|
||||||
// count of short ticks between ticks
|
// count of short ticks between ticks
|
||||||
int short_ticks_cnt = val_it == values.end() ? tick : val_it - values.begin() - tick;
|
int short_ticks_cnt = val_it == values.end() ? tick : val_it - values.begin() - tick;
|
||||||
// there couldn't be more then 10 short ticks between thicks
|
|
||||||
|
if (lround(short_ticks_cnt * scroll_step) > pixels_per_sm) {
|
||||||
|
step = istep;
|
||||||
|
// there couldn't be more then 10 short ticks between ticks
|
||||||
short_step = 0.1 * short_ticks_cnt;
|
short_step = 0.1 * short_ticks_cnt;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -931,13 +936,22 @@ void Control::Ruler::update(wxWindow* win, const std::vector<double>& values, do
|
||||||
void Control::draw_ruler(wxDC& dc)
|
void Control::draw_ruler(wxDC& dc)
|
||||||
{
|
{
|
||||||
m_ruler.update(this->GetParent(), m_values, get_scroll_step());
|
m_ruler.update(this->GetParent(), m_values, get_scroll_step());
|
||||||
if (!m_ruler.is_ok())
|
|
||||||
return;
|
|
||||||
|
|
||||||
int height, width;
|
int height, width;
|
||||||
get_size(&width, &height);
|
get_size(&width, &height);
|
||||||
const wxCoord mid = is_horizontal() ? 0.5 * height : 0.5 * width;
|
const wxCoord mid = is_horizontal() ? 0.5 * height : 0.5 * width;
|
||||||
|
|
||||||
|
dc.SetPen(GREY_PEN);
|
||||||
|
wxColour old_clr = dc.GetTextForeground();
|
||||||
|
dc.SetTextForeground(GREY_PEN.GetColour());
|
||||||
|
|
||||||
|
if (m_ruler.long_step < 0)
|
||||||
|
for (int tick = 1; tick < m_values.size(); tick++) {
|
||||||
|
wxCoord pos = get_position_from_value(tick);
|
||||||
|
draw_ticks_pair(dc, pos, mid, 5);
|
||||||
|
draw_tick_text(dc, wxPoint(mid, pos), tick);
|
||||||
|
}
|
||||||
|
else {
|
||||||
auto draw_short_ticks = [this, mid](wxDC& dc, double& current_tick, int max_tick) {
|
auto draw_short_ticks = [this, mid](wxDC& dc, double& current_tick, int max_tick) {
|
||||||
while (current_tick < max_tick) {
|
while (current_tick < max_tick) {
|
||||||
wxCoord pos = get_position_from_value(lround(current_tick));
|
wxCoord pos = get_position_from_value(lround(current_tick));
|
||||||
|
@ -948,10 +962,6 @@ void Control::draw_ruler(wxDC& dc)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
dc.SetPen(LIGHT_GREY_PEN);
|
|
||||||
wxColour old_clr = dc.GetTextForeground();
|
|
||||||
dc.SetTextForeground(LIGHT_GREY_PEN.GetColour());
|
|
||||||
|
|
||||||
double short_tick;
|
double short_tick;
|
||||||
int tick = 0;
|
int tick = 0;
|
||||||
double value = 0.0;
|
double value = 0.0;
|
||||||
|
@ -996,6 +1006,7 @@ void Control::draw_ruler(wxDC& dc)
|
||||||
}
|
}
|
||||||
// short ticks from the last tick to the end
|
// short ticks from the last tick to the end
|
||||||
draw_short_ticks(dc, short_tick, m_max_value);
|
draw_short_ticks(dc, short_tick, m_max_value);
|
||||||
|
}
|
||||||
|
|
||||||
dc.SetTextForeground(old_clr);
|
dc.SetTextForeground(old_clr);
|
||||||
}
|
}
|
||||||
|
|
|
@ -862,7 +862,19 @@ void Choice::BUILD() {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// temp->Bind(wxEVT_TEXT, ([this](wxCommandEvent e) { on_change_field(); }), temp->GetId());
|
// temp->Bind(wxEVT_TEXT, ([this](wxCommandEvent e) { on_change_field(); }), temp->GetId());
|
||||||
temp->Bind(wxEVT_COMBOBOX, ([this](wxCommandEvent e) { on_change_field(); }), temp->GetId());
|
temp->Bind(wxEVT_COMBOBOX_DROPDOWN, [this](wxCommandEvent&) { m_is_dropped = true; });
|
||||||
|
temp->Bind(wxEVT_COMBOBOX_CLOSEUP, [this](wxCommandEvent&) { m_is_dropped = false; });
|
||||||
|
|
||||||
|
temp->Bind(wxEVT_COMBOBOX, ([this, temp](wxCommandEvent evt) {
|
||||||
|
if (m_suppress_scroll) {
|
||||||
|
if (!m_is_dropped) {
|
||||||
|
temp->SetSelection(m_last_selected);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_last_selected = evt.GetSelection();
|
||||||
|
}
|
||||||
|
on_change_field();
|
||||||
|
}), temp->GetId());
|
||||||
|
|
||||||
if (m_is_editable) {
|
if (m_is_editable) {
|
||||||
temp->Bind(wxEVT_KILL_FOCUS, ([this](wxEvent& e) {
|
temp->Bind(wxEVT_KILL_FOCUS, ([this](wxEvent& e) {
|
||||||
|
@ -876,7 +888,6 @@ void Choice::BUILD() {
|
||||||
if (is_defined_input_value<choice_ctrl>(window, m_opt.type)) {
|
if (is_defined_input_value<choice_ctrl>(window, m_opt.type)) {
|
||||||
if (fabs(old_val - boost::any_cast<double>(get_value())) <= 0.0001)
|
if (fabs(old_val - boost::any_cast<double>(get_value())) <= 0.0001)
|
||||||
return;
|
return;
|
||||||
else
|
|
||||||
on_change_field();
|
on_change_field();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -887,6 +898,13 @@ void Choice::BUILD() {
|
||||||
temp->SetToolTip(get_tooltip_text(temp->GetValue()));
|
temp->SetToolTip(get_tooltip_text(temp->GetValue()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Choice::suppress_scroll()
|
||||||
|
{
|
||||||
|
m_suppress_scroll = true;
|
||||||
|
choice_ctrl* ctrl = dynamic_cast<choice_ctrl*>(window);
|
||||||
|
m_last_selected = ctrl->GetSelection();
|
||||||
|
}
|
||||||
|
|
||||||
void Choice::set_selection()
|
void Choice::set_selection()
|
||||||
{
|
{
|
||||||
/* To prevent earlier control updating under OSX set m_disable_change_event to true
|
/* To prevent earlier control updating under OSX set m_disable_change_event to true
|
||||||
|
@ -901,6 +919,7 @@ void Choice::set_selection()
|
||||||
case coEnum:{
|
case coEnum:{
|
||||||
int id_value = m_opt.get_default_value<ConfigOptionEnum<SeamPosition>>()->value; //!!
|
int id_value = m_opt.get_default_value<ConfigOptionEnum<SeamPosition>>()->value; //!!
|
||||||
field->SetSelection(id_value);
|
field->SetSelection(id_value);
|
||||||
|
if (m_suppress_scroll) m_last_selected = id_value;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case coFloat:
|
case coFloat:
|
||||||
|
@ -934,6 +953,8 @@ void Choice::set_selection()
|
||||||
++idx;
|
++idx;
|
||||||
}
|
}
|
||||||
idx == m_opt.enum_values.size() ? field->SetValue(text_value) : field->SetSelection(idx);
|
idx == m_opt.enum_values.size() ? field->SetValue(text_value) : field->SetSelection(idx);
|
||||||
|
|
||||||
|
if (m_suppress_scroll && idx < m_opt.enum_values.size()) m_last_selected = idx;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -953,6 +974,7 @@ void Choice::set_value(const std::string& value, bool change_event) //! Redunda
|
||||||
idx == m_opt.enum_values.size() ?
|
idx == m_opt.enum_values.size() ?
|
||||||
field->SetValue(value) :
|
field->SetValue(value) :
|
||||||
field->SetSelection(idx);
|
field->SetSelection(idx);
|
||||||
|
if (m_suppress_scroll && idx < m_opt.enum_values.size()) m_last_selected = idx;
|
||||||
|
|
||||||
m_disable_change_event = false;
|
m_disable_change_event = false;
|
||||||
}
|
}
|
||||||
|
@ -990,6 +1012,7 @@ void Choice::set_value(const boost::any& value, bool change_event)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
field->SetSelection(idx);
|
field->SetSelection(idx);
|
||||||
|
if (m_suppress_scroll && idx < m_opt.enum_values.size()) m_last_selected = idx;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case coEnum: {
|
case coEnum: {
|
||||||
|
@ -1020,6 +1043,7 @@ void Choice::set_value(const boost::any& value, bool change_event)
|
||||||
val = 0;
|
val = 0;
|
||||||
}
|
}
|
||||||
field->SetSelection(val);
|
field->SetSelection(val);
|
||||||
|
if (m_suppress_scroll) m_last_selected = val;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
@ -1179,6 +1203,7 @@ void Choice::msw_rescale()
|
||||||
idx == m_opt.enum_values.size() ?
|
idx == m_opt.enum_values.size() ?
|
||||||
field->SetValue(selection) :
|
field->SetValue(selection) :
|
||||||
field->SetSelection(idx);
|
field->SetSelection(idx);
|
||||||
|
if (m_suppress_scroll && idx < m_opt.enum_values.size()) m_last_selected = idx;
|
||||||
#else
|
#else
|
||||||
auto size = wxSize(def_width_wider() * m_em_unit, wxDefaultCoord);
|
auto size = wxSize(def_width_wider() * m_em_unit, wxDefaultCoord);
|
||||||
if (m_opt.height >= 0) size.SetHeight(m_opt.height * m_em_unit);
|
if (m_opt.height >= 0) size.SetHeight(m_opt.height * m_em_unit);
|
||||||
|
|
|
@ -386,6 +386,9 @@ public:
|
||||||
* so let use a flag, which has TRUE value for a control without wxCB_READONLY style
|
* so let use a flag, which has TRUE value for a control without wxCB_READONLY style
|
||||||
*/
|
*/
|
||||||
bool m_is_editable { false };
|
bool m_is_editable { false };
|
||||||
|
bool m_is_dropped { false };
|
||||||
|
bool m_suppress_scroll { false };
|
||||||
|
int m_last_selected { wxNOT_FOUND };
|
||||||
|
|
||||||
void set_selection();
|
void set_selection();
|
||||||
void set_value(const std::string& value, bool change_event = false);
|
void set_value(const std::string& value, bool change_event = false);
|
||||||
|
@ -399,6 +402,8 @@ public:
|
||||||
void enable() override ;//{ dynamic_cast<wxBitmapComboBox*>(window)->Enable(); };
|
void enable() override ;//{ dynamic_cast<wxBitmapComboBox*>(window)->Enable(); };
|
||||||
void disable() override;//{ dynamic_cast<wxBitmapComboBox*>(window)->Disable(); };
|
void disable() override;//{ dynamic_cast<wxBitmapComboBox*>(window)->Disable(); };
|
||||||
wxWindow* getWindow() override { return window; }
|
wxWindow* getWindow() override { return window; }
|
||||||
|
|
||||||
|
void suppress_scroll();
|
||||||
};
|
};
|
||||||
|
|
||||||
class ColourPicker : public Field {
|
class ColourPicker : public Field {
|
||||||
|
|
|
@ -3892,7 +3892,7 @@ bool GLCanvas3D::_render_arrange_menu(float pos_x)
|
||||||
settings_changed = true;
|
settings_changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (imgui->checkbox(_(L("Enable rotations")), settings.enable_rotation)) {
|
if (imgui->checkbox(_(L("Enable rotations (slow)")), settings.enable_rotation)) {
|
||||||
m_arrange_settings.enable_rotation = settings.enable_rotation;
|
m_arrange_settings.enable_rotation = settings.enable_rotation;
|
||||||
settings_changed = true;
|
settings_changed = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -158,14 +158,14 @@ void ArrangeJob::process()
|
||||||
params.stopcondition = [this]() { return was_canceled(); };
|
params.stopcondition = [this]() { return was_canceled(); };
|
||||||
|
|
||||||
try {
|
try {
|
||||||
params.progressind = [this, count](unsigned st) {
|
params.progressind = [this, count](unsigned st, unsigned) {
|
||||||
st += m_unprintable.size();
|
st += m_unprintable.size();
|
||||||
if (st > 0) update_status(int(count - st), arrangestr);
|
if (st > 0) update_status(int(count - st), arrangestr);
|
||||||
};
|
};
|
||||||
|
|
||||||
arrangement::arrange(m_selected, m_unselected, bedpts, params);
|
arrangement::arrange(m_selected, m_unselected, bedpts, params);
|
||||||
|
|
||||||
params.progressind = [this, count](unsigned st) {
|
params.progressind = [this, count](unsigned st, unsigned) {
|
||||||
if (st > 0) update_status(int(count - st), arrangestr);
|
if (st > 0) update_status(int(count - st), arrangestr);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -90,9 +90,13 @@ void FillBedJob::process()
|
||||||
params.min_obj_distance = scaled(settings.distance);
|
params.min_obj_distance = scaled(settings.distance);
|
||||||
params.allow_rotations = settings.enable_rotation;
|
params.allow_rotations = settings.enable_rotation;
|
||||||
|
|
||||||
params.stopcondition = [this]() { return was_canceled(); };
|
unsigned curr_bed = 0;
|
||||||
|
params.stopcondition = [this, &curr_bed]() {
|
||||||
|
return was_canceled() || curr_bed > 0;
|
||||||
|
};
|
||||||
|
|
||||||
params.progressind = [this](unsigned st) {
|
params.progressind = [this, &curr_bed](unsigned st, unsigned bed) {
|
||||||
|
curr_bed = bed;
|
||||||
if (st > 0)
|
if (st > 0)
|
||||||
update_status(int(m_status_range - st), _(L("Filling bed")));
|
update_status(int(m_status_range - st), _(L("Filling bed")));
|
||||||
};
|
};
|
||||||
|
|
|
@ -439,6 +439,9 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent) :
|
||||||
|
|
||||||
m_og->activate();
|
m_og->activate();
|
||||||
|
|
||||||
|
Choice* choice = dynamic_cast<Choice*>(m_og->get_field("support"));
|
||||||
|
choice->suppress_scroll();
|
||||||
|
|
||||||
// Frequently changed parameters for SLA_technology
|
// Frequently changed parameters for SLA_technology
|
||||||
m_og_sla = std::make_shared<ConfigOptionsGroup>(parent, "");
|
m_og_sla = std::make_shared<ConfigOptionsGroup>(parent, "");
|
||||||
m_og_sla->hide_labels();
|
m_og_sla->hide_labels();
|
||||||
|
|
|
@ -256,8 +256,8 @@ void PreferencesDialog::build()
|
||||||
create_settings_mode_widget();
|
create_settings_mode_widget();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_editor) {
|
|
||||||
#if ENABLE_ENVIRONMENT_MAP
|
#if ENABLE_ENVIRONMENT_MAP
|
||||||
|
if (is_editor) {
|
||||||
m_optgroup_render = std::make_shared<ConfigOptionsGroup>(this, _L("Render"));
|
m_optgroup_render = std::make_shared<ConfigOptionsGroup>(this, _L("Render"));
|
||||||
m_optgroup_render->label_width = 40;
|
m_optgroup_render->label_width = 40;
|
||||||
m_optgroup_render->m_on_change = [this](t_config_option_key opt_key, boost::any value) {
|
m_optgroup_render->m_on_change = [this](t_config_option_key opt_key, boost::any value) {
|
||||||
|
@ -272,8 +272,8 @@ void PreferencesDialog::build()
|
||||||
m_optgroup_render->append_single_option_line(option);
|
m_optgroup_render->append_single_option_line(option);
|
||||||
|
|
||||||
m_optgroup_render->activate();
|
m_optgroup_render->activate();
|
||||||
#endif // ENABLE_ENVIRONMENT_MAP
|
|
||||||
}
|
}
|
||||||
|
#endif // ENABLE_ENVIRONMENT_MAP
|
||||||
|
|
||||||
auto sizer = new wxBoxSizer(wxVERTICAL);
|
auto sizer = new wxBoxSizer(wxVERTICAL);
|
||||||
sizer->Add(m_optgroup_general->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10);
|
sizer->Add(m_optgroup_general->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10);
|
||||||
|
@ -293,13 +293,13 @@ void PreferencesDialog::build()
|
||||||
|
|
||||||
SetSizer(sizer);
|
SetSizer(sizer);
|
||||||
sizer->SetSizeHints(this);
|
sizer->SetSizeHints(this);
|
||||||
|
this->CenterOnParent();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PreferencesDialog::accept()
|
void PreferencesDialog::accept()
|
||||||
{
|
{
|
||||||
if (m_values.find("no_defaults") != m_values.end()) {
|
if (m_values.find("no_defaults") != m_values.end())
|
||||||
warning_catcher(this, wxString::Format(_L("You need to restart %s to make the changes effective."), SLIC3R_APP_NAME));
|
warning_catcher(this, wxString::Format(_L("You need to restart %s to make the changes effective."), SLIC3R_APP_NAME));
|
||||||
}
|
|
||||||
|
|
||||||
auto app_config = get_app_config();
|
auto app_config = get_app_config();
|
||||||
|
|
||||||
|
@ -319,8 +319,7 @@ void PreferencesDialog::accept()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const std::string& key : {"default_action_on_close_application", "default_action_on_select_preset"})
|
for (const std::string& key : {"default_action_on_close_application", "default_action_on_select_preset"}) {
|
||||||
{
|
|
||||||
auto it = m_values.find(key);
|
auto it = m_values.find(key);
|
||||||
if (it != m_values.end() && it->second != "none" && app_config->get(key) != "none")
|
if (it != m_values.end() && it->second != "none" && app_config->get(key) != "none")
|
||||||
m_values.erase(it); // we shouldn't change value, if some of those parameters was selected, and then deselected
|
m_values.erase(it); // we shouldn't change value, if some of those parameters was selected, and then deselected
|
||||||
|
@ -432,14 +431,13 @@ void PreferencesDialog::create_settings_mode_widget()
|
||||||
|
|
||||||
wxWindow* parent = m_optgroup_gui->ctrl_parent();
|
wxWindow* parent = m_optgroup_gui->ctrl_parent();
|
||||||
|
|
||||||
m_layout_mode_box = new wxRadioBox(parent, wxID_ANY, _L("Layout Options"), wxDefaultPosition, wxDefaultSize, WXSIZEOF(choices), choices,
|
m_layout_mode_box = new wxRadioBox(parent, wxID_ANY, _L("Layout Options"), wxDefaultPosition, wxDefaultSize,
|
||||||
3, wxRA_SPECIFY_ROWS);
|
WXSIZEOF(choices), choices, 3, wxRA_SPECIFY_ROWS);
|
||||||
m_layout_mode_box->SetFont(wxGetApp().normal_font());
|
m_layout_mode_box->SetFont(wxGetApp().normal_font());
|
||||||
m_layout_mode_box->SetSelection(selection);
|
m_layout_mode_box->SetSelection(selection);
|
||||||
|
|
||||||
m_layout_mode_box->Bind(wxEVT_RADIOBOX, [this](wxCommandEvent& e) {
|
m_layout_mode_box->Bind(wxEVT_RADIOBOX, [this](wxCommandEvent& e) {
|
||||||
int selection = e.GetSelection();
|
int selection = e.GetSelection();
|
||||||
|
|
||||||
m_values["old_settings_layout_mode"] = boost::any_cast<bool>(selection == 0) ? "1" : "0";
|
m_values["old_settings_layout_mode"] = boost::any_cast<bool>(selection == 0) ? "1" : "0";
|
||||||
m_values["new_settings_layout_mode"] = boost::any_cast<bool>(selection == 1) ? "1" : "0";
|
m_values["new_settings_layout_mode"] = boost::any_cast<bool>(selection == 1) ? "1" : "0";
|
||||||
m_values["dlg_settings_layout_mode"] = boost::any_cast<bool>(selection == 2) ? "1" : "0";
|
m_values["dlg_settings_layout_mode"] = boost::any_cast<bool>(selection == 2) ? "1" : "0";
|
||||||
|
@ -447,7 +445,6 @@ void PreferencesDialog::create_settings_mode_widget()
|
||||||
|
|
||||||
auto sizer = new wxBoxSizer(wxHORIZONTAL);
|
auto sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||||
sizer->Add(m_layout_mode_box, 1, wxALIGN_CENTER_VERTICAL);
|
sizer->Add(m_layout_mode_box, 1, wxALIGN_CENTER_VERTICAL);
|
||||||
|
|
||||||
m_optgroup_gui->sizer->Add(sizer, 0, wxEXPAND);
|
m_optgroup_gui->sizer->Add(sizer, 0, wxEXPAND);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -103,6 +103,8 @@ PresetComboBox::PresetComboBox(wxWindow* parent, Preset::Type preset_type, const
|
||||||
|
|
||||||
// parameters for an icon's drawing
|
// parameters for an icon's drawing
|
||||||
fill_width_height();
|
fill_width_height();
|
||||||
|
Bind(wxEVT_COMBOBOX_DROPDOWN, [this](wxCommandEvent& evt) { m_suppress_change = false; });
|
||||||
|
Bind(wxEVT_COMBOBOX_CLOSEUP, [this](wxCommandEvent& evt) { m_suppress_change = true ; });
|
||||||
|
|
||||||
Bind(wxEVT_COMBOBOX, [this](wxCommandEvent& evt) {
|
Bind(wxEVT_COMBOBOX, [this](wxCommandEvent& evt) {
|
||||||
// see https://github.com/prusa3d/PrusaSlicer/issues/3889
|
// see https://github.com/prusa3d/PrusaSlicer/issues/3889
|
||||||
|
@ -147,6 +149,15 @@ bool PresetComboBox::set_printer_technology(PrinterTechnology pt)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool PresetComboBox::check_event_for_suppress_change(wxCommandEvent& evt)
|
||||||
|
{
|
||||||
|
if (m_suppress_change) {
|
||||||
|
evt.StopPropagation();
|
||||||
|
SetSelection(m_last_selected);
|
||||||
|
}
|
||||||
|
return m_suppress_change;
|
||||||
|
}
|
||||||
|
|
||||||
void PresetComboBox::invalidate_selection()
|
void PresetComboBox::invalidate_selection()
|
||||||
{
|
{
|
||||||
m_last_selected = INT_MAX; // this value means that no one item is selected
|
m_last_selected = INT_MAX; // this value means that no one item is selected
|
||||||
|
@ -534,6 +545,8 @@ PlaterPresetComboBox::PlaterPresetComboBox(wxWindow *parent, Preset::Type preset
|
||||||
PresetComboBox(parent, preset_type, wxSize(15 * wxGetApp().em_unit(), -1))
|
PresetComboBox(parent, preset_type, wxSize(15 * wxGetApp().em_unit(), -1))
|
||||||
{
|
{
|
||||||
Bind(wxEVT_COMBOBOX, [this](wxCommandEvent &evt) {
|
Bind(wxEVT_COMBOBOX, [this](wxCommandEvent &evt) {
|
||||||
|
if (check_event_for_suppress_change(evt))
|
||||||
|
return;
|
||||||
auto selected_item = evt.GetSelection();
|
auto selected_item = evt.GetSelection();
|
||||||
|
|
||||||
auto marker = reinterpret_cast<Marker>(this->GetClientData(selected_item));
|
auto marker = reinterpret_cast<Marker>(this->GetClientData(selected_item));
|
||||||
|
@ -871,6 +884,8 @@ TabPresetComboBox::TabPresetComboBox(wxWindow* parent, Preset::Type preset_type)
|
||||||
PresetComboBox(parent, preset_type, wxSize(35 * wxGetApp().em_unit(), -1))
|
PresetComboBox(parent, preset_type, wxSize(35 * wxGetApp().em_unit(), -1))
|
||||||
{
|
{
|
||||||
Bind(wxEVT_COMBOBOX, [this](wxCommandEvent& evt) {
|
Bind(wxEVT_COMBOBOX, [this](wxCommandEvent& evt) {
|
||||||
|
if (check_event_for_suppress_change(evt))
|
||||||
|
return;
|
||||||
// see https://github.com/prusa3d/PrusaSlicer/issues/3889
|
// see https://github.com/prusa3d/PrusaSlicer/issues/3889
|
||||||
// Under OSX: in case of use of a same names written in different case (like "ENDER" and "Ender")
|
// Under OSX: in case of use of a same names written in different case (like "ENDER" and "Ender")
|
||||||
// m_presets_choice->GetSelection() will return first item, because search in PopupListCtrl is case-insensitive.
|
// m_presets_choice->GetSelection() will return first item, because search in PopupListCtrl is case-insensitive.
|
||||||
|
|
|
@ -86,6 +86,7 @@ protected:
|
||||||
|
|
||||||
int m_last_selected;
|
int m_last_selected;
|
||||||
int m_em_unit;
|
int m_em_unit;
|
||||||
|
bool m_suppress_change { true };
|
||||||
|
|
||||||
// parameters for an icon's drawing
|
// parameters for an icon's drawing
|
||||||
int icon_height;
|
int icon_height;
|
||||||
|
@ -98,6 +99,7 @@ protected:
|
||||||
|
|
||||||
PrinterTechnology printer_technology {ptAny};
|
PrinterTechnology printer_technology {ptAny};
|
||||||
|
|
||||||
|
bool check_event_for_suppress_change(wxCommandEvent& evt);
|
||||||
void invalidate_selection();
|
void invalidate_selection();
|
||||||
void validate_selection(bool predicate = false);
|
void validate_selection(bool predicate = false);
|
||||||
void update_selection();
|
void update_selection();
|
||||||
|
|
|
@ -1423,6 +1423,7 @@ void TabPrint::build()
|
||||||
optgroup->append_single_option_line("fill_density", category_path + "fill-density");
|
optgroup->append_single_option_line("fill_density", category_path + "fill-density");
|
||||||
optgroup->append_single_option_line("fill_pattern", category_path + "fill-pattern");
|
optgroup->append_single_option_line("fill_pattern", category_path + "fill-pattern");
|
||||||
optgroup->append_single_option_line("infill_anchor", category_path + "fill-pattern");
|
optgroup->append_single_option_line("infill_anchor", category_path + "fill-pattern");
|
||||||
|
optgroup->append_single_option_line("infill_anchor_max", category_path + "fill-pattern");
|
||||||
optgroup->append_single_option_line("top_fill_pattern", category_path + "top-fill-pattern");
|
optgroup->append_single_option_line("top_fill_pattern", category_path + "top-fill-pattern");
|
||||||
optgroup->append_single_option_line("bottom_fill_pattern", category_path + "bottom-fill-pattern");
|
optgroup->append_single_option_line("bottom_fill_pattern", category_path + "bottom-fill-pattern");
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue