void addLineInfill(Polygons& result, PointMatrix matrix, int scanline_min_idx, int lineSpacing, AABB boundary, std::vector<std::vector<int64_t> > cutList, int extrusionWidth) { auto addLine = [&](Point from, Point to) { PolygonRef p = result.newPoly(); p.add(matrix.unapply(from)); p.add(matrix.unapply(to)); }; auto compare_int64_t = [](const void* a, const void* b) { int64_t n = (*(int64_t*)a) - (*(int64_t*)b); if (n < 0) return -1; if (n > 0) return 1; return 0; }; int scanline_idx = 0; for(int64_t x = scanline_min_idx * lineSpacing; x < boundary.max.X; x += lineSpacing) { qsort(cutList[scanline_idx].data(), cutList[scanline_idx].size(), sizeof(int64_t), compare_int64_t); for(unsigned int i = 0; i + 1 < cutList[scanline_idx].size(); i+=2) { if (cutList[scanline_idx][i+1] - cutList[scanline_idx][i] < extrusionWidth / 5) continue; addLine(Point(x, cutList[scanline_idx][i]), Point(x, cutList[scanline_idx][i+1])); } scanline_idx += 1; } }
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 = turn90CCW(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; } } }
void FffPolygonGenerator::processInsets(SliceMeshStorage& mesh, unsigned int layer_nr) { SliceLayer* layer = &mesh.layers[layer_nr]; if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::SURFACE) { int inset_count = mesh.getSettingAsCount("wall_line_count"); if (mesh.getSettingBoolean("magic_spiralize") && static_cast<int>(layer_nr) < mesh.getSettingAsCount("bottom_layers") && layer_nr % 2 == 1)//Add extra insets every 2 layers when spiralizing, this makes bottoms of cups watertight. inset_count += 5; int line_width_x = mesh.getSettingInMicrons("wall_line_width_x"); int line_width_0 = mesh.getSettingInMicrons("wall_line_width_0"); if (mesh.getSettingBoolean("alternate_extra_perimeter")) inset_count += layer_nr % 2; bool recompute_outline_based_on_outer_wall = mesh.getSettingBoolean("support_enable"); WallsComputation walls_computation(mesh.getSettingInMicrons("wall_0_inset"), line_width_0, line_width_x, inset_count, recompute_outline_based_on_outer_wall); walls_computation.generateInsets(layer); } if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL) { for (PolygonRef polyline : layer->openPolyLines) { Polygons segments; for (unsigned int point_idx = 1; point_idx < polyline.size(); point_idx++) { PolygonRef segment = segments.newPoly(); segment.add(polyline[point_idx-1]); segment.add(polyline[point_idx]); } } } }
void PrimeTower::generateGroundpoly(const SliceDataStorage& storage) { extruder_count = storage.meshgroup->getExtruderCount(); int64_t prime_tower_wall_thickness = storage.getSettingInMicrons("prime_tower_wall_thickness"); int64_t tower_size = storage.getSettingInMicrons("prime_tower_size"); if (prime_tower_wall_thickness * 2 < tower_size) { is_hollow = true; } PolygonRef p = ground_poly.newPoly(); int tower_distance = 0; int x = storage.getSettingInMicrons("prime_tower_position_x"); // storage.model_max.x int y = storage.getSettingInMicrons("prime_tower_position_y"); // storage.model_max.y p.add(Point(x + tower_distance, y + tower_distance)); p.add(Point(x + tower_distance, y + tower_distance + tower_size)); p.add(Point(x + tower_distance - tower_size, y + tower_distance + tower_size)); p.add(Point(x + tower_distance - tower_size, y + tower_distance)); middle = Point(x - tower_size / 2, y + tower_size / 2); if (is_hollow) { ground_poly = ground_poly.difference(ground_poly.offset(-prime_tower_wall_thickness)); } post_wipe_point = Point(x + tower_distance - tower_size / 2, y + tower_distance + tower_size / 2); }
void generateLineInfill(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, int infillOverlap, double rotation) { Polygons outline = in_outline.offset(extrusionWidth * infillOverlap / 100); PointMatrix matrix(rotation); outline.applyMatrix(matrix); AABB boundary(outline); boundary.min.X = ((boundary.min.X / lineSpacing) - 1) * lineSpacing; int lineCount = (boundary.max.X - boundary.min.X + (lineSpacing - 1)) / lineSpacing; vector<vector<int64_t> > cutList; for(int n=0; n<lineCount; n++) cutList.push_back(vector<int64_t>()); for(unsigned int polyNr=0; polyNr < outline.size(); polyNr++) { Point p1 = outline[polyNr][outline[polyNr].size()-1]; for(unsigned int i=0; i < outline[polyNr].size(); i++) { Point p0 = outline[polyNr][i]; int idx0 = (p0.X - boundary.min.X) / lineSpacing; int idx1 = (p1.X - boundary.min.X) / lineSpacing; int64_t xMin = p0.X, xMax = p1.X; if (p0.X > p1.X) { xMin = p1.X; xMax = p0.X; } if (idx0 > idx1) { int tmp = idx0; idx0 = idx1; idx1 = tmp; } for(int idx = idx0; idx<=idx1; idx++) { int x = (idx * lineSpacing) + boundary.min.X + lineSpacing / 2; if (x < xMin) continue; if (x >= xMax) continue; int y = p0.Y + (p1.Y - p0.Y) * (x - p0.X) / (p1.X - p0.X); cutList[idx].push_back(y); } p1 = p0; } } int idx = 0; for(int64_t x = boundary.min.X + lineSpacing / 2; x < boundary.max.X; x += lineSpacing) { std::sort(cutList[idx].begin(), cutList[idx].end()); for(unsigned int i = 0; i + 1 < cutList[idx].size(); i+=2) { if (cutList[idx][i+1] - cutList[idx][i] < extrusionWidth / 5) continue; PolygonRef p = result.newPoly(); p.add(matrix.unapply(Point(x, cutList[idx][i]))); p.add(matrix.unapply(Point(x, cutList[idx][i+1]))); } idx += 1; } }
void PrimeTower::generateGroundpoly(SliceDataStorage& storage) { PolygonRef p = storage.primeTower.ground_poly.newPoly(); int tower_size = storage.getSettingInMicrons("prime_tower_size"); int tower_distance = 0; int x = storage.getSettingInMicrons("prime_tower_position_x"); // storage.model_max.x int y = storage.getSettingInMicrons("prime_tower_position_y"); // storage.model_max.y p.add(Point(x + tower_distance, y + tower_distance)); p.add(Point(x + tower_distance, y + tower_distance + tower_size)); p.add(Point(x + tower_distance - tower_size, y + tower_distance + tower_size)); p.add(Point(x + tower_distance - tower_size, y + tower_distance)); storage.wipePoint = Point(x + tower_distance - tower_size / 2, y + tower_distance + tower_size / 2); }
void ListPolyIt::convertListPolygonToPolygon(ListPolygon& list_polygon, PolygonRef polygon) { for (Point& p : list_polygon) { polygon.add(p); } }
void WallOverlapComputation::convertListPolygonToPolygon(ListPolygon& poly, PolygonRef result) { result.clear(); for (Point& p : poly) { result.add(p); } }
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 PrimeTower::generatePaths_OLD(SliceDataStorage& storage, unsigned int total_layers) { if (storage.max_object_height_second_to_last_extruder >= 0 && storage.getSettingBoolean("prime_tower_enable")) { PolygonRef p = storage.primeTower.ground_poly.newPoly(); int tower_size = storage.getSettingInMicrons("prime_tower_size"); int tower_distance = 0; int x = storage.getSettingInMicrons("prime_tower_position_x"); // storage.model_max.x int y = storage.getSettingInMicrons("prime_tower_position_y"); // storage.model_max.y p.add(Point(x + tower_distance, y + tower_distance)); p.add(Point(x + tower_distance, y + tower_distance + tower_size)); p.add(Point(x + tower_distance - tower_size, y + tower_distance + tower_size)); p.add(Point(x + tower_distance - tower_size, y + tower_distance)); storage.wipePoint = Point(x + tower_distance - tower_size / 2, y + tower_distance + tower_size / 2); } }
void FffPolygonGenerator::processInsets(SliceDataStorage& storage, unsigned int layer_nr) { for(SliceMeshStorage& mesh : storage.meshes) { SliceLayer* layer = &mesh.layers[layer_nr]; if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::SURFACE) { int inset_count = mesh.getSettingAsCount("wall_line_count"); if (mesh.getSettingBoolean("magic_spiralize") && static_cast<int>(layer_nr) < mesh.getSettingAsCount("bottom_layers") && layer_nr % 2 == 1)//Add extra insets every 2 layers when spiralizing, this makes bottoms of cups watertight. inset_count += 5; int line_width_x = mesh.getSettingInMicrons("wall_line_width_x"); int line_width_0 = mesh.getSettingInMicrons("wall_line_width_0"); if (mesh.getSettingBoolean("alternate_extra_perimeter")) inset_count += layer_nr % 2; generateInsets(layer, mesh.getSettingInMicrons("machine_nozzle_size"), line_width_0, line_width_x, inset_count, mesh.getSettingBoolean("remove_overlapping_walls_0_enabled"), mesh.getSettingBoolean("remove_overlapping_walls_x_enabled")); for(unsigned int partNr=0; partNr<layer->parts.size(); partNr++) { if (layer->parts[partNr].insets.size() > 0) { // sendPolygons(Inset0Type, layer_nr, layer->parts[partNr].insets[0], line_width_0); // done after processing fuzzy skin for(unsigned int inset=1; inset<layer->parts[partNr].insets.size(); inset++) sendPolygons(InsetXType, layer_nr, layer->parts[partNr].insets[inset], line_width_x); } } } if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL) { for (PolygonRef polyline : layer->openPolyLines) { Polygons segments; for (unsigned int point_idx = 1; point_idx < polyline.size(); point_idx++) { PolygonRef segment = segments.newPoly(); segment.add(polyline[point_idx-1]); segment.add(polyline[point_idx]); } sendPolygons(Inset0Type, layer_nr, segments, mesh.getSettingInMicrons("wall_line_width_0")); } } } }
void FffPolygonGenerator::processWipeTower(SliceDataStorage& storage, unsigned int totalLayers) { // TODO: move this code into its own function? { // compute storage.max_object_height_second_to_last_extruder, which is used to determine the highest point in the wipe tower int max_object_height_per_extruder[MAX_EXTRUDERS]; { // compute max_object_height_per_extruder memset(max_object_height_per_extruder, -1, sizeof(max_object_height_per_extruder)); for (SliceMeshStorage& mesh : storage.meshes) { max_object_height_per_extruder[mesh.settings->getSettingAsIndex("extruder_nr")] = std::max( max_object_height_per_extruder[mesh.settings->getSettingAsIndex("extruder_nr")] , mesh.layer_nr_max_filled_layer ); } int support_extruder_nr = getSettingAsIndex("support_extruder_nr"); max_object_height_per_extruder[support_extruder_nr] = std::max( max_object_height_per_extruder[support_extruder_nr] , storage.support.layer_nr_max_filled_layer ); } { // // compute max_object_height_second_to_last_extruder int extruder_max_object_height = 0; for (unsigned int extruder_nr = 1; extruder_nr < MAX_EXTRUDERS; extruder_nr++) { if (max_object_height_per_extruder[extruder_nr] > max_object_height_per_extruder[extruder_max_object_height]) { extruder_max_object_height = extruder_nr; } } int extruder_second_max_object_height = -1; for (int extruder_nr = 0; extruder_nr < MAX_EXTRUDERS; extruder_nr++) { if (extruder_nr == extruder_max_object_height) { continue; } if (max_object_height_per_extruder[extruder_nr] > max_object_height_per_extruder[extruder_second_max_object_height]) { extruder_second_max_object_height = extruder_nr; } } if (extruder_second_max_object_height < 0) { storage.max_object_height_second_to_last_extruder = -1; } else { storage.max_object_height_second_to_last_extruder = max_object_height_per_extruder[extruder_second_max_object_height]; } } } if (storage.max_object_height_second_to_last_extruder >= 0 && getSettingInMicrons("wipe_tower_distance") > 0 && getSettingInMicrons("wipe_tower_size") > 0) { PolygonRef p = storage.wipeTower.newPoly(); int tower_size = getSettingInMicrons("wipe_tower_size"); int tower_distance = getSettingInMicrons("wipe_tower_distance"); p.add(Point(storage.model_min.x - tower_distance, storage.model_max.y + tower_distance)); p.add(Point(storage.model_min.x - tower_distance, storage.model_max.y + tower_distance + tower_size)); p.add(Point(storage.model_min.x - tower_distance - tower_size, storage.model_max.y + tower_distance + tower_size)); p.add(Point(storage.model_min.x - tower_distance - tower_size, storage.model_max.y + tower_distance)); storage.wipePoint = Point(storage.model_min.x - tower_distance - tower_size / 2, storage.model_max.y + tower_distance + tower_size / 2); } }
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); } }
void generateZigZagIninfill_noEndPieces(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation) { if (in_outline.size() == 0) return; Polygons outline = in_outline.offset(extrusionWidth * infillOverlap / 100 - extrusionWidth / 2); if (outline.size() == 0) return; PointMatrix matrix(rotation); outline.applyMatrix(matrix); auto addLine = [&](Point from, Point to) { PolygonRef p = result.newPoly(); p.add(matrix.unapply(from)); p.add(matrix.unapply(to)); }; AABB boundary(outline); int scanline_min_idx = boundary.min.X / lineSpacing; int lineCount = (boundary.max.X + (lineSpacing - 1)) / lineSpacing - scanline_min_idx; std::vector<std::vector<int64_t> > cutList; // mapping from scanline to all intersections with polygon segments for(int n=0; n<lineCount; n++) cutList.push_back(std::vector<int64_t>()); for(unsigned int polyNr=0; polyNr < outline.size(); polyNr++) { std::vector<Point> firstBoundarySegment; std::vector<Point> boundarySegment; bool isFirstBoundarySegment = true; bool firstBoundarySegmentEndsInEven; bool isEvenScanSegment = false; Point p0 = outline[polyNr][outline[polyNr].size()-1]; for(unsigned int i=0; i < outline[polyNr].size(); i++) { Point p1 = outline[polyNr][i]; int64_t xMin = p1.X, xMax = p0.X; if (xMin == xMax) { p0 = p1; continue; } if (xMin > xMax) { xMin = p0.X; xMax = p1.X; } int scanline_idx0 = (p0.X + ((p0.X > 0)? -1 : -lineSpacing)) / lineSpacing; // -1 cause a linesegment on scanline x counts as belonging to scansegment x-1 ... int scanline_idx1 = (p1.X + ((p1.X > 0)? -1 : -lineSpacing)) / lineSpacing; // -linespacing because a line between scanline -n and -n-1 belongs to scansegment -n-1 (for n=positive natural number) int direction = 1; if (p0.X > p1.X) { direction = -1; scanline_idx1 += 1; // only consider the scanlines in between the scansegments } else scanline_idx0 += 1; // only consider the scanlines in between the scansegments if (isFirstBoundarySegment) firstBoundarySegment.push_back(p0); else boundarySegment.push_back(p0); for(int scanline_idx = scanline_idx0; scanline_idx != scanline_idx1+direction; scanline_idx+=direction) { int x = scanline_idx * lineSpacing; int y = p1.Y + (p0.Y - p1.Y) * (x - p1.X) / (p0.X - p1.X); cutList[scanline_idx - scanline_min_idx].push_back(y); bool last_isEvenScanSegment = isEvenScanSegment; if (scanline_idx % 2 == 0) isEvenScanSegment = true; else isEvenScanSegment = false; if (!isFirstBoundarySegment) { if (last_isEvenScanSegment && !isEvenScanSegment) { // add whole boundarySegment (including the just obtained point) for (unsigned int p = 1; p < boundarySegment.size(); p++) { addLine(boundarySegment[p-1], boundarySegment[p]); } addLine(boundarySegment[boundarySegment.size()-1], Point(x,y)); boundarySegment.clear(); } else if (isEvenScanSegment) // we are either in an end piece or an uneven boundary segment { boundarySegment.clear(); boundarySegment.emplace_back(x,y); } else boundarySegment.clear(); } if (isFirstBoundarySegment) { firstBoundarySegment.emplace_back(x,y); firstBoundarySegmentEndsInEven = isEvenScanSegment; isFirstBoundarySegment = false; boundarySegment.emplace_back(x,y); } } if (!isFirstBoundarySegment && isEvenScanSegment) boundarySegment.push_back(p1); p0 = p1; } if (!isFirstBoundarySegment && isEvenScanSegment && !firstBoundarySegmentEndsInEven) { for (unsigned int i = 1; i < firstBoundarySegment.size() ; i++) addLine(firstBoundarySegment[i-1], firstBoundarySegment[i]); } } addLineInfill(result, matrix, scanline_min_idx, lineSpacing, boundary, cutList, extrusionWidth); }
/*! * adapted from generateLineInfill(.) * * generate lines within the area of [in_outline], at regular intervals of [lineSpacing] * idea: * intersect a regular grid of 'scanlines' with the area inside [in_outline] * sigzag: * include pieces of boundary, connecting the lines, forming an accordion like zigzag instead of separate lines |_|^|_| * * we call the areas between two consecutive scanlines a 'scansegment' * * 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 * * 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]) * if polygon intersects with even scanline again (instead of odd) * dont add the last line segment to the boundary (unless [connect_zigzags]) * * * <-- * ___ * | | | * | | | * | |___| * --> * * ^ = even scanline * * start boundary from even scanline! :D * * * _____ * | | | , * | | | | * |_____| |__/ * * ^ ^ ^ scanlines * ^ disconnected end piece */ void generateZigZagIninfill_endPieces(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation, bool connect_zigzags) { // if (in_outline.size() == 0) return; // Polygons outline = in_outline.offset(extrusionWidth * infillOverlap / 100 - extrusionWidth / 2); Polygons empty; Polygons outline = in_outline.difference(empty); // copy if (outline.size() == 0) return; PointMatrix matrix(rotation); outline.applyMatrix(matrix); auto addLine = [&](Point from, Point to) { PolygonRef p = result.newPoly(); p.add(matrix.unapply(from)); p.add(matrix.unapply(to)); }; AABB boundary(outline); int scanline_min_idx = boundary.min.X / lineSpacing; int lineCount = (boundary.max.X + (lineSpacing - 1)) / lineSpacing - scanline_min_idx; std::vector<std::vector<int64_t> > cutList; // mapping from scanline to all intersections with polygon segments for(int n=0; n<lineCount; n++) cutList.push_back(std::vector<int64_t>()); for(unsigned int polyNr=0; polyNr < outline.size(); polyNr++) { std::vector<Point> firstBoundarySegment; std::vector<Point> unevenBoundarySegment; // stored cause for connected_zigzags a boundary segment which ends in an uneven scanline needs to be included bool isFirstBoundarySegment = true; bool firstBoundarySegmentEndsInEven; bool isEvenScanSegment = false; Point p0 = outline[polyNr][outline[polyNr].size()-1]; Point lastPoint = p0; for(unsigned int i=0; i < outline[polyNr].size(); i++) { Point p1 = outline[polyNr][i]; int64_t xMin = p1.X, xMax = p0.X; if (xMin == xMax) { lastPoint = p1; p0 = p1; continue; } if (xMin > xMax) { xMin = p0.X; xMax = p1.X; } int scanline_idx0 = (p0.X + ((p0.X > 0)? -1 : -lineSpacing)) / lineSpacing; // -1 cause a linesegment on scanline x counts as belonging to scansegment x-1 ... int scanline_idx1 = (p1.X + ((p1.X > 0)? -1 : -lineSpacing)) / lineSpacing; // -linespacing because a line between scanline -n and -n-1 belongs to scansegment -n-1 (for n=positive natural number) int direction = 1; if (p0.X > p1.X) { direction = -1; scanline_idx1 += 1; // only consider the scanlines in between the scansegments } else scanline_idx0 += 1; // only consider the scanlines in between the scansegments if (isFirstBoundarySegment) firstBoundarySegment.push_back(p0); for(int scanline_idx = scanline_idx0; scanline_idx != scanline_idx1+direction; scanline_idx+=direction) { int x = scanline_idx * lineSpacing; int y = p1.Y + (p0.Y - p1.Y) * (x - p1.X) / (p0.X - p1.X); cutList[scanline_idx - scanline_min_idx].push_back(y); bool last_isEvenScanSegment = isEvenScanSegment; if (scanline_idx % 2 == 0) isEvenScanSegment = true; else isEvenScanSegment = false; if (!isFirstBoundarySegment) { if (last_isEvenScanSegment && (connect_zigzags || !isEvenScanSegment)) addLine(lastPoint, Point(x,y)); else if (connect_zigzags && !last_isEvenScanSegment && !isEvenScanSegment) // if we end an uneven boundary in an uneven segment { // add whole unevenBoundarySegment (including the just obtained point) for (unsigned int p = 1; p < unevenBoundarySegment.size(); p++) { addLine(unevenBoundarySegment[p-1], unevenBoundarySegment[p]); } addLine(unevenBoundarySegment[unevenBoundarySegment.size()-1], Point(x,y)); unevenBoundarySegment.clear(); } if (connect_zigzags && last_isEvenScanSegment && !isEvenScanSegment) unevenBoundarySegment.push_back(Point(x,y)); else unevenBoundarySegment.clear(); } lastPoint = Point(x,y); if (isFirstBoundarySegment) { firstBoundarySegment.emplace_back(x,y); firstBoundarySegmentEndsInEven = isEvenScanSegment; isFirstBoundarySegment = false; } } if (!isFirstBoundarySegment) { if (isEvenScanSegment) addLine(lastPoint, p1); else if (connect_zigzags) unevenBoundarySegment.push_back(p1); } lastPoint = p1; p0 = p1; } if (isEvenScanSegment || isFirstBoundarySegment || connect_zigzags) { for (unsigned int i = 1; i < firstBoundarySegment.size() ; i++) { if (i < firstBoundarySegment.size() - 1 || !firstBoundarySegmentEndsInEven || connect_zigzags) // only add last element if connect_zigzags or boundary segment ends in uneven scanline addLine(firstBoundarySegment[i-1], firstBoundarySegment[i]); } } else if (!firstBoundarySegmentEndsInEven) addLine(firstBoundarySegment[firstBoundarySegment.size()-2], firstBoundarySegment[firstBoundarySegment.size()-1]); } if (cutList.size() == 0) return; if (connect_zigzags && cutList.size() == 1 && cutList[0].size() <= 2) return; // don't add connection if boundary already contains whole outline! addLineInfill(result, matrix, scanline_min_idx, lineSpacing, boundary, cutList, extrusionWidth); }
void GCodeExport::writeMove(int x, int y, int z, double speed, double extrusion_mm3_per_mm) { if (currentPosition.x == x && currentPosition.y == y && currentPosition.z == z) return; #ifdef ASSERT_INSANE_OUTPUT assert(speed < 200 && speed > 1); // normal F values occurring in UM2 gcode (this code should not be compiled for release) assert(currentPosition != no_point3); assert((Point3(x,y,z) - currentPosition).vSize() < MM2INT(300)); // no crazy positions (this code should not be compiled for release) #endif //ASSERT_INSANE_OUTPUT if (extrusion_mm3_per_mm < 0) logWarning("Warning! Negative extrusion move!"); if (flavor == EGCodeFlavor::BFB) { writeMoveBFB(x, y, z, speed, extrusion_mm3_per_mm); return; } double extrusion_per_mm = extrusion_mm3_per_mm; if (!is_volumatric) { extrusion_per_mm = extrusion_mm3_per_mm / extruder_attr[current_extruder].filament_area; } Point gcode_pos = getGcodePos(x,y, current_extruder); if (extrusion_mm3_per_mm > 0.000001) { Point3 diff = Point3(x,y,z) - getPosition(); if (isZHopped > 0) { *output_stream << std::setprecision(3) << "G1 Z" << INT2MM(currentPosition.z) << "\n"; isZHopped = 0; } double prime_volume = extruder_attr[current_extruder].prime_volume; current_e_value += (is_volumatric) ? prime_volume : prime_volume / extruder_attr[current_extruder].filament_area; if (extruder_attr[current_extruder].retraction_e_amount_current) { if (firmware_retract) { // note that BFB is handled differently *output_stream << "G11\n"; //Assume default UM2 retraction settings. if (prime_volume > 0) { *output_stream << "G1 F" << (extruder_attr[current_extruder].last_retraction_prime_speed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value << "\n"; currentSpeed = extruder_attr[current_extruder].last_retraction_prime_speed; } estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), current_e_value), 25.0); } else { current_e_value += extruder_attr[current_extruder].retraction_e_amount_current; *output_stream << "G1 F" << (extruder_attr[current_extruder].last_retraction_prime_speed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value << "\n"; currentSpeed = extruder_attr[current_extruder].last_retraction_prime_speed; estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), current_e_value), currentSpeed); } if (getCurrentExtrudedVolume() > 10000.0) //According to https://github.com/Ultimaker/CuraEngine/issues/14 having more then 21m of extrusion causes inaccuracies. So reset it every 10m, just to be sure. { resetExtrusionValue(); } extruder_attr[current_extruder].retraction_e_amount_current = 0.0; } else if (prime_volume > 0.0) { current_e_value += prime_volume; *output_stream << "G1 F" << (extruder_attr[current_extruder].last_retraction_prime_speed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value << "\n"; currentSpeed = extruder_attr[current_extruder].last_retraction_prime_speed; estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), current_e_value), currentSpeed); } extruder_attr[current_extruder].prime_volume = 0.0; current_e_value += extrusion_per_mm * diff.vSizeMM(); *output_stream << "G1"; } else { *output_stream << "G0"; if (commandSocket) { // we should send this travel as a non-retraction move cura::Polygons travelPoly; PolygonRef travel = travelPoly.newPoly(); travel.add(Point(currentPosition.x, currentPosition.y)); travel.add(Point(x, y)); commandSocket->sendPolygons(extruder_attr[current_extruder].retraction_e_amount_current ? PrintFeatureType::MoveRetraction : PrintFeatureType::MoveCombing, layer_nr, travelPoly, extruder_attr[current_extruder].retraction_e_amount_current ? MM2INT(0.2) : MM2INT(0.1)); } } if (currentSpeed != speed) { *output_stream << " F" << (speed * 60); currentSpeed = speed; } *output_stream << std::setprecision(3) << " X" << INT2MM(gcode_pos.X) << " Y" << INT2MM(gcode_pos.Y); if (z != currentPosition.z + isZHopped) *output_stream << " Z" << INT2MM(z + isZHopped); if (extrusion_mm3_per_mm > 0.000001) *output_stream << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value; *output_stream << "\n"; currentPosition = Point3(x, y, z); estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), current_e_value), speed); }
void SlicerLayer::makePolygons(OptimizedVolume* ov, bool keepNoneClosed, bool extensiveStitching) { 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; 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, MM2INT(0.01))) { 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 < 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 (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) { 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; } } } /* int q=0; for(unsigned int i=0;i<openPolygonList.size();i++) { if (openPolygonList[i].size() < 2) continue; if (!q) log("***\n"); log("S: %f %f\n", float(openPolygonList[i][0].X), float(openPolygonList[i][0].Y)); log("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 = 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); }
void GCodeExport::writeMove(int x, int y, int z, double speed, double extrusion_mm3_per_mm) { if (currentPosition.x == x && currentPosition.y == y && currentPosition.z == z) return; assert(speed < 200 && speed > 1); // normal F values occurring in UM2 gcode (this code should not be compiled for release) assert((Point3(x,y,z) - currentPosition).vSize() < MM2INT(300)); // no crazy positions (this code should not be compiled for release) if (extrusion_mm3_per_mm < 0) logWarning("Warning! Negative extrusion move!"); double extrusion_per_mm = extrusion_mm3_per_mm; if (!is_volumatric) { extrusion_per_mm = extrusion_mm3_per_mm / getFilamentArea(current_extruder); } Point gcode_pos = getGcodePos(x,y, current_extruder); if (flavor == EGCodeFlavor::BFB) { //For Bits From Bytes machines, we need to handle this completely differently. As they do not use E values but RPM values. float fspeed = speed * 60; float rpm = extrusion_per_mm * speed * 60; const float mm_per_rpm = 4.0; //All BFB machines have 4mm per RPM extrusion. rpm /= mm_per_rpm; if (rpm > 0) { if (isRetracted) { if (currentSpeed != double(rpm)) { //fprintf(f, "; %f e-per-mm %d mm-width %d mm/s\n", extrusion_per_mm, lineWidth, speed); //fprintf(f, "M108 S%0.1f\r\n", rpm); *output_stream << "M108 S" << std::setprecision(1) << rpm << "\r\n"; currentSpeed = double(rpm); } //Add M101 or M201 to enable the proper extruder. *output_stream << "M" << int((current_extruder + 1) * 100 + 1) << "\r\n"; isRetracted = false; } //Fix the speed by the actual RPM we are asking, because of rounding errors we cannot get all RPM values, but we have a lot more resolution in the feedrate value. // (Trick copied from KISSlicer, thanks Jonathan) fspeed *= (rpm / (roundf(rpm * 100) / 100)); //Increase the extrusion amount to calculate the amount of filament used. Point3 diff = Point3(x,y,z) - getPosition(); extrusion_amount += extrusion_per_mm * diff.vSizeMM(); }else{ //If we are not extruding, check if we still need to disable the extruder. This causes a retraction due to auto-retraction. if (!isRetracted) { *output_stream << "M103\r\n"; isRetracted = true; } } *output_stream << std::setprecision(3) << "G1 X" << INT2MM(gcode_pos.X) << " Y" << INT2MM(gcode_pos.Y) << " Z" << INT2MM(z) << std::setprecision(1) << " F" << fspeed << "\r\n"; } else { //Normal E handling. if (extrusion_mm3_per_mm > 0.000001) { Point3 diff = Point3(x,y,z) - getPosition(); if (isZHopped > 0) { // TinyG G1: Straight feed *output_stream << std::setprecision(3) << "G1 Z" << INT2MM(currentPosition.z) << "\n"; isZHopped = 0; } extrusion_amount += (is_volumatric) ? last_coasted_amount_mm3 : last_coasted_amount_mm3 / getFilamentArea(current_extruder); if (isRetracted) { if (flavor == EGCodeFlavor::ULTIGCODE || flavor == EGCodeFlavor::REPRAP_VOLUMATRIC) { *output_stream << "G11\n"; //TODO try this code and see what happens //Assume default UM2 retraction settings. if (last_coasted_amount_mm3 > 0) { *output_stream << "G1 F" << (retractionPrimeSpeed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << extrusion_amount << "\n"; } estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), extrusion_amount), 25.0); }else{ // TinyG checked *output_stream << "G1 F" << (retractionPrimeSpeed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << extrusion_amount << "\n"; currentSpeed = retractionPrimeSpeed; estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), extrusion_amount), currentSpeed); } if (getExtrusionAmountMM3(current_extruder) > 10000.0) //According to https://github.com/Ultimaker/CuraEngine/issues/14 having more then 21m of extrusion causes inaccuracies. So reset it every 10m, just to be sure. resetExtrusionValue(); isRetracted = false; } else { if (last_coasted_amount_mm3 > 0) { *output_stream << "G1 F" << (retractionPrimeSpeed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << extrusion_amount << "\n"; estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), extrusion_amount), currentSpeed); } } last_coasted_amount_mm3 = 0; extrusion_amount += extrusion_per_mm * diff.vSizeMM(); // TinyG TODO: add one axis *output_stream << "G1"; }else{ *output_stream << "G0"; if (commandSocket) { // we should send this travel as a non-retraction move cura::Polygons travelPoly; PolygonRef travel = travelPoly.newPoly(); travel.add(Point(currentPosition.x, currentPosition.y)); travel.add(Point(x, y)); commandSocket->sendPolygons(isRetracted ? MoveRetractionType : MoveCombingType, layer_nr, travelPoly, isRetracted ? MM2INT(0.2) : MM2INT(0.1)); } } if (currentSpeed != speed) { *output_stream << " F" << (speed * 60); currentSpeed = speed; } *output_stream << std::setprecision(3) << " X" << INT2MM(gcode_pos.X) << " Y" << INT2MM(gcode_pos.Y); if (z != currentPosition.z) *output_stream << " Z" << INT2MM(z + isZHopped); if (extrusion_mm3_per_mm > 0.000001) *output_stream << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << extrusion_amount; *output_stream << "\n"; } currentPosition = Point3(x, y, z); startPosition = currentPosition; estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), extrusion_amount), speed); }
void generateTroctInfill(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, int infillOverlap, double rotation, int posZ) { Polygons outline = in_outline.offset(extrusionWidth * infillOverlap / 100); PointMatrix matrix(rotation); outline.applyMatrix(matrix); AABB boundary(outline); // ignore infill for areas smaller than line spacing if((abs(boundary.min.X - boundary.max.X) + abs(boundary.min.Y - boundary.max.Y)) < lineSpacing){ return; } // fix to normalise against diagonal infill lineSpacing = lineSpacing * 2; uint64_t Zscale = SQRT2MUL(lineSpacing); int offset = abs(posZ % (Zscale) - (Zscale/2)) - (Zscale/4); boundary.min.X = ((boundary.min.X / lineSpacing) - 1) * lineSpacing; boundary.min.Y = ((boundary.min.Y / lineSpacing) - 1) * lineSpacing; unsigned int lineCountX = (boundary.max.X - boundary.min.X + (lineSpacing - 1)) / lineSpacing; unsigned int lineCountY = (boundary.max.Y - boundary.min.Y + (lineSpacing - 1)) / lineSpacing; int rtMod = int(rotation / 90) % 2; // with an odd number of lines, sides need to be swapped around if(rtMod == 1){ rtMod = (lineCountX + int(rotation / 90)) % 2; } // draw non-horizontal walls of octohedrons Polygons po; PolygonRef p = po.newPoly(); for(unsigned int ly=0; ly < lineCountY;){ for(size_t it = 0; it < 2; ly++, it++){ int side = (2*((ly + it + rtMod) % 2) - 1); int y = (ly * lineSpacing) + boundary.min.Y + lineSpacing / 2 - (offset/2 * side); int x = boundary.min.X-(offset/2); if(it == 1){ x = (lineCountX * (lineSpacing)) + boundary.min.X + lineSpacing / 2 - (offset/2); } p.add(Point(x,y)); for(unsigned int lx=0; lx < lineCountX; lx++){ if(it == 1){ side = (2*((lx + ly + it + rtMod + lineCountX) % 2) - 1); y = (ly * lineSpacing) + boundary.min.Y + lineSpacing / 2 + (offset/2 * side); x = ((lineCountX - lx - 1) * lineSpacing) + boundary.min.X + lineSpacing / 2; p.add(Point(x+lineSpacing-abs(offset/2), y)); p.add(Point(x+abs(offset/2), y)); } else { side = (2*((lx + ly + it + rtMod) % 2) - 1); y = (ly * lineSpacing) + boundary.min.Y + lineSpacing / 2 + (offset/2 * side); x = (lx * lineSpacing) + boundary.min.X + lineSpacing / 2; p.add(Point(x+abs(offset/2), y)); p.add(Point(x+lineSpacing-abs(offset/2), y)); } } x = (lineCountX * lineSpacing) + boundary.min.X + lineSpacing / 2 - (offset/2); if(it == 1){ x = boundary.min.X-(offset/2); } y = (ly * lineSpacing) + boundary.min.Y + lineSpacing / 2 - (offset/2 * side); p.add(Point(x,y)); } } // Generate tops / bottoms of octohedrons if(abs((abs(offset) - Zscale/4)) < (extrusionWidth/2)){ uint64_t startLine = (offset < 0) ? 0 : 1; uint64_t coverWidth = OCTSLEN(lineSpacing); vector<Point> points; for(size_t xi = 0; xi < (lineCountX+1); xi++){ for(size_t yi = 0; yi < (lineCountY); yi += 2){ points.push_back(Point(boundary.min.X + OCTDLEN(lineSpacing) + (xi - startLine + rtMod) * lineSpacing, boundary.min.Y + OCTDLEN(lineSpacing) + (yi + (xi%2)) * lineSpacing + extrusionWidth/2)); } } uint64_t order = 0; for(Point pp : points){ PolygonRef p = po.newPoly(); for(size_t yi = 0; yi <= coverWidth; yi += extrusionWidth) { if(order == 0){ p.add(Point(pp.X, pp.Y + yi)); p.add(Point(pp.X + coverWidth + extrusionWidth, pp.Y + yi)); } else { p.add(Point(pp.X + coverWidth + extrusionWidth, pp.Y + yi)); p.add(Point(pp.X, pp.Y + yi)); } order = (order + 1) % 2; } } } // intersect with outline polygon(s) Polygons pi = po.intersection(outline); // Hack to add intersection to result. There doesn't seem // to be a direct way to do this for(unsigned int polyNr=0; polyNr < pi.size(); polyNr++) { PolygonRef p = result.newPoly(); // = result.newPoly() for(unsigned int i=0; i < pi[polyNr].size(); i++) { Point p0 = pi[polyNr][i]; p.add(matrix.unapply(Point(p0.X,p0.Y))); } } }