2011-09-05 10:21:27 +00:00
package Slic3r::Fill::Rectilinear ;
2011-09-06 09:50:43 +00:00
use Moo ;
2011-09-05 10:21:27 +00:00
2011-10-06 11:43:32 +00:00
extends 'Slic3r::Fill::Base' ;
2013-04-18 15:34:21 +00:00
has 'cache' = > ( is = > 'rw' , default = > sub { { } } ) ;
2013-06-16 10:21:25 +00:00
use Slic3r::Geometry qw( A B X Y scale unscale scaled_epsilon ) ;
2011-09-05 10:21:27 +00:00
2011-10-06 11:43:32 +00:00
sub fill_surface {
2011-09-05 10:21:27 +00:00
my $ self = shift ;
2011-10-06 11:43:32 +00:00
my ( $ surface , % params ) = @ _ ;
# rotate polygons so that we can work with vertical lines here
2012-04-09 20:04:57 +00:00
my $ expolygon = $ surface - > expolygon - > clone ;
2011-10-07 17:07:57 +00:00
my $ rotate_vector = $ self - > infill_direction ( $ surface ) ;
2011-11-13 17:14:02 +00:00
$ self - > rotate_points ( $ expolygon , $ rotate_vector ) ;
2011-10-06 11:43:32 +00:00
2012-08-24 16:59:23 +00:00
my ( $ expolygon_off ) = $ expolygon - > offset_ex ( scale $ params { flow_spacing } / 2 ) ;
2013-07-07 13:17:09 +00:00
return { } if ! defined $ expolygon_off ; # skip some very small polygons (which shouldn't arrive here)
2011-12-04 15:24:46 +00:00
2013-04-18 15:34:21 +00:00
my $ flow_spacing = $ params { flow_spacing } ;
2011-12-17 18:52:34 +00:00
my $ min_spacing = scale $ params { flow_spacing } ;
2011-12-04 15:24:46 +00:00
my $ distance_between_lines = $ min_spacing / $ params { density } ;
my $ line_oscillation = $ distance_between_lines - $ min_spacing ;
2013-04-18 15:34:21 +00:00
my $ is_line_pattern = $ self - > isa ( 'Slic3r::Fill::Line' ) ;
2011-12-04 15:24:46 +00:00
2013-05-18 14:48:26 +00:00
my $ cache_id = sprintf "d%s_s%.2f_a%.2f" ,
2013-04-18 15:34:21 +00:00
$ params { density } , $ params { flow_spacing } , $ rotate_vector - > [ 0 ] [ 0 ] ;
2011-12-17 19:29:06 +00:00
2013-04-18 15:34:21 +00:00
if ( ! $ self - > cache - > { $ cache_id } ) {
# compute bounding box
2013-05-18 14:48:26 +00:00
my $ bounding_box ;
2013-04-18 15:34:21 +00:00
{
2013-06-16 10:21:25 +00:00
my $ bb_polygon = $ self - > bounding_box - > polygon ;
2013-05-18 14:48:26 +00:00
$ bb_polygon - > scale ( sqrt 2 ) ;
$ self - > rotate_points ( $ bb_polygon , $ rotate_vector ) ;
2013-06-16 10:21:25 +00:00
$ bounding_box = $ bb_polygon - > bounding_box ;
2013-04-18 15:34:21 +00:00
}
# define flow spacing according to requested density
if ( $ params { density } == 1 && ! $ params { dont_adjust } ) {
$ distance_between_lines = $ self - > adjust_solid_spacing (
2013-06-16 10:21:25 +00:00
width = > $ bounding_box - > size - > [ X ] ,
2013-04-18 15:34:21 +00:00
distance = > $ distance_between_lines ,
) ;
$ flow_spacing = unscale $ distance_between_lines ;
2013-04-11 17:36:49 +00:00
}
2013-04-18 15:34:21 +00:00
# generate the basic pattern
2013-06-16 10:21:25 +00:00
my $ x = $ bounding_box - > x_min ;
2013-04-18 15:34:21 +00:00
my @ vertical_lines = ( ) ;
2013-06-16 10:21:25 +00:00
for ( my $ i = 0 ; $ x <= $ bounding_box - > x_max + scaled_epsilon ; $ i + + ) {
my $ vertical_line = Slic3r::Line - > new ( [ $ x , $ bounding_box - > y_max ] , [ $ x , $ bounding_box - > y_min ] ) ;
2013-04-18 15:34:21 +00:00
if ( $ is_line_pattern && $ i % 2 ) {
$ vertical_line - > [ A ] [ X ] += $ line_oscillation ;
$ vertical_line - > [ B ] [ X ] -= $ line_oscillation ;
}
push @ vertical_lines , $ vertical_line ;
$ x += $ distance_between_lines ;
}
$ self - > cache - > { $ cache_id } = [ @ vertical_lines ] ;
2011-12-04 15:24:46 +00:00
}
2012-08-24 16:59:23 +00:00
# clip paths against a slightly offsetted expolygon, so that the first and last paths
# are kept even if the expolygon has vertical sides
2013-01-27 23:32:19 +00:00
my @ paths = @ { Boost::Geometry::Utils:: polygon_multi_linestring_intersection (
2013-07-07 13:17:09 +00:00
+ ( $ expolygon - > offset_ex ( scaled_epsilon ) ) [ 0 ] - > arrayref , # TODO: we should use all the resulting expolygons and clip the linestrings to a multipolygon object
2013-04-18 15:34:21 +00:00
[ @ { $ self - > cache - > { $ cache_id } } ] ,
2012-04-09 09:04:32 +00:00
) } ;
2011-12-04 15:24:46 +00:00
# connect lines
2012-10-24 14:17:09 +00:00
unless ( $ params { dont_connect } ) {
2012-10-30 13:07:01 +00:00
my $ collection = Slic3r::Polyline::Collection - > new (
polylines = > [ map Slic3r::Polyline - > new ( @$ _ ) , @ paths ] ,
2011-12-04 15:24:46 +00:00
) ;
@ paths = ( ) ;
2012-09-12 13:22:43 +00:00
my $ tolerance = 10 * scaled_epsilon ;
2013-06-16 16:05:56 +00:00
my $ diagonal_distance = $ distance_between_lines * 2 ;
2012-05-04 18:51:09 +00:00
my $ can_connect = $ is_line_pattern
? sub {
( $ _ [ X ] >= ( $ distance_between_lines - $ line_oscillation ) - $ tolerance ) && ( $ _ [ X ] <= ( $ distance_between_lines + $ line_oscillation ) + $ tolerance )
2012-06-23 18:27:28 +00:00
&& $ _ [ Y ] <= $ diagonal_distance
2012-05-04 18:51:09 +00:00
}
2013-06-16 16:05:56 +00:00
: sub { $ _ [ X ] <= $ diagonal_distance && $ _ [ Y ] <= $ diagonal_distance } ;
2011-12-04 15:24:46 +00:00
2013-02-05 16:27:45 +00:00
foreach my $ path ( $ collection - > chained_path ) {
2011-12-04 15:24:46 +00:00
if ( @ paths ) {
2012-10-30 13:07:01 +00:00
my @ distance = map abs ( $ path - > [ 0 ] [ $ _ ] - $ paths [ - 1 ] [ - 1 ] [ $ _ ] ) , ( X , Y ) ;
2011-12-04 15:24:46 +00:00
# TODO: we should also check that both points are on a fill_boundary to avoid
# connecting paths on the boundaries of internal regions
2012-10-30 13:07:01 +00:00
if ( $ can_connect - > ( @ distance , $ paths [ - 1 ] [ - 1 ] , $ path - > [ 0 ] )
&& $ expolygon_off - > encloses_line ( Slic3r::Line - > new ( $ paths [ - 1 ] [ - 1 ] , $ path - > [ 0 ] ) , $ tolerance ) ) {
push @ { $ paths [ - 1 ] } , @$ path ;
2011-12-04 15:24:46 +00:00
next ;
}
}
2012-10-30 13:07:01 +00:00
push @ paths , $ path ;
2011-12-04 15:24:46 +00:00
}
2011-09-25 18:09:30 +00:00
}
2011-10-06 11:43:32 +00:00
# paths must be rotated back
$ self - > rotate_points_back ( \ @ paths , $ rotate_vector ) ;
2011-12-17 18:52:34 +00:00
return { flow_spacing = > $ flow_spacing } , @ paths ;
2011-09-05 10:21:27 +00:00
}
1 ;