mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-01 14:27:00 +00:00
Fixes from Full spectrum port
https://github.com/OrcaSlicer/OrcaSlicer/pull/14383
This commit is contained in:
@@ -3795,7 +3795,7 @@ unsigned int PresetBundle::sync_ams_list(std::vector<std::pair<DynamicPrintConfi
|
||||
return -1;
|
||||
};
|
||||
for (size_t i = 0; i < need_append_colors.size(); i++){
|
||||
if (exist_filament_presets.size() >= size_t(EnforcerBlockerType::ExtruderMax)){
|
||||
if (exist_filament_presets.size() >= MAXIMUM_AMS_SYNC_FILAMENT_NUMBER){
|
||||
break;
|
||||
}
|
||||
auto idx = get_idx_in_array(exist_filament_presets, exist_colors, need_append_colors[i].filament_preset, need_append_colors[i].filament_color);
|
||||
|
||||
@@ -1931,7 +1931,8 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
|
||||
for (const ModelVolume *volume : volumes) {
|
||||
const std::vector<bool> &volume_used_facet_states = volume->mmu_segmentation_facets.get_data().used_states;
|
||||
|
||||
assert(volume_used_facet_states.size() == used_facet_states.size());
|
||||
// Sizes may legitimately differ: paint data stored before the state range was
|
||||
// extended carries a shorter used_states vector. Merge over the common prefix.
|
||||
for (size_t state_idx = 0; state_idx < std::min(volume_used_facet_states.size(), used_facet_states.size()); ++state_idx)
|
||||
used_facet_states[state_idx] |= volume_used_facet_states[state_idx];
|
||||
}
|
||||
|
||||
@@ -1736,13 +1736,22 @@ TriangleSelector::TriangleSplittingData TriangleSelector::serialize() const {
|
||||
data.used_states[n] = true;
|
||||
|
||||
if (n >= 3) {
|
||||
assert(n <= 16);
|
||||
if (n <= 16) {
|
||||
// Store "11" plus 4 bits of (n-3).
|
||||
data.bitstream.insert(data.bitstream.end(), { true, true });
|
||||
n -= 3;
|
||||
assert(n <= int(EnforcerBlockerType::ExtruderMax));
|
||||
// Store "11" plus 4 bits of (n-3), which covers states 3..17. State 18 and
|
||||
// above set that nibble to 0b1111 and store (n-18) in a second nibble. This is
|
||||
// the encoding the CONST_FILAMENTS table in Model.cpp already writes for
|
||||
// colored mesh imports.
|
||||
data.bitstream.insert(data.bitstream.end(), { true, true });
|
||||
auto &bitstream = data.bitstream;
|
||||
auto push_nibble = [&bitstream](int value) {
|
||||
for (size_t bit_idx = 0; bit_idx < 4; ++bit_idx)
|
||||
data.bitstream.push_back(n & (uint64_t(0b0001) << bit_idx));
|
||||
bitstream.push_back(value & (uint64_t(0b0001) << bit_idx));
|
||||
};
|
||||
if (n <= 17) {
|
||||
push_nibble(n - 3);
|
||||
} else {
|
||||
push_nibble(0b1111);
|
||||
push_nibble(n - 18);
|
||||
}
|
||||
} else {
|
||||
// Simple case, compatible with PrusaSlicer 2.3.1 and older for storing paint on supports and seams.
|
||||
@@ -1810,6 +1819,12 @@ void TriangleSelector::deserialize(const TriangleSplittingData &data,
|
||||
n |= data.bitstream[ibit ++] << i;
|
||||
return n;
|
||||
};
|
||||
// Decode a leaf state stored behind the "11" prefix: one nibble of (state-3) for states
|
||||
// 3..17, or 0b1111 followed by a nibble of (state-18) above that.
|
||||
auto decode_leaf_state = [&next_nibble]() {
|
||||
const int nibble = next_nibble();
|
||||
return EnforcerBlockerType(nibble == 0b1111 ? next_nibble() + 18 : nibble + 3);
|
||||
};
|
||||
|
||||
parents.clear();
|
||||
while (true) {
|
||||
@@ -1818,8 +1833,8 @@ void TriangleSelector::deserialize(const TriangleSplittingData &data,
|
||||
int num_of_split_sides = code & 0b11;
|
||||
int num_of_children = num_of_split_sides == 0 ? 0 : num_of_split_sides + 1;
|
||||
bool is_split = num_of_children != 0;
|
||||
// Only valid if not is_split. Value of the second nibble was subtracted by 3, so it is added back.
|
||||
auto state = is_split ? EnforcerBlockerType::NONE : EnforcerBlockerType((code & 0b1100) == 0b1100 ? next_nibble() + 3 : code >> 2);
|
||||
// Only valid if not is_split.
|
||||
auto state = is_split ? EnforcerBlockerType::NONE : ((code & 0b1100) == 0b1100 ? decode_leaf_state() : EnforcerBlockerType(code >> 2));
|
||||
|
||||
// BBS
|
||||
if (state == to_delete_filament)
|
||||
@@ -1916,7 +1931,14 @@ void TriangleSelector::TriangleSplittingData::update_used_states(const size_t bi
|
||||
if (const bool is_split = (code & 0b11) != 0; is_split)
|
||||
continue;
|
||||
|
||||
const uint8_t facet_state = (code & 0b1100) == 0b1100 ? read_next_nibble() + 3 : code >> 2;
|
||||
uint8_t facet_state;
|
||||
if ((code & 0b1100) == 0b1100) {
|
||||
// Leaf behind the "11" prefix: one nibble of (state-3), or 0b1111 + (state-18).
|
||||
const uint8_t nibble = read_next_nibble();
|
||||
facet_state = nibble == 0b1111 ? uint8_t(read_next_nibble() + 18) : uint8_t(nibble + 3);
|
||||
} else {
|
||||
facet_state = code >> 2;
|
||||
}
|
||||
assert(facet_state < this->used_states.size());
|
||||
if (facet_state >= this->used_states.size())
|
||||
continue;
|
||||
@@ -1946,9 +1968,13 @@ bool TriangleSelector::has_facets(const TriangleSplittingData &data, const Enfor
|
||||
auto num_children_or_state = [&next_nibble]() -> int {
|
||||
int code = next_nibble();
|
||||
int num_of_split_sides = code & 0b11;
|
||||
return num_of_split_sides == 0 ?
|
||||
((code & 0b1100) == 0b1100 ? next_nibble() + 3 : code >> 2) :
|
||||
- num_of_split_sides - 1;
|
||||
if (num_of_split_sides != 0)
|
||||
return - num_of_split_sides - 1;
|
||||
if ((code & 0b1100) != 0b1100)
|
||||
return code >> 2;
|
||||
// Leaf behind the "11" prefix: one nibble of (state-3), or 0b1111 + (state-18).
|
||||
const int nibble = next_nibble();
|
||||
return nibble == 0b1111 ? next_nibble() + 18 : nibble + 3;
|
||||
};
|
||||
|
||||
int state = num_children_or_state();
|
||||
|
||||
@@ -17,7 +17,9 @@ enum class EnforcerBlockerType : int8_t {
|
||||
BLOCKER = 2,
|
||||
// For the fuzzy skin, we use just two values (NONE and FUZZY_SKIN).
|
||||
FUZZY_SKIN = ENFORCER,
|
||||
// Maximum is 15. The value is serialized in TriangleSelector into 6 bits using a 2 bit prefix code.
|
||||
// States 3..17 are serialized into 6 bits using a 2 bit prefix code; states 18 and above use
|
||||
// one additional nibble (see TriangleSelector::serialize). ExtruderMax matches the last entry
|
||||
// of CONST_FILAMENTS in Model.cpp, which encodes the same range for colored mesh imports.
|
||||
Extruder1 = ENFORCER,
|
||||
Extruder2 = BLOCKER,
|
||||
Extruder3,
|
||||
@@ -34,7 +36,23 @@ enum class EnforcerBlockerType : int8_t {
|
||||
Extruder14,
|
||||
Extruder15,
|
||||
Extruder16,
|
||||
ExtruderMax = Extruder16
|
||||
Extruder17,
|
||||
Extruder18,
|
||||
Extruder19,
|
||||
Extruder20,
|
||||
Extruder21,
|
||||
Extruder22,
|
||||
Extruder23,
|
||||
Extruder24,
|
||||
Extruder25,
|
||||
Extruder26,
|
||||
Extruder27,
|
||||
Extruder28,
|
||||
Extruder29,
|
||||
Extruder30,
|
||||
Extruder31,
|
||||
Extruder32,
|
||||
ExtruderMax = Extruder32
|
||||
};
|
||||
|
||||
// Type alias for the state mapping array to improve code readability
|
||||
|
||||
@@ -64,6 +64,12 @@ static constexpr double LARGE_BED_THRESHOLD = 2147;
|
||||
// Orca: maximum number of extruders is 64. For SEMM printers, it defines maximum filament number.
|
||||
static constexpr size_t MAXIMUM_EXTRUDER_NUMBER = 64;
|
||||
|
||||
// Orca: how many filament slots syncing an AMS setup may create. This used to follow
|
||||
// EnforcerBlockerType::ExtruderMax, which capped it at the number of paintable filaments; that
|
||||
// limit has since been raised to 32, so the value is pinned here to keep AMS sync behaving as
|
||||
// before for projects that use no mixed-colour filaments.
|
||||
static constexpr size_t MAXIMUM_AMS_SYNC_FILAMENT_NUMBER = 16;
|
||||
|
||||
// Orca: maximum line width is 5 times the nozzle diameter
|
||||
static constexpr float MAX_LINE_WIDTH_MULTIPLIER = 5;
|
||||
|
||||
|
||||
@@ -682,13 +682,19 @@ void GLVolume::simple_render(GLShaderProgram* shader, ModelObjectPtrs& model_obj
|
||||
if (shader) {
|
||||
if (idx == 0) {
|
||||
int extruder_id = model_volume->extruder_id();
|
||||
//to make black not too hard too see
|
||||
ColorRGBA new_color = adjust_color_for_rendering(extruder_colors[extruder_id - 1]);
|
||||
if (ban_light) {
|
||||
new_color[3] = (255 - (extruder_id - 1))/255.0f;
|
||||
// ORCA: extruder_id may be 0 (unset) or point past the colour list after a
|
||||
// filament is deleted/remapped, so clamp the index instead of reading out of
|
||||
// bounds.
|
||||
if (!extruder_colors.empty()) {
|
||||
int color_idx = std::clamp(extruder_id - 1, 0, int(extruder_colors.size()) - 1);
|
||||
//to make black not too hard too see
|
||||
ColorRGBA new_color = adjust_color_for_rendering(extruder_colors[color_idx]);
|
||||
if (ban_light) {
|
||||
new_color[3] = (255 - color_idx)/255.0f;
|
||||
}
|
||||
m.set_color(new_color);
|
||||
// shader->set_uniform("uniform_color", new_color);
|
||||
}
|
||||
m.set_color(new_color);
|
||||
// shader->set_uniform("uniform_color", new_color);
|
||||
}
|
||||
else {
|
||||
if (idx <= extruder_colors.size()) {
|
||||
|
||||
@@ -577,36 +577,40 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con
|
||||
}
|
||||
|
||||
// BBS
|
||||
// A per-role filament override must name a real, physical filament. Out-of-range values are
|
||||
// stale; a mixed-color slot is virtual and cannot be driven directly by a role override, so
|
||||
// both are reset to 0 ("inherit the object's filament"). The object's own extruder assignment
|
||||
// is what legitimately carries a mixed slot. Orca splits BBS's wall/solid_infill roles into
|
||||
// six keys, so all of them are checked here.
|
||||
static const char* keys[] = { "support_filament", "support_interface_filament",
|
||||
"outer_wall_filament_id", "inner_wall_filament_id",
|
||||
"sparse_infill_filament_id", "internal_solid_filament_id",
|
||||
"top_surface_filament_id", "bottom_surface_filament_id" };
|
||||
for (int i = 0; i < sizeof(keys) / sizeof(keys[0]); i++) {
|
||||
std::string key = std::string(keys[i]);
|
||||
// A filament override naming a slot that no longer exists is stale and falls back to the
|
||||
// plater's value. Support is additionally restricted to physical filaments: the support paths
|
||||
// (ToolOrdering::collect_extruders, Print::validate) consume support_filament 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.
|
||||
static const char* support_keys[] = { "support_filament", "support_interface_filament" };
|
||||
static const char* feature_keys[] = { "outer_wall_filament_id", "inner_wall_filament_id",
|
||||
"sparse_infill_filament_id", "internal_solid_filament_id",
|
||||
"top_surface_filament_id", "bottom_surface_filament_id" };
|
||||
auto reset_invalid_filament = [this, config, filament_cnt](const char* key, bool allow_mixed) {
|
||||
auto* opt = dynamic_cast<ConfigOptionInt*>(config->option(key, false));
|
||||
if (opt != nullptr) {
|
||||
int val = opt->getInt();
|
||||
bool out_of_range = val > filament_cnt;
|
||||
bool is_mixed = (val > 0 && val <= filament_cnt &&
|
||||
wxGetApp().preset_bundle->is_mixed_filament(val - 1));
|
||||
if (out_of_range || is_mixed) {
|
||||
DynamicPrintConfig new_conf = *config;
|
||||
int new_value = 0;
|
||||
if (out_of_range) {
|
||||
const DynamicPrintConfig *conf_temp = wxGetApp().plater()->config();
|
||||
if (conf_temp != nullptr && conf_temp->has(key))
|
||||
new_value = conf_temp->opt_int(key);
|
||||
}
|
||||
new_conf.set_key_value(key, new ConfigOptionInt(new_value));
|
||||
apply(config, &new_conf);
|
||||
}
|
||||
if (opt == nullptr)
|
||||
return;
|
||||
const int val = opt->getInt();
|
||||
const bool out_of_range = val > filament_cnt;
|
||||
const bool is_mixed = !allow_mixed && val > 0 && val <= filament_cnt &&
|
||||
wxGetApp().preset_bundle->is_mixed_filament(val - 1);
|
||||
if (!out_of_range && !is_mixed)
|
||||
return;
|
||||
DynamicPrintConfig new_conf = *config;
|
||||
int new_value = 0;
|
||||
if (out_of_range) {
|
||||
const DynamicPrintConfig *conf_temp = wxGetApp().plater()->config();
|
||||
if (conf_temp != nullptr && conf_temp->has(key))
|
||||
new_value = conf_temp->opt_int(key);
|
||||
}
|
||||
}
|
||||
new_conf.set_key_value(key, new ConfigOptionInt(new_value));
|
||||
apply(config, &new_conf);
|
||||
};
|
||||
for (const char* key : support_keys)
|
||||
reset_invalid_filament(key, false);
|
||||
for (const char* key : feature_keys)
|
||||
reset_invalid_filament(key, true);
|
||||
|
||||
// Sub-layer splitting divides each layer by the mix ratio; an adaptive layer profile makes
|
||||
// those sub-layer heights vary per layer, which degrades the blend. Warn once per enable.
|
||||
|
||||
@@ -9681,6 +9681,14 @@ 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).
|
||||
while (int(filament_text_first_line.size()) < extruder_num) {
|
||||
filament_text_first_line.emplace_back();
|
||||
filament_text_second_line.emplace_back();
|
||||
}
|
||||
|
||||
ImGuiWrapper& imgui = *wxGetApp().imgui();
|
||||
const float canvas_w = float(get_canvas_size().get_width());
|
||||
|
||||
@@ -454,7 +454,7 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
|
||||
ImGui::GetWindowDrawList()->AddRectFilledMultiColor(r_min, r_max, col_from, col_to, col_to, col_from);
|
||||
}
|
||||
|
||||
if (extruder_idx < 16 && ImGui::IsItemHovered()) m_imgui->tooltip(_L("Shortcut Key ") + std::to_string(extruder_idx + 1), max_tooltip_width);
|
||||
if (extruder_idx < int(GLGizmoMmuSegmentation::EXTRUDERS_LIMIT) && ImGui::IsItemHovered()) m_imgui->tooltip(_L("Shortcut Key ") + std::to_string(extruder_idx + 1), max_tooltip_width);
|
||||
}
|
||||
// ORCA: Remap filaments section (Border only, Title in border).
|
||||
// Styled as a panel for visual grouping.
|
||||
|
||||
@@ -73,11 +73,10 @@ public:
|
||||
|
||||
void data_changed(bool is_serializing) override;
|
||||
|
||||
// TriangleSelector::serialization/deserialization has a limit to store 19 different states.
|
||||
// EXTRUDER_LIMIT + 1 states are used to storing the painting because also uncolored triangles are stored.
|
||||
// When increasing EXTRUDER_LIMIT, it needs to ensure that TriangleSelector::serialization/deserialization
|
||||
// will be also extended to support additional states, requiring at least one state to remain free out of 19 states.
|
||||
static const constexpr size_t EXTRUDERS_LIMIT = 16;
|
||||
// The paint material limit follows EnforcerBlockerType::ExtruderMax: TriangleSelector
|
||||
// serialization covers the extended (17..32) range through an escape nibble. Mixed-color
|
||||
// filaments occupy ordinary slots, so they draw from the same budget as physical ones.
|
||||
static const constexpr size_t EXTRUDERS_LIMIT = static_cast<size_t>(EnforcerBlockerType::ExtruderMax);
|
||||
|
||||
// Endpoint colours for gradient mixed filaments, mirrored from Plater so the extruder
|
||||
// swatches below can be drawn as a two-tone fade instead of a single blended colour.
|
||||
|
||||
@@ -998,16 +998,40 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt)
|
||||
keyCode = keyCode- WXK_NUMPAD0+'0';
|
||||
}
|
||||
if (keyCode >= '0' && keyCode <= '9') {
|
||||
if (keyCode == '1' && !m_timer_set_color.IsRunning()) {
|
||||
// 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
|
||||
// number waits briefly for a second one.
|
||||
const int digit = keyCode - '0';
|
||||
const int shortcut_max = int(GLGizmoMmuSegmentation::EXTRUDERS_LIMIT);
|
||||
auto can_start_two_digit = [shortcut_max](int d) { return d > 0 && d * 10 <= shortcut_max; };
|
||||
auto select = [mmu_seg](int number) { return number > 0 && mmu_seg->on_number_key_down(number); };
|
||||
|
||||
if (m_timer_set_color.IsRunning() && m_pending_color_shortcut_tens > 0) {
|
||||
const int two_digit = m_pending_color_shortcut_tens * 10 + digit;
|
||||
const int pending = m_pending_color_shortcut_tens;
|
||||
m_pending_color_shortcut_tens = 0;
|
||||
m_timer_set_color.Stop();
|
||||
if (two_digit <= shortcut_max) {
|
||||
processed = select(two_digit);
|
||||
} else {
|
||||
// Out of range: commit the pending digit, then treat this one as new input.
|
||||
processed = select(pending);
|
||||
if (can_start_two_digit(digit)) {
|
||||
m_pending_color_shortcut_tens = digit;
|
||||
m_timer_set_color.StartOnce(500);
|
||||
processed = true;
|
||||
} else {
|
||||
processed = select(digit) || processed;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (can_start_two_digit(digit)) {
|
||||
m_pending_color_shortcut_tens = digit;
|
||||
m_timer_set_color.StartOnce(500);
|
||||
processed = true;
|
||||
}
|
||||
else if (keyCode < '7' && m_timer_set_color.IsRunning()) {
|
||||
processed = mmu_seg->on_number_key_down(keyCode - '0'+10);
|
||||
m_timer_set_color.Stop();
|
||||
}
|
||||
else {
|
||||
processed = mmu_seg->on_number_key_down(keyCode - '0');
|
||||
processed = select(digit);
|
||||
}
|
||||
}
|
||||
else if (keyCode == 'F' || keyCode == 'T' || keyCode == 'S' || keyCode == 'C' || keyCode == 'H' || keyCode == 'G') {
|
||||
@@ -1054,11 +1078,15 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt)
|
||||
|
||||
void GLGizmosManager::on_set_color_timer(wxTimerEvent& evt)
|
||||
{
|
||||
if (m_current == MmSegmentation) {
|
||||
// No second digit arrived in time: commit the pending leading digit on its own.
|
||||
if (m_current == MmSegmentation && m_pending_color_shortcut_tens > 0) {
|
||||
GLGizmoMmuSegmentation* mmu_seg = dynamic_cast<GLGizmoMmuSegmentation*>(get_current());
|
||||
mmu_seg->on_number_key_down(1);
|
||||
m_parent.set_as_dirty();
|
||||
if (mmu_seg != nullptr) {
|
||||
mmu_seg->on_number_key_down(m_pending_color_shortcut_tens);
|
||||
m_parent.set_as_dirty();
|
||||
}
|
||||
}
|
||||
m_pending_color_shortcut_tens = 0;
|
||||
}
|
||||
|
||||
void GLGizmosManager::update_after_undo_redo(const UndoRedo::Snapshot& snapshot)
|
||||
|
||||
@@ -144,6 +144,8 @@ private:
|
||||
|
||||
//When there are more than 9 colors, shortcut key coloring
|
||||
wxTimer m_timer_set_color;
|
||||
// Leading digit of a two-digit color shortcut still waiting for its second digit.
|
||||
int m_pending_color_shortcut_tens = 0;
|
||||
void on_set_color_timer(wxTimerEvent& evt);
|
||||
|
||||
// key MENU_ICON_NAME, value = ImtextureID
|
||||
|
||||
Reference in New Issue
Block a user