gcode_sectioncut.pl 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. #!/usr/bin/perl
  2. # This script generates section cuts from a given G-Code file
  3. use strict;
  4. use warnings;
  5. BEGIN {
  6. use FindBin;
  7. use lib "$FindBin::Bin/../lib";
  8. use local::lib "$FindBin::Bin/../local-lib";
  9. }
  10. use Getopt::Long qw(:config no_auto_abbrev);
  11. use IO::All;
  12. use List::Util qw(max);
  13. use Slic3r;
  14. use Slic3r::Geometry qw(X Y);
  15. use Slic3r::Geometry::Clipper qw(JT_SQUARE);
  16. use Slic3r::Test;
  17. use SVG;
  18. my %opt = (
  19. layer_height => 0.2,
  20. extrusion_width => 0.5,
  21. scale => 30,
  22. );
  23. {
  24. my %options = (
  25. 'help' => sub { usage() },
  26. 'layer-height|h=f' => \$opt{layer_height},
  27. 'extrusion-width|w=f' => \$opt{extrusion_width},
  28. 'scale|s=i' => \$opt{scale},
  29. );
  30. GetOptions(%options) or usage(1);
  31. $ARGV[0] or usage(1);
  32. }
  33. {
  34. my $input_file = $ARGV[0];
  35. my $output_file = $input_file;
  36. $output_file =~ s/\.(?:gcode|gco|ngc|g)$/.svg/;
  37. # read paths
  38. my %paths = (); # z => [ path, path ... ]
  39. Slic3r::GCode::Reader->new->parse(io($input_file)->all, sub {
  40. my ($self, $cmd, $args, $info) = @_;
  41. if ($cmd eq 'G1' && $info->{extruding}) {
  42. $paths{ $self->Z } ||= [];
  43. push @{ $paths{ $self->Z } }, Slic3r::Line->new(
  44. [ $self->X, $self->Y ],
  45. [ $info->{new_X}, $info->{new_Y} ],
  46. );
  47. }
  48. });
  49. # calculate print extents
  50. my $bounding_box = Slic3r::Geometry::BoundingBox->new_from_points([ map @$_, map @$_, values %paths ]);
  51. # calculate section line
  52. my $section_y = $bounding_box->center->[Y];
  53. my $section_line = [
  54. [ $bounding_box->x_min, $section_y ],
  55. [ $bounding_box->x_max, $section_y ],
  56. ];
  57. # initialize output
  58. my $max_z = max(keys %paths);
  59. my $svg = SVG->new(
  60. width => $opt{scale} * $bounding_box->size->[X],
  61. height => $opt{scale} * $max_z,
  62. );
  63. # put everything into a group
  64. my $g = $svg->group(style => {
  65. 'stroke-width' => 1,
  66. 'stroke' => '#444444',
  67. 'fill' => 'grey',
  68. });
  69. # draw paths
  70. foreach my $z (sort keys %paths) {
  71. foreach my $line (@{ $paths{$z} }) {
  72. my @intersections = @{intersection_pl(
  73. [ $section_line ],
  74. [ _grow($line, $opt{extrusion_width}/2) ],
  75. )};
  76. $g->rectangle(
  77. 'x' => $opt{scale} * ($_->[0][X] - $bounding_box->x_min),
  78. 'y' => $opt{scale} * ($max_z - $z),
  79. 'width' => $opt{scale} * abs($_->[1][X] - $_->[0][X]),
  80. 'height' => $opt{scale} * $opt{layer_height},
  81. 'rx' => $opt{scale} * $opt{layer_height} * 0.35,
  82. 'ry' => $opt{scale} * $opt{layer_height} * 0.35,
  83. ) for @intersections;
  84. }
  85. }
  86. # write output
  87. Slic3r::open(\my $fh, '>', $output_file);
  88. print $fh $svg->xmlify;
  89. close $fh;
  90. printf "Section cut SVG written to %s\n", $output_file;
  91. }
  92. # replace built-in Line->grow method which relies on int_offset()
  93. sub _grow {
  94. my ($line, $distance) = @_;
  95. my $polygon = [ @$line, CORE::reverse @$line[1..($#$line-1)] ];
  96. return @{Math::Clipper::offset([$polygon], $distance, 100000, JT_SQUARE, 2)};
  97. }
  98. sub usage {
  99. my ($exit_code) = @_;
  100. print <<"EOF";
  101. Usage: gcode_sectioncut.pl [ OPTIONS ] file.gcode
  102. --help Output this usage screen and exit
  103. --layer-height, -h Use the specified layer height
  104. --extrusion-width, -w Use the specified extrusion width
  105. --scale Factor for converting G-code units to SVG units
  106. EOF
  107. exit ($exit_code || 0);
  108. }
  109. __END__