Flow.pm 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. package Slic3r::Flow;
  2. use Moo;
  3. require Exporter;
  4. our @ISA = qw(Exporter);
  5. our @EXPORT_OK = qw(FLOW_ROLE_PERIMETER FLOW_ROLE_INFILL FLOW_ROLE_SOLID_INFILL FLOW_ROLE_TOP_SOLID_INFILL
  6. FLOW_ROLE_SUPPORT_MATERIAL FLOW_ROLE_SUPPORT_MATERIAL_INTERFACE);
  7. our %EXPORT_TAGS = (roles => \@EXPORT_OK);
  8. use Slic3r::Geometry qw(PI);
  9. has 'width' => (is => 'ro', required => 1);
  10. has 'spacing' => (is => 'ro', required => 1);
  11. has 'nozzle_diameter' => (is => 'ro', required => 1);
  12. has 'bridge' => (is => 'ro', default => sub {0});
  13. has 'scaled_width' => (is => 'lazy');
  14. has 'scaled_spacing' => (is => 'lazy');
  15. use constant FLOW_ROLE_PERIMETER => 1;
  16. use constant FLOW_ROLE_INFILL => 2;
  17. use constant FLOW_ROLE_SOLID_INFILL => 3;
  18. use constant FLOW_ROLE_TOP_SOLID_INFILL => 4;
  19. use constant FLOW_ROLE_SUPPORT_MATERIAL => 5;
  20. use constant FLOW_ROLE_SUPPORT_MATERIAL_INTERFACE => 6;
  21. use constant BRIDGE_EXTRA_SPACING => 0.05;
  22. use constant OVERLAP_FACTOR => 1;
  23. sub new_from_width {
  24. my ($class, %args) = @_;
  25. if ($args{width} eq '0') {
  26. $args{width} = _width(@args{qw(role nozzle_diameter layer_height bridge_flow_ratio)});
  27. } elsif ($args{width} =~ /^(\d+(?:\.\d+)?)%$/) {
  28. $args{width} = $args{layer_height} * $1 / 100;
  29. }
  30. return $class->new(
  31. width => $args{width},
  32. spacing => _spacing(@args{qw(width nozzle_diameter layer_height bridge_flow_ratio)}),
  33. nozzle_diameter => $args{nozzle_diameter},
  34. bridge => ($args{bridge_flow_ratio} > 0) ? 1 : 0,
  35. );
  36. }
  37. sub new_from_spacing {
  38. my ($class, %args) = @_;
  39. return $class->new(
  40. width => _width_from_spacing(@args{qw(spacing nozzle_diameter layer_height bridge)}),
  41. spacing => $args{spacing},
  42. nozzle_diameter => $args{nozzle_diameter},
  43. bridge => $args{bridge},
  44. );
  45. }
  46. sub clone {
  47. my $self = shift;
  48. return (ref $self)->new(
  49. width => $self->width,
  50. spacing => $self->spacing,
  51. nozzle_diameter => $self->nozzle_diameter,
  52. bridge => $self->bridge,
  53. );
  54. }
  55. sub mm3_per_mm {
  56. my ($self, $h) = @_;
  57. my $w = $self->width;
  58. my $s = $self->spacing;
  59. if ($self->bridge) {
  60. return ($w**2) * PI/4;
  61. } elsif ($w >= ($self->nozzle_diameter + $h)) {
  62. # rectangle with semicircles at the ends
  63. return $w * $h + ($h**2) / 4 * (PI - 4);
  64. } else {
  65. # rectangle with shrunk semicircles at the ends
  66. return $self->nozzle_diameter * $h * (1 - PI/4) + $h * $w * PI/4;
  67. }
  68. }
  69. sub _width {
  70. my ($role, $nozzle_diameter, $layer_height, $bridge_flow_ratio) = @_;
  71. if ($bridge_flow_ratio > 0) {
  72. return sqrt($bridge_flow_ratio * ($nozzle_diameter**2));
  73. }
  74. # here we calculate a sane default by matching the flow speed (at the nozzle) and the feed rate
  75. my $volume = ($nozzle_diameter**2) * PI/4;
  76. my $shape_threshold = $nozzle_diameter * $layer_height + ($layer_height**2) * PI/4;
  77. my $width;
  78. if ($volume >= $shape_threshold) {
  79. # rectangle with semicircles at the ends
  80. $width = (($nozzle_diameter**2) * PI + ($layer_height**2) * (4 - PI)) / (4 * $layer_height);
  81. } else {
  82. # rectangle with squished semicircles at the ends
  83. $width = $nozzle_diameter * ($nozzle_diameter/$layer_height - 4/PI + 1);
  84. }
  85. my $min = $nozzle_diameter * 1.05;
  86. my $max;
  87. if ($role == FLOW_ROLE_PERIMETER || $role == FLOW_ROLE_SUPPORT_MATERIAL) {
  88. $min = $max = $nozzle_diameter;
  89. } elsif ($role != FLOW_ROLE_INFILL) {
  90. # do not limit width for sparse infill so that we use full native flow for it
  91. $max = $nozzle_diameter * 1.7;
  92. }
  93. $width = $max if defined($max) && $width > $max;
  94. $width = $min if $width < $min;
  95. return $width;
  96. }
  97. sub _width_from_spacing {
  98. my ($s, $nozzle_diameter, $h, $bridge) = @_;
  99. if ($bridge) {
  100. return $s - BRIDGE_EXTRA_SPACING;
  101. }
  102. my $w_threshold = $h + $nozzle_diameter;
  103. my $s_threshold = $w_threshold - OVERLAP_FACTOR * ($w_threshold - ($w_threshold - $h * (1 - PI/4)));
  104. if ($s >= $s_threshold) {
  105. # rectangle with semicircles at the ends
  106. return $s + OVERLAP_FACTOR * $h * (1 - PI/4);
  107. } else {
  108. # rectangle with shrunk semicircles at the ends
  109. return ($s + $nozzle_diameter * OVERLAP_FACTOR * (PI/4 - 1)) / (1 + OVERLAP_FACTOR * (PI/4 - 1));
  110. }
  111. }
  112. sub _spacing {
  113. my ($width, $nozzle_diameter, $layer_height, $bridge_flow_ratio) = @_;
  114. if ($bridge_flow_ratio > 0) {
  115. return $width + BRIDGE_EXTRA_SPACING;
  116. }
  117. use XXX; ZZZ "here" if !defined $nozzle_diameter;
  118. my $min_flow_spacing;
  119. if ($width >= ($nozzle_diameter + $layer_height)) {
  120. # rectangle with semicircles at the ends
  121. $min_flow_spacing = $width - $layer_height * (1 - PI/4);
  122. } else {
  123. # rectangle with shrunk semicircles at the ends
  124. $min_flow_spacing = $nozzle_diameter * (1 - PI/4) + $width * PI/4;
  125. }
  126. return $width - OVERLAP_FACTOR * ($width - $min_flow_spacing);
  127. }
  128. sub _build_scaled_width {
  129. my $self = shift;
  130. return Slic3r::Geometry::scale($self->width);
  131. }
  132. sub _build_scaled_spacing {
  133. my $self = shift;
  134. return Slic3r::Geometry::scale($self->spacing);
  135. }
  136. 1;