mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-28 19:31:22 +00:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
536ef75894 |
@@ -1,80 +0,0 @@
|
|||||||
# G-code preview while dragging
|
|
||||||
|
|
||||||
The sliced preview draws every toolpath segment of the plate as an instanced box. On a large
|
|
||||||
plate that is tens of millions of segments, and the frame is GPU-bound: the cost is the number of
|
|
||||||
instances drawn, not anything the CPU does per frame. Dragging the camera over such a plate cannot
|
|
||||||
keep up. The `preview_reduced_detail_mode` preference (*Graphics > G-code Preview*, off by
|
|
||||||
default) lets the preview draw less while the user drags and put the full toolpaths back when they
|
|
||||||
let go.
|
|
||||||
|
|
||||||
| Preference | Values | Effect |
|
|
||||||
|---|---|---|
|
|
||||||
| `preview_reduced_detail_mode` | `off`, `solid`, `shell` | what is drawn while dragging |
|
|
||||||
|
|
||||||
libvgcode (`src/libvgcode`) builds and binds the reduced toolpath set, `GCodeViewer` maps the
|
|
||||||
preferences onto it and draws the solid model, and `GLCanvas3D` decides when the user is dragging.
|
|
||||||
The OpenGL ES path keeps a single set and ignores the preference.
|
|
||||||
|
|
||||||
## Two sets, one walk
|
|
||||||
|
|
||||||
`ViewerImpl::update_enabled_entities()` walks the visible vertex range once and fills two segment
|
|
||||||
index buffers side by side: the **full** set and the **reduced** set (segments and options).
|
|
||||||
Building them together is what makes switching free: starting or ending a drag is a buffer
|
|
||||||
binding, never a rebuild. A change of mode does rebuild. Nothing is built while the mode
|
|
||||||
is off, and the reduced buffers are then uploaded empty so that the last set does not stay
|
|
||||||
allocated.
|
|
||||||
|
|
||||||
Whatever the mode leaves out, the bottom and top layers of the visible range are kept whole: they
|
|
||||||
are the faces the range cuts open, and the top is what the user is looking at.
|
|
||||||
|
|
||||||
### Modes
|
|
||||||
|
|
||||||
- `EndLayersOnly` (`solid` in the preference) keeps only the two end layers. `GCodeViewer` then
|
|
||||||
draws the sliced objects and the prime tower as opaque solids, see below.
|
|
||||||
- `ShellOnly` (`shell`) keeps every role but the sparse infill, internal solid infill, internal
|
|
||||||
bridge infill and gap fill, which lie under the skins or between the walls. Walls, top and
|
|
||||||
bottom surfaces, bridges, supports and the prime tower are drawn whole, so from outside the
|
|
||||||
print looks as it does at rest. The roles come from
|
|
||||||
the slicer, so the classification is exact wherever the slicer's is; what it cannot express is
|
|
||||||
the inside of the prime tower and of a support, which share one role with their outside and are
|
|
||||||
kept whole. A profile with no top or bottom shell layers, or no walls, leaves the infill on the
|
|
||||||
surface, so `GCodeViewer` turns the hiding off for such a print when it loads it, from the
|
|
||||||
print's default region settings; per-object overrides are not consulted.
|
|
||||||
|
|
||||||
## The solid model
|
|
||||||
|
|
||||||
The preview already loads the sliced objects as shells for its translucent ghost.
|
|
||||||
`GCodeViewer::render_solid_model()` draws those shells opaque, in their filament colors, with the
|
|
||||||
`gouraud` shader, whose z range cuts them to the visible layer range. The two toolpath layers of
|
|
||||||
the reduced set are drawn afterwards and cap the cut with what was really printed there. The
|
|
||||||
shells hold only the objects, so while this mode is on the prime tower is added from its sliced
|
|
||||||
mesh, positioned as the print placed it. It is added or removed on its own when the mode changes,
|
|
||||||
without reloading the objects, keeps its opaque color so that it never appears among the
|
|
||||||
translucent shells, and stays out of their bounding box. Supports have no mesh and are not shown,
|
|
||||||
and `load_shells()` drops every non-model-part volume, so a negative volume is not cut out.
|
|
||||||
|
|
||||||
A plate whose shells are not loaded keeps drawing toolpaths, since the solid model would leave
|
|
||||||
only the end layers.
|
|
||||||
|
|
||||||
## Deciding that the user is dragging
|
|
||||||
|
|
||||||
`GLCanvas3D::_update_preview_interaction()` runs at the top of every preview frame, before the
|
|
||||||
canvas decides whether to reuse its cached scene, so that the switch lands in that frame. Dragging
|
|
||||||
is `GLCanvas3D::is_user_interacting()`, the same answer the scene cache reads: the camera, the
|
|
||||||
navigator, a gizmo, the rectangle selection or either slider being held. A slider reports this from
|
|
||||||
ImGui's active id rather than its dirty flag, which is raised and consumed inside one frame. A
|
|
||||||
wheel step has no duration, so it holds the reduced set for a 150 ms settle time instead, and the
|
|
||||||
frame that restores the toolpaths is scheduled for when that time runs out, since the render timer
|
|
||||||
only wakes the idle loop. A drag cut short by focus or capture loss is ended explicitly, and a
|
|
||||||
button release wakes the idle loop, because on some platforms nothing else would until the next
|
|
||||||
input.
|
|
||||||
|
|
||||||
## Reused scene frames
|
|
||||||
|
|
||||||
`GLCanvas3D` keeps its last scene pass for frames that only rebuild the overlay (`SceneCache`). Its
|
|
||||||
key covers the canvas size, the camera and hover state, not what the toolpath sets draw, so a frame
|
|
||||||
that reuses the scene must never be one on which the set is switched.
|
|
||||||
`_update_preview_interaction()` therefore reports whether the bound set changed, and a frame on
|
|
||||||
which it did redraws the scene. The canvas neither captures nor reuses the scene while the user
|
|
||||||
drags, so no reduced frame outlives a drag, and the frame that ends a wheel's settle time is
|
|
||||||
requested as a full frame.
|
|
||||||
+52
-6
@@ -2116,9 +2116,14 @@ int CLI::run(int argc, char **argv)
|
|||||||
|
|
||||||
// One resolver for the whole run, so presets from the same vendor tree share its load.
|
// One resolver for the whole run, so presets from the same vendor tree share its load.
|
||||||
std::unique_ptr<PresetBundle> system_preset_resolver;
|
std::unique_ptr<PresetBundle> system_preset_resolver;
|
||||||
auto resolve_preset = [&ensure_cli_preset_bundle, &system_preset_resolver](const std::string &file, DynamicPrintConfig &config,
|
auto ensure_system_preset_resolver = [&system_preset_resolver]() -> PresetBundle & {
|
||||||
std::string &config_type, const std::string &config_from,
|
if (!system_preset_resolver)
|
||||||
bool probe_type, std::string &error) {
|
system_preset_resolver = std::make_unique<PresetBundle>();
|
||||||
|
return *system_preset_resolver;
|
||||||
|
};
|
||||||
|
auto resolve_preset = [&ensure_cli_preset_bundle, &ensure_system_preset_resolver](const std::string &file, DynamicPrintConfig &config,
|
||||||
|
std::string &config_type, const std::string &config_from,
|
||||||
|
bool probe_type, std::string &error) {
|
||||||
const auto *inherits = config.option<ConfigOptionString>(BBL_JSON_KEY_INHERITS);
|
const auto *inherits = config.option<ConfigOptionString>(BBL_JSON_KEY_INHERITS);
|
||||||
if (!probe_type && (inherits == nullptr || inherits->value.empty()))
|
if (!probe_type && (inherits == nullptr || inherits->value.empty()))
|
||||||
return true;
|
return true;
|
||||||
@@ -2126,9 +2131,7 @@ int CLI::run(int argc, char **argv)
|
|||||||
PresetBundle *bundle = nullptr;
|
PresetBundle *bundle = nullptr;
|
||||||
bool allow_source_manifest = false;
|
bool allow_source_manifest = false;
|
||||||
if (config_from == "system") {
|
if (config_from == "system") {
|
||||||
if (!system_preset_resolver)
|
bundle = &ensure_system_preset_resolver();
|
||||||
system_preset_resolver = std::make_unique<PresetBundle>();
|
|
||||||
bundle = system_preset_resolver.get();
|
|
||||||
allow_source_manifest = true;
|
allow_source_manifest = true;
|
||||||
} else {
|
} else {
|
||||||
bundle = ensure_cli_preset_bundle(error);
|
bundle = ensure_cli_preset_bundle(error);
|
||||||
@@ -3120,6 +3123,49 @@ int CLI::run(int argc, char **argv)
|
|||||||
return 0;
|
return 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// A project saved before a printer or process option existed has no value for it. The GUI takes such
|
||||||
|
// keys from the project's system preset (load_external_preset refreshes every key the project did not
|
||||||
|
// override), so fill them from there too instead of leaving them to the option default.
|
||||||
|
// The extruder variant keys describe the project's variant layout and are kept as they are, so an
|
||||||
|
// older project is not left with a variant list from one layout and ids from another.
|
||||||
|
auto fill_missing_project_keys = [this, &ensure_system_preset_resolver](const std::string &system_name, Preset::Type type) {
|
||||||
|
if (system_name.empty())
|
||||||
|
return;
|
||||||
|
static const std::set<std::string> skip_keys = {
|
||||||
|
"inherits", "compatible_printers", "compatible_prints", "compatible_printers_condition", "compatible_prints_condition",
|
||||||
|
"print_settings_id", "filament_settings_id", "printer_settings_id",
|
||||||
|
"print_host", "print_host_webui", "printhost_apikey", "printhost_cafile", "printhost_user", "printhost_password", "printhost_port",
|
||||||
|
"printer_extruder_id", "printer_extruder_variant", "print_extruder_id", "print_extruder_variant", "extruder_variant_list"};
|
||||||
|
const std::vector<std::string> &options = type == Preset::TYPE_PRINTER ? Preset::printer_options() : Preset::print_options();
|
||||||
|
// Keys the legacy handler drops on load can never be in a project, so they do not count as missing.
|
||||||
|
auto dropped_on_load = [](std::string key) {
|
||||||
|
std::string value;
|
||||||
|
PrintConfigDef::handle_legacy(key, value);
|
||||||
|
return key.empty();
|
||||||
|
};
|
||||||
|
std::vector<std::string> missing;
|
||||||
|
for (const std::string &key : options)
|
||||||
|
if (m_print_config.option(key) == nullptr && skip_keys.count(key) == 0 && !dropped_on_load(key))
|
||||||
|
missing.push_back(key);
|
||||||
|
if (missing.empty())
|
||||||
|
return;
|
||||||
|
DynamicPrintConfig system_config;
|
||||||
|
std::string error;
|
||||||
|
if (!ensure_system_preset_resolver().resolve_system_preset(system_config, type, system_name, config_substitution_rule, error)) {
|
||||||
|
BOOST_LOG_TRIVIAL(warning) << boost::format("CLI: system preset '%1%' not resolved (%2%); keys missing from the project keep their defaults") % system_name % error;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (const std::string &key : missing)
|
||||||
|
if (const ConfigOption *opt = system_config.option(key)) {
|
||||||
|
m_print_config.set_key_value(key, opt->clone());
|
||||||
|
BOOST_LOG_TRIVIAL(info) << boost::format("CLI: %1% missing from the project, taken from '%2%': %3%") % key % system_name % opt->serialize();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (new_printer_name.empty())
|
||||||
|
fill_missing_project_keys(current_printer_system_name, Preset::TYPE_PRINTER);
|
||||||
|
if (new_process_name.empty())
|
||||||
|
fill_missing_project_keys(current_process_system_name, Preset::TYPE_PRINT);
|
||||||
|
|
||||||
std::vector<std::string>& different_settings = m_print_config.option<ConfigOptionStrings>("different_settings_to_system", true)->values;
|
std::vector<std::string>& different_settings = m_print_config.option<ConfigOptionStrings>("different_settings_to_system", true)->values;
|
||||||
std::vector<std::string>& inherits_group = m_print_config.option<ConfigOptionStrings>("inherits_group", true)->values;
|
std::vector<std::string>& inherits_group = m_print_config.option<ConfigOptionStrings>("inherits_group", true)->values;
|
||||||
inherits_group.resize(filament_count + 2, std::string());
|
inherits_group.resize(filament_count + 2, std::string());
|
||||||
|
|||||||
@@ -205,13 +205,6 @@ void AppConfig::set_defaults()
|
|||||||
if (get("seq_top_layer_only").empty())
|
if (get("seq_top_layer_only").empty())
|
||||||
set("seq_top_layer_only", "1");
|
set("seq_top_layer_only", "1");
|
||||||
|
|
||||||
// what the preview draws while the user drags it
|
|
||||||
{
|
|
||||||
const std::string mode = get("preview_reduced_detail_mode");
|
|
||||||
if (mode != "off" && mode != "solid" && mode != "shell")
|
|
||||||
set("preview_reduced_detail_mode", "off");
|
|
||||||
}
|
|
||||||
|
|
||||||
// ORCA: darken the layers the preview layer slider is not scrubbed to
|
// ORCA: darken the layers the preview layer slider is not scrubbed to
|
||||||
if (get("preview_dim_previous_layers").empty())
|
if (get("preview_dim_previous_layers").empty())
|
||||||
set_bool("preview_dim_previous_layers", false);
|
set_bool("preview_dim_previous_layers", false);
|
||||||
|
|||||||
@@ -575,18 +575,20 @@ bool PresetBundle::resolve_preset_config(DynamicPrintConfig &config, Preset::Typ
|
|||||||
const PresetBundle *PresetBundle::load_source_vendor(const boost::filesystem::path &root_dir,
|
const PresetBundle *PresetBundle::load_source_vendor(const boost::filesystem::path &root_dir,
|
||||||
const std::string &vendor_id,
|
const std::string &vendor_id,
|
||||||
ForwardCompatibilitySubstitutionRule compatibility_rule,
|
ForwardCompatibilitySubstitutionRule compatibility_rule,
|
||||||
std::string &error)
|
std::string &error, bool allow_cache)
|
||||||
{
|
{
|
||||||
auto key = std::make_tuple(root_dir.string(), vendor_id, compatibility_rule);
|
auto key = std::make_tuple(root_dir.string(), vendor_id, compatibility_rule, allow_cache);
|
||||||
if (auto it = m_source_vendor_bundles.find(key); it != m_source_vendor_bundles.end())
|
if (auto it = m_source_vendor_bundles.find(key); it != m_source_vendor_bundles.end())
|
||||||
return it->second.get();
|
return it->second.get();
|
||||||
|
|
||||||
// The library loads with no base of its own, so the tree a vendor inherits from
|
// The library loads with no base of its own, so the tree a vendor inherits from
|
||||||
// is the same one that resolves the library's own presets.
|
// is the same one that resolves the library's own presets.
|
||||||
const PresetBundle *library = nullptr;
|
const std::string library_file = std::string(ORCA_FILAMENT_LIBRARY);
|
||||||
|
const PresetBundle *library = nullptr;
|
||||||
if (vendor_id != ORCA_FILAMENT_LIBRARY &&
|
if (vendor_id != ORCA_FILAMENT_LIBRARY &&
|
||||||
boost::filesystem::is_regular_file(root_dir / (std::string(ORCA_FILAMENT_LIBRARY) + ".json"))) {
|
(boost::filesystem::is_regular_file(root_dir / (library_file + ".json")) ||
|
||||||
library = load_source_vendor(root_dir, ORCA_FILAMENT_LIBRARY, compatibility_rule, error);
|
(allow_cache && boost::filesystem::is_regular_file(root_dir / (library_file + ".opc"))))) {
|
||||||
|
library = load_source_vendor(root_dir, ORCA_FILAMENT_LIBRARY, compatibility_rule, error, allow_cache);
|
||||||
if (library == nullptr) {
|
if (library == nullptr) {
|
||||||
error = "OrcaFilamentLibrary contains invalid presets";
|
error = "OrcaFilamentLibrary contains invalid presets";
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@@ -595,7 +597,7 @@ const PresetBundle *PresetBundle::load_source_vendor(const boost::filesystem::pa
|
|||||||
|
|
||||||
auto bundle = std::make_unique<PresetBundle>();
|
auto bundle = std::make_unique<PresetBundle>();
|
||||||
bundle->m_preserve_vendor_source_paths = true;
|
bundle->m_preserve_vendor_source_paths = true;
|
||||||
bundle->load_vendor_configs_from_json(root_dir.string(), vendor_id, LoadSystem, compatibility_rule, library, false);
|
bundle->load_vendor_configs_from_json(root_dir.string(), vendor_id, LoadSystem, compatibility_rule, library, allow_cache);
|
||||||
if (bundle->error_count() != 0) {
|
if (bundle->error_count() != 0) {
|
||||||
error = "Vendor bundle contains invalid presets";
|
error = "Vendor bundle contains invalid presets";
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@@ -638,6 +640,43 @@ bool PresetBundle::resolve_preset_config_type(DynamicPrintConfig &config, Preset
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool PresetBundle::resolve_system_preset(DynamicPrintConfig &config, Preset::Type type, const std::string &name,
|
||||||
|
ForwardCompatibilitySubstitutionRule compatibility_rule, std::string &error)
|
||||||
|
{
|
||||||
|
const std::string vendor_id = find_preset_vendor(name, type);
|
||||||
|
if (vendor_id.empty()) {
|
||||||
|
error = "No vendor lists the preset";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Release builds ship a vendor as its preset cache alone, without the profile JSONs.
|
||||||
|
auto installed = [&vendor_id](const fs::path &root) {
|
||||||
|
return fs::is_regular_file(root / (vendor_id + ".json")) || fs::is_regular_file(root / (vendor_id + ".opc"));
|
||||||
|
};
|
||||||
|
fs::path root_dir = fs::path(data_dir()) / PRESET_SYSTEM_DIR;
|
||||||
|
if (!installed(root_dir))
|
||||||
|
root_dir = fs::path(resources_dir()) / PRESET_PROFILES_DIR;
|
||||||
|
const bool cache_only = !fs::is_regular_file(root_dir / (vendor_id + ".json"));
|
||||||
|
|
||||||
|
try {
|
||||||
|
const PresetBundle *vendor = load_source_vendor(root_dir, vendor_id, compatibility_rule, error, cache_only);
|
||||||
|
if (vendor == nullptr)
|
||||||
|
return false;
|
||||||
|
const PresetCollection &collection = type == Preset::TYPE_PRINTER ? vendor->printers :
|
||||||
|
type == Preset::TYPE_PRINT ? vendor->prints : vendor->filaments;
|
||||||
|
const Preset *preset = collection.find_preset(name, false);
|
||||||
|
if (preset == nullptr) {
|
||||||
|
error = "Preset was not found in its vendor bundle";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
config = preset->config;
|
||||||
|
} catch (const std::exception &ex) {
|
||||||
|
error = ex.what();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
error.clear();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
PresetBundle::PresetBundle(const PresetBundle &rhs)
|
PresetBundle::PresetBundle(const PresetBundle &rhs)
|
||||||
{
|
{
|
||||||
*this = rhs;
|
*this = rhs;
|
||||||
|
|||||||
@@ -273,6 +273,10 @@ public:
|
|||||||
const std::string &source_file,
|
const std::string &source_file,
|
||||||
ForwardCompatibilitySubstitutionRule compatibility_rule,
|
ForwardCompatibilitySubstitutionRule compatibility_rule,
|
||||||
std::string &error, bool allow_source_manifest = true);
|
std::string &error, bool allow_source_manifest = true);
|
||||||
|
// Resolve a system preset by name. The vendor tree is read from data_dir()/system when installed
|
||||||
|
// there, as the GUI reads it, and from the bundled profiles otherwise.
|
||||||
|
bool resolve_system_preset(DynamicPrintConfig &config, Preset::Type type, const std::string &name,
|
||||||
|
ForwardCompatibilitySubstitutionRule compatibility_rule, std::string &error);
|
||||||
|
|
||||||
// Load selections (current print, current filaments, current printer) from config.ini
|
// Load selections (current print, current filaments, current printer) from config.ini
|
||||||
// This is done just once on application start up.
|
// This is done just once on application start up.
|
||||||
@@ -663,13 +667,14 @@ private:
|
|||||||
// Vendor trees loaded by resolve_preset_config's manifest path, so every preset
|
// Vendor trees loaded by resolve_preset_config's manifest path, so every preset
|
||||||
// resolved through this bundle shares one load per source root and vendor. The
|
// resolved through this bundle shares one load per source root and vendor. The
|
||||||
// filament library is one such tree, shared by every vendor under its root.
|
// filament library is one such tree, shared by every vendor under its root.
|
||||||
std::map<std::tuple<std::string, std::string, ForwardCompatibilitySubstitutionRule>, std::unique_ptr<PresetBundle>>
|
// A tree read from its preset cache is kept apart: its presets carry no source file.
|
||||||
|
std::map<std::tuple<std::string, std::string, ForwardCompatibilitySubstitutionRule, bool>, std::unique_ptr<PresetBundle>>
|
||||||
m_source_vendor_bundles;
|
m_source_vendor_bundles;
|
||||||
|
|
||||||
const PresetBundle *load_source_vendor(const boost::filesystem::path &root_dir,
|
const PresetBundle *load_source_vendor(const boost::filesystem::path &root_dir,
|
||||||
const std::string &vendor_id,
|
const std::string &vendor_id,
|
||||||
ForwardCompatibilitySubstitutionRule compatibility_rule,
|
ForwardCompatibilitySubstitutionRule compatibility_rule,
|
||||||
std::string &error);
|
std::string &error, bool allow_cache = false);
|
||||||
|
|
||||||
// Orca: validation only - flag any printer with two or more compatible
|
// Orca: validation only - flag any printer with two or more compatible
|
||||||
// filament presets sharing one filament_id (ambiguous AMS subtype match).
|
// filament presets sharing one filament_id (ambiguous AMS subtype match).
|
||||||
|
|||||||
@@ -158,21 +158,6 @@ enum class EGCodeExtrusionRole : uint8_t
|
|||||||
|
|
||||||
static constexpr std::size_t GCODE_EXTRUSION_ROLES_COUNT = static_cast<std::size_t>(EGCodeExtrusionRole::COUNT);
|
static constexpr std::size_t GCODE_EXTRUSION_ROLES_COUNT = static_cast<std::size_t>(EGCodeExtrusionRole::COUNT);
|
||||||
|
|
||||||
//
|
|
||||||
// What the reduced set, drawn while the user is dragging, holds in place of the full toolpaths
|
|
||||||
//
|
|
||||||
enum class EReducedDetailMode : uint8_t
|
|
||||||
{
|
|
||||||
// nothing: no reduced set is built
|
|
||||||
Off,
|
|
||||||
// only the bottom and top layers of the visible range, for a caller that draws the print
|
|
||||||
// itself some other way
|
|
||||||
EndLayersOnly,
|
|
||||||
// every role but the infill under the skins and the gap fill: what can be seen from outside
|
|
||||||
ShellOnly,
|
|
||||||
COUNT
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Option types
|
// Option types
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -114,19 +114,6 @@ public:
|
|||||||
//
|
//
|
||||||
bool is_dim_previous_layers() const;
|
bool is_dim_previous_layers() const;
|
||||||
void set_dim_previous_layers(bool value);
|
void set_dim_previous_layers(bool value);
|
||||||
//
|
|
||||||
// The reduced set drawn while the user drags: what the mode keeps, and always the bottom and
|
|
||||||
// top layers of the visible range. While a mode is set it is built alongside the full set, so
|
|
||||||
// set_reduced_detail() rebuilds nothing. Ignored on the OpenGL ES path.
|
|
||||||
//
|
|
||||||
EReducedDetailMode get_reduced_detail_mode() const;
|
|
||||||
void set_reduced_detail_mode(EReducedDetailMode mode);
|
|
||||||
// Whether the shell mode hides the infill roles; off for a profile that leaves them on the
|
|
||||||
// surface, with no top or bottom shell or no walls.
|
|
||||||
bool get_reduced_detail_hide_infill() const;
|
|
||||||
void set_reduced_detail_hide_infill(bool value);
|
|
||||||
void set_reduced_detail(bool value);
|
|
||||||
bool is_reduced_detail() const;
|
|
||||||
float get_dim_previous_layers_brightness() const;
|
float get_dim_previous_layers_brightness() const;
|
||||||
void set_dim_previous_layers_brightness(float value);
|
void set_dim_previous_layers_brightness(float value);
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -25,11 +25,6 @@ struct Settings
|
|||||||
// ORCA: how bright those darkened layers are rendered, 1.0 = unchanged, 0.0 = black
|
// ORCA: how bright those darkened layers are rendered, 1.0 = unchanged, 0.0 = black
|
||||||
float dim_previous_layers_brightness{ 0.4f };
|
float dim_previous_layers_brightness{ 0.4f };
|
||||||
bool spiral_vase_mode{ false };
|
bool spiral_vase_mode{ false };
|
||||||
// what the reduced set holds and whether it is drawn. Ignored on the OpenGL ES path.
|
|
||||||
EReducedDetailMode reduced_detail_mode{ EReducedDetailMode::Off };
|
|
||||||
// whether the shell mode may hide the infill roles: false when the profile leaves them on the surface
|
|
||||||
bool reduced_detail_hide_infill{ true };
|
|
||||||
bool reduced_detail{ false };
|
|
||||||
//
|
//
|
||||||
// Required update flags
|
// Required update flags
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -92,36 +92,6 @@ bool Viewer::is_dim_previous_layers() const
|
|||||||
return m_impl->is_dim_previous_layers();
|
return m_impl->is_dim_previous_layers();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Viewer::set_reduced_detail(bool value)
|
|
||||||
{
|
|
||||||
m_impl->set_reduced_detail(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Viewer::is_reduced_detail() const
|
|
||||||
{
|
|
||||||
return m_impl->is_reduced_detail();
|
|
||||||
}
|
|
||||||
|
|
||||||
EReducedDetailMode Viewer::get_reduced_detail_mode() const
|
|
||||||
{
|
|
||||||
return m_impl->get_reduced_detail_mode();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Viewer::set_reduced_detail_mode(EReducedDetailMode mode)
|
|
||||||
{
|
|
||||||
m_impl->set_reduced_detail_mode(mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Viewer::get_reduced_detail_hide_infill() const
|
|
||||||
{
|
|
||||||
return m_impl->get_reduced_detail_hide_infill();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Viewer::set_reduced_detail_hide_infill(bool value)
|
|
||||||
{
|
|
||||||
m_impl->set_reduced_detail_hide_infill(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Viewer::set_dim_previous_layers(bool value)
|
void Viewer::set_dim_previous_layers(bool value)
|
||||||
{
|
{
|
||||||
m_impl->set_dim_previous_layers(value);
|
m_impl->set_dim_previous_layers(value);
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
#include <cfloat>
|
|
||||||
|
|
||||||
namespace libvgcode {
|
namespace libvgcode {
|
||||||
|
|
||||||
@@ -902,17 +901,9 @@ void ViewerImpl::reset()
|
|||||||
#else
|
#else
|
||||||
m_enabled_segments_count = 0;
|
m_enabled_segments_count = 0;
|
||||||
m_enabled_options_count = 0;
|
m_enabled_options_count = 0;
|
||||||
m_enabled_segments_reduced_count = 0;
|
|
||||||
m_enabled_options_reduced_count = 0;
|
|
||||||
m_enabled_segments_reduced_tex_size = 0;
|
|
||||||
m_enabled_options_reduced_tex_size = 0;
|
|
||||||
|
|
||||||
m_settings_used_for_ranges = std::nullopt;
|
m_settings_used_for_ranges = std::nullopt;
|
||||||
|
|
||||||
delete_textures(m_enabled_options_reduced_tex_id);
|
|
||||||
delete_buffers(m_enabled_options_reduced_buf_id);
|
|
||||||
delete_textures(m_enabled_segments_reduced_tex_id);
|
|
||||||
delete_buffers(m_enabled_segments_reduced_buf_id);
|
|
||||||
delete_textures(m_enabled_options_tex_id);
|
delete_textures(m_enabled_options_tex_id);
|
||||||
delete_buffers(m_enabled_options_buf_id);
|
delete_buffers(m_enabled_options_buf_id);
|
||||||
delete_textures(m_enabled_segments_tex_id);
|
delete_textures(m_enabled_segments_tex_id);
|
||||||
@@ -1172,17 +1163,6 @@ void ViewerImpl::load(GCodeInputData&& gcode_data)
|
|||||||
glsafe(glGenTextures(1, &m_enabled_options_tex_id));
|
glsafe(glGenTextures(1, &m_enabled_options_tex_id));
|
||||||
glsafe(glBindTexture(GL_TEXTURE_BUFFER, m_enabled_options_tex_id));
|
glsafe(glBindTexture(GL_TEXTURE_BUFFER, m_enabled_options_tex_id));
|
||||||
|
|
||||||
// create (but do not fill) the reduced counterparts of the two buffers above
|
|
||||||
glsafe(glGenBuffers(1, &m_enabled_segments_reduced_buf_id));
|
|
||||||
glsafe(glBindBuffer(GL_TEXTURE_BUFFER, m_enabled_segments_reduced_buf_id));
|
|
||||||
glsafe(glGenTextures(1, &m_enabled_segments_reduced_tex_id));
|
|
||||||
glsafe(glBindTexture(GL_TEXTURE_BUFFER, m_enabled_segments_reduced_tex_id));
|
|
||||||
|
|
||||||
glsafe(glGenBuffers(1, &m_enabled_options_reduced_buf_id));
|
|
||||||
glsafe(glBindBuffer(GL_TEXTURE_BUFFER, m_enabled_options_reduced_buf_id));
|
|
||||||
glsafe(glGenTextures(1, &m_enabled_options_reduced_tex_id));
|
|
||||||
glsafe(glBindTexture(GL_TEXTURE_BUFFER, m_enabled_options_reduced_tex_id));
|
|
||||||
|
|
||||||
glsafe(glBindBuffer(GL_TEXTURE_BUFFER, 0));
|
glsafe(glBindBuffer(GL_TEXTURE_BUFFER, 0));
|
||||||
glsafe(glBindTexture(GL_TEXTURE_BUFFER, old_bound_texture));
|
glsafe(glBindTexture(GL_TEXTURE_BUFFER, old_bound_texture));
|
||||||
#endif // ENABLE_OPENGL_ES
|
#endif // ENABLE_OPENGL_ES
|
||||||
@@ -1194,18 +1174,6 @@ void ViewerImpl::load(GCodeInputData&& gcode_data)
|
|||||||
update_colors();
|
update_colors();
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef ENABLE_OPENGL_ES
|
|
||||||
// the roles that lie under a skin or between walls, never seen from outside the print
|
|
||||||
static bool is_hidden_in_shell(EGCodeExtrusionRole role)
|
|
||||||
{
|
|
||||||
return role == EGCodeExtrusionRole::InternalInfill ||
|
|
||||||
role == EGCodeExtrusionRole::SolidInfill ||
|
|
||||||
role == EGCodeExtrusionRole::InternalBridgeInfill ||
|
|
||||||
role == EGCodeExtrusionRole::GapFill;
|
|
||||||
}
|
|
||||||
#endif // ENABLE_OPENGL_ES
|
|
||||||
|
|
||||||
|
|
||||||
void ViewerImpl::update_enabled_entities()
|
void ViewerImpl::update_enabled_entities()
|
||||||
{
|
{
|
||||||
if (m_vertices.empty())
|
if (m_vertices.empty())
|
||||||
@@ -1213,17 +1181,6 @@ void ViewerImpl::update_enabled_entities()
|
|||||||
|
|
||||||
std::vector<uint32_t> enabled_segments;
|
std::vector<uint32_t> enabled_segments;
|
||||||
std::vector<uint32_t> enabled_options;
|
std::vector<uint32_t> enabled_options;
|
||||||
#ifndef ENABLE_OPENGL_ES
|
|
||||||
// the reduced set is filled by the same walk, so switching to it costs no rebuild. Whatever the
|
|
||||||
// mode leaves out, the bottom and top layers of the visible range are kept whole: they are the
|
|
||||||
// surfaces the range cuts open
|
|
||||||
const EReducedDetailMode reduced_mode = m_settings.reduced_detail_mode;
|
|
||||||
const bool build_reduced = reduced_mode != EReducedDetailMode::Off;
|
|
||||||
const bool hide_infill = m_settings.reduced_detail_hide_infill;
|
|
||||||
std::vector<uint32_t> enabled_segments_reduced;
|
|
||||||
std::vector<uint32_t> enabled_options_reduced;
|
|
||||||
const Interval& layers_range = m_layers.get_view_range();
|
|
||||||
#endif // ENABLE_OPENGL_ES
|
|
||||||
Interval range = m_view_range.get_visible();
|
Interval range = m_view_range.get_visible();
|
||||||
|
|
||||||
// when top layer only visualization is enabled, we need to render
|
// when top layer only visualization is enabled, we need to render
|
||||||
@@ -1271,20 +1228,6 @@ void ViewerImpl::update_enabled_entities()
|
|||||||
enabled_options.push_back(static_cast<uint32_t>(i));
|
enabled_options.push_back(static_cast<uint32_t>(i));
|
||||||
else
|
else
|
||||||
enabled_segments.push_back(static_cast<uint32_t>(i));
|
enabled_segments.push_back(static_cast<uint32_t>(i));
|
||||||
|
|
||||||
#ifndef ENABLE_OPENGL_ES
|
|
||||||
if (build_reduced) {
|
|
||||||
const bool end_layer = v.layer_id == layers_range[0] || v.layer_id == layers_range[1];
|
|
||||||
if (end_layer)
|
|
||||||
(v.is_option() ? enabled_options_reduced : enabled_segments_reduced).push_back(static_cast<uint32_t>(i));
|
|
||||||
else if (reduced_mode == EReducedDetailMode::ShellOnly) {
|
|
||||||
if (v.is_option())
|
|
||||||
enabled_options_reduced.push_back(static_cast<uint32_t>(i));
|
|
||||||
else if (!v.is_extrusion() || !hide_infill || !is_hidden_in_shell(v.role))
|
|
||||||
enabled_segments_reduced.push_back(static_cast<uint32_t>(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif // ENABLE_OPENGL_ES
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ENABLE_OPENGL_ES
|
#ifdef ENABLE_OPENGL_ES
|
||||||
@@ -1313,23 +1256,6 @@ void ViewerImpl::update_enabled_entities()
|
|||||||
else
|
else
|
||||||
glsafe(glBufferData(GL_TEXTURE_BUFFER, 0, nullptr, GL_STATIC_DRAW));
|
glsafe(glBufferData(GL_TEXTURE_BUFFER, 0, nullptr, GL_STATIC_DRAW));
|
||||||
|
|
||||||
m_enabled_segments_reduced_count = enabled_segments_reduced.size();
|
|
||||||
m_enabled_options_reduced_count = enabled_options_reduced.size();
|
|
||||||
m_enabled_segments_reduced_tex_size = enabled_segments_reduced.size() * sizeof(uint32_t);
|
|
||||||
m_enabled_options_reduced_tex_size = enabled_options_reduced.size() * sizeof(uint32_t);
|
|
||||||
|
|
||||||
// uploaded even when nothing was built, so that the last reduced set is released as soon as
|
|
||||||
// the preference is switched off
|
|
||||||
assert(m_enabled_segments_reduced_buf_id > 0);
|
|
||||||
glsafe(glBindBuffer(GL_TEXTURE_BUFFER, m_enabled_segments_reduced_buf_id));
|
|
||||||
glsafe(glBufferData(GL_TEXTURE_BUFFER, m_enabled_segments_reduced_tex_size,
|
|
||||||
enabled_segments_reduced.empty() ? nullptr : enabled_segments_reduced.data(), GL_STATIC_DRAW));
|
|
||||||
|
|
||||||
assert(m_enabled_options_reduced_buf_id > 0);
|
|
||||||
glsafe(glBindBuffer(GL_TEXTURE_BUFFER, m_enabled_options_reduced_buf_id));
|
|
||||||
glsafe(glBufferData(GL_TEXTURE_BUFFER, m_enabled_options_reduced_tex_size,
|
|
||||||
enabled_options_reduced.empty() ? nullptr : enabled_options_reduced.data(), GL_STATIC_DRAW));
|
|
||||||
|
|
||||||
glsafe(glBindBuffer(GL_TEXTURE_BUFFER, 0));
|
glsafe(glBindBuffer(GL_TEXTURE_BUFFER, 0));
|
||||||
#endif // ENABLE_OPENGL_ES
|
#endif // ENABLE_OPENGL_ES
|
||||||
|
|
||||||
@@ -1537,23 +1463,6 @@ void ViewerImpl::toggle_top_layer_only_view_range()
|
|||||||
update_colors_texture();
|
update_colors_texture();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Both decide which vertices land in the reduced set, so the sets are rebuilt.
|
|
||||||
void ViewerImpl::set_reduced_detail_mode(EReducedDetailMode mode)
|
|
||||||
{
|
|
||||||
if (m_settings.reduced_detail_mode == mode)
|
|
||||||
return;
|
|
||||||
m_settings.reduced_detail_mode = mode;
|
|
||||||
m_settings.update_enabled_entities = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ViewerImpl::set_reduced_detail_hide_infill(bool value)
|
|
||||||
{
|
|
||||||
if (m_settings.reduced_detail_hide_infill == value)
|
|
||||||
return;
|
|
||||||
m_settings.reduced_detail_hide_infill = value;
|
|
||||||
m_settings.update_enabled_entities = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ORCA: enable/disable darkening of the layers the layer slider is not scrubbed to
|
// ORCA: enable/disable darkening of the layers the layer slider is not scrubbed to
|
||||||
void ViewerImpl::set_dim_previous_layers(bool value)
|
void ViewerImpl::set_dim_previous_layers(bool value)
|
||||||
{
|
{
|
||||||
@@ -1947,8 +1856,6 @@ size_t ViewerImpl::get_used_gpu_memory() const
|
|||||||
ret += m_colors_tex_size;
|
ret += m_colors_tex_size;
|
||||||
ret += m_enabled_segments_tex_size;
|
ret += m_enabled_segments_tex_size;
|
||||||
ret += m_enabled_options_tex_size;
|
ret += m_enabled_options_tex_size;
|
||||||
ret += m_enabled_segments_reduced_tex_size;
|
|
||||||
ret += m_enabled_options_reduced_tex_size;
|
|
||||||
#endif // ENABLE_OPENGL_ES
|
#endif // ENABLE_OPENGL_ES
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -2165,8 +2072,7 @@ void ViewerImpl::render_segments(const Mat4x4& view_matrix, const Mat4x4& projec
|
|||||||
#ifdef ENABLE_OPENGL_ES
|
#ifdef ENABLE_OPENGL_ES
|
||||||
if (m_texture_data.get_enabled_segments_count() == 0)
|
if (m_texture_data.get_enabled_segments_count() == 0)
|
||||||
#else
|
#else
|
||||||
const ActiveSet segments = active_segments();
|
if (m_enabled_segments_count == 0)
|
||||||
if (segments.count == 0)
|
|
||||||
#endif // ENABLE_OPENGL_ES
|
#endif // ENABLE_OPENGL_ES
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -2193,7 +2099,7 @@ void ViewerImpl::render_segments(const Mat4x4& view_matrix, const Mat4x4& projec
|
|||||||
const bool top_down = !m_rendering_shadow_casters && view_matrix[10] > 0.0f;
|
const bool top_down = !m_rendering_shadow_casters && view_matrix[10] > 0.0f;
|
||||||
glsafe(glUniform1i(m_uni_segments_reverse_order_id, top_down ? 1 : 0));
|
glsafe(glUniform1i(m_uni_segments_reverse_order_id, top_down ? 1 : 0));
|
||||||
#ifndef ENABLE_OPENGL_ES
|
#ifndef ENABLE_OPENGL_ES
|
||||||
glsafe(glUniform1i(m_uni_segments_instance_count_id, static_cast<int>(segments.count)));
|
glsafe(glUniform1i(m_uni_segments_instance_count_id, static_cast<int>(m_enabled_segments_count)));
|
||||||
#endif // ENABLE_OPENGL_ES
|
#endif // ENABLE_OPENGL_ES
|
||||||
// ORCA: realistic view. The depth pass writes the map it would otherwise read, so it shades
|
// ORCA: realistic view. The depth pass writes the map it would otherwise read, so it shades
|
||||||
// with the lookup off.
|
// with the lookup off.
|
||||||
@@ -2243,10 +2149,10 @@ void ViewerImpl::render_segments(const Mat4x4& view_matrix, const Mat4x4& projec
|
|||||||
glsafe(glBindTexture(GL_TEXTURE_BUFFER, m_colors_tex_id));
|
glsafe(glBindTexture(GL_TEXTURE_BUFFER, m_colors_tex_id));
|
||||||
glsafe(glTexBuffer(GL_TEXTURE_BUFFER, GL_R32F, m_colors_buf_id));
|
glsafe(glTexBuffer(GL_TEXTURE_BUFFER, GL_R32F, m_colors_buf_id));
|
||||||
glsafe(glActiveTexture(GL_TEXTURE3));
|
glsafe(glActiveTexture(GL_TEXTURE3));
|
||||||
glsafe(glBindTexture(GL_TEXTURE_BUFFER, segments.tex_id));
|
glsafe(glBindTexture(GL_TEXTURE_BUFFER, m_enabled_segments_tex_id));
|
||||||
glsafe(glTexBuffer(GL_TEXTURE_BUFFER, GL_R32UI, segments.buf_id));
|
glsafe(glTexBuffer(GL_TEXTURE_BUFFER, GL_R32UI, m_enabled_segments_buf_id));
|
||||||
|
|
||||||
m_segment_template.render(segments.count);
|
m_segment_template.render(m_enabled_segments_count);
|
||||||
#endif // ENABLE_OPENGL_ES
|
#endif // ENABLE_OPENGL_ES
|
||||||
|
|
||||||
if (curr_cull_face)
|
if (curr_cull_face)
|
||||||
@@ -2272,8 +2178,7 @@ void ViewerImpl::render_options(const Mat4x4& view_matrix, const Mat4x4& project
|
|||||||
#ifdef ENABLE_OPENGL_ES
|
#ifdef ENABLE_OPENGL_ES
|
||||||
if (m_texture_data.get_enabled_options_count() == 0)
|
if (m_texture_data.get_enabled_options_count() == 0)
|
||||||
#else
|
#else
|
||||||
const ActiveSet options = active_options();
|
if (m_enabled_options_count == 0)
|
||||||
if (options.count == 0)
|
|
||||||
#endif // ENABLE_OPENGL_ES
|
#endif // ENABLE_OPENGL_ES
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -2331,10 +2236,10 @@ void ViewerImpl::render_options(const Mat4x4& view_matrix, const Mat4x4& project
|
|||||||
glsafe(glBindTexture(GL_TEXTURE_BUFFER, m_colors_tex_id));
|
glsafe(glBindTexture(GL_TEXTURE_BUFFER, m_colors_tex_id));
|
||||||
glsafe(glTexBuffer(GL_TEXTURE_BUFFER, GL_R32F, m_colors_buf_id));
|
glsafe(glTexBuffer(GL_TEXTURE_BUFFER, GL_R32F, m_colors_buf_id));
|
||||||
glsafe(glActiveTexture(GL_TEXTURE3));
|
glsafe(glActiveTexture(GL_TEXTURE3));
|
||||||
glsafe(glBindTexture(GL_TEXTURE_BUFFER, options.tex_id));
|
glsafe(glBindTexture(GL_TEXTURE_BUFFER, m_enabled_options_tex_id));
|
||||||
glsafe(glTexBuffer(GL_TEXTURE_BUFFER, GL_R32UI, options.buf_id));
|
glsafe(glTexBuffer(GL_TEXTURE_BUFFER, GL_R32UI, m_enabled_options_buf_id));
|
||||||
|
|
||||||
m_option_template.render(options.count);
|
m_option_template.render(m_enabled_options_count);
|
||||||
#endif // ENABLE_OPENGL_ES
|
#endif // ENABLE_OPENGL_ES
|
||||||
|
|
||||||
if (!curr_cull_face)
|
if (!curr_cull_face)
|
||||||
|
|||||||
@@ -109,21 +109,6 @@ public:
|
|||||||
// 0.0 = black
|
// 0.0 = black
|
||||||
bool is_dim_previous_layers() const { return m_settings.dim_previous_layers; }
|
bool is_dim_previous_layers() const { return m_settings.dim_previous_layers; }
|
||||||
void set_dim_previous_layers(bool value);
|
void set_dim_previous_layers(bool value);
|
||||||
//
|
|
||||||
// Draw from the reduced set; it is already built, so this is just a buffer binding.
|
|
||||||
//
|
|
||||||
void set_reduced_detail(bool value) {
|
|
||||||
#ifdef ENABLE_OPENGL_ES
|
|
||||||
// no reduced set is built on OpenGL ES
|
|
||||||
value = false;
|
|
||||||
#endif // ENABLE_OPENGL_ES
|
|
||||||
m_settings.reduced_detail = value;
|
|
||||||
}
|
|
||||||
bool is_reduced_detail() const { return m_settings.reduced_detail; }
|
|
||||||
EReducedDetailMode get_reduced_detail_mode() const { return m_settings.reduced_detail_mode; }
|
|
||||||
void set_reduced_detail_mode(EReducedDetailMode mode);
|
|
||||||
bool get_reduced_detail_hide_infill() const { return m_settings.reduced_detail_hide_infill; }
|
|
||||||
void set_reduced_detail_hide_infill(bool value);
|
|
||||||
float get_dim_previous_layers_brightness() const { return m_settings.dim_previous_layers_brightness; }
|
float get_dim_previous_layers_brightness() const { return m_settings.dim_previous_layers_brightness; }
|
||||||
void set_dim_previous_layers_brightness(float value);
|
void set_dim_previous_layers_brightness(float value);
|
||||||
|
|
||||||
@@ -516,15 +501,6 @@ private:
|
|||||||
unsigned int m_enabled_options_tex_id{ 0 };
|
unsigned int m_enabled_options_tex_id{ 0 };
|
||||||
size_t m_enabled_options_count{ 0 };
|
size_t m_enabled_options_count{ 0 };
|
||||||
//
|
//
|
||||||
// OpenGL buffers to store the reduced set drawn while Settings::reduced_detail is set
|
|
||||||
//
|
|
||||||
unsigned int m_enabled_segments_reduced_buf_id{ 0 };
|
|
||||||
unsigned int m_enabled_segments_reduced_tex_id{ 0 };
|
|
||||||
size_t m_enabled_segments_reduced_count{ 0 };
|
|
||||||
unsigned int m_enabled_options_reduced_buf_id{ 0 };
|
|
||||||
unsigned int m_enabled_options_reduced_tex_id{ 0 };
|
|
||||||
size_t m_enabled_options_reduced_count{ 0 };
|
|
||||||
//
|
|
||||||
// Caches for size of data sent to gpu, in bytes
|
// Caches for size of data sent to gpu, in bytes
|
||||||
//
|
//
|
||||||
size_t m_positions_tex_size{ 0 };
|
size_t m_positions_tex_size{ 0 };
|
||||||
@@ -532,27 +508,6 @@ private:
|
|||||||
size_t m_colors_tex_size{ 0 };
|
size_t m_colors_tex_size{ 0 };
|
||||||
size_t m_enabled_segments_tex_size{ 0 };
|
size_t m_enabled_segments_tex_size{ 0 };
|
||||||
size_t m_enabled_options_tex_size{ 0 };
|
size_t m_enabled_options_tex_size{ 0 };
|
||||||
size_t m_enabled_segments_reduced_tex_size{ 0 };
|
|
||||||
size_t m_enabled_options_reduced_tex_size{ 0 };
|
|
||||||
|
|
||||||
// The set the next draw reads from: the reduced one while dragging, if one is built.
|
|
||||||
bool use_reduced_set() const { return m_settings.reduced_detail && m_settings.reduced_detail_mode != EReducedDetailMode::Off; }
|
|
||||||
struct ActiveSet
|
|
||||||
{
|
|
||||||
size_t count{ 0 };
|
|
||||||
unsigned int buf_id{ 0 };
|
|
||||||
unsigned int tex_id{ 0 };
|
|
||||||
};
|
|
||||||
ActiveSet active_segments() const {
|
|
||||||
if (use_reduced_set())
|
|
||||||
return { m_enabled_segments_reduced_count, m_enabled_segments_reduced_buf_id, m_enabled_segments_reduced_tex_id };
|
|
||||||
return { m_enabled_segments_count, m_enabled_segments_buf_id, m_enabled_segments_tex_id };
|
|
||||||
}
|
|
||||||
ActiveSet active_options() const {
|
|
||||||
if (use_reduced_set())
|
|
||||||
return { m_enabled_options_reduced_count, m_enabled_options_reduced_buf_id, m_enabled_options_reduced_tex_id };
|
|
||||||
return { m_enabled_options_count, m_enabled_options_buf_id, m_enabled_options_tex_id };
|
|
||||||
}
|
|
||||||
#endif // ENABLE_OPENGL_ES
|
#endif // ENABLE_OPENGL_ES
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -1267,8 +1267,6 @@ void GCodeViewer::load_as_gcode(const GCodeProcessorResult& gcode_result, const
|
|||||||
if (current_top_layer_only != required_top_layer_only)
|
if (current_top_layer_only != required_top_layer_only)
|
||||||
m_viewer.toggle_top_layer_only_view_range();
|
m_viewer.toggle_top_layer_only_view_range();
|
||||||
|
|
||||||
read_reduced_detail_preferences();
|
|
||||||
|
|
||||||
// ORCA: darken the layers the preview layer slider is not scrubbed to
|
// ORCA: darken the layers the preview layer slider is not scrubbed to
|
||||||
m_viewer.set_dim_previous_layers(get_app_config()->get_bool("preview_dim_previous_layers"));
|
m_viewer.set_dim_previous_layers(get_app_config()->get_bool("preview_dim_previous_layers"));
|
||||||
m_viewer.set_dim_previous_layers_brightness(0.01f * std::stoi(get_app_config()->get("preview_dim_previous_layers_brightness")));
|
m_viewer.set_dim_previous_layers_brightness(0.01f * std::stoi(get_app_config()->get("preview_dim_previous_layers_brightness")));
|
||||||
@@ -1492,10 +1490,6 @@ void GCodeViewer::load_as_gcode(const GCodeProcessorResult& gcode_result, const
|
|||||||
|
|
||||||
// BBS: data for rendering color arrangement recommendation
|
// BBS: data for rendering color arrangement recommendation
|
||||||
m_nozzle_nums = print.config().option<ConfigOptionFloats>("nozzle_diameter")->values.size();
|
m_nozzle_nums = print.config().option<ConfigOptionFloats>("nozzle_diameter")->values.size();
|
||||||
// the shell drag mode hides the infill only where the profile covers it: with no top or bottom
|
|
||||||
// shell, or no walls, the infill is the surface. Per-object settings are not consulted.
|
|
||||||
const PrintRegionConfig& region = print.default_region_config();
|
|
||||||
m_viewer.set_reduced_detail_hide_infill(region.top_shell_layers.value > 0 && region.bottom_shell_layers.value > 0 && region.wall_loops.value > 0);
|
|
||||||
// Orca hack: Hide filament group for non-bbl printers
|
// Orca hack: Hide filament group for non-bbl printers
|
||||||
if (!print.is_BBL_printer()) m_nozzle_nums = 1;
|
if (!print.is_BBL_printer()) m_nozzle_nums = 1;
|
||||||
std::vector<int> filament_maps = print.get_filament_maps();
|
std::vector<int> filament_maps = print.get_filament_maps();
|
||||||
@@ -1666,7 +1660,6 @@ void GCodeViewer::reset_shell()
|
|||||||
{
|
{
|
||||||
m_shells.volumes.clear();
|
m_shells.volumes.clear();
|
||||||
m_shells.print_id = -1;
|
m_shells.print_id = -1;
|
||||||
m_shells.with_wipe_tower = false;
|
|
||||||
m_shell_bounding_box = BoundingBoxf3();
|
m_shell_bounding_box = BoundingBoxf3();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1703,12 +1696,7 @@ void GCodeViewer::reset()
|
|||||||
void GCodeViewer::render_scene(int canvas_width, int canvas_height)
|
void GCodeViewer::render_scene(int canvas_width, int canvas_height)
|
||||||
{
|
{
|
||||||
glsafe(::glEnable(GL_DEPTH_TEST));
|
glsafe(::glEnable(GL_DEPTH_TEST));
|
||||||
// while dragging in the solid model mode, the objects stand in for their toolpaths, cut to the
|
render_shells(canvas_width, canvas_height);
|
||||||
// visible layer range; the toolpath set then holds only the range's bottom and top layers
|
|
||||||
if (m_viewer.is_reduced_detail() && solid_model_enabled())
|
|
||||||
render_solid_model(canvas_width, canvas_height);
|
|
||||||
else
|
|
||||||
render_shells(canvas_width, canvas_height);
|
|
||||||
|
|
||||||
if (m_viewer.get_extrusion_roles_count() == 0)
|
if (m_viewer.get_extrusion_roles_count() == 0)
|
||||||
return;
|
return;
|
||||||
@@ -2031,56 +2019,6 @@ void GCodeViewer::update_layers_slider_mode()
|
|||||||
// TODO m_layers_slider->SetModeAndOnlyExtruder(one_extruder_printed_model, only_extruder);
|
// TODO m_layers_slider->SetModeAndOnlyExtruder(one_extruder_printed_model, only_extruder);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GCodeViewer::set_interacting(bool interacting)
|
|
||||||
{
|
|
||||||
// with no shells to stand in for the toolpaths, the solid model would leave only the end layers
|
|
||||||
const bool usable = !solid_model_enabled() || !m_shells.volumes.empty();
|
|
||||||
m_viewer.set_reduced_detail(interacting && usable);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GCodeViewer::read_reduced_detail_preferences()
|
|
||||||
{
|
|
||||||
m_reduced_detail_mode = reduced_detail_mode_from_string(get_app_config()->get("preview_reduced_detail_mode"));
|
|
||||||
apply_reduced_detail_settings();
|
|
||||||
}
|
|
||||||
|
|
||||||
void GCodeViewer::apply_reduced_detail_settings()
|
|
||||||
{
|
|
||||||
m_viewer.set_reduced_detail_mode(m_reduced_detail_mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GCodeViewer::set_reduced_detail_mode(const std::string& mode)
|
|
||||||
{
|
|
||||||
const bool was_solid = solid_model_enabled();
|
|
||||||
m_reduced_detail_mode = reduced_detail_mode_from_string(mode);
|
|
||||||
apply_reduced_detail_settings();
|
|
||||||
reload_shells_if_solid_model_changed(was_solid);
|
|
||||||
}
|
|
||||||
|
|
||||||
libvgcode::EReducedDetailMode GCodeViewer::reduced_detail_mode_from_string(const std::string& mode)
|
|
||||||
{
|
|
||||||
if (mode == "solid")
|
|
||||||
return libvgcode::EReducedDetailMode::EndLayersOnly;
|
|
||||||
if (mode == "shell")
|
|
||||||
return libvgcode::EReducedDetailMode::ShellOnly;
|
|
||||||
return libvgcode::EReducedDetailMode::Off;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GCodeViewer::reload_shells_if_solid_model_changed(bool was_enabled)
|
|
||||||
{
|
|
||||||
if (was_enabled == solid_model_enabled() || m_shells.print_id == -1)
|
|
||||||
return;
|
|
||||||
// only the prime tower comes and goes with the mode: a full reload would drop the shells
|
|
||||||
// whenever the print has moved on since they were loaded, leaving the solid model nothing to draw
|
|
||||||
if (wxGetApp().plater() == nullptr)
|
|
||||||
return;
|
|
||||||
// the shells are loaded from the current plate's print, which is not the plater's own
|
|
||||||
const Print& print = wxGetApp().plater()->get_partplate_list().get_current_fff_print();
|
|
||||||
if (static_cast<int>(print.id().id) != m_shells.print_id)
|
|
||||||
return;
|
|
||||||
update_shell_wipe_tower(print, m_gl_data_initialized);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GCodeViewer::set_layers_z_range(const std::array<unsigned int, 2>& layers_z_range)
|
void GCodeViewer::set_layers_z_range(const std::array<unsigned int, 2>& layers_z_range)
|
||||||
{
|
{
|
||||||
m_viewer.set_layers_view_range(static_cast<uint32_t>(layers_z_range[0]), static_cast<uint32_t>(layers_z_range[1]));
|
m_viewer.set_layers_view_range(static_cast<uint32_t>(layers_z_range[0]), static_cast<uint32_t>(layers_z_range[1]));
|
||||||
@@ -2405,11 +2343,7 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const
|
|||||||
void GCodeViewer::load_shells(const Print& print, bool initialized, bool force_previewing)
|
void GCodeViewer::load_shells(const Print& print, bool initialized, bool force_previewing)
|
||||||
{
|
{
|
||||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": initialized=%1%, force_previewing=%2%")%initialized %force_previewing;
|
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": initialized=%1%, force_previewing=%2%")%initialized %force_previewing;
|
||||||
// the shells can load before the first G-code does, so the preferences are read here as well
|
|
||||||
read_reduced_detail_preferences();
|
|
||||||
if ((print.id().id == m_shells.print_id)&&(print.get_modified_count() == m_shells.print_modify_count)) {
|
if ((print.id().id == m_shells.print_id)&&(print.get_modified_count() == m_shells.print_modify_count)) {
|
||||||
// the prime tower comes and goes on its own, without reloading the objects
|
|
||||||
update_shell_wipe_tower(print, initialized);
|
|
||||||
//BBS: update force previewing logic
|
//BBS: update force previewing logic
|
||||||
if (force_previewing)
|
if (force_previewing)
|
||||||
m_shells.previewing = force_previewing;
|
m_shells.previewing = force_previewing;
|
||||||
@@ -2518,45 +2452,10 @@ void GCodeViewer::load_shells(const Print& print, bool initialized, bool force_p
|
|||||||
m_shells.print_id = print.id().id;
|
m_shells.print_id = print.id().id;
|
||||||
m_shells.print_modify_count = print.get_modified_count();
|
m_shells.print_modify_count = print.get_modified_count();
|
||||||
m_shells.previewing = true;
|
m_shells.previewing = true;
|
||||||
update_shell_wipe_tower(print, initialized);
|
|
||||||
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": shell loaded, id change to %1%, modify_count %2%, object count %3%, glvolume count %4%")
|
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": shell loaded, id change to %1%, modify_count %2%, object count %3%, glvolume count %4%")
|
||||||
% m_shells.print_id % m_shells.print_modify_count % object_count %m_shells.volumes.volumes.size();
|
% m_shells.print_id % m_shells.print_modify_count % object_count %m_shells.volumes.volumes.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
// The prime tower as it was sliced, so that the solid model shows what the print shows. It keeps its
|
|
||||||
// opaque colour, so it never appears among the translucent shells, and stays out of their bounding box.
|
|
||||||
void GCodeViewer::update_shell_wipe_tower(const Print& print, bool initialized)
|
|
||||||
{
|
|
||||||
const bool with_wipe_tower = solid_model_enabled() && print.is_step_done(psWipeTower) && print.wipe_tower_data().wipe_tower_mesh_data;
|
|
||||||
if (with_wipe_tower == m_shells.with_wipe_tower)
|
|
||||||
return;
|
|
||||||
m_shells.with_wipe_tower = with_wipe_tower;
|
|
||||||
GLVolumePtrs& volumes = m_shells.volumes.volumes;
|
|
||||||
if (!with_wipe_tower) {
|
|
||||||
volumes.erase(std::remove_if(volumes.begin(), volumes.end(), [](GLVolume* volume) {
|
|
||||||
if (!volume->is_wipe_tower)
|
|
||||||
return false;
|
|
||||||
delete volume;
|
|
||||||
return true;
|
|
||||||
}), volumes.end());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const PrintConfig& config = print.config();
|
|
||||||
const int plate_idx = print.get_plate_index();
|
|
||||||
const Vec3d plate_origin = print.get_plate_origin();
|
|
||||||
const float x = static_cast<float>(config.wipe_tower_x.get_at(plate_idx) + plate_origin.x());
|
|
||||||
const float y = static_cast<float>(config.wipe_tower_y.get_at(plate_idx) + plate_origin.y());
|
|
||||||
const size_t first_new = volumes.size();
|
|
||||||
m_shells.volumes.load_real_wipe_tower_preview(1000 + plate_idx, x, y, print.wipe_tower_data().wipe_tower_mesh_data->real_wipe_tower_mesh,
|
|
||||||
print.wipe_tower_data().wipe_tower_mesh_data->real_brim_mesh, true,
|
|
||||||
static_cast<float>(config.wipe_tower_rotation_angle), false, initialized);
|
|
||||||
for (size_t i = first_new; i < volumes.size(); ++i) {
|
|
||||||
volumes[i]->zoom_to_volumes = false;
|
|
||||||
volumes[i]->force_native_color = true;
|
|
||||||
volumes[i]->set_render_color();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GCodeViewer::render_toolpaths()
|
void GCodeViewer::render_toolpaths()
|
||||||
{
|
{
|
||||||
const Camera& camera = wxGetApp().plater()->get_camera();
|
const Camera& camera = wxGetApp().plater()->get_camera();
|
||||||
@@ -2757,50 +2656,6 @@ void GCodeViewer::render_shells(int canvas_width, int canvas_height)
|
|||||||
glsafe(::glDepthMask(GL_TRUE));
|
glsafe(::glDepthMask(GL_TRUE));
|
||||||
}
|
}
|
||||||
|
|
||||||
// The sliced objects and the prime tower drawn opaque, in their filament colours, cut to the
|
|
||||||
// visible layer range by the shader's z range. The toolpaths of the range's bottom and top layers
|
|
||||||
// are drawn afterwards and cap the cut.
|
|
||||||
void GCodeViewer::render_solid_model(int canvas_width, int canvas_height)
|
|
||||||
{
|
|
||||||
if (m_shells.volumes.empty())
|
|
||||||
return;
|
|
||||||
// gouraud_light has no z range, so it could not cut the model
|
|
||||||
GLShaderProgram* shader = wxGetApp().get_shader("gouraud");
|
|
||||||
if (shader == nullptr)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const libvgcode::Interval& layers = m_viewer.get_layers_view_range();
|
|
||||||
const float z_top = m_viewer.get_layer_z(layers[1]) - m_z_offset + 0.001f;
|
|
||||||
const float z_bottom = (layers[0] > 0) ? m_viewer.get_layer_z(layers[0] - 1) - m_z_offset - 0.001f : -FLT_MAX;
|
|
||||||
|
|
||||||
std::vector<float> alphas;
|
|
||||||
alphas.reserve(m_shells.volumes.volumes.size());
|
|
||||||
for (GLVolume* volume : m_shells.volumes.volumes) {
|
|
||||||
alphas.push_back(volume->color.a());
|
|
||||||
volume->color.a(1.0f);
|
|
||||||
volume->set_render_color();
|
|
||||||
}
|
|
||||||
m_shells.volumes.set_z_range(z_bottom, z_top);
|
|
||||||
// gouraud also clips by this plane, which nothing else sets on the shells
|
|
||||||
m_shells.volumes.set_clipping_plane(ClippingPlane::ClipsNothing().get_data());
|
|
||||||
|
|
||||||
shader->start_using();
|
|
||||||
// the 3D view leaves its shadow settings on the shared program
|
|
||||||
shader->set_uniform("shadow_intensity", 0.0f);
|
|
||||||
const Camera& camera = wxGetApp().plater()->get_camera();
|
|
||||||
shader->set_uniform("z_far", camera.get_far_z());
|
|
||||||
shader->set_uniform("z_near", camera.get_near_z());
|
|
||||||
m_shells.volumes.render(GLVolumeCollection::ERenderType::Opaque, false, camera.get_view_matrix(), camera.get_projection_matrix(), {canvas_width, canvas_height});
|
|
||||||
shader->stop_using();
|
|
||||||
|
|
||||||
m_shells.volumes.set_z_range(-FLT_MAX, FLT_MAX);
|
|
||||||
size_t k = 0;
|
|
||||||
for (GLVolume* volume : m_shells.volumes.volumes) {
|
|
||||||
volume->color.a(alphas[k++]);
|
|
||||||
volume->set_render_color();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//BBS
|
//BBS
|
||||||
void GCodeViewer::render_all_plates_stats(const std::vector<const GCodeProcessorResult*>& gcode_result_list, bool show /*= true*/) const {
|
void GCodeViewer::render_all_plates_stats(const std::vector<const GCodeProcessorResult*>& gcode_result_list, bool show /*= true*/) const {
|
||||||
if (!show)
|
if (!show)
|
||||||
|
|||||||
@@ -174,8 +174,6 @@ public:
|
|||||||
int print_id{-1};
|
int print_id{-1};
|
||||||
int print_modify_count{-1};
|
int print_modify_count{-1};
|
||||||
bool previewing{false};
|
bool previewing{false};
|
||||||
// the prime tower was loaded with the objects, for the solid model
|
|
||||||
bool with_wipe_tower{false};
|
|
||||||
};
|
};
|
||||||
//BBS
|
//BBS
|
||||||
ConflictResultOpt m_conflict_result;
|
ConflictResultOpt m_conflict_result;
|
||||||
@@ -236,17 +234,6 @@ private:
|
|||||||
|
|
||||||
bool m_legend_visible{ true };
|
bool m_legend_visible{ true };
|
||||||
bool m_legend_enabled{ true };
|
bool m_legend_enabled{ true };
|
||||||
// the reduced-detail preferences, pushed to libvgcode by apply_reduced_detail_settings()
|
|
||||||
libvgcode::EReducedDetailMode m_reduced_detail_mode{ libvgcode::EReducedDetailMode::Off };
|
|
||||||
void read_reduced_detail_preferences();
|
|
||||||
void apply_reduced_detail_settings();
|
|
||||||
static libvgcode::EReducedDetailMode reduced_detail_mode_from_string(const std::string& mode);
|
|
||||||
// in the solid model mode, the sliced objects are drawn as solid shapes instead of toolpaths
|
|
||||||
bool solid_model_enabled() const { return m_reduced_detail_mode == libvgcode::EReducedDetailMode::EndLayersOnly; }
|
|
||||||
void render_solid_model(int canvas_width, int canvas_height);
|
|
||||||
// the prime tower is only among the shells for the solid model, so it is added or removed when that changes
|
|
||||||
void reload_shells_if_solid_model_changed(bool was_enabled);
|
|
||||||
void update_shell_wipe_tower(const Print& print, bool initialized);
|
|
||||||
|
|
||||||
float m_legend_height;
|
float m_legend_height;
|
||||||
PrintEstimatedStatistics m_print_statistics;
|
PrintEstimatedStatistics m_print_statistics;
|
||||||
@@ -304,7 +291,7 @@ public:
|
|||||||
// void _render_calibration_thumbnail_internal(ThumbnailData& thumbnail_data, const ThumbnailsParams& thumbnail_params, PartPlateList& partplate_list, OpenGLManager& opengl_manager);
|
// void _render_calibration_thumbnail_internal(ThumbnailData& thumbnail_data, const ThumbnailsParams& thumbnail_params, PartPlateList& partplate_list, OpenGLManager& opengl_manager);
|
||||||
// void _render_calibration_thumbnail_framebuffer(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, PartPlateList& partplate_list, OpenGLManager& opengl_manager);
|
// void _render_calibration_thumbnail_framebuffer(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, PartPlateList& partplate_list, OpenGLManager& opengl_manager);
|
||||||
// void render_calibration_thumbnail(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, PartPlateList& partplate_list, OpenGLManager& opengl_manager);
|
// void render_calibration_thumbnail(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, PartPlateList& partplate_list, OpenGLManager& opengl_manager);
|
||||||
bool has_data() const { return m_viewer.get_extrusion_roles_count() != 0; }
|
bool has_data() const { return !m_viewer.get_extrusion_roles().empty(); }
|
||||||
|
|
||||||
bool can_export_toolpaths() const;
|
bool can_export_toolpaths() const;
|
||||||
std::vector<int> get_plater_extruder();
|
std::vector<int> get_plater_extruder();
|
||||||
@@ -376,14 +363,6 @@ public:
|
|||||||
void set_dim_previous_layers_brightness(float value) { m_viewer.set_dim_previous_layers_brightness(value); }
|
void set_dim_previous_layers_brightness(float value) { m_viewer.set_dim_previous_layers_brightness(value); }
|
||||||
float get_dim_previous_layers_brightness() const { return m_viewer.get_dim_previous_layers_brightness(); }
|
float get_dim_previous_layers_brightness() const { return m_viewer.get_dim_previous_layers_brightness(); }
|
||||||
|
|
||||||
// whether the mouse is holding either slider's handle
|
|
||||||
bool is_slider_dragging() const { return m_layers_slider->is_dragging() || m_moves_slider->is_dragging(); }
|
|
||||||
// while the user drags the camera or a slider, draw the reduced set, if the preference asks for one
|
|
||||||
void set_interacting(bool interacting);
|
|
||||||
bool is_reduced_detail() const { return m_viewer.is_reduced_detail(); }
|
|
||||||
// the preference's string value: "off", "solid" or "shell"
|
|
||||||
void set_reduced_detail_mode(const std::string& mode);
|
|
||||||
|
|
||||||
void set_layers_z_range(const std::array<unsigned int, 2>& layers_z_range);
|
void set_layers_z_range(const std::array<unsigned int, 2>& layers_z_range);
|
||||||
|
|
||||||
bool is_legend_shown() const { return m_legend_visible && m_legend_enabled; }
|
bool is_legend_shown() const { return m_legend_visible && m_legend_enabled; }
|
||||||
|
|||||||
@@ -2084,11 +2084,6 @@ void GLCanvas3D::_render_frame(bool scene_dirty, bool only_init)
|
|||||||
const bool overlay_tick = m_fps_overlay_tick;
|
const bool overlay_tick = m_fps_overlay_tick;
|
||||||
m_fps_overlay_tick = false;
|
m_fps_overlay_tick = false;
|
||||||
|
|
||||||
// Whether the preview draws its reduced set is decided before the cached scene is consulted,
|
|
||||||
// since switching changes what the scene pass draws.
|
|
||||||
if (m_canvas_type == ECanvasType::CanvasPreview && m_render_preview && m_gcode_viewer.has_data() && _update_preview_interaction())
|
|
||||||
scene_dirty = true;
|
|
||||||
|
|
||||||
// An overlay-only frame reuses the last scene pass. The overlay is rebuilt either way, and drawn
|
// An overlay-only frame reuses the last scene pass. The overlay is rebuilt either way, and drawn
|
||||||
// below once it is known whether the frame differs from the one on screen.
|
// below once it is known whether the frame differs from the one on screen.
|
||||||
const bool reuse_scene = !scene_dirty && _can_reuse_cached_scene(camera);
|
const bool reuse_scene = !scene_dirty && _can_reuse_cached_scene(camera);
|
||||||
@@ -3282,16 +3277,9 @@ void GLCanvas3D::bind_event_handlers()
|
|||||||
if (m_selection_edit.kind != SelectionEdit::None)
|
if (m_selection_edit.kind != SelectionEdit::None)
|
||||||
finish_selection_edit();
|
finish_selection_edit();
|
||||||
ImGui::SetWindowFocus(nullptr);
|
ImGui::SetWindowFocus(nullptr);
|
||||||
// a drag cut short never sees its button release, which would leave the reduced set drawn
|
|
||||||
if (m_canvas_type == CanvasPreview && m_mouse.dragging && m_gcode_viewer.is_reduced_detail())
|
|
||||||
mouse_up_cleanup();
|
|
||||||
render();
|
render();
|
||||||
evt.Skip();
|
evt.Skip();
|
||||||
});
|
});
|
||||||
m_canvas->Bind(wxEVT_MOUSE_CAPTURE_LOST, [this](wxMouseCaptureLostEvent&) {
|
|
||||||
if (m_canvas_type == CanvasPreview && m_mouse.dragging && m_gcode_viewer.is_reduced_detail())
|
|
||||||
mouse_up_cleanup();
|
|
||||||
});
|
|
||||||
m_event_handlers_bound = true;
|
m_event_handlers_bound = true;
|
||||||
|
|
||||||
m_canvas->Bind(wxEVT_GESTURE_PAN, &GLCanvas3D::on_gesture, this);
|
m_canvas->Bind(wxEVT_GESTURE_PAN, &GLCanvas3D::on_gesture, this);
|
||||||
@@ -3367,17 +3355,6 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt)
|
|||||||
m_overlay_dirty |= imgui_requires_extra_frame;
|
m_overlay_dirty |= imgui_requires_extra_frame;
|
||||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||||
m_dirty |= GLTexture::Compressor::has_compressed_texture_to_refresh();
|
m_dirty |= GLTexture::Compressor::has_compressed_texture_to_refresh();
|
||||||
// the render timer only wakes the idle loop; the frame that puts the preview's toolpaths back
|
|
||||||
// after a wheel burst has to be asked for here, once the settle time is really up
|
|
||||||
if (m_preview_settle_pending) {
|
|
||||||
const auto now = std::chrono::steady_clock::now();
|
|
||||||
if (now >= m_preview_interaction_until) {
|
|
||||||
m_preview_settle_pending = false;
|
|
||||||
m_dirty = true;
|
|
||||||
}
|
|
||||||
else // the timer fired early
|
|
||||||
schedule_extra_frame(static_cast<int>(std::chrono::duration_cast<std::chrono::milliseconds>(m_preview_interaction_until - now).count()) + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!m_dirty && !m_overlay_dirty)
|
if (!m_dirty && !m_overlay_dirty)
|
||||||
return;
|
return;
|
||||||
@@ -3908,10 +3885,6 @@ void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// only a wheel the panels did not take moves the camera
|
|
||||||
if (m_canvas_type == CanvasPreview)
|
|
||||||
note_preview_interaction();
|
|
||||||
|
|
||||||
#ifdef __WXMSW__
|
#ifdef __WXMSW__
|
||||||
// For some reason the Idle event is not being generated after the mouse scroll event in case of scrolling with the two fingers on the touch pad,
|
// For some reason the Idle event is not being generated after the mouse scroll event in case of scrolling with the two fingers on the touch pad,
|
||||||
// if the event is not allowed to be passed further.
|
// if the event is not allowed to be passed further.
|
||||||
@@ -4012,11 +3985,6 @@ void GLCanvas3D::on_fps_overlay_timer(wxTimerEvent& evt)
|
|||||||
wxWakeUpIdle();
|
wxWakeUpIdle();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLCanvas3D::note_preview_interaction()
|
|
||||||
{
|
|
||||||
m_preview_interaction_until = std::chrono::steady_clock::now() + std::chrono::milliseconds(150);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GLCanvas3D::schedule_extra_frame(int milliseconds)
|
void GLCanvas3D::schedule_extra_frame(int milliseconds)
|
||||||
{
|
{
|
||||||
// Schedule idle event right now
|
// Schedule idle event right now
|
||||||
@@ -5663,9 +5631,6 @@ void GLCanvas3D::mouse_up_cleanup()
|
|||||||
m_mouse.ignore_left_up = false;
|
m_mouse.ignore_left_up = false;
|
||||||
m_mouse.ignore_right_up = false;
|
m_mouse.ignore_right_up = false;
|
||||||
m_dirty = true;
|
m_dirty = true;
|
||||||
// the frame that follows a release puts the preview's toolpaths back, and on some platforms
|
|
||||||
// no idle event follows a button release until the next input
|
|
||||||
wxWakeUpIdle();
|
|
||||||
|
|
||||||
if (m_canvas->HasCapture())
|
if (m_canvas->HasCapture())
|
||||||
m_canvas->ReleaseMouse();
|
m_canvas->ReleaseMouse();
|
||||||
@@ -7814,20 +7779,13 @@ bool GLCanvas3D::_is_scene_cacheable() const
|
|||||||
return false;
|
return false;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// The scene follows the cursor while the user drags, under a gizmo that draws at the cursor, and
|
// The scene follows the cursor during a drag, under a gizmo that draws at the cursor, and while
|
||||||
// while the cursor is on the layer height bar, where the object shader draws a band at its height.
|
// the cursor is on the layer height bar, where the object shader draws a band at its height.
|
||||||
const GLGizmoBase* gizmo = m_gizmos.get_current();
|
const GLGizmoBase* gizmo = m_gizmos.get_current();
|
||||||
const bool cursor_on_layers_bar = is_layers_editing_enabled() &&
|
const bool cursor_on_layers_bar = is_layers_editing_enabled() &&
|
||||||
m_layers_editing.bar_rect_contains(*this, (float)m_mouse.position.x(), (float)m_mouse.position.y());
|
m_layers_editing.bar_rect_contains(*this, (float)m_mouse.position.x(), (float)m_mouse.position.y());
|
||||||
return !is_user_interacting() && (gizmo == nullptr || !gizmo->render_follows_cursor()) && !cursor_on_layers_bar;
|
return !m_mouse.dragging && !m_gizmos.is_dragging() && !m_rectangle_selection.is_dragging() &&
|
||||||
}
|
(gizmo == nullptr || !gizmo->render_follows_cursor()) && !cursor_on_layers_bar;
|
||||||
|
|
||||||
// Whether the user is holding something that moves the scene: the camera, the navigator, a gizmo,
|
|
||||||
// the rectangle selection or a preview slider.
|
|
||||||
bool GLCanvas3D::is_user_interacting() const
|
|
||||||
{
|
|
||||||
return m_mouse.dragging || m_navigator_dragging || m_gizmos.is_dragging() || m_rectangle_selection.is_dragging() ||
|
|
||||||
m_gcode_viewer.is_slider_dragging();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GLCanvas3D::_is_frame_skipping_enabled() const
|
bool GLCanvas3D::_is_frame_skipping_enabled() const
|
||||||
@@ -8818,24 +8776,6 @@ void GLCanvas3D::_render_wireframe_overlay()
|
|||||||
shader->stop_using();
|
shader->stop_using();
|
||||||
}
|
}
|
||||||
|
|
||||||
// The reduced set is drawn while the camera, the navigator or either slider is dragged. A wheel
|
|
||||||
// step has no duration, so it holds the reduced set for a settle time instead, and the frame that
|
|
||||||
// restores the full toolpaths is scheduled for when that time runs out. Returns whether what the scene
|
|
||||||
// pass draws changed, since a frame that reuses the cached scene would hide the change.
|
|
||||||
bool GLCanvas3D::_update_preview_interaction()
|
|
||||||
{
|
|
||||||
const auto now = std::chrono::steady_clock::now();
|
|
||||||
const bool settling = now < m_preview_interaction_until;
|
|
||||||
const bool dragging = is_user_interacting();
|
|
||||||
const bool was_reduced = m_gcode_viewer.is_reduced_detail();
|
|
||||||
m_gcode_viewer.set_interacting(dragging || settling);
|
|
||||||
if (settling && !dragging && m_gcode_viewer.is_reduced_detail()) {
|
|
||||||
m_preview_settle_pending = true;
|
|
||||||
schedule_extra_frame(static_cast<int>(std::chrono::duration_cast<std::chrono::milliseconds>(m_preview_interaction_until - now).count()) + 1);
|
|
||||||
}
|
|
||||||
return m_gcode_viewer.is_reduced_detail() != was_reduced;
|
|
||||||
}
|
|
||||||
|
|
||||||
//BBS: GUI refactor: add canvas size as parameters
|
//BBS: GUI refactor: add canvas size as parameters
|
||||||
void GLCanvas3D::_render_gcode(int canvas_width, int canvas_height)
|
void GLCanvas3D::_render_gcode(int canvas_width, int canvas_height)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -656,10 +656,6 @@ private:
|
|||||||
ECursorType m_cursor_type;
|
ECursorType m_cursor_type;
|
||||||
GLSelectionRectangle m_rectangle_selection;
|
GLSelectionRectangle m_rectangle_selection;
|
||||||
bool m_navigator_dragging{ false };
|
bool m_navigator_dragging{ false };
|
||||||
// until when a wheel step keeps the preview's reduced set drawn
|
|
||||||
std::chrono::time_point<std::chrono::steady_clock> m_preview_interaction_until{};
|
|
||||||
// whether the frame that restores the toolpaths once that time is up is still owed
|
|
||||||
bool m_preview_settle_pending{ false };
|
|
||||||
|
|
||||||
//BBS:add plate related logic
|
//BBS:add plate related logic
|
||||||
mutable std::vector<int> m_hover_volume_idxs;
|
mutable std::vector<int> m_hover_volume_idxs;
|
||||||
@@ -1226,10 +1222,6 @@ public:
|
|||||||
void msw_rescale() { m_gcode_viewer.invalidate_legend(); }
|
void msw_rescale() { m_gcode_viewer.invalidate_legend(); }
|
||||||
|
|
||||||
void request_extra_frame() { m_extra_frame_requested = true; }
|
void request_extra_frame() { m_extra_frame_requested = true; }
|
||||||
// whether the user is holding the camera, the navigator, a gizmo, the rectangle selection or a preview slider
|
|
||||||
bool is_user_interacting() const;
|
|
||||||
// a wheel step is over before the next frame, so it holds the preview's reduced set for a settle time
|
|
||||||
void note_preview_interaction();
|
|
||||||
|
|
||||||
void schedule_extra_frame(int milliseconds);
|
void schedule_extra_frame(int milliseconds);
|
||||||
|
|
||||||
@@ -1380,9 +1372,6 @@ private:
|
|||||||
//BBS: GUI refactor: add canvas size as parameters
|
//BBS: GUI refactor: add canvas size as parameters
|
||||||
void _render_gcode(int canvas_width, int canvas_height);
|
void _render_gcode(int canvas_width, int canvas_height);
|
||||||
void _render_gcode_overlay(int canvas_width, int canvas_height);
|
void _render_gcode_overlay(int canvas_width, int canvas_height);
|
||||||
// decides whether the preview draws its reduced set this frame and returns whether what the scene
|
|
||||||
// pass draws changed; runs before the cached scene is consulted
|
|
||||||
bool _update_preview_interaction();
|
|
||||||
//BBS: render a plane for assemble
|
//BBS: render a plane for assemble
|
||||||
void _render_plane() const;
|
void _render_plane() const;
|
||||||
void _render_selection();
|
void _render_selection();
|
||||||
|
|||||||
@@ -483,11 +483,6 @@ void IMSlider::draw_background_and_groove(const ImRect& bg_rect, const ImRect& g
|
|||||||
ImGui::RenderFrame(groove.Min, groove.Max, groove_col, false, 0.5 * groove.GetWidth());
|
ImGui::RenderFrame(groove.Min, groove.Max, groove_col, false, 0.5 * groove.GetWidth());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IMSlider::is_dragging() const
|
|
||||||
{
|
|
||||||
return GImGui != nullptr && m_imgui_id != 0 && GImGui->ActiveId == m_imgui_id && GImGui->IO.MouseDown[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IMSlider::horizontal_slider(const char* str_id, int* value, int v_min, int v_max, const ImVec2& size, float scale)
|
bool IMSlider::horizontal_slider(const char* str_id, int* value, int v_min, int v_max, const ImVec2& size, float scale)
|
||||||
{
|
{
|
||||||
ImGuiWindow* window = ImGui::GetCurrentWindow();
|
ImGuiWindow* window = ImGui::GetCurrentWindow();
|
||||||
@@ -496,7 +491,6 @@ bool IMSlider::horizontal_slider(const char* str_id, int* value, int v_min, int
|
|||||||
|
|
||||||
ImGuiContext& context = *GImGui;
|
ImGuiContext& context = *GImGui;
|
||||||
const ImGuiID id = window->GetID(str_id);
|
const ImGuiID id = window->GetID(str_id);
|
||||||
m_imgui_id = id;
|
|
||||||
|
|
||||||
const ImVec2 pos = window->DC.CursorPos;
|
const ImVec2 pos = window->DC.CursorPos;
|
||||||
const ImRect draw_region(pos, pos + size);
|
const ImRect draw_region(pos, pos + size);
|
||||||
@@ -889,7 +883,6 @@ bool IMSlider::vertical_slider(const char* str_id, int* higher_value, int* lower
|
|||||||
|
|
||||||
ImGuiContext& context = *GImGui;
|
ImGuiContext& context = *GImGui;
|
||||||
const ImGuiID id = window->GetID(str_id);
|
const ImGuiID id = window->GetID(str_id);
|
||||||
m_imgui_id = id;
|
|
||||||
|
|
||||||
const ImVec2 pos = window->DC.CursorPos;
|
const ImVec2 pos = window->DC.CursorPos;
|
||||||
const ImRect draw_region(pos, pos + size);
|
const ImRect draw_region(pos, pos + size);
|
||||||
|
|||||||
@@ -118,9 +118,6 @@ public:
|
|||||||
|
|
||||||
//BBS update scroll value changed
|
//BBS update scroll value changed
|
||||||
bool is_dirty() { return m_dirty; }
|
bool is_dirty() { return m_dirty; }
|
||||||
// whether the mouse is holding this slider's handle, read from ImGui's active id rather than
|
|
||||||
// from the dirty flag, which is raised and consumed inside a single frame
|
|
||||||
bool is_dragging() const;
|
|
||||||
void set_as_dirty(bool dirty = true) { m_dirty = dirty; }
|
void set_as_dirty(bool dirty = true) { m_dirty = dirty; }
|
||||||
bool is_need_post_tick_event() { return m_is_need_post_tick_changed_event; }
|
bool is_need_post_tick_event() { return m_is_need_post_tick_changed_event; }
|
||||||
void reset_post_tick_event(bool val = false) {
|
void reset_post_tick_event(bool val = false) {
|
||||||
@@ -185,8 +182,6 @@ private:
|
|||||||
int m_higher_value;
|
int m_higher_value;
|
||||||
int m_one_layer_value; // ORCA
|
int m_one_layer_value; // ORCA
|
||||||
bool m_dirty = false;
|
bool m_dirty = false;
|
||||||
// the ImGui id of the slider widget, as of its last render
|
|
||||||
unsigned int m_imgui_id = 0;
|
|
||||||
|
|
||||||
bool m_render_as_disabled{ false };
|
bool m_render_as_disabled{ false };
|
||||||
|
|
||||||
|
|||||||
@@ -322,7 +322,7 @@ wxBoxSizer* PreferencesDialog::create_item_combobox(wxString title, wxString too
|
|||||||
return sizer;
|
return sizer;
|
||||||
}
|
}
|
||||||
|
|
||||||
wxBoxSizer *PreferencesDialog::create_item_combobox(wxString title, wxString tooltip, std::string param, std::vector<wxString> vlist, std::vector<std::string> config_name_index, std::function<void(std::string)> onchange, const wxString wiki_url)
|
wxBoxSizer *PreferencesDialog::create_item_combobox(wxString title, wxString tooltip, std::string param, std::vector<wxString> vlist, std::vector<std::string> config_name_index, const wxString wiki_url)
|
||||||
{
|
{
|
||||||
assert(vlist.size() == config_name_index.size());
|
assert(vlist.size() == config_name_index.size());
|
||||||
unsigned int current_index = 0;
|
unsigned int current_index = 0;
|
||||||
@@ -338,9 +338,8 @@ wxBoxSizer *PreferencesDialog::create_item_combobox(wxString title, wxString too
|
|||||||
auto [sizer, combobox] = create_item_combobox_base(title, tooltip, param, vlist, current_index);
|
auto [sizer, combobox] = create_item_combobox_base(title, tooltip, param, vlist, current_index);
|
||||||
|
|
||||||
//// save config
|
//// save config
|
||||||
combobox->GetDropDown().Bind(wxEVT_COMBOBOX, [this, param, config_name_index, onchange](wxCommandEvent& e) {
|
combobox->GetDropDown().Bind(wxEVT_COMBOBOX, [this, param, config_name_index](wxCommandEvent& e) {
|
||||||
app_config->set(param, config_name_index[e.GetSelection()]);
|
app_config->set(param, config_name_index[e.GetSelection()]);
|
||||||
if (onchange != nullptr) onchange(config_name_index[e.GetSelection()]);
|
|
||||||
e.Skip();
|
e.Skip();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -2045,32 +2044,6 @@ void PreferencesDialog::create_items()
|
|||||||
"preview_default_view_type", PreviewViewTypeLabels, PreviewViewTypeValues);
|
"preview_default_view_type", PreviewViewTypeLabels, PreviewViewTypeValues);
|
||||||
g_sizer->Add(item_preview_view_type);
|
g_sizer->Add(item_preview_view_type);
|
||||||
|
|
||||||
auto item_reduced_detail_mode = create_item_combobox(
|
|
||||||
_L("Simplify preview while dragging"),
|
|
||||||
_L("What the sliced preview draws while you drag the camera or a preview slider, or zoom with the mouse wheel, so that large prints stay responsive. "
|
|
||||||
"The full toolpaths are restored as soon as you let go.\n"
|
|
||||||
"Off: the full toolpaths.\n"
|
|
||||||
"Solid model: the sliced objects and the prime tower as solid shapes in their filament colors, cut to the visible layer range, "
|
|
||||||
"with its bottom and top layers drawn as toolpaths. Supports are not shown, and negative volumes are not cut out.\n"
|
|
||||||
"Shell only: every layer without its sparse infill, internal solid infill and gap fill, which lie under the walls and skins. "
|
|
||||||
"Walls, top and bottom surfaces, bridges, supports and the prime tower are drawn whole, so the print looks the same from outside.\n"
|
|
||||||
"The bottom and top of the visible layer range are always drawn whole."),
|
|
||||||
"preview_reduced_detail_mode",
|
|
||||||
{_L("Off"), _L("Solid model"), _L("Shell only")},
|
|
||||||
{"off", "solid", "shell"},
|
|
||||||
// apply the new mode immediately to the currently loaded preview
|
|
||||||
[](std::string value) {
|
|
||||||
if (Plater* plater = wxGetApp().plater()) {
|
|
||||||
if (GLCanvas3D* canvas = plater->get_preview_canvas3D()) {
|
|
||||||
canvas->get_gcode_viewer().set_reduced_detail_mode(value);
|
|
||||||
canvas->set_as_dirty();
|
|
||||||
canvas->request_extra_frame();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
g_sizer->Add(item_reduced_detail_mode);
|
|
||||||
|
|
||||||
auto item_dim_previous_layers = create_item_checkbox(
|
auto item_dim_previous_layers = create_item_checkbox(
|
||||||
_L("Dim lower layers"),
|
_L("Dim lower layers"),
|
||||||
_L("When scrubbing the layer slider in the sliced preview, render the layers below the current one darkened so that only the layer being viewed is shown at full brightness."),
|
_L("When scrubbing the layer slider in the sliced preview, render the layers below the current one darkened so that only the layer being viewed is shown at full brightness."),
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ public:
|
|||||||
wxBoxSizer *create_item_title(wxString title);
|
wxBoxSizer *create_item_title(wxString title);
|
||||||
wxBoxSizer *create_item_label(wxString label, const wxString tooltip = "", const wxString wiki_url = "");
|
wxBoxSizer *create_item_label(wxString label, const wxString tooltip = "", const wxString wiki_url = "");
|
||||||
wxBoxSizer *create_item_combobox(wxString title, wxString tooltip, std::string param, std::vector<wxString> vlist, std::function<void(wxString)> onchange = {}, const wxString wiki_url = "");
|
wxBoxSizer *create_item_combobox(wxString title, wxString tooltip, std::string param, std::vector<wxString> vlist, std::function<void(wxString)> onchange = {}, const wxString wiki_url = "");
|
||||||
wxBoxSizer *create_item_combobox(wxString title, wxString tooltip, std::string param, std::vector<wxString> vlist, std::vector<std::string> config_name_index, std::function<void(std::string)> onchange = {}, const wxString wiki_url = "");
|
wxBoxSizer *create_item_combobox(wxString title, wxString tooltip, std::string param, std::vector<wxString> vlist, std::vector<std::string> config_name_index, const wxString wiki_url = "");
|
||||||
wxBoxSizer *create_item_region_combobox(wxString title, wxString tooltip);
|
wxBoxSizer *create_item_region_combobox(wxString title, wxString tooltip);
|
||||||
wxBoxSizer *create_item_language_combobox(wxString title, wxString tooltip);
|
wxBoxSizer *create_item_language_combobox(wxString title, wxString tooltip);
|
||||||
wxBoxSizer *create_item_loglevel_combobox(wxString title, wxString tooltip, std::vector<wxString> vlist);
|
wxBoxSizer *create_item_loglevel_combobox(wxString title, wxString tooltip, std::vector<wxString> vlist);
|
||||||
|
|||||||
@@ -15,3 +15,11 @@ set_tests_properties(cli_strict_mode PROPERTIES
|
|||||||
LABELS "CLI;RequiresApp"
|
LABELS "CLI;RequiresApp"
|
||||||
SKIP_RETURN_CODE 77
|
SKIP_RETURN_CODE 77
|
||||||
TIMEOUT 900)
|
TIMEOUT 900)
|
||||||
|
|
||||||
|
add_test(NAME cli_project_missing_keys
|
||||||
|
COMMAND bash ${CMAKE_CURRENT_SOURCE_DIR}/test_cli_project_missing_keys.sh $<TARGET_FILE:OrcaSlicer> ${ORCA_CLI_TEST_PYTHON}
|
||||||
|
${CMAKE_SOURCE_DIR}/resources/profiles/BBL)
|
||||||
|
set_tests_properties(cli_project_missing_keys PROPERTIES
|
||||||
|
LABELS "CLI;RequiresApp"
|
||||||
|
SKIP_RETURN_CODE 77
|
||||||
|
TIMEOUT 900)
|
||||||
|
|||||||
@@ -0,0 +1,89 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# End-to-end check that the CLI fills settings missing from a project from the project's system presets.
|
||||||
|
#
|
||||||
|
# A project saved before an option existed has no value for it. The GUI takes such keys from the
|
||||||
|
# project's system printer and process presets, not from the option defaults, and the CLI must slice
|
||||||
|
# the project with the same values. A project is exported from the shipped Bambu Lab P1S presets, one
|
||||||
|
# printer key and one process key are removed from it, one kept key is changed, and it is sliced again.
|
||||||
|
#
|
||||||
|
# usage: test_cli_project_missing_keys.sh <orca-slicer binary> <python3> <resources/profiles/BBL>
|
||||||
|
set -u
|
||||||
|
|
||||||
|
BIN="${1:-}"
|
||||||
|
PY="${2:-python3}"
|
||||||
|
PROFILES="${3:-}"
|
||||||
|
# 77 is the test's SKIP_RETURN_CODE.
|
||||||
|
[ -x "$BIN" ] || { echo "SKIP: orca-slicer binary not found: $BIN"; exit 77; }
|
||||||
|
[ -d "$PROFILES" ] || { echo "FAIL: profiles directory not found: $PROFILES"; exit 1; }
|
||||||
|
|
||||||
|
WORK="$(mktemp -d "${TMPDIR:-/tmp}/orca-cli-missing-keys.XXXXXX")"
|
||||||
|
trap 'rm -rf "$WORK"' EXIT
|
||||||
|
|
||||||
|
"$PY" - "$WORK/cube.stl" <<'EOF'
|
||||||
|
import sys
|
||||||
|
|
||||||
|
v = [(x, y, z) for z in (0, 10) for y in (0, 10) for x in (0, 10)]
|
||||||
|
with open(sys.argv[1], "w") as f:
|
||||||
|
f.write("solid cube\n")
|
||||||
|
for a, b, c, d in ((0, 2, 3, 1), (4, 5, 7, 6), (0, 1, 5, 4), (2, 6, 7, 3), (0, 4, 6, 2), (1, 3, 7, 5)):
|
||||||
|
for tri in ((v[a], v[b], v[c]), (v[a], v[c], v[d])):
|
||||||
|
f.write("facet normal 0 0 0\nouter loop\n")
|
||||||
|
for p in tri:
|
||||||
|
f.write("vertex %g %g %g\n" % p)
|
||||||
|
f.write("endloop\nendfacet\n")
|
||||||
|
f.write("endsolid cube\n")
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# slice <tag> <input> [option...]: slice into $WORK/<tag>/out.3mf with a fresh data directory.
|
||||||
|
slice() {
|
||||||
|
local out="$WORK/$1" input="$2"; shift 2
|
||||||
|
mkdir -p "$out"
|
||||||
|
timeout 300 "$BIN" --datadir "$out/datadir" "$@" --slice 0 --outputdir "$out" --export-3mf out.3mf "$input" \
|
||||||
|
> "$out/log" 2>&1 || { echo "FAIL: $1: orca-slicer exited $?"; tail -n 40 "$out/log"; exit 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
slice base "$WORK/cube.stl" \
|
||||||
|
--load-settings "$PROFILES/machine/Bambu Lab P1S 0.4 nozzle.json;$PROFILES/process/0.20mm Standard @BBL X1C.json" \
|
||||||
|
--load-filaments "$PROFILES/filament/Bambu PLA Basic @BBL P1S 0.4 nozzle.json"
|
||||||
|
|
||||||
|
# The removed keys, with their option defaults from PrintConfig.cpp, and a kept key with a new value.
|
||||||
|
"$PY" - "$WORK/base/out.3mf" "$WORK/old.3mf" <<'EOF' || exit $?
|
||||||
|
import json, sys, zipfile
|
||||||
|
|
||||||
|
src, dst = sys.argv[1], sys.argv[2]
|
||||||
|
missing = {"extruder_clearance_dist_to_rod": "40", "sparse_infill_density": "20%"}
|
||||||
|
with zipfile.ZipFile(src) as zin, zipfile.ZipFile(dst, "w", zipfile.ZIP_DEFLATED) as zout:
|
||||||
|
for item in zin.infolist():
|
||||||
|
data = zin.read(item.filename)
|
||||||
|
if item.filename == "Metadata/project_settings.config":
|
||||||
|
config = json.loads(data)
|
||||||
|
for key, default in missing.items():
|
||||||
|
if config[key] == default:
|
||||||
|
print("SKIP: %s is %s in the system preset, the option default, so the test cannot tell them apart" % (key, default))
|
||||||
|
sys.exit(77)
|
||||||
|
expected = {key: config.pop(key) for key in missing}
|
||||||
|
expected["wall_loops"] = str(int(config["wall_loops"]) + 1)
|
||||||
|
config["wall_loops"] = expected["wall_loops"]
|
||||||
|
data = json.dumps(config, indent=4)
|
||||||
|
zout.writestr(item, data)
|
||||||
|
with open(dst + ".expected.json", "w") as f:
|
||||||
|
json.dump(expected, f)
|
||||||
|
EOF
|
||||||
|
|
||||||
|
slice project "$WORK/old.3mf"
|
||||||
|
|
||||||
|
"$PY" - "$WORK/project/out.3mf" "$WORK/old.3mf.expected.json" <<'EOF'
|
||||||
|
import json, sys, zipfile
|
||||||
|
|
||||||
|
with zipfile.ZipFile(sys.argv[1]) as z:
|
||||||
|
config = json.loads(z.read("Metadata/project_settings.config"))
|
||||||
|
with open(sys.argv[2]) as f:
|
||||||
|
expected = json.load(f)
|
||||||
|
errors = ["%s is %r, want %r" % (key, config.get(key), want) for key, want in expected.items() if config.get(key) != want]
|
||||||
|
for e in errors:
|
||||||
|
print("FAIL: " + e)
|
||||||
|
sys.exit(1 if errors else 0)
|
||||||
|
EOF
|
||||||
|
status=$?
|
||||||
|
[ "$status" -eq 0 ] || { tail -n 40 "$WORK/project/log"; exit 1; }
|
||||||
|
echo "PASS"
|
||||||
@@ -5405,6 +5405,35 @@ struct ScopedDataDir
|
|||||||
~ScopedDataDir() { set_data_dir(previous); }
|
~ScopedDataDir() { set_data_dir(previous); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// resources_dir() is process-wide too; system preset lookups scan its profiles directory.
|
||||||
|
struct ScopedResourcesDir
|
||||||
|
{
|
||||||
|
std::string previous = resources_dir();
|
||||||
|
explicit ScopedResourcesDir(const fs::path &dir) { set_resources_dir(dir.string()); }
|
||||||
|
~ScopedResourcesDir() { set_resources_dir(previous); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// An "Acme" vendor under root whose "Acme Printer" inherits extruder_clearance_dist_to_rod from an
|
||||||
|
// abstract base, with the printer in a nested sub_path so the name cannot be derived from the file.
|
||||||
|
void write_acme_printer_vendor(const fs::path &root, double dist_to_rod)
|
||||||
|
{
|
||||||
|
const fs::path machine_dir = root / "Acme" / "machine";
|
||||||
|
fs::create_directories(machine_dir / "nested");
|
||||||
|
std::ofstream((root / "Acme.json").string())
|
||||||
|
<< R"({"version":"1.0.0","name":"Acme",)"
|
||||||
|
<< R"("machine_model_list":[{"name":"Acme One","sub_path":"machine/model.json"}],"machine_list":[)"
|
||||||
|
<< R"({"name":"fdm_acme_common","sub_path":"machine/base.json"},)"
|
||||||
|
<< R"({"name":"Acme Printer","sub_path":"machine/nested/printer.json"}]})";
|
||||||
|
std::ofstream((machine_dir / "model.json").string())
|
||||||
|
<< R"({"type":"machine_model","name":"Acme One","nozzle_diameter":"0.4"})";
|
||||||
|
std::ofstream((machine_dir / "base.json").string())
|
||||||
|
<< R"({"type":"machine","name":"fdm_acme_common","from":"system","instantiation":"false",)"
|
||||||
|
<< R"("extruder_clearance_dist_to_rod":")" << dist_to_rod << R"("})";
|
||||||
|
std::ofstream((machine_dir / "nested" / "printer.json").string())
|
||||||
|
<< R"({"type":"machine","name":"Acme Printer","from":"system","instantiation":"true","inherits":"fdm_acme_common",)"
|
||||||
|
<< R"("printer_model":"Acme One","printer_variant":"0.4"})";
|
||||||
|
}
|
||||||
|
|
||||||
std::string read_file(const fs::path &file)
|
std::string read_file(const fs::path &file)
|
||||||
{
|
{
|
||||||
std::ifstream in(file.string(), std::ios::binary);
|
std::ifstream in(file.string(), std::ios::binary);
|
||||||
@@ -5482,3 +5511,75 @@ TEST_CASE("Config import confines zip entries, preset names and bundle ids to th
|
|||||||
CHECK_FALSE(any_filename_contains(temp_dir.path(), "bundle-escape"));
|
CHECK_FALSE(any_filename_contains(temp_dir.path(), "bundle-escape"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("A system preset resolves by name from the bundled profiles", "[Preset][Bundle]")
|
||||||
|
{
|
||||||
|
ScopedTemporaryDir temp_dir;
|
||||||
|
ScopedDataDir data(temp_dir.path() / "data");
|
||||||
|
ScopedResourcesDir resources(temp_dir.path() / "resources");
|
||||||
|
write_acme_printer_vendor(temp_dir.path() / "resources" / "profiles", 33.);
|
||||||
|
|
||||||
|
PresetBundle bundle;
|
||||||
|
DynamicPrintConfig config;
|
||||||
|
std::string error;
|
||||||
|
REQUIRE(bundle.resolve_system_preset(config, Preset::TYPE_PRINTER, "Acme Printer",
|
||||||
|
ForwardCompatibilitySubstitutionRule::EnableSilent, error));
|
||||||
|
CHECK(error.empty());
|
||||||
|
CHECK_THAT(config.opt_float("extruder_clearance_dist_to_rod"), Catch::Matchers::WithinAbs(33., 1e-6));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("A system preset resolves from the data directory copy of its vendor", "[Preset][Bundle]")
|
||||||
|
{
|
||||||
|
ScopedTemporaryDir temp_dir;
|
||||||
|
ScopedDataDir data(temp_dir.path() / "data");
|
||||||
|
ScopedResourcesDir resources(temp_dir.path() / "resources");
|
||||||
|
write_acme_printer_vendor(temp_dir.path() / "resources" / "profiles", 33.);
|
||||||
|
write_acme_printer_vendor(temp_dir.path() / "data" / PRESET_SYSTEM_DIR, 35.);
|
||||||
|
|
||||||
|
PresetBundle bundle;
|
||||||
|
DynamicPrintConfig config;
|
||||||
|
std::string error;
|
||||||
|
REQUIRE(bundle.resolve_system_preset(config, Preset::TYPE_PRINTER, "Acme Printer",
|
||||||
|
ForwardCompatibilitySubstitutionRule::EnableSilent, error));
|
||||||
|
CHECK_THAT(config.opt_float("extruder_clearance_dist_to_rod"), Catch::Matchers::WithinAbs(35., 1e-6));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("A system preset resolves from a vendor shipped as its preset cache alone", "[Preset][Bundle]")
|
||||||
|
{
|
||||||
|
ScopedTemporaryDir temp_dir;
|
||||||
|
ScopedDataDir data(temp_dir.path() / "data");
|
||||||
|
ScopedResourcesDir resources(temp_dir.path() / "resources");
|
||||||
|
const fs::path profiles = temp_dir.path() / "resources" / "profiles";
|
||||||
|
write_acme_printer_vendor(profiles, 33.);
|
||||||
|
|
||||||
|
PresetBundle writer;
|
||||||
|
writer.set_generate_vendor_caches(true);
|
||||||
|
writer.load_vendor_configs_from_json(profiles.string(), "Acme", PresetBundle::LoadSystem,
|
||||||
|
ForwardCompatibilitySubstitutionRule::EnableSilent);
|
||||||
|
REQUIRE(fs::exists(profiles / "Acme.opc"));
|
||||||
|
// Release builds ship the cache and drop the profile JSONs, manifest included.
|
||||||
|
fs::remove(profiles / "Acme.json");
|
||||||
|
fs::remove_all(profiles / "Acme");
|
||||||
|
|
||||||
|
PresetBundle bundle;
|
||||||
|
DynamicPrintConfig config;
|
||||||
|
std::string error;
|
||||||
|
REQUIRE(bundle.resolve_system_preset(config, Preset::TYPE_PRINTER, "Acme Printer",
|
||||||
|
ForwardCompatibilitySubstitutionRule::EnableSilent, error));
|
||||||
|
CHECK_THAT(config.opt_float("extruder_clearance_dist_to_rod"), Catch::Matchers::WithinAbs(33., 1e-6));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("A system preset no vendor lists is not resolved", "[Preset][Bundle]")
|
||||||
|
{
|
||||||
|
ScopedTemporaryDir temp_dir;
|
||||||
|
ScopedDataDir data(temp_dir.path() / "data");
|
||||||
|
ScopedResourcesDir resources(temp_dir.path() / "resources");
|
||||||
|
write_acme_printer_vendor(temp_dir.path() / "resources" / "profiles", 33.);
|
||||||
|
|
||||||
|
PresetBundle bundle;
|
||||||
|
DynamicPrintConfig config;
|
||||||
|
std::string error;
|
||||||
|
CHECK_FALSE(bundle.resolve_system_preset(config, Preset::TYPE_PRINTER, "Unknown Printer",
|
||||||
|
ForwardCompatibilitySubstitutionRule::EnableSilent, error));
|
||||||
|
CHECK_FALSE(error.empty());
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user