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; };
int64_t WallOverlapComputation::overlapEndingDistance(Point& a1, Point& a2, Point& b1, Point& b2, int a1b1_dist) { int overlap = lineWidth - a1b1_dist; Point a = a2-a1; Point b = b2-b1; double cos_angle = INT2MM2(dot(a, b)) / vSizeMM(a) / vSizeMM(b); // result == .5*overlap / tan(.5*angle) == .5*overlap / tan(.5*acos(cos_angle)) // [wolfram alpha] == 0.5*overlap * sqrt(cos_angle+1)/sqrt(1-cos_angle) // [assuming positive x] == 0.5*overlap / sqrt( 2 / (cos_angle + 1) - 1 ) if (cos_angle <= 0) { return 0; } else { int64_t dist = overlap * double ( 1.0 / (2.0 * sqrt(2.0 / (cos_angle+1.0) - 1.0)) ); if (dist * dist > vSize2(a) || dist * dist > vSize2(b)) { return 0; DEBUG_PRINTLN("ERROR! overlap end too long!! "); } return dist; } }
void GCodePlanner::forceMinimalLayerTime(double minTime, int minimalSpeed) { Point p0 = gcode.getPositionXY(); double travelTime = 0.0; double extrudeTime = 0.0; for(unsigned int n=0; n<paths.size(); n++) { GCodePath* path = &paths[n]; for(unsigned int i=0; i<path->points.size(); i++) { double thisTime = vSizeMM(p0 - path->points[i]) / double(path->config->speed); if (path->config->lineWidth != 0) extrudeTime += thisTime; else travelTime += thisTime; p0 = path->points[i]; } } double totalTime = extrudeTime + travelTime; if (totalTime < minTime && extrudeTime > 0.0) { double minExtrudeTime = minTime - travelTime; if (minExtrudeTime < 1) minExtrudeTime = 1; double factor = extrudeTime / minExtrudeTime; for(unsigned int n=0; n<paths.size(); n++) { GCodePath* path = &paths[n]; if (path->config->lineWidth == 0) continue; int speed = path->config->speed * factor; if (speed < minimalSpeed) factor = double(minimalSpeed) / double(path->config->speed); } //Only slow down with the minimal time if that will be slower then a factor already set. First layer slowdown also sets the speed factor. if (factor * 100 < getExtrudeSpeedFactor()) setExtrudeSpeedFactor(factor * 100); else factor = getExtrudeSpeedFactor() / 100.0; if (minTime - (extrudeTime / factor) - travelTime > 0.1) { //TODO: Use up this extra time (circle around the print?) this->extraTime = minTime - (extrudeTime / factor) - travelTime; } this->totalPrintTime = (extrudeTime / factor) + travelTime; }else{ this->totalPrintTime = totalTime; } }
void GCodePlanner::getNaiveTimeEstimates(double& travelTime, double& extrudeTime) { travelTime = 0.0; extrudeTime = 0.0; Point p0 = gcode.getPositionXY(); for(unsigned int n=0; n<paths.size(); n++) { GCodePath* path = &paths[n]; for(unsigned int i=0; i<path->points.size(); i++) { double thisTime = vSizeMM(p0 - path->points[i]) / path->config->getSpeed(); if (path->getExtrusionMM3perMM() != 0) extrudeTime += thisTime; else travelTime += thisTime; p0 = path->points[i]; } } }
void GCodeExport::addMove(Point p, int speed, int lineWidth) { if (lineWidth != 0) { Point diff = p - getPositionXY(); if (isRetracted) { if (flavor == GCODE_FLAVOR_ULTIGCODE) { fprintf(f, "G11\n"); }else{ fprintf(f, "G1 F%i E%0.5lf\n", retractionSpeed * 60, extrusionAmount); currentSpeed = retractionSpeed; } 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 * double(lineWidth) / 1000.0 * 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", float(p.X - extruderOffset[extruderNr].X)/1000, float(p.Y - extruderOffset[extruderNr].Y)/1000); if (zPos != currentPosition.z) fprintf(f, " Z%0.2f", float(zPos)/1000); if (lineWidth != 0) fprintf(f, " E%0.5lf", extrusionAmount); fprintf(f, "\n"); currentPosition = Point3(p.X, p.Y, zPos); }
void GCodePlanner::writeGCode(bool liftHeadIfNeeded, int layerThickness) { GCodePathConfig* lastConfig = NULL; int extruder = gcode.getExtruderNr(); for(unsigned int n=0; n<paths.size(); n++) { GCodePath* path = &paths[n]; if (extruder != path->extruder) { extruder = path->extruder; gcode.switchExtruder(extruder); }else if (path->retract) { gcode.addRetraction(); } if (path->config != &travelConfig && lastConfig != path->config) { gcode.addComment("TYPE:%s", path->config->name); lastConfig = path->config; } int speed = path->config->speed; if (path->config->lineWidth != 0)// Only apply the extrudeSpeedFactor to extrusion moves speed = speed * extrudeSpeedFactor / 100; else speed = speed * travelSpeedFactor / 100; if (path->points.size() == 1 && path->config != &travelConfig && shorterThen(gcode.getPositionXY() - path->points[0], path->config->lineWidth * 2)) { //Check for lots of small moves and combine them into one large line Point p0 = path->points[0]; unsigned int i = n + 1; while(i < paths.size() && paths[i].points.size() == 1 && shorterThen(p0 - paths[i].points[0], path->config->lineWidth * 2)) { p0 = paths[i].points[0]; i ++; } if (paths[i-1].config == &travelConfig) i --; if (i > n + 2) { p0 = gcode.getPositionXY(); for(unsigned int x=n; x<i-1; x+=2) { int64_t oldLen = vSize(p0 - paths[x].points[0]); Point newPoint = (paths[x].points[0] + paths[x+1].points[0]) / 2; int64_t newLen = vSize(gcode.getPositionXY() - newPoint); if (newLen > 0) gcode.addMove(newPoint, speed, path->config->lineWidth * oldLen / newLen); p0 = paths[x+1].points[0]; } gcode.addMove(paths[i-1].points[0], speed, path->config->lineWidth); n = i - 1; continue; } } if (path->config->spiralize) { //If we need to spiralize then raise the head slowly by 1 layer as this path progresses. float totalLength = 0.0; int z = gcode.getPositionZ(); Point p0 = gcode.getPositionXY(); for(unsigned int i=0; i<path->points.size(); i++) { Point p1 = path->points[i]; totalLength += vSizeMM(p0 - p1); p0 = p1; } float length = 0.0; p0 = gcode.getPositionXY(); for(unsigned int i=0; i<path->points.size(); i++) { Point p1 = path->points[i]; length += vSizeMM(p0 - p1); p0 = p1; gcode.setZ(z + layerThickness * length / totalLength); gcode.addMove(path->points[i], speed, path->config->lineWidth); } }else{ for(unsigned int i=0; i<path->points.size(); i++) { gcode.addMove(path->points[i], speed, path->config->lineWidth); } } } gcode.totalPrintTime += this->totalPrintTime; if (liftHeadIfNeeded && extraTime > 0.0) { gcode.totalPrintTime += extraTime; gcode.addComment("Small layer, adding delay of %f", extraTime); gcode.addRetraction(); gcode.setZ(gcode.getPositionZ() + 3000); gcode.addMove(gcode.getPositionXY(), travelConfig.speed, 0); gcode.addMove(gcode.getPositionXY() - Point(-20000, 0), travelConfig.speed, 0); gcode.addDelay(extraTime); } }
void GCodePlanner::writeGCode(bool liftHeadIfNeeded, int layerThickness) { GCodePathConfig* last_extrusion_config = nullptr; int extruder = gcode.getExtruderNr(); for(unsigned int path_idx = 0; path_idx < paths.size(); path_idx++) { GCodePath& path = paths[path_idx]; if (extruder != path.extruder) { extruder = path.extruder; gcode.switchExtruder(extruder); }else if (path.retract) { writeRetraction(path_idx); } if (path.config != &travelConfig && last_extrusion_config != path.config) { gcode.writeTypeComment(path.config->name); last_extrusion_config = path.config; } double speed = path.config->getSpeed(); if (path.getExtrusionMM3perMM() != 0)// Only apply the extrudeSpeed to extrusion moves speed *= getExtrudeSpeedFactor(); else speed *= getExtrudeSpeedFactor(); int64_t nozzle_size = 400; // TODO allow the machine settings to be passed on everywhere :: depends on which nozzle! if (MergeInfillLines(gcode, paths, travelConfig, nozzle_size).mergeInfillLines(speed, path_idx)) // !! has effect on path_idx !! { // !! has effect on path_idx !! // works when path_idx is the index of the travel move BEFORE the infill lines to be merged continue; } if (path.config == &travelConfig) { // early comp for travel paths, which are handled more simply for(unsigned int point_idx = 0; point_idx < path.points.size(); point_idx++) { gcode.writeMove(path.points[point_idx], speed, path.getExtrusionMM3perMM()); } continue; } bool spiralize = path.config->spiralize; if (spiralize) { //Check if we are the last spiralize path in the list, if not, do not spiralize. for(unsigned int m=path_idx+1; m<paths.size(); m++) { if (paths[m].config->spiralize) spiralize = false; } } if (!spiralize) // normal (extrusion) move (with coasting { CoastingConfig& coasting_config = storage.coasting_config[extruder]; bool coasting = coasting_config.coasting_enable; if (coasting) { coasting = writePathWithCoasting(path_idx, layerThickness , coasting_config.coasting_volume_move, coasting_config.coasting_speed_move, coasting_config.coasting_min_volume_move , coasting_config.coasting_volume_retract, coasting_config.coasting_speed_retract, coasting_config.coasting_min_volume_retract); } if (! coasting) // not same as 'else', cause we might have changed coasting in the line above... { // normal path to gcode algorithm if ( // change |||||| to /\/\/\/\/ ... false && path_idx + 2 < paths.size() // has a next move && paths[path_idx+1].points.size() == 1 // is single extruded line && paths[path_idx+1].config != &travelConfig // next move is extrusion && paths[path_idx+2].config == &travelConfig // next next move is travel && shorterThen(path.points.back() - gcode.getPositionXY(), 2 * nozzle_size) // preceding extrusion is close by && shorterThen(paths[path_idx+1].points.back() - path.points.back(), 2 * nozzle_size) // extrusion move is small && shorterThen(paths[path_idx+2].points.back() - paths[path_idx+1].points.back(), 2 * nozzle_size) // consecutive extrusion is close by ) { gcode.writeMove(paths[path_idx+2].points.back(), speed, paths[path_idx+1].getExtrusionMM3perMM()); path_idx += 2; } else { for(unsigned int point_idx = 0; point_idx < path.points.size(); point_idx++) { gcode.writeMove(path.points[point_idx], speed, path.getExtrusionMM3perMM()); } } } } else { // SPIRALIZE //If we need to spiralize then raise the head slowly by 1 layer as this path progresses. float totalLength = 0.0; int z = gcode.getPositionZ(); Point p0 = gcode.getPositionXY(); for(unsigned int i=0; i<path.points.size(); i++) { Point p1 = path.points[i]; totalLength += vSizeMM(p0 - p1); p0 = p1; } float length = 0.0; p0 = gcode.getPositionXY(); for(unsigned int point_idx = 0; point_idx < path.points.size(); point_idx++) { Point p1 = path.points[point_idx]; length += vSizeMM(p0 - p1); p0 = p1; gcode.setZ(z + layerThickness * length / totalLength); gcode.writeMove(path.points[point_idx], speed, path.getExtrusionMM3perMM()); } } } gcode.updateTotalPrintTime(); if (liftHeadIfNeeded && extraTime > 0.0) { gcode.writeComment("Small layer, adding delay"); if (last_extrusion_config) { bool extruder_switch_retract = false;// TODO: check whether we should do a retractoin_extruderSwitch; is the next path with a different extruder? writeRetraction(extruder_switch_retract, last_extrusion_config->retraction_config); } gcode.setZ(gcode.getPositionZ() + MM2INT(3.0)); gcode.writeMove(gcode.getPositionXY(), travelConfig.getSpeed(), 0); gcode.writeMove(gcode.getPositionXY() - Point(-MM2INT(20.0), 0), travelConfig.getSpeed(), 0); gcode.writeDelay(extraTime); } }
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); }