Point PrimeTower::getLocationBeforePrimeTower(const SliceDataStorage& storage) const { Point ret(0, 0); int absolute_starting_points = 0; for (int extruder_nr = 0; extruder_nr < storage.meshgroup->getExtruderCount(); extruder_nr++) { ExtruderTrain& train = *storage.meshgroup->getExtruderTrain(0); if (train.getSettingBoolean("machine_extruder_start_pos_abs")) { ret += Point(train.getSettingInMicrons("machine_extruder_start_pos_x"), train.getSettingInMicrons("machine_extruder_start_pos_y")); absolute_starting_points++; } } if (absolute_starting_points > 0) { // take the average over all absolute starting positions ret /= absolute_starting_points; } else { // use the middle of the bed if (!storage.getSettingBoolean("machine_center_is_zero")) { ret = Point(storage.getSettingInMicrons("machine_width"), storage.getSettingInMicrons("machine_depth")) / 2; } // otherwise keep (0, 0) } return ret; }
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); }
int Raft::getFillerLayerHeight(const SliceDataStorage& storage) { if (storage.getSettingAsPlatformAdhesion("adhesion_type") != EPlatformAdhesion::RAFT) { const int64_t normal_layer_height = storage.getSettingInMicrons("layer_height"); return normal_layer_height; } const unsigned int filler_layer_height = round_divide(getZdiffBetweenRaftAndLayer1(storage), getFillerLayerCount(storage)); return filler_layer_height; }
void PrimeTower::computePrimeTowerMax(SliceDataStorage& storage) { // compute storage.max_object_height_second_to_last_extruder, which is used to determine the highest point in the prime tower extruder_count = storage.getSettingAsCount("machine_extruder_count"); int max_object_height_per_extruder[extruder_count]; { // 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.getSettingAsIndex("extruder_nr")] = std::max( max_object_height_per_extruder[mesh.getSettingAsIndex("extruder_nr")] , mesh.layer_nr_max_filled_layer ); } int support_extruder_nr = storage.getSettingAsIndex("support_extruder_nr"); // TODO: support extruder should be configurable per object 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 ); int support_roof_extruder_nr = storage.getSettingAsIndex("support_roof_extruder_nr"); // TODO: support roof extruder should be configurable per object max_object_height_per_extruder[support_roof_extruder_nr] = std::max( max_object_height_per_extruder[support_roof_extruder_nr] , storage.support.layer_nr_max_filled_layer ); } { // // compute max_object_height_second_to_last_extruder int extruder_max_object_height = 0; for (int extruder_nr = 1; extruder_nr < extruder_count; 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 < extruder_count; 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]; } } }
PrimeTower::PrimeTower(const SliceDataStorage& storage) : is_hollow(false) , wipe_from_middle(false) { enabled = storage.getSettingBoolean("prime_tower_enable") && storage.getSettingInMicrons("prime_tower_wall_thickness") > 10 && storage.getSettingInMicrons("prime_tower_size") > 10; if (enabled) { generateGroundpoly(storage); } }
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); }
int Raft::getZdiffBetweenRaftAndLayer1(const SliceDataStorage& storage) { const ExtruderTrain& train = *storage.meshgroup->getExtruderTrain(storage.getSettingAsIndex("adhesion_extruder_nr")); if (storage.getSettingAsPlatformAdhesion("adhesion_type") != EPlatformAdhesion::RAFT) { return 0; } const int64_t airgap = std::max((coord_t)0, train.getSettingInMicrons("raft_airgap")); const int64_t layer_0_overlap = storage.getSettingInMicrons("layer_0_z_overlap"); const int64_t layer_height_0 = storage.getSettingInMicrons("layer_height_0"); const int64_t z_diff_raft_to_bottom_of_layer_1 = std::max(int64_t(0), airgap + layer_height_0 - layer_0_overlap); return z_diff_raft_to_bottom_of_layer_1; }
void PrimeTower::generatePaths(SliceDataStorage& storage, unsigned int total_layers) { if (storage.max_object_height_second_to_last_extruder >= 0 && storage.getSettingBoolean("prime_tower_enable")) { generatePaths3(storage); } }
void FffPolygonGenerator::processOozeShield(SliceDataStorage& storage, unsigned int total_layers) { if (!getSettingBoolean("ooze_shield_enabled")) { return; } int ooze_shield_dist = getSettingInMicrons("ooze_shield_dist"); for(unsigned int layer_nr=0; layer_nr<total_layers; layer_nr++) { storage.oozeShield.push_back(storage.getLayerOutlines(layer_nr, true).offset(ooze_shield_dist)); } int largest_printed_radius = MM2INT(1.0); // TODO: make var a parameter, and perhaps even a setting? for(unsigned int layer_nr=0; layer_nr<total_layers; layer_nr++) { storage.oozeShield[layer_nr] = storage.oozeShield[layer_nr].offset(-largest_printed_radius).offset(largest_printed_radius); } int allowed_angle_offset = tan(getSettingInAngleRadians("ooze_shield_angle")) * getSettingInMicrons("layer_height");//Allow for a 60deg angle in the oozeShield. for(unsigned int layer_nr=1; layer_nr<total_layers; layer_nr++) { storage.oozeShield[layer_nr] = storage.oozeShield[layer_nr].unionPolygons(storage.oozeShield[layer_nr-1].offset(-allowed_angle_offset)); } for(unsigned int layer_nr=total_layers-1; layer_nr>0; layer_nr--) { storage.oozeShield[layer_nr-1] = storage.oozeShield[layer_nr-1].unionPolygons(storage.oozeShield[layer_nr].offset(-allowed_angle_offset)); } }
Comb::Comb(SliceDataStorage& storage, int layer_nr, Polygons& comb_boundary_inside, int64_t comb_boundary_offset, bool travel_avoid_other_parts, int64_t travel_avoid_distance) : storage(storage) , layer_nr(layer_nr) , offset_from_outlines(comb_boundary_offset) // between second wall and infill / other walls , max_moveInside_distance2(offset_from_outlines * 2 * offset_from_outlines * 2) , offset_from_outlines_outside(travel_avoid_distance) , offset_from_inside_to_outside(offset_from_outlines + offset_from_outlines_outside) , max_crossing_dist2(offset_from_inside_to_outside * offset_from_inside_to_outside * 2) // so max_crossing_dist = offset_from_inside_to_outside * sqrt(2) =approx 1.5 to allow for slightly diagonal crossings and slightly inaccurate crossing computation , avoid_other_parts(travel_avoid_other_parts) , boundary_inside( comb_boundary_inside ) , partsView_inside( boundary_inside.splitIntoPartsView() ) // WARNING !! changes the order of boundary_inside !! , inside_loc_to_line(PolygonUtils::createLocToLineGrid(boundary_inside, comb_boundary_offset)) , boundary_outside( [&storage, layer_nr, travel_avoid_distance]() { return storage.getLayerOutlines(layer_nr, false).offset(travel_avoid_distance); } ) , outside_loc_to_line( [](Comb* comber, const int64_t offset_from_inside_to_outside) { return PolygonUtils::createLocToLineGrid(comber->getBoundaryOutside(), offset_from_inside_to_outside * 3 / 2); } , this , offset_from_inside_to_outside ) { }
int Raft::getTotalThickness(const SliceDataStorage& storage) { const ExtruderTrain& train = *storage.meshgroup->getExtruderTrain(storage.getSettingAsIndex("adhesion_extruder_nr")); return train.getSettingInMicrons("raft_base_thickness") + train.getSettingInMicrons("raft_interface_thickness") + train.getSettingAsCount("raft_surface_layers") * train.getSettingInMicrons("raft_surface_thickness"); }
void AreaSupport::generateSupportInterface(SliceDataStorage& storage, const SliceMeshStorage& mesh, std::vector<Polygons>& support_areas, const unsigned int layer_count) { const unsigned int roof_layer_count = round_divide(mesh.getSettingInMicrons("support_roof_height"), storage.getSettingInMicrons("layer_height")); const unsigned int bottom_layer_count = round_divide(mesh.getSettingInMicrons("support_bottom_height"), storage.getSettingInMicrons("layer_height")); const unsigned int z_distance_bottom = round_up_divide(mesh.getSettingInMicrons("support_bottom_distance"), storage.getSettingInMicrons("layer_height")); const unsigned int z_distance_top = round_up_divide(mesh.getSettingInMicrons("support_top_distance"), storage.getSettingInMicrons("layer_height")); const int skip_layer_count = std::max(1u, round_divide(mesh.getSettingInMicrons("support_interface_skip_height"), storage.getSettingInMicrons("layer_height"))); const int interface_line_width = storage.meshgroup->getExtruderTrain(storage.getSettingAsIndex("support_interface_extruder_nr"))->getSettingInMicrons("support_interface_line_width"); std::vector<SupportLayer>& supportLayers = storage.support.supportLayers; for (unsigned int layer_idx = 0; layer_idx < layer_count; layer_idx++) { SupportLayer& layer = supportLayers[layer_idx]; const unsigned int top_layer_idx_above = layer_idx + roof_layer_count + z_distance_top; const unsigned int bottom_layer_idx_below = std::max(0, int(layer_idx) - int(bottom_layer_count) - int(z_distance_bottom)); if (top_layer_idx_above < supportLayers.size()) { Polygons roofs; if (roof_layer_count > 0) { Polygons model; const unsigned int n_scans = std::max(1u, (roof_layer_count - 1) / skip_layer_count); const float z_skip = std::max(1.0f, float(roof_layer_count - 1) / float(n_scans)); for (float layer_idx_above = top_layer_idx_above; layer_idx_above > layer_idx + z_distance_top; layer_idx_above -= z_skip) { const Polygons outlines_above = mesh.layers[std::round(layer_idx_above)].getOutlines(); model = model.unionPolygons(outlines_above); } roofs = support_areas[layer_idx].intersection(model); } Polygons bottoms; if (bottom_layer_count > 0) { Polygons model; const unsigned int n_scans = std::max(1u, (bottom_layer_count - 1) / skip_layer_count); const float z_skip = std::max(1.0f, float(bottom_layer_count - 1) / float(n_scans)); for (float layer_idx_below = bottom_layer_idx_below; std::round(layer_idx_below) < (int)(layer_idx - z_distance_bottom); layer_idx_below += z_skip) { const Polygons outlines_below = mesh.layers[std::round(layer_idx_below)].getOutlines(); model = model.unionPolygons(outlines_below); } bottoms = support_areas[layer_idx].intersection(model); } // expand skin a bit so that we're sure it's not too thin to be printed. Polygons skin = roofs.unionPolygons(bottoms).offset(interface_line_width).intersection(support_areas[layer_idx]); skin.removeSmallAreas(1.0); layer.skin.add(skin); layer.supportAreas.add(support_areas[layer_idx].difference(layer.skin)); } else { layer.skin.add(support_areas[layer_idx]); } } }
int Raft::getTotalExtraLayers(const SliceDataStorage& storage) { const ExtruderTrain& train = *storage.meshgroup->getExtruderTrain(storage.getSettingAsIndex("adhesion_extruder_nr")); if (train.getSettingAsPlatformAdhesion("adhesion_type") != EPlatformAdhesion::RAFT) { return 0; } return 2 + train.getSettingAsCount("raft_surface_layers") + getFillerLayerCount(storage); }
void PrimeTower::generatePaths(SliceDataStorage& storage, unsigned int total_layers) { if (storage.max_object_height_second_to_last_extruder >= 0 // && storage.getSettingInMicrons("prime_tower_distance") > 0 && storage.getSettingInMicrons("prime_tower_size") > 0) { generatePaths3(storage); } }
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 SkirtBrim::getFirstLayerOutline(SliceDataStorage& storage, const unsigned int primary_line_count, const int primary_extruder_skirt_brim_line_width, const bool is_skirt, const bool outside_only, Polygons& first_layer_outline) { bool external_only = is_skirt; // whether to include holes or not const int layer_nr = 0; if (is_skirt) { const bool include_helper_parts = true; first_layer_outline = storage.getLayerOutlines(layer_nr, include_helper_parts, external_only); first_layer_outline = first_layer_outline.approxConvexHull(); } else { // add brim underneath support by removing support where there's brim around the model const bool include_helper_parts = false; // include manually below first_layer_outline = storage.getLayerOutlines(layer_nr, include_helper_parts, external_only); first_layer_outline.add(storage.primeTower.ground_poly); // don't remove parts of the prime tower, but make a brim for it if (outside_only) { first_layer_outline = first_layer_outline.removeEmptyHoles(); } if (storage.support.generated && primary_line_count > 0) { // remove model-brim from support // avoid gap in the middle // V // +---+ +----+ // |+-+| |+--+| // || || ||[]|| > expand to fit an extra brim line // |+-+| |+--+| // +---+ +----+ const Polygons& model_brim_covered_area = first_layer_outline.offset(primary_extruder_skirt_brim_line_width * (primary_line_count + primary_line_count % 2)); // always leave a gap of an even number of brim lines, so that it fits if it's generating brim from both sides SupportLayer& support_layer = storage.support.supportLayers[0]; support_layer.supportAreas = support_layer.supportAreas.difference(model_brim_covered_area); first_layer_outline.add(support_layer.supportAreas); first_layer_outline.add(support_layer.skin); } } constexpr int join_distance = 20; first_layer_outline = first_layer_outline.offset(join_distance).offset(-join_distance); // merge adjacent models into single polygon constexpr int smallest_line_length = 200; constexpr int largest_error_of_removed_point = 50; first_layer_outline.simplify(smallest_line_length, largest_error_of_removed_point); // simplify for faster processing of the brim lines }
void Raft::generate(SliceDataStorage& storage, int distance) { assert(storage.raftOutline.size() == 0 && "Raft polygon isn't generated yet, so should be empty!"); storage.raftOutline = storage.getLayerOutlines(0, true).offset(distance, ClipperLib::jtRound); ExtruderTrain* train = storage.meshgroup->getExtruderTrain(storage.getSettingAsIndex("adhesion_extruder_nr")); const int shield_line_width_layer0 = train->getSettingInMicrons("skirt_brim_line_width"); if (storage.draft_protection_shield.size() > 0) { Polygons draft_shield_raft = storage.draft_protection_shield.offset(shield_line_width_layer0) // start half a line width outside shield .difference(storage.draft_protection_shield.offset(-distance - shield_line_width_layer0 / 2, ClipperLib::jtRound)); // end distance inside shield storage.raftOutline = storage.raftOutline.unionPolygons(draft_shield_raft); } if (storage.oozeShield.size() > 0 && storage.oozeShield[0].size() > 0) { const Polygons& ooze_shield = storage.oozeShield[0]; Polygons ooze_shield_raft = ooze_shield.offset(shield_line_width_layer0) // start half a line width outside shield .difference(ooze_shield.offset(-distance - shield_line_width_layer0 / 2, ClipperLib::jtRound)); // end distance inside shield storage.raftOutline = storage.raftOutline.unionPolygons(ooze_shield_raft); } coord_t smoothing = train->getSettingInMicrons("raft_smoothing"); storage.raftOutline = storage.raftOutline.offset(smoothing, ClipperLib::jtRound).offset(-smoothing, ClipperLib::jtRound); // remove small holes and smooth inward corners }
void generateRaft(SliceDataStorage& storage, int distance) { if (storage.draft_protection_shield.size() > 0) { storage.raftOutline = storage.raftOutline.unionPolygons(storage.draft_protection_shield.offset(distance, ClipperLib::jtRound)); } else if (storage.oozeShield.size() > 0 && storage.oozeShield[0].size() > 0) { storage.raftOutline = storage.raftOutline.unionPolygons(storage.oozeShield[0].offset(distance, ClipperLib::jtRound)); } else { storage.raftOutline = storage.getLayerOutlines(0, true).offset(distance, ClipperLib::jtRound); } }
void AreaSupport::detectOverhangPoints( SliceDataStorage& storage, SliceMeshStorage& mesh, std::vector<std::pair<int, std::vector<Polygons>>>& overhang_points, // stores overhang_points along with the layer index at which the overhang point occurs) int layer_count, int supportMinAreaSqrt ) { ExtruderTrain* infill_extr = storage.meshgroup->getExtruderTrain(storage.getSettingAsIndex("support_infill_extruder_nr")); const unsigned int support_line_width = infill_extr->getSettingInMicrons("support_line_width"); for (int layer_idx = 0; layer_idx < layer_count; layer_idx++) { SliceLayer& layer = mesh.layers[layer_idx]; for (SliceLayerPart& part : layer.parts) { if (part.outline.outerPolygon().area() < supportMinAreaSqrt * supportMinAreaSqrt) { Polygons part_poly_computed; Polygons& part_poly = (part.insets.size() > 0) ? part.insets[0] : part_poly_computed; // don't copy inset if its already computed if (part.insets.size() == 0) { part_poly_computed = part.outline.offset(-support_line_width / 2); } if (part_poly.size() > 0) { Polygons part_poly_recomputed = part_poly.difference(storage.support.supportLayers[layer_idx].anti_overhang); if (part_poly_recomputed.size() == 0) { continue; } if (overhang_points.size() > 0 && overhang_points.back().first == layer_idx) overhang_points.back().second.push_back(part_poly_recomputed); else { std::vector<Polygons> small_part_polys; small_part_polys.push_back(part_poly_recomputed); overhang_points.emplace_back<std::pair<int, std::vector<Polygons>>>(std::make_pair(layer_idx, small_part_polys)); } } } } } }
void PrimeTower::generatePaths_denseInfill(const SliceDataStorage& storage) { int n_patterns = 2; // alternating patterns between layers int infill_overlap = 60; // so that it can't be zero; EDIT: wtf? int extra_infill_shift = 0; int64_t z = 0; // (TODO) because the prime tower stores the paths for each extruder for once instead of generating each layer, we don't know the z position for (int extruder = 0; extruder < extruder_count; extruder++) { int line_width = storage.meshgroup->getExtruderTrain(extruder)->getSettingInMicrons("prime_tower_line_width"); patterns_per_extruder.emplace_back(n_patterns); std::vector<ExtrusionMoves>& patterns = patterns_per_extruder.back(); patterns.resize(n_patterns); for (int pattern_idx = 0; pattern_idx < n_patterns; pattern_idx++) { patterns[pattern_idx].polygons = ground_poly.offset(-line_width / 2); Polygons& result_lines = patterns[pattern_idx].lines; int outline_offset = -line_width; int line_distance = line_width; double fill_angle = 45 + pattern_idx * 90; Polygons result_polygons; // should remain empty, since we generate lines pattern! constexpr bool zig_zaggify_infill = false; Infill infill_comp(EFillMethod::LINES, zig_zaggify_infill, ground_poly, outline_offset, line_width, line_distance, infill_overlap, fill_angle, z, extra_infill_shift); infill_comp.generate(result_polygons, result_lines); } int line_width_layer0 = line_width; if (storage.getSettingAsPlatformAdhesion("adhesion_type") != EPlatformAdhesion::RAFT) { line_width_layer0 *= storage.meshgroup->getExtruderTrain(extruder)->getSettingAsRatio("initial_layer_line_width_factor"); } pattern_per_extruder_layer0.emplace_back(); ExtrusionMoves& pattern = pattern_per_extruder_layer0.back(); pattern.polygons = ground_poly.offset(-line_width_layer0 / 2); int outline_offset = -line_width_layer0; int line_distance = line_width_layer0; double fill_angle = 45; Polygons result_polygons; constexpr bool zig_zaggify_infill = false; Infill infill_comp(EFillMethod::LINES, zig_zaggify_infill, ground_poly, outline_offset, line_width_layer0, line_distance, infill_overlap, fill_angle, z, extra_infill_shift); infill_comp.generate(result_polygons, pattern.lines); } }
void PrimeTower::addToGcode(SliceDataStorage& storage, GCodePlanner& gcodeLayer, GCodeExport& gcode, int layer_nr, int prev_extruder, bool prime_tower_dir_outward, bool wipe, int* last_prime_tower_poly_printed) { if (!( storage.max_object_height_second_to_last_extruder >= 0 && storage.getSettingInMicrons("prime_tower_size") > 0) ) { return; } bool prime_tower_added = false; for (int extruder = 0; extruder < storage.meshgroup->getExtruderCount() && !prime_tower_added; extruder++) { prime_tower_added = last_prime_tower_poly_printed[extruder] == int(layer_nr); } if (prime_tower_added) { // don't print the prime tower if it has been printed already return; } if (prev_extruder == gcodeLayer.getExtruder()) { wipe = false; } addToGcode3(storage, gcodeLayer, gcode, layer_nr, prev_extruder, prime_tower_dir_outward, wipe, last_prime_tower_poly_printed); }
/* layer 2 * layer 1 ______________| * _______| ^^^^^ basic overhang * * ^^^^^^^ supporter * ^^^^^^^^^^^^^^^^^ supported * ^^^^^^^^^^^^^^^^^^^^^^ supportee * ^^^^^^^^^^^^^^^^^^^^^^^^ overhang extended * ^^^^^^^^^ overhang extensions * ^^^^^^^^^^^^^^ overhang */ std::pair<Polygons, Polygons> AreaSupport::computeBasicAndFullOverhang(const SliceDataStorage& storage, const SliceMeshStorage& mesh, const unsigned int layer_idx, const int64_t max_dist_from_lower_layer) { Polygons supportLayer_supportee = mesh.layers[layer_idx].getOutlines(); Polygons supportLayer_supporter = storage.getLayerOutlines(layer_idx-1, false); Polygons supportLayer_supported = supportLayer_supporter.offset(max_dist_from_lower_layer); Polygons basic_overhang = supportLayer_supportee.difference(supportLayer_supported); const SupportLayer& support_layer = storage.support.supportLayers[layer_idx]; basic_overhang = basic_overhang.difference(support_layer.anti_overhang); // Polygons support_extension = basic_overhang.offset(max_dist_from_lower_layer); // support_extension = support_extension.intersection(supportLayer_supported); // support_extension = support_extension.intersection(supportLayer_supportee); // // Polygons overhang = basic_overhang.unionPolygons(support_extension); // presumably the computation above is slower than the one below Polygons overhang_extented = basic_overhang.offset(max_dist_from_lower_layer + 100); // +100 for easier joining with support from layer above Polygons full_overhang = overhang_extented.intersection(supportLayer_supported.unionPolygons(supportLayer_supportee)); return std::make_pair(basic_overhang, full_overhang); }
void FffPolygonGenerator::processDraftShield(SliceDataStorage& storage, unsigned int total_layers) { int draft_shield_height = getSettingInMicrons("draft_shield_height"); int draft_shield_dist = getSettingInMicrons("draft_shield_dist"); int layer_height_0 = getSettingInMicrons("layer_height_0"); int layer_height = getSettingInMicrons("layer_height"); if (draft_shield_height < layer_height_0) { return; } unsigned int max_screen_layer = (draft_shield_height - layer_height_0) / layer_height + 1; int layer_skip = 500 / layer_height + 1; Polygons& draft_shield = storage.draft_protection_shield; for (unsigned int layer_nr = 0; layer_nr < total_layers && layer_nr < max_screen_layer; layer_nr += layer_skip) { draft_shield = draft_shield.unionPolygons(storage.getLayerOutlines(layer_nr, true)); } storage.draft_protection_shield = draft_shield.convexHull(draft_shield_dist); }
void SkirtBrim::generate(SliceDataStorage& storage, int start_distance, unsigned int primary_line_count) { const bool is_skirt = start_distance > 0; const unsigned int adhesion_extruder_nr = storage.getSettingAsIndex("adhesion_extruder_nr"); const ExtruderTrain* adhesion_extruder = storage.meshgroup->getExtruderTrain(adhesion_extruder_nr); const int primary_extruder_skirt_brim_line_width = adhesion_extruder->getSettingInMicrons("skirt_brim_line_width") * adhesion_extruder->getSettingAsRatio("initial_layer_line_width_factor"); const int64_t primary_extruder_minimal_length = adhesion_extruder->getSettingInMicrons("skirt_brim_minimal_length"); Polygons& skirt_brim_primary_extruder = storage.skirt_brim[adhesion_extruder_nr]; Polygons first_layer_outline; getFirstLayerOutline(storage, primary_line_count, primary_extruder_skirt_brim_line_width, is_skirt, first_layer_outline); const bool has_ooze_shield = storage.oozeShield.size() > 0 && storage.oozeShield[0].size() > 0; const bool has_draft_shield = storage.draft_protection_shield.size() > 0; if (is_skirt && (has_ooze_shield || has_draft_shield)) { // make sure we don't generate skirt through draft / ooze shield first_layer_outline = first_layer_outline.offset(start_distance - primary_extruder_skirt_brim_line_width / 2, ClipperLib::jtRound).unionPolygons(storage.draft_protection_shield); if (has_ooze_shield) { first_layer_outline = first_layer_outline.unionPolygons(storage.oozeShield[0]); } first_layer_outline = first_layer_outline.approxConvexHull(); start_distance = primary_extruder_skirt_brim_line_width / 2; } int offset_distance = generatePrimarySkirtBrimLines(start_distance, primary_line_count, primary_extruder_skirt_brim_line_width, primary_extruder_minimal_length, first_layer_outline, skirt_brim_primary_extruder); // generate brim for ooze shield and draft shield if (!is_skirt && (has_ooze_shield || has_draft_shield)) { // generate areas where to make extra brim for the shields // avoid gap in the middle // V // +---+ +----+ // |+-+| |+--+| // || || ||[]|| > expand to fit an extra brim line // |+-+| |+--+| // +---+ +----+ const int64_t primary_skirt_brim_width = (primary_line_count + primary_line_count % 2) * primary_extruder_skirt_brim_line_width; // always use an even number, because we will fil the area from both sides Polygons shield_brim; if (has_ooze_shield) { shield_brim = storage.oozeShield[0].difference(storage.oozeShield[0].offset(-primary_skirt_brim_width - primary_extruder_skirt_brim_line_width)); } if (has_draft_shield) { shield_brim = shield_brim.unionPolygons(storage.draft_protection_shield.difference(storage.draft_protection_shield.offset(-primary_skirt_brim_width - primary_extruder_skirt_brim_line_width))); } const Polygons outer_primary_brim = first_layer_outline.offset(offset_distance, ClipperLib::jtRound); shield_brim = shield_brim.difference(outer_primary_brim.offset(primary_extruder_skirt_brim_line_width)); // generate brim within shield_brim skirt_brim_primary_extruder.add(shield_brim); while (shield_brim.size() > 0) { shield_brim = shield_brim.offset(-primary_extruder_skirt_brim_line_width); skirt_brim_primary_extruder.add(shield_brim); } // update parameters to generate secondary skirt around first_layer_outline = outer_primary_brim; if (has_draft_shield) { first_layer_outline = first_layer_outline.unionPolygons(storage.draft_protection_shield); } if (has_ooze_shield) { first_layer_outline = first_layer_outline.unionPolygons(storage.oozeShield[0]); } offset_distance = 0; } { // process other extruders' brim/skirt (as one brim line around the old brim) int last_width = primary_extruder_skirt_brim_line_width; std::vector<bool> extruder_is_used = storage.getExtrudersUsed(); for (unsigned int extruder = 0; extruder < storage.meshgroup->getExtruderCount(); extruder++) { if (extruder == adhesion_extruder_nr || !extruder_is_used[extruder]) { continue; } const ExtruderTrain* train = storage.meshgroup->getExtruderTrain(extruder); const int width = train->getSettingInMicrons("skirt_brim_line_width") * train->getSettingAsRatio("initial_layer_line_width_factor"); const int64_t minimal_length = train->getSettingInMicrons("skirt_brim_minimal_length"); offset_distance += last_width / 2 + width/2; last_width = width; while (storage.skirt_brim[extruder].polygonLength() < minimal_length) { storage.skirt_brim[extruder].add(first_layer_outline.offset(offset_distance, ClipperLib::jtRound)); offset_distance += width; } } } }
void SkirtBrim::generate(SliceDataStorage& storage, Polygons first_layer_outline, int start_distance, unsigned int primary_line_count) { const bool is_skirt = start_distance > 0; Scene& scene = Application::getInstance().current_slice->scene; const size_t adhesion_extruder_nr = scene.current_mesh_group->settings.get<ExtruderTrain&>("adhesion_extruder_nr").extruder_nr; const Settings& adhesion_settings = scene.extruders[adhesion_extruder_nr].settings; const coord_t primary_extruder_skirt_brim_line_width = adhesion_settings.get<coord_t>("skirt_brim_line_width") * adhesion_settings.get<Ratio>("initial_layer_line_width_factor"); const coord_t primary_extruder_minimal_length = adhesion_settings.get<coord_t>("skirt_brim_minimal_length"); Polygons& skirt_brim_primary_extruder = storage.skirt_brim[adhesion_extruder_nr]; const bool has_ooze_shield = storage.oozeShield.size() > 0 && storage.oozeShield[0].size() > 0; const bool has_draft_shield = storage.draft_protection_shield.size() > 0; if (is_skirt && (has_ooze_shield || has_draft_shield)) { // make sure we don't generate skirt through draft / ooze shield first_layer_outline = first_layer_outline.offset(start_distance - primary_extruder_skirt_brim_line_width / 2, ClipperLib::jtRound).unionPolygons(storage.draft_protection_shield); if (has_ooze_shield) { first_layer_outline = first_layer_outline.unionPolygons(storage.oozeShield[0]); } first_layer_outline = first_layer_outline.approxConvexHull(); start_distance = primary_extruder_skirt_brim_line_width / 2; } int offset_distance = generatePrimarySkirtBrimLines(start_distance, primary_line_count, primary_extruder_minimal_length, first_layer_outline, skirt_brim_primary_extruder); // handle support-brim const ExtruderTrain& support_infill_extruder = scene.current_mesh_group->settings.get<ExtruderTrain&>("support_infill_extruder_nr"); if (support_infill_extruder.settings.get<bool>("support_brim_enable")) { generateSupportBrim(storage); } // generate brim for ooze shield and draft shield if (!is_skirt && (has_ooze_shield || has_draft_shield)) { // generate areas where to make extra brim for the shields // avoid gap in the middle // V // +---+ +----+ // |+-+| |+--+| // || || ||[]|| > expand to fit an extra brim line // |+-+| |+--+| // +---+ +----+ const int64_t primary_skirt_brim_width = (primary_line_count + primary_line_count % 2) * primary_extruder_skirt_brim_line_width; // always use an even number, because we will fil the area from both sides Polygons shield_brim; if (has_ooze_shield) { shield_brim = storage.oozeShield[0].difference(storage.oozeShield[0].offset(-primary_skirt_brim_width - primary_extruder_skirt_brim_line_width)); } if (has_draft_shield) { shield_brim = shield_brim.unionPolygons(storage.draft_protection_shield.difference(storage.draft_protection_shield.offset(-primary_skirt_brim_width - primary_extruder_skirt_brim_line_width))); } const Polygons outer_primary_brim = first_layer_outline.offset(offset_distance, ClipperLib::jtRound); shield_brim = shield_brim.difference(outer_primary_brim.offset(primary_extruder_skirt_brim_line_width)); // generate brim within shield_brim skirt_brim_primary_extruder.add(shield_brim); while (shield_brim.size() > 0) { shield_brim = shield_brim.offset(-primary_extruder_skirt_brim_line_width); skirt_brim_primary_extruder.add(shield_brim); } // update parameters to generate secondary skirt around first_layer_outline = outer_primary_brim; if (has_draft_shield) { first_layer_outline = first_layer_outline.unionPolygons(storage.draft_protection_shield); } if (has_ooze_shield) { first_layer_outline = first_layer_outline.unionPolygons(storage.oozeShield[0]); } offset_distance = 0; } { // process other extruders' brim/skirt (as one brim line around the old brim) int last_width = primary_extruder_skirt_brim_line_width; std::vector<bool> extruder_is_used = storage.getExtrudersUsed(); for (size_t extruder_nr = 0; extruder_nr < Application::getInstance().current_slice->scene.extruders.size(); extruder_nr++) { if (extruder_nr == adhesion_extruder_nr || !extruder_is_used[extruder_nr]) { continue; } const ExtruderTrain& train = Application::getInstance().current_slice->scene.extruders[extruder_nr]; const coord_t width = train.settings.get<coord_t>("skirt_brim_line_width") * train.settings.get<Ratio>("initial_layer_line_width_factor"); const coord_t minimal_length = train.settings.get<coord_t>("skirt_brim_minimal_length"); offset_distance += last_width / 2 + width/2; last_width = width; while (storage.skirt_brim[extruder_nr].polygonLength() < minimal_length) { storage.skirt_brim[extruder_nr].add(first_layer_outline.offset(offset_distance, ClipperLib::jtRound)); offset_distance += width; } } } }
void SkirtBrim::getFirstLayerOutline(SliceDataStorage& storage, const size_t primary_line_count, const bool is_skirt, Polygons& first_layer_outline) { const ExtruderTrain& train = Application::getInstance().current_slice->scene.current_mesh_group->settings.get<ExtruderTrain&>("adhesion_extruder_nr"); const ExtruderTrain& support_infill_extruder = Application::getInstance().current_slice->scene.current_mesh_group->settings.get<ExtruderTrain&>("support_infill_extruder_nr"); const bool external_only = is_skirt || train.settings.get<bool>("brim_outside_only"); //Whether to include holes or not. Skirt doesn't have any holes. const LayerIndex layer_nr = 0; if (is_skirt) { constexpr bool include_support = true; constexpr bool include_prime_tower = true; first_layer_outline = storage.getLayerOutlines(layer_nr, include_support, include_prime_tower, external_only); first_layer_outline = first_layer_outline.approxConvexHull(); } else { // add brim underneath support by removing support where there's brim around the model constexpr bool include_support = false; //Include manually below. constexpr bool include_prime_tower = false; //Include manually below. constexpr bool external_outlines_only = false; //Remove manually below. first_layer_outline = storage.getLayerOutlines(layer_nr, include_support, include_prime_tower, external_outlines_only); first_layer_outline = first_layer_outline.unionPolygons(); //To guard against overlapping outlines, which would produce holes according to the even-odd rule. Polygons first_layer_empty_holes; if (external_only) { first_layer_empty_holes = first_layer_outline.getEmptyHoles(); first_layer_outline = first_layer_outline.removeEmptyHoles(); } if (storage.support.generated && primary_line_count > 0 && !storage.support.supportLayers.empty()) { // remove model-brim from support SupportLayer& support_layer = storage.support.supportLayers[0]; if (support_infill_extruder.settings.get<bool>("brim_replaces_support")) { // avoid gap in the middle // V // +---+ +----+ // |+-+| |+--+| // || || ||[]|| > expand to fit an extra brim line // |+-+| |+--+| // +---+ +----+ const coord_t primary_extruder_skirt_brim_line_width = train.settings.get<coord_t>("skirt_brim_line_width") * train.settings.get<Ratio>("initial_layer_line_width_factor"); Polygons model_brim_covered_area = first_layer_outline.offset(primary_extruder_skirt_brim_line_width * (primary_line_count + primary_line_count % 2), ClipperLib::jtRound); // always leave a gap of an even number of brim lines, so that it fits if it's generating brim from both sides if (external_only) { // don't remove support within empty holes where no brim is generated. model_brim_covered_area.add(first_layer_empty_holes); } AABB model_brim_covered_area_boundary_box(model_brim_covered_area); support_layer.excludeAreasFromSupportInfillAreas(model_brim_covered_area, model_brim_covered_area_boundary_box); } for (const SupportInfillPart& support_infill_part : support_layer.support_infill_parts) { first_layer_outline.add(support_infill_part.outline); } first_layer_outline.add(support_layer.support_bottom); first_layer_outline.add(support_layer.support_roof); } if (storage.primeTower.enabled) { first_layer_outline.add(storage.primeTower.outer_poly_first_layer); // don't remove parts of the prime tower, but make a brim for it } } constexpr coord_t join_distance = 20; first_layer_outline = first_layer_outline.offset(join_distance).offset(-join_distance); // merge adjacent models into single polygon constexpr coord_t smallest_line_length = 200; constexpr coord_t largest_error_of_removed_point = 50; first_layer_outline.simplify(smallest_line_length, largest_error_of_removed_point); // simplify for faster processing of the brim lines if (first_layer_outline.size() == 0) { logError("Couldn't generate skirt / brim! No polygons on first layer.\n"); } }
/* * Algorithm: * From top layer to bottom layer: * - find overhang by looking at the difference between two consucutive layers * - join with support areas from layer above * - subtract current layer * - use the result for the next lower support layer (without doing XY-distance and Z bottom distance, so that a single support beam may move around the model a bit => more stability) * - perform inset using X/Y-distance and bottom Z distance * * for support buildplate only: purge all support not connected to buildplate */ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int mesh_idx, unsigned int layer_count, std::vector<Polygons>& supportAreas, CommandSocket* commandSocket) { SliceMeshStorage& mesh = storage.meshes[mesh_idx]; // given settings ESupportType support_type = mesh.getSettingAsSupportType("support_type"); if (!mesh.getSettingBoolean("support_enable")) return; if (support_type == ESupportType::NONE) return; double supportAngle = mesh.getSettingInAngleRadians("support_angle"); bool supportOnBuildplateOnly = support_type == ESupportType::PLATFORM_ONLY; int supportZDistance = mesh.getSettingInMicrons("support_z_distance"); int supportZDistanceBottom = mesh.getSettingInMicrons("support_bottom_distance"); int supportZDistanceTop = mesh.getSettingInMicrons("support_top_distance"); int join_distance = mesh.getSettingInMicrons("support_join_distance"); int support_bottom_stair_step_height = mesh.getSettingInMicrons("support_bottom_stair_step_height"); int smoothing_distance = mesh.getSettingInMicrons("support_area_smoothing"); int extension_offset = mesh.getSettingInMicrons("support_offset"); int supportTowerDiameter = mesh.getSettingInMicrons("support_tower_diameter"); int supportMinAreaSqrt = mesh.getSettingInMicrons("support_minimal_diameter"); double supportTowerRoofAngle = mesh.getSettingInAngleRadians("support_tower_roof_angle"); //std::cerr <<" towerDiameter=" << towerDiameter <<", supportMinAreaSqrt=" << supportMinAreaSqrt << std::endl; int min_smoothing_area = 100*100; // minimal area for which to perform smoothing int z_layer_distance_tower = 1; // start tower directly below overhang point int layerThickness = mesh.getSettingInMicrons("layer_height"); int extrusionWidth = mesh.getSettingInMicrons("support_line_width"); int supportXYDistance = mesh.getSettingInMicrons("support_xy_distance") + extrusionWidth / 2; bool conical_support = mesh.getSettingBoolean("support_conical_enabled"); double conical_support_angle = mesh.getSettingInAngleRadians("support_conical_angle"); int64_t conical_smallest_breadth = mesh.getSettingInMicrons("support_conical_min_width"); // derived settings: if (supportZDistanceBottom < 0) supportZDistanceBottom = supportZDistance; if (supportZDistanceTop < 0) supportZDistanceTop = supportZDistance; int supportLayerThickness = layerThickness; int layerZdistanceTop = supportZDistanceTop / supportLayerThickness + 1; // support must always be 1 layer below overhang unsigned int layerZdistanceBottom = std::max(0, supportZDistanceBottom / supportLayerThickness); double tanAngle = tan(supportAngle) - 0.01; // the XY-component of the supportAngle int maxDistFromLowerLayer = tanAngle * supportLayerThickness; // max dist which can be bridged int64_t conical_support_offset; if (conical_support_angle > 0) { conical_support_offset = (tan(conical_support_angle) - 0.01) * supportLayerThickness; } else { conical_support_offset = -(tan(-conical_support_angle) - 0.01) * supportLayerThickness; } unsigned int support_layer_count = layer_count; double tanTowerRoofAngle = tan(supportTowerRoofAngle); int towerRoofExpansionDistance = layerThickness / tanTowerRoofAngle; // early out if ( layerZdistanceTop + 1 > (int) support_layer_count ) { storage.support.generated = false; // no (first layer) support can be generated return; } // computation std::vector<std::pair<int, std::vector<Polygons>>> overhang_points; // stores overhang_points along with the layer index at which the overhang point occurs AreaSupport::detectOverhangPoints(storage, mesh, overhang_points, layer_count, supportMinAreaSqrt, extrusionWidth); bool still_in_upper_empty_layers = true; int overhang_points_pos = overhang_points.size() - 1; Polygons supportLayer_last; std::vector<Polygons> towerRoofs; for (unsigned int layer_idx = support_layer_count - 1 - layerZdistanceTop; layer_idx != (unsigned int) -1 ; layer_idx--) { Polygons overhang; { // compute basic overhang and put in right layer ([layerZdistanceTOp] layers below) Polygons supportLayer_supportee = mesh.layers[layer_idx+layerZdistanceTop].getOutlines(); Polygons supportLayer_supporter = storage.getLayerOutlines(layer_idx-1+layerZdistanceTop, false); Polygons supportLayer_supported = supportLayer_supporter.offset(maxDistFromLowerLayer); Polygons basic_overhang = supportLayer_supportee.difference(supportLayer_supported); // Polygons support_extension = basic_overhang.offset(maxDistFromLowerLayer); // support_extension = support_extension.intersection(supportLayer_supported); // support_extension = support_extension.intersection(supportLayer_supportee); // // Polygons overhang = basic_overhang.unionPolygons(support_extension); // presumably the computation above is slower than the one below Polygons overhang_extented = basic_overhang.offset(maxDistFromLowerLayer + 100); // +100 for easier joining with support from layer above overhang = overhang_extented.intersection(supportLayer_supported.unionPolygons(supportLayer_supportee)); /* layer 2 * layer 1 ______________| * _______| ^^^^^ basic overhang * * ^^^^^^^ supporter * ^^^^^^^^^^^^^^^^^ supported * ^^^^^^^^^^^^^^^^^^^^^^ supportee * ^^^^^^^^^^^^^^^^^^^^^^^^ overhang extended * ^^^^^^^^^ overhang extensions * ^^^^^^^^^^^^^^ overhang */ } Polygons& supportLayer_this = overhang; if (extension_offset) { supportLayer_this = supportLayer_this.offset(extension_offset); } supportLayer_this.simplify(50); // TODO: hardcoded value! if (supportMinAreaSqrt > 0) { // handle straight walls AreaSupport::handleWallStruts(supportLayer_this, supportMinAreaSqrt, supportTowerDiameter); // handle towers AreaSupport::handleTowers(supportLayer_this, towerRoofs, overhang_points, overhang_points_pos, layer_idx, towerRoofExpansionDistance, supportTowerDiameter, supportMinAreaSqrt, layer_count, z_layer_distance_tower); } if (layer_idx+1 < support_layer_count) { // join with support from layer up supportLayer_this = AreaSupport::join(supportLayer_last, supportLayer_this, join_distance, smoothing_distance, min_smoothing_area, conical_support, conical_support_offset, conical_smallest_breadth); } // move up from model if (layerZdistanceBottom > 0 && layer_idx >= layerZdistanceBottom) { int stepHeight = support_bottom_stair_step_height / supportLayerThickness + 1; int bottomLayer = ((layer_idx - layerZdistanceBottom) / stepHeight) * stepHeight; supportLayer_this = supportLayer_this.difference(storage.getLayerOutlines(bottomLayer, false)); } supportLayer_last = supportLayer_this; // inset using X/Y distance if (supportLayer_this.size() > 0) supportLayer_this = supportLayer_this.difference(storage.getLayerOutlines(layer_idx, false).offset(supportXYDistance)); supportAreas[layer_idx] = supportLayer_this; if (still_in_upper_empty_layers && supportLayer_this.size() > 0) { storage.support.layer_nr_max_filled_layer = layer_idx; still_in_upper_empty_layers = false; } Progress::messageProgress(Progress::Stage::SUPPORT, storage.meshes.size() * mesh_idx + support_layer_count - layer_idx, support_layer_count * storage.meshes.size(), commandSocket); } // do stuff for when support on buildplate only if (supportOnBuildplateOnly) { Polygons touching_buildplate = supportAreas[0]; // TODO: not working for conical support! for (unsigned int layer_idx = 1 ; layer_idx < storage.support.supportLayers.size() ; layer_idx++) { Polygons& supportLayer = supportAreas[layer_idx]; touching_buildplate = supportLayer.intersection(touching_buildplate); // from bottom to top, support areas can only decrease! supportAreas[layer_idx] = touching_buildplate; } } }
/* * Algorithm: * From top layer to bottom layer: * - find overhang by looking at the difference between two consucutive layers * - join with support areas from layer above * - subtract current layer * - use the result for the next lower support layer (without doing XY-distance and Z bottom distance, so that a single support beam may move around the model a bit => more stability) * - perform inset using X/Y-distance and bottom Z distance * * for support buildplate only: purge all support not connected to buildplate */ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int mesh_idx, unsigned int layer_count, std::vector<Polygons>& supportAreas) { SliceMeshStorage& mesh = storage.meshes[mesh_idx]; // given settings ESupportType support_type = storage.getSettingAsSupportType("support_type"); if (!mesh.getSettingBoolean("support_enable")) return; if (support_type == ESupportType::NONE) return; const double supportAngle = mesh.getSettingInAngleRadians("support_angle"); const bool supportOnBuildplateOnly = support_type == ESupportType::PLATFORM_ONLY; const int supportZDistanceBottom = mesh.getSettingInMicrons("support_bottom_distance"); const int supportZDistanceTop = mesh.getSettingInMicrons("support_top_distance"); const int join_distance = mesh.getSettingInMicrons("support_join_distance"); const int support_bottom_stair_step_height = mesh.getSettingInMicrons("support_bottom_stair_step_height"); const int extension_offset = mesh.getSettingInMicrons("support_offset"); const int supportTowerDiameter = mesh.getSettingInMicrons("support_tower_diameter"); const int supportMinAreaSqrt = mesh.getSettingInMicrons("support_minimal_diameter"); const double supportTowerRoofAngle = mesh.getSettingInAngleRadians("support_tower_roof_angle"); const int layerThickness = storage.getSettingInMicrons("layer_height"); const int supportXYDistance = mesh.getSettingInMicrons("support_xy_distance"); const int support_xy_distance_overhang = mesh.getSettingInMicrons("support_xy_distance_overhang"); const bool use_support_xy_distance_overhang = mesh.getSettingAsSupportDistPriority("support_xy_overrides_z") == SupportDistPriority::Z_OVERRIDES_XY; // whether to use a different xy distance at overhangs const double conical_support_angle = mesh.getSettingInAngleRadians("support_conical_angle"); const bool conical_support = mesh.getSettingBoolean("support_conical_enabled") && conical_support_angle != 0; const int64_t conical_smallest_breadth = mesh.getSettingInMicrons("support_conical_min_width"); int support_skin_extruder_nr = storage.getSettingAsIndex("support_interface_extruder_nr"); int support_infill_extruder_nr = storage.getSettingAsIndex("support_infill_extruder_nr"); bool interface_enable = mesh.getSettingBoolean("support_interface_enable"); // derived settings: const int max_smoothing_angle = 135; // maximum angle of inner corners to be smoothed int smoothing_distance; { // compute best smoothing_distance ExtruderTrain& infill_train = *storage.meshgroup->getExtruderTrain(support_infill_extruder_nr); int support_infill_line_width = infill_train.getSettingInMicrons("support_interface_line_width"); smoothing_distance = support_infill_line_width; if (interface_enable) { ExtruderTrain& interface_train = *storage.meshgroup->getExtruderTrain(support_skin_extruder_nr); int support_interface_line_width = interface_train.getSettingInMicrons("support_interface_line_width"); smoothing_distance = std::max(support_interface_line_width, smoothing_distance); } } const int z_layer_distance_tower = 1; // start tower directly below overhang point int supportLayerThickness = layerThickness; const unsigned int layerZdistanceTop = std::max(0U, round_up_divide(supportZDistanceTop, supportLayerThickness)) + 1; // support must always be 1 layer below overhang const unsigned int layerZdistanceBottom = std::max(0U, round_up_divide(supportZDistanceBottom, supportLayerThickness)); double tanAngle = tan(supportAngle) - 0.01; // the XY-component of the supportAngle int max_dist_from_lower_layer = tanAngle * supportLayerThickness; // max dist which can be bridged int64_t conical_support_offset; if (conical_support_angle > 0) { // outward ==> wider base than overhang conical_support_offset = -(tan(conical_support_angle) - 0.01) * supportLayerThickness; } else { // inward ==> smaller base than overhang conical_support_offset = (tan(-conical_support_angle) - 0.01) * supportLayerThickness; } unsigned int support_layer_count = layer_count; double tanTowerRoofAngle = tan(supportTowerRoofAngle); int towerRoofExpansionDistance = layerThickness / tanTowerRoofAngle; // early out if ( layerZdistanceTop + 1 > support_layer_count ) { return; } // computation std::vector<std::pair<int, std::vector<Polygons>>> overhang_points; // stores overhang_points along with the layer index at which the overhang point occurs AreaSupport::detectOverhangPoints(storage, mesh, overhang_points, layer_count, supportMinAreaSqrt); std::deque<std::pair<Polygons, Polygons>> basic_and_full_overhang_above; for (unsigned int layer_idx = support_layer_count - 1; layer_idx != support_layer_count - 1 - layerZdistanceTop ; layer_idx--) { basic_and_full_overhang_above.push_front(computeBasicAndFullOverhang(storage, mesh, layer_idx, max_dist_from_lower_layer)); } int overhang_points_pos = overhang_points.size() - 1; Polygons supportLayer_last; std::vector<Polygons> towerRoofs; for (unsigned int layer_idx = support_layer_count - 1 - layerZdistanceTop; layer_idx != (unsigned int) -1 ; layer_idx--) { basic_and_full_overhang_above.push_front(computeBasicAndFullOverhang(storage, mesh, layer_idx, max_dist_from_lower_layer)); Polygons overhang; { // compute basic overhang and put in right layer ([layerZdistanceTOp] layers below) overhang = basic_and_full_overhang_above.back().second; basic_and_full_overhang_above.pop_back(); } Polygons& supportLayer_this = overhang; if (extension_offset) { supportLayer_this = supportLayer_this.offset(extension_offset); } if (supportMinAreaSqrt > 0) { // handle straight walls AreaSupport::handleWallStruts(supportLayer_this, supportMinAreaSqrt, supportTowerDiameter); // handle towers AreaSupport::handleTowers(supportLayer_this, towerRoofs, overhang_points, overhang_points_pos, layer_idx, towerRoofExpansionDistance, supportTowerDiameter, supportMinAreaSqrt, layer_count, z_layer_distance_tower); } if (layer_idx+1 < support_layer_count) { // join with support from layer up supportLayer_this = AreaSupport::join(supportLayer_last, supportLayer_this, join_distance, smoothing_distance, max_smoothing_angle, conical_support, conical_support_offset, conical_smallest_breadth); } supportLayer_this = supportLayer_this.unionPolygons(storage.support.supportLayers[layer_idx].support_mesh); // move up from model if (layerZdistanceBottom > 0 && layer_idx >= layerZdistanceBottom) { int stepHeight = support_bottom_stair_step_height / supportLayerThickness + 1; int bottomLayer = ((layer_idx - layerZdistanceBottom) / stepHeight) * stepHeight; supportLayer_this = supportLayer_this.difference(storage.getLayerOutlines(bottomLayer, false)); } supportLayer_last = supportLayer_this; // inset using X/Y distance if (supportLayer_this.size() > 0) { Polygons& basic_overhang = basic_and_full_overhang_above.front().first; // basic overhang on this layer Polygons outlines = storage.getLayerOutlines(layer_idx, false); if (use_support_xy_distance_overhang) { Polygons xy_overhang_disallowed = basic_overhang.offset(supportZDistanceTop * tanAngle); Polygons xy_non_overhang_disallowed = outlines.difference(basic_overhang.offset(supportXYDistance)).offset(supportXYDistance); Polygons xy_disallowed = xy_overhang_disallowed.unionPolygons(xy_non_overhang_disallowed.unionPolygons(outlines.offset(support_xy_distance_overhang))); supportLayer_this = supportLayer_this.difference(xy_disallowed); } else { supportLayer_this = supportLayer_this.difference(outlines.offset(supportXYDistance)); } if (storage.primeTower.enabled) //Don't intersect with prime tower. { supportLayer_this = supportLayer_this.difference(storage.primeTower.ground_poly.getOutsidePolygons()); } } supportAreas[layer_idx] = supportLayer_this; Progress::messageProgress(Progress::Stage::SUPPORT, storage.meshes.size() * mesh_idx + support_layer_count - layer_idx, support_layer_count * storage.meshes.size()); } // do stuff for when support on buildplate only if (supportOnBuildplateOnly) { Polygons touching_buildplate = supportAreas[0]; // TODO: not working for conical support! for (unsigned int layer_idx = 1 ; layer_idx < storage.support.supportLayers.size() ; layer_idx++) { Polygons& supportLayer = supportAreas[layer_idx]; if (conical_support) { // with conical support the next layer is allowed to be larger than the previous touching_buildplate = touching_buildplate.offset(std::abs(conical_support_offset) + 10, ClipperLib::jtMiter, 10); // + 10 and larger miter limit cause performing an outward offset after an inward offset can disregard sharp corners // // conical support can make // layer above layer below // v v // | : | // | ==> : |__ // |____ :.... // // a miter limit would result in // | : : | // | :.. <== : |__ // .\___ :.... // } touching_buildplate = supportLayer.intersection(touching_buildplate); // from bottom to top, support areas can only decrease! supportAreas[layer_idx] = touching_buildplate; } } for (unsigned int layer_idx = supportAreas.size() - 1; layer_idx != (unsigned int) std::max(-1, storage.support.layer_nr_max_filled_layer) ; layer_idx--) { const Polygons& support_here = supportAreas[layer_idx]; if (support_here.size() > 0) { storage.support.layer_nr_max_filled_layer = layer_idx; break; } } storage.support.generated = true; }
void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper& time_keeper) { size_t total_layers = 0; for (SliceMeshStorage& mesh : storage.meshes) { total_layers = std::max<unsigned int>(total_layers, mesh.layers.size()); } //layerparts2HTML(storage, "output/output.html"); for(unsigned int layer_number = 0; layer_number < total_layers; layer_number++) { processInsets(storage, layer_number); Progress::messageProgress(Progress::Stage::INSET, layer_number+1, total_layers, commandSocket); } removeEmptyFirstLayers(storage, getSettingInMicrons("layer_height"), total_layers); if (total_layers < 1) { log("Stopping process because there are no layers.\n"); return; } Progress::messageProgressStage(Progress::Stage::SUPPORT, &time_keeper, commandSocket); AreaSupport::generateSupportAreas(storage, total_layers, commandSocket); /* if (storage.support.generated) { for (unsigned int layer_idx = 0; layer_idx < total_layers; layer_idx++) { Polygons& support = storage.support.supportLayers[layer_idx].supportAreas; sendPolygons(SupportType, layer_idx, support, getSettingInMicrons("support_line_width")); } } */ Progress::messageProgressStage(Progress::Stage::SKIN, &time_keeper, commandSocket); int mesh_max_bottom_layer_count = 0; if (getSettingBoolean("magic_spiralize")) { for(SliceMeshStorage& mesh : storage.meshes) { mesh_max_bottom_layer_count = std::max(mesh_max_bottom_layer_count, mesh.getSettingAsCount("bottom_layers")); } } for(unsigned int layer_number = 0; layer_number < total_layers; layer_number++) { if (!getSettingBoolean("magic_spiralize") || static_cast<int>(layer_number) < mesh_max_bottom_layer_count) //Only generate up/downskin and infill for the first X layers when spiralize is choosen. { processSkins(storage, layer_number); } Progress::messageProgress(Progress::Stage::SKIN, layer_number+1, total_layers, commandSocket); } unsigned int combined_infill_layers = storage.getSettingInMicrons("infill_sparse_thickness") / std::max(storage.getSettingInMicrons("layer_height"),1); //How many infill layers to combine to obtain the requested sparse thickness. for(SliceMeshStorage& mesh : storage.meshes) { combineInfillLayers(mesh,combined_infill_layers); } storage.primeTower.computePrimeTowerMax(storage); storage.primeTower.generatePaths(storage, total_layers); processOozeShield(storage, total_layers); processDraftShield(storage, total_layers); processPlatformAdhesion(storage); for(SliceMeshStorage& mesh : storage.meshes) { if (mesh.getSettingBoolean("magic_fuzzy_skin_enabled")) { processFuzzyWalls(mesh); } else if (mesh.getSettingAsCount("wall_line_count") > 0) { // only send polygon data for (unsigned int layer_nr = 0; layer_nr < total_layers; layer_nr++) { SliceLayer* layer = &mesh.layers[layer_nr]; for(SliceLayerPart& part : layer->parts) { sendPolygons(Inset0Type, layer_nr, (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") == ESurfaceMode::SURFACE)? part.outline : part.insets[0], mesh.getSettingInMicrons("wall_line_width_0")); } } } } }
int Raft::getFillerLayerCount(const SliceDataStorage& storage) { const int64_t normal_layer_height = storage.getSettingInMicrons("layer_height"); const unsigned int filler_layer_count = round_divide(getZdiffBetweenRaftAndLayer1(storage), normal_layer_height); return filler_layer_count; }