Rectilinear.pm 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. package Slic3r::Fill::Rectilinear;
  2. use Moo;
  3. extends 'Slic3r::Fill::Base';
  4. use Slic3r::Geometry qw(X1 Y1 X2 Y2 A B X Y scale unscale epsilon);
  5. sub fill_surface {
  6. my $self = shift;
  7. my ($surface, %params) = @_;
  8. # rotate polygons so that we can work with vertical lines here
  9. my $expolygon = $surface->expolygon->clone;
  10. my $rotate_vector = $self->infill_direction($surface);
  11. $self->rotate_points($expolygon, $rotate_vector);
  12. my ($expolygon_off) = $expolygon->offset_ex(scale 0.3);
  13. return {} if !$expolygon_off; # skip some very small polygons (which shouldn't arrive here)
  14. my ($expolygon_epsilon_off) = $expolygon->offset_ex(scale epsilon);
  15. my $bounding_box = [ $expolygon->bounding_box ];
  16. my $min_spacing = scale $params{flow_spacing};
  17. my $distance_between_lines = $min_spacing / $params{density};
  18. my $line_oscillation = $distance_between_lines - $min_spacing;
  19. my $flow_spacing;
  20. if ($params{density} == 1) {
  21. $distance_between_lines = $self->adjust_solid_spacing(
  22. width => $bounding_box->[X2] - $bounding_box->[X1],
  23. distance => $distance_between_lines,
  24. );
  25. $flow_spacing = unscale $distance_between_lines;
  26. }
  27. my $overlap_distance = scale($self->layer->flow_width || $Slic3r::flow_width) * 0.4;
  28. my $x = $bounding_box->[X1];
  29. my $is_line_pattern = $self->isa('Slic3r::Fill::Line');
  30. my @vertical_lines = ();
  31. for (my $i = 0; $x <= $bounding_box->[X2] + scale epsilon; $i++) {
  32. my $vertical_line = Slic3r::Line->new([$x, $bounding_box->[Y2]], [$x, $bounding_box->[Y1]]);
  33. if ($is_line_pattern && $i % 2) {
  34. $vertical_line->[A][X] += $line_oscillation;
  35. $vertical_line->[B][X] -= $line_oscillation;
  36. }
  37. push @vertical_lines, $vertical_line;
  38. $x += $distance_between_lines;
  39. }
  40. my @paths = @{ Boost::Geometry::Utils::polygon_linestring_intersection(
  41. $expolygon_epsilon_off->boost_polygon,
  42. Boost::Geometry::Utils::linestring(@vertical_lines),
  43. ) };
  44. for (@paths) {
  45. $_->[0][Y] += $overlap_distance;
  46. $_->[-1][Y] -= $overlap_distance;
  47. }
  48. # connect lines
  49. {
  50. my $collection = Slic3r::ExtrusionPath::Collection->new(
  51. paths => [ map Slic3r::ExtrusionPath->new(polyline => Slic3r::Polyline->new(@$_), role => -1), @paths ],
  52. );
  53. @paths = ();
  54. my $tolerance = scale epsilon;
  55. my $diagonal_distance = $distance_between_lines * 5;
  56. my $can_connect = $is_line_pattern
  57. ? sub {
  58. ($_[X] >= ($distance_between_lines - $line_oscillation) - $tolerance) && ($_[X] <= ($distance_between_lines + $line_oscillation) + $tolerance)
  59. && abs($_[Y]) <= $diagonal_distance
  60. }
  61. : sub { abs($_[X] - $distance_between_lines) <= $tolerance && abs($_[Y]) <= $diagonal_distance };
  62. foreach my $path ($collection->shortest_path) {
  63. if (@paths) {
  64. my @distance = map +($path->points->[0][$_] - $paths[-1][-1][$_]), (X,Y);
  65. # TODO: we should also check that both points are on a fill_boundary to avoid
  66. # connecting paths on the boundaries of internal regions
  67. if ($can_connect->(@distance, $paths[-1][-1], $path->points->[0])
  68. && $expolygon_off->encloses_line(Slic3r::Line->new($paths[-1][-1], $path->points->[0]))) {
  69. push @{$paths[-1]}, @{$path->points};
  70. next;
  71. }
  72. }
  73. push @paths, $path->points;
  74. }
  75. }
  76. # paths must be rotated back
  77. $self->rotate_points_back(\@paths, $rotate_vector);
  78. return { flow_spacing => $flow_spacing }, @paths;
  79. }
  80. 1;