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:
Lukáš Hejl 2020-11-29 17:27:23 +01:00
commit 87879034f6
175 changed files with 34821 additions and 26174 deletions

View file

@ -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 ()

View file

@ -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();
}
};
}

View file

@ -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);
}

View file

@ -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));
};

View file

@ -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;
};
}

View file

@ -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

View file

@ -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");

View file

@ -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 &params) {
// 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 &params,
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 &params,
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>

View file

@ -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;

View file

@ -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);
};

View file

@ -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

View file

@ -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.);

View file

@ -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)

View file

@ -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

View file

@ -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)));
}
}

View file

@ -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;

View file

@ -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)

View file

@ -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 &region_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) {

View file

@ -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;

View file

@ -137,7 +137,7 @@ void Fill3DHoneycomb::_fill_surface_single(
const FillParams &params,
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

View file

@ -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 &params,
unsigned int thickness_layers,
const std::pair<float, Point> &direction,
ExPolygon &expolygon,
Polylines &polylines_out);
ExPolygon expolygon,
Polylines &polylines_out) override;
};
} // namespace Slic3r

View file

@ -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 &params,
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
{

View file

@ -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 &params,
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

View file

@ -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 &params);
static void connect_infill(Polylines &&infill_ordered, const ExPolygon &boundary, Polylines &polylines_out, const double spacing, const FillParams &params);
static void connect_infill(Polylines &&infill_ordered, const Polygons &boundary, const BoundingBox& bbox, Polylines &polylines_out, const double spacing, const FillParams &params);
static void connect_infill(Polylines &&infill_ordered, const std::vector<const Polygon*> &boundary, const BoundingBox &bbox, Polylines &polylines_out, double spacing, const FillParams &params);
static coord_t _adjust_solid_spacing(const coord_t width, const coord_t distance);

View file

@ -10,7 +10,7 @@ void FillConcentric::_fill_surface_single(
const FillParams &params,
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();
}

View file

@ -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 &params,
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

View file

@ -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 &params,
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);

View file

@ -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 &params,
unsigned int thickness_layers,
const std::pair<float, Point> &direction,
ExPolygon &expolygon,
Polylines &polylines_out);
ExPolygon expolygon,
Polylines &polylines_out) override;
};
} // namespace Slic3r

View file

@ -10,7 +10,7 @@ void FillHoneycomb::_fill_surface_single(
const FillParams &params,
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

View file

@ -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 &params,
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

View 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 &params,
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

View 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 &params,
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_

View file

@ -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 &params,
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;
}

View file

@ -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 &params,
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

View file

@ -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 &params) override;
protected:
virtual void _fill_surface_single(
const FillParams &params,
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 &params, 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 &params) 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 &params) 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 &params) 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 &params) 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 &params) 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

View file

@ -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 &params);
protected:
bool fill_surface_by_lines(const Surface *surface, const FillParams &params, 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 &params);
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 &params);
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 &params);
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 &params);
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 &params);
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_

View file

@ -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>

View file

@ -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);

View file

@ -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)

View file

@ -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);

View file

@ -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());

View file

@ -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());
}
};

View file

@ -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));

View file

@ -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);

View file

@ -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>();

View file

@ -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 &center) { 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)); }

View file

@ -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;
}

View file

@ -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)) {}

View file

@ -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

View 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

View file

@ -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

View file

@ -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,

View file

@ -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);

View file

@ -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 &center);
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 &center) 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;

View file

@ -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();

View file

@ -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);

View file

@ -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;
}

View file

@ -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;

View file

@ -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

View file

@ -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;

View file

@ -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));

View file

@ -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);

View file

@ -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);

View file

@ -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; }

View file

@ -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();
}
};

View file

@ -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;

View file

@ -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);

View file

@ -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); }
};
}

View file

@ -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.

View file

@ -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_

View file

@ -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)

View file

@ -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;
}

View file

@ -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

View file

@ -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)

View file

@ -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));

View file

@ -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;

View file

@ -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

View file

@ -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);

View file

@ -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__

View file

@ -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;
}

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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)
{

View file

@ -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));

View file

@ -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)
{}

View file

@ -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++;

View file

@ -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;

View file

@ -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;

View file

@ -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)
{

View file

@ -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

View file

@ -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 {

View file

@ -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

View file

@ -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

View file

@ -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(); }

View file

@ -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