Merge branch 'dev' into fs_simplify_multipart_object
This commit is contained in:
commit
80231a22f2
4
deps/wxWidgets/wxWidgets.cmake
vendored
4
deps/wxWidgets/wxWidgets.cmake
vendored
@ -12,8 +12,8 @@ endif()
|
||||
prusaslicer_add_cmake_project(wxWidgets
|
||||
# GIT_REPOSITORY "https://github.com/prusa3d/wxWidgets"
|
||||
# GIT_TAG tm_cross_compile #${_wx_git_tag}
|
||||
URL https://github.com/prusa3d/wxWidgets/archive/refs/heads/v3.1.4-patched.zip
|
||||
URL_HASH SHA256=69dec874981d2fc3d90345660c27f3450d8430c483e8446edcc87b6ed18bff8f
|
||||
URL https://github.com/prusa3d/wxWidgets/archive/73f029adfcc82fb3aa4b01220a013f716e57d110.zip
|
||||
URL_HASH SHA256=c35fe0187db497b6a3f477e24ed5e307028657ff0c2554385810b6e7961ad2e4
|
||||
DEPENDS ${PNG_PKG} ${ZLIB_PKG} ${EXPAT_PKG} dep_TIFF dep_JPEG
|
||||
CMAKE_ARGS
|
||||
-DwxBUILD_PRECOMP=ON
|
||||
|
@ -139,7 +139,7 @@ documentation_link= https://help.prusa3d.com/en/article/per-model-settings_1674
|
||||
disabled_tags = SLA
|
||||
|
||||
[hint:Solid infill threshold area]
|
||||
text = Solid infill threshold area\nDid you know that you can make parts of your model with a small cross-section be filled with solid infill automatically? Set the<a>Solid infill threshold area</a>.(Expert mode only.)
|
||||
text = Solid infill threshold area\nDid you know that you can make parts of your model with a small cross-section be filled with solid infill automatically? Set the<a>Solid infill threshold area.</a>(Expert mode only.)
|
||||
hypertext_type = settings
|
||||
hypertext_settings_opt = solid_infill_below_area
|
||||
hypertext_settings_type = 1
|
||||
@ -197,7 +197,7 @@ documentation_link = https://help.prusa3d.com/en/article/insert-pause-or-custom-
|
||||
disabled_tags = SLA
|
||||
|
||||
[hint:Configuration snapshots]
|
||||
text = Configuration snapshots\nDid you know that roll back to a complete backup of all system and user profiles? You can view and move back and forth between snapshots using the Configuration - <a>Configuration snapshots menu</a>.
|
||||
text = Configuration snapshots\nDid you know that you can roll back to a complete backup of all system and user profiles? You can view and move back and forth between snapshots using the Configuration - <a>Configuration snapshots menu</a>.
|
||||
documentation_link = https://help.prusa3d.com/en/article/configuration-snapshots_1776
|
||||
hypertext_type = menubar
|
||||
hypertext_menubar_menu_name = Configuration
|
||||
|
File diff suppressed because it is too large
Load Diff
8
resources/shaders/flat.fs
Normal file
8
resources/shaders/flat.fs
Normal file
@ -0,0 +1,8 @@
|
||||
#version 110
|
||||
|
||||
uniform vec4 uniform_color;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_FragColor = uniform_color;
|
||||
}
|
6
resources/shaders/flat.vs
Normal file
6
resources/shaders/flat.vs
Normal file
@ -0,0 +1,6 @@
|
||||
#version 110
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = ftransform();
|
||||
}
|
@ -100,6 +100,8 @@ set(SLIC3R_SOURCES
|
||||
GCode/ThumbnailData.hpp
|
||||
GCode/CoolingBuffer.cpp
|
||||
GCode/CoolingBuffer.hpp
|
||||
GCode/FindReplace.cpp
|
||||
GCode/FindReplace.hpp
|
||||
GCode/PostProcessor.cpp
|
||||
GCode/PostProcessor.hpp
|
||||
# GCode/PressureEqualizer.cpp
|
||||
|
@ -1099,6 +1099,11 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
||||
// modifies m_silent_time_estimator_enabled
|
||||
DoExport::init_gcode_processor(print.config(), m_processor, m_silent_time_estimator_enabled);
|
||||
|
||||
if (! print.config().gcode_substitutions.values.empty()) {
|
||||
m_find_replace = make_unique<GCodeFindReplace>(print.config());
|
||||
file.set_find_replace(m_find_replace.get());
|
||||
}
|
||||
|
||||
// resets analyzer's tracking data
|
||||
m_last_height = 0.f;
|
||||
m_last_layer_z = 0.f;
|
||||
@ -1560,15 +1565,25 @@ void GCode::process_layers(
|
||||
[&cooling_buffer = *this->m_cooling_buffer.get()](GCode::LayerResult in) -> std::string {
|
||||
return cooling_buffer.process_layer(std::move(in.gcode), in.layer_id, in.cooling_buffer_flush);
|
||||
});
|
||||
const auto find_replace = tbb::make_filter<std::string, std::string>(slic3r_tbb_filtermode::serial_in_order,
|
||||
[&self = *this->m_find_replace.get()](std::string s) -> std::string {
|
||||
return self.process_layer(std::move(s));
|
||||
});
|
||||
const auto output = tbb::make_filter<std::string, void>(slic3r_tbb_filtermode::serial_in_order,
|
||||
[&output_stream](std::string s) { output_stream.write(s); }
|
||||
);
|
||||
|
||||
// The pipeline elements are joined using const references, thus no copying is performed.
|
||||
if (m_spiral_vase)
|
||||
output_stream.set_find_replace(nullptr);
|
||||
if (m_spiral_vase && m_find_replace)
|
||||
tbb::parallel_pipeline(12, generator & spiral_vase & cooling & find_replace & output);
|
||||
else if (m_spiral_vase)
|
||||
tbb::parallel_pipeline(12, generator & spiral_vase & cooling & output);
|
||||
else if (m_find_replace)
|
||||
tbb::parallel_pipeline(12, generator & cooling & find_replace & output);
|
||||
else
|
||||
tbb::parallel_pipeline(12, generator & cooling & output);
|
||||
output_stream.set_find_replace(m_find_replace.get());
|
||||
}
|
||||
|
||||
// Process all layers of a single object instance (sequential mode) with a parallel pipeline:
|
||||
@ -1603,15 +1618,25 @@ void GCode::process_layers(
|
||||
[&cooling_buffer = *this->m_cooling_buffer.get()](GCode::LayerResult in)->std::string {
|
||||
return cooling_buffer.process_layer(std::move(in.gcode), in.layer_id, in.cooling_buffer_flush);
|
||||
});
|
||||
const auto find_replace = tbb::make_filter<std::string, std::string>(slic3r_tbb_filtermode::serial_in_order,
|
||||
[&self = *this->m_find_replace.get()](std::string s) -> std::string {
|
||||
return self.process_layer(std::move(s));
|
||||
});
|
||||
const auto output = tbb::make_filter<std::string, void>(slic3r_tbb_filtermode::serial_in_order,
|
||||
[&output_stream](std::string s) { output_stream.write(s); }
|
||||
);
|
||||
|
||||
// The pipeline elements are joined using const references, thus no copying is performed.
|
||||
if (m_spiral_vase)
|
||||
output_stream.set_find_replace(nullptr);
|
||||
if (m_spiral_vase && m_find_replace)
|
||||
tbb::parallel_pipeline(12, generator & spiral_vase & cooling & find_replace & output);
|
||||
else if (m_spiral_vase)
|
||||
tbb::parallel_pipeline(12, generator & spiral_vase & cooling & output);
|
||||
else if (m_find_replace)
|
||||
tbb::parallel_pipeline(12, generator & cooling & find_replace & output);
|
||||
else
|
||||
tbb::parallel_pipeline(12, generator & cooling & output);
|
||||
output_stream.set_find_replace(m_find_replace.get());
|
||||
}
|
||||
|
||||
std::string GCode::placeholder_parser_process(const std::string &name, const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override)
|
||||
@ -2824,11 +2849,11 @@ void GCode::GCodeOutputStream::close()
|
||||
void GCode::GCodeOutputStream::write(const char *what)
|
||||
{
|
||||
if (what != nullptr) {
|
||||
const char* gcode = what;
|
||||
// writes string to file
|
||||
fwrite(gcode, 1, ::strlen(gcode), this->f);
|
||||
//FIXME don't allocate a string, maybe process a batch of lines?
|
||||
m_processor.process_buffer(std::string(gcode));
|
||||
std::string gcode(m_find_replace ? m_find_replace->process_layer(what) : what);
|
||||
// writes string to file
|
||||
fwrite(gcode.c_str(), 1, gcode.size(), this->f);
|
||||
m_processor.process_buffer(gcode);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "PrintConfig.hpp"
|
||||
#include "GCode/AvoidCrossingPerimeters.hpp"
|
||||
#include "GCode/CoolingBuffer.hpp"
|
||||
#include "GCode/FindReplace.hpp"
|
||||
#include "GCode/SpiralVase.hpp"
|
||||
#include "GCode/ToolOrdering.hpp"
|
||||
#include "GCode/WipeTower.hpp"
|
||||
@ -189,6 +190,11 @@ private:
|
||||
GCodeOutputStream(FILE *f, GCodeProcessor &processor) : f(f), m_processor(processor) {}
|
||||
~GCodeOutputStream() { this->close(); }
|
||||
|
||||
// Set a find-replace post-processor to modify the G-code before GCodePostProcessor.
|
||||
// It is being set to null inside process_layers(), because the find-replace process
|
||||
// is being called on a secondary thread to improve performance.
|
||||
void set_find_replace(GCodeFindReplace *find_replace) { m_find_replace = find_replace; }
|
||||
|
||||
bool is_open() const { return f; }
|
||||
bool is_error() const;
|
||||
|
||||
@ -208,7 +214,9 @@ private:
|
||||
void write_format(const char* format, ...);
|
||||
|
||||
private:
|
||||
FILE *f = nullptr;
|
||||
FILE *f { nullptr };
|
||||
// Find-replace post-processor to be called before GCodePostProcessor.
|
||||
GCodeFindReplace *m_find_replace { nullptr };
|
||||
GCodeProcessor &m_processor;
|
||||
};
|
||||
void _do_export(Print &print, GCodeOutputStream &file, ThumbnailsGeneratorCallback thumbnail_cb);
|
||||
@ -393,6 +401,7 @@ private:
|
||||
|
||||
std::unique_ptr<CoolingBuffer> m_cooling_buffer;
|
||||
std::unique_ptr<SpiralVase> m_spiral_vase;
|
||||
std::unique_ptr<GCodeFindReplace> m_find_replace;
|
||||
#ifdef HAS_PRESSURE_EQUALIZER
|
||||
std::unique_ptr<PressureEqualizer> m_pressure_equalizer;
|
||||
#endif /* HAS_PRESSURE_EQUALIZER */
|
||||
|
@ -439,7 +439,15 @@ static std::vector<TravelPoint> simplify_travel(const AvoidCrossingPerimeters::B
|
||||
visitor.pt_current = ¤t_point;
|
||||
|
||||
if (!next.do_not_remove)
|
||||
for (size_t point_idx_2 = point_idx + 1; point_idx_2 < travel.size() && !travel[point_idx_2].do_not_remove; ++point_idx_2) {
|
||||
for (size_t point_idx_2 = point_idx + 1; point_idx_2 < travel.size(); ++point_idx_2) {
|
||||
// Workaround for some issue in MSVC 19.29.30037 32-bit compiler.
|
||||
#if defined(_WIN32) && !defined(_WIN64)
|
||||
if (bool volatile do_not_remove = travel[point_idx_2].do_not_remove; do_not_remove)
|
||||
break;
|
||||
#else
|
||||
if (travel[point_idx_2].do_not_remove)
|
||||
break;
|
||||
#endif
|
||||
if (travel[point_idx_2].point == current_point) {
|
||||
next = travel[point_idx_2];
|
||||
point_idx = point_idx_2;
|
||||
|
153
src/libslic3r/GCode/FindReplace.cpp
Normal file
153
src/libslic3r/GCode/FindReplace.cpp
Normal file
@ -0,0 +1,153 @@
|
||||
#include "FindReplace.hpp"
|
||||
#include "../Utils.hpp"
|
||||
|
||||
#include <cctype> // isalpha
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
// Similar to https://npp-user-manual.org/docs/searching/#extended-search-mode
|
||||
const void unescape_extended_search_mode(std::string &s)
|
||||
{
|
||||
boost::replace_all(s, "\\n", "\n"); // Line Feed control character LF (ASCII 0x0A)
|
||||
boost::replace_all(s, "\\r", "\r"); // Carriage Return control character CR (ASCII 0x0D)
|
||||
boost::replace_all(s, "\\t", "\t"); // TAB control character (ASCII 0x09)
|
||||
boost::replace_all(s, "\\0", "\0x00"); // NUL control character (ASCII 0x00)
|
||||
boost::replace_all(s, "\\\\", "\\"); // Backslash character (ASCII 0x5C)
|
||||
|
||||
// Notepad++ also supports:
|
||||
// \o: the octal representation of a byte, made of 3 digits in the 0-7 range
|
||||
// \d: the decimal representation of a byte, made of 3 digits in the 0-9 range
|
||||
// \x: the hexadecimal representation of a byte, made of 2 digits in the 0-9, A-F/a-f range.
|
||||
// \u: The hexadecimal representation of a two-byte character, made of 4 digits in the 0-9, A-F/a-f range.
|
||||
}
|
||||
|
||||
GCodeFindReplace::GCodeFindReplace(const std::vector<std::string> &gcode_substitutions)
|
||||
{
|
||||
if ((gcode_substitutions.size() % 3) != 0)
|
||||
throw RuntimeError("Invalid length of gcode_substitutions parameter");
|
||||
|
||||
m_substitutions.reserve(gcode_substitutions.size() / 3);
|
||||
for (size_t i = 0; i < gcode_substitutions.size(); i += 3) {
|
||||
Substitution out;
|
||||
try {
|
||||
out.plain_pattern = gcode_substitutions[i];
|
||||
out.format = gcode_substitutions[i + 1];
|
||||
const std::string ¶ms = gcode_substitutions[i + 2];
|
||||
out.regexp = strchr(params.c_str(), 'r') != nullptr || strchr(params.c_str(), 'R') != nullptr;
|
||||
out.case_insensitive = strchr(params.c_str(), 'i') != nullptr || strchr(params.c_str(), 'I') != nullptr;
|
||||
out.whole_word = strchr(params.c_str(), 'w') != nullptr || strchr(params.c_str(), 'W') != nullptr;
|
||||
if (out.regexp) {
|
||||
out.regexp_pattern.assign(
|
||||
out.whole_word ?
|
||||
std::string("\\b") + out.plain_pattern + "\\b" :
|
||||
out.plain_pattern,
|
||||
(out.case_insensitive ? boost::regex::icase : 0) | boost::regex::optimize);
|
||||
} else {
|
||||
unescape_extended_search_mode(out.plain_pattern);
|
||||
unescape_extended_search_mode(out.format);
|
||||
}
|
||||
} catch (const std::exception &ex) {
|
||||
throw RuntimeError(std::string("Invalid gcode_substitutions parameter, failed to compile regular expression: ") + ex.what());
|
||||
}
|
||||
m_substitutions.emplace_back(std::move(out));
|
||||
}
|
||||
}
|
||||
|
||||
class ToStringIterator
|
||||
{
|
||||
public:
|
||||
using iterator_category = std::output_iterator_tag;
|
||||
using value_type = void;
|
||||
using difference_type = void;
|
||||
using pointer = void;
|
||||
using reference = void;
|
||||
|
||||
ToStringIterator(std::string &data) : m_data(&data) {}
|
||||
|
||||
ToStringIterator& operator=(const char val) {
|
||||
size_t needs = m_data->size() + 1;
|
||||
if (m_data->capacity() < needs)
|
||||
m_data->reserve(next_highest_power_of_2(needs));
|
||||
m_data->push_back(val);
|
||||
return *this;
|
||||
}
|
||||
|
||||
ToStringIterator& operator*() { return *this; }
|
||||
ToStringIterator& operator++() { return *this; }
|
||||
ToStringIterator operator++(int) { return *this; }
|
||||
|
||||
private:
|
||||
std::string *m_data;
|
||||
};
|
||||
|
||||
template<typename FindFn>
|
||||
static void find_and_replace_whole_word(std::string &inout, const std::string &match, const std::string &replace, FindFn find_fn)
|
||||
{
|
||||
if (! match.empty() && inout.size() >= match.size() && match != replace) {
|
||||
std::string out;
|
||||
auto [i, j] = find_fn(inout, 0, match);
|
||||
size_t k = 0;
|
||||
for (; i != std::string::npos; std::tie(i, j) = find_fn(inout, i, match)) {
|
||||
if ((i == 0 || ! std::isalnum(inout[i - 1])) && (j == inout.size() || ! std::isalnum(inout[j]))) {
|
||||
out.reserve(inout.size());
|
||||
out.append(inout, k, i - k);
|
||||
out.append(replace);
|
||||
i = k = j;
|
||||
} else
|
||||
i += match.size();
|
||||
}
|
||||
if (k > 0) {
|
||||
out.append(inout, k, inout.size() - k);
|
||||
inout.swap(out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string GCodeFindReplace::process_layer(const std::string &ain)
|
||||
{
|
||||
std::string out;
|
||||
const std::string *in = &ain;
|
||||
std::string temp;
|
||||
temp.reserve(in->size());
|
||||
|
||||
for (const Substitution &substitution : m_substitutions) {
|
||||
if (substitution.regexp) {
|
||||
temp.clear();
|
||||
temp.reserve(in->size());
|
||||
boost::regex_replace(ToStringIterator(temp), in->begin(), in->end(),
|
||||
substitution.regexp_pattern, substitution.format, boost::match_default | boost::match_not_dot_newline | boost::match_not_dot_null | boost::format_all);
|
||||
std::swap(out, temp);
|
||||
} else {
|
||||
if (in == &ain)
|
||||
out = ain;
|
||||
// Plain substitution
|
||||
if (substitution.case_insensitive) {
|
||||
if (substitution.whole_word)
|
||||
find_and_replace_whole_word(out, substitution.plain_pattern, substitution.format,
|
||||
[](const std::string &str, size_t start_pos, const std::string &match) {
|
||||
auto begin = str.begin() + start_pos;
|
||||
boost::iterator_range<std::string::const_iterator> r1(begin, str.end());
|
||||
boost::iterator_range<std::string::const_iterator> r2(match.begin(), match.end());
|
||||
auto res = boost::ifind_first(r1, r2);
|
||||
return res ? std::make_pair(size_t(res.begin() - str.begin()), size_t(res.end() - str.begin())) : std::make_pair(std::string::npos, std::string::npos);
|
||||
});
|
||||
else
|
||||
boost::ireplace_all(out, substitution.plain_pattern, substitution.format);
|
||||
} else {
|
||||
if (substitution.whole_word)
|
||||
find_and_replace_whole_word(out, substitution.plain_pattern, substitution.format,
|
||||
[](const std::string &str, size_t start_pos, const std::string &match) {
|
||||
size_t pos = str.find(match, start_pos);
|
||||
return std::make_pair(pos, pos + (pos == std::string::npos ? 0 : match.size()));
|
||||
});
|
||||
else
|
||||
boost::replace_all(out, substitution.plain_pattern, substitution.format);
|
||||
}
|
||||
}
|
||||
in = &out;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
}
|
33
src/libslic3r/GCode/FindReplace.hpp
Normal file
33
src/libslic3r/GCode/FindReplace.hpp
Normal file
@ -0,0 +1,33 @@
|
||||
#ifndef slic3r_FindReplace_hpp_
|
||||
#define slic3r_FindReplace_hpp_
|
||||
|
||||
#include "../PrintConfig.hpp"
|
||||
|
||||
#include <boost/regex.hpp>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class GCodeFindReplace {
|
||||
public:
|
||||
GCodeFindReplace(const PrintConfig &print_config) : GCodeFindReplace(print_config.gcode_substitutions.values) {}
|
||||
GCodeFindReplace(const std::vector<std::string> &gcode_substitutions);
|
||||
|
||||
|
||||
std::string process_layer(const std::string &gcode);
|
||||
|
||||
private:
|
||||
struct Substitution {
|
||||
std::string plain_pattern;
|
||||
boost::regex regexp_pattern;
|
||||
std::string format;
|
||||
|
||||
bool regexp { false };
|
||||
bool case_insensitive { false };
|
||||
bool whole_word { false };
|
||||
};
|
||||
std::vector<Substitution> m_substitutions;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // slic3r_FindReplace_hpp_
|
@ -439,7 +439,7 @@ static std::vector<std::string> s_Preset_print_options {
|
||||
"support_material_interface_pattern", "support_material_interface_spacing", "support_material_interface_contact_loops",
|
||||
"support_material_contact_distance", "support_material_bottom_contact_distance",
|
||||
"support_material_buildplate_only", "dont_support_bridges", "thick_bridges", "notes", "complete_objects", "extruder_clearance_radius",
|
||||
"extruder_clearance_height", "gcode_comments", "gcode_label_objects", "output_filename_format", "post_process", "perimeter_extruder",
|
||||
"extruder_clearance_height", "gcode_comments", "gcode_label_objects", "output_filename_format", "post_process", "gcode_substitutions", "perimeter_extruder",
|
||||
"infill_extruder", "solid_infill_extruder", "support_material_extruder", "support_material_interface_extruder",
|
||||
"ooze_prevention", "standby_temperature_delta", "interface_shells", "extrusion_width", "first_layer_extrusion_width",
|
||||
"perimeter_extrusion_width", "external_perimeter_extrusion_width", "infill_extrusion_width", "solid_infill_extrusion_width",
|
||||
@ -1133,6 +1133,20 @@ void add_correct_opts_to_diff(const std::string &opt_key, t_config_option_keys&
|
||||
}
|
||||
}
|
||||
|
||||
// list of options with vector variable, which is independent from number of extruders
|
||||
static const std::vector<std::string> independent_from_extruder_number_options = {
|
||||
"bed_shape",
|
||||
"filament_ramming_parameters",
|
||||
"gcode_substitutions",
|
||||
"compatible_prints",
|
||||
"compatible_printers"
|
||||
};
|
||||
|
||||
bool PresetCollection::is_independent_from_extruder_number_option(const std::string& opt_key)
|
||||
{
|
||||
return std::find(independent_from_extruder_number_options.begin(), independent_from_extruder_number_options.end(), opt_key) != independent_from_extruder_number_options.end();
|
||||
}
|
||||
|
||||
// Use deep_diff to correct return of changed options, considering individual options for each extruder.
|
||||
inline t_config_option_keys deep_diff(const ConfigBase &config_this, const ConfigBase &config_other)
|
||||
{
|
||||
@ -1142,7 +1156,7 @@ inline t_config_option_keys deep_diff(const ConfigBase &config_this, const Confi
|
||||
const ConfigOption *other_opt = config_other.option(opt_key);
|
||||
if (this_opt != nullptr && other_opt != nullptr && *this_opt != *other_opt)
|
||||
{
|
||||
if (opt_key == "bed_shape" || opt_key == "thumbnails" || opt_key == "compatible_prints" || opt_key == "compatible_printers") {
|
||||
if (PresetCollection::is_independent_from_extruder_number_option(opt_key)) {
|
||||
// Scalar variable, or a vector variable, which is independent from number of extruders,
|
||||
// thus the vector is presented to the user as a single input.
|
||||
diff.emplace_back(opt_key);
|
||||
|
@ -558,6 +558,7 @@ private:
|
||||
public:
|
||||
static bool is_dirty(const Preset *edited, const Preset *reference);
|
||||
static std::vector<std::string> dirty_options(const Preset *edited, const Preset *reference, const bool deep_compare = false);
|
||||
static bool is_independent_from_extruder_number_option(const std::string& opt_key);
|
||||
private:
|
||||
// Type of this PresetCollection: TYPE_PRINT, TYPE_FILAMENT or TYPE_PRINTER.
|
||||
Preset::Type m_type;
|
||||
|
@ -110,6 +110,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|
||||
"output_filename_format",
|
||||
"perimeter_acceleration",
|
||||
"post_process",
|
||||
"gcode_substitutions",
|
||||
"printer_notes",
|
||||
"retract_before_travel",
|
||||
"retract_before_wipe",
|
||||
|
@ -1357,6 +1357,12 @@ void PrintConfigDef::init_fff_params()
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionBool(0));
|
||||
|
||||
def = this->add("gcode_substitutions", coStrings);
|
||||
def->label = L("G-code substitutions");
|
||||
def->tooltip = L("Find / replace patterns in G-code lines and substitute them.");
|
||||
def->mode = comExpert;
|
||||
def->set_default_value(new ConfigOptionStrings());
|
||||
|
||||
def = this->add("high_current_on_filament_swap", coBool);
|
||||
def->label = L("High extruder current on filament swap");
|
||||
def->tooltip = L("It may be beneficial to increase the extruder motor current during the filament exchange"
|
||||
|
@ -641,6 +641,12 @@ PRINT_CONFIG_CLASS_DEFINE(
|
||||
((ConfigOptionBool, gcode_comments))
|
||||
((ConfigOptionEnum<GCodeFlavor>, gcode_flavor))
|
||||
((ConfigOptionBool, gcode_label_objects))
|
||||
// Triples of strings: "search pattern", "replace with pattern", "attribs"
|
||||
// where "attribs" are one of:
|
||||
// r - regular expression
|
||||
// i - case insensitive
|
||||
// w - whole word
|
||||
((ConfigOptionStrings, gcode_substitutions))
|
||||
((ConfigOptionString, layer_gcode))
|
||||
((ConfigOptionFloat, max_print_speed))
|
||||
((ConfigOptionFloat, max_volumetric_speed))
|
||||
|
@ -60,6 +60,10 @@
|
||||
#define ENABLE_TRAVEL_TIME (1 && ENABLE_2_5_0_ALPHA1)
|
||||
// Enable not killing focus in object manipulator fields when hovering over 3D scene
|
||||
#define ENABLE_OBJECT_MANIPULATOR_FOCUS (1 && ENABLE_2_5_0_ALPHA1)
|
||||
// Enable removal of wipe tower magic object_id equal to 1000
|
||||
#define ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL (1 && ENABLE_2_5_0_ALPHA1)
|
||||
// Enable removal of old OpenGL render calls
|
||||
#define ENABLE_GLBEGIN_GLEND_REMOVAL (1 && ENABLE_2_5_0_ALPHA1)
|
||||
|
||||
|
||||
#endif // _prusaslicer_technologies_h_
|
||||
|
@ -669,9 +669,15 @@ void GLVolumeCollection::load_object_auxiliary(
|
||||
}
|
||||
}
|
||||
|
||||
#if ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL
|
||||
int GLVolumeCollection::load_wipe_tower_preview(
|
||||
float pos_x, float pos_y, float width, float depth, float height,
|
||||
float rotation_angle, bool size_unknown, float brim_width, bool opengl_initialized)
|
||||
#else
|
||||
int GLVolumeCollection::load_wipe_tower_preview(
|
||||
int obj_idx, float pos_x, float pos_y, float width, float depth, float height,
|
||||
float rotation_angle, bool size_unknown, float brim_width, bool opengl_initialized)
|
||||
#endif // ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL
|
||||
{
|
||||
if (depth < 0.01f)
|
||||
return int(this->volumes.size() - 1);
|
||||
@ -733,7 +739,11 @@ int GLVolumeCollection::load_wipe_tower_preview(
|
||||
v.indexed_vertex_array.finalize_geometry(opengl_initialized);
|
||||
v.set_volume_offset(Vec3d(pos_x, pos_y, 0.0));
|
||||
v.set_volume_rotation(Vec3d(0., 0., (M_PI / 180.) * rotation_angle));
|
||||
#if ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL
|
||||
v.composite_id = GLVolume::CompositeID(INT_MAX, 0, 0);
|
||||
#else
|
||||
v.composite_id = GLVolume::CompositeID(obj_idx, 0, 0);
|
||||
#endif // ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL
|
||||
v.geometry_id.first = 0;
|
||||
v.geometry_id.second = wipe_tower_instance_id().id;
|
||||
v.is_wipe_tower = true;
|
||||
|
@ -590,8 +590,13 @@ public:
|
||||
size_t timestamp,
|
||||
bool opengl_initialized);
|
||||
|
||||
#if ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL
|
||||
int load_wipe_tower_preview(
|
||||
float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool size_unknown, float brim_width, bool opengl_initialized);
|
||||
#else
|
||||
int load_wipe_tower_preview(
|
||||
int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool size_unknown, float brim_width, bool opengl_initialized);
|
||||
#endif // ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL
|
||||
|
||||
GLVolume* new_toolpath_volume(const ColorRGBA& rgba, size_t reserve_vbo_floats = 0);
|
||||
GLVolume* new_nontoolpath_volume(const ColorRGBA& rgba, size_t reserve_vbo_floats = 0);
|
||||
|
@ -2135,8 +2135,13 @@ void GCodeViewer::load_shells(const Print& print, bool initialized)
|
||||
const float depth = print.wipe_tower_data(extruders_count).depth;
|
||||
const float brim_width = print.wipe_tower_data(extruders_count).brim_width;
|
||||
|
||||
#if ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL
|
||||
m_shells.volumes.load_wipe_tower_preview(config.wipe_tower_x, config.wipe_tower_y, config.wipe_tower_width, depth, max_z, config.wipe_tower_rotation_angle,
|
||||
!print.is_step_done(psWipeTower), brim_width, initialized);
|
||||
#else
|
||||
m_shells.volumes.load_wipe_tower_preview(1000, config.wipe_tower_x, config.wipe_tower_y, config.wipe_tower_width, depth, max_z, config.wipe_tower_rotation_angle,
|
||||
!print.is_step_done(psWipeTower), brim_width, initialized);
|
||||
#endif // ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1123,11 +1123,18 @@ ModelInstanceEPrintVolumeState GLCanvas3D::check_volumes_outside_state() const
|
||||
|
||||
void GLCanvas3D::toggle_sla_auxiliaries_visibility(bool visible, const ModelObject* mo, int instance_idx)
|
||||
{
|
||||
#if ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL
|
||||
if (current_printer_technology() != ptSLA)
|
||||
return;
|
||||
#endif // ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL
|
||||
|
||||
m_render_sla_auxiliaries = visible;
|
||||
|
||||
for (GLVolume* vol : m_volumes.volumes) {
|
||||
#if !ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL
|
||||
if (vol->composite_id.object_id == 1000)
|
||||
continue; // the wipe tower
|
||||
#endif // !ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL
|
||||
if ((mo == nullptr || m_model->objects[vol->composite_id.object_id] == mo)
|
||||
&& (instance_idx == -1 || vol->composite_id.instance_id == instance_idx)
|
||||
&& vol->composite_id.volume_id < 0)
|
||||
@ -1138,9 +1145,14 @@ void GLCanvas3D::toggle_sla_auxiliaries_visibility(bool visible, const ModelObje
|
||||
void GLCanvas3D::toggle_model_objects_visibility(bool visible, const ModelObject* mo, int instance_idx, const ModelVolume* mv)
|
||||
{
|
||||
for (GLVolume* vol : m_volumes.volumes) {
|
||||
#if ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL
|
||||
if (vol->is_wipe_tower)
|
||||
vol->is_active = (visible && mo == nullptr);
|
||||
#else
|
||||
if (vol->composite_id.object_id == 1000) { // wipe tower
|
||||
vol->is_active = (visible && mo == nullptr);
|
||||
}
|
||||
#endif // ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL
|
||||
else {
|
||||
if ((mo == nullptr || m_model->objects[vol->composite_id.object_id] == mo)
|
||||
&& (instance_idx == -1 || vol->composite_id.instance_id == instance_idx)
|
||||
@ -1165,6 +1177,7 @@ void GLCanvas3D::toggle_model_objects_visibility(bool visible, const ModelObject
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (visible && !mo)
|
||||
toggle_sla_auxiliaries_visibility(true, mo, instance_idx);
|
||||
|
||||
@ -1182,7 +1195,7 @@ void GLCanvas3D::update_instance_printable_state_for_object(const size_t obj_idx
|
||||
ModelInstance* instance = model_object->instances[inst_idx];
|
||||
|
||||
for (GLVolume* volume : m_volumes.volumes) {
|
||||
if ((volume->object_idx() == (int)obj_idx) && (volume->instance_idx() == inst_idx))
|
||||
if (volume->object_idx() == (int)obj_idx && volume->instance_idx() == inst_idx)
|
||||
volume->printable = instance->printable;
|
||||
}
|
||||
}
|
||||
@ -2021,9 +2034,15 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
|
||||
float depth = print->wipe_tower_data(extruders_count).depth;
|
||||
float brim_width = print->wipe_tower_data(extruders_count).brim_width;
|
||||
|
||||
#if ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL
|
||||
int volume_idx_wipe_tower_new = m_volumes.load_wipe_tower_preview(
|
||||
x, y, w, depth, (float)height, a, !print->is_step_done(psWipeTower),
|
||||
brim_width, m_initialized);
|
||||
#else
|
||||
int volume_idx_wipe_tower_new = m_volumes.load_wipe_tower_preview(
|
||||
1000, x, y, w, depth, (float)height, a, !print->is_step_done(psWipeTower),
|
||||
brim_width, m_initialized);
|
||||
#endif // ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL
|
||||
if (volume_idx_wipe_tower_old != -1)
|
||||
map_glvolume_old_to_new[volume_idx_wipe_tower_old] = volume_idx_wipe_tower_new;
|
||||
}
|
||||
@ -3039,7 +3058,11 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
||||
|
||||
#if ENABLE_OBJECT_MANIPULATOR_FOCUS
|
||||
if (evt.ButtonDown()) {
|
||||
std::string curr_sidebar_field = m_sidebar_field;
|
||||
handle_sidebar_focus_event("", false);
|
||||
if (boost::algorithm::istarts_with(curr_sidebar_field, "layer"))
|
||||
// restore visibility of layers hints after left clicking on the 3D scene
|
||||
m_sidebar_field = curr_sidebar_field;
|
||||
if (wxWindow::FindFocus() != m_canvas)
|
||||
// Grab keyboard focus on any mouse click event.
|
||||
m_canvas->SetFocus();
|
||||
@ -3448,9 +3471,15 @@ void GLCanvas3D::do_move(const std::string& snapshot_type)
|
||||
model_object->invalidate_bounding_box();
|
||||
}
|
||||
}
|
||||
#if ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL
|
||||
else if (v->is_wipe_tower)
|
||||
// Move a wipe tower proxy.
|
||||
wipe_tower_origin = v->get_volume_offset();
|
||||
#else
|
||||
else if (object_idx == 1000)
|
||||
// Move a wipe tower proxy.
|
||||
wipe_tower_origin = v->get_volume_offset();
|
||||
#endif // ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL
|
||||
}
|
||||
|
||||
// Fixes flying instances
|
||||
@ -3510,11 +3539,18 @@ void GLCanvas3D::do_rotate(const std::string& snapshot_type)
|
||||
Selection::EMode selection_mode = m_selection.get_mode();
|
||||
|
||||
for (const GLVolume* v : m_volumes.volumes) {
|
||||
#if ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL
|
||||
if (v->is_wipe_tower) {
|
||||
#else
|
||||
int object_idx = v->object_idx();
|
||||
if (object_idx == 1000) { // the wipe tower
|
||||
#endif // ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL
|
||||
Vec3d offset = v->get_volume_offset();
|
||||
post_event(Vec3dEvent(EVT_GLCANVAS_WIPETOWER_ROTATED, Vec3d(offset(0), offset(1), v->get_volume_rotation()(2))));
|
||||
}
|
||||
#if ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL
|
||||
int object_idx = v->object_idx();
|
||||
#endif // ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL
|
||||
if (object_idx < 0 || (int)m_model->objects.size() <= object_idx)
|
||||
continue;
|
||||
|
||||
@ -3708,7 +3744,6 @@ void GLCanvas3D::update_gizmos_on_off_state()
|
||||
void GLCanvas3D::handle_sidebar_focus_event(const std::string& opt_key, bool focus_on)
|
||||
{
|
||||
m_sidebar_field = focus_on ? opt_key : "";
|
||||
|
||||
if (!m_sidebar_field.empty())
|
||||
m_gizmos.reset_all_states();
|
||||
|
||||
@ -4852,19 +4887,31 @@ BoundingBoxf3 GLCanvas3D::_max_bounding_box(bool include_gizmos, bool include_be
|
||||
|
||||
// The following is a workaround for gizmos not being taken in account when calculating the tight camera frustrum
|
||||
// A better solution would ask the gizmo manager for the bounding box of the current active gizmo, if any
|
||||
if (include_gizmos && m_gizmos.is_running())
|
||||
{
|
||||
BoundingBoxf3 sel_bb = m_selection.get_bounding_box();
|
||||
Vec3d sel_bb_center = sel_bb.center();
|
||||
Vec3d extend_by = sel_bb.max_size() * Vec3d::Ones();
|
||||
if (include_gizmos && m_gizmos.is_running()) {
|
||||
const BoundingBoxf3 sel_bb = m_selection.get_bounding_box();
|
||||
const Vec3d sel_bb_center = sel_bb.center();
|
||||
const Vec3d extend_by = sel_bb.max_size() * Vec3d::Ones();
|
||||
bb.merge(BoundingBoxf3(sel_bb_center - extend_by, sel_bb_center + extend_by));
|
||||
}
|
||||
|
||||
bb.merge(include_bed_model ? m_bed.extended_bounding_box() : m_bed.build_volume().bounding_volume());
|
||||
const BoundingBoxf3 bed_bb = include_bed_model ? m_bed.extended_bounding_box() : m_bed.build_volume().bounding_volume();
|
||||
bb.merge(bed_bb);
|
||||
|
||||
if (!m_main_toolbar.is_enabled())
|
||||
bb.merge(m_gcode_viewer.get_max_bounding_box());
|
||||
|
||||
// clamp max bb size with respect to bed bb size
|
||||
static const double max_scale_factor = 1.5;
|
||||
const Vec3d bb_size = bb.size();
|
||||
const Vec3d bed_bb_size = bed_bb.size();
|
||||
if (bb_size.x() > max_scale_factor * bed_bb_size.x() ||
|
||||
bb_size.y() > max_scale_factor * bed_bb_size.y() ||
|
||||
bb_size.z() > max_scale_factor * bed_bb_size.z()) {
|
||||
const Vec3d bed_bb_center = bed_bb.center();
|
||||
const Vec3d extend_by = max_scale_factor * bed_bb_size;
|
||||
bb = BoundingBoxf3(bed_bb_center - extend_by, bed_bb_center + extend_by);
|
||||
}
|
||||
|
||||
return bb;
|
||||
}
|
||||
|
||||
@ -5208,7 +5255,11 @@ void GLCanvas3D::_render_gcode()
|
||||
m_gcode_viewer.render();
|
||||
}
|
||||
|
||||
#if ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||
void GLCanvas3D::_render_selection()
|
||||
#else
|
||||
void GLCanvas3D::_render_selection() const
|
||||
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||
{
|
||||
float scale_factor = 1.0;
|
||||
#if ENABLE_RETINA_GL
|
||||
@ -5239,7 +5290,7 @@ void GLCanvas3D::_render_sequential_clearance()
|
||||
}
|
||||
|
||||
#if ENABLE_RENDER_SELECTION_CENTER
|
||||
void GLCanvas3D::_render_selection_center() const
|
||||
void GLCanvas3D::_render_selection_center()
|
||||
{
|
||||
m_selection.render_center(m_gizmos.is_dragging());
|
||||
}
|
||||
@ -5626,7 +5677,11 @@ void GLCanvas3D::_render_sla_slices()
|
||||
}
|
||||
}
|
||||
|
||||
#if ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||
void GLCanvas3D::_render_selection_sidebar_hints()
|
||||
#else
|
||||
void GLCanvas3D::_render_selection_sidebar_hints() const
|
||||
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||
{
|
||||
m_selection.render_sidebar_hints(m_sidebar_field);
|
||||
}
|
||||
|
@ -915,10 +915,14 @@ private:
|
||||
void _render_bed_for_picking(bool bottom);
|
||||
void _render_objects(GLVolumeCollection::ERenderType type);
|
||||
void _render_gcode();
|
||||
#if ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||
void _render_selection();
|
||||
#else
|
||||
void _render_selection() const;
|
||||
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||
void _render_sequential_clearance();
|
||||
#if ENABLE_RENDER_SELECTION_CENTER
|
||||
void _render_selection_center() const;
|
||||
void _render_selection_center();
|
||||
#endif // ENABLE_RENDER_SELECTION_CENTER
|
||||
void _check_and_update_toolbar_icon_scale();
|
||||
void _render_overlays();
|
||||
@ -933,7 +937,11 @@ private:
|
||||
void _render_camera_target() const;
|
||||
#endif // ENABLE_SHOW_CAMERA_TARGET
|
||||
void _render_sla_slices();
|
||||
#if ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||
void _render_selection_sidebar_hints();
|
||||
#else
|
||||
void _render_selection_sidebar_hints() const;
|
||||
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||
bool _render_undo_redo_stack(const bool is_undo, float pos_x);
|
||||
bool _render_search_list(float pos_x);
|
||||
bool _render_arrange_menu(float pos_x);
|
||||
|
@ -33,6 +33,10 @@ std::pair<bool, std::string> GLShadersManager::init()
|
||||
|
||||
bool valid = true;
|
||||
|
||||
#if ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||
// basic shader, used to render selection bbox
|
||||
valid &= append_shader("flat", { "flat.vs", "flat.fs" });
|
||||
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||
// used to render bed axes and model, selection hints, gcode sequential view marker model, preview shells, options in gcode preview
|
||||
valid &= append_shader("gouraud_light", { "gouraud_light.vs", "gouraud_light.fs" });
|
||||
// used to render printbed
|
||||
|
@ -143,7 +143,7 @@ void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt
|
||||
config.set_key_value(opt_key, new ConfigOptionString(boost::any_cast<std::string>(value)));
|
||||
break;
|
||||
case coStrings:{
|
||||
if (opt_key == "compatible_prints" || opt_key == "compatible_printers") {
|
||||
if (opt_key == "compatible_prints" || opt_key == "compatible_printers" || opt_key == "gcode_substitutions") {
|
||||
config.option<ConfigOptionStrings>(opt_key)->values =
|
||||
boost::any_cast<std::vector<std::string>>(value);
|
||||
}
|
||||
|
@ -718,6 +718,12 @@ void MenuFactory::append_menu_item_export_stl(wxMenu* menu)
|
||||
const Selection& selection = plater()->canvas3D()->get_selection();
|
||||
return selection.is_single_full_instance() || selection.is_single_full_object() || selection.is_single_volume() || selection.is_single_modifier();
|
||||
}, m_parent);
|
||||
append_menu_item(menu, wxID_ANY, _L("Export as OBJ") + dots, "",
|
||||
[](wxCommandEvent&) { plater()->export_obj(false, true); }, "", nullptr,
|
||||
[]() {
|
||||
const Selection& selection = plater()->canvas3D()->get_selection();
|
||||
return selection.is_single_full_instance() || selection.is_single_full_object() || selection.is_single_volume() || selection.is_single_modifier();
|
||||
}, m_parent);
|
||||
menu->AppendSeparator();
|
||||
}
|
||||
|
||||
|
@ -1931,9 +1931,15 @@ void ObjectList::del_layers_from_object(const int obj_idx)
|
||||
bool ObjectList::del_subobject_from_object(const int obj_idx, const int idx, const int type)
|
||||
{
|
||||
assert(idx >= 0);
|
||||
#if ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL
|
||||
if (m_objects->empty() || int(m_objects->size()) <= obj_idx)
|
||||
// Cannot delete a wipe tower
|
||||
return false;
|
||||
#else
|
||||
if (obj_idx == 1000 || idx<0)
|
||||
// Cannot delete a wipe tower or volume with negative id
|
||||
return false;
|
||||
#endif // ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL
|
||||
|
||||
ModelObject* object = (*m_objects)[obj_idx];
|
||||
|
||||
|
@ -102,6 +102,47 @@ void GLGizmoCut::on_render()
|
||||
glsafe(::glEnable(GL_BLEND));
|
||||
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
|
||||
|
||||
#if ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||
GLShaderProgram* shader = wxGetApp().get_shader("flat");
|
||||
if (shader == nullptr)
|
||||
return;
|
||||
shader->start_using();
|
||||
|
||||
bool z_changed = std::abs(plane_center.z() - m_old_z) > EPSILON;
|
||||
m_old_z = plane_center.z();
|
||||
|
||||
if (!m_plane.is_initialized() || z_changed) {
|
||||
m_plane.reset();
|
||||
|
||||
GLModel::InitializationData init_data;
|
||||
GUI::GLModel::InitializationData::Entity entity;
|
||||
entity.type = GUI::GLModel::PrimitiveType::Triangles;
|
||||
entity.positions.reserve(4);
|
||||
entity.positions.emplace_back(Vec3f(min_x, min_y, plane_center.z()));
|
||||
entity.positions.emplace_back(Vec3f(max_x, min_y, plane_center.z()));
|
||||
entity.positions.emplace_back(Vec3f(max_x, max_y, plane_center.z()));
|
||||
entity.positions.emplace_back(Vec3f(min_x, max_y, plane_center.z()));
|
||||
|
||||
entity.normals.reserve(4);
|
||||
for (size_t i = 0; i < 4; ++i) {
|
||||
entity.normals.emplace_back(Vec3f::UnitZ());
|
||||
}
|
||||
|
||||
entity.indices.reserve(6);
|
||||
entity.indices.emplace_back(0);
|
||||
entity.indices.emplace_back(1);
|
||||
entity.indices.emplace_back(2);
|
||||
entity.indices.emplace_back(2);
|
||||
entity.indices.emplace_back(3);
|
||||
entity.indices.emplace_back(0);
|
||||
|
||||
init_data.entities.emplace_back(entity);
|
||||
m_plane.init_from(init_data);
|
||||
m_plane.set_color(-1, PLANE_COLOR);
|
||||
}
|
||||
|
||||
m_plane.render();
|
||||
#else
|
||||
// Draw the cutting plane
|
||||
::glBegin(GL_QUADS);
|
||||
::glColor4fv(PLANE_COLOR.data());
|
||||
@ -110,6 +151,7 @@ void GLGizmoCut::on_render()
|
||||
::glVertex3f(max_x, max_y, plane_center.z());
|
||||
::glVertex3f(min_x, max_y, plane_center.z());
|
||||
glsafe(::glEnd());
|
||||
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||
|
||||
glsafe(::glEnable(GL_CULL_FACE));
|
||||
glsafe(::glDisable(GL_BLEND));
|
||||
@ -123,6 +165,37 @@ void GLGizmoCut::on_render()
|
||||
glsafe(::glClear(GL_DEPTH_BUFFER_BIT));
|
||||
|
||||
glsafe(::glLineWidth(m_hover_id != -1 ? 2.0f : 1.5f));
|
||||
#if ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||
if (!m_grabber_connection.is_initialized() || z_changed) {
|
||||
m_grabber_connection.reset();
|
||||
|
||||
GLModel::InitializationData init_data;
|
||||
GUI::GLModel::InitializationData::Entity entity;
|
||||
entity.type = GUI::GLModel::PrimitiveType::Lines;
|
||||
entity.positions.reserve(2);
|
||||
entity.positions.emplace_back(plane_center.cast<float>());
|
||||
entity.positions.emplace_back(m_grabbers[0].center.cast<float>());
|
||||
|
||||
entity.normals.reserve(2);
|
||||
for (size_t i = 0; i < 2; ++i) {
|
||||
entity.normals.emplace_back(Vec3f::UnitZ());
|
||||
}
|
||||
|
||||
entity.indices.reserve(2);
|
||||
entity.indices.emplace_back(0);
|
||||
entity.indices.emplace_back(1);
|
||||
|
||||
init_data.entities.emplace_back(entity);
|
||||
m_grabber_connection.init_from(init_data);
|
||||
m_grabber_connection.set_color(-1, ColorRGBA::YELLOW());
|
||||
}
|
||||
|
||||
m_grabber_connection.render();
|
||||
|
||||
shader->stop_using();
|
||||
|
||||
shader = wxGetApp().get_shader("gouraud_light");
|
||||
#else
|
||||
glsafe(::glColor3f(1.0, 1.0, 0.0));
|
||||
::glBegin(GL_LINES);
|
||||
::glVertex3dv(plane_center.data());
|
||||
@ -130,6 +203,8 @@ void GLGizmoCut::on_render()
|
||||
glsafe(::glEnd());
|
||||
|
||||
GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light");
|
||||
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||
|
||||
if (shader == nullptr)
|
||||
return;
|
||||
shader->start_using();
|
||||
|
@ -22,6 +22,11 @@ class GLGizmoCut : public GLGizmoBase
|
||||
bool m_keep_upper{ true };
|
||||
bool m_keep_lower{ true };
|
||||
bool m_rotate_lower{ false };
|
||||
#if ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||
GLModel m_plane;
|
||||
GLModel m_grabber_connection;
|
||||
float m_old_z{ 0.0f };
|
||||
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||
|
||||
struct CutContours
|
||||
{
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "libslic3r/Exception.hpp"
|
||||
#include "libslic3r/Utils.hpp"
|
||||
#include "libslic3r/AppConfig.hpp"
|
||||
#include "libslic3r/Preset.hpp"
|
||||
#include "I18N.hpp"
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
@ -596,8 +597,7 @@ void ConfigOptionsGroup::back_to_config_value(const DynamicPrintConfig& config,
|
||||
}
|
||||
else if (m_opt_map.find(opt_key) == m_opt_map.end() ||
|
||||
// This option don't have corresponded field
|
||||
opt_key == "bed_shape" || opt_key == "filament_ramming_parameters" ||
|
||||
opt_key == "compatible_printers" || opt_key == "compatible_prints" ) {
|
||||
PresetCollection::is_independent_from_extruder_number_option(opt_key) ) {
|
||||
value = get_config_value(config, opt_key);
|
||||
this->change_opt_value(opt_key, value);
|
||||
OptionsGroup::on_change_OG(opt_key, value);
|
||||
@ -876,7 +876,7 @@ boost::any ConfigOptionsGroup::get_config_value(const DynamicPrintConfig& config
|
||||
ret = from_u8(config.opt_string(opt_key));
|
||||
break;
|
||||
case coStrings:
|
||||
if (opt_key == "compatible_printers" || opt_key == "compatible_prints") {
|
||||
if (opt_key == "compatible_printers" || opt_key == "compatible_prints" || opt_key == "gcode_substitutions") {
|
||||
ret = config.option<ConfigOptionStrings>(opt_key)->values;
|
||||
break;
|
||||
}
|
||||
|
@ -37,6 +37,7 @@
|
||||
#include "libslic3r/Format/STL.hpp"
|
||||
#include "libslic3r/Format/AMF.hpp"
|
||||
#include "libslic3r/Format/3mf.hpp"
|
||||
#include "libslic3r/Format/OBJ.hpp"
|
||||
#include "libslic3r/GCode/ThumbnailData.hpp"
|
||||
#include "libslic3r/Model.hpp"
|
||||
#include "libslic3r/SLA/Hollowing.hpp"
|
||||
@ -1245,7 +1246,11 @@ void Sidebar::show_info_sizer()
|
||||
ModelObjectPtrs objects = p->plater->model().objects;
|
||||
int obj_idx = selection.get_object_idx();
|
||||
|
||||
#if ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL
|
||||
if (m_mode < comExpert || objects.empty() || obj_idx < 0 || int(objects.size()) <= obj_idx ||
|
||||
#else
|
||||
if (m_mode < comExpert || objects.empty() || obj_idx < 0 || obj_idx == 1000 ||
|
||||
#endif // ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL
|
||||
objects[obj_idx]->volumes.empty() || // hack to avoid crash when deleting the last object on the bed
|
||||
(selection.is_single_full_object() && objects[obj_idx]->instances.size()> 1) ||
|
||||
!(selection.is_single_full_instance() || selection.is_single_volume())) {
|
||||
@ -2866,14 +2871,22 @@ Selection& Plater::priv::get_selection()
|
||||
int Plater::priv::get_selected_object_idx() const
|
||||
{
|
||||
int idx = get_selection().get_object_idx();
|
||||
#if ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL
|
||||
return (0 <= idx && idx < int(model.objects.size())) ? idx : -1;
|
||||
#else
|
||||
return ((0 <= idx) && (idx < 1000)) ? idx : -1;
|
||||
#endif // ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL
|
||||
}
|
||||
|
||||
int Plater::priv::get_selected_volume_idx() const
|
||||
{
|
||||
auto& selection = get_selection();
|
||||
int idx = selection.get_object_idx();
|
||||
#if ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL
|
||||
if (idx < 0 || int(model.objects.size()) <= idx)
|
||||
#else
|
||||
if ((0 > idx) || (idx > 1000))
|
||||
#endif // ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL
|
||||
return-1;
|
||||
const GLVolume* v = selection.get_volume(*selection.get_volume_idxs().begin());
|
||||
if (model.objects[idx]->volumes.size() > 1)
|
||||
@ -5843,7 +5856,7 @@ void Plater::export_stl(bool extended, bool selection_only)
|
||||
inst_mesh.merge(inst_object_mesh);
|
||||
|
||||
// ensure that the instance lays on the bed
|
||||
inst_mesh.translate(0.0f, 0.0f, -inst_mesh.bounding_box().min[2]);
|
||||
inst_mesh.translate(0.0f, 0.0f, -inst_mesh.bounding_box().min.z());
|
||||
|
||||
// merge instance with global mesh
|
||||
mesh.merge(inst_mesh);
|
||||
@ -5859,6 +5872,144 @@ void Plater::export_stl(bool extended, bool selection_only)
|
||||
// p->statusbar()->set_status_text(format_wxstr(_L("STL file exported to %s"), path));
|
||||
}
|
||||
|
||||
void Plater::export_obj(bool extended, bool selection_only)
|
||||
{
|
||||
if (p->model.objects.empty()) { return; }
|
||||
|
||||
wxString path = p->get_export_file(FT_OBJ);
|
||||
if (path.empty()) { return; }
|
||||
const std::string path_u8 = into_u8(path);
|
||||
|
||||
wxBusyCursor wait;
|
||||
|
||||
const auto& selection = p->get_selection();
|
||||
const auto obj_idx = selection.get_object_idx();
|
||||
if (selection_only && (obj_idx == -1 || selection.is_wipe_tower()))
|
||||
return;
|
||||
|
||||
// Following lambda generates a combined mesh for export with normals pointing outwards.
|
||||
auto mesh_to_export = [](const ModelObject& mo, int instance_id) {
|
||||
TriangleMesh mesh;
|
||||
for (const ModelVolume* v : mo.volumes)
|
||||
if (v->is_model_part()) {
|
||||
TriangleMesh vol_mesh(v->mesh());
|
||||
vol_mesh.transform(v->get_matrix(), true);
|
||||
mesh.merge(vol_mesh);
|
||||
}
|
||||
if (instance_id == -1) {
|
||||
TriangleMesh vols_mesh(mesh);
|
||||
mesh = TriangleMesh();
|
||||
for (const ModelInstance* i : mo.instances) {
|
||||
TriangleMesh m = vols_mesh;
|
||||
m.transform(i->get_matrix(), true);
|
||||
mesh.merge(m);
|
||||
}
|
||||
}
|
||||
else if (0 <= instance_id && instance_id < int(mo.instances.size()))
|
||||
mesh.transform(mo.instances[instance_id]->get_matrix(), true);
|
||||
|
||||
return mesh;
|
||||
};
|
||||
|
||||
TriangleMesh mesh;
|
||||
if (p->printer_technology == ptFFF) {
|
||||
if (selection_only) {
|
||||
const ModelObject* model_object = p->model.objects[obj_idx];
|
||||
if (selection.get_mode() == Selection::Instance)
|
||||
mesh = mesh_to_export(*model_object, (selection.is_single_full_object() && model_object->instances.size() > 1) ? -1 : selection.get_instance_idx());
|
||||
else {
|
||||
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
|
||||
mesh = model_object->volumes[volume->volume_idx()]->mesh();
|
||||
mesh.transform(volume->get_volume_transformation().get_matrix(), true);
|
||||
}
|
||||
|
||||
if (!selection.is_single_full_object() || model_object->instances.size() == 1)
|
||||
mesh.translate(-model_object->origin_translation.cast<float>());
|
||||
}
|
||||
else {
|
||||
for (const ModelObject* o : p->model.objects) {
|
||||
mesh.merge(mesh_to_export(*o, -1));
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// This is SLA mode, all objects have only one volume.
|
||||
// However, we must have a look at the backend to load
|
||||
// hollowed mesh and/or supports
|
||||
|
||||
const PrintObjects& objects = p->sla_print.objects();
|
||||
for (const SLAPrintObject* object : objects) {
|
||||
const ModelObject* model_object = object->model_object();
|
||||
if (selection_only) {
|
||||
if (model_object->id() != p->model.objects[obj_idx]->id())
|
||||
continue;
|
||||
}
|
||||
const Transform3d mesh_trafo_inv = object->trafo().inverse();
|
||||
const bool is_left_handed = object->is_left_handed();
|
||||
|
||||
TriangleMesh pad_mesh;
|
||||
const bool has_pad_mesh = extended && object->has_mesh(slaposPad);
|
||||
if (has_pad_mesh) {
|
||||
pad_mesh = object->get_mesh(slaposPad);
|
||||
pad_mesh.transform(mesh_trafo_inv);
|
||||
}
|
||||
|
||||
TriangleMesh supports_mesh;
|
||||
const bool has_supports_mesh = extended && object->has_mesh(slaposSupportTree);
|
||||
if (has_supports_mesh) {
|
||||
supports_mesh = object->get_mesh(slaposSupportTree);
|
||||
supports_mesh.transform(mesh_trafo_inv);
|
||||
}
|
||||
const std::vector<SLAPrintObject::Instance>& obj_instances = object->instances();
|
||||
for (const SLAPrintObject::Instance& obj_instance : obj_instances) {
|
||||
auto it = std::find_if(model_object->instances.begin(), model_object->instances.end(),
|
||||
[&obj_instance](const ModelInstance* mi) { return mi->id() == obj_instance.instance_id; });
|
||||
assert(it != model_object->instances.end());
|
||||
|
||||
if (it != model_object->instances.end()) {
|
||||
const bool one_inst_only = selection_only && !selection.is_single_full_object();
|
||||
|
||||
const int instance_idx = it - model_object->instances.begin();
|
||||
const Transform3d& inst_transform = one_inst_only
|
||||
? Transform3d::Identity()
|
||||
: object->model_object()->instances[instance_idx]->get_transformation().get_matrix();
|
||||
|
||||
TriangleMesh inst_mesh;
|
||||
|
||||
if (has_pad_mesh) {
|
||||
TriangleMesh inst_pad_mesh = pad_mesh;
|
||||
inst_pad_mesh.transform(inst_transform, is_left_handed);
|
||||
inst_mesh.merge(inst_pad_mesh);
|
||||
}
|
||||
|
||||
if (has_supports_mesh) {
|
||||
TriangleMesh inst_supports_mesh = supports_mesh;
|
||||
inst_supports_mesh.transform(inst_transform, is_left_handed);
|
||||
inst_mesh.merge(inst_supports_mesh);
|
||||
}
|
||||
|
||||
TriangleMesh inst_object_mesh = object->get_mesh_to_slice();
|
||||
inst_object_mesh.transform(mesh_trafo_inv);
|
||||
inst_object_mesh.transform(inst_transform, is_left_handed);
|
||||
|
||||
inst_mesh.merge(inst_object_mesh);
|
||||
|
||||
// ensure that the instance lays on the bed
|
||||
inst_mesh.translate(0.0f, 0.0f, -inst_mesh.bounding_box().min.z());
|
||||
|
||||
// merge instance with global mesh
|
||||
mesh.merge(inst_mesh);
|
||||
|
||||
if (one_inst_only)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Slic3r::store_obj(path_u8.c_str(), &mesh);
|
||||
}
|
||||
|
||||
void Plater::export_amf()
|
||||
{
|
||||
if (p->model.objects.empty()) { return; }
|
||||
|
@ -257,6 +257,7 @@ public:
|
||||
|
||||
void export_gcode(bool prefer_removable);
|
||||
void export_stl(bool extended = false, bool selection_only = false);
|
||||
void export_obj(bool extended = false, bool selection_only = false);
|
||||
void export_amf();
|
||||
bool export_3mf(const boost::filesystem::path& output_path = boost::filesystem::path());
|
||||
void reload_from_disk();
|
||||
|
@ -76,7 +76,8 @@ PrintHostSendDialog::PrintHostSendDialog(const fs::path &path, PrintHostPostUplo
|
||||
txt_filename->SetValue(recent_path);
|
||||
txt_filename->SetFocus();
|
||||
|
||||
m_valid_suffix = recent_path.substr(recent_path.find_last_of('.'));
|
||||
if (size_t extension_start = recent_path.find_last_of('.'); extension_start != std::string::npos)
|
||||
m_valid_suffix = recent_path.substr(extension_start);
|
||||
// .gcode suffix control
|
||||
auto validate_path = [this](const wxString &path) -> bool {
|
||||
if (! path.Lower().EndsWith(m_valid_suffix.Lower())) {
|
||||
|
@ -132,9 +132,8 @@ bool Selection::init()
|
||||
{
|
||||
m_arrow.init_from(straight_arrow(10.0f, 5.0f, 5.0f, 10.0f, 1.0f));
|
||||
m_curved_arrow.init_from(circular_arrow(16, 10.0f, 5.0f, 10.0f, 5.0f, 1.0f));
|
||||
|
||||
#if ENABLE_RENDER_SELECTION_CENTER
|
||||
m_vbo_sphere.init_from(make_sphere(0.75, 2*PI/24));
|
||||
m_vbo_sphere.init_from(its_make_sphere(0.75, PI / 12.0));
|
||||
#endif // ENABLE_RENDER_SELECTION_CENTER
|
||||
|
||||
return true;
|
||||
@ -539,7 +538,11 @@ bool Selection::is_single_full_instance() const
|
||||
bool Selection::is_from_single_object() const
|
||||
{
|
||||
const int idx = get_object_idx();
|
||||
#if ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL
|
||||
return 0 <= idx && idx < int(m_model->objects.size());
|
||||
#else
|
||||
return 0 <= idx && idx < 1000;
|
||||
#endif // ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL
|
||||
}
|
||||
|
||||
bool Selection::is_sla_compliant() const
|
||||
@ -1067,9 +1070,16 @@ void Selection::translate(unsigned int object_idx, const Vec3d& displacement)
|
||||
if (done.size() == m_volumes->size())
|
||||
break;
|
||||
|
||||
#if ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL
|
||||
if ((*m_volumes)[i]->is_wipe_tower)
|
||||
continue;
|
||||
|
||||
int object_idx = (*m_volumes)[i]->object_idx();
|
||||
#else
|
||||
int object_idx = (*m_volumes)[i]->object_idx();
|
||||
if (object_idx >= 1000)
|
||||
continue;
|
||||
#endif // ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL
|
||||
|
||||
// Process unselected volumes of the object.
|
||||
for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j) {
|
||||
@ -1109,9 +1119,16 @@ void Selection::translate(unsigned int object_idx, unsigned int instance_idx, co
|
||||
if (done.size() == m_volumes->size())
|
||||
break;
|
||||
|
||||
#if ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL
|
||||
if ((*m_volumes)[i]->is_wipe_tower)
|
||||
continue;
|
||||
|
||||
int object_idx = (*m_volumes)[i]->object_idx();
|
||||
#else
|
||||
int object_idx = (*m_volumes)[i]->object_idx();
|
||||
if (object_idx >= 1000)
|
||||
continue;
|
||||
#endif // ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL
|
||||
|
||||
// Process unselected volumes of the object.
|
||||
for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j) {
|
||||
@ -1238,20 +1255,29 @@ void Selection::erase()
|
||||
}
|
||||
}
|
||||
|
||||
#if ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||
void Selection::render(float scale_factor)
|
||||
#else
|
||||
void Selection::render(float scale_factor) const
|
||||
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||
{
|
||||
if (!m_valid || is_empty())
|
||||
return;
|
||||
|
||||
#if ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||
m_scale_factor = scale_factor;
|
||||
render_bounding_box(get_bounding_box(), ColorRGB::WHITE());
|
||||
#else
|
||||
*const_cast<float*>(&m_scale_factor) = scale_factor;
|
||||
|
||||
// render cumulative bounding box of selected volumes
|
||||
render_selected_volumes();
|
||||
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||
render_synchronized_volumes();
|
||||
}
|
||||
|
||||
#if ENABLE_RENDER_SELECTION_CENTER
|
||||
void Selection::render_center(bool gizmo_is_dragging) const
|
||||
void Selection::render_center(bool gizmo_is_dragging)
|
||||
{
|
||||
if (!m_valid || is_empty())
|
||||
return;
|
||||
@ -1260,15 +1286,31 @@ void Selection::render_center(bool gizmo_is_dragging) const
|
||||
|
||||
glsafe(::glDisable(GL_DEPTH_TEST));
|
||||
|
||||
glsafe(::glColor3f(1.0f, 1.0f, 1.0f));
|
||||
glsafe(::glPushMatrix());
|
||||
glsafe(::glTranslated(center(0), center(1), center(2)));
|
||||
glsafe(::glTranslated(center.x(), center.y(), center.z()));
|
||||
|
||||
#if ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||
GLShaderProgram* shader = wxGetApp().get_shader("flat");
|
||||
if (shader == nullptr)
|
||||
return;
|
||||
|
||||
shader->start_using();
|
||||
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||
m_vbo_sphere.set_color(-1, ColorRGBA::WHITE());
|
||||
m_vbo_sphere.render();
|
||||
#if ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||
shader->stop_using();
|
||||
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||
|
||||
glsafe(::glPopMatrix());
|
||||
}
|
||||
#endif // ENABLE_RENDER_SELECTION_CENTER
|
||||
|
||||
#if ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||
void Selection::render_sidebar_hints(const std::string& sidebar_field)
|
||||
#else
|
||||
void Selection::render_sidebar_hints(const std::string& sidebar_field) const
|
||||
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||
{
|
||||
if (sidebar_field.empty())
|
||||
return;
|
||||
@ -1292,7 +1334,7 @@ void Selection::render_sidebar_hints(const std::string& sidebar_field) const
|
||||
const Vec3d& center = get_bounding_box().center();
|
||||
|
||||
if (is_single_full_instance() && !wxGetApp().obj_manipul()->get_world_coordinates()) {
|
||||
glsafe(::glTranslated(center(0), center(1), center(2)));
|
||||
glsafe(::glTranslated(center.x(), center.y(), center.z()));
|
||||
if (!boost::starts_with(sidebar_field, "position")) {
|
||||
Transform3d orient_matrix = Transform3d::Identity();
|
||||
if (boost::starts_with(sidebar_field, "scale"))
|
||||
@ -1312,7 +1354,7 @@ void Selection::render_sidebar_hints(const std::string& sidebar_field) const
|
||||
glsafe(::glMultMatrixd(orient_matrix.data()));
|
||||
}
|
||||
} else if (is_single_volume() || is_single_modifier()) {
|
||||
glsafe(::glTranslated(center(0), center(1), center(2)));
|
||||
glsafe(::glTranslated(center.x(), center.y(), center.z()));
|
||||
Transform3d orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true);
|
||||
if (!boost::starts_with(sidebar_field, "position"))
|
||||
orient_matrix = orient_matrix * (*m_volumes)[*m_list.begin()]->get_volume_transformation().get_matrix(true, false, true, true);
|
||||
@ -1780,50 +1822,172 @@ void Selection::do_remove_object(unsigned int object_idx)
|
||||
}
|
||||
}
|
||||
|
||||
#if !ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||
void Selection::render_selected_volumes() const
|
||||
{
|
||||
float color[3] = { 1.0f, 1.0f, 1.0f };
|
||||
render_bounding_box(get_bounding_box(), color);
|
||||
}
|
||||
#endif // !ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||
|
||||
#if ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||
void Selection::render_synchronized_volumes()
|
||||
#else
|
||||
void Selection::render_synchronized_volumes() const
|
||||
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||
{
|
||||
if (m_mode == Instance)
|
||||
return;
|
||||
|
||||
#if !ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||
float color[3] = { 1.0f, 1.0f, 0.0f };
|
||||
#endif // !ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||
|
||||
for (unsigned int i : m_list) {
|
||||
const GLVolume* volume = (*m_volumes)[i];
|
||||
int object_idx = volume->object_idx();
|
||||
int volume_idx = volume->volume_idx();
|
||||
const GLVolume& volume = *(*m_volumes)[i];
|
||||
int object_idx = volume.object_idx();
|
||||
int volume_idx = volume.volume_idx();
|
||||
for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j) {
|
||||
if (i == j)
|
||||
continue;
|
||||
|
||||
const GLVolume* v = (*m_volumes)[j];
|
||||
if (v->object_idx() != object_idx || v->volume_idx() != volume_idx)
|
||||
const GLVolume& v = *(*m_volumes)[j];
|
||||
if (v.object_idx() != object_idx || v.volume_idx() != volume_idx)
|
||||
continue;
|
||||
|
||||
render_bounding_box(v->transformed_convex_hull_bounding_box(), color);
|
||||
#if ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||
render_bounding_box(v.transformed_convex_hull_bounding_box(), ColorRGB::YELLOW());
|
||||
#else
|
||||
render_bounding_box(v.transformed_convex_hull_bounding_box(), color);
|
||||
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Selection::render_bounding_box(const BoundingBoxf3& box, float* color) const
|
||||
#if ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||
static bool is_approx(const Vec3d& v1, const Vec3d& v2)
|
||||
{
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
if (std::abs(v1[i] - v2[i]) > EPSILON)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Selection::render_bounding_box(const BoundingBoxf3& box, const ColorRGB& color)
|
||||
{
|
||||
#else
|
||||
void Selection::render_bounding_box(const BoundingBoxf3 & box, float* color) const
|
||||
{
|
||||
if (color == nullptr)
|
||||
return;
|
||||
|
||||
Vec3f b_min = box.min.cast<float>();
|
||||
Vec3f b_max = box.max.cast<float>();
|
||||
Vec3f size = 0.2f * box.size().cast<float>();
|
||||
const Vec3f b_min = box.min.cast<float>();
|
||||
const Vec3f b_max = box.max.cast<float>();
|
||||
const Vec3f size = 0.2f * box.size().cast<float>();
|
||||
|
||||
glsafe(::glEnable(GL_DEPTH_TEST));
|
||||
glsafe(::glColor3fv(color));
|
||||
glsafe(::glLineWidth(2.0f * m_scale_factor));
|
||||
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||
|
||||
#if ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||
const BoundingBoxf3& curr_box = m_box.get_bounding_box();
|
||||
if (!m_box.is_initialized() || !is_approx(box.min, curr_box.min) || !is_approx(box.max, curr_box.max)) {
|
||||
m_box.reset();
|
||||
|
||||
const Vec3f b_min = box.min.cast<float>();
|
||||
const Vec3f b_max = box.max.cast<float>();
|
||||
const Vec3f size = 0.2f * box.size().cast<float>();
|
||||
|
||||
GLModel::InitializationData init_data;
|
||||
GUI::GLModel::InitializationData::Entity entity;
|
||||
entity.type = GUI::GLModel::PrimitiveType::Lines;
|
||||
entity.positions.reserve(48);
|
||||
|
||||
entity.positions.emplace_back(Vec3f(b_min.x(), b_min.y(), b_min.z()));
|
||||
entity.positions.emplace_back(Vec3f(b_min.x() + size.x(), b_min.y(), b_min.z()));
|
||||
entity.positions.emplace_back(Vec3f(b_min.x(), b_min.y(), b_min.z()));
|
||||
entity.positions.emplace_back(Vec3f(b_min.x(), b_min.y() + size.y(), b_min.z()));
|
||||
entity.positions.emplace_back(Vec3f(b_min.x(), b_min.y(), b_min.z()));
|
||||
entity.positions.emplace_back(Vec3f(b_min.x(), b_min.y(), b_min.z() + size.z()));
|
||||
|
||||
entity.positions.emplace_back(Vec3f(b_max.x(), b_min.y(), b_min.z()));
|
||||
entity.positions.emplace_back(Vec3f(b_max.x() - size.x(), b_min.y(), b_min.z()));
|
||||
entity.positions.emplace_back(Vec3f(b_max.x(), b_min.y(), b_min.z()));
|
||||
entity.positions.emplace_back(Vec3f(b_max.x(), b_min.y() + size.y(), b_min.z()));
|
||||
entity.positions.emplace_back(Vec3f(b_max.x(), b_min.y(), b_min.z()));
|
||||
entity.positions.emplace_back(Vec3f(b_max.x(), b_min.y(), b_min.z() + size.z()));
|
||||
|
||||
entity.positions.emplace_back(Vec3f(b_max.x(), b_max.y(), b_min.z()));
|
||||
entity.positions.emplace_back(Vec3f(b_max.x() - size.x(), b_max.y(), b_min.z()));
|
||||
entity.positions.emplace_back(Vec3f(b_max.x(), b_max.y(), b_min.z()));
|
||||
entity.positions.emplace_back(Vec3f(b_max.x(), b_max.y() - size.y(), b_min.z()));
|
||||
entity.positions.emplace_back(Vec3f(b_max.x(), b_max.y(), b_min.z()));
|
||||
entity.positions.emplace_back(Vec3f(b_max.x(), b_max.y(), b_min.z() + size.z()));
|
||||
|
||||
entity.positions.emplace_back(Vec3f(b_min.x(), b_max.y(), b_min.z()));
|
||||
entity.positions.emplace_back(Vec3f(b_min.x() + size.x(), b_max.y(), b_min.z()));
|
||||
entity.positions.emplace_back(Vec3f(b_min.x(), b_max.y(), b_min.z()));
|
||||
entity.positions.emplace_back(Vec3f(b_min.x(), b_max.y() - size.y(), b_min.z()));
|
||||
entity.positions.emplace_back(Vec3f(b_min.x(), b_max.y(), b_min.z()));
|
||||
entity.positions.emplace_back(Vec3f(b_min.x(), b_max.y(), b_min.z() + size.z()));
|
||||
|
||||
entity.positions.emplace_back(Vec3f(b_min.x(), b_min.y(), b_max.z()));
|
||||
entity.positions.emplace_back(Vec3f(b_min.x() + size.x(), b_min.y(), b_max.z()));
|
||||
entity.positions.emplace_back(Vec3f(b_min.x(), b_min.y(), b_max.z()));
|
||||
entity.positions.emplace_back(Vec3f(b_min.x(), b_min.y() + size.y(), b_max.z()));
|
||||
entity.positions.emplace_back(Vec3f(b_min.x(), b_min.y(), b_max.z()));
|
||||
entity.positions.emplace_back(Vec3f(b_min.x(), b_min.y(), b_max.z() - size.z()));
|
||||
|
||||
entity.positions.emplace_back(Vec3f(b_max.x(), b_min.y(), b_max.z()));
|
||||
entity.positions.emplace_back(Vec3f(b_max.x() - size.x(), b_min.y(), b_max.z()));
|
||||
entity.positions.emplace_back(Vec3f(b_max.x(), b_min.y(), b_max.z()));
|
||||
entity.positions.emplace_back(Vec3f(b_max.x(), b_min.y() + size.y(), b_max.z()));
|
||||
entity.positions.emplace_back(Vec3f(b_max.x(), b_min.y(), b_max.z()));
|
||||
entity.positions.emplace_back(Vec3f(b_max.x(), b_min.y(), b_max.z() - size.z()));
|
||||
|
||||
entity.positions.emplace_back(Vec3f(b_max.x(), b_max.y(), b_max.z()));
|
||||
entity.positions.emplace_back(Vec3f(b_max.x() - size.x(), b_max.y(), b_max.z()));
|
||||
entity.positions.emplace_back(Vec3f(b_max.x(), b_max.y(), b_max.z()));
|
||||
entity.positions.emplace_back(Vec3f(b_max.x(), b_max.y() - size.y(), b_max.z()));
|
||||
entity.positions.emplace_back(Vec3f(b_max.x(), b_max.y(), b_max.z()));
|
||||
entity.positions.emplace_back(Vec3f(b_max.x(), b_max.y(), b_max.z() - size.z()));
|
||||
|
||||
entity.positions.emplace_back(Vec3f(b_min.x(), b_max.y(), b_max.z()));
|
||||
entity.positions.emplace_back(Vec3f(b_min.x() + size.x(), b_max.y(), b_max.z()));
|
||||
entity.positions.emplace_back(Vec3f(b_min.x(), b_max.y(), b_max.z()));
|
||||
entity.positions.emplace_back(Vec3f(b_min.x(), b_max.y() - size.y(), b_max.z()));
|
||||
entity.positions.emplace_back(Vec3f(b_min.x(), b_max.y(), b_max.z()));
|
||||
entity.positions.emplace_back(Vec3f(b_min.x(), b_max.y(), b_max.z() - size.z()));
|
||||
|
||||
entity.normals.reserve(48);
|
||||
for (size_t i = 0; i < 48; ++i) {
|
||||
entity.normals.emplace_back(Vec3f::UnitZ());
|
||||
}
|
||||
|
||||
entity.indices.reserve(48);
|
||||
for (size_t i = 0; i < 48; ++i) {
|
||||
entity.indices.emplace_back(i);
|
||||
}
|
||||
|
||||
init_data.entities.emplace_back(entity);
|
||||
m_box.init_from(init_data);
|
||||
}
|
||||
|
||||
glsafe(::glEnable(GL_DEPTH_TEST));
|
||||
|
||||
glsafe(::glColor3fv(color));
|
||||
glsafe(::glLineWidth(2.0f * m_scale_factor));
|
||||
|
||||
GLShaderProgram* shader = wxGetApp().get_shader("flat");
|
||||
if (shader == nullptr)
|
||||
return;
|
||||
|
||||
shader->start_using();
|
||||
m_box.set_color(-1, to_rgba(color));
|
||||
m_box.render();
|
||||
shader->stop_using();
|
||||
#else
|
||||
::glBegin(GL_LINES);
|
||||
|
||||
::glVertex3f(b_min(0), b_min(1), b_min(2)); ::glVertex3f(b_min(0) + size(0), b_min(1), b_min(2));
|
||||
@ -1859,6 +2023,7 @@ void Selection::render_bounding_box(const BoundingBoxf3& box, float* color) cons
|
||||
::glVertex3f(b_min(0), b_max(1), b_max(2)); ::glVertex3f(b_min(0), b_max(1), b_max(2) - size(2));
|
||||
|
||||
glsafe(::glEnd());
|
||||
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||
}
|
||||
|
||||
static ColorRGBA get_color(Axis axis)
|
||||
@ -1866,6 +2031,25 @@ static ColorRGBA get_color(Axis axis)
|
||||
return AXES_COLOR[axis];
|
||||
}
|
||||
|
||||
#if ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||
void Selection::render_sidebar_position_hints(const std::string& sidebar_field)
|
||||
{
|
||||
if (boost::ends_with(sidebar_field, "x")) {
|
||||
glsafe(::glRotated(-90.0, 0.0, 0.0, 1.0));
|
||||
m_arrow.set_color(-1, get_color(X));
|
||||
m_arrow.render();
|
||||
}
|
||||
else if (boost::ends_with(sidebar_field, "y")) {
|
||||
m_arrow.set_color(-1, get_color(Y));
|
||||
m_arrow.render();
|
||||
}
|
||||
else if (boost::ends_with(sidebar_field, "z")) {
|
||||
glsafe(::glRotated(90.0, 1.0, 0.0, 0.0));
|
||||
m_arrow.set_color(-1, get_color(Z));
|
||||
m_arrow.render();
|
||||
}
|
||||
}
|
||||
#else
|
||||
void Selection::render_sidebar_position_hints(const std::string& sidebar_field) const
|
||||
{
|
||||
if (boost::ends_with(sidebar_field, "x")) {
|
||||
@ -1883,7 +2067,33 @@ void Selection::render_sidebar_position_hints(const std::string& sidebar_field)
|
||||
m_arrow.render();
|
||||
}
|
||||
}
|
||||
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||
|
||||
#if ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||
void Selection::render_sidebar_rotation_hints(const std::string& sidebar_field)
|
||||
{
|
||||
auto render_sidebar_rotation_hint = [this]() {
|
||||
m_curved_arrow.render();
|
||||
glsafe(::glRotated(180.0, 0.0, 0.0, 1.0));
|
||||
m_curved_arrow.render();
|
||||
};
|
||||
|
||||
if (boost::ends_with(sidebar_field, "x")) {
|
||||
glsafe(::glRotated(90.0, 0.0, 1.0, 0.0));
|
||||
m_curved_arrow.set_color(-1, get_color(X));
|
||||
render_sidebar_rotation_hint();
|
||||
}
|
||||
else if (boost::ends_with(sidebar_field, "y")) {
|
||||
glsafe(::glRotated(-90.0, 1.0, 0.0, 0.0));
|
||||
m_curved_arrow.set_color(-1, get_color(Y));
|
||||
render_sidebar_rotation_hint();
|
||||
}
|
||||
else if (boost::ends_with(sidebar_field, "z")) {
|
||||
m_curved_arrow.set_color(-1, get_color(Z));
|
||||
render_sidebar_rotation_hint();
|
||||
}
|
||||
}
|
||||
#else
|
||||
void Selection::render_sidebar_rotation_hints(const std::string& sidebar_field) const
|
||||
{
|
||||
auto render_sidebar_rotation_hint = [this]() {
|
||||
@ -1907,13 +2117,22 @@ void Selection::render_sidebar_rotation_hints(const std::string& sidebar_field)
|
||||
render_sidebar_rotation_hint();
|
||||
}
|
||||
}
|
||||
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||
|
||||
#if ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||
void Selection::render_sidebar_scale_hints(const std::string& sidebar_field)
|
||||
#else
|
||||
void Selection::render_sidebar_scale_hints(const std::string& sidebar_field) const
|
||||
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||
{
|
||||
bool uniform_scale = requires_uniform_scale() || wxGetApp().obj_manipul()->get_uniform_scaling();
|
||||
|
||||
auto render_sidebar_scale_hint = [this, uniform_scale](Axis axis) {
|
||||
#if ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||
m_arrow.set_color(-1, uniform_scale ? UNIFORM_SCALE_COLOR : get_color(axis));
|
||||
#else
|
||||
const_cast<GLModel*>(&m_arrow)->set_color(-1, uniform_scale ? UNIFORM_SCALE_COLOR : get_color(axis));
|
||||
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||
GLShaderProgram* shader = wxGetApp().get_current_shader();
|
||||
if (shader != nullptr)
|
||||
shader->set_uniform("emission_factor", 0.0f);
|
||||
@ -1947,9 +2166,13 @@ void Selection::render_sidebar_scale_hints(const std::string& sidebar_field) con
|
||||
}
|
||||
}
|
||||
|
||||
#if ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||
void Selection::render_sidebar_layers_hints(const std::string& sidebar_field)
|
||||
#else
|
||||
void Selection::render_sidebar_layers_hints(const std::string& sidebar_field) const
|
||||
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||
{
|
||||
static const double Margin = 10.0;
|
||||
static const float Margin = 10.0f;
|
||||
|
||||
std::string field = sidebar_field;
|
||||
|
||||
@ -1958,7 +2181,7 @@ void Selection::render_sidebar_layers_hints(const std::string& sidebar_field) co
|
||||
if (pos == std::string::npos)
|
||||
return;
|
||||
|
||||
double max_z = string_to_double_decimal_point(field.substr(pos + 1));
|
||||
const float max_z = float(string_to_double_decimal_point(field.substr(pos + 1)));
|
||||
|
||||
// extract min_z
|
||||
field = field.substr(0, pos);
|
||||
@ -1966,7 +2189,7 @@ void Selection::render_sidebar_layers_hints(const std::string& sidebar_field) co
|
||||
if (pos == std::string::npos)
|
||||
return;
|
||||
|
||||
const double min_z = string_to_double_decimal_point(field.substr(pos + 1));
|
||||
const float min_z = float(string_to_double_decimal_point(field.substr(pos + 1)));
|
||||
|
||||
// extract type
|
||||
field = field.substr(0, pos);
|
||||
@ -1978,24 +2201,101 @@ void Selection::render_sidebar_layers_hints(const std::string& sidebar_field) co
|
||||
|
||||
const BoundingBoxf3& box = get_bounding_box();
|
||||
|
||||
const float min_x = box.min(0) - Margin;
|
||||
const float max_x = box.max(0) + Margin;
|
||||
const float min_y = box.min(1) - Margin;
|
||||
const float max_y = box.max(1) + Margin;
|
||||
#if !ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||
const float min_x = float(box.min.x()) - Margin;
|
||||
const float max_x = float(box.max.x()) + Margin;
|
||||
const float min_y = float(box.min.y()) - Margin;
|
||||
const float max_y = float(box.max.y()) + Margin;
|
||||
#endif // !ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||
|
||||
// view dependend order of rendering to keep correct transparency
|
||||
bool camera_on_top = wxGetApp().plater()->get_camera().is_looking_downward();
|
||||
const bool camera_on_top = wxGetApp().plater()->get_camera().is_looking_downward();
|
||||
const float z1 = camera_on_top ? min_z : max_z;
|
||||
const float z2 = camera_on_top ? max_z : min_z;
|
||||
|
||||
#if ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||
const Vec3f p1 = { float(box.min.x()) - Margin, float(box.min.y()) - Margin, z1 };
|
||||
const Vec3f p2 = { float(box.max.x()) + Margin, float(box.max.y()) + Margin, z2 };
|
||||
#else
|
||||
const float min_x = float(box.min.x()) - Margin;
|
||||
const float max_x = float(box.max.x()) + Margin;
|
||||
const float min_y = float(box.min.y()) - Margin;
|
||||
const float max_y = float(box.max.y()) + Margin;
|
||||
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||
|
||||
glsafe(::glEnable(GL_DEPTH_TEST));
|
||||
glsafe(::glDisable(GL_CULL_FACE));
|
||||
glsafe(::glEnable(GL_BLEND));
|
||||
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
|
||||
|
||||
#if ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||
if (!m_planes.models[0].is_initialized() || !is_approx(m_planes.check_points[0].cast<double>(), p1.cast<double>())) {
|
||||
m_planes.check_points[0] = p1;
|
||||
m_planes.models[0].reset();
|
||||
|
||||
GLModel::InitializationData init_data;
|
||||
GUI::GLModel::InitializationData::Entity entity;
|
||||
entity.type = GUI::GLModel::PrimitiveType::Triangles;
|
||||
entity.positions.reserve(4);
|
||||
entity.positions.emplace_back(Vec3f(p1.x(), p1.y(), z1));
|
||||
entity.positions.emplace_back(Vec3f(p2.x(), p1.y(), z1));
|
||||
entity.positions.emplace_back(Vec3f(p2.x(), p2.y(), z1));
|
||||
entity.positions.emplace_back(Vec3f(p1.x(), p2.y(), z1));
|
||||
|
||||
entity.normals.reserve(4);
|
||||
for (size_t i = 0; i < 4; ++i) {
|
||||
entity.normals.emplace_back(Vec3f::UnitZ());
|
||||
}
|
||||
|
||||
entity.indices.reserve(6);
|
||||
entity.indices.emplace_back(0);
|
||||
entity.indices.emplace_back(1);
|
||||
entity.indices.emplace_back(2);
|
||||
entity.indices.emplace_back(2);
|
||||
entity.indices.emplace_back(3);
|
||||
entity.indices.emplace_back(0);
|
||||
|
||||
init_data.entities.emplace_back(entity);
|
||||
m_planes.models[0].init_from(init_data);
|
||||
}
|
||||
|
||||
if (!m_planes.models[1].is_initialized() || !is_approx(m_planes.check_points[1].cast<double>(), p2.cast<double>())) {
|
||||
m_planes.check_points[1] = p2;
|
||||
m_planes.models[1].reset();
|
||||
|
||||
GLModel::InitializationData init_data;
|
||||
GUI::GLModel::InitializationData::Entity entity;
|
||||
entity.type = GUI::GLModel::PrimitiveType::Triangles;
|
||||
entity.positions.reserve(4);
|
||||
entity.positions.emplace_back(Vec3f(p1.x(), p1.y(), z2));
|
||||
entity.positions.emplace_back(Vec3f(p2.x(), p1.y(), z2));
|
||||
entity.positions.emplace_back(Vec3f(p2.x(), p2.y(), z2));
|
||||
entity.positions.emplace_back(Vec3f(p1.x(), p2.y(), z2));
|
||||
|
||||
entity.normals.reserve(4);
|
||||
for (size_t i = 0; i < 4; ++i) {
|
||||
entity.normals.emplace_back(Vec3f::UnitZ());
|
||||
}
|
||||
|
||||
entity.indices.reserve(6);
|
||||
entity.indices.emplace_back(0);
|
||||
entity.indices.emplace_back(1);
|
||||
entity.indices.emplace_back(2);
|
||||
entity.indices.emplace_back(2);
|
||||
entity.indices.emplace_back(3);
|
||||
entity.indices.emplace_back(0);
|
||||
|
||||
init_data.entities.emplace_back(entity);
|
||||
m_planes.models[1].init_from(init_data);
|
||||
}
|
||||
|
||||
m_planes.models[0].set_color(-1, (camera_on_top && type == 1) || (!camera_on_top && type == 2) ? SOLID_PLANE_COLOR : TRANSPARENT_PLANE_COLOR);
|
||||
m_planes.models[0].render();
|
||||
m_planes.models[1].set_color(-1, (camera_on_top && type == 2) || (!camera_on_top && type == 1) ? SOLID_PLANE_COLOR : TRANSPARENT_PLANE_COLOR);
|
||||
m_planes.models[1].render();
|
||||
#else
|
||||
::glBegin(GL_QUADS);
|
||||
::glColor4fv((camera_on_top && type == 1) || (!camera_on_top && type == 2) ?
|
||||
SOLID_PLANE_COLOR.data() : TRANSPARENT_PLANE_COLOR.data());
|
||||
::glColor4fv((camera_on_top && type == 1) || (!camera_on_top && type == 2) ? SOLID_PLANE_COLOR.data() : TRANSPARENT_PLANE_COLOR.data());
|
||||
::glVertex3f(min_x, min_y, z1);
|
||||
::glVertex3f(max_x, min_y, z1);
|
||||
::glVertex3f(max_x, max_y, z1);
|
||||
@ -2003,13 +2303,13 @@ void Selection::render_sidebar_layers_hints(const std::string& sidebar_field) co
|
||||
glsafe(::glEnd());
|
||||
|
||||
::glBegin(GL_QUADS);
|
||||
::glColor4fv((camera_on_top && type == 2) || (!camera_on_top && type == 1) ?
|
||||
SOLID_PLANE_COLOR.data() : TRANSPARENT_PLANE_COLOR.data());
|
||||
::glColor4fv((camera_on_top && type == 2) || (!camera_on_top && type == 1) ? SOLID_PLANE_COLOR.data() : TRANSPARENT_PLANE_COLOR.data());
|
||||
::glVertex3f(min_x, min_y, z2);
|
||||
::glVertex3f(max_x, min_y, z2);
|
||||
::glVertex3f(max_x, max_y, z2);
|
||||
::glVertex3f(min_x, max_y, z2);
|
||||
glsafe(::glEnd());
|
||||
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||
|
||||
glsafe(::glEnable(GL_CULL_FACE));
|
||||
glsafe(::glDisable(GL_BLEND));
|
||||
@ -2062,9 +2362,16 @@ void Selection::synchronize_unselected_instances(SyncRotationType sync_rotation_
|
||||
break;
|
||||
|
||||
const GLVolume* volume = (*m_volumes)[i];
|
||||
#if ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL
|
||||
if (volume->is_wipe_tower)
|
||||
continue;
|
||||
|
||||
const int object_idx = volume->object_idx();
|
||||
#else
|
||||
const int object_idx = volume->object_idx();
|
||||
if (object_idx >= 1000)
|
||||
continue;
|
||||
#endif // ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL
|
||||
|
||||
const int instance_idx = volume->instance_idx();
|
||||
const Vec3d& rotation = volume->get_instance_rotation();
|
||||
@ -2116,9 +2423,16 @@ void Selection::synchronize_unselected_volumes()
|
||||
{
|
||||
for (unsigned int i : m_list) {
|
||||
const GLVolume* volume = (*m_volumes)[i];
|
||||
#if ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL
|
||||
if (volume->is_wipe_tower)
|
||||
continue;
|
||||
|
||||
const int object_idx = volume->object_idx();
|
||||
#else
|
||||
const int object_idx = volume->object_idx();
|
||||
if (object_idx >= 1000)
|
||||
continue;
|
||||
#endif // ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL
|
||||
|
||||
const int volume_idx = volume->volume_idx();
|
||||
const Vec3d& offset = volume->get_volume_offset();
|
||||
|
@ -218,6 +218,15 @@ private:
|
||||
|
||||
GLModel m_arrow;
|
||||
GLModel m_curved_arrow;
|
||||
#if ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||
GLModel m_box;
|
||||
struct Planes
|
||||
{
|
||||
std::array<Vec3f, 2> check_points{ Vec3f::Zero(), Vec3f::Zero() };
|
||||
std::array<GLModel, 2> models;
|
||||
};
|
||||
Planes m_planes;
|
||||
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||
|
||||
float m_scale_factor;
|
||||
bool m_dragging;
|
||||
@ -329,11 +338,16 @@ public:
|
||||
|
||||
void erase();
|
||||
|
||||
#if ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||
void render(float scale_factor = 1.0);
|
||||
void render_sidebar_hints(const std::string& sidebar_field);
|
||||
#else
|
||||
void render(float scale_factor = 1.0) const;
|
||||
#if ENABLE_RENDER_SELECTION_CENTER
|
||||
void render_center(bool gizmo_is_dragging) const;
|
||||
#endif // ENABLE_RENDER_SELECTION_CENTER
|
||||
void render_sidebar_hints(const std::string& sidebar_field) const;
|
||||
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||
#if ENABLE_RENDER_SELECTION_CENTER
|
||||
void render_center(bool gizmo_is_dragging);
|
||||
#endif // ENABLE_RENDER_SELECTION_CENTER
|
||||
|
||||
bool requires_local_axes() const;
|
||||
|
||||
@ -363,6 +377,14 @@ private:
|
||||
void do_remove_instance(unsigned int object_idx, unsigned int instance_idx);
|
||||
void do_remove_object(unsigned int object_idx);
|
||||
void set_bounding_boxes_dirty() { m_bounding_box.reset(); m_unscaled_instance_bounding_box.reset(); m_scaled_instance_bounding_box.reset(); }
|
||||
#if ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||
void render_synchronized_volumes();
|
||||
void render_bounding_box(const BoundingBoxf3& box, const ColorRGB& color);
|
||||
void render_sidebar_position_hints(const std::string& sidebar_field);
|
||||
void render_sidebar_rotation_hints(const std::string& sidebar_field);
|
||||
void render_sidebar_scale_hints(const std::string& sidebar_field);
|
||||
void render_sidebar_layers_hints(const std::string& sidebar_field);
|
||||
#else
|
||||
void render_selected_volumes() const;
|
||||
void render_synchronized_volumes() const;
|
||||
void render_bounding_box(const BoundingBoxf3& box, float* color) const;
|
||||
@ -370,6 +392,7 @@ private:
|
||||
void render_sidebar_rotation_hints(const std::string& sidebar_field) const;
|
||||
void render_sidebar_scale_hints(const std::string& sidebar_field) const;
|
||||
void render_sidebar_layers_hints(const std::string& sidebar_field) const;
|
||||
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||
|
||||
public:
|
||||
enum SyncRotationType {
|
||||
|
@ -479,8 +479,7 @@ void Tab::update_label_colours()
|
||||
else
|
||||
color = &m_modified_label_clr;
|
||||
}
|
||||
if (opt.first == "bed_shape" || opt.first == "filament_ramming_parameters" ||
|
||||
opt.first == "compatible_prints" || opt.first == "compatible_printers" ) {
|
||||
if (PresetCollection::is_independent_from_extruder_number_option(opt.first)) {
|
||||
if (Line* line = get_line(opt.first))
|
||||
line->set_label_colour(color);
|
||||
continue;
|
||||
@ -521,8 +520,7 @@ void Tab::decorate()
|
||||
Field* field = nullptr;
|
||||
bool option_without_field = false;
|
||||
|
||||
if (opt.first == "bed_shape" || opt.first == "filament_ramming_parameters" ||
|
||||
opt.first == "compatible_prints" || opt.first == "compatible_printers")
|
||||
if(PresetCollection::is_independent_from_extruder_number_option(opt.first))
|
||||
option_without_field = true;
|
||||
|
||||
if (!option_without_field) {
|
||||
@ -812,6 +810,12 @@ void Tab::on_roll_back_value(const bool to_sys /*= true*/)
|
||||
if ((m_options_list["filament_ramming_parameters"] & os) == 0)
|
||||
to_sys ? group->back_to_sys_value("filament_ramming_parameters") : group->back_to_initial_value("filament_ramming_parameters");
|
||||
}
|
||||
if (group->title == "G-code Substitutions") {
|
||||
if ((m_options_list["gcode_substitutions"] & os) == 0) {
|
||||
to_sys ? group->back_to_sys_value("gcode_substitutions") : group->back_to_initial_value("gcode_substitutions");
|
||||
load_key_value("gcode_substitutions", true/*some value*/, true);
|
||||
}
|
||||
}
|
||||
if (group->title == "Profile dependencies") {
|
||||
// "compatible_printers" option doesn't exists in Printer Settimgs Tab
|
||||
if (m_type != Preset::TYPE_PRINTER && (m_options_list["compatible_printers"] & os) == 0) {
|
||||
@ -906,7 +910,7 @@ void Tab::update_visibility()
|
||||
page->update_visibility(m_mode, page.get() == m_active_page);
|
||||
rebuild_page_tree();
|
||||
|
||||
if (m_type == Preset::TYPE_SLA_PRINT)
|
||||
if (m_type == Preset::TYPE_SLA_PRINT || m_type == Preset::TYPE_PRINT)
|
||||
update_description_lines();
|
||||
|
||||
Layout();
|
||||
@ -1655,6 +1659,18 @@ void TabPrint::build()
|
||||
option.opt.height = 5;//50;
|
||||
optgroup->append_single_option_line(option);
|
||||
|
||||
optgroup = page->new_optgroup(L("G-code Substitutions"));
|
||||
|
||||
create_line_with_widget(optgroup.get(), "gcode_substitutions", "", [this](wxWindow* parent) {
|
||||
return create_manage_substitution_widget(parent);
|
||||
});
|
||||
line = { "", "" };
|
||||
line.full_width = 1;
|
||||
line.widget = [this](wxWindow* parent) {
|
||||
return create_substitutions_widget(parent);
|
||||
};
|
||||
optgroup->append_line(line);
|
||||
|
||||
page = add_options_page(L("Notes"), "note.png");
|
||||
optgroup = page->new_optgroup(L("Notes"), 0);
|
||||
option = optgroup->get_option("notes");
|
||||
@ -1699,13 +1715,21 @@ void TabPrint::update_description_lines()
|
||||
from_u8(PresetHints::top_bottom_shell_thickness_explanation(*m_preset_bundle)));
|
||||
}
|
||||
|
||||
if (m_active_page && m_active_page->title() == "Output options" && m_post_process_explanation) {
|
||||
if (m_active_page && m_active_page->title() == "Output options") {
|
||||
if (m_post_process_explanation) {
|
||||
m_post_process_explanation->SetText(
|
||||
_L("Post processing scripts shall modify G-code file in place."));
|
||||
#ifndef __linux__
|
||||
m_post_process_explanation->SetPathEnd("post-processing-scripts_283913");
|
||||
#endif // __linux__
|
||||
}
|
||||
// upadte G-code substitutions from the current configuration
|
||||
{
|
||||
m_subst_manager.update_from_config();
|
||||
if (m_del_all_substitutions_btn)
|
||||
m_del_all_substitutions_btn->Show(!m_subst_manager.is_empty_substitutions());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TabPrint::toggle_options()
|
||||
@ -3824,6 +3848,244 @@ wxSizer* Tab::compatible_widget_create(wxWindow* parent, PresetDependencies &dep
|
||||
return sizer;
|
||||
}
|
||||
|
||||
// G-code substitutions
|
||||
|
||||
void SubstitutionManager::init(DynamicPrintConfig* config, wxWindow* parent, wxFlexGridSizer* grid_sizer)
|
||||
{
|
||||
m_config = config;
|
||||
m_parent = parent;
|
||||
m_grid_sizer = grid_sizer;
|
||||
m_em = em_unit(parent);
|
||||
}
|
||||
|
||||
void SubstitutionManager::create_legend()
|
||||
{
|
||||
if (!m_grid_sizer->IsEmpty())
|
||||
return;
|
||||
// name of the first column is empty
|
||||
m_grid_sizer->Add(new wxStaticText(m_parent, wxID_ANY, wxEmptyString));
|
||||
// Legend for another columns
|
||||
for (const std::string col : { L("Plain pattern"), L("Format"), L("Params") }) {
|
||||
auto temp = new wxStaticText(m_parent, wxID_ANY, _(col), wxDefaultPosition, wxDefaultSize, wxST_ELLIPSIZE_MIDDLE);
|
||||
// temp->SetBackgroundStyle(wxBG_STYLE_PAINT);
|
||||
m_grid_sizer->Add(temp);
|
||||
}
|
||||
}
|
||||
|
||||
// delete substitution_id from substitutions
|
||||
void SubstitutionManager::delete_substitution(int substitution_id)
|
||||
{
|
||||
// delete substitution
|
||||
std::vector<std::string>& substitutions = m_config->option<ConfigOptionStrings>("gcode_substitutions")->values;
|
||||
if ((substitutions.size() % 3) != 0)
|
||||
throw RuntimeError("Invalid length of gcode_substitutions parameter");
|
||||
|
||||
if ((substitutions.size() / 3) < substitution_id)
|
||||
throw RuntimeError("Invalid substitution_id to delete");
|
||||
|
||||
substitutions.erase(std::next(substitutions.begin(), substitution_id * 3), std::next(substitutions.begin(), substitution_id * 3 + 3));
|
||||
call_ui_update();
|
||||
|
||||
// update grid_sizer
|
||||
update_from_config();
|
||||
}
|
||||
|
||||
// Add substitution line
|
||||
void SubstitutionManager::add_substitution(int substitution_id, const std::string& plain_pattern, const std::string& format, const std::string& params)
|
||||
{
|
||||
bool call_after_layout = false;
|
||||
|
||||
if (substitution_id < 0) {
|
||||
if (m_grid_sizer->IsEmpty()) {
|
||||
create_legend();
|
||||
substitution_id = 0;
|
||||
}
|
||||
substitution_id = m_grid_sizer->GetEffectiveRowsCount() - 1;
|
||||
|
||||
// create new substitution
|
||||
// it have to be added toconfig too
|
||||
std::vector<std::string>& substitutions = m_config->option<ConfigOptionStrings>("gcode_substitutions")->values;
|
||||
for (size_t i = 0; i < 3; i ++)
|
||||
substitutions.push_back(std::string());
|
||||
|
||||
call_after_layout = true;
|
||||
}
|
||||
|
||||
auto del_btn = new ScalableButton(m_parent, wxID_ANY, "cross");
|
||||
del_btn->Bind(wxEVT_BUTTON, [substitution_id, this](wxEvent&) {
|
||||
delete_substitution(substitution_id);
|
||||
});
|
||||
|
||||
m_grid_sizer->Add(del_btn, 0, wxRIGHT | wxLEFT, m_em);
|
||||
|
||||
auto add_text_editor = [substitution_id, this](const wxString& value, int opt_pos) {
|
||||
auto editor = new wxTextCtrl(m_parent, wxID_ANY, value, wxDefaultPosition, wxSize(15 * m_em, wxDefaultCoord), wxTE_PROCESS_ENTER
|
||||
#ifdef _WIN32
|
||||
| wxBORDER_SIMPLE
|
||||
#endif
|
||||
);
|
||||
|
||||
editor->SetFont(wxGetApp().normal_font());
|
||||
wxGetApp().UpdateDarkUI(editor);
|
||||
m_grid_sizer->Add(editor, 0, wxALIGN_CENTER_VERTICAL);
|
||||
|
||||
editor->Bind(wxEVT_TEXT_ENTER, [this, editor, substitution_id, opt_pos](wxEvent& e) {
|
||||
#if !defined(__WXGTK__)
|
||||
e.Skip();
|
||||
#endif // __WXGTK__
|
||||
edit_substitution(substitution_id, opt_pos, into_u8(editor->GetValue()));
|
||||
});
|
||||
|
||||
editor->Bind(wxEVT_KILL_FOCUS, [this, editor, substitution_id, opt_pos](wxEvent& e) {
|
||||
e.Skip();
|
||||
edit_substitution(substitution_id, opt_pos, into_u8(editor->GetValue()));
|
||||
});
|
||||
};
|
||||
|
||||
add_text_editor(from_u8(plain_pattern), 0);
|
||||
add_text_editor(from_u8(format), 1);
|
||||
|
||||
auto params_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
bool regexp = strchr(params.c_str(), 'r') != nullptr || strchr(params.c_str(), 'R') != nullptr;
|
||||
bool case_insensitive = strchr(params.c_str(), 'i') != nullptr || strchr(params.c_str(), 'I') != nullptr;
|
||||
bool whole_word = strchr(params.c_str(), 'w') != nullptr || strchr(params.c_str(), 'W') != nullptr;
|
||||
|
||||
auto chb_regexp = new wxCheckBox(m_parent, wxID_ANY, wxString("Regular expression"));
|
||||
chb_regexp->SetValue(regexp);
|
||||
params_sizer->Add(chb_regexp, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, m_em);
|
||||
|
||||
auto chb_case_insensitive = new wxCheckBox(m_parent, wxID_ANY, wxString("Case insensitive"));
|
||||
chb_case_insensitive->SetValue(case_insensitive);
|
||||
params_sizer->Add(chb_case_insensitive, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT | wxLEFT, m_em);
|
||||
|
||||
auto chb_whole_word = new wxCheckBox(m_parent, wxID_ANY, wxString("Whole word"));
|
||||
chb_whole_word->SetValue(whole_word);
|
||||
params_sizer->Add(chb_whole_word, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT | wxLEFT, m_em);
|
||||
|
||||
for (wxCheckBox* chb : std::initializer_list<wxCheckBox*>{ chb_regexp, chb_case_insensitive, chb_whole_word }) {
|
||||
chb->SetFont(wxGetApp().normal_font());
|
||||
chb->Bind(wxEVT_CHECKBOX, [this, substitution_id, chb_regexp, chb_case_insensitive, chb_whole_word](wxCommandEvent e) {
|
||||
std::string value = std::string();
|
||||
if (chb_regexp->GetValue())
|
||||
value += "r";
|
||||
if (chb_case_insensitive->GetValue())
|
||||
value += "i";
|
||||
if (chb_whole_word->GetValue())
|
||||
value += "w";
|
||||
edit_substitution(substitution_id, 2, value);
|
||||
});
|
||||
}
|
||||
|
||||
m_grid_sizer->Add(params_sizer);
|
||||
|
||||
if (call_after_layout) {
|
||||
m_parent->GetParent()->Layout();
|
||||
call_ui_update();
|
||||
}
|
||||
}
|
||||
|
||||
void SubstitutionManager::update_from_config()
|
||||
{
|
||||
if (!m_grid_sizer->IsEmpty())
|
||||
m_grid_sizer->Clear(true);
|
||||
|
||||
std::vector<std::string>& subst = m_config->option<ConfigOptionStrings>("gcode_substitutions")->values;
|
||||
if (!subst.empty())
|
||||
create_legend();
|
||||
|
||||
if ((subst.size() % 3) != 0)
|
||||
throw RuntimeError("Invalid length of gcode_substitutions parameter");
|
||||
|
||||
int subst_id = 0;
|
||||
for (size_t i = 0; i < subst.size(); i += 3)
|
||||
add_substitution(subst_id++, subst[i], subst[i + 1], subst[i + 2]);
|
||||
|
||||
m_parent->GetParent()->Layout();
|
||||
}
|
||||
|
||||
void SubstitutionManager::delete_all()
|
||||
{
|
||||
m_config->option<ConfigOptionStrings>("gcode_substitutions")->values.clear();
|
||||
call_ui_update();
|
||||
|
||||
if (!m_grid_sizer->IsEmpty())
|
||||
m_grid_sizer->Clear(true);
|
||||
|
||||
m_parent->GetParent()->Layout();
|
||||
}
|
||||
|
||||
void SubstitutionManager::edit_substitution(int substitution_id, int opt_pos, const std::string& value)
|
||||
{
|
||||
std::vector<std::string>& substitutions = m_config->option<ConfigOptionStrings>("gcode_substitutions")->values;
|
||||
|
||||
if ((substitutions.size() % 3) != 0)
|
||||
throw RuntimeError("Invalid length of gcode_substitutions parameter");
|
||||
|
||||
if ((substitutions.size() / 3) != m_grid_sizer->GetEffectiveRowsCount()-1)
|
||||
throw RuntimeError("Invalid compatibility between UI and BE");
|
||||
|
||||
if ((substitutions.size() / 3) < substitution_id)
|
||||
throw RuntimeError("Invalid substitution_id to edit");
|
||||
|
||||
substitutions[substitution_id * 3 + opt_pos] = value;
|
||||
|
||||
call_ui_update();
|
||||
}
|
||||
|
||||
bool SubstitutionManager::is_empty_substitutions()
|
||||
{
|
||||
return m_config->option<ConfigOptionStrings>("gcode_substitutions")->values.empty();
|
||||
}
|
||||
|
||||
// Return a callback to create a TabPrint widget to edit G-code substitutions
|
||||
wxSizer* TabPrint::create_manage_substitution_widget(wxWindow* parent)
|
||||
{
|
||||
auto create_btn = [parent](ScalableButton** btn, const wxString& label, const std::string& icon_name) {
|
||||
*btn = new ScalableButton(parent, wxID_ANY, icon_name, " " + label + " ", wxDefaultSize, wxDefaultPosition, wxBU_LEFT | wxBU_EXACTFIT, true);
|
||||
(*btn)->SetFont(wxGetApp().normal_font());
|
||||
(*btn)->SetSize((*btn)->GetBestSize());
|
||||
};
|
||||
|
||||
ScalableButton* add_substitution_btn;
|
||||
create_btn(&add_substitution_btn, _L("Add G-code substitution"), "add_copies");
|
||||
add_substitution_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent e) {
|
||||
m_subst_manager.add_substitution();
|
||||
m_del_all_substitutions_btn->Show();
|
||||
});
|
||||
|
||||
create_btn(&m_del_all_substitutions_btn, _L("Delete all G-code substitution"), "cross");
|
||||
m_del_all_substitutions_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent e) {
|
||||
m_subst_manager.delete_all();
|
||||
m_del_all_substitutions_btn->Hide();
|
||||
});
|
||||
|
||||
auto sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
sizer->Add(add_substitution_btn, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT | wxLEFT, em_unit(parent));
|
||||
sizer->Add(m_del_all_substitutions_btn, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT | wxLEFT, em_unit(parent));
|
||||
|
||||
parent->GetParent()->Layout();
|
||||
return sizer;
|
||||
}
|
||||
|
||||
// Return a callback to create a TabPrint widget to edit G-code substitutions
|
||||
wxSizer* TabPrint::create_substitutions_widget(wxWindow* parent)
|
||||
{
|
||||
wxFlexGridSizer* grid_sizer = new wxFlexGridSizer(4, 5, wxGetApp().em_unit()); // delete_button, "Old val", "New val", "Params"
|
||||
grid_sizer->SetFlexibleDirection(wxHORIZONTAL);
|
||||
|
||||
m_subst_manager.init(m_config, parent, grid_sizer);
|
||||
m_subst_manager.set_cb_edited_substitution([this]() {
|
||||
update_dirty();
|
||||
wxGetApp().mainframe->on_config_changed(m_config); // invalidate print
|
||||
});
|
||||
|
||||
auto sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
sizer->Add(grid_sizer, 0, wxALIGN_CENTER_VERTICAL);
|
||||
|
||||
parent->GetParent()->Layout();
|
||||
return sizer;
|
||||
}
|
||||
|
||||
// Return a callback to create a TabPrinter widget to edit bed shape
|
||||
wxSizer* TabPrinter::create_bed_shape_widget(wxWindow* parent)
|
||||
{
|
||||
|
@ -43,6 +43,44 @@ namespace GUI {
|
||||
class TabPresetComboBox;
|
||||
class OG_CustomCtrl;
|
||||
|
||||
// G-code substitutions
|
||||
|
||||
// Substitution Manager - helper for manipuation of the substitutions
|
||||
class SubstitutionManager
|
||||
{
|
||||
DynamicPrintConfig* m_config{ nullptr };
|
||||
wxWindow* m_parent{ nullptr };
|
||||
wxFlexGridSizer* m_grid_sizer{ nullptr };
|
||||
|
||||
int m_em{10};
|
||||
std::function<void()> m_cb_edited_substitution{ nullptr };
|
||||
|
||||
public:
|
||||
SubstitutionManager() {};
|
||||
~SubstitutionManager() {};
|
||||
|
||||
void init(DynamicPrintConfig* config, wxWindow* parent, wxFlexGridSizer* grid_sizer);
|
||||
void create_legend();
|
||||
void delete_substitution(int substitution_id);
|
||||
void add_substitution( int substitution_id = -1,
|
||||
const std::string& plain_pattern = std::string(),
|
||||
const std::string& format = std::string(),
|
||||
const std::string& params = std::string());
|
||||
void update_from_config();
|
||||
void delete_all();
|
||||
void edit_substitution(int substitution_id,
|
||||
int opt_pos, // option position insubstitution [0, 2]
|
||||
const std::string& value);
|
||||
void set_cb_edited_substitution(std::function<void()> cb_edited_substitution) {
|
||||
m_cb_edited_substitution = cb_edited_substitution;
|
||||
}
|
||||
void call_ui_update() {
|
||||
if (m_cb_edited_substitution)
|
||||
m_cb_edited_substitution();
|
||||
}
|
||||
bool is_empty_substitutions();
|
||||
};
|
||||
|
||||
// Single Tab page containing a{ vsizer } of{ optgroups }
|
||||
// package Slic3r::GUI::Tab::Page;
|
||||
using ConfigOptionsGroupShp = std::shared_ptr<ConfigOptionsGroup>;
|
||||
@ -368,11 +406,15 @@ public:
|
||||
void update() override;
|
||||
void clear_pages() override;
|
||||
bool supports_printer_technology(const PrinterTechnology tech) const override { return tech == ptFFF; }
|
||||
wxSizer* create_manage_substitution_widget(wxWindow* parent);
|
||||
wxSizer* create_substitutions_widget(wxWindow* parent);
|
||||
|
||||
private:
|
||||
ogStaticText* m_recommended_thin_wall_thickness_description_line = nullptr;
|
||||
ogStaticText* m_top_bottom_shell_thickness_explanation = nullptr;
|
||||
ogStaticText* m_post_process_explanation = nullptr;
|
||||
ScalableButton* m_del_all_substitutions_btn{nullptr};
|
||||
SubstitutionManager m_subst_manager;
|
||||
};
|
||||
|
||||
class TabFilament : public Tab
|
||||
|
@ -1156,6 +1156,14 @@ static wxString get_string_value(std::string opt_key, const DynamicPrintConfig&
|
||||
out.RemoveLast(1);
|
||||
return out;
|
||||
}
|
||||
if (opt_key == "gcode_substitutions") {
|
||||
if (!strings->empty())
|
||||
for (size_t id = 0; id < strings->size(); id += 3)
|
||||
out += from_u8(strings->get_at(id)) + ";\t" +
|
||||
from_u8(strings->get_at(id + 1)) + ";\t" +
|
||||
from_u8(strings->get_at(id + 2)) + ";\n";
|
||||
return out;
|
||||
}
|
||||
if (!strings->empty() && opt_idx < strings->values.size())
|
||||
return from_u8(strings->get_at(opt_idx));
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ add_executable(${_TEST_NAME}_tests
|
||||
test_fill.cpp
|
||||
test_flow.cpp
|
||||
test_gcode.cpp
|
||||
test_gcodefindreplace.cpp
|
||||
test_gcodewriter.cpp
|
||||
test_model.cpp
|
||||
test_print.cpp
|
||||
|
218
tests/fff_print/test_gcodefindreplace.cpp
Normal file
218
tests/fff_print/test_gcodefindreplace.cpp
Normal file
@ -0,0 +1,218 @@
|
||||
#include <catch2/catch.hpp>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "libslic3r/GCode/FindReplace.hpp"
|
||||
|
||||
using namespace Slic3r;
|
||||
|
||||
SCENARIO("Find/Replace with plain text", "[GCodeFindReplace]") {
|
||||
GIVEN("G-code") {
|
||||
const std::string gcode =
|
||||
"G1 Z0; home\n"
|
||||
"G1 Z1; move up\n"
|
||||
"G1 X0 Y1 Z1; perimeter\n"
|
||||
"G1 X13 Y32 Z1; infill\n"
|
||||
"G1 X13 Y32 Z1; wipe\n";
|
||||
WHEN("Replace \"move up\" with \"move down\", case sensitive") {
|
||||
GCodeFindReplace find_replace({ "move up", "move down", "" });
|
||||
REQUIRE(find_replace.process_layer(gcode) ==
|
||||
"G1 Z0; home\n"
|
||||
// substituted
|
||||
"G1 Z1; move down\n"
|
||||
"G1 X0 Y1 Z1; perimeter\n"
|
||||
"G1 X13 Y32 Z1; infill\n"
|
||||
"G1 X13 Y32 Z1; wipe\n");
|
||||
}
|
||||
WHEN("Replace \"move up\" with \"move down\", case insensitive") {
|
||||
GCodeFindReplace find_replace({ "move up", "move down", "i" });
|
||||
REQUIRE(find_replace.process_layer(gcode) ==
|
||||
"G1 Z0; home\n"
|
||||
// substituted
|
||||
"G1 Z1; move down\n"
|
||||
"G1 X0 Y1 Z1; perimeter\n"
|
||||
"G1 X13 Y32 Z1; infill\n"
|
||||
"G1 X13 Y32 Z1; wipe\n");
|
||||
}
|
||||
WHEN("Replace \"move UP\" with \"move down\", case insensitive") {
|
||||
GCodeFindReplace find_replace({ "move UP", "move down", "i" });
|
||||
REQUIRE(find_replace.process_layer(gcode) ==
|
||||
"G1 Z0; home\n"
|
||||
// substituted
|
||||
"G1 Z1; move down\n"
|
||||
"G1 X0 Y1 Z1; perimeter\n"
|
||||
"G1 X13 Y32 Z1; infill\n"
|
||||
"G1 X13 Y32 Z1; wipe\n");
|
||||
}
|
||||
WHEN("Replace \"move up\" with \"move down\", case sensitive") {
|
||||
GCodeFindReplace find_replace({ "move UP", "move down", "" });
|
||||
REQUIRE(find_replace.process_layer(gcode) == gcode);
|
||||
}
|
||||
|
||||
// Whole word
|
||||
WHEN("Replace \"move up\" with \"move down\", whole word") {
|
||||
GCodeFindReplace find_replace({ "move up", "move down", "w" });
|
||||
REQUIRE(find_replace.process_layer(gcode) ==
|
||||
"G1 Z0; home\n"
|
||||
// substituted
|
||||
"G1 Z1; move down\n"
|
||||
"G1 X0 Y1 Z1; perimeter\n"
|
||||
"G1 X13 Y32 Z1; infill\n"
|
||||
"G1 X13 Y32 Z1; wipe\n");
|
||||
}
|
||||
WHEN("Replace \"move u\" with \"move down\", whole word") {
|
||||
GCodeFindReplace find_replace({ "move u", "move down", "w" });
|
||||
REQUIRE(find_replace.process_layer(gcode) == gcode);
|
||||
}
|
||||
WHEN("Replace \"ove up\" with \"move down\", whole word") {
|
||||
GCodeFindReplace find_replace({ "move u", "move down", "w" });
|
||||
REQUIRE(find_replace.process_layer(gcode) == gcode);
|
||||
}
|
||||
|
||||
// Multi-line replace
|
||||
WHEN("Replace \"move up\\nG1 X0 \" with \"move down\\nG0 X1 \"") {
|
||||
GCodeFindReplace find_replace({ "move up\\nG1 X0 ", "move down\\nG0 X1 ", "" });
|
||||
REQUIRE(find_replace.process_layer(gcode) ==
|
||||
"G1 Z0; home\n"
|
||||
// substituted
|
||||
"G1 Z1; move down\n"
|
||||
"G0 X1 Y1 Z1; perimeter\n"
|
||||
"G1 X13 Y32 Z1; infill\n"
|
||||
"G1 X13 Y32 Z1; wipe\n");
|
||||
}
|
||||
// Multi-line replace, whole word.
|
||||
WHEN("Replace \"move up\\nG1 X0\" with \"move down\\nG0 X1\", whole word") {
|
||||
GCodeFindReplace find_replace({ "move up\\nG1 X0", "move down\\nG0 X1", "w" });
|
||||
REQUIRE(find_replace.process_layer(gcode) ==
|
||||
"G1 Z0; home\n"
|
||||
// substituted
|
||||
"G1 Z1; move down\n"
|
||||
"G0 X1 Y1 Z1; perimeter\n"
|
||||
"G1 X13 Y32 Z1; infill\n"
|
||||
"G1 X13 Y32 Z1; wipe\n");
|
||||
}
|
||||
// Multi-line replace, whole word, fails.
|
||||
WHEN("Replace \"move up\\nG1 X\" with \"move down\\nG0 X\", whole word") {
|
||||
GCodeFindReplace find_replace({ "move up\\nG1 X", "move down\\nG0 X", "w" });
|
||||
REQUIRE(find_replace.process_layer(gcode) == gcode);
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("G-code with decimals") {
|
||||
const std::string gcode =
|
||||
"G1 Z0.123; home\n"
|
||||
"G1 Z1.21; move up\n"
|
||||
"G1 X0 Y.33 Z.431 E1.2; perimeter\n";
|
||||
WHEN("Regular expression NOT processed in non-regex mode") {
|
||||
GCodeFindReplace find_replace({ "( [XYZEF]-?)\\.([0-9]+)", "\\10.\\2", "" });
|
||||
REQUIRE(find_replace.process_layer(gcode) == gcode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SCENARIO("Find/Replace with regexp", "[GCodeFindReplace]") {
|
||||
GIVEN("G-code") {
|
||||
const std::string gcode =
|
||||
"G1 Z0; home\n"
|
||||
"G1 Z1; move up\n"
|
||||
"G1 X0 Y1 Z1; perimeter\n"
|
||||
"G1 X13 Y32 Z1; infill\n"
|
||||
"G1 X13 Y32 Z1; wipe\n";
|
||||
WHEN("Replace \"move up\" with \"move down\", case sensitive") {
|
||||
GCodeFindReplace find_replace({ "move up", "move down", "r" });
|
||||
REQUIRE(find_replace.process_layer(gcode) ==
|
||||
"G1 Z0; home\n"
|
||||
// substituted
|
||||
"G1 Z1; move down\n"
|
||||
"G1 X0 Y1 Z1; perimeter\n"
|
||||
"G1 X13 Y32 Z1; infill\n"
|
||||
"G1 X13 Y32 Z1; wipe\n");
|
||||
}
|
||||
WHEN("Replace \"move up\" with \"move down\", case insensitive") {
|
||||
GCodeFindReplace find_replace({ "move up", "move down", "ri" });
|
||||
REQUIRE(find_replace.process_layer(gcode) ==
|
||||
"G1 Z0; home\n"
|
||||
// substituted
|
||||
"G1 Z1; move down\n"
|
||||
"G1 X0 Y1 Z1; perimeter\n"
|
||||
"G1 X13 Y32 Z1; infill\n"
|
||||
"G1 X13 Y32 Z1; wipe\n");
|
||||
}
|
||||
WHEN("Replace \"move UP\" with \"move down\", case insensitive") {
|
||||
GCodeFindReplace find_replace({ "move UP", "move down", "ri" });
|
||||
REQUIRE(find_replace.process_layer(gcode) ==
|
||||
"G1 Z0; home\n"
|
||||
// substituted
|
||||
"G1 Z1; move down\n"
|
||||
"G1 X0 Y1 Z1; perimeter\n"
|
||||
"G1 X13 Y32 Z1; infill\n"
|
||||
"G1 X13 Y32 Z1; wipe\n");
|
||||
}
|
||||
WHEN("Replace \"move up\" with \"move down\", case sensitive") {
|
||||
GCodeFindReplace find_replace({ "move UP", "move down", "r" });
|
||||
REQUIRE(find_replace.process_layer(gcode) == gcode);
|
||||
}
|
||||
|
||||
// Whole word
|
||||
WHEN("Replace \"move up\" with \"move down\", whole word") {
|
||||
GCodeFindReplace find_replace({ "move up", "move down", "rw" });
|
||||
REQUIRE(find_replace.process_layer(gcode) ==
|
||||
"G1 Z0; home\n"
|
||||
// substituted
|
||||
"G1 Z1; move down\n"
|
||||
"G1 X0 Y1 Z1; perimeter\n"
|
||||
"G1 X13 Y32 Z1; infill\n"
|
||||
"G1 X13 Y32 Z1; wipe\n");
|
||||
}
|
||||
WHEN("Replace \"move u\" with \"move down\", whole word") {
|
||||
GCodeFindReplace find_replace({ "move u", "move down", "rw" });
|
||||
REQUIRE(find_replace.process_layer(gcode) == gcode);
|
||||
}
|
||||
WHEN("Replace \"ove up\" with \"move down\", whole word") {
|
||||
GCodeFindReplace find_replace({ "move u", "move down", "rw" });
|
||||
REQUIRE(find_replace.process_layer(gcode) == gcode);
|
||||
}
|
||||
|
||||
// Multi-line replace
|
||||
WHEN("Replace \"move up\\nG1 X0 \" with \"move down\\nG0 X1 \"") {
|
||||
GCodeFindReplace find_replace({ "move up\\nG1 X0 ", "move down\\nG0 X1 ", "r" });
|
||||
REQUIRE(find_replace.process_layer(gcode) ==
|
||||
"G1 Z0; home\n"
|
||||
// substituted
|
||||
"G1 Z1; move down\n"
|
||||
"G0 X1 Y1 Z1; perimeter\n"
|
||||
"G1 X13 Y32 Z1; infill\n"
|
||||
"G1 X13 Y32 Z1; wipe\n");
|
||||
}
|
||||
// Multi-line replace, whole word.
|
||||
WHEN("Replace \"move up\\nG1 X0\" with \"move down\\nG0 X1\", whole word") {
|
||||
GCodeFindReplace find_replace({ "move up\\nG1 X0", "move down\\nG0 X1", "rw" });
|
||||
REQUIRE(find_replace.process_layer(gcode) ==
|
||||
"G1 Z0; home\n"
|
||||
// substituted
|
||||
"G1 Z1; move down\n"
|
||||
"G0 X1 Y1 Z1; perimeter\n"
|
||||
"G1 X13 Y32 Z1; infill\n"
|
||||
"G1 X13 Y32 Z1; wipe\n");
|
||||
}
|
||||
// Multi-line replace, whole word, fails.
|
||||
WHEN("Replace \"move up\\nG1 X\" with \"move down\\nG0 X\", whole word") {
|
||||
GCodeFindReplace find_replace({ "move up\\nG1 X", "move down\\nG0 X", "rw" });
|
||||
REQUIRE(find_replace.process_layer(gcode) == gcode);
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("G-code with decimals") {
|
||||
const std::string gcode =
|
||||
"G1 Z0.123; home\n"
|
||||
"G1 Z1.21; move up\n"
|
||||
"G1 X0 Y.33 Z.431 E1.2; perimeter\n";
|
||||
WHEN("Missing zeros before dot filled in") {
|
||||
GCodeFindReplace find_replace({ "( [XYZEF]-?)\\.([0-9]+)", "\\10.\\2", "r" });
|
||||
REQUIRE(find_replace.process_layer(gcode) ==
|
||||
"G1 Z0.123; home\n"
|
||||
"G1 Z1.21; move up\n"
|
||||
"G1 X0 Y0.33 Z0.431 E1.2; perimeter\n");
|
||||
}
|
||||
}
|
||||
}
|
@ -95,26 +95,28 @@ TEST_CASE("cancel_all should remove all pending jobs", "[Jobs]") {
|
||||
auto pri = std::make_shared<Progress>();
|
||||
BoostThreadWorker worker{pri};
|
||||
|
||||
std::array<bool, 4> jobres = {false};
|
||||
std::array<bool, 4> jobres = {false, false, false, false};
|
||||
|
||||
queue_job(worker, [&jobres](Job::Ctl &) {
|
||||
jobres[0] = true;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
// FIXME: the long wait time is needed to prevent fail in MSVC
|
||||
// where the sleep_for function is behaving stupidly.
|
||||
// see https://developercommunity.visualstudio.com/t/bogus-stdthis-threadsleep-for-implementation/58530
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
});
|
||||
queue_job(worker, [&jobres](Job::Ctl &) {
|
||||
jobres[1] = true;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
});
|
||||
queue_job(worker, [&jobres](Job::Ctl &) {
|
||||
jobres[2] = true;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
});
|
||||
queue_job(worker, [&jobres](Job::Ctl &) {
|
||||
jobres[3] = true;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
});
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(500));
|
||||
// wait until the first job's half time to be sure, the cancel is made
|
||||
// during the first job's execution.
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
||||
worker.cancel_all();
|
||||
|
||||
REQUIRE(jobres[0] == true);
|
||||
|
Loading…
Reference in New Issue
Block a user