New automatic built-in STL repair

This commit is contained in:
Alessandro Ranellucci 2013-06-23 21:11:46 +02:00
parent f62896a77a
commit 2a2633da0a
7 changed files with 131 additions and 9 deletions

View File

@ -90,6 +90,8 @@ The author of the Silk icon set is Mark James.
-o, --output <file> File to output gcode to (by default, the file will be saved -o, --output <file> File to output gcode to (by default, the file will be saved
into the same directory as the input file using the into the same directory as the input file using the
--output-filename-format to generate the filename) --output-filename-format to generate the filename)
--repair Automatically repair given STL files and saves them as _fixed.obj
GUI options: GUI options:
--no-plater Disable the plater tab --no-plater Disable the plater tab
--gui-mode Overrides the configured mode (simple/expert) --gui-mode Overrides the configured mode (simple/expert)

View File

@ -26,6 +26,7 @@ use constant MI_QUICK_SLICE => &Wx::NewId;
use constant MI_REPEAT_QUICK => &Wx::NewId; use constant MI_REPEAT_QUICK => &Wx::NewId;
use constant MI_QUICK_SAVE_AS => &Wx::NewId; use constant MI_QUICK_SAVE_AS => &Wx::NewId;
use constant MI_SLICE_SVG => &Wx::NewId; use constant MI_SLICE_SVG => &Wx::NewId;
use constant MI_REPAIR_STL => &Wx::NewId;
use constant MI_COMBINE_STLS => &Wx::NewId; use constant MI_COMBINE_STLS => &Wx::NewId;
use constant MI_PLATER_EXPORT_GCODE => &Wx::NewId; use constant MI_PLATER_EXPORT_GCODE => &Wx::NewId;
@ -112,6 +113,7 @@ sub OnInit {
$fileMenu->AppendSeparator(); $fileMenu->AppendSeparator();
$fileMenu->Append(MI_SLICE_SVG, "Slice to SV&G…\tCtrl+G", 'Slice file to SVG'); $fileMenu->Append(MI_SLICE_SVG, "Slice to SV&G…\tCtrl+G", 'Slice file to SVG');
$fileMenu->AppendSeparator(); $fileMenu->AppendSeparator();
$fileMenu->Append(MI_REPAIR_STL, "Repair STL file…", 'Automatically repair an STL file');
$fileMenu->Append(MI_COMBINE_STLS, "Combine multi-material STL files…", 'Combine multiple STL files into a single multi-material AMF file'); $fileMenu->Append(MI_COMBINE_STLS, "Combine multi-material STL files…", 'Combine multiple STL files into a single multi-material AMF file');
$fileMenu->AppendSeparator(); $fileMenu->AppendSeparator();
$fileMenu->Append(wxID_PREFERENCES, "Preferences…", 'Application preferences'); $fileMenu->Append(wxID_PREFERENCES, "Preferences…", 'Application preferences');
@ -125,6 +127,7 @@ sub OnInit {
EVT_MENU($frame, MI_QUICK_SAVE_AS, sub { $self->{skeinpanel}->quick_slice(save_as => 1); EVT_MENU($frame, MI_QUICK_SAVE_AS, sub { $self->{skeinpanel}->quick_slice(save_as => 1);
$repeat->Enable(defined $Slic3r::GUI::SkeinPanel::last_input_file) }); $repeat->Enable(defined $Slic3r::GUI::SkeinPanel::last_input_file) });
EVT_MENU($frame, MI_SLICE_SVG, sub { $self->{skeinpanel}->quick_slice(save_as => 1, export_svg => 1) }); EVT_MENU($frame, MI_SLICE_SVG, sub { $self->{skeinpanel}->quick_slice(save_as => 1, export_svg => 1) });
EVT_MENU($frame, MI_REPAIR_STL, sub { $self->{skeinpanel}->repair_stl });
EVT_MENU($frame, MI_COMBINE_STLS, sub { $self->{skeinpanel}->combine_stls }); EVT_MENU($frame, MI_COMBINE_STLS, sub { $self->{skeinpanel}->combine_stls });
EVT_MENU($frame, wxID_PREFERENCES, sub { Slic3r::GUI::Preferences->new($frame)->ShowModal }); EVT_MENU($frame, wxID_PREFERENCES, sub { Slic3r::GUI::Preferences->new($frame)->ShowModal });
EVT_MENU($frame, wxID_EXIT, sub {$_[0]->Close(0)}); EVT_MENU($frame, wxID_EXIT, sub {$_[0]->Close(0)});

View File

@ -192,6 +192,38 @@ sub quick_slice {
Slic3r::GUI::catch_error($self, sub { $process_dialog->Destroy if $process_dialog }); Slic3r::GUI::catch_error($self, sub { $process_dialog->Destroy if $process_dialog });
} }
sub repair_stl {
my $self = shift;
my $input_file;
{
my $dir = $Slic3r::GUI::Settings->{recent}{skein_directory} || $Slic3r::GUI::Settings->{recent}{config_directory} || '';
my $dialog = Wx::FileDialog->new($self, 'Select the STL file to repair:', $dir, "", FILE_WILDCARDS->{stl}, wxFD_OPEN | wxFD_FILE_MUST_EXIST);
if ($dialog->ShowModal != wxID_OK) {
$dialog->Destroy;
return;
}
$input_file = $dialog->GetPaths;
$dialog->Destroy;
}
my $output_file = $input_file;
{
$output_file =~ s/\.stl$/_fixed.obj/i;
my $dlg = Wx::FileDialog->new($self, "Save OBJ file (less prone to coordinate errors than STL) as:", dirname($output_file),
basename($output_file), &Slic3r::GUI::SkeinPanel::FILE_WILDCARDS->{obj}, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
if ($dlg->ShowModal != wxID_OK) {
$dlg->Destroy;
return undef;
}
$output_file = $dlg->GetPath;
$dlg->Destroy;
}
Slic3r::TriangleMesh::XS::stl_repair($input_file, $output_file);
Slic3r::GUI::show_info($self, "Your file was repaired.", "Repair");
}
sub init_print { sub init_print {
my $self = shift; my $self = shift;

View File

@ -33,6 +33,7 @@ my %cli_options = ();
'datadir=s' => \$opt{datadir}, 'datadir=s' => \$opt{datadir},
'export-svg' => \$opt{export_svg}, 'export-svg' => \$opt{export_svg},
'merge|m' => \$opt{merge}, 'merge|m' => \$opt{merge},
'repair' => \$opt{repair},
); );
foreach my $opt_key (keys %{$Slic3r::Config::Options}) { foreach my $opt_key (keys %{$Slic3r::Config::Options}) {
my $cli = $Slic3r::Config::Options->{$opt_key}->{cli} or next; my $cli = $Slic3r::Config::Options->{$opt_key}->{cli} or next;
@ -92,6 +93,18 @@ die $@ if $@ && $opt{gui};
if (@ARGV) { # slicing from command line if (@ARGV) { # slicing from command line
$config->validate; $config->validate;
if ($opt{repair}) {
foreach my $file (@ARGV) {
die "Repair is currently supported only on STL files\n"
if $file !~ /\.stl$/i;
my $output_file = $file;
$output_file =~ s/\.(stl)$/_fixed.obj/i;
Slic3r::TriangleMesh::XS::stl_repair($file, $output_file);
}
exit;
}
while (my $input_file = shift @ARGV) { while (my $input_file = shift @ARGV) {
my $model; my $model;
if ($opt{merge}) { if ($opt{merge}) {
@ -150,6 +163,7 @@ Usage: slic3r.pl [ OPTIONS ] file.stl
-o, --output <file> File to output gcode to (by default, the file will be saved -o, --output <file> File to output gcode to (by default, the file will be saved
into the same directory as the input file using the into the same directory as the input file using the
--output-filename-format to generate the filename) --output-filename-format to generate the filename)
--repair Automatically repair given STL files and saves them as _fixed.obj
$j $j
GUI options: GUI options:
--no-plater Disable the plater tab --no-plater Disable the plater tab

View File

@ -241,3 +241,26 @@ stl_write_vrml(stl_file *stl, char *file)
fprintf(fp, "}\n"); fprintf(fp, "}\n");
fclose(fp); fclose(fp);
} }
void stl_write_obj (stl_file *stl, char *file) {
int i;
/* Open the file */
FILE* fp = fopen(file, "w");
if (fp == NULL) {
char* error_msg = (char*)malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */
sprintf(error_msg, "stl_write_ascii: Couldn't open %s for writing", file);
perror(error_msg);
free(error_msg);
exit(1);
}
for (i = 0; i < stl->stats.shared_vertices; i++) {
fprintf(fp, "v %f %f %f\n", stl->v_shared[i].x, stl->v_shared[i].y, stl->v_shared[i].z);
}
for (i = 0; i < stl->stats.number_of_facets; i++) {
fprintf(fp, "f %d %d %d\n", stl->v_indices[i].vertex[0]+1, stl->v_indices[i].vertex[1]+1, stl->v_indices[i].vertex[2]+1);
}
fclose(fp);
}

View File

@ -164,6 +164,7 @@ extern void stl_mirror_yz(stl_file *stl);
extern void stl_mirror_xz(stl_file *stl); extern void stl_mirror_xz(stl_file *stl);
extern void stl_open_merge(stl_file *stl, char *file); extern void stl_open_merge(stl_file *stl, char *file);
extern void stl_generate_shared_vertices(stl_file *stl); extern void stl_generate_shared_vertices(stl_file *stl);
extern void stl_write_obj(stl_file *stl, char *file);
extern void stl_write_off(stl_file *stl, char *file); extern void stl_write_off(stl_file *stl, char *file);
extern void stl_write_dxf(stl_file *stl, char *file, char *label); extern void stl_write_dxf(stl_file *stl, char *file, char *label);
extern void stl_write_vrml(stl_file *stl, char *file); extern void stl_write_vrml(stl_file *stl, char *file);

View File

@ -13,25 +13,72 @@ hello_world()
OUTPUT: OUTPUT:
RETVAL RETVAL
float void
stl_volume(filename) stl_repair(input_file, output_file)
char* filename; char* input_file;
char* output_file;
CODE: CODE:
stl_file stl_in; int i;
stl_open(&stl_in, filename);
stl_file stl_in;
stl_open(&stl_in, input_file);
// checking exact
stl_check_facets_exact(&stl_in); stl_check_facets_exact(&stl_in);
stl_in.stats.facets_w_1_bad_edge = (stl_in.stats.connected_facets_2_edge - stl_in.stats.connected_facets_3_edge); stl_in.stats.facets_w_1_bad_edge = (stl_in.stats.connected_facets_2_edge - stl_in.stats.connected_facets_3_edge);
stl_in.stats.facets_w_2_bad_edge = (stl_in.stats.connected_facets_1_edge - stl_in.stats.connected_facets_2_edge); stl_in.stats.facets_w_2_bad_edge = (stl_in.stats.connected_facets_1_edge - stl_in.stats.connected_facets_2_edge);
stl_in.stats.facets_w_3_bad_edge = (stl_in.stats.number_of_facets - stl_in.stats.connected_facets_1_edge); stl_in.stats.facets_w_3_bad_edge = (stl_in.stats.number_of_facets - stl_in.stats.connected_facets_1_edge);
// checking nearby
int last_edges_fixed = 0;
float tolerance = stl_in.stats.shortest_edge;
float increment = stl_in.stats.bounding_diameter / 10000.0;
int iterations = 2;
if (stl_in.stats.connected_facets_3_edge < stl_in.stats.number_of_facets) {
for (i = 0; i < iterations; i++) {
if (stl_in.stats.connected_facets_3_edge < stl_in.stats.number_of_facets) {
printf("Checking nearby. Tolerance= %f Iteration=%d of %d...", tolerance, i + 1, iterations);
stl_check_facets_nearby(&stl_in, tolerance);
printf(" Fixed %d edges.\n", stl_in.stats.edges_fixed - last_edges_fixed);
last_edges_fixed = stl_in.stats.edges_fixed;
tolerance += increment;
} else {
printf("All facets connected. No further nearby check necessary.\n");
break;
}
}
} else {
printf("All facets connected. No nearby check necessary.\n");
}
// remove_unconnected
if (stl_in.stats.connected_facets_3_edge < stl_in.stats.number_of_facets) {
printf("Removing unconnected facets...\n");
stl_remove_unconnected_facets(&stl_in);
} else
printf("No unconnected need to be removed.\n");
// fill_holes
if (stl_in.stats.connected_facets_3_edge < stl_in.stats.number_of_facets) {
printf("Filling holes...\n");
stl_fill_holes(&stl_in);
} else
printf("No holes need to be filled.\n");
// normal_directions
printf("Checking normal directions...\n");
stl_fix_normal_directions(&stl_in); stl_fix_normal_directions(&stl_in);
// normal_values
printf("Checking normal values...\n");
stl_fix_normal_values(&stl_in); stl_fix_normal_values(&stl_in);
stl_calculate_volume(&stl_in); // write STL
RETVAL = stl_in.stats.volume; //stl_write_ascii(&stl_in, output_file, (char*)"Repaired by Slic3r + ADMesh");
stl_generate_shared_vertices(&stl_in);
stl_write_obj(&stl_in, output_file);
stl_close(&stl_in); stl_close(&stl_in);
OUTPUT:
RETVAL
%} %}