clean up comments

This commit is contained in:
SoftFever
2026-08-23 22:43:41 +08:00
parent 7934814077
commit 2b1499a087
33 changed files with 127 additions and 193 deletions
+3 -7
View File
@@ -577,13 +577,9 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con
}
// BBS
// A filament override naming a slot that no longer exists is stale and falls back to the
// plater's value. Support and the wipe tower are additionally restricted to physical filaments:
// the engine consumes those keys directly, with no per-layer mixed resolution, so a virtual
// slot there would reach the G-code unresolved. The per-feature keys have no such restriction —
// LayerTools::extruder() and its siblings resolve a mixed slot to the physical filament chosen
// for each layer. The sidebar dropdowns already hide mixed slots for the restricted keys
// (Plater.cpp DynamicFilamentList); this reset covers values loaded from projects.
// Reset filament overrides pointing at a slot that no longer exists. Support and the wipe
// tower additionally reject mixed slots: the engine consumes those keys directly, so a virtual
// slot would reach the G-code unresolved, while the per-feature keys are resolved per layer.
static const char* physical_only_keys[] = { "support_filament", "support_interface_filament", "wipe_tower_filament" };
static const char* feature_keys[] = { "outer_wall_filament_id", "inner_wall_filament_id",
"sparse_infill_filament_id", "internal_solid_filament_id",
+1 -2
View File
@@ -131,8 +131,7 @@ void fill_gradient_ramp_rect(wxDC& dc, const wxRect& rect, const std::vector<wxC
dc.SetPen(*wxTRANSPARENT_PEN);
for (int y = 0; y < rect.height; ++y) {
// Row 0 is the top of the rect and so takes the ramp's last entry, the model's top.
// Mapping over height - 1 keeps both ends of the ramp on screen; a swatch is often
// shorter than the ramp is long, so truncating either end would be visible.
// Mapping over height - 1 puts both ends of the ramp on screen even in a short swatch.
const double t = (rect.height > 1) ? (double) (rect.height - 1 - y) / (rect.height - 1) : 0.5;
dc.SetBrush(wxBrush(ramp[static_cast<size_t>(t * (ramp.size() - 1) + 0.5)]));
dc.DrawRectangle(rect.x, rect.y + y, rect.width, 1);
+5 -7
View File
@@ -38,19 +38,17 @@ wxColour blend_n_colors(const std::vector<wxColour>& cols, const std::vector<dou
// Sample a gradient mixed filament the way the slicer builds it: t runs 0..1 over the
// model's height, the curve gives the first component's ratio at t, and the two
// components are blended at that ratio. Entry 0 is the bottom of the model, the last
// entry its top. Blending goes through blend_n_colors, so measured mixes and the
// reserved [kGradientMinRatio, kGradientMaxRatio] band are both respected — a plain
// two-endpoint fade is neither.
// components are blended at that ratio through blend_n_colors. Entry 0 is the bottom
// of the model, the last entry its top.
std::vector<wxColour> sample_gradient_ramp(const wxColour& first,
const wxColour& second,
const Slic3r::GradientCurve& curve,
int steps);
// Same ramp for a project config slot, resolving components, colours and curve (or the
// linear gradient_range fallback) from cfg. Empty unless the slot is a two-component
// gradient mixed filament, which is what gates every caller to mixed slots only.
// steps is the ramp's resolution; pass the destination's height in pixels.
// linear gradient_range fallback) from cfg. Returns empty for any slot that is not a
// two-component gradient mixed filament. steps is the ramp's resolution; pass the
// destination's height in pixels.
std::vector<wxColour> mixed_gradient_ramp(const Slic3r::DynamicPrintConfig& cfg, size_t slot, int steps);
// Fill rect with a ramp, ramp.front() along the bottom edge.
+3 -4
View File
@@ -9681,10 +9681,9 @@ void GLCanvas3D::_render_paint_toolbar() const
}
}
}
// ORCA: the loop above only produces a label for a slot whose preset is found in the preset
// collection, while the render loop below iterates extruder_num (= colour count). Pad the
// label arrays so a slot without a matching preset cannot index past them — reading a garbage
// std::string here crashes in ImGui::CalcTextSize (strlen).
// ORCA: the loop above only labels a slot whose preset was found in the preset collection,
// while the render loop below iterates extruder_num. Pad the label arrays so a slot without a
// matching preset cannot index past them; a garbage std::string crashes ImGui::CalcTextSize.
while (int(filament_text_first_line.size()) < extruder_num) {
filament_text_first_line.emplace_back();
filament_text_second_line.emplace_back();
+3 -3
View File
@@ -8906,9 +8906,9 @@ void GUI_App::load_current_presets(bool active_preset_combox/*= false*/, bool ch
auto* nozzle_diameter = edited_printer_preset.config.option<ConfigOptionFloats>("nozzle_diameter");
if (nozzle_diameter) {
// Mixed-color slots are virtual filaments kept at the tail of the list, so they have no
// nozzle of their own. Sizing to the nozzle count alone truncates them away — and this
// runs right after a project is loaded, so it would silently drop the project's mixes
// and then let update_extruder_count() strip every painted facet above the new count.
// nozzle of their own. Sizing to the nozzle count alone would silently drop the mixes of
// a just-loaded project, and update_extruder_count() would then strip the facets painted
// with them.
preset_bundle->set_num_filaments(nozzle_diameter->values.size() + preset_bundle->num_mixed_filaments());
}
}
+2 -2
View File
@@ -3234,8 +3234,8 @@ void ObjectList::merge(bool to_multipart_object)
void ObjectList::layers_editing()
{
// Height ranges give each range its own layer height, varying the mixed sub-layer heights just
// like an adaptive profile; sibling of the on_action_layersediting/ConfigManipulation warnings,
// sharing the same do-not-show-again flag.
// like an adaptive profile, so this raises the same warning as variable layer height and shares
// its do-not-show-again flag.
const auto& print_config = wxGetApp().preset_bundle->prints.get_edited_preset().config;
if (print_config.opt_bool("enable_mixed_color_sublayer")) {
if (wxGetApp().app_config->get("no_warn_mixed_sublayer_variable_layer") != "1") {
@@ -326,10 +326,9 @@ bool GLGizmoMmuSegmentation::draw_color_button(int idx, const char* id_str, cons
bool dark_tone = gradient ? (*gradient)[gradient->size() / 2].GetLuminance() < 0.51 :
(0.299f * color.r() + 0.587f * color.g() + 0.114f * color.b()) < 0.51f; // matching values used by wxWidgets with clr.GetLuminance() < 0.51
// Paint a gradient mixed filament's fade before the button and keep the button transparent, so the
// slot number and the frame below stay on top of it. The bands cannot round their corners, so the
// fade is drawn at the frame's inset and the frame masks it into the same shape a plain color slot
// gets.
// Paint a gradient mixed filament's fade before the button and keep the button transparent, so
// the slot number and the frame below stay on top of it. The bands cannot round their corners,
// so the fade is inset to the frame, which masks it into the shape a plain color slot gets.
if (gradient) {
ImGuiWrapper::draw_gradient_ramp(draw_list, {pos.x + frame_inset * scale, pos.y + frame_inset * scale},
{pos.x + size.x - frame_inset * scale, pos.y + size.y - frame_inset * scale}, *gradient);
@@ -778,7 +777,7 @@ void GLGizmoMmuSegmentation::update_triangle_selectors_colors()
TriangleSelectorPatch* selector = dynamic_cast<TriangleSelectorPatch*>(m_triangle_selectors[i].get());
int extruder_idx = m_volumes_extruder_idxs[i];
int extruder_color_idx = std::max(0, extruder_idx - 1);
// As above: a mixed-color slot can index past the physical colour list.
// A mixed-color slot can index past the physical colour list; fall back to the first colour.
if (extruder_color_idx >= (int)m_extruders_colors.size())
extruder_color_idx = 0;
std::vector<ColorRGBA> ebt_colors;
@@ -142,7 +142,7 @@ private:
// ORCA
bool draw_color_button(int idx, const char* id_str, const ColorRGBA& color, ColorRGBA& map_color, bool active, float scale);
// Gradient ramp of a filament slot, or nullptr when the slot is a plain single color
// filament, so callers can index into what they get back freely.
// filament. A non-null result is never empty.
const std::vector<wxColour>* gradient_of(int idx) const
{
return idx >= 0 && idx < (int) m_gradient_ramps.size() && !m_gradient_ramps[idx].empty() ? &m_gradient_ramps[idx] : nullptr;
+2 -2
View File
@@ -998,8 +998,8 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt)
keyCode = keyCode- WXK_NUMPAD0+'0';
}
if (keyCode >= '0' && keyCode <= '9') {
// The paint palette now reaches EXTRUDERS_LIMIT (mixed-color filaments share
// the same slots), so any leading digit that can start a valid two-digit
// The paint palette reaches EXTRUDERS_LIMIT slots (mixed-color filaments take
// ordinary slots too), so any leading digit that can start a valid two-digit
// number waits briefly for a second one.
const int digit = keyCode - '0';
const int shortcut_max = int(GLGizmoMmuSegmentation::EXTRUDERS_LIMIT);
+11 -19
View File
@@ -19,7 +19,7 @@ namespace GUI {
wxDEFINE_EVENT(wxEVT_GRADIENT_CURVE_CHANGED, wxCommandEvent);
namespace {
// Layout (Figma "Property 1=Default", 214.06 x 179.63 px reference).
// Layout ratios of the plot rect within the widget, taken from a 214 x 180 px reference drawing.
// Plot rect occupies the upper-left region; right + bottom margins host axis arrows / labels.
constexpr double kPlotLeftRatio = 0.0316;
constexpr double kPlotRightRatio = 0.6766;
@@ -37,7 +37,7 @@ constexpr int kStrokeAxis = 2; // axis line width (px, no DPI scaling -
constexpr int kAxisArrowHalf = 5; // half-base of the axis arrow triangle (DIP)
constexpr int kAxisArrowLen = 10; // length of the axis arrow triangle (DIP)
// Light-mode design tokens from Figma. Resolved through StateColor::darkModeColorFor()
// Light-mode design tokens. Resolved through StateColor::darkModeColorFor()
// at paint time so the editor follows the app theme (#EEEEEE -> #4C4C55, #6B6B6B ->
// #818183, #262E30 -> #EFEFF0, *wxWHITE -> #2D2D31). Don't read these directly in paint;
// always go through the resolved locals declared at the top of on_paint().
@@ -46,11 +46,9 @@ const wxColour kAxisColor (107, 107, 107); // #6B6B6B grey 700
const wxColour kLabelMuted (107, 107, 107); // #6B6B6B grey 700
const wxColour kLabelStrong ( 38, 46, 48); // #262E30 grey 900
// LAB (DeltaE76) threshold for "curve color is too close to the background". Below this
// we paint a subtle axis-color outline so the curve doesn't visually vanish; above this
// we draw the curve plain. ~15 is "perceptible but still close", looser than the strict
// 5.0 used by FlushPredict::is_similar_color but loose enough that a pastel pink on white
// or a charcoal on #2B2B2B still triggers an outline.
// LAB (DeltaE76) threshold for "curve color is too close to the background": below it the curve
// gets a subtle outline so it does not visually vanish, otherwise it is drawn plain. Looser than
// the 5.0 of FlushPredict::is_similar_color, so a pastel pink on white still gets an outline.
constexpr float kBgSimilarThreshold = 15.0f;
constexpr int kOutlineExtraDip = 2;
} // namespace
@@ -65,8 +63,6 @@ GradientCurveEditor::GradientCurveEditor(wxWindow* parent,
SetBackgroundStyle(wxBG_STYLE_PAINT);
SetBackgroundColour(wxGetApp().get_window_default_clr());
// Wide enough so the X-axis "Material Ratio" label fits past the arrow tip without overlap.
// 260 (was 240): adds room for the "Material Ratio" label that gets shifted right by the
// longer axis arrow; the hosting MixedFilamentDialog grows to 470 DIP to accommodate.
SetMinSize(FromDIP(wxSize(260, 200)));
reset_to_linear(0.10, 0.90);
@@ -456,10 +452,9 @@ void GradientCurveEditor::on_paint(wxPaintEvent& /*evt*/)
return poly;
};
// Only the geometry goes through the graphics context: dc.DrawLines() takes integer
// wxPoint and would quantize the curve back to whole pixels. The pen is still set on
// the dc, which forwards it to this same context while keeping the dc's own cached
// state in sync, so later dc drawing does not inherit the curve's pen.
// Only the geometry goes through the graphics context: dc.DrawLines() takes integer wxPoint
// and would quantize the curve back to whole pixels. The pen is still set on the dc, which
// forwards it here while keeping its own cached state in sync for later dc drawing.
auto draw_polyline = [&](const std::vector<wxPoint2DDouble>& poly, const wxColour& col, int stroke_dip) {
dc.SetPen(wxPen(col, FromDIP(stroke_dip)));
gc->StrokeLines(poly.size(), poly.data());
@@ -552,12 +547,9 @@ void GradientCurveEditor::on_left_down(wxMouseEvent& evt)
// 4) Selected curve line body hit -> insert a new anchor at cursor x (snapped
// to the current smooth curve so the initial click is visually invisible)
// and immediately enter Anchor drag mode. PS Curves style: the drag-bend
// interaction has no separate "bend without anchor" mode; pressing and
// dragging on the line is equivalent to clicking to add then dragging the
// fresh anchor. Trades the previous (failed) "no anchor on drag" promise
// for genuine cursor tracking, since a single cubic between two existing
// anchors mathematically cannot put its peak under an off-center cursor.
// and immediately enter Anchor drag mode. Bending the segment without
// inserting an anchor is not an option: a single cubic between two existing
// anchors cannot put its peak under an off-center cursor.
double nx = 0, dummy = 0;
px_to_data(pos.x, pos.y, nx, dummy);
if (nx <= 0.0 || nx >= 1.0 || seg < 0) {
+6 -8
View File
@@ -894,9 +894,9 @@ wxBoxSizer* MixedFilamentDialog::create_ratio_slider()
on_ratio_changed(std::max(MIN_COMPONENT_RATIO, std::min(100 - MIN_COMPONENT_RATIO, new_ratio)));
});
// Release whenever the capture is held, not only when the drag flag is set:
// the flag can be cleared behind our back, and a capture that outlives the
// widget wedges mouse input for the whole application.
// Key the release off the capture itself, not off the drag flag: the two can fall out of
// sync (a lost capture clears the flag on its own), and a capture that outlives the widget
// wedges mouse input for the whole application.
m_ratio_bar->Bind(wxEVT_LEFT_UP, [this](wxMouseEvent&) {
m_ratio_dragging = false;
if (m_ratio_bar->HasCapture())
@@ -1492,11 +1492,9 @@ void MixedFilamentDialog::on_ratio_changed(int new_ratio_a)
void MixedFilamentDialog::on_gradient_toggled()
{
// Orca: the engine only produces a gradient when the print profile's
// "enable_mixed_color_sublayer" option is on (ToolOrdering::resolve_mixed_filaments
// falls back to whole-layer round-robin without it, and BBS leaves users to find the
// option themselves). Offer to switch it on so the gradient the user just enabled
// actually shows up in the sliced result. Keep this block on future BBS syncs.
// Orca: a gradient is only sliced when the print profile's "enable_mixed_color_sublayer"
// option is on; without it ToolOrdering picks a single component per whole layer. Offer to
// turn the option on instead of silently ignoring the gradient the user just enabled.
bool checked = m_chk_gradient->GetValue();
if (checked) {
+2 -7
View File
@@ -2048,13 +2048,8 @@ bool PartPlate::check_tpu_printable_status(const DynamicPrintConfig & config, co
}
// A mixed-color filament alternates between its components constantly. On a single-nozzle
// printer every one of those switches is a full filament change plus a purge, so warn the
// user before they commit to it. Printers with more than one nozzle can keep the components
// loaded simultaneously and are not affected.
//
// BBS additionally excludes its H2C/H2D/X2D models by name; those are multi-nozzle machines
// already ruled out by the nozzle_diameter test above, so the name check is dropped here
// rather than carried over as a Bambu-specific special case.
// printer every one of those switches is a full filament change plus a purge, so warn before
// slicing. Multi-nozzle printers keep the components loaded at once and are not affected.
bool PartPlate::check_single_extruder_mixed_filament_risk(const DynamicPrintConfig &config, std::string &warning_text) const
{
warning_text.clear();
+1 -2
View File
@@ -473,8 +473,7 @@ PlateSettingsDialog::PlateSettingsDialog(wxWindow* parent, const wxString& title
m_sizer_main->Add(m_other_layers_seq_panel, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(30));
// A mixed-color slot resolves to a different physical filament per layer, so a user-defined
// filament order cannot be honoured. Disable the choice and say why. BBS puts this warning
// inside its button sizer; Orca builds the buttons with DialogButtons, so it gets its own row.
// filament order cannot be honoured; grey out the choice and explain that in the dialog.
{
auto &proj_cfg = wxGetApp().preset_bundle->project_config;
auto *is_mixed_opt = proj_cfg.option<ConfigOptionBools>("filament_is_mixed");
+14 -22
View File
@@ -3876,10 +3876,8 @@ void Sidebar::change_top_border_for_mode_sizer(bool increase_border)
// ---- Mixed-color filament sidebar support ----
// Ported from BambuStudio's 混色耗材 feature. BBS hosts these widgets in an
// m_filament_area_wrapper that Orca's sidebar has no counterpart for, so the mixed
// section is parented to p->scrolled and sized with Orca's own row-height preference
// (filaments_area_preferred_count) rather than BBS's fixed 3-row / 12-filament cap.
// The mixed rows get their own scroll area, capped by Orca's filaments_area_preferred_count
// row budget rather than BBS's fixed 3-row / 12-filament limit.
void Sidebar::recalc_filament_scroll_sizes()
{
if (!p->m_mixed_scroll_area || !p->m_mixed_scroll_area->GetSizer())
@@ -4102,8 +4100,8 @@ void Sidebar::update_mixed_filament_list()
unsigned int mix_num = (unsigned int)(cfg_idx + 1);
// The swatch fades bottom to top over the model's height, sampled the same way
// the slicer builds the sublayers, so it matches the editor's Effect Preview. It
// comes back empty for every slot that is not a two component gradient mix.
// the slicer builds the sublayers, so it matches the editor's Effect Preview. The
// ramp comes back empty for every slot that is not a two component gradient mix.
const int swatch_sz = FromDIP(20);
const std::vector<wxColour> gradient_ramp = mixed_gradient_ramp(project_config, cfg_idx, swatch_sz);
@@ -4642,9 +4640,8 @@ static bool create_mixed_filament_from_result(
multi_colour_opt->values[new_idx] = mixed_color;
}
// set_num_filaments() above is what grows these parallel arrays. Guard the writes anyway,
// matching the gradient writes below, so a sizing bug degrades into a no-op rather than a
// heap overwrite.
// set_num_filaments() above already grows these parallel arrays; the writes are still
// size-guarded so a sizing bug degrades into a no-op rather than a heap overwrite.
{
auto* is_mixed_opt = project_config.option<ConfigOptionBools>("filament_is_mixed");
while (is_mixed_opt->values.size() <= new_idx) is_mixed_opt->values.push_back(false);
@@ -14071,11 +14068,9 @@ bool Plater::priv::can_layers_editing() const
void Plater::priv::on_action_layersediting(SimpleEvent&)
{
// Sub-layer splitting divides each layer by the mix ratio, so an adaptive layer profile makes
// those sub-layer heights vary and degrades the blend. ConfigManipulation warns when the
// option is switched on with a variable profile already present; this is the other direction,
// warning when variable layer editing is switched on while the option is active. All three
// sites (with ObjectList::layers_editing for height ranges) honour the same do-not-show-again flag.
// Sub-layer splitting divides each layer by the mix ratio, so a variable layer height profile
// makes those sub-layer heights uneven and degrades the blend. ConfigManipulation warns for the
// opposite order, when the option is switched on while a variable profile already exists.
if (!view3D->is_layers_editing_enabled()) {
const auto& print_config = wxGetApp().preset_bundle->prints.get_edited_preset().config;
if (print_config.opt_bool("enable_mixed_color_sublayer")) {
@@ -19988,14 +19983,11 @@ std::vector<std::string> Plater::get_filament_color_render_type() const
const std::vector<std::vector<wxColour>>& Plater::get_filament_gradient_ramps() const
{
// Sampling a ramp walks the measured-blend recipe table once per step, and the paint toolbar
// asks for the ramps on every rendered frame, so they are cached against the config values
// they are built from and resampled only when one of those actually changes.
//
// The cache cannot live on the Plater: the extruder icons ask for the ramps from inside
// MenuFactory::init(), which runs while this Plater is still being constructed, so `this` is
// not usable yet. Everything the ramps are built from is global anyway, and there is one
// Plater per process, which is the same reasoning behind the icons' own static BitmapCache.
// Sampling a ramp walks the measured-blend recipe table once per step and the paint toolbar
// asks for the ramps every rendered frame, so they are cached against the config values they
// are built from. The cache is static rather than a Plater member because the extruder icons
// ask for the ramps from MenuFactory::init(), which runs while this Plater is still inside its
// own constructor, so wxGetApp().plater_ is not assigned yet.
static std::string s_ramps_key;
static std::vector<std::vector<wxColour>> s_ramps;
+2 -2
View File
@@ -2577,7 +2577,7 @@ void SyncAmsInfoDialog::reset_and_sync_ams_list()
m_filaments.clear();
// Mixed-color slots are virtual: they never occupy a tray, so they must not appear as
// AMS sync targets. Look the flags up once and skip those slots in the loop below.
// AMS sync targets.
auto* is_mixed_opt = preset_bundle->project_config.option<ConfigOptionBools>("filament_is_mixed");
bool use_double_extruder = get_is_double_extruder();
@@ -2801,7 +2801,7 @@ void SyncAmsInfoDialog::generate_override_fix_ams_list()
m_fix_filaments.clear();
// Mixed-color slots are virtual: they never occupy a tray, so they must not appear as
// AMS sync targets. Look the flags up once and skip those slots in the loop below.
// AMS sync targets.
auto* is_mixed_opt = preset_bundle->project_config.option<ConfigOptionBools>("filament_is_mixed");
bool use_double_extruder = get_is_double_extruder();
+6 -9
View File
@@ -1368,11 +1368,9 @@ void TexturePreviewCanvas::ensure_gl_ready()
{
if (m_gl_initialized) return;
// BBS loads GL entry points here with GLEW. Orca uses glad and centralises loading in
// OpenGLManager, which has already run by the time any canvas is realized, so just
// verify the loader is up and drain any stale error state.
// glad leaves unresolved entry points as null pointers, so this is a cheap guard against
// painting before OpenGLManager::init_gl() has run.
// BBS loads the GL entry points here with GLEW; Orca loads them centrally in
// OpenGLManager, so only check that this has already happened (glad leaves unresolved
// entry points null) and drain any stale error state.
if (glGetString == nullptr) {
BOOST_LOG_TRIVIAL(error) << "TexturePreviewCanvas: OpenGL functions are not loaded yet";
return;
@@ -2436,10 +2434,9 @@ void TextureImportDialog::start_computation(bool auto_color, bool initial)
settings.target_colors_num = auto_color ? 0 : (size_t)m_param_color_count;
settings.smooth_weight = m_param_smooth / 10.0;
settings.mesh_repair_decision = m_mesh_repair_decision;
// BBS repairs the mesh through the Windows 3D SDK, which only exists on Windows and only
// when the SDK is present at build time. Orca already ships a CGAL-based repair
// (MeshBoolean::cgal::repair) that works on all three platforms, so use that instead —
// this makes the repair path available on Linux and macOS too.
// BBS repairs the mesh through the Windows 3D SDK, which is only available on Windows
// builds that ship the SDK. Orca's CGAL-based repair (MeshBoolean::cgal::repair) works
// on all three platforms, so use that instead.
settings.mesh_repair_callback = [](const indexed_triangle_set& mesh,
indexed_triangle_set& repaired_mesh,
std::function<void(const char*, unsigned)> progress_callback,
+1 -2
View File
@@ -360,8 +360,7 @@ void DropDown::render(wxDC &dc)
for (int i = 0; i < items.size(); ++i) {
auto &item = items[i];
int states2 = states;
// Dimmed items stay selectable but render greyed out (used by the mixed-filament
// dialog to show components that are already consumed by another mix).
// Dimmed items render greyed out but stay selectable, so they cannot reuse the disabled state.
bool is_dimmed = (item.style & DD_ITEM_STYLE_DIMMED) != 0;
if ((item.style & DD_ITEM_STYLE_DISABLED) != 0)
states2 &= ~StateColor::Enabled;
+3 -4
View File
@@ -261,10 +261,9 @@ static std::vector<float> MatrixFlatten(const WipingDialog::VolumeMatrix& matrix
return vec;
}
// Mixed-color slots are virtual: they are never loaded into a tray and so have no flushing
// volumes of their own. The dialog therefore shows only the physical filaments, which means
// converting between the full config matrix (indexed by config slot) and a dense physical
// sub-matrix (indexed by row/column in the table).
// Mixed-color slots are virtual and have no flushing volumes, so the dialog shows only the
// physical filaments. That means converting between the full config matrix (indexed by config
// slot) and a dense physical sub-matrix (indexed by row/column in the table).
static std::vector<double> extract_physical_sub_matrix(
const std::vector<double>& full_matrix, size_t full_n,
const std::vector<size_t>& indices)