Merge branch 'master' into fs_undoredo

This commit is contained in:
Filip Sykala - NTB T15p 2023-03-28 17:19:50 +02:00
commit cc9a3a473e
41 changed files with 744 additions and 388 deletions

View File

@ -37,6 +37,7 @@ std::pair<double, double> Extruder::extrude(double dE)
value supplied will overwrite the previous one if any. */
std::pair<double, double> Extruder::retract(double retract_length, double restart_extra)
{
assert(restart_extra >= 0);
// in case of relative E distances we always reset to 0 before any output
if (m_config->use_relative_e_distances)
m_E = 0.;
@ -64,6 +65,24 @@ std::pair<double, double> Extruder::unretract()
return std::make_pair(dE, emitE);
}
// Setting the retract state from the script.
// Sets current retraction value & restart extra filament amount if retracted > 0.
void Extruder::set_retracted(double retracted, double restart_extra)
{
if (retracted < - EPSILON)
throw Slic3r::RuntimeError("Custom G-code reports negative z_retracted.");
if (restart_extra < - EPSILON)
throw Slic3r::RuntimeError("Custom G-code reports negative z_restart_extra.");
if (retracted > EPSILON) {
m_retracted = retracted;
m_restart_extra = restart_extra < EPSILON ? 0 : restart_extra;
} else {
m_retracted = 0;
m_restart_extra = 0;
}
}
// Used filament volume in mm^3.
double Extruder::extruded_volume() const
{

View File

@ -36,6 +36,19 @@ public:
double extruded_volume() const;
// Used filament length in mm.
double used_filament() const;
// Getters for the PlaceholderParser.
// Get current extruder position. Only applicable with absolute extruder addressing.
double position() const { return m_E; }
// Get current retraction value. Only non-negative values.
double retracted() const { return m_retracted; }
// Get extra retraction planned after
double restart_extra() const { return m_restart_extra; }
// Setters for the PlaceholderParser.
// Set current extruder position. Only applicable with absolute extruder addressing.
void set_position(double e) { m_E = e; }
// Sets current retraction value & restart extra filament amount if retracted > 0.
void set_retracted(double retracted, double restart_extra);
double filament_diameter() const;
double filament_crossection() const { return this->filament_diameter() * this->filament_diameter() * 0.25 * PI; }

View File

@ -1,4 +1,5 @@
#include "ExtrusionRole.hpp"
#include "I18N.hpp"
#include <string>
#include <string_view>

View File

@ -1,6 +1,7 @@
#include "SLAArchiveReader.hpp"
#include "SL1.hpp"
#include "SL1_SVG.hpp"
#include "I18N.hpp"
#include "libslic3r/SlicesToTriangleMesh.hpp"
@ -36,8 +37,8 @@ static const std::map<std::string, ArchiveEntry> REGISTERED_ARCHIVES {
[] (const std::string &fname, SLAImportQuality quality, const ProgrFn &progr) { return std::make_unique<SL1Reader>(fname, quality, progr); } }
},
{
"SL2",
{ L("SL2 archive files"), {"sl2", "sl1_svg"/*, "zip"*/}, // also a zip but unnecessary hassle to implement single extension for multiple archives
"SL1SVG",
{ L("SL1SVG archive files"), {"sl1_svg"/*, "zip"*/}, // also a zip but unnecessary hassle to implement single extension for multiple archives
[] (const std::string &fname, SLAImportQuality quality, const ProgrFn &progr) { return std::make_unique<SL1_SVGReader>(fname, quality, progr); }}
},
// TODO: pwmx and future others.

View File

@ -25,9 +25,13 @@ static const std::map<std::string, ArchiveEntry> REGISTERED_ARCHIVES {
"SL1",
{ "sl1", [] (const auto &cfg) { return std::make_unique<SL1Archive>(cfg); } }
},
{
"SL1SVG",
{ "sl1_svg", [] (const auto &cfg) { return std::make_unique<SL1_SVGArchive>(cfg); } }
},
{
"SL2",
{ "sl2", [] (const auto &cfg) { return std::make_unique<SL1_SVGArchive>(cfg); } }
{ "sl1_svg", [] (const auto &cfg) { return std::make_unique<SL1_SVGArchive>(cfg); } }
},
{
"pwmx",

View File

@ -419,7 +419,7 @@ namespace Slic3r {
std::string WipeTowerIntegration::finalize(GCode& gcodegen)
{
std::string gcode;
if (std::abs(gcodegen.writer().get_position()(2) - m_final_purge.print_z) > EPSILON)
if (std::abs(gcodegen.writer().get_position().z() - m_final_purge.print_z) > EPSILON)
gcode += gcodegen.change_layer(m_final_purge.print_z);
gcode += append_tcr(gcodegen, m_final_purge, -1);
return gcode;
@ -429,6 +429,90 @@ namespace Slic3r {
#define EXTRUDER_CONFIG(OPT) m_config.OPT.get_at(m_writer.extruder()->id())
void GCode::PlaceholderParserIntegration::reset()
{
this->failed_templates.clear();
this->output_config.clear();
this->opt_position = nullptr;
this->opt_zhop = nullptr;
this->opt_e_position = nullptr;
this->opt_e_retracted = nullptr;
this->opt_e_restart_extra = nullptr;
this->num_extruders = 0;
this->position.clear();
this->e_position.clear();
this->e_retracted.clear();
this->e_restart_extra.clear();
}
void GCode::PlaceholderParserIntegration::init(const GCodeWriter &writer)
{
this->reset();
const std::vector<Extruder> &extruders = writer.extruders();
if (! extruders.empty()) {
this->num_extruders = extruders.back().id() + 1;
this->e_retracted.assign(num_extruders, 0);
this->e_restart_extra.assign(num_extruders, 0);
this->opt_e_retracted = new ConfigOptionFloats(e_retracted);
this->opt_e_restart_extra = new ConfigOptionFloats(e_restart_extra);
this->output_config.set_key_value("e_retracted", this->opt_e_retracted);
this->output_config.set_key_value("e_restart_extra", this->opt_e_restart_extra);
if (! writer.config.use_relative_e_distances) {
e_position.assign(num_extruders, 0);
opt_e_position = new ConfigOptionFloats(e_position);
this->output_config.set_key_value("e_position", opt_e_position);
}
}
// Reserve buffer for current position.
this->position.assign(3, 0);
this->opt_position = new ConfigOptionFloats(this->position);
// Store zhop variable into the parser itself, it is a read-only variable to the script.
this->opt_zhop = new ConfigOptionFloat(writer.get_zhop());
this->parser.set("zhop", this->opt_zhop);
}
void GCode::PlaceholderParserIntegration::update_from_gcodewriter(const GCodeWriter &writer)
{
memcpy(this->position.data(), writer.get_position().data(), sizeof(double) * 3);
this->opt_position->values = this->position;
this->opt_zhop->value = writer.get_zhop();
if (this->num_extruders > 0) {
const std::vector<Extruder> &extruders = writer.extruders();
assert(! extruders.empty() && num_extruders == extruders.back().id() + 1);
this->e_retracted.assign(num_extruders, 0);
this->e_restart_extra.assign(num_extruders, 0);
for (const Extruder &e : extruders) {
this->e_retracted[e.id()] = e.retracted();
this->e_restart_extra[e.id()] = e.restart_extra();
}
opt_e_retracted->values = this->e_retracted;
opt_e_restart_extra->values = this->e_restart_extra;
if (! writer.config.use_relative_e_distances) {
this->e_position.assign(num_extruders, 0);
for (const Extruder &e : extruders)
this->e_position[e.id()] = e.position();
this->opt_e_position->values = this->e_position;
}
}
}
// Throw if any of the output vector variables were resized by the script.
void GCode::PlaceholderParserIntegration::validate_output_vector_variables()
{
if (this->opt_position->values.size() != 3)
throw Slic3r::RuntimeError("\"position\" output variable must not be resized by the script.");
if (this->num_extruders > 0) {
if (this->opt_e_position && this->opt_e_position->values.size() != this->num_extruders)
throw Slic3r::RuntimeError("\"e_position\" output variable must not be resized by the script.");
if (this->opt_e_retracted->values.size() != this->num_extruders)
throw Slic3r::RuntimeError("\"e_retracted\" output variable must not be resized by the script.");
if (this->opt_e_restart_extra->values.size() != this->num_extruders)
throw Slic3r::RuntimeError("\"e_restart_extra\" output variable must not be resized by the script.");
}
}
// Collect pairs of object_layer + support_layer sorted by print_z.
// object_layer & support_layer are considered to be on the same print_z, if they are not further than EPSILON.
GCode::ObjectsLayerToPrint GCode::collect_layers_to_print(const PrintObject& object)
@ -728,7 +812,6 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu
throw Slic3r::RuntimeError(std::string("G-code export to ") + path + " failed.\nCannot open the file for writing.\n");
try {
m_placeholder_parser_failed_templates.clear();
this->_do_export(*print, file, thumbnail_cb);
file.flush();
if (file.is_error()) {
@ -745,11 +828,11 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu
}
file.close();
if (! m_placeholder_parser_failed_templates.empty()) {
if (! m_placeholder_parser_integration.failed_templates.empty()) {
// G-code export proceeded, but some of the PlaceholderParser substitutions failed.
//FIXME localize!
std::string msg = std::string("G-code export to ") + path + " failed due to invalid custom G-code sections:\n\n";
for (const auto &name_and_error : m_placeholder_parser_failed_templates)
for (const auto &name_and_error : m_placeholder_parser_integration.failed_templates)
msg += name_and_error.first + "\n" + name_and_error.second + "\n";
msg += "\nPlease inspect the file ";
msg += path_tmp + " for error messages enclosed between\n";
@ -1078,12 +1161,12 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
file.find_replace_enable();
// Prepare the helper object for replacing placeholders in custom G-code and output filename.
m_placeholder_parser = print.placeholder_parser();
m_placeholder_parser.update_timestamp();
m_placeholder_parser_context.rng = std::mt19937(std::chrono::high_resolution_clock::now().time_since_epoch().count());
m_placeholder_parser_integration.parser = print.placeholder_parser();
m_placeholder_parser_integration.parser.update_timestamp();
m_placeholder_parser_integration.context.rng = std::mt19937(std::chrono::high_resolution_clock::now().time_since_epoch().count());
// Enable passing global variables between PlaceholderParser invocations.
m_placeholder_parser_context.global_config = std::make_unique<DynamicConfig>();
print.update_object_placeholders(m_placeholder_parser.config_writable(), ".gcode");
m_placeholder_parser_integration.context.global_config = std::make_unique<DynamicConfig>();
print.update_object_placeholders(m_placeholder_parser_integration.parser.config_writable(), ".gcode");
// Get optimal tool ordering to minimize tool switches of a multi-exruder print.
// For a print by objects, find the 1st printing object.
@ -1147,23 +1230,25 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
// Emit machine envelope limits for the Marlin firmware.
this->print_machine_envelope(file, print);
// Update output variables after the extruders were initialized.
m_placeholder_parser_integration.init(m_writer);
// Let the start-up script prime the 1st printing tool.
m_placeholder_parser.set("initial_tool", initial_extruder_id);
m_placeholder_parser.set("initial_extruder", initial_extruder_id);
m_placeholder_parser.set("current_extruder", initial_extruder_id);
this->placeholder_parser().set("initial_tool", initial_extruder_id);
this->placeholder_parser().set("initial_extruder", initial_extruder_id);
this->placeholder_parser().set("current_extruder", initial_extruder_id);
//Set variable for total layer count so it can be used in custom gcode.
m_placeholder_parser.set("total_layer_count", m_layer_count);
this->placeholder_parser().set("total_layer_count", m_layer_count);
// Useful for sequential prints.
m_placeholder_parser.set("current_object_idx", 0);
this->placeholder_parser().set("current_object_idx", 0);
// For the start / end G-code to do the priming and final filament pull in case there is no wipe tower provided.
m_placeholder_parser.set("has_wipe_tower", has_wipe_tower);
m_placeholder_parser.set("has_single_extruder_multi_material_priming", has_wipe_tower && print.config().single_extruder_multi_material_priming);
m_placeholder_parser.set("total_toolchanges", std::max(0, print.wipe_tower_data().number_of_toolchanges)); // Check for negative toolchanges (single extruder mode) and set to 0 (no tool change).
this->placeholder_parser().set("has_wipe_tower", has_wipe_tower);
this->placeholder_parser().set("has_single_extruder_multi_material_priming", has_wipe_tower && print.config().single_extruder_multi_material_priming);
this->placeholder_parser().set("total_toolchanges", std::max(0, print.wipe_tower_data().number_of_toolchanges)); // Check for negative toolchanges (single extruder mode) and set to 0 (no tool change).
{
BoundingBoxf bbox(print.config().bed_shape.values);
m_placeholder_parser.set("print_bed_min", new ConfigOptionFloats({ bbox.min.x(), bbox.min.y() }));
m_placeholder_parser.set("print_bed_max", new ConfigOptionFloats({ bbox.max.x(), bbox.max.y() }));
m_placeholder_parser.set("print_bed_size", new ConfigOptionFloats({ bbox.size().x(), bbox.size().y() }));
this->placeholder_parser().set("print_bed_min", new ConfigOptionFloats({ bbox.min.x(), bbox.min.y() }));
this->placeholder_parser().set("print_bed_max", new ConfigOptionFloats({ bbox.max.x(), bbox.max.y() }));
this->placeholder_parser().set("print_bed_size", new ConfigOptionFloats({ bbox.size().x(), bbox.size().y() }));
}
{
// Convex hull of the 1st layer extrusions, for bed leveling and placing the initial purge line.
@ -1176,15 +1261,15 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
for (const Point &pt : print.first_layer_convex_hull().points)
pts->values.emplace_back(unscale(pt));
BoundingBoxf bbox(pts->values);
m_placeholder_parser.set("first_layer_print_convex_hull", pts.release());
m_placeholder_parser.set("first_layer_print_min", new ConfigOptionFloats({ bbox.min.x(), bbox.min.y() }));
m_placeholder_parser.set("first_layer_print_max", new ConfigOptionFloats({ bbox.max.x(), bbox.max.y() }));
m_placeholder_parser.set("first_layer_print_size", new ConfigOptionFloats({ bbox.size().x(), bbox.size().y() }));
this->placeholder_parser().set("first_layer_print_convex_hull", pts.release());
this->placeholder_parser().set("first_layer_print_min", new ConfigOptionFloats({ bbox.min.x(), bbox.min.y() }));
this->placeholder_parser().set("first_layer_print_max", new ConfigOptionFloats({ bbox.max.x(), bbox.max.y() }));
this->placeholder_parser().set("first_layer_print_size", new ConfigOptionFloats({ bbox.size().x(), bbox.size().y() }));
std::vector<unsigned char> is_extruder_used(print.config().nozzle_diameter.size(), 0);
for (unsigned int extruder_id : tool_ordering.all_extruders())
is_extruder_used[extruder_id] = true;
m_placeholder_parser.set("is_extruder_used", new ConfigOptionBools(is_extruder_used));
this->placeholder_parser().set("is_extruder_used", new ConfigOptionBools(is_extruder_used));
}
// Enable ooze prevention if configured so.
@ -1251,7 +1336,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
// Ff we are printing the bottom layer of an object, and we have already finished
// another one, set first layer temperatures. This happens before the Z move
// is triggered, so machine has more time to reach such temperatures.
m_placeholder_parser.set("current_object_idx", int(finished_objects));
this->placeholder_parser().set("current_object_idx", int(finished_objects));
std::string between_objects_gcode = this->placeholder_parser_process("between_objects_gcode", print.config().between_objects_gcode.value, initial_extruder_id);
// Set first layer bed and extruder temperatures, don't wait for it to reach the temperature.
this->_print_first_layer_bed_temperature(file, print, between_objects_gcode, initial_extruder_id, false);
@ -1337,7 +1422,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
{
DynamicConfig config;
config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index));
config.set_key_value("layer_z", new ConfigOptionFloat(m_writer.get_position()(2) - m_config.z_offset.value));
config.set_key_value("layer_z", new ConfigOptionFloat(m_writer.get_position().z() - m_config.z_offset.value));
config.set_key_value("max_layer_z", new ConfigOptionFloat(m_max_layer_z));
if (print.config().single_extruder_multi_material) {
// Process the end_filament_gcode for the active filament only.
@ -1560,17 +1645,46 @@ void GCode::process_layers(
output_stream.find_replace_enable();
}
std::string GCode::placeholder_parser_process(const std::string &name, const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override)
std::string GCode::placeholder_parser_process(
const std::string &name,
const std::string &templ,
unsigned int current_extruder_id,
const DynamicConfig *config_override)
{
PlaceholderParserIntegration &ppi = m_placeholder_parser_integration;
try {
return m_placeholder_parser.process(templ, current_extruder_id, config_override, &m_placeholder_parser_context);
} catch (std::runtime_error &err) {
ppi.update_from_gcodewriter(m_writer);
std::string output = ppi.parser.process(templ, current_extruder_id, config_override, &ppi.output_config, &ppi.context);
ppi.validate_output_vector_variables();
if (const std::vector<double> &pos = ppi.opt_position->values; ppi.position != pos) {
// Update G-code writer.
m_writer.update_position({ pos[0], pos[1], pos[2] });
this->set_last_pos(this->gcode_to_point({ pos[0], pos[1] }));
}
for (const Extruder &e : m_writer.extruders()) {
unsigned int eid = e.id();
assert(eid < ppi.num_extruders);
if ( eid < ppi.num_extruders) {
if (! m_writer.config.use_relative_e_distances && ! is_approx(ppi.e_position[eid], ppi.opt_e_position->values[eid]))
const_cast<Extruder&>(e).set_position(ppi.opt_e_position->values[eid]);
if (! is_approx(ppi.e_retracted[eid], ppi.opt_e_retracted->values[eid]) ||
! is_approx(ppi.e_restart_extra[eid], ppi.opt_e_restart_extra->values[eid]))
const_cast<Extruder&>(e).set_retracted(ppi.opt_e_retracted->values[eid], ppi.opt_e_restart_extra->values[eid]);
}
}
return output;
}
catch (std::runtime_error &err)
{
// Collect the names of failed template substitutions for error reporting.
auto it = m_placeholder_parser_failed_templates.find(name);
if (it == m_placeholder_parser_failed_templates.end())
auto it = ppi.failed_templates.find(name);
if (it == ppi.failed_templates.end())
// Only if there was no error reported for this template, store the first error message into the map to be reported.
// We don't want to collect error message for each and every occurence of a single custom G-code section.
m_placeholder_parser_failed_templates.insert(it, std::make_pair(name, std::string(err.what())));
ppi.failed_templates.insert(it, std::make_pair(name, std::string(err.what())));
// Insert the macro error message into the G-code.
return
std::string("\n!!!!! Failed to process the custom G-code template ") + name + "\n" +
@ -3160,7 +3274,7 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z)
// if we are running a single-extruder setup, just set the extruder and return nothing
if (!m_writer.multiple_extruders) {
m_placeholder_parser.set("current_extruder", extruder_id);
this->placeholder_parser().set("current_extruder", extruder_id);
std::string gcode;
// Append the filament start G-code.
@ -3169,7 +3283,7 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z)
// Process the start_filament_gcode for the filament.
DynamicConfig config;
config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index));
config.set_key_value("layer_z", new ConfigOptionFloat(this->writer().get_position()(2) - m_config.z_offset.value));
config.set_key_value("layer_z", new ConfigOptionFloat(this->writer().get_position().z() - m_config.z_offset.value));
config.set_key_value("max_layer_z", new ConfigOptionFloat(m_max_layer_z));
config.set_key_value("filament_extruder_id", new ConfigOptionInt(int(extruder_id)));
gcode += this->placeholder_parser_process("start_filament_gcode", start_filament_gcode, extruder_id, &config);
@ -3233,7 +3347,7 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z)
gcode += m_writer.set_temperature(temp, false);
}
m_placeholder_parser.set("current_extruder", extruder_id);
this->placeholder_parser().set("current_extruder", extruder_id);
// Append the filament start G-code.
const std::string &start_filament_gcode = m_config.start_filament_gcode.get_at(extruder_id);
@ -3241,7 +3355,7 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z)
// Process the start_filament_gcode for the new filament.
DynamicConfig config;
config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index));
config.set_key_value("layer_z", new ConfigOptionFloat(this->writer().get_position()(2) - m_config.z_offset.value));
config.set_key_value("layer_z", new ConfigOptionFloat(this->writer().get_position().z() - m_config.z_offset.value));
config.set_key_value("max_layer_z", new ConfigOptionFloat(m_max_layer_z));
config.set_key_value("filament_extruder_id", new ConfigOptionInt(int(extruder_id)));
gcode += this->placeholder_parser_process("start_filament_gcode", start_filament_gcode, extruder_id, &config);

View File

@ -173,8 +173,8 @@ public:
const Layer* layer() const { return m_layer; }
GCodeWriter& writer() { return m_writer; }
const GCodeWriter& writer() const { return m_writer; }
PlaceholderParser& placeholder_parser() { return m_placeholder_parser; }
const PlaceholderParser& placeholder_parser() const { return m_placeholder_parser; }
PlaceholderParser& placeholder_parser() { return m_placeholder_parser_integration.parser; }
const PlaceholderParser& placeholder_parser() const { return m_placeholder_parser_integration.parser; }
// Process a template through the placeholder parser, collect error messages to be reported
// inside the generated string and after the G-code export finishes.
std::string placeholder_parser_process(const std::string &name, const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override = nullptr);
@ -343,11 +343,33 @@ private:
// scaled G-code resolution
double m_scaled_resolution;
GCodeWriter m_writer;
PlaceholderParser m_placeholder_parser;
// For random number generator etc.
PlaceholderParser::ContextData m_placeholder_parser_context;
// Collection of templates, on which the placeholder substitution failed.
std::map<std::string, std::string> m_placeholder_parser_failed_templates;
struct PlaceholderParserIntegration {
void reset();
void init(const GCodeWriter &config);
void update_from_gcodewriter(const GCodeWriter &writer);
void validate_output_vector_variables();
PlaceholderParser parser;
// For random number generator etc.
PlaceholderParser::ContextData context;
// Collection of templates, on which the placeholder substitution failed.
std::map<std::string, std::string> failed_templates;
// Input/output from/to custom G-code block, for returning position, retraction etc.
DynamicConfig output_config;
ConfigOptionFloats *opt_position { nullptr };
ConfigOptionFloat *opt_zhop { nullptr };
ConfigOptionFloats *opt_e_position { nullptr };
ConfigOptionFloats *opt_e_retracted { nullptr };
ConfigOptionFloats *opt_e_restart_extra { nullptr };
// Caches of the data passed to the script.
size_t num_extruders;
std::vector<double> position;
std::vector<double> e_position;
std::vector<double> e_retracted;
std::vector<double> e_restart_extra;
} m_placeholder_parser_integration;
OozePrevention m_ooze_prevention;
Wipe m_wipe;
AvoidCrossingPerimeters m_avoid_crossing_perimeters;
@ -403,15 +425,15 @@ private:
// Index of a last object copy extruded.
std::pair<const PrintObject*, Point> m_last_obj_copy;
bool m_silent_time_estimator_enabled;
bool m_silent_time_estimator_enabled;
// Processor
GCodeProcessor m_processor;
GCodeProcessor m_processor;
std::string _extrude(const ExtrusionPath &path, const std::string_view description, double speed = -1);
void print_machine_envelope(GCodeOutputStream &file, Print &print);
void _print_first_layer_bed_temperature(GCodeOutputStream &file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait);
void _print_first_layer_extruder_temperatures(GCodeOutputStream &file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait);
std::string _extrude(const ExtrusionPath &path, const std::string_view description, double speed = -1);
void print_machine_envelope(GCodeOutputStream &file, Print &print);
void _print_first_layer_bed_temperature(GCodeOutputStream &file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait);
void _print_first_layer_extruder_temperatures(GCodeOutputStream &file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait);
// On the first printing layer. This flag triggers first layer speeds.
bool on_first_layer() const { return m_layer != nullptr && m_layer->id() == 0; }
// To control print speed of 1st object layer over raft interface.

View File

@ -486,6 +486,20 @@ std::string GCodeWriter::unlift()
return gcode;
}
void GCodeWriter::update_position(const Vec3d &new_pos)
{
assert(this->m_lifted >= 0);
const double nominal_z = m_pos.z() - m_lifted;
m_lifted = new_pos.z() - nominal_z;
if (m_lifted < - EPSILON)
throw Slic3r::RuntimeError("Custom G-code reports negative Z-hop. Final Z position is below the print_z height.");
// In case that retract_lift == layer_height we could end up with almost zero in_m_lifted
// and a retract could be skipped (https://github.com/prusa3d/PrusaSlicer/issues/2154
if (m_lifted < EPSILON)
m_lifted = 0.;
m_pos = new_pos;
}
std::string GCodeWriter::set_fan(const GCodeFlavor gcode_flavor, bool gcode_comments, unsigned int speed)
{
std::ostringstream gcode;

View File

@ -68,7 +68,18 @@ public:
std::string unretract();
std::string lift();
std::string unlift();
// Current position of the printer, in G-code coordinates.
// Z coordinate of current position contains zhop. If zhop is applied (this->zhop() > 0),
// then the print_z = this->get_position().z() - this->zhop().
Vec3d get_position() const { return m_pos; }
// Current Z hop value.
double get_zhop() const { return m_lifted; }
// Update position of the print head based on the final position returned by a custom G-code block.
// The new position Z coordinate contains the Z-hop.
// GCodeWriter expects the custom script to NOT change print_z, only Z-hop, thus the print_z is maintained
// by this function while the current Z-hop accumulator is updated.
void update_position(const Vec3d &new_pos);
// Returns whether this flavor supports separate print and travel acceleration.
static bool supports_separate_travel_acceleration(GCodeFlavor flavor);

View File

@ -727,7 +727,7 @@ void LayerRegion::prepare_fill_surfaces()
if (! spiral_vase && this->region().config().top_solid_layers == 0) {
for (Surface &surface : m_fill_surfaces)
if (surface.is_top())
surface.surface_type = this->layer()->object()->config().infill_only_where_needed && this->region().config().fill_pattern != ipLightning ? stInternalVoid : stInternal;
surface.surface_type = /*this->layer()->object()->config().infill_only_where_needed && this->region().config().fill_pattern != ipLightning ? stInternalVoid :*/ stInternal;
}
if (this->region().config().bottom_solid_layers == 0) {
for (Surface &surface : m_fill_surfaces)

View File

@ -177,7 +177,7 @@ namespace client
bool writable { false };
// -1 means it is a scalar variable, or it is a vector variable and index was not assigned yet or the whole vector is considered.
int index { -1 };
IteratorRange it_range;
IteratorRange it_range;
bool empty() const { return opt == nullptr; }
bool has_index() const { return index != -1; }
@ -1862,7 +1862,7 @@ namespace client
// Allow back tracking after '{' in case of a text_block embedded inside a condition.
// In that case the inner-most {else} wins and the {if}/{elsif}/{else} shall be paired.
// {elsif}/{else} without an {if} will be allowed to back track from the embedded text_block.
| (lit('{') >> macro(_r1)[_val+=_1] > *(+lit(';') >> macro(_r1)[_val+=_1]) > *lit(';') > '}')
| (lit('{') >> (macros(_r1)[_val += _1] > '}') | '}')
| (lit('[') > legacy_variable_expansion(_r1) [_val+=_1] > ']')
);
text_block.name("text_block");
@ -1874,26 +1874,45 @@ namespace client
// New style of macro expansion.
// The macro expansion may contain numeric or string expressions, ifs and cases.
macro =
(kw["if"] > if_else_output(_r1) [_val = _1])
// | (kw["switch"] > switch_output(_r1) [_val = _1])
| (assignment_statement(_r1) [_val = _1])
| (new_variable_statement(_r1) [_val = _1])
| (conditional_expression(_r1) [ px::bind(&expr::to_string2, _1, _val) ])
macros =
+(block(_r1)[_val += _1] | (statement(_r1) > (+lit(';') | &lit('}')))[_val += _1] | +lit(';'));
macros.name("macro");
// if_macros and else_macros only differ by the look-ahead ending condition, which is to not have to repeat the last semicolon
// at the end of the block.
if_macros = kw["then"] > *(block(_r1)[_val += _1] | (statement(_r1) > (+lit(';') | &(kw["elsif"] | kw["else"] | kw["endif"])))[_val += _1] | +lit(';'));
if_macros.name("if_macros");
else_macros = *(block(_r1)[_val += _1] | (statement(_r1) > (+lit(';') | &kw["endif"]))[_val += _1] | +lit(';'));
else_macros.name("else_macros");
// Blocks do not require a separating semicolon.
block =
(kw["if"] > if_else_output(_r1)[_val = _1])
// (kw["switch"] ...
;
block.name("block");
// Statements require a separating semicolon.
statement =
(assignment_statement(_r1) [_val = _1])
| (new_variable_statement(_r1)[_val = _1])
| (conditional_expression(_r1)[px::bind(&expr::to_string2, _1, _val)])
;
macro.name("macro");
// An if expression enclosed in {} (the outmost {} are already parsed by the caller).
// Also }{ could be replaced with ; to simplify writing of pure code.
if_else_output =
eps[_a=true] >
(bool_expr_eval(_r1)[px::bind(&MyContext::block_enter, _r1, _1)] > '}' >
text_block(_r1))[px::bind(&MyContext::block_exit, _r1, _1, _a, _2, _val)] > '{' >
*((kw["elsif"] > bool_expr_eval(_r1)[px::bind(&MyContext::block_enter, _r1, _1 && _a)] > '}' >
text_block(_r1))[px::bind(&MyContext::block_exit, _r1, _1, _a, _2, _val)] > '{') >
-(kw["else"] > eps[px::bind(&MyContext::block_enter, _r1, _a)] > lit('}') >
text_block(_r1)[px::bind(&MyContext::block_exit, _r1, _a, _a, _1, _val)] > '{') >
(bool_expr_eval(_r1)[px::bind(&MyContext::block_enter, _r1, _1)] > (if_text_block(_r1) | if_macros(_r1)))
[px::bind(&MyContext::block_exit, _r1, _1, _a, _2, _val)] >
*((kw["elsif"] > bool_expr_eval(_r1)[px::bind(&MyContext::block_enter, _r1, _1 && _a)] > (if_text_block(_r1) | if_macros(_r1)))
[px::bind(&MyContext::block_exit, _r1, _1, _a, _2, _val)]) >
-(kw["else"] > eps[px::bind(&MyContext::block_enter, _r1, _a)] > (if_text_block(_r1) | else_macros(_r1)))
[px::bind(&MyContext::block_exit, _r1, _a, _a, _1, _val)] >
kw["endif"];
if_else_output.name("if_else_output");
if_text_block = (lit('}') > text_block(_r1) > '{');
if_text_block.name("if_text_block");
// A switch expression enclosed in {} (the outmost {} are already parsed by the caller).
/*
switch_output =
@ -2119,7 +2138,7 @@ namespace client
debug(start);
debug(text);
debug(text_block);
debug(macro);
debug(macros);
debug(if_else_output);
debug(interpolate_table);
// debug(switch_output);
@ -2155,7 +2174,7 @@ namespace client
// A free-form text, possibly empty, possibly containing macro expansions.
qi::rule<Iterator, std::string(const MyContext*), spirit_encoding::space_type> text_block;
// Statements enclosed in curely braces {}
qi::rule<Iterator, std::string(const MyContext*), spirit_encoding::space_type> macro;
qi::rule<Iterator, std::string(const MyContext*), spirit_encoding::space_type> block, statement, macros, if_text_block, if_macros, else_macros;
// Legacy variable expansion of the original Slic3r, in the form of [scalar_variable] or [vector_variable_index].
qi::rule<Iterator, std::string(const MyContext*), spirit_encoding::space_type> legacy_variable_expansion;
// Parsed identifier name.

View File

@ -3,6 +3,7 @@
#include "Exception.hpp"
#include "Preset.hpp"
#include "AppConfig.hpp"
#include "I18N.hpp"
#ifdef _MSC_VER
#define WIN32_LEAN_AND_MEAN
@ -424,7 +425,7 @@ static std::vector<std::string> s_Preset_print_options {
"top_solid_layers", "top_solid_min_thickness", "bottom_solid_layers", "bottom_solid_min_thickness",
"extra_perimeters", "extra_perimeters_on_overhangs", "avoid_crossing_curled_overhangs", "avoid_crossing_perimeters", "thin_walls", "overhangs",
"seam_position","staggered_inner_seams", "external_perimeters_first", "fill_density", "fill_pattern", "top_fill_pattern", "bottom_fill_pattern",
"infill_every_layers", "infill_only_where_needed", "solid_infill_every_layers", "fill_angle", "bridge_angle",
"infill_every_layers", /*"infill_only_where_needed",*/ "solid_infill_every_layers", "fill_angle", "bridge_angle",
"solid_infill_below_area", "only_retract_when_crossing_perimeters", "infill_first",
"ironing", "ironing_type", "ironing_flowrate", "ironing_speed", "ironing_spacing",
"max_print_speed", "max_volumetric_speed", "avoid_crossing_perimeters_max_detour",

View File

@ -1560,14 +1560,14 @@ void PrintConfigDef::init_fff_params()
def->mode = comExpert;
def->set_default_value(new ConfigOptionBool(false));
def = this->add("infill_only_where_needed", coBool);
def->label = L("Only infill where needed");
def->category = L("Infill");
def->tooltip = L("This option will limit infill to the areas actually needed for supporting ceilings "
"(it will act as internal support material). If enabled, slows down the G-code generation "
"due to the multiple checks involved.");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(false));
// def = this->add("infill_only_where_needed", coBool);
// def->label = L("Only infill where needed");
// def->category = L("Infill");
// def->tooltip = L("This option will limit infill to the areas actually needed for supporting ceilings "
// "(it will act as internal support material). If enabled, slows down the G-code generation "
// "due to the multiple checks involved.");
// def->mode = comAdvanced;
// def->set_default_value(new ConfigOptionBool(false));
def = this->add("infill_overlap", coFloatOrPercent);
def->label = L("Infill/perimeters overlap");
@ -4154,6 +4154,8 @@ static std::set<std::string> PrintConfigDef_ignore = {
"wall_add_middle_threshold", "wall_split_middle_threshold",
// Replaced by new concentric ensuring in 2.6.0-alpha5
"ensure_vertical_shell_thickness",
// Disabled in 2.6.0-alpha6, this option is problematic
"infill_only_where_needed",
};
void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &value)
@ -4552,12 +4554,19 @@ std::string validate(const FullPrintConfig &cfg)
}
case coFloats:
case coPercents:
for (double v : static_cast<const ConfigOptionVector<double>*>(opt)->values)
{
const auto* vec = static_cast<const ConfigOptionVector<double>*>(opt);
for (size_t i = 0; i < vec->size(); ++i) {
if (vec->is_nil(i))
continue;
double v = vec->values[i];
if (v < optdef->min || v > optdef->max) {
out_of_range = true;
break;
}
}
break;
}
case coInt:
{
auto *iopt = static_cast<const ConfigOptionInt*>(opt);
@ -4565,12 +4574,19 @@ std::string validate(const FullPrintConfig &cfg)
break;
}
case coInts:
for (int v : static_cast<const ConfigOptionVector<int>*>(opt)->values)
{
const auto* vec = static_cast<const ConfigOptionVector<int>*>(opt);
for (size_t i = 0; i < vec->size(); ++i) {
if (vec->is_nil(i))
continue;
int v = vec->values[i];
if (v < optdef->min || v > optdef->max) {
out_of_range = true;
break;
}
}
break;
}
default:;
}
if (out_of_range)

View File

@ -495,7 +495,7 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionFloatOrPercent, extrusion_width))
((ConfigOptionFloat, first_layer_acceleration_over_raft))
((ConfigOptionFloatOrPercent, first_layer_speed_over_raft))
((ConfigOptionBool, infill_only_where_needed))
// ((ConfigOptionBool, infill_only_where_needed))
// Force the generation of solid shells between adjacent materials/volumes.
((ConfigOptionBool, interface_shells))
((ConfigOptionFloat, layer_height))

View File

@ -348,8 +348,8 @@ void PrintObject::prepare_infill()
//FIXME The surfaces are supported by a sparse infill, but the sparse infill is only as large as the area to support.
// Likely the sparse infill will not be anchored correctly, so it will not work as intended.
// Also one wishes the perimeters to be supported by a full infill.
this->clip_fill_surfaces();
m_print->throw_if_canceled();
// this->clip_fill_surfaces();
// m_print->throw_if_canceled();
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) {
@ -2107,7 +2107,7 @@ void PrintObject::bridge_over_infill()
total_fill_area = closing(total_fill_area, SCALED_EPSILON);
expansion_area = closing(expansion_area, SCALED_EPSILON);
expansion_area = intersection(expansion_area, deep_infill_area);
Polylines anchors = intersection_pl(infill_lines[lidx - 1], expansion_area);
Polylines anchors = intersection_pl(infill_lines[lidx - 1], shrink(expansion_area, spacing));
#ifdef DEBUG_BRIDGE_OVER_INFILL
debug_draw(std::to_string(lidx) + "_" + std::to_string(cluster_idx) + "_" + std::to_string(job_idx) + "_" +
@ -2419,98 +2419,98 @@ bool PrintObject::update_layer_height_profile(const ModelObject &model_object, c
// Also one wishes the perimeters to be supported by a full infill.
// Idempotence of this method is guaranteed by the fact that we don't remove things from
// fill_surfaces but we only turn them into VOID surfaces, thus preserving the boundaries.
void PrintObject::clip_fill_surfaces()
{
bool has_lightning_infill = false;
for (size_t region_id = 0; region_id < this->num_printing_regions(); ++region_id)
if (const PrintRegionConfig &config = this->printing_region(region_id).config(); config.fill_density > 0 && config.fill_pattern == ipLightning)
has_lightning_infill = true;
// void PrintObject::clip_fill_surfaces()
// {
// bool has_lightning_infill = false;
// for (size_t region_id = 0; region_id < this->num_printing_regions(); ++region_id)
// if (const PrintRegionConfig &config = this->printing_region(region_id).config(); config.fill_density > 0 && config.fill_pattern == ipLightning)
// has_lightning_infill = true;
// For Lightning infill, infill_only_where_needed is ignored because both
// do a similar thing, and their combination doesn't make much sense.
if (! m_config.infill_only_where_needed.value || has_lightning_infill)
return;
bool has_infill = false;
for (size_t i = 0; i < this->num_printing_regions(); ++ i)
if (this->printing_region(i).config().fill_density > 0) {
has_infill = true;
break;
}
if (! has_infill)
return;
// // For Lightning infill, infill_only_where_needed is ignored because both
// // do a similar thing, and their combination doesn't make much sense.
// if (! m_config.infill_only_where_needed.value || has_lightning_infill)
// return;
// bool has_infill = false;
// for (size_t i = 0; i < this->num_printing_regions(); ++ i)
// if (this->printing_region(i).config().fill_density > 0) {
// has_infill = true;
// break;
// }
// if (! has_infill)
// return;
// We only want infill under ceilings; this is almost like an
// internal support material.
// Proceed top-down, skipping the bottom layer.
Polygons upper_internal;
for (int layer_id = int(m_layers.size()) - 1; layer_id > 0; -- layer_id) {
Layer *layer = m_layers[layer_id];
Layer *lower_layer = m_layers[layer_id - 1];
// Detect things that we need to support.
// Cummulative fill surfaces.
Polygons fill_surfaces;
// Solid surfaces to be supported.
Polygons overhangs;
for (const LayerRegion *layerm : layer->m_regions)
for (const Surface &surface : layerm->fill_surfaces()) {
Polygons polygons = to_polygons(surface.expolygon);
if (surface.is_solid())
polygons_append(overhangs, polygons);
polygons_append(fill_surfaces, std::move(polygons));
}
Polygons lower_layer_fill_surfaces;
Polygons lower_layer_internal_surfaces;
for (const LayerRegion *layerm : lower_layer->m_regions)
for (const Surface &surface : layerm->fill_surfaces()) {
Polygons polygons = to_polygons(surface.expolygon);
if (surface.surface_type == stInternal || surface.surface_type == stInternalVoid)
polygons_append(lower_layer_internal_surfaces, polygons);
polygons_append(lower_layer_fill_surfaces, std::move(polygons));
}
// We also need to support perimeters when there's at least one full unsupported loop
{
// Get perimeters area as the difference between slices and fill_surfaces
// Only consider the area that is not supported by lower perimeters
Polygons perimeters = intersection(diff(layer->lslices, fill_surfaces), lower_layer_fill_surfaces);
// Only consider perimeter areas that are at least one extrusion width thick.
//FIXME Offset2 eats out from both sides, while the perimeters are create outside in.
//Should the pw not be half of the current value?
float pw = FLT_MAX;
for (const LayerRegion *layerm : layer->m_regions)
pw = std::min(pw, (float)layerm->flow(frPerimeter).scaled_width());
// Append such thick perimeters to the areas that need support
polygons_append(overhangs, opening(perimeters, pw));
}
// Merge the new overhangs, find new internal infill.
polygons_append(upper_internal, std::move(overhangs));
static constexpr const auto closing_radius = scaled<float>(2.f);
upper_internal = intersection(
// Regularize the overhang regions, so that the infill areas will not become excessively jagged.
smooth_outward(
closing(upper_internal, closing_radius, ClipperLib::jtSquare, 0.),
scaled<coord_t>(0.1)),
lower_layer_internal_surfaces);
// Apply new internal infill to regions.
for (LayerRegion *layerm : lower_layer->m_regions) {
if (layerm->region().config().fill_density.value == 0)
continue;
Polygons internal;
for (Surface &surface : layerm->m_fill_surfaces.surfaces)
if (surface.surface_type == stInternal || surface.surface_type == stInternalVoid)
polygons_append(internal, std::move(surface.expolygon));
layerm->m_fill_surfaces.remove_types({ stInternal, stInternalVoid });
layerm->m_fill_surfaces.append(intersection_ex(internal, upper_internal, ApplySafetyOffset::Yes), stInternal);
layerm->m_fill_surfaces.append(diff_ex (internal, upper_internal, ApplySafetyOffset::Yes), stInternalVoid);
// If there are voids it means that our internal infill is not adjacent to
// perimeters. In this case it would be nice to add a loop around infill to
// make it more robust and nicer. TODO.
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
layerm->export_region_fill_surfaces_to_svg_debug("6_clip_fill_surfaces");
#endif
}
m_print->throw_if_canceled();
}
} // void PrintObject::clip_fill_surfaces()
// // We only want infill under ceilings; this is almost like an
// // internal support material.
// // Proceed top-down, skipping the bottom layer.
// Polygons upper_internal;
// for (int layer_id = int(m_layers.size()) - 1; layer_id > 0; -- layer_id) {
// Layer *layer = m_layers[layer_id];
// Layer *lower_layer = m_layers[layer_id - 1];
// // Detect things that we need to support.
// // Cummulative fill surfaces.
// Polygons fill_surfaces;
// // Solid surfaces to be supported.
// Polygons overhangs;
// for (const LayerRegion *layerm : layer->m_regions)
// for (const Surface &surface : layerm->fill_surfaces()) {
// Polygons polygons = to_polygons(surface.expolygon);
// if (surface.is_solid())
// polygons_append(overhangs, polygons);
// polygons_append(fill_surfaces, std::move(polygons));
// }
// Polygons lower_layer_fill_surfaces;
// Polygons lower_layer_internal_surfaces;
// for (const LayerRegion *layerm : lower_layer->m_regions)
// for (const Surface &surface : layerm->fill_surfaces()) {
// Polygons polygons = to_polygons(surface.expolygon);
// if (surface.surface_type == stInternal || surface.surface_type == stInternalVoid)
// polygons_append(lower_layer_internal_surfaces, polygons);
// polygons_append(lower_layer_fill_surfaces, std::move(polygons));
// }
// // We also need to support perimeters when there's at least one full unsupported loop
// {
// // Get perimeters area as the difference between slices and fill_surfaces
// // Only consider the area that is not supported by lower perimeters
// Polygons perimeters = intersection(diff(layer->lslices, fill_surfaces), lower_layer_fill_surfaces);
// // Only consider perimeter areas that are at least one extrusion width thick.
// //FIXME Offset2 eats out from both sides, while the perimeters are create outside in.
// //Should the pw not be half of the current value?
// float pw = FLT_MAX;
// for (const LayerRegion *layerm : layer->m_regions)
// pw = std::min(pw, (float)layerm->flow(frPerimeter).scaled_width());
// // Append such thick perimeters to the areas that need support
// polygons_append(overhangs, opening(perimeters, pw));
// }
// // Merge the new overhangs, find new internal infill.
// polygons_append(upper_internal, std::move(overhangs));
// static constexpr const auto closing_radius = scaled<float>(2.f);
// upper_internal = intersection(
// // Regularize the overhang regions, so that the infill areas will not become excessively jagged.
// smooth_outward(
// closing(upper_internal, closing_radius, ClipperLib::jtSquare, 0.),
// scaled<coord_t>(0.1)),
// lower_layer_internal_surfaces);
// // Apply new internal infill to regions.
// for (LayerRegion *layerm : lower_layer->m_regions) {
// if (layerm->region().config().fill_density.value == 0)
// continue;
// Polygons internal;
// for (Surface &surface : layerm->m_fill_surfaces.surfaces)
// if (surface.surface_type == stInternal || surface.surface_type == stInternalVoid)
// polygons_append(internal, std::move(surface.expolygon));
// layerm->m_fill_surfaces.remove_types({ stInternal, stInternalVoid });
// layerm->m_fill_surfaces.append(intersection_ex(internal, upper_internal, ApplySafetyOffset::Yes), stInternal);
// layerm->m_fill_surfaces.append(diff_ex (internal, upper_internal, ApplySafetyOffset::Yes), stInternalVoid);
// // If there are voids it means that our internal infill is not adjacent to
// // perimeters. In this case it would be nice to add a loop around infill to
// // make it more robust and nicer. TODO.
// #ifdef SLIC3R_DEBUG_SLICE_PROCESSING
// layerm->export_region_fill_surfaces_to_svg_debug("6_clip_fill_surfaces");
// #endif
// }
// m_print->throw_if_canceled();
// }
// } // void PrintObject::clip_fill_surfaces()
void PrintObject::discover_horizontal_shells()
{

View File

@ -588,6 +588,15 @@ std::string SLAPrint::validate(std::string*) const
return "";
}
void SLAPrint::export_print(const std::string &fname, const ThumbnailsList &thumbnails, const std::string &projectname)
{
if (m_archiver)
m_archiver->export_print(fname, *this, thumbnails, projectname);
else {
throw ExportError(format(_u8L("Unknown archive format: %s"), m_printer_config.sla_archive_format.value));
}
}
bool SLAPrint::invalidate_step(SLAPrintStep step)
{
bool invalidated = Inherited::invalidate_step(step);

View File

@ -546,10 +546,7 @@ public:
void export_print(const std::string &fname,
const ThumbnailsList &thumbnails,
const std::string &projectname = "")
{
m_archiver->export_print(fname, *this, thumbnails, projectname);
}
const std::string &projectname = "");
private:

View File

@ -199,7 +199,13 @@ void SLAPrint::Steps::generate_preview(SLAPrintObject &po, SLAPrintObjectStep st
m = csgmesh_merge_positive_parts(r);
handled = true;
} else if (csg::check_csgmesh_booleans(r) == r.end()) {
auto cgalmeshptr = csg::perform_csgmesh_booleans(r);
MeshBoolean::cgal::CGALMeshPtr cgalmeshptr;
try {
cgalmeshptr = csg::perform_csgmesh_booleans(r);
} catch (...) {
// leaves cgalmeshptr as nullptr
}
if (cgalmeshptr) {
m = MeshBoolean::cgal::cgal_to_indexed_triangle_set(*cgalmeshptr);
handled = true;

View File

@ -44,17 +44,23 @@ void CoordAxes::render(const Transform3d& trafo, float emission_factor)
shader->start_using();
shader->set_uniform("emission_factor", emission_factor);
// Scale the axes if the camera is close to them to avoid issues
// such as https://github.com/prusa3d/PrusaSlicer/issues/9483
const Camera& camera = wxGetApp().plater()->get_camera();
Transform3d scale_tr = Transform3d::Identity();
scale_tr.scale(std::min(1., camera.get_inv_zoom() * 10.));
// x axis
m_arrow.set_color(ColorRGBA::X());
render_axis(*shader, trafo * Geometry::translation_transform(m_origin) * Geometry::rotation_transform({ 0.0, 0.5 * M_PI, 0.0 }));
render_axis(*shader, trafo * Geometry::translation_transform(m_origin) * Geometry::rotation_transform({ 0.0, 0.5 * M_PI, 0.0 }) * scale_tr);
// y axis
m_arrow.set_color(ColorRGBA::Y());
render_axis(*shader, trafo * Geometry::translation_transform(m_origin) * Geometry::rotation_transform({ -0.5 * M_PI, 0.0, 0.0 }));
render_axis(*shader, trafo * Geometry::translation_transform(m_origin) * Geometry::rotation_transform({ -0.5 * M_PI, 0.0, 0.0 }) * scale_tr);
// z axis
m_arrow.set_color(ColorRGBA::Z());
render_axis(*shader, trafo * Geometry::translation_transform(m_origin));
render_axis(*shader, trafo * Geometry::translation_transform(m_origin) * scale_tr);
shader->stop_using();
if (curr_shader != nullptr)

View File

@ -1572,8 +1572,7 @@ void GLCanvas3D::render()
_render_objects(GLVolumeCollection::ERenderType::Opaque);
_render_sla_slices();
_render_selection();
if (m_show_bed_axes)
_render_bed_axes();
_render_bed_axes();
if (is_looking_downward)
_render_bed(camera.get_view_matrix(), camera.get_projection_matrix(), false);
if (!m_main_toolbar.is_enabled())
@ -2351,15 +2350,6 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
#endif /* __APPLE__ */
post_event(SimpleEvent(EVT_GLTOOLBAR_COPY));
break;
#ifdef __APPLE__
case 'd':
case 'D':
#else /* __APPLE__ */
case WXK_CONTROL_D:
#endif /* __APPLE__ */
m_show_bed_axes = !m_show_bed_axes;
m_dirty = true;
break;
#ifdef __APPLE__
case 'f':
case 'F':

View File

@ -522,7 +522,6 @@ private:
ECursorType m_cursor_type;
GLSelectionRectangle m_rectangle_selection;
std::vector<int> m_hover_volume_idxs;
bool m_show_bed_axes{ true };
// Following variable is obsolete and it should be safe to remove it.
// I just don't want to do it now before a release (Lukas Matena 24.3.2019)

View File

@ -1322,7 +1322,7 @@ bool GUI_App::on_init_inner()
if (!delayed_error_load_presets.empty())
show_error(nullptr, delayed_error_load_presets);
mainframe = new MainFrame();
mainframe = new MainFrame(app_config->has("font_size") ? atoi(app_config->get("font_size").c_str()) : -1);
// hide settings tabs after first Layout
if (is_editor())
mainframe->select_tab(size_t(0));
@ -1810,7 +1810,7 @@ void GUI_App::recreate_GUI(const wxString& msg_name)
dlg.Update(10, _L("Recreating") + dots);
MainFrame *old_main_frame = mainframe;
mainframe = new MainFrame();
mainframe = new MainFrame(app_config->has("font_size") ? atoi(app_config->get("font_size").c_str()) : -1);
if (is_editor())
// hide settings tabs after first Layout
mainframe->select_tab(size_t(0));

View File

@ -2939,6 +2939,16 @@ void ObjectList::update_info_items(size_t obj_idx, wxDataViewItemArray* selectio
if (obj_idx >= m_objects->size())
return;
wxDataViewItemArray sels;
if (!selections) {
GetSelections(sels);
for (wxDataViewItem item : sels)
if (item.IsOk() && m_objects_model->GetItemType(item) == itVolume) {
selections = &sels;
break;
}
}
const ModelObject* model_object = (*m_objects)[obj_idx];
wxDataViewItem item_obj = m_objects_model->GetItemById(obj_idx);
assert(item_obj.IsOk());

View File

@ -82,7 +82,7 @@ template<class P> class DPIAware : public P
{
public:
DPIAware(wxWindow *parent, wxWindowID id, const wxString &title, const wxPoint &pos=wxDefaultPosition,
const wxSize &size=wxDefaultSize, long style=wxDEFAULT_FRAME_STYLE, const wxString &name=wxFrameNameStr)
const wxSize &size=wxDefaultSize, long style=wxDEFAULT_FRAME_STYLE, const wxString &name= wxFrameNameStr, const int font_point_size = -1)
: P(parent, id, title, pos, size, style, name)
{
int dpi = get_dpi_for_window(this);
@ -90,6 +90,9 @@ public:
m_prev_scale_factor = m_scale_factor;
m_normal_font = get_default_font_for_dpi(this, dpi);
if (font_point_size > 0)
m_normal_font.SetPointSize(font_point_size);
/* Because of default window font is a primary display font,
* We should set correct font for window before getting em_unit value.
*/

View File

@ -774,7 +774,7 @@ GLGizmoEmboss::GuiCfg GLGizmoEmboss::create_gui_configuration()
tr.skew_ration = _u8L("Skew ratio");
tr.from_surface = _u8L("From surface");
tr.rotation = _u8L("Rotation");
tr.keep_up = _u8L("Keep Up");
tr.keep_up = "Keep Rotation";
tr.collection = _u8L("Collection");
float max_advanced_text_width = std::max({
@ -2996,7 +2996,7 @@ void GLGizmoEmboss::draw_advanced()
}
}
if (ImGui::IsItemHovered())
ImGui::SetTooltip("%s", _u8L("Keep text orientation during surface dragging.\nNot stable between horizontal and vertical alignment.").c_str());
ImGui::SetTooltip("%s", _u8L("Lock the text's rotation when moving text along the object's surface.").c_str());
// when more collection add selector
if (ff.font_file->infos.size() > 1) {

View File

@ -13,6 +13,7 @@
#include <GL/glew.h>
#include <memory>
#include <wx/string.h>
namespace Slic3r::GUI {

View File

@ -151,7 +151,6 @@ void KBShortcutsDialog::fill_shortcuts()
{ L("Arrow Right"), L("Move selection 10 mm in positive X direction") },
{ std::string("Shift+") + L("Any arrow"), L("Movement step set to 1 mm") },
{ ctrl + L("Any arrow"), L("Movement in camera space") },
{ ctrl + "D", L("Show/hide reference axes") },
{ L("Page Up"), L("Rotate selection 45 degrees CCW") },
{ L("Page Down"), L("Rotate selection 45 degrees CW") },
{ "M", L("Gizmo move") },
@ -232,7 +231,6 @@ void KBShortcutsDialog::fill_shortcuts()
{ "X", L("On/Off one layer mode of the vertical slider") },
{ "L", L("Show/Hide legend") },
{ "C", L("Show/Hide G-code window") },
{ ctrl + "D", L("Show/hide reference axes") },
};
m_full_shortcuts.push_back({ { _L("Preview"), "" }, preview_shortcuts });

View File

@ -122,8 +122,8 @@ static wxIcon main_frame_icon(GUI_App::EAppMode app_mode)
#endif // _WIN32
}
MainFrame::MainFrame() :
DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE, "mainframe"),
MainFrame::MainFrame(const int font_point_size) :
DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE, "mainframe", font_point_size),
m_printhost_queue_dlg(new PrintHostQueueDialog(this))
, m_recent_projects(9)
, m_settings_dialog(this)
@ -1540,6 +1540,7 @@ void MainFrame::init_menubar_as_editor()
// assign menubar to frame after appending items, otherwise special items
// will not be handled correctly
m_menubar = new wxMenuBar();
m_menubar->SetFont(this->normal_font());
m_menubar->Append(fileMenu, _L("&File"));
if (editMenu) m_menubar->Append(editMenu, _L("&Edit"));
m_menubar->Append(windowMenu, _L("&Window"));
@ -2237,7 +2238,7 @@ std::string MainFrame::get_dir_name(const wxString &full_name) const
// ----------------------------------------------------------------------------
SettingsDialog::SettingsDialog(MainFrame* mainframe)
:DPIFrame(NULL, wxID_ANY, wxString(SLIC3R_APP_NAME) + " - " + _L("Settings"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE, "settings_dialog"),
:DPIFrame(NULL, wxID_ANY, wxString(SLIC3R_APP_NAME) + " - " + _L("Settings"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE, "settings_dialog", mainframe->normal_font().GetPointSize()),
//: DPIDialog(mainframe, wxID_ANY, wxString(SLIC3R_APP_NAME) + " - " + _L("Settings"), wxDefaultPosition, wxDefaultSize,
// wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxMINIMIZE_BOX | wxMAXIMIZE_BOX, "settings_dialog"),
m_main_frame(mainframe)

View File

@ -139,7 +139,7 @@ protected:
virtual void on_sys_color_changed() override;
public:
MainFrame();
MainFrame(const int font_point_size);
~MainFrame() = default;
void update_layout();

View File

@ -136,7 +136,7 @@ static void add_msg_content(wxWindow* parent, wxBoxSizer* content_sizer, wxStrin
msg_lines++;
}
wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
wxFont font = wxGetApp().normal_font();//wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
wxFont monospace = wxGetApp().code_font();
wxColour text_clr = wxGetApp().get_label_clr_default();
wxColour bgr_clr = parent->GetBackgroundColour();

View File

@ -54,6 +54,10 @@
#include "libslic3r/ClipperUtils.hpp"
#include "libslic3r/miniz_extension.hpp"
// For stl export
#include "libslic3r/CSGMesh/ModelToCSGMesh.hpp"
#include "libslic3r/CSGMesh/PerformCSGMeshBooleans.hpp"
#include "GUI.hpp"
#include "GUI_App.hpp"
#include "GUI_ObjectList.hpp"
@ -6382,14 +6386,34 @@ void Plater::export_stl_obj(bool extended, bool selection_only)
return;
// Following lambda generates a combined mesh for export with normals pointing outwards.
auto mesh_to_export_fff = [](const ModelObject& mo, int instance_id) {
auto mesh_to_export_fff = [this](const ModelObject& mo, int instance_id) {
TriangleMesh mesh;
for (const ModelVolume* v : mo.volumes)
if (v->is_model_part()) {
TriangleMesh vol_mesh(v->mesh());
vol_mesh.transform(v->get_matrix(), true);
mesh.merge(vol_mesh);
}
std::vector<csg::CSGPart> csgmesh;
csgmesh.reserve(2 * mo.volumes.size());
csg::model_to_csgmesh(mo, Transform3d::Identity(), std::back_inserter(csgmesh),
csg::mpartsPositive | csg::mpartsNegative | csg::mpartsDoSplits);
if (csg::check_csgmesh_booleans(range(csgmesh)) == csgmesh.end()) {
try {
auto cgalm = csg::perform_csgmesh_booleans(range(csgmesh));
mesh = MeshBoolean::cgal::cgal_to_triangle_mesh(*cgalm);
} catch (...) {}
}
if (mesh.empty()) {
get_notification_manager()->push_plater_error_notification(
_u8L("Unable to perform boolean operation on model meshes. "
"Only positive parts will be exported."));
for (const ModelVolume* v : mo.volumes)
if (v->is_model_part()) {
TriangleMesh vol_mesh(v->mesh());
vol_mesh.transform(v->get_matrix(), true);
mesh.merge(vol_mesh);
}
}
if (instance_id == -1) {
TriangleMesh vols_mesh(mesh);
mesh = TriangleMesh();

View File

@ -4,6 +4,7 @@
#include "Plater.hpp"
#include "MsgDialog.hpp"
#include "I18N.hpp"
#include "format.hpp"
#include "libslic3r/AppConfig.hpp"
#include <wx/notebook.h>
#include "Notebook.hpp"
@ -580,6 +581,7 @@ void PreferencesDialog::build()
activate_options_tab(m_optgroup_other);
create_downloader_path_sizer();
// create_settings_font_widget();
#if ENABLE_ENVIRONMENT_MAP
// Add "Render" tab
@ -694,7 +696,7 @@ void PreferencesDialog::accept(wxEvent&)
bool update_filament_sidebar = (m_values.find("no_templates") != m_values.end());
std::vector<std::string> options_to_recreate_GUI = { "no_defaults", "tabs_as_menu", "sys_menu_enabled" };
std::vector<std::string> options_to_recreate_GUI = { "no_defaults", "tabs_as_menu", "sys_menu_enabled", "font_size" };
for (const std::string& option : options_to_recreate_GUI) {
if (m_values.find(option) != m_values.end()) {
@ -943,7 +945,6 @@ void PreferencesDialog::create_icon_size_slider()
void PreferencesDialog::create_settings_mode_widget()
{
wxWindow* parent = m_optgroup_gui->parent();
wxGetApp().UpdateDarkUI(parent);
wxString title = L("Layout Options");
wxStaticBox* stb = new wxStaticBox(parent, wxID_ANY, _(title));
@ -1045,6 +1046,74 @@ void PreferencesDialog::create_settings_mode_color_widget()
append_preferences_option_to_searcher(m_optgroup_gui, opt_key, title);
}
void PreferencesDialog::create_settings_font_widget()
{
wxWindow* parent = m_optgroup_other->parent();
wxGetApp().UpdateDarkUI(parent);
const wxString title = L("Application font size");
wxStaticBox* stb = new wxStaticBox(parent, wxID_ANY, _(title));
if (!wxOSX) stb->SetBackgroundStyle(wxBG_STYLE_PAINT);
const std::string opt_key = "font_size";
m_blinkers[opt_key] = new BlinkingBitmap(parent);
wxSizer* stb_sizer = new wxStaticBoxSizer(stb, wxHORIZONTAL);
wxStaticText* font_example = new wxStaticText(parent, wxID_ANY, "Application text");
int val = wxGetApp().normal_font().GetPointSize();
wxSpinCtrl* size_sc = new wxSpinCtrl(parent, wxID_ANY, format_wxstr("%1%", val), wxDefaultPosition, wxSize(15*em_unit(), -1), wxTE_PROCESS_ENTER | wxSP_ARROW_KEYS
#ifdef _WIN32
| wxBORDER_SIMPLE
#endif
, 8, 20);
wxGetApp().UpdateDarkUI(size_sc);
auto apply_font = [this, font_example, opt_key](const int val, const wxFont& font) {
font_example->SetFont(font);
m_values[opt_key] = format("%1%", val);
refresh_og(m_optgroup_other);
};
auto change_value = [size_sc, apply_font](wxCommandEvent& evt) {
const int val = size_sc->GetValue();
wxFont font = wxGetApp().normal_font();
font.SetPointSize(val);
apply_font(val, font);
};
size_sc->Bind(wxEVT_SPINCTRL, change_value);
size_sc->Bind(wxEVT_TEXT_ENTER, change_value);
auto revert_btn = new ScalableButton(parent, wxID_ANY, "undo");
revert_btn->SetToolTip(_L("Revert font to default"));
revert_btn->Bind(wxEVT_BUTTON, [size_sc, apply_font](wxEvent& event) {
wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
const int val = font.GetPointSize();
size_sc->SetValue(val);
apply_font(val, font);
});
parent->Bind(wxEVT_UPDATE_UI, [size_sc](wxUpdateUIEvent& evt) {
const int def_size = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).GetPointSize();
evt.Enable(def_size != size_sc->GetValue());
}, revert_btn->GetId());
stb_sizer->Add(new wxStaticText(parent, wxID_ANY, _L("Font size") + ":"), 0, wxALIGN_CENTER_VERTICAL | wxLEFT, em_unit());
stb_sizer->Add(size_sc, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT | wxLEFT, em_unit());
stb_sizer->Add(revert_btn, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, em_unit());
wxBoxSizer* font_sizer = new wxBoxSizer(wxVERTICAL);
font_sizer->Add(font_example, 1, wxALIGN_CENTER_HORIZONTAL);
stb_sizer->Add(font_sizer, 1, wxALIGN_CENTER_VERTICAL);
auto sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(m_blinkers[opt_key], 0, wxRIGHT, 2);
sizer->Add(stb_sizer, 1, wxALIGN_CENTER_VERTICAL);
m_optgroup_other->sizer->Add(sizer, 1, wxEXPAND | wxTOP, em_unit());
append_preferences_option_to_searcher(m_optgroup_other, opt_key, title);
}
void PreferencesDialog::create_downloader_path_sizer()
{
wxWindow* parent = m_optgroup_other->parent();

View File

@ -91,10 +91,12 @@ protected:
void layout();
void clear_cache();
void refresh_og(std::shared_ptr<ConfigOptionsGroup> og);
void refresh_og(ConfigOptionsGroup* og);
void create_icon_size_slider();
void create_settings_mode_widget();
void create_settings_text_color_widget();
void create_settings_mode_color_widget();
void create_settings_font_widget();
void create_downloader_path_sizer();
void init_highlighter(const t_config_option_key& opt_key);
std::vector<ConfigOptionsGroup*> optgroups();

View File

@ -85,18 +85,17 @@ void SavePresetDialog::Item::init_input_name_ctrl(wxBoxSizer *input_name_sizer,
}
}
wxString SavePresetDialog::Item::get_top_label_text() const
static std::map<Preset::Type, std::string> TOP_LABELS =
{
const std::string label_str = m_use_text_ctrl ?
// TRN %1% = "Preset"
L("Rename %1% to") :
// TRN %1% = "Preset"
L("Save %1% as");
Tab* tab = wxGetApp().get_tab(m_type);
return format_wxstr(_(label_str) + ":", tab->title());
}
// type Save settings
{ Preset::Type::TYPE_PRINT, L("Save print settings as") },
{ Preset::Type::TYPE_SLA_PRINT, L("Save print settings as") },
{ Preset::Type::TYPE_FILAMENT, L("Save filament settings as")},
{ Preset::Type::TYPE_SLA_MATERIAL, L("Save material settings as")},
{ Preset::Type::TYPE_PRINTER, L("Save printer settings as") },
};
SavePresetDialog::Item::Item(Preset::Type type, const std::string& suffix, wxBoxSizer* sizer, SavePresetDialog* parent):
SavePresetDialog::Item::Item(Preset::Type type, const std::string& suffix, wxBoxSizer* sizer, SavePresetDialog* parent, bool is_for_multiple_save):
m_type(type),
m_use_text_ctrl(parent->is_for_rename()),
m_parent(parent),
@ -105,14 +104,15 @@ SavePresetDialog::Item::Item(Preset::Type type, const std::string& suffix, wxBox
{
m_valid_label->SetFont(wxGetApp().bold_font());
wxStaticText* label_top = new wxStaticText(m_parent, wxID_ANY, get_top_label_text());
wxStaticText* label_top = is_for_multiple_save ? new wxStaticText(m_parent, wxID_ANY, _(TOP_LABELS.at(m_type)) + ":") : nullptr;
wxBoxSizer* input_name_sizer = new wxBoxSizer(wxHORIZONTAL);
input_name_sizer->Add(m_valid_bmp, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, BORDER_W);
init_input_name_ctrl(input_name_sizer, get_init_preset_name(suffix));
sizer->Add(label_top, 0, wxEXPAND | wxTOP| wxBOTTOM, BORDER_W);
sizer->Add(input_name_sizer,0, wxEXPAND | wxBOTTOM, BORDER_W);
if (label_top)
sizer->Add(label_top, 0, wxEXPAND | wxTOP| wxBOTTOM, BORDER_W);
sizer->Add(input_name_sizer,0, wxEXPAND | (label_top ? 0 : wxTOP) | wxBOTTOM, BORDER_W);
sizer->Add(m_valid_label, 0, wxEXPAND | wxLEFT, 3*BORDER_W);
if (m_type == Preset::TYPE_PRINTER)
@ -205,8 +205,6 @@ void SavePresetDialog::Item::update()
if ((!m_use_text_ctrl && m_presets->get_edited_preset().is_dirty) ||
(dlg && dlg->get_preset_bundle())) // means that we save modifications from the DiffDialog
info_line = _L("Save preset modifications to existing user profile");
else
info_line = _L("Nothing changed");
m_valid_type = ValidationType::Valid;
}
else {
@ -266,8 +264,8 @@ void SavePresetDialog::Item::update()
void SavePresetDialog::Item::update_valid_bmp()
{
std::string bmp_name = m_valid_type == ValidationType::Warning ? "exclamation" :
m_valid_type == ValidationType::NoValid ? "cross" : "tick_mark" ;
std::string bmp_name = m_valid_type == ValidationType::Warning ? "exclamation_manifold" :
m_valid_type == ValidationType::NoValid ? "exclamation" : "tick_mark" ;
m_valid_bmp->SetBitmap(*get_bmp_bundle(bmp_name));
}
@ -289,22 +287,17 @@ void SavePresetDialog::Item::Enable(bool enable /*= true*/)
// SavePresetDialog
//-----------------------------------------------
SavePresetDialog::SavePresetDialog(wxWindow* parent, Preset::Type type, std::string suffix, bool template_filament)
: DPIDialog(parent, wxID_ANY, _L("Save preset"), wxDefaultPosition, wxSize(45 * wxGetApp().em_unit(), 5 * wxGetApp().em_unit()), wxDEFAULT_DIALOG_STYLE | wxICON_WARNING | wxRESIZE_BORDER)
{
build(std::vector<Preset::Type>{type}, suffix, template_filament);
}
SavePresetDialog::SavePresetDialog(wxWindow* parent, std::vector<Preset::Type> types, std::string suffix, bool template_filament/* =false*/, PresetBundle* preset_bundle/* = nullptr*/)
: DPIDialog(parent, wxID_ANY, _L("Save presets"), wxDefaultPosition, wxSize(45 * wxGetApp().em_unit(), 5 * wxGetApp().em_unit()), wxDEFAULT_DIALOG_STYLE | wxICON_WARNING | wxRESIZE_BORDER),
: DPIDialog(parent, wxID_ANY, types.size() == 1 ? _L("Save preset") : _L("Save presets"),
wxDefaultPosition, wxSize(45 * wxGetApp().em_unit(), 5 * wxGetApp().em_unit()), wxDEFAULT_DIALOG_STYLE | wxICON_WARNING),
m_preset_bundle(preset_bundle)
{
build(types, suffix, template_filament);
}
SavePresetDialog::SavePresetDialog(wxWindow* parent, Preset::Type type, bool rename, const wxString& info_line_extention)
: DPIDialog(parent, wxID_ANY, _L("Rename preset"), wxDefaultPosition, wxSize(45 * wxGetApp().em_unit(), 5 * wxGetApp().em_unit()), wxDEFAULT_DIALOG_STYLE | wxICON_WARNING | wxRESIZE_BORDER),
m_use_for_rename(rename),
SavePresetDialog::SavePresetDialog(wxWindow* parent, Preset::Type type, const wxString& info_line_extention)
: DPIDialog(parent, wxID_ANY, _L("Rename preset"), wxDefaultPosition, wxSize(45 * wxGetApp().em_unit(), 5 * wxGetApp().em_unit()), wxDEFAULT_DIALOG_STYLE | wxICON_WARNING),
m_use_for_rename(true),
m_info_line_extention(info_line_extention)
{
build(std::vector<Preset::Type>{type});
@ -318,11 +311,13 @@ SavePresetDialog::~SavePresetDialog()
void SavePresetDialog::build(std::vector<Preset::Type> types, std::string suffix, bool template_filament)
{
this->SetFont(wxGetApp().normal_font());
#if defined(__WXMSW__)
// ys_FIXME! temporary workaround for correct font scaling
// Because of from wxWidgets 3.1.3 auto rescaling is implemented for the Fonts,
// From the very beginning set dialog font to the wxSYS_DEFAULT_GUI_FONT
this->SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
// this->SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
#else
SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
#endif // __WXMSW__
@ -335,9 +330,9 @@ void SavePresetDialog::build(std::vector<Preset::Type> types, std::string suffix
m_presets_sizer = new wxBoxSizer(wxVERTICAL);
// Add first item
for (Preset::Type type : types)
AddItem(type, suffix);
const bool is_for_multiple_save = types.size() > 1;
for (const Preset::Type& type : types)
AddItem(type, suffix, is_for_multiple_save);
// Add dialog's buttons
wxStdDialogButtonSizer* btns = this->CreateStdDialogButtonSizer(wxOK | wxCANCEL);
@ -367,9 +362,9 @@ void SavePresetDialog::build(std::vector<Preset::Type> types, std::string suffix
#endif
}
void SavePresetDialog::AddItem(Preset::Type type, const std::string& suffix)
void SavePresetDialog::AddItem(Preset::Type type, const std::string& suffix, bool is_for_multiple_save)
{
m_items.emplace_back(new Item{type, suffix, m_presets_sizer, this});
m_items.emplace_back(new Item{type, suffix, m_presets_sizer, this, is_for_multiple_save});
}
std::string SavePresetDialog::get_name()

View File

@ -36,7 +36,7 @@ public:
Warning
};
Item(Preset::Type type, const std::string& suffix, wxBoxSizer* sizer, SavePresetDialog* parent);
Item(Preset::Type type, const std::string& suffix, wxBoxSizer* sizer, SavePresetDialog* parent, bool is_for_multiple_save);
Item(wxWindow* parent, wxBoxSizer* sizer, const std::string& def_name, PrinterTechnology pt = ptFFF);
void update_valid_bmp();
@ -65,7 +65,6 @@ public:
std::string get_init_preset_name(const std::string &suffix);
void init_input_name_ctrl(wxBoxSizer *input_name_sizer, std::string preset_name);
const Preset* get_existing_preset() const ;
wxString get_top_label_text() const ;
void update();
};
@ -89,12 +88,11 @@ public:
const wxString& get_info_line_extention() { return m_info_line_extention; }
SavePresetDialog(wxWindow* parent, Preset::Type type, std::string suffix = "", bool template_filament = false);
SavePresetDialog(wxWindow* parent, std::vector<Preset::Type> types, std::string suffix = "", bool template_filament = false, PresetBundle* preset_bundle = nullptr);
SavePresetDialog(wxWindow* parent, Preset::Type type, bool rename, const wxString& info_line_extention);
SavePresetDialog(wxWindow* parent, Preset::Type type, const wxString& info_line_extention);
~SavePresetDialog() override;
void AddItem(Preset::Type type, const std::string& suffix);
void AddItem(Preset::Type type, const std::string& suffix, bool is_for_multiple_save);
PresetBundle* get_preset_bundle() const { return m_preset_bundle; }
std::string get_name();

View File

@ -265,6 +265,7 @@ void Tab::create_preset_tab()
// tree
m_treectrl = new wxTreeCtrl(panel, wxID_ANY, wxDefaultPosition, wxSize(20 * m_em_unit, -1),
wxTR_NO_BUTTONS | wxTR_HIDE_ROOT | wxTR_SINGLE | wxTR_NO_LINES | wxBORDER_SUNKEN | wxWANTS_CHARS);
m_treectrl->SetFont(wxGetApp().normal_font());
m_left_sizer->Add(m_treectrl, 1, wxEXPAND);
// Index of the last icon inserted into m_treectrl
m_icon_count = -1;
@ -1469,7 +1470,7 @@ void TabPrint::build()
optgroup = page->new_optgroup(L("Reducing printing time"));
category_path = "infill_42#";
optgroup->append_single_option_line("infill_every_layers", category_path + "combine-infill-every-x-layers");
optgroup->append_single_option_line("infill_only_where_needed", category_path + "only-infill-where-needed");
// optgroup->append_single_option_line("infill_only_where_needed", category_path + "only-infill-where-needed");
optgroup = page->new_optgroup(L("Advanced"));
optgroup->append_single_option_line("solid_infill_every_layers", category_path + "solid-infill-every-x-layers");
@ -3820,7 +3821,7 @@ void Tab::save_preset(std::string name /*= ""*/, bool detach)
}
if (name.empty()) {
SavePresetDialog dlg(m_parent, m_type, detach ? _u8L("Detached") : "", from_template);
SavePresetDialog dlg(m_parent, { m_type }, detach ? _u8L("Detached") : "", from_template);
if (dlg.ShowModal() != wxID_OK)
return;
name = dlg.get_name();
@ -3931,7 +3932,7 @@ void Tab::rename_preset()
// get new name
SavePresetDialog dlg(m_parent, m_type, true, msg);
SavePresetDialog dlg(m_parent, m_type, msg);
if (dlg.ShowModal() != wxID_OK)
return;

View File

@ -837,7 +837,8 @@ void UnsavedChangesDialog::build(Preset::Type type, PresetCollection* dependent_
// ys_FIXME! temporary workaround for correct font scaling
// Because of from wxWidgets 3.1.3 auto rescaling is implemented for the Fonts,
// From the very beginning set dialog font to the wxSYS_DEFAULT_GUI_FONT
this->SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
// this->SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
this->SetFont(wxGetApp().normal_font());
#endif // __WXMSW__
int border = 10;
@ -850,7 +851,8 @@ void UnsavedChangesDialog::build(Preset::Type type, PresetCollection* dependent_
add_new_value_column = false;
m_action_line = new wxStaticText(this, wxID_ANY, "");
m_action_line->SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Bold());
// m_action_line->SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Bold());
m_action_line->SetFont(wxGetApp().bold_font());
m_tree = new DiffViewCtrl(this, wxSize(em * (add_new_value_column ? 80 : 60), em * 30));
m_tree->AppendToggleColumn_(L"\u2714" , DiffModel::colToggle, wxLinux ? 9 : 6);
@ -910,7 +912,8 @@ void UnsavedChangesDialog::build(Preset::Type type, PresetCollection* dependent_
cancel_btn->Bind(wxEVT_BUTTON, [this](wxEvent&) { this->EndModal(wxID_CANCEL); });
m_info_line = new wxStaticText(this, wxID_ANY, "");
m_info_line->SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Bold());
// m_info_line->SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Bold());
m_info_line->SetFont(wxGetApp().bold_font());
m_info_line->Hide();
if (!m_app_config_key.empty()) {
@ -1012,7 +1015,7 @@ bool UnsavedChangesDialog::save(PresetCollection* dependent_presets, bool show_s
// for system/default/external presets we should take an edited name
if (preset.is_system || preset.is_default || preset.is_external) {
SavePresetDialog save_dlg(this, preset.type);
SavePresetDialog save_dlg(this, { preset.type });
if (save_dlg.ShowModal() != wxID_OK) {
m_exit_action = Action::Discard;
return false;
@ -1362,6 +1365,7 @@ FullCompareDialog::FullCompareDialog(const wxString& option_name, const wxString
: wxDialog(nullptr, wxID_ANY, option_name, wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
{
wxGetApp().UpdateDarkUI(this);
this->SetFont(wxGetApp().normal_font());
int border = 10;
bool has_new_value_column = !new_value_header.IsEmpty();
@ -1528,7 +1532,8 @@ void DiffPresetDialog::create_show_all_presets_chb()
void DiffPresetDialog::create_info_lines()
{
const wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Bold();
// const wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Bold();
const wxFont font = GetFont().Bold();
m_top_info_line = new wxStaticText(this, wxID_ANY, _L("Select presets to compare"));
m_top_info_line->SetFont(font);
@ -1668,7 +1673,8 @@ DiffPresetDialog::DiffPresetDialog(MainFrame* mainframe)
// ys_FIXME! temporary workaround for correct font scaling
// Because of from wxWidgets 3.1.3 auto rescaling is implemented for the Fonts,
// From the very beginning set dialog font to the wxSYS_DEFAULT_GUI_FONT
this->SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
// this->SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
this->SetFont(mainframe->normal_font());
#endif // __WXMSW__
// Init bundles

View File

@ -512,11 +512,8 @@ std::string OctoPrint::make_url(const std::string &path) const
}
}
SL1Host::SL1Host(DynamicPrintConfig *config) :
OctoPrint(config),
m_authorization_type(dynamic_cast<const ConfigOptionEnum<AuthorizationType>*>(config->option("printhost_authorization_type"))->value),
m_username(config->opt_string("printhost_user")),
m_password(config->opt_string("printhost_password"))
SL1Host::SL1Host(DynamicPrintConfig *config)
: PrusaLink(config)
{
}
@ -538,22 +535,6 @@ bool SL1Host::validate_version_text(const boost::optional<std::string> &version_
return version_text ? boost::starts_with(*version_text, "Prusa SLA") : false;
}
void SL1Host::set_auth(Http &http) const
{
switch (m_authorization_type) {
case atKeyPassword:
http.header("X-Api-Key", get_apikey());
break;
case atUserPassword:
http.auth_digest(m_username, m_password);
break;
}
if (! get_cafile().empty()) {
http.ca_file(get_cafile());
}
}
// PrusaLink
PrusaLink::PrusaLink(DynamicPrintConfig* config, bool show_after_message) :
OctoPrint(config),

View File

@ -55,30 +55,6 @@ private:
#endif
};
class SL1Host: public OctoPrint
{
public:
SL1Host(DynamicPrintConfig *config);
~SL1Host() override = default;
const char* get_name() const override;
wxString get_test_ok_msg() const override;
wxString get_test_failed_msg(wxString &msg) const override;
PrintHostPostUploadActions get_post_upload_actions() const override { return {}; }
protected:
bool validate_version_text(const boost::optional<std::string> &version_text) const override;
private:
void set_auth(Http &http) const override;
// Host authorization type.
AuthorizationType m_authorization_type;
// username and password for HTTP Digest Authentization (RFC RFC2617)
std::string m_username;
std::string m_password;
};
class PrusaLink : public OctoPrint
{
@ -106,6 +82,12 @@ protected:
bool upload_inner_with_resolved_ip(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn, const boost::asio::ip::address& resolved_addr) const override;
#endif
// Host authorization type.
AuthorizationType m_authorization_type;
// username and password for HTTP Digest Authentization (RFC RFC2617)
std::string m_username;
std::string m_password;
private:
bool test_with_method_check(wxString& curl_msg, bool& use_put) const;
bool put_inner(PrintHostUpload upload_data, std::string url, const std::string& name, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const;
@ -113,11 +95,7 @@ private:
#ifdef WIN32
bool test_with_resolved_ip_and_method_check(wxString& curl_msg, bool& use_put) const;
#endif
// Host authorization type.
AuthorizationType m_authorization_type;
// username and password for HTTP Digest Authentization (RFC RFC2617)
std::string m_username;
std::string m_password;
bool m_show_after_message;
#if 0
@ -149,6 +127,22 @@ public:
const char* get_name() const override { return "Mainsail/Fluidd"; }
};
class SL1Host : public PrusaLink
{
public:
SL1Host(DynamicPrintConfig* config);
~SL1Host() override = default;
const char* get_name() const override;
wxString get_test_ok_msg() const override;
wxString get_test_failed_msg(wxString& msg) const override;
PrintHostPostUploadActions get_post_upload_actions() const override { return {}; }
protected:
bool validate_version_text(const boost::optional<std::string>& version_text) const override;
};
}
#endif

View File

@ -252,85 +252,85 @@ SCENARIO("Infill does not exceed perimeters", "[Fill]")
GIVEN("Concentric") { test("concentric"sv); }
}
SCENARIO("Infill only where needed", "[Fill]")
{
DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
config.set_deserialize_strict({
{ "nozzle_diameter", "0.4, 0.4, 0.4, 0.4" },
{ "infill_only_where_needed", true },
{ "bottom_solid_layers", 0 },
{ "infill_extruder", 2 },
{ "infill_extrusion_width", 0.5 },
{ "wipe_into_infill", false },
{ "fill_density", 0.4 },
// for preventing speeds from being altered
{ "cooling", "0, 0, 0, 0" },
// for preventing speeds from being altered
{ "first_layer_speed", "100%" }
});
// SCENARIO("Infill only where needed", "[Fill]")
// {
// DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
// config.set_deserialize_strict({
// { "nozzle_diameter", "0.4, 0.4, 0.4, 0.4" },
// { "infill_only_where_needed", true },
// { "bottom_solid_layers", 0 },
// { "infill_extruder", 2 },
// { "infill_extrusion_width", 0.5 },
// { "wipe_into_infill", false },
// { "fill_density", 0.4 },
// // for preventing speeds from being altered
// { "cooling", "0, 0, 0, 0" },
// // for preventing speeds from being altered
// { "first_layer_speed", "100%" }
// });
auto test = [&config]() -> double {
TriangleMesh pyramid = Test::mesh(Slic3r::Test::TestMesh::pyramid);
// Arachne doesn't use "Detect thin walls," and because of this, it filters out tiny infill areas differently.
// So, for Arachne, we cut the pyramid model to achieve similar results.
if (config.opt_enum<PerimeterGeneratorType>("perimeter_generator") == Slic3r::PerimeterGeneratorType::Arachne) {
indexed_triangle_set lower{};
cut_mesh(pyramid.its, 35, nullptr, &lower);
pyramid = TriangleMesh(lower);
}
std::string gcode = Slic3r::Test::slice({ pyramid }, config);
THEN("gcode not empty") {
REQUIRE(! gcode.empty());
}
// auto test = [&config]() -> double {
// TriangleMesh pyramid = Test::mesh(Slic3r::Test::TestMesh::pyramid);
// // Arachne doesn't use "Detect thin walls," and because of this, it filters out tiny infill areas differently.
// // So, for Arachne, we cut the pyramid model to achieve similar results.
// if (config.opt_enum<PerimeterGeneratorType>("perimeter_generator") == Slic3r::PerimeterGeneratorType::Arachne) {
// indexed_triangle_set lower{};
// cut_mesh(pyramid.its, 35, nullptr, &lower);
// pyramid = TriangleMesh(lower);
// }
// std::string gcode = Slic3r::Test::slice({ pyramid }, config);
// THEN("gcode not empty") {
// REQUIRE(! gcode.empty());
// }
GCodeReader parser;
int tool = -1;
const int infill_extruder = config.opt_int("infill_extruder");
Points infill_points;
parser.parse_buffer(gcode, [&tool, &infill_points, infill_extruder](Slic3r::GCodeReader &self, const Slic3r::GCodeReader::GCodeLine &line)
{
// if the command is a T command, set the the current tool
if (boost::starts_with(line.cmd(), "T")) {
tool = atoi(line.cmd().data() + 1) + 1;
} else if (line.cmd() == "G1" && line.extruding(self) && line.dist_XY(self) > 0) {
if (tool == infill_extruder) {
infill_points.emplace_back(self.xy_scaled());
infill_points.emplace_back(line.new_XY_scaled(self));
}
}
});
// prevent calling convex_hull() with no points
THEN("infill not empty") {
REQUIRE(! infill_points.empty());
}
// GCodeReader parser;
// int tool = -1;
// const int infill_extruder = config.opt_int("infill_extruder");
// Points infill_points;
// parser.parse_buffer(gcode, [&tool, &infill_points, infill_extruder](Slic3r::GCodeReader &self, const Slic3r::GCodeReader::GCodeLine &line)
// {
// // if the command is a T command, set the the current tool
// if (boost::starts_with(line.cmd(), "T")) {
// tool = atoi(line.cmd().data() + 1) + 1;
// } else if (line.cmd() == "G1" && line.extruding(self) && line.dist_XY(self) > 0) {
// if (tool == infill_extruder) {
// infill_points.emplace_back(self.xy_scaled());
// infill_points.emplace_back(line.new_XY_scaled(self));
// }
// }
// });
// // prevent calling convex_hull() with no points
// THEN("infill not empty") {
// REQUIRE(! infill_points.empty());
// }
auto opt_width = config.opt<ConfigOptionFloatOrPercent>("infill_extrusion_width");
REQUIRE(! opt_width->percent);
Polygons convex_hull = expand(Geometry::convex_hull(infill_points), scaled<float>(opt_width->value / 2));
return SCALING_FACTOR * SCALING_FACTOR * std::accumulate(convex_hull.begin(), convex_hull.end(), 0., [](double acc, const Polygon &poly){ return acc + poly.area(); });
};
// auto opt_width = config.opt<ConfigOptionFloatOrPercent>("infill_extrusion_width");
// REQUIRE(! opt_width->percent);
// Polygons convex_hull = expand(Geometry::convex_hull(infill_points), scaled<float>(opt_width->value / 2));
// return SCALING_FACTOR * SCALING_FACTOR * std::accumulate(convex_hull.begin(), convex_hull.end(), 0., [](double acc, const Polygon &poly){ return acc + poly.area(); });
// };
double tolerance = 5; // mm^2
// double tolerance = 5; // mm^2
// GIVEN("solid_infill_below_area == 0") {
// config.opt_float("solid_infill_below_area") = 0;
// WHEN("pyramid is sliced ") {
// auto area = test();
// THEN("no infill is generated when using infill_only_where_needed on a pyramid") {
// REQUIRE(area < tolerance);
// }
// }
// }
// GIVEN("solid_infill_below_area == 70") {
// config.opt_float("solid_infill_below_area") = 70;
// WHEN("pyramid is sliced ") {
// auto area = test();
// THEN("infill is only generated under the forced solid shells") {
// REQUIRE(std::abs(area - 70) < tolerance);
// }
// }
// }
}
// // GIVEN("solid_infill_below_area == 0") {
// // config.opt_float("solid_infill_below_area") = 0;
// // WHEN("pyramid is sliced ") {
// // auto area = test();
// // THEN("no infill is generated when using infill_only_where_needed on a pyramid") {
// // REQUIRE(area < tolerance);
// // }
// // }
// // }
// // GIVEN("solid_infill_below_area == 70") {
// // config.opt_float("solid_infill_below_area") = 70;
// // WHEN("pyramid is sliced ") {
// // auto area = test();
// // THEN("infill is only generated under the forced solid shells") {
// // REQUIRE(std::abs(area - 70) < tolerance);
// // }
// // }
// // }
// }
SCENARIO("Combine infill", "[Fill]")
{

View File

@ -245,10 +245,41 @@ SCENARIO("Placeholder parser variables", "[PlaceholderParser]") {
"{size(myfloats)}";
REQUIRE(parser.process(script, 0, nullptr, nullptr, nullptr) == "7");
}
SECTION("nested if with new variables 2, mixing }{ with ;") {
std::string script =
"{if 1 == 0 then local myints = (5, 4, 3, 2, 1);else;local myfloats = (1., 2., 3., 4., 5., 6., 7.);endif}"
"{size(myfloats)}";
REQUIRE(parser.process(script, 0, nullptr, nullptr, nullptr) == "7");
}
SECTION("nested if with new variables, two level") {
std::string script =
"{if 1 == 1}{if 2 == 3}{nejaka / haluz}{else}{local myints = (6, 5, 4, 3, 2, 1)}{endif}{else}{if zase * haluz}{else}{local myfloats = (1., 2., 3., 4., 5., 6., 7.)}{endif}{endif}"
"{size(myints)}";
REQUIRE(parser.process(script, 0, nullptr, nullptr, nullptr) == "6");
}
SECTION("if with empty block and ;") {
std::string script =
"{if false then else;local myfloats = (1., 2., 3., 4., 5., 6., 7.);endif}"
"{size(myfloats)}";
REQUIRE(parser.process(script, 0, nullptr, nullptr, nullptr) == "7");
}
SECTION("nested if with new variables, two level, mixing }{ with ;") {
std::string script =
"{if 1 == 1 then if 2 == 3}nejaka / haluz{else local myints = (6, 5, 4, 3, 2, 1) endif else if zase * haluz then else local myfloats = (1., 2., 3., 4., 5., 6., 7.) endif endif}"
"{size(myints)}";
REQUIRE(parser.process(script, 0, nullptr, nullptr, nullptr) == "6");
}
SECTION("nested if with new variables, two level, mixing }{ with ; 2") {
std::string script =
"{if 1 == 1 then if 2 == 3 then nejaka / haluz else}{local myints = (6, 5, 4, 3, 2, 1)}{endif else if zase * haluz then else local myfloats = (1., 2., 3., 4., 5., 6., 7.) endif endif}"
"{size(myints)}";
REQUIRE(parser.process(script, 0, nullptr, nullptr, nullptr) == "6");
}
SECTION("nested if with new variables, two level, mixing }{ with ; 3") {
std::string script =
"{if 1 == 1 then if 2 == 3 then nejaka / haluz else}{local myints = (6, 5, 4, 3, 2, 1)}{endif else}{if zase * haluz}{else local myfloats = (1., 2., 3., 4., 5., 6., 7.) endif}{endif}"
"{size(myints)}";
REQUIRE(parser.process(script, 0, nullptr, nullptr, nullptr) == "6");
}
SECTION("if else completely empty") { REQUIRE(parser.process("{if false then elsif false then else endif}", 0, nullptr, nullptr, nullptr) == ""); }
}