mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-01 10:42:03 +08:00
Merge branch 'master' into fs_undoredo
This commit is contained in:
commit
cc9a3a473e
@ -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
|
||||
{
|
||||
|
@ -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; }
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include "ExtrusionRole.hpp"
|
||||
#include "I18N.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
@ -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.
|
||||
|
@ -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",
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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.
|
||||
|
@ -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",
|
||||
|
@ -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)
|
||||
|
@ -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))
|
||||
|
@ -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()
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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:
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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':
|
||||
|
@ -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)
|
||||
|
@ -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));
|
||||
|
@ -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());
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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) {
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include <GL/glew.h>
|
||||
|
||||
#include <memory>
|
||||
#include <wx/string.h>
|
||||
|
||||
|
||||
namespace Slic3r::GUI {
|
||||
|
@ -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 });
|
||||
|
@ -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)
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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()
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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),
|
||||
|
@ -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
|
||||
|
@ -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]")
|
||||
{
|
||||
|
@ -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) == ""); }
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user