mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-07-04 01:20:57 +00:00
Compare commits
2 Commits
fix/time_f
...
epp/video_
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
70bb888794 | ||
|
|
adc8763099 |
238
README.md
238
README.md
@@ -1,237 +1 @@
|
||||
<div align="center">
|
||||
|
||||
<picture>
|
||||
<img alt="OrcaSlicer logo" src="resources/images/OrcaSlicer.png" width="15%" height="15%">
|
||||
</picture>
|
||||
|
||||
<a href="https://trendshift.io/repositories/15552" target="_blank"><img src="https://trendshift.io/api/badge/repositories/15552" alt="OrcaSlicer%2FOrcaSlicer | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
|
||||
|
||||
[](https://github.com/OrcaSlicer/OrcaSlicer/stargazers) [](https://github.com/OrcaSlicer/OrcaSlicer/actions/workflows/build_all.yml)
|
||||
|
||||
OrcaSlicer: an open source Next-Gen Slicing Software for Precision 3D Prints.
|
||||
Optimize your prints with ultra-fast slicing, intelligent support generation, and seamless printer compatibility—engineered for perfection.
|
||||
<h3>
|
||||
|
||||
# Official links and community
|
||||
|
||||
#### Official Website:
|
||||
|
||||
<a href="https://www.orcaslicer.com/" style="font-size:2em;">OrcaSlicer.com</a>
|
||||
|
||||
#### Github Repository:
|
||||
|
||||
<a href="https://github.com/OrcaSlicer/OrcaSlicer"><img src="https://img.shields.io/badge/OrcaSlicer-181717?style=flat&logo=github&logoColor=white" width="200" alt="GitHub Logo"/> </a>
|
||||
|
||||
#### Follow us:
|
||||
|
||||
<a href="https://twitter.com/real_OrcaSlicer"><img src="https://img.shields.io/badge/real__OrcaSlicer-000000?style=flat&logo=x&logoColor=white" width="200" alt="X Logo"/> </a>
|
||||
<a href="https://www.youtube.com/@OfficialOrcaSlicer"><img src="https://img.shields.io/badge/OfficialOrcaSlicer-FF0000?style=flat&logo=youtube&logoColor=white" width="200" alt="YouTube Logo"/> </a>
|
||||
|
||||
#### Join our Discord community:
|
||||
|
||||
<a href="https://discord.gg/P4VE9UY9gJ"><img src="https://img.shields.io/badge/-Discord-5865F2?style=flat&logo=discord&logoColor=fff" width="200" alt="discord logo"/> </a>
|
||||
|
||||
<table border="2" style="border-color: #ffa500; background-color:rgb(232, 220, 180); color: #856404;">
|
||||
<tr>
|
||||
<td>
|
||||
<strong>⚠️ CAUTION:</strong><br>
|
||||
Several clickbait and malicious websites, such as <b>orca-slicer[.]com</b> and <b>orcaslicer[.]net</b>, are pretending to be the official OrcaSlicer site. These sites may redirect you to dangerous downloads or contain misleading information.<br>
|
||||
<b>Our only official website is <a href="https://www.orcaslicer.com/">www.orcaslicer.com</a>.</b><br><br>
|
||||
If you come across any of these in search results, please <b>report them</b> as unsafe or phishing to help keep the community secure with:<br>
|
||||
- <a href="https://safebrowsing.google.com/safebrowsing/report_phish/">Google Safe Browsing</a><br>
|
||||
- <a href="https://www.microsoft.com/en-us/wdsi/support/report-unsafe-site">Microsoft Security Intelligence</a><br>
|
||||
- <a href="https://ipthreat.net/tools/reportphishing">IPThreat</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
# Main features
|
||||
|
||||
- **[Advanced Calibration Tools](https://www.orcaslicer.com/wiki/calibration_guide)**
|
||||
Comprehensive suite: temperature towers, flow rate, retraction & more for optimal performance.
|
||||
- **[Precise Wall](https://www.orcaslicer.com/wiki/quality_settings_precision#precise-wall) and [Seam Control](https://www.orcaslicer.com/wiki/quality_settings_seam)**
|
||||
Adjust outer wall spacing and apply scarf seams to enhance print accuracy.
|
||||
- **[Sandwich Mode](https://www.orcaslicer.com/wiki/quality_settings_wall_and_surfaces#innerouterinner) and [Polyholes](https://www.orcaslicer.com/wiki/quality_settings_precision#polyholes) Support**
|
||||
Use varied infill [patterns](https://www.orcaslicer.com/wiki/strength_settings_patterns) and accurate hole shapes for improved clarity.
|
||||
- **[Overhang](https://www.orcaslicer.com/wiki/quality_settings_overhangs) and [Support Optimization](https://www.orcaslicer.com/wiki#support-settings)**
|
||||
Modify geometry for printable overhangs with precise support placement.
|
||||
- **[Granular Controls and Customization](https://www.orcaslicer.com/wiki#process-settings)**
|
||||
Fine-tune print speed, layer height, pressure, and temperature with precision.
|
||||
- **Network Printer Support**
|
||||
Seamless integration with Klipper, PrusaLink, and OctoPrint for remote control.
|
||||
- **[Mouse Ear Brims](https://www.orcaslicer.com/wiki/others_settings_brim) & [Adaptive Bed Mesh](https://www.orcaslicer.com/wiki/printer_basic_information_adaptive_bed_mesh)**
|
||||
Automatic brims and adaptive mesh calibration ensure consistent adhesion.
|
||||
- **User-Friendly Interface**
|
||||
Intuitive drag-and-drop design with pre-made profiles for popular printers.
|
||||
- **[Open-Source](https://github.com/OrcaSlicer/OrcaSlicer) & [Community Driven](https://discord.gg/P4VE9UY9gJ)**
|
||||
Regular updates fueled by continuous community contributions.
|
||||
- **Wide Printer Compatibility**
|
||||
Supports a broad range of printers: Bambu Lab, Prusa, Creality, Voron, and more.
|
||||
- Additional features can be found in the [change notes](https://github.com/OrcaSlicer/OrcaSlicer/releases/).
|
||||
|
||||
# Wiki
|
||||
|
||||
The [wiki](https://www.orcaslicer.com/wiki) aims to provide a detailed explanation of the slicer settings, including how to maximize their use and how to calibrate and set up your printer.
|
||||
|
||||
- **[Access the wiki here](https://www.orcaslicer.com/wiki)**
|
||||
- **[Contribute to the wiki](https://www.orcaslicer.com/wiki/how_to_wiki)**
|
||||
|
||||
# Download
|
||||
|
||||
## Stable Release
|
||||
|
||||
📥 **[Download the Latest Stable Release](https://github.com/OrcaSlicer/OrcaSlicer/releases/latest)**
|
||||
Visit our GitHub Releases page for the latest stable version of OrcaSlicer, recommended for most users.
|
||||
|
||||
## Nightly Builds
|
||||
|
||||
🌙 **[Download the Latest Nightly Build](https://github.com/OrcaSlicer/OrcaSlicer/releases/tag/nightly-builds)**
|
||||
Explore the latest developments in OrcaSlicer with our nightly builds. Feedback on these versions is highly appreciated.
|
||||
|
||||
# How to install
|
||||
|
||||
## Windows
|
||||
|
||||
Download the **Windows Installer exe** for your preferred version from the [releases page](https://github.com/OrcaSlicer/OrcaSlicer/releases).
|
||||
|
||||
- *For convenience there is also a portable build available.*
|
||||
<details>
|
||||
<summary>Troubleshooting</summary>
|
||||
|
||||
- *If you have troubles to run the build, you might need to install following runtimes:*
|
||||
- [MicrosoftEdgeWebView2RuntimeInstallerX64](https://github.com/OrcaSlicer/OrcaSlicer/releases/download/v1.0.10-sf2/MicrosoftEdgeWebView2RuntimeInstallerX64.exe)
|
||||
- [Details of this runtime](https://aka.ms/webview2)
|
||||
- [Alternative Download Link Hosted by Microsoft](https://go.microsoft.com/fwlink/p/?LinkId=2124703)
|
||||
- [vcredist2019_x64](https://github.com/OrcaSlicer/OrcaSlicer/releases/download/v1.0.10-sf2/vcredist2019_x64.exe)
|
||||
- [Alternative Download Link Hosted by Microsoft](https://aka.ms/vs/17/release/vc_redist.x64.exe)
|
||||
- This file may already be available on your computer if you've installed visual studio. Check the following location: `%VCINSTALLDIR%Redist\MSVC\v142`
|
||||
</details>
|
||||
|
||||
Windows Package Manager
|
||||
|
||||
```shell
|
||||
winget install --id=SoftFever.OrcaSlicer -e
|
||||
```
|
||||
|
||||
## Mac
|
||||
|
||||
1. Download the DMG for your computer: `arm64` version for Apple Silicon and `x86_64` for Intel CPU.
|
||||
2. Drag OrcaSlicer.app to Application folder.
|
||||
3. *If you want to run a build from a PR, you also need to follow the instructions below:*
|
||||
|
||||
<details>
|
||||
<summary>Quarantine</summary>
|
||||
|
||||
- Option 1 (You only need to do this once. After that the app can be opened normally.):
|
||||
- Step 1: Hold _cmd_ and right click the app, from the context menu choose **Open**.
|
||||
- Step 2: A warning window will pop up, click _Open_
|
||||
|
||||
- Option 2:
|
||||
Execute this command in terminal:
|
||||
|
||||
```shell
|
||||
xattr -dr com.apple.quarantine /Applications/OrcaSlicer.app
|
||||
```
|
||||
|
||||
- Option 3:
|
||||
- Step 1: open the app, a warning window will pop up
|
||||

|
||||
- Step 2: in `System Settings` -> `Privacy & Security`, click `Open Anyway`:
|
||||

|
||||
</details>
|
||||
|
||||
## Linux
|
||||
|
||||
### Flathub (Recommended)
|
||||
|
||||
OrcaSlicer is available through FlatHub:
|
||||
|
||||
<a href='https://flathub.org/apps/com.orcaslicer.OrcaSlicer'><img width='240' alt='Download on Flathub' src='https://dl.flathub.org/assets/badges/flathub-badge-en.png'/></a>
|
||||
|
||||
Install from the command line:
|
||||
|
||||
```shell
|
||||
flatpak install flathub com.orcaslicer.OrcaSlicer
|
||||
flatpak run com.orcaslicer.OrcaSlicer
|
||||
```
|
||||
|
||||
It can also be installed through graphical software managers (KDE Discover, GNOME Software, etc.) when Flathub is enabled. Search for **OrcaSlicer** in your software center.
|
||||
|
||||
### AppImage
|
||||
|
||||
AppImages are published for both **x86_64** and **aarch64** (ARM64). Pick the file matching your CPU — the ARM64 build has `aarch64` in its name (e.g. `OrcaSlicer_Linux_AppImage_Ubuntu2404_aarch64_*.AppImage`).
|
||||
|
||||
1. Download App image from the [releases page](https://github.com/OrcaSlicer/OrcaSlicer/releases).
|
||||
2. Double click the downloaded file to run it.
|
||||
|
||||
3. If you run into trouble executing it, try this command in the terminal:
|
||||
`chmod +x /path_to_appimage/OrcaSlicer_Linux.AppImage`
|
||||
|
||||
# How to Compile
|
||||
|
||||
All updated build instructions for Windows, macOS, and Linux are now available on the official [OrcaSlicer Wiki - How to build](https://www.orcaslicer.com/wiki/how_to_build) page.
|
||||
|
||||
Please refer to the wiki to ensure you're following the latest and most accurate steps for your platform.
|
||||
|
||||
# Klipper Note
|
||||
|
||||
If you're running Klipper, it's recommended to add the following configuration to your `printer.cfg` file.
|
||||
|
||||
```gcode
|
||||
# Enable object exclusion
|
||||
[exclude_object]
|
||||
|
||||
# Enable arcs support
|
||||
[gcode_arcs]
|
||||
resolution: 0.1
|
||||
```
|
||||
|
||||
# Supports
|
||||
|
||||
**OrcaSlicer** is an open-source project and I'm deeply grateful to all my sponsors and backers.
|
||||
Their generous support enables me to purchase filaments and other essential 3D printing materials for the project.
|
||||
Thank you! :)
|
||||
|
||||
## Sponsors
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="https://qidi3d.com/" style="display:inline-block; border-radius:8px; background:#fff;">
|
||||
<img src="SoftFever_doc\sponsor_logos\QIDI.png" alt="QIDI" width="100" height="100">
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="https://bigtree-tech.com/" style="display:inline-block; border-radius:8px; background:#222;">
|
||||
<img src="SoftFever_doc\sponsor_logos\BigTreeTech.png" alt="BIGTREE TECH" width="100" height="100">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
## Backers:
|
||||
|
||||
**Ko-fi supporters** ☕: [Backers list](https://github.com/user-attachments/files/16147016/Supporters_638561417699952499.csv)
|
||||
|
||||
## Support me
|
||||
|
||||
<a href="https://github.com/sponsors/SoftFever"><img src="https://img.shields.io/badge/GitHub%20Sponsors-30363D?style=flat&logo=GitHub-Sponsors&logoColor=EA4AAA" height="50"></a>
|
||||
<a href="https://ko-fi.com/G2G5IP3CP"><img src="https://img.shields.io/badge/Support_me_on_Ko--fi-FF5E5B?style=flat&logo=ko-fi&logoColor=white" height="50"></a>
|
||||
<a href="https://paypal.me/softfever3d"><img src="https://img.shields.io/badge/PayPal-003087?style=flat&logo=paypal&logoColor=fff" height="50"></a>
|
||||
|
||||
## Some Background
|
||||
|
||||
Open-source slicing has always been built on a tradition of collaboration and attribution. [Slic3r](https://github.com/Slic3r/Slic3r), created by Alessandro Ranellucci and the RepRap community, laid the foundation. [PrusaSlicer](https://github.com/prusa3d/PrusaSlicer) by Prusa Research built on Slic3r and acknowledged that heritage. [Bambu Studio](https://github.com/bambulab/BambuStudio) in turn forked from PrusaSlicer, and [SuperSlicer](https://github.com/supermerill/SuperSlicer) by @supermerill extended PrusaSlicer with community-driven enhancements. Each project carried the work of its predecessors forward, crediting those who came before.
|
||||
|
||||
OrcaSlicer began in that same spirit, drawing from BambuStudio, PrusaSlicer, and ideas inspired by CuraSlicer and SuperSlicer. But it has since grown far beyond its origins. Through relentless innovation — introducing advanced calibration tools, precise wall and seam control, tree supports, adaptive slicing, and hundreds of other features — OrcaSlicer has become the most widely used and actively developed open-source slicer in the 3D printing community. Many of its innovations have been adopted by other slicers, making it a driving force for the entire industry.
|
||||
|
||||
The OrcaSlicer logo was designed by community member [Justin Levine](https://github.com/jal-co).
|
||||
|
||||
# License
|
||||
|
||||
- **OrcaSlicer** is licensed under the GNU Affero General Public License, version 3.
|
||||
- The **GNU Affero General Public License**, version 3 ensures that if you use any part of this software in any way (even behind a web server), your software must be released under the same license.
|
||||
- OrcaSlicer includes a **pressure advance calibration pattern test** adapted from Andrew Ellis' generator, which is licensed under GNU General Public License, version 3. Ellis' generator is itself adapted from a generator developed by Sineos for Marlin, which is licensed under GNU General Public License, version 3.
|
||||
- The **Bambu networking plugin** is based on non-free libraries from BambuLab. It is optional to the OrcaSlicer and provides extended functionalities for Bambulab printer users.
|
||||
Remove this branch and PR later
|
||||
@@ -310,7 +310,6 @@ void GCodeProcessor::TimeMachine::reset()
|
||||
g1_times_cache = std::vector<G1LinesCacheItem>();
|
||||
first_layer_time = 0.0f;
|
||||
prepare_time = 0.0f;
|
||||
m_additional_time_buffer.clear();
|
||||
}
|
||||
|
||||
static void planner_forward_pass_kernel(const GCodeProcessor::TimeBlock& prev, GCodeProcessor::TimeBlock& curr)
|
||||
@@ -396,48 +395,13 @@ static void recalculate_trapezoids(std::vector<GCodeProcessor::TimeBlock>& block
|
||||
}
|
||||
}
|
||||
|
||||
GCodeProcessor::TimeMachine::AdditionalBuffer GCodeProcessor::TimeMachine::merge_adjacent_additional_time_blocks(const AdditionalBuffer& buffer)
|
||||
void GCodeProcessor::TimeMachine::calculate_time(GCodeProcessorResult& result, PrintEstimatedStatistics::ETimeMode mode, size_t keep_last_n_blocks, float additional_time)
|
||||
{
|
||||
AdditionalBuffer merged;
|
||||
if (buffer.empty())
|
||||
return merged;
|
||||
|
||||
AdditionalBufferBlock current_block = buffer.front();
|
||||
for (size_t idx = 1; idx < buffer.size(); ++idx) {
|
||||
const AdditionalBufferBlock& next_block = buffer[idx];
|
||||
if (current_block.first == next_block.first)
|
||||
current_block.second += next_block.second;
|
||||
else {
|
||||
merged.push_back(current_block);
|
||||
current_block = next_block;
|
||||
}
|
||||
}
|
||||
merged.push_back(current_block);
|
||||
return merged;
|
||||
}
|
||||
|
||||
void GCodeProcessor::TimeMachine::calculate_time(GCodeProcessorResult& result, PrintEstimatedStatistics::ETimeMode mode, size_t keep_last_n_blocks, float additional_time, EMoveType target_move_type)
|
||||
{
|
||||
if (!enabled)
|
||||
if (!enabled || blocks.size() < 2)
|
||||
return;
|
||||
if (blocks.size() < 2) {
|
||||
// Not enough blocks to attribute the extra time to yet; buffer it so it is
|
||||
// applied on a later pass instead of being dropped.
|
||||
if (additional_time > 0.0f)
|
||||
m_additional_time_buffer.emplace_back(target_move_type, additional_time);
|
||||
return;
|
||||
}
|
||||
|
||||
assert(keep_last_n_blocks <= blocks.size());
|
||||
|
||||
// Merge any previously buffered extra time with this call's extra time. Each
|
||||
// entry is applied, in order, to the first block matching its target move type
|
||||
// (EMoveType::Noop matches any block).
|
||||
AdditionalBuffer additional_buffer = m_additional_time_buffer;
|
||||
if (additional_time > 0.0f)
|
||||
additional_buffer.emplace_back(target_move_type, additional_time);
|
||||
additional_buffer = merge_adjacent_additional_time_blocks(additional_buffer);
|
||||
|
||||
// reverse_pass
|
||||
for (int i = static_cast<int>(blocks.size()) - 1; i > 0; --i) {
|
||||
planner_reverse_pass_kernel(blocks[i - 1], blocks[i]);
|
||||
@@ -451,17 +415,11 @@ void GCodeProcessor::TimeMachine::calculate_time(GCodeProcessorResult& result, P
|
||||
recalculate_trapezoids(blocks);
|
||||
|
||||
const size_t n_blocks_process = blocks.size() - keep_last_n_blocks;
|
||||
size_t additional_buffer_idx = 0;
|
||||
for (size_t i = 0; i < n_blocks_process; ++i) {
|
||||
const TimeBlock& block = blocks[i];
|
||||
float block_time = block.time();
|
||||
if (additional_buffer_idx < additional_buffer.size()) {
|
||||
const EMoveType buf_move_type = additional_buffer[additional_buffer_idx].first;
|
||||
if (buf_move_type == EMoveType::Noop || buf_move_type == block.move_type) {
|
||||
block_time += additional_buffer[additional_buffer_idx].second;
|
||||
++additional_buffer_idx;
|
||||
}
|
||||
}
|
||||
if (i == 0)
|
||||
block_time += additional_time;
|
||||
|
||||
time += double(block_time);
|
||||
result.moves[block.move_id].time[static_cast<size_t>(mode)] = block_time;
|
||||
@@ -574,14 +532,6 @@ void GCodeProcessor::TimeMachine::calculate_time(GCodeProcessorResult& result, P
|
||||
it_stop_time->elapsed_time = float(time);
|
||||
}
|
||||
|
||||
// Carry forward any extra time that found no matching block this pass, so it
|
||||
// is retried against the blocks of a later pass.
|
||||
m_additional_time_buffer.clear();
|
||||
if (additional_buffer_idx < additional_buffer.size())
|
||||
m_additional_time_buffer.insert(m_additional_time_buffer.end(),
|
||||
additional_buffer.begin() + additional_buffer_idx,
|
||||
additional_buffer.end());
|
||||
|
||||
if (keep_last_n_blocks) {
|
||||
blocks.erase(blocks.begin(), blocks.begin() + n_blocks_process);
|
||||
|
||||
@@ -5575,34 +5525,9 @@ void GCodeProcessor::process_filament_change(int id)
|
||||
}
|
||||
|
||||
m_cp_color.current = m_extruder_colors[next_filament_id];
|
||||
|
||||
// Store the tool-change move first, then attribute the filament-change delay to
|
||||
// it rather than to whichever motion block happens to be pending. This keeps the
|
||||
// delay out of the per-role feature-time distribution (tool-change moves are not
|
||||
// counted as an extrusion role) while still including it in the total and
|
||||
// per-layer times. Mirrors BambuStudio's "separate flush time from other types"
|
||||
// (c54a8333c7) and "unprocessed additional time" (27ef0b1bef) fixes.
|
||||
simulate_st_synchronize(extra_time);
|
||||
// store tool change move
|
||||
store_move_vertex(EMoveType::Tool_change);
|
||||
|
||||
// Construct a zero-distance time block for the tool-change move on each enabled
|
||||
// machine so the synchronize below can land the delay on it. simulate_st_synchronize
|
||||
// flushes without keeping any blocks, so it is safe to append these directly.
|
||||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
|
||||
TimeMachine& machine = m_time_processor.machines[i];
|
||||
if (!machine.enabled)
|
||||
continue;
|
||||
TimeBlock block;
|
||||
block.move_id = static_cast<unsigned int>(m_result.moves.size()) - 1;
|
||||
block.move_type = EMoveType::Tool_change;
|
||||
block.layer_id = std::max<unsigned int>(1, m_layer_id);
|
||||
block.g1_line_id = m_g1_line_id;
|
||||
block.flags.prepare_stage = m_processing_start_custom_gcode;
|
||||
block.distance = 0.0f;
|
||||
block.calculate_trapezoid();
|
||||
machine.blocks.push_back(block);
|
||||
}
|
||||
|
||||
simulate_st_synchronize(extra_time, EMoveType::Tool_change);
|
||||
}
|
||||
|
||||
void GCodeProcessor::store_move_vertex(EMoveType type, EMovePathType path_type, bool internal_only)
|
||||
@@ -5926,13 +5851,13 @@ void GCodeProcessor::process_filaments(CustomGCode::Type code)
|
||||
}
|
||||
}
|
||||
|
||||
void GCodeProcessor::calculate_time(GCodeProcessorResult& result, size_t keep_last_n_blocks, float additional_time, EMoveType target_move_type)
|
||||
void GCodeProcessor::calculate_time(GCodeProcessorResult& result, size_t keep_last_n_blocks, float additional_time)
|
||||
{
|
||||
// calculate times
|
||||
std::vector<TimeMachine::ActualSpeedMove> actual_speed_moves;
|
||||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
|
||||
TimeMachine& machine = m_time_processor.machines[i];
|
||||
machine.calculate_time(m_result, static_cast<PrintEstimatedStatistics::ETimeMode>(i), keep_last_n_blocks, additional_time, target_move_type);
|
||||
machine.calculate_time(m_result, static_cast<PrintEstimatedStatistics::ETimeMode>(i), keep_last_n_blocks, additional_time);
|
||||
if (static_cast<PrintEstimatedStatistics::ETimeMode>(i) == PrintEstimatedStatistics::ETimeMode::Normal)
|
||||
actual_speed_moves = std::move(machine.actual_speed_moves);
|
||||
}
|
||||
@@ -5986,9 +5911,9 @@ void GCodeProcessor::calculate_time(GCodeProcessorResult& result, size_t keep_la
|
||||
}
|
||||
}
|
||||
|
||||
void GCodeProcessor::simulate_st_synchronize(float additional_time, EMoveType target_move_type)
|
||||
void GCodeProcessor::simulate_st_synchronize(float additional_time)
|
||||
{
|
||||
calculate_time(m_result, 0, additional_time, target_move_type);
|
||||
calculate_time(m_result, 0, additional_time);
|
||||
}
|
||||
|
||||
void GCodeProcessor::update_estimated_times_stats()
|
||||
|
||||
@@ -560,21 +560,9 @@ class Print;
|
||||
//BBS: prepare stage time before print model, including start gcode time and mostly same with start gcode time
|
||||
float prepare_time;
|
||||
|
||||
// Orca: extra time (e.g. filament-change delay) that could not be attributed
|
||||
// to a matching block on this pass is buffered here and applied on a later
|
||||
// pass, so it is never dropped or folded into an unrelated move.
|
||||
using AdditionalBufferBlock = std::pair<EMoveType, float>;
|
||||
using AdditionalBuffer = std::vector<AdditionalBufferBlock>;
|
||||
AdditionalBuffer m_additional_time_buffer;
|
||||
|
||||
void reset();
|
||||
|
||||
// Merge adjacent buffer entries that target the same move type.
|
||||
static AdditionalBuffer merge_adjacent_additional_time_blocks(const AdditionalBuffer& buffer);
|
||||
|
||||
// additional_time is attributed to the first block matching target_move_type
|
||||
// (EMoveType::Noop matches any block, i.e. the first processed block).
|
||||
void calculate_time(GCodeProcessorResult& result, PrintEstimatedStatistics::ETimeMode mode, size_t keep_last_n_blocks = 0, float additional_time = 0.0f, EMoveType target_move_type = EMoveType::Noop);
|
||||
void calculate_time(GCodeProcessorResult& result, PrintEstimatedStatistics::ETimeMode mode, size_t keep_last_n_blocks = 0, float additional_time = 0.0f);
|
||||
};
|
||||
|
||||
struct UsedFilaments // filaments per ColorChange
|
||||
@@ -1127,10 +1115,10 @@ class Print;
|
||||
void process_custom_gcode_time(CustomGCode::Type code);
|
||||
void process_filaments(CustomGCode::Type code);
|
||||
|
||||
void calculate_time(GCodeProcessorResult& result, size_t keep_last_n_blocks = 0, float additional_time = 0.0f, EMoveType target_move_type = EMoveType::Noop);
|
||||
void calculate_time(GCodeProcessorResult& result, size_t keep_last_n_blocks = 0, float additional_time = 0.0f);
|
||||
|
||||
// Simulates firmware st_synchronize() call
|
||||
void simulate_st_synchronize(float additional_time = 0.0f, EMoveType target_move_type = EMoveType::Noop);
|
||||
void simulate_st_synchronize(float additional_time = 0.0f);
|
||||
|
||||
void update_estimated_times_stats();
|
||||
|
||||
|
||||
@@ -91,9 +91,14 @@ Vec3d GLGizmoMeasure::get_feature_offset(const Measure::SurfaceFeature &feature)
|
||||
}
|
||||
case Measure::SurfaceFeatureType::Edge:
|
||||
{
|
||||
std::optional<Vec3d> p = feature.get_extra_point();
|
||||
assert(p.has_value());
|
||||
ret = *p;
|
||||
// Only polygon edges store an extra point (the polygon centre); plain edges have none.
|
||||
const std::optional<Vec3d> extra = feature.get_extra_point();
|
||||
if (extra.has_value())
|
||||
ret = *extra;
|
||||
else {
|
||||
const auto [pt1, pt2] = feature.get_edge();
|
||||
ret = 0.5 * (pt1 + pt2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Measure::SurfaceFeatureType::Point:
|
||||
@@ -1065,7 +1070,7 @@ void GLGizmoMeasure::on_render()
|
||||
|
||||
if (requires_raycaster_update) {
|
||||
if (m_gripper_id_raycast_map.find(GripperType::SPHERE_2) != m_gripper_id_raycast_map.end()) {
|
||||
m_gripper_id_raycast_map[GripperType::SPHERE_2]->set_transform(Geometry::translation_transform(get_feature_offset(*m_selected_features.first.feature)) *
|
||||
m_gripper_id_raycast_map[GripperType::SPHERE_2]->set_transform(Geometry::translation_transform(get_feature_offset(*m_selected_features.second.feature)) *
|
||||
Geometry::scale_transform(inv_zoom));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ add_executable(${_TEST_NAME}_tests
|
||||
test_fill.cpp
|
||||
test_flow.cpp
|
||||
test_gcode.cpp
|
||||
test_gcode_timing.cpp
|
||||
test_gcodewriter.cpp
|
||||
test_model.cpp
|
||||
test_print.cpp
|
||||
|
||||
@@ -1,185 +0,0 @@
|
||||
#include <catch2/catch_all.hpp>
|
||||
|
||||
#include "libslic3r/libslic3r.h"
|
||||
#include "libslic3r/GCode/GCodeProcessor.hpp"
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
|
||||
#include "test_utils.hpp"
|
||||
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
|
||||
using namespace Slic3r;
|
||||
using Catch::Matchers::WithinAbs;
|
||||
|
||||
// Regression coverage for filament/tool-change time being folded into the first
|
||||
// pending motion block (an extrusion move) instead of the tool-change move, and
|
||||
// for that delay being dropped entirely when too few motion blocks precede the
|
||||
// change. See BambuStudio "seperate flush time from other types" (c54a8333c7)
|
||||
// and the follow-up "unprocessed addtional time" fix (27ef0b1bef).
|
||||
namespace {
|
||||
|
||||
constexpr size_t NORMAL = static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Normal);
|
||||
|
||||
FullPrintConfig make_config(double load_time, double unload_time, double tool_change_time)
|
||||
{
|
||||
FullPrintConfig config; // default-initialized with the built-in defaults
|
||||
config.gcode_flavor.value = gcfMarlinFirmware;
|
||||
// Two filaments, both assigned to the same (single) extruder, so a T1 after
|
||||
// T0 is a same-extruder filament swap that costs unload + load time.
|
||||
config.filament_diameter.values = {1.75, 1.75};
|
||||
config.filament_map.values = {1, 1};
|
||||
config.machine_load_filament_time.value = load_time;
|
||||
config.machine_unload_filament_time.value = unload_time;
|
||||
config.machine_tool_change_time.value = tool_change_time;
|
||||
return config;
|
||||
}
|
||||
|
||||
void run_processor(GCodeProcessor& proc, const FullPrintConfig& config, const char* gcode)
|
||||
{
|
||||
// reserved_tag() selects between two tag tables based on this shared static, and
|
||||
// other tests in the binary mutate it -- pin it so our "; FEATURE:" role tags are
|
||||
// parsed deterministically regardless of test execution order.
|
||||
GCodeProcessor::s_IsBBLPrinter = true;
|
||||
ScopedTemporaryFile temp(".gcode");
|
||||
{
|
||||
std::ofstream os(temp.string());
|
||||
os << gcode;
|
||||
}
|
||||
proc.apply_config(config);
|
||||
// No producer marker in the gcode, so process_file keeps our applied config.
|
||||
proc.process_file(temp.string());
|
||||
}
|
||||
|
||||
// Estimated time per extrusion role, grouped exactly the way libvgcode builds the
|
||||
// feature-type legend: sum MoveVertex.time over EMoveType::Extrude moves keyed by
|
||||
// extrusion_role (see ViewerImpl.cpp:1017 -- only Extrude moves are counted).
|
||||
std::map<ExtrusionRole, double> role_times(const GCodeProcessorResult& r)
|
||||
{
|
||||
std::map<ExtrusionRole, double> m;
|
||||
for (const auto& mv : r.moves)
|
||||
if (mv.type == EMoveType::Extrude)
|
||||
m[mv.extrusion_role] += mv.time[NORMAL];
|
||||
return m;
|
||||
}
|
||||
|
||||
// Sum of estimated time attributed to tool-change moves.
|
||||
double sum_tool_change_time(const GCodeProcessorResult& r)
|
||||
{
|
||||
double t = 0.0;
|
||||
for (const auto& mv : r.moves)
|
||||
if (mv.type == EMoveType::Tool_change)
|
||||
t += mv.time[NORMAL];
|
||||
return t;
|
||||
}
|
||||
|
||||
// Total filament-change delay, accumulated independently of the timing machinery.
|
||||
double filament_change_delay(const GCodeProcessorResult& r)
|
||||
{
|
||||
const auto& s = r.print_statistics;
|
||||
return s.total_filament_load_time + s.total_filament_unload_time + s.total_tool_change_time;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("Filament-change time is attributed to tool-change moves, not extrusion roles", "[GCodeTiming]")
|
||||
{
|
||||
// Relative extrusion (M83) so every "E5" is a real 5mm extrusion move rather
|
||||
// than a zero-delta travel. Two real travels precede T0 so its delay is flushed
|
||||
// cleanly. The extrusions after T0 span several roles (Outer wall, Sparse infill,
|
||||
// Inner wall); the first pending block at T1 is an "Outer wall" move, so the
|
||||
// buggy code folds the T1 delay into that role. The per-role check below verifies
|
||||
// EVERY role stays clean, not just one, and catches any role-to-role misattribution.
|
||||
const char* gcode =
|
||||
"M83\n"
|
||||
"; FEATURE: Outer wall\n"
|
||||
"G1 X10 Y10 Z0.2 F600\n"
|
||||
"G1 X0 Y0 F6000\n"
|
||||
"T0\n"
|
||||
"; FEATURE: Outer wall\n"
|
||||
"G1 X50 Y0 E5 F1800\n"
|
||||
"G1 X50 Y50 E5\n"
|
||||
"; FEATURE: Sparse infill\n"
|
||||
"G1 X0 Y50 E5\n"
|
||||
"G1 X0 Y0 E5\n"
|
||||
"T1\n"
|
||||
"; FEATURE: Inner wall\n"
|
||||
"G1 X50 Y0 E5\n"
|
||||
"G1 X50 Y50 E5\n";
|
||||
|
||||
GCodeProcessor proc_zero;
|
||||
run_processor(proc_zero, make_config(0.0, 0.0, 0.0), gcode);
|
||||
const GCodeProcessorResult& r_zero = proc_zero.get_result();
|
||||
|
||||
const double load = 10.0;
|
||||
const double unload = 5.0;
|
||||
GCodeProcessor proc_delay;
|
||||
run_processor(proc_delay, make_config(load, unload, 0.0), gcode);
|
||||
const GCodeProcessorResult& r_delay = proc_delay.get_result();
|
||||
|
||||
const double delay = filament_change_delay(r_delay);
|
||||
|
||||
// Preconditions: the filament changes were charged, and cost nothing in the
|
||||
// zero-time baseline.
|
||||
REQUIRE(delay > 0.0);
|
||||
REQUIRE_THAT(filament_change_delay(r_zero), WithinAbs(0.0, 1e-9));
|
||||
|
||||
// The delay must not inflate the time of ANY extrusion role. Compare the full
|
||||
// per-role breakdown (exactly how the feature-type legend is built) between the
|
||||
// zero-delay and delayed runs -- every role must match to within tolerance.
|
||||
const auto roles_zero = role_times(r_zero);
|
||||
const auto roles_delay = role_times(r_delay);
|
||||
// Guard: the gcode must genuinely exercise multiple distinct roles (Outer wall,
|
||||
// Sparse infill, Inner wall), otherwise this check would silently cover only one.
|
||||
REQUIRE(roles_zero.size() >= 3);
|
||||
REQUIRE(roles_zero.size() == roles_delay.size());
|
||||
for (const auto& [role, zero_time] : roles_zero) {
|
||||
INFO("extrusion role index = " << static_cast<int>(role));
|
||||
REQUIRE(roles_delay.count(role) == 1);
|
||||
REQUIRE_THAT(roles_delay.at(role), WithinAbs(zero_time, 1e-2));
|
||||
}
|
||||
|
||||
// The delay must instead land on the tool-change moves, so per-move consumers
|
||||
// (layer-time view, layer slider) stay consistent.
|
||||
REQUIRE_THAT(sum_tool_change_time(r_delay), WithinAbs(delay, 1e-2));
|
||||
|
||||
// Both tool changes occur on layer 1, so the delay must also be reflected in
|
||||
// the first-layer time.
|
||||
const double first_layer_delta = proc_delay.get_first_layer_time(PrintEstimatedStatistics::ETimeMode::Normal)
|
||||
- proc_zero.get_first_layer_time(PrintEstimatedStatistics::ETimeMode::Normal);
|
||||
REQUIRE_THAT(first_layer_delta, WithinAbs(delay, 1e-2));
|
||||
}
|
||||
|
||||
TEST_CASE("Filament-change time is not dropped when few motion blocks precede the change", "[GCodeTiming]")
|
||||
{
|
||||
// Only a single motion block precedes T0, so the buggy code's "fewer than two
|
||||
// pending blocks" early-out discards that filament-change delay entirely,
|
||||
// making the total print time inconsistent with the reported statistics.
|
||||
const char* gcode =
|
||||
"; FEATURE: Outer wall\n"
|
||||
"G1 X10 Y10 Z0.2 F600\n"
|
||||
"T0\n"
|
||||
"G1 X50 Y0 E5 F1800\n"
|
||||
"G1 X50 Y50 E5\n"
|
||||
"T1\n"
|
||||
"G1 X0 Y50 E5\n"
|
||||
"G1 X0 Y0 E5\n";
|
||||
|
||||
GCodeProcessor proc_zero;
|
||||
run_processor(proc_zero, make_config(0.0, 0.0, 0.0), gcode);
|
||||
|
||||
const double load = 10.0;
|
||||
const double unload = 5.0;
|
||||
GCodeProcessor proc_delay;
|
||||
run_processor(proc_delay, make_config(load, unload, 0.0), gcode);
|
||||
const GCodeProcessorResult& r_delay = proc_delay.get_result();
|
||||
|
||||
const double delay = filament_change_delay(r_delay);
|
||||
REQUIRE(delay > 0.0);
|
||||
|
||||
// Every second of reported filament-change delay must be present in the total
|
||||
// estimated print time; none may be silently dropped.
|
||||
const double total_delta = proc_delay.get_time(PrintEstimatedStatistics::ETimeMode::Normal)
|
||||
- proc_zero.get_time(PrintEstimatedStatistics::ETimeMode::Normal);
|
||||
REQUIRE_THAT(total_delta, WithinAbs(delay, 1e-2));
|
||||
}
|
||||
Reference in New Issue
Block a user