This file is indexed.

/usr/share/perl5/Slic3r/GCode/ArcFitting.pm is in slic3r 1.2.9+dfsg-2build1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
package Slic3r::GCode::ArcFitting;
use Moo;

use Slic3r::Geometry qw(X Y PI scale unscale epsilon scaled_epsilon deg2rad angle3points);

extends 'Slic3r::GCode::Reader';
has 'config'                    => (is => 'ro', required => 0);
has 'min_segments'              => (is => 'rw', default => sub { 2 });
has 'min_total_angle'           => (is => 'rw', default => sub { deg2rad(30) });
has 'max_relative_angle'        => (is => 'rw', default => sub { deg2rad(15) });
has 'len_epsilon'               => (is => 'rw', default => sub { scale 0.2 });
has 'angle_epsilon'             => (is => 'rw', default => sub { abs(deg2rad(10)) });
has '_extrusion_axis'           => (is => 'lazy');
has '_path'                     => (is => 'rw');
has '_cur_F'                    => (is => 'rw');
has '_cur_E'                    => (is => 'rw');
has '_cur_E0'                   => (is => 'rw');
has '_comment'                  => (is => 'rw');

sub _build__extrusion_axis {
    my ($self) = @_;
    return $self->config ? $self->config->get_extrusion_axis : 'E';
}

sub process {
    my $self = shift;
    my ($gcode) = @_;
    
    die "Arc fitting is not available (incomplete feature)\n";
    die "Arc fitting doesn't support extrusion axis not being E\n" if $self->_extrusion_axis ne 'E';
    
    my $new_gcode = "";
    
    $self->parse($gcode, sub {
        my ($reader, $cmd, $args, $info) = @_;
        
        if ($info->{extruding} && $info->{dist_XY} > 0) {
            # this is an extrusion segment
            
            # get segment
            my $line = Slic3r::Line->new(
                Slic3r::Point->new_scale($self->X, $self->Y),
                Slic3r::Point->new_scale($args->{X}, $args->{Y}),
            );
            
            # get segment speed
            my $F = $args->{F} // $reader->F;
            
            # get extrusion per unscaled distance unit
            my $e = $info->{dist_E} / unscale($line->length);
            
            if ($self->_path && $F == $self->_cur_F && abs($e - $self->_cur_E) < epsilon) {
                # if speed and extrusion per unit are the same as the previous segments,
                # append this segment to path
                $self->_path->append($line->b);
            } elsif ($self->_path) {
                # segment can't be appended to previous path, so we flush the previous one
                # and start over
                $new_gcode .= $self->path_to_gcode;
                $self->_path(undef);
            }
            
            if (!$self->_path) {
                # if this is the first segment of a path, start it from scratch
                $self->_path(Slic3r::Polyline->new(@$line));
                $self->_cur_F($F);
                $self->_cur_E($e);
                $self->_cur_E0($self->E);
                $self->_comment($info->{comment});
            }
        } else {
            # if we have a path, we flush it and go on
            $new_gcode .= $self->path_to_gcode if $self->_path;
            $new_gcode .= $info->{raw} . "\n";
            $self->_path(undef);
        }
    });
    
    $new_gcode .= $self->path_to_gcode if $self->_path;
    return $new_gcode;
}

sub path_to_gcode {
    my ($self) = @_;
    
    my @chunks = $self->detect_arcs($self->_path);
    
    my $gcode = "";
    my $E = $self->_cur_E0;
    foreach my $chunk (@chunks) {
        if ($chunk->isa('Slic3r::Polyline')) {
            my @lines = @{$chunk->lines};
            
            $gcode .= sprintf "G1 F%s\n", $self->_cur_F;
            foreach my $line (@lines) {
                $E += $self->_cur_E * unscale($line->length);
                $gcode .= sprintf "G1 X%.3f Y%.3f %s%.5f",
                    (map unscale($_), @{$line->b}),
                    $self->_extrusion_axis, $E;
                $gcode .= sprintf " ; %s", $self->_comment if $self->_comment;
                $gcode .= "\n";
            }
        } elsif ($chunk->isa('Slic3r::GCode::ArcFitting::Arc')) {
            $gcode .= !$chunk->is_ccw ? "G2" : "G3";
            $gcode .= sprintf " X%.3f Y%.3f", map unscale($_), @{$chunk->end};  # destination point
            
            # XY distance of the center from the start position
            $gcode .= sprintf " I%.3f", unscale($chunk->center->[X] - $chunk->start->[X]);
            $gcode .= sprintf " J%.3f", unscale($chunk->center->[Y] - $chunk->start->[Y]);
            
            $E += $self->_cur_E * unscale($chunk->length);
            $gcode .= sprintf " %s%.5f", $self->_extrusion_axis, $E;
            
            $gcode .= sprintf " F%s\n", $self->_cur_F;
        }
    }
    return $gcode;
}

sub detect_arcs {
    my ($self, $path) = @_;
    
    my @chunks = ();
    my @arc_points = ();
    my $polyline = undef;
    my $arc_start = undef;
    
    my @points = @$path;
    for (my $i = 1; $i <= $#points; ++$i) {
        my $end = undef;
        
        # we need at least three points to check whether they form an arc
        if ($i < $#points) {
            my $len = $points[$i-1]->distance_to($points[$i]);
            my $rel_angle = PI - angle3points(@points[$i, $i-1, $i+1]);
            if (abs($rel_angle) <= $self->max_relative_angle) {
                for (my $j = $i+1; $j <= $#points; ++$j) {
                    # check whether @points[($i-1)..$j] form an arc
                    last if abs($points[$j-1]->distance_to($points[$j]) - $len) > $self->len_epsilon;
                    last if abs(PI - angle3points(@points[$j-1, $j-2, $j]) - $rel_angle) > $self->angle_epsilon;
                    
                    $end = $j;
                }
            }
        }
        
        if (defined $end && ($end - $i + 1) >= $self->min_segments) {
            my $arc = polyline_to_arc(Slic3r::Polyline->new(@points[($i-1)..$end]));
            
            if (1||$arc->angle >= $self->min_total_angle) {
                push @chunks, $arc;
                
                # continue scanning after arc points
                $i = $end;
                next;
            }
        }
        
        # if last chunk was a polyline, append to it
        if (@chunks && $chunks[-1]->isa('Slic3r::Polyline')) {
            $chunks[-1]->append($points[$i]);
        } else {
            push @chunks, Slic3r::Polyline->new(@points[($i-1)..$i]);
        }
    }
    
    return @chunks;
}

sub polyline_to_arc {
    my ($polyline) = @_;
    
    my @points = @$polyline;
    
    my $is_ccw = $points[2]->ccw(@points[0,1]) > 0;
        
    # to find the center, we intersect the perpendicular lines
    # passing by first and last vertex;
    # a better method would be to draw all the perpendicular lines
    # and find the centroid of the enclosed polygon, or to
    # intersect multiple lines and find the centroid of the convex hull
    # around the intersections
    my $arc_center;
    {
        my $first_ray = Slic3r::Line->new(@points[0,1]);
        $first_ray->rotate(PI/2 * ($is_ccw ? 1 : -1), $points[0]);
        
        my $last_ray = Slic3r::Line->new(@points[-2,-1]);
        $last_ray->rotate(PI/2 * ($is_ccw ? -1 : 1), $points[-1]);
        
        # require non-parallel rays in order to compute an accurate center
        return if abs($first_ray->atan2_ - $last_ray->atan2_) < deg2rad(30);
        
        $arc_center = $first_ray->intersection($last_ray, 0) or return;
    }
    
    # angle measured in ccw orientation
    my $abs_angle = Slic3r::Geometry::angle3points($arc_center, @points[0,-1]);
    
    my $rel_angle = $is_ccw
        ? $abs_angle
        : (2*PI - $abs_angle);
    
    my $arc = Slic3r::GCode::ArcFitting::Arc->new(
        start   => $points[0]->clone,
        end     => $points[-1]->clone,
        center  => $arc_center,
        is_ccw  => $is_ccw || 0,
        angle   => $rel_angle,
    );
    
    if (0) {
        printf "points = %d, path length = %f, arc angle = %f, arc length = %f\n",
            scalar(@points),
            unscale(Slic3r::Polyline->new(@points)->length),
            Slic3r::Geometry::rad2deg($rel_angle),
            unscale($arc->length);
    }
    
    return $arc;
}

package Slic3r::GCode::ArcFitting::Arc;
use Moo;

has 'start'  => (is => 'ro', required => 1);
has 'end'    => (is => 'ro', required => 1);
has 'center' => (is => 'ro', required => 1);
has 'is_ccw' => (is => 'ro', required => 1);
has 'angle'  => (is => 'ro', required => 1);

sub radius {
    my ($self) = @_;
    return $self->start->distance_to($self->center);
}

sub length {
    my ($self) = @_;
    return $self->radius * $self->angle;
}

1;