mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-10-22 16:21:24 -06:00
Merge branch 'master' into wipe_tower_improvements
This commit is contained in:
commit
51b6557ada
123 changed files with 18417 additions and 3280 deletions
|
@ -2,6 +2,8 @@
|
|||
#include <algorithm>
|
||||
#include <assert.h>
|
||||
|
||||
#include <Eigen/Dense>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
template BoundingBoxBase<Point>::BoundingBoxBase(const std::vector<Point> &points);
|
||||
|
@ -251,4 +253,41 @@ void BoundingBox::align_to_grid(const coord_t cell_size)
|
|||
}
|
||||
}
|
||||
|
||||
BoundingBoxf3 BoundingBoxf3::transformed(const std::vector<float>& matrix) const
|
||||
{
|
||||
Eigen::Matrix<float, 3, 8> vertices;
|
||||
|
||||
vertices(0, 0) = (float)min.x; vertices(1, 0) = (float)min.y; vertices(2, 0) = (float)min.z;
|
||||
vertices(0, 1) = (float)max.x; vertices(1, 1) = (float)min.y; vertices(2, 1) = (float)min.z;
|
||||
vertices(0, 2) = (float)max.x; vertices(1, 2) = (float)max.y; vertices(2, 2) = (float)min.z;
|
||||
vertices(0, 3) = (float)min.x; vertices(1, 3) = (float)max.y; vertices(2, 3) = (float)min.z;
|
||||
vertices(0, 4) = (float)min.x; vertices(1, 4) = (float)min.y; vertices(2, 4) = (float)max.z;
|
||||
vertices(0, 5) = (float)max.x; vertices(1, 5) = (float)min.y; vertices(2, 5) = (float)max.z;
|
||||
vertices(0, 6) = (float)max.x; vertices(1, 6) = (float)max.y; vertices(2, 6) = (float)max.z;
|
||||
vertices(0, 7) = (float)min.x; vertices(1, 7) = (float)max.y; vertices(2, 7) = (float)max.z;
|
||||
|
||||
Eigen::Transform<float, 3, Eigen::Affine> m;
|
||||
::memcpy((void*)m.data(), (const void*)matrix.data(), 16 * sizeof(float));
|
||||
Eigen::Matrix<float, 3, 8> transf_vertices = m * vertices.colwise().homogeneous();
|
||||
|
||||
float min_x = transf_vertices(0, 0);
|
||||
float max_x = transf_vertices(0, 0);
|
||||
float min_y = transf_vertices(1, 0);
|
||||
float max_y = transf_vertices(1, 0);
|
||||
float min_z = transf_vertices(2, 0);
|
||||
float max_z = transf_vertices(2, 0);
|
||||
|
||||
for (int i = 1; i < 8; ++i)
|
||||
{
|
||||
min_x = std::min(min_x, transf_vertices(0, i));
|
||||
max_x = std::max(max_x, transf_vertices(0, i));
|
||||
min_y = std::min(min_y, transf_vertices(1, i));
|
||||
max_y = std::max(max_y, transf_vertices(1, i));
|
||||
min_z = std::min(min_z, transf_vertices(2, i));
|
||||
max_z = std::max(max_z, transf_vertices(2, i));
|
||||
}
|
||||
|
||||
return BoundingBoxf3(Pointf3((coordf_t)min_x, (coordf_t)min_y, (coordf_t)min_z), Pointf3((coordf_t)max_x, (coordf_t)max_y, (coordf_t)max_z));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -148,6 +148,8 @@ public:
|
|||
BoundingBoxf3() : BoundingBox3Base<Pointf3>() {};
|
||||
BoundingBoxf3(const Pointf3 &pmin, const Pointf3 &pmax) : BoundingBox3Base<Pointf3>(pmin, pmax) {};
|
||||
BoundingBoxf3(const std::vector<Pointf3> &points) : BoundingBox3Base<Pointf3>(points) {};
|
||||
|
||||
BoundingBoxf3 transformed(const std::vector<float>& matrix) const;
|
||||
};
|
||||
|
||||
template<typename VT>
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
namespace Slic3r {
|
||||
|
||||
// Escape \n, \r and backslash
|
||||
std::string escape_string_cstyle(const std::string &str)
|
||||
{
|
||||
// Allocate a buffer twice the input string length,
|
||||
|
@ -28,9 +29,15 @@ std::string escape_string_cstyle(const std::string &str)
|
|||
char *outptr = out.data();
|
||||
for (size_t i = 0; i < str.size(); ++ i) {
|
||||
char c = str[i];
|
||||
if (c == '\n' || c == '\r') {
|
||||
if (c == '\r') {
|
||||
(*outptr ++) = '\\';
|
||||
(*outptr ++) = 'r';
|
||||
} else if (c == '\n') {
|
||||
(*outptr ++) = '\\';
|
||||
(*outptr ++) = 'n';
|
||||
} else if (c == '\\') {
|
||||
(*outptr ++) = '\\';
|
||||
(*outptr ++) = '\\';
|
||||
} else
|
||||
(*outptr ++) = c;
|
||||
}
|
||||
|
@ -69,7 +76,10 @@ std::string escape_strings_cstyle(const std::vector<std::string> &strs)
|
|||
if (c == '\\' || c == '"') {
|
||||
(*outptr ++) = '\\';
|
||||
(*outptr ++) = c;
|
||||
} else if (c == '\n' || c == '\r') {
|
||||
} else if (c == '\r') {
|
||||
(*outptr ++) = '\\';
|
||||
(*outptr ++) = 'r';
|
||||
} else if (c == '\n') {
|
||||
(*outptr ++) = '\\';
|
||||
(*outptr ++) = 'n';
|
||||
} else
|
||||
|
@ -84,6 +94,7 @@ std::string escape_strings_cstyle(const std::vector<std::string> &strs)
|
|||
return std::string(out.data(), outptr - out.data());
|
||||
}
|
||||
|
||||
// Unescape \n, \r and backslash
|
||||
bool unescape_string_cstyle(const std::string &str, std::string &str_out)
|
||||
{
|
||||
std::vector<char> out(str.size(), 0);
|
||||
|
@ -94,8 +105,12 @@ bool unescape_string_cstyle(const std::string &str, std::string &str_out)
|
|||
if (++ i == str.size())
|
||||
return false;
|
||||
c = str[i];
|
||||
if (c == 'n')
|
||||
if (c == 'r')
|
||||
(*outptr ++) = '\r';
|
||||
else if (c == 'n')
|
||||
(*outptr ++) = '\n';
|
||||
else
|
||||
(*outptr ++) = c;
|
||||
} else
|
||||
(*outptr ++) = c;
|
||||
}
|
||||
|
@ -134,7 +149,9 @@ bool unescape_strings_cstyle(const std::string &str, std::vector<std::string> &o
|
|||
if (++ i == str.size())
|
||||
return false;
|
||||
c = str[i];
|
||||
if (c == 'n')
|
||||
if (c == 'r')
|
||||
c = '\r';
|
||||
else if (c == 'n')
|
||||
c = '\n';
|
||||
}
|
||||
buf.push_back(c);
|
||||
|
@ -188,7 +205,10 @@ void ConfigBase::apply_only(const ConfigBase &other, const t_config_option_keys
|
|||
throw UnknownOptionException(opt_key);
|
||||
}
|
||||
const ConfigOption *other_opt = other.option(opt_key);
|
||||
if (other_opt != nullptr)
|
||||
if (other_opt == nullptr) {
|
||||
// The key was not found in the source config, therefore it will not be initialized!
|
||||
// printf("Not found, therefore not initialized: %s\n", opt_key.c_str());
|
||||
} else
|
||||
my_opt->set(other_opt);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -291,6 +291,8 @@ public:
|
|||
ConfigOptionFloats() : ConfigOptionVector<double>() {}
|
||||
explicit ConfigOptionFloats(size_t n, double value) : ConfigOptionVector<double>(n, value) {}
|
||||
explicit ConfigOptionFloats(std::initializer_list<double> il) : ConfigOptionVector<double>(std::move(il)) {}
|
||||
explicit ConfigOptionFloats(const std::vector<double> &vec) : ConfigOptionVector<double>(vec) {}
|
||||
explicit ConfigOptionFloats(std::vector<double> &&vec) : ConfigOptionVector<double>(std::move(vec)) {}
|
||||
|
||||
static ConfigOptionType static_type() { return coFloats; }
|
||||
ConfigOptionType type() const override { return static_type(); }
|
||||
|
|
|
@ -470,9 +470,9 @@ static bool prepare_infill_hatching_segments(
|
|||
int ir = std::min<int>(int(out.segs.size()) - 1, (r - x0) / line_spacing);
|
||||
// The previous tests were done with floating point arithmetics over an epsilon-extended interval.
|
||||
// Now do the same tests with exact arithmetics over the exact interval.
|
||||
while (il <= ir && Int128::orient(out.segs[il].pos, out.segs[il].pos + out.direction, *pl) < 0)
|
||||
while (il <= ir && int128::orient(out.segs[il].pos, out.segs[il].pos + out.direction, *pl) < 0)
|
||||
++ il;
|
||||
while (il <= ir && Int128::orient(out.segs[ir].pos, out.segs[ir].pos + out.direction, *pr) > 0)
|
||||
while (il <= ir && int128::orient(out.segs[ir].pos, out.segs[ir].pos + out.direction, *pr) > 0)
|
||||
-- ir;
|
||||
// Here it is ensured, that
|
||||
// 1) out.seg is not parallel to (pl, pr)
|
||||
|
@ -489,8 +489,8 @@ static bool prepare_infill_hatching_segments(
|
|||
is.iSegment = iSegment;
|
||||
// Test whether the calculated intersection point falls into the bounding box of the input segment.
|
||||
// +-1 to take rounding into account.
|
||||
assert(Int128::orient(out.segs[i].pos, out.segs[i].pos + out.direction, *pl) >= 0);
|
||||
assert(Int128::orient(out.segs[i].pos, out.segs[i].pos + out.direction, *pr) <= 0);
|
||||
assert(int128::orient(out.segs[i].pos, out.segs[i].pos + out.direction, *pl) >= 0);
|
||||
assert(int128::orient(out.segs[i].pos, out.segs[i].pos + out.direction, *pr) <= 0);
|
||||
assert(is.pos().x + 1 >= std::min(pl->x, pr->x));
|
||||
assert(is.pos().y + 1 >= std::min(pl->y, pr->y));
|
||||
assert(is.pos().x <= std::max(pl->x, pr->x) + 1);
|
||||
|
@ -527,7 +527,7 @@ static bool prepare_infill_hatching_segments(
|
|||
const Points &contour = poly_with_offset.contour(iContour).points;
|
||||
size_t iSegment = sil.intersections[i].iSegment;
|
||||
size_t iPrev = ((iSegment == 0) ? contour.size() : iSegment) - 1;
|
||||
int dir = Int128::cross(contour[iSegment] - contour[iPrev], sil.dir);
|
||||
int dir = int128::cross(contour[iSegment] - contour[iPrev], sil.dir);
|
||||
bool low = dir > 0;
|
||||
sil.intersections[i].type = poly_with_offset.is_contour_outer(iContour) ?
|
||||
(low ? SegmentIntersection::OUTER_LOW : SegmentIntersection::OUTER_HIGH) :
|
||||
|
|
|
@ -13,9 +13,7 @@
|
|||
|
||||
#include <boost/filesystem/operations.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
//############################################################################################################################################
|
||||
#include <boost/nowide/fstream.hpp>
|
||||
//############################################################################################################################################
|
||||
#include <miniz/miniz_zip.h>
|
||||
|
||||
#if 0
|
||||
|
|
|
@ -374,6 +374,12 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_
|
|||
throw std::runtime_error(std::string("G-code export to ") + path + " failed\nIs the disk full?\n");
|
||||
}
|
||||
fclose(file);
|
||||
|
||||
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 (! this->m_placeholder_parser_failed_templates.empty()) {
|
||||
// G-code export proceeded, but some of the PlaceholderParser substitutions failed.
|
||||
std::string msg = std::string("G-code export to ") + path + " failed due to invalid custom G-code sections:\n\n";
|
||||
|
@ -403,10 +409,48 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
|
|||
{
|
||||
PROFILE_FUNC();
|
||||
|
||||
// resets time estimator
|
||||
m_time_estimator.reset();
|
||||
m_time_estimator.set_dialect(print.config.gcode_flavor);
|
||||
// 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 && 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]);
|
||||
}
|
||||
// resets analyzer
|
||||
m_analyzer.reset();
|
||||
m_enable_analyzer = preview_data != nullptr;
|
||||
|
@ -569,6 +613,9 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
|
|||
|
||||
m_cooling_buffer->set_current_extruder(initial_extruder_id);
|
||||
|
||||
// Emit machine envelope limits for the Marlin firmware.
|
||||
this->print_machine_envelope(file, print);
|
||||
|
||||
// Disable fan.
|
||||
if (! print.config.cooling.get_at(initial_extruder_id) || print.config.disable_fan_first_layers.get_at(initial_extruder_id))
|
||||
_write(file, m_writer.set_fan(0, true));
|
||||
|
@ -806,7 +853,9 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
|
|||
_write(file, m_writer.postamble());
|
||||
|
||||
// calculates estimated printing time
|
||||
m_time_estimator.calculate_time();
|
||||
m_normal_time_estimator.calculate_time();
|
||||
if (m_silent_time_estimator_enabled)
|
||||
m_silent_time_estimator.calculate_time();
|
||||
|
||||
// Get filament stats.
|
||||
print.filament_stats.clear();
|
||||
|
@ -814,13 +863,14 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
|
|||
print.total_extruded_volume = 0.;
|
||||
print.total_weight = 0.;
|
||||
print.total_cost = 0.;
|
||||
print.estimated_print_time = m_time_estimator.get_time_hms();
|
||||
print.estimated_normal_print_time = m_normal_time_estimator.get_time_dhms();
|
||||
print.estimated_silent_print_time = m_silent_time_estimator_enabled ? m_silent_time_estimator.get_time_dhms() : "N/A";
|
||||
for (const Extruder &extruder : m_writer.extruders()) {
|
||||
double used_filament = extruder.used_filament();
|
||||
double extruded_volume = extruder.extruded_volume();
|
||||
double filament_weight = extruded_volume * extruder.filament_density() * 0.001;
|
||||
double filament_cost = filament_weight * extruder.filament_cost() * 0.001;
|
||||
print.filament_stats.insert(std::pair<size_t,float>(extruder.id(), used_filament));
|
||||
print.filament_stats.insert(std::pair<size_t, float>(extruder.id(), (float)used_filament));
|
||||
_write_format(file, "; filament used = %.1lfmm (%.1lfcm3)\n", used_filament, extruded_volume * 0.001);
|
||||
if (filament_weight > 0.) {
|
||||
print.total_weight = print.total_weight + filament_weight;
|
||||
|
@ -834,7 +884,9 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
|
|||
print.total_extruded_volume = print.total_extruded_volume + extruded_volume;
|
||||
}
|
||||
_write_format(file, "; total filament cost = %.1lf\n", print.total_cost);
|
||||
_write_format(file, "; estimated printing time = %s\n", m_time_estimator.get_time_hms().c_str());
|
||||
_write_format(file, "; estimated printing time (normal mode) = %s\n", m_normal_time_estimator.get_time_dhms().c_str());
|
||||
if (m_silent_time_estimator_enabled)
|
||||
_write_format(file, "; estimated printing time (silent mode) = %s\n", m_silent_time_estimator.get_time_dhms().c_str());
|
||||
|
||||
// Append full config.
|
||||
_write(file, "\n");
|
||||
|
@ -919,6 +971,35 @@ static bool custom_gcode_sets_temperature(const std::string &gcode, const int mc
|
|||
return temp_set_by_gcode;
|
||||
}
|
||||
|
||||
// Print the machine envelope G-code for the Marlin firmware based on the "machine_max_xxx" parameters.
|
||||
// Do not process this piece of G-code by the time estimator, it already knows the values through another sources.
|
||||
void GCode::print_machine_envelope(FILE *file, Print &print)
|
||||
{
|
||||
if (print.config.gcode_flavor.value == gcfMarlin) {
|
||||
fprintf(file, "M201 X%d Y%d Z%d E%d ; sets maximum accelerations, mm/sec^2\n",
|
||||
int(print.config.machine_max_acceleration_x.values.front() + 0.5),
|
||||
int(print.config.machine_max_acceleration_y.values.front() + 0.5),
|
||||
int(print.config.machine_max_acceleration_z.values.front() + 0.5),
|
||||
int(print.config.machine_max_acceleration_e.values.front() + 0.5));
|
||||
fprintf(file, "M203 X%d Y%d Z%d E%d ; sets maximum feedrates, mm/sec\n",
|
||||
int(print.config.machine_max_feedrate_x.values.front() + 0.5),
|
||||
int(print.config.machine_max_feedrate_y.values.front() + 0.5),
|
||||
int(print.config.machine_max_feedrate_z.values.front() + 0.5),
|
||||
int(print.config.machine_max_feedrate_e.values.front() + 0.5));
|
||||
fprintf(file, "M204 S%d T%d ; sets acceleration (S) and retract acceleration (T), mm/sec^2\n",
|
||||
int(print.config.machine_max_acceleration_extruding.values.front() + 0.5),
|
||||
int(print.config.machine_max_acceleration_retracting.values.front() + 0.5));
|
||||
fprintf(file, "M205 X%.2lf Y%.2lf Z%.2lf E%.2lf ; sets the jerk limits, mm/sec\n",
|
||||
print.config.machine_max_jerk_x.values.front(),
|
||||
print.config.machine_max_jerk_y.values.front(),
|
||||
print.config.machine_max_jerk_z.values.front(),
|
||||
print.config.machine_max_jerk_e.values.front());
|
||||
fprintf(file, "M205 S%d T%d ; sets the minimum extruding and travel feed rate, mm/sec\n",
|
||||
int(print.config.machine_min_extruding_rate.values.front() + 0.5),
|
||||
int(print.config.machine_min_travel_rate.values.front() + 0.5));
|
||||
}
|
||||
}
|
||||
|
||||
// Write 1st layer bed temperatures into the G-code.
|
||||
// Only do that if the start G-code does not already contain any M-code controlling an extruder temperature.
|
||||
// M140 - Set Extruder Temperature
|
||||
|
@ -1406,7 +1487,7 @@ void GCode::process_layer(
|
|||
if (m_pressure_equalizer)
|
||||
gcode = m_pressure_equalizer->process(gcode.c_str(), false);
|
||||
// printf("G-code after filter:\n%s\n", out.c_str());
|
||||
|
||||
|
||||
_write(file, gcode);
|
||||
}
|
||||
|
||||
|
@ -1418,15 +1499,22 @@ void GCode::apply_print_config(const PrintConfig &print_config)
|
|||
|
||||
void GCode::append_full_config(const Print& print, std::string& str)
|
||||
{
|
||||
const StaticPrintConfig *configs[] = { &print.config, &print.default_object_config, &print.default_region_config };
|
||||
const StaticPrintConfig *configs[] = { static_cast<const GCodeConfig*>(&print.config), &print.default_object_config, &print.default_region_config };
|
||||
for (size_t i = 0; i < sizeof(configs) / sizeof(configs[0]); ++i) {
|
||||
const StaticPrintConfig *cfg = configs[i];
|
||||
for (const std::string &key : cfg->keys())
|
||||
{
|
||||
if (key != "compatible_printers")
|
||||
str += "; " + key + " = " + cfg->serialize(key) + "\n";
|
||||
}
|
||||
}
|
||||
const DynamicConfig &full_config = print.placeholder_parser.config();
|
||||
for (const char *key : {
|
||||
"print_settings_id", "filament_settings_id", "printer_settings_id",
|
||||
"printer_model", "printer_variant", "default_print_profile", "default_filament_profile",
|
||||
"compatible_printers_condition_cummulative", "inherits_cummulative" }) {
|
||||
const ConfigOption *opt = full_config.option(key);
|
||||
if (opt != nullptr)
|
||||
str += std::string("; ") + key + " = " + opt->serialize() + "\n";
|
||||
}
|
||||
}
|
||||
|
||||
void GCode::set_extruders(const std::vector<unsigned int> &extruder_ids)
|
||||
|
@ -2066,7 +2154,9 @@ void GCode::_write(FILE* file, const char *what)
|
|||
// writes string to file
|
||||
fwrite(gcode, 1, ::strlen(gcode), file);
|
||||
// updates time estimator and gcode lines vector
|
||||
m_time_estimator.add_gcode_block(gcode);
|
||||
m_normal_time_estimator.add_gcode_block(gcode);
|
||||
if (m_silent_time_estimator_enabled)
|
||||
m_silent_time_estimator.add_gcode_block(gcode);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -133,6 +133,9 @@ public:
|
|||
m_last_height(GCodeAnalyzer::Default_Height),
|
||||
m_brim_done(false),
|
||||
m_second_layer_things_done(false),
|
||||
m_normal_time_estimator(GCodeTimeEstimator::Normal),
|
||||
m_silent_time_estimator(GCodeTimeEstimator::Silent),
|
||||
m_silent_time_estimator_enabled(false),
|
||||
m_last_obj_copy(nullptr, Point(std::numeric_limits<coord_t>::max(), std::numeric_limits<coord_t>::max()))
|
||||
{}
|
||||
~GCode() {}
|
||||
|
@ -303,8 +306,10 @@ protected:
|
|||
// Index of a last object copy extruded.
|
||||
std::pair<const PrintObject*, Point> m_last_obj_copy;
|
||||
|
||||
// Time estimator
|
||||
GCodeTimeEstimator m_time_estimator;
|
||||
// Time estimators
|
||||
GCodeTimeEstimator m_normal_time_estimator;
|
||||
GCodeTimeEstimator m_silent_time_estimator;
|
||||
bool m_silent_time_estimator_enabled;
|
||||
|
||||
// Analyzer
|
||||
GCodeAnalyzer m_analyzer;
|
||||
|
@ -322,6 +327,7 @@ protected:
|
|||
void _write_format(FILE* file, const char* format, ...);
|
||||
|
||||
std::string _extrude(const ExtrusionPath &path, std::string description = "", double speed = -1);
|
||||
void print_machine_envelope(FILE *file, Print &print);
|
||||
void _print_first_layer_bed_temperature(FILE *file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait);
|
||||
void _print_first_layer_extruder_temperatures(FILE *file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait);
|
||||
// this flag triggers first layer speeds
|
||||
|
|
|
@ -2,7 +2,12 @@
|
|||
#include "PreviewData.hpp"
|
||||
#include <float.h>
|
||||
#include <wx/intl.h>
|
||||
#include "slic3r/GUI/GUI.hpp"
|
||||
#include <I18N.hpp>
|
||||
|
||||
#include <boost/format.hpp>
|
||||
|
||||
//! macro used to mark string used at localization,
|
||||
#define L(s) (s)
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
@ -405,7 +410,7 @@ GCodePreviewData::LegendItemsList GCodePreviewData::get_legend_items(const std::
|
|||
items.reserve(last_valid - first_valid + 1);
|
||||
for (unsigned int i = (unsigned int)first_valid; i <= (unsigned int)last_valid; ++i)
|
||||
{
|
||||
items.emplace_back(_CHB(extrusion.role_names[i].c_str()).data(), extrusion.role_colors[i]);
|
||||
items.emplace_back(Slic3r::I18N::translate(extrusion.role_names[i]), extrusion.role_colors[i]);
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -436,13 +441,9 @@ GCodePreviewData::LegendItemsList GCodePreviewData::get_legend_items(const std::
|
|||
items.reserve(tools_colors_count);
|
||||
for (unsigned int i = 0; i < tools_colors_count; ++i)
|
||||
{
|
||||
char buf[MIN_BUF_LENGTH_FOR_L];
|
||||
sprintf(buf, _CHB(L("Extruder %d")), i + 1);
|
||||
|
||||
GCodePreviewData::Color color;
|
||||
::memcpy((void*)color.rgba, (const void*)(tool_colors.data() + i * 4), 4 * sizeof(float));
|
||||
|
||||
items.emplace_back(buf, color);
|
||||
items.emplace_back((boost::format(Slic3r::I18N::translate(L("Extruder %d"))) % (i + 1)).str(), color);
|
||||
}
|
||||
|
||||
break;
|
||||
|
|
|
@ -141,10 +141,10 @@ public:
|
|||
}
|
||||
|
||||
m_gcode += "G1";
|
||||
if (std::abs(dx) > EPSILON)
|
||||
if (std::abs(rot.x - rotated_current_pos.x) > EPSILON)
|
||||
m_gcode += set_format_X(rot.x);
|
||||
|
||||
if (std::abs(dy) > EPSILON)
|
||||
if (std::abs(rot.y - rotated_current_pos.y) > EPSILON)
|
||||
m_gcode += set_format_Y(rot.y);
|
||||
|
||||
if (e != 0.f)
|
||||
|
|
|
@ -4,15 +4,20 @@
|
|||
|
||||
#include <Shiny/Shiny.h>
|
||||
|
||||
#include <boost/nowide/fstream.hpp>
|
||||
#include <boost/nowide/cstdio.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
||||
static const float MMMIN_TO_MMSEC = 1.0f / 60.0f;
|
||||
static const float MILLISEC_TO_SEC = 0.001f;
|
||||
static const float INCHES_TO_MM = 25.4f;
|
||||
|
||||
static const float DEFAULT_FEEDRATE = 1500.0f; // from Prusa Firmware (Marlin_main.cpp)
|
||||
static const float DEFAULT_ACCELERATION = 1500.0f; // Prusa Firmware 1_75mm_MK2
|
||||
static const float DEFAULT_RETRACT_ACCELERATION = 1500.0f; // Prusa Firmware 1_75mm_MK2
|
||||
static const float DEFAULT_AXIS_MAX_FEEDRATE[] = { 500.0f, 500.0f, 12.0f, 120.0f }; // Prusa Firmware 1_75mm_MK2
|
||||
static const float DEFAULT_AXIS_MAX_ACCELERATION[] = { 9000.0f, 9000.0f, 500.0f, 10000.0f }; // Prusa Firmware 1_75mm_MK2
|
||||
static const float DEFAULT_AXIS_MAX_JERK[] = { 10.0f, 10.0f, 0.2f, 2.5f }; // from Prusa Firmware (Configuration.h)
|
||||
static const float DEFAULT_AXIS_MAX_JERK[] = { 10.0f, 10.0f, 0.4f, 2.5f }; // from Prusa Firmware (Configuration.h)
|
||||
static const float DEFAULT_MINIMUM_FEEDRATE = 0.0f; // from Prusa Firmware (Configuration_adv.h)
|
||||
static const float DEFAULT_MINIMUM_TRAVEL_FEEDRATE = 0.0f; // from Prusa Firmware (Configuration_adv.h)
|
||||
static const float DEFAULT_EXTRUDE_FACTOR_OVERRIDE_PERCENTAGE = 1.0f; // 100 percent
|
||||
|
@ -73,6 +78,11 @@ namespace Slic3r {
|
|||
return ::sqrt(value);
|
||||
}
|
||||
|
||||
GCodeTimeEstimator::Block::Block()
|
||||
: st_synchronized(false)
|
||||
{
|
||||
}
|
||||
|
||||
float GCodeTimeEstimator::Block::move_length() const
|
||||
{
|
||||
float length = ::sqrt(sqr(delta_pos[X]) + sqr(delta_pos[Y]) + sqr(delta_pos[Z]));
|
||||
|
@ -159,63 +169,13 @@ namespace Slic3r {
|
|||
}
|
||||
#endif // ENABLE_MOVE_STATS
|
||||
|
||||
GCodeTimeEstimator::GCodeTimeEstimator()
|
||||
GCodeTimeEstimator::GCodeTimeEstimator(EMode mode)
|
||||
: _mode(mode)
|
||||
{
|
||||
reset();
|
||||
set_default();
|
||||
}
|
||||
|
||||
void GCodeTimeEstimator::calculate_time_from_text(const std::string& gcode)
|
||||
{
|
||||
reset();
|
||||
|
||||
_parser.parse_buffer(gcode,
|
||||
[this](GCodeReader &reader, const GCodeReader::GCodeLine &line)
|
||||
{ this->_process_gcode_line(reader, line); });
|
||||
|
||||
_calculate_time();
|
||||
|
||||
#if ENABLE_MOVE_STATS
|
||||
_log_moves_stats();
|
||||
#endif // ENABLE_MOVE_STATS
|
||||
|
||||
_reset_blocks();
|
||||
_reset();
|
||||
}
|
||||
|
||||
void GCodeTimeEstimator::calculate_time_from_file(const std::string& file)
|
||||
{
|
||||
reset();
|
||||
|
||||
_parser.parse_file(file, boost::bind(&GCodeTimeEstimator::_process_gcode_line, this, _1, _2));
|
||||
_calculate_time();
|
||||
|
||||
#if ENABLE_MOVE_STATS
|
||||
_log_moves_stats();
|
||||
#endif // ENABLE_MOVE_STATS
|
||||
|
||||
_reset_blocks();
|
||||
_reset();
|
||||
}
|
||||
|
||||
void GCodeTimeEstimator::calculate_time_from_lines(const std::vector<std::string>& gcode_lines)
|
||||
{
|
||||
reset();
|
||||
|
||||
auto action = [this](GCodeReader &reader, const GCodeReader::GCodeLine &line)
|
||||
{ this->_process_gcode_line(reader, line); };
|
||||
for (const std::string& line : gcode_lines)
|
||||
_parser.parse_line(line, action);
|
||||
_calculate_time();
|
||||
|
||||
#if ENABLE_MOVE_STATS
|
||||
_log_moves_stats();
|
||||
#endif // ENABLE_MOVE_STATS
|
||||
|
||||
_reset_blocks();
|
||||
_reset();
|
||||
}
|
||||
|
||||
void GCodeTimeEstimator::add_gcode_line(const std::string& gcode_line)
|
||||
{
|
||||
PROFILE_FUNC();
|
||||
|
@ -239,14 +199,167 @@ namespace Slic3r {
|
|||
void GCodeTimeEstimator::calculate_time()
|
||||
{
|
||||
PROFILE_FUNC();
|
||||
_reset_time();
|
||||
_set_blocks_st_synchronize(false);
|
||||
_calculate_time();
|
||||
|
||||
#if ENABLE_MOVE_STATS
|
||||
_log_moves_stats();
|
||||
#endif // ENABLE_MOVE_STATS
|
||||
}
|
||||
|
||||
_reset_blocks();
|
||||
_reset();
|
||||
void GCodeTimeEstimator::calculate_time_from_text(const std::string& gcode)
|
||||
{
|
||||
reset();
|
||||
|
||||
_parser.parse_buffer(gcode,
|
||||
[this](GCodeReader &reader, const GCodeReader::GCodeLine &line)
|
||||
{ this->_process_gcode_line(reader, line); });
|
||||
|
||||
_calculate_time();
|
||||
|
||||
#if ENABLE_MOVE_STATS
|
||||
_log_moves_stats();
|
||||
#endif // ENABLE_MOVE_STATS
|
||||
}
|
||||
|
||||
void GCodeTimeEstimator::calculate_time_from_file(const std::string& file)
|
||||
{
|
||||
reset();
|
||||
|
||||
_parser.parse_file(file, boost::bind(&GCodeTimeEstimator::_process_gcode_line, this, _1, _2));
|
||||
_calculate_time();
|
||||
|
||||
#if ENABLE_MOVE_STATS
|
||||
_log_moves_stats();
|
||||
#endif // ENABLE_MOVE_STATS
|
||||
}
|
||||
|
||||
void GCodeTimeEstimator::calculate_time_from_lines(const std::vector<std::string>& gcode_lines)
|
||||
{
|
||||
reset();
|
||||
|
||||
auto action = [this](GCodeReader &reader, const GCodeReader::GCodeLine &line)
|
||||
{ this->_process_gcode_line(reader, line); };
|
||||
for (const std::string& line : gcode_lines)
|
||||
_parser.parse_line(line, action);
|
||||
_calculate_time();
|
||||
|
||||
#if ENABLE_MOVE_STATS
|
||||
_log_moves_stats();
|
||||
#endif // ENABLE_MOVE_STATS
|
||||
}
|
||||
|
||||
bool GCodeTimeEstimator::post_process_remaining_times(const std::string& filename, float interval)
|
||||
{
|
||||
boost::nowide::ifstream in(filename);
|
||||
if (!in.good())
|
||||
throw std::runtime_error(std::string("Remaining times export failed.\nCannot open file for reading.\n"));
|
||||
|
||||
std::string path_tmp = filename + ".times";
|
||||
|
||||
FILE* out = boost::nowide::fopen(path_tmp.c_str(), "wb");
|
||||
if (out == nullptr)
|
||||
throw std::runtime_error(std::string("Remaining times export failed.\nCannot open file for writing.\n"));
|
||||
|
||||
std::string time_mask;
|
||||
switch (_mode)
|
||||
{
|
||||
default:
|
||||
case Normal:
|
||||
{
|
||||
time_mask = "M73 P%s R%s\n";
|
||||
break;
|
||||
}
|
||||
case Silent:
|
||||
{
|
||||
time_mask = "M73 Q%s S%s\n";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int g1_lines_count = 0;
|
||||
float last_recorded_time = 0.0f;
|
||||
std::string gcode_line;
|
||||
// buffer line to export only when greater than 64K to reduce writing calls
|
||||
std::string export_line;
|
||||
char time_line[64];
|
||||
while (std::getline(in, gcode_line))
|
||||
{
|
||||
if (!in.good())
|
||||
{
|
||||
fclose(out);
|
||||
throw std::runtime_error(std::string("Remaining times export failed.\nError while reading from file.\n"));
|
||||
}
|
||||
|
||||
gcode_line += "\n";
|
||||
|
||||
// add remaining time lines where needed
|
||||
_parser.parse_line(gcode_line,
|
||||
[this, &g1_lines_count, &last_recorded_time, &time_line, &gcode_line, time_mask, interval](GCodeReader& reader, const GCodeReader::GCodeLine& line)
|
||||
{
|
||||
if (line.cmd_is("G1"))
|
||||
{
|
||||
++g1_lines_count;
|
||||
|
||||
if (!line.has_e())
|
||||
return;
|
||||
|
||||
G1LineIdToBlockIdMap::const_iterator it = _g1_line_ids.find(g1_lines_count);
|
||||
if ((it != _g1_line_ids.end()) && (it->second < (unsigned int)_blocks.size()))
|
||||
{
|
||||
const Block& block = _blocks[it->second];
|
||||
if (block.elapsed_time != -1.0f)
|
||||
{
|
||||
float block_remaining_time = _time - block.elapsed_time;
|
||||
if (std::abs(last_recorded_time - block_remaining_time) > interval)
|
||||
{
|
||||
sprintf(time_line, time_mask.c_str(), std::to_string((int)(100.0f * block.elapsed_time / _time)).c_str(), _get_time_minutes(block_remaining_time).c_str());
|
||||
gcode_line += time_line;
|
||||
|
||||
last_recorded_time = block_remaining_time;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export_line += gcode_line;
|
||||
if (export_line.length() > 65535)
|
||||
{
|
||||
fwrite((const void*)export_line.c_str(), 1, export_line.length(), out);
|
||||
if (ferror(out))
|
||||
{
|
||||
in.close();
|
||||
fclose(out);
|
||||
boost::nowide::remove(path_tmp.c_str());
|
||||
throw std::runtime_error(std::string("Remaining times export failed.\nIs the disk full?\n"));
|
||||
}
|
||||
export_line.clear();
|
||||
}
|
||||
}
|
||||
|
||||
if (export_line.length() > 0)
|
||||
{
|
||||
fwrite((const void*)export_line.c_str(), 1, export_line.length(), out);
|
||||
if (ferror(out))
|
||||
{
|
||||
in.close();
|
||||
fclose(out);
|
||||
boost::nowide::remove(path_tmp.c_str());
|
||||
throw std::runtime_error(std::string("Remaining times export failed.\nIs the disk full?\n"));
|
||||
}
|
||||
}
|
||||
|
||||
fclose(out);
|
||||
in.close();
|
||||
|
||||
boost::nowide::remove(filename.c_str());
|
||||
if (boost::nowide::rename(path_tmp.c_str(), filename.c_str()) != 0)
|
||||
throw std::runtime_error(std::string("Failed to rename the output G-code file from ") + path_tmp + " to " + filename + '\n' +
|
||||
"Is " + path_tmp + " locked?" + '\n');
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GCodeTimeEstimator::set_axis_position(EAxis axis, float position)
|
||||
|
@ -389,6 +502,21 @@ namespace Slic3r {
|
|||
return _state.e_local_positioning_type;
|
||||
}
|
||||
|
||||
int GCodeTimeEstimator::get_g1_line_id() const
|
||||
{
|
||||
return _state.g1_line_id;
|
||||
}
|
||||
|
||||
void GCodeTimeEstimator::increment_g1_line_id()
|
||||
{
|
||||
++_state.g1_line_id;
|
||||
}
|
||||
|
||||
void GCodeTimeEstimator::reset_g1_line_id()
|
||||
{
|
||||
_state.g1_line_id = 0;
|
||||
}
|
||||
|
||||
void GCodeTimeEstimator::add_additional_time(float timeSec)
|
||||
{
|
||||
_state.additional_time += timeSec;
|
||||
|
@ -417,7 +545,7 @@ namespace Slic3r {
|
|||
set_minimum_feedrate(DEFAULT_MINIMUM_FEEDRATE);
|
||||
set_minimum_travel_feedrate(DEFAULT_MINIMUM_TRAVEL_FEEDRATE);
|
||||
set_extrude_factor_override_percentage(DEFAULT_EXTRUDE_FACTOR_OVERRIDE_PERCENTAGE);
|
||||
|
||||
|
||||
for (unsigned char a = X; a < Num_Axis; ++a)
|
||||
{
|
||||
EAxis axis = (EAxis)a;
|
||||
|
@ -429,7 +557,7 @@ namespace Slic3r {
|
|||
|
||||
void GCodeTimeEstimator::reset()
|
||||
{
|
||||
_time = 0.0f;
|
||||
_reset_time();
|
||||
#if ENABLE_MOVE_STATS
|
||||
_moves_stats.clear();
|
||||
#endif // ENABLE_MOVE_STATS
|
||||
|
@ -442,23 +570,14 @@ namespace Slic3r {
|
|||
return _time;
|
||||
}
|
||||
|
||||
std::string GCodeTimeEstimator::get_time_hms() const
|
||||
std::string GCodeTimeEstimator::get_time_dhms() const
|
||||
{
|
||||
float timeinsecs = get_time();
|
||||
int hours = (int)(timeinsecs / 3600.0f);
|
||||
timeinsecs -= (float)hours * 3600.0f;
|
||||
int minutes = (int)(timeinsecs / 60.0f);
|
||||
timeinsecs -= (float)minutes * 60.0f;
|
||||
return _get_time_dhms(get_time());
|
||||
}
|
||||
|
||||
char buffer[64];
|
||||
if (hours > 0)
|
||||
::sprintf(buffer, "%dh %dm %ds", hours, minutes, (int)timeinsecs);
|
||||
else if (minutes > 0)
|
||||
::sprintf(buffer, "%dm %ds", minutes, (int)timeinsecs);
|
||||
else
|
||||
::sprintf(buffer, "%ds", (int)timeinsecs);
|
||||
|
||||
return buffer;
|
||||
std::string GCodeTimeEstimator::get_time_minutes() const
|
||||
{
|
||||
return _get_time_minutes(get_time());
|
||||
}
|
||||
|
||||
void GCodeTimeEstimator::_reset()
|
||||
|
@ -471,6 +590,14 @@ namespace Slic3r {
|
|||
set_axis_position(Z, 0.0f);
|
||||
|
||||
set_additional_time(0.0f);
|
||||
|
||||
reset_g1_line_id();
|
||||
_g1_line_ids.clear();
|
||||
}
|
||||
|
||||
void GCodeTimeEstimator::_reset_time()
|
||||
{
|
||||
_time = 0.0f;
|
||||
}
|
||||
|
||||
void GCodeTimeEstimator::_reset_blocks()
|
||||
|
@ -478,6 +605,14 @@ namespace Slic3r {
|
|||
_blocks.clear();
|
||||
}
|
||||
|
||||
void GCodeTimeEstimator::_set_blocks_st_synchronize(bool state)
|
||||
{
|
||||
for (Block& block : _blocks)
|
||||
{
|
||||
block.st_synchronized = state;
|
||||
}
|
||||
}
|
||||
|
||||
void GCodeTimeEstimator::_calculate_time()
|
||||
{
|
||||
_forward_pass();
|
||||
|
@ -486,14 +621,18 @@ namespace Slic3r {
|
|||
|
||||
_time += get_additional_time();
|
||||
|
||||
for (const Block& block : _blocks)
|
||||
for (Block& block : _blocks)
|
||||
{
|
||||
if (block.st_synchronized)
|
||||
continue;
|
||||
|
||||
#if ENABLE_MOVE_STATS
|
||||
float block_time = 0.0f;
|
||||
block_time += block.acceleration_time();
|
||||
block_time += block.cruise_time();
|
||||
block_time += block.deceleration_time();
|
||||
_time += block_time;
|
||||
block.elapsed_time = _time;
|
||||
|
||||
MovesStatsMap::iterator it = _moves_stats.find(block.move_type);
|
||||
if (it == _moves_stats.end())
|
||||
|
@ -505,6 +644,7 @@ namespace Slic3r {
|
|||
_time += block.acceleration_time();
|
||||
_time += block.cruise_time();
|
||||
_time += block.deceleration_time();
|
||||
block.elapsed_time = _time;
|
||||
#endif // ENABLE_MOVE_STATS
|
||||
}
|
||||
}
|
||||
|
@ -642,6 +782,8 @@ namespace Slic3r {
|
|||
|
||||
void GCodeTimeEstimator::_processG1(const GCodeReader::GCodeLine& line)
|
||||
{
|
||||
increment_g1_line_id();
|
||||
|
||||
// updates axes positions from line
|
||||
EUnits units = get_units();
|
||||
float new_pos[Num_Axis];
|
||||
|
@ -690,13 +832,16 @@ namespace Slic3r {
|
|||
if (_curr.abs_axis_feedrate[a] > 0.0f)
|
||||
min_feedrate_factor = std::min(min_feedrate_factor, get_axis_max_feedrate((EAxis)a) / _curr.abs_axis_feedrate[a]);
|
||||
}
|
||||
|
||||
|
||||
block.feedrate.cruise = min_feedrate_factor * _curr.feedrate;
|
||||
|
||||
for (unsigned char a = X; a < Num_Axis; ++a)
|
||||
if (min_feedrate_factor < 1.0f)
|
||||
{
|
||||
_curr.axis_feedrate[a] *= min_feedrate_factor;
|
||||
_curr.abs_axis_feedrate[a] *= min_feedrate_factor;
|
||||
for (unsigned char a = X; a < Num_Axis; ++a)
|
||||
{
|
||||
_curr.axis_feedrate[a] *= min_feedrate_factor;
|
||||
_curr.abs_axis_feedrate[a] *= min_feedrate_factor;
|
||||
}
|
||||
}
|
||||
|
||||
// calculates block acceleration
|
||||
|
@ -829,6 +974,7 @@ namespace Slic3r {
|
|||
|
||||
// adds block to blocks list
|
||||
_blocks.emplace_back(block);
|
||||
_g1_line_ids.insert(G1LineIdToBlockIdMap::value_type(get_g1_line_id(), (unsigned int)_blocks.size() - 1));
|
||||
}
|
||||
|
||||
void GCodeTimeEstimator::_processG4(const GCodeReader::GCodeLine& line)
|
||||
|
@ -1043,7 +1189,7 @@ namespace Slic3r {
|
|||
void GCodeTimeEstimator::_simulate_st_synchronize()
|
||||
{
|
||||
_calculate_time();
|
||||
_reset_blocks();
|
||||
_set_blocks_st_synchronize(true);
|
||||
}
|
||||
|
||||
void GCodeTimeEstimator::_forward_pass()
|
||||
|
@ -1051,7 +1197,10 @@ namespace Slic3r {
|
|||
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;
|
||||
|
||||
_planner_forward_pass_kernel(_blocks[i], _blocks[i + 1]);
|
||||
}
|
||||
}
|
||||
|
@ -1063,6 +1212,9 @@ namespace Slic3r {
|
|||
{
|
||||
for (int i = (int)_blocks.size() - 1; i >= 1; --i)
|
||||
{
|
||||
if (_blocks[i - 1].st_synchronized || _blocks[i].st_synchronized)
|
||||
continue;
|
||||
|
||||
_planner_reverse_pass_kernel(_blocks[i - 1], _blocks[i]);
|
||||
}
|
||||
}
|
||||
|
@ -1115,6 +1267,9 @@ namespace Slic3r {
|
|||
|
||||
for (Block& b : _blocks)
|
||||
{
|
||||
if (b.st_synchronized)
|
||||
continue;
|
||||
|
||||
curr = next;
|
||||
next = &b;
|
||||
|
||||
|
@ -1144,6 +1299,33 @@ namespace Slic3r {
|
|||
}
|
||||
}
|
||||
|
||||
std::string GCodeTimeEstimator::_get_time_dhms(float time_in_secs)
|
||||
{
|
||||
int days = (int)(time_in_secs / 86400.0f);
|
||||
time_in_secs -= (float)days * 86400.0f;
|
||||
int hours = (int)(time_in_secs / 3600.0f);
|
||||
time_in_secs -= (float)hours * 3600.0f;
|
||||
int minutes = (int)(time_in_secs / 60.0f);
|
||||
time_in_secs -= (float)minutes * 60.0f;
|
||||
|
||||
char buffer[64];
|
||||
if (days > 0)
|
||||
::sprintf(buffer, "%dd %dh %dm %ds", days, hours, minutes, (int)time_in_secs);
|
||||
else if (hours > 0)
|
||||
::sprintf(buffer, "%dh %dm %ds", hours, minutes, (int)time_in_secs);
|
||||
else if (minutes > 0)
|
||||
::sprintf(buffer, "%dm %ds", minutes, (int)time_in_secs);
|
||||
else
|
||||
::sprintf(buffer, "%ds", (int)time_in_secs);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
std::string GCodeTimeEstimator::_get_time_minutes(float time_in_secs)
|
||||
{
|
||||
return std::to_string((int)(::roundf(time_in_secs / 60.0f)));
|
||||
}
|
||||
|
||||
#if ENABLE_MOVE_STATS
|
||||
void GCodeTimeEstimator::_log_moves_stats() const
|
||||
{
|
||||
|
|
|
@ -17,6 +17,12 @@ namespace Slic3r {
|
|||
class GCodeTimeEstimator
|
||||
{
|
||||
public:
|
||||
enum EMode : unsigned char
|
||||
{
|
||||
Normal,
|
||||
Silent
|
||||
};
|
||||
|
||||
enum EUnits : unsigned char
|
||||
{
|
||||
Millimeters,
|
||||
|
@ -70,7 +76,8 @@ namespace Slic3r {
|
|||
float additional_time; // s
|
||||
float minimum_feedrate; // mm/s
|
||||
float minimum_travel_feedrate; // mm/s
|
||||
float extrude_factor_override_percentage;
|
||||
float extrude_factor_override_percentage;
|
||||
unsigned int g1_line_id;
|
||||
};
|
||||
|
||||
public:
|
||||
|
@ -121,7 +128,6 @@ namespace Slic3r {
|
|||
bool nominal_length;
|
||||
};
|
||||
|
||||
|
||||
#if ENABLE_MOVE_STATS
|
||||
EMoveType move_type;
|
||||
#endif // ENABLE_MOVE_STATS
|
||||
|
@ -134,6 +140,11 @@ namespace Slic3r {
|
|||
|
||||
FeedrateProfile feedrate;
|
||||
Trapezoid trapezoid;
|
||||
float elapsed_time;
|
||||
|
||||
bool st_synchronized;
|
||||
|
||||
Block();
|
||||
|
||||
// Returns the length of the move covered by this block, in mm
|
||||
float move_length() const;
|
||||
|
@ -187,19 +198,34 @@ namespace Slic3r {
|
|||
typedef std::map<Block::EMoveType, MoveStats> MovesStatsMap;
|
||||
#endif // ENABLE_MOVE_STATS
|
||||
|
||||
typedef std::map<unsigned int, unsigned int> G1LineIdToBlockIdMap;
|
||||
|
||||
private:
|
||||
EMode _mode;
|
||||
GCodeReader _parser;
|
||||
State _state;
|
||||
Feedrates _curr;
|
||||
Feedrates _prev;
|
||||
BlocksList _blocks;
|
||||
// Map between g1 line id and blocks id, used to speed up export of remaining times
|
||||
G1LineIdToBlockIdMap _g1_line_ids;
|
||||
float _time; // s
|
||||
|
||||
#if ENABLE_MOVE_STATS
|
||||
MovesStatsMap _moves_stats;
|
||||
#endif // ENABLE_MOVE_STATS
|
||||
|
||||
public:
|
||||
GCodeTimeEstimator();
|
||||
explicit GCodeTimeEstimator(EMode mode);
|
||||
|
||||
// Adds the given gcode line
|
||||
void add_gcode_line(const std::string& gcode_line);
|
||||
|
||||
void add_gcode_block(const char *ptr);
|
||||
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();
|
||||
|
||||
// Calculates the time estimate from the given gcode in string format
|
||||
void calculate_time_from_text(const std::string& gcode);
|
||||
|
@ -210,14 +236,12 @@ namespace Slic3r {
|
|||
// Calculates the time estimate from the gcode contained in given list of gcode lines
|
||||
void calculate_time_from_lines(const std::vector<std::string>& gcode_lines);
|
||||
|
||||
// Adds the given gcode line
|
||||
void add_gcode_line(const std::string& gcode_line);
|
||||
|
||||
void add_gcode_block(const char *ptr);
|
||||
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()
|
||||
void calculate_time();
|
||||
// Process the gcode contained in the file with the given filename,
|
||||
// placing in it new lines (M73) containing the remaining time, at the given interval in seconds
|
||||
// and saving the result back in the same file
|
||||
// This time estimator should have been already used to calculate the time estimate for the gcode
|
||||
// contained in the given file before to call this method
|
||||
bool post_process_remaining_times(const std::string& filename, float interval_sec);
|
||||
|
||||
// Set current position on the given axis with the given value
|
||||
void set_axis_position(EAxis axis, float position);
|
||||
|
@ -263,6 +287,10 @@ namespace Slic3r {
|
|||
void set_e_local_positioning_type(EPositioningType type);
|
||||
EPositioningType get_e_local_positioning_type() const;
|
||||
|
||||
int get_g1_line_id() const;
|
||||
void increment_g1_line_id();
|
||||
void reset_g1_line_id();
|
||||
|
||||
void add_additional_time(float timeSec);
|
||||
void set_additional_time(float timeSec);
|
||||
float get_additional_time() const;
|
||||
|
@ -275,13 +303,19 @@ namespace Slic3r {
|
|||
// Returns the estimated time, in seconds
|
||||
float get_time() const;
|
||||
|
||||
// Returns the estimated time, in format HHh MMm SSs
|
||||
std::string get_time_hms() const;
|
||||
// Returns the estimated time, in format DDd HHh MMm SSs
|
||||
std::string get_time_dhms() const;
|
||||
|
||||
// Returns the estimated time, in minutes (integer)
|
||||
std::string get_time_minutes() const;
|
||||
|
||||
private:
|
||||
void _reset();
|
||||
void _reset_time();
|
||||
void _reset_blocks();
|
||||
|
||||
void _set_blocks_st_synchronize(bool state);
|
||||
|
||||
// Calculates the time estimate
|
||||
void _calculate_time();
|
||||
|
||||
|
@ -353,6 +387,12 @@ namespace Slic3r {
|
|||
|
||||
void _recalculate_trapezoids();
|
||||
|
||||
// Returns the given time is seconds in format DDd HHh MMm SSs
|
||||
static std::string _get_time_dhms(float time_in_secs);
|
||||
|
||||
// Returns the given, in minutes (integer)
|
||||
static std::string _get_time_minutes(float time_in_secs);
|
||||
|
||||
#if ENABLE_MOVE_STATS
|
||||
void _log_moves_stats() const;
|
||||
#endif // ENABLE_MOVE_STATS
|
||||
|
|
18
xs/src/libslic3r/I18N.hpp
Normal file
18
xs/src/libslic3r/I18N.hpp
Normal file
|
@ -0,0 +1,18 @@
|
|||
#ifndef slic3r_I18N_hpp_
|
||||
#define slic3r_I18N_hpp_
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
namespace I18N {
|
||||
typedef std::string (*translate_fn_type)(const char*);
|
||||
extern translate_fn_type translate_fn;
|
||||
inline void set_translate_callback(translate_fn_type fn) { translate_fn = fn; }
|
||||
inline std::string translate(const std::string &s) { return (translate_fn == nullptr) ? s : (*translate_fn)(s.c_str()); }
|
||||
inline std::string translate(const char *ptr) { return (translate_fn == nullptr) ? std::string(ptr) : (*translate_fn)(ptr); }
|
||||
} // namespace I18N
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif /* slic3r_I18N_hpp_ */
|
|
@ -48,7 +48,6 @@
|
|||
#endif
|
||||
|
||||
#include <cassert>
|
||||
#include "Point.hpp"
|
||||
|
||||
#if ! defined(_MSC_VER) && defined(__SIZEOF_INT128__)
|
||||
#define HAS_INTRINSIC_128_TYPE
|
||||
|
@ -288,20 +287,4 @@ public:
|
|||
}
|
||||
return sign_determinant_2x2(p1, q1, p2, q2) * invert;
|
||||
}
|
||||
|
||||
// Exact orientation predicate,
|
||||
// returns +1: CCW, 0: collinear, -1: CW.
|
||||
static int orient(const Slic3r::Point &p1, const Slic3r::Point &p2, const Slic3r::Point &p3)
|
||||
{
|
||||
Slic3r::Vector v1(p2 - p1);
|
||||
Slic3r::Vector v2(p3 - p1);
|
||||
return sign_determinant_2x2_filtered(v1.x, v1.y, v2.x, v2.y);
|
||||
}
|
||||
|
||||
// Exact orientation predicate,
|
||||
// returns +1: CCW, 0: collinear, -1: CW.
|
||||
static int cross(const Slic3r::Point &v1, const Slic3r::Point &v2)
|
||||
{
|
||||
return sign_determinant_2x2_filtered(v1.x, v1.y, v2.x, v2.y);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -7,6 +7,11 @@
|
|||
#include "Format/STL.hpp"
|
||||
#include "Format/3mf.hpp"
|
||||
|
||||
#include <numeric>
|
||||
#include <libnest2d.h>
|
||||
#include <ClipperUtils.hpp>
|
||||
#include "slic3r/GUI/GUI.hpp"
|
||||
|
||||
#include <float.h>
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
@ -14,6 +19,9 @@
|
|||
#include <boost/nowide/iostream.hpp>
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
|
||||
#include "SVG.hpp"
|
||||
#include <Eigen/Dense>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
unsigned int Model::s_auto_extruder_id = 1;
|
||||
|
@ -296,35 +304,369 @@ static bool _arrange(const Pointfs &sizes, coordf_t dist, const BoundingBoxf* bb
|
|||
return result;
|
||||
}
|
||||
|
||||
namespace arr {
|
||||
|
||||
using namespace libnest2d;
|
||||
|
||||
std::string toString(const Model& model, bool holes = true) {
|
||||
std::stringstream ss;
|
||||
|
||||
ss << "{\n";
|
||||
|
||||
for(auto objptr : model.objects) {
|
||||
if(!objptr) continue;
|
||||
|
||||
auto rmesh = objptr->raw_mesh();
|
||||
|
||||
for(auto objinst : objptr->instances) {
|
||||
if(!objinst) continue;
|
||||
|
||||
Slic3r::TriangleMesh tmpmesh = rmesh;
|
||||
tmpmesh.scale(objinst->scaling_factor);
|
||||
objinst->transform_mesh(&tmpmesh);
|
||||
ExPolygons expolys = tmpmesh.horizontal_projection();
|
||||
for(auto& expoly_complex : expolys) {
|
||||
|
||||
auto tmp = expoly_complex.simplify(1.0/SCALING_FACTOR);
|
||||
if(tmp.empty()) continue;
|
||||
auto expoly = tmp.front();
|
||||
expoly.contour.make_clockwise();
|
||||
for(auto& h : expoly.holes) h.make_counter_clockwise();
|
||||
|
||||
ss << "\t{\n";
|
||||
ss << "\t\t{\n";
|
||||
|
||||
for(auto v : expoly.contour.points) ss << "\t\t\t{"
|
||||
<< v.x << ", "
|
||||
<< v.y << "},\n";
|
||||
{
|
||||
auto v = expoly.contour.points.front();
|
||||
ss << "\t\t\t{" << v.x << ", " << v.y << "},\n";
|
||||
}
|
||||
ss << "\t\t},\n";
|
||||
|
||||
// Holes:
|
||||
ss << "\t\t{\n";
|
||||
if(holes) for(auto h : expoly.holes) {
|
||||
ss << "\t\t\t{\n";
|
||||
for(auto v : h.points) ss << "\t\t\t\t{"
|
||||
<< v.x << ", "
|
||||
<< v.y << "},\n";
|
||||
{
|
||||
auto v = h.points.front();
|
||||
ss << "\t\t\t\t{" << v.x << ", " << v.y << "},\n";
|
||||
}
|
||||
ss << "\t\t\t},\n";
|
||||
}
|
||||
ss << "\t\t},\n";
|
||||
|
||||
ss << "\t},\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ss << "}\n";
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
void toSVG(SVG& svg, const Model& model) {
|
||||
for(auto objptr : model.objects) {
|
||||
if(!objptr) continue;
|
||||
|
||||
auto rmesh = objptr->raw_mesh();
|
||||
|
||||
for(auto objinst : objptr->instances) {
|
||||
if(!objinst) continue;
|
||||
|
||||
Slic3r::TriangleMesh tmpmesh = rmesh;
|
||||
tmpmesh.scale(objinst->scaling_factor);
|
||||
objinst->transform_mesh(&tmpmesh);
|
||||
ExPolygons expolys = tmpmesh.horizontal_projection();
|
||||
svg.draw(expolys);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A container which stores a pointer to the 3D object and its projected
|
||||
// 2D shape from top view.
|
||||
using ShapeData2D =
|
||||
std::vector<std::pair<Slic3r::ModelInstance*, Item>>;
|
||||
|
||||
ShapeData2D projectModelFromTop(const Slic3r::Model &model) {
|
||||
ShapeData2D ret;
|
||||
|
||||
auto s = std::accumulate(model.objects.begin(), model.objects.end(), 0,
|
||||
[](size_t s, ModelObject* o){
|
||||
return s + o->instances.size();
|
||||
});
|
||||
|
||||
ret.reserve(s);
|
||||
|
||||
for(auto objptr : model.objects) {
|
||||
if(objptr) {
|
||||
|
||||
auto rmesh = objptr->raw_mesh();
|
||||
|
||||
for(auto objinst : objptr->instances) {
|
||||
if(objinst) {
|
||||
Slic3r::TriangleMesh tmpmesh = rmesh;
|
||||
ClipperLib::PolygonImpl pn;
|
||||
|
||||
tmpmesh.scale(objinst->scaling_factor);
|
||||
|
||||
// TODO export the exact 2D projection
|
||||
auto p = tmpmesh.convex_hull();
|
||||
|
||||
p.make_clockwise();
|
||||
p.append(p.first_point());
|
||||
pn.Contour = Slic3rMultiPoint_to_ClipperPath( p );
|
||||
|
||||
// Efficient conversion to item.
|
||||
Item item(std::move(pn));
|
||||
|
||||
// Invalid geometries would throw exceptions when arranging
|
||||
if(item.vertexCount() > 3) {
|
||||
item.rotation(objinst->rotation);
|
||||
item.translation( {
|
||||
ClipperLib::cInt(objinst->offset.x/SCALING_FACTOR),
|
||||
ClipperLib::cInt(objinst->offset.y/SCALING_FACTOR)
|
||||
});
|
||||
ret.emplace_back(objinst, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Arranges the model objects on the screen.
|
||||
*
|
||||
* The arrangement considers multiple bins (aka. print beds) for placing all
|
||||
* the items provided in the model argument. If the items don't fit on one
|
||||
* print bed, the remaining will be placed onto newly created print beds.
|
||||
* The first_bin_only parameter, if set to true, disables this behaviour and
|
||||
* makes sure that only one print bed is filled and the remaining items will be
|
||||
* untouched. When set to false, the items which could not fit onto the
|
||||
* print bed will be placed next to the print bed so the user should see a
|
||||
* pile of items on the print bed and some other piles outside the print
|
||||
* area that can be dragged later onto the print bed as a group.
|
||||
*
|
||||
* \param model The model object with the 3D content.
|
||||
* \param dist The minimum distance which is allowed for any pair of items
|
||||
* on the print bed in any direction.
|
||||
* \param bb The bounding box of the print bed. It corresponds to the 'bin'
|
||||
* for bin packing.
|
||||
* \param first_bin_only This parameter controls whether to place the
|
||||
* remaining items which do not fit onto the print area next to the print
|
||||
* bed or leave them untouched (let the user arrange them by hand or remove
|
||||
* them).
|
||||
*/
|
||||
bool arrange(Model &model, coordf_t dist, const Slic3r::BoundingBoxf* bb,
|
||||
bool first_bin_only,
|
||||
std::function<void(unsigned)> progressind)
|
||||
{
|
||||
using ArrangeResult = _IndexedPackGroup<PolygonImpl>;
|
||||
|
||||
bool ret = true;
|
||||
|
||||
// Create the arranger config
|
||||
auto min_obj_distance = static_cast<Coord>(dist/SCALING_FACTOR);
|
||||
|
||||
// Get the 2D projected shapes with their 3D model instance pointers
|
||||
auto shapemap = arr::projectModelFromTop(model);
|
||||
|
||||
bool hasbin = bb != nullptr && bb->defined;
|
||||
double area_max = 0;
|
||||
|
||||
// Copy the references for the shapes only as the arranger expects a
|
||||
// sequence of objects convertible to Item or ClipperPolygon
|
||||
std::vector<std::reference_wrapper<Item>> shapes;
|
||||
shapes.reserve(shapemap.size());
|
||||
std::for_each(shapemap.begin(), shapemap.end(),
|
||||
[&shapes, min_obj_distance, &area_max, hasbin]
|
||||
(ShapeData2D::value_type& it)
|
||||
{
|
||||
shapes.push_back(std::ref(it.second));
|
||||
});
|
||||
|
||||
Box bin;
|
||||
|
||||
if(hasbin) {
|
||||
// Scale up the bounding box to clipper scale.
|
||||
BoundingBoxf bbb = *bb;
|
||||
bbb.scale(1.0/SCALING_FACTOR);
|
||||
|
||||
bin = Box({
|
||||
static_cast<libnest2d::Coord>(bbb.min.x),
|
||||
static_cast<libnest2d::Coord>(bbb.min.y)
|
||||
},
|
||||
{
|
||||
static_cast<libnest2d::Coord>(bbb.max.x),
|
||||
static_cast<libnest2d::Coord>(bbb.max.y)
|
||||
});
|
||||
}
|
||||
|
||||
// Will use the DJD selection heuristic with the BottomLeft placement
|
||||
// strategy
|
||||
using Arranger = Arranger<NfpPlacer, FirstFitSelection>;
|
||||
using PConf = Arranger::PlacementConfig;
|
||||
using SConf = Arranger::SelectionConfig;
|
||||
|
||||
PConf pcfg; // Placement configuration
|
||||
SConf scfg; // Selection configuration
|
||||
|
||||
// Align the arranged pile into the center of the bin
|
||||
pcfg.alignment = PConf::Alignment::CENTER;
|
||||
|
||||
// Start placing the items from the center of the print bed
|
||||
pcfg.starting_point = PConf::Alignment::CENTER;
|
||||
|
||||
// TODO cannot use rotations until multiple objects of same geometry can
|
||||
// handle different rotations
|
||||
// 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
|
||||
// calculate the convex hulls)
|
||||
pcfg.object_function = [bin, hasbin](
|
||||
NfpPlacer::Pile pile, // The currently arranged pile
|
||||
double /*area*/, // Sum area of items (not needed)
|
||||
double norm, // A norming factor for physical dimensions
|
||||
double penality) // Min penality in case of bad arrangement
|
||||
{
|
||||
auto bb = ShapeLike::boundingBox(pile);
|
||||
|
||||
// We get the current item that's being evaluated.
|
||||
auto& sh = pile.back();
|
||||
|
||||
// We retrieve the reference point of this item
|
||||
auto rv = Nfp::referenceVertex(sh);
|
||||
|
||||
// We get the distance of the reference point from the center of the
|
||||
// heat bed
|
||||
auto c = bin.center();
|
||||
auto d = PointLike::distance(rv, c);
|
||||
|
||||
// The score will be the normalized distance which will be minimized,
|
||||
// effectively creating a circle shaped pile of items
|
||||
double score = double(d)/norm;
|
||||
|
||||
// If it does not fit into the print bed we will beat it
|
||||
// with a large penality. If we would not do this, there would be only
|
||||
// one big pile that doesn't care whether it fits onto the print bed.
|
||||
if(hasbin && !NfpPlacer::wouldFit(bb, bin)) score = 2*penality - score;
|
||||
|
||||
return score;
|
||||
};
|
||||
|
||||
// Create the arranger object
|
||||
Arranger arranger(bin, min_obj_distance, pcfg, scfg);
|
||||
|
||||
// Set the progress indicator for the arranger.
|
||||
arranger.progressIndicator(progressind);
|
||||
|
||||
// Arrange and return the items with their respective indices within the
|
||||
// input sequence.
|
||||
auto result = arranger.arrangeIndexed(shapes.begin(), shapes.end());
|
||||
|
||||
auto applyResult = [&shapemap](ArrangeResult::value_type& group,
|
||||
Coord batch_offset)
|
||||
{
|
||||
for(auto& r : group) {
|
||||
auto idx = r.first; // get the original item index
|
||||
Item& item = r.second; // get the item itself
|
||||
|
||||
// Get the model instance from the shapemap using the index
|
||||
ModelInstance *inst_ptr = shapemap[idx].first;
|
||||
|
||||
// Get the tranformation data from the item object and scale it
|
||||
// appropriately
|
||||
auto off = item.translation();
|
||||
Radians rot = item.rotation();
|
||||
Pointf foff(off.X*SCALING_FACTOR + batch_offset,
|
||||
off.Y*SCALING_FACTOR);
|
||||
|
||||
// write the tranformation data into the model instance
|
||||
inst_ptr->rotation = rot;
|
||||
inst_ptr->offset = foff;
|
||||
}
|
||||
};
|
||||
|
||||
if(first_bin_only) {
|
||||
applyResult(result.front(), 0);
|
||||
} else {
|
||||
|
||||
const auto STRIDE_PADDING = 1.2;
|
||||
|
||||
Coord stride = static_cast<Coord>(STRIDE_PADDING*
|
||||
bin.width()*SCALING_FACTOR);
|
||||
Coord batch_offset = 0;
|
||||
|
||||
for(auto& group : result) {
|
||||
applyResult(group, batch_offset);
|
||||
|
||||
// Only the first pack group can be placed onto the print bed. The
|
||||
// other objects which could not fit will be placed next to the
|
||||
// print bed
|
||||
batch_offset += stride;
|
||||
}
|
||||
}
|
||||
|
||||
for(auto objptr : model.objects) objptr->invalidate_bounding_box();
|
||||
|
||||
return ret && result.size() == 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* arrange objects preserving their instance count
|
||||
but altering their instance positions */
|
||||
bool Model::arrange_objects(coordf_t dist, const BoundingBoxf* bb)
|
||||
bool Model::arrange_objects(coordf_t dist, const BoundingBoxf* bb,
|
||||
std::function<void(unsigned)> progressind)
|
||||
{
|
||||
// get the (transformed) size of each instance so that we take
|
||||
// into account their different transformations when packing
|
||||
Pointfs instance_sizes;
|
||||
Pointfs instance_centers;
|
||||
for (const ModelObject *o : this->objects)
|
||||
for (size_t i = 0; i < o->instances.size(); ++ i) {
|
||||
// an accurate snug bounding box around the transformed mesh.
|
||||
BoundingBoxf3 bbox(o->instance_bounding_box(i, true));
|
||||
instance_sizes.push_back(bbox.size());
|
||||
instance_centers.push_back(bbox.center());
|
||||
}
|
||||
bool ret = false;
|
||||
if(bb != nullptr && bb->defined) {
|
||||
// Despite the new arrange is able to run without a specified bin,
|
||||
// the perl testsuit still fails for this case. For now the safest
|
||||
// thing to do is to use the new arrange only when a proper bin is
|
||||
// specified.
|
||||
ret = arr::arrange(*this, dist, bb, false, progressind);
|
||||
} else {
|
||||
// get the (transformed) size of each instance so that we take
|
||||
// into account their different transformations when packing
|
||||
Pointfs instance_sizes;
|
||||
Pointfs instance_centers;
|
||||
for (const ModelObject *o : this->objects)
|
||||
for (size_t i = 0; i < o->instances.size(); ++ i) {
|
||||
// an accurate snug bounding box around the transformed mesh.
|
||||
BoundingBoxf3 bbox(o->instance_bounding_box(i, true));
|
||||
instance_sizes.push_back(bbox.size());
|
||||
instance_centers.push_back(bbox.center());
|
||||
}
|
||||
|
||||
Pointfs positions;
|
||||
if (! _arrange(instance_sizes, dist, bb, positions))
|
||||
return false;
|
||||
|
||||
size_t idx = 0;
|
||||
for (ModelObject *o : this->objects) {
|
||||
for (ModelInstance *i : o->instances) {
|
||||
i->offset = positions[idx] - instance_centers[idx];
|
||||
++ idx;
|
||||
Pointfs positions;
|
||||
if (! _arrange(instance_sizes, dist, bb, positions))
|
||||
return false;
|
||||
|
||||
size_t idx = 0;
|
||||
for (ModelObject *o : this->objects) {
|
||||
for (ModelInstance *i : o->instances) {
|
||||
i->offset = positions[idx] - instance_centers[idx];
|
||||
++ idx;
|
||||
}
|
||||
o->invalidate_bounding_box();
|
||||
}
|
||||
o->invalidate_bounding_box();
|
||||
}
|
||||
return true;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Duplicate the entire model preserving instance relative positions.
|
||||
|
@ -603,10 +945,7 @@ void ModelObject::clear_instances()
|
|||
|
||||
// Returns the bounding box of the transformed instances.
|
||||
// This bounding box is approximate and not snug.
|
||||
//========================================================================================================
|
||||
const BoundingBoxf3& ModelObject::bounding_box() const
|
||||
//const BoundingBoxf3& ModelObject::bounding_box()
|
||||
//========================================================================================================
|
||||
{
|
||||
if (! m_bounding_box_valid) {
|
||||
BoundingBoxf3 raw_bbox;
|
||||
|
@ -1048,32 +1387,16 @@ BoundingBoxf3 ModelInstance::transform_mesh_bounding_box(const TriangleMesh* mes
|
|||
|
||||
BoundingBoxf3 ModelInstance::transform_bounding_box(const BoundingBoxf3 &bbox, bool dont_translate) const
|
||||
{
|
||||
// rotate around mesh origin
|
||||
double c = cos(this->rotation);
|
||||
double s = sin(this->rotation);
|
||||
Pointf3 pts[4] = {
|
||||
bbox.min,
|
||||
bbox.max,
|
||||
Pointf3(bbox.min.x, bbox.max.y, bbox.min.z),
|
||||
Pointf3(bbox.max.x, bbox.min.y, bbox.max.z)
|
||||
};
|
||||
BoundingBoxf3 out;
|
||||
for (int i = 0; i < 4; ++ i) {
|
||||
Pointf3 &v = pts[i];
|
||||
double xold = v.x;
|
||||
double yold = v.y;
|
||||
v.x = float(c * xold - s * yold);
|
||||
v.y = float(s * xold + c * yold);
|
||||
v.x *= this->scaling_factor;
|
||||
v.y *= this->scaling_factor;
|
||||
v.z *= this->scaling_factor;
|
||||
if (! dont_translate) {
|
||||
v.x += this->offset.x;
|
||||
v.y += this->offset.y;
|
||||
}
|
||||
out.merge(v);
|
||||
}
|
||||
return out;
|
||||
Eigen::Transform<float, 3, Eigen::Affine> matrix = Eigen::Transform<float, 3, Eigen::Affine>::Identity();
|
||||
if (!dont_translate)
|
||||
matrix.translate(Eigen::Vector3f((float)offset.x, (float)offset.y, 0.0f));
|
||||
|
||||
matrix.rotate(Eigen::AngleAxisf(rotation, Eigen::Vector3f::UnitZ()));
|
||||
matrix.scale(scaling_factor);
|
||||
|
||||
std::vector<float> m(16, 0.0f);
|
||||
::memcpy((void*)m.data(), (const void*)matrix.data(), 16 * sizeof(float));
|
||||
return bbox.transformed(m);
|
||||
}
|
||||
|
||||
void ModelInstance::transform_polygon(Polygon* polygon) const
|
||||
|
|
|
@ -103,10 +103,7 @@ public:
|
|||
// Returns the bounding box of the transformed instances.
|
||||
// This bounding box is approximate and not snug.
|
||||
// This bounding box is being cached.
|
||||
//========================================================================================================
|
||||
const BoundingBoxf3& bounding_box() const;
|
||||
// const BoundingBoxf3& bounding_box();
|
||||
//========================================================================================================
|
||||
void invalidate_bounding_box() { m_bounding_box_valid = false; }
|
||||
// Returns a snug bounding box of the transformed instances.
|
||||
// This bounding box is not being cached.
|
||||
|
@ -148,10 +145,9 @@ private:
|
|||
// Parent object, owning this ModelObject.
|
||||
Model *m_model;
|
||||
// Bounding box, cached.
|
||||
//========================================================================================================
|
||||
|
||||
mutable BoundingBoxf3 m_bounding_box;
|
||||
mutable bool m_bounding_box_valid;
|
||||
//========================================================================================================
|
||||
};
|
||||
|
||||
// An object STL, or a modifier volume, over which a different set of parameters shall be applied.
|
||||
|
@ -207,7 +203,7 @@ public:
|
|||
double scaling_factor;
|
||||
Pointf offset; // in unscaled coordinates
|
||||
|
||||
ModelObject* get_object() const { return this->object; };
|
||||
ModelObject* get_object() const { return this->object; }
|
||||
|
||||
// To be called on an external mesh
|
||||
void transform_mesh(TriangleMesh* mesh, bool dont_translate = false) const;
|
||||
|
@ -278,7 +274,8 @@ public:
|
|||
void center_instances_around_point(const Pointf &point);
|
||||
void translate(coordf_t x, coordf_t y, coordf_t z) { for (ModelObject *o : this->objects) o->translate(x, y, z); }
|
||||
TriangleMesh mesh() const;
|
||||
bool arrange_objects(coordf_t dist, const BoundingBoxf* bb = NULL);
|
||||
bool arrange_objects(coordf_t dist, const BoundingBoxf* bb = NULL,
|
||||
std::function<void(unsigned)> progressind = [](unsigned){});
|
||||
// Croaks if the duplicated objects do not fit the print bed.
|
||||
void duplicate(size_t copies_num, coordf_t dist, const BoundingBoxf* bb = NULL);
|
||||
void duplicate_objects(size_t copies_num, coordf_t dist, const BoundingBoxf* bb = NULL);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "Point.hpp"
|
||||
#include "Line.hpp"
|
||||
#include "MultiPoint.hpp"
|
||||
#include "Int128.hpp"
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
|
@ -375,4 +376,20 @@ Pointf3::vector_to(const Pointf3 &point) const
|
|||
return Vectorf3(point.x - this->x, point.y - this->y, point.z - this->z);
|
||||
}
|
||||
|
||||
namespace int128 {
|
||||
|
||||
int orient(const Point &p1, const Point &p2, const Point &p3)
|
||||
{
|
||||
Slic3r::Vector v1(p2 - p1);
|
||||
Slic3r::Vector v2(p3 - p1);
|
||||
return Int128::sign_determinant_2x2_filtered(v1.x, v1.y, v2.x, v2.y);
|
||||
}
|
||||
|
||||
int cross(const Point &v1, const Point &v2)
|
||||
{
|
||||
return Int128::sign_determinant_2x2_filtered(v1.x, v1.y, v2.x, v2.y);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -81,6 +81,17 @@ inline Point operator*(double scalar, const Point& point2) { return Point(scalar
|
|||
inline int64_t cross(const Point &v1, const Point &v2) { return int64_t(v1.x) * int64_t(v2.y) - int64_t(v1.y) * int64_t(v2.x); }
|
||||
inline int64_t dot(const Point &v1, const Point &v2) { return int64_t(v1.x) * int64_t(v2.x) + int64_t(v1.y) * int64_t(v2.y); }
|
||||
|
||||
namespace int128 {
|
||||
|
||||
// Exact orientation predicate,
|
||||
// returns +1: CCW, 0: collinear, -1: CW.
|
||||
int orient(const Point &p1, const Point &p2, const Point &p3);
|
||||
|
||||
// Exact orientation predicate,
|
||||
// returns +1: CCW, 0: collinear, -1: CW.
|
||||
int cross(const Point &v1, const Slic3r::Point &v2);
|
||||
}
|
||||
|
||||
// To be used by std::unordered_map, std::unordered_multimap and friends.
|
||||
struct PointHash {
|
||||
size_t operator()(const Point &pt) const {
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "Extruder.hpp"
|
||||
#include "Flow.hpp"
|
||||
#include "Geometry.hpp"
|
||||
#include "I18N.hpp"
|
||||
#include "SupportMaterial.hpp"
|
||||
#include "GCode/WipeTowerPrusaMM.hpp"
|
||||
#include <algorithm>
|
||||
|
@ -11,6 +12,10 @@
|
|||
#include <boost/filesystem.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
//! macro used to mark string used at localization,
|
||||
//! return same string
|
||||
#define L(s) Slic3r::I18N::translate(s)
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
template class PrintState<PrintStep, psCount>;
|
||||
|
@ -452,7 +457,7 @@ bool Print::apply_config(DynamicPrintConfig config)
|
|||
const ModelVolume &volume = *object->model_object()->volumes[volume_id];
|
||||
if (this_region_config_set) {
|
||||
// If the new config for this volume differs from the other
|
||||
// volume configs currently associated to this region, it means
|
||||
// volume configs currently associated to this region, it means
|
||||
// the region subdivision does not make sense anymore.
|
||||
if (! this_region_config.equals(this->_region_config_from_model_volume(volume))) {
|
||||
rearrange_regions = true;
|
||||
|
@ -531,7 +536,7 @@ std::string Print::validate() const
|
|||
print_volume.min.z = -1e10;
|
||||
for (PrintObject *po : this->objects) {
|
||||
if (!print_volume.contains(po->model_object()->tight_bounding_box(false)))
|
||||
return "Some objects are outside of the print volume.";
|
||||
return L("Some objects are outside of the print volume.");
|
||||
}
|
||||
|
||||
if (this->config.complete_objects) {
|
||||
|
@ -558,7 +563,7 @@ std::string Print::validate() const
|
|||
Polygon p = convex_hull;
|
||||
p.translate(copy);
|
||||
if (! intersection(convex_hulls_other, p).empty())
|
||||
return "Some objects are too close; your extruder will collide with them.";
|
||||
return L("Some objects are too close; your extruder will collide with them.");
|
||||
polygons_append(convex_hulls_other, p);
|
||||
}
|
||||
}
|
||||
|
@ -573,7 +578,7 @@ std::string Print::validate() const
|
|||
// it will be printed as last one so its height doesn't matter.
|
||||
object_height.pop_back();
|
||||
if (! object_height.empty() && object_height.back() > scale_(this->config.extruder_clearance_height.value))
|
||||
return "Some objects are too tall and cannot be printed without extruder collisions.";
|
||||
return L("Some objects are too tall and cannot be printed without extruder collisions.");
|
||||
}
|
||||
} // end if (this->config.complete_objects)
|
||||
|
||||
|
@ -583,27 +588,22 @@ std::string Print::validate() const
|
|||
total_copies_count += object->copies().size();
|
||||
// #4043
|
||||
if (total_copies_count > 1 && ! this->config.complete_objects.value)
|
||||
return "The Spiral Vase option can only be used when printing a single object.";
|
||||
return L("The Spiral Vase option can only be used when printing a single object.");
|
||||
if (this->regions.size() > 1)
|
||||
return "The Spiral Vase option can only be used when printing single material objects.";
|
||||
return L("The Spiral Vase option can only be used when printing single material objects.");
|
||||
}
|
||||
|
||||
if (this->config.single_extruder_multi_material) {
|
||||
for (size_t i=1; i<this->config.nozzle_diameter.values.size(); ++i)
|
||||
if (this->config.nozzle_diameter.values[i] != this->config.nozzle_diameter.values[i-1])
|
||||
return "All extruders must have the same diameter for single extruder multimaterial printer.";
|
||||
return L("All extruders must have the same diameter for single extruder multimaterial printer.");
|
||||
}
|
||||
|
||||
if (this->has_wipe_tower() && ! this->objects.empty()) {
|
||||
#if 0
|
||||
for (auto dmr : this->config.nozzle_diameter.values)
|
||||
if (std::abs(dmr - 0.4) > EPSILON)
|
||||
return "The Wipe Tower is currently only supported for the 0.4mm nozzle diameter.";
|
||||
#endif
|
||||
if (this->config.gcode_flavor != gcfRepRap && this->config.gcode_flavor != gcfMarlin)
|
||||
return "The Wipe Tower is currently only supported for the Marlin and RepRap/Sprinter G-code flavors.";
|
||||
return L("The Wipe Tower is currently only supported for the Marlin and RepRap/Sprinter G-code flavors.");
|
||||
if (! this->config.use_relative_e_distances)
|
||||
return "The Wipe Tower is currently only supported with the relative extruder addressing (use_relative_e_distances=1).";
|
||||
return L("The Wipe Tower is currently only supported with the relative extruder addressing (use_relative_e_distances=1).");
|
||||
SlicingParameters slicing_params0 = this->objects.front()->slicing_parameters();
|
||||
|
||||
const PrintObject* tallest_object = this->objects.front(); // let's find the tallest object
|
||||
|
@ -615,13 +615,13 @@ std::string Print::validate() const
|
|||
SlicingParameters slicing_params = object->slicing_parameters();
|
||||
if (std::abs(slicing_params.first_print_layer_height - slicing_params0.first_print_layer_height) > EPSILON ||
|
||||
std::abs(slicing_params.layer_height - slicing_params0.layer_height ) > EPSILON)
|
||||
return "The Wipe Tower is only supported for multiple objects if they have equal layer heigths";
|
||||
return L("The Wipe Tower is only supported for multiple objects if they have equal layer heigths");
|
||||
if (slicing_params.raft_layers() != slicing_params0.raft_layers())
|
||||
return "The Wipe Tower is only supported for multiple objects if they are printed over an equal number of raft layers";
|
||||
return L("The Wipe Tower is only supported for multiple objects if they are printed over an equal number of raft layers");
|
||||
if (object->config.support_material_contact_distance != this->objects.front()->config.support_material_contact_distance)
|
||||
return "The Wipe Tower is only supported for multiple objects if they are printed with the same support_material_contact_distance";
|
||||
return L("The Wipe Tower is only supported for multiple objects if they are printed with the same support_material_contact_distance");
|
||||
if (! equal_layering(slicing_params, slicing_params0))
|
||||
return "The Wipe Tower is only supported for multiple objects if they are sliced equally.";
|
||||
return L("The Wipe Tower is only supported for multiple objects if they are sliced equally.");
|
||||
bool was_layer_height_profile_valid = object->layer_height_profile_valid;
|
||||
object->update_layer_height_profile();
|
||||
object->layer_height_profile_valid = was_layer_height_profile_valid;
|
||||
|
@ -645,13 +645,8 @@ std::string Print::validate() const
|
|||
failed = true;
|
||||
|
||||
if (failed)
|
||||
return "The Wipe tower is only supported if all objects have the same layer height profile";
|
||||
return L("The Wipe tower is only supported if all objects have the same layer height profile");
|
||||
}
|
||||
|
||||
/*for (size_t i = 5; i < object->layer_height_profile.size(); i += 2)
|
||||
if (object->layer_height_profile[i-1] > slicing_params.object_print_z_min + EPSILON &&
|
||||
std::abs(object->layer_height_profile[i] - object->config.layer_height) > EPSILON)
|
||||
return "The Wipe Tower is currently only supported with constant Z layer spacing. Layer editing is not allowed.";*/
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -659,7 +654,7 @@ std::string Print::validate() const
|
|||
// find the smallest nozzle diameter
|
||||
std::vector<unsigned int> extruders = this->extruders();
|
||||
if (extruders.empty())
|
||||
return "The supplied settings will cause an empty print.";
|
||||
return L("The supplied settings will cause an empty print.");
|
||||
|
||||
std::vector<double> nozzle_diameters;
|
||||
for (unsigned int extruder_id : extruders)
|
||||
|
@ -669,7 +664,7 @@ std::string Print::validate() const
|
|||
unsigned int total_extruders_count = this->config.nozzle_diameter.size();
|
||||
for (const auto& extruder_idx : extruders)
|
||||
if ( extruder_idx >= total_extruders_count )
|
||||
return "One or more object were assigned an extruder that the printer does not have.";
|
||||
return L("One or more object were assigned an extruder that the printer does not have.");
|
||||
|
||||
for (PrintObject *object : this->objects) {
|
||||
if ((object->config.support_material_extruder == -1 || object->config.support_material_interface_extruder == -1) &&
|
||||
|
@ -678,13 +673,13 @@ std::string Print::validate() const
|
|||
// will be printed with the current tool without a forced tool change. Play safe, assert that all object nozzles
|
||||
// are of the same diameter.
|
||||
if (nozzle_diameters.size() > 1)
|
||||
return "Printing with multiple extruders of differing nozzle diameters. "
|
||||
return L("Printing with multiple extruders of differing nozzle diameters. "
|
||||
"If support is to be printed with the current extruder (support_material_extruder == 0 or support_material_interface_extruder == 0), "
|
||||
"all nozzles have to be of the same diameter.";
|
||||
"all nozzles have to be of the same diameter.");
|
||||
}
|
||||
|
||||
// validate first_layer_height
|
||||
double first_layer_height = object->config.get_abs_value("first_layer_height");
|
||||
double first_layer_height = object->config.get_abs_value(L("first_layer_height"));
|
||||
double first_layer_min_nozzle_diameter;
|
||||
if (object->config.raft_layers > 0) {
|
||||
// if we have raft layers, only support material extruder is used on first layer
|
||||
|
@ -699,11 +694,11 @@ std::string Print::validate() const
|
|||
first_layer_min_nozzle_diameter = min_nozzle_diameter;
|
||||
}
|
||||
if (first_layer_height > first_layer_min_nozzle_diameter)
|
||||
return "First layer height can't be greater than nozzle diameter";
|
||||
return L("First layer height can't be greater than nozzle diameter");
|
||||
|
||||
// validate layer_height
|
||||
if (object->config.layer_height.value > min_nozzle_diameter)
|
||||
return "Layer height can't be greater than nozzle diameter";
|
||||
return L("Layer height can't be greater than nozzle diameter");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1183,7 +1178,7 @@ std::string Print::output_filename()
|
|||
try {
|
||||
return this->placeholder_parser.process(this->config.output_filename_format.value, 0);
|
||||
} catch (std::runtime_error &err) {
|
||||
throw std::runtime_error(std::string("Failed processing of the output_filename_format template.\n") + err.what());
|
||||
throw std::runtime_error(L("Failed processing of the output_filename_format template.") + "\n" + err.what());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -236,7 +236,8 @@ public:
|
|||
PrintRegionPtrs regions;
|
||||
PlaceholderParser placeholder_parser;
|
||||
// TODO: status_cb
|
||||
std::string estimated_print_time;
|
||||
std::string estimated_normal_print_time;
|
||||
std::string estimated_silent_print_time;
|
||||
double total_used_filament, total_extruded_volume, total_cost, total_weight;
|
||||
std::map<size_t, float> filament_stats;
|
||||
PrintState<PrintStep, psCount> state;
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
#include "PrintConfig.hpp"
|
||||
#include "I18N.hpp"
|
||||
|
||||
#include <set>
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
#include <boost/algorithm/string/case_conv.hpp>
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/thread.hpp>
|
||||
|
||||
|
@ -11,7 +14,7 @@ namespace Slic3r {
|
|||
|
||||
//! macro used to mark string used at localization,
|
||||
//! return same string
|
||||
#define L(s) s
|
||||
#define L(s) Slic3r::I18N::translate(s)
|
||||
|
||||
PrintConfigDef::PrintConfigDef()
|
||||
{
|
||||
|
@ -151,6 +154,11 @@ PrintConfigDef::PrintConfigDef()
|
|||
"with the active printer profile.");
|
||||
def->default_value = new ConfigOptionString();
|
||||
|
||||
// The following value is to be stored into the project file (AMF, 3MF, Config ...)
|
||||
// and it contains a sum of "compatible_printers_condition" values over the print and filament profiles.
|
||||
def = this->add("compatible_printers_condition_cummulative", coStrings);
|
||||
def->default_value = new ConfigOptionStrings();
|
||||
|
||||
def = this->add("complete_objects", coBool);
|
||||
def->label = L("Complete individual objects");
|
||||
def->tooltip = L("When printing multiple objects or copies, this feature will complete "
|
||||
|
@ -283,11 +291,11 @@ PrintConfigDef::PrintConfigDef()
|
|||
def->enum_values.push_back("hilbertcurve");
|
||||
def->enum_values.push_back("archimedeanchords");
|
||||
def->enum_values.push_back("octagramspiral");
|
||||
def->enum_labels.push_back("Rectilinear");
|
||||
def->enum_labels.push_back("Concentric");
|
||||
def->enum_labels.push_back("Hilbert Curve");
|
||||
def->enum_labels.push_back("Archimedean Chords");
|
||||
def->enum_labels.push_back("Octagram Spiral");
|
||||
def->enum_labels.push_back(L("Rectilinear"));
|
||||
def->enum_labels.push_back(L("Concentric"));
|
||||
def->enum_labels.push_back(L("Hilbert Curve"));
|
||||
def->enum_labels.push_back(L("Archimedean Chords"));
|
||||
def->enum_labels.push_back(L("Octagram Spiral"));
|
||||
// solid_fill_pattern is an obsolete equivalent to external_fill_pattern.
|
||||
def->aliases.push_back("solid_fill_pattern");
|
||||
def->default_value = new ConfigOptionEnum<InfillPattern>(ipRectilinear);
|
||||
|
@ -643,19 +651,19 @@ PrintConfigDef::PrintConfigDef()
|
|||
def->enum_values.push_back("hilbertcurve");
|
||||
def->enum_values.push_back("archimedeanchords");
|
||||
def->enum_values.push_back("octagramspiral");
|
||||
def->enum_labels.push_back("Rectilinear");
|
||||
def->enum_labels.push_back("Grid");
|
||||
def->enum_labels.push_back("Triangles");
|
||||
def->enum_labels.push_back("Stars");
|
||||
def->enum_labels.push_back("Cubic");
|
||||
def->enum_labels.push_back("Line");
|
||||
def->enum_labels.push_back("Concentric");
|
||||
def->enum_labels.push_back("Honeycomb");
|
||||
def->enum_labels.push_back("3D Honeycomb");
|
||||
def->enum_labels.push_back("Gyroid");
|
||||
def->enum_labels.push_back("Hilbert Curve");
|
||||
def->enum_labels.push_back("Archimedean Chords");
|
||||
def->enum_labels.push_back("Octagram Spiral");
|
||||
def->enum_labels.push_back(L("Rectilinear"));
|
||||
def->enum_labels.push_back(L("Grid"));
|
||||
def->enum_labels.push_back(L("Triangles"));
|
||||
def->enum_labels.push_back(L("Stars"));
|
||||
def->enum_labels.push_back(L("Cubic"));
|
||||
def->enum_labels.push_back(L("Line"));
|
||||
def->enum_labels.push_back(L("Concentric"));
|
||||
def->enum_labels.push_back(L("Honeycomb"));
|
||||
def->enum_labels.push_back(L("3D Honeycomb"));
|
||||
def->enum_labels.push_back(L("Gyroid"));
|
||||
def->enum_labels.push_back(L("Hilbert Curve"));
|
||||
def->enum_labels.push_back(L("Archimedean Chords"));
|
||||
def->enum_labels.push_back(L("Octagram Spiral"));
|
||||
def->default_value = new ConfigOptionEnum<InfillPattern>(ipStars);
|
||||
|
||||
def = this->add("first_layer_acceleration", coFloat);
|
||||
|
@ -763,8 +771,8 @@ PrintConfigDef::PrintConfigDef()
|
|||
def->enum_labels.push_back("Mach3/LinuxCNC");
|
||||
def->enum_labels.push_back("Machinekit");
|
||||
def->enum_labels.push_back("Smoothie");
|
||||
def->enum_labels.push_back("No extrusion");
|
||||
def->default_value = new ConfigOptionEnum<GCodeFlavor>(gcfMarlin);
|
||||
def->enum_labels.push_back(L("No extrusion"));
|
||||
def->default_value = new ConfigOptionEnum<GCodeFlavor>(gcfRepRap);
|
||||
|
||||
def = this->add("infill_acceleration", coFloat);
|
||||
def->label = L("Infill");
|
||||
|
@ -847,7 +855,12 @@ PrintConfigDef::PrintConfigDef()
|
|||
def->tooltip = L("Name of the profile, from which this profile inherits.");
|
||||
def->full_width = true;
|
||||
def->height = 50;
|
||||
def->default_value = new ConfigOptionString("");
|
||||
def->default_value = new ConfigOptionString();
|
||||
|
||||
// The following value is to be stored into the project file (AMF, 3MF, Config ...)
|
||||
// and it contains a sum of "inherits" values over the print and filament profiles.
|
||||
def = this->add("inherits_cummulative", coStrings);
|
||||
def->default_value = new ConfigOptionStrings();
|
||||
|
||||
def = this->add("interface_shells", coBool);
|
||||
def->label = L("Interface shells");
|
||||
|
@ -879,6 +892,98 @@ PrintConfigDef::PrintConfigDef()
|
|||
def->min = 0;
|
||||
def->default_value = new ConfigOptionFloat(0.3);
|
||||
|
||||
def = this->add("silent_mode", coBool);
|
||||
def->label = L("Support silent mode");
|
||||
def->tooltip = L("Set silent mode for the G-code flavor");
|
||||
def->default_value = new ConfigOptionBool(true);
|
||||
|
||||
const int machine_limits_opt_width = 70;
|
||||
{
|
||||
struct AxisDefault {
|
||||
std::string name;
|
||||
std::vector<double> max_feedrate;
|
||||
std::vector<double> max_acceleration;
|
||||
std::vector<double> max_jerk;
|
||||
};
|
||||
std::vector<AxisDefault> axes {
|
||||
// name, max_feedrate, max_acceleration, max_jerk
|
||||
{ "x", { 500., 200. }, { 9000., 1000. }, { 10. , 10. } },
|
||||
{ "y", { 500., 200. }, { 9000., 1000. }, { 10. , 10. } },
|
||||
{ "z", { 12., 12. }, { 500., 200. }, { 0.2, 0.4 } },
|
||||
{ "e", { 120., 120. }, { 10000., 5000. }, { 2.5, 2.5 } }
|
||||
};
|
||||
for (const AxisDefault &axis : axes) {
|
||||
std::string axis_upper = boost::to_upper_copy<std::string>(axis.name);
|
||||
// Add the machine feedrate limits for XYZE axes. (M203)
|
||||
def = this->add("machine_max_feedrate_" + axis.name, coFloats);
|
||||
def->full_label = (boost::format(L("Maximum feedrate %1%")) % axis_upper).str();
|
||||
def->category = L("Machine limits");
|
||||
def->tooltip = (boost::format(L("Maximum feedrate of the %1% axis")) % axis_upper).str();
|
||||
def->sidetext = L("mm/s");
|
||||
def->min = 0;
|
||||
def->width = machine_limits_opt_width;
|
||||
def->default_value = new ConfigOptionFloats(axis.max_feedrate);
|
||||
// Add the machine acceleration limits for XYZE axes (M201)
|
||||
def = this->add("machine_max_acceleration_" + axis.name, coFloats);
|
||||
def->full_label = (boost::format(L("Maximum acceleration %1%")) % axis_upper).str();
|
||||
def->category = L("Machine limits");
|
||||
def->tooltip = (boost::format(L("Maximum acceleration of the %1% axis")) % axis_upper).str();
|
||||
def->sidetext = L("mm/s²");
|
||||
def->min = 0;
|
||||
def->width = machine_limits_opt_width;
|
||||
def->default_value = new ConfigOptionFloats(axis.max_acceleration);
|
||||
// Add the machine jerk limits for XYZE axes (M205)
|
||||
def = this->add("machine_max_jerk_" + axis.name, coFloats);
|
||||
def->full_label = (boost::format(L("Maximum jerk %1%")) % axis_upper).str();
|
||||
def->category = L("Machine limits");
|
||||
def->tooltip = (boost::format(L("Maximum jerk of the %1% axis")) % axis_upper).str();
|
||||
def->sidetext = L("mm/s");
|
||||
def->min = 0;
|
||||
def->width = machine_limits_opt_width;
|
||||
def->default_value = new ConfigOptionFloats(axis.max_jerk);
|
||||
}
|
||||
}
|
||||
|
||||
// M205 S... [mm/sec]
|
||||
def = this->add("machine_min_extruding_rate", coFloats);
|
||||
def->full_label = L("Minimum feedrate when extruding");
|
||||
def->category = L("Machine limits");
|
||||
def->tooltip = L("Minimum feedrate when extruding") + " (M205 S)";
|
||||
def->sidetext = L("mm/s");
|
||||
def->min = 0;
|
||||
def->width = machine_limits_opt_width;
|
||||
def->default_value = new ConfigOptionFloats{ 0., 0. };
|
||||
|
||||
// M205 T... [mm/sec]
|
||||
def = this->add("machine_min_travel_rate", coFloats);
|
||||
def->full_label = L("Minimum travel feedrate");
|
||||
def->category = L("Machine limits");
|
||||
def->tooltip = L("Minimum travel feedrate") + " (M205 T)";
|
||||
def->sidetext = L("mm/s");
|
||||
def->min = 0;
|
||||
def->width = machine_limits_opt_width;
|
||||
def->default_value = new ConfigOptionFloats{ 0., 0. };
|
||||
|
||||
// M204 S... [mm/sec^2]
|
||||
def = this->add("machine_max_acceleration_extruding", coFloats);
|
||||
def->full_label = L("Maximum acceleration when extruding");
|
||||
def->category = L("Machine limits");
|
||||
def->tooltip = L("Maximum acceleration when extruding") + " (M204 S)";
|
||||
def->sidetext = L("mm/s²");
|
||||
def->min = 0;
|
||||
def->width = machine_limits_opt_width;
|
||||
def->default_value = new ConfigOptionFloats{ 1500., 1250. };
|
||||
|
||||
// M204 T... [mm/sec^2]
|
||||
def = this->add("machine_max_acceleration_retracting", coFloats);
|
||||
def->full_label = L("Maximum acceleration when retracting");
|
||||
def->category = L("Machine limits");
|
||||
def->tooltip = L("Maximum acceleration when retracting") + " (M204 T)";
|
||||
def->sidetext = L("mm/s²");
|
||||
def->min = 0;
|
||||
def->width = machine_limits_opt_width;
|
||||
def->default_value = new ConfigOptionFloats{ 1500., 1250. };
|
||||
|
||||
def = this->add("max_fan_speed", coInts);
|
||||
def->label = L("Max");
|
||||
def->tooltip = L("This setting represents the maximum speed of your fan.");
|
||||
|
@ -1300,10 +1405,10 @@ PrintConfigDef::PrintConfigDef()
|
|||
def->enum_values.push_back("nearest");
|
||||
def->enum_values.push_back("aligned");
|
||||
def->enum_values.push_back("rear");
|
||||
def->enum_labels.push_back("Random");
|
||||
def->enum_labels.push_back("Nearest");
|
||||
def->enum_labels.push_back("Aligned");
|
||||
def->enum_labels.push_back("Rear");
|
||||
def->enum_labels.push_back(L("Random"));
|
||||
def->enum_labels.push_back(L("Nearest"));
|
||||
def->enum_labels.push_back(L("Aligned"));
|
||||
def->enum_labels.push_back(L("Rear"));
|
||||
def->default_value = new ConfigOptionEnum<SeamPosition>(spAligned);
|
||||
|
||||
#if 0
|
||||
|
@ -1516,7 +1621,7 @@ PrintConfigDef::PrintConfigDef()
|
|||
def->label = L("Single Extruder Multi Material");
|
||||
def->tooltip = L("The printer multiplexes filaments into a single hot end.");
|
||||
def->cli = "single-extruder-multi-material!";
|
||||
def->default_value = new ConfigOptionBool(false);
|
||||
def->default_value = new ConfigOptionBool(false);
|
||||
|
||||
def = this->add("support_material", coBool);
|
||||
def->label = L("Generate support material");
|
||||
|
@ -1566,8 +1671,8 @@ PrintConfigDef::PrintConfigDef()
|
|||
def->min = 0;
|
||||
def->enum_values.push_back("0");
|
||||
def->enum_values.push_back("0.2");
|
||||
def->enum_labels.push_back("0 (soluble)");
|
||||
def->enum_labels.push_back("0.2 (detachable)");
|
||||
def->enum_labels.push_back((boost::format("0 (%1%)") % L("soluble")).str());
|
||||
def->enum_labels.push_back((boost::format("0.2 (%1%)") % L("detachable")).str());
|
||||
def->default_value = new ConfigOptionFloat(0.2);
|
||||
|
||||
def = this->add("support_material_enforce_layers", coInt);
|
||||
|
@ -1656,9 +1761,9 @@ PrintConfigDef::PrintConfigDef()
|
|||
def->enum_values.push_back("rectilinear");
|
||||
def->enum_values.push_back("rectilinear-grid");
|
||||
def->enum_values.push_back("honeycomb");
|
||||
def->enum_labels.push_back("rectilinear");
|
||||
def->enum_labels.push_back("rectilinear grid");
|
||||
def->enum_labels.push_back("honeycomb");
|
||||
def->enum_labels.push_back(L("Rectilinear"));
|
||||
def->enum_labels.push_back(L("Rectilinear grid"));
|
||||
def->enum_labels.push_back(L("Honeycomb"));
|
||||
def->default_value = new ConfigOptionEnum<SupportMaterialPattern>(smpRectilinear);
|
||||
|
||||
def = this->add("support_material_spacing", coFloat);
|
||||
|
@ -2251,6 +2356,7 @@ std::string FullPrintConfig::validate()
|
|||
// Declare the static caches for each StaticPrintConfig derived class.
|
||||
StaticPrintConfig::StaticCache<class Slic3r::PrintObjectConfig> PrintObjectConfig::s_cache_PrintObjectConfig;
|
||||
StaticPrintConfig::StaticCache<class Slic3r::PrintRegionConfig> PrintRegionConfig::s_cache_PrintRegionConfig;
|
||||
StaticPrintConfig::StaticCache<class Slic3r::MachineEnvelopeConfig> MachineEnvelopeConfig::s_cache_MachineEnvelopeConfig;
|
||||
StaticPrintConfig::StaticCache<class Slic3r::GCodeConfig> GCodeConfig::s_cache_GCodeConfig;
|
||||
StaticPrintConfig::StaticCache<class Slic3r::PrintConfig> PrintConfig::s_cache_PrintConfig;
|
||||
StaticPrintConfig::StaticCache<class Slic3r::HostConfig> HostConfig::s_cache_HostConfig;
|
||||
|
|
|
@ -459,6 +459,56 @@ protected:
|
|||
}
|
||||
};
|
||||
|
||||
class MachineEnvelopeConfig : public StaticPrintConfig
|
||||
{
|
||||
STATIC_PRINT_CONFIG_CACHE(MachineEnvelopeConfig)
|
||||
public:
|
||||
// M201 X... Y... Z... E... [mm/sec^2]
|
||||
ConfigOptionFloats machine_max_acceleration_x;
|
||||
ConfigOptionFloats machine_max_acceleration_y;
|
||||
ConfigOptionFloats machine_max_acceleration_z;
|
||||
ConfigOptionFloats machine_max_acceleration_e;
|
||||
// M203 X... Y... Z... E... [mm/sec]
|
||||
ConfigOptionFloats machine_max_feedrate_x;
|
||||
ConfigOptionFloats machine_max_feedrate_y;
|
||||
ConfigOptionFloats machine_max_feedrate_z;
|
||||
ConfigOptionFloats machine_max_feedrate_e;
|
||||
// M204 S... [mm/sec^2]
|
||||
ConfigOptionFloats machine_max_acceleration_extruding;
|
||||
// M204 T... [mm/sec^2]
|
||||
ConfigOptionFloats machine_max_acceleration_retracting;
|
||||
// M205 X... Y... Z... E... [mm/sec]
|
||||
ConfigOptionFloats machine_max_jerk_x;
|
||||
ConfigOptionFloats machine_max_jerk_y;
|
||||
ConfigOptionFloats machine_max_jerk_z;
|
||||
ConfigOptionFloats machine_max_jerk_e;
|
||||
// M205 T... [mm/sec]
|
||||
ConfigOptionFloats machine_min_travel_rate;
|
||||
// M205 S... [mm/sec]
|
||||
ConfigOptionFloats machine_min_extruding_rate;
|
||||
|
||||
protected:
|
||||
void initialize(StaticCacheBase &cache, const char *base_ptr)
|
||||
{
|
||||
OPT_PTR(machine_max_acceleration_x);
|
||||
OPT_PTR(machine_max_acceleration_y);
|
||||
OPT_PTR(machine_max_acceleration_z);
|
||||
OPT_PTR(machine_max_acceleration_e);
|
||||
OPT_PTR(machine_max_feedrate_x);
|
||||
OPT_PTR(machine_max_feedrate_y);
|
||||
OPT_PTR(machine_max_feedrate_z);
|
||||
OPT_PTR(machine_max_feedrate_e);
|
||||
OPT_PTR(machine_max_acceleration_extruding);
|
||||
OPT_PTR(machine_max_acceleration_retracting);
|
||||
OPT_PTR(machine_max_jerk_x);
|
||||
OPT_PTR(machine_max_jerk_y);
|
||||
OPT_PTR(machine_max_jerk_z);
|
||||
OPT_PTR(machine_max_jerk_e);
|
||||
OPT_PTR(machine_min_travel_rate);
|
||||
OPT_PTR(machine_min_extruding_rate);
|
||||
}
|
||||
};
|
||||
|
||||
// This object is mapped to Perl as Slic3r::Config::GCode.
|
||||
class GCodeConfig : public StaticPrintConfig
|
||||
{
|
||||
|
@ -512,9 +562,9 @@ public:
|
|||
ConfigOptionFloat cooling_tube_retraction;
|
||||
ConfigOptionFloat cooling_tube_length;
|
||||
ConfigOptionFloat parking_pos_retraction;
|
||||
ConfigOptionBool silent_mode;
|
||||
ConfigOptionFloat extra_loading_move;
|
||||
|
||||
|
||||
std::string get_extrusion_axis() const
|
||||
{
|
||||
return
|
||||
|
@ -573,12 +623,13 @@ protected:
|
|||
OPT_PTR(cooling_tube_retraction);
|
||||
OPT_PTR(cooling_tube_length);
|
||||
OPT_PTR(parking_pos_retraction);
|
||||
OPT_PTR(silent_mode);
|
||||
OPT_PTR(extra_loading_move);
|
||||
}
|
||||
};
|
||||
|
||||
// This object is mapped to Perl as Slic3r::Config::Print.
|
||||
class PrintConfig : public GCodeConfig
|
||||
class PrintConfig : public MachineEnvelopeConfig, public GCodeConfig
|
||||
{
|
||||
STATIC_PRINT_CONFIG_CACHE_DERIVED(PrintConfig)
|
||||
PrintConfig() : GCodeConfig(0) { initialize_cache(); *this = s_cache_PrintConfig.defaults(); }
|
||||
|
@ -626,6 +677,7 @@ public:
|
|||
ConfigOptionString output_filename_format;
|
||||
ConfigOptionFloat perimeter_acceleration;
|
||||
ConfigOptionStrings post_process;
|
||||
ConfigOptionString printer_model;
|
||||
ConfigOptionString printer_notes;
|
||||
ConfigOptionFloat resolution;
|
||||
ConfigOptionFloats retract_before_travel;
|
||||
|
@ -654,6 +706,7 @@ protected:
|
|||
PrintConfig(int) : GCodeConfig(1) {}
|
||||
void initialize(StaticCacheBase &cache, const char *base_ptr)
|
||||
{
|
||||
this->MachineEnvelopeConfig::initialize(cache, base_ptr);
|
||||
this->GCodeConfig::initialize(cache, base_ptr);
|
||||
OPT_PTR(avoid_crossing_perimeters);
|
||||
OPT_PTR(bed_shape);
|
||||
|
@ -695,6 +748,7 @@ protected:
|
|||
OPT_PTR(output_filename_format);
|
||||
OPT_PTR(perimeter_acceleration);
|
||||
OPT_PTR(post_process);
|
||||
OPT_PTR(printer_model);
|
||||
OPT_PTR(printer_notes);
|
||||
OPT_PTR(resolution);
|
||||
OPT_PTR(retract_before_travel);
|
||||
|
|
|
@ -96,7 +96,8 @@ public:
|
|||
void call(int i, int j) const;
|
||||
void call(const std::vector<int>& ints) const;
|
||||
void call(double d) const;
|
||||
void call(double x, double y) const;
|
||||
void call(double a, double b) const;
|
||||
void call(double a, double b, double c, double d) const;
|
||||
void call(bool b) const;
|
||||
private:
|
||||
void *m_callback;
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
#include <boost/thread.hpp>
|
||||
|
||||
#define SLIC3R_FORK_NAME "Slic3r Prusa Edition"
|
||||
#define SLIC3R_VERSION "1.40.0"
|
||||
#define SLIC3R_VERSION "1.41.0-alpha"
|
||||
#define SLIC3R_BUILD "UNKNOWN"
|
||||
|
||||
typedef int32_t coord_t;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "Utils.hpp"
|
||||
#include "I18N.hpp"
|
||||
|
||||
#include <locale>
|
||||
#include <ctime>
|
||||
|
@ -123,6 +124,9 @@ const std::string& localization_dir()
|
|||
return g_local_dir;
|
||||
}
|
||||
|
||||
// Translate function callback, to call wxWidgets translate function to convert non-localized UTF8 string to a localized one.
|
||||
Slic3r::I18N::translate_fn_type Slic3r::I18N::translate_fn = nullptr;
|
||||
|
||||
static std::string g_data_dir;
|
||||
|
||||
void set_data_dir(const std::string &dir)
|
||||
|
@ -262,7 +266,7 @@ void PerlCallback::call(double d) const
|
|||
LEAVE;
|
||||
}
|
||||
|
||||
void PerlCallback::call(double x, double y) const
|
||||
void PerlCallback::call(double a, double b) const
|
||||
{
|
||||
if (!m_callback)
|
||||
return;
|
||||
|
@ -270,8 +274,26 @@ void PerlCallback::call(double x, double y) const
|
|||
ENTER;
|
||||
SAVETMPS;
|
||||
PUSHMARK(SP);
|
||||
XPUSHs(sv_2mortal(newSVnv(x)));
|
||||
XPUSHs(sv_2mortal(newSVnv(y)));
|
||||
XPUSHs(sv_2mortal(newSVnv(a)));
|
||||
XPUSHs(sv_2mortal(newSVnv(b)));
|
||||
PUTBACK;
|
||||
perl_call_sv(SvRV((SV*)m_callback), G_DISCARD);
|
||||
FREETMPS;
|
||||
LEAVE;
|
||||
}
|
||||
|
||||
void PerlCallback::call(double a, double b, double c, double d) const
|
||||
{
|
||||
if (!m_callback)
|
||||
return;
|
||||
dSP;
|
||||
ENTER;
|
||||
SAVETMPS;
|
||||
PUSHMARK(SP);
|
||||
XPUSHs(sv_2mortal(newSVnv(a)));
|
||||
XPUSHs(sv_2mortal(newSVnv(b)));
|
||||
XPUSHs(sv_2mortal(newSVnv(c)));
|
||||
XPUSHs(sv_2mortal(newSVnv(d)));
|
||||
PUTBACK;
|
||||
perl_call_sv(SvRV((SV*)m_callback), G_DISCARD);
|
||||
FREETMPS;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue