Merge branch 'master' of https://github.com/prusa3d/PrusaSlicer into et_6dof_camera

This commit is contained in:
Enrico Turri 2020-01-17 08:13:58 +01:00
commit 34aac7e292
17 changed files with 723 additions and 391 deletions

View file

@ -1088,7 +1088,7 @@ namespace Slic3r {
return;
pt::ptree code_tree = main_tree.front().second;
m_model->custom_gcode_per_print_z.clear();
m_model->custom_gcode_per_print_z.gcodes.clear();
for (const auto& code : code_tree)
{
@ -1100,7 +1100,7 @@ namespace Slic3r {
int extruder = tree.get<int> ("<xmlattr>.extruder" );
std::string color = tree.get<std::string> ("<xmlattr>.color" );
m_model->custom_gcode_per_print_z.push_back(Model::CustomGCode{print_z, gcode, extruder, color}) ;
m_model->custom_gcode_per_print_z.gcodes.push_back(Model::CustomGCode{print_z, gcode, extruder, color}) ;
}
}
}
@ -2631,12 +2631,12 @@ bool _3MF_Exporter::_add_custom_gcode_per_print_z_file_to_archive( mz_zip_archiv
{
std::string out = "";
if (!model.custom_gcode_per_print_z.empty())
if (!model.custom_gcode_per_print_z.gcodes.empty())
{
pt::ptree tree;
pt::ptree& main_tree = tree.add("custom_gcodes_per_print_z", "");
for (const Model::CustomGCode& code : model.custom_gcode_per_print_z)
for (const Model::CustomGCode& code : model.custom_gcode_per_print_z.gcodes)
{
pt::ptree& code_tree = main_tree.add("code", "");
// store minX and maxZ

View file

@ -653,7 +653,7 @@ void AMFParserContext::endElement(const char * /* name */)
int extruder = atoi(m_value[2].c_str());
const std::string& color = m_value[3];
m_model.custom_gcode_per_print_z.push_back(Model::CustomGCode{height, gcode, extruder, color});
m_model.custom_gcode_per_print_z.gcodes.push_back(Model::CustomGCode{height, gcode, extruder, color});
for (std::string& val: m_value)
val.clear();
@ -1250,14 +1250,14 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
stream << " </constellation>\n";
}
if (!model->custom_gcode_per_print_z.empty())
if (!model->custom_gcode_per_print_z.gcodes.empty())
{
std::string out = "";
pt::ptree tree;
pt::ptree& main_tree = tree.add("custom_gcodes_per_height", "");
for (const Model::CustomGCode& code : model->custom_gcode_per_print_z)
for (const Model::CustomGCode& code : model->custom_gcode_per_print_z.gcodes)
{
pt::ptree& code_tree = main_tree.add("code", "");
// store minX and maxZ

View file

@ -978,7 +978,7 @@ void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& previ
float volumetric_rate = FLT_MAX;
GCodePreviewData::Range height_range;
GCodePreviewData::Range width_range;
GCodePreviewData::Range feedrate_range;
GCodePreviewData::MultiRange<GCodePreviewData::FeedrateKind> feedrate_range;
GCodePreviewData::Range volumetric_rate_range;
GCodePreviewData::Range fan_speed_range;
@ -1013,7 +1013,7 @@ void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& previ
volumetric_rate = move.data.feedrate * (float)move.data.mm3_per_mm;
height_range.update_from(move.data.height);
width_range.update_from(move.data.width);
feedrate_range.update_from(move.data.feedrate);
feedrate_range.update_from(move.data.feedrate, GCodePreviewData::FeedrateKind::EXTRUSION);
volumetric_rate_range.update_from(volumetric_rate);
fan_speed_range.update_from(move.data.fan_speed);
}
@ -1066,7 +1066,7 @@ void GCodeAnalyzer::_calc_gcode_preview_travel(GCodePreviewData& preview_data, s
GCodePreviewData::Range height_range;
GCodePreviewData::Range width_range;
GCodePreviewData::Range feedrate_range;
GCodePreviewData::MultiRange<GCodePreviewData::FeedrateKind> feedrate_range;
// to avoid to call the callback too often
unsigned int cancel_callback_threshold = (unsigned int)std::max((int)travel_moves->second.size() / 25, 1);
@ -1106,7 +1106,7 @@ void GCodeAnalyzer::_calc_gcode_preview_travel(GCodePreviewData& preview_data, s
extruder_id = move.data.extruder_id;
height_range.update_from(move.data.height);
width_range.update_from(move.data.width);
feedrate_range.update_from(move.data.feedrate);
feedrate_range.update_from(move.data.feedrate, GCodePreviewData::FeedrateKind::TRAVEL);
}
// store last polyline

View file

@ -1,6 +1,5 @@
#include "Analyzer.hpp"
#include "PreviewData.hpp"
#include <float.h>
#include <I18N.hpp>
#include "Utils.hpp"
@ -11,9 +10,7 @@
namespace Slic3r {
const GCodePreviewData::Color GCodePreviewData::Color::Dummy(0.0f, 0.0f, 0.0f, 0.0f);
std::vector<unsigned char> GCodePreviewData::Color::as_bytes() const
std::vector<unsigned char> Color::as_bytes() const
{
std::vector<unsigned char> ret;
for (unsigned int i = 0; i < 4; ++i)
@ -38,20 +35,6 @@ GCodePreviewData::Travel::Polyline::Polyline(EType type, EDirection direction, f
{
}
const GCodePreviewData::Color GCodePreviewData::Range::Default_Colors[Colors_Count] =
{
Color(0.043f, 0.173f, 0.478f, 1.0f),
Color(0.075f, 0.349f, 0.522f, 1.0f),
Color(0.110f, 0.533f, 0.569f, 1.0f),
Color(0.016f, 0.839f, 0.059f, 1.0f),
Color(0.667f, 0.949f, 0.000f, 1.0f),
Color(0.988f, 0.975f, 0.012f, 1.0f),
Color(0.961f, 0.808f, 0.039f, 1.0f),
Color(0.890f, 0.533f, 0.125f, 1.0f),
Color(0.820f, 0.408f, 0.188f, 1.0f),
Color(0.761f, 0.322f, 0.235f, 1.0f)
};
GCodePreviewData::Range::Range()
{
reset();
@ -59,54 +42,52 @@ GCodePreviewData::Range::Range()
void GCodePreviewData::Range::reset()
{
min = FLT_MAX;
max = -FLT_MAX;
min_val = FLT_MAX;
max_val = -FLT_MAX;
}
bool GCodePreviewData::Range::empty() const
{
return min == max;
return min_val >= max_val;
}
void GCodePreviewData::Range::update_from(float value)
{
min = std::min(min, value);
max = std::max(max, value);
min_val = std::min(min_val, value);
max_val = std::max(max_val, value);
}
void GCodePreviewData::Range::update_from(const Range& other)
void GCodePreviewData::Range::update_from(const RangeBase& other)
{
min = std::min(min, other.min);
max = std::max(max, other.max);
min_val = std::min(min_val, other.min());
max_val = std::max(max_val, other.max());
}
void GCodePreviewData::Range::set_from(const Range& other)
float GCodePreviewData::RangeBase::step_size() const
{
min = other.min;
max = other.max;
return (max() - min()) / static_cast<float>(range_rainbow_colors.size() - 1);
}
float GCodePreviewData::Range::step_size() const
{
return (max - min) / (float)(Colors_Count - 1);
}
GCodePreviewData::Color GCodePreviewData::Range::get_color_at(float value) const
Color GCodePreviewData::RangeBase::get_color_at(float value) const
{
if (empty())
return Color::Dummy;
return Color{};
float global_t = (value - min) / step_size();
// Input value scaled to the color range
const float global_t = std::max(0.0f, value - min()) / step_size(); // lower limit of 0.0f
constexpr std::size_t color_max_idx = range_rainbow_colors.size() - 1;
unsigned int low = (unsigned int)global_t;
unsigned int high = clamp((unsigned int)0, Colors_Count - 1, low + 1);
// Compute the two colors just below (low) and above (high) the input value
const std::size_t color_low_idx = std::clamp(static_cast<std::size_t>(global_t), std::size_t{ 0 }, color_max_idx);
const std::size_t color_high_idx = std::clamp(color_low_idx + 1, std::size_t{ 0 }, color_max_idx);
const Color color_low = range_rainbow_colors[color_low_idx];
const Color color_high = range_rainbow_colors[color_high_idx];
Color color_low = colors[low];
Color color_high = colors[high];
// Compute how far the value is between the low and high colors so that they can be interpolated
const float local_t = std::min(global_t - static_cast<float>(color_low_idx), 1.0f); // upper limit of 1.0f
float local_t = global_t - (float)low;
// interpolate in RGB space
// Interpolate between the low and high colors in RGB space to find exactly which color the input value should get
Color ret;
for (unsigned int i = 0; i < 4; ++i)
{
@ -115,13 +96,23 @@ GCodePreviewData::Color GCodePreviewData::Range::get_color_at(float value) const
return ret;
}
GCodePreviewData::LegendItem::LegendItem(const std::string& text, const GCodePreviewData::Color& color)
float GCodePreviewData::Range::min() const
{
return min_val;
}
float GCodePreviewData::Range::max() const
{
return max_val;
}
GCodePreviewData::LegendItem::LegendItem(const std::string& text, const Color& color)
: text(text)
, color(color)
{
}
const GCodePreviewData::Color GCodePreviewData::Extrusion::Default_Extrusion_Role_Colors[erCount] =
const Color GCodePreviewData::Extrusion::Default_Extrusion_Role_Colors[erCount] =
{
Color(0.0f, 0.0f, 0.0f, 1.0f), // erNone
Color(1.0f, 0.0f, 0.0f, 1.0f), // erPerimeter
@ -180,7 +171,7 @@ size_t GCodePreviewData::Extrusion::memory_used() const
const float GCodePreviewData::Travel::Default_Width = 0.075f;
const float GCodePreviewData::Travel::Default_Height = 0.075f;
const GCodePreviewData::Color GCodePreviewData::Travel::Default_Type_Colors[Num_Types] =
const Color GCodePreviewData::Travel::Default_Type_Colors[Num_Types] =
{
Color(0.0f, 0.0f, 0.75f, 1.0f), // Move
Color(0.0f, 0.75f, 0.0f, 1.0f), // Extrude
@ -206,7 +197,7 @@ size_t GCodePreviewData::Travel::memory_used() const
return out;
}
const GCodePreviewData::Color GCodePreviewData::Retraction::Default_Color = GCodePreviewData::Color(1.0f, 1.0f, 1.0f, 1.0f);
const Color GCodePreviewData::Retraction::Default_Color = Color(1.0f, 1.0f, 1.0f, 1.0f);
GCodePreviewData::Retraction::Position::Position(const Vec3crd& position, float width, float height)
: position(position)
@ -238,17 +229,15 @@ GCodePreviewData::GCodePreviewData()
void GCodePreviewData::set_default()
{
::memcpy((void*)ranges.height.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color));
::memcpy((void*)ranges.width.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color));
::memcpy((void*)ranges.feedrate.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color));
::memcpy((void*)ranges.fan_speed.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color));
::memcpy((void*)ranges.volumetric_rate.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color));
extrusion.set_default();
travel.set_default();
retraction.set_default();
unretraction.set_default();
shell.set_default();
// Configure the color range for feedrate to match the default for travels and to enable extrusions since they are always visible
ranges.feedrate.set_mode(FeedrateKind::TRAVEL, travel.is_visible);
ranges.feedrate.set_mode(FeedrateKind::EXTRUSION, true);
}
void GCodePreviewData::reset()
@ -268,32 +257,32 @@ bool GCodePreviewData::empty() const
return extrusion.layers.empty() && travel.polylines.empty() && retraction.positions.empty() && unretraction.positions.empty();
}
GCodePreviewData::Color GCodePreviewData::get_extrusion_role_color(ExtrusionRole role) const
Color GCodePreviewData::get_extrusion_role_color(ExtrusionRole role) const
{
return extrusion.role_colors[role];
}
GCodePreviewData::Color GCodePreviewData::get_height_color(float height) const
Color GCodePreviewData::get_height_color(float height) const
{
return ranges.height.get_color_at(height);
}
GCodePreviewData::Color GCodePreviewData::get_width_color(float width) const
Color GCodePreviewData::get_width_color(float width) const
{
return ranges.width.get_color_at(width);
}
GCodePreviewData::Color GCodePreviewData::get_feedrate_color(float feedrate) const
Color GCodePreviewData::get_feedrate_color(float feedrate) const
{
return ranges.feedrate.get_color_at(feedrate);
}
GCodePreviewData::Color GCodePreviewData::get_fan_speed_color(float fan_speed) const
Color GCodePreviewData::get_fan_speed_color(float fan_speed) const
{
return ranges.fan_speed.get_color_at(fan_speed);
}
GCodePreviewData::Color GCodePreviewData::get_volumetric_rate_color(float rate) const
Color GCodePreviewData::get_volumetric_rate_color(float rate) const
{
return ranges.volumetric_rate.get_color_at(rate);
}
@ -384,16 +373,16 @@ GCodePreviewData::LegendItemsList GCodePreviewData::get_legend_items(const std::
{
struct Helper
{
static void FillListFromRange(LegendItemsList& list, const Range& range, unsigned int decimals, float scale_factor)
static void FillListFromRange(LegendItemsList& list, const RangeBase& range, unsigned int decimals, float scale_factor)
{
list.reserve(Range::Colors_Count);
list.reserve(range_rainbow_colors.size());
float step = range.step_size();
for (int i = Range::Colors_Count - 1; i >= 0; --i)
for (int i = static_cast<int>(range_rainbow_colors.size()) - 1; i >= 0; --i)
{
char buf[1024];
sprintf(buf, "%.*f", decimals, scale_factor * (range.min + (float)i * step));
list.emplace_back(buf, range.colors[i]);
sprintf(buf, "%.*f", decimals, scale_factor * (range.min() + (float)i * step));
list.emplace_back(buf, range_rainbow_colors[i]);
}
}
};
@ -446,8 +435,8 @@ GCodePreviewData::LegendItemsList GCodePreviewData::get_legend_items(const std::
items.reserve(tools_colors_count);
for (unsigned int i = 0; i < tools_colors_count; ++i)
{
GCodePreviewData::Color color;
::memcpy((void*)color.rgba, (const void*)(tool_colors.data() + i * 4), 4 * sizeof(float));
Color color;
::memcpy((void*)color.rgba.data(), (const void*)(tool_colors.data() + i * 4), 4 * sizeof(float));
items.emplace_back((boost::format(Slic3r::I18N::translate(L("Extruder %d"))) % (i + 1)).str(), color);
}
@ -460,7 +449,7 @@ GCodePreviewData::LegendItemsList GCodePreviewData::get_legend_items(const std::
if (color_print_cnt == 1) // means "Default print color"
{
Color color;
::memcpy((void*)color.rgba, (const void*)(tool_colors.data()), 4 * sizeof(float));
::memcpy((void*)color.rgba.data(), (const void*)(tool_colors.data()), 4 * sizeof(float));
items.emplace_back(cp_items[0], color);
break;
@ -472,7 +461,7 @@ GCodePreviewData::LegendItemsList GCodePreviewData::get_legend_items(const std::
for (int i = 0 ; i < color_print_cnt; ++i)
{
Color color;
::memcpy((void*)color.rgba, (const void*)(tool_colors.data() + i * 4), 4 * sizeof(float));
::memcpy((void*)color.rgba.data(), (const void*)(tool_colors.data() + i * 4), 4 * sizeof(float));
items.emplace_back(cp_items[i], color);
}
@ -502,20 +491,20 @@ const std::vector<std::string>& GCodePreviewData::ColorPrintColors()
return color_print;
}
GCodePreviewData::Color operator + (const GCodePreviewData::Color& c1, const GCodePreviewData::Color& c2)
Color operator + (const Color& c1, const Color& c2)
{
return GCodePreviewData::Color(clamp(0.0f, 1.0f, c1.rgba[0] + c2.rgba[0]),
clamp(0.0f, 1.0f, c1.rgba[1] + c2.rgba[1]),
clamp(0.0f, 1.0f, c1.rgba[2] + c2.rgba[2]),
clamp(0.0f, 1.0f, c1.rgba[3] + c2.rgba[3]));
return Color(std::clamp(c1.rgba[0] + c2.rgba[0], 0.0f, 1.0f),
std::clamp(c1.rgba[1] + c2.rgba[1], 0.0f, 1.0f),
std::clamp(c1.rgba[2] + c2.rgba[2], 0.0f, 1.0f),
std::clamp(c1.rgba[3] + c2.rgba[3], 0.0f, 1.0f));
}
GCodePreviewData::Color operator * (float f, const GCodePreviewData::Color& color)
Color operator * (float f, const Color& color)
{
return GCodePreviewData::Color(clamp(0.0f, 1.0f, f * color.rgba[0]),
clamp(0.0f, 1.0f, f * color.rgba[1]),
clamp(0.0f, 1.0f, f * color.rgba[2]),
clamp(0.0f, 1.0f, f * color.rgba[3]));
return Color(std::clamp(f * color.rgba[0], 0.0f, 1.0f),
std::clamp(f * color.rgba[1], 0.0f, 1.0f),
std::clamp(f * color.rgba[2], 0.0f, 1.0f),
std::clamp(f * color.rgba[3], 0.0f, 1.0f));
}
} // namespace Slic3r

View file

@ -5,43 +5,190 @@
#include "../ExtrusionEntity.hpp"
#include "../Point.hpp"
#include <tuple>
#include <array>
#include <vector>
#include <bitset>
#include <cstddef>
#include <algorithm>
#include <string>
#include <float.h>
namespace Slic3r {
// Represents an RGBA color
struct Color
{
std::array<float,4> rgba;
Color(const float *argba)
{
memcpy(this->rgba.data(), argba, sizeof(float) * 4);
}
constexpr Color(float r = 1.f, float g = 1.f, float b = 1.f, float a = 1.f) : rgba{r,g,b,a}
{
// Intentionally empty
}
std::vector<unsigned char> as_bytes() const;
};
Color operator + (const Color& c1, const Color& c2);
Color operator * (float f, const Color& color);
// Default colors for Ranges
constexpr std::array<Color, 10> range_rainbow_colors{
Color{0.043f, 0.173f, 0.478f, 1.0f},
Color{0.075f, 0.349f, 0.522f, 1.0f},
Color{0.110f, 0.533f, 0.569f, 1.0f},
Color{0.016f, 0.839f, 0.059f, 1.0f},
Color{0.667f, 0.949f, 0.000f, 1.0f},
Color{0.988f, 0.975f, 0.012f, 1.0f},
Color{0.961f, 0.808f, 0.039f, 1.0f},
Color{0.890f, 0.533f, 0.125f, 1.0f},
Color{0.820f, 0.408f, 0.188f, 1.0f},
Color{0.761f, 0.322f, 0.235f, 1.0f}};
class GCodePreviewData
{
public:
struct Color
// Color mapping to convert a float into a smooth rainbow of 10 colors.
class RangeBase
{
float rgba[4];
public:
Color(const float *argba) { memcpy(this->rgba, argba, sizeof(float) * 4); }
Color(float r = 1.f, float g = 1.f, float b = 1.f, float a = 1.f) { rgba[0] = r; rgba[1] = g; rgba[2] = b; rgba[3] = a; }
std::vector<unsigned char> as_bytes() const;
static const Color Dummy;
virtual void reset() = 0;
virtual bool empty() const = 0;
virtual float min() const = 0;
virtual float max() const = 0;
// Gets the step size using min(), max() and colors
float step_size() const;
// Gets the color at a value using colors, min(), and max()
Color get_color_at(float value) const;
};
// Color mapping from a <min, max> range into a smooth rainbow of 10 colors.
struct Range
// Color mapping converting a float in a range between a min and a max into a smooth rainbow of 10 colors.
class Range : public RangeBase
{
static const unsigned int Colors_Count = 10;
static const Color Default_Colors[Colors_Count];
Color colors[Colors_Count];
float min;
float max;
public:
Range();
void reset();
bool empty() const;
// RangeBase Overrides
void reset() override;
bool empty() const override;
float min() const override;
float max() const override;
// Range-specific methods
void update_from(float value);
void update_from(const Range& other);
void set_from(const Range& other);
float step_size() const;
void update_from(const RangeBase& other);
Color get_color_at(float value) const;
private:
float min_val;
float max_val;
};
// Like Range, but stores multiple ranges internally that are used depending on mode.
// Template param EnumRangeType must be an enum with values for each type of range that needs to be tracked in this MultiRange.
// The last enum value should be num_values. The numerical values of all enum values should range from 0 to num_values.
template <typename EnumRangeType>
class MultiRange : public RangeBase
{
public:
void reset() override
{
bounds = decltype(bounds){};
}
bool empty() const override
{
for (std::size_t i = 0; i < bounds.size(); ++i)
{
if (bounds[i].min != bounds[i].max)
return false;
}
return true;
}
float min() const override
{
float min = FLT_MAX;
for (std::size_t i = 0; i < bounds.size(); ++i)
{
// Only use bounds[i] if the current mode includes it
if (mode.test(i))
{
min = std::min(min, bounds[i].min);
}
}
return min;
}
float max() const override
{
float max = -FLT_MAX;
for (std::size_t i = 0; i < bounds.size(); ++i)
{
// Only use bounds[i] if the current mode includes it
if (mode.test(i))
{
max = std::max(max, bounds[i].max);
}
}
return max;
}
void update_from(const float value, EnumRangeType range_type_value)
{
bounds[static_cast<std::size_t>(range_type_value)].update_from(value);
}
void update_from(const MultiRange& other)
{
for (std::size_t i = 0; i < bounds.size(); ++i)
{
bounds[i].update_from(other.bounds[i]);
}
}
void set_mode(const EnumRangeType range_type_value, const bool enable)
{
mode.set(static_cast<std::size_t>(range_type_value), enable);
}
private:
// Interval bounds
struct Bounds
{
float min{FLT_MAX};
float max{-FLT_MAX};
void update_from(const float value)
{
min = std::min(min, value);
max = std::max(max, value);
}
void update_from(const Bounds other_bounds)
{
min = std::min(min, other_bounds.min);
max = std::max(max, other_bounds.max);
}
};
std::array<Bounds, static_cast<std::size_t>(EnumRangeType::num_values)> bounds;
std::bitset<static_cast<std::size_t>(EnumRangeType::num_values)> mode;
};
// Enum distinguishing different kinds of feedrate data
enum class FeedrateKind
{
EXTRUSION = 0, // values must go from 0 up to num_values
TRAVEL,
num_values //must be last in the list of values
};
struct Ranges
@ -51,7 +198,7 @@ public:
// Color mapping by extrusion width.
Range width;
// Color mapping by feedrate.
Range feedrate;
MultiRange<FeedrateKind> feedrate;
// Color mapping by fan speed.
Range fan_speed;
// Color mapping by volumetric extrusion rate.
@ -245,9 +392,6 @@ public:
static const std::vector<std::string>& ColorPrintColors();
};
GCodePreviewData::Color operator + (const GCodePreviewData::Color& c1, const GCodePreviewData::Color& c2);
GCodePreviewData::Color operator * (float f, const GCodePreviewData::Color& color);
} // namespace Slic3r
#endif /* slic3r_GCode_PreviewData_hpp_ */

View file

@ -129,8 +129,12 @@ ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool
// Use the extruder switches from Model::custom_gcode_per_print_z to override the extruder to print the object.
// Do it only if all the objects were configured to be printed with a single extruder.
std::vector<std::pair<double, unsigned int>> per_layer_extruder_switches;
if (print.object_extruders().size() == 1)
per_layer_extruder_switches = custom_tool_changes(print.model(), (unsigned int)print.config().nozzle_diameter.size());
if (auto num_extruders = unsigned(print.config().nozzle_diameter.size());
num_extruders > 1 && print.object_extruders().size() == 1) {
// Printing a single extruder platter on a printer with more than 1 extruder (or single-extruder multi-material).
// There may be custom per-layer tool changes available at the model.
per_layer_extruder_switches = custom_tool_changes(print.model(), num_extruders);
}
// Collect extruders reuqired to print the layers.
for (auto object : print.objects())
@ -142,11 +146,6 @@ ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool
this->fill_wipe_tower_partitions(print.config(), object_bottom_z);
this->collect_extruder_statistics(prime_multi_material);
// Assign custom G-code actions from Model::custom_gcode_per_print_z to their respecive layers,
// ignoring the extruder switches, which were processed above, and ignoring color changes for extruders,
// that do not print above their respective print_z.
this->assign_custom_gcodes(print);
}
void ToolOrdering::initialize_layers(std::vector<coordf_t> &zs)
@ -463,13 +462,15 @@ void ToolOrdering::assign_custom_gcodes(const Print &print)
// Only valid for non-sequential print.
assert(! print.config().complete_objects.value);
const std::vector<Model::CustomGCode> &custom_gcode_per_print_z = print.model().custom_gcode_per_print_z;
if (custom_gcode_per_print_z.empty())
const Model::CustomGCodeInfo &custom_gcode_per_print_z = print.model().custom_gcode_per_print_z;
if (custom_gcode_per_print_z.gcodes.empty())
return;
unsigned int num_extruders = *std::max_element(m_all_printing_extruders.begin(), m_all_printing_extruders.end()) + 1;
std::vector<unsigned char> extruder_printing_above(num_extruders, false);
auto custom_gcode_it = custom_gcode_per_print_z.rbegin();
auto custom_gcode_it = custom_gcode_per_print_z.gcodes.rbegin();
// If printing on a single extruder machine, make the tool changes trigger color change (M600) events.
bool tool_changes_as_color_changes = num_extruders == 1;
// From the last layer to the first one:
for (auto it_lt = m_layer_tools.rbegin(); it_lt != m_layer_tools.rend(); ++ it_lt) {
LayerTools &lt = *it_lt;
@ -477,8 +478,8 @@ void ToolOrdering::assign_custom_gcodes(const Print &print)
for (unsigned int i : lt.extruders)
extruder_printing_above[i] = true;
// Skip all custom G-codes above this layer and skip all extruder switches.
for (; custom_gcode_it != custom_gcode_per_print_z.rend() && (custom_gcode_it->print_z > lt.print_z + EPSILON || custom_gcode_it->gcode == ExtruderChangeCode); ++ custom_gcode_it);
if (custom_gcode_it == custom_gcode_per_print_z.rend())
for (; custom_gcode_it != custom_gcode_per_print_z.gcodes.rend() && (custom_gcode_it->print_z > lt.print_z + EPSILON || custom_gcode_it->gcode == ExtruderChangeCode); ++ custom_gcode_it);
if (custom_gcode_it == custom_gcode_per_print_z.gcodes.rend())
// Custom G-codes were processed.
break;
// Some custom G-code is configured for this layer or a layer below.
@ -489,7 +490,8 @@ void ToolOrdering::assign_custom_gcodes(const Print &print)
print_z_below = it_lt_below->print_z;
if (custom_gcode.print_z > print_z_below + 0.5 * EPSILON) {
// The custom G-code applies to the current layer.
if (custom_gcode.gcode != ColorChangeCode || extruder_printing_above[unsigned(custom_gcode.extruder - 1)])
if ( tool_changes_as_color_changes || custom_gcode.gcode != ColorChangeCode ||
(custom_gcode.extruder <= num_extruders && extruder_printing_above[unsigned(custom_gcode.extruder - 1)]))
// If it is color change, it will actually be useful as the exturder above will print.
lt.custom_gcode = &custom_gcode;
// Consume that custom G-code event.

View file

@ -126,7 +126,7 @@ Model Model::read_from_file(const std::string& input_file, DynamicPrintConfig* c
if (add_default_instances)
model.add_default_instances();
update_custom_gcode_per_print_z_from_config(model.custom_gcode_per_print_z, config);
update_custom_gcode_per_print_z_from_config(model.custom_gcode_per_print_z.gcodes, config);
return model;
}
@ -163,7 +163,7 @@ Model Model::read_from_archive(const std::string& input_file, DynamicPrintConfig
if (add_default_instances)
model.add_default_instances();
update_custom_gcode_per_print_z_from_config(model.custom_gcode_per_print_z, config);
update_custom_gcode_per_print_z_from_config(model.custom_gcode_per_print_z.gcodes, config);
return model;
}
@ -1846,7 +1846,7 @@ arrangement::ArrangePolygon ModelInstance::get_arrange_polygon() const
std::vector<std::pair<double, unsigned int>> custom_tool_changes(const Model &model, size_t num_extruders)
{
std::vector<std::pair<double, unsigned int>> custom_tool_changes;
for (const Model::CustomGCode &custom_gcode : model.custom_gcode_per_print_z)
for (const Model::CustomGCode &custom_gcode : model.custom_gcode_per_print_z.gcodes)
if (custom_gcode.gcode == ExtruderChangeCode) {
// If extruder count in PrinterSettings was changed, use default (0) extruder for extruders, more than num_extruders
custom_tool_changes.emplace_back(custom_gcode.print_z, static_cast<unsigned int>(custom_gcode.extruder > num_extruders ? 1 : custom_gcode.extruder));

View file

@ -772,7 +772,28 @@ public:
std::string color; // if gcode is equal to PausePrintCode,
// this field is used for save a short message shown on Printer display
};
std::vector<CustomGCode> custom_gcode_per_print_z;
struct CustomGCodeInfo
{
enum MODE
{
SingleExtruder, // single extruder printer preset is selected
MultiAsSingle, // multiple extruder printer preset is selected, but
// this mode works just for Single extruder print
// (For all print from objects settings is used just one extruder)
MultiExtruder // multiple extruder printer preset is selected
} mode;
std::vector<CustomGCode> gcodes;
bool operator==(const CustomGCodeInfo& rhs) const
{
return (rhs.mode == this->mode ) &&
(rhs.gcodes == this->gcodes );
}
bool operator!=(const CustomGCodeInfo& rhs) const { return !(*this == rhs); }
}
custom_gcode_per_print_z;
// Default constructor assigns a new ID to the model.
Model() { assert(this->id().valid()); }

View file

@ -723,7 +723,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
model_object_status.emplace(model_object->id(), ModelObjectStatus::New);
} else {
if (m_model.custom_gcode_per_print_z != model.custom_gcode_per_print_z) {
update_apply_status(custom_per_printz_gcodes_tool_changes_differ(m_model.custom_gcode_per_print_z, model.custom_gcode_per_print_z) ?
update_apply_status(custom_per_printz_gcodes_tool_changes_differ(m_model.custom_gcode_per_print_z.gcodes, model.custom_gcode_per_print_z.gcodes) ?
// The Tool Ordering and the Wipe Tower are no more valid.
this->invalidate_steps({ psWipeTower, psGCodeExport }) :
// There is no change in Tool Changes stored in custom_gcode_per_print_z, therefore there is no need to update Tool Ordering.

View file

@ -242,11 +242,10 @@ private:
struct WipeTowerData
{
WipeTowerData(ToolOrdering &tool_ordering) : tool_ordering(tool_ordering) { clear(); }
// Following section will be consumed by the GCodeGenerator.
// Tool ordering of a non-sequential print has to be known to calculate the wipe tower.
// Cache it here, so it does not need to be recalculated during the G-code generation.
ToolOrdering tool_ordering;
ToolOrdering &tool_ordering;
// Cache of tool changes per print layer.
std::unique_ptr<std::vector<WipeTower::ToolChangeResult>> priming;
std::vector<std::vector<WipeTower::ToolChangeResult>> tool_changes;
@ -267,6 +266,14 @@ struct WipeTowerData
depth = 0.f;
brim_width = 0.f;
}
private:
// Only allow the WipeTowerData to be instantiated internally by Print,
// as this WipeTowerData shares reference to Print::m_tool_ordering.
friend class Print;
WipeTowerData(ToolOrdering &tool_ordering) : tool_ordering(tool_ordering) { clear(); }
WipeTowerData(const WipeTowerData & /* rhs */) = delete;
WipeTowerData &operator=(const WipeTowerData & /* rhs */) = delete;
};
struct PrintStatistics
@ -394,6 +401,7 @@ public:
// Accessed by SupportMaterial
const PrintRegion* get_region(size_t idx) const { return m_regions[idx]; }
const ToolOrdering& get_tool_ordering() const { return m_wipe_tower_data.tool_ordering; } // #ys_FIXME just for testing
protected:
// methods for handling regions

View file

@ -1007,7 +1007,7 @@ void GLCanvas3D::LegendTexture::fill_color_print_legend_items( const GLCanvas3D
std::vector<float>& colors,
std::vector<std::string>& cp_legend_items)
{
std::vector<Model::CustomGCode> custom_gcode_per_print_z = wxGetApp().plater()->model().custom_gcode_per_print_z;
std::vector<Model::CustomGCode> custom_gcode_per_print_z = wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes;
const int extruders_cnt = wxGetApp().extruders_edited_cnt();
if (extruders_cnt == 1)
@ -2434,7 +2434,7 @@ static void load_gcode_retractions(const GCodePreviewData::Retraction& retractio
volume_index.first_volumes.emplace_back(extrusion_type, 0, (unsigned int)volumes.volumes.size());
GLVolume *volume = volumes.new_nontoolpath_volume(retractions.color.rgba, VERTEX_BUFFER_RESERVE_SIZE);
GLVolume *volume = volumes.new_nontoolpath_volume(retractions.color.rgba.data(), VERTEX_BUFFER_RESERVE_SIZE);
GCodePreviewData::Retraction::PositionsList copy(retractions.positions);
std::sort(copy.begin(), copy.end(), [](const GCodePreviewData::Retraction::Position& p1, const GCodePreviewData::Retraction::Position& p2) { return p1.position(2) < p2.position(2); });
@ -5889,7 +5889,7 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat
return 0.0f;
}
static GCodePreviewData::Color path_color(const GCodePreviewData& data, const std::vector<float>& tool_colors, float value)
static Color path_color(const GCodePreviewData& data, const std::vector<float>& tool_colors, float value)
{
switch (data.extrusion.view_type)
{
@ -5907,8 +5907,8 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat
return data.get_volumetric_rate_color(value);
case GCodePreviewData::Extrusion::Tool:
{
GCodePreviewData::Color color;
::memcpy((void*)color.rgba, (const void*)(tool_colors.data() + (unsigned int)value * 4), 4 * sizeof(float));
Color color;
::memcpy((void*)color.rgba.data(), (const void*)(tool_colors.data() + (unsigned int)value * 4), 4 * sizeof(float));
return color;
}
case GCodePreviewData::Extrusion::ColorPrint:
@ -5916,16 +5916,16 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat
int color_cnt = (int)tool_colors.size() / 4;
int val = value > color_cnt ? color_cnt - 1 : value;
GCodePreviewData::Color color;
::memcpy((void*)color.rgba, (const void*)(tool_colors.data() + val * 4), 4 * sizeof(float));
Color color;
::memcpy((void*)color.rgba.data(), (const void*)(tool_colors.data() + val * 4), 4 * sizeof(float));
return color;
}
default:
return GCodePreviewData::Color::Dummy;
return Color{};
}
return GCodePreviewData::Color::Dummy;
return Color{};
}
};
@ -5968,7 +5968,7 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat
if (! values.empty()) {
m_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Extrusion, role, (unsigned int)m_volumes.volumes.size());
for (const float value : values)
roles_filters.back().emplace_back(value, m_volumes.new_toolpath_volume(Helper::path_color(preview_data, tool_colors, value).rgba, vertex_buffer_prealloc_size));
roles_filters.back().emplace_back(value, m_volumes.new_toolpath_volume(Helper::path_color(preview_data, tool_colors, value).rgba.data(), vertex_buffer_prealloc_size));
}
}
}
@ -6051,7 +6051,7 @@ inline void travel_paths_internal(
by_type.reserve(values.size());
// creates a new volume for each feedrate
for (TYPE type : values)
by_type.emplace_back(type, volumes.new_nontoolpath_volume(func_color(type).rgba, VERTEX_BUFFER_RESERVE_SIZE));
by_type.emplace_back(type, volumes.new_nontoolpath_volume(func_color(type).rgba.data(), VERTEX_BUFFER_RESERVE_SIZE));
}
// populates volumes
@ -6098,19 +6098,19 @@ void GLCanvas3D::_load_gcode_travel_paths(const GCodePreviewData& preview_data,
case GCodePreviewData::Extrusion::Feedrate:
travel_paths_internal<float>(preview_data,
[](const GCodePreviewData::Travel::Polyline &polyline) { return polyline.feedrate; },
[&preview_data](const float feedrate) -> const GCodePreviewData::Color { return preview_data.get_feedrate_color(feedrate); },
[&preview_data](const float feedrate) -> const Color { return preview_data.get_feedrate_color(feedrate); },
m_volumes, m_initialized);
break;
case GCodePreviewData::Extrusion::Tool:
travel_paths_internal<unsigned int>(preview_data,
[](const GCodePreviewData::Travel::Polyline &polyline) { return polyline.extruder_id; },
[&tool_colors](const unsigned int extruder_id) -> const GCodePreviewData::Color { assert((extruder_id + 1) * 4 <= tool_colors.size()); return GCodePreviewData::Color(tool_colors.data() + extruder_id * 4); },
[&tool_colors](const unsigned int extruder_id) -> const Color { assert((extruder_id + 1) * 4 <= tool_colors.size()); return Color(tool_colors.data() + extruder_id * 4); },
m_volumes, m_initialized);
break;
default:
travel_paths_internal<unsigned int>(preview_data,
[](const GCodePreviewData::Travel::Polyline &polyline) { return polyline.type; },
[&preview_data](const unsigned int type) -> const GCodePreviewData::Color& { return preview_data.travel.type_colors[type]; },
[&preview_data](const unsigned int type) -> const Color& { return preview_data.travel.type_colors[type]; },
m_volumes, m_initialized);
break;
}

View file

@ -533,7 +533,9 @@ void Preview::on_combochecklist_features(wxCommandEvent& evt)
void Preview::on_checkbox_travel(wxCommandEvent& evt)
{
m_gcode_preview_data->travel.is_visible = m_checkbox_travel->IsChecked();
refresh_print();
m_gcode_preview_data->ranges.feedrate.set_mode(GCodePreviewData::FeedrateKind::TRAVEL, m_gcode_preview_data->travel.is_visible);
// Rather than refresh, reload print so that speed color ranges get recomputed (affected by travel visibility)
reload_print();
}
void Preview::on_checkbox_retractions(wxCommandEvent& evt)
@ -564,7 +566,7 @@ void Preview::update_view_type(bool slice_completed)
{
const DynamicPrintConfig& config = wxGetApp().preset_bundle->project_config;
const wxString& choice = !wxGetApp().plater()->model().custom_gcode_per_print_z.empty() /*&&
const wxString& choice = !wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes.empty() /*&&
(wxGetApp().extruders_edited_cnt()==1 || !slice_completed) */?
_(L("Color Print")) :
config.option<ConfigOptionFloats>("wiping_volumes_matrix")->values.size() > 1 ?
@ -595,7 +597,7 @@ void Preview::create_double_slider()
Bind(wxCUSTOMEVT_TICKSCHANGED, [this](wxEvent&) {
Model& model = wxGetApp().plater()->model();
model.custom_gcode_per_print_z = m_slider->GetTicksValues();
model.custom_gcode_per_print_z.gcodes = m_slider->GetTicksValues();
m_schedule_background_process();
update_view_type(false);
@ -664,7 +666,7 @@ void Preview::update_double_slider(const std::vector<double>& layers_z, bool kee
bool snap_to_min = force_sliders_full_range || m_slider->is_lower_at_min();
bool snap_to_max = force_sliders_full_range || m_slider->is_higher_at_max();
std::vector<Model::CustomGCode> &ticks_from_model = wxGetApp().plater()->model().custom_gcode_per_print_z;
std::vector<Model::CustomGCode> &ticks_from_model = wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes;
check_slider_values(ticks_from_model, layers_z);
m_slider->SetSliderValues(layers_z);
@ -692,7 +694,60 @@ void Preview::update_double_slider(const std::vector<double>& layers_z, bool kee
bool color_print_enable = (wxGetApp().plater()->printer_technology() == ptFFF);
m_slider->EnableTickManipulation(color_print_enable);
m_slider->SetManipulationState(wxGetApp().extruders_edited_cnt());
// Detect and set manipulation mode for double slider
update_double_slider_mode();
}
void Preview::update_double_slider_mode()
{
// true -> single-extruder printer profile OR
// multi-extruder printer profile , but whole model is printed by only one extruder
// false -> multi-extruder printer profile , and model is printed by several extruders
bool one_extruder_printed_model = true;
// extruder used for whole model for multi-extruder printer profile
int only_extruder = -1;
if (wxGetApp().extruders_edited_cnt() > 1)
{
const ModelObjectPtrs& objects = wxGetApp().plater()->model().objects;
// check if whole model uses just only one extruder
if (!objects.empty())
{
const int extruder = objects[0]->config.has("extruder") ?
objects[0]->config.option("extruder")->getInt() : 0;
auto is_one_extruder_printed_model = [objects, extruder]()
{
for (ModelObject* object : objects)
{
if (object->config.has("extruder") &&
object->config.option("extruder")->getInt() != extruder)
return false;
if (object->volumes.size() > 1)
for (ModelVolume* volume : object->volumes)
if (volume->config.has("extruder") &&
volume->config.option("extruder")->getInt() != extruder)
return false;
for (const auto& range : object->layer_config_ranges)
if (range.second.has("extruder") &&
range.second.option("extruder")->getInt() != extruder)
return false;
}
return true;
};
if (is_one_extruder_printed_model())
only_extruder = extruder;
else
one_extruder_printed_model = false;
}
}
m_slider->SetModeAndOnlyExtruder(one_extruder_printed_model, only_extruder);
}
void Preview::reset_double_slider()
@ -784,7 +839,7 @@ void Preview::load_print_as_fff(bool keep_z_range)
colors.push_back("#808080"); // gray color for pause print or custom G-code
if (!gcode_preview_data_valid)
color_print_values = wxGetApp().plater()->model().custom_gcode_per_print_z;
color_print_values = wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes;
}
else if (gcode_preview_data_valid || (m_gcode_preview_data->extrusion.view_type == GCodePreviewData::Extrusion::Tool) )
{

View file

@ -157,8 +157,9 @@ private:
void create_double_slider();
void check_slider_values(std::vector<Model::CustomGCode> &ticks_from_model,
const std::vector<double> &layers_z);
void update_double_slider(const std::vector<double>& layers_z, bool keep_z_range = false);
void reset_double_slider();
void update_double_slider(const std::vector<double>& layers_z, bool keep_z_range = false);
void update_double_slider_mode();
// update DoubleSlider after keyDown in canvas
void update_double_slider_from_canvas(wxKeyEvent& event);

View file

@ -2791,7 +2791,7 @@ void Plater::priv::reset()
// The hiding of the slicing results, if shown, is not taken care by the background process, so we do it here
this->sidebar->show_sliced_info_sizer(false);
model.custom_gcode_per_print_z.clear();
model.custom_gcode_per_print_z.gcodes.clear();
}
void Plater::priv::mirror(Axis axis)
@ -5349,9 +5349,9 @@ std::vector<std::string> Plater::get_extruder_colors_from_plater_config() const
std::vector<std::string> Plater::get_colors_for_color_print() const
{
std::vector<std::string> colors = get_extruder_colors_from_plater_config();
colors.reserve(colors.size() + p->model.custom_gcode_per_print_z.size());
colors.reserve(colors.size() + p->model.custom_gcode_per_print_z.gcodes.size());
for (const Model::CustomGCode& code : p->model.custom_gcode_per_print_z)
for (const Model::CustomGCode& code : p->model.custom_gcode_per_print_z.gcodes)
if (code.gcode == ColorChangeCode)
colors.emplace_back(code.color);

View file

@ -877,7 +877,7 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool
// 4) Load the project config values (the per extruder wipe matrix etc).
this->project_config.apply_only(config, s_project_options);
update_custom_gcode_per_print_z_from_config(GUI::wxGetApp().plater()->model().custom_gcode_per_print_z, &this->project_config);
update_custom_gcode_per_print_z_from_config(GUI::wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes, &this->project_config);
break;
}

View file

@ -5,6 +5,7 @@
#include "libslic3r/Utils.hpp"
#include "libslic3r/Model.hpp"
#include "libslic3r/Print.hpp"
#include <wx/sizer.h>
#include <wx/bmpcbox.h>
@ -2537,7 +2538,7 @@ std::vector<t_custom_code> DoubleSlider::GetTicksValues() const
const int val_size = m_values.size();
if (!m_values.empty())
for (const TICK_CODE& tick : m_ticks_) {
for (const TICK_CODE& tick : m_ticks) {
if (tick.tick > val_size)
break;
values.emplace_back(t_custom_code{m_values[tick.tick], tick.gcode, tick.extruder, tick.color});
@ -2551,19 +2552,19 @@ void DoubleSlider::SetTicksValues(const std::vector<t_custom_code>& heights)
if (m_values.empty())
return;
const bool was_empty = m_ticks_.empty();
const bool was_empty = m_ticks.empty();
m_ticks_.clear();
m_ticks.clear();
for (auto h : heights) {
auto it = std::lower_bound(m_values.begin(), m_values.end(), h.print_z - epsilon());
if (it == m_values.end())
continue;
m_ticks_.emplace(TICK_CODE{int(it-m_values.begin()), h.gcode, h.extruder, h.color});
m_ticks.emplace(TICK_CODE{int(it-m_values.begin()), h.gcode, h.extruder, h.color});
}
if (!was_empty && m_ticks_.empty())
if (!was_empty && m_ticks.empty())
// Switch to the "Feature type"/"Tool" from the very beginning of a new object slicing after deleting of the old one
wxPostEvent(this->GetParent(), wxCommandEvent(wxCUSTOMEVT_TICKSCHANGED));
@ -2614,11 +2615,6 @@ void DoubleSlider::render()
// draw line
draw_scroll_line(dc, lower_pos, higher_pos);
// //lower slider:
// draw_thumb(dc, lower_pos, ssLower);
// //higher slider:
// draw_thumb(dc, higher_pos, ssHigher);
//draw color print ticks
draw_ticks(dc);
@ -2644,7 +2640,7 @@ void DoubleSlider::draw_action_icon(wxDC& dc, const wxPoint pt_beg, const wxPoin
return;
wxBitmap* icon = m_is_action_icon_focesed ? &m_bmp_add_tick_off.bmp() : &m_bmp_add_tick_on.bmp();
if (m_ticks_.find(TICK_CODE{tick}) != m_ticks_.end())
if (m_ticks.find(TICK_CODE{tick}) != m_ticks.end())
icon = m_is_action_icon_focesed ? &m_bmp_del_tick_off.bmp() : &m_bmp_del_tick_on.bmp();
wxCoord x_draw, y_draw;
@ -2787,7 +2783,7 @@ void DoubleSlider::draw_ticks(wxDC& dc)
int height, width;
get_size(&width, &height);
const wxCoord mid = is_horizontal() ? 0.5*height : 0.5*width;
for (auto tick : m_ticks_)
for (auto tick : m_ticks)
{
const wxCoord pos = get_position_from_value(tick.tick);
@ -2810,6 +2806,40 @@ void DoubleSlider::draw_ticks(wxDC& dc)
}
}
std::string DoubleSlider::get_color_for_tool_change_tick(std::set<TICK_CODE>::const_iterator it) const
{
const int current_extruder = it->extruder == 0 ? std::max<int>(m_only_extruder, 1) : it->extruder;
auto it_n = it;
while (it_n != m_ticks.begin()) {
--it_n;
if (it_n->gcode == Slic3r::ColorChangeCode && it_n->extruder == current_extruder)
return it_n->color;
}
return it->color;
}
std::string DoubleSlider::get_color_for_color_change_tick(std::set<TICK_CODE>::const_iterator it) const
{
const int def_extruder = std::max<int>(1, m_only_extruder);
auto it_n = it;
bool is_tool_change = false;
while (it_n != m_ticks.begin()) {
--it_n;
if (it_n->gcode == Slic3r::ExtruderChangeCode) {
is_tool_change = true;
if (it_n->extruder == it->extruder)
return it->color;
break;
}
}
if (!is_tool_change && it->extruder == def_extruder)
return it->color;
return "";
}
void DoubleSlider::draw_colored_band(wxDC& dc)
{
if (!m_is_enabled_tick_manipulation)
@ -2832,29 +2862,36 @@ void DoubleSlider::draw_colored_band(wxDC& dc)
dc.DrawRectangle(band_rc);
};
const std::vector<std::string>& colors = Slic3r::GUI::wxGetApp().plater()->get_extruder_colors_from_plater_config();
int colors_cnt = colors.size();
const wxColour bg_clr = GetParent()->GetBackgroundColour();
wxColour clr = m_state == msSingleExtruder ? wxColour(colors[0]) : bg_clr;
draw_band(dc, clr, main_band);
size_t i = 1;
for (auto tick : m_ticks_)
// don't color a band for MultiExtruder mode
if (m_ticks.empty() || m_mode == mmMultiExtruder)
{
if ( (m_state == msSingleExtruder && tick.gcode != Slic3r::ColorChangeCode) ||
(m_state == msMultiExtruder && tick.gcode != Slic3r::ExtruderChangeCode) )
continue;
draw_band(dc, GetParent()->GetBackgroundColour(), main_band);
return;
}
const wxCoord pos = get_position_from_value(tick.tick);
is_horizontal() ? main_band.SetLeft(SLIDER_MARGIN + pos) :
main_band.SetBottom(pos - 1);
const int default_color_idx = m_mode==mmMultiAsSingle ? std::max<int>(m_only_extruder - 1, 0) : 0;
draw_band(dc, wxColour(Slic3r::GUI::wxGetApp().plater()->get_extruder_colors_from_plater_config()[default_color_idx]), main_band);
clr = (m_state == msMultiExtruder && tick.color.empty()) ? bg_clr :
m_state == msMultiExtruder ? wxColour(colors[std::min<int>(colors_cnt - 1, tick.extruder-1)]) : wxColour(tick.color);
draw_band(dc, clr, main_band);
i++;
std::set<TICK_CODE>::const_iterator tick_it = m_ticks.begin();
while (tick_it != m_ticks.end())
{
if ( (m_mode == mmSingleExtruder && tick_it->gcode == Slic3r::ColorChangeCode ) ||
(m_mode == mmMultiAsSingle && (tick_it->gcode == Slic3r::ExtruderChangeCode || tick_it->gcode == Slic3r::ColorChangeCode)) )
{
const wxCoord pos = get_position_from_value(tick_it->tick);
is_horizontal() ? main_band.SetLeft(SLIDER_MARGIN + pos) :
main_band.SetBottom(pos - 1);
const std::string clr_str = m_mode == mmSingleExtruder ? tick_it->color :
tick_it->gcode == Slic3r::ExtruderChangeCode ?
get_color_for_tool_change_tick(tick_it) :
get_color_for_color_change_tick(tick_it);
if (!clr_str.empty())
draw_band(dc, wxColour(clr_str), main_band);
}
++tick_it;
}
}
@ -2879,7 +2916,7 @@ void DoubleSlider::draw_one_layer_icon(wxDC& dc)
void DoubleSlider::draw_revert_icon(wxDC& dc)
{
if (m_ticks_.empty() || !m_is_enabled_tick_manipulation)
if (m_ticks.empty() || !m_is_enabled_tick_manipulation)
return;
int width, height;
@ -2897,7 +2934,7 @@ void DoubleSlider::draw_revert_icon(wxDC& dc)
void DoubleSlider::draw_cog_icon(wxDC& dc)
{
if (m_state != msMultiExtruder)
if (m_mode != mmMultiExtruder)
return;
int width, height;
@ -2941,15 +2978,13 @@ void DoubleSlider::detect_selected_slider(const wxPoint& pt)
bool DoubleSlider::is_point_in_rect(const wxPoint& pt, const wxRect& rect)
{
if (rect.GetLeft() <= pt.x && pt.x <= rect.GetRight() &&
rect.GetTop() <= pt.y && pt.y <= rect.GetBottom())
return true;
return false;
return rect.GetLeft() <= pt.x && pt.x <= rect.GetRight() &&
rect.GetTop() <= pt.y && pt.y <= rect.GetBottom();
}
int DoubleSlider::is_point_near_tick(const wxPoint& pt)
{
for (auto tick : m_ticks_) {
for (auto tick : m_ticks) {
const wxCoord pos = get_position_from_value(tick.tick);
if (is_horizontal()) {
@ -3010,10 +3045,10 @@ void DoubleSlider::OnLeftDown(wxMouseEvent& event)
m_selection == ssLower ? correct_lower_value() : correct_higher_value();
if (!m_selection) m_selection = ssHigher;
m_ticks_.clear();
m_ticks.clear();
wxPostEvent(this->GetParent(), wxCommandEvent(wxCUSTOMEVT_TICKSCHANGED));
}
else if (is_point_in_rect(pos, m_rect_cog_icon) && m_state == msMultiExtruder) {
else if (is_point_in_rect(pos, m_rect_cog_icon) && m_mode == mmMultiExtruder) {
// show dialog for set extruder sequence
m_edit_extruder_sequence = true;
}
@ -3083,17 +3118,18 @@ wxString DoubleSlider::get_tooltip(IconFocus icon_focus)
else if (m_is_action_icon_focesed)
{
const int tick = m_selection == ssLower ? m_lower_value : m_higher_value;
const auto tick_code_it = m_ticks_.find(TICK_CODE{tick});
tooltip = tick_code_it == m_ticks_.end() ? (m_state == msSingleExtruder ?
_(L("For add color change use left mouse button click")) :
_(L("For add change extruder use left mouse button click"))) + "\n" +
_(L("For add another code use right mouse button click")) :
tick_code_it->gcode == Slic3r::ColorChangeCode ? ( m_state == msSingleExtruder ?
const auto tick_code_it = m_ticks.find(TICK_CODE{tick});
tooltip = tick_code_it == m_ticks.end() ? (m_mode == mmMultiAsSingle ?
_(L("For add change extruder use left mouse button click")) :
_(L("For add color change use left mouse button click")) ) + "\n" +
_(L("For add another code use right mouse button click")) :
tick_code_it->gcode == Slic3r::ColorChangeCode ? ( m_mode == mmSingleExtruder ?
_(L("For Delete color change use left mouse button click\n"
"For Edit color use right mouse button click")) :
from_u8((boost::format(_utf8(L("Delete color change for Extruder %1%"))) % tick_code_it->extruder).str()) ):
// tick_code_it->gcode == Slic3r::PausePrintCode ? _(L("Delete pause")) :
tick_code_it->gcode == Slic3r::ExtruderChangeCode ?
tick_code_it->gcode == Slic3r::PausePrintCode ?
_(L("Delete pause")) :
tick_code_it->gcode == Slic3r::ExtruderChangeCode ?
from_u8((boost::format(_utf8(L("Delete extruder change to \"%1%\""))) % tick_code_it->extruder).str()) :
from_u8((boost::format(_utf8(L("For Delete \"%1%\" code use left mouse button click\n"
"For Edit \"%1%\" code use right mouse button click"))) % tick_code_it->gcode ).str());
@ -3114,7 +3150,7 @@ void DoubleSlider::OnMotion(wxMouseEvent& event)
if (!m_is_left_down && !m_is_one_layer) {
m_is_action_icon_focesed = is_point_in_rect(pos, m_rect_tick_action);
if (!m_ticks_.empty() && is_point_in_rect(pos, m_rect_revert_icon))
if (!m_ticks.empty() && is_point_in_rect(pos, m_rect_revert_icon))
icon_focus = ifRevert;
else if (is_point_in_rect(pos, m_rect_cog_icon))
icon_focus = ifCog;
@ -3149,6 +3185,67 @@ void DoubleSlider::OnMotion(wxMouseEvent& event)
}
}
void DoubleSlider::append_change_extruder_menu_item(wxMenu* menu)
{
const int extruders_cnt = Slic3r::GUI::wxGetApp().extruders_edited_cnt();
if (extruders_cnt > 1)
{
const int initial_extruder = std::max<int>(1 , get_extruder_for_tick(m_selection == ssLower ? m_lower_value : m_higher_value));
wxMenu* change_extruder_menu = new wxMenu();
for (int i = 1; i <= extruders_cnt; i++)
{
const bool is_active_extruder = i == initial_extruder;
const wxString item_name = wxString::Format(_(L("Extruder %d")), i) +
(is_active_extruder ? " (" + _(L("active")) + ")" : "");
if (m_mode == mmMultiAsSingle)
append_menu_item(change_extruder_menu, wxID_ANY, item_name, "",
[this, i](wxCommandEvent&) { change_extruder(i); }, "", menu,
[is_active_extruder]() { return !is_active_extruder; }, Slic3r::GUI::wxGetApp().plater());
// append_menu_radio_item(change_extruder_menu, wxID_ANY, item_name, "",
// [this, i](wxCommandEvent&) { change_extruder(i); }, menu)->Check(i == initial_extruder);
}
const wxString change_extruder_menu_name = m_mode == mmMultiAsSingle ? _(L("Change extruder")) : _(L("Change extruder (N/A)"));
wxMenuItem* change_extruder_menu_item = menu->AppendSubMenu(change_extruder_menu, change_extruder_menu_name, _(L("Use another extruder")));
change_extruder_menu_item->SetBitmap(create_scaled_bitmap(this, "change_extruder"));
Slic3r::GUI::wxGetApp().plater()->Bind(wxEVT_UPDATE_UI, [this, change_extruder_menu_item](wxUpdateUIEvent& evt) {
enable_menu_item(evt, [this]() {return m_mode == mmMultiAsSingle; }, change_extruder_menu_item, this); },
change_extruder_menu_item->GetId());
}
}
void DoubleSlider::append_add_color_change_menu_item(wxMenu* menu)
{
const int extruders_cnt = Slic3r::GUI::wxGetApp().extruders_edited_cnt();
if (extruders_cnt > 1)
{
std::set<int> used_extruders_for_tick = get_used_extruders_for_tick(m_selection == ssLower ? m_lower_value : m_higher_value);
wxMenu* add_color_change_menu = new wxMenu();
for (int i = 1; i <= extruders_cnt; i++)
{
const bool is_used_extruder = used_extruders_for_tick.empty() ? true : // #ys_FIXME till used_extruders_for_tick doesn't filled correct for mmMultiExtruder
used_extruders_for_tick.find(i) != used_extruders_for_tick.end();
const wxString item_name = wxString::Format(_(L("Extruder %d")), i) +
(is_used_extruder ? " (" + _(L("used")) + ")" : "");
append_menu_item(add_color_change_menu, wxID_ANY, item_name, "",
[this, i](wxCommandEvent&) { add_code(Slic3r::ColorChangeCode, i); }, "", menu,
[is_used_extruder]() { return is_used_extruder; }, Slic3r::GUI::wxGetApp().plater());
}
const wxString menu_name = from_u8((boost::format(_utf8(L("Add color change (%1%) for:"))) % Slic3r::ColorChangeCode).str());
wxMenuItem* add_color_change_menu_item = menu->AppendSubMenu(add_color_change_menu, menu_name, "");
add_color_change_menu_item->SetBitmap(create_scaled_bitmap(nullptr, "colorchange_add_m"));
}
}
void DoubleSlider::OnLeftUp(wxMouseEvent& event)
{
if (!HasCapture())
@ -3158,31 +3255,19 @@ void DoubleSlider::OnLeftUp(wxMouseEvent& event)
if (m_show_context_menu)
{
if (m_state == msMultiExtruder)
if (m_mode == mmSingleExtruder)
add_code(Slic3r::ColorChangeCode);
else
{
wxMenu menu;
const int extruders_cnt = Slic3r::GUI::wxGetApp().extruders_edited_cnt();
if (extruders_cnt > 1)
{
const int initial_extruder = get_extruder_for_tick(m_selection == ssLower ? m_lower_value : m_higher_value);
wxMenu* change_extruder_menu = new wxMenu();
for (int i = 0; i <= extruders_cnt; i++) {
const wxString item_name = i == 0 ? _(L("Default")) : wxString::Format(_(L("Extruder %d")), i);
append_menu_radio_item(change_extruder_menu, wxID_ANY, item_name, "",
[this, i](wxCommandEvent&) { change_extruder(i); }, &menu)->Check(i == initial_extruder);
}
wxMenuItem* change_extruder_menu_item = menu.AppendSubMenu(change_extruder_menu, _(L("Change extruder")), _(L("Use another extruder")));
change_extruder_menu_item->SetBitmap(create_scaled_bitmap(nullptr, "change_extruder"));
}
if (m_mode == mmMultiAsSingle)
append_change_extruder_menu_item(&menu);
else
append_add_color_change_menu_item(&menu);
Slic3r::GUI::wxGetApp().plater()->PopupMenu(&menu);
}
else
add_code(Slic3r::ColorChangeCode);
m_show_context_menu = false;
}
@ -3242,13 +3327,13 @@ void DoubleSlider::action_tick(const TicksAction action)
const int tick = m_selection == ssLower ? m_lower_value : m_higher_value;
const auto it = m_ticks_.find(TICK_CODE{tick});
const auto it = m_ticks.find(TICK_CODE{tick});
if (it != m_ticks_.end()) // erase this tick
if (it != m_ticks.end()) // erase this tick
{
if (action == taAdd)
return;
m_ticks_.erase(TICK_CODE{tick});
m_ticks.erase(TICK_CODE{tick});
wxPostEvent(this->GetParent(), wxCommandEvent(wxCUSTOMEVT_TICKSCHANGED));
Refresh();
@ -3265,7 +3350,7 @@ void DoubleSlider::action_tick(const TicksAction action)
if (m_suppress_add_code)
return;
m_suppress_add_code = true;
if (m_state != msMultiExtruder)
if (m_mode == mmSingleExtruder) // if (m_mode != mmMultiExtruder)
add_code(Slic3r::ColorChangeCode);
m_suppress_add_code = false;
return;
@ -3352,8 +3437,8 @@ void DoubleSlider::OnRightDown(wxMouseEvent& event)
{
const int tick = m_selection == ssLower ? m_lower_value : m_higher_value;
// if on this Z doesn't exist tick
auto it = m_ticks_.find(TICK_CODE{ tick });
if (it == m_ticks_.end())
auto it = m_ticks.find(TICK_CODE{ tick });
if (it == m_ticks.end())
{
// show context menu on OnRightUp()
m_show_context_menu = true;
@ -3386,17 +3471,77 @@ void DoubleSlider::OnRightDown(wxMouseEvent& event)
int DoubleSlider::get_extruder_for_tick(int tick)
{
if (m_ticks_.empty())
return 0;
int default_initial_extruder = m_mode == mmMultiAsSingle ? m_only_extruder : 0;
if (m_ticks.empty())
return default_initial_extruder;
auto it = m_ticks_.lower_bound(TICK_CODE{tick});
while (it != m_ticks_.begin()) {
auto it = m_ticks.lower_bound(TICK_CODE{tick});
while (it != m_ticks.begin()) {
--it;
if(it->gcode == Slic3r::ExtruderChangeCode)
return it->extruder;
}
return 0;
return default_initial_extruder;
}
std::set<int> DoubleSlider::get_used_extruders_for_tick(int tick)
{
if (m_mode == mmMultiExtruder)
{
// #ys_FIXME: get tool ordering from _correct_ place
const Slic3r::ToolOrdering& tool_ordering = Slic3r::GUI::wxGetApp().plater()->fff_print().get_tool_ordering();
if (tool_ordering.empty())
return {};
std::set<int> used_extruders;
auto it_layer_tools = std::lower_bound(tool_ordering.begin(), tool_ordering.end(), Slic3r::LayerTools(m_values[tick]));
for (; it_layer_tools != tool_ordering.end(); it_layer_tools++)
{
const std::vector<unsigned>& extruders = it_layer_tools->extruders;
for (const auto& extruder : extruders)
used_extruders.emplace(extruder+1);
}
return used_extruders;
}
const int default_initial_extruder = m_mode == mmMultiAsSingle ? std::max(m_only_extruder, 1) : 1;
if (m_ticks.empty())
return {default_initial_extruder};
std::set<int> used_extruders;
auto it_start = m_ticks.lower_bound(TICK_CODE{tick});
auto it = it_start;
if (it == m_ticks.begin() && it->gcode == Slic3r::ExtruderChangeCode) {
used_extruders.emplace(it->extruder);
if (tick < it->tick)
used_extruders.emplace(default_initial_extruder);
}
while (it != m_ticks.begin()) {
--it;
if(it->gcode == Slic3r::ExtruderChangeCode)
{
used_extruders.emplace(it->extruder);
break;
}
}
if (it == m_ticks.begin() && used_extruders.empty())
used_extruders.emplace(default_initial_extruder);
it = it_start;
while (it != m_ticks.end()) {
if(it->gcode == Slic3r::ExtruderChangeCode)
used_extruders.emplace(it->extruder);
++it;
}
return used_extruders;
}
void DoubleSlider::OnRightUp(wxMouseEvent& event)
@ -3409,45 +3554,23 @@ void DoubleSlider::OnRightUp(wxMouseEvent& event)
if (m_show_context_menu) {
wxMenu menu;
if (m_state == msMultiExtruder)
{
const int extruders_cnt = Slic3r::GUI::wxGetApp().extruders_edited_cnt();
if (extruders_cnt > 1)
{
const int initial_extruder = get_extruder_for_tick(m_selection == ssLower ? m_lower_value : m_higher_value);
wxMenu* change_extruder_menu = new wxMenu();
wxMenu* add_color_change_menu = new wxMenu();
for (int i = 0; i <= extruders_cnt; i++) {
const wxString item_name = i == 0 ? _(L("Default")) : wxString::Format(_(L("Extruder %d")), i);
append_menu_radio_item(change_extruder_menu, wxID_ANY, item_name, "",
[this, i](wxCommandEvent&) { change_extruder(i); }, &menu)->Check(i == initial_extruder);
if (i==0) // don't use M600 for default extruder, if multimaterial print is selected
continue;
append_menu_item(add_color_change_menu, wxID_ANY, item_name, "",
[this, i](wxCommandEvent&) { add_code(Slic3r::ColorChangeCode, i); }, "", &menu);
}
wxMenuItem* change_extruder_menu_item = menu.AppendSubMenu(change_extruder_menu, _(L("Change extruder")), _(L("Use another extruder")));
change_extruder_menu_item->SetBitmap(create_scaled_bitmap(nullptr, "change_extruder"));
const wxString menu_name = from_u8((boost::format(_utf8(L("Add color change (%1%) for:"))) % Slic3r::ColorChangeCode).str());
wxMenuItem* add_color_change_menu_item = menu.AppendSubMenu(add_color_change_menu, menu_name, "");
add_color_change_menu_item->SetBitmap(create_scaled_bitmap(nullptr, "colorchange_add_m"));
}
}
if (m_mode == mmSingleExtruder)
append_menu_item(&menu, wxID_ANY, _(L("Add color change")) + " (M600)", "",
[this](wxCommandEvent&) { add_code(Slic3r::ColorChangeCode); }, "colorchange_add_m", &menu,
[](){return true;}, this);
else
append_menu_item(&menu, wxID_ANY, _(L("Add color change")) + " (M600)", "",
[this](wxCommandEvent&) { add_code(Slic3r::ColorChangeCode); }, "colorchange_add_m", &menu);
{
append_change_extruder_menu_item(&menu);
append_add_color_change_menu_item(&menu);
}
append_menu_item(&menu, wxID_ANY, _(L("Add pause print")) + " (M601)", "",
[this](wxCommandEvent&) { add_code(Slic3r::PausePrintCode); }, "pause_print", &menu);
[this](wxCommandEvent&) { add_code(Slic3r::PausePrintCode); }, "pause_print", &menu,
[]() {return true; }, this);
append_menu_item(&menu, wxID_ANY, _(L("Add custom G-code")), "",
[this](wxCommandEvent&) { add_code(""); }, "edit_gcode", &menu);
[this](wxCommandEvent&) { add_code(""); }, "edit_gcode", &menu,
[]() {return true; }, this);
Slic3r::GUI::wxGetApp().plater()->PopupMenu(&menu);
@ -3456,7 +3579,7 @@ void DoubleSlider::OnRightUp(wxMouseEvent& event)
else if (m_show_edit_menu) {
wxMenu menu;
std::set<TICK_CODE>::iterator it = m_ticks_.find(TICK_CODE{ m_selection == ssLower ? m_lower_value : m_higher_value });
std::set<TICK_CODE>::iterator it = m_ticks.find(TICK_CODE{ m_selection == ssLower ? m_lower_value : m_higher_value });
const bool is_color_change = it->gcode == Slic3r::ColorChangeCode;
append_menu_item(&menu, wxID_ANY, it->gcode == Slic3r::ColorChangeCode ? _(L("Edit color")) :
@ -3528,74 +3651,69 @@ void DoubleSlider::add_code(std::string code, int selected_extruder/* = -1*/)
{
const int tick = m_selection == ssLower ? m_lower_value : m_higher_value;
// if on this Z doesn't exist tick
auto it = m_ticks_.find(TICK_CODE{ tick });
if (it == m_ticks_.end())
auto it = m_ticks.find(TICK_CODE{ tick });
if (it != m_ticks.end())
return;
std::string color;
const int extruder = selected_extruder > 0 ? selected_extruder : std::max<int>(1, m_only_extruder);
if (code == Slic3r::ColorChangeCode)
{
std::string color = "";
if (code == Slic3r::ColorChangeCode)
{
std::vector<std::string> colors = Slic3r::GUI::wxGetApp().plater()->get_extruder_colors_from_plater_config();
std::vector<std::string> colors = Slic3r::GUI::wxGetApp().plater()->get_extruder_colors_from_plater_config();
if (m_state == msSingleExtruder && !m_ticks_.empty()) {
auto before_tick_it = std::lower_bound(m_ticks_.begin(), m_ticks_.end(), TICK_CODE{ tick });
while (before_tick_it != m_ticks_.begin()) {
--before_tick_it;
if (before_tick_it->gcode == Slic3r::ColorChangeCode) {
color = before_tick_it->color;
break;
}
if (m_ticks.empty())
color = colors[extruder-1];
else
{
auto before_tick_it = std::lower_bound(m_ticks.begin(), m_ticks.end(), TICK_CODE{ tick });
while (before_tick_it != m_ticks.begin()) {
--before_tick_it;
if (before_tick_it->gcode == Slic3r::ColorChangeCode && before_tick_it->extruder == extruder) {
color = before_tick_it->color;
break;
}
if (color.empty())
color = colors[0];
}
else
color = colors[selected_extruder > 0 ? selected_extruder-1 : 0];
color = get_new_color(color);
if (color.empty())
return;
}
else if (code == Slic3r::PausePrintCode)
{
/* PausePrintCode doesn't need a color, so
* this field is used for save a short message shown on Printer display
* */
color = get_pause_print_msg(m_pause_print_msg, m_values[tick]);
if (color.empty())
return;
m_pause_print_msg = color;
}
else if (code.empty())
{
code = get_custom_code(m_custom_gcode, m_values[tick]);
if (code.empty())
return;
m_custom_gcode = code;
color = colors[extruder-1];
}
int extruder = 1;
if (m_state == msMultiExtruder) {
if (code == Slic3r::ColorChangeCode && selected_extruder >= 0)
extruder = selected_extruder;
else
extruder = get_extruder_for_tick(m_selection == ssLower ? m_lower_value : m_higher_value);
}
m_ticks_.emplace(TICK_CODE{tick, code, extruder, color});
wxPostEvent(this->GetParent(), wxCommandEvent(wxCUSTOMEVT_TICKSCHANGED));
Refresh();
Update();
color = get_new_color(color);
if (color.empty())
return;
}
else if (code == Slic3r::PausePrintCode)
{
/* PausePrintCode doesn't need a color, so
* this field is used for save a short message shown on Printer display
* */
color = get_pause_print_msg(m_pause_print_msg, m_values[tick]);
if (color.empty())
return;
m_pause_print_msg = color;
}
else if (code.empty())
{
code = get_custom_code(m_custom_gcode, m_values[tick]);
if (code.empty())
return;
m_custom_gcode = code;
}
m_ticks.emplace(TICK_CODE{tick, code, extruder, color});
wxPostEvent(this->GetParent(), wxCommandEvent(wxCUSTOMEVT_TICKSCHANGED));
Refresh();
Update();
}
void DoubleSlider::edit_tick()
{
const int tick = m_selection == ssLower ? m_lower_value : m_higher_value;
// if on this Z exists tick
std::set<TICK_CODE>::iterator it = m_ticks_.find(TICK_CODE{ tick });
if (it != m_ticks_.end())
std::set<TICK_CODE>::iterator it = m_ticks.find(TICK_CODE{ tick });
if (it != m_ticks.end())
{
std::string edited_value;
if (it->gcode == Slic3r::ColorChangeCode)
@ -3620,8 +3738,8 @@ void DoubleSlider::edit_tick()
changed_tick.gcode = edited_value;
}
m_ticks_.erase(it);
m_ticks_.emplace(changed_tick);
m_ticks.erase(it);
m_ticks.emplace(changed_tick);
wxPostEvent(this->GetParent(), wxCommandEvent(wxCUSTOMEVT_TICKSCHANGED));
}
@ -3634,9 +3752,9 @@ void DoubleSlider::change_extruder(int extruder)
std::vector<std::string> colors = Slic3r::GUI::wxGetApp().plater()->get_extruder_colors_from_plater_config();
// if on this Y doesn't exist tick
if (m_ticks_.find(TICK_CODE{tick}) == m_ticks_.end())
if (m_ticks.find(TICK_CODE{tick}) == m_ticks.end())
{
m_ticks_.emplace(TICK_CODE{tick, Slic3r::ExtruderChangeCode, extruder, extruder == 0 ? "" : colors[extruder-1]});
m_ticks.emplace(TICK_CODE{tick, Slic3r::ExtruderChangeCode, extruder, extruder == 0 ? "" : colors[extruder-1]});
wxPostEvent(this->GetParent(), wxCommandEvent(wxCUSTOMEVT_TICKSCHANGED));
Refresh();
@ -3656,10 +3774,10 @@ void DoubleSlider::edit_extruder_sequence()
m_extruders_sequence = from_dlg_val;
auto it = m_ticks_.begin();
while (it != m_ticks_.end()) {
auto it = m_ticks.begin();
while (it != m_ticks.end()) {
if (it->gcode == Slic3r::ExtruderChangeCode)
it = m_ticks_.erase(it);
it = m_ticks.erase(it);
else
++it;
}
@ -3674,7 +3792,7 @@ void DoubleSlider::edit_extruder_sequence()
while (tick <= m_max_value)
{
int cur_extruder = m_extruders_sequence.extruders[extruder];
m_ticks_.emplace(TICK_CODE{tick, Slic3r::ExtruderChangeCode, cur_extruder + 1, colors[cur_extruder]});
m_ticks.emplace(TICK_CODE{tick, Slic3r::ExtruderChangeCode, cur_extruder + 1, colors[cur_extruder]});
extruder++;
if (extruder == extr_cnt)

View file

@ -821,17 +821,23 @@ public:
EnableTickManipulation(false);
}
enum ManipulationState {
msSingleExtruder, // single extruder printer preset is selected
msMultiExtruder // multiple extruder printer preset is selected, and "Whole print" is selected
enum ManipulationMode {
mmSingleExtruder, // single extruder printer preset is selected
mmMultiAsSingle, // multiple extruder printer preset is selected, but
// this mode works just for Single extruder print
// (For all print from objects settings is used just one extruder)
mmMultiExtruder // multiple extruder printer preset is selected
};
void SetManipulationState(ManipulationState state) {
m_state = state;
void SetManipulationMode(ManipulationMode mode) { m_mode = mode; }
ManipulationMode GetManipulationMode() const { return m_mode; }
void SetModeAndOnlyExtruder(const bool is_one_extruder_printed_model, const int only_extruder)
{
m_mode = !is_one_extruder_printed_model ? mmMultiExtruder :
only_extruder < 0 ? mmSingleExtruder :
mmMultiAsSingle;
m_only_extruder = only_extruder;
}
void SetManipulationState(int extruders_cnt) {
m_state = extruders_cnt ==1 ? msSingleExtruder : msMultiExtruder;
}
ManipulationState GetManipulationState() const { return m_state; }
bool is_horizontal() const { return m_style == wxSL_HORIZONTAL; }
bool is_one_layer() const { return m_is_one_layer; }
@ -850,13 +856,23 @@ public:
void OnKeyUp(wxKeyEvent &event);
void OnChar(wxKeyEvent &event);
void OnRightDown(wxMouseEvent& event);
int get_extruder_for_tick(int tick);
void OnRightUp(wxMouseEvent& event);
void add_code(std::string code, int selected_extruder = -1);
void edit_tick();
void change_extruder(int extruder);
void edit_extruder_sequence();
struct TICK_CODE
{
bool operator<(const TICK_CODE& other) const { return other.tick > this->tick; }
bool operator>(const TICK_CODE& other) const { return other.tick < this->tick; }
int tick = 0;
std::string gcode = Slic3r::ColorChangeCode;
int extruder = 0;
std::string color;
};
protected:
void render();
@ -878,11 +894,12 @@ protected:
void detect_selected_slider(const wxPoint& pt);
void correct_lower_value();
void correct_higher_value();
wxString get_tooltip(IconFocus icon_focus);
void move_current_thumb(const bool condition);
void action_tick(const TicksAction action);
void enter_window(wxMouseEvent& event, const bool enter);
private:
bool is_point_in_rect(const wxPoint& pt, const wxRect& rect);
int is_point_near_tick(const wxPoint& pt);
@ -894,8 +911,17 @@ protected:
wxSize get_size();
void get_size(int *w, int *h);
double get_double_value(const SelectedSlider& selection);
wxString get_tooltip(IconFocus icon_focus);
std::string get_color_for_tool_change_tick(std::set<TICK_CODE>::const_iterator it) const;
std::string get_color_for_color_change_tick(std::set<TICK_CODE>::const_iterator it) const;
int get_extruder_for_tick(int tick);
std::set<int> get_used_extruders_for_tick(int tick);
void append_change_extruder_menu_item(wxMenu*);
void append_add_color_change_menu_item(wxMenu*);
private:
bool is_osx { false };
wxFont m_font;
int m_min_value;
@ -914,7 +940,7 @@ private:
ScalableBitmap m_bmp_one_layer_unlock_off;
ScalableBitmap m_bmp_revert;
ScalableBitmap m_bmp_cog;
SelectedSlider m_selection;
SelectedSlider m_selection;
bool m_is_left_down = false;
bool m_is_right_down = false;
bool m_is_one_layer = false;
@ -926,9 +952,10 @@ private:
bool m_show_edit_menu = false;
bool m_edit_extruder_sequence = false;
bool m_suppress_add_code = false;
ManipulationState m_state = msSingleExtruder;
ManipulationMode m_mode = mmSingleExtruder;
std::string m_custom_gcode = "";
std::string m_pause_print_msg;
int m_only_extruder = -1;
wxRect m_rect_lower_thumb;
wxRect m_rect_higher_thumb;
@ -957,50 +984,17 @@ private:
std::vector<wxPen*> m_line_pens;
std::vector<wxPen*> m_segm_pens;
std::set<int> m_ticks;
std::vector<double> m_values;
struct TICK_CODE
{
bool operator<(const TICK_CODE& other) const { return other.tick > this->tick; }
bool operator>(const TICK_CODE& other) const { return other.tick < this->tick; }
int tick = 0;
std::string gcode = Slic3r::ColorChangeCode;
int extruder = 0;
std::string color;
};
std::set<TICK_CODE> m_ticks_;
std::set<TICK_CODE> m_ticks;
public:
struct ExtrudersSequence
{
bool is_mm_intervals;
double interval_by_mm;
int interval_by_layers;
std::vector<size_t> extruders;
bool is_mm_intervals = true;
double interval_by_mm = 3.0;
int interval_by_layers = 10;
std::vector<size_t> extruders = { 0 };
ExtrudersSequence() :
is_mm_intervals(true),
interval_by_mm(3.0),
interval_by_layers(10),
extruders({ 0 }) {}
ExtrudersSequence(const ExtrudersSequence& other) :
is_mm_intervals(other.is_mm_intervals),
interval_by_mm(other.interval_by_mm),
interval_by_layers(other.interval_by_layers),
extruders(other.extruders) {}
ExtrudersSequence& operator=(const ExtrudersSequence& other) {
this->is_mm_intervals = other.is_mm_intervals;
this->interval_by_mm = other.interval_by_mm;
this->interval_by_layers= other.interval_by_layers;
this->extruders = other.extruders;
return *this;
}
bool operator==(const ExtrudersSequence& other) const
{
return (other.is_mm_intervals == this->is_mm_intervals ) &&