1 //Copyright (c) 2018 Ultimaker B.V. 2 //CuraEngine is released under the terms of the AGPLv3 or higher. 3 4 #include "ExtruderTrain.h" 5 #include "sliceDataStorage.h" 6 #include "WallsComputation.h" 7 #include "settings/types/Ratio.h" 8 #include "settings/EnumSettings.h" 9 #include "utils/polygonUtils.h" 10 11 namespace cura { 12 WallsComputation(const Settings & settings,const LayerIndex layer_nr)13WallsComputation::WallsComputation(const Settings& settings, const LayerIndex layer_nr) 14 : settings(settings) 15 , layer_nr(layer_nr) 16 { 17 } 18 19 /* 20 * This function is executed in a parallel region based on layer_nr. 21 * When modifying make sure any changes does not introduce data races. 22 * 23 * generateInsets only reads and writes data for the current layer 24 */ generateInsets(SliceLayerPart * part)25void WallsComputation::generateInsets(SliceLayerPart* part) 26 { 27 size_t inset_count = settings.get<size_t>("wall_line_count"); 28 const bool spiralize = settings.get<bool>("magic_spiralize"); 29 if (spiralize && layer_nr < LayerIndex(settings.get<size_t>("initial_bottom_layers")) && ((layer_nr % 2) + 2) % 2 == 1) //Add extra insets every 2 layers when spiralizing. This makes bottoms of cups watertight. 30 { 31 inset_count += 5; 32 } 33 if (settings.get<bool>("alternate_extra_perimeter")) 34 { 35 inset_count += ((layer_nr % 2) + 2) % 2; 36 } 37 38 if (inset_count == 0) 39 { 40 part->insets.push_back(part->outline); 41 part->print_outline = part->outline; 42 return; 43 } 44 45 const coord_t wall_0_inset = settings.get<coord_t>("wall_0_inset"); 46 coord_t line_width_0 = settings.get<coord_t>("wall_line_width_0"); 47 coord_t line_width_x = settings.get<coord_t>("wall_line_width_x"); 48 if (layer_nr == 0) 49 { 50 const ExtruderTrain& train_wall_0 = settings.get<ExtruderTrain&>("wall_0_extruder_nr"); 51 line_width_0 *= train_wall_0.settings.get<Ratio>("initial_layer_line_width_factor"); 52 const ExtruderTrain& train_wall_x = settings.get<ExtruderTrain&>("wall_x_extruder_nr"); 53 line_width_x *= train_wall_x.settings.get<Ratio>("initial_layer_line_width_factor"); 54 } 55 56 const bool recompute_outline_based_on_outer_wall = 57 settings.get<bool>("support_enable") && 58 !settings.get<bool>("fill_outline_gaps"); 59 for(size_t i = 0; i < inset_count; i++) 60 { 61 part->insets.push_back(Polygons()); 62 if (i == 0) 63 { 64 part->insets[0] = part->outline.offset(-line_width_0 / 2 - wall_0_inset); 65 } 66 else if (i == 1) 67 { 68 part->insets[1] = part->insets[0].offset(-line_width_0 / 2 + wall_0_inset - line_width_x / 2); 69 } 70 else 71 { 72 part->insets[i] = part->insets[i - 1].offset(-line_width_x); 73 } 74 75 const size_t inset_part_count = part->insets[i].size(); 76 constexpr size_t minimum_part_saving = 3; //Only try if the part has more pieces than the previous inset and saves at least this many parts. 77 constexpr coord_t try_smaller = 10; //How many micrometres to inset with the try with a smaller inset. 78 if (inset_part_count > minimum_part_saving + 1 && (i == 0 || (i > 0 && inset_part_count > part->insets[i - 1].size() + minimum_part_saving))) 79 { 80 //Try a different line thickness and see if this fits better, based on these criteria: 81 // - There are fewer parts to the polygon (fits better in slim areas). 82 // - The polygon area is largely unaffected. 83 Polygons alternative_inset; 84 if (i == 0) 85 { 86 alternative_inset = part->outline.offset(-(line_width_0 - try_smaller) / 2 - wall_0_inset); 87 } 88 else if (i == 1) 89 { 90 alternative_inset = part->insets[0].offset(-(line_width_0 - try_smaller) / 2 + wall_0_inset - line_width_x / 2); 91 } 92 else 93 { 94 alternative_inset = part->insets[i - 1].offset(-(line_width_x - try_smaller)); 95 } 96 if (alternative_inset.size() < inset_part_count - minimum_part_saving) //Significantly fewer parts (saves more than 3 parts). 97 { 98 part->insets[i] = alternative_inset; 99 } 100 } 101 102 //Finally optimize all the polygons. Every point removed saves time in the long run. 103 part->insets[i].simplify(); 104 part->insets[i].removeDegenerateVerts(); 105 if (i == 0) 106 { 107 if (recompute_outline_based_on_outer_wall) 108 { 109 part->print_outline = part->insets[0].offset(line_width_0 / 2, ClipperLib::jtSquare); 110 } 111 else 112 { 113 part->print_outline = part->outline; 114 } 115 } 116 if (part->insets[i].size() < 1) 117 { 118 part->insets.pop_back(); 119 break; 120 } 121 } 122 } 123 124 /* 125 * This function is executed in a parallel region based on layer_nr. 126 * When modifying make sure any changes does not introduce data races. 127 * 128 * generateInsets only reads and writes data for the current layer 129 */ generateInsets(SliceLayer * layer)130void WallsComputation::generateInsets(SliceLayer* layer) 131 { 132 for(unsigned int partNr = 0; partNr < layer->parts.size(); partNr++) 133 { 134 generateInsets(&layer->parts[partNr]); 135 } 136 137 const bool remove_parts_with_no_insets = !settings.get<bool>("fill_outline_gaps"); 138 //Remove the parts which did not generate an inset. As these parts are too small to print, 139 // and later code can now assume that there is always minimal 1 inset line. 140 for (unsigned int part_idx = 0; part_idx < layer->parts.size(); part_idx++) 141 { 142 if (layer->parts[part_idx].insets.size() == 0 && remove_parts_with_no_insets) 143 { 144 if (part_idx != layer->parts.size() - 1) 145 { // move existing part into part to be deleted 146 layer->parts[part_idx] = std::move(layer->parts.back()); 147 } 148 layer->parts.pop_back(); // always remove last element from array (is more efficient) 149 part_idx -= 1; // check the part we just moved here 150 } 151 } 152 } 153 154 }//namespace cura 155