float WallOverlapComputation::getFlow(Point& from, Point& to) { Point2Link::iterator from_link_pair = point_to_link.find(from); if (from_link_pair == point_to_link.end()) { return 1; } Point2Link::iterator to_link_pair = point_to_link.find(to); if (to_link_pair == point_to_link.end()) { return 1; } WallOverlapPointLinks::iterator from_link = from_link_pair->second; WallOverlapPointLinks::iterator to_link = to_link_pair->second; if (!from_link->second || !to_link->second) { // overlap_point_links.emplace_hint(from_link, from_link->first, true); // overlap_point_links.emplace_hint(to_link, to_link->first, true); // from_link->second = true; to_link->second = true; return 1; } // from_link->second = true; to_link->second = true; // overlap_point_links.emplace_hint(from_link, from_link->first, true); // overlap_point_links.emplace_hint(to_link, to_link->first, true); // both points have already been passed float avg_link_dist = 0.5 * ( INT2MM(from_link->first.dist) + INT2MM(to_link->first.dist) ); float ratio = avg_link_dist / INT2MM(lineWidth); if (ratio > 1.0) { return 1.0; } return ratio; }
std::string GCodeExport::getFileHeader(const double* print_time, const std::vector<double>& filament_used, const std::vector<std::string>& mat_ids) { std::ostringstream prefix; switch (flavor) { case EGCodeFlavor::GRIFFIN: prefix << ";START_OF_HEADER" << new_line; prefix << ";HEADER_VERSION:0.1" << new_line; prefix << ";FLAVOR:" << toString(flavor) << new_line; prefix << ";GENERATOR.NAME:Cura_SteamEngine" << new_line; prefix << ";GENERATOR.VERSION:" << VERSION << new_line; prefix << ";GENERATOR.BUILD_DATE:" << Date::getDate().toStringDashed() << new_line; prefix << ";TARGET_MACHINE.NAME:" << machine_name << new_line; for (unsigned int extr_nr = 0; extr_nr < extruder_count; extr_nr++) { if (!extruder_attr[extr_nr].is_used) { continue; } prefix << ";EXTRUDER_TRAIN." << extr_nr << ".INITIAL_TEMPERATURE:" << extruder_attr[extr_nr].initial_temp << new_line; if (filament_used.size() == extruder_count) { prefix << ";EXTRUDER_TRAIN." << extr_nr << ".MATERIAL.VOLUME_USED:" << static_cast<int>(filament_used[extr_nr]) << new_line; } if (mat_ids.size() == extruder_count && mat_ids[extr_nr] != "") { prefix << ";EXTRUDER_TRAIN." << extr_nr << ".MATERIAL.GUID:" << mat_ids[extr_nr] << new_line; } prefix << ";EXTRUDER_TRAIN." << extr_nr << ".NOZZLE.DIAMETER:" << float(INT2MM(getNozzleSize(extr_nr))) << new_line; } prefix << ";BUILD_PLATE.INITIAL_TEMPERATURE:" << initial_bed_temp << new_line; if (print_time) { prefix << ";PRINT.TIME:" << static_cast<int>(*print_time) << new_line; } prefix << ";PRINT.SIZE.MIN.X:0" << new_line; prefix << ";PRINT.SIZE.MIN.Y:0" << new_line; prefix << ";PRINT.SIZE.MIN.Z:0" << new_line; prefix << ";PRINT.SIZE.MAX.X:" << INT2MM(machine_dimensions.x) << new_line; prefix << ";PRINT.SIZE.MAX.Y:" << INT2MM(machine_dimensions.y) << new_line; prefix << ";PRINT.SIZE.MAX.Z:" << INT2MM(machine_dimensions.z) << new_line; prefix << ";END_OF_HEADER" << new_line; return prefix.str(); default: prefix << ";FLAVOR:" << toString(flavor) << new_line; prefix << ";TIME:" << ((print_time)? static_cast<int>(*print_time) : 6666) << new_line; if (flavor == EGCodeFlavor::ULTIGCODE) { prefix << ";MATERIAL:" << ((filament_used.size() >= 1)? static_cast<int>(filament_used[0]) : 6666) << new_line; prefix << ";MATERIAL2:" << ((filament_used.size() >= 2)? static_cast<int>(filament_used[1]) : 0) << new_line; prefix << ";NOZZLE_DIAMETER:" << float(INT2MM(getNozzleSize(0))) << new_line; // TODO: the second nozzle size isn't always initiated! ";NOZZLE_DIAMETER2:" } return prefix.str(); } }
void PrimeTower::addPurgeMove(LayerPlan& gcode_layer, int extruder_nr, const ExtruderTrain *train, const Point& start_pos, const Point& end_pos, double purge_volume) const { // Find out how much purging needs to be done. const GCodePathConfig& current_gcode_path_config = gcode_layer.configs_storage.prime_tower_config_per_extruder[extruder_nr]; const coord_t purge_move_length = vSize(start_pos - end_pos); const unsigned int line_width = current_gcode_path_config.getLineWidth(); const double layer_height_mm = (gcode_layer.getLayerNr() == 0) ? train->getSettingInMillimeters("layer_height_0") : train->getSettingInMillimeters("layer_height"); const double normal_volume = INT2MM(INT2MM(purge_move_length * line_width)) * layer_height_mm; // Volume extruded on the "normal" move float purge_flow = purge_volume / normal_volume; const double purge_move_length_mm = INT2MM(purge_move_length); const double purge_move_time = purge_move_length_mm / current_gcode_path_config.getSpeed(); const double purge_extrusion_speed_mm3_per_sec = purge_volume / purge_move_time; const double max_possible_extursion_speed_mm3_per_sec = 3.0; const double speed = current_gcode_path_config.getSpeed(); double speed_factor = 1.0; if (purge_extrusion_speed_mm3_per_sec > max_possible_extursion_speed_mm3_per_sec) { // compensate the travel speed for the large extrusion amount const double min_time_needed_for_extrusion = purge_volume / max_possible_extursion_speed_mm3_per_sec; const double compensated_speed = purge_move_length_mm / min_time_needed_for_extrusion; speed_factor = compensated_speed / speed; } // As we need a plan, which can't have a stationary extrusion, we use an extrusion move to prime. // This has the added benefit that it will evenly spread the primed material inside the tower. gcode_layer.addExtrusionMove(end_pos, current_gcode_path_config, SpaceFillType::None, purge_flow, false, speed_factor); }
void GCodeExport::setExtrusion(int layerThickness, int filamentDiameter, int flow) { double filamentArea = M_PI * (INT2MM(filamentDiameter) / 2.0) * (INT2MM(filamentDiameter) / 2.0); if (flavor == GCODE_FLAVOR_ULTIGCODE)//UltiGCode uses volume extrusion as E value, and thus does not need the filamentArea in the mix. extrusionPerMM = INT2MM(layerThickness); else extrusionPerMM = INT2MM(layerThickness) / filamentArea * double(flow) / 100.0; }
/*! * \brief Implements the functionality of adding a single 2D line segment to * the path data. * * All member functions adding a 2D line segment should use this functions. * \param print_feature_type The type of feature that the polygon is part of * (infill, wall, etc). * \param point The destination point of the line segment. * \param width The width of the lines of the polygon. * \param thickness The layer thickness of the polygon. * \param velocity How fast the polygon is printed. */ void addLineSegment(const PrintFeatureType& print_feature_type, const Point& point, const coord_t& width, const coord_t& thickness, const Velocity& velocity) { addPoint2D(point); line_types.push_back(print_feature_type); line_widths.push_back(INT2MM(width)); line_thicknesses.push_back(INT2MM(thickness)); line_velocities.push_back(velocity); }
void GCodeExport::setRetractionSettings(int extruderSwitchRetraction, int extruderSwitchRetractionSpeed, int extruderSwitchPrimeSpeed, int retraction_extrusion_window, int retraction_count_max) { this->extruderSwitchRetraction = INT2MM(extruderSwitchRetraction); this->extruderSwitchRetractionSpeed = extruderSwitchRetractionSpeed; this->extruderSwitchPrimeSpeed = extruderSwitchPrimeSpeed; this->retraction_extrusion_window = INT2MM(retraction_extrusion_window); this->retraction_count_max = INT2MM(retraction_count_max); }
void MergeInfillLines::writeCompensatedMove(Point& to, double speed, GCodePath& last_path, int64_t new_line_width) { double old_line_width = INT2MM(last_path.config->getLineWidth()); double new_line_width_mm = INT2MM(new_line_width); double speed_mod = old_line_width / new_line_width_mm; double extrusion_mod = new_line_width_mm / old_line_width; gcode.writeMove(to, speed * speed_mod, last_path.getExtrusionMM3perMM() * extrusion_mod); }
void GCodeExport::setRetractionSettings(int retractionAmount, int retractionSpeed, int extruderSwitchRetraction, int minimalExtrusionBeforeRetraction, int zHop) { this->retractionAmount = INT2MM(retractionAmount); this->retractionSpeed = retractionSpeed; this->extruderSwitchRetraction = INT2MM(extruderSwitchRetraction); this->minimalExtrusionBeforeRetraction = INT2MM(minimalExtrusionBeforeRetraction); this->retractionZHop = zHop; }
void MergeInfillLines::writeCompensatedMove(Point& to, double speed, GCodePath& last_path, int64_t new_line_width) { double old_line_width = INT2MM(last_path.config->getLineWidth()); double new_line_width_mm = INT2MM(new_line_width); double speed_mod = old_line_width / new_line_width_mm; double extrusion_mod = new_line_width_mm / old_line_width; double new_speed = std::min(speed * speed_mod, 150.0); // TODO: hardcoded value: max extrusion speed is 150 mm/s = 9000 mm/min gcode.writeMove(to, new_speed, last_path.getExtrusionMM3perMM() * extrusion_mod); }
void GCodeExport::writeMoveBFB(int x, int y, int z, double speed, double extrusion_mm3_per_mm) { double extrusion_per_mm = extrusion_mm3_per_mm; if (!is_volumatric) { extrusion_per_mm = extrusion_mm3_per_mm / extruder_attr[current_extruder].filament_area; } Point gcode_pos = getGcodePos(x,y, current_extruder); //For Bits From Bytes machines, we need to handle this completely differently. As they do not use E values but RPM values. float fspeed = speed * 60; float rpm = extrusion_per_mm * speed * 60; const float mm_per_rpm = 4.0; //All BFB machines have 4mm per RPM extrusion. rpm /= mm_per_rpm; if (rpm > 0) { if (extruder_attr[current_extruder].retraction_e_amount_current) { if (currentSpeed != double(rpm)) { //fprintf(f, "; %f e-per-mm %d mm-width %d mm/s\n", extrusion_per_mm, lineWidth, speed); //fprintf(f, "M108 S%0.1f\r\n", rpm); *output_stream << "M108 S" << std::setprecision(1) << rpm << "\r\n"; currentSpeed = double(rpm); } //Add M101 or M201 to enable the proper extruder. *output_stream << "M" << int((current_extruder + 1) * 100 + 1) << "\r\n"; extruder_attr[current_extruder].retraction_e_amount_current = 0.0; } //Fix the speed by the actual RPM we are asking, because of rounding errors we cannot get all RPM values, but we have a lot more resolution in the feedrate value. // (Trick copied from KISSlicer, thanks Jonathan) fspeed *= (rpm / (roundf(rpm * 100) / 100)); //Increase the extrusion amount to calculate the amount of filament used. Point3 diff = Point3(x,y,z) - getPosition(); current_e_value += extrusion_per_mm * diff.vSizeMM(); } else { //If we are not extruding, check if we still need to disable the extruder. This causes a retraction due to auto-retraction. if (!extruder_attr[current_extruder].retraction_e_amount_current) { *output_stream << "M103\r\n"; extruder_attr[current_extruder].retraction_e_amount_current = 1.0; // 1.0 used as stub; BFB doesn't use the actual retraction amount; it performs retraction on the firmware automatically } } *output_stream << std::setprecision(3) << "G1 X" << INT2MM(gcode_pos.X) << " Y" << INT2MM(gcode_pos.Y) << " Z" << INT2MM(z) << std::setprecision(1) << " F" << fspeed << "\r\n"; currentPosition = Point3(x, y, z); estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), current_e_value), speed); }
void OptimizedModel::saveDebugSTL(const char* filename) { char buffer[80] = "Cura_Engine_STL_export"; uint32_t n; uint16_t s; float flt; OptimizedVolume* vol = &volumes[0]; FILE* f = fopen(filename, "wb"); fwrite(buffer, 80, 1, f); n = vol->faces.size(); fwrite(&n, sizeof(n), 1, f); for(unsigned int i=0;i<vol->faces.size();i++) { flt = 0; s = 0; fwrite(&flt, sizeof(flt), 1, f); fwrite(&flt, sizeof(flt), 1, f); fwrite(&flt, sizeof(flt), 1, f); flt = INT2MM(vol->points[vol->faces[i].index[0]].p.x); fwrite(&flt, sizeof(flt), 1, f); flt = INT2MM(vol->points[vol->faces[i].index[0]].p.y); fwrite(&flt, sizeof(flt), 1, f); flt = INT2MM(vol->points[vol->faces[i].index[0]].p.z); fwrite(&flt, sizeof(flt), 1, f); flt = INT2MM(vol->points[vol->faces[i].index[1]].p.x); fwrite(&flt, sizeof(flt), 1, f); flt = INT2MM(vol->points[vol->faces[i].index[1]].p.y); fwrite(&flt, sizeof(flt), 1, f); flt = INT2MM(vol->points[vol->faces[i].index[1]].p.z); fwrite(&flt, sizeof(flt), 1, f); flt = INT2MM(vol->points[vol->faces[i].index[2]].p.x); fwrite(&flt, sizeof(flt), 1, f); flt = INT2MM(vol->points[vol->faces[i].index[2]].p.y); fwrite(&flt, sizeof(flt), 1, f); flt = INT2MM(vol->points[vol->faces[i].index[2]].p.z); fwrite(&flt, sizeof(flt), 1, f); fwrite(&s, sizeof(s), 1, f); } fclose(f); }
void CommandSocket::handleObjectList(cura::proto::ObjectList* list) { FMatrix3x3 matrix; //d->object_count = 0; //d->object_ids.clear(); d->objects_to_slice.push_back(std::make_shared<MeshGroup>(FffProcessor::getInstance())); MeshGroup* object_to_slice = d->objects_to_slice.back().get(); for(auto object : list->objects()) { DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR("solid Cura_out\n"); object_to_slice->meshes.push_back(object_to_slice); //Construct a new mesh (with object_to_slice as settings parent object) and put it into MeshGroup's mesh list. Mesh& mesh = object_to_slice->meshes.back(); int bytes_per_face = BYTES_PER_FLOAT * FLOATS_PER_VECTOR * VECTORS_PER_FACE; int face_count = object.vertices().size() / bytes_per_face; for(int i = 0; i < face_count; ++i) { //TODO: Apply matrix std::string data = object.vertices().substr(i * bytes_per_face, bytes_per_face); const FPoint3* float_vertices = reinterpret_cast<const FPoint3*>(data.data()); Point3 verts[3]; verts[0] = matrix.apply(float_vertices[0]); verts[1] = matrix.apply(float_vertices[1]); verts[2] = matrix.apply(float_vertices[2]); mesh.addFace(verts[0], verts[1], verts[2]); DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR(" facet normal -1 0 0\n"); DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR(" outer loop\n"); DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR(" vertex "<<INT2MM(verts[0].x) <<" " << INT2MM(verts[0].y) <<" " << INT2MM(verts[0].z) << "\n"); DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR(" vertex "<<INT2MM(verts[1].x) <<" " << INT2MM(verts[1].y) <<" " << INT2MM(verts[1].z) << "\n"); DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR(" vertex "<<INT2MM(verts[2].x) <<" " << INT2MM(verts[2].y) <<" " << INT2MM(verts[2].z) << "\n"); DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR(" endloop\n"); DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR(" endfacet\n"); } DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR("endsolid Cura_out\n"); for(auto setting : object.settings()) { mesh.setSetting(setting.name(), setting.value()); } d->object_ids.push_back(object.id()); mesh.finish(); } for(auto setting : list->settings()) { object_to_slice->setSetting(setting.name(), setting.value()); } d->object_count++; object_to_slice->finalize(); }
void generateSkins(int layerNr, SliceVolumeStorage& storage, int extrusionWidth, int downSkinCount, int upSkinCount, int infillOverlap) { SliceLayer* layer = &storage.layers[layerNr]; for(unsigned int partNr=0; partNr<layer->parts.size(); partNr++) { SliceLayerPart* part = &layer->parts[partNr]; Polygons upskin = part->insets[part->insets.size() - 1].offset(-extrusionWidth/2); Polygons downskin = upskin; if (part->insets.size() > 1) { //Add thin wall filling by taking the area between the insets. Polygons thinWalls = part->insets[0].offset(-extrusionWidth / 2 - extrusionWidth * infillOverlap / 100).difference(part->insets[1].offset(extrusionWidth * 6 / 10)); upskin.add(thinWalls); downskin.add(thinWalls); } if (int(layerNr - downSkinCount) >= 0) { SliceLayer* layer2 = &storage.layers[layerNr - downSkinCount]; for(unsigned int partNr2=0; partNr2<layer2->parts.size(); partNr2++) { if (part->boundaryBox.hit(layer2->parts[partNr2].boundaryBox)) downskin = downskin.difference(layer2->parts[partNr2].insets[layer2->parts[partNr2].insets.size() - 1]); } } if (int(layerNr + upSkinCount) < (int)storage.layers.size()) { SliceLayer* layer2 = &storage.layers[layerNr + upSkinCount]; for(unsigned int partNr2=0; partNr2<layer2->parts.size(); partNr2++) { if (part->boundaryBox.hit(layer2->parts[partNr2].boundaryBox)) upskin = upskin.difference(layer2->parts[partNr2].insets[layer2->parts[partNr2].insets.size() - 1]); } } part->skinOutline = upskin.unionPolygons(downskin); double minAreaSize = (2 * M_PI * INT2MM(extrusionWidth) * INT2MM(extrusionWidth)) * 0.3; for(unsigned int i=0; i<part->skinOutline.size(); i++) { double area = INT2MM(INT2MM(fabs(part->skinOutline[i].area()))); if (area < minAreaSize) // Only create an up/down skin if the area is large enough. So you do not create tiny blobs of "trying to fill" { part->skinOutline.remove(i); i -= 1; } } } }
void MergeInfillLines::writeCompensatedMove(Point& to, double speed, GCodePath& last_path, int64_t new_line_width) { double old_line_width = INT2MM(last_path.config->getLineWidth()); double new_line_width_mm = INT2MM(new_line_width); double extrusion_mod = new_line_width_mm / old_line_width; double new_speed = speed; if (speed_equalize_flow_enabled) { double speed_mod = old_line_width / new_line_width_mm; new_speed = std::min(speed * speed_mod, speed_equalize_flow_max); } sendLineTo(last_path.config->type, to, last_path.getLineWidthForLayerView(), last_path.config->getLayerThickness(), new_speed); gcode.writeExtrusion(to, new_speed, last_path.getExtrusionMM3perMM() * extrusion_mod, last_path.config->type); }
bool MergeInfillLines::isConvertible(unsigned int path_idx_first_move, Point& first_middle, Point& second_middle, int64_t& line_width, bool use_second_middle_as_first) { int64_t max_line_width = nozzle_size * 3 / 2; unsigned int idx = path_idx_first_move; if (idx + 3 > paths.size()-1) return false; if (paths[idx+0].config != &travelConfig) return false; if (paths[idx+1].points.size() > 1) return false; if (paths[idx+1].config == &travelConfig) return false; // if (paths[idx+2].points.size() > 1) return false; if (paths[idx+2].config != &travelConfig) return false; if (paths[idx+3].points.size() > 1) return false; if (paths[idx+3].config == &travelConfig) return false; Point& a = paths[idx+0].points.back(); // first extruded line from Point& b = paths[idx+1].points.back(); // first extruded line to Point& c = paths[idx+2].points.back(); // second extruded line from Point& d = paths[idx+3].points.back(); // second extruded line to Point ab = b - a; Point cd = d - c; int64_t prod = dot(ab,cd); if (std::abs(prod) + 400 < vSize(ab) * vSize(cd)) // 400 = 20*20, where 20 micron is the allowed inaccuracy in the dot product, introduced by the inaccurate point locations of a,b,c,d return false; // extrusion moves not in the same or opposite diraction if (prod < 0) { ab = ab * -1; } Point infill_vector = (cd + ab) / 2; if (!shorterThen(infill_vector, 5 * nozzle_size)) return false; // infill lines too far apart first_middle = (use_second_middle_as_first)? second_middle : (a + b) / 2; second_middle = (c + d) / 2; Point dir_vector_perp = crossZ(second_middle - first_middle); int64_t dir_vector_perp_length = vSize(dir_vector_perp); // == dir_vector_length if (dir_vector_perp_length == 0) return false; if (dir_vector_perp_length > 5 * nozzle_size) return false; // infill lines too far apart line_width = std::abs( dot(dir_vector_perp, infill_vector) / dir_vector_perp_length ); if (line_width > max_line_width) return false; // combined lines would be too wide if (line_width == 0) return false; // dot is zero, so lines are in each others extension, not next to eachother { // check whether the two lines are adjacent Point ca = first_middle - c; double ca_size = vSizeMM(ca); double cd_size = vSizeMM(cd); double prod = INT2MM(dot(ca, cd)); double fraction = prod / ( ca_size * cd_size ); int64_t line2line_dist = MM2INT(cd_size * std::sqrt(1.0 - fraction * fraction)); if (line2line_dist + 20 > paths[idx+1].config->getLineWidth()) return false; // there is a gap between the two lines } return true; };
void GCodeExport::writeZhopStart(int hop_height) { if (hop_height > 0) { isZHopped = hop_height; *output_stream << std::setprecision(3) << "G1 Z" << INT2MM(currentPosition.z + isZHopped) << new_line; } }
void GCodeExport::writeRetraction(RetractionConfig* config, bool force) { if (flavor == EGCodeFlavor::BFB)//BitsFromBytes does automatic retraction. return; if (isRetracted) return; if (config->amount <= 0) return; if (!force && config->retraction_count_max > 0 && int(extrusion_amount_at_previous_n_retractions.size()) == config->retraction_count_max - 1 && extrusion_amount < extrusion_amount_at_previous_n_retractions.back() + config->retraction_extrusion_window) return; if (config->primeAmount > 0) { extrusion_amount += config->primeAmount; } retractionPrimeSpeed = config->primeSpeed; if (flavor == EGCodeFlavor::ULTIGCODE || flavor == EGCodeFlavor::REPRAP_VOLUMATRIC) { *output_stream << "G10\n"; //Assume default UM2 retraction settings. double retraction_distance = 4.5; estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), extrusion_amount - retraction_distance), 25); // TODO: hardcoded values! }else{ *output_stream << "G1 F" << (config->speed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << extrusion_amount - config->amount << "\n"; currentSpeed = config->speed; estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), extrusion_amount - config->amount), currentSpeed); } if (config->zHop > 0) { isZHopped = config->zHop; *output_stream << std::setprecision(3) << "G1 Z" << INT2MM(currentPosition.z + isZHopped) << "\n"; } extrusion_amount_at_previous_n_retractions.push_front(extrusion_amount); if (int(extrusion_amount_at_previous_n_retractions.size()) == config->retraction_count_max) { extrusion_amount_at_previous_n_retractions.pop_back(); } isRetracted = true; }
void GCodeExport::writeRetraction() { if (flavor == GCODE_FLAVOR_BFB)//BitsFromBytes does automatic retraction. return; if (retractionAmount > 0 && !isRetracted && extrusionAmountAtPreviousRetraction + minimalExtrusionBeforeRetraction < extrusionAmount) { if (flavor == GCODE_FLAVOR_ULTIGCODE) { fprintf(f, "G10\n"); }else{ fprintf(f, "G1 F%i %c%0.5lf\n", retractionSpeed * 60, extruderCharacter[extruderNr], extrusionAmount - retractionAmount); currentSpeed = retractionSpeed; estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), extrusionAmount - retractionAmount), currentSpeed); } if (retractionZHop > 0) fprintf(f, "G1 Z%0.2f\n", INT2MM(currentPosition.z + retractionZHop)); extrusionAmountAtPreviousRetraction = extrusionAmount; isRetracted = true; } }
void GCodePathConfig::calculateExtrusion() { extrusion_mm3_per_mm = INT2MM(current_config.line_width) * INT2MM(layer_thickness) * double(current_config.flow) / 100.0; }
void GCodeExport::writeRetraction(RetractionConfig* config, bool force, bool extruder_switch) { ExtruderTrainAttributes& extr_attr = extruder_attr[current_extruder]; if (flavor == EGCodeFlavor::BFB)//BitsFromBytes does automatic retraction. { if (extruder_switch) { if (!extr_attr.retraction_e_amount_current) *output_stream << "M103" << new_line; extr_attr.retraction_e_amount_current = 1.0; // 1.0 is a stub; BFB doesn't use the actual retracted amount; retraction is performed by firmware } return; } double old_retraction_e_amount = extr_attr.retraction_e_amount_current; double new_retraction_e_amount = mmToE(config->distance); double retraction_diff_e_amount = old_retraction_e_amount - new_retraction_e_amount; if (std::abs(retraction_diff_e_amount) < 0.000001) { return; } { // handle retraction limitation double current_extruded_volume = getCurrentExtrudedVolume(); std::deque<double>& extruded_volume_at_previous_n_retractions = extr_attr.extruded_volume_at_previous_n_retractions; while (int(extruded_volume_at_previous_n_retractions.size()) > config->retraction_count_max && !extruded_volume_at_previous_n_retractions.empty()) { // extruder switch could have introduced data which falls outside the retraction window // also the retraction_count_max could have changed between the last retraction and this extruded_volume_at_previous_n_retractions.pop_back(); } if (!force && config->retraction_count_max <= 0) { return; } if (!force && int(extruded_volume_at_previous_n_retractions.size()) == config->retraction_count_max && current_extruded_volume < extruded_volume_at_previous_n_retractions.back() + config->retraction_extrusion_window * extr_attr.filament_area) { return; } extruded_volume_at_previous_n_retractions.push_front(current_extruded_volume); if (int(extruded_volume_at_previous_n_retractions.size()) == config->retraction_count_max + 1) { extruded_volume_at_previous_n_retractions.pop_back(); } } if (firmware_retract) { if (extruder_switch && extr_attr.retraction_e_amount_current) { return; } *output_stream << "G10"; if (extruder_switch) { *output_stream << " S1"; } *output_stream << new_line; //Assume default UM2 retraction settings. estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value + retraction_diff_e_amount)), 25); // TODO: hardcoded values! } else { double speed = ((retraction_diff_e_amount < 0.0)? config->speed : extr_attr.last_retraction_prime_speed) * 60; current_e_value += retraction_diff_e_amount; *output_stream << "G1 F" << speed << " " << extr_attr.extruderCharacter << std::setprecision(5) << current_e_value << new_line; currentSpeed = speed; estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value)), currentSpeed); extr_attr.last_retraction_prime_speed = config->primeSpeed; } extr_attr.retraction_e_amount_current = new_retraction_e_amount; // suppose that for UM2 the retraction amount in the firmware is equal to the provided amount extr_attr.prime_volume += config->prime_volume; }
void GCodeExport::writeMove(int x, int y, int z, double speed, double extrusion_mm3_per_mm) { if (currentPosition.x == x && currentPosition.y == y && currentPosition.z == z) return; #ifdef ASSERT_INSANE_OUTPUT assert(speed < 200 && speed > 1); // normal F values occurring in UM2 gcode (this code should not be compiled for release) assert(currentPosition != no_point3); assert((Point3(x,y,z) - currentPosition).vSize() < MM2INT(300)); // no crazy positions (this code should not be compiled for release) #endif //ASSERT_INSANE_OUTPUT if (extrusion_mm3_per_mm < 0) logWarning("Warning! Negative extrusion move!"); if (flavor == EGCodeFlavor::BFB) { writeMoveBFB(x, y, z, speed, extrusion_mm3_per_mm); return; } double extrusion_per_mm = mm3ToE(extrusion_mm3_per_mm); Point gcode_pos = getGcodePos(x,y, current_extruder); if (extrusion_mm3_per_mm > 0.000001) { Point3 diff = Point3(x,y,z) - getPosition(); if (isZHopped > 0) { *output_stream << std::setprecision(3) << "G1 Z" << INT2MM(currentPosition.z) << new_line; isZHopped = 0; } double prime_volume = extruder_attr[current_extruder].prime_volume; current_e_value += mm3ToE(prime_volume); if (extruder_attr[current_extruder].retraction_e_amount_current) { if (firmware_retract) { // note that BFB is handled differently *output_stream << "G11" << new_line; //Assume default UM2 retraction settings. if (prime_volume > 0) { *output_stream << "G1 F" << (extruder_attr[current_extruder].last_retraction_prime_speed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value << new_line; currentSpeed = extruder_attr[current_extruder].last_retraction_prime_speed; } estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value)), 25.0); } else { current_e_value += extruder_attr[current_extruder].retraction_e_amount_current; *output_stream << "G1 F" << (extruder_attr[current_extruder].last_retraction_prime_speed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value << new_line; currentSpeed = extruder_attr[current_extruder].last_retraction_prime_speed; estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value)), currentSpeed); } if (getCurrentExtrudedVolume() > 10000.0) //According to https://github.com/Ultimaker/CuraEngine/issues/14 having more then 21m of extrusion causes inaccuracies. So reset it every 10m, just to be sure. { resetExtrusionValue(); } extruder_attr[current_extruder].retraction_e_amount_current = 0.0; } else if (prime_volume > 0.0) { *output_stream << "G1 F" << (extruder_attr[current_extruder].last_retraction_prime_speed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value << new_line; currentSpeed = extruder_attr[current_extruder].last_retraction_prime_speed; estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value)), currentSpeed); } extruder_attr[current_extruder].prime_volume = 0.0; current_e_value += extrusion_per_mm * diff.vSizeMM(); *output_stream << "G1"; } else { *output_stream << "G0"; CommandSocket::sendLineTo(extruder_attr[current_extruder].retraction_e_amount_current ? PrintFeatureType::MoveRetraction : PrintFeatureType::MoveCombing, Point(x, y), extruder_attr[current_extruder].retraction_e_amount_current ? MM2INT(0.2) : MM2INT(0.1)); } if (currentSpeed != speed) { *output_stream << " F" << (speed * 60); currentSpeed = speed; } *output_stream << std::setprecision(3) << " X" << INT2MM(gcode_pos.X) << " Y" << INT2MM(gcode_pos.Y); if (z != currentPosition.z + isZHopped) *output_stream << " Z" << INT2MM(z + isZHopped); if (extrusion_mm3_per_mm > 0.000001) *output_stream << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value; *output_stream << new_line; currentPosition = Point3(x, y, z); estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value)), speed); }
void GCodeExport::setFilamentDiameter(unsigned int extruder, int diameter) { double r = INT2MM(diameter) / 2.0; double area = M_PI * r * r; extruder_attr[extruder].filament_area = area; }
void GCodeExport::writeRetraction(RetractionConfig* config, bool force) { if (flavor == EGCodeFlavor::BFB)//BitsFromBytes does automatic retraction. { return; } if (extruder_attr[current_extruder].retraction_e_amount_current == config->distance * ((is_volumatric)? extruder_attr[current_extruder].filament_area : 1.0)) { return; } if (config->distance <= 0) { return; } { // handle retraction limitation double current_extruded_volume = getCurrentExtrudedVolume(); std::deque<double>& extruded_volume_at_previous_n_retractions = extruder_attr[current_extruder].extruded_volume_at_previous_n_retractions; while (int(extruded_volume_at_previous_n_retractions.size()) >= config->retraction_count_max && !extruded_volume_at_previous_n_retractions.empty()) { // extruder switch could have introduced data which falls outside the retraction window // also the retraction_count_max could have changed between the last retraction and this extruded_volume_at_previous_n_retractions.pop_back(); } if (!force && config->retraction_count_max <= 0) { return; } if (!force && int(extruded_volume_at_previous_n_retractions.size()) == config->retraction_count_max - 1 && current_extruded_volume < extruded_volume_at_previous_n_retractions.back() + config->retraction_extrusion_window * extruder_attr[current_extruder].filament_area) { return; } extruded_volume_at_previous_n_retractions.push_front(current_extruded_volume); if (int(extruded_volume_at_previous_n_retractions.size()) == config->retraction_count_max) { extruded_volume_at_previous_n_retractions.pop_back(); } } extruder_attr[current_extruder].last_retraction_prime_speed = config->primeSpeed; double retraction_e_amount = config->distance * ((is_volumatric)? extruder_attr[current_extruder].filament_area : 1.0); if (firmware_retract) { *output_stream << "G10\n"; //Assume default UM2 retraction settings. estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), current_e_value - retraction_e_amount), 25); // TODO: hardcoded values! } else { current_e_value -= retraction_e_amount; *output_stream << "G1 F" << (config->speed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value << "\n"; currentSpeed = config->speed; estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), current_e_value), currentSpeed); } extruder_attr[current_extruder].retraction_e_amount_current = retraction_e_amount ; extruder_attr[current_extruder].prime_volume += config->prime_volume; if (config->zHop > 0) { isZHopped = config->zHop; *output_stream << std::setprecision(3) << "G1 Z" << INT2MM(currentPosition.z + isZHopped) << "\n"; } }
bool SpaghettiInfillPathGenerator::processSpaghettiInfill(const SliceDataStorage& storage, const FffGcodeWriter& fff_gcode_writer, LayerPlan& gcode_layer, const SliceMeshStorage& mesh, const int extruder_nr, const PathConfigStorage::MeshPathConfigs& mesh_config, const SliceLayerPart& part, int infill_line_distance, int infill_overlap, int infill_angle, const Point& infill_origin) { if (extruder_nr != mesh.getSettingAsExtruderNr("infill_extruder_nr")) { return false; } bool added_something = false; const GCodePathConfig& config = mesh_config.infill_config[0]; const EFillMethod pattern = mesh.getSettingAsFillMethod("infill_pattern"); const bool zig_zaggify_infill = mesh.getSettingBoolean("zig_zaggify_infill"); const bool connect_polygons = true; // spaghetti infill should have as least as possible travel moves const unsigned int infill_line_width = config.getLineWidth(); constexpr int infill_multiplier = 1; const int64_t infill_shift = 0; constexpr int wall_line_count = 0; const int64_t outline_offset = 0; const double layer_height_mm = (gcode_layer.getLayerNr() == 0) ? mesh.getSettingInMillimeters("layer_height_0") : mesh.getSettingInMillimeters("layer_height"); // For each part on this layer which is used to fill that part and parts below: for (const std::pair<Polygons, double>& filling_area : part.spaghetti_infill_volumes) { Polygons infill_lines; Polygons infill_polygons; const Polygons& area = filling_area.first; // Area of the top within which to move while extruding (might be empty if the spaghetti_inset was too large) const double total_volume = filling_area.second * mesh.getSettingAsRatio("spaghetti_flow") + mesh.getSettingInCubicMillimeters("spaghetti_infill_extra_volume"); // volume to be extruded if (total_volume <= 0.0) { continue; } // generate zigzag print head paths Polygons* perimeter_gaps_output = nullptr; const bool connected_zigzags = true; const bool use_endpieces = false; Infill infill_comp(pattern, zig_zaggify_infill, connect_polygons, area, outline_offset , infill_line_width, infill_line_distance, infill_overlap, infill_multiplier, infill_angle, gcode_layer.z, infill_shift, wall_line_count, infill_origin, perimeter_gaps_output, connected_zigzags, use_endpieces , mesh.getSettingInMicrons("cross_infill_pocket_size")); // cross_fill_patterns is only generated when spaghetti infill is not used, // so we pass nullptr here. infill_comp.generate(infill_polygons, infill_lines, nullptr, &mesh); // add paths to plan with a higher flow ratio in order to extrude the required amount. const coord_t total_length = infill_polygons.polygonLength() + infill_lines.polyLineLength(); if (total_length > 0) { // zigzag path generation actually generated paths // calculate the normal volume extruded when using the layer height and line width to calculate extrusion const double normal_volume = INT2MM(INT2MM(total_length * infill_line_width)) * layer_height_mm; assert(normal_volume > 0.0); const float flow_ratio = total_volume / normal_volume; assert(flow_ratio / mesh.getSettingAsRatio("spaghetti_flow") >= 0.9); assert(!std::isnan(flow_ratio) && !std::isinf(flow_ratio)); if (!infill_polygons.empty() || !infill_lines.empty()) { added_something = true; fff_gcode_writer.setExtruder_addPrime(storage, gcode_layer, extruder_nr); if (!infill_polygons.empty()) { constexpr bool force_comb_retract = false; gcode_layer.addTravel(infill_polygons[0][0], force_comb_retract); gcode_layer.addPolygonsByOptimizer(infill_polygons, config, nullptr, ZSeamConfig(), 0, false, flow_ratio); } const bool is_zigzag = mesh.getSettingBoolean("zig_zaggify_infill") || pattern == EFillMethod::ZIG_ZAG; const coord_t wipe_dist = is_zigzag ? 0 : -mesh.getSettingInMicrons("infill_wipe_dist"); const SpaceFillType line_type = is_zigzag ? SpaceFillType::Lines : SpaceFillType::PolyLines; gcode_layer.addLinesByOptimizer(infill_lines, config, line_type, false, wipe_dist, flow_ratio); } } else { // zigzag path generation couldn't generate paths, probably because the area was too small // generate small path near the middle of the filling area // note that we need a path with positive length because that is currently the only way to insert an extrusion in a layer plan constexpr int path_length = 10; Point middle = AABB(area).getMiddle(); if (!area.inside(middle)) { PolygonUtils::ensureInsideOrOutside(area, middle, infill_line_width / 2); } const double normal_volume = INT2MM(INT2MM(path_length * infill_line_width)) * layer_height_mm; const float flow_ratio = total_volume / normal_volume; gcode_layer.addTravel(middle); gcode_layer.addExtrusionMove(middle + Point(0, path_length), config, SpaceFillType::Lines, flow_ratio); } } return added_something; }
/*! * \brief Convert and add a point to the points buffer. * * Each point is represented as two consecutive floats. All members adding a * 2D point to the data should use this function. */ void addPoint2D(const Point& point) { points.push_back(INT2MM(point.X)); points.push_back(INT2MM(point.Y)); last_point = point; }
bool GCodePlanner::writePathWithCoasting(GCodePath& path, GCodePath& path_next, int64_t layerThickness, double coasting_volume, double coasting_speed, double coasting_min_volume, bool extruder_switch_retract) { int64_t coasting_min_dist_considered = 100; // hardcoded setting for when to not perform coasting double extrude_speed = path.config->getSpeed() * getExtrudeSpeedFactor(); // travel speed int64_t coasting_dist = MM2INT(MM2_2INT(coasting_volume) / layerThickness) / path.config->getLineWidth(); // closing brackets of MM2INT at weird places for precision issues int64_t coasting_min_dist = MM2INT(MM2_2INT(coasting_min_volume) / layerThickness) / path.config->getLineWidth(); // closing brackets of MM2INT at weird places for precision issues std::vector<int64_t> accumulated_dist_per_point; // the first accumulated dist is that of the last point! (that of the last point is always zero...) accumulated_dist_per_point.push_back(0); int64_t accumulated_dist = 0; bool length_is_less_than_min_dist = true; unsigned int acc_dist_idx_gt_coast_dist = NO_INDEX; // the index of the first point with accumulated_dist more than coasting_dist (= index into accumulated_dist_per_point) // == the point printed BEFORE the start point for coasting Point* last = &path.points[path.points.size() - 1]; for (unsigned int backward_point_idx = 1; backward_point_idx < path.points.size(); backward_point_idx++) { Point& point = path.points[path.points.size() - 1 - backward_point_idx]; int64_t dist = vSize(point - *last); accumulated_dist += dist; accumulated_dist_per_point.push_back(accumulated_dist); if (acc_dist_idx_gt_coast_dist == NO_INDEX && accumulated_dist >= coasting_dist) { acc_dist_idx_gt_coast_dist = backward_point_idx; // the newly added point } if (accumulated_dist >= coasting_min_dist) { length_is_less_than_min_dist = false; break; } last = &point; } if (accumulated_dist < coasting_min_dist_considered) { return false; } int64_t actual_coasting_dist = coasting_dist; if (length_is_less_than_min_dist) { // in this case accumulated_dist is the length of the whole path actual_coasting_dist = accumulated_dist * coasting_dist / coasting_min_dist; for (acc_dist_idx_gt_coast_dist = 0 ; acc_dist_idx_gt_coast_dist < accumulated_dist_per_point.size() ; acc_dist_idx_gt_coast_dist++) { // search for the correct coast_dist_idx if (accumulated_dist_per_point[acc_dist_idx_gt_coast_dist] > actual_coasting_dist) { break; } } } if (acc_dist_idx_gt_coast_dist == NO_INDEX) { // something has gone wrong; coasting_min_dist < coasting_dist ? return false; } unsigned int point_idx_before_start = path.points.size() - 1 - acc_dist_idx_gt_coast_dist; Point start; { // computation of begin point of coasting int64_t residual_dist = actual_coasting_dist - accumulated_dist_per_point[acc_dist_idx_gt_coast_dist - 1]; Point& a = path.points[point_idx_before_start]; Point& b = path.points[point_idx_before_start + 1]; start = b + normal(a-b, residual_dist); } { // write normal extrude path: for(unsigned int point_idx = 0; point_idx <= point_idx_before_start; point_idx++) { gcode.writeMove(path.points[point_idx], extrude_speed, path.getExtrusionMM3perMM()); } gcode.writeMove(start, extrude_speed, path.getExtrusionMM3perMM()); } if (path_next.retract) { writeRetraction(extruder_switch_retract, path.config->retraction_config); } for (unsigned int point_idx = point_idx_before_start + 1; point_idx < path.points.size(); point_idx++) { gcode.writeMove(path.points[point_idx], coasting_speed * path.config->getSpeed(), 0); } gcode.setLastCoastedAmountMM3(path.getExtrusionMM3perMM() * INT2MM(actual_coasting_dist)); return true; }
void GCodeExport::writeMove(int x, int y, int z, double speed, double extrusion_mm3_per_mm) { if (currentPosition.x == x && currentPosition.y == y && currentPosition.z == z) return; assert(speed < 200 && speed > 1); // normal F values occurring in UM2 gcode (this code should not be compiled for release) assert((Point3(x,y,z) - currentPosition).vSize() < MM2INT(300)); // no crazy positions (this code should not be compiled for release) if (extrusion_mm3_per_mm < 0) logWarning("Warning! Negative extrusion move!"); double extrusion_per_mm = extrusion_mm3_per_mm; if (!is_volumatric) { extrusion_per_mm = extrusion_mm3_per_mm / getFilamentArea(current_extruder); } Point gcode_pos = getGcodePos(x,y, current_extruder); if (flavor == EGCodeFlavor::BFB) { //For Bits From Bytes machines, we need to handle this completely differently. As they do not use E values but RPM values. float fspeed = speed * 60; float rpm = extrusion_per_mm * speed * 60; const float mm_per_rpm = 4.0; //All BFB machines have 4mm per RPM extrusion. rpm /= mm_per_rpm; if (rpm > 0) { if (isRetracted) { if (currentSpeed != double(rpm)) { //fprintf(f, "; %f e-per-mm %d mm-width %d mm/s\n", extrusion_per_mm, lineWidth, speed); //fprintf(f, "M108 S%0.1f\r\n", rpm); *output_stream << "M108 S" << std::setprecision(1) << rpm << "\r\n"; currentSpeed = double(rpm); } //Add M101 or M201 to enable the proper extruder. *output_stream << "M" << int((current_extruder + 1) * 100 + 1) << "\r\n"; isRetracted = false; } //Fix the speed by the actual RPM we are asking, because of rounding errors we cannot get all RPM values, but we have a lot more resolution in the feedrate value. // (Trick copied from KISSlicer, thanks Jonathan) fspeed *= (rpm / (roundf(rpm * 100) / 100)); //Increase the extrusion amount to calculate the amount of filament used. Point3 diff = Point3(x,y,z) - getPosition(); extrusion_amount += extrusion_per_mm * diff.vSizeMM(); }else{ //If we are not extruding, check if we still need to disable the extruder. This causes a retraction due to auto-retraction. if (!isRetracted) { *output_stream << "M103\r\n"; isRetracted = true; } } *output_stream << std::setprecision(3) << "G1 X" << INT2MM(gcode_pos.X) << " Y" << INT2MM(gcode_pos.Y) << " Z" << INT2MM(z) << std::setprecision(1) << " F" << fspeed << "\r\n"; } else { //Normal E handling. if (extrusion_mm3_per_mm > 0.000001) { Point3 diff = Point3(x,y,z) - getPosition(); if (isZHopped > 0) { // TinyG G1: Straight feed *output_stream << std::setprecision(3) << "G1 Z" << INT2MM(currentPosition.z) << "\n"; isZHopped = 0; } extrusion_amount += (is_volumatric) ? last_coasted_amount_mm3 : last_coasted_amount_mm3 / getFilamentArea(current_extruder); if (isRetracted) { if (flavor == EGCodeFlavor::ULTIGCODE || flavor == EGCodeFlavor::REPRAP_VOLUMATRIC) { *output_stream << "G11\n"; //TODO try this code and see what happens //Assume default UM2 retraction settings. if (last_coasted_amount_mm3 > 0) { *output_stream << "G1 F" << (retractionPrimeSpeed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << extrusion_amount << "\n"; } estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), extrusion_amount), 25.0); }else{ // TinyG checked *output_stream << "G1 F" << (retractionPrimeSpeed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << extrusion_amount << "\n"; currentSpeed = retractionPrimeSpeed; estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), extrusion_amount), currentSpeed); } if (getExtrusionAmountMM3(current_extruder) > 10000.0) //According to https://github.com/Ultimaker/CuraEngine/issues/14 having more then 21m of extrusion causes inaccuracies. So reset it every 10m, just to be sure. resetExtrusionValue(); isRetracted = false; } else { if (last_coasted_amount_mm3 > 0) { *output_stream << "G1 F" << (retractionPrimeSpeed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << extrusion_amount << "\n"; estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), extrusion_amount), currentSpeed); } } last_coasted_amount_mm3 = 0; extrusion_amount += extrusion_per_mm * diff.vSizeMM(); // TinyG TODO: add one axis *output_stream << "G1"; }else{ *output_stream << "G0"; if (commandSocket) { // we should send this travel as a non-retraction move cura::Polygons travelPoly; PolygonRef travel = travelPoly.newPoly(); travel.add(Point(currentPosition.x, currentPosition.y)); travel.add(Point(x, y)); commandSocket->sendPolygons(isRetracted ? MoveRetractionType : MoveCombingType, layer_nr, travelPoly, isRetracted ? MM2INT(0.2) : MM2INT(0.1)); } } if (currentSpeed != speed) { *output_stream << " F" << (speed * 60); currentSpeed = speed; } *output_stream << std::setprecision(3) << " X" << INT2MM(gcode_pos.X) << " Y" << INT2MM(gcode_pos.Y); if (z != currentPosition.z) *output_stream << " Z" << INT2MM(z + isZHopped); if (extrusion_mm3_per_mm > 0.000001) *output_stream << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << extrusion_amount; *output_stream << "\n"; } currentPosition = Point3(x, y, z); startPosition = currentPosition; estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), extrusion_amount), speed); }
void CommandSocket::handleObjectList(cura::proto::ObjectList* list) { if (list->objects_size() <= 0) { return; } FMatrix3x3 matrix; //private_data->object_count = 0; //private_data->object_ids.clear(); private_data->objects_to_slice.push_back(std::make_shared<MeshGroup>(FffProcessor::getInstance())); MeshGroup* meshgroup = private_data->objects_to_slice.back().get(); for (auto setting : list->settings()) { meshgroup->setSetting(setting.name(), setting.value()); } for (int extruder_nr = 0; extruder_nr < FffProcessor::getInstance()->getSettingAsCount("machine_extruder_count"); extruder_nr++) { // initialize remaining extruder trains and load the defaults ExtruderTrain* train = meshgroup->createExtruderTrain(extruder_nr); // create new extruder train objects or use already existing ones SettingRegistry::getInstance()->loadExtruderJSONsettings(extruder_nr, train); } for (auto object : list->objects()) { int bytes_per_face = BYTES_PER_FLOAT * FLOATS_PER_VECTOR * VECTORS_PER_FACE; int face_count = object.vertices().size() / bytes_per_face; if (face_count <= 0) { logWarning("Got an empty mesh, ignoring it!"); continue; } DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR("solid Cura_out\n"); int extruder_train_nr = 0; // TODO: make primary extruder configurable! for (auto setting : object.settings()) { if (setting.name() == "extruder_nr") { extruder_train_nr = std::stoi(setting.value()); break; } } SettingsBase* extruder_train = meshgroup->getExtruderTrain(extruder_train_nr); meshgroup->meshes.push_back(extruder_train); //Construct a new mesh (with the corresponding extruder train as settings parent object) and put it into MeshGroup's mesh list. Mesh& mesh = meshgroup->meshes.back(); for (int i = 0; i < face_count; ++i) { //TODO: Apply matrix std::string data = object.vertices().substr(i * bytes_per_face, bytes_per_face); const FPoint3* float_vertices = reinterpret_cast<const FPoint3*>(data.data()); Point3 verts[3]; verts[0] = matrix.apply(float_vertices[0]); verts[1] = matrix.apply(float_vertices[1]); verts[2] = matrix.apply(float_vertices[2]); mesh.addFace(verts[0], verts[1], verts[2]); DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR(" facet normal -1 0 0\n"); DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR(" outer loop\n"); DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR(" vertex "<<INT2MM(verts[0].x) <<" " << INT2MM(verts[0].y) <<" " << INT2MM(verts[0].z) << "\n"); DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR(" vertex "<<INT2MM(verts[1].x) <<" " << INT2MM(verts[1].y) <<" " << INT2MM(verts[1].z) << "\n"); DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR(" vertex "<<INT2MM(verts[2].x) <<" " << INT2MM(verts[2].y) <<" " << INT2MM(verts[2].z) << "\n"); DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR(" endloop\n"); DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR(" endfacet\n"); } DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR("endsolid Cura_out\n"); for (auto setting : object.settings()) { mesh.setSetting(setting.name(), setting.value()); } mesh.finish(); } private_data->object_count++; meshgroup->finalize(); }
/*! * 直线运动到指定点 */ void GCodeExport::writeMove(int x, int y, int z, int speed, double extrusion_per_mm) { if (currentPosition.x == x && currentPosition.y == y && currentPosition.z == z) return; if (flavor == GCODE_FLAVOR_BFB) { //For Bits From Bytes machines, we need to handle this completely differently. As they do not use E values but RPM values. float fspeed = speed * 60; float rpm = extrusion_per_mm * speed * 60; const float mm_per_rpm = 4.0; //All BFB machines have 4mm per RPM extrusion. rpm /= mm_per_rpm; if (rpm > 0) { if (isRetracted) { if (currentSpeed != int(rpm * 10)) { //fprintf(f, "; %f e-per-mm %d mm-width %d mm/s\n", extrusion_per_mm, lineWidth, speed); //fprintf(f, "M108 S%0.1f\r\n", rpm); *output_stream << "M108 S" << std::setprecision(1) << rpm << "\r\n"; currentSpeed = int(rpm * 10); } //Add M101 or M201 to enable the proper extruder. *output_stream << "M" << int((extruderNr + 1) * 100 + 1) << "\r\n"; isRetracted = false; } //Fix the speed by the actual RPM we are asking, because of rounding errors we cannot get all RPM values, but we have a lot more resolution in the feedrate value. // (Trick copied from KISSlicer, thanks Jonathan) fspeed *= (rpm / (roundf(rpm * 100) / 100)); //Increase the extrusion amount to calculate the amount of filament used. Point3 diff = Point3(x,y,z) - getPosition(); extrusion_amount += extrusion_per_mm * diff.vSizeMM(); }else{ //If we are not extruding, check if we still need to disable the extruder. This causes a retraction due to auto-retraction. if (!isRetracted) { *output_stream << "M103\r\n"; isRetracted = true; } } *output_stream << std::setprecision(3)/*精度设定*/ << "G1 X" << INT2MM(x - extruderOffset[extruderNr].X) << " Y" << INT2MM(y - extruderOffset[extruderNr].Y) << " Z" << INT2MM(z) << std::setprecision(1) << " F" << fspeed << "\r\n"; }else{ //Normal E handling. if (extrusion_per_mm > 0.000001) { Point3 diff = Point3(x,y,z) - getPosition(); if (isZHopped > 0) { *output_stream << std::setprecision(3) << "G1 Z" << INT2MM(currentPosition.z) << "\n"; isZHopped = false; } if (isRetracted) { if (flavor == GCODE_FLAVOR_ULTIGCODE || flavor == GCODE_FLAVOR_REPRAP_VOLUMATRIC) { *output_stream << "G11\n"; //Assume default UM2 retraction settings. estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), extrusion_amount), 25.0); }else{ *output_stream << "G1 F" << (retractionPrimeSpeed * 60) << " " << extruderCharacter[extruderNr] << std::setprecision(5) << extrusion_amount << "\n"; currentSpeed = retractionPrimeSpeed; estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), extrusion_amount), currentSpeed); } if (extrusion_amount > 10000.0) //According to https://github.com/Ultimaker/CuraEngine/issues/14 having more then 21m of extrusion causes inaccuracies. So reset it every 10m, just to be sure. resetExtrusionValue(); isRetracted = false; } extrusion_amount += extrusion_per_mm * diff.vSizeMM(); *output_stream << "G1"; }else{ *output_stream << "G0"; } if (currentSpeed != speed) { *output_stream << " F" << (speed * 60); currentSpeed = speed; } *output_stream << std::setprecision(3) << " X" << INT2MM(x - extruderOffset[extruderNr].X) << " Y" << INT2MM(y - extruderOffset[extruderNr].Y); if (z != currentPosition.z) *output_stream << " Z" << INT2MM(z); if (extrusion_per_mm > 0.000001) *output_stream << " " << extruderCharacter[extruderNr] << std::setprecision(5) << extrusion_amount; *output_stream << "\n"; } currentPosition = Point3(x, y, z); startPosition = currentPosition; estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), extrusion_amount), speed); }
void GCodeExport::writeMove(Point p, int speed, int lineWidth) { if (flavor == GCODE_FLAVOR_BFB) { //For Bits From Bytes machines, we need to handle this completely differently. As they do not use E values but RPM values. float fspeed = speed * 60; float rpm = (extrusionPerMM * double(lineWidth) / 1000.0) * speed * 60; const float mm_per_rpm = 4.0; //All BFB machines have 4mm per RPM extrusion. rpm /= mm_per_rpm; if (rpm > 0) { if (isRetracted) { if (currentSpeed != int(rpm * 10)) { //fprintf(f, "; %f e-per-mm %d mm-width %d mm/s\n", extrusionPerMM, lineWidth, speed); fprintf(f, "M108 S%0.1f\n", rpm * 10); currentSpeed = int(rpm * 10); } fprintf(f, "M101\n"); isRetracted = false; } //Fix the speed by the actual RPM we are asking, because of rounding errors we cannot get all RPM values, but we have a lot more resolution in the feedrate value. // (Trick copied from KISSlicer, thanks Jonathan) fspeed *= (rpm / (roundf(rpm * 100) / 100)); }else{ //If we are not extruding, check if we still need to disable the extruder. This causes a retraction due to auto-retraction. if (!isRetracted) { fprintf(f, "M103\n"); isRetracted = true; } } fprintf(f, "G1 X%0.2f Y%0.2f Z%0.2f F%0.1f\n", INT2MM(p.X - extruderOffset[extruderNr].X), INT2MM(p.Y - extruderOffset[extruderNr].Y), INT2MM(zPos), fspeed); }else{ //Normal E handling. if (lineWidth != 0) { Point diff = p - getPositionXY(); if (isRetracted) { if (retractionZHop > 0) fprintf(f, "G1 Z%0.2f\n", float(currentPosition.z)/1000); if (flavor == GCODE_FLAVOR_ULTIGCODE) { fprintf(f, "G11\n"); }else{ fprintf(f, "G1 F%i %c%0.5lf\n", retractionSpeed * 60, extruderCharacter[extruderNr], extrusionAmount); currentSpeed = retractionSpeed; estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(p.X), INT2MM(p.Y), INT2MM(zPos), extrusionAmount), currentSpeed); } if (extrusionAmount > 10000.0) //According to https://github.com/Ultimaker/CuraEngine/issues/14 having more then 21m of extrusion causes inaccuracies. So reset it every 10m, just to be sure. resetExtrusionValue(); isRetracted = false; } extrusionAmount += extrusionPerMM * INT2MM(lineWidth) * vSizeMM(diff); fprintf(f, "G1"); }else{ fprintf(f, "G0"); } if (currentSpeed != speed) { fprintf(f, " F%i", speed * 60); currentSpeed = speed; } fprintf(f, " X%0.2f Y%0.2f", INT2MM(p.X - extruderOffset[extruderNr].X), INT2MM(p.Y - extruderOffset[extruderNr].Y)); if (zPos != currentPosition.z) fprintf(f, " Z%0.2f", INT2MM(zPos)); if (lineWidth != 0) fprintf(f, " %c%0.5lf", extruderCharacter[extruderNr], extrusionAmount); fprintf(f, "\n"); } currentPosition = Point3(p.X, p.Y, zPos); estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), extrusionAmount), speed); }