Fixed conflicts after merge with master
This commit is contained in:
commit
b19c742455
17
resources/icons/exclamation_manifold.svg
Normal file
17
resources/icons/exclamation_manifold.svg
Normal file
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 23.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path fill="#808080" d="M8,11c-0.55,0-1-0.45-1-1V7c0-0.55,0.45-1,1-1s1,0.45,1,1v3C9,10.55,8.55,11,8,11z"/>
|
||||
</g>
|
||||
<g>
|
||||
<circle fill="#808080" cx="8" cy="13" r="1"/>
|
||||
</g>
|
||||
<g>
|
||||
<path fill="#808080" d="M15,15.5H1c-0.18,0-0.34-0.09-0.43-0.24c-0.09-0.15-0.09-0.34-0.01-0.49l7-13c0.17-0.32,0.71-0.32,0.88,0
|
||||
l7,13c0.08,0.16,0.08,0.34-0.01,0.49C15.34,15.41,15.18,15.5,15,15.5z M1.84,14.5h12.33L8,3.05L1.84,14.5z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 781 B |
17
resources/icons/white/exclamation_manifold.svg
Normal file
17
resources/icons/white/exclamation_manifold.svg
Normal file
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 23.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path fill="#FFFFFF" d="M8,11c-0.55,0-1-0.45-1-1V7c0-0.55,0.45-1,1-1s1,0.45,1,1v3C9,10.55,8.55,11,8,11z"/>
|
||||
</g>
|
||||
<g>
|
||||
<circle fill="#FFFFFF" cx="8" cy="13" r="1"/>
|
||||
</g>
|
||||
<g>
|
||||
<path fill="#FFFFFF" d="M15,15.5H1c-0.18,0-0.34-0.09-0.43-0.24c-0.09-0.15-0.09-0.34-0.01-0.49l7-13c0.17-0.32,0.71-0.32,0.88,0
|
||||
l7,13c0.08,0.16,0.08,0.34-0.01,0.49C15.34,15.41,15.18,15.5,15,15.5z M1.84,14.5h12.33L8,3.05L1.84,14.5z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 781 B |
@ -139,6 +139,9 @@ void AppConfig::set_defaults()
|
||||
if (get("default_action_on_select_preset").empty())
|
||||
set("default_action_on_select_preset", "none"); // , "transfer", "discard" or "save"
|
||||
|
||||
if (get("default_action_on_new_project").empty())
|
||||
set("default_action_on_new_project", "none"); // , "keep(transfer)", "discard" or "save"
|
||||
|
||||
if (get("color_mapinulation_panel").empty())
|
||||
set("color_mapinulation_panel", "0");
|
||||
|
||||
|
@ -12,7 +12,7 @@ namespace Slic3r {
|
||||
#ifdef WIN32
|
||||
|
||||
//only dll name with .dll suffix - currently case sensitive
|
||||
const std::vector<std::wstring> BlacklistedLibraryCheck::blacklist({ L"NahimicOSD.dll", L"SS2OSD.dll" });
|
||||
const std::vector<std::wstring> BlacklistedLibraryCheck::blacklist({ L"NahimicOSD.dll", L"SS2OSD.dll", L"amhook.dll", L"AMHook.dll" });
|
||||
|
||||
bool BlacklistedLibraryCheck::get_blacklisted(std::vector<std::wstring>& names)
|
||||
{
|
||||
|
@ -126,6 +126,45 @@ ExPolygons ClipperPaths_to_Slic3rExPolygons(const ClipperLib::Paths &input)
|
||||
return PolyTreeToExPolygons(std::move(polytree));
|
||||
}
|
||||
|
||||
#if 0
|
||||
// Global test.
|
||||
bool has_duplicate_points(const ClipperLib::PolyTree &polytree)
|
||||
{
|
||||
struct Helper {
|
||||
static void collect_points_recursive(const ClipperLib::PolyNode &polynode, ClipperLib::Path &out) {
|
||||
// For each hole of the current expolygon:
|
||||
out.insert(out.end(), polynode.Contour.begin(), polynode.Contour.end());
|
||||
for (int i = 0; i < polynode.ChildCount(); ++ i)
|
||||
collect_points_recursive(*polynode.Childs[i], out);
|
||||
}
|
||||
};
|
||||
ClipperLib::Path pts;
|
||||
for (int i = 0; i < polytree.ChildCount(); ++ i)
|
||||
Helper::collect_points_recursive(*polytree.Childs[i], pts);
|
||||
return has_duplicate_points(std::move(pts));
|
||||
}
|
||||
#else
|
||||
// Local test inside each of the contours.
|
||||
bool has_duplicate_points(const ClipperLib::PolyTree &polytree)
|
||||
{
|
||||
struct Helper {
|
||||
static bool has_duplicate_points_recursive(const ClipperLib::PolyNode &polynode) {
|
||||
if (has_duplicate_points(polynode.Contour))
|
||||
return true;
|
||||
for (int i = 0; i < polynode.ChildCount(); ++ i)
|
||||
if (has_duplicate_points_recursive(*polynode.Childs[i]))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
ClipperLib::Path pts;
|
||||
for (int i = 0; i < polytree.ChildCount(); ++ i)
|
||||
if (Helper::has_duplicate_points_recursive(*polytree.Childs[i]))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Offset outside by 10um, one by one.
|
||||
template<typename PathsProvider>
|
||||
static ClipperLib::Paths safety_offset(PathsProvider &&paths)
|
||||
|
@ -740,7 +740,11 @@ ConfigSubstitutions ConfigBase::load(const boost::property_tree::ptree &tree, Fo
|
||||
}
|
||||
|
||||
// Load the config keys from the given string.
|
||||
static inline size_t load_from_gcode_string_legacy(ConfigBase &config, const char *str, ConfigSubstitutionContext &substitutions)
|
||||
#if ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
|
||||
size_t ConfigBase::load_from_gcode_string_legacy(ConfigBase& config, const char* str, ConfigSubstitutionContext& substitutions)
|
||||
#else
|
||||
static inline size_t load_from_gcode_string_legacy(ConfigBase& config, const char* str, ConfigSubstitutionContext& substitutions)
|
||||
#endif // ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
|
||||
{
|
||||
if (str == nullptr)
|
||||
return 0;
|
||||
|
@ -2015,6 +2015,10 @@ public:
|
||||
// Set all the nullable values to nils.
|
||||
void null_nullables();
|
||||
|
||||
#if ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
|
||||
static size_t load_from_gcode_string_legacy(ConfigBase& config, const char* str, ConfigSubstitutionContext& substitutions);
|
||||
#endif // ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
|
||||
|
||||
private:
|
||||
// Set a configuration value from a string.
|
||||
bool set_deserialize_raw(const t_config_option_key& opt_key_src, const std::string& value, ConfigSubstitutionContext& substitutions, bool append);
|
||||
|
@ -114,10 +114,11 @@ bool ExPolygon::contains(const Polylines &polylines) const
|
||||
|
||||
bool ExPolygon::contains(const Point &point) const
|
||||
{
|
||||
if (!this->contour.contains(point)) return false;
|
||||
for (Polygons::const_iterator it = this->holes.begin(); it != this->holes.end(); ++it) {
|
||||
if (it->contains(point)) return false;
|
||||
}
|
||||
if (! this->contour.contains(point))
|
||||
return false;
|
||||
for (const Polygon &hole : this->holes)
|
||||
if (hole.contains(point))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -367,6 +368,57 @@ extern std::vector<BoundingBox> get_extents_vector(const ExPolygons &polygons)
|
||||
return out;
|
||||
}
|
||||
|
||||
bool has_duplicate_points(const ExPolygon &expoly)
|
||||
{
|
||||
#if 1
|
||||
// Check globally.
|
||||
size_t cnt = expoly.contour.points.size();
|
||||
for (const Polygon &hole : expoly.holes)
|
||||
cnt += hole.points.size();
|
||||
std::vector<Point> allpts;
|
||||
allpts.reserve(cnt);
|
||||
allpts.insert(allpts.begin(), expoly.contour.points.begin(), expoly.contour.points.end());
|
||||
for (const Polygon &hole : expoly.holes)
|
||||
allpts.insert(allpts.end(), hole.points.begin(), hole.points.end());
|
||||
return has_duplicate_points(std::move(allpts));
|
||||
#else
|
||||
// Check per contour.
|
||||
if (has_duplicate_points(expoly.contour))
|
||||
return true;
|
||||
for (const Polygon &hole : expoly.holes)
|
||||
if (has_duplicate_points(hole))
|
||||
return true;
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool has_duplicate_points(const ExPolygons &expolys)
|
||||
{
|
||||
#if 1
|
||||
// Check globally.
|
||||
size_t cnt = 0;
|
||||
for (const ExPolygon &expoly : expolys) {
|
||||
cnt += expoly.contour.points.size();
|
||||
for (const Polygon &hole : expoly.holes)
|
||||
cnt += hole.points.size();
|
||||
}
|
||||
std::vector<Point> allpts;
|
||||
allpts.reserve(cnt);
|
||||
for (const ExPolygon &expoly : expolys) {
|
||||
allpts.insert(allpts.begin(), expoly.contour.points.begin(), expoly.contour.points.end());
|
||||
for (const Polygon &hole : expoly.holes)
|
||||
allpts.insert(allpts.end(), hole.points.begin(), hole.points.end());
|
||||
}
|
||||
return has_duplicate_points(std::move(allpts));
|
||||
#else
|
||||
// Check per contour.
|
||||
for (const ExPolygon &expoly : expolys)
|
||||
if (has_duplicate_points(expoly))
|
||||
return true;
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool remove_sticks(ExPolygon &poly)
|
||||
{
|
||||
return remove_sticks(poly.contour) || remove_sticks(poly.holes);
|
||||
|
@ -351,20 +351,24 @@ inline ExPolygons expolygons_simplify(const ExPolygons &expolys, double toleranc
|
||||
return out;
|
||||
}
|
||||
|
||||
extern BoundingBox get_extents(const ExPolygon &expolygon);
|
||||
extern BoundingBox get_extents(const ExPolygons &expolygons);
|
||||
extern BoundingBox get_extents_rotated(const ExPolygon &poly, double angle);
|
||||
extern BoundingBox get_extents_rotated(const ExPolygons &polygons, double angle);
|
||||
extern std::vector<BoundingBox> get_extents_vector(const ExPolygons &polygons);
|
||||
BoundingBox get_extents(const ExPolygon &expolygon);
|
||||
BoundingBox get_extents(const ExPolygons &expolygons);
|
||||
BoundingBox get_extents_rotated(const ExPolygon &poly, double angle);
|
||||
BoundingBox get_extents_rotated(const ExPolygons &polygons, double angle);
|
||||
std::vector<BoundingBox> get_extents_vector(const ExPolygons &polygons);
|
||||
|
||||
extern bool remove_sticks(ExPolygon &poly);
|
||||
extern void keep_largest_contour_only(ExPolygons &polygons);
|
||||
// Test for duplicate points. The points are copied, sorted and checked for duplicates globally.
|
||||
bool has_duplicate_points(const ExPolygon &expoly);
|
||||
bool has_duplicate_points(const ExPolygons &expolys);
|
||||
|
||||
bool remove_sticks(ExPolygon &poly);
|
||||
void keep_largest_contour_only(ExPolygons &polygons);
|
||||
|
||||
inline double area(const ExPolygon &poly) { return poly.area(); }
|
||||
inline double area(const ExPolygons &polys) { double s = 0.; for (auto &p : polys) s += p.area(); return s; }
|
||||
|
||||
// Removes all expolygons smaller than min_area and also removes all holes smaller than min_area
|
||||
extern bool remove_small_and_small_holes(ExPolygons &expolygons, double min_area);
|
||||
bool remove_small_and_small_holes(ExPolygons &expolygons, double min_area);
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
|
@ -1890,6 +1890,7 @@ namespace Slic3r {
|
||||
|
||||
unsigned int geo_tri_count = (unsigned int)geometry.triangles.size();
|
||||
unsigned int renamed_volumes_count = 0;
|
||||
int processed_vertices_max_id = 0;
|
||||
|
||||
for (const ObjectMetadata::VolumeMetadata& volume_data : volumes) {
|
||||
if (geo_tri_count <= volume_data.first_triangle_id || geo_tri_count <= volume_data.last_triangle_id || volume_data.last_triangle_id < volume_data.first_triangle_id) {
|
||||
@ -1911,13 +1912,31 @@ namespace Slic3r {
|
||||
// splits volume out of imported geometry
|
||||
std::vector<Vec3i> faces(geometry.triangles.begin() + volume_data.first_triangle_id, geometry.triangles.begin() + volume_data.last_triangle_id + 1);
|
||||
const size_t triangles_count = faces.size();
|
||||
for (Vec3i face : faces)
|
||||
for (unsigned int tri_id : face)
|
||||
if (tri_id < 0 || tri_id >= geometry.vertices.size()) {
|
||||
|
||||
int min_id = faces.front()[0];
|
||||
int max_id = faces.front()[0];
|
||||
for (const Vec3i& face : faces) {
|
||||
for (const int tri_id : face) {
|
||||
if (tri_id < 0 || tri_id >= int(geometry.vertices.size())) {
|
||||
add_error("Found invalid vertex id");
|
||||
return false;
|
||||
}
|
||||
TriangleMesh triangle_mesh(std::move(geometry.vertices), std::move(faces));
|
||||
min_id = std::min(min_id, tri_id);
|
||||
max_id = std::max(max_id, tri_id);
|
||||
}
|
||||
}
|
||||
|
||||
// rebase indices to the current vertices list
|
||||
for (Vec3i& face : faces) {
|
||||
for (int& tri_id : face) {
|
||||
tri_id -= min_id;
|
||||
}
|
||||
}
|
||||
|
||||
processed_vertices_max_id = 1 + std::max(processed_vertices_max_id, max_id);
|
||||
|
||||
std::vector<Vec3f> vertices(geometry.vertices.begin() + min_id, geometry.vertices.begin() + max_id + 1);
|
||||
TriangleMesh triangle_mesh(std::move(vertices), std::move(faces));
|
||||
|
||||
if (m_version == 0) {
|
||||
// if the 3mf was not produced by PrusaSlicer and there is only one instance,
|
||||
|
@ -598,7 +598,7 @@ void AMFParserContext::endElement(const char * /* name */)
|
||||
case NODE_TYPE_VERTEX:
|
||||
assert(m_object);
|
||||
// Parse the vertex data
|
||||
m_object_vertices.emplace_back(float(atof(m_value[0].c_str())), float(atof(m_value[1].c_str())), float(atof(m_value[1].c_str())));
|
||||
m_object_vertices.emplace_back(float(atof(m_value[0].c_str())), float(atof(m_value[1].c_str())), float(atof(m_value[2].c_str())));
|
||||
m_value[0].clear();
|
||||
m_value[1].clear();
|
||||
m_value[2].clear();
|
||||
|
@ -73,7 +73,7 @@ bool load_obj(const char *path, TriangleMesh *meshptr)
|
||||
break;
|
||||
} else {
|
||||
assert(cnt < 4);
|
||||
if (vertex.coordIdx < 0 || vertex.coordIdx >= its.vertices.size()) {
|
||||
if (vertex.coordIdx < 0 || vertex.coordIdx >= int(its.vertices.size())) {
|
||||
BOOST_LOG_TRIVIAL(error) << "load_obj: failed to parse " << path << ". The file contains invalid vertex index.";
|
||||
return false;
|
||||
}
|
||||
|
@ -203,7 +203,7 @@ RasterParams get_raster_params(const DynamicPrintConfig &cfg)
|
||||
|
||||
if (!opt_disp_cols || !opt_disp_rows || !opt_disp_w || !opt_disp_h ||
|
||||
!opt_mirror_x || !opt_mirror_y || !opt_orient)
|
||||
throw Slic3r::FileIOError("Invalid SL1 / SL1S file");
|
||||
throw MissingProfileError("Invalid SL1 / SL1S file");
|
||||
|
||||
RasterParams rstp;
|
||||
|
||||
@ -229,7 +229,7 @@ SliceParams get_slice_params(const DynamicPrintConfig &cfg)
|
||||
auto *opt_init_layerh = cfg.option<ConfigOptionFloat>("initial_layer_height");
|
||||
|
||||
if (!opt_layerh || !opt_init_layerh)
|
||||
throw Slic3r::FileIOError("Invalid SL1 / SL1S file");
|
||||
throw MissingProfileError("Invalid SL1 / SL1S file");
|
||||
|
||||
return SliceParams{opt_layerh->getFloat(), opt_init_layerh->getFloat()};
|
||||
}
|
||||
@ -293,24 +293,34 @@ ConfigSubstitutions import_sla_archive(const std::string &zipfname, DynamicPrint
|
||||
return out.load(arch.profile, ForwardCompatibilitySubstitutionRule::Enable);
|
||||
}
|
||||
|
||||
// If the profile is missing from the archive (older PS versions did not have
|
||||
// it), profile_out's initial value will be used as fallback. profile_out will be empty on
|
||||
// function return if the archive did not contain any profile.
|
||||
ConfigSubstitutions import_sla_archive(
|
||||
const std::string & zipfname,
|
||||
Vec2i windowsize,
|
||||
indexed_triangle_set & out,
|
||||
DynamicPrintConfig & profile,
|
||||
DynamicPrintConfig & profile_out,
|
||||
std::function<bool(int)> progr)
|
||||
{
|
||||
// Ensure minimum window size for marching squares
|
||||
windowsize.x() = std::max(2, windowsize.x());
|
||||
windowsize.y() = std::max(2, windowsize.y());
|
||||
|
||||
ArchiveData arch = extract_sla_archive(zipfname, "thumbnail");
|
||||
ConfigSubstitutions config_substitutions = profile.load(arch.profile, ForwardCompatibilitySubstitutionRule::Enable);
|
||||
std::string exclude_entries{"thumbnail"};
|
||||
ArchiveData arch = extract_sla_archive(zipfname, exclude_entries);
|
||||
DynamicPrintConfig profile_in, profile_use;
|
||||
ConfigSubstitutions config_substitutions = profile_in.load(arch.profile, ForwardCompatibilitySubstitutionRule::Enable);
|
||||
|
||||
RasterParams rstp = get_raster_params(profile);
|
||||
// If the archive contains an empty profile, use the one that was passed as output argument
|
||||
// then replace it with the readed profile to report that it was empty.
|
||||
profile_use = profile_in.empty() ? profile_out : profile_in;
|
||||
profile_out = profile_in;
|
||||
|
||||
RasterParams rstp = get_raster_params(profile_use);
|
||||
rstp.win = {windowsize.y(), windowsize.x()};
|
||||
|
||||
SliceParams slicp = get_slice_params(profile);
|
||||
SliceParams slicp = get_slice_params(profile_use);
|
||||
|
||||
std::vector<ExPolygons> slices =
|
||||
extract_slices_from_sla_archive(arch, rstp, progr);
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class SL1Archive: public SLAPrinter {
|
||||
class SL1Archive: public SLAArchive {
|
||||
SLAPrinterConfig m_cfg;
|
||||
|
||||
protected:
|
||||
@ -57,6 +57,8 @@ inline ConfigSubstitutions import_sla_archive(
|
||||
return import_sla_archive(zipfname, windowsize, out, profile, progr);
|
||||
}
|
||||
|
||||
class MissingProfileError : public RuntimeError { using RuntimeError::RuntimeError; };
|
||||
|
||||
} // namespace Slic3r::sla
|
||||
|
||||
#endif // ARCHIVETRAITS_HPP
|
||||
|
@ -784,7 +784,8 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessor::Result* re
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << "Start processing gcode, " << log_memory_info();
|
||||
m_processor.finalize();
|
||||
// Post-process the G-code to update time stamps.
|
||||
m_processor.finalize(true);
|
||||
// DoExport::update_print_estimated_times_stats(m_processor, print->m_print_statistics);
|
||||
DoExport::update_print_estimated_stats(m_processor, m_writer.extruders(), print->m_print_statistics);
|
||||
if (result != nullptr) {
|
||||
|
@ -2,6 +2,7 @@
|
||||
#include "libslic3r/Utils.hpp"
|
||||
#include "libslic3r/Print.hpp"
|
||||
#include "libslic3r/LocalesUtils.hpp"
|
||||
#include "libslic3r/format.hpp"
|
||||
#include "GCodeProcessor.hpp"
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
@ -746,6 +747,9 @@ const std::vector<std::pair<GCodeProcessor::EProducer, std::string>> GCodeProces
|
||||
{ EProducer::PrusaSlicer, "generated by PrusaSlicer" },
|
||||
{ EProducer::Slic3rPE, "generated by Slic3r Prusa Edition" },
|
||||
{ EProducer::Slic3r, "generated by Slic3r" },
|
||||
#if ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
|
||||
{ EProducer::SuperSlicer, "generated by SuperSlicer" },
|
||||
#endif // ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
|
||||
{ EProducer::Cura, "Cura_SteamEngine" },
|
||||
{ EProducer::Simplify3D, "G-Code generated by Simplify3D(R)" },
|
||||
{ EProducer::CraftWare, "CraftWare" },
|
||||
@ -1189,6 +1193,8 @@ static inline const char* remove_eols(const char *begin, const char *end) {
|
||||
return end;
|
||||
}
|
||||
|
||||
// Load a G-code into a stand-alone G-code viewer.
|
||||
// throws CanceledException through print->throw_if_canceled() (sent by the caller as callback).
|
||||
void GCodeProcessor::process_file(const std::string& filename, std::function<void()> cancel_callback)
|
||||
{
|
||||
CNumericLocalesSetter locales_setter;
|
||||
@ -1225,6 +1231,10 @@ void GCodeProcessor::process_file(const std::string& filename, std::function<voi
|
||||
}
|
||||
else if (m_producer == EProducer::Simplify3D)
|
||||
apply_config_simplify3d(filename);
|
||||
#if ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
|
||||
else if (m_producer == EProducer::SuperSlicer)
|
||||
apply_config_superslicer(filename);
|
||||
#endif // ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
|
||||
}
|
||||
|
||||
// process gcode
|
||||
@ -1243,7 +1253,8 @@ void GCodeProcessor::process_file(const std::string& filename, std::function<voi
|
||||
this->process_gcode_line(line, true);
|
||||
});
|
||||
|
||||
this->finalize();
|
||||
// Don't post-process the G-code to update time stamps.
|
||||
this->finalize(false);
|
||||
}
|
||||
|
||||
void GCodeProcessor::initialize(const std::string& filename)
|
||||
@ -1269,7 +1280,7 @@ void GCodeProcessor::process_buffer(const std::string &buffer)
|
||||
});
|
||||
}
|
||||
|
||||
void GCodeProcessor::finalize()
|
||||
void GCodeProcessor::finalize(bool post_process)
|
||||
{
|
||||
// update width/height of wipe moves
|
||||
for (MoveVertex& move : m_result.moves) {
|
||||
@ -1299,7 +1310,8 @@ void GCodeProcessor::finalize()
|
||||
m_width_compare.output();
|
||||
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
|
||||
m_time_processor.post_process(m_result.filename, m_result.moves, m_result.lines_ends);
|
||||
if (post_process)
|
||||
m_time_processor.post_process(m_result.filename, m_result.moves, m_result.lines_ends);
|
||||
#if ENABLE_GCODE_VIEWER_STATISTICS
|
||||
m_result.time = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - m_start_time).count();
|
||||
#endif // ENABLE_GCODE_VIEWER_STATISTICS
|
||||
@ -1356,6 +1368,41 @@ std::vector<std::pair<ExtrusionRole, float>> GCodeProcessor::get_roles_time(Prin
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
|
||||
ConfigSubstitutions load_from_superslicer_gcode_file(const std::string& filename, DynamicPrintConfig& config, ForwardCompatibilitySubstitutionRule compatibility_rule)
|
||||
{
|
||||
// for reference, see: ConfigBase::load_from_gcode_file()
|
||||
|
||||
boost::nowide::ifstream ifs(filename);
|
||||
|
||||
auto header_end_pos = ifs.tellg();
|
||||
ConfigSubstitutionContext substitutions_ctxt(compatibility_rule);
|
||||
size_t key_value_pairs = 0;
|
||||
|
||||
ifs.seekg(0, ifs.end);
|
||||
auto file_length = ifs.tellg();
|
||||
auto data_length = std::min<std::fstream::pos_type>(65535, file_length - header_end_pos);
|
||||
ifs.seekg(file_length - data_length, ifs.beg);
|
||||
std::vector<char> data(size_t(data_length) + 1, 0);
|
||||
ifs.read(data.data(), data_length);
|
||||
ifs.close();
|
||||
key_value_pairs = ConfigBase::load_from_gcode_string_legacy(config, data.data(), substitutions_ctxt);
|
||||
|
||||
if (key_value_pairs < 80)
|
||||
throw Slic3r::RuntimeError(format("Suspiciously low number of configuration values extracted from %1%: %2%", filename, key_value_pairs));
|
||||
|
||||
return std::move(substitutions_ctxt.substitutions);
|
||||
}
|
||||
|
||||
void GCodeProcessor::apply_config_superslicer(const std::string& filename)
|
||||
{
|
||||
DynamicPrintConfig config;
|
||||
config.apply(FullPrintConfig::defaults());
|
||||
load_from_superslicer_gcode_file(filename, config, ForwardCompatibilitySubstitutionRule::EnableSilent);
|
||||
apply_config(config);
|
||||
}
|
||||
#endif // ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
|
||||
|
||||
std::vector<float> GCodeProcessor::get_layers_time(PrintEstimatedStatistics::ETimeMode mode) const
|
||||
{
|
||||
return (mode < PrintEstimatedStatistics::ETimeMode::Count) ?
|
||||
@ -1845,6 +1892,9 @@ bool GCodeProcessor::process_producers_tags(const std::string_view comment)
|
||||
{
|
||||
case EProducer::Slic3rPE:
|
||||
case EProducer::Slic3r:
|
||||
#if ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
|
||||
case EProducer::SuperSlicer:
|
||||
#endif // ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
|
||||
case EProducer::PrusaSlicer: { return process_prusaslicer_tags(comment); }
|
||||
case EProducer::Cura: { return process_cura_tags(comment); }
|
||||
case EProducer::Simplify3D: { return process_simplify3d_tags(comment); }
|
||||
@ -2674,15 +2724,15 @@ void GCodeProcessor::process_G28(const GCodeReader::GCodeLine& line)
|
||||
std::string_view cmd = line.cmd();
|
||||
std::string new_line_raw = { cmd.data(), cmd.size() };
|
||||
bool found = false;
|
||||
if (line.has_x()) {
|
||||
if (line.has('X')) {
|
||||
new_line_raw += " X0";
|
||||
found = true;
|
||||
}
|
||||
if (line.has_y()) {
|
||||
if (line.has('Y')) {
|
||||
new_line_raw += " Y0";
|
||||
found = true;
|
||||
}
|
||||
if (line.has_z()) {
|
||||
if (line.has('Z')) {
|
||||
new_line_raw += " Z0";
|
||||
found = true;
|
||||
}
|
||||
@ -2818,16 +2868,16 @@ void GCodeProcessor::process_M132(const GCodeReader::GCodeLine& line)
|
||||
// see: https://github.com/makerbot/s3g/blob/master/doc/GCodeProtocol.md
|
||||
// Using this command to reset the axis origin to zero helps in fixing: https://github.com/prusa3d/PrusaSlicer/issues/3082
|
||||
|
||||
if (line.has_x())
|
||||
if (line.has('X'))
|
||||
m_origin[X] = 0.0f;
|
||||
|
||||
if (line.has_y())
|
||||
if (line.has('Y'))
|
||||
m_origin[Y] = 0.0f;
|
||||
|
||||
if (line.has_z())
|
||||
if (line.has('Z'))
|
||||
m_origin[Z] = 0.0f;
|
||||
|
||||
if (line.has_e())
|
||||
if (line.has('E'))
|
||||
m_origin[E] = 0.0f;
|
||||
}
|
||||
|
||||
@ -2988,7 +3038,7 @@ void GCodeProcessor::process_M402(const GCodeReader::GCodeLine& line)
|
||||
// https://github.com/repetier/Repetier-Firmware/blob/master/src/ArduinoAVR/Repetier/Printer.cpp
|
||||
// void Printer::GoToMemoryPosition(bool x, bool y, bool z, bool e, float feed)
|
||||
|
||||
bool has_xyz = !(line.has_x() || line.has_y() || line.has_z());
|
||||
bool has_xyz = !(line.has('X') || line.has('Y') || line.has('Z'));
|
||||
|
||||
float p = FLT_MAX;
|
||||
for (unsigned char a = X; a <= Z; ++a) {
|
||||
|
@ -543,6 +543,9 @@ namespace Slic3r {
|
||||
PrusaSlicer,
|
||||
Slic3rPE,
|
||||
Slic3r,
|
||||
#if ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
|
||||
SuperSlicer,
|
||||
#endif // ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
|
||||
Cura,
|
||||
Simplify3D,
|
||||
CraftWare,
|
||||
@ -579,14 +582,14 @@ namespace Slic3r {
|
||||
const Result& get_result() const { return m_result; }
|
||||
Result&& extract_result() { return std::move(m_result); }
|
||||
|
||||
// Process the gcode contained in the file with the given filename
|
||||
// Load a G-code into a stand-alone G-code viewer.
|
||||
// throws CanceledException through print->throw_if_canceled() (sent by the caller as callback).
|
||||
void process_file(const std::string& filename, std::function<void()> cancel_callback = nullptr);
|
||||
|
||||
// Streaming interface, for processing G-codes just generated by PrusaSlicer in a pipelined fashion.
|
||||
void initialize(const std::string& filename);
|
||||
void process_buffer(const std::string& buffer);
|
||||
void finalize();
|
||||
void finalize(bool post_process);
|
||||
|
||||
float get_time(PrintEstimatedStatistics::ETimeMode mode) const;
|
||||
std::string get_time_dhm(PrintEstimatedStatistics::ETimeMode mode) const;
|
||||
@ -599,6 +602,9 @@ namespace Slic3r {
|
||||
private:
|
||||
void apply_config(const DynamicPrintConfig& config);
|
||||
void apply_config_simplify3d(const std::string& filename);
|
||||
#if ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
|
||||
void apply_config_superslicer(const std::string& filename);
|
||||
#endif // ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
|
||||
void process_gcode_line(const GCodeReader::GCodeLine& line, bool producers_enabled);
|
||||
|
||||
// Process tags embedded into comments
|
||||
|
@ -2,6 +2,7 @@
|
||||
#include <boost/algorithm/string/classification.hpp>
|
||||
#include <boost/algorithm/string/split.hpp>
|
||||
#include <boost/nowide/fstream.hpp>
|
||||
#include <boost/nowide/cstdio.hpp>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
|
@ -1619,6 +1619,7 @@ TriangleMeshStats ModelObject::get_object_stl_stats() const
|
||||
const TriangleMeshStats& stats = volume->mesh().stats();
|
||||
|
||||
// initialize full_stats (for repaired errors)
|
||||
full_stats.open_edges += stats.open_edges;
|
||||
full_stats.degenerate_facets += stats.degenerate_facets;
|
||||
full_stats.edges_fixed += stats.edges_fixed;
|
||||
full_stats.facets_removed += stats.facets_removed;
|
||||
|
@ -179,6 +179,15 @@ Point Point::projection_onto(const Line &line) const
|
||||
return ((line.a - *this).cast<double>().squaredNorm() < (line.b - *this).cast<double>().squaredNorm()) ? line.a : line.b;
|
||||
}
|
||||
|
||||
bool has_duplicate_points(std::vector<Point> &&pts)
|
||||
{
|
||||
std::sort(pts.begin(), pts.end());
|
||||
for (size_t i = 1; i < pts.size(); ++ i)
|
||||
if (pts[i - 1] == pts[i])
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
BoundingBox get_extents(const Points &pts)
|
||||
{
|
||||
return BoundingBox(pts);
|
||||
|
@ -211,8 +211,34 @@ inline Point lerp(const Point &a, const Point &b, double t)
|
||||
return ((1. - t) * a.cast<double>() + t * b.cast<double>()).cast<coord_t>();
|
||||
}
|
||||
|
||||
extern BoundingBox get_extents(const Points &pts);
|
||||
extern BoundingBox get_extents(const std::vector<Points> &pts);
|
||||
BoundingBox get_extents(const Points &pts);
|
||||
BoundingBox get_extents(const std::vector<Points> &pts);
|
||||
|
||||
// Test for duplicate points in a vector of points.
|
||||
// The points are copied, sorted and checked for duplicates globally.
|
||||
bool has_duplicate_points(std::vector<Point> &&pts);
|
||||
inline bool has_duplicate_points(const std::vector<Point> &pts)
|
||||
{
|
||||
std::vector<Point> cpy = pts;
|
||||
return has_duplicate_points(std::move(cpy));
|
||||
}
|
||||
|
||||
// Test for duplicate points in a vector of points.
|
||||
// Only successive points are checked for equality.
|
||||
inline bool has_duplicate_successive_points(const std::vector<Point> &pts)
|
||||
{
|
||||
for (size_t i = 1; i < pts.size(); ++ i)
|
||||
if (pts[i - 1] == pts[i])
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Test for duplicate points in a vector of points.
|
||||
// Only successive points are checked for equality. Additionally, first and last points are compared for equality.
|
||||
inline bool has_duplicate_successive_points_closed(const std::vector<Point> &pts)
|
||||
{
|
||||
return has_duplicate_successive_points(pts) || (pts.size() >= 2 && pts.front() == pts.back());
|
||||
}
|
||||
|
||||
namespace int128 {
|
||||
// Exact orientation predicate,
|
||||
|
@ -334,6 +334,27 @@ extern std::vector<BoundingBox> get_extents_vector(const Polygons &polygons)
|
||||
return out;
|
||||
}
|
||||
|
||||
bool has_duplicate_points(const Polygons &polys)
|
||||
{
|
||||
#if 1
|
||||
// Check globally.
|
||||
size_t cnt = 0;
|
||||
for (const Polygon &poly : polys)
|
||||
cnt += poly.points.size();
|
||||
std::vector<Point> allpts;
|
||||
allpts.reserve(cnt);
|
||||
for (const Polygon &poly : polys)
|
||||
allpts.insert(allpts.end(), poly.points.begin(), poly.points.end());
|
||||
return has_duplicate_points(std::move(allpts));
|
||||
#else
|
||||
// Check per contour.
|
||||
for (const Polygon &poly : polys)
|
||||
if (has_duplicate_points(poly))
|
||||
return true;
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline bool is_stick(const Point &p1, const Point &p2, const Point &p3)
|
||||
{
|
||||
Point v1 = p2 - p1;
|
||||
|
@ -78,11 +78,16 @@ public:
|
||||
inline bool operator==(const Polygon &lhs, const Polygon &rhs) { return lhs.points == rhs.points; }
|
||||
inline bool operator!=(const Polygon &lhs, const Polygon &rhs) { return lhs.points != rhs.points; }
|
||||
|
||||
extern BoundingBox get_extents(const Polygon &poly);
|
||||
extern BoundingBox get_extents(const Polygons &polygons);
|
||||
extern BoundingBox get_extents_rotated(const Polygon &poly, double angle);
|
||||
extern BoundingBox get_extents_rotated(const Polygons &polygons, double angle);
|
||||
extern std::vector<BoundingBox> get_extents_vector(const Polygons &polygons);
|
||||
BoundingBox get_extents(const Polygon &poly);
|
||||
BoundingBox get_extents(const Polygons &polygons);
|
||||
BoundingBox get_extents_rotated(const Polygon &poly, double angle);
|
||||
BoundingBox get_extents_rotated(const Polygons &polygons, double angle);
|
||||
std::vector<BoundingBox> get_extents_vector(const Polygons &polygons);
|
||||
|
||||
// Test for duplicate points. The points are copied, sorted and checked for duplicates globally.
|
||||
inline bool has_duplicate_points(Polygon &&poly) { return has_duplicate_points(std::move(poly.points)); }
|
||||
inline bool has_duplicate_points(const Polygon &poly) { return has_duplicate_points(poly.points); }
|
||||
bool has_duplicate_points(const Polygons &polys);
|
||||
|
||||
inline double total_length(const Polygons &polylines) {
|
||||
double total = 0;
|
||||
@ -102,19 +107,19 @@ inline double area(const Polygons &polys)
|
||||
}
|
||||
|
||||
// Remove sticks (tentacles with zero area) from the polygon.
|
||||
extern bool remove_sticks(Polygon &poly);
|
||||
extern bool remove_sticks(Polygons &polys);
|
||||
bool remove_sticks(Polygon &poly);
|
||||
bool remove_sticks(Polygons &polys);
|
||||
|
||||
// Remove polygons with less than 3 edges.
|
||||
extern bool remove_degenerate(Polygons &polys);
|
||||
extern bool remove_small(Polygons &polys, double min_area);
|
||||
extern void remove_collinear(Polygon &poly);
|
||||
extern void remove_collinear(Polygons &polys);
|
||||
bool remove_degenerate(Polygons &polys);
|
||||
bool remove_small(Polygons &polys, double min_area);
|
||||
void remove_collinear(Polygon &poly);
|
||||
void remove_collinear(Polygons &polys);
|
||||
|
||||
// Append a vector of polygons at the end of another vector of polygons.
|
||||
inline void polygons_append(Polygons &dst, const Polygons &src) { dst.insert(dst.end(), src.begin(), src.end()); }
|
||||
inline void polygons_append(Polygons &dst, const Polygons &src) { dst.insert(dst.end(), src.begin(), src.end()); }
|
||||
|
||||
inline void polygons_append(Polygons &dst, Polygons &&src)
|
||||
inline void polygons_append(Polygons &dst, Polygons &&src)
|
||||
{
|
||||
if (dst.empty()) {
|
||||
dst = std::move(src);
|
||||
|
@ -1097,14 +1097,6 @@ size_t PresetCollection::update_compatible_internal(const PresetWithVendorProfil
|
||||
return m_idx_selected;
|
||||
}
|
||||
|
||||
// Save the preset under a new name. If the name is different from the old one,
|
||||
// a new preset is stored into the list of presets.
|
||||
// All presets are marked as not modified and the new preset is activated.
|
||||
//void PresetCollection::save_current_preset(const std::string &new_name);
|
||||
|
||||
// Delete the current preset, activate the first visible preset.
|
||||
//void PresetCollection::delete_current_preset();
|
||||
|
||||
// Update a dirty flag of the current preset
|
||||
// Return true if the dirty flag changed.
|
||||
bool PresetCollection::update_dirty()
|
||||
|
@ -143,7 +143,7 @@ public:
|
||||
|
||||
const std::string& get_preset_name_by_alias(const Preset::Type& preset_type, const std::string& alias) const;
|
||||
|
||||
// Save current preset of a required type under a new name. If the name is different from the old one,
|
||||
// Save current preset of a provided type under a new name. If the name is different from the old one,
|
||||
// Unselected option would be reverted to the beginning values
|
||||
void save_changes_for_preset(const std::string& new_name, Preset::Type type, const std::vector<std::string>& unselected_options);
|
||||
|
||||
|
@ -60,6 +60,7 @@ std::string PrintBase::output_filename(const std::string &format, const std::str
|
||||
DynamicConfig cfg;
|
||||
if (config_override != nullptr)
|
||||
cfg = *config_override;
|
||||
cfg.set_key_value("version", new ConfigOptionString(std::string(SLIC3R_VERSION)));
|
||||
PlaceholderParser::update_timestamp(cfg);
|
||||
this->update_object_placeholders(cfg, default_ext);
|
||||
if (! filename_base.empty()) {
|
||||
|
@ -1447,7 +1447,7 @@ void PrintObject::bridge_over_infill()
|
||||
Polygons to_bridge_pp = internal_solid;
|
||||
|
||||
// iterate through lower layers spanned by bridge_flow
|
||||
double bottom_z = layer->print_z - bridge_flow.height();
|
||||
double bottom_z = layer->print_z - bridge_flow.height() - EPSILON;
|
||||
for (int i = int(layer_it - m_layers.begin()) - 1; i >= 0; --i) {
|
||||
const Layer* lower_layer = m_layers[i];
|
||||
|
||||
|
@ -670,7 +670,7 @@ std::string SLAPrint::validate(std::string*) const
|
||||
return "";
|
||||
}
|
||||
|
||||
void SLAPrint::set_printer(SLAPrinter *arch)
|
||||
void SLAPrint::set_printer(SLAArchive *arch)
|
||||
{
|
||||
invalidate_step(slapsRasterize);
|
||||
m_printer = arch;
|
||||
@ -1047,15 +1047,15 @@ Vec3d SLAPrint::relative_correction() const
|
||||
Vec3d corr(1., 1., 1.);
|
||||
|
||||
if(printer_config().relative_correction.values.size() >= 2) {
|
||||
corr(X) = printer_config().relative_correction.values[0];
|
||||
corr(Y) = printer_config().relative_correction.values[0];
|
||||
corr(Z) = printer_config().relative_correction.values.back();
|
||||
corr.x() = printer_config().relative_correction.values[0];
|
||||
corr.y() = corr.x();
|
||||
corr.z() = printer_config().relative_correction.values[1];
|
||||
}
|
||||
|
||||
if(material_config().material_correction.values.size() >= 2) {
|
||||
corr(X) *= material_config().material_correction.values[0];
|
||||
corr(Y) *= material_config().material_correction.values[0];
|
||||
corr(Z) *= material_config().material_correction.values.back();
|
||||
corr.x() *= material_config().material_correction.values[0];
|
||||
corr.y() = corr.x();
|
||||
corr.z() *= material_config().material_correction.values[1];
|
||||
}
|
||||
|
||||
return corr;
|
||||
|
@ -387,7 +387,7 @@ struct SLAPrintStatistics
|
||||
}
|
||||
};
|
||||
|
||||
class SLAPrinter {
|
||||
class SLAArchive {
|
||||
protected:
|
||||
std::vector<sla::EncodedRaster> m_layers;
|
||||
|
||||
@ -395,7 +395,7 @@ protected:
|
||||
virtual sla::RasterEncoder get_encoder() const = 0;
|
||||
|
||||
public:
|
||||
virtual ~SLAPrinter() = default;
|
||||
virtual ~SLAArchive() = default;
|
||||
|
||||
virtual void apply(const SLAPrinterConfig &cfg) = 0;
|
||||
|
||||
@ -526,7 +526,7 @@ public:
|
||||
// TODO: use this structure for the preview in the future.
|
||||
const std::vector<PrintLayer>& print_layers() const { return m_printer_input; }
|
||||
|
||||
void set_printer(SLAPrinter *archiver);
|
||||
void set_printer(SLAArchive *archiver);
|
||||
|
||||
private:
|
||||
|
||||
@ -548,7 +548,7 @@ private:
|
||||
std::vector<PrintLayer> m_printer_input;
|
||||
|
||||
// The archive object which collects the raster images after slicing
|
||||
SLAPrinter *m_printer = nullptr;
|
||||
SLAArchive *m_printer = nullptr;
|
||||
|
||||
// Estimated print time, material consumed.
|
||||
SLAPrintStatistics m_print_statistics;
|
||||
|
@ -721,9 +721,9 @@ public:
|
||||
#ifdef SUPPORT_USE_AGG_RASTERIZER
|
||||
m_bbox = bbox;
|
||||
// Oversample the grid to avoid leaking of supports through or around the object walls.
|
||||
int oversampling = std::min(8, int(scale_(m_support_spacing) / (scale_(params.extrusion_width) + 100)));
|
||||
m_pixel_size = scale_(m_support_spacing / oversampling);
|
||||
assert(scale_(params.extrusion_width) + 20 < m_pixel_size);
|
||||
int extrusion_width_scaled = scale_(params.extrusion_width);
|
||||
int oversampling = std::clamp(int(scale_(m_support_spacing) / (extrusion_width_scaled + 100)), 1, 8);
|
||||
m_pixel_size = std::max<double>(extrusion_width_scaled + 21, scale_(m_support_spacing / oversampling));
|
||||
// Add one empty column / row boundaries.
|
||||
m_bbox.offset(m_pixel_size);
|
||||
// Grid size fitting the support polygons plus one pixel boundary around the polygons.
|
||||
@ -1599,7 +1599,8 @@ static inline std::tuple<Polygons, Polygons, Polygons, float> detect_overhangs(
|
||||
static inline std::pair<PrintObjectSupportMaterial::MyLayer*, PrintObjectSupportMaterial::MyLayer*> new_contact_layer(
|
||||
const PrintConfig &print_config,
|
||||
const PrintObjectConfig &object_config,
|
||||
const SlicingParameters &slicing_params,
|
||||
const SlicingParameters &slicing_params,
|
||||
const coordf_t support_layer_height_min,
|
||||
const Layer &layer,
|
||||
std::deque<PrintObjectSupportMaterial::MyLayer> &layer_storage,
|
||||
tbb::spin_mutex &layer_storage_mutex)
|
||||
@ -1629,7 +1630,8 @@ static inline std::pair<PrintObjectSupportMaterial::MyLayer*, PrintObjectSupport
|
||||
// Don't want to print a layer below the first layer height as it may not stick well.
|
||||
//FIXME there may be a need for a single layer support, then one may decide to print it either as a bottom contact or a top contact
|
||||
// and it may actually make sense to do it with a thinner layer than the first layer height.
|
||||
if (print_z < slicing_params.first_print_layer_height - EPSILON) {
|
||||
const coordf_t min_print_z = slicing_params.raft_layers() > 1 ? slicing_params.raft_interface_top_z + support_layer_height_min + EPSILON : slicing_params.first_print_layer_height - EPSILON;
|
||||
if (print_z < min_print_z) {
|
||||
// This contact layer is below the first layer height, therefore not printable. Don't support this surface.
|
||||
return std::pair<PrintObjectSupportMaterial::MyLayer*, PrintObjectSupportMaterial::MyLayer*>(nullptr, nullptr);
|
||||
} else if (print_z < slicing_params.first_print_layer_height + EPSILON) {
|
||||
@ -1650,7 +1652,7 @@ static inline std::pair<PrintObjectSupportMaterial::MyLayer*, PrintObjectSupport
|
||||
bridging_height += region->region().bridging_height_avg(print_config);
|
||||
bridging_height /= coordf_t(layer.regions().size());
|
||||
coordf_t bridging_print_z = layer.print_z - bridging_height - slicing_params.gap_support_object;
|
||||
if (bridging_print_z >= slicing_params.first_print_layer_height - EPSILON) {
|
||||
if (bridging_print_z >= min_print_z) {
|
||||
// Not below the first layer height means this layer is printable.
|
||||
if (print_z < slicing_params.first_print_layer_height + EPSILON) {
|
||||
// Align the layer with the 1st layer height.
|
||||
@ -1664,8 +1666,7 @@ static inline std::pair<PrintObjectSupportMaterial::MyLayer*, PrintObjectSupport
|
||||
if (bridging_print_z == slicing_params.first_print_layer_height) {
|
||||
bridging_layer->bottom_z = 0;
|
||||
bridging_layer->height = slicing_params.first_print_layer_height;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// Don't know the height yet.
|
||||
bridging_layer->bottom_z = bridging_print_z;
|
||||
bridging_layer->height = 0;
|
||||
@ -1917,7 +1918,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
|
||||
|
||||
// Now apply the contact areas to the layer where they need to be made.
|
||||
if (! contact_polygons.empty()) {
|
||||
auto [new_layer, bridging_layer] = new_contact_layer(*m_print_config, *m_object_config, m_slicing_params, layer, layer_storage, layer_storage_mutex);
|
||||
auto [new_layer, bridging_layer] = new_contact_layer(*m_print_config, *m_object_config, m_slicing_params, m_support_params.support_layer_height_min, layer, layer_storage, layer_storage_mutex);
|
||||
if (new_layer) {
|
||||
fill_contact_layer(*new_layer, layer_id, m_slicing_params,
|
||||
*m_object_config, slices_margin, overhang_polygons, contact_polygons, enforcer_polygons, lower_layer_polygons,
|
||||
@ -2600,8 +2601,6 @@ void PrintObjectSupportMaterial::generate_base_layers(
|
||||
// No top contacts -> no intermediate layers will be produced.
|
||||
return;
|
||||
|
||||
// coordf_t fillet_radius_scaled = scale_(m_object_config->support_material_spacing);
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::generate_base_layers() in parallel - start";
|
||||
tbb::parallel_for(
|
||||
tbb::blocked_range<size_t>(0, intermediate_layers.size()),
|
||||
@ -2696,6 +2695,7 @@ void PrintObjectSupportMaterial::generate_base_layers(
|
||||
layer_intermediate.layer_type = sltBase;
|
||||
|
||||
#if 0
|
||||
// coordf_t fillet_radius_scaled = scale_(m_object_config->support_material_spacing);
|
||||
// Fillet the base polygons and trim them again with the top, interface and contact layers.
|
||||
$base->{$i} = diff(
|
||||
offset2(
|
||||
@ -3784,7 +3784,7 @@ void PrintObjectSupportMaterial::generate_toolpaths(
|
||||
// Prepare fillers.
|
||||
SupportMaterialPattern support_pattern = m_object_config->support_material_pattern;
|
||||
bool with_sheath = m_object_config->support_material_with_sheath;
|
||||
InfillPattern infill_pattern = (support_pattern == smpHoneycomb ? ipHoneycomb : ipSupportBase);
|
||||
InfillPattern infill_pattern = support_pattern == smpHoneycomb ? ipHoneycomb : (support_density < 1.05 ? ipRectilinear : ipSupportBase);
|
||||
std::vector<float> angles;
|
||||
angles.push_back(base_angle);
|
||||
|
||||
|
@ -60,8 +60,19 @@
|
||||
#define ENABLE_FIX_PREVIEW_OPTIONS_Z (1 && ENABLE_SEAMS_USING_MODELS && ENABLE_2_4_0_ALPHA2)
|
||||
// Enable replacing a missing file during reload from disk command
|
||||
#define ENABLE_RELOAD_FROM_DISK_REPLACE_FILE (1 && ENABLE_2_4_0_ALPHA2)
|
||||
// Enable fixing the synchronization of seams with the horizontal slider in preview
|
||||
#define ENABLE_FIX_SEAMS_SYNCH (1 && ENABLE_2_4_0_ALPHA2)
|
||||
|
||||
|
||||
//====================
|
||||
// 2.4.0.alpha3 techs
|
||||
//====================
|
||||
#define ENABLE_2_4_0_ALPHA3 1
|
||||
|
||||
// Enable fixing loading of gcode files generated with SuperSlicer in GCodeViewer
|
||||
#define ENABLE_FIX_SUPERSLICER_GCODE_IMPORT (1 && ENABLE_2_4_0_ALPHA3)
|
||||
// Enable the fix for the detection of the out of bed state for sinking objects
|
||||
#define ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION (1 && ENABLE_2_4_0_ALPHA2)
|
||||
#define ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION (1 && ENABLE_2_4_0_ALPHA3)
|
||||
// Enable detection of out of bed using the bed perimeter and other improvements
|
||||
#define ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS (1 && ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION)
|
||||
|
||||
|
@ -23,6 +23,7 @@
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/nowide/cstdio.hpp>
|
||||
#include <boost/predef/other/endian.h>
|
||||
|
||||
#include <Eigen/Core>
|
||||
#include <Eigen/Dense>
|
||||
@ -191,7 +192,7 @@ bool TriangleMesh::ReadSTLFile(const char* input_file, bool repair)
|
||||
auto facets_w_1_bad_edge = stl.stats.connected_facets_2_edge - stl.stats.connected_facets_3_edge;
|
||||
auto facets_w_2_bad_edge = stl.stats.connected_facets_1_edge - stl.stats.connected_facets_2_edge;
|
||||
auto facets_w_3_bad_edge = stl.stats.number_of_facets - stl.stats.connected_facets_1_edge;
|
||||
m_stats.open_edges = facets_w_1_bad_edge + facets_w_2_bad_edge * 2 + facets_w_3_bad_edge * 3;
|
||||
m_stats.open_edges = stl.stats.backwards_edges + facets_w_1_bad_edge + facets_w_2_bad_edge * 2 + facets_w_3_bad_edge * 3;
|
||||
|
||||
m_stats.edges_fixed = stl.stats.edges_fixed;
|
||||
m_stats.degenerate_facets = stl.stats.degenerate_facets;
|
||||
@ -532,7 +533,9 @@ TriangleMesh TriangleMesh::convex_hull_3d() const
|
||||
}
|
||||
}
|
||||
|
||||
return TriangleMesh { std::move(dst_vertices), std::move(dst_facets) };
|
||||
TriangleMesh mesh{ std::move(dst_vertices), std::move(dst_facets) };
|
||||
assert(mesh.stats().manifold());
|
||||
return mesh;
|
||||
}
|
||||
|
||||
std::vector<ExPolygons> TriangleMesh::slice(const std::vector<double> &z) const
|
||||
|
@ -12,7 +12,7 @@ PRODUCTVERSION @SLIC3R_RC_VERSION@
|
||||
VALUE "ProductName", "@SLIC3R_APP_NAME@ G-code Viewer"
|
||||
VALUE "ProductVersion", "@SLIC3R_BUILD_ID@"
|
||||
VALUE "InternalName", "@SLIC3R_APP_NAME@ G-code Viewer"
|
||||
VALUE "LegalCopyright", "Copyright \251 2016-2020 Prusa Research, \251 2011-2018 Alessandro Ranellucci"
|
||||
VALUE "LegalCopyright", "Copyright \251 2016-2021 Prusa Research, \251 2011-2018 Alessandro Ranellucci"
|
||||
VALUE "OriginalFilename", "prusa-gcodeviewer.exe"
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ PRODUCTVERSION @SLIC3R_RC_VERSION@
|
||||
VALUE "ProductName", "@SLIC3R_APP_NAME@"
|
||||
VALUE "ProductVersion", "@SLIC3R_BUILD_ID@"
|
||||
VALUE "InternalName", "@SLIC3R_APP_NAME@"
|
||||
VALUE "LegalCopyright", "Copyright \251 2016-2020 Prusa Research, \251 2011-2018 Alessandro Ranellucci"
|
||||
VALUE "LegalCopyright", "Copyright \251 2016-2021 Prusa Research, \251 2011-2018 Alessandro Ranellucci"
|
||||
VALUE "OriginalFilename", "prusa-slicer.exe"
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>@SLIC3R_APP_KEY@</string>
|
||||
<key>CFBundleGetInfoString</key>
|
||||
<string>@SLIC3R_APP_NAME@ Copyright (C) 2011-2019 Alessandro Ranellucci, (C) 2016-2020 Prusa Reseach</string>
|
||||
<string>@SLIC3R_APP_NAME@ Copyright (C) 2011-2019 Alessandro Ranellucci, (C) 2016-2021 Prusa Reseach</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>PrusaSlicer.icns</string>
|
||||
<key>CFBundleName</key>
|
||||
|
@ -275,7 +275,7 @@ AboutDialog::AboutDialog()
|
||||
"<html>"
|
||||
"<body bgcolor= %1% link= %2%>"
|
||||
"<font color=%3%>"
|
||||
"%4% © 2016-2020 Prusa Research. <br />"
|
||||
"%4% © 2016-2021 Prusa Research. <br />"
|
||||
"%5% © 2011-2018 Alessandro Ranellucci. <br />"
|
||||
"<a href=\"http://slic3r.org/\">Slic3r</a> %6% "
|
||||
"<a href=\"http://www.gnu.org/licenses/agpl-3.0.html\">%7%</a>."
|
||||
|
@ -41,6 +41,7 @@
|
||||
#include "format.hpp"
|
||||
#include "MsgDialog.hpp"
|
||||
#include "libslic3r/libslic3r.h"
|
||||
#include "UnsavedChangesDialog.hpp"
|
||||
|
||||
#if defined(__linux__) && defined(__WXGTK3__)
|
||||
#define wxLinux_gtk3 true
|
||||
@ -741,10 +742,10 @@ void PageMaterials::set_compatible_printers_html_window(const std::vector<std::s
|
||||
const auto bgr_clr_str = wxString::Format(wxT("#%02X%02X%02X"), bgr_clr.Red(), bgr_clr.Green(), bgr_clr.Blue());
|
||||
const auto text_clr = wxGetApp().get_label_clr_default();//wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
|
||||
const auto text_clr_str = wxString::Format(wxT("#%02X%02X%02X"), text_clr.Red(), text_clr.Green(), text_clr.Blue());
|
||||
wxString first_line = _L("Filaments marked with <b>*</b> are <b>not</b> compatible with some installed printers.");
|
||||
wxString first_line = format_wxstr(_L("%1% marked with <b>*</b> are <b>not</b> compatible with some installed printers."), materials->technology == T_FFF ? _L("Filaments") : _L("SLA materials"));
|
||||
wxString text;
|
||||
if (all_printers) {
|
||||
wxString second_line = _L("All installed printers are compatible with the selected filament.");
|
||||
wxString second_line = format_wxstr(_L("All installed printers are compatible with the selected %1%."), materials->technology == T_FFF ? _L("filament") : _L("SLA material"));
|
||||
text = wxString::Format(
|
||||
"<html>"
|
||||
"<style>"
|
||||
@ -764,7 +765,7 @@ void PageMaterials::set_compatible_printers_html_window(const std::vector<std::s
|
||||
, second_line
|
||||
);
|
||||
} else {
|
||||
wxString second_line = _L("Only the following installed printers are compatible with the selected filament:");
|
||||
wxString second_line = printer_names.empty() ? "" : format_wxstr(_L("Only the following installed printers are compatible with the selected %1%:"), materials->technology == T_FFF ? _L("filament") : _L("SLA material"));
|
||||
text = wxString::Format(
|
||||
"<html>"
|
||||
"<style>"
|
||||
@ -2473,8 +2474,16 @@ static std::string get_first_added_preset(const std::map<std::string, std::strin
|
||||
return *diff.begin();
|
||||
}
|
||||
|
||||
bool ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater)
|
||||
bool ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater, bool& apply_keeped_changes)
|
||||
{
|
||||
wxString header, caption = _L("Configuration is editing from ConfigWizard");
|
||||
bool check_unsaved_preset_changes = page_welcome->reset_user_profile();
|
||||
if (check_unsaved_preset_changes)
|
||||
header = _L("All user presets will be deleted.");
|
||||
int act_btns = UnsavedChangesDialog::ActionButtons::KEEP;
|
||||
if (!check_unsaved_preset_changes)
|
||||
act_btns |= UnsavedChangesDialog::ActionButtons::SAVE;
|
||||
|
||||
const auto enabled_vendors = appconfig_new.vendors();
|
||||
|
||||
// Install bundles from resources if needed:
|
||||
@ -2500,6 +2509,9 @@ bool ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese
|
||||
install_bundles.emplace_back(pair.first);
|
||||
}
|
||||
}
|
||||
if (!check_unsaved_preset_changes)
|
||||
if ((check_unsaved_preset_changes = install_bundles.size() > 0))
|
||||
header = _L_PLURAL("New vendor was installed and one of its printer will be activated", "New vendors were installed and one of theirs printer will be activated", install_bundles.size());
|
||||
|
||||
#ifdef __linux__
|
||||
// Desktop integration on Linux
|
||||
@ -2531,6 +2543,10 @@ bool ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese
|
||||
if (snapshot && ! take_config_snapshot_cancel_on_error(*app_config, snapshot_reason, "", _u8L("Continue with applying configuration changes?")))
|
||||
return false;
|
||||
|
||||
if (check_unsaved_preset_changes &&
|
||||
!wxGetApp().check_and_keep_current_preset_changes(caption, header, act_btns, &apply_keeped_changes))
|
||||
return false;
|
||||
|
||||
if (install_bundles.size() > 0) {
|
||||
// Install bundles from resources.
|
||||
// Don't create snapshot - we've already done that above if applicable.
|
||||
@ -2586,17 +2602,52 @@ bool ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese
|
||||
}
|
||||
}
|
||||
|
||||
// if unsaved changes was not cheched till this moment
|
||||
if (!check_unsaved_preset_changes) {
|
||||
if ((check_unsaved_preset_changes = !preferred_model.empty())) {
|
||||
header = _L("A new Printer was installed and it will be activated.");
|
||||
if (!wxGetApp().check_and_keep_current_preset_changes(caption, header, act_btns, &apply_keeped_changes))
|
||||
return false;
|
||||
}
|
||||
else if ((check_unsaved_preset_changes = enabled_vendors_old != enabled_vendors)) {
|
||||
header = _L("Some Printers were uninstalled.");
|
||||
if (!wxGetApp().check_and_keep_current_preset_changes(caption, header, act_btns, &apply_keeped_changes))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::string first_added_filament, first_added_sla_material;
|
||||
auto apply_section = [this, app_config](const std::string& section_name, std::string& first_added_preset) {
|
||||
auto get_first_added_material_preset = [this, app_config](const std::string& section_name, std::string& first_added_preset) {
|
||||
if (appconfig_new.has_section(section_name)) {
|
||||
// get first of new added preset names
|
||||
const std::map<std::string, std::string>& old_presets = app_config->has_section(section_name) ? app_config->get_section(section_name) : std::map<std::string, std::string>();
|
||||
first_added_preset = get_first_added_preset(old_presets, appconfig_new.get_section(section_name));
|
||||
app_config->set_section(section_name, appconfig_new.get_section(section_name));
|
||||
}
|
||||
};
|
||||
apply_section(AppConfig::SECTION_FILAMENTS, first_added_filament);
|
||||
apply_section(AppConfig::SECTION_MATERIALS, first_added_sla_material);
|
||||
get_first_added_material_preset(AppConfig::SECTION_FILAMENTS, first_added_filament);
|
||||
get_first_added_material_preset(AppConfig::SECTION_MATERIALS, first_added_sla_material);
|
||||
|
||||
// if unsaved changes was not cheched till this moment
|
||||
if (!check_unsaved_preset_changes) {
|
||||
if ((check_unsaved_preset_changes = !first_added_filament.empty() || !first_added_sla_material.empty())) {
|
||||
header = format_wxstr(_L("A new %1% was installed and it will be activated."), !first_added_filament.empty() ? _L("Filament") : _L("SLA material"));
|
||||
if (!wxGetApp().check_and_keep_current_preset_changes(caption, header, act_btns, &apply_keeped_changes))
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
bool is_filaments_changed = app_config->get_section(AppConfig::SECTION_FILAMENTS) != appconfig_new.get_section(AppConfig::SECTION_FILAMENTS);
|
||||
bool is_sla_materials_changed = app_config->get_section(AppConfig::SECTION_MATERIALS) != appconfig_new.get_section(AppConfig::SECTION_MATERIALS);
|
||||
if ((check_unsaved_preset_changes = is_filaments_changed || is_sla_materials_changed)) {
|
||||
header = format_wxstr(_L("Some %1% were uninstalled."), is_filaments_changed ? _L("Filaments") : _L("SLA materials"));
|
||||
if (!wxGetApp().check_and_keep_current_preset_changes(caption, header, act_btns, &apply_keeped_changes))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// apply materials in app_config
|
||||
for (const std::string& section_name : {AppConfig::SECTION_FILAMENTS, AppConfig::SECTION_MATERIALS})
|
||||
app_config->set_section(section_name, appconfig_new.get_section(section_name));
|
||||
|
||||
app_config->set_vendors(appconfig_new);
|
||||
|
||||
@ -2624,10 +2675,16 @@ bool ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese
|
||||
|
||||
page_mode->serialize_mode(app_config);
|
||||
|
||||
preset_bundle->load_presets(*app_config, ForwardCompatibilitySubstitutionRule::EnableSilentDisableSystem,
|
||||
{preferred_model, preferred_variant, first_added_filament, first_added_sla_material});
|
||||
if (check_unsaved_preset_changes)
|
||||
preset_bundle->load_presets(*app_config, ForwardCompatibilitySubstitutionRule::EnableSilentDisableSystem,
|
||||
{preferred_model, preferred_variant, first_added_filament, first_added_sla_material});
|
||||
|
||||
if (page_custom->custom_wanted()) {
|
||||
// if unsaved changes was not cheched till this moment
|
||||
if (!check_unsaved_preset_changes &&
|
||||
!wxGetApp().check_and_keep_current_preset_changes(caption, _L("Custom printer was installed and it will be activated."), act_btns, &apply_keeped_changes))
|
||||
return false;
|
||||
|
||||
page_firmware->apply_custom_config(*custom_config);
|
||||
page_bed->apply_custom_config(*custom_config);
|
||||
page_diams->apply_custom_config(*custom_config);
|
||||
@ -2851,8 +2908,13 @@ bool ConfigWizard::run(RunReason reason, StartPage start_page)
|
||||
p->set_start_page(start_page);
|
||||
|
||||
if (ShowModal() == wxID_OK) {
|
||||
if (! p->apply_config(app.app_config, app.preset_bundle, app.preset_updater))
|
||||
bool apply_keeped_changes = false;
|
||||
if (! p->apply_config(app.app_config, app.preset_bundle, app.preset_updater, apply_keeped_changes))
|
||||
return false;
|
||||
|
||||
if (apply_keeped_changes)
|
||||
app.apply_keeped_preset_modifications();
|
||||
|
||||
app.app_config->set_legacy_datadir(false);
|
||||
app.update_mode();
|
||||
app.obj_manipul()->update_ui_from_settings();
|
||||
|
@ -611,7 +611,7 @@ struct ConfigWizard::priv
|
||||
|
||||
bool on_bnt_finish();
|
||||
bool check_and_install_missing_materials(Technology technology, const std::string &only_for_model_id = std::string());
|
||||
bool apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater);
|
||||
bool apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater, bool& apply_keeped_changes);
|
||||
// #ys_FIXME_alise
|
||||
void update_presets_in_config(const std::string& section, const std::string& alias_key, bool add);
|
||||
#ifdef __linux__
|
||||
|
@ -1819,7 +1819,8 @@ void Control::OnChar(wxKeyEvent& event)
|
||||
|
||||
void Control::OnRightDown(wxMouseEvent& event)
|
||||
{
|
||||
if (HasCapture()) return;
|
||||
if (HasCapture() || m_is_left_down)
|
||||
return;
|
||||
this->CaptureMouse();
|
||||
|
||||
const wxPoint pos = event.GetLogicalPosition(wxClientDC(this));
|
||||
@ -2097,7 +2098,7 @@ void Control::auto_color_change()
|
||||
|
||||
void Control::OnRightUp(wxMouseEvent& event)
|
||||
{
|
||||
if (!HasCapture())
|
||||
if (!HasCapture() || m_is_left_down)
|
||||
return;
|
||||
this->ReleaseMouse();
|
||||
m_is_right_down = m_is_one_layer = false;
|
||||
|
@ -323,15 +323,19 @@ wxWindow* BitmapChoiceRenderer::CreateEditorCtrl(wxWindow* parent, wxRect labelR
|
||||
|
||||
c_editor->SetSelection(atoi(data.GetText().c_str()));
|
||||
|
||||
// to avoid event propagation to other sidebar items
|
||||
c_editor->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent& evt) {
|
||||
evt.StopPropagation();
|
||||
|
||||
#ifdef __linux__
|
||||
// FinishEditing grabs new selection and triggers config update. We better call
|
||||
// it explicitly, automatic update on KILL_FOCUS didn't work on Linux.
|
||||
this->FinishEditing();
|
||||
#endif
|
||||
c_editor->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent& evt) {
|
||||
// to avoid event propagation to other sidebar items
|
||||
evt.StopPropagation();
|
||||
// FinishEditing grabs new selection and triggers config update. We better call
|
||||
// it explicitly, automatic update on KILL_FOCUS didn't work on Linux.
|
||||
this->FinishEditing();
|
||||
});
|
||||
#else
|
||||
// to avoid event propagation to other sidebar items
|
||||
c_editor->Bind(wxEVT_COMBOBOX, [](wxCommandEvent& evt) { evt.StopPropagation(); });
|
||||
#endif
|
||||
|
||||
return c_editor;
|
||||
}
|
||||
|
@ -267,7 +267,12 @@ void GCodeViewer::SequentialView::Marker::render() const
|
||||
imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Tool position") + ":");
|
||||
ImGui::SameLine();
|
||||
char buf[1024];
|
||||
sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", m_world_position(0), m_world_position(1), m_world_position(2));
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
const Vec3f position = m_world_position + m_world_offset;
|
||||
sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", position.x(), position.y(), position.z());
|
||||
#else
|
||||
sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", m_world_position.x(), m_world_position.y(), m_world_position.z());
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
imgui.text(std::string(buf));
|
||||
|
||||
// force extra frame to automatically update window size
|
||||
@ -568,6 +573,89 @@ GCodeViewer::GCodeViewer()
|
||||
// m_sequential_view.skip_invisible_moves = true;
|
||||
}
|
||||
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
void GCodeViewer::init()
|
||||
{
|
||||
if (m_gl_data_initialized)
|
||||
return;
|
||||
|
||||
// initializes opengl data of TBuffers
|
||||
for (size_t i = 0; i < m_buffers.size(); ++i) {
|
||||
TBuffer& buffer = m_buffers[i];
|
||||
EMoveType type = buffer_type(i);
|
||||
switch (type)
|
||||
{
|
||||
default: { break; }
|
||||
case EMoveType::Tool_change:
|
||||
case EMoveType::Color_change:
|
||||
case EMoveType::Pause_Print:
|
||||
case EMoveType::Custom_GCode:
|
||||
case EMoveType::Retract:
|
||||
case EMoveType::Unretract:
|
||||
case EMoveType::Seam: {
|
||||
#if ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
if (wxGetApp().is_gl_version_greater_or_equal_to(3, 3)) {
|
||||
buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::InstancedModel;
|
||||
buffer.shader = "gouraud_light_instanced";
|
||||
buffer.model.model.init_from(diamond(16));
|
||||
buffer.model.color = option_color(type);
|
||||
buffer.model.instances.format = InstanceVBuffer::EFormat::InstancedModel;
|
||||
}
|
||||
else {
|
||||
buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::BatchedModel;
|
||||
buffer.vertices.format = VBuffer::EFormat::PositionNormal3;
|
||||
buffer.shader = "gouraud_light";
|
||||
|
||||
buffer.model.data = diamond(16);
|
||||
buffer.model.color = option_color(type);
|
||||
buffer.model.instances.format = InstanceVBuffer::EFormat::BatchedModel;
|
||||
}
|
||||
break;
|
||||
#else
|
||||
if (wxGetApp().is_gl_version_greater_or_equal_to(3, 3)) {
|
||||
buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::Model;
|
||||
buffer.shader = "gouraud_light_instanced";
|
||||
buffer.model.model.init_from(diamond(16));
|
||||
buffer.model.color = option_color(type);
|
||||
}
|
||||
else {
|
||||
buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::Point;
|
||||
buffer.vertices.format = VBuffer::EFormat::Position;
|
||||
buffer.shader = wxGetApp().is_glsl_version_greater_or_equal_to(1, 20) ? "options_120" : "options_110";
|
||||
}
|
||||
break;
|
||||
#endif // ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
}
|
||||
case EMoveType::Wipe:
|
||||
case EMoveType::Extrude: {
|
||||
buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::Triangle;
|
||||
buffer.vertices.format = VBuffer::EFormat::PositionNormal3;
|
||||
buffer.shader = "gouraud_light";
|
||||
break;
|
||||
}
|
||||
case EMoveType::Travel: {
|
||||
buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::Line;
|
||||
buffer.vertices.format = VBuffer::EFormat::PositionNormal1;
|
||||
buffer.shader = "toolpaths_lines";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
set_toolpath_move_type_visible(EMoveType::Extrude, true);
|
||||
}
|
||||
|
||||
// initializes tool marker
|
||||
m_sequential_view.marker.init();
|
||||
|
||||
// initializes point sizes
|
||||
std::array<int, 2> point_sizes;
|
||||
::glGetIntegerv(GL_ALIASED_POINT_SIZE_RANGE, point_sizes.data());
|
||||
m_detected_point_sizes = { static_cast<float>(point_sizes[0]), static_cast<float>(point_sizes[1]) };
|
||||
|
||||
m_gl_data_initialized = true;
|
||||
}
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
|
||||
void GCodeViewer::load(const GCodeProcessor::Result& gcode_result, const Print& print, bool initialized)
|
||||
{
|
||||
// avoid processing if called with the same gcode_result
|
||||
@ -751,6 +839,7 @@ void GCodeViewer::reset()
|
||||
|
||||
void GCodeViewer::render()
|
||||
{
|
||||
#if !ENABLE_SEAMS_USING_MODELS
|
||||
auto init_gl_data = [this]() {
|
||||
// initializes opengl data of TBuffers
|
||||
for (size_t i = 0; i < m_buffers.size(); ++i) {
|
||||
@ -766,26 +855,6 @@ void GCodeViewer::render()
|
||||
case EMoveType::Retract:
|
||||
case EMoveType::Unretract:
|
||||
case EMoveType::Seam: {
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
#if ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
if (wxGetApp().is_gl_version_greater_or_equal_to(3, 3)) {
|
||||
buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::InstancedModel;
|
||||
buffer.shader = "gouraud_light_instanced";
|
||||
buffer.model.model.init_from(diamond(16));
|
||||
buffer.model.color = option_color(type);
|
||||
buffer.model.instances.format = InstanceVBuffer::EFormat::InstancedModel;
|
||||
}
|
||||
else {
|
||||
buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::BatchedModel;
|
||||
buffer.vertices.format = VBuffer::EFormat::PositionNormal3;
|
||||
buffer.shader = "gouraud_light";
|
||||
|
||||
buffer.model.data = diamond(16);
|
||||
buffer.model.color = option_color(type);
|
||||
buffer.model.instances.format = InstanceVBuffer::EFormat::BatchedModel;
|
||||
}
|
||||
break;
|
||||
#else
|
||||
if (wxGetApp().is_gl_version_greater_or_equal_to(3, 3)) {
|
||||
buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::Model;
|
||||
buffer.shader = "gouraud_light_instanced";
|
||||
@ -798,36 +867,17 @@ void GCodeViewer::render()
|
||||
buffer.shader = wxGetApp().is_glsl_version_greater_or_equal_to(1, 20) ? "options_120" : "options_110";
|
||||
}
|
||||
break;
|
||||
#endif // ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
#else
|
||||
buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::Point;
|
||||
buffer.vertices.format = VBuffer::EFormat::Position;
|
||||
buffer.shader = wxGetApp().is_glsl_version_greater_or_equal_to(1, 20) ? "options_120" : "options_110";
|
||||
break;
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
}
|
||||
case EMoveType::Wipe:
|
||||
case EMoveType::Extrude: {
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::Triangle;
|
||||
buffer.vertices.format = VBuffer::EFormat::PositionNormal3;
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
buffer.shader = "gouraud_light";
|
||||
break;
|
||||
}
|
||||
case EMoveType::Travel: {
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::Line;
|
||||
buffer.vertices.format = VBuffer::EFormat::PositionNormal1;
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
buffer.shader = "toolpaths_lines";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
set_toolpath_move_type_visible(EMoveType::Extrude, true);
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
}
|
||||
|
||||
// initializes tool marker
|
||||
@ -839,6 +889,7 @@ void GCodeViewer::render()
|
||||
m_detected_point_sizes = { static_cast<float>(point_sizes[0]), static_cast<float>(point_sizes[1]) };
|
||||
m_gl_data_initialized = true;
|
||||
};
|
||||
#endif // !ENABLE_SEAMS_USING_MODELS
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_STATISTICS
|
||||
m_statistics.reset_opengl();
|
||||
@ -847,10 +898,12 @@ void GCodeViewer::render()
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
#endif // ENABLE_GCODE_VIEWER_STATISTICS
|
||||
|
||||
#if !ENABLE_SEAMS_USING_MODELS
|
||||
// OpenGL data must be initialized after the glContext has been created.
|
||||
// This is ensured when this method is called by GLCanvas3D::_render_gcode().
|
||||
if (!m_gl_data_initialized)
|
||||
init_gl_data();
|
||||
#endif // !ENABLE_SEAMS_USING_MODELS
|
||||
|
||||
if (m_roles.empty())
|
||||
return;
|
||||
@ -862,6 +915,9 @@ void GCodeViewer::render()
|
||||
render_legend(legend_height);
|
||||
if (m_sequential_view.current.last != m_sequential_view.endpoints.last) {
|
||||
m_sequential_view.marker.set_world_position(m_sequential_view.current_position);
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
m_sequential_view.marker.set_world_offset(m_sequential_view.current_offset);
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
m_sequential_view.render(legend_height);
|
||||
}
|
||||
#if ENABLE_GCODE_VIEWER_STATISTICS
|
||||
@ -1500,10 +1556,18 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
||||
m_max_bounding_box = m_paths_bounding_box;
|
||||
m_max_bounding_box.merge(m_paths_bounding_box.max + m_sequential_view.marker.get_bounding_box().size()[2] * Vec3d::UnitZ());
|
||||
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
m_sequential_view.gcode_ids.clear();
|
||||
for (size_t i = 0; i < gcode_result.moves.size(); ++i) {
|
||||
const GCodeProcessor::MoveVertex& move = gcode_result.moves[i];
|
||||
if (move.type != EMoveType::Seam)
|
||||
m_sequential_view.gcode_ids.push_back(move.gcode_id);
|
||||
}
|
||||
#else
|
||||
for (const GCodeProcessor::MoveVertex& move : gcode_result.moves) {
|
||||
m_sequential_view.gcode_ids.push_back(move.gcode_id);
|
||||
}
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
|
||||
std::vector<MultiVertexBuffer> vertices(m_buffers.size());
|
||||
std::vector<MultiIndexBuffer> indices(m_buffers.size());
|
||||
@ -1511,11 +1575,27 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
||||
std::vector<InstanceBuffer> instances(m_buffers.size());
|
||||
std::vector<InstanceIdBuffer> instances_ids(m_buffers.size());
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
std::vector<InstancesOffsets> instances_offsets(m_buffers.size());
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
std::vector<float> options_zs;
|
||||
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
size_t seams_count = 0;
|
||||
std::vector<size_t> seams_ids;
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
|
||||
// toolpaths data -> extract vertices from result
|
||||
for (size_t i = 0; i < m_moves_count; ++i) {
|
||||
const GCodeProcessor::MoveVertex& curr = gcode_result.moves[i];
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
if (curr.type == EMoveType::Seam) {
|
||||
++seams_count;
|
||||
seams_ids.push_back(i);
|
||||
}
|
||||
|
||||
size_t move_id = i - seams_count;
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
|
||||
// skip first vertex
|
||||
if (i == 0)
|
||||
@ -1538,6 +1618,9 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
InstanceBuffer& inst_buffer = instances[id];
|
||||
InstanceIdBuffer& inst_id_buffer = instances_ids[id];
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
InstancesOffsets& inst_offsets = instances_offsets[id];
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
|
||||
// ensure there is at least one vertex buffer
|
||||
@ -1556,7 +1639,11 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
||||
if (t_buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::Triangle) {
|
||||
Path& last_path = t_buffer.paths.back();
|
||||
if (prev.type == curr.type && last_path.matches(curr))
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
last_path.add_sub_path(prev, static_cast<unsigned int>(v_multibuffer.size()) - 1, 0, move_id - 1);
|
||||
#else
|
||||
last_path.add_sub_path(prev, static_cast<unsigned int>(v_multibuffer.size()) - 1, 0, i - 1);
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
}
|
||||
}
|
||||
|
||||
@ -1566,12 +1653,21 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
||||
{
|
||||
case TBuffer::ERenderPrimitiveType::Point: { add_vertices_as_point(curr, v_buffer); break; }
|
||||
case TBuffer::ERenderPrimitiveType::Line: { add_vertices_as_line(prev, curr, v_buffer); break; }
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
case TBuffer::ERenderPrimitiveType::Triangle: { add_vertices_as_solid(prev, curr, t_buffer, static_cast<unsigned int>(v_multibuffer.size()) - 1, v_buffer, move_id); break; }
|
||||
#else
|
||||
case TBuffer::ERenderPrimitiveType::Triangle: { add_vertices_as_solid(prev, curr, t_buffer, static_cast<unsigned int>(v_multibuffer.size()) - 1, v_buffer, i); break; }
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
#if ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
case TBuffer::ERenderPrimitiveType::InstancedModel:
|
||||
{
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
add_model_instance(curr, inst_buffer, inst_id_buffer, move_id);
|
||||
inst_offsets.push_back(prev.position - curr.position);
|
||||
#else
|
||||
add_model_instance(curr, inst_buffer, inst_id_buffer, i);
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
#if ENABLE_GCODE_VIEWER_STATISTICS
|
||||
++m_statistics.instances_count;
|
||||
#endif // ENABLE_GCODE_VIEWER_STATISTICS
|
||||
@ -1579,7 +1675,12 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
||||
}
|
||||
case TBuffer::ERenderPrimitiveType::BatchedModel:
|
||||
{
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
add_vertices_as_model_batch(curr, t_buffer.model.data, v_buffer, inst_buffer, inst_id_buffer, move_id);
|
||||
inst_offsets.push_back(prev.position - curr.position);
|
||||
#else
|
||||
add_vertices_as_model_batch(curr, t_buffer.model.data, v_buffer, inst_buffer, inst_id_buffer, i);
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
#if ENABLE_GCODE_VIEWER_STATISTICS
|
||||
++m_statistics.batched_count;
|
||||
#endif // ENABLE_GCODE_VIEWER_STATISTICS
|
||||
@ -1588,7 +1689,11 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
||||
#else
|
||||
case TBuffer::ERenderPrimitiveType::Model:
|
||||
{
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
add_model_instance(curr, inst_buffer, inst_id_buffer, move_id);
|
||||
#else
|
||||
add_model_instance(curr, inst_buffer, inst_id_buffer, i);
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
#if ENABLE_GCODE_VIEWER_STATISTICS
|
||||
++m_statistics.instances_count;
|
||||
#endif // ENABLE_GCODE_VIEWER_STATISTICS
|
||||
@ -1607,7 +1712,11 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
||||
}
|
||||
|
||||
// smooth toolpaths corners for the given TBuffer using triangles
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
auto smooth_triangle_toolpaths_corners = [&gcode_result, &seams_ids](const TBuffer& t_buffer, MultiVertexBuffer& v_multibuffer) {
|
||||
#else
|
||||
auto smooth_triangle_toolpaths_corners = [&gcode_result](const TBuffer& t_buffer, MultiVertexBuffer& v_multibuffer) {
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
auto extract_position_at = [](const VertexBuffer& vertices, size_t offset) {
|
||||
return Vec3f(vertices[offset + 0], vertices[offset + 1], vertices[offset + 2]);
|
||||
};
|
||||
@ -1681,6 +1790,16 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
||||
}
|
||||
};
|
||||
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
auto extract_move_id = [&seams_ids](size_t id) {
|
||||
for (int i = seams_ids.size() - 1; i >= 0; --i) {
|
||||
if (seams_ids[i] < id + i + 1)
|
||||
return id + (size_t)i + 1;
|
||||
}
|
||||
return id;
|
||||
};
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
|
||||
size_t vertex_size_floats = t_buffer.vertices.vertex_size_floats();
|
||||
for (const Path& path : t_buffer.paths) {
|
||||
// the two segments of the path sharing the current vertex may belong
|
||||
@ -1691,9 +1810,16 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
||||
const float half_width = 0.5f * path.width;
|
||||
for (size_t j = 1; j < path_vertices_count - 1; ++j) {
|
||||
size_t curr_s_id = path.sub_paths.front().first.s_id + j;
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
size_t move_id = extract_move_id(curr_s_id);
|
||||
const Vec3f& prev = gcode_result.moves[move_id - 1].position;
|
||||
const Vec3f& curr = gcode_result.moves[move_id].position;
|
||||
const Vec3f& next = gcode_result.moves[move_id + 1].position;
|
||||
#else
|
||||
const Vec3f& prev = gcode_result.moves[curr_s_id - 1].position;
|
||||
const Vec3f& curr = gcode_result.moves[curr_s_id].position;
|
||||
const Vec3f& next = gcode_result.moves[curr_s_id + 1].position;
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
|
||||
// select the subpaths which contains the previous/next segments
|
||||
if (!path.sub_paths[prev_sub_path_id].contains(curr_s_id))
|
||||
@ -1760,6 +1886,11 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
||||
}
|
||||
}
|
||||
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
// dismiss, no more needed
|
||||
std::vector<size_t>().swap(seams_ids);
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
|
||||
for (MultiVertexBuffer& v_multibuffer : vertices) {
|
||||
for (VertexBuffer& v_buffer : v_multibuffer) {
|
||||
v_buffer.shrink_to_fit();
|
||||
@ -1784,6 +1915,9 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
||||
if (!inst_buffer.empty()) {
|
||||
t_buffer.model.instances.buffer = inst_buffer;
|
||||
t_buffer.model.instances.s_ids = instances_ids[i];
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
t_buffer.model.instances.offsets = instances_offsets[i];
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
}
|
||||
}
|
||||
else {
|
||||
@ -1792,6 +1926,9 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
||||
if (!inst_buffer.empty()) {
|
||||
t_buffer.model.instances.buffer = inst_buffer;
|
||||
t_buffer.model.instances.s_ids = instances_ids[i];
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
t_buffer.model.instances.offsets = instances_offsets[i];
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
}
|
||||
}
|
||||
#else
|
||||
@ -1800,6 +1937,9 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
||||
if (!inst_buffer.empty()) {
|
||||
t_buffer.model.instances.buffer = inst_buffer;
|
||||
t_buffer.model.instances.s_ids = instances_ids[i];
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
t_buffer.model.instances.offsets = instances_offsets[i];
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
}
|
||||
}
|
||||
else {
|
||||
@ -1860,8 +2000,18 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
||||
using VboIndexList = std::vector<unsigned int>;
|
||||
std::vector<VboIndexList> vbo_indices(m_buffers.size());
|
||||
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
seams_count = 0;
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
|
||||
for (size_t i = 0; i < m_moves_count; ++i) {
|
||||
const GCodeProcessor::MoveVertex& curr = gcode_result.moves[i];
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
if (curr.type == EMoveType::Seam)
|
||||
++seams_count;
|
||||
|
||||
size_t move_id = i - seams_count;
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
|
||||
// skip first vertex
|
||||
if (i == 0)
|
||||
@ -1912,7 +2062,11 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
||||
if (t_buffer.render_primitive_type != TBuffer::ERenderPrimitiveType::Point) {
|
||||
#endif // ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
Path& last_path = t_buffer.paths.back();
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
last_path.add_sub_path(prev, static_cast<unsigned int>(i_multibuffer.size()) - 1, 0, move_id - 1);
|
||||
#else
|
||||
last_path.add_sub_path(prev, static_cast<unsigned int>(i_multibuffer.size()) - 1, 0, i - 1);
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
}
|
||||
}
|
||||
|
||||
@ -1937,7 +2091,11 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
||||
if (t_buffer.render_primitive_type != TBuffer::ERenderPrimitiveType::Point) {
|
||||
#endif // ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
Path& last_path = t_buffer.paths.back();
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
last_path.add_sub_path(prev, static_cast<unsigned int>(i_multibuffer.size()) - 1, 0, move_id - 1);
|
||||
#else
|
||||
last_path.add_sub_path(prev, static_cast<unsigned int>(i_multibuffer.size()) - 1, 0, i - 1);
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
}
|
||||
}
|
||||
|
||||
@ -1946,17 +2104,29 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
||||
switch (t_buffer.render_primitive_type)
|
||||
{
|
||||
case TBuffer::ERenderPrimitiveType::Point: {
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
add_indices_as_point(curr, t_buffer, static_cast<unsigned int>(i_multibuffer.size()) - 1, i_buffer, move_id);
|
||||
#else
|
||||
add_indices_as_point(curr, t_buffer, static_cast<unsigned int>(i_multibuffer.size()) - 1, i_buffer, i);
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
curr_vertex_buffer.second += t_buffer.max_vertices_per_segment();
|
||||
break;
|
||||
}
|
||||
case TBuffer::ERenderPrimitiveType::Line: {
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
add_indices_as_line(prev, curr, t_buffer, static_cast<unsigned int>(i_multibuffer.size()) - 1, i_buffer, move_id);
|
||||
#else
|
||||
add_indices_as_line(prev, curr, t_buffer, static_cast<unsigned int>(i_multibuffer.size()) - 1, i_buffer, i);
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
curr_vertex_buffer.second += t_buffer.max_vertices_per_segment();
|
||||
break;
|
||||
}
|
||||
case TBuffer::ERenderPrimitiveType::Triangle: {
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
add_indices_as_solid(prev, curr, next, t_buffer, curr_vertex_buffer.second, static_cast<unsigned int>(i_multibuffer.size()) - 1, i_buffer, move_id);
|
||||
#else
|
||||
add_indices_as_solid(prev, curr, next, t_buffer, curr_vertex_buffer.second, static_cast<unsigned int>(i_multibuffer.size()) - 1, i_buffer, i);
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
break;
|
||||
}
|
||||
#if ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
@ -2051,16 +2221,34 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
||||
|
||||
// layers zs / roles / extruder ids -> extract from result
|
||||
size_t last_travel_s_id = 0;
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
seams_count = 0;
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
for (size_t i = 0; i < m_moves_count; ++i) {
|
||||
const GCodeProcessor::MoveVertex& move = gcode_result.moves[i];
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
if (move.type == EMoveType::Seam)
|
||||
++seams_count;
|
||||
|
||||
size_t move_id = i - seams_count;
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
|
||||
if (move.type == EMoveType::Extrude) {
|
||||
// layers zs
|
||||
const double* const last_z = m_layers.empty() ? nullptr : &m_layers.get_zs().back();
|
||||
const double z = static_cast<double>(move.position.z());
|
||||
if (last_z == nullptr || z < *last_z - EPSILON || *last_z + EPSILON < z)
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
m_layers.append(z, { last_travel_s_id, move_id });
|
||||
#else
|
||||
m_layers.append(z, { last_travel_s_id, i });
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
else
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
m_layers.get_endpoints().back().last = move_id;
|
||||
#else
|
||||
m_layers.get_endpoints().back().last = i;
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
// extruder ids
|
||||
m_extruder_ids.emplace_back(move.extruder_id);
|
||||
// roles
|
||||
@ -2068,10 +2256,17 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
||||
m_roles.emplace_back(move.extrusion_role);
|
||||
}
|
||||
else if (move.type == EMoveType::Travel) {
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
if (move_id - last_travel_s_id > 1 && !m_layers.empty())
|
||||
m_layers.get_endpoints().back().last = move_id;
|
||||
|
||||
last_travel_s_id = move_id;
|
||||
#else
|
||||
if (i - last_travel_s_id > 1 && !m_layers.empty())
|
||||
m_layers.get_endpoints().back().last = i;
|
||||
|
||||
|
||||
last_travel_s_id = i;
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
}
|
||||
}
|
||||
|
||||
@ -2342,6 +2537,9 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool
|
||||
sequential_view->current_position.x() = buffer.model.instances.buffer[offset + 0];
|
||||
sequential_view->current_position.y() = buffer.model.instances.buffer[offset + 1];
|
||||
sequential_view->current_position.z() = buffer.model.instances.buffer[offset + 2];
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
sequential_view->current_offset = buffer.model.instances.offsets[i];
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
|
||||
found = true;
|
||||
break;
|
||||
@ -2381,6 +2579,10 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool
|
||||
glsafe(::glGetBufferSubData(GL_ARRAY_BUFFER, static_cast<GLintptr>(index * buffer.vertices.vertex_size_bytes()), static_cast<GLsizeiptr>(3 * sizeof(float)), static_cast<void*>(sequential_view->current_position.data())));
|
||||
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
|
||||
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
sequential_view->current_offset = Vec3f::Zero();
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
@ -31,6 +31,9 @@ class GCodeViewer
|
||||
using InstanceBuffer = std::vector<float>;
|
||||
using InstanceIdBuffer = std::vector<size_t>;
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
using InstancesOffsets = std::vector<Vec3f>;
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
|
||||
static const std::vector<Color> Extrusion_Role_Colors;
|
||||
static const std::vector<Color> Options_Colors;
|
||||
@ -151,6 +154,10 @@ class GCodeViewer
|
||||
InstanceBuffer buffer;
|
||||
// indices of the moves for all instances
|
||||
std::vector<size_t> s_ids;
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
// position offsets, used to show the correct value of the tool position
|
||||
InstancesOffsets offsets;
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
Ranges render_ranges;
|
||||
|
||||
size_t data_size_bytes() const { return s_ids.size() * instance_size_bytes(); }
|
||||
@ -665,6 +672,12 @@ public:
|
||||
GLModel m_model;
|
||||
Vec3f m_world_position;
|
||||
Transform3f m_world_transform;
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
// for seams, the position of the marker is on the last endpoint of the toolpath containing it
|
||||
// the offset is used to show the correct value of tool position in the "ToolPosition" window
|
||||
// see implementation of render() method
|
||||
Vec3f m_world_offset;
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
float m_z_offset{ 0.5f };
|
||||
bool m_visible{ true };
|
||||
|
||||
@ -674,6 +687,9 @@ public:
|
||||
const BoundingBoxf3& get_bounding_box() const { return m_model.get_bounding_box(); }
|
||||
|
||||
void set_world_position(const Vec3f& position);
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
void set_world_offset(const Vec3f& offset) { m_world_offset = offset; }
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
|
||||
bool is_visible() const { return m_visible; }
|
||||
void set_visible(bool visible) { m_visible = visible; }
|
||||
@ -731,6 +747,9 @@ public:
|
||||
Endpoints global;
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
Vec3f current_position{ Vec3f::Zero() };
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
Vec3f current_offset{ Vec3f::Zero() };
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
Marker marker;
|
||||
GCodeWindow gcode_window;
|
||||
std::vector<unsigned int> gcode_ids;
|
||||
@ -789,6 +808,10 @@ public:
|
||||
GCodeViewer();
|
||||
~GCodeViewer() { reset(); }
|
||||
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
void init();
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
|
||||
// extract rendering data from the given parameters
|
||||
void load(const GCodeProcessor::Result& gcode_result, const Print& print, bool initialized);
|
||||
// recalculate ranges in dependence of what is visible and sets tool/print colors
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "slic3r/GUI/3DBed.hpp"
|
||||
#include "slic3r/GUI/Plater.hpp"
|
||||
#include "slic3r/GUI/MainFrame.hpp"
|
||||
#include "slic3r/Utils/UndoRedo.hpp"
|
||||
|
||||
#include "GUI_App.hpp"
|
||||
#include "GUI_ObjectList.hpp"
|
||||
@ -1399,6 +1400,11 @@ void GLCanvas3D::render()
|
||||
if (!is_initialized() && !init())
|
||||
return;
|
||||
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
if (!m_main_toolbar.is_enabled())
|
||||
m_gcode_viewer.init();
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
|
||||
if (wxGetApp().plater()->get_bed().get_shape().empty()) {
|
||||
// this happens at startup when no data is still saved under <>\AppData\Roaming\Slic3rPE
|
||||
post_event(SimpleEvent(EVT_GLCANVAS_UPDATE_BED_SHAPE));
|
||||
@ -4429,12 +4435,11 @@ bool GLCanvas3D::_init_main_toolbar()
|
||||
arrow_data.top = 0;
|
||||
arrow_data.right = 0;
|
||||
arrow_data.bottom = 0;
|
||||
|
||||
if (!m_main_toolbar.init_arrow(arrow_data))
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error) << "Main toolbar failed to load arrow texture.";
|
||||
}
|
||||
|
||||
// m_gizmos is created at constructor, thus we can init arrow here.
|
||||
if (!m_gizmos.init_arrow(arrow_data))
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error) << "Gizmos manager failed to load arrow texture.";
|
||||
@ -4645,6 +4650,18 @@ bool GLCanvas3D::_init_undoredo_toolbar()
|
||||
return true;
|
||||
}
|
||||
|
||||
// init arrow
|
||||
BackgroundTexture::Metadata arrow_data;
|
||||
arrow_data.filename = "toolbar_arrow.svg";
|
||||
arrow_data.left = 0;
|
||||
arrow_data.top = 0;
|
||||
arrow_data.right = 0;
|
||||
arrow_data.bottom = 0;
|
||||
if (!m_undoredo_toolbar.init_arrow(arrow_data))
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error) << "Undo/Redo toolbar failed to load arrow texture.";
|
||||
}
|
||||
|
||||
// m_undoredo_toolbar.set_layout_type(GLToolbar::Layout::Vertical);
|
||||
m_undoredo_toolbar.set_layout_type(GLToolbar::Layout::Horizontal);
|
||||
m_undoredo_toolbar.set_horizontal_orientation(GLToolbar::Layout::HO_Left);
|
||||
@ -5424,6 +5441,10 @@ void GLCanvas3D::_render_undoredo_toolbar()
|
||||
|
||||
m_undoredo_toolbar.set_position(top, left);
|
||||
m_undoredo_toolbar.render(*this);
|
||||
if (m_toolbar_highlighter.m_render_arrow)
|
||||
{
|
||||
m_undoredo_toolbar.render_arrow(*this, m_toolbar_highlighter.m_toolbar_item);
|
||||
}
|
||||
}
|
||||
|
||||
void GLCanvas3D::_render_collapse_toolbar() const
|
||||
@ -6460,7 +6481,7 @@ void GLCanvas3D::_update_selection_from_hover()
|
||||
|
||||
// the selection is going to be modified (Add)
|
||||
if (!contains_all) {
|
||||
wxGetApp().plater()->take_snapshot(_(L("Selection-Add from rectangle")));
|
||||
wxGetApp().plater()->take_snapshot(_(L("Selection-Add from rectangle")), UndoRedo::SnapshotType::Selection);
|
||||
selection_changed = true;
|
||||
}
|
||||
}
|
||||
@ -6475,7 +6496,7 @@ void GLCanvas3D::_update_selection_from_hover()
|
||||
|
||||
// the selection is going to be modified (Remove)
|
||||
if (contains_any) {
|
||||
wxGetApp().plater()->take_snapshot(_(L("Selection-Remove from rectangle")));
|
||||
wxGetApp().plater()->take_snapshot(_(L("Selection-Remove from rectangle")), UndoRedo::SnapshotType::Selection);
|
||||
selection_changed = true;
|
||||
}
|
||||
}
|
||||
|
@ -623,6 +623,9 @@ public:
|
||||
void reset_volumes();
|
||||
ModelInstanceEPrintVolumeState check_volumes_outside_state() const;
|
||||
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
void init_gcode_viewer() { m_gcode_viewer.init(); }
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
void reset_gcode_toolpaths() { m_gcode_viewer.reset(); }
|
||||
const GCodeViewer::SequentialView& get_gcode_sequential_view() const { return m_gcode_viewer.get_sequential_view(); }
|
||||
void update_gcode_sequential_view_current(unsigned int first, unsigned int last) { m_gcode_viewer.update_sequential_view_current(first, last); }
|
||||
|
@ -1143,6 +1143,10 @@ void GLToolbar::render_background(float left, float top, float right, float bott
|
||||
|
||||
void GLToolbar::render_arrow(const GLCanvas3D& parent, GLToolbarItem* highlighted_item)
|
||||
{
|
||||
// arrow texture not initialized
|
||||
if (m_arrow_texture.texture.get_id() == 0)
|
||||
return;
|
||||
|
||||
float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom();
|
||||
float factor = inv_zoom * m_layout.scale;
|
||||
|
||||
@ -1157,6 +1161,7 @@ void GLToolbar::render_arrow(const GLCanvas3D& parent, GLToolbarItem* highlighte
|
||||
float left = m_layout.left;
|
||||
float top = m_layout.top - icon_stride;
|
||||
|
||||
bool found = false;
|
||||
for (const GLToolbarItem* item : m_items) {
|
||||
if (!item->is_visible())
|
||||
continue;
|
||||
@ -1164,11 +1169,15 @@ void GLToolbar::render_arrow(const GLCanvas3D& parent, GLToolbarItem* highlighte
|
||||
if (item->is_separator())
|
||||
left += separator_stride;
|
||||
else {
|
||||
if (item->get_name() == highlighted_item->get_name())
|
||||
if (item->get_name() == highlighted_item->get_name()) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
left += icon_stride;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
return;
|
||||
|
||||
left += border;
|
||||
top -= separator_stride;
|
||||
|
@ -694,7 +694,6 @@ GUI_App::GUI_App(EAppMode mode)
|
||||
, m_app_mode(mode)
|
||||
, m_em_unit(10)
|
||||
, m_imgui(new ImGuiWrapper())
|
||||
, m_wizard(nullptr)
|
||||
, m_removable_drive_manager(std::make_unique<RemovableDriveManager>())
|
||||
, m_other_instance_message_handler(std::make_unique<OtherInstanceMessageHandler>())
|
||||
{
|
||||
@ -1333,8 +1332,6 @@ void GUI_App::recreate_GUI(const wxString& msg_name)
|
||||
|
||||
dlg.Update(30, _L("Recreating") + dots);
|
||||
old_main_frame->Destroy();
|
||||
// For this moment ConfigWizard is deleted, invalidate it.
|
||||
m_wizard = nullptr;
|
||||
|
||||
dlg.Update(80, _L("Loading of current presets") + dots);
|
||||
m_printhost_job_queue.reset(new PrintHostJobQueue(mainframe->printhost_queue_dlg()));
|
||||
@ -1403,17 +1400,15 @@ void GUI_App::force_colors_update()
|
||||
void GUI_App::update_ui_from_settings()
|
||||
{
|
||||
update_label_colours();
|
||||
mainframe->update_ui_from_settings();
|
||||
|
||||
#ifdef _WIN32
|
||||
// Upadte UU colors before Update UI from settings
|
||||
if (m_force_colors_update) {
|
||||
m_force_colors_update = false;
|
||||
mainframe->force_color_changed();
|
||||
mainframe->diff_dialog.force_color_changed();
|
||||
if (m_wizard)
|
||||
m_wizard->force_color_changed();
|
||||
}
|
||||
#endif
|
||||
mainframe->update_ui_from_settings();
|
||||
}
|
||||
|
||||
void GUI_App::persist_window_geometry(wxTopLevelWindow *window, bool default_maximized)
|
||||
@ -1871,8 +1866,9 @@ void GUI_App::add_config_menu(wxMenuBar *menu)
|
||||
#endif
|
||||
case ConfigMenuTakeSnapshot:
|
||||
// Take a configuration snapshot.
|
||||
if (check_and_save_current_preset_changes()) {
|
||||
wxTextEntryDialog dlg(nullptr, _L("Taking configuration snapshot"), _L("Snapshot name"));
|
||||
if (wxString action_name = _L("Taking a configuration snapshot");
|
||||
check_and_save_current_preset_changes(action_name, _L("Some presets are modified and the unsaved changes will not be captured by the configuration snapshot."), false, true)) {
|
||||
wxTextEntryDialog dlg(nullptr, action_name, _L("Snapshot name"));
|
||||
UpdateDlgDarkUI(&dlg);
|
||||
|
||||
// set current normal font for dialog children,
|
||||
@ -1888,7 +1884,7 @@ void GUI_App::add_config_menu(wxMenuBar *menu)
|
||||
}
|
||||
break;
|
||||
case ConfigMenuSnapshots:
|
||||
if (check_and_save_current_preset_changes()) {
|
||||
if (check_and_save_current_preset_changes(_L("Loading a configuration snapshot"), "", false)) {
|
||||
std::string on_snapshot;
|
||||
if (Config::SnapshotDB::singleton().is_on_snapshot(*app_config))
|
||||
on_snapshot = app_config->get("on_snapshot");
|
||||
@ -1910,9 +1906,6 @@ void GUI_App::add_config_menu(wxMenuBar *menu)
|
||||
|
||||
// Load the currently selected preset into the GUI, update the preset selection box.
|
||||
load_current_presets();
|
||||
|
||||
// update config wizard in respect to the new config
|
||||
update_wizard_from_config();
|
||||
} catch (std::exception &ex) {
|
||||
GUI::show_error(nullptr, _L("Failed to activate configuration snapshot.") + "\n" + into_u8(ex.what()));
|
||||
}
|
||||
@ -2081,13 +2074,28 @@ std::vector<std::pair<unsigned int, std::string>> GUI_App::get_selected_presets(
|
||||
return ret;
|
||||
}
|
||||
|
||||
// This is called when closing the application, when loading a config file or when starting the config wizard
|
||||
// to notify the user whether he is aware that some preset changes will be lost.
|
||||
bool GUI_App::check_and_save_current_preset_changes(const wxString& header, const wxString& caption)
|
||||
// To notify the user whether he is aware that some preset changes will be lost,
|
||||
// UnsavedChangesDialog: "Discard / Save / Cancel"
|
||||
// This is called when:
|
||||
// - Close Application & Current project isn't saved
|
||||
// - Load Project & Current project isn't saved
|
||||
// - Undo / Redo with change of print technologie
|
||||
// - Loading snapshot
|
||||
// - Loading config_file/bundle
|
||||
// UnsavedChangesDialog: "Don't save / Save / Cancel"
|
||||
// This is called when:
|
||||
// - Exporting config_bundle
|
||||
// - Taking snapshot
|
||||
bool GUI_App::check_and_save_current_preset_changes(const wxString& caption, const wxString& header, bool remember_choice/* = true*/, bool dont_save_insted_of_discard/* = false*/)
|
||||
{
|
||||
if (/*this->plater()->model().objects.empty() && */has_current_preset_changes()) {
|
||||
UnsavedChangesDialog dlg(header, caption);
|
||||
if (wxGetApp().app_config->get("default_action_on_close_application") == "none" && dlg.ShowModal() == wxID_CANCEL)
|
||||
if (has_current_preset_changes()) {
|
||||
const std::string app_config_key = remember_choice ? "default_action_on_close_application" : "";
|
||||
int act_buttons = UnsavedChangesDialog::ActionButtons::SAVE;
|
||||
if (dont_save_insted_of_discard)
|
||||
act_buttons |= UnsavedChangesDialog::ActionButtons::DONT_SAVE;
|
||||
UnsavedChangesDialog dlg(caption, header, app_config_key, act_buttons);
|
||||
std::string act = app_config_key.empty() ? "none" : wxGetApp().app_config->get(app_config_key);
|
||||
if (act == "none" && dlg.ShowModal() == wxID_CANCEL)
|
||||
return false;
|
||||
|
||||
if (dlg.save_preset()) // save selected changes
|
||||
@ -2095,18 +2103,122 @@ bool GUI_App::check_and_save_current_preset_changes(const wxString& header, cons
|
||||
for (const std::pair<std::string, Preset::Type>& nt : dlg.get_names_and_types())
|
||||
preset_bundle->save_changes_for_preset(nt.first, nt.second, dlg.get_unselected_options(nt.second));
|
||||
|
||||
load_current_presets(false);
|
||||
|
||||
// if we saved changes to the new presets, we should to
|
||||
// synchronize config.ini with the current selections.
|
||||
preset_bundle->export_selections(*app_config);
|
||||
|
||||
wxMessageBox(_L_PLURAL("The preset modifications are successfully saved",
|
||||
"The presets modifications are successfully saved", dlg.get_names_and_types().size()));
|
||||
MessageDialog(nullptr, _L_PLURAL("The preset modifications are successfully saved",
|
||||
"The presets modifications are successfully saved", dlg.get_names_and_types().size())).ShowModal();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GUI_App::apply_keeped_preset_modifications()
|
||||
{
|
||||
PrinterTechnology printer_technology = preset_bundle->printers.get_edited_preset().printer_technology();
|
||||
for (Tab* tab : tabs_list) {
|
||||
if (tab->supports_printer_technology(printer_technology))
|
||||
tab->apply_config_from_cache();
|
||||
}
|
||||
load_current_presets(false);
|
||||
}
|
||||
|
||||
// This is called when creating new project or load another project
|
||||
// OR close ConfigWizard
|
||||
// to ask the user what should we do with unsaved changes for presets.
|
||||
// New Project => Current project is saved => UnsavedChangesDialog: "Keep / Discard / Cancel"
|
||||
// => Current project isn't saved => UnsavedChangesDialog: "Keep / Discard / Save / Cancel"
|
||||
// Close ConfigWizard => Current project is saved => UnsavedChangesDialog: "Keep / Discard / Save / Cancel"
|
||||
// Note: no_nullptr postponed_apply_of_keeped_changes indicates that thie function is called after ConfigWizard is closed
|
||||
bool GUI_App::check_and_keep_current_preset_changes(const wxString& caption, const wxString& header, int action_buttons, bool* postponed_apply_of_keeped_changes/* = nullptr*/)
|
||||
{
|
||||
if (has_current_preset_changes()) {
|
||||
bool is_called_from_configwizard = postponed_apply_of_keeped_changes != nullptr;
|
||||
|
||||
const std::string app_config_key = is_called_from_configwizard ? "" : "default_action_on_new_project";
|
||||
UnsavedChangesDialog dlg(caption, header, app_config_key, action_buttons);
|
||||
std::string act = app_config_key.empty() ? "none" : wxGetApp().app_config->get(app_config_key);
|
||||
if (act == "none" && dlg.ShowModal() == wxID_CANCEL)
|
||||
return false;
|
||||
|
||||
auto reset_modifications = [this, is_called_from_configwizard]() {
|
||||
if (is_called_from_configwizard)
|
||||
return; // no need to discared changes. It will be done fromConfigWizard closing
|
||||
|
||||
PrinterTechnology printer_technology = preset_bundle->printers.get_edited_preset().printer_technology();
|
||||
for (const Tab* const tab : tabs_list) {
|
||||
if (tab->supports_printer_technology(printer_technology) && tab->current_preset_is_dirty())
|
||||
tab->m_presets->discard_current_changes();
|
||||
}
|
||||
load_current_presets(false);
|
||||
};
|
||||
|
||||
if (dlg.discard())
|
||||
reset_modifications();
|
||||
else // save selected changes
|
||||
{
|
||||
const auto& preset_names_and_types = dlg.get_names_and_types();
|
||||
if (dlg.save_preset()) {
|
||||
for (const std::pair<std::string, Preset::Type>& nt : preset_names_and_types)
|
||||
preset_bundle->save_changes_for_preset(nt.first, nt.second, dlg.get_unselected_options(nt.second));
|
||||
|
||||
// if we saved changes to the new presets, we should to
|
||||
// synchronize config.ini with the current selections.
|
||||
preset_bundle->export_selections(*app_config);
|
||||
|
||||
wxString text = _L_PLURAL("The preset modifications are successfully saved",
|
||||
"The presets modifications are successfully saved", preset_names_and_types.size());
|
||||
if (!is_called_from_configwizard)
|
||||
text += "\n\n" + _L("For new project all modifications will be reseted");
|
||||
|
||||
MessageDialog(nullptr, text).ShowModal();
|
||||
reset_modifications();
|
||||
}
|
||||
else if (dlg.transfer_changes() && (dlg.has_unselected_options() || is_called_from_configwizard)) {
|
||||
// execute this part of code only if not all modifications are keeping to the new project
|
||||
// OR this function is called when ConfigWizard is closed and "Keep modifications" is selected
|
||||
for (const std::pair<std::string, Preset::Type>& nt : preset_names_and_types) {
|
||||
Preset::Type type = nt.second;
|
||||
Tab* tab = get_tab(type);
|
||||
std::vector<std::string> selected_options = dlg.get_selected_options(type);
|
||||
if (type == Preset::TYPE_PRINTER) {
|
||||
auto it = std::find(selected_options.begin(), selected_options.end(), "extruders_count");
|
||||
if (it != selected_options.end()) {
|
||||
// erase "extruders_count" option from the list
|
||||
selected_options.erase(it);
|
||||
// cache the extruders count
|
||||
static_cast<TabPrinter*>(tab)->cache_extruder_cnt();
|
||||
}
|
||||
}
|
||||
tab->cache_config_diff(selected_options);
|
||||
if (!is_called_from_configwizard)
|
||||
tab->m_presets->discard_current_changes();
|
||||
}
|
||||
if (is_called_from_configwizard)
|
||||
*postponed_apply_of_keeped_changes = true;
|
||||
else
|
||||
apply_keeped_preset_modifications();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GUI_App::can_load_project()
|
||||
{
|
||||
int saved_project = plater()->save_project_if_dirty(_L("Loading a new project while the current project is modified."));
|
||||
if (saved_project == wxID_CANCEL ||
|
||||
(plater()->is_project_dirty() && saved_project == wxID_NO &&
|
||||
!check_and_save_current_preset_changes(_L("Project is loading"), _L("Loading a new project while some presets are modified."))))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GUI_App::check_print_host_queue()
|
||||
{
|
||||
wxString dirty;
|
||||
@ -2164,17 +2276,6 @@ void GUI_App::load_current_presets(bool check_printer_presets_ /*= true*/)
|
||||
}
|
||||
}
|
||||
|
||||
void GUI_App::update_wizard_from_config()
|
||||
{
|
||||
if (!m_wizard)
|
||||
return;
|
||||
// If ConfigWizard was created before changing of the configuration,
|
||||
// we have to destroy it to have possibility to create it again in respect to the new config's parameters
|
||||
m_wizard->Reparent(nullptr);
|
||||
m_wizard->Destroy();
|
||||
m_wizard = nullptr;
|
||||
}
|
||||
|
||||
bool GUI_App::OnExceptionInMainLoop()
|
||||
{
|
||||
generic_exception_handle();
|
||||
@ -2336,19 +2437,12 @@ bool GUI_App::run_wizard(ConfigWizard::RunReason reason, ConfigWizard::StartPage
|
||||
wxCHECK_MSG(mainframe != nullptr, false, "Internal error: Main frame not created / null");
|
||||
|
||||
if (reason == ConfigWizard::RR_USER) {
|
||||
wxString header = _L("Updates to Configuration Wizard may cause an another preset selection and lost of preset modification as a result.\n"
|
||||
"So, check unsaved changes and save them if necessary.") + "\n";
|
||||
if (!check_and_save_current_preset_changes(header, _L("ConfigWizard is opening")) ||
|
||||
preset_updater->config_update(app_config->orig_version(), PresetUpdater::UpdateParams::FORCED_BEFORE_WIZARD) == PresetUpdater::R_ALL_CANCELED)
|
||||
if (preset_updater->config_update(app_config->orig_version(), PresetUpdater::UpdateParams::FORCED_BEFORE_WIZARD) == PresetUpdater::R_ALL_CANCELED)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! m_wizard) {
|
||||
wxBusyCursor wait;
|
||||
m_wizard = new ConfigWizard(mainframe);
|
||||
}
|
||||
|
||||
const bool res = m_wizard->run(reason, start_page);
|
||||
auto wizard = new ConfigWizard(mainframe);
|
||||
const bool res = wizard->run(reason, start_page);
|
||||
|
||||
if (res) {
|
||||
load_current_presets();
|
||||
|
@ -150,7 +150,6 @@ private:
|
||||
|
||||
std::unique_ptr<ImGuiWrapper> m_imgui;
|
||||
std::unique_ptr<PrintHostJobQueue> m_printhost_job_queue;
|
||||
ConfigWizard* m_wizard; // Managed by wxWindow tree
|
||||
std::unique_ptr <OtherInstanceMessageHandler> m_other_instance_message_handler;
|
||||
std::unique_ptr <wxSingleInstanceChecker> m_single_instance_checker;
|
||||
std::string m_instance_hash_string;
|
||||
@ -246,11 +245,13 @@ public:
|
||||
bool has_current_preset_changes() const;
|
||||
void update_saved_preset_from_current_preset();
|
||||
std::vector<std::pair<unsigned int, std::string>> get_selected_presets() const;
|
||||
bool check_and_save_current_preset_changes(const wxString& header = wxString(), const wxString& caption = wxString());
|
||||
bool check_and_save_current_preset_changes(const wxString& caption, const wxString& header, bool remember_choice = true, bool use_dont_save_insted_of_discard = false);
|
||||
void apply_keeped_preset_modifications();
|
||||
bool check_and_keep_current_preset_changes(const wxString& caption, const wxString& header, int action_buttons, bool* postponed_apply_of_keeped_changes = nullptr);
|
||||
bool can_load_project();
|
||||
bool check_print_host_queue();
|
||||
bool checked_tab(Tab* tab);
|
||||
void load_current_presets(bool check_printer_presets = true);
|
||||
void update_wizard_from_config();
|
||||
|
||||
wxString current_language_code() const { return m_wxLocale->GetCanonicalName(); }
|
||||
// Translate the language code to a code, for which Prusa Research maintains translations. Defaults to "en_US".
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "BitmapComboBox.hpp"
|
||||
#include "GalleryDialog.hpp"
|
||||
#include "MainFrame.hpp"
|
||||
#include "slic3r/Utils/UndoRedo.hpp"
|
||||
|
||||
#include "OptionsGroup.hpp"
|
||||
#include "Tab.hpp"
|
||||
@ -382,12 +383,17 @@ int ObjectList::get_mesh_errors_count(const int obj_idx, const int vol_idx /*= -
|
||||
return (*m_objects)[obj_idx]->get_mesh_errors_count(vol_idx);
|
||||
}
|
||||
|
||||
wxString ObjectList::get_mesh_errors_list(const int obj_idx, const int vol_idx /*= -1*/) const
|
||||
static std::string get_warning_icon_name(const TriangleMeshStats& stats)
|
||||
{
|
||||
return stats.repaired() ? (stats.manifold() ? "exclamation_manifold" : "exclamation") : "";
|
||||
}
|
||||
|
||||
std::pair<wxString, std::string> ObjectList::get_mesh_errors(const int obj_idx, const int vol_idx /*= -1*/, bool from_plater /*= false*/) const
|
||||
{
|
||||
const int errors = get_mesh_errors_count(obj_idx, vol_idx);
|
||||
|
||||
if (errors == 0)
|
||||
return ""; // hide tooltip
|
||||
return { "", "" }; // hide tooltip
|
||||
|
||||
// Create tooltip string, if there are errors
|
||||
wxString tooltip = format_wxstr(_L_PLURAL("Auto-repaired %1$d error", "Auto-repaired %1$d errors", errors), errors) + ":\n";
|
||||
@ -407,21 +413,26 @@ wxString ObjectList::get_mesh_errors_list(const int obj_idx, const int vol_idx /
|
||||
if (stats.backwards_edges > 0)
|
||||
tooltip += "\t" + format_wxstr(_L_PLURAL("%1$d backwards edge", "%1$d backwards edges", stats.backwards_edges), stats.backwards_edges) + "\n";
|
||||
|
||||
if (is_windows10())
|
||||
tooltip += _L("Right button click the icon to fix STL through Netfabb");
|
||||
if (!stats.manifold()) {
|
||||
tooltip += _L("Remaning errors") + ":\n";
|
||||
tooltip += "\t" + format_wxstr(_L_PLURAL("%1$d open edge", "%1$d open edges", stats.open_edges), stats.open_edges) + "\n";
|
||||
}
|
||||
|
||||
return tooltip;
|
||||
if (is_windows10() && !from_plater)
|
||||
tooltip += "\n" + _L("Right button click the icon to fix STL through Netfabb");
|
||||
|
||||
return { tooltip, get_warning_icon_name(stats) };
|
||||
}
|
||||
|
||||
wxString ObjectList::get_mesh_errors_list()
|
||||
std::pair<wxString, std::string> ObjectList::get_mesh_errors(bool from_plater /*= false*/)
|
||||
{
|
||||
if (!GetSelection())
|
||||
return "";
|
||||
return { "", "" };
|
||||
|
||||
int obj_idx, vol_idx;
|
||||
get_selected_item_indexes(obj_idx, vol_idx);
|
||||
|
||||
return get_mesh_errors_list(obj_idx, vol_idx);
|
||||
return get_mesh_errors(obj_idx, vol_idx, from_plater);
|
||||
}
|
||||
|
||||
void ObjectList::set_tooltip_for_item(const wxPoint& pt)
|
||||
@ -459,7 +470,7 @@ void ObjectList::set_tooltip_for_item(const wxPoint& pt)
|
||||
{
|
||||
int obj_idx, vol_idx;
|
||||
get_selected_item_indexes(obj_idx, vol_idx, item);
|
||||
tooltip = get_mesh_errors_list(obj_idx, vol_idx);
|
||||
tooltip = get_mesh_errors(obj_idx, vol_idx).first;
|
||||
}
|
||||
|
||||
GetMainWindow()->SetToolTip(tooltip);
|
||||
@ -1775,8 +1786,12 @@ void ObjectList::del_subobject_item(wxDataViewItem& item)
|
||||
return;
|
||||
|
||||
// If last volume item with warning was deleted, unmark object item
|
||||
if (type & itVolume && (*m_objects)[obj_idx]->get_mesh_errors_count() == 0)
|
||||
m_objects_model->DeleteWarningIcon(parent);
|
||||
if (type & itVolume) {
|
||||
if (auto obj = object(obj_idx); obj->get_mesh_errors_count() == 0)
|
||||
m_objects_model->DeleteWarningIcon(parent);
|
||||
else
|
||||
m_objects_model->AddWarningIcon(parent, get_warning_icon_name(obj->mesh().stats()));
|
||||
}
|
||||
|
||||
m_objects_model->Delete(item);
|
||||
update_info_items(obj_idx);
|
||||
@ -1985,7 +2000,7 @@ void ObjectList::split()
|
||||
for (const ModelVolume* volume : model_object->volumes) {
|
||||
const wxDataViewItem& vol_item = m_objects_model->AddVolumeChild(parent, from_u8(volume->name),
|
||||
volume->type(),// is_modifier() ? ModelVolumeType::PARAMETER_MODIFIER : ModelVolumeType::MODEL_PART,
|
||||
volume->get_mesh_errors_count()>0,
|
||||
get_warning_icon_name(volume->mesh().stats()),
|
||||
volume->config.has("extruder") ? volume->config.extruder() : 0,
|
||||
false);
|
||||
// add settings to the part, if it has those
|
||||
@ -2484,7 +2499,7 @@ void ObjectList::part_selection_changed()
|
||||
if (item) {
|
||||
// wxGetApp().obj_manipul()->get_og()->set_value("object_name", m_objects_model->GetName(item));
|
||||
wxGetApp().obj_manipul()->update_item_name(m_objects_model->GetName(item));
|
||||
wxGetApp().obj_manipul()->update_warning_icon_state(get_mesh_errors_list(obj_idx, volume_id));
|
||||
wxGetApp().obj_manipul()->update_warning_icon_state(get_mesh_errors(obj_idx, volume_id));
|
||||
}
|
||||
}
|
||||
|
||||
@ -2627,7 +2642,7 @@ void ObjectList::add_object_to_list(size_t obj_idx, bool call_selection_changed)
|
||||
const wxString& item_name = from_u8(model_object->name);
|
||||
const auto item = m_objects_model->Add(item_name,
|
||||
model_object->config.has("extruder") ? model_object->config.extruder() : 0,
|
||||
get_mesh_errors_count(obj_idx) > 0);
|
||||
get_warning_icon_name(model_object->mesh().stats()));
|
||||
|
||||
update_info_items(obj_idx, nullptr, call_selection_changed);
|
||||
|
||||
@ -2637,7 +2652,7 @@ void ObjectList::add_object_to_list(size_t obj_idx, bool call_selection_changed)
|
||||
const wxDataViewItem& vol_item = m_objects_model->AddVolumeChild(item,
|
||||
from_u8(volume->name),
|
||||
volume->type(),
|
||||
volume->get_mesh_errors_count()>0,
|
||||
get_warning_icon_name(volume->mesh().stats()),
|
||||
volume->config.has("extruder") ? volume->config.extruder() : 0,
|
||||
false);
|
||||
add_settings_item(vol_item, &volume->config.get());
|
||||
@ -2746,6 +2761,8 @@ void ObjectList::delete_from_model_and_list(const std::vector<ItemForDelete>& it
|
||||
// If last volume item with warning was deleted, unmark object item
|
||||
if (obj->get_mesh_errors_count() == 0)
|
||||
m_objects_model->DeleteWarningIcon(parent);
|
||||
else
|
||||
m_objects_model->AddWarningIcon(parent, get_warning_icon_name(obj->mesh().stats()));
|
||||
}
|
||||
wxGetApp().plater()->canvas3D()->ensure_on_bed(item->obj_idx, printer_technology() != ptSLA);
|
||||
}
|
||||
@ -3453,7 +3470,7 @@ void ObjectList::update_selections_on_canvas()
|
||||
volume_idxs = selection.get_missing_volume_idxs_from(volume_idxs);
|
||||
if (volume_idxs.size() > 0)
|
||||
{
|
||||
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Selection-Remove from list")));
|
||||
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Selection-Remove from list")), UndoRedo::SnapshotType::Selection);
|
||||
selection.remove_volumes(mode, volume_idxs);
|
||||
}
|
||||
}
|
||||
@ -3465,7 +3482,7 @@ void ObjectList::update_selections_on_canvas()
|
||||
// OR there is no single selection
|
||||
if (selection.get_mode() == mode || !single_selection)
|
||||
volume_idxs = selection.get_unselected_volume_idxs_from(volume_idxs);
|
||||
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Selection-Add from list")));
|
||||
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Selection-Add from list")), UndoRedo::SnapshotType::Selection);
|
||||
selection.add_volumes(mode, volume_idxs, single_selection);
|
||||
}
|
||||
|
||||
@ -4055,7 +4072,7 @@ void ObjectList::fix_through_netfabb()
|
||||
msg += ": " + from_u8(model_name) + "\n";
|
||||
else {
|
||||
msg += ":\n";
|
||||
for (size_t i = 0; i < model_names.size(); ++i)
|
||||
for (int i = 0; i < int(model_names.size()); ++i)
|
||||
msg += (i == model_idx ? " > " : " ") + from_u8(model_names[i]) + "\n";
|
||||
msg += "\n";
|
||||
}
|
||||
@ -4162,8 +4179,10 @@ void ObjectList::update_item_error_icon(const int obj_idx, const int vol_idx) co
|
||||
// unmark fixed item only
|
||||
m_objects_model->DeleteWarningIcon(item);
|
||||
}
|
||||
else
|
||||
m_objects_model->AddWarningIcon(item);
|
||||
else {
|
||||
auto obj = object(obj_idx);
|
||||
m_objects_model->AddWarningIcon(item, get_warning_icon_name(vol_idx < 0 ? obj->mesh().stats() : obj->volumes[vol_idx]->mesh().stats()));
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectList::msw_rescale()
|
||||
@ -4294,7 +4313,7 @@ wxDataViewItemArray ObjectList::reorder_volumes_and_get_selection(int obj_idx, s
|
||||
for (const ModelVolume* volume : object->volumes) {
|
||||
wxDataViewItem vol_item = m_objects_model->AddVolumeChild(object_item, from_u8(volume->name),
|
||||
volume->type(),
|
||||
volume->get_mesh_errors_count() > 0,
|
||||
get_warning_icon_name(volume->mesh().stats()),
|
||||
volume->config.has("extruder") ? volume->config.extruder() : 0,
|
||||
false);
|
||||
// add settings to the part, if it has those
|
||||
|
@ -210,12 +210,12 @@ public:
|
||||
void get_selection_indexes(std::vector<int>& obj_idxs, std::vector<int>& vol_idxs);
|
||||
// Get count of errors in the mesh
|
||||
int get_mesh_errors_count(const int obj_idx, const int vol_idx = -1) const;
|
||||
/* Get list of errors in the mesh. Return value is a string, used for the tooltip
|
||||
* Function without parameters is for a call from Manipulation panel,
|
||||
* when we don't know parameters of selected item
|
||||
*/
|
||||
wxString get_mesh_errors_list(const int obj_idx, const int vol_idx = -1) const;
|
||||
wxString get_mesh_errors_list();
|
||||
// Get list of errors in the mesh and name of the warning icon
|
||||
// Return value is a pair <Tooltip, warning_icon_name>, used for the tooltip and related warning icon
|
||||
// Function without parameters is for a call from Manipulation panel,
|
||||
// when we don't know parameters of selected item
|
||||
std::pair<wxString, std::string> get_mesh_errors(const int obj_idx, const int vol_idx = -1, bool from_plater = false) const;
|
||||
std::pair<wxString, std::string> get_mesh_errors(bool from_plater = false);
|
||||
void set_tooltip_for_item(const wxPoint& pt);
|
||||
|
||||
void selection_changed();
|
||||
|
@ -132,7 +132,7 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
|
||||
return;
|
||||
|
||||
wxGetApp().obj_list()->fix_through_netfabb();
|
||||
update_warning_icon_state(wxGetApp().obj_list()->get_mesh_errors_list());
|
||||
update_warning_icon_state(wxGetApp().obj_list()->get_mesh_errors());
|
||||
});
|
||||
|
||||
sizer->Add(m_fix_throught_netfab_bitmap);
|
||||
@ -776,8 +776,12 @@ void ObjectManipulation::update_item_name(const wxString& item_name)
|
||||
m_item_name->SetLabel(item_name);
|
||||
}
|
||||
|
||||
void ObjectManipulation::update_warning_icon_state(const wxString& tooltip)
|
||||
{
|
||||
void ObjectManipulation::update_warning_icon_state(const std::pair<wxString, std::string>& warning)
|
||||
{
|
||||
if (const std::string& warning_icon_name = warning.second;
|
||||
!warning_icon_name.empty())
|
||||
m_manifold_warning_bmp = ScalableBitmap(m_parent, warning_icon_name);
|
||||
const wxString& tooltip = warning.first;
|
||||
m_fix_throught_netfab_bitmap->SetBitmap(tooltip.IsEmpty() ? wxNullBitmap : m_manifold_warning_bmp.bmp());
|
||||
m_fix_throught_netfab_bitmap->SetMinSize(tooltip.IsEmpty() ? wxSize(0,0) : m_manifold_warning_bmp.bmp().GetSize());
|
||||
m_fix_throught_netfab_bitmap->SetToolTip(tooltip);
|
||||
|
@ -194,7 +194,7 @@ public:
|
||||
#endif // __APPLE__
|
||||
|
||||
void update_item_name(const wxString &item_name);
|
||||
void update_warning_icon_state(const wxString& tooltip);
|
||||
void update_warning_icon_state(const std::pair<wxString, std::string>& warning);
|
||||
void msw_rescale();
|
||||
void sys_color_changed();
|
||||
void on_change(const std::string& opt_key, int axis, double new_value);
|
||||
|
@ -237,9 +237,8 @@ void GLGizmoBase::render_input_window(float x, float y, float bottom_limit)
|
||||
std::string GLGizmoBase::get_name(bool include_shortcut) const
|
||||
{
|
||||
int key = get_shortcut_key();
|
||||
assert( key >= WXK_CONTROL_A && key <= WXK_CONTROL_Z);
|
||||
std::string out = on_get_name();
|
||||
if (include_shortcut)
|
||||
if (include_shortcut && key >= WXK_CONTROL_A && key <= WXK_CONTROL_Z)
|
||||
out += std::string(" [") + char(int('A') + key - int(WXK_CONTROL_A)) + "]";
|
||||
return out;
|
||||
}
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
#include "slic3r/GUI/Camera.hpp"
|
||||
#include "slic3r/GUI/Plater.hpp"
|
||||
#include "slic3r/Utils/UndoRedo.hpp"
|
||||
#include "libslic3r/Model.hpp"
|
||||
#include "libslic3r/PresetBundle.hpp"
|
||||
#include "libslic3r/TriangleMesh.hpp"
|
||||
@ -42,14 +43,14 @@ void GLGizmoPainterBase::activate_internal_undo_redo_stack(bool activate)
|
||||
|
||||
if (activate && !m_internal_stack_active) {
|
||||
if (std::string str = this->get_gizmo_entering_text(); last_snapshot_name != str)
|
||||
Plater::TakeSnapshot(plater, str);
|
||||
Plater::TakeSnapshot(plater, str, UndoRedo::SnapshotType::EnteringGizmo);
|
||||
plater->enter_gizmos_stack();
|
||||
m_internal_stack_active = true;
|
||||
}
|
||||
if (!activate && m_internal_stack_active) {
|
||||
plater->leave_gizmos_stack();
|
||||
if (std::string str = this->get_gizmo_leaving_text(); last_snapshot_name != str)
|
||||
Plater::TakeSnapshot(plater, str);
|
||||
Plater::TakeSnapshot(plater, str, UndoRedo::SnapshotType::LeavingGizmoWithAction);
|
||||
m_internal_stack_active = false;
|
||||
}
|
||||
}
|
||||
|
@ -53,11 +53,12 @@ void GLGizmoSimplify::on_render_for_picking() {}
|
||||
void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limit)
|
||||
{
|
||||
create_gui_cfg();
|
||||
const Selection &selection = m_parent.get_selection();
|
||||
int object_idx = selection.get_object_idx();
|
||||
if (!is_selected_object(&object_idx)) return;
|
||||
ModelObject *obj = wxGetApp().plater()->model().objects[object_idx];
|
||||
ModelVolume *act_volume = obj->volumes.front();
|
||||
int obj_index;
|
||||
ModelVolume *act_volume = get_selected_volume(&obj_index);
|
||||
if (act_volume == nullptr) {
|
||||
close();
|
||||
return;
|
||||
}
|
||||
|
||||
// Check selection of new volume
|
||||
// Do not reselect object when processing
|
||||
@ -68,7 +69,7 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi
|
||||
set_its(*m_original_its);
|
||||
}
|
||||
|
||||
m_obj_index = object_idx; // to remember correct object
|
||||
m_obj_index = obj_index; // to remember correct object
|
||||
m_volume = act_volume;
|
||||
m_original_its = {};
|
||||
m_configuration.decimate_ratio = 50.; // default value
|
||||
@ -329,9 +330,10 @@ void GLGizmoSimplify::on_set_state()
|
||||
{
|
||||
// Closing gizmo. e.g. selecting another one
|
||||
if (GLGizmoBase::m_state == GLGizmoBase::Off) {
|
||||
bool exist_selected_object = is_selected_object();
|
||||
// refuse outgoing during simlification
|
||||
// object is not selected when it is deleted(cancel and close gizmo)
|
||||
if (m_state != State::settings && is_selected_object()) {
|
||||
if (m_state != State::settings && exist_selected_object) {
|
||||
GLGizmoBase::m_state = GLGizmoBase::On;
|
||||
auto notification_manager = wxGetApp().plater()->get_notification_manager();
|
||||
notification_manager->push_notification(
|
||||
@ -342,7 +344,7 @@ void GLGizmoSimplify::on_set_state()
|
||||
}
|
||||
|
||||
// revert preview
|
||||
if (m_exist_preview) {
|
||||
if (m_exist_preview && exist_selected_object) {
|
||||
set_its(*m_original_its);
|
||||
m_parent.reload_scene(true);
|
||||
m_need_reload = false;
|
||||
@ -399,4 +401,17 @@ bool GLGizmoSimplify::is_selected_object(int *object_idx)
|
||||
return true;
|
||||
}
|
||||
|
||||
ModelVolume *GLGizmoSimplify::get_selected_volume(int *object_idx_ptr)
|
||||
{
|
||||
const Selection &selection = m_parent.get_selection();
|
||||
int object_idx = selection.get_object_idx();
|
||||
if (object_idx_ptr != nullptr) *object_idx_ptr = object_idx;
|
||||
if (!is_selected_object(&object_idx)) return nullptr;
|
||||
ModelObjectPtrs &objs = wxGetApp().plater()->model().objects;
|
||||
if (objs.size() <= object_idx) return nullptr;
|
||||
ModelObject *obj = objs[object_idx];
|
||||
if (obj->volumes.empty()) return nullptr;
|
||||
return obj->volumes.front();
|
||||
}
|
||||
|
||||
} // namespace Slic3r::GUI
|
||||
|
@ -38,13 +38,14 @@ private:
|
||||
void set_its(indexed_triangle_set &its);
|
||||
void create_gui_cfg();
|
||||
void request_rerender();
|
||||
bool is_selected_object(int *object_idx = nullptr);
|
||||
bool is_selected_object(int *object_idx_ptr = nullptr);
|
||||
ModelVolume *get_selected_volume(int *object_idx = nullptr);
|
||||
|
||||
std::atomic_bool m_is_valid_result; // differ what to do in apply
|
||||
std::atomic_bool m_exist_preview; // set when process end
|
||||
|
||||
volatile int m_progress; // percent of done work
|
||||
ModelVolume *m_volume; //
|
||||
ModelVolume *m_volume; // keep pointer to actual working volume
|
||||
size_t m_obj_index;
|
||||
|
||||
std::optional<indexed_triangle_set> m_original_its;
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include "slic3r/GUI/Camera.hpp"
|
||||
#include "slic3r/GUI/Gizmos/GLGizmosCommon.hpp"
|
||||
#include "slic3r/GUI/MainFrame.hpp"
|
||||
#include "slic3r/Utils/UndoRedo.hpp"
|
||||
|
||||
#include <GL/glew.h>
|
||||
|
||||
|
@ -352,10 +352,16 @@ bool instance_check(int argc, char** argv, bool app_config_single_instance)
|
||||
if (instance_check_internal::get_lock(lock_name + ".lock", data_dir() + "/cache/") && *cla.should_send) {
|
||||
#endif
|
||||
instance_check_internal::send_message(cla.cl_string, lock_name);
|
||||
BOOST_LOG_TRIVIAL(info) << "instance check: Another instance found. This instance will terminate.";
|
||||
BOOST_LOG_TRIVIAL(error) << "Instance check: Another instance found. This instance will terminate. Lock file of current running instance is located at " << data_dir() <<
|
||||
#ifdef _WIN32
|
||||
"\\cache\\"
|
||||
#else // mac & linx
|
||||
"/cache/"
|
||||
#endif
|
||||
<< lock_name << ".lock";
|
||||
return true;
|
||||
}
|
||||
BOOST_LOG_TRIVIAL(info) << "instance check: Another instance not found or single-instance not set.";
|
||||
BOOST_LOG_TRIVIAL(info) << "Instance check: Another instance not found or single-instance not set.";
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
#include "slic3r/GUI/Plater.hpp"
|
||||
#include "slic3r/GUI/GUI_ObjectList.hpp"
|
||||
#include "slic3r/GUI/NotificationManager.hpp"
|
||||
|
||||
#include "libslic3r/Model.hpp"
|
||||
#include "libslic3r/PresetBundle.hpp"
|
||||
@ -144,16 +145,16 @@ void SLAImportJob::process()
|
||||
try {
|
||||
switch (p->sel) {
|
||||
case Sel::modelAndProfile:
|
||||
p->config_substitutions = import_sla_archive(path, p->win, p->mesh, p->profile, progr);
|
||||
break;
|
||||
case Sel::modelOnly:
|
||||
p->config_substitutions = import_sla_archive(path, p->win, p->mesh, progr);
|
||||
p->config_substitutions = import_sla_archive(path, p->win, p->mesh, p->profile, progr);
|
||||
break;
|
||||
case Sel::profileOnly:
|
||||
p->config_substitutions = import_sla_archive(path, p->profile);
|
||||
break;
|
||||
}
|
||||
|
||||
} catch (MissingProfileError &) {
|
||||
p->err = _L("The archive doesn't contain any profile data. Try to import after switching "
|
||||
"to an SLA profile that can be used as fallback.").ToStdString();
|
||||
} catch (std::exception &ex) {
|
||||
p->err = ex.what();
|
||||
}
|
||||
@ -166,7 +167,7 @@ void SLAImportJob::reset()
|
||||
{
|
||||
p->sel = Sel::modelAndProfile;
|
||||
p->mesh = {};
|
||||
p->profile = {};
|
||||
p->profile = m_plater->sla_print().full_print_config();
|
||||
p->win = {2, 2};
|
||||
p->path.Clear();
|
||||
}
|
||||
@ -202,7 +203,18 @@ void SLAImportJob::finalize()
|
||||
|
||||
std::string name = wxFileName(p->path).GetName().ToUTF8().data();
|
||||
|
||||
if (!p->profile.empty()) {
|
||||
if (p->profile.empty()) {
|
||||
m_plater->get_notification_manager()->push_notification(
|
||||
NotificationType::CustomNotification,
|
||||
NotificationManager::NotificationLevel::WarningNotificationLevel,
|
||||
_L("Loaded archive did not contain any profile data. "
|
||||
"The current SLA profile was used as fallback.").ToStdString());
|
||||
}
|
||||
|
||||
if (p->sel != Sel::modelOnly) {
|
||||
if (p->profile.empty())
|
||||
p->profile = m_plater->sla_print().full_print_config();
|
||||
|
||||
const ModelObjectPtrs& objects = p->plater->model().objects;
|
||||
for (auto object : objects)
|
||||
if (object->volumes.size() > 1)
|
||||
|
@ -222,13 +222,14 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S
|
||||
}
|
||||
|
||||
if (m_plater != nullptr) {
|
||||
int saved_project = m_plater->save_project_if_dirty();
|
||||
int saved_project = m_plater->save_project_if_dirty(_L("Closing PrusaSlicer. Current project is modified."));
|
||||
if (saved_project == wxID_CANCEL) {
|
||||
event.Veto();
|
||||
return;
|
||||
}
|
||||
// check unsaved changes only if project wasn't saved
|
||||
else if (saved_project == wxID_NO && event.CanVeto() && !wxGetApp().check_and_save_current_preset_changes()) {
|
||||
else if (saved_project == wxID_NO && event.CanVeto() &&
|
||||
!wxGetApp().check_and_save_current_preset_changes(_L("PrusaSlicer is closing"), _L("Closing PrusaSlicer while some presets are modified."))) {
|
||||
event.Veto();
|
||||
return;
|
||||
}
|
||||
@ -434,8 +435,9 @@ void MainFrame::update_layout()
|
||||
{
|
||||
m_plater->Reparent(m_tabpanel);
|
||||
#ifdef _MSW_DARK_MODE
|
||||
m_plater->Layout();
|
||||
if (!wxGetApp().tabs_as_menu())
|
||||
dynamic_cast<Notebook*>(m_tabpanel)->InsertPage(0, m_plater, _L("Plater"), std::string("plater"));
|
||||
dynamic_cast<Notebook*>(m_tabpanel)->InsertPage(0, m_plater, _L("Plater"), std::string("plater"), true);
|
||||
else
|
||||
#endif
|
||||
m_tabpanel->InsertPage(0, m_plater, _L("Plater"));
|
||||
@ -460,7 +462,7 @@ void MainFrame::update_layout()
|
||||
m_plater_page = new wxPanel(m_tabpanel);
|
||||
#ifdef _MSW_DARK_MODE
|
||||
if (!wxGetApp().tabs_as_menu())
|
||||
dynamic_cast<Notebook*>(m_tabpanel)->InsertPage(0, m_plater_page, _L("Plater"), std::string("plater"));
|
||||
dynamic_cast<Notebook*>(m_tabpanel)->InsertPage(0, m_plater_page, _L("Plater"), std::string("plater"), true);
|
||||
else
|
||||
#endif
|
||||
m_tabpanel->InsertPage(0, m_plater_page, _L("Plater")); // empty panel just for Plater tab */
|
||||
@ -820,7 +822,10 @@ bool MainFrame::is_active_and_shown_tab(Tab* tab)
|
||||
|
||||
bool MainFrame::can_start_new_project() const
|
||||
{
|
||||
return (m_plater != nullptr) && (!m_plater->get_project_filename(".3mf").IsEmpty() || GetTitle().StartsWith('*') || !m_plater->model().objects.empty());
|
||||
return m_plater && (!m_plater->get_project_filename(".3mf").IsEmpty() ||
|
||||
GetTitle().StartsWith('*')||
|
||||
wxGetApp().has_current_preset_changes() ||
|
||||
!m_plater->model().objects.empty() );
|
||||
}
|
||||
|
||||
bool MainFrame::can_save() const
|
||||
@ -852,13 +857,14 @@ void MainFrame::save_project()
|
||||
save_project_as(m_plater->get_project_filename(".3mf"));
|
||||
}
|
||||
|
||||
void MainFrame::save_project_as(const wxString& filename)
|
||||
bool MainFrame::save_project_as(const wxString& filename)
|
||||
{
|
||||
bool ret = (m_plater != nullptr) ? m_plater->export_3mf(into_path(filename)) : false;
|
||||
if (ret) {
|
||||
// wxGetApp().update_saved_preset_from_current_preset();
|
||||
m_plater->reset_project_dirty_after_save();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool MainFrame::can_export_model() const
|
||||
@ -1151,8 +1157,10 @@ void MainFrame::init_menubar_as_editor()
|
||||
Bind(wxEVT_MENU, [this](wxCommandEvent& evt) {
|
||||
size_t file_id = evt.GetId() - wxID_FILE1;
|
||||
wxString filename = m_recent_projects.GetHistoryFile(file_id);
|
||||
if (wxFileExists(filename))
|
||||
m_plater->load_project(filename);
|
||||
if (wxFileExists(filename)) {
|
||||
if (wxGetApp().can_load_project())
|
||||
m_plater->load_project(filename);
|
||||
}
|
||||
else
|
||||
{
|
||||
//wxMessageDialog msg(this, _L("The selected project is no longer available.\nDo you want to remove it from the recent projects list?"), _L("Error"), wxYES_NO | wxYES_DEFAULT);
|
||||
@ -1772,7 +1780,7 @@ void MainFrame::export_config()
|
||||
// Load a config file containing a Print, Filament & Printer preset.
|
||||
void MainFrame::load_config_file()
|
||||
{
|
||||
if (!wxGetApp().check_and_save_current_preset_changes())
|
||||
if (!wxGetApp().check_and_save_current_preset_changes(_L("Loading of a configuration file"), "", false))
|
||||
return;
|
||||
wxFileDialog dlg(this, _L("Select configuration to load:"),
|
||||
!m_last_config.IsEmpty() ? get_dir_name(m_last_config) : wxGetApp().app_config->get_last_dir(),
|
||||
@ -1803,7 +1811,8 @@ bool MainFrame::load_config_file(const std::string &path)
|
||||
|
||||
void MainFrame::export_configbundle(bool export_physical_printers /*= false*/)
|
||||
{
|
||||
if (!wxGetApp().check_and_save_current_preset_changes())
|
||||
if (!wxGetApp().check_and_save_current_preset_changes(_L("Exporting configuration bundle"),
|
||||
_L("Some presets are modified and the unsaved changes will not be exported into configuration bundle."), false, true))
|
||||
return;
|
||||
// validate current configuration in case it's dirty
|
||||
auto err = wxGetApp().preset_bundle->full_config().validate();
|
||||
@ -1835,7 +1844,7 @@ void MainFrame::export_configbundle(bool export_physical_printers /*= false*/)
|
||||
// but that behavior was not documented and likely buggy.
|
||||
void MainFrame::load_configbundle(wxString file/* = wxEmptyString, const bool reset_user_profile*/)
|
||||
{
|
||||
if (!wxGetApp().check_and_save_current_preset_changes())
|
||||
if (!wxGetApp().check_and_save_current_preset_changes(_L("Loading of a configuration bundle"), "", false))
|
||||
return;
|
||||
if (file.IsEmpty()) {
|
||||
wxFileDialog dlg(this, _L("Select configuration to load:"),
|
||||
|
@ -190,7 +190,7 @@ public:
|
||||
bool can_save() const;
|
||||
bool can_save_as() const;
|
||||
void save_project();
|
||||
void save_project_as(const wxString& filename = wxString());
|
||||
bool save_project_as(const wxString& filename = wxString());
|
||||
|
||||
void add_to_recent_projects(const wxString& filename);
|
||||
void technology_changed();
|
||||
|
@ -253,11 +253,10 @@ std::vector<unsigned> MeshRaycaster::get_unobscured_idxs(const Geometry::Transfo
|
||||
std::vector<unsigned> out;
|
||||
|
||||
const Transform3d& instance_matrix_no_translation_no_scaling = trafo.get_matrix(true,false,true);
|
||||
Vec3f direction_to_camera = -camera.get_dir_forward().cast<float>();
|
||||
Vec3f direction_to_camera_mesh = (instance_matrix_no_translation_no_scaling.inverse().cast<float>() * direction_to_camera).normalized().eval();
|
||||
Vec3f scaling = trafo.get_scaling_factor().cast<float>();
|
||||
direction_to_camera_mesh = Vec3f(direction_to_camera_mesh(0)*scaling(0), direction_to_camera_mesh(1)*scaling(1), direction_to_camera_mesh(2)*scaling(2));
|
||||
const Transform3f inverse_trafo = trafo.get_matrix().inverse().cast<float>();
|
||||
Vec3d direction_to_camera = -camera.get_dir_forward();
|
||||
Vec3d direction_to_camera_mesh = (instance_matrix_no_translation_no_scaling.inverse() * direction_to_camera).normalized().eval();
|
||||
direction_to_camera_mesh = direction_to_camera_mesh.cwiseProduct(trafo.get_scaling_factor());
|
||||
const Transform3d inverse_trafo = trafo.get_matrix().inverse();
|
||||
|
||||
for (size_t i=0; i<points.size(); ++i) {
|
||||
const Vec3f& pt = points[i];
|
||||
@ -268,9 +267,8 @@ std::vector<unsigned> MeshRaycaster::get_unobscured_idxs(const Geometry::Transfo
|
||||
// Cast a ray in the direction of the camera and look for intersection with the mesh:
|
||||
std::vector<sla::IndexedMesh::hit_result> hits;
|
||||
// Offset the start of the ray by EPSILON to account for numerical inaccuracies.
|
||||
hits = m_emesh.query_ray_hits((inverse_trafo * pt + direction_to_camera_mesh * EPSILON).cast<double>(),
|
||||
direction_to_camera.cast<double>());
|
||||
|
||||
hits = m_emesh.query_ray_hits((inverse_trafo * pt.cast<double>() + direction_to_camera_mesh * EPSILON),
|
||||
direction_to_camera_mesh);
|
||||
|
||||
if (! hits.empty()) {
|
||||
// If the closest hit facet normal points in the same direction as the ray,
|
||||
|
@ -84,6 +84,9 @@ public:
|
||||
if (int page_idx = evt.GetId(); page_idx >= 0)
|
||||
SetSelection(page_idx);
|
||||
});
|
||||
|
||||
this->Bind(wxEVT_NAVIGATION_KEY, &Notebook::OnNavigationKey, this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -165,8 +168,8 @@ public:
|
||||
|
||||
GetBtnsListCtrl()->InsertPage(n, text, bSelect, bmp_name);
|
||||
|
||||
if (!DoSetSelectionAfterInsertion(n, bSelect))
|
||||
page->Hide();
|
||||
if (bSelect)
|
||||
SetSelection(n);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -174,7 +177,14 @@ public:
|
||||
virtual int SetSelection(size_t n) override
|
||||
{
|
||||
GetBtnsListCtrl()->SetSelection(n);
|
||||
return DoSetSelection(n, SetSelection_SendEvent);
|
||||
int ret = DoSetSelection(n, SetSelection_SendEvent);
|
||||
|
||||
// check that only the selected page is visible and others are hidden:
|
||||
for (size_t page = 0; page < m_pages.size(); page++)
|
||||
if (page != n)
|
||||
m_pages[page]->Hide();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
virtual int ChangeSelection(size_t n) override
|
||||
@ -235,6 +245,89 @@ public:
|
||||
GetBtnsListCtrl()->Rescale();
|
||||
}
|
||||
|
||||
void Notebook::OnNavigationKey(wxNavigationKeyEvent& event)
|
||||
{
|
||||
if (event.IsWindowChange()) {
|
||||
// change pages
|
||||
AdvanceSelection(event.GetDirection());
|
||||
}
|
||||
else {
|
||||
// we get this event in 3 cases
|
||||
//
|
||||
// a) one of our pages might have generated it because the user TABbed
|
||||
// out from it in which case we should propagate the event upwards and
|
||||
// our parent will take care of setting the focus to prev/next sibling
|
||||
//
|
||||
// or
|
||||
//
|
||||
// b) the parent panel wants to give the focus to us so that we
|
||||
// forward it to our selected page. We can't deal with this in
|
||||
// OnSetFocus() because we don't know which direction the focus came
|
||||
// from in this case and so can't choose between setting the focus to
|
||||
// first or last panel child
|
||||
//
|
||||
// or
|
||||
//
|
||||
// c) we ourselves (see MSWTranslateMessage) generated the event
|
||||
//
|
||||
wxWindow* const parent = GetParent();
|
||||
|
||||
// the wxObject* casts are required to avoid MinGW GCC 2.95.3 ICE
|
||||
const bool isFromParent = event.GetEventObject() == (wxObject*)parent;
|
||||
const bool isFromSelf = event.GetEventObject() == (wxObject*)this;
|
||||
const bool isForward = event.GetDirection();
|
||||
|
||||
if (isFromSelf && !isForward)
|
||||
{
|
||||
// focus is currently on notebook tab and should leave
|
||||
// it backwards (Shift-TAB)
|
||||
event.SetCurrentFocus(this);
|
||||
parent->HandleWindowEvent(event);
|
||||
}
|
||||
else if (isFromParent || isFromSelf)
|
||||
{
|
||||
// no, it doesn't come from child, case (b) or (c): forward to a
|
||||
// page but only if entering notebook page (i.e. direction is
|
||||
// backwards (Shift-TAB) comething from out-of-notebook, or
|
||||
// direction is forward (TAB) from ourselves),
|
||||
if (m_selection != wxNOT_FOUND &&
|
||||
(!event.GetDirection() || isFromSelf))
|
||||
{
|
||||
// so that the page knows that the event comes from it's parent
|
||||
// and is being propagated downwards
|
||||
event.SetEventObject(this);
|
||||
|
||||
wxWindow* page = m_pages[m_selection];
|
||||
if (!page->HandleWindowEvent(event))
|
||||
{
|
||||
page->SetFocus();
|
||||
}
|
||||
//else: page manages focus inside it itself
|
||||
}
|
||||
else // otherwise set the focus to the notebook itself
|
||||
{
|
||||
SetFocus();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// it comes from our child, case (a), pass to the parent, but only
|
||||
// if the direction is forwards. Otherwise set the focus to the
|
||||
// notebook itself. The notebook is always the 'first' control of a
|
||||
// page.
|
||||
if (!isForward)
|
||||
{
|
||||
SetFocus();
|
||||
}
|
||||
else if (parent)
|
||||
{
|
||||
event.SetCurrentFocus(this);
|
||||
parent->HandleWindowEvent(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void UpdateSelectedPage(size_t WXUNUSED(newsel)) override
|
||||
{
|
||||
|
@ -593,7 +593,7 @@ bool NotificationManager::PopNotification::update_state(bool paused, const int64
|
||||
m_state = EState::Unknown;
|
||||
init();
|
||||
// Timers when not fading
|
||||
} else if (m_state != EState::NotFading && m_state != EState::FadingOut && get_duration() != 0 && !paused) {
|
||||
} else if (m_state != EState::NotFading && m_state != EState::FadingOut && m_state != EState::ClosePending && m_state != EState::Finished && get_duration() != 0 && !paused) {
|
||||
int64_t up_time = now - m_notification_start;
|
||||
if (up_time >= get_duration() * 1000) {
|
||||
m_state = EState::FadingOut;
|
||||
@ -633,6 +633,10 @@ bool NotificationManager::PopNotification::update_state(bool paused, const int64
|
||||
//---------------ExportFinishedNotification-----------
|
||||
void NotificationManager::ExportFinishedNotification::count_spaces()
|
||||
{
|
||||
if (m_eject_pending)
|
||||
{
|
||||
return PopNotification::count_spaces();
|
||||
}
|
||||
//determine line width
|
||||
m_line_height = ImGui::CalcTextSize("A").y;
|
||||
|
||||
@ -650,7 +654,10 @@ void NotificationManager::ExportFinishedNotification::count_spaces()
|
||||
|
||||
void NotificationManager::ExportFinishedNotification::render_text(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
|
||||
{
|
||||
|
||||
if (m_eject_pending)
|
||||
{
|
||||
return PopNotification::render_text(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
|
||||
}
|
||||
float x_offset = m_left_indentation;
|
||||
std::string fulltext = m_text1 + m_hypertext; //+ m_text2;
|
||||
// Lines are always at least two and m_multiline is always true for ExportFinishedNotification.
|
||||
@ -669,7 +676,7 @@ void NotificationManager::ExportFinishedNotification::render_text(ImGuiWrapper&
|
||||
ImGui::SetCursorPosY(starting_y + i * shift_y);
|
||||
imgui.text(line.c_str());
|
||||
//hyperlink text
|
||||
if ( i == 0 ) {
|
||||
if ( i == 0 && !m_eject_pending) {
|
||||
render_hypertext(imgui, x_offset + ImGui::CalcTextSize(line.c_str()).x + ImGui::CalcTextSize(" ").x, starting_y, _u8L("Open Folder."));
|
||||
}
|
||||
}
|
||||
@ -680,7 +687,7 @@ void NotificationManager::ExportFinishedNotification::render_text(ImGuiWrapper&
|
||||
void NotificationManager::ExportFinishedNotification::render_close_button(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
|
||||
{
|
||||
PopNotification::render_close_button(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
|
||||
if(m_to_removable)
|
||||
if(m_to_removable && ! m_eject_pending)
|
||||
render_eject_button(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
|
||||
}
|
||||
|
||||
@ -725,7 +732,7 @@ void NotificationManager::ExportFinishedNotification::render_eject_button(ImGuiW
|
||||
assert(m_evt_handler != nullptr);
|
||||
if (m_evt_handler != nullptr)
|
||||
wxPostEvent(m_evt_handler, EjectDriveNotificationClickedEvent(EVT_EJECT_DRIVE_NOTIFICAION_CLICKED));
|
||||
close();
|
||||
on_eject_click();
|
||||
}
|
||||
|
||||
//invisible large button
|
||||
@ -736,7 +743,7 @@ void NotificationManager::ExportFinishedNotification::render_eject_button(ImGuiW
|
||||
assert(m_evt_handler != nullptr);
|
||||
if (m_evt_handler != nullptr)
|
||||
wxPostEvent(m_evt_handler, EjectDriveNotificationClickedEvent(EVT_EJECT_DRIVE_NOTIFICAION_CLICKED));
|
||||
close();
|
||||
on_eject_click();
|
||||
}
|
||||
ImGui::PopStyleColor(5);
|
||||
}
|
||||
@ -745,6 +752,14 @@ bool NotificationManager::ExportFinishedNotification::on_text_click()
|
||||
open_folder(m_export_dir_path);
|
||||
return false;
|
||||
}
|
||||
void NotificationManager::ExportFinishedNotification::on_eject_click()
|
||||
{
|
||||
NotificationData data{ get_data().type, get_data().level , 0, _utf8("Ejecting.") };
|
||||
m_eject_pending = true;
|
||||
m_multiline = false;
|
||||
update(data);
|
||||
}
|
||||
|
||||
//------ProgressBar----------------
|
||||
void NotificationManager::ProgressBarNotification::init()
|
||||
{
|
||||
@ -1071,7 +1086,7 @@ void NotificationManager::UpdatedItemsInfoNotification::render_left_sign(ImGuiWr
|
||||
imgui.text(text.c_str());
|
||||
}
|
||||
|
||||
//------SlicingProgressNotificastion
|
||||
//------SlicingProgressNotification
|
||||
void NotificationManager::SlicingProgressNotification::init()
|
||||
{
|
||||
if (m_sp_state == SlicingProgressState::SP_PROGRESS) {
|
||||
@ -1084,46 +1099,53 @@ void NotificationManager::SlicingProgressNotification::init()
|
||||
}
|
||||
|
||||
}
|
||||
void NotificationManager::SlicingProgressNotification::set_progress_state(float percent)
|
||||
bool NotificationManager::SlicingProgressNotification::set_progress_state(float percent)
|
||||
{
|
||||
if (percent < 0.f)
|
||||
set_progress_state(SlicingProgressState::SP_CANCELLED);
|
||||
return true;//set_progress_state(SlicingProgressState::SP_CANCELLED);
|
||||
else if (percent >= 1.f)
|
||||
set_progress_state(SlicingProgressState::SP_COMPLETED);
|
||||
return set_progress_state(SlicingProgressState::SP_COMPLETED);
|
||||
else
|
||||
set_progress_state(SlicingProgressState::SP_PROGRESS, percent);
|
||||
return set_progress_state(SlicingProgressState::SP_PROGRESS, percent);
|
||||
}
|
||||
void NotificationManager::SlicingProgressNotification::set_progress_state(NotificationManager::SlicingProgressNotification::SlicingProgressState state, float percent/* = 0.f*/)
|
||||
bool NotificationManager::SlicingProgressNotification::set_progress_state(NotificationManager::SlicingProgressNotification::SlicingProgressState state, float percent/* = 0.f*/)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case Slic3r::GUI::NotificationManager::SlicingProgressNotification::SlicingProgressState::SP_NO_SLICING:
|
||||
case Slic3r::GUI::NotificationManager::SlicingProgressNotification::SlicingProgressState::SP_BEGAN:
|
||||
m_state = EState::Hidden;
|
||||
set_percentage(-1);
|
||||
m_has_print_info = false;
|
||||
set_export_possible(false);
|
||||
break;
|
||||
m_sp_state = state;
|
||||
return true;
|
||||
case Slic3r::GUI::NotificationManager::SlicingProgressNotification::SlicingProgressState::SP_PROGRESS:
|
||||
if ((m_sp_state != SlicingProgressState::SP_BEGAN && m_sp_state != SlicingProgressState::SP_PROGRESS) || percent < m_percentage)
|
||||
return false;
|
||||
set_percentage(percent);
|
||||
m_has_cancel_button = true;
|
||||
break;
|
||||
m_sp_state = state;
|
||||
return true;
|
||||
case Slic3r::GUI::NotificationManager::SlicingProgressNotification::SlicingProgressState::SP_CANCELLED:
|
||||
set_percentage(-1);
|
||||
m_has_cancel_button = false;
|
||||
m_has_print_info = false;
|
||||
set_export_possible(false);
|
||||
break;
|
||||
m_sp_state = state;
|
||||
return true;
|
||||
case Slic3r::GUI::NotificationManager::SlicingProgressNotification::SlicingProgressState::SP_COMPLETED:
|
||||
set_percentage(1);
|
||||
m_has_cancel_button = false;
|
||||
m_has_print_info = false;
|
||||
// m_export_possible is important only for PROGRESS state, thus we can reset it here
|
||||
// m_export_possible is important only for SP_PROGRESS state, thus we can reset it here
|
||||
set_export_possible(false);
|
||||
break;
|
||||
m_sp_state = state;
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
m_sp_state = state;
|
||||
return false;
|
||||
}
|
||||
void NotificationManager::SlicingProgressNotification::set_status_text(const std::string& text)
|
||||
{
|
||||
@ -1169,8 +1191,8 @@ void NotificationManager::SlicingProgressNotification::set_print_info(const std:
|
||||
void NotificationManager::SlicingProgressNotification::set_sidebar_collapsed(bool collapsed)
|
||||
{
|
||||
m_sidebar_collapsed = collapsed;
|
||||
if (m_sp_state == SlicingProgressState::SP_COMPLETED)
|
||||
m_state = EState::Shown;
|
||||
if (m_sp_state == SlicingProgressState::SP_COMPLETED && collapsed)
|
||||
m_state = EState::NotFading;
|
||||
}
|
||||
|
||||
void NotificationManager::SlicingProgressNotification::on_cancel_button()
|
||||
@ -1184,9 +1206,9 @@ void NotificationManager::SlicingProgressNotification::on_cancel_button()
|
||||
int NotificationManager::SlicingProgressNotification::get_duration()
|
||||
{
|
||||
if (m_sp_state == SlicingProgressState::SP_CANCELLED)
|
||||
return 10;
|
||||
return 2;
|
||||
else if (m_sp_state == SlicingProgressState::SP_COMPLETED && !m_sidebar_collapsed)
|
||||
return 5;
|
||||
return 2;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
@ -1200,7 +1222,7 @@ bool NotificationManager::SlicingProgressNotification::update_state(bool paused
|
||||
}
|
||||
void NotificationManager::SlicingProgressNotification::render_text(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
|
||||
{
|
||||
if (m_sp_state == SlicingProgressState::SP_PROGRESS || (m_sp_state == SlicingProgressState::SP_COMPLETED && !m_sidebar_collapsed)) {
|
||||
if (m_sp_state == SlicingProgressState::SP_PROGRESS /*|| (m_sp_state == SlicingProgressState::SP_COMPLETED && !m_sidebar_collapsed)*/) {
|
||||
ProgressBarNotification::render_text(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
|
||||
/* // enable for hypertext during slicing (correct call of export_enabled needed)
|
||||
if (m_multiline) {
|
||||
@ -1235,7 +1257,7 @@ void NotificationManager::SlicingProgressNotification::render_text(ImGuiWrapper&
|
||||
render_bar(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
|
||||
}
|
||||
*/
|
||||
} else if (m_sp_state == SlicingProgressState::SP_COMPLETED) {
|
||||
} else if (m_sp_state == SlicingProgressState::SP_COMPLETED && m_sidebar_collapsed) {
|
||||
// "Slicing Finished" on line 1 + hypertext, print info on line
|
||||
ImVec2 win_size(win_size_x, win_size_y);
|
||||
ImVec2 text1_size = ImGui::CalcTextSize(m_text1.c_str());
|
||||
@ -1260,21 +1282,18 @@ void NotificationManager::SlicingProgressNotification::render_text(ImGuiWrapper&
|
||||
PopNotification::render_text(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
|
||||
}
|
||||
}
|
||||
void NotificationManager::SlicingProgressNotification::render_bar(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
|
||||
void NotificationManager::SlicingProgressNotification::render_bar(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
|
||||
{
|
||||
if (!(m_sp_state == SlicingProgressState::SP_PROGRESS || (m_sp_state == SlicingProgressState::SP_COMPLETED && !m_sidebar_collapsed))) {
|
||||
if (m_sp_state != SlicingProgressState::SP_PROGRESS) {
|
||||
return;
|
||||
}
|
||||
//std::string text;
|
||||
ProgressBarNotification::render_bar(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
|
||||
/*
|
||||
std::stringstream stream;
|
||||
stream << std::fixed << std::setprecision(2) << (int)(m_percentage * 100) << "%";
|
||||
text = stream.str();
|
||||
ImGui::SetCursorPosX(m_left_indentation);
|
||||
ImGui::SetCursorPosY(win_size_y / 2 + win_size_y / 6 - (m_multiline ? 0 : m_line_height / 4));
|
||||
imgui.text(text.c_str());
|
||||
*/
|
||||
}
|
||||
void NotificationManager::SlicingProgressNotification::render_hypertext(ImGuiWrapper& imgui,const float text_x, const float text_y, const std::string text, bool more)
|
||||
{
|
||||
if (m_sp_state == SlicingProgressState::SP_COMPLETED && !m_sidebar_collapsed)
|
||||
return;
|
||||
ProgressBarNotification::render_hypertext(imgui, text_x, text_y, text, more);
|
||||
}
|
||||
void NotificationManager::SlicingProgressNotification::render_cancel_button(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
|
||||
{
|
||||
@ -1360,7 +1379,7 @@ void NotificationManager::ProgressIndicatorNotification::init()
|
||||
m_state = EState::NotFading;
|
||||
break;
|
||||
case Slic3r::GUI::NotificationManager::ProgressIndicatorNotification::ProgressIndicatorState::PIS_COMPLETED:
|
||||
m_state = EState::Shown;
|
||||
m_state = EState::ClosePending;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@ -1374,7 +1393,7 @@ void NotificationManager::ProgressIndicatorNotification::set_percentage(float pe
|
||||
m_has_cancel_button = true;
|
||||
m_progress_state = ProgressIndicatorState::PIS_PROGRESS_REQUEST;
|
||||
} else if (percent >= 1.0f) {
|
||||
m_state = EState::Shown;
|
||||
m_state = EState::FadingOut;
|
||||
m_progress_state = ProgressIndicatorState::PIS_COMPLETED;
|
||||
m_has_cancel_button = false;
|
||||
} else {
|
||||
@ -1388,6 +1407,7 @@ bool NotificationManager::ProgressIndicatorNotification::update_state(bool pause
|
||||
// percentage was changed (and it called schedule_extra_frame), now update must know this needs render
|
||||
m_next_render = 0;
|
||||
m_progress_state = ProgressIndicatorState::PIS_PROGRESS_UPDATED;
|
||||
m_current_fade_opacity = 1.0f;
|
||||
return true;
|
||||
}
|
||||
bool ret = ProgressBarNotification::update_state(paused, delta);
|
||||
@ -1628,6 +1648,7 @@ void NotificationManager::push_exporting_finished_notification(const std::string
|
||||
close_notification_of_type(NotificationType::ExportFinished);
|
||||
NotificationData data{ NotificationType::ExportFinished, NotificationLevel::RegularNotificationLevel, on_removable ? 0 : 20, _u8L("Exporting finished.") + "\n" + path };
|
||||
push_notification_data(std::make_unique<NotificationManager::ExportFinishedNotification>(data, m_id_provider, m_evt_handler, on_removable, path, dir_path), 0);
|
||||
set_slicing_progress_hidden();
|
||||
}
|
||||
|
||||
void NotificationManager::push_upload_job_notification(int id, float filesize, const std::string& filename, const std::string& host, float percentage)
|
||||
@ -1700,12 +1721,39 @@ void NotificationManager::init_slicing_progress_notification(std::function<bool(
|
||||
};
|
||||
push_notification_data(std::make_unique<NotificationManager::SlicingProgressNotification>(data, m_id_provider, m_evt_handler, cancel_callback), 0);
|
||||
}
|
||||
void NotificationManager::set_slicing_progress_began()
|
||||
{
|
||||
for (std::unique_ptr<PopNotification> & notification : m_pop_notifications) {
|
||||
if (notification->get_type() == NotificationType::SlicingProgress) {
|
||||
SlicingProgressNotification* spn = dynamic_cast<SlicingProgressNotification*>(notification.get());
|
||||
spn->set_progress_state(SlicingProgressNotification::SlicingProgressState::SP_BEGAN);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Slicing progress notification was not found - init it thru plater so correct cancel callback function is appended
|
||||
wxGetApp().plater()->init_notification_manager();
|
||||
}
|
||||
void NotificationManager::set_slicing_progress_percentage(const std::string& text, float percentage)
|
||||
{
|
||||
for (std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
|
||||
if (notification->get_type() == NotificationType::SlicingProgress) {
|
||||
SlicingProgressNotification* spn = dynamic_cast<SlicingProgressNotification*>(notification.get());
|
||||
spn->set_progress_state(percentage);
|
||||
if(spn->set_progress_state(percentage)) {
|
||||
spn->set_status_text(text);
|
||||
wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Slicing progress notification was not found - init it thru plater so correct cancel callback function is appended
|
||||
wxGetApp().plater()->init_notification_manager();
|
||||
}
|
||||
void NotificationManager::set_slicing_progress_canceled(const std::string& text)
|
||||
{
|
||||
for (std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
|
||||
if (notification->get_type() == NotificationType::SlicingProgress) {
|
||||
SlicingProgressNotification* spn = dynamic_cast<SlicingProgressNotification*>(notification.get());
|
||||
spn->set_progress_state(SlicingProgressNotification::SlicingProgressState::SP_CANCELLED);
|
||||
spn->set_status_text(text);
|
||||
wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0);
|
||||
return;
|
||||
@ -1714,7 +1762,6 @@ void NotificationManager::set_slicing_progress_percentage(const std::string& tex
|
||||
// Slicing progress notification was not found - init it thru plater so correct cancel callback function is appended
|
||||
wxGetApp().plater()->init_notification_manager();
|
||||
}
|
||||
|
||||
void NotificationManager::set_slicing_progress_hidden()
|
||||
{
|
||||
for (std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
|
||||
@ -1772,7 +1819,7 @@ void NotificationManager::init_progress_indicator()
|
||||
return;
|
||||
}
|
||||
}
|
||||
NotificationData data{ NotificationType::ProgressIndicator, NotificationLevel::ProgressBarNotificationLevel, 2};
|
||||
NotificationData data{ NotificationType::ProgressIndicator, NotificationLevel::ProgressBarNotificationLevel, 1};
|
||||
auto notification = std::make_unique<NotificationManager::ProgressIndicatorNotification>(data, m_id_provider, m_evt_handler);
|
||||
push_notification_data(std::move(notification), 0);
|
||||
}
|
||||
|
@ -189,8 +189,10 @@ public:
|
||||
void upload_job_notification_show_error(int id, const std::string& filename, const std::string& host);
|
||||
// slicing progress
|
||||
void init_slicing_progress_notification(std::function<bool()> cancel_callback);
|
||||
void set_slicing_progress_began();
|
||||
// percentage negative = canceled, <0-1) = progress, 1 = completed
|
||||
void set_slicing_progress_percentage(const std::string& text, float percentage);
|
||||
void set_slicing_progress_canceled(const std::string& text);
|
||||
// hides slicing progress notification imidietly
|
||||
void set_slicing_progress_hidden();
|
||||
// Add a print time estimate to an existing SlicingProgress notification. Set said notification to SP_COMPLETED state.
|
||||
@ -492,6 +494,7 @@ private:
|
||||
enum class SlicingProgressState
|
||||
{
|
||||
SP_NO_SLICING, // hidden
|
||||
SP_BEGAN, // still hidden but allows to go to SP_PROGRESS state. This prevents showing progress after slicing was canceled.
|
||||
SP_PROGRESS, // never fades outs, no close button, has cancel button
|
||||
SP_CANCELLED, // fades after 10 seconds, simple message
|
||||
SP_COMPLETED // Has export hyperlink and print info, fades after 20 sec if sidebar is shown, otherwise no fade out
|
||||
@ -509,10 +512,10 @@ private:
|
||||
// sets cancel button callback
|
||||
void set_cancel_callback(std::function<bool()> callback) { m_cancel_callback = callback; }
|
||||
bool has_cancel_callback() const { return m_cancel_callback != nullptr; }
|
||||
// sets SlicingProgressState, negative percent means canceled
|
||||
void set_progress_state(float percent);
|
||||
// sets SlicingProgressState, percent is used only at progress state.
|
||||
void set_progress_state(SlicingProgressState state,float percent = 0.f);
|
||||
// sets SlicingProgressState, negative percent means canceled, returns true if state was set succesfully.
|
||||
bool set_progress_state(float percent);
|
||||
// sets SlicingProgressState, percent is used only at progress state. Returns true if state was set succesfully.
|
||||
bool set_progress_state(SlicingProgressState state,float percent = 0.f);
|
||||
// sets additional string of print info and puts notification into Completed state.
|
||||
void set_print_info(const std::string& info);
|
||||
// sets fading if in Completed state.
|
||||
@ -541,8 +544,12 @@ private:
|
||||
const float win_size_x, const float win_size_y,
|
||||
const float win_pos_x, const float win_pos_y) override;
|
||||
void render_close_button(ImGuiWrapper& imgui,
|
||||
const float win_size_x, const float win_size_y,
|
||||
const float win_pos_x, const float win_pos_y) override;
|
||||
const float win_size_x, const float win_size_y,
|
||||
const float win_pos_x, const float win_pos_y) override;
|
||||
void render_hypertext(ImGuiWrapper& imgui,
|
||||
const float text_x, const float text_y,
|
||||
const std::string text,
|
||||
bool more = false) override ;
|
||||
void on_cancel_button();
|
||||
int get_duration() override;
|
||||
// if returns false, process was already canceled
|
||||
@ -626,8 +633,10 @@ private:
|
||||
void render_minimize_button(ImGuiWrapper& imgui, const float win_pos_x, const float win_pos_y) override
|
||||
{ m_minimize_b_visible = false; }
|
||||
bool on_text_click() override;
|
||||
void on_eject_click();
|
||||
// local time of last hover for showing tooltip
|
||||
long m_hover_time { 0 };
|
||||
bool m_eject_pending { false };
|
||||
};
|
||||
|
||||
class UpdatedItemsInfoNotification : public PopNotification
|
||||
|
@ -37,6 +37,7 @@ void ObjectDataViewModelNode::init_container()
|
||||
static constexpr char LayerRootIcon[] = "edit_layers_all";
|
||||
static constexpr char LayerIcon[] = "edit_layers_some";
|
||||
static constexpr char WarningIcon[] = "exclamation";
|
||||
static constexpr char WarningManifoldIcon[] = "exclamation_manifold";
|
||||
|
||||
struct InfoItemAtributes {
|
||||
std::string name;
|
||||
@ -57,13 +58,15 @@ ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* pare
|
||||
Slic3r::ModelVolumeType type,
|
||||
const wxBitmap& bmp,
|
||||
const wxString& extruder,
|
||||
const int idx/* = -1*/) :
|
||||
const int idx/* = -1*/,
|
||||
const std::string& warning_icon_name /*= std::string*/) :
|
||||
m_parent(parent),
|
||||
m_name(sub_obj_name),
|
||||
m_type(itVolume),
|
||||
m_volume_type(type),
|
||||
m_idx(idx),
|
||||
m_extruder(type == Slic3r::ModelVolumeType::MODEL_PART || type == Slic3r::ModelVolumeType::PARAMETER_MODIFIER ? extruder : "")
|
||||
m_extruder(type == Slic3r::ModelVolumeType::MODEL_PART || type == Slic3r::ModelVolumeType::PARAMETER_MODIFIER ? extruder : ""),
|
||||
m_warning_icon_name(warning_icon_name)
|
||||
{
|
||||
m_bmp = bmp;
|
||||
set_action_and_extruder_icons();
|
||||
@ -170,6 +173,13 @@ void ObjectDataViewModelNode::set_printable_icon(PrintIndicator printable)
|
||||
create_scaled_bitmap(m_printable == piPrintable ? "eye_open.png" : "eye_closed.png");
|
||||
}
|
||||
|
||||
void ObjectDataViewModelNode::set_warning_icon(const std::string& warning_icon_name)
|
||||
{
|
||||
m_warning_icon_name = warning_icon_name;
|
||||
if (warning_icon_name.empty())
|
||||
m_bmp = m_empty_bmp;
|
||||
}
|
||||
|
||||
void ObjectDataViewModelNode::update_settings_digest_bitmaps()
|
||||
{
|
||||
m_bmp = m_empty_bmp;
|
||||
@ -316,6 +326,8 @@ ObjectDataViewModel::ObjectDataViewModel()
|
||||
|
||||
m_volume_bmps = MenuFactory::get_volume_bitmaps();
|
||||
m_warning_bmp = create_scaled_bitmap(WarningIcon);
|
||||
m_warning_manifold_bmp = create_scaled_bitmap(WarningManifoldIcon);
|
||||
|
||||
for (auto item : INFO_ITEMS)
|
||||
m_info_bmps[item.first] = create_scaled_bitmap(item.second.bmp_name);
|
||||
}
|
||||
@ -328,15 +340,19 @@ ObjectDataViewModel::~ObjectDataViewModel()
|
||||
m_bitmap_cache = nullptr;
|
||||
}
|
||||
|
||||
wxBitmap& ObjectDataViewModel::GetWarningBitmap(const std::string& warning_icon_name)
|
||||
{
|
||||
return warning_icon_name.empty() ? m_empty_bmp : warning_icon_name == WarningIcon ? m_warning_bmp : m_warning_manifold_bmp;
|
||||
}
|
||||
|
||||
wxDataViewItem ObjectDataViewModel::Add(const wxString &name,
|
||||
const int extruder,
|
||||
const bool has_errors/* = false*/)
|
||||
const std::string& warning_icon_name/* = std::string()*/ )
|
||||
{
|
||||
const wxString extruder_str = extruder == 0 ? _(L("default")) : wxString::Format("%d", extruder);
|
||||
const wxString extruder_str = extruder == 0 ? _L("default") : wxString::Format("%d", extruder);
|
||||
auto root = new ObjectDataViewModelNode(name, extruder_str);
|
||||
// Add error icon if detected auto-repaire
|
||||
if (has_errors)
|
||||
root->m_bmp = m_warning_bmp;
|
||||
// Add warning icon if detected auto-repaire
|
||||
root->SetWarningBitmap(GetWarningBitmap(warning_icon_name), warning_icon_name);
|
||||
|
||||
m_objects.push_back(root);
|
||||
// notify control
|
||||
@ -350,7 +366,7 @@ wxDataViewItem ObjectDataViewModel::Add(const wxString &name,
|
||||
wxDataViewItem ObjectDataViewModel::AddVolumeChild( const wxDataViewItem &parent_item,
|
||||
const wxString &name,
|
||||
const Slic3r::ModelVolumeType volume_type,
|
||||
const bool has_errors/* = false*/,
|
||||
const std::string& warning_icon_name/* = std::string()*/,
|
||||
const int extruder/* = 0*/,
|
||||
const bool create_frst_child/* = true*/)
|
||||
{
|
||||
@ -364,12 +380,10 @@ wxDataViewItem ObjectDataViewModel::AddVolumeChild( const wxDataViewItem &parent
|
||||
if (insert_position < 0)
|
||||
insert_position = get_root_idx(root, itInstanceRoot);
|
||||
|
||||
const bool obj_errors = root->m_bmp.IsOk();
|
||||
|
||||
if (create_frst_child && root->m_volumes_cnt == 0)
|
||||
{
|
||||
const Slic3r::ModelVolumeType type = Slic3r::ModelVolumeType::MODEL_PART;
|
||||
const auto node = new ObjectDataViewModelNode(root, root->m_name, type, GetVolumeIcon(type, obj_errors), extruder_str, 0);
|
||||
const auto node = new ObjectDataViewModelNode(root, root->m_name, type, GetVolumeIcon(type, root->m_warning_icon_name), extruder_str, 0, root->m_warning_icon_name);
|
||||
|
||||
insert_position < 0 ? root->Append(node) : root->Insert(node, insert_position);
|
||||
// notify control
|
||||
@ -380,12 +394,13 @@ wxDataViewItem ObjectDataViewModel::AddVolumeChild( const wxDataViewItem &parent
|
||||
if (insert_position >= 0) insert_position++;
|
||||
}
|
||||
|
||||
const auto node = new ObjectDataViewModelNode(root, name, volume_type, GetVolumeIcon(volume_type, has_errors), extruder_str, root->m_volumes_cnt);
|
||||
const auto node = new ObjectDataViewModelNode(root, name, volume_type, GetVolumeIcon(volume_type, warning_icon_name), extruder_str, root->m_volumes_cnt, warning_icon_name);
|
||||
insert_position < 0 ? root->Append(node) : root->Insert(node, insert_position);
|
||||
|
||||
// if part with errors is added, but object wasn't marked, then mark it
|
||||
if (!obj_errors && has_errors)
|
||||
root->SetBitmap(m_warning_bmp);
|
||||
if (!warning_icon_name.empty() && warning_icon_name != root->m_warning_icon_name &&
|
||||
(root->m_warning_icon_name.empty() || root->m_warning_icon_name == WarningManifoldIcon) )
|
||||
root->SetWarningBitmap(GetWarningBitmap(warning_icon_name), warning_icon_name);
|
||||
|
||||
// notify control
|
||||
const wxDataViewItem child((void*)node);
|
||||
@ -1665,6 +1680,7 @@ void ObjectDataViewModel::Rescale()
|
||||
{
|
||||
m_volume_bmps = MenuFactory::get_volume_bitmaps();
|
||||
m_warning_bmp = create_scaled_bitmap(WarningIcon);
|
||||
m_warning_manifold_bmp = create_scaled_bitmap(WarningManifoldIcon);
|
||||
|
||||
wxDataViewItemArray all_items;
|
||||
GetAllChildren(wxDataViewItem(0), all_items);
|
||||
@ -1680,10 +1696,10 @@ void ObjectDataViewModel::Rescale()
|
||||
switch (node->m_type)
|
||||
{
|
||||
case itObject:
|
||||
if (node->m_bmp.IsOk()) node->m_bmp = m_warning_bmp;
|
||||
if (node->m_bmp.IsOk()) node->m_bmp = GetWarningBitmap(node->m_warning_icon_name);
|
||||
break;
|
||||
case itVolume:
|
||||
node->m_bmp = GetVolumeIcon(node->m_volume_type, node->m_bmp.GetWidth() != node->m_bmp.GetHeight());
|
||||
node->m_bmp = GetVolumeIcon(node->m_volume_type, node->m_warning_icon_name);
|
||||
break;
|
||||
case itLayerRoot:
|
||||
node->m_bmp = create_scaled_bitmap(LayerRootIcon);
|
||||
@ -1697,19 +1713,19 @@ void ObjectDataViewModel::Rescale()
|
||||
}
|
||||
}
|
||||
|
||||
wxBitmap ObjectDataViewModel::GetVolumeIcon(const Slic3r::ModelVolumeType vol_type, const bool is_marked/* = false*/)
|
||||
wxBitmap ObjectDataViewModel::GetVolumeIcon(const Slic3r::ModelVolumeType vol_type, const std::string& warning_icon_name/* = std::string()*/)
|
||||
{
|
||||
if (!is_marked)
|
||||
if (warning_icon_name.empty())
|
||||
return m_volume_bmps[static_cast<int>(vol_type)];
|
||||
|
||||
std::string scaled_bitmap_name = "warning" + std::to_string(static_cast<int>(vol_type));
|
||||
scaled_bitmap_name += "-em" + std::to_string(Slic3r::GUI::wxGetApp().em_unit());
|
||||
std::string scaled_bitmap_name = warning_icon_name + std::to_string(static_cast<int>(vol_type));
|
||||
scaled_bitmap_name += "-em" + std::to_string(wxGetApp().em_unit()) + (wxGetApp().dark_mode() ? "-dm" : "-lm");
|
||||
|
||||
wxBitmap *bmp = m_bitmap_cache->find(scaled_bitmap_name);
|
||||
if (bmp == nullptr) {
|
||||
std::vector<wxBitmap> bmps;
|
||||
|
||||
bmps.emplace_back(m_warning_bmp);
|
||||
bmps.emplace_back(GetWarningBitmap(warning_icon_name));
|
||||
bmps.emplace_back(m_volume_bmps[static_cast<int>(vol_type)]);
|
||||
|
||||
bmp = m_bitmap_cache->insert(scaled_bitmap_name, bmps);
|
||||
@ -1718,20 +1734,20 @@ wxBitmap ObjectDataViewModel::GetVolumeIcon(const Slic3r::ModelVolumeType vol_ty
|
||||
return *bmp;
|
||||
}
|
||||
|
||||
void ObjectDataViewModel::AddWarningIcon(const wxDataViewItem& item)
|
||||
void ObjectDataViewModel::AddWarningIcon(const wxDataViewItem& item, const std::string& warning_icon_name)
|
||||
{
|
||||
if (!item.IsOk())
|
||||
return;
|
||||
ObjectDataViewModelNode *node = static_cast<ObjectDataViewModelNode*>(item.GetID());
|
||||
|
||||
if (node->GetType() & itObject) {
|
||||
node->SetBitmap(m_warning_bmp);
|
||||
node->SetWarningBitmap(GetWarningBitmap(warning_icon_name), warning_icon_name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (node->GetType() & itVolume) {
|
||||
node->SetBitmap(GetVolumeIcon(node->GetVolumeType(), true));
|
||||
node->GetParent()->SetBitmap(m_warning_bmp);
|
||||
node->SetWarningBitmap(GetVolumeIcon(node->GetVolumeType(), warning_icon_name), warning_icon_name);
|
||||
node->GetParent()->SetWarningBitmap(GetWarningBitmap(warning_icon_name), warning_icon_name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -1747,11 +1763,11 @@ void ObjectDataViewModel::DeleteWarningIcon(const wxDataViewItem& item, const bo
|
||||
return;
|
||||
|
||||
if (node->GetType() & itVolume) {
|
||||
node->SetBitmap(m_volume_bmps[static_cast<int>(node->volume_type())]);
|
||||
node->SetWarningBitmap(m_volume_bmps[static_cast<int>(node->volume_type())], "");
|
||||
return;
|
||||
}
|
||||
|
||||
node->SetBitmap(wxNullBitmap);
|
||||
node->SetWarningBitmap(wxNullBitmap, "");
|
||||
if (unmark_object)
|
||||
{
|
||||
wxDataViewItemArray children;
|
||||
|
@ -78,6 +78,7 @@ class ObjectDataViewModelNode
|
||||
wxBitmap m_action_icon;
|
||||
PrintIndicator m_printable {piUndef};
|
||||
wxBitmap m_printable_icon;
|
||||
std::string m_warning_icon_name{ "" };
|
||||
|
||||
std::string m_action_icon_name = "";
|
||||
ModelVolumeType m_volume_type;
|
||||
@ -100,7 +101,8 @@ public:
|
||||
Slic3r::ModelVolumeType type,
|
||||
const wxBitmap& bmp,
|
||||
const wxString& extruder,
|
||||
const int idx = -1);
|
||||
const int idx = -1,
|
||||
const std::string& warning_icon_name = std::string());
|
||||
|
||||
ObjectDataViewModelNode(ObjectDataViewModelNode* parent,
|
||||
const t_layer_height_range& layer_range,
|
||||
@ -179,6 +181,7 @@ public:
|
||||
void SetVolumeType(ModelVolumeType type) { m_volume_type = type; }
|
||||
void SetBitmap(const wxBitmap &icon) { m_bmp = icon; }
|
||||
void SetExtruder(const wxString &extruder) { m_extruder = extruder; }
|
||||
void SetWarningBitmap(const wxBitmap& icon, const std::string& warning_icon_name) { m_bmp = icon; m_warning_icon_name = warning_icon_name; }
|
||||
const wxBitmap& GetBitmap() const { return m_bmp; }
|
||||
const wxString& GetName() const { return m_name; }
|
||||
ItemType GetType() const { return m_type; }
|
||||
@ -225,6 +228,8 @@ public:
|
||||
void set_extruder_icon();
|
||||
// Set printable icon for node
|
||||
void set_printable_icon(PrintIndicator printable);
|
||||
// Set warning icon for node
|
||||
void set_warning_icon(const std::string& warning_icon);
|
||||
|
||||
void update_settings_digest_bitmaps();
|
||||
bool update_settings_digest(const std::vector<std::string>& categories);
|
||||
@ -253,7 +258,9 @@ class ObjectDataViewModel :public wxDataViewModel
|
||||
std::vector<ObjectDataViewModelNode*> m_objects;
|
||||
std::vector<wxBitmap> m_volume_bmps;
|
||||
std::map<InfoItemType, wxBitmap> m_info_bmps;
|
||||
wxBitmap m_empty_bmp;
|
||||
wxBitmap m_warning_bmp;
|
||||
wxBitmap m_warning_manifold_bmp;
|
||||
|
||||
wxDataViewCtrl* m_ctrl { nullptr };
|
||||
|
||||
@ -263,11 +270,11 @@ public:
|
||||
|
||||
wxDataViewItem Add( const wxString &name,
|
||||
const int extruder,
|
||||
const bool has_errors = false);
|
||||
const std::string& warning_icon_name = std::string());
|
||||
wxDataViewItem AddVolumeChild( const wxDataViewItem &parent_item,
|
||||
const wxString &name,
|
||||
const Slic3r::ModelVolumeType volume_type,
|
||||
const bool has_errors = false,
|
||||
const std::string& warning_icon_name = std::string(),
|
||||
const int extruder = 0,
|
||||
const bool create_frst_child = true);
|
||||
wxDataViewItem AddSettingsChild(const wxDataViewItem &parent_item);
|
||||
@ -378,8 +385,8 @@ public:
|
||||
void Rescale();
|
||||
|
||||
wxBitmap GetVolumeIcon(const Slic3r::ModelVolumeType vol_type,
|
||||
const bool is_marked = false);
|
||||
void AddWarningIcon(const wxDataViewItem& item);
|
||||
const std::string& warning_icon_name = std::string());
|
||||
void AddWarningIcon(const wxDataViewItem& item, const std::string& warning_name);
|
||||
void DeleteWarningIcon(const wxDataViewItem& item, const bool unmark_object = false);
|
||||
t_layer_height_range GetLayerRangeByItem(const wxDataViewItem& item) const;
|
||||
|
||||
@ -392,6 +399,8 @@ private:
|
||||
wxDataViewItem AddRoot(const wxDataViewItem& parent_item, const ItemType root_type);
|
||||
wxDataViewItem AddInstanceRoot(const wxDataViewItem& parent_item);
|
||||
void AddAllChildren(const wxDataViewItem& parent);
|
||||
|
||||
wxBitmap& GetWarningBitmap(const std::string& warning_icon_name);
|
||||
};
|
||||
|
||||
|
||||
|
@ -125,6 +125,7 @@ wxDEFINE_EVENT(EVT_EXPORT_BEGAN, wxCommandEvent);
|
||||
|
||||
class ObjectInfo : public wxStaticBoxSizer
|
||||
{
|
||||
std::string m_warning_icon_name{ "exclamation" };
|
||||
public:
|
||||
ObjectInfo(wxWindow *parent);
|
||||
|
||||
@ -142,6 +143,7 @@ public:
|
||||
bool showing_manifold_warning_icon;
|
||||
void show_sizer(bool show);
|
||||
void msw_rescale();
|
||||
void update_warning_icon(const std::string& warning_icon_name);
|
||||
};
|
||||
|
||||
ObjectInfo::ObjectInfo(wxWindow *parent) :
|
||||
@ -175,7 +177,7 @@ ObjectInfo::ObjectInfo(wxWindow *parent) :
|
||||
info_manifold_text->SetFont(wxGetApp().small_font());
|
||||
info_manifold = new wxStaticText(parent, wxID_ANY, "");
|
||||
info_manifold->SetFont(wxGetApp().small_font());
|
||||
manifold_warning_icon = new wxStaticBitmap(parent, wxID_ANY, create_scaled_bitmap("exclamation"));
|
||||
manifold_warning_icon = new wxStaticBitmap(parent, wxID_ANY, create_scaled_bitmap(m_warning_icon_name));
|
||||
auto *sizer_manifold = new wxBoxSizer(wxHORIZONTAL);
|
||||
sizer_manifold->Add(info_manifold_text, 0);
|
||||
sizer_manifold->Add(manifold_warning_icon, 0, wxLEFT, 2);
|
||||
@ -194,7 +196,15 @@ void ObjectInfo::show_sizer(bool show)
|
||||
|
||||
void ObjectInfo::msw_rescale()
|
||||
{
|
||||
manifold_warning_icon->SetBitmap(create_scaled_bitmap("exclamation"));
|
||||
manifold_warning_icon->SetBitmap(create_scaled_bitmap(m_warning_icon_name));
|
||||
}
|
||||
|
||||
void ObjectInfo::update_warning_icon(const std::string& warning_icon_name)
|
||||
{
|
||||
if (warning_icon_name.empty())
|
||||
return;
|
||||
m_warning_icon_name = warning_icon_name;
|
||||
manifold_warning_icon->SetBitmap(create_scaled_bitmap(m_warning_icon_name));
|
||||
}
|
||||
|
||||
enum SlicedInfoIdx
|
||||
@ -1129,30 +1139,19 @@ void Sidebar::show_info_sizer()
|
||||
p->object_info->info_size->SetLabel(wxString::Format("%.2f x %.2f x %.2f",size(0)*koef, size(1)*koef, size(2)*koef));
|
||||
p->object_info->info_materials->SetLabel(wxString::Format("%d", static_cast<int>(model_object->materials_count())));
|
||||
|
||||
const auto& stats = model_object->get_object_stl_stats();//model_object->volumes.front()->mesh.stl.stats;
|
||||
const auto& stats = model_object->get_object_stl_stats();
|
||||
p->object_info->info_volume->SetLabel(wxString::Format("%.2f", stats.volume*pow(koef,3)));
|
||||
p->object_info->info_facets->SetLabel(format_wxstr(_L_PLURAL("%1% (%2$d shell)", "%1% (%2$d shells)", stats.number_of_parts),
|
||||
static_cast<int>(model_object->facets_count()), stats.number_of_parts));
|
||||
|
||||
int errors = stats.degenerate_facets + stats.edges_fixed + stats.facets_removed +
|
||||
stats.facets_reversed + stats.backwards_edges;
|
||||
if (errors > 0) {
|
||||
wxString tooltip = format_wxstr(_L_PLURAL("Auto-repaired %1$d error", "Auto-repaired %1$d errors", errors), errors);
|
||||
p->object_info->info_manifold->SetLabel(tooltip);
|
||||
if (stats.repaired()) {
|
||||
int errors = stats.degenerate_facets + stats.edges_fixed + stats.facets_removed + stats.facets_reversed + stats.backwards_edges;
|
||||
p->object_info->info_manifold->SetLabel(format_wxstr(_L_PLURAL("Auto-repaired %1$d error", "Auto-repaired %1$d errors", errors), errors));
|
||||
|
||||
tooltip += ":\n";
|
||||
if (stats.degenerate_facets > 0)
|
||||
tooltip += format_wxstr(_L_PLURAL("%1$d degenerate facet", "%1$d degenerate facets", stats.degenerate_facets), stats.degenerate_facets) + ", ";
|
||||
if (stats.edges_fixed > 0)
|
||||
tooltip += format_wxstr(_L_PLURAL("%1$d edge fixed", "%1$d edges fixed", stats.edges_fixed), stats.edges_fixed) + ", ";
|
||||
if (stats.facets_removed > 0)
|
||||
tooltip += format_wxstr(_L_PLURAL("%1$d facet removed", "%1$d facets removed", stats.facets_removed), stats.facets_removed) + ", ";
|
||||
if (stats.facets_reversed > 0)
|
||||
tooltip += format_wxstr(_L_PLURAL("%1$d facet reversed", "%1$d facets reversed", stats.facets_reversed), stats.facets_reversed) + ", ";
|
||||
if (stats.backwards_edges > 0)
|
||||
tooltip += format_wxstr(_L_PLURAL("%1$d backwards edge", "%1$d backwards edges", stats.backwards_edges), stats.backwards_edges) + ", ";
|
||||
tooltip.RemoveLast(2);//remove last coma
|
||||
auto mesh_errors = obj_list()->get_mesh_errors(true);
|
||||
wxString tooltip = mesh_errors.first;
|
||||
|
||||
p->object_info->update_warning_icon(mesh_errors.second);
|
||||
p->object_info->showing_manifold_warning_icon = true;
|
||||
p->object_info->info_manifold->SetToolTip(tooltip);
|
||||
p->object_info->manifold_warning_icon->SetToolTip(tooltip);
|
||||
@ -1531,25 +1530,25 @@ struct Plater::priv
|
||||
|
||||
void arrange()
|
||||
{
|
||||
m->take_snapshot(_(L("Arrange")));
|
||||
m->take_snapshot(_L("Arrange"));
|
||||
start(m_arrange_id);
|
||||
}
|
||||
|
||||
void fill_bed()
|
||||
{
|
||||
m->take_snapshot(_(L("Fill bed")));
|
||||
m->take_snapshot(_L("Fill bed"));
|
||||
start(m_fill_bed_id);
|
||||
}
|
||||
|
||||
void optimize_rotation()
|
||||
{
|
||||
m->take_snapshot(_(L("Optimize Rotation")));
|
||||
m->take_snapshot(_L("Optimize Rotation"));
|
||||
start(m_rotoptimize_id);
|
||||
}
|
||||
|
||||
void import_sla_arch()
|
||||
{
|
||||
m->take_snapshot(_(L("Import SLA archive")));
|
||||
m->take_snapshot(_L("Import SLA archive"));
|
||||
start(m_sla_import_id);
|
||||
}
|
||||
|
||||
@ -1576,16 +1575,22 @@ struct Plater::priv
|
||||
|
||||
bool is_project_dirty() const { return dirty_state.is_dirty(); }
|
||||
void update_project_dirty_from_presets() { dirty_state.update_from_presets(); }
|
||||
int save_project_if_dirty() {
|
||||
int save_project_if_dirty(const wxString& reason) {
|
||||
int res = wxID_NO;
|
||||
if (dirty_state.is_dirty()) {
|
||||
MainFrame* mainframe = wxGetApp().mainframe;
|
||||
if (mainframe->can_save_as()) {
|
||||
//wxMessageDialog dlg(mainframe, _L("Do you want to save the changes to the current project ?"), wxString(SLIC3R_APP_NAME), wxYES_NO | wxCANCEL);
|
||||
MessageDialog dlg(mainframe, _L("Do you want to save the changes to the current project ?"), wxString(SLIC3R_APP_NAME), wxYES_NO | wxCANCEL);
|
||||
res = dlg.ShowModal();
|
||||
wxString suggested_project_name;
|
||||
wxString project_name = suggested_project_name = get_project_filename(".3mf");
|
||||
if (suggested_project_name.IsEmpty()) {
|
||||
fs::path output_file = get_export_file_path(FT_3MF);
|
||||
suggested_project_name = output_file.empty() ? _L("Untitled") : from_u8(output_file.stem().string());
|
||||
}
|
||||
res = MessageDialog(mainframe, reason + "\n" + format_wxstr(_L("Do you want to save the changes to \"%1%\"?"), suggested_project_name),
|
||||
wxString(SLIC3R_APP_NAME), wxYES_NO | wxCANCEL).ShowModal();
|
||||
if (res == wxID_YES)
|
||||
mainframe->save_project_as(wxGetApp().plater()->get_project_filename());
|
||||
if (!mainframe->save_project_as(project_name))
|
||||
res = wxID_CANCEL;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
@ -1645,6 +1650,7 @@ struct Plater::priv
|
||||
std::vector<size_t> load_files(const std::vector<fs::path>& input_files, bool load_model, bool load_config, bool used_inches = false);
|
||||
std::vector<size_t> load_model_objects(const ModelObjectPtrs& model_objects, bool allow_negative_z = false);
|
||||
|
||||
fs::path get_export_file_path(GUI::FileType file_type);
|
||||
wxString get_export_file(GUI::FileType file_type);
|
||||
|
||||
const Selection& get_selection() const;
|
||||
@ -1671,8 +1677,9 @@ struct Plater::priv
|
||||
void enter_gizmos_stack();
|
||||
void leave_gizmos_stack();
|
||||
|
||||
void take_snapshot(const std::string& snapshot_name);
|
||||
void take_snapshot(const wxString& snapshot_name) { this->take_snapshot(std::string(snapshot_name.ToUTF8().data())); }
|
||||
void take_snapshot(const std::string& snapshot_name, UndoRedo::SnapshotType snapshot_type = UndoRedo::SnapshotType::Action);
|
||||
void take_snapshot(const wxString& snapshot_name, UndoRedo::SnapshotType snapshot_type = UndoRedo::SnapshotType::Action)
|
||||
{ this->take_snapshot(std::string(snapshot_name.ToUTF8().data()), snapshot_type); }
|
||||
int get_active_snapshot_index();
|
||||
|
||||
void undo();
|
||||
@ -1914,6 +1921,8 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
||||
sidebar->Bind(wxEVT_COMBOBOX, &priv::on_select_preset, this);
|
||||
sidebar->Bind(EVT_OBJ_LIST_OBJECT_SELECT, [this](wxEvent&) { priv::selection_changed(); });
|
||||
sidebar->Bind(EVT_SCHEDULE_BACKGROUND_PROCESS, [this](SimpleEvent&) { this->schedule_background_process(); });
|
||||
// jump to found option from SearchDialog
|
||||
q->Bind(wxCUSTOMEVT_JUMP_TO_OPTION, [this](wxCommandEvent& evt) { sidebar->jump_to_option(evt.GetInt()); });
|
||||
}
|
||||
|
||||
wxGLCanvas* view3D_canvas = view3D->get_wxglcanvas();
|
||||
@ -2054,7 +2063,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
||||
}
|
||||
|
||||
// Initialize the Undo / Redo stack with a first snapshot.
|
||||
this->take_snapshot(_L("New Project"));
|
||||
this->take_snapshot(_L("New Project"), UndoRedo::SnapshotType::ProjectSeparator);
|
||||
|
||||
this->q->Bind(EVT_LOAD_MODEL_OTHER_INSTANCE, [this](LoadFromOtherInstanceEvent& evt) {
|
||||
BOOST_LOG_TRIVIAL(trace) << "Received load from other instance event.";
|
||||
@ -2430,7 +2439,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
||||
for (ModelObject* model_object : model.objects) {
|
||||
if (!type_3mf && !type_zip_amf)
|
||||
model_object->center_around_origin(false);
|
||||
model_object->ensure_on_bed(is_project_file);
|
||||
model_object->ensure_on_bed(is_project_file || type_3mf || type_zip_amf);
|
||||
}
|
||||
|
||||
// check multi-part object adding for the SLA-printing
|
||||
@ -2447,7 +2456,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
||||
if (one_by_one) {
|
||||
if (type_3mf && !is_project_file)
|
||||
model.center_instances_around_point(bed_shape_bb().center());
|
||||
auto loaded_idxs = load_model_objects(model.objects, is_project_file);
|
||||
auto loaded_idxs = load_model_objects(model.objects, is_project_file || type_3mf || type_zip_amf);
|
||||
obj_idxs.insert(obj_idxs.end(), loaded_idxs.begin(), loaded_idxs.end());
|
||||
} else {
|
||||
// This must be an .stl or .obj file, which may contain a maximum of one volume.
|
||||
@ -2600,22 +2609,8 @@ std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs& mode
|
||||
return obj_idxs;
|
||||
}
|
||||
|
||||
wxString Plater::priv::get_export_file(GUI::FileType file_type)
|
||||
fs::path Plater::priv::get_export_file_path(GUI::FileType file_type)
|
||||
{
|
||||
wxString wildcard;
|
||||
switch (file_type) {
|
||||
case FT_STL:
|
||||
case FT_AMF:
|
||||
case FT_3MF:
|
||||
case FT_GCODE:
|
||||
case FT_OBJ:
|
||||
wildcard = file_wildcards(file_type);
|
||||
break;
|
||||
default:
|
||||
wildcard = file_wildcards(FT_MODEL);
|
||||
break;
|
||||
}
|
||||
|
||||
// Update printbility state of each of the ModelInstances.
|
||||
this->update_print_volume_state();
|
||||
|
||||
@ -2640,7 +2635,31 @@ wxString Plater::priv::get_export_file(GUI::FileType file_type)
|
||||
if (output_file.empty() && !model.objects.empty())
|
||||
// Find the file name of the first object.
|
||||
output_file = this->model.objects[0]->get_export_filename();
|
||||
|
||||
if (output_file.empty())
|
||||
// Use _L("Untitled") name
|
||||
output_file = into_path(_L("Untitled"));
|
||||
}
|
||||
return output_file;
|
||||
}
|
||||
|
||||
wxString Plater::priv::get_export_file(GUI::FileType file_type)
|
||||
{
|
||||
wxString wildcard;
|
||||
switch (file_type) {
|
||||
case FT_STL:
|
||||
case FT_AMF:
|
||||
case FT_3MF:
|
||||
case FT_GCODE:
|
||||
case FT_OBJ:
|
||||
wildcard = file_wildcards(file_type);
|
||||
break;
|
||||
default:
|
||||
wildcard = file_wildcards(FT_MODEL);
|
||||
break;
|
||||
}
|
||||
|
||||
fs::path output_file = get_export_file_path(file_type);
|
||||
|
||||
wxString dlg_title;
|
||||
switch (file_type) {
|
||||
@ -2800,7 +2819,7 @@ void Plater::priv::delete_all_objects_from_model()
|
||||
|
||||
void Plater::priv::reset()
|
||||
{
|
||||
Plater::TakeSnapshot snapshot(q, _L("Reset Project"));
|
||||
Plater::TakeSnapshot snapshot(q, _L("Reset Project"), UndoRedo::SnapshotType::ProjectSeparator);
|
||||
|
||||
clear_warnings();
|
||||
|
||||
@ -3086,6 +3105,9 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool
|
||||
// if ((return_state & UPDATE_BACKGROUND_PROCESS_RESTART) != 0 ||
|
||||
// (return_state & UPDATE_BACKGROUND_PROCESS_REFRESH_SCENE) != 0 )
|
||||
// this->statusbar()->set_status_text(_L("Ready to slice"));
|
||||
if ((return_state & UPDATE_BACKGROUND_PROCESS_RESTART) != 0 ||
|
||||
(return_state & UPDATE_BACKGROUND_PROCESS_REFRESH_SCENE) != 0 )
|
||||
notification_manager->set_slicing_progress_hidden();
|
||||
|
||||
sidebar->set_btn_label(ActionButtonType::abExport, _(label_btn_export));
|
||||
sidebar->set_btn_label(ActionButtonType::abSendGCode, _(label_btn_send));
|
||||
@ -3751,10 +3773,10 @@ void Plater::priv::set_current_panel(wxPanel* panel)
|
||||
|
||||
if (view3D->is_reload_delayed()) {
|
||||
// Delayed loading of the 3D scene.
|
||||
if (this->printer_technology == ptSLA) {
|
||||
if (printer_technology == ptSLA) {
|
||||
// Update the SLAPrint from the current Model, so that the reload_scene()
|
||||
// pulls the correct data.
|
||||
this->update_restart_background_process(true, false);
|
||||
update_restart_background_process(true, false);
|
||||
} else
|
||||
view3D->reload_scene(true);
|
||||
}
|
||||
@ -3777,8 +3799,12 @@ void Plater::priv::set_current_panel(wxPanel* panel)
|
||||
// FIXME: it may be better to have a single function making this check and let it be called wherever needed
|
||||
bool export_in_progress = this->background_process.is_export_scheduled();
|
||||
bool model_fits = view3D->get_canvas3d()->check_volumes_outside_state() != ModelInstancePVS_Partly_Outside;
|
||||
if (!model.objects.empty() && !export_in_progress && model_fits)
|
||||
this->q->reslice();
|
||||
if (!model.objects.empty() && !export_in_progress && model_fits) {
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
preview->get_canvas3d()->init_gcode_viewer();
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
q->reslice();
|
||||
}
|
||||
// keeps current gcode preview, if any
|
||||
preview->reload_print(true);
|
||||
|
||||
@ -3919,20 +3945,16 @@ void Plater::priv::on_slicing_update(SlicingStatusEvent &evt)
|
||||
|
||||
void Plater::priv::on_slicing_completed(wxCommandEvent & evt)
|
||||
{
|
||||
switch (this->printer_technology) {
|
||||
case ptFFF:
|
||||
this->update_fff_scene();
|
||||
break;
|
||||
case ptSLA:
|
||||
if (view3D->is_dragging())
|
||||
delayed_scene_refresh = true;
|
||||
if (view3D->is_dragging()) // updating scene now would interfere with the gizmo dragging
|
||||
delayed_scene_refresh = true;
|
||||
else {
|
||||
if (this->printer_technology == ptFFF)
|
||||
this->update_fff_scene();
|
||||
else
|
||||
this->update_sla_scene();
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Plater::priv::on_export_began(wxCommandEvent& evt)
|
||||
{
|
||||
if (show_warning_dialog)
|
||||
@ -3943,6 +3965,7 @@ void Plater::priv::on_slicing_began()
|
||||
clear_warnings();
|
||||
notification_manager->close_notification_of_type(NotificationType::SignDetected);
|
||||
notification_manager->close_notification_of_type(NotificationType::ExportFinished);
|
||||
notification_manager->set_slicing_progress_began();
|
||||
}
|
||||
void Plater::priv::add_warning(const Slic3r::PrintStateBase::Warning& warning, size_t oid)
|
||||
{
|
||||
@ -4040,7 +4063,7 @@ void Plater::priv::on_process_completed(SlicingProcessCompletedEvent &evt)
|
||||
}
|
||||
if (evt.cancelled()) {
|
||||
// this->statusbar()->set_status_text(_L("Cancelled"));
|
||||
this->notification_manager->set_slicing_progress_percentage(_utf8("Slicing Cancelled."), -1);
|
||||
this->notification_manager->set_slicing_progress_canceled(_utf8("Slicing Cancelled."));
|
||||
}
|
||||
|
||||
this->sidebar->show_sliced_info_sizer(evt.success());
|
||||
@ -4051,17 +4074,13 @@ void Plater::priv::on_process_completed(SlicingProcessCompletedEvent &evt)
|
||||
this->object_list_changed();
|
||||
|
||||
// refresh preview
|
||||
switch (this->printer_technology) {
|
||||
case ptFFF:
|
||||
this->update_fff_scene();
|
||||
break;
|
||||
case ptSLA:
|
||||
if (view3D->is_dragging())
|
||||
delayed_scene_refresh = true;
|
||||
if (view3D->is_dragging()) // updating scene now would interfere with the gizmo dragging
|
||||
delayed_scene_refresh = true;
|
||||
else {
|
||||
if (this->printer_technology == ptFFF)
|
||||
this->update_fff_scene();
|
||||
else
|
||||
this->update_sla_scene();
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
if (evt.cancelled()) {
|
||||
@ -4640,12 +4659,13 @@ int Plater::priv::get_active_snapshot_index()
|
||||
return it - ss_stack.begin();
|
||||
}
|
||||
|
||||
void Plater::priv::take_snapshot(const std::string& snapshot_name)
|
||||
void Plater::priv::take_snapshot(const std::string& snapshot_name, const UndoRedo::SnapshotType snapshot_type)
|
||||
{
|
||||
if (m_prevent_snapshots > 0)
|
||||
return;
|
||||
assert(m_prevent_snapshots >= 0);
|
||||
UndoRedo::SnapshotData snapshot_data;
|
||||
snapshot_data.snapshot_type = snapshot_type;
|
||||
snapshot_data.printer_technology = this->printer_technology;
|
||||
if (this->view3D->is_layers_editing_enabled())
|
||||
snapshot_data.flags |= UndoRedo::SnapshotData::VARIABLE_LAYER_EDITING_ACTIVE;
|
||||
@ -4717,8 +4737,10 @@ void Plater::priv::undo_redo_to(std::vector<UndoRedo::Snapshot>::const_iterator
|
||||
if (printer_technology_changed) {
|
||||
// Switching the printer technology when jumping forwards / backwards in time. Switch to the last active printer profile of the other type.
|
||||
std::string s_pt = (it_snapshot->snapshot_data.printer_technology == ptFFF) ? "FFF" : "SLA";
|
||||
if (!wxGetApp().check_and_save_current_preset_changes(format_wxstr(_L(
|
||||
"%1% printer was active at the time the target Undo / Redo snapshot was taken. Switching to %1% printer requires reloading of %1% presets."), s_pt)))
|
||||
if (!wxGetApp().check_and_save_current_preset_changes(_L("Undo / Redo is processing"),
|
||||
// format_wxstr(_L("%1% printer was active at the time the target Undo / Redo snapshot was taken. Switching to %1% printer requires reloading of %1% presets."), s_pt)))
|
||||
format_wxstr(_L("Switching the printer technology from %1% to %2%.\n"
|
||||
"Some %1% presets were modified, which will be lost after switching the printer technology."), s_pt =="FFF" ? "SLA" : "FFF", s_pt), false))
|
||||
// Don't switch the profiles.
|
||||
return;
|
||||
}
|
||||
@ -4896,7 +4918,7 @@ Plater::Plater(wxWindow *parent, MainFrame *main_frame)
|
||||
|
||||
bool Plater::is_project_dirty() const { return p->is_project_dirty(); }
|
||||
void Plater::update_project_dirty_from_presets() { p->update_project_dirty_from_presets(); }
|
||||
int Plater::save_project_if_dirty() { return p->save_project_if_dirty(); }
|
||||
int Plater::save_project_if_dirty(const wxString& reason) { return p->save_project_if_dirty(reason); }
|
||||
void Plater::reset_project_dirty_after_save() { p->reset_project_dirty_after_save(); }
|
||||
void Plater::reset_project_dirty_initial_presets() { p->reset_project_dirty_initial_presets(); }
|
||||
#if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW
|
||||
@ -4913,11 +4935,23 @@ SLAPrint& Plater::sla_print() { return p->sla_print; }
|
||||
|
||||
void Plater::new_project()
|
||||
{
|
||||
if (p->save_project_if_dirty() == wxID_CANCEL)
|
||||
if (int saved_project = p->save_project_if_dirty(_L("Creating a new project while the current project is modified.")); saved_project == wxID_CANCEL)
|
||||
return;
|
||||
else {
|
||||
wxString header = _L("Creating a new project while some presets are modified.") + "\n" +
|
||||
(saved_project == wxID_YES ? _L("You can keep presets modifications to the new project or discard them") :
|
||||
_L("You can keep presets modifications to the new project, discard them or save changes as new presets.\n"
|
||||
"Note, if changes will be saved than new project wouldn't keep them"));
|
||||
using ab = UnsavedChangesDialog::ActionButtons;
|
||||
int act_buttons = ab::KEEP;
|
||||
if (saved_project == wxID_NO)
|
||||
act_buttons |= ab::SAVE;
|
||||
if (!wxGetApp().check_and_keep_current_preset_changes(_L("New Project is creating"), header, act_buttons))
|
||||
return;
|
||||
}
|
||||
|
||||
p->select_view_3D("3D");
|
||||
take_snapshot(_L("New Project"));
|
||||
take_snapshot(_L("New Project"), UndoRedo::SnapshotType::ProjectSeparator);
|
||||
Plater::SuppressSnapshots suppress(this);
|
||||
reset();
|
||||
reset_project_dirty_initial_presets();
|
||||
@ -4926,7 +4960,7 @@ void Plater::new_project()
|
||||
|
||||
void Plater::load_project()
|
||||
{
|
||||
if (p->save_project_if_dirty() == wxID_CANCEL)
|
||||
if (!wxGetApp().can_load_project())
|
||||
return;
|
||||
|
||||
// Ask user for a project file name.
|
||||
@ -4942,7 +4976,7 @@ void Plater::load_project(const wxString& filename)
|
||||
return;
|
||||
|
||||
// Take the Undo / Redo snapshot.
|
||||
Plater::TakeSnapshot snapshot(this, _L("Load Project") + ": " + wxString::FromUTF8(into_path(filename).stem().string().c_str()));
|
||||
Plater::TakeSnapshot snapshot(this, _L("Load Project") + ": " + wxString::FromUTF8(into_path(filename).stem().string().c_str()), UndoRedo::SnapshotType::ProjectSeparator);
|
||||
|
||||
p->reset();
|
||||
|
||||
@ -5222,7 +5256,8 @@ bool Plater::load_files(const wxArrayString& filenames)
|
||||
|
||||
switch (load_type) {
|
||||
case LoadType::OpenProject: {
|
||||
load_project(from_path(*it));
|
||||
if (wxGetApp().can_load_project())
|
||||
load_project(from_path(*it));
|
||||
break;
|
||||
}
|
||||
case LoadType::LoadGeometry: {
|
||||
@ -5957,6 +5992,8 @@ void Plater::eject_drive()
|
||||
|
||||
void Plater::take_snapshot(const std::string &snapshot_name) { p->take_snapshot(snapshot_name); }
|
||||
void Plater::take_snapshot(const wxString &snapshot_name) { p->take_snapshot(snapshot_name); }
|
||||
void Plater::take_snapshot(const std::string &snapshot_name, UndoRedo::SnapshotType snapshot_type) { p->take_snapshot(snapshot_name, snapshot_type); }
|
||||
void Plater::take_snapshot(const wxString &snapshot_name, UndoRedo::SnapshotType snapshot_type) { p->take_snapshot(snapshot_name, snapshot_type); }
|
||||
void Plater::suppress_snapshots() { p->suppress_snapshots(); }
|
||||
void Plater::allow_snapshots() { p->allow_snapshots(); }
|
||||
void Plater::undo() { p->undo(); }
|
||||
@ -6344,6 +6381,7 @@ bool Plater::set_printer_technology(PrinterTechnology printer_technology)
|
||||
p->sidebar->get_searcher().set_printer_technology(printer_technology);
|
||||
|
||||
p->notification_manager->set_fff(printer_technology == ptFFF);
|
||||
p->notification_manager->set_slicing_progress_hidden();
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -6507,12 +6545,7 @@ void Plater::search(bool plater_is_active)
|
||||
canvas3D()->on_char(evt);
|
||||
}
|
||||
else
|
||||
{
|
||||
wxPoint pos = this->ClientToScreen(wxPoint(0, 0));
|
||||
pos.x += em_unit(this) * 40;
|
||||
pos.y += em_unit(this) * 4;
|
||||
p->sidebar->get_searcher().search_dialog->Popup(pos);
|
||||
}
|
||||
p->sidebar->get_searcher().show_dialog();
|
||||
}
|
||||
|
||||
void Plater::msw_rescale()
|
||||
|
@ -37,6 +37,7 @@ using ModelInstancePtrs = std::vector<ModelInstance*>;
|
||||
|
||||
namespace UndoRedo {
|
||||
class Stack;
|
||||
enum class SnapshotType : unsigned char;
|
||||
struct Snapshot;
|
||||
}
|
||||
|
||||
@ -140,7 +141,7 @@ public:
|
||||
|
||||
bool is_project_dirty() const;
|
||||
void update_project_dirty_from_presets();
|
||||
int save_project_if_dirty();
|
||||
int save_project_if_dirty(const wxString& reason);
|
||||
void reset_project_dirty_after_save();
|
||||
void reset_project_dirty_initial_presets();
|
||||
#if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW
|
||||
@ -240,6 +241,9 @@ public:
|
||||
|
||||
void take_snapshot(const std::string &snapshot_name);
|
||||
void take_snapshot(const wxString &snapshot_name);
|
||||
void take_snapshot(const std::string &snapshot_name, UndoRedo::SnapshotType snapshot_type);
|
||||
void take_snapshot(const wxString &snapshot_name, UndoRedo::SnapshotType snapshot_type);
|
||||
|
||||
void undo();
|
||||
void redo();
|
||||
void undo_to(int selection);
|
||||
@ -391,6 +395,12 @@ public:
|
||||
m_plater->take_snapshot(snapshot_name);
|
||||
m_plater->suppress_snapshots();
|
||||
}
|
||||
TakeSnapshot(Plater *plater, const wxString &snapshot_name, UndoRedo::SnapshotType snapshot_type) : m_plater(plater)
|
||||
{
|
||||
m_plater->take_snapshot(snapshot_name, snapshot_type);
|
||||
m_plater->suppress_snapshots();
|
||||
}
|
||||
|
||||
~TakeSnapshot()
|
||||
{
|
||||
m_plater->allow_snapshots();
|
||||
|
@ -73,7 +73,7 @@ void PreferencesDialog::build(size_t selected_tab)
|
||||
// Add "General" tab
|
||||
m_optgroup_general = create_options_tab(_L("General"), tabs);
|
||||
m_optgroup_general->m_on_change = [this](t_config_option_key opt_key, boost::any value) {
|
||||
if (opt_key == "default_action_on_close_application" || opt_key == "default_action_on_select_preset")
|
||||
if (opt_key == "default_action_on_close_application" || opt_key == "default_action_on_select_preset" || opt_key == "default_action_on_new_project")
|
||||
m_values[opt_key] = boost::any_cast<bool>(value) ? "none" : "discard";
|
||||
else
|
||||
m_values[opt_key] = boost::any_cast<bool>(value) ? "1" : "0";
|
||||
@ -186,19 +186,28 @@ void PreferencesDialog::build(size_t selected_tab)
|
||||
option = Option(def, "single_instance");
|
||||
m_optgroup_general->append_single_option_line(option);
|
||||
|
||||
def.label = L("Ask for unsaved changes when closing application");
|
||||
def.label = L("Ask for unsaved changes when closing application or loading new project");
|
||||
def.type = coBool;
|
||||
def.tooltip = L("When closing the application, always ask for unsaved changes");
|
||||
def.tooltip = L("Always ask for unsaved changes, when: \n"
|
||||
"- Closing PrusaSlicer while some presets are modified,\n"
|
||||
"- Loading a new project while some presets are modified");
|
||||
def.set_default_value(new ConfigOptionBool{ app_config->get("default_action_on_close_application") == "none" });
|
||||
option = Option(def, "default_action_on_close_application");
|
||||
m_optgroup_general->append_single_option_line(option);
|
||||
|
||||
def.label = L("Ask for unsaved changes when selecting new preset");
|
||||
def.type = coBool;
|
||||
def.tooltip = L("Always ask for unsaved changes when selecting new preset");
|
||||
def.tooltip = L("Always ask for unsaved changes when selecting new preset or resetting a preset");
|
||||
def.set_default_value(new ConfigOptionBool{ app_config->get("default_action_on_select_preset") == "none" });
|
||||
option = Option(def, "default_action_on_select_preset");
|
||||
m_optgroup_general->append_single_option_line(option);
|
||||
|
||||
def.label = L("Ask for unsaved changes when creating new project");
|
||||
def.type = coBool;
|
||||
def.tooltip = L("Always ask for unsaved changes when creating new project");
|
||||
def.set_default_value(new ConfigOptionBool{ app_config->get("default_action_on_new_project") == "none" });
|
||||
option = Option(def, "default_action_on_new_project");
|
||||
m_optgroup_general->append_single_option_line(option);
|
||||
}
|
||||
#ifdef _WIN32
|
||||
else {
|
||||
|
@ -195,8 +195,10 @@ void ProjectDirtyStateManager::update_from_undo_redo_stack(UpdateType type)
|
||||
void ProjectDirtyStateManager::update_from_presets()
|
||||
{
|
||||
m_state.presets = false;
|
||||
for (const auto& [type, name] : wxGetApp().get_selected_presets()) {
|
||||
m_state.presets |= !m_initial_presets[type].empty() && m_initial_presets[type] != name;
|
||||
// check switching of the presets only for exist/loaded project, but not for new
|
||||
if (!wxGetApp().plater()->get_project_filename().IsEmpty()) {
|
||||
for (const auto& [type, name] : wxGetApp().get_selected_presets())
|
||||
m_state.presets |= !m_initial_presets[type].empty() && m_initial_presets[type] != name;
|
||||
}
|
||||
m_state.presets |= wxGetApp().has_unsaved_preset_changes();
|
||||
wxGetApp().mainframe->update_title();
|
||||
|
@ -201,9 +201,7 @@ namespace search_for_drives_internal
|
||||
struct stat buf;
|
||||
stat(path.c_str(), &buf);
|
||||
uid_t uid = buf.st_uid;
|
||||
std::string username(std::getenv("USER"));
|
||||
struct passwd *pw = getpwuid(uid);
|
||||
if (pw != 0 && pw->pw_name == username)
|
||||
if (getuid() == uid)
|
||||
out.emplace_back(DriveData{ boost::filesystem::basename(boost::filesystem::path(path)), path });
|
||||
}
|
||||
}
|
||||
@ -245,7 +243,7 @@ std::vector<DriveData> RemovableDriveManager::search_for_removable_drives() cons
|
||||
search_for_drives_internal::search_path("/media/*", "/media", current_drives);
|
||||
|
||||
//search_path("/Volumes/*", "/Volumes");
|
||||
std::string path(std::getenv("USER"));
|
||||
std::string path = wxGetUserId().ToUTF8().data();
|
||||
std::string pp(path);
|
||||
|
||||
//search /media/USERNAME/* folder
|
||||
|
@ -24,6 +24,8 @@ using boost::optional;
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
wxDEFINE_EVENT(wxCUSTOMEVT_JUMP_TO_OPTION, wxCommandEvent);
|
||||
|
||||
using GUI::from_u8;
|
||||
using GUI::into_u8;
|
||||
|
||||
@ -289,13 +291,10 @@ bool OptionsSearcher::search(const std::string& search, bool force/* = false*/)
|
||||
|
||||
OptionsSearcher::OptionsSearcher()
|
||||
{
|
||||
search_dialog = new SearchDialog(this);
|
||||
}
|
||||
|
||||
OptionsSearcher::~OptionsSearcher()
|
||||
{
|
||||
if (search_dialog)
|
||||
search_dialog->Destroy();
|
||||
}
|
||||
|
||||
void OptionsSearcher::init(std::vector<InputInfo> input_values)
|
||||
@ -386,6 +385,22 @@ Option OptionsSearcher::get_option(const std::string& opt_key, const wxString& l
|
||||
return create_option(opt_key, label, type, gc);
|
||||
}
|
||||
|
||||
void OptionsSearcher::show_dialog()
|
||||
{
|
||||
if (!search_dialog) {
|
||||
search_dialog = new SearchDialog(this);
|
||||
|
||||
auto parent = search_dialog->GetParent();
|
||||
wxPoint pos = parent->ClientToScreen(wxPoint(0, 0));
|
||||
pos.x += em_unit(parent) * 40;
|
||||
pos.y += em_unit(parent) * 4;
|
||||
|
||||
search_dialog->SetPosition(pos);
|
||||
}
|
||||
|
||||
search_dialog->Popup();
|
||||
}
|
||||
|
||||
void OptionsSearcher::add_key(const std::string& opt_key, Preset::Type type, const wxString& group, const wxString& category)
|
||||
{
|
||||
groups_and_categories[get_key(opt_key, type)] = GroupAndCategory{group, category};
|
||||
@ -405,7 +420,7 @@ static const std::map<const char, int> icon_idxs = {
|
||||
};
|
||||
|
||||
SearchDialog::SearchDialog(OptionsSearcher* searcher)
|
||||
: GUI::DPIDialog(NULL, wxID_ANY, _L("Search"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER),
|
||||
: GUI::DPIDialog(GUI::wxGetApp().tab_panel(), wxID_ANY, _L("Search"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER),
|
||||
searcher(searcher)
|
||||
{
|
||||
SetFont(GUI::wxGetApp().normal_font());
|
||||
@ -506,7 +521,8 @@ void SearchDialog::Popup(wxPoint position /*= wxDefaultPosition*/)
|
||||
if (check_english)
|
||||
check_english->SetValue(params.english);
|
||||
|
||||
this->SetPosition(position);
|
||||
if (position != wxDefaultPosition)
|
||||
this->SetPosition(position);
|
||||
this->ShowModal();
|
||||
}
|
||||
|
||||
@ -514,9 +530,16 @@ void SearchDialog::ProcessSelection(wxDataViewItem selection)
|
||||
{
|
||||
if (!selection.IsOk())
|
||||
return;
|
||||
|
||||
GUI::wxGetApp().sidebar().jump_to_option(search_list_model->GetRow(selection));
|
||||
this->EndModal(wxID_CLOSE);
|
||||
|
||||
// If call GUI::wxGetApp().sidebar.jump_to_option() directly from here,
|
||||
// then mainframe will not have focus and found option will not be "active" (have cursor) as a result
|
||||
// SearchDialog have to be closed and have to lose a focus
|
||||
// and only after that jump_to_option() function can be called
|
||||
// So, post event to plater:
|
||||
wxCommandEvent event(wxCUSTOMEVT_JUMP_TO_OPTION);
|
||||
event.SetInt(search_list_model->GetRow(selection));
|
||||
wxPostEvent(GUI::wxGetApp().plater(), event);
|
||||
}
|
||||
|
||||
void SearchDialog::OnInputText(wxCommandEvent&)
|
||||
|
@ -22,6 +22,8 @@
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
wxDECLARE_EVENT(wxCUSTOMEVT_JUMP_TO_OPTION, wxCommandEvent);
|
||||
|
||||
namespace Search{
|
||||
|
||||
class SearchDialog;
|
||||
@ -134,6 +136,8 @@ public:
|
||||
return o1.key < o2.key; });
|
||||
}
|
||||
void sort_options_by_label() { sort_options(); }
|
||||
|
||||
void show_dialog();
|
||||
};
|
||||
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "Gizmos/GLGizmoBase.hpp"
|
||||
#include "Camera.hpp"
|
||||
#include "Plater.hpp"
|
||||
#include "slic3r/Utils/UndoRedo.hpp"
|
||||
|
||||
#include "libslic3r/LocalesUtils.hpp"
|
||||
#include "libslic3r/Model.hpp"
|
||||
@ -162,7 +163,7 @@ void Selection::add(unsigned int volume_idx, bool as_single_selection, bool chec
|
||||
needs_reset |= is_any_modifier() && !volume->is_modifier;
|
||||
|
||||
if (!already_contained || needs_reset) {
|
||||
wxGetApp().plater()->take_snapshot(_L("Selection-Add"));
|
||||
wxGetApp().plater()->take_snapshot(_L("Selection-Add"), UndoRedo::SnapshotType::Selection);
|
||||
|
||||
if (needs_reset)
|
||||
clear();
|
||||
@ -203,7 +204,7 @@ void Selection::remove(unsigned int volume_idx)
|
||||
if (!contains_volume(volume_idx))
|
||||
return;
|
||||
|
||||
wxGetApp().plater()->take_snapshot(_L("Selection-Remove"));
|
||||
wxGetApp().plater()->take_snapshot(_L("Selection-Remove"), UndoRedo::SnapshotType::Selection);
|
||||
|
||||
GLVolume* volume = (*m_volumes)[volume_idx];
|
||||
|
||||
@ -235,7 +236,7 @@ void Selection::add_object(unsigned int object_idx, bool as_single_selection)
|
||||
(as_single_selection && matches(volume_idxs)))
|
||||
return;
|
||||
|
||||
wxGetApp().plater()->take_snapshot(_L("Selection-Add Object"));
|
||||
wxGetApp().plater()->take_snapshot(_L("Selection-Add Object"), UndoRedo::SnapshotType::Selection);
|
||||
|
||||
// resets the current list if needed
|
||||
if (as_single_selection)
|
||||
@ -254,7 +255,7 @@ void Selection::remove_object(unsigned int object_idx)
|
||||
if (!m_valid)
|
||||
return;
|
||||
|
||||
wxGetApp().plater()->take_snapshot(_L("Selection-Remove Object"));
|
||||
wxGetApp().plater()->take_snapshot(_L("Selection-Remove Object"), UndoRedo::SnapshotType::Selection);
|
||||
|
||||
do_remove_object(object_idx);
|
||||
|
||||
@ -272,7 +273,7 @@ void Selection::add_instance(unsigned int object_idx, unsigned int instance_idx,
|
||||
(as_single_selection && matches(volume_idxs)))
|
||||
return;
|
||||
|
||||
wxGetApp().plater()->take_snapshot(_L("Selection-Add Instance"));
|
||||
wxGetApp().plater()->take_snapshot(_L("Selection-Add Instance"), UndoRedo::SnapshotType::Selection);
|
||||
|
||||
// resets the current list if needed
|
||||
if (as_single_selection)
|
||||
@ -291,7 +292,7 @@ void Selection::remove_instance(unsigned int object_idx, unsigned int instance_i
|
||||
if (!m_valid)
|
||||
return;
|
||||
|
||||
wxGetApp().plater()->take_snapshot(_L("Selection-Remove Instance"));
|
||||
wxGetApp().plater()->take_snapshot(_L("Selection-Remove Instance"), UndoRedo::SnapshotType::Selection);
|
||||
|
||||
do_remove_instance(object_idx, instance_idx);
|
||||
|
||||
@ -388,7 +389,7 @@ void Selection::add_all()
|
||||
if ((unsigned int)m_list.size() == count)
|
||||
return;
|
||||
|
||||
wxGetApp().plater()->take_snapshot(_(L("Selection-Add All")));
|
||||
wxGetApp().plater()->take_snapshot(_(L("Selection-Add All")), UndoRedo::SnapshotType::Selection);
|
||||
|
||||
m_mode = Instance;
|
||||
clear();
|
||||
@ -413,7 +414,7 @@ void Selection::remove_all()
|
||||
// Not taking the snapshot with non-empty Redo stack will likely be more confusing than losing the Redo stack.
|
||||
// Let's wait for user feedback.
|
||||
// if (!wxGetApp().plater()->can_redo())
|
||||
wxGetApp().plater()->take_snapshot(_L("Selection-Remove All"));
|
||||
wxGetApp().plater()->take_snapshot(_L("Selection-Remove All"), UndoRedo::SnapshotType::Selection);
|
||||
|
||||
m_mode = Instance;
|
||||
clear();
|
||||
|
@ -501,6 +501,8 @@ void Tab::OnActivate()
|
||||
m_presets_choice->SetMinSize(ok_sz);
|
||||
m_presets_choice->SetSize(ok_sz);
|
||||
GetSizer()->GetItem(size_t(0))->GetSizer()->Layout();
|
||||
if (wxGetApp().tabs_as_menu())
|
||||
m_presets_choice->update();
|
||||
}
|
||||
#endif // _MSW_DARK_MODE
|
||||
Refresh();
|
||||
@ -1186,11 +1188,21 @@ void Tab::activate_option(const std::string& opt_key, const wxString& category)
|
||||
break;
|
||||
}
|
||||
|
||||
auto set_focus = [](wxWindow* win) {
|
||||
win->SetFocus();
|
||||
#ifdef WIN32
|
||||
if (wxTextCtrl* text = dynamic_cast<wxTextCtrl*>(win))
|
||||
text->SetSelection(-1, -1);
|
||||
else if (wxSpinCtrl* spin = dynamic_cast<wxSpinCtrl*>(win))
|
||||
spin->SetSelection(-1, -1);
|
||||
#endif // WIN32
|
||||
};
|
||||
|
||||
Field* field = get_field(opt_key);
|
||||
|
||||
// focused selected field
|
||||
if (field)
|
||||
field->getWindow()->SetFocus();
|
||||
set_focus(field->getWindow());
|
||||
else if (category == "Single extruder MM setup") {
|
||||
// When we show and hide "Single extruder MM setup" page,
|
||||
// related options are still in the search list
|
||||
@ -1198,7 +1210,7 @@ void Tab::activate_option(const std::string& opt_key, const wxString& category)
|
||||
// as a "way" to show hidden page again
|
||||
field = get_field("single_extruder_multi_material");
|
||||
if (field)
|
||||
field->getWindow()->SetFocus();
|
||||
set_focus(field->getWindow());
|
||||
}
|
||||
|
||||
m_highlighter.init(get_custom_ctrl_with_blinking_ptr(opt_key));
|
||||
@ -1216,12 +1228,20 @@ void Tab::cache_config_diff(const std::vector<std::string>& selected_options)
|
||||
|
||||
void Tab::apply_config_from_cache()
|
||||
{
|
||||
bool was_applied = false;
|
||||
// check and apply extruders count for printer preset
|
||||
if (m_type == Preset::TYPE_PRINTER)
|
||||
was_applied = static_cast<TabPrinter*>(this)->apply_extruder_cnt_from_cache();
|
||||
|
||||
if (!m_cache_config.empty()) {
|
||||
m_presets->get_edited_preset().config.apply(m_cache_config);
|
||||
m_cache_config.clear();
|
||||
|
||||
update_dirty();
|
||||
was_applied = true;
|
||||
}
|
||||
|
||||
if (was_applied)
|
||||
update_dirty();
|
||||
}
|
||||
|
||||
|
||||
@ -3322,10 +3342,6 @@ void Tab::select_preset(std::string preset_name, bool delete_current /*=false*/,
|
||||
m_dependent_tabs = { Preset::Type::TYPE_SLA_PRINT, Preset::Type::TYPE_SLA_MATERIAL };
|
||||
}
|
||||
|
||||
// check and apply extruders count for printer preset
|
||||
if (m_type == Preset::TYPE_PRINTER)
|
||||
static_cast<TabPrinter*>(this)->apply_extruder_cnt_from_cache();
|
||||
|
||||
// check if there is something in the cache to move to the new selected preset
|
||||
apply_config_from_cache();
|
||||
|
||||
@ -3862,15 +3878,17 @@ void TabPrinter::cache_extruder_cnt()
|
||||
m_cache_extruder_count = m_extruders_count;
|
||||
}
|
||||
|
||||
void TabPrinter::apply_extruder_cnt_from_cache()
|
||||
bool TabPrinter::apply_extruder_cnt_from_cache()
|
||||
{
|
||||
if (m_presets->get_edited_preset().printer_technology() == ptSLA)
|
||||
return;
|
||||
return false;
|
||||
|
||||
if (m_cache_extruder_count > 0) {
|
||||
m_presets->get_edited_preset().set_num_extruders(m_cache_extruder_count);
|
||||
m_cache_extruder_count = 0;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Tab::validate_custom_gcodes()
|
||||
|
@ -469,7 +469,7 @@ public:
|
||||
|
||||
wxSizer* create_bed_shape_widget(wxWindow* parent);
|
||||
void cache_extruder_cnt();
|
||||
void apply_extruder_cnt_from_cache();
|
||||
bool apply_extruder_cnt_from_cache();
|
||||
};
|
||||
|
||||
class TabSLAMaterial : public Tab
|
||||
|
@ -727,14 +727,21 @@ void DiffViewCtrl::item_value_changed(wxDataViewEvent& event)
|
||||
m_empty_selection = selected_options().empty();
|
||||
}
|
||||
|
||||
std::vector<std::string> DiffViewCtrl::unselected_options(Preset::Type type)
|
||||
bool DiffViewCtrl::has_unselected_options()
|
||||
{
|
||||
for (auto item : m_items_map)
|
||||
if (!model->IsEnabledItem(item.first))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<std::string> DiffViewCtrl::options(Preset::Type type, bool selected)
|
||||
{
|
||||
std::vector<std::string> ret;
|
||||
|
||||
for (auto item : m_items_map) {
|
||||
if (item.second.opt_key == "extruders_count")
|
||||
continue;
|
||||
if (item.second.type == type && !model->IsEnabledItem(item.first))
|
||||
if (item.second.type == type && model->IsEnabledItem(item.first) == selected)
|
||||
ret.emplace_back(get_pure_opt_key(item.second.opt_key));
|
||||
}
|
||||
|
||||
@ -757,20 +764,24 @@ std::vector<std::string> DiffViewCtrl::selected_options()
|
||||
// UnsavedChangesDialog
|
||||
//------------------------------------------
|
||||
|
||||
UnsavedChangesDialog::UnsavedChangesDialog(const wxString& header, const wxString& caption/* = wxString()*/)
|
||||
: DPIDialog(static_cast<wxWindow*>(wxGetApp().mainframe), wxID_ANY, (caption.IsEmpty() ? _L("PrusaSlicer is closing") : caption) + ": " + _L("Unsaved Changes"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
|
||||
{
|
||||
m_app_config_key = "default_action_on_close_application";
|
||||
static std::string none{"none"};
|
||||
|
||||
UnsavedChangesDialog::UnsavedChangesDialog(const wxString& caption, const wxString& header,
|
||||
const std::string& app_config_key, int act_buttons)
|
||||
: DPIDialog(static_cast<wxWindow*>(wxGetApp().mainframe), wxID_ANY, caption + ": " + _L("Unsaved Changes"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER),
|
||||
m_app_config_key(app_config_key),
|
||||
m_buttons(act_buttons)
|
||||
{
|
||||
build(Preset::TYPE_INVALID, nullptr, "", header);
|
||||
|
||||
const std::string& def_action = wxGetApp().app_config->get(m_app_config_key);
|
||||
if (def_action == "none")
|
||||
const std::string& def_action = m_app_config_key.empty() ? none : wxGetApp().app_config->get(m_app_config_key);
|
||||
if (def_action == none)
|
||||
this->CenterOnScreen();
|
||||
else {
|
||||
m_exit_action = def_action == ActSave ? Action::Save : Action::Discard;
|
||||
if (m_exit_action == Action::Save)
|
||||
save(nullptr);
|
||||
m_exit_action = def_action == ActTransfer ? Action::Transfer :
|
||||
def_action == ActSave ? Action::Save : Action::Discard;
|
||||
if (m_exit_action != Action::Discard)
|
||||
save(nullptr, m_exit_action == Action::Save);
|
||||
}
|
||||
}
|
||||
|
||||
@ -782,7 +793,7 @@ UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type, PresetCollection*
|
||||
build(type, dependent_presets, new_selected_preset);
|
||||
|
||||
const std::string& def_action = wxGetApp().app_config->get(m_app_config_key);
|
||||
if (def_action == "none") {
|
||||
if (def_action == none) {
|
||||
if (wxGetApp().mainframe->is_dlg_layout() && wxGetApp().mainframe->m_settings_dialog.HasFocus())
|
||||
this->SetPosition(wxGetApp().mainframe->m_settings_dialog.GetPosition());
|
||||
this->CenterOnScreen();
|
||||
@ -833,7 +844,8 @@ void UnsavedChangesDialog::build(Preset::Type type, PresetCollection* dependent_
|
||||
|
||||
(*btn)->Bind(wxEVT_BUTTON, [this, close_act, dependent_presets](wxEvent&) {
|
||||
update_config(close_act);
|
||||
if (close_act == Action::Save && !save(dependent_presets))
|
||||
bool save_names_and_types = close_act == Action::Save || (close_act == Action::Transfer && ActionButtons::KEEP & m_buttons);
|
||||
if (save_names_and_types && !save(dependent_presets, close_act == Action::Save))
|
||||
return;
|
||||
close(close_act);
|
||||
});
|
||||
@ -842,13 +854,26 @@ void UnsavedChangesDialog::build(Preset::Type type, PresetCollection* dependent_
|
||||
(*btn)->Bind(wxEVT_LEAVE_WINDOW, [this](wxMouseEvent& e) { show_info_line(Action::Undef); e.Skip(); });
|
||||
};
|
||||
|
||||
const PresetCollection* switched_presets = type == Preset::TYPE_INVALID ? nullptr : wxGetApp().get_tab(type)->get_presets();
|
||||
if (dependent_presets && switched_presets && (type == dependent_presets->type() ?
|
||||
dependent_presets->get_edited_preset().printer_technology() == dependent_presets->find_preset(new_selected_preset)->printer_technology() :
|
||||
switched_presets->get_edited_preset().printer_technology() == switched_presets->find_preset(new_selected_preset)->printer_technology()))
|
||||
add_btn(&m_transfer_btn, m_move_btn_id, "paste_menu", Action::Transfer, _L("Transfer"));
|
||||
add_btn(&m_discard_btn, m_continue_btn_id, dependent_presets ? "switch_presets" : "exit", Action::Discard, _L("Discard"), false);
|
||||
add_btn(&m_save_btn, m_save_btn_id, "save", Action::Save, _L("Save"));
|
||||
// "Transfer" / "Keep" button
|
||||
if (ActionButtons::TRANSFER & m_buttons) {
|
||||
const PresetCollection* switched_presets = type == Preset::TYPE_INVALID ? nullptr : wxGetApp().get_tab(type)->get_presets();
|
||||
if (dependent_presets && switched_presets && (type == dependent_presets->type() ?
|
||||
dependent_presets->get_edited_preset().printer_technology() == dependent_presets->find_preset(new_selected_preset)->printer_technology() :
|
||||
switched_presets->get_edited_preset().printer_technology() == switched_presets->find_preset(new_selected_preset)->printer_technology()))
|
||||
add_btn(&m_transfer_btn, m_move_btn_id, "paste_menu", Action::Transfer, switched_presets->get_edited_preset().name == new_selected_preset ? _L("Keep") : _L("Transfer"));
|
||||
}
|
||||
if (!m_transfer_btn && (ActionButtons::KEEP & m_buttons))
|
||||
add_btn(&m_transfer_btn, m_move_btn_id, "paste_menu", Action::Transfer, _L("Keep"));
|
||||
|
||||
{ // "Don't save" / "Discard" button
|
||||
std::string btn_icon = (ActionButtons::DONT_SAVE & m_buttons) ? "" : (dependent_presets || (ActionButtons::KEEP & m_buttons)) ? "switch_presets" : "exit";
|
||||
wxString btn_label = (ActionButtons::DONT_SAVE & m_buttons) ? _L("Don't save") : _L("Discard");
|
||||
add_btn(&m_discard_btn, m_continue_btn_id, btn_icon, Action::Discard, btn_label, false);
|
||||
}
|
||||
|
||||
// "Save" button
|
||||
if (ActionButtons::SAVE & m_buttons)
|
||||
add_btn(&m_save_btn, m_save_btn_id, "save", Action::Save, _L("Save"));
|
||||
|
||||
ScalableButton* cancel_btn = new ScalableButton(this, wxID_CANCEL, "cross", _L("Cancel"), wxDefaultSize, wxDefaultPosition, wxBORDER_DEFAULT, true, 24);
|
||||
buttons->Add(cancel_btn, 1, wxLEFT|wxRIGHT, 5);
|
||||
@ -859,34 +884,42 @@ void UnsavedChangesDialog::build(Preset::Type type, PresetCollection* dependent_
|
||||
m_info_line->SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Bold());
|
||||
m_info_line->Hide();
|
||||
|
||||
m_remember_choice = new wxCheckBox(this, wxID_ANY, _L("Remember my choice"));
|
||||
m_remember_choice->SetValue(wxGetApp().app_config->get(m_app_config_key) != "none");
|
||||
m_remember_choice->Bind(wxEVT_CHECKBOX, [type, this](wxCommandEvent& evt)
|
||||
{
|
||||
if (!evt.IsChecked())
|
||||
return;
|
||||
wxString preferences_item = type == Preset::TYPE_INVALID ? _L("Ask for unsaved changes when closing application") :
|
||||
_L("Ask for unsaved changes when selecting new preset");
|
||||
wxString msg =
|
||||
_L("PrusaSlicer will remember your action.") + "\n\n" +
|
||||
(type == Preset::TYPE_INVALID ?
|
||||
_L("You will not be asked about the unsaved changes the next time you close PrusaSlicer.") :
|
||||
_L("You will not be asked about the unsaved changes the next time you switch a preset.")) + "\n\n" +
|
||||
format_wxstr(_L("Visit \"Preferences\" and check \"%1%\"\nto be asked about unsaved changes again."), preferences_item);
|
||||
if (!m_app_config_key.empty()) {
|
||||
m_remember_choice = new wxCheckBox(this, wxID_ANY, _L("Remember my choice"));
|
||||
m_remember_choice->SetValue(wxGetApp().app_config->get(m_app_config_key) != none);
|
||||
m_remember_choice->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent& evt)
|
||||
{
|
||||
if (!evt.IsChecked())
|
||||
return;
|
||||
wxString preferences_item = m_app_config_key == "default_action_on_new_project" ? _L("Ask for unsaved changes when creating new project") :
|
||||
m_app_config_key == "default_action_on_select_preset" ? _L("Ask for unsaved changes when selecting new preset") :
|
||||
_L("Ask for unsaved changes when ??closing application??") ;
|
||||
wxString action = m_app_config_key == "default_action_on_new_project" ? _L("You will not be asked about the unsaved changes the next time you create new project") :
|
||||
m_app_config_key == "default_action_on_select_preset" ? _L("You will not be asked about the unsaved changes the next time you switch a preset") :
|
||||
_L("You will not be asked about the unsaved changes the next time you: \n"
|
||||
"- close the application,\n"
|
||||
"- load project,\n"
|
||||
"- process Undo / Redo with change of print technologie,\n"
|
||||
"- take/load snapshot,\n"
|
||||
"- load config file/bundle,\n"
|
||||
"- export config_bundle") ;
|
||||
wxString msg = _L("PrusaSlicer will remember your action.") + "\n\n" + action + "\n\n" +
|
||||
format_wxstr(_L("Visit \"Preferences\" and check \"%1%\"\nto be asked about unsaved changes again."), preferences_item);
|
||||
|
||||
//wxMessageDialog dialog(nullptr, msg, _L("PrusaSlicer: Don't ask me again"), wxOK | wxCANCEL | wxICON_INFORMATION);
|
||||
MessageDialog dialog(nullptr, msg, _L("PrusaSlicer: Don't ask me again"), wxOK | wxCANCEL | wxICON_INFORMATION);
|
||||
if (dialog.ShowModal() == wxID_CANCEL)
|
||||
m_remember_choice->SetValue(false);
|
||||
});
|
||||
MessageDialog dialog(nullptr, msg, _L("PrusaSlicer: Don't ask me again"), wxOK | wxCANCEL | wxICON_INFORMATION);
|
||||
if (dialog.ShowModal() == wxID_CANCEL)
|
||||
m_remember_choice->SetValue(false);
|
||||
});
|
||||
}
|
||||
|
||||
wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
topSizer->Add(m_action_line,0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border);
|
||||
topSizer->Add(m_tree, 1, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border);
|
||||
topSizer->Add(m_info_line, 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, 2*border);
|
||||
topSizer->Add(buttons, 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border);
|
||||
topSizer->Add(m_remember_choice, 0, wxEXPAND | wxALL, border);
|
||||
topSizer->Add(buttons, 0, wxEXPAND | wxALL, border);
|
||||
if (m_remember_choice)
|
||||
topSizer->Add(m_remember_choice, 0, wxEXPAND | wxLEFT | wxBOTTOM | wxRIGHT, border);
|
||||
|
||||
update(type, dependent_presets, new_selected_preset, header);
|
||||
|
||||
@ -905,10 +938,12 @@ void UnsavedChangesDialog::show_info_line(Action action, std::string preset_name
|
||||
if (action == Action::Undef)
|
||||
text = _L("Some fields are too long to fit. Right mouse click reveals the full text.");
|
||||
else if (action == Action::Discard)
|
||||
text = _L("All settings changes will be discarded.");
|
||||
text = ActionButtons::DONT_SAVE & m_buttons ? _L("All settings changes will not be saved") :_L("All settings changes will be discarded.");
|
||||
else {
|
||||
if (preset_name.empty())
|
||||
text = action == Action::Save ? _L("Save the selected options.") : _L("Transfer the selected settings to the newly selected preset.");
|
||||
text = action == Action::Save ? _L("Save the selected options.") :
|
||||
ActionButtons::KEEP & m_buttons ? _L("Keep the selected settings.") :
|
||||
_L("Transfer the selected settings to the newly selected preset.");
|
||||
else
|
||||
text = format_wxstr(
|
||||
action == Action::Save ?
|
||||
@ -927,7 +962,7 @@ void UnsavedChangesDialog::show_info_line(Action action, std::string preset_name
|
||||
|
||||
void UnsavedChangesDialog::update_config(Action action)
|
||||
{
|
||||
if (!m_remember_choice->GetValue())
|
||||
if (!m_remember_choice || !m_remember_choice->GetValue())
|
||||
return;
|
||||
|
||||
std::string act = action == Action::Transfer ? ActTransfer :
|
||||
@ -941,7 +976,7 @@ void UnsavedChangesDialog::close(Action action)
|
||||
this->EndModal(wxID_CLOSE);
|
||||
}
|
||||
|
||||
bool UnsavedChangesDialog::save(PresetCollection* dependent_presets)
|
||||
bool UnsavedChangesDialog::save(PresetCollection* dependent_presets, bool show_save_preset_dialog/* = true*/)
|
||||
{
|
||||
names_and_types.clear();
|
||||
|
||||
@ -979,7 +1014,7 @@ bool UnsavedChangesDialog::save(PresetCollection* dependent_presets)
|
||||
}
|
||||
|
||||
|
||||
if (!types_for_save.empty()) {
|
||||
if (show_save_preset_dialog && !types_for_save.empty()) {
|
||||
SavePresetDialog save_dlg(this, types_for_save);
|
||||
if (save_dlg.ShowModal() != wxID_OK) {
|
||||
m_exit_action = Action::Discard;
|
||||
@ -1164,16 +1199,24 @@ void UnsavedChangesDialog::update(Preset::Type type, PresetCollection* dependent
|
||||
PresetCollection* presets = dependent_presets;
|
||||
|
||||
// activate buttons and labels
|
||||
m_save_btn ->Bind(wxEVT_ENTER_WINDOW, [this, presets] (wxMouseEvent& e) { show_info_line(Action::Save, presets ? presets->get_selected_preset().name : ""); e.Skip(); });
|
||||
if (m_save_btn)
|
||||
m_save_btn ->Bind(wxEVT_ENTER_WINDOW, [this, presets] (wxMouseEvent& e) { show_info_line(Action::Save, presets ? presets->get_selected_preset().name : ""); e.Skip(); });
|
||||
if (m_transfer_btn) {
|
||||
bool is_empty_name = type != dependent_presets->type();
|
||||
m_transfer_btn ->Bind(wxEVT_ENTER_WINDOW, [this, new_selected_preset, is_empty_name] (wxMouseEvent& e) { show_info_line(Action::Transfer, is_empty_name ? "" : new_selected_preset); e.Skip(); });
|
||||
bool is_empty_name = dependent_presets && type != dependent_presets->type();
|
||||
m_transfer_btn->Bind(wxEVT_ENTER_WINDOW, [this, new_selected_preset, is_empty_name](wxMouseEvent& e) { show_info_line(Action::Transfer, is_empty_name ? "" : new_selected_preset); e.Skip(); });
|
||||
}
|
||||
m_discard_btn ->Bind(wxEVT_ENTER_WINDOW, [this] (wxMouseEvent& e) { show_info_line(Action::Discard); e.Skip(); });
|
||||
|
||||
if (m_discard_btn)
|
||||
m_discard_btn ->Bind(wxEVT_ENTER_WINDOW, [this] (wxMouseEvent& e) { show_info_line(Action::Discard); e.Skip(); });
|
||||
|
||||
if (type == Preset::TYPE_INVALID) {
|
||||
m_action_line->SetLabel(header + "\n" + _L("The following presets were modified:"));
|
||||
PrinterTechnology printer_technology = wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology();
|
||||
int presets_cnt = 0;
|
||||
for (Tab* tab : wxGetApp().tabs_list)
|
||||
if (tab->supports_printer_technology(printer_technology) && tab->current_preset_is_dirty())
|
||||
presets_cnt++;
|
||||
m_action_line->SetLabel((header.IsEmpty() ? "" : header + "\n\n") +
|
||||
_L_PLURAL("The following preset was modified",
|
||||
"The following presets were modified", presets_cnt));
|
||||
}
|
||||
else {
|
||||
wxString action_msg;
|
||||
|
@ -220,8 +220,9 @@ public:
|
||||
void context_menu(wxDataViewEvent& event);
|
||||
void item_value_changed(wxDataViewEvent& event);
|
||||
void set_em_unit(int em) { m_em_unit = em; }
|
||||
bool has_unselected_options();
|
||||
|
||||
std::vector<std::string> unselected_options(Preset::Type type);
|
||||
std::vector<std::string> options(Preset::Type type, bool selected);
|
||||
std::vector<std::string> selected_options();
|
||||
};
|
||||
|
||||
@ -261,10 +262,22 @@ class UnsavedChangesDialog : public DPIDialog
|
||||
Action m_exit_action {Action::Undef};
|
||||
// preset names which are modified in SavePresetDialog and related types
|
||||
std::vector<std::pair<std::string, Preset::Type>> names_and_types;
|
||||
// additional action buttons used in dialog
|
||||
int m_buttons { ActionButtons::TRANSFER | ActionButtons::SAVE };
|
||||
|
||||
public:
|
||||
UnsavedChangesDialog(const wxString& header, const wxString& caption = wxString());
|
||||
// Discard and Cancel buttons are always but next buttons are optional
|
||||
enum ActionButtons {
|
||||
TRANSFER = 1,
|
||||
KEEP = 2,
|
||||
SAVE = 4,
|
||||
DONT_SAVE = 8,
|
||||
};
|
||||
|
||||
// show unsaved changes when preset is switching
|
||||
UnsavedChangesDialog(Preset::Type type, PresetCollection* dependent_presets, const std::string& new_selected_preset);
|
||||
// show unsaved changes for all another cases
|
||||
UnsavedChangesDialog(const wxString& caption, const wxString& header, const std::string& app_config_key, int act_buttons);
|
||||
~UnsavedChangesDialog() {}
|
||||
|
||||
void build(Preset::Type type, PresetCollection* dependent_presets, const std::string& new_selected_preset, const wxString& header = "");
|
||||
@ -273,7 +286,8 @@ public:
|
||||
void show_info_line(Action action, std::string preset_name = "");
|
||||
void update_config(Action action);
|
||||
void close(Action action);
|
||||
bool save(PresetCollection* dependent_presets);
|
||||
// save information about saved presets and their types to names_and_types and show SavePresetDialog to set the names for new presets
|
||||
bool save(PresetCollection* dependent_presets, bool show_save_preset_dialog = true);
|
||||
|
||||
bool save_preset() const { return m_exit_action == Action::Save; }
|
||||
bool transfer_changes() const { return m_exit_action == Action::Transfer; }
|
||||
@ -284,8 +298,10 @@ public:
|
||||
// short version of the previous function, for the case, when just one preset is modified
|
||||
std::string get_preset_name() { return names_and_types[0].first; }
|
||||
|
||||
std::vector<std::string> get_unselected_options(Preset::Type type) { return m_tree->unselected_options(type); }
|
||||
std::vector<std::string> get_unselected_options(Preset::Type type) { return m_tree->options(type, false); }
|
||||
std::vector<std::string> get_selected_options (Preset::Type type) { return m_tree->options(type, true); }
|
||||
std::vector<std::string> get_selected_options() { return m_tree->selected_options(); }
|
||||
bool has_unselected_options() { return m_tree->has_unselected_options(); }
|
||||
|
||||
protected:
|
||||
void on_dpi_changed(const wxRect& suggested_rect) override;
|
||||
|
@ -672,10 +672,9 @@ void ModeButton::focus_button(const bool focus)
|
||||
Slic3r::GUI::wxGetApp().normal_font();
|
||||
|
||||
SetFont(new_font);
|
||||
//#ifdef _WIN32
|
||||
// GetParent()->Refresh();
|
||||
//#else
|
||||
#ifndef _WIN32
|
||||
#ifdef _WIN32
|
||||
GetParent()->Refresh(); // force redraw a background of the selected mode button
|
||||
#else
|
||||
SetForegroundColour(wxSystemSettings::GetColour(focus ? wxSYS_COLOUR_BTNTEXT :
|
||||
#if defined (__linux__) && defined (__WXGTK3__)
|
||||
wxSYS_COLOUR_GRAYTEXT
|
||||
|
@ -720,8 +720,13 @@ void PresetUpdater::slic3r_update_notify()
|
||||
}
|
||||
}
|
||||
|
||||
static void reload_configs_update_gui()
|
||||
static bool reload_configs_update_gui()
|
||||
{
|
||||
wxString header = _L("Configuration Updates causes a lost of preset modification.\n"
|
||||
"So, check unsaved changes and save them if necessary.");
|
||||
if (!GUI::wxGetApp().check_and_save_current_preset_changes(_L("Updater is processing"), header, false ))
|
||||
return false;
|
||||
|
||||
// Reload global configuration
|
||||
auto* app_config = GUI::wxGetApp().app_config;
|
||||
// System profiles should not trigger any substitutions, user profiles may trigger substitutions, but these substitutions
|
||||
@ -730,7 +735,8 @@ static void reload_configs_update_gui()
|
||||
GUI::wxGetApp().preset_bundle->load_presets(*app_config, ForwardCompatibilitySubstitutionRule::EnableSilentDisableSystem);
|
||||
GUI::wxGetApp().load_current_presets();
|
||||
GUI::wxGetApp().plater()->set_bed_shape();
|
||||
GUI::wxGetApp().update_wizard_from_config();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
PresetUpdater::UpdateResult PresetUpdater::config_update(const Semver& old_slic3r_version, UpdateParams params) const
|
||||
@ -803,9 +809,9 @@ PresetUpdater::UpdateResult PresetUpdater::config_update(const Semver& old_slic3
|
||||
const auto res = dlg.ShowModal();
|
||||
if (res == wxID_OK) {
|
||||
BOOST_LOG_TRIVIAL(info) << "User wants to update...";
|
||||
if (! p->perform_updates(std::move(updates)))
|
||||
if (! p->perform_updates(std::move(updates)) ||
|
||||
! reload_configs_update_gui())
|
||||
return R_INCOMPAT_EXIT;
|
||||
reload_configs_update_gui();
|
||||
return R_UPDATE_INSTALLED;
|
||||
}
|
||||
else {
|
||||
@ -833,9 +839,9 @@ PresetUpdater::UpdateResult PresetUpdater::config_update(const Semver& old_slic3
|
||||
const auto res = dlg.ShowModal();
|
||||
if (res == wxID_OK) {
|
||||
BOOST_LOG_TRIVIAL(debug) << "User agreed to perform the update";
|
||||
if (! p->perform_updates(std::move(updates)))
|
||||
if (! p->perform_updates(std::move(updates)) ||
|
||||
! reload_configs_update_gui())
|
||||
return R_ALL_CANCELED;
|
||||
reload_configs_update_gui();
|
||||
return R_UPDATE_INSTALLED;
|
||||
}
|
||||
else {
|
||||
@ -886,8 +892,8 @@ void PresetUpdater::on_update_notification_confirm()
|
||||
const auto res = dlg.ShowModal();
|
||||
if (res == wxID_OK) {
|
||||
BOOST_LOG_TRIVIAL(debug) << "User agreed to perform the update";
|
||||
if (p->perform_updates(std::move(p->waiting_updates))) {
|
||||
reload_configs_update_gui();
|
||||
if (p->perform_updates(std::move(p->waiting_updates)) &&
|
||||
reload_configs_update_gui()) {
|
||||
p->has_waiting_updates = false;
|
||||
}
|
||||
}
|
||||
|
@ -556,6 +556,12 @@ public:
|
||||
|
||||
// Snapshot history (names with timestamps).
|
||||
const std::vector<Snapshot>& snapshots() const { return m_snapshots; }
|
||||
const Snapshot& snapshot(size_t time) const {
|
||||
const auto it = std::lower_bound(m_snapshots.cbegin(), m_snapshots.cend(), UndoRedo::Snapshot(time));
|
||||
assert(it != m_snapshots.end() && it->timestamp == time);
|
||||
return *it;
|
||||
}
|
||||
|
||||
// Timestamp of the active snapshot.
|
||||
size_t active_snapshot_time() const { return m_active_snapshot_time; }
|
||||
bool temp_snapshot_active() const { return m_snapshots.back().timestamp == m_active_snapshot_time && ! m_snapshots.back().is_topmost_captured(); }
|
||||
@ -1097,48 +1103,9 @@ bool Stack::redo(Slic3r::Model& model, Slic3r::GUI::GLGizmosManager& gizmos, siz
|
||||
const Selection& Stack::selection_deserialized() const { return pimpl->selection_deserialized(); }
|
||||
|
||||
const std::vector<Snapshot>& Stack::snapshots() const { return pimpl->snapshots(); }
|
||||
const Snapshot& Stack::snapshot(size_t time) const { return pimpl->snapshot(time); }
|
||||
size_t Stack::active_snapshot_time() const { return pimpl->active_snapshot_time(); }
|
||||
bool Stack::temp_snapshot_active() const { return pimpl->temp_snapshot_active(); }
|
||||
|
||||
} // namespace UndoRedo
|
||||
} // namespace Slic3r
|
||||
|
||||
|
||||
//FIXME we should have unit tests for testing serialization of basic types as DynamicPrintConfig.
|
||||
#if 0
|
||||
#include "libslic3r/Config.hpp"
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
namespace Slic3r {
|
||||
bool test_dynamic_print_config_serialization() {
|
||||
FullPrintConfig full_print_config;
|
||||
DynamicPrintConfig cfg;
|
||||
cfg.apply(full_print_config, false);
|
||||
|
||||
std::string serialized;
|
||||
try {
|
||||
std::ostringstream ss;
|
||||
cereal::BinaryOutputArchive oarchive(ss);
|
||||
oarchive(cfg);
|
||||
serialized = ss.str();
|
||||
} catch (std::runtime_error e) {
|
||||
e.what();
|
||||
}
|
||||
|
||||
DynamicPrintConfig cfg2;
|
||||
try {
|
||||
std::stringstream ss(serialized);
|
||||
cereal::BinaryInputArchive iarchive(ss);
|
||||
iarchive(cfg2);
|
||||
} catch (std::runtime_error e) {
|
||||
e.what();
|
||||
}
|
||||
|
||||
if (cfg == cfg2) {
|
||||
printf("Yes!\n");
|
||||
return true;
|
||||
}
|
||||
printf("No!\n");
|
||||
return false;
|
||||
}
|
||||
} // namespace Slic3r
|
||||
#endif
|
||||
|
@ -24,6 +24,23 @@ namespace GUI {
|
||||
|
||||
namespace UndoRedo {
|
||||
|
||||
enum class SnapshotType : unsigned char {
|
||||
// Some action modifying project state.
|
||||
Action,
|
||||
// Selection change at the Plater.
|
||||
Selection,
|
||||
// New project, Reset project, Load project ...
|
||||
ProjectSeparator,
|
||||
// Entering a Gizmo, which opens a secondary Undo / Redo stack.
|
||||
EnteringGizmo,
|
||||
// Leaving a Gizmo, which closes a secondary Undo / Redo stack.
|
||||
// No action modifying a project state was done between EnteringGizmo / LeavingGizmo.
|
||||
LeavingGizmoNoAction,
|
||||
// Leaving a Gizmo, which closes a secondary Undo / Redo stack.
|
||||
// Some action modifying a project state was done between EnteringGizmo / LeavingGizmo.
|
||||
LeavingGizmoWithAction,
|
||||
};
|
||||
|
||||
// Data structure to be stored with each snapshot.
|
||||
// Storing short data (bit masks, ints) with each snapshot instead of being serialized into the Undo / Redo stack
|
||||
// is likely cheaper in term of both the runtime and memory allocation.
|
||||
@ -34,6 +51,7 @@ struct SnapshotData
|
||||
// Constructor is defined in .cpp due to the forward declaration of enum PrinterTechnology.
|
||||
SnapshotData();
|
||||
|
||||
SnapshotType snapshot_type;
|
||||
PrinterTechnology printer_technology;
|
||||
// Bitmap of Flags (see the Flags enum).
|
||||
unsigned int flags;
|
||||
@ -122,10 +140,13 @@ public:
|
||||
// There is one additional snapshot taken at the very end, which indicates the current unnamed state.
|
||||
|
||||
const std::vector<Snapshot>& snapshots() const;
|
||||
const Snapshot& snapshot(size_t time) const;
|
||||
|
||||
// Timestamp of the active snapshot. One of the snapshots of this->snapshots() shall have Snapshot::timestamp equal to this->active_snapshot_time().
|
||||
// The snapshot time indicates start of an operation, which is finished at the time of the following snapshot, therefore
|
||||
// the active snapshot is the successive snapshot. The same logic applies to the time_to_load parameter of undo() and redo() operations.
|
||||
// The active snapshot may be a special placeholder "@@@ Topmost @@@" indicating an uncaptured current state,
|
||||
// or the active snapshot may be an active state to which the application state was undoed or redoed.
|
||||
size_t active_snapshot_time() const;
|
||||
const Snapshot& active_snapshot() const { return this->snapshot(this->active_snapshot_time()); }
|
||||
// Temporary snapshot is active if the topmost snapshot is active and it has not been captured yet.
|
||||
// In that case the Undo action will capture the last snapshot.
|
||||
bool temp_snapshot_active() const;
|
||||
|
@ -3,6 +3,11 @@
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
#include "libslic3r/LocalesUtils.hpp"
|
||||
|
||||
#include <cereal/types/polymorphic.hpp>
|
||||
#include <cereal/types/string.hpp>
|
||||
#include <cereal/types/vector.hpp>
|
||||
#include <cereal/archives/binary.hpp>
|
||||
|
||||
using namespace Slic3r;
|
||||
|
||||
SCENARIO("Generic config validation performs as expected.", "[Config]") {
|
||||
@ -202,3 +207,33 @@ SCENARIO("Config ini load/save interface", "[Config]") {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SCENARIO("DynamicPrintConfig serialization", "[Config]") {
|
||||
WHEN("DynamicPrintConfig is serialized and deserialized") {
|
||||
FullPrintConfig full_print_config;
|
||||
DynamicPrintConfig cfg;
|
||||
cfg.apply(full_print_config, false);
|
||||
|
||||
std::string serialized;
|
||||
try {
|
||||
std::ostringstream ss;
|
||||
cereal::BinaryOutputArchive oarchive(ss);
|
||||
oarchive(cfg);
|
||||
serialized = ss.str();
|
||||
} catch (std::runtime_error e) {
|
||||
e.what();
|
||||
}
|
||||
|
||||
THEN("Config object contains ini file options.") {
|
||||
DynamicPrintConfig cfg2;
|
||||
try {
|
||||
std::stringstream ss(serialized);
|
||||
cereal::BinaryInputArchive iarchive(ss);
|
||||
iarchive(cfg2);
|
||||
} catch (std::runtime_error e) {
|
||||
e.what();
|
||||
}
|
||||
REQUIRE(cfg == cfg2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
set(SLIC3R_APP_NAME "PrusaSlicer")
|
||||
set(SLIC3R_APP_KEY "PrusaSlicer")
|
||||
set(SLIC3R_VERSION "2.4.0-alpha1")
|
||||
set(SLIC3R_VERSION "2.4.0-alpha2")
|
||||
set(SLIC3R_BUILD_ID "PrusaSlicer-${SLIC3R_VERSION}+UNKNOWN")
|
||||
set(SLIC3R_RC_VERSION "2,4,0,0")
|
||||
set(SLIC3R_RC_VERSION_DOTS "2.4.0.0")
|
||||
|
Loading…
Reference in New Issue
Block a user