diff --git a/build_release_vs.bat b/build_release_vs.bat
index d2832450b3..fc3445bb5d 100644
--- a/build_release_vs.bat
+++ b/build_release_vs.bat
@@ -2,6 +2,19 @@
@echo off
set WP=%CD%
+@REM Check for Ninja Multi-Config option (-x)
+set USE_NINJA=0
+for %%a in (%*) do (
+ if "%%a"=="-x" set USE_NINJA=1
+)
+
+if "%USE_NINJA%"=="1" (
+ echo Using Ninja Multi-Config generator
+ set CMAKE_GENERATOR="Ninja Multi-Config"
+ set VS_VERSION=Ninja
+ goto :generator_ready
+)
+
@REM Detect Visual Studio version using msbuild
echo Detecting Visual Studio version using msbuild...
@@ -50,6 +63,8 @@ if "%VS_MAJOR%"=="16" (
echo Detected Visual Studio %VS_VERSION% (version %VS_MAJOR%)
echo Using CMake generator: %CMAKE_GENERATOR%
+:generator_ready
+
@REM Pack deps
if "%1"=="pack" (
setlocal ENABLEDELAYEDEXPANSION
@@ -96,8 +111,13 @@ echo "building deps.."
echo on
REM Set minimum CMake policy to avoid <3.5 errors
set CMAKE_POLICY_VERSION_MINIMUM=3.5
-cmake ../ -G %CMAKE_GENERATOR% -A x64 -DCMAKE_BUILD_TYPE=%build_type%
-cmake --build . --config %build_type% --target deps -- -m
+if "%USE_NINJA%"=="1" (
+ cmake ../ -G %CMAKE_GENERATOR% -DCMAKE_BUILD_TYPE=%build_type%
+ cmake --build . --config %build_type% --target deps
+) else (
+ cmake ../ -G %CMAKE_GENERATOR% -A x64 -DCMAKE_BUILD_TYPE=%build_type%
+ cmake --build . --config %build_type% --target deps -- -m
+)
@echo off
if "%1"=="deps" exit /b 0
@@ -110,8 +130,13 @@ cd %build_dir%
echo on
set CMAKE_POLICY_VERSION_MINIMUM=3.5
-cmake .. -G %CMAKE_GENERATOR% -A x64 -DORCA_TOOLS=ON %SIG_FLAG% -DCMAKE_BUILD_TYPE=%build_type%
-cmake --build . --config %build_type% --target ALL_BUILD -- -m
+if "%USE_NINJA%"=="1" (
+ cmake .. -G %CMAKE_GENERATOR% -DORCA_TOOLS=ON %SIG_FLAG% -DCMAKE_BUILD_TYPE=%build_type%
+ cmake --build . --config %build_type% --target ALL_BUILD
+) else (
+ cmake .. -G %CMAKE_GENERATOR% -A x64 -DORCA_TOOLS=ON %SIG_FLAG% -DCMAKE_BUILD_TYPE=%build_type%
+ cmake --build . --config %build_type% --target ALL_BUILD -- -m
+)
@echo off
cd ..
call scripts/run_gettext.bat
diff --git a/resources/data/hints.ini b/resources/data/hints.ini
index 27e6da9e24..ff3c8e158a 100644
--- a/resources/data/hints.ini
+++ b/resources/data/hints.ini
@@ -64,26 +64,26 @@
[hint:Precise wall]
text = Precise wall\nDid you know that turning on precise wall can improve precision and layer consistency?
-documentation_link = https://github.com/OrcaSlicer/OrcaSlicer/wiki/quality_settings_precision
+documentation_link = https://www.orcaslicer.com/wiki/quality_settings_precision#precise-wall
[hint:Sandwich mode]
text = Sandwich mode\nDid you know that you can use sandwich mode (inner-outer-inner) to improve precision and layer consistency if your model doesn't have very steep overhangs?
[hint:Chamber temperature]
text = Chamber temperature\nDid you know that OrcaSlicer supports chamber temperature?
-documentation_link = https://github.com/OrcaSlicer/OrcaSlicer/wiki/Chamber-temperature
+documentation_link = https://www.orcaslicer.com/wiki/material_temperatures#print-chamber-temperature
[hint:Calibration]
text = Calibration\nDid you know that calibrating your printer can do wonders? Check out our beloved calibration solution in OrcaSlicer.
-documentation_link = https://github.com/OrcaSlicer/OrcaSlicer/wiki/Calibration
+documentation_link = https://www.orcaslicer.com/wiki/calibration
[hint:Auxiliary fan]
text = Auxiliary fan\nDid you know that OrcaSlicer supports Auxiliary part cooling fan?
-documentation_link = https://github.com/OrcaSlicer/OrcaSlicer/wiki/Auxiliary-fan
+documentation_link = https://www.orcaslicer.com/wiki/material_cooling#auxiliary-part-cooling-fan
[hint:Air filtration]
text = Air filtration/Exhaust Fan\nDid you know that OrcaSlicer can support Air filtration/Exhaust Fan?
-documentation_link = https://github.com/OrcaSlicer/OrcaSlicer/wiki/air-filtration
+documentation_link = https://www.orcaslicer.com/wiki/material_cooling#activate-air-filtration
[hint:G-code window]
text = G-code window\nYou can turn on/off the G-code window by pressing the C key.
diff --git a/src/libslic3r/Fill/Fill3DHoneycomb.cpp b/src/libslic3r/Fill/Fill3DHoneycomb.cpp
index de4aaeacd3..6429a81073 100644
--- a/src/libslic3r/Fill/Fill3DHoneycomb.cpp
+++ b/src/libslic3r/Fill/Fill3DHoneycomb.cpp
@@ -224,7 +224,9 @@ void Fill3DHoneycomb::_fill_surface_single(
// This means that the resultant infill won't be an ideal truncated octahedron,
// but it should look better than the equivalent quantised version
- coordf_t layerHeight = scale_(thickness_layers);
+ //Orca: uses a fixed layer height to avoid inconsistent bridges and variable layer height artifacts.
+ //coordf_t layerHeight = scale_(thickness_layers);
+ coordf_t layerHeight = scale_(1.0);
// ceiling to an integer value of layers per Z
// (with a little nudge in case it's close to perfect)
coordf_t layersPerModule = floor((gridSize * 2) / (zScale * layerHeight) + 0.05);
@@ -300,4 +302,4 @@ void Fill3DHoneycomb::_fill_surface_single(
}
}
-} // namespace Slic3r
\ No newline at end of file
+} // namespace Slic3r
diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp
index 56bff00055..b7ab12ee0d 100644
--- a/src/libslic3r/Print.cpp
+++ b/src/libslic3r/Print.cpp
@@ -1194,7 +1194,7 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons*
}
}
- if (m_config.print_sequence == PrintSequence::ByObject && m_objects.size() > 1) {
+ if (m_config.print_sequence == PrintSequence::ByObject && (m_objects.size() > 1 || m_objects[0]->instances().size() > 1)) {
if (m_config.timelapse_type == TimelapseType::tlSmooth)
return {L("Smooth mode of timelapse is not supported when \"by object\" sequence is enabled.")};
diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp
index 4b4237dca1..fc68fce76b 100644
--- a/src/libslic3r/PrintObject.cpp
+++ b/src/libslic3r/PrintObject.cpp
@@ -2615,13 +2615,16 @@ void PrintObject::bridge_over_infill()
auto determine_bridging_angle = [](const Polygons &bridged_area, const Lines &anchors, InfillPattern dominant_pattern, double infill_direction) {
AABBTreeLines::LinesDistancer lines_tree(anchors);
+ // Orca: since 3D Honeycomb was "fixed" by forcing coordf_t layerHeight = scale_(1.0), this is no longer needed.
+ // CorssHatch also does not need fixed angle.
+ //
// Check it the infill that require a fixed infill angle.
- switch (dominant_pattern) {
- case ip3DHoneycomb:
- case ipCrossHatch:
- return (infill_direction + 45.0) * 2.0 * M_PI / 360.;
- default: break;
- }
+ //switch (dominant_pattern) {
+ //case ip3DHoneycomb:
+ //case ipCrossHatch:
+ // return (infill_direction + 45.0) * 2.0 * M_PI / 360.;
+ //default: break;
+ //}
std::map counted_directions;
for (const Polygon &p : bridged_area) {
diff --git a/src/libslic3r/Slicing.cpp b/src/libslic3r/Slicing.cpp
index f3499b7d36..aa24b1074d 100644
--- a/src/libslic3r/Slicing.cpp
+++ b/src/libslic3r/Slicing.cpp
@@ -260,7 +260,7 @@ std::vector layer_height_profile_adaptive(const SlicingParameters& slici
// last facet visited by the as.next_layer_height() function, where the facets are sorted by their increasing Z span.
size_t current_facet = 0;
// loop until we have at least one layer and the max slice_z reaches the object height
- while (print_z + EPSILON < slicing_params.object_print_z_height()) {
+ while (print_z + EPSILON < slicing_params.object_print_z_uncompensated_height()) {
float height = slicing_params.max_layer_height;
// Slic3r::debugf "\n Slice layer: %d\n", $id;
// determine next layer height
@@ -331,10 +331,10 @@ std::vector layer_height_profile_adaptive(const SlicingParameters& slici
print_z += height;
}
- double z_gap = slicing_params.object_print_z_height() - *(layer_height_profile.end() - 2);
+ double z_gap = slicing_params.object_print_z_uncompensated_height() - *(layer_height_profile.end() - 2);
if (z_gap > 0.0)
{
- layer_height_profile.push_back(slicing_params.object_print_z_height());
+ layer_height_profile.push_back(slicing_params.object_print_z_uncompensated_height());
layer_height_profile.push_back(std::clamp(z_gap, slicing_params.min_layer_height, slicing_params.max_layer_height));
}
diff --git a/src/libslic3r/TriangleSelector.cpp b/src/libslic3r/TriangleSelector.cpp
index f09d540189..a6d19f505c 100644
--- a/src/libslic3r/TriangleSelector.cpp
+++ b/src/libslic3r/TriangleSelector.cpp
@@ -2197,4 +2197,15 @@ bool TriangleSelector::Capsule2D::is_edge_inside_cursor(const Triangle &tr, cons
return false;
}
+// ORCA: Helper to extract used states from serialized data
+std::vector TriangleSelector::extract_used_facet_states(const TriangleSplittingData &data)
+{
+ std::vector out;
+ for (size_t i = 0; i < data.used_states.size(); ++i) {
+ if (data.used_states[i])
+ out.push_back(static_cast(i));
+ }
+ return out;
+}
+
} // namespace Slic3r
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp
index d8bc90fdd6..b719bd7f54 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp
@@ -679,23 +679,60 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
}
ImGui::Separator();
+ ImGui::Dummy(ImVec2(0.0f, ImGui::GetFontSize() * 0.5f));
-
+ // ORCA: Remap filaments section (Border only, Title in border).
+ // Styled as a panel for visual grouping.
if (m_imgui->button(m_desc.at("perform_remap"))) {
- m_show_filament_remap_ui = !m_show_filament_remap_ui;
- if (m_show_filament_remap_ui) {
- // reset remap to identity on opening
- m_extruder_remap.resize(m_extruders_colors.size());
- for (size_t i = 0; i < m_extruder_remap.size(); ++i)
- m_extruder_remap[i] = i;
+ m_show_remap_panel = !m_show_remap_panel;
+ }
+
+ if (m_show_remap_panel)
+ {
+ ImDrawList* draw_list = ImGui::GetWindowDrawList();
+ std::string title = into_u8(m_desc.at("perform_remap"));
+ float available_width = ImGui::GetContentRegionAvail().x;
+
+ // ORCA: Draw Background filled (consistent with Filaments section)
+ // Use static to remember height from previous frame so we can draw it behind.
+ static float remap_panel_high = 40.0f;
+ ImVec2 p_bg_min = ImGui::GetCursorScreenPos();
+ // Adjust background position: slight negative offset to align with padding, width fills available
+ // height from static variable.
+ draw_list->AddRectFilled({p_bg_min.x - 10.0f, p_bg_min.y - 7.0f}, {p_bg_min.x + available_width + ImGui::GetFrameHeight(), p_bg_min.y + remap_panel_high}, ImGui::GetColorU32(ImGuiCol_FrameBgActive, 1.0f), 5.0f);
+
+ float start_y = ImGui::GetCursorPos().y;
+
+ // ORCA: Title as simple text - Removed as per request (redundant with button)
+ // m_imgui->text(title);
+
+ ImGui::BeginGroup();
+ // ORCA: Reduce vertical spacing within this group
+ ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(m_imgui->scaled(0.4f), m_imgui->scaled(0.2f)));
+
+ render_filament_remap_ui(window_width, max_tooltip_width);
+
+ ImGui::PopStyleVar();
+ ImGui::EndGroup();
+
+ // ORCA: Update height for next frame fill
+ remap_panel_high = ImGui::GetCursorPos().y - start_y;
+
+ // ORCA: Add Remap and Cancel buttons (outside the panel)
+ ImGui::Dummy(ImVec2(0.0f, ImGui::GetFontSize() * 0.2f));
+ if (m_imgui->button(m_desc.at("remap"))) {
+ this->remap_filament_assignments();
+ // Reset mapping to identity after apply
+ for (size_t i = 0; i < m_extruder_remap.size(); ++i) m_extruder_remap[i] = i;
+ }
+ ImGui::SameLine();
+ if (m_imgui->button(m_desc.at("cancel_remap"))) {
+ // Reset mapping to identity
+ for (size_t i = 0; i < m_extruder_remap.size(); ++i) m_extruder_remap[i] = i;
}
}
-
- // Render filament swap UI if enabled
- if (m_show_filament_remap_ui) {
- ImGui::Separator();
- render_filament_remap_ui(window_width, max_tooltip_width);
- }
+
+ ImGui::Dummy(ImVec2(0.0f, ImGui::GetFontSize() * 0.5f));
ImGui::Separator();
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(6.0f, 10.0f));
@@ -763,6 +800,9 @@ void GLGizmoMmuSegmentation::update_model_object()
wxGetApp().obj_list()->update_info_items(obj_idx);
wxGetApp().plater()->get_partplate_list().notify_instance_update(obj_idx, 0);
m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
+
+ // ORCA: Refresh cache
+ this->update_used_filaments();
}
}
@@ -825,6 +865,9 @@ void GLGizmoMmuSegmentation::update_from_model_object(bool first_update)
this->init_extruders_data();
this->init_model_triangle_selectors();
+
+ // ORCA: Refresh cache when model changes
+ this->update_used_filaments();
}
void GLGizmoMmuSegmentation::tool_changed(wchar_t old_tool, wchar_t new_tool)
@@ -1010,6 +1053,35 @@ void GLMmSegmentationGizmo3DScene::finalize_triangle_indices()
}
}
+// ORCA: Update the cache of used filaments (both base volume extruders and painted triangles)
+void GLGizmoMmuSegmentation::update_used_filaments()
+{
+ m_used_filaments.clear();
+
+ // Add base extruder IDs from volumes (unpainted areas)
+ for (int ext_id : m_volumes_extruder_idxs) {
+ // ext_id is 1-based (1 = Extruder 1), 0 = Default (usually maps to first available or object default)
+ // Here we assume 0 maps to index 0 (Extruder 1) for simplicity in display,
+ // or we should check logic in init_model_triangle_selectors where it does:
+ // int extruder_idx = (mv->extruder_id() > 0) ? mv->extruder_id() - 1 : 0;
+ int idx = (ext_id > 0) ? ext_id - 1 : 0;
+ if (idx >= 0 && idx < m_extruders_colors.size())
+ m_used_filaments.insert((size_t)idx);
+ }
+
+ // Add painted states
+ for (const auto& selector : m_triangle_selectors) {
+ if (!selector) continue;
+ TriangleSelector::TriangleSplittingData data = selector->serialize();
+ std::vector states = TriangleSelector::extract_used_facet_states(data);
+ for (EnforcerBlockerType s : states) {
+ int idx = (int)s - (int)EnforcerBlockerType::Extruder1;
+ if (idx >= 0 && idx < m_extruders_colors.size())
+ m_used_filaments.insert((size_t)idx);
+ }
+ }
+}
+
void GLGizmoMmuSegmentation::render_filament_remap_ui(float window_width, float max_tooltip_width)
{
size_t n_extr = std::min((size_t)EnforcerBlockerType::ExtruderMax, m_extruders_colors.size());
@@ -1017,18 +1089,34 @@ void GLGizmoMmuSegmentation::render_filament_remap_ui(float window_width, float
const ImVec2 max_label_size = ImGui::CalcTextSize("99", NULL, true);
const ImVec2 button_size(max_label_size.x + m_imgui->scaled(0.5f), 0.f);
- for (int src = 0; src < (int)n_extr; ++src) {
+ int displayed_count = 0;
+ const int max_per_line = 8;
+
+ // ORCA: Use m_used_filaments to show only relevant source filaments
+ for (size_t src : m_used_filaments) {
+ if (src >= n_extr) continue;
+
const ColorRGBA &src_col = m_extruders_colors[src]; // keep for text contrast
const ColorRGBA &dst_col = m_extruders_colors[m_extruder_remap[src]];
- ImVec4 col_vec = ImGuiWrapper::to_ImVec4(dst_col);
+
+ // ORCA: Button now shows the SOURCE color (per maintainer request)
+ // This keeps the UI stable until "Remap" is clicked.
+ ImVec4 col_vec = ImGuiWrapper::to_ImVec4(src_col);
- if (src) ImGui::SameLine();
+ if (displayed_count > 0 && (displayed_count % max_per_line != 0))
+ ImGui::SameLine();
+
std::string btn_id = "##remap_src_" + std::to_string(src);
+ std::string pop_id = "popup_" + std::to_string(src);
ImGuiColorEditFlags flags = ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoInputs |
ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_NoPicker |
ImGuiColorEditFlags_NoTooltip;
- if (m_selected_extruder_idx != src) flags |= ImGuiColorEditFlags_NoBorder;
+
+ // ORCA: Show border ONLY if the popup is open (visual feedback for active selection)
+ // Decoupled from m_selected_extruder_idx to prevent unwanted selection highlights.
+ if (!ImGui::IsPopupOpen(pop_id.c_str()))
+ flags |= ImGuiColorEditFlags_NoBorder;
#ifdef __APPLE__
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImGuiWrapper::COL_ORCA);
@@ -1047,8 +1135,9 @@ void GLGizmoMmuSegmentation::render_filament_remap_ui(float window_width, float
#endif
// overlay destination number with proper contrast calculation
+ // ORCA: Text still shows DESTINATION index, but contrast is against SOURCE color now.
std::string dst_txt = std::to_string(m_extruder_remap[src] + 1);
- float gray = 0.299f * dst_col.r() + 0.587f * dst_col.g() + 0.114f * dst_col.b();
+ float gray = 0.299f * src_col.r() + 0.587f * src_col.g() + 0.114f * src_col.b();
ImVec2 txt_sz = ImGui::CalcTextSize(dst_txt.c_str());
ImVec2 pos = ImGui::GetItemRectMin();
ImVec2 size = ImGui::GetItemRectSize();
@@ -1062,8 +1151,35 @@ void GLGizmoMmuSegmentation::render_filament_remap_ui(float window_width, float
ImVec2(pos.x + (size.x - txt_sz.x) * 0.5f, pos.y + (size.y - txt_sz.y) * 0.5f),
IM_COL32(0,0,0,255), dst_txt.c_str());
+ // ORCA: Show NEW color as a small triangle in the corner if remapped
+ if (src != m_extruder_remap[src]) {
+ float s = m_imgui->scaled(0.55f);
+ float offset = m_imgui->scaled(0.15f); // Inset to avoid rounded corner clipping
+ ImVec2 p = ImVec2(pos.x + offset, pos.y + offset);
+
+ // Contrast outline: White for dark backgrounds, Black for light backgrounds
+ // Use dst_col (new color) for outline contrast check? Or src_col?
+ // Usually outline is around the triangle (dst_col).
+ float dst_gray = 0.299f * dst_col.r() + 0.587f * dst_col.g() + 0.114f * dst_col.b();
+ ImU32 outline_col = (dst_gray * 255.f < 80.f) ? IM_COL32(255, 255, 255, 180) : IM_COL32(0, 0, 0, 180);
+
+ ImDrawList* draw_list = ImGui::GetWindowDrawList();
+ draw_list->AddTriangleFilled(
+ p,
+ ImVec2(p.x + s, p.y),
+ ImVec2(p.x, p.y + s),
+ ImGuiWrapper::to_ImU32(dst_col));
+
+ // ORCA: Add a thin outline for better contrast when colors are similar
+ draw_list->AddTriangle(
+ p,
+ ImVec2(p.x + s, p.y),
+ ImVec2(p.x, p.y + s),
+ outline_col,
+ 0.5f);
+ }
+
// popup with possible destinations
- std::string pop_id = "popup_" + std::to_string(src);
if (clicked) {
// Calculate popup position centered below the current button
ImVec2 button_pos = ImGui::GetItemRectMin();
@@ -1079,15 +1195,19 @@ void GLGizmoMmuSegmentation::render_filament_remap_ui(float window_width, float
// Apply popup styling before BeginPopup using standard Orca colors
ImGui::PushStyleVar(ImGuiStyleVar_PopupRounding, 4.0f);
ImGui::PushStyleVar(ImGuiStyleVar_PopupBorderSize, 1.0f);
- ImGui::PushStyleColor(ImGuiCol_PopupBg, m_is_dark_mode ? ImGuiWrapper::COL_WINDOW_BG_DARK : ImGuiWrapper::COL_WINDOW_BG);
+ // ORCA: Use FrameBgActive for consistency and to ensure visibility of white filaments
+ ImGui::PushStyleColor(ImGuiCol_PopupBg, ImGui::GetStyleColorVec4(ImGuiCol_FrameBgActive));
ImGui::PushStyleColor(ImGuiCol_Border, m_is_dark_mode ? ImVec4(0.5f, 0.5f, 0.5f, 1.0f) : ImVec4(0.6f, 0.6f, 0.6f, 1.0f));
if (ImGui::BeginPopup(pop_id.c_str())) {
+ m_imgui->text(_L("To:"));
+
for (int dst = 0; dst < (int)n_extr; ++dst) {
const ColorRGBA &dst_col_popup = m_extruders_colors[dst];
ImVec4 dst_vec = ImGuiWrapper::to_ImVec4(dst_col_popup);
- if (dst) ImGui::SameLine();
+ if (dst > 0 && (dst % max_per_line != 0))
+ ImGui::SameLine();
std::string dst_btn = "##dst_" + std::to_string(src) + "_" + std::to_string(dst);
// Apply same styling to destination buttons
@@ -1142,18 +1262,9 @@ void GLGizmoMmuSegmentation::render_filament_remap_ui(float window_width, float
// Clean up popup styling (always pop, whether popup was open or not)
ImGui::PopStyleColor(2); // PopupBg and Border
ImGui::PopStyleVar(2); // PopupRounding and PopupBorderSize
+
+ displayed_count++;
}
-
- ImGui::Dummy(ImVec2(0.0f, ImGui::GetFontSize() * 0.3f));
-
- if (m_imgui->button(m_desc.at("remap"))) {
- remap_filament_assignments();
- m_show_filament_remap_ui = false;
- }
-
- ImGui::SameLine();
- if (m_imgui->button(m_desc.at("cancel_remap")))
- m_show_filament_remap_ui = false;
}
void GLGizmoMmuSegmentation::remap_filament_assignments()
@@ -1193,21 +1304,46 @@ void GLGizmoMmuSegmentation::remap_filament_assignments()
ModelObject* mo = m_c->selection_info()->model_object();
if (!mo) return;
+ bool volume_extruder_changed = false;
+
for (ModelVolume* mv : mo->volumes) {
if (!mv->is_model_part()) continue;
++idx;
TriangleSelectorGUI* ts = m_triangle_selectors[idx].get();
if (!ts) continue;
+
+ // Remap painted triangles
ts->remap_triangle_state(state_map);
ts->request_update_render_data(true);
+
+ // 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);
+ if (idx < m_volumes_extruder_idxs.size())
+ m_volumes_extruder_idxs[idx] = (int)dest_idx + 1;
+ volume_extruder_changed = true;
+ }
+ }
+
updated = true;
}
if (updated) {
- wxGetApp().plater()->get_notification_manager()->push_notification(
- _L("Filament remapping finished.").ToStdString());
+ // ORCA: Update renderer colors if base volume extruder changed
+ if (volume_extruder_changed)
+ this->update_triangle_selectors_colors();
+
+ // ORCA: Removed "Filament remapping finished" notification to reduce UI noise.
update_model_object();
m_parent.set_as_dirty();
+
+ // ORCA: Refresh used filaments cache
+ this->update_used_filaments();
}
}
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp
index 2ab524e379..f9bcfdee12 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp
@@ -114,8 +114,10 @@ protected:
bool m_detect_geometry_edge = true;
// Filament remap feature
+ bool m_show_remap_panel = false;
std::vector m_extruder_remap; // index → target extruder index
- bool m_show_filament_remap_ui = false;
+ // ORCA: Cache used filaments to filter UI
+ std::set m_used_filaments; // Set of used filament indices (cached)
static const constexpr float CursorRadiusMin = 0.1f; // cannot be zero
@@ -141,6 +143,8 @@ private:
// Filament remapping methods
void remap_filament_assignments();
void render_filament_remap_ui(float window_width, float max_tooltip_width);
+ // ORCA: Helper to update the cache of used filaments
+ void update_used_filaments();
// This map holds all translated description texts, so they can be easily referenced during layout calculations
// etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect.
diff --git a/task.md b/task.md
index 4067c4d613..10fc9ac724 100644
--- a/task.md
+++ b/task.md
@@ -1,12 +1,12 @@
-Analyze the bug that it failed to load project(3mf) from old version.
-It failed pass below check in PresetBundle::load_config_file_config function, hence throw error.
- if (config.option("extruder_variant_list")) {
- //3mf support multiple extruder logic
- size_t extruder_count = config.option("nozzle_diameter")->values.size();
- extruder_variant_count = config.option("filament_extruder_variant", true)->size();
- if ((extruder_variant_count != filament_self_indice.size())
- || (extruder_variant_count < num_filaments)) {
- assert(false);
- BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(": invalid config file %1%, can not find suitable filament_extruder_variant or filament_self_index") % name_or_path;
- throw Slic3r::RuntimeError(std::string("Invalid configuration file: ") + name_or_path);
+Analyze the bug that it failed to load project(3mf) from old version.
+It failed pass below check in PresetBundle::load_config_file_config function, hence throw error.
+ if (config.option("extruder_variant_list")) {
+ //3mf support multiple extruder logic
+ size_t extruder_count = config.option("nozzle_diameter")->values.size();
+ extruder_variant_count = config.option("filament_extruder_variant", true)->size();
+ if ((extruder_variant_count != filament_self_indice.size())
+ || (extruder_variant_count < num_filaments)) {
+ assert(false);
+ BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(": invalid config file %1%, can not find suitable filament_extruder_variant or filament_self_index") % name_or_path;
+ throw Slic3r::RuntimeError(std::string("Invalid configuration file: ") + name_or_path);
}
\ No newline at end of file