Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/prusa3d/PrusaSlicer.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLukas Matena <lukasmatena@seznam.cz>2018-02-28 18:04:56 +0300
committerLukas Matena <lukasmatena@seznam.cz>2018-02-28 18:04:56 +0300
commita62ad3323f389e3ce93915b7fea92bd1eccf321e (patch)
treeea1b311809d552e4e91b2c5ad8e0dca0785acc3a
parent3099c32d0805a6fff26929e9be3dcb27e50f9bfa (diff)
First naive implementation of wipe tower settings dialog
-rw-r--r--xs/CMakeLists.txt5
-rw-r--r--xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp65
-rw-r--r--xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp152
-rw-r--r--xs/src/libslic3r/Print.cpp4
-rw-r--r--xs/src/libslic3r/PrintConfig.cpp9
-rw-r--r--xs/src/libslic3r/PrintConfig.hpp2
-rw-r--r--xs/src/slic3r/GUI/BedShapeDialog.cpp2
-rw-r--r--xs/src/slic3r/GUI/Preset.cpp6
-rw-r--r--xs/src/slic3r/GUI/RammingChart.cpp273
-rw-r--r--xs/src/slic3r/GUI/RammingChart.hpp129
-rw-r--r--xs/src/slic3r/GUI/Tab.cpp24
-rw-r--r--xs/src/slic3r/GUI/Tab.hpp1
-rw-r--r--xs/src/slic3r/GUI/WipeTowerDialog.cpp264
-rw-r--r--xs/src/slic3r/GUI/WipeTowerDialog.hpp115
14 files changed, 996 insertions, 55 deletions
diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt
index 41bf9de26..4e217d148 100644
--- a/xs/CMakeLists.txt
+++ b/xs/CMakeLists.txt
@@ -195,6 +195,11 @@ add_library(libslic3r_gui STATIC
${LIBDIR}/slic3r/GUI/2DBed.hpp
${LIBDIR}/slic3r/GUI/wxExtensions.cpp
${LIBDIR}/slic3r/GUI/wxExtensions.hpp
+ ${LIBDIR}/slic3r/GUI/WipeTowerDialog.cpp
+ ${LIBDIR}/slic3r/GUI/WipeTowerDialog.hpp
+ ${LIBDIR}/slic3r/GUI/RammingChart.cpp
+ ${LIBDIR}/slic3r/GUI/RammingChart.hpp
+
)
add_library(admesh STATIC
diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
index 2a78ed6f1..d7492fdbb 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
@@ -33,33 +33,6 @@ TODO LIST
constexpr bool peters_wipe_tower = false; // sparse wipe tower inspired by Peter's post processor - not finished yet
constexpr float min_layer_difference = 2*m_perimeter_width;
-constexpr float max_bridge_distance = 10.f; // in mm
-constexpr bool improve_first_layer_adhesion = true;
-// experimental: ramming speed (mm^3/s) sampled in 0.25s intervals (one filament so far)
-const std::vector<float> ramming_speed = {7.6, 7.6, 7.6, 7.6, 9.0, 9.0, 9.0, 10.7, 10.7, 10.7};
-constexpr float ramming_step_multiplicator = 1.2f; // extra spacing may be needed for some materials
-constexpr float ramming_line_width_multiplicator = 1.5f;
-
-// experimental: time requested for cooling in seconds (common for all materials so far)
-constexpr float cooling_time = 14; // PVA: 20; SCAFF: 17; PLA+others: 14
-
-
-// volumes in mm^3 required for wipe: {{from 0 to ...},{from 1 to ...},{from 2 to ...},{from 3 to ...}}, usage [from][to]
-const std::vector<std::vector<float>> wipe_volumes = {{ 0,120, 10, 50},
- { 20, 0, 30, 40},
- { 90, 20, 0, 85},
- {100,140, 30, 0}};
-
-/*const std::vector<std::vector<float>> wipe_volumes = {{0, 67, 67, 67},
- {67, 0, 67, 67},
- {67, 67, 0, 67},
- {67, 67, 67, 0}};
-*/
-/*const std::vector<std::vector<float>> wipe_volumes = {{0, 10, 10, 10},
- {10, 0, 10, 10},
- {10, 10, 0, 10},
- {10, 10, 10, 0}};
-*/
namespace Slic3r
{
@@ -614,7 +587,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
{
for (const auto &b : m_layer_info->tool_changes)
if ( b.new_tool == tool ) {
- wipe_volume = wipe_volumes[b.old_tool][b.new_tool];
+ wipe_volume = m_par.wipe_volumes[b.old_tool][b.new_tool];
if (tool == m_layer_info->tool_changes.back().new_tool)
last_change_in_layer = true;
wipe_area = b.required_depth * m_layer_info->extra_spacing;
@@ -804,20 +777,20 @@ void WipeTowerPrusaMM::toolchange_Unload(
writer.append("; CP TOOLCHANGE UNLOAD\n");
- const float line_width = m_line_width * ramming_line_width_multiplicator; // desired ramming line thickness
- const float y_step = line_width * ramming_step_multiplicator * m_extra_spacing; // spacing between lines in mm
+ const float line_width = m_line_width * m_par.ramming_line_width_multiplicator[m_current_tool]; // desired ramming line thickness
+ const float y_step = line_width * m_par.ramming_step_multiplicator[m_current_tool] * m_extra_spacing; // spacing between lines in mm
unsigned i = 0; // iterates through ramming_speed
m_left_to_right = true; // current direction of ramming
float remaining = xr - xl ; // keeps track of distance to the next turnaround
float e_done = 0; // measures E move done from each segment
-
+
writer.travel(xl, cleaning_box.ld.y + m_depth_traversed + y_step/2.f ); // move to starting position
- while (i < ramming_speed.size())
+ while (i < m_par.ramming_speed[m_current_tool].size())
{
- const float x = volume_to_length(ramming_speed[i] * 0.25f, line_width, m_layer_height);
- const float e = ramming_speed[i] * 0.25f / Filament_Area; // transform volume per sec to E move;
+ const float x = volume_to_length(m_par.ramming_speed[m_current_tool][i] * 0.25f, line_width, m_layer_height);
+ const float e = m_par.ramming_speed[m_current_tool][i] * 0.25f / Filament_Area; // transform volume per sec to E move;
const float dist = std::min(x - e_done, remaining); // distance to travel for either the next 0.25s, or to the next turnaround
const float actual_time = dist/x * 0.25;
writer.ram(writer.x(), writer.x() + (m_left_to_right ? 1.f : -1.f) * dist, 0, 0, e * (dist / x), std::hypot(dist, e * (dist / x)) / (actual_time / 60.));
@@ -880,21 +853,21 @@ void WipeTowerPrusaMM::toolchange_Unload(
const float start_x = writer.x();
const float turning_point = ( xr-start_x > start_x-xl ? xr : xl );
const float max_x_dist = 2*std::abs(start_x-turning_point);
- const int N = 4 + (cooling_time-14)/3;
- float time = cooling_time / N;
-
+ const unsigned int N = 4 + std::max(0,(m_par.cooling_time[m_current_tool]-14)/3);
+ float time = m_par.cooling_time[m_current_tool] / N;
+
i = 0;
- while (i<N) {
+ while (i<N) {
const float speed = std::min(3.4,2.2 + i*0.3 + (i==0 ? 0 : 0.3)); // mm per second: 2.2, 2.8, 3.1, 3.4, 3.4, 3.4, ...
const float e_dist = std::min(speed * time,10.f); // distance to travel
if (speed * time < 10.f) { // this move is the last one at this speed
++i;
- time = cooling_time / N;
+ time = m_par.cooling_time[m_current_tool] / N;
}
else
time -= e_dist / speed; // subtract time this part will really take
-
+
// as for x, we will make sure the feedrate is at most 2000
float x_dist = (turning_point - WT_EPSILON < xl ? -1.f : 1.f) * std::min(e_dist * (float)sqrt(pow(2000 / (60 * speed), 2) - 1),max_x_dist);
const float feedrate = std::hypot(e_dist, x_dist) / ((e_dist / speed) / 60.f);
@@ -1124,7 +1097,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer(Purpose purpose)
.extrude(box.ld);
}
- if (m_is_first_layer && improve_first_layer_adhesion) {
+ if (m_is_first_layer && m_par.adhesion) {
// Extrude a dense infill at the 1st layer to improve 1st layer adhesion of the wipe tower.
box.expand(-m_perimeter_width/2.f);
unsigned nsteps = int(floor((box.lu.y - box.ld.y) / (2*m_perimeter_width)));
@@ -1150,7 +1123,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer(Purpose purpose)
const float left = fill_box.lu.x+2*m_perimeter_width;
const float right = fill_box.ru.x - 2 * m_perimeter_width;
- const int n = 1+(right-left)/max_bridge_distance;
+ const int n = 1+(right-left)/(m_par.bridging);
const float dx = (right-left)/n;
for (int i=1;i<=n;++i) {
float x=left+dx*i;
@@ -1207,12 +1180,12 @@ void WipeTowerPrusaMM::plan_toolchange(float z_par, float layer_height_par, unsi
// this is an actual toolchange - let's calculate depth to reserve on the wipe tower
float depth = 0.f;
float width = m_wipe_tower_width - 3*m_perimeter_width;
- float length_to_extrude = volume_to_length(0.25f * std::accumulate(ramming_speed.begin(), ramming_speed.end(), 0.f),
- m_line_width * ramming_line_width_multiplicator,
+ float length_to_extrude = volume_to_length(0.25f * std::accumulate(m_par.ramming_speed[old_tool].begin(), m_par.ramming_speed[old_tool].end(), 0.f),
+ m_line_width * m_par.ramming_line_width_multiplicator[old_tool],
layer_height_par);
- depth = (int(length_to_extrude / width) + 1) * (m_line_width * ramming_line_width_multiplicator * ramming_step_multiplicator);
+ depth = (int(length_to_extrude / width) + 1) * (m_line_width * m_par.ramming_line_width_multiplicator[old_tool] * m_par.ramming_step_multiplicator[old_tool]);
length_to_extrude = width*((length_to_extrude / width)-int(length_to_extrude / width)) - width;
- length_to_extrude += volume_to_length(wipe_volumes[old_tool][new_tool], m_line_width, layer_height_par);
+ length_to_extrude += volume_to_length(m_par.wipe_volumes[old_tool][new_tool], m_line_width, layer_height_par);
length_to_extrude = std::max(length_to_extrude,0.f);
depth += (int(length_to_extrude / width) + 1) * m_line_width;
depth *= m_extra_spacing;
diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
index f5fc2985e..43163a73a 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
@@ -4,6 +4,7 @@
#include <algorithm>
#include <cmath>
#include <string>
+#include <sstream>
#include <utility>
#include "WipeTower.hpp"
@@ -22,6 +23,11 @@ constexpr float m_perimeter_width = Nozzle_Diameter * Width_To_Nozzle_Ratio * Ko
constexpr float WT_EPSILON = 1e-3f;
+
+
+
+
+
namespace Slic3r
{
@@ -29,6 +35,141 @@ namespace PrusaMultiMaterial {
class Writer;
};
+
+
+// Operator overload to output std::pairs
+template <typename T>
+std::ostream& operator<<(std::ostream& stream,const std::pair<T,T>& pair) {
+ return stream << pair.first << " " << pair.second;
+}
+
+// Operator overload to output elements of a vector to std::ofstream easily:
+template <typename T>
+std::ostream& operator<<(std::ostream& stream,const std::vector<T>& vect) {
+ for (const auto& element : vect)
+ stream << element << " ";
+ return stream;
+}
+
+// Operator overload to input elements of a vector from std::ifstream easily (reads until a fail)
+template <typename T>
+std::istream& operator>>(std::istream& stream, std::vector<T>& vect) {
+ vect.clear();
+ T value{};
+ bool we_read_something = false;
+ while (stream >> value) {
+ vect.push_back(value);
+ we_read_something = true;
+ }
+ if (!stream.eof() && we_read_something) { // if this is not eof, we might be at separator - let's get rid of it
+ stream.clear(); // if we failed on very first line or reached eof, return stream in !good() state
+ stream.get(); // get() whatever we are stuck at
+ }
+ return stream;
+}
+
+
+// This struct is used to store parameters and to pass it to wipe tower generator
+struct WipeTowerParameters {
+ WipeTowerParameters() { } // create new empty object
+ WipeTowerParameters(const std::string& init_data) { // create object and initialize from std::string
+ std::istringstream in(init_data); // validation of input is left to the caller
+ in >> bridging >> adhesion >> sampling;
+ for (std::vector<float> vect{} ; in >> vect ;) { // until we get to fail state ("**")...
+ if (vect.size()>=3) {
+ cooling_time.push_back(vect[0]);
+ ramming_line_width_multiplicator.push_back(vect[1]);
+ ramming_step_multiplicator.push_back(vect[2]);
+ vect.erase(vect.begin(),vect.begin()+3);
+ }
+ else vect.clear(); // something's not right, we will restore defaults anyway
+ ramming_speed.push_back(vect);
+
+ if (in.good()) {
+ in >> vect;
+ std::vector<std::pair<float,float>> pairs;
+ for (unsigned int i=0;i<vect.size();++i)
+ if (i%2==1)
+ pairs.push_back(std::make_pair(vect[i-1],vect[i]));
+ ramming_buttons.push_back(pairs);
+ }
+ }
+ in.clear();
+ in.get();
+
+ for (std::vector<float> vect{} ; in >> vect ;) { // let's keep reading
+ wipe_volumes.push_back(vect);
+ }
+ in.clear();
+ in.get();
+
+ std::vector<int> vect{};
+ in >> vect;
+ for (unsigned int i=0;i<vect.size();++i)
+ if (i%2==1)
+ filament_wipe_volumes.push_back(std::make_pair(vect[i-1],vect[i]));
+ }
+
+ std::string to_string() {
+ std::ostringstream out;
+ out << bridging << " " << int(adhesion) << " " << sampling << "\n";
+ for (unsigned extruder=0;extruder<cooling_time.size();++extruder) {
+ out << "\n" << cooling_time[extruder] << " " << ramming_line_width_multiplicator[extruder] << " "
+ << ramming_step_multiplicator[extruder] << " " << ramming_speed[extruder] << "*"
+ << ramming_buttons[extruder] << "*";
+ }
+ out << "*\n";
+ for (auto& radek : wipe_volumes)
+ out << "\n" << radek << "*";
+ out << "*\n";
+ out << filament_wipe_volumes << "*";
+ return out.str();
+ }
+
+ bool validate() const { // basic check for validity to distinguish most dramatic failures
+ const unsigned int num = cooling_time.size();
+ if ( num < 1 || ramming_line_width_multiplicator.size()!=num || ramming_step_multiplicator.size()!=num ||
+ ramming_buttons.size()!=num || wipe_volumes.size()!=num ||
+ filament_wipe_volumes.size()!=num)
+ return false;
+ for (const auto& row : wipe_volumes)
+ if (row.size()!=num)
+ return false;
+ return true;
+ }
+ void set_defaults() {
+ bridging = 10;
+ adhesion = true;
+ sampling = 0.25f;
+ cooling_time = {15,15,15,15};
+ ramming_line_width_multiplicator = {1.5f, 1.5f, 1.5f, 1.5f};
+ ramming_step_multiplicator = {1.1f, 1.1f, 1.1f, 1.1f};
+ ramming_speed.clear();
+ ramming_buttons.clear();
+ for (unsigned int i=0;i<4;++i) {
+ ramming_speed.push_back(std::vector<float>{7.6, 7.6, 7.6, 7.6, 9.0, 9.0, 9.0, 10.7, 10.7, 10.7});
+ ramming_buttons.push_back(std::vector<std::pair<float,float>>{{0.05, 6.6},{0.45, 6.8},{0.95, 7.8},{1.45, 8.3},{1.95, 9.7},{2.45,10},{2.95, 7.6},{3.45, 7.6},{3.95, 7.6},{4.45, 7.6},{4.95, 7.6}});
+ }
+ wipe_volumes = {{ 0, 60, 60, 60},
+ { 60, 0, 60, 60},
+ { 60, 60, 0, 60},
+ { 60, 60, 60, 0}};
+ filament_wipe_volumes = {{30,30},{30,30},{30,30},{30,30}};
+ }
+
+ int bridging = 0.f;
+ bool adhesion = false;
+ float sampling = 0.25f; // this does not quite work yet, keep it fixed to 0.25f
+ std::vector<int> cooling_time;
+ std::vector<float> ramming_line_width_multiplicator;
+ std::vector<float> ramming_step_multiplicator;
+ std::vector<std::vector<float>> ramming_speed;
+ std::vector<std::vector<std::pair<float,float>>> ramming_buttons;
+ std::vector<std::vector<float>> wipe_volumes;
+ std::vector<std::pair<int,int>> filament_wipe_volumes;
+};
+
+
class WipeTowerPrusaMM : public WipeTower
{
public:
@@ -53,15 +194,16 @@ public:
// y -- y coordinates of wipe tower in mm ( left bottom corner )
// width -- width of wipe tower in mm ( default 60 mm - leave as it is )
// wipe_area -- space available for one toolchange in mm
- WipeTowerPrusaMM(float x, float y, float width, float wipe_area, float rotation_angle, unsigned int initial_tool) :
+ WipeTowerPrusaMM(float x, float y, float width, float wipe_area, float rotation_angle, unsigned int initial_tool,std::string& parameters) :
m_wipe_tower_pos(x, y),
m_wipe_tower_width(width),
m_wipe_tower_rotation_angle(rotation_angle),
m_y_shift(0.f),
m_z_pos(0.f),
m_is_first_layer(false),
- m_is_last_layer(false),
- m_current_tool(initial_tool)
+ m_is_last_layer(false),
+ m_current_tool(initial_tool),
+ m_par(parameters)
{
for (size_t i = 0; i < 4; ++ i) {
// Extruder specific parameters.
@@ -206,6 +348,7 @@ private:
// A fill-in direction (positive Y, negative Y) alternates with each layer.
wipe_shape m_current_shape = SHAPE_NORMAL;
unsigned int m_current_tool = 0;
+ WipeTowerParameters m_par;
float m_depth_traversed = 0.f; // Current y position at the wipe tower.
// How much to wipe the 1st extruder over the wipe tower at the 1st layer
@@ -310,6 +453,9 @@ private:
float wipe_volume);
};
+
+
+
}; // namespace Slic3r
#endif /* WipeTowerPrusaMM_hpp_ */
diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp
index 1c63dbf60..fe98612cf 100644
--- a/xs/src/libslic3r/Print.cpp
+++ b/xs/src/libslic3r/Print.cpp
@@ -187,6 +187,7 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
|| opt_key == "spiral_vase"
|| opt_key == "temperature"
|| opt_key == "wipe_tower"
+ || opt_key == "wipe_tower_advanced"
|| opt_key == "wipe_tower_x"
|| opt_key == "wipe_tower_y"
|| opt_key == "wipe_tower_width"
@@ -1021,7 +1022,8 @@ void Print::_make_wipe_tower()
WipeTowerPrusaMM wipe_tower(
float(this->config.wipe_tower_x.value), float(this->config.wipe_tower_y.value),
float(this->config.wipe_tower_width.value), float(this->config.wipe_tower_per_color_wipe.value),
- float(this->config.wipe_tower_rotation_angle.value), m_tool_ordering.first_extruder());
+ float(this->config.wipe_tower_rotation_angle.value), m_tool_ordering.first_extruder(),
+ this->config.wipe_tower_advanced.value);
//wipe_tower.set_retract();
//wipe_tower.set_zhop();
diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp
index 995dcee99..7a48b3a32 100644
--- a/xs/src/libslic3r/PrintConfig.cpp
+++ b/xs/src/libslic3r/PrintConfig.cpp
@@ -1701,6 +1701,13 @@ PrintConfigDef::PrintConfigDef()
def->cli = "wipe-tower!";
def->default_value = new ConfigOptionBool(false);
+ def = this->add("wipe_tower_advanced", coString);
+ def->label = _L("Advanced string");
+ def->tooltip = _L("Advanced tooltip ");
+ def->sidetext = _L("advanced sidetext");
+ def->cli = "wipe-tower-advanced=s";
+ def->default_value = new ConfigOptionString("");
+
def = this->add("wipe_tower_x", coFloat);
def->label = _L("Position X");
def->tooltip = _L("X coordinate of the left front corner of a wipe tower");
@@ -1723,7 +1730,7 @@ PrintConfigDef::PrintConfigDef()
def->default_value = new ConfigOptionFloat(60.);
def = this->add("wipe_tower_per_color_wipe", coFloat);
- def->label = _L("Per color change depth");
+ def->label = "(Unused and will be likely removed)";//_L("Per color change depth");
def->tooltip = _L("Depth of a wipe color per color change. For N colors, there will be "
"maximum (N-1) tool switches performed, therefore the total depth "
"of the wipe tower will be (N-1) times this value.");
diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp
index 7c2d40458..d55aafed6 100644
--- a/xs/src/libslic3r/PrintConfig.hpp
+++ b/xs/src/libslic3r/PrintConfig.hpp
@@ -605,6 +605,7 @@ public:
ConfigOptionInt threads;
ConfigOptionBools wipe;
ConfigOptionBool wipe_tower;
+ ConfigOptionString wipe_tower_advanced;
ConfigOptionFloat wipe_tower_x;
ConfigOptionFloat wipe_tower_y;
ConfigOptionFloat wipe_tower_width;
@@ -670,6 +671,7 @@ protected:
OPT_PTR(threads);
OPT_PTR(wipe);
OPT_PTR(wipe_tower);
+ OPT_PTR(wipe_tower_advanced);
OPT_PTR(wipe_tower_x);
OPT_PTR(wipe_tower_y);
OPT_PTR(wipe_tower_width);
diff --git a/xs/src/slic3r/GUI/BedShapeDialog.cpp b/xs/src/slic3r/GUI/BedShapeDialog.cpp
index 5ee0c1f8b..8e7f57241 100644
--- a/xs/src/slic3r/GUI/BedShapeDialog.cpp
+++ b/xs/src/slic3r/GUI/BedShapeDialog.cpp
@@ -34,7 +34,7 @@ void BedShapeDialog::build_dialog(ConfigOptionPoints* default_pt)
void BedShapePanel::build_panel(ConfigOptionPoints* default_pt)
{
-// on_change(nullptr);
+// on_change(nullptr);
auto box = new wxStaticBox(this, wxID_ANY, _L("Shape"));
auto sbsizer = new wxStaticBoxSizer(box, wxVERTICAL);
diff --git a/xs/src/slic3r/GUI/Preset.cpp b/xs/src/slic3r/GUI/Preset.cpp
index 4d55f347a..be31c1bd1 100644
--- a/xs/src/slic3r/GUI/Preset.cpp
+++ b/xs/src/slic3r/GUI/Preset.cpp
@@ -198,9 +198,9 @@ const std::vector<std::string>& Preset::print_options()
"ooze_prevention", "standby_temperature_delta", "interface_shells", "extrusion_width", "first_layer_extrusion_width",
"perimeter_extrusion_width", "external_perimeter_extrusion_width", "infill_extrusion_width", "solid_infill_extrusion_width",
"top_infill_extrusion_width", "support_material_extrusion_width", "infill_overlap", "bridge_flow_ratio", "clip_multipart_objects",
- "elefant_foot_compensation", "xy_size_compensation", "threads", "resolution", "wipe_tower", "wipe_tower_x", "wipe_tower_y",
- "wipe_tower_width", "wipe_tower_per_color_wipe", "wipe_tower_rotation_angle",
- "compatible_printers", "compatible_printers_condition"
+ "elefant_foot_compensation", "xy_size_compensation", "threads", "resolution", "wipe_tower_advanced", "wipe_tower", "wipe_tower_x",
+ "wipe_tower_y", "wipe_tower_width", "wipe_tower_per_color_wipe", "wipe_tower_rotation_angle", "compatible_printers",
+ "compatible_printers_condition"
};
return s_opts;
diff --git a/xs/src/slic3r/GUI/RammingChart.cpp b/xs/src/slic3r/GUI/RammingChart.cpp
new file mode 100644
index 000000000..3b5f4e41e
--- /dev/null
+++ b/xs/src/slic3r/GUI/RammingChart.cpp
@@ -0,0 +1,273 @@
+#include <algorithm>
+#include "RammingChart.hpp"
+
+
+wxDEFINE_EVENT(EVT_WIPE_TOWER_CHART_CHANGED, wxCommandEvent);
+
+
+void Chart::draw(wxDC& dc) {
+ dc.SetPen(*wxBLACK_PEN);
+ dc.SetBrush(*wxWHITE_BRUSH);
+ dc.DrawRectangle(m_rect);
+
+ if (visible_area->m_width < 0.499) {
+ dc.DrawText("NO RAMMING AT ALL",wxPoint(m_rect.GetLeft()+m_rect.GetWidth()/2-50,m_rect.GetBottom()-m_rect.GetHeight()/2));
+ return;
+ }
+
+
+ if (!m_line_to_draw->empty()) {
+ for (uint i=0;i<m_line_to_draw->size()-2;++i) {
+ int color = 510*((m_rect.GetBottom()-(*m_line_to_draw)[i])/double(m_rect.GetHeight()));
+ dc.SetPen( wxPen( wxColor(std::min(255,color),255-std::max(color-255,0),0), 1 ) );
+ dc.DrawLine(m_rect.GetLeft()+1+i,(*m_line_to_draw)[i],m_rect.GetLeft()+1+i,m_rect.GetBottom());
+ }
+ dc.SetPen( wxPen( wxColor(0,0,0), 1 ) );
+ for (uint i=0;i<m_line_to_draw->size()-2;++i) {
+ if (splines)
+ dc.DrawLine(m_rect.GetLeft()+i,(*m_line_to_draw)[i],m_rect.GetLeft()+i+1,(*m_line_to_draw)[i+1]);
+ else {
+ dc.DrawLine(m_rect.GetLeft()+i,(*m_line_to_draw)[i],m_rect.GetLeft()+i+1,(*m_line_to_draw)[i]);
+ dc.DrawLine(m_rect.GetLeft()+i+1,(*m_line_to_draw)[i],m_rect.GetLeft()+i+1,(*m_line_to_draw)[i+1]);
+ }
+ }
+ }
+
+ // draw draggable buttons
+ dc.SetBrush(*wxBLUE_BRUSH);
+ dc.SetPen( wxPen( wxColor(0,0,0), 1 ) );
+ for (auto& button : m_buttons[m_current_extruder])
+ //dc.DrawRectangle(math_to_screen(button.get_pos())-wxPoint(side/2.,side/2.), wxSize(side,side));
+ dc.DrawCircle(math_to_screen(button.get_pos()),side/2.);
+ //dc.DrawRectangle(math_to_screen(button.get_pos()-wxPoint2DDouble(0.125,0))-wxPoint(0,5),wxSize(50,10));
+
+ // draw x-axis:
+ float last_mark = -10000;
+ for (float math_x=int(visible_area->m_x*10)/10 ; math_x <= (visible_area->m_x+visible_area->m_width) ; math_x+=0.1) {
+ int x = math_to_screen(wxPoint2DDouble(math_x,visible_area->m_y)).x;
+ int y = m_rect.GetBottom();
+ if (x-last_mark < 50) continue;
+ dc.DrawLine(x,y+3,x,y-3);
+ dc.DrawText(wxString().Format(wxT("%.1f"), math_x),wxPoint(x-10,y+7));
+ last_mark = x;
+ }
+
+ // draw y-axis:
+ last_mark=10000;
+ for (int math_y=visible_area->m_y ; math_y <= (visible_area->m_y+visible_area->m_height) ; math_y+=1) {
+ int y = math_to_screen(wxPoint2DDouble(visible_area->m_x,math_y)).y;
+ int x = m_rect.GetLeft();
+ if (last_mark-y < 50) continue;
+ dc.DrawLine(x-3,y,x+3,y);
+ dc.DrawText(wxString()<<math_y,wxPoint(x-25,y-7));
+ last_mark = y;
+ }
+
+
+
+}
+
+void Chart::mouse_right_button_clicked(wxMouseEvent& event) {
+ if (!manual_points_manipulation)
+ return;
+ wxPoint point = event.GetPosition();
+ int button_index = which_button_is_clicked(point);
+ if (button_index != -1 && m_buttons[m_current_extruder].size()>2) {
+ m_buttons[m_current_extruder].erase(m_buttons[m_current_extruder].begin()+button_index);
+ recalculate_line();
+ }
+}
+
+
+
+void Chart::mouse_clicked(wxMouseEvent& event) {
+ wxPoint point = event.GetPosition();
+ int button_index = which_button_is_clicked(point);
+ if ( button_index != -1) {
+ m_dragged = &m_buttons[m_current_extruder][button_index];
+ m_previous_mouse = point;
+ }
+}
+
+
+
+void Chart::mouse_moved(wxMouseEvent& event) {
+ if (!event.Dragging() || !m_dragged) return;
+ wxPoint pos = event.GetPosition();
+ wxRect rect = m_rect;
+ rect.Deflate(side/2.);
+ if (!(rect.Contains(pos))) { // the mouse left chart area
+ mouse_left_window(event);
+ return;
+ }
+ int delta_x = pos.x - m_previous_mouse.x;
+ int delta_y = pos.y - m_previous_mouse.y;
+ m_dragged->move(fixed_x?0:double(delta_x)/m_rect.GetWidth() * visible_area->m_width,-double(delta_y)/m_rect.GetHeight() * visible_area->m_height);
+ m_previous_mouse = pos;
+ recalculate_line();
+}
+
+
+
+void Chart::mouse_double_clicked(wxMouseEvent& event) {
+ if (!manual_points_manipulation)
+ return;
+ wxPoint point = event.GetPosition();
+ if (!m_rect.Contains(point)) // the click is outside the chart
+ return;
+ m_buttons[m_current_extruder].push_back(screen_to_math(point));
+ std::sort(m_buttons[m_current_extruder].begin(),m_buttons[m_current_extruder].end());
+ recalculate_line();
+ return;
+}
+
+
+
+
+void Chart::recalculate_line() {
+ std::vector<wxPoint> points;
+ for (auto& but : m_buttons[m_current_extruder]) {
+ points.push_back(wxPoint(math_to_screen(but.get_pos())));
+ if (points.size()>1 && points.back().x==points[points.size()-2].x) points.pop_back();
+ if (points.size()>1 && points.back().x > m_rect.GetRight()) {
+ points.pop_back();
+ break;
+ }
+ }
+ std::sort(points.begin(),points.end(),[](wxPoint& a,wxPoint& b) { return a.x < b.x; });
+
+ m_line_to_draw->clear();
+ m_total_volume = 0.f;
+
+
+ // Cubic spline interpolation: see https://en.wikiversity.org/wiki/Cubic_Spline_Interpolation#Methods
+ const bool boundary_first_derivative = true; // true - first derivative is 0 at the leftmost and rightmost point
+ // false - second ---- || -------
+ const int N = points.size()-1; // last point can be accessed as N, we have N+1 total points
+ std::vector<float> diag(N+1);
+ std::vector<float> mu(N+1);
+ std::vector<float> lambda(N+1);
+ std::vector<float> h(N+1);
+ std::vector<float> rhs(N+1);
+
+ // let's fill in inner equations
+ for (int i=1;i<=N;++i) h[i] = points[i].x-points[i-1].x;
+ std::fill(diag.begin(),diag.end(),2.f);
+ for (int i=1;i<=N-1;++i) {
+ mu[i] = h[i]/(h[i]+h[i+1]);
+ lambda[i] = 1.f - mu[i];
+ rhs[i] = 6 * ( float(points[i+1].y-points[i].y )/(h[i+1]*(points[i+1].x-points[i-1].x)) -
+ float(points[i].y -points[i-1].y)/(h[i] *(points[i+1].x-points[i-1].x)) );
+ }
+
+ // now fill in the first and last equations, according to boundary conditions:
+ if (boundary_first_derivative) {
+ const float endpoints_derivative = 0;
+ lambda[0] = 1;
+ mu[N] = 1;
+ rhs[0] = (6.f/h[1]) * (float(points[0].y-points[1].y)/(points[0].x-points[1].x) - endpoints_derivative);
+ rhs[N] = (6.f/h[N]) * (endpoints_derivative - float(points[N-1].y-points[N].y)/(points[N-1].x-points[N].x));
+ }
+ else {
+ lambda[0] = 0;
+ mu[N] = 0;
+ rhs[0] = 0;
+ rhs[N] = 0;
+ }
+
+ // the trilinear system is ready to be solved:
+ for (int i=1;i<=N;++i) {
+ float multiple = mu[i]/diag[i-1]; // let's subtract proper multiple of above equation
+ diag[i]-= multiple * lambda[i-1];
+ rhs[i] -= multiple * rhs[i-1];
+ }
+ // now the back substitution (vector mu contains invalid values from now on):
+ rhs[N] = rhs[N]/diag[N];
+ for (int i=N-1;i>=0;--i)
+ rhs[i] = (rhs[i]-lambda[i]*rhs[i+1])/diag[i];
+
+
+
+
+ unsigned int i=1;
+ float y=0.f;
+ for (int x=m_rect.GetLeft(); x<=m_rect.GetRight() ; ++x) {
+ if (splines) {
+ if (i<points.size()-1 && points[i].x < x ) {
+ ++i;
+ }
+ if (points[0].x > x)
+ y = points[0].y;
+ else
+ if (points[N].x < x)
+ y = points[N].y;
+ else
+ y = (rhs[i-1]*pow(points[i].x-x,3)+rhs[i]*pow(x-points[i-1].x,3)) / (6*h[i]) +
+ (points[i-1].y-rhs[i-1]*h[i]*h[i]/6.f) * (points[i].x-x)/h[i] +
+ (points[i].y -rhs[i] *h[i]*h[i]/6.f) * (x-points[i-1].x)/h[i];
+ m_line_to_draw->push_back(y);
+ }
+ else {
+ float x_math = screen_to_math(wxPoint(x,0)).m_x;
+ if (i+2<=points.size() && m_buttons[m_current_extruder][i+1].get_pos().m_x-0.125 < x_math)
+ ++i;
+ m_line_to_draw->push_back(math_to_screen(wxPoint2DDouble(x_math,m_buttons[m_current_extruder][i].get_pos().m_y)).y);
+ }
+
+
+ m_line_to_draw->back() = std::max(m_line_to_draw->back(), m_rect.GetTop()-1);
+ m_line_to_draw->back() = std::min(m_line_to_draw->back(), m_rect.GetBottom()-1);
+ m_total_volume += (m_rect.GetBottom() - m_line_to_draw->back()) * (visible_area->m_width / m_rect.GetWidth()) * (visible_area->m_height / m_rect.GetHeight());
+ }
+
+ wxPostEvent(this->GetParent(), wxCommandEvent(EVT_WIPE_TOWER_CHART_CHANGED));
+ Refresh();
+}
+
+
+
+std::vector<std::vector<float>> Chart::get_ramming_speeds(float sampling) const {
+ std::vector<std::vector<float>> speeds_out;
+ for (unsigned int extruder_id = 0;extruder_id<m_buttons.size();++extruder_id) { // repeat for each extruder
+ std::vector<float> this_extruder;
+ const int number_of_samples = std::round( visible_areas[extruder_id].m_width / sampling);
+ if (number_of_samples>0) {
+ const int dx = (m_lines_to_draw[extruder_id].size()-1) / number_of_samples;
+ for (int j=0;j<number_of_samples;++j) {
+ float left = screen_to_math(wxPoint(0,m_lines_to_draw[extruder_id][j*dx])).m_y;
+ float right = screen_to_math(wxPoint(0,m_lines_to_draw[extruder_id][(j+1)*dx])).m_y;
+ this_extruder.push_back((left+right)/2.f);
+ }
+ }
+ /*else
+ this_extruder.push_back(0.f); // so it does not stay empty*/
+ speeds_out.push_back(std::move(this_extruder));
+ }
+
+ return speeds_out;
+}
+
+
+std::vector<std::vector<std::pair<float,float>>> Chart::get_buttons() const {
+ std::vector<std::vector<std::pair<float, float>>> buttons_out;
+ for (const auto& ext : m_buttons) {
+ std::vector<std::pair<float,float>> this_extruder;
+ for (const auto& button : ext)
+ this_extruder.push_back(std::make_pair(button.get_pos().m_x,button.get_pos().m_y));
+ buttons_out.push_back(std::move(this_extruder));
+ }
+ return buttons_out;
+}
+
+
+
+
+BEGIN_EVENT_TABLE(Chart, wxWindow)
+EVT_MOTION(Chart::mouse_moved)
+EVT_LEFT_DOWN(Chart::mouse_clicked)
+EVT_LEFT_UP(Chart::mouse_released)
+EVT_LEFT_DCLICK(Chart::mouse_double_clicked)
+EVT_RIGHT_DOWN(Chart::mouse_right_button_clicked)
+EVT_LEAVE_WINDOW(Chart::mouse_left_window)
+EVT_PAINT(Chart::paint_event)
+END_EVENT_TABLE()
diff --git a/xs/src/slic3r/GUI/RammingChart.hpp b/xs/src/slic3r/GUI/RammingChart.hpp
new file mode 100644
index 000000000..5443e957a
--- /dev/null
+++ b/xs/src/slic3r/GUI/RammingChart.hpp
@@ -0,0 +1,129 @@
+#ifndef RAMMING_CHART_H_
+#define RAMMING_CHART_H_
+
+#include <vector>
+#include <wx/wxprec.h>
+#ifndef WX_PRECOMP
+ #include <wx/wx.h>
+#endif
+
+wxDECLARE_EVENT(EVT_WIPE_TOWER_CHART_CHANGED, wxCommandEvent);
+
+
+class Chart : public wxWindow {
+
+public:
+ Chart(wxWindow* parent, wxRect rect,const std::vector<std::vector<std::pair<float,float>>>& initial_buttons,std::vector<std::vector<float>> ramming_speed, float sampling) :
+ wxWindow(parent,wxID_ANY,rect.GetTopLeft(),rect.GetSize())
+ {
+ m_rect=wxRect(wxPoint(30,0),rect.GetSize()-wxSize(30,30));
+ for (int i=0;i<4;++i) {
+ visible_areas.push_back(wxRect2DDouble(0.0, 0.0, sampling*ramming_speed[i].size(), 20.));
+ m_buttons.push_back(std::vector<ButtonToDrag>());
+ m_lines_to_draw.push_back(std::vector<int>());
+ if (initial_buttons.size()>0)
+ for (const auto& pair : initial_buttons[i])
+ m_buttons.back().push_back(wxPoint2DDouble(pair.first,pair.second));
+ set_extruder(i); // to calculate all interpolating splines
+ }
+ set_extruder(0);
+ }
+ void set_extruder(unsigned ext) {
+ if (ext>=4) return;
+ m_current_extruder = ext;
+ visible_area = &(visible_areas[ext]);
+ m_line_to_draw = &(m_lines_to_draw[ext]);
+ recalculate_line();
+ }
+ void set_xy_range(float x,float y) {
+ x = int(x/0.5) * 0.5;
+ if (x>=0) visible_area->SetRight(x);
+ if (y>=0) visible_area->SetBottom(y);
+ recalculate_line();
+ }
+ float get_volume() const { return m_total_volume; }
+ float get_time() const { return visible_area->m_width; }
+ std::vector<std::vector<float>> get_ramming_speeds(float sampling) const; //returns sampled ramming speed for all extruders
+ std::vector<std::vector<std::pair<float,float>>> get_buttons() const; // returns buttons position for all extruders
+
+
+ void draw(wxDC& dc);
+
+ void mouse_clicked(wxMouseEvent& event);
+ void mouse_right_button_clicked(wxMouseEvent& event);
+ void mouse_moved(wxMouseEvent& event);
+ void mouse_double_clicked(wxMouseEvent& event);
+ void mouse_left_window(wxMouseEvent&) { m_dragged = nullptr; }
+ void mouse_released(wxMouseEvent&) { m_dragged = nullptr; }
+ void paint_event(wxPaintEvent&) { wxPaintDC dc(this); draw(dc); }
+ DECLARE_EVENT_TABLE()
+
+
+
+private:
+ static const bool fixed_x = true;
+ static const bool splines = true;
+ static const bool manual_points_manipulation = false;
+ static const int side = 10; // side of draggable button
+
+ class ButtonToDrag {
+ public:
+ bool operator<(ButtonToDrag& a) { return m_pos.m_x < a.m_pos.m_x; }
+ ButtonToDrag(wxPoint2DDouble pos) : m_pos{pos} {};
+ wxPoint2DDouble get_pos() const { return m_pos; }
+ void move(double x,double y) { m_pos.m_x+=x; m_pos.m_y+=y; }
+ private:
+ wxPoint2DDouble m_pos; // position in math coordinates
+ };
+
+
+
+ wxPoint math_to_screen(const wxPoint2DDouble& math) const {
+ wxPoint screen;
+ screen.x = (math.m_x-visible_area->m_x) * (m_rect.GetWidth() / visible_area->m_width );
+ screen.y = (math.m_y-visible_area->m_y) * (m_rect.GetHeight() / visible_area->m_height );
+ screen.y *= -1;
+ screen += m_rect.GetLeftBottom();
+ return screen;
+ }
+ wxPoint2DDouble screen_to_math(const wxPoint& screen) const {
+ wxPoint2DDouble math = screen;
+ math -= m_rect.GetLeftBottom();
+ math.m_y *= -1;
+ math.m_x *= visible_area->m_width / m_rect.GetWidth(); // scales to [0;1]x[0,1]
+ math.m_y *= visible_area->m_height / m_rect.GetHeight();
+ return (math+visible_area->GetLeftTop());
+ }
+
+ int which_button_is_clicked(const wxPoint& point) const {
+ if (!m_rect.Contains(point))
+ return -1;
+ for (uint i=0;i<m_buttons[m_current_extruder].size();++i) {
+ wxRect rect(math_to_screen(m_buttons[m_current_extruder][i].get_pos())-wxPoint(side/2.,side/2.),wxSize(side,side)); // bounding rectangle of this button
+ if ( rect.Contains(point) )
+ return i;
+ }
+ return (-1);
+ }
+
+
+ void recalculate_line();
+ void recalculate_volume();
+
+
+ unsigned m_current_extruder = 0;
+ wxRect m_rect; // rectangle on screen the chart is mapped into (screen coordinates)
+ wxPoint m_previous_mouse;
+ std::vector< std::vector<ButtonToDrag> > m_buttons;
+ std::vector< std::vector<int> > m_lines_to_draw;
+ std::vector< wxRect2DDouble > visible_areas;
+ wxRect2DDouble* visible_area = nullptr;
+ std::vector<int>* m_line_to_draw = nullptr;
+ ButtonToDrag* m_dragged = nullptr;
+ float m_total_volume = 0.f;
+
+
+};
+
+
+#endif // RAMMING_CHART_H_ \ No newline at end of file
diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp
index ebfa69eb5..7e1ad5dc5 100644
--- a/xs/src/slic3r/GUI/Tab.cpp
+++ b/xs/src/slic3r/GUI/Tab.cpp
@@ -3,6 +3,7 @@
#include "PresetBundle.hpp"
#include "PresetHints.hpp"
#include "../../libslic3r/Utils.hpp"
+#include "WipeTowerDialog.hpp"
#include <wx/app.h>
#include <wx/button.h>
@@ -440,6 +441,7 @@ void TabPrint::build()
optgroup->append_single_option_line("ooze_prevention");
optgroup->append_single_option_line("standby_temperature_delta");
+ if (true) {
optgroup = page->new_optgroup(_L("Wipe tower"));
optgroup->append_single_option_line("wipe_tower");
optgroup->append_single_option_line("wipe_tower_x");
@@ -447,6 +449,27 @@ void TabPrint::build()
optgroup->append_single_option_line("wipe_tower_width");
optgroup->append_single_option_line("wipe_tower_per_color_wipe");
optgroup->append_single_option_line("wipe_tower_rotation_angle");
+ Line line{ _L("Advanced"), "" };
+ line.widget = [this](wxWindow* parent){
+ m_wipe_tower_btn = new wxButton(parent, wxID_ANY, _L("Advanced settings")+"\u2026", wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT);
+ auto sizer = new wxBoxSizer(wxHORIZONTAL);
+ sizer->Add(m_wipe_tower_btn);
+ m_wipe_tower_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e)
+ {
+ std::string init_data = (m_config->option<ConfigOptionString>("wipe_tower_advanced"))->value;
+ std::cout << "dialog init: " << init_data << std::endl;
+ WipeTowerDialog dlg(this,init_data); // dlg lives on stack, no need to call Destroy
+
+ if (dlg.ShowModal() == wxID_OK) {
+ load_key_value("wipe_tower_advanced", dlg.GetValue());
+ std::cout << std::endl << "dialog returned: " << dlg.GetValue() << std::endl;
+ }
+ }));
+ return sizer;
+ };
+ optgroup->append_line(line);
+ }
+
optgroup = page->new_optgroup(_L("Advanced"));
optgroup->append_single_option_line("interface_shells");
@@ -766,6 +789,7 @@ void TabPrint::update()
vec_enable = { "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_per_color_wipe", "wipe_tower_rotation_angle"};
for (auto el : vec_enable)
get_field(el)->toggle(have_wipe_tower);
+ m_wipe_tower_btn->Enable(have_wipe_tower);
m_recommended_thin_wall_thickness_description_line->SetText(
PresetHints::recommended_thin_wall_thickness(*m_preset_bundle));
diff --git a/xs/src/slic3r/GUI/Tab.hpp b/xs/src/slic3r/GUI/Tab.hpp
index 4f3a15736..b6d273e38 100644
--- a/xs/src/slic3r/GUI/Tab.hpp
+++ b/xs/src/slic3r/GUI/Tab.hpp
@@ -90,6 +90,7 @@ protected:
wxImageList* m_icons;
wxCheckBox* m_compatible_printers_checkbox;
wxButton* m_compatible_printers_btn;
+ wxButton* m_wipe_tower_btn;
int m_icon_count;
std::map<std::string, size_t> m_icon_index; // Map from an icon file name to its index in $self->{icons}.
diff --git a/xs/src/slic3r/GUI/WipeTowerDialog.cpp b/xs/src/slic3r/GUI/WipeTowerDialog.cpp
new file mode 100644
index 000000000..5b188e6d1
--- /dev/null
+++ b/xs/src/slic3r/GUI/WipeTowerDialog.cpp
@@ -0,0 +1,264 @@
+#include "WipeTowerDialog.hpp"
+
+// Human-readable output of Parameters structure
+std::ostream& operator<<(std::ostream& str,Slic3r::WipeTowerParameters& par) {
+ str << "bridging: " << par.bridging << "\n";
+ str << "adhesion: " << par.adhesion << "\n";
+ str << "sampling: " << par.sampling << "\n";
+
+ str << "cooling times: ";
+ for (const auto& a : par.cooling_time) str << a << " ";
+
+ str << "line widths: ";
+ for (const auto& a : par.ramming_line_width_multiplicator) str << a << " ";
+
+ str << "line spacing: ";
+ for (const auto& a : par.ramming_step_multiplicator) str << a << " ";
+
+ str<<"\n\nramming_speeds:\n";
+ for (const auto& a : par.ramming_speed) {
+ for (const auto& b : a)
+ str << b << " ";
+ str<<"\n";
+ }
+ str<<"\n\nramming_buttons:\n";
+ for (const auto& a : par.ramming_buttons) {
+ for (const auto& b : a) {
+ Slic3r::operator <<(str,b); // temporary hack (this << is in the namespace Slic3r)
+ str << " | "; // the function will be deleted after everything is debugged, anyway
+ }
+ str<<"\n";
+ }
+ str<<"\n\nwipe volumes:\n";
+ for (const auto& a : par.wipe_volumes) {
+ for (const auto& b : a)
+ str << b << " ";
+ str<<"\n";
+ }
+ str<<"\n\nfilament wipe volumes:\n";
+ for (const auto& a : par.filament_wipe_volumes) {
+ Slic3r::operator <<(str,a);
+ str << " ";
+ }
+ str<<"\n";
+
+ return str;
+}
+
+
+
+
+RammingPanel::RammingPanel(wxWindow* parent,const Slic3r::WipeTowerParameters& p)
+: wxPanel(parent,wxID_ANY,wxPoint(0,0),wxSize(0,0),wxBORDER_RAISED)
+{
+ new wxStaticText(this,wxID_ANY,wxString("Total ramming time (s):"), wxPoint(500,105), wxSize(200,25),wxALIGN_LEFT);
+ m_widget_time = new wxSpinCtrlDouble(this,wxID_ANY,wxEmptyString, wxPoint(700,100), wxSize(75,25),wxSP_ARROW_KEYS|wxALIGN_RIGHT,0.,5.0,3.,0.5);
+ new wxStaticText(this,wxID_ANY,wxString("Total rammed volume (mm3):"), wxPoint(500,135), wxSize(200,25),wxALIGN_LEFT);
+ m_widget_volume = new wxSpinCtrl(this,wxID_ANY,wxEmptyString, wxPoint(700,130), wxSize(75,25),wxSP_ARROW_KEYS|wxALIGN_RIGHT,0,10000,0);
+ new wxStaticText(this,wxID_ANY,wxString("Ramming line width (%):"), wxPoint(500,205), wxSize(200,25),wxALIGN_LEFT);
+ m_widget_ramming_line_width_multiplicator = new wxSpinCtrl(this,wxID_ANY,wxEmptyString, wxPoint(700,200), wxSize(75,25),wxSP_ARROW_KEYS|wxALIGN_RIGHT,10,200,100);
+ new wxStaticText(this,wxID_ANY,wxString("Ramming line spacing (%):"), wxPoint(500,235), wxSize(200,25),wxALIGN_LEFT);
+ m_widget_ramming_step_multiplicator = new wxSpinCtrl(this,wxID_ANY,wxEmptyString, wxPoint(700,230), wxSize(75,25),wxSP_ARROW_KEYS|wxALIGN_RIGHT,10,200,100);
+ new wxStaticText(this,wxID_ANY,wxString("Extruder #:"), wxPoint(500,12), wxSize(200,25),wxALIGN_LEFT);
+
+ wxArrayString choices;
+ for (unsigned int i=0;i<p.ramming_line_width_multiplicator.size();++i) { // for all extruders
+ choices.Add(wxString("")<<i+1);
+ m_ramming_line_width_multiplicators.push_back(p.ramming_line_width_multiplicator[i]*100);
+ m_ramming_step_multiplicators.push_back(p.ramming_step_multiplicator[i]*100);
+ }
+ m_widget_extruder = new wxChoice(this,wxID_ANY,wxPoint(580,5),wxSize(50,27),choices);
+
+ m_chart = new Chart(this,wxRect(10,10,480,360),p.ramming_buttons,p.ramming_speed,p.sampling);
+
+ m_chart->set_extruder(0);
+ m_widget_time->SetValue(m_chart->get_time());
+ m_widget_time->SetDigits(2);
+ m_widget_volume->SetValue(m_chart->get_volume());
+ m_widget_volume->Disable();
+ m_widget_extruder->SetSelection(0);
+ extruder_selection_changed(); // tell everyone to redraw
+
+ m_widget_ramming_step_multiplicator->Bind(wxEVT_TEXT,[this](wxCommandEvent&) { line_parameters_changed(); });
+ m_widget_ramming_line_width_multiplicator->Bind(wxEVT_TEXT,[this](wxCommandEvent&) { line_parameters_changed(); });
+ m_widget_extruder->Bind(wxEVT_CHOICE,[this](wxCommandEvent&) { extruder_selection_changed(); });
+ m_widget_time->Bind(wxEVT_TEXT,[this](wxCommandEvent&) {m_chart->set_xy_range(m_widget_time->GetValue(),-1);});
+ m_widget_time->Bind(wxEVT_CHAR,[](wxKeyEvent&){}); // do nothing - prevents the user to change the value
+ m_widget_volume->Bind(wxEVT_CHAR,[](wxKeyEvent&){}); // do nothing - prevents the user to change the value
+ Bind(EVT_WIPE_TOWER_CHART_CHANGED,[this](wxCommandEvent&) {m_widget_volume->SetValue(m_chart->get_volume()); m_widget_time->SetValue(m_chart->get_time());} );
+}
+
+void RammingPanel::fill_parameters(Slic3r::WipeTowerParameters& p)
+{
+ if (!m_chart) return;
+ p.ramming_buttons = m_chart->get_buttons();
+ p.ramming_speed = m_chart->get_ramming_speeds(p.sampling);
+ for (unsigned int i=0;i<m_ramming_line_width_multiplicators.size();++i) { // we assume m_ramming_line_width_multiplicators.size() == m_ramming_step_multiplicators.size()
+ p.ramming_line_width_multiplicator.push_back(m_ramming_line_width_multiplicators[i]/100.f);
+ p.ramming_step_multiplicator.push_back(m_ramming_step_multiplicators[i]/100.f);
+ }
+}
+
+void RammingPanel::extruder_selection_changed() {
+ m_current_extruder = m_widget_extruder->GetSelection();
+ m_chart->set_extruder(m_current_extruder); // tell our chart to redraw
+ m_widget_ramming_line_width_multiplicator ->SetValue(m_ramming_line_width_multiplicators[m_current_extruder]);
+ m_widget_ramming_step_multiplicator->SetValue(m_ramming_step_multiplicators[m_current_extruder]);
+}
+
+void RammingPanel::line_parameters_changed() {
+ m_ramming_line_width_multiplicators[m_current_extruder]=m_widget_ramming_line_width_multiplicator->GetValue();
+ m_ramming_step_multiplicators[m_current_extruder]=m_widget_ramming_step_multiplicator->GetValue();
+}
+
+
+
+
+
+
+CoolingPanel::CoolingPanel(wxWindow* parent,const Slic3r::WipeTowerParameters& p)
+: wxPanel(parent,wxID_ANY,wxPoint(0,0),wxSize(0,0),wxBORDER_RAISED)
+{
+ new wxStaticText(this,wxID_ANY,wxString("Time (in seconds) reserved for cooling after unload:"),wxPoint(220,50) ,wxSize(400,25),wxALIGN_LEFT);
+ for (int i=0;i<4;++i) {
+ new wxStaticText(this,wxID_ANY,wxString("Filament #")<<i+1<<": ",wxPoint(300,105+30*i) ,wxSize(150,25),wxALIGN_LEFT);
+ m_widget_edits.push_back(new wxSpinCtrl(this,wxID_ANY,wxEmptyString,wxPoint(400,100+30*i),wxSize(75,25),wxSP_ARROW_KEYS|wxALIGN_RIGHT,0,30,15));
+ }
+ for (unsigned int i=0;i<p.cooling_time.size();++i) {
+ if (i>=m_widget_edits.size())
+ break; // so we don't initialize non-existent widget
+ m_widget_edits[i]->SetValue(p.cooling_time[i]);
+ }
+}
+
+void CoolingPanel::fill_parameters(Slic3r::WipeTowerParameters& p) {
+ p.cooling_time.clear();
+ for (int i=0;i<4;++i)
+ p.cooling_time.push_back(m_widget_edits[i]->GetValue());
+}
+
+
+
+WipingPanel::WipingPanel(wxWindow* parent,const Slic3r::WipeTowerParameters& p)
+: wxPanel(parent,wxID_ANY,wxPoint(0,0),wxSize(0,0),wxBORDER_RAISED)
+{
+ const int N = 4; // number of extruders
+ new wxStaticText(this,wxID_ANY,wxString("Volume to wipe when the filament is being"),wxPoint(40,55) ,wxSize(500,25));
+ new wxStaticText(this,wxID_ANY,wxString("unloaded"),wxPoint(110,75) ,wxSize(500,25));
+ new wxStaticText(this,wxID_ANY,wxString("loaded"),wxPoint(195,75) ,wxSize(500,25));
+ m_widget_button = new wxButton(this,wxID_ANY,"-> Fill in the matrix ->",wxPoint(300,130),wxSize(175,50));
+ for (int i=0;i<N;++i) {
+ new wxStaticText(this,wxID_ANY,wxString("Filament #")<<i+1<<": ",wxPoint(20,105+30*i) ,wxSize(150,25),wxALIGN_LEFT);
+ m_old.push_back(new wxSpinCtrl(this,wxID_ANY,wxEmptyString,wxPoint(120,100+30*i),wxSize(50,25),wxSP_ARROW_KEYS|wxALIGN_RIGHT,0,100,p.filament_wipe_volumes[i].first));
+ m_new.push_back(new wxSpinCtrl(this,wxID_ANY,wxEmptyString,wxPoint(195,100+30*i),wxSize(50,25),wxSP_ARROW_KEYS|wxALIGN_RIGHT,0,100,p.filament_wipe_volumes[i].second));
+ }
+
+ wxPoint origin(515,55);
+ for (int i=0;i<N;++i) {
+ edit_boxes.push_back(std::vector<wxTextCtrl*>(0));
+ new wxStaticText(this,wxID_ANY,wxString("")<<i+1,origin+wxPoint(45+60*i,25) ,wxSize(20,25));
+ new wxStaticText(this,wxID_ANY,wxString("")<<i+1,origin+wxPoint(0,50+30*i) ,wxSize(500,25));
+ for (int j=0;j<N;++j) {
+ edit_boxes.back().push_back(new wxTextCtrl(this,wxID_ANY,wxEmptyString,origin+wxPoint(25+60*i,45+30*j),wxSize(50,25)));
+ if (i==j)
+ edit_boxes[i][j]->Disable();
+ else
+ edit_boxes[i][j]->SetValue(wxString("")<<int(p.wipe_volumes[j][i]));
+ }
+ new wxStaticText(this,wxID_ANY,wxString("Filament changed to"),origin+wxPoint(75,0) ,wxSize(500,25));
+ }
+
+ m_widget_button->Bind(wxEVT_BUTTON,[this](wxCommandEvent&){fill_in_matrix();});
+}
+
+void WipingPanel::fill_parameters(Slic3r::WipeTowerParameters& p) {
+ p.wipe_volumes.clear();
+ p.filament_wipe_volumes.clear();
+ for (int i=0;i<4;++i) {
+ // first go through the full matrix:
+ p.wipe_volumes.push_back(std::vector<float>());
+ for (int j=0;j<4;++j) {
+ double val = 0.;
+ edit_boxes[j][i]->GetValue().ToDouble(&val);
+ p.wipe_volumes[i].push_back((float)val);
+ }
+
+ // now the filament volumes:
+ p.filament_wipe_volumes.push_back(std::make_pair(m_old[i]->GetValue(),m_new[i]->GetValue()));
+ }
+}
+
+void WipingPanel::fill_in_matrix() {
+ wxArrayString choices;
+ choices.Add("sum");
+ choices.Add("maximum");
+ wxSingleChoiceDialog dialog(this,"How shall I calculate volume for any given pair?\n\nI can either sum volumes for old and new filament, or just use the higher value.","DEBUGGING",choices);
+ if (dialog.ShowModal() == wxID_CANCEL)
+ return;
+ for (unsigned i=0;i<4;++i) {
+ for (unsigned j=0;j<4;++j) {
+ if (i==j) continue;
+ if (!dialog.GetSelection()) edit_boxes[j][i]->SetValue(wxString("")<< (m_old[i]->GetValue() + m_new[j]->GetValue()));
+ else
+ edit_boxes[j][i]->SetValue(wxString("")<< (std::max(m_old[i]->GetValue(), m_new[j]->GetValue())));
+ }
+ }
+}
+
+
+
+GeneralPanel::GeneralPanel(wxWindow* parent,const Slic3r::WipeTowerParameters& p) : wxPanel(parent,wxID_ANY,wxPoint(0,0),wxSize(0,0),wxBORDER_RAISED) {
+ new wxStaticText(this,wxID_ANY,wxString("Maximum bridging over sparse infill (mm):"),wxPoint(100,105) ,wxSize(280,25),wxALIGN_LEFT);
+ m_widget_bridge = new wxSpinCtrl(this,wxID_ANY,wxEmptyString,wxPoint(380,100),wxSize(50,25),wxALIGN_RIGHT|wxSP_ARROW_KEYS,1,50,10);
+ m_widget_adhesion = new wxCheckBox(this,wxID_ANY,"Increased adhesion of first layer",wxPoint(100,150),wxSize(330,25),wxALIGN_RIGHT);
+ m_widget_bridge->SetValue(p.bridging);
+ m_widget_adhesion->SetValue(p.adhesion);
+}
+
+void GeneralPanel::fill_parameters(Slic3r::WipeTowerParameters& p) {
+ p.bridging = m_widget_bridge->GetValue();
+ p.adhesion = m_widget_adhesion->GetValue();
+}
+
+
+
+
+
+WipeTowerDialog::WipeTowerDialog(wxWindow* parent,const std::string& init_data)
+: wxDialog(parent, -1, wxT("Wipe tower advanced settings"), wxPoint(50,50), wxSize(800,550), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
+{
+ this->Centre();
+
+ Slic3r::WipeTowerParameters parameters(init_data);
+ if (!parameters.validate()) {
+ wxMessageDialog(this,"Wipe tower parameters not parsed correctly!\nRestoring default settings.","Error",wxICON_ERROR);
+ parameters.set_defaults();
+ }
+
+ wxNotebook* notebook = new wxNotebook(this,wxID_ANY,wxPoint(0,0),wxSize(800,450));
+
+ m_panel_general = new GeneralPanel(notebook,parameters);
+ m_panel_ramming = new RammingPanel(notebook,parameters);
+ m_panel_cooling = new CoolingPanel(notebook,parameters);
+ m_panel_wiping = new WipingPanel(notebook,parameters);
+ notebook->AddPage(m_panel_general,"General");
+ notebook->AddPage(m_panel_ramming,"Ramming");
+ notebook->AddPage(m_panel_cooling,"Cooling");
+ notebook->AddPage(m_panel_wiping,"Wiping");
+ this->Show();
+
+ auto main_sizer = new wxBoxSizer(wxVERTICAL);
+ main_sizer->Add(notebook, 1, wxEXPAND);
+ main_sizer->Add(CreateButtonSizer(wxOK | wxCANCEL), 0, wxALIGN_CENTER_HORIZONTAL | wxBOTTOM, 10);
+ SetSizer(main_sizer);
+ SetMinSize(GetSize());
+ main_sizer->SetSizeHints(this);
+
+ this->Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent& e) { EndModal(wxCANCEL); });
+
+ this->Bind(wxEVT_BUTTON,[this](wxCommandEvent&) {
+ m_output_data=read_dialog_values();
+ EndModal(wxID_OK);
+ },wxID_OK);
+}
+
diff --git a/xs/src/slic3r/GUI/WipeTowerDialog.hpp b/xs/src/slic3r/GUI/WipeTowerDialog.hpp
new file mode 100644
index 000000000..452886bc3
--- /dev/null
+++ b/xs/src/slic3r/GUI/WipeTowerDialog.hpp
@@ -0,0 +1,115 @@
+#ifndef _WIPE_TOWER_DIALOG_H_
+#define _WIPE_TOWER_DIALOG_H_
+
+#include <wx/spinctrl.h>
+#include <wx/stattext.h>
+#include <wx/textctrl.h>
+#include <wx/checkbox.h>
+#include <wx/choicdlg.h>
+#include <wx/notebook.h>
+#include <wx/msgdlg.h>
+
+#include "../../libslic3r/GCode/WipeTowerPrusaMM.hpp"
+#include "RammingChart.hpp"
+
+
+// Human-readable output of Parameters structure
+std::ostream& operator<<(std::ostream& str,Slic3r::WipeTowerParameters& par);
+
+
+class RammingPanel : public wxPanel {
+public:
+ RammingPanel(wxWindow* parent,const Slic3r::WipeTowerParameters& p);
+ void fill_parameters(Slic3r::WipeTowerParameters& p);
+
+private:
+ Chart* m_chart = nullptr;
+ wxSpinCtrl* m_widget_volume = nullptr;
+ wxSpinCtrl* m_widget_ramming_line_width_multiplicator = nullptr;
+ wxSpinCtrl* m_widget_ramming_step_multiplicator = nullptr;
+ wxSpinCtrlDouble* m_widget_time = nullptr;
+ wxChoice* m_widget_extruder = nullptr;
+ std::vector<int> m_ramming_step_multiplicators;
+ std::vector<int> m_ramming_line_width_multiplicators;
+ int m_current_extruder = 0; // zero-based index
+
+ void extruder_selection_changed();
+
+ void line_parameters_changed();
+};
+
+
+
+
+
+
+class CoolingPanel : public wxPanel {
+public:
+ CoolingPanel(wxWindow* parent,const Slic3r::WipeTowerParameters& p);
+ void fill_parameters(Slic3r::WipeTowerParameters& p);
+
+private:
+ std::vector<wxSpinCtrl*> m_widget_edits;
+};
+
+
+
+
+
+
+class WipingPanel : public wxPanel {
+public:
+ WipingPanel(wxWindow* parent,const Slic3r::WipeTowerParameters& p);
+ void fill_parameters(Slic3r::WipeTowerParameters& p);
+
+private:
+ void fill_in_matrix();
+
+ std::vector<wxSpinCtrl*> m_old;
+ std::vector<wxSpinCtrl*> m_new;
+ std::vector<std::vector<wxTextCtrl*>> edit_boxes;
+ wxButton* m_widget_button=nullptr;
+};
+
+
+
+
+class GeneralPanel : public wxPanel {
+public:
+ GeneralPanel(wxWindow* parent,const Slic3r::WipeTowerParameters& p);
+ void fill_parameters(Slic3r::WipeTowerParameters& p);
+
+private:
+ wxSpinCtrl* m_widget_bridge;
+ wxCheckBox* m_widget_adhesion;
+};
+
+
+
+
+class WipeTowerDialog : public wxDialog {
+public:
+ WipeTowerDialog(wxWindow* parent,const std::string& init_data);
+
+ std::string GetValue() const { return m_output_data; }
+
+
+private:
+ std::string m_file_name="config_wipe_tower";
+ GeneralPanel* m_panel_general = nullptr;
+ RammingPanel* m_panel_ramming = nullptr;
+ CoolingPanel* m_panel_cooling = nullptr;
+ WipingPanel* m_panel_wiping = nullptr;
+ std::string m_output_data = "";
+
+ std::string read_dialog_values() {
+ Slic3r::WipeTowerParameters p;
+ m_panel_general->fill_parameters(p);
+ m_panel_ramming->fill_parameters(p);
+ m_panel_cooling->fill_parameters(p);
+ m_panel_wiping ->fill_parameters(p);
+ return p.to_string();
+ }
+};
+
+#endif // _WIPE_TOWER_DIALOG_H_ \ No newline at end of file