Merge branch 'main' into zaa

This commit is contained in:
Aleksandr Dobkin committed 2026-03-11 01:04:44 -07:00
commit b945c957d8
838 files changed
+81546 -33829

No files matched your search

+12
View File
@@ -88,6 +88,7 @@ using namespace nlohmann;
#ifdef __WXGTK__
#include <X11/Xlib.h>
#include <unistd.h>
#endif
#ifdef SLIC3R_GUI
@@ -1191,6 +1192,17 @@ int CLI::run(int argc, char **argv)
// mode forces software rendering, which works reliably on all backends.
::setenv("WEBKIT_DISABLE_COMPOSITING_MODE", "1", /* replace */ false);
// On Linux dual-GPU systems, request the high-performance discrete GPU.
// DRI_PRIME=1 handles AMD and nouveau (open-source NVIDIA) PRIME setups.
::setenv("DRI_PRIME", "1", /* replace */ false);
// For NVIDIA proprietary driver PRIME render offload, set additional variables.
// Only set if the NVIDIA kernel module is loaded to avoid breaking systems without NVIDIA.
if (::access("/proc/driver/nvidia/version", F_OK) == 0) {
::setenv("__NV_PRIME_RENDER_OFFLOAD", "1", /* replace */ false);
::setenv("__GLX_VENDOR_LIBRARY_NAME", "nvidia", /* replace */ false);
}
// Also on Linux, we need to tell Xlib that we will be using threads,
// lest we crash when we fire up GStreamer.
XInitThreads();
+3 -2
View File
@@ -257,8 +257,9 @@ SplittedLine do_split_line(const ClipperZUtils::ZPath& path, const ExPolygons& c
idx++;
} else {
if (!is_src(node.front()->front())) {
const auto& last = result.back();
if (result.empty() || last.get_src_index() != to_src_idx(p)) {
if (result.empty() || result.back().get_src_index() != to_src_idx(p)) {
//const auto& last = result.back();
//if (result.empty() || last.get_src_index() != to_src_idx(p)) {
result.emplace_back(to_point(p), false, idx);
}
}
+6
View File
@@ -196,6 +196,9 @@ void AppConfig::set_defaults()
if (get("seq_top_layer_only").empty())
set("seq_top_layer_only", "1");
if (get("filaments_area_preferred_count").empty())
set("filaments_area_preferred_count", "10");
if (get("use_perspective_camera").empty())
set_bool("use_perspective_camera", true);
@@ -248,6 +251,9 @@ void AppConfig::set_defaults()
if (get("show_3d_navigator").empty())
set_bool("show_3d_navigator", true);
if (get("show_plate_gridlines").empty())
set_bool("show_plate_gridlines", true);
if (get("show_outline").empty())
set_bool("show_outline", false);
+6 -1
View File
@@ -29,7 +29,12 @@ using namespace nlohmann;
#define SETTING_NETWORK_PLUGIN_UPDATE_DISABLED "network_plugin_update_prompts_disabled"
#define SETTING_NETWORK_PLUGIN_REMIND_LATER "network_plugin_remind_later"
#define SETTING_USE_ENCRYPTED_TOKEN_FILE "use_encrypted_token_file"
#define BAMBU_NETWORK_AGENT_VERSION_LEGACY "01.10.01.01"
#if defined(_WIN32) || defined(_WIN64)
#define BAMBU_NETWORK_AGENT_VERSION_LEGACY "01.10.01.09"
#else
#define BAMBU_NETWORK_AGENT_VERSION_LEGACY "01.10.01.01"
#endif
#define SUPPORT_DARK_MODE
//#define _MSW_DARK_MODE
+2
View File
@@ -450,6 +450,8 @@ set(lisbslic3r_sources
Timer.hpp
TriangleMesh.cpp
TriangleMesh.hpp
TriangleMeshDeal.cpp
TriangleMeshDeal.hpp
TriangleMeshSlicer.cpp
TriangleMeshSlicer.hpp
TriangleSelector.cpp
+18
View File
@@ -318,6 +318,24 @@ ConfigOption* ConfigOptionDef::create_default_option() const
return this->create_empty_option();
}
bool ConfigOptionDef::is_value_valid(const double value, const int max_precision /*= 4*/) const
{
// Special handling for the nil values
// The nil value is a valid one only for nullable options
if (std::isnan(value))
return this->nullable;
// Special handling of 0
if (this->min == 0.f && value < 0)
return false;
const double ep = std::pow(0.1, max_precision);
if (is_approx(value, (double) this->min, ep) || is_approx(value, (double) this->max, ep))
return true;
return this->min <= value && value <= this->max;
}
// Assignment of the serialization IDs is not thread safe. The Defs shall be initialized from the main thread!
ConfigOptionDef* ConfigDef::add(const t_config_option_key &opt_key, ConfigOptionType type)
{
+22 -12
View File
@@ -4,6 +4,7 @@
#include <assert.h>
#include <map>
#include <climits>
#include <cfloat>
#include <cstdio>
#include <cstdlib>
#include <functional>
@@ -36,9 +37,9 @@ namespace Slic3r {
template<class Archive> void serialize(Archive& ar) { ar(this->value); ar(this->percent); }
};
inline bool operator==(const FloatOrPercent& l, const FloatOrPercent& r) throw() { return l.value == r.value && l.percent == r.percent; }
inline bool operator==(const FloatOrPercent& l, const FloatOrPercent& r) throw() { return is_approx(l.value, r.value) && l.percent == r.percent; }
inline bool operator!=(const FloatOrPercent& l, const FloatOrPercent& r) throw() { return !(l == r); }
inline bool operator< (const FloatOrPercent& l, const FloatOrPercent& r) throw() { return l.value < r.value || (l.value == r.value && int(l.percent) < int(r.percent)); }
inline bool operator< (const FloatOrPercent& l, const FloatOrPercent& r) throw() { return l.value < r.value || (is_approx(l.value, r.value) && int(l.percent) < int(r.percent)); }
}
namespace std {
@@ -761,8 +762,8 @@ public:
ConfigOptionType type() const override { return static_type(); }
double getFloat() const override { return this->value; }
ConfigOption* clone() const override { return new ConfigOptionFloat(*this); }
bool operator==(const ConfigOptionFloat &rhs) const throw() { return this->value == rhs.value; }
bool operator< (const ConfigOptionFloat &rhs) const throw() { return this->value < rhs.value; }
bool operator==(const ConfigOptionFloat &rhs) const throw() { return is_approx(this->value, rhs.value); }
bool operator< (const ConfigOptionFloat &rhs) const throw() { return this->value < rhs.value; }
std::string serialize() const override
{
@@ -779,6 +780,14 @@ public:
return !iss.fail();
}
bool operator==(const ConfigOption &rhs) const override
{
if (rhs.type() != this->type())
throw ConfigurationError("ConfigOptionFloat: Comparing incompatible types");
assert(dynamic_cast<const ConfigOptionFloat*>(&rhs));
return *this == *static_cast<const ConfigOptionFloat*>(&rhs);
}
ConfigOptionFloat& operator=(const ConfigOption *opt)
{
this->set(opt);
@@ -905,7 +914,7 @@ protected:
if (v1.size() != v2.size())
return false;
for (auto it1 = v1.begin(), it2 = v2.begin(); it1 != v1.end(); ++ it1, ++ it2)
if (! ((std::isnan(*it1) && std::isnan(*it2)) || *it1 == *it2))
if (! ((std::isnan(*it1) && std::isnan(*it2)) || is_approx(*it1, *it2)))
return false;
return true;
} else
@@ -1257,11 +1266,11 @@ public:
return *this == *static_cast<const ConfigOptionFloatOrPercent*>(&rhs);
}
bool operator==(const ConfigOptionFloatOrPercent &rhs) const throw()
{ return this->value == rhs.value && this->percent == rhs.percent; }
{ return is_approx(this->value, rhs.value) && this->percent == rhs.percent; }
size_t hash() const throw() override
{ size_t seed = std::hash<double>{}(this->value); return this->percent ? seed ^ 0x9e3779b9 : seed; }
bool operator< (const ConfigOptionFloatOrPercent &rhs) const throw()
{ return this->value < rhs.value || (this->value == rhs.value && int(this->percent) < int(rhs.percent)); }
{ return this->value < rhs.value || (is_approx(this->value, rhs.value) && int(this->percent) < int(rhs.percent)); }
double get_abs_value(double ratio_over) const
{ return this->percent ? (ratio_over * this->value / 100) : this->value; }
@@ -2085,11 +2094,11 @@ class ConfigOptionEnumsGenericTempl : public ConfigOptionInts
public:
ConfigOptionEnumsGenericTempl(const t_config_enum_values *keys_map = nullptr) : keys_map(keys_map) {}
explicit ConfigOptionEnumsGenericTempl(const t_config_enum_values *keys_map, size_t size, int value) : ConfigOptionInts(size, value), keys_map(keys_map) {}
explicit ConfigOptionEnumsGenericTempl(std::initializer_list<int> il) : ConfigOptionInts(std::move(il)), keys_map(keys_map) {}
explicit ConfigOptionEnumsGenericTempl(std::initializer_list<int> il) : ConfigOptionInts(std::move(il)) {}
explicit ConfigOptionEnumsGenericTempl(const std::vector<int> &vec) : ConfigOptionInts(vec) {}
explicit ConfigOptionEnumsGenericTempl(std::vector<int> &&vec) : ConfigOptionInts(std::move(vec)) {}
const t_config_enum_values* keys_map = nullptr;
const t_config_enum_values* keys_map { nullptr };
static ConfigOptionType static_type() { return coEnums; }
ConfigOptionType type() const override { return static_type(); }
@@ -2455,10 +2464,11 @@ public:
// Optional width of an input field.
int width = -1;
// <min, max> limit of a numeric input.
// If not set, the <min, max> is set to <INT_MIN, INT_MAX>
// If not set, the <min, max> is set to <-FLT_MAX, FLT_MAX>
// By setting min=0, only nonnegative input is allowed.
int min = INT_MIN;
int max = INT_MAX;
float min = -FLT_MAX;
float max = FLT_MAX;
bool is_value_valid(const double value, const int max_precision = 4) const;
// To check if it's not a typo and a % is missing
double max_literal = 1;
ConfigOptionMode mode = comSimple;
+77 -14
View File
@@ -111,7 +111,7 @@ void fuzzy_polyline(Points& poly, bool closed, coordf_t slice_z, const FuzzySkin
}
// Thanks Cura developers for this function.
void fuzzy_extrusion_line(Arachne::ExtrusionJunctions& ext_lines, coordf_t slice_z, const FuzzySkinConfig& cfg)
void fuzzy_extrusion_line(Arachne::ExtrusionJunctions& ext_lines, coordf_t slice_z, const FuzzySkinConfig& cfg, bool closed)
{
std::unique_ptr<noise::module::Module> noise = get_noise_module(cfg);
@@ -124,9 +124,12 @@ void fuzzy_extrusion_line(Arachne::ExtrusionJunctions& ext_lines, coordf_t slice
Arachne::ExtrusionJunctions out;
out.reserve(ext_lines.size());
for (auto& p1 : ext_lines) {
if (p0->p == p1.p) { // Connect endpoints.
out.emplace_back(p1.p, p1.w, p1.perimeter_index);
continue;
// Orca: only skip the first point for closed path, open path should not skip any point
if (closed) {
if (p0->p == p1.p) { // Connect endpoints.
out.emplace_back(p1.p, p1.w, p1.perimeter_index);
continue;
}
}
// 'a' is the (next) new point between p0 and p1
@@ -266,11 +269,11 @@ Polygon apply_fuzzy_skin(const Polygon& polygon, const PerimeterGenerator& perim
BoundingBox bbox = get_extents(perimeter_generator.slices->surfaces);
bbox.offset(scale_(1.));
::Slic3r::SVG svg(debug_out_path("fuzzy_traverse_loops_%d_%d_%d_region_%d.svg", perimeter_generator.layer_id,
loop.is_contour ? 0 : 1, loop.depth, i)
is_contour ? 0 : 1, loop_idx, i)
.c_str(),
bbox);
svg.draw_outline(perimeter_generator.slices->surfaces);
svg.draw_outline(loop.polygon, "green");
svg.draw_outline(polygon, "green");
svg.draw(r.second, "red", 0.5);
svg.draw_outline(r.second, "red");
svg.Close();
@@ -298,11 +301,21 @@ Polygon apply_fuzzy_skin(const Polygon& polygon, const PerimeterGenerator& perim
fuzzified.points.clear();
const auto fuzzy_current_segment = [&segment, &fuzzified, &r, slice_z]() {
fuzzified.points.push_back(segment.front());
const auto back = segment.back();
// Orca: non fuzzy points to isolate fuzzy region
const auto front = segment.front();
const auto back = segment.back();
fuzzy_polyline(segment, false, slice_z, r.first);
//Orca: only add non fuzzy point if it's not in the polygon closing point.
if (!fuzzified.points.empty()
&& fuzzified.points.back() != front) {
fuzzified.points.push_back(front);
}
fuzzified.points.insert(fuzzified.points.end(), segment.begin(), segment.end());
fuzzified.points.push_back(back);
//Orca: only add non fuzzy point if it's not in the polygon closing point.
if (!fuzzified.points.empty() && fuzzified.points.back() != front) {
fuzzified.points.push_back(back);
}
segment.clear();
};
@@ -325,7 +338,12 @@ Polygon apply_fuzzy_skin(const Polygon& polygon, const PerimeterGenerator& perim
}
}
}
// Orca: ensure the loop is closed after fuzzification
if (!fuzzified.points.empty() && fuzzified.points.front() != fuzzified.points.back()) {
fuzzified.points.back() = fuzzified.points.front();
}
return fuzzified;
}
@@ -348,6 +366,35 @@ void apply_fuzzy_skin(Arachne::ExtrusionLine* extrusion, const PerimeterGenerato
}
}
if (!fuzzified_regions.empty()) {
#ifdef DEBUG_FUZZY
{
int i = 0;
for (const auto& r : fuzzified_regions) {
BoundingBox bbox = get_extents(perimeter_generator.slices->surfaces);
bbox.offset(scale_(1.));
::Slic3r::SVG svg(debug_out_path("fuzzy_traverse_loops_%d_%d_%d_region_%d.svg", perimeter_generator.layer_id,
is_contour ? 0 : 1, extrusion->inset_idx, i)
.c_str(),
bbox);
// Convert extrusion line to polygon for visualization
Polygon extrusion_polygon;
extrusion_polygon.points.reserve(extrusion->junctions.size());
for (const auto& junction : extrusion->junctions) {
extrusion_polygon.points.push_back(junction.p);
}
svg.draw_outline(perimeter_generator.slices->surfaces);
svg.draw_outline(extrusion_polygon, "green");
svg.draw(r.second, "red", 0.5);
svg.draw_outline(r.second, "red");
svg.Close();
i++;
}
}
#endif
// Split the loops into lines with different config, and fuzzy them separately
for (const auto& r : fuzzified_regions) {
const auto splitted = Algorithm::split_line(*extrusion, r.second, false);
@@ -360,6 +407,7 @@ void apply_fuzzy_skin(Arachne::ExtrusionLine* extrusion, const PerimeterGenerato
if (std::all_of(splitted.begin(), splitted.end(), [](const Algorithm::SplitLineJunction& j) { return j.clipped; })) {
// The entire polygon is fuzzified
fuzzy_extrusion_line(extrusion->junctions, slice_z, r.first);
continue;
} else {
const auto current_ext = extrusion->junctions;
std::vector<Arachne::ExtrusionJunction> segment;
@@ -367,11 +415,20 @@ void apply_fuzzy_skin(Arachne::ExtrusionLine* extrusion, const PerimeterGenerato
extrusion->junctions.clear();
const auto fuzzy_current_segment = [&segment, &extrusion, &r, slice_z]() {
extrusion->junctions.push_back(segment.front());
const auto back = segment.back();
fuzzy_extrusion_line(segment, slice_z, r.first);
// Orca: non fuzzy points to isolate fuzzy region
const auto front = segment.front();
const auto back = segment.back();
fuzzy_extrusion_line(segment, slice_z, r.first, false);
// Orca: only add non fuzzy point if it's not in the extrusion closing point.
if (!extrusion->junctions.empty() && extrusion->junctions.front().p != front.p) {
extrusion->junctions.push_back(front);
}
extrusion->junctions.insert(extrusion->junctions.end(), segment.begin(), segment.end());
extrusion->junctions.push_back(back);
// Orca: only add non fuzzy point if it's not in the extrusion closing point.
if (!extrusion->junctions.empty() && extrusion->junctions.back().p != front.p) {
extrusion->junctions.push_back(back);
}
segment.clear();
};
@@ -398,6 +455,12 @@ void apply_fuzzy_skin(Arachne::ExtrusionLine* extrusion, const PerimeterGenerato
if (!segment.empty()) {
fuzzy_current_segment();
}
//Orca: ensure the loop is closed after fuzzy
if (!extrusion->junctions.empty() && extrusion->junctions.front().p != extrusion->junctions.back().p) {
extrusion->junctions.back().p = extrusion->junctions.front().p;
extrusion->junctions.back().w = extrusion->junctions.front().w;
}
}
}
}
@@ -9,7 +9,7 @@ namespace Slic3r::Feature::FuzzySkin {
void fuzzy_polyline(Points& poly, bool closed, coordf_t slice_z, const FuzzySkinConfig& cfg);
void fuzzy_extrusion_line(Arachne::ExtrusionJunctions& ext_lines, coordf_t slice_z, const FuzzySkinConfig& cfg);
void fuzzy_extrusion_line(Arachne::ExtrusionJunctions& ext_lines, coordf_t slice_z, const FuzzySkinConfig& cfg, bool closed = true);
void group_region_by_fuzzify(PerimeterGenerator& g);
+1 -1
View File
@@ -167,7 +167,7 @@ double calculate_infill_rotation_angle(const PrintObject* object,
idx = std::min(idx, (int) object->layers().size() - 1);
limit_fill_z = object->get_layer(idx)->print_z + sdx * object->config().layer_height;
}
repeats = std::max(--repeats, 0);
repeats = std::max(repeats - 1, 0);
} else
_noop = true; // set the dumb cycle
if (_absolute) { // is absolute
+18 -10
View File
@@ -3021,24 +3021,25 @@ bool FillRectilinear::fill_surface_by_multilines(const Surface *surface, FillPar
multiline_fill(fill_lines, params, spacing);
// Contract surface polygon by half line width to avoid excesive overlap with perimeter
ExPolygons contracted = offset_ex(surface->expolygon, -float(scale_(0.5 * this->spacing)));
// if contraction results in empty polygon, use original surface
const ExPolygon &intersection_surface = contracted.empty() ? surface->expolygon : contracted.front();
const ExPolygons contracted = offset_ex(surface->expolygon, -float(scale_(0.5 * this->spacing)));
// if contraction results in empty ExPolygons, use original surface
const ExPolygons& intersection_surface = contracted.empty() ? ExPolygons{surface->expolygon} : contracted;
// Intersect polylines with perimeter
fill_lines = intersection_pl(std::move(fill_lines), intersection_surface);
if ((params.pattern == ipLateralLattice || params.pattern == ipLateralHoneycomb ) && params.multiline >1 )
remove_overlapped(fill_lines, line_width);
if ((params.pattern == ipLateralLattice || params.pattern == ipLateralHoneycomb) && params.multiline > 1)
remove_overlapped(fill_lines, line_width);
if (!fill_lines.empty()) {
if (params.dont_connect()) {
if (params.dont_connect()) {
if (fill_lines.size() > 1)
fill_lines = chain_polylines(std::move(fill_lines));
append(polylines_out, std::move(fill_lines));
} else
connect_infill(std::move(fill_lines), intersection_surface, polylines_out, this->spacing, params);
connect_infill(std::move(fill_lines), to_polygons(intersection_surface), get_extents(surface->expolygon.contour), polylines_out,
this->spacing, params);
}
return true;
@@ -3268,7 +3269,7 @@ bool FillRectilinear::fill_surface_trapezoidal(
ExPolygons contracted = offset_ex(expolygon, -float(scale_(0.5 * this->spacing)));
// if contraction results in empty polygon, use original surface
const ExPolygon &intersection_surface = contracted.empty() ? expolygon : contracted.front();
const ExPolygons& intersection_surface = contracted.empty() ? ExPolygons{expolygon} : contracted;
// Intersect polylines with offset expolygon
polylines = intersection_pl(std::move(polylines), intersection_surface);
@@ -3284,7 +3285,14 @@ bool FillRectilinear::fill_surface_trapezoidal(
// Connect infill lines using offset expolygon
int infill_start_idx = polylines_out.size();
if (!polylines.empty()) {
Slic3r::Fill::chain_or_connect_infill(std::move(polylines), intersection_surface, polylines_out, this->spacing, params);
if (params.dont_connect()) {
if (polylines.size() > 1)
polylines = chain_polylines(std::move(polylines));
append(polylines_out, std::move(polylines));
} else {
connect_infill(std::move(polylines), to_polygons(intersection_surface), get_extents(intersection_surface), polylines_out,
this->spacing, params);
}
// Rotate back the infill lines to original orientation
if (std::abs(base_angle) >= EPSILON) {
+1 -1
View File
@@ -26,7 +26,7 @@ namespace Slic3r {
bool load_drc(const char *path, TriangleMesh *meshptr)
{
try {
boost::iostreams::mapped_file_source file(path);
boost::iostreams::mapped_file_source file{boost::filesystem::path{path}};
DecoderBuffer buffer;
buffer.Init(file.data(), file.size());
+227 -23
View File
@@ -882,7 +882,23 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
config.set_key_value("old_retract_length_toolchange", new ConfigOptionFloat(old_retract_length_toolchange));
config.set_key_value("new_retract_length_toolchange", new ConfigOptionFloat(new_retract_length_toolchange));
config.set_key_value("old_filament_temp", new ConfigOptionInt(old_filament_temp));
int interface_temp = full_config.filament_tower_interface_print_temp.get_at(new_filament_id);
if (interface_temp == -1)
interface_temp = full_config.nozzle_temperature_range_high.get_at(new_filament_id);
if (full_config.enable_tower_interface_features && tcr.is_contact)
new_filament_temp = interface_temp;
config.set_key_value("new_filament_temp", new ConfigOptionInt(new_filament_temp));
if (full_config.enable_tower_interface_features && tcr.is_contact) {
auto temps = full_config.nozzle_temperature.values;
if (new_filament_id >= 0 && new_filament_id < (int)temps.size())
temps[new_filament_id] = interface_temp;
config.set_key_value("temperature", new ConfigOptionInts(temps));
auto first_layer_temps = full_config.nozzle_temperature_initial_layer.values;
if (new_filament_id >= 0 && new_filament_id < (int)first_layer_temps.size())
first_layer_temps[new_filament_id] = interface_temp;
config.set_key_value("first_layer_temperature", new ConfigOptionInts(first_layer_temps));
}
config.set_key_value("x_after_toolchange", new ConfigOptionFloat(tool_change_start_pos(0)));
config.set_key_value("y_after_toolchange", new ConfigOptionFloat(tool_change_start_pos(1)));
config.set_key_value("z_after_toolchange", new ConfigOptionFloat(nozzle_pos(2)));
@@ -912,6 +928,9 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
config.set_key_value("flush_length", new ConfigOptionFloat(purge_length));
config.set_key_value("wipe_avoid_perimeter", new ConfigOptionBool(is_used_travel_avoid_perimeter));
config.set_key_value("wipe_avoid_pos_x", new ConfigOptionFloat(wipe_avoid_pos_x));
config.set_key_value("is_prime_tower_interface", new ConfigOptionBool(tcr.is_contact));
config.set_key_value("filament_tower_interface_purge_volume", new ConfigOptionFloat(full_config.filament_tower_interface_purge_volume.get_at(new_filament_id)));
config.set_key_value("filament_tower_interface_print_temp", new ConfigOptionInt(interface_temp));
int flush_count = std::min(g_max_flush_count, (int) std::round(purge_volume / g_purge_volume_one_time));
float flush_unit = purge_length / flush_count;
@@ -1146,10 +1165,18 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
std::string toolchange_gcode_str;
std::string deretraction_str;
int toolchange_temp_override = -1;
int interface_temp = -1;
if (tcr.priming || (new_extruder_id >= 0 && needs_toolchange)) {
if (is_ramming)
gcodegen.m_wipe.reset_path(); // We don't want wiping on the ramming lines.
toolchange_gcode_str = gcodegen.set_extruder(new_extruder_id, tcr.print_z); // TODO: toolchange_z vs print_z
if (gcodegen.config().enable_tower_interface_features && tcr.is_contact) {
interface_temp = gcodegen.config().filament_tower_interface_print_temp.get_at(new_extruder_id);
if (interface_temp == -1)
interface_temp = gcodegen.config().nozzle_temperature_range_high.get_at(new_extruder_id);
toolchange_temp_override = interface_temp;
}
toolchange_gcode_str = gcodegen.set_extruder(new_extruder_id, tcr.print_z, false, toolchange_temp_override); // TODO: toolchange_z vs print_z
if (gcodegen.config().enable_prime_tower) {
deretraction_str += gcodegen.writer().travel_to_z(z, "Force restore layer Z", true);
Vec3d position{gcodegen.writer().get_position()};
@@ -1159,6 +1186,144 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
}
}
if (toolchange_temp_override > 0) {
int base_temp = gcodegen.on_first_layer() ? gcodegen.config().nozzle_temperature_initial_layer.get_at(new_extruder_id)
: gcodegen.config().nozzle_temperature.get_at(new_extruder_id);
if (std::abs(tcr.print_z) < EPSILON)
base_temp = gcodegen.config().nozzle_temperature_initial_layer.get_at(new_extruder_id);
const std::string t_token = " T" + std::to_string(new_extruder_id);
std::string out;
out.reserve(toolchange_gcode_str.size());
size_t pos = 0;
while (pos < toolchange_gcode_str.size()) {
size_t line_end = toolchange_gcode_str.find('\n', pos);
if (line_end == std::string::npos)
line_end = toolchange_gcode_str.size();
std::string line = toolchange_gcode_str.substr(pos, line_end - pos);
std::string trimmed = line;
trimmed.erase(0, trimmed.find_first_not_of(" \t"));
bool skip_line = false;
if (boost::starts_with(trimmed, "M109")) {
bool matches_extruder = trimmed.find(t_token) != std::string::npos;
if (!matches_extruder) {
size_t t_pos = trimmed.find('T');
if (t_pos != std::string::npos) {
size_t t_end = trimmed.find_first_not_of("0123456789", t_pos + 1);
const std::string t_val = trimmed.substr(t_pos + 1, t_end == std::string::npos ? std::string::npos : t_end - (t_pos + 1));
if (!t_val.empty()) {
try {
matches_extruder = std::stoi(t_val) == new_extruder_id;
} catch (...) {
matches_extruder = false;
}
}
}
}
if (matches_extruder) {
size_t s_pos = trimmed.find('S');
if (s_pos != std::string::npos) {
size_t s_end = trimmed.find_first_not_of("0123456789", s_pos + 1);
const std::string s_val = trimmed.substr(s_pos + 1, s_end == std::string::npos ? std::string::npos : s_end - (s_pos + 1));
if (!s_val.empty()) {
try {
skip_line = std::stoi(s_val) == base_temp;
} catch (...) {
skip_line = false;
}
}
}
}
}
if (!skip_line) {
out.append(line);
if (line_end < toolchange_gcode_str.size())
out.push_back('\n');
}
pos = line_end + 1;
}
toolchange_gcode_str.swap(out);
}
if (toolchange_temp_override > 0) {
const std::string preheat_token = "preheat T" + std::to_string(new_extruder_id);
const int preheat_temp = interface_temp > 0 ? interface_temp : toolchange_temp_override;
std::string out;
out.reserve(tcr_rotated_gcode.size());
size_t pos = 0;
while (pos < tcr_rotated_gcode.size()) {
size_t line_end = tcr_rotated_gcode.find('\n', pos);
if (line_end == std::string::npos)
line_end = tcr_rotated_gcode.size();
std::string line = tcr_rotated_gcode.substr(pos, line_end - pos);
std::string trimmed = line;
trimmed.erase(0, trimmed.find_first_not_of(" \t"));
const bool is_preheat_line = (trimmed.find(preheat_token) != std::string::npos);
if (is_preheat_line) {
// Preserve early-preheat timing while forcing interface temp for contact toolchanges.
size_t s_pos = trimmed.find('S');
if (s_pos != std::string::npos) {
size_t s_end = trimmed.find_first_not_of("0123456789", s_pos + 1);
trimmed.replace(s_pos + 1,
(s_end == std::string::npos ? trimmed.size() : s_end) - (s_pos + 1),
std::to_string(preheat_temp));
// Reapply left indentation from the original line.
size_t line_prefix = line.find_first_not_of(" \t");
if (line_prefix != std::string::npos)
line = line.substr(0, line_prefix) + trimmed;
else
line = trimmed;
}
}
out.append(line);
if (line_end < tcr_rotated_gcode.size())
out.push_back('\n');
pos = line_end + 1;
}
tcr_rotated_gcode.swap(out);
}
if (toolchange_temp_override > 0 && interface_temp > 0) {
const std::string t_token = " T" + std::to_string(new_extruder_id);
std::string out;
out.reserve(tcr_rotated_gcode.size());
size_t pos = 0;
while (pos < tcr_rotated_gcode.size()) {
size_t line_end = tcr_rotated_gcode.find('\n', pos);
if (line_end == std::string::npos)
line_end = tcr_rotated_gcode.size();
std::string line = tcr_rotated_gcode.substr(pos, line_end - pos);
std::string trimmed = line;
trimmed.erase(0, trimmed.find_first_not_of(" \t"));
bool skip_line = false;
if (boost::starts_with(trimmed, "M109")) {
bool matches_extruder = true;
if (trimmed.find('T') != std::string::npos)
matches_extruder = trimmed.find(t_token) != std::string::npos;
if (matches_extruder) {
size_t s_pos = trimmed.find('S');
if (s_pos != std::string::npos) {
size_t s_end = trimmed.find_first_not_of("0123456789", s_pos + 1);
const std::string s_val = trimmed.substr(s_pos + 1, s_end == std::string::npos ? std::string::npos : s_end - (s_pos + 1));
if (!s_val.empty()) {
try {
skip_line = std::stoi(s_val) == interface_temp;
} catch (...) {
skip_line = false;
}
}
}
}
}
if (!skip_line) {
out.append(line);
if (line_end < tcr_rotated_gcode.size())
out.push_back('\n');
}
pos = line_end + 1;
}
tcr_rotated_gcode.swap(out);
}
// Insert the toolchange and deretraction gcode into the generated gcode.
DynamicConfig config;
@@ -1246,11 +1411,12 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
else
line_out << ch;
}
// Strip original wipe tower X/Y even if position unchanged (fixes out of bed moves).
line = line_out.str();
transformed_pos = trans_pos(pos);
if (transformed_pos != old_pos || never_skip) {
line = line_out.str();
std::ostringstream oss;
oss << std::fixed << std::setprecision(3) << cur_gcode_start;
if (transformed_pos.x() != old_pos.x() || never_skip)
@@ -1283,7 +1449,7 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
}
old_pos = Vec2f{-1000.1f, -1000.1f};
pos = tcr.tool_change_start_pos;
transformed_pos = pos;
transformed_pos = trans_pos(pos);
}
}
return gcode_out;
@@ -1582,7 +1748,7 @@ std::vector<GCode::LayerToPrint> GCode::collect_layers_to_print(const PrintObjec
// first layer may result in skirt/brim in the air and maybe other issues.
if (layers_to_print.size() == 1u) {
if (!has_extrusions)
throw Slic3r::SlicingError(_(L("One object has empty initial layer and can't be printed. Please Cut the bottom or enable supports.")), object.id().id);
throw Slic3r::SlicingError(_(L("One object has an empty first layer and can't be printed. Please Cut the bottom or enable supports.")), object.id().id);
}
// In case there are extrusions on this layer, check there is a layer to lay it on.
@@ -2580,7 +2746,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
m_pa_processor = std::make_unique<AdaptivePAProcessor>(*this, tool_ordering.all_extruders());
// Emit machine envelope limits for the Marlin firmware.
this->print_machine_envelope(file, print, initial_extruder_id);
this->print_machine_envelope(file, print);
// Disable fan.
if (m_config.auxiliary_fan.value && print.config().close_fan_the_first_x_layers.get_at(initial_extruder_id)) {
@@ -2861,7 +3027,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
}
}
// wipe tower area
if (has_wipe_tower) {
if (has_wipe_tower && print.wipe_tower_data().wipe_tower_mesh_data) {
Polygon temp_Expoly = print.wipe_tower_data().wipe_tower_mesh_data->bottom;
area_sum_temp += temp_Expoly.area();
}
@@ -3645,23 +3811,22 @@ PlaceholderParserIntegration &ppi = m_placeholder_parser_integration;
// Print the machine envelope G-code for the Marlin firmware based on the "machine_max_xxx" parameters.
// Do not process this piece of G-code by the time estimator, it already knows the values through another sources.
void GCode::print_machine_envelope(GCodeOutputStream &file, Print &print, int extruder_id)
void GCode::print_machine_envelope(GCodeOutputStream &file, Print &print)
{
int matched_machine_limit_idx = get_extruder_id(extruder_id) * 2;
const auto flavor = print.config().gcode_flavor.value;
if ((flavor == gcfMarlinLegacy || flavor == gcfMarlinFirmware || flavor == gcfRepRapFirmware) &&
print.config().emit_machine_limits_to_gcode.value == true) {
int factor = flavor == gcfRepRapFirmware ? 60 : 1; // RRF M203 and M566 are in mm/min
file.write_format("M201 X%d Y%d Z%d E%d\n",
int(print.config().machine_max_acceleration_x.values[matched_machine_limit_idx] + 0.5),
int(print.config().machine_max_acceleration_y.values[matched_machine_limit_idx] + 0.5),
int(print.config().machine_max_acceleration_z.values[matched_machine_limit_idx] + 0.5),
int(print.config().machine_max_acceleration_e.values[matched_machine_limit_idx] + 0.5));
int(print.config().machine_max_acceleration_x.values.front() + 0.5),
int(print.config().machine_max_acceleration_y.values.front() + 0.5),
int(print.config().machine_max_acceleration_z.values.front() + 0.5),
int(print.config().machine_max_acceleration_e.values.front() + 0.5));
file.write_format("M203 X%d Y%d Z%d E%d\n",
int(print.config().machine_max_speed_x.values[matched_machine_limit_idx] * factor + 0.5),
int(print.config().machine_max_speed_y.values[matched_machine_limit_idx] * factor + 0.5),
int(print.config().machine_max_speed_z.values[matched_machine_limit_idx] * factor + 0.5),
int(print.config().machine_max_speed_e.values[matched_machine_limit_idx] * factor + 0.5));
int(print.config().machine_max_speed_x.values.front() * factor + 0.5),
int(print.config().machine_max_speed_y.values.front() * factor + 0.5),
int(print.config().machine_max_speed_z.values.front() * factor + 0.5),
int(print.config().machine_max_speed_e.values.front() * factor + 0.5));
// Now M204 - acceleration. This one is quite hairy thanks to how Marlin guys care about
// Legacy Marlin should export travel acceleration the same as printing acceleration.
@@ -4720,7 +4885,10 @@ LayerResult GCode::process_layer(
if (print.config().print_sequence == PrintSequence::ByObject) {
filament_to_print_instances[filament_id] = sort_print_object_instances(objects_by_extruder_it->second, layers, ordering, single_object_instance_idx);
} else {
filament_to_print_instances[filament_id] = sort_print_object_instances(objects_by_extruder_it->second, layers, &new_ordering, single_object_instance_idx);
// PrintSequence::ByLayer to use global ordering ( per object ordering ) if intra-layer order PrintOrder::AsObjectList is specified while keeping behaviour of PrintSequence::ByLayer
const std::vector<const PrintInstance*>* ordering_for_filament = (print.config().print_order == PrintOrder::AsObjectList && ordering != nullptr) ? ordering: &new_ordering;
filament_to_print_instances[filament_id] = sort_print_object_instances(objects_by_extruder_it->second, layers, ordering_for_filament, single_object_instance_idx);
}
}
}
@@ -5236,7 +5404,7 @@ void GCode::append_full_config(const Print &print, std::string &str)
size_t temp_begin_t = idx * matrix_value_count, temp_end_t = (idx + 1) * matrix_value_count;
std::transform(temp_flush_volumes_matrix.begin() + temp_begin_t, temp_flush_volumes_matrix.begin() + temp_end_t,
temp_flush_volumes_matrix.begin() + temp_begin_t,
[temp_cfg_flush_multiplier_idx](double inputx) { return inputx * temp_cfg_flush_multiplier_idx; });
[temp_cfg_flush_multiplier_idx](double inputx) { return std::round(inputx * temp_cfg_flush_multiplier_idx); });
}
cfg.option<ConfigOptionFloats>("flush_volumes_matrix")->values = temp_flush_volumes_matrix;
} else if (filament_count_tmp == 1) {
@@ -5925,9 +6093,9 @@ bool GCode::_needSAFC(const ExtrusionPath &path)
};
return std::any_of(std::begin(supported_patterns), std::end(supported_patterns), [&](const InfillPattern pattern) {
return this->on_first_layer() && this->config().bottom_surface_pattern == pattern ||
path.role() == erSolidInfill && this->config().internal_solid_infill_pattern == pattern ||
path.role() == erTopSolidInfill && this->config().top_surface_pattern == pattern;
return (this->on_first_layer() && this->config().bottom_surface_pattern == pattern) ||
(path.role() == erSolidInfill && this->config().internal_solid_infill_pattern == pattern) ||
(path.role() == erTopSolidInfill && this->config().top_surface_pattern == pattern);
});
}
@@ -6160,7 +6328,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
if (speed == 0)
speed = filament_max_volumetric_speed / _mm3_per_mm;
if (this->on_first_layer()) {
//BBS: for solid infill of initial layer, speed can be higher as long as
//BBS: for solid infill of first layer, speed can be higher as long as
//wall lines have be attached
if (path.role() != erBottomSurface)
speed = m_config.get_abs_value("initial_layer_speed");
@@ -7233,7 +7401,7 @@ std::string GCode::retract(bool toolchange, bool is_last_retraction, LiftType li
return gcode;
}
std::string GCode::set_extruder(unsigned int new_filament_id, double print_z, bool by_object)
std::string GCode::set_extruder(unsigned int new_filament_id, double print_z, bool by_object, int toolchange_temp_override)
{
int new_extruder_id = get_extruder_id(new_filament_id);
if (!m_writer.need_toolchange(new_filament_id))
@@ -7321,6 +7489,8 @@ std::string GCode::set_extruder(unsigned int new_filament_id, double print_z, bo
// BBS: if print_z == 0 use first layer temperature
if (abs(print_z) < EPSILON)
new_filament_temp = m_config.nozzle_temperature_initial_layer.get_at(new_filament_id);
if (toolchange_temp_override > 0)
new_filament_temp = toolchange_temp_override;
Vec3d nozzle_pos = m_writer.get_position();
float old_retract_length, old_retract_length_toolchange, wipe_volume;
@@ -7413,6 +7583,27 @@ std::string GCode::set_extruder(unsigned int new_filament_id, double print_z, bo
dyn_config.set_key_value("travel_point_3_y", new ConfigOptionFloat(float(travel_point_3.y())));
dyn_config.set_key_value("wipe_avoid_perimeter", new ConfigOptionBool(false));
dyn_config.set_key_value("wipe_avoid_pos_x", new ConfigOptionFloat(wipe_avoid_pos_x));
dyn_config.set_key_value("is_prime_tower_interface", new ConfigOptionBool(false));
dyn_config.set_key_value("filament_tower_interface_purge_volume", new ConfigOptionFloat(m_config.filament_tower_interface_purge_volume.get_at(new_filament_id)));
{
int interface_temp = m_config.filament_tower_interface_print_temp.get_at(new_filament_id);
if (interface_temp == -1)
interface_temp = m_config.nozzle_temperature_range_high.get_at(new_filament_id);
dyn_config.set_key_value("filament_tower_interface_print_temp", new ConfigOptionInt(interface_temp));
}
if (toolchange_temp_override > 0) {
auto temps = m_config.nozzle_temperature.values;
if (new_filament_id < temps.size())
temps[new_filament_id] = toolchange_temp_override;
dyn_config.set_key_value("temperature", new ConfigOptionInts(temps));
dyn_config.set_key_value("nozzle_temperature", new ConfigOptionInts(temps));
auto first_layer_temps = m_config.nozzle_temperature_initial_layer.values;
if (new_filament_id < first_layer_temps.size())
first_layer_temps[new_filament_id] = toolchange_temp_override;
dyn_config.set_key_value("first_layer_temperature", new ConfigOptionInts(first_layer_temps));
dyn_config.set_key_value("nozzle_temperature_initial_layer", new ConfigOptionInts(first_layer_temps));
}
auto flush_v_speed = m_print->config().filament_flush_volumetric_speed.values;
auto flush_temps =m_print->config().filament_flush_temp.values;
@@ -7513,6 +7704,19 @@ std::string GCode::set_extruder(unsigned int new_filament_id, double print_z, bo
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(new_filament_id)));
if (toolchange_temp_override > 0) {
auto temps = m_config.nozzle_temperature.values;
if (new_filament_id < temps.size())
temps[new_filament_id] = toolchange_temp_override;
config.set_key_value("temperature", new ConfigOptionInts(temps));
config.set_key_value("nozzle_temperature", new ConfigOptionInts(temps));
auto first_layer_temps = m_config.nozzle_temperature_initial_layer.values;
if (new_filament_id < first_layer_temps.size())
first_layer_temps[new_filament_id] = toolchange_temp_override;
config.set_key_value("first_layer_temperature", new ConfigOptionInts(first_layer_temps));
config.set_key_value("nozzle_temperature_initial_layer", new ConfigOptionInts(first_layer_temps));
}
gcode += this->placeholder_parser_process("filament_start_gcode", filament_start_gcode, new_filament_id, &config);
if (add_change_filament_624) {
gcode += "M625\n";
+2 -2
View File
@@ -249,7 +249,7 @@ public:
bool needs_retraction(const Polyline& travel, ExtrusionRole role, LiftType& lift_type);
std::string retract(bool toolchange = false, bool is_last_retraction = false, LiftType lift_type = LiftType::NormalLift, bool apply_instantly = false, ExtrusionRole role = erNone);
std::string unretract() { return m_writer.unlift() + m_writer.unretract(); }
std::string set_extruder(unsigned int extruder_id, double print_z, bool by_object=false);
std::string set_extruder(unsigned int extruder_id, double print_z, bool by_object=false, int toolchange_temp_override = -1);
bool is_BBL_Printer();
bool is_QIDI_Printer();
@@ -641,7 +641,7 @@ private:
double calc_max_volumetric_speed(const double layer_height, const double line_width, const std::string co_str);
std::string _extrude(const ExtrusionPath &path, std::string description = "", double speed = -1);
bool _needSAFC(const ExtrusionPath &path);
void print_machine_envelope(GCodeOutputStream& file, Print& print, int extruder_id);
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.
+2 -2
View File
@@ -879,8 +879,8 @@ std::string CoolingBuffer::apply_layer_cooldown(
fan_speed_change_requests[CoolingLine::TYPE_IRONING_FAN_START] = true;
need_set_fan = true;
}
} else if (line->type & CoolingLine::TYPE_IRONING_FAN_END && fan_speed_change_requests[CoolingLine::TYPE_IRONING_FAN_START]) {
if (ironing_fan_control) {
} else if (line->type & CoolingLine::TYPE_IRONING_FAN_END) {
if (ironing_fan_control && fan_speed_change_requests[CoolingLine::TYPE_IRONING_FAN_START]) {
fan_speed_change_requests[CoolingLine::TYPE_IRONING_FAN_START] = false;
}
need_set_fan = true;
+135 -49
View File
@@ -124,7 +124,7 @@ static void set_option_value(ConfigOptionFloats& option, size_t id, float value)
static float get_option_value(const ConfigOptionFloats& option, size_t id)
{
return option.values.empty() ? 0.0f :
((id < option.values.size()) ? static_cast<float>(option.values[id]) : static_cast<float>(option.values.back()));
((id < option.values.size()) ? static_cast<float>(option.values[id]) : static_cast<float>(option.values.front()));
}
static float estimated_acceleration_distance(float initial_rate, float target_rate, float acceleration)
@@ -1259,7 +1259,7 @@ void GCodeProcessor::run_post_process()
// line inserter
[tool_number, this](unsigned int id, const std::vector<float>& time_diffs) {
const int temperature = int(m_layer_id != 1 ? m_filament_nozzle_temp[tool_number] :
m_filament_nozzle_temp_first_layer[tool_number]);
m_filament_nozzle_temp_first_layer[tool_number]);
// Orca: M104.1 for XL printers, I can't find the documentation for this so I copied the C++ comments from
// Prusa-Firmware-Buddy here
/**
@@ -1731,6 +1731,10 @@ void GCodeProcessor::register_commands()
{"M702", [this](const GCodeReader::GCodeLine& line) { process_M702(line); }}, // Unload the current filament into the MK3 MMU2 unit at the end of print.
{"M1020", [this](const GCodeReader::GCodeLine& line) { process_M1020(line); }}, // Select Tool
// ORCA: Add Pressure Advance visualization support
{"M900", [this](const GCodeReader::GCodeLine& line) { process_M900(line); }}, // Marlin: Set pressure advance
{"M572", [this](const GCodeReader::GCodeLine& line) { process_M572(line); }}, // RepRapFirmware/Duet: Set pressure advance
{"T", [this](const GCodeReader::GCodeLine& line) { process_T(line); }}, // Select Tool
{"SYNC", [this](const GCodeReader::GCodeLine& line) { process_SYNC(line); }}, // SYNC TIME
@@ -2792,6 +2796,12 @@ void GCodeProcessor::process_gcode_line(const GCodeReader::GCodeLine& line, bool
process_SET_VELOCITY_LIMIT(line);
return;
}
// ORCA: Add Pressure Advance visualization support
if (boost::iequals(cmd, "SET_PRESSURE_ADVANCE"))
{
process_SET_PRESSURE_ADVANCE(line);
return;
}
}
if (cmd.length() > 1) {
@@ -3931,7 +3941,7 @@ void GCodeProcessor::process_G1(const std::array<std::optional<double>, 4>& axes
curr.abs_axis_feedrate[a] = std::abs(curr.axis_feedrate[a]);
if (curr.abs_axis_feedrate[a] != 0.0f) {
float axis_max_feedrate = get_axis_max_feedrate(static_cast<PrintEstimatedStatistics::ETimeMode>(i), static_cast<Axis>(a), m_extruder_id);
float axis_max_feedrate = get_axis_max_feedrate(static_cast<PrintEstimatedStatistics::ETimeMode>(i), static_cast<Axis>(a));
if (axis_max_feedrate != 0.0f) min_feedrate_factor = std::min<float>(min_feedrate_factor, axis_max_feedrate / curr.abs_axis_feedrate[a]);
}
}
@@ -3955,7 +3965,7 @@ void GCodeProcessor::process_G1(const std::array<std::optional<double>, 4>& axes
//BBS
for (unsigned char a = X; a <= E; ++a) {
float axis_max_acceleration = get_axis_max_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i), static_cast<Axis>(a), m_extruder_id);
float axis_max_acceleration = get_axis_max_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i), static_cast<Axis>(a));
if (acceleration * std::abs(delta_pos[a]) * inv_distance > axis_max_acceleration)
acceleration = axis_max_acceleration / (std::abs(delta_pos[a]) * inv_distance);
}
@@ -4289,7 +4299,7 @@ void GCodeProcessor::process_VG1(const GCodeReader::GCodeLine& line)
curr.abs_axis_feedrate[a] = std::abs(curr.axis_feedrate[a]);
if (curr.abs_axis_feedrate[a] != 0.0f) {
float axis_max_feedrate = get_axis_max_feedrate(static_cast<PrintEstimatedStatistics::ETimeMode>(i), static_cast<Axis>(a), m_extruder_id);
float axis_max_feedrate = get_axis_max_feedrate(static_cast<PrintEstimatedStatistics::ETimeMode>(i), static_cast<Axis>(a));
if (axis_max_feedrate != 0.0f) min_feedrate_factor = std::min<float>(min_feedrate_factor, axis_max_feedrate / curr.abs_axis_feedrate[a]);
}
}
@@ -4313,7 +4323,7 @@ void GCodeProcessor::process_VG1(const GCodeReader::GCodeLine& line)
//BBS
for (unsigned char a = X; a <= E; ++a) {
float axis_max_acceleration = get_axis_max_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i), static_cast<Axis>(a), m_extruder_id);
float axis_max_acceleration = get_axis_max_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i), static_cast<Axis>(a));
if (acceleration * std::abs(delta_pos[a]) * inv_distance > axis_max_acceleration)
acceleration = axis_max_acceleration / (std::abs(delta_pos[a]) * inv_distance);
}
@@ -4914,6 +4924,37 @@ void GCodeProcessor::process_M106(const GCodeReader::GCodeLine& line)
}
}
// ORCA: Add Pressure Advance visualization support
void GCodeProcessor::process_M900(const GCodeReader::GCodeLine &line)
{
float pa_value = m_pressure_advance;
line.has_value('K', pa_value);
m_pressure_advance = std::max(0.0f, pa_value);
// BOOST_LOG_TRIVIAL(debug) << "M900 command: PA set to " << m_pressure_advance;
}
void GCodeProcessor::process_M572(const GCodeReader::GCodeLine &line)
{
float pa_value = m_pressure_advance;
line.has_value('S', pa_value);
m_pressure_advance = std::max(0.0f, pa_value);
// BOOST_LOG_TRIVIAL(debug) << "M572 command: PA set to " << m_pressure_advance;
}
void GCodeProcessor::process_SET_PRESSURE_ADVANCE(const GCodeReader::GCodeLine& line)
{
std::regex regex(R"(SET_PRESSURE_ADVANCE\s+(?:.*\s+)?ADVANCE\s*=\s*([\d.]+))");
std::smatch matches;
if (std::regex_search(line.raw(), matches, regex) && matches.size() > 1) {
float pa_value = 0;
try {
pa_value = std::stof(matches[1].str());
} catch (...) {}
m_pressure_advance = std::max(0.0f, pa_value);
}
}
void GCodeProcessor::process_M107(const GCodeReader::GCodeLine& line)
{
m_fan_speed = 0.0f;
@@ -5016,18 +5057,17 @@ void GCodeProcessor::process_M201(const GCodeReader::GCodeLine& line)
{
// see http://reprap.org/wiki/G-code#M201:_Set_max_printing_acceleration
float factor = ((m_flavor != gcfRepRapSprinter && m_flavor != gcfRepRapFirmware) && m_units == EUnits::Inches) ? INCHES_TO_MM : 1.0f;
int indx_limit = m_time_processor.machine_limits.machine_max_acceleration_x.size() / 2;
for (size_t index = 0; index < indx_limit; index += 2) {
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
if (static_cast<PrintEstimatedStatistics::ETimeMode>(i) == PrintEstimatedStatistics::ETimeMode::Normal || m_time_processor.machine_envelope_processing_enabled) {
if (line.has_x()) set_option_value(m_time_processor.machine_limits.machine_max_acceleration_x, index + i, line.x() * factor);
if (line.has_y()) set_option_value(m_time_processor.machine_limits.machine_max_acceleration_y, index + i, line.y() * factor);
// Write to index i (0=Normal, 1=Stealth) — matches get_axis_max_acceleration's read pattern.
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
if (static_cast<PrintEstimatedStatistics::ETimeMode>(i) == PrintEstimatedStatistics::ETimeMode::Normal || m_time_processor.machine_envelope_processing_enabled) {
if (line.has_x()) set_option_value(m_time_processor.machine_limits.machine_max_acceleration_x, i, line.x() * factor);
if (line.has_z()) set_option_value(m_time_processor.machine_limits.machine_max_acceleration_z, index + i, line.z() * factor);
if (line.has_y()) set_option_value(m_time_processor.machine_limits.machine_max_acceleration_y, i, line.y() * factor);
if (line.has_e()) set_option_value(m_time_processor.machine_limits.machine_max_acceleration_e, index + i, line.e() * factor);
}
if (line.has_z()) set_option_value(m_time_processor.machine_limits.machine_max_acceleration_z, i, line.z() * factor);
if (line.has_e()) set_option_value(m_time_processor.machine_limits.machine_max_acceleration_e, i, line.e() * factor);
}
}
}
@@ -5042,23 +5082,20 @@ void GCodeProcessor::process_M203(const GCodeReader::GCodeLine& line)
// http://smoothieware.org/supported-g-codes
float factor = (m_flavor == gcfMarlinLegacy || m_flavor == gcfMarlinFirmware || m_flavor == gcfSmoothie || m_flavor == gcfKlipper) ? 1.0f : MMMIN_TO_MMSEC;
//BBS:
int indx_limit = m_time_processor.machine_limits.machine_max_speed_x.size() / 2;
for (size_t index = 0; index < indx_limit; index += 2) {
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
if (static_cast<PrintEstimatedStatistics::ETimeMode>(i) == PrintEstimatedStatistics::ETimeMode::Normal || m_time_processor.machine_envelope_processing_enabled) {
if (line.has_x())
set_option_value(m_time_processor.machine_limits.machine_max_speed_x, index + i, line.x() * factor);
// Write to index i (0=Normal, 1=Stealth) — matches get_axis_max_feedrate's read pattern.
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
if (static_cast<PrintEstimatedStatistics::ETimeMode>(i) == PrintEstimatedStatistics::ETimeMode::Normal || m_time_processor.machine_envelope_processing_enabled) {
if (line.has_x())
set_option_value(m_time_processor.machine_limits.machine_max_speed_x, i, line.x() * factor);
if (line.has_y())
set_option_value(m_time_processor.machine_limits.machine_max_speed_y, index + i, line.y() * factor);
if (line.has_y())
set_option_value(m_time_processor.machine_limits.machine_max_speed_y, i, line.y() * factor);
if (line.has_z())
set_option_value(m_time_processor.machine_limits.machine_max_speed_z, index + i, line.z() * factor);
if (line.has_z())
set_option_value(m_time_processor.machine_limits.machine_max_speed_z, i, line.z() * factor);
if (line.has_e())
set_option_value(m_time_processor.machine_limits.machine_max_speed_e, index + i, line.e() * factor);
}
if (line.has_e())
set_option_value(m_time_processor.machine_limits.machine_max_speed_e, i, line.e() * factor);
}
}
}
@@ -5118,6 +5155,9 @@ void GCodeProcessor::process_M205(const GCodeReader::GCodeLine& line)
if (line.has_value('T', value))
set_option_value(m_time_processor.machine_limits.machine_min_travel_rate, i, value);
if (line.has_value('J', value))
set_option_value(m_time_processor.machine_limits.machine_max_junction_deviation, i, value);
}
}
}
@@ -5453,6 +5493,8 @@ void GCodeProcessor::store_move_vertex(EMoveType type, EMovePathType path_type,
m_travel_dist,
m_fan_speed,
m_extruder_temps[filament_id],
// ORCA: Add Pressure Advance visualization support
m_pressure_advance,
{ 0.0f, 0.0f }, // time
static_cast<float>(m_layer_id), //layer_duration: set later
std::max<unsigned int>(1, m_layer_id) - 1,
@@ -5499,49 +5541,94 @@ float GCodeProcessor::minimum_travel_feedrate(PrintEstimatedStatistics::ETimeMod
return std::max(feedrate, get_option_value(m_time_processor.machine_limits.machine_min_travel_rate, static_cast<size_t>(mode)));
}
float GCodeProcessor::get_axis_max_feedrate(PrintEstimatedStatistics::ETimeMode mode, Axis axis, int extruder_id) const
// Machine limit arrays hold 2 values: [0]=Normal, [1]=Stealth. Index by mode only.
// BambuStudio used extruder_id*2+mode to support per-nozzle limits, but OrcaSlicer
// never ported that system (filament_map_2 / get_config_idx_for_filament), so the
// extruder_id offset was always wrong: uninitialized extruder (255) or extruder > 0
// would overshoot the array and fall back to values.back() (stealth limits).
float GCodeProcessor::get_axis_max_feedrate(PrintEstimatedStatistics::ETimeMode mode, Axis axis) const
{
int matched_pos = extruder_id * 2;
switch (axis)
{
case X: { return get_option_value(m_time_processor.machine_limits.machine_max_speed_x, matched_pos + static_cast<size_t>(mode)); }
case Y: { return get_option_value(m_time_processor.machine_limits.machine_max_speed_y, matched_pos + static_cast<size_t>(mode)); }
case Z: { return get_option_value(m_time_processor.machine_limits.machine_max_speed_z, matched_pos + static_cast<size_t>(mode)); }
case E: { return get_option_value(m_time_processor.machine_limits.machine_max_speed_e, matched_pos + static_cast<size_t>(mode)); }
case X: { return get_option_value(m_time_processor.machine_limits.machine_max_speed_x, static_cast<size_t>(mode)); }
case Y: { return get_option_value(m_time_processor.machine_limits.machine_max_speed_y, static_cast<size_t>(mode)); }
case Z: { return get_option_value(m_time_processor.machine_limits.machine_max_speed_z, static_cast<size_t>(mode)); }
case E: { return get_option_value(m_time_processor.machine_limits.machine_max_speed_e, static_cast<size_t>(mode)); }
default: { return 0.0f; }
}
}
float GCodeProcessor::get_axis_max_acceleration(PrintEstimatedStatistics::ETimeMode mode, Axis axis, int extruder_id) const
float GCodeProcessor::get_axis_max_acceleration(PrintEstimatedStatistics::ETimeMode mode, Axis axis) const
{
int matched_pos = extruder_id * 2;
switch (axis)
{
case X: { return get_option_value(m_time_processor.machine_limits.machine_max_acceleration_x, matched_pos + static_cast<size_t>(mode)); }
case Y: { return get_option_value(m_time_processor.machine_limits.machine_max_acceleration_y, matched_pos + static_cast<size_t>(mode)); }
case Z: { return get_option_value(m_time_processor.machine_limits.machine_max_acceleration_z, matched_pos + static_cast<size_t>(mode)); }
case E: { return get_option_value(m_time_processor.machine_limits.machine_max_acceleration_e, matched_pos + static_cast<size_t>(mode)); }
case X: { return get_option_value(m_time_processor.machine_limits.machine_max_acceleration_x, static_cast<size_t>(mode)); }
case Y: { return get_option_value(m_time_processor.machine_limits.machine_max_acceleration_y, static_cast<size_t>(mode)); }
case Z: { return get_option_value(m_time_processor.machine_limits.machine_max_acceleration_z, static_cast<size_t>(mode)); }
case E: { return get_option_value(m_time_processor.machine_limits.machine_max_acceleration_e, static_cast<size_t>(mode)); }
default: { return 0.0f; }
}
}
float GCodeProcessor::get_axis_max_jerk_with_jd(PrintEstimatedStatistics::ETimeMode mode, Axis axis) const
{
if (axis != X && axis != Y && axis != Z && axis != E)
return 0.0f;
const size_t id = static_cast<size_t>(mode);
const float jd = get_option_value(m_time_processor.machine_limits.machine_max_junction_deviation, id);
if (jd <= 0.0f)
return 0.0f;
const float axis_max_acc = get_axis_max_acceleration(mode, axis);
const float generic_acc = get_acceleration(mode);
const float effective_acc = axis_max_acc > 0.0f ? axis_max_acc : generic_acc;
return std::sqrt(jd * effective_acc * 2.5f);
}
float GCodeProcessor::get_axis_max_jerk(PrintEstimatedStatistics::ETimeMode mode, Axis axis) const
{
const size_t id = static_cast<size_t>(mode);
const float jd = get_option_value(m_time_processor.machine_limits.machine_max_junction_deviation, id);
if (m_flavor == gcfMarlinFirmware && jd > 0.0f) {
return get_axis_max_jerk_with_jd(mode, axis);
}
switch (axis)
{
case X: { return get_option_value(m_time_processor.machine_limits.machine_max_jerk_x, static_cast<size_t>(mode)); }
case Y: { return get_option_value(m_time_processor.machine_limits.machine_max_jerk_y, static_cast<size_t>(mode)); }
case Z: { return get_option_value(m_time_processor.machine_limits.machine_max_jerk_z, static_cast<size_t>(mode)); }
case E: { return get_option_value(m_time_processor.machine_limits.machine_max_jerk_e, static_cast<size_t>(mode)); }
case X: { return get_option_value(m_time_processor.machine_limits.machine_max_jerk_x, id); }
case Y: { return get_option_value(m_time_processor.machine_limits.machine_max_jerk_y, id); }
case Z: { return get_option_value(m_time_processor.machine_limits.machine_max_jerk_z, id); }
case E: { return get_option_value(m_time_processor.machine_limits.machine_max_jerk_e, id); }
default: { return 0.0f; }
}
}
Vec3f GCodeProcessor::get_xyz_max_jerk(PrintEstimatedStatistics::ETimeMode mode) const
{
return Vec3f(get_option_value(m_time_processor.machine_limits.machine_max_jerk_x, static_cast<size_t>(mode)),
get_option_value(m_time_processor.machine_limits.machine_max_jerk_y, static_cast<size_t>(mode)),
get_option_value(m_time_processor.machine_limits.machine_max_jerk_z, static_cast<size_t>(mode)));
// Default values from config
const size_t id = static_cast<size_t>(mode);
float jx = 0.0f;
float jy = 0.0f;
float jz = 0.0f;
const float jd = get_option_value(m_time_processor.machine_limits.machine_max_junction_deviation, id);
// Classic Jerk: Junction Deviation is only supported by Marlin firmware when using a JD value grater than 0.
if (m_flavor != gcfMarlinFirmware || jd <= 0.0f)
{
jx = get_option_value(m_time_processor.machine_limits.machine_max_jerk_x, id);
jy = get_option_value(m_time_processor.machine_limits.machine_max_jerk_y, id);
jz = get_option_value(m_time_processor.machine_limits.machine_max_jerk_z, id);
}
else
{
jx = get_axis_max_jerk_with_jd(mode, X);
jy = get_axis_max_jerk_with_jd(mode, Y);
jz = get_axis_max_jerk_with_jd(mode, Z);
}
return Vec3f(jx, jy, jz);
}
float GCodeProcessor::get_retract_acceleration(PrintEstimatedStatistics::ETimeMode mode) const
@@ -5897,4 +5984,3 @@ int GCodeProcessor::get_extruder_id(bool force_initialize)const
}
} /* namespace Slic3r */
+16 -2
View File
@@ -184,6 +184,8 @@ class Print;
float travel_dist{ 0.0f }; // mm
float fan_speed{ 0.0f }; // percentage
float temperature{ 0.0f }; // Celsius degrees
// ORCA: Add Pressure Advance visualization support
float pressure_advance{ 0.0f };
std::array<float, static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count)> time{ 0.0f, 0.0f }; // s
float layer_duration{ 0.0f }; // s
unsigned int layer_id{ 0 };
@@ -777,6 +779,8 @@ class Print;
float m_travel_dist; // mm
float m_fan_speed; // percentage
float m_z_offset; // mm
// ORCA: Add Pressure Advance visualization support
float m_pressure_advance;
ExtrusionRole m_extrusion_role;
std::vector<int> m_filament_maps;
std::vector<unsigned char> m_last_filament_id;
@@ -981,6 +985,12 @@ class Print;
// Disable fan
void process_M107(const GCodeReader::GCodeLine& line);
// ORCA: Add Pressure Advance visualization support
// Set pressure advance
void process_M900(const GCodeReader::GCodeLine& line);
void process_M572(const GCodeReader::GCodeLine &line);
void process_SET_PRESSURE_ADVANCE(const GCodeReader::GCodeLine& line);
// Set tool (Sailfish)
void process_M108(const GCodeReader::GCodeLine& line);
@@ -1059,8 +1069,12 @@ class Print;
float minimum_feedrate(PrintEstimatedStatistics::ETimeMode mode, float feedrate) const;
float minimum_travel_feedrate(PrintEstimatedStatistics::ETimeMode mode, float feedrate) const;
float get_axis_max_feedrate(PrintEstimatedStatistics::ETimeMode mode, Axis axis, int extruder_id) const;
float get_axis_max_acceleration(PrintEstimatedStatistics::ETimeMode mode, Axis axis, int extruder_id) const;
// Machine limit arrays are indexed by time mode only: [0]=Normal, [1]=Stealth.
// Do NOT add an extruder_id parameter — OrcaSlicer does not use BambuStudio's
// per-nozzle machine limits (filament_map_2 / get_config_idx_for_filament).
float get_axis_max_feedrate(PrintEstimatedStatistics::ETimeMode mode, Axis axis) const;
float get_axis_max_acceleration(PrintEstimatedStatistics::ETimeMode mode, Axis axis) const;
float get_axis_max_jerk_with_jd(PrintEstimatedStatistics::ETimeMode mode, Axis axis) const;
float get_axis_max_jerk(PrintEstimatedStatistics::ETimeMode mode, Axis axis) const;
Vec3f get_xyz_max_jerk(PrintEstimatedStatistics::ETimeMode mode) const;
float get_retract_acceleration(PrintEstimatedStatistics::ETimeMode mode) const;
+9 -2
View File
@@ -541,10 +541,17 @@ void PressureEqualizer::output_gcode_line(const size_t line_idx)
// We don't have enough time to accel to max possible extrusion rate
// now we calculate the actual possible value
target_max_extrusion_rate = std::sqrt((2 * max_sloped_extrusion * sp * sn + sn * e0_2 + sp * e1_2) / (sp + sn));
// Worst case: we don't have enough time to do an accl-steady-decel movement at all, fallback to the old fashion
// single slope mode
if (target_max_extrusion_rate <= line.volumetric_extrusion_rate_start ||
target_max_extrusion_rate <= line.volumetric_extrusion_rate_end) {
goto single_slope_fallback; // TODO: FIXIT: better way than a goto?
}
}
assert(target_max_extrusion_rate > line.volumetric_extrusion_rate_start);
assert(target_max_extrusion_rate > line.volumetric_extrusion_rate_end);
assert(target_max_extrusion_rate >= line.volumetric_extrusion_rate);
assert(target_max_extrusion_rate <= line.volumetric_extrusion_rate);
// if the extrusion rate change is trivial, then ignore this algorithm and use the single sloped version instead
delta_volumetric_rate = std::round(std::min({ // important! it's MIN here not max!
@@ -626,7 +633,7 @@ void PressureEqualizer::output_gcode_line(const size_t line_idx)
return;
}
}
single_slope_fallback:
bool accelerating = line.volumetric_extrusion_rate_start < line.volumetric_extrusion_rate_end;
float feed_avg = 0.5f * (line.pos_start[4] + line.pos_end[4]);
// Limiting volumetric extrusion rate slope for this segment.
+69 -15
View File
@@ -16,7 +16,6 @@
namespace Slic3r
{
static constexpr float flat_iron_area = 4.f;
constexpr float flat_iron_speed = 10.f * 60.f;
static const double wipe_tower_wall_infill_overlap = 0.0;
static constexpr double WIPE_TOWER_RESOLUTION = 0.1;
@@ -1243,7 +1242,8 @@ WipeTower::ToolChangeResult WipeTower::construct_tcr(WipeTowerWriter& writer,
size_t old_tool,
bool is_finish,
bool is_tool_change,
float purge_volume) const
float purge_volume,
bool is_contact) const
{
ToolChangeResult result;
result.priming = priming;
@@ -1260,6 +1260,7 @@ WipeTower::ToolChangeResult WipeTower::construct_tcr(WipeTowerWriter& writer,
result.is_finish_first = is_finish;
result.nozzle_change_result = m_nozzle_change_result;
result.is_tool_change = is_tool_change;
result.is_contact = is_contact;
result.tool_change_start_pos = is_tool_change ? result.start_pos : Vec2f(0, 0);
// BBS
@@ -1283,6 +1284,7 @@ WipeTower::ToolChangeResult WipeTower::construct_block_tcr(WipeTowerWriter &writ
result.wipe_path = std::move(writer.wipe_path());
result.is_finish_first = is_finish;
result.is_tool_change = false;
result.is_contact = false;
result.tool_change_start_pos = Vec2f(0, 0);
// BBS
result.purge_volume = purge_volume;
@@ -1486,7 +1488,9 @@ WipeTower::WipeTower(const PrintConfig& config, int plate_idx, Vec3d plate_origi
m_used_fillet(config.wipe_tower_fillet_wall.value),
m_extra_spacing((float)config.prime_tower_infill_gap.value/100.f),
m_tower_framework(config.prime_tower_enable_framework.value),
m_flat_ironing(config.prime_tower_flat_ironing.value)
m_flat_ironing(config.prime_tower_flat_ironing.value),
m_enable_tower_interface_features(config.enable_tower_interface_features.value),
m_enable_tower_interface_cooldown_during_tower(config.enable_tower_interface_cooldown_during_tower.value)
{
m_flat_ironing = (m_flat_ironing && m_use_gap_wall);
// Read absolute value of first layer speed, if given as percentage,
@@ -1546,6 +1550,16 @@ void WipeTower::set_extruder(size_t idx, const PrintConfig& config)
m_filpar[idx].nozzle_temperature = config.nozzle_temperature.get_at(idx);
m_filpar[idx].nozzle_temperature_initial_layer = config.nozzle_temperature_initial_layer.get_at(idx);
m_filpar[idx].category = config.filament_adhesiveness_category.get_at(idx);
{
int interface_temp = config.filament_tower_interface_print_temp.get_at(idx);
if (interface_temp == -1)
interface_temp = config.nozzle_temperature_range_high.get_at(idx);
m_filpar[idx].interface_print_temperature = interface_temp;
}
m_filpar[idx].tower_interface_pre_extrusion_dist = config.filament_tower_interface_pre_extrusion_dist.get_at(idx);
m_filpar[idx].tower_interface_pre_extrusion_length = config.filament_tower_interface_pre_extrusion_length.get_at(idx);
m_filpar[idx].tower_ironing_area = config.filament_tower_ironing_area.get_at(idx);
m_filpar[idx].tower_interface_purge_length = config.filament_tower_interface_purge_volume.get_at(idx);
// If this is a single extruder MM printer, we will use all the SE-specific config values.
// Otherwise, the defaults will be used to turn off the SE stuff.
@@ -1608,7 +1622,7 @@ std::vector<WipeTower::ToolChangeResult> WipeTower::prime(
return std::vector<ToolChangeResult>();
}
Vec2f WipeTower::get_next_pos(const WipeTower::box_coordinates &cleaning_box, float wipe_length)
Vec2f WipeTower::get_next_pos(const WipeTower::box_coordinates &cleaning_box, float wipe_length, bool interface_layer, size_t interface_tool)
{
const float &xl = cleaning_box.ld.x();
const float &xr = cleaning_box.rd.x();
@@ -1740,7 +1754,7 @@ WipeTower::ToolChangeResult WipeTower::tool_change(size_t tool, bool extrude_per
}
}
Vec2f initial_position = get_next_pos(cleaning_box, wipe_length);
Vec2f initial_position = get_next_pos(cleaning_box, wipe_length, false, tool);
writer.set_initial_position(initial_position, m_wipe_tower_width, m_wipe_tower_depth, m_internal_rotation);
if (extrude_perimeter) {
@@ -1796,7 +1810,7 @@ WipeTower::ToolChangeResult WipeTower::tool_change(size_t tool, bool extrude_per
if (m_current_tool < m_used_filament_length.size())
m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
return construct_tcr(writer, false, old_tool, false, true, purge_volume);
return construct_tcr(writer, false, old_tool, false, true, purge_volume, false);
}
WipeTower::NozzleChangeResult WipeTower::nozzle_change(int old_filament_id, int new_filament_id)
@@ -2284,6 +2298,8 @@ WipeTower::ToolChangeResult WipeTower::finish_layer(bool extrude_perimeter, bool
bool first_layer = is_first_layer();
// BBS: speed up perimeter speed to 90mm/s for non-first layer
float feedrate = first_layer ? std::min(m_first_layer_speed * 60.f, 5400.f) : std::min(60.0f * m_filpar[m_current_tool].max_e_speed / m_extrusion_flow, 5400.f);
if (m_enable_tower_interface_features && m_prev_layer_had_interface)
feedrate = std::min(feedrate, 20.f * 60.f);
float fill_box_y = m_layer_info->toolchanges_depth() + m_perimeter_width;
box_coordinates fill_box(Vec2f(m_perimeter_width, fill_box_y),
m_wipe_tower_width - 2 * m_perimeter_width, m_layer_info->depth - fill_box_y);
@@ -2426,7 +2442,7 @@ WipeTower::ToolChangeResult WipeTower::finish_layer(bool extrude_perimeter, bool
if (m_current_tool < m_used_filament_length.size())
m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
return construct_tcr(writer, false, old_tool, true, false, 0.f);
return construct_tcr(writer, false, old_tool, true, false, 0.f, false);
}
// Appends a toolchange into m_plan and calculates neccessary depth of the corresponding box
@@ -2646,6 +2662,7 @@ static WipeTower::ToolChangeResult merge_tcr(WipeTower::ToolChangeResult& first,
{
assert(first.new_tool == second.initial_tool);
WipeTower::ToolChangeResult out = first;
out.is_contact = first.is_contact || second.is_contact;
if ((first.end_pos - second.start_pos).norm() > (float)EPSILON) {
std::string travel_gcode = "G1 X" + Slic3r::float_to_string_decimal_point(second.start_pos.x(), 3) + " Y" +
Slic3r::float_to_string_decimal_point(second.start_pos.y(), 3) + " F5400" + "\n";
@@ -2769,6 +2786,15 @@ WipeTower::ToolChangeResult WipeTower::tool_change_new(size_t new_tool, bool sol
}
}
bool interface_layer = solid_toolchange && m_enable_tower_interface_features;
if (interface_layer && new_tool < m_filpar.size()) {
float extra_purge_length = m_filpar[new_tool].tower_interface_purge_length;
if (extra_purge_length > 0.f) {
purge_volume += extra_purge_length * m_filpar[new_tool].filament_area;
wipe_length += extra_purge_length;
}
}
WipeTowerBlock* block = get_block_by_category(m_filpar[new_tool].category, false);
if (!block) {
assert(block != nullptr);
@@ -2795,7 +2821,7 @@ WipeTower::ToolChangeResult WipeTower::tool_change_new(size_t new_tool, bool sol
// Ram the hot material out of the melt zone, retract the filament into the cooling tubes and let it cool.
if (new_tool != (unsigned int) -1) { // This is not the last change.
Vec2f initial_position = get_next_pos(cleaning_box, wipe_length);
Vec2f initial_position = get_next_pos(cleaning_box, wipe_length, interface_layer, new_tool);
writer.set_initial_position(initial_position, m_wipe_tower_width, m_wipe_tower_depth, m_internal_rotation);
writer.append(";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Wipe_Tower_Start) + "\n");
@@ -2804,6 +2830,23 @@ WipeTower::ToolChangeResult WipeTower::tool_change_new(size_t new_tool, bool sol
toolchange_Change(writer, new_tool, m_filpar[new_tool].material); // Change the tool, set a speed override for soluble and flex materials.
toolchange_Load(writer, cleaning_box);
int base_temp = is_first_layer() ? m_filpar[new_tool].nozzle_temperature_initial_layer : m_filpar[new_tool].nozzle_temperature;
if (interface_layer) {
int interface_temp = m_filpar[new_tool].interface_print_temperature;
if (interface_temp > 0 && interface_temp != base_temp)
writer.set_extruder_temp(interface_temp, true);
if (m_enable_tower_interface_cooldown_during_tower && interface_temp > 0 && interface_temp != base_temp)
writer.set_extruder_temp(base_temp, false);
float pre_dist = m_filpar[new_tool].tower_interface_pre_extrusion_dist;
float pre_len = m_filpar[new_tool].tower_interface_pre_extrusion_length;
if (pre_dist > 0.f && pre_len > 0.f) {
bool start_left = (m_cur_layer_id % 4 == 0 || m_cur_layer_id % 4 == 3);
float target_x = writer.x() + (start_left ? pre_dist : -pre_dist);
target_x = std::max(cleaning_box.ld.x(), std::min(cleaning_box.rd.x(), target_x));
writer.extrude_explicit(target_x, writer.y(), pre_len, 600.f);
}
}
if (m_is_multi_extruder && is_tpu_filament(new_tool)) {
float dy = m_layer_info->extra_spacing * m_nozzle_change_perimeter_width;
if (m_layer_info->extra_spacing < m_tpu_fixed_spacing) {
@@ -2838,6 +2881,13 @@ WipeTower::ToolChangeResult WipeTower::tool_change_new(size_t new_tool, bool sol
toolchange_wipe_new(writer, cleaning_box, wipe_length, solid_toolchange);
if (interface_layer) {
int base_temp = is_first_layer() ? m_filpar[new_tool].nozzle_temperature_initial_layer : m_filpar[new_tool].nozzle_temperature;
int interface_temp = m_filpar[new_tool].interface_print_temperature;
if (!m_enable_tower_interface_cooldown_during_tower && interface_temp > 0 && interface_temp != base_temp)
writer.set_extruder_temp(base_temp, false);
}
writer.append(";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Wipe_Tower_End) + "\n");
++m_num_tool_changes;
} else
@@ -2859,7 +2909,7 @@ WipeTower::ToolChangeResult WipeTower::tool_change_new(size_t new_tool, bool sol
if (m_current_tool < m_used_filament_length.size())
m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
return construct_tcr(writer, false, old_tool, false, true, purge_volume);
return construct_tcr(writer, false, old_tool, false, true, purge_volume, interface_layer);
}
WipeTower::NozzleChangeResult WipeTower::nozzle_change_new(int old_filament_id, int new_filament_id, bool solid_infill)
@@ -3157,7 +3207,7 @@ WipeTower::ToolChangeResult WipeTower::finish_layer_new(bool extrude_perimeter,
m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
m_nozzle_change_result.gcode.clear();
return construct_tcr(writer, false, m_current_tool, true, false, 0.f);
return construct_tcr(writer, false, m_current_tool, true, false, 0.f, false);
}
WipeTower::ToolChangeResult WipeTower::finish_block(const WipeTowerBlock &block, int filament_id, bool extrude_fill)
@@ -3350,6 +3400,8 @@ void WipeTower::toolchange_wipe_new(WipeTowerWriter &writer, const box_coordinat
}
float retract_length = m_filpar[m_current_tool].retract_length;
float retract_speed = m_filpar[m_current_tool].retract_speed * 60;
const float ironing_area = m_filpar[m_current_tool].tower_ironing_area;
const bool do_ironing = m_flat_ironing && (!solid_tool_toolchange || !m_enable_tower_interface_features);
const float &xl = cleaning_box.ld.x();
const float &xr = cleaning_box.rd.x();
@@ -3393,10 +3445,10 @@ void WipeTower::toolchange_wipe_new(WipeTowerWriter &writer, const box_coordinat
writer.extrude(writer.x() + ironing_length, writer.y(), wipe_speed);
writer.retract(retract_length, retract_speed);
writer.travel(writer.x() - 1.5 * ironing_length, writer.y(), 600.);
if (m_flat_ironing) {
if (do_ironing && ironing_area > 0.f) {
writer.travel(writer.x() + 0.5f * ironing_length, writer.y(), 240.);
Vec2f pos{writer.x() + 1.f * ironing_length, writer.y()};
writer.spiral_flat_ironing(writer.pos(), flat_iron_area, m_perimeter_width, flat_iron_speed);
writer.spiral_flat_ironing(writer.pos(), ironing_area, m_perimeter_width, flat_iron_speed);
writer.travel(pos, wipe_speed);
} else
writer.travel(writer.x() + 1.5 * ironing_length, writer.y(), 240.);
@@ -3408,10 +3460,10 @@ void WipeTower::toolchange_wipe_new(WipeTowerWriter &writer, const box_coordinat
writer.extrude(writer.x() - ironing_length, writer.y(), wipe_speed);
writer.retract(retract_length, retract_speed);
writer.travel(writer.x() + 1.5 * ironing_length, writer.y(), 600.);
if (m_flat_ironing) {
if (do_ironing && ironing_area > 0.f) {
writer.travel(writer.x() - 0.5f * ironing_length, writer.y(), 240.);
Vec2f pos{writer.x() - 1.0f * ironing_length, writer.y()};
writer.spiral_flat_ironing(writer.pos(), flat_iron_area, m_perimeter_width, flat_iron_speed);
writer.spiral_flat_ironing(writer.pos(), ironing_area, m_perimeter_width, flat_iron_speed);
writer.travel(pos, wipe_speed);
}else
writer.travel(writer.x() - 1.5 * ironing_length, writer.y(), 240.);
@@ -3837,6 +3889,8 @@ void WipeTower::generate_new(std::vector<std::vector<WipeTower::ToolChangeResult
for (auto layer : m_plan) {
reset_block_status();
m_cur_layer_id = index++;
m_prev_layer_had_interface = m_current_layer_has_interface;
m_current_layer_has_interface = !solid_blocks_id.empty();
set_layer(layer.z, layer.height, 0, false, layer.z == m_plan.back().z);
if (m_layer_info->depth < m_perimeter_width) continue;
@@ -4188,7 +4242,7 @@ WipeTower::ToolChangeResult WipeTower::only_generate_out_wall(bool is_new_mode)
if (!m_no_sparse_layers || toolchanges_on_layer)
if (m_current_tool < m_used_filament_length.size()) m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
return construct_tcr(writer, false, old_tool, true, false, 0.f);
return construct_tcr(writer, false, old_tool, true, false, 0.f, false);
}
Polygon WipeTower::generate_rib_polygon(const box_coordinates &wt_box)
+14 -3
View File
@@ -83,6 +83,7 @@ public:
bool priming;
bool is_tool_change{false};
bool is_contact{false};
Vec2f tool_change_start_pos;
// Pass a polyline so that normal G-code generator can do a wipe for us.
@@ -160,8 +161,9 @@ public:
bool priming,
size_t old_tool,
bool is_finish,
bool is_tool_change,
float purge_volume) const;
bool is_tool_change,
float purge_volume,
bool is_contact = false) const;
ToolChangeResult construct_block_tcr(WipeTowerWriter& writer,
bool priming,
@@ -319,6 +321,7 @@ public:
bool is_support = false;
int nozzle_temperature = 0;
int nozzle_temperature_initial_layer = 0;
int interface_print_temperature = 0;
float loading_speed = 0.f;
float loading_speed_start = 0.f;
float unloading_speed = 0.f;
@@ -336,6 +339,10 @@ public:
float retract_length;
float retract_speed;
float wipe_dist;
float tower_interface_pre_extrusion_dist = 0.f;
float tower_interface_pre_extrusion_length = 0.f;
float tower_ironing_area = 4.f;
float tower_interface_purge_length = 0.f;
};
@@ -492,6 +499,10 @@ private:
std::map<float,Polylines> m_outer_wall;
bool is_first_layer() const { return size_t(m_layer_info - m_plan.begin()) == m_first_layer_idx; }
bool m_flat_ironing=false;
bool m_enable_tower_interface_features=false;
bool m_enable_tower_interface_cooldown_during_tower=false;
bool m_prev_layer_had_interface=false;
bool m_current_layer_has_interface=false;
// Calculates length of extrusion line to extrude given volume
float volume_to_length(float volume, float line_width, float layer_height) const {
return std::max(0.f, volume / (layer_height * (line_width - layer_height * (1.f - float(M_PI) / 4.f))));
@@ -503,7 +514,7 @@ private:
// Goes through m_plan and recalculates depths and width of the WT to make it exactly square - experimental
void make_wipe_tower_square();
Vec2f get_next_pos(const WipeTower::box_coordinates &cleaning_box, float wipe_length);
Vec2f get_next_pos(const WipeTower::box_coordinates &cleaning_box, float wipe_length, bool interface_layer, size_t interface_tool);
// Goes through m_plan, calculates border and finish_layer extrusions and subtracts them from last wipe
void save_on_last_wipe();
+112 -12
View File
@@ -972,6 +972,25 @@ public:
return add_wipe_point(Vec2f(x, y));
}
void spiral_flat_ironing(const Vec2f &center, float area, float step_length, float feedrate)
{
float edge_length = std::sqrt(area);
Vec2f box_max = center + Vec2f{step_length, step_length};
Vec2f box_min = center - Vec2f{step_length, step_length};
int n = std::ceil(edge_length / step_length / 2.f);
if (n <= 0)
return;
while (n--) {
travel(box_max.x(), m_current_pos.y(), feedrate);
travel(m_current_pos.x(), box_max.y(), feedrate);
travel(box_min.x(), m_current_pos.y(), feedrate);
travel(m_current_pos.x(), box_min.y(), feedrate);
box_max += Vec2f{step_length, step_length};
box_min -= Vec2f{step_length, step_length};
}
}
// Extrude with an explicitely provided amount of extrusion.
WipeTowerWriter2& extrude_arc_explicit(ArcSegment& arc,
float f = 0.f,
@@ -1200,7 +1219,8 @@ private:
WipeTower::ToolChangeResult WipeTower2::construct_tcr(WipeTowerWriter2& writer,
bool priming,
size_t old_tool,
bool is_finish) const
bool is_finish,
bool is_contact) const
{
WipeTower::ToolChangeResult result;
result.priming = priming;
@@ -1215,6 +1235,7 @@ WipeTower::ToolChangeResult WipeTower2::construct_tcr(WipeTowerWriter2& writer,
result.extrusions = std::move(writer.extrusions());
result.wipe_path = std::move(writer.wipe_path());
result.is_finish_first = is_finish;
result.is_contact = is_contact;
// ORCA: Always initialize the tool_change_start_pos with a valid position
// to avoid undefined variable travel on X in Gcode.cpp function std::string WipeTowerIntegration::post_process_wipe_tower_moves
result.tool_change_start_pos = result.start_pos; // always valid fallback
@@ -1250,7 +1271,10 @@ WipeTower2::WipeTower2(const PrintConfig& config, const PrintRegionConfig& defau
m_used_fillet(config.wipe_tower_fillet_wall),
m_rib_width(config.wipe_tower_rib_width),
m_extra_rib_length(config.wipe_tower_extra_rib_length),
m_wall_type((int)config.wipe_tower_wall_type)
m_wall_type((int)config.wipe_tower_wall_type),
m_flat_ironing(config.prime_tower_flat_ironing.value),
m_enable_tower_interface_features(config.enable_tower_interface_features.value),
m_enable_tower_interface_cooldown_during_tower(config.enable_tower_interface_cooldown_during_tower.value)
{
// Read absolute value of first layer speed, if given as percentage,
// it is taken over following default. Speeds from config are not
@@ -1317,6 +1341,16 @@ void WipeTower2::set_extruder(size_t idx, const PrintConfig& config)
m_filpar[idx].temperature = config.nozzle_temperature.get_at(idx);
m_filpar[idx].first_layer_temperature = config.nozzle_temperature_initial_layer.get_at(idx);
m_filpar[idx].filament_minimal_purge_on_wipe_tower = config.filament_minimal_purge_on_wipe_tower.get_at(idx);
{
int interface_temp = config.filament_tower_interface_print_temp.get_at(idx);
if (interface_temp == -1)
interface_temp = config.nozzle_temperature_range_high.get_at(idx);
m_filpar[idx].interface_print_temperature = interface_temp;
}
m_filpar[idx].tower_interface_pre_extrusion_dist = config.filament_tower_interface_pre_extrusion_dist.get_at(idx);
m_filpar[idx].tower_interface_pre_extrusion_length = config.filament_tower_interface_pre_extrusion_length.get_at(idx);
m_filpar[idx].tower_ironing_area = config.filament_tower_ironing_area.get_at(idx);
m_filpar[idx].tower_interface_purge_length = config.filament_tower_interface_purge_volume.get_at(idx);
// If this is a single extruder MM printer, we will use all the SE-specific config values.
// Otherwise, the defaults will be used to turn off the SE stuff.
@@ -1440,11 +1474,11 @@ std::vector<WipeTower::ToolChangeResult> WipeTower2::prime(
toolchange_Load(writer, cleaning_box); // Prime the tool.
if (idx_tool + 1 == tools.size()) {
// Last tool should not be unloaded, but it should be wiped enough to become of a pure color.
toolchange_Wipe(writer, cleaning_box, wipe_volumes[tools[idx_tool-1]][tool]);
toolchange_Wipe(writer, cleaning_box, wipe_volumes[tools[idx_tool-1]][tool], false);
} else {
// Ram the hot material out of the melt zone, retract the filament into the cooling tubes and let it cool.
//writer.travel(writer.x(), writer.y() + m_perimeter_width, 7200);
toolchange_Wipe(writer, cleaning_box , 20.f);
toolchange_Wipe(writer, cleaning_box , 20.f, false);
WipeTower::box_coordinates box = cleaning_box;
box.translate(0.f, writer.y() - cleaning_box.ld.y() + m_perimeter_width);
toolchange_Unload(writer, box , m_filpar[m_current_tool].material, m_filpar[m_current_tool].first_layer_temperature, m_filpar[tools[idx_tool + 1]].first_layer_temperature);
@@ -1472,7 +1506,7 @@ std::vector<WipeTower::ToolChangeResult> WipeTower2::prime(
"\n\n");
}
results.emplace_back(construct_tcr(writer, true, old_tool, true));
results.emplace_back(construct_tcr(writer, true, old_tool, true, false));
}
m_old_temperature = -1; // If the priming is turned off in config, the temperature changing commands will not actually appear
@@ -1487,6 +1521,7 @@ WipeTower::ToolChangeResult WipeTower2::tool_change(size_t tool)
float wipe_area = 0.f;
float wipe_volume = 0.f;
bool interface_layer = m_enable_tower_interface_features && m_current_layer_has_interface;
// Finds this toolchange info
if (tool != (unsigned int)(-1))
@@ -1501,6 +1536,12 @@ WipeTower::ToolChangeResult WipeTower2::tool_change(size_t tool)
else {
// Otherwise we are going to Unload only. And m_layer_info would be invalid.
}
if (interface_layer && tool != (unsigned int)(-1) && tool < m_filpar.size()) {
float extra_purge_length = m_filpar[tool].tower_interface_purge_length;
if (extra_purge_length > 0.f) {
wipe_volume += extra_purge_length * m_filpar[tool].filament_area;
}
}
WipeTower::box_coordinates cleaning_box(
Vec2f(m_perimeter_width / 2.f, m_perimeter_width / 2.f),
@@ -1542,7 +1583,27 @@ WipeTower::ToolChangeResult WipeTower2::tool_change(size_t tool)
toolchange_Change(writer, tool, m_filpar[tool].material); // Change the tool, set a speed override for soluble and flex materials.
toolchange_Load(writer, cleaning_box);
writer.travel(writer.x(), writer.y()-m_perimeter_width); // cooling and loading were done a bit down the road
toolchange_Wipe(writer, cleaning_box, wipe_volume); // Wipe the newly loaded filament until the end of the assigned wipe area.
int base_temp = is_first_layer() ? m_filpar[tool].first_layer_temperature : m_filpar[tool].temperature;
if (interface_layer) {
int interface_temp = m_filpar[tool].interface_print_temperature;
if (interface_temp > 0 && interface_temp != base_temp)
writer.set_extruder_temp(interface_temp, true);
if (m_enable_tower_interface_cooldown_during_tower && interface_temp > 0 && interface_temp != base_temp)
writer.set_extruder_temp(base_temp, false);
float pre_dist = m_filpar[tool].tower_interface_pre_extrusion_dist;
float pre_len = m_filpar[tool].tower_interface_pre_extrusion_length;
if (pre_dist > 0.f && pre_len > 0.f) {
float target_x = writer.x() + pre_dist;
target_x = std::max(cleaning_box.ld.x(), std::min(cleaning_box.rd.x(), target_x));
writer.extrude_explicit(target_x, writer.y(), pre_len, 600.f);
}
}
toolchange_Wipe(writer, cleaning_box, wipe_volume, interface_layer); // Wipe the newly loaded filament until the end of the assigned wipe area.
if (interface_layer) {
int interface_temp = m_filpar[tool].interface_print_temperature;
if (!m_enable_tower_interface_cooldown_during_tower && interface_temp > 0 && interface_temp != base_temp)
writer.set_extruder_temp(base_temp, false);
}
writer.append(";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Wipe_Tower_End) + "\n");
++ m_num_tool_changes;
} else
@@ -1564,7 +1625,7 @@ WipeTower::ToolChangeResult WipeTower2::tool_change(size_t tool)
if (m_current_tool < m_used_filament_length.size())
m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
return construct_tcr(writer, false, old_tool, false);
return construct_tcr(writer, false, old_tool, false, interface_layer);
}
@@ -1854,7 +1915,8 @@ void WipeTower2::toolchange_Load(
void WipeTower2::toolchange_Wipe(
WipeTowerWriter2 &writer,
const WipeTower::box_coordinates &cleaning_box,
float wipe_volume)
float wipe_volume,
bool interface_layer)
{
// Increase flow on first layer, slow down print.
writer.set_extrusion_flow(m_extrusion_flow * (is_first_layer() ? 1.18f : 1.f))
@@ -1884,6 +1946,9 @@ void WipeTower2::toolchange_Wipe(
m_left_to_right = !m_left_to_right;
}
const bool do_ironing = m_flat_ironing && (!interface_layer || !m_enable_tower_interface_features);
const float ironing_area = m_filpar[m_current_tool].tower_ironing_area;
// now the wiping itself:
for (int i = 0; true; ++i) {
if (i!=0) {
@@ -1899,6 +1964,11 @@ void WipeTower2::toolchange_Wipe(
else
writer.extrude(xl + (i % 4 == 1 ? 0 : 1.5f*line_width), writer.y(), wipe_speed);
if (i == 0 && do_ironing && ironing_area > 0.f) {
writer.travel(writer.x(), writer.y(), 600.f);
writer.spiral_flat_ironing(writer.pos(), ironing_area, m_perimeter_width, 10.f * 60.f);
}
if (writer.y()+float(EPSILON) > cleaning_box.lu.y()-0.5f*line_width)
break; // in case next line would not fit
@@ -1943,10 +2013,12 @@ WipeTower::ToolChangeResult WipeTower2::finish_layer()
.set_y_shift(m_y_shift - (m_current_shape == SHAPE_REVERSED ? m_layer_info->toolchanges_depth() : 0.f));
// Slow down on the 1st layer.
// Slow down on the 1st layer.
// If spare layers are excluded -> if 1 or less toolchange has been done, it must be still the first layer, too. So slow down.
bool first_layer = is_first_layer() || (m_num_tool_changes <= 1 && m_no_sparse_layers);
float feedrate = first_layer ? m_first_layer_speed * 60.f : std::min(m_wipe_tower_max_purge_speed * 60.f, m_infill_speed * 60.f);
if (m_enable_tower_interface_features && m_prev_layer_had_interface)
feedrate = std::min(feedrate, 20.f * 60.f);
float current_depth = m_layer_info->depth - m_layer_info->toolchanges_depth();
WipeTower::box_coordinates fill_box(Vec2f(m_perimeter_width, m_layer_info->depth-(current_depth-m_perimeter_width)),
m_wipe_tower_width - 2 * m_perimeter_width, current_depth-m_perimeter_width);
@@ -2062,6 +2134,11 @@ WipeTower::ToolChangeResult WipeTower2::finish_layer()
// Save actual brim width to be later passed to the Print object, which will use it
// for skirt calculation and pass it to GLCanvas for precise preview box
m_wipe_tower_brim_width_real = loops_num * spacing;
// Compute actual first-layer bounding box from the outermost brim polygon,
// matching how WipeTower::get_bbx() uses m_outer_wall extents.
BoundingBox first_layer_box = get_extents(poly);
m_first_layer_bbx = BoundingBoxf(unscale(first_layer_box.min), unscale(first_layer_box.max));
}
// Now prepare future wipe.
@@ -2077,7 +2154,7 @@ WipeTower::ToolChangeResult WipeTower2::finish_layer()
m_current_height += m_layer_info->height;
}
return construct_tcr(writer, false, old_tool, true);
return construct_tcr(writer, false, old_tool, true, false);
}
// Static method to get the radius and x-scaling of the stabilizing cone base.
@@ -2156,7 +2233,18 @@ void WipeTower2::plan_toolchange(float z_par, float layer_height_par, unsigned i
float first_wipe_line = - (width*((length_to_extrude / width)-int(length_to_extrude / width)) - width);
float first_wipe_volume = length_to_volume(first_wipe_line, m_perimeter_width * m_extra_flow, layer_height_par);
float wiping_depth = get_wipe_depth(wipe_volume - first_wipe_volume, layer_height_par, m_perimeter_width, m_extra_flow, m_extra_spacing_wipe, width);
// ORCA: Keep wipe-depth planning consistent with toolchange_Wipe().
// ORCA: On the first layer, toolchange_Wipe() advances purge rows using
// ORCA: m_extra_flow * m_perimeter_width, while later layers use
// ORCA: m_extra_spacing_wipe * m_perimeter_width.
// ORCA: float dy = (is_first_layer() ? m_extra_flow : m_extra_spacing_wipe) * m_perimeter_width;
// ORCA: Use the same spacing here so reserved depth matches consumed depth
// ORCA: and first-layer purge segments do not leave visible gaps.
const bool first_layer_plan = (m_plan.size() - 1) == m_first_layer_idx;
const float planning_spacing = first_layer_plan ? m_extra_flow : m_extra_spacing_wipe;
float wiping_depth = get_wipe_depth(wipe_volume - first_wipe_volume, layer_height_par, m_perimeter_width, m_extra_flow, planning_spacing, width);
m_plan.back().tool_changes.push_back(WipeTowerInfo::ToolChange(old_tool, new_tool, ramming_depth + wiping_depth, ramming_depth, first_wipe_line, wipe_volume));
}
@@ -2213,7 +2301,18 @@ void WipeTower2::save_on_last_wipe()
float volume_to_save = length_to_volume(finish_layer().total_extrusion_length_in_plane(), m_perimeter_width, m_layer_info->height);
float volume_left_to_wipe = std::max(m_filpar[toolchange.new_tool].filament_minimal_purge_on_wipe_tower, toolchange.wipe_volume_total - volume_to_save);
float volume_we_need_depth_for = std::max(0.f, volume_left_to_wipe - length_to_volume(toolchange.first_wipe_line, m_perimeter_width*m_extra_flow, m_layer_info->height));
float depth_to_wipe = get_wipe_depth(volume_we_need_depth_for, m_layer_info->height, m_perimeter_width, m_extra_flow, m_extra_spacing_wipe, width);
// ORCA: Keep wipe-depth planning consistent with toolchange_Wipe().
// ORCA: On the first layer, toolchange_Wipe() advances purge rows using
// ORCA: m_extra_flow * m_perimeter_width, while later layers use
// ORCA: m_extra_spacing_wipe * m_perimeter_width.
// ORCA: float dy = (is_first_layer() ? m_extra_flow : m_extra_spacing_wipe) * m_perimeter_width;
// ORCA: Use the same spacing here so reserved depth matches consumed depth
// ORCA: and first-layer purge segments do not leave visible gaps.
const bool first_layer_plan = size_t(m_layer_info - m_plan.begin()) == m_first_layer_idx;
const float planning_spacing = first_layer_plan ? m_extra_flow : m_extra_spacing_wipe;
float depth_to_wipe = get_wipe_depth(volume_we_need_depth_for, m_layer_info->height, m_perimeter_width, m_extra_flow, planning_spacing, width);
toolchange.required_depth = toolchange.ramming_depth + depth_to_wipe;
toolchange.wipe_volume = volume_left_to_wipe;
@@ -2245,6 +2344,7 @@ static WipeTower::ToolChangeResult merge_tcr(WipeTower::ToolChangeResult& first,
{
assert(first.new_tool == second.initial_tool);
WipeTower::ToolChangeResult out = first;
out.is_contact = first.is_contact || second.is_contact;
if (first.end_pos != second.start_pos)
out.gcode += "G1 X" + Slic3r::float_to_string_decimal_point(second.start_pos.x(), 3)
+ " Y" + Slic3r::float_to_string_decimal_point(second.start_pos.y(), 3)
+25 -7
View File
@@ -31,7 +31,8 @@ public:
WipeTower::ToolChangeResult construct_tcr(WipeTowerWriter2& writer,
bool priming,
size_t old_tool,
bool is_finish) const;
bool is_finish,
bool is_contact = false) const;
// x -- x coordinates of wipe tower in mm ( left bottom corner )
// y -- y coordinates of wipe tower in mm ( left bottom corner )
@@ -60,16 +61,19 @@ public:
float get_wipe_tower_height() const { return m_wipe_tower_height; }
// ORCA: Match WipeTower API used by Print skirt/brim planning.
// Returned bounding box is in WIPE-TOWER-LOCAL coordinates (before placement on the bed).
// Include brim and y-shift to match what WT gcode actually prints.
BoundingBoxf get_bbx() const{
// Computed from the actual first-layer polygon (including brim), like WipeTower::get_bbx().
BoundingBoxf get_bbx() const {
if (m_first_layer_bbx.defined)
return m_first_layer_bbx;
// Fallback: nominal rectangle (used if generate() hasn't run yet)
const float brim = m_wipe_tower_brim_width_real;
const Vec2d min(-brim, -brim + double(m_y_shift));
const Vec2d max(double(m_wipe_tower_width) + brim, double(m_wipe_tower_depth) + brim + double(m_y_shift));
return BoundingBoxf(min, max);
return BoundingBoxf(Vec2d(-brim, -brim), Vec2d(double(m_wipe_tower_width) + brim, double(m_wipe_tower_depth) + brim));
}
// WT2 doesn't currently compute a rib-origin compensation like WipeTower (m_rib_offset),
// so expose a zero offset for consistency purposes (to maintain API parity).
Vec2f get_rib_offset() const { return Vec2f::Zero(); }
float get_rib_width() const { return m_rib_width; }
float get_rib_length() const { return m_rib_length; }
// Switch to a next layer.
void set_layer(
@@ -88,11 +92,13 @@ public:
m_layer_height = layer_height;
m_depth_traversed = 0.f;
m_current_layer_finished = false;
m_prev_layer_had_interface = m_current_layer_has_interface;
// Advance m_layer_info iterator, making sure we got it right
while (!m_plan.empty() && m_layer_info->z < print_z - WT_EPSILON && m_layer_info+1 != m_plan.end())
++m_layer_info;
m_current_layer_has_interface = (m_layer_info != m_plan.end()) && (m_layer_info->toolchanges_depth() > WT_EPSILON);
//m_current_shape = (! this->is_first_layer() && m_current_shape == SHAPE_NORMAL) ? SHAPE_REVERSED : SHAPE_NORMAL;
m_current_shape = SHAPE_NORMAL;
@@ -145,6 +151,7 @@ public:
bool is_soluble = false;
int temperature = 0;
int first_layer_temperature = 0;
int interface_print_temperature = 0;
float loading_speed = 0.f;
float loading_speed_start = 0.f;
float unloading_speed = 0.f;
@@ -168,6 +175,10 @@ public:
float filament_minimal_purge_on_wipe_tower = 0.f;
float retract_length;
float retract_speed;
float tower_interface_pre_extrusion_dist = 0.f;
float tower_interface_pre_extrusion_length = 0.f;
float tower_ironing_area = 4.f;
float tower_interface_purge_length = 0.f;
};
private:
@@ -195,6 +206,7 @@ private:
float m_wipe_tower_cone_angle = 0.f;
float m_wipe_tower_brim_width = 0.f; // Width of brim (mm) from config
float m_wipe_tower_brim_width_real = 0.f; // Width of brim (mm) after generation
BoundingBoxf m_first_layer_bbx; // Actual first-layer bounding box (incl. brim/ribs)
float m_wipe_tower_rotation_angle = 0.f; // Wipe tower rotation angle in degrees (with respect to x axis)
float m_internal_rotation = 0.f;
float m_y_shift = 0.f; // y shift passed to writer
@@ -208,6 +220,11 @@ private:
float m_perimeter_speed = 0.f;
float m_first_layer_speed = 0.f;
size_t m_first_layer_idx = size_t(-1);
bool m_flat_ironing = false;
bool m_enable_tower_interface_features = false;
bool m_enable_tower_interface_cooldown_during_tower = false;
bool m_prev_layer_had_interface = false;
bool m_current_layer_has_interface = false;
int m_wall_type;
bool m_used_fillet = true;
@@ -335,7 +352,8 @@ private:
void toolchange_Wipe(
WipeTowerWriter2 &writer,
const WipeTower::box_coordinates &cleaning_box,
float wipe_volume);
float wipe_volume,
bool interface_layer);
Polygon generate_support_rib_wall(WipeTowerWriter2& writer,
+5 -1
View File
@@ -49,6 +49,9 @@ void GCodeWriter::set_extruders(std::vector<unsigned int> extruder_ids)
{
std::sort(extruder_ids.begin(), extruder_ids.end());
m_filament_extruders.clear();
//ORCA: Reset current extruder ID and clear pointers to prevent dangling pointers when extruders are recreated.
m_curr_extruder_id = -1;
std::fill(m_curr_filament_extruder.begin(), m_curr_filament_extruder.end(), nullptr);
m_filament_extruders.reserve(extruder_ids.size());
for (unsigned int extruder_id : extruder_ids)
m_filament_extruders.emplace_back(Extruder(extruder_id, &this->config, config.single_extruder_multi_material.value));
@@ -56,7 +59,8 @@ void GCodeWriter::set_extruders(std::vector<unsigned int> extruder_ids)
/* we enable support for multiple extruder if any extruder greater than 0 is used
(even if prints only uses that one) since we need to output Tx commands
first extruder has index 0 */
this->multiple_extruders = (*std::max_element(extruder_ids.begin(), extruder_ids.end())) > 0;
//ORCA: Fix undefined behavior by checking if the vector is empty before taking max_element.
this->multiple_extruders = !extruder_ids.empty() && (*std::max_element(extruder_ids.begin(), extruder_ids.end())) > 0;
}
std::string GCodeWriter::preamble()
+1 -1
View File
@@ -343,7 +343,7 @@ void segment(CGALMesh& src, std::vector<CGALMesh>& dst, double smoothing_alpha =
std::cout << "* Number of facets in constructed patch: " << patch_facets.size() << std::endl;
std::cout << " Number of vertices in constructed patch: " << patch_vertices.size() << std::endl;
#else
CGAL::Polygon_mesh_processing::triangulate_hole(out, h, std::back_inserter(patch_facets));
CGAL::Polygon_mesh_processing::triangulate_hole(out, h, CGAL::parameters::default_values().face_output_iterator(std::back_inserter(patch_facets)));
#endif
}
+1 -1
View File
@@ -1170,7 +1170,7 @@ ModelObject& ModelObject::assign_copy(ModelObject &&rhs)
this->sla_support_points = std::move(rhs.sla_support_points);
this->sla_points_status = std::move(rhs.sla_points_status);
this->sla_drain_holes = std::move(rhs.sla_drain_holes);
this->brim_points = std::move(brim_points);
this->brim_points = std::move(rhs.brim_points);
this->layer_config_ranges = std::move(rhs.layer_config_ranges);
this->layer_height_profile = std::move(rhs.layer_height_profile);
this->printable = std::move(rhs.printable);
+1 -1
View File
@@ -464,7 +464,7 @@ public:
}
cost += (costs.bottom < params.BOTTOM_MIN) * 100;// +(costs.height_to_bottom_hull_ratio > params.height_to_bottom_hull_ratio_MIN) * 110;
costs.unprintability = costs.unprintability = cost;
costs.unprintability = cost;
return cost;
}
+4
View File
@@ -923,6 +923,8 @@ static std::vector<std::string> s_Preset_print_options {
"prime_tower_width", "prime_tower_brim_width", "prime_tower_skip_points", "prime_volume",
"prime_tower_infill_gap",
"prime_tower_flat_ironing",
"enable_tower_interface_features",
"enable_tower_interface_cooldown_during_tower",
"wipe_tower_no_sparse_layers", "compatible_printers", "compatible_printers_condition", "inherits",
"flush_into_infill", "flush_into_objects", "flush_into_support",
"tree_support_branch_angle", "tree_support_angle_slow", "tree_support_wall_count", "tree_support_top_rate", "tree_support_branch_distance", "tree_support_tip_diameter",
@@ -961,6 +963,8 @@ static std::vector<std::string> s_Preset_filament_options {/*"filament_colour",
"filament_soluble", "filament_is_support", "filament_printable",
"filament_max_volumetric_speed", "filament_adaptive_volumetric_speed",
"filament_flow_ratio", "filament_density", "filament_adhesiveness_category", "filament_cost", "filament_minimal_purge_on_wipe_tower",
"filament_tower_interface_pre_extrusion_dist", "filament_tower_interface_pre_extrusion_length", "filament_tower_ironing_area", "filament_tower_interface_purge_volume",
"filament_tower_interface_print_temp",
"nozzle_temperature", "nozzle_temperature_initial_layer",
// BBS
"cool_plate_temp", "textured_cool_plate_temp", "eng_plate_temp", "hot_plate_temp", "textured_plate_temp", "cool_plate_temp_initial_layer", "textured_cool_plate_temp_initial_layer", "eng_plate_temp_initial_layer", "hot_plate_temp_initial_layer", "textured_plate_temp_initial_layer", "supertack_plate_temp_initial_layer", "supertack_plate_temp",
+61 -21
View File
@@ -317,6 +317,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|| opt_key == "prime_tower_brim_width"
|| opt_key == "prime_tower_skip_points"
|| opt_key == "prime_tower_flat_ironing"
|| opt_key == "enable_tower_interface_features"
|| opt_key == "first_layer_print_sequence"
|| opt_key == "other_layers_print_sequence"
|| opt_key == "other_layers_print_sequence_nums"
@@ -324,6 +325,11 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|| opt_key == "filament_map_mode"
|| opt_key == "filament_map"
|| opt_key == "filament_adhesiveness_category"
|| opt_key == "filament_tower_interface_pre_extrusion_dist"
|| opt_key == "filament_tower_interface_pre_extrusion_length"
|| opt_key == "filament_tower_ironing_area"
|| opt_key == "filament_tower_interface_purge_volume"
|| opt_key == "filament_tower_interface_print_temp"
|| opt_key == "wipe_tower_bridging"
|| opt_key == "wipe_tower_extra_flow"
|| opt_key == "wipe_tower_no_sparse_layers"
@@ -1231,7 +1237,7 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons*
} else {
if (m_config.enable_wrapping_detection && warning!=nullptr) {
StringObjectException warningtemp;
warningtemp.string = L("Prime tower is required for clumping detection; otherwise, there may be flaws on the model.");
warningtemp.string = L("A prime tower is required for clumping detection; otherwise, there may be flaws on the model.");
warningtemp.opt_key = "enable_prime_tower";
warningtemp.is_warning = true;
*warning = warningtemp;
@@ -1484,18 +1490,42 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons*
// Prusa: Fixing crashes with invalid tip diameter or branch diameter
// https://github.com/prusa3d/PrusaSlicer/commit/96b3ae85013ac363cd1c3e98ec6b7938aeacf46d
if (is_tree(object->config().support_type.value) && (object->config().support_style == smsTreeOrganic ||
// Orca: use organic as default
object->config().support_style == smsDefault)) {
float extrusion_width = std::min(
support_material_flow(object).width(),
support_material_interface_flow(object).width());
if (object->config().tree_support_tip_diameter < extrusion_width - EPSILON)
return { L("Organic support tree tip diameter must not be smaller than support material extrusion width."), object, "tree_support_tip_diameter" };
if (object->config().tree_support_branch_diameter_organic < 2. * extrusion_width - EPSILON)
return { L("Organic support branch diameter must not be smaller than 2x support material extrusion width."), object, "tree_support_branch_diameter_organic" };
if (object->config().tree_support_branch_diameter_organic < object->config().tree_support_tip_diameter)
return { L("Organic support branch diameter must not be smaller than support tree tip diameter."), object, "tree_support_branch_diameter_organic" };
if (is_tree(object->config().support_type.value)) {
if (object->config().support_style == smsTreeOrganic ||
// Orca: use organic as default
object->config().support_style == smsDefault) {
if (warning) {
// Orca: check the support wall count and the base pattern
if (object->config().tree_support_wall_count > 1 &&
object->config().support_base_pattern != SupportMaterialPattern::smpNone &&
object->config().support_base_pattern != SupportMaterialPattern::smpDefault) {
warning->string = L("For Organic supports, two walls are supported only with the Hollow/Default base pattern.");
warning->opt_key = "support_base_pattern";
}
// Orca: check if the Lightning base pattern selected
if (object->config().support_base_pattern == SupportMaterialPattern::smpLightning) {
warning->string = L(
"The Lightning base pattern is not supported by this support type; Rectilinear will be used instead.");
warning->opt_key = "support_base_pattern";
}
}
float extrusion_width = std::min(
support_material_flow(object).width(),
support_material_interface_flow(object).width());
if (object->config().tree_support_tip_diameter < extrusion_width - EPSILON)
return { L("Organic support tree tip diameter must not be smaller than support material extrusion width."), object, "tree_support_tip_diameter" };
if (object->config().tree_support_branch_diameter_organic < 2. * extrusion_width - EPSILON)
return { L("Organic support branch diameter must not be smaller than 2x support material extrusion width."), object, "tree_support_branch_diameter_organic" };
if (object->config().tree_support_branch_diameter_organic < object->config().tree_support_tip_diameter)
return { L("Organic support branch diameter must not be smaller than support tree tip diameter."), object, "tree_support_branch_diameter_organic" };
}
} else if (object->config().support_base_pattern == SupportMaterialPattern::smpLightning && warning) {
// Orca: check if the Lightning base pattern selected
warning->string = L("The Lightning base pattern is not supported by this support type; Rectilinear will be used instead.");
warning->opt_key = "support_base_pattern";
}
}
@@ -2739,14 +2769,6 @@ Vec2d Print::translate_to_print_space(const Point &point) const {
FilamentTempType Print::get_filament_temp_type(const std::string& filament_type)
{
// FilamentTempType Temperature-based logic
int min_temp, max_temp;
if (MaterialType::get_temperature_range(filament_type, min_temp, max_temp)) {
if (max_temp <= 250) return FilamentTempType::LowTemp;
else if (max_temp < 280) return FilamentTempType::HighLowCompatible;
else return FilamentTempType::HighTemp;
}
const static std::string HighTempFilamentStr = "high_temp_filament";
const static std::string LowTempFilamentStr = "low_temp_filament";
const static std::string HighLowCompatibleFilamentStr = "high_low_compatible_filament";
@@ -2781,6 +2803,19 @@ FilamentTempType Print::get_filament_temp_type(const std::string& filament_type)
return HighTemp;
if (filament_temp_type_map[LowTempFilamentStr].find(filament_type) != filament_temp_type_map[LowTempFilamentStr].end())
return LowTemp;
// Orca: prefer explicit definition from JSON, if the filament type is not defined in json, fallback to temperature-based logic to determine the filament temp type.
// FilamentTempType Temperature-based logic
int min_temp, max_temp;
if (MaterialType::get_temperature_range(filament_type, min_temp, max_temp)) {
if (max_temp <= 250)
return FilamentTempType::LowTemp;
else if (max_temp < 280)
return FilamentTempType::HighLowCompatible;
else
return FilamentTempType::HighTemp;
}
return Undefine;
}
@@ -3379,6 +3414,11 @@ void Print::_make_wipe_tower()
m_wipe_tower_data.used_filament = wipe_tower.get_used_filament();
m_wipe_tower_data.number_of_toolchanges = wipe_tower.get_number_of_toolchanges();
m_wipe_tower_data.construct_mesh(wipe_tower.width(), wipe_tower.get_depth(),
wipe_tower.get_wipe_tower_height(), wipe_tower.get_brim_width(),
config().wipe_tower_wall_type.value == WipeTowerWallType::wtwRib,
wipe_tower.get_rib_width(), wipe_tower.get_rib_length(),
config().wipe_tower_fillet_wall.value);
const Vec3d origin = Vec3d::Zero();
m_fake_wipe_tower.set_fake_extrusion_data(wipe_tower.position(), wipe_tower.width(), wipe_tower.get_wipe_tower_height(),
config().initial_layer_print_height, m_wipe_tower_data.depth,
+1
View File
@@ -771,6 +771,7 @@ struct WipeTowerData
number_of_toolchanges = -1;
depth = 0.f;
brim_width = 0.f;
rib_offset = Vec2f::Zero();
wipe_tower_mesh_data = std::nullopt;
}
void construct_mesh(float width, float depth, float height, float brim_width, bool is_rib_wipe_tower, float rib_width, float rib_length, bool fillet_wall);
+245 -86
View File
@@ -296,10 +296,10 @@ static t_config_enum_values s_keys_map_SupportMaterialStyle {
{ "default", smsDefault },
{ "grid", smsGrid },
{ "snug", smsSnug },
{ "organic", smsTreeOrganic },
{ "tree_slim", smsTreeSlim },
{ "tree_strong", smsTreeStrong },
{ "tree_hybrid", smsTreeHybrid },
{ "organic", smsTreeOrganic }
{ "tree_hybrid", smsTreeHybrid }
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(SupportMaterialStyle)
@@ -439,7 +439,7 @@ CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(OverhangFanThreshold)
// BBS
static const t_config_enum_values s_keys_map_BedType = {
{ "Default Plate", btDefault },
{ "Supertack Plate", btSuperTack },
{ "SuperTack Plate", btSuperTack },
{ "Cool Plate", btPC },
{ "Engineering Plate", btEP },
{ "High Temp Plate", btPEI },
@@ -673,7 +673,7 @@ void PrintConfigDef::init_common_params()
def = this->add("elefant_foot_compensation", coFloat);
def->label = L("Elephant foot compensation");
def->category = L("Quality");
def->tooltip = L("Shrinks the initial layer on build plate to compensate for elephant foot effect.");
def->tooltip = L("Shrinks the first layer on build plate to compensate for elephant foot effect.");
def->sidetext = L("mm"); // milimeters, CIS languages need translation
def->min = 0;
def->mode = comAdvanced;
@@ -926,9 +926,9 @@ void PrintConfigDef::init_fff_params()
def->set_default_value(new ConfigOptionInts{45});
def = this->add("supertack_plate_temp_initial_layer", coInts);
def->label = L("Initial layer");
def->full_label = L("Initial layer bed temperature");
def->tooltip = L("Bed temperature of the initial layer. "
def->label = L("First layer");
def->full_label = L("First layer bed temperature");
def->tooltip = L("Bed temperature of the first layer. "
"A value of 0 means the filament does not support printing on the Cool Plate SuperTack.");
def->sidetext = L(u8"\u2103" /* °C */); // degrees Celsius, CIS languages need translation
def->min = 0;
@@ -936,9 +936,9 @@ void PrintConfigDef::init_fff_params()
def->set_default_value(new ConfigOptionInts{ 35 });
def = this->add("cool_plate_temp_initial_layer", coInts);
def->label = L("Initial layer");
def->full_label = L("Initial layer bed temperature");
def->tooltip = L("Bed temperature of the initial layer. "
def->label = L("First layer");
def->full_label = L("First layer bed temperature");
def->tooltip = L("Bed temperature of the first layer. "
"A value of 0 means the filament does not support printing on the Cool Plate.");
def->sidetext = L(u8"\u2103" /* °C */); // degrees Celsius, CIS languages need translation
def->min = 0;
@@ -946,9 +946,9 @@ void PrintConfigDef::init_fff_params()
def->set_default_value(new ConfigOptionInts{ 35 });
def = this->add("textured_cool_plate_temp_initial_layer", coInts);
def->label = L("Initial layer");
def->full_label = L("Initial layer bed temperature");
def->tooltip = L("Bed temperature of the initial layer. "
def->label = L("First layer");
def->full_label = L("First layer bed temperature");
def->tooltip = L("Bed temperature of the first layer. "
"A value of 0 means the filament does not support printing on the Textured Cool Plate.");
def->sidetext = L(u8"\u2103" /* °C */); // degrees Celsius, CIS languages need translation
def->min = 0;
@@ -956,9 +956,9 @@ void PrintConfigDef::init_fff_params()
def->set_default_value(new ConfigOptionInts{ 40 });
def = this->add("eng_plate_temp_initial_layer", coInts);
def->label = L("Initial layer");
def->full_label = L("Initial layer bed temperature");
def->tooltip = L("Bed temperature of the initial layer. "
def->label = L("First layer");
def->full_label = L("First layer bed temperature");
def->tooltip = L("Bed temperature of the first layer. "
"A value of 0 means the filament does not support printing on the Engineering Plate.");
def->sidetext = L(u8"\u2103" /* °C */); // degrees Celsius, CIS languages need translation
def->min = 0;
@@ -966,18 +966,18 @@ void PrintConfigDef::init_fff_params()
def->set_default_value(new ConfigOptionInts{ 45 });
def = this->add("hot_plate_temp_initial_layer", coInts);
def->label = L("Initial layer");
def->full_label = L("Initial layer bed temperature");
def->tooltip = L("Bed temperature of the initial layer. "
def->label = L("First layer");
def->full_label = L("First layer bed temperature");
def->tooltip = L("Bed temperature of the first layer. "
"A value of 0 means the filament does not support printing on the High Temp Plate.");
def->sidetext = L(u8"\u2103" /* °C */); // degrees Celsius, CIS languages need translation
def->max = 300;
def->set_default_value(new ConfigOptionInts{ 45 });
def = this->add("textured_plate_temp_initial_layer", coInts);
def->label = L("Initial layer");
def->full_label = L("Initial layer bed temperature");
def->tooltip = L("Bed temperature of the initial layer. "
def->label = L("First layer");
def->full_label = L("First layer bed temperature");
def->tooltip = L("Bed temperature of the first layer. "
"A value of 0 means the filament does not support printing on the Textured PEI Plate.");
def->sidetext = L(u8"\u2103" /* °C */); // degrees Celsius, CIS languages need translation
def->min = 0;
@@ -995,7 +995,7 @@ void PrintConfigDef::init_fff_params()
def->enum_values.emplace_back("High Temp Plate");
def->enum_values.emplace_back("Textured PEI Plate");
def->enum_values.emplace_back("Textured Cool Plate");
def->enum_values.emplace_back("Supertack Plate");
def->enum_values.emplace_back("SuperTack Plate");
def->enum_labels.emplace_back(L("Smooth Cool Plate"));
def->enum_labels.emplace_back(L("Engineering Plate"));
def->enum_labels.emplace_back(L("Smooth High Temp Plate"));
@@ -1829,7 +1829,7 @@ void PrintConfigDef::init_fff_params()
"unnecessary bridges. This works well for most difficult models\n"
"3. No filtering - creates internal bridges on every potential internal overhang. This option is "
"useful for heavily slanted top surface models; however, in most cases, it creates too many "
"unnecessary bridges");
"unnecessary bridges.");
def->enum_keys_map = &ConfigOptionEnum<InternalBridgeFilter>::get_enum_values();
def->enum_values.push_back("disabled");
def->enum_values.push_back("limited");
@@ -2159,9 +2159,9 @@ void PrintConfigDef::init_fff_params()
"You may be able to tune this value to get a nice flat surface if there is slight overflow or underflow."
"\n\nThe final object flow ratio is this value multiplied by the filament flow ratio.");
def->mode = comAdvanced;
def->max = 2;
def->min = 0.01;
def->set_default_value(new ConfigOptionFloat(1));
def->max = 2.f;
def->min = 0.01f;
def->set_default_value(new ConfigOptionFloat(1.f));
def = this->add("enable_pressure_advance", coBools);
def->label = L("Enable pressure advance");
@@ -2208,7 +2208,7 @@ void PrintConfigDef::init_fff_params()
"at the bottom of the page. The ideal PA value should be decreasing the higher the volumetric flow is. "
"If it is not, confirm that your extruder is functioning correctly. The slower and with less acceleration you print, "
"the larger the range of acceptable PA values. If no difference is visible, use the PA value from the faster test\n"
"3. Enter the triplets of PA values, Flow and Accelerations in the text box here and save your filament profile");
"3. Enter the triplets of PA values, Flow and Accelerations in the text box here and save your filament profile.");
def->mode = comAdvanced;
//def->gui_flags = "serialized";
def->multiline = true;
@@ -2405,13 +2405,13 @@ void PrintConfigDef::init_fff_params()
def = this->add("bed_temperature_formula", coEnum);
def->label = L("Bed temperature type");
def->tooltip = L("This option determines how the bed temperature is set during slicing: based on the temperature of the first filament or the highest temperature of the printed filaments.");
def->mode = comDevelop;
def->mode = comAdvanced;
def->enum_keys_map = &ConfigOptionEnum<BedTempFormula>::get_enum_values();
def->enum_values.push_back("by_first_filament");
def->enum_values.push_back("by_highest_temp");
def->enum_labels.push_back(L("By First filament"));
def->enum_labels.push_back(L("By Highest Temp"));
def->set_default_value(new ConfigOptionEnum<BedTempFormula>(BedTempFormula::btfFirstFilament));
def->set_default_value(new ConfigOptionEnum<BedTempFormula>(BedTempFormula::btfHighestTemp));
def = this->add("nozzle_flush_dataset", coInts);
def->nullable = true;
@@ -2460,8 +2460,9 @@ void PrintConfigDef::init_fff_params()
def->label = L("Adaptive volumetric speed");
def->tooltip = L("When enabled, the extrusion flow is limited by the smaller of "
"the fitted value (calculated from line width and layer height) and the user-defined maximum flow."
" When disabled, only the user-defined maximum flow is applied.");
def->mode = comAdvanced;
" When disabled, only the user-defined maximum flow is applied.\n\n"
"Note: Experimental and incomplete feature imported from BBS. Functional for some profiles that already have the variable saved.");
def->mode = comDevelop;
def->nullable = true;
def->set_default_value(new ConfigOptionBoolsNullable {false});
@@ -2587,6 +2588,46 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloats { 15. });
def = this->add("filament_tower_interface_pre_extrusion_dist", coFloats);
def->label = L("Interface layer pre-extrusion distance");
def->tooltip = L("Pre-extrusion distance for prime tower interface layer (where different materials meet).");
def->sidetext = L("mm"); // milimeters, CIS languages need translation
def->min = 0;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloats { 10. });
def = this->add("filament_tower_interface_pre_extrusion_length", coFloats);
def->label = L("Interface layer pre-extrusion length");
def->tooltip = L("Pre-extrusion length for prime tower interface layer (where different materials meet).");
def->sidetext = L("mm"); // milimeters, CIS languages need translation
def->min = 0;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloats { 0. });
def = this->add("filament_tower_ironing_area", coFloats);
def->label = L("Tower ironing area");
def->tooltip = L("Ironing area for prime tower interface layer (where different materials meet).");
def->sidetext = L(u8"mm²"); // square milimeters, CIS languages need translation
def->min = 0;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloats { 4. });
def = this->add("filament_tower_interface_purge_volume", coFloats);
def->label = L("Interface layer purge length");
def->tooltip = L("Purge length for prime tower interface layer (where different materials meet).");
def->sidetext = L("mm"); // milimeters, CIS languages need translation
def->min = 0;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloats { 20. });
def = this->add("filament_tower_interface_print_temp", coInts);
def->label = L("Interface layer print temperature");
def->tooltip = L("Print temperature for prime tower interface layer (where different materials meet). If set to -1, use max recommended nozzle temperature.");
def->sidetext = L(u8"\u2103" /* °C */); // degrees Celsius, CIS languages need translation
def->min = -1;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionInts { -1 });
def = this->add("filament_cooling_final_speed", coFloats);
def->label = L("Speed of the last cooling move");
def->tooltip = L("Cooling moves are gradually accelerating towards this speed.");
@@ -2972,9 +3013,9 @@ void PrintConfigDef::init_fff_params()
def->set_default_value(new ConfigOptionFloatOrPercent(100, true));
def = this->add("initial_layer_acceleration", coFloat);
def->label = L("Initial layer");
def->label = L("First layer");
def->category = L("Speed");
def->tooltip = L("Acceleration of initial layer. Using a lower value can improve build plate adhesion.");
def->tooltip = L("Acceleration of the first layer. Using a lower value can improve build plate adhesion.");
def->sidetext = L(u8"mm/s²"); // milimeters per second per second, CIS languages need translation
def->min = 0;
def->mode = comAdvanced;
@@ -3011,9 +3052,10 @@ void PrintConfigDef::init_fff_params()
def->category = L("Speed");
def->tooltip = L("Marlin Firmware Junction Deviation (replaces the traditional XY Jerk setting).");
def->sidetext = L("mm"); // milimeters, CIS languages need translation
def->min = 0;
def->min = 0.f;
def->max = 0.5f;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(0));
def->set_default_value(new ConfigOptionFloat(0.f));
def = this->add("outer_wall_jerk", coFloat);
def->label = L("Outer wall");
@@ -3052,9 +3094,9 @@ void PrintConfigDef::init_fff_params()
def->set_default_value(new ConfigOptionFloat(9));
def = this->add("initial_layer_jerk", coFloat);
def->label = L("Initial layer");
def->label = L("First layer");
def->category = L("Speed");
def->tooltip = L("Jerk for initial layer.");
def->tooltip = L("Jerk for the first layer.");
def->sidetext = L("mm/s"); // milimeters per second, CIS languages need translation
def->min = 0;
def->mode = comAdvanced;
@@ -3070,9 +3112,9 @@ void PrintConfigDef::init_fff_params()
def->set_default_value(new ConfigOptionFloat(12));
def = this->add("initial_layer_line_width", coFloatOrPercent);
def->label = L("Initial layer");
def->label = L("First layer");
def->category = L("Quality");
def->tooltip = L("Line width of initial layer. If expressed as a %, it will be computed over the nozzle diameter.");
def->tooltip = L("Line width of the first layer. If expressed as a %, it will be computed over the nozzle diameter.");
def->sidetext = L("mm or %");
def->ratio_over = "nozzle_diameter";
def->min = 0;
@@ -3083,9 +3125,9 @@ void PrintConfigDef::init_fff_params()
def = this->add("initial_layer_print_height", coFloat);
def->label = L("Initial layer height");
def->label = L("First layer height");
def->category = L("Quality");
def->tooltip = L("Height of initial layer. Making initial layer height to be thick slightly can improve build plate adhesion.");
def->tooltip = L("Height of the first layer. Making the first layer height thicker can improve build plate adhesion.");
def->sidetext = L("mm"); // milimeters, CIS languages need translation
def->min = 0;
def->set_default_value(new ConfigOptionFloat(0.2));
@@ -3099,24 +3141,24 @@ void PrintConfigDef::init_fff_params()
//def->set_default_value(new ConfigOptionBool(0));
def = this->add("initial_layer_speed", coFloat);
def->label = L("Initial layer");
def->tooltip = L("Speed of initial layer except the solid infill part.");
def->label = L("First layer");
def->tooltip = L("Speed of the first layer except the solid infill part.");
def->sidetext = L("mm/s"); // milimeters per second, CIS languages need translation
def->min = 1;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(30));
def = this->add("initial_layer_infill_speed", coFloat);
def->label = L("Initial layer infill");
def->tooltip = L("Speed of solid infill part of initial layer.");
def->label = L("First layer infill");
def->tooltip = L("Speed of solid infill part of the first layer.");
def->sidetext = L("mm/s"); // milimeters per second, CIS languages need translation
def->min = 1;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(60.0));
def = this->add("initial_layer_travel_speed", coFloatOrPercent);
def->label = L("Initial layer travel speed");
def->tooltip = L("Travel speed of initial layer.");
def->label = L("First layer travel speed");
def->tooltip = L("Travel speed of the first layer.");
def->category = L("Speed");
def->sidetext = L("mm/s or %");
def->ratio_over = "travel_speed";
@@ -3135,9 +3177,9 @@ void PrintConfigDef::init_fff_params()
def->set_default_value(new ConfigOptionInt(0));
def = this->add("nozzle_temperature_initial_layer", coInts);
def->label = L("Initial layer");
def->full_label = L("Initial layer nozzle temperature");
def->tooltip = L("Nozzle temperature for printing initial layer when using this filament.");
def->label = L("First layer");
def->full_label = L("First layer nozzle temperature");
def->tooltip = L("Nozzle temperature for printing the first layer when using this filament.");
def->sidetext = L(u8"\u2103" /* °C */); // degrees Celsius, CIS languages need translation
def->min = 0;
def->max = max_temp;
@@ -3208,7 +3250,7 @@ void PrintConfigDef::init_fff_params()
def->category = L("Quality");
def->tooltip = L("Filament-specific override for ironing line spacing. This allows you to customize the spacing "
"between ironing lines for each filament type.");
def->sidetext = "mm";
def->sidetext = L("mm"); // milimeters, CIS languages need translation
def->min = 0;
def->max = 1;
def->mode = comAdvanced;
@@ -3220,7 +3262,7 @@ void PrintConfigDef::init_fff_params()
def->category = L("Quality");
def->tooltip = L("Filament-specific override for ironing inset. This allows you to customize the distance to keep "
"from the edges when ironing for each filament type.");
def->sidetext = "mm";
def->sidetext = L("mm"); // milimeters, CIS languages need translation
def->min = 0;
def->max = 100;
def->mode = comAdvanced;
@@ -3232,7 +3274,7 @@ void PrintConfigDef::init_fff_params()
def->category = L("Speed");
def->tooltip = L("Filament-specific override for ironing speed. This allows you to customize the print speed "
"of ironing lines for each filament type.");
def->sidetext = "mm/s";
def->sidetext = L("mm/s"); // milimeters per second, CIS languages need translation
def->min = 1;
def->mode = comAdvanced;
def->nullable = true;
@@ -3249,7 +3291,7 @@ void PrintConfigDef::init_fff_params()
def->enum_values.push_back("all");
def->enum_values.push_back("allwalls");
def->enum_values.push_back("disabled_fuzzy");
def->enum_labels.push_back(L("None (allow paint)"));
def->enum_labels.push_back(L("Painted only"));
def->enum_labels.push_back(L("Contour"));
def->enum_labels.push_back(L("Contour and hole"));
def->enum_labels.push_back(L("All walls"));
@@ -3272,10 +3314,10 @@ void PrintConfigDef::init_fff_params()
def->category = L("Others");
def->tooltip = L("The average distance between the random points introduced on each line segment.");
def->sidetext = L("mm"); // milimeters, CIS languages need translation
def->min = 0;
def->max = 5;
def->min = 0.01f; // point distance cannot be 0! Otherwise we get infinite loop + OOM due to infinite line division.
def->max = 5.f;
def->mode = comSimple;
def->set_default_value(new ConfigOptionFloat(0.3));
def->set_default_value(new ConfigOptionFloat(0.3f));
def = this->add("fuzzy_skin_first_layer", coBool);
def->label = L("Apply fuzzy skin to first layer");
@@ -3335,7 +3377,7 @@ void PrintConfigDef::init_fff_params()
def->category = L("Others");
def->tooltip = L("The base size of the coherent noise features, in mm. Higher values will result in larger features.");
def->sidetext = L("mm"); // milimeters, CIS languages need translation
def->min = 0.1;
def->min = 0.1f;
def->max = 500;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(1.0));
@@ -3353,7 +3395,7 @@ void PrintConfigDef::init_fff_params()
def->label = L("Fuzzy skin noise persistence");
def->category = L("Others");
def->tooltip = L("The decay rate for higher octaves of the coherent noise. Lower values will result in smoother noise.");
def->min = 0.01;
def->min = 0.01f;
def->max = 1;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(0.5));
@@ -3591,7 +3633,7 @@ void PrintConfigDef::init_fff_params()
def = this->add("gcode_label_objects", coBool);
def->label = L("Label objects");
def->tooltip = L("Enable this to add comments into the G-code labeling print moves with what object they belong to,"
" which is useful for the Octoprint CancelObject plugin. This settings is NOT compatible with "
" which is useful for the Octoprint CancelObject plug-in. This setting is NOT compatible with "
"Single Extruder Multi Material setup and Wipe into Object / Wipe into Infill.");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(1));
@@ -3922,7 +3964,7 @@ void PrintConfigDef::init_fff_params()
def->label = L("Ironing Type");
def->category = L("Quality");
def->tooltip = L("Ironing is using small flow to print on same height of surface again to make flat surface more smooth. "
"This setting controls which layer being ironed");
"This setting controls which layer being ironed.");
def->enum_keys_map = &ConfigOptionEnum<IroningType>::get_enum_values();
def->enum_values.push_back("no ironing");
def->enum_values.push_back("top");
@@ -4216,10 +4258,10 @@ void PrintConfigDef::init_fff_params()
def->category = L("Machine limits");
def->tooltip = L("Maximum junction deviation (M205 J, only apply if JD > 0 for Marlin Firmware\nIf your Marlin 2 printer uses Classic Jerk set this value to 0.)");
def->sidetext = L("mm"); // milimeters, CIS languages need translation
def->min = 0;
def->max = 1;
def->min = 0.f;
def->max = 0.5f;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloats { 0.01});
def->set_default_value(new ConfigOptionFloats{ 0.01f });
// M205 S... [mm/sec]
def = this->add("machine_min_extruding_rate", coFloats);
@@ -4682,7 +4724,7 @@ void PrintConfigDef::init_fff_params()
def->set_default_value(new ConfigOptionFloat(1.5));
def = this->add("raft_first_layer_density", coPercent);
def->label = L("Initial layer density");
def->label = L("First layer density");
def->category = L("Support");
def->tooltip = L("Density of the first raft or support layer.");
def->sidetext = "%";
@@ -4692,7 +4734,7 @@ void PrintConfigDef::init_fff_params()
def->set_default_value(new ConfigOptionPercent(90));
def = this->add("raft_first_layer_expansion", coFloat);
def->label = L("Initial layer expansion");
def->label = L("First layer expansion");
def->category = L("Support");
def->tooltip = L("Expand the first raft or support layer to improve bed plate adhesion.");
def->sidetext = L("mm"); // milimeters, CIS languages need translation
@@ -5408,7 +5450,7 @@ void PrintConfigDef::init_fff_params()
"If smooth mode is selected, the toolhead will move to the excess chute after each layer is printed "
"and then take a snapshot. "
"Since the melt filament may leak from the nozzle during the process of taking a snapshot, "
"prime tower is required for smooth mode to wipe nozzle.");
"a prime tower is required for smooth mode to wipe nozzle.");
def->enum_keys_map = &ConfigOptionEnum<TimelapseType>::get_enum_values();
def->enum_values.emplace_back("0");
def->enum_values.emplace_back("1");
@@ -5423,7 +5465,7 @@ void PrintConfigDef::init_fff_params()
def->tooltip = L("Temperature difference to be applied when an extruder is not active. "
"The value is not used when 'idle_temperature' in filament settings "
"is set to non-zero value.");
def->sidetext = L(u8"∆\u2103"); // delta degrees Celsius, CIS languages need translation
def->sidetext = L(u8"\u2206\u2103" /* ∆°C */); // delta degrees Celsius, CIS languages need translation
def->min = -max_temp;
def->max = max_temp;
def->mode = comAdvanced;
@@ -5785,7 +5827,12 @@ void PrintConfigDef::init_fff_params()
def = this->add("support_base_pattern", coEnum);
def->label = L("Base pattern");
def->category = L("Support");
def->tooltip = L("Line pattern of support.");
def->tooltip = L("Line pattern of support.\n\n"
"The Default option for Tree supports is Hollow, which means no base pattern. "
"For other support types, the Default option is the Rectilinear pattern.\n\n"
"NOTE: For Organic supports, the two walls are supported only with the Hollow/Default base pattern. "
"The Lightning base pattern is supported only by Tree Slim/Strong/Hybrid supports. "
"For the other support types, the Rectilinear will be used instead of Lightning.");
def->enum_keys_map = &ConfigOptionEnum<SupportMaterialPattern>::get_enum_values();
def->enum_values.push_back("default");
def->enum_values.push_back("rectilinear");
@@ -6401,9 +6448,9 @@ void PrintConfigDef::init_fff_params()
def->enum_values.emplace_back("rectangle");
def->enum_values.emplace_back("cone");
def->enum_values.emplace_back("rib");
def->enum_labels.emplace_back("Rectangle");
def->enum_labels.emplace_back("Cone");
def->enum_labels.emplace_back("Rib");
def->enum_labels.emplace_back(L("Rectangle"));
def->enum_labels.emplace_back(L("Cone"));
def->enum_labels.emplace_back(L("Rib"));
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionEnum<WipeTowerWallType>(wtwRectangle));
@@ -6459,6 +6506,18 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(false));
def = this->add("enable_tower_interface_features", coBool);
def->label = L("Enable tower interface features");
def->tooltip = L("Enable optimized prime tower interface behavior when different materials meet.");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(false));
def = this->add("enable_tower_interface_cooldown_during_tower", coBool);
def->label = L("Cool down from interface boost during prime tower");
def->tooltip = L("When interface-layer temperature boost is active, set the nozzle back to print temperature at the start of the prime tower so it cools down during the tower.");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(false));
def = this->add("prime_tower_infill_gap", coPercent);
def->label = L("Infill gap");
def->tooltip = L("Infill gap.");
@@ -6766,6 +6825,7 @@ void PrintConfigDef::init_extruder_option_keys()
// ConfigOptionFloats, ConfigOptionPercents, ConfigOptionBools, ConfigOptionStrings
m_extruder_option_keys = {
"extruder_type", "nozzle_diameter", "default_nozzle_volume_type", "min_layer_height", "max_layer_height", "extruder_offset",
"extruder_printable_height", "nozzle_volume", "nozzle_type", "nozzle_flush_dataset",
"retraction_length", "z_hop", "z_hop_types", "travel_slope", "retract_lift_above", "retract_lift_below", "retract_lift_enforce", "retraction_speed", "deretraction_speed",
"retract_before_wipe", "retract_restart_extra", "retraction_minimum_travel", "wipe", "wipe_distance",
"retract_when_changing_layer", "retract_length_toolchange", "retract_restart_extra_toolchange", "extruder_colour",
@@ -9287,6 +9347,61 @@ void DynamicPrintConfig::update_values_to_printer_extruders_for_multiple_filamen
}
}
namespace {
// Options in printer_options_with_variant_2 are stored as (normal,silent) pairs per printer variant.
// Some legacy presets/projects carry a variant list but still store only one pair; normalize to avoid crashes.
static void normalize_stride2_floats(ConfigOptionFloats &opt, size_t expected_size)
{
auto &v = opt.values;
if (expected_size == 0) {
v.clear();
return;
}
if (v.empty()) {
// Fallback: keep behavior predictable instead of crashing. This should be rare.
v.resize(expected_size, 0.0);
return;
}
const double first = v[0];
const double second = (v.size() >= 2) ? v[1] : first;
// Ensure we have at least one (normal,silent) pair to replicate.
if (v.size() < 2) {
v.resize(2, first);
v[1] = second;
}
// Keep pair alignment if some legacy preset produced odd length.
if (v.size() % 2 != 0)
v.push_back(second);
if (v.size() > expected_size) {
v.resize(expected_size);
return;
}
const size_t have_variants = v.size() / 2;
const size_t want_variants = expected_size / 2;
v.resize(expected_size);
for (size_t vi = have_variants; vi < want_variants; ++vi) {
v[vi * 2] = first;
if (vi * 2 + 1 < v.size())
v[vi * 2 + 1] = second;
}
}
static void log_normalize_legacy_vector_size(const char *fn, const std::string &key, int stride, size_t src_size, size_t dest_size, size_t expected_size,
size_t restore_n, int cur_variant_count, int target_variant_count, size_t cur_ids, size_t target_ids,
const ConfigOption *opt_src, const ConfigOption *opt_target)
{
BOOST_LOG_TRIVIAL(debug) << fn << ": normalizing legacy vector size for key '" << key << "'"
<< " stride=" << stride << " src_size=" << src_size << " dest_size=" << dest_size << " expected=" << expected_size
<< " restore_index.size=" << restore_n << " cur_variants=" << cur_variant_count << " target_variants=" << target_variant_count
<< " cur_ids=" << cur_ids << " target_ids=" << target_ids << " cur_value=" << opt_src->serialize()
<< " target_value=" << opt_target->serialize();
}
} // namespace
void DynamicPrintConfig::update_non_diff_values_to_base_config(DynamicPrintConfig& new_config, const t_config_option_keys& keys, const std::set<std::string>& different_keys,
std::string extruder_id_name, std::string extruder_variant_name, std::set<std::string>& key_set1, std::set<std::string>& key_set2)
{
@@ -9309,7 +9424,10 @@ void DynamicPrintConfig::update_non_diff_values_to_base_config(DynamicPrintConfi
variant_index.resize(target_variant_count, -1);
if (cur_variant_count == 0) {
variant_index[0] = 0;
// Defensive: target_variant_count may be 0 if the preset doesn't carry extruder_variant_name.
// In that case keep variant_index empty and let the downstream size checks produce a useful error.
if (!variant_index.empty())
variant_index[0] = 0;
}
else if ((cur_extruder_ids.size() > 0) && cur_variant_count != cur_extruder_ids.size()){
//should not happen
@@ -9351,12 +9469,53 @@ void DynamicPrintConfig::update_non_diff_values_to_base_config(DynamicPrintConfi
//nothing to do, keep the original one
}
else {
ConfigOptionVectorBase* opt_vec_src = static_cast<ConfigOptionVectorBase*>(opt_src);
const ConfigOptionVectorBase* opt_vec_dest = static_cast<const ConfigOptionVectorBase*>(opt_target);
int stride = 1;
if (key_set2.find(opt) != key_set2.end())
stride = 2;
opt_vec_src->set_with_restore(opt_vec_dest, variant_index, stride);
const size_t restore_n = variant_index.size();
const size_t expected_size = restore_n * size_t(stride);
if (stride == 2) {
// Options in key_set2 are machine limits stored as (normal,silent) pairs per printer variant.
if (opt_src->type() != coFloats || opt_target->type() != coFloats)
throw ConfigurationError((boost::format("%1%: key '%2%' is expected to be ConfigOptionFloats for stride=2.") % __FUNCTION__ % opt).str());
auto *src_f = static_cast<ConfigOptionFloats*>(opt_src);
ConfigOptionFloats rhs_tmp(*static_cast<const ConfigOptionFloats*>(opt_target));
const size_t src_size = src_f->values.size();
const size_t dest_size = rhs_tmp.values.size();
if (src_size != expected_size || dest_size != expected_size)
log_normalize_legacy_vector_size(__FUNCTION__, opt, stride, src_size, dest_size, expected_size, restore_n, cur_variant_count,
target_variant_count, cur_extruder_ids.size(), target_extruder_ids.size(), opt_src, opt_target);
// Normalize src in-place so backup_values indexing is safe, normalize rhs via a temporary copy.
normalize_stride2_floats(*src_f, expected_size);
normalize_stride2_floats(rhs_tmp, expected_size);
src_f->set_with_restore(&rhs_tmp, variant_index, stride);
} else {
ConfigOptionVectorBase* opt_vec_src = static_cast<ConfigOptionVectorBase*>(opt_src);
const size_t src_size = opt_vec_src->size();
const size_t dest_size = static_cast<const ConfigOptionVectorBase*>(opt_target)->size();
if (src_size != expected_size || dest_size != expected_size)
log_normalize_legacy_vector_size(__FUNCTION__, opt, stride, src_size, dest_size, expected_size, restore_n, cur_variant_count,
target_variant_count, cur_extruder_ids.size(), target_extruder_ids.size(), opt_src, opt_target);
if (opt_vec_src->size() != expected_size)
opt_vec_src->resize(expected_size, opt_target);
// Normalize rhs via a cloned temporary (rhs itself is const).
ConfigOptionUniquePtr rhs_owner(opt_target->clone());
ConfigOptionVectorBase *rhs_vec = dynamic_cast<ConfigOptionVectorBase*>(rhs_owner.get());
if (rhs_vec == nullptr)
throw ConfigurationError((boost::format("%1%: key '%2%' is expected to be a vector option.") % __FUNCTION__ % opt).str());
if (rhs_vec->size() != expected_size)
rhs_vec->resize(expected_size, opt_target);
opt_vec_src->set_with_restore(rhs_vec, variant_index, stride);
}
}
}
}
@@ -9667,13 +9826,13 @@ std::map<std::string, std::string> validate(const FullPrintConfig &cfg, bool und
case coFloatOrPercent:
{
auto *fopt = static_cast<const ConfigOptionFloat*>(opt);
out_of_range = fopt->value < optdef->min || fopt->value > optdef->max;
out_of_range = !optdef->is_value_valid(fopt->value);
break;
}
case coFloats:
case coPercents:
for (double v : static_cast<const ConfigOptionVector<double>*>(opt)->values)
if (v < optdef->min || v > optdef->max) {
if (!optdef->is_value_valid(v)) {
out_of_range = true;
break;
}
@@ -9681,12 +9840,12 @@ std::map<std::string, std::string> validate(const FullPrintConfig &cfg, bool und
case coInt:
{
auto *iopt = static_cast<const ConfigOptionInt*>(opt);
out_of_range = iopt->value < optdef->min || iopt->value > optdef->max;
out_of_range = !optdef->is_value_valid(iopt->value);
break;
}
case coInts:
for (int v : static_cast<const ConfigOptionVector<int>*>(opt)->values)
if (v < optdef->min || v > optdef->max) {
if (!optdef->is_value_valid(v)) {
out_of_range = true;
break;
}
@@ -10065,7 +10224,7 @@ CLIMiscConfigDef::CLIMiscConfigDef()
def->set_default_value(new ConfigOptionBool(false));
def = this->add("downward_settings", coStrings);
def->label = L("downward machines settings");
def->label = L("Downward machines settings");
def->tooltip = L("The machine settings list needs to do downward checking.");
def->cli_params = "\"machine1.json;machine2.json;...\"";
def->set_default_value(new ConfigOptionStrings());
@@ -10411,11 +10570,11 @@ DimensionsConfigDef::DimensionsConfigDef()
"'[x, y]' (x and y are floating-point numbers in mm).");
def = this->add("first_layer_print_min", coFloats);
def->label = L("Bottom-left corner of first layer bounding box");
def->label = L("Bottom-left corner of the first layer bounding box");
def->tooltip = point_tooltip;
def = this->add("first_layer_print_max", coFloats);
def->label = L("Top-right corner of first layer bounding box");
def->label = L("Top-right corner of the first layer bounding box");
def->tooltip = point_tooltip;
def = this->add("first_layer_print_size", coFloats);
@@ -10443,8 +10602,8 @@ TemperaturesConfigDef::TemperaturesConfigDef()
ConfigOptionDef* def;
new_def("bed_temperature", coInts, "Bed temperature", "Vector of bed temperatures for each extruder/filament.")
new_def("bed_temperature_initial_layer", coInts, "Initial layer bed temperature", "Vector of initial layer bed temperatures for each extruder/filament. Provides the same value as first_layer_bed_temperature.")
new_def("bed_temperature_initial_layer_single", coInt, "Initial layer bed temperature (initial extruder)", "Initial layer bed temperature for the initial extruder. Same as bed_temperature_initial_layer[initial_extruder]")
new_def("bed_temperature_initial_layer", coInts, "First layer bed temperature", "Vector of first layer bed temperatures for each extruder/filament. Provides the same value as first_layer_bed_temperature.")
new_def("bed_temperature_initial_layer_single", coInt, "First layer bed temperature (initial extruder)", "First layer bed temperature for the initial extruder. Same as bed_temperature_initial_layer[initial_extruder]")
new_def("chamber_temperature", coInts, "Chamber temperature", "Vector of chamber temperatures for each extruder/filament.")
new_def("overall_chamber_temperature", coInt, "Overall chamber temperature", "Overall chamber temperature. This value is the maximum chamber temperature of any extruder/filament used.")
new_def("first_layer_bed_temperature", coInts, "First layer bed temperature", "Vector of first layer bed temperatures for each extruder/filament. Provides the same value as bed_temperature_initial_layer.")
+8 -1
View File
@@ -160,7 +160,7 @@ enum SupportMaterialPattern {
};
enum SupportMaterialStyle {
smsDefault, smsGrid, smsSnug, smsTreeSlim, smsTreeStrong, smsTreeHybrid, smsTreeOrganic,
smsDefault, smsGrid, smsSnug, smsTreeOrganic, smsTreeSlim, smsTreeStrong, smsTreeHybrid,
};
enum LongRectrationLevel
@@ -1386,6 +1386,11 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionInts, filament_cooling_moves))
((ConfigOptionFloats, filament_cooling_initial_speed))
((ConfigOptionFloats, filament_minimal_purge_on_wipe_tower))
((ConfigOptionFloats, filament_tower_interface_pre_extrusion_dist))
((ConfigOptionFloats, filament_tower_interface_pre_extrusion_length))
((ConfigOptionFloats, filament_tower_ironing_area))
((ConfigOptionFloats, filament_tower_interface_purge_volume))
((ConfigOptionInts, filament_tower_interface_print_temp))
((ConfigOptionFloats, filament_cooling_final_speed))
((ConfigOptionStrings, filament_ramming_parameters))
((ConfigOptionBools, filament_multitool_ramming))
@@ -1515,6 +1520,8 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE(
((ConfigOptionPercent, prime_tower_infill_gap))
((ConfigOptionBool, prime_tower_skip_points))
((ConfigOptionBool, prime_tower_flat_ironing))
((ConfigOptionBool, enable_tower_interface_features))
((ConfigOptionBool, enable_tower_interface_cooldown_during_tower))
((ConfigOptionFloat, wipe_tower_bridging))
((ConfigOptionPercent, wipe_tower_extra_flow))
((ConfigOptionFloats, flush_volumes_matrix))
+8 -4
View File
@@ -1576,8 +1576,10 @@ void generate_support_toolpaths(
{
SupportLayer &support_layer = *support_layers[support_layer_id];
LayerCache &layer_cache = layer_caches[support_layer_id];
const float support_interface_angle = (support_params.support_style == smsGrid || config.support_interface_pattern == smipRectilinear) ?
support_params.interface_angle : support_params.raft_interface_angle(support_layer.interface_id());
const float support_interface_angle = (config.support_interface_pattern == smipRectilinearInterlaced) ?
support_params.raft_interface_angle(support_layer.interface_id()) :
((support_params.support_style == smsGrid || config.support_interface_pattern == smipRectilinear) ?
support_params.interface_angle : support_params.raft_interface_angle(support_layer.interface_id()));
// Find polygons with the same print_z.
SupportGeneratorLayerExtruded &bottom_contact_layer = layer_cache.bottom_contact_layer;
@@ -1744,8 +1746,10 @@ void generate_support_toolpaths(
filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / density));
sheath = true;
no_sort = true;
} else if (support_params.support_style == SupportMaterialStyle::smsTreeOrganic) {
// if the tree supports are too tall, use double wall to make it stronger
} else if (support_params.support_style == SupportMaterialStyle::smsTreeOrganic &&
(config.support_base_pattern == smpNone || config.support_base_pattern == smpDefault)) {
// Orca: A special case for the hollow Organic supports
// Orca: If the tree supports are too tall, use a double wall to make it stronger
SupportParameters support_params2 = support_params;
if (support_layer.print_z > 100.0)
support_params2.tree_branch_diameter_double_wall_area_scaled = 0.1;
-1
View File
@@ -3549,7 +3549,6 @@ static void generate_support_areas(Print &print, TreeSupport* tree_support, cons
if (layer) layer->polygons = intersection(layer->polygons, volumes.m_bed_area);
});
// Don't fill in the tree supports, make them hollow with just a single sheath line.
print.set_status(69, _L("Generating support"));
generate_support_toolpaths(print_object.support_layers(), print_object.config(), support_params, print_object.slicing_parameters(),
raft_layers, bottom_contacts, top_contacts, intermediate_layers, interface_layers, base_interface_layers);
+55
View File
@@ -0,0 +1,55 @@
#include "TriangleMeshDeal.hpp"
#include <igl/read_triangle_mesh.h>
#include <igl/loop.h>
#include <igl/upsample.h>
#include <igl/false_barycentric_subdivision.h>
namespace Slic3r {
TriangleMesh TriangleMeshDeal::smooth_triangle_mesh(const TriangleMesh &mesh, bool &ok)
{
{
using namespace std;
using namespace igl;
Eigen::MatrixXi OF, F;
Eigen::MatrixXd OV, V;
auto vertices_count = mesh.its.vertices.size();
OV = Eigen::MatrixXd(vertices_count, 3);
for (int i = 0; i < vertices_count; i++) {
auto v = mesh.its.vertices[i];
OV.row(i) << v[0], v[1], v[2];
}
auto indices_count = mesh.its.indices.size();
OF = Eigen::MatrixXi(indices_count, 3);
for (int i = 0; i < indices_count; i++) {
auto face = mesh.its.indices[i];
OF.row(i) << face[0], face[1], face[2];
}
//igl:: read_triangle_mesh( "E:/Download/libigl-2.6.0/out/build/x64-Debug/_deps/libigl_tutorial_data-src/decimated-knight.off", OV, OF);
V = OV;
F = OF;
//igl::upsample(Eigen::MatrixXd(V), Eigen::MatrixXi(F), V, F);
ok = true;
if (!igl::loop(Eigen::MatrixXd(V), Eigen::MatrixXi(F), V, F)) {
ok = false;
return TriangleMesh();
}
//igl::false_barycentric_subdivision(Eigen::MatrixXd(V), Eigen::MatrixXi(F), V, F);
indexed_triangle_set its;
int vertex_count = V.rows();
its.vertices.resize(vertex_count);
for (int i = 0; i < vertex_count; i++) {
its.vertices[i] = V.row(i).cast<float>();
}
int indice_count = F.rows();
its.indices.resize(indice_count);
for (int i = 0; i < indice_count; i++) {
auto cur = F.row(i);
its.indices[i] = Slic3r::Vec3i32(cur[0], cur[1], cur[2]);
}
TriangleMesh result_mesh(its);
return result_mesh;
}
}
} // namespace Slic3r
+14
View File
@@ -0,0 +1,14 @@
#ifndef libslic3r_Timer_hpp_
#define libslic3r_Timer_hpp_
#include "TriangleMesh.hpp"
namespace Slic3r {
class TriangleMeshDeal
{
public:
static TriangleMesh smooth_triangle_mesh(const TriangleMesh &mesh,bool& ok);
};
} // namespace Slic3r
#endif // libslic3r_Timer_hpp_
+5
View File
@@ -86,6 +86,11 @@ struct PathVertex
// Layer duration in seconds
//
float layer_duration{ 0.0f };
//
// ORCA: Add Pressure Advance visualization support
// Pressure advance value
//
float pressure_advance{ 0.0f };
//
// Return true if the segment is an extrusion move
+2
View File
@@ -92,6 +92,8 @@ enum class EViewType : uint8_t
LayerTimeLogarithmic,
FanSpeed,
Temperature,
// ORCA: Add Pressure Advance visualization support
PressureAdvance,
Tool,
COUNT
};
+4
View File
@@ -169,6 +169,8 @@ public:
// EViewType::ActualSpeed
// EViewType::FanSpeed
// EViewType::Temperature
// ORCA: Add Pressure Advance visualization support
// EViewType::PressureAdvance
// EViewType::VolumetricFlowRate
// EViewType::ActualVolumetricFlowRate
// EViewType::LayerTimeLinear
@@ -185,6 +187,8 @@ public:
// EViewType::ActualSpeed
// EViewType::FanSpeed
// EViewType::Temperature
// ORCA: Add Pressure Advance visualization support
// EViewType::PressureAdvance
// EViewType::VolumetricFlowRate
// EViewType::ActualVolumetricFlowRate
// EViewType::LayerTimeLinear
+16
View File
@@ -1493,6 +1493,11 @@ Color ViewerImpl::get_vertex_color(const PathVertex& v) const
{
return v.is_travel() ? get_option_color(move_type_to_option(v.type)) : m_temperature_range.get_color_at(v.temperature);
}
// ORCA: Add Pressure Advance visualization support
case EViewType::PressureAdvance:
{
return v.is_travel() ? get_option_color(move_type_to_option(v.type)) : m_pressure_advance_range.get_color_at(v.pressure_advance);
}
case EViewType::VolumetricFlowRate:
{
return v.is_travel() ? get_option_color(move_type_to_option(v.type)) : m_volumetric_rate_range.get_color_at(v.volumetric_rate());
@@ -1582,6 +1587,8 @@ const ColorRange& ViewerImpl::get_color_range(EViewType type) const
case EViewType::ActualSpeed: { return m_actual_speed_range; }
case EViewType::FanSpeed: { return m_fan_speed_range; }
case EViewType::Temperature: { return m_temperature_range; }
// ORCA: Add Pressure Advance visualization support
case EViewType::PressureAdvance: { return m_pressure_advance_range; }
case EViewType::VolumetricFlowRate: { return m_volumetric_rate_range; }
case EViewType::ActualVolumetricFlowRate: { return m_actual_volumetric_rate_range; }
case EViewType::LayerTimeLinear: { return m_layer_time_range[0]; }
@@ -1600,6 +1607,8 @@ void ViewerImpl::set_color_range_palette(EViewType type, const Palette& palette)
case EViewType::ActualSpeed: { m_actual_speed_range.set_palette(palette); break; }
case EViewType::FanSpeed: { m_fan_speed_range.set_palette(palette); break; }
case EViewType::Temperature: { m_temperature_range.set_palette(palette); break; }
// ORCA: Add Pressure Advance visualization support
case EViewType::PressureAdvance: { m_pressure_advance_range.set_palette(palette); break; }
case EViewType::VolumetricFlowRate: { m_volumetric_rate_range.set_palette(palette); break; }
case EViewType::ActualVolumetricFlowRate: { m_actual_volumetric_rate_range.set_palette(palette); break; }
case EViewType::LayerTimeLinear: { m_layer_time_range[0].set_palette(palette); break; }
@@ -1637,6 +1646,8 @@ size_t ViewerImpl::get_used_cpu_memory() const
ret += m_actual_speed_range.size_in_bytes_cpu();
ret += m_fan_speed_range.size_in_bytes_cpu();
ret += m_temperature_range.size_in_bytes_cpu();
// ORCA: Add Pressure Advance visualization support
ret += m_pressure_advance_range.size_in_bytes_cpu();
ret += m_volumetric_rate_range.size_in_bytes_cpu();
ret += m_actual_volumetric_rate_range.size_in_bytes_cpu();
for (size_t i = 0; i < COLOR_RANGE_TYPES_COUNT; ++i) {
@@ -1787,6 +1798,8 @@ void ViewerImpl::update_color_ranges()
m_actual_speed_range.reset();
m_fan_speed_range.reset();
m_temperature_range.reset();
// ORCA: Add Pressure Advance visualization support
m_pressure_advance_range.reset();
m_volumetric_rate_range.reset();
m_actual_volumetric_rate_range.reset();
m_layer_time_range[0].reset(); // ColorRange::EType::Linear
@@ -1803,6 +1816,9 @@ void ViewerImpl::update_color_ranges()
}
m_fan_speed_range.update(round_to_bin(v.fan_speed));
m_temperature_range.update(round_to_bin(v.temperature));
// ORCA: Add Pressure Advance visualization support
if (v.pressure_advance >= 0.0f)
m_pressure_advance_range.update(v.pressure_advance);
}
if ((v.is_travel() && m_settings.options_visibility[size_t(EOptionType::Travels)]) ||
(v.is_wipe() && m_settings.options_visibility[size_t(EOptionType::Wipes)]) ||
+2
View File
@@ -289,6 +289,8 @@ private:
ColorRange m_actual_speed_range;
ColorRange m_fan_speed_range;
ColorRange m_temperature_range;
// ORCA: Add Pressure Advance visualization support
ColorRange m_pressure_advance_range;
ColorRange m_volumetric_rate_range;
ColorRange m_actual_volumetric_rate_range;
std::array<ColorRange, COLOR_RANGE_TYPES_COUNT> m_layer_time_range{
+1 -1
View File
@@ -736,7 +736,7 @@ else()
set(_opengl_link_lib OpenGL::GL)
endif()
target_link_libraries(libslic3r_gui libslic3r cereal::cereal imgui imguizmo minilzo libvgcode GLEW::GLEW OpenGL::GL hidapi ${wxWidgets_LIBRARIES} glfw libcurl OpenSSL::SSL OpenSSL::Crypto noise::noise)
target_link_libraries(libslic3r_gui libslic3r cereal::cereal imgui imguizmo minilzo libvgcode md4c-html GLEW::GLEW OpenGL::GL hidapi ${wxWidgets_LIBRARIES} glfw libcurl OpenSSL::SSL OpenSSL::Crypto noise::noise)
if (MSVC)
+1 -1
View File
@@ -222,7 +222,7 @@ void Bed_2D::repaint(const std::vector<Vec2d>& shape)
dc.DrawText(origin_label, origin_label_x, origin_label_y);
// ORCA add grid size value as information for large scale beds
auto grid_label = wxString("1x1 Grid: " + std::to_string(step) + " mm");
auto grid_label = wxString::Format(_L("1x1 Grid: %d mm"), step);
Point draw_bb = to_pixels(Vec2d(
std::min(m_pos(0),bb.min(0)),
std::min(m_pos(1),bb.min(1))
+76 -8
View File
@@ -188,6 +188,58 @@ void GLVolume::load_render_colors()
RenderColor::colors[RenderCol_Model_Unprintable] = GUI::ImGuiWrapper::to_ImVec4(GLVolume::UNPRINTABLE_COLOR);
}
ColorRGBA GLVolume::brighten_color(const ColorRGBA& color, float multiplier)
{
// Convert RGB to HSL, increase lightness, convert back
float r = color.r(), g = color.g(), b = color.b();
// RGB to HSL conversion
float max_val = std::max({r, g, b});
float min_val = std::min({r, g, b});
float l = (max_val + min_val) / 2.0f;
float h = 0.0f, s = 0.0f;
if (max_val != min_val) {
float delta = max_val - min_val;
s = l > 0.5f ? delta / (2.0f - max_val - min_val) : delta / (max_val + min_val);
if (max_val == r)
h = (g - b) / delta + (g < b ? 6.0f : 0.0f);
else if (max_val == g)
h = (b - r) / delta + 2.0f;
else
h = (r - g) / delta + 4.0f;
h /= 6.0f;
}
// Increase lightness by a fixed amount (0.25)
// Ensures even saturated colors become visibly brighter
l = std::min(l + 0.25f, 1.0f);
// HSL to RGB conversion
auto hue_to_rgb = [](float p, float q, float t) {
if (t < 0.0f) t += 1.0f;
if (t > 1.0f) t -= 1.0f;
if (t < 1.0f / 6.0f) return p + (q - p) * 6.0f * t;
if (t < 1.0f / 2.0f) return q;
if (t < 2.0f / 3.0f) return p + (q - p) * (2.0f / 3.0f - t) * 6.0f;
return p;
};
if (s == 0.0f) {
r = g = b = l; // achromatic (gray)
} else {
float q = l < 0.5f ? l * (1.0f + s) : l + s - l * s;
float p = 2.0f * l - q;
r = hue_to_rgb(p, q, h + 1.0f / 3.0f);
g = hue_to_rgb(p, q, h);
b = hue_to_rgb(p, q, h - 1.0f / 3.0f);
}
return ColorRGBA(r, g, b, color.a());
}
GLVolume::GLVolume(float r, float g, float b, float a)
: m_sla_shift_z(0.0)
, m_sinking_contours(*this)
@@ -253,16 +305,28 @@ void GLVolume::set_render_color()
set_render_color(outside ? SELECTED_OUTSIDE_COLOR : SELECTED_COLOR);
else if (disabled)
*/
if (disabled)
set_render_color(DISABLED_COLOR);
// Determine base color first
ColorRGBA base_color;
if (disabled) {
base_color = DISABLED_COLOR;
}
#ifdef ENABLE_OUTSIDE_COLOR
else if (is_outside && shader_outside_printer_detection_enabled)
set_render_color(OUTSIDE_COLOR);
else if (is_outside && shader_outside_printer_detection_enabled) {
base_color = OUTSIDE_COLOR;
}
#endif
else {
//to make black not too hard too see
ColorRGBA new_color = adjust_color_for_rendering(color);
set_render_color(new_color);
// to make black not too hard too see
base_color = adjust_color_for_rendering(color);
}
// Apply selection brightening AFTER determining base color
if (selected && !disabled) {
set_render_color(brighten_color(base_color, 1.25f));
}
else {
set_render_color(base_color);
}
}
@@ -276,7 +340,11 @@ void GLVolume::set_render_color()
//BBS set unprintable color
if (!printable) {
render_color = UNPRINTABLE_COLOR;
if (selected) {
render_color = brighten_color(UNPRINTABLE_COLOR, 1.25f);
} else {
render_color = UNPRINTABLE_COLOR;
}
}
//BBS set invisible color
+1
View File
@@ -96,6 +96,7 @@ public:
static void update_render_colors();
static void load_render_colors();
static ColorRGBA brighten_color(const ColorRGBA& color, float multiplier = 1.25f);
static float explosion_ratio;
static float last_explosion_ratio;
+1 -1
View File
@@ -721,7 +721,7 @@ void AMSSettingTypePanel::OnAmsTypeChanged(wxCommandEvent& event)
return;
}
MessageDialog dlg(this, _L("AMS type switching needs firmware update, taking about 30s. Switch now ?"), SLIC3R_APP_NAME + _L("Info"), wxOK | wxCANCEL | wxICON_INFORMATION);
MessageDialog dlg(this, _L("AMS type switching needs firmware update, taking about 30s. Switch now?"), SLIC3R_APP_NAME + _L("Info"), wxOK | wxCANCEL | wxICON_INFORMATION);
dlg.SetButtonLabel(wxID_OK, _L("Confirm"));
int rtn = dlg.ShowModal();
if (rtn != wxID_OK) {
+1 -1
View File
@@ -2202,7 +2202,7 @@ void AmsReplaceMaterialDialog::create()
label_txt->SetMaxSize(wxSize(FromDIP(380), -1));
label_txt->Wrap(FromDIP(380));
identical_filament = new Label(this, _L("Identical filament: same brand, type and color"));
identical_filament = new Label(this, _L("Identical filament: same brand, type and color."));
identical_filament->SetFont(Label::Body_13);
identical_filament->SetForegroundColour(StateColor::darkModeColorFor(wxColour("#009688")));
+30 -4
View File
@@ -13,6 +13,10 @@
#include <boost/log/trivial.hpp>
#ifdef __WXGTK__
#include <gtk/gtk.h>
#endif
#define TOPBAR_ICON_SIZE 18
#define TOPBAR_TITLE_WIDTH 300
@@ -532,6 +536,18 @@ void BBLTopbar::OnIconize(wxAuiToolBarEvent& event)
void BBLTopbar::OnFullScreen(wxAuiToolBarEvent& event)
{
#ifdef __WXGTK__
GtkWindow* gtk_window = GTK_WINDOW(m_frame->m_widget);
if (gtk_window_is_maximized(gtk_window)) {
gtk_window_unmaximize(gtk_window);
}
else {
m_normalRect = m_frame->GetRect();
gtk_window_maximize(gtk_window);
}
return;
#endif
if (m_frame->IsMaximized()) {
m_frame->Restore();
}
@@ -621,17 +637,27 @@ void BBLTopbar::OnMouseLeftDown(wxMouseEvent& event)
wxPoint frame_pos = m_frame->GetScreenPosition();
m_delta = mouse_pos - frame_pos;
if (FindToolByCurrentPosition() == NULL
if (FindToolByCurrentPosition() == NULL
|| this->FindToolByCurrentPosition() == m_title_item)
{
CaptureMouse();
#ifdef __WXMSW__
CaptureMouse();
ReleaseMouse();
::PostMessage((HWND) m_frame->GetHandle(), WM_NCLBUTTONDOWN, HTCAPTION, MAKELPARAM(mouse_pos.x, mouse_pos.y));
return;
#endif // __WXMSW__
#elif defined(__WXGTK__)
// Use WM-integrated drag for smoother window movement on Linux.
gtk_window_begin_move_drag(
GTK_WINDOW(m_frame->m_widget),
1, // left mouse button
mouse_pos.x, mouse_pos.y,
gtk_get_current_event_time());
return;
#else
CaptureMouse();
#endif
}
event.Skip();
}
+1 -1
View File
@@ -303,7 +303,7 @@ bool CalibrationWizard::save_preset_with_index(const std::string &old_preset_nam
PresetCollection *filament_presets = &wxGetApp().preset_bundle->filaments;
Preset *preset = filament_presets->find_preset(old_preset_name);
if (!preset) {
message = wxString::Format(_L("The selected preset: %s is not found."), old_preset_name);
message = wxString::Format(_L("The selected preset: %s was not found."), old_preset_name);
return false;
}
@@ -764,7 +764,7 @@ void CalibrationPresetPage::create_selection_panel(wxWindow* parent)
m_filament_from_panel = new wxPanel(parent);
m_filament_from_panel->Hide();
auto filament_from_sizer = new wxBoxSizer(wxVERTICAL);
auto filament_from_text = new Label(m_filament_from_panel, _L("filament position"));
auto filament_from_text = new Label(m_filament_from_panel, _L("Filament position"));
filament_from_text->SetFont(Label::Head_14);
filament_from_sizer->Add(filament_from_text, 0);
+4 -1
View File
@@ -816,9 +816,12 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co
toggle_line("preheat_steps", have_ooze_prevention && (preheat_steps > 0));
bool have_prime_tower = config->opt_bool("enable_prime_tower");
for (auto el : {"prime_tower_width", "prime_tower_brim_width", "prime_tower_skip_points", "wipe_tower_wall_type", "prime_tower_infill_gap","prime_tower_enable_framework"})
for (auto el : {"prime_tower_width", "prime_tower_brim_width", "prime_tower_skip_points", "wipe_tower_wall_type", "prime_tower_infill_gap","prime_tower_enable_framework", "enable_tower_interface_features"})
toggle_line(el, have_prime_tower);
toggle_line("enable_tower_interface_cooldown_during_tower",
have_prime_tower && config->opt_bool("enable_tower_interface_features"));
for (auto el : {"wall_filament", "sparse_infill_filament", "solid_infill_filament", "wipe_tower_filament"})
toggle_line(el, !bSEMM);
+1 -1
View File
@@ -3724,7 +3724,7 @@ wxBoxSizer *ExportConfigsDialog::create_export_config_item(wxWindow *parent)
radioBoxSizer->Add(create_radio_item(m_exprot_type.preset_bundle, parent, wxEmptyString, m_export_type_btns), 0, wxEXPAND | wxALL, 0);
radioBoxSizer->Add(0, 0, 0, wxTOP, FromDIP(6));
wxStaticText *static_export_printer_preset_bundle_text = new wxStaticText(parent, wxID_ANY, _L("Printer and all the filament&&process presets that belongs to the printer.\n"
wxStaticText *static_export_printer_preset_bundle_text = new wxStaticText(parent, wxID_ANY, _L("Printer and all the filament and process presets that belongs to the printer.\n"
"Can be shared with others."), wxDefaultPosition, wxDefaultSize);
static_export_printer_preset_bundle_text->SetFont(Label::Body_12);
static_export_printer_preset_bundle_text->SetForegroundColour(wxColour("#6B6B6B"));
+1 -1
View File
@@ -150,7 +150,7 @@ wxString Slic3r::get_stage_string(int stage)
case 39:
return _L("Nozzle offset calibration");
case 40:
return _L("high temperature auto bed leveling");
return _L("High temperature auto bed leveling");
case 41:
return _L("Auto Check: Quick Release Lever");
case 42:
-4
View File
@@ -1,7 +1,3 @@
# GUI/DeviceTab
# usage -- GUI about device tab for BambuStudio
# date -- 2025.01.01
# status -- Building
list(APPEND SLIC3R_GUI_SOURCES
GUI/DeviceTab/uiAmsHumidityPopup.h
+5 -7
View File
@@ -31,16 +31,14 @@
namespace Slic3r {
namespace GUI {
DownloadProgressDialog::DownloadProgressDialog(wxString title)
: DPIDialog(static_cast<wxWindow *>(wxGetApp().mainframe), wxID_ANY, title, wxDefaultPosition, wxDefaultSize, wxCAPTION | wxCLOSE_BOX)
{
wxString download_failed_url = wxT("https://wiki.bambulab.com/en/software/bambu-studio/failed-to-get-network-plugin");
wxString install_failed_url = wxT("https://wiki.bambulab.com/en/software/bambu-studio/failed-to-get-network-plugin");
wxString download_failed_msg = _L("Failed to download the plug-in. Please check your firewall settings and vpn software, check and retry.");
wxString install_failed_msg = _L("Failed to install the plug-in. Please check whether it is blocked or deleted by anti-virus software.");
wxString download_failed_msg = _L("Failed to download the plug-in. Please check your firewall settings and VPN software and retry.");
wxString install_failed_msg = _L("Failed to install the plug-in. The plug-in file may be in use. Please restart OrcaSlicer and try again. Also check whether it is blocked or deleted by anti-virus software.");
SetBackgroundColour(*wxWHITE);
wxBoxSizer *m_sizer_main = new wxBoxSizer(wxVERTICAL);
@@ -76,7 +74,7 @@ DownloadProgressDialog::DownloadProgressDialog(wxString title)
sizer_download_failed->Add(m_statictext_download_failed, 0, wxALIGN_CENTER | wxALL, 5);
// ORCA standardized HyperLink
auto m_download_hyperlink = new HyperLink(m_panel_download_failed, _L("click here to see more info"), download_failed_url);
auto m_download_hyperlink = new HyperLink(m_panel_download_failed, _L("Click here to see more info"), download_failed_url);
sizer_download_failed->Add(m_download_hyperlink, 0, wxALIGN_CENTER | wxALL, 5);
@@ -98,7 +96,7 @@ DownloadProgressDialog::DownloadProgressDialog(wxString title)
sizer_install_failed->Add(m_statictext_install_failed, 0, wxALIGN_CENTER | wxALL, 5);
// ORCA standardized HyperLink
auto m_install_hyperlink = new HyperLink(m_panel_install_failed, _L("click here to see more info"), install_failed_url);
auto m_install_hyperlink = new HyperLink(m_panel_install_failed, _L("Click here to see more info"), install_failed_url);
sizer_install_failed->Add(m_install_hyperlink, 0, wxALIGN_CENTER | wxALL, 5);
@@ -215,7 +213,7 @@ void DownloadProgressDialog::on_finish()
}
MessageDialog dlg(nullptr,
_L("The network plugin was installed but could not be loaded. Please restart the application."),
_L("The network plug-in was installed but could not be loaded. Please restart the application."),
_L("Restart Required"), wxOK | wxICON_INFORMATION);
dlg.ShowModal();
}
+87 -29
View File
@@ -101,6 +101,45 @@ ThumbnailErrors validate_thumbnails_string(wxString& str, const wxString& def_ex
return errors;
}
wxString get_formatted_tooltip_text(const ConfigOptionDef& opt, const t_config_option_key& id)
{
wxString tooltip = _(opt.tooltip);
if (tooltip.length() > 0) {
edit_tooltip(tooltip);
std::string opt_id = id;
auto hash_pos = opt_id.find("#");
if (hash_pos != std::string::npos) {
opt_id.replace(hash_pos, 1,"[");
opt_id += "]";
}
tooltip += "\n\n" + _(L("parameter name")) + ": " + opt_id;
if (opt.type == coFloat || opt.type == coInt) {
double default_value = 0.;
if (opt.type == coFloat)
default_value = opt.get_default_value<ConfigOptionFloat>()->value;
else if (opt.type == coInt)
default_value = opt.get_default_value<ConfigOptionInt>()->value;
tooltip += "\n\n" + _(L("Default")) + ": " + _(double_to_string(default_value));
if (opt.min > -FLT_MAX && opt.max < FLT_MAX) {
tooltip += "\n" + _(L("Range")) + ": [" +
_(double_to_string(opt.min)) + ", " +
_(double_to_string(opt.max)) + "]";
}
}
return tooltip;
}
return "";
}
Field::~Field()
{
if (m_on_kill_focus)
@@ -223,23 +262,9 @@ void Field::toggle(bool en) { en && !m_opt.readonly ? enable() : disable(); }
wxString Field::get_tooltip_text(const wxString &default_string)
{
wxString tooltip_text("");
#ifdef NDEBUG
wxString tooltip = _(m_opt.tooltip);
::edit_tooltip(tooltip);
wxString tooltip_text = get_formatted_tooltip_text(m_opt, m_opt_id);
std::string opt_id = m_opt_id;
auto hash_pos = opt_id.find("#");
if (hash_pos != std::string::npos) {
opt_id.replace(hash_pos, 1,"[");
opt_id += "]";
}
if (tooltip.length() > 0)
tooltip_text = tooltip + "\n" +
_(L("parameter name")) + "\t: " + opt_id;
#endif
return tooltip_text;
return tooltip_text.length() > 0 ? tooltip_text : default_string;
}
bool Field::is_matched(const std::string& string, const std::string& pattern)
@@ -251,9 +276,15 @@ bool Field::is_matched(const std::string& string, const std::string& pattern)
void Field::get_value_by_opt_type(wxString& str, const bool check_value/* = true*/)
{
switch (m_opt.type) {
case coInts:
case coInt: {
long val = 0;
if (!str.ToLong(&val)) {
bool is_na_value = m_opt.nullable && str == m_na_value;
if (is_na_value)
val = ConfigOptionIntsNullable::nil_value();
else if (!str.ToLong(&val)) {
if (!check_value) {
m_value.clear();
break;
@@ -261,6 +292,27 @@ void Field::get_value_by_opt_type(wxString& str, const bool check_value/* = true
show_error(m_parent, _(L("Invalid numeric.")));
set_value(int(val), true);
}
if (!m_opt.is_value_valid(double(val))) {
if (!check_value) {
m_value.clear();
break;
}
if (!is_na_value) {
// Orca: no need to check ranges for the nil value
show_error(m_parent, _L("Value is out of range."));
int min = static_cast<int>(m_opt.min);
int max = static_cast<int>(m_opt.max);
if (min > val) val = min;
if (val > max) val = max;
set_value(int(val), true);
}
}
m_value = int(val);
break;
}
@@ -309,7 +361,7 @@ void Field::get_value_by_opt_type(wxString& str, const bool check_value/* = true
show_error(m_parent, _(L("Invalid numeric.")));
set_value(double_to_string(val), true);
}
if (m_opt.min > val || val > m_opt.max)
if (!m_opt.is_value_valid(val))
{
if (!check_value) {
m_value.clear();
@@ -347,7 +399,8 @@ void Field::get_value_by_opt_type(wxString& str, const bool check_value/* = true
}
}
}
else {
else if (!is_na_value) {
// Orca: no need to check ranges for the nil value
show_error(m_parent, _L("Value is out of range."));
if (m_opt.min > val) val = m_opt.min;
if (val > m_opt.max) val = m_opt.max;
@@ -830,13 +883,18 @@ bool TextCtrl::value_was_changed()
case coInt:
return boost::any_cast<int>(m_value) != boost::any_cast<int>(val);
case coPercent:
case coPercents:
case coPercents: {
if (m_opt.nullable && std::isnan(boost::any_cast<double>(m_value)) &&
std::isnan(boost::any_cast<double>(val)))
return false;
return boost::any_cast<double>(m_value) != boost::any_cast<double>(val);
}
case coFloats:
case coFloat: {
if (m_opt.nullable && std::isnan(boost::any_cast<double>(m_value)) &&
std::isnan(boost::any_cast<double>(val)))
return false;
return boost::any_cast<double>(m_value) != boost::any_cast<double>(val);
return !is_approx(boost::any_cast<double>(m_value), boost::any_cast<double>(val));
}
case coString:
case coStrings:
@@ -1088,8 +1146,8 @@ void SpinCtrl::BUILD() {
break;
}
const int min_val = m_opt.min == INT_MIN ? 0 : m_opt.min;
const int max_val = m_opt.max < 2147483647 ? m_opt.max : 2147483647;
const int min_val = m_opt.min == -FLT_MAX ? 0 : (int)m_opt.min;
const int max_val = m_opt.max < FLT_MAX ? (int)m_opt.max : INT_MAX;
static Builder<SpinInput> builder;
auto temp = builder.build(m_parent, "", "", wxDefaultPosition, size,
@@ -1145,7 +1203,7 @@ void SpinCtrl::BUILD() {
if (!parsed || value < INT_MIN || value > INT_MAX)
tmp_value = UNDEF_VALUE;
else {
tmp_value = std::min(std::max((int)value, m_opt.min), m_opt.max);
tmp_value = std::min(std::max((int)value, temp->GetMin()), temp->GetMax());
#ifdef __WXOSX__
#ifdef UNDEFINED__WXOSX__ // BBS
// Forcibly set the input value for SpinControl, since the value
@@ -1198,7 +1256,7 @@ void SpinCtrl::set_value(const boost::any& value, bool change_event) {
m_disable_change_event = !change_event;
m_value = value;
if (value.empty()) { // BBS: null value
dynamic_cast<SpinInput*>(window)->SetValue(m_opt.min);
dynamic_cast<SpinInput*>(window)->SetValue(dynamic_cast<SpinInput*>(window)->GetMin());
dynamic_cast<SpinInput*>(window)->GetTextCtrl()->SetValue("");
}
else {
@@ -2139,8 +2197,8 @@ boost::any& PointCtrl::get_value()
show_error(m_parent, _L("Invalid numeric."));
}
else
if (m_opt.min > x || x > m_opt.max ||
m_opt.min > y || y > m_opt.max)
if (!m_opt.is_value_valid(x) ||
!m_opt.is_value_valid(y))
{
if (m_opt.min > x) x = m_opt.min;
if (x > m_opt.max) x = m_opt.max;
@@ -2199,8 +2257,8 @@ void SliderCtrl::BUILD()
auto temp = new wxBoxSizer(wxHORIZONTAL);
auto def_val = m_opt.get_default_value<ConfigOptionInt>()->value;
auto min = m_opt.min == INT_MIN ? 0 : m_opt.min;
auto max = m_opt.max == INT_MAX ? 100 : m_opt.max;
auto min = m_opt.min == -FLT_MAX ? 0 : (int)m_opt.min;
auto max = m_opt.max == FLT_MAX ? 100 : INT_MAX;
m_slider = new wxSlider(m_parent, wxID_ANY, def_val * m_scale,
min * m_scale, max * m_scale,
+1
View File
@@ -43,6 +43,7 @@ using t_back_to_init = std::function<void(const std::string&)>;
wxString double_to_string(double const value, const int max_precision = 4);
wxString get_thumbnail_string(const Vec2d& value);
wxString get_thumbnails_string(const std::vector<Vec2d>& values);
wxString get_formatted_tooltip_text(const ConfigOptionDef& opt, const t_config_option_key& id);
class UndoValueUIManager
{
+11
View File
@@ -207,6 +207,17 @@ FilamentMapDialog::FilamentMapDialog(wxWindow *parent,
m_ok_btn->Bind(wxEVT_BUTTON, &FilamentMapDialog::on_ok, this);
m_cancel_btn->Bind(wxEVT_BUTTON, &FilamentMapDialog::on_cancle, this);
SetEscapeId(wxID_CANCEL);
Bind(wxEVT_CHAR_HOOK, [this](wxKeyEvent& e) {
if (e.GetKeyCode() == WXK_ESCAPE) {
if (IsModal())
EndModal(wxID_CANCEL);
else
Close();
return;
}
e.Skip();
});
m_auto_btn->Bind(wxEVT_BUTTON, &FilamentMapDialog::on_switch_mode, this);
m_manual_btn->Bind(wxEVT_BUTTON, &FilamentMapDialog::on_switch_mode, this);
+23 -2
View File
@@ -91,6 +91,9 @@ static std::string get_view_type_string(libvgcode::EViewType view_type)
return _u8L("Layer Time");
else if (view_type == libvgcode::EViewType::LayerTimeLogarithmic)
return _u8L("Layer Time (log)");
// ORCA: Add Pressure Advance visualization support
else if (view_type == libvgcode::EViewType::PressureAdvance)
return _u8L("Pressure Advance");
return "";
}
@@ -397,6 +400,11 @@ void GCodeViewer::SequentialView::Marker::render_position_window(const libvgcode
sprintf(buff, ("%.0f " + _u8L("°C")).c_str(), vertex.temperature);
ImGuiWrapper::text(std::string(buff));
});
// ORCA: Add Pressure Advance visualization support
append_table_row(_u8L("Pressure Advance"), [&vertex, &buff]() {
sprintf(buff, "%.4f", vertex.pressure_advance);
ImGuiWrapper::text(std::string(buff));
});
append_table_row(_u8L("Time"), [viewer, &vertex, &buff, vertex_id]() {
const float estimated_time = viewer->get_estimated_time_at(vertex_id);
sprintf(buff, "%s (%.3fs)", get_time_dhms(estimated_time).c_str(), vertex.times[static_cast<size_t>(viewer->get_time_mode())]);
@@ -607,6 +615,11 @@ void GCodeViewer::SequentialView::Marker::render_position_window(const libvgcode
sprintf(buf, "%s %s%.1f", buf, _u8L("Actual Speed: ").c_str(), vertex.actual_feedrate);
break;
}
// ORCA: Add Pressure Advance visualization support
case libvgcode::EViewType::PressureAdvance: {
sprintf(buf, "%s %s%.4f", buf, _u8L("PA: ").c_str(), vertex.pressure_advance);
break;
}
default:
break;
@@ -1024,6 +1037,8 @@ void GCodeViewer::update_by_mode(ConfigOptionMode mode)
view_type_items.push_back(libvgcode::EViewType::LayerTimeLogarithmic);
view_type_items.push_back(libvgcode::EViewType::FanSpeed);
view_type_items.push_back(libvgcode::EViewType::Temperature);
// ORCA: Add Pressure Advance visualization support
view_type_items.push_back(libvgcode::EViewType::PressureAdvance);
//if (mode == ConfigOptionMode::comDevelop) {
// view_type_items.push_back(EViewType::Tool);
//}
@@ -2250,6 +2265,8 @@ void GCodeViewer::render_toolpaths()
add_range_property_row("speed range", m_viewer.get_color_range(libvgcode::EViewType::Speed).get_range());
add_range_property_row("fan speed range", m_viewer.get_color_range(libvgcode::EViewType::FanSpeed).get_range());
add_range_property_row("temperature range", m_viewer.get_color_range(libvgcode::EViewType::Temperature).get_range());
// ORCA: Add Pressure Advance visualization support
add_range_property_row("pressure advance range", m_viewer.get_color_range(libvgcode::EViewType::PressureAdvance).get_range());
add_range_property_row("volumetric rate range", m_viewer.get_color_range(libvgcode::EViewType::VolumetricFlowRate).get_range());
add_range_property_row("layer time linear range", m_viewer.get_color_range(libvgcode::EViewType::LayerTimeLinear).get_range());
add_range_property_row("layer time logarithmic range", m_viewer.get_color_range(libvgcode::EViewType::LayerTimeLogarithmic).get_range());
@@ -3498,6 +3515,8 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv
}
case libvgcode::EViewType::FanSpeed: { imgui.title(_u8L("Fan Speed (%)")); break; }
case libvgcode::EViewType::Temperature: { imgui.title(_u8L("Temperature (°C)")); break; }
// ORCA: Add Pressure Advance visualization support
case libvgcode::EViewType::PressureAdvance:{ imgui.title(_u8L("Pressure Advance")); break; }
case libvgcode::EViewType::VolumetricFlowRate:
{ imgui.title(_u8L("Volumetric flow rate (mm³/s)")); break; }
case libvgcode::EViewType::ActualVolumetricFlowRate:
@@ -3647,7 +3666,7 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv
append_headers({ {_u8L("Options"), offsets[0] }, { _u8L("Display"), offsets[1]} });
const bool travel_visible = m_viewer.is_option_visible(libvgcode::EOptionType::Travels);
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 3.0f));
append_item(EItemType::None, libvgcode::convert(m_viewer.get_option_color(libvgcode::EOptionType::Travels)), { {_u8L("travel"), offsets[0] }}, true, predictable_icon_pos/*ORCA checkbox_pos*/, travel_visible, [this, travel_visible]() {
append_item(EItemType::None, libvgcode::convert(m_viewer.get_option_color(libvgcode::EOptionType::Travels)), { {_u8L("Travel"), offsets[0] }}, true, predictable_icon_pos/*ORCA checkbox_pos*/, travel_visible, [this, travel_visible]() {
m_viewer.toggle_option_visibility(libvgcode::EOptionType::Travels);
// refresh(*m_gcode_result, wxGetApp().plater()->get_extruder_colors_from_plater_config(m_gcode_result));
update_moves_slider();
@@ -3664,7 +3683,7 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv
append_headers({ {_u8L("Options"), offsets[0] }, { _u8L("Display"), offsets[1]} });
const bool travel_visible = m_viewer.is_option_visible(libvgcode::EOptionType::Travels);
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 3.0f));
append_item(EItemType::None, libvgcode::convert(m_viewer.get_option_color(libvgcode::EOptionType::Travels)), { {_u8L("travel"), offsets[0] }}, true, predictable_icon_pos/*ORCA checkbox_pos*/, travel_visible, [this, travel_visible]() {
append_item(EItemType::None, libvgcode::convert(m_viewer.get_option_color(libvgcode::EOptionType::Travels)), { {_u8L("Travel"), offsets[0] }}, true, predictable_icon_pos/*ORCA checkbox_pos*/, travel_visible, [this, travel_visible]() {
m_viewer.toggle_option_visibility(libvgcode::EOptionType::Travels);
// refresh(*m_gcode_result, wxGetApp().plater()->get_extruder_colors_from_plater_config(m_gcode_result));
update_moves_slider();
@@ -3674,6 +3693,8 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv
}
case libvgcode::EViewType::FanSpeed: { append_range(m_viewer.get_color_range(libvgcode::EViewType::FanSpeed), 0); break; }
case libvgcode::EViewType::Temperature: { append_range(m_viewer.get_color_range(libvgcode::EViewType::Temperature), 0); break; }
// ORCA: Add Pressure Advance visualization support
case libvgcode::EViewType::PressureAdvance: { append_range(m_viewer.get_color_range(libvgcode::EViewType::PressureAdvance), 3); break; }
case libvgcode::EViewType::LayerTimeLinear: { append_range(m_viewer.get_color_range(libvgcode::EViewType::LayerTimeLinear), true); break; }
case libvgcode::EViewType::LayerTimeLogarithmic: { append_range(m_viewer.get_color_range(libvgcode::EViewType::LayerTimeLogarithmic), true); break; }
case libvgcode::EViewType::VolumetricFlowRate: { append_range(m_viewer.get_color_range(libvgcode::EViewType::VolumetricFlowRate), 2); break; }
+32 -20
View File
@@ -1216,12 +1216,12 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, Bed3D &bed)
m_selection.set_volumes(&m_volumes.volumes);
m_assembly_view_desc["object_selection_caption"] = _L("Left mouse button");
m_assembly_view_desc["object_selection"] = _L("object selection");
m_assembly_view_desc["object_selection"] = _L("Object selection");
// FIXME: maybe should be using GUI::shortkey_alt_prefix() or equivalent?
m_assembly_view_desc["part_selection_caption"] = _L("Alt+") + _L("Left mouse button");
m_assembly_view_desc["part_selection"] = _L("part selection");
m_assembly_view_desc["part_selection"] = _L("Part selection");
m_assembly_view_desc["number_key_caption"] = "1~16 " + _L("number keys");
m_assembly_view_desc["number_key"] = _L("number keys can quickly change the color of objects");
m_assembly_view_desc["number_key"] = _L("Number keys can quickly change the color of objects");
}
GLCanvas3D::~GLCanvas3D()
@@ -1254,7 +1254,7 @@ bool GLCanvas3D::init()
// Controls the display of object names directly over the object
m_labels.show(wxGetApp().app_config->get_bool("show_labels"));
// Controls the color coding of overhang surfaces
m_slope.globalUse(wxGetApp().app_config->get_bool("show_labels"));
m_slope.globalUse(wxGetApp().app_config->get_bool("show_overhang"));
BOOST_LOG_TRIVIAL(info) <<__FUNCTION__<< " enter";
glsafe(::glClearColor(1.0f, 1.0f, 1.0f, 1.0f));
@@ -3272,8 +3272,12 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
#else /* __APPLE__ */
case WXK_CONTROL_A:
#endif /* __APPLE__ */
if (!is_in_painting_mode && !m_layers_editing.is_enabled())
post_event(SimpleEvent(EVT_GLCANVAS_SELECT_ALL));
if (!is_in_painting_mode && !m_layers_editing.is_enabled()) {
if (evt.ShiftDown())
post_event(SimpleEvent(EVT_GLCANVAS_SELECT_ALL));
else
post_event(SimpleEvent(EVT_GLCANVAS_SELECT_CURR_PLATE_ALL));
}
break;
#ifdef __APPLE__
case 'c':
@@ -8541,9 +8545,11 @@ void GLCanvas3D::_render_canvas_toolbar()
zoom_to_selection();
}
} else if (ImGui::IsItemHovered()) {
auto tooltip = _L("Fit camera to scene or selected object.");
auto width = ImGui::CalcTextSize(tooltip.c_str()).x + imgui.scaled(2.0f);
imgui.tooltip(tooltip, width);
auto tooltip_str_wx = _L("Fit camera to scene or selected object.");
std::string tooltip_str = tooltip_str_wx.ToUTF8().data();
float width = ImGui::CalcTextSize(tooltip_str.c_str()).x + imgui.scaled(2.0f);
imgui.tooltip(tooltip_str, width);
}
}
@@ -8582,7 +8588,7 @@ void GLCanvas3D::_render_canvas_toolbar()
ImGui::TextColored(enable ? ImVec4(1,1,1,1) : ImGui::GetStyleColorVec4(ImGuiCol_TextDisabled), "%s", into_u8(condition ? ImGui::VisibleIcon : ImGui::HiddenIcon).c_str());
};
create_menu_item( "3D Navigator",
create_menu_item( _utf8(L("3D Navigator")),
m_canvas_type != ECanvasType::CanvasAssembleView, // not work on assembly
wxGetApp().show_3d_navigator(),
[this]{
@@ -8591,7 +8597,7 @@ void GLCanvas3D::_render_canvas_toolbar()
}
);
create_menu_item( "Zoom button",
create_menu_item( _utf8(L("Zoom button")),
true, // work on all
wxGetApp().show_canvas_zoom_button(),
[this]{
@@ -8602,13 +8608,13 @@ void GLCanvas3D::_render_canvas_toolbar()
ImGui::Separator();
create_menu_item( "Overhangs",
create_menu_item( _utf8(L("Overhangs")),
m_canvas_type == ECanvasType::CanvasView3D, // work only on prepare
p->is_view3D_overhang_shown(),
[this, p]{p->show_view3D_overhang(!p->is_view3D_overhang_shown());}
);
create_menu_item( "Outline",
create_menu_item( _utf8(L("Outline")),
m_canvas_type != ECanvasType::CanvasPreview, // not work on preview
wxGetApp().show_outline(),
[this]{wxGetApp().toggle_show_outline();}
@@ -8616,7 +8622,7 @@ void GLCanvas3D::_render_canvas_toolbar()
ImGui::Separator();
create_menu_item( "Perspective",
create_menu_item( _utf8(L("Perspective")),
true, // work on all
cfg->get_bool("use_perspective_camera"),
[this, &cfg]{
@@ -8627,15 +8633,21 @@ void GLCanvas3D::_render_canvas_toolbar()
ImGui::Separator();
create_menu_item( "Axes",
create_menu_item( _utf8(L("Axes")),
m_canvas_type != ECanvasType::CanvasAssembleView, // not work on assembly
m_show_world_axes,
[this]{toggle_world_axes_visibility(false);}
);
// will add an option for gridlines in here
create_menu_item( _utf8(L("Gridlines")),
m_canvas_type != ECanvasType::CanvasAssembleView, // not work on assembly
wxGetApp().show_plate_gridlines(),
[this]{wxGetApp().toggle_show_plate_gridlines();}
);
create_menu_item( "Labels",
ImGui::Separator();
create_menu_item( _utf8(L("Labels")),
m_canvas_type == ECanvasType::CanvasView3D, // work only on prepare
p->are_view3D_labels_shown(),
[this, p]{p->show_view3D_labels(!p->are_view3D_labels_shown());}
@@ -8922,7 +8934,7 @@ void GLCanvas3D::_render_assemble_control()
caption_max = std::max(caption_max, imgui->calc_text_size(m_assembly_view_desc.at(t + "_caption")).x);
}
const ImVec2 pos = ImGui::GetCursorScreenPos();
const float text_y =imgui->calc_text_size(_L("part selection")).y;
const float text_y = imgui->calc_text_size(_L("Part selection")).y;
float get_cur_x = pos.x;
float get_cur_y = pos.y - ImGui::GetFrameHeight() - 4 * text_y;
tip_icon_size =_show_assembly_tooltip_information(caption_max, get_cur_x, get_cur_y);
@@ -9643,7 +9655,7 @@ void GLCanvas3D::_set_warning_notification(EWarning warning, bool state)
warning += std::to_string(filament + 1);
warning += " ";
}
text = (boost::format(_u8L("filaments %s cannot be printed directly on the surface of this plate.")) % warning).str();
text = (boost::format(_u8L("Filaments %s cannot be printed directly on the surface of this plate.")) % warning).str();
error = ErrorType::SLICING_ERROR;
break;
}
@@ -9665,7 +9677,7 @@ void GLCanvas3D::_set_warning_notification(EWarning warning, bool state)
break;
}
case EWarning::FlushingVolumeZero:
text = _u8L("Partial flushing volume set to 0. Multi-color printing may cause color mixing in models. Please redjust flushing settings.");
text = _u8L("Partial flushing volume set to 0. Multi-color printing may cause color mixing in models. Please readjust flushing settings.");
error = ErrorType::SLICING_ERROR;
break;
}
+49 -28
View File
@@ -523,34 +523,34 @@ bool static check_old_linux_datadir(const wxString& app_name) {
#endif
struct FileWildcards {
std::string_view title;
const char* title_id;
std::vector<std::string_view> file_extensions;
};
static const FileWildcards file_wildcards_by_type[FT_SIZE] = {
/* FT_STEP */ { "STEP files"sv, { ".stp"sv, ".step"sv } },
/* FT_STL */ { "STL files"sv, { ".stl"sv } },
/* FT_OBJ */ { "OBJ files"sv, { ".obj"sv } },
/* FT_AMF */ { "AMF files"sv, { ".amf"sv, ".zip.amf"sv, ".xml"sv } },
/* FT_3MF */ { "3MF files"sv, { ".3mf"sv } },
/* FT_GCODE_3MF */ {"Gcode 3MF files"sv, {".gcode.3mf"sv}},
/* FT_GCODE */ { "G-code files"sv, { ".gcode"sv} },
/* FT_STEP */ { L("STEP files"), { ".stp"sv, ".step"sv } },
/* FT_STL */ { L("STL files"), { ".stl"sv } },
/* FT_OBJ */ { L("OBJ files"), { ".obj"sv } },
/* FT_AMF */ { L("AMF files"), { ".amf"sv, ".zip.amf"sv, ".xml"sv } },
/* FT_3MF */ { L("3MF files"), { ".3mf"sv } },
/* FT_GCODE_3MF */ {L("Gcode 3MF files"), {".gcode.3mf"sv}},
/* FT_GCODE */ { L("G-code files"), { ".gcode"sv} },
#ifdef __APPLE__
/* FT_MODEL */
{"Supported files"sv, {".3mf"sv, ".stl"sv, ".oltp"sv, ".stp"sv, ".step"sv, ".svg"sv, ".amf"sv, ".obj"sv, ".usd"sv, ".usda"sv, ".usdc"sv, ".usdz"sv, ".abc"sv, ".ply"sv, ".drc"sv}},
{L("Supported files"), {".3mf"sv, ".stl"sv, ".oltp"sv, ".stp"sv, ".step"sv, ".svg"sv, ".amf"sv, ".obj"sv, ".usd"sv, ".usda"sv, ".usdc"sv, ".usdz"sv, ".abc"sv, ".ply"sv, ".drc"sv}},
#else
/* FT_MODEL */
{"Supported files"sv, {".3mf"sv, ".stl"sv, ".oltp"sv, ".stp"sv, ".step"sv, ".svg"sv, ".amf"sv, ".obj"sv, ".drc"sv}},
{L("Supported files"), {".3mf"sv, ".stl"sv, ".oltp"sv, ".stp"sv, ".step"sv, ".svg"sv, ".amf"sv, ".obj"sv, ".drc"sv}},
#endif
/* FT_ZIP */ { "ZIP files"sv, { ".zip"sv } },
/* FT_PROJECT */ { "Project files"sv, { ".3mf"sv} },
/* FT_GALLERY */ { "Known files"sv, { ".stl"sv, ".obj"sv } },
/* FT_ZIP */ { L("ZIP files"), { ".zip"sv } },
/* FT_PROJECT */ { L("Project files"), { ".3mf"sv} },
/* FT_GALLERY */ { L("Known files"), { ".stl"sv, ".obj"sv } },
/* FT_INI */ { "INI files"sv, { ".ini"sv } },
/* FT_SVG */ { "SVG files"sv, { ".svg"sv } },
/* FT_TEX */ { "Texture"sv, { ".png"sv, ".svg"sv } },
/* FT_SL1 */ { "Masked SLA files"sv, { ".sl1"sv, ".sl1s"sv } },
/* FT_DRC */ { "Draco files"sv, { ".drc"sv } },
/* FT_INI */ { L("INI files"), { ".ini"sv } },
/* FT_SVG */ { L("SVG files"), { ".svg"sv } },
/* FT_TEX */ { L("Texture"), { ".png"sv, ".svg"sv } },
/* FT_SL1 */ { L("Masked SLA files"), { ".sl1"sv, ".sl1s"sv } },
/* FT_DRC */ { L("Draco files"), { ".drc"sv } },
};
// This function produces a Win32 file dialog file template mask to be consumed by wxWidgets on all platforms.
@@ -605,7 +605,8 @@ wxString file_wildcards(FileType file_type, const std::string &custom_extension)
mask += ";*";
mask += boost::to_upper_copy(std::string(ext));
}
return GUI::format_wxstr("%s (%s)|%s", data.title, title, mask);
const wxString translated_title = Slic3r::GUI::I18N::translate(data.title_id);
return GUI::format_wxstr("%s (%s)|%s", translated_title, title, mask);
}
static std::string libslic3r_translate_callback(const char *s) { return wxGetTranslation(wxString(s, wxConvUTF8)).utf8_str().data(); }
@@ -872,7 +873,9 @@ void GUI_App::post_init()
}
if (!switch_to_3d) {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ", begin load_gl_resources";
#ifndef __linux__
mainframe->Freeze();
#endif
plater_->canvas3D()->enable_render(false);
mainframe->select_tab(size_t(MainFrame::tp3DEditor));
plater_->select_view_3D("3D");
@@ -909,7 +912,9 @@ void GUI_App::post_init()
mainframe->select_tab(size_t(0));
if (app_config->get("default_page") == "1")
mainframe->select_tab(size_t(1));
#ifndef __linux__
mainframe->Thaw();
#endif
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ", end load_gl_resources";
}
@@ -1717,7 +1722,7 @@ bool GUI_App::hot_reload_network_plugin()
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": starting hot reload";
wxBusyCursor busy;
wxBusyInfo info(_L("Reloading network plugin..."), mainframe);
wxBusyInfo info(_L("Reloading network plug-in..."), mainframe);
wxYield();
wxWindowDisabler disabler;
@@ -1860,7 +1865,7 @@ void GUI_App::show_network_plugin_download_dialog(bool is_update)
app_config->set_network_plugin_version(selected);
app_config->save();
DownloadProgressDialog download_dlg(_L("Downloading Network Plugin"));
DownloadProgressDialog download_dlg(_L("Downloading Network Plug-in"));
download_dlg.ShowModal();
}
break;
@@ -2716,6 +2721,18 @@ bool GUI_App::on_init_inner()
g_object_set (gtk_settings_get_default (), "gtk-menu-images", TRUE, NULL);
#endif
#if defined(__WXGTK20__) || defined(__WXGTK3__)
// Suppress harmless GTK critical warnings from the GTK3/wxWidgets interaction.
// These include widget allocation on hidden widgets and events on unrealized widgets.
g_log_set_handler("Gtk", G_LOG_LEVEL_CRITICAL,
[](const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer user_data) {
if (message && (strstr(message, "gtk_widget_set_allocation") ||
strstr(message, "WIDGET_REALIZED_FOR_EVENT")))
return;
g_log_default_handler(log_domain, log_level, message, user_data);
}, nullptr);
#endif
#ifdef WIN32
//BBS set crash log folder
CBaseException::set_log_folder(data_dir());
@@ -4416,7 +4433,7 @@ wxString GUI_App::transition_tridid(int trid_id) const
if (trid_id == VIRTUAL_TRAY_MAIN_ID || trid_id == VIRTUAL_TRAY_DEPUTY_ID)
{
assert(0);
return wxString("Ext");
return _L("Ext");
}
wxString maping_dict[] = { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" };
@@ -5450,7 +5467,7 @@ void GUI_App::check_new_version_sf(bool show_tips, int by_user)
}
version_info.url = prefer_release ? best_release_url : best_pre_url;
version_info.version_str = prefer_release ? best_release.to_string_sf() : best_pre.to_string();
version_info.version_str = prefer_release ? best_release.to_string_sf() : best_pre.to_string_sf();
version_info.description = prefer_release ? best_release_content : best_pre_content;
version_info.force_upgrade = false;
@@ -5535,11 +5552,15 @@ bool GUI_App::process_network_msg(std::string dev_id, std::string msg)
}
else if (msg == "unsigned_studio") {
BOOST_LOG_TRIVIAL(info) << "process_network_msg, unsigned_studio";
MessageDialog msg_dlg(nullptr,
_L("Bambu Lab has implemented a signature verification check in their network plugin that restricts "
"third-party software from communicating with your printer.\n\n"
"As a result, some printing functions are unavailable in OrcaSlicer."),
_L("Network Plugin Restriction"), wxAPPLY | wxOK);
MessageDialog
msg_dlg(nullptr,
_L("To use OrcaSlicer with Bambu Lab printers, you need to enable LAN mode and Developer mode on your printer.\n\n"
"Please go to your printer's settings and:\n"
"1. Turn on LAN mode\n"
"2. Enable Developer mode\n\n"
"Developer mode allows the printer to work exclusively through local network access, "
"enabling full functionality with OrcaSlicer."),
_L("Network Plug-in Restriction"), wxAPPLY | wxOK);
m_show_error_msgdlg = true;
msg_dlg.ShowModal();
m_show_error_msgdlg = false;
+3
View File
@@ -363,6 +363,9 @@ public:
bool show_3d_navigator() const { return app_config->get_bool("show_3d_navigator"); }
void toggle_show_3d_navigator() const { app_config->set_bool("show_3d_navigator", !show_3d_navigator()); }
bool show_plate_gridlines() const { return app_config->get_bool("show_plate_gridlines"); }
void toggle_show_plate_gridlines() const { app_config->set_bool("show_plate_gridlines", !show_plate_gridlines()); }
bool show_canvas_zoom_button() const { return app_config->get_bool("show_canvas_zoom_button"); }
void toggle_canvas_zoom_button() const { app_config->set_bool("show_canvas_zoom_button", !show_canvas_zoom_button()); }
+27 -6
View File
@@ -580,7 +580,7 @@ wxMenu* MenuFactory::append_submenu_add_handy_model(wxMenu* menu, ModelVolumeTyp
"Yes - Change these settings automatically\n"
"No - Do not change these settings for me");
MessageDialog dialog(wxGetApp().plater(), msg_text, "Suggestion", wxICON_WARNING | wxYES | wxNO);
MessageDialog dialog(wxGetApp().plater(), msg_text, _L("Suggestion"), wxICON_WARNING | wxYES | wxNO);
if (dialog.ShowModal() == wxID_YES) {
m_config->set_key_value("min_width_top_surface", new ConfigOptionFloatOrPercent(0, false));
wxGetApp().get_tab(Preset::TYPE_PRINT)->update_dirty();
@@ -1351,6 +1351,8 @@ void MenuFactory::create_extra_object_menu()
append_menu_item_fix_through_netfabb(&m_object_menu);
// Object Simplify
append_menu_item_simplify(&m_object_menu);
// Object Mesh Subdivision
append_menu_item_smooth_mesh(&m_object_menu);
// merge to single part
append_menu_item_merge_parts_to_single_part(&m_object_menu);
// Object Center
@@ -1402,6 +1404,8 @@ void MenuFactory::create_bbl_assemble_object_menu()
append_menu_item_fix_through_netfabb(&m_assemble_object_menu);
// Object Simplify
append_menu_item_simplify(&m_assemble_object_menu);
// Object Mesh Subdivision
append_menu_item_smooth_mesh(&m_assemble_object_menu);
m_assemble_object_menu.AppendSeparator();
}
@@ -1485,6 +1489,7 @@ void MenuFactory::create_bbl_part_menu()
append_menu_item_edit_text(menu);
append_menu_item_fix_through_netfabb(menu);
append_menu_item_simplify(menu);
append_menu_item_smooth_mesh(menu);
append_menu_item_center(menu);
append_menu_item_drop(menu);
append_menu_items_mirror(menu);
@@ -1516,6 +1521,7 @@ void MenuFactory::create_bbl_assemble_part_menu()
append_menu_item_delete(menu);
append_menu_item_simplify(menu);
append_menu_item_smooth_mesh(menu);
menu->AppendSeparator();
}
@@ -1568,7 +1574,7 @@ void MenuFactory::create_plate_menu()
{
wxMenu* menu = &m_plate_menu;
// select objects on current plate
append_menu_item(menu, wxID_ANY, _L("Select All"), _L("select all objects on current plate"),
append_menu_item(menu, wxID_ANY, _L("Select All"), _L("Select all objects on the current plate"),
[](wxCommandEvent&) {
plater()->select_curr_plate_all();
}, "", nullptr, []() {
@@ -1577,8 +1583,16 @@ void MenuFactory::create_plate_menu()
return !plate->get_objects().empty();
}, m_parent);
// select objects on all plates
append_menu_item(menu, wxID_ANY, _L("Select All Plates"), _L("Select all objects on all plates"),
[](wxCommandEvent&) {
plater()->select_all();
}, "", nullptr, []() {
return !plater()->model().objects.empty();
}, m_parent);
// delete objects on current plate
append_menu_item(menu, wxID_ANY, _L("Delete All"), _L("delete all objects on current plate"),
append_menu_item(menu, wxID_ANY, _L("Delete All"), _L("Delete all objects on the current plate"),
[](wxCommandEvent&) {
plater()->remove_curr_plate_all();
}, "", nullptr, []() {
@@ -1588,7 +1602,7 @@ void MenuFactory::create_plate_menu()
}, m_parent);
// arrange objects on current plate
append_menu_item(menu, wxID_ANY, _L("Arrange"), _L("arrange current plate"),
append_menu_item(menu, wxID_ANY, _L("Arrange"), _L("Arrange current plate"),
[](wxCommandEvent&) {
PartPlate* plate = plater()->get_partplate_list().get_selected_plate();
assert(plate);
@@ -1602,7 +1616,7 @@ void MenuFactory::create_plate_menu()
// reload all objects on current plate
append_menu_item(
menu, wxID_ANY, _L("Reload All"), _L("reload all from disk"),
menu, wxID_ANY, _L("Reload All"), _L("Reload all from disk"),
[](wxCommandEvent&) {
PartPlate* plate = plater()->get_partplate_list().get_selected_plate();
assert(plate);
@@ -1612,7 +1626,7 @@ void MenuFactory::create_plate_menu()
"", nullptr, []() { return !plater()->get_partplate_list().get_selected_plate()->get_objects().empty(); }, m_parent);
// orient objects on current plate
append_menu_item(menu, wxID_ANY, _L("Auto Rotate"), _L("auto rotate current plate"),
append_menu_item(menu, wxID_ANY, _L("Auto Rotate"), _L("Auto rotate current plate"),
[](wxCommandEvent&) {
PartPlate* plate = plater()->get_partplate_list().get_selected_plate();
assert(plate);
@@ -1951,6 +1965,13 @@ void MenuFactory::append_menu_item_simplify(wxMenu* menu)
[]() {return plater()->can_simplify(); }, m_parent);
}
void MenuFactory::append_menu_item_smooth_mesh(wxMenu *menu)
{
wxMenuItem *menu_item = append_menu_item(
menu, wxID_ANY, _L("Subdivision mesh") + _L("(Lost color)"), "", [](wxCommandEvent &) { obj_list()->smooth_mesh(); }, "", menu, []() { return plater()->can_smooth_mesh(); },
m_parent);
}
void MenuFactory::append_menu_item_center(wxMenu* menu)
{
append_menu_item(menu, wxID_ANY, _L("Center") , "",
+1
View File
@@ -164,6 +164,7 @@ private:
//BBS add bbl menu item
void append_menu_item_clone(wxMenu* menu);
void append_menu_item_simplify(wxMenu* menu);
void append_menu_item_smooth_mesh(wxMenu *menu);
void append_menu_item_center(wxMenu* menu);
void append_menu_item_drop(wxMenu* menu);
void append_menu_item_per_object_process(wxMenu* menu);
+87
View File
@@ -44,6 +44,7 @@
#endif /* __WXMSW__ */
#include "Gizmos/GLGizmoScale.hpp"
#include "libslic3r/TriangleMeshDeal.hpp"
namespace Slic3r
{
namespace GUI
@@ -5931,6 +5932,92 @@ void ObjectList::simplify()
gizmos_mgr.open_gizmo(GLGizmosManager::EType::Simplify);
}
void GUI::ObjectList::smooth_mesh()
{
wxBusyCursor cursor;
auto plater = wxGetApp().plater();
if (!plater) { return; }
plater->take_snapshot("smooth_mesh");
std::vector<int> obj_idxs, vol_idxs;
get_selection_indexes(obj_idxs, vol_idxs);
auto object_idx = obj_idxs.front();
ModelObject *obj{nullptr};
auto show_warning_dlg = [this](int cur_face_count,std::string name,bool is_part) {
int limit_face_count = 1000000;
if (cur_face_count > limit_face_count) {
auto name_str = wxString::FromUTF8(name);
auto content = wxString::Format(_L("\"%s\" will exceed 1 million faces after this subdivision, which may increase slicing time. Do you want to continue?"), name_str);
WarningDialog dlg(static_cast<wxWindow *>(wxGetApp().mainframe), (is_part ? _L("Part") : _L("Object")) + " " + content, wxEmptyString, wxYES_NO);
if (dlg.ShowModal() == wxID_NO) {
return true;
}
return false;
}
return false;
};
auto show_smooth_mesh_error_dlg = [this](std::string name) {
auto name_str = wxString::FromUTF8(name);
auto content = wxString::Format(_L("\"%s\" part's mesh contains errors. Please repair it first."), name_str);
WarningDialog dlg(static_cast<wxWindow *>(wxGetApp().mainframe), content, wxEmptyString, wxOK);
dlg.ShowModal();
};
bool has_show_smooth_mesh_error_dlg = false;
if (vol_idxs.empty()) {
obj = object(object_idx);
auto future_face_count = static_cast<int>(obj->facets_count()) * 4;
if (show_warning_dlg(future_face_count, obj->name,false)) {
return;
}
for (auto mv : obj->volumes) {
bool ok;
auto result_mesh = TriangleMeshDeal::smooth_triangle_mesh(mv->mesh(), ok);
if (ok) {
mv->set_mesh(result_mesh);
mv->reset_extra_facets(); // reset paint color
mv->calculate_convex_hull();
mv->invalidate_convex_hull_2d();
mv->set_new_unique_id();
} else {
if (!has_show_smooth_mesh_error_dlg) {
show_smooth_mesh_error_dlg(mv->name);
has_show_smooth_mesh_error_dlg = true;
}
}
}
obj->invalidate_bounding_box();
obj->ensure_on_bed();
plater->changed_mesh(object_idx);
} else {
obj = object(obj_idxs.front());
for (int vol_idx : vol_idxs) {
auto mv = obj->volumes[vol_idx];
auto future_face_count = static_cast<int>(mv->mesh().facets_count()) * 4;
if (show_warning_dlg(future_face_count, mv->name,true)) {
return;
}
bool ok;
auto result_mesh = TriangleMeshDeal::smooth_triangle_mesh(mv->mesh(),ok);
if (ok) {
mv->set_mesh(result_mesh);
mv->reset_extra_facets(); // reset paint color
mv->calculate_convex_hull();
mv->invalidate_convex_hull_2d();
mv->set_new_unique_id();
} else {
if (!has_show_smooth_mesh_error_dlg) {
show_smooth_mesh_error_dlg(mv->name);
has_show_smooth_mesh_error_dlg = true;
}
}
}
}
if (obj) {
obj->invalidate_bounding_box();
obj->ensure_on_bed();
plater->changed_mesh(object_idx);
}
}
void ObjectList::update_item_error_icon(const int obj_idx, const int vol_idx) const
{
auto obj = object(obj_idx);
+1
View File
@@ -428,6 +428,7 @@ public:
void rename_item();
void fix_through_netfabb();
void simplify();
void smooth_mesh();
void update_item_error_icon(const int obj_idx, int vol_idx) const ;
void copy_layers_to_clipboard();
+1 -1
View File
@@ -1070,7 +1070,7 @@ void ObjectGrid::paste_data( wxTextDataObject& text_data )
if ((src_row_cnt == 1) && (src_col_cnt == 1))
{
if ((dst_col_cnt != 1) || (dst_left_col != src_left_col)) {
wxLogWarning(_L("one cell can only be copied to one or multiple cells in the same column"));
wxLogWarning(_L("One cell can only be copied to one or more cells in the same column."));
}
else {
split(buf, string_array);
+1 -1
View File
@@ -668,7 +668,7 @@ void GLGizmoAdvancedCut::perform_cut(const Selection& selection)
if (its_num_open_edges(new_objects[i]->volumes[j]->mesh().its) > 0) {
if (!is_showed_dialog) {
is_showed_dialog = true;
MessageDialog dlg(nullptr, _L("non-manifold edges be caused by cut tool, do you want to fix it now?"), "", wxYES | wxNO);
MessageDialog dlg(nullptr, _L("Non-manifold edges be caused by cut tool, do you want to fix it now?"), "", wxYES | wxNO);
int ret = dlg.ShowModal();
if (ret == wxID_YES) {
user_fix_model = true;
+1 -1
View File
@@ -3376,7 +3376,7 @@ void GLGizmoCut3D::perform_cut(const Selection& selection)
if (its_num_open_edges(new_objects[i]->volumes[j]->mesh().its) > 0) {
if (!is_showed_dialog) {
is_showed_dialog = true;
MessageDialog dlg(nullptr, _L("non-manifold edges be caused by cut tool, do you want to fix it now?"), "", wxYES | wxCANCEL);
MessageDialog dlg(nullptr, _L("Non-manifold edges be caused by cut tool, do you want to fix it now?"), "", wxYES | wxCANCEL);
int ret = dlg.ShowModal();
if (ret == wxID_YES) {
user_fix_model = true;
+4 -1
View File
@@ -3401,8 +3401,11 @@ bool load(Facenames &facenames) {
facenames.hash = data.hash;
facenames.faces.reserve(data.good.size());
for (const wxString &face : data.good)
facenames.faces_names.reserve(data.good.size());
for (const wxString &face : data.good) {
facenames.faces.push_back({face});
facenames.faces_names.push_back(face.utf8_string());
}
facenames.bad = data.bad;
return true;
}
@@ -1286,9 +1286,6 @@ void GLGizmoMmuSegmentation::remap_filament_assignments()
size_t dst = m_extruder_remap[src];
if (dst != src) {
state_map[src+start_extruder] = static_cast<EnforcerBlockerType>(dst+start_extruder);
if (src == 0)
state_map[0] = static_cast<EnforcerBlockerType>(dst + start_extruder);
any_change = true;
}
}
@@ -1319,11 +1316,19 @@ void GLGizmoMmuSegmentation::remap_filament_assignments()
// ORCA: Remap base volume extruder as well if selected
int current_ext_id = mv->extruder_id();
int current_idx = (current_ext_id > 0) ? current_ext_id - 1 : 0;
if (current_idx >= 0 && current_idx < m_extruder_remap.size()) {
size_t dest_idx = m_extruder_remap[current_idx];
if (dest_idx != current_idx) {
mv->config.set("extruder", (int)dest_idx + 1);
// Check if volume has its own extruder config or uses object's fallback
const ConfigOption *vol_opt = mv->config.option("extruder");
if (vol_opt != nullptr && vol_opt->getInt() != 0) {
// Volume has its own extruder setting, update it
mv->config.set("extruder", (int)dest_idx + 1);
} else {
// Volume uses object's extruder setting, update the object
mo->config.set("extruder", (int)dest_idx + 1);
}
if (idx < m_volumes_extruder_idxs.size())
m_volumes_extruder_idxs[idx] = (int)dest_idx + 1;
volume_extruder_changed = true;
@@ -1335,8 +1340,11 @@ void GLGizmoMmuSegmentation::remap_filament_assignments()
if (updated) {
// ORCA: Update renderer colors if base volume extruder changed
if (volume_extruder_changed)
if (volume_extruder_changed) {
this->update_triangle_selectors_colors();
// ORCA: Update GUI_ObjectList extruder column to reflect the new extruder value
wxGetApp().obj_list()->update_objects_list_filament_column(wxGetApp().filaments_cnt());
}
// ORCA: Removed "Filament remapping finished" notification to reduce UI noise.
update_model_object();
+1 -1
View File
@@ -709,7 +709,7 @@ GLGizmoRotate3D::RotoptimzeWindow::RotoptimzeWindow(ImGuiWrapper * imgui,
wxGetApp().app_config->set("sla_auto_rotate",
"method_id",
std::to_string(state.method_id));
#endif SUPPORT_SLA_AUTO_ROTATE
#endif // SUPPORT_SLA_AUTO_ROTATE
}
if (ImGui::IsItemHovered())
@@ -1332,7 +1332,7 @@ void GizmoObjectManipulation::do_render_scale_input_window(ImGuiWrapper* imgui_w
is_avoid_one_update = true;
}
auto uniform_scale_size =imgui_wrapper->calc_text_size(_L("uniform scale")).x;
auto uniform_scale_size =imgui_wrapper->calc_text_size(_L("Uniform scale")).x;
ImGui::PushItemWidth(uniform_scale_size);
int size_sel{-1};
if (!is_avoid_one_update) {
@@ -1348,10 +1348,10 @@ void GizmoObjectManipulation::do_render_scale_input_window(ImGuiWrapper* imgui_w
//if (uniform_scale_only) {
// imgui_wrapper->disabled_begin(true);
// imgui_wrapper->bbl_checkbox(_L("uniform scale"), uniform_scale_only);
// imgui_wrapper->bbl_checkbox(_L("Uniform scale"), uniform_scale_only);
// imgui_wrapper->disabled_end();
//} else {
imgui_wrapper->bbl_checkbox(_L("uniform scale"), uniform_scale);
imgui_wrapper->bbl_checkbox(_L("Uniform scale"), uniform_scale);
//}
if (uniform_scale != this->m_uniform_scale) { this->set_uniform_scaling(uniform_scale); }
+3 -2
View File
@@ -1470,10 +1470,11 @@ void IMSlider::on_mouse_wheel(wxMouseEvent& evt) {
ImGuiWrapper& imgui = *wxGetApp().imgui();
float wheel = 0.0f;
wheel = evt.GetWheelRotation() > 0 ? 1.0f : -1.0f;
float wheel = evt.GetWheelRotation();
// mac trackpads trigger this event with value 0 when right-clicking with two fingers
if (wheel == 0.0f)
return;
wheel = wheel > 0 ? 1.0f : -1.0f;
#ifdef __WXOSX__
if (wxGetKeyState(WXK_SHIFT)) {
+5 -5
View File
@@ -299,7 +299,7 @@ CreateObjectJob::CreateObjectJob(DataCreateObject &&input): m_input(std::move(in
void CreateObjectJob::process(Ctl &ctl)
{
if (!check(m_input))
throw JobException("Bad input data for EmbossCreateObjectJob.");
throw JobException(_u8L("Bad input data for EmbossCreateObjectJob."));
// can't create new object with using surface
if (m_input.base->shape.projection.use_surface)
@@ -398,13 +398,13 @@ UpdateJob::UpdateJob(DataUpdate&& input): m_input(std::move(input)){ assert(chec
void UpdateJob::process(Ctl &ctl)
{
if (!check(m_input))
throw JobException("Bad input data for EmbossUpdateJob.");
throw JobException(_u8L("Bad input data for EmbossUpdateJob."));
auto was_canceled = ::was_canceled(ctl, *m_input.base);
m_result = ::try_create_mesh(*m_input.base, was_canceled);
if (was_canceled()) return;
if (m_result.its.empty())
throw JobException("Created text volume is empty. Change text or font.");
throw JobException(_u8L("Created text volume is empty. Change text or font."));
}
void UpdateJob::finalize(bool canceled, std::exception_ptr &eptr)
@@ -462,7 +462,7 @@ CreateSurfaceVolumeJob::CreateSurfaceVolumeJob(CreateSurfaceVolumeData &&input)
void CreateSurfaceVolumeJob::process(Ctl &ctl) {
if (!check(m_input))
throw JobException("Bad input data for CreateSurfaceVolumeJob.");
throw JobException(_u8L("Bad input data for CreateSurfaceVolumeJob."));
m_result = cut_surface(*m_input.base, m_input, was_canceled(ctl, *m_input.base));
}
@@ -484,7 +484,7 @@ UpdateSurfaceVolumeJob::UpdateSurfaceVolumeJob(UpdateSurfaceVolumeData &&input)
void UpdateSurfaceVolumeJob::process(Ctl &ctl)
{
if (!check(m_input))
throw JobException("Bad input data for UseSurfaceJob.");
throw JobException(_u8L("Bad input data for UseSurfaceJob."));
m_result = cut_surface(*m_input.base, m_input, was_canceled(ctl, *m_input.base));
}
+10 -1
View File
@@ -13,6 +13,7 @@
#include "slic3r/GUI/DeviceCore/DevUtil.h"
#include "slic3r/Utils/FileTransferUtils.hpp"
#include "slic3r/Utils/BBLNetworkPlugin.hpp"
namespace Slic3r {
namespace GUI {
@@ -228,7 +229,15 @@ void PrintJob::process(Ctl &ctl)
ftp_ok = result == 0;
}
if (!emmc_ok && !ftp_ok) {
BOOST_LOG_TRIVIAL(error) << "access code is invalid";
bool legacy_mode = BBLNetworkPlugin::instance().use_legacy_network();
BOOST_LOG_TRIVIAL(error) << "LAN connection verification failed:"
<< " emmc_ok=" << emmc_ok
<< ", ftp_ok=" << ftp_ok
<< ", ftp_result=" << result
<< ", dev_ip=" << m_dev_ip
<< ", dev_id=" << m_dev_id
<< ", password_length=" << m_access_code.size()
<< ", legacy_mode=" << (legacy_mode ? "true" : "false");
m_enter_ip_address_fun_fail();
m_job_finished = true;
return;
+4 -4
View File
@@ -211,9 +211,9 @@ void KBShortcutsDialog::fill_shortcuts()
bool swap_mouse_buttons = wxGetApp().app_config->get_bool("swap_mouse_buttons");
Shortcuts plater_shortcuts = {
{ L("Left mouse button"), swap_mouse_buttons ? L("Pan View") : L("Rotate View") },
{ L("Right mouse button"), swap_mouse_buttons ? L("Rotate View") : L("Pan View") },
{ L("Mouse wheel"), L("Zoom View") },
{ L("Left mouse button"), swap_mouse_buttons ? L("Pan view") : L("Rotate view") },
{ L("Right mouse button"), swap_mouse_buttons ? L("Rotate view") : L("Pan view") },
{ L("Mouse wheel"), L("Zoom view") },
{ "A", L("Arrange all objects") },
{ shift + "A", L("Arrange objects on selected plates") },
@@ -231,7 +231,7 @@ void KBShortcutsDialog::fill_shortcuts()
{L("Arrow Right"), L("Move selection 10 mm in positive X direction")},
{shift + L("Any arrow"), L("Movement step set to 1 mm")},
{L("Esc"), L("Deselect all")},
{"1-9", L("keyboard 1-9: set filament for object/part")},
{"1-9", L("Keyboard 1-9: set filament for object/part")},
{ctrl + "0", L("Camera view - Default")},
{ctrl + "1", L("Camera view - Top")},
{ctrl + "2", L("Camera view - Bottom")},
@@ -224,12 +224,14 @@ GCodeInputData convert(const Slic3r::GCodeProcessorResult& result, const std::ve
const libvgcode::PathVertex vertex = { convert(prev.position), curr.height, curr.width, curr.feedrate, prev.actual_feedrate,
curr.mm3_per_mm, curr.fan_speed, curr.temperature, 0.0f, convert(curr.extrusion_role), curr_type,
static_cast<uint32_t>(curr.gcode_id), static_cast<uint32_t>(curr.layer_id),
static_cast<uint8_t>(curr.extruder_id), static_cast<uint8_t>(curr.cp_color_id), { 0.0f, 0.0f } };
static_cast<uint8_t>(curr.extruder_id), static_cast<uint8_t>(curr.cp_color_id), { 0.0f, 0.0f },
/* ORCA: Add Pressure Advance visualization support */ 0.0f, curr.pressure_advance };
#else
const libvgcode::PathVertex vertex = { convert(prev.position), curr.height, curr.width, curr.feedrate, prev.actual_feedrate,
curr.mm3_per_mm, curr.fan_speed, curr.temperature, convert(curr.extrusion_role), curr_type,
static_cast<uint32_t>(curr.gcode_id), static_cast<uint32_t>(curr.layer_id),
static_cast<uint8_t>(curr.extruder_id), static_cast<uint8_t>(curr.cp_color_id), { 0.0f, 0.0f } };
static_cast<uint8_t>(curr.extruder_id), static_cast<uint8_t>(curr.cp_color_id), { 0.0f, 0.0f },
/* ORCA: Add Pressure Advance visualization support */ 0.0f, curr.pressure_advance };
#endif // VGCODE_ENABLE_COG_AND_TOOL_MARKERS
ret.vertices.emplace_back(vertex);
}
@@ -240,12 +242,14 @@ GCodeInputData convert(const Slic3r::GCodeProcessorResult& result, const std::ve
curr.mm3_per_mm, curr.fan_speed, curr.temperature,
result.filament_densities[curr.extruder_id] * curr.mm3_per_mm * (curr.position - prev.position).norm(),
convert(curr.extrusion_role), curr_type, static_cast<uint32_t>(curr.gcode_id), static_cast<uint32_t>(curr.layer_id),
static_cast<uint8_t>(curr.extruder_id), static_cast<uint8_t>(curr.cp_color_id), curr.time };
static_cast<uint8_t>(curr.extruder_id), static_cast<uint8_t>(curr.cp_color_id), curr.time,
/* ORCA: Add Pressure Advance visualization support */ 0.0f, curr.pressure_advance };
#else
const libvgcode::PathVertex vertex = { convert(curr.position), curr.height, curr.width, curr.feedrate, curr.actual_feedrate,
curr.mm3_per_mm, curr.fan_speed, curr.temperature, convert(curr.extrusion_role), curr_type,
static_cast<uint32_t>(curr.gcode_id), static_cast<uint32_t>(curr.layer_id),
static_cast<uint8_t>(curr.extruder_id), static_cast<uint8_t>(curr.cp_color_id), curr.time };
static_cast<uint8_t>(curr.extruder_id), static_cast<uint8_t>(curr.cp_color_id), curr.time,
/* ORCA: Add Pressure Advance visualization support */ 0.0f, curr.pressure_advance };
#endif // VGCODE_ENABLE_COG_AND_TOOL_MARKERS
ret.vertices.emplace_back(vertex);
}
+162 -1
View File
@@ -74,6 +74,10 @@
#include <shlobj.h>
#include <shellapi.h>
#endif // _WIN32
#ifdef __WXGTK__
#include <gtk/gtk.h>
#endif // __WXGTK__
#include <slic3r/GUI/CreatePresetsDialog.hpp>
@@ -103,6 +107,113 @@ enum class ERescaleTarget
SettingsDialog
};
#ifdef __WXGTK__
// A thin transparent panel placed at a window edge to handle resize.
// Works regardless of underlying content (GLCanvas3D, wxWebView, etc.)
// because these panels are Raise()'d above all siblings, so their GDK
// windows receive pointer events even over WebKit2GTK or GL surfaces.
class ResizeEdgePanel : public wxPanel
{
public:
enum Edge { Bottom, Left, Right };
static constexpr int BORDER_PX = 5;
ResizeEdgePanel(MainFrame* frame, Edge edge)
: wxPanel(frame, wxID_ANY, wxDefaultPosition, wxDefaultSize,
wxBORDER_NONE | wxTRANSPARENT_WINDOW)
, m_frame(frame)
, m_edge(edge)
{
SetBackgroundStyle(wxBG_STYLE_TRANSPARENT);
Bind(wxEVT_MOTION, &ResizeEdgePanel::OnCursorUpdate, this);
Bind(wxEVT_ENTER_WINDOW, &ResizeEdgePanel::OnCursorUpdate, this);
Bind(wxEVT_LEFT_DOWN, &ResizeEdgePanel::OnLeftDown, this);
Bind(wxEVT_LEAVE_WINDOW, &ResizeEdgePanel::OnLeave, this);
Bind(wxEVT_PAINT, &ResizeEdgePanel::OnPaint, this);
}
private:
void OnPaint(wxPaintEvent&)
{
wxPaintDC dc(this);
// Transparent — draw nothing
}
GdkWindowEdge get_gdk_edge(const wxPoint& pos) const
{
wxSize size = GetSize();
switch (m_edge) {
case Bottom:
if (pos.x < BORDER_PX) return GDK_WINDOW_EDGE_SOUTH_WEST;
if (pos.x > size.x - BORDER_PX) return GDK_WINDOW_EDGE_SOUTH_EAST;
return GDK_WINDOW_EDGE_SOUTH;
case Left:
if (pos.y < BORDER_PX) return GDK_WINDOW_EDGE_NORTH_WEST;
if (pos.y > size.y - BORDER_PX) return GDK_WINDOW_EDGE_SOUTH_WEST;
return GDK_WINDOW_EDGE_WEST;
case Right:
if (pos.y < BORDER_PX) return GDK_WINDOW_EDGE_NORTH_EAST;
if (pos.y > size.y - BORDER_PX) return GDK_WINDOW_EDGE_SOUTH_EAST;
return GDK_WINDOW_EDGE_EAST;
}
return GDK_WINDOW_EDGE_SOUTH;
}
void OnCursorUpdate(wxMouseEvent& evt)
{
GdkWindowEdge edge = get_gdk_edge(evt.GetPosition());
const char* name;
switch (edge) {
case GDK_WINDOW_EDGE_NORTH: name = "n-resize"; break;
case GDK_WINDOW_EDGE_SOUTH: name = "s-resize"; break;
case GDK_WINDOW_EDGE_WEST: name = "w-resize"; break;
case GDK_WINDOW_EDGE_EAST: name = "e-resize"; break;
case GDK_WINDOW_EDGE_NORTH_WEST: name = "nw-resize"; break;
case GDK_WINDOW_EDGE_NORTH_EAST: name = "ne-resize"; break;
case GDK_WINDOW_EDGE_SOUTH_WEST: name = "sw-resize"; break;
case GDK_WINDOW_EDGE_SOUTH_EAST: name = "se-resize"; break;
default: name = "s-resize"; break;
}
if (name == m_last_cursor_name) return;
m_last_cursor_name = name;
GdkDisplay* display = gtk_widget_get_display(m_widget);
GdkCursor* cursor = gdk_cursor_new_from_name(display, name);
if (cursor) {
gdk_window_set_cursor(gtk_widget_get_window(m_widget), cursor);
g_object_unref(cursor);
}
}
void OnLeave(wxMouseEvent&)
{
m_last_cursor_name = nullptr;
gdk_window_set_cursor(gtk_widget_get_window(m_widget), nullptr);
}
void OnLeftDown(wxMouseEvent& evt)
{
if (m_frame->IsMaximized() || m_frame->IsFullScreen())
return;
GdkWindowEdge edge = get_gdk_edge(evt.GetPosition());
wxPoint mouse = ClientToScreen(evt.GetPosition());
gtk_window_begin_resize_drag(
GTK_WINDOW(m_frame->m_widget),
edge,
1, // left button
mouse.x, mouse.y,
gtk_get_current_event_time());
}
MainFrame* m_frame;
Edge m_edge;
const char* m_last_cursor_name{nullptr};
};
#endif // __WXGTK__
#ifdef __APPLE__
class OrcaSlicerTaskBarIcon : public wxTaskBarIcon
{
@@ -194,6 +305,14 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, BORDERLESS_FRAME_
set_miniaturizable(GetHandle());
#endif
#ifdef __WXGTK__
m_gdkDecor = 0;
m_edge_bottom = new ResizeEdgePanel(this, ResizeEdgePanel::Bottom);
m_edge_left = new ResizeEdgePanel(this, ResizeEdgePanel::Left);
m_edge_right = new ResizeEdgePanel(this, ResizeEdgePanel::Right);
#endif
if (!wxGetApp().app_config->has("user_mode")) {
wxGetApp().app_config->set("user_mode", "simple");
wxGetApp().app_config->set_bool("developer_mode", false);
@@ -355,6 +474,9 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, BORDERLESS_FRAME_
#endif
Refresh();
Layout();
#ifdef __WXGTK__
update_edge_panels();
#endif
wxQueueEvent(wxGetApp().plater(), new SimpleEvent(EVT_NOTICE_CHILDE_SIZE_CHANGED));
});
@@ -936,10 +1058,39 @@ void MainFrame::update_layout()
Thaw();
}
#ifdef __WXGTK__
void MainFrame::update_edge_panels()
{
if (!m_edge_bottom) return;
bool hide = IsMaximized() || IsFullScreen();
m_edge_bottom->Show(!hide);
m_edge_left->Show(!hide);
m_edge_right->Show(!hide);
if (hide) return;
constexpr int B = ResizeEdgePanel::BORDER_PX;
wxSize cs = GetClientSize();
m_edge_bottom->SetSize(0, cs.y - B, cs.x, B);
m_edge_left->SetSize(0, 0, B, cs.y);
m_edge_right->SetSize(cs.x - B, 0, B, cs.y);
m_edge_bottom->Raise();
m_edge_left->Raise();
m_edge_right->Raise();
}
#endif
// Called when closing the application and when switching the application language.
void MainFrame::shutdown()
{
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "MainFrame::shutdown enter";
#ifdef __WXGTK__
// Edge panels are child windows — wxWidgets destroys them automatically.
m_edge_bottom = nullptr;
m_edge_left = nullptr;
m_edge_right = nullptr;
#endif
// BBS: backup
Slic3r::set_backup_callback(nullptr);
#ifdef _WIN32
@@ -1699,7 +1850,9 @@ wxBoxSizer* MainFrame::create_side_tools()
auto curr_plate = m_plater->get_partplate_list().get_curr_plate();
#ifdef __linux__
slice = try_pop_up_before_slice(m_slice_select == eSliceAll, m_plater, curr_plate, true);
PresetBundle* preset = wxGetApp().preset_bundle;
bool force_show_fila_group_dlg = (preset && preset->is_bbl_vendor() && preset->get_printer_extruder_count() == 2);
slice = try_pop_up_before_slice(m_slice_select == eSliceAll, m_plater, curr_plate, force_show_fila_group_dlg);
#else
slice = try_pop_up_before_slice(m_slice_select == eSliceAll, m_plater, curr_plate, false);
#endif
@@ -2828,6 +2981,14 @@ void MainFrame::init_menubar_as_editor()
this, [this]() { return m_tabpanel->GetSelection() == TabPosition::tp3DEditor || m_tabpanel->GetSelection() == TabPosition::tpPreview; },
[this]() { return wxGetApp().show_3d_navigator(); }, this);
append_menu_check_item(viewMenu, wxID_ANY, _L("Show Gridlines"), _L("Show Gridlines on plate"),
[this](wxCommandEvent&) {
wxGetApp().toggle_show_plate_gridlines();
m_plater->get_current_canvas3D()->post_event(SimpleEvent(wxEVT_PAINT));
}, this,
[this]() { return m_tabpanel->GetSelection() == TabPosition::tp3DEditor || m_tabpanel->GetSelection() == TabPosition::tpPreview; },
[this]() { return wxGetApp().show_plate_gridlines(); }, this);
append_menu_item(
viewMenu, wxID_ANY, _L("Reset Window Layout"), _L("Reset to default window layout"),
[this](wxCommandEvent&) { m_plater->reset_window_layout(); }, "", this,
+11
View File
@@ -52,6 +52,9 @@ class PrintHostQueueDialog;
class Plater;
class MainFrame;
class ParamsDialog;
#ifdef __WXGTK__
class ResizeEdgePanel;
#endif
enum QuickSlice
{
@@ -422,6 +425,14 @@ public:
uint32_t m_ulSHChangeNotifyRegister { 0 };
static constexpr int WM_USER_MEDIACHANGED { 0x7FFF }; // WM_USER from 0x0400 to 0x7FFF, picking the last one to not interfere with wxWidgets allocation
#endif // _WIN32
#ifdef __WXGTK__
friend class ResizeEdgePanel;
ResizeEdgePanel* m_edge_bottom{nullptr};
ResizeEdgePanel* m_edge_left{nullptr};
ResizeEdgePanel* m_edge_right{nullptr};
void update_edge_panels();
#endif // __WXGTK__
};
wxDECLARE_EVENT(EVT_HTTP_ERROR, wxCommandEvent);
+4 -4
View File
@@ -68,7 +68,7 @@ AddMachinePanel::AddMachinePanel(wxWindow* parent, wxWindowID id, const wxPoint&
m_button_add_machine->SetBorderColor(0x909090);
m_button_add_machine->SetMinSize(wxSize(96, 39));
btn_sizer->Add(m_button_add_machine, 0, wxALL | wxALIGN_CENTER_HORIZONTAL, 5);
m_staticText_add_machine = new wxStaticText(this, wxID_ANY, wxT("click to add machine"), wxDefaultPosition, wxDefaultSize, 0);
m_staticText_add_machine = new wxStaticText(this, wxID_ANY, _L("click to add machine"), wxDefaultPosition, wxDefaultSize, 0);
m_staticText_add_machine->Wrap(-1);
m_staticText_add_machine->SetForegroundColour(0x909090);
btn_sizer->Add(m_staticText_add_machine, 0, wxALL | wxALIGN_CENTER_HORIZONTAL, 5);
@@ -200,7 +200,7 @@ void MonitorPanel::init_tabpanel()
std::string network_ver = Slic3r::NetworkAgent::get_version();
if (!network_ver.empty()) {
m_tabpanel->SetFooterText(wxString::Format("Network plugin v%s", network_ver));
m_tabpanel->SetFooterText(wxString::Format(_L("Network plug-in v%s"), network_ver));
}
m_initialized = true;
@@ -543,9 +543,9 @@ void MonitorPanel::update_network_version_footer()
wxString footer_text;
if (!suffix.empty() && configured_base == binary_version) {
footer_text = wxString::Format("Network plugin v%s (%s)", binary_version, suffix);
footer_text = wxString::Format(_L("Network plug-in v%s (%s)"), binary_version, suffix);
} else {
footer_text = wxString::Format("Network plugin v%s", binary_version);
footer_text = wxString::Format(_L("Network plug-in v%s"), binary_version);
}
m_tabpanel->SetFooterText(footer_text);
+2 -2
View File
@@ -147,7 +147,7 @@ wxString DeviceItem::get_state_printable()
str_state_printable.push_back(_L("Printing"));
str_state_printable.push_back(_L("Upgrading"));
str_state_printable.push_back(_L("Incompatible"));
str_state_printable.push_back(_L("syncing"));
str_state_printable.push_back(_L("Syncing"));
return str_state_printable[state_printable];
}
@@ -163,7 +163,7 @@ wxString DeviceItem::get_state_device()
str_state_device.push_back(_L("Printing Pause"));
str_state_device.push_back(_L("Prepare"));
str_state_device.push_back(_L("Slicing"));
str_state_device.push_back(_L("syncing"));
str_state_device.push_back(_L("Syncing"));
return str_state_device[state_device];
}
+6 -6
View File
@@ -20,7 +20,7 @@ NetworkPluginDownloadDialog::NetworkPluginDownloadDialog(wxWindow* parent, Mode
const std::string& error_message,
const std::string& error_details)
: DPIDialog(parent, wxID_ANY, mode == Mode::UpdateAvailable ?
_L("Network Plugin Update Available") : _L("Bambu Network Plugin Required"),
_L("Network Plug-in Update Available") : _L("Bambu Network Plug-in Required"),
wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE)
, m_mode(mode)
, m_error_message(error_message)
@@ -54,8 +54,8 @@ void NetworkPluginDownloadDialog::create_missing_plugin_ui()
auto* desc = new wxStaticText(this, wxID_ANY,
m_mode == Mode::CorruptedPlugin ?
_L("The Bambu Network Plugin is corrupted or incompatible. Please reinstall it.") :
_L("The Bambu Network Plugin is required for cloud features, printer discovery, and remote printing."));
_L("The Bambu Network Plug-in is corrupted or incompatible. Please reinstall it.") :
_L("The Bambu Network Plug-in is required for cloud features, printer discovery, and remote printing."));
desc->SetFont(::Label::Body_13);
desc->Wrap(FromDIP(400));
main_sizer->Add(desc, 0, wxLEFT | wxRIGHT, FromDIP(25));
@@ -134,7 +134,7 @@ void NetworkPluginDownloadDialog::create_update_available_ui(const std::string&
wxBoxSizer* main_sizer = static_cast<wxBoxSizer*>(GetSizer());
auto* desc = new wxStaticText(this, wxID_ANY,
_L("A new version of the Bambu Network Plugin is available."));
_L("A new version of the Bambu Network Plug-in is available."));
desc->SetFont(::Label::Body_13);
desc->Wrap(FromDIP(400));
main_sizer->Add(desc, 0, wxLEFT | wxRIGHT, FromDIP(25));
@@ -305,14 +305,14 @@ NetworkPluginRestartDialog::NetworkPluginRestartDialog(wxWindow* parent)
auto* text_sizer = new wxBoxSizer(wxVERTICAL);
auto* desc = new wxStaticText(this, wxID_ANY,
_L("The Bambu Network Plugin has been installed successfully."));
_L("The Bambu Network Plug-in has been installed successfully."));
desc->SetFont(::Label::Body_14);
desc->Wrap(FromDIP(350));
text_sizer->Add(desc, 0, wxTOP, FromDIP(10));
text_sizer->Add(0, 0, 0, wxTOP, FromDIP(10));
auto* restart_msg = new wxStaticText(this, wxID_ANY,
_L("A restart is required to load the new plugin. Would you like to restart now?"));
_L("A restart is required to load the new plug-in. Would you like to restart now?"));
restart_msg->SetFont(::Label::Body_13);
restart_msg->Wrap(FromDIP(350));
text_sizer->Add(restart_msg, 0, wxBOTTOM, FromDIP(10));
+7
View File
@@ -322,6 +322,13 @@ void NotificationManager::PopNotification::render(GLCanvas3D& canvas, float init
render_minimize_button(imgui, win_pos.x, win_pos.y);
render_close_button(imgui, win_size.x, win_size.y, win_pos.x, win_pos.y); // ORCA draw it after minimize button since its position related to minimize button
}
const bool gcode_window_visible = canvas.get_canvas_type() == GLCanvas3D::ECanvasType::CanvasPreview && wxGetApp().show_gcode_window();
if (!gcode_window_visible)
{
ImGui::BringWindowToDisplayFront(ImGui::GetCurrentWindow());
}
imgui.end();
restore_default_theme();
+2 -9
View File
@@ -48,14 +48,7 @@ wxBoxSizer* ObjColorDialog::create_btn_sizer(long flags,bool exist_error)
if (!exist_error) {
btn_sizer->AddSpacer(FromDIP(25));
auto *tips = new HyperLink(this, _L("Wiki Guide")); // ORCA
tips->Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent &e) {
bool is_zh = wxGetApp().app_config->get("language") == "zh_CN";
if (is_zh) {
wxLaunchDefaultBrowser("https://wiki.bambulab.com/zh/software/bambu-studio/import_obj");
} else {
wxLaunchDefaultBrowser("https://wiki.bambulab.com/en/software/bambu-studio/import_obj");
}
});
tips->SetURL("https://www.orcaslicer.com/wiki/general-settings/import_export.html#obj");
btn_sizer->Add(tips, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
}
btn_sizer->AddStretchSpacer();
@@ -924,7 +917,7 @@ wxBoxSizer *ObjColorPanel::create_color_icon_map_rgba_sizer(wxWindow *parent, in
icon_sizer->Add(icon, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, 0); // wxALIGN_CENTER_VERTICAL | wxTOP | wxBOTTOM
icon_sizer->AddSpacer(FromDIP(10));
wxStaticText *map_text = new wxStaticText(parent, wxID_ANY, u8"—> ");
wxStaticText *map_text = new wxStaticText(parent, wxID_ANY, _L(u8"—> "));
map_text->SetFont(Label::Head_12);
icon_sizer->Add(map_text, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, 0);
+12
View File
@@ -25,6 +25,18 @@
#include "../Utils/MacDarkMode.hpp"
#endif // __APPLE__
// Verify GLEW and wxWidgets use the same OpenGL backend (EGL vs GLX).
// A mismatch causes rendering failures: GLEW's function loading must match
// the context type created by wxWidgets.
#if defined(__linux__)
#if defined(GLEW_EGL) && (!defined(wxUSE_GLCANVAS_EGL) || !wxUSE_GLCANVAS_EGL)
#error "OpenGL backend mismatch: GLEW has EGL support enabled but wxWidgets does not. Ensure GLEW_USE_EGL and wxUSE_GLCANVAS_EGL are both ON or both OFF."
#endif
#if !defined(GLEW_EGL) && defined(wxUSE_GLCANVAS_EGL) && wxUSE_GLCANVAS_EGL
#error "OpenGL backend mismatch: wxWidgets has EGL support enabled but GLEW does not. Ensure GLEW_USE_EGL and wxUSE_GLCANVAS_EGL are both ON or both OFF."
#endif
#endif
namespace Slic3r {
namespace GUI {
+3 -2
View File
@@ -594,11 +594,12 @@ void OptionsGroup::clear(bool destroy_custom_ctrl)
Line OptionsGroup::create_single_option_line(const Option& option, const std::string& path/* = std::string()*/) const
{
wxString tooltip = _(option.opt.tooltip);
edit_tooltip(tooltip);
wxString tooltip = _(get_formatted_tooltip_text(option.opt, option.opt_id));
Line retval{ _(option.opt.label), tooltip };
retval.label_path = path;
retval.append_option(option);
return retval;
}
+7 -2
View File
@@ -1485,7 +1485,12 @@ void PartPlate::register_raycasters_for_picking(GLCanvas3D &canvas)
canvas.remove_raycasters_for_picking(SceneRaycaster::EType::Bed, picking_id_component(6));
register_model_for_picking(canvas, m_plate_name_edit_icon, picking_id_component(6));
register_model_for_picking(canvas, m_move_front_icon, picking_id_component(7));
register_model_for_picking(canvas, m_plate_filament_map_icon, picking_id_component(PLATE_FILAMENT_MAP_ID));
// Only register filament map button for H2D (dual-extruder Bambu Lab) printers
PresetBundle* preset = wxGetApp().preset_bundle;
bool dual_bbl = (preset && preset->is_bbl_vendor() && preset->get_printer_extruder_count() == 2);
if (dual_bbl)
register_model_for_picking(canvas, m_plate_filament_map_icon, picking_id_component(PLATE_FILAMENT_MAP_ID));
}
int PartPlate::picking_id_component(int idx) const
@@ -3241,7 +3246,7 @@ void PartPlate::render(const Transform3d& view_matrix, const Transform3d& projec
shader->stop_using();
}
if (show_grid)
if (wxGetApp().show_plate_gridlines() && show_grid)
render_grid(bottom);
if (!bottom && m_selected && !force_background_color) {
+59 -20
View File
@@ -469,6 +469,7 @@ struct Sidebar::priv
ScalableButton * m_bpButton_ams_filament;
ScalableButton * m_bpButton_set_filament;
int m_menu_filament_id = -1;
int filament_area_height;
wxScrolledWindow* m_panel_filament_content;
wxScrolledWindow* m_scrolledWindow_filament_content;
wxStaticLine* m_staticline2;
@@ -1236,6 +1237,18 @@ bool Sidebar::priv::switch_diameter(bool single)
diameter = diameter_left;
}
}
// ORCA: Check if the selected diameter matches the current nozzle diameter in the config
Preset& printer_preset = wxGetApp().preset_bundle->printers.get_edited_preset();
auto* nozzle_diameter = dynamic_cast<const ConfigOptionFloats*>(printer_preset.config.option("nozzle_diameter"));
if (nozzle_diameter && nozzle_diameter->size() > 0) {
auto current_nozzle_dia = get_diameter_string(nozzle_diameter->values[0]);
// If the selected diameter is the same as current nozzle, don't switch profiles
if (current_nozzle_dia == diameter.ToStdString()) {
return true;
}
}
auto preset = wxGetApp().preset_bundle->get_similar_printer_preset({}, diameter.ToStdString());
if (preset == nullptr) {
// ORCA add a text. this appears when user tries to change nozzle value but config doesnt have a inherited or compatible preset
@@ -2093,10 +2106,14 @@ Sidebar::Sidebar(Plater *parent)
bSizer39->AddSpacer(FromDIP(SidebarProps::TitlebarMargin()));
// add filament content
// ORCA use a height with user preference
int filament_count_user = std::stoi(wxGetApp().app_config->get("filaments_area_preferred_count"));
p->filament_area_height = std::ceil(filament_count_user * 0.5) * (30 + SidebarProps::ElementSpacing()) - SidebarProps::ElementSpacing();
p->m_panel_filament_content = new wxScrolledWindow( p->scrolled, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
p->m_panel_filament_content->SetScrollbars(0, 100, 1, 2);
p->m_panel_filament_content->SetScrollRate(0, 5);
p->m_panel_filament_content->SetMaxSize(wxSize{-1, FromDIP(174)});
p->m_panel_filament_content->SetMaxSize(wxSize{-1, FromDIP(p->filament_area_height)}); // ORCA
p->m_panel_filament_content->SetBackgroundColour(wxColour(255, 255, 255));
//wxBoxSizer* bSizer_filament_content;
@@ -2563,22 +2580,23 @@ void Sidebar::update_presets(Preset::Type preset_type)
auto update_extruder_diameter = [&diameters, &diameter, &nozzle_diameter](int extruder_index,ExtruderGroup & extruder) {
extruder.combo_diameter->Clear();
int select = -1;
// ORCA if user defined a custom nozzle in printer config select it instead inherited one. this will show correct nozzle diameter in combobox if its exist in nozzle diameters list
// ORCA get the actual nozzle diameter from printer config
auto nozzle_dia = get_diameter_string(nozzle_diameter->values[extruder_index]);
if(nozzle_dia != diameter && std::find(diameters.begin(), diameters.end(), nozzle_dia) != diameters.end())
diameter = nozzle_dia;
// ORCA try to add nozzle diameter from config if list is empty. fixes blank nozzle combo box when preset has no alias
if(diameters[0].empty() && !nozzle_dia.empty()){
diameters[0] = nozzle_dia;
diameter = nozzle_dia;
}
// Orca: Check if the actual nozzle diameter exists in the list, if not add it as a custom option
if (std::find(diameters.begin(), diameters.end(), nozzle_dia) == diameters.end() && !nozzle_dia.empty()) {
diameters.push_back(nozzle_dia);
}
for (size_t i = 0; i < diameters.size(); ++i) {
if (diameters[i] == diameter)
if (diameters[i] == nozzle_dia)
select = extruder.combo_diameter->GetCount();
extruder.combo_diameter->Append(diameters[i], {});
}
extruder.combo_diameter->SetSelection(select);
extruder.diameter = diameter;
extruder.diameter = nozzle_dia;
};
auto image_path = get_cur_select_bed_image();
if (is_dual_extruder) {
@@ -3506,7 +3524,7 @@ void Sidebar::sync_ams_list(bool is_from_big_sync_btn)
for (auto& c : p->combos_filament)
c->update();
// Expand filament list
p->m_panel_filament_content->SetMaxSize({-1, FromDIP(174)});
p->m_panel_filament_content->SetMaxSize({-1, FromDIP(p->filament_area_height)}); // ORCA
auto min_size = p->m_panel_filament_content->GetSizer()->GetMinSize();
if (min_size.y > p->m_panel_filament_content->GetMaxHeight())
min_size.y = p->m_panel_filament_content->GetMaxHeight();
@@ -4642,6 +4660,7 @@ struct Plater::priv
bool can_layers_editing() const;
bool can_fix_through_netfabb() const;
bool can_simplify() const;
bool can_smooth_mesh() const;
bool can_set_instance_to_object() const;
bool can_mirror() const;
bool can_reload_from_disk() const;
@@ -10081,7 +10100,7 @@ void Plater::priv::update_plugin_when_launch(wxCommandEvent &event)
void Plater::priv::show_install_plugin_hint(wxCommandEvent &event)
{
notification_manager->bbl_show_plugin_install_notification(into_u8(_L("Network Plug-in is not detected. Network related features are unavailable.")));
notification_manager->bbl_show_plugin_install_notification(into_u8(_L("The network plug-in was not detected. Network related features are unavailable.")));
}
void Plater::priv::show_preview_only_hint(wxCommandEvent &event)
@@ -10400,13 +10419,13 @@ void Plater::priv::set_project_name(const wxString& project_name)
m_project_name = project_name;
wxString name = project_name + " - OrcaSlicer-ZAA";
//update topbar title
#ifdef __WINDOWS__
wxGetApp().mainframe->SetTitle(name);
wxGetApp().mainframe->topbar()->SetTitle(name);
#else
wxGetApp().mainframe->SetTitle(name);
#ifdef __APPLE__
wxGetApp().mainframe->SetTitle(m_project_name);
if (!m_project_name.IsEmpty())
wxGetApp().mainframe->update_title_colour_after_set_title();
#else
wxGetApp().mainframe->SetTitle(m_project_name + " - OrcaSlicer");
wxGetApp().mainframe->topbar()->SetTitle(m_project_name);
#endif
}
@@ -10421,11 +10440,12 @@ void Plater::priv::update_title_dirty_status()
else
title = m_project_name;
#ifdef __WINDOWS__
wxGetApp().mainframe->topbar()->SetTitle(title);
#ifdef __APPLE__
wxGetApp().mainframe->SetTitle(title);
wxGetApp().mainframe->update_title_colour_after_set_title();
#else
wxGetApp().mainframe->SetTitle(title);
wxGetApp().mainframe->update_title_colour_after_set_title();
wxGetApp().mainframe->topbar()->SetTitle(title);
#endif
}
@@ -10664,7 +10684,7 @@ bool Plater::priv::check_ams_status_impl(bool is_slice_all)
: MessageDialog(parent,
_L("The nozzle type and AMS quantity information has not been synced from the connected printer.\n"
"After syncing, software can optimize printing time and filament usage when slicing.\n"
"Would you like to sync now ?"),
"Would you like to sync now?"),
_L("Warning"), 0)
{
add_button(wxID_YES, true, _L("Sync now"));
@@ -11038,6 +11058,24 @@ bool Plater::priv::can_simplify() const
return true;
}
bool Plater::priv::can_smooth_mesh() const
{
std::vector<int> obj_idxs, vol_idxs;
sidebar->obj_list()->get_selection_indexes(obj_idxs, vol_idxs);
if (vol_idxs.empty()) {
for (auto obj_idx : obj_idxs)
if (model.objects[obj_idx]->get_object_stl_stats().open_edges > 0)
return false;
return true;
}
int obj_idx = obj_idxs.front();
for (auto vol_idx : vol_idxs)
if (model.objects[obj_idx]->get_object_stl_stats().open_edges > 0)
return false;
return true;
}
bool Plater::priv::can_increase_instances() const
{
if (!m_worker.is_idle()
@@ -11914,7 +11952,7 @@ void Plater::import_model_id(wxString download_info)
int res = 0;
std::string http_body;
msg = _L("prepare 3MF file...");
msg = _L("Preparing 3MF file...");
//gets the number of files with the same name
std::vector<wxString> vecFiles;
@@ -11963,7 +12001,7 @@ void Plater::import_model_id(wxString download_info)
}
msg = _L("downloading project...");
msg = _L("Downloading project...");
//target_path = wxStandardPaths::Get().GetTempDir().utf8_str().data();
@@ -17930,6 +17968,7 @@ bool Plater::can_decrease_instances() const { return p->can_decrease_instances()
bool Plater::can_set_instance_to_object() const { return p->can_set_instance_to_object(); }
bool Plater::can_fix_through_netfabb() const { return p->can_fix_through_netfabb(); }
bool Plater::can_simplify() const { return p->can_simplify(); }
bool Plater::can_smooth_mesh() const { return p->can_smooth_mesh(); }
bool Plater::can_split_to_objects() const { return p->can_split_to_objects(); }
bool Plater::can_split_to_volumes() const { return p->can_split_to_volumes(); }
bool Plater::can_arrange() const { return p->can_arrange(); }
+1
View File
@@ -662,6 +662,7 @@ public:
bool can_set_instance_to_object() const;
bool can_fix_through_netfabb() const;
bool can_simplify() const;
bool can_smooth_mesh() const;
bool can_split_to_objects() const;
bool can_split_to_volumes() const;
bool can_arrange() const;
+65 -18
View File
@@ -558,6 +558,50 @@ wxBoxSizer *PreferencesDialog::create_item_input(wxString title, wxString title2
return sizer_input;
}
wxBoxSizer *PreferencesDialog::create_item_spinctrl(wxString title, wxString title2, wxString side_label, wxString tooltip, std::string param, int min, int max, std::function<void(int)> onchange)
{
wxBoxSizer *sizer = new wxBoxSizer(wxHORIZONTAL);
auto label = new wxStaticText(m_parent, wxID_ANY, title, wxDefaultPosition, DESIGN_TITLE_SIZE, wxST_NO_AUTORESIZE);
label->SetForegroundColour(DESIGN_GRAY900_COLOR);
label->SetFont(::Label::Body_14);
label->SetToolTip(tooltip);
label->Wrap(DESIGN_TITLE_SIZE.x);
label->Wrap(DESIGN_TITLE_SIZE.x);
auto input = new SpinInput(m_parent, wxEmptyString, side_label, wxDefaultPosition, DESIGN_INPUT_SIZE, wxSP_ARROW_KEYS, min, max, stoi(app_config->get(param)));
input->SetToolTip(tooltip);
sizer->AddSpacer(FromDIP(DESIGN_LEFT_MARGIN));
sizer->Add(label, 0, wxALIGN_CENTER_VERTICAL);
sizer->Add(input, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(5));
if(!title2.empty()){
auto second_title = new wxStaticText(m_parent, wxID_ANY, title2, wxDefaultPosition, wxDefaultSize, 0);
second_title->SetForegroundColour(DESIGN_GRAY900_COLOR);
second_title->SetFont(::Label::Body_14);
second_title->SetToolTip(tooltip);
sizer->Add(second_title, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(5));
}
input->Bind(wxEVT_TEXT_ENTER, [this, param, input, onchange](wxCommandEvent& e) {
auto value = input->GetValue();
app_config->set(param, std::to_string(value));
app_config->save();
if (onchange != nullptr) onchange(value);
e.Skip();
});
input->Bind(wxEVT_KILL_FOCUS, [this, param, input, onchange](wxFocusEvent &e) {
auto value = input->GetValue();
app_config->set(param, std::to_string(value));
if (onchange != nullptr) onchange(value);
e.Skip();
});
return sizer;
}
wxBoxSizer *PreferencesDialog::create_camera_orbit_mult_input(wxString title, wxString tooltip)
{
wxBoxSizer *sizer_input = new wxBoxSizer(wxHORIZONTAL);
@@ -1354,6 +1398,9 @@ void PreferencesDialog::create_items()
"group_filament_presets", {_L("All"), _L("None"), _L("By type"), _L("By vendor")}, [](wxString value) {wxGetApp().plater()->sidebar().update_presets(Preset::TYPE_FILAMENT);});
g_sizer->Add(item_filament_preset_grouping);
auto item_filament_area_height = create_item_spinctrl(_L("Optimize filaments area height for..."), _L("(Requires restart)"), _L("filaments"), _L("Optimizes filament area maximum height by chosen filament count."), "filaments_area_preferred_count", 8, 99);
g_sizer->Add(item_filament_area_height);
//// GENERAL > Features
g_sizer->Add(create_item_title(_L("Features")), 1, wxEXPAND);
@@ -1503,18 +1550,18 @@ void PreferencesDialog::create_items()
g_sizer->Add(item_filament_sync_mode);
//// ONLINE > Network plugin
g_sizer->Add(create_item_title(_L("Network plugin")), 1, wxEXPAND);
g_sizer->Add(create_item_title(_L("Network plug-in")), 1, wxEXPAND);
auto item_enable_plugin = create_item_checkbox(_L("Enable network plugin"), "", "installed_networking");
auto item_enable_plugin = create_item_checkbox(_L("Enable network plug-in"), "", "installed_networking");
g_sizer->Add(item_enable_plugin);
m_network_version_sizer = new wxBoxSizer(wxHORIZONTAL);
m_network_version_sizer->AddSpacer(FromDIP(DESIGN_LEFT_MARGIN));
auto version_title = new wxStaticText(m_parent, wxID_ANY, _L("Network plugin version"), wxDefaultPosition, DESIGN_TITLE_SIZE, wxST_NO_AUTORESIZE);
auto version_title = new wxStaticText(m_parent, wxID_ANY, _L("Network plug-in version"), wxDefaultPosition, DESIGN_TITLE_SIZE, wxST_NO_AUTORESIZE);
version_title->SetForegroundColour(DESIGN_GRAY900_COLOR);
version_title->SetFont(::Label::Body_14);
version_title->SetToolTip(_L("Select the network plugin version to use"));
version_title->SetToolTip(_L("Select the network plug-in version to use"));
version_title->Wrap(DESIGN_TITLE_SIZE.x);
m_network_version_sizer->Add(version_title, 0, wxALIGN_CENTER);
@@ -1591,20 +1638,20 @@ void PreferencesDialog::create_items()
if (Slic3r::NetworkAgent::versioned_library_exists(new_version)) {
BOOST_LOG_TRIVIAL(info) << "Version " << new_version << " already exists on disk, triggering hot reload";
if (wxGetApp().hot_reload_network_plugin()) {
MessageDialog dlg(this, _L("Network plugin switched successfully."), _L("Success"), wxOK | wxICON_INFORMATION);
MessageDialog dlg(this, _L("Network plug-in switched successfully."), _L("Success"), wxOK | wxICON_INFORMATION);
dlg.ShowModal();
} else {
MessageDialog dlg(this, _L("Failed to load network plugin. Please restart the application."), _L("Restart Required"), wxOK | wxICON_WARNING);
MessageDialog dlg(this, _L("Failed to load network plug-in. Please restart the application."), _L("Restart Required"), wxOK | wxICON_WARNING);
dlg.ShowModal();
}
} else {
wxString msg = wxString::Format(
_L("You've selected network plugin version %s.\n\nWould you like to download and install this version now?\n\nNote: The application may need to restart after installation."),
_L("You've selected network plug-in version %s.\n\nWould you like to download and install this version now?\n\nNote: The application may need to restart after installation."),
wxString::FromUTF8(new_version));
MessageDialog dlg(this, msg, _L("Download Network Plugin"), wxYES_NO | wxICON_QUESTION);
MessageDialog dlg(this, msg, _L("Download Network Plug-in"), wxYES_NO | wxICON_QUESTION);
if (dlg.ShowModal() == wxID_YES) {
DownloadProgressDialog progress_dlg(_L("Downloading Network Plugin"));
DownloadProgressDialog progress_dlg(_L("Downloading Network Plug-in"));
progress_dlg.ShowModal();
}
}
@@ -1684,13 +1731,13 @@ void PreferencesDialog::create_items()
auto loglevel_combox = create_item_loglevel_combobox(_L("Log Level"), _L("Log Level"), log_level_list);
g_sizer->Add(loglevel_combox);
g_sizer->Add(create_item_title(_L("Network Plugin")), 1, wxEXPAND);
auto item_reload_plugin = create_item_button(_L("Network plugin"), _L("Reload"), _L("Reload the network plugin without restarting the application"), "", [this]() {
g_sizer->Add(create_item_title(_L("Network plug-in")), 1, wxEXPAND);
auto item_reload_plugin = create_item_button(_L("Network plug-in"), _L("Reload"), _L("Reload the network plug-in without restarting the application"), "", [this]() {
if (wxGetApp().hot_reload_network_plugin()) {
MessageDialog dlg(this, _L("Network plugin reloaded successfully."), _L("Reload"), wxOK | wxICON_INFORMATION);
MessageDialog dlg(this, _L("Network plug-in reloaded successfully."), _L("Reload"), wxOK | wxICON_INFORMATION);
dlg.ShowModal();
} else {
MessageDialog dlg(this, _L("Failed to reload network plugin. Please restart the application."), _L("Reload Failed"), wxOK | wxICON_ERROR);
MessageDialog dlg(this, _L("Failed to reload network plug-in. Please restart the application."), _L("Reload Failed"), wxOK | wxICON_ERROR);
dlg.ShowModal();
}
});
@@ -1755,10 +1802,10 @@ void PreferencesDialog::create_shortcuts_page()
std::vector<wxString> mouse_supported;
Split(app_config->get("mouse_supported"), "/", mouse_supported);
auto item_rotate_view = create_item_multiple_combobox(_L("Rotate of view"), _L("Rotate of view"), "rotate_view", keyboard_supported,
auto item_rotate_view = create_item_multiple_combobox(_L("Rotate view"), _L("Rotate view"), "rotate_view", keyboard_supported,
mouse_supported);
auto item_move_view = create_item_multiple_combobox(_L("Move of view"), _L("Move of view"), "move_view", keyboard_supported, mouse_supported);
auto item_zoom_view = create_item_multiple_combobox(_L("Zoom of view"), _L("Zoom of view"), "rotate_view", keyboard_supported, mouse_supported);
auto item_move_view = create_item_multiple_combobox(_L("Pan view"), _L("Pan view"), "move_view", keyboard_supported, mouse_supported);
auto item_zoom_view = create_item_multiple_combobox(_L("Zoom view"), _L("Zoom view"), "rotate_view", keyboard_supported, mouse_supported);
auto title_other = create_item_title(_L("Other"));
auto item_other = create_item_checkbox(_L("Mouse wheel reverses when zooming"), _L("Mouse wheel reverses when zooming"), "mouse_wheel");
@@ -1812,12 +1859,12 @@ wxBoxSizer* PreferencesDialog::create_debug_page()
radio_group->SetSelection(3);
}
Button* debug_button = new Button(m_parent, _L("debug save button"));
Button* debug_button = new Button(m_parent, _L("Debug save button"));
debug_button->SetStyle(ButtonStyle::Confirm, ButtonType::Window);
debug_button->Bind(wxEVT_LEFT_DOWN, [this, radio_group](wxMouseEvent &e) {
// success message box
MessageDialog dialog(this, _L("save debug settings"), _L("DEBUG settings have been saved successfully!"), wxNO_DEFAULT | wxYES_NO | wxICON_INFORMATION);
MessageDialog dialog(this, _L("Save debug settings"), _L("DEBUG settings have been saved successfully!"), wxNO_DEFAULT | wxYES_NO | wxICON_INFORMATION);
dialog.SetSize(400,-1);
switch (dialog.ShowModal()) {
case wxID_NO: {
+1 -1
View File
@@ -185,7 +185,7 @@ std::string PresetHints::maximum_volumetric_flow_description(const PresetBundle
//FIXME handle gap_infill_speed
if (! out.empty())
out += "\n";
out += (first_layer ? _utf8(L("Initial layer volumetric")) : (bridging ? _utf8(L("Bridge volumetric")) : _utf8(L("Volumetric"))));
out += (first_layer ? _utf8(L("First layer volumetric")) : (bridging ? _utf8(L("Bridge volumetric")) : _utf8(L("Volumetric"))));
out += " " + _utf8(L("flow rate is maximized")) + " ";
bool limited_by_max_volumetric_speed = max_volumetric_speed > 0 && max_volumetric_speed < max_flow;
out += (limited_by_max_volumetric_speed ?
+2 -2
View File
@@ -181,13 +181,13 @@ void PublishDialog::SetPublishStep(PublishStep step, bool yield, int percent)
else
m_progress->SetValue(0);
} else if (step == PublishStep::STEP_PACKING) {
m_text_progress->SetLabelText(_L("Packing data to 3mf"));
m_text_progress->SetLabelText(_L("Packing data to 3MF"));
if (percent > 0)
m_progress->SetValue(percent);
else
m_progress->SetValue(70);
} else if (step == PublishStep::STEP_UPLOADING) {
m_text_progress->SetLabelText(_L("Packing data to 3mf"));
m_text_progress->SetLabelText(_L("Uploading data"));
if (percent > 0)
m_progress->SetValue(percent);
else
+46 -40
View File
@@ -27,6 +27,7 @@
#include "DeviceCore/DevManager.h"
#include "DeviceCore/DevStorage.h"
#include "md4c/src/md4c-html.h"
namespace Slic3r { namespace GUI {
@@ -268,32 +269,33 @@ UpdateVersionDialog::UpdateVersionDialog(wxWindow *parent)
//webview
m_vebview_release_note = CreateTipView(m_simplebook_release_note);
m_vebview_release_note->SetBackgroundColour(wxColour(0xF8, 0xF8, 0xF8));
m_vebview_release_note->SetSize(wxSize(FromDIP(560), FromDIP(430)));
m_vebview_release_note->SetMinSize(wxSize(FromDIP(560), FromDIP(430)));
//m_vebview_release_note->SetMaxSize(wxSize(FromDIP(560), FromDIP(430)));
m_vebview_release_note->Bind(wxEVT_WEBVIEW_NAVIGATING,[=](wxWebViewEvent& event){
static bool load_url_first = false;
if(load_url_first){
// Orca: not used in Orca Slicer
// wxLaunchDefaultBrowser(url_line);
if (wxGetApp().app_config->get_bool("developer_mode"))
m_vebview_release_note->EnableAccessToDevTools();
m_vebview_release_note->Bind(wxEVT_WEBVIEW_NAVIGATING,[=, count = 0](wxWebViewEvent& event) mutable {
count++;
if (count == 1) {
m_vebview_release_note->SetPage(wxString::FromUTF8(html_source), "");
} else if (count >= 3) {
// Launch the default browser for links clicked by the user
wxLaunchDefaultBrowser(event.GetURL());
event.Veto();
}else{
load_url_first = true;
}
});
fs::path ph(data_dir());
ph /= "resources/tooltip/releasenote.html";
if (!fs::exists(ph)) {
ph = resources_dir();
ph /= "tooltip/releasenote.html";
}
auto url = ph.string();
std::replace(url.begin(), url.end(), '\\', '/');
url = "file:///" + url;
m_vebview_release_note->LoadURL(from_u8(url));
// fs::path ph(data_dir());
// ph /= "resources/tooltip/releasenote.html";
// if (!fs::exists(ph)) {
// ph = resources_dir();
// ph /= "tooltip/releasenote.html";
// }
// auto url = ph.string();
// std::replace(url.begin(), url.end(), '\\', '/');
// url = "file:///" + url;
// m_vebview_release_note->LoadURL(from_u8(url));
m_simplebook_release_note->AddPage(m_scrollwindows_release_note, wxEmptyString, false);
m_simplebook_release_note->AddPage(m_vebview_release_note, wxEmptyString, false);
@@ -470,27 +472,31 @@ void UpdateVersionDialog::update_version_info(wxString release_note, wxString ve
// }
// }
if (use_web_link) {
m_brand->Hide();
m_text_up_info->Hide();
m_simplebook_release_note->SetSelection(1);
m_vebview_release_note->LoadURL(from_u8(url_line));
}
else {
m_simplebook_release_note->SetMaxSize(wxSize(FromDIP(560), FromDIP(430)));
m_simplebook_release_note->SetSelection(0);
m_text_up_info->SetLabel(wxString::Format(_L("Click to download new version in default browser: %s"), version));
wxBoxSizer* sizer_text_release_note = new wxBoxSizer(wxVERTICAL);
auto m_staticText_release_note = new ::Label(m_scrollwindows_release_note, release_note, LB_AUTO_WRAP);
m_staticText_release_note->SetMinSize(wxSize(FromDIP(560), -1));
m_staticText_release_note->SetMaxSize(wxSize(FromDIP(560), -1));
sizer_text_release_note->Add(m_staticText_release_note, 0, wxALL, 5);
m_scrollwindows_release_note->SetSizer(sizer_text_release_note);
m_scrollwindows_release_note->Layout();
m_scrollwindows_release_note->Fit();
SetMinSize(GetSize());
SetMaxSize(GetSize());
}
// if (use_web_link) {
// m_brand->Hide();
// m_text_up_info->Hide();
// m_simplebook_release_note->SetSelection(1);
// m_vebview_release_note->LoadURL(from_u8(url_line));
// }
// else {
m_simplebook_release_note->SetMaxSize(wxSize(FromDIP(560), FromDIP(430)));
m_simplebook_release_note->SetSelection(1);
m_text_up_info->SetLabel(wxString::Format(_L("Click to download new version in default browser: %s"), version));
auto data_buf_in = release_note.utf8_str();
auto bg_color = StateColor::darkModeColorFor(*wxWHITE).GetAsString();
auto fg_color = StateColor::darkModeColorFor(*wxBLACK).GetAsString();
html_source = (boost::format("<html><head><style>body { color: %1%; background-color: %2%; font-family: sans-serif; } a { color: #1E90FF }</style></head><body>")
% fg_color % bg_color).str();
md_html(data_buf_in.data(), data_buf_in.length(), [](const MD_CHAR* text, MD_SIZE size, void* userdata) {
std::string* out_buf = (std::string*)userdata;
out_buf->append(text, size);
}, (void*) &html_source, MD_DIALECT_GITHUB | MD_FLAG_STRIKETHROUGH | MD_FLAG_WIKILINKS, 0);
html_source.append("</body></html>");
m_vebview_release_note->LoadURL("file://" + (boost::filesystem::path (resources_dir()) / "web/guide/0/index.html").string());
SetMinSize(GetSize());
SetMaxSize(GetSize());
// }
wxGetApp().UpdateDlgDarkUI(this);
Layout();
+1
View File
@@ -107,6 +107,7 @@ public:
Button* m_button_download;
Button* m_button_cancel;
std::string url_line;
std::string html_source;
};
class SecondaryCheckDialog : public DPIFrame
+2 -2
View File
@@ -203,14 +203,14 @@ wxBoxSizer* SafetyOptionsDialog::create_settings_group(wxWindow* parent)
line_sizer->Add(FromDIP(10), 0, 0, 0);
sizer->Add(0, 0, 0, wxTOP, FromDIP(15));
//Idel Heating Protect
//Idle Heating Protect
m_idel_heating_container = new wxPanel(parent, wxID_ANY);
m_idel_heating_container->SetBackgroundColour(*wxWHITE);
wxBoxSizer* idel_container_sizer = new wxBoxSizer(wxVERTICAL);
line_sizer = new wxBoxSizer(wxHORIZONTAL);
m_cb_idel_heating_protection = new CheckBox(m_idel_heating_container);
m_text_idel_heating_protection = new Label(m_idel_heating_container, _L("Idel Heating Protection"));
m_text_idel_heating_protection = new Label(m_idel_heating_container, _L("Idle Heating Protection"));
m_text_idel_heating_protection->SetFont(Label::Body_14);
line_sizer->AddSpacer(FromDIP(5));
line_sizer->Add(m_cb_idel_heating_protection, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(5));
+4 -4
View File
@@ -607,7 +607,7 @@ SelectMachineDialog::SelectMachineDialog(Plater *plater)
auto completedimg = new wxStaticBitmap(m_panel_finish, wxID_ANY, create_scaled_bitmap("completed", m_panel_finish, 25), wxDefaultPosition, wxSize(imgsize, imgsize), 0);
m_sizer_finish_h->Add(completedimg, 0, wxALIGN_CENTER | wxALL, FromDIP(5));
m_statictext_finish = new wxStaticText(m_panel_finish, wxID_ANY, L("send completed"), wxDefaultPosition, wxDefaultSize, 0);
m_statictext_finish = new wxStaticText(m_panel_finish, wxID_ANY, L("Send complete"), wxDefaultPosition, wxDefaultSize, 0);
m_statictext_finish->Wrap(-1);
m_statictext_finish->SetForegroundColour(wxColour(0, 150, 136));
m_sizer_finish_h->Add(m_statictext_finish, 0, wxALIGN_CENTER | wxALL, FromDIP(5));
@@ -3555,7 +3555,7 @@ void SelectMachineDialog::update_show_status(MachineObject* obj_)
}
if (obj_->GetConfig()->HasChamber()) {
const auto& msg = wxString::Format(_L("[ %s ] requires printing in a high-temperature environment.Please close the door."), filament_strs);
const auto& msg = wxString::Format(_L("[ %s ] requires printing in a high-temperature environment. Please close the door."), filament_strs);
show_status(PrintDialogStatus::PrintStatusFilamentWarningHighChamberTempCloseDoor, { msg });
if (PrePrintChecker::is_error(PrintDialogStatus::PrintStatusFilamentWarningHighChamberTempCloseDoor)) { return; }
} else {
@@ -5129,8 +5129,8 @@ void PrinterInfoBox::UpdatePlate(const std::string& plate_name)
name = _L("Textured PEI Plate");
m_bed_image->SetBitmap(create_scaled_bitmap("bed_pei", this, 40));
}
else if (plate_name == "Supertack Plate") {
name = _L("Cool Plate (Supertack)");
else if (plate_name == "SuperTack Plate") {
name = _L("Cool Plate (SuperTack)");
m_bed_image->SetBitmap(create_scaled_bitmap("bed_cool_supertack", this, 40));
}
+1 -1
View File
@@ -367,7 +367,7 @@ SendToPrinterDialog::SendToPrinterDialog(Plater *plater)
auto completedimg = new wxStaticBitmap(m_panel_finish, wxID_ANY, create_scaled_bitmap("completed", m_panel_finish, 25), wxDefaultPosition, wxSize(imgsize, imgsize), 0);
m_sizer_finish_h->Add(completedimg, 0, wxALIGN_CENTER | wxALL, FromDIP(5));
m_statictext_finish = new wxStaticText(m_panel_finish, wxID_ANY, L("send completed"), wxDefaultPosition, wxDefaultSize, 0);
m_statictext_finish = new wxStaticText(m_panel_finish, wxID_ANY, L("Send complete"), wxDefaultPosition, wxDefaultSize, 0);
m_statictext_finish->Wrap(-1);
m_statictext_finish->SetForegroundColour(wxColour(0, 150, 136));
m_sizer_finish_h->Add(m_statictext_finish, 0, wxALIGN_CENTER | wxALL, FromDIP(5));
+3 -3
View File
@@ -3986,13 +3986,13 @@ void StatusPanel::axis_ctrl_e_hint(bool up_down)
{
if (ctrl_e_hint_dlg == nullptr) {
/* ctrl_e_hint_dlg = new SecondaryCheckDialog(this->GetParent(), wxID_ANY, _L("Warning"), SecondaryCheckDialog::VisibleButtons::CONFIRM_AND_CANCEL, wxDefaultPosition,
ctrl_e_hint_dlg->update_text(_L("Please heat the nozzle to above 170°C before loading or unloading filament."));
ctrl_e_hint_dlg->update_text(_L("Please heat the nozzle to above 170\u2103 before loading or unloading filament."));
ctrl_e_hint_dlg->m_show_again_checkbox->Hide();
ctrl_e_hint_dlg->m_button_cancel->Hide();
ctrl_e_hint_dlg->m_staticText_release_note->SetMaxSize(wxSize(FromDIP(360), -1));
ctrl_e_hint_dlg->m_staticText_release_note->SetMinSize(wxSize(FromDIP(360), -1));
ctrl_e_hint_dlg->Fit();*/
ctrl_e_hint_dlg = new MessageDialog(this, _L("Please heat the nozzle to above 170°C before loading or unloading filament."), wxString(_L("Warning")), wxOK | wxCENTER);
ctrl_e_hint_dlg = new MessageDialog(this, _L("Please heat the nozzle to above 170\u2103 before loading or unloading filament."), wxString(_L("Warning")), wxOK | wxCENTER);
}
ctrl_e_hint_dlg->ShowModal();
// ctrl_e_hint_dlg->on_show();
@@ -5891,7 +5891,7 @@ wxBoxSizer *ScoreDialog::get_button_sizer()
if (ret == -1)
error_info += _L("Upload failed\n").ToUTF8().data();
else
error_info += _L("obtaining instance_id failed\n").ToUTF8().data();
error_info += _L("Obtaining instance_id failed\n").ToUTF8().data();
if (!error_info.empty()) { BOOST_LOG_TRIVIAL(info) << error_info; }
dlg_info = new MessageDialog(this,
+1 -1
View File
@@ -119,7 +119,7 @@ StepMeshDialog::StepMeshDialog(wxWindow* parent, Slic3r::Step& file, double line
info->SetForegroundColour(StateColor::darkModeColorFor(FONT_COLOR));
// ORCA standardized HyperLink
HyperLink *tips = new HyperLink(this, _L("Wiki Guide"), "https://www.orcaslicer.com/wiki/prepare_stl_transformation");
HyperLink *tips = new HyperLink(this, _L("Wiki Guide"), "https://www.orcaslicer.com/wiki/import_export#step");
tips->SetFont(::Label::Body_12);
info->Wrap(FromDIP(400));
+40 -13
View File
@@ -903,9 +903,21 @@ void Tab::filter_diff_option(std::vector<std::string> &options)
break;
}
}
if (!found) opt = opt.substr(0, hash_pos);
if (found) continue;
// Keep key#index if that exact option is tracked.
if (m_options_list.find(opt) != m_options_list.end())
continue;
const std::string base = opt.substr(0, hash_pos);
const std::string idx0 = base + "#0";
if (m_options_list.find(idx0) != m_options_list.end()) {
opt = idx0;
continue;
}
if (m_options_list.find(base) != m_options_list.end())
opt = base;
}
options.erase(std::remove(options.begin(), options.end(), ""), options.end());
}
// Update UI according to changes
@@ -974,7 +986,8 @@ void Tab::init_options_list()
m_options_list.emplace(opt_key, m_opt_status_value);
continue;
}
if (m_config->option(opt_key)->is_vector())
const ConfigOptionDef* opt_def = m_config->def()->get(opt_key);
if (m_config->option(opt_key)->is_vector() && !(opt_def && opt_def->gui_flags == "serialized"))
m_options_list.emplace(opt_key + "#0", m_opt_status_value);
else
m_options_list.emplace(opt_key, m_opt_status_value);
@@ -987,10 +1000,15 @@ void TabPrinter::init_options_list()
if (m_printer_technology == ptFFF)
m_options_list.emplace("extruders_count", m_opt_status_value);
for (size_t i = 1; i < m_extruders_count; ++i) {
auto extruder_page = m_pages[3 + i];
for (auto group : extruder_page->m_optgroups) {
for (auto & opt : group->opt_map())
m_options_list.emplace(opt.first, m_opt_status_value);
wxString target_title = wxString::Format("Extruder %d", int(i + 1));
for (auto &page : m_pages) {
if (page->title() == target_title) {
for (auto group : page->m_optgroups) {
for (auto &opt : group->opt_map())
m_options_list.emplace(opt.first, m_opt_status_value);
}
break;
}
}
}
}
@@ -1597,7 +1615,7 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value)
bool wipe_tower_enabled = m_config->option<ConfigOptionBool>("enable_prime_tower")->value;
if (boost::any_cast<bool>(value) && !wipe_tower_enabled) {
MessageDialog dlg(wxGetApp().plater(),
_L("Prime tower is required for clumping detection. There may be flaws on the model without prime tower. Do you still want to enable clumping detection?"),
_L("A prime tower is required for clumping detection. There may be flaws on the model without prime tower. Do you still want to enable clumping detection?"),
_L("Warning"), wxICON_WARNING | wxYES | wxNO);
if (dlg.ShowModal() == wxID_NO) {
DynamicPrintConfig new_conf = *m_config;
@@ -1706,7 +1724,7 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value)
"Yes - Change these settings automatically\n"
"No - Do not change these settings for me");
}
MessageDialog dialog(wxGetApp().plater(), msg_text, "Suggestion", wxICON_WARNING | wxYES | wxNO);
MessageDialog dialog(wxGetApp().plater(), msg_text, _L("Suggestion"), wxICON_WARNING | wxYES | wxNO);
DynamicPrintConfig new_conf = *m_config;
if (dialog.ShowModal() == wxID_YES) {
auto &filament_presets = Slic3r::GUI::wxGetApp().preset_bundle->filament_presets;
@@ -1758,7 +1776,7 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value)
wxString msg_text = _(
L("Infill patterns are typically designed to handle rotation automatically to ensure proper printing and achieve their "
"intended effects (e.g., Gyroid, Cubic). Rotating the current sparse infill pattern may lead to insufficient support. "
"Please proceed with caution and thoroughly check for any potential printing issues."
"Please proceed with caution and thoroughly check for any potential printing issues. "
"Are you sure you want to enable this option?"));
msg_text += "\n\n" + _(L("Are you sure you want to enable this option?"));
MessageDialog dialog(wxGetApp().plater(), msg_text, "", wxICON_WARNING | wxYES | wxNO);
@@ -2448,7 +2466,7 @@ void TabPrint::build()
optgroup->append_single_option_line("ensure_vertical_shell_thickness", "strength_settings_advanced#ensure-vertical-shell-thickness");
page = add_options_page(L("Speed"), "custom-gcode_speed"); // ORCA: icon only visible on placeholders
optgroup = page->new_optgroup(L("Initial layer speed"), L"param_speed_first", 15);
optgroup = page->new_optgroup(L("First layer speed"), L"param_speed_first", 15);
optgroup->append_single_option_line("initial_layer_speed", "speed_settings_initial_layer_speed#initial-layer");
optgroup->append_single_option_line("initial_layer_infill_speed", "speed_settings_initial_layer_speed#initial-layer-infill");
optgroup->append_single_option_line("initial_layer_travel_speed", "speed_settings_initial_layer_speed#initial-layer-travel-speed");
@@ -2584,6 +2602,8 @@ void TabPrint::build()
optgroup = page->new_optgroup(L("Prime tower"), L"param_tower");
optgroup->append_single_option_line("enable_prime_tower", "multimaterial_settings_prime_tower");
optgroup->append_single_option_line("prime_tower_skip_points", "multimaterial_settings_prime_tower");
optgroup->append_single_option_line("enable_tower_interface_features", "multimaterial_settings_prime_tower");
optgroup->append_single_option_line("enable_tower_interface_cooldown_during_tower", "multimaterial_settings_prime_tower");
optgroup->append_single_option_line("prime_tower_enable_framework", "multimaterial_settings_prime_tower");
optgroup->append_single_option_line("prime_tower_width", "multimaterial_settings_prime_tower#width");
optgroup->append_single_option_line("prime_volume", "multimaterial_settings_prime_tower");
@@ -4048,6 +4068,11 @@ void TabFilament::build()
page = add_options_page(L("Multimaterial"), "custom-gcode_multi_material"); // ORCA: icon only visible on placeholders
optgroup = page->new_optgroup(L("Wipe tower parameters"), "param_tower");
optgroup->append_single_option_line("filament_minimal_purge_on_wipe_tower", "material_multimaterial#multimaterial-wipe-tower-parameters");
optgroup->append_single_option_line("filament_tower_interface_pre_extrusion_dist", "material_multimaterial#multimaterial-wipe-tower-parameters");
optgroup->append_single_option_line("filament_tower_interface_pre_extrusion_length", "material_multimaterial#multimaterial-wipe-tower-parameters");
optgroup->append_single_option_line("filament_tower_ironing_area", "material_multimaterial#multimaterial-wipe-tower-parameters");
optgroup->append_single_option_line("filament_tower_interface_purge_volume", "material_multimaterial#multimaterial-wipe-tower-parameters");
optgroup->append_single_option_line("filament_tower_interface_print_temp", "material_multimaterial#multimaterial-wipe-tower-parameters");
optgroup = page->new_optgroup(L("Multi Filament"));
// optgroup->append_single_option_line("filament_flush_temp", "", 0);
@@ -4407,7 +4432,6 @@ void TabPrinter::build_fff()
optgroup->append_single_option_line("use_relative_e_distances", "printer_basic_information_advanced#use-relative-e-distances");
optgroup->append_single_option_line("use_firmware_retraction", "printer_basic_information_advanced#use-firmware-retraction");
optgroup->append_single_option_line("bed_temperature_formula", "printer_basic_information_advanced#bed-temperature-type");
// optgroup->append_single_option_line("spaghetti_detector");
optgroup->append_single_option_line("time_cost", "printer_basic_information_advanced#time-cost");
@@ -4892,6 +4916,7 @@ if (is_marlin_flavor)
});
};
optgroup->append_single_option_line("manual_filament_change", "printer_multimaterial_setup#manual-filament-change");
optgroup->append_single_option_line("bed_temperature_formula", "printer_basic_information_advanced#bed-temperature-type");
optgroup = page->new_optgroup(L("Wipe tower"), "param_tower");
optgroup->append_single_option_line("purge_in_prime_tower", "printer_multimaterial_wipe_tower#purge-in-prime-tower");
@@ -5621,7 +5646,7 @@ void Tab::rebuild_page_tree()
if (sel_item == m_last_select_item)
m_last_select_item = item;
else
m_last_select_item = NULL;
m_last_select_item = 0;
// allow activate page before selection of a page_tree item
m_disable_tree_sel_changed_event = false;
@@ -6969,6 +6994,8 @@ void Tab::switch_excluder(int extruder_id)
{}, {"", "filament_extruder_variant"}, // Preset::TYPE_FILAMENT filament don't use id anymore
{}, {"printer_extruder_id", "printer_extruder_variant"}, // Preset::TYPE_PRINTER
};
if (extruder_id >= nozzle_volumes->size() || extruder_id >= extruders->size())
extruder_id = 0;
if (m_extruder_switch && m_type != Preset::TYPE_PRINTER) {
int current_extruder = m_extruder_switch->GetValue() ? 1 : 0;
if (extruder_id == -1)
Loaded 100 of 838 files, more files were not shown because too many files have changed in this diff. Show more