void GCodePlanner::addTravel(Point p) { GCodePath* path = getLatestPathWithConfig(&travelConfig); if (forceRetraction) { if (!shorterThen(lastPosition - p, retractionMinimalDistance)) { path->retract = true; } forceRetraction = false; }else if (comb != NULL) { vector<Point> pointList; if (comb->calc(lastPosition, p, pointList)) { for(unsigned int n=0; n<pointList.size(); n++) { path->points.push_back(pointList[n]); } }else{ if (!shorterThen(lastPosition - p, retractionMinimalDistance)) path->retract = true; } }else if (alwaysRetract) { if (!shorterThen(lastPosition - p, retractionMinimalDistance)) path->retract = true; } path->points.push_back(p); lastPosition = p; }
void optimizePolygon(ClipperLib::Polygon& poly) { Point p0 = poly[poly.size()-1]; for(unsigned int i=0;i<poly.size();i++) { Point p1 = poly[i]; if (shorterThen(p0 - p1, 10)) { poly.erase(poly.begin() + i); i --; }else{ Point p2; if (i < poly.size() - 1) p2 = poly[i+1]; else p2 = poly[0]; Point diff0 = normal(p1 - p0, 1000000); Point diff2 = normal(p1 - p2, 1000000); int64_t d = dot(diff0, diff2); if (d < -999999000000LL) { poly.erase(poly.begin() + i); i --; }else{ p0 = p1; } } } }
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 optimizePolygon(PolygonRef poly) { Point p0 = poly[poly.size()-1]; for(unsigned int i=0;i<poly.size();i++) { Point p1 = poly[i]; if (shorterThen(p0 - p1, MICRON2INT(10))) { poly.remove(i); i --; }else{ Point p2; if (i < poly.size() - 1) p2 = poly[i+1]; else p2 = poly[0]; Point diff0 = normal(p1 - p0, 1000000); Point diff2 = normal(p1 - p2, 1000000); int64_t d = dot(diff0, diff2); if (d < -999999999999LL) { poly.remove(i); i --; }else{ p0 = p1; } } } }
bool Comb::calc(Point startPoint, Point endPoint, std::vector<std::vector<Point>>& combPaths) { if (shorterThen(endPoint - startPoint, MM2INT(1.5))) { // DEBUG_PRINTLN("too short dist!"); return true; } bool addEndpoint = false; //Check if we are inside the comb boundaries if (!boundary.inside(startPoint)) { if (!moveInside(&startPoint)) //If we fail to move the point inside the comb boundary we need to retract. { // std::cerr << " fail to move the start point inside the comb boundary we need to retract."<< std::endl; return false; } combPaths.emplace_back(); combPaths.back().push_back(startPoint); } if (!boundary.inside(endPoint)) { if (!moveInside(&endPoint)) //If we fail to move the point inside the comb boundary we need to retract. { // std::cerr << " fail to move the end point inside the comb boundary we need to retract."<< std::endl; return false; } addEndpoint = true; } //Check if we are crossing any boundaries, and pre-calculate some values. if (!lineSegmentCollidesWithBoundary(startPoint, endPoint)) { //We're not crossing any boundaries. So skip the comb generation. if (!addEndpoint && combPaths.size() == 0) //Only skip if we didn't move the start and end point. return true; } // std::cerr << "calcuklating comb path!" << std::endl; //Calculate the minimum and maximum positions where we cross the comb boundary calcMinMax(); std::vector<std::vector<Point>> basicCombPaths; getBasicCombingPaths(endPoint, basicCombPaths); bool succeeded = optimizePaths(startPoint, basicCombPaths, combPaths); if (addEndpoint) combPaths.back().push_back(endPoint); // std::cerr << "succeeded = " << succeeded << std::endl; return succeeded; }
void LinePolygonsCrossings::getCombingPath(CombPath& combPath) { if (shorterThen(endPoint - startPoint, Comb::max_comb_distance_ignored) || !lineSegmentCollidesWithBoundary()) { //We're not crossing any boundaries. So skip the comb generation. combPath.push_back(startPoint); combPath.push_back(endPoint); return; } calcScanlineCrossings(); CombPath basicPath; getBasicCombingPath(basicPath); optimizePath(basicPath, combPath); }
void GCodePlanner::addTravel_simple(Point p, GCodePath* path) { if (alwaysRetract) { path = getLatestPathWithConfig(&travelConfig); if (!shorterThen(lastPosition - p, last_retraction_config->retraction_min_travel_distance)) { path->retract = true; } } if (path == nullptr) { path = getLatestPathWithConfig(&travelConfig); } path->points.push_back(p); lastPosition = p; }
bool LinePolygonsCrossings::getCombingPath(CombPath& combPath, int64_t max_comb_distance_ignored, bool fail_on_unavoidable_obstacles) { if (shorterThen(endPoint - startPoint, max_comb_distance_ignored) || !lineSegmentCollidesWithBoundary()) { //We're not crossing any boundaries. So skip the comb generation. combPath.push_back(startPoint); combPath.push_back(endPoint); return true; } bool success = calcScanlineCrossings(fail_on_unavoidable_obstacles); if (!success) { return false; } CombPath basicPath; getBasicCombingPath(basicPath); optimizePath(basicPath, combPath); // combPath = basicPath; // uncomment to disable comb path optimization return true; }
bool Comb::calc(Point startPoint, Point endPoint, std::vector<Point>& combPoints) { if (shorterThen(endPoint - startPoint, MM2INT(1.5))) return true; bool addEndpoint = false; //Check if we are inside the comb boundaries if (!boundary.inside(startPoint)) { if (!moveInside(&startPoint)) //If we fail to move the point inside the comb boundary we need to retract. return false; combPoints.push_back(startPoint); } if (!boundary.inside(endPoint)) { if (!moveInside(&endPoint)) //If we fail to move the point inside the comb boundary we need to retract. return false; addEndpoint = true; } //Check if we are crossing any boundaries, and pre-calculate some values. if (!lineSegmentCollidesWithBoundary(startPoint, endPoint)) { //We're not crossing any boundaries. So skip the comb generation. if (!addEndpoint && combPoints.size() == 0) //Only skip if we didn't move the start and end point. return true; } //Calculate the minimum and maximum positions where we cross the comb boundary calcMinMax(); std::vector<Point> pointList; getBasicCombingPath(endPoint, pointList); bool succeeded = optimizePath(startPoint, pointList, combPoints); if (addEndpoint) combPoints.push_back(endPoint); return succeeded; }
bool Comb::calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool _startInside, bool _endInside, int64_t max_comb_distance_ignored, bool via_outside_makes_combing_fail, bool fail_on_unavoidable_obstacles) { if (shorterThen(endPoint - startPoint, max_comb_distance_ignored)) { return true; } //Move start and end point inside the comb boundary unsigned int start_inside_poly = NO_INDEX; const bool startInside = moveInside(_startInside, startPoint, start_inside_poly); unsigned int end_inside_poly = NO_INDEX; const bool endInside = moveInside(_endInside, endPoint, end_inside_poly); unsigned int start_part_boundary_poly_idx; unsigned int end_part_boundary_poly_idx; unsigned int start_part_idx = (start_inside_poly == NO_INDEX)? NO_INDEX : partsView_inside.getPartContaining(start_inside_poly, &start_part_boundary_poly_idx); unsigned int end_part_idx = (end_inside_poly == NO_INDEX)? NO_INDEX : partsView_inside.getPartContaining(end_inside_poly, &end_part_boundary_poly_idx); if (startInside && endInside && start_part_idx == end_part_idx) { // normal combing within part PolygonsPart part = partsView_inside.assemblePart(start_part_idx); combPaths.emplace_back(); return LinePolygonsCrossings::comb(part, *inside_loc_to_line, startPoint, endPoint, combPaths.back(), -offset_dist_to_get_from_on_the_polygon_to_outside, max_comb_distance_ignored, fail_on_unavoidable_obstacles); } else { // comb inside part to edge (if needed) >> move through air avoiding other parts >> comb inside end part upto the endpoint (if needed) // INSIDE | in_between | OUTSIDE | in_between | INSIDE // ^crossing_1_in ^crossing_1_mid ^crossing_1_out ^crossing_2_out ^crossing_2_mid ^crossing_2_in // // when startPoint is inside crossing_1_in is of interest // when it is in between inside and outside it is equal to crossing_1_mid if (via_outside_makes_combing_fail) { return false; } Crossing start_crossing(startPoint, startInside, start_part_idx, start_part_boundary_poly_idx, boundary_inside, inside_loc_to_line); Crossing end_crossing(endPoint, endInside, end_part_idx, end_part_boundary_poly_idx, boundary_inside, inside_loc_to_line); { // find crossing over the in-between area between inside and outside start_crossing.findCrossingInOrMid(partsView_inside, endPoint); end_crossing.findCrossingInOrMid(partsView_inside, start_crossing.in_or_mid); } bool skip_avoid_other_parts_path = false; if (skip_avoid_other_parts_path && vSize2(start_crossing.in_or_mid - end_crossing.in_or_mid) < offset_from_inside_to_outside * offset_from_inside_to_outside * 4) { // parts are next to eachother, i.e. the direct crossing will always be smaller than two crossings via outside skip_avoid_other_parts_path = true; } if (avoid_other_parts && !skip_avoid_other_parts_path) { // compute the crossing points when moving through air // comb through all air, since generally the outside consists of a single part bool success = start_crossing.findOutside(*boundary_outside, end_crossing.in_or_mid, fail_on_unavoidable_obstacles, *this); if (!success) { return false; } success = end_crossing.findOutside(*boundary_outside, start_crossing.out, fail_on_unavoidable_obstacles, *this); if (!success) { return false; } } // generate the actual comb paths if (startInside) { // start to boundary assert(start_crossing.dest_part.size() > 0 && "The part we start inside when combing should have been computed already!"); combPaths.emplace_back(); bool combing_succeeded = LinePolygonsCrossings::comb(start_crossing.dest_part, *inside_loc_to_line, startPoint, start_crossing.in_or_mid, combPaths.back(), -offset_dist_to_get_from_on_the_polygon_to_outside, max_comb_distance_ignored, fail_on_unavoidable_obstacles); if (!combing_succeeded) { // Couldn't comb between start point and computed crossing from the start part! Happens for very thin parts when the offset_to_get_off_boundary moves points to outside the polygon return false; } } // throught air from boundary to boundary if (avoid_other_parts && !skip_avoid_other_parts_path) { combPaths.emplace_back(); combPaths.throughAir = true; if ( vSize(start_crossing.in_or_mid - end_crossing.in_or_mid) < vSize(start_crossing.in_or_mid - start_crossing.out) + vSize(end_crossing.in_or_mid - end_crossing.out) ) { // via outside is moving more over the in-between zone combPaths.back().push_back(start_crossing.in_or_mid); combPaths.back().push_back(end_crossing.in_or_mid); } else { bool combing_succeeded = LinePolygonsCrossings::comb(*boundary_outside, *outside_loc_to_line, start_crossing.out, end_crossing.out, combPaths.back(), offset_dist_to_get_from_on_the_polygon_to_outside, max_comb_distance_ignored, fail_on_unavoidable_obstacles); if (!combing_succeeded) { return false; } } } else { // directly through air (not avoiding other parts) combPaths.emplace_back(); combPaths.throughAir = true; combPaths.back().cross_boundary = true; // note: we don't actually know whether this is cross boundary, but it might very well be combPaths.back().push_back(start_crossing.in_or_mid); combPaths.back().push_back(end_crossing.in_or_mid); } if (skip_avoid_other_parts_path) { if (startInside == endInside && start_part_idx == end_part_idx) { if (startInside) { // both start and end are inside combPaths.back().cross_boundary = PolygonUtils::polygonCollidesWithLineSegment(startPoint, endPoint, *inside_loc_to_line); } else { // both start and end are outside combPaths.back().cross_boundary = PolygonUtils::polygonCollidesWithLineSegment(startPoint, endPoint, *outside_loc_to_line); } } else { combPaths.back().cross_boundary = true; } } if (endInside) { // boundary to end assert(end_crossing.dest_part.size() > 0 && "The part we end up inside when combing should have been computed already!"); combPaths.emplace_back(); bool combing_succeeded = LinePolygonsCrossings::comb(end_crossing.dest_part, *inside_loc_to_line, end_crossing.in_or_mid, endPoint, combPaths.back(), -offset_dist_to_get_from_on_the_polygon_to_outside, max_comb_distance_ignored, fail_on_unavoidable_obstacles); if (!combing_succeeded) { // Couldn't comb between end point and computed crossing to the end part! Happens for very thin parts when the offset_to_get_off_boundary moves points to outside the polygon return false; } } return true; } }
bool Comb::calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool startInside, bool endInside, int64_t max_comb_distance_ignored) { if (shorterThen(endPoint - startPoint, max_comb_distance_ignored)) { return true; } //Move start and end point inside the comb boundary unsigned int start_inside_poly = NO_INDEX; if (startInside) { start_inside_poly = PolygonUtils::moveInside(boundary_inside, startPoint, offset_extra_start_end, max_moveInside_distance2); if (!boundary_inside.inside(start_inside_poly) || start_inside_poly == NO_INDEX) { if (start_inside_poly != NO_INDEX) { // if not yet inside because of overshoot, try again start_inside_poly = PolygonUtils::moveInside(boundary_inside, startPoint, offset_extra_start_end, max_moveInside_distance2); } if (start_inside_poly == NO_INDEX) //If we fail to move the point inside the comb boundary we need to retract. { startInside = false; } } } unsigned int end_inside_poly = NO_INDEX; if (endInside) { end_inside_poly = PolygonUtils::moveInside(boundary_inside, endPoint, offset_extra_start_end, max_moveInside_distance2); if (!boundary_inside.inside(endPoint) || end_inside_poly == NO_INDEX) { if (end_inside_poly != NO_INDEX) { // if not yet inside because of overshoot, try again end_inside_poly = PolygonUtils::moveInside(boundary_inside, endPoint, offset_extra_start_end, max_moveInside_distance2); } if (end_inside_poly == NO_INDEX) //If we fail to move the point inside the comb boundary we need to retract. { endInside = false; } } } unsigned int start_part_boundary_poly_idx; unsigned int end_part_boundary_poly_idx; unsigned int start_part_idx = (start_inside_poly == NO_INDEX)? NO_INDEX : partsView_inside.getPartContaining(start_inside_poly, &start_part_boundary_poly_idx); unsigned int end_part_idx = (end_inside_poly == NO_INDEX)? NO_INDEX : partsView_inside.getPartContaining(end_inside_poly, &end_part_boundary_poly_idx); if (startInside && endInside && start_part_idx == end_part_idx) { // normal combing within part PolygonsPart part = partsView_inside.assemblePart(start_part_idx); combPaths.emplace_back(); LinePolygonsCrossings::comb(part, startPoint, endPoint, combPaths.back(), -offset_dist_to_get_from_on_the_polygon_to_outside, max_comb_distance_ignored); return true; } else { // comb inside part to edge (if needed) >> move through air avoiding other parts >> comb inside end part upto the endpoint (if needed) // INSIDE | in_between | OUTSIDE | in_between | INSIDE // ^crossing_1_in ^crossing_1_mid ^crossing_1_out ^crossing_2_out ^crossing_2_mid ^crossing_2_in // // when startPoint is inside crossing_1_in is of interest // when it is in between inside and outside it is equal to crossing_1_mid Point crossing_1_in_or_mid; // the point inside the starting polygon if startPoint is inside or the startPoint itself if it is not inside Point crossing_1_out; Point crossing_2_in_or_mid; // the point inside the ending polygon if endPoint is inside or the endPoint itself if it is not inside Point crossing_2_out; { // find crossing over the in-between area between inside and outside if (startInside) { ClosestPolygonPoint crossing_1_in_cp = PolygonUtils::findClosest(endPoint, boundary_inside[start_part_boundary_poly_idx]); crossing_1_in_or_mid = PolygonUtils::moveInside(crossing_1_in_cp, offset_dist_to_get_from_on_the_polygon_to_outside); // in-case } else { crossing_1_in_or_mid = startPoint; // mid-case } if (endInside) { ClosestPolygonPoint crossing_2_in_cp = PolygonUtils::findClosest(crossing_1_in_or_mid, boundary_inside[end_part_boundary_poly_idx]); crossing_2_in_or_mid = PolygonUtils::moveInside(crossing_2_in_cp, offset_dist_to_get_from_on_the_polygon_to_outside); // in-case } else { crossing_2_in_or_mid = endPoint; // mid-case } } bool avoid_other_parts_now = avoid_other_parts; if (avoid_other_parts_now && vSize2(crossing_1_in_or_mid - crossing_2_in_or_mid) < offset_from_outlines_outside * offset_from_outlines_outside * 4) { // parts are next to eachother, i.e. the direct crossing will always be smaller than two crossings via outside avoid_other_parts_now = false; } if (avoid_other_parts_now) { // compute the crossing points when moving through air Polygons& outside = getBoundaryOutside(); // comb through all air, since generally the outside consists of a single part crossing_1_out = crossing_1_in_or_mid; if (startInside || outside.inside(crossing_1_in_or_mid, true)) // start in_between { // move outside ClosestPolygonPoint* crossing_1_out_cpp = PolygonUtils::findClose(crossing_1_in_or_mid, outside, getOutsideLocToLine()); if (crossing_1_out_cpp) { crossing_1_out = PolygonUtils::moveOutside(*crossing_1_out_cpp, offset_dist_to_get_from_on_the_polygon_to_outside); } else { PolygonUtils::moveOutside(outside, crossing_1_out, offset_dist_to_get_from_on_the_polygon_to_outside); } } int64_t in_out_dist2_1 = vSize2(crossing_1_out - crossing_1_in_or_mid); if (startInside && in_out_dist2_1 > max_crossing_dist2) // moveInside moved too far { // if move is to far over in_between // find crossing closer by std::shared_ptr<std::pair<ClosestPolygonPoint, ClosestPolygonPoint>> best = findBestCrossing(boundary_inside[start_part_boundary_poly_idx], startPoint, endPoint); if (best) { crossing_1_in_or_mid = PolygonUtils::moveInside(best->first, offset_dist_to_get_from_on_the_polygon_to_outside); crossing_1_out = PolygonUtils::moveOutside(best->second, offset_dist_to_get_from_on_the_polygon_to_outside); } } crossing_2_out = crossing_2_in_or_mid; if (endInside || outside.inside(crossing_2_in_or_mid, true)) { // move outside ClosestPolygonPoint* crossing_2_out_cpp = PolygonUtils::findClose(crossing_2_in_or_mid, outside, getOutsideLocToLine()); if (crossing_2_out_cpp) { crossing_2_out = PolygonUtils::moveOutside(*crossing_2_out_cpp, offset_dist_to_get_from_on_the_polygon_to_outside); } else { PolygonUtils::moveOutside(outside, crossing_2_out, offset_dist_to_get_from_on_the_polygon_to_outside); } } int64_t in_out_dist2_2 = vSize2(crossing_2_out - crossing_2_in_or_mid); if (endInside && in_out_dist2_2 > max_crossing_dist2) // moveInside moved too far { // if move is to far over in_between // find crossing closer by std::shared_ptr<std::pair<ClosestPolygonPoint, ClosestPolygonPoint>> best = findBestCrossing(boundary_inside[end_part_boundary_poly_idx], endPoint, crossing_1_out); if (best) { crossing_2_in_or_mid = PolygonUtils::moveInside(best->first, offset_dist_to_get_from_on_the_polygon_to_outside); crossing_2_out = PolygonUtils::moveOutside(best->second, offset_dist_to_get_from_on_the_polygon_to_outside); } } } if (startInside) { // start to boundary PolygonsPart part_begin = partsView_inside.assemblePart(start_part_idx); // comb through the starting part only combPaths.emplace_back(); LinePolygonsCrossings::comb(part_begin, startPoint, crossing_1_in_or_mid, combPaths.back(), -offset_dist_to_get_from_on_the_polygon_to_outside, max_comb_distance_ignored); } // throught air from boundary to boundary if (avoid_other_parts_now) { combPaths.emplace_back(); combPaths.throughAir = true; if ( vSize(crossing_1_in_or_mid - crossing_2_in_or_mid) < vSize(crossing_1_in_or_mid - crossing_1_out) + vSize(crossing_2_in_or_mid - crossing_2_out) ) { // via outside is moving more over the in-between zone combPaths.back().push_back(crossing_1_in_or_mid); combPaths.back().push_back(crossing_2_in_or_mid); } else { LinePolygonsCrossings::comb(getBoundaryOutside(), crossing_1_out, crossing_2_out, combPaths.back(), offset_dist_to_get_from_on_the_polygon_to_outside, max_comb_distance_ignored); } } else { // directly through air (not avoiding other parts) combPaths.emplace_back(); combPaths.throughAir = true; combPaths.back().cross_boundary = true; // TODO: calculate whether we cross a boundary! combPaths.back().push_back(crossing_1_in_or_mid); combPaths.back().push_back(crossing_2_in_or_mid); } if (endInside) { // boundary to end PolygonsPart part_end = partsView_inside.assemblePart(end_part_idx); // comb through end part only combPaths.emplace_back(); LinePolygonsCrossings::comb(part_end, crossing_2_in_or_mid, endPoint, combPaths.back(), -offset_dist_to_get_from_on_the_polygon_to_outside, max_comb_distance_ignored); } return true; } }
bool getNextPointWithDistance(Point from, int64_t dist, const PolygonRef poly, int start_idx, int poly_start_idx, GivenDistPoint& result) { Point prev_poly_point = poly[(start_idx + poly_start_idx) % poly.size()]; for (unsigned int prev_idx = start_idx; prev_idx < poly.size(); prev_idx++) { int next_idx = (prev_idx + 1 + poly_start_idx) % poly.size(); // last checked segment is between last point in poly and poly[0]... Point& next_poly_point = poly[next_idx]; if ( !shorterThen(next_poly_point - from, dist) ) { /* * x r * p.---------+---+------------.n * L| / * | / dist * |/ * f. * * f=from * p=prev_poly_point * n=next_poly_point * x= f projected on pn * r=result point at distance [dist] from f */ Point pn = next_poly_point - prev_poly_point; if (shorterThen(pn, 100)) // when precision is limited { Point middle = (next_poly_point + prev_poly_point) / 2; int64_t dist_to_middle = vSize(from - middle); if (dist_to_middle - dist < 100 && dist_to_middle - dist > -100) { result.location = middle; result.pos = prev_idx; return true; } else { prev_poly_point = next_poly_point; continue; } } Point pf = from - prev_poly_point; Point px = dot(pf, pn) / vSize(pn) * pn / vSize(pn); Point xf = pf - px; if (!shorterThen(xf, dist)) // line lies wholly further than pn { prev_poly_point = next_poly_point; continue; } int64_t xr_dist = std::sqrt(dist*dist - vSize2(xf)); // inverse Pythagoras if (vSize(pn - px) - xr_dist < 1) // r lies beyond n { prev_poly_point = next_poly_point; continue; } Point xr = xr_dist * pn / vSize(pn); Point pr = px + xr; result.location = prev_poly_point + pr; result.pos = prev_idx; return true; } prev_poly_point = next_poly_point; } return false; }
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 SlicerLayer::makePolygons(Mesh* mesh, bool keep_none_closed, bool extensive_stitching) { Polygons openPolygonList; for(unsigned int startSegment=0; startSegment < segmentList.size(); startSegment++) { if (segmentList[startSegment].addedToPolygon) continue; Polygon poly; poly.add(segmentList[startSegment].start); unsigned int segmentIndex = startSegment; bool canClose; while(true) { canClose = false; segmentList[segmentIndex].addedToPolygon = true; Point p0 = segmentList[segmentIndex].end; poly.add(p0); int nextIndex = -1; const MeshFace& face = mesh->faces[segmentList[segmentIndex].faceIndex]; for(unsigned int i=0;i<3;i++) { decltype(face_idx_to_segment_index.begin()) it; if (face.connected_face_index[i] > -1 && (it = face_idx_to_segment_index.find(face.connected_face_index[i])) != face_idx_to_segment_index.end()) { int index = (*it).second; Point p1 = segmentList[index].start; Point diff = p0 - p1; if (shorterThen(diff, MM2INT(0.01))) { if (index == static_cast<int>(startSegment)) canClose = true; if (segmentList[index].addedToPolygon) continue; nextIndex = index; } } } if (nextIndex == -1) break; segmentIndex = nextIndex; } if (canClose) polygonList.add(poly); else openPolygonList.add(poly); } //Clear the segmentList to save memory, it is no longer needed after this point. segmentList.clear(); //Connecting polygons that are not closed yet, as models are not always perfect manifold we need to join some stuff up to get proper polygons //First link up polygon ends that are within 2 microns. for(unsigned int i=0;i<openPolygonList.size();i++) { if (openPolygonList[i].size() < 1) continue; for(unsigned int j=0;j<openPolygonList.size();j++) { if (openPolygonList[j].size() < 1) continue; Point diff = openPolygonList[i][openPolygonList[i].size()-1] - openPolygonList[j][0]; int64_t distSquared = vSize2(diff); if (distSquared < MM2INT(0.02) * MM2INT(0.02)) { if (i == j) { polygonList.add(openPolygonList[i]); openPolygonList[i].clear(); break; }else{ for(unsigned int n=0; n<openPolygonList[j].size(); n++) openPolygonList[i].add(openPolygonList[j][n]); openPolygonList[j].clear(); } } } } //Next link up all the missing ends, closing up the smallest gaps first. This is an inefficient implementation which can run in O(n*n*n) time. while(1) { int64_t bestScore = MM2INT(10.0) * MM2INT(10.0); unsigned int bestA = -1; unsigned int bestB = -1; bool reversed = false; for(unsigned int i=0;i<openPolygonList.size();i++) { if (openPolygonList[i].size() < 1) continue; for(unsigned int j=0;j<openPolygonList.size();j++) { if (openPolygonList[j].size() < 1) continue; Point diff = openPolygonList[i][openPolygonList[i].size()-1] - openPolygonList[j][0]; int64_t distSquared = vSize2(diff); if (distSquared < bestScore) { bestScore = distSquared; bestA = i; bestB = j; reversed = false; } if (i != j) { Point diff = openPolygonList[i][openPolygonList[i].size()-1] - openPolygonList[j][openPolygonList[j].size()-1]; int64_t distSquared = vSize2(diff); if (distSquared < bestScore) { bestScore = distSquared; bestA = i; bestB = j; reversed = true; } } } } if (bestScore >= MM2INT(10.0) * MM2INT(10.0)) break; if (bestA == bestB) { polygonList.add(openPolygonList[bestA]); openPolygonList[bestA].clear(); }else{ if (reversed) { if (openPolygonList[bestA].polygonLength() > openPolygonList[bestB].polygonLength()) { for(unsigned int n=openPolygonList[bestB].size()-1; int(n)>=0; n--) openPolygonList[bestA].add(openPolygonList[bestB][n]); openPolygonList[bestB].clear(); }else{ for(unsigned int n=openPolygonList[bestA].size()-1; int(n)>=0; n--) openPolygonList[bestB].add(openPolygonList[bestA][n]); openPolygonList[bestA].clear(); } }else{ for(unsigned int n=0; n<openPolygonList[bestB].size(); n++) openPolygonList[bestA].add(openPolygonList[bestB][n]); openPolygonList[bestB].clear(); } } } if (extensive_stitching) { //For extensive stitching find 2 open polygons that are touching 2 closed polygons. // Then find the sortest path over this polygon that can be used to connect the open polygons, // And generate a path over this shortest bit to link up the 2 open polygons. // (If these 2 open polygons are the same polygon, then the final result is a closed polyon) while(1) { unsigned int bestA = -1; unsigned int bestB = -1; gapCloserResult bestResult; bestResult.len = POINT_MAX; bestResult.polygonIdx = -1; bestResult.pointIdxA = -1; bestResult.pointIdxB = -1; for(unsigned int i=0; i<openPolygonList.size(); i++) { if (openPolygonList[i].size() < 1) continue; { gapCloserResult res = findPolygonGapCloser(openPolygonList[i][0], openPolygonList[i][openPolygonList[i].size()-1]); if (res.len > 0 && res.len < bestResult.len) { bestA = i; bestB = i; bestResult = res; } } for(unsigned int j=0; j<openPolygonList.size(); j++) { if (openPolygonList[j].size() < 1 || i == j) continue; gapCloserResult res = findPolygonGapCloser(openPolygonList[i][0], openPolygonList[j][openPolygonList[j].size()-1]); if (res.len > 0 && res.len < bestResult.len) { bestA = i; bestB = j; bestResult = res; } } } if (bestResult.len < POINT_MAX) { if (bestA == bestB) { if (bestResult.pointIdxA == bestResult.pointIdxB) { polygonList.add(openPolygonList[bestA]); openPolygonList[bestA].clear(); } else if (bestResult.AtoB) { PolygonRef poly = polygonList.newPoly(); for(unsigned int j = bestResult.pointIdxA; j != bestResult.pointIdxB; j = (j + 1) % polygonList[bestResult.polygonIdx].size()) poly.add(polygonList[bestResult.polygonIdx][j]); for(unsigned int j = openPolygonList[bestA].size() - 1; int(j) >= 0; j--) poly.add(openPolygonList[bestA][j]); openPolygonList[bestA].clear(); } else { unsigned int n = polygonList.size(); polygonList.add(openPolygonList[bestA]); for(unsigned int j = bestResult.pointIdxB; j != bestResult.pointIdxA; j = (j + 1) % polygonList[bestResult.polygonIdx].size()) polygonList[n].add(polygonList[bestResult.polygonIdx][j]); openPolygonList[bestA].clear(); } } else { if (bestResult.pointIdxA == bestResult.pointIdxB) { for(unsigned int n=0; n<openPolygonList[bestA].size(); n++) openPolygonList[bestB].add(openPolygonList[bestA][n]); openPolygonList[bestA].clear(); } else if (bestResult.AtoB) { Polygon poly; for(unsigned int n = bestResult.pointIdxA; n != bestResult.pointIdxB; n = (n + 1) % polygonList[bestResult.polygonIdx].size()) poly.add(polygonList[bestResult.polygonIdx][n]); for(unsigned int n=poly.size()-1;int(n) >= 0; n--) openPolygonList[bestB].add(poly[n]); for(unsigned int n=0; n<openPolygonList[bestA].size(); n++) openPolygonList[bestB].add(openPolygonList[bestA][n]); openPolygonList[bestA].clear(); } else { for(unsigned int n = bestResult.pointIdxB; n != bestResult.pointIdxA; n = (n + 1) % polygonList[bestResult.polygonIdx].size()) openPolygonList[bestB].add(polygonList[bestResult.polygonIdx][n]); for(unsigned int n = openPolygonList[bestA].size() - 1; int(n) >= 0; n--) openPolygonList[bestB].add(openPolygonList[bestA][n]); openPolygonList[bestA].clear(); } } } else { break; } } } if (keep_none_closed) { for(unsigned int n=0; n<openPolygonList.size(); n++) { if (openPolygonList[n].size() > 0) polygonList.add(openPolygonList[n]); } } for(unsigned int i=0;i<openPolygonList.size();i++) { if (openPolygonList[i].size() > 0) openPolygons.newPoly() = openPolygonList[i]; } //Remove all the tiny polygons, or polygons that are not closed. As they do not contribute to the actual print. int snapDistance = MM2INT(1.0); for(unsigned int i=0;i<polygonList.size();i++) { int length = 0; for(unsigned int n=1; n<polygonList[i].size(); n++) { length += vSize(polygonList[i][n] - polygonList[i][n-1]); if (length > snapDistance) break; } if (length < snapDistance) { polygonList.remove(i); i--; } } //Finally optimize all the polygons. Every point removed saves time in the long run. optimizePolygons(polygonList); int xy_offset = mesh->getSettingInMicrons("xy_offset"); if (xy_offset != 0) { polygonList = polygonList.offset(xy_offset); } }
bool Comb::calc(Point startPoint, Point endPoint, vector<Point>& combPoints) { if (shorterThen(endPoint - startPoint, MM2INT(1.5))) return true; bool addEndpoint = false; //Check if we are inside the comb boundaries if (!checkInside(startPoint)) { if (!moveInside(&startPoint)) //If we fail to move the point inside the comb boundary we need to retract. return false; combPoints.push_back(startPoint); } if (!checkInside(endPoint)) { if (!moveInside(&endPoint)) //If we fail to move the point inside the comb boundary we need to retract. return false; addEndpoint = true; } //Check if we are crossing any bounderies, and pre-calculate some values. if (!preTest(startPoint, endPoint)) { //We're not crossing any boundaries. So skip the comb generation. if (!addEndpoint && combPoints.size() == 0) //Only skip if we didn't move the start and end point. return true; } //Calculate the minimum and maximum positions where we cross the comb boundary calcMinMax(); int64_t x = sp.X; vector<Point> pointList; //Now walk trough the crossings, for every boundary we cross, find the initial cross point and the exit point. Then add all the points in between // to the pointList and continue with the next boundary we will cross, until there are no more boundaries to cross. // This gives a path from the start to finish curved around the holes that it encounters. while(true) { unsigned int n = getPolygonAbove(x); if (n == UINT_MAX) break; pointList.push_back(matrix.unapply(Point(minX[n] - MM2INT(0.2), sp.Y))); if ( (minIdx[n] - maxIdx[n] + boundery[n].size()) % boundery[n].size() > (maxIdx[n] - minIdx[n] + boundery[n].size()) % boundery[n].size()) { for(unsigned int i=minIdx[n]; i != maxIdx[n]; i = (i < boundery[n].size() - 1) ? (i + 1) : (0)) { pointList.push_back(getBounderyPointWithOffset(n, i)); } }else{ minIdx[n]--; if (minIdx[n] == UINT_MAX) minIdx[n] = boundery[n].size() - 1; maxIdx[n]--; if (maxIdx[n] == UINT_MAX) maxIdx[n] = boundery[n].size() - 1; for(unsigned int i=minIdx[n]; i != maxIdx[n]; i = (i > 0) ? (i - 1) : (boundery[n].size() - 1)) { pointList.push_back(getBounderyPointWithOffset(n, i)); } } pointList.push_back(matrix.unapply(Point(maxX[n] + MM2INT(0.2), sp.Y))); x = maxX[n]; } pointList.push_back(endPoint); //Optimize the pointList, skip each point we could already reach by not crossing a boundary. This smooths out the path and makes it skip any unneeded corners. Point p0 = startPoint; for(unsigned int n=1; n<pointList.size(); n++) { if (collisionTest(p0, pointList[n])) { if (collisionTest(p0, pointList[n-1])) return false; p0 = pointList[n-1]; combPoints.push_back(p0); } } if (addEndpoint) combPoints.push_back(endPoint); return true; }
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); } }
bool Comb::calc(Point startPoint, Point endPoint, CombPaths& combPaths) { if (shorterThen(endPoint - startPoint, max_comb_distance_ignored)) { return true; } bool startInside = true; bool endInside = true; //Move start and end point inside the comb boundary unsigned int start_inside_poly = boundary_inside.findInside(startPoint, true); if (start_inside_poly == NO_INDEX) { start_inside_poly = moveInside(boundary_inside, startPoint, offset_extra_start_end, max_moveInside_distance2); if (start_inside_poly == NO_INDEX) //If we fail to move the point inside the comb boundary we need to retract. { startInside = false; } } unsigned int end_inside_poly = boundary_inside.findInside(endPoint, true); if (end_inside_poly == NO_INDEX) { end_inside_poly = moveInside(boundary_inside, endPoint, offset_extra_start_end, max_moveInside_distance2); if (end_inside_poly == NO_INDEX) //If we fail to move the point inside the comb boundary we need to retract. { endInside = false; } } unsigned int start_part_boundary_poly_idx; unsigned int end_part_boundary_poly_idx; unsigned int start_part_idx = partsView_inside.getPartContaining(start_inside_poly, &start_part_boundary_poly_idx); unsigned int end_part_idx = partsView_inside.getPartContaining(end_inside_poly, &end_part_boundary_poly_idx); if (startInside && endInside && start_part_idx == end_part_idx) { // normal combing within part PolygonsPart part = partsView_inside.assemblePart(start_part_idx); combPaths.emplace_back(); LinePolygonsCrossings::comb(part, startPoint, endPoint, combPaths.back(), -offset_dist_to_get_from_on_the_polygon_to_outside); return true; } else { // comb inside part to edge (if needed) >> move through air avoiding other parts >> comb inside end part upto the endpoint (if needed) Point middle_from; Point middle_to; if (startInside && endInside) { ClosestPolygonPoint middle_from_cp = findClosest(endPoint, boundary_inside[start_part_boundary_poly_idx]); ClosestPolygonPoint middle_to_cp = findClosest(middle_from_cp.location, boundary_inside[end_part_boundary_poly_idx]); // walkToNearestSmallestConnection(middle_from_cp, middle_to_cp); // TODO: perform this optimization? middle_from = middle_from_cp.location; middle_to = middle_to_cp.location; } else { if (!startInside && !endInside) { middle_from = startPoint; middle_to = endPoint; } else if (!startInside && endInside) { middle_from = startPoint; ClosestPolygonPoint middle_to_cp = findClosest(middle_from, boundary_inside[end_part_boundary_poly_idx]); middle_to = middle_to_cp.location; } else if (startInside && !endInside) { middle_to = endPoint; ClosestPolygonPoint middle_from_cp = findClosest(middle_to, boundary_inside[start_part_boundary_poly_idx]); middle_from = middle_from_cp.location; } } if (startInside) { // start to boundary PolygonsPart part_begin = partsView_inside.assemblePart(start_part_idx); // comb through the starting part only combPaths.emplace_back(); LinePolygonsCrossings::comb(part_begin, startPoint, middle_from, combPaths.back(), -offset_dist_to_get_from_on_the_polygon_to_outside); } // throught air from boundary to boundary if (avoid_other_parts) { Polygons& middle = *getBoundaryOutside(); // comb through all air, since generally the outside consists of a single part Point from_outside = middle_from; if (startInside || middle.inside(from_outside, true)) { // move outside moveInside(middle, from_outside, -offset_extra_start_end, max_moveOutside_distance2); } Point to_outside = middle_to; if (endInside || middle.inside(to_outside, true)) { // move outside moveInside(middle, to_outside, -offset_extra_start_end, max_moveOutside_distance2); } combPaths.emplace_back(); combPaths.back().throughAir = true; if ( vSize(middle_from - middle_to) < vSize(middle_from - from_outside) + vSize(middle_to - to_outside) ) { // via outside is a detour combPaths.back().push_back(middle_from); combPaths.back().push_back(middle_to); } else { LinePolygonsCrossings::comb(middle, from_outside, to_outside, combPaths.back(), offset_dist_to_get_from_on_the_polygon_to_outside); } } else { // directly through air (not avoiding other parts) combPaths.emplace_back(); combPaths.back().throughAir = true; combPaths.back().cross_boundary = true; // TODO: calculate whether we cross a boundary! combPaths.back().push_back(middle_from); combPaths.back().push_back(middle_to); } if (endInside) { // boundary to end PolygonsPart part_end = partsView_inside.assemblePart(end_part_idx); // comb through end part only combPaths.emplace_back(); LinePolygonsCrossings::comb(part_end, middle_to, endPoint, combPaths.back(), -offset_dist_to_get_from_on_the_polygon_to_outside); } return true; } }
void SlicerLayer::makePolygons(OptimizedVolume* ov, bool keepNoneClosed, bool extensiveStitching) { for(unsigned int startSegment=0; startSegment < segmentList.size(); startSegment++) { if (segmentList[startSegment].addedToPolygon) continue; ClipperLib::Polygon poly; poly.push_back(segmentList[startSegment].start); unsigned int segmentIndex = startSegment; bool canClose; while(true) { canClose = false; segmentList[segmentIndex].addedToPolygon = true; Point p0 = segmentList[segmentIndex].end; poly.push_back(p0); int nextIndex = -1; OptimizedFace* face = &ov->faces[segmentList[segmentIndex].faceIndex]; for(unsigned int i=0;i<3;i++) { if (face->touching[i] > -1 && faceToSegmentIndex.find(face->touching[i]) != faceToSegmentIndex.end()) { Point p1 = segmentList[faceToSegmentIndex[face->touching[i]]].start; Point diff = p0 - p1; if (shorterThen(diff, 10)) { if (faceToSegmentIndex[face->touching[i]] == (int)startSegment) canClose = true; if (segmentList[faceToSegmentIndex[face->touching[i]]].addedToPolygon) continue; nextIndex = faceToSegmentIndex[face->touching[i]]; } } } if (nextIndex == -1) break; segmentIndex = nextIndex; } if (canClose) polygonList.add(poly); else openPolygonList.add(poly); } //Clear the segmentList to save memory, it is no longer needed after this point. segmentList.clear(); //Connecting polygons that are not closed yet, as models are not always perfect manifold we need to join some stuff up to get proper polygons //First link up polygon ends that are within 2 microns. for(unsigned int i=0;i<openPolygonList.size();i++) { if (openPolygonList[i].size() < 1) continue; for(unsigned int j=0;j<openPolygonList.size();j++) { if (openPolygonList[j].size() < 1) continue; Point diff = openPolygonList[i][openPolygonList[i].size()-1] - openPolygonList[j][0]; int64_t distSquared = vSize2(diff); if (distSquared < 2 * 2) { if (i == j) { polygonList.add(openPolygonList[i]); openPolygonList[i].clear(); break; }else{ for(unsigned int n=0; n<openPolygonList[j].size(); n++) openPolygonList[i].push_back(openPolygonList[j][n]); openPolygonList[j].clear(); } } } } //Next link up all the missing ends, closing up the smallest gaps first. This is an inefficient implementation which can run in O(n*n*n) time. while(1) { int64_t bestScore = 10000 * 10000; unsigned int bestA = -1; unsigned int bestB = -1; bool reversed = false; for(unsigned int i=0;i<openPolygonList.size();i++) { if (openPolygonList[i].size() < 1) continue; for(unsigned int j=0;j<openPolygonList.size();j++) { if (openPolygonList[j].size() < 1) continue; Point diff = openPolygonList[i][openPolygonList[i].size()-1] - openPolygonList[j][0]; int64_t distSquared = vSize2(diff); if (distSquared < bestScore) { bestScore = distSquared; bestA = i; bestB = j; reversed = false; } if (i != j) { Point diff = openPolygonList[i][openPolygonList[i].size()-1] - openPolygonList[j][openPolygonList[j].size()-1]; int64_t distSquared = vSize2(diff); if (distSquared < bestScore) { bestScore = distSquared; bestA = i; bestB = j; reversed = true; } } } } if (bestScore >= 10000 * 10000) break; if (bestA == bestB) { polygonList.add(openPolygonList[bestA]); openPolygonList[bestA].clear(); }else{ if (reversed) { for(unsigned int n=openPolygonList[bestB].size()-1; int(n)>=0; n--) openPolygonList[bestA].push_back(openPolygonList[bestB][n]); }else{ for(unsigned int n=0; n<openPolygonList[bestB].size(); n++) openPolygonList[bestA].push_back(openPolygonList[bestB][n]); } openPolygonList[bestB].clear(); } } if (extensiveStitching) { //For extensive stitching find 2 open polygons that are touching 2 closed polygons. // Then find the sortest path over this polygon that can be used to connect the open polygons, // And generate a path over this shortest bit to link up the 2 open polygons. // (If these 2 open polygons are the same polygon, then the final result is a closed polyon) while(1) { unsigned int bestA = -1; unsigned int bestB = -1; gapCloserResult bestResult; bestResult.len = LLONG_MAX; bestResult.polygonIdx = -1; bestResult.pointIdxA = -1; bestResult.pointIdxB = -1; for(unsigned int i=0; i<openPolygonList.size(); i++) { if (openPolygonList[i].size() < 1) continue; { gapCloserResult res = findPolygonGapCloser(openPolygonList[i][0], openPolygonList[i][openPolygonList[i].size()-1]); if (res.len > 0 && res.len < bestResult.len) { bestA = i; bestB = i; bestResult = res; } } for(unsigned int j=0; j<openPolygonList.size(); j++) { if (openPolygonList[j].size() < 1 || i == j) continue; gapCloserResult res = findPolygonGapCloser(openPolygonList[i][0], openPolygonList[j][openPolygonList[j].size()-1]); if (res.len > 0 && res.len < bestResult.len) { bestA = i; bestB = j; bestResult = res; } } } if (bestResult.len < LLONG_MAX) { if (bestA == bestB) { if (bestResult.pointIdxA == bestResult.pointIdxB) { polygonList.add(openPolygonList[bestA]); openPolygonList[bestA].clear(); } else if (bestResult.AtoB) { unsigned int n = polygonList.size(); polygonList.add(ClipperLib::Polygon()); for(unsigned int j = bestResult.pointIdxA; j != bestResult.pointIdxB; j = (j + 1) % polygonList[bestResult.polygonIdx].size()) polygonList[n].push_back(polygonList[bestResult.polygonIdx][j]); for(unsigned int j = openPolygonList[bestA].size() - 1; int(j) >= 0; j--) polygonList[n].push_back(openPolygonList[bestA][j]); openPolygonList[bestA].clear(); } else { unsigned int n = polygonList.size(); polygonList.add(openPolygonList[bestA]); for(unsigned int j = bestResult.pointIdxB; j != bestResult.pointIdxA; j = (j + 1) % polygonList[bestResult.polygonIdx].size()) polygonList[n].push_back(polygonList[bestResult.polygonIdx][j]); openPolygonList[bestA].clear(); } } else { if (bestResult.pointIdxA == bestResult.pointIdxB) { for(unsigned int n=0; n<openPolygonList[bestA].size(); n++) openPolygonList[bestB].push_back(openPolygonList[bestA][n]); openPolygonList[bestA].clear(); } else if (bestResult.AtoB) { ClipperLib::Polygon poly; for(unsigned int n = bestResult.pointIdxA; n != bestResult.pointIdxB; n = (n + 1) % polygonList[bestResult.polygonIdx].size()) poly.push_back(polygonList[bestResult.polygonIdx][n]); for(unsigned int n=poly.size()-1;int(n) >= 0; n--) openPolygonList[bestB].push_back(poly[n]); for(unsigned int n=0; n<openPolygonList[bestA].size(); n++) openPolygonList[bestB].push_back(openPolygonList[bestA][n]); openPolygonList[bestA].clear(); } else { for(unsigned int n = bestResult.pointIdxB; n != bestResult.pointIdxA; n = (n + 1) % polygonList[bestResult.polygonIdx].size()) openPolygonList[bestB].push_back(polygonList[bestResult.polygonIdx][n]); for(unsigned int n = openPolygonList[bestA].size() - 1; int(n) >= 0; n--) openPolygonList[bestB].push_back(openPolygonList[bestA][n]); openPolygonList[bestA].clear(); } } } else { break; } } } /* int q=0; for(unsigned int i=0;i<openPolygonList.size();i++) { if (openPolygonList[i].size() < 2) continue; if (!q) printf("***\n"); printf("S: %f %f\n", float(openPolygonList[i][0].X), float(openPolygonList[i][0].Y)); printf("E: %f %f\n", float(openPolygonList[i][openPolygonList[i].size()-1].X), float(openPolygonList[i][openPolygonList[i].size()-1].Y)); q = 1; } */ //if (q) exit(1); if (keepNoneClosed) { for(unsigned int n=0; n<openPolygonList.size(); n++) { if (openPolygonList[n].size() > 0) polygonList.add(openPolygonList[n]); } } //Clear the openPolygonList to save memory, the only reason to keep it after this is for debugging. //openPolygonList.clear(); //Remove all the tiny polygons, or polygons that are not closed. As they do not contribute to the actual print. int snapDistance = 1000; for(unsigned int i=0;i<polygonList.size();i++) { int length = 0; for(unsigned int n=1; n<polygonList[i].size(); n++) { length += vSize(polygonList[i][n] - polygonList[i][n-1]); if (length > snapDistance) break; } if (length < snapDistance) { polygonList.remove(i); i--; } } //Finally optimize all the polygons. Every point removed saves time in the long run. optimizePolygons(polygonList); }
void GCodePlanner::addTravel(Point p) { GCodePath* path = nullptr; if (comb != nullptr && lastPosition != Point(0,0)) { CombPaths combPaths; if (comb->calc(lastPosition, p, combPaths, was_combing, is_going_to_comb)) { bool retract = combPaths.size() > 1; { // check whether we want to retract if (!retract && combPaths.size() == 1 && combPaths[0].throughAir && combPaths[0].size() > 2) { // retract when avoiding obstacles through air retract = true; } for (unsigned int path_idx = 0; path_idx < combPaths.size() && !retract; path_idx++) { // retract when path moves through a boundary if (combPaths[path_idx].cross_boundary) { retract = true; } } } if (retract && last_retraction_config->zHop > 0) { // TODO: stop comb calculation early! (as soon as we see we don't end in the same part as we began) path = getLatestPathWithConfig(&travelConfig); if (!shorterThen(lastPosition - p, last_retraction_config->retraction_min_travel_distance)) { path->retract = true; } } else { for (CombPath& combPath : combPaths) { // add all comb paths (don't do anything special for paths which are moving through air) if (combPath.size() == 0) { continue; } path = getLatestPathWithConfig(&travelConfig); path->retract = retract; for (Point& combPoint : combPath) { path->points.push_back(combPoint); } lastPosition = combPath.back(); } } } else { path = getLatestPathWithConfig(&travelConfig); if (!shorterThen(lastPosition - p, last_retraction_config->retraction_min_travel_distance)) { path->retract = true; } } was_combing = is_going_to_comb; } addTravel_simple(p, path); }