Compare commits

..
Author SHA1 Message Date
Hanif Koh 7b05bc5ea1 Keep Layer Pauses on Their Own Layer in the Preview
The G-code processor remembers where a pause, color change or custom
G-code vertex sits in the move list, so it can move that vertex to the
first extrusion after it. A pause flushes the time planner first, which
inserts actual speed moves ahead of the vertex, so the remembered index
pointed at an earlier move: the previous layer's last extrusion was
moved instead, a real move was erased, and the time blocks queued after
the vertex were left one move off.

Shift the remembered index by the moves each flush inserts before it,
and shift the queued blocks after the erase. The Preview numbers layers
from the move order, so the stray move showed up as extra layers and
listed pauses on the wrong layer.
2026-10-21 13:50:40 +08:00
SoftFever 069bd402d9 Forward CMake arguments after -- in the macOS build script (#16683)
Unknown options and missing option values now stop the script instead of being ignored.
2026-10-20 16:06:33 +08:00
7 changed files with 129 additions and 78 deletions

No files matched your search

+25 -3
View File
@@ -47,7 +47,7 @@ while getopts ":dpa:snt:xbc:i:j:Tuh" opt; do
u )
export BUILD_TARGET="universal"
;;
h ) echo "Usage: ./build_release_macos.sh [-d]"
h ) echo "Usage: ./build_release_macos.sh [-d] [-- CMAKE_ARGS...]"
echo " -d: Build deps only"
echo " -a: Set ARCHITECTURE (arm64 or x86_64 or universal)"
echo " -s: Build slicer only"
@@ -60,13 +60,33 @@ while getopts ":dpa:snt:xbc:i:j:Tuh" opt; do
echo " -i: Add a prefix to ignore during CMake dependency discovery (repeatable), defaults to /opt/local:/usr/local:/opt/homebrew"
echo " -j: Set the number of parallel build jobs (CMAKE_BUILD_PARALLEL_LEVEL)"
echo " -T: Build and run tests (set ORCA_TESTS_BUILD_ONLY=1 to build without running)"
echo " -- CMAKE_ARGS...: pass everything after '--' to the cmake configure step of the deps (-d) and slicer (-s) builds,"
echo " e.g. -DDESTDIR=<dir> for the deps, -DCMAKE_PREFIX_PATH=<dir>/usr/local -DSLIC3R_PCH=OFF for the slicer, --fresh."
echo " They come last on the cmake command line. Use absolute paths: cmake runs in the build directory."
exit 0
;;
: )
echo "Option -${OPTARG} requires an argument, aborting."
exit 1
;;
* )
echo "Unknown option -${OPTARG}, aborting. CMake arguments must follow '--'."
exit 1
;;
esac
done
# getopts stops at '--'; whatever follows is handed to cmake unchanged.
if [[ ${OPTIND} -le $# ]] ; then
separator_index=$((OPTIND-1))
if [[ "${!separator_index}" != "--" ]] ; then
echo "Unexpected argument '${!OPTIND}': cmake arguments must follow '--', aborting."
exit 1
fi
fi
shift $((OPTIND-1))
EXTRA_CMAKE_ARGS=("$@")
# Set defaults
if [ -z "$ARCH" ]; then
@@ -211,7 +231,8 @@ function build_deps() {
-DCMAKE_OSX_DEPLOYMENT_TARGET="${OSX_DEPLOYMENT_TARGET}" \
-DCMAKE_IGNORE_PREFIX_PATH="${CMAKE_IGNORE_PREFIX_PATH}" \
"${CMAKE_CCACHE_ARGS[@]}" \
${CMAKE_POLICY_COMPAT}
${CMAKE_POLICY_COMPAT} \
"${EXTRA_CMAKE_ARGS[@]}"
fi
cmake --build . --config "$BUILD_CONFIG" --target deps
)
@@ -314,7 +335,8 @@ function build_slicer() {
-DCMAKE_OSX_DEPLOYMENT_TARGET="${OSX_DEPLOYMENT_TARGET}" \
-DCMAKE_IGNORE_PREFIX_PATH="${CMAKE_IGNORE_PREFIX_PATH}" \
"${CMAKE_CCACHE_ARGS[@]}" \
${CMAKE_POLICY_COMPAT}
${CMAKE_POLICY_COMPAT} \
"${EXTRA_CMAKE_ARGS[@]}"
fi
cmake --build . --config "$BUILD_CONFIG" --target "$SLICER_BUILD_TARGET"
)
+10 -1
View File
@@ -5234,7 +5234,15 @@ void GCodeProcessor::process_G1(const std::array<std::optional<double>, 4>& axes
if (origin == G1DiscretizationOrigin::G1)
m_extruded_last_z = m_end_position[Z];
m_options_z_corrector.update(m_height);
// The queued blocks of the moves after the erased one follow them down the list.
if (const std::optional<size_t> erased = m_options_z_corrector.update(m_height)) {
for (TimeMachine& machine : m_time_processor.machines) {
for (TimeBlock& block : machine.blocks) {
if (block.move_id > *erased)
--block.move_id;
}
}
}
if (m_forced_width > 0.0f)
m_width = m_forced_width;
@@ -7958,6 +7966,7 @@ void GCodeProcessor::calculate_time(GCodeProcessorResult& result, size_t keep_la
it->second : block.move_id + (unsigned int)inserted_actual_speed_moves_count;
}
}
m_options_z_corrector.on_moves_inserted(m_actual_speed_id_map);
}
void GCodeProcessor::simulate_st_synchronize(float additional_time, EMoveType target_move_type)
+22 -5
View File
@@ -18,6 +18,7 @@
#include <cstddef>
#include <cassert>
#include <cstdint>
#include <algorithm>
#include <array>
#include <unordered_map>
#include <utility>
@@ -1076,18 +1077,34 @@ inline constexpr float DEFAULT_FILAMENT_DENSITY = 1.245f;
m_custom_gcode_per_print_z_id = m_result.custom_gcode_per_print_z.size() - 1;
}
void update(float height) {
if (!m_move_id.has_value() || !m_custom_gcode_per_print_z_id.has_value())
// The time pass inserts actual speed moves in front of the moves of its blocks, and the stored
// vertex has no block of its own, so it shifts with every insertion made before it.
void on_moves_inserted(const std::vector<std::pair<unsigned int, unsigned int>>& id_map) {
if (!m_move_id.has_value())
return;
auto it = std::lower_bound(id_map.begin(), id_map.end(), *m_move_id,
[](const std::pair<unsigned int, unsigned int>& entry, size_t id) { return entry.first < id; });
if (it != id_map.begin()) {
--it;
*m_move_id += it->second - it->first;
}
}
const Vec3f position = m_result.moves.back().position;
// Returns the index of the move erased from the list, if any.
std::optional<size_t> update(float height) {
if (!m_move_id.has_value() || !m_custom_gcode_per_print_z_id.has_value())
return std::nullopt;
GCodeProcessorResult::MoveVertex& move = m_result.moves.emplace_back(m_result.moves[*m_move_id]);
const Vec3f position = m_result.moves.back().position;
const size_t erased = *m_move_id;
GCodeProcessorResult::MoveVertex& move = m_result.moves.emplace_back(m_result.moves[erased]);
move.position = position;
move.height = height;
m_result.moves.erase(m_result.moves.begin() + *m_move_id);
m_result.moves.erase(m_result.moves.begin() + erased);
m_result.custom_gcode_per_print_z[*m_custom_gcode_per_print_z_id].print_z = position.z();
reset();
return erased;
}
void reset() {
+7 -15
View File
@@ -435,7 +435,7 @@ void CalibrationWizard::cache_preset_info(MachineObject *obj, float nozzle_dia,
if (obj->is_multi_extruders()) {
int ams_id, slot_id, tray_id;
get_tray_ams_and_slot_id(curr_obj, result.tray_id, ams_id, slot_id, tray_id);
result.extruder_id = preset_page->get_extruder_id_by_tray_id(result.tray_id);
result.extruder_id = preset_page->get_extruder_id(ams_id);
result.nozzle_diameter = preset_page->get_nozzle_diameter(result.extruder_id);
}
else {
@@ -753,7 +753,7 @@ void PressureAdvanceWizard::on_cali_start()
X1CCalibInfos::X1CCalibInfo calib_info;
get_tray_ams_and_slot_id(curr_obj, item.first, calib_info.ams_id, calib_info.slot_id, calib_info.tray_id);
calib_info.extruder_id = preset_page->get_extruder_id_by_tray_id(item.first);
calib_info.extruder_id = preset_page->get_extruder_id(calib_info.ams_id);
calib_info.extruder_type = preset_page->get_extruder_type(calib_info.extruder_id);
calib_info.nozzle_volume_type = preset_page->get_nozzle_volume_type(calib_info.extruder_id);
calib_info.nozzle_diameter = preset_page->get_nozzle_diameter(calib_info.extruder_id);
@@ -792,7 +792,7 @@ void PressureAdvanceWizard::on_cali_start()
CalibInfo calib_info;
calib_info.dev_id = curr_obj->get_dev_id();
get_tray_ams_and_slot_id(curr_obj, selected_filaments.begin()->first, calib_info.ams_id, calib_info.slot_id, selected_tray_id);
calib_info.extruder_id = preset_page->get_extruder_id_by_tray_id(selected_filaments.begin()->first);
calib_info.extruder_id = preset_page->get_extruder_id(calib_info.ams_id);
calib_info.extruder_type = preset_page->get_extruder_type(calib_info.extruder_id);
calib_info.nozzle_volume_type = preset_page->get_nozzle_volume_type(calib_info.extruder_id);
calib_info.nozzle_diameter = preset_page->get_nozzle_diameter(calib_info.extruder_id);
@@ -877,7 +877,7 @@ void PressureAdvanceWizard::on_cali_start()
calib_info.dev_id = curr_obj->get_dev_id();
get_tray_ams_and_slot_id(curr_obj, item.first, calib_info.ams_id, calib_info.slot_id, selected_tray_id);
calib_info.index = preset_page->get_index_by_tray_id(item.first);
calib_info.extruder_id = preset_page->get_extruder_id_by_tray_id(item.first);
calib_info.extruder_id = preset_page->get_extruder_id(calib_info.ams_id);
calib_info.nozzle_diameter = preset_page->get_nozzle_diameter(calib_info.extruder_id);
calib_info.extruder_type = preset_page->get_extruder_type(calib_info.extruder_id);
calib_info.nozzle_volume_type = preset_page->get_nozzle_volume_type(calib_info.extruder_id);
@@ -1252,7 +1252,7 @@ void FlowRateWizard::on_cali_start(CaliPresetStage stage, float cali_value, Flow
X1CCalibInfos::X1CCalibInfo calib_info;
calib_info.tray_id = item.first;
get_tray_ams_and_slot_id(curr_obj, item.first, calib_info.ams_id, calib_info.slot_id, calib_info.tray_id);
calib_info.extruder_id = preset_page->get_extruder_id_by_tray_id(item.first);
calib_info.extruder_id = preset_page->get_extruder_id(calib_info.ams_id);
calib_info.extruder_type = preset_page->get_extruder_type(calib_info.extruder_id);
calib_info.nozzle_volume_type = preset_page->get_nozzle_volume_type(calib_info.extruder_id);
calib_info.nozzle_diameter = preset_page->get_nozzle_diameter(calib_info.extruder_id);
@@ -1308,15 +1308,7 @@ void FlowRateWizard::on_cali_start(CaliPresetStage stage, float cali_value, Flow
int selected_tray_id = 0;
get_tray_ams_and_slot_id(curr_obj, selected_filaments.begin()->first, calib_info.ams_id, calib_info.slot_id, selected_tray_id);
calib_info.select_ams = std::to_string(selected_tray_id);
calib_info.extruder_id = preset_page->get_extruder_id_by_tray_id(selected_filaments.begin()->first);
if (from_page == FlowRatioCaliSource::FROM_COARSE_PAGE) {
// The fine pass must run on the extruder the coarse pass used. The cache records it;
// the preset page's selection may have changed or been reset since.
const auto cached = get_cached_selected_filament_for_multi_extruder(curr_obj);
const auto it = cached.find(selected_filaments.begin()->first);
if (it != cached.end())
calib_info.extruder_id = it->second.extruder_id;
}
calib_info.extruder_id = preset_page->get_extruder_id(calib_info.ams_id);
calib_info.extruder_type = preset_page->get_extruder_type(calib_info.extruder_id);
calib_info.nozzle_volume_type = preset_page->get_nozzle_volume_type(calib_info.extruder_id);
calib_info.nozzle_diameter = preset_page->get_nozzle_diameter(calib_info.extruder_id);
@@ -1732,7 +1724,7 @@ void MaxVolumetricSpeedWizard::on_cali_start()
int selected_tray_id = 0;
get_tray_ams_and_slot_id(curr_obj, selected_filaments.begin()->first, calib_info.ams_id, calib_info.slot_id, selected_tray_id);
calib_info.select_ams = std::to_string(selected_tray_id);
calib_info.extruder_id = preset_page->get_extruder_id_by_tray_id(selected_filaments.begin()->first);
calib_info.extruder_id = preset_page->get_extruder_id(calib_info.ams_id);
calib_info.extruder_type = preset_page->get_extruder_type(calib_info.extruder_id);
calib_info.nozzle_volume_type = preset_page->get_nozzle_volume_type(calib_info.extruder_id);
calib_info.nozzle_diameter = preset_page->get_nozzle_diameter(calib_info.extruder_id);
+22 -53
View File
@@ -587,23 +587,8 @@ void CalibrationPresetPage::on_sys_color_changed()
m_ams_sync_button->msw_rescale();
}
int CalibrationPresetPage::get_extruder_id_by_tray_id(int tray_id)
int CalibrationPresetPage::get_extruder_id(int ams_id)
{
// An AMS behind a Filament Track Switch feeds both extruders, so the extruder is the one
// whose panel the filament was selected in.
if (curr_obj && curr_obj->is_multi_extruders()) {
for (FilamentComboBox *fcb : get_selected_filament_combobox()) {
if (fcb->get_tray_id() != tray_id)
continue;
const bool is_deputy = std::find(m_deputy_filament_comboBox_list.begin(), m_deputy_filament_comboBox_list.end(), fcb) !=
m_deputy_filament_comboBox_list.end();
return is_deputy ? DEPUTY_EXTRUDER_ID : MAIN_EXTRUDER_ID;
}
}
int ams_id = tray_id / 4, slot_id, unused_tray_id;
if (curr_obj)
get_tray_ams_and_slot_id(curr_obj, tray_id, ams_id, slot_id, unused_tray_id);
if (m_ams_id_to_extruder_id_map.find(ams_id) != m_ams_id_to_extruder_id_map.end()) {
return m_ams_id_to_extruder_id_map[ams_id];
}
@@ -881,18 +866,6 @@ static void fill_nozzle_volume_combo(ComboBox *combo)
select_nozzle_volume(combo, NozzleVolumeType::nvtStandard);
}
static DynamicPrintConfig empty_tray_config()
{
DynamicPrintConfig config;
config.set_key_value("filament_id", new ConfigOptionStrings{""});
config.set_key_value("tag_uid", new ConfigOptionStrings{""});
config.set_key_value("filament_type", new ConfigOptionStrings{""});
config.set_key_value("tray_name", new ConfigOptionStrings{""});
config.set_key_value("filament_colour", new ConfigOptionStrings{""});
config.set_key_value("filament_exist", new ConfigOptionBools{false});
return config;
}
void CalibrationPresetPage::init_selection_values()
{
// init nozzle diameter and nozzle volume
@@ -2299,36 +2272,27 @@ void CalibrationPresetPage::sync_ams_info(MachineObject* obj)
}
}
// init virtual tray info. With a Filament Track Switch installed the external spools feed
// through the switch, so they can't be calibrated as their own trays.
if (obj->GetFilaSwitch()->IsInstalled()) {
filament_ams_list[VIRTUAL_TRAY_MAIN_ID] = empty_tray_config();
filament_ams_list[VIRTUAL_TRAY_DEPUTY_ID] = empty_tray_config();
} else {
if (full_filament_ams_list.find(VIRTUAL_TRAY_MAIN_ID) != full_filament_ams_list.end()) {
filament_ams_list[VIRTUAL_TRAY_MAIN_ID] = full_filament_ams_list[VIRTUAL_TRAY_MAIN_ID];
}
// init virtual tray info
if (full_filament_ams_list.find(VIRTUAL_TRAY_MAIN_ID) != full_filament_ams_list.end()) {
filament_ams_list[VIRTUAL_TRAY_MAIN_ID] = full_filament_ams_list[VIRTUAL_TRAY_MAIN_ID];
}
if (full_filament_ams_list.find(VIRTUAL_TRAY_DEPUTY_ID) != full_filament_ams_list.end()) {
filament_ams_list[VIRTUAL_TRAY_DEPUTY_ID] = full_filament_ams_list[VIRTUAL_TRAY_DEPUTY_ID];
}
if (full_filament_ams_list.find(VIRTUAL_TRAY_DEPUTY_ID) != full_filament_ams_list.end()) {
filament_ams_list[VIRTUAL_TRAY_DEPUTY_ID] = full_filament_ams_list[VIRTUAL_TRAY_DEPUTY_ID];
}
// update filament from panel, display only obj has ams
// update multi ams panel, display only obj has multi ams
if (obj->HasAms()) {
if (obj->is_multi_extruders()) {
// An AMS behind a Filament Track Switch is bound to both extruders and appears in both panels.
bool main_done = false;
bool deputy_done = false;
for (auto &ams_item : obj->GetFilaSystem()->GetAmsList()) {
const auto &extruder_set = ams_item.second->GetBindedExtruderSet();
if (!main_done && extruder_set.count(MAIN_EXTRUDER_ID) != 0) {
update_multi_extruder_filament_combobox(ams_item.second->GetAmsId(), MAIN_EXTRUDER_ID);
if (ams_item.second->GetExtruderId() == 0 && !main_done) {
update_multi_extruder_filament_combobox(ams_item.second->GetAmsId(), ams_item.second->GetExtruderId());
main_done = true;
}
if (!deputy_done && extruder_set.count(DEPUTY_EXTRUDER_ID) != 0) {
update_multi_extruder_filament_combobox(ams_item.second->GetAmsId(), DEPUTY_EXTRUDER_ID);
} else if (ams_item.second->GetExtruderId() == 1 && !deputy_done) {
update_multi_extruder_filament_combobox(ams_item.second->GetAmsId(), ams_item.second->GetExtruderId());
deputy_done = true;
}
}
@@ -2369,11 +2333,10 @@ void CalibrationPresetPage::sync_ams_info(MachineObject* obj)
if (ams->second->IsExist()
&& info.parse_ams_info(obj, ams->second, obj->GetFilaSystem()->IsDetectRemainEnabled(), obj->is_support_ams_humidity)) {
ams_info.push_back(info);
for (int extruder_id : ams->second->GetBindedExtruderSet()) {
if (extruder_id == MAIN_EXTRUDER_ID)
main_ams_info.push_back(info);
else
deputy_ams_info.push_back(info);
if (info.nozzle_id == 0) {
main_ams_info.push_back(info);
} else {
deputy_ams_info.push_back(info);
}
}
m_ams_id_to_extruder_id_map[stoi(info.ams_id)] = info.nozzle_id;
@@ -2621,7 +2584,13 @@ void CalibrationPresetPage::update_multi_extruder_filament_combobox(const std::s
}
}
DynamicPrintConfig empty_config = empty_tray_config();
DynamicPrintConfig empty_config;
empty_config.set_key_value("filament_id", new ConfigOptionStrings{""});
empty_config.set_key_value("tag_uid", new ConfigOptionStrings{""});
empty_config.set_key_value("filament_type", new ConfigOptionStrings{""});
empty_config.set_key_value("tray_name", new ConfigOptionStrings{""});
empty_config.set_key_value("filament_colour", new ConfigOptionStrings{""});
empty_config.set_key_value("filament_exist", new ConfigOptionBools{false});
if (filament_ams_list.empty()) return;
@@ -251,7 +251,7 @@ public:
void msw_rescale() override;
void on_sys_color_changed() override;
int get_extruder_id_by_tray_id(int tray_id);
int get_extruder_id(int ams_id);
float get_nozzle_diameter(int extruder_id) const;
NozzleVolumeType get_nozzle_volume_type(int extruder_id) const;
ExtruderType get_extruder_type(int extruder_id) const;
+42
View File
@@ -282,6 +282,48 @@ TEST_CASE("A seam takes the actual speed of the move it follows", "[GCodeProcess
REQUIRE(seams > 0);
}
TEST_CASE("A pause keeps the moves around it in order and on their own layers", "[GCodeProcessor][Regression]")
{
// libvgcode numbers layers from the order of the moves, so the moves around a pause must stay in layer and
// line order. The pause vertex itself moves to the first extrusion after it and keeps its own line.
std::ostringstream gcode;
gcode << "M83\nG90\n";
for (int layer = 1; layer <= 3; ++layer) {
gcode << "; CHANGE_LAYER\n; LAYER_HEIGHT: 0.2\n";
if (layer == 2)
gcode << "G1 E-0.8 F3000\n; PAUSE_PRINTING\nM601\n";
gcode << "G1 X10 Y10 F12000\nG1 Z" << 0.2 * layer << "\n";
if (layer == 2)
gcode << "G1 E0.8 F3000\n";
gcode << "; FEATURE: Outer wall\n"
<< "G1 X50 Y10 E2 F3000\nG1 X50 Y50 E2\nG1 X10 Y50 E2\nG1 X10 Y10 E2\n";
}
GCodeProcessorResult result;
process_gcode(gcode.str(), result);
const auto &moves = result.moves;
REQUIRE(result.custom_gcode_per_print_z.size() == 1);
CHECK_THAT(result.custom_gcode_per_print_z.front().print_z, Catch::Matchers::WithinAbs(0.4, 1e-4));
REQUIRE(std::count_if(moves.begin(), moves.end(), [](const auto &move) { return move.internal_only; }) > 0);
const auto pause = std::find_if(moves.begin(), moves.end(), [](const auto &move) { return move.type == EMoveType::Pause_Print; });
REQUIRE(pause != moves.end());
CHECK(pause->layer_id == 1);
CHECK_THAT(pause->position.z(), Catch::Matchers::WithinAbs(0.4f, 1e-4f));
const GCodeProcessorResult::MoveVertex *previous = nullptr;
for (const auto &move : moves) {
if (move.type == EMoveType::Pause_Print)
continue;
if (previous != nullptr) {
CAPTURE(previous->gcode_id, move.gcode_id, static_cast<int>(move.type));
CHECK(move.layer_id >= previous->layer_id);
CHECK(move.gcode_id >= previous->gcode_id);
}
previous = &move;
}
}
TEST_CASE("Line ends of the exported G-code mark every newline in the file", "[GCodeProcessor]")
{
struct Case