void GCodePlanner::addPolygon(PolygonRef polygon, int startIdx, GCodePathConfig* config, WallOverlapComputation* wall_overlap_computation) { Point p0 = polygon[startIdx]; addTravel(p0); for(unsigned int i=1; i<polygon.size(); i++) { Point p1 = polygon[(startIdx + i) % polygon.size()]; addExtrusionMove(p1, config, (wall_overlap_computation)? wall_overlap_computation->getFlow(p0, p1) : 1.0); p0 = p1; } if (polygon.size() > 2) { Point& p1 = polygon[startIdx]; addExtrusionMove(p1, config, (wall_overlap_computation)? wall_overlap_computation->getFlow(p0, p1) : 1.0); } }
bool LinePolygonsCrossings::calcScanlineCrossings(bool fail_on_unavoidable_obstacles) { min_crossing_idx = NO_INDEX; max_crossing_idx = NO_INDEX; for(unsigned int poly_idx = 0; poly_idx < boundary.size(); poly_idx++) { PolyCrossings minMax(poly_idx); PolygonRef poly = boundary[poly_idx]; Point p0 = transformation_matrix.apply(poly[poly.size() - 1]); for(unsigned int point_idx = 0; point_idx < poly.size(); point_idx++) { Point p1 = transformation_matrix.apply(poly[point_idx]); if ((p0.Y >= transformed_startPoint.Y && p1.Y <= transformed_startPoint.Y) || (p1.Y >= transformed_startPoint.Y && p0.Y <= transformed_startPoint.Y)) { // if line segment crosses the line through the transformed start and end point (aka scanline) if (p1.Y == p0.Y) //Line segment is parallel with the scanline. That means that both endpoints lie on the scanline, so they will have intersected with the adjacent line. { p0 = p1; continue; } int64_t x = p0.X + (p1.X - p0.X) * (transformed_startPoint.Y - p0.Y) / (p1.Y - p0.Y); // intersection point between line segment and the scanline if (x >= transformed_startPoint.X && x <= transformed_endPoint.X) { if (!((p1.Y == transformed_startPoint.Y && p1.Y < p0.Y) || (p0.Y == transformed_startPoint.Y && p0.Y < p1.Y))) { // perform edge case only for line segments on and below the scanline, not for line segments on and above. // \/ will be no crossings and /\ two, but most importantly | will be one crossing. minMax.n_crossings++; } if(x < minMax.min.x) //For the leftmost intersection, move x left to stay outside of the border. //Note: The actual distance from the intersection to the border is almost always less than dist_to_move_boundary_point_outside, since it only moves along the direction of the scanline. { minMax.min.x = x; minMax.min.point_idx = point_idx; } if(x > minMax.max.x) //For the rightmost intersection, move x right to stay outside of the border. { minMax.max.x = x; minMax.max.point_idx = point_idx; } } } p0 = p1; } if (fail_on_unavoidable_obstacles && minMax.n_crossings % 2 == 1) { // if start area and end area are not the same return false; } else if (minMax.min.point_idx != NO_INDEX) // then always also max.point_idx != NO_INDEX { // if this polygon crossed the scanline if (min_crossing_idx == NO_INDEX || minMax.min.x < crossings[min_crossing_idx].min.x) { min_crossing_idx = crossings.size(); } if (max_crossing_idx == NO_INDEX || minMax.max.x > crossings[max_crossing_idx].max.x) { max_crossing_idx = crossings.size(); } crossings.push_back(minMax); } } return true; }
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 Slicer::dumpSegmentsToHTML(const char* filename) { float scale = std::max(modelSize.x, modelSize.y) / 1500; FILE* f = fopen(filename, "w"); fprintf(f, "<!DOCTYPE html><html><body>\n"); for(unsigned int i=0; i<layers.size(); i++) { fprintf(f, "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" style='width:%ipx;height:%ipx'>\n", int(modelSize.x / scale), int(modelSize.y / scale)); fprintf(f, "<marker id='MidMarker' viewBox='0 0 10 10' refX='5' refY='5' markerUnits='strokeWidth' markerWidth='10' markerHeight='10' stroke='lightblue' stroke-width='2' fill='none' orient='auto'>"); fprintf(f, "<path d='M 0 0 L 10 5 M 0 10 L 10 5'/>"); fprintf(f, "</marker>"); fprintf(f, "<g fill-rule='evenodd' style=\"fill: gray; stroke:black;stroke-width:1\">\n"); fprintf(f, "<path marker-mid='url(#MidMarker)' d=\""); for(unsigned int j=0; j<layers[i].polygonList.size(); j++) { PolygonRef p = layers[i].polygonList[j]; for(unsigned int n=0; n<p.size(); n++) { if (n == 0) fprintf(f, "M"); else fprintf(f, "L"); fprintf(f, "%f,%f ", float(p[n].X - modelMin.x)/scale, float(p[n].Y - modelMin.y)/scale); } fprintf(f, "Z\n"); } fprintf(f, "\"/>"); fprintf(f, "</g>\n"); for(unsigned int j=0; j<layers[i].openPolygonList.size(); j++) { PolygonRef p = layers[i].openPolygonList[j]; if (p.size() < 1) continue; fprintf(f, "<polyline marker-mid='url(#MidMarker)' points=\""); for(unsigned int n=0; n<p.size(); n++) { fprintf(f, "%f,%f ", float(p[n].X - modelMin.x)/scale, float(p[n].Y - modelMin.y)/scale); } fprintf(f, "\" style=\"fill: none; stroke:red;stroke-width:1\" />\n"); } fprintf(f, "</svg>\n"); } fprintf(f, "</body></html>"); fclose(f); }
inline int PathOrderOptimizer::getClosestPointInPolygon(Point prev_point, int i_polygon) { PolygonRef poly = polygons[i_polygon]; int best = -1; float bestDist = std::numeric_limits<float>::infinity(); bool orientation = poly.orientation(); for(unsigned int i_point=0 ; i_point<poly.size() ; i_point++) { float dist = vSize2f(poly[i_point] - prev_point); Point n0 = normal(poly[(i_point-1+poly.size())%poly.size()] - poly[i_point], 2000); Point n1 = normal(poly[i_point] - poly[(i_point + 1) % poly.size()], 2000); float dot_score = dot(n0, n1) - dot(crossZ(n0), n1); /// prefer binnenbocht if (orientation) dot_score = -dot_score; if (dist + dot_score < bestDist) { best = i_point; bestDist = dist; } } return best; }
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; }
void LinePolygonsCrossings::calcScanlineCrossings() { min_crossing_idx = NO_INDEX; max_crossing_idx = NO_INDEX; for(unsigned int poly_idx = 0; poly_idx < boundary.size(); poly_idx++) { PolyCrossings minMax(poly_idx); PolygonRef poly = boundary[poly_idx]; Point p0 = transformation_matrix.apply(poly[poly.size() - 1]); for(unsigned int point_idx = 0; point_idx < poly.size(); point_idx++) { Point p1 = transformation_matrix.apply(poly[point_idx]); if((p0.Y >= transformed_startPoint.Y && p1.Y <= transformed_startPoint.Y) || (p1.Y >= transformed_startPoint.Y && p0.Y <= transformed_startPoint.Y)) { if(p1.Y == p0.Y) //Line segment is parallel with the scanline. That means that both endpoints lie on the scanline, so they will have intersected with the adjacent line. { p0 = p1; continue; } int64_t x = p0.X + (p1.X - p0.X) * (transformed_startPoint.Y - p0.Y) / (p1.Y - p0.Y); if (x >= transformed_startPoint.X && x <= transformed_endPoint.X) { if(x < minMax.min.x) //For the leftmost intersection, move x left to stay outside of the border. //Note: The actual distance from the intersection to the border is almost always less than dist_to_move_boundary_point_outside, since it only moves along the direction of the scanline. { minMax.min.x = x; minMax.min.point_idx = point_idx; } if(x > minMax.max.x) //For the rightmost intersection, move x right to stay outside of the border. { minMax.max.x = x; minMax.max.point_idx = point_idx; } } } p0 = p1; } if (minMax.min.point_idx != NO_INDEX) { // then also max.point_idx != -1 if (min_crossing_idx == NO_INDEX || minMax.min.x < crossings[min_crossing_idx].min.x) { min_crossing_idx = crossings.size(); } if (max_crossing_idx == NO_INDEX || minMax.max.x > crossings[max_crossing_idx].max.x) { max_crossing_idx = crossings.size(); } crossings.push_back(minMax); } } }
void LinePolygonsCrossings::calcScanlineCrossings() { min_crossing_idx = NO_INDEX; max_crossing_idx = NO_INDEX; for(unsigned int poly_idx = 0; poly_idx < boundary.size(); poly_idx++) { PolyCrossings minMax(poly_idx); PolygonRef poly = boundary[poly_idx]; Point p0 = transformation_matrix.apply(poly.back()); for(unsigned int point_idx = 0; point_idx < poly.size(); point_idx++) { Point p1 = transformation_matrix.apply(poly[point_idx]); if ((p0.Y > transformed_startPoint.Y && p1.Y < transformed_startPoint.Y) || (p1.Y > transformed_startPoint.Y && p0.Y < transformed_startPoint.Y)) { int64_t x = p0.X + (p1.X - p0.X) * (transformed_startPoint.Y - p0.Y) / (p1.Y - p0.Y); if (x >= transformed_startPoint.X && x <= transformed_endPoint.X) { if (x < minMax.min.x) { minMax.min.x = x; minMax.min.point_idx = point_idx; } if (x > minMax.max.x) { minMax.max.x = x; minMax.max.point_idx = point_idx; } } } p0 = p1; } if (minMax.min.point_idx != NO_INDEX) { // then also max.point_idx != -1 if (min_crossing_idx == NO_INDEX || minMax.min.x < crossings[min_crossing_idx].min.x) { min_crossing_idx = crossings.size(); } if (max_crossing_idx == NO_INDEX || minMax.max.x > crossings[max_crossing_idx].max.x) { max_crossing_idx = crossings.size(); } crossings.push_back(minMax); } } }
void PathOrderOptimizer::optimize() { bool* picked=new bool[polygons.size()]; // bool picked[polygons.size()]; memset(picked, false, sizeof(bool) * polygons.size());/// initialized as falses for(unsigned int i_polygon=0 ; i_polygon<polygons.size() ; i_polygon++) /// find closest point to initial starting point within each polygon +initialize picked { int best = -1; float bestDist = std::numeric_limits<float>::infinity(); PolygonRef poly = polygons[i_polygon]; for(unsigned int i_point=0; i_point<poly.size(); i_point++) /// get closest point in polygon { float dist = vSize2f(poly[i_point] - startPoint); if (dist < bestDist) { best = i_point; bestDist = dist; } } polyStart.push_back(best); //picked.push_back(false); /// initialize all picked values as false assert(poly.size() != 2); } Point prev_point = startPoint; for(unsigned int i_polygon=0 ; i_polygon<polygons.size() ; i_polygon++) /// actual path order optimizer { int best = -1; float bestDist = std::numeric_limits<float>::infinity(); for(unsigned int i_polygon=0 ; i_polygon<polygons.size() ; i_polygon++) { if (picked[i_polygon] || polygons[i_polygon].size() < 1) /// skip single-point-polygons continue; assert (polygons[i_polygon].size() != 2); float dist = vSize2f(polygons[i_polygon][polyStart[i_polygon]] - prev_point); if (dist < bestDist) { best = i_polygon; bestDist = dist; } } if (best > -1) /// should always be true; we should have been able to identify the best next polygon { assert(polygons[best].size() != 2); prev_point = polygons[best][polyStart[best]]; picked[best] = true; polyOrder.push_back(best); } else logError("Failed to find next closest polygon.\n"); } prev_point = startPoint; for(unsigned int n=0; n<polyOrder.size(); n++) /// decide final starting points in each polygon { int i_polygon = polyOrder[n]; int best = getClosestPointInPolygon(prev_point, i_polygon); polyStart[i_polygon] = best; prev_point = polygons[i_polygon][best]; } }
void LineOrderOptimizer::optimize() { int gridSize = 5000; // the size of the cells in the hash grid. BucketGrid2D<unsigned int> line_bucket_grid(gridSize); bool* picked = new bool[polygons.size()]; // bool picked[polygons.size()]; memset(picked, false, sizeof(bool) * polygons.size());/// initialized as falses for(unsigned int i_polygon=0 ; i_polygon<polygons.size() ; i_polygon++) /// find closest point to initial starting point within each polygon +initialize picked { int best = -1; float bestDist = std::numeric_limits<float>::infinity(); PolygonRef poly = polygons[i_polygon]; for(unsigned int i_point=0; i_point<poly.size(); i_point++) /// get closest point from polygon { float dist = vSize2f(poly[i_point] - startPoint); if (dist < bestDist) { best = i_point; bestDist = dist; } } polyStart.push_back(best); assert(poly.size() == 2); line_bucket_grid.insert(poly[0], i_polygon); line_bucket_grid.insert(poly[1], i_polygon); } Point incommingPerpundicularNormal(0, 0); Point prev_point = startPoint; for(unsigned int i_polygon=0 ; i_polygon<polygons.size() ; i_polygon++) /// actual path order optimizer { int best = -1; float bestDist = std::numeric_limits<float>::infinity(); for(unsigned int i_close_line_polygon : line_bucket_grid.findNearbyObjects(prev_point)) /// check if single-line-polygon is close to last point { if (picked[i_close_line_polygon] || polygons[i_close_line_polygon].size() < 1) continue; checkIfLineIsBest(i_close_line_polygon, best, bestDist, prev_point, incommingPerpundicularNormal); } if (best == -1) /// if single-line-polygon hasn't been found yet { for(unsigned int i_polygon=0 ; i_polygon<polygons.size() ; i_polygon++) { if (picked[i_polygon] || polygons[i_polygon].size() < 1) /// skip single-point-polygons continue; assert(polygons[i_polygon].size() == 2); checkIfLineIsBest(i_polygon, best, bestDist, prev_point, incommingPerpundicularNormal); } } if (best > -1) /// should always be true; we should have been able to identify the best next polygon { assert(polygons[best].size() == 2); int endIdx = polyStart[best] * -1 + 1; /// 1 -> 0 , 0 -> 1 prev_point = polygons[best][endIdx]; incommingPerpundicularNormal = crossZ(normal(polygons[best][endIdx] - polygons[best][polyStart[best]], 1000)); picked[best] = true; polyOrder.push_back(best); } else logError("Failed to find next closest line.\n"); } prev_point = startPoint; for(unsigned int n=0; n<polyOrder.size(); n++) /// decide final starting points in each polygon { int nr = polyOrder[n]; PolygonRef poly = polygons[nr]; int best = -1; float bestDist = std::numeric_limits<float>::infinity(); bool orientation = poly.orientation(); for(unsigned int i=0;i<poly.size(); i++) { float dist = vSize2f(polygons[nr][i] - prev_point); Point n0 = normal(poly[(i+poly.size()-1)%poly.size()] - poly[i], 2000); Point n1 = normal(poly[i] - poly[(i + 1) % poly.size()], 2000); float dot_score = dot(n0, n1) - dot(crossZ(n0), n1); if (orientation) dot_score = -dot_score; if (dist + dot_score < bestDist) { best = i; bestDist = dist + dot_score; } } polyStart[nr] = best; assert(poly.size() == 2); prev_point = poly[best *-1 + 1]; /// 1 -> 0 , 0 -> 1 } }
void PathOrderOptimizer::optimize() { std::vector<bool> picked; for(unsigned int i=0;i<polygons.size(); i++) { int best = -1; float bestDist = 0xFFFFFFFFFFFFFFFFLL; PolygonRef poly = polygons[i]; for(unsigned int j=0; j<poly.size(); j++) { float dist = vSize2f(poly[j] - startPoint); if (dist < bestDist) { best = j; bestDist = dist; } } polyStart.push_back(best); picked.push_back(false); } Point p0 = startPoint; for(unsigned int n=0; n<polygons.size(); n++) { int best = -1; float bestDist = 0xFFFFFFFFFFFFFFFFLL; for(unsigned int i=0;i<polygons.size(); i++) { if (picked[i] || polygons[i].size() < 1) continue; if (polygons[i].size() == 2) { float dist = vSize2f(polygons[i][0] - p0); if (dist < bestDist) { best = i; bestDist = dist; polyStart[i] = 0; } dist = vSize2f(polygons[i][1] - p0); if (dist < bestDist) { best = i; bestDist = dist; polyStart[i] = 1; } }else{ float dist = vSize2f(polygons[i][polyStart[i]] - p0); if (dist < bestDist) { best = i; bestDist = dist; } } } if (best > -1) { if (polygons[best].size() == 2) { p0 = polygons[best][(polyStart[best] + 1) % 2]; }else{ p0 = polygons[best][polyStart[best]]; } picked[best] = true; polyOrder.push_back(best); } } p0 = startPoint; for(unsigned int n=0; n<polyOrder.size(); n++) { int nr = polyOrder[n]; int best = -1; float bestDist = 0xFFFFFFFFFFFFFFFFLL; for(unsigned int i=0;i<polygons[nr].size(); i++) { float dist = vSize2f(polygons[nr][i] - p0); if (dist < bestDist) { best = i; bestDist = dist; } } polyStart[nr] = best; if (polygons[nr].size() <= 2) { p0 = polygons[nr][(best + 1) % 2]; }else{ p0 = polygons[nr][best]; } } }
/* * algorithm: * 1. for each line segment of each polygon: * store the intersections of that line segment with all scanlines in a mapping (vector of vectors) from scanline to intersections * (zigzag): add boundary segments to result * 2. for each scanline: * sort the associated intersections * and connect them using the even-odd rule * * rough explanation of the zigzag algorithm: * while walking around (each) polygon (1.) * if polygon intersects with even scanline * start boundary segment (add each following segment to the [result]) * when polygon intersects with a scanline again * stop boundary segment (stop adding segments to the [result]) * (see infill/ZigzagConnectorProcessor.h for actual implementation details) * * * we call the areas between two consecutive scanlines a 'scansegment'. * Scansegment x is the area between scanline x and scanline x+1 * Edit: the term scansegment is wrong, since I call a boundary segment leaving from an even scanline to the left as belonging to an even scansegment, * while I also call a boundary segment leaving from an even scanline toward the right as belonging to an even scansegment. */ void Infill::generateLinearBasedInfill(const int outline_offset, Polygons& result, const int line_distance, const PointMatrix& rotation_matrix, ZigzagConnectorProcessor& zigzag_connector_processor, const bool connected_zigzags, int64_t extra_shift) { if (line_distance == 0) { return; } if (in_outline.size() == 0) { return; } int shift = extra_shift + this->shift; Polygons outline; if (outline_offset != 0) { outline = in_outline.offset(outline_offset); if (perimeter_gaps) { perimeter_gaps->add(in_outline.difference(outline.offset(infill_line_width / 2 + perimeter_gaps_extra_offset))); } } else { outline = in_outline; } outline = outline.offset(infill_overlap); if (outline.size() == 0) { return; } outline.applyMatrix(rotation_matrix); if (shift < 0) { shift = line_distance - (-shift) % line_distance; } else { shift = shift % line_distance; } AABB boundary(outline); int scanline_min_idx = computeScanSegmentIdx(boundary.min.X - shift, line_distance); int line_count = computeScanSegmentIdx(boundary.max.X - shift, line_distance) + 1 - scanline_min_idx; std::vector<std::vector<int64_t> > cut_list; // mapping from scanline to all intersections with polygon segments for(int scanline_idx = 0; scanline_idx < line_count; scanline_idx++) { cut_list.push_back(std::vector<int64_t>()); } for(unsigned int poly_idx = 0; poly_idx < outline.size(); poly_idx++) { PolygonRef poly = outline[poly_idx]; Point p0 = poly.back(); zigzag_connector_processor.registerVertex(p0); // always adds the first point to ZigzagConnectorProcessorEndPieces::first_zigzag_connector when using a zigzag infill type for(unsigned int point_idx = 0; point_idx < poly.size(); point_idx++) { Point p1 = poly[point_idx]; if (p1.X == p0.X) { zigzag_connector_processor.registerVertex(p1); // TODO: how to make sure it always adds the shortest line? (in order to prevent overlap with the zigzag connectors) // note: this is already a problem for normal infill, but hasn't really cothered anyone so far. p0 = p1; continue; } int scanline_idx0; int scanline_idx1; // this way of handling the indices takes care of the case where a boundary line segment ends exactly on a scanline: // in case the next segment moves back from that scanline either 2 or 0 scanline-boundary intersections are created // otherwise only 1 will be created, counting as an actual intersection int direction = 1; if (p0.X < p1.X) { scanline_idx0 = computeScanSegmentIdx(p0.X - shift, line_distance) + 1; // + 1 cause we don't cross the scanline of the first scan segment scanline_idx1 = computeScanSegmentIdx(p1.X - shift, line_distance); // -1 cause the vertex point is handled in the next segment (or not in the case which looks like >) } else { direction = -1; scanline_idx0 = computeScanSegmentIdx(p0.X - shift, line_distance); // -1 cause the vertex point is handled in the previous segment (or not in the case which looks like >) scanline_idx1 = computeScanSegmentIdx(p1.X - shift, line_distance) + 1; // + 1 cause we don't cross the scanline of the first scan segment } for(int scanline_idx = scanline_idx0; scanline_idx != scanline_idx1 + direction; scanline_idx += direction) { int x = scanline_idx * line_distance + shift; int y = p1.Y + (p0.Y - p1.Y) * (x - p1.X) / (p0.X - p1.X); assert(scanline_idx - scanline_min_idx >= 0 && scanline_idx - scanline_min_idx < int(cut_list.size()) && "reading infill cutlist index out of bounds!"); cut_list[scanline_idx - scanline_min_idx].push_back(y); Point scanline_linesegment_intersection(x, y); zigzag_connector_processor.registerScanlineSegmentIntersection(scanline_linesegment_intersection, scanline_idx % 2 == 0); } zigzag_connector_processor.registerVertex(p1); p0 = p1; } zigzag_connector_processor.registerPolyFinished(); } if (cut_list.size() == 0) { return; } if (connected_zigzags && cut_list.size() == 1 && cut_list[0].size() <= 2) { return; // don't add connection if boundary already contains whole outline! } addLineInfill(result, rotation_matrix, scanline_min_idx, line_distance, boundary, cut_list, shift); }
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; }
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 LineOrderOptimizer::optimize() { int gridSize = 5000; // the size of the cells in the hash grid. TODO SparsePointGridInclusive<unsigned int> line_bucket_grid(gridSize); bool picked[polygons.size()]; memset(picked, false, sizeof(bool) * polygons.size());/// initialized as falses for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++) /// find closest point to initial starting point within each polygon +initialize picked { int best_point_idx = -1; float best_point_dist = std::numeric_limits<float>::infinity(); PolygonRef poly = polygons[poly_idx]; for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++) /// get closest point from polygon { float dist = vSize2f(poly[point_idx] - startPoint); if (dist < best_point_dist) { best_point_idx = point_idx; best_point_dist = dist; } } polyStart.push_back(best_point_idx); assert(poly.size() == 2); line_bucket_grid.insert(poly[0], poly_idx); line_bucket_grid.insert(poly[1], poly_idx); } Point incoming_perpundicular_normal(0, 0); Point prev_point = startPoint; for (unsigned int order_idx = 0; order_idx < polygons.size(); order_idx++) /// actual path order optimizer { int best_line_idx = -1; float best_score = std::numeric_limits<float>::infinity(); // distance score for the best next line /// check if single-line-polygon is close to last point for(unsigned int close_line_idx : line_bucket_grid.getNearbyVals(prev_point, gridSize)) { if (picked[close_line_idx] || polygons[close_line_idx].size() < 1) { continue; } updateBestLine(close_line_idx, best_line_idx, best_score, prev_point, incoming_perpundicular_normal); } if (best_line_idx == -1) /// if single-line-polygon hasn't been found yet { for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++) { if (picked[poly_idx] || polygons[poly_idx].size() < 1) /// skip single-point-polygons { continue; } assert(polygons[poly_idx].size() == 2); updateBestLine(poly_idx, best_line_idx, best_score, prev_point, incoming_perpundicular_normal); } } if (best_line_idx > -1) /// should always be true; we should have been able to identify the best next polygon { PolygonRef best_line = polygons[best_line_idx]; assert(best_line.size() == 2); int line_start_point_idx = polyStart[best_line_idx]; int line_end_point_idx = line_start_point_idx * -1 + 1; /// 1 -> 0 , 0 -> 1 Point& line_start = best_line[line_start_point_idx]; Point& line_end = best_line[line_end_point_idx]; prev_point = line_end; incoming_perpundicular_normal = turn90CCW(normal(line_end - line_start, 1000)); picked[best_line_idx] = true; polyOrder.push_back(best_line_idx); } else { logError("Failed to find next closest line.\n"); } } }
void PathOrderOptimizer::optimize() { const float incommingPerpundicularNormalScale = 0.0001f; std::map<uint32_t, std::vector<unsigned int>> location_to_polygon_map; std::vector<bool> picked; for(unsigned int i=0; i<polygons.size(); i++) { int best = -1; float bestDist = 0xFFFFFFFFFFFFFFFFLL; PolygonRef poly = polygons[i]; for(unsigned int j=0; j<poly.size(); j++) { float dist = vSize2f(poly[j] - startPoint); if (dist < bestDist) { best = j; bestDist = dist; } } polyStart.push_back(best); picked.push_back(false); if (poly.size() == 2) { Point p0 = poly[0]; Point p1 = poly[1]; location_to_polygon_map[hashPoint(p0)].push_back(i); location_to_polygon_map[hashPoint(p1)].push_back(i); } } Point incommingPerpundicularNormal(0, 0); Point p0 = startPoint; for(unsigned int n=0; n<polygons.size(); n++) { int best = -1; float bestDist = 0xFFFFFFFFFFFFFFFFLL; for(unsigned int i : location_to_polygon_map[hashPoint(p0)]) { if (picked[i] || polygons[i].size() < 1) continue; float dist = vSize2f(polygons[i][0] - p0); dist += abs(dot(incommingPerpundicularNormal, normal(polygons[i][1] - polygons[i][0], 1000))) * incommingPerpundicularNormalScale; if (dist < bestDist) { best = i; bestDist = dist; polyStart[i] = 0; } dist = vSize2f(polygons[i][1] - p0); dist += abs(dot(incommingPerpundicularNormal, normal(polygons[i][0] - polygons[i][1], 1000))) * incommingPerpundicularNormalScale; if (dist < bestDist) { best = i; bestDist = dist; polyStart[i] = 1; } } if (best == -1) { for(unsigned int i=0; i<polygons.size(); i++) { if (picked[i] || polygons[i].size() < 1) continue; if (polygons[i].size() == 2) { float dist = vSize2f(polygons[i][0] - p0); dist += abs(dot(incommingPerpundicularNormal, normal(polygons[i][1] - polygons[i][0], 1000))) * incommingPerpundicularNormalScale; if (dist < bestDist) { best = i; bestDist = dist; polyStart[i] = 0; } dist = vSize2f(polygons[i][1] - p0); dist += abs(dot(incommingPerpundicularNormal, normal(polygons[i][0] - polygons[i][1], 1000))) * incommingPerpundicularNormalScale; if (dist < bestDist) { best = i; bestDist = dist; polyStart[i] = 1; } } else { float dist = vSize2f(polygons[i][polyStart[i]] - p0); if (dist < bestDist) { best = i; bestDist = dist; } } } } if (best > -1) { if (polygons[best].size() == 2) { int endIdx = (polyStart[best] + 1) % 2; p0 = polygons[best][endIdx]; incommingPerpundicularNormal = crossZ(normal(polygons[best][endIdx] - polygons[best][polyStart[best]], 1000)); } else { p0 = polygons[best][polyStart[best]]; incommingPerpundicularNormal = Point(0, 0); } picked[best] = true; polyOrder.push_back(best); } } p0 = startPoint; for(int nr : polyOrder) { PolygonRef poly = polygons[nr]; if (poly.size() > 2) { int best = -1; float bestDist = 0xFFFFFFFFFFFFFFFFLL; bool orientation = poly.orientation(); for(unsigned int i=0; i<poly.size(); i++) { const int64_t dot_score_scale = 2000; float dist = vSize2f(polygons[nr][i] - p0); Point n0 = normal(poly[(i+poly.size()-1)%poly.size()] - poly[i], dot_score_scale); Point n1 = normal(poly[i] - poly[(i + 1) % poly.size()], dot_score_scale); float dot_score = dot(n0, n1) - dot(crossZ(n0), n1); if (orientation) dot_score = -dot_score; dist += dot_score; if (dist < bestDist) { best = i; bestDist = dist; } } polyStart[nr] = best; } if (poly.size() <= 2) { p0 = poly[(polyStart[nr] + 1) % 2]; } else { p0 = poly[polyStart[nr]]; } } }
void FffPolygonGenerator::processFuzzyWalls(SliceMeshStorage& mesh) { if (mesh.getSettingAsCount("wall_line_count") == 0) { return; } int64_t fuzziness = mesh.getSettingInMicrons("magic_fuzzy_skin_thickness"); int64_t avg_dist_between_points = mesh.getSettingInMicrons("magic_fuzzy_skin_point_dist"); int64_t min_dist_between_points = avg_dist_between_points * 3 / 4; // hardcoded: the point distance may vary between 3/4 and 5/4 the supplied value int64_t range_random_point_dist = avg_dist_between_points / 2; for (unsigned int layer_nr = 0; layer_nr < mesh.layers.size(); layer_nr++) { SliceLayer& layer = mesh.layers[layer_nr]; for (SliceLayerPart& part : layer.parts) { Polygons results; Polygons& skin = (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") == ESurfaceMode::SURFACE)? part.outline : part.insets[0]; for (PolygonRef poly : skin) { // generate points in between p0 and p1 PolygonRef result = results.newPoly(); int64_t dist_left_over = rand() % (min_dist_between_points / 2); // the distance to be traversed on the line before making the first new point Point* p0 = &poly.back(); for (Point& p1 : poly) { // 'a' is the (next) new point between p0 and p1 Point p0p1 = p1 - *p0; int64_t p0p1_size = vSize(p0p1); int64_t dist_last_point = dist_left_over + p0p1_size * 2; // so that p0p1_size - dist_last_point evaulates to dist_left_over - p0p1_size for (int64_t p0pa_dist = dist_left_over; p0pa_dist < p0p1_size; p0pa_dist += min_dist_between_points + rand() % range_random_point_dist) { int r = rand() % (fuzziness * 2) - fuzziness; Point perp_to_p0p1 = crossZ(p0p1); Point fuzz = normal(perp_to_p0p1, r); Point pa = *p0 + normal(p0p1, p0pa_dist) + fuzz; result.add(pa); dist_last_point = p0pa_dist; } dist_left_over = p0p1_size - dist_last_point; p0 = &p1; } while (result.size() < 3 ) { unsigned int point_idx = poly.size() - 2; result.add(poly[point_idx]); if (point_idx == 0) { break; } point_idx--; } if (result.size() < 3) { result.clear(); for (Point& p : poly) result.add(p); } } skin = results; sendPolygons(Inset0Type, layer_nr, skin, mesh.getSettingInMicrons("wall_line_width_0")); } } }