Wipe tower postprocessing, wipe tower block on 3D plate improved.

- it renders red with one egde as indeterminate, the front edge is where the wipe tower will start
- changing width changes depth of the block (as requested)
- the block shows the brim of the wipe tower
- after slicing, the block is rendered in usual dark green and takes the exact shape of the tower (also with brim)
- moving or rotationg the block after slicing does not invalidate the wipe tower (and hence the exact block dimensions are preserved)
- changing anything that invalidates the wipe tower reverts the block back to the "indeterminate" shape
- the block is not shown after slicing, if the wipe tower is not actually generated (printing single color object with the wipe tower enabled)

This required changes in the wipe tower generator, which now generates the tower
at origin with no rotation. Resulting gcode is postprocessed and transformed during
gcode export. This means the wipe tower needs not be invalidated when it is moved or rotated.
This commit is contained in:
Lukas Matena 2018-07-27 15:56:27 +02:00
parent dd724e9dab
commit d5f042b4b8
14 changed files with 264 additions and 77 deletions

View File

@ -1281,6 +1281,11 @@ sub async_apply_config {
$self->{gcode_preview_data}->reset;
$self->{toolpaths2D}->reload_print if $self->{toolpaths2D};
$self->{preview3D}->reload_print if $self->{preview3D};
# We also need to reload 3D scene because of the wipe tower preview box
if ($self->{config}->wipe_tower) {
Slic3r::GUI::_3DScene::reload_scene($self->{canvas3D}, 1) if $self->{canvas3D}
}
}
}
@ -1494,6 +1499,9 @@ sub on_process_completed {
$self->{toolpaths2D}->reload_print if $self->{toolpaths2D};
$self->{preview3D}->reload_print if $self->{preview3D};
# in case this was MM print, wipe tower bounding box on 3D tab might need redrawing with exact depth:
Slic3r::GUI::_3DScene::reload_scene($self->{canvas3D}, 1);
# if we have an export filename, start a new thread for exporting G-code
if ($self->{export_gcode_output_file}) {
@_ = ();

View File

@ -167,6 +167,18 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T
{
std::string gcode;
// Toolchangeresult.gcode assumes the wipe tower corner is at the origin
// We want to rotate and shift all extrusions (gcode postprocessing) and starting and ending position
float alpha = m_wipe_tower_rotation/180.f * M_PI;
WipeTower::xy start_pos = tcr.start_pos;
WipeTower::xy end_pos = tcr.end_pos;
start_pos.rotate(alpha);
start_pos.translate(m_wipe_tower_pos);
end_pos.rotate(alpha);
end_pos.translate(m_wipe_tower_pos);
std::string tcr_rotated_gcode = rotate_wipe_tower_moves(tcr.gcode, tcr.start_pos, m_wipe_tower_pos, alpha);
// Disable linear advance for the wipe tower operations.
gcode += "M900 K0\n";
// Move over the wipe tower.
@ -174,14 +186,14 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T
gcode += gcodegen.retract(true);
gcodegen.m_avoid_crossing_perimeters.use_external_mp_once = true;
gcode += gcodegen.travel_to(
wipe_tower_point_to_object_point(gcodegen, tcr.start_pos),
wipe_tower_point_to_object_point(gcodegen, start_pos),
erMixed,
"Travel to a Wipe Tower");
gcode += gcodegen.unretract();
// Let the tool change be executed by the wipe tower class.
// Inform the G-code writer about the changes done behind its back.
gcode += tcr.gcode;
gcode += tcr_rotated_gcode;
// Let the m_writer know the current extruder_id, but ignore the generated G-code.
if (new_extruder_id >= 0 && gcodegen.writer().need_toolchange(new_extruder_id))
gcodegen.writer().toolchange(new_extruder_id);
@ -195,18 +207,18 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T
check_add_eol(gcode);
}
// A phony move to the end position at the wipe tower.
gcodegen.writer().travel_to_xy(Pointf(tcr.end_pos.x, tcr.end_pos.y));
gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, tcr.end_pos));
gcodegen.writer().travel_to_xy(Pointf(end_pos.x, end_pos.y));
gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, end_pos));
// Prepare a future wipe.
gcodegen.m_wipe.path.points.clear();
if (new_extruder_id >= 0) {
// Start the wipe at the current position.
gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen, tcr.end_pos));
gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen, end_pos));
// Wipe end point: Wipe direction away from the closer tower edge to the further tower edge.
gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen,
WipeTower::xy((std::abs(m_left - tcr.end_pos.x) < std::abs(m_right - tcr.end_pos.x)) ? m_right : m_left,
tcr.end_pos.y)));
WipeTower::xy((std::abs(m_left - end_pos.x) < std::abs(m_right - end_pos.x)) ? m_right : m_left,
end_pos.y)));
}
// Let the planner know we are traveling between objects.
@ -214,6 +226,57 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T
return gcode;
}
// This function postprocesses gcode_original, rotates and moves all G1 extrusions and returns resulting gcode
// Starting position has to be supplied explicitely (otherwise it would fail in case first G1 command only contained one coordinate)
std::string WipeTowerIntegration::rotate_wipe_tower_moves(const std::string& gcode_original, const WipeTower::xy& start_pos, const WipeTower::xy& translation, float angle) const
{
std::istringstream gcode_str(gcode_original);
std::string gcode_out;
std::string line;
WipeTower::xy pos = start_pos;
WipeTower::xy transformed_pos;
WipeTower::xy old_pos(-1000.1f, -1000.1f);
while (gcode_str) {
std::getline(gcode_str, line); // we read the gcode line by line
if (line.find("G1 ") == 0) {
std::ostringstream line_out;
std::istringstream line_str(line);
line_str >> std::noskipws; // don't skip whitespace
char ch = 0;
while (line_str >> ch) {
if (ch == 'X')
line_str >> pos.x;
else
if (ch == 'Y')
line_str >> pos.y;
else
line_out << ch;
}
transformed_pos = pos;
transformed_pos.rotate(angle);
transformed_pos.translate(translation);
if (transformed_pos != old_pos) {
line = line_out.str();
char buf[2048] = "G1";
if (transformed_pos.x != old_pos.x)
sprintf(buf + strlen(buf), " X%.3f", transformed_pos.x);
if (transformed_pos.y != old_pos.y)
sprintf(buf + strlen(buf), " Y%.3f", transformed_pos.y);
line.replace(line.find("G1 "), 3, buf);
old_pos = transformed_pos;
}
}
gcode_out += line + "\n";
}
return gcode_out;
}
std::string WipeTowerIntegration::prime(GCode &gcodegen)
{
assert(m_layer_idx == 0);

View File

@ -83,8 +83,10 @@ public:
const WipeTower::ToolChangeResult &priming,
const std::vector<std::vector<WipeTower::ToolChangeResult>> &tool_changes,
const WipeTower::ToolChangeResult &final_purge) :
m_left(float(print_config.wipe_tower_x.value)),
m_right(float(print_config.wipe_tower_x.value + print_config.wipe_tower_width.value)),
m_left(/*float(print_config.wipe_tower_x.value)*/ 0.f),
m_right(float(/*print_config.wipe_tower_x.value +*/ print_config.wipe_tower_width.value)),
m_wipe_tower_pos(float(print_config.wipe_tower_x.value), float(print_config.wipe_tower_y.value)),
m_wipe_tower_rotation(float(print_config.wipe_tower_rotation_angle)),
m_priming(priming),
m_tool_changes(tool_changes),
m_final_purge(final_purge),
@ -101,9 +103,14 @@ private:
WipeTowerIntegration& operator=(const WipeTowerIntegration&);
std::string append_tcr(GCode &gcodegen, const WipeTower::ToolChangeResult &tcr, int new_extruder_id) const;
// Postprocesses gcode: rotates and moves all G1 extrusions and returns result
std::string rotate_wipe_tower_moves(const std::string& gcode_original, const WipeTower::xy& start_pos, const WipeTower::xy& translation, float angle) const;
// Left / right edges of the wipe tower, for the planning of wipe moves.
const float m_left;
const float m_right;
const WipeTower::xy m_wipe_tower_pos;
const float m_wipe_tower_rotation;
// Reference to cached values at the Printer class.
const WipeTower::ToolChangeResult &m_priming;
const std::vector<std::vector<WipeTower::ToolChangeResult>> &m_tool_changes;
@ -112,6 +119,7 @@ private:
int m_layer_idx;
int m_tool_change_idx;
bool m_brim_done;
bool i_have_brim = false;
};
class GCode {

View File

@ -134,6 +134,11 @@ BoundingBoxf get_print_object_extrusions_extents(const PrintObject &print_object
// The projection does not contain the priming regions.
BoundingBoxf get_wipe_tower_extrusions_extents(const Print &print, const coordf_t max_print_z)
{
// Wipe tower extrusions are saved as if the tower was at the origin with no rotation
// We need to get position and angle of the wipe tower to transform them to actual position.
Pointf wipe_tower_pos(print.config.wipe_tower_x.value, print.config.wipe_tower_y.value);
float wipe_tower_angle = print.config.wipe_tower_rotation_angle.value;
BoundingBoxf bbox;
for (const std::vector<WipeTower::ToolChangeResult> &tool_changes : print.m_wipe_tower_tool_changes) {
if (! tool_changes.empty() && tool_changes.front().print_z > max_print_z)
@ -144,6 +149,11 @@ BoundingBoxf get_wipe_tower_extrusions_extents(const Print &print, const coordf_
if (e.width > 0) {
Pointf p1((&e - 1)->pos.x, (&e - 1)->pos.y);
Pointf p2(e.pos.x, e.pos.y);
p1.rotate(wipe_tower_angle);
p1.translate(wipe_tower_pos);
p2.rotate(wipe_tower_angle);
p2.translate(wipe_tower_pos);
bbox.merge(p1);
coordf_t radius = 0.5 * e.width;
bbox.min.x = std::min(bbox.min.x, std::min(p1.x, p2.x) - radius);

View File

@ -25,18 +25,30 @@ public:
bool operator==(const xy &rhs) const { return x == rhs.x && y == rhs.y; }
bool operator!=(const xy &rhs) const { return x != rhs.x || y != rhs.y; }
// Rotate the point around given point about given angle (in degrees)
// shifts the result so that point of rotation is in the middle of the tower
xy rotate(const xy& origin, float width, float depth, float angle) const {
// Rotate the point around center of the wipe tower about given angle (in degrees)
xy rotate(float width, float depth, float angle) const {
xy out(0,0);
float temp_x = x - width / 2.f;
float temp_y = y - depth / 2.f;
angle *= M_PI/180.;
out.x += (temp_x - origin.x) * cos(angle) - (temp_y - origin.y) * sin(angle);
out.y += (temp_x - origin.x) * sin(angle) + (temp_y - origin.y) * cos(angle);
return out + origin;
out.x += temp_x * cos(angle) - temp_y * sin(angle) + width / 2.f;
out.y += temp_x * sin(angle) + temp_y * cos(angle) + depth / 2.f;
return out;
}
// Rotate the point around origin about given angle in degrees
void rotate(float angle) {
float temp_x = x * cos(angle) - y * sin(angle);
y = x * sin(angle) + y * cos(angle);
x = temp_x;
}
void translate(const xy& vect) {
x += vect.x;
y += vect.y;
}
float x;
float y;
};
@ -104,6 +116,9 @@ public:
// This is useful not only for the print time estimation, but also for the control of layer cooling.
float elapsed_time;
// Is this a priming extrusion? (If so, the wipe tower rotation & translation will not be applied later)
bool priming;
// Sum the total length of the extrusion.
float total_extrusion_length_in_plane() {
float e_length = 0.f;

View File

@ -5,7 +5,7 @@ TODO LIST
1. cooling moves - DONE
2. account for perimeter and finish_layer extrusions and subtract it from last wipe - DONE
3. priming extrusions (last wipe must clear the color)
3. priming extrusions (last wipe must clear the color) - DONE
4. Peter's wipe tower - layer's are not exactly square
5. Peter's wipe tower - variable width for higher levels
6. Peter's wipe tower - make sure it is not too sparse (apply max_bridge_distance and make last wipe longer)
@ -17,7 +17,6 @@ TODO LIST
#include <assert.h>
#include <math.h>
#include <fstream>
#include <iostream>
#include <vector>
#include <numeric>
@ -68,8 +67,11 @@ public:
return *this;
}
Writer& set_initial_position(const WipeTower::xy &pos) {
m_start_pos = WipeTower::xy(pos,0.f,m_y_shift).rotate(m_wipe_tower_pos, m_wipe_tower_width, m_wipe_tower_depth, m_angle_deg);
Writer& set_initial_position(const WipeTower::xy &pos, float width = 0.f, float depth = 0.f, float internal_angle = 0.f) {
m_wipe_tower_width = width;
m_wipe_tower_depth = depth;
m_internal_angle = internal_angle;
m_start_pos = WipeTower::xy(pos,0.f,m_y_shift).rotate(m_wipe_tower_width, m_wipe_tower_depth, m_internal_angle);
m_current_pos = pos;
return *this;
}
@ -82,9 +84,6 @@ public:
Writer& set_extrusion_flow(float flow)
{ m_extrusion_flow = flow; return *this; }
Writer& set_rotation(WipeTower::xy& pos, float width, float depth, float angle)
{ m_wipe_tower_pos = pos; m_wipe_tower_width = width; m_wipe_tower_depth=depth; m_angle_deg = angle; return (*this); }
Writer& set_y_shift(float shift) {
m_current_pos.y -= shift-m_y_shift;
m_y_shift = shift;
@ -110,7 +109,7 @@ public:
float y() const { return m_current_pos.y; }
const WipeTower::xy& pos() const { return m_current_pos; }
const WipeTower::xy start_pos_rotated() const { return m_start_pos; }
const WipeTower::xy pos_rotated() const { return WipeTower::xy(m_current_pos,0.f,m_y_shift).rotate(m_wipe_tower_pos, m_wipe_tower_width, m_wipe_tower_depth, m_angle_deg); }
const WipeTower::xy pos_rotated() const { return WipeTower::xy(m_current_pos, 0.f, m_y_shift).rotate(m_wipe_tower_width, m_wipe_tower_depth, m_internal_angle); }
float elapsed_time() const { return m_elapsed_time; }
// Extrude with an explicitely provided amount of extrusion.
@ -125,9 +124,9 @@ public:
double len = sqrt(dx*dx+dy*dy);
// For rotated wipe tower, transform position to printer coordinates
WipeTower::xy rotated_current_pos(WipeTower::xy(m_current_pos,0.f,m_y_shift).rotate(m_wipe_tower_pos, m_wipe_tower_width, m_wipe_tower_depth, m_angle_deg)); // this is where we are
WipeTower::xy rot(WipeTower::xy(x,y+m_y_shift).rotate(m_wipe_tower_pos, m_wipe_tower_width, m_wipe_tower_depth, m_angle_deg)); // this is where we want to go
// Now do the "internal rotation" with respect to the wipe tower center
WipeTower::xy rotated_current_pos(WipeTower::xy(m_current_pos,0.f,m_y_shift).rotate(m_wipe_tower_width, m_wipe_tower_depth, m_internal_angle)); // this is where we are
WipeTower::xy rot(WipeTower::xy(x,y+m_y_shift).rotate(m_wipe_tower_width, m_wipe_tower_depth, m_internal_angle)); // this is where we want to go
if (! m_preview_suppressed && e > 0.f && len > 0.) {
// Width of a squished extrusion, corrected for the roundings of the squished extrusions.
@ -147,6 +146,7 @@ public:
if (std::abs(rot.y - rotated_current_pos.y) > EPSILON)
m_gcode += set_format_Y(rot.y);
if (e != 0.f)
m_gcode += set_format_E(e);
@ -397,9 +397,8 @@ private:
std::string m_gcode;
std::vector<WipeTower::Extrusion> m_extrusions;
float m_elapsed_time;
float m_angle_deg = 0.f;
float m_internal_angle = 0.f;
float m_y_shift = 0.f;
WipeTower::xy m_wipe_tower_pos;
float m_wipe_tower_width = 0.f;
float m_wipe_tower_depth = 0.f;
float m_last_fan_speed = 0.f;
@ -539,6 +538,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
m_print_brim = true;
ToolChangeResult result;
result.priming = true;
result.print_z = this->m_z_pos;
result.layer_height = this->m_layer_height;
result.gcode = writer.gcode();
@ -575,7 +575,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
}
box_coordinates cleaning_box(
m_wipe_tower_pos + xy(m_perimeter_width / 2.f, m_perimeter_width / 2.f),
xy(m_perimeter_width / 2.f, m_perimeter_width / 2.f),
m_wipe_tower_width - m_perimeter_width,
(tool != (unsigned int)(-1) ? /*m_layer_info->depth*/wipe_area+m_depth_traversed-0.5*m_perimeter_width
: m_wipe_tower_depth-m_perimeter_width));
@ -584,7 +584,6 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
writer.set_extrusion_flow(m_extrusion_flow)
.set_z(m_z_pos)
.set_initial_tool(m_current_tool)
.set_rotation(m_wipe_tower_pos, m_wipe_tower_width, m_wipe_tower_depth, m_wipe_tower_rotation_angle)
.set_y_shift(m_y_shift + (tool!=(unsigned int)(-1) && (m_current_shape == SHAPE_REVERSED && !m_peters_wipe_tower) ? m_layer_info->depth - m_layer_info->toolchanges_depth(): 0.f))
.append(";--------------------\n"
"; CP TOOLCHANGE START\n")
@ -594,7 +593,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
.speed_override(100);
xy initial_position = cleaning_box.ld + WipeTower::xy(0.f,m_depth_traversed);
writer.set_initial_position(initial_position);
writer.set_initial_position(initial_position, m_wipe_tower_width, m_wipe_tower_depth, m_internal_rotation);
// Increase the extruder driver current to allow fast ramming.
writer.set_extruder_trimpot(750);
@ -616,11 +615,11 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
if (last_change_in_layer) {// draw perimeter line
writer.set_y_shift(m_y_shift);
if (m_peters_wipe_tower)
writer.rectangle(m_wipe_tower_pos,m_layer_info->depth + 3*m_perimeter_width,m_wipe_tower_depth);
writer.rectangle(WipeTower::xy(0.f, 0.f),m_layer_info->depth + 3*m_perimeter_width,m_wipe_tower_depth);
else {
writer.rectangle(m_wipe_tower_pos,m_wipe_tower_width, m_layer_info->depth + m_perimeter_width);
writer.rectangle(WipeTower::xy(0.f, 0.f),m_wipe_tower_width, m_layer_info->depth + m_perimeter_width);
if (layer_finished()) { // no finish_layer will be called, we must wipe the nozzle
writer.travel(m_wipe_tower_pos.x + (writer.x()> (m_wipe_tower_pos.x + m_wipe_tower_width) / 2.f ? 0.f : m_wipe_tower_width), writer.y());
writer.travel(writer.x()> m_wipe_tower_width / 2.f ? 0.f : m_wipe_tower_width, writer.y());
}
}
}
@ -634,6 +633,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
"\n\n");
ToolChangeResult result;
result.priming = false;
result.print_z = this->m_z_pos;
result.layer_height = this->m_layer_height;
result.gcode = writer.gcode();
@ -647,7 +647,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
WipeTower::ToolChangeResult WipeTowerPrusaMM::toolchange_Brim(bool sideOnly, float y_offset)
{
const box_coordinates wipeTower_box(
m_wipe_tower_pos,
WipeTower::xy(0.f, 0.f),
m_wipe_tower_width,
m_wipe_tower_depth);
@ -655,12 +655,11 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::toolchange_Brim(bool sideOnly, flo
writer.set_extrusion_flow(m_extrusion_flow * 1.1f)
.set_z(m_z_pos) // Let the writer know the current Z position as a base for Z-hop.
.set_initial_tool(m_current_tool)
.set_rotation(m_wipe_tower_pos, m_wipe_tower_width, m_wipe_tower_depth, m_wipe_tower_rotation_angle)
.append(";-------------------------------------\n"
"; CP WIPE TOWER FIRST LAYER BRIM START\n");
xy initial_position = wipeTower_box.lu - xy(m_perimeter_width * 6.f, 0);
writer.set_initial_position(initial_position);
writer.set_initial_position(initial_position, m_wipe_tower_width, m_wipe_tower_depth, m_internal_rotation);
writer.extrude_explicit(wipeTower_box.ld - xy(m_perimeter_width * 6.f, 0), // Prime the extruder left of the wipe tower.
1.5f * m_extrusion_flow * (wipeTower_box.lu.y - wipeTower_box.ld.y), 2400);
@ -685,6 +684,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::toolchange_Brim(bool sideOnly, flo
m_print_brim = false; // Mark the brim as extruded
ToolChangeResult result;
result.priming = false;
result.print_z = this->m_z_pos;
result.layer_height = this->m_layer_height;
result.gcode = writer.gcode();
@ -724,7 +724,7 @@ void WipeTowerPrusaMM::toolchange_Unload(
if (m_layer_info > m_plan.begin() && m_layer_info < m_plan.end() && (m_layer_info-1!=m_plan.begin() || !m_adhesion )) {
// this is y of the center of previous sparse infill border
float sparse_beginning_y = m_wipe_tower_pos.y;
float sparse_beginning_y = 0.f;
if (m_current_shape == SHAPE_REVERSED)
sparse_beginning_y += ((m_layer_info-1)->depth - (m_layer_info-1)->toolchanges_depth())
- ((m_layer_info)->depth-(m_layer_info)->toolchanges_depth()) ;
@ -742,7 +742,7 @@ void WipeTowerPrusaMM::toolchange_Unload(
for (const auto& tch : m_layer_info->tool_changes) { // let's find this toolchange
if (tch.old_tool == m_current_tool) {
sum_of_depths += tch.ramming_depth;
float ramming_end_y = m_wipe_tower_pos.y + sum_of_depths;
float ramming_end_y = sum_of_depths;
ramming_end_y -= (y_step/m_extra_spacing-m_perimeter_width) / 2.f; // center of final ramming line
// debugging:
@ -950,7 +950,7 @@ void WipeTowerPrusaMM::toolchange_Wipe(
if (m_layer_info != m_plan.end() && m_current_tool != m_layer_info->tool_changes.back().new_tool) {
m_left_to_right = !m_left_to_right;
writer.travel(writer.x(), writer.y() - dy)
.travel(m_wipe_tower_pos.x + (m_left_to_right ? m_wipe_tower_width : 0.f), writer.y());
.travel(m_left_to_right ? m_wipe_tower_width : 0.f, writer.y());
}
writer.set_extrusion_flow(m_extrusion_flow); // Reset the extrusion flow.
@ -969,7 +969,6 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer()
writer.set_extrusion_flow(m_extrusion_flow)
.set_z(m_z_pos)
.set_initial_tool(m_current_tool)
.set_rotation(m_wipe_tower_pos, m_wipe_tower_width, m_wipe_tower_depth, m_wipe_tower_rotation_angle)
.set_y_shift(m_y_shift - (m_current_shape == SHAPE_REVERSED && !m_peters_wipe_tower ? m_layer_info->toolchanges_depth() : 0.f))
.append(";--------------------\n"
"; CP EMPTY GRID START\n")
@ -978,14 +977,12 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer()
// Slow down on the 1st layer.
float speed_factor = m_is_first_layer ? 0.5f : 1.f;
float current_depth = m_layer_info->depth - m_layer_info->toolchanges_depth();
box_coordinates fill_box(m_wipe_tower_pos + xy(m_perimeter_width, m_depth_traversed + m_perimeter_width),
box_coordinates fill_box(xy(m_perimeter_width, m_depth_traversed + m_perimeter_width),
m_wipe_tower_width - 2 * m_perimeter_width, current_depth-m_perimeter_width);
if (m_left_to_right) // so there is never a diagonal travel
writer.set_initial_position(fill_box.ru);
else
writer.set_initial_position(fill_box.lu);
writer.set_initial_position((m_left_to_right ? fill_box.ru : fill_box.lu), // so there is never a diagonal travel
m_wipe_tower_width, m_wipe_tower_depth, m_internal_rotation);
box_coordinates box = fill_box;
for (int i=0;i<2;++i) {
@ -1044,6 +1041,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer()
m_depth_traversed = m_wipe_tower_depth-m_perimeter_width;
ToolChangeResult result;
result.priming = false;
result.print_z = this->m_z_pos;
result.layer_height = this->m_layer_height;
result.gcode = writer.gcode();
@ -1165,9 +1163,9 @@ void WipeTowerPrusaMM::generate(std::vector<std::vector<WipeTower::ToolChangeRes
{
set_layer(layer.z,layer.height,0,layer.z == m_plan.front().z,layer.z == m_plan.back().z);
if (m_peters_wipe_tower)
m_wipe_tower_rotation_angle += 90.f;
m_internal_rotation += 90.f;
else
m_wipe_tower_rotation_angle += 180.f;
m_internal_rotation += 180.f;
if (!m_peters_wipe_tower && m_layer_info->depth < m_wipe_tower_depth - m_perimeter_width)
m_y_shift = (m_wipe_tower_depth-m_layer_info->depth-m_perimeter_width)/2.f;
@ -1188,7 +1186,7 @@ void WipeTowerPrusaMM::generate(std::vector<std::vector<WipeTower::ToolChangeRes
last_toolchange.gcode += buf;
}
last_toolchange.gcode += finish_layer_toolchange.gcode;
last_toolchange.extrusions.insert(last_toolchange.extrusions.end(),finish_layer_toolchange.extrusions.begin(),finish_layer_toolchange.extrusions.end());
last_toolchange.extrusions.insert(last_toolchange.extrusions.end(), finish_layer_toolchange.extrusions.begin(), finish_layer_toolchange.extrusions.end());
last_toolchange.end_pos = finish_layer_toolchange.end_pos;
}
else

View File

@ -102,6 +102,8 @@ public:
// Iterates through prepared m_plan, generates ToolChangeResults and appends them to "result"
void generate(std::vector<std::vector<WipeTower::ToolChangeResult>> &result);
float get_depth() const { return m_wipe_tower_depth; }
// Switch to a next layer.
@ -189,6 +191,7 @@ private:
float m_wipe_tower_width; // Width of the wipe tower.
float m_wipe_tower_depth = 0.f; // Depth of the wipe tower
float m_wipe_tower_rotation_angle = 0.f; // Wipe tower rotation angle in degrees (with respect to x axis)
float m_internal_rotation = 0.f;
float m_y_shift = 0.f; // y shift passed to writer
float m_z_pos = 0.f; // Current Z position.
float m_layer_height = 0.f; // Current layer height.

View File

@ -166,7 +166,10 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
"use_relative_e_distances",
"use_volumetric_e",
"variable_layer_height",
"wipe"
"wipe",
"wipe_tower_x",
"wipe_tower_y",
"wipe_tower_rotation_angle"
};
std::vector<PrintStep> steps;
@ -175,7 +178,12 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
// Always invalidate the wipe tower. This is probably necessary because of the wipe_into_infill / wipe_into_objects
// features - nearly anything can influence what should (and could) be wiped into.
steps.emplace_back(psWipeTower);
// Only these three parameters don't invalidate the wipe tower (they only affect the gcode export):
for (const t_config_option_key &opt_key : opt_keys)
if (opt_key != "wipe_tower_x" && opt_key != "wipe_tower_y" && opt_key != "wipe_tower_rotation_angle") {
steps.emplace_back(psWipeTower);
break;
}
for (const t_config_option_key &opt_key : opt_keys) {
if (steps_ignore.find(opt_key) != steps_ignore.end()) {
@ -212,10 +220,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_x"
|| opt_key == "wipe_tower_y"
|| opt_key == "wipe_tower_width"
|| opt_key == "wipe_tower_rotation_angle"
|| opt_key == "wipe_tower_bridging"
|| opt_key == "wiping_volumes_matrix"
|| opt_key == "parking_pos_retraction"
@ -1051,6 +1056,8 @@ void Print::_make_wipe_tower()
if (! this->has_wipe_tower())
return;
m_wipe_tower_depth = 0.f;
// Get wiping matrix to get number of extruders and convert vector<double> to vector<float>:
std::vector<float> wiping_matrix((this->config.wiping_volumes_matrix.values).begin(),(this->config.wiping_volumes_matrix.values).end());
// Extract purging volumes for each extruder pair:
@ -1162,6 +1169,7 @@ void Print::_make_wipe_tower()
// Generate the wipe tower layers.
m_wipe_tower_tool_changes.reserve(m_tool_ordering.layer_tools().size());
wipe_tower.generate(m_wipe_tower_tool_changes);
m_wipe_tower_depth = wipe_tower.get_depth();
// Unload the current filament over the purge tower.
coordf_t layer_height = this->objects.front()->config.layer_height.value;

View File

@ -273,6 +273,7 @@ public:
void add_model_object(ModelObject* model_object, int idx = -1);
bool apply_config(DynamicPrintConfig config);
float get_wipe_tower_depth() const { return m_wipe_tower_depth; }
bool has_infinite_skirt() const;
bool has_skirt() const;
// Returns an empty string if valid, otherwise returns an error message.
@ -326,6 +327,9 @@ private:
bool invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys);
PrintRegionConfig _region_config_from_model_volume(const ModelVolume &volume);
// Depth of the wipe tower to pass to GLCanvas3D for exact bounding box:
float m_wipe_tower_depth = 0.f;
// Has the calculation been canceled?
tbb::atomic<bool> m_canceled;
};

View File

@ -75,6 +75,7 @@ bool PrintObject::delete_last_copy()
bool PrintObject::set_copies(const Points &points)
{
bool copies_num_changed = this->_copies.size() != points.size();
this->_copies = points;
// order copies with a nearest neighbor search and translate them by _copies_shift
@ -93,7 +94,8 @@ bool PrintObject::set_copies(const Points &points)
bool invalidated = this->_print->invalidate_step(psSkirt);
invalidated |= this->_print->invalidate_step(psBrim);
invalidated |= this->_print->invalidate_step(psWipeTower);
if (copies_num_changed)
invalidated |= this->_print->invalidate_step(psWipeTower);
return invalidated;
}

View File

@ -643,20 +643,62 @@ std::vector<int> GLVolumeCollection::load_object(
return volumes_idx;
}
int GLVolumeCollection::load_wipe_tower_preview(
int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool use_VBOs)
{
float color[4] = { 0.5f, 0.5f, 0.0f, 0.5f };
this->volumes.emplace_back(new GLVolume(color));
GLVolume &v = *this->volumes.back();
int GLVolumeCollection::load_wipe_tower_preview(
int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool use_VBOs, bool size_unknown, float brim_width)
{
if (depth < 0.01f)
return int(this->volumes.size() - 1);
if (height == 0.0f)
height = 0.1f;
auto mesh = make_cube(width, depth, height);
mesh.translate(-width / 2.f, -depth / 2.f, 0.f);
Point origin_of_rotation(0.f, 0.f);
mesh.rotate(rotation_angle,&origin_of_rotation);
TriangleMesh mesh;
float color[4] = { 0.5f, 0.5f, 0.0f, 1.f };
// In case we don't know precise dimensions of the wipe tower yet, we'll draw the box with different color with one side jagged:
if (size_unknown) {
color[0] = 1.f;
color[1] = 0.f;
depth = std::max(depth, 10.f); // Too narrow tower would interfere with the teeth. The estimate is not precise anyway.
float min_width = 30.f;
// We'll now create the box with jagged edge. y-coordinates of the pre-generated model are shifted so that the front
// edge has y=0 and centerline of the back edge has y=depth:
Pointf3s points;
std::vector<Point3> facets;
float out_points_idx[][3] = {{0, -depth, 0}, {0, 0, 0}, {38.453, 0, 0}, {61.547, 0, 0}, {100, 0, 0}, {100, -depth, 0}, {55.7735, -10, 0}, {44.2265, 10, 0},
{38.453, 0, 1}, {0, 0, 1}, {0, -depth, 1}, {100, -depth, 1}, {100, 0, 1}, {61.547, 0, 1}, {55.7735, -10, 1}, {44.2265, 10, 1}};
int out_facets_idx[][3] = {{0, 1, 2}, {3, 4, 5}, {6, 5, 0}, {3, 5, 6}, {6, 2, 7}, {6, 0, 2}, {8, 9, 10}, {11, 12, 13}, {10, 11, 14}, {14, 11, 13}, {15, 8, 14},
{8, 10, 14}, {3, 12, 4}, {3, 13, 12}, {6, 13, 3}, {6, 14, 13}, {7, 14, 6}, {7, 15, 14}, {2, 15, 7}, {2, 8, 15}, {1, 8, 2}, {1, 9, 8},
{0, 9, 1}, {0, 10, 9}, {5, 10, 0}, {5, 11, 10}, {4, 11, 5}, {4, 12, 11}};
for (int i=0;i<16;++i)
points.push_back(Pointf3(out_points_idx[i][0] / (100.f/min_width), out_points_idx[i][1] + depth, out_points_idx[i][2]));
for (int i=0;i<28;++i)
facets.push_back(Point3(out_facets_idx[i][0], out_facets_idx[i][1], out_facets_idx[i][2]));
TriangleMesh tooth_mesh(points, facets);
// We have the mesh ready. It has one tooth and width of min_width. We will now append several of these together until we are close to
// the required width of the block. Than we can scale it precisely.
size_t n = std::max(1, int(width/min_width)); // How many shall be merged?
for (size_t i=0;i<n;++i) {
mesh.merge(tooth_mesh);
tooth_mesh.translate(min_width, 0.f, 0.f);
}
mesh.scale(Pointf3(width/(n*min_width), 1.f, height)); // Scaling to proper width
}
else
mesh = make_cube(width, depth, height);
// We'll make another mesh to show the brim (fixed layer height):
TriangleMesh brim_mesh = make_cube(width+2.f*brim_width, depth+2.f*brim_width, 0.2f);
brim_mesh.translate(-brim_width, -brim_width, 0.f);
mesh.merge(brim_mesh);
mesh.rotate(rotation_angle, &origin_of_rotation); // rotates the box according to the config rotation setting
this->volumes.emplace_back(new GLVolume(color));
GLVolume &v = *this->volumes.back();
if (use_VBOs)
v.indexed_vertex_array.load_mesh_full_shading(mesh);

View File

@ -399,7 +399,7 @@ public:
bool use_VBOs);
int load_wipe_tower_preview(
int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool use_VBOs);
int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool use_VBOs, bool size_unknown, float brim_width);
// Render the volumes by OpenGL.
void render_VBOs() const;

View File

@ -2292,7 +2292,12 @@ void GLCanvas3D::reload_scene(bool force)
float w = dynamic_cast<const ConfigOptionFloat*>(m_config->option("wipe_tower_width"))->value;
float a = dynamic_cast<const ConfigOptionFloat*>(m_config->option("wipe_tower_rotation_angle"))->value;
m_volumes.load_wipe_tower_preview(1000, x, y, w, 15.0f * (float)(extruders_count - 1), (float)height, a, m_use_VBOs && m_initialized);
float depth = m_print->get_wipe_tower_depth();
if (!m_print->state.is_done(psWipeTower))
depth = (900.f/w) * (float)(extruders_count - 1) ;
m_volumes.load_wipe_tower_preview(1000, x, y, w, depth, (float)height, a, m_use_VBOs && m_initialized, !m_print->state.is_done(psWipeTower),
m_print->config.nozzle_diameter.values[0] * 1.25f * 4.5f);
}
}
@ -2554,6 +2559,8 @@ void GLCanvas3D::load_wipe_tower_toolpaths(const std::vector<std::string>& str_t
{
const Print *print;
const std::vector<float> *tool_colors;
WipeTower::xy wipe_tower_pos;
float wipe_tower_angle;
// Number of vertices (each vertex is 6x4=24 bytes long)
static const size_t alloc_size_max() { return 131072; } // 3.15MB
@ -2586,6 +2593,9 @@ void GLCanvas3D::load_wipe_tower_toolpaths(const std::vector<std::string>& str_t
if (m_print->m_wipe_tower_final_purge)
ctxt.final.emplace_back(*m_print->m_wipe_tower_final_purge.get());
ctxt.wipe_tower_angle = ctxt.print->config.wipe_tower_rotation_angle.value/180.f * M_PI;
ctxt.wipe_tower_pos = WipeTower::xy(ctxt.print->config.wipe_tower_x.value, ctxt.print->config.wipe_tower_y.value);
BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - start";
//FIXME Improve the heuristics for a grain size.
@ -2644,12 +2654,25 @@ void GLCanvas3D::load_wipe_tower_toolpaths(const std::vector<std::string>& str_t
lines.reserve(n_lines);
widths.reserve(n_lines);
heights.assign(n_lines, extrusions.layer_height);
WipeTower::Extrusion e_prev = extrusions.extrusions[i-1];
if (!extrusions.priming) { // wipe tower extrusions describe the wipe tower at the origin with no rotation
e_prev.pos.rotate(ctxt.wipe_tower_angle);
e_prev.pos.translate(ctxt.wipe_tower_pos);
}
for (; i < j; ++i) {
const WipeTower::Extrusion &e = extrusions.extrusions[i];
WipeTower::Extrusion e = extrusions.extrusions[i];
assert(e.width > 0.f);
const WipeTower::Extrusion &e_prev = *(&e - 1);
if (!extrusions.priming) {
e.pos.rotate(ctxt.wipe_tower_angle);
e.pos.translate(ctxt.wipe_tower_pos);
}
lines.emplace_back(Point::new_scale(e_prev.pos.x, e_prev.pos.y), Point::new_scale(e.pos.x, e.pos.y));
widths.emplace_back(e.width);
e_prev = e;
}
_3DScene::thick_lines_to_verts(lines, widths, heights, lines.front().a == lines.back().b, extrusions.print_z,
*vols[ctxt.volume_idx(e.tool, 0)]);
@ -3652,7 +3675,7 @@ void GLCanvas3D::_camera_tranform() const
::glMatrixMode(GL_MODELVIEW);
::glLoadIdentity();
::glRotatef(-m_camera.get_theta(), 1.0f, 0.0f, 0.0f); // pitch
::glRotatef(-m_camera.get_theta(), 1.0f, 0.0f, 0.0f); // pitch
::glRotatef(m_camera.phi, 0.0f, 0.0f, 1.0f); // yaw
Pointf3 neg_target = m_camera.target.negative();
@ -4627,8 +4650,11 @@ void GLCanvas3D::_load_shells()
const PrintConfig& config = m_print->config;
unsigned int extruders_count = config.nozzle_diameter.size();
if ((extruders_count > 1) && config.single_extruder_multi_material && config.wipe_tower && !config.complete_objects) {
const float width_per_extruder = 15.0f; // a simple workaround after wipe_tower_per_color_wipe got obsolete
m_volumes.load_wipe_tower_preview(1000, config.wipe_tower_x, config.wipe_tower_y, config.wipe_tower_width, width_per_extruder * (extruders_count - 1), max_z, config.wipe_tower_rotation_angle, m_use_VBOs && m_initialized);
float depth = m_print->get_wipe_tower_depth();
if (!m_print->state.is_done(psWipeTower))
depth = (900.f/config.wipe_tower_width) * (float)(extruders_count - 1) ;
m_volumes.load_wipe_tower_preview(1000, config.wipe_tower_x, config.wipe_tower_y, config.wipe_tower_width, depth, max_z, config.wipe_tower_rotation_angle,
m_use_VBOs && m_initialized, !m_print->state.is_done(psWipeTower), m_print->config.nozzle_diameter.values[0] * 1.25f * 4.5f);
}
}
@ -4696,7 +4722,7 @@ void GLCanvas3D::_on_move(const std::vector<int>& volume_idxs)
if (m_model == nullptr)
return;
std::set<std::string> done; // prevent moving instances twice
std::set<std::string> done; // prevent moving instances twice
bool object_moved = false;
Pointf3 wipe_tower_origin(0.0, 0.0, 0.0);
for (int volume_idx : volume_idxs)
@ -4705,7 +4731,7 @@ void GLCanvas3D::_on_move(const std::vector<int>& volume_idxs)
int obj_idx = volume->object_idx();
int instance_idx = volume->instance_idx();
// prevent moving instances twice
// prevent moving instances twice
char done_id[64];
::sprintf(done_id, "%d_%d", obj_idx, instance_idx);
if (done.find(done_id) != done.end())

View File

@ -89,7 +89,7 @@
std::vector<int> load_object(ModelObject *object, int obj_idx, std::vector<int> instance_idxs, std::string color_by, std::string select_by, std::string drag_by, bool use_VBOs);
int load_wipe_tower_preview(int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool use_VBOs);
int load_wipe_tower_preview(int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool use_VBOs, bool size_unknown, float brim_width);
void erase()
%code{% THIS->clear(); %};