New --cut-grid command for cutting a mesh in multiple tiles according to a XY grid

This commit is contained in:
Alessandro Ranellucci 2016-12-17 20:30:40 +01:00
parent a778cd9820
commit 27e456039c
6 changed files with 109 additions and 8 deletions

View File

@ -12,9 +12,9 @@ BEGIN {
use File::Basename qw(basename); use File::Basename qw(basename);
use Getopt::Long qw(:config no_auto_abbrev); use Getopt::Long qw(:config no_auto_abbrev);
use List::Util qw(first); use List::Util qw(first);
use POSIX qw(setlocale LC_NUMERIC); use POSIX qw(setlocale LC_NUMERIC ceil);
use Slic3r; use Slic3r;
use Slic3r::Geometry qw(deg2rad); use Slic3r::Geometry qw(epsilon X Y Z deg2rad);
use Time::HiRes qw(gettimeofday tv_interval); use Time::HiRes qw(gettimeofday tv_interval);
$|++; $|++;
binmode STDOUT, ':utf8'; binmode STDOUT, ':utf8';
@ -42,6 +42,7 @@ my %cli_options = ();
'merge|m' => \$opt{merge}, 'merge|m' => \$opt{merge},
'repair' => \$opt{repair}, 'repair' => \$opt{repair},
'cut=f' => \$opt{cut}, 'cut=f' => \$opt{cut},
'cut-grid=s' => \$opt{cut_grid},
'split' => \$opt{split}, 'split' => \$opt{split},
'info' => \$opt{info}, 'info' => \$opt{info},
@ -150,7 +151,7 @@ if (@ARGV) { # slicing from command line
$mesh->translate(0, 0, -$mesh->bounding_box->z_min); $mesh->translate(0, 0, -$mesh->bounding_box->z_min);
my $upper = Slic3r::TriangleMesh->new; my $upper = Slic3r::TriangleMesh->new;
my $lower = Slic3r::TriangleMesh->new; my $lower = Slic3r::TriangleMesh->new;
$mesh->cut($opt{cut}, $upper, $lower); $mesh->cut(Z, $opt{cut}, $upper, $lower);
$upper->repair; $upper->repair;
$lower->repair; $lower->repair;
$upper->write_ascii("${file}_upper.stl") $upper->write_ascii("${file}_upper.stl")
@ -161,6 +162,49 @@ if (@ARGV) { # slicing from command line
exit; exit;
} }
if ($opt{cut_grid}) {
my ($grid_x, $grid_y) = split /[,x]/, $opt{cut_grid}, 2;
foreach my $file (@ARGV) {
$file = Slic3r::decode_path($file);
my $model = Slic3r::Model->read_from_file($file);
$model->add_default_instances;
my $mesh = $model->mesh;
my $bb = $mesh->bounding_box;
$mesh->translate(0, 0, -$bb->z_min);
my $x_parts = ceil(($bb->size->x - epsilon)/$grid_x);
my $y_parts = ceil(($bb->size->y - epsilon)/$grid_y); #--
for my $i (1..$x_parts) {
my $this = Slic3r::TriangleMesh->new;
if ($i == $x_parts) {
$this = $mesh;
} else {
my $next = Slic3r::TriangleMesh->new;
$mesh->cut(X, $bb->x_min + ($grid_x * $i), $next, $this);
$this->repair;
$next->repair;
$mesh = $next;
}
for my $j (1..$y_parts) {
my $tile = Slic3r::TriangleMesh->new;
if ($j == $y_parts) {
$tile = $this;
} else {
my $next = Slic3r::TriangleMesh->new;
$this->cut(Y, $bb->y_min + ($grid_y * $j), $next, $tile);
$tile->repair;
$next->repair;
$this = $next;
}
$tile->write_ascii("${file}_${i}_${j}.stl");
}
}
}
exit;
}
if ($opt{split}) { if ($opt{split}) {
foreach my $file (@ARGV) { foreach my $file (@ARGV) {
$file = Slic3r::decode_path($file); $file = Slic3r::decode_path($file);

View File

@ -9,6 +9,7 @@
#include <string> #include <string>
#include <cstring> #include <cstring>
#include <iostream> #include <iostream>
#include <math.h>
#include "boost/filesystem.hpp" #include "boost/filesystem.hpp"
using namespace Slic3r; using namespace Slic3r;
@ -152,6 +153,46 @@ main(const int argc, const char **argv)
IO::STL::write(m, lower.input_file + "_lower.stl"); IO::STL::write(m, lower.input_file + "_lower.stl");
} }
} }
} else if (cli_config.cut_grid.value.x > 0 && cli_config.cut_grid.value.y > 0) {
TriangleMesh mesh = model->mesh();
mesh.repair();
const BoundingBoxf3 bb = mesh.bounding_box();
mesh.translate(0, 0, -bb.min.z);
const Sizef3 size = bb.size();
const size_t x_parts = ceil((size.x - EPSILON)/cli_config.cut_grid.value.x);
const size_t y_parts = ceil((size.y - EPSILON)/cli_config.cut_grid.value.y);
for (size_t i = 1; i <= x_parts; ++i) {
TriangleMesh curr;
if (i == x_parts) {
curr = mesh;
} else {
TriangleMesh next;
TriangleMeshSlicer<X>(&mesh).cut(bb.min.x + (cli_config.cut_grid.value.x * i), &next, &curr);
curr.repair();
next.repair();
mesh = next;
}
for (size_t j = 1; j <= y_parts; ++j) {
TriangleMesh tile;
if (j == y_parts) {
tile = curr;
} else {
TriangleMesh next;
TriangleMeshSlicer<Y>(&curr).cut(bb.min.y + (cli_config.cut_grid.value.y * j), &next, &tile);
tile.repair();
next.repair();
curr = next;
}
std::ostringstream ss;
ss << model->objects.front()->input_file << "_" << i << "_" << j << ".stl";
IO::STL::write(tile, ss.str());
}
}
} else { } else {
std::cerr << "error: command not supported" << std::endl; std::cerr << "error: command not supported" << std::endl;
return 1; return 1;

View File

@ -1404,6 +1404,12 @@ CLIConfigDef::CLIConfigDef()
def->cli = "cut"; def->cli = "cut";
def->default_value = new ConfigOptionFloat(0); def->default_value = new ConfigOptionFloat(0);
def = this->add("cut_grid", coFloat);
def->label = "Cut";
def->tooltip = "Cut model in the XY plane into tiles of the specified max size.";
def->cli = "cut-grid";
def->default_value = new ConfigOptionPoint();
def = this->add("cut_x", coFloat); def = this->add("cut_x", coFloat);
def->label = "Cut"; def->label = "Cut";
def->tooltip = "Cut model at the given X."; def->tooltip = "Cut model at the given X.";

View File

@ -590,6 +590,7 @@ class CLIConfig
{ {
public: public:
ConfigOptionFloat cut; ConfigOptionFloat cut;
ConfigOptionPoint cut_grid;
ConfigOptionFloat cut_x; ConfigOptionFloat cut_x;
ConfigOptionFloat cut_y; ConfigOptionFloat cut_y;
ConfigOptionBool export_obj; ConfigOptionBool export_obj;
@ -610,6 +611,7 @@ class CLIConfig
virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) { virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) {
OPT_PTR(cut); OPT_PTR(cut);
OPT_PTR(cut_grid);
OPT_PTR(cut_x); OPT_PTR(cut_x);
OPT_PTR(cut_y); OPT_PTR(cut_y);
OPT_PTR(export_obj); OPT_PTR(export_obj);

View File

@ -6,6 +6,8 @@ use warnings;
use Slic3r::XS; use Slic3r::XS;
use Test::More tests => 49; use Test::More tests => 49;
use constant Z => 2;
is Slic3r::TriangleMesh::hello_world(), 'Hello world!', is Slic3r::TriangleMesh::hello_world(), 'Hello world!',
'hello world'; 'hello world';
@ -117,7 +119,7 @@ my $cube = {
{ {
my $upper = Slic3r::TriangleMesh->new; my $upper = Slic3r::TriangleMesh->new;
my $lower = Slic3r::TriangleMesh->new; my $lower = Slic3r::TriangleMesh->new;
$m->cut(0, $upper, $lower); $m->cut(Z, 0, $upper, $lower);
$upper->repair; $lower->repair; $upper->repair; $lower->repair;
is $upper->facets_count, 12, 'upper mesh has all facets except those belonging to the slicing plane'; is $upper->facets_count, 12, 'upper mesh has all facets except those belonging to the slicing plane';
is $lower->facets_count, 0, 'lower mesh has no facets'; is $lower->facets_count, 0, 'lower mesh has no facets';
@ -125,7 +127,7 @@ my $cube = {
{ {
my $upper = Slic3r::TriangleMesh->new; my $upper = Slic3r::TriangleMesh->new;
my $lower = Slic3r::TriangleMesh->new; my $lower = Slic3r::TriangleMesh->new;
$m->cut(10, $upper, $lower); $m->cut(Z, 10, $upper, $lower);
#$upper->repair; $lower->repair; #$upper->repair; $lower->repair;
# we expect: # we expect:
# 2 facets on external horizontal surfaces # 2 facets on external horizontal surfaces

View File

@ -221,13 +221,19 @@ TriangleMesh::slice_at(axis, z)
RETVAL RETVAL
void void
TriangleMesh::cut(z, upper, lower) TriangleMesh::cut(axis, z, upper, lower)
Axis axis
float z; float z;
TriangleMesh* upper; TriangleMesh* upper;
TriangleMesh* lower; TriangleMesh* lower;
CODE: CODE:
TriangleMeshSlicer<Z> mslicer(THIS); if (axis == X) {
mslicer.cut(z, upper, lower); TriangleMeshSlicer<X>(THIS).cut(z, upper, lower);
} else if (axis == Y) {
TriangleMeshSlicer<Y>(THIS).cut(z, upper, lower);
} else {
TriangleMeshSlicer<Z>(THIS).cut(z, upper, lower);
}
std::vector<double> std::vector<double>
TriangleMesh::bb3() TriangleMesh::bb3()