Fill.pm 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. package Slic3r::Fill;
  2. use Moo;
  3. use List::Util qw(max);
  4. use Slic3r::ExtrusionPath ':roles';
  5. use Slic3r::Fill::3DHoneycomb;
  6. use Slic3r::Fill::Base;
  7. use Slic3r::Fill::Concentric;
  8. use Slic3r::Fill::Honeycomb;
  9. use Slic3r::Fill::PlanePath;
  10. use Slic3r::Fill::Rectilinear;
  11. use Slic3r::Flow ':roles';
  12. use Slic3r::Geometry qw(X Y PI scale chained_path deg2rad);
  13. use Slic3r::Geometry::Clipper qw(union union_ex diff diff_ex intersection_ex offset offset2);
  14. use Slic3r::Surface ':types';
  15. has 'bounding_box' => (is => 'ro', required => 0);
  16. has 'fillers' => (is => 'rw', default => sub { {} });
  17. our %FillTypes = (
  18. archimedeanchords => 'Slic3r::Fill::ArchimedeanChords',
  19. rectilinear => 'Slic3r::Fill::Rectilinear',
  20. grid => 'Slic3r::Fill::Grid',
  21. flowsnake => 'Slic3r::Fill::Flowsnake',
  22. octagramspiral => 'Slic3r::Fill::OctagramSpiral',
  23. hilbertcurve => 'Slic3r::Fill::HilbertCurve',
  24. line => 'Slic3r::Fill::Line',
  25. concentric => 'Slic3r::Fill::Concentric',
  26. honeycomb => 'Slic3r::Fill::Honeycomb',
  27. '3dhoneycomb' => 'Slic3r::Fill::3DHoneycomb',
  28. );
  29. sub filler {
  30. my $self = shift;
  31. my ($filler) = @_;
  32. if (!ref $self) {
  33. return $FillTypes{$filler}->new;
  34. }
  35. $self->fillers->{$filler} ||= $FillTypes{$filler}->new(
  36. bounding_box => $self->bounding_box,
  37. );
  38. return $self->fillers->{$filler};
  39. }
  40. sub make_fill {
  41. my $self = shift;
  42. my ($layerm) = @_;
  43. Slic3r::debugf "Filling layer %d:\n", $layerm->layer->id;
  44. my $fill_density = $layerm->region->config->fill_density;
  45. my $infill_flow = $layerm->flow(FLOW_ROLE_INFILL);
  46. my $solid_infill_flow = $layerm->flow(FLOW_ROLE_SOLID_INFILL);
  47. my $top_solid_infill_flow = $layerm->flow(FLOW_ROLE_TOP_SOLID_INFILL);
  48. my @surfaces = ();
  49. # merge adjacent surfaces
  50. # in case of bridge surfaces, the ones with defined angle will be attached to the ones
  51. # without any angle (shouldn't this logic be moved to process_external_surfaces()?)
  52. {
  53. my @surfaces_with_bridge_angle = grep { $_->bridge_angle >= 0 } @{$layerm->fill_surfaces};
  54. # group surfaces by distinct properties
  55. my @groups = @{$layerm->fill_surfaces->group};
  56. # merge compatible groups (we can generate continuous infill for them)
  57. {
  58. # cache flow widths and patterns used for all solid groups
  59. # (we'll use them for comparing compatible groups)
  60. my @is_solid = my @fw = my @pattern = ();
  61. for (my $i = 0; $i <= $#groups; $i++) {
  62. # we can only merge solid non-bridge surfaces, so discard
  63. # non-solid surfaces
  64. if ($groups[$i][0]->is_solid && (!$groups[$i][0]->is_bridge || $layerm->layer->id == 0)) {
  65. $is_solid[$i] = 1;
  66. $fw[$i] = ($groups[$i][0]->surface_type == S_TYPE_TOP)
  67. ? $top_solid_infill_flow->width
  68. : $solid_infill_flow->width;
  69. $pattern[$i] = $groups[$i][0]->is_external
  70. ? $layerm->region->config->external_fill_pattern
  71. : 'rectilinear';
  72. } else {
  73. $is_solid[$i] = 0;
  74. $fw[$i] = 0;
  75. $pattern[$i] = 'none';
  76. }
  77. }
  78. # loop through solid groups
  79. for (my $i = 0; $i <= $#groups; $i++) {
  80. next if !$is_solid[$i];
  81. # find compatible groups and append them to this one
  82. for (my $j = $i+1; $j <= $#groups; $j++) {
  83. next if !$is_solid[$j];
  84. if ($fw[$i] == $fw[$j] && $pattern[$i] eq $pattern[$j]) {
  85. # groups are compatible, merge them
  86. push @{$groups[$i]}, @{$groups[$j]};
  87. splice @groups, $j, 1;
  88. splice @is_solid, $j, 1;
  89. splice @fw, $j, 1;
  90. splice @pattern, $j, 1;
  91. }
  92. }
  93. }
  94. }
  95. # give priority to bridges
  96. @groups = sort { ($a->[0]->bridge_angle >= 0) ? -1 : 0 } @groups;
  97. foreach my $group (@groups) {
  98. my $union_p = union([ map $_->p, @$group ], 1);
  99. # subtract surfaces having a defined bridge_angle from any other
  100. if (@surfaces_with_bridge_angle && $group->[0]->bridge_angle < 0) {
  101. $union_p = diff(
  102. $union_p,
  103. [ map $_->p, @surfaces_with_bridge_angle ],
  104. 1,
  105. );
  106. }
  107. # subtract any other surface already processed
  108. my $union = diff_ex(
  109. $union_p,
  110. [ map $_->p, @surfaces ],
  111. 1,
  112. );
  113. push @surfaces, map $group->[0]->clone(expolygon => $_), @$union;
  114. }
  115. }
  116. # we need to detect any narrow surfaces that might collapse
  117. # when adding spacing below
  118. # such narrow surfaces are often generated in sloping walls
  119. # by bridge_over_infill() and combine_infill() as a result of the
  120. # subtraction of the combinable area from the layer infill area,
  121. # which leaves small areas near the perimeters
  122. # we are going to grow such regions by overlapping them with the void (if any)
  123. # TODO: detect and investigate whether there could be narrow regions without
  124. # any void neighbors
  125. {
  126. my $distance_between_surfaces = max(
  127. $infill_flow->scaled_spacing,
  128. $solid_infill_flow->scaled_spacing,
  129. $top_solid_infill_flow->scaled_spacing,
  130. );
  131. my $collapsed = diff(
  132. [ map @{$_->expolygon}, @surfaces ],
  133. offset2([ map @{$_->expolygon}, @surfaces ], -$distance_between_surfaces/2, +$distance_between_surfaces/2),
  134. 1,
  135. );
  136. push @surfaces, map Slic3r::Surface->new(
  137. expolygon => $_,
  138. surface_type => S_TYPE_INTERNALSOLID,
  139. ), @{intersection_ex(
  140. offset($collapsed, $distance_between_surfaces),
  141. [
  142. (map @{$_->expolygon}, grep $_->surface_type == S_TYPE_INTERNALVOID, @surfaces),
  143. (@$collapsed),
  144. ],
  145. 1,
  146. )};
  147. }
  148. if (0) {
  149. require "Slic3r/SVG.pm";
  150. Slic3r::SVG::output("fill_" . $layerm->print_z . ".svg",
  151. expolygons => [ map $_->expolygon, grep !$_->is_solid, @surfaces ],
  152. red_expolygons => [ map $_->expolygon, grep $_->is_solid, @surfaces ],
  153. );
  154. }
  155. my @fills = ();
  156. SURFACE: foreach my $surface (@surfaces) {
  157. next if $surface->surface_type == S_TYPE_INTERNALVOID;
  158. my $filler = $layerm->region->config->fill_pattern;
  159. my $density = $fill_density;
  160. my $role = ($surface->surface_type == S_TYPE_TOP) ? FLOW_ROLE_TOP_SOLID_INFILL
  161. : $surface->is_solid ? FLOW_ROLE_SOLID_INFILL
  162. : FLOW_ROLE_INFILL;
  163. my $is_bridge = $layerm->layer->id > 0 && $surface->is_bridge;
  164. my $is_solid = $surface->is_solid;
  165. if ($surface->is_solid) {
  166. $density = 100;
  167. $filler = 'rectilinear';
  168. if ($surface->is_external && !$is_bridge) {
  169. $filler = $layerm->region->config->external_fill_pattern;
  170. }
  171. } else {
  172. next SURFACE unless $density > 0;
  173. }
  174. # get filler object
  175. my $f = $self->filler($filler);
  176. # calculate the actual flow we'll be using for this infill
  177. my $h = $surface->thickness == -1 ? $layerm->layer->height : $surface->thickness;
  178. my $flow = $layerm->region->flow(
  179. $role,
  180. $h,
  181. $is_bridge || $f->use_bridge_flow,
  182. $layerm->layer->id == 0,
  183. -1,
  184. $layerm->layer->object,
  185. );
  186. # calculate flow spacing for infill pattern generation
  187. my $using_internal_flow = 0;
  188. if (!$is_solid && !$is_bridge) {
  189. # it's internal infill, so we can calculate a generic flow spacing
  190. # for all layers, for avoiding the ugly effect of
  191. # misaligned infill on first layer because of different extrusion width and
  192. # layer height
  193. my $internal_flow = $layerm->region->flow(
  194. FLOW_ROLE_INFILL,
  195. $layerm->layer->object->config->layer_height, # TODO: handle infill_every_layers?
  196. 0, # no bridge
  197. 0, # no first layer
  198. -1, # auto width
  199. $layerm->layer->object,
  200. );
  201. $f->spacing($internal_flow->spacing);
  202. $using_internal_flow = 1;
  203. # } elsif ($surface->surface_type == S_TYPE_INTERNALBRIDGE) {
  204. # # The internal bridging layer will be sparse.
  205. # $f->spacing($flow->spacing * 2.);
  206. } else {
  207. $f->spacing($flow->spacing);
  208. }
  209. my $old_spacing = $f->spacing;
  210. $f->layer_id($layerm->layer->id);
  211. $f->z($layerm->layer->print_z);
  212. $f->angle(deg2rad($layerm->region->config->fill_angle));
  213. $f->loop_clipping(scale($flow->nozzle_diameter) * &Slic3r::LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER);
  214. # apply half spacing using this flow's own spacing and generate infill
  215. my @polylines = map $f->fill_surface(
  216. $_,
  217. density => $density/100,
  218. layer_height => $h,
  219. #FIXME Vojtech disabled the automatic extrusion width adjustment as this feature quite often
  220. # generated extrusions with excessive widths.
  221. # The goal of the automatic line width adjustment was to fill in a region without a gap, but because
  222. # the filled regions are mostly not aligned with the fill direction, very likely
  223. # the extrusion width adjustment causes more harm than good.
  224. dont_adjust => 1,
  225. ), @{ $surface->offset(-scale($f->spacing)/2) };
  226. next unless @polylines;
  227. # calculate actual flow from spacing (which might have been adjusted by the infill
  228. # pattern generator)
  229. if ($using_internal_flow) {
  230. # if we used the internal flow we're not doing a solid infill
  231. # so we can safely ignore the slight variation that might have
  232. # been applied to $f->flow_spacing
  233. } else {
  234. if (abs($old_spacing - $f->spacing) > 0.3 * $old_spacing) {
  235. print "Infill: Extreme spacing adjustment, from: ", $old_spacing, " to: ", $f->spacing, "\n";
  236. }
  237. $flow = Slic3r::Flow->new_from_spacing(
  238. spacing => $f->spacing,
  239. nozzle_diameter => $flow->nozzle_diameter,
  240. layer_height => $h,
  241. bridge => $is_bridge || $f->use_bridge_flow,
  242. );
  243. }
  244. my $mm3_per_mm = $flow->mm3_per_mm;
  245. # save into layer
  246. {
  247. my $role = $is_bridge ? EXTR_ROLE_BRIDGE
  248. : $is_solid ? (($surface->surface_type == S_TYPE_TOP) ? EXTR_ROLE_TOPSOLIDFILL : EXTR_ROLE_SOLIDFILL)
  249. : EXTR_ROLE_FILL;
  250. push @fills, my $collection = Slic3r::ExtrusionPath::Collection->new;
  251. $collection->no_sort($f->no_sort);
  252. $collection->append(
  253. map Slic3r::ExtrusionPath->new(
  254. polyline => $_,
  255. role => $role,
  256. mm3_per_mm => $mm3_per_mm,
  257. width => $flow->width,
  258. height => $flow->height,
  259. ), @polylines,
  260. );
  261. }
  262. }
  263. # add thin fill regions
  264. foreach my $thin_fill (@{$layerm->thin_fills}) {
  265. push @fills, Slic3r::ExtrusionPath::Collection->new($thin_fill);
  266. }
  267. return @fills;
  268. }
  269. 1;