Merge branch 'main' into draco

This commit is contained in:
Maeyanie
2026-01-30 12:16:43 -05:00
committed by GitHub
10 changed files with 248 additions and 67 deletions

View File

@@ -2,6 +2,19 @@
@echo off @echo off
set WP=%CD% 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 @REM Detect Visual Studio version using msbuild
echo Detecting 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 Detected Visual Studio %VS_VERSION% (version %VS_MAJOR%)
echo Using CMake generator: %CMAKE_GENERATOR% echo Using CMake generator: %CMAKE_GENERATOR%
:generator_ready
@REM Pack deps @REM Pack deps
if "%1"=="pack" ( if "%1"=="pack" (
setlocal ENABLEDELAYEDEXPANSION setlocal ENABLEDELAYEDEXPANSION
@@ -96,8 +111,13 @@ echo "building deps.."
echo on echo on
REM Set minimum CMake policy to avoid <3.5 errors REM Set minimum CMake policy to avoid <3.5 errors
set CMAKE_POLICY_VERSION_MINIMUM=3.5 set CMAKE_POLICY_VERSION_MINIMUM=3.5
cmake ../ -G %CMAKE_GENERATOR% -A x64 -DCMAKE_BUILD_TYPE=%build_type% if "%USE_NINJA%"=="1" (
cmake --build . --config %build_type% --target deps -- -m 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 @echo off
if "%1"=="deps" exit /b 0 if "%1"=="deps" exit /b 0
@@ -110,8 +130,13 @@ cd %build_dir%
echo on echo on
set CMAKE_POLICY_VERSION_MINIMUM=3.5 set CMAKE_POLICY_VERSION_MINIMUM=3.5
cmake .. -G %CMAKE_GENERATOR% -A x64 -DORCA_TOOLS=ON %SIG_FLAG% -DCMAKE_BUILD_TYPE=%build_type% if "%USE_NINJA%"=="1" (
cmake --build . --config %build_type% --target ALL_BUILD -- -m 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 @echo off
cd .. cd ..
call scripts/run_gettext.bat call scripts/run_gettext.bat

View File

@@ -64,26 +64,26 @@
[hint:Precise wall] [hint:Precise wall]
text = Precise wall\nDid you know that turning on precise wall can improve precision and layer consistency? 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] [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? 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] [hint:Chamber temperature]
text = Chamber temperature\nDid you know that OrcaSlicer supports 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] [hint:Calibration]
text = Calibration\nDid you know that calibrating your printer can do wonders? Check out our beloved calibration solution in OrcaSlicer. 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] [hint:Auxiliary fan]
text = Auxiliary fan\nDid you know that OrcaSlicer supports Auxiliary part cooling 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] [hint:Air filtration]
text = Air filtration/Exhaust Fan\nDid you know that OrcaSlicer can support Air filtration/Exhaust Fan? 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] [hint:G-code window]
text = G-code window\nYou can turn on/off the G-code window by pressing the <b>C</b> key. text = G-code window\nYou can turn on/off the G-code window by pressing the <b>C</b> key.

View File

@@ -224,7 +224,9 @@ void Fill3DHoneycomb::_fill_surface_single(
// This means that the resultant infill won't be an ideal truncated octahedron, // This means that the resultant infill won't be an ideal truncated octahedron,
// but it should look better than the equivalent quantised version // 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 // ceiling to an integer value of layers per Z
// (with a little nudge in case it's close to perfect) // (with a little nudge in case it's close to perfect)
coordf_t layersPerModule = floor((gridSize * 2) / (zScale * layerHeight) + 0.05); coordf_t layersPerModule = floor((gridSize * 2) / (zScale * layerHeight) + 0.05);
@@ -300,4 +302,4 @@ void Fill3DHoneycomb::_fill_surface_single(
} }
} }
} // namespace Slic3r } // namespace Slic3r

View File

@@ -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) if (m_config.timelapse_type == TimelapseType::tlSmooth)
return {L("Smooth mode of timelapse is not supported when \"by object\" sequence is enabled.")}; return {L("Smooth mode of timelapse is not supported when \"by object\" sequence is enabled.")};

View File

@@ -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) { auto determine_bridging_angle = [](const Polygons &bridged_area, const Lines &anchors, InfillPattern dominant_pattern, double infill_direction) {
AABBTreeLines::LinesDistancer<Line> lines_tree(anchors); AABBTreeLines::LinesDistancer<Line> 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. // Check it the infill that require a fixed infill angle.
switch (dominant_pattern) { //switch (dominant_pattern) {
case ip3DHoneycomb: //case ip3DHoneycomb:
case ipCrossHatch: //case ipCrossHatch:
return (infill_direction + 45.0) * 2.0 * M_PI / 360.; // return (infill_direction + 45.0) * 2.0 * M_PI / 360.;
default: break; //default: break;
} //}
std::map<double, int> counted_directions; std::map<double, int> counted_directions;
for (const Polygon &p : bridged_area) { for (const Polygon &p : bridged_area) {

View File

@@ -260,7 +260,7 @@ std::vector<double> 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. // 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; size_t current_facet = 0;
// loop until we have at least one layer and the max slice_z reaches the object height // 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; float height = slicing_params.max_layer_height;
// Slic3r::debugf "\n Slice layer: %d\n", $id; // Slic3r::debugf "\n Slice layer: %d\n", $id;
// determine next layer height // determine next layer height
@@ -331,10 +331,10 @@ std::vector<double> layer_height_profile_adaptive(const SlicingParameters& slici
print_z += height; 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) 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)); layer_height_profile.push_back(std::clamp(z_gap, slicing_params.min_layer_height, slicing_params.max_layer_height));
} }

View File

@@ -2197,4 +2197,15 @@ bool TriangleSelector::Capsule2D::is_edge_inside_cursor(const Triangle &tr, cons
return false; return false;
} }
// ORCA: Helper to extract used states from serialized data
std::vector<EnforcerBlockerType> TriangleSelector::extract_used_facet_states(const TriangleSplittingData &data)
{
std::vector<EnforcerBlockerType> out;
for (size_t i = 0; i < data.used_states.size(); ++i) {
if (data.used_states[i])
out.push_back(static_cast<EnforcerBlockerType>(i));
}
return out;
}
} // namespace Slic3r } // namespace Slic3r

View File

@@ -679,23 +679,60 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
} }
ImGui::Separator(); 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"))) { if (m_imgui->button(m_desc.at("perform_remap"))) {
m_show_filament_remap_ui = !m_show_filament_remap_ui; m_show_remap_panel = !m_show_remap_panel;
if (m_show_filament_remap_ui) { }
// reset remap to identity on opening
m_extruder_remap.resize(m_extruders_colors.size()); if (m_show_remap_panel)
for (size_t i = 0; i < m_extruder_remap.size(); ++i) {
m_extruder_remap[i] = i; 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 ImGui::Dummy(ImVec2(0.0f, ImGui::GetFontSize() * 0.5f));
if (m_show_filament_remap_ui) {
ImGui::Separator();
render_filament_remap_ui(window_width, max_tooltip_width);
}
ImGui::Separator(); ImGui::Separator();
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(6.0f, 10.0f)); 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().obj_list()->update_info_items(obj_idx);
wxGetApp().plater()->get_partplate_list().notify_instance_update(obj_idx, 0); wxGetApp().plater()->get_partplate_list().notify_instance_update(obj_idx, 0);
m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); 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_extruders_data();
this->init_model_triangle_selectors(); 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) 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<EnforcerBlockerType> 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) 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()); 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 max_label_size = ImGui::CalcTextSize("99", NULL, true);
const ImVec2 button_size(max_label_size.x + m_imgui->scaled(0.5f), 0.f); 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 &src_col = m_extruders_colors[src]; // keep for text contrast
const ColorRGBA &dst_col = m_extruders_colors[m_extruder_remap[src]]; 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 btn_id = "##remap_src_" + std::to_string(src);
std::string pop_id = "popup_" + std::to_string(src);
ImGuiColorEditFlags flags = ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags flags = ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoInputs |
ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_NoPicker |
ImGuiColorEditFlags_NoTooltip; 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__ #ifdef __APPLE__
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImGuiWrapper::COL_ORCA); ImGui::PushStyleColor(ImGuiCol_FrameBg, ImGuiWrapper::COL_ORCA);
@@ -1047,8 +1135,9 @@ void GLGizmoMmuSegmentation::render_filament_remap_ui(float window_width, float
#endif #endif
// overlay destination number with proper contrast calculation // 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); 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 txt_sz = ImGui::CalcTextSize(dst_txt.c_str());
ImVec2 pos = ImGui::GetItemRectMin(); ImVec2 pos = ImGui::GetItemRectMin();
ImVec2 size = ImGui::GetItemRectSize(); 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), 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()); 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 // popup with possible destinations
std::string pop_id = "popup_" + std::to_string(src);
if (clicked) { if (clicked) {
// Calculate popup position centered below the current button // Calculate popup position centered below the current button
ImVec2 button_pos = ImGui::GetItemRectMin(); 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 // Apply popup styling before BeginPopup using standard Orca colors
ImGui::PushStyleVar(ImGuiStyleVar_PopupRounding, 4.0f); ImGui::PushStyleVar(ImGuiStyleVar_PopupRounding, 4.0f);
ImGui::PushStyleVar(ImGuiStyleVar_PopupBorderSize, 1.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)); 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())) { if (ImGui::BeginPopup(pop_id.c_str())) {
m_imgui->text(_L("To:"));
for (int dst = 0; dst < (int)n_extr; ++dst) { for (int dst = 0; dst < (int)n_extr; ++dst) {
const ColorRGBA &dst_col_popup = m_extruders_colors[dst]; const ColorRGBA &dst_col_popup = m_extruders_colors[dst];
ImVec4 dst_vec = ImGuiWrapper::to_ImVec4(dst_col_popup); 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); std::string dst_btn = "##dst_" + std::to_string(src) + "_" + std::to_string(dst);
// Apply same styling to destination buttons // 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) // Clean up popup styling (always pop, whether popup was open or not)
ImGui::PopStyleColor(2); // PopupBg and Border ImGui::PopStyleColor(2); // PopupBg and Border
ImGui::PopStyleVar(2); // PopupRounding and PopupBorderSize 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() void GLGizmoMmuSegmentation::remap_filament_assignments()
@@ -1193,21 +1304,46 @@ void GLGizmoMmuSegmentation::remap_filament_assignments()
ModelObject* mo = m_c->selection_info()->model_object(); ModelObject* mo = m_c->selection_info()->model_object();
if (!mo) return; if (!mo) return;
bool volume_extruder_changed = false;
for (ModelVolume* mv : mo->volumes) { for (ModelVolume* mv : mo->volumes) {
if (!mv->is_model_part()) continue; if (!mv->is_model_part()) continue;
++idx; ++idx;
TriangleSelectorGUI* ts = m_triangle_selectors[idx].get(); TriangleSelectorGUI* ts = m_triangle_selectors[idx].get();
if (!ts) continue; if (!ts) continue;
// Remap painted triangles
ts->remap_triangle_state(state_map); ts->remap_triangle_state(state_map);
ts->request_update_render_data(true); 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; updated = true;
} }
if (updated) { if (updated) {
wxGetApp().plater()->get_notification_manager()->push_notification( // ORCA: Update renderer colors if base volume extruder changed
_L("Filament remapping finished.").ToStdString()); if (volume_extruder_changed)
this->update_triangle_selectors_colors();
// ORCA: Removed "Filament remapping finished" notification to reduce UI noise.
update_model_object(); update_model_object();
m_parent.set_as_dirty(); m_parent.set_as_dirty();
// ORCA: Refresh used filaments cache
this->update_used_filaments();
} }
} }

View File

@@ -114,8 +114,10 @@ protected:
bool m_detect_geometry_edge = true; bool m_detect_geometry_edge = true;
// Filament remap feature // Filament remap feature
bool m_show_remap_panel = false;
std::vector<size_t> m_extruder_remap; // index → target extruder index std::vector<size_t> m_extruder_remap; // index → target extruder index
bool m_show_filament_remap_ui = false; // ORCA: Cache used filaments to filter UI
std::set<size_t> m_used_filaments; // Set of used filament indices (cached)
static const constexpr float CursorRadiusMin = 0.1f; // cannot be zero static const constexpr float CursorRadiusMin = 0.1f; // cannot be zero
@@ -141,6 +143,8 @@ private:
// Filament remapping methods // Filament remapping methods
void remap_filament_assignments(); void remap_filament_assignments();
void render_filament_remap_ui(float window_width, float max_tooltip_width); 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 // 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. // etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect.

22
task.md
View File

@@ -1,12 +1,12 @@
Analyze the bug that it failed to load project(3mf) from old version. 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. It failed pass below check in PresetBundle::load_config_file_config function, hence throw error.
if (config.option("extruder_variant_list")) { if (config.option("extruder_variant_list")) {
//3mf support multiple extruder logic //3mf support multiple extruder logic
size_t extruder_count = config.option<ConfigOptionFloats>("nozzle_diameter")->values.size(); size_t extruder_count = config.option<ConfigOptionFloats>("nozzle_diameter")->values.size();
extruder_variant_count = config.option<ConfigOptionStrings>("filament_extruder_variant", true)->size(); extruder_variant_count = config.option<ConfigOptionStrings>("filament_extruder_variant", true)->size();
if ((extruder_variant_count != filament_self_indice.size()) if ((extruder_variant_count != filament_self_indice.size())
|| (extruder_variant_count < num_filaments)) { || (extruder_variant_count < num_filaments)) {
assert(false); 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; 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); throw Slic3r::RuntimeError(std::string("Invalid configuration file: ") + name_or_path);
} }