std::string Wipe::wipe(GCode &gcodegen, bool toolchange) { std::string gcode; /* Reduce feedrate a bit; travel speed is often too high to move on existing material. Too fast = ripping of existing material; too slow = short wipe path, thus more blob. */ double wipe_speed = gcodegen.writer.config.travel_speed.value * 0.8; // get the retraction length double length = toolchange ? gcodegen.writer.extruder()->retract_length_toolchange() : gcodegen.writer.extruder()->retract_length(); if (length > 0) { /* Calculate how long we need to travel in order to consume the required amount of retraction. In other words, how far do we move in XY at wipe_speed for the time needed to consume retract_length at retract_speed? */ double wipe_dist = scale_(length / gcodegen.writer.extruder()->retract_speed() * wipe_speed); /* Take the stored wipe path and replace first point with the current actual position (they might be different, for example, in case of loop clipping). */ Polyline wipe_path; wipe_path.append(gcodegen.last_pos()); wipe_path.append( this->path.points.begin() + 1, this->path.points.end() ); wipe_path.clip_end(wipe_path.length() - wipe_dist); // subdivide the retraction in segments double retracted = 0; Lines lines = wipe_path.lines(); for (Lines::const_iterator line = lines.begin(); line != lines.end(); ++line) { double segment_length = line->length(); /* Reduce retraction length a bit to avoid effective retraction speed to be greater than the configured one due to rounding (TODO: test and/or better math for this) */ double dE = length * (segment_length / wipe_dist) * 0.95; gcode += gcodegen.writer.set_speed(wipe_speed*60); gcode += gcodegen.writer.extrude_to_xy( gcodegen.point_to_gcode(line->b), -dE, (std::string)"wipe and retract" + (gcodegen.enable_cooling_markers ? ";_WIPE" : "") ); retracted += dE; } gcodegen.writer.extruder()->retracted += retracted; // prevent wiping again on same path this->reset_path(); } return gcode; }
std::string GCode::_extrude(ExtrusionPath path, std::string description, double speed) { path.simplify(SCALED_RESOLUTION); std::string gcode; // go to first point of extrusion path if (!this->_last_pos_defined || !this->_last_pos.coincides_with(path.first_point())) { gcode += this->travel_to( path.first_point(), path.role, "move to first " + description + " point" ); } // compensate retraction gcode += this->unretract(); // adjust acceleration { double acceleration; if (this->config.first_layer_acceleration.value > 0 && this->first_layer) { acceleration = this->config.first_layer_acceleration.value; } else if (this->config.perimeter_acceleration.value > 0 && path.is_perimeter()) { acceleration = this->config.perimeter_acceleration.value; } else if (this->config.bridge_acceleration.value > 0 && path.is_bridge()) { acceleration = this->config.bridge_acceleration.value; } else if (this->config.infill_acceleration.value > 0 && path.is_infill()) { acceleration = this->config.infill_acceleration.value; } else { acceleration = this->config.default_acceleration.value; } gcode += this->writer.set_acceleration(acceleration); } // calculate extrusion length per distance unit double e_per_mm = this->writer.extruder()->e_per_mm3 * path.mm3_per_mm; if (this->writer.extrusion_axis().empty()) e_per_mm = 0; // set speed if (speed == -1) { if (path.role == erPerimeter) { speed = this->config.get_abs_value("perimeter_speed"); } else if (path.role == erExternalPerimeter) { speed = this->config.get_abs_value("external_perimeter_speed"); } else if (path.role == erOverhangPerimeter || path.role == erBridgeInfill) { speed = this->config.get_abs_value("bridge_speed"); } else if (path.role == erInternalInfill) { speed = this->config.get_abs_value("infill_speed"); } else if (path.role == erSolidInfill) { speed = this->config.get_abs_value("solid_infill_speed"); } else if (path.role == erTopSolidInfill) { speed = this->config.get_abs_value("top_solid_infill_speed"); } else if (path.role == erGapFill) { speed = this->config.get_abs_value("gap_fill_speed"); } else { CONFESS("Invalid speed"); } } if (this->first_layer) { speed = this->config.get_abs_value("first_layer_speed", speed); } if (this->volumetric_speed != 0 && speed == 0) { speed = this->volumetric_speed / path.mm3_per_mm; } if (this->config.max_volumetric_speed.value > 0) { // cap speed with max_volumetric_speed anyway (even if user is not using autospeed) speed = std::min( speed, this->config.max_volumetric_speed.value / path.mm3_per_mm ); } double F = speed * 60; // convert mm/sec to mm/min // extrude arc or line if (path.is_bridge() && this->enable_cooling_markers) gcode += ";_BRIDGE_FAN_START\n"; if (lowerSpeed) { gcode += this->writer.set_speed(600, "", this->enable_cooling_markers ? ";_EXTRUDE_SET_SPEED" : ""); } else { gcode += this->writer.set_speed(F, "", this->enable_cooling_markers ? ";_EXTRUDE_SET_SPEED" : ""); } double path_length = 0; { std::string comment = this->config.gcode_comments ? description : ""; Lines lines = path.polyline.lines(); for (Lines::const_iterator line = lines.begin(); line != lines.end(); ++line) { const double line_length = line->length() * SCALING_FACTOR; path_length += line_length; gcode += this->writer.extrude_to_xy( this->point_to_gcode(line->b), e_per_mm * line_length, comment ); if (path_length > 10 && lowerSpeed) { lowerSpeed = false; gcode += this->writer.set_speed(F, "", this->enable_cooling_markers ? ";_EXTRUDE_SET_SPEED" : ""); } } lowerSpeed = false; } if (this->wipe.enable) { this->wipe.path = path.polyline; this->wipe.path.reverse(); } if (path.is_bridge() && this->enable_cooling_markers) gcode += ";_BRIDGE_FAN_END\n"; this->set_last_pos(path.last_point()); if (this->config.cooling) this->elapsed_time += path_length / F * 60; return gcode; }
// This method accepts &point in print coordinates. std::string GCode::travel_to(const Point &point, ExtrusionRole role, std::string comment) { /* Define the travel move as a line between current position and the taget point. This is expressed in print coordinates, so it will need to be translated by this->origin in order to get G-code coordinates. */ Polyline travel; travel.append(this->last_pos()); travel.append(point); std::string gcode; // check whether a straight travel move would need retraction bool needs_retraction = this->needs_retraction(travel, role); std::stringstream ss; ss << ";needs retr 1: " << needs_retraction << "\n"; gcode += ss.str(); // if a retraction would be needed, try to use avoid_crossing_perimeters to plan a // multi-hop travel path inside the configuration space if (needs_retraction && this->config.avoid_crossing_perimeters && !this->avoid_crossing_perimeters.disable_once) { travel = this->avoid_crossing_perimeters.travel_to(*this, point); gcode += ";HERE!!!!!\n"; // check again whether the new travel path still needs a retraction needs_retraction = this->needs_retraction(travel, role); std::stringstream ss; ss << ";needs retr 2: " << needs_retraction << "\n"; gcode += ss.str(); //if (needs_retraction && this->layer_index > 1) exit(0); } //needs_retraction = true; //vladi //if (this->first_layer) needs_retraction = false;//vladi // Re-allow avoid_crossing_perimeters for the next travel moves this->avoid_crossing_perimeters.disable_once = false; this->avoid_crossing_perimeters.use_external_mp_once = false; // generate G-code for the travel move //if (travel.lines().begin()->length() * SCALING_FACTOR > 2 && this->first_layer) needs_retraction = true; //std::size_t found = comment.find("infill"); bool willDoInfill = (comment.find("infill") != std::string::npos); //if (comment.find("infill") != std::string::npos && this->first_layer) needs_retraction = false; if (needs_retraction) gcode += this->retract(); // use G1 because we rely on paths being straight (G0 may make round paths) Lines lines = travel.lines(); double path_length = 0; for (Lines::const_iterator line = lines.begin(); line != lines.end(); ++line) { const double line_length = line->length() * SCALING_FACTOR; path_length += line_length; std::stringstream ss1; ss1 << ";Will Travel: " << line_length << ", on layer height: " << this->layer->print_z << ", id" << this->layer->id() << "\n"; gcode += ss1.str(); if (this->first_layer && line_length > 3 && !willDoInfill) { if (needs_retraction) { gcode += this->unretract(); gcode += this->writer.travel_to_z(this->layer->print_z, "extrude move on layer height"); } else { gcode += this->writer.travel_to_z(0.1, "extrude move 333"); } double fil_sq = 3.14f *1.75f*1.75f/4; double exr_line = 0.1f*0.4f; double e = exr_line *line_length/fil_sq; //double e_per_mm = this->writer.extruder()->e_per_mm3 * path.mm3_per_mm; // gcode += "G1 F6000\n"; gcode += this->writer.extrude_to_xy(this->point_to_gcode(line->b), e, comment); gcode += this->writer.travel_to_z(this->layer->print_z, "return"); lowerSpeed = true; } else { gcode += this->writer.travel_to_xy(this->point_to_gcode(line->b), comment); } } if (this->config.cooling) this->elapsed_time += path_length / this->config.get_abs_value("travel_speed"); return gcode; }
bool BridgeDetector::detect_angle() { if (this->_edges.empty() || this->_anchors.empty()) return false; /* Outset the bridge expolygon by half the amount we used for detecting anchors; we'll use this one to clip our test lines and be sure that their endpoints are inside the anchors and not on their contours leading to false negatives. */ Polygons clip_area; offset(this->expolygon, &clip_area, +this->extrusion_width/2); /* we'll now try several directions using a rudimentary visibility check: bridge in several directions and then sum the length of lines having both endpoints within anchors */ // we test angles according to configured resolution std::vector<double> angles; for (int i = 0; i <= PI/this->resolution; ++i) angles.push_back(i * this->resolution); // we also test angles of each bridge contour { Polygons pp = this->expolygon; for (Polygons::const_iterator p = pp.begin(); p != pp.end(); ++p) { Lines lines = p->lines(); for (Lines::const_iterator line = lines.begin(); line != lines.end(); ++line) angles.push_back(line->direction()); } } /* we also test angles of each open supporting edge (this finds the optimal angle for C-shaped supports) */ for (Polylines::const_iterator edge = this->_edges.begin(); edge != this->_edges.end(); ++edge) { if (edge->first_point().coincides_with(edge->last_point())) continue; angles.push_back(Line(edge->first_point(), edge->last_point()).direction()); } // remove duplicates double min_resolution = PI/180.0; // 1 degree std::sort(angles.begin(), angles.end()); for (size_t i = 1; i < angles.size(); ++i) { if (xd::Geometry::directions_parallel(angles[i], angles[i-1], min_resolution)) { angles.erase(angles.begin() + i); --i; } } /* compare first value with last one and remove the greatest one (PI) in case they are parallel (PI, 0) */ if (xd::Geometry::directions_parallel(angles.front(), angles.back(), min_resolution)) angles.pop_back(); BridgeDirectionComparator bdcomp(this->extrusion_width); double line_increment = this->extrusion_width; bool have_coverage = false; for (std::vector<double>::const_iterator angle = angles.begin(); angle != angles.end(); ++angle) { Polygons my_clip_area = clip_area; ExPolygons my_anchors = this->_anchors; // rotate everything - the center point doesn't matter for (Polygons::iterator it = my_clip_area.begin(); it != my_clip_area.end(); ++it) it->rotate(-*angle, Point(0,0)); for (ExPolygons::iterator it = my_anchors.begin(); it != my_anchors.end(); ++it) it->rotate(-*angle, Point(0,0)); // generate lines in this direction BoundingBox bb; for (ExPolygons::const_iterator it = my_anchors.begin(); it != my_anchors.end(); ++it) bb.merge((Points)*it); Lines lines; for (coord_t y = bb.min.y; y <= bb.max.y; y += line_increment) lines.push_back(Line(Point(bb.min.x, y), Point(bb.max.x, y))); Lines clipped_lines; intersection(lines, my_clip_area, &clipped_lines); // remove any line not having both endpoints within anchors for (size_t i = 0; i < clipped_lines.size(); ++i) { Line &line = clipped_lines[i]; if (!xd::Geometry::contains(my_anchors, line.a) || !xd::Geometry::contains(my_anchors, line.b)) { clipped_lines.erase(clipped_lines.begin() + i); --i; } } std::vector<double> lengths; double total_length = 0; for (Lines::const_iterator line = clipped_lines.begin(); line != clipped_lines.end(); ++line) { double len = line->length(); lengths.push_back(len); total_length += len; } if (total_length) have_coverage = true; // sum length of bridged lines bdcomp.dir_coverage[*angle] = total_length; /* The following produces more correct results in some cases and more broken in others. TODO: investigate, as it looks more reliable than line clipping. */ // $directions_coverage{$angle} = sum(map $_->area, @{$self->coverage($angle)}) // 0; // max length of bridged lines bdcomp.dir_avg_length[*angle] = !lengths.empty() ? *std::max_element(lengths.begin(), lengths.end()) : 0; } // if no direction produced coverage, then there's no bridge direction if (!have_coverage) return false; // sort directions by score std::sort(angles.begin(), angles.end(), bdcomp); this->angle = angles.front(); if (this->angle >= PI) this->angle -= PI; // #ifdef SLIC3R_DEBUG // printf(" Optimal infill angle is %d degrees\n", (int)Slic3r::Geometry::rad2deg(this->angle)); // #endif return true; }