Layer.pm 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633
  1. package Slic3r::Layer;
  2. use Moo;
  3. use Math::Clipper ':all';
  4. use Slic3r::ExtrusionPath ':roles';
  5. use Slic3r::Geometry qw(scale unscale collinear X Y A B PI rad2deg_dir bounding_box_center shortest_path);
  6. use Slic3r::Geometry::Clipper qw(safety_offset union_ex diff_ex intersection_ex xor_ex is_counter_clockwise);
  7. use Slic3r::Surface ':types';
  8. # a sequential number of layer, starting at 0
  9. has 'id' => (
  10. is => 'rw',
  11. #isa => 'Int',
  12. required => 1,
  13. );
  14. has 'slicing_errors' => (is => 'rw');
  15. has 'slice_z' => (is => 'lazy');
  16. has 'print_z' => (is => 'lazy');
  17. has 'height' => (is => 'lazy');
  18. has 'flow' => (is => 'lazy');
  19. has 'perimeters_flow' => (is => 'lazy');
  20. has 'infill_flow' => (is => 'lazy');
  21. # collection of spare segments generated by slicing the original geometry;
  22. # these need to be merged in continuos (closed) polylines
  23. has 'lines' => (
  24. is => 'rw',
  25. #isa => 'ArrayRef[ArrayRef]',
  26. default => sub { [] },
  27. );
  28. # collection of surfaces generated by slicing the original geometry
  29. has 'slices' => (is => 'ro', default => sub { [] });
  30. # collection of polygons or polylines representing thin walls contained
  31. # in the original geometry
  32. has 'thin_walls' => (is => 'ro', default => sub { [] });
  33. # collection of expolygons generated by offsetting the innermost perimeter(s)
  34. # they represent boundaries of areas to fill
  35. has 'fill_boundaries' => (is => 'ro', default => sub { [] });
  36. # collection of polygons or polylines representing thin infill regions that
  37. # need to be filled with a medial axis
  38. has 'thin_fills' => (is => 'ro', default => sub { [] });
  39. # collection of surfaces generated by clipping the slices to the fill boundaries
  40. has 'surfaces' => (
  41. is => 'rw',
  42. #isa => 'ArrayRef[Slic3r::Surface]',
  43. default => sub { [] },
  44. );
  45. # collection of surfaces for infill
  46. has 'fill_surfaces' => (
  47. is => 'rw',
  48. #isa => 'ArrayRef[Slic3r::Surface]',
  49. default => sub { [] },
  50. );
  51. # ordered collection of extrusion paths to build all perimeters
  52. has 'perimeters' => (
  53. is => 'rw',
  54. #isa => 'ArrayRef[Slic3r::ExtrusionLoop]',
  55. default => sub { [] },
  56. );
  57. # ordered collection of extrusion paths to fill surfaces for support material
  58. has 'support_fills' => (
  59. is => 'rw',
  60. #isa => 'Slic3r::ExtrusionPath::Collection',
  61. );
  62. # ordered collection of extrusion paths to fill surfaces
  63. has 'fills' => (
  64. is => 'rw',
  65. #isa => 'ArrayRef[Slic3r::ExtrusionPath::Collection]',
  66. default => sub { [] },
  67. );
  68. # Z used for slicing
  69. sub _build_slice_z {
  70. my $self = shift;
  71. if ($self->id == 0) {
  72. return $Slic3r::_first_layer_height / 2 / $Slic3r::scaling_factor;
  73. }
  74. return ($Slic3r::_first_layer_height + (($self->id-1) * $Slic3r::layer_height) + ($Slic3r::layer_height/2))
  75. / $Slic3r::scaling_factor; #/
  76. }
  77. # Z used for printing
  78. sub _build_print_z {
  79. my $self = shift;
  80. return ($Slic3r::_first_layer_height + ($self->id * $Slic3r::layer_height)) / $Slic3r::scaling_factor;
  81. }
  82. sub _build_height {
  83. my $self = shift;
  84. return $self->id == 0 ? $Slic3r::_first_layer_height : $Slic3r::layer_height;
  85. }
  86. sub _build_flow {
  87. my $self = shift;
  88. return $self->id == 0 && $Slic3r::first_layer_flow
  89. ? $Slic3r::first_layer_flow
  90. : $Slic3r::flow;
  91. }
  92. sub _build_perimeters_flow {
  93. my $self = shift;
  94. return $self->id == 0 && $Slic3r::first_layer_flow
  95. ? $Slic3r::first_layer_flow
  96. : $Slic3r::perimeters_flow;
  97. }
  98. sub _build_infill_flow {
  99. my $self = shift;
  100. return $self->id == 0 && $Slic3r::first_layer_flow
  101. ? $Slic3r::first_layer_flow
  102. : $Slic3r::infill_flow;
  103. }
  104. sub add_line {
  105. my $self = shift;
  106. my ($line) = @_;
  107. push @{ $self->lines }, $line;
  108. return $line;
  109. }
  110. # build polylines from lines
  111. sub make_surfaces {
  112. my $self = shift;
  113. my ($loops) = @_;
  114. {
  115. my $safety_offset = scale 0.1;
  116. # merge everything
  117. my $expolygons = [ map $_->offset_ex(-$safety_offset), @{union_ex(safety_offset($loops, $safety_offset))} ];
  118. Slic3r::debugf " %d surface(s) having %d holes detected from %d polylines\n",
  119. scalar(@$expolygons), scalar(map $_->holes, @$expolygons), scalar(@$loops);
  120. push @{$self->slices},
  121. map Slic3r::Surface->new(expolygon => $_, surface_type => S_TYPE_INTERNAL),
  122. @$expolygons;
  123. }
  124. # the contours must be offsetted by half extrusion width inwards
  125. {
  126. my $distance = scale $self->perimeters_flow->width / 2;
  127. my @surfaces = @{$self->slices};
  128. @{$self->slices} = ();
  129. foreach my $surface (@surfaces) {
  130. push @{$self->slices}, map Slic3r::Surface->new
  131. (expolygon => $_, surface_type => S_TYPE_INTERNAL),
  132. map $_->offset_ex(+$distance),
  133. $surface->expolygon->offset_ex(-2*$distance);
  134. }
  135. # now detect thin walls by re-outgrowing offsetted surfaces and subtracting
  136. # them from the original slices
  137. my $outgrown = Math::Clipper::offset([ map $_->p, @{$self->slices} ], $distance);
  138. my $diff = diff_ex(
  139. [ map $_->p, @surfaces ],
  140. $outgrown,
  141. 1,
  142. );
  143. if (@$diff) {
  144. my $area_threshold = scale($self->perimeters_flow->spacing) ** 2;
  145. @$diff = grep $_->area > ($area_threshold), @$diff;
  146. push @{$self->thin_walls},
  147. map $_->medial_axis(scale $self->perimeters_flow->width),
  148. @$diff;
  149. Slic3r::debugf " %d thin walls detected\n", scalar(@{$self->thin_walls}) if @{$self->thin_walls};
  150. }
  151. }
  152. if (0) {
  153. require "Slic3r/SVG.pm";
  154. Slic3r::SVG::output(undef, "surfaces.svg",
  155. polygons => [ map $_->contour, @{$self->slices} ],
  156. red_polygons => [ map $_->p, map @{$_->holes}, @{$self->slices} ],
  157. );
  158. }
  159. }
  160. sub make_perimeters {
  161. my $self = shift;
  162. Slic3r::debugf "Making perimeters for layer %d\n", $self->id;
  163. my $gap_area_threshold = scale($self->perimeters_flow->width)** 2;
  164. # this array will hold one arrayref per original surface (island);
  165. # each item of this arrayref is an arrayref representing a depth (from outer
  166. # perimeters to inner); each item of this arrayref is an ExPolygon:
  167. # @perimeters = (
  168. # [ # first island
  169. # [ Slic3r::ExPolygon, Slic3r::ExPolygon... ], #depth 0: outer loop
  170. # [ Slic3r::ExPolygon, Slic3r::ExPolygon... ], #depth 1: inner loop
  171. # ],
  172. # [ # second island
  173. # ...
  174. # ]
  175. # )
  176. my @perimeters = (); # one item per depth; each item
  177. # organize islands using a shortest path search
  178. my @surfaces = @{shortest_path([
  179. map [ $_->contour->[0], $_ ], @{$self->slices},
  180. ])};
  181. # for each island:
  182. foreach my $surface (@surfaces) {
  183. my @last_offsets = ($surface->expolygon);
  184. my $distance = 0;
  185. # experimental hole compensation (see ArcCompensation in the RepRap wiki)
  186. if (0) {
  187. foreach my $hole ($last_offsets[0]->holes) {
  188. my $circumference = abs($hole->length);
  189. next unless $circumference <= $Slic3r::small_perimeter_length;
  190. # this compensation only works for circular holes, while it would
  191. # overcompensate for hexagons and other shapes having straight edges.
  192. # so we require a minimum number of vertices.
  193. next unless $circumference / @$hole >= scale 3 * $Slic3r::flow->width;
  194. # revert the compensation done in make_surfaces() and get the actual radius
  195. # of the hole
  196. my $radius = ($circumference / PI / 2) - scale $self->perimeters_flow->spacing/2;
  197. my $new_radius = (scale($self->perimeters_flow->width) + sqrt((scale($self->perimeters_flow->width)**2) + (4*($radius**2)))) / 2;
  198. # holes are always turned to contours, so reverse point order before and after
  199. $hole->reverse;
  200. my @offsetted = $hole->offset(+ ($new_radius - $radius));
  201. # skip arc compensation when hole is not round (thus leads to multiple offsets)
  202. @$hole = map Slic3r::Point->new($_), @{ $offsetted[0] } if @offsetted == 1;
  203. $hole->reverse;
  204. }
  205. }
  206. my @gaps = ();
  207. # generate perimeters inwards
  208. my $loop_number = $Slic3r::perimeters + ($surface->additional_inner_perimeters || 0);
  209. push @perimeters, [];
  210. for (my $loop = 0; $loop < $loop_number; $loop++) {
  211. # offsetting a polygon can result in one or many offset polygons
  212. if ($distance) {
  213. my @new_offsets = ();
  214. foreach my $expolygon (@last_offsets) {
  215. my @offsets = map $_->offset_ex(+0.5*$distance), $expolygon->offset_ex(-1.5*$distance);
  216. push @new_offsets, @offsets;
  217. my $diff = diff_ex(
  218. [ map @$_, $expolygon->offset_ex(-$distance) ],
  219. [ map @$_, @offsets ],
  220. );
  221. push @gaps, grep $_->area >= $gap_area_threshold, @$diff;
  222. }
  223. @last_offsets = @new_offsets;
  224. }
  225. last if !@last_offsets;
  226. push @{ $perimeters[-1] }, [@last_offsets];
  227. # offset distance for inner loops
  228. $distance = scale $self->perimeters_flow->spacing;
  229. }
  230. # create one more offset to be used as boundary for fill
  231. {
  232. my @fill_boundaries = map $_->offset_ex(-$distance), @last_offsets;
  233. $_->simplify(scale $Slic3r::resolution) for @fill_boundaries;
  234. push @{ $self->fill_boundaries }, @fill_boundaries;
  235. # detect the small gaps that we need to treat like thin polygons,
  236. # thus generating the skeleton and using it to fill them
  237. push @{ $self->thin_fills },
  238. map $_->medial_axis(scale $self->perimeters_flow->width),
  239. @gaps;
  240. Slic3r::debugf " %d gaps filled\n", scalar @{ $self->thin_fills }
  241. if @{ $self->thin_fills };
  242. }
  243. }
  244. # process one island (original surface) at time
  245. foreach my $island (@perimeters) {
  246. # do holes starting from innermost one
  247. my @holes = ();
  248. my %is_external = ();
  249. my @hole_depths = map [ map $_->holes, @$_ ], @$island;
  250. # organize the outermost hole loops using a shortest path search
  251. @{$hole_depths[0]} = @{shortest_path([
  252. map [ $_->[0], $_ ], @{$hole_depths[0]},
  253. ])};
  254. CYCLE: while (map @$_, @hole_depths) {
  255. shift @hole_depths while !@{$hole_depths[0]};
  256. # take first available hole
  257. push @holes, shift @{$hole_depths[0]};
  258. $is_external{$#holes} = 1;
  259. my $current_depth = 0;
  260. while (1) {
  261. $current_depth++;
  262. # look for the hole containing this one if any
  263. next CYCLE if !$hole_depths[$current_depth];
  264. my $parent_hole;
  265. for (@{$hole_depths[$current_depth]}) {
  266. if ($_->encloses_point($holes[-1]->[0])) {
  267. $parent_hole = $_;
  268. last;
  269. }
  270. }
  271. next CYCLE if !$parent_hole;
  272. # look for other holes contained in such parent
  273. for (@{$hole_depths[$current_depth-1]}) {
  274. if ($parent_hole->encloses_point($_->[0])) {
  275. # we have a sibling, so let's move onto next iteration
  276. next CYCLE;
  277. }
  278. }
  279. push @holes, $parent_hole;
  280. @{$hole_depths[$current_depth]} = grep $_ ne $parent_hole, @{$hole_depths[$current_depth]};
  281. }
  282. }
  283. # do holes, then contours starting from innermost one
  284. $self->add_perimeter($holes[$_], $is_external{$_} ? EXTR_ROLE_EXTERNAL_PERIMETER : undef)
  285. for reverse 0 .. $#holes;
  286. for my $depth (reverse 0 .. $#$island) {
  287. my $role = $depth == $#$island ? EXTR_ROLE_CONTOUR_INTERNAL_PERIMETER
  288. : $depth == 0 ? EXTR_ROLE_EXTERNAL_PERIMETER
  289. : EXTR_ROLE_PERIMETER;
  290. $self->add_perimeter($_, $role) for map $_->contour, @{$island->[$depth]};
  291. }
  292. }
  293. # add thin walls as perimeters
  294. {
  295. my @thin_paths = ();
  296. for (@{ $self->thin_walls }) {
  297. if ($_->isa('Slic3r::Polygon')) {
  298. push @thin_paths, Slic3r::ExtrusionLoop->pack(polygon => $_, role => EXTR_ROLE_PERIMETER);
  299. } else {
  300. push @thin_paths, Slic3r::ExtrusionPath->pack(polyline => $_, role => EXTR_ROLE_PERIMETER);
  301. }
  302. $thin_paths[-1]->flow_spacing($self->perimeters_flow->spacing);
  303. }
  304. my $collection = Slic3r::ExtrusionPath::Collection->new(paths => \@thin_paths);
  305. push @{ $self->perimeters }, $collection->shortest_path;
  306. }
  307. }
  308. sub add_perimeter {
  309. my $self = shift;
  310. my ($polygon, $role) = @_;
  311. return unless $polygon->is_printable($self->perimeters_flow->width);
  312. push @{ $self->perimeters }, Slic3r::ExtrusionLoop->pack(
  313. polygon => $polygon,
  314. role => (abs($polygon->length) <= $Slic3r::small_perimeter_length) ? EXTR_ROLE_SMALLPERIMETER : ($role // EXTR_ROLE_PERIMETER), #/
  315. flow_spacing => $self->perimeters_flow->spacing,
  316. );
  317. }
  318. sub prepare_fill_surfaces {
  319. my $self = shift;
  320. my @surfaces = @{$self->surfaces};
  321. # merge too small internal surfaces with their surrounding tops
  322. # (if they're too small, they can be treated as solid)
  323. {
  324. my $min_area = ((7 * $self->infill_flow->spacing / $Slic3r::scaling_factor)**2) * PI;
  325. my $small_internal = [
  326. grep { $_->expolygon->contour->area <= $min_area }
  327. grep { $_->surface_type == S_TYPE_INTERNAL }
  328. @surfaces
  329. ];
  330. foreach my $s (@$small_internal) {
  331. @surfaces = grep $_ ne $s, @surfaces;
  332. }
  333. my $union = union_ex([
  334. (map $_->p, grep $_->surface_type == S_TYPE_TOP, @surfaces),
  335. (map @$_, map $_->expolygon->safety_offset, @$small_internal),
  336. ]);
  337. my @top = map Slic3r::Surface->new(expolygon => $_, surface_type => S_TYPE_TOP), @$union;
  338. @surfaces = (grep($_->surface_type != S_TYPE_TOP, @surfaces), @top);
  339. }
  340. # remove top/bottom surfaces
  341. if ($Slic3r::solid_layers == 0) {
  342. $_->surface_type(S_TYPE_INTERNAL) for grep $_->surface_type != S_TYPE_INTERNAL, @surfaces;
  343. }
  344. # remove internal surfaces
  345. if ($Slic3r::fill_density == 0) {
  346. @surfaces = grep $_->surface_type != S_TYPE_INTERNAL, @surfaces;
  347. }
  348. $self->fill_surfaces([@surfaces]);
  349. }
  350. sub remove_small_surfaces {
  351. my $self = shift;
  352. my $distance = scale $self->infill_flow->spacing / 2;
  353. my @surfaces = @{$self->fill_surfaces};
  354. @{$self->fill_surfaces} = ();
  355. foreach my $surface (@surfaces) {
  356. # offset inwards
  357. my @offsets = $surface->expolygon->offset_ex(-$distance);
  358. # offset the results outwards again and merge the results
  359. @offsets = map $_->offset_ex($distance), @offsets;
  360. @offsets = @{ union_ex([ map @$_, @offsets ], undef, 1) };
  361. push @{$self->fill_surfaces}, map Slic3r::Surface->new(
  362. expolygon => $_,
  363. surface_type => $surface->surface_type), @offsets;
  364. }
  365. Slic3r::debugf "identified %d small surfaces at layer %d\n",
  366. (@surfaces - @{$self->fill_surfaces}), $self->id
  367. if @{$self->fill_surfaces} != @surfaces;
  368. # the difference between @surfaces and $self->fill_surfaces
  369. # is what's too small; we add it back as solid infill
  370. if ($Slic3r::fill_density > 0) {
  371. my $diff = diff_ex(
  372. [ map $_->p, @surfaces ],
  373. [ map $_->p, @{$self->fill_surfaces} ],
  374. );
  375. push @{$self->fill_surfaces}, map Slic3r::Surface->new(
  376. expolygon => $_,
  377. surface_type => S_TYPE_INTERNALSOLID), @$diff;
  378. }
  379. }
  380. # make bridges printable
  381. sub process_bridges {
  382. my $self = shift;
  383. # no bridges are possible if we have no internal surfaces
  384. return if $Slic3r::fill_density == 0;
  385. my @bridges = ();
  386. # a bottom surface on a layer > 0 is either a bridge or a overhang
  387. # or a combination of both; any top surface is a candidate for
  388. # reverse bridge processing
  389. my @solid_surfaces = grep {
  390. ($_->surface_type == S_TYPE_BOTTOM && $self->id > 0) || $_->surface_type == S_TYPE_TOP
  391. } @{$self->fill_surfaces} or return;
  392. my @internal_surfaces = grep { $_->surface_type == S_TYPE_INTERNAL || $_->surface_type == S_TYPE_INTERNALSOLID } @{$self->slices};
  393. SURFACE: foreach my $surface (@solid_surfaces) {
  394. my $expolygon = $surface->expolygon->safety_offset;
  395. my $description = $surface->surface_type == S_TYPE_BOTTOM ? 'bridge/overhang' : 'reverse bridge';
  396. # offset the contour and intersect it with the internal surfaces to discover
  397. # which of them has contact with our bridge
  398. my @supporting_surfaces = ();
  399. my ($contour_offset) = $expolygon->contour->offset(scale $self->flow->spacing * sqrt(2));
  400. foreach my $internal_surface (@internal_surfaces) {
  401. my $intersection = intersection_ex([$contour_offset], [$internal_surface->p]);
  402. if (@$intersection) {
  403. push @supporting_surfaces, $internal_surface;
  404. }
  405. }
  406. if (0) {
  407. require "Slic3r/SVG.pm";
  408. Slic3r::SVG::output(undef, "bridge_surfaces.svg",
  409. green_polygons => [ map $_->p, @supporting_surfaces ],
  410. red_polygons => [ @$expolygon ],
  411. );
  412. }
  413. Slic3r::debugf "Found $description on layer %d with %d support(s)\n",
  414. $self->id, scalar(@supporting_surfaces);
  415. next SURFACE unless @supporting_surfaces;
  416. my $bridge_angle = undef;
  417. if ($surface->surface_type == S_TYPE_BOTTOM) {
  418. # detect optimal bridge angle
  419. my $bridge_over_hole = 0;
  420. my @edges = (); # edges are POLYLINES
  421. foreach my $supporting_surface (@supporting_surfaces) {
  422. my @surface_edges = map $_->clip_with_polygon($contour_offset),
  423. ($supporting_surface->contour, $supporting_surface->holes);
  424. if (@supporting_surfaces == 1 && @surface_edges == 1
  425. && @{$supporting_surface->contour} == @{$surface_edges[0]}) {
  426. $bridge_over_hole = 1;
  427. }
  428. push @edges, grep { @$_ } @surface_edges;
  429. }
  430. Slic3r::debugf " Bridge is supported on %d edge(s)\n", scalar(@edges);
  431. Slic3r::debugf " and covers a hole\n" if $bridge_over_hole;
  432. if (0) {
  433. require "Slic3r/SVG.pm";
  434. Slic3r::SVG::output(undef, "bridge_edges.svg",
  435. polylines => [ map $_->p, @edges ],
  436. );
  437. }
  438. if (@edges == 2) {
  439. my @chords = map Slic3r::Line->new($_->[0], $_->[-1]), @edges;
  440. my @midpoints = map $_->midpoint, @chords;
  441. my $line_between_midpoints = Slic3r::Line->new(@midpoints);
  442. $bridge_angle = rad2deg_dir($line_between_midpoints->direction);
  443. } elsif (@edges == 1) {
  444. # TODO: this case includes both U-shaped bridges and plain overhangs;
  445. # we need a trapezoidation algorithm to detect the actual bridged area
  446. # and separate it from the overhang area.
  447. # in the mean time, we're treating as overhangs all cases where
  448. # our supporting edge is a straight line
  449. if (@{$edges[0]} > 2) {
  450. my $line = Slic3r::Line->new($edges[0]->[0], $edges[0]->[-1]);
  451. $bridge_angle = rad2deg_dir($line->direction);
  452. }
  453. } elsif (@edges) {
  454. my $center = bounding_box_center([ map @$_, @edges ]);
  455. my $x = my $y = 0;
  456. foreach my $point (map @$, @edges) {
  457. my $line = Slic3r::Line->new($center, $point);
  458. my $dir = $line->direction;
  459. my $len = $line->length;
  460. $x += cos($dir) * $len;
  461. $y += sin($dir) * $len;
  462. }
  463. $bridge_angle = rad2deg_dir(atan2($y, $x));
  464. }
  465. Slic3r::debugf " Optimal infill angle of bridge on layer %d is %d degrees\n",
  466. $self->id, $bridge_angle if defined $bridge_angle;
  467. }
  468. # now, extend our bridge by taking a portion of supporting surfaces
  469. {
  470. # offset the bridge by the specified amount of mm (minimum 3)
  471. my $bridge_overlap = scale 3;
  472. my ($bridge_offset) = $expolygon->contour->offset($bridge_overlap);
  473. # calculate the new bridge
  474. my $intersection = intersection_ex(
  475. [ @$expolygon, map $_->p, @supporting_surfaces ],
  476. [ $bridge_offset ],
  477. );
  478. push @bridges, map Slic3r::Surface->new(
  479. expolygon => $_,
  480. surface_type => $surface->surface_type,
  481. bridge_angle => $bridge_angle,
  482. ), @$intersection;
  483. }
  484. }
  485. # now we need to merge bridges to avoid overlapping
  486. {
  487. # build a list of unique bridge types
  488. my @surface_groups = Slic3r::Surface->group(@bridges);
  489. # merge bridges of the same type, removing any of the bridges already merged;
  490. # the order of @surface_groups determines the priority between bridges having
  491. # different surface_type or bridge_angle
  492. @bridges = ();
  493. foreach my $surfaces (@surface_groups) {
  494. my $union = union_ex([ map $_->p, @$surfaces ]);
  495. my $diff = diff_ex(
  496. [ map @$_, @$union ],
  497. [ map $_->p, @bridges ],
  498. );
  499. push @bridges, map Slic3r::Surface->new(
  500. expolygon => $_,
  501. surface_type => $surfaces->[0]->surface_type,
  502. bridge_angle => $surfaces->[0]->bridge_angle,
  503. ), @$union;
  504. }
  505. }
  506. # apply bridges to layer
  507. {
  508. my @surfaces = @{$self->fill_surfaces};
  509. @{$self->fill_surfaces} = ();
  510. # intersect layer surfaces with bridges to get actual bridges
  511. foreach my $bridge (@bridges) {
  512. my $actual_bridge = intersection_ex(
  513. [ map $_->p, @surfaces ],
  514. [ $bridge->p ],
  515. );
  516. push @{$self->fill_surfaces}, map Slic3r::Surface->new(
  517. expolygon => $_,
  518. surface_type => $bridge->surface_type,
  519. bridge_angle => $bridge->bridge_angle,
  520. ), @$actual_bridge;
  521. }
  522. # difference between layer surfaces and bridges are the other surfaces
  523. foreach my $group (Slic3r::Surface->group(@surfaces)) {
  524. my $difference = diff_ex(
  525. [ map $_->p, @$group ],
  526. [ map $_->p, @bridges ],
  527. );
  528. push @{$self->fill_surfaces}, map Slic3r::Surface->new(
  529. expolygon => $_,
  530. surface_type => $group->[0]->surface_type), @$difference;
  531. }
  532. }
  533. }
  534. 1;