mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-10-24 09:11:23 -06:00
Merge remote-tracking branch 'origin/master' into lh_avoid_crossing_perimeters
# Conflicts: # src/libslic3r/MotionPlanner.cpp # src/libslic3r/libslic3r.h
This commit is contained in:
commit
87879034f6
175 changed files with 34821 additions and 26174 deletions
|
|
@ -249,5 +249,5 @@ else ()
|
|||
install(TARGETS PrusaSlicer RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}")
|
||||
|
||||
# Install the symlink for gcodeviewer
|
||||
install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink prusa-slicer prusa-gcodeviewer WORKING_DIRECTORY ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR})")
|
||||
install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink prusa-slicer prusa-gcodeviewer WORKING_DIRECTORY \$ENV{DESTDIR}/${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR})")
|
||||
endif ()
|
||||
|
|
|
|||
|
|
@ -741,6 +741,10 @@ public:
|
|||
return impl_.getResult();
|
||||
}
|
||||
|
||||
inline int lastPackedBinId() const {
|
||||
return impl_.lastPackedBinId();
|
||||
}
|
||||
|
||||
void clear() { impl_.clear(); }
|
||||
};
|
||||
|
||||
|
|
@ -862,6 +866,10 @@ public:
|
|||
{
|
||||
return selector_.getResult();
|
||||
}
|
||||
|
||||
inline int lastPackedBinId() const {
|
||||
return selector_.lastPackedBinId();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -479,13 +479,18 @@ class _NofitPolyPlacer: public PlacerBoilerplate<_NofitPolyPlacer<RawShape, TBin
|
|||
|
||||
using MaxNfpLevel = nfp::MaxNfpLevel<RawShape>;
|
||||
|
||||
// Norming factor for the optimization function
|
||||
const double norm_;
|
||||
|
||||
public:
|
||||
|
||||
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):
|
||||
Base(bin),
|
||||
norm_(std::sqrt(sl::area(bin)))
|
||||
|
|
@ -616,135 +621,9 @@ private:
|
|||
template<class Level>
|
||||
Shapes calcnfp(const Item &trsh, Level)
|
||||
{ // Function for arbitrary level of nfp implementation
|
||||
using namespace nfp;
|
||||
|
||||
Shapes nfps;
|
||||
|
||||
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;
|
||||
}
|
||||
// TODO: implement
|
||||
return {};
|
||||
}
|
||||
|
||||
struct Optimum {
|
||||
|
|
@ -798,6 +677,50 @@ private:
|
|||
Radians final_rot = initial_rot;
|
||||
Shapes nfps;
|
||||
|
||||
auto& bin = bin_;
|
||||
double norm = norm_;
|
||||
auto pbb = sl::boundingBox(merged_pile_);
|
||||
auto binbb = sl::boundingBox(bin);
|
||||
|
||||
// This is the kernel part of the object function that is
|
||||
// customizable by the library client
|
||||
std::function<double(const Item&)> _objfunc;
|
||||
if(config_.object_function) _objfunc = config_.object_function;
|
||||
else {
|
||||
|
||||
// Inside check has to be strict if no alignment was enabled
|
||||
std::function<double(const Box&)> ins_check;
|
||||
if(config_.alignment == Config::Alignment::DONT_ALIGN)
|
||||
ins_check = [&binbb, norm](const Box& fullbb) {
|
||||
double ret = 0;
|
||||
if(!sl::isInside(fullbb, binbb))
|
||||
ret += norm;
|
||||
return ret;
|
||||
};
|
||||
else
|
||||
ins_check = [&bin](const Box& fullbb) {
|
||||
double miss = overfit(fullbb, bin);
|
||||
miss = miss > 0? miss : 0;
|
||||
return std::pow(miss, 2);
|
||||
};
|
||||
|
||||
_objfunc = [norm, binbb, pbb, ins_check](const Item& item)
|
||||
{
|
||||
auto ibb = item.boundingBox();
|
||||
auto fullbb = sl::boundingBox(pbb, ibb);
|
||||
|
||||
double score = pl::distance(ibb.center(),
|
||||
binbb.center());
|
||||
score /= norm;
|
||||
|
||||
score += ins_check(fullbb);
|
||||
|
||||
return score;
|
||||
};
|
||||
}
|
||||
|
||||
Pile merged_pile = merged_pile_;
|
||||
|
||||
for(auto rot : config_.rotations) {
|
||||
|
||||
item.translation(initial_tr);
|
||||
|
|
@ -822,57 +745,6 @@ private:
|
|||
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_;
|
||||
double norm = norm_;
|
||||
auto pbb = sl::boundingBox(merged_pile);
|
||||
auto binbb = sl::boundingBox(bin);
|
||||
|
||||
// This is the kernel part of the object function that is
|
||||
// customizable by the library client
|
||||
std::function<double(const Item&)> _objfunc;
|
||||
if(config_.object_function) _objfunc = config_.object_function;
|
||||
else {
|
||||
|
||||
// Inside check has to be strict if no alignment was enabled
|
||||
std::function<double(const Box&)> ins_check;
|
||||
if(config_.alignment == Config::Alignment::DONT_ALIGN)
|
||||
ins_check = [&binbb, norm](const Box& fullbb) {
|
||||
double ret = 0;
|
||||
if(!sl::isInside(fullbb, binbb))
|
||||
ret += norm;
|
||||
return ret;
|
||||
};
|
||||
else
|
||||
ins_check = [&bin](const Box& fullbb) {
|
||||
double miss = overfit(fullbb, bin);
|
||||
miss = miss > 0? miss : 0;
|
||||
return std::pow(miss, 2);
|
||||
};
|
||||
|
||||
_objfunc = [norm, binbb, pbb, ins_check](const Item& item)
|
||||
{
|
||||
auto ibb = item.boundingBox();
|
||||
auto fullbb = sl::boundingBox(pbb, ibb);
|
||||
|
||||
double score = pl::distance(ibb.center(),
|
||||
binbb.center());
|
||||
score /= norm;
|
||||
|
||||
score += ins_check(fullbb);
|
||||
|
||||
return score;
|
||||
};
|
||||
}
|
||||
|
||||
// Our object function for placement
|
||||
auto rawobjfunc = [_objfunc, iv, startpos]
|
||||
(Vertex v, Item& itm)
|
||||
|
|
@ -1045,6 +917,7 @@ private:
|
|||
|
||||
if(can_pack) {
|
||||
ret = PackResult(item);
|
||||
merged_pile_ = nfp::merge(merged_pile_, item.transformedShape());
|
||||
} else {
|
||||
ret = PackResult(best_overfit);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,8 +71,9 @@ public:
|
|||
std::sort(store_.begin(), store_.end(), sortfunc);
|
||||
|
||||
auto total = last-first;
|
||||
auto makeProgress = [this, &total](Placer& placer, size_t idx) {
|
||||
packed_bins_[idx] = placer.getItems();
|
||||
auto makeProgress = [this, &total](Placer& placer, size_t bin_idx) {
|
||||
packed_bins_[bin_idx] = placer.getItems();
|
||||
this->last_packed_bin_id_ = int(bin_idx);
|
||||
this->progress_(static_cast<unsigned>(--total));
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ public:
|
|||
return packed_bins_;
|
||||
}
|
||||
|
||||
inline int lastPackedBinId() const { return last_packed_bin_id_; }
|
||||
|
||||
inline void progressIndicator(ProgressFunction fn) { progress_ = fn; }
|
||||
|
||||
inline void stopCondition(StopCondition cond) { stopcond_ = cond; }
|
||||
|
|
@ -54,6 +56,7 @@ protected:
|
|||
PackGroup packed_bins_;
|
||||
ProgressFunction progress_ = [](unsigned){};
|
||||
StopCondition stopcond_ = [](){ return false; };
|
||||
int last_packed_bin_id_ = -1;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -726,6 +726,36 @@ inline bool is_any_triangle_in_radius(
|
|||
return hit_point.allFinite();
|
||||
}
|
||||
|
||||
|
||||
// Traverse the tree and return the index of an entity whose bounding box
|
||||
// contains a given point. Returns size_t(-1) when the point is outside.
|
||||
template<typename TreeType, typename VectorType>
|
||||
size_t get_candidate_idx(const TreeType& tree, const VectorType& v)
|
||||
{
|
||||
if (tree.empty() || ! tree.node(0).bbox.contains(v))
|
||||
return size_t(-1);
|
||||
|
||||
size_t node_idx = 0;
|
||||
while (true) {
|
||||
decltype(tree.node(node_idx)) node = tree.node(node_idx);
|
||||
static_assert(std::is_reference<decltype(node)>::value,
|
||||
"Nodes shall be addressed by reference.");
|
||||
assert(node.is_valid());
|
||||
assert(node.bbox.contains(v));
|
||||
|
||||
if (! node.is_leaf()) {
|
||||
if (tree.left_child(node_idx).bbox.contains(v))
|
||||
node_idx = tree.left_child_idx(node_idx);
|
||||
else if (tree.right_child(node_idx).bbox.contains(v))
|
||||
node_idx = tree.right_child_idx(node_idx);
|
||||
else
|
||||
return size_t(-1);
|
||||
} else
|
||||
return node.idx;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace AABBTreeIndirect
|
||||
} // namespace Slic3r
|
||||
|
||||
|
|
|
|||
|
|
@ -68,6 +68,15 @@ void AppConfig::set_defaults()
|
|||
if (get("export_sources_full_pathnames").empty())
|
||||
set("export_sources_full_pathnames", "0");
|
||||
|
||||
#if ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN
|
||||
#ifdef _WIN32
|
||||
if (get("associate_3mf").empty())
|
||||
set("associate_3mf", "0");
|
||||
if (get("associate_stl").empty())
|
||||
set("associate_stl", "0");
|
||||
#endif // _WIN32
|
||||
#endif // ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN
|
||||
|
||||
// remove old 'use_legacy_opengl' parameter from this config, if present
|
||||
if (!get("use_legacy_opengl").empty())
|
||||
erase("", "use_legacy_opengl");
|
||||
|
|
@ -108,7 +117,21 @@ void AppConfig::set_defaults()
|
|||
|
||||
if (get("use_inches").empty())
|
||||
set("use_inches", "0");
|
||||
|
||||
if (get("default_action_on_close_application").empty())
|
||||
set("default_action_on_close_application", "none"); // , "discard" or "save"
|
||||
|
||||
if (get("default_action_on_select_preset").empty())
|
||||
set("default_action_on_select_preset", "none"); // , "transfer", "discard" or "save"
|
||||
}
|
||||
#if ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN
|
||||
else {
|
||||
#ifdef _WIN32
|
||||
if (get("associate_gcode").empty())
|
||||
set("associate_gcode", "0");
|
||||
#endif // _WIN32
|
||||
}
|
||||
#endif // ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN
|
||||
|
||||
if (get("seq_top_layer_only").empty())
|
||||
set("seq_top_layer_only", "1");
|
||||
|
|
@ -125,11 +148,12 @@ void AppConfig::set_defaults()
|
|||
if (get("show_splash_screen").empty())
|
||||
set("show_splash_screen", "1");
|
||||
|
||||
if (get("default_action_on_close_application").empty())
|
||||
set("default_action_on_close_application", "none"); // , "discard" or "save"
|
||||
|
||||
if (get("default_action_on_select_preset").empty())
|
||||
set("default_action_on_select_preset", "none"); // , "transfer", "discard" or "save"
|
||||
#if ENABLE_CTRL_M_ON_WINDOWS
|
||||
#ifdef _WIN32
|
||||
if (get("use_legacy_3DConnexion").empty())
|
||||
set("use_legacy_3DConnexion", "0");
|
||||
#endif // _WIN32
|
||||
#endif // ENABLE_CTRL_M_ON_WINDOWS
|
||||
|
||||
// Remove legacy window positions/sizes
|
||||
erase("", "main_frame_maximized");
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#include <libnest2d/optimizers/nlopt/subplex.hpp>
|
||||
#include <libnest2d/placers/nfpplacer.hpp>
|
||||
#include <libnest2d/selections/firstfit.hpp>
|
||||
#include <libnest2d/utils/rotcalipers.hpp>
|
||||
|
||||
#include <numeric>
|
||||
#include <ClipperUtils.hpp>
|
||||
|
|
@ -83,7 +84,7 @@ const double BIG_ITEM_TRESHOLD = 0.02;
|
|||
// Fill in the placer algorithm configuration with values carefully chosen for
|
||||
// Slic3r.
|
||||
template<class PConf>
|
||||
void fill_config(PConf& pcfg) {
|
||||
void fill_config(PConf& pcfg, const ArrangeParams ¶ms) {
|
||||
|
||||
// Align the arranged pile into the center of the bin
|
||||
pcfg.alignment = PConf::Alignment::CENTER;
|
||||
|
|
@ -93,14 +94,17 @@ void fill_config(PConf& pcfg) {
|
|||
|
||||
// TODO cannot use rotations until multiple objects of same geometry can
|
||||
// handle different rotations.
|
||||
pcfg.rotations = { 0.0 };
|
||||
if (params.allow_rotations)
|
||||
pcfg.rotations = {0., PI / 2., PI, 3. * PI / 2. };
|
||||
else
|
||||
pcfg.rotations = {0.};
|
||||
|
||||
// The accuracy of optimization.
|
||||
// Goes from 0.0 to 1.0 and scales performance as well
|
||||
pcfg.accuracy = 0.65f;
|
||||
pcfg.accuracy = params.accuracy;
|
||||
|
||||
// Allow parallel execution.
|
||||
pcfg.parallel = true;
|
||||
pcfg.parallel = params.parallel;
|
||||
}
|
||||
|
||||
// Apply penalty to object function result. This is used only when alignment
|
||||
|
|
@ -277,10 +281,10 @@ protected:
|
|||
if (result.empty())
|
||||
score = 0.50 * dist + 0.50 * density;
|
||||
else
|
||||
score = R * 0.60 * dist +
|
||||
(1.0 - R) * 0.20 * density +
|
||||
0.20 * alignment_score;
|
||||
|
||||
// Let the density matter more when fewer objects remain
|
||||
score = 0.50 * dist + (1.0 - R) * 0.20 * density +
|
||||
0.30 * alignment_score;
|
||||
|
||||
break;
|
||||
}
|
||||
case LAST_BIG_ITEM: {
|
||||
|
|
@ -304,15 +308,15 @@ protected:
|
|||
|
||||
public:
|
||||
AutoArranger(const TBin & bin,
|
||||
Distance dist,
|
||||
const ArrangeParams ¶ms,
|
||||
std::function<void(unsigned)> progressind,
|
||||
std::function<bool(void)> stopcond)
|
||||
: m_pck(bin, dist)
|
||||
: m_pck(bin, params.min_obj_distance)
|
||||
, m_bin(bin)
|
||||
, m_bin_area(sl::area(bin))
|
||||
, m_norm(std::sqrt(m_bin_area))
|
||||
{
|
||||
fill_config(m_pconf);
|
||||
fill_config(m_pconf, params);
|
||||
|
||||
// Set up a callback that is called just before arranging starts
|
||||
// This functionality is provided by the Nester class (m_pack).
|
||||
|
|
@ -343,18 +347,31 @@ public:
|
|||
};
|
||||
|
||||
m_pconf.object_function = get_objfn();
|
||||
|
||||
auto on_packed = params.on_packed;
|
||||
|
||||
if (progressind) m_pck.progressIndicator(progressind);
|
||||
if (progressind || on_packed)
|
||||
m_pck.progressIndicator([this, progressind, on_packed](unsigned rem) {
|
||||
|
||||
if (progressind)
|
||||
progressind(rem);
|
||||
|
||||
if (on_packed) {
|
||||
int last_bed = m_pck.lastPackedBinId();
|
||||
if (last_bed >= 0) {
|
||||
Item &last_packed = m_pck.lastResult()[last_bed].back();
|
||||
ArrangePolygon ap;
|
||||
ap.bed_idx = last_packed.binId();
|
||||
ap.priority = last_packed.priority();
|
||||
on_packed(ap);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (stopcond) m_pck.stopCondition(stopcond);
|
||||
|
||||
m_pck.configure(m_pconf);
|
||||
}
|
||||
|
||||
AutoArranger(const TBin & bin,
|
||||
std::function<void(unsigned)> progressind,
|
||||
std::function<bool(void)> stopcond)
|
||||
: AutoArranger{bin, 0 /* no min distance */, progressind, stopcond}
|
||||
{}
|
||||
|
||||
template<class It> inline void operator()(It from, It to) {
|
||||
m_rtree.clear();
|
||||
|
|
@ -452,12 +469,18 @@ template<class Bin> void remove_large_items(std::vector<Item> &items, Bin &&bin)
|
|||
++it : it = items.erase(it);
|
||||
}
|
||||
|
||||
template<class S> Radians min_area_boundingbox_rotation(const S &sh)
|
||||
{
|
||||
return minAreaBoundingBox<S, TCompute<S>, boost::rational<LargeInt>>(sh)
|
||||
.angleToX();
|
||||
}
|
||||
|
||||
template<class BinT> // Arrange for arbitrary bin type
|
||||
void _arrange(
|
||||
std::vector<Item> & shapes,
|
||||
std::vector<Item> & excludes,
|
||||
const BinT & bin,
|
||||
const ArrangeParams & params,
|
||||
const ArrangeParams ¶ms,
|
||||
std::function<void(unsigned)> progressfn,
|
||||
std::function<bool()> stopfn)
|
||||
{
|
||||
|
|
@ -467,11 +490,10 @@ void _arrange(
|
|||
|
||||
auto corrected_bin = bin;
|
||||
sl::offset(corrected_bin, md);
|
||||
|
||||
AutoArranger<BinT> arranger{corrected_bin, progressfn, stopfn};
|
||||
|
||||
arranger.config().accuracy = params.accuracy;
|
||||
arranger.config().parallel = params.parallel;
|
||||
ArrangeParams mod_params = params;
|
||||
mod_params.min_obj_distance = 0;
|
||||
|
||||
AutoArranger<BinT> arranger{corrected_bin, mod_params, progressfn, stopfn};
|
||||
|
||||
auto infl = coord_t(std::ceil(params.min_obj_distance / 2.0));
|
||||
for (Item& itm : shapes) itm.inflate(infl);
|
||||
|
|
@ -487,6 +509,13 @@ void _arrange(
|
|||
for (auto &itm : shapes ) inp.emplace_back(itm);
|
||||
for (auto &itm : excludes) inp.emplace_back(itm);
|
||||
|
||||
// Use the minimum bounding box rotation as a starting point.
|
||||
// TODO: This only works for convex hull. If we ever switch to concave
|
||||
// polygon nesting, a convex hull needs to be calculated.
|
||||
if (params.allow_rotations)
|
||||
for (auto &itm : shapes)
|
||||
itm.rotation(min_area_boundingbox_rotation(itm.rawShape()));
|
||||
|
||||
arranger(inp.begin(), inp.end());
|
||||
for (Item &itm : inp) itm.inflate(-infl);
|
||||
}
|
||||
|
|
@ -556,28 +585,35 @@ static void process_arrangeable(const ArrangePolygon &arrpoly,
|
|||
outp.back().priority(arrpoly.priority);
|
||||
}
|
||||
|
||||
template<class Fn> auto call_with_bed(const Points &bed, Fn &&fn)
|
||||
{
|
||||
if (bed.empty())
|
||||
return fn(InfiniteBed{});
|
||||
else if (bed.size() == 1)
|
||||
return fn(InfiniteBed{bed.front()});
|
||||
else {
|
||||
auto bb = BoundingBox(bed);
|
||||
CircleBed circ = to_circle(bb.center(), bed);
|
||||
auto parea = poly_area(bed);
|
||||
|
||||
if ((1.0 - parea / area(bb)) < 1e-3)
|
||||
return fn(bb);
|
||||
else if (!std::isnan(circ.radius()))
|
||||
return fn(circ);
|
||||
else
|
||||
return fn(Polygon(bed));
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
void arrange(ArrangePolygons & items,
|
||||
const ArrangePolygons &excludes,
|
||||
const Points & bed,
|
||||
const ArrangeParams & params)
|
||||
{
|
||||
if (bed.empty())
|
||||
arrange(items, excludes, InfiniteBed{}, params);
|
||||
else if (bed.size() == 1)
|
||||
arrange(items, excludes, InfiniteBed{bed.front()}, params);
|
||||
else {
|
||||
auto bb = BoundingBox(bed);
|
||||
CircleBed circ = to_circle(bb.center(), bed);
|
||||
auto parea = poly_area(bed);
|
||||
|
||||
if ((1.0 - parea / area(bb)) < 1e-3)
|
||||
arrange(items, excludes, bb, params);
|
||||
else if (!std::isnan(circ.radius()))
|
||||
arrange(items, excludes, circ, params);
|
||||
else
|
||||
arrange(items, excludes, Polygon(bed), params);
|
||||
}
|
||||
call_with_bed(bed, [&](const auto &bin) {
|
||||
arrange(items, excludes, bin, params);
|
||||
});
|
||||
}
|
||||
|
||||
template<class BedT>
|
||||
|
|
|
|||
|
|
@ -74,14 +74,18 @@ struct ArrangeParams {
|
|||
|
||||
/// The accuracy of optimization.
|
||||
/// Goes from 0.0 to 1.0 and scales performance as well
|
||||
float accuracy = 0.65f;
|
||||
float accuracy = 1.f;
|
||||
|
||||
/// Allow parallel execution.
|
||||
bool parallel = true;
|
||||
|
||||
bool allow_rotations = false;
|
||||
|
||||
/// Progress indicator callback called when an object gets packed.
|
||||
/// The unsigned argument is the number of items remaining to pack.
|
||||
std::function<void(unsigned)> progressind;
|
||||
|
||||
std::function<void(const ArrangePolygon &)> on_packed;
|
||||
|
||||
/// A predicate returning true if abort is needed.
|
||||
std::function<bool(void)> stopcondition;
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ public:
|
|||
void translate(coordf_t x, coordf_t y) { assert(this->defined); PointClass v(x, y); this->min += v; this->max += v; }
|
||||
void translate(const Vec2d &v) { this->min += v; this->max += v; }
|
||||
void offset(coordf_t delta);
|
||||
BoundingBoxBase<PointClass> inflated(coordf_t delta) const throw() { BoundingBoxBase<PointClass> out(*this); out.offset(delta); return out; }
|
||||
PointClass center() const;
|
||||
bool contains(const PointClass &point) const {
|
||||
return point(0) >= this->min(0) && point(0) <= this->max(0)
|
||||
|
|
@ -91,6 +92,7 @@ public:
|
|||
void translate(coordf_t x, coordf_t y, coordf_t z) { assert(this->defined); PointClass v(x, y, z); this->min += v; this->max += v; }
|
||||
void translate(const Vec3d &v) { this->min += v; this->max += v; }
|
||||
void offset(coordf_t delta);
|
||||
BoundingBoxBase<PointClass> inflated(coordf_t delta) const throw() { BoundingBoxBase<PointClass> out(*this); out.offset(delta); return out; }
|
||||
PointClass center() const;
|
||||
coordf_t max_size() const;
|
||||
|
||||
|
|
@ -159,6 +161,8 @@ public:
|
|||
BoundingBox(const Point &pmin, const Point &pmax) : BoundingBoxBase<Point>(pmin, pmax) {}
|
||||
BoundingBox(const Points &points) : BoundingBoxBase<Point>(points) {}
|
||||
|
||||
BoundingBox inflated(coordf_t delta) const throw() { BoundingBox out(*this); out.offset(delta); return out; }
|
||||
|
||||
friend BoundingBox get_extents_rotated(const Points &points, double angle);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -58,10 +58,10 @@ add_library(libslic3r STATIC
|
|||
Fill/FillGyroid.hpp
|
||||
Fill/FillPlanePath.cpp
|
||||
Fill/FillPlanePath.hpp
|
||||
Fill/FillLine.cpp
|
||||
Fill/FillLine.hpp
|
||||
Fill/FillRectilinear.cpp
|
||||
Fill/FillRectilinear.hpp
|
||||
Fill/FillRectilinear2.cpp
|
||||
Fill/FillRectilinear2.hpp
|
||||
Flow.cpp
|
||||
Flow.hpp
|
||||
format.hpp
|
||||
|
|
@ -159,8 +159,8 @@ add_library(libslic3r STATIC
|
|||
PrintConfig.hpp
|
||||
PrintObject.cpp
|
||||
PrintRegion.cpp
|
||||
PNGRead.hpp
|
||||
PNGRead.cpp
|
||||
PNGReadWrite.hpp
|
||||
PNGReadWrite.cpp
|
||||
Semver.cpp
|
||||
ShortestPath.cpp
|
||||
ShortestPath.hpp
|
||||
|
|
|
|||
|
|
@ -1069,7 +1069,7 @@ Polygons variable_offset_inner(const ExPolygon &expoly, const std::vector<std::v
|
|||
ClipperLib::Paths holes;
|
||||
holes.reserve(expoly.holes.size());
|
||||
for (const Polygon& hole : expoly.holes)
|
||||
append(holes, fix_after_outer_offset(mittered_offset_path_scaled(hole, deltas[1 + &hole - expoly.holes.data()], miter_limit), ClipperLib::pftNegative, 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
|
||||
for (auto &c : holes)
|
||||
assert(ClipperLib::Area(c) > 0.);
|
||||
|
|
@ -1113,7 +1113,7 @@ for (const std::vector<float>& ds : deltas)
|
|||
ClipperLib::Paths holes;
|
||||
holes.reserve(expoly.holes.size());
|
||||
for (const Polygon& hole : expoly.holes)
|
||||
append(holes, fix_after_inner_offset(mittered_offset_path_scaled(hole, deltas[1 + &hole - expoly.holes.data()], miter_limit), ClipperLib::pftPositive, true));
|
||||
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
|
||||
for (auto &c : holes)
|
||||
assert(ClipperLib::Area(c) > 0.);
|
||||
|
|
@ -1157,7 +1157,7 @@ for (const std::vector<float>& ds : deltas)
|
|||
ClipperLib::Paths holes;
|
||||
holes.reserve(expoly.holes.size());
|
||||
for (const Polygon& hole : expoly.holes)
|
||||
append(holes, fix_after_inner_offset(mittered_offset_path_scaled(hole, deltas[1 + &hole - expoly.holes.data()], miter_limit), ClipperLib::pftPositive, true));
|
||||
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
|
||||
for (auto &c : holes)
|
||||
assert(ClipperLib::Area(c) > 0.);
|
||||
|
|
@ -1205,7 +1205,7 @@ ExPolygons variable_offset_inner_ex(const ExPolygon &expoly, const std::vector<s
|
|||
ClipperLib::Paths holes;
|
||||
holes.reserve(expoly.holes.size());
|
||||
for (const Polygon& hole : expoly.holes)
|
||||
append(holes, fix_after_outer_offset(mittered_offset_path_scaled(hole, deltas[1 + &hole - expoly.holes.data()], miter_limit), ClipperLib::pftNegative, 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
|
||||
for (auto &c : holes)
|
||||
assert(ClipperLib::Area(c) > 0.);
|
||||
|
|
|
|||
|
|
@ -3,16 +3,16 @@
|
|||
#include <float.h>
|
||||
#include <unordered_map>
|
||||
|
||||
#if 0
|
||||
// #ifdef SLIC3R_GUI
|
||||
#include <wx/image.h>
|
||||
#endif /* SLIC3R_GUI */
|
||||
#include <png.h>
|
||||
|
||||
#include "libslic3r.h"
|
||||
#include "ClipperUtils.hpp"
|
||||
#include "EdgeGrid.hpp"
|
||||
#include "Geometry.hpp"
|
||||
#include "SVG.hpp"
|
||||
#include "PNGReadWrite.hpp"
|
||||
|
||||
// #define EDGE_GRID_DEBUG_OUTPUT
|
||||
|
||||
#if 0
|
||||
// Enable debugging and assert in this file.
|
||||
|
|
@ -55,6 +55,24 @@ void EdgeGrid::Grid::create(const Polygons &polygons, coord_t resolution)
|
|||
create_from_m_contours(resolution);
|
||||
}
|
||||
|
||||
void EdgeGrid::Grid::create(const std::vector<const Polygon*> &polygons, coord_t resolution)
|
||||
{
|
||||
// Count the contours.
|
||||
size_t ncontours = 0;
|
||||
for (size_t j = 0; j < polygons.size(); ++ j)
|
||||
if (! polygons[j]->points.empty())
|
||||
++ ncontours;
|
||||
|
||||
// Collect the contours.
|
||||
m_contours.assign(ncontours, nullptr);
|
||||
ncontours = 0;
|
||||
for (size_t j = 0; j < polygons.size(); ++ j)
|
||||
if (! polygons[j]->points.empty())
|
||||
m_contours[ncontours ++] = &polygons[j]->points;
|
||||
|
||||
create_from_m_contours(resolution);
|
||||
}
|
||||
|
||||
void EdgeGrid::Grid::create(const std::vector<Points> &polygons, coord_t resolution)
|
||||
{
|
||||
// Count the contours.
|
||||
|
|
@ -659,6 +677,11 @@ struct PropagateDanielssonSingleVStep3 {
|
|||
|
||||
void EdgeGrid::Grid::calculate_sdf()
|
||||
{
|
||||
#ifdef EDGE_GRID_DEBUG_OUTPUT
|
||||
static int iRun = 0;
|
||||
++ iRun;
|
||||
#endif
|
||||
|
||||
// 1) Initialize a signum and an unsigned vector to a zero iso surface.
|
||||
size_t nrows = m_rows + 1;
|
||||
size_t ncols = m_cols + 1;
|
||||
|
|
@ -756,19 +779,12 @@ void EdgeGrid::Grid::calculate_sdf()
|
|||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
static int iRun = 0;
|
||||
++ iRun;
|
||||
if (wxImage::FindHandler(wxBITMAP_TYPE_PNG) == nullptr)
|
||||
wxImage::AddHandler(new wxPNGHandler);
|
||||
//#ifdef SLIC3R_GUI
|
||||
#ifdef EDGE_GRID_DEBUG_OUTPUT
|
||||
{
|
||||
wxImage img(ncols, nrows);
|
||||
unsigned char *data = img.GetData();
|
||||
memset(data, 0, ncols * nrows * 3);
|
||||
for (coord_t r = 0; r < nrows; ++r) {
|
||||
for (coord_t c = 0; c < ncols; ++c) {
|
||||
unsigned char *pxl = data + (((nrows - r - 1) * ncols) + c) * 3;
|
||||
std::vector<uint8_t> pixels(ncols * nrows * 3, 0);
|
||||
for (coord_t r = 0; r < nrows; ++ r) {
|
||||
for (coord_t c = 0; c < ncols; ++ c) {
|
||||
uint8_t *pxl = pixels.data() + (((nrows - r - 1) * ncols) + c) * 3;
|
||||
float d = m_signed_distance_field[r * ncols + c];
|
||||
if (d != search_radius) {
|
||||
float s = 255 * d / search_radius;
|
||||
|
|
@ -784,15 +800,13 @@ void EdgeGrid::Grid::calculate_sdf()
|
|||
}
|
||||
}
|
||||
}
|
||||
img.SaveFile(debug_out_path("unsigned_df-%d.png", iRun), wxBITMAP_TYPE_PNG);
|
||||
png::write_rgb_to_file_scaled(debug_out_path("unsigned_df-%d.png", iRun), ncols, nrows, pixels, 10);
|
||||
}
|
||||
{
|
||||
wxImage img(ncols, nrows);
|
||||
unsigned char *data = img.GetData();
|
||||
memset(data, 0, ncols * nrows * 3);
|
||||
for (coord_t r = 0; r < nrows; ++r) {
|
||||
for (coord_t c = 0; c < ncols; ++c) {
|
||||
unsigned char *pxl = data + (((nrows - r - 1) * ncols) + c) * 3;
|
||||
std::vector<uint8_t> pixels(ncols * nrows * 3, 0);
|
||||
for (coord_t r = 0; r < nrows; ++ r) {
|
||||
for (coord_t c = 0; c < ncols; ++ c) {
|
||||
unsigned char *pxl = pixels.data() + (((nrows - r - 1) * ncols) + c) * 3;
|
||||
float d = m_signed_distance_field[r * ncols + c];
|
||||
if (d != search_radius) {
|
||||
float s = 255 * d / search_radius;
|
||||
|
|
@ -817,9 +831,9 @@ void EdgeGrid::Grid::calculate_sdf()
|
|||
}
|
||||
}
|
||||
}
|
||||
img.SaveFile(debug_out_path("signed_df-%d.png", iRun), wxBITMAP_TYPE_PNG);
|
||||
png::write_rgb_to_file_scaled(debug_out_path("signed_df-%d.png", iRun), ncols, nrows, pixels, 10);
|
||||
}
|
||||
#endif /* SLIC3R_GUI */
|
||||
#endif // EDGE_GRID_DEBUG_OUTPUT
|
||||
|
||||
// 2) Propagate the signum.
|
||||
#define PROPAGATE_SIGNUM_SINGLE_STEP(DELTA) do { \
|
||||
|
|
@ -891,17 +905,14 @@ void EdgeGrid::Grid::calculate_sdf()
|
|||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
//#ifdef SLIC3R_GUI
|
||||
#ifdef EDGE_GRID_DEBUG_OUTPUT
|
||||
{
|
||||
wxImage img(ncols, nrows);
|
||||
unsigned char *data = img.GetData();
|
||||
memset(data, 0, ncols * nrows * 3);
|
||||
std::vector<uint8_t> pixels(ncols * nrows * 3, 0);
|
||||
float search_radius = float(m_resolution * 5);
|
||||
for (coord_t r = 0; r < nrows; ++r) {
|
||||
for (coord_t c = 0; c < ncols; ++c) {
|
||||
unsigned char *pxl = data + (((nrows - r - 1) * ncols) + c) * 3;
|
||||
unsigned char sign = signs[r * ncols + c];
|
||||
uint8_t *pxl = pixels.data() + (((nrows - r - 1) * ncols) + c) * 3;
|
||||
uint8_t sign = signs[r * ncols + c];
|
||||
switch (sign) {
|
||||
case 0:
|
||||
// Positive, outside of a narrow band.
|
||||
|
|
@ -942,20 +953,17 @@ void EdgeGrid::Grid::calculate_sdf()
|
|||
}
|
||||
}
|
||||
}
|
||||
img.SaveFile(debug_out_path("signed_df-signs-%d.png", iRun), wxBITMAP_TYPE_PNG);
|
||||
png::write_rgb_to_file_scaled(debug_out_path("signed_df-signs-%d.png", iRun), ncols, nrows, pixels, 10);
|
||||
}
|
||||
#endif /* SLIC3R_GUI */
|
||||
#endif // EDGE_GRID_DEBUG_OUTPUT
|
||||
|
||||
#if 0
|
||||
//#ifdef SLIC3R_GUI
|
||||
#ifdef EDGE_GRID_DEBUG_OUTPUT
|
||||
{
|
||||
wxImage img(ncols, nrows);
|
||||
unsigned char *data = img.GetData();
|
||||
memset(data, 0, ncols * nrows * 3);
|
||||
std::vector<uint8_t> pixels(ncols * nrows * 3, 0);
|
||||
float search_radius = float(m_resolution * 5);
|
||||
for (coord_t r = 0; r < nrows; ++r) {
|
||||
for (coord_t c = 0; c < ncols; ++c) {
|
||||
unsigned char *pxl = data + (((nrows - r - 1) * ncols) + c) * 3;
|
||||
uint8_t *pxl = pixels.data() + (((nrows - r - 1) * ncols) + c) * 3;
|
||||
float d = m_signed_distance_field[r * ncols + c];
|
||||
float s = 255.f * fabs(d) / search_radius;
|
||||
int is = std::max(0, std::min(255, int(floor(s + 0.5f))));
|
||||
|
|
@ -971,9 +979,9 @@ void EdgeGrid::Grid::calculate_sdf()
|
|||
}
|
||||
}
|
||||
}
|
||||
img.SaveFile(debug_out_path("signed_df2-%d.png", iRun), wxBITMAP_TYPE_PNG);
|
||||
png::write_rgb_to_file_scaled(debug_out_path("signed_df2-%d.png", iRun), ncols, nrows, pixels, 10);
|
||||
}
|
||||
#endif /* SLIC3R_GUI */
|
||||
#endif // EDGE_GRID_DEBUG_OUTPUT
|
||||
}
|
||||
|
||||
float EdgeGrid::Grid::signed_distance_bilinear(const Point &pt) const
|
||||
|
|
@ -1150,7 +1158,7 @@ EdgeGrid::Grid::ClosestPointResult EdgeGrid::Grid::closest_point(const Point &pt
|
|||
if (result.contour_idx != size_t(-1) && d_min <= double(search_radius)) {
|
||||
result.distance = d_min * sign_min;
|
||||
result.t /= l2_seg_min;
|
||||
assert(result.t >= 0. && result.t < 1.);
|
||||
assert(result.t >= 0. && result.t <= 1.);
|
||||
#ifndef NDEBUG
|
||||
{
|
||||
const Slic3r::Points &pts = *m_contours[result.contour_idx];
|
||||
|
|
@ -1473,26 +1481,18 @@ bool EdgeGrid::Grid::has_intersecting_edges() const
|
|||
return false;
|
||||
}
|
||||
|
||||
#if 0
|
||||
void EdgeGrid::save_png(const EdgeGrid::Grid &grid, const BoundingBox &bbox, coord_t resolution, const char *path)
|
||||
void EdgeGrid::save_png(const EdgeGrid::Grid &grid, const BoundingBox &bbox, coord_t resolution, const char *path, size_t scale)
|
||||
{
|
||||
if (wxImage::FindHandler(wxBITMAP_TYPE_PNG) == nullptr)
|
||||
wxImage::AddHandler(new wxPNGHandler);
|
||||
|
||||
unsigned int w = (bbox.max(0) - bbox.min(0) + resolution - 1) / resolution;
|
||||
unsigned int h = (bbox.max(1) - bbox.min(1) + resolution - 1) / resolution;
|
||||
wxImage img(w, h);
|
||||
unsigned char *data = img.GetData();
|
||||
memset(data, 0, w * h * 3);
|
||||
|
||||
static int iRun = 0;
|
||||
++iRun;
|
||||
|
||||
std::vector<uint8_t> pixels(w * h * 3, 0);
|
||||
|
||||
const coord_t search_radius = grid.resolution() * 2;
|
||||
const coord_t display_blend_radius = grid.resolution() * 2;
|
||||
for (coord_t r = 0; r < h; ++r) {
|
||||
for (coord_t c = 0; c < w; ++ c) {
|
||||
unsigned char *pxl = data + (((h - r - 1) * w) + c) * 3;
|
||||
unsigned char *pxl = pixels.data() + (((h - r - 1) * w) + c) * 3;
|
||||
Point pt(c * resolution + bbox.min(0), r * resolution + bbox.min(1));
|
||||
coordf_t min_dist;
|
||||
bool on_segment = true;
|
||||
|
|
@ -1566,9 +1566,8 @@ void EdgeGrid::save_png(const EdgeGrid::Grid &grid, const BoundingBox &bbox, coo
|
|||
}
|
||||
}
|
||||
|
||||
img.SaveFile(path, wxBITMAP_TYPE_PNG);
|
||||
png::write_rgb_to_file_scaled(path, w, h, pixels, scale);
|
||||
}
|
||||
#endif /* SLIC3R_GUI */
|
||||
|
||||
// Find all pairs of intersectiong edges from the set of polygons.
|
||||
std::vector<std::pair<EdgeGrid::Grid::ContourEdge, EdgeGrid::Grid::ContourEdge>> intersecting_edges(const Polygons &polygons)
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ public:
|
|||
void set_bbox(const BoundingBox &bbox) { m_bbox = bbox; }
|
||||
|
||||
void create(const Polygons &polygons, coord_t resolution);
|
||||
void create(const std::vector<const Polygon*> &polygons, coord_t resolution);
|
||||
void create(const std::vector<Points> &polygons, coord_t resolution);
|
||||
void create(const ExPolygon &expoly, coord_t resolution);
|
||||
void create(const ExPolygons &expolygons, coord_t resolution);
|
||||
|
|
@ -83,10 +84,14 @@ public:
|
|||
template<typename VISITOR> void visit_cells_intersecting_line(Slic3r::Point p1, Slic3r::Point p2, VISITOR &visitor) const
|
||||
{
|
||||
// End points of the line segment.
|
||||
p1(0) -= m_bbox.min(0);
|
||||
p1(1) -= m_bbox.min(1);
|
||||
p2(0) -= m_bbox.min(0);
|
||||
p2(1) -= m_bbox.min(1);
|
||||
assert(m_bbox.contains(p1));
|
||||
assert(m_bbox.contains(p2));
|
||||
p1 -= m_bbox.min;
|
||||
p2 -= m_bbox.min;
|
||||
assert(p1.x() >= 0 && p1.x() < m_cols * m_resolution);
|
||||
assert(p1.y() >= 0 && p1.y() < m_rows * m_resolution);
|
||||
assert(p2.x() >= 0 && p2.x() < m_cols * m_resolution);
|
||||
assert(p2.y() >= 0 && p2.y() < m_rows * m_resolution);
|
||||
// Get the cells of the end points.
|
||||
coord_t ix = p1(0) / m_resolution;
|
||||
coord_t iy = p1(1) / m_resolution;
|
||||
|
|
@ -114,18 +119,22 @@ public:
|
|||
ey -= ex;
|
||||
ex = int64_t(dy) * m_resolution;
|
||||
ix += 1;
|
||||
assert(ix <= ixb);
|
||||
}
|
||||
else if (ex == ey) {
|
||||
ex = int64_t(dy) * m_resolution;
|
||||
ey = int64_t(dx) * m_resolution;
|
||||
ix += 1;
|
||||
iy += 1;
|
||||
assert(ix <= ixb);
|
||||
assert(iy <= iyb);
|
||||
}
|
||||
else {
|
||||
assert(ex > ey);
|
||||
ex -= ey;
|
||||
ey = int64_t(dx) * m_resolution;
|
||||
iy += 1;
|
||||
assert(iy <= iyb);
|
||||
}
|
||||
if (! visitor(iy, ix))
|
||||
return;
|
||||
|
|
@ -140,11 +149,13 @@ public:
|
|||
ey -= ex;
|
||||
ex = int64_t(dy) * m_resolution;
|
||||
ix += 1;
|
||||
assert(ix <= ixb);
|
||||
}
|
||||
else {
|
||||
ex -= ey;
|
||||
ey = int64_t(dx) * m_resolution;
|
||||
iy -= 1;
|
||||
assert(iy >= iyb);
|
||||
}
|
||||
if (! visitor(iy, ix))
|
||||
return;
|
||||
|
|
@ -162,12 +173,14 @@ public:
|
|||
ey -= ex;
|
||||
ex = int64_t(dy) * m_resolution;
|
||||
ix -= 1;
|
||||
assert(ix >= ixb);
|
||||
}
|
||||
else {
|
||||
assert(ex >= ey);
|
||||
ex -= ey;
|
||||
ey = int64_t(dx) * m_resolution;
|
||||
iy += 1;
|
||||
assert(iy <= iyb);
|
||||
}
|
||||
if (! visitor(iy, ix))
|
||||
return;
|
||||
|
|
@ -182,6 +195,7 @@ public:
|
|||
ey -= ex;
|
||||
ex = int64_t(dy) * m_resolution;
|
||||
ix -= 1;
|
||||
assert(ix >= ixb);
|
||||
}
|
||||
else if (ex == ey) {
|
||||
// The lower edge of a grid cell belongs to the cell.
|
||||
|
|
@ -190,10 +204,12 @@ public:
|
|||
if (dx > 0) {
|
||||
ex = int64_t(dy) * m_resolution;
|
||||
ix -= 1;
|
||||
assert(ix >= ixb);
|
||||
}
|
||||
if (dy > 0) {
|
||||
ey = int64_t(dx) * m_resolution;
|
||||
iy -= 1;
|
||||
assert(iy >= iyb);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
|
@ -201,6 +217,7 @@ public:
|
|||
ex -= ey;
|
||||
ey = int64_t(dx) * m_resolution;
|
||||
iy -= 1;
|
||||
assert(iy >= iyb);
|
||||
}
|
||||
if (! visitor(iy, ix))
|
||||
return;
|
||||
|
|
@ -230,6 +247,10 @@ public:
|
|||
|
||||
std::pair<std::vector<std::pair<size_t, size_t>>::const_iterator, std::vector<std::pair<size_t, size_t>>::const_iterator> cell_data_range(coord_t row, coord_t col) const
|
||||
{
|
||||
assert(row >= 0);
|
||||
assert(row < m_rows);
|
||||
assert(col >= 0);
|
||||
assert(col < m_cols);
|
||||
const EdgeGrid::Grid::Cell &cell = m_cells[row * m_cols + col];
|
||||
return std::make_pair(m_cell_data.begin() + cell.begin, m_cell_data.begin() + cell.end);
|
||||
}
|
||||
|
|
@ -295,10 +316,8 @@ protected:
|
|||
std::vector<float> m_signed_distance_field;
|
||||
};
|
||||
|
||||
#if 0
|
||||
// Debugging utility. Save the signed distance field.
|
||||
extern void save_png(const Grid &grid, const BoundingBox &bbox, coord_t resolution, const char *path);
|
||||
#endif /* SLIC3R_GUI */
|
||||
extern void save_png(const Grid &grid, const BoundingBox &bbox, coord_t resolution, const char *path, size_t scale = 1);
|
||||
|
||||
} // namespace EdgeGrid
|
||||
|
||||
|
|
|
|||
|
|
@ -350,23 +350,10 @@ void ExPolygon::get_trapezoids2(Polygons* polygons) const
|
|||
// 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) {
|
||||
coord_t next_x = *(x + 1);
|
||||
if (*x == next_x) continue;
|
||||
|
||||
// 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
|
||||
polygons_append(*polygons, intersection(poly, to_polygons(*this)));
|
||||
if (*x != next_x)
|
||||
// intersect with rectangle
|
||||
// append results to return value
|
||||
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)));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,9 +17,9 @@ typedef std::vector<ExPolygon> ExPolygons;
|
|||
class ExPolygon
|
||||
{
|
||||
public:
|
||||
ExPolygon() {}
|
||||
ExPolygon(const ExPolygon &other) : contour(other.contour), holes(other.holes) {}
|
||||
ExPolygon(ExPolygon &&other) noexcept : contour(std::move(other.contour)), holes(std::move(other.holes)) {}
|
||||
ExPolygon() = default;
|
||||
ExPolygon(const ExPolygon &other) = default;
|
||||
ExPolygon(ExPolygon &&other) = default;
|
||||
explicit ExPolygon(const Polygon &contour) : contour(contour) {}
|
||||
explicit ExPolygon(Polygon &&contour) : contour(std::move(contour)) {}
|
||||
explicit ExPolygon(const Points &contour) : contour(contour) {}
|
||||
|
|
@ -31,10 +31,10 @@ public:
|
|||
ExPolygon(std::initializer_list<Point> contour) : contour(contour) {}
|
||||
ExPolygon(std::initializer_list<Point> contour, std::initializer_list<Point> hole) : contour(contour), holes({ hole }) {}
|
||||
|
||||
ExPolygon& operator=(const ExPolygon &other) { contour = other.contour; holes = other.holes; return *this; }
|
||||
ExPolygon& operator=(ExPolygon &&other) noexcept { contour = std::move(other.contour); holes = std::move(other.holes); return *this; }
|
||||
ExPolygon& operator=(const ExPolygon &other) = default;
|
||||
ExPolygon& operator=(ExPolygon &&other) = default;
|
||||
|
||||
Polygon contour;
|
||||
Polygon contour;
|
||||
Polygons holes;
|
||||
|
||||
operator Points() const;
|
||||
|
|
|
|||
|
|
@ -14,12 +14,12 @@ namespace Slic3r {
|
|||
|
||||
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
|
||||
{
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
#include "../Surface.hpp"
|
||||
|
||||
#include "FillBase.hpp"
|
||||
#include "FillRectilinear2.hpp"
|
||||
#include "FillRectilinear.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
|
@ -33,10 +33,12 @@ struct SurfaceFillParams
|
|||
|
||||
// FillParams
|
||||
float density = 0.f;
|
||||
// Don't connect the fill lines around the inner perimeter.
|
||||
bool dont_connect = false;
|
||||
// Don't adjust spacing to fill the space evenly.
|
||||
bool dont_adjust = false;
|
||||
// Length of the infill anchor along the perimeter line.
|
||||
// 1000mm is roughly the maximum length line that fits into a 32bit coord_t.
|
||||
float anchor_length = 1000.f;
|
||||
float anchor_length_max = 1000.f;
|
||||
|
||||
// width, height of extrusion, nozzle diameter, is bridge
|
||||
// For the output, for fill generator.
|
||||
|
|
@ -65,8 +67,9 @@ struct SurfaceFillParams
|
|||
RETURN_COMPARE_NON_EQUAL(overlap);
|
||||
RETURN_COMPARE_NON_EQUAL(angle);
|
||||
RETURN_COMPARE_NON_EQUAL(density);
|
||||
RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, dont_connect);
|
||||
RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, dont_adjust);
|
||||
RETURN_COMPARE_NON_EQUAL(anchor_length);
|
||||
RETURN_COMPARE_NON_EQUAL(anchor_length_max);
|
||||
RETURN_COMPARE_NON_EQUAL(flow.width);
|
||||
RETURN_COMPARE_NON_EQUAL(flow.height);
|
||||
RETURN_COMPARE_NON_EQUAL(flow.nozzle_diameter);
|
||||
|
|
@ -83,8 +86,9 @@ struct SurfaceFillParams
|
|||
this->overlap == rhs.overlap &&
|
||||
this->angle == rhs.angle &&
|
||||
this->density == rhs.density &&
|
||||
this->dont_connect == rhs.dont_connect &&
|
||||
this->dont_adjust == rhs.dont_adjust &&
|
||||
this->anchor_length == rhs.anchor_length &&
|
||||
this->anchor_length_max == rhs.anchor_length_max &&
|
||||
this->flow == rhs.flow &&
|
||||
this->extrusion_role == rhs.extrusion_role;
|
||||
}
|
||||
|
|
@ -115,16 +119,17 @@ std::vector<SurfaceFill> group_fills(const Layer &layer)
|
|||
if (surface.surface_type == stInternalVoid)
|
||||
has_internal_voids = true;
|
||||
else {
|
||||
const PrintRegionConfig ®ion_config = layerm.region()->config();
|
||||
FlowRole extrusion_role = surface.is_top() ? frTopSolidInfill : (surface.is_solid() ? frSolidInfill : frInfill);
|
||||
bool is_bridge = layer.id() > 0 && surface.is_bridge();
|
||||
params.extruder = layerm.region()->extruder(extrusion_role);
|
||||
params.pattern = layerm.region()->config().fill_pattern.value;
|
||||
params.density = float(layerm.region()->config().fill_density);
|
||||
params.pattern = region_config.fill_pattern.value;
|
||||
params.density = float(region_config.fill_density);
|
||||
|
||||
if (surface.is_solid()) {
|
||||
params.density = 100.f;
|
||||
params.pattern = (surface.is_external() && ! is_bridge) ?
|
||||
(surface.is_top() ? layerm.region()->config().top_fill_pattern.value : layerm.region()->config().bottom_fill_pattern.value) :
|
||||
(surface.is_top() ? region_config.top_fill_pattern.value : region_config.bottom_fill_pattern.value) :
|
||||
ipRectilinear;
|
||||
} else if (params.density <= 0)
|
||||
continue;
|
||||
|
|
@ -136,7 +141,7 @@ std::vector<SurfaceFill> group_fills(const Layer &layer)
|
|||
(surface.is_top() ? erTopSolidInfill : erSolidInfill) :
|
||||
erInternalInfill);
|
||||
params.bridge_angle = float(surface.bridge_angle);
|
||||
params.angle = float(Geometry::deg2rad(layerm.region()->config().fill_angle.value));
|
||||
params.angle = float(Geometry::deg2rad(region_config.fill_angle.value));
|
||||
|
||||
// calculate the actual flow we'll be using for this infill
|
||||
params.flow = layerm.region()->flow(
|
||||
|
|
@ -149,7 +154,11 @@ std::vector<SurfaceFill> group_fills(const Layer &layer)
|
|||
);
|
||||
|
||||
// Calculate flow spacing for infill pattern generation.
|
||||
if (! surface.is_solid() && ! is_bridge) {
|
||||
if (surface.is_solid() || is_bridge) {
|
||||
params.spacing = params.flow.spacing();
|
||||
// Don't limit anchor length for solid or bridging infill.
|
||||
params.anchor_length = 1000.f;
|
||||
} else {
|
||||
// it's internal infill, so we can calculate a generic flow spacing
|
||||
// for all layers, for avoiding the ugly effect of
|
||||
// misaligned infill on first layer because of different extrusion width and
|
||||
|
|
@ -162,8 +171,15 @@ std::vector<SurfaceFill> group_fills(const Layer &layer)
|
|||
-1, // auto width
|
||||
*layer.object()
|
||||
).spacing();
|
||||
} else
|
||||
params.spacing = params.flow.spacing();
|
||||
// Anchor a sparse infill to inner perimeters with the following anchor length:
|
||||
params.anchor_length = float(region_config.infill_anchor);
|
||||
if (region_config.infill_anchor.percent)
|
||||
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);
|
||||
if (it_params == set_surface_params.end())
|
||||
|
|
@ -367,8 +383,10 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
|
|||
|
||||
// apply half spacing using this flow's own spacing and generate infill
|
||||
FillParams params;
|
||||
params.density = float(0.01 * surface_fill.params.density);
|
||||
params.dont_adjust = surface_fill.params.dont_adjust; // false
|
||||
params.density = float(0.01 * surface_fill.params.density);
|
||||
params.dont_adjust = surface_fill.params.dont_adjust; // false
|
||||
params.anchor_length = surface_fill.params.anchor_length;
|
||||
params.anchor_length_max = surface_fill.params.anchor_length_max;
|
||||
|
||||
for (ExPolygon &expoly : surface_fill.expolygons) {
|
||||
// Spacing is modified by the filler to indicate adjustments. Reset it for each expolygon.
|
||||
|
|
@ -526,15 +544,13 @@ void Layer::make_ironing()
|
|||
}
|
||||
std::sort(by_extruder.begin(), by_extruder.end());
|
||||
|
||||
FillRectilinear2 fill;
|
||||
FillRectilinear fill;
|
||||
FillParams fill_params;
|
||||
fill.set_bounding_box(this->object()->bounding_box());
|
||||
fill.layer_id = this->id();
|
||||
fill.z = this->print_z;
|
||||
fill.overlap = 0;
|
||||
fill_params.density = 1.;
|
||||
// fill_params.dont_connect = true;
|
||||
fill_params.dont_connect = false;
|
||||
fill_params.monotonic = true;
|
||||
|
||||
for (size_t i = 0; i < by_extruder.size(); ++ i) {
|
||||
|
|
|
|||
|
|
@ -19,10 +19,10 @@ class LayerRegion;
|
|||
class Filler
|
||||
{
|
||||
public:
|
||||
Filler() : fill(NULL) {}
|
||||
Filler() : fill(nullptr) {}
|
||||
~Filler() {
|
||||
delete fill;
|
||||
fill = NULL;
|
||||
fill = nullptr;
|
||||
}
|
||||
Fill *fill;
|
||||
FillParams params;
|
||||
|
|
|
|||
|
|
@ -137,7 +137,7 @@ void Fill3DHoneycomb::_fill_surface_single(
|
|||
const FillParams ¶ms,
|
||||
unsigned int thickness_layers,
|
||||
const std::pair<float, Point> &direction,
|
||||
ExPolygon &expolygon,
|
||||
ExPolygon expolygon,
|
||||
Polylines &polylines_out)
|
||||
{
|
||||
// no rotation is supported for this infill pattern
|
||||
|
|
@ -162,15 +162,13 @@ void Fill3DHoneycomb::_fill_surface_single(
|
|||
pl.translate(bb.min);
|
||||
|
||||
// clip pattern to boundaries, chain the clipped polylines
|
||||
Polylines polylines_chained = chain_polylines(intersection_pl(polylines, to_polygons(expolygon)));
|
||||
polylines = intersection_pl(polylines, to_polygons(expolygon));
|
||||
|
||||
// connect lines if needed
|
||||
if (! polylines_chained.empty()) {
|
||||
if (params.dont_connect)
|
||||
append(polylines_out, std::move(polylines_chained));
|
||||
else
|
||||
this->connect_infill(std::move(polylines_chained), expolygon, polylines_out, this->spacing, params);
|
||||
}
|
||||
if (params.dont_connect() || polylines.size() <= 1)
|
||||
append(polylines_out, chain_polylines(std::move(polylines)));
|
||||
else
|
||||
this->connect_infill(std::move(polylines), expolygon, polylines_out, this->spacing, params);
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
|
|
|||
|
|
@ -12,19 +12,19 @@ namespace Slic3r {
|
|||
class Fill3DHoneycomb : public Fill
|
||||
{
|
||||
public:
|
||||
virtual Fill* clone() const { return new Fill3DHoneycomb(*this); };
|
||||
virtual ~Fill3DHoneycomb() {}
|
||||
Fill* clone() const override { return new Fill3DHoneycomb(*this); };
|
||||
~Fill3DHoneycomb() override {}
|
||||
|
||||
// require bridge flow since most of this pattern hangs in air
|
||||
virtual bool use_bridge_flow() const { return true; }
|
||||
bool use_bridge_flow() const override { return true; }
|
||||
|
||||
protected:
|
||||
virtual void _fill_surface_single(
|
||||
void _fill_surface_single(
|
||||
const FillParams ¶ms,
|
||||
unsigned int thickness_layers,
|
||||
const std::pair<float, Point> &direction,
|
||||
ExPolygon &expolygon,
|
||||
Polylines &polylines_out);
|
||||
ExPolygon expolygon,
|
||||
Polylines &polylines_out) override;
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
|
|
|
|||
|
|
@ -14,11 +14,18 @@
|
|||
#include <cstdlib>
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
#include <numeric>
|
||||
|
||||
// Boost pool: Don't use mutexes to synchronize memory allocation.
|
||||
#define BOOST_POOL_NO_MT
|
||||
#include <boost/pool/object_pool.hpp>
|
||||
|
||||
#include <boost/geometry.hpp>
|
||||
#include <boost/geometry/geometries/point.hpp>
|
||||
#include <boost/geometry/geometries/segment.hpp>
|
||||
#include <boost/geometry/index/rtree.hpp>
|
||||
|
||||
|
||||
namespace Slic3r {
|
||||
namespace FillAdaptive {
|
||||
|
||||
|
|
@ -288,7 +295,7 @@ std::pair<double, double> adaptive_fill_line_spacing(const PrintObject &print_ob
|
|||
bool build_octree = false;
|
||||
const std::vector<double> &nozzle_diameters = print_object.print()->config().nozzle_diameter.values;
|
||||
double max_nozzle_diameter = *std::max_element(nozzle_diameters.begin(), nozzle_diameters.end());
|
||||
double default_infill_extrusion_width = Flow::auto_extrusion_width(FlowRole::frInfill, max_nozzle_diameter);
|
||||
double default_infill_extrusion_width = Flow::auto_extrusion_width(FlowRole::frInfill, float(max_nozzle_diameter));
|
||||
for (const PrintRegion *region : print_object.print()->regions()) {
|
||||
const PrintRegionConfig &config = region->config();
|
||||
bool nonempty = config.fill_density > 0;
|
||||
|
|
@ -475,7 +482,7 @@ static void generate_infill_lines_recursive(
|
|||
Line new_line(Point::new_scale(from), Point::new_scale(to));
|
||||
if (last_line.a.x() == std::numeric_limits<coord_t>::max()) {
|
||||
last_line.a = new_line.a;
|
||||
} else if ((new_line.a - last_line.b).cwiseAbs().maxCoeff() > 300) { // SCALED_EPSILON is 100 and it is not enough
|
||||
} else if ((new_line.a - last_line.b).cwiseAbs().maxCoeff() > 1000) { // SCALED_EPSILON is 100 and it is not enough
|
||||
context.output_lines.emplace_back(last_line);
|
||||
last_line.a = new_line.a;
|
||||
}
|
||||
|
|
@ -501,7 +508,7 @@ static void generate_infill_lines_recursive(
|
|||
#endif
|
||||
|
||||
#ifdef ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
|
||||
static void export_infill_lines_to_svg(const ExPolygon &expoly, const Polylines &polylines, const std::string &path)
|
||||
static void export_infill_lines_to_svg(const ExPolygon &expoly, const Polylines &polylines, const std::string &path, const Points &pts = Points())
|
||||
{
|
||||
BoundingBox bbox = get_extents(expoly);
|
||||
bbox.offset(scale_(3.));
|
||||
|
|
@ -511,46 +518,805 @@ static void export_infill_lines_to_svg(const ExPolygon &expoly, const Polylines
|
|||
svg.draw_outline(expoly, "green");
|
||||
svg.draw(polylines, "red");
|
||||
static constexpr double trim_length = scale_(0.4);
|
||||
for (Polyline polyline : polylines) {
|
||||
Vec2d a = polyline.points.front().cast<double>();
|
||||
Vec2d d = polyline.points.back().cast<double>();
|
||||
if (polyline.size() == 2) {
|
||||
Vec2d v = d - a;
|
||||
double l = v.norm();
|
||||
if (l > 2. * trim_length) {
|
||||
a += v * trim_length / l;
|
||||
d -= v * trim_length / l;
|
||||
polyline.points.front() = a.cast<coord_t>();
|
||||
polyline.points.back() = d.cast<coord_t>();
|
||||
} else
|
||||
polyline.points.clear();
|
||||
} else if (polyline.size() > 2) {
|
||||
Vec2d b = polyline.points[1].cast<double>();
|
||||
Vec2d c = polyline.points[polyline.points.size() - 2].cast<double>();
|
||||
Vec2d v = b - a;
|
||||
double l = v.norm();
|
||||
if (l > trim_length) {
|
||||
a += v * trim_length / l;
|
||||
polyline.points.front() = a.cast<coord_t>();
|
||||
} else
|
||||
polyline.points.erase(polyline.points.begin());
|
||||
v = d - c;
|
||||
l = v.norm();
|
||||
if (l > trim_length)
|
||||
polyline.points.back() = (d - v * trim_length / l).cast<coord_t>();
|
||||
else
|
||||
polyline.points.pop_back();
|
||||
for (Polyline polyline : polylines)
|
||||
if (! polyline.empty()) {
|
||||
Vec2d a = polyline.points.front().cast<double>();
|
||||
Vec2d d = polyline.points.back().cast<double>();
|
||||
if (polyline.size() == 2) {
|
||||
Vec2d v = d - a;
|
||||
double l = v.norm();
|
||||
if (l > 2. * trim_length) {
|
||||
a += v * trim_length / l;
|
||||
d -= v * trim_length / l;
|
||||
polyline.points.front() = a.cast<coord_t>();
|
||||
polyline.points.back() = d.cast<coord_t>();
|
||||
} else
|
||||
polyline.points.clear();
|
||||
} else if (polyline.size() > 2) {
|
||||
Vec2d b = polyline.points[1].cast<double>();
|
||||
Vec2d c = polyline.points[polyline.points.size() - 2].cast<double>();
|
||||
Vec2d v = b - a;
|
||||
double l = v.norm();
|
||||
if (l > trim_length) {
|
||||
a += v * trim_length / l;
|
||||
polyline.points.front() = a.cast<coord_t>();
|
||||
} else
|
||||
polyline.points.erase(polyline.points.begin());
|
||||
v = d - c;
|
||||
l = v.norm();
|
||||
if (l > trim_length)
|
||||
polyline.points.back() = (d - v * trim_length / l).cast<coord_t>();
|
||||
else
|
||||
polyline.points.pop_back();
|
||||
}
|
||||
svg.draw(polyline, "black");
|
||||
}
|
||||
svg.draw(polyline, "black");
|
||||
}
|
||||
svg.draw(pts, "magenta");
|
||||
}
|
||||
#endif /* ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT */
|
||||
|
||||
// Representing a T-joint (in general case) between two infill lines
|
||||
// (between one end point of intersect_pl/intersect_line and
|
||||
struct Intersection
|
||||
{
|
||||
// Closest line to intersect_point.
|
||||
const Line *closest_line;
|
||||
|
||||
// The line for which is computed closest line from intersect_point to closest_line
|
||||
const Line *intersect_line;
|
||||
// Pointer to the polyline from which is computed closest_line
|
||||
Polyline *intersect_pl;
|
||||
// Point for which is computed closest line (closest_line)
|
||||
Point intersect_point;
|
||||
// Indicate if intersect_point is the first or the last point of intersect_pl
|
||||
bool front;
|
||||
// Signum of intersect_line_dir.cross(closest_line.dir()):
|
||||
bool left;
|
||||
|
||||
// Indication if this intersection has been proceed
|
||||
bool used = false;
|
||||
|
||||
bool fresh() const throw() { return ! used && ! intersect_pl->empty(); }
|
||||
|
||||
Intersection(const Line &closest_line, const Line &intersect_line, Polyline *intersect_pl, const Point &intersect_point, bool front) :
|
||||
closest_line(&closest_line), intersect_line(&intersect_line), intersect_pl(intersect_pl), intersect_point(intersect_point), front(front)
|
||||
{
|
||||
// Calculate side of this intersection line of the closest line.
|
||||
Vec2d v1((this->closest_line->b - this->closest_line->a).cast<double>());
|
||||
Vec2d v2(this->intersect_line_dir());
|
||||
#ifndef NDEBUG
|
||||
{
|
||||
Vec2d v1n = v1.normalized();
|
||||
Vec2d v2n = v2.normalized();
|
||||
double c = cross2(v1n, v2n);
|
||||
assert(std::abs(c) > sin(M_PI / 12.));
|
||||
}
|
||||
#endif // NDEBUG
|
||||
this->left = cross2(v1, v2) > 0.;
|
||||
}
|
||||
|
||||
std::optional<Line> other_hook() const {
|
||||
std::optional<Line> out;
|
||||
const Points &pts = intersect_pl->points;
|
||||
if (pts.size() >= 3)
|
||||
out = this->front ? Line(pts[1], pts[2]) : Line(pts[pts.size() - 2], pts[pts.size() - 3]);
|
||||
return out;
|
||||
}
|
||||
|
||||
bool other_hook_intersects(const Line &l, Point &pt) {
|
||||
std::optional<Line> h = other_hook();
|
||||
return h && h->intersection(l, &pt);
|
||||
}
|
||||
bool other_hook_intersects(const Line &l) { Point pt; return this->other_hook_intersects(l, pt); }
|
||||
|
||||
// Direction to intersect_point.
|
||||
Vec2d intersect_line_dir() const throw() {
|
||||
return (this->intersect_point == intersect_line->a ? intersect_line->b - intersect_line->a : intersect_line->a - intersect_line->b).cast<double>();
|
||||
}
|
||||
};
|
||||
|
||||
static inline Intersection* get_nearest_intersection(std::vector<std::pair<Intersection*, double>>& intersect_line, const size_t first_idx)
|
||||
{
|
||||
assert(intersect_line.size() >= 2);
|
||||
bool take_next = false;
|
||||
if (first_idx == 0)
|
||||
take_next = true;
|
||||
else if (first_idx + 1 == intersect_line.size())
|
||||
take_next = false;
|
||||
else {
|
||||
// Has both prev and next.
|
||||
const std::pair<Intersection*, double> &ithis = intersect_line[first_idx];
|
||||
const std::pair<Intersection*, double> &iprev = intersect_line[first_idx - 1];
|
||||
const std::pair<Intersection*, double> &inext = intersect_line[first_idx + 1];
|
||||
take_next = iprev.first->fresh() && inext.first->fresh() ?
|
||||
inext.second - ithis.second < ithis.second - iprev.second :
|
||||
inext.first->fresh();
|
||||
}
|
||||
return intersect_line[take_next ? first_idx + 1 : first_idx - 1].first;
|
||||
}
|
||||
|
||||
// Create a line representing the anchor aka hook extrusion based on line_to_offset
|
||||
// translated in the direction of the intersection line (intersection.intersect_line).
|
||||
static Line create_offset_line(Line offset_line, const Intersection &intersection, const double scaled_offset)
|
||||
{
|
||||
offset_line.translate((perp(intersection.closest_line->vector().cast<double>().normalized()) * (intersection.left ? scaled_offset : - scaled_offset)).cast<coord_t>());
|
||||
// Extend the line by a small value to guarantee a collision with adjacent lines
|
||||
offset_line.extend(coord_t(scaled_offset * 1.16)); // / cos(PI/6)
|
||||
return offset_line;
|
||||
}
|
||||
|
||||
namespace bg = boost::geometry;
|
||||
namespace bgm = boost::geometry::model;
|
||||
namespace bgi = boost::geometry::index;
|
||||
|
||||
// float is needed because for coord_t bgi::intersects throws "bad numeric conversion: positive overflow"
|
||||
using rtree_point_t = bgm::point<float, 2, boost::geometry::cs::cartesian>;
|
||||
using rtree_segment_t = bgm::segment<rtree_point_t>;
|
||||
using rtree_t = bgi::rtree<std::pair<rtree_segment_t, size_t>, bgi::rstar<16, 4>>;
|
||||
|
||||
static inline rtree_point_t mk_rtree_point(const Point &pt) {
|
||||
return rtree_point_t(float(pt.x()), float(pt.y()));
|
||||
}
|
||||
static inline rtree_segment_t mk_rtree_seg(const Point &a, const Point &b) {
|
||||
return { mk_rtree_point(a), mk_rtree_point(b) };
|
||||
}
|
||||
static inline rtree_segment_t mk_rtree_seg(const Line &l) {
|
||||
return mk_rtree_seg(l.a, l.b);
|
||||
}
|
||||
|
||||
// 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(
|
||||
const Intersection &intersection, const double scaled_offset,
|
||||
const coordf_t hook_length, double scaled_trim_distance,
|
||||
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.
|
||||
Point hook_start;
|
||||
bool intersection_found = intersection.intersect_line->intersection(
|
||||
create_offset_line(*intersection.closest_line, intersection, scaled_offset),
|
||||
&hook_start);
|
||||
assert(intersection_found);
|
||||
|
||||
std::optional<Line> other_hook = intersection.other_hook();
|
||||
|
||||
Vec2d hook_vector_norm = intersection.closest_line->vector().cast<double>().normalized();
|
||||
// hook_vector is extended by the thickness of the infill line, so that a collision is found against
|
||||
// the infill centerline to be later trimmed by the thickened line.
|
||||
Vector hook_vector = ((hook_length + 1.16 * scaled_trim_distance) * hook_vector_norm).cast<coord_t>();
|
||||
Line hook_forward(hook_start, hook_start + hook_vector);
|
||||
|
||||
auto filter_itself = [&intersection, &lines_src](const auto &item) { return item.second != intersection.intersect_line - lines_src.data(); };
|
||||
|
||||
std::vector<std::pair<rtree_segment_t, size_t>> hook_intersections;
|
||||
rtree.query(bgi::intersects(mk_rtree_seg(hook_forward)) && bgi::satisfies(filter_itself), std::back_inserter(hook_intersections));
|
||||
Point self_intersection_point;
|
||||
bool self_intersection = other_hook && other_hook->intersection(hook_forward, &self_intersection_point);
|
||||
|
||||
// Find closest intersection of a line segment starting with pt pointing in dir
|
||||
// with any of the hook_intersections, returns Euclidian distance.
|
||||
// dir is normalized.
|
||||
auto max_hook_length = [hook_length, scaled_trim_distance, &lines_src](
|
||||
const Vec2d &pt, const Vec2d &dir,
|
||||
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) {
|
||||
// No hook is longer than hook_length, there shouldn't be any intersection closer than that.
|
||||
auto max_length = hook_length;
|
||||
auto update_max_length = [&max_length](double d) {
|
||||
if (d < max_length)
|
||||
max_length = d;
|
||||
};
|
||||
// Shift the trimming point away from the colliding thick line.
|
||||
auto shift_from_thick_line = [&dir, scaled_trim_distance](const Vec2d& dir2) {
|
||||
return scaled_trim_distance * std::abs(cross2(dir, dir2.normalized()));
|
||||
};
|
||||
|
||||
for (const auto &hook_intersection : hook_intersections) {
|
||||
const rtree_segment_t &segment = hook_intersection.first;
|
||||
// Segment start and end points, segment vector.
|
||||
Vec2d pt2(bg::get<0, 0>(segment), bg::get<0, 1>(segment));
|
||||
Vec2d dir2 = Vec2d(bg::get<1, 0>(segment), bg::get<1, 1>(segment)) - pt2;
|
||||
// Find intersection of (pt, dir) with (pt2, dir2), where dir is normalized.
|
||||
double denom = cross2(dir, dir2);
|
||||
assert(std::abs(denom) > EPSILON);
|
||||
double t = cross2(pt2 - pt, dir2) / denom;
|
||||
if (hook_intersection.second < lines_src.size())
|
||||
// Trimming by another infill line. Reduce overlap.
|
||||
t -= shift_from_thick_line(dir2);
|
||||
update_max_length(t);
|
||||
}
|
||||
if (self_intersection) {
|
||||
double t = (self_intersection_point.cast<double>() - pt).dot(dir) - shift_from_thick_line((*self_intersection_line).vector().cast<double>());
|
||||
max_length = std::min(max_length, t);
|
||||
}
|
||||
return std::max(0., max_length);
|
||||
};
|
||||
|
||||
Vec2d hook_startf = hook_start.cast<double>();
|
||||
double hook_forward_max_length = max_hook_length(hook_startf, hook_vector_norm, hook_intersections, self_intersection, other_hook, self_intersection_point);
|
||||
double hook_backward_max_length = 0.;
|
||||
if (hook_forward_max_length < hook_length - SCALED_EPSILON) {
|
||||
// Try the other side.
|
||||
hook_intersections.clear();
|
||||
Line hook_backward(hook_start, hook_start - hook_vector);
|
||||
rtree.query(bgi::intersects(mk_rtree_seg(hook_backward)) && bgi::satisfies(filter_itself), std::back_inserter(hook_intersections));
|
||||
self_intersection = other_hook && other_hook->intersection(hook_backward, &self_intersection_point);
|
||||
hook_backward_max_length = max_hook_length(hook_startf, - hook_vector_norm, hook_intersections, self_intersection, other_hook, self_intersection_point);
|
||||
}
|
||||
|
||||
// Take the longer hook.
|
||||
Vec2d hook_dir = (hook_forward_max_length > hook_backward_max_length ? hook_forward_max_length : - hook_backward_max_length) * hook_vector_norm;
|
||||
Point hook_end = hook_start + hook_dir.cast<coord_t>();
|
||||
|
||||
Points &pl = intersection.intersect_pl->points;
|
||||
if (intersection.front) {
|
||||
pl.front() = hook_start;
|
||||
pl.emplace(pl.begin(), hook_end);
|
||||
} else {
|
||||
pl.back() = hook_start;
|
||||
pl.emplace_back(hook_end);
|
||||
}
|
||||
}
|
||||
|
||||
#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;
|
||||
size_t poly_idx = 0;
|
||||
|
||||
// 19% overlap, slightly lower than the allowed overlap in Fill::connect_infill()
|
||||
const float scaled_offset = float(scale_(spacing) * 0.81);
|
||||
// 25% overlap
|
||||
const float scaled_trim_distance = float(scale_(spacing) * 0.5 * 0.75);
|
||||
|
||||
// Keeping the vector of closest points outside the loop, so the vector does not need to be reallocated.
|
||||
std::vector<std::pair<rtree_segment_t, size_t>> closest;
|
||||
// Pairs of lines touching at one end point. The pair is sorted to make the end point connection test symmetric.
|
||||
std::vector<std::pair<const Polyline*, const Polyline*>> lines_touching_at_endpoints;
|
||||
{
|
||||
// Insert infill lines into rtree, merge close collinear segments split by the infill boundary,
|
||||
// collect lines_touching_at_endpoints.
|
||||
double r2_close = Slic3r::sqr(1200.);
|
||||
for (Polyline &poly : lines) {
|
||||
assert(poly.points.size() == 2);
|
||||
if (&poly != lines.data()) {
|
||||
// Join collinear segments separated by a tiny gap. These gaps were likely created by clipping the infill lines with a concave dent in an infill boundary.
|
||||
auto collinear_segment = [&rtree, &closest, &lines, &lines_touching_at_endpoints, r2_close](const Point& pt, const Point& pt_other, const Polyline* polyline) -> std::pair<Polyline*, bool> {
|
||||
closest.clear();
|
||||
rtree.query(bgi::nearest(mk_rtree_point(pt), 1), std::back_inserter(closest));
|
||||
const Polyline *other = &lines[closest.front().second];
|
||||
double dist2_front = (other->points.front() - pt).cast<double>().squaredNorm();
|
||||
double dist2_back = (other->points.back() - pt).cast<double>().squaredNorm();
|
||||
double dist2_min = std::min(dist2_front, dist2_back);
|
||||
if (dist2_min < r2_close) {
|
||||
// Don't connect the segments in an opposite direction.
|
||||
double dist2_min_other = std::min((other->points.front() - pt_other).cast<double>().squaredNorm(), (other->points.back() - pt_other).cast<double>().squaredNorm());
|
||||
if (dist2_min_other > dist2_min) {
|
||||
// End points of the two lines are very close, they should have been merged together if they are collinear.
|
||||
Vec2d v1 = (pt_other - pt).cast<double>();
|
||||
Vec2d v2 = (other->points.back() - other->points.front()).cast<double>();
|
||||
Vec2d v1n = v1.normalized();
|
||||
Vec2d v2n = v2.normalized();
|
||||
// The vectors must not be collinear.
|
||||
double d = v1n.dot(v2n);
|
||||
if (std::abs(d) > 0.99f) {
|
||||
// Lines are collinear, merge them.
|
||||
rtree.remove(closest.front());
|
||||
return std::make_pair(const_cast<Polyline*>(other), dist2_min == dist2_front);
|
||||
} else {
|
||||
if (polyline > other)
|
||||
std::swap(polyline, other);
|
||||
lines_touching_at_endpoints.emplace_back(polyline, other);
|
||||
}
|
||||
}
|
||||
}
|
||||
return std::make_pair(static_cast<Polyline*>(nullptr), false);
|
||||
};
|
||||
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) {
|
||||
Polyline &other = *collinear_front.first;
|
||||
assert(&other != &poly);
|
||||
poly.points.front() = collinear_front.second ? other.points.back() : other.points.front();
|
||||
other.points.clear();
|
||||
}
|
||||
if (collinear_back.first) {
|
||||
Polyline &other = *collinear_back.first;
|
||||
assert(&other != &poly);
|
||||
poly.points.back() = collinear_back.second ? other.points.back() : other.points.front();
|
||||
other.points.clear();
|
||||
}
|
||||
}
|
||||
rtree.insert(std::make_pair(mk_rtree_seg(poly.points.front(), poly.points.back()), poly_idx++));
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
std::vector<Intersection> intersections;
|
||||
{
|
||||
// Minimum lenght of an infill line to anchor. Very short lines cannot be trimmed from both sides,
|
||||
// it does not help to anchor extremely short infill lines, it consumes too much plastic while not adding
|
||||
// to the object rigidity.
|
||||
assert(scaled_offset > scaled_trim_distance);
|
||||
const double line_len_threshold_drop_both_sides = scaled_offset * (2. / cos(PI / 6.) + 0.5) + SCALED_EPSILON;
|
||||
const double line_len_threshold_anchor_both_sides = line_len_threshold_drop_both_sides + scaled_offset;
|
||||
const double line_len_threshold_drop_single_side = scaled_offset * (1. / cos(PI / 6.) + 1.5) + SCALED_EPSILON;
|
||||
const double line_len_threshold_anchor_single_side = line_len_threshold_drop_single_side + scaled_offset;
|
||||
for (size_t line_idx = 0; line_idx < lines.size(); ++ line_idx) {
|
||||
Polyline &line = lines[line_idx];
|
||||
if (line.points.empty())
|
||||
continue;
|
||||
|
||||
Point &front_point = line.points.front();
|
||||
Point &back_point = line.points.back();
|
||||
|
||||
// Find the nearest line from the start point of the line.
|
||||
std::optional<size_t> tjoint_front, tjoint_back;
|
||||
{
|
||||
auto has_tjoint = [&closest, line_idx, &rtree, &lines, &lines_src](const Point &pt) {
|
||||
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();
|
||||
rtree.query(bgi::nearest(mk_rtree_point(pt), 1) && bgi::satisfies(filter_t_joint), std::back_inserter(closest));
|
||||
std::optional<size_t> out;
|
||||
if (! closest.empty()) {
|
||||
const Polyline &pl = lines[closest.front().second];
|
||||
if (pl.points.empty()) {
|
||||
// The closest infill line was already dropped as it was too short.
|
||||
// Such an infill line should not make a T-joint anyways.
|
||||
#if 0 // #ifndef NDEBUG
|
||||
const auto &seg = closest.front().first;
|
||||
struct Linef { Vec2d a; Vec2d b; };
|
||||
Linef l { { bg::get<0, 0>(seg), bg::get<0, 1>(seg) }, { bg::get<1, 0>(seg), bg::get<1, 1>(seg) } };
|
||||
assert(line_alg::distance_to_squared(l, Vec2d(pt.cast<double>())) > 1000 * 1000);
|
||||
#endif // NDEBUG
|
||||
} else if (((Line)pl).distance_to_squared(pt) <= 1000 * 1000)
|
||||
out = closest.front().second;
|
||||
}
|
||||
return out;
|
||||
};
|
||||
// Refuse to create a T-joint if the infill lines touch at their ends.
|
||||
auto filter_end_point_connections = [&lines_touching_at_endpoints, &lines, &line](std::optional<size_t> in) {
|
||||
std::optional<size_t> out;
|
||||
if (in) {
|
||||
const Polyline *lo = &line;
|
||||
const Polyline *hi = &lines[*in];
|
||||
if (lo > hi)
|
||||
std::swap(lo, hi);
|
||||
if (! std::binary_search(lines_touching_at_endpoints.begin(), lines_touching_at_endpoints.end(), std::make_pair(lo, hi)))
|
||||
// Not an end-point connection, it is a valid T-joint.
|
||||
out = in;
|
||||
}
|
||||
return out;
|
||||
};
|
||||
tjoint_front = filter_end_point_connections(has_tjoint(front_point));
|
||||
tjoint_back = filter_end_point_connections(has_tjoint(back_point));
|
||||
}
|
||||
|
||||
int num_tjoints = int(tjoint_front.has_value()) + int(tjoint_back.has_value());
|
||||
if (num_tjoints > 0) {
|
||||
double line_len = line.length();
|
||||
bool drop = false;
|
||||
bool anchor = false;
|
||||
if (num_tjoints == 1) {
|
||||
// Connected to perimeters on a single side only, connected to another infill line on the other side.
|
||||
drop = line_len < line_len_threshold_drop_single_side;
|
||||
anchor = line_len > line_len_threshold_anchor_single_side;
|
||||
} else {
|
||||
// Not connected to perimeters at all, connected to two infill lines.
|
||||
assert(num_tjoints == 2);
|
||||
drop = line_len < line_len_threshold_drop_both_sides;
|
||||
anchor = line_len > line_len_threshold_anchor_both_sides;
|
||||
}
|
||||
if (drop) {
|
||||
// Drop a very short line if connected to another infill line.
|
||||
// Lines shorter than spacing are skipped because it is needed to shrink a line by the value of spacing.
|
||||
// A shorter line than spacing could produce a degenerate polyline.
|
||||
line.points.clear();
|
||||
} else if (anchor) {
|
||||
if (tjoint_front) {
|
||||
// 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);
|
||||
assert(validate_intersection_t_joint(intersections.back()));
|
||||
}
|
||||
if (tjoint_back) {
|
||||
// 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);
|
||||
assert(validate_intersection_t_joint(intersections.back()));
|
||||
}
|
||||
} else {
|
||||
if (tjoint_front)
|
||||
// T joint at the front at a 60 degree angle, the line is very short.
|
||||
// Trim the front side.
|
||||
front_point += ((scaled_trim_distance * 1.155) * (back_point - front_point).cast<double>().normalized()).cast<coord_t>();
|
||||
if (tjoint_back)
|
||||
// T joint at the front at a 60 degree angle, the line is very short.
|
||||
// Trim the front side.
|
||||
back_point += ((scaled_trim_distance * 1.155) * (front_point - back_point).cast<double>().normalized()).cast<coord_t>();
|
||||
}
|
||||
}
|
||||
}
|
||||
// Remove those intersections, that point to a dropped line.
|
||||
for (auto it = intersections.begin(); it != intersections.end(); ) {
|
||||
assert(! lines[it->intersect_line - lines_src.data()].points.empty());
|
||||
if (lines[it->closest_line - lines_src.data()].points.empty()) {
|
||||
*it = intersections.back();
|
||||
intersections.pop_back();
|
||||
} else
|
||||
++ it;
|
||||
}
|
||||
}
|
||||
assert(validate_intersections(intersections));
|
||||
|
||||
#ifdef ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
|
||||
static int iRun = 0;
|
||||
int iStep = 0;
|
||||
{
|
||||
Points pts;
|
||||
for (const Intersection &i : intersections)
|
||||
pts.emplace_back(i.intersect_point);
|
||||
export_infill_lines_to_svg(boundary, lines, debug_out_path("FillAdaptive-Tjoints-%d.svg", iRun++), pts);
|
||||
}
|
||||
#endif /* ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT */
|
||||
|
||||
// Sort lexicographically by closest_line_idx and left/right orientation.
|
||||
std::sort(intersections.begin(), intersections.end(),
|
||||
[](const Intersection &i1, const Intersection &i2) {
|
||||
return (i1.closest_line == i2.closest_line) ?
|
||||
int(i1.left) < int(i2.left) :
|
||||
i1.closest_line < i2.closest_line;
|
||||
});
|
||||
|
||||
std::vector<size_t> merged_with(lines.size());
|
||||
std::iota(merged_with.begin(), merged_with.end(), 0);
|
||||
|
||||
// Appends the boundary polygon with all holes to rtree for detection to check whether hooks are not crossing the boundary
|
||||
{
|
||||
Point prev = boundary.contour.points.back();
|
||||
for (const Point &point : boundary.contour.points) {
|
||||
rtree.insert(std::make_pair(mk_rtree_seg(prev, point), poly_idx++));
|
||||
prev = point;
|
||||
}
|
||||
for (const Polygon &polygon : boundary.holes) {
|
||||
Point prev = polygon.points.back();
|
||||
for (const Point &point : polygon.points) {
|
||||
rtree.insert(std::make_pair(mk_rtree_seg(prev, point), poly_idx++));
|
||||
prev = point;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto update_merged_polyline_idx = [&merged_with](size_t pl_idx) {
|
||||
// Update the polyline index to index which is merged
|
||||
for (size_t last = pl_idx;;) {
|
||||
size_t lower = merged_with[last];
|
||||
if (lower == last) {
|
||||
merged_with[pl_idx] = lower;
|
||||
return lower;
|
||||
}
|
||||
last = lower;
|
||||
}
|
||||
assert(false);
|
||||
return size_t(0);
|
||||
};
|
||||
auto update_merged_polyline = [&lines, update_merged_polyline_idx](Intersection& intersection) {
|
||||
// Update the polyline index to index which is merged
|
||||
size_t intersect_pl_idx = update_merged_polyline_idx(intersection.intersect_pl - lines.data());
|
||||
intersection.intersect_pl = &lines[intersect_pl_idx];
|
||||
// After polylines are merged, it is necessary to update "forward" based on if intersect_point is the first or the last point of intersect_pl.
|
||||
if (intersection.fresh()) {
|
||||
assert(intersection.intersect_pl->points.front() == intersection.intersect_point ||
|
||||
intersection.intersect_pl->points.back() == intersection.intersect_point);
|
||||
intersection.front = intersection.intersect_pl->points.front() == intersection.intersect_point;
|
||||
}
|
||||
};
|
||||
|
||||
// Merge polylines touching at their ends. This should be a very rare case, but it happens surprisingly often.
|
||||
for (auto it = lines_touching_at_endpoints.rbegin(); it != lines_touching_at_endpoints.rend(); ++ it) {
|
||||
Polyline *pl1 = const_cast<Polyline*>(it->first);
|
||||
Polyline *pl2 = const_cast<Polyline*>(it->second);
|
||||
assert(pl1 < pl2);
|
||||
// pl1 was visited for the 1st time.
|
||||
// pl2 may have alread been merged with another polyline, even with this one.
|
||||
pl2 = &lines[update_merged_polyline_idx(pl2 - lines.data())];
|
||||
assert(pl1 <= pl2);
|
||||
// Avoid closing a loop, ignore dropped infill lines.
|
||||
if (pl1 != pl2 && ! pl1->points.empty() && ! pl2->points.empty()) {
|
||||
// Merge the polylines.
|
||||
assert(pl1 < pl2);
|
||||
assert(pl1->points.size() >= 2);
|
||||
assert(pl2->points.size() >= 2);
|
||||
double d11 = (pl1->points.front() - pl2->points.front()).cast<double>().squaredNorm();
|
||||
double d12 = (pl1->points.front() - pl2->points.back()) .cast<double>().squaredNorm();
|
||||
double d21 = (pl1->points.back() - pl2->points.front()).cast<double>().squaredNorm();
|
||||
double d22 = (pl1->points.back() - pl2->points.back()) .cast<double>().squaredNorm();
|
||||
double d1min = std::min(d11, d12);
|
||||
double d2min = std::min(d21, d22);
|
||||
if (d1min < d2min) {
|
||||
pl1->reverse();
|
||||
if (d12 == d1min)
|
||||
pl2->reverse();
|
||||
} else if (d22 == d2min)
|
||||
pl2->reverse();
|
||||
pl1->points.back() = (pl1->points.back() + pl2->points.front()) / 2;
|
||||
pl1->append(pl2->points.begin() + 1, pl2->points.end());
|
||||
pl2->points.clear();
|
||||
merged_with[pl2 - lines.data()] = pl1 - lines.data();
|
||||
}
|
||||
}
|
||||
|
||||
// Keep intersect_line outside the loop, so it does not get reallocated.
|
||||
std::vector<std::pair<Intersection*, double>> intersect_line;
|
||||
for (size_t min_idx = 0; min_idx < intersections.size();) {
|
||||
intersect_line.clear();
|
||||
// All the nearest points (T-joints) ending at the same line are projected onto this line. Because of it, it can easily find the nearest point.
|
||||
{
|
||||
const Vec2d line_dir = intersections[min_idx].closest_line->vector().cast<double>();
|
||||
size_t max_idx = min_idx;
|
||||
for (; max_idx < intersections.size() &&
|
||||
intersections[min_idx].closest_line == intersections[max_idx].closest_line &&
|
||||
intersections[min_idx].left == intersections[max_idx].left;
|
||||
++ max_idx)
|
||||
intersect_line.emplace_back(&intersections[max_idx], line_dir.dot(intersections[max_idx].intersect_point.cast<double>()));
|
||||
min_idx = max_idx;
|
||||
assert(intersect_line.size() > 0);
|
||||
// Sort the intersections along line_dir.
|
||||
std::sort(intersect_line.begin(), intersect_line.end(), [](const auto &i1, const auto &i2) { return i1.second < i2.second; });
|
||||
}
|
||||
|
||||
if (intersect_line.size() == 1) {
|
||||
// Simple case: The current intersection is the only one touching its adjacent line.
|
||||
Intersection &first_i = *intersect_line.front().first;
|
||||
update_merged_polyline(first_i);
|
||||
if (first_i.fresh()) {
|
||||
// Try to connect left or right. If not enough space for hook_length, take the longer side.
|
||||
#ifdef ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
|
||||
export_infill_lines_to_svg(boundary, lines, debug_out_path("FillAdaptive-add_hook0-pre-%d-%d.svg", iRun, iStep), { first_i.intersect_point });
|
||||
#endif // ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
|
||||
add_hook(first_i, scaled_offset, hook_length, scaled_trim_distance, rtree, lines_src);
|
||||
#ifdef ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
|
||||
export_infill_lines_to_svg(boundary, lines, debug_out_path("FillAdaptive-add_hook0-pre-%d-%d.svg", iRun, iStep), { first_i.intersect_point });
|
||||
++ iStep;
|
||||
#endif // ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
|
||||
first_i.used = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
for (size_t first_idx = 0; first_idx < intersect_line.size(); ++ first_idx) {
|
||||
Intersection &first_i = *intersect_line[first_idx].first;
|
||||
update_merged_polyline(first_i);
|
||||
if (! first_i.fresh())
|
||||
// The intersection has been processed, or the polyline has been merged to another polyline.
|
||||
continue;
|
||||
|
||||
// Get the previous or next intersection on the same line, pick the closer one.
|
||||
if (first_idx > 0)
|
||||
update_merged_polyline(*intersect_line[first_idx - 1].first);
|
||||
if (first_idx + 1 < intersect_line.size())
|
||||
update_merged_polyline(*intersect_line[first_idx + 1].first);
|
||||
Intersection &nearest_i = *get_nearest_intersection(intersect_line, first_idx);
|
||||
assert(first_i.closest_line == nearest_i.closest_line);
|
||||
assert(first_i.intersect_line != nearest_i.intersect_line);
|
||||
assert(first_i.intersect_line != first_i.closest_line);
|
||||
assert(nearest_i.intersect_line != first_i.closest_line);
|
||||
// A line between two intersections points
|
||||
Line offset_line = create_offset_line(Line(first_i.intersect_point, nearest_i.intersect_point), first_i, scaled_offset);
|
||||
// Check if both intersections lie on the offset_line and simultaneously get their points of intersecting.
|
||||
// These points are used as start and end of the hook
|
||||
Point first_i_point, nearest_i_point;
|
||||
bool could_connect = false;
|
||||
if (nearest_i.fresh()) {
|
||||
could_connect = first_i.intersect_line->intersection(offset_line, &first_i_point) &&
|
||||
nearest_i.intersect_line->intersection(offset_line, &nearest_i_point);
|
||||
assert(could_connect);
|
||||
}
|
||||
Points &first_points = first_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(hook_length_max);
|
||||
if (could_connect) {
|
||||
// Both intersections are so close that their polylines can be connected.
|
||||
// Verify that no other infill line intersects this anchor line.
|
||||
closest.clear();
|
||||
rtree.query(
|
||||
bgi::intersects(mk_rtree_seg(first_i_point, nearest_i_point)) &&
|
||||
bgi::satisfies([&first_i, &nearest_i, &lines_src](const auto &item)
|
||||
{ return item.second != first_i.intersect_line - lines_src.data() && item.second != nearest_i.intersect_line - lines_src.data(); }),
|
||||
std::back_inserter(closest));
|
||||
could_connect = closest.empty();
|
||||
#if 0
|
||||
// Avoid self intersections. Maybe it is better to trim the self intersection after the connection?
|
||||
if (could_connect && first_i.intersect_pl != nearest_i.intersect_pl) {
|
||||
Line l(first_i_point, nearest_i_point);
|
||||
could_connect = ! first_i.other_hook_intersects(l) && ! nearest_i.other_hook_intersects(l);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
bool connected = false;
|
||||
if (could_connect) {
|
||||
#ifdef ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
|
||||
export_infill_lines_to_svg(boundary, lines, debug_out_path("FillAdaptive-connecting-pre-%d-%d.svg", iRun, iStep), { first_i.intersect_point, nearest_i.intersect_point });
|
||||
#endif // ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
|
||||
// No other infill line intersects this anchor line. Extrude it as a whole.
|
||||
if (first_i.intersect_pl == nearest_i.intersect_pl) {
|
||||
// Both intersections are on the same polyline, that means a loop is being closed.
|
||||
assert(first_i.front != nearest_i.front);
|
||||
if (! first_i.front)
|
||||
std::swap(first_i_point, nearest_i_point);
|
||||
first_points.front() = first_i_point;
|
||||
first_points.back() = nearest_i_point;
|
||||
//FIXME trim the end of a closed loop a bit?
|
||||
first_points.emplace(first_points.begin(), nearest_i_point);
|
||||
} else {
|
||||
// Both intersections are on different polylines
|
||||
Line l(first_i_point, nearest_i_point);
|
||||
l.translate((perp(first_i.closest_line->vector().cast<double>().normalized()) * (first_i.left ? scaled_trim_distance : - scaled_trim_distance)).cast<coord_t>());
|
||||
Point pt_start, pt_end;
|
||||
bool trim_start = first_i .intersect_pl->points.size() == 3 && first_i .other_hook_intersects(l, pt_start);
|
||||
bool trim_end = nearest_i.intersect_pl->points.size() == 3 && nearest_i.other_hook_intersects(l, pt_end);
|
||||
first_points.reserve(first_points.size() + second_points.size());
|
||||
if (first_i.front)
|
||||
std::reverse(first_points.begin(), first_points.end());
|
||||
if (trim_start)
|
||||
first_points.front() = pt_start;
|
||||
first_points.back() = first_i_point;
|
||||
first_points.emplace_back(nearest_i_point);
|
||||
if (nearest_i.front)
|
||||
first_points.insert(first_points.end(), second_points.begin() + 1, second_points.end());
|
||||
else
|
||||
first_points.insert(first_points.end(), second_points.rbegin() + 1, second_points.rend());
|
||||
if (trim_end)
|
||||
first_points.back() = pt_end;
|
||||
// Keep the polyline at the lower index slot.
|
||||
if (first_i.intersect_pl < nearest_i.intersect_pl) {
|
||||
second_points.clear();
|
||||
merged_with[nearest_i.intersect_pl - lines.data()] = first_i.intersect_pl - lines.data();
|
||||
} else {
|
||||
second_points = std::move(first_points);
|
||||
first_points.clear();
|
||||
merged_with[first_i.intersect_pl - lines.data()] = nearest_i.intersect_pl - lines.data();
|
||||
}
|
||||
}
|
||||
nearest_i.used = true;
|
||||
connected = true;
|
||||
#ifdef ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
|
||||
export_infill_lines_to_svg(boundary, lines, debug_out_path("FillAdaptive-connecting-post-%d-%d.svg", iRun, iStep), { first_i.intersect_point, nearest_i.intersect_point });
|
||||
#endif // ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
|
||||
}
|
||||
if (! connected) {
|
||||
// Try to connect left or right. If not enough space for hook_length, take the longer side.
|
||||
#ifdef ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
|
||||
export_infill_lines_to_svg(boundary, lines, debug_out_path("FillAdaptive-add_hook-pre-%d-%d.svg", iRun, iStep), { first_i.intersect_point });
|
||||
#endif // ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
|
||||
add_hook(first_i, scaled_offset, hook_length, scaled_trim_distance, rtree, lines_src);
|
||||
#ifdef ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
|
||||
export_infill_lines_to_svg(boundary, lines, debug_out_path("FillAdaptive-add_hook-post-%d-%d.svg", iRun, iStep), { first_i.intersect_point });
|
||||
#endif // ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
|
||||
}
|
||||
#ifdef ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
|
||||
++ iStep;
|
||||
#endif ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
|
||||
first_i.used = true;
|
||||
}
|
||||
}
|
||||
|
||||
Polylines polylines_out;
|
||||
polylines_out.reserve(polylines_out.size() + std::count_if(lines.begin(), lines.end(), [](const Polyline &pl) { return !pl.empty(); }));
|
||||
for (Polyline &pl : lines)
|
||||
if (!pl.empty()) polylines_out.emplace_back(std::move(pl));
|
||||
return polylines_out;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
bool has_no_collinear_lines(const Polylines &polylines)
|
||||
{
|
||||
// Create line end point lookup.
|
||||
struct LineEnd {
|
||||
LineEnd(const Polyline *line, bool start) : line(line), start(start) {}
|
||||
const Polyline *line;
|
||||
// Is it the start or end point?
|
||||
bool start;
|
||||
const Point& point() const { return start ? line->points.front() : line->points.back(); }
|
||||
const Point& other_point() const { return start ? line->points.back() : line->points.front(); }
|
||||
LineEnd other_end() const { return LineEnd(line, !start); }
|
||||
Vec2d vec() const { return Vec2d((this->other_point() - this->point()).cast<double>()); }
|
||||
bool operator==(const LineEnd &rhs) const { return this->line == rhs.line && this->start == rhs.start; }
|
||||
};
|
||||
struct LineEndAccessor {
|
||||
const Point* operator()(const LineEnd &pt) const { return &pt.point(); }
|
||||
};
|
||||
typedef ClosestPointInRadiusLookup<LineEnd, LineEndAccessor> ClosestPointLookupType;
|
||||
ClosestPointLookupType closest_end_point_lookup(coord_t(1001. * sqrt(2.)));
|
||||
for (const Polyline& pl : polylines) {
|
||||
// assert(pl.points.size() == 2);
|
||||
auto line_start = LineEnd(&pl, true);
|
||||
auto line_end = LineEnd(&pl, false);
|
||||
|
||||
auto assert_not_collinear = [&closest_end_point_lookup](const LineEnd &line_start) {
|
||||
std::vector<std::pair<const LineEnd*, double>> hits = closest_end_point_lookup.find_all(line_start.point());
|
||||
for (const std::pair<const LineEnd*, double> &hit : hits)
|
||||
if ((line_start.point() - hit.first->point()).cwiseAbs().maxCoeff() <= 1000) {
|
||||
// End points of the two lines are very close, they should have been merged together if they are collinear.
|
||||
Vec2d v1 = line_start.vec();
|
||||
Vec2d v2 = hit.first->vec();
|
||||
Vec2d v1n = v1.normalized();
|
||||
Vec2d v2n = v2.normalized();
|
||||
// The vectors must not be collinear.
|
||||
assert(std::abs(v1n.dot(v2n)) < cos(M_PI / 12.));
|
||||
}
|
||||
};
|
||||
assert_not_collinear(line_start);
|
||||
assert_not_collinear(line_end);
|
||||
|
||||
closest_end_point_lookup.insert(line_start);
|
||||
closest_end_point_lookup.insert(line_end);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
void Filler::_fill_surface_single(
|
||||
const FillParams & params,
|
||||
const FillParams ¶ms,
|
||||
unsigned int thickness_layers,
|
||||
const std::pair<float, Point> &direction,
|
||||
ExPolygon &expolygon,
|
||||
ExPolygon expolygon,
|
||||
Polylines &polylines_out)
|
||||
{
|
||||
assert (this->adapt_fill_octree);
|
||||
|
|
@ -569,6 +1335,23 @@ void Filler::_fill_surface_single(
|
|||
generate_infill_lines_recursive(context, adapt_fill_octree->root_cube, 0, int(adapt_fill_octree->cubes_properties.size()) - 1);
|
||||
num_lines += context.output_lines.size() + context.temp_lines.size();
|
||||
}
|
||||
|
||||
#if 0
|
||||
// Collect the lines, trim them by the expolygon.
|
||||
all_polylines.reserve(num_lines);
|
||||
auto boundary = to_polygons(expolygon);
|
||||
for (auto &context : contexts) {
|
||||
Polylines lines;
|
||||
lines.reserve(context.output_lines.size() + context.temp_lines.size());
|
||||
std::transform(context.output_lines.begin(), context.output_lines.end(), std::back_inserter(lines), [](const Line& l) { return Polyline{ l.a, l.b }; });
|
||||
for (const Line &l : context.temp_lines)
|
||||
if (l.a.x() != std::numeric_limits<coord_t>::max())
|
||||
lines.push_back({ l.a, l.b });
|
||||
// Crop all polylines
|
||||
append(all_polylines, intersection_pl(std::move(lines), boundary));
|
||||
}
|
||||
// assert(has_no_collinear_lines(all_polylines));
|
||||
#else
|
||||
// Collect the lines.
|
||||
std::vector<Line> lines;
|
||||
lines.reserve(num_lines);
|
||||
|
|
@ -578,18 +1361,21 @@ void Filler::_fill_surface_single(
|
|||
if (line.a.x() != std::numeric_limits<coord_t>::max())
|
||||
lines.emplace_back(line);
|
||||
}
|
||||
#if 0
|
||||
// Chain touching line segments, convert lines to polylines.
|
||||
//all_polylines = chain_lines(lines, 300.); // SCALED_EPSILON is 100 and it is not enough
|
||||
#else
|
||||
// Convert lines to polylines.
|
||||
all_polylines.reserve(lines.size());
|
||||
std::transform(lines.begin(), lines.end(), std::back_inserter(all_polylines), [](const Line& l) { return Polyline{ l.a, l.b }; });
|
||||
// Crop all polylines
|
||||
all_polylines = intersection_pl(std::move(all_polylines), to_polygons(expolygon));
|
||||
#endif
|
||||
}
|
||||
|
||||
// Crop all polylines
|
||||
all_polylines = intersection_pl(std::move(all_polylines), to_polygons(expolygon));
|
||||
// After intersection_pl some polylines with only one line are split into more lines
|
||||
for (Polyline &polyline : all_polylines) {
|
||||
//FIXME assert that all the points are collinear and in between the start and end point.
|
||||
if (polyline.points.size() > 2)
|
||||
polyline.points.erase(polyline.points.begin() + 1, polyline.points.end() - 1);
|
||||
}
|
||||
// assert(has_no_collinear_lines(all_polylines));
|
||||
|
||||
#ifdef ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
|
||||
{
|
||||
|
|
@ -598,10 +1384,22 @@ void Filler::_fill_surface_single(
|
|||
}
|
||||
#endif /* ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT */
|
||||
|
||||
if (params.dont_connect || all_polylines.size() <= 1)
|
||||
append(polylines_out, std::move(all_polylines));
|
||||
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, hook_length_max) : std::move(all_polylines);
|
||||
|
||||
#ifdef ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
|
||||
{
|
||||
static int iRun = 0;
|
||||
export_infill_lines_to_svg(expolygon, all_polylines_with_hooks, debug_out_path("FillAdaptive-hooks-%d.svg", iRun++));
|
||||
}
|
||||
#endif /* ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT */
|
||||
|
||||
if (params.dont_connect() || all_polylines_with_hooks.size() <= 1)
|
||||
append(polylines_out, chain_polylines(std::move(all_polylines_with_hooks)));
|
||||
else
|
||||
connect_infill(chain_polylines(std::move(all_polylines)), expolygon, polylines_out, this->spacing, params);
|
||||
connect_infill(std::move(all_polylines_with_hooks), expolygon, polylines_out, this->spacing, params);
|
||||
|
||||
#ifdef ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
|
||||
{
|
||||
|
|
|
|||
|
|
@ -56,17 +56,17 @@ FillAdaptive::OctreePtr build_octree(
|
|||
class Filler : public Slic3r::Fill
|
||||
{
|
||||
public:
|
||||
virtual ~Filler() {}
|
||||
~Filler() override {}
|
||||
|
||||
protected:
|
||||
virtual Fill* clone() const { return new Filler(*this); };
|
||||
virtual void _fill_surface_single(
|
||||
Fill* clone() const override { return new Filler(*this); };
|
||||
void _fill_surface_single(
|
||||
const FillParams ¶ms,
|
||||
unsigned int thickness_layers,
|
||||
const std::pair<float, Point> &direction,
|
||||
ExPolygon &expolygon,
|
||||
Polylines &polylines_out);
|
||||
virtual bool no_sort() const { return true; }
|
||||
ExPolygon expolygon,
|
||||
Polylines &polylines_out) override;
|
||||
bool no_sort() const override { return true; }
|
||||
};
|
||||
|
||||
}; // namespace FillAdaptive
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -33,12 +33,16 @@ public:
|
|||
struct FillParams
|
||||
{
|
||||
bool full_infill() const { return density > 0.9999f; }
|
||||
// Don't connect the fill lines around the inner perimeter.
|
||||
bool dont_connect() const { return anchor_length_max < 0.05f; }
|
||||
|
||||
// Fill density, fraction in <0, 1>
|
||||
float density { 0.f };
|
||||
|
||||
// Don't connect the fill lines around the inner perimeter.
|
||||
bool dont_connect { false };
|
||||
// Length of an infill anchor along the perimeter.
|
||||
// 1000mm is roughly the maximum length line that fits into a 32bit coord_t.
|
||||
float anchor_length { 1000.f };
|
||||
float anchor_length_max { 1000.f };
|
||||
|
||||
// Don't adjust spacing to fill the space evenly.
|
||||
bool dont_adjust { true };
|
||||
|
|
@ -80,6 +84,7 @@ public:
|
|||
|
||||
public:
|
||||
virtual ~Fill() {}
|
||||
virtual Fill* clone() const = 0;
|
||||
|
||||
static Fill* new_from_type(const InfillPattern type);
|
||||
static Fill* new_from_type(const std::string &type);
|
||||
|
|
@ -116,7 +121,7 @@ protected:
|
|||
const FillParams & /* params */,
|
||||
unsigned int /* thickness_layers */,
|
||||
const std::pair<float, Point> & /* direction */,
|
||||
ExPolygon & /* expolygon */,
|
||||
ExPolygon /* expolygon */,
|
||||
Polylines & /* polylines_out */) {};
|
||||
|
||||
virtual float _layer_angle(size_t idx) const { return (idx & 1) ? float(M_PI/2.) : 0; }
|
||||
|
|
@ -124,7 +129,9 @@ protected:
|
|||
virtual std::pair<float, Point> _infill_direction(const Surface *surface) const;
|
||||
|
||||
public:
|
||||
static void connect_infill(Polylines &&infill_ordered, const ExPolygon &boundary, Polylines &polylines_out, double spacing, const FillParams ¶ms);
|
||||
static void connect_infill(Polylines &&infill_ordered, const ExPolygon &boundary, Polylines &polylines_out, const double spacing, const FillParams ¶ms);
|
||||
static void connect_infill(Polylines &&infill_ordered, const Polygons &boundary, const BoundingBox& bbox, Polylines &polylines_out, const double spacing, const FillParams ¶ms);
|
||||
static void connect_infill(Polylines &&infill_ordered, const std::vector<const Polygon*> &boundary, const BoundingBox &bbox, Polylines &polylines_out, double spacing, const FillParams ¶ms);
|
||||
|
||||
static coord_t _adjust_solid_spacing(const coord_t width, const coord_t distance);
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ void FillConcentric::_fill_surface_single(
|
|||
const FillParams ¶ms,
|
||||
unsigned int thickness_layers,
|
||||
const std::pair<float, Point> &direction,
|
||||
ExPolygon &expolygon,
|
||||
ExPolygon expolygon,
|
||||
Polylines &polylines_out)
|
||||
{
|
||||
// no rotation is supported for this infill pattern
|
||||
|
|
@ -39,7 +39,7 @@ void FillConcentric::_fill_surface_single(
|
|||
size_t iPathFirst = polylines_out.size();
|
||||
Point last_pos(0, 0);
|
||||
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();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,18 +8,18 @@ namespace Slic3r {
|
|||
class FillConcentric : public Fill
|
||||
{
|
||||
public:
|
||||
virtual ~FillConcentric() {}
|
||||
~FillConcentric() override {}
|
||||
|
||||
protected:
|
||||
virtual Fill* clone() const { return new FillConcentric(*this); };
|
||||
virtual void _fill_surface_single(
|
||||
Fill* clone() const override { return new FillConcentric(*this); };
|
||||
void _fill_surface_single(
|
||||
const FillParams ¶ms,
|
||||
unsigned int thickness_layers,
|
||||
const std::pair<float, Point> &direction,
|
||||
ExPolygon &expolygon,
|
||||
Polylines &polylines_out);
|
||||
ExPolygon expolygon,
|
||||
Polylines &polylines_out) override;
|
||||
|
||||
virtual bool no_sort() const { return true; }
|
||||
bool no_sort() const override { return true; }
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
|
|
|
|||
|
|
@ -37,12 +37,12 @@ static inline Polyline make_wave(
|
|||
double period = points.back()(0);
|
||||
if (width != period) // do not extend if already truncated
|
||||
{
|
||||
points.reserve(one_period.size() * floor(width / period));
|
||||
points.reserve(one_period.size() * size_t(floor(width / period)));
|
||||
points.pop_back();
|
||||
|
||||
int n = points.size();
|
||||
size_t n = points.size();
|
||||
do {
|
||||
points.emplace_back(Vec2d(points[points.size()-n](0) + period, points[points.size()-n](1)));
|
||||
points.emplace_back(points[points.size()-n].x() + period, points[points.size()-n].y());
|
||||
} while (points.back()(0) < width - EPSILON);
|
||||
|
||||
points.emplace_back(Vec2d(width, f(width, z_sin, z_cos, vertical, flip)));
|
||||
|
|
@ -67,7 +67,7 @@ static std::vector<Vec2d> make_one_period(double width, double scaleFactor, doub
|
|||
std::vector<Vec2d> points;
|
||||
double dx = M_PI_2; // exact coordinates on main inflexion lobes
|
||||
double limit = std::min(2*M_PI, width);
|
||||
points.reserve(ceil(limit / tolerance / 3));
|
||||
points.reserve(coord_t(ceil(limit / tolerance / 3)));
|
||||
|
||||
for (double x = 0.; x < limit - EPSILON; x += dx) {
|
||||
points.emplace_back(Vec2d(x, f(x, z_sin, z_cos, vertical, flip)));
|
||||
|
|
@ -152,10 +152,10 @@ void FillGyroid::_fill_surface_single(
|
|||
const FillParams ¶ms,
|
||||
unsigned int thickness_layers,
|
||||
const std::pair<float, Point> &direction,
|
||||
ExPolygon &expolygon,
|
||||
ExPolygon expolygon,
|
||||
Polylines &polylines_out)
|
||||
{
|
||||
float infill_angle = this->angle + (CorrectionAngle * 2*M_PI) / 360.;
|
||||
auto infill_angle = float(this->angle + (CorrectionAngle * 2*M_PI) / 360.);
|
||||
if(std::abs(infill_angle) >= EPSILON)
|
||||
expolygon.rotate(-infill_angle);
|
||||
|
||||
|
|
@ -182,19 +182,20 @@ void FillGyroid::_fill_surface_single(
|
|||
|
||||
polylines = intersection_pl(polylines, to_polygons(expolygon));
|
||||
|
||||
if (! polylines.empty())
|
||||
// remove too small bits (larger than longer)
|
||||
if (! polylines.empty()) {
|
||||
// Remove very small bits, but be careful to not remove infill lines connecting thin walls!
|
||||
// The infill perimeter lines should be separated by around a single infill line width.
|
||||
const double minlength = scale_(0.8 * this->spacing);
|
||||
polylines.erase(
|
||||
//FIXME what is the small size? Removing tiny extrusions disconnects walls!
|
||||
std::remove_if(polylines.begin(), polylines.end(), [this](const Polyline &pl) { return pl.length() < scale_(this->spacing * 3); }),
|
||||
std::remove_if(polylines.begin(), polylines.end(), [minlength](const Polyline &pl) { return pl.length() < minlength; }),
|
||||
polylines.end());
|
||||
}
|
||||
|
||||
if (! polylines.empty()) {
|
||||
polylines = chain_polylines(polylines);
|
||||
// connect lines
|
||||
size_t polylines_out_first_idx = polylines_out.size();
|
||||
if (params.dont_connect)
|
||||
append(polylines_out, std::move(polylines));
|
||||
if (params.dont_connect())
|
||||
append(polylines_out, chain_polylines(polylines));
|
||||
else
|
||||
this->connect_infill(std::move(polylines), expolygon, polylines_out, this->spacing, params);
|
||||
|
||||
|
|
|
|||
|
|
@ -11,10 +11,10 @@ class FillGyroid : public Fill
|
|||
{
|
||||
public:
|
||||
FillGyroid() {}
|
||||
virtual Fill* clone() const { return new FillGyroid(*this); }
|
||||
Fill* clone() const override { return new FillGyroid(*this); }
|
||||
|
||||
// require bridge flow since most of this pattern hangs in air
|
||||
virtual bool use_bridge_flow() const { return false; }
|
||||
bool use_bridge_flow() const override { return false; }
|
||||
|
||||
// Correction applied to regular infill angle to maximize printing
|
||||
// speed in default configuration (degrees)
|
||||
|
|
@ -28,12 +28,12 @@ public:
|
|||
|
||||
|
||||
protected:
|
||||
virtual void _fill_surface_single(
|
||||
void _fill_surface_single(
|
||||
const FillParams ¶ms,
|
||||
unsigned int thickness_layers,
|
||||
const std::pair<float, Point> &direction,
|
||||
ExPolygon &expolygon,
|
||||
Polylines &polylines_out);
|
||||
ExPolygon expolygon,
|
||||
Polylines &polylines_out) override;
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ void FillHoneycomb::_fill_surface_single(
|
|||
const FillParams ¶ms,
|
||||
unsigned int thickness_layers,
|
||||
const std::pair<float, Point> &direction,
|
||||
ExPolygon &expolygon,
|
||||
ExPolygon expolygon,
|
||||
Polylines &polylines_out)
|
||||
{
|
||||
// cache hexagons math
|
||||
|
|
@ -18,21 +18,21 @@ void FillHoneycomb::_fill_surface_single(
|
|||
Cache::iterator it_m = this->cache.find(cache_id);
|
||||
if (it_m == this->cache.end()) {
|
||||
it_m = this->cache.insert(it_m, std::pair<CacheID, CacheData>(cache_id, CacheData()));
|
||||
CacheData &m = it_m->second;
|
||||
coord_t min_spacing = scale_(this->spacing);
|
||||
m.distance = min_spacing / params.density;
|
||||
m.hex_side = m.distance / (sqrt(3)/2);
|
||||
m.hex_width = m.distance * 2; // $m->{hex_width} == $m->{hex_side} * sqrt(3);
|
||||
coord_t hex_height = m.hex_side * 2;
|
||||
m.pattern_height = hex_height + m.hex_side;
|
||||
m.y_short = m.distance * sqrt(3)/3;
|
||||
m.x_offset = min_spacing / 2;
|
||||
m.y_offset = m.x_offset * sqrt(3)/3;
|
||||
m.hex_center = Point(m.hex_width/2, m.hex_side);
|
||||
CacheData &m = it_m->second;
|
||||
coord_t min_spacing = coord_t(scale_(this->spacing));
|
||||
m.distance = coord_t(min_spacing / params.density);
|
||||
m.hex_side = coord_t(m.distance / (sqrt(3)/2));
|
||||
m.hex_width = m.distance * 2; // $m->{hex_width} == $m->{hex_side} * sqrt(3);
|
||||
coord_t hex_height = m.hex_side * 2;
|
||||
m.pattern_height = hex_height + m.hex_side;
|
||||
m.y_short = coord_t(m.distance * sqrt(3)/3);
|
||||
m.x_offset = min_spacing / 2;
|
||||
m.y_offset = coord_t(m.x_offset * sqrt(3)/3);
|
||||
m.hex_center = Point(m.hex_width/2, m.hex_side);
|
||||
}
|
||||
CacheData &m = it_m->second;
|
||||
|
||||
Polygons polygons;
|
||||
Polylines all_polylines;
|
||||
{
|
||||
// adjust actual bounding box to the nearest multiple of our hex pattern
|
||||
// and align it so that it matches across layers
|
||||
|
|
@ -52,7 +52,7 @@ void FillHoneycomb::_fill_surface_single(
|
|||
|
||||
coord_t x = bounding_box.min(0);
|
||||
while (x <= bounding_box.max(0)) {
|
||||
Polygon p;
|
||||
Polyline p;
|
||||
coord_t ax[2] = { x + m.x_offset, x + m.distance - m.x_offset };
|
||||
for (size_t i = 0; i < 2; ++ i) {
|
||||
std::reverse(p.points.begin(), p.points.end()); // turn first half upside down
|
||||
|
|
@ -69,55 +69,15 @@ void FillHoneycomb::_fill_surface_single(
|
|||
x += m.distance;
|
||||
}
|
||||
p.rotate(-direction.first, m.hex_center);
|
||||
polygons.push_back(p);
|
||||
all_polylines.push_back(p);
|
||||
}
|
||||
}
|
||||
|
||||
if (params.complete || true) {
|
||||
// we were requested to complete each loop;
|
||||
// in this case we don't try to make more continuous paths
|
||||
Polygons polygons_trimmed = intersection((Polygons)expolygon, polygons);
|
||||
for (Polygons::iterator it = polygons_trimmed.begin(); it != polygons_trimmed.end(); ++ it)
|
||||
polylines_out.push_back(it->split_at_first_point());
|
||||
} else {
|
||||
// consider polygons as polylines without re-appending the initial point:
|
||||
// this cuts the last segment on purpose, so that the jump to the next
|
||||
// path is more straight
|
||||
Polylines paths;
|
||||
{
|
||||
Polylines p;
|
||||
for (Polygon &poly : polygons)
|
||||
p.emplace_back(poly.points);
|
||||
paths = intersection_pl(p, to_polygons(expolygon));
|
||||
}
|
||||
|
||||
// connect paths
|
||||
if (! paths.empty()) { // prevent calling leftmost_point() on empty collections
|
||||
Polylines chained = chain_polylines(std::move(paths));
|
||||
assert(paths.empty());
|
||||
paths.clear();
|
||||
for (Polyline &path : chained) {
|
||||
if (! paths.empty()) {
|
||||
// distance between first point of this path and last point of last path
|
||||
double distance = (path.first_point() - paths.back().last_point()).cast<double>().norm();
|
||||
if (distance <= m.hex_width) {
|
||||
paths.back().points.insert(paths.back().points.end(), path.points.begin(), path.points.end());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// Don't connect the paths.
|
||||
paths.push_back(std::move(path));
|
||||
}
|
||||
}
|
||||
|
||||
// clip paths again to prevent connection segments from crossing the expolygon boundaries
|
||||
paths = intersection_pl(paths, to_polygons(offset_ex(expolygon, SCALED_EPSILON)));
|
||||
// Move the polylines to the output, avoid a deep copy.
|
||||
size_t j = polylines_out.size();
|
||||
polylines_out.resize(j + paths.size(), Polyline());
|
||||
for (size_t i = 0; i < paths.size(); ++ i)
|
||||
std::swap(polylines_out[j ++], paths[i]);
|
||||
}
|
||||
all_polylines = intersection_pl(std::move(all_polylines), to_polygons(expolygon));
|
||||
if (params.dont_connect() || all_polylines.size() <= 1)
|
||||
append(polylines_out, chain_polylines(std::move(all_polylines)));
|
||||
else
|
||||
connect_infill(std::move(all_polylines), expolygon, polylines_out, this->spacing, params);
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
|
|
|||
|
|
@ -12,16 +12,16 @@ namespace Slic3r {
|
|||
class FillHoneycomb : public Fill
|
||||
{
|
||||
public:
|
||||
virtual ~FillHoneycomb() {}
|
||||
~FillHoneycomb() override {}
|
||||
|
||||
protected:
|
||||
virtual Fill* clone() const { return new FillHoneycomb(*this); };
|
||||
virtual void _fill_surface_single(
|
||||
Fill* clone() const override { return new FillHoneycomb(*this); };
|
||||
void _fill_surface_single(
|
||||
const FillParams ¶ms,
|
||||
unsigned int thickness_layers,
|
||||
const std::pair<float, Point> &direction,
|
||||
ExPolygon &expolygon,
|
||||
Polylines &polylines_out);
|
||||
ExPolygon expolygon,
|
||||
Polylines &polylines_out) override;
|
||||
|
||||
// Caching the
|
||||
struct CacheID
|
||||
|
|
@ -49,7 +49,7 @@ protected:
|
|||
typedef std::map<CacheID, CacheData> Cache;
|
||||
Cache cache;
|
||||
|
||||
virtual float _layer_angle(size_t idx) const { return float(M_PI/3.) * (idx % 3); }
|
||||
float _layer_angle(size_t idx) const override { return float(M_PI/3.) * (idx % 3); }
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
|
|
|
|||
122
src/libslic3r/Fill/FillLine.cpp
Normal file
122
src/libslic3r/Fill/FillLine.cpp
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
#include "../ClipperUtils.hpp"
|
||||
#include "../ExPolygon.hpp"
|
||||
#include "../ShortestPath.hpp"
|
||||
#include "../Surface.hpp"
|
||||
|
||||
#include "FillLine.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
void FillLine::_fill_surface_single(
|
||||
const FillParams ¶ms,
|
||||
unsigned int thickness_layers,
|
||||
const std::pair<float, Point> &direction,
|
||||
ExPolygon expolygon,
|
||||
Polylines &polylines_out)
|
||||
{
|
||||
// rotate polygons so that we can work with vertical lines here
|
||||
expolygon.rotate(- direction.first);
|
||||
|
||||
this->_min_spacing = scale_(this->spacing);
|
||||
assert(params.density > 0.0001f && params.density <= 1.f);
|
||||
this->_line_spacing = coord_t(coordf_t(this->_min_spacing) / params.density);
|
||||
this->_diagonal_distance = this->_line_spacing * 2;
|
||||
this->_line_oscillation = this->_line_spacing - this->_min_spacing; // only for Line infill
|
||||
BoundingBox bounding_box = expolygon.contour.bounding_box();
|
||||
|
||||
// define flow spacing according to requested density
|
||||
if (params.density > 0.9999f && !params.dont_adjust) {
|
||||
this->_line_spacing = this->_adjust_solid_spacing(bounding_box.size()(0), this->_line_spacing);
|
||||
this->spacing = unscale<double>(this->_line_spacing);
|
||||
} else {
|
||||
// extend bounding box so that our pattern will be aligned with other layers
|
||||
// Transform the reference point to the rotated coordinate system.
|
||||
bounding_box.merge(_align_to_grid(
|
||||
bounding_box.min,
|
||||
Point(this->_line_spacing, this->_line_spacing),
|
||||
direction.second.rotated(- direction.first)));
|
||||
}
|
||||
|
||||
// generate the basic pattern
|
||||
coord_t x_max = bounding_box.max(0) + SCALED_EPSILON;
|
||||
Lines lines;
|
||||
for (coord_t x = bounding_box.min(0); x <= x_max; x += this->_line_spacing)
|
||||
lines.push_back(this->_line(lines.size(), x, bounding_box.min(1), bounding_box.max(1)));
|
||||
|
||||
// clip paths against a slightly larger expolygon, so that the first and last paths
|
||||
// are kept even if the expolygon has vertical sides
|
||||
// the minimum offset for preventing edge lines from being clipped is SCALED_EPSILON;
|
||||
// however we use a larger offset to support expolygons with slightly skewed sides and
|
||||
// not perfectly straight
|
||||
//FIXME Vojtech: Update the intersecton function to work directly with lines.
|
||||
Polylines polylines_src;
|
||||
polylines_src.reserve(lines.size());
|
||||
for (Lines::const_iterator it = lines.begin(); it != lines.end(); ++ it) {
|
||||
polylines_src.push_back(Polyline());
|
||||
Points &pts = polylines_src.back().points;
|
||||
pts.reserve(2);
|
||||
pts.push_back(it->a);
|
||||
pts.push_back(it->b);
|
||||
}
|
||||
Polylines polylines = intersection_pl(polylines_src, offset(to_polygons(expolygon), scale_(0.02)), false);
|
||||
|
||||
// FIXME Vojtech: This is only performed for horizontal lines, not for the vertical lines!
|
||||
const float INFILL_OVERLAP_OVER_SPACING = 0.3f;
|
||||
// How much to extend an infill path from expolygon outside?
|
||||
coord_t extra = coord_t(floor(this->_min_spacing * INFILL_OVERLAP_OVER_SPACING + 0.5f));
|
||||
for (Polylines::iterator it_polyline = polylines.begin(); it_polyline != polylines.end(); ++ it_polyline) {
|
||||
Point *first_point = &it_polyline->points.front();
|
||||
Point *last_point = &it_polyline->points.back();
|
||||
if (first_point->y() > last_point->y())
|
||||
std::swap(first_point, last_point);
|
||||
first_point->y() -= extra;
|
||||
last_point->y() += extra;
|
||||
}
|
||||
|
||||
size_t n_polylines_out_old = polylines_out.size();
|
||||
|
||||
// connect lines
|
||||
if (! params.dont_connect() && ! polylines.empty()) { // prevent calling leftmost_point() on empty collections
|
||||
// offset the expolygon by max(min_spacing/2, extra)
|
||||
ExPolygon expolygon_off;
|
||||
{
|
||||
ExPolygons expolygons_off = offset_ex(expolygon, this->_min_spacing/2);
|
||||
if (! expolygons_off.empty()) {
|
||||
// When expanding a polygon, the number of islands could only shrink. Therefore the offset_ex shall generate exactly one expanded island for one input island.
|
||||
assert(expolygons_off.size() == 1);
|
||||
std::swap(expolygon_off, expolygons_off.front());
|
||||
}
|
||||
}
|
||||
bool first = true;
|
||||
for (Polyline &polyline : chain_polylines(std::move(polylines))) {
|
||||
if (! first) {
|
||||
// Try to connect the lines.
|
||||
Points &pts_end = polylines_out.back().points;
|
||||
const Point &first_point = polyline.points.front();
|
||||
const Point &last_point = pts_end.back();
|
||||
// Distance in X, Y.
|
||||
const Vector distance = last_point - first_point;
|
||||
// TODO: we should also check that both points are on a fill_boundary to avoid
|
||||
// connecting paths on the boundaries of internal regions
|
||||
if (this->_can_connect(std::abs(distance(0)), std::abs(distance(1))) &&
|
||||
expolygon_off.contains(Line(last_point, first_point))) {
|
||||
// Append the polyline.
|
||||
pts_end.insert(pts_end.end(), polyline.points.begin(), polyline.points.end());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// The lines cannot be connected.
|
||||
polylines_out.emplace_back(std::move(polyline));
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
|
||||
// paths must be rotated back
|
||||
for (Polylines::iterator it = polylines_out.begin() + n_polylines_out_old; it != polylines_out.end(); ++ it) {
|
||||
// No need to translate, the absolute position is irrelevant.
|
||||
// it->translate(- direction.second(0), - direction.second(1));
|
||||
it->rotate(direction.first);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
49
src/libslic3r/Fill/FillLine.hpp
Normal file
49
src/libslic3r/Fill/FillLine.hpp
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
#ifndef slic3r_FillLine_hpp_
|
||||
#define slic3r_FillLine_hpp_
|
||||
|
||||
#include "../libslic3r.h"
|
||||
|
||||
#include "FillBase.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class Surface;
|
||||
|
||||
class FillLine : public Fill
|
||||
{
|
||||
public:
|
||||
Fill* clone() const override { return new FillLine(*this); };
|
||||
~FillLine() override = default;
|
||||
|
||||
protected:
|
||||
void _fill_surface_single(
|
||||
const FillParams ¶ms,
|
||||
unsigned int thickness_layers,
|
||||
const std::pair<float, Point> &direction,
|
||||
ExPolygon expolygon,
|
||||
Polylines &polylines_out) override;
|
||||
|
||||
coord_t _min_spacing;
|
||||
coord_t _line_spacing;
|
||||
// distance threshold for allowing the horizontal infill lines to be connected into a continuous path
|
||||
coord_t _diagonal_distance;
|
||||
// only for line infill
|
||||
coord_t _line_oscillation;
|
||||
|
||||
Line _line(int i, coord_t x, coord_t y_min, coord_t y_max) const {
|
||||
coord_t osc = (i & 1) ? this->_line_oscillation : 0;
|
||||
return Line(Point(x - osc, y_min), Point(x + osc, y_max));
|
||||
}
|
||||
|
||||
bool _can_connect(coord_t dist_X, coord_t dist_Y)
|
||||
{
|
||||
coord_t TOLERANCE = 10 * SCALED_EPSILON;
|
||||
return (dist_X >= (this->_line_spacing - this->_line_oscillation) - TOLERANCE)
|
||||
&& (dist_X <= (this->_line_spacing + this->_line_oscillation) + TOLERANCE)
|
||||
&& (dist_Y <= this->_diagonal_distance);
|
||||
}
|
||||
};
|
||||
|
||||
}; // namespace Slic3r
|
||||
|
||||
#endif // slic3r_FillLine_hpp_
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
#include "../ClipperUtils.hpp"
|
||||
#include "../ShortestPath.hpp"
|
||||
#include "../Surface.hpp"
|
||||
|
||||
#include "FillPlanePath.hpp"
|
||||
|
|
@ -9,7 +10,7 @@ void FillPlanePath::_fill_surface_single(
|
|||
const FillParams ¶ms,
|
||||
unsigned int thickness_layers,
|
||||
const std::pair<float, Point> &direction,
|
||||
ExPolygon &expolygon,
|
||||
ExPolygon expolygon,
|
||||
Polylines &polylines_out)
|
||||
{
|
||||
expolygon.rotate(- direction.first);
|
||||
|
|
@ -23,14 +24,14 @@ void FillPlanePath::_fill_surface_single(
|
|||
Point shift = this->_centered() ?
|
||||
bounding_box.center() :
|
||||
bounding_box.min;
|
||||
expolygon.translate(-shift(0), -shift(1));
|
||||
bounding_box.translate(-shift(0), -shift(1));
|
||||
expolygon.translate(-shift.x(), -shift.y());
|
||||
bounding_box.translate(-shift.x(), -shift.y());
|
||||
|
||||
Pointfs pts = _generate(
|
||||
coord_t(ceil(coordf_t(bounding_box.min(0)) / distance_between_lines)),
|
||||
coord_t(ceil(coordf_t(bounding_box.min(1)) / distance_between_lines)),
|
||||
coord_t(ceil(coordf_t(bounding_box.max(0)) / distance_between_lines)),
|
||||
coord_t(ceil(coordf_t(bounding_box.max(1)) / distance_between_lines)));
|
||||
coord_t(ceil(coordf_t(bounding_box.min.x()) / distance_between_lines)),
|
||||
coord_t(ceil(coordf_t(bounding_box.min.y()) / distance_between_lines)),
|
||||
coord_t(ceil(coordf_t(bounding_box.max.x()) / distance_between_lines)),
|
||||
coord_t(ceil(coordf_t(bounding_box.max.y()) / distance_between_lines)));
|
||||
|
||||
Polylines polylines;
|
||||
if (pts.size() >= 2) {
|
||||
|
|
@ -38,39 +39,24 @@ void FillPlanePath::_fill_surface_single(
|
|||
polylines.push_back(Polyline());
|
||||
Polyline &polyline = polylines.back();
|
||||
polyline.points.reserve(pts.size());
|
||||
for (Pointfs::iterator it = pts.begin(); it != pts.end(); ++ it)
|
||||
for (const Vec2d &pt : pts)
|
||||
polyline.points.push_back(Point(
|
||||
coord_t(floor((*it)(0) * distance_between_lines + 0.5)),
|
||||
coord_t(floor((*it)(1) * distance_between_lines + 0.5))));
|
||||
coord_t(floor(pt.x() * distance_between_lines + 0.5)),
|
||||
coord_t(floor(pt.y() * distance_between_lines + 0.5))));
|
||||
// intersection(polylines_src, offset((Polygons)expolygon, scale_(0.02)), &polylines);
|
||||
polylines = intersection_pl(polylines, to_polygons(expolygon));
|
||||
|
||||
/*
|
||||
if (1) {
|
||||
require "Slic3r/SVG.pm";
|
||||
print "Writing fill.svg\n";
|
||||
Slic3r::SVG::output("fill.svg",
|
||||
no_arrows => 1,
|
||||
polygons => \@$expolygon,
|
||||
green_polygons => [ $bounding_box->polygon ],
|
||||
polylines => [ $polyline ],
|
||||
red_polylines => \@paths,
|
||||
);
|
||||
}
|
||||
*/
|
||||
|
||||
polylines = intersection_pl(std::move(polylines), to_polygons(expolygon));
|
||||
Polylines chained;
|
||||
if (params.dont_connect() || params.density > 0.5 || polylines.size() <= 1)
|
||||
chained = chain_polylines(std::move(polylines));
|
||||
else
|
||||
connect_infill(std::move(polylines), expolygon, chained, this->spacing, params);
|
||||
// paths must be repositioned and rotated back
|
||||
for (Polylines::iterator it = polylines.begin(); it != polylines.end(); ++ it) {
|
||||
it->translate(shift(0), shift(1));
|
||||
it->rotate(direction.first);
|
||||
for (Polyline &pl : chained) {
|
||||
pl.translate(shift.x(), shift.y());
|
||||
pl.rotate(direction.first);
|
||||
}
|
||||
append(polylines_out, std::move(chained));
|
||||
}
|
||||
|
||||
// Move the polylines to the output, avoid a deep copy.
|
||||
size_t j = polylines_out.size();
|
||||
polylines_out.resize(j + polylines.size(), Polyline());
|
||||
for (size_t i = 0; i < polylines.size(); ++ i)
|
||||
std::swap(polylines_out[j ++], polylines[i]);
|
||||
}
|
||||
|
||||
// Follow an Archimedean spiral, in polar coordinates: r=a+b\theta
|
||||
|
|
@ -85,13 +71,13 @@ Pointfs FillArchimedeanChords::_generate(coord_t min_x, coord_t min_y, coord_t m
|
|||
coordf_t r = 1;
|
||||
Pointfs out;
|
||||
//FIXME Vojtech: If used as a solid infill, there is a gap left at the center.
|
||||
out.push_back(Vec2d(0, 0));
|
||||
out.push_back(Vec2d(1, 0));
|
||||
out.emplace_back(0, 0);
|
||||
out.emplace_back(1, 0);
|
||||
while (r < rmax) {
|
||||
// Discretization angle to achieve a discretization error lower than RESOLUTION.
|
||||
theta += 2. * acos(1. - RESOLUTION / r);
|
||||
r = a + b * theta;
|
||||
out.push_back(Vec2d(r * cos(theta), r * sin(theta)));
|
||||
out.emplace_back(r * cos(theta), r * sin(theta));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
|
@ -128,15 +114,12 @@ static inline Point hilbert_n_to_xy(const size_t n)
|
|||
++ ndigits;
|
||||
}
|
||||
}
|
||||
int state = (ndigits & 1) ? 4 : 0;
|
||||
// int dirstate = (ndigits & 1) ? 0 : 4;
|
||||
int state = (ndigits & 1) ? 4 : 0;
|
||||
coord_t x = 0;
|
||||
coord_t y = 0;
|
||||
for (int i = (int)ndigits - 1; i >= 0; -- i) {
|
||||
int digit = (n >> (i * 2)) & 3;
|
||||
state += digit;
|
||||
// if (digit != 3)
|
||||
// dirstate = state; // lowest non-3 digit
|
||||
x |= digit_to_x[state] << i;
|
||||
y |= digit_to_y[state] << i;
|
||||
state = next_state[state];
|
||||
|
|
@ -162,7 +145,7 @@ Pointfs FillHilbertCurve::_generate(coord_t min_x, coord_t min_y, coord_t max_x,
|
|||
line.reserve(sz2);
|
||||
for (size_t i = 0; i < sz2; ++ i) {
|
||||
Point p = hilbert_n_to_xy(i);
|
||||
line.push_back(Vec2d(p(0) + min_x, p(1) + min_y));
|
||||
line.emplace_back(p.x() + min_x, p.y() + min_y);
|
||||
}
|
||||
return line;
|
||||
}
|
||||
|
|
@ -175,27 +158,27 @@ Pointfs FillOctagramSpiral::_generate(coord_t min_x, coord_t min_y, coord_t max_
|
|||
coordf_t r = 0;
|
||||
coordf_t r_inc = sqrt(2.);
|
||||
Pointfs out;
|
||||
out.push_back(Vec2d(0, 0));
|
||||
out.emplace_back(0., 0.);
|
||||
while (r < rmax) {
|
||||
r += r_inc;
|
||||
coordf_t rx = r / sqrt(2.);
|
||||
coordf_t r2 = r + rx;
|
||||
out.push_back(Vec2d( r, 0.));
|
||||
out.push_back(Vec2d( r2, rx));
|
||||
out.push_back(Vec2d( rx, rx));
|
||||
out.push_back(Vec2d( rx, r2));
|
||||
out.push_back(Vec2d(0., r));
|
||||
out.push_back(Vec2d(-rx, r2));
|
||||
out.push_back(Vec2d(-rx, rx));
|
||||
out.push_back(Vec2d(-r2, rx));
|
||||
out.push_back(Vec2d(-r, 0.));
|
||||
out.push_back(Vec2d(-r2, -rx));
|
||||
out.push_back(Vec2d(-rx, -rx));
|
||||
out.push_back(Vec2d(-rx, -r2));
|
||||
out.push_back(Vec2d(0., -r));
|
||||
out.push_back(Vec2d( rx, -r2));
|
||||
out.push_back(Vec2d( rx, -rx));
|
||||
out.push_back(Vec2d( r2+r_inc, -rx));
|
||||
out.emplace_back( r, 0.);
|
||||
out.emplace_back( r2, rx);
|
||||
out.emplace_back( rx, rx);
|
||||
out.emplace_back( rx, r2);
|
||||
out.emplace_back( 0., r);
|
||||
out.emplace_back(-rx, r2);
|
||||
out.emplace_back(-rx, rx);
|
||||
out.emplace_back(-r2, rx);
|
||||
out.emplace_back(- r, 0.);
|
||||
out.emplace_back(-r2, -rx);
|
||||
out.emplace_back(-rx, -rx);
|
||||
out.emplace_back(-rx, -r2);
|
||||
out.emplace_back( 0., -r);
|
||||
out.emplace_back( rx, -r2);
|
||||
out.emplace_back( rx, -rx);
|
||||
out.emplace_back( r2+r_inc, -rx);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,17 +16,17 @@ namespace Slic3r {
|
|||
class FillPlanePath : public Fill
|
||||
{
|
||||
public:
|
||||
virtual ~FillPlanePath() {}
|
||||
~FillPlanePath() override = default;
|
||||
|
||||
protected:
|
||||
virtual void _fill_surface_single(
|
||||
void _fill_surface_single(
|
||||
const FillParams ¶ms,
|
||||
unsigned int thickness_layers,
|
||||
const std::pair<float, Point> &direction,
|
||||
ExPolygon &expolygon,
|
||||
Polylines &polylines_out);
|
||||
ExPolygon expolygon,
|
||||
Polylines &polylines_out) override;
|
||||
|
||||
virtual float _layer_angle(size_t idx) const { return 0.f; }
|
||||
float _layer_angle(size_t idx) const override { return 0.f; }
|
||||
virtual bool _centered() const = 0;
|
||||
virtual Pointfs _generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y) = 0;
|
||||
};
|
||||
|
|
@ -34,34 +34,34 @@ protected:
|
|||
class FillArchimedeanChords : public FillPlanePath
|
||||
{
|
||||
public:
|
||||
virtual Fill* clone() const { return new FillArchimedeanChords(*this); };
|
||||
virtual ~FillArchimedeanChords() {}
|
||||
Fill* clone() const override { return new FillArchimedeanChords(*this); };
|
||||
~FillArchimedeanChords() override = default;
|
||||
|
||||
protected:
|
||||
virtual bool _centered() const { return true; }
|
||||
virtual Pointfs _generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y);
|
||||
bool _centered() const override { return true; }
|
||||
Pointfs _generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y) override;
|
||||
};
|
||||
|
||||
class FillHilbertCurve : public FillPlanePath
|
||||
{
|
||||
public:
|
||||
virtual Fill* clone() const { return new FillHilbertCurve(*this); };
|
||||
virtual ~FillHilbertCurve() {}
|
||||
Fill* clone() const override { return new FillHilbertCurve(*this); };
|
||||
~FillHilbertCurve() override = default;
|
||||
|
||||
protected:
|
||||
virtual bool _centered() const { return false; }
|
||||
virtual Pointfs _generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y);
|
||||
bool _centered() const override { return false; }
|
||||
Pointfs _generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y) override;
|
||||
};
|
||||
|
||||
class FillOctagramSpiral : public FillPlanePath
|
||||
{
|
||||
public:
|
||||
virtual Fill* clone() const { return new FillOctagramSpiral(*this); };
|
||||
virtual ~FillOctagramSpiral() {}
|
||||
Fill* clone() const override { return new FillOctagramSpiral(*this); };
|
||||
~FillOctagramSpiral() override = default;
|
||||
|
||||
protected:
|
||||
virtual bool _centered() const { return true; }
|
||||
virtual Pointfs _generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y);
|
||||
bool _centered() const override { return true; }
|
||||
Pointfs _generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y) override;
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -12,68 +12,92 @@ class Surface;
|
|||
class FillRectilinear : public Fill
|
||||
{
|
||||
public:
|
||||
virtual Fill* clone() const { return new FillRectilinear(*this); };
|
||||
virtual ~FillRectilinear() {}
|
||||
Fill* clone() const override { return new FillRectilinear(*this); };
|
||||
~FillRectilinear() override = default;
|
||||
Polylines fill_surface(const Surface *surface, const FillParams ¶ms) override;
|
||||
|
||||
protected:
|
||||
virtual void _fill_surface_single(
|
||||
const FillParams ¶ms,
|
||||
unsigned int thickness_layers,
|
||||
const std::pair<float, Point> &direction,
|
||||
ExPolygon &expolygon,
|
||||
Polylines &polylines_out);
|
||||
// Fill by single directional lines, interconnect the lines along perimeters.
|
||||
bool fill_surface_by_lines(const Surface *surface, const FillParams ¶ms, float angleBase, float pattern_shift, Polylines &polylines_out);
|
||||
|
||||
coord_t _min_spacing;
|
||||
coord_t _line_spacing;
|
||||
// distance threshold for allowing the horizontal infill lines to be connected into a continuous path
|
||||
coord_t _diagonal_distance;
|
||||
// only for line infill
|
||||
coord_t _line_oscillation;
|
||||
|
||||
// Enabled for the grid infill, disabled for the rectilinear and line infill.
|
||||
virtual bool _horizontal_lines() const { return false; }
|
||||
|
||||
virtual Line _line(int i, coord_t x, coord_t y_min, coord_t y_max) const
|
||||
{ return Line(Point(x, y_min), Point(x, y_max)); }
|
||||
|
||||
virtual bool _can_connect(coord_t dist_X, coord_t dist_Y) {
|
||||
return dist_X <= this->_diagonal_distance
|
||||
&& dist_Y <= this->_diagonal_distance;
|
||||
}
|
||||
// Fill by multiple sweeps of differing directions.
|
||||
struct SweepParams {
|
||||
float angle_base;
|
||||
float pattern_shift;
|
||||
};
|
||||
bool fill_surface_by_multilines(const Surface *surface, FillParams params, const std::initializer_list<SweepParams> &sweep_params, Polylines &polylines_out);
|
||||
};
|
||||
|
||||
class FillLine : public FillRectilinear
|
||||
class FillAlignedRectilinear : public FillRectilinear
|
||||
{
|
||||
public:
|
||||
virtual ~FillLine() {}
|
||||
Fill* clone() const override { return new FillAlignedRectilinear(*this); };
|
||||
~FillAlignedRectilinear() override = default;
|
||||
|
||||
protected:
|
||||
virtual Line _line(int i, coord_t x, coord_t y_min, coord_t y_max) const {
|
||||
coord_t osc = (i & 1) ? this->_line_oscillation : 0;
|
||||
return Line(Point(x - osc, y_min), Point(x + osc, y_max));
|
||||
}
|
||||
// Always generate infill at the same angle.
|
||||
virtual float _layer_angle(size_t idx) const { return 0.f; }
|
||||
};
|
||||
|
||||
virtual bool _can_connect(coord_t dist_X, coord_t dist_Y)
|
||||
{
|
||||
coord_t TOLERANCE = 10 * SCALED_EPSILON;
|
||||
return (dist_X >= (this->_line_spacing - this->_line_oscillation) - TOLERANCE)
|
||||
&& (dist_X <= (this->_line_spacing + this->_line_oscillation) + TOLERANCE)
|
||||
&& (dist_Y <= this->_diagonal_distance);
|
||||
}
|
||||
class FillMonotonic : public FillRectilinear
|
||||
{
|
||||
public:
|
||||
Fill* clone() const override { return new FillMonotonic(*this); };
|
||||
~FillMonotonic() override = default;
|
||||
Polylines fill_surface(const Surface *surface, const FillParams ¶ms) override;
|
||||
bool no_sort() const override { return true; }
|
||||
};
|
||||
|
||||
class FillGrid : public FillRectilinear
|
||||
{
|
||||
public:
|
||||
virtual ~FillGrid() {}
|
||||
Fill* clone() const override { return new FillGrid(*this); };
|
||||
~FillGrid() override = default;
|
||||
Polylines fill_surface(const Surface *surface, const FillParams ¶ms) override;
|
||||
|
||||
protected:
|
||||
// The grid fill will keep the angle constant between the layers, see the implementation of Slic3r::Fill.
|
||||
virtual float _layer_angle(size_t idx) const { return 0.f; }
|
||||
// Flag for Slic3r::Fill::Rectilinear to fill both directions.
|
||||
virtual bool _horizontal_lines() const { return true; }
|
||||
float _layer_angle(size_t idx) const override { return 0.f; }
|
||||
};
|
||||
|
||||
class FillTriangles : public FillRectilinear
|
||||
{
|
||||
public:
|
||||
Fill* clone() const override { return new FillTriangles(*this); };
|
||||
~FillTriangles() override = default;
|
||||
Polylines fill_surface(const Surface *surface, const FillParams ¶ms) override;
|
||||
|
||||
protected:
|
||||
// The grid fill will keep the angle constant between the layers, see the implementation of Slic3r::Fill.
|
||||
float _layer_angle(size_t idx) const override { return 0.f; }
|
||||
};
|
||||
|
||||
class FillStars : public FillRectilinear
|
||||
{
|
||||
public:
|
||||
Fill* clone() const override { return new FillStars(*this); };
|
||||
~FillStars() override = default;
|
||||
Polylines fill_surface(const Surface *surface, const FillParams ¶ms) override;
|
||||
|
||||
protected:
|
||||
// The grid fill will keep the angle constant between the layers, see the implementation of Slic3r::Fill.
|
||||
float _layer_angle(size_t idx) const override { return 0.f; }
|
||||
};
|
||||
|
||||
class FillCubic : public FillRectilinear
|
||||
{
|
||||
public:
|
||||
Fill* clone() const override { return new FillCubic(*this); };
|
||||
~FillCubic() override = default;
|
||||
Polylines fill_surface(const Surface *surface, const FillParams ¶ms) override;
|
||||
|
||||
protected:
|
||||
// The grid fill will keep the angle constant between the layers, see the implementation of Slic3r::Fill.
|
||||
float _layer_angle(size_t idx) const override { return 0.f; }
|
||||
};
|
||||
|
||||
|
||||
}; // namespace Slic3r
|
||||
|
||||
#endif // slic3r_FillRectilinear_hpp_
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,83 +0,0 @@
|
|||
#ifndef slic3r_FillRectilinear2_hpp_
|
||||
#define slic3r_FillRectilinear2_hpp_
|
||||
|
||||
#include "../libslic3r.h"
|
||||
|
||||
#include "FillBase.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class Surface;
|
||||
|
||||
class FillRectilinear2 : public Fill
|
||||
{
|
||||
public:
|
||||
virtual Fill* clone() const { return new FillRectilinear2(*this); };
|
||||
virtual ~FillRectilinear2() = default;
|
||||
virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms);
|
||||
|
||||
protected:
|
||||
bool fill_surface_by_lines(const Surface *surface, const FillParams ¶ms, float angleBase, float pattern_shift, Polylines &polylines_out);
|
||||
};
|
||||
|
||||
class FillMonotonic : public FillRectilinear2
|
||||
{
|
||||
public:
|
||||
virtual Fill* clone() const { return new FillMonotonic(*this); };
|
||||
virtual ~FillMonotonic() = default;
|
||||
virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms);
|
||||
virtual bool no_sort() const { return true; }
|
||||
};
|
||||
|
||||
class FillGrid2 : public FillRectilinear2
|
||||
{
|
||||
public:
|
||||
virtual Fill* clone() const { return new FillGrid2(*this); };
|
||||
virtual ~FillGrid2() = default;
|
||||
virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms);
|
||||
|
||||
protected:
|
||||
// The grid fill will keep the angle constant between the layers, see the implementation of Slic3r::Fill.
|
||||
virtual float _layer_angle(size_t idx) const { return 0.f; }
|
||||
};
|
||||
|
||||
class FillTriangles : public FillRectilinear2
|
||||
{
|
||||
public:
|
||||
virtual Fill* clone() const { return new FillTriangles(*this); };
|
||||
virtual ~FillTriangles() = default;
|
||||
virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms);
|
||||
|
||||
protected:
|
||||
// The grid fill will keep the angle constant between the layers, see the implementation of Slic3r::Fill.
|
||||
virtual float _layer_angle(size_t idx) const { return 0.f; }
|
||||
};
|
||||
|
||||
class FillStars : public FillRectilinear2
|
||||
{
|
||||
public:
|
||||
virtual Fill* clone() const { return new FillStars(*this); };
|
||||
virtual ~FillStars() = default;
|
||||
virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms);
|
||||
|
||||
protected:
|
||||
// The grid fill will keep the angle constant between the layers, see the implementation of Slic3r::Fill.
|
||||
virtual float _layer_angle(size_t idx) const { return 0.f; }
|
||||
};
|
||||
|
||||
class FillCubic : public FillRectilinear2
|
||||
{
|
||||
public:
|
||||
virtual Fill* clone() const { return new FillCubic(*this); };
|
||||
virtual ~FillCubic() = default;
|
||||
virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms);
|
||||
|
||||
protected:
|
||||
// The grid fill will keep the angle constant between the layers, see the implementation of Slic3r::Fill.
|
||||
virtual float _layer_angle(size_t idx) const { return 0.f; }
|
||||
};
|
||||
|
||||
|
||||
}; // namespace Slic3r
|
||||
|
||||
#endif // slic3r_FillRectilinear2_hpp_
|
||||
|
|
@ -18,7 +18,7 @@
|
|||
#include "libslic3r/PrintConfig.hpp"
|
||||
#include "libslic3r/SLA/RasterBase.hpp"
|
||||
#include "libslic3r/miniz_extension.hpp"
|
||||
#include "libslic3r/PNGRead.hpp"
|
||||
#include "libslic3r/PNGReadWrite.hpp"
|
||||
|
||||
#include <boost/property_tree/ini_parser.hpp>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
|
|
|
|||
|
|
@ -169,6 +169,10 @@ namespace Slic3r {
|
|||
|
||||
// subdivide the retraction in segments
|
||||
if (!wipe_path.empty()) {
|
||||
#if ENABLE_SHOW_WIPE_MOVES
|
||||
// add tag for processor
|
||||
gcode += ";" + GCodeProcessor::Wipe_Start_Tag + "\n";
|
||||
#endif // ENABLE_SHOW_WIPE_MOVES
|
||||
for (const Line& line : wipe_path.lines()) {
|
||||
double segment_length = line.length();
|
||||
/* Reduce retraction length a bit to avoid effective retraction speed to be greater than the configured one
|
||||
|
|
@ -183,6 +187,10 @@ namespace Slic3r {
|
|||
"wipe and retract"
|
||||
);
|
||||
}
|
||||
#if ENABLE_SHOW_WIPE_MOVES
|
||||
// add tag for processor
|
||||
gcode += ";" + GCodeProcessor::Wipe_End_Tag + "\n";
|
||||
#endif // ENABLE_SHOW_WIPE_MOVES
|
||||
gcodegen.set_last_pos(wipe_path.points.back());
|
||||
}
|
||||
|
||||
|
|
@ -1764,7 +1772,7 @@ void GCode::process_layer(
|
|||
std::string gcode;
|
||||
|
||||
// add tag for processor
|
||||
gcode += "; " + GCodeProcessor::Layer_Change_Tag + "\n";
|
||||
gcode += ";" + GCodeProcessor::Layer_Change_Tag + "\n";
|
||||
// export layer z
|
||||
char buf[64];
|
||||
sprintf(buf, ";Z:%g\n", print_z);
|
||||
|
|
@ -2240,7 +2248,7 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
|
|||
const EdgeGrid::Grid* edge_grid_ptr = (lower_layer_edge_grid && *lower_layer_edge_grid)
|
||||
? lower_layer_edge_grid->get()
|
||||
: nullptr;
|
||||
Point seam = m_seam_placer.get_seam(m_layer->id(), seam_position, loop,
|
||||
Point seam = m_seam_placer.get_seam(*m_layer, seam_position, loop,
|
||||
last_pos, EXTRUDER_CONFIG(nozzle_diameter),
|
||||
(m_layer == NULL ? nullptr : m_layer->object()),
|
||||
was_clockwise, edge_grid_ptr);
|
||||
|
|
|
|||
|
|
@ -25,6 +25,10 @@ static const float DEFAULT_ACCELERATION = 1500.0f; // Prusa Firmware 1_75mm_MK2
|
|||
namespace Slic3r {
|
||||
|
||||
const std::string GCodeProcessor::Extrusion_Role_Tag = "TYPE:";
|
||||
#if ENABLE_SHOW_WIPE_MOVES
|
||||
const std::string GCodeProcessor::Wipe_Start_Tag = "WIPE_START";
|
||||
const std::string GCodeProcessor::Wipe_End_Tag = "WIPE_END";
|
||||
#endif // ENABLE_SHOW_WIPE_MOVES
|
||||
const std::string GCodeProcessor::Height_Tag = "HEIGHT:";
|
||||
const std::string GCodeProcessor::Layer_Change_Tag = "LAYER_CHANGE";
|
||||
const std::string GCodeProcessor::Color_Change_Tag = "COLOR_CHANGE";
|
||||
|
|
@ -35,6 +39,11 @@ const std::string GCodeProcessor::First_Line_M73_Placeholder_Tag = "; _
|
|||
const std::string GCodeProcessor::Last_Line_M73_Placeholder_Tag = "; _GP_LAST_LINE_M73_PLACEHOLDER";
|
||||
const std::string GCodeProcessor::Estimated_Printing_Time_Placeholder_Tag = "; _GP_ESTIMATED_PRINTING_TIME_PLACEHOLDER";
|
||||
|
||||
#if ENABLE_SHOW_WIPE_MOVES
|
||||
const float GCodeProcessor::Wipe_Width = 0.05f;
|
||||
const float GCodeProcessor::Wipe_Height = 0.05f;
|
||||
#endif // ENABLE_SHOW_WIPE_MOVES
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
const std::string GCodeProcessor::Width_Tag = "WIDTH:";
|
||||
const std::string GCodeProcessor::Mm3_Per_Mm_Tag = "MM3_PER_MM:";
|
||||
|
|
@ -390,13 +399,17 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename)
|
|||
};
|
||||
|
||||
// check for temporary lines
|
||||
auto is_temporary_decoration = [](const std::string& gcode_line) {
|
||||
auto is_temporary_decoration = [](const std::string_view gcode_line) {
|
||||
// remove trailing '\n'
|
||||
std::string line = gcode_line.substr(0, gcode_line.length() - 1);
|
||||
if (line == "; " + Layer_Change_Tag)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
assert(! gcode_line.empty());
|
||||
assert(gcode_line.back() == '\n');
|
||||
|
||||
// return true for decorations which are used in processing the gcode but that should not be exported into the final gcode
|
||||
// i.e.:
|
||||
// bool ret = gcode_line.substr(0, gcode_line.length() - 1) == ";" + Layer_Change_Tag;
|
||||
// ...
|
||||
// return ret;
|
||||
return false;
|
||||
};
|
||||
|
||||
// Iterators for the normal and silent cached time estimate entry recently processed, used by process_line_G1.
|
||||
|
|
@ -488,7 +501,8 @@ const std::vector<std::pair<GCodeProcessor::EProducer, std::string>> GCodeProces
|
|||
{ EProducer::Cura, "Cura_SteamEngine" },
|
||||
{ EProducer::Simplify3D, "Simplify3D" },
|
||||
{ EProducer::CraftWare, "CraftWare" },
|
||||
{ EProducer::ideaMaker, "ideaMaker" }
|
||||
{ EProducer::ideaMaker, "ideaMaker" },
|
||||
{ EProducer::KissSlicer, "KISSlicer" }
|
||||
};
|
||||
|
||||
unsigned int GCodeProcessor::s_result_id = 0;
|
||||
|
|
@ -591,9 +605,6 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
|
|||
}
|
||||
}
|
||||
|
||||
// ensure at least one (default) color is defined
|
||||
std::string default_color = "#FF8000";
|
||||
m_result.extruder_colors = std::vector<std::string>(1, default_color);
|
||||
const ConfigOptionStrings* extruder_colour = config.option<ConfigOptionStrings>("extruder_colour");
|
||||
if (extruder_colour != nullptr) {
|
||||
// takes colors from config
|
||||
|
|
@ -608,7 +619,9 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// replace missing values with default
|
||||
std::string default_color = "#FF8000";
|
||||
for (size_t i = 0; i < m_result.extruder_colors.size(); ++i) {
|
||||
if (m_result.extruder_colors[i].empty())
|
||||
m_result.extruder_colors[i] = default_color;
|
||||
|
|
@ -725,6 +738,9 @@ void GCodeProcessor::reset()
|
|||
m_end_position = { 0.0f, 0.0f, 0.0f, 0.0f };
|
||||
m_origin = { 0.0f, 0.0f, 0.0f, 0.0f };
|
||||
m_cached_position.reset();
|
||||
#if ENABLE_SHOW_WIPE_MOVES
|
||||
m_wiping = false;
|
||||
#endif // ENABLE_SHOW_WIPE_MOVES
|
||||
|
||||
m_feedrate = 0.0f;
|
||||
m_width = 0.0f;
|
||||
|
|
@ -806,6 +822,16 @@ void GCodeProcessor::process_file(const std::string& filename, bool apply_postpr
|
|||
process_gcode_line(line);
|
||||
});
|
||||
|
||||
#if ENABLE_SHOW_WIPE_MOVES
|
||||
// update width/height of wipe moves
|
||||
for (MoveVertex& move : m_result.moves) {
|
||||
if (move.type == EMoveType::Wipe) {
|
||||
move.width = Wipe_Width;
|
||||
move.height = Wipe_Height;
|
||||
}
|
||||
}
|
||||
#endif // ENABLE_SHOW_WIPE_MOVES
|
||||
|
||||
// process the time blocks
|
||||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) {
|
||||
TimeMachine& machine = m_time_processor.machines[i];
|
||||
|
|
@ -1027,6 +1053,20 @@ void GCodeProcessor::process_tags(const std::string_view comment)
|
|||
return;
|
||||
}
|
||||
|
||||
#if ENABLE_SHOW_WIPE_MOVES
|
||||
// wipe start tag
|
||||
if (starts_with(comment, Wipe_Start_Tag)) {
|
||||
m_wiping = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// wipe end tag
|
||||
if (starts_with(comment, Wipe_End_Tag)) {
|
||||
m_wiping = false;
|
||||
return;
|
||||
}
|
||||
#endif // ENABLE_SHOW_WIPE_MOVES
|
||||
|
||||
if ((!m_producers_enabled || m_producer == EProducer::PrusaSlicer) &&
|
||||
starts_with(comment, Height_Tag)) {
|
||||
// height tag
|
||||
|
|
@ -1104,11 +1144,14 @@ bool GCodeProcessor::process_producers_tags(const std::string_view comment)
|
|||
{
|
||||
switch (m_producer)
|
||||
{
|
||||
case EProducer::Slic3rPE:
|
||||
case EProducer::Slic3r:
|
||||
case EProducer::PrusaSlicer: { return process_prusaslicer_tags(comment); }
|
||||
case EProducer::Cura: { return process_cura_tags(comment); }
|
||||
case EProducer::Simplify3D: { return process_simplify3d_tags(comment); }
|
||||
case EProducer::CraftWare: { return process_craftware_tags(comment); }
|
||||
case EProducer::ideaMaker: { return process_ideamaker_tags(comment); }
|
||||
case EProducer::KissSlicer: { return process_kissslicer_tags(comment); }
|
||||
default: { return false; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1178,6 +1221,14 @@ bool GCodeProcessor::process_cura_tags(const std::string_view comment)
|
|||
return true;
|
||||
}
|
||||
|
||||
// layer
|
||||
tag = "LAYER:";
|
||||
pos = comment.find(tag);
|
||||
if (pos != comment.npos) {
|
||||
++m_layer_id;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -1262,9 +1313,8 @@ bool GCodeProcessor::process_simplify3d_tags(const std::string_view comment)
|
|||
return true;
|
||||
}
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
// geometry
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
// ; tool
|
||||
std::string tag = " tool";
|
||||
pos = comment.find(tag);
|
||||
|
|
@ -1289,6 +1339,19 @@ bool GCodeProcessor::process_simplify3d_tags(const std::string_view comment)
|
|||
}
|
||||
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
|
||||
// ; layer
|
||||
std::string tag = " layer";
|
||||
pos = comment.find(tag);
|
||||
if (pos == 0) {
|
||||
// skip lines "; layer end"
|
||||
const std::string_view data = comment.substr(pos + tag.length());
|
||||
size_t end_start = data.find("end");
|
||||
if (end_start == data.npos)
|
||||
++m_layer_id;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -1329,6 +1392,13 @@ bool GCodeProcessor::process_craftware_tags(const std::string_view comment)
|
|||
return true;
|
||||
}
|
||||
|
||||
// layer
|
||||
pos = comment.find(" Layer #");
|
||||
if (pos == 0) {
|
||||
++m_layer_id;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -1360,9 +1430,8 @@ bool GCodeProcessor::process_ideamaker_tags(const std::string_view comment)
|
|||
return true;
|
||||
}
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
// geometry
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
// width
|
||||
tag = "WIDTH:";
|
||||
pos = comment.find(tag);
|
||||
|
|
@ -1382,6 +1451,120 @@ bool GCodeProcessor::process_ideamaker_tags(const std::string_view comment)
|
|||
}
|
||||
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
|
||||
// layer
|
||||
pos = comment.find("LAYER:");
|
||||
if (pos == 0) {
|
||||
++m_layer_id;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GCodeProcessor::process_kissslicer_tags(const std::string_view comment)
|
||||
{
|
||||
// extrusion roles
|
||||
|
||||
// ; 'Raft Path'
|
||||
size_t pos = comment.find(" 'Raft Path'");
|
||||
if (pos == 0) {
|
||||
m_extrusion_role = erSkirt;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ; 'Support Interface Path'
|
||||
pos = comment.find(" 'Support Interface Path'");
|
||||
if (pos == 0) {
|
||||
m_extrusion_role = erSupportMaterialInterface;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ; 'Travel/Ironing Path'
|
||||
pos = comment.find(" 'Travel/Ironing Path'");
|
||||
if (pos == 0) {
|
||||
m_extrusion_role = erIroning;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ; 'Support (may Stack) Path'
|
||||
pos = comment.find(" 'Support (may Stack) Path'");
|
||||
if (pos == 0) {
|
||||
m_extrusion_role = erSupportMaterial;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ; 'Perimeter Path'
|
||||
pos = comment.find(" 'Perimeter Path'");
|
||||
if (pos == 0) {
|
||||
m_extrusion_role = erExternalPerimeter;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ; 'Pillar Path'
|
||||
pos = comment.find(" 'Pillar Path'");
|
||||
if (pos == 0) {
|
||||
m_extrusion_role = erNone; // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
return true;
|
||||
}
|
||||
|
||||
// ; 'Destring/Wipe/Jump Path'
|
||||
pos = comment.find(" 'Destring/Wipe/Jump Path'");
|
||||
if (pos == 0) {
|
||||
m_extrusion_role = erNone; // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
return true;
|
||||
}
|
||||
|
||||
// ; 'Prime Pillar Path'
|
||||
pos = comment.find(" 'Prime Pillar Path'");
|
||||
if (pos == 0) {
|
||||
m_extrusion_role = erNone; // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
return true;
|
||||
}
|
||||
|
||||
// ; 'Loop Path'
|
||||
pos = comment.find(" 'Loop Path'");
|
||||
if (pos == 0) {
|
||||
m_extrusion_role = erNone; // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
return true;
|
||||
}
|
||||
|
||||
// ; 'Crown Path'
|
||||
pos = comment.find(" 'Crown Path'");
|
||||
if (pos == 0) {
|
||||
m_extrusion_role = erNone; // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
return true;
|
||||
}
|
||||
|
||||
// ; 'Solid Path'
|
||||
pos = comment.find(" 'Solid Path'");
|
||||
if (pos == 0) {
|
||||
m_extrusion_role = erNone;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ; 'Stacked Sparse Infill Path'
|
||||
pos = comment.find(" 'Stacked Sparse Infill Path'");
|
||||
if (pos == 0) {
|
||||
m_extrusion_role = erInternalInfill;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ; 'Sparse Infill Path'
|
||||
pos = comment.find(" 'Sparse Infill Path'");
|
||||
if (pos == 0) {
|
||||
m_extrusion_role = erSolidInfill;
|
||||
return true;
|
||||
}
|
||||
|
||||
// geometry
|
||||
|
||||
// layer
|
||||
pos = comment.find(" BEGIN_LAYER_");
|
||||
if (pos == 0) {
|
||||
++m_layer_id;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -1423,7 +1606,13 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
|
|||
auto move_type = [this](const AxisCoords& delta_pos) {
|
||||
EMoveType type = EMoveType::Noop;
|
||||
|
||||
#if ENABLE_SHOW_WIPE_MOVES
|
||||
if (m_wiping)
|
||||
type = EMoveType::Wipe;
|
||||
else if (delta_pos[E] < 0.0f)
|
||||
#else
|
||||
if (delta_pos[E] < 0.0f)
|
||||
#endif // ENABLE_SHOW_WIPE_MOVES
|
||||
type = (delta_pos[X] != 0.0f || delta_pos[Y] != 0.0f || delta_pos[Z] != 0.0f) ? EMoveType::Travel : EMoveType::Retract;
|
||||
else if (delta_pos[E] > 0.0f) {
|
||||
if (delta_pos[X] == 0.0f && delta_pos[Y] == 0.0f)
|
||||
|
|
|
|||
|
|
@ -24,6 +24,9 @@ namespace Slic3r {
|
|||
Pause_Print,
|
||||
Custom_GCode,
|
||||
Travel,
|
||||
#if ENABLE_SHOW_WIPE_MOVES
|
||||
Wipe,
|
||||
#endif // ENABLE_SHOW_WIPE_MOVES
|
||||
Extrude,
|
||||
Count
|
||||
};
|
||||
|
|
@ -69,6 +72,10 @@ namespace Slic3r {
|
|||
{
|
||||
public:
|
||||
static const std::string Extrusion_Role_Tag;
|
||||
#if ENABLE_SHOW_WIPE_MOVES
|
||||
static const std::string Wipe_Start_Tag;
|
||||
static const std::string Wipe_End_Tag;
|
||||
#endif // ENABLE_SHOW_WIPE_MOVES
|
||||
static const std::string Height_Tag;
|
||||
static const std::string Layer_Change_Tag;
|
||||
static const std::string Color_Change_Tag;
|
||||
|
|
@ -78,6 +85,11 @@ namespace Slic3r {
|
|||
static const std::string Last_Line_M73_Placeholder_Tag;
|
||||
static const std::string Estimated_Printing_Time_Placeholder_Tag;
|
||||
|
||||
#if ENABLE_SHOW_WIPE_MOVES
|
||||
static const float Wipe_Width;
|
||||
static const float Wipe_Height;
|
||||
#endif // ENABLE_SHOW_WIPE_MOVES
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
static const std::string Width_Tag;
|
||||
static const std::string Mm3_Per_Mm_Tag;
|
||||
|
|
@ -390,6 +402,9 @@ namespace Slic3r {
|
|||
AxisCoords m_end_position; // mm
|
||||
AxisCoords m_origin; // mm
|
||||
CachedPosition m_cached_position;
|
||||
#if ENABLE_SHOW_WIPE_MOVES
|
||||
bool m_wiping;
|
||||
#endif // ENABLE_SHOW_WIPE_MOVES
|
||||
|
||||
float m_feedrate; // mm/s
|
||||
float m_width; // mm
|
||||
|
|
@ -414,7 +429,8 @@ namespace Slic3r {
|
|||
Cura,
|
||||
Simplify3D,
|
||||
CraftWare,
|
||||
ideaMaker
|
||||
ideaMaker,
|
||||
KissSlicer
|
||||
};
|
||||
|
||||
static const std::vector<std::pair<GCodeProcessor::EProducer, std::string>> Producers;
|
||||
|
|
@ -471,6 +487,7 @@ namespace Slic3r {
|
|||
bool process_simplify3d_tags(const std::string_view comment);
|
||||
bool process_craftware_tags(const std::string_view comment);
|
||||
bool process_ideamaker_tags(const std::string_view comment);
|
||||
bool process_kissslicer_tags(const std::string_view comment);
|
||||
|
||||
bool detect_producer(const std::string_view comment);
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#include "libslic3r/EdgeGrid.hpp"
|
||||
#include "libslic3r/ClipperUtils.hpp"
|
||||
#include "libslic3r/SVG.hpp"
|
||||
#include "libslic3r/Layer.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
|
@ -191,24 +192,98 @@ void SeamPlacer::init(const Print& print)
|
|||
{
|
||||
m_enforcers.clear();
|
||||
m_blockers.clear();
|
||||
//m_last_seam_position.clear();
|
||||
m_seam_history.clear();
|
||||
m_po_list.clear();
|
||||
|
||||
for (const PrintObject* po : print.objects()) {
|
||||
po->project_and_append_custom_facets(true, EnforcerBlockerType::ENFORCER, m_enforcers);
|
||||
po->project_and_append_custom_facets(true, EnforcerBlockerType::BLOCKER, m_blockers);
|
||||
}
|
||||
const std::vector<double>& nozzle_dmrs = print.config().nozzle_diameter.values;
|
||||
float max_nozzle_dmr = *std::max_element(nozzle_dmrs.begin(), nozzle_dmrs.end());
|
||||
for (ExPolygons& explgs : m_enforcers)
|
||||
explgs = Slic3r::offset_ex(explgs, scale_(max_nozzle_dmr));
|
||||
for (ExPolygons& explgs : m_blockers)
|
||||
explgs = Slic3r::offset_ex(explgs, scale_(max_nozzle_dmr));
|
||||
const std::vector<double>& nozzle_dmrs = print.config().nozzle_diameter.values;
|
||||
float max_nozzle_dmr = *std::max_element(nozzle_dmrs.begin(), nozzle_dmrs.end());
|
||||
|
||||
|
||||
std::vector<ExPolygons> temp_enf;
|
||||
std::vector<ExPolygons> temp_blk;
|
||||
|
||||
for (const PrintObject* po : print.objects()) {
|
||||
temp_enf.clear();
|
||||
temp_blk.clear();
|
||||
po->project_and_append_custom_facets(true, EnforcerBlockerType::ENFORCER, temp_enf);
|
||||
po->project_and_append_custom_facets(true, EnforcerBlockerType::BLOCKER, temp_blk);
|
||||
|
||||
// Offset the triangles out slightly.
|
||||
for (auto* custom_per_object : {&temp_enf, &temp_blk})
|
||||
for (ExPolygons& explgs : *custom_per_object)
|
||||
explgs = Slic3r::offset_ex(explgs, scale_(max_nozzle_dmr));
|
||||
|
||||
// FIXME: Offsetting should be done somehow cheaper, but following does not work
|
||||
// for (auto* custom_per_object : {&temp_enf, &temp_blk}) {
|
||||
// for (ExPolygons& plgs : *custom_per_object) {
|
||||
// for (ExPolygon& plg : plgs) {
|
||||
// auto out = Slic3r::offset_ex(plg, scale_(max_nozzle_dmr));
|
||||
// plg = out.empty() ? ExPolygon() : out.front();
|
||||
// assert(out.empty() || out.size() == 1);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
|
||||
// Remember this PrintObject and initialize a store of enforcers and blockers for it.
|
||||
m_po_list.push_back(po);
|
||||
size_t po_idx = m_po_list.size() - 1;
|
||||
m_enforcers.emplace_back(std::vector<CustomTrianglesPerLayer>(temp_enf.size()));
|
||||
m_blockers.emplace_back(std::vector<CustomTrianglesPerLayer>(temp_blk.size()));
|
||||
|
||||
// A helper class to store data to build the AABB tree from.
|
||||
class CustomTriangleRef {
|
||||
public:
|
||||
CustomTriangleRef(size_t idx,
|
||||
Point&& centroid,
|
||||
BoundingBox&& bb)
|
||||
: m_idx{idx}, m_centroid{centroid},
|
||||
m_bbox{AlignedBoxType(bb.min, bb.max)}
|
||||
{}
|
||||
size_t idx() const { return m_idx; }
|
||||
const Point& centroid() const { return m_centroid; }
|
||||
const TreeType::BoundingBox& bbox() const { return m_bbox; }
|
||||
|
||||
private:
|
||||
size_t m_idx;
|
||||
Point m_centroid;
|
||||
AlignedBoxType m_bbox;
|
||||
};
|
||||
|
||||
// A lambda to extract the ExPolygons and save them into the member AABB tree.
|
||||
// Will be called for enforcers and blockers separately.
|
||||
auto add_custom = [](std::vector<ExPolygons>& src, std::vector<CustomTrianglesPerLayer>& dest) {
|
||||
// Go layer by layer, and append all the ExPolygons into the AABB tree.
|
||||
size_t layer_idx = 0;
|
||||
for (ExPolygons& expolys_on_layer : src) {
|
||||
CustomTrianglesPerLayer& layer_data = dest[layer_idx];
|
||||
std::vector<CustomTriangleRef> triangles_data;
|
||||
layer_data.polys.reserve(expolys_on_layer.size());
|
||||
triangles_data.reserve(expolys_on_layer.size());
|
||||
|
||||
for (ExPolygon& expoly : expolys_on_layer) {
|
||||
if (expoly.empty())
|
||||
continue;
|
||||
layer_data.polys.emplace_back(std::move(expoly));
|
||||
triangles_data.emplace_back(layer_data.polys.size() - 1,
|
||||
layer_data.polys.back().centroid(),
|
||||
layer_data.polys.back().bounding_box());
|
||||
}
|
||||
// All polygons are saved, build the AABB tree for them.
|
||||
layer_data.tree.build(std::move(triangles_data));
|
||||
++layer_idx;
|
||||
}
|
||||
};
|
||||
|
||||
add_custom(temp_enf, m_enforcers.at(po_idx));
|
||||
add_custom(temp_blk, m_blockers.at(po_idx));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Point SeamPlacer::get_seam(const size_t layer_idx, const SeamPosition seam_position,
|
||||
Point SeamPlacer::get_seam(const Layer& layer, const SeamPosition seam_position,
|
||||
const ExtrusionLoop& loop, Point last_pos, coordf_t nozzle_dmr,
|
||||
const PrintObject* po, bool was_clockwise, const EdgeGrid::Grid* lower_layer_edge_grid)
|
||||
{
|
||||
|
|
@ -216,7 +291,28 @@ Point SeamPlacer::get_seam(const size_t layer_idx, const SeamPosition seam_posit
|
|||
BoundingBox polygon_bb = polygon.bounding_box();
|
||||
const coord_t nozzle_r = coord_t(scale_(0.5 * nozzle_dmr) + 0.5);
|
||||
|
||||
if (this->is_custom_seam_on_layer(layer_idx)) {
|
||||
size_t po_idx = std::find(m_po_list.begin(), m_po_list.end(), po) - m_po_list.begin();
|
||||
|
||||
// Find current layer in respective PrintObject. Cache the result so the
|
||||
// lookup is only done once per layer, not for each loop.
|
||||
const Layer* layer_po = nullptr;
|
||||
if (po == m_last_po && layer.print_z == m_last_print_z)
|
||||
layer_po = m_last_layer_po;
|
||||
else {
|
||||
layer_po = po->get_layer_at_printz(layer.print_z);
|
||||
m_last_po = po;
|
||||
m_last_print_z = layer.print_z;
|
||||
m_last_layer_po = layer_po;
|
||||
}
|
||||
if (! layer_po)
|
||||
return last_pos;
|
||||
|
||||
// Index of this layer in the respective PrintObject.
|
||||
size_t layer_idx = layer_po->id() - po->layers().front()->id(); // raft layers
|
||||
|
||||
assert(layer_idx < po->layer_count());
|
||||
|
||||
if (this->is_custom_seam_on_layer(layer_idx, po_idx)) {
|
||||
// Seam enf/blockers can begin and end in between the original vertices.
|
||||
// Let add extra points in between and update the leghths.
|
||||
polygon.densify(MINIMAL_POLYGON_SIDE);
|
||||
|
|
@ -229,11 +325,10 @@ Point SeamPlacer::get_seam(const size_t layer_idx, const SeamPosition seam_posit
|
|||
if (seam_position == spAligned) {
|
||||
// Seam is aligned to the seam at the preceding layer.
|
||||
if (po != nullptr) {
|
||||
std::optional<Point> pos = m_seam_history.get_last_seam(po, layer_idx, polygon_bb);
|
||||
std::optional<Point> pos = m_seam_history.get_last_seam(m_po_list[po_idx], layer_idx, polygon_bb);
|
||||
if (pos.has_value()) {
|
||||
//last_pos = m_last_seam_position[po];
|
||||
last_pos = *pos;
|
||||
last_pos_weight = is_custom_enforcer_on_layer(layer_idx) ? 0.f : 1.f;
|
||||
last_pos_weight = is_custom_enforcer_on_layer(layer_idx, po_idx) ? 0.f : 1.f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -313,12 +408,12 @@ Point SeamPlacer::get_seam(const size_t layer_idx, const SeamPosition seam_posit
|
|||
|
||||
// Custom seam. Huge (negative) constant penalty is applied inside
|
||||
// blockers (enforcers) to rule out points that should not win.
|
||||
this->apply_custom_seam(polygon, penalties, lengths, layer_idx, seam_position);
|
||||
this->apply_custom_seam(polygon, po_idx, penalties, lengths, layer_idx, seam_position);
|
||||
|
||||
// Find a point with a minimum penalty.
|
||||
size_t idx_min = std::min_element(penalties.begin(), penalties.end()) - penalties.begin();
|
||||
|
||||
if (seam_position != spAligned || ! is_custom_enforcer_on_layer(layer_idx)) {
|
||||
if (seam_position != spAligned || ! is_custom_enforcer_on_layer(layer_idx, po_idx)) {
|
||||
// Very likely the weight of idx_min is very close to the weight of last_pos_proj_idx.
|
||||
// In that case use last_pos_proj_idx instead.
|
||||
float penalty_aligned = penalties[last_pos_proj_idx];
|
||||
|
|
@ -363,29 +458,45 @@ Point SeamPlacer::get_seam(const size_t layer_idx, const SeamPosition seam_posit
|
|||
return polygon.points[idx_min];
|
||||
|
||||
} else { // spRandom
|
||||
if (loop.loop_role() == elrContourInternalPerimeter && loop.role() != erExternalPerimeter) {
|
||||
// This loop does not contain any other loop. Set a random position.
|
||||
// The other loops will get a seam close to the random point chosen
|
||||
// on the innermost contour.
|
||||
//FIXME This works correctly for inner contours first only.
|
||||
last_pos = this->get_random_seam(layer_idx, polygon);
|
||||
}
|
||||
if (loop.role() == erExternalPerimeter && is_custom_seam_on_layer(layer_idx)) {
|
||||
// There is a possibility that the loop will be influenced by custom
|
||||
// seam enforcer/blocker. In this case do not inherit the seam
|
||||
// from internal loops (which may conflict with the custom selection
|
||||
// and generate another random one.
|
||||
bool saw_custom = false;
|
||||
Point candidate = this->get_random_seam(layer_idx, polygon, &saw_custom);
|
||||
if (saw_custom)
|
||||
last_pos = candidate;
|
||||
if (po->print()->default_region_config().external_perimeters_first) {
|
||||
if (loop.role() == erExternalPerimeter)
|
||||
last_pos = this->get_random_seam(layer_idx, polygon, po_idx);
|
||||
else {
|
||||
// Internal perimeters will just use last_pos.
|
||||
}
|
||||
} else {
|
||||
if (loop.loop_role() == elrContourInternalPerimeter && loop.role() != erExternalPerimeter) {
|
||||
// This loop does not contain any other loop. Set a random position.
|
||||
// The other loops will get a seam close to the random point chosen
|
||||
// on the innermost contour.
|
||||
last_pos = this->get_random_seam(layer_idx, polygon, po_idx);
|
||||
m_last_loop_was_external = false;
|
||||
}
|
||||
if (loop.role() == erExternalPerimeter) {
|
||||
if (m_last_loop_was_external) {
|
||||
// There was no internal perimeter before this one.
|
||||
last_pos = this->get_random_seam(layer_idx, polygon, po_idx);
|
||||
} else {
|
||||
if (is_custom_seam_on_layer(layer_idx, po_idx)) {
|
||||
// There is a possibility that the loop will be influenced by custom
|
||||
// seam enforcer/blocker. In this case do not inherit the seam
|
||||
// from internal loops (which may conflict with the custom selection
|
||||
// and generate another random one.
|
||||
bool saw_custom = false;
|
||||
Point candidate = this->get_random_seam(layer_idx, polygon, po_idx, &saw_custom);
|
||||
if (saw_custom)
|
||||
last_pos = candidate;
|
||||
}
|
||||
}
|
||||
m_last_loop_was_external = true;
|
||||
}
|
||||
}
|
||||
return last_pos;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Point SeamPlacer::get_random_seam(size_t layer_idx, const Polygon& polygon,
|
||||
Point SeamPlacer::get_random_seam(size_t layer_idx, const Polygon& polygon, size_t po_idx,
|
||||
bool* saw_custom) const
|
||||
{
|
||||
// Parametrize the polygon by its length.
|
||||
|
|
@ -394,7 +505,7 @@ Point SeamPlacer::get_random_seam(size_t layer_idx, const Polygon& polygon,
|
|||
// Which of the points are inside enforcers/blockers?
|
||||
std::vector<size_t> enforcers_idxs;
|
||||
std::vector<size_t> blockers_idxs;
|
||||
this->get_enforcers_and_blockers(layer_idx, polygon, enforcers_idxs, blockers_idxs);
|
||||
this->get_enforcers_and_blockers(layer_idx, polygon, po_idx, enforcers_idxs, blockers_idxs);
|
||||
|
||||
bool has_enforcers = ! enforcers_idxs.empty();
|
||||
bool has_blockers = ! blockers_idxs.empty();
|
||||
|
|
@ -444,32 +555,44 @@ Point SeamPlacer::get_random_seam(size_t layer_idx, const Polygon& polygon,
|
|||
|
||||
void SeamPlacer::get_enforcers_and_blockers(size_t layer_id,
|
||||
const Polygon& polygon,
|
||||
size_t po_idx,
|
||||
std::vector<size_t>& enforcers_idxs,
|
||||
std::vector<size_t>& blockers_idxs) const
|
||||
{
|
||||
enforcers_idxs.clear();
|
||||
blockers_idxs.clear();
|
||||
|
||||
// FIXME: This is quadratic and it should be improved, maybe by building
|
||||
// an AABB tree (or at least utilize bounding boxes).
|
||||
for (size_t i=0; i<polygon.points.size(); ++i) {
|
||||
auto is_inside = [](const Point& pt,
|
||||
const CustomTrianglesPerLayer& custom_data) -> bool {
|
||||
assert(! custom_data.polys.empty());
|
||||
// Now ask the AABB tree which polygon we should check and check it.
|
||||
size_t candidate = AABBTreeIndirect::get_candidate_idx(custom_data.tree, pt);
|
||||
if (candidate != size_t(-1)
|
||||
&& custom_data.polys[candidate].contains(pt))
|
||||
return true;
|
||||
return false;
|
||||
};
|
||||
|
||||
if (! m_enforcers.empty()) {
|
||||
assert(layer_id < m_enforcers.size());
|
||||
for (const ExPolygon& explg : m_enforcers[layer_id]) {
|
||||
if (explg.contains(polygon.points[i]))
|
||||
enforcers_idxs.push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
if (! m_blockers.empty()) {
|
||||
assert(layer_id < m_blockers.size());
|
||||
for (const ExPolygon& explg : m_blockers[layer_id]) {
|
||||
if (explg.contains(polygon.points[i]))
|
||||
blockers_idxs.push_back(i);
|
||||
if (! m_enforcers[po_idx].empty()) {
|
||||
const CustomTrianglesPerLayer& enforcers = m_enforcers[po_idx][layer_id];
|
||||
if (! enforcers.polys.empty()) {
|
||||
for (size_t i=0; i<polygon.points.size(); ++i) {
|
||||
if (is_inside(polygon.points[i], enforcers))
|
||||
enforcers_idxs.emplace_back(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (! m_blockers[po_idx].empty()) {
|
||||
const CustomTrianglesPerLayer& blockers = m_blockers[po_idx][layer_id];
|
||||
if (! blockers.polys.empty()) {
|
||||
for (size_t i=0; i<polygon.points.size(); ++i) {
|
||||
if (is_inside(polygon.points[i], blockers))
|
||||
blockers_idxs.emplace_back(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -543,17 +666,17 @@ static std::vector<size_t> find_enforcer_centers(const Polygon& polygon,
|
|||
|
||||
|
||||
|
||||
void SeamPlacer::apply_custom_seam(const Polygon& polygon,
|
||||
void SeamPlacer::apply_custom_seam(const Polygon& polygon, size_t po_idx,
|
||||
std::vector<float>& penalties,
|
||||
const std::vector<float>& lengths,
|
||||
int layer_id, SeamPosition seam_position) const
|
||||
{
|
||||
if (! is_custom_seam_on_layer(layer_id))
|
||||
if (! is_custom_seam_on_layer(layer_id, po_idx))
|
||||
return;
|
||||
|
||||
std::vector<size_t> enforcers_idxs;
|
||||
std::vector<size_t> blockers_idxs;
|
||||
this->get_enforcers_and_blockers(layer_id, polygon, enforcers_idxs, blockers_idxs);
|
||||
this->get_enforcers_and_blockers(layer_id, polygon, po_idx, enforcers_idxs, blockers_idxs);
|
||||
|
||||
for (size_t i : enforcers_idxs) {
|
||||
assert(i < penalties.size());
|
||||
|
|
|
|||
|
|
@ -3,15 +3,17 @@
|
|||
|
||||
#include <optional>
|
||||
|
||||
#include "libslic3r/ExPolygon.hpp"
|
||||
#include "libslic3r/Polygon.hpp"
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
#include "libslic3r/BoundingBox.hpp"
|
||||
#include "libslic3r/AABBTreeIndirect.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class PrintObject;
|
||||
class ExtrusionLoop;
|
||||
class Print;
|
||||
class Layer;
|
||||
namespace EdgeGrid { class Grid; }
|
||||
|
||||
|
||||
|
|
@ -39,14 +41,31 @@ class SeamPlacer {
|
|||
public:
|
||||
void init(const Print& print);
|
||||
|
||||
Point get_seam(const size_t layer_idx, const SeamPosition seam_position,
|
||||
Point get_seam(const Layer& layer, const SeamPosition seam_position,
|
||||
const ExtrusionLoop& loop, Point last_pos,
|
||||
coordf_t nozzle_diameter, const PrintObject* po,
|
||||
bool was_clockwise, const EdgeGrid::Grid* lower_layer_edge_grid);
|
||||
|
||||
using TreeType = AABBTreeIndirect::Tree<2, coord_t>;
|
||||
using AlignedBoxType = Eigen::AlignedBox<TreeType::CoordType, TreeType::NumDimensions>;
|
||||
|
||||
private:
|
||||
std::vector<ExPolygons> m_enforcers;
|
||||
std::vector<ExPolygons> m_blockers;
|
||||
|
||||
struct CustomTrianglesPerLayer {
|
||||
Polygons polys;
|
||||
TreeType tree;
|
||||
};
|
||||
|
||||
// Just a cache to save some lookups.
|
||||
const Layer* m_last_layer_po = nullptr;
|
||||
coordf_t m_last_print_z = -1.;
|
||||
const PrintObject* m_last_po = nullptr;
|
||||
|
||||
bool m_last_loop_was_external = true;
|
||||
|
||||
std::vector<std::vector<CustomTrianglesPerLayer>> m_enforcers;
|
||||
std::vector<std::vector<CustomTrianglesPerLayer>> m_blockers;
|
||||
std::vector<const PrintObject*> m_po_list;
|
||||
|
||||
//std::map<const PrintObject*, Point> m_last_seam_position;
|
||||
SeamHistory m_seam_history;
|
||||
|
|
@ -54,32 +73,33 @@ private:
|
|||
// Get indices of points inside enforcers and blockers.
|
||||
void get_enforcers_and_blockers(size_t layer_id,
|
||||
const Polygon& polygon,
|
||||
size_t po_id,
|
||||
std::vector<size_t>& enforcers_idxs,
|
||||
std::vector<size_t>& blockers_idxs) const;
|
||||
|
||||
// Apply penalties to points inside enforcers/blockers.
|
||||
void apply_custom_seam(const Polygon& polygon,
|
||||
void apply_custom_seam(const Polygon& polygon, size_t po_id,
|
||||
std::vector<float>& penalties,
|
||||
const std::vector<float>& lengths,
|
||||
int layer_id, SeamPosition seam_position) const;
|
||||
|
||||
// Return random point of a polygon. The distribution will be uniform
|
||||
// along the contour and account for enforcers and blockers.
|
||||
Point get_random_seam(size_t layer_idx, const Polygon& polygon,
|
||||
Point get_random_seam(size_t layer_idx, const Polygon& polygon, size_t po_id,
|
||||
bool* saw_custom = nullptr) const;
|
||||
|
||||
// Is there any enforcer/blocker on this layer?
|
||||
bool is_custom_seam_on_layer(size_t layer_id) const {
|
||||
return is_custom_enforcer_on_layer(layer_id)
|
||||
|| is_custom_blocker_on_layer(layer_id);
|
||||
bool is_custom_seam_on_layer(size_t layer_id, size_t po_idx) const {
|
||||
return is_custom_enforcer_on_layer(layer_id, po_idx)
|
||||
|| is_custom_blocker_on_layer(layer_id, po_idx);
|
||||
}
|
||||
|
||||
bool is_custom_enforcer_on_layer(size_t layer_id) const {
|
||||
return (! m_enforcers.empty() && ! m_enforcers[layer_id].empty());
|
||||
bool is_custom_enforcer_on_layer(size_t layer_id, size_t po_idx) const {
|
||||
return (! m_enforcers.at(po_idx).empty() && ! m_enforcers.at(po_idx)[layer_id].polys.empty());
|
||||
}
|
||||
|
||||
bool is_custom_blocker_on_layer(size_t layer_id) const {
|
||||
return (! m_blockers.empty() && ! m_blockers[layer_id].empty());
|
||||
bool is_custom_blocker_on_layer(size_t layer_id, size_t po_idx) const {
|
||||
return (! m_blockers.at(po_idx).empty() && ! m_blockers.at(po_idx)[layer_id].polys.empty());
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -338,19 +338,19 @@ double rad2deg_dir(double angle)
|
|||
return rad2deg(angle);
|
||||
}
|
||||
|
||||
Point circle_taubin_newton(const Points::const_iterator& input_begin, const Points::const_iterator& input_end, size_t cycles)
|
||||
Point circle_center_taubin_newton(const Points::const_iterator& input_begin, const Points::const_iterator& input_end, size_t cycles)
|
||||
{
|
||||
Vec2ds tmp;
|
||||
tmp.reserve(std::distance(input_begin, input_end));
|
||||
std::transform(input_begin, input_end, std::back_inserter(tmp), [] (const Point& in) { return unscale(in); } );
|
||||
Vec2d center = circle_taubin_newton(tmp.cbegin(), tmp.end(), cycles);
|
||||
Vec2d center = circle_center_taubin_newton(tmp.cbegin(), tmp.end(), cycles);
|
||||
return Point::new_scale(center.x(), center.y());
|
||||
}
|
||||
|
||||
/// Adapted from work in "Circular and Linear Regression: Fitting circles and lines by least squares", pg 126
|
||||
/// Returns a point corresponding to the center of a circle for which all of the points from input_begin to input_end
|
||||
/// lie on.
|
||||
Vec2d circle_taubin_newton(const Vec2ds::const_iterator& input_begin, const Vec2ds::const_iterator& input_end, size_t cycles)
|
||||
Vec2d circle_center_taubin_newton(const Vec2ds::const_iterator& input_begin, const Vec2ds::const_iterator& input_end, size_t cycles)
|
||||
{
|
||||
// calculate the centroid of the data set
|
||||
const Vec2d sum = std::accumulate(input_begin, input_end, Vec2d(0,0));
|
||||
|
|
|
|||
|
|
@ -201,6 +201,57 @@ inline double ray_point_distance(const Line &iline, const Point &ipt)
|
|||
}
|
||||
|
||||
// Based on Liang-Barsky function by Daniel White @ http://www.skytopia.com/project/articles/compsci/clipping.html
|
||||
template<typename T>
|
||||
inline bool liang_barsky_line_clipping_interval(
|
||||
// Start and end points of the source line, result will be stored there as well.
|
||||
const Eigen::Matrix<T, 2, 1, Eigen::DontAlign> &x0,
|
||||
const Eigen::Matrix<T, 2, 1, Eigen::DontAlign> &v,
|
||||
// Bounding box to clip with.
|
||||
const BoundingBoxBase<Eigen::Matrix<T, 2, 1, Eigen::DontAlign>> &bbox,
|
||||
std::pair<double, double> &out_interval)
|
||||
{
|
||||
double t0 = 0.0;
|
||||
double t1 = 1.0;
|
||||
// Traverse through left, right, bottom, top edges.
|
||||
auto clip_side = [&x0, &v, &bbox, &t0, &t1](double p, double q) -> bool {
|
||||
if (p == 0) {
|
||||
if (q < 0)
|
||||
// Line parallel to the bounding box edge is fully outside of the bounding box.
|
||||
return false;
|
||||
// else don't clip
|
||||
} else {
|
||||
double r = q / p;
|
||||
if (p < 0) {
|
||||
if (r > t1)
|
||||
// Fully clipped.
|
||||
return false;
|
||||
if (r > t0)
|
||||
// Partially clipped.
|
||||
t0 = r;
|
||||
} else {
|
||||
assert(p > 0);
|
||||
if (r < t0)
|
||||
// Fully clipped.
|
||||
return false;
|
||||
if (r < t1)
|
||||
// Partially clipped.
|
||||
t1 = r;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
if (clip_side(- v.x(), - bbox.min.x() + x0.x()) &&
|
||||
clip_side( v.x(), bbox.max.x() - x0.x()) &&
|
||||
clip_side(- v.y(), - bbox.min.y() + x0.y()) &&
|
||||
clip_side( v.y(), bbox.max.y() - x0.y())) {
|
||||
out_interval.first = t0;
|
||||
out_interval.second = t1;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline bool liang_barsky_line_clipping(
|
||||
// Start and end points of the source line, result will be stored there as well.
|
||||
|
|
@ -210,49 +261,12 @@ inline bool liang_barsky_line_clipping(
|
|||
const BoundingBoxBase<Eigen::Matrix<T, 2, 1, Eigen::DontAlign>> &bbox)
|
||||
{
|
||||
Eigen::Matrix<T, 2, 1, Eigen::DontAlign> v = x1 - x0;
|
||||
double t0 = 0.0;
|
||||
double t1 = 1.0;
|
||||
|
||||
// Traverse through left, right, bottom, top edges.
|
||||
for (int edge = 0; edge < 4; ++ edge)
|
||||
{
|
||||
double p, q;
|
||||
switch (edge) {
|
||||
case 0: p = - v.x(); q = - bbox.min.x() + x0.x(); break;
|
||||
case 1: p = v.x(); q = bbox.max.x() - x0.x(); break;
|
||||
case 2: p = - v.y(); q = - bbox.min.y() + x0.y(); break;
|
||||
default: p = v.y(); q = bbox.max.y() - x0.y(); break;
|
||||
}
|
||||
|
||||
if (p == 0) {
|
||||
if (q < 0)
|
||||
// Line parallel to the bounding box edge is fully outside of the bounding box.
|
||||
return false;
|
||||
// else don't clip
|
||||
} else {
|
||||
double r = q / p;
|
||||
if (p < 0) {
|
||||
if (r > t1)
|
||||
// Fully clipped.
|
||||
return false;
|
||||
if (r > t0)
|
||||
// Partially clipped.
|
||||
t0 = r;
|
||||
} else {
|
||||
assert(p > 0);
|
||||
if (r < t0)
|
||||
// Fully clipped.
|
||||
return false;
|
||||
if (r < t1)
|
||||
// Partially clipped.
|
||||
t1 = r;
|
||||
}
|
||||
}
|
||||
std::pair<double, double> interval;
|
||||
if (liang_barsky_line_clipping_interval(x0, v, bbox, interval)) {
|
||||
// Clipped successfully.
|
||||
x1 = x0 + interval.second * v;
|
||||
x0 += interval.first * v;
|
||||
}
|
||||
|
||||
// Clipped successfully.
|
||||
x1 = x0 + t1 * v;
|
||||
x0 += t0 * v;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -273,6 +287,35 @@ bool liang_barsky_line_clipping(
|
|||
return liang_barsky_line_clipping(x0clip, x1clip, bbox);
|
||||
}
|
||||
|
||||
// Ugly named variant, that accepts the squared line
|
||||
// Don't call me with a nearly zero length vector!
|
||||
template<typename T>
|
||||
int ray_circle_intersections_r2_lv2_c(T r2, T a, T b, T lv2, T c, std::pair<Eigen::Matrix<T, 2, 1, Eigen::DontAlign>, Eigen::Matrix<T, 2, 1, Eigen::DontAlign>> &out)
|
||||
{
|
||||
T x0 = - a * c / lv2;
|
||||
T y0 = - b * c / lv2;
|
||||
T d = r2 - c * c / lv2;
|
||||
if (d < T(0))
|
||||
return 0;
|
||||
T mult = sqrt(d / lv2);
|
||||
out.first.x() = x0 + b * mult;
|
||||
out.first.y() = y0 - a * mult;
|
||||
out.second.x() = x0 - b * mult;
|
||||
out.second.y() = y0 + a * mult;
|
||||
return mult == T(0) ? 1 : 2;
|
||||
}
|
||||
template<typename T>
|
||||
int ray_circle_intersections(T r, T a, T b, T c, std::pair<Eigen::Matrix<T, 2, 1, Eigen::DontAlign>, Eigen::Matrix<T, 2, 1, Eigen::DontAlign>> &out)
|
||||
{
|
||||
T lv2 = a * a + b * b;
|
||||
if (lv2 < T(SCALED_EPSILON * SCALED_EPSILON)) {
|
||||
//FIXME what is the correct epsilon?
|
||||
// What if the line touches the circle?
|
||||
return false;
|
||||
}
|
||||
return ray_circle_intersections_r2_lv2_c2(r * r, a, b, a * a + b * b, c, out);
|
||||
}
|
||||
|
||||
Pointf3s convex_hull(Pointf3s points);
|
||||
Polygon convex_hull(Points points);
|
||||
Polygon convex_hull(const Polygons &polygons);
|
||||
|
|
@ -298,12 +341,12 @@ template<typename T> T angle_to_0_2PI(T angle)
|
|||
}
|
||||
|
||||
/// Find the center of the circle corresponding to the vector of Points as an arc.
|
||||
Point circle_taubin_newton(const Points::const_iterator& input_start, const Points::const_iterator& input_end, size_t cycles = 20);
|
||||
inline Point circle_taubin_newton(const Points& input, size_t cycles = 20) { return circle_taubin_newton(input.cbegin(), input.cend(), cycles); }
|
||||
Point circle_center_taubin_newton(const Points::const_iterator& input_start, const Points::const_iterator& input_end, size_t cycles = 20);
|
||||
inline Point circle_center_taubin_newton(const Points& input, size_t cycles = 20) { return circle_center_taubin_newton(input.cbegin(), input.cend(), cycles); }
|
||||
|
||||
/// Find the center of the circle corresponding to the vector of Pointfs as an arc.
|
||||
Vec2d circle_taubin_newton(const Vec2ds::const_iterator& input_start, const Vec2ds::const_iterator& input_end, size_t cycles = 20);
|
||||
inline Vec2d circle_taubin_newton(const Vec2ds& input, size_t cycles = 20) { return circle_taubin_newton(input.cbegin(), input.cend(), cycles); }
|
||||
Vec2d circle_center_taubin_newton(const Vec2ds::const_iterator& input_start, const Vec2ds::const_iterator& input_end, size_t cycles = 20);
|
||||
inline Vec2d circle_center_taubin_newton(const Vec2ds& input, size_t cycles = 20) { return circle_center_taubin_newton(input.cbegin(), input.cend(), cycles); }
|
||||
|
||||
void simplify_polygons(const Polygons &polygons, double tolerance, Polygons* retval);
|
||||
|
||||
|
|
|
|||
|
|
@ -100,6 +100,13 @@ bool Line::clip_with_bbox(const BoundingBox &bbox)
|
|||
return result;
|
||||
}
|
||||
|
||||
void Line::extend(double offset)
|
||||
{
|
||||
Vector offset_vector = (offset * this->vector().cast<double>().normalized()).cast<coord_t>();
|
||||
this->a -= offset_vector;
|
||||
this->b += offset_vector;
|
||||
}
|
||||
|
||||
Vec3d Linef3::intersect_plane(double z) const
|
||||
{
|
||||
auto v = (this->b - this->a).cast<double>();
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ namespace line_alg {
|
|||
template<class L, class T, int N>
|
||||
double distance_to_squared(const L &line, const Vec<N, T> &point)
|
||||
{
|
||||
const Vec<N, double> v = line.vector().template cast<double>();
|
||||
const Vec<N, double> v = (line.b - line.a).template cast<double>();
|
||||
const Vec<N, double> va = (point - line.a).template cast<double>();
|
||||
const double l2 = v.squaredNorm(); // avoid a sqrt
|
||||
if (l2 == 0.0)
|
||||
|
|
@ -54,7 +54,8 @@ public:
|
|||
Line(const Point& _a, const Point& _b) : a(_a), b(_b) {}
|
||||
explicit operator Lines() const { Lines lines; lines.emplace_back(*this); return lines; }
|
||||
void scale(double factor) { this->a *= factor; this->b *= factor; }
|
||||
void translate(double x, double y) { Vector v(x, y); this->a += v; this->b += v; }
|
||||
void translate(const Point &v) { this->a += v; this->b += v; }
|
||||
void translate(double x, double y) { this->translate(Point(x, y)); }
|
||||
void rotate(double angle, const Point ¢er) { this->a.rotate(angle, center); this->b.rotate(angle, center); }
|
||||
void reverse() { std::swap(this->a, this->b); }
|
||||
double length() const { return (b - a).cast<double>().norm(); }
|
||||
|
|
@ -75,6 +76,8 @@ public:
|
|||
double ccw(const Point& point) const { return point.ccw(*this); }
|
||||
// Clip a line with a bounding box. Returns false if the line is completely outside of the bounding box.
|
||||
bool clip_with_bbox(const BoundingBox &bbox);
|
||||
// Extend the line from both sides by an offset.
|
||||
void extend(double offset);
|
||||
|
||||
static inline double distance_to_squared(const Point &point, const Point &a, const Point &b) { return line_alg::distance_to_squared(Line{a, b}, Vec<2, coord_t>{point}); }
|
||||
static double distance_to(const Point &point, const Point &a, const Point &b) { return sqrt(distance_to_squared(point, a, b)); }
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ Slic3r::arrangement::ArrangePolygon get_arrange_poly(const Model &model)
|
|||
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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,8 +17,6 @@ class MultiPoint
|
|||
public:
|
||||
Points points;
|
||||
|
||||
operator Points() const { return this->points; }
|
||||
|
||||
MultiPoint() {}
|
||||
MultiPoint(const MultiPoint &other) : points(other.points) {}
|
||||
MultiPoint(MultiPoint &&other) : points(std::move(other.points)) {}
|
||||
|
|
|
|||
|
|
@ -1,100 +0,0 @@
|
|||
#include "PNGRead.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <cstdio>
|
||||
#include <png.h>
|
||||
|
||||
namespace Slic3r { namespace png {
|
||||
|
||||
struct PNGDescr {
|
||||
png_struct *png = nullptr; png_info *info = nullptr;
|
||||
|
||||
PNGDescr() = default;
|
||||
PNGDescr(const PNGDescr&) = delete;
|
||||
PNGDescr(PNGDescr&&) = delete;
|
||||
PNGDescr& operator=(const PNGDescr&) = delete;
|
||||
PNGDescr& operator=(PNGDescr&&) = delete;
|
||||
|
||||
~PNGDescr()
|
||||
{
|
||||
if (png && info) png_destroy_info_struct(png, &info);
|
||||
if (png) png_destroy_read_struct( &png, nullptr, nullptr);
|
||||
}
|
||||
};
|
||||
|
||||
bool is_png(const ReadBuf &rb)
|
||||
{
|
||||
static const constexpr int PNG_SIG_BYTES = 8;
|
||||
|
||||
#if PNG_LIBPNG_VER_MINOR <= 2
|
||||
// Earlier libpng versions had png_sig_cmp(png_bytep, ...) which is not
|
||||
// a const pointer. It is not possible to cast away the const qualifier from
|
||||
// the input buffer so... yes... life is challenging...
|
||||
png_byte buf[PNG_SIG_BYTES];
|
||||
auto inbuf = static_cast<const std::uint8_t *>(rb.buf);
|
||||
std::copy(inbuf, inbuf + PNG_SIG_BYTES, buf);
|
||||
#else
|
||||
auto buf = static_cast<png_const_bytep>(rb.buf);
|
||||
#endif
|
||||
|
||||
return rb.sz >= PNG_SIG_BYTES && !png_sig_cmp(buf, 0, PNG_SIG_BYTES);
|
||||
}
|
||||
|
||||
// Buffer read callback for libpng. It provides an allocated output buffer and
|
||||
// the amount of data it desires to read from the input.
|
||||
void png_read_callback(png_struct *png_ptr,
|
||||
png_bytep outBytes,
|
||||
png_size_t byteCountToRead)
|
||||
{
|
||||
// Retrieve our input buffer through the png_ptr
|
||||
auto reader = static_cast<IStream *>(png_get_io_ptr(png_ptr));
|
||||
|
||||
if (!reader || !reader->is_ok()) return;
|
||||
|
||||
reader->read(static_cast<std::uint8_t *>(outBytes), byteCountToRead);
|
||||
}
|
||||
|
||||
bool decode_png(IStream &in_buf, ImageGreyscale &out_img)
|
||||
{
|
||||
static const constexpr int PNG_SIG_BYTES = 8;
|
||||
|
||||
std::vector<png_byte> sig(PNG_SIG_BYTES, 0);
|
||||
in_buf.read(sig.data(), PNG_SIG_BYTES);
|
||||
if (!png_check_sig(sig.data(), PNG_SIG_BYTES))
|
||||
return false;
|
||||
|
||||
PNGDescr dsc;
|
||||
dsc.png = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr,
|
||||
nullptr);
|
||||
|
||||
if(!dsc.png) return false;
|
||||
|
||||
dsc.info = png_create_info_struct(dsc.png);
|
||||
if(!dsc.info) return false;
|
||||
|
||||
png_set_read_fn(dsc.png, static_cast<void *>(&in_buf), png_read_callback);
|
||||
|
||||
// Tell that we have already read the first bytes to check the signature
|
||||
png_set_sig_bytes(dsc.png, PNG_SIG_BYTES);
|
||||
|
||||
png_read_info(dsc.png, dsc.info);
|
||||
|
||||
out_img.cols = png_get_image_width(dsc.png, dsc.info);
|
||||
out_img.rows = png_get_image_height(dsc.png, dsc.info);
|
||||
size_t color_type = png_get_color_type(dsc.png, dsc.info);
|
||||
size_t bit_depth = png_get_bit_depth(dsc.png, dsc.info);
|
||||
|
||||
if (color_type != PNG_COLOR_TYPE_GRAY || bit_depth != 8)
|
||||
return false;
|
||||
|
||||
out_img.buf.resize(out_img.rows * out_img.cols);
|
||||
|
||||
auto readbuf = static_cast<png_bytep>(out_img.buf.data());
|
||||
for (size_t r = 0; r < out_img.rows; ++r)
|
||||
png_read_row(dsc.png, readbuf + r * out_img.cols, nullptr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}} // namespace Slic3r::png
|
||||
225
src/libslic3r/PNGReadWrite.cpp
Normal file
225
src/libslic3r/PNGReadWrite.cpp
Normal file
|
|
@ -0,0 +1,225 @@
|
|||
#include "PNGReadWrite.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <cstdio>
|
||||
#include <png.h>
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/nowide/cstdio.hpp>
|
||||
|
||||
namespace Slic3r { namespace png {
|
||||
|
||||
struct PNGDescr {
|
||||
png_struct *png = nullptr; png_info *info = nullptr;
|
||||
|
||||
PNGDescr() = default;
|
||||
PNGDescr(const PNGDescr&) = delete;
|
||||
PNGDescr(PNGDescr&&) = delete;
|
||||
PNGDescr& operator=(const PNGDescr&) = delete;
|
||||
PNGDescr& operator=(PNGDescr&&) = delete;
|
||||
|
||||
~PNGDescr()
|
||||
{
|
||||
if (png && info) png_destroy_info_struct(png, &info);
|
||||
if (png) png_destroy_read_struct( &png, nullptr, nullptr);
|
||||
}
|
||||
};
|
||||
|
||||
bool is_png(const ReadBuf &rb)
|
||||
{
|
||||
static const constexpr int PNG_SIG_BYTES = 8;
|
||||
|
||||
#if PNG_LIBPNG_VER_MINOR <= 2
|
||||
// Earlier libpng versions had png_sig_cmp(png_bytep, ...) which is not
|
||||
// a const pointer. It is not possible to cast away the const qualifier from
|
||||
// the input buffer so... yes... life is challenging...
|
||||
png_byte buf[PNG_SIG_BYTES];
|
||||
auto inbuf = static_cast<const std::uint8_t *>(rb.buf);
|
||||
std::copy(inbuf, inbuf + PNG_SIG_BYTES, buf);
|
||||
#else
|
||||
auto buf = static_cast<png_const_bytep>(rb.buf);
|
||||
#endif
|
||||
|
||||
return rb.sz >= PNG_SIG_BYTES && !png_sig_cmp(buf, 0, PNG_SIG_BYTES);
|
||||
}
|
||||
|
||||
// Buffer read callback for libpng. It provides an allocated output buffer and
|
||||
// the amount of data it desires to read from the input.
|
||||
static void png_read_callback(png_struct *png_ptr,
|
||||
png_bytep outBytes,
|
||||
png_size_t byteCountToRead)
|
||||
{
|
||||
// Retrieve our input buffer through the png_ptr
|
||||
auto reader = static_cast<IStream *>(png_get_io_ptr(png_ptr));
|
||||
|
||||
if (!reader || !reader->is_ok()) return;
|
||||
|
||||
reader->read(static_cast<std::uint8_t *>(outBytes), byteCountToRead);
|
||||
}
|
||||
|
||||
bool decode_png(IStream &in_buf, ImageGreyscale &out_img)
|
||||
{
|
||||
static const constexpr int PNG_SIG_BYTES = 8;
|
||||
|
||||
std::vector<png_byte> sig(PNG_SIG_BYTES, 0);
|
||||
in_buf.read(sig.data(), PNG_SIG_BYTES);
|
||||
if (!png_check_sig(sig.data(), PNG_SIG_BYTES))
|
||||
return false;
|
||||
|
||||
PNGDescr dsc;
|
||||
dsc.png = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr,
|
||||
nullptr);
|
||||
|
||||
if(!dsc.png) return false;
|
||||
|
||||
dsc.info = png_create_info_struct(dsc.png);
|
||||
if(!dsc.info) return false;
|
||||
|
||||
png_set_read_fn(dsc.png, static_cast<void *>(&in_buf), png_read_callback);
|
||||
|
||||
// Tell that we have already read the first bytes to check the signature
|
||||
png_set_sig_bytes(dsc.png, PNG_SIG_BYTES);
|
||||
|
||||
png_read_info(dsc.png, dsc.info);
|
||||
|
||||
out_img.cols = png_get_image_width(dsc.png, dsc.info);
|
||||
out_img.rows = png_get_image_height(dsc.png, dsc.info);
|
||||
size_t color_type = png_get_color_type(dsc.png, dsc.info);
|
||||
size_t bit_depth = png_get_bit_depth(dsc.png, dsc.info);
|
||||
|
||||
if (color_type != PNG_COLOR_TYPE_GRAY || bit_depth != 8)
|
||||
return false;
|
||||
|
||||
out_img.buf.resize(out_img.rows * out_img.cols);
|
||||
|
||||
auto readbuf = static_cast<png_bytep>(out_img.buf.data());
|
||||
for (size_t r = 0; r < out_img.rows; ++r)
|
||||
png_read_row(dsc.png, readbuf + r * out_img.cols, nullptr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Down to earth function to store a packed RGB image to file. Mostly useful for debugging purposes.
|
||||
// Based on https://www.lemoda.net/c/write-png/
|
||||
bool write_rgb_to_file(const char *file_name_utf8, size_t width, size_t height, const uint8_t *data_rgb)
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
// Forward declaration due to the gotos.
|
||||
png_structp png_ptr = nullptr;
|
||||
png_infop info_ptr = nullptr;
|
||||
png_byte **row_pointers = nullptr;
|
||||
|
||||
FILE *fp = boost::nowide::fopen(file_name_utf8, "wb");
|
||||
if (! fp) {
|
||||
BOOST_LOG_TRIVIAL(error) << "write_png_file: File could not be opened for writing: " << file_name_utf8;
|
||||
goto fopen_failed;
|
||||
}
|
||||
|
||||
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
|
||||
if (! png_ptr) {
|
||||
BOOST_LOG_TRIVIAL(error) << "write_png_file: png_create_write_struct() failed";
|
||||
goto png_create_write_struct_failed;
|
||||
}
|
||||
|
||||
info_ptr = png_create_info_struct(png_ptr);
|
||||
if (! info_ptr) {
|
||||
BOOST_LOG_TRIVIAL(error) << "write_png_file: png_create_info_struct() failed";
|
||||
goto png_create_info_struct_failed;
|
||||
}
|
||||
|
||||
// Set up error handling.
|
||||
if (setjmp(png_jmpbuf(png_ptr))) {
|
||||
BOOST_LOG_TRIVIAL(error) << "write_png_file: setjmp() failed";
|
||||
goto png_failure;
|
||||
}
|
||||
|
||||
// Set image attributes.
|
||||
png_set_IHDR(png_ptr,
|
||||
info_ptr,
|
||||
png_uint_32(width),
|
||||
png_uint_32(height),
|
||||
8, // depth
|
||||
PNG_COLOR_TYPE_RGB,
|
||||
PNG_INTERLACE_NONE,
|
||||
PNG_COMPRESSION_TYPE_DEFAULT,
|
||||
PNG_FILTER_TYPE_DEFAULT);
|
||||
|
||||
// Initialize rows of PNG.
|
||||
row_pointers = reinterpret_cast<png_byte**>(::png_malloc(png_ptr, height * sizeof(png_byte*)));
|
||||
for (size_t y = 0; y < height; ++ y) {
|
||||
auto row = reinterpret_cast<png_byte*>(::png_malloc(png_ptr, sizeof(uint8_t) * width * 3));
|
||||
row_pointers[y] = row;
|
||||
memcpy(row, data_rgb + width * y * 3, sizeof(uint8_t) * width * 3);
|
||||
}
|
||||
|
||||
// Write the image data to "fp".
|
||||
png_init_io(png_ptr, fp);
|
||||
png_set_rows(png_ptr, info_ptr, row_pointers);
|
||||
png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, nullptr);
|
||||
|
||||
for (size_t y = 0; y < height; ++ y)
|
||||
png_free(png_ptr, row_pointers[y]);
|
||||
png_free(png_ptr, row_pointers);
|
||||
|
||||
result = true;
|
||||
|
||||
png_failure:
|
||||
png_create_info_struct_failed:
|
||||
::png_destroy_write_struct(&png_ptr, &info_ptr);
|
||||
png_create_write_struct_failed:
|
||||
::fclose(fp);
|
||||
fopen_failed:
|
||||
return result;
|
||||
}
|
||||
|
||||
bool write_rgb_to_file(const std::string &file_name_utf8, size_t width, size_t height, const uint8_t *data_rgb)
|
||||
{
|
||||
return write_rgb_to_file(file_name_utf8.c_str(), width, height, data_rgb);
|
||||
}
|
||||
|
||||
bool write_rgb_to_file(const std::string &file_name_utf8, size_t width, size_t height, const std::vector<uint8_t> &data_rgb)
|
||||
{
|
||||
assert(width * height * 3 == data_rgb.size());
|
||||
return write_rgb_to_file(file_name_utf8.c_str(), width, height, data_rgb.data());
|
||||
}
|
||||
|
||||
// Scaled variants are mostly useful for debugging purposes, for example to export images of low resolution distance fileds.
|
||||
// Scaling is done by multiplying rows and columns without any smoothing to emphasise the original pixels.
|
||||
bool write_rgb_to_file_scaled(const char *file_name_utf8, size_t width, size_t height, const uint8_t *data_rgb, size_t scale)
|
||||
{
|
||||
if (scale <= 1)
|
||||
return write_rgb_to_file(file_name_utf8, width, height, data_rgb);
|
||||
else {
|
||||
std::vector<uint8_t> scaled(width * height * 3 * scale * scale);
|
||||
uint8_t *dst = scaled.data();
|
||||
for (size_t r = 0; r < height; ++ r) {
|
||||
for (size_t repr = 0; repr < scale; ++ repr) {
|
||||
const uint8_t *row = data_rgb + width * 3 * r;
|
||||
for (size_t c = 0; c < width; ++ c) {
|
||||
for (size_t repc = 0; repc < scale; ++ repc) {
|
||||
*dst ++ = row[0];
|
||||
*dst ++ = row[1];
|
||||
*dst ++ = row[2];
|
||||
}
|
||||
row += 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
return write_rgb_to_file(file_name_utf8, width * scale, height * scale, scaled.data());
|
||||
}
|
||||
}
|
||||
|
||||
bool write_rgb_to_file_scaled(const std::string &file_name_utf8, size_t width, size_t height, const uint8_t *data_rgb, size_t scale)
|
||||
{
|
||||
return write_rgb_to_file_scaled(file_name_utf8.c_str(), width, height, data_rgb, scale);
|
||||
}
|
||||
|
||||
bool write_rgb_to_file_scaled(const std::string &file_name_utf8, size_t width, size_t height, const std::vector<uint8_t> &data_rgb, size_t scale)
|
||||
{
|
||||
assert(width * height * 3 == data_rgb.size());
|
||||
return write_rgb_to_file_scaled(file_name_utf8.c_str(), width, height, data_rgb.data(), scale);
|
||||
}
|
||||
|
||||
}} // namespace Slic3r::png
|
||||
|
|
@ -65,6 +65,18 @@ template<class Img> bool decode_png(const ReadBuf &in_buf, Img &out_img)
|
|||
|
||||
// TODO: std::istream of FILE* could be similarly adapted in case its needed...
|
||||
|
||||
|
||||
|
||||
// Down to earth function to store a packed RGB image to file. Mostly useful for debugging purposes.
|
||||
bool write_rgb_to_file(const char *file_name_utf8, size_t width, size_t height, const uint8_t *data_rgb);
|
||||
bool write_rgb_to_file(const std::string &file_name_utf8, size_t width, size_t height, const uint8_t *data_rgb);
|
||||
bool write_rgb_to_file(const std::string &file_name_utf8, size_t width, size_t height, const std::vector<uint8_t> &data_rgb);
|
||||
// Scaled variants are mostly useful for debugging purposes, for example to export images of low resolution distance fileds.
|
||||
// Scaling is done by multiplying rows and columns without any smoothing to emphasise the original pixels.
|
||||
bool write_rgb_to_file_scaled(const char *file_name_utf8, size_t width, size_t height, const uint8_t *data_rgb, size_t scale);
|
||||
bool write_rgb_to_file_scaled(const std::string &file_name_utf8, size_t width, size_t height, const uint8_t *data_rgb, size_t scale);
|
||||
bool write_rgb_to_file_scaled(const std::string &file_name_utf8, size_t width, size_t height, const std::vector<uint8_t> &data_rgb, size_t scale);
|
||||
|
||||
}} // namespace Slic3r::png
|
||||
|
||||
#endif // PNGREAD_HPP
|
||||
|
|
@ -158,7 +158,7 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime
|
|||
// get non-overhang paths by intersecting this loop with the grown lower slices
|
||||
extrusion_paths_append(
|
||||
paths,
|
||||
intersection_pl(loop.polygon, perimeter_generator.lower_slices_polygons()),
|
||||
intersection_pl((Polygons)loop.polygon, perimeter_generator.lower_slices_polygons()),
|
||||
role,
|
||||
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,
|
||||
|
|
@ -169,7 +169,7 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime
|
|||
// the loop centerline and original lower slices is >= half nozzle diameter
|
||||
extrusion_paths_append(
|
||||
paths,
|
||||
diff_pl(loop.polygon, perimeter_generator.lower_slices_polygons()),
|
||||
diff_pl((Polygons)loop.polygon, perimeter_generator.lower_slices_polygons()),
|
||||
erOverhangPerimeter,
|
||||
perimeter_generator.mm3_per_mm_overhang(),
|
||||
perimeter_generator.overhang_flow.width,
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
#include "Line.hpp"
|
||||
#include "MultiPoint.hpp"
|
||||
#include "Int128.hpp"
|
||||
#include "BoundingBox.hpp"
|
||||
#include <algorithm>
|
||||
|
||||
namespace Slic3r {
|
||||
|
|
@ -176,6 +177,19 @@ Point Point::projection_onto(const Line &line) const
|
|||
return ((line.a - *this).cast<double>().squaredNorm() < (line.b - *this).cast<double>().squaredNorm()) ? line.a : line.b;
|
||||
}
|
||||
|
||||
BoundingBox get_extents(const Points &pts)
|
||||
{
|
||||
return BoundingBox(pts);
|
||||
}
|
||||
|
||||
BoundingBox get_extents(const std::vector<Points> &pts)
|
||||
{
|
||||
BoundingBox bbox;
|
||||
for (const Points &p : pts)
|
||||
bbox.merge(get_extents(p));
|
||||
return bbox;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream &stm, const Vec2d &pointf)
|
||||
{
|
||||
return stm << pointf(0) << "," << pointf(1);
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
namespace Slic3r {
|
||||
|
||||
class BoundingBox;
|
||||
class Line;
|
||||
class MultiPoint;
|
||||
class Point;
|
||||
|
|
@ -55,23 +56,20 @@ typedef Eigen::Transform<double, 3, Eigen::Affine, Eigen::DontAlign> Transform3d
|
|||
|
||||
inline bool operator<(const Vec2d &lhs, const Vec2d &rhs) { return lhs(0) < rhs(0) || (lhs(0) == rhs(0) && lhs(1) < rhs(1)); }
|
||||
|
||||
inline int32_t cross2(const Vec2i32 &v1, const Vec2i32 &v2) { return v1(0) * v2(1) - v1(1) * v2(0); }
|
||||
// One likely does not want to perform the cross product with a 32bit accumulator.
|
||||
//inline int32_t cross2(const Vec2i32 &v1, const Vec2i32 &v2) { return v1(0) * v2(1) - v1(1) * v2(0); }
|
||||
inline int64_t cross2(const Vec2i64 &v1, const Vec2i64 &v2) { return v1(0) * v2(1) - v1(1) * v2(0); }
|
||||
inline float cross2(const Vec2f &v1, const Vec2f &v2) { return v1(0) * v2(1) - v1(1) * v2(0); }
|
||||
inline double cross2(const Vec2d &v1, const Vec2d &v2) { return v1(0) * v2(1) - v1(1) * v2(0); }
|
||||
|
||||
template<class T, int N> Eigen::Matrix<T, 2, 1, Eigen::DontAlign>
|
||||
to_2d(const Eigen::Matrix<T, N, 1, Eigen::DontAlign> &ptN) { return {ptN(0), ptN(1)}; }
|
||||
template<typename T, int Options>
|
||||
inline Eigen::Matrix<T, 2, 1, Eigen::DontAlign> perp(const Eigen::MatrixBase<Eigen::Matrix<T, 2, 1, Options>> &v) { return Eigen::Matrix<T, 2, 1, Eigen::DontAlign>(- v.y(), v.x()); }
|
||||
|
||||
//inline Vec2i32 to_2d(const Vec3i32 &pt3) { return Vec2i32(pt3(0), pt3(1)); }
|
||||
//inline Vec2i64 to_2d(const Vec3i64 &pt3) { return Vec2i64(pt3(0), pt3(1)); }
|
||||
//inline Vec2f to_2d(const Vec3f &pt3) { return Vec2f (pt3(0), pt3(1)); }
|
||||
//inline Vec2d to_2d(const Vec3d &pt3) { return Vec2d (pt3(0), pt3(1)); }
|
||||
template<class T, int N, int Options>
|
||||
Eigen::Matrix<T, 2, 1, Eigen::DontAlign> to_2d(const Eigen::MatrixBase<Eigen::Matrix<T, N, 1, Options>> &ptN) { return { ptN(0), ptN(1) }; }
|
||||
|
||||
inline Vec3d to_3d(const Vec2d &v, double z) { return Vec3d(v(0), v(1), z); }
|
||||
inline Vec3f to_3d(const Vec2f &v, float z) { return Vec3f(v(0), v(1), z); }
|
||||
inline Vec3i64 to_3d(const Vec2i64 &v, float z) { return Vec3i64(int64_t(v(0)), int64_t(v(1)), int64_t(z)); }
|
||||
inline Vec3crd to_3d(const Vec3crd &p, coord_t z) { return Vec3crd(p(0), p(1), z); }
|
||||
template<class T, int Options>
|
||||
Eigen::Matrix<T, 3, 1, Eigen::DontAlign> to_3d(const Eigen::MatrixBase<Eigen::Matrix<T, 2, 1, Options>> & pt, const T z) { return { pt(0), pt(1), z }; }
|
||||
|
||||
inline Vec2d unscale(coord_t x, coord_t y) { return Vec2d(unscale<double>(x), unscale<double>(y)); }
|
||||
inline Vec2d unscale(const Vec2crd &pt) { return Vec2d(unscale<double>(pt(0)), unscale<double>(pt(1))); }
|
||||
|
|
@ -132,6 +130,7 @@ public:
|
|||
|
||||
void rotate(double angle, const Point ¢er);
|
||||
Point rotated(double angle) const { Point res(*this); res.rotate(angle); return res; }
|
||||
Point rotated(double cos_a, double sin_a) const { Point res(*this); res.rotate(cos_a, sin_a); return res; }
|
||||
Point rotated(double angle, const Point ¢er) const { Point res(*this); res.rotate(angle, center); return res; }
|
||||
int nearest_point_index(const Points &points) const;
|
||||
int nearest_point_index(const PointConstPtrs &points) const;
|
||||
|
|
@ -174,6 +173,15 @@ inline bool is_approx(const Vec3d &p1, const Vec3d &p2, double epsilon = EPSILON
|
|||
return d.x() < epsilon && d.y() < epsilon && d.z() < epsilon;
|
||||
}
|
||||
|
||||
inline Point lerp(const Point &a, const Point &b, double t)
|
||||
{
|
||||
assert((t >= -EPSILON) && (t <= 1. + EPSILON));
|
||||
return ((1. - t) * a.cast<double>() + t * b.cast<double>()).cast<coord_t>();
|
||||
}
|
||||
|
||||
extern BoundingBox get_extents(const Points &pts);
|
||||
extern BoundingBox get_extents(const std::vector<Points> &pts);
|
||||
|
||||
namespace int128 {
|
||||
// Exact orientation predicate,
|
||||
// returns +1: CCW, 0: collinear, -1: CW.
|
||||
|
|
@ -291,6 +299,33 @@ public:
|
|||
std::make_pair(nullptr, std::numeric_limits<double>::max());
|
||||
}
|
||||
|
||||
// Returns all pairs of values and squared distances.
|
||||
std::vector<std::pair<const ValueType*, double>> find_all(const Vec2crd &pt) {
|
||||
// Iterate over 4 closest grid cells around pt,
|
||||
// Round pt to a closest grid_cell corner.
|
||||
Vec2crd grid_corner((pt(0)+(m_grid_resolution>>1))>>m_grid_log2, (pt(1)+(m_grid_resolution>>1))>>m_grid_log2);
|
||||
// For four neighbors of grid_corner:
|
||||
std::vector<std::pair<const ValueType*, double>> out;
|
||||
const double r2 = double(m_search_radius) * m_search_radius;
|
||||
for (coord_t neighbor_y = -1; neighbor_y < 1; ++ neighbor_y) {
|
||||
for (coord_t neighbor_x = -1; neighbor_x < 1; ++ neighbor_x) {
|
||||
// Range of fragment starts around grid_corner, close to pt.
|
||||
auto range = m_map.equal_range(Vec2crd(grid_corner(0) + neighbor_x, grid_corner(1) + neighbor_y));
|
||||
// Find the map entry closest to pt.
|
||||
for (auto it = range.first; it != range.second; ++it) {
|
||||
const ValueType &value = it->second;
|
||||
const Vec2crd *pt2 = m_point_accessor(value);
|
||||
if (pt2 != nullptr) {
|
||||
const double d2 = (pt - *pt2).cast<double>().squaredNorm();
|
||||
if (d2 <= r2)
|
||||
out.emplace_back(&value, d2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
private:
|
||||
typedef typename std::unordered_multimap<Vec2crd, ValueType, PointHash> map_type;
|
||||
PointAccessor m_point_accessor;
|
||||
|
|
|
|||
|
|
@ -298,11 +298,6 @@ void Polygon::densify(float min_length, std::vector<float>* lengths_ptr)
|
|||
assert(points.size() == lengths.size() - 1);
|
||||
}
|
||||
|
||||
BoundingBox get_extents(const Points &points)
|
||||
{
|
||||
return BoundingBox(points);
|
||||
}
|
||||
|
||||
BoundingBox get_extents(const Polygon &poly)
|
||||
{
|
||||
return poly.bounding_box();
|
||||
|
|
|
|||
|
|
@ -16,12 +16,12 @@ typedef std::vector<Polygon> Polygons;
|
|||
class Polygon : public MultiPoint
|
||||
{
|
||||
public:
|
||||
operator Polygons() const { Polygons pp; pp.push_back(*this); return pp; }
|
||||
operator Polyline() const { return this->split_at_first_point(); }
|
||||
explicit operator Polygons() const { Polygons pp; pp.push_back(*this); return pp; }
|
||||
explicit operator Polyline() const { return this->split_at_first_point(); }
|
||||
Point& operator[](Points::size_type idx) { return this->points[idx]; }
|
||||
const Point& operator[](Points::size_type idx) const { return this->points[idx]; }
|
||||
|
||||
Polygon() {}
|
||||
Polygon() = default;
|
||||
virtual ~Polygon() = default;
|
||||
explicit Polygon(const Points &points) : MultiPoint(points) {}
|
||||
Polygon(std::initializer_list<Point> points) : MultiPoint(points) {}
|
||||
|
|
@ -74,7 +74,6 @@ public:
|
|||
inline bool operator==(const Polygon &lhs, const Polygon &rhs) { return lhs.points == rhs.points; }
|
||||
inline bool operator!=(const Polygon &lhs, const Polygon &rhs) { return lhs.points != rhs.points; }
|
||||
|
||||
extern BoundingBox get_extents(const Points &points);
|
||||
extern BoundingBox get_extents(const Polygon &poly);
|
||||
extern BoundingBox get_extents(const Polygons &polygons);
|
||||
extern BoundingBox get_extents_rotated(const Polygon &poly, double angle);
|
||||
|
|
|
|||
|
|
@ -200,7 +200,7 @@ BoundingBox get_extents(const Polylines &polylines)
|
|||
if (! polylines.empty()) {
|
||||
bb = polylines.front().bounding_box();
|
||||
for (size_t i = 1; i < polylines.size(); ++ i)
|
||||
bb.merge(polylines[i]);
|
||||
bb.merge(polylines[i].points);
|
||||
}
|
||||
return bb;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,8 +60,8 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
operator Polylines() const;
|
||||
operator Line() const;
|
||||
explicit operator Polylines() const;
|
||||
explicit operator Line() const;
|
||||
const Point& last_point() const override { return this->points.back(); }
|
||||
|
||||
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",
|
||||
"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",
|
||||
"top_infill_extrusion_width", "support_material_extrusion_width", "infill_overlap", "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",
|
||||
"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"
|
||||
|
|
@ -439,7 +439,7 @@ const std::vector<std::string>& Preset::filament_options()
|
|||
{
|
||||
static std::vector<std::string> s_opts {
|
||||
"filament_colour", "filament_diameter", "filament_type", "filament_soluble", "filament_notes", "filament_max_volumetric_speed",
|
||||
"extrusion_multiplier", "filament_density", "filament_cost", "filament_loading_speed", "filament_loading_speed_start", "filament_load_time",
|
||||
"extrusion_multiplier", "filament_density", "filament_cost", "filament_spool_weight", "filament_loading_speed", "filament_loading_speed_start", "filament_load_time",
|
||||
"filament_unloading_speed", "filament_unloading_speed_start", "filament_unload_time", "filament_toolchange_delay", "filament_cooling_moves",
|
||||
"filament_cooling_initial_speed", "filament_cooling_final_speed", "filament_ramming_parameters", "filament_minimal_purge_on_wipe_tower",
|
||||
"temperature", "first_layer_temperature", "bed_temperature", "first_layer_bed_temperature", "fan_always_on", "cooling", "min_fan_speed",
|
||||
|
|
@ -1840,8 +1840,11 @@ namespace PresetUtils {
|
|||
{
|
||||
std::string out;
|
||||
const VendorProfile::PrinterModel* pm = PresetUtils::system_printer_model(preset);
|
||||
if (pm != nullptr && !pm->bed_model.empty())
|
||||
out = Slic3r::resources_dir() + "/profiles/" + preset.vendor->id + "/" + pm->bed_model;
|
||||
if (pm != nullptr && !pm->bed_model.empty()) {
|
||||
out = Slic3r::data_dir() + "/vendor/" + preset.vendor->id + "/" + pm->bed_model;
|
||||
if (!boost::filesystem::exists(boost::filesystem::path(out)))
|
||||
out = Slic3r::resources_dir() + "/profiles/" + preset.vendor->id + "/" + pm->bed_model;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
|
|
@ -1849,8 +1852,11 @@ namespace PresetUtils {
|
|||
{
|
||||
std::string out;
|
||||
const VendorProfile::PrinterModel* pm = PresetUtils::system_printer_model(preset);
|
||||
if (pm != nullptr && !pm->bed_texture.empty())
|
||||
out = Slic3r::resources_dir() + "/profiles/" + preset.vendor->id + "/" + pm->bed_texture;
|
||||
if (pm != nullptr && !pm->bed_texture.empty()) {
|
||||
out = Slic3r::data_dir() + "/vendor/" + preset.vendor->id + "/" + pm->bed_texture;
|
||||
if (!boost::filesystem::exists(boost::filesystem::path(out)))
|
||||
out = Slic3r::resources_dir() + "/profiles/" + preset.vendor->id + "/" + pm->bed_texture;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
} // namespace PresetUtils
|
||||
|
|
|
|||
|
|
@ -99,6 +99,7 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
|
|||
"filament_density",
|
||||
"filament_notes",
|
||||
"filament_cost",
|
||||
"filament_spool_weight",
|
||||
"first_layer_acceleration",
|
||||
"first_layer_bed_temperature",
|
||||
"first_layer_speed",
|
||||
|
|
@ -1220,9 +1221,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.
|
||||
// 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());
|
||||
if (! intersection(convex_hulls_other, convex_hull).empty())
|
||||
if (! intersection(convex_hulls_other, (Polygons)convex_hull).empty())
|
||||
return false;
|
||||
polygons_append(convex_hulls_other, convex_hull);
|
||||
convex_hulls_other.emplace_back(std::move(convex_hull));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -470,12 +470,14 @@ void PrintConfigDef::init_fff_params()
|
|||
def->enum_keys_map = &ConfigOptionEnum<InfillPattern>::get_enum_values();
|
||||
def->enum_values.push_back("rectilinear");
|
||||
def->enum_values.push_back("monotonic");
|
||||
def->enum_values.push_back("alignedrectilinear");
|
||||
def->enum_values.push_back("concentric");
|
||||
def->enum_values.push_back("hilbertcurve");
|
||||
def->enum_values.push_back("archimedeanchords");
|
||||
def->enum_values.push_back("octagramspiral");
|
||||
def->enum_labels.push_back(L("Rectilinear"));
|
||||
def->enum_labels.push_back(L("Monotonic"));
|
||||
def->enum_labels.push_back(L("Aligned Rectilinear"));
|
||||
def->enum_labels.push_back(L("Concentric"));
|
||||
def->enum_labels.push_back(L("Hilbert Curve"));
|
||||
def->enum_labels.push_back(L("Archimedean Chords"));
|
||||
|
|
@ -493,7 +495,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->enum_values = def_top_fill_pattern->enum_values;
|
||||
def->enum_labels = def_top_fill_pattern->enum_labels;
|
||||
def->aliases = def_top_fill_pattern->aliases;
|
||||
def->set_default_value(new ConfigOptionEnum<InfillPattern>(ipRectilinear));
|
||||
def->set_default_value(new ConfigOptionEnum<InfillPattern>(ipMonotonic));
|
||||
|
||||
def = this->add("external_perimeter_extrusion_width", coFloatOrPercent);
|
||||
def->label = L("External perimeters");
|
||||
|
|
@ -816,6 +818,16 @@ void PrintConfigDef::init_fff_params()
|
|||
def->min = 0;
|
||||
def->set_default_value(new ConfigOptionFloats { 0. });
|
||||
|
||||
def = this->add("filament_spool_weight", coFloats);
|
||||
def->label = L("Spool weight");
|
||||
def->tooltip = L("Enter weight of the empty filament spool. "
|
||||
"One may weigh a partially consumed filament spool before printing and one may compare the measured weight "
|
||||
"with the calculated weight of the filament with the spool to find out whether the amount "
|
||||
"of filament on the spool is sufficient to finish the print.");
|
||||
def->sidetext = L("g");
|
||||
def->min = 0;
|
||||
def->set_default_value(new ConfigOptionFloats { 0. });
|
||||
|
||||
def = this->add("filament_settings_id", coStrings);
|
||||
def->set_default_value(new ConfigOptionStrings { "" });
|
||||
def->cli = ConfigOptionDef::nocli;
|
||||
|
|
@ -881,6 +893,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->tooltip = L("Fill pattern for general low-density infill.");
|
||||
def->enum_keys_map = &ConfigOptionEnum<InfillPattern>::get_enum_values();
|
||||
def->enum_values.push_back("rectilinear");
|
||||
def->enum_values.push_back("alignedrectilinear");
|
||||
def->enum_values.push_back("grid");
|
||||
def->enum_values.push_back("triangles");
|
||||
def->enum_values.push_back("stars");
|
||||
|
|
@ -896,6 +909,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->enum_values.push_back("adaptivecubic");
|
||||
def->enum_values.push_back("supportcubic");
|
||||
def->enum_labels.push_back(L("Rectilinear"));
|
||||
def->enum_labels.push_back(L("Aligned Rectilinear"));
|
||||
def->enum_labels.push_back(L("Grid"));
|
||||
def->enum_labels.push_back(L("Triangles"));
|
||||
def->enum_labels.push_back(L("Stars"));
|
||||
|
|
@ -1060,6 +1074,55 @@ void PrintConfigDef::init_fff_params()
|
|||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionInt(1));
|
||||
|
||||
auto def_infill_anchor_min = def = this->add("infill_anchor", coFloatOrPercent);
|
||||
def->label = L("Length of the infill anchor");
|
||||
def->category = L("Advanced");
|
||||
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 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->ratio_over = "infill_extrusion_width";
|
||||
def->gui_type = "f_enum_open";
|
||||
def->enum_values.push_back("0");
|
||||
def->enum_values.push_back("1");
|
||||
def->enum_values.push_back("2");
|
||||
def->enum_values.push_back("5");
|
||||
def->enum_values.push_back("10");
|
||||
def->enum_values.push_back("1000");
|
||||
def->enum_labels.push_back(L("0 (no open anchors)"));
|
||||
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 = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloatOrPercent(600, true));
|
||||
|
||||
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->label = L("Infill extruder");
|
||||
def->category = L("Extruders");
|
||||
|
|
@ -1527,8 +1590,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def = this->add("perimeter_acceleration", coFloat);
|
||||
def->label = L("Perimeters");
|
||||
def->tooltip = L("This is the acceleration your printer will use for perimeters. "
|
||||
"A high value like 9000 usually gives good results if your hardware is up to the job. "
|
||||
"Set zero to disable acceleration control for perimeters.");
|
||||
"Set zero to disable acceleration control for perimeters.");
|
||||
def->sidetext = L("mm/s²");
|
||||
def->mode = comExpert;
|
||||
def->set_default_value(new ConfigOptionFloat(0));
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ enum AuthorizationType {
|
|||
};
|
||||
|
||||
enum InfillPattern : int {
|
||||
ipRectilinear, ipMonotonic, ipGrid, ipTriangles, ipStars, ipCubic, ipLine, ipConcentric, ipHoneycomb, ip3DHoneycomb,
|
||||
ipRectilinear, ipMonotonic, ipAlignedRectilinear, ipGrid, ipTriangles, ipStars, ipCubic, ipLine, ipConcentric, ipHoneycomb, ip3DHoneycomb,
|
||||
ipGyroid, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, ipAdaptiveCubic, ipSupportCubic, ipCount,
|
||||
};
|
||||
|
||||
|
|
@ -145,6 +145,7 @@ template<> inline const t_config_enum_values& ConfigOptionEnum<InfillPattern>::g
|
|||
if (keys_map.empty()) {
|
||||
keys_map["rectilinear"] = ipRectilinear;
|
||||
keys_map["monotonic"] = ipMonotonic;
|
||||
keys_map["alignedrectilinear"] = ipAlignedRectilinear;
|
||||
keys_map["grid"] = ipGrid;
|
||||
keys_map["triangles"] = ipTriangles;
|
||||
keys_map["stars"] = ipStars;
|
||||
|
|
@ -530,6 +531,8 @@ public:
|
|||
ConfigOptionPercent fill_density;
|
||||
ConfigOptionEnum<InfillPattern> fill_pattern;
|
||||
ConfigOptionFloat gap_fill_speed;
|
||||
ConfigOptionFloatOrPercent infill_anchor;
|
||||
ConfigOptionFloatOrPercent infill_anchor_max;
|
||||
ConfigOptionInt infill_extruder;
|
||||
ConfigOptionFloatOrPercent infill_extrusion_width;
|
||||
ConfigOptionInt infill_every_layers;
|
||||
|
|
@ -581,6 +584,8 @@ protected:
|
|||
OPT_PTR(fill_density);
|
||||
OPT_PTR(fill_pattern);
|
||||
OPT_PTR(gap_fill_speed);
|
||||
OPT_PTR(infill_anchor);
|
||||
OPT_PTR(infill_anchor_max);
|
||||
OPT_PTR(infill_extruder);
|
||||
OPT_PTR(infill_extrusion_width);
|
||||
OPT_PTR(infill_every_layers);
|
||||
|
|
@ -681,6 +686,7 @@ public:
|
|||
ConfigOptionStrings filament_type;
|
||||
ConfigOptionBools filament_soluble;
|
||||
ConfigOptionFloats filament_cost;
|
||||
ConfigOptionFloats filament_spool_weight;
|
||||
ConfigOptionFloats filament_max_volumetric_speed;
|
||||
ConfigOptionFloats filament_loading_speed;
|
||||
ConfigOptionFloats filament_loading_speed_start;
|
||||
|
|
@ -757,6 +763,7 @@ protected:
|
|||
OPT_PTR(filament_type);
|
||||
OPT_PTR(filament_soluble);
|
||||
OPT_PTR(filament_cost);
|
||||
OPT_PTR(filament_spool_weight);
|
||||
OPT_PTR(filament_max_volumetric_speed);
|
||||
OPT_PTR(filament_loading_speed);
|
||||
OPT_PTR(filament_loading_speed_start);
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@
|
|||
#include "Slicing.hpp"
|
||||
#include "Tesselate.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "AABBTreeIndirect.hpp"
|
||||
#include "Fill/FillAdaptive.hpp"
|
||||
#include "Format/STL.hpp"
|
||||
|
||||
|
|
@ -590,7 +589,8 @@ bool PrintObject::invalidate_state_by_config_options(const std::vector<t_config_
|
|||
|| opt_key == "external_fill_link_max_length"
|
||||
|| opt_key == "fill_angle"
|
||||
|| opt_key == "fill_pattern"
|
||||
|| opt_key == "fill_link_max_length"
|
||||
|| opt_key == "infill_anchor"
|
||||
|| opt_key == "infill_anchor_max"
|
||||
|| opt_key == "top_infill_extrusion_width"
|
||||
|| opt_key == "first_layer_extrusion_width") {
|
||||
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; }
|
||||
|
||||
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; }
|
||||
|
||||
|
|
|
|||
|
|
@ -350,6 +350,7 @@ struct SLAPrintStatistics
|
|||
size_t fast_layers_count;
|
||||
double total_cost;
|
||||
double total_weight;
|
||||
std::vector<double> layers_times;
|
||||
|
||||
// Config with the filled in print statistics.
|
||||
DynamicConfig config() const;
|
||||
|
|
@ -366,6 +367,7 @@ struct SLAPrintStatistics
|
|||
fast_layers_count = 0;
|
||||
total_cost = 0.;
|
||||
total_weight = 0.;
|
||||
layers_times.clear();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -671,6 +671,8 @@ void SLAPrint::Steps::merge_slices_and_eval_stats() {
|
|||
double models_volume(0.0);
|
||||
|
||||
double estim_time(0.0);
|
||||
std::vector<double> layers_times;
|
||||
layers_times.reserve(printer_input.size());
|
||||
|
||||
size_t slow_layers = 0;
|
||||
size_t fast_layers = 0;
|
||||
|
|
@ -688,7 +690,7 @@ void SLAPrint::Steps::merge_slices_and_eval_stats() {
|
|||
|
||||
// write vars
|
||||
&mutex, &models_volume, &supports_volume, &estim_time, &slow_layers,
|
||||
&fast_layers, &fade_layer_time](size_t sliced_layer_cnt)
|
||||
&fast_layers, &fade_layer_time, &layers_times](size_t sliced_layer_cnt)
|
||||
{
|
||||
PrintLayer &layer = m_print->m_printer_input[sliced_layer_cnt];
|
||||
|
||||
|
|
@ -775,20 +777,21 @@ void SLAPrint::Steps::merge_slices_and_eval_stats() {
|
|||
else
|
||||
slow_layers++;
|
||||
|
||||
|
||||
// Calculation of the printing time
|
||||
|
||||
|
||||
double layer_times = 0.0;
|
||||
if (sliced_layer_cnt < 3)
|
||||
estim_time += init_exp_time;
|
||||
else if (fade_layer_time > exp_time)
|
||||
{
|
||||
layer_times += init_exp_time;
|
||||
else if (fade_layer_time > exp_time) {
|
||||
fade_layer_time -= delta_fade_time;
|
||||
estim_time += fade_layer_time;
|
||||
layer_times += fade_layer_time;
|
||||
}
|
||||
else
|
||||
estim_time += exp_time;
|
||||
|
||||
estim_time += tilt_time;
|
||||
layer_times += exp_time;
|
||||
layer_times += tilt_time;
|
||||
|
||||
layers_times.push_back(layer_times);
|
||||
estim_time += layer_times;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -804,8 +807,10 @@ void SLAPrint::Steps::merge_slices_and_eval_stats() {
|
|||
// A layers count o the highest object
|
||||
if (printer_input.size() == 0)
|
||||
print_statistics.estimated_print_time = std::nan("");
|
||||
else
|
||||
else {
|
||||
print_statistics.estimated_print_time = estim_time;
|
||||
print_statistics.layers_times = layers_times;
|
||||
}
|
||||
|
||||
print_statistics.fast_layers_count = fast_layers;
|
||||
print_statistics.slow_layers_count = slow_layers;
|
||||
|
|
|
|||
|
|
@ -3,8 +3,6 @@
|
|||
|
||||
#include <boost/nowide/cstdio.hpp>
|
||||
|
||||
#define COORD(x) (unscale<float>((x))*10)
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
bool SVG::open(const char* afilename)
|
||||
|
|
@ -33,8 +31,9 @@ bool SVG::open(const char* afilename, const BoundingBox &bbox, const coord_t bbo
|
|||
this->f = boost::nowide::fopen(afilename, "w");
|
||||
if (f == NULL)
|
||||
return false;
|
||||
float w = COORD(bbox.max(0) - bbox.min(0) + 2 * bbox_offset);
|
||||
float h = COORD(bbox.max(1) - bbox.min(1) + 2 * bbox_offset);
|
||||
float w = to_svg_coord(bbox.max(0) - bbox.min(0) + 2 * bbox_offset);
|
||||
float h = to_svg_coord(bbox.max(1) - bbox.min(1) + 2 * bbox_offset);
|
||||
this->height = h;
|
||||
fprintf(this->f,
|
||||
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n"
|
||||
"<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.0//EN\" \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n"
|
||||
|
|
@ -47,12 +46,11 @@ bool SVG::open(const char* afilename, const BoundingBox &bbox, const coord_t bbo
|
|||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
SVG::draw(const Line &line, std::string stroke, coordf_t stroke_width)
|
||||
void SVG::draw(const Line &line, std::string stroke, coordf_t stroke_width)
|
||||
{
|
||||
fprintf(this->f,
|
||||
" <line x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\" style=\"stroke: %s; stroke-width: %f\"",
|
||||
COORD(line.a(0) - origin(0)), COORD(line.a(1) - origin(1)), COORD(line.b(0) - origin(0)), COORD(line.b(1) - origin(1)), stroke.c_str(), (stroke_width == 0) ? 1.f : COORD(stroke_width));
|
||||
to_svg_x(line.a(0) - origin(0)), to_svg_y(line.a(1) - origin(1)), to_svg_x(line.b(0) - origin(0)), to_svg_y(line.b(1) - origin(1)), stroke.c_str(), (stroke_width == 0) ? 1.f : to_svg_coord(stroke_width));
|
||||
if (this->arrows)
|
||||
fprintf(this->f, " marker-end=\"url(#endArrow)\"");
|
||||
fprintf(this->f, "/>\n");
|
||||
|
|
@ -67,34 +65,31 @@ void SVG::draw(const ThickLine &line, const std::string &fill, const std::string
|
|||
coordf_t db = coordf_t(0.5)*line.b_width/len;
|
||||
fprintf(this->f,
|
||||
" <polygon points=\"%f,%f %f,%f %f,%f %f,%f\" style=\"fill:%s; stroke: %s; stroke-width: %f\"/>\n",
|
||||
COORD(line.a(0)-da*perp(0)-origin(0)),
|
||||
COORD(line.a(1)-da*perp(1)-origin(1)),
|
||||
COORD(line.b(0)-db*perp(0)-origin(0)),
|
||||
COORD(line.b(1)-db*perp(1)-origin(1)),
|
||||
COORD(line.b(0)+db*perp(0)-origin(0)),
|
||||
COORD(line.b(1)+db*perp(1)-origin(1)),
|
||||
COORD(line.a(0)+da*perp(0)-origin(0)),
|
||||
COORD(line.a(1)+da*perp(1)-origin(1)),
|
||||
to_svg_x(line.a(0)-da*perp(0)-origin(0)),
|
||||
to_svg_y(line.a(1)-da*perp(1)-origin(1)),
|
||||
to_svg_x(line.b(0)-db*perp(0)-origin(0)),
|
||||
to_svg_y(line.b(1)-db*perp(1)-origin(1)),
|
||||
to_svg_x(line.b(0)+db*perp(0)-origin(0)),
|
||||
to_svg_y(line.b(1)+db*perp(1)-origin(1)),
|
||||
to_svg_x(line.a(0)+da*perp(0)-origin(0)),
|
||||
to_svg_y(line.a(1)+da*perp(1)-origin(1)),
|
||||
fill.c_str(), stroke.c_str(),
|
||||
(stroke_width == 0) ? 1.f : COORD(stroke_width));
|
||||
(stroke_width == 0) ? 1.f : to_svg_coord(stroke_width));
|
||||
}
|
||||
|
||||
void
|
||||
SVG::draw(const Lines &lines, std::string stroke, coordf_t stroke_width)
|
||||
void SVG::draw(const Lines &lines, std::string stroke, coordf_t stroke_width)
|
||||
{
|
||||
for (Lines::const_iterator it = lines.begin(); it != lines.end(); ++it)
|
||||
this->draw(*it, stroke, stroke_width);
|
||||
for (const Line &l : lines)
|
||||
this->draw(l, stroke, stroke_width);
|
||||
}
|
||||
|
||||
void
|
||||
SVG::draw(const IntersectionLines &lines, std::string stroke)
|
||||
void SVG::draw(const IntersectionLines &lines, std::string stroke)
|
||||
{
|
||||
for (IntersectionLines::const_iterator it = lines.begin(); it != lines.end(); ++it)
|
||||
this->draw((Line)*it, stroke);
|
||||
for (const IntersectionLine &il : lines)
|
||||
this->draw((Line)il, stroke);
|
||||
}
|
||||
|
||||
void
|
||||
SVG::draw(const ExPolygon &expolygon, std::string fill, const float fill_opacity)
|
||||
void SVG::draw(const ExPolygon &expolygon, std::string fill, const float fill_opacity)
|
||||
{
|
||||
this->fill = fill;
|
||||
|
||||
|
|
@ -106,8 +101,7 @@ SVG::draw(const ExPolygon &expolygon, std::string fill, const float fill_opacity
|
|||
this->path(d, true, 0, fill_opacity);
|
||||
}
|
||||
|
||||
void
|
||||
SVG::draw_outline(const ExPolygon &expolygon, std::string stroke_outer, std::string stroke_holes, coordf_t stroke_width)
|
||||
void SVG::draw_outline(const ExPolygon &expolygon, std::string stroke_outer, std::string stroke_holes, coordf_t stroke_width)
|
||||
{
|
||||
draw_outline(expolygon.contour, stroke_outer, stroke_width);
|
||||
for (Polygons::const_iterator it = expolygon.holes.begin(); it != expolygon.holes.end(); ++ it) {
|
||||
|
|
@ -115,83 +109,71 @@ SVG::draw_outline(const ExPolygon &expolygon, std::string stroke_outer, std::str
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
SVG::draw(const ExPolygons &expolygons, std::string fill, const float fill_opacity)
|
||||
void SVG::draw(const ExPolygons &expolygons, std::string fill, const float fill_opacity)
|
||||
{
|
||||
for (ExPolygons::const_iterator it = expolygons.begin(); it != expolygons.end(); ++it)
|
||||
this->draw(*it, fill, fill_opacity);
|
||||
}
|
||||
|
||||
void
|
||||
SVG::draw_outline(const ExPolygons &expolygons, std::string stroke_outer, std::string stroke_holes, coordf_t stroke_width)
|
||||
void SVG::draw_outline(const ExPolygons &expolygons, std::string stroke_outer, std::string stroke_holes, coordf_t stroke_width)
|
||||
{
|
||||
for (ExPolygons::const_iterator it = expolygons.begin(); it != expolygons.end(); ++ it)
|
||||
draw_outline(*it, stroke_outer, stroke_holes, stroke_width);
|
||||
}
|
||||
|
||||
void
|
||||
SVG::draw(const Surface &surface, std::string fill, const float fill_opacity)
|
||||
void SVG::draw(const Surface &surface, std::string fill, const float fill_opacity)
|
||||
{
|
||||
draw(surface.expolygon, fill, fill_opacity);
|
||||
}
|
||||
|
||||
void
|
||||
SVG::draw_outline(const Surface &surface, std::string stroke_outer, std::string stroke_holes, coordf_t stroke_width)
|
||||
void SVG::draw_outline(const Surface &surface, std::string stroke_outer, std::string stroke_holes, coordf_t stroke_width)
|
||||
{
|
||||
draw_outline(surface.expolygon, stroke_outer, stroke_holes, stroke_width);
|
||||
}
|
||||
|
||||
void
|
||||
SVG::draw(const Surfaces &surfaces, std::string fill, const float fill_opacity)
|
||||
void SVG::draw(const Surfaces &surfaces, std::string fill, const float fill_opacity)
|
||||
{
|
||||
for (Surfaces::const_iterator it = surfaces.begin(); it != surfaces.end(); ++it)
|
||||
this->draw(*it, fill, fill_opacity);
|
||||
}
|
||||
|
||||
void
|
||||
SVG::draw_outline(const Surfaces &surfaces, std::string stroke_outer, std::string stroke_holes, coordf_t stroke_width)
|
||||
void SVG::draw_outline(const Surfaces &surfaces, std::string stroke_outer, std::string stroke_holes, coordf_t stroke_width)
|
||||
{
|
||||
for (Surfaces::const_iterator it = surfaces.begin(); it != surfaces.end(); ++ it)
|
||||
draw_outline(*it, stroke_outer, stroke_holes, stroke_width);
|
||||
}
|
||||
|
||||
void
|
||||
SVG::draw(const SurfacesPtr &surfaces, std::string fill, const float fill_opacity)
|
||||
void SVG::draw(const SurfacesPtr &surfaces, std::string fill, const float fill_opacity)
|
||||
{
|
||||
for (SurfacesPtr::const_iterator it = surfaces.begin(); it != surfaces.end(); ++it)
|
||||
this->draw(*(*it), fill, fill_opacity);
|
||||
}
|
||||
|
||||
void
|
||||
SVG::draw_outline(const SurfacesPtr &surfaces, std::string stroke_outer, std::string stroke_holes, coordf_t stroke_width)
|
||||
void SVG::draw_outline(const SurfacesPtr &surfaces, std::string stroke_outer, std::string stroke_holes, coordf_t stroke_width)
|
||||
{
|
||||
for (SurfacesPtr::const_iterator it = surfaces.begin(); it != surfaces.end(); ++ it)
|
||||
draw_outline(*(*it), stroke_outer, stroke_holes, stroke_width);
|
||||
}
|
||||
|
||||
void
|
||||
SVG::draw(const Polygon &polygon, std::string fill)
|
||||
void SVG::draw(const Polygon &polygon, std::string fill)
|
||||
{
|
||||
this->fill = fill;
|
||||
this->path(this->get_path_d(polygon, true), !fill.empty(), 0, 1.f);
|
||||
}
|
||||
|
||||
void
|
||||
SVG::draw(const Polygons &polygons, std::string fill)
|
||||
void SVG::draw(const Polygons &polygons, std::string fill)
|
||||
{
|
||||
for (Polygons::const_iterator it = polygons.begin(); it != polygons.end(); ++it)
|
||||
this->draw(*it, fill);
|
||||
}
|
||||
|
||||
void
|
||||
SVG::draw(const Polyline &polyline, std::string stroke, coordf_t stroke_width)
|
||||
void SVG::draw(const Polyline &polyline, std::string stroke, coordf_t stroke_width)
|
||||
{
|
||||
this->stroke = stroke;
|
||||
this->path(this->get_path_d(polyline, false), false, stroke_width, 1.f);
|
||||
}
|
||||
|
||||
void
|
||||
SVG::draw(const Polylines &polylines, std::string stroke, coordf_t stroke_width)
|
||||
void SVG::draw(const Polylines &polylines, std::string stroke, coordf_t stroke_width)
|
||||
{
|
||||
for (Polylines::const_iterator it = polylines.begin(); it != polylines.end(); ++it)
|
||||
this->draw(*it, stroke, stroke_width);
|
||||
|
|
@ -203,73 +185,64 @@ void SVG::draw(const ThickLines &thicklines, const std::string &fill, const std:
|
|||
this->draw(*it, fill, stroke, stroke_width);
|
||||
}
|
||||
|
||||
void
|
||||
SVG::draw(const ThickPolylines &polylines, const std::string &stroke, coordf_t stroke_width)
|
||||
void SVG::draw(const ThickPolylines &polylines, const std::string &stroke, coordf_t stroke_width)
|
||||
{
|
||||
for (ThickPolylines::const_iterator it = polylines.begin(); it != polylines.end(); ++it)
|
||||
this->draw((Polyline)*it, stroke, stroke_width);
|
||||
}
|
||||
|
||||
void
|
||||
SVG::draw(const ThickPolylines &thickpolylines, const std::string &fill, const std::string &stroke, coordf_t stroke_width)
|
||||
void SVG::draw(const ThickPolylines &thickpolylines, const std::string &fill, const std::string &stroke, coordf_t stroke_width)
|
||||
{
|
||||
for (ThickPolylines::const_iterator it = thickpolylines.begin(); it != thickpolylines.end(); ++ it)
|
||||
draw(it->thicklines(), fill, stroke, stroke_width);
|
||||
}
|
||||
|
||||
void
|
||||
SVG::draw(const Point &point, std::string fill, coord_t iradius)
|
||||
void SVG::draw(const Point &point, std::string fill, coord_t iradius)
|
||||
{
|
||||
float radius = (iradius == 0) ? 3.f : COORD(iradius);
|
||||
float radius = (iradius == 0) ? 3.f : to_svg_coord(iradius);
|
||||
std::ostringstream svg;
|
||||
svg << " <circle cx=\"" << COORD(point(0) - origin(0)) << "\" cy=\"" << COORD(point(1) - origin(1))
|
||||
svg << " <circle cx=\"" << to_svg_x(point(0) - origin(0)) << "\" cy=\"" << to_svg_y(point(1) - origin(1))
|
||||
<< "\" r=\"" << radius << "\" "
|
||||
<< "style=\"stroke: none; fill: " << fill << "\" />";
|
||||
|
||||
fprintf(this->f, "%s\n", svg.str().c_str());
|
||||
}
|
||||
|
||||
void
|
||||
SVG::draw(const Points &points, std::string fill, coord_t radius)
|
||||
void SVG::draw(const Points &points, std::string fill, coord_t radius)
|
||||
{
|
||||
for (Points::const_iterator it = points.begin(); it != points.end(); ++it)
|
||||
this->draw(*it, fill, radius);
|
||||
}
|
||||
|
||||
void
|
||||
SVG::draw(const ClipperLib::Path &polygon, double scale, std::string stroke, coordf_t stroke_width)
|
||||
void SVG::draw(const ClipperLib::Path &polygon, double scale, std::string stroke, coordf_t stroke_width)
|
||||
{
|
||||
this->stroke = stroke;
|
||||
this->path(this->get_path_d(polygon, scale, true), false, stroke_width, 1.f);
|
||||
}
|
||||
|
||||
void
|
||||
SVG::draw(const ClipperLib::Paths &polygons, double scale, std::string stroke, coordf_t stroke_width)
|
||||
void SVG::draw(const ClipperLib::Paths &polygons, double scale, std::string stroke, coordf_t stroke_width)
|
||||
{
|
||||
for (ClipperLib::Paths::const_iterator it = polygons.begin(); it != polygons.end(); ++ it)
|
||||
draw(*it, scale, stroke, stroke_width);
|
||||
}
|
||||
|
||||
void
|
||||
SVG::draw_outline(const Polygon &polygon, std::string stroke, coordf_t stroke_width)
|
||||
void SVG::draw_outline(const Polygon &polygon, std::string stroke, coordf_t stroke_width)
|
||||
{
|
||||
this->stroke = stroke;
|
||||
this->path(this->get_path_d(polygon, true), false, stroke_width, 1.f);
|
||||
}
|
||||
|
||||
void
|
||||
SVG::draw_outline(const Polygons &polygons, std::string stroke, coordf_t stroke_width)
|
||||
void SVG::draw_outline(const Polygons &polygons, std::string stroke, coordf_t stroke_width)
|
||||
{
|
||||
for (Polygons::const_iterator it = polygons.begin(); it != polygons.end(); ++ it)
|
||||
draw_outline(*it, stroke, stroke_width);
|
||||
}
|
||||
|
||||
void
|
||||
SVG::path(const std::string &d, bool fill, coordf_t stroke_width, const float fill_opacity)
|
||||
void SVG::path(const std::string &d, bool fill, coordf_t stroke_width, const float fill_opacity)
|
||||
{
|
||||
float lineWidth = 0.f;
|
||||
if (! fill)
|
||||
lineWidth = (stroke_width == 0) ? 2.f : COORD(stroke_width);
|
||||
lineWidth = (stroke_width == 0) ? 2.f : to_svg_coord(stroke_width);
|
||||
|
||||
fprintf(
|
||||
this->f,
|
||||
|
|
@ -283,27 +256,25 @@ SVG::path(const std::string &d, bool fill, coordf_t stroke_width, const float fi
|
|||
);
|
||||
}
|
||||
|
||||
std::string
|
||||
SVG::get_path_d(const MultiPoint &mp, bool closed) const
|
||||
std::string SVG::get_path_d(const MultiPoint &mp, bool closed) const
|
||||
{
|
||||
std::ostringstream d;
|
||||
d << "M ";
|
||||
for (Points::const_iterator p = mp.points.begin(); p != mp.points.end(); ++p) {
|
||||
d << COORD((*p)(0) - origin(0)) << " ";
|
||||
d << COORD((*p)(1) - origin(1)) << " ";
|
||||
d << to_svg_x((*p)(0) - origin(0)) << " ";
|
||||
d << to_svg_y((*p)(1) - origin(1)) << " ";
|
||||
}
|
||||
if (closed) d << "z";
|
||||
return d.str();
|
||||
}
|
||||
|
||||
std::string
|
||||
SVG::get_path_d(const ClipperLib::Path &path, double scale, bool closed) const
|
||||
std::string SVG::get_path_d(const ClipperLib::Path &path, double scale, bool closed) const
|
||||
{
|
||||
std::ostringstream d;
|
||||
d << "M ";
|
||||
for (ClipperLib::Path::const_iterator p = path.begin(); p != path.end(); ++p) {
|
||||
d << COORD(scale * p->X - origin(0)) << " ";
|
||||
d << COORD(scale * p->Y - origin(1)) << " ";
|
||||
d << to_svg_x(scale * p->X - origin(0)) << " ";
|
||||
d << to_svg_y(scale * p->Y - origin(1)) << " ";
|
||||
}
|
||||
if (closed) d << "z";
|
||||
return d.str();
|
||||
|
|
@ -313,8 +284,8 @@ void SVG::draw_text(const Point &pt, const char *text, const char *color)
|
|||
{
|
||||
fprintf(this->f,
|
||||
"<text x=\"%f\" y=\"%f\" font-family=\"sans-serif\" font-size=\"20px\" fill=\"%s\">%s</text>",
|
||||
COORD(pt(0)-origin(0)),
|
||||
COORD(pt(1)-origin(1)),
|
||||
to_svg_x(pt(0)-origin(0)),
|
||||
to_svg_y(pt(1)-origin(1)),
|
||||
color, text);
|
||||
}
|
||||
|
||||
|
|
@ -322,18 +293,17 @@ void SVG::draw_legend(const Point &pt, const char *text, const char *color)
|
|||
{
|
||||
fprintf(this->f,
|
||||
"<circle cx=\"%f\" cy=\"%f\" r=\"10\" fill=\"%s\"/>",
|
||||
COORD(pt(0)-origin(0)),
|
||||
COORD(pt(1)-origin(1)),
|
||||
to_svg_x(pt(0)-origin(0)),
|
||||
to_svg_y(pt(1)-origin(1)),
|
||||
color);
|
||||
fprintf(this->f,
|
||||
"<text x=\"%f\" y=\"%f\" font-family=\"sans-serif\" font-size=\"10px\" fill=\"%s\">%s</text>",
|
||||
COORD(pt(0)-origin(0)) + 20.f,
|
||||
COORD(pt(1)-origin(1)),
|
||||
to_svg_x(pt(0)-origin(0)) + 20.f,
|
||||
to_svg_y(pt(1)-origin(1)),
|
||||
"black", text);
|
||||
}
|
||||
|
||||
void
|
||||
SVG::Close()
|
||||
void SVG::Close()
|
||||
{
|
||||
fprintf(this->f, "</svg>\n");
|
||||
fclose(this->f);
|
||||
|
|
|
|||
|
|
@ -16,27 +16,28 @@ public:
|
|||
bool arrows;
|
||||
std::string fill, stroke;
|
||||
Point origin;
|
||||
bool flipY;
|
||||
float height;
|
||||
bool flipY;
|
||||
|
||||
SVG(const char* afilename) :
|
||||
arrows(false), fill("grey"), stroke("black"), filename(afilename), flipY(false)
|
||||
{ open(filename); }
|
||||
SVG(const char* afilename, const BoundingBox &bbox, const coord_t bbox_offset = scale_(1.), bool aflipY = false) :
|
||||
arrows(false), fill("grey"), stroke("black"), filename(afilename), origin(bbox.min - Point(bbox_offset, bbox_offset)), flipY(aflipY)
|
||||
{ open(filename, bbox, bbox_offset, aflipY); }
|
||||
SVG(const char* afilename, const BoundingBox &bbox, const coord_t bbox_offset = scale_(1.), bool flipY = true) :
|
||||
arrows(false), fill("grey"), stroke("black"), filename(afilename), origin(bbox.min - Point(bbox_offset, bbox_offset)), flipY(flipY)
|
||||
{ open(filename, bbox, bbox_offset, flipY); }
|
||||
SVG(const std::string &filename) :
|
||||
arrows(false), fill("grey"), stroke("black"), filename(filename), flipY(false)
|
||||
{ open(filename); }
|
||||
SVG(const std::string &filename, const BoundingBox &bbox, const coord_t bbox_offset = scale_(1.), bool aflipY = false) :
|
||||
arrows(false), fill("grey"), stroke("black"), filename(filename), origin(bbox.min - Point(bbox_offset, bbox_offset)), flipY(aflipY)
|
||||
{ open(filename, bbox, bbox_offset, aflipY); }
|
||||
SVG(const std::string &filename, const BoundingBox &bbox, const coord_t bbox_offset = scale_(1.), bool flipY = true) :
|
||||
arrows(false), fill("grey"), stroke("black"), filename(filename), origin(bbox.min - Point(bbox_offset, bbox_offset)), flipY(flipY)
|
||||
{ open(filename, bbox, bbox_offset, flipY); }
|
||||
~SVG() { if (f != NULL) Close(); }
|
||||
|
||||
bool open(const char* filename);
|
||||
bool open(const char* filename, const BoundingBox &bbox, const coord_t bbox_offset = scale_(1.), bool flipY = false);
|
||||
bool open(const char* filename, const BoundingBox &bbox, const coord_t bbox_offset = scale_(1.), bool flipY = true);
|
||||
bool open(const std::string &filename)
|
||||
{ return open(filename.c_str()); }
|
||||
bool open(const std::string &filename, const BoundingBox &bbox, const coord_t bbox_offset = scale_(1.), bool flipY = false)
|
||||
bool open(const std::string &filename, const BoundingBox &bbox, const coord_t bbox_offset = scale_(1.), bool flipY = true)
|
||||
{ return open(filename.c_str(), bbox, bbox_offset, flipY); }
|
||||
|
||||
void draw(const Line &line, std::string stroke = "black", coordf_t stroke_width = 0);
|
||||
|
|
@ -127,6 +128,11 @@ public:
|
|||
};
|
||||
|
||||
static void export_expolygons(const char *path, const std::vector<std::pair<Slic3r::ExPolygons, ExPolygonAttributes>> &expolygons_with_attributes);
|
||||
|
||||
private:
|
||||
static float to_svg_coord(float x) throw() { return unscale<float>(x) * 10.f; }
|
||||
static float to_svg_x(float x) throw() { return to_svg_coord(x); }
|
||||
float to_svg_y(float x) const throw() { return flipY ? this->height - to_svg_coord(x) : to_svg_coord(x); }
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2324,7 +2324,6 @@ static inline void fill_expolygons_generate_paths(
|
|||
{
|
||||
FillParams fill_params;
|
||||
fill_params.density = density;
|
||||
fill_params.complete = true;
|
||||
fill_params.dont_adjust = true;
|
||||
for (const ExPolygon &expoly : expolygons) {
|
||||
Surface surface(stInternal, expoly);
|
||||
|
|
@ -2351,7 +2350,6 @@ static inline void fill_expolygons_generate_paths(
|
|||
{
|
||||
FillParams fill_params;
|
||||
fill_params.density = density;
|
||||
fill_params.complete = true;
|
||||
fill_params.dont_adjust = true;
|
||||
for (ExPolygon &expoly : expolygons) {
|
||||
Surface surface(stInternal, std::move(expoly));
|
||||
|
|
@ -2515,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];
|
||||
const Point *seg_current_pt = nullptr;
|
||||
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.
|
||||
//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.
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
#ifndef _prusaslicer_technologies_h_
|
||||
#define _prusaslicer_technologies_h_
|
||||
|
||||
//============
|
||||
//=============
|
||||
// debug techs
|
||||
//============
|
||||
//=============
|
||||
|
||||
// Shows camera target in the 3D scene
|
||||
#define ENABLE_SHOW_CAMERA_TARGET 0
|
||||
|
|
@ -23,20 +23,24 @@
|
|||
#define DISABLE_INSTANCES_SYNCH 0
|
||||
// Use wxDataViewRender instead of wxDataViewCustomRenderer
|
||||
#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
|
||||
//================
|
||||
//=================
|
||||
#define ENABLE_2_2_0_RC1 1
|
||||
|
||||
// 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)
|
||||
|
||||
|
||||
//===================
|
||||
//====================
|
||||
// 2.3.0.alpha1 techs
|
||||
//===================
|
||||
//====================
|
||||
#define ENABLE_2_3_0_ALPHA1 1
|
||||
|
||||
// Enable rendering of objects using environment map
|
||||
|
|
@ -51,26 +55,32 @@
|
|||
// 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)
|
||||
|
||||
// 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
|
||||
//===================
|
||||
//====================
|
||||
#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 (1 && ENABLE_2_3_0_ALPHA3)
|
||||
|
||||
|
||||
//===================
|
||||
//====================
|
||||
// 2.3.0.alpha4 techs
|
||||
//===================
|
||||
//====================
|
||||
#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_2_3_0_ALPHA4)
|
||||
|
||||
|
||||
//===================
|
||||
// 2.3.0.beta1 techs
|
||||
//===================
|
||||
#define ENABLE_2_3_0_BETA1 1
|
||||
|
||||
#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_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN (1 && ENABLE_2_3_0_BETA1)
|
||||
|
||||
|
||||
#endif // _prusaslicer_technologies_h_
|
||||
|
|
|
|||
|
|
@ -103,12 +103,6 @@ enum Axis {
|
|||
NUM_AXES_WITH_UNKNOWN,
|
||||
};
|
||||
|
||||
template <class T>
|
||||
inline void append_to(std::vector<T> &dst, const std::vector<T> &src)
|
||||
{
|
||||
dst.insert(dst.end(), src.begin(), src.end());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void append(std::vector<T>& dest, const std::vector<T>& src)
|
||||
{
|
||||
|
|
@ -131,6 +125,30 @@ inline void append(std::vector<T>& dest, std::vector<T>&& src)
|
|||
src.shrink_to_fit();
|
||||
}
|
||||
|
||||
// Append the source in reverse.
|
||||
template <typename T>
|
||||
inline void append_reversed(std::vector<T>& dest, const std::vector<T>& src)
|
||||
{
|
||||
if (dest.empty())
|
||||
dest = src;
|
||||
else
|
||||
dest.insert(dest.end(), src.rbegin(), src.rend());
|
||||
}
|
||||
|
||||
// Append the source in reverse.
|
||||
template <typename T>
|
||||
inline void append_reversed(std::vector<T>& dest, std::vector<T>&& src)
|
||||
{
|
||||
if (dest.empty())
|
||||
dest = std::move(src);
|
||||
else {
|
||||
dest.reserve(dest.size() + src.size());
|
||||
std::move(std::rbegin(src), std::rend(src), std::back_inserter(dest));
|
||||
}
|
||||
src.clear();
|
||||
src.shrink_to_fit();
|
||||
}
|
||||
|
||||
// Casting an std::vector<> from one type to another type without warnings about a loss of accuracy.
|
||||
template<typename T_TO, typename T_FROM>
|
||||
std::vector<T_TO> cast(const std::vector<T_FROM> &src)
|
||||
|
|
|
|||
|
|
@ -432,7 +432,7 @@ CopyFileResult copy_file_inner(const std::string& from, const std::string& to, s
|
|||
boost::system::error_code ec;
|
||||
boost::filesystem::permissions(target, perms, ec);
|
||||
if (ec)
|
||||
BOOST_LOG_TRIVIAL(error) << "boost::filesystem::permisions before copy error message (this could be irrelevant message based on file system): " << ec.message();
|
||||
BOOST_LOG_TRIVIAL(debug) << "boost::filesystem::permisions before copy error message (this could be irrelevant message based on file system): " << ec.message();
|
||||
ec.clear();
|
||||
boost::filesystem::copy_file(source, target, boost::filesystem::copy_option::overwrite_if_exists, ec);
|
||||
if (ec) {
|
||||
|
|
@ -442,7 +442,7 @@ CopyFileResult copy_file_inner(const std::string& from, const std::string& to, s
|
|||
ec.clear();
|
||||
boost::filesystem::permissions(target, perms, ec);
|
||||
if (ec)
|
||||
BOOST_LOG_TRIVIAL(error) << "boost::filesystem::permisions after copy error message (this could be irrelevant message based on file system): " << ec.message();
|
||||
BOOST_LOG_TRIVIAL(debug) << "boost::filesystem::permisions after copy error message (this could be irrelevant message based on file system): " << ec.message();
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -162,6 +162,8 @@ set(SLIC3R_GUI_SOURCES
|
|||
GUI/Jobs/ArrangeJob.cpp
|
||||
GUI/Jobs/RotoptimizeJob.hpp
|
||||
GUI/Jobs/RotoptimizeJob.cpp
|
||||
GUI/Jobs/FillBedJob.hpp
|
||||
GUI/Jobs/FillBedJob.cpp
|
||||
GUI/Jobs/SLAImportJob.hpp
|
||||
GUI/Jobs/SLAImportJob.cpp
|
||||
GUI/Jobs/ProgressIndicator.hpp
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
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));
|
||||
for (auto pl : polylines)
|
||||
|
|
|
|||
|
|
@ -5,24 +5,18 @@
|
|||
#include "libslic3r/Polygon.hpp"
|
||||
#include "libslic3r/ClipperUtils.hpp"
|
||||
#include "libslic3r/BoundingBox.hpp"
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
#include "libslic3r/Geometry.hpp"
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
|
||||
#include "GUI_App.hpp"
|
||||
#include "libslic3r/PresetBundle.hpp"
|
||||
#include "GLCanvas3D.hpp"
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
#include "3DScene.hpp"
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
|
||||
#include <GL/glew.h>
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/filesystem/operations.hpp>
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
#include <boost/log/trivial.hpp>
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
|
||||
static const float GROUND_Z = -0.02f;
|
||||
|
||||
|
|
@ -121,43 +115,19 @@ const float* GeometryBuffer::get_vertices_data() const
|
|||
return (m_vertices.size() > 0) ? (const float*)m_vertices.data() : nullptr;
|
||||
}
|
||||
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
const float Bed3D::Axes::DefaultStemRadius = 0.5f;
|
||||
const float Bed3D::Axes::DefaultStemLength = 25.0f;
|
||||
const float Bed3D::Axes::DefaultTipRadius = 2.5f * Bed3D::Axes::DefaultStemRadius;
|
||||
const float Bed3D::Axes::DefaultTipLength = 5.0f;
|
||||
#else
|
||||
const double Bed3D::Axes::Radius = 0.5;
|
||||
const double Bed3D::Axes::ArrowBaseRadius = 2.5 * Bed3D::Axes::Radius;
|
||||
const double Bed3D::Axes::ArrowLength = 5.0;
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
void Bed3D::Axes::set_stem_length(float length)
|
||||
{
|
||||
m_stem_length = length;
|
||||
m_arrow.reset();
|
||||
}
|
||||
#else
|
||||
Bed3D::Axes::Axes()
|
||||
: origin(Vec3d::Zero())
|
||||
, length(25.0 * Vec3d::Ones())
|
||||
{
|
||||
m_quadric = ::gluNewQuadric();
|
||||
if (m_quadric != nullptr)
|
||||
::gluQuadricDrawStyle(m_quadric, GLU_FILL);
|
||||
}
|
||||
|
||||
Bed3D::Axes::~Axes()
|
||||
{
|
||||
if (m_quadric != nullptr)
|
||||
::gluDeleteQuadric(m_quadric);
|
||||
}
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
|
||||
void Bed3D::Axes::render() const
|
||||
{
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
auto render_axis = [this](const Transform3f& transform) {
|
||||
glsafe(::glPushMatrix());
|
||||
glsafe(::glMultMatrixf(transform.data()));
|
||||
|
|
@ -193,56 +163,8 @@ void Bed3D::Axes::render() const
|
|||
shader->stop_using();
|
||||
|
||||
glsafe(::glDisable(GL_DEPTH_TEST));
|
||||
#else
|
||||
if (m_quadric == nullptr)
|
||||
return;
|
||||
|
||||
glsafe(::glEnable(GL_DEPTH_TEST));
|
||||
glsafe(::glEnable(GL_LIGHTING));
|
||||
|
||||
// x axis
|
||||
glsafe(::glColor3fv(AXES_COLOR[0]));
|
||||
glsafe(::glPushMatrix());
|
||||
glsafe(::glTranslated(origin(0), origin(1), origin(2)));
|
||||
glsafe(::glRotated(90.0, 0.0, 1.0, 0.0));
|
||||
render_axis(length(0));
|
||||
glsafe(::glPopMatrix());
|
||||
|
||||
// y axis
|
||||
glsafe(::glColor3fv(AXES_COLOR[1]));
|
||||
glsafe(::glPushMatrix());
|
||||
glsafe(::glTranslated(origin(0), origin(1), origin(2)));
|
||||
glsafe(::glRotated(-90.0, 1.0, 0.0, 0.0));
|
||||
render_axis(length(1));
|
||||
glsafe(::glPopMatrix());
|
||||
|
||||
// z axis
|
||||
glsafe(::glColor3fv(AXES_COLOR[2]));
|
||||
glsafe(::glPushMatrix());
|
||||
glsafe(::glTranslated(origin(0), origin(1), origin(2)));
|
||||
render_axis(length(2));
|
||||
glsafe(::glPopMatrix());
|
||||
|
||||
glsafe(::glDisable(GL_LIGHTING));
|
||||
glsafe(::glDisable(GL_DEPTH_TEST));
|
||||
#endif // !ENABLE_GCODE_VIEWER
|
||||
}
|
||||
|
||||
#if !ENABLE_GCODE_VIEWER
|
||||
void Bed3D::Axes::render_axis(double length) const
|
||||
{
|
||||
::gluQuadricOrientation(m_quadric, GLU_OUTSIDE);
|
||||
::gluCylinder(m_quadric, Radius, Radius, length, 32, 1);
|
||||
::gluQuadricOrientation(m_quadric, GLU_INSIDE);
|
||||
::gluDisk(m_quadric, 0.0, Radius, 32, 1);
|
||||
glsafe(::glTranslated(0.0, 0.0, length));
|
||||
::gluQuadricOrientation(m_quadric, GLU_OUTSIDE);
|
||||
::gluCylinder(m_quadric, ArrowBaseRadius, 0.0, ArrowLength, 32, 1);
|
||||
::gluQuadricOrientation(m_quadric, GLU_INSIDE);
|
||||
::gluDisk(m_quadric, 0.0, ArrowBaseRadius, 32, 1);
|
||||
}
|
||||
#endif // !ENABLE_GCODE_VIEWER
|
||||
|
||||
Bed3D::Bed3D()
|
||||
: m_type(Custom)
|
||||
, m_vbo_id(0)
|
||||
|
|
@ -308,13 +230,8 @@ bool Bed3D::set_shape(const Pointfs& shape, const std::string& custom_texture, c
|
|||
m_model.reset();
|
||||
|
||||
// Set the origin and size for rendering the coordinate system axes.
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
m_axes.set_origin({ 0.0, 0.0, static_cast<double>(GROUND_Z) });
|
||||
m_axes.set_stem_length(0.1f * static_cast<float>(m_bounding_box.max_size()));
|
||||
#else
|
||||
m_axes.origin = Vec3d(0.0, 0.0, (double)GROUND_Z);
|
||||
m_axes.length = 0.1 * m_bounding_box.max_size() * Vec3d::Ones();
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
|
||||
// Let the calee to update the UI.
|
||||
return true;
|
||||
|
|
@ -360,7 +277,6 @@ void Bed3D::calc_bounding_boxes() const
|
|||
m_extended_bounding_box = m_bounding_box;
|
||||
|
||||
// extend to contain axes
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
m_extended_bounding_box.merge(m_axes.get_origin() + m_axes.get_total_length() * Vec3d::Ones());
|
||||
m_extended_bounding_box.merge(m_extended_bounding_box.min + Vec3d(-Axes::DefaultTipRadius, -Axes::DefaultTipRadius, m_extended_bounding_box.max(2)));
|
||||
|
||||
|
|
@ -370,12 +286,6 @@ void Bed3D::calc_bounding_boxes() const
|
|||
model_bb.translate(m_model_offset);
|
||||
m_extended_bounding_box.merge(model_bb);
|
||||
}
|
||||
#else
|
||||
m_extended_bounding_box.merge(m_axes.length + Axes::ArrowLength * Vec3d::Ones());
|
||||
// extend to contain model, if any
|
||||
if (!m_model.get_filename().empty())
|
||||
m_extended_bounding_box.merge(m_model.get_transformed_bounding_box());
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
}
|
||||
|
||||
void Bed3D::calc_triangles(const ExPolygon& poly)
|
||||
|
|
@ -414,25 +324,6 @@ void Bed3D::calc_gridlines(const ExPolygon& poly, const BoundingBox& bed_bbox)
|
|||
printf("Unable to create bed grid lines\n");
|
||||
}
|
||||
|
||||
#if !ENABLE_GCODE_VIEWER
|
||||
static std::string system_print_bed_model(const Preset &preset)
|
||||
{
|
||||
std::string out;
|
||||
const VendorProfile::PrinterModel *pm = PresetUtils::system_printer_model(preset);
|
||||
if (pm != nullptr && ! pm->bed_model.empty())
|
||||
out = Slic3r::resources_dir() + "/profiles/" + preset.vendor->id + "/" + pm->bed_model;
|
||||
return out;
|
||||
}
|
||||
|
||||
static std::string system_print_bed_texture(const Preset &preset)
|
||||
{
|
||||
std::string out;
|
||||
const VendorProfile::PrinterModel *pm = PresetUtils::system_printer_model(preset);
|
||||
if (pm != nullptr && ! pm->bed_texture.empty())
|
||||
out = Slic3r::resources_dir() + "/profiles/" + preset.vendor->id + "/" + pm->bed_texture;
|
||||
return out;
|
||||
}
|
||||
#endif // !ENABLE_GCODE_VIEWER
|
||||
|
||||
std::tuple<Bed3D::EType, std::string, std::string> Bed3D::detect_type(const Pointfs& shape) const
|
||||
{
|
||||
|
|
@ -442,13 +333,8 @@ std::tuple<Bed3D::EType, std::string, std::string> Bed3D::detect_type(const Poin
|
|||
while (curr != nullptr) {
|
||||
if (curr->config.has("bed_shape")) {
|
||||
if (shape == dynamic_cast<const ConfigOptionPoints*>(curr->config.option("bed_shape"))->values) {
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
std::string model_filename = PresetUtils::system_printer_bed_model(*curr);
|
||||
std::string texture_filename = PresetUtils::system_printer_bed_texture(*curr);
|
||||
#else
|
||||
std::string model_filename = system_print_bed_model(*curr);
|
||||
std::string texture_filename = system_print_bed_texture(*curr);
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
if (!model_filename.empty() && !texture_filename.empty())
|
||||
return { System, model_filename, texture_filename };
|
||||
}
|
||||
|
|
@ -614,11 +500,7 @@ void Bed3D::render_model() const
|
|||
// move the model so that its origin (0.0, 0.0, 0.0) goes into the bed shape center and a bit down to avoid z-fighting with the texture quad
|
||||
Vec3d shift = m_bounding_box.center();
|
||||
shift(2) = -0.03;
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
m_model_offset = shift;
|
||||
#else
|
||||
m_model.set_offset(shift);
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
|
||||
// update extended bounding box
|
||||
calc_bounding_boxes();
|
||||
|
|
@ -628,15 +510,11 @@ void Bed3D::render_model() const
|
|||
GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light");
|
||||
if (shader != nullptr) {
|
||||
shader->start_using();
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
shader->set_uniform("uniform_color", m_model_color);
|
||||
::glPushMatrix();
|
||||
::glTranslated(m_model_offset(0), m_model_offset(1), m_model_offset(2));
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
m_model.render();
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
::glPopMatrix();
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
shader->stop_using();
|
||||
}
|
||||
}
|
||||
|
|
@ -673,11 +551,7 @@ void Bed3D::render_default(bool bottom) const
|
|||
if (!has_model && !bottom) {
|
||||
// draw background
|
||||
glsafe(::glDepthMask(GL_FALSE));
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
glsafe(::glColor4fv(m_model_color.data()));
|
||||
#else
|
||||
glsafe(::glColor4f(0.35f, 0.35f, 0.35f, 0.4f));
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
glsafe(::glNormal3d(0.0f, 0.0f, 1.0f));
|
||||
glsafe(::glVertexPointer(3, GL_FLOAT, m_triangles.get_vertex_data_size(), (GLvoid*)m_triangles.get_vertices_data()));
|
||||
glsafe(::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)triangles_vcount));
|
||||
|
|
|
|||
|
|
@ -3,19 +3,10 @@
|
|||
|
||||
#include "GLTexture.hpp"
|
||||
#include "3DScene.hpp"
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
#include "GLModel.hpp"
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
|
||||
#include <tuple>
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
#include <array>
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
|
||||
#if !ENABLE_GCODE_VIEWER
|
||||
class GLUquadric;
|
||||
typedef class GLUquadric GLUquadricObj;
|
||||
#endif // !ENABLE_GCODE_VIEWER
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
|
@ -52,7 +43,6 @@ public:
|
|||
|
||||
class Bed3D
|
||||
{
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
class Axes
|
||||
{
|
||||
public:
|
||||
|
|
@ -62,43 +52,16 @@ class Bed3D
|
|||
static const float DefaultTipLength;
|
||||
|
||||
private:
|
||||
#else
|
||||
struct Axes
|
||||
{
|
||||
static const double Radius;
|
||||
static const double ArrowBaseRadius;
|
||||
static const double ArrowLength;
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
Vec3d m_origin{ Vec3d::Zero() };
|
||||
float m_stem_length{ DefaultStemLength };
|
||||
mutable GLModel m_arrow;
|
||||
|
||||
public:
|
||||
#else
|
||||
Vec3d origin;
|
||||
Vec3d length;
|
||||
GLUquadricObj* m_quadric;
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
|
||||
#if !ENABLE_GCODE_VIEWER
|
||||
Axes();
|
||||
~Axes();
|
||||
#endif // !ENABLE_GCODE_VIEWER
|
||||
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
const Vec3d& get_origin() const { return m_origin; }
|
||||
void set_origin(const Vec3d& origin) { m_origin = origin; }
|
||||
void set_stem_length(float length);
|
||||
float get_total_length() const { return m_stem_length + DefaultTipLength; }
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
void render() const;
|
||||
|
||||
#if !ENABLE_GCODE_VIEWER
|
||||
private:
|
||||
void render_axis(double length) const;
|
||||
#endif // !ENABLE_GCODE_VIEWER
|
||||
};
|
||||
|
||||
public:
|
||||
|
|
@ -120,13 +83,9 @@ private:
|
|||
GeometryBuffer m_triangles;
|
||||
GeometryBuffer m_gridlines;
|
||||
mutable GLTexture m_texture;
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
mutable GLModel m_model;
|
||||
mutable Vec3d m_model_offset{ Vec3d::Zero() };
|
||||
std::array<float, 4> m_model_color{ 0.235f, 0.235f, 0.235f, 1.0f };
|
||||
#else
|
||||
mutable GLBed m_model;
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
// temporary texture shown until the main texture has still no levels compressed
|
||||
mutable GLTexture m_temp_texture;
|
||||
mutable unsigned int m_vbo_id;
|
||||
|
|
|
|||
|
|
@ -993,290 +993,6 @@ bool GLVolumeCollection::has_toolpaths_to_export() const
|
|||
return false;
|
||||
}
|
||||
|
||||
#if !ENABLE_GCODE_VIEWER
|
||||
void GLVolumeCollection::export_toolpaths_to_obj(const char* filename) const
|
||||
{
|
||||
if (filename == nullptr)
|
||||
return;
|
||||
|
||||
if (!has_toolpaths_to_export())
|
||||
return;
|
||||
|
||||
// collect color information to generate materials
|
||||
typedef std::array<float, 4> Color;
|
||||
std::set<Color> colors;
|
||||
for (const GLVolume* volume : this->volumes)
|
||||
{
|
||||
if (!can_export_to_obj(*volume))
|
||||
continue;
|
||||
|
||||
Color color;
|
||||
::memcpy((void*)color.data(), (const void*)volume->color, 4 * sizeof(float));
|
||||
colors.insert(color);
|
||||
}
|
||||
|
||||
// save materials file
|
||||
boost::filesystem::path mat_filename(filename);
|
||||
mat_filename.replace_extension("mtl");
|
||||
FILE* fp = boost::nowide::fopen(mat_filename.string().c_str(), "w");
|
||||
if (fp == nullptr) {
|
||||
BOOST_LOG_TRIVIAL(error) << "GLVolumeCollection::export_toolpaths_to_obj: Couldn't open " << mat_filename.string().c_str() << " for writing";
|
||||
return;
|
||||
}
|
||||
|
||||
fprintf(fp, "# G-Code Toolpaths Materials\n");
|
||||
fprintf(fp, "# Generated by %s based on Slic3r\n", SLIC3R_BUILD_ID);
|
||||
|
||||
unsigned int colors_count = 1;
|
||||
for (const Color& color : colors)
|
||||
{
|
||||
fprintf(fp, "\nnewmtl material_%d\n", colors_count++);
|
||||
fprintf(fp, "Ka 1 1 1\n");
|
||||
fprintf(fp, "Kd %f %f %f\n", color[0], color[1], color[2]);
|
||||
fprintf(fp, "Ks 0 0 0\n");
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
// save geometry file
|
||||
fp = boost::nowide::fopen(filename, "w");
|
||||
if (fp == nullptr) {
|
||||
BOOST_LOG_TRIVIAL(error) << "GLVolumeCollection::export_toolpaths_to_obj: Couldn't open " << filename << " for writing";
|
||||
return;
|
||||
}
|
||||
|
||||
fprintf(fp, "# G-Code Toolpaths\n");
|
||||
fprintf(fp, "# Generated by %s based on Slic3r\n", SLIC3R_BUILD_ID);
|
||||
fprintf(fp, "\nmtllib ./%s\n", mat_filename.filename().string().c_str());
|
||||
|
||||
unsigned int vertices_count = 0;
|
||||
unsigned int normals_count = 0;
|
||||
unsigned int volumes_count = 0;
|
||||
|
||||
for (const GLVolume* volume : this->volumes)
|
||||
{
|
||||
if (!can_export_to_obj(*volume))
|
||||
continue;
|
||||
|
||||
std::vector<float> src_vertices_and_normals_interleaved;
|
||||
std::vector<int> src_triangle_indices;
|
||||
std::vector<int> src_quad_indices;
|
||||
|
||||
if (!volume->indexed_vertex_array.vertices_and_normals_interleaved.empty())
|
||||
// data are in CPU memory
|
||||
src_vertices_and_normals_interleaved = volume->indexed_vertex_array.vertices_and_normals_interleaved;
|
||||
else if ((volume->indexed_vertex_array.vertices_and_normals_interleaved_VBO_id != 0) && (volume->indexed_vertex_array.vertices_and_normals_interleaved_size != 0))
|
||||
{
|
||||
// data are in GPU memory
|
||||
src_vertices_and_normals_interleaved = std::vector<float>(volume->indexed_vertex_array.vertices_and_normals_interleaved_size, 0.0f);
|
||||
|
||||
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, volume->indexed_vertex_array.vertices_and_normals_interleaved_VBO_id));
|
||||
glsafe(::glGetBufferSubData(GL_ARRAY_BUFFER, 0, src_vertices_and_normals_interleaved.size() * sizeof(float), src_vertices_and_normals_interleaved.data()));
|
||||
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
|
||||
}
|
||||
else
|
||||
continue;
|
||||
|
||||
if (!volume->indexed_vertex_array.triangle_indices.empty())
|
||||
{
|
||||
// data are in CPU memory
|
||||
size_t size = std::min(volume->indexed_vertex_array.triangle_indices.size(), volume->tverts_range.second - volume->tverts_range.first);
|
||||
if (size != 0)
|
||||
{
|
||||
std::vector<int>::const_iterator it_begin = volume->indexed_vertex_array.triangle_indices.begin() + volume->tverts_range.first;
|
||||
std::vector<int>::const_iterator it_end = volume->indexed_vertex_array.triangle_indices.begin() + volume->tverts_range.first + size;
|
||||
std::copy(it_begin, it_end, std::back_inserter(src_triangle_indices));
|
||||
}
|
||||
}
|
||||
else if ((volume->indexed_vertex_array.triangle_indices_VBO_id != 0) && (volume->indexed_vertex_array.triangle_indices_size != 0))
|
||||
{
|
||||
// data are in GPU memory
|
||||
size_t size = std::min(volume->indexed_vertex_array.triangle_indices_size, volume->tverts_range.second - volume->tverts_range.first);
|
||||
if (size != 0)
|
||||
{
|
||||
src_triangle_indices = std::vector<int>(size, 0);
|
||||
|
||||
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, volume->indexed_vertex_array.triangle_indices_VBO_id));
|
||||
glsafe(::glGetBufferSubData(GL_ELEMENT_ARRAY_BUFFER, volume->tverts_range.first * sizeof(int), size * sizeof(int), src_triangle_indices.data()));
|
||||
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
|
||||
}
|
||||
}
|
||||
|
||||
if (!volume->indexed_vertex_array.quad_indices.empty())
|
||||
{
|
||||
// data are in CPU memory
|
||||
size_t size = std::min(volume->indexed_vertex_array.quad_indices.size(), volume->qverts_range.second - volume->qverts_range.first);
|
||||
if (size != 0)
|
||||
{
|
||||
std::vector<int>::const_iterator it_begin = volume->indexed_vertex_array.quad_indices.begin() + volume->qverts_range.first;
|
||||
std::vector<int>::const_iterator it_end = volume->indexed_vertex_array.quad_indices.begin() + volume->qverts_range.first + size;
|
||||
std::copy(it_begin, it_end, std::back_inserter(src_quad_indices));
|
||||
}
|
||||
}
|
||||
else if ((volume->indexed_vertex_array.quad_indices_VBO_id != 0) && (volume->indexed_vertex_array.quad_indices_size != 0))
|
||||
{
|
||||
// data are in GPU memory
|
||||
size_t size = std::min(volume->indexed_vertex_array.quad_indices_size, volume->qverts_range.second - volume->qverts_range.first);
|
||||
if (size != 0)
|
||||
{
|
||||
src_quad_indices = std::vector<int>(size, 0);
|
||||
|
||||
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, volume->indexed_vertex_array.quad_indices_VBO_id));
|
||||
glsafe(::glGetBufferSubData(GL_ELEMENT_ARRAY_BUFFER, volume->qverts_range.first * sizeof(int), size * sizeof(int), src_quad_indices.data()));
|
||||
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
|
||||
}
|
||||
}
|
||||
|
||||
if (src_triangle_indices.empty() && src_quad_indices.empty())
|
||||
continue;
|
||||
|
||||
++volumes_count;
|
||||
|
||||
// reduce output size by keeping only used vertices and normals
|
||||
|
||||
struct Vector
|
||||
{
|
||||
std::array<coord_t, 3> vector;
|
||||
|
||||
explicit Vector(float* ptr)
|
||||
{
|
||||
vector[0] = scale_(*(ptr + 0));
|
||||
vector[1] = scale_(*(ptr + 1));
|
||||
vector[2] = scale_(*(ptr + 2));
|
||||
}
|
||||
};
|
||||
typedef std::vector<Vector> Vectors;
|
||||
|
||||
auto vector_less = [](const Vector& v1, const Vector& v2)->bool {
|
||||
return v1.vector < v2.vector;
|
||||
};
|
||||
|
||||
auto vector_equal = [](const Vector& v1, const Vector& v2)->bool {
|
||||
return (v1.vector[0] == v2.vector[0]) && (v1.vector[1] == v2.vector[1]) && (v1.vector[2] == v2.vector[2]);
|
||||
};
|
||||
|
||||
// copy used vertices and normals data
|
||||
Vectors dst_normals;
|
||||
Vectors dst_vertices;
|
||||
|
||||
unsigned int src_triangle_indices_size = (unsigned int)src_triangle_indices.size();
|
||||
for (unsigned int i = 0; i < src_triangle_indices_size; ++i)
|
||||
{
|
||||
float* src_ptr = src_vertices_and_normals_interleaved.data() + src_triangle_indices[i] * 6;
|
||||
dst_normals.emplace_back(src_ptr + 0);
|
||||
dst_vertices.emplace_back(src_ptr + 3);
|
||||
}
|
||||
|
||||
unsigned int src_quad_indices_size = (unsigned int)src_quad_indices.size();
|
||||
for (unsigned int i = 0; i < src_quad_indices_size; ++i)
|
||||
{
|
||||
float* src_ptr = src_vertices_and_normals_interleaved.data() + src_quad_indices[i] * 6;
|
||||
dst_normals.emplace_back(src_ptr + 0);
|
||||
dst_vertices.emplace_back(src_ptr + 3);
|
||||
}
|
||||
|
||||
// sort vertices and normals
|
||||
std::sort(dst_normals.begin(), dst_normals.end(), vector_less);
|
||||
std::sort(dst_vertices.begin(), dst_vertices.end(), vector_less);
|
||||
|
||||
// remove duplicated vertices and normals
|
||||
dst_normals.erase(std::unique(dst_normals.begin(), dst_normals.end(), vector_equal), dst_normals.end());
|
||||
dst_vertices.erase(std::unique(dst_vertices.begin(), dst_vertices.end(), vector_equal), dst_vertices.end());
|
||||
|
||||
// reindex triangles and quads
|
||||
struct IndicesPair
|
||||
{
|
||||
int vertex;
|
||||
int normal;
|
||||
IndicesPair(int vertex, int normal) : vertex(vertex), normal(normal) {}
|
||||
};
|
||||
typedef std::vector<IndicesPair> Indices;
|
||||
|
||||
unsigned int src_vertices_count = (unsigned int)src_vertices_and_normals_interleaved.size() / 6;
|
||||
std::vector<int> src_dst_vertex_indices_map(src_vertices_count, -1);
|
||||
std::vector<int> src_dst_normal_indices_map(src_vertices_count, -1);
|
||||
|
||||
for (unsigned int i = 0; i < src_vertices_count; ++i)
|
||||
{
|
||||
float* src_ptr = src_vertices_and_normals_interleaved.data() + i * 6;
|
||||
src_dst_normal_indices_map[i] = std::distance(dst_normals.begin(), std::lower_bound(dst_normals.begin(), dst_normals.end(), Vector(src_ptr + 0), vector_less));
|
||||
src_dst_vertex_indices_map[i] = std::distance(dst_vertices.begin(), std::lower_bound(dst_vertices.begin(), dst_vertices.end(), Vector(src_ptr + 3), vector_less));
|
||||
}
|
||||
|
||||
Indices dst_triangle_indices;
|
||||
if (src_triangle_indices_size > 0)
|
||||
dst_triangle_indices.reserve(src_triangle_indices_size);
|
||||
|
||||
for (unsigned int i = 0; i < src_triangle_indices_size; ++i)
|
||||
{
|
||||
int id = src_triangle_indices[i];
|
||||
dst_triangle_indices.emplace_back(src_dst_vertex_indices_map[id], src_dst_normal_indices_map[id]);
|
||||
}
|
||||
|
||||
Indices dst_quad_indices;
|
||||
if (src_quad_indices_size > 0)
|
||||
dst_quad_indices.reserve(src_quad_indices_size);
|
||||
|
||||
for (unsigned int i = 0; i < src_quad_indices_size; ++i)
|
||||
{
|
||||
int id = src_quad_indices[i];
|
||||
dst_quad_indices.emplace_back(src_dst_vertex_indices_map[id], src_dst_normal_indices_map[id]);
|
||||
}
|
||||
|
||||
// save to file
|
||||
fprintf(fp, "\n# vertices volume %d\n", volumes_count);
|
||||
for (const Vector& v : dst_vertices)
|
||||
{
|
||||
fprintf(fp, "v %g %g %g\n", unscale<float>(v.vector[0]), unscale<float>(v.vector[1]), unscale<float>(v.vector[2]));
|
||||
}
|
||||
|
||||
fprintf(fp, "\n# normals volume %d\n", volumes_count);
|
||||
for (const Vector& n : dst_normals)
|
||||
{
|
||||
fprintf(fp, "vn %g %g %g\n", unscale<float>(n.vector[0]), unscale<float>(n.vector[1]), unscale<float>(n.vector[2]));
|
||||
}
|
||||
|
||||
Color color;
|
||||
::memcpy((void*)color.data(), (const void*)volume->color, 4 * sizeof(float));
|
||||
fprintf(fp, "\n# material volume %d\n", volumes_count);
|
||||
fprintf(fp, "usemtl material_%lld\n", (long long)(1 + std::distance(colors.begin(), colors.find(color))));
|
||||
|
||||
int base_vertex_id = vertices_count + 1;
|
||||
int base_normal_id = normals_count + 1;
|
||||
|
||||
if (!dst_triangle_indices.empty())
|
||||
{
|
||||
fprintf(fp, "\n# triangular facets volume %d\n", volumes_count);
|
||||
for (unsigned int i = 0; i < (unsigned int)dst_triangle_indices.size(); i += 3)
|
||||
{
|
||||
fprintf(fp, "f %d//%d %d//%d %d//%d\n",
|
||||
base_vertex_id + dst_triangle_indices[i + 0].vertex, base_normal_id + dst_triangle_indices[i + 0].normal,
|
||||
base_vertex_id + dst_triangle_indices[i + 1].vertex, base_normal_id + dst_triangle_indices[i + 1].normal,
|
||||
base_vertex_id + dst_triangle_indices[i + 2].vertex, base_normal_id + dst_triangle_indices[i + 2].normal);
|
||||
}
|
||||
}
|
||||
|
||||
if (!dst_quad_indices.empty())
|
||||
{
|
||||
fprintf(fp, "\n# quadrangular facets volume %d\n", volumes_count);
|
||||
for (unsigned int i = 0; i < (unsigned int)src_quad_indices.size(); i += 4)
|
||||
{
|
||||
fprintf(fp, "f %d//%d %d//%d %d//%d %d//%d\n",
|
||||
base_vertex_id + dst_quad_indices[i + 0].vertex, base_normal_id + dst_quad_indices[i + 0].normal,
|
||||
base_vertex_id + dst_quad_indices[i + 1].vertex, base_normal_id + dst_quad_indices[i + 1].normal,
|
||||
base_vertex_id + dst_quad_indices[i + 2].vertex, base_normal_id + dst_quad_indices[i + 2].normal,
|
||||
base_vertex_id + dst_quad_indices[i + 3].vertex, base_normal_id + dst_quad_indices[i + 3].normal);
|
||||
}
|
||||
}
|
||||
|
||||
vertices_count += (unsigned int)dst_vertices.size();
|
||||
normals_count += (unsigned int)dst_normals.size();
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
}
|
||||
#endif // !ENABLE_GCODE_VIEWER
|
||||
|
||||
// caller is responsible for supplying NO lines with zero length
|
||||
static void thick_lines_to_indexed_vertex_array(
|
||||
const Lines &lines,
|
||||
|
|
@ -1923,287 +1639,4 @@ void _3DScene::point3_to_verts(const Vec3crd& point, double width, double height
|
|||
thick_point_to_verts(point, width, height, volume);
|
||||
}
|
||||
|
||||
#if !ENABLE_GCODE_VIEWER
|
||||
GLModel::GLModel()
|
||||
: m_filename("")
|
||||
{
|
||||
m_volume.shader_outside_printer_detection_enabled = false;
|
||||
}
|
||||
|
||||
GLModel::~GLModel()
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
void GLModel::set_color(const float* color, unsigned int size)
|
||||
{
|
||||
::memcpy((void*)m_volume.color, (const void*)color, (size_t)(std::min((unsigned int)4, size) * sizeof(float)));
|
||||
m_volume.set_render_color(color, size);
|
||||
}
|
||||
|
||||
const Vec3d& GLModel::get_offset() const
|
||||
{
|
||||
return m_volume.get_volume_offset();
|
||||
}
|
||||
|
||||
void GLModel::set_offset(const Vec3d& offset)
|
||||
{
|
||||
m_volume.set_volume_offset(offset);
|
||||
}
|
||||
|
||||
const Vec3d& GLModel::get_rotation() const
|
||||
{
|
||||
return m_volume.get_volume_rotation();
|
||||
}
|
||||
|
||||
void GLModel::set_rotation(const Vec3d& rotation)
|
||||
{
|
||||
m_volume.set_volume_rotation(rotation);
|
||||
}
|
||||
|
||||
const Vec3d& GLModel::get_scale() const
|
||||
{
|
||||
return m_volume.get_volume_scaling_factor();
|
||||
}
|
||||
|
||||
void GLModel::set_scale(const Vec3d& scale)
|
||||
{
|
||||
m_volume.set_volume_scaling_factor(scale);
|
||||
}
|
||||
|
||||
void GLModel::reset()
|
||||
{
|
||||
m_volume.indexed_vertex_array.release_geometry();
|
||||
m_filename = "";
|
||||
}
|
||||
|
||||
void GLModel::render() const
|
||||
{
|
||||
GLShaderProgram* shader = GUI::wxGetApp().get_current_shader();
|
||||
if (shader == nullptr)
|
||||
return;
|
||||
|
||||
glsafe(::glEnable(GL_BLEND));
|
||||
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
|
||||
|
||||
glsafe(::glCullFace(GL_BACK));
|
||||
glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
|
||||
glsafe(::glEnableClientState(GL_NORMAL_ARRAY));
|
||||
|
||||
shader->set_uniform("uniform_color", m_volume.render_color, 4);
|
||||
m_volume.render();
|
||||
|
||||
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
|
||||
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
|
||||
|
||||
glsafe(::glDisableClientState(GL_VERTEX_ARRAY));
|
||||
glsafe(::glDisableClientState(GL_NORMAL_ARRAY));
|
||||
|
||||
glsafe(::glDisable(GL_BLEND));
|
||||
}
|
||||
|
||||
bool GLArrow::on_init()
|
||||
{
|
||||
Pointf3s vertices;
|
||||
std::vector<Vec3i> triangles;
|
||||
|
||||
// bottom face
|
||||
vertices.emplace_back(0.5, 0.0, -0.1);
|
||||
vertices.emplace_back(0.5, 2.0, -0.1);
|
||||
vertices.emplace_back(1.0, 2.0, -0.1);
|
||||
vertices.emplace_back(0.0, 3.0, -0.1);
|
||||
vertices.emplace_back(-1.0, 2.0, -0.1);
|
||||
vertices.emplace_back(-0.5, 2.0, -0.1);
|
||||
vertices.emplace_back(-0.5, 0.0, -0.1);
|
||||
|
||||
// top face
|
||||
vertices.emplace_back(0.5, 0.0, 0.1);
|
||||
vertices.emplace_back(0.5, 2.0, 0.1);
|
||||
vertices.emplace_back(1.0, 2.0, 0.1);
|
||||
vertices.emplace_back(0.0, 3.0, 0.1);
|
||||
vertices.emplace_back(-1.0, 2.0, 0.1);
|
||||
vertices.emplace_back(-0.5, 2.0, 0.1);
|
||||
vertices.emplace_back(-0.5, 0.0, 0.1);
|
||||
|
||||
// bottom face
|
||||
triangles.emplace_back(0, 6, 1);
|
||||
triangles.emplace_back(6, 5, 1);
|
||||
triangles.emplace_back(5, 4, 3);
|
||||
triangles.emplace_back(5, 3, 1);
|
||||
triangles.emplace_back(1, 3, 2);
|
||||
|
||||
// top face
|
||||
triangles.emplace_back(7, 8, 13);
|
||||
triangles.emplace_back(13, 8, 12);
|
||||
triangles.emplace_back(12, 10, 11);
|
||||
triangles.emplace_back(8, 10, 12);
|
||||
triangles.emplace_back(8, 9, 10);
|
||||
|
||||
// side face
|
||||
triangles.emplace_back(0, 1, 8);
|
||||
triangles.emplace_back(8, 7, 0);
|
||||
triangles.emplace_back(1, 2, 9);
|
||||
triangles.emplace_back(9, 8, 1);
|
||||
triangles.emplace_back(2, 3, 10);
|
||||
triangles.emplace_back(10, 9, 2);
|
||||
triangles.emplace_back(3, 4, 11);
|
||||
triangles.emplace_back(11, 10, 3);
|
||||
triangles.emplace_back(4, 5, 12);
|
||||
triangles.emplace_back(12, 11, 4);
|
||||
triangles.emplace_back(5, 6, 13);
|
||||
triangles.emplace_back(13, 12, 5);
|
||||
triangles.emplace_back(6, 0, 7);
|
||||
triangles.emplace_back(7, 13, 6);
|
||||
|
||||
m_volume.indexed_vertex_array.load_mesh(TriangleMesh(vertices, triangles));
|
||||
m_volume.indexed_vertex_array.finalize_geometry(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
GLCurvedArrow::GLCurvedArrow(unsigned int resolution)
|
||||
: GLModel()
|
||||
, m_resolution(resolution)
|
||||
{
|
||||
if (m_resolution == 0)
|
||||
m_resolution = 1;
|
||||
}
|
||||
|
||||
bool GLCurvedArrow::on_init()
|
||||
{
|
||||
Pointf3s vertices;
|
||||
std::vector<Vec3i> triangles;
|
||||
|
||||
double ext_radius = 2.5;
|
||||
double int_radius = 1.5;
|
||||
double step = 0.5 * (double)PI / (double)m_resolution;
|
||||
|
||||
unsigned int vertices_per_level = 4 + 2 * m_resolution;
|
||||
|
||||
// bottom face
|
||||
vertices.emplace_back(0.0, 1.5, -0.1);
|
||||
vertices.emplace_back(0.0, 1.0, -0.1);
|
||||
vertices.emplace_back(-1.0, 2.0, -0.1);
|
||||
vertices.emplace_back(0.0, 3.0, -0.1);
|
||||
vertices.emplace_back(0.0, 2.5, -0.1);
|
||||
|
||||
for (unsigned int i = 1; i <= m_resolution; ++i)
|
||||
{
|
||||
double angle = (double)i * step;
|
||||
double x = ext_radius * ::sin(angle);
|
||||
double y = ext_radius * ::cos(angle);
|
||||
|
||||
vertices.emplace_back(x, y, -0.1);
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < m_resolution; ++i)
|
||||
{
|
||||
double angle = (double)i * step;
|
||||
double x = int_radius * ::cos(angle);
|
||||
double y = int_radius * ::sin(angle);
|
||||
|
||||
vertices.emplace_back(x, y, -0.1);
|
||||
}
|
||||
|
||||
// top face
|
||||
vertices.emplace_back(0.0, 1.5, 0.1);
|
||||
vertices.emplace_back(0.0, 1.0, 0.1);
|
||||
vertices.emplace_back(-1.0, 2.0, 0.1);
|
||||
vertices.emplace_back(0.0, 3.0, 0.1);
|
||||
vertices.emplace_back(0.0, 2.5, 0.1);
|
||||
|
||||
for (unsigned int i = 1; i <= m_resolution; ++i)
|
||||
{
|
||||
double angle = (double)i * step;
|
||||
double x = ext_radius * ::sin(angle);
|
||||
double y = ext_radius * ::cos(angle);
|
||||
|
||||
vertices.emplace_back(x, y, 0.1);
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < m_resolution; ++i)
|
||||
{
|
||||
double angle = (double)i * step;
|
||||
double x = int_radius * ::cos(angle);
|
||||
double y = int_radius * ::sin(angle);
|
||||
|
||||
vertices.emplace_back(x, y, 0.1);
|
||||
}
|
||||
|
||||
// bottom face
|
||||
triangles.emplace_back(0, 1, 2);
|
||||
triangles.emplace_back(0, 2, 4);
|
||||
triangles.emplace_back(4, 2, 3);
|
||||
|
||||
int first_id = 4;
|
||||
int last_id = (int)vertices_per_level;
|
||||
triangles.emplace_back(last_id, 0, first_id);
|
||||
triangles.emplace_back(last_id, first_id, first_id + 1);
|
||||
for (unsigned int i = 1; i < m_resolution; ++i)
|
||||
{
|
||||
triangles.emplace_back(last_id - i, last_id - i + 1, first_id + i);
|
||||
triangles.emplace_back(last_id - i, first_id + i, first_id + i + 1);
|
||||
}
|
||||
|
||||
// top face
|
||||
last_id += 1;
|
||||
triangles.emplace_back(last_id + 0, last_id + 2, last_id + 1);
|
||||
triangles.emplace_back(last_id + 0, last_id + 4, last_id + 2);
|
||||
triangles.emplace_back(last_id + 4, last_id + 3, last_id + 2);
|
||||
|
||||
first_id = last_id + 4;
|
||||
last_id = last_id + 4 + 2 * (int)m_resolution;
|
||||
triangles.emplace_back(last_id, first_id, (int)vertices_per_level + 1);
|
||||
triangles.emplace_back(last_id, first_id + 1, first_id);
|
||||
for (unsigned int i = 1; i < m_resolution; ++i)
|
||||
{
|
||||
triangles.emplace_back(last_id - i, first_id + i, last_id - i + 1);
|
||||
triangles.emplace_back(last_id - i, first_id + i + 1, first_id + i);
|
||||
}
|
||||
|
||||
// side face
|
||||
for (unsigned int i = 0; i < 4 + 2 * (unsigned int)m_resolution; ++i)
|
||||
{
|
||||
triangles.emplace_back(i, vertices_per_level + 2 + i, i + 1);
|
||||
triangles.emplace_back(i, vertices_per_level + 1 + i, vertices_per_level + 2 + i);
|
||||
}
|
||||
triangles.emplace_back(vertices_per_level, vertices_per_level + 1, 0);
|
||||
triangles.emplace_back(vertices_per_level, 2 * vertices_per_level + 1, vertices_per_level + 1);
|
||||
|
||||
m_volume.indexed_vertex_array.load_mesh(TriangleMesh(vertices, triangles));
|
||||
m_volume.indexed_vertex_array.finalize_geometry(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GLBed::on_init_from_file(const std::string& filename)
|
||||
{
|
||||
reset();
|
||||
|
||||
if (!boost::filesystem::exists(filename))
|
||||
return false;
|
||||
|
||||
if (!boost::algorithm::iends_with(filename, ".stl"))
|
||||
return false;
|
||||
|
||||
Model model;
|
||||
try
|
||||
{
|
||||
model = Model::read_from_file(filename);
|
||||
}
|
||||
catch (std::exception & /* ex */)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_filename = filename;
|
||||
|
||||
m_volume.indexed_vertex_array.load_mesh(model.mesh());
|
||||
m_volume.indexed_vertex_array.finalize_geometry(true);
|
||||
|
||||
float color[4] = { 0.235f, 0.235f, 0.235f, 1.0f };
|
||||
set_color(color, 4);
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif // !ENABLE_GCODE_VIEWER
|
||||
|
||||
} // namespace Slic3r
|
||||
|
|
|
|||
|
|
@ -588,10 +588,6 @@ public:
|
|||
std::string log_memory_info() const;
|
||||
|
||||
bool has_toolpaths_to_export() const;
|
||||
#if !ENABLE_GCODE_VIEWER
|
||||
// Export the geometry of the GLVolumes toolpaths of this collection into the file with the given path, in obj format
|
||||
void export_toolpaths_to_obj(const char* filename) const;
|
||||
#endif // !ENABLE_GCODE_VIEWER
|
||||
|
||||
private:
|
||||
GLVolumeCollection(const GLVolumeCollection &other);
|
||||
|
|
@ -600,68 +596,6 @@ private:
|
|||
|
||||
GLVolumeWithIdAndZList volumes_to_render(const GLVolumePtrs& volumes, GLVolumeCollection::ERenderType type, const Transform3d& view_matrix, std::function<bool(const GLVolume&)> filter_func = nullptr);
|
||||
|
||||
#if !ENABLE_GCODE_VIEWER
|
||||
class GLModel
|
||||
{
|
||||
protected:
|
||||
GLVolume m_volume;
|
||||
std::string m_filename;
|
||||
|
||||
public:
|
||||
GLModel();
|
||||
virtual ~GLModel();
|
||||
|
||||
// init() / init_from_file() shall be called with the OpenGL context active!
|
||||
bool init() { return on_init(); }
|
||||
bool init_from_file(const std::string& filename) { return on_init_from_file(filename); }
|
||||
|
||||
void center_around(const Vec3d& center) { m_volume.set_volume_offset(center - m_volume.bounding_box().center()); }
|
||||
void set_color(const float* color, unsigned int size);
|
||||
|
||||
const Vec3d& get_offset() const;
|
||||
void set_offset(const Vec3d& offset);
|
||||
const Vec3d& get_rotation() const;
|
||||
void set_rotation(const Vec3d& rotation);
|
||||
const Vec3d& get_scale() const;
|
||||
void set_scale(const Vec3d& scale);
|
||||
|
||||
const std::string& get_filename() const { return m_filename; }
|
||||
const BoundingBoxf3& get_bounding_box() const { return m_volume.bounding_box(); }
|
||||
const BoundingBoxf3& get_transformed_bounding_box() const { return m_volume.transformed_bounding_box(); }
|
||||
|
||||
void reset();
|
||||
|
||||
void render() const;
|
||||
|
||||
protected:
|
||||
virtual bool on_init() { return false; }
|
||||
virtual bool on_init_from_file(const std::string& filename) { return false; }
|
||||
};
|
||||
|
||||
class GLArrow : public GLModel
|
||||
{
|
||||
protected:
|
||||
bool on_init() override;
|
||||
};
|
||||
|
||||
class GLCurvedArrow : public GLModel
|
||||
{
|
||||
unsigned int m_resolution;
|
||||
|
||||
public:
|
||||
explicit GLCurvedArrow(unsigned int resolution);
|
||||
|
||||
protected:
|
||||
bool on_init() override;
|
||||
};
|
||||
|
||||
class GLBed : public GLModel
|
||||
{
|
||||
protected:
|
||||
bool on_init_from_file(const std::string& filename) override;
|
||||
};
|
||||
#endif // !ENABLE_GCODE_VIEWER
|
||||
|
||||
struct _3DScene
|
||||
{
|
||||
static void thick_lines_to_verts(const Lines& lines, const std::vector<double>& widths, const std::vector<double>& heights, bool closed, double top_z, GLVolume& volume);
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
#include "libslic3r/Utils.hpp"
|
||||
#include "GUI.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
|
||||
#include "MainFrame.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
|
@ -37,17 +37,10 @@ void AboutDialogLogo::onRepaint(wxEvent &event)
|
|||
// CopyrightsDialog
|
||||
// -----------------------------------------
|
||||
CopyrightsDialog::CopyrightsDialog()
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
: DPIDialog((wxWindow*)wxGetApp().mainframe, wxID_ANY, from_u8((boost::format("%1% - %2%")
|
||||
: DPIDialog(static_cast<wxWindow*>(wxGetApp().mainframe), wxID_ANY, from_u8((boost::format("%1% - %2%")
|
||||
% (wxGetApp().is_editor() ? SLIC3R_APP_NAME : GCODEVIEWER_APP_NAME)
|
||||
% _utf8(L("Portions copyright"))).str()),
|
||||
wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
|
||||
#else
|
||||
: DPIDialog((wxWindow*)wxGetApp().mainframe, wxID_ANY, from_u8((boost::format("%1% - %2%")
|
||||
% SLIC3R_APP_NAME
|
||||
% _utf8(L("Portions copyright"))).str()),
|
||||
wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
{
|
||||
this->SetFont(wxGetApp().normal_font());
|
||||
this->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
|
||||
|
|
@ -208,13 +201,8 @@ void CopyrightsDialog::onCloseDialog(wxEvent &)
|
|||
}
|
||||
|
||||
AboutDialog::AboutDialog()
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
: DPIDialog((wxWindow*)wxGetApp().mainframe, wxID_ANY, from_u8((boost::format(_utf8(L("About %s"))) % (wxGetApp().is_editor() ? SLIC3R_APP_NAME : GCODEVIEWER_APP_NAME)).str()), wxDefaultPosition,
|
||||
: DPIDialog(static_cast<wxWindow*>(wxGetApp().mainframe), wxID_ANY, from_u8((boost::format(_utf8(L("About %s"))) % (wxGetApp().is_editor() ? SLIC3R_APP_NAME : GCODEVIEWER_APP_NAME)).str()), wxDefaultPosition,
|
||||
wxDefaultSize, /*wxCAPTION*/wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
|
||||
#else
|
||||
: DPIDialog((wxWindow*)wxGetApp().mainframe, wxID_ANY, from_u8((boost::format(_utf8(L("About %s"))) % SLIC3R_APP_NAME).str()), wxDefaultPosition,
|
||||
wxDefaultSize, /*wxCAPTION*/wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
{
|
||||
SetFont(wxGetApp().normal_font());
|
||||
|
||||
|
|
@ -226,11 +214,7 @@ AboutDialog::AboutDialog()
|
|||
main_sizer->Add(hsizer, 0, wxEXPAND | wxALL, 20);
|
||||
|
||||
// logo
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
m_logo_bitmap = ScalableBitmap(this, wxGetApp().is_editor() ? "PrusaSlicer_192px.png" : "PrusaSlicer-gcodeviewer_192px.png", 192);
|
||||
#else
|
||||
m_logo_bitmap = ScalableBitmap(this, "PrusaSlicer_192px.png", 192);
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
m_logo = new wxStaticBitmap(this, wxID_ANY, m_logo_bitmap.bmp());
|
||||
hsizer->Add(m_logo, 1, wxALIGN_CENTER_VERTICAL);
|
||||
|
||||
|
|
@ -239,11 +223,7 @@ AboutDialog::AboutDialog()
|
|||
|
||||
// title
|
||||
{
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
wxStaticText* title = new wxStaticText(this, wxID_ANY, wxGetApp().is_editor() ? SLIC3R_APP_NAME : GCODEVIEWER_APP_NAME, wxDefaultPosition, wxDefaultSize);
|
||||
#else
|
||||
wxStaticText* title = new wxStaticText(this, wxID_ANY, SLIC3R_APP_NAME, wxDefaultPosition, wxDefaultSize);
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
wxFont title_font = GUI::wxGetApp().bold_font();
|
||||
title_font.SetFamily(wxFONTFAMILY_ROMAN);
|
||||
title_font.SetPointSize(24);
|
||||
|
|
@ -253,7 +233,7 @@ AboutDialog::AboutDialog()
|
|||
|
||||
// version
|
||||
{
|
||||
auto version_string = _L("Version")+ " " + std::string(SLIC3R_VERSION);
|
||||
auto version_string = _L("Version") + " " + std::string(SLIC3R_VERSION);
|
||||
wxStaticText* version = new wxStaticText(this, wxID_ANY, version_string.c_str(), wxDefaultPosition, wxDefaultSize);
|
||||
wxFont version_font = GetFont();
|
||||
#ifdef __WXMSW__
|
||||
|
|
|
|||
|
|
@ -141,11 +141,7 @@ void BackgroundSlicingProcess::process_fff()
|
|||
// Passing the timestamp
|
||||
evt.SetInt((int)(m_fff_print->step_state_with_timestamp(PrintStep::psSlicingFinished).timestamp));
|
||||
wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, evt.Clone());
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
m_fff_print->export_gcode(m_temp_output_path, m_gcode_result, m_thumbnail_cb);
|
||||
#else
|
||||
m_fff_print->export_gcode(m_temp_output_path, m_gcode_preview_data, m_thumbnail_cb);
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
if (this->set_step_started(bspsGCodeFinalize)) {
|
||||
if (! m_export_path.empty()) {
|
||||
wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, new wxCommandEvent(m_event_export_began_id));
|
||||
|
|
@ -433,25 +429,14 @@ Print::ApplyStatus BackgroundSlicingProcess::apply(const Model &model, const Dyn
|
|||
assert(m_print != nullptr);
|
||||
assert(config.opt_enum<PrinterTechnology>("printer_technology") == m_print->technology());
|
||||
Print::ApplyStatus invalidated = m_print->apply(model, config);
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
if ((invalidated & PrintBase::APPLY_STATUS_INVALIDATED) != 0 && m_print->technology() == ptFFF &&
|
||||
!this->m_fff_print->is_step_done(psGCodeExport))
|
||||
{
|
||||
!this->m_fff_print->is_step_done(psGCodeExport)) {
|
||||
// Some FFF status was invalidated, and the G-code was not exported yet.
|
||||
// Let the G-code preview UI know that the final G-code preview is not valid.
|
||||
// In addition, this early memory deallocation reduces memory footprint.
|
||||
if (m_gcode_result != nullptr)
|
||||
m_gcode_result->reset();
|
||||
}
|
||||
#else
|
||||
if ((invalidated & PrintBase::APPLY_STATUS_INVALIDATED) != 0 && m_print->technology() == ptFFF &&
|
||||
m_gcode_preview_data != nullptr && ! this->m_fff_print->is_step_done(psGCodeExport)) {
|
||||
// Some FFF status was invalidated, and the G-code was not exported yet.
|
||||
// Let the G-code preview UI know that the final G-code preview is not valid.
|
||||
// In addition, this early memory deallocation reduces memory footprint.
|
||||
m_gcode_preview_data->reset();
|
||||
}
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
return invalidated;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,9 +11,7 @@
|
|||
#include "libslic3r/GCode/ThumbnailData.hpp"
|
||||
#include "libslic3r/Format/SL1.hpp"
|
||||
#include "slic3r/Utils/PrintHost.hpp"
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
#include "libslic3r/GCode/GCodeProcessor.hpp"
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
|
||||
|
||||
namespace boost { namespace filesystem { class path; } }
|
||||
|
|
@ -21,9 +19,6 @@ namespace boost { namespace filesystem { class path; } }
|
|||
namespace Slic3r {
|
||||
|
||||
class DynamicPrintConfig;
|
||||
#if !ENABLE_GCODE_VIEWER
|
||||
class GCodePreviewData;
|
||||
#endif // !ENABLE_GCODE_VIEWER
|
||||
class Model;
|
||||
class SLAPrint;
|
||||
|
||||
|
|
@ -88,11 +83,7 @@ public:
|
|||
void set_fff_print(Print *print) { m_fff_print = print; }
|
||||
void set_sla_print(SLAPrint *print) { m_sla_print = print; m_sla_print->set_printer(&m_sla_archive); }
|
||||
void set_thumbnail_cb(ThumbnailsGeneratorCallback cb) { m_thumbnail_cb = cb; }
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
void set_gcode_result(GCodeProcessor::Result* result) { m_gcode_result = result; }
|
||||
#else
|
||||
void set_gcode_preview_data(GCodePreviewData* gpd) { m_gcode_preview_data = gpd; }
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
|
||||
// The following wxCommandEvent will be sent to the UI thread / Plater window, when the slicing is finished
|
||||
// and the background processing will transition into G-code export.
|
||||
|
|
@ -198,13 +189,8 @@ private:
|
|||
// Non-owned pointers to Print instances.
|
||||
Print *m_fff_print = nullptr;
|
||||
SLAPrint *m_sla_print = nullptr;
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
// Data structure, to which the G-code export writes its annotations.
|
||||
GCodeProcessor::Result *m_gcode_result = nullptr;
|
||||
#else
|
||||
// Data structure, to which the G-code export writes its annotations.
|
||||
GCodePreviewData *m_gcode_preview_data = nullptr;
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
// Callback function, used to write thumbnails into gcode.
|
||||
ThumbnailsGeneratorCallback m_thumbnail_cb = nullptr;
|
||||
SL1Archive m_sla_archive;
|
||||
|
|
|
|||
|
|
@ -3,9 +3,7 @@
|
|||
#include "libslic3r/Utils.hpp"
|
||||
#include "../Utils/MacDarkMode.hpp"
|
||||
#include "GUI.hpp"
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
#include "GUI_Utils.hpp"
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
|
|
@ -357,17 +355,6 @@ wxBitmap BitmapCache::mksolid(size_t width, size_t height, unsigned char r, unsi
|
|||
return wxImage_to_wxBitmap_with_alpha(std::move(image), scale);
|
||||
}
|
||||
|
||||
|
||||
#if !ENABLE_GCODE_VIEWER
|
||||
static inline int hex_digit_to_int(const char c)
|
||||
{
|
||||
return
|
||||
(c >= '0' && c <= '9') ? int(c - '0') :
|
||||
(c >= 'A' && c <= 'F') ? int(c - 'A') + 10 :
|
||||
(c >= 'a' && c <= 'f') ? int(c - 'a') + 10 : -1;
|
||||
}
|
||||
#endif // !ENABLE_GCODE_VIEWER
|
||||
|
||||
bool BitmapCache::parse_color(const std::string& scolor, unsigned char* rgb_out)
|
||||
{
|
||||
rgb_out[0] = rgb_out[1] = rgb_out[2] = 0;
|
||||
|
|
|
|||
|
|
@ -237,8 +237,11 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config)
|
|||
bool have_infill = config->option<ConfigOptionPercent>("fill_density")->value > 0;
|
||||
// infill_extruder uses the same logic as in Print::extruders()
|
||||
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);
|
||||
// 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_top_solid_infill = config->opt_int("top_solid_layers") > 0;
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#include "libslic3r/Utils.hpp"
|
||||
#include "libslic3r/Time.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "MainFrame.hpp"
|
||||
#include "wxExtensions.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
|
@ -109,7 +110,7 @@ static wxString generate_html_page(const Config::SnapshotDB &snapshot_db, const
|
|||
}
|
||||
|
||||
ConfigSnapshotDialog::ConfigSnapshotDialog(const Config::SnapshotDB &snapshot_db, const wxString &on_snapshot)
|
||||
: DPIDialog((wxWindow*)wxGetApp().mainframe, wxID_ANY, _(L("Configuration Snapshots")), wxDefaultPosition,
|
||||
: DPIDialog(static_cast<wxWindow*>(wxGetApp().mainframe), wxID_ANY, _(L("Configuration Snapshots")), wxDefaultPosition,
|
||||
wxSize(45 * wxGetApp().em_unit(), 40 * wxGetApp().em_unit()),
|
||||
wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxMAXIMIZE_BOX)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -192,26 +192,23 @@ PrinterPicker::PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxSt
|
|||
|
||||
wxBitmap bitmap;
|
||||
int bitmap_width = 0;
|
||||
int bitmap_height = 0;
|
||||
const wxString bitmap_file = GUI::from_u8(Slic3r::resources_dir() + "/profiles/" + vendor.id + "/" + model.id + "_thumbnail.png");
|
||||
if (wxFileExists(bitmap_file)) {
|
||||
bitmap.LoadFile(bitmap_file, wxBITMAP_TYPE_PNG);
|
||||
bitmap_width = bitmap.GetWidth();
|
||||
bitmap_height = bitmap.GetHeight();
|
||||
} else {
|
||||
BOOST_LOG_TRIVIAL(warning) << boost::format("Can't find bitmap file `%1%` for vendor `%2%`, printer `%3%`, using placeholder icon instead")
|
||||
% bitmap_file
|
||||
% vendor.id
|
||||
% model.id;
|
||||
|
||||
const wxString placeholder_file = GUI::from_u8(Slic3r::var(PRINTER_PLACEHOLDER));
|
||||
if (wxFileExists(placeholder_file)) {
|
||||
bitmap.LoadFile(placeholder_file, wxBITMAP_TYPE_PNG);
|
||||
auto load_bitmap = [](const wxString& bitmap_file, wxBitmap& bitmap, int& bitmap_width)->bool {
|
||||
if (wxFileExists(bitmap_file)) {
|
||||
bitmap.LoadFile(bitmap_file, wxBITMAP_TYPE_PNG);
|
||||
bitmap_width = bitmap.GetWidth();
|
||||
bitmap_height = bitmap.GetHeight();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
if (!load_bitmap(GUI::from_u8(Slic3r::data_dir() + "/vendor/" + vendor.id + "/" + model.id + "_thumbnail.png"), bitmap, bitmap_width)) {
|
||||
if (!load_bitmap(GUI::from_u8(Slic3r::resources_dir() + "/profiles/" + vendor.id + "/" + model.id + "_thumbnail.png"), bitmap, bitmap_width)) {
|
||||
BOOST_LOG_TRIVIAL(warning) << boost::format("Can't find bitmap file `%1%` for vendor `%2%`, printer `%3%`, using placeholder icon instead")
|
||||
% (Slic3r::resources_dir() + "/profiles/" + vendor.id + "/" + model.id + "_thumbnail.png")
|
||||
% vendor.id
|
||||
% model.id;
|
||||
load_bitmap(Slic3r::var(PRINTER_PLACEHOLDER), bitmap, bitmap_width);
|
||||
}
|
||||
}
|
||||
|
||||
auto *title = new wxStaticText(this, wxID_ANY, model.name, wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
|
||||
title->SetFont(font_name);
|
||||
const int wrap_width = std::max((int)MODEL_MIN_WRAP, bitmap_width);
|
||||
|
|
@ -241,7 +238,7 @@ PrinterPicker::PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxSt
|
|||
: from_u8(model.name);
|
||||
|
||||
if (i == 1) {
|
||||
auto *alt_label = new wxStaticText(variants_panel, wxID_ANY, _(L("Alternate nozzles:")));
|
||||
auto *alt_label = new wxStaticText(variants_panel, wxID_ANY, _L("Alternate nozzles:"));
|
||||
alt_label->SetFont(font_alt_nozzle);
|
||||
variants_sizer->Add(alt_label, 0, wxBOTTOM, 3);
|
||||
is_variants = true;
|
||||
|
|
@ -305,9 +302,9 @@ PrinterPicker::PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxSt
|
|||
if (/*titles.size() > 1*/is_variants) {
|
||||
// It only makes sense to add the All / None buttons if there's multiple printers
|
||||
|
||||
auto *sel_all_std = new wxButton(this, wxID_ANY, titles.size() > 1 ? _(L("All standard")) : _(L("Standard")));
|
||||
auto *sel_all = new wxButton(this, wxID_ANY, _(L("All")));
|
||||
auto *sel_none = new wxButton(this, wxID_ANY, _(L("None")));
|
||||
auto *sel_all_std = new wxButton(this, wxID_ANY, titles.size() > 1 ? _L("All standard") : _L("Standard"));
|
||||
auto *sel_all = new wxButton(this, wxID_ANY, _L("All"));
|
||||
auto *sel_none = new wxButton(this, wxID_ANY, _L("None"));
|
||||
sel_all_std->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &event) { this->select_all(true, false); });
|
||||
sel_all->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &event) { this->select_all(true, true); });
|
||||
sel_none->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &event) { this->select_all(false); });
|
||||
|
|
@ -447,14 +444,14 @@ PageWelcome::PageWelcome(ConfigWizard *parent)
|
|||
#else
|
||||
_utf8(L("Welcome to the %s Configuration Wizard"))
|
||||
#endif
|
||||
) % SLIC3R_APP_NAME).str()), _(L("Welcome")))
|
||||
) % SLIC3R_APP_NAME).str()), _L("Welcome"))
|
||||
, welcome_text(append_text(from_u8((boost::format(
|
||||
_utf8(L("Hello, welcome to %s! This %s helps you with the initial configuration; just a few settings and you will be ready to print.")))
|
||||
% SLIC3R_APP_NAME
|
||||
% _utf8(ConfigWizard::name())).str())
|
||||
))
|
||||
, cbox_reset(append(
|
||||
new wxCheckBox(this, wxID_ANY, _(L("Remove user profiles (a snapshot will be taken beforehand)")))
|
||||
new wxCheckBox(this, wxID_ANY, _L("Remove user profiles (a snapshot will be taken beforehand)"))
|
||||
))
|
||||
{
|
||||
welcome_text->Hide();
|
||||
|
|
@ -585,10 +582,10 @@ PageMaterials::PageMaterials(ConfigWizard *parent, Materials *materials, wxStrin
|
|||
grid->AddGrowableCol(3, 1);
|
||||
grid->AddGrowableRow(1, 1);
|
||||
|
||||
grid->Add(new wxStaticText(this, wxID_ANY, _(L("Printer:"))));
|
||||
grid->Add(new wxStaticText(this, wxID_ANY, _L("Printer:")));
|
||||
grid->Add(new wxStaticText(this, wxID_ANY, list1name));
|
||||
grid->Add(new wxStaticText(this, wxID_ANY, _(L("Vendor:"))));
|
||||
grid->Add(new wxStaticText(this, wxID_ANY, _(L("Profile:"))));
|
||||
grid->Add(new wxStaticText(this, wxID_ANY, _L("Vendor:")));
|
||||
grid->Add(new wxStaticText(this, wxID_ANY, _L("Profile:")));
|
||||
|
||||
grid->Add(list_printer, 0, wxEXPAND);
|
||||
grid->Add(list_type, 0, wxEXPAND);
|
||||
|
|
@ -596,8 +593,8 @@ PageMaterials::PageMaterials(ConfigWizard *parent, Materials *materials, wxStrin
|
|||
grid->Add(list_profile, 1, wxEXPAND);
|
||||
|
||||
auto *btn_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto *sel_all = new wxButton(this, wxID_ANY, _(L("All")));
|
||||
auto *sel_none = new wxButton(this, wxID_ANY, _(L("None")));
|
||||
auto *sel_all = new wxButton(this, wxID_ANY, _L("All"));
|
||||
auto *sel_none = new wxButton(this, wxID_ANY, _L("None"));
|
||||
btn_sizer->Add(sel_all, 0, wxRIGHT, em / 2);
|
||||
btn_sizer->Add(sel_none);
|
||||
|
||||
|
|
@ -660,7 +657,7 @@ void PageMaterials::reload_presets()
|
|||
{
|
||||
clear();
|
||||
|
||||
list_printer->append(_(L("(All)")), &EMPTY);
|
||||
list_printer->append(_L("(All)"), &EMPTY);
|
||||
//list_printer->SetLabelMarkup("<b>bald</b>");
|
||||
for (const Preset* printer : materials->printers) {
|
||||
list_printer->append(printer->name, &printer->name);
|
||||
|
|
@ -689,10 +686,10 @@ void PageMaterials::set_compatible_printers_html_window(const std::vector<std::s
|
|||
const auto bgr_clr_str = wxString::Format(wxT("#%02X%02X%02X"), bgr_clr.Red(), bgr_clr.Green(), bgr_clr.Blue());
|
||||
const auto text_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
|
||||
const auto text_clr_str = wxString::Format(wxT("#%02X%02X%02X"), text_clr.Red(), text_clr.Green(), text_clr.Blue());
|
||||
wxString first_line = _(L("Filaments marked with <b>*</b> are <b>not</b> compatible with some installed printers."));
|
||||
wxString first_line = _L("Filaments marked with <b>*</b> are <b>not</b> compatible with some installed printers.");
|
||||
wxString text;
|
||||
if (all_printers) {
|
||||
wxString second_line = _(L("All installed printers are compatible with the selected filament."));
|
||||
wxString second_line = _L("All installed printers are compatible with the selected filament.");
|
||||
text = wxString::Format(
|
||||
"<html>"
|
||||
"<style>"
|
||||
|
|
@ -712,7 +709,7 @@ void PageMaterials::set_compatible_printers_html_window(const std::vector<std::s
|
|||
, second_line
|
||||
);
|
||||
} else {
|
||||
wxString second_line = _(L("Only the following installed printers are compatible with the selected filament:"));
|
||||
wxString second_line = _L("Only the following installed printers are compatible with the selected filament:");
|
||||
text = wxString::Format(
|
||||
"<html>"
|
||||
"<style>"
|
||||
|
|
@ -810,7 +807,7 @@ void PageMaterials::update_lists(int sel_printer, int sel_type, int sel_vendor)
|
|||
if (sel_printers_count != sel_printer_count_prev || (sel_printers_count == 1 && sel_printer_item_prev != sel_printer && sel_printer != -1)) {
|
||||
// Refresh type list
|
||||
list_type->Clear();
|
||||
list_type->append(_(L("(All)")), &EMPTY);
|
||||
list_type->append(_L("(All)"), &EMPTY);
|
||||
if (sel_printers_count > 0) {
|
||||
// If all is selected with other printers
|
||||
// unselect "all" or all printers depending on last value
|
||||
|
|
@ -871,7 +868,7 @@ void PageMaterials::update_lists(int sel_printer, int sel_type, int sel_vendor)
|
|||
// but the number of vendors is going to be very small this shouldn't be a problem.
|
||||
|
||||
list_vendor->Clear();
|
||||
list_vendor->append(_(L("(All)")), &EMPTY);
|
||||
list_vendor->append(_L("(All)"), &EMPTY);
|
||||
if (sel_printers_count != 0 && sel_type != wxNOT_FOUND) {
|
||||
const std::string& type = list_type->get_data(sel_type);
|
||||
// find printer preset
|
||||
|
|
@ -1008,7 +1005,7 @@ void PageMaterials::sort_list_data(StringList* list, bool add_All_item, bool mat
|
|||
|
||||
list->Clear();
|
||||
if (add_All_item)
|
||||
list->append(_(L("(All)")), &EMPTY);
|
||||
list->append(_L("(All)"), &EMPTY);
|
||||
for (const auto& item : prusa_profiles)
|
||||
list->append(item, &const_cast<std::string&>(item.get()));
|
||||
for (const auto& item : other_profiles)
|
||||
|
|
@ -1098,11 +1095,11 @@ void PageMaterials::on_activate()
|
|||
const char *PageCustom::default_profile_name = "My Settings";
|
||||
|
||||
PageCustom::PageCustom(ConfigWizard *parent)
|
||||
: ConfigWizardPage(parent, _(L("Custom Printer Setup")), _(L("Custom Printer")))
|
||||
: ConfigWizardPage(parent, _L("Custom Printer Setup"), _L("Custom Printer"))
|
||||
{
|
||||
cb_custom = new wxCheckBox(this, wxID_ANY, _(L("Define a custom printer profile")));
|
||||
cb_custom = new wxCheckBox(this, wxID_ANY, _L("Define a custom printer profile"));
|
||||
tc_profile_name = new wxTextCtrl(this, wxID_ANY, default_profile_name);
|
||||
auto *label = new wxStaticText(this, wxID_ANY, _(L("Custom profile name:")));
|
||||
auto *label = new wxStaticText(this, wxID_ANY, _L("Custom profile name:"));
|
||||
|
||||
tc_profile_name->Enable(false);
|
||||
tc_profile_name->Bind(wxEVT_KILL_FOCUS, [this](wxFocusEvent &evt) {
|
||||
|
|
@ -1127,7 +1124,7 @@ PageCustom::PageCustom(ConfigWizard *parent)
|
|||
}
|
||||
|
||||
PageUpdate::PageUpdate(ConfigWizard *parent)
|
||||
: ConfigWizardPage(parent, _(L("Automatic updates")), _(L("Updates")))
|
||||
: ConfigWizardPage(parent, _L("Automatic updates"), _L("Updates"))
|
||||
, version_check(true)
|
||||
, preset_update(true)
|
||||
{
|
||||
|
|
@ -1135,60 +1132,76 @@ PageUpdate::PageUpdate(ConfigWizard *parent)
|
|||
auto boldfont = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
|
||||
boldfont.SetWeight(wxFONTWEIGHT_BOLD);
|
||||
|
||||
auto *box_slic3r = new wxCheckBox(this, wxID_ANY, _(L("Check for application updates")));
|
||||
auto *box_slic3r = new wxCheckBox(this, wxID_ANY, _L("Check for application updates"));
|
||||
box_slic3r->SetValue(app_config->get("version_check") == "1");
|
||||
append(box_slic3r);
|
||||
append_text(wxString::Format(_(L(
|
||||
append_text(wxString::Format(_L(
|
||||
"If enabled, %s checks for new application versions online. When a new version becomes available, "
|
||||
"a notification is displayed at the next application startup (never during program usage). "
|
||||
"This is only a notification mechanisms, no automatic installation is done.")), SLIC3R_APP_NAME));
|
||||
"This is only a notification mechanisms, no automatic installation is done."), SLIC3R_APP_NAME));
|
||||
|
||||
append_spacer(VERTICAL_SPACING);
|
||||
|
||||
auto *box_presets = new wxCheckBox(this, wxID_ANY, _(L("Update built-in Presets automatically")));
|
||||
auto *box_presets = new wxCheckBox(this, wxID_ANY, _L("Update built-in Presets automatically"));
|
||||
box_presets->SetValue(app_config->get("preset_update") == "1");
|
||||
append(box_presets);
|
||||
append_text(wxString::Format(_(L(
|
||||
append_text(wxString::Format(_L(
|
||||
"If enabled, %s downloads updates of built-in system presets in the background."
|
||||
"These updates are downloaded into a separate temporary location."
|
||||
"When a new preset version becomes available it is offered at application startup.")), SLIC3R_APP_NAME));
|
||||
const auto text_bold = _(L("Updates are never applied without user's consent and never overwrite user's customized settings."));
|
||||
"When a new preset version becomes available it is offered at application startup."), SLIC3R_APP_NAME));
|
||||
const auto text_bold = _L("Updates are never applied without user's consent and never overwrite user's customized settings.");
|
||||
auto *label_bold = new wxStaticText(this, wxID_ANY, text_bold);
|
||||
label_bold->SetFont(boldfont);
|
||||
label_bold->Wrap(WRAP_WIDTH);
|
||||
append(label_bold);
|
||||
append_text(_(L("Additionally a backup snapshot of the whole configuration is created before an update is applied.")));
|
||||
append_text(_L("Additionally a backup snapshot of the whole configuration is created before an update is applied."));
|
||||
|
||||
box_slic3r->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent &event) { this->version_check = event.IsChecked(); });
|
||||
box_presets->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent &event) { this->preset_update = event.IsChecked(); });
|
||||
}
|
||||
|
||||
PageReloadFromDisk::PageReloadFromDisk(ConfigWizard* parent)
|
||||
: ConfigWizardPage(parent, _(L("Reload from disk")), _(L("Reload from disk")))
|
||||
: ConfigWizardPage(parent, _L("Reload from disk"), _L("Reload from disk"))
|
||||
, full_pathnames(false)
|
||||
{
|
||||
auto* box_pathnames = new wxCheckBox(this, wxID_ANY, _(L("Export full pathnames of models and parts sources into 3mf and amf files")));
|
||||
auto* box_pathnames = new wxCheckBox(this, wxID_ANY, _L("Export full pathnames of models and parts sources into 3mf and amf files"));
|
||||
box_pathnames->SetValue(wxGetApp().app_config->get("export_sources_full_pathnames") == "1");
|
||||
append(box_pathnames);
|
||||
append_text(_(L(
|
||||
append_text(_L(
|
||||
"If enabled, allows the Reload from disk command to automatically find and load the files when invoked.\n"
|
||||
"If not enabled, the Reload from disk command will ask to select each file using an open file dialog."
|
||||
)));
|
||||
));
|
||||
|
||||
box_pathnames->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent& event) { this->full_pathnames = event.IsChecked(); });
|
||||
}
|
||||
|
||||
PageMode::PageMode(ConfigWizard *parent)
|
||||
: ConfigWizardPage(parent, _(L("View mode")), _(L("View mode")))
|
||||
#if ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN
|
||||
#ifdef _WIN32
|
||||
PageFilesAssociation::PageFilesAssociation(ConfigWizard* parent)
|
||||
: ConfigWizardPage(parent, _L("Files association"), _L("Files association"))
|
||||
{
|
||||
append_text(_(L("PrusaSlicer's user interfaces comes in three variants:\nSimple, Advanced, and Expert.\n"
|
||||
cb_3mf = new wxCheckBox(this, wxID_ANY, _L("Associate .3mf files to PrusaSlicer"));
|
||||
cb_stl = new wxCheckBox(this, wxID_ANY, _L("Associate .stl files to PrusaSlicer"));
|
||||
// cb_gcode = new wxCheckBox(this, wxID_ANY, _L("Associate .gcode files to PrusaSlicer G-code Viewer"));
|
||||
|
||||
append(cb_3mf);
|
||||
append(cb_stl);
|
||||
// append(cb_gcode);
|
||||
}
|
||||
#endif // _WIN32
|
||||
#endif // ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN
|
||||
|
||||
PageMode::PageMode(ConfigWizard *parent)
|
||||
: ConfigWizardPage(parent, _L("View mode"), _L("View mode"))
|
||||
{
|
||||
append_text(_L("PrusaSlicer's user interfaces comes in three variants:\nSimple, Advanced, and Expert.\n"
|
||||
"The Simple mode shows only the most frequently used settings relevant for regular 3D printing. "
|
||||
"The other two offer progressively more sophisticated fine-tuning, "
|
||||
"they are suitable for advanced and expert users, respectively.")));
|
||||
"they are suitable for advanced and expert users, respectively."));
|
||||
|
||||
radio_simple = new wxRadioButton(this, wxID_ANY, _(L("Simple mode")));
|
||||
radio_advanced = new wxRadioButton(this, wxID_ANY, _(L("Advanced mode")));
|
||||
radio_expert = new wxRadioButton(this, wxID_ANY, _(L("Expert mode")));
|
||||
radio_simple = new wxRadioButton(this, wxID_ANY, _L("Simple mode"));
|
||||
radio_advanced = new wxRadioButton(this, wxID_ANY, _L("Advanced mode"));
|
||||
radio_expert = new wxRadioButton(this, wxID_ANY, _L("Expert mode"));
|
||||
|
||||
append(radio_simple);
|
||||
append(radio_advanced);
|
||||
|
|
@ -1229,11 +1242,11 @@ void PageMode::serialize_mode(AppConfig *app_config) const
|
|||
}
|
||||
|
||||
PageVendors::PageVendors(ConfigWizard *parent)
|
||||
: ConfigWizardPage(parent, _(L("Other Vendors")), _(L("Other Vendors")))
|
||||
: ConfigWizardPage(parent, _L("Other Vendors"), _L("Other Vendors"))
|
||||
{
|
||||
const AppConfig &appconfig = this->wizard_p()->appconfig_new;
|
||||
|
||||
append_text(wxString::Format(_(L("Pick another vendor supported by %s")), SLIC3R_APP_NAME) + ":");
|
||||
append_text(wxString::Format(_L("Pick another vendor supported by %s"), SLIC3R_APP_NAME) + ":");
|
||||
|
||||
auto boldfont = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
|
||||
boldfont.SetWeight(wxFONTWEIGHT_BOLD);
|
||||
|
|
@ -1264,11 +1277,11 @@ PageVendors::PageVendors(ConfigWizard *parent)
|
|||
}
|
||||
|
||||
PageFirmware::PageFirmware(ConfigWizard *parent)
|
||||
: ConfigWizardPage(parent, _(L("Firmware Type")), _(L("Firmware")), 1)
|
||||
: ConfigWizardPage(parent, _L("Firmware Type"), _L("Firmware"), 1)
|
||||
, gcode_opt(*print_config_def.get("gcode_flavor"))
|
||||
, gcode_picker(nullptr)
|
||||
{
|
||||
append_text(_(L("Choose the type of firmware used by your printer.")));
|
||||
append_text(_L("Choose the type of firmware used by your printer."));
|
||||
append_text(_(gcode_opt.tooltip));
|
||||
|
||||
wxArrayString choices;
|
||||
|
|
@ -1302,10 +1315,10 @@ void PageFirmware::apply_custom_config(DynamicPrintConfig &config)
|
|||
}
|
||||
|
||||
PageBedShape::PageBedShape(ConfigWizard *parent)
|
||||
: ConfigWizardPage(parent, _(L("Bed Shape and Size")), _(L("Bed Shape")), 1)
|
||||
: ConfigWizardPage(parent, _L("Bed Shape and Size"), _L("Bed Shape"), 1)
|
||||
, shape_panel(new BedShapePanel(this))
|
||||
{
|
||||
append_text(_(L("Set the shape of your printer's bed.")));
|
||||
append_text(_L("Set the shape of your printer's bed."));
|
||||
|
||||
shape_panel->build_panel(*wizard_p()->custom_config->option<ConfigOptionPoints>("bed_shape"),
|
||||
*wizard_p()->custom_config->option<ConfigOptionString>("bed_custom_texture"),
|
||||
|
|
@ -1325,7 +1338,7 @@ void PageBedShape::apply_custom_config(DynamicPrintConfig &config)
|
|||
}
|
||||
|
||||
PageDiameters::PageDiameters(ConfigWizard *parent)
|
||||
: ConfigWizardPage(parent, _(L("Filament and Nozzle Diameters")), _(L("Print Diameters")), 1)
|
||||
: ConfigWizardPage(parent, _L("Filament and Nozzle Diameters"), _L("Print Diameters"), 1)
|
||||
, spin_nozzle(new wxSpinCtrlDouble(this, wxID_ANY))
|
||||
, spin_filam(new wxSpinCtrlDouble(this, wxID_ANY))
|
||||
{
|
||||
|
|
@ -1339,11 +1352,11 @@ PageDiameters::PageDiameters(ConfigWizard *parent)
|
|||
auto *default_filam = print_config_def.get("filament_diameter")->get_default_value<ConfigOptionFloats>();
|
||||
spin_filam->SetValue(default_filam != nullptr && default_filam->size() > 0 ? default_filam->get_at(0) : 3.0);
|
||||
|
||||
append_text(_(L("Enter the diameter of your printer's hot end nozzle.")));
|
||||
append_text(_L("Enter the diameter of your printer's hot end nozzle."));
|
||||
|
||||
auto *sizer_nozzle = new wxFlexGridSizer(3, 5, 5);
|
||||
auto *text_nozzle = new wxStaticText(this, wxID_ANY, _(L("Nozzle Diameter:")));
|
||||
auto *unit_nozzle = new wxStaticText(this, wxID_ANY, _(L("mm")));
|
||||
auto *text_nozzle = new wxStaticText(this, wxID_ANY, _L("Nozzle Diameter:"));
|
||||
auto *unit_nozzle = new wxStaticText(this, wxID_ANY, _L("mm"));
|
||||
sizer_nozzle->AddGrowableCol(0, 1);
|
||||
sizer_nozzle->Add(text_nozzle, 0, wxALIGN_CENTRE_VERTICAL);
|
||||
sizer_nozzle->Add(spin_nozzle);
|
||||
|
|
@ -1352,12 +1365,12 @@ PageDiameters::PageDiameters(ConfigWizard *parent)
|
|||
|
||||
append_spacer(VERTICAL_SPACING);
|
||||
|
||||
append_text(_(L("Enter the diameter of your filament.")));
|
||||
append_text(_(L("Good precision is required, so use a caliper and do multiple measurements along the filament, then compute the average.")));
|
||||
append_text(_L("Enter the diameter of your filament."));
|
||||
append_text(_L("Good precision is required, so use a caliper and do multiple measurements along the filament, then compute the average."));
|
||||
|
||||
auto *sizer_filam = new wxFlexGridSizer(3, 5, 5);
|
||||
auto *text_filam = new wxStaticText(this, wxID_ANY, _(L("Filament Diameter:")));
|
||||
auto *unit_filam = new wxStaticText(this, wxID_ANY, _(L("mm")));
|
||||
auto *text_filam = new wxStaticText(this, wxID_ANY, _L("Filament Diameter:"));
|
||||
auto *unit_filam = new wxStaticText(this, wxID_ANY, _L("mm"));
|
||||
sizer_filam->AddGrowableCol(0, 1);
|
||||
sizer_filam->Add(text_filam, 0, wxALIGN_CENTRE_VERTICAL);
|
||||
sizer_filam->Add(spin_filam);
|
||||
|
|
@ -1390,7 +1403,7 @@ void PageDiameters::apply_custom_config(DynamicPrintConfig &config)
|
|||
}
|
||||
|
||||
PageTemperatures::PageTemperatures(ConfigWizard *parent)
|
||||
: ConfigWizardPage(parent, _(L("Nozzle and Bed Temperatures")), _(L("Temperatures")), 1)
|
||||
: ConfigWizardPage(parent, _L("Nozzle and Bed Temperatures"), _L("Temperatures"), 1)
|
||||
, spin_extr(new wxSpinCtrlDouble(this, wxID_ANY))
|
||||
, spin_bed(new wxSpinCtrlDouble(this, wxID_ANY))
|
||||
{
|
||||
|
|
@ -1406,12 +1419,12 @@ PageTemperatures::PageTemperatures(ConfigWizard *parent)
|
|||
auto *default_bed = def_bed.get_default_value<ConfigOptionInts>();
|
||||
spin_bed->SetValue(default_bed != nullptr && default_bed->size() > 0 ? default_bed->get_at(0) : 0);
|
||||
|
||||
append_text(_(L("Enter the temperature needed for extruding your filament.")));
|
||||
append_text(_(L("A rule of thumb is 160 to 230 °C for PLA, and 215 to 250 °C for ABS.")));
|
||||
append_text(_L("Enter the temperature needed for extruding your filament."));
|
||||
append_text(_L("A rule of thumb is 160 to 230 °C for PLA, and 215 to 250 °C for ABS."));
|
||||
|
||||
auto *sizer_extr = new wxFlexGridSizer(3, 5, 5);
|
||||
auto *text_extr = new wxStaticText(this, wxID_ANY, _(L("Extrusion Temperature:")));
|
||||
auto *unit_extr = new wxStaticText(this, wxID_ANY, _(L("°C")));
|
||||
auto *text_extr = new wxStaticText(this, wxID_ANY, _L("Extrusion Temperature:"));
|
||||
auto *unit_extr = new wxStaticText(this, wxID_ANY, _L("°C"));
|
||||
sizer_extr->AddGrowableCol(0, 1);
|
||||
sizer_extr->Add(text_extr, 0, wxALIGN_CENTRE_VERTICAL);
|
||||
sizer_extr->Add(spin_extr);
|
||||
|
|
@ -1420,12 +1433,12 @@ PageTemperatures::PageTemperatures(ConfigWizard *parent)
|
|||
|
||||
append_spacer(VERTICAL_SPACING);
|
||||
|
||||
append_text(_(L("Enter the bed temperature needed for getting your filament to stick to your heated bed.")));
|
||||
append_text(_(L("A rule of thumb is 60 °C for PLA and 110 °C for ABS. Leave zero if you have no heated bed.")));
|
||||
append_text(_L("Enter the bed temperature needed for getting your filament to stick to your heated bed."));
|
||||
append_text(_L("A rule of thumb is 60 °C for PLA and 110 °C for ABS. Leave zero if you have no heated bed."));
|
||||
|
||||
auto *sizer_bed = new wxFlexGridSizer(3, 5, 5);
|
||||
auto *text_bed = new wxStaticText(this, wxID_ANY, _(L("Bed Temperature:")));
|
||||
auto *unit_bed = new wxStaticText(this, wxID_ANY, _(L("°C")));
|
||||
auto *text_bed = new wxStaticText(this, wxID_ANY, _L("Bed Temperature:"));
|
||||
auto *unit_bed = new wxStaticText(this, wxID_ANY, _L("°C"));
|
||||
sizer_bed->AddGrowableCol(0, 1);
|
||||
sizer_bed->Add(text_bed, 0, wxALIGN_CENTRE_VERTICAL);
|
||||
sizer_bed->Add(spin_bed);
|
||||
|
|
@ -1783,6 +1796,11 @@ void ConfigWizard::priv::load_pages()
|
|||
|
||||
index->add_page(page_update);
|
||||
index->add_page(page_reload_from_disk);
|
||||
#if ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN
|
||||
#ifdef _WIN32
|
||||
index->add_page(page_files_association);
|
||||
#endif // _WIN32
|
||||
#endif // ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN
|
||||
index->add_page(page_mode);
|
||||
|
||||
index->go_to(former_active); // Will restore the active item/page if possible
|
||||
|
|
@ -1879,7 +1897,7 @@ void ConfigWizard::priv::load_vendors()
|
|||
|
||||
void ConfigWizard::priv::add_page(ConfigWizardPage *page)
|
||||
{
|
||||
const int proportion = (page->shortname == _(L("Filaments"))) || (page->shortname == _(L("SLA Materials"))) ? 1 : 0;
|
||||
const int proportion = (page->shortname == _L("Filaments")) || (page->shortname == _L("SLA Materials")) ? 1 : 0;
|
||||
hscroll_sizer->Add(page, proportion, wxEXPAND);
|
||||
all_pages.push_back(page);
|
||||
}
|
||||
|
|
@ -1933,12 +1951,12 @@ void ConfigWizard::priv::create_3rdparty_pages()
|
|||
PagePrinters* pageSLA = nullptr;
|
||||
|
||||
if (is_fff_technology) {
|
||||
pageFFF = new PagePrinters(q, vendor->name + " " +_(L("FFF Technology Printers")), vendor->name+" FFF", *vendor, 1, T_FFF);
|
||||
pageFFF = new PagePrinters(q, vendor->name + " " +_L("FFF Technology Printers"), vendor->name+" FFF", *vendor, 1, T_FFF);
|
||||
add_page(pageFFF);
|
||||
}
|
||||
|
||||
if (is_sla_technology) {
|
||||
pageSLA = new PagePrinters(q, vendor->name + " " + _(L("SLA Technology Printers")), vendor->name+" MSLA", *vendor, 1, T_SLA);
|
||||
pageSLA = new PagePrinters(q, vendor->name + " " + _L("SLA Technology Printers"), vendor->name+" MSLA", *vendor, 1, T_SLA);
|
||||
add_page(pageSLA);
|
||||
}
|
||||
|
||||
|
|
@ -2244,7 +2262,7 @@ bool ConfigWizard::priv::check_and_install_missing_materials(Technology technolo
|
|||
|
||||
const auto ask_and_select_default_materials = [this](const wxString &message, const std::set<const VendorProfile::PrinterModel*> &printer_models, Technology technology)
|
||||
{
|
||||
wxMessageDialog msg(q, message, _(L("Notice")), wxYES_NO);
|
||||
wxMessageDialog msg(q, message, _L("Notice"), wxYES_NO);
|
||||
if (msg.ShowModal() == wxID_YES)
|
||||
select_default_materials_for_printer_models(technology, printer_models);
|
||||
};
|
||||
|
|
@ -2375,6 +2393,26 @@ void ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese
|
|||
app_config->set("preset_update", page_update->preset_update ? "1" : "0");
|
||||
app_config->set("export_sources_full_pathnames", page_reload_from_disk->full_pathnames ? "1" : "0");
|
||||
|
||||
#if ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN
|
||||
#ifdef _WIN32
|
||||
app_config->set("associate_3mf", page_files_association->associate_3mf() ? "1" : "0");
|
||||
app_config->set("associate_stl", page_files_association->associate_stl() ? "1" : "0");
|
||||
// app_config->set("associate_gcode", page_files_association->associate_gcode() ? "1" : "0");
|
||||
|
||||
if (wxGetApp().is_editor()) {
|
||||
if (page_files_association->associate_3mf())
|
||||
wxGetApp().associate_3mf_files();
|
||||
if (page_files_association->associate_stl())
|
||||
wxGetApp().associate_stl_files();
|
||||
}
|
||||
// else {
|
||||
// if (page_files_association->associate_gcode())
|
||||
// wxGetApp().associate_gcode_files();
|
||||
// }
|
||||
|
||||
#endif // _WIN32
|
||||
#endif // ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN
|
||||
|
||||
page_mode->serialize_mode(app_config);
|
||||
|
||||
std::string preferred_model;
|
||||
|
|
@ -2494,13 +2532,13 @@ ConfigWizard::ConfigWizard(wxWindow *parent)
|
|||
topsizer->AddSpacer(INDEX_MARGIN);
|
||||
topsizer->Add(p->hscroll, 1, wxEXPAND);
|
||||
|
||||
p->btn_sel_all = new wxButton(this, wxID_ANY, _(L("Select all standard printers")));
|
||||
p->btn_sel_all = new wxButton(this, wxID_ANY, _L("Select all standard printers"));
|
||||
p->btnsizer->Add(p->btn_sel_all);
|
||||
|
||||
p->btn_prev = new wxButton(this, wxID_ANY, _(L("< &Back")));
|
||||
p->btn_next = new wxButton(this, wxID_ANY, _(L("&Next >")));
|
||||
p->btn_finish = new wxButton(this, wxID_APPLY, _(L("&Finish")));
|
||||
p->btn_cancel = new wxButton(this, wxID_CANCEL, _(L("Cancel"))); // Note: The label needs to be present, otherwise we get accelerator bugs on Mac
|
||||
p->btn_prev = new wxButton(this, wxID_ANY, _L("< &Back"));
|
||||
p->btn_next = new wxButton(this, wxID_ANY, _L("&Next >"));
|
||||
p->btn_finish = new wxButton(this, wxID_APPLY, _L("&Finish"));
|
||||
p->btn_cancel = new wxButton(this, wxID_CANCEL, _L("Cancel")); // Note: The label needs to be present, otherwise we get accelerator bugs on Mac
|
||||
p->btnsizer->AddStretchSpacer();
|
||||
p->btnsizer->Add(p->btn_prev, 0, wxLEFT, BTN_SPACING);
|
||||
p->btnsizer->Add(p->btn_next, 0, wxLEFT, BTN_SPACING);
|
||||
|
|
@ -2513,10 +2551,10 @@ ConfigWizard::ConfigWizard(wxWindow *parent)
|
|||
|
||||
p->add_page(p->page_welcome = new PageWelcome(this));
|
||||
|
||||
p->page_fff = new PagePrinters(this, _(L("Prusa FFF Technology Printers")), "Prusa FFF", *vendor_prusa, 0, T_FFF);
|
||||
p->page_fff = new PagePrinters(this, _L("Prusa FFF Technology Printers"), "Prusa FFF", *vendor_prusa, 0, T_FFF);
|
||||
p->add_page(p->page_fff);
|
||||
|
||||
p->page_msla = new PagePrinters(this, _(L("Prusa MSLA Technology Printers")), "Prusa MSLA", *vendor_prusa, 0, T_SLA);
|
||||
p->page_msla = new PagePrinters(this, _L("Prusa MSLA Technology Printers"), "Prusa MSLA", *vendor_prusa, 0, T_SLA);
|
||||
p->add_page(p->page_msla);
|
||||
|
||||
// Pages for 3rd party vendors
|
||||
|
|
@ -2531,13 +2569,18 @@ ConfigWizard::ConfigWizard(wxWindow *parent)
|
|||
p->update_materials(T_ANY);
|
||||
|
||||
p->add_page(p->page_filaments = new PageMaterials(this, &p->filaments,
|
||||
_(L("Filament Profiles Selection")), _(L("Filaments")), _(L("Type:")) ));
|
||||
_L("Filament Profiles Selection"), _L("Filaments"), _L("Type:") ));
|
||||
p->add_page(p->page_sla_materials = new PageMaterials(this, &p->sla_materials,
|
||||
_(L("SLA Material Profiles Selection")) + " ", _(L("SLA Materials")), _(L("Type:")) ));
|
||||
_L("SLA Material Profiles Selection") + " ", _L("SLA Materials"), _L("Type:") ));
|
||||
|
||||
|
||||
p->add_page(p->page_update = new PageUpdate(this));
|
||||
p->add_page(p->page_reload_from_disk = new PageReloadFromDisk(this));
|
||||
#if ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN
|
||||
#ifdef _WIN32
|
||||
p->add_page(p->page_files_association = new PageFilesAssociation(this));
|
||||
#endif // _WIN32
|
||||
#endif // ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN
|
||||
p->add_page(p->page_mode = new PageMode(this));
|
||||
p->add_page(p->page_firmware = new PageFirmware(this));
|
||||
p->add_page(p->page_bed = new PageBedShape(this));
|
||||
|
|
|
|||
|
|
@ -392,6 +392,25 @@ struct PageReloadFromDisk : ConfigWizardPage
|
|||
PageReloadFromDisk(ConfigWizard* parent);
|
||||
};
|
||||
|
||||
#if ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN
|
||||
#ifdef _WIN32
|
||||
struct PageFilesAssociation : ConfigWizardPage
|
||||
{
|
||||
private:
|
||||
wxCheckBox* cb_3mf{ nullptr };
|
||||
wxCheckBox* cb_stl{ nullptr };
|
||||
// wxCheckBox* cb_gcode;
|
||||
|
||||
public:
|
||||
PageFilesAssociation(ConfigWizard* parent);
|
||||
|
||||
bool associate_3mf() const { return cb_3mf->IsChecked(); }
|
||||
bool associate_stl() const { return cb_stl->IsChecked(); }
|
||||
// bool associate_gcode() const { return cb_gcode->IsChecked(); }
|
||||
};
|
||||
#endif // _WIN32
|
||||
#endif // ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN
|
||||
|
||||
struct PageMode: ConfigWizardPage
|
||||
{
|
||||
wxRadioButton *radio_simple;
|
||||
|
|
@ -550,6 +569,11 @@ struct ConfigWizard::priv
|
|||
PageCustom *page_custom = nullptr;
|
||||
PageUpdate *page_update = nullptr;
|
||||
PageReloadFromDisk *page_reload_from_disk = nullptr;
|
||||
#if ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN
|
||||
#ifdef _WIN32
|
||||
PageFilesAssociation* page_files_association = nullptr;
|
||||
#endif // _WIN32
|
||||
#endif // ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN
|
||||
PageMode *page_mode = nullptr;
|
||||
PageVendors *page_vendors = nullptr;
|
||||
Pages3rdparty pages_3rdparty;
|
||||
|
|
@ -565,9 +589,7 @@ struct ConfigWizard::priv
|
|||
|
||||
priv(ConfigWizard *q)
|
||||
: q(q)
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
, appconfig_new(AppConfig::EAppMode::Editor)
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
, filaments(T_FFF)
|
||||
, sla_materials(T_SLA)
|
||||
{}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,6 @@
|
|||
#include "libslic3r/libslic3r.h"
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
#include "DoubleSlider.hpp"
|
||||
#include "libslic3r/GCode.hpp"
|
||||
#else
|
||||
#include "wxExtensions.hpp"
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
#include "GUI.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "Plater.hpp"
|
||||
|
|
@ -12,6 +8,7 @@
|
|||
#include "ExtruderSequenceDialog.hpp"
|
||||
#include "libslic3r/Print.hpp"
|
||||
#include "libslic3r/AppConfig.hpp"
|
||||
#include "GUI_Utils.hpp"
|
||||
|
||||
#include <wx/button.h>
|
||||
#include <wx/dialog.h>
|
||||
|
|
@ -21,9 +18,6 @@
|
|||
#include <wx/bmpcbox.h>
|
||||
#include <wx/statline.h>
|
||||
#include <wx/dcclient.h>
|
||||
#if !ENABLE_GCODE_VIEWER
|
||||
#include <wx/numformatter.h>
|
||||
#endif // !ENABLE_GCODE_VIEWER
|
||||
#include <wx/colordlg.h>
|
||||
|
||||
#include <cmath>
|
||||
|
|
@ -68,7 +62,8 @@ Control::Control( wxWindow *parent,
|
|||
m_higher_value (higherValue),
|
||||
m_min_value(minValue),
|
||||
m_max_value(maxValue),
|
||||
m_style(style == wxSL_HORIZONTAL || style == wxSL_VERTICAL ? style: wxSL_HORIZONTAL)
|
||||
m_style(style == wxSL_HORIZONTAL || style == wxSL_VERTICAL ? style: wxSL_HORIZONTAL),
|
||||
m_extra_style(style == wxSL_VERTICAL ? wxSL_AUTOTICKS | wxSL_VALUE_LABEL : 0)
|
||||
{
|
||||
#ifdef __WXOSX__
|
||||
is_osx = true;
|
||||
|
|
@ -76,13 +71,8 @@ Control::Control( wxWindow *parent,
|
|||
if (!is_osx)
|
||||
SetDoubleBuffered(true);// SetDoubleBuffered exists on Win and Linux/GTK, but is missing on OSX
|
||||
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
m_bmp_thumb_higher = (style == wxSL_HORIZONTAL ? ScalableBitmap(this, "thumb_right") : ScalableBitmap(this, "thumb_up"));
|
||||
m_bmp_thumb_lower = (style == wxSL_HORIZONTAL ? ScalableBitmap(this, "thumb_left") : ScalableBitmap(this, "thumb_down"));
|
||||
#else
|
||||
m_bmp_thumb_higher = (style == wxSL_HORIZONTAL ? ScalableBitmap(this, "right_half_circle.png") : ScalableBitmap(this, "thumb_up"));
|
||||
m_bmp_thumb_lower = (style == wxSL_HORIZONTAL ? ScalableBitmap(this, "left_half_circle.png" ) : ScalableBitmap(this, "thumb_down"));
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
m_thumb_size = m_bmp_thumb_lower.GetBmpSize();
|
||||
|
||||
m_bmp_add_tick_on = ScalableBitmap(this, "colorchange_add");
|
||||
|
|
@ -134,14 +124,13 @@ Control::Control( wxWindow *parent,
|
|||
m_line_pens = { &DARK_GREY_PEN, &GREY_PEN, &LIGHT_GREY_PEN };
|
||||
m_segm_pens = { &DARK_ORANGE_PEN, &ORANGE_PEN, &LIGHT_ORANGE_PEN };
|
||||
|
||||
const wxFont& font = GetFont();
|
||||
m_font = is_osx ? font.Smaller().Smaller() : font.Smaller();
|
||||
m_font = GetFont();
|
||||
this->SetMinSize(get_min_size());
|
||||
}
|
||||
|
||||
void Control::msw_rescale()
|
||||
{
|
||||
const wxFont& font = GUI::wxGetApp().normal_font();
|
||||
m_font = is_osx ? font.Smaller().Smaller() : font.Smaller();
|
||||
m_font = GUI::wxGetApp().normal_font();
|
||||
|
||||
m_bmp_thumb_higher.msw_rescale();
|
||||
m_bmp_thumb_lower .msw_rescale();
|
||||
|
|
@ -179,8 +168,7 @@ int Control::GetActiveValue() const
|
|||
|
||||
wxSize Control::get_min_size() const
|
||||
{
|
||||
const int min_side = GUI::wxGetApp().em_unit() * ( is_horizontal() ? (is_osx ? 8 : 6) : 10 );
|
||||
|
||||
const int min_side = GUI::wxGetApp().em_unit() * ( is_horizontal() ? 5 : 11 );
|
||||
return wxSize(min_side, min_side);
|
||||
}
|
||||
|
||||
|
|
@ -240,6 +228,12 @@ void Control::SetMaxValue(const int max_value)
|
|||
Update();
|
||||
}
|
||||
|
||||
void Control::SetSliderValues(const std::vector<double>& values)
|
||||
{
|
||||
m_values = values;
|
||||
m_ruler.count = std::count(m_values.begin(), m_values.end(), m_values.front());
|
||||
}
|
||||
|
||||
void Control::draw_scroll_line(wxDC& dc, const int lower_pos, const int higher_pos)
|
||||
{
|
||||
int width;
|
||||
|
|
@ -312,22 +306,14 @@ double Control::get_double_value(const SelectedSlider& selection)
|
|||
Info Control::GetTicksValues() const
|
||||
{
|
||||
Info custom_gcode_per_print_z;
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
std::vector<CustomGCode::Item>& values = custom_gcode_per_print_z.gcodes;
|
||||
#else
|
||||
std::vector<Item>& values = custom_gcode_per_print_z.gcodes;
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
|
||||
const int val_size = m_values.size();
|
||||
if (!m_values.empty())
|
||||
for (const TickCode& tick : m_ticks.ticks) {
|
||||
if (tick.tick > val_size)
|
||||
break;
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
values.emplace_back(CustomGCode::Item{ m_values[tick.tick], tick.type, tick.extruder, tick.color, tick.extra });
|
||||
#else
|
||||
values.emplace_back(Item{m_values[tick.tick], tick.type, tick.extruder, tick.color, tick.extra});
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
}
|
||||
|
||||
if (m_force_mode_apply)
|
||||
|
|
@ -346,11 +332,7 @@ void Control::SetTicksValues(const Info& custom_gcode_per_print_z)
|
|||
const bool was_empty = m_ticks.empty();
|
||||
|
||||
m_ticks.ticks.clear();
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
const std::vector<CustomGCode::Item>& heights = custom_gcode_per_print_z.gcodes;
|
||||
#else
|
||||
const std::vector<Item>& heights = custom_gcode_per_print_z.gcodes;
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
for (auto h : heights) {
|
||||
auto it = std::lower_bound(m_values.begin(), m_values.end(), h.print_z - epsilon());
|
||||
|
||||
|
|
@ -371,6 +353,24 @@ void Control::SetTicksValues(const Info& custom_gcode_per_print_z)
|
|||
Update();
|
||||
}
|
||||
|
||||
void Control::SetLayersTimes(const std::vector<float>& layers_times)
|
||||
{
|
||||
m_layers_times.clear();
|
||||
if (layers_times.empty())
|
||||
return;
|
||||
m_layers_times.resize(layers_times.size(), 0.0);
|
||||
m_layers_times[0] = layers_times[0];
|
||||
for (size_t i = 1; i < layers_times.size(); i++)
|
||||
m_layers_times[i] = m_layers_times[i - 1] + layers_times[i];
|
||||
}
|
||||
|
||||
void Control::SetLayersTimes(const std::vector<double>& layers_times)
|
||||
{
|
||||
m_layers_times = layers_times;
|
||||
for (size_t i = 1; i < m_layers_times.size(); i++)
|
||||
m_layers_times[i] += m_layers_times[i - 1];
|
||||
}
|
||||
|
||||
void Control::SetDrawMode(bool is_sla_print, bool is_sequential_print)
|
||||
{
|
||||
m_draw_mode = is_sla_print ? dmSlaPrint :
|
||||
|
|
@ -422,15 +422,11 @@ void Control::draw_focus_rect()
|
|||
|
||||
void Control::render()
|
||||
{
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
#ifdef _WIN32
|
||||
SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
|
||||
#else
|
||||
SetBackgroundColour(GetParent()->GetBackgroundColour());
|
||||
#endif // _WIN32
|
||||
#else
|
||||
SetBackgroundColour(GetParent()->GetBackgroundColour());
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
draw_focus_rect();
|
||||
|
||||
wxPaintDC dc(this);
|
||||
|
|
@ -443,6 +439,9 @@ void Control::render()
|
|||
// and only in a case of no-empty m_values
|
||||
draw_colored_band(dc);
|
||||
|
||||
if (m_extra_style & wxSL_AUTOTICKS)
|
||||
draw_ruler(dc);
|
||||
|
||||
if (!m_render_as_disabled) {
|
||||
// draw line
|
||||
draw_scroll_line(dc, lower_pos, higher_pos);
|
||||
|
|
@ -471,10 +470,8 @@ void Control::draw_action_icon(wxDC& dc, const wxPoint pt_beg, const wxPoint pt_
|
|||
{
|
||||
const int tick = m_selection == ssLower ? m_lower_value : m_higher_value;
|
||||
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
if (!m_enable_action_icon)
|
||||
return;
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
|
||||
// suppress add tick on first layer
|
||||
if (tick == 0)
|
||||
|
|
@ -560,7 +557,7 @@ void Control::draw_tick_on_mouse_position(wxDC& dc)
|
|||
}
|
||||
|
||||
tick = get_value_from_position(m_moving_pos);
|
||||
if (tick >= m_max_value || tick <= m_min_value || tick == m_higher_value || tick == m_lower_value)
|
||||
if (tick > m_max_value || tick < m_min_value || tick == m_higher_value || tick == m_lower_value)
|
||||
return;
|
||||
|
||||
wxCoord new_pos = get_position_from_value(tick);
|
||||
|
|
@ -569,9 +566,57 @@ void Control::draw_tick_on_mouse_position(wxDC& dc)
|
|||
//draw info line
|
||||
dc.SetPen(LIGHT_GREY_PEN);
|
||||
draw_ticks(dc, pos);
|
||||
|
||||
if (m_extra_style & wxSL_VALUE_LABEL) {
|
||||
wxColour old_clr = dc.GetTextForeground();
|
||||
dc.SetTextForeground(GREY_PEN.GetColour());
|
||||
draw_tick_text(dc, pos, tick, ltEstimatedTime, false);
|
||||
dc.SetTextForeground(old_clr);
|
||||
}
|
||||
}
|
||||
|
||||
wxString Control::get_label(int tick) const
|
||||
static std::string short_and_splitted_time(const std::string& time)
|
||||
{
|
||||
// Parse the dhms time format.
|
||||
int days = 0;
|
||||
int hours = 0;
|
||||
int minutes = 0;
|
||||
int seconds = 0;
|
||||
if (time.find('d') != std::string::npos)
|
||||
::sscanf(time.c_str(), "%dd %dh %dm %ds", &days, &hours, &minutes, &seconds);
|
||||
else if (time.find('h') != std::string::npos)
|
||||
::sscanf(time.c_str(), "%dh %dm %ds", &hours, &minutes, &seconds);
|
||||
else if (time.find('m') != std::string::npos)
|
||||
::sscanf(time.c_str(), "%dm %ds", &minutes, &seconds);
|
||||
else if (time.find('s') != std::string::npos)
|
||||
::sscanf(time.c_str(), "%ds", &seconds);
|
||||
|
||||
// Format the dhm time.
|
||||
char buffer[64];
|
||||
if (days > 0)
|
||||
::sprintf(buffer, "%dd%dh\n%dm", days, hours, minutes);
|
||||
else if (hours > 0) {
|
||||
if (hours < 10 && minutes < 10 && seconds < 10)
|
||||
::sprintf(buffer, "%dh%dm%ds", hours, minutes, seconds);
|
||||
else if (hours > 10 && minutes > 10 && seconds > 10)
|
||||
::sprintf(buffer, "%dh\n%dm\n%ds", hours, minutes, seconds);
|
||||
else if (minutes < 10 && seconds > 10 || minutes > 10 && seconds < 10)
|
||||
::sprintf(buffer, "%dh\n%dm%ds", hours, minutes, seconds);
|
||||
else
|
||||
::sprintf(buffer, "%dh%dm\n%ds", hours, minutes, seconds);
|
||||
}
|
||||
else if (minutes > 0) {
|
||||
if (minutes > 10 && seconds > 10)
|
||||
::sprintf(buffer, "%dm\n%ds", minutes, seconds);
|
||||
else
|
||||
::sprintf(buffer, "%dm%ds", minutes, seconds);
|
||||
}
|
||||
else
|
||||
::sprintf(buffer, "%ds", seconds);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
wxString Control::get_label(int tick, LabelType label_type/* = ltHeightWithLayer*/) const
|
||||
{
|
||||
const int value = tick;
|
||||
|
||||
|
|
@ -580,27 +625,28 @@ wxString Control::get_label(int tick) const
|
|||
if (value >= m_values.size())
|
||||
return "ErrVal";
|
||||
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
if (m_draw_mode == dmSequentialGCodeView)
|
||||
return wxString::Format("%d", static_cast<unsigned int>(m_values[value]));
|
||||
else {
|
||||
const wxString str = m_values.empty() ?
|
||||
if (label_type == ltEstimatedTime) {
|
||||
return (value < m_layers_times.size()) ? short_and_splitted_time(get_time_dhms(m_layers_times[value])) : "";
|
||||
}
|
||||
wxString str = m_values.empty() ?
|
||||
wxString::Format("%.*f", 2, m_label_koef * value) :
|
||||
wxString::Format("%.*f", 2, m_values[value]);
|
||||
return format_wxstr("%1%\n(%2%)", str, m_values.empty() ? value : value + 1);
|
||||
if (label_type == ltHeight)
|
||||
return str;
|
||||
if (label_type == ltHeightWithLayer)
|
||||
return format_wxstr("%1%\n(%2%)", str, m_values.empty() ? value : value + 1);
|
||||
}
|
||||
#else
|
||||
const wxString str = m_values.empty() ?
|
||||
wxNumberFormatter::ToString(m_label_koef * value, 2, wxNumberFormatter::Style_None) :
|
||||
wxNumberFormatter::ToString(m_values[value], 2, wxNumberFormatter::Style_None);
|
||||
return format_wxstr("%1%\n(%2%)", str, m_values.empty() ? value : value + 1);
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
|
||||
return wxEmptyString;
|
||||
}
|
||||
|
||||
void Control::draw_tick_text(wxDC& dc, const wxPoint& pos, int tick, bool right_side/*=true*/) const
|
||||
void Control::draw_tick_text(wxDC& dc, const wxPoint& pos, int tick, LabelType label_type/* = ltHeight*/, bool right_side/*=true*/) const
|
||||
{
|
||||
wxCoord text_width, text_height;
|
||||
const wxString label = get_label(tick);
|
||||
const wxString label = get_label(tick, label_type);
|
||||
dc.GetMultiLineTextExtent(label, &text_width, &text_height);
|
||||
wxPoint text_pos;
|
||||
if (right_side) {
|
||||
|
|
@ -615,9 +661,6 @@ void Control::draw_tick_text(wxDC& dc, const wxPoint& pos, int tick, bool right_
|
|||
}
|
||||
else
|
||||
text_pos = wxPoint(pos.x + m_thumb_size.x + 1, pos.y - 0.5 * text_height - 1);
|
||||
|
||||
// update text rectangle
|
||||
m_rect_lower_thumb_text = wxRect(text_pos, wxSize(text_width, text_height));
|
||||
}
|
||||
else {
|
||||
if (is_horizontal()) {
|
||||
|
|
@ -626,48 +669,24 @@ void Control::draw_tick_text(wxDC& dc, const wxPoint& pos, int tick, bool right_
|
|||
text_pos = wxPoint(xx, pos.y - m_thumb_size.x / 2 - text_height - 1);
|
||||
}
|
||||
else
|
||||
text_pos = wxPoint(pos.x - text_width - 1 - m_thumb_size.x, pos.y - 0.5 * text_height + 1);
|
||||
|
||||
// update text rectangle
|
||||
m_rect_higher_thumb_text = wxRect(text_pos, wxSize(text_width, text_height));
|
||||
text_pos = wxPoint(std::max(2, pos.x - text_width - 1 - m_thumb_size.x), pos.y - 0.5 * text_height + 1);
|
||||
}
|
||||
|
||||
dc.DrawText(label, text_pos);
|
||||
if (label_type == ltEstimatedTime)
|
||||
dc.DrawLabel(label, wxRect(text_pos, wxSize(text_width, text_height)), wxALIGN_RIGHT);
|
||||
else
|
||||
dc.DrawText(label, text_pos);
|
||||
}
|
||||
|
||||
void Control::draw_thumb_text(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection) const
|
||||
{
|
||||
draw_tick_text(dc, pos, selection == ssLower ? m_lower_value : m_higher_value, selection == ssLower);
|
||||
draw_tick_text(dc, pos, selection == ssLower ? m_lower_value : m_higher_value, ltHeightWithLayer, selection == ssLower);
|
||||
}
|
||||
|
||||
void Control::draw_thumb_item(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection)
|
||||
{
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
wxCoord x_draw = pos.x - int(0.5 * m_thumb_size.x);
|
||||
wxCoord y_draw = pos.y - int(0.5 * m_thumb_size.y);
|
||||
#else
|
||||
wxCoord x_draw, y_draw;
|
||||
if (selection == ssLower) {
|
||||
if (is_horizontal()) {
|
||||
x_draw = pos.x - m_thumb_size.x;
|
||||
y_draw = pos.y - int(0.5*m_thumb_size.y);
|
||||
}
|
||||
else {
|
||||
x_draw = pos.x - int(0.5*m_thumb_size.x);
|
||||
y_draw = pos.y - int(0.5*m_thumb_size.y);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (is_horizontal()) {
|
||||
x_draw = pos.x;
|
||||
y_draw = pos.y - int(0.5*m_thumb_size.y);
|
||||
}
|
||||
else {
|
||||
x_draw = pos.x - int(0.5*m_thumb_size.x);
|
||||
y_draw = pos.y - int(0.5*m_thumb_size.y);
|
||||
}
|
||||
}
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
dc.DrawBitmap(selection == ssLower ? m_bmp_thumb_lower.bmp() : m_bmp_thumb_higher.bmp(), x_draw, y_draw);
|
||||
|
||||
// Update thumb rect
|
||||
|
|
@ -715,6 +734,15 @@ void Control::draw_thumbs(wxDC& dc, const wxCoord& lower_pos, const wxCoord& hig
|
|||
draw_thumb_text(dc, pos_l, ssLower);
|
||||
}
|
||||
|
||||
void Control::draw_ticks_pair(wxDC& dc, wxCoord pos, wxCoord mid, int tick_len)
|
||||
{
|
||||
int mid_space = 9;
|
||||
is_horizontal() ? dc.DrawLine(pos, mid - (mid_space + tick_len), pos, mid - mid_space) :
|
||||
dc.DrawLine(mid - (mid_space + tick_len), pos, mid - mid_space, pos);
|
||||
is_horizontal() ? dc.DrawLine(pos, mid + (mid_space + tick_len), pos, mid + mid_space) :
|
||||
dc.DrawLine(mid + (mid_space + tick_len), pos, mid + mid_space, pos);
|
||||
};
|
||||
|
||||
void Control::draw_ticks(wxDC& dc)
|
||||
{
|
||||
if (m_draw_mode == dmSlaPrint)
|
||||
|
|
@ -726,11 +754,7 @@ void Control::draw_ticks(wxDC& dc)
|
|||
const wxCoord mid = is_horizontal() ? 0.5*height : 0.5*width;
|
||||
for (auto tick : m_ticks.ticks) {
|
||||
const wxCoord pos = get_position_from_value(tick.tick);
|
||||
|
||||
is_horizontal() ? dc.DrawLine(pos, mid-14, pos, mid-9) :
|
||||
dc.DrawLine(mid - 14, pos/* - 1*/, mid - 9, pos/* - 1*/);
|
||||
is_horizontal() ? dc.DrawLine(pos, mid+14, pos, mid+9) :
|
||||
dc.DrawLine(mid + 14, pos/* - 1*/, mid + 9, pos/* - 1*/);
|
||||
draw_ticks_pair(dc, pos, mid, 7);
|
||||
|
||||
// if current tick if focused, we should to use a specific "focused" icon
|
||||
bool focused_tick = m_moving_pos != wxDefaultPosition && tick.tick == get_tick_near_point(m_moving_pos);
|
||||
|
|
@ -828,15 +852,11 @@ void Control::draw_colored_band(wxDC& dc)
|
|||
|
||||
// don't color a band for MultiExtruder mode
|
||||
if (m_ticks.empty() || m_mode == MultiExtruder) {
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
#ifdef _WIN32
|
||||
draw_band(dc, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW), main_band);
|
||||
#else
|
||||
draw_band(dc, GetParent()->GetBackgroundColour(), main_band);
|
||||
#endif // _WIN32
|
||||
#else
|
||||
draw_band(dc, GetParent()->GetBackgroundColour(), main_band);
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -866,12 +886,133 @@ void Control::draw_colored_band(wxDC& dc)
|
|||
}
|
||||
}
|
||||
|
||||
void Control::Ruler::update(wxWindow* win, const std::vector<double>& values, double scroll_step)
|
||||
{
|
||||
int DPI = GUI::get_dpi_for_window(win);
|
||||
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 step = 0;
|
||||
auto end_it = count == 1 ? values.end() : values.begin() + lround(values.size() / count);
|
||||
|
||||
while (pow < 3) {
|
||||
int tick = 0;
|
||||
for (int istep : {1, 2, 5}) {
|
||||
double val = (double)istep * std::pow(10,pow);
|
||||
auto val_it = std::lower_bound(values.begin(), end_it, val - epsilon());
|
||||
|
||||
if (val_it == values.end())
|
||||
break;
|
||||
int tick = val_it - values.begin();
|
||||
|
||||
// find next tick with istep
|
||||
val *= 2;
|
||||
val_it = std::lower_bound(values.begin(), end_it, val - epsilon());
|
||||
// count of short ticks between ticks
|
||||
int short_ticks_cnt = val_it == values.end() ? tick : val_it - values.begin() - tick;
|
||||
|
||||
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;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (step > 0)
|
||||
break;
|
||||
pow++;
|
||||
}
|
||||
|
||||
long_step = step == 0 ? -1.0 : (double)step* std::pow(10, pow);
|
||||
}
|
||||
|
||||
void Control::draw_ruler(wxDC& dc)
|
||||
{
|
||||
m_ruler.update(this->GetParent(), m_values, get_scroll_step());
|
||||
|
||||
int height, width;
|
||||
get_size(&width, &height);
|
||||
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) {
|
||||
while (current_tick < max_tick) {
|
||||
wxCoord pos = get_position_from_value(lround(current_tick));
|
||||
draw_ticks_pair(dc, pos, mid, 2);
|
||||
current_tick += m_ruler.short_step;
|
||||
if (current_tick > m_max_value)
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
double short_tick;
|
||||
int tick = 0;
|
||||
double value = 0.0;
|
||||
int sequence = 0;
|
||||
|
||||
while (tick <= m_max_value) {
|
||||
value += m_ruler.long_step;
|
||||
if (value > m_values.back() && sequence < m_ruler.count) {
|
||||
value = m_ruler.long_step;
|
||||
for (tick; tick < m_values.size(); tick++)
|
||||
if (m_values[tick] < value)
|
||||
break;
|
||||
// short ticks from the last tick to the end of current sequence
|
||||
draw_short_ticks(dc, short_tick, tick);
|
||||
sequence++;
|
||||
}
|
||||
short_tick = tick;
|
||||
|
||||
for (tick; tick < m_values.size(); tick++) {
|
||||
if (m_values[tick] == value)
|
||||
break;
|
||||
if (m_values[tick] > value) {
|
||||
if (tick > 0)
|
||||
tick--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (tick > m_max_value)
|
||||
break;
|
||||
|
||||
wxCoord pos = get_position_from_value(tick);
|
||||
draw_ticks_pair(dc, pos, mid, 5);
|
||||
draw_tick_text(dc, wxPoint(mid, pos), tick);
|
||||
|
||||
draw_short_ticks(dc, short_tick, tick);
|
||||
|
||||
if (value == m_values.back() && sequence < m_ruler.count) {
|
||||
value = 0.0;
|
||||
sequence++;
|
||||
tick++;
|
||||
}
|
||||
}
|
||||
// short ticks from the last tick to the end
|
||||
draw_short_ticks(dc, short_tick, m_max_value);
|
||||
}
|
||||
|
||||
dc.SetTextForeground(old_clr);
|
||||
}
|
||||
|
||||
void Control::draw_one_layer_icon(wxDC& dc)
|
||||
{
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
if (m_draw_mode == dmSequentialGCodeView)
|
||||
return;
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
|
||||
const wxBitmap& icon = m_is_one_layer ?
|
||||
m_focus == fiOneLayerIcon ? m_bmp_one_layer_lock_off.bmp() : m_bmp_one_layer_lock_on.bmp() :
|
||||
|
|
@ -910,26 +1051,21 @@ void Control::draw_revert_icon(wxDC& dc)
|
|||
|
||||
void Control::draw_cog_icon(wxDC& dc)
|
||||
{
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
if (m_draw_mode == dmSequentialGCodeView)
|
||||
return;
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
|
||||
int width, height;
|
||||
get_size(&width, &height);
|
||||
|
||||
wxCoord x_draw, y_draw;
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
if (m_draw_mode == dmSequentialGCodeView) {
|
||||
is_horizontal() ? x_draw = width - 2 : x_draw = 0.5 * width - 0.5 * m_cog_icon_dim;
|
||||
is_horizontal() ? y_draw = 0.5 * height - 0.5 * m_cog_icon_dim : y_draw = height - 2;
|
||||
}
|
||||
else {
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
is_horizontal() ? x_draw = width - 2 : x_draw = width - m_cog_icon_dim - 2;
|
||||
is_horizontal() ? y_draw = height - m_cog_icon_dim - 2 : y_draw = height - 2;
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
}
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
|
||||
dc.DrawBitmap(m_bmp_cog.bmp(), x_draw, y_draw);
|
||||
|
||||
|
|
@ -1076,19 +1212,15 @@ wxString Control::get_tooltip(int tick/*=-1*/)
|
|||
if (m_focus == fiRevertIcon)
|
||||
return _L("Discard all custom changes");
|
||||
if (m_focus == fiCogIcon)
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
{
|
||||
if (m_draw_mode == dmSequentialGCodeView)
|
||||
return _L("Jump to move") + " (Shift + G)";
|
||||
else
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
return m_mode == MultiAsSingle ?
|
||||
GUI::from_u8((boost::format(_u8L("Jump to height %s or "
|
||||
"Set extruder sequence for the entire print")) % " (Shift + G)\n").str()) :
|
||||
_L("Jump to height") + " (Shift + G)";
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
GUI::from_u8((boost::format(_u8L("Jump to height %s Set ruler mode\n or "
|
||||
"Set extruder sequence for the entire print")) % " (Shift + G)\n").str()) :
|
||||
GUI::from_u8((boost::format(_u8L("Jump to height %s or Set ruler mode")) % " (Shift + G)\n").str());
|
||||
}
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
if (m_focus == fiColorBand)
|
||||
return m_mode != SingleExtruder ? "" :
|
||||
_L("Edit current color - Right click the colored slider segment");
|
||||
|
|
@ -1339,14 +1471,7 @@ void Control::OnLeftUp(wxMouseEvent& event)
|
|||
add_current_tick();
|
||||
break;
|
||||
case maCogIconClick :
|
||||
if (m_mode == MultiAsSingle && m_draw_mode == dmRegular)
|
||||
show_cog_icon_context_menu();
|
||||
else
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
jump_to_value();
|
||||
#else
|
||||
jump_to_print_z();
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
show_cog_icon_context_menu();
|
||||
break;
|
||||
case maOneLayerIconClick:
|
||||
switch_one_layer_mode();
|
||||
|
|
@ -1429,17 +1554,12 @@ void Control::OnWheel(wxMouseEvent& event)
|
|||
if (m_selection == ssLower && !is_lower_thumb_editable())
|
||||
m_selection = ssUndef;
|
||||
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
move_current_thumb((m_draw_mode == dmSequentialGCodeView) ? event.GetWheelRotation() < 0 : event.GetWheelRotation() > 0);
|
||||
#else
|
||||
move_current_thumb(event.GetWheelRotation() > 0);
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
}
|
||||
|
||||
void Control::OnKeyDown(wxKeyEvent &event)
|
||||
{
|
||||
const int key = event.GetKeyCode();
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
if (m_draw_mode != dmSequentialGCodeView && key == WXK_NUMPAD_ADD) {
|
||||
// OnChar() is called immediately after OnKeyDown(), which can cause call of add_tick() twice.
|
||||
// To avoid this case we should suppress second add_tick() call.
|
||||
|
|
@ -1454,26 +1574,8 @@ void Control::OnKeyDown(wxKeyEvent &event)
|
|||
}
|
||||
else if (m_draw_mode != dmSequentialGCodeView && event.GetKeyCode() == WXK_SHIFT)
|
||||
UseDefaultColors(false);
|
||||
#else
|
||||
if (key == WXK_NUMPAD_ADD) {
|
||||
// OnChar() is called immediately after OnKeyDown(), which can cause call of add_tick() twice.
|
||||
// To avoid this case we should suppress second add_tick() call.
|
||||
m_ticks.suppress_plus(true);
|
||||
add_current_tick(true);
|
||||
}
|
||||
else if (key == 390 || key == WXK_DELETE || key == WXK_BACK) {
|
||||
// OnChar() is called immediately after OnKeyDown(), which can cause call of delete_tick() twice.
|
||||
// To avoid this case we should suppress second delete_tick() call.
|
||||
m_ticks.suppress_minus(true);
|
||||
delete_current_tick();
|
||||
}
|
||||
else if (event.GetKeyCode() == WXK_SHIFT)
|
||||
UseDefaultColors(false);
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
else if (is_horizontal()) {
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
if (m_is_focused) {
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
if (key == WXK_LEFT || key == WXK_RIGHT)
|
||||
move_current_thumb(key == WXK_LEFT);
|
||||
else if (key == WXK_UP || key == WXK_DOWN) {
|
||||
|
|
@ -1483,14 +1585,10 @@ void Control::OnKeyDown(wxKeyEvent &event)
|
|||
m_selection = ssLower;
|
||||
Refresh();
|
||||
}
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
}
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
}
|
||||
else {
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
if (m_is_focused) {
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
if (key == WXK_LEFT || key == WXK_RIGHT) {
|
||||
if (key == WXK_LEFT)
|
||||
m_selection = ssHigher;
|
||||
|
|
@ -1500,9 +1598,7 @@ void Control::OnKeyDown(wxKeyEvent &event)
|
|||
}
|
||||
else if (key == WXK_UP || key == WXK_DOWN)
|
||||
move_current_thumb(key == WXK_UP);
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
}
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
}
|
||||
|
||||
event.Skip(); // !Needed to have EVT_CHAR generated as well
|
||||
|
|
@ -1523,10 +1619,7 @@ void Control::OnKeyUp(wxKeyEvent &event)
|
|||
void Control::OnChar(wxKeyEvent& event)
|
||||
{
|
||||
const int key = event.GetKeyCode();
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
if (m_draw_mode != dmSequentialGCodeView)
|
||||
{
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
if (m_draw_mode != dmSequentialGCodeView) {
|
||||
if (key == '+' && !m_ticks.suppressed_plus()) {
|
||||
add_current_tick(true);
|
||||
m_ticks.suppress_plus(false);
|
||||
|
|
@ -1535,15 +1628,9 @@ void Control::OnChar(wxKeyEvent& event)
|
|||
delete_current_tick();
|
||||
m_ticks.suppress_minus(false);
|
||||
}
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
}
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
if (key == 'G')
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
jump_to_value();
|
||||
#else
|
||||
jump_to_print_z();
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
}
|
||||
|
||||
void Control::OnRightDown(wxMouseEvent& event)
|
||||
|
|
@ -1730,14 +1817,29 @@ void Control::show_cog_icon_context_menu()
|
|||
wxMenu menu;
|
||||
|
||||
append_menu_item(&menu, wxID_ANY, _L("Jump to height") + " (Shift+G)", "",
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
[this](wxCommandEvent&) { jump_to_value(); }, "", & menu);
|
||||
#else
|
||||
[this](wxCommandEvent&) { jump_to_print_z(); }, "", &menu);
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
[this](wxCommandEvent&) { jump_to_value(); }, "", & menu);
|
||||
|
||||
append_menu_item(&menu, wxID_ANY, _L("Set extruder sequence for the entire print"), "",
|
||||
[this](wxCommandEvent&) { edit_extruder_sequence(); }, "", &menu);
|
||||
wxMenu* ruler_mode_menu = new wxMenu();
|
||||
if (ruler_mode_menu) {
|
||||
append_menu_check_item(ruler_mode_menu, wxID_ANY, _L("None"), _L("Supprese show the ruler"),
|
||||
[this](wxCommandEvent&) { if (m_extra_style != 0) m_extra_style = 0; }, ruler_mode_menu,
|
||||
[]() { return true; }, [this]() { return m_extra_style == 0; }, GUI::wxGetApp().plater());
|
||||
|
||||
append_menu_check_item(ruler_mode_menu, wxID_ANY, _L("Show object height"), _L("Show object height on the ruler"),
|
||||
[this](wxCommandEvent&) { m_extra_style & wxSL_AUTOTICKS ? m_extra_style &= wxSL_AUTOTICKS : m_extra_style |= wxSL_AUTOTICKS; }, ruler_mode_menu,
|
||||
[]() { return true; }, [this]() { return m_extra_style & wxSL_AUTOTICKS; }, GUI::wxGetApp().plater());
|
||||
|
||||
append_menu_check_item(ruler_mode_menu, wxID_ANY, _L("Show estimated print time"), _L("Show estimated print time on the ruler"),
|
||||
[this](wxCommandEvent&) { m_extra_style & wxSL_VALUE_LABEL ? m_extra_style &= wxSL_VALUE_LABEL : m_extra_style |= wxSL_VALUE_LABEL; }, ruler_mode_menu,
|
||||
[]() { return true; }, [this]() { return m_extra_style & wxSL_VALUE_LABEL; }, GUI::wxGetApp().plater());
|
||||
|
||||
append_submenu(&menu, ruler_mode_menu, wxID_ANY, _L("Ruler mode"), _L("Set ruler mode"), "",
|
||||
[this]() { return true; }, this);
|
||||
}
|
||||
|
||||
if (m_mode == MultiAsSingle && m_draw_mode == dmRegular)
|
||||
append_menu_item(&menu, wxID_ANY, _L("Set extruder sequence for the entire print"), "",
|
||||
[this](wxCommandEvent&) { edit_extruder_sequence(); }, "", &menu);
|
||||
|
||||
GUI::wxGetApp().plater()->PopupMenu(&menu);
|
||||
}
|
||||
|
|
@ -1852,21 +1954,11 @@ static std::string get_pause_print_msg(const std::string& msg_in, double height)
|
|||
return into_u8(dlg.GetValue());
|
||||
}
|
||||
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
static double get_value_to_jump(double active_value, double min_z, double max_z, DrawMode mode)
|
||||
#else
|
||||
static double get_print_z_to_jump(double active_print_z, double min_z, double max_z)
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
{
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
wxString msg_text = (mode == dmSequentialGCodeView) ? _L("Enter the move you want to jump to") + ":" : _L("Enter the height you want to jump to") + ":";
|
||||
wxString msg_header = (mode == dmSequentialGCodeView) ? _L("Jump to move") : _L("Jump to height");
|
||||
wxString msg_in = GUI::double_to_string(active_value);
|
||||
#else
|
||||
wxString msg_text = _L("Enter the height you want to jump to") + ":";
|
||||
wxString msg_header = _L("Jump to height");
|
||||
wxString msg_in = GUI::double_to_string(active_print_z);
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
|
||||
// get custom gcode
|
||||
wxTextEntryDialog dlg(nullptr, msg_text, msg_header, msg_in, wxTextEntryDialogStyle);
|
||||
|
|
@ -2068,7 +2160,6 @@ void Control::edit_extruder_sequence()
|
|||
post_ticks_changed_event(ToolChange);
|
||||
}
|
||||
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
void Control::jump_to_value()
|
||||
{
|
||||
double value = get_value_to_jump(m_values[m_selection == ssLower ? m_lower_value : m_higher_value],
|
||||
|
|
@ -2084,23 +2175,6 @@ void Control::jump_to_value()
|
|||
else
|
||||
SetHigherValue(tick_value);
|
||||
}
|
||||
#else
|
||||
void Control::jump_to_print_z()
|
||||
{
|
||||
double print_z = get_print_z_to_jump(m_values[m_selection == ssLower ? m_lower_value : m_higher_value],
|
||||
m_values[m_min_value], m_values[m_max_value]);
|
||||
if (print_z < 0)
|
||||
return;
|
||||
|
||||
auto it = std::lower_bound(m_values.begin(), m_values.end(), print_z - epsilon());
|
||||
int tick_value = it - m_values.begin();
|
||||
|
||||
if (m_selection == ssLower)
|
||||
SetLowerValue(tick_value);
|
||||
else
|
||||
SetHigherValue(tick_value);
|
||||
}
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
|
||||
void Control::post_ticks_changed_event(Type type /*= Custom*/)
|
||||
{
|
||||
|
|
@ -2169,11 +2243,7 @@ bool Control::check_ticks_changed_event(Type type)
|
|||
std::string TickCodeInfo::get_color_for_tick(TickCode tick, Type type, const int extruder)
|
||||
{
|
||||
if (mode == SingleExtruder && type == ColorChange && m_use_default_colors) {
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
const std::vector<std::string>& colors = ColorPrintColors::get();
|
||||
#else
|
||||
const std::vector<std::string>& colors = GCodePreviewData::ColorPrintColors();
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
if (ticks.empty())
|
||||
return colors[0];
|
||||
m_default_color_idx++;
|
||||
|
|
|
|||
|
|
@ -4,9 +4,6 @@
|
|||
#include "libslic3r/CustomGCode.hpp"
|
||||
#include "wxExtensions.hpp"
|
||||
|
||||
#if !ENABLE_GCODE_VIEWER
|
||||
#include <wx/wx.h>
|
||||
#endif // !ENABLE_GCODE_VIEWER
|
||||
#include <wx/window.h>
|
||||
#include <wx/control.h>
|
||||
#include <wx/dc.h>
|
||||
|
|
@ -79,9 +76,14 @@ enum DrawMode
|
|||
dmRegular,
|
||||
dmSlaPrint,
|
||||
dmSequentialFffPrint,
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
dmSequentialGCodeView,
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
};
|
||||
|
||||
enum LabelType
|
||||
{
|
||||
ltHeightWithLayer,
|
||||
ltHeight,
|
||||
ltEstimatedTime,
|
||||
};
|
||||
|
||||
struct TickCode
|
||||
|
|
@ -212,16 +214,16 @@ public:
|
|||
|
||||
void SetMaxValue(const int max_value);
|
||||
void SetKoefForLabels(const double koef) { m_label_koef = koef; }
|
||||
void SetSliderValues(const std::vector<double>& values) { m_values = values; }
|
||||
void SetSliderValues(const std::vector<double>& values);
|
||||
void ChangeOneLayerLock();
|
||||
|
||||
Info GetTicksValues() const;
|
||||
void SetTicksValues(const Info &custom_gcode_per_print_z);
|
||||
Info GetTicksValues() const;
|
||||
void SetTicksValues(const Info &custom_gcode_per_print_z);
|
||||
void SetLayersTimes(const std::vector<float>& layers_times);
|
||||
void SetLayersTimes(const std::vector<double>& layers_times);
|
||||
|
||||
void SetDrawMode(bool is_sla_print, bool is_sequential_print);
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
void SetDrawMode(DrawMode mode) { m_draw_mode = mode; }
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
|
||||
void SetManipulationMode(Mode mode) { m_mode = mode; }
|
||||
Mode GetManipulationMode() const { return m_mode; }
|
||||
|
|
@ -261,12 +263,8 @@ public:
|
|||
void discard_all_thicks();
|
||||
void move_current_thumb_to_pos(wxPoint pos);
|
||||
void edit_extruder_sequence();
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
void jump_to_value();
|
||||
void enable_action_icon(bool enable) { m_enable_action_icon = enable; }
|
||||
#else
|
||||
void jump_to_print_z();
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
void show_add_context_menu();
|
||||
void show_edit_context_menu();
|
||||
void show_cog_icon_context_menu();
|
||||
|
|
@ -281,15 +279,17 @@ protected:
|
|||
void draw_scroll_line(wxDC& dc, const int lower_pos, const int higher_pos);
|
||||
void draw_thumb(wxDC& dc, const wxCoord& pos_coord, const SelectedSlider& selection);
|
||||
void draw_thumbs(wxDC& dc, const wxCoord& lower_pos, const wxCoord& higher_pos);
|
||||
void draw_ticks_pair(wxDC& dc, wxCoord pos, wxCoord mid, int tick_len);
|
||||
void draw_ticks(wxDC& dc);
|
||||
void draw_colored_band(wxDC& dc);
|
||||
void draw_ruler(wxDC& dc);
|
||||
void draw_one_layer_icon(wxDC& dc);
|
||||
void draw_revert_icon(wxDC& dc);
|
||||
void draw_cog_icon(wxDC &dc);
|
||||
void draw_thumb_item(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection);
|
||||
void draw_info_line_with_icon(wxDC& dc, const wxPoint& pos, SelectedSlider selection);
|
||||
void draw_tick_on_mouse_position(wxDC &dc);
|
||||
void draw_tick_text(wxDC& dc, const wxPoint& pos, int tick, bool right_side = true) const;
|
||||
void draw_tick_text(wxDC& dc, const wxPoint& pos, int tick, LabelType label_type = ltHeight, bool right_side = true) const;
|
||||
void draw_thumb_text(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection) const;
|
||||
|
||||
void update_thumb_rect(const wxCoord begin_x, const wxCoord begin_y, const SelectedSlider& selection);
|
||||
|
|
@ -306,7 +306,7 @@ private:
|
|||
int get_tick_near_point(const wxPoint& pt);
|
||||
|
||||
double get_scroll_step();
|
||||
wxString get_label(int tick) const;
|
||||
wxString get_label(int tick, LabelType label_type = ltHeightWithLayer) const;
|
||||
void get_lower_and_higher_position(int& lower_pos, int& higher_pos);
|
||||
int get_value_from_position(const wxCoord x, const wxCoord y);
|
||||
int get_value_from_position(const wxPoint pos) { return get_value_from_position(pos.x, pos.y); }
|
||||
|
|
@ -360,9 +360,7 @@ private:
|
|||
bool m_is_one_layer = false;
|
||||
bool m_is_focused = false;
|
||||
bool m_force_mode_apply = true;
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
bool m_enable_action_icon = true;
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
|
||||
DrawMode m_draw_mode = dmRegular;
|
||||
|
||||
|
|
@ -387,10 +385,12 @@ private:
|
|||
int m_revert_icon_dim;
|
||||
int m_cog_icon_dim;
|
||||
long m_style;
|
||||
long m_extra_style;
|
||||
float m_label_koef = 1.0;
|
||||
|
||||
std::vector<double> m_values;
|
||||
TickCodeInfo m_ticks;
|
||||
std::vector<double> m_layers_times;
|
||||
|
||||
std::vector<std::string> m_extruder_colors;
|
||||
|
||||
|
|
@ -407,6 +407,15 @@ private:
|
|||
|
||||
std::vector<wxPen*> m_line_pens;
|
||||
std::vector<wxPen*> m_segm_pens;
|
||||
|
||||
struct Ruler {
|
||||
double long_step;
|
||||
double short_step;
|
||||
int count { 1 }; // > 1 for sequential print
|
||||
|
||||
void update(wxWindow* win, const std::vector<double>& values, double scroll_step);
|
||||
bool is_ok() { return long_step > 0 && short_step > 0; }
|
||||
} m_ruler;
|
||||
};
|
||||
|
||||
} // DoubleSlider;
|
||||
|
|
|
|||
|
|
@ -316,7 +316,7 @@ wxWindow* BitmapChoiceRenderer::CreateEditorCtrl(wxWindow* parent, wxRect labelR
|
|||
|
||||
bool BitmapChoiceRenderer::GetValueFromEditorCtrl(wxWindow* ctrl, wxVariant& value)
|
||||
{
|
||||
wxBitmapComboBox* c = (wxBitmapComboBox*)ctrl;
|
||||
wxBitmapComboBox* c = static_cast<wxBitmapComboBox*>(ctrl);
|
||||
int selection = c->GetSelection();
|
||||
if (selection < 0)
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -14,13 +14,14 @@
|
|||
#include "GUI_App.hpp"
|
||||
#include "I18N.hpp"
|
||||
#include "OptionsGroup.hpp"
|
||||
#include "MainFrame.hpp"
|
||||
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
ExtruderSequenceDialog::ExtruderSequenceDialog(const DoubleSlider::ExtrudersSequence& sequence)
|
||||
: DPIDialog((wxWindow*)wxGetApp().mainframe, wxID_ANY, wxString(SLIC3R_APP_NAME) + " - " + _(L("Set extruder sequence")),
|
||||
: DPIDialog(static_cast<wxWindow*>(wxGetApp().mainframe), wxID_ANY, wxString(SLIC3R_APP_NAME) + " - " + _(L("Set extruder sequence")),
|
||||
wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER),
|
||||
m_sequence(sequence)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -262,6 +262,11 @@ void Field::get_value_by_opt_type(wxString& str, const bool check_value/* = true
|
|||
double val = 0.;
|
||||
// Replace the first occurence of comma in decimal number.
|
||||
str.Replace(",", ".", false);
|
||||
|
||||
// remove space and "mm" substring, if any exists
|
||||
str.Replace(" ", "", true);
|
||||
str.Replace("m", "", true);
|
||||
|
||||
if (!str.ToCDouble(&val))
|
||||
{
|
||||
if (!check_value) {
|
||||
|
|
@ -280,13 +285,15 @@ void Field::get_value_by_opt_type(wxString& str, const bool check_value/* = true
|
|||
break;
|
||||
}
|
||||
|
||||
bool infill_anchors = m_opt.opt_key == "infill_anchor" || m_opt.opt_key == "infill_anchor_max";
|
||||
|
||||
const std::string sidetext = m_opt.sidetext.rfind("mm/s") != std::string::npos ? "mm/s" : "mm";
|
||||
const wxString stVal = double_to_string(val, 2);
|
||||
const wxString msg_text = from_u8((boost::format(_utf8(L("Do you mean %s%% instead of %s %s?\n"
|
||||
"Select YES if you want to change this value to %s%%, \n"
|
||||
"or NO if you are sure that %s %s is a correct value."))) % stVal % stVal % sidetext % stVal % stVal % sidetext).str());
|
||||
wxMessageDialog dialog(m_parent, msg_text, _(L("Parameter validation")) + ": " + m_opt_id , wxICON_WARNING | wxYES | wxNO);
|
||||
if (dialog.ShowModal() == wxID_YES) {
|
||||
if ((!infill_anchors || val > 100) && dialog.ShowModal() == wxID_YES) {
|
||||
set_value(from_u8((boost::format("%s%%") % stVal).str()), false/*true*/);
|
||||
str += "%%";
|
||||
}
|
||||
|
|
@ -861,8 +868,16 @@ void Choice::BUILD() {
|
|||
temp->SetItemBitmap(0, empty_bmp);
|
||||
#endif
|
||||
|
||||
// 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_MOUSEWHEEL, [this](wxMouseEvent& e) {
|
||||
if (m_suppress_scroll && !m_is_dropped)
|
||||
e.StopPropagation();
|
||||
else
|
||||
e.Skip();
|
||||
});
|
||||
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](wxCommandEvent&) { on_change_field(); }, temp->GetId());
|
||||
|
||||
if (m_is_editable) {
|
||||
temp->Bind(wxEVT_KILL_FOCUS, ([this](wxEvent& e) {
|
||||
|
|
@ -872,12 +887,18 @@ void Choice::BUILD() {
|
|||
return;
|
||||
}
|
||||
|
||||
double old_val = !m_value.empty() ? boost::any_cast<double>(m_value) : -99999;
|
||||
if (is_defined_input_value<choice_ctrl>(window, m_opt.type)) {
|
||||
if (fabs(old_val - boost::any_cast<double>(get_value())) <= 0.0001)
|
||||
return;
|
||||
else
|
||||
on_change_field();
|
||||
if (m_opt.type == coFloatOrPercent) {
|
||||
std::string old_val = !m_value.empty() ? boost::any_cast<std::string>(m_value) : "";
|
||||
if (old_val == boost::any_cast<std::string>(get_value()))
|
||||
return;
|
||||
}
|
||||
else {
|
||||
double old_val = !m_value.empty() ? boost::any_cast<double>(m_value) : -99999;
|
||||
if (fabs(old_val - boost::any_cast<double>(get_value())) <= 0.0001)
|
||||
return;
|
||||
}
|
||||
on_change_field();
|
||||
}
|
||||
else
|
||||
on_kill_focus();
|
||||
|
|
@ -887,6 +908,11 @@ void Choice::BUILD() {
|
|||
temp->SetToolTip(get_tooltip_text(temp->GetValue()));
|
||||
}
|
||||
|
||||
void Choice::suppress_scroll()
|
||||
{
|
||||
m_suppress_scroll = true;
|
||||
}
|
||||
|
||||
void Choice::set_selection()
|
||||
{
|
||||
/* To prevent earlier control updating under OSX set m_disable_change_event to true
|
||||
|
|
@ -898,59 +924,42 @@ void Choice::set_selection()
|
|||
|
||||
choice_ctrl* field = dynamic_cast<choice_ctrl*>(window);
|
||||
switch (m_opt.type) {
|
||||
case coFloat:
|
||||
case coPercent: {
|
||||
double val = m_opt.default_value->getFloat();
|
||||
text_value = val - int(val) == 0 ? wxString::Format(_T("%i"), int(val)) : wxNumberFormatter::ToString(val, 1);
|
||||
size_t idx = 0;
|
||||
for (auto el : m_opt.enum_values)
|
||||
{
|
||||
if (el == text_value)
|
||||
break;
|
||||
++idx;
|
||||
}
|
||||
// if (m_opt.type == coPercent) text_value += "%";
|
||||
idx == m_opt.enum_values.size() ?
|
||||
field->SetValue(text_value) :
|
||||
field->SetSelection(idx);
|
||||
break;
|
||||
}
|
||||
case coEnum:{
|
||||
int id_value = m_opt.get_default_value<ConfigOptionEnum<SeamPosition>>()->value; //!!
|
||||
field->SetSelection(id_value);
|
||||
break;
|
||||
}
|
||||
case coFloat:
|
||||
case coPercent: {
|
||||
double val = m_opt.default_value->getFloat();
|
||||
text_value = val - int(val) == 0 ? wxString::Format(_T("%i"), int(val)) : wxNumberFormatter::ToString(val, 1);
|
||||
break;
|
||||
}
|
||||
case coInt:{
|
||||
int val = m_opt.default_value->getInt(); //!!
|
||||
text_value = wxString::Format(_T("%i"), int(val));
|
||||
size_t idx = 0;
|
||||
for (auto el : m_opt.enum_values)
|
||||
{
|
||||
if (el == text_value)
|
||||
break;
|
||||
++idx;
|
||||
}
|
||||
idx == m_opt.enum_values.size() ?
|
||||
field->SetValue(text_value) :
|
||||
field->SetSelection(idx);
|
||||
text_value = wxString::Format(_T("%i"), int(m_opt.default_value->getInt()));
|
||||
break;
|
||||
}
|
||||
case coStrings:{
|
||||
text_value = m_opt.get_default_value<ConfigOptionStrings>()->get_at(m_opt_idx);
|
||||
break;
|
||||
}
|
||||
case coFloatOrPercent: {
|
||||
text_value = double_to_string(m_opt.default_value->getFloat());
|
||||
if (m_opt.get_default_value<ConfigOptionFloatOrPercent>()->percent)
|
||||
text_value += "%";
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
|
||||
size_t idx = 0;
|
||||
for (auto el : m_opt.enum_values)
|
||||
{
|
||||
if (!text_value.IsEmpty()) {
|
||||
int idx = 0;
|
||||
for (auto el : m_opt.enum_values) {
|
||||
if (el == text_value)
|
||||
break;
|
||||
++idx;
|
||||
}
|
||||
idx == m_opt.enum_values.size() ?
|
||||
field->SetValue(text_value) :
|
||||
field->SetSelection(idx);
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
idx == m_opt.enum_values.size() ? field->SetValue(text_value) : field->SetSelection(idx);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -984,6 +993,7 @@ void Choice::set_value(const boost::any& value, bool change_event)
|
|||
case coInt:
|
||||
case coFloat:
|
||||
case coPercent:
|
||||
case coFloatOrPercent:
|
||||
case coString:
|
||||
case coStrings: {
|
||||
wxString text_value;
|
||||
|
|
@ -992,13 +1002,14 @@ void Choice::set_value(const boost::any& value, bool change_event)
|
|||
else
|
||||
text_value = boost::any_cast<wxString>(value);
|
||||
size_t idx = 0;
|
||||
for (auto el : m_opt.enum_values)
|
||||
const std::vector<std::string>& enums = m_opt.enum_values.empty() ? m_opt.enum_labels : m_opt.enum_values;
|
||||
for (auto el : enums)
|
||||
{
|
||||
if (el == text_value)
|
||||
break;
|
||||
++idx;
|
||||
}
|
||||
if (idx == m_opt.enum_values.size()) {
|
||||
if (idx == enums.size()) {
|
||||
// For editable Combobox under OSX is needed to set selection to -1 explicitly,
|
||||
// otherwise selection doesn't be changed
|
||||
field->SetSelection(-1);
|
||||
|
|
@ -1137,7 +1148,9 @@ boost::any& Choice::get_value()
|
|||
(ret_str != m_opt.enum_values[ret_enum] && ret_str != _(m_opt.enum_labels[ret_enum])))
|
||||
// modifies ret_string!
|
||||
get_value_by_opt_type(ret_str);
|
||||
else
|
||||
else if (m_opt.type == coFloatOrPercent)
|
||||
m_value = m_opt.enum_values[ret_enum];
|
||||
else
|
||||
m_value = atof(m_opt.enum_values[ret_enum].c_str());
|
||||
}
|
||||
else
|
||||
|
|
|
|||
|
|
@ -38,39 +38,6 @@ using t_back_to_init = std::function<void(const std::string&)>;
|
|||
|
||||
wxString double_to_string(double const value, const int max_precision = 4);
|
||||
|
||||
class RevertButton : public ScalableButton
|
||||
{
|
||||
bool hidden = false; // never show button if it's hidden ones
|
||||
public:
|
||||
// RevertButton() {}
|
||||
// RevertButton(wxWindow* parent, wxWindowID id, const wxString& label = wxEmptyString,
|
||||
// const wxPoint& pos = wxDefaultPosition,
|
||||
// const wxSize& size = wxDefaultSize, long style = 0,
|
||||
// const wxValidator& validator = wxDefaultValidator,
|
||||
// const wxString& name = wxTextCtrlNameStr)
|
||||
// {
|
||||
// this->Create(parent, id, label, pos, size, style, validator, name);
|
||||
// }
|
||||
RevertButton(
|
||||
wxWindow *parent,
|
||||
const std::string& icon_name = ""
|
||||
) :
|
||||
ScalableButton(parent, wxID_ANY, icon_name) {}
|
||||
|
||||
// overridden from wxWindow base class
|
||||
virtual bool
|
||||
AcceptsFocusFromKeyboard() const { return false; }
|
||||
|
||||
void set_as_hidden() {
|
||||
Hide();
|
||||
hidden = true;
|
||||
}
|
||||
|
||||
virtual bool Show(bool show = true) override {
|
||||
return wxButton::Show(hidden ? false : show);
|
||||
}
|
||||
};
|
||||
|
||||
class Field {
|
||||
protected:
|
||||
// factory function to defer and enforce creation of derived type.
|
||||
|
|
@ -283,14 +250,14 @@ public:
|
|||
void propagate_value();
|
||||
wxWindow* window {nullptr};
|
||||
|
||||
virtual void set_value(const std::string& value, bool change_event = false) {
|
||||
void set_value(const std::string& value, bool change_event = false) {
|
||||
m_disable_change_event = !change_event;
|
||||
dynamic_cast<wxTextCtrl*>(window)->SetValue(wxString(value));
|
||||
m_disable_change_event = false;
|
||||
}
|
||||
virtual void set_value(const boost::any& value, bool change_event = false) override;
|
||||
virtual void set_last_meaningful_value() override;
|
||||
virtual void set_na_value() override;
|
||||
void set_value(const boost::any& value, bool change_event = false) override;
|
||||
void set_last_meaningful_value() override;
|
||||
void set_na_value() override;
|
||||
|
||||
boost::any& get_value() override;
|
||||
|
||||
|
|
@ -385,11 +352,14 @@ public:
|
|||
/* Under OSX: wxBitmapComboBox->GetWindowStyle() returns some weard value,
|
||||
* 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_value(const std::string& value, bool change_event = false);
|
||||
void set_value(const boost::any& value, bool change_event = false);
|
||||
void set_value(const boost::any& value, bool change_event = false) override;
|
||||
void set_values(const std::vector<std::string> &values);
|
||||
void set_values(const wxArrayString &values);
|
||||
boost::any& get_value() override;
|
||||
|
|
@ -399,6 +369,8 @@ public:
|
|||
void enable() override ;//{ dynamic_cast<wxBitmapComboBox*>(window)->Enable(); };
|
||||
void disable() override;//{ dynamic_cast<wxBitmapComboBox*>(window)->Disable(); };
|
||||
wxWindow* getWindow() override { return window; }
|
||||
|
||||
void suppress_scroll();
|
||||
};
|
||||
|
||||
class ColourPicker : public Field {
|
||||
|
|
@ -443,7 +415,7 @@ public:
|
|||
// Propagate value from field to the OptionGroupe and Config after kill_focus/ENTER
|
||||
void propagate_value(wxTextCtrl* win);
|
||||
void set_value(const Vec2d& value, bool change_event = false);
|
||||
void set_value(const boost::any& value, bool change_event = false);
|
||||
void set_value(const boost::any& value, bool change_event = false) override;
|
||||
boost::any& get_value() override;
|
||||
|
||||
void msw_rescale() override;
|
||||
|
|
@ -473,7 +445,7 @@ public:
|
|||
dynamic_cast<wxStaticText*>(window)->SetLabel(wxString::FromUTF8(value.data()));
|
||||
m_disable_change_event = false;
|
||||
}
|
||||
void set_value(const boost::any& value, bool change_event = false) {
|
||||
void set_value(const boost::any& value, bool change_event = false) override {
|
||||
m_disable_change_event = !change_event;
|
||||
dynamic_cast<wxStaticText*>(window)->SetLabel(boost::any_cast<wxString>(value));
|
||||
m_disable_change_event = false;
|
||||
|
|
@ -504,7 +476,7 @@ public:
|
|||
void BUILD() override;
|
||||
|
||||
void set_value(const int value, bool change_event = false);
|
||||
void set_value(const boost::any& value, bool change_event = false);
|
||||
void set_value(const boost::any& value, bool change_event = false) override;
|
||||
boost::any& get_value() override;
|
||||
|
||||
void enable() override {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
#include "libslic3r/libslic3r.h"
|
||||
#include "GCodeViewer.hpp"
|
||||
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
#include "libslic3r/Print.hpp"
|
||||
#include "libslic3r/Geometry.hpp"
|
||||
#include "libslic3r/Model.hpp"
|
||||
|
|
@ -272,6 +271,10 @@ const std::vector<GCodeViewer::Color> GCodeViewer::Travel_Colors {{
|
|||
{ 0.505f, 0.064f, 0.028f } // Retract
|
||||
}};
|
||||
|
||||
#if ENABLE_SHOW_WIPE_MOVES
|
||||
const GCodeViewer::Color GCodeViewer::Wipe_Color = { 1.0f, 1.0f, 0.0f };
|
||||
#endif // ENABLE_SHOW_WIPE_MOVES
|
||||
|
||||
const std::vector<GCodeViewer::Color> GCodeViewer::Range_Colors {{
|
||||
{ 0.043f, 0.173f, 0.478f }, // bluish
|
||||
{ 0.075f, 0.349f, 0.522f },
|
||||
|
|
@ -370,6 +373,10 @@ void GCodeViewer::refresh(const GCodeProcessor::Result& gcode_result, const std:
|
|||
// update tool colors
|
||||
m_tool_colors = decode_colors(str_tool_colors);
|
||||
|
||||
// ensure at least one (default) color is defined
|
||||
if (m_tool_colors.empty())
|
||||
m_tool_colors.push_back(decode_color("#FF8000"));
|
||||
|
||||
// update ranges for coloring / legend
|
||||
m_extrusions.reset_ranges();
|
||||
for (size_t i = 0; i < m_moves_count; ++i) {
|
||||
|
|
@ -456,6 +463,9 @@ void GCodeViewer::render() const
|
|||
buffer.shader = wxGetApp().is_glsl_version_greater_or_equal_to(1, 20) ? "options_120" : "options_110";
|
||||
break;
|
||||
}
|
||||
#if ENABLE_SHOW_WIPE_MOVES
|
||||
case EMoveType::Wipe:
|
||||
#endif // ENABLE_SHOW_WIPE_MOVES
|
||||
case EMoveType::Extrude: {
|
||||
buffer.shader = "gouraud_light";
|
||||
break;
|
||||
|
|
@ -569,6 +579,9 @@ unsigned int GCodeViewer::get_options_visibility_flags() const
|
|||
|
||||
unsigned int flags = 0;
|
||||
flags = set_flag(flags, static_cast<unsigned int>(Preview::OptionType::Travel), is_toolpath_move_type_visible(EMoveType::Travel));
|
||||
#if ENABLE_SHOW_WIPE_MOVES
|
||||
flags = set_flag(flags, static_cast<unsigned int>(Preview::OptionType::Wipe), is_toolpath_move_type_visible(EMoveType::Wipe));
|
||||
#endif // ENABLE_SHOW_WIPE_MOVES
|
||||
flags = set_flag(flags, static_cast<unsigned int>(Preview::OptionType::Retractions), is_toolpath_move_type_visible(EMoveType::Retract));
|
||||
flags = set_flag(flags, static_cast<unsigned int>(Preview::OptionType::Unretractions), is_toolpath_move_type_visible(EMoveType::Unretract));
|
||||
flags = set_flag(flags, static_cast<unsigned int>(Preview::OptionType::ToolChanges), is_toolpath_move_type_visible(EMoveType::Tool_change));
|
||||
|
|
@ -588,6 +601,9 @@ void GCodeViewer::set_options_visibility_from_flags(unsigned int flags)
|
|||
};
|
||||
|
||||
set_toolpath_move_type_visible(EMoveType::Travel, is_flag_set(static_cast<unsigned int>(Preview::OptionType::Travel)));
|
||||
#if ENABLE_SHOW_WIPE_MOVES
|
||||
set_toolpath_move_type_visible(EMoveType::Wipe, is_flag_set(static_cast<unsigned int>(Preview::OptionType::Wipe)));
|
||||
#endif // ENABLE_SHOW_WIPE_MOVES
|
||||
set_toolpath_move_type_visible(EMoveType::Retract, is_flag_set(static_cast<unsigned int>(Preview::OptionType::Retractions)));
|
||||
set_toolpath_move_type_visible(EMoveType::Unretract, is_flag_set(static_cast<unsigned int>(Preview::OptionType::Unretractions)));
|
||||
set_toolpath_move_type_visible(EMoveType::Tool_change, is_flag_set(static_cast<unsigned int>(Preview::OptionType::ToolChanges)));
|
||||
|
|
@ -925,6 +941,9 @@ void GCodeViewer::init()
|
|||
buffer.vertices.format = VBuffer::EFormat::Position;
|
||||
break;
|
||||
}
|
||||
#if ENABLE_SHOW_WIPE_MOVES
|
||||
case EMoveType::Wipe:
|
||||
#endif // ENABLE_SHOW_WIPE_MOVES
|
||||
case EMoveType::Extrude:
|
||||
{
|
||||
buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::Triangle;
|
||||
|
|
@ -1197,6 +1216,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
|||
prev_up = up;
|
||||
prev_length = length;
|
||||
};
|
||||
|
||||
auto add_indices_as_solid = [](const GCodeProcessor::MoveVertex& prev, const GCodeProcessor::MoveVertex& curr, TBuffer& buffer,
|
||||
size_t& buffer_vertices_size, unsigned int index_buffer_id, IndexBuffer& buffer_indices, size_t move_id) {
|
||||
static Vec3f prev_dir;
|
||||
|
|
@ -1339,6 +1359,9 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
|||
// the data are deleted as soon as they are sent to the gpu.
|
||||
std::vector<std::vector<float>> vertices(m_buffers.size());
|
||||
std::vector<MultiIndexBuffer> indices(m_buffers.size());
|
||||
#if ENABLE_SHOW_OPTION_POINT_LAYERS
|
||||
std::vector<float> options_zs;
|
||||
#endif // ENABLE_SHOW_OPTION_POINT_LAYERS
|
||||
|
||||
// toolpaths data -> extract vertices from result
|
||||
for (size_t i = 0; i < m_moves_count; ++i) {
|
||||
|
|
@ -1363,24 +1386,39 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
|||
|
||||
switch (buffer.render_primitive_type)
|
||||
{
|
||||
case TBuffer::ERenderPrimitiveType::Point:
|
||||
{
|
||||
case TBuffer::ERenderPrimitiveType::Point: {
|
||||
add_vertices_as_point(curr, buffer_vertices);
|
||||
break;
|
||||
}
|
||||
case TBuffer::ERenderPrimitiveType::Line:
|
||||
{
|
||||
case TBuffer::ERenderPrimitiveType::Line: {
|
||||
add_vertices_as_line(prev, curr, buffer, buffer_vertices);
|
||||
break;
|
||||
}
|
||||
case TBuffer::ERenderPrimitiveType::Triangle:
|
||||
{
|
||||
case TBuffer::ERenderPrimitiveType::Triangle: {
|
||||
add_vertices_as_solid(prev, curr, buffer, buffer_vertices, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#if ENABLE_SHOW_OPTION_POINT_LAYERS
|
||||
EMoveType type = buffer_type(id);
|
||||
if (type == EMoveType::Pause_Print || type == EMoveType::Custom_GCode) {
|
||||
const float* const last_z = options_zs.empty() ? nullptr : &options_zs.back();
|
||||
float z = static_cast<double>(curr.position[2]);
|
||||
if (last_z == nullptr || z < *last_z - EPSILON || *last_z + EPSILON < z)
|
||||
options_zs.emplace_back(curr.position[2]);
|
||||
}
|
||||
#endif // ENABLE_SHOW_OPTION_POINT_LAYERS
|
||||
}
|
||||
|
||||
#if ENABLE_SHOW_WIPE_MOVES
|
||||
// move the wipe toolpaths half height up to render them on proper position
|
||||
std::vector<float>& wipe_vertices = vertices[buffer_id(EMoveType::Wipe)];
|
||||
for (size_t i = 2; i < wipe_vertices.size(); i += 3) {
|
||||
wipe_vertices[i] += 0.5f * GCodeProcessor::Wipe_Height;
|
||||
}
|
||||
#endif // ENABLE_SHOW_WIPE_MOVES
|
||||
|
||||
log_memory_usage("Loaded G-code generated vertex buffers, ", vertices, indices);
|
||||
|
||||
// toolpaths data -> send vertices data to gpu
|
||||
|
|
@ -1414,7 +1452,11 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
|||
buffer.paths.clear();
|
||||
}
|
||||
// variable used to keep track of the current size (in vertices) of the vertex buffer
|
||||
#if ENABLE_SHOW_WIPE_MOVES
|
||||
std::vector<size_t> curr_buffer_vertices_size(m_buffers.size(), 0);
|
||||
#else
|
||||
size_t curr_buffer_vertices_size = 0;
|
||||
#endif // ENABLE_SHOW_WIPE_MOVES
|
||||
for (size_t i = 0; i < m_moves_count; ++i) {
|
||||
// skip first vertex
|
||||
if (i == 0)
|
||||
|
|
@ -1442,7 +1484,11 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
|||
// create another index buffer, and move the current path indices into it
|
||||
if (buffer_indices.back().size() >= THRESHOLD - static_cast<size_t>(buffer.indices_per_segment())) {
|
||||
buffer_indices.push_back(IndexBuffer());
|
||||
#if ENABLE_SHOW_WIPE_MOVES
|
||||
if (buffer.render_primitive_type != TBuffer::ERenderPrimitiveType::Point) {
|
||||
#else
|
||||
if (curr.type == EMoveType::Extrude || curr.type == EMoveType::Travel) {
|
||||
#endif // ENABLE_SHOW_WIPE_MOVES
|
||||
if (!(prev.type != curr.type || !buffer.paths.back().matches(curr))) {
|
||||
Path& last_path = buffer.paths.back();
|
||||
size_t delta_id = last_path.last.i_id - last_path.first.i_id;
|
||||
|
|
@ -1464,19 +1510,20 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
|||
|
||||
switch (buffer.render_primitive_type)
|
||||
{
|
||||
case TBuffer::ERenderPrimitiveType::Point:
|
||||
{
|
||||
case TBuffer::ERenderPrimitiveType::Point: {
|
||||
add_indices_as_point(curr, buffer, static_cast<unsigned int>(buffer_indices.size()) - 1, buffer_indices.back(), i);
|
||||
break;
|
||||
}
|
||||
case TBuffer::ERenderPrimitiveType::Line:
|
||||
{
|
||||
case TBuffer::ERenderPrimitiveType::Line: {
|
||||
add_indices_as_line(prev, curr, buffer, static_cast<unsigned int>(buffer_indices.size()) - 1, buffer_indices.back(), i);
|
||||
break;
|
||||
}
|
||||
case TBuffer::ERenderPrimitiveType::Triangle:
|
||||
{
|
||||
case TBuffer::ERenderPrimitiveType::Triangle: {
|
||||
#if ENABLE_SHOW_WIPE_MOVES
|
||||
add_indices_as_solid(prev, curr, buffer, curr_buffer_vertices_size[id], static_cast<unsigned int>(buffer_indices.size()) - 1, buffer_indices.back(), i);
|
||||
#else
|
||||
add_indices_as_solid(prev, curr, buffer, curr_buffer_vertices_size, static_cast<unsigned int>(buffer_indices.size()) - 1, buffer_indices.back(), i);
|
||||
#endif // ENABLE_SHOW_WIPE_MOVES
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -1521,6 +1568,13 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
|||
for (size_t i = 0; i < travel_buffer_indices.size(); ++i) {
|
||||
m_statistics.travel_segments_count = travel_buffer_indices[i].size() / m_buffers[travel_buffer_id].indices_per_segment();
|
||||
}
|
||||
#if ENABLE_SHOW_WIPE_MOVES
|
||||
unsigned int wipe_buffer_id = buffer_id(EMoveType::Wipe);
|
||||
const MultiIndexBuffer& wipe_buffer_indices = indices[wipe_buffer_id];
|
||||
for (size_t i = 0; i < wipe_buffer_indices.size(); ++i) {
|
||||
m_statistics.wipe_segments_count = wipe_buffer_indices[i].size() / m_buffers[wipe_buffer_id].indices_per_segment();
|
||||
}
|
||||
#endif // ENABLE_SHOW_WIPE_MOVES
|
||||
unsigned int extrude_buffer_id = buffer_id(EMoveType::Extrude);
|
||||
const MultiIndexBuffer& extrude_buffer_indices = indices[extrude_buffer_id];
|
||||
for (size_t i = 0; i < extrude_buffer_indices.size(); ++i) {
|
||||
|
|
@ -1558,9 +1612,20 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
|||
}
|
||||
|
||||
// set layers z range
|
||||
if (!m_layers.empty()) {
|
||||
if (!m_layers.empty())
|
||||
m_layers_z_range = { 0, static_cast<unsigned int>(m_layers.size() - 1) };
|
||||
|
||||
#if ENABLE_SHOW_OPTION_POINT_LAYERS
|
||||
// change color of paths whose layer contains option points
|
||||
if (!options_zs.empty()) {
|
||||
TBuffer& extrude_buffer = m_buffers[buffer_id(EMoveType::Extrude)];
|
||||
for (Path& path : extrude_buffer.paths) {
|
||||
float z = path.first.position[2];
|
||||
if (std::find_if(options_zs.begin(), options_zs.end(), [z](float f) { return f - EPSILON <= z && z <= f + EPSILON; }) != options_zs.end())
|
||||
path.cp_color_id = 255 - path.cp_color_id;
|
||||
}
|
||||
}
|
||||
#endif // ENABLE_SHOW_OPTION_POINT_LAYERS
|
||||
|
||||
// roles -> remove duplicates
|
||||
std::sort(m_roles.begin(), m_roles.end());
|
||||
|
|
@ -1657,9 +1722,25 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool
|
|||
case EViewType::FanSpeed: { color = m_extrusions.ranges.fan_speed.get_color_at(path.fan_speed); break; }
|
||||
case EViewType::VolumetricRate: { color = m_extrusions.ranges.volumetric_rate.get_color_at(path.volumetric_rate); break; }
|
||||
case EViewType::Tool: { color = m_tool_colors[path.extruder_id]; break; }
|
||||
#if ENABLE_SHOW_OPTION_POINT_LAYERS
|
||||
case EViewType::ColorPrint: {
|
||||
if (path.cp_color_id >= static_cast<unsigned char>(m_tool_colors.size())) {
|
||||
color = { 0.5f, 0.5f, 0.5f };
|
||||
// // complementary color
|
||||
// color = m_tool_colors[255 - path.cp_color_id];
|
||||
// color = { 1.0f - color[0], 1.0f - color[1], 1.0f - color[2] };
|
||||
}
|
||||
else
|
||||
color = m_tool_colors[path.cp_color_id];
|
||||
|
||||
break;
|
||||
}
|
||||
#else
|
||||
case EViewType::ColorPrint: { color = m_tool_colors[path.cp_color_id]; break; }
|
||||
#endif // ENABLE_SHOW_OPTION_POINT_LAYERS
|
||||
default: { color = { 1.0f, 1.0f, 1.0f }; break; }
|
||||
}
|
||||
|
||||
return color;
|
||||
};
|
||||
|
||||
|
|
@ -1830,6 +1911,9 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool
|
|||
|
||||
break;
|
||||
}
|
||||
#if ENABLE_SHOW_WIPE_MOVES
|
||||
case EMoveType::Wipe: { color = Wipe_Color; break; }
|
||||
#endif // ENABLE_SHOW_WIPE_MOVES
|
||||
default: { color = { 0.0f, 0.0f, 0.0f }; break; }
|
||||
}
|
||||
|
||||
|
|
@ -2562,6 +2646,28 @@ void GCodeViewer::render_legend() const
|
|||
}
|
||||
}
|
||||
|
||||
#if ENABLE_SHOW_WIPE_MOVES
|
||||
// wipe paths section
|
||||
if (m_buffers[buffer_id(EMoveType::Wipe)].visible) {
|
||||
switch (m_view_type)
|
||||
{
|
||||
case EViewType::Feedrate:
|
||||
case EViewType::Tool:
|
||||
case EViewType::ColorPrint: { break; }
|
||||
default: {
|
||||
// title
|
||||
ImGui::Spacing();
|
||||
imgui.title(_u8L("Wipe"));
|
||||
|
||||
// items
|
||||
append_item(EItemType::Line, Wipe_Color, _u8L("Wipe"));
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // ENABLE_SHOW_WIPE_MOVES
|
||||
|
||||
auto any_option_available = [this]() {
|
||||
auto available = [this](EMoveType type) {
|
||||
const TBuffer& buffer = m_buffers[buffer_id(type)];
|
||||
|
|
@ -2640,7 +2746,9 @@ void GCodeViewer::render_legend() const
|
|||
}
|
||||
if (!m_settings_ids.filament.empty()) {
|
||||
for (unsigned char i : m_extruder_ids) {
|
||||
imgui.text(_u8L("Filament") + " " + std::to_string(i + 1) + ":");
|
||||
std::string txt = _u8L("Filament");
|
||||
txt += (m_extruder_ids.size() == 1) ? ":" : " " + std::to_string(i + 1);
|
||||
imgui.text(txt);
|
||||
ImGui::SameLine(offset);
|
||||
imgui.text(m_settings_ids.filament[i]);
|
||||
}
|
||||
|
|
@ -2675,7 +2783,7 @@ void GCodeViewer::render_legend() const
|
|||
ImGui::SameLine();
|
||||
imgui.text(short_time(get_time_dhms(time_mode.time)));
|
||||
|
||||
auto show_mode_button = [this, &imgui](const std::string& label, PrintEstimatedTimeStatistics::ETimeMode mode) {
|
||||
auto show_mode_button = [this, &imgui](const wxString& label, PrintEstimatedTimeStatistics::ETimeMode mode) {
|
||||
bool show = false;
|
||||
for (size_t i = 0; i < m_time_statistics.modes.size(); ++i) {
|
||||
if (i != static_cast<size_t>(mode) &&
|
||||
|
|
@ -2695,11 +2803,11 @@ void GCodeViewer::render_legend() const
|
|||
|
||||
switch (m_time_estimate_mode) {
|
||||
case PrintEstimatedTimeStatistics::ETimeMode::Normal: {
|
||||
show_mode_button(_u8L("Show stealth mode"), PrintEstimatedTimeStatistics::ETimeMode::Stealth);
|
||||
show_mode_button(_L("Show stealth mode"), PrintEstimatedTimeStatistics::ETimeMode::Stealth);
|
||||
break;
|
||||
}
|
||||
case PrintEstimatedTimeStatistics::ETimeMode::Stealth: {
|
||||
show_mode_button(_u8L("Show normal mode"), PrintEstimatedTimeStatistics::ETimeMode::Normal);
|
||||
show_mode_button(_L("Show normal mode"), PrintEstimatedTimeStatistics::ETimeMode::Normal);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -2790,6 +2898,9 @@ void GCodeViewer::render_statistics() const
|
|||
|
||||
if (ImGui::CollapsingHeader("Other")) {
|
||||
add_counter(std::string("Travel segments count:"), m_statistics.travel_segments_count);
|
||||
#if ENABLE_SHOW_WIPE_MOVES
|
||||
add_counter(std::string("Wipe segments count:"), m_statistics.wipe_segments_count);
|
||||
#endif // ENABLE_SHOW_WIPE_MOVES
|
||||
add_counter(std::string("Extrude segments count:"), m_statistics.extrude_segments_count);
|
||||
add_counter(std::string("Max vertices in vertex buffer:"), m_statistics.max_vertices_in_vertex_buffer);
|
||||
add_counter(std::string("Max indices in index buffer:"), m_statistics.max_indices_in_index_buffer);
|
||||
|
|
@ -2826,4 +2937,3 @@ void GCodeViewer::log_memory_used(const std::string& label, long long additional
|
|||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
#ifndef slic3r_GCodeViewer_hpp_
|
||||
#define slic3r_GCodeViewer_hpp_
|
||||
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
#include "3DScene.hpp"
|
||||
#include "libslic3r/GCode/GCodeProcessor.hpp"
|
||||
#include "GLModel.hpp"
|
||||
|
|
@ -24,6 +23,9 @@ class GCodeViewer
|
|||
static const std::vector<Color> Extrusion_Role_Colors;
|
||||
static const std::vector<Color> Options_Colors;
|
||||
static const std::vector<Color> Travel_Colors;
|
||||
#if ENABLE_SHOW_WIPE_MOVES
|
||||
static const Color Wipe_Color;
|
||||
#endif // ENABLE_SHOW_WIPE_MOVES
|
||||
static const std::vector<Color> Range_Colors;
|
||||
|
||||
enum class EOptionsColors : unsigned char
|
||||
|
|
@ -327,6 +329,9 @@ class GCodeViewer
|
|||
long long render_paths_size{ 0 };
|
||||
// other
|
||||
long long travel_segments_count{ 0 };
|
||||
#if ENABLE_SHOW_WIPE_MOVES
|
||||
long long wipe_segments_count{ 0 };
|
||||
#endif // ENABLE_SHOW_WIPE_MOVES
|
||||
long long extrude_segments_count{ 0 };
|
||||
long long max_vertices_in_vertex_buffer{ 0 };
|
||||
long long max_indices_in_index_buffer{ 0 };
|
||||
|
|
@ -361,6 +366,9 @@ class GCodeViewer
|
|||
|
||||
void reset_others() {
|
||||
travel_segments_count = 0;
|
||||
#if ENABLE_SHOW_WIPE_MOVES
|
||||
wipe_segments_count = 0;
|
||||
#endif // ENABLE_SHOW_WIPE_MOVES
|
||||
extrude_segments_count = 0;
|
||||
max_vertices_in_vertex_buffer = 0;
|
||||
max_indices_in_index_buffer = 0;
|
||||
|
|
@ -513,7 +521,5 @@ private:
|
|||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
|
||||
#endif // slic3r_GCodeViewer_hpp_
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -12,10 +12,8 @@
|
|||
#include "GUI_ObjectLayers.hpp"
|
||||
#include "GLSelectionRectangle.hpp"
|
||||
#include "MeshUtils.hpp"
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
#include "libslic3r/GCode/GCodeProcessor.hpp"
|
||||
#include "GCodeViewer.hpp"
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
|
||||
#include "libslic3r/Slicing.hpp"
|
||||
|
||||
|
|
@ -39,9 +37,6 @@ namespace Slic3r {
|
|||
|
||||
struct Camera;
|
||||
class BackgroundSlicingProcess;
|
||||
#if !ENABLE_GCODE_VIEWER
|
||||
class GCodePreviewData;
|
||||
#endif // !ENABLE_GCODE_VIEWER
|
||||
struct ThumbnailData;
|
||||
class ModelObject;
|
||||
class ModelInstance;
|
||||
|
|
@ -108,11 +103,7 @@ wxDECLARE_EVENT(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED, SimpleEvent);
|
|||
wxDECLARE_EVENT(EVT_GLCANVAS_UPDATE_BED_SHAPE, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_TAB, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_RESETGIZMOS, SimpleEvent);
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_MOVE_LAYERS_SLIDER, wxKeyEvent);
|
||||
#else
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_MOVE_DOUBLE_SLIDER, wxKeyEvent);
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_EDIT_COLOR_CHANGE, wxKeyEvent);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_JUMP_TO, wxKeyEvent);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_UNDO, SimpleEvent);
|
||||
|
|
@ -127,37 +118,6 @@ class GLCanvas3D
|
|||
{
|
||||
static const double DefaultCameraZoomToBoxMarginFactor;
|
||||
|
||||
public:
|
||||
#if !ENABLE_GCODE_VIEWER
|
||||
struct GCodePreviewVolumeIndex
|
||||
{
|
||||
enum EType
|
||||
{
|
||||
Extrusion,
|
||||
Travel,
|
||||
Retraction,
|
||||
Unretraction,
|
||||
Shell,
|
||||
Num_Geometry_Types
|
||||
};
|
||||
|
||||
struct FirstVolume
|
||||
{
|
||||
EType type;
|
||||
unsigned int flag;
|
||||
// Index of the first volume in a GLVolumeCollection.
|
||||
unsigned int id;
|
||||
|
||||
FirstVolume(EType type, unsigned int flag, unsigned int id) : type(type), flag(flag), id(id) {}
|
||||
};
|
||||
|
||||
std::vector<FirstVolume> first_volumes;
|
||||
|
||||
void reset() { first_volumes.clear(); }
|
||||
};
|
||||
#endif // !ENABLE_GCODE_VIEWER
|
||||
|
||||
private:
|
||||
class LayersEditing
|
||||
{
|
||||
public:
|
||||
|
|
@ -355,35 +315,6 @@ private:
|
|||
bool generate(const std::string& msg, const GLCanvas3D& canvas, bool compress, bool red_colored = false);
|
||||
};
|
||||
|
||||
#if !ENABLE_GCODE_VIEWER
|
||||
class LegendTexture : public GUI::GLTexture
|
||||
{
|
||||
static const int Px_Title_Offset = 5;
|
||||
static const int Px_Text_Offset = 5;
|
||||
static const int Px_Square = 20;
|
||||
static const int Px_Square_Contour = 1;
|
||||
static const int Px_Border = Px_Square / 2;
|
||||
static const unsigned char Squares_Border_Color[3];
|
||||
static const unsigned char Default_Background_Color[3];
|
||||
static const unsigned char Error_Background_Color[3];
|
||||
static const unsigned char Opacity;
|
||||
|
||||
int m_original_width;
|
||||
int m_original_height;
|
||||
|
||||
public:
|
||||
LegendTexture();
|
||||
void fill_color_print_legend_items(const GLCanvas3D& canvas,
|
||||
const std::vector<float>& colors_in,
|
||||
std::vector<float>& colors,
|
||||
std::vector<std::string>& cp_legend_items);
|
||||
|
||||
bool generate(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors, const GLCanvas3D& canvas, bool compress);
|
||||
|
||||
void render(const GLCanvas3D& canvas) const;
|
||||
};
|
||||
#endif // !ENABLE_GCODE_VIEWER
|
||||
|
||||
#if ENABLE_RENDER_STATISTICS
|
||||
struct RenderStats
|
||||
{
|
||||
|
|
@ -450,6 +381,13 @@ public:
|
|||
Cross
|
||||
};
|
||||
|
||||
struct ArrangeSettings
|
||||
{
|
||||
float distance = 6.;
|
||||
float accuracy = 0.65f;
|
||||
bool enable_rotation = false;
|
||||
};
|
||||
|
||||
private:
|
||||
wxGLCanvas* m_canvas;
|
||||
wxGLContext* m_context;
|
||||
|
|
@ -457,9 +395,6 @@ private:
|
|||
std::unique_ptr<RetinaHelper> m_retina_helper;
|
||||
#endif
|
||||
bool m_in_render;
|
||||
#if !ENABLE_GCODE_VIEWER
|
||||
LegendTexture m_legend_texture;
|
||||
#endif // !ENABLE_GCODE_VIEWER
|
||||
WarningTexture m_warning_texture;
|
||||
wxTimer m_timer;
|
||||
LayersEditing m_layers_editing;
|
||||
|
|
@ -478,9 +413,7 @@ private:
|
|||
bool m_event_handlers_bound{ false };
|
||||
|
||||
mutable GLVolumeCollection m_volumes;
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
GCodeViewer m_gcode_viewer;
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
|
||||
Selection m_selection;
|
||||
const DynamicPrintConfig* m_config;
|
||||
|
|
@ -492,9 +425,6 @@ private:
|
|||
bool m_initialized;
|
||||
bool m_apply_zoom_to_volumes_filter;
|
||||
mutable std::vector<int> m_hover_volume_idxs;
|
||||
#if !ENABLE_GCODE_VIEWER
|
||||
bool m_legend_texture_enabled;
|
||||
#endif // !ENABLE_GCODE_VIEWER
|
||||
bool m_picking_enabled;
|
||||
bool m_moving_enabled;
|
||||
bool m_dynamic_background_enabled;
|
||||
|
|
@ -512,10 +442,6 @@ private:
|
|||
|
||||
bool m_reload_delayed;
|
||||
|
||||
#if !ENABLE_GCODE_VIEWER
|
||||
GCodePreviewVolumeIndex m_gcode_preview_volume_index;
|
||||
#endif // !ENABLE_GCODE_VIEWER
|
||||
|
||||
#if ENABLE_RENDER_PICKING_PASS
|
||||
bool m_show_picking_texture;
|
||||
#endif // ENABLE_RENDER_PICKING_PASS
|
||||
|
|
@ -533,6 +459,8 @@ private:
|
|||
mutable bool m_tooltip_enabled{ true };
|
||||
Slope m_slope;
|
||||
|
||||
ArrangeSettings m_arrange_settings;
|
||||
|
||||
public:
|
||||
explicit GLCanvas3D(wxGLCanvas* canvas);
|
||||
~GLCanvas3D();
|
||||
|
|
@ -554,11 +482,9 @@ public:
|
|||
void reset_volumes();
|
||||
int check_volumes_outside_state() const;
|
||||
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
void reset_gcode_toolpaths() { m_gcode_viewer.reset(); }
|
||||
const GCodeViewer::SequentialView& get_gcode_sequential_view() const { return m_gcode_viewer.get_sequential_view(); }
|
||||
void update_gcode_sequential_view_current(unsigned int first, unsigned int last) { m_gcode_viewer.update_sequential_view_current(first, last); }
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
|
||||
void toggle_sla_auxiliaries_visibility(bool visible, const ModelObject* mo = nullptr, int instance_idx = -1);
|
||||
void toggle_model_objects_visibility(bool visible, const ModelObject* mo = nullptr, int instance_idx = -1);
|
||||
|
|
@ -622,9 +548,7 @@ public:
|
|||
void zoom_to_bed();
|
||||
void zoom_to_volumes();
|
||||
void zoom_to_selection();
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
void zoom_to_gcode();
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
void select_view(const std::string& direction);
|
||||
|
||||
void update_volumes_colors_by_extruder();
|
||||
|
|
@ -641,7 +565,6 @@ public:
|
|||
void delete_selected();
|
||||
void ensure_on_bed(unsigned int object_idx);
|
||||
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
bool is_gcode_legend_enabled() const { return m_gcode_viewer.is_legend_enabled(); }
|
||||
GCodeViewer::EViewType get_gcode_view_type() const { return m_gcode_viewer.get_view_type(); }
|
||||
const std::vector<double>& get_gcode_layers_zs() const;
|
||||
|
|
@ -653,9 +576,6 @@ public:
|
|||
void set_toolpath_view_type(GCodeViewer::EViewType type);
|
||||
void set_volumes_z_range(const std::array<double, 2>& range);
|
||||
void set_toolpaths_z_range(const std::array<unsigned int, 2>& range);
|
||||
#else
|
||||
std::vector<double> get_current_print_zs(bool active_only) const;
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
void set_toolpaths_range(double low, double high);
|
||||
|
||||
std::vector<int> load_object(const ModelObject& model_object, int obj_idx, std::vector<int> instance_idxs);
|
||||
|
|
@ -665,14 +585,10 @@ public:
|
|||
|
||||
void reload_scene(bool refresh_immediately, bool force_full_scene_refresh = false);
|
||||
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
void load_gcode_preview(const GCodeProcessor::Result& gcode_result);
|
||||
void refresh_gcode_preview(const GCodeProcessor::Result& gcode_result, const std::vector<std::string>& str_tool_colors);
|
||||
void set_gcode_view_preview_type(GCodeViewer::EViewType type) { return m_gcode_viewer.set_view_type(type); }
|
||||
GCodeViewer::EViewType get_gcode_view_preview_type() const { return m_gcode_viewer.get_view_type(); }
|
||||
#else
|
||||
void load_gcode_preview(const GCodePreviewData& preview_data, const std::vector<std::string>& str_tool_colors);
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
void load_sla_preview();
|
||||
void load_preview(const std::vector<std::string>& str_tool_colors, const std::vector<CustomGCode::Item>& color_print_values);
|
||||
void bind_event_handlers();
|
||||
|
|
@ -691,10 +607,6 @@ public:
|
|||
Size get_canvas_size() const;
|
||||
Vec2d get_local_mouse_position() const;
|
||||
|
||||
#if !ENABLE_GCODE_VIEWER
|
||||
void reset_legend_texture();
|
||||
#endif // !ENABLE_GCODE_VIEWER
|
||||
|
||||
void set_tooltip(const std::string& tooltip) const;
|
||||
|
||||
// the following methods add a snapshot to the undo/redo stack, unless the given string is empty
|
||||
|
|
@ -768,6 +680,8 @@ public:
|
|||
void use_slope(bool use) { m_slope.use(use); }
|
||||
void set_slope_normal_angle(float angle_in_deg) { m_slope.set_normal_angle(angle_in_deg); }
|
||||
|
||||
const ArrangeSettings& get_arrange_settings() const { return m_arrange_settings; }
|
||||
|
||||
private:
|
||||
bool _is_shown_on_screen() const;
|
||||
|
||||
|
|
@ -792,9 +706,7 @@ private:
|
|||
void _render_background() const;
|
||||
void _render_bed(bool bottom, bool show_axes) const;
|
||||
void _render_objects() const;
|
||||
#if ENABLE_GCODE_VIEWER
|
||||
void _render_gcode() const;
|
||||
#endif // ENABLE_GCODE_VIEWER
|
||||
void _render_selection() const;
|
||||
#if ENABLE_RENDER_SELECTION_CENTER
|
||||
void _render_selection_center() const;
|
||||
|
|
@ -802,9 +714,6 @@ private:
|
|||
void _check_and_update_toolbar_icon_scale() const;
|
||||
void _render_overlays() const;
|
||||
void _render_warning_texture() const;
|
||||
#if !ENABLE_GCODE_VIEWER
|
||||
void _render_legend_texture() const;
|
||||
#endif // !ENABLE_GCODE_VIEWER
|
||||
void _render_volumes_for_picking() const;
|
||||
void _render_current_gizmo() const;
|
||||
void _render_gizmos_overlay() const;
|
||||
|
|
@ -819,6 +728,7 @@ private:
|
|||
void _render_selection_sidebar_hints() const;
|
||||
bool _render_undo_redo_stack(const bool is_undo, float pos_x) const;
|
||||
bool _render_search_list(float pos_x) const;
|
||||
bool _render_arrange_menu(float pos_x);
|
||||
void _render_thumbnail_internal(ThumbnailData& thumbnail_data, bool printable_only, bool parts_only, bool show_bed, bool transparent_background) const;
|
||||
// render thumbnail using an off-screen framebuffer
|
||||
void _render_thumbnail_framebuffer(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, bool printable_only, bool parts_only, bool show_bed, bool transparent_background) const;
|
||||
|
|
@ -852,29 +762,12 @@ private:
|
|||
// Create 3D thick extrusion lines for wipe tower extrusions
|
||||
void _load_wipe_tower_toolpaths(const std::vector<std::string>& str_tool_colors);
|
||||
|
||||
#if !ENABLE_GCODE_VIEWER
|
||||
// generates gcode extrusion paths geometry
|
||||
void _load_gcode_extrusion_paths(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors);
|
||||
// generates gcode travel paths geometry
|
||||
void _load_gcode_travel_paths(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors);
|
||||
// generates objects and wipe tower geometry
|
||||
void _load_fff_shells();
|
||||
#endif // !ENABLE_GCODE_VIEWER
|
||||
// Load SLA objects and support structures for objects, for which the slaposSliceSupports step has been finished.
|
||||
void _load_sla_shells();
|
||||
#if !ENABLE_GCODE_VIEWER
|
||||
// sets gcode geometry visibility according to user selection
|
||||
void _update_gcode_volumes_visibility(const GCodePreviewData& preview_data);
|
||||
#endif // !ENABLE_GCODE_VIEWER
|
||||
void _update_toolpath_volumes_outside_state();
|
||||
void _update_sla_shells_outside_state();
|
||||
void _show_warning_texture_if_needed(WarningTexture::Warning warning);
|
||||
|
||||
#if !ENABLE_GCODE_VIEWER
|
||||
// generates the legend texture in dependence of the current shown view type
|
||||
void _generate_legend_texture(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors);
|
||||
#endif // !ENABLE_GCODE_VIEWER
|
||||
|
||||
// generates a warning texture containing the given message
|
||||
void _set_warning_texture(WarningTexture::Warning warning, bool state);
|
||||
|
||||
|
|
@ -887,6 +780,7 @@ private:
|
|||
bool _deactivate_search_toolbar_item();
|
||||
bool _activate_search_toolbar_item();
|
||||
bool _deactivate_collapse_toolbar_items();
|
||||
bool _deactivate_arrange_menu();
|
||||
|
||||
float get_overlay_window_width() { return LayersEditing::get_overlay_window_width(); }
|
||||
|
||||
|
|
|
|||
|
|
@ -75,11 +75,9 @@ void GLTexture::Compressor::send_compressed_data_to_gpu()
|
|||
glsafe(::glBindTexture(GL_TEXTURE_2D, m_texture.m_id));
|
||||
// Querying the atomic m_num_levels_compressed value synchronizes processor caches, so that the dat of m_levels modified by the worker thread are accessible to the calling thread.
|
||||
int num_compressed = (int)m_num_levels_compressed;
|
||||
for (int i = 0; i < num_compressed; ++ i)
|
||||
{
|
||||
for (int i = 0; i < num_compressed; ++ i) {
|
||||
Level& level = m_levels[i];
|
||||
if (! level.sent_to_gpu && ! level.compressed_data.empty())
|
||||
{
|
||||
if (! level.sent_to_gpu && ! level.compressed_data.empty()) {
|
||||
glsafe(::glCompressedTexSubImage2D(GL_TEXTURE_2D, (GLint)i, 0, 0, (GLsizei)level.w, (GLsizei)level.h, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)level.compressed_data.size(), (const GLvoid*)level.compressed_data.data()));
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, i));
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (i > 0) ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR));
|
||||
|
|
@ -102,14 +100,13 @@ void GLTexture::Compressor::compress()
|
|||
assert(m_num_levels_compressed == 0);
|
||||
assert(m_abort_compressing == false);
|
||||
|
||||
for (Level& level : m_levels)
|
||||
{
|
||||
for (Level& level : m_levels) {
|
||||
if (m_abort_compressing)
|
||||
break;
|
||||
|
||||
// stb_dxt library, despite claiming that the needed size of the destination buffer is equal to (source buffer size)/4,
|
||||
// crashes if doing so, requiring a minimum of 16 bytes and up to a third of the source buffer size, so we set the destination buffer initial size to be half the source buffer size
|
||||
level.compressed_data = std::vector<unsigned char>(std::max((unsigned int)16, level.w * level.h * 2), 0);
|
||||
// crashes if doing so, requiring a minimum of 64 bytes and up to a third of the source buffer size, so we set the destination buffer initial size to be half the source buffer size
|
||||
level.compressed_data = std::vector<unsigned char>(std::max((unsigned int)64, (unsigned int)level.src_data.size() / 2), 0);
|
||||
int compressed_size = 0;
|
||||
rygCompress(level.compressed_data.data(), level.src_data.data(), level.w, level.h, 1, compressed_size);
|
||||
level.compressed_data.resize(compressed_size);
|
||||
|
|
@ -166,7 +163,7 @@ bool GLTexture::load_from_svg_files_as_sprites_array(const std::vector<std::stri
|
|||
{
|
||||
reset();
|
||||
|
||||
if (filenames.empty() || states.empty() || (sprite_size_px == 0))
|
||||
if (filenames.empty() || states.empty() || sprite_size_px == 0)
|
||||
return false;
|
||||
|
||||
// every tile needs to have a 1px border around it to avoid artifacts when linear sampling on its edges
|
||||
|
|
@ -180,8 +177,7 @@ bool GLTexture::load_from_svg_files_as_sprites_array(const std::vector<std::stri
|
|||
int sprite_stride = sprite_size_px_ex * 4;
|
||||
int sprite_bytes = sprite_n_pixels * 4;
|
||||
|
||||
if (n_pixels <= 0)
|
||||
{
|
||||
if (n_pixels <= 0) {
|
||||
reset();
|
||||
return false;
|
||||
}
|
||||
|
|
@ -193,15 +189,13 @@ bool GLTexture::load_from_svg_files_as_sprites_array(const std::vector<std::stri
|
|||
std::vector<unsigned char> output_data(sprite_bytes, 0);
|
||||
|
||||
NSVGrasterizer* rast = nsvgCreateRasterizer();
|
||||
if (rast == nullptr)
|
||||
{
|
||||
if (rast == nullptr) {
|
||||
reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
int sprite_id = -1;
|
||||
for (const std::string& filename : filenames)
|
||||
{
|
||||
for (const std::string& filename : filenames) {
|
||||
++sprite_id;
|
||||
|
||||
if (!boost::filesystem::exists(filename))
|
||||
|
|
@ -221,8 +215,7 @@ bool GLTexture::load_from_svg_files_as_sprites_array(const std::vector<std::stri
|
|||
|
||||
// makes white only copy of the sprite
|
||||
::memcpy((void*)sprite_white_only_data.data(), (const void*)sprite_data.data(), sprite_bytes);
|
||||
for (int i = 0; i < sprite_n_pixels; ++i)
|
||||
{
|
||||
for (int i = 0; i < sprite_n_pixels; ++i) {
|
||||
int offset = i * 4;
|
||||
if (sprite_white_only_data.data()[offset] != 0)
|
||||
::memset((void*)&sprite_white_only_data.data()[offset], 255, 3);
|
||||
|
|
@ -230,8 +223,7 @@ bool GLTexture::load_from_svg_files_as_sprites_array(const std::vector<std::stri
|
|||
|
||||
// makes gray only copy of the sprite
|
||||
::memcpy((void*)sprite_gray_only_data.data(), (const void*)sprite_data.data(), sprite_bytes);
|
||||
for (int i = 0; i < sprite_n_pixels; ++i)
|
||||
{
|
||||
for (int i = 0; i < sprite_n_pixels; ++i) {
|
||||
int offset = i * 4;
|
||||
if (sprite_gray_only_data.data()[offset] != 0)
|
||||
::memset((void*)&sprite_gray_only_data.data()[offset], 128, 3);
|
||||
|
|
@ -239,30 +231,26 @@ bool GLTexture::load_from_svg_files_as_sprites_array(const std::vector<std::stri
|
|||
|
||||
int sprite_offset_px = sprite_id * (int)sprite_size_px_ex * m_width;
|
||||
int state_id = -1;
|
||||
for (const std::pair<int, bool>& state : states)
|
||||
{
|
||||
for (const std::pair<int, bool>& state : states) {
|
||||
++state_id;
|
||||
|
||||
// select the sprite variant
|
||||
std::vector<unsigned char>* src = nullptr;
|
||||
switch (state.first)
|
||||
{
|
||||
case 1: { src = &sprite_white_only_data; break; }
|
||||
case 2: { src = &sprite_gray_only_data; break; }
|
||||
case 1: { src = &sprite_white_only_data; break; }
|
||||
case 2: { src = &sprite_gray_only_data; break; }
|
||||
default: { src = &sprite_data; break; }
|
||||
}
|
||||
|
||||
::memcpy((void*)output_data.data(), (const void*)src->data(), sprite_bytes);
|
||||
// applies background, if needed
|
||||
if (state.second)
|
||||
{
|
||||
if (state.second) {
|
||||
float inv_255 = 1.0f / 255.0f;
|
||||
// offset by 1 to leave the first pixel empty (both in x and y)
|
||||
for (unsigned int r = 1; r <= sprite_size_px; ++r)
|
||||
{
|
||||
for (unsigned int r = 1; r <= sprite_size_px; ++r) {
|
||||
unsigned int offset_r = r * sprite_size_px_ex;
|
||||
for (unsigned int c = 1; c <= sprite_size_px; ++c)
|
||||
{
|
||||
for (unsigned int c = 1; c <= sprite_size_px; ++c) {
|
||||
unsigned int offset = (offset_r + c) * 4;
|
||||
float alpha = (float)output_data.data()[offset + 3] * inv_255;
|
||||
output_data.data()[offset + 0] = (unsigned char)(output_data.data()[offset + 0] * alpha);
|
||||
|
|
@ -274,8 +262,7 @@ bool GLTexture::load_from_svg_files_as_sprites_array(const std::vector<std::stri
|
|||
}
|
||||
|
||||
int state_offset_px = sprite_offset_px + state_id * sprite_size_px_ex;
|
||||
for (int j = 0; j < (int)sprite_size_px_ex; ++j)
|
||||
{
|
||||
for (int j = 0; j < (int)sprite_size_px_ex; ++j) {
|
||||
::memcpy((void*)&data.data()[(state_offset_px + j * m_width) * 4], (const void*)&output_data.data()[j * sprite_stride], sprite_stride);
|
||||
}
|
||||
}
|
||||
|
|
@ -309,11 +296,9 @@ bool GLTexture::load_from_svg_files_as_sprites_array(const std::vector<std::stri
|
|||
wxImage output(m_width, m_height);
|
||||
output.InitAlpha();
|
||||
|
||||
for (int h = 0; h < m_height; ++h)
|
||||
{
|
||||
for (int h = 0; h < m_height; ++h) {
|
||||
int px_h = h * m_width;
|
||||
for (int w = 0; w < m_width; ++w)
|
||||
{
|
||||
for (int w = 0; w < m_width; ++w) {
|
||||
int offset = (px_h + w) * 4;
|
||||
output.SetRGB(w, h, data.data()[offset + 0], data.data()[offset + 1], data.data()[offset + 2]);
|
||||
output.SetAlpha(w, h, data.data()[offset + 3]);
|
||||
|
|
@ -373,8 +358,7 @@ bool GLTexture::load_from_png(const std::string& filename, bool use_mipmaps, ECo
|
|||
|
||||
// Load a PNG with an alpha channel.
|
||||
wxImage image;
|
||||
if (!image.LoadFile(wxString::FromUTF8(filename.c_str()), wxBITMAP_TYPE_PNG))
|
||||
{
|
||||
if (!image.LoadFile(wxString::FromUTF8(filename.c_str()), wxBITMAP_TYPE_PNG)) {
|
||||
reset();
|
||||
return false;
|
||||
}
|
||||
|
|
@ -384,20 +368,17 @@ bool GLTexture::load_from_png(const std::string& filename, bool use_mipmaps, ECo
|
|||
|
||||
bool requires_rescale = false;
|
||||
|
||||
if (compression_enabled && (compression_type == MultiThreaded))
|
||||
{
|
||||
if (compression_enabled && compression_type == MultiThreaded) {
|
||||
// the stb_dxt compression library seems to like only texture sizes which are a multiple of 4
|
||||
int width_rem = m_width % 4;
|
||||
int height_rem = m_height % 4;
|
||||
|
||||
if (width_rem != 0)
|
||||
{
|
||||
if (width_rem != 0) {
|
||||
m_width += (4 - width_rem);
|
||||
requires_rescale = true;
|
||||
}
|
||||
|
||||
if (height_rem != 0)
|
||||
{
|
||||
if (height_rem != 0) {
|
||||
m_height += (4 - height_rem);
|
||||
requires_rescale = true;
|
||||
}
|
||||
|
|
@ -407,16 +388,14 @@ bool GLTexture::load_from_png(const std::string& filename, bool use_mipmaps, ECo
|
|||
image = image.ResampleBicubic(m_width, m_height);
|
||||
|
||||
int n_pixels = m_width * m_height;
|
||||
if (n_pixels <= 0)
|
||||
{
|
||||
if (n_pixels <= 0) {
|
||||
reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get RGB & alpha raw data from wxImage, pack them into an array.
|
||||
unsigned char* img_rgb = image.GetData();
|
||||
if (img_rgb == nullptr)
|
||||
{
|
||||
if (img_rgb == nullptr) {
|
||||
reset();
|
||||
return false;
|
||||
}
|
||||
|
|
@ -424,8 +403,7 @@ bool GLTexture::load_from_png(const std::string& filename, bool use_mipmaps, ECo
|
|||
unsigned char* img_alpha = image.GetAlpha();
|
||||
|
||||
std::vector<unsigned char> data(n_pixels * 4, 0);
|
||||
for (int i = 0; i < n_pixels; ++i)
|
||||
{
|
||||
for (int i = 0; i < n_pixels; ++i) {
|
||||
int data_id = i * 4;
|
||||
int img_id = i * 3;
|
||||
data[data_id + 0] = img_rgb[img_id + 0];
|
||||
|
|
@ -439,19 +417,16 @@ bool GLTexture::load_from_png(const std::string& filename, bool use_mipmaps, ECo
|
|||
glsafe(::glGenTextures(1, &m_id));
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, m_id));
|
||||
|
||||
if (apply_anisotropy)
|
||||
{
|
||||
if (apply_anisotropy) {
|
||||
GLfloat max_anisotropy = OpenGLManager::get_gl_info().get_max_anisotropy();
|
||||
if (max_anisotropy > 1.0f)
|
||||
glsafe(::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy));
|
||||
}
|
||||
|
||||
if (compression_enabled)
|
||||
{
|
||||
if (compression_enabled) {
|
||||
if (compression_type == SingleThreaded)
|
||||
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
|
||||
else
|
||||
{
|
||||
else {
|
||||
// initializes the texture on GPU
|
||||
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0));
|
||||
// and send the uncompressed data to the compressor
|
||||
|
|
@ -461,14 +436,12 @@ bool GLTexture::load_from_png(const std::string& filename, bool use_mipmaps, ECo
|
|||
else
|
||||
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
|
||||
|
||||
if (use_mipmaps)
|
||||
{
|
||||
if (use_mipmaps) {
|
||||
// we manually generate mipmaps because glGenerateMipmap() function is not reliable on all graphics cards
|
||||
int lod_w = m_width;
|
||||
int lod_h = m_height;
|
||||
GLint level = 0;
|
||||
while ((lod_w > 1) || (lod_h > 1))
|
||||
{
|
||||
while (lod_w > 1 || lod_h > 1) {
|
||||
++level;
|
||||
|
||||
lod_w = std::max(lod_w / 2, 1);
|
||||
|
|
@ -482,8 +455,7 @@ bool GLTexture::load_from_png(const std::string& filename, bool use_mipmaps, ECo
|
|||
img_rgb = image.GetData();
|
||||
img_alpha = image.GetAlpha();
|
||||
|
||||
for (int i = 0; i < n_pixels; ++i)
|
||||
{
|
||||
for (int i = 0; i < n_pixels; ++i) {
|
||||
int data_id = i * 4;
|
||||
int img_id = i * 3;
|
||||
data[data_id + 0] = img_rgb[img_id + 0];
|
||||
|
|
@ -492,12 +464,10 @@ bool GLTexture::load_from_png(const std::string& filename, bool use_mipmaps, ECo
|
|||
data[data_id + 3] = (img_alpha != nullptr) ? img_alpha[i] : 255;
|
||||
}
|
||||
|
||||
if (compression_enabled)
|
||||
{
|
||||
if (compression_enabled) {
|
||||
if (compression_type == SingleThreaded)
|
||||
glsafe(::glTexImage2D(GL_TEXTURE_2D, level, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
|
||||
else
|
||||
{
|
||||
else {
|
||||
// initializes the texture on GPU
|
||||
glsafe(::glTexImage2D(GL_TEXTURE_2D, level, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)lod_w, (GLsizei)lod_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0));
|
||||
// and send the uncompressed data to the compressor
|
||||
|
|
@ -508,14 +478,12 @@ bool GLTexture::load_from_png(const std::string& filename, bool use_mipmaps, ECo
|
|||
glsafe(::glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, (GLsizei)lod_w, (GLsizei)lod_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
|
||||
}
|
||||
|
||||
if (!compression_enabled)
|
||||
{
|
||||
if (!compression_enabled) {
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, level));
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0));
|
||||
}
|
||||
|
|
@ -526,7 +494,7 @@ bool GLTexture::load_from_png(const std::string& filename, bool use_mipmaps, ECo
|
|||
|
||||
m_source = filename;
|
||||
|
||||
if (compression_enabled && (compression_type == MultiThreaded))
|
||||
if (compression_enabled && compression_type == MultiThreaded)
|
||||
// start asynchronous compression
|
||||
m_compressor.start_compressing();
|
||||
|
||||
|
|
@ -538,8 +506,7 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, boo
|
|||
bool compression_enabled = compress && GLEW_EXT_texture_compression_s3tc;
|
||||
|
||||
NSVGimage* image = nsvgParseFromFile(filename.c_str(), "px", 96.0f);
|
||||
if (image == nullptr)
|
||||
{
|
||||
if (image == nullptr) {
|
||||
reset();
|
||||
return false;
|
||||
}
|
||||
|
|
@ -549,8 +516,7 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, boo
|
|||
m_width = (int)(scale * image->width);
|
||||
m_height = (int)(scale * image->height);
|
||||
|
||||
if (compression_enabled)
|
||||
{
|
||||
if (compression_enabled) {
|
||||
// the stb_dxt compression library seems to like only texture sizes which are a multiple of 4
|
||||
int width_rem = m_width % 4;
|
||||
int height_rem = m_height % 4;
|
||||
|
|
@ -564,16 +530,14 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, boo
|
|||
|
||||
int n_pixels = m_width * m_height;
|
||||
|
||||
if (n_pixels <= 0)
|
||||
{
|
||||
if (n_pixels <= 0) {
|
||||
reset();
|
||||
nsvgDelete(image);
|
||||
return false;
|
||||
}
|
||||
|
||||
NSVGrasterizer* rast = nsvgCreateRasterizer();
|
||||
if (rast == nullptr)
|
||||
{
|
||||
if (rast == nullptr) {
|
||||
nsvgDelete(image);
|
||||
reset();
|
||||
return false;
|
||||
|
|
@ -588,15 +552,13 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, boo
|
|||
glsafe(::glGenTextures(1, &m_id));
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, m_id));
|
||||
|
||||
if (apply_anisotropy)
|
||||
{
|
||||
if (apply_anisotropy) {
|
||||
GLfloat max_anisotropy = OpenGLManager::get_gl_info().get_max_anisotropy();
|
||||
if (max_anisotropy > 1.0f)
|
||||
glsafe(::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy));
|
||||
}
|
||||
|
||||
if (compression_enabled)
|
||||
{
|
||||
if (compression_enabled) {
|
||||
// initializes the texture on GPU
|
||||
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0));
|
||||
// and send the uncompressed data to the compressor
|
||||
|
|
@ -605,14 +567,12 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, boo
|
|||
else
|
||||
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
|
||||
|
||||
if (use_mipmaps)
|
||||
{
|
||||
if (use_mipmaps) {
|
||||
// we manually generate mipmaps because glGenerateMipmap() function is not reliable on all graphics cards
|
||||
int lod_w = m_width;
|
||||
int lod_h = m_height;
|
||||
GLint level = 0;
|
||||
while ((lod_w > 1) || (lod_h > 1))
|
||||
{
|
||||
while (lod_w > 1 || lod_h > 1) {
|
||||
++level;
|
||||
|
||||
lod_w = std::max(lod_w / 2, 1);
|
||||
|
|
@ -622,8 +582,7 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, boo
|
|||
data.resize(lod_w * lod_h * 4);
|
||||
|
||||
nsvgRasterize(rast, image, 0, 0, scale, data.data(), lod_w, lod_h, lod_w * 4);
|
||||
if (compression_enabled)
|
||||
{
|
||||
if (compression_enabled) {
|
||||
// initializes the texture on GPU
|
||||
glsafe(::glTexImage2D(GL_TEXTURE_2D, level, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)lod_w, (GLsizei)lod_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0));
|
||||
// and send the uncompressed data to the compressor
|
||||
|
|
@ -633,14 +592,12 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, boo
|
|||
glsafe(::glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, (GLsizei)lod_w, (GLsizei)lod_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
|
||||
}
|
||||
|
||||
if (!compression_enabled)
|
||||
{
|
||||
if (!compression_enabled) {
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, level));
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0));
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue