Flip Y changed back to BOTTOM_LEFT

Merge remote-tracking branch 'origin/master' into feature_slice_to_png
This commit is contained in:
tamasmeszaros 2018-07-25 17:27:02 +02:00
commit 93ae3a0f1a
50 changed files with 1244 additions and 860 deletions

View file

@ -25,6 +25,9 @@ THE SOFTWARE.
#ifndef SHINY_PREREQS_H
#define SHINY_PREREQS_H
#include <stdint.h>
/*---------------------------------------------------------------------------*/
#ifndef FALSE

View file

@ -93,8 +93,11 @@ float ShinyGetTickInvFreq(void) {
#elif SHINY_PLATFORM == SHINY_PLATFORM_POSIX
//#include <time.h>
//#include <sys/time.h>
void ShinyGetTicks(shinytick_t *p) {
timeval time;
struct timeval time;
gettimeofday(&time, NULL);
*p = time.tv_sec * 1000000 + time.tv_usec;

View file

@ -519,20 +519,28 @@ void arrangeRectangles() {
std::vector<Item> proba = {
{
{ {0, 0}, {20, 20}, {40, 0}, {0, 0} }
Rectangle(100, 2)
},
{
{ {0, 100}, {50, 60}, {100, 100}, {50, 0}, {0, 100} }
Rectangle(100, 2)
},
{
Rectangle(100, 2)
},
{
Rectangle(10, 10)
},
};
proba[0].rotate(Pi/3);
proba[1].rotate(Pi-Pi/3);
std::vector<Item> input;
input.insert(input.end(), prusaParts().begin(), prusaParts().end());
// input.insert(input.end(), prusaExParts().begin(), prusaExParts().end());
// input.insert(input.end(), stegoParts().begin(), stegoParts().end());
input.insert(input.end(), stegoParts().begin(), stegoParts().end());
// input.insert(input.end(), rects.begin(), rects.end());
// input.insert(input.end(), proba.begin(), proba.end());
input.insert(input.end(), proba.begin(), proba.end());
// input.insert(input.end(), crasher.begin(), crasher.end());
Box bin(250*SCALE, 210*SCALE);
@ -569,9 +577,9 @@ void arrangeRectangles() {
Packer::SelectionConfig sconf;
// sconf.allow_parallel = false;
// sconf.force_parallel = false;
// sconf.try_triplets = true;
// sconf.try_triplets = false;
// sconf.try_reverse_order = true;
// sconf.waste_increment = 0.1;
// sconf.waste_increment = 0.005;
arrange.configure(pconf, sconf);

View file

@ -25,9 +25,60 @@ using Shapes = typename ShapeLike::Shapes<RawShape>;
/// Minkowski addition (not used yet)
template<class RawShape>
static RawShape minkowskiDiff(const RawShape& sh, const RawShape& /*other*/)
static RawShape minkowskiDiff(const RawShape& sh, const RawShape& cother)
{
using Vertex = TPoint<RawShape>;
//using Coord = TCoord<Vertex>;
using Edge = _Segment<Vertex>;
using sl = ShapeLike;
using std::signbit;
// Copy the orbiter (controur only), we will have to work on it
RawShape orbiter = sl::create(sl::getContour(cother));
// Make the orbiter reverse oriented
for(auto &v : sl::getContour(orbiter)) v = -v;
// An egde with additional data for marking it
struct MarkedEdge { Edge e; Radians turn_angle; bool is_turning_point; };
// Container for marked edges
using EdgeList = std::vector<MarkedEdge>;
EdgeList A, B;
auto fillEdgeList = [](EdgeList& L, const RawShape& poly) {
L.reserve(sl::contourVertexCount(poly));
auto it = sl::cbegin(poly);
auto nextit = std::next(it);
L.emplace_back({Edge(*it, *nextit), 0, false});
it++; nextit++;
while(nextit != sl::cend(poly)) {
Edge e(*it, *nextit);
auto& L_prev = L.back();
auto phi = L_prev.e.angleToXaxis();
auto phi_prev = e.angleToXaxis();
auto turn_angle = phi-phi_prev;
if(turn_angle > Pi) turn_angle -= 2*Pi;
L.emplace_back({
e,
turn_angle,
signbit(turn_angle) != signbit(L_prev.turn_angle)
});
it++; nextit++;
}
L.front().turn_angle = L.front().e.angleToXaxis() -
L.back().e.angleToXaxis();
if(L.front().turn_angle > Pi) L.front().turn_angle -= 2*Pi;
};
fillEdgeList(A, sh);
fillEdgeList(B, orbiter);
return sh;
}
@ -193,6 +244,9 @@ static RawShape nfpConvexOnly(const RawShape& sh, const RawShape& cother)
// Lindmark's reasoning about the reference vertex of nfp in his thesis
// ("No fit polygon problem" - section 2.1.9)
// TODO: dont do this here. Cache the rmu and lmd in Item and get translate
// the nfp after this call
auto csh = sh; // Copy sh, we will sort the verices in the copy
auto& cmp = _vsort<RawShape>;
std::sort(ShapeLike::begin(csh), ShapeLike::end(csh), cmp);

View file

@ -48,32 +48,89 @@ template<class RawShape> class EdgeCache {
using Coord = TCoord<Vertex>;
using Edge = _Segment<Vertex>;
mutable std::vector<double> corners_;
struct ContourCache {
mutable std::vector<double> corners;
std::vector<Edge> emap;
std::vector<double> distances;
double full_distance = 0;
} contour_;
std::vector<Edge> emap_;
std::vector<double> distances_;
double full_distance_ = 0;
std::vector<ContourCache> holes_;
void createCache(const RawShape& sh) {
auto first = ShapeLike::cbegin(sh);
auto next = first + 1;
auto endit = ShapeLike::cend(sh);
{ // For the contour
auto first = ShapeLike::cbegin(sh);
auto next = std::next(first);
auto endit = ShapeLike::cend(sh);
distances_.reserve(ShapeLike::contourVertexCount(sh));
contour_.distances.reserve(ShapeLike::contourVertexCount(sh));
while(next != endit) {
emap_.emplace_back(*(first++), *(next++));
full_distance_ += emap_.back().length();
distances_.push_back(full_distance_);
while(next != endit) {
contour_.emap.emplace_back(*(first++), *(next++));
contour_.full_distance += contour_.emap.back().length();
contour_.distances.push_back(contour_.full_distance);
}
}
for(auto& h : ShapeLike::holes(sh)) { // For the holes
auto first = h.begin();
auto next = std::next(first);
auto endit = h.end();
ContourCache hc;
hc.distances.reserve(endit - first);
while(next != endit) {
hc.emap.emplace_back(*(first++), *(next++));
hc.full_distance += hc.emap.back().length();
hc.distances.push_back(hc.full_distance);
}
holes_.push_back(hc);
}
}
void fetchCorners() const {
if(!corners_.empty()) return;
if(!contour_.corners.empty()) return;
// TODO Accuracy
corners_ = distances_;
for(auto& d : corners_) d /= full_distance_;
contour_.corners = contour_.distances;
for(auto& d : contour_.corners) d /= contour_.full_distance;
}
void fetchHoleCorners(unsigned hidx) const {
auto& hc = holes_[hidx];
if(!hc.corners.empty()) return;
// TODO Accuracy
hc.corners = hc.distances;
for(auto& d : hc.corners) d /= hc.full_distance;
}
inline Vertex coords(const ContourCache& cache, double distance) const {
assert(distance >= .0 && distance <= 1.0);
// distance is from 0.0 to 1.0, we scale it up to the full length of
// the circumference
double d = distance*cache.full_distance;
auto& distances = cache.distances;
// Magic: we find the right edge in log time
auto it = std::lower_bound(distances.begin(), distances.end(), d);
auto idx = it - distances.begin(); // get the index of the edge
auto edge = cache.emap[idx]; // extrac the edge
// Get the remaining distance on the target edge
auto ed = d - (idx > 0 ? *std::prev(it) : 0 );
auto angle = edge.angleToXaxis();
Vertex ret = edge.first();
// Get the point on the edge which lies in ed distance from the start
ret += { static_cast<Coord>(std::round(ed*std::cos(angle))),
static_cast<Coord>(std::round(ed*std::sin(angle))) };
return ret;
}
public:
@ -102,37 +159,36 @@ public:
* @return Returns the coordinates of the point lying on the polygon
* circumference.
*/
inline Vertex coords(double distance) {
assert(distance >= .0 && distance <= 1.0);
// distance is from 0.0 to 1.0, we scale it up to the full length of
// the circumference
double d = distance*full_distance_;
// Magic: we find the right edge in log time
auto it = std::lower_bound(distances_.begin(), distances_.end(), d);
auto idx = it - distances_.begin(); // get the index of the edge
auto edge = emap_[idx]; // extrac the edge
// Get the remaining distance on the target edge
auto ed = d - (idx > 0 ? *std::prev(it) : 0 );
auto angle = edge.angleToXaxis();
Vertex ret = edge.first();
// Get the point on the edge which lies in ed distance from the start
ret += { static_cast<Coord>(std::round(ed*std::cos(angle))),
static_cast<Coord>(std::round(ed*std::sin(angle))) };
return ret;
inline Vertex coords(double distance) const {
return coords(contour_, distance);
}
inline double circumference() const BP2D_NOEXCEPT { return full_distance_; }
inline Vertex coords(unsigned hidx, double distance) const {
assert(hidx < holes_.size());
return coords(holes_[hidx], distance);
}
inline double circumference() const BP2D_NOEXCEPT {
return contour_.full_distance;
}
inline double circumference(unsigned hidx) const BP2D_NOEXCEPT {
return holes_[hidx].full_distance;
}
inline const std::vector<double>& corners() const BP2D_NOEXCEPT {
fetchCorners();
return corners_;
return contour_.corners;
}
inline const std::vector<double>&
corners(unsigned holeidx) const BP2D_NOEXCEPT {
fetchHoleCorners(holeidx);
return holes_[holeidx].corners;
}
inline unsigned holeCount() const BP2D_NOEXCEPT { return holes_.size(); }
};
template<NfpLevel lvl>
@ -294,12 +350,20 @@ public:
for(auto& nfp : nfps ) ecache.emplace_back(nfp);
auto getNfpPoint = [&ecache](double relpos) {
auto relpfloor = std::floor(relpos);
auto nfp_idx = static_cast<unsigned>(relpfloor);
if(nfp_idx >= ecache.size()) nfp_idx--;
auto p = relpos - relpfloor;
return ecache[nfp_idx].coords(p);
struct Optimum {
double relpos;
unsigned nfpidx;
int hidx;
Optimum(double pos, unsigned nidx):
relpos(pos), nfpidx(nidx), hidx(-1) {}
Optimum(double pos, unsigned nidx, int holeidx):
relpos(pos), nfpidx(nidx), hidx(holeidx) {}
};
auto getNfpPoint = [&ecache](const Optimum& opt)
{
return opt.hidx < 0? ecache[opt.nfpidx].coords(opt.relpos) :
ecache[opt.nfpidx].coords(opt.nfpidx, opt.relpos);
};
Nfp::Shapes<RawShape> pile;
@ -310,6 +374,8 @@ public:
pile_area += mitem.area();
}
// This is the kernel part of the object function that is
// customizable by the library client
auto _objfunc = config_.object_function?
config_.object_function :
[this](const Nfp::Shapes<RawShape>& pile, double occupied_area,
@ -334,9 +400,8 @@ public:
};
// Our object function for placement
auto objfunc = [&] (double relpos)
auto rawobjfunc = [&] (Vertex v)
{
Vertex v = getNfpPoint(relpos);
auto d = v - iv;
d += startpos;
item.translation(d);
@ -359,46 +424,74 @@ public:
stopcr.type = opt::StopLimitType::RELATIVE;
opt::TOptimizer<opt::Method::L_SIMPLEX> solver(stopcr);
double optimum = 0;
Optimum optimum(0, 0);
double best_score = penality_;
// double max_bound = 1.0*nfps.size();
// Genetic should look like this:
/*auto result = solver.optimize_min(objfunc,
opt::initvals<double>(0.0),
opt::bound(0.0, max_bound)
);
if(result.score < penality_) {
best_score = result.score;
optimum = std::get<0>(result.optimum);
}*/
// Local optimization with the four polygon corners as
// starting points
for(unsigned ch = 0; ch < ecache.size(); ch++) {
auto& cache = ecache[ch];
auto contour_ofn = [&rawobjfunc, &getNfpPoint, ch]
(double relpos)
{
return rawobjfunc(getNfpPoint(Optimum(relpos, ch)));
};
std::for_each(cache.corners().begin(),
cache.corners().end(),
[ch, &solver, &objfunc,
&best_score, &optimum]
(double pos)
[ch, &contour_ofn, &solver, &best_score,
&optimum] (double pos)
{
try {
auto result = solver.optimize_min(objfunc,
opt::initvals<double>(ch+pos),
opt::bound<double>(ch, 1.0 + ch)
auto result = solver.optimize_min(contour_ofn,
opt::initvals<double>(pos),
opt::bound<double>(0, 1.0)
);
if(result.score < best_score) {
best_score = result.score;
optimum = std::get<0>(result.optimum);
optimum.relpos = std::get<0>(result.optimum);
optimum.nfpidx = ch;
optimum.hidx = -1;
}
} catch(std::exception& e) {
derr() << "ERROR: " << e.what() << "\n";
}
});
for(unsigned hidx = 0; hidx < cache.holeCount(); ++hidx) {
auto hole_ofn =
[&rawobjfunc, &getNfpPoint, ch, hidx]
(double pos)
{
Optimum opt(pos, ch, hidx);
return rawobjfunc(getNfpPoint(opt));
};
std::for_each(cache.corners(hidx).begin(),
cache.corners(hidx).end(),
[&hole_ofn, &solver, &best_score,
&optimum, ch, hidx]
(double pos)
{
try {
auto result = solver.optimize_min(hole_ofn,
opt::initvals<double>(pos),
opt::bound<double>(0, 1.0)
);
if(result.score < best_score) {
best_score = result.score;
Optimum o(std::get<0>(result.optimum),
ch, hidx);
optimum = o;
}
} catch(std::exception& e) {
derr() << "ERROR: " << e.what() << "\n";
}
});
}
}
if( best_score < global_score ) {

View file

@ -56,7 +56,7 @@ public:
};
// Safety test: try to pack each item into an empty bin. If it fails
// then it should be removed from the not_packed list
// then it should be removed from the list
{ auto it = store_.begin();
while (it != store_.end()) {
Placer p(bin);
@ -72,7 +72,7 @@ public:
while(!was_packed) {
for(size_t j = 0; j < placers.size() && !was_packed; j++) {
if(was_packed = placers[j].pack(item))
if((was_packed = placers[j].pack(item)))
makeProgress(placers[j], j);
}

View file

@ -103,6 +103,10 @@ public:
bool contains(const BoundingBox3Base<PointClass>& other) const {
return contains(other.min) && contains(other.max);
}
bool intersects(const BoundingBox3Base<PointClass>& other) const {
return (this->min.x < other.max.x) && (this->max.x > other.min.x) && (this->min.y < other.max.y) && (this->max.y > other.min.y) && (this->min.z < other.max.z) && (this->max.z > other.min.z);
}
};
class BoundingBox : public BoundingBoxBase<Point>

View file

@ -1989,7 +1989,7 @@ namespace Slic3r {
// stores object's name
if (!obj->name.empty())
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << OBJECT_TYPE << "\" " << KEY_ATTR << "=\"name\" " << VALUE_ATTR << "=\"" << obj->name << "\"/>\n";
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << OBJECT_TYPE << "\" " << KEY_ATTR << "=\"name\" " << VALUE_ATTR << "=\"" << xml_escape(obj->name) << "\"/>\n";
// stores object's config data
for (const std::string& key : obj->config.keys())
@ -2012,7 +2012,7 @@ namespace Slic3r {
// stores volume's name
if (!volume->name.empty())
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << NAME_KEY << "\" " << VALUE_ATTR << "=\"" << volume->name << "\"/>\n";
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << NAME_KEY << "\" " << VALUE_ATTR << "=\"" << xml_escape(volume->name) << "\"/>\n";
// stores volume's modifier field
if (volume->modifier)

View file

@ -8,6 +8,7 @@
#include "../libslic3r.h"
#include "../Model.hpp"
#include "../GCode.hpp"
#include "../Utils.hpp"
#include "../slic3r/GUI/PresetBundle.hpp"
#include "AMF.hpp"
@ -686,33 +687,6 @@ bool load_amf(const char *path, PresetBundle* bundle, Model *model)
return false;
}
std::string xml_escape(std::string text)
{
std::string::size_type pos = 0;
for (;;)
{
pos = text.find_first_of("\"\'&<>", pos);
if (pos == std::string::npos)
break;
std::string replacement;
switch (text[pos])
{
case '\"': replacement = "&quot;"; break;
case '\'': replacement = "&apos;"; break;
case '&': replacement = "&amp;"; break;
case '<': replacement = "&lt;"; break;
case '>': replacement = "&gt;"; break;
default: break;
}
text.replace(pos, 1, replacement);
pos += replacement.size();
}
return text;
}
bool store_amf(const char *path, Model *model, Print* print, bool export_print_config)
{
if ((path == nullptr) || (model == nullptr) || (print == nullptr))
@ -761,7 +735,7 @@ bool store_amf(const char *path, Model *model, Print* print, bool export_print_c
for (const std::string &key : object->config.keys())
stream << " <metadata type=\"slic3r." << key << "\">" << object->config.serialize(key) << "</metadata>\n";
if (!object->name.empty())
stream << " <metadata type=\"name\">" << object->name << "</metadata>\n";
stream << " <metadata type=\"name\">" << xml_escape(object->name) << "</metadata>\n";
std::vector<double> layer_height_profile = object->layer_height_profile_valid ? object->layer_height_profile : std::vector<double>();
if (layer_height_profile.size() >= 4 && (layer_height_profile.size() % 2) == 0) {
// Store the layer height profile as a single semicolon separated list.
@ -805,7 +779,7 @@ bool store_amf(const char *path, Model *model, Print* print, bool export_print_c
for (const std::string &key : volume->config.keys())
stream << " <metadata type=\"slic3r." << key << "\">" << volume->config.serialize(key) << "</metadata>\n";
if (!volume->name.empty())
stream << " <metadata type=\"name\">" << volume->name << "</metadata>\n";
stream << " <metadata type=\"name\">" << xml_escape(volume->name) << "</metadata>\n";
if (volume->modifier)
stream << " <metadata type=\"slic3r.modifier\">1</metadata>\n";
for (int i = 0; i < volume->mesh.stl.stats.number_of_facets; ++i) {

View file

@ -309,10 +309,12 @@ std::vector<std::pair<coordf_t, std::vector<GCode::LayerToPrint>>> GCode::collec
size_t object_idx;
size_t layer_idx;
};
std::vector<std::vector<LayerToPrint>> per_object(print.objects.size(), std::vector<LayerToPrint>());
PrintObjectPtrs printable_objects = print.get_printable_objects();
std::vector<std::vector<LayerToPrint>> per_object(printable_objects.size(), std::vector<LayerToPrint>());
std::vector<OrderingItem> ordering;
for (size_t i = 0; i < print.objects.size(); ++ i) {
per_object[i] = collect_layers_to_print(*print.objects[i]);
for (size_t i = 0; i < printable_objects.size(); ++i) {
per_object[i] = collect_layers_to_print(*printable_objects[i]);
OrderingItem ordering_item;
ordering_item.object_idx = i;
ordering.reserve(ordering.size() + per_object[i].size());
@ -337,8 +339,8 @@ std::vector<std::pair<coordf_t, std::vector<GCode::LayerToPrint>>> GCode::collec
std::pair<coordf_t, std::vector<LayerToPrint>> merged;
// Assign an average print_z to the set of layers with nearly equal print_z.
merged.first = 0.5 * (ordering[i].print_z + ordering[j-1].print_z);
merged.second.assign(print.objects.size(), LayerToPrint());
for (; i < j; ++ i) {
merged.second.assign(printable_objects.size(), LayerToPrint());
for (; i < j; ++i) {
const OrderingItem &oi = ordering[i];
assert(merged.second[oi.object_idx].layer() == nullptr);
merged.second[oi.object_idx] = std::move(per_object[oi.object_idx][oi.layer_idx]);
@ -375,10 +377,13 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_
}
fclose(file);
m_normal_time_estimator.post_process_remaining_times(path_tmp, 60.0f);
if (print->config.gcode_flavor.value == gcfMarlin)
{
m_normal_time_estimator.post_process_remaining_times(path_tmp, 60.0f);
if (m_silent_time_estimator_enabled)
m_silent_time_estimator.post_process_remaining_times(path_tmp, 60.0f);
if (m_silent_time_estimator_enabled)
m_silent_time_estimator.post_process_remaining_times(path_tmp, 60.0f);
}
if (! this->m_placeholder_parser_failed_templates.empty()) {
// G-code export proceeded, but some of the PlaceholderParser substitutions failed.
@ -412,45 +417,54 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
// resets time estimators
m_normal_time_estimator.reset();
m_normal_time_estimator.set_dialect(print.config.gcode_flavor);
m_normal_time_estimator.set_acceleration(print.config.machine_max_acceleration_extruding.values[0]);
m_normal_time_estimator.set_retract_acceleration(print.config.machine_max_acceleration_retracting.values[0]);
m_normal_time_estimator.set_minimum_feedrate(print.config.machine_min_extruding_rate.values[0]);
m_normal_time_estimator.set_minimum_travel_feedrate(print.config.machine_min_travel_rate.values[0]);
m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::X, print.config.machine_max_acceleration_x.values[0]);
m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Y, print.config.machine_max_acceleration_y.values[0]);
m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Z, print.config.machine_max_acceleration_z.values[0]);
m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::E, print.config.machine_max_acceleration_e.values[0]);
m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::X, print.config.machine_max_feedrate_x.values[0]);
m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Y, print.config.machine_max_feedrate_y.values[0]);
m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Z, print.config.machine_max_feedrate_z.values[0]);
m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::E, print.config.machine_max_feedrate_e.values[0]);
m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::X, print.config.machine_max_jerk_x.values[0]);
m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Y, print.config.machine_max_jerk_y.values[0]);
m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Z, print.config.machine_max_jerk_z.values[0]);
m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::E, print.config.machine_max_jerk_e.values[0]);
m_silent_time_estimator_enabled = (print.config.gcode_flavor == gcfMarlin) && print.config.silent_mode;
m_silent_time_estimator_enabled = (print.config.gcode_flavor == gcfMarlin) && print.config.silent_mode && boost::starts_with(print.config.printer_model.value, "MK3");
if (m_silent_time_estimator_enabled)
{
m_silent_time_estimator.reset();
m_silent_time_estimator.set_dialect(print.config.gcode_flavor);
m_silent_time_estimator.set_acceleration(print.config.machine_max_acceleration_extruding.values[1]);
m_silent_time_estimator.set_retract_acceleration(print.config.machine_max_acceleration_retracting.values[1]);
m_silent_time_estimator.set_minimum_feedrate(print.config.machine_min_extruding_rate.values[1]);
m_silent_time_estimator.set_minimum_travel_feedrate(print.config.machine_min_travel_rate.values[1]);
m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::X, print.config.machine_max_acceleration_x.values[1]);
m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Y, print.config.machine_max_acceleration_y.values[1]);
m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Z, print.config.machine_max_acceleration_z.values[1]);
m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::E, print.config.machine_max_acceleration_e.values[1]);
m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::X, print.config.machine_max_feedrate_x.values[1]);
m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Y, print.config.machine_max_feedrate_y.values[1]);
m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Z, print.config.machine_max_feedrate_z.values[1]);
m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::E, print.config.machine_max_feedrate_e.values[1]);
m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::X, print.config.machine_max_jerk_x.values[1]);
m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Y, print.config.machine_max_jerk_y.values[1]);
m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Z, print.config.machine_max_jerk_z.values[1]);
m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::E, print.config.machine_max_jerk_e.values[1]);
// Until we have a UI support for the other firmwares than the Marlin, use the hardcoded default values
// and let the user to enter the G-code limits into the start G-code.
// If the following block is enabled for other firmwares than the Marlin, then the function
// this->print_machine_envelope(file, print);
// shall be adjusted as well to produce a G-code block compatible with the particular firmware flavor.
if (print.config.gcode_flavor.value == gcfMarlin) {
m_normal_time_estimator.set_max_acceleration(print.config.machine_max_acceleration_extruding.values[0]);
m_normal_time_estimator.set_retract_acceleration(print.config.machine_max_acceleration_retracting.values[0]);
m_normal_time_estimator.set_minimum_feedrate(print.config.machine_min_extruding_rate.values[0]);
m_normal_time_estimator.set_minimum_travel_feedrate(print.config.machine_min_travel_rate.values[0]);
m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::X, print.config.machine_max_acceleration_x.values[0]);
m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Y, print.config.machine_max_acceleration_y.values[0]);
m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Z, print.config.machine_max_acceleration_z.values[0]);
m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::E, print.config.machine_max_acceleration_e.values[0]);
m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::X, print.config.machine_max_feedrate_x.values[0]);
m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Y, print.config.machine_max_feedrate_y.values[0]);
m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Z, print.config.machine_max_feedrate_z.values[0]);
m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::E, print.config.machine_max_feedrate_e.values[0]);
m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::X, print.config.machine_max_jerk_x.values[0]);
m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Y, print.config.machine_max_jerk_y.values[0]);
m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Z, print.config.machine_max_jerk_z.values[0]);
m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::E, print.config.machine_max_jerk_e.values[0]);
if (m_silent_time_estimator_enabled)
{
m_silent_time_estimator.reset();
m_silent_time_estimator.set_dialect(print.config.gcode_flavor);
m_silent_time_estimator.set_max_acceleration(print.config.machine_max_acceleration_extruding.values[1]);
m_silent_time_estimator.set_retract_acceleration(print.config.machine_max_acceleration_retracting.values[1]);
m_silent_time_estimator.set_minimum_feedrate(print.config.machine_min_extruding_rate.values[1]);
m_silent_time_estimator.set_minimum_travel_feedrate(print.config.machine_min_travel_rate.values[1]);
m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::X, print.config.machine_max_acceleration_x.values[1]);
m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Y, print.config.machine_max_acceleration_y.values[1]);
m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Z, print.config.machine_max_acceleration_z.values[1]);
m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::E, print.config.machine_max_acceleration_e.values[1]);
m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::X, print.config.machine_max_feedrate_x.values[1]);
m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Y, print.config.machine_max_feedrate_y.values[1]);
m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Z, print.config.machine_max_feedrate_z.values[1]);
m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::E, print.config.machine_max_feedrate_e.values[1]);
m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::X, print.config.machine_max_jerk_x.values[1]);
m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Y, print.config.machine_max_jerk_y.values[1]);
m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Z, print.config.machine_max_jerk_z.values[1]);
m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::E, print.config.machine_max_jerk_e.values[1]);
}
}
// resets analyzer
m_analyzer.reset();
m_enable_analyzer = preview_data != nullptr;
@ -463,9 +477,10 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
// How many times will be change_layer() called?
// change_layer() in turn increments the progress bar status.
m_layer_count = 0;
PrintObjectPtrs printable_objects = print.get_printable_objects();
if (print.config.complete_objects.value) {
// Add each of the object's layers separately.
for (auto object : print.objects) {
for (auto object : printable_objects) {
std::vector<coordf_t> zs;
zs.reserve(object->layers.size() + object->support_layers.size());
for (auto layer : object->layers)
@ -478,7 +493,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
} else {
// Print all objects with the same print_z together.
std::vector<coordf_t> zs;
for (auto object : print.objects) {
for (auto object : printable_objects) {
zs.reserve(zs.size() + object->layers.size() + object->support_layers.size());
for (auto layer : object->layers)
zs.push_back(layer->print_z);
@ -497,8 +512,8 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
{
// get the minimum cross-section used in the print
std::vector<double> mm3_per_mm;
for (auto object : print.objects) {
for (size_t region_id = 0; region_id < print.regions.size(); ++ region_id) {
for (auto object : printable_objects) {
for (size_t region_id = 0; region_id < print.regions.size(); ++region_id) {
auto region = print.regions[region_id];
for (auto layer : object->layers) {
auto layerm = layer->regions[region_id];
@ -558,7 +573,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
_write(file, "\n");
}
// Write some terse information on the slicing parameters.
const PrintObject *first_object = print.objects.front();
const PrintObject *first_object = printable_objects.front();
const double layer_height = first_object->config.layer_height.value;
const double first_layer_height = first_object->config.first_layer_height.get_abs_value(layer_height);
for (size_t region_id = 0; region_id < print.regions.size(); ++ region_id) {
@ -587,13 +602,14 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
size_t initial_print_object_id = 0;
bool has_wipe_tower = false;
if (print.config.complete_objects.value) {
// Find the 1st printing object, find its tool ordering and the initial extruder ID.
for (; initial_print_object_id < print.objects.size(); ++initial_print_object_id) {
tool_ordering = ToolOrdering(*print.objects[initial_print_object_id], initial_extruder_id);
if ((initial_extruder_id = tool_ordering.first_extruder()) != (unsigned int)-1)
break;
}
} else {
// Find the 1st printing object, find its tool ordering and the initial extruder ID.
for (; initial_print_object_id < printable_objects.size(); ++initial_print_object_id) {
tool_ordering = ToolOrdering(*printable_objects[initial_print_object_id], initial_extruder_id);
if ((initial_extruder_id = tool_ordering.first_extruder()) != (unsigned int)-1)
break;
}
}
else {
// Find tool ordering for all the objects at once, and the initial extruder ID.
// If the tool ordering has been pre-calculated by Print class for wipe tower already, reuse it.
tool_ordering = print.m_tool_ordering.empty() ?
@ -667,7 +683,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
// Collect outer contours of all objects over all layers.
// Discard objects only containing thin walls (offset would fail on an empty polygon).
Polygons islands;
for (const PrintObject *object : print.objects)
for (const PrintObject *object : printable_objects)
for (const Layer *layer : object->layers)
for (const ExPolygon &expoly : layer->slices.expolygons)
for (const Point &copy : object->_shifted_copies) {
@ -715,7 +731,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
if (print.config.complete_objects.value) {
// Print objects from the smallest to the tallest to avoid collisions
// when moving onto next object starting point.
std::vector<PrintObject*> objects(print.objects);
std::vector<PrintObject*> objects(printable_objects);
std::sort(objects.begin(), objects.end(), [](const PrintObject* po1, const PrintObject* po2) { return po1->size.z < po2->size.z; });
size_t finished_objects = 0;
for (size_t object_id = initial_print_object_id; object_id < objects.size(); ++ object_id) {
@ -776,7 +792,8 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
// Order objects using a nearest neighbor search.
std::vector<size_t> object_indices;
Points object_reference_points;
for (PrintObject *object : print.objects)
PrintObjectPtrs printable_objects = print.get_printable_objects();
for (PrintObject *object : printable_objects)
object_reference_points.push_back(object->_shifted_copies.front());
Slic3r::Geometry::chained_path(object_reference_points, object_indices);
// Sort layers by Z.
@ -790,7 +807,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
// Verify, whether the print overaps the priming extrusions.
BoundingBoxf bbox_print(get_print_extrusions_extents(print));
coordf_t twolayers_printz = ((layers_to_print.size() == 1) ? layers_to_print.front() : layers_to_print[1]).first + EPSILON;
for (const PrintObject *print_object : print.objects)
for (const PrintObject *print_object : printable_objects)
bbox_print.merge(get_print_object_extrusions_extents(*print_object, twolayers_printz));
bbox_print.merge(get_wipe_tower_extrusions_extents(print, twolayers_printz));
BoundingBoxf bbox_prime(get_wipe_tower_priming_extrusions_extents(print));
@ -853,9 +870,9 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
_write(file, m_writer.postamble());
// calculates estimated printing time
m_normal_time_estimator.calculate_time();
m_normal_time_estimator.calculate_time(false);
if (m_silent_time_estimator_enabled)
m_silent_time_estimator.calculate_time();
m_silent_time_estimator.calculate_time(false);
// Get filament stats.
print.filament_stats.clear();

View file

@ -67,11 +67,13 @@ ToolOrdering::ToolOrdering(const PrintObject &object, unsigned int first_extrude
ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool prime_multi_material)
{
m_print_config_ptr = &print.config;
PrintObjectPtrs objects = print.get_printable_objects();
// Initialize the print layers for all objects and all layers.
coordf_t object_bottom_z = 0.;
{
std::vector<coordf_t> zs;
for (auto object : print.objects) {
for (auto object : objects) {
zs.reserve(zs.size() + object->layers.size() + object->support_layers.size());
for (auto layer : object->layers)
zs.emplace_back(layer->print_z);
@ -84,7 +86,7 @@ ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool
}
// Collect extruders reuqired to print the layers.
for (auto object : print.objects)
for (auto object : objects)
this->collect_extruders(*object);
// Reorder the extruders to minimize tool switches.
@ -440,19 +442,18 @@ bool WipingExtrusions::is_overriddable(const ExtrusionEntityCollection& eec, con
// Following function iterates through all extrusions on the layer, remembers those that could be used for wiping after toolchange
// and returns volume that is left to be wiped on the wipe tower.
float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int new_extruder, float volume_to_wipe)
float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int old_extruder, unsigned int new_extruder, float volume_to_wipe)
{
const LayerTools& lt = *m_layer_tools;
const float min_infill_volume = 0.f; // ignore infill with smaller volume than this
if (print.config.filament_soluble.get_at(new_extruder))
return volume_to_wipe; // Soluble filament cannot be wiped in a random infill
if (print.config.filament_soluble.get_at(old_extruder) || print.config.filament_soluble.get_at(new_extruder))
return volume_to_wipe; // Soluble filament cannot be wiped in a random infill, neither the filament after it
// we will sort objects so that dedicated for wiping are at the beginning:
PrintObjectPtrs object_list = print.objects;
PrintObjectPtrs object_list = print.get_printable_objects();
std::sort(object_list.begin(), object_list.end(), [](const PrintObject* a, const PrintObject* b) { return a->config.wipe_into_objects; });
// We will now iterate through
// - first the dedicated objects to mark perimeters or infills (depending on infill_first)
// - second through the dedicated ones again to mark infills or perimeters (depending on infill_first)
@ -546,7 +547,8 @@ void WipingExtrusions::ensure_perimeters_infills_order(const Print& print)
unsigned int first_nonsoluble_extruder = first_nonsoluble_extruder_on_layer(print.config);
unsigned int last_nonsoluble_extruder = last_nonsoluble_extruder_on_layer(print.config);
for (const PrintObject* object : print.objects) {
PrintObjectPtrs printable_objects = print.get_printable_objects();
for (const PrintObject* object : printable_objects) {
// Finds this layer:
auto this_layer_it = std::find_if(object->layers.begin(), object->layers.end(), [&lt](const Layer* lay) { return std::abs(lt.print_z - lay->print_z)<EPSILON; });
if (this_layer_it == object->layers.end())

View file

@ -28,7 +28,7 @@ public:
// This function goes through all infill entities, decides which ones will be used for wiping and
// marks them by the extruder id. Returns volume that remains to be wiped on the wipe tower:
float mark_wiping_extrusions(const Print& print, unsigned int new_extruder, float volume_to_wipe);
float mark_wiping_extrusions(const Print& print, unsigned int old_extruder, unsigned int new_extruder, float volume_to_wipe);
void ensure_perimeters_infills_order(const Print& print);
@ -66,8 +66,10 @@ public:
wipe_tower_partitions(0),
wipe_tower_layer_height(0.) {}
bool operator< (const LayerTools &rhs) const { return print_z - EPSILON < rhs.print_z; }
bool operator==(const LayerTools &rhs) const { return std::abs(print_z - rhs.print_z) < EPSILON; }
// Changing these operators to epsilon version can make a problem in cases where support and object layers get close to each other.
// In case someone tries to do it, make sure you know what you're doing and test it properly (slice multiple objects at once with supports).
bool operator< (const LayerTools &rhs) const { return print_z < rhs.print_z; }
bool operator==(const LayerTools &rhs) const { return print_z == rhs.print_z; }
bool is_extruder_order(unsigned int a, unsigned int b) const;

View file

@ -490,7 +490,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
// box_coordinates cleaning_box(xy(0.5f, - 1.5f), m_wipe_tower_width, wipe_area);
const float prime_section_width = std::min(240.f / tools.size(), 60.f);
box_coordinates cleaning_box(xy(5.f, 0.f), prime_section_width, 100.f);
box_coordinates cleaning_box(xy(5.f, 0.01f + m_perimeter_width/2.f), prime_section_width, 100.f);
PrusaMultiMaterial::Writer writer(m_layer_height, m_perimeter_width);
writer.set_extrusion_flow(m_extrusion_flow)

View file

@ -79,7 +79,6 @@ namespace Slic3r {
}
GCodeTimeEstimator::Block::Block()
: st_synchronized(false)
{
}
@ -196,11 +195,14 @@ namespace Slic3r {
}
}
void GCodeTimeEstimator::calculate_time()
void GCodeTimeEstimator::calculate_time(bool start_from_beginning)
{
PROFILE_FUNC();
_reset_time();
_set_blocks_st_synchronize(false);
if (start_from_beginning)
{
_reset_time();
_last_st_synchronized_block_id = -1;
}
_calculate_time();
#if ENABLE_MOVE_STATS
@ -414,7 +416,10 @@ namespace Slic3r {
void GCodeTimeEstimator::set_acceleration(float acceleration_mm_sec2)
{
_state.acceleration = acceleration_mm_sec2;
_state.acceleration = (_state.max_acceleration == 0) ?
acceleration_mm_sec2 :
// Clamp the acceleration with the maximum.
std::min(_state.max_acceleration, acceleration_mm_sec2);
}
float GCodeTimeEstimator::get_acceleration() const
@ -422,6 +427,18 @@ namespace Slic3r {
return _state.acceleration;
}
void GCodeTimeEstimator::set_max_acceleration(float acceleration_mm_sec2)
{
_state.max_acceleration = acceleration_mm_sec2;
if (acceleration_mm_sec2 > 0)
_state.acceleration = acceleration_mm_sec2;
}
float GCodeTimeEstimator::get_max_acceleration() const
{
return _state.max_acceleration;
}
void GCodeTimeEstimator::set_retract_acceleration(float acceleration_mm_sec2)
{
_state.retract_acceleration = acceleration_mm_sec2;
@ -469,6 +486,7 @@ namespace Slic3r {
GCodeFlavor GCodeTimeEstimator::get_dialect() const
{
PROFILE_FUNC();
return _state.dialect;
}
@ -519,6 +537,7 @@ namespace Slic3r {
void GCodeTimeEstimator::add_additional_time(float timeSec)
{
PROFILE_FUNC();
_state.additional_time += timeSec;
}
@ -540,6 +559,9 @@ namespace Slic3r {
set_e_local_positioning_type(Absolute);
set_feedrate(DEFAULT_FEEDRATE);
// Setting the maximum acceleration to zero means that the there is no limit and the G-code
// is allowed to set excessive values.
set_max_acceleration(0);
set_acceleration(DEFAULT_ACCELERATION);
set_retract_acceleration(DEFAULT_RETRACT_ACCELERATION);
set_minimum_feedrate(DEFAULT_MINIMUM_FEEDRATE);
@ -593,6 +615,8 @@ namespace Slic3r {
reset_g1_line_id();
_g1_line_ids.clear();
_last_st_synchronized_block_id = -1;
}
void GCodeTimeEstimator::_reset_time()
@ -605,26 +629,19 @@ namespace Slic3r {
_blocks.clear();
}
void GCodeTimeEstimator::_set_blocks_st_synchronize(bool state)
{
for (Block& block : _blocks)
{
block.st_synchronized = state;
}
}
void GCodeTimeEstimator::_calculate_time()
{
PROFILE_FUNC();
_forward_pass();
_reverse_pass();
_recalculate_trapezoids();
_time += get_additional_time();
for (Block& block : _blocks)
for (int i = _last_st_synchronized_block_id + 1; i < (int)_blocks.size(); ++i)
{
if (block.st_synchronized)
continue;
Block& block = _blocks[i];
#if ENABLE_MOVE_STATS
float block_time = 0.0f;
@ -647,6 +664,8 @@ namespace Slic3r {
block.elapsed_time = _time;
#endif // ENABLE_MOVE_STATS
}
_last_st_synchronized_block_id = _blocks.size() - 1;
}
void GCodeTimeEstimator::_process_gcode_line(GCodeReader&, const GCodeReader::GCodeLine& line)
@ -782,6 +801,7 @@ namespace Slic3r {
void GCodeTimeEstimator::_processG1(const GCodeReader::GCodeLine& line)
{
PROFILE_FUNC();
increment_g1_line_id();
// updates axes positions from line
@ -979,6 +999,7 @@ namespace Slic3r {
void GCodeTimeEstimator::_processG4(const GCodeReader::GCodeLine& line)
{
PROFILE_FUNC();
GCodeFlavor dialect = get_dialect();
float value;
@ -1000,31 +1021,37 @@ namespace Slic3r {
void GCodeTimeEstimator::_processG20(const GCodeReader::GCodeLine& line)
{
PROFILE_FUNC();
set_units(Inches);
}
void GCodeTimeEstimator::_processG21(const GCodeReader::GCodeLine& line)
{
PROFILE_FUNC();
set_units(Millimeters);
}
void GCodeTimeEstimator::_processG28(const GCodeReader::GCodeLine& line)
{
PROFILE_FUNC();
// TODO
}
void GCodeTimeEstimator::_processG90(const GCodeReader::GCodeLine& line)
{
PROFILE_FUNC();
set_global_positioning_type(Absolute);
}
void GCodeTimeEstimator::_processG91(const GCodeReader::GCodeLine& line)
{
PROFILE_FUNC();
set_global_positioning_type(Relative);
}
void GCodeTimeEstimator::_processG92(const GCodeReader::GCodeLine& line)
{
PROFILE_FUNC();
float lengthsScaleFactor = (get_units() == Inches) ? INCHES_TO_MM : 1.0f;
bool anyFound = false;
@ -1065,26 +1092,31 @@ namespace Slic3r {
void GCodeTimeEstimator::_processM1(const GCodeReader::GCodeLine& line)
{
PROFILE_FUNC();
_simulate_st_synchronize();
}
void GCodeTimeEstimator::_processM82(const GCodeReader::GCodeLine& line)
{
PROFILE_FUNC();
set_e_local_positioning_type(Absolute);
}
void GCodeTimeEstimator::_processM83(const GCodeReader::GCodeLine& line)
{
PROFILE_FUNC();
set_e_local_positioning_type(Relative);
}
void GCodeTimeEstimator::_processM109(const GCodeReader::GCodeLine& line)
{
PROFILE_FUNC();
// TODO
}
void GCodeTimeEstimator::_processM201(const GCodeReader::GCodeLine& line)
{
PROFILE_FUNC();
GCodeFlavor dialect = get_dialect();
// see http://reprap.org/wiki/G-code#M201:_Set_max_printing_acceleration
@ -1105,6 +1137,7 @@ namespace Slic3r {
void GCodeTimeEstimator::_processM203(const GCodeReader::GCodeLine& line)
{
PROFILE_FUNC();
GCodeFlavor dialect = get_dialect();
// see http://reprap.org/wiki/G-code#M203:_Set_maximum_feedrate
@ -1129,6 +1162,7 @@ namespace Slic3r {
void GCodeTimeEstimator::_processM204(const GCodeReader::GCodeLine& line)
{
PROFILE_FUNC();
float value;
if (line.has_value('S', value))
set_acceleration(value);
@ -1139,6 +1173,7 @@ namespace Slic3r {
void GCodeTimeEstimator::_processM205(const GCodeReader::GCodeLine& line)
{
PROFILE_FUNC();
if (line.has_x())
{
float max_jerk = line.x();
@ -1165,6 +1200,7 @@ namespace Slic3r {
void GCodeTimeEstimator::_processM221(const GCodeReader::GCodeLine& line)
{
PROFILE_FUNC();
float value_s;
float value_t;
if (line.has_value('S', value_s) && !line.has_value('T', value_t))
@ -1173,6 +1209,7 @@ namespace Slic3r {
void GCodeTimeEstimator::_processM566(const GCodeReader::GCodeLine& line)
{
PROFILE_FUNC();
if (line.has_x())
set_axis_max_jerk(X, line.x() * MMMIN_TO_MMSEC);
@ -1188,19 +1225,17 @@ namespace Slic3r {
void GCodeTimeEstimator::_simulate_st_synchronize()
{
PROFILE_FUNC();
_calculate_time();
_set_blocks_st_synchronize(true);
}
void GCodeTimeEstimator::_forward_pass()
{
PROFILE_FUNC();
if (_blocks.size() > 1)
{
for (unsigned int i = 0; i < (unsigned int)_blocks.size() - 1; ++i)
{
if (_blocks[i].st_synchronized || _blocks[i + 1].st_synchronized)
continue;
for (int i = _last_st_synchronized_block_id + 1; i < (int)_blocks.size() - 1; ++i)
{
_planner_forward_pass_kernel(_blocks[i], _blocks[i + 1]);
}
}
@ -1208,13 +1243,11 @@ namespace Slic3r {
void GCodeTimeEstimator::_reverse_pass()
{
PROFILE_FUNC();
if (_blocks.size() > 1)
{
for (int i = (int)_blocks.size() - 1; i >= 1; --i)
for (int i = (int)_blocks.size() - 1; i >= _last_st_synchronized_block_id + 2; --i)
{
if (_blocks[i - 1].st_synchronized || _blocks[i].st_synchronized)
continue;
_planner_reverse_pass_kernel(_blocks[i - 1], _blocks[i]);
}
}
@ -1222,6 +1255,7 @@ namespace Slic3r {
void GCodeTimeEstimator::_planner_forward_pass_kernel(Block& prev, Block& curr)
{
PROFILE_FUNC();
// If the previous block is an acceleration block, but it is not long enough to complete the
// full speed change within the block, we need to adjust the entry speed accordingly. Entry
// speeds have already been reset, maximized, and reverse planned by reverse planner.
@ -1262,13 +1296,13 @@ namespace Slic3r {
void GCodeTimeEstimator::_recalculate_trapezoids()
{
PROFILE_FUNC();
Block* curr = nullptr;
Block* next = nullptr;
for (Block& b : _blocks)
for (int i = _last_st_synchronized_block_id + 1; i < (int)_blocks.size(); ++i)
{
if (b.st_synchronized)
continue;
Block& b = _blocks[i];
curr = next;
next = &b;

View file

@ -72,6 +72,8 @@ namespace Slic3r {
Axis axis[Num_Axis];
float feedrate; // mm/s
float acceleration; // mm/s^2
// hard limit for the acceleration, to which the firmware will clamp.
float max_acceleration; // mm/s^2
float retract_acceleration; // mm/s^2
float additional_time; // s
float minimum_feedrate; // mm/s
@ -142,8 +144,6 @@ namespace Slic3r {
Trapezoid trapezoid;
float elapsed_time;
bool st_synchronized;
Block();
// Returns the length of the move covered by this block, in mm
@ -209,6 +209,8 @@ namespace Slic3r {
BlocksList _blocks;
// Map between g1 line id and blocks id, used to speed up export of remaining times
G1LineIdToBlockIdMap _g1_line_ids;
// Index of the last block already st_synchronized
int _last_st_synchronized_block_id;
float _time; // s
#if ENABLE_MOVE_STATS
@ -225,7 +227,10 @@ namespace Slic3r {
void add_gcode_block(const std::string &str) { this->add_gcode_block(str.c_str()); }
// Calculates the time estimate from the gcode lines added using add_gcode_line() or add_gcode_block()
void calculate_time();
// start_from_beginning:
// if set to true all blocks will be used to calculate the time estimate,
// if set to false only the blocks not yet processed will be used and the calculated time will be added to the current calculated time
void calculate_time(bool start_from_beginning);
// Calculates the time estimate from the given gcode in string format
void calculate_time_from_text(const std::string& gcode);
@ -263,6 +268,10 @@ namespace Slic3r {
void set_acceleration(float acceleration_mm_sec2);
float get_acceleration() const;
// Maximum acceleration for the machine. The firmware simulator will clamp the M204 Sxxx to this maximum.
void set_max_acceleration(float acceleration_mm_sec2);
float get_max_acceleration() const;
void set_retract_acceleration(float acceleration_mm_sec2);
float get_retract_acceleration() const;
@ -314,8 +323,6 @@ namespace Slic3r {
void _reset_time();
void _reset_blocks();
void _set_blocks_st_synchronize(bool state);
// Calculates the time estimate
void _calculate_time();

View file

@ -18,7 +18,9 @@ void GCodeWriter::apply_print_config(const PrintConfig &print_config)
{
this->config.apply(print_config, true);
m_extrusion_axis = this->config.get_extrusion_axis();
this->m_single_extruder_multi_material = print_config.single_extruder_multi_material.value;
m_single_extruder_multi_material = print_config.single_extruder_multi_material.value;
m_max_acceleration = (print_config.gcode_flavor.value == gcfMarlin) ?
print_config.machine_max_acceleration_extruding.values.front() : 0;
}
void GCodeWriter::set_extruders(const std::vector<unsigned int> &extruder_ids)
@ -85,7 +87,7 @@ std::string GCodeWriter::set_temperature(unsigned int temperature, bool wait, in
}
gcode << temperature;
if (tool != -1 &&
( (this->multiple_extruders && ! this->m_single_extruder_multi_material) ||
( (this->multiple_extruders && ! m_single_extruder_multi_material) ||
FLAVOR_IS(gcfMakerWare) || FLAVOR_IS(gcfSailfish)) ) {
gcode << " T" << tool;
}
@ -170,6 +172,10 @@ std::string GCodeWriter::set_fan(unsigned int speed, bool dont_save)
std::string GCodeWriter::set_acceleration(unsigned int acceleration)
{
// Clamp the acceleration to the allowed maximum.
if (m_max_acceleration > 0 && acceleration > m_max_acceleration)
acceleration = m_max_acceleration;
if (acceleration == 0 || acceleration == m_last_acceleration)
return std::string();

View file

@ -18,7 +18,7 @@ public:
GCodeWriter() :
multiple_extruders(false), m_extrusion_axis("E"), m_extruder(nullptr),
m_single_extruder_multi_material(false),
m_last_acceleration(0), m_last_fan_speed(0),
m_last_acceleration(0), m_max_acceleration(0), m_last_fan_speed(0),
m_last_bed_temperature(0), m_last_bed_temperature_reached(true),
m_lifted(0)
{}
@ -74,6 +74,9 @@ private:
bool m_single_extruder_multi_material;
Extruder* m_extruder;
unsigned int m_last_acceleration;
// Limit for setting the acceleration, to respect the machine limits set for the Marlin firmware.
// If set to zero, the limit is not in action.
unsigned int m_max_acceleration;
unsigned int m_last_fan_speed;
unsigned int m_last_bed_temperature;
bool m_last_bed_temperature_reached;

View file

@ -530,12 +530,12 @@ bool arrange(Model &model, coordf_t dist, const Slic3r::BoundingBoxf* bb,
// arranger.useMinimumBoundigBoxRotation();
pcfg.rotations = { 0.0 };
// Magic: we will specify what is the goal of arrangement...
// In this case we override the default object to make the larger items go
// into the center of the pile and smaller items orbit it so the resulting
// pile has a circle-like shape. This is good for the print bed's heat
// profile. We alse sacrafice a bit of pack efficiency for this to work. As
// a side effect, the arrange procedure is a lot faster (we do not need to
// Magic: we will specify what is the goal of arrangement... In this case
// we override the default object function to make the larger items go into
// the center of the pile and smaller items orbit it so the resulting pile
// has a circle-like shape. This is good for the print bed's heat profile.
// We alse sacrafice a bit of pack efficiency for this to work. As a side
// effect, the arrange procedure is a lot faster (we do not need to
// calculate the convex hulls)
pcfg.object_function = [bin, hasbin](
NfpPlacer::Pile pile, // The currently arranged pile
@ -1235,6 +1235,59 @@ void ModelObject::split(ModelObjectPtrs* new_objects)
return;
}
void ModelObject::check_instances_print_volume_state(const BoundingBoxf3& print_volume)
{
for (ModelVolume* vol : this->volumes)
{
if (!vol->modifier)
{
for (ModelInstance* inst : this->instances)
{
BoundingBoxf3 bb;
double c = cos(inst->rotation);
double s = sin(inst->rotation);
for (int f = 0; f < vol->mesh.stl.stats.number_of_facets; ++f)
{
const stl_facet& facet = vol->mesh.stl.facet_start[f];
for (int i = 0; i < 3; ++i)
{
// original point
const stl_vertex& v = facet.vertex[i];
Pointf3 p((double)v.x, (double)v.y, (double)v.z);
// scale
p.x *= inst->scaling_factor;
p.y *= inst->scaling_factor;
p.z *= inst->scaling_factor;
// rotate Z
double x = p.x;
double y = p.y;
p.x = c * x - s * y;
p.y = s * x + c * y;
// translate
p.x += inst->offset.x;
p.y += inst->offset.y;
bb.merge(p);
}
}
if (print_volume.contains(bb))
inst->print_volume_state = ModelInstance::PVS_Inside;
else if (print_volume.intersects(bb))
inst->print_volume_state = ModelInstance::PVS_Partly_Outside;
else
inst->print_volume_state = ModelInstance::PVS_Fully_Outside;
}
}
}
}
void ModelObject::print_info() const
{
using namespace std;

View file

@ -132,6 +132,8 @@ public:
void cut(coordf_t z, Model* model) const;
void split(ModelObjectPtrs* new_objects);
void check_instances_print_volume_state(const BoundingBoxf3& print_volume);
// Print object statistics to console.
void print_info() const;
@ -197,12 +199,24 @@ private:
// Knows the affine transformation of an object.
class ModelInstance
{
friend class ModelObject;
public:
enum EPrintVolumeState : unsigned char
{
PVS_Inside,
PVS_Partly_Outside,
PVS_Fully_Outside,
Num_BedStates
};
friend class ModelObject;
double rotation; // Rotation around the Z axis, in radians around mesh center point
double scaling_factor;
Pointf offset; // in unscaled coordinates
// flag showing the position of this instance with respect to the print volume (set by Print::validate() using ModelObject::check_instances_print_volume_state())
EPrintVolumeState print_volume_state;
ModelObject* get_object() const { return this->object; }
// To be called on an external mesh
@ -213,14 +227,16 @@ public:
BoundingBoxf3 transform_bounding_box(const BoundingBoxf3 &bbox, bool dont_translate = false) const;
// To be called on an external polygon. It does not translate the polygon, only rotates and scales.
void transform_polygon(Polygon* polygon) const;
bool is_printable() const { return print_volume_state == PVS_Inside; }
private:
// Parent object, owning this instance.
ModelObject* object;
ModelInstance(ModelObject *object) : rotation(0), scaling_factor(1), object(object) {}
ModelInstance(ModelObject *object) : rotation(0), scaling_factor(1), object(object), print_volume_state(PVS_Inside) {}
ModelInstance(ModelObject *object, const ModelInstance &other) :
rotation(other.rotation), scaling_factor(other.scaling_factor), offset(other.offset), object(object) {}
rotation(other.rotation), scaling_factor(other.scaling_factor), offset(other.offset), object(object), print_volume_state(PVS_Inside) {}
};

View file

@ -75,6 +75,13 @@ bool Print::reload_model_instances()
return invalidated;
}
PrintObjectPtrs Print::get_printable_objects() const
{
PrintObjectPtrs printable_objects(this->objects);
printable_objects.erase(std::remove_if(printable_objects.begin(), printable_objects.end(), [](PrintObject* o) { return !o->is_printable(); }), printable_objects.end());
return printable_objects;
}
PrintRegion* Print::add_region()
{
regions.push_back(new PrintRegion(this));
@ -538,11 +545,17 @@ std::string Print::validate() const
BoundingBoxf3 print_volume(Pointf3(unscale(bed_box_2D.min.x), unscale(bed_box_2D.min.y), 0.0), Pointf3(unscale(bed_box_2D.max.x), unscale(bed_box_2D.max.y), config.max_print_height));
// Allow the objects to protrude below the print bed, only the part of the object above the print bed will be sliced.
print_volume.min.z = -1e10;
unsigned int printable_count = 0;
for (PrintObject *po : this->objects) {
if (!print_volume.contains(po->model_object()->tight_bounding_box(false)))
return L("Some objects are outside of the print volume.");
po->model_object()->check_instances_print_volume_state(print_volume);
po->reload_model_instances();
if (po->is_printable())
++printable_count;
}
if (printable_count == 0)
return L("All objects are outside of the print volume.");
if (this->config.complete_objects) {
// Check horizontal clearance.
{
@ -862,8 +875,9 @@ void Print::_make_skirt()
// prepended to the first 'n' layers (with 'n' = skirt_height).
// $skirt_height_z in this case is the highest possible skirt height for safety.
coordf_t skirt_height_z = 0.;
for (const PrintObject *object : this->objects) {
size_t skirt_layers = this->has_infinite_skirt() ?
PrintObjectPtrs printable_objects = get_printable_objects();
for (const PrintObject *object : printable_objects) {
size_t skirt_layers = this->has_infinite_skirt() ?
object->layer_count() :
std::min(size_t(this->config.skirt_height.value), object->layer_count());
skirt_height_z = std::max(skirt_height_z, object->layers[skirt_layers-1]->print_z);
@ -871,7 +885,7 @@ void Print::_make_skirt()
// Collect points from all layers contained in skirt height.
Points points;
for (const PrintObject *object : this->objects) {
for (const PrintObject *object : printable_objects) {
Points object_points;
// Get object layers up to skirt_height_z.
for (const Layer *layer : object->layers) {
@ -984,7 +998,8 @@ void Print::_make_brim()
// Brim is only printed on first layer and uses perimeter extruder.
Flow flow = this->brim_flow();
Polygons islands;
for (PrintObject *object : this->objects) {
PrintObjectPtrs printable_objects = get_printable_objects();
for (PrintObject *object : printable_objects) {
Polygons object_islands;
for (ExPolygon &expoly : object->layers.front()->slices.expolygons)
object_islands.push_back(expoly.contour);
@ -1136,7 +1151,7 @@ void Print::_make_wipe_tower()
float volume_to_wipe = wipe_volumes[current_extruder_id][extruder_id]; // total volume to wipe after this toolchange
// try to assign some infills/objects for the wiping:
volume_to_wipe = layer_tools.wiping_extrusions().mark_wiping_extrusions(*this, extruder_id, wipe_volumes[current_extruder_id][extruder_id]);
volume_to_wipe = layer_tools.wiping_extrusions().mark_wiping_extrusions(*this, current_extruder_id, extruder_id, wipe_volumes[current_extruder_id][extruder_id]);
wipe_tower.plan_toolchange(layer_tools.print_z, layer_tools.wipe_tower_layer_height, current_extruder_id, extruder_id, first_layer && extruder_id == m_tool_ordering.all_extruders().back(), volume_to_wipe);
current_extruder_id = extruder_id;

View file

@ -209,6 +209,8 @@ public:
void combine_infill();
void _generate_support_material();
bool is_printable() const { return !this->_shifted_copies.empty(); }
private:
Print* _print;
ModelObject* _model_object;
@ -263,6 +265,8 @@ public:
void reload_object(size_t idx);
bool reload_model_instances();
PrintObjectPtrs get_printable_objects() const;
// methods for handling regions
PrintRegion* get_region(size_t idx) { return regions.at(idx); }
const PrintRegion* get_region(size_t idx) const { return regions.at(idx); }

View file

@ -83,8 +83,6 @@ template<> class FilePrinter<FilePrinterFormat::PNG> {
std::stringstream second;
Layer() {}
Layer(const Raster::Resolution& res, const Raster::PixelDim& pd):
first(res, pd) {}
Layer(const Layer&) = delete;
Layer(Layer&& m):
@ -129,13 +127,17 @@ template<> class FilePrinter<FilePrinterFormat::PNG> {
+layerh_str+"+printer=DWARF3\n";
}
// Change this to TOP_LEFT if you want correct PNG orientation
static const Raster::Origin ORIGIN = Raster::Origin::BOTTOM_LEFT;
public:
inline FilePrinter(double width_mm, double height_mm,
unsigned width_px, unsigned height_px,
double exp_time, double exp_time_first):
res_(width_px, height_px), exp_time_s_(exp_time),
exp_time_first_s_(exp_time_first),
pxdim_(width_mm/width_px, height_mm/height_px)
res_(width_px, height_px),
pxdim_(width_mm/width_px, height_mm/height_px),
exp_time_s_(exp_time),
exp_time_first_s_(exp_time_first)
{
}
@ -157,12 +159,12 @@ public:
inline void beginLayer(unsigned lyr) {
if(layers_rst_.size() <= lyr) layers_rst_.resize(lyr+1);
layers_rst_[lyr].first.reset(res_, pxdim_);
layers_rst_[lyr].first.reset(res_, pxdim_, ORIGIN);
}
inline void beginLayer() {
layers_rst_.emplace_back();
layers_rst_.front().first.reset(res_, pxdim_);
layers_rst_.front().first.reset(res_, pxdim_, ORIGIN);
}
inline void finishLayer(unsigned lyr_id) {
@ -337,13 +339,13 @@ void print_to(Print& print,
});
});
if(print.has_support_material() && layer_id > 0) {
/*if(print.has_support_material() && layer_id > 0) {
BOOST_LOG_TRIVIAL(warning) << "support material for layer "
<< layer_id
<< " defined but export is "
"not yet implemented.";
}
}*/
}

View file

@ -102,7 +102,10 @@ bool PrintObject::reload_model_instances()
Points copies;
copies.reserve(this->_model_object->instances.size());
for (const ModelInstance *mi : this->_model_object->instances)
copies.emplace_back(Point::new_scale(mi->offset.x, mi->offset.y));
{
if (mi->is_printable())
copies.emplace_back(Point::new_scale(mi->offset.x, mi->offset.y));
}
return this->set_copies(copies);
}
@ -291,6 +294,9 @@ bool PrintObject::has_support_material() const
void PrintObject::_prepare_infill()
{
if (!this->is_printable())
return;
// This will assign a type (top/bottom/internal) to $layerm->slices.
// Then the classifcation of $layerm->slices is transfered onto
// the $layerm->fill_surfaces by clipping $layerm->fill_surfaces
@ -1177,8 +1183,8 @@ void PrintObject::_slice()
this->typed_slices = false;
#if 0
// Disable parallelization for debugging purposes.
#ifdef SLIC3R_PROFILE
// Disable parallelization so the Shiny profiler works
static tbb::task_scheduler_init *tbb_init = nullptr;
tbb_init = new tbb::task_scheduler_init(1);
#endif
@ -1442,6 +1448,9 @@ void PrintObject::_simplify_slices(double distance)
void PrintObject::_make_perimeters()
{
if (!this->is_printable())
return;
if (this->state.is_done(posPerimeters)) return;
this->state.set_started(posPerimeters);
@ -1550,6 +1559,9 @@ void PrintObject::_make_perimeters()
void PrintObject::_infill()
{
if (!this->is_printable())
return;
if (this->state.is_done(posInfill)) return;
this->state.set_started(posInfill);
@ -1954,6 +1966,9 @@ void PrintObject::combine_infill()
void PrintObject::_generate_support_material()
{
if (!this->is_printable())
return;
PrintObjectSupportMaterial support_material(this, PrintObject::slicing_parameters());
support_material.generate(*this);
}

View file

@ -45,9 +45,10 @@ private:
TRawRenderer raw_renderer_;
TRendererAA renderer_;
Origin o_;
std::function<void(agg::path_storage&)> flipy_ = [](agg::path_storage&) {};
public:
inline Impl(const Raster::Resolution& res, const Raster::PixelDim &pd,
Origin o = Origin::TOP_LEFT):
Origin o):
resolution_(res), pxdim_(pd),
buf_(res.pixels()),
rbuf_(reinterpret_cast<TPixelRenderer::value_type*>(buf_.data()),
@ -64,6 +65,10 @@ public:
// ras.gamma(agg::gamma_power(1.0));
clear();
if(o_ == Origin::TOP_LEFT) flipy_ = [this](agg::path_storage& path) {
path.flip_y(0, resolution_.height_px);
};
}
void draw(const ExPolygon &poly) {
@ -71,13 +76,12 @@ public:
agg::scanline_p8 scanlines;
auto&& path = to_path(poly.contour);
if(o_ == Origin::TOP_LEFT) path.flip_y(0, resolution_.height_px);
flipy_(path);
ras.add_path(path);
for(auto h : poly.holes) {
auto&& holepath = to_path(h);
if(o_ == Origin::TOP_LEFT)
holepath.flip_y(0, resolution_.height_px);
flipy_(holepath);
ras.add_path(holepath);
}
@ -92,6 +96,8 @@ public:
inline const Raster::Resolution resolution() { return resolution_; }
inline Origin origin() const /*noexcept*/ { return o_; }
private:
double getPx(const Point& p) {
return p.x * SCALING_FACTOR/pxdim_.w_mm;
@ -131,9 +137,15 @@ void Raster::reset(const Raster::Resolution &r, const Raster::PixelDim &pd)
{
// Free up the unneccessary memory and make sure it stays clear after
// an exception
impl_.reset();
auto o = impl_? impl_->origin() : Origin::TOP_LEFT;
reset(r, pd, o);
}
impl_.reset(new Impl(r, pd));
void Raster::reset(const Raster::Resolution &r, const Raster::PixelDim &pd,
Raster::Origin o)
{
impl_.reset();
impl_.reset(new Impl(r, pd, o));
}
void Raster::reset()

View file

@ -52,7 +52,7 @@ public:
/// Constructor taking the resolution and the pixel dimension.
explicit Raster(const Resolution& r, const PixelDim& pd,
Origin o = Origin::TOP_LEFT );
Origin o = Origin::BOTTOM_LEFT );
Raster();
Raster(const Raster& cpy) = delete;
Raster& operator=(const Raster& cpy) = delete;
@ -61,6 +61,7 @@ public:
/// Reallocated everything for the given resolution and pixel dimension.
void reset(const Resolution& r, const PixelDim& pd);
void reset(const Resolution& r, const PixelDim& pd, Origin o);
/**
* Release the allocated resources. Drawing in this state ends in

View file

@ -84,6 +84,8 @@ inline T next_highest_power_of_2(T v)
return ++ v;
}
extern std::string xml_escape(std::string text);
class PerlCallback {
public:
PerlCallback(void *sv) : m_callback(nullptr) { this->register_callback(sv); }

View file

@ -14,7 +14,7 @@
#include <boost/thread.hpp>
#define SLIC3R_FORK_NAME "Slic3r Prusa Edition"
#define SLIC3R_VERSION "1.41.0-alpha"
#define SLIC3R_VERSION "1.41.0-alpha2"
#define SLIC3R_BUILD "UNKNOWN"
typedef int32_t coord_t;

View file

@ -387,4 +387,31 @@ unsigned get_current_pid()
#endif
}
std::string xml_escape(std::string text)
{
std::string::size_type pos = 0;
for (;;)
{
pos = text.find_first_of("\"\'&<>", pos);
if (pos == std::string::npos)
break;
std::string replacement;
switch (text[pos])
{
case '\"': replacement = "&quot;"; break;
case '\'': replacement = "&apos;"; break;
case '&': replacement = "&amp;"; break;
case '<': replacement = "&lt;"; break;
case '>': replacement = "&gt;"; break;
default: break;
}
text.replace(pos, 1, replacement);
pos += replacement.size();
}
return text;
}
}; // namespace Slic3r

View file

@ -22,11 +22,6 @@
#include <tbb/parallel_for.h>
#include <tbb/spin_mutex.h>
#include <wx/bitmap.h>
#include <wx/dcmemory.h>
#include <wx/image.h>
#include <wx/settings.h>
#include <Eigen/Dense>
#include "GUI.hpp"
@ -540,8 +535,8 @@ double GLVolume::layer_height_texture_z_to_row_id() const
void GLVolume::generate_layer_height_texture(PrintObject *print_object, bool force)
{
GLTexture *tex = this->layer_height_texture.get();
if (tex == nullptr)
LayersTexture *tex = this->layer_height_texture.get();
if (tex == nullptr)
// No layer_height_texture is assigned to this GLVolume, therefore the layer height texture cannot be filled.
return;
@ -588,8 +583,8 @@ std::vector<int> GLVolumeCollection::load_object(
};
// Object will have a single common layer height texture for all volumes.
std::shared_ptr<GLTexture> layer_height_texture = std::make_shared<GLTexture>();
std::shared_ptr<LayersTexture> layer_height_texture = std::make_shared<LayersTexture>();
std::vector<int> volumes_idx;
for (int volume_idx = 0; volume_idx < int(model_object->volumes.size()); ++ volume_idx) {
const ModelVolume *model_volume = model_object->volumes[volume_idx];
@ -743,7 +738,7 @@ void GLVolumeCollection::render_legacy() const
glDisable(GL_BLEND);
}
bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config)
bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, ModelInstance::EPrintVolumeState* out_state)
{
if (config == nullptr)
return false;
@ -757,18 +752,31 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config)
// Allow the objects to protrude below the print bed
print_volume.min.z = -1e10;
bool contained = true;
ModelInstance::EPrintVolumeState state = ModelInstance::PVS_Inside;
bool all_contained = true;
for (GLVolume* volume : this->volumes)
{
if ((volume != nullptr) && !volume->is_modifier)
{
bool state = print_volume.contains(volume->transformed_bounding_box());
contained &= state;
volume->is_outside = !state;
const BoundingBoxf3& bb = volume->transformed_bounding_box();
bool contained = print_volume.contains(bb);
all_contained &= contained;
volume->is_outside = !contained;
if ((state == ModelInstance::PVS_Inside) && volume->is_outside)
state = ModelInstance::PVS_Fully_Outside;
if ((state == ModelInstance::PVS_Fully_Outside) && volume->is_outside && print_volume.intersects(bb))
state = ModelInstance::PVS_Partly_Outside;
}
}
return contained;
if (out_state != nullptr)
*out_state = state;
return all_contained;
}
void GLVolumeCollection::reset_outside_state()
@ -1576,246 +1584,8 @@ void _3DScene::point3_to_verts(const Point3& point, double width, double height,
thick_point_to_verts(point, width, height, volume);
}
_3DScene::LegendTexture _3DScene::s_legend_texture;
_3DScene::WarningTexture _3DScene::s_warning_texture;
GUI::GLCanvas3DManager _3DScene::s_canvas_mgr;
unsigned int _3DScene::TextureBase::finalize()
{
if ((m_tex_id == 0) && !m_data.empty()) {
// sends buffer to gpu
::glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
::glGenTextures(1, &m_tex_id);
::glBindTexture(GL_TEXTURE_2D, (GLuint)m_tex_id);
::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_tex_width, (GLsizei)m_tex_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)m_data.data());
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
::glBindTexture(GL_TEXTURE_2D, 0);
m_data.clear();
}
return (m_tex_width > 0 && m_tex_height > 0) ? m_tex_id : 0;
}
void _3DScene::TextureBase::_destroy_texture()
{
if (m_tex_id > 0)
{
::glDeleteTextures(1, &m_tex_id);
m_tex_id = 0;
m_tex_height = 0;
m_tex_width = 0;
}
m_data.clear();
}
const unsigned char _3DScene::WarningTexture::Background_Color[3] = { 9, 91, 134 };
const unsigned char _3DScene::WarningTexture::Opacity = 255;
// Generate a texture data, but don't load it into the GPU yet, as the GPU context may not yet be valid.
bool _3DScene::WarningTexture::generate(const std::string& msg)
{
// Mark the texture as released, but don't release the texture from the GPU yet.
m_tex_width = m_tex_height = 0;
m_data.clear();
if (msg.empty())
return false;
wxMemoryDC memDC;
// select default font
memDC.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
// calculates texture size
wxCoord w, h;
memDC.GetTextExtent(msg, &w, &h);
m_tex_width = (unsigned int)w;
m_tex_height = (unsigned int)h;
// generates bitmap
wxBitmap bitmap(m_tex_width, m_tex_height);
#if defined(__APPLE__) || defined(_MSC_VER)
bitmap.UseAlpha();
#endif
memDC.SelectObject(bitmap);
memDC.SetBackground(wxBrush(wxColour(Background_Color[0], Background_Color[1], Background_Color[2])));
memDC.Clear();
memDC.SetTextForeground(*wxWHITE);
// draw message
memDC.DrawText(msg, 0, 0);
memDC.SelectObject(wxNullBitmap);
// Convert the bitmap into a linear data ready to be loaded into the GPU.
{
wxImage image = bitmap.ConvertToImage();
image.SetMaskColour(Background_Color[0], Background_Color[1], Background_Color[2]);
// prepare buffer
m_data.assign(4 * m_tex_width * m_tex_height, 0);
for (unsigned int h = 0; h < m_tex_height; ++h)
{
unsigned int hh = h * m_tex_width;
unsigned char* px_ptr = m_data.data() + 4 * hh;
for (unsigned int w = 0; w < m_tex_width; ++w)
{
*px_ptr++ = image.GetRed(w, h);
*px_ptr++ = image.GetGreen(w, h);
*px_ptr++ = image.GetBlue(w, h);
*px_ptr++ = image.IsTransparent(w, h) ? 0 : Opacity;
}
}
}
return true;
}
const unsigned char _3DScene::LegendTexture::Squares_Border_Color[3] = { 64, 64, 64 };
const unsigned char _3DScene::LegendTexture::Background_Color[3] = { 9, 91, 134 };
const unsigned char _3DScene::LegendTexture::Opacity = 255;
// Generate a texture data, but don't load it into the GPU yet, as the GPU context may not yet be valid.
bool _3DScene::LegendTexture::generate(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors)
{
// Mark the texture as released, but don't release the texture from the GPU yet.
m_tex_width = m_tex_height = 0;
m_data.clear();
// collects items to render
auto title = _(preview_data.get_legend_title());
const GCodePreviewData::LegendItemsList& items = preview_data.get_legend_items(tool_colors);
unsigned int items_count = (unsigned int)items.size();
if (items_count == 0)
// nothing to render, return
return false;
wxMemoryDC memDC;
// select default font
memDC.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
// calculates texture size
wxCoord w, h;
memDC.GetTextExtent(title, &w, &h);
unsigned int title_width = (unsigned int)w;
unsigned int title_height = (unsigned int)h;
unsigned int max_text_width = 0;
unsigned int max_text_height = 0;
for (const GCodePreviewData::LegendItem& item : items)
{
memDC.GetTextExtent(GUI::from_u8(item.text), &w, &h);
max_text_width = std::max(max_text_width, (unsigned int)w);
max_text_height = std::max(max_text_height, (unsigned int)h);
}
m_tex_width = std::max(2 * Px_Border + title_width, 2 * (Px_Border + Px_Square_Contour) + Px_Square + Px_Text_Offset + max_text_width);
m_tex_height = 2 * (Px_Border + Px_Square_Contour) + title_height + Px_Title_Offset + items_count * Px_Square;
if (items_count > 1)
m_tex_height += (items_count - 1) * Px_Square_Contour;
// generates bitmap
wxBitmap bitmap(m_tex_width, m_tex_height);
#if defined(__APPLE__) || defined(_MSC_VER)
bitmap.UseAlpha();
#endif
memDC.SelectObject(bitmap);
memDC.SetBackground(wxBrush(wxColour(Background_Color[0], Background_Color[1], Background_Color[2])));
memDC.Clear();
memDC.SetTextForeground(*wxWHITE);
// draw title
unsigned int title_x = Px_Border;
unsigned int title_y = Px_Border;
memDC.DrawText(title, title_x, title_y);
// draw icons contours as background
unsigned int squares_contour_x = Px_Border;
unsigned int squares_contour_y = Px_Border + title_height + Px_Title_Offset;
unsigned int squares_contour_width = Px_Square + 2 * Px_Square_Contour;
unsigned int squares_contour_height = items_count * Px_Square + 2 * Px_Square_Contour;
if (items_count > 1)
squares_contour_height += (items_count - 1) * Px_Square_Contour;
wxColour color(Squares_Border_Color[0], Squares_Border_Color[1], Squares_Border_Color[2]);
wxPen pen(color);
wxBrush brush(color);
memDC.SetPen(pen);
memDC.SetBrush(brush);
memDC.DrawRectangle(wxRect(squares_contour_x, squares_contour_y, squares_contour_width, squares_contour_height));
// draw items (colored icon + text)
unsigned int icon_x = squares_contour_x + Px_Square_Contour;
unsigned int icon_x_inner = icon_x + 1;
unsigned int icon_y = squares_contour_y + Px_Square_Contour;
unsigned int icon_y_step = Px_Square + Px_Square_Contour;
unsigned int text_x = icon_x + Px_Square + Px_Text_Offset;
unsigned int text_y_offset = (Px_Square - max_text_height) / 2;
unsigned int px_inner_square = Px_Square - 2;
for (const GCodePreviewData::LegendItem& item : items)
{
// draw darker icon perimeter
const std::vector<unsigned char>& item_color_bytes = item.color.as_bytes();
wxImage::HSVValue dark_hsv = wxImage::RGBtoHSV(wxImage::RGBValue(item_color_bytes[0], item_color_bytes[1], item_color_bytes[2]));
dark_hsv.value *= 0.75;
wxImage::RGBValue dark_rgb = wxImage::HSVtoRGB(dark_hsv);
color.Set(dark_rgb.red, dark_rgb.green, dark_rgb.blue, item_color_bytes[3]);
pen.SetColour(color);
brush.SetColour(color);
memDC.SetPen(pen);
memDC.SetBrush(brush);
memDC.DrawRectangle(wxRect(icon_x, icon_y, Px_Square, Px_Square));
// draw icon interior
color.Set(item_color_bytes[0], item_color_bytes[1], item_color_bytes[2], item_color_bytes[3]);
pen.SetColour(color);
brush.SetColour(color);
memDC.SetPen(pen);
memDC.SetBrush(brush);
memDC.DrawRectangle(wxRect(icon_x_inner, icon_y + 1, px_inner_square, px_inner_square));
// draw text
memDC.DrawText(GUI::from_u8(item.text), text_x, icon_y + text_y_offset);
// update y
icon_y += icon_y_step;
}
memDC.SelectObject(wxNullBitmap);
// Convert the bitmap into a linear data ready to be loaded into the GPU.
{
wxImage image = bitmap.ConvertToImage();
image.SetMaskColour(Background_Color[0], Background_Color[1], Background_Color[2]);
// prepare buffer
m_data.assign(4 * m_tex_width * m_tex_height, 0);
for (unsigned int h = 0; h < m_tex_height; ++h)
{
unsigned int hh = h * m_tex_width;
unsigned char* px_ptr = m_data.data() + 4 * hh;
for (unsigned int w = 0; w < m_tex_width; ++w)
{
*px_ptr++ = image.GetRed(w, h);
*px_ptr++ = image.GetGreen(w, h);
*px_ptr++ = image.GetBlue(w, h);
*px_ptr++ = image.IsTransparent(w, h) ? 0 : Opacity;
}
}
}
return true;
}
void _3DScene::init_gl()
{
s_canvas_mgr.init_gl();
@ -1881,7 +1651,7 @@ void _3DScene::update_volumes_selection(wxGLCanvas* canvas, const std::vector<in
s_canvas_mgr.update_volumes_selection(canvas, selections);
}
bool _3DScene::check_volumes_outside_state(wxGLCanvas* canvas, const DynamicPrintConfig* config)
int _3DScene::check_volumes_outside_state(wxGLCanvas* canvas, const DynamicPrintConfig* config)
{
return s_canvas_mgr.check_volumes_outside_state(canvas, config);
}
@ -2218,54 +1988,9 @@ void _3DScene::load_gcode_preview(wxGLCanvas* canvas, const GCodePreviewData* pr
s_canvas_mgr.load_gcode_preview(canvas, preview_data, str_tool_colors);
}
void _3DScene::generate_legend_texture(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors)
{
s_legend_texture.generate(preview_data, tool_colors);
}
unsigned int _3DScene::get_legend_texture_width()
{
return s_legend_texture.get_texture_width();
}
unsigned int _3DScene::get_legend_texture_height()
{
return s_legend_texture.get_texture_height();
}
void _3DScene::reset_legend_texture()
{
s_legend_texture.reset_texture();
}
unsigned int _3DScene::finalize_legend_texture()
{
return s_legend_texture.finalize();
}
unsigned int _3DScene::get_warning_texture_width()
{
return s_warning_texture.get_texture_width();
}
unsigned int _3DScene::get_warning_texture_height()
{
return s_warning_texture.get_texture_height();
}
void _3DScene::generate_warning_texture(const std::string& msg)
{
s_warning_texture.generate(msg);
}
void _3DScene::reset_warning_texture()
{
s_warning_texture.reset_texture();
}
unsigned int _3DScene::finalize_warning_texture()
{
return s_warning_texture.finalize();
s_canvas_mgr.reset_legend_texture();
}
} // namespace Slic3r

View file

@ -6,6 +6,7 @@
#include "../../libslic3r/Line.hpp"
#include "../../libslic3r/TriangleMesh.hpp"
#include "../../libslic3r/Utils.hpp"
#include "../../libslic3r/Model.hpp"
#include "../../slic3r/GUI/GLCanvas3DManager.hpp"
class wxBitmap;
@ -199,10 +200,10 @@ private:
}
};
class GLTexture
class LayersTexture
{
public:
GLTexture() : width(0), height(0), levels(0), cells(0) {}
LayersTexture() : width(0), height(0), levels(0), cells(0) {}
// Texture data
std::vector<char> data;
@ -341,7 +342,7 @@ public:
void release_geometry() { this->indexed_vertex_array.release_geometry(); }
/************************************************ Layer height texture ****************************************************/
std::shared_ptr<GLTexture> layer_height_texture;
std::shared_ptr<LayersTexture> layer_height_texture;
// Data to render this volume using the layer height texture
LayerHeightTextureData layer_height_texture_data;
@ -422,7 +423,9 @@ public:
print_box_max[0] = max_x; print_box_max[1] = max_y; print_box_max[2] = max_z;
}
bool check_outside_state(const DynamicPrintConfig* config);
// returns true if all the volumes are completely contained in the print volume
// returns the containment state in the given out_state, if non-null
bool check_outside_state(const DynamicPrintConfig* config, ModelInstance::EPrintVolumeState* out_state);
void reset_outside_state();
void update_colors_by_extruder(const DynamicPrintConfig* config);
@ -437,65 +440,6 @@ private:
class _3DScene
{
class TextureBase
{
protected:
unsigned int m_tex_id;
unsigned int m_tex_width;
unsigned int m_tex_height;
// generate() fills in m_data with the pixels, while finalize() moves the data to the GPU before rendering.
std::vector<unsigned char> m_data;
public:
TextureBase() : m_tex_id(0), m_tex_width(0), m_tex_height(0) {}
virtual ~TextureBase() { _destroy_texture(); }
// If not loaded, load the texture data into the GPU. Return a texture ID or 0 if the texture has zero size.
unsigned int finalize();
unsigned int get_texture_id() const { return m_tex_id; }
unsigned int get_texture_width() const { return m_tex_width; }
unsigned int get_texture_height() const { return m_tex_height; }
void reset_texture() { _destroy_texture(); }
private:
void _destroy_texture();
};
class WarningTexture : public TextureBase
{
static const unsigned char Background_Color[3];
static const unsigned char Opacity;
public:
WarningTexture() : TextureBase() {}
// Generate a texture data, but don't load it into the GPU yet, as the glcontext may not be valid yet.
bool generate(const std::string& msg);
};
class LegendTexture : public TextureBase
{
static const unsigned int Px_Title_Offset = 5;
static const unsigned int Px_Text_Offset = 5;
static const unsigned int Px_Square = 20;
static const unsigned int Px_Square_Contour = 1;
static const unsigned int Px_Border = Px_Square / 2;
static const unsigned char Squares_Border_Color[3];
static const unsigned char Background_Color[3];
static const unsigned char Opacity;
public:
LegendTexture() : TextureBase() {}
// Generate a texture data, but don't load it into the GPU yet, as the glcontext may not be valid yet.
bool generate(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors);
};
static LegendTexture s_legend_texture;
static WarningTexture s_warning_texture;
static GUI::GLCanvas3DManager s_canvas_mgr;
public:
@ -516,7 +460,7 @@ public:
static void deselect_volumes(wxGLCanvas* canvas);
static void select_volume(wxGLCanvas* canvas, unsigned int id);
static void update_volumes_selection(wxGLCanvas* canvas, const std::vector<int>& selections);
static bool check_volumes_outside_state(wxGLCanvas* canvas, const DynamicPrintConfig* config);
static int check_volumes_outside_state(wxGLCanvas* canvas, const DynamicPrintConfig* config);
static bool move_volume_up(wxGLCanvas* canvas, unsigned int id);
static bool move_volume_down(wxGLCanvas* canvas, unsigned int id);
@ -597,21 +541,7 @@ public:
static void load_wipe_tower_toolpaths(wxGLCanvas* canvas, const std::vector<std::string>& str_tool_colors);
static void load_gcode_preview(wxGLCanvas* canvas, const GCodePreviewData* preview_data, const std::vector<std::string>& str_tool_colors);
// generates the legend texture in dependence of the current shown view type
static void generate_legend_texture(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors);
static unsigned int get_legend_texture_width();
static unsigned int get_legend_texture_height();
static void reset_legend_texture();
static unsigned int finalize_legend_texture();
static unsigned int get_warning_texture_width();
static unsigned int get_warning_texture_height();
// generates a warning texture containing the given message
static void generate_warning_texture(const std::string& msg);
static void reset_warning_texture();
static unsigned int finalize_warning_texture();
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);
static void thick_lines_to_verts(const Lines3& lines, const std::vector<double>& widths, const std::vector<double>& heights, bool closed, GLVolume& volume);

View file

@ -65,22 +65,27 @@ PrinterPicker::PrinterPicker(wxWindow *parent, const VendorProfile &vendor, cons
auto namefont = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
namefont.SetWeight(wxFONTWEIGHT_BOLD);
// wxGrid appends widgets by rows, but we need to construct them in columns.
// These vectors are used to hold the elements so that they can be appended in the right order.
std::vector<wxStaticText*> titles;
std::vector<wxStaticBitmap*> bitmaps;
std::vector<wxPanel*> variants_panels;
for (const auto &model : models) {
auto *panel = new wxPanel(this);
auto *col_sizer = new wxBoxSizer(wxVERTICAL);
panel->SetSizer(col_sizer);
auto *title = new wxStaticText(panel, wxID_ANY, model.name, wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
title->SetFont(namefont);
col_sizer->Add(title, 0, wxBOTTOM, 3);
auto bitmap_file = wxString::Format("printers/%s_%s.png", vendor.id, model.id);
wxBitmap bitmap(GUI::from_u8(Slic3r::var(bitmap_file.ToStdString())), wxBITMAP_TYPE_PNG);
auto *bitmap_widget = new wxStaticBitmap(panel, wxID_ANY, bitmap);
col_sizer->Add(bitmap_widget, 0, wxBOTTOM, 3);
col_sizer->AddSpacer(20);
auto *title = new wxStaticText(this, wxID_ANY, model.name, wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
title->SetFont(namefont);
title->Wrap(std::max((int)MODEL_MIN_WRAP, bitmap.GetWidth()));
titles.push_back(title);
auto *bitmap_widget = new wxStaticBitmap(this, wxID_ANY, bitmap);
bitmaps.push_back(bitmap_widget);
auto *variants_panel = new wxPanel(this);
auto *variants_sizer = new wxBoxSizer(wxVERTICAL);
variants_panel->SetSizer(variants_sizer);
const auto model_id = model.id;
bool default_variant = true; // Mark the first variant as default in the GUI
@ -88,22 +93,26 @@ PrinterPicker::PrinterPicker(wxWindow *parent, const VendorProfile &vendor, cons
const auto label = wxString::Format("%s %s %s %s", variant.name, _(L("mm")), _(L("nozzle")),
(default_variant ? _(L("(default)")) : wxString()));
default_variant = false;
auto *cbox = new Checkbox(panel, label, model_id, variant.name);
auto *cbox = new Checkbox(variants_panel, label, model_id, variant.name);
const size_t idx = cboxes.size();
cboxes.push_back(cbox);
bool enabled = appconfig_vendors.get_variant("PrusaResearch", model_id, variant.name);
variants_checked += enabled;
cbox->SetValue(enabled);
col_sizer->Add(cbox, 0, wxBOTTOM, 3);
variants_sizer->Add(cbox, 0, wxBOTTOM, 3);
cbox->Bind(wxEVT_CHECKBOX, [this, idx](wxCommandEvent &event) {
if (idx >= this->cboxes.size()) { return; }
this->on_checkbox(this->cboxes[idx], event.IsChecked());
});
}
printer_grid->Add(panel);
variants_panels.push_back(variants_panel);
}
for (auto title : titles) { printer_grid->Add(title, 0, wxBOTTOM, 3); }
for (auto bitmap : bitmaps) { printer_grid->Add(bitmap, 0, wxBOTTOM, 20); }
for (auto vp : variants_panels) { printer_grid->Add(vp); }
auto *all_none_sizer = new wxBoxSizer(wxHORIZONTAL);
auto *sel_all = new wxButton(this, wxID_ANY, _(L("Select all")));
auto *sel_none = new wxButton(this, wxID_ANY, _(L("Select none")));
@ -214,7 +223,7 @@ void ConfigWizardPage::enable_next(bool enable) { parent->p->enable_next(enable)
// Wizard pages
PageWelcome::PageWelcome(ConfigWizard *parent) :
PageWelcome::PageWelcome(ConfigWizard *parent, bool check_first_variant) :
ConfigWizardPage(parent, wxString::Format(_(L("Welcome to the Slic3r %s")), ConfigWizard::name()), _(L("Welcome"))),
printer_picker(nullptr),
others_buttons(new wxPanel(parent)),
@ -238,7 +247,10 @@ PageWelcome::PageWelcome(ConfigWizard *parent) :
AppConfig &appconfig_vendors = this->wizard_p()->appconfig_vendors;
printer_picker = new PrinterPicker(this, vendor_prusa->second, appconfig_vendors);
printer_picker->select_one(0, true); // Select the default (first) model/variant on the Prusa vendor
if (check_first_variant) {
// Select the default (first) model/variant on the Prusa vendor
printer_picker->select_one(0, true);
}
printer_picker->Bind(EVT_PRINTER_PICK, [this, &appconfig_vendors](const PrinterPickerEvent &evt) {
appconfig_vendors.set_variant(evt.vendor_id, evt.model_id, evt.variant_name, evt.enable);
this->on_variant_checked();
@ -779,7 +791,6 @@ void ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese
app_config->set("version_check", page_update->version_check ? "1" : "0");
app_config->set("preset_update", page_update->preset_update ? "1" : "0");
app_config->reset_selections();
// ^ TODO: replace with appropriate printer selection
preset_bundle->load_presets(*app_config);
} else {
for (ConfigWizardPage *page = page_firmware; page != nullptr; page = page->page_next()) {
@ -831,7 +842,7 @@ ConfigWizard::ConfigWizard(wxWindow *parent, RunReason reason) :
p->btnsizer->Add(p->btn_finish, 0, wxLEFT, BTN_SPACING);
p->btnsizer->Add(p->btn_cancel, 0, wxLEFT, BTN_SPACING);
p->add_page(p->page_welcome = new PageWelcome(this));
p->add_page(p->page_welcome = new PageWelcome(this, reason == RR_DATA_EMPTY || reason == RR_DATA_LEGACY));
p->add_page(p->page_update = new PageUpdate(this));
p->add_page(p->page_vendors = new PageVendors(this));
p->add_page(p->page_firmware = new PageFirmware(this));

View file

@ -27,6 +27,7 @@ namespace GUI {
enum {
WRAP_WIDTH = 500,
MODEL_MIN_WRAP = 150,
DIALOG_MARGIN = 15,
INDEX_MARGIN = 40,
@ -103,7 +104,7 @@ struct PageWelcome: ConfigWizardPage
wxPanel *others_buttons;
wxCheckBox *cbox_reset;
PageWelcome(ConfigWizard *parent);
PageWelcome(ConfigWizard *parent, bool check_first_variant);
virtual wxPanel* extra_buttons() { return others_buttons; }
virtual void on_page_set();

View file

@ -15,6 +15,10 @@
#include <wx/glcanvas.h>
#include <wx/timer.h>
#include <wx/bitmap.h>
#include <wx/dcmemory.h>
#include <wx/image.h>
#include <wx/settings.h>
#include <tbb/parallel_for.h>
#include <tbb/spin_mutex.h>
@ -1381,7 +1385,8 @@ void GLCanvas3D::Gizmos::render(const GLCanvas3D& canvas, const BoundingBoxf3& b
::glDisable(GL_DEPTH_TEST);
_render_current_gizmo(box);
if (box.radius() > 0.0)
_render_current_gizmo(box);
::glPushMatrix();
::glLoadIdentity();
@ -1456,6 +1461,224 @@ float GLCanvas3D::Gizmos::_get_total_overlay_height() const
return height;
}
const unsigned char GLCanvas3D::WarningTexture::Background_Color[3] = { 9, 91, 134 };
const unsigned char GLCanvas3D::WarningTexture::Opacity = 255;
bool GLCanvas3D::WarningTexture::generate(const std::string& msg)
{
reset();
if (msg.empty())
return false;
wxMemoryDC memDC;
// select default font
memDC.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
// calculates texture size
wxCoord w, h;
memDC.GetTextExtent(msg, &w, &h);
m_width = (int)w;
m_height = (int)h;
// generates bitmap
wxBitmap bitmap(m_width, m_height);
#if defined(__APPLE__) || defined(_MSC_VER)
bitmap.UseAlpha();
#endif
memDC.SelectObject(bitmap);
memDC.SetBackground(wxBrush(wxColour(Background_Color[0], Background_Color[1], Background_Color[2])));
memDC.Clear();
memDC.SetTextForeground(*wxWHITE);
// draw message
memDC.DrawText(msg, 0, 0);
memDC.SelectObject(wxNullBitmap);
// Convert the bitmap into a linear data ready to be loaded into the GPU.
wxImage image = bitmap.ConvertToImage();
image.SetMaskColour(Background_Color[0], Background_Color[1], Background_Color[2]);
// prepare buffer
std::vector<unsigned char> data(4 * m_width * m_height, 0);
for (int h = 0; h < m_height; ++h)
{
int hh = h * m_width;
unsigned char* px_ptr = data.data() + 4 * hh;
for (int w = 0; w < m_width; ++w)
{
*px_ptr++ = image.GetRed(w, h);
*px_ptr++ = image.GetGreen(w, h);
*px_ptr++ = image.GetBlue(w, h);
*px_ptr++ = image.IsTransparent(w, h) ? 0 : Opacity;
}
}
// sends buffer to gpu
::glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
::glGenTextures(1, &m_id);
::glBindTexture(GL_TEXTURE_2D, (GLuint)m_id);
::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data());
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
::glBindTexture(GL_TEXTURE_2D, 0);
return true;
}
const unsigned char GLCanvas3D::LegendTexture::Squares_Border_Color[3] = { 64, 64, 64 };
const unsigned char GLCanvas3D::LegendTexture::Background_Color[3] = { 9, 91, 134 };
const unsigned char GLCanvas3D::LegendTexture::Opacity = 255;
bool GLCanvas3D::LegendTexture::generate(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors)
{
reset();
// collects items to render
auto title = _(preview_data.get_legend_title());
const GCodePreviewData::LegendItemsList& items = preview_data.get_legend_items(tool_colors);
unsigned int items_count = (unsigned int)items.size();
if (items_count == 0)
// nothing to render, return
return false;
wxMemoryDC memDC;
// select default font
memDC.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
// calculates texture size
wxCoord w, h;
memDC.GetTextExtent(title, &w, &h);
int title_width = (int)w;
int title_height = (int)h;
int max_text_width = 0;
int max_text_height = 0;
for (const GCodePreviewData::LegendItem& item : items)
{
memDC.GetTextExtent(GUI::from_u8(item.text), &w, &h);
max_text_width = std::max(max_text_width, (int)w);
max_text_height = std::max(max_text_height, (int)h);
}
m_width = std::max(2 * Px_Border + title_width, 2 * (Px_Border + Px_Square_Contour) + Px_Square + Px_Text_Offset + max_text_width);
m_height = 2 * (Px_Border + Px_Square_Contour) + title_height + Px_Title_Offset + items_count * Px_Square;
if (items_count > 1)
m_height += (items_count - 1) * Px_Square_Contour;
// generates bitmap
wxBitmap bitmap(m_width, m_height);
#if defined(__APPLE__) || defined(_MSC_VER)
bitmap.UseAlpha();
#endif
memDC.SelectObject(bitmap);
memDC.SetBackground(wxBrush(wxColour(Background_Color[0], Background_Color[1], Background_Color[2])));
memDC.Clear();
memDC.SetTextForeground(*wxWHITE);
// draw title
int title_x = Px_Border;
int title_y = Px_Border;
memDC.DrawText(title, title_x, title_y);
// draw icons contours as background
int squares_contour_x = Px_Border;
int squares_contour_y = Px_Border + title_height + Px_Title_Offset;
int squares_contour_width = Px_Square + 2 * Px_Square_Contour;
int squares_contour_height = items_count * Px_Square + 2 * Px_Square_Contour;
if (items_count > 1)
squares_contour_height += (items_count - 1) * Px_Square_Contour;
wxColour color(Squares_Border_Color[0], Squares_Border_Color[1], Squares_Border_Color[2]);
wxPen pen(color);
wxBrush brush(color);
memDC.SetPen(pen);
memDC.SetBrush(brush);
memDC.DrawRectangle(wxRect(squares_contour_x, squares_contour_y, squares_contour_width, squares_contour_height));
// draw items (colored icon + text)
int icon_x = squares_contour_x + Px_Square_Contour;
int icon_x_inner = icon_x + 1;
int icon_y = squares_contour_y + Px_Square_Contour;
int icon_y_step = Px_Square + Px_Square_Contour;
int text_x = icon_x + Px_Square + Px_Text_Offset;
int text_y_offset = (Px_Square - max_text_height) / 2;
int px_inner_square = Px_Square - 2;
for (const GCodePreviewData::LegendItem& item : items)
{
// draw darker icon perimeter
const std::vector<unsigned char>& item_color_bytes = item.color.as_bytes();
wxImage::HSVValue dark_hsv = wxImage::RGBtoHSV(wxImage::RGBValue(item_color_bytes[0], item_color_bytes[1], item_color_bytes[2]));
dark_hsv.value *= 0.75;
wxImage::RGBValue dark_rgb = wxImage::HSVtoRGB(dark_hsv);
color.Set(dark_rgb.red, dark_rgb.green, dark_rgb.blue, item_color_bytes[3]);
pen.SetColour(color);
brush.SetColour(color);
memDC.SetPen(pen);
memDC.SetBrush(brush);
memDC.DrawRectangle(wxRect(icon_x, icon_y, Px_Square, Px_Square));
// draw icon interior
color.Set(item_color_bytes[0], item_color_bytes[1], item_color_bytes[2], item_color_bytes[3]);
pen.SetColour(color);
brush.SetColour(color);
memDC.SetPen(pen);
memDC.SetBrush(brush);
memDC.DrawRectangle(wxRect(icon_x_inner, icon_y + 1, px_inner_square, px_inner_square));
// draw text
memDC.DrawText(GUI::from_u8(item.text), text_x, icon_y + text_y_offset);
// update y
icon_y += icon_y_step;
}
memDC.SelectObject(wxNullBitmap);
// Convert the bitmap into a linear data ready to be loaded into the GPU.
wxImage image = bitmap.ConvertToImage();
image.SetMaskColour(Background_Color[0], Background_Color[1], Background_Color[2]);
// prepare buffer
std::vector<unsigned char> data(4 * m_width * m_height, 0);
for (int h = 0; h < m_height; ++h)
{
int hh = h * m_width;
unsigned char* px_ptr = data.data() + 4 * hh;
for (int w = 0; w < m_width; ++w)
{
*px_ptr++ = image.GetRed(w, h);
*px_ptr++ = image.GetGreen(w, h);
*px_ptr++ = image.GetBlue(w, h);
*px_ptr++ = image.IsTransparent(w, h) ? 0 : Opacity;
}
}
// sends buffer to gpu
::glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
::glGenTextures(1, &m_id);
::glBindTexture(GL_TEXTURE_2D, (GLuint)m_id);
::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data());
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
::glBindTexture(GL_TEXTURE_2D, 0);
return true;
}
GLGizmoBase* GLCanvas3D::Gizmos::_get_current() const
{
GizmosMap::const_iterator it = m_gizmos.find(m_current);
@ -1655,9 +1878,11 @@ void GLCanvas3D::update_volumes_selection(const std::vector<int>& selections)
}
}
bool GLCanvas3D::check_volumes_outside_state(const DynamicPrintConfig* config) const
int GLCanvas3D::check_volumes_outside_state(const DynamicPrintConfig* config) const
{
return m_volumes.check_outside_state(config);
ModelInstance::EPrintVolumeState state;
m_volumes.check_outside_state(config, &state);
return (int)state;
}
bool GLCanvas3D::move_volume_up(unsigned int id)
@ -1905,7 +2130,7 @@ void GLCanvas3D::update_volumes_colors_by_extruder()
void GLCanvas3D::update_gizmos_data()
{
if (!m_gizmos.is_running())
if (!m_gizmos.is_enabled())
return;
int id = _get_first_selected_object_id();
@ -1917,26 +2142,16 @@ void GLCanvas3D::update_gizmos_data()
ModelInstance* model_instance = model_object->instances[0];
if (model_instance != nullptr)
{
switch (m_gizmos.get_current_type())
{
case Gizmos::Scale:
{
m_gizmos.set_scale(model_instance->scaling_factor);
break;
}
case Gizmos::Rotate:
{
m_gizmos.set_angle_z(model_instance->rotation);
break;
}
default:
{
break;
}
}
m_gizmos.set_scale(model_instance->scaling_factor);
m_gizmos.set_angle_z(model_instance->rotation);
}
}
}
else
{
m_gizmos.set_scale(1.0f);
m_gizmos.set_angle_z(0.0f);
}
}
void GLCanvas3D::render()
@ -2051,8 +2266,12 @@ void GLCanvas3D::reload_scene(bool force)
m_objects_volumes_idxs.push_back(load_object(*m_model, obj_idx));
}
// 1st call to reset if no objects left
update_gizmos_data();
update_volumes_selection(m_objects_selections);
// 2nd call to restore if something selected
if (!m_objects_selections.empty())
update_gizmos_data();
if (m_config->has("nozzle_diameter"))
{
@ -2082,25 +2301,28 @@ void GLCanvas3D::reload_scene(bool force)
// checks for geometry outside the print volume to render it accordingly
if (!m_volumes.empty())
{
bool contained = m_volumes.check_outside_state(m_config);
ModelInstance::EPrintVolumeState state;
bool contained = m_volumes.check_outside_state(m_config, &state);
if (!contained)
{
enable_warning_texture(true);
_3DScene::generate_warning_texture(L("Detected object outside print volume"));
m_on_enable_action_buttons_callback.call(false);
_generate_warning_texture(L("Detected object outside print volume"));
m_on_enable_action_buttons_callback.call(state == ModelInstance::PVS_Fully_Outside);
}
else
{
enable_warning_texture(false);
m_volumes.reset_outside_state();
_3DScene::reset_warning_texture();
_reset_warning_texture();
m_on_enable_action_buttons_callback.call(!m_model->objects.empty());
}
}
else
{
enable_warning_texture(false);
_3DScene::reset_warning_texture();
_reset_warning_texture();
m_on_enable_action_buttons_callback.call(false);
}
}
@ -2490,11 +2712,11 @@ void GLCanvas3D::load_gcode_preview(const GCodePreviewData& preview_data, const
_load_gcode_unretractions(preview_data);
if (m_volumes.empty())
_3DScene::reset_legend_texture();
reset_legend_texture();
else
{
_3DScene::generate_legend_texture(preview_data, tool_colors);
_generate_legend_texture(preview_data, tool_colors);
// removes empty volumes
m_volumes.volumes.erase(std::remove_if(m_volumes.volumes.begin(), m_volumes.volumes.end(),
[](const GLVolume* volume) { return volume->print_zs.empty(); }), m_volumes.volumes.end());
@ -3103,6 +3325,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
{
deselect_volumes();
_on_select(-1);
update_gizmos_data();
}
}
else if (evt.LeftUp() && m_gizmos.is_dragging())
@ -3130,6 +3353,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
m_mouse.set_start_position_3D_as_invalid();
m_mouse.set_start_position_2D_as_invalid();
m_mouse.dragging = false;
m_dirty = true;
}
else if (evt.Moving())
{
@ -3181,6 +3405,14 @@ Point GLCanvas3D::get_local_mouse_position() const
return Point(mouse_pos.x, mouse_pos.y);
}
void GLCanvas3D::reset_legend_texture()
{
if (!set_current())
return;
m_legend_texture.reset();
}
bool GLCanvas3D::_is_shown_on_screen() const
{
return (m_canvas != nullptr) ? m_canvas->IsShownOnScreen() : false;
@ -3272,7 +3504,7 @@ BoundingBoxf3 GLCanvas3D::_selected_volumes_bounding_box() const
BoundingBoxf3 bb;
for (const GLVolume* volume : m_volumes.volumes)
{
if ((volume != nullptr) && volume->selected)
if ((volume != nullptr) && !volume->is_wipe_tower && volume->selected)
bb.merge(volume->transformed_bounding_box());
}
return bb;
@ -3441,6 +3673,7 @@ void GLCanvas3D::_picking_pass() const
::glDisable(GL_MULTISAMPLE);
::glDisable(GL_BLEND);
::glEnable(GL_DEPTH_TEST);
::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
@ -3549,7 +3782,7 @@ void GLCanvas3D::_render_objects() const
{
const BoundingBoxf3& bed_bb = m_bed.get_bounding_box();
m_volumes.set_print_box((float)bed_bb.min.x, (float)bed_bb.min.y, 0.0f, (float)bed_bb.max.x, (float)bed_bb.max.y, (float)m_config->opt_float("max_print_height"));
m_volumes.check_outside_state(m_config);
m_volumes.check_outside_state(m_config, nullptr);
}
// do not cull backfaces to show broken geometry, if any
::glDisable(GL_CULL_FACE);
@ -3588,11 +3821,11 @@ void GLCanvas3D::_render_warning_texture() const
return;
// If the warning texture has not been loaded into the GPU, do it now.
unsigned int tex_id = _3DScene::finalize_warning_texture();
unsigned int tex_id = m_warning_texture.get_id();
if (tex_id > 0)
{
unsigned int w = _3DScene::get_warning_texture_width();
unsigned int h = _3DScene::get_warning_texture_height();
int w = m_warning_texture.get_width();
int h = m_warning_texture.get_height();
if ((w > 0) && (h > 0))
{
::glDisable(GL_DEPTH_TEST);
@ -3621,11 +3854,11 @@ void GLCanvas3D::_render_legend_texture() const
return;
// If the legend texture has not been loaded into the GPU, do it now.
unsigned int tex_id = _3DScene::finalize_legend_texture();
unsigned int tex_id = m_legend_texture.get_id();
if (tex_id > 0)
{
unsigned int w = _3DScene::get_legend_texture_width();
unsigned int h = _3DScene::get_legend_texture_height();
int w = m_legend_texture.get_width();
int h = m_legend_texture.get_height();
if ((w > 0) && (h > 0))
{
::glDisable(GL_DEPTH_TEST);
@ -4539,5 +4772,29 @@ std::vector<float> GLCanvas3D::_parse_colors(const std::vector<std::string>& col
return output;
}
void GLCanvas3D::_generate_legend_texture(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors)
{
if (!set_current())
return;
m_legend_texture.generate(preview_data, tool_colors);
}
void GLCanvas3D::_generate_warning_texture(const std::string& msg)
{
if (!set_current())
return;
m_warning_texture.generate(msg);
}
void GLCanvas3D::_reset_warning_texture()
{
if (!set_current())
return;
m_warning_texture.reset();
}
} // namespace GUI
} // namespace Slic3r

View file

@ -394,9 +394,35 @@ public:
GLGizmoBase* _get_current() const;
};
class WarningTexture : public GUI::GLTexture
{
static const unsigned char Background_Color[3];
static const unsigned char Opacity;
public:
bool generate(const std::string& msg);
};
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 Background_Color[3];
static const unsigned char Opacity;
public:
bool generate(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors);
};
private:
wxGLCanvas* m_canvas;
wxGLContext* m_context;
LegendTexture m_legend_texture;
WarningTexture m_warning_texture;
wxTimer* m_timer;
Camera m_camera;
Bed m_bed;
@ -469,7 +495,7 @@ public:
void deselect_volumes();
void select_volume(unsigned int id);
void update_volumes_selection(const std::vector<int>& selections);
bool check_volumes_outside_state(const DynamicPrintConfig* config) const;
int check_volumes_outside_state(const DynamicPrintConfig* config) const;
bool move_volume_up(unsigned int id);
bool move_volume_down(unsigned int id);
@ -578,6 +604,8 @@ public:
Size get_canvas_size() const;
Point get_local_mouse_position() const;
void reset_legend_texture();
private:
bool _is_shown_on_screen() const;
void _force_zoom_to_bed();
@ -643,6 +671,13 @@ private:
void _on_move(const std::vector<int>& volume_idxs);
void _on_select(int volume_idx);
// 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);
// generates a warning texture containing the given message
void _generate_warning_texture(const std::string& msg);
void _reset_warning_texture();
static std::vector<float> _parse_colors(const std::vector<std::string>& colors);
};

View file

@ -237,7 +237,7 @@ void GLCanvas3DManager::update_volumes_selection(wxGLCanvas* canvas, const std::
it->second->update_volumes_selection(selections);
}
bool GLCanvas3DManager::check_volumes_outside_state(wxGLCanvas* canvas, const DynamicPrintConfig* config) const
int GLCanvas3DManager::check_volumes_outside_state(wxGLCanvas* canvas, const DynamicPrintConfig* config) const
{
CanvasesMap::const_iterator it = _get_canvas(canvas);
return (it != m_canvases.end()) ? it->second->check_volumes_outside_state(config) : false;
@ -550,6 +550,15 @@ void GLCanvas3DManager::load_gcode_preview(wxGLCanvas* canvas, const GCodePrevie
it->second->load_gcode_preview(*preview_data, str_tool_colors);
}
void GLCanvas3DManager::reset_legend_texture()
{
for (CanvasesMap::value_type& canvas : m_canvases)
{
if (canvas.second != nullptr)
canvas.second->reset_legend_texture();
}
}
void GLCanvas3DManager::register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback)
{
CanvasesMap::iterator it = _get_canvas(canvas);

View file

@ -75,7 +75,7 @@ public:
void deselect_volumes(wxGLCanvas* canvas);
void select_volume(wxGLCanvas* canvas, unsigned int id);
void update_volumes_selection(wxGLCanvas* canvas, const std::vector<int>& selections);
bool check_volumes_outside_state(wxGLCanvas* canvas, const DynamicPrintConfig* config) const;
int check_volumes_outside_state(wxGLCanvas* canvas, const DynamicPrintConfig* config) const;
bool move_volume_up(wxGLCanvas* canvas, unsigned int id);
bool move_volume_down(wxGLCanvas* canvas, unsigned int id);
@ -137,6 +137,8 @@ public:
void load_wipe_tower_toolpaths(wxGLCanvas* canvas, const std::vector<std::string>& str_tool_colors);
void load_gcode_preview(wxGLCanvas* canvas, const GCodePreviewData* preview_data, const std::vector<std::string>& str_tool_colors);
void reset_legend_texture();
void register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback);
void register_on_double_click_callback(wxGLCanvas* canvas, void* callback);
void register_on_right_click_callback(wxGLCanvas* canvas, void* callback);

View file

@ -79,7 +79,8 @@ bool GLTexture::load_from_file(const std::string& filename, bool generate_mipmap
if (generate_mipmaps)
{
// we manually generate mipmaps because glGenerateMipmap() function is not reliable on all graphics cards
_generate_mipmaps(image);
unsigned int levels_count = _generate_mipmaps(image);
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1 + levels_count);
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
}
else
@ -149,14 +150,14 @@ void GLTexture::render_texture(unsigned int tex_id, float left, float right, flo
::glDisable(GL_BLEND);
}
void GLTexture::_generate_mipmaps(wxImage& image)
unsigned int GLTexture::_generate_mipmaps(wxImage& image)
{
int w = image.GetWidth();
int h = image.GetHeight();
GLint level = 0;
std::vector<unsigned char> data(w * h * 4, 0);
while ((w > 1) && (h > 1))
while ((w > 1) || (h > 1))
{
++level;
@ -183,6 +184,8 @@ void GLTexture::_generate_mipmaps(wxImage& image)
::glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, (GLsizei)w, (GLsizei)h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data());
}
return (unsigned int)level;
}
} // namespace GUI

View file

@ -10,7 +10,7 @@ namespace GUI {
class GLTexture
{
private:
protected:
unsigned int m_id;
int m_width;
int m_height;
@ -18,7 +18,7 @@ namespace GUI {
public:
GLTexture();
~GLTexture();
virtual ~GLTexture();
bool load_from_file(const std::string& filename, bool generate_mipmaps);
void reset();
@ -26,12 +26,13 @@ namespace GUI {
unsigned int get_id() const;
int get_width() const;
int get_height() const;
const std::string& get_source() const;
static void render_texture(unsigned int tex_id, float left, float right, float bottom, float top);
private:
void _generate_mipmaps(wxImage& image);
protected:
unsigned int _generate_mipmaps(wxImage& image);
};
} // namespace GUI

View file

@ -268,7 +268,7 @@ void PresetBundle::load_installed_printers(const AppConfig &config)
}
// Load selections (current print, current filaments, current printer) from config.ini
// This is done just once on application start up.
// This is done on application start up or after updates are applied.
void PresetBundle::load_selections(const AppConfig &config)
{
// Update visibility of presets based on application vendor / model / variant configuration.

View file

@ -1742,27 +1742,27 @@ PageShp TabPrinter::build_kinematics_page()
}
std::vector<std::string> axes{ "x", "y", "z", "e" };
auto optgroup = page->new_optgroup(_(L("Maximum accelerations")));
for (const std::string &axis : axes) {
append_option_line(optgroup, "machine_max_acceleration_" + axis);
}
optgroup = page->new_optgroup(_(L("Maximum feedrates")));
auto optgroup = page->new_optgroup(_(L("Maximum feedrates")));
for (const std::string &axis : axes) {
append_option_line(optgroup, "machine_max_feedrate_" + axis);
}
optgroup = page->new_optgroup(_(L("Starting Acceleration")));
optgroup = page->new_optgroup(_(L("Maximum accelerations")));
for (const std::string &axis : axes) {
append_option_line(optgroup, "machine_max_acceleration_" + axis);
}
append_option_line(optgroup, "machine_max_acceleration_extruding");
append_option_line(optgroup, "machine_max_acceleration_retracting");
optgroup = page->new_optgroup(_(L("Advanced")));
append_option_line(optgroup, "machine_min_extruding_rate");
append_option_line(optgroup, "machine_min_travel_rate");
optgroup = page->new_optgroup(_(L("Jerk limits")));
for (const std::string &axis : axes) {
append_option_line(optgroup, "machine_max_jerk_" + axis);
}
optgroup = page->new_optgroup(_(L("Minimum feedrates")));
append_option_line(optgroup, "machine_min_extruding_rate");
append_option_line(optgroup, "machine_min_travel_rate");
return page;
}

View file

@ -322,10 +322,13 @@ Updates PresetUpdater::priv::get_config_updates() const
const auto ver_current = idx.find(vp.config_version);
if (ver_current == idx.end()) {
BOOST_LOG_TRIVIAL(error) << boost::format("Preset bundle (`%1%`) version not found in index: %2%") % idx.vendor() % vp.config_version.to_string();
continue;
auto message = (boost::format("Preset bundle `%1%` version not found in index: %2%") % idx.vendor() % vp.config_version.to_string()).str();
BOOST_LOG_TRIVIAL(error) << message;
throw std::runtime_error(message);
}
// Getting a recommended version from the latest index, wich may have been downloaded
// from the internet, or installed / updated from the installation resources.
const auto recommended = idx.recommended();
if (recommended == idx.end()) {
BOOST_LOG_TRIVIAL(error) << boost::format("No recommended version for vendor: %1%, invalid index?") % idx.vendor();
@ -353,25 +356,34 @@ Updates PresetUpdater::priv::get_config_updates() const
}
auto path_src = cache_path / (idx.vendor() + ".ini");
auto path_in_rsrc = rsrc_path / (idx.vendor() + ".ini");
if (! fs::exists(path_src)) {
auto path_in_rsrc = rsrc_path / (idx.vendor() + ".ini");
if (! fs::exists(path_in_rsrc)) {
BOOST_LOG_TRIVIAL(warning) << boost::format("Index for vendor %1% indicates update, but bundle found in neither cache nor resources")
% idx.vendor();;
% idx.vendor();
continue;
} else {
path_src = std::move(path_in_rsrc);
path_in_rsrc.clear();
}
}
const auto new_vp = VendorProfile::from_ini(path_src, false);
auto new_vp = VendorProfile::from_ini(path_src, false);
bool found = false;
if (new_vp.config_version == recommended->config_version) {
updates.updates.emplace_back(std::move(path_src), std::move(bundle_path), *recommended);
} else {
found = true;
} else if (! path_in_rsrc.empty() && fs::exists(path_in_rsrc)) {
new_vp = VendorProfile::from_ini(path_in_rsrc, false);
if (new_vp.config_version == recommended->config_version) {
updates.updates.emplace_back(std::move(path_in_rsrc), std::move(bundle_path), *recommended);
found = true;
}
}
if (! found)
BOOST_LOG_TRIVIAL(warning) << boost::format("Index for vendor %1% indicates update (%2%) but the new bundle was found neither in cache nor resources")
% idx.vendor()
% recommended->config_version.to_string();
}
}
}
@ -530,10 +542,21 @@ bool PresetUpdater::config_update() const
std::unordered_map<std::string, wxString> incompats_map;
for (const auto &incompat : updates.incompats) {
auto vendor = incompat.name();
auto restrictions = wxString::Format(_(L("requires min. %s and max. %s")),
incompat.version.min_slic3r_version.to_string(),
incompat.version.max_slic3r_version.to_string()
);
const auto min_slic3r = incompat.version.min_slic3r_version;
const auto max_slic3r = incompat.version.max_slic3r_version;
wxString restrictions;
if (min_slic3r != Semver::zero() && max_slic3r != Semver::inf()) {
restrictions = wxString::Format(_(L("requires min. %s and max. %s")),
min_slic3r.to_string(),
max_slic3r.to_string()
);
} else if (min_slic3r != Semver::zero()) {
restrictions = wxString::Format(_(L("requires min. %s")), min_slic3r.to_string());
} else {
restrictions = wxString::Format(_(L("requires max. %s")), max_slic3r.to_string());
}
incompats_map.emplace(std::make_pair(std::move(vendor), std::move(restrictions)));
}
@ -545,9 +568,10 @@ bool PresetUpdater::config_update() const
BOOST_LOG_TRIVIAL(info) << "User wants to re-configure...";
p->perform_updates(std::move(updates));
GUI::ConfigWizard wizard(nullptr, GUI::ConfigWizard::RR_DATA_INCOMPAT);
if (! wizard.run(GUI::get_preset_bundle(), this)) {
if (! wizard.run(GUI::get_preset_bundle(), this)) {
return false;
}
GUI::load_current_presets();
} else {
BOOST_LOG_TRIVIAL(info) << "User wants to exit Slic3r, bye...";
return false;
@ -577,7 +601,6 @@ bool PresetUpdater::config_update() const
// Reload global configuration
auto *app_config = GUI::get_app_config();
app_config->reset_selections();
GUI::get_preset_bundle()->load_presets(*app_config);
GUI::load_current_presets();
} else {