mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-10 17:21:10 +00:00
Merge upstream, including cached config lookups in G-code export
Three commits: G-code export caches its filament config slot and repeated option lookups, per-plate bed type overrides follow the printer's multi-bed support, and the gizmo checkboxes and texture displacement panel get styling and refresh fixes. No conflicts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+33
-25
@@ -4528,9 +4528,9 @@ size_t GCode::get_extruder_id(unsigned int filament_id) const
|
||||
|
||||
size_t GCode::get_filament_config_index(int filament_id) const
|
||||
{
|
||||
if (m_print) {
|
||||
return m_print->get_filament_config_indx(filament_id, m_cur_layer_idx);
|
||||
}
|
||||
if (m_print)
|
||||
return m_filament_index_cache.get(filament_id, m_cur_layer_idx, m_print->config_index_generation(),
|
||||
[&] { return m_print->get_filament_config_indx(filament_id, m_cur_layer_idx); });
|
||||
// Orca: without a Print the filament-indexed arrays are unexpanded, so the
|
||||
// filament id itself is the only meaningful column.
|
||||
return filament_id;
|
||||
@@ -4544,9 +4544,9 @@ size_t GCode::get_filament_config_index(int filament_id, size_t layer_id) const
|
||||
|
||||
size_t GCode::get_nozzle_config_index(int filament_id) const
|
||||
{
|
||||
if (m_print) {
|
||||
return m_print->get_nozzle_config_index(filament_id, m_cur_layer_idx);
|
||||
}
|
||||
if (m_print)
|
||||
return m_nozzle_index_cache.get(filament_id, m_cur_layer_idx, m_print->config_index_generation(),
|
||||
[&] { return m_print->get_nozzle_config_index(filament_id, m_cur_layer_idx); });
|
||||
// Orca: same reasoning; degenerate to the filament's extruder column.
|
||||
return get_extruder_id(filament_id);
|
||||
}
|
||||
@@ -8319,28 +8319,36 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
||||
unsigned int acceleration_i = 0;
|
||||
double jerk = 0;
|
||||
// adjust acceleration
|
||||
if (NOZZLE_CONFIG(default_acceleration) > 0) {
|
||||
const size_t nozzle = get_nozzle_config_index(m_writer.filament()->id());
|
||||
if (m_config.default_acceleration.get_at(nozzle) > 0) {
|
||||
const ExtrusionRole role = path.role();
|
||||
const double bridge_acceleration = is_bridge(role) ?
|
||||
m_config.bridge_acceleration.get_at(nozzle).get_abs_value(m_config.outer_wall_acceleration.get_at(nozzle)) : 0.;
|
||||
const double sparse_infill_acceleration = role == erInternalInfill ?
|
||||
m_config.sparse_infill_acceleration.get_at(nozzle).get_abs_value(m_config.default_acceleration.get_at(nozzle)) : 0.;
|
||||
const double internal_solid_infill_acceleration = role == erSolidInfill ?
|
||||
m_config.internal_solid_infill_acceleration.get_at(nozzle).get_abs_value(m_config.default_acceleration.get_at(nozzle)) : 0.;
|
||||
double acceleration;
|
||||
if (this->on_first_layer() && NOZZLE_CONFIG(initial_layer_acceleration) > 0) {
|
||||
acceleration = NOZZLE_CONFIG(initial_layer_acceleration);
|
||||
if (this->on_first_layer() && m_config.initial_layer_acceleration.get_at(nozzle) > 0) {
|
||||
acceleration = m_config.initial_layer_acceleration.get_at(nozzle);
|
||||
#if 0
|
||||
} else if (this->object_layer_over_raft() && m_config.first_layer_acceleration_over_raft.value > 0) {
|
||||
acceleration = m_config.first_layer_acceleration_over_raft.value;
|
||||
#endif
|
||||
} else if (m_config.get_abs_value_at("bridge_acceleration", get_nozzle_config_index(m_writer.filament()->id())) > 0 && is_bridge(path.role())) {
|
||||
acceleration = m_config.get_abs_value_at("bridge_acceleration", get_nozzle_config_index(m_writer.filament()->id()));
|
||||
} else if (m_config.get_abs_value_at("sparse_infill_acceleration", get_nozzle_config_index(m_writer.filament()->id())) > 0 && (path.role() == erInternalInfill)) {
|
||||
acceleration = m_config.get_abs_value_at("sparse_infill_acceleration", get_nozzle_config_index(m_writer.filament()->id()));
|
||||
} else if (m_config.get_abs_value_at("internal_solid_infill_acceleration", get_nozzle_config_index(m_writer.filament()->id())) > 0 && (path.role() == erSolidInfill)) {
|
||||
acceleration = m_config.get_abs_value_at("internal_solid_infill_acceleration", get_nozzle_config_index(m_writer.filament()->id()));
|
||||
} else if (NOZZLE_CONFIG(outer_wall_acceleration) > 0 && is_external_perimeter(path.role())) {
|
||||
acceleration = NOZZLE_CONFIG(outer_wall_acceleration);
|
||||
} else if (NOZZLE_CONFIG(inner_wall_acceleration) > 0 && is_internal_perimeter(path.role())) {
|
||||
acceleration = NOZZLE_CONFIG(inner_wall_acceleration);
|
||||
} else if (NOZZLE_CONFIG(top_surface_acceleration) > 0 && is_top_surface(path.role())) {
|
||||
acceleration = NOZZLE_CONFIG(top_surface_acceleration);
|
||||
} else if (bridge_acceleration > 0) {
|
||||
acceleration = bridge_acceleration;
|
||||
} else if (sparse_infill_acceleration > 0) {
|
||||
acceleration = sparse_infill_acceleration;
|
||||
} else if (internal_solid_infill_acceleration > 0) {
|
||||
acceleration = internal_solid_infill_acceleration;
|
||||
} else if (m_config.outer_wall_acceleration.get_at(nozzle) > 0 && is_external_perimeter(role)) {
|
||||
acceleration = m_config.outer_wall_acceleration.get_at(nozzle);
|
||||
} else if (m_config.inner_wall_acceleration.get_at(nozzle) > 0 && is_internal_perimeter(role)) {
|
||||
acceleration = m_config.inner_wall_acceleration.get_at(nozzle);
|
||||
} else if (m_config.top_surface_acceleration.get_at(nozzle) > 0 && is_top_surface(role)) {
|
||||
acceleration = m_config.top_surface_acceleration.get_at(nozzle);
|
||||
} else {
|
||||
acceleration = NOZZLE_CONFIG(default_acceleration);
|
||||
acceleration = m_config.default_acceleration.get_at(nozzle);
|
||||
}
|
||||
acceleration_i = (unsigned int)floor(acceleration + 0.5);
|
||||
}
|
||||
@@ -8441,7 +8449,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
||||
speed = std::min(speed, m_config.scarf_joint_speed.get_abs_value(speed));
|
||||
}
|
||||
} else if(path.role() == erInternalBridgeInfill) {
|
||||
speed = m_config.get_abs_value_at("internal_bridge_speed", get_nozzle_config_index(m_writer.filament()->id()));
|
||||
speed = m_config.internal_bridge_speed.get_at(nozzle).get_abs_value(m_config.bridge_speed.get_at(nozzle));
|
||||
} else if (path.role() == erOverhangPerimeter || path.role() == erSupportTransition || path.role() == erBridgeInfill) {
|
||||
speed = NOZZLE_CONFIG(bridge_speed);
|
||||
} else if (path.role() == erInternalInfill) {
|
||||
@@ -8453,7 +8461,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
||||
} else if (path.role() == erIroning) {
|
||||
const size_t filament_idx = get_filament_config_index(m_writer.filament()->id());
|
||||
speed = m_config.filament_ironing_speed.is_nil(filament_idx)
|
||||
? m_config.get_abs_value("ironing_speed")
|
||||
? m_config.ironing_speed.value
|
||||
: m_config.filament_ironing_speed.get_at(filament_idx);
|
||||
} else if (path.role() == erBottomSurface) {
|
||||
speed = NOZZLE_CONFIG(initial_layer_infill_speed);
|
||||
@@ -8514,7 +8522,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
||||
}
|
||||
// Override skirt speed if set
|
||||
if (path.role() == erSkirt) {
|
||||
const double skirt_speed = m_config.get_abs_value("skirt_speed");
|
||||
const double skirt_speed = m_config.skirt_speed.value;
|
||||
if (skirt_speed > 0.0)
|
||||
speed = skirt_speed;
|
||||
}
|
||||
|
||||
@@ -314,6 +314,24 @@ public:
|
||||
size_t get_filament_config_index(int filament_id, size_t layer_id) const;
|
||||
size_t get_nozzle_config_index(int filament_id) const;
|
||||
|
||||
// Holds the last slot a resolver returned without locking, so only the G-code generator may
|
||||
// call the resolvers.
|
||||
struct ConfigIndexCache
|
||||
{
|
||||
bool valid{false};
|
||||
int filament_id{0};
|
||||
size_t layer_idx{0};
|
||||
size_t generation{0};
|
||||
size_t index{0};
|
||||
|
||||
template<class Lookup> size_t get(int filament, size_t layer, size_t gen, Lookup &&lookup)
|
||||
{
|
||||
if (!valid || filament_id != filament || layer_idx != layer || generation != gen)
|
||||
*this = {true, filament, layer, gen, size_t(lookup())};
|
||||
return index;
|
||||
}
|
||||
};
|
||||
|
||||
// Object and support extrusions of the same PrintObject at the same print_z.
|
||||
// public, so that it could be accessed by free helper functions from GCode.cpp
|
||||
struct LayerToPrint
|
||||
@@ -805,6 +823,8 @@ private:
|
||||
// Object layer id of the layer being generated; keys the per-filament config-slot
|
||||
// resolvers. Distinct from m_layer_index (an export progress counter starting at -1).
|
||||
size_t m_cur_layer_idx{0};
|
||||
mutable ConfigIndexCache m_filament_index_cache;
|
||||
mutable ConfigIndexCache m_nozzle_index_cache;
|
||||
|
||||
std::set<unsigned int> m_initial_layer_extruders;
|
||||
std::vector<std::vector<unsigned int>> m_sorted_layer_filaments;
|
||||
|
||||
@@ -4488,10 +4488,11 @@ Polygons Print::get_extruder_shared_printable_polygon() const
|
||||
return shared_printable_polys;
|
||||
}
|
||||
|
||||
// Narrow the stored grouping result to the layer-aware type the slicing pipeline uses.
|
||||
std::shared_ptr<MultiNozzleUtils::LayeredNozzleGroupResult> Print::get_layered_nozzle_group_result() const
|
||||
void Print::set_nozzle_group_result(std::shared_ptr<MultiNozzleUtils::NozzleGroupResultBase> result)
|
||||
{
|
||||
return std::dynamic_pointer_cast<MultiNozzleUtils::LayeredNozzleGroupResult>(m_nozzle_group_result);
|
||||
m_nozzle_group_result = std::move(result);
|
||||
m_layered_nozzle_group_result = std::dynamic_pointer_cast<MultiNozzleUtils::LayeredNozzleGroupResult>(m_nozzle_group_result);
|
||||
++m_config_index_generation;
|
||||
}
|
||||
|
||||
// Dynamic (per-layer selector) regroup predicate.
|
||||
@@ -4527,6 +4528,7 @@ int Print::get_filament_config_indx(int filament_id, int layer_id, bool use_cach
|
||||
void Print::update_filament_self_index_cache()
|
||||
{
|
||||
m_missing_nozzle_group_logged.clear(); // reset the per-slice get_config_index log dedupe
|
||||
++m_config_index_generation;
|
||||
|
||||
std::vector<int> values;
|
||||
if (m_full_print_config.has("filament_self_index")) {
|
||||
@@ -4566,7 +4568,7 @@ int Print::get_nozzle_config_index(int filament_id, int layer_id)
|
||||
|
||||
int Print::get_config_index(int filament_id, int layer_id, const std::vector<std::string> &variant_list, const std::vector<int>& self_index_list, FilamentIndexMap *index_map)
|
||||
{
|
||||
auto group_result = get_layered_nozzle_group_result();
|
||||
const MultiNozzleUtils::LayeredNozzleGroupResult *group_result = m_layered_nozzle_group_result.get();
|
||||
// Orca: defensive — when no grouping producer has published a result yet, fall back to the
|
||||
// static identity: one filament-variant column per filament.
|
||||
if (!group_result)
|
||||
@@ -4601,7 +4603,7 @@ int Print::get_config_index(int filament_id, int layer_id, const std::vector<std
|
||||
|
||||
int Print::get_config_index(int filament_id, int layer_id, const std::vector<std::string> &variant_list, const std::vector<int>& self_index_list, PrintIndexMap &index_map)
|
||||
{
|
||||
auto group_result = get_layered_nozzle_group_result();
|
||||
const MultiNozzleUtils::LayeredNozzleGroupResult *group_result = m_layered_nozzle_group_result.get();
|
||||
// Orca: same static fallback as the filament overload; the slot degenerates to the filament's
|
||||
// extruder column (filament_map is 1 based, get_extruder_id guards the filament id range).
|
||||
if (!group_result)
|
||||
|
||||
@@ -1073,9 +1073,9 @@ public:
|
||||
|
||||
// Logical (extruder, nozzle) grouping result produced by ToolOrdering during reorder.
|
||||
// Consumed by GCode via get_layered_nozzle_group_result()->get_nozzle_id(filament, layer) etc.
|
||||
void set_nozzle_group_result(std::shared_ptr<MultiNozzleUtils::NozzleGroupResultBase> result) { m_nozzle_group_result = result; }
|
||||
void set_nozzle_group_result(std::shared_ptr<MultiNozzleUtils::NozzleGroupResultBase> result);
|
||||
std::shared_ptr<MultiNozzleUtils::NozzleGroupResultBase> get_nozzle_group_result() const { return m_nozzle_group_result; }
|
||||
std::shared_ptr<MultiNozzleUtils::LayeredNozzleGroupResult> get_layered_nozzle_group_result() const;
|
||||
std::shared_ptr<MultiNozzleUtils::LayeredNozzleGroupResult> get_layered_nozzle_group_result() const { return m_layered_nozzle_group_result; }
|
||||
|
||||
// True only when the project opts into the per-layer filament selector
|
||||
// (enable_filament_dynamic_map) in auto-for-flush mode on a multi-extruder machine. Gates the
|
||||
@@ -1238,6 +1238,9 @@ public:
|
||||
// pipeline's cooling stage, which runs concurrently with the generator stage filling it.
|
||||
int get_filament_config_indx(int filament_id, int layer_id, bool use_cache = true);
|
||||
int get_nozzle_config_index(int filament_id, int layer_id);
|
||||
// Changes with the grouping result and the filament maps, so a caller may reuse a resolved slot
|
||||
// until it changes.
|
||||
size_t config_index_generation() const { return m_config_index_generation; }
|
||||
|
||||
// Orca: Implement prusa's filament shrink compensation approach
|
||||
// Returns if all used filaments have same shrinkage compensations.
|
||||
@@ -1364,6 +1367,9 @@ private:
|
||||
|
||||
// Logical (extruder, nozzle) grouping result, set by ToolOrdering during reorder.
|
||||
std::shared_ptr<MultiNozzleUtils::NozzleGroupResultBase> m_nozzle_group_result;
|
||||
// m_nozzle_group_result narrowed to the layer-aware type; only set_nozzle_group_result() assigns
|
||||
// either.
|
||||
std::shared_ptr<MultiNozzleUtils::LayeredNozzleGroupResult> m_layered_nozzle_group_result;
|
||||
|
||||
// Sequential (by-object) selector plans, keyed by object; see sequential_dynamic_orderings().
|
||||
// Rebuilt (or cleared) on every process().
|
||||
@@ -1373,6 +1379,7 @@ private:
|
||||
FilamentIndexMap m_filament_index_map;
|
||||
// Used to cache printer and process parameter information
|
||||
PrintIndexMap m_nozzle_index_map;
|
||||
size_t m_config_index_generation{0};
|
||||
// Orca: filament ids already reported as missing a nozzle-group entry this slice. get_config_index()
|
||||
// falls back per-filament/per-layer in the g-code hot path, so this dedupes its log to once per
|
||||
// filament instead of flooding thousands of identical error lines. Cleared with the caches each slice.
|
||||
|
||||
@@ -1310,7 +1310,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
|
||||
// Reproduce that exact expansion here so an unchanged config diffs empty — the expanded
|
||||
// keys invalidate the wipe tower / g-code export, and the placeholder parser aliases
|
||||
// the full config — instead of trimming back to one slot per filament.
|
||||
auto group_result = std::dynamic_pointer_cast<MultiNozzleUtils::LayeredNozzleGroupResult>(this->get_nozzle_group_result());
|
||||
auto group_result = this->get_layered_nozzle_group_result();
|
||||
std::unordered_map<int, std::vector<FilamentVariantUse>> filament_variant_uses;
|
||||
if (group_result && group_result->is_support_dynamic_nozzle_map()
|
||||
&& collect_filament_variant_uses(*group_result, m_ori_full_print_config, filament_variant_uses))
|
||||
|
||||
@@ -6735,8 +6735,10 @@ void GUI_App::reload_settings()
|
||||
tab->reload_config();
|
||||
tab->update_changed_ui();
|
||||
}
|
||||
if (plater_)
|
||||
if (plater_) {
|
||||
plater_->sidebar().update_all_preset_comboboxes();
|
||||
plater_->normalize_bed_types(false);
|
||||
}
|
||||
};
|
||||
if (is_main_thread_active())
|
||||
refresh_synced_ui();
|
||||
|
||||
@@ -5151,7 +5151,7 @@ void GLGizmoTextureDisplacement::render_debug_stage_panel(ModelVolume *mv)
|
||||
m_imgui->scaled(20.f));
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::Checkbox(_u8L("Check topology").c_str(), &m_debug_check_topology);
|
||||
m_imgui->bbl_checkbox(_L("Check topology"), m_debug_check_topology);
|
||||
if (ImGui::IsItemHovered())
|
||||
m_imgui->tooltip(_u8L("Count open and non-manifold edges after each stage, which is how a stage "
|
||||
"that tore the mesh is spotted. It scans every edge, so it adds noticeably "
|
||||
@@ -5341,6 +5341,8 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float
|
||||
GizmoImguiBegin(get_name(), flags);
|
||||
ensure_panel_icons();
|
||||
process_uv_commands(); // clicks from the UV editor pane, run here where the GL context is current
|
||||
const float previous_body_h = m_panel_body_h;
|
||||
const float previous_footer_h = m_panel_footer_h;
|
||||
|
||||
// Pinned every frame while Standard is active, so what Preview shows is always what Bake will do.
|
||||
if (!pro_mode() && apply_standard_mode_presets(mv))
|
||||
@@ -5362,20 +5364,16 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float
|
||||
const float card_pad = std::round(m_imgui->scaled(0.55f));
|
||||
const float wrap_w = m_imgui->scaled(20.f);
|
||||
const ImVec4 orca = ImGuiWrapper::COL_ORCA;
|
||||
const ImVec4 col_link = dark ? ImVec4(0.30f, 0.71f, 0.67f, 1.f) : ImVec4(0.f, 0.47f, 0.42f, 1.f);
|
||||
const ImVec4 col_frame = dark ? ImVec4(0.212f, 0.212f, 0.235f, 1.f) : ImVec4(0.808f, 0.808f, 0.808f, 1.f);
|
||||
const ImU32 col_card = dark ? IM_COL32(255, 255, 255, 10) : IM_COL32(0, 0, 0, 12);
|
||||
const ImU32 col_line = dark ? IM_COL32(255, 255, 255, 18) : IM_COL32(0, 0, 0, 23);
|
||||
const ImVec4 col_link = ImGuiWrapper::COL_ORCA;
|
||||
const ImVec4 col_frame = ImGui::GetStyleColorVec4(ImGuiCol_Separator);
|
||||
const ImU32 col_card = ImGui::GetColorU32(ImGuiCol_Text, dark ? 0.045f : 0.047f);
|
||||
const ImU32 col_line = ImGui::GetColorU32(ImGuiCol_Text, dark ? 0.08f : 0.09f);
|
||||
const ImU32 col_sep = ImGui::GetColorU32(ImGuiCol_Separator);
|
||||
|
||||
// Combo drop-downs otherwise inherit ImGui's near-black default popup background; under the light
|
||||
// theme that leaves the dark item text unreadable ("the dropbox is black"). Pushed only around each
|
||||
// Combo below (never around a tooltip, whose own near-black default is what makes it readable).
|
||||
const ImVec4 combo_popup_bg = dark ? ImVec4(0.18f, 0.18f, 0.19f, 1.f) : ImVec4(0.93f, 0.93f, 0.93f, 1.f);
|
||||
const auto scoped_combo = [&](const char *id, int *v, const char *const items[], int n) {
|
||||
ImGui::PushStyleColor(ImGuiCol_PopupBg, combo_popup_bg);
|
||||
ImGuiWrapper::push_combo_style(m_parent.get_scale());
|
||||
const bool changed = ImGui::Combo(id, v, items, n);
|
||||
ImGui::PopStyleColor();
|
||||
ImGuiWrapper::pop_combo_style();
|
||||
return changed;
|
||||
};
|
||||
const auto hover_tip = [&](const auto &text) {
|
||||
@@ -5782,10 +5780,10 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float
|
||||
wf_toggle = true;
|
||||
|
||||
const std::string auto_label = _u8L("Auto");
|
||||
const float auto_w = frame_h + style.ItemInnerSpacing.x + ImGui::CalcTextSize(auto_label.c_str()).x;
|
||||
const float auto_w = frame_h * 0.78f /*ratio from BBLCheckbox*/ + style.ItemInnerSpacing.x + ImGui::CalcTextSize(auto_label.c_str()).x;
|
||||
ImGui::SameLine();
|
||||
ImGui::SetCursorPosX(std::max(ImGui::GetCursorPosX(), ImGui::GetWindowContentRegionMax().x - auto_w));
|
||||
if (ImGui::Checkbox((auto_label + "##auto_update").c_str(), &m_auto_update) && m_auto_update)
|
||||
if (m_imgui->bbl_checkbox(wxString::FromUTF8(auto_label) + "##auto_update", m_auto_update) && m_auto_update)
|
||||
rebuild_preview(); // catch up anything that changed while it was off
|
||||
hover_tip(_u8L("Rebuilds the preview as soon as anything changes. Turn it off on a heavy model if painting "
|
||||
"or dragging a slider starts to stutter - the preview then waits until you let go."));
|
||||
@@ -6020,7 +6018,7 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float
|
||||
"costs fine detail."));
|
||||
|
||||
// Edge fade: the relief flattens toward the boundary of the painted area.
|
||||
m_preview_params_dirty |= ImGui::Checkbox((_u8L("Edge fade") + "##edge_smoothing").c_str(), &layer.edge_smoothing);
|
||||
m_preview_params_dirty |= m_imgui->bbl_checkbox(_L("Edge fade") + "##edge_smoothing", layer.edge_smoothing);
|
||||
hover_tip(_u8L("Flattens the relief as it approaches the edge of the painted area, so it "
|
||||
"blends into the bare surface instead of stopping at a step."));
|
||||
ImGui::SameLine();
|
||||
@@ -6035,7 +6033,7 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float
|
||||
// Invert and Colours share a row.
|
||||
{
|
||||
const float x0 = ImGui::GetCursorPosX();
|
||||
m_preview_params_dirty |= ImGui::Checkbox(_u8L("Invert").c_str(), &layer.invert);
|
||||
m_preview_params_dirty |= m_imgui->bbl_checkbox(_L("Invert"), layer.invert);
|
||||
hover_tip(_u8L("Turns the relief inside out: what stood out is cut in, and the other way "
|
||||
"round. The same as using a negative of the image."));
|
||||
ImGui::SameLine();
|
||||
@@ -6047,7 +6045,7 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float
|
||||
const bool has_color = decode_height_texture(layer).has_color();
|
||||
bool color_enabled = layer.color_enabled && has_color;
|
||||
m_imgui->disabled_begin(!has_color);
|
||||
if (ImGui::Checkbox(_u8L("Colours").c_str(), &color_enabled)) {
|
||||
if (m_imgui->bbl_checkbox(_L("Colours"), color_enabled)) {
|
||||
layer.color_enabled = color_enabled;
|
||||
m_preview_params_dirty = true;
|
||||
}
|
||||
@@ -6065,7 +6063,7 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float
|
||||
// under whichever layer turned colour on.
|
||||
if (color_enabled) {
|
||||
TextureDisplacementOptions &opts = mv->texture_displacement_options;
|
||||
if (ImGui::Checkbox(_u8L("Mix filaments").c_str(), &opts.color_mix_enabled))
|
||||
if (m_imgui->bbl_checkbox(_L("Mix filaments"), opts.color_mix_enabled))
|
||||
m_preview_params_dirty = true;
|
||||
hover_tip(_u8L("Interleaves two filaments to fake the colours in between, so a handful "
|
||||
"of filaments can cover a photo or a gradient. An image of flat colours "
|
||||
@@ -6184,7 +6182,7 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float
|
||||
"a slide projector. Faces turned away from you stretch, so line the view up with "
|
||||
"the surface you care about first."));
|
||||
|
||||
if (ImGui::Checkbox(_u8L("Project only on visible").c_str(), &m_project_only_visible)) {
|
||||
if (m_imgui->bbl_checkbox(_L("Project only on visible"), m_project_only_visible)) {
|
||||
if (m_project_only_visible && select_visible_faces() == 0)
|
||||
show_error(nullptr, _u8L("Nothing is visible from this angle - turn the model to face the "
|
||||
"part you want to project onto."));
|
||||
@@ -6195,7 +6193,7 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float
|
||||
"behind anything - and projects onto those. Replaces what the layer had painted."));
|
||||
|
||||
bool projector_open = m_projector_frame != nullptr && m_projector_frame->IsShown();
|
||||
if (ImGui::Checkbox(_u8L("Projection frame").c_str(), &projector_open))
|
||||
if (m_imgui->bbl_checkbox(_L("Projection frame"), projector_open))
|
||||
show_projector(projector_open);
|
||||
hover_tip(_u8L("Opens a window you drag over the model. Whatever you can see through it is what "
|
||||
"gets the texture, and its border becomes the edge of the projection."));
|
||||
@@ -6236,7 +6234,7 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float
|
||||
// Tile, and how it repeats.
|
||||
{
|
||||
const float x0 = ImGui::GetCursorPosX();
|
||||
m_preview_params_dirty |= ImGui::Checkbox((_u8L("Tile") + "##tile_enabled").c_str(), &layer.tile_enabled);
|
||||
m_preview_params_dirty |= m_imgui->bbl_checkbox(_L("Tile") + "##tile_enabled", layer.tile_enabled);
|
||||
hover_tip(_u8L("Repeats the texture across the painted area. Off places one copy, like a decal, "
|
||||
"at the size set by Tile size."));
|
||||
ImGui::SameLine();
|
||||
@@ -6424,7 +6422,7 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float
|
||||
const ImVec2 mn(mouse.x - inner - 0.5f * grip_w, mouse.y - 0.5f * row_h);
|
||||
const ImVec2 mx(mn.x + (content_rx - list_x), mn.y + row_h);
|
||||
fg->AddRectFilled(ImVec2(mn.x + 2.f, mn.y + 4.f), ImVec2(mx.x + 2.f, mx.y + 4.f), IM_COL32(0, 0, 0, 70), rounding);
|
||||
fg->AddRectFilled(mn, mx, dark ? IM_COL32(0x3a, 0x3a, 0x40, 245) : IM_COL32(255, 255, 255, 245), rounding);
|
||||
fg->AddRectFilled(mn, mx, ImGui::GetColorU32(ImGuiCol_WindowBg, 245.f / 255.f), rounding);
|
||||
fg->AddRect(mn, mx, ImGui::GetColorU32(orca), rounding, 0, 1.5f);
|
||||
|
||||
float x = mn.x + inner;
|
||||
@@ -6463,7 +6461,7 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float
|
||||
ImGui::Separator();
|
||||
heading(_L("Subdivision"));
|
||||
|
||||
if (ImGui::Checkbox(_u8L("Only painted area (adaptive)").c_str(), &m_subdivide_adaptive)) {
|
||||
if (m_imgui->bbl_checkbox(_L("Only painted area (adaptive)"), m_subdivide_adaptive)) {
|
||||
if (m_subdivide_editing)
|
||||
rebuild_subdivide_preview(); // switch the wireframe between the uniform and adaptive result
|
||||
m_parent.set_as_dirty();
|
||||
@@ -6494,7 +6492,7 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float
|
||||
m_parent.set_as_dirty();
|
||||
};
|
||||
|
||||
if (ImGui::Checkbox(_u8L("Follow texture detail").c_str(), &m_subdivide_feature))
|
||||
if (m_imgui->bbl_checkbox(_L("Follow texture detail"), m_subdivide_feature))
|
||||
preview_live();
|
||||
hover_tip(_u8L("Spends the triangles where the texture actually bends - packed along ridges and edges, "
|
||||
"sparse over flat ground - instead of spreading them evenly. The same detail for fewer "
|
||||
@@ -6621,7 +6619,7 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float
|
||||
hover_tip(_u8L("Triangle size the whole model is rebuilt with, in millimetres. Displacement works best on an "
|
||||
"even mesh; this is what makes one out of an uneven import."));
|
||||
|
||||
ImGui::Checkbox((_u8L("Keep sharp edges") + "##remesh_sharp").c_str(), &m_remesh_keep_sharp_edges);
|
||||
m_imgui->bbl_checkbox(_L("Keep sharp edges") + "##remesh_sharp", m_remesh_keep_sharp_edges);
|
||||
hover_tip(_u8L("Holds hard edges and open borders in place while the rest is remeshed. Without it "
|
||||
"the remesher slides vertices along the surface and rounds every crisp edge off - "
|
||||
"a cube comes back with wobbly edges."));
|
||||
@@ -6648,12 +6646,12 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float
|
||||
ImGui::Separator();
|
||||
heading(_L("Result"));
|
||||
|
||||
m_preview_params_dirty |= ImGui::Checkbox(_u8L("Displace up to the border").c_str(), &opts.displace_border);
|
||||
m_preview_params_dirty |= m_imgui->bbl_checkbox(_L("Displace up to the border"), opts.displace_border);
|
||||
hover_tip(_u8L("Lets the relief run right to the edge of the painted area. Turn it off to hold that "
|
||||
"outer ring flat, which keeps the displacement strictly inside your paint but flattens "
|
||||
"the pattern at the border."));
|
||||
|
||||
m_preview_params_dirty |= ImGui::Checkbox(_u8L("Smooth result").c_str(), &opts.smooth_enabled);
|
||||
m_preview_params_dirty |= m_imgui->bbl_checkbox(_L("Smooth result"), opts.smooth_enabled);
|
||||
hover_tip(_u8L("Smooths the geometry after the texture has been applied, to take the hard steps out of a "
|
||||
"low-resolution image. Only what the displacement moved is touched. The Smoothing slider "
|
||||
"on a layer is a different thing: it blurs the image before it is used."));
|
||||
@@ -6670,7 +6668,7 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float
|
||||
hover_tip(_u8L("How many smoothing passes to run. More passes spread the smoothing further across "
|
||||
"the surface; Strength decides how much each one moves."));
|
||||
|
||||
m_preview_params_dirty |= ImGui::Checkbox(_u8L("Ignore outer ring").c_str(), &opts.smooth_skip_border);
|
||||
m_preview_params_dirty |= m_imgui->bbl_checkbox(_L("Ignore outer ring"), opts.smooth_skip_border);
|
||||
hover_tip(_u8L("Keeps the outer ring of the painted area out of the smoothing. Its neighbours "
|
||||
"outside the paint never move, so smoothing it drags the relief down and leaves the "
|
||||
"pattern half-melted at the border."));
|
||||
@@ -6695,7 +6693,7 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float
|
||||
// The classic path is the opt-in: the one-run pipeline is the default, and its resolution
|
||||
// control lives in the footer next to Bake (see below).
|
||||
bool classic = !opts.pipeline_v2;
|
||||
if (ImGui::Checkbox(_u8L("Experimental: classic bake pipeline").c_str(), &classic)) {
|
||||
if (m_imgui->bbl_checkbox(_L("Experimental: classic bake pipeline"), classic)) {
|
||||
opts.pipeline_v2 = !classic;
|
||||
m_preview_params_dirty = true;
|
||||
}
|
||||
@@ -6724,13 +6722,13 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float
|
||||
if (SHOW_PIPELINE_DEV_CONTROLS && opts.pipeline_v2) {
|
||||
// Keeping the relief above the plate is not a checkbox: it is unconditional, in both pipelines
|
||||
// (see build_texture_displacement()).
|
||||
m_preview_params_dirty |= ImGui::Checkbox(_u8L("Align mesh to texture edges").c_str(), &opts.v2_relocate);
|
||||
m_preview_params_dirty |= m_imgui->bbl_checkbox(_L("Align mesh to texture edges"), opts.v2_relocate);
|
||||
hover_tip(_u8L("Slide vertices sideways onto the edges in the texture before displacing them. "
|
||||
"Displacement can only move vertices up and down, so without this a sharp step "
|
||||
"in the image lands wherever the triangles happen to be and comes out as a "
|
||||
"staircase. Moving the vertices onto the step first gives a straight wall at the "
|
||||
"same triangle count."));
|
||||
m_preview_params_dirty |= ImGui::Checkbox(_u8L("Clean up slivers").c_str(), &opts.v2_regularize);
|
||||
m_preview_params_dirty |= m_imgui->bbl_checkbox(_L("Clean up slivers"), opts.v2_regularize);
|
||||
hover_tip(_u8L("Collapse the thin triangles refinement inherits from the model's own "
|
||||
"tessellation, before displacement samples them. A sliver's three corners "
|
||||
"land on three unrelated parts of the texture, which is what makes the "
|
||||
@@ -6810,7 +6808,7 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float
|
||||
// a fixed value, seeded with the recommendation so it starts from something sensible.
|
||||
bool auto_res = opts.v2_refine_mm <= 0.f;
|
||||
m_imgui->disabled_begin(busy);
|
||||
if (ImGui::Checkbox("##v2auto", &auto_res)) {
|
||||
if (m_imgui->bbl_checkbox(wxString::FromUTF8("##v2auto"), auto_res)) {
|
||||
if (auto_res) {
|
||||
opts.v2_refine_mm = 0.f;
|
||||
opts.v2_max_triangles_k = -1;
|
||||
@@ -6908,11 +6906,7 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float
|
||||
const std::string bake_label = m_prepare_in_progress ? _u8L("Preparing...") :
|
||||
m_bake_in_progress ? _u8L("Baking...") :
|
||||
into_u8(m_desc.at("bake"));
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, orca);
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImGuiWrapper::COL_ORCA_HOVER);
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, orca);
|
||||
ImGui::PushStyleColor(ImGuiCol_Border, orca);
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f));
|
||||
GLGizmoUtils::push_orca_button_style();
|
||||
m_imgui->push_bold_font();
|
||||
m_imgui->disabled_begin(!can_bake);
|
||||
if (ImGui::Button((bake_label + "##bake").c_str(), ImVec2(x0 + panel_w - ImGui::GetCursorPosX(), button_h))) {
|
||||
@@ -6925,7 +6919,7 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float
|
||||
}
|
||||
m_imgui->disabled_end();
|
||||
m_imgui->pop_bold_font();
|
||||
ImGui::PopStyleColor(5);
|
||||
GLGizmoUtils::pop_orca_button_style();
|
||||
if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled))
|
||||
m_imgui->tooltip(mv != nullptr && !mv->is_texture_displacement_painted() ?
|
||||
(m_seam_edit_mode ? _u8L("Nothing is painted yet. The UV editor's seam tool is on, so "
|
||||
@@ -6964,7 +6958,17 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float
|
||||
}
|
||||
}
|
||||
m_panel_footer_h = ImGui::GetCursorScreenPos().y - footer_top;
|
||||
|
||||
constexpr float resize_epsilon = 0.5f;
|
||||
const float content_bottom = ImGui::GetCursorPosY() - style.ItemSpacing.y;
|
||||
const float window_bottom = ImGui::GetWindowContentRegionMax().y;
|
||||
const bool outer_window_needs_fit = content_bottom < window_bottom - resize_epsilon ||
|
||||
(content_bottom > window_bottom + resize_epsilon &&
|
||||
ImGui::GetWindowHeight() < ImGui::GetMainViewport()->Size.y -
|
||||
2.f * style.DisplaySafeAreaPadding.y - resize_epsilon);
|
||||
if (std::abs(m_panel_body_h - previous_body_h) > resize_epsilon ||
|
||||
std::abs(m_panel_footer_h - previous_footer_h) > resize_epsilon || outer_window_needs_fit) {
|
||||
m_parent.request_extra_frame();
|
||||
}
|
||||
GizmoImguiEnd();
|
||||
ImGuiWrapper::pop_toolbar_style();
|
||||
|
||||
|
||||
@@ -2656,11 +2656,7 @@ void ImGuiWrapper::push_toolbar_style(const float scale)
|
||||
ImGui::PushStyleColor(ImGuiCol_FrameBgActive, ImVec4(238 / 255.0f, 238 / 255.0f, 238 / 255.0f, 1.00f)); // 10
|
||||
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(238 / 255.0f, 238 / 255.0f, 238 / 255.0f, 0.00f)); // 11
|
||||
ImGui::PushStyleColor(ImGuiCol_TextSelectedBg, COL_GREEN_LIGHT); // 12
|
||||
// The checkbox/radio frame behind this is drawn fully transparent (see FrameBg above,
|
||||
// alpha 0), showing the light window background through it - a white check mark there is
|
||||
// invisible. Dark mode doesn't have this problem (its window background is dark), so only
|
||||
// this branch needs a check mark color with real contrast against a light background.
|
||||
ImGui::PushStyleColor(ImGuiCol_CheckMark, ImVec4(0.f, 156 / 255.f, 136 / 255.f, 1.00f));//13
|
||||
ImGui::PushStyleColor(ImGuiCol_CheckMark, ImVec4(1.00f, 1.00f, 1.00f, 1.00f));//13
|
||||
ImGui::PushStyleColor(ImGuiCol_ScrollbarGrab, ImVec4(0.42f, 0.42f, 0.42f, 1.00f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ScrollbarGrabHovered, ImVec4(0.93f, 0.93f, 0.93f, 1.00f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ScrollbarGrabActive, ImVec4(0.93f, 0.93f, 0.93f, 1.00f));
|
||||
|
||||
@@ -3894,6 +3894,8 @@ bool MainFrame::load_config_file(const std::string &path)
|
||||
return false;
|
||||
}
|
||||
wxGetApp().load_current_presets();
|
||||
if (Plater *plater = wxGetApp().plater())
|
||||
plater->normalize_bed_types(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "ParamsPanel.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "MainFrame.hpp"
|
||||
#include "Plater.hpp"
|
||||
#include "Tab.hpp"
|
||||
|
||||
#include "libslic3r/Utils.hpp"
|
||||
@@ -70,6 +71,18 @@ ParamsDialog::ParamsDialog(wxWindow * parent)
|
||||
}
|
||||
|
||||
Hide();
|
||||
if (tab && tab->type() == Preset::TYPE_PRINTER) {
|
||||
// Normalize only after the dialog closes, when the final capability is known.
|
||||
auto &preset_bundle = *wxGetApp().preset_bundle;
|
||||
const bool supports_multiple_bed_types = preset_bundle.is_bbl_vendor() ||
|
||||
preset_bundle.printers.get_edited_preset().config.opt_bool("support_multi_bed_types");
|
||||
if (m_initial_multi_bed_types != supports_multiple_bed_types) {
|
||||
wxGetApp().plater()->normalize_bed_types(true);
|
||||
if (auto *plate_tab = dynamic_cast<TabPrintPlate *>(wxGetApp().get_plate_tab()))
|
||||
plate_tab->update_model_config();
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_editing_filament_id.empty()) {
|
||||
Filamentinformation *filament_info = new Filamentinformation();
|
||||
filament_info->filament_id = m_editing_filament_id;
|
||||
@@ -93,7 +106,15 @@ void ParamsDialog::Popup()
|
||||
if (m_panel && m_panel->get_current_tab()) {
|
||||
bool just_edit = false;
|
||||
if (!m_editing_filament_id.empty()) just_edit = true;
|
||||
dynamic_cast<Tab *>(m_panel->get_current_tab())->set_just_edit(just_edit);
|
||||
auto *tab = dynamic_cast<Tab *>(m_panel->get_current_tab());
|
||||
tab->set_just_edit(just_edit);
|
||||
if (tab->type() == Preset::TYPE_PRINTER) {
|
||||
// Remember the initial capability and compare it when the dialog closes.
|
||||
// Bambu profiles support multiple bed types even when this option is unset.
|
||||
auto &preset_bundle = *wxGetApp().preset_bundle;
|
||||
m_initial_multi_bed_types = preset_bundle.is_bbl_vendor() ||
|
||||
preset_bundle.printers.get_edited_preset().config.opt_bool("support_multi_bed_types");
|
||||
}
|
||||
}
|
||||
Show();
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ protected:
|
||||
|
||||
private:
|
||||
std::string m_editing_filament_id;
|
||||
bool m_initial_multi_bed_types = false;
|
||||
ParamsPanel * m_panel;
|
||||
wxWindowDisabler *m_winDisabler = nullptr;
|
||||
};
|
||||
|
||||
@@ -431,8 +431,9 @@ PlateSettingsDialog::PlateSettingsDialog(wxWindow* parent, const wxString& title
|
||||
}
|
||||
}
|
||||
|
||||
if (!wxGetApp().preset_bundle->is_bbl_vendor())
|
||||
m_bed_type_choice->Disable();
|
||||
auto &preset_bundle = *wxGetApp().preset_bundle;
|
||||
const auto &printer_config = preset_bundle.printers.get_edited_preset().config;
|
||||
m_bed_type_choice->Enable(preset_bundle.is_bbl_vendor() || printer_config.opt_bool("support_multi_bed_types"));
|
||||
|
||||
wxStaticText* m_bed_type_txt = new wxStaticText(this, wxID_ANY, _L("Bed type"));
|
||||
m_bed_type_txt->SetFont(Label::Body_14);
|
||||
|
||||
@@ -7403,7 +7403,8 @@ struct Plater::priv
|
||||
std::vector<size_t> load_files(const std::vector<fs::path>& input_files,
|
||||
LoadStrategy strategy,
|
||||
bool ask_multi = false,
|
||||
bool* published_out = nullptr);
|
||||
bool* published_out = nullptr,
|
||||
bool* config_loaded_out = nullptr);
|
||||
std::vector<size_t> load_model_objects(const ModelObjectPtrs& model_objects, bool allow_negative_z = false, bool split_object = false, bool auto_drop = true);
|
||||
|
||||
// Texture-to-color import: a mesh loaded with UVs + a texture map gets its faces clustered
|
||||
@@ -8875,8 +8876,12 @@ void read_binary_stl(const std::string& filename, std::string& model_id, std::st
|
||||
std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_files,
|
||||
LoadStrategy strategy,
|
||||
bool ask_multi,
|
||||
bool* published_out)
|
||||
bool* published_out,
|
||||
bool* config_loaded_out)
|
||||
{
|
||||
if (config_loaded_out != nullptr)
|
||||
*config_loaded_out = false;
|
||||
|
||||
std::vector<size_t> empty_result;
|
||||
bool dlg_cont = true;
|
||||
bool is_user_cancel = false;
|
||||
@@ -9561,6 +9566,8 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
||||
id = agent->to_orca_filament_id(id);
|
||||
}
|
||||
preset_bundle->load_config_model(filename.string(), std::move(config), file_version, &published_config);
|
||||
if (config_loaded_out != nullptr)
|
||||
*config_loaded_out = true;
|
||||
|
||||
// Mixed-filament definitions that collided with one of the
|
||||
// receiver's real slots were relocated during the preset load.
|
||||
@@ -12820,6 +12827,8 @@ void Plater::priv::on_select_bed_type(wxCommandEvent &evt)
|
||||
|
||||
// update plater with new config
|
||||
q->on_config_change(wxGetApp().preset_bundle->full_config());
|
||||
if (auto *plate_tab = dynamic_cast<TabPrintPlate *>(wxGetApp().get_plate_tab()))
|
||||
plate_tab->update_model_config();
|
||||
|
||||
// update app_config
|
||||
AppConfig* app_config = wxGetApp().app_config;
|
||||
@@ -15723,6 +15732,38 @@ void Plater::reset_project_dirty_initial_presets() { p->reset_project_dirty_init
|
||||
void Plater::render_project_state_debug_window() const { p->render_project_state_debug_window(); }
|
||||
#endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW
|
||||
|
||||
void Plater::normalize_bed_types(bool printer_setting_changed)
|
||||
{
|
||||
if (only_gcode_mode() || is_gcode_3mf())
|
||||
return;
|
||||
|
||||
auto &preset_bundle = *wxGetApp().preset_bundle;
|
||||
// Keep FFF plate settings intact while an SLA printer is selected.
|
||||
if (preset_bundle.printers.get_edited_preset().printer_technology() != ptFFF)
|
||||
return;
|
||||
|
||||
const auto &printer_config = preset_bundle.printers.get_edited_preset().config;
|
||||
|
||||
const bool supports_multiple_bed_types =
|
||||
preset_bundle.is_bbl_vendor() || printer_config.opt_bool("support_multi_bed_types");
|
||||
// Clear local overrides for single-bed printers.
|
||||
const bool overrides_reset = !supports_multiple_bed_types &&
|
||||
!p->partplate_list.check_all_plate_local_bed_type({});
|
||||
|
||||
if (overrides_reset) {
|
||||
set_plater_dirty(true);
|
||||
show_info(this,
|
||||
_L("The selected printer does not support multiple bed types.\nBed type overrides were reset to the global bed type."),
|
||||
_L("Plate bed types reset"));
|
||||
}
|
||||
|
||||
// Refresh the controls after a capability change, even when no override was reset.
|
||||
if (printer_setting_changed || overrides_reset) {
|
||||
sidebar().update_all_preset_comboboxes();
|
||||
wxGetApp().obj_list()->update_and_show_object_settings_item();
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<size_t> Plater::mixed_filament_config_indices() const
|
||||
{
|
||||
std::vector<size_t> indices;
|
||||
@@ -16303,6 +16344,9 @@ void Plater::load_project(wxString const& filename2,
|
||||
p->dirty_state.update_from_undo_redo_stack(true);
|
||||
up_to_date(true, true);
|
||||
|
||||
// Clear plate overrides that are incompatible with the selected printer.
|
||||
normalize_bed_types(false);
|
||||
|
||||
wxGetApp().params_panel()->switch_to_object_if_has_object_configs();
|
||||
|
||||
auto has_modify = is_flush_config_modified();
|
||||
@@ -17935,7 +17979,11 @@ std::vector<size_t> Plater::load_files(const std::vector<fs::path>& input_files,
|
||||
p->m_slice_all_only_has_gcode = false;
|
||||
//BBS: wish to reset all plates stats item selected state when load a new file
|
||||
p->preview->get_canvas3d()->reset_select_plate_toolbar_selection();
|
||||
return p->load_files(input_files, strategy, ask_multi, published_out);
|
||||
bool config_loaded = false;
|
||||
std::vector<size_t> result = p->load_files(input_files, strategy, ask_multi, published_out, &config_loaded);
|
||||
if (config_loaded && !is_loading_project())
|
||||
normalize_bed_types(false);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Plater::preview_zip_archive(const boost::filesystem::path& archive_path)
|
||||
|
||||
@@ -343,6 +343,7 @@ public:
|
||||
bool is_presets_dirty() const;
|
||||
void set_plater_dirty(bool is_dirty);
|
||||
void update_project_dirty_from_presets();
|
||||
void normalize_bed_types(bool printer_setting_changed);
|
||||
int save_project_if_dirty(const wxString& reason);
|
||||
void reset_project_dirty_after_save();
|
||||
void reset_project_dirty_initial_presets();
|
||||
|
||||
@@ -88,6 +88,7 @@
|
||||
#include "slic3r/plugin/PluginConfig.hpp"
|
||||
#include "slic3r/plugin/PluginManager.hpp"
|
||||
#include "Plater.hpp"
|
||||
#include "ParamsDialog.hpp"
|
||||
#include "MainFrame.hpp"
|
||||
#include "format.hpp"
|
||||
#include "UnsavedChangesDialog.hpp"
|
||||
@@ -3305,6 +3306,11 @@ void TabPrint::toggle_options()
|
||||
|
||||
m_config_manipulation.toggle_print_fff_options(m_config, int(intptr_t(m_extruder_switch->GetClientData())), m_type < Preset::TYPE_COUNT);
|
||||
|
||||
if (m_type == Preset::TYPE_PLATE) {
|
||||
const auto &printer_config = m_preset_bundle->printers.get_edited_preset().config;
|
||||
toggle_option("curr_bed_type", m_preset_bundle->is_bbl_vendor() || printer_config.opt_bool("support_multi_bed_types"));
|
||||
}
|
||||
|
||||
Field *field = m_active_page->get_field("support_style");
|
||||
auto support_type = m_config->opt_enum<SupportType>("support_type");
|
||||
if (auto choice = dynamic_cast<Choice*>(field)) {
|
||||
@@ -7248,6 +7254,15 @@ bool Tab::select_preset(
|
||||
}
|
||||
load_current_preset();
|
||||
|
||||
// Wait for the settings dialog to close; its edits are still provisional.
|
||||
if (printer_tab && is_selected) {
|
||||
Plater *plater = wxGetApp().plater();
|
||||
ParamsDialog *dialog = wxGetApp().params_dialog();
|
||||
if (plater && !plater->is_loading_project() &&
|
||||
(!dialog || !dialog->IsShown()))
|
||||
plater->normalize_bed_types(false);
|
||||
}
|
||||
|
||||
{
|
||||
Slic3r::LifecycleEventContext ctx;
|
||||
ctx.name = preset_name;
|
||||
|
||||
@@ -71,6 +71,7 @@
|
||||
#include <unordered_map>
|
||||
|
||||
#include "MainFrame.hpp"
|
||||
#include "Plater.hpp"
|
||||
#include <boost/dll.hpp>
|
||||
#include <slic3r/GUI/Widgets/WebView.hpp>
|
||||
#include <slic3r/Utils/Http.hpp>
|
||||
@@ -1161,6 +1162,8 @@ bool GuideFrame::run()
|
||||
|
||||
app.app_config->set_legacy_datadir(false);
|
||||
app.update_mode();
|
||||
if (Plater *plater = app.plater())
|
||||
plater->normalize_bed_types(false);
|
||||
// BBS
|
||||
//app.obj_manipul()->update_ui_from_settings();
|
||||
BOOST_LOG_TRIVIAL(info) << "GuideFrame applied";
|
||||
|
||||
@@ -4,18 +4,21 @@
|
||||
#include <cstdlib>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "nlohmann/json.hpp"
|
||||
|
||||
#include "libslic3r/GCodeReader.hpp"
|
||||
#include "libslic3r/GCodeWriter.hpp"
|
||||
#include "libslic3r/GCode.hpp"
|
||||
#include "libslic3r/Model.hpp"
|
||||
#include "libslic3r/Print.hpp"
|
||||
#include "libslic3r/ModelArrange.hpp"
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#include "test_helpers.hpp"
|
||||
@@ -97,6 +100,22 @@ SCENARIO("Origin manipulation", "[GCodeWriter]") {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("A cached config slot is looked up again whenever its key changes", "[GCodeWriter]")
|
||||
{
|
||||
GCode::ConfigIndexCache cache;
|
||||
int lookups = 0;
|
||||
auto slot = [&](int filament, size_t layer, size_t generation) {
|
||||
return cache.get(filament, layer, generation, [&] { ++lookups; return filament * 100 + int(layer) * 10 + int(generation); });
|
||||
};
|
||||
REQUIRE(slot(1, 2, 3) == 123);
|
||||
REQUIRE(slot(1, 2, 3) == 123);
|
||||
REQUIRE(lookups == 1);
|
||||
REQUIRE(slot(4, 2, 3) == 423);
|
||||
REQUIRE(slot(4, 5, 3) == 453);
|
||||
REQUIRE(slot(4, 5, 6) == 456);
|
||||
REQUIRE(lookups == 4);
|
||||
}
|
||||
|
||||
// Verify that emit_machine_limits_to_gcode emits the correct max value across
|
||||
// used extruders (regression for commit b4ee665: "Emit max value of machine
|
||||
// limit among used extruders").
|
||||
@@ -1136,3 +1155,46 @@ TEST_CASE("Custom G-code motion limits are restored before generated moves", "[G
|
||||
REQUIRE(gcode.find("M204 S6000 ; adjust acceleration", custom_gcode_pos) != std::string::npos);
|
||||
REQUIRE(gcode.find("M205 X8 Y8 ; adjust jerk", custom_gcode_pos) != std::string::npos);
|
||||
}
|
||||
|
||||
TEST_CASE("Percent accelerations resolve against the option they are a percentage of", "[GCodeWriter]")
|
||||
{
|
||||
DynamicPrintConfig config = DynamicPrintConfig::full_print_config();
|
||||
config.set_deserialize_strict({
|
||||
{ "gcode_flavor", "marlin" },
|
||||
{ "machine_max_acceleration_extruding", "20000,20000" },
|
||||
{ "default_acceleration", "4000" },
|
||||
{ "initial_layer_acceleration", "0" },
|
||||
{ "outer_wall_acceleration", "3000" },
|
||||
{ "bridge_acceleration", "50%" },
|
||||
{ "sparse_infill_acceleration", "25%" },
|
||||
{ "internal_solid_infill_acceleration", "60%" },
|
||||
{ "sparse_infill_density", "20%" },
|
||||
});
|
||||
// get_abs_value_at() resolves each percentage through the ratio_over in the config definitions.
|
||||
const std::map<std::string, int> expected = {
|
||||
{ "Bridge", int(config.get_abs_value_at("bridge_acceleration", 0)) },
|
||||
{ "Sparse infill", int(config.get_abs_value_at("sparse_infill_acceleration", 0)) },
|
||||
{ "Internal solid infill", int(config.get_abs_value_at("internal_solid_infill_acceleration", 0)) },
|
||||
};
|
||||
REQUIRE(expected.at("Bridge") == 1500);
|
||||
REQUIRE(expected.at("Sparse infill") == 1000);
|
||||
REQUIRE(expected.at("Internal solid infill") == 2400);
|
||||
|
||||
std::map<std::string, std::set<int>> accelerations_by_role;
|
||||
std::string role;
|
||||
int acceleration = 0;
|
||||
GCodeReader reader;
|
||||
reader.parse_buffer(Slic3r::Test::slice({ TestMesh::bridge }, config), [&](GCodeReader &self, const GCodeReader::GCodeLine &line) {
|
||||
float value;
|
||||
if (boost::starts_with(line.raw(), ";TYPE:"))
|
||||
role = line.raw().substr(6);
|
||||
else if (line.cmd_is("M204") && line.has_value('S', value))
|
||||
acceleration = int(value);
|
||||
else if (line.extruding(self) && line.dist_XY(self) > 0)
|
||||
accelerations_by_role[role].insert(acceleration);
|
||||
});
|
||||
for (const auto &[role_name, value] : expected) {
|
||||
INFO(role_name);
|
||||
REQUIRE(accelerations_by_role[role_name] == std::set<int>{ value });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -510,6 +510,22 @@ TEST_CASE("Print config-index resolvers pick per-filament Hybrid slots", "[Print
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Regrouping or rewriting the filament maps changes the config-index generation", "[Print][H2C]")
|
||||
{
|
||||
Model model;
|
||||
model.add_object("cube", "", make_cube(20, 20, 20))->add_instance();
|
||||
Print print;
|
||||
print.apply(model, DynamicPrintConfig::full_print_config());
|
||||
|
||||
size_t generation = print.config_index_generation();
|
||||
print.set_nozzle_group_result(nullptr);
|
||||
REQUIRE(print.config_index_generation() != generation);
|
||||
|
||||
generation = print.config_index_generation();
|
||||
print.update_filament_maps_to_config({1}, {(int) nvtStandard}, {0});
|
||||
REQUIRE(print.config_index_generation() != generation);
|
||||
}
|
||||
|
||||
TEST_CASE("Re-applying an unchanged config after slicing keeps the result valid", "[Print][H2C]")
|
||||
{
|
||||
// apply() rebuilds m_config.filament_map_2 to the real per-filament slot map, while the
|
||||
|
||||
Reference in New Issue
Block a user