~ubuntu-branches/ubuntu/utopic/slic3r/utopic

« back to all changes in this revision

Viewing changes to lib/Slic3r/GCode/ArcFitting.pm

  • Committer: Package Import Robot
  • Author(s): Chow Loong Jin
  • Date: 2014-06-17 01:27:26 UTC
  • Revision ID: package-import@ubuntu.com-20140617012726-2wrs4zdo251nr4vg
Tags: upstream-1.1.4+dfsg
Import upstream version 1.1.4+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
package Slic3r::GCode::ArcFitting;
 
2
use Moo;
 
3
 
 
4
use Slic3r::Geometry qw(X Y PI scale unscale epsilon scaled_epsilon deg2rad angle3points);
 
5
 
 
6
extends 'Slic3r::GCode::Reader';
 
7
has 'config'                    => (is => 'ro', required => 0);
 
8
has 'min_segments'              => (is => 'rw', default => sub { 2 });
 
9
has 'min_total_angle'           => (is => 'rw', default => sub { deg2rad(30) });
 
10
has 'max_relative_angle'        => (is => 'rw', default => sub { deg2rad(15) });
 
11
has 'len_epsilon'               => (is => 'rw', default => sub { scale 0.2 });
 
12
has 'angle_epsilon'             => (is => 'rw', default => sub { abs(deg2rad(10)) });
 
13
has '_extrusion_axis'           => (is => 'lazy');
 
14
has '_path'                     => (is => 'rw');
 
15
has '_cur_F'                    => (is => 'rw');
 
16
has '_cur_E'                    => (is => 'rw');
 
17
has '_cur_E0'                   => (is => 'rw');
 
18
has '_comment'                  => (is => 'rw');
 
19
 
 
20
sub _build__extrusion_axis {
 
21
    my ($self) = @_;
 
22
    return $self->config ? $self->config->get_extrusion_axis : 'E';
 
23
}
 
24
 
 
25
sub process {
 
26
    my $self = shift;
 
27
    my ($gcode) = @_;
 
28
    
 
29
    die "Arc fitting is not available (incomplete feature)\n";
 
30
    die "Arc fitting doesn't support extrusion axis not being E\n" if $self->_extrusion_axis ne 'E';
 
31
    
 
32
    my $new_gcode = "";
 
33
    
 
34
    $self->parse($gcode, sub {
 
35
        my ($reader, $cmd, $args, $info) = @_;
 
36
        
 
37
        if ($info->{extruding} && $info->{dist_XY} > 0) {
 
38
            # this is an extrusion segment
 
39
            
 
40
            # get segment
 
41
            my $line = Slic3r::Line->new(
 
42
                Slic3r::Point->new_scale($self->X, $self->Y),
 
43
                Slic3r::Point->new_scale($args->{X}, $args->{Y}),
 
44
            );
 
45
            
 
46
            # get segment speed
 
47
            my $F = $args->{F} // $reader->F;
 
48
            
 
49
            # get extrusion per unscaled distance unit
 
50
            my $e = $info->{dist_E} / unscale($line->length);
 
51
            
 
52
            if ($self->_path && $F == $self->_cur_F && abs($e - $self->_cur_E) < epsilon) {
 
53
                # if speed and extrusion per unit are the same as the previous segments,
 
54
                # append this segment to path
 
55
                $self->_path->append($line->b);
 
56
            } elsif ($self->_path) {
 
57
                # segment can't be appended to previous path, so we flush the previous one
 
58
                # and start over
 
59
                $new_gcode .= $self->path_to_gcode;
 
60
                $self->_path(undef);
 
61
            }
 
62
            
 
63
            if (!$self->_path) {
 
64
                # if this is the first segment of a path, start it from scratch
 
65
                $self->_path(Slic3r::Polyline->new(@$line));
 
66
                $self->_cur_F($F);
 
67
                $self->_cur_E($e);
 
68
                $self->_cur_E0($self->E);
 
69
                $self->_comment($info->{comment});
 
70
            }
 
71
        } else {
 
72
            # if we have a path, we flush it and go on
 
73
            $new_gcode .= $self->path_to_gcode if $self->_path;
 
74
            $new_gcode .= $info->{raw} . "\n";
 
75
            $self->_path(undef);
 
76
        }
 
77
    });
 
78
    
 
79
    $new_gcode .= $self->path_to_gcode if $self->_path;
 
80
    return $new_gcode;
 
81
}
 
82
 
 
83
sub path_to_gcode {
 
84
    my ($self) = @_;
 
85
    
 
86
    my @chunks = $self->detect_arcs($self->_path);
 
87
    
 
88
    my $gcode = "";
 
89
    my $E = $self->_cur_E0;
 
90
    foreach my $chunk (@chunks) {
 
91
        if ($chunk->isa('Slic3r::Polyline')) {
 
92
            my @lines = @{$chunk->lines};
 
93
            
 
94
            $gcode .= sprintf "G1 F%s\n", $self->_cur_F;
 
95
            foreach my $line (@lines) {
 
96
                $E += $self->_cur_E * unscale($line->length);
 
97
                $gcode .= sprintf "G1 X%.3f Y%.3f %s%.5f",
 
98
                    (map unscale($_), @{$line->b}),
 
99
                    $self->_extrusion_axis, $E;
 
100
                $gcode .= sprintf " ; %s", $self->_comment if $self->_comment;
 
101
                $gcode .= "\n";
 
102
            }
 
103
        } elsif ($chunk->isa('Slic3r::GCode::ArcFitting::Arc')) {
 
104
            $gcode .= !$chunk->is_ccw ? "G2" : "G3";
 
105
            $gcode .= sprintf " X%.3f Y%.3f", map unscale($_), @{$chunk->end};  # destination point
 
106
            
 
107
            # XY distance of the center from the start position
 
108
            $gcode .= sprintf " I%.3f", unscale($chunk->center->[X] - $chunk->start->[X]);
 
109
            $gcode .= sprintf " J%.3f", unscale($chunk->center->[Y] - $chunk->start->[Y]);
 
110
            
 
111
            $E += $self->_cur_E * unscale($chunk->length);
 
112
            $gcode .= sprintf " %s%.5f", $self->_extrusion_axis, $E;
 
113
            
 
114
            $gcode .= sprintf " F%s\n", $self->_cur_F;
 
115
        }
 
116
    }
 
117
    return $gcode;
 
118
}
 
119
 
 
120
sub detect_arcs {
 
121
    my ($self, $path) = @_;
 
122
    
 
123
    my @chunks = ();
 
124
    my @arc_points = ();
 
125
    my $polyline = undef;
 
126
    my $arc_start = undef;
 
127
    
 
128
    my @points = @$path;
 
129
    for (my $i = 1; $i <= $#points; ++$i) {
 
130
        my $end = undef;
 
131
        
 
132
        # we need at least three points to check whether they form an arc
 
133
        if ($i < $#points) {
 
134
            my $len = $points[$i-1]->distance_to($points[$i]);
 
135
            my $rel_angle = PI - angle3points(@points[$i, $i-1, $i+1]);
 
136
            if (abs($rel_angle) <= $self->max_relative_angle) {
 
137
                for (my $j = $i+1; $j <= $#points; ++$j) {
 
138
                    # check whether @points[($i-1)..$j] form an arc
 
139
                    last if abs($points[$j-1]->distance_to($points[$j]) - $len) > $self->len_epsilon;
 
140
                    last if abs(PI - angle3points(@points[$j-1, $j-2, $j]) - $rel_angle) > $self->angle_epsilon;
 
141
                    
 
142
                    $end = $j;
 
143
                }
 
144
            }
 
145
        }
 
146
        
 
147
        if (defined $end && ($end - $i + 1) >= $self->min_segments) {
 
148
            my $arc = polyline_to_arc(Slic3r::Polyline->new(@points[($i-1)..$end]));
 
149
            
 
150
            if (1||$arc->angle >= $self->min_total_angle) {
 
151
                push @chunks, $arc;
 
152
                
 
153
                # continue scanning after arc points
 
154
                $i = $end;
 
155
                next;
 
156
            }
 
157
        }
 
158
        
 
159
        # if last chunk was a polyline, append to it
 
160
        if (@chunks && $chunks[-1]->isa('Slic3r::Polyline')) {
 
161
            $chunks[-1]->append($points[$i]);
 
162
        } else {
 
163
            push @chunks, Slic3r::Polyline->new(@points[($i-1)..$i]);
 
164
        }
 
165
    }
 
166
    
 
167
    return @chunks;
 
168
}
 
169
 
 
170
sub polyline_to_arc {
 
171
    my ($polyline) = @_;
 
172
    
 
173
    my @points = @$polyline;
 
174
    
 
175
    my $is_ccw = $points[2]->ccw(@points[0,1]) > 0;
 
176
        
 
177
    # to find the center, we intersect the perpendicular lines
 
178
    # passing by first and last vertex;
 
179
    # a better method would be to draw all the perpendicular lines
 
180
    # and find the centroid of the enclosed polygon, or to
 
181
    # intersect multiple lines and find the centroid of the convex hull
 
182
    # around the intersections
 
183
    my $arc_center;
 
184
    {
 
185
        my $first_ray = Slic3r::Line->new(@points[0,1]);
 
186
        $first_ray->rotate(PI/2 * ($is_ccw ? 1 : -1), $points[0]);
 
187
        
 
188
        my $last_ray = Slic3r::Line->new(@points[-2,-1]);
 
189
        $last_ray->rotate(PI/2 * ($is_ccw ? -1 : 1), $points[-1]);
 
190
        
 
191
        # require non-parallel rays in order to compute an accurate center
 
192
        return if abs($first_ray->atan2_ - $last_ray->atan2_) < deg2rad(30);
 
193
        
 
194
        $arc_center = $first_ray->intersection($last_ray, 0) or return;
 
195
    }
 
196
    
 
197
    # angle measured in ccw orientation
 
198
    my $abs_angle = Slic3r::Geometry::angle3points($arc_center, @points[0,-1]);
 
199
    
 
200
    my $rel_angle = $is_ccw
 
201
        ? $abs_angle
 
202
        : (2*PI - $abs_angle);
 
203
    
 
204
    my $arc = Slic3r::GCode::ArcFitting::Arc->new(
 
205
        start   => $points[0]->clone,
 
206
        end     => $points[-1]->clone,
 
207
        center  => $arc_center,
 
208
        is_ccw  => $is_ccw || 0,
 
209
        angle   => $rel_angle,
 
210
    );
 
211
    
 
212
    if (0) {
 
213
        printf "points = %d, path length = %f, arc angle = %f, arc length = %f\n",
 
214
            scalar(@points),
 
215
            unscale(Slic3r::Polyline->new(@points)->length),
 
216
            Slic3r::Geometry::rad2deg($rel_angle),
 
217
            unscale($arc->length);
 
218
    }
 
219
    
 
220
    return $arc;
 
221
}
 
222
 
 
223
package Slic3r::GCode::ArcFitting::Arc;
 
224
use Moo;
 
225
 
 
226
has 'start'  => (is => 'ro', required => 1);
 
227
has 'end'    => (is => 'ro', required => 1);
 
228
has 'center' => (is => 'ro', required => 1);
 
229
has 'is_ccw' => (is => 'ro', required => 1);
 
230
has 'angle'  => (is => 'ro', required => 1);
 
231
 
 
232
sub radius {
 
233
    my ($self) = @_;
 
234
    return $self->start->distance_to($self->center);
 
235
}
 
236
 
 
237
sub length {
 
238
    my ($self) = @_;
 
239
    return $self->radius * $self->angle;
 
240
}
 
241
 
 
242
1;