bool Comb::Crossing::findOutside(const Polygons& outside, const Point close_to, const bool fail_on_unavoidable_obstacles, Comb& comber) { out = in_or_mid; if (dest_is_inside || outside.inside(in_or_mid, true)) // start in_between { // move outside Point preferred_crossing_1_out = in_or_mid + normal(close_to - in_or_mid, comber.offset_from_inside_to_outside); std::function<int(Point)> close_to_penalty_function([preferred_crossing_1_out](Point candidate){ return vSize2((candidate - preferred_crossing_1_out) / 2); }); std::optional<ClosestPolygonPoint> crossing_1_out_cpp = PolygonUtils::findClose(in_or_mid, outside, comber.getOutsideLocToLine(), close_to_penalty_function); if (crossing_1_out_cpp) { out = PolygonUtils::moveOutside(*crossing_1_out_cpp, comber.offset_dist_to_get_from_on_the_polygon_to_outside); } else { PolygonUtils::moveOutside(outside, out, comber.offset_dist_to_get_from_on_the_polygon_to_outside); } } int64_t in_out_dist2_1 = vSize2(out - in_or_mid); if (dest_is_inside && in_out_dist2_1 > comber.max_crossing_dist2) // moveInside moved too far { // if move is too far over in_between // find crossing closer by assert(dest_crossing_poly && "destination crossing poly should have been instantiated!"); std::shared_ptr<std::pair<ClosestPolygonPoint, ClosestPolygonPoint>> best = findBestCrossing(outside, *dest_crossing_poly, dest_point, close_to, comber); if (best) { in_or_mid = PolygonUtils::moveInside(best->first, comber.offset_dist_to_get_from_on_the_polygon_to_outside); out = PolygonUtils::moveOutside(best->second, comber.offset_dist_to_get_from_on_the_polygon_to_outside); } if (fail_on_unavoidable_obstacles && vSize2(out - in_or_mid) > comber.max_crossing_dist2) // moveInside moved still too far { return false; } } return true; }
Point getClosestOnLine(Point from, Point p0, Point p1) { Point direction = p1 - p0; Point toFrom = from-p0; int64_t projected_x = dot(toFrom, direction) ; int64_t x_p0 = 0; int64_t x_p1 = vSize2(direction); if (projected_x <= x_p0) { return p0; } if (projected_x >= x_p1) { return p1; } else { if (vSize2(direction) == 0) { std::cout << "warning! too small segment" << std::endl; return p0; } Point ret = p0 + projected_x / vSize(direction) * direction / vSize(direction); return ret ; } }
ClosestPolygonPoint findClosest(Point from, Polygons& polygons) { Polygon emptyPoly; ClosestPolygonPoint none(from, -1, emptyPoly); if (polygons.size() == 0) return none; PolygonRef aPolygon = polygons[0]; if (aPolygon.size() == 0) return none; Point aPoint = aPolygon[0]; ClosestPolygonPoint best(aPoint, 0, aPolygon); int64_t closestDist = vSize2(from - best.location); for (unsigned int ply = 0; ply < polygons.size(); ply++) { PolygonRef poly = polygons[ply]; if (poly.size() == 0) continue; ClosestPolygonPoint closestHere = findClosest(from, poly); int64_t dist = vSize2(from - closestHere.location); if (dist < closestDist) { best = closestHere; closestDist = dist; } } return best; }
ClosestPolygonPoint findClosest(Point from, PolygonRef polygon) { if (polygon.size() == 0) { return ClosestPolygonPoint(polygon); } Point aPoint = polygon[0]; Point best = aPoint; int64_t closestDist = vSize2(from - best); int bestPos = 0; // for (unsigned int p = 0; p<polygon.size(); p++) { Point& p1 = polygon[p]; unsigned int p2_idx = p+1; if (p2_idx >= polygon.size()) p2_idx = 0; Point& p2 = polygon[p2_idx]; Point closestHere = getClosestOnLine(from, p1 ,p2); int64_t dist = vSize2(from - closestHere); if (dist < closestDist) { best = closestHere; closestDist = dist; bestPos = p; } } return ClosestPolygonPoint(best, bestPos, polygon); }
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; } }
ClosestPolygonPoint findNearestClosest(Point from, PolygonRef polygon, int start_idx, int direction) { if (polygon.size() == 0) { return ClosestPolygonPoint(polygon); } Point aPoint = polygon[0]; Point best = aPoint; int64_t closestDist = vSize2(from - best); int bestPos = 0; for (unsigned int p = 0; p<polygon.size(); p++) { int p1_idx = (polygon.size() + direction*p + start_idx) % polygon.size(); int p2_idx = (polygon.size() + direction*(p+1) + start_idx) % polygon.size(); Point& p1 = polygon[p1_idx]; Point& p2 = polygon[p2_idx]; Point closestHere = getClosestOnLine(from, p1 ,p2); int64_t dist = vSize2(from - closestHere); if (dist < closestDist) { best = closestHere; closestDist = dist; bestPos = p1_idx; } else { return ClosestPolygonPoint(best, bestPos, polygon); } } return ClosestPolygonPoint(best, bestPos, polygon); }
void Comb::Crossing::findCrossingInOrMid(const PartsView& partsView_inside, const Point close_to) { if (dest_is_inside) { // in-case // find the point on the start inside-polygon closest to the endpoint, but also kind of close to the start point Point _dest_point(dest_point); // copy to local variable for lambda capture std::function<int(Point)> close_towards_start_penalty_function([_dest_point](Point candidate){ return vSize2((candidate - _dest_point) / 10); }); dest_part = partsView_inside.assemblePart(dest_part_idx); ClosestPolygonPoint boundary_crossing_point; { // set [result] to a point on the destination part closest to close_to (but also a bit close to _dest_point) std::unordered_set<unsigned int> dest_part_poly_indices; for (unsigned int poly_idx : partsView_inside[dest_part_idx]) { dest_part_poly_indices.emplace(poly_idx); } coord_t dist2_score = std::numeric_limits<coord_t>::max(); std::function<bool (const PolygonsPointIndex&)> line_processor = [close_to, _dest_point, &boundary_crossing_point, &dist2_score, &dest_part_poly_indices](const PolygonsPointIndex& boundary_segment) { if (dest_part_poly_indices.find(boundary_segment.poly_idx) == dest_part_poly_indices.end()) { // we're not looking at a polygon from the dest_part return true; // a.k.a. continue; } Point closest_here = LinearAlg2D::getClosestOnLineSegment(close_to, boundary_segment.p(), boundary_segment.next().p()); coord_t dist2_score_here = vSize2(close_to - closest_here) + vSize2(_dest_point - closest_here) / 10; if (dist2_score_here < dist2_score) { dist2_score = dist2_score_here; boundary_crossing_point = ClosestPolygonPoint(closest_here, boundary_segment.point_idx, boundary_segment.getPolygon(), boundary_segment.poly_idx); } return true; }; inside_loc_to_line->processLine(std::make_pair(dest_point, close_to), line_processor); } Point result(boundary_crossing_point.p()); // the inside point of the crossing if (!boundary_crossing_point.isValid()) { // no point has been found in the sparse grid result = dest_point; } int64_t max_dist2 = std::numeric_limits<int64_t>::max(); ClosestPolygonPoint crossing_1_in_cp = PolygonUtils::ensureInsideOrOutside(dest_part, result, boundary_crossing_point, offset_dist_to_get_from_on_the_polygon_to_outside, max_dist2, &boundary_inside, inside_loc_to_line, close_towards_start_penalty_function); if (crossing_1_in_cp.isValid()) { dest_crossing_poly = crossing_1_in_cp.poly; in_or_mid = result; } else { // part is too small to be ensuring a point inside with the given distance in_or_mid = dest_point; // just use the startPoint or endPoint itself } } else { in_or_mid = dest_point; // mid-case } };
std::shared_ptr<std::pair<ClosestPolygonPoint, ClosestPolygonPoint>> Comb::Crossing::findBestCrossing(const Polygons& outside, const PolygonRef from, const Point estimated_start, const Point estimated_end, Comb& comber) { ClosestPolygonPoint* best_in = nullptr; ClosestPolygonPoint* best_out = nullptr; int64_t best_detour_score = std::numeric_limits<int64_t>::max(); int64_t best_crossing_dist2; std::vector<std::pair<ClosestPolygonPoint, ClosestPolygonPoint>> crossing_out_candidates = PolygonUtils::findClose(from, outside, comber.getOutsideLocToLine()); bool seen_close_enough_connection = false; for (std::pair<ClosestPolygonPoint, ClosestPolygonPoint>& crossing_candidate : crossing_out_candidates) { int64_t crossing_dist2 = vSize2(crossing_candidate.first.location - crossing_candidate.second.location); if (crossing_dist2 > comber.max_crossing_dist2 * 2) { // preliminary filtering continue; } int64_t dist_to_start = vSize(crossing_candidate.second.location - estimated_start); // use outside location, so that the crossing direction is taken into account int64_t dist_to_end = vSize(crossing_candidate.second.location - estimated_end); int64_t detour_dist = dist_to_start + dist_to_end; int64_t detour_score = crossing_dist2 + detour_dist * detour_dist / 1000; // prefer a closest connection over a detour // The detour distance is generally large compared to the crossing distance. // While the crossing is generally about 1mm across, // the distance between an arbitrary point and the boundary may well be a couple of centimetres. // So the crossing_dist2 is about 1.000.000 while the detour_dist_2 is in the order of 400.000.000 // In the end we just want to choose between two points which have the _same_ crossing distance, modulo rounding error. if ((!seen_close_enough_connection && detour_score < best_detour_score) // keep the best as long as we havent seen one close enough (so that we may walk along the polygon to find a closer connection from it in the code below) || (!seen_close_enough_connection && crossing_dist2 <= comber.max_crossing_dist2) // make the one which is close enough the best as soon as we see one close enough || (seen_close_enough_connection && crossing_dist2 <= comber.max_crossing_dist2 && detour_score < best_detour_score)) // update to keep the best crossing which is close enough already { if (!seen_close_enough_connection && crossing_dist2 <= comber.max_crossing_dist2) { seen_close_enough_connection = true; } best_in = &crossing_candidate.first; best_out = &crossing_candidate.second; best_detour_score = detour_score; best_crossing_dist2 = crossing_dist2; } } if (best_detour_score == std::numeric_limits<int64_t>::max()) { // i.e. if best_in == nullptr or if best_out == nullptr return std::shared_ptr<std::pair<ClosestPolygonPoint, ClosestPolygonPoint>>(); } if (best_crossing_dist2 > comber.max_crossing_dist2) { // find closer point on line segments, rather than moving between vertices of the polygons only PolygonUtils::walkToNearestSmallestConnection(*best_in, *best_out); best_crossing_dist2 = vSize2(best_in->location - best_out->location); if (best_crossing_dist2 > comber.max_crossing_dist2) { return std::shared_ptr<std::pair<ClosestPolygonPoint, ClosestPolygonPoint>>(); } } return std::make_shared<std::pair<ClosestPolygonPoint, ClosestPolygonPoint>>(*best_in, *best_out); }
ClosestPolygonPoint findNearestClosest(Point from, PolygonRef polygon, int start_idx) { ClosestPolygonPoint forth = findNearestClosest(from, polygon, start_idx, 1); ClosestPolygonPoint back = findNearestClosest(from, polygon, start_idx, -1); if (vSize2(forth.location - from) < vSize2(back.location - from)) { return forth; } else { return back; } }
bool MergeInfillLines::tryMerge(const bool first_is_already_merged, GCodePath& first_path, const Point first_path_start, GCodePath& second_path, const Point second_path_start, Point& new_first_path_start, coord_t& error_area) const { const Point first_path_end = first_path.points.back(); const Point second_path_end = second_path.points.back(); const coord_t line_width = first_path.config->getLineWidth(); //Lines may be adjacent side-by-side then. Point first_path_leave_point; coord_t merged_size2; if (first_is_already_merged) { first_path_leave_point = first_path.points.back(); // this is the point that's going to merge } else { first_path_leave_point = (first_path_start + first_path_end) / 2; } const Point second_path_destination_point = (second_path_start + second_path_end) / 2; const Point merged_direction = second_path_destination_point - first_path_leave_point; if (first_is_already_merged) { merged_size2 = vSize2(second_path_destination_point - first_path.points.back()); // check distance with last point in merged line that is to be replaced } else { merged_size2 = vSize2(merged_direction); } if (merged_size2 > 25 * line_width * line_width) { return false; //Lines are too far away from each other. } if (merged_direction.X == 0 && merged_direction.Y == 0) { return true; // we can just disregard the second point as it's exactly at the leave point of the first path. } // Max 1 line width to the side of the merged_direction if (LinearAlg2D::getDist2FromLine(first_path_end, second_path_destination_point, second_path_destination_point + merged_direction) > line_width * line_width || LinearAlg2D::getDist2FromLine(second_path_start, first_path_leave_point, first_path_leave_point + merged_direction) > line_width * line_width || LinearAlg2D::getDist2FromLine(second_path_end, first_path_leave_point, first_path_leave_point + merged_direction) > line_width * line_width //|| abs(dot(normal(merged_direction, 1000), normal(second_path_end - second_path_start, 1000))) > 866000 // 866000 angle of old second_path with new merged direction should not be too small (30 degrees), as it will introduce holes ) { return false; //One of the lines is too far from the merged line. Lines would be too wide or too far off. } if (first_is_already_merged && first_path.points.size() > 1 && first_path.points[first_path.points.size() - 2] == second_path_destination_point) // yes this can actually happen { return false; } return mergeLinesSideBySide(first_is_already_merged, first_path, first_path_start, second_path, second_path_start, new_first_path_start, error_area); }
std::shared_ptr<std::pair<ClosestPolygonPoint, ClosestPolygonPoint>> Comb::findBestCrossing(PolygonRef from, Point estimated_start, Point estimated_end) { ClosestPolygonPoint* best_in = nullptr; ClosestPolygonPoint* best_out = nullptr; int64_t best_detour_dist = std::numeric_limits<int64_t>::max(); std::vector<std::pair<ClosestPolygonPoint, ClosestPolygonPoint>> crossing_out_candidates = PolygonUtils::findClose(from, getBoundaryOutside(), getOutsideLocToLine()); for (std::pair<ClosestPolygonPoint, ClosestPolygonPoint>& crossing_candidate : crossing_out_candidates) { int64_t crossing_dist2 = vSize2(crossing_candidate.first.location - crossing_candidate.second.location); if (crossing_dist2 > max_crossing_dist2) { continue; } int64_t dist_to_start = vSize(crossing_candidate.second.location - estimated_start); // use outside location, so that the crossing direction is taken into account int64_t dist_to_end = vSize(crossing_candidate.second.location - estimated_end); int64_t detour_dist = dist_to_start + dist_to_end; if (detour_dist < best_detour_dist) { best_in = &crossing_candidate.first; best_out = &crossing_candidate.second; best_detour_dist = detour_dist; } } if (best_detour_dist == std::numeric_limits<int64_t>::max()) { return std::shared_ptr<std::pair<ClosestPolygonPoint, ClosestPolygonPoint>>(); } return std::make_shared<std::pair<ClosestPolygonPoint, ClosestPolygonPoint>>(*best_in, *best_out); }
void findSmallestConnection(ClosestPolygonPoint& poly1_result, ClosestPolygonPoint& poly2_result, int sample_size) { PolygonRef poly1 = poly1_result.poly; PolygonRef poly2 = poly2_result.poly; if (poly1.size() == 0 || poly2.size() == 0) { return; } int bestDist2 = -1; int step1 = std::max<unsigned int>(2, poly1.size() / sample_size); int step2 = std::max<unsigned int>(2, poly2.size() / sample_size); for (unsigned int i = 0; i < poly1.size(); i += step1) { for (unsigned int j = 0; j < poly2.size(); j += step2) { int dist2 = vSize2(poly1[i] - poly2[j]); if (bestDist2 == -1 || dist2 < bestDist2) { bestDist2 = dist2; poly1_result.pos = i; poly2_result.pos = j; } } } walkToNearestSmallestConnection(poly1_result, poly2_result); }
void GCodePlanner::addLinesByOptimizer(Polygons& polygons, GCodePathConfig* config, int wipe_dist) { LineOrderOptimizer orderOptimizer(lastPosition); for(unsigned int i=0;i<polygons.size();i++) orderOptimizer.addPolygon(polygons[i]); orderOptimizer.optimize(); for(unsigned int i=0;i<orderOptimizer.polyOrder.size();i++) { int nr = orderOptimizer.polyOrder[i]; // addPolygon(polygons[nr], orderOptimizer.polyStart[nr], config); PolygonRef polygon = polygons[nr]; int start = orderOptimizer.polyStart[nr]; int end = 1 - start; Point& p0 = polygon[start]; addTravel(p0); Point& p1 = polygon[end]; addExtrusionMove(p1, config); if (wipe_dist != 0) { int line_width = config->getLineWidth(); if (vSize2(p1-p0) > line_width * line_width * 4) { // otherwise line will get optimized by combining multiple into a single extrusion move addExtrusionMove(p1 + normal(p1-p0, wipe_dist), config, 0.0); } } } }
void AreaSupport::handleWallStruts( Polygons& supportLayer_this, int supportMinAreaSqrt, int supportTowerDiameter ) { for (unsigned int p = 0; p < supportLayer_this.size(); p++) { PolygonRef poly = supportLayer_this[p]; if (poly.size() < 6) // might be a single wall { PolygonRef poly = supportLayer_this[p]; int best = -1; int best_length2 = -1; for (unsigned int i = 0; i < poly.size(); i++) { int length2 = vSize2(poly[i] - poly[(i+1) % poly.size()]); if (length2 > best_length2) { best = i; best_length2 = length2; } } if (best_length2 < supportMinAreaSqrt * supportMinAreaSqrt) break; // this is a small area, not a wall! // an estimate of the width of the area int width = sqrt( poly.area() * poly.area() / best_length2 ); // sqrt (a^2 / l^2) instead of a / sqrt(l^2) // add square tower (strut) in the middle of the wall if (width < supportMinAreaSqrt) { Point mid = (poly[best] + poly[(best+1) % poly.size()] ) / 2; Polygons struts; PolygonRef strut = struts.newPoly(); strut.add(mid + Point( supportTowerDiameter/2, supportTowerDiameter/2)); strut.add(mid + Point(-supportTowerDiameter/2, supportTowerDiameter/2)); strut.add(mid + Point(-supportTowerDiameter/2, -supportTowerDiameter/2)); strut.add(mid + Point( supportTowerDiameter/2, -supportTowerDiameter/2)); supportLayer_this = supportLayer_this.unionPolygons(struts); } } } }
void WallOverlapComputation::findOverlapPoints(ListPolyIt from_it, unsigned int to_list_poly_idx, ListPolygon::iterator start) { ListPolygon& to_list_poly = list_polygons[to_list_poly_idx]; Point& from = from_it.p(); ListPolygon::iterator last_it = to_list_poly.end(); last_it--; for (ListPolygon::iterator it = start; it != to_list_poly.end(); ++it) { Point& last_point = *last_it; Point& point = *it; if (from == last_point || from == point ) { // we currently consider a linesegment directly connected to [from] last_point = point; continue; } Point closest = getClosestOnLine(from, last_point, point); int64_t dist2 = vSize2(closest - from); if (dist2 > lineWidth * lineWidth) { // line segment too far away to have overlap last_point = point; continue; } int64_t dist = sqrt(dist2); if (closest == last_point) { addOverlapPoint(from_it, ListPolyIt(to_list_poly, last_it), dist); } else if (closest == point) { addOverlapPoint(from_it, ListPolyIt(to_list_poly, it), dist); } else { ListPolygon::iterator new_it = to_list_poly.insert(it, closest); addOverlapPoint(from_it, ListPolyIt(to_list_poly, new_it), dist); } last_point = point; } }
int SubDivCube::distanceFromPointToMesh(SliceMeshStorage& mesh, int layer_nr, Point& location, int64_t* distance2) { if (layer_nr < 0 || (unsigned int)layer_nr >= mesh.layers.size()) //!< this layer is outside of valid range { return 2; *distance2 = 0; } Polygons collide; mesh.layers[layer_nr].getSecondOrInnermostWalls(collide); Point centerpoint = location; bool inside = collide.inside(centerpoint); ClosestPolygonPoint border_point = PolygonUtils::moveInside2(collide, centerpoint); Point diff = border_point.location - location; *distance2 = vSize2(diff); if (inside) { return 1; } return 0; }
bool Comb::moveInside(Point* p, int distance) { Point ret = *p; int64_t bestDist = MM2INT(2.0) * MM2INT(2.0); for(unsigned int n=0; n<boundery.size(); n++) { if (boundery[n].size() < 1) continue; Point p0 = boundery[n][boundery[n].size()-1]; for(unsigned int i=0; i<boundery[n].size(); i++) { Point p1 = boundery[n][i]; //Q = A + Normal( B - A ) * ((( B - A ) dot ( P - A )) / VSize( A - B )); Point pDiff = p1 - p0; int64_t lineLength = vSize(pDiff); int64_t distOnLine = dot(pDiff, *p - p0) / lineLength; if (distOnLine < 10) distOnLine = 10; if (distOnLine > lineLength - 10) distOnLine = lineLength - 10; Point q = p0 + pDiff * distOnLine / lineLength; int64_t dist = vSize2(q - *p); if (dist < bestDist) { bestDist = dist; ret = q + crossZ(normal(p1 - p0, distance)); } p0 = p1; } } if (bestDist < MM2INT(2.0) * MM2INT(2.0)) { *p = ret; return true; } return false; }
int PathOrderOptimizer::getClosestPointInPolygon(Point prev_point, int poly_idx) { PolygonRef poly = polygons[poly_idx]; int best_point_idx = -1; float best_point_score = std::numeric_limits<float>::infinity(); Point p0 = poly.back(); for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++) { Point& p1 = poly[point_idx]; Point& p2 = poly[(point_idx + 1) % poly.size()]; int64_t dist = vSize2(p1 - prev_point); float is_on_inside_corner_score = -LinearAlg2D::getAngleLeft(p0, p1, p2) / M_PI * 5000 * 5000; // prefer inside corners // this score is in the order of 5 mm if (dist + is_on_inside_corner_score < best_point_score) { best_point_idx = point_idx; best_point_score = dist + is_on_inside_corner_score; } p0 = p1; } return best_point_idx; }
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 GyroidInfill::generateTotalGyroidInfill(Polygons& result_lines, bool zig_zaggify, coord_t outline_offset, coord_t infill_line_width, coord_t line_distance, const Polygons& in_outline, coord_t z) { // generate infill based on the gyroid equation: sin_x * cos_y + sin_y * cos_z + sin_z * cos_x = 0 // kudos to the author of the Slic3r implementation equation code, the equation code here is based on that if (zig_zaggify) { outline_offset -= infill_line_width / 2; // the infill line zig zag connections must lie next to the border, not on it } const Polygons outline = in_outline.offset(outline_offset); const AABB aabb(outline); int pitch = line_distance * 2.41; // this produces similar density to the "line" infill pattern int num_steps = 4; int step = pitch / num_steps; while (step > 500 && num_steps < 16) { num_steps *= 2; step = pitch / num_steps; } pitch = step * num_steps; // recalculate to avoid precision errors const double z_rads = 2 * M_PI * z / pitch; const double cos_z = std::cos(z_rads); const double sin_z = std::sin(z_rads); std::vector<coord_t> odd_line_coords; std::vector<coord_t> even_line_coords; Polygons result; std::vector<Point> chains[2]; // [start_points[], end_points[]] std::vector<unsigned> connected_to[2]; // [chain_indices[], chain_indices[]] std::vector<int> line_numbers; // which row/column line a chain is part of if (std::abs(sin_z) <= std::abs(cos_z)) { // "vertical" lines const double phase_offset = ((cos_z < 0) ? M_PI : 0) + M_PI; for (coord_t y = 0; y < pitch; y += step) { const double y_rads = 2 * M_PI * y / pitch; const double a = cos_z; const double b = std::sin(y_rads + phase_offset); const double odd_c = sin_z * std::cos(y_rads + phase_offset); const double even_c = sin_z * std::cos(y_rads + phase_offset + M_PI); const double h = std::sqrt(a * a + b * b); const double odd_x_rads = ((h != 0) ? std::asin(odd_c / h) + std::asin(b / h) : 0) - M_PI/2; const double even_x_rads = ((h != 0) ? std::asin(even_c / h) + std::asin(b / h) : 0) - M_PI/2; odd_line_coords.push_back(odd_x_rads / M_PI * pitch); even_line_coords.push_back(even_x_rads / M_PI * pitch); } const unsigned num_coords = odd_line_coords.size(); unsigned num_columns = 0; for (coord_t x = (std::floor(aabb.min.X / pitch) - 2.25) * pitch; x <= aabb.max.X + pitch/2; x += pitch/2) { bool is_first_point = true; Point last; bool last_inside = false; unsigned chain_end_index = 0; Point chain_end[2]; for (coord_t y = (std::floor(aabb.min.Y / pitch) - 1) * pitch; y <= aabb.max.Y + pitch; y += pitch) { for (unsigned i = 0; i < num_coords; ++i) { Point current(x + ((num_columns & 1) ? odd_line_coords[i] : even_line_coords[i])/2 + pitch, y + (coord_t)(i * step)); bool current_inside = outline.inside(current, true); if (!is_first_point) { if (last_inside && current_inside) { // line doesn't hit the boundary, add the whole line result.addLine(last, current); } else if (last_inside != current_inside) { // line hits the boundary, add the part that's inside the boundary Polygons line; line.addLine(last, current); line = outline.intersectionPolyLines(line); if (line.size() > 0) { // some of the line is inside the boundary result.addLine(line[0][0], line[0][1]); if (zig_zaggify) { chain_end[chain_end_index] = line[0][(line[0][0] != last && line[0][0] != current) ? 0 : 1]; if (++chain_end_index == 2) { chains[0].push_back(chain_end[0]); chains[1].push_back(chain_end[1]); chain_end_index = 0; connected_to[0].push_back(std::numeric_limits<unsigned>::max()); connected_to[1].push_back(std::numeric_limits<unsigned>::max()); line_numbers.push_back(num_columns); } } } else { // none of the line is inside the boundary so the point that's actually on the boundary // is the chain end if (zig_zaggify) { chain_end[chain_end_index] = (last_inside) ? last : current; if (++chain_end_index == 2) { chains[0].push_back(chain_end[0]); chains[1].push_back(chain_end[1]); chain_end_index = 0; connected_to[0].push_back(std::numeric_limits<unsigned>::max()); connected_to[1].push_back(std::numeric_limits<unsigned>::max()); line_numbers.push_back(num_columns); } } } } } last = current; last_inside = current_inside; is_first_point = false; } } ++num_columns; } } else { // "horizontal" lines const double phase_offset = (sin_z < 0) ? M_PI : 0; for (coord_t x = 0; x < pitch; x += step) { const double x_rads = 2 * M_PI * x / pitch; const double a = sin_z; const double b = std::cos(x_rads + phase_offset); const double odd_c = cos_z * std::sin(x_rads + phase_offset + M_PI); const double even_c = cos_z * std::sin(x_rads + phase_offset); const double h = std::sqrt(a * a + b * b); const double odd_y_rads = ((h != 0) ? std::asin(odd_c / h) + std::asin(b / h) : 0) + M_PI/2; const double even_y_rads = ((h != 0) ? std::asin(even_c / h) + std::asin(b / h) : 0) + M_PI/2; odd_line_coords.push_back(odd_y_rads / M_PI * pitch); even_line_coords.push_back(even_y_rads / M_PI * pitch); } const unsigned num_coords = odd_line_coords.size(); unsigned num_rows = 0; for (coord_t y = (std::floor(aabb.min.Y / pitch) - 1) * pitch; y <= aabb.max.Y + pitch/2; y += pitch/2) { bool is_first_point = true; Point last; bool last_inside = false; unsigned chain_end_index = 0; Point chain_end[2]; for (coord_t x = (std::floor(aabb.min.X / pitch) - 1) * pitch; x <= aabb.max.X + pitch; x += pitch) { for (unsigned i = 0; i < num_coords; ++i) { Point current(x + (coord_t)(i * step), y + ((num_rows & 1) ? odd_line_coords[i] : even_line_coords[i])/2); bool current_inside = outline.inside(current, true); if (!is_first_point) { if (last_inside && current_inside) { // line doesn't hit the boundary, add the whole line result.addLine(last, current); } else if (last_inside != current_inside) { // line hits the boundary, add the part that's inside the boundary Polygons line; line.addLine(last, current); line = outline.intersectionPolyLines(line); if (line.size() > 0) { // some of the line is inside the boundary result.addLine(line[0][0], line[0][1]); if (zig_zaggify) { chain_end[chain_end_index] = line[0][(line[0][0] != last && line[0][0] != current) ? 0 : 1]; if (++chain_end_index == 2) { chains[0].push_back(chain_end[0]); chains[1].push_back(chain_end[1]); chain_end_index = 0; connected_to[0].push_back(std::numeric_limits<unsigned>::max()); connected_to[1].push_back(std::numeric_limits<unsigned>::max()); line_numbers.push_back(num_rows); } } } else { // none of the line is inside the boundary so the point that's actually on the boundary // is the chain end if (zig_zaggify) { chain_end[chain_end_index] = (last_inside) ? last : current; if (++chain_end_index == 2) { chains[0].push_back(chain_end[0]); chains[1].push_back(chain_end[1]); chain_end_index = 0; connected_to[0].push_back(std::numeric_limits<unsigned>::max()); connected_to[1].push_back(std::numeric_limits<unsigned>::max()); line_numbers.push_back(num_rows); } } } } } last = current; last_inside = current_inside; is_first_point = false; } } ++num_rows; } } if (zig_zaggify && chains[0].size() > 0) { // zig-zaggification consists of joining alternate chain ends to make a chain of chains // the basic algorithm is that we follow the infill area boundary and as we progress we are either drawing a connector or not // whenever we come across the end of a chain we toggle the connector drawing state // things are made more complicated by the fact that we want to avoid generating loops and so we need to keep track // of the indentity of the first chain in a connected sequence int chain_ends_remaining = chains[0].size() * 2; for (ConstPolygonRef outline_poly : outline) { std::vector<Point> connector_points; // the points that make up a connector line // we need to remember the first chain processed and the path to it from the first outline point // so that later we can possibly connect to it from the last chain processed unsigned first_chain_chain_index = std::numeric_limits<unsigned>::max(); std::vector<Point> path_to_first_chain; bool drawing = false; // true when a connector line is being (potentially) created // keep track of the chain+point that a connector line started at unsigned connector_start_chain_index = std::numeric_limits<unsigned>::max(); unsigned connector_start_point_index = std::numeric_limits<unsigned>::max(); Point cur_point; // current point of interest - either an outline point or a chain end // go round all of the region's outline and find the chain ends that meet it // quit the loop early if we have seen all the chain ends and are not currently drawing a connector for (unsigned outline_point_index = 0; (chain_ends_remaining > 0 || drawing) && outline_point_index < outline_poly.size(); ++outline_point_index) { Point op0 = outline_poly[outline_point_index]; Point op1 = outline_poly[(outline_point_index + 1) % outline_poly.size()]; std::vector<unsigned> points_on_outline_chain_index; std::vector<unsigned> points_on_outline_point_index; // collect the chain ends that meet this segment of the outline for (unsigned chain_index = 0; chain_index < chains[0].size(); ++chain_index) { for (unsigned point_index = 0; point_index < 2; ++point_index) { // don't include chain ends that are close to the segment but are beyond the segment ends short beyond = 0; if (LinearAlg2D::getDist2FromLineSegment(op0, chains[point_index][chain_index], op1, &beyond) < 10 && !beyond) { points_on_outline_point_index.push_back(point_index); points_on_outline_chain_index.push_back(chain_index); } } } if (outline_point_index == 0 || vSize2(op0 - cur_point) > 100) { // this is either the first outline point or it is another outline point that is not too close to cur_point if (first_chain_chain_index == std::numeric_limits<unsigned>::max()) { // include the outline point in the path to the first chain path_to_first_chain.push_back(op0); } cur_point = op0; if (drawing) { // include the start point of this outline segment in the connector connector_points.push_back(op0); } } // iterate through each of the chain ends that meet the current outline segment while (points_on_outline_chain_index.size() > 0) { // find the nearest chain end to the current point unsigned nearest_point_index = 0; float nearest_point_dist2 = std::numeric_limits<float>::infinity(); for (unsigned pi = 0; pi < points_on_outline_chain_index.size(); ++pi) { float dist2 = vSize2f(chains[points_on_outline_point_index[pi]][points_on_outline_chain_index[pi]] - cur_point); if (dist2 < nearest_point_dist2) { nearest_point_dist2 = dist2; nearest_point_index = pi; } } const unsigned point_index = points_on_outline_point_index[nearest_point_index]; const unsigned chain_index = points_on_outline_chain_index[nearest_point_index]; // make the chain end the current point and add it to the connector line cur_point = chains[point_index][chain_index]; if (drawing && connector_points.size() > 0 && vSize2(cur_point - connector_points.back()) < 100) { // this chain end will be too close to the last connector point so throw away the last connector point connector_points.pop_back(); } connector_points.push_back(cur_point); if (first_chain_chain_index == std::numeric_limits<unsigned>::max()) { // this is the first chain to be processed, remember it first_chain_chain_index = chain_index; path_to_first_chain.push_back(cur_point); } if (drawing) { // add the connector line segments but only if // 1 - the start/end points are not the opposite ends of the same chain // 2 - the other end of the current chain is not connected to the chain the connector line is coming from if (chain_index != connector_start_chain_index && connected_to[(point_index + 1) % 2][chain_index] != connector_start_chain_index) { for (unsigned pi = 1; pi < connector_points.size(); ++pi) { result.addLine(connector_points[pi - 1], connector_points[pi]); } drawing = false; connector_points.clear(); // remember the connection connected_to[point_index][chain_index] = connector_start_chain_index; connected_to[connector_start_point_index][connector_start_chain_index] = chain_index; } else { // start a new connector from the current location connector_points.clear(); connector_points.push_back(cur_point); // remember the chain+point that the connector started from connector_start_chain_index = chain_index; connector_start_point_index = point_index; } } else { // we have just jumped a gap so now we want to start drawing again drawing = true; // if this connector is the first to be created or we are not connecting chains from the same row/column, // remember the chain+point that this connector is starting from if (connector_start_chain_index == std::numeric_limits<unsigned>::max() || line_numbers[chain_index] != line_numbers[connector_start_chain_index]) { connector_start_chain_index = chain_index; connector_start_point_index = point_index; } } // done with this chain end points_on_outline_chain_index.erase(points_on_outline_chain_index.begin() + nearest_point_index); points_on_outline_point_index.erase(points_on_outline_point_index.begin() + nearest_point_index); // decrement total amount of work to do --chain_ends_remaining; } } // we have now visited all the points in the outline, if a connector was (potentially) being drawn // check whether the first chain is already connected to the last chain and, if not, draw the // connector between if (drawing && first_chain_chain_index != std::numeric_limits<unsigned>::max() && first_chain_chain_index != connector_start_chain_index && connected_to[0][first_chain_chain_index] != connector_start_chain_index && connected_to[1][first_chain_chain_index] != connector_start_chain_index) { // output the connector line segments from the last chain to the first point in the outline connector_points.push_back(outline_poly[0]); for (unsigned pi = 1; pi < connector_points.size(); ++pi) { result.addLine(connector_points[pi - 1], connector_points[pi]); } // output the connector line segments from the first point in the outline to the first chain for (unsigned pi = 1; pi < path_to_first_chain.size(); ++pi) { result.addLine(path_to_first_chain[pi - 1], path_to_first_chain[pi]); } } if (chain_ends_remaining < 1) { break; } } } result_lines = result; }
void Comb::Crossing::findCrossingInOrMid(const PartsView& partsView_inside, const Point close_to) { if (dest_is_inside) { // in-case // find the point on the start inside-polygon closest to the endpoint, but also kind of close to the start point Point _dest_point(dest_point); // copy to local variable for lambda capture std::function<int(Point)> close_towards_start_penalty_function([_dest_point](Point candidate){ return vSize2((candidate - _dest_point) / 10); }); dest_part = partsView_inside.assemblePart(dest_part_idx); Point result(close_to); int64_t max_dist2 = std::numeric_limits<int64_t>::max(); ClosestPolygonPoint crossing_1_in_cp = PolygonUtils::ensureInsideOrOutside(dest_part, result, offset_dist_to_get_from_on_the_polygon_to_outside, max_dist2, close_towards_start_penalty_function); if (crossing_1_in_cp.point_idx != NO_INDEX) { dest_crossing_poly = crossing_1_in_cp.poly; in_or_mid = result; } else { // part is too small to be ensuring a point inside with the given distance in_or_mid = dest_point; // just use the startPoint or endPoint itself } } else { in_or_mid = dest_point; // mid-case } };
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::moveInside(Point* from, int distance) { Point ret = *from; int64_t maxDist2 = MM2INT(2.0) * MM2INT(2.0); int64_t bestDist2 = maxDist2; for(PolygonRef poly : boundary) { if (poly.size() < 2) continue; Point p0 = poly[poly.size()-2]; Point p1 = poly.back(); bool projected_p_beyond_prev_segment = dot(p1 - p0, *from - p0) > vSize2(p1 - p0); for(Point& p2 : poly) { // X = A + Normal( B - A ) * ((( B - A ) dot ( P - A )) / VSize( A - B )); // X = P projected on AB Point& a = p1; Point& b = p2; Point& p = *from; Point ab = b - a; Point ap = p - a; int64_t ab_length = vSize(ab); int64_t ax_length = dot(ab, ap) / ab_length; if (ax_length < 0) // x is projected to before ab { if (projected_p_beyond_prev_segment) { // case which looks like: > . projected_p_beyond_prev_segment = false; Point& x = p1; int64_t dist2 = vSize2(x - p); if (dist2 < bestDist2) { bestDist2 = dist2; ret = x + normal(crossZ(normal(a, distance*4) + normal(p1 - p0, distance*4)), distance); // *4 to retain more precision for the eventual normalization } } else { projected_p_beyond_prev_segment = false; p0 = p1; p1 = p2; continue; } } else if (ax_length > ab_length) // x is projected to beyond ab { projected_p_beyond_prev_segment = true; p0 = p1; p1 = p2; continue; } else { projected_p_beyond_prev_segment = false; Point x = a + ab * ax_length / ab_length; int64_t dist2 = vSize2(x - *from); if (dist2 < bestDist2) { bestDist2 = dist2; ret = x + crossZ(normal(ab, distance)); } } p0 = p1; p1 = p2; } } if (bestDist2 < maxDist2) { *from = ret; return true; } return false; }
void LinearAlg2DTest::getPointOnLineWithDistAssert(const Point p, const Point a, const Point b, int64_t dist, Point actual_result, bool actual_returned) { Point supposed_result; bool supposed_returned = LinearAlg2D::getPointOnLineWithDist(p, a, b, dist, supposed_result); int64_t returned_dist = vSize(supposed_result - p); std::stringstream ss; if (actual_returned) { if (supposed_returned) { ss << "Point " << p << " was projected on (" << a << "-" << b << ") to " << supposed_result << " instead of " << actual_result << "."; } else { ss << "Point " << p << " wasn't projected on (" << a << "-" << b << ") instead of projecting to " << actual_result << "."; } } else { if (supposed_returned) { ss << "Point " << p << " was projected on (" << a << "-" << b << ") to " << supposed_result << ", but it wasn't supposed to project."; } else { ss << "This is no error! This should never show!"; } } ss << " \t Requested dist was " << dist << " result dist is " << returned_dist << "."; CPPUNIT_ASSERT_MESSAGE(ss.str(), (!actual_returned && !supposed_returned) || (actual_returned && vSize2(actual_result - supposed_result) < 10 * 10 && std::abs(returned_dist - dist) < 10)); }
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, 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; } }
unsigned int moveInside(Polygons& polygons, Point& from, int distance, int64_t maxDist2) { Point ret = from; int64_t bestDist2 = maxDist2; unsigned int bestPoly = NO_INDEX; for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++) { PolygonRef poly = polygons[poly_idx]; if (poly.size() < 2) continue; Point p0 = poly[poly.size()-2]; Point p1 = poly.back(); bool projected_p_beyond_prev_segment = dot(p1 - p0, from - p0) > vSize2(p1 - p0); for(Point& p2 : poly) { // X = A + Normal( B - A ) * ((( B - A ) dot ( P - A )) / VSize( A - B )); // X = P projected on AB Point& a = p1; Point& b = p2; Point& p = from; Point ab = b - a; Point ap = p - a; int64_t ab_length = vSize(ab); int64_t ax_length = dot(ab, ap) / ab_length; if (ax_length < 0) // x is projected to before ab { if (projected_p_beyond_prev_segment) { // case which looks like: > . projected_p_beyond_prev_segment = false; Point& x = p1; int64_t dist2 = vSize2(x - p); if (dist2 < bestDist2) { bestDist2 = dist2; if (distance == 0) { ret = x; } else { ret = x + normal(crossZ(normal(a, distance*4) + normal(p1 - p0, distance*4)), distance); } // *4 to retain more precision for the eventual normalization bestPoly = poly_idx; } } else { projected_p_beyond_prev_segment = false; p0 = p1; p1 = p2; continue; } } else if (ax_length > ab_length) // x is projected to beyond ab { projected_p_beyond_prev_segment = true; p0 = p1; p1 = p2; continue; } else { projected_p_beyond_prev_segment = false; Point x = a + ab * ax_length / ab_length; int64_t dist2 = vSize2(x - from); if (dist2 < bestDist2) { bestDist2 = dist2; if (distance == 0) { ret = x; } else { ret = x + crossZ(normal(ab, distance)); } bestPoly = poly_idx; } } p0 = p1; p1 = p2; } } if (bestDist2 < maxDist2) { from = ret; return bestPoly; } return NO_INDEX; }
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); }
bool MergeInfillLines::tryMerge(const bool first_is_already_merged, GCodePath& first_path, const Point first_path_start, GCodePath& second_path, const Point second_path_start, Point& new_first_path_start, coord_t& error_area) const { const Point first_path_end = first_path.points.back(); const Point second_path_end = second_path.points.back(); const coord_t line_width = first_path.config->getLineWidth(); // This check prevents [CURA-5690] fat skin lines: const coord_t line_width_squared = line_width * line_width; if (vSize2(first_path_end - second_path_start) < line_width_squared || vSize2(first_path_start - second_path_end) < line_width_squared) { // Define max_dot_product_squared as 20*20, where 20 micron is the allowed inaccuracy in the dot product, allowing a slight curve: constexpr coord_t max_dot_product_squared = 400; const Point first_direction = first_path_end - first_path_start; const Point second_direction = second_path_end - second_path_start; // Only continue to try-merge at this point if the lines line up straight: if (dot(first_direction, second_direction) + max_dot_product_squared > vSize(first_direction) * vSize(second_direction)) { return false; } } //Lines may be adjacent side-by-side then. Point first_path_leave_point; coord_t merged_size2; if (first_is_already_merged) { first_path_leave_point = first_path.points.back(); // this is the point that's going to merge } else { first_path_leave_point = (first_path_start + first_path_end) / 2; } const Point second_path_destination_point = (second_path_start + second_path_end) / 2; const Point merged_direction = second_path_destination_point - first_path_leave_point; if (first_is_already_merged) { merged_size2 = vSize2(second_path_destination_point - first_path.points.back()); // check distance with last point in merged line that is to be replaced } else { merged_size2 = vSize2(merged_direction); } if (merged_size2 > 25 * line_width * line_width) { return false; //Lines are too far away from each other. } if (merged_direction.X == 0 && merged_direction.Y == 0) { new_first_path_start = first_path_start; return false; // returning true will not work for the gradual infill } // Max 1 line width to the side of the merged_direction if (LinearAlg2D::getDist2FromLine(first_path_end, second_path_destination_point, second_path_destination_point + merged_direction) > line_width * line_width || LinearAlg2D::getDist2FromLine(second_path_start, first_path_leave_point, first_path_leave_point + merged_direction) > line_width * line_width || LinearAlg2D::getDist2FromLine(second_path_end, first_path_leave_point, first_path_leave_point + merged_direction) > line_width * line_width //|| abs(dot(normal(merged_direction, 1000), normal(second_path_end - second_path_start, 1000))) > 866000 // 866000 angle of old second_path with new merged direction should not be too small (30 degrees), as it will introduce holes ) { return false; //One of the lines is too far from the merged line. Lines would be too wide or too far off. } if (first_is_already_merged && first_path.points.size() > 1 && first_path.points[first_path.points.size() - 2] == second_path_destination_point) // yes this can actually happen { return false; } return mergeLinesSideBySide(first_is_already_merged, first_path, first_path_start, second_path, second_path_start, new_first_path_start, error_area); }