mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-14 22:36:20 +08:00
Merge branch 'master' into lift-z-fix
This commit is contained in:
commit
de3de87bb6
15
.github/CONTRIBUTING.md
vendored
15
.github/CONTRIBUTING.md
vendored
@ -13,9 +13,22 @@ If possible, please include the following information when [reporting an issue](
|
|||||||
* Any error messages
|
* Any error messages
|
||||||
* If the issue is related to G-code generation, please include the following:
|
* If the issue is related to G-code generation, please include the following:
|
||||||
* STL, OBJ or AMF input file (please make sure the input file is not broken, e.g. non-manifold, before reporting a bug)
|
* STL, OBJ or AMF input file (please make sure the input file is not broken, e.g. non-manifold, before reporting a bug)
|
||||||
* a screenshot of the G-code layer with the issue (e.g. using [Pronterface](https://github.com/kliment/Printrun))
|
* a screenshot of the G-code layer with the issue (e.g. using [Pronterface](https://github.com/kliment/Printrun) or preferably the internal preview tab in Slic3r).
|
||||||
|
* If the issue is a request for a new feature, be ready to explain why you think it's needed.
|
||||||
|
* Doing more prepatory work on your end makes it more likely it'll get done. This includes the "how" it can be done in addition to the "what".
|
||||||
|
* Define the "What" as strictly as you can. Consider what might happen with different infills than simple rectilinear.
|
||||||
|
|
||||||
Please make sure only to include one issue per report. If you encounter multiple, unrelated issues, please report them as such.
|
Please make sure only to include one issue per report. If you encounter multiple, unrelated issues, please report them as such.
|
||||||
|
|
||||||
Simon Tatham has written an excellent on article on [How to Report Bugs Effectively](http://www.chiark.greenend.org.uk/~sgtatham/bugs.html) which is well worth reading, although it is not specific to Slic3r.
|
Simon Tatham has written an excellent on article on [How to Report Bugs Effectively](http://www.chiark.greenend.org.uk/~sgtatham/bugs.html) which is well worth reading, although it is not specific to Slic3r.
|
||||||
|
|
||||||
|
Do you want to help fix issues in or add features to Slic3r? That's also very, very welcome :)
|
||||||
|
|
||||||
|
* A good place to start if you can is to look over the [Pull Request or Bust](https://github.com/alexrj/Slic3r/milestones/Pull%20Request%20or%20Bust) milestone. This contains all of the things (mostly new feature requests) that there isn't time or resources to address at this time.
|
||||||
|
* Things that are probably fixable via scripts (usually marked as such) have the lowest bar to getting something that works, as you don't need to recompile Slic3r to test.
|
||||||
|
* If you're starting on an issue, please say something in the related issues thread so that someone else doesn't start working on it too.
|
||||||
|
* If there's nothing in the [Pull Request or Bust](https://github.com/alexrj/Slic3r/milestones/Pull%20Request%20or%20Bust) milestone that interests you, the next place to look is for issues that don't have a milestone. Lots of people commit ideas to Slic3r, and it's difficult to keep up and sort through them.
|
||||||
|
* Before sending a pull request, please make sure that the changes you are submitting are contained in their own git branch, as PRs merge histories.
|
||||||
|
* Pull requests that contain unrelated changes will be rejected.
|
||||||
|
* A common workflow is to fork the master branch, create your new branch and do your work there. git-rebase and git-cherry-pick are also helpful for separating out unrelated changes.
|
||||||
|
* If you are pushing Slic3r code changes that touch the main application, it is very much appreciated if you write some tests that check the functionality of the code. It's much easier to vet and merge in code that includes tests and doing so will likely speed things up.
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,3 +1,4 @@
|
|||||||
|
build
|
||||||
Build
|
Build
|
||||||
Build.bat
|
Build.bat
|
||||||
MYMETA.json
|
MYMETA.json
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
language: perl
|
language: perl
|
||||||
install: true
|
install: export LDLOADLIBS=-lstdc++
|
||||||
script: perl ./Build.PL
|
script: perl ./Build.PL
|
||||||
perl:
|
perl:
|
||||||
- "5.14"
|
- "5.14"
|
||||||
@ -19,3 +19,4 @@ addons:
|
|||||||
packages:
|
packages:
|
||||||
- libboost-thread1.55-dev
|
- libboost-thread1.55-dev
|
||||||
- libboost-system1.55-dev
|
- libboost-system1.55-dev
|
||||||
|
- libboost-filesystem1.55-dev
|
||||||
|
@ -2,7 +2,7 @@ _Q: Oh cool, a new RepRap slicer?_
|
|||||||
|
|
||||||
A: Yes.
|
A: Yes.
|
||||||
|
|
||||||
Slic3r [](https://travis-ci.org/alexrj/Slic3r)
|
Slic3r [](https://travis-ci.org/alexrj/Slic3r) [](https://ci.appveyor.com/project/lordofhyphens/slic3r)
|
||||||
======
|
======
|
||||||
Prebuilt Win32 builds https://bintray.com/lordofhyphens/Slic3r/slic3r_dev/view
|
Prebuilt Win32 builds https://bintray.com/lordofhyphens/Slic3r/slic3r_dev/view
|
||||||
|
|
||||||
|
@ -87,8 +87,10 @@ sub load {
|
|||||||
my $class = shift;
|
my $class = shift;
|
||||||
my ($file) = @_;
|
my ($file) = @_;
|
||||||
|
|
||||||
my $ini = __PACKAGE__->read_ini($file);
|
# legacy syntax of load()
|
||||||
return $class->load_ini_hash($ini->{_});
|
my $config = $class->new;
|
||||||
|
$config->_load($file);
|
||||||
|
return $config;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub load_ini_hash {
|
sub load_ini_hash {
|
||||||
@ -186,13 +188,6 @@ sub as_ini {
|
|||||||
return $ini;
|
return $ini;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub save {
|
|
||||||
my $self = shift;
|
|
||||||
my ($file) = @_;
|
|
||||||
|
|
||||||
__PACKAGE__->write_ini($file, $self->as_ini);
|
|
||||||
}
|
|
||||||
|
|
||||||
# this method is idempotent by design and only applies to ::DynamicConfig or ::Full
|
# this method is idempotent by design and only applies to ::DynamicConfig or ::Full
|
||||||
# objects because it performs cross checks
|
# objects because it performs cross checks
|
||||||
sub validate {
|
sub validate {
|
||||||
@ -235,7 +230,7 @@ sub validate {
|
|||||||
die "Invalid value for --gcode-flavor\n"
|
die "Invalid value for --gcode-flavor\n"
|
||||||
if !first { $_ eq $self->gcode_flavor } @{$Options->{gcode_flavor}{values}};
|
if !first { $_ eq $self->gcode_flavor } @{$Options->{gcode_flavor}{values}};
|
||||||
|
|
||||||
die "--use-firmware-retraction is only supported by Marlin firmware\n"
|
die "--use-firmware-retraction is only supported by Marlin and Machinekit firmware\n"
|
||||||
if $self->use_firmware_retraction && $self->gcode_flavor ne 'smoothie' && $self->gcode_flavor ne 'reprap' && $self->gcode_flavor ne 'machinekit';
|
if $self->use_firmware_retraction && $self->gcode_flavor ne 'smoothie' && $self->gcode_flavor ne 'reprap' && $self->gcode_flavor ne 'machinekit';
|
||||||
|
|
||||||
die "--use-firmware-retraction is not compatible with --wipe\n"
|
die "--use-firmware-retraction is not compatible with --wipe\n"
|
||||||
|
@ -214,16 +214,6 @@ sub make_fill {
|
|||||||
|
|
||||||
# calculate flow spacing for infill pattern generation
|
# calculate flow spacing for infill pattern generation
|
||||||
my $using_internal_flow = 0;
|
my $using_internal_flow = 0;
|
||||||
my $infill_combined_flow = $layerm->layer->object->config->layer_height * $layerm->region->config->infill_every_layers; # multiply the required layer height by infill_every_layers
|
|
||||||
if ($infill_combined_flow == 0) {
|
|
||||||
# just in case something happens we don't want to put in a 0mm layer.
|
|
||||||
$infill_combined_flow = $layerm->layer->object->config->layer_height;
|
|
||||||
}
|
|
||||||
if ($infill_combined_flow > $flow->nozzle_diameter) {
|
|
||||||
# can't put out layers bigger than the nozzle width, go as big as possible
|
|
||||||
$infill_combined_flow = (($flow->nozzle_diameter / $layerm->layer->object->config->layer_height) % 1) * $layerm->layer->object->config->layer_height
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$is_solid && !$is_bridge) {
|
if (!$is_solid && !$is_bridge) {
|
||||||
# it's internal infill, so we can calculate a generic flow spacing
|
# it's internal infill, so we can calculate a generic flow spacing
|
||||||
# for all layers, for avoiding the ugly effect of
|
# for all layers, for avoiding the ugly effect of
|
||||||
@ -231,8 +221,7 @@ sub make_fill {
|
|||||||
# layer height
|
# layer height
|
||||||
my $internal_flow = $layerm->region->flow(
|
my $internal_flow = $layerm->region->flow(
|
||||||
FLOW_ROLE_INFILL,
|
FLOW_ROLE_INFILL,
|
||||||
$infill_combined_flow,
|
$layerm->layer->object->config->layer_height, # TODO: handle infill_every_layers?
|
||||||
|
|
||||||
0, # no bridge
|
0, # no bridge
|
||||||
0, # no first layer
|
0, # no first layer
|
||||||
-1, # auto width
|
-1, # auto width
|
||||||
|
@ -56,10 +56,30 @@ use constant HOVER_COLOR => [0.4,0.9,0,1];
|
|||||||
sub new {
|
sub new {
|
||||||
my ($class, $parent) = @_;
|
my ($class, $parent) = @_;
|
||||||
|
|
||||||
|
# We can only enable multi sample anti aliasing wih wxWidgets 3.0.3 and with a hacked Wx::GLCanvas,
|
||||||
|
# which exports some new WX_GL_XXX constants, namely WX_GL_SAMPLE_BUFFERS and WX_GL_SAMPLES.
|
||||||
|
my $can_multisample =
|
||||||
|
Wx::wxVERSION >= 3.000003 &&
|
||||||
|
defined Wx::GLCanvas->can('WX_GL_SAMPLE_BUFFERS') &&
|
||||||
|
defined Wx::GLCanvas->can('WX_GL_SAMPLES');
|
||||||
|
my $attrib = [WX_GL_RGBA, WX_GL_DOUBLEBUFFER, WX_GL_DEPTH_SIZE, 24];
|
||||||
|
if ($can_multisample) {
|
||||||
|
# Request a window with multi sampled anti aliasing. This is a new feature in Wx 3.0.3 (backported from 3.1.0).
|
||||||
|
# Use eval to avoid compilation, if the subs WX_GL_SAMPLE_BUFFERS and WX_GL_SAMPLES are missing.
|
||||||
|
eval 'push(@$attrib, (WX_GL_SAMPLE_BUFFERS, 1, WX_GL_SAMPLES, 4));';
|
||||||
|
}
|
||||||
|
# wxWidgets expect the attrib list to be ended by zero.
|
||||||
|
push(@$attrib, 0);
|
||||||
|
|
||||||
# we request a depth buffer explicitely because it looks like it's not created by
|
# we request a depth buffer explicitely because it looks like it's not created by
|
||||||
# default on Linux, causing transparency issues
|
# default on Linux, causing transparency issues
|
||||||
my $self = $class->SUPER::new($parent, -1, Wx::wxDefaultPosition, Wx::wxDefaultSize, 0, "",
|
my $self = $class->SUPER::new($parent, -1, Wx::wxDefaultPosition, Wx::wxDefaultSize, 0, "", $attrib);
|
||||||
[WX_GL_RGBA, WX_GL_DOUBLEBUFFER, WX_GL_DEPTH_SIZE, 16, 0]);
|
if (Wx::wxVERSION >= 3.000003) {
|
||||||
|
# Wx 3.0.3 contains an ugly hack to support some advanced OpenGL attributes through the attribute list.
|
||||||
|
# The attribute list is transferred between the wxGLCanvas and wxGLContext constructors using a single static array s_wglContextAttribs.
|
||||||
|
# Immediatelly force creation of the OpenGL context to consume the static variable s_wglContextAttribs.
|
||||||
|
$self->GetContext();
|
||||||
|
}
|
||||||
|
|
||||||
$self->background(1);
|
$self->background(1);
|
||||||
$self->_quat((0, 0, 0, 1));
|
$self->_quat((0, 0, 0, 1));
|
||||||
|
@ -1640,7 +1640,7 @@ sub selection_changed {
|
|||||||
$self->{object_info_size}->SetLabel(sprintf("%.2f x %.2f x %.2f", @{$model_object->instance_bounding_box(0)->size}));
|
$self->{object_info_size}->SetLabel(sprintf("%.2f x %.2f x %.2f", @{$model_object->instance_bounding_box(0)->size}));
|
||||||
$self->{object_info_materials}->SetLabel($model_object->materials_count);
|
$self->{object_info_materials}->SetLabel($model_object->materials_count);
|
||||||
|
|
||||||
if (my $stats = $model_object->mesh_stats) {
|
if (my $stats = $model_object->raw_mesh->stats) {
|
||||||
$self->{object_info_volume}->SetLabel(sprintf('%.2f', $stats->{volume} * ($model_instance->scaling_factor**3)));
|
$self->{object_info_volume}->SetLabel(sprintf('%.2f', $stats->{volume} * ($model_instance->scaling_factor**3)));
|
||||||
$self->{object_info_facets}->SetLabel(sprintf('%d (%d shells)', $model_object->facets_count, $stats->{number_of_parts}));
|
$self->{object_info_facets}->SetLabel(sprintf('%d (%d shells)', $model_object->facets_count, $stats->{number_of_parts}));
|
||||||
if (my $errors = sum(@$stats{qw(degenerate_facets edges_fixed facets_removed facets_added facets_reversed backwards_edges)})) {
|
if (my $errors = sum(@$stats{qw(degenerate_facets edges_fixed facets_removed facets_added facets_reversed backwards_edges)})) {
|
||||||
|
@ -143,7 +143,13 @@ __PACKAGE__->mk_accessors(qw(
|
|||||||
sub new {
|
sub new {
|
||||||
my ($class, $parent, $print) = @_;
|
my ($class, $parent, $print) = @_;
|
||||||
|
|
||||||
my $self = $class->SUPER::new($parent);
|
my $self = (Wx::wxVERSION >= 3.000003) ?
|
||||||
|
# The wxWidgets 3.0.3-beta have a bug, they crash with NULL attribute list.
|
||||||
|
$class->SUPER::new($parent, -1, Wx::wxDefaultPosition, Wx::wxDefaultSize, 0, "",
|
||||||
|
[WX_GL_RGBA, WX_GL_DOUBLEBUFFER, WX_GL_DEPTH_SIZE, 24, 0]) :
|
||||||
|
$class->SUPER::new($parent);
|
||||||
|
# Immediatelly force creation of the OpenGL context to consume the static variable s_wglContextAttribs.
|
||||||
|
$self->GetContext();
|
||||||
$self->print($print);
|
$self->print($print);
|
||||||
$self->_zoom(1);
|
$self->_zoom(1);
|
||||||
|
|
||||||
|
@ -15,6 +15,11 @@ sub new {
|
|||||||
$self->{model_object_idx} = $params{model_object_idx};
|
$self->{model_object_idx} = $params{model_object_idx};
|
||||||
$self->{model_object} = $params{model_object};
|
$self->{model_object} = $params{model_object};
|
||||||
$self->{new_model_objects} = [];
|
$self->{new_model_objects} = [];
|
||||||
|
# Mark whether the mesh cut is valid.
|
||||||
|
# If not, it needs to be recalculated by _update() on wxTheApp->CallAfter() or on exit of the dialog.
|
||||||
|
$self->{mesh_cut_valid} = 0;
|
||||||
|
# Note whether the window was already closed, so a pending update is not executed.
|
||||||
|
$self->{already_closed} = 0;
|
||||||
|
|
||||||
# cut options
|
# cut options
|
||||||
$self->{cut_options} = {
|
$self->{cut_options} = {
|
||||||
@ -31,11 +36,18 @@ sub new {
|
|||||||
title => 'Cut',
|
title => 'Cut',
|
||||||
on_change => sub {
|
on_change => sub {
|
||||||
my ($opt_id) = @_;
|
my ($opt_id) = @_;
|
||||||
|
# There seems to be an issue with wxWidgets 3.0.2/3.0.3, where the slider
|
||||||
|
# genates tens of events for a single value change.
|
||||||
|
# Only trigger the recalculation if the value changes
|
||||||
|
# or a live preview was activated and the mesh cut is not valid yet.
|
||||||
|
if ($self->{cut_options}{$opt_id} != $optgroup->get_value($opt_id) ||
|
||||||
|
! $self->{mesh_cut_valid} && $self->_life_preview_active()) {
|
||||||
$self->{cut_options}{$opt_id} = $optgroup->get_value($opt_id);
|
$self->{cut_options}{$opt_id} = $optgroup->get_value($opt_id);
|
||||||
|
$self->{mesh_cut_valid} = 0;
|
||||||
wxTheApp->CallAfter(sub {
|
wxTheApp->CallAfter(sub {
|
||||||
$self->_update;
|
$self->_update;
|
||||||
});
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
label_width => 120,
|
label_width => 120,
|
||||||
);
|
);
|
||||||
@ -113,6 +125,10 @@ sub new {
|
|||||||
$self->{sizer}->SetSizeHints($self);
|
$self->{sizer}->SetSizeHints($self);
|
||||||
|
|
||||||
EVT_BUTTON($self, $self->{btn_cut}, sub {
|
EVT_BUTTON($self, $self->{btn_cut}, sub {
|
||||||
|
# Recalculate the cut if the preview was not active.
|
||||||
|
$self->_perform_cut() unless $self->{mesh_cut_valid};
|
||||||
|
|
||||||
|
# Adjust position / orientation of the split object halves.
|
||||||
if ($self->{new_model_objects}{lower}) {
|
if ($self->{new_model_objects}{lower}) {
|
||||||
if ($self->{cut_options}{rotate_lower}) {
|
if ($self->{cut_options}{rotate_lower}) {
|
||||||
$self->{new_model_objects}{lower}->rotate(PI, X);
|
$self->{new_model_objects}{lower}->rotate(PI, X);
|
||||||
@ -123,8 +139,17 @@ sub new {
|
|||||||
$self->{new_model_objects}{upper}->center_around_origin; # align to Z = 0
|
$self->{new_model_objects}{upper}->center_around_origin; # align to Z = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Note that the window was already closed, so a pending update will not be executed.
|
||||||
|
$self->{already_closed} = 1;
|
||||||
$self->EndModal(wxID_OK);
|
$self->EndModal(wxID_OK);
|
||||||
$self->Close;
|
$self->Destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
EVT_CLOSE($self, sub {
|
||||||
|
# Note that the window was already closed, so a pending update will not be executed.
|
||||||
|
$self->{already_closed} = 1;
|
||||||
|
$self->EndModal(wxID_CANCEL);
|
||||||
|
$self->Destroy();
|
||||||
});
|
});
|
||||||
|
|
||||||
$self->_update;
|
$self->_update;
|
||||||
@ -132,15 +157,31 @@ sub new {
|
|||||||
return $self;
|
return $self;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub _update {
|
# scale Z down to original size since we're using the transformed mesh for 3D preview
|
||||||
|
# and cut dialog but ModelObject::cut() needs Z without any instance transformation
|
||||||
|
sub _mesh_slice_z_pos
|
||||||
|
{
|
||||||
|
my ($self) = @_;
|
||||||
|
return $self->{cut_options}{z} / $self->{model_object}->instances->[0]->scaling_factor;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Only perform live preview if just a single part of the object shall survive.
|
||||||
|
sub _life_preview_active
|
||||||
|
{
|
||||||
|
my ($self) = @_;
|
||||||
|
return $self->{cut_options}{preview} && ($self->{cut_options}{keep_upper} != $self->{cut_options}{keep_lower});
|
||||||
|
}
|
||||||
|
|
||||||
|
# Slice the mesh, keep the top / bottom part.
|
||||||
|
sub _perform_cut
|
||||||
|
{
|
||||||
my ($self) = @_;
|
my ($self) = @_;
|
||||||
|
|
||||||
{
|
# Early exit. If the cut is valid, don't recalculate it.
|
||||||
# scale Z down to original size since we're using the transformed mesh for 3D preview
|
return if $self->{mesh_cut_valid};
|
||||||
# and cut dialog but ModelObject::cut() needs Z without any instance transformation
|
|
||||||
my $z = $self->{cut_options}{z} / $self->{model_object}->instances->[0]->scaling_factor;
|
my $z = $self->_mesh_slice_z_pos();
|
||||||
|
|
||||||
{
|
|
||||||
my ($new_model) = $self->{model_object}->cut($z);
|
my ($new_model) = $self->{model_object}->cut($z);
|
||||||
my ($upper_object, $lower_object) = @{$new_model->objects};
|
my ($upper_object, $lower_object) = @{$new_model->objects};
|
||||||
$self->{new_model} = $new_model;
|
$self->{new_model} = $new_model;
|
||||||
@ -151,13 +192,33 @@ sub _update {
|
|||||||
if ($self->{cut_options}{keep_lower} && $lower_object->volumes_count > 0) {
|
if ($self->{cut_options}{keep_lower} && $lower_object->volumes_count > 0) {
|
||||||
$self->{new_model_objects}{lower} = $lower_object;
|
$self->{new_model_objects}{lower} = $lower_object;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
$self->{mesh_cut_valid} = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub _update {
|
||||||
|
my ($self) = @_;
|
||||||
|
|
||||||
|
# Don't update if the window was already closed.
|
||||||
|
# We are not sure whether the action planned by wxTheApp->CallAfter() may be triggered after the window is closed.
|
||||||
|
# Probably not, but better be safe than sorry, which is espetially true on multiple platforms.
|
||||||
|
return if $self->{already_closed};
|
||||||
|
|
||||||
|
# Only recalculate the cut, if the live cut preview is active.
|
||||||
|
my $life_preview_active = $self->_life_preview_active();
|
||||||
|
$self->_perform_cut() if $life_preview_active;
|
||||||
|
|
||||||
|
{
|
||||||
|
# scale Z down to original size since we're using the transformed mesh for 3D preview
|
||||||
|
# and cut dialog but ModelObject::cut() needs Z without any instance transformation
|
||||||
|
my $z = $self->_mesh_slice_z_pos();
|
||||||
|
|
||||||
|
|
||||||
# update canvas
|
# update canvas
|
||||||
if ($self->{canvas}) {
|
if ($self->{canvas}) {
|
||||||
# get volumes to render
|
# get volumes to render
|
||||||
my @objects = ();
|
my @objects = ();
|
||||||
if ($self->{cut_options}{preview}) {
|
if ($life_preview_active) {
|
||||||
push @objects, values %{$self->{new_model_objects}};
|
push @objects, values %{$self->{new_model_objects}};
|
||||||
} else {
|
} else {
|
||||||
push @objects, $self->{model_object};
|
push @objects, $self->{model_object};
|
||||||
|
@ -67,11 +67,6 @@ sub set_material {
|
|||||||
return $material;
|
return $material;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub print_info {
|
|
||||||
my $self = shift;
|
|
||||||
$_->print_info for @{$self->objects};
|
|
||||||
}
|
|
||||||
|
|
||||||
sub looks_like_multipart_object {
|
sub looks_like_multipart_object {
|
||||||
my ($self) = @_;
|
my ($self) = @_;
|
||||||
|
|
||||||
@ -182,36 +177,4 @@ sub add_instance {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub mesh_stats {
|
|
||||||
my $self = shift;
|
|
||||||
|
|
||||||
# TODO: sum values from all volumes
|
|
||||||
return $self->volumes->[0]->mesh->stats;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub print_info {
|
|
||||||
my $self = shift;
|
|
||||||
|
|
||||||
printf "Info about %s:\n", basename($self->input_file);
|
|
||||||
printf " size: x=%.3f y=%.3f z=%.3f\n", @{$self->raw_mesh->bounding_box->size};
|
|
||||||
if (my $stats = $self->mesh_stats) {
|
|
||||||
printf " number of facets: %d\n", $stats->{number_of_facets};
|
|
||||||
printf " number of shells: %d\n", $stats->{number_of_parts};
|
|
||||||
printf " volume: %.3f\n", $stats->{volume};
|
|
||||||
if ($self->needed_repair) {
|
|
||||||
printf " needed repair: yes\n";
|
|
||||||
printf " degenerate facets: %d\n", $stats->{degenerate_facets};
|
|
||||||
printf " edges fixed: %d\n", $stats->{edges_fixed};
|
|
||||||
printf " facets removed: %d\n", $stats->{facets_removed};
|
|
||||||
printf " facets added: %d\n", $stats->{facets_added};
|
|
||||||
printf " facets reversed: %d\n", $stats->{facets_reversed};
|
|
||||||
printf " backwards edges: %d\n", $stats->{backwards_edges};
|
|
||||||
} else {
|
|
||||||
printf " needed repair: no\n";
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
printf " number of facets: %d\n", scalar(map @{$_->facets}, grep !$_->modifier, @{$self->volumes});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
@ -491,20 +491,17 @@ sub process_layer {
|
|||||||
# group extrusions by extruder and then by island
|
# group extrusions by extruder and then by island
|
||||||
my %by_extruder = (); # extruder_id => [ { perimeters => \@perimeters, infill => \@infill } ]
|
my %by_extruder = (); # extruder_id => [ { perimeters => \@perimeters, infill => \@infill } ]
|
||||||
|
|
||||||
my $n_slices = $#{$layer->slices};
|
# cache bounding boxes of layer slices
|
||||||
my @layer_surface_bboxes = ();
|
my @layer_slices_bb = map $_->contour->bounding_box, @{$layer->slices};
|
||||||
for my $i (0 .. $n_slices) {
|
|
||||||
push @layer_surface_bboxes, $layer->slices->[$i]->contour->bounding_box;
|
|
||||||
}
|
|
||||||
my $point_inside_surface = sub {
|
my $point_inside_surface = sub {
|
||||||
my ($i, $point) = @_;
|
my ($i, $point) = @_;
|
||||||
my $bbox = $layer_surface_bboxes[$i];
|
|
||||||
return
|
my $bbox = $layer_slices_bb[$i];
|
||||||
$point->x >= $bbox->x_min && $point->x < $bbox->x_max &&
|
return $layer_slices_bb[$i]->contains_point($point)
|
||||||
$point->y >= $bbox->y_min && $point->y < $bbox->y_max &&
|
&& $layer->slices->[$i]->contour->contains_point($point);
|
||||||
$layer->slices->[$i]->contour->contains_point($point);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
my $n_slices = $layer->slices->count - 1;
|
||||||
foreach my $region_id (0..($self->print->region_count-1)) {
|
foreach my $region_id (0..($self->print->region_count-1)) {
|
||||||
my $layerm = $layer->regions->[$region_id] or next;
|
my $layerm = $layer->regions->[$region_id] or next;
|
||||||
my $region = $self->print->get_region($region_id);
|
my $region = $self->print->get_region($region_id);
|
||||||
|
134
src/CMakeLists.txt
Normal file
134
src/CMakeLists.txt
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
cmake_minimum_required (VERSION 2.8)
|
||||||
|
project (slic3r)
|
||||||
|
|
||||||
|
# only on newer GCCs: -ftemplate-backtrace-limit=0
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSLIC3R_DEBUG")
|
||||||
|
|
||||||
|
set(workaround "")
|
||||||
|
if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.7.0)
|
||||||
|
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.7.3)
|
||||||
|
set(workaround "-fno-inline-small-functions")
|
||||||
|
endif(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.7.3)
|
||||||
|
endif(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.7.0)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_FLAGS "-DM_PI=3.14159265358979323846 -D_GLIBCXX_USE_C99 -DHAS_BOOL -DNOGDI -DBOOST_ASIO_DISABLE_KQUEUE ${workaround}")
|
||||||
|
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||||
|
|
||||||
|
IF(CMAKE_HOST_APPLE)
|
||||||
|
set(CMAKE_EXE_LINKER_FLAGS "-framework IOKit -framework CoreFoundation")
|
||||||
|
ELSE(CMAKE_HOST_APPLE)
|
||||||
|
set(CMAKE_EXE_LINKER_FLAGS "-static-libgcc -static-libstdc++")
|
||||||
|
ENDIF(CMAKE_HOST_APPLE)
|
||||||
|
set(Boost_USE_STATIC_LIBS ON)
|
||||||
|
set(Boost_USE_STATIC_RUNTIME ON)
|
||||||
|
set(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
|
||||||
|
find_package(Boost COMPONENTS system thread filesystem)
|
||||||
|
|
||||||
|
set(LIBDIR ${CMAKE_CURRENT_SOURCE_DIR}/../xs/src/)
|
||||||
|
|
||||||
|
include_directories(${LIBDIR})
|
||||||
|
include_directories(${LIBDIR}/libslic3r)
|
||||||
|
include_directories(${LIBDIR}/Slic3r/GUI/)
|
||||||
|
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/standalone/)
|
||||||
|
include_directories(${LIBDIR}/admesh/)
|
||||||
|
include_directories(${LIBDIR}/poly2tri/)
|
||||||
|
include_directories(${LIBDIR}/poly2tri/sweep)
|
||||||
|
include_directories(${LIBDIR}/poly2tri/common)
|
||||||
|
|
||||||
|
add_library(libslic3r STATIC
|
||||||
|
${LIBDIR}/libslic3r/BoundingBox.cpp
|
||||||
|
${LIBDIR}/libslic3r/BridgeDetector.cpp
|
||||||
|
${LIBDIR}/libslic3r/ClipperUtils.cpp
|
||||||
|
${LIBDIR}/libslic3r/Config.cpp
|
||||||
|
${LIBDIR}/libslic3r/ExPolygon.cpp
|
||||||
|
${LIBDIR}/libslic3r/ExPolygonCollection.cpp
|
||||||
|
${LIBDIR}/libslic3r/Extruder.cpp
|
||||||
|
${LIBDIR}/libslic3r/ExtrusionEntity.cpp
|
||||||
|
${LIBDIR}/libslic3r/ExtrusionEntityCollection.cpp
|
||||||
|
${LIBDIR}/libslic3r/Flow.cpp
|
||||||
|
${LIBDIR}/libslic3r/GCode.cpp
|
||||||
|
${LIBDIR}/libslic3r/GCodeSender.cpp
|
||||||
|
${LIBDIR}/libslic3r/GCodeWriter.cpp
|
||||||
|
${LIBDIR}/libslic3r/Geometry.cpp
|
||||||
|
${LIBDIR}/libslic3r/IO.cpp
|
||||||
|
${LIBDIR}/libslic3r/Layer.cpp
|
||||||
|
${LIBDIR}/libslic3r/LayerRegion.cpp
|
||||||
|
${LIBDIR}/libslic3r/Line.cpp
|
||||||
|
${LIBDIR}/libslic3r/Model.cpp
|
||||||
|
${LIBDIR}/libslic3r/MotionPlanner.cpp
|
||||||
|
${LIBDIR}/libslic3r/MultiPoint.cpp
|
||||||
|
${LIBDIR}/libslic3r/PerimeterGenerator.cpp
|
||||||
|
${LIBDIR}/libslic3r/PlaceholderParser.cpp
|
||||||
|
${LIBDIR}/libslic3r/Point.cpp
|
||||||
|
${LIBDIR}/libslic3r/Polygon.cpp
|
||||||
|
${LIBDIR}/libslic3r/Polyline.cpp
|
||||||
|
${LIBDIR}/libslic3r/PolylineCollection.cpp
|
||||||
|
${LIBDIR}/libslic3r/Print.cpp
|
||||||
|
${LIBDIR}/libslic3r/PrintConfig.cpp
|
||||||
|
${LIBDIR}/libslic3r/PrintObject.cpp
|
||||||
|
${LIBDIR}/libslic3r/PrintRegion.cpp
|
||||||
|
${LIBDIR}/libslic3r/Surface.cpp
|
||||||
|
${LIBDIR}/libslic3r/SurfaceCollection.cpp
|
||||||
|
${LIBDIR}/libslic3r/SVG.cpp
|
||||||
|
${LIBDIR}/libslic3r/SVGExport.cpp
|
||||||
|
${LIBDIR}/libslic3r/TriangleMesh.cpp
|
||||||
|
)
|
||||||
|
add_library(admesh STATIC
|
||||||
|
${LIBDIR}/admesh/connect.c
|
||||||
|
${LIBDIR}/admesh/normals.c
|
||||||
|
${LIBDIR}/admesh/shared.c
|
||||||
|
${LIBDIR}/admesh/stl_io.c
|
||||||
|
${LIBDIR}/admesh/stlinit.c
|
||||||
|
${LIBDIR}/admesh/util.c
|
||||||
|
)
|
||||||
|
add_library(clipper STATIC ${LIBDIR}/clipper.cpp)
|
||||||
|
add_library(polypartition STATIC ${LIBDIR}/polypartition.cpp)
|
||||||
|
add_library(poly2tri STATIC
|
||||||
|
${LIBDIR}/poly2tri/common/shapes.cc
|
||||||
|
${LIBDIR}/poly2tri/sweep/advancing_front.cc
|
||||||
|
${LIBDIR}/poly2tri/sweep/cdt.cc
|
||||||
|
${LIBDIR}/poly2tri/sweep/sweep_context.cc
|
||||||
|
${LIBDIR}/poly2tri/sweep/sweep.cc
|
||||||
|
)
|
||||||
|
|
||||||
|
add_executable(slic3r slic3r.cpp)
|
||||||
|
set_target_properties(slic3r PROPERTIES LINK_SEARCH_START_STATIC 1)
|
||||||
|
set_target_properties(slic3r PROPERTIES LINK_SEARCH_END_STATIC 1)
|
||||||
|
|
||||||
|
add_executable(extrude-tin utils/extrude-tin.cpp)
|
||||||
|
set_target_properties(extrude-tin PROPERTIES LINK_SEARCH_START_STATIC 1)
|
||||||
|
set_target_properties(extrude-tin PROPERTIES LINK_SEARCH_END_STATIC 1)
|
||||||
|
|
||||||
|
set(wxWidgets_USE_STATIC)
|
||||||
|
SET(wxWidgets_USE_LIBS)
|
||||||
|
|
||||||
|
set(Boost_USE_STATIC_LIBS ON)
|
||||||
|
set(Boost_USE_STATIC_RUNTIME ON)
|
||||||
|
set(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
|
||||||
|
find_library(bsystem_l boost_system)
|
||||||
|
add_library(bsystem STATIC IMPORTED)
|
||||||
|
set_target_properties(bsystem PROPERTIES IMPORTED_LOCATION ${bsystem_l})
|
||||||
|
find_library(bthread_l boost_thread)
|
||||||
|
add_library(bthread STATIC IMPORTED)
|
||||||
|
set_target_properties(bthread PROPERTIES IMPORTED_LOCATION ${bthread_l})
|
||||||
|
include_directories(${Boost_INCLUDE_DIRS})
|
||||||
|
|
||||||
|
#find_package(wxWidgets)
|
||||||
|
#disable wx for the time being - we're not building any of the gui yet
|
||||||
|
IF(CMAKE_HOST_UNIX)
|
||||||
|
#set(Boost_LIBRARIES bsystem bthread bfilesystem)
|
||||||
|
ENDIF(CMAKE_HOST_UNIX)
|
||||||
|
IF(wxWidgets_FOUND)
|
||||||
|
MESSAGE("wx found!")
|
||||||
|
INCLUDE("${wxWidgets_USE_FILE}")
|
||||||
|
add_library(slic3r_gui STATIC ${LIBDIR}/slic3r/GUI/3DScene.cpp ${LIBDIR}/slic3r/GUI/GUI.cpp)
|
||||||
|
#only build GUI lib if building with wx
|
||||||
|
target_link_libraries (slic3r slic3r_gui libslic3r admesh clipper polypartition poly2tri ${Boost_LIBRARIES} ${wxWidgets_LIBRARIES})
|
||||||
|
ELSE(wxWidgets_FOUND)
|
||||||
|
# For convenience. When we cannot continue, inform the user
|
||||||
|
MESSAGE("wx not found!")
|
||||||
|
target_link_libraries (slic3r libslic3r admesh clipper polypartition poly2tri ${Boost_LIBRARIES})
|
||||||
|
#skip gui when no wx included
|
||||||
|
ENDIF(wxWidgets_FOUND)
|
||||||
|
|
||||||
|
target_link_libraries (extrude-tin libslic3r admesh clipper polypartition poly2tri ${Boost_LIBRARIES})
|
110
src/slic3r.cpp
Normal file
110
src/slic3r.cpp
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
#include "Config.hpp"
|
||||||
|
#include "Model.hpp"
|
||||||
|
#include "IO.hpp"
|
||||||
|
#include "TriangleMesh.hpp"
|
||||||
|
#include "SVGExport.hpp"
|
||||||
|
#include "libslic3r.h"
|
||||||
|
#include <cstdio>
|
||||||
|
#include <string>
|
||||||
|
#include <cstring>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
using namespace Slic3r;
|
||||||
|
|
||||||
|
void confess_at(const char *file, int line, const char *func, const char *pat, ...){}
|
||||||
|
|
||||||
|
int
|
||||||
|
main(const int argc, const char **argv)
|
||||||
|
{
|
||||||
|
// parse all command line options into a DynamicConfig
|
||||||
|
ConfigDef config_def;
|
||||||
|
config_def.merge(cli_config_def);
|
||||||
|
config_def.merge(print_config_def);
|
||||||
|
DynamicConfig config(&config_def);
|
||||||
|
t_config_option_keys input_files;
|
||||||
|
config.read_cli(argc, argv, &input_files);
|
||||||
|
|
||||||
|
// apply command line options to a more handy CLIConfig
|
||||||
|
CLIConfig cli_config;
|
||||||
|
cli_config.apply(config, true);
|
||||||
|
|
||||||
|
DynamicPrintConfig print_config;
|
||||||
|
|
||||||
|
// load config files supplied via --load
|
||||||
|
for (std::vector<std::string>::const_iterator file = cli_config.load.values.begin();
|
||||||
|
file != cli_config.load.values.end(); ++file) {
|
||||||
|
DynamicPrintConfig c;
|
||||||
|
c.load(*file);
|
||||||
|
c.normalize();
|
||||||
|
print_config.apply(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
// apply command line options to a more specific DynamicPrintConfig which provides normalize()
|
||||||
|
// (command line options override --load files)
|
||||||
|
print_config.apply(config, true);
|
||||||
|
print_config.normalize();
|
||||||
|
|
||||||
|
// write config if requested
|
||||||
|
if (!cli_config.save.value.empty()) print_config.save(cli_config.save.value);
|
||||||
|
|
||||||
|
// read input file(s) if any
|
||||||
|
std::vector<Model> models;
|
||||||
|
for (t_config_option_keys::const_iterator it = input_files.begin(); it != input_files.end(); ++it) {
|
||||||
|
Model model;
|
||||||
|
// TODO: read other file formats with Model::read_from_file()
|
||||||
|
Slic3r::IO::STL::read(*it, &model);
|
||||||
|
|
||||||
|
if (model.objects.empty()) {
|
||||||
|
printf("Error: file is empty: %s\n", it->c_str());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
model.add_default_instances();
|
||||||
|
|
||||||
|
// apply command line transform options
|
||||||
|
for (ModelObjectPtrs::iterator o = model.objects.begin(); o != model.objects.end(); ++o) {
|
||||||
|
(*o)->scale(cli_config.scale.value);
|
||||||
|
(*o)->rotate(cli_config.rotate.value, Z);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: handle --merge
|
||||||
|
models.push_back(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (std::vector<Model>::iterator model = models.begin(); model != models.end(); ++model) {
|
||||||
|
if (cli_config.info) {
|
||||||
|
// --info works on unrepaired model
|
||||||
|
model->print_info();
|
||||||
|
} else if (cli_config.export_obj) {
|
||||||
|
std::string outfile = cli_config.output.value;
|
||||||
|
if (outfile.empty()) outfile = model->objects.front()->input_file + ".obj";
|
||||||
|
|
||||||
|
TriangleMesh mesh = model->mesh();
|
||||||
|
mesh.repair();
|
||||||
|
Slic3r::IO::OBJ::write(mesh, outfile);
|
||||||
|
printf("File exported to %s\n", outfile.c_str());
|
||||||
|
} else if (cli_config.export_pov) {
|
||||||
|
std::string outfile = cli_config.output.value;
|
||||||
|
if (outfile.empty()) outfile = model->objects.front()->input_file + ".pov";
|
||||||
|
|
||||||
|
TriangleMesh mesh = model->mesh();
|
||||||
|
mesh.repair();
|
||||||
|
Slic3r::IO::POV::write(mesh, outfile);
|
||||||
|
printf("File exported to %s\n", outfile.c_str());
|
||||||
|
} else if (cli_config.export_svg) {
|
||||||
|
std::string outfile = cli_config.output.value;
|
||||||
|
if (outfile.empty()) outfile = model->objects.front()->input_file + ".svg";
|
||||||
|
|
||||||
|
SVGExport svg_export(model->mesh());
|
||||||
|
svg_export.mesh.repair();
|
||||||
|
svg_export.config.apply(print_config, true);
|
||||||
|
svg_export.writeSVG(outfile);
|
||||||
|
printf("SVG file exported to %s\n", outfile.c_str());
|
||||||
|
} else {
|
||||||
|
std::cerr << "error: only --export-svg and --export-obj are currently supported" << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
0
src/standalone/config.h
Normal file
0
src/standalone/config.h
Normal file
94
src/utils/extrude-tin.cpp
Normal file
94
src/utils/extrude-tin.cpp
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
#include "Config.hpp"
|
||||||
|
#include "Model.hpp"
|
||||||
|
#include "IO.hpp"
|
||||||
|
#include "TriangleMesh.hpp"
|
||||||
|
#include "libslic3r.h"
|
||||||
|
|
||||||
|
using namespace Slic3r;
|
||||||
|
|
||||||
|
void confess_at(const char *file, int line, const char *func, const char *pat, ...){}
|
||||||
|
|
||||||
|
int
|
||||||
|
main(const int argc, const char **argv)
|
||||||
|
{
|
||||||
|
// read config
|
||||||
|
ConfigDef config_def;
|
||||||
|
{
|
||||||
|
ConfigOptionDef* def;
|
||||||
|
|
||||||
|
def = config_def.add("offset", coFloat);
|
||||||
|
def->label = "Offset from the lowest point (min thickness)";
|
||||||
|
def->cli = "offset";
|
||||||
|
def->default_value = new ConfigOptionFloat(1);
|
||||||
|
|
||||||
|
def = config_def.add("output", coString);
|
||||||
|
def->label = "Output File";
|
||||||
|
def->tooltip = "The file where the output will be written (if not specified, it will be based on the input file).";
|
||||||
|
def->cli = "output";
|
||||||
|
def->default_value = new ConfigOptionString("");
|
||||||
|
}
|
||||||
|
DynamicConfig config(&config_def);
|
||||||
|
t_config_option_keys input_files;
|
||||||
|
config.read_cli(argc, argv, &input_files);
|
||||||
|
|
||||||
|
for (t_config_option_keys::const_iterator it = input_files.begin(); it != input_files.end(); ++it) {
|
||||||
|
TriangleMesh mesh;
|
||||||
|
Slic3r::IO::STL::read(*it, &mesh);
|
||||||
|
calculate_normals(&mesh.stl);
|
||||||
|
|
||||||
|
if (mesh.facets_count() == 0) {
|
||||||
|
printf("Error: file is empty: %s\n", it->c_str());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
float z = mesh.stl.stats.min.z - config.option("offset", true)->getFloat();
|
||||||
|
printf("min.z = %f, z = %f\n", mesh.stl.stats.min.z, z);
|
||||||
|
TriangleMesh mesh2 = mesh;
|
||||||
|
|
||||||
|
for (int i = 0; i < mesh.stl.stats.number_of_facets; ++i) {
|
||||||
|
const stl_facet &facet = mesh.stl.facet_start[i];
|
||||||
|
|
||||||
|
if (facet.normal.z < 0) {
|
||||||
|
printf("Invalid 2.5D mesh / TIN (one facet points downwards = %f)\n", facet.normal.z);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int j = 0; j < 3; ++j) {
|
||||||
|
if (mesh.stl.neighbors_start[i].neighbor[j] == -1) {
|
||||||
|
stl_facet new_facet;
|
||||||
|
float normal[3];
|
||||||
|
|
||||||
|
// first triangle
|
||||||
|
new_facet.vertex[0] = new_facet.vertex[2] = facet.vertex[(j+1)%3];
|
||||||
|
new_facet.vertex[1] = facet.vertex[j];
|
||||||
|
new_facet.vertex[2].z = z;
|
||||||
|
stl_calculate_normal(normal, &new_facet);
|
||||||
|
stl_normalize_vector(normal);
|
||||||
|
new_facet.normal.x = normal[0];
|
||||||
|
new_facet.normal.y = normal[1];
|
||||||
|
new_facet.normal.z = normal[2];
|
||||||
|
stl_add_facet(&mesh2.stl, &new_facet);
|
||||||
|
|
||||||
|
// second triangle
|
||||||
|
new_facet.vertex[0] = new_facet.vertex[1] = facet.vertex[j];
|
||||||
|
new_facet.vertex[2] = facet.vertex[(j+1)%3];
|
||||||
|
new_facet.vertex[1].z = new_facet.vertex[2].z = z;
|
||||||
|
new_facet.normal.x = normal[0];
|
||||||
|
new_facet.normal.y = normal[1];
|
||||||
|
new_facet.normal.z = normal[2];
|
||||||
|
stl_add_facet(&mesh2.stl, &new_facet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mesh2.repair();
|
||||||
|
|
||||||
|
std::string outfile = config.option("output", true)->getString();
|
||||||
|
if (outfile.empty()) outfile = *it + "_extruded.stl";
|
||||||
|
|
||||||
|
Slic3r::IO::STL::write(mesh2, outfile);
|
||||||
|
printf("Extruded mesh written to %s\n", outfile.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
40
src/windows-build.txt
Normal file
40
src/windows-build.txt
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
Install:
|
||||||
|
mingw
|
||||||
|
boost
|
||||||
|
cmake
|
||||||
|
git
|
||||||
|
|
||||||
|
Assuming boost is in c:\program files\boost\boost_1_61_0 and mingw is in c:\mingw
|
||||||
|
|
||||||
|
start cmd.exe
|
||||||
|
> cd c:\program files\boost\boost_1_61_0
|
||||||
|
> set PATH=c:\mingw\bin
|
||||||
|
> bootstrap gcc
|
||||||
|
> .\b2 --build-dir=c:\boost-mingw toolset=gcc link=static runtime-link=static variant=release --with-system --with-thread
|
||||||
|
leave cmd window open
|
||||||
|
|
||||||
|
start git bash
|
||||||
|
> cd /c
|
||||||
|
> git clone http://github.com/alexrj/slic3r
|
||||||
|
> cd slic3r
|
||||||
|
> git checkout cppsvg
|
||||||
|
close git bash when done
|
||||||
|
|
||||||
|
make sure c:\mingw\bin is part of the Path system variable, add it otherwise
|
||||||
|
|
||||||
|
start cmake gui
|
||||||
|
source code: c:\slic3r\src
|
||||||
|
build directory: c:\slic3r\build
|
||||||
|
click configure, select "mingw makefiles" from list, select "default native compilers", click finish
|
||||||
|
click generate
|
||||||
|
close cmake gui
|
||||||
|
|
||||||
|
alternatively, do it from command line:
|
||||||
|
cmake ..\src -G "MinGW Makefiles" -DBOOST_ROOT="c:\program files\boost\boost_1_61_0"
|
||||||
|
(in case cmake can't find the libs, -DBoost_DEBUG=1 and -DBoost_COMPILER=-mgw46 are useful)
|
||||||
|
|
||||||
|
go back to cmd window
|
||||||
|
> cd c:\slic3r\build
|
||||||
|
> mingw32-make.exe
|
||||||
|
might be mingw64 on 64-bit setup, I'm not sure
|
||||||
|
The resulting slic3r.exe is the target executable, it has no dependencies except windows system libraries (kernel32 and msvcrt)
|
1
utils/autorun.bat
Normal file
1
utils/autorun.bat
Normal file
@ -0,0 +1 @@
|
|||||||
|
@perl5.22.2.exe slic3r.pl %*
|
222
utils/package_win32.ps1
Normal file
222
utils/package_win32.ps1
Normal file
@ -0,0 +1,222 @@
|
|||||||
|
# Written by Joseph Lenox
|
||||||
|
# Licensed under the same license as the rest of Slic3r.
|
||||||
|
# ------------------------
|
||||||
|
# You need to have Strawberry Perl 5.22 installed for this to work,
|
||||||
|
echo "Make this is run from the perl command window."
|
||||||
|
echo "Requires PAR."
|
||||||
|
|
||||||
|
New-Variable -Name "current_branch" -Value ""
|
||||||
|
|
||||||
|
git branch | foreach {
|
||||||
|
if ($_ -match "` (.*)"){
|
||||||
|
$current_branch += $matches[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Change this to where you have Strawberry Perl installed.
|
||||||
|
New-Variable -Name "STRAWBERRY_PATH" -Value "C:\Strawberry"
|
||||||
|
|
||||||
|
cpanm "PAR::Packer"
|
||||||
|
|
||||||
|
pp `
|
||||||
|
-a "../utils;utils" `
|
||||||
|
-a "autorun.bat;slic3r.bat" `
|
||||||
|
-a "../var;var" `
|
||||||
|
-a "${STRAWBERRY_PATH}\perl\bin\perl5.22.2.exe;perl5.22.2.exe" `
|
||||||
|
-a "${STRAWBERRY_PATH}\perl\bin\perl522.dll;perl522.dll" `
|
||||||
|
-a "${STRAWBERRY_PATH}\perl\bin\libgcc_s_sjlj-1.dll;libgcc_s_sjlj-1.dll" `
|
||||||
|
-a "${STRAWBERRY_PATH}\perl\bin\libstdc++-6.dll;libstdc++-6.dll" `
|
||||||
|
-a "${STRAWBERRY_PATH}\perl\bin\libwinpthread-1.dll;libwinpthread-1.dll" `
|
||||||
|
-a "${STRAWBERRY_PATH}\perl\bin\freeglut.dll;freeglut.dll" `
|
||||||
|
-a "${STRAWBERRY_PATH}\perl\site\lib\Alien\wxWidgets\msw_3_0_2_uni_gcc_3_4\lib\wxbase30u_gcc_custom.dll;wxbase30u_gcc_custom.dll" `
|
||||||
|
-a "${STRAWBERRY_PATH}\perl\site\lib\Alien\wxWidgets\msw_3_0_2_uni_gcc_3_4\lib\wxmsw30u_adv_gcc_custom.dll;wxmsw30u_adv_gcc_custom.dll" `
|
||||||
|
-a "${STRAWBERRY_PATH}\perl\site\lib\Alien\wxWidgets\msw_3_0_2_uni_gcc_3_4\lib\wxmsw30u_gl_gcc_custom.dll;wxmsw30u_gl_gcc_custom.dll" `
|
||||||
|
-a "${STRAWBERRY_PATH}\perl\site\lib\Alien\wxWidgets\msw_3_0_2_uni_gcc_3_4\lib\wxmsw30u_core_gcc_custom.dll;wxmsw30u_core_gcc_custom.dll" `
|
||||||
|
-a "${STRAWBERRY_PATH}\perl\site\lib\Alien\wxWidgets\msw_3_0_2_uni_gcc_3_4\lib\wxmsw30u_html_gcc_custom.dll;wxmsw30u_html_gcc_custom.dll" `
|
||||||
|
-a "${STRAWBERRY_PATH}\perl\site\lib\Alien\wxWidgets\msw_3_0_2_uni_gcc_3_4\lib\wxbase30u_xml_gcc_custom.dll;wxbase30u_xml_gcc_custom.dll" `
|
||||||
|
-a "${STRAWBERRY_PATH}\perl\site\lib\Alien\wxWidgets\msw_3_0_2_uni_gcc_3_4\lib\wxbase30u_net_gcc_custom.dll;wxbase30u_net_gcc_custom.dll" `
|
||||||
|
-a "../lib;lib" `
|
||||||
|
-a "../slic3r.pl;slic3r.pl" `
|
||||||
|
-M AutoLoader `
|
||||||
|
-M B `
|
||||||
|
-M Carp `
|
||||||
|
-M Class::Accessor `
|
||||||
|
-M Class::XSAccessor `
|
||||||
|
-M Class::XSAccessor::Heavy `
|
||||||
|
-M Config `
|
||||||
|
-M Crypt::CBC `
|
||||||
|
-M Cwd `
|
||||||
|
-M Data::UUID `
|
||||||
|
-M Devel::GlobalDestruction `
|
||||||
|
-M Digest `
|
||||||
|
-M Digest::MD5 `
|
||||||
|
-M Digest::SHA `
|
||||||
|
-M Digest::base `
|
||||||
|
-M DynaLoader `
|
||||||
|
-M Encode `
|
||||||
|
-M Encode::Alias `
|
||||||
|
-M Encode::Byte `
|
||||||
|
-M Encode::Config `
|
||||||
|
-M Encode::Encoding `
|
||||||
|
-M Encode::Locale `
|
||||||
|
-M Encode::MIME::Name `
|
||||||
|
-M Errno `
|
||||||
|
-M Exporter `
|
||||||
|
-M Exporter::Heavy `
|
||||||
|
-M Fcntl `
|
||||||
|
-M File::Basename `
|
||||||
|
-M File::Glob `
|
||||||
|
-M File::Spec `
|
||||||
|
-M File::Spec::Unix `
|
||||||
|
-M File::Spec::Win32 `
|
||||||
|
-M FindBin `
|
||||||
|
-M Getopt::Long `
|
||||||
|
-M Growl::GNTP `
|
||||||
|
-M HTTP::Config `
|
||||||
|
-M HTTP::Date `
|
||||||
|
-M HTTP::Headers `
|
||||||
|
-M HTTP::Headers::Util `
|
||||||
|
-M HTTP::Message `
|
||||||
|
-M HTTP::Request `
|
||||||
|
-M HTTP::Request::Common `
|
||||||
|
-M HTTP::Response `
|
||||||
|
-M HTTP::Status `
|
||||||
|
-M IO `
|
||||||
|
-M IO::Handle `
|
||||||
|
-M IO::Select `
|
||||||
|
-M IO::Socket `
|
||||||
|
-M IO::Socket::INET `
|
||||||
|
-M IO::Socket::INET6 `
|
||||||
|
-M IO::Socket::IP `
|
||||||
|
-M IO::Socket::UNIX `
|
||||||
|
-M LWP `
|
||||||
|
-M LWP::MediaTypes `
|
||||||
|
-M LWP::MemberMixin `
|
||||||
|
-M LWP::Protocol `
|
||||||
|
-M LWP::Protocol::http `
|
||||||
|
-M LWP::UserAgent `
|
||||||
|
-M List::Util `
|
||||||
|
-M Math::Libm `
|
||||||
|
-M Math::PlanePath `
|
||||||
|
-M Math::PlanePath::ArchimedeanChords `
|
||||||
|
-M Math::PlanePath::Base::Digits `
|
||||||
|
-M Math::PlanePath::Base::Generic `
|
||||||
|
-M Math::PlanePath::Base::NSEW `
|
||||||
|
-M Math::PlanePath::Flowsnake `
|
||||||
|
-M Math::PlanePath::FlowsnakeCentres `
|
||||||
|
-M Math::PlanePath::HilbertCurve `
|
||||||
|
-M Math::PlanePath::OctagramSpiral `
|
||||||
|
-M Math::PlanePath::SacksSpiral `
|
||||||
|
-M Math::Trig `
|
||||||
|
-M Method::Generate::Accessor `
|
||||||
|
-M Method::Generate::BuildAll `
|
||||||
|
-M Method::Generate::Constructor `
|
||||||
|
-M Module::Runtime `
|
||||||
|
-M Moo `
|
||||||
|
-M Moo::HandleMoose `
|
||||||
|
-M Moo::Object `
|
||||||
|
-M Moo::Role `
|
||||||
|
-M Moo::sification `
|
||||||
|
-M Net::Bonjour `
|
||||||
|
-M Net::Bonjour::Entry `
|
||||||
|
-M Net::DNS `
|
||||||
|
-M Net::DNS::Domain `
|
||||||
|
-M Net::DNS::DomainName `
|
||||||
|
-M Net::DNS::Header `
|
||||||
|
-M Net::DNS::Packet `
|
||||||
|
-M Net::DNS::Parameters `
|
||||||
|
-M Net::DNS::Question `
|
||||||
|
-M Net::DNS::RR `
|
||||||
|
-M Net::DNS::RR::OPT `
|
||||||
|
-M Net::DNS::RR::PTR `
|
||||||
|
-M Net::DNS::Resolver `
|
||||||
|
-M Net::DNS::Resolver `
|
||||||
|
-M Net::DNS::Resolver::Base `
|
||||||
|
-M Net::DNS::Resolver::MSWin32 `
|
||||||
|
-M Net::DNS::Update `
|
||||||
|
-M Net::HTTP `
|
||||||
|
-M Net::HTTP::Methods `
|
||||||
|
-M OpenGL `
|
||||||
|
-M POSIX `
|
||||||
|
-M Pod::Escapes `
|
||||||
|
-M Pod::Text `
|
||||||
|
-M Pod::Usage `
|
||||||
|
-M Role::Tiny `
|
||||||
|
-M Scalar::Util `
|
||||||
|
-M SelectSaver `
|
||||||
|
-M Slic3r::* `
|
||||||
|
-M Slic3r::XS `
|
||||||
|
-M Socket `
|
||||||
|
-M Socket6 `
|
||||||
|
-M Storable `
|
||||||
|
-M Sub::Defer `
|
||||||
|
-M Sub::Exporter `
|
||||||
|
-M Sub::Exporter::Progressive `
|
||||||
|
-M Sub::Name `
|
||||||
|
-M Sub::Quote `
|
||||||
|
-M Sub::Util `
|
||||||
|
-M Symbol `
|
||||||
|
-M Term::Cap `
|
||||||
|
-M Text::ParseWords `
|
||||||
|
-M Thread `
|
||||||
|
-M Thread::Queue `
|
||||||
|
-M Thread::Semaphore `
|
||||||
|
-M Tie::Handle `
|
||||||
|
-M Tie::Hash `
|
||||||
|
-M Tie::StdHandle `
|
||||||
|
-M Time::HiRes `
|
||||||
|
-M Time::Local `
|
||||||
|
-M URI `
|
||||||
|
-M URI::Escape `
|
||||||
|
-M URI::http `
|
||||||
|
-M Unicode::Normalize `
|
||||||
|
-M Win32 `
|
||||||
|
-M Win32::API `
|
||||||
|
-M Win32::API::Struct `
|
||||||
|
-M Win32::API::Type `
|
||||||
|
-M Win32::IPHelper `
|
||||||
|
-M Win32::TieRegistry `
|
||||||
|
-M Win32::WinError `
|
||||||
|
-M Win32API::Registry `
|
||||||
|
-M Wx `
|
||||||
|
-M Wx::App `
|
||||||
|
-M Wx::DND `
|
||||||
|
-M Wx::DropSource `
|
||||||
|
-M Wx::Event `
|
||||||
|
-M Wx::GLCanvas `
|
||||||
|
-M Wx::Grid `
|
||||||
|
-M Wx::Html `
|
||||||
|
-M Wx::Locale `
|
||||||
|
-M Wx::Menu `
|
||||||
|
-M Wx::Mini `
|
||||||
|
-M Wx::Print `
|
||||||
|
-M Wx::RadioBox `
|
||||||
|
-M Wx::Timer `
|
||||||
|
-M XSLoader `
|
||||||
|
-M attributes `
|
||||||
|
-M base `
|
||||||
|
-M bytes `
|
||||||
|
-M constant `
|
||||||
|
-M constant `
|
||||||
|
-M constant::defer `
|
||||||
|
-M enum `
|
||||||
|
-M feature `
|
||||||
|
-M integer `
|
||||||
|
-M locale `
|
||||||
|
-M lib `
|
||||||
|
-M mro `
|
||||||
|
-M overload `
|
||||||
|
-M overload::numbers `
|
||||||
|
-M overloading `
|
||||||
|
-M parent `
|
||||||
|
-M re `
|
||||||
|
-M strict `
|
||||||
|
-M threads `
|
||||||
|
-M threads::shared `
|
||||||
|
-M utf8 `
|
||||||
|
-M vars `
|
||||||
|
-M warnings `
|
||||||
|
-M warnings::register `
|
||||||
|
-e -p ..\slic3r.pl -o ..\slic3r.par
|
||||||
|
|
||||||
|
copy ..\slic3r.par "..\slic3r-${current_branch}-${APPVEYOR_BUILD_NUMBER}-$(git rev-parse --short HEAD).zip"
|
@ -47,7 +47,7 @@ if (defined $ENV{BOOST_DIR}) {
|
|||||||
|
|
||||||
# In order to generate the -l switches we need to know how Boost libraries are named
|
# In order to generate the -l switches we need to know how Boost libraries are named
|
||||||
my $have_boost = 0;
|
my $have_boost = 0;
|
||||||
my @boost_libraries = qw(system thread); # we need these
|
my @boost_libraries = qw(system thread filesystem); # we need these
|
||||||
|
|
||||||
# check without explicit lib path (works on Linux)
|
# check without explicit lib path (works on Linux)
|
||||||
$have_boost = 1
|
$have_boost = 1
|
||||||
|
@ -162,6 +162,7 @@ extern void stl_translate(stl_file *stl, float x, float y, float z);
|
|||||||
extern void stl_translate_relative(stl_file *stl, float x, float y, float z);
|
extern void stl_translate_relative(stl_file *stl, float x, float y, float z);
|
||||||
extern void stl_scale_versor(stl_file *stl, float versor[3]);
|
extern void stl_scale_versor(stl_file *stl, float versor[3]);
|
||||||
extern void stl_scale(stl_file *stl, float factor);
|
extern void stl_scale(stl_file *stl, float factor);
|
||||||
|
extern void calculate_normals(stl_file *stl);
|
||||||
extern void stl_rotate_x(stl_file *stl, float angle);
|
extern void stl_rotate_x(stl_file *stl, float angle);
|
||||||
extern void stl_rotate_y(stl_file *stl, float angle);
|
extern void stl_rotate_y(stl_file *stl, float angle);
|
||||||
extern void stl_rotate_z(stl_file *stl, float angle);
|
extern void stl_rotate_z(stl_file *stl, float angle);
|
||||||
|
@ -170,7 +170,7 @@ stl_scale(stl_file *stl, float factor) {
|
|||||||
stl_scale_versor(stl, versor);
|
stl_scale_versor(stl, versor);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void calculate_normals(stl_file *stl) {
|
void calculate_normals(stl_file *stl) {
|
||||||
long i;
|
long i;
|
||||||
float normal[3];
|
float normal[3];
|
||||||
|
|
||||||
|
@ -219,4 +219,13 @@ BoundingBox3Base<PointClass>::center() const
|
|||||||
}
|
}
|
||||||
template Pointf3 BoundingBox3Base<Pointf3>::center() const;
|
template Pointf3 BoundingBox3Base<Pointf3>::center() const;
|
||||||
|
|
||||||
|
template <class PointClass> bool
|
||||||
|
BoundingBoxBase<PointClass>::contains(const PointClass &point) const
|
||||||
|
{
|
||||||
|
return point.x >= this->min.x && point.x <= this->max.x
|
||||||
|
&& point.y >= this->min.y && point.y <= this->max.y;
|
||||||
|
}
|
||||||
|
template bool BoundingBoxBase<Point>::contains(const Point &point) const;
|
||||||
|
template bool BoundingBoxBase<Pointf>::contains(const Pointf &point) const;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,7 @@ class BoundingBoxBase
|
|||||||
void translate(coordf_t x, coordf_t y);
|
void translate(coordf_t x, coordf_t y);
|
||||||
void offset(coordf_t delta);
|
void offset(coordf_t delta);
|
||||||
PointClass center() const;
|
PointClass center() const;
|
||||||
|
bool contains(const PointClass &point) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <class PointClass>
|
template <class PointClass>
|
||||||
|
@ -222,6 +222,18 @@ offset_ex(const Slic3r::Polygons &polygons, const float delta,
|
|||||||
return expp;
|
return expp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Slic3r::ExPolygons
|
||||||
|
offset_ex(const Slic3r::ExPolygons &expolygons, const float delta,
|
||||||
|
double scale, ClipperLib::JoinType joinType, double miterLimit)
|
||||||
|
{
|
||||||
|
Slic3r::Polygons pp;
|
||||||
|
for (Slic3r::ExPolygons::const_iterator ex = expolygons.begin(); ex != expolygons.end(); ++ex) {
|
||||||
|
Slic3r::Polygons pp2 = *ex;
|
||||||
|
pp.insert(pp.end(), pp2.begin(), pp2.end());
|
||||||
|
}
|
||||||
|
return offset_ex(pp, delta, scale, joinType, miterLimit);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
offset2(const Slic3r::Polygons &polygons, ClipperLib::Paths* retval, const float delta1,
|
offset2(const Slic3r::Polygons &polygons, ClipperLib::Paths* retval, const float delta1,
|
||||||
const float delta2, const double scale, const ClipperLib::JoinType joinType, const double miterLimit)
|
const float delta2, const double scale, const ClipperLib::JoinType joinType, const double miterLimit)
|
||||||
|
@ -61,6 +61,9 @@ void offset(const Slic3r::Polygons &polygons, Slic3r::ExPolygons* retval, const
|
|||||||
Slic3r::ExPolygons offset_ex(const Slic3r::Polygons &polygons, const float delta,
|
Slic3r::ExPolygons offset_ex(const Slic3r::Polygons &polygons, const float delta,
|
||||||
double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
||||||
double miterLimit = 3);
|
double miterLimit = 3);
|
||||||
|
Slic3r::ExPolygons offset_ex(const Slic3r::ExPolygons &expolygons, const float delta,
|
||||||
|
double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
||||||
|
double miterLimit = 3);
|
||||||
|
|
||||||
void offset2(const Slic3r::Polygons &polygons, ClipperLib::Paths* retval, const float delta1,
|
void offset2(const Slic3r::Polygons &polygons, ClipperLib::Paths* retval, const float delta1,
|
||||||
const float delta2, double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
const float delta2, double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
||||||
|
@ -1,6 +1,14 @@
|
|||||||
#include "Config.hpp"
|
#include "Config.hpp"
|
||||||
#include <stdlib.h> // for setenv()
|
#include <stdlib.h> // for setenv()
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <ctime>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <boost/algorithm/string/erase.hpp>
|
||||||
|
#include <boost/algorithm/string/predicate.hpp>
|
||||||
|
#include <boost/foreach.hpp>
|
||||||
|
#include <boost/property_tree/ini_parser.hpp>
|
||||||
|
#include <boost/property_tree/ptree.hpp>
|
||||||
|
|
||||||
#if defined(_WIN32) && !defined(setenv) && defined(_putenv_s)
|
#if defined(_WIN32) && !defined(setenv) && defined(_putenv_s)
|
||||||
#define setenv(k, v, o) _putenv_s(k, v)
|
#define setenv(k, v, o) _putenv_s(k, v)
|
||||||
@ -20,12 +28,22 @@ operator!= (const ConfigOption &a, const ConfigOption &b)
|
|||||||
return !(a == b);
|
return !(a == b);
|
||||||
}
|
}
|
||||||
|
|
||||||
ConfigDef::~ConfigDef()
|
ConfigOptionDef::ConfigOptionDef(const ConfigOptionDef &other)
|
||||||
|
: type(other.type), default_value(NULL),
|
||||||
|
gui_type(other.gui_type), gui_flags(other.gui_flags), label(other.label),
|
||||||
|
full_label(other.full_label), category(other.category), tooltip(other.tooltip),
|
||||||
|
sidetext(other.sidetext), cli(other.cli), ratio_over(other.ratio_over),
|
||||||
|
multiline(other.multiline), full_width(other.full_width), readonly(other.readonly),
|
||||||
|
height(other.height), width(other.width), min(other.min), max(other.max)
|
||||||
{
|
{
|
||||||
for (t_optiondef_map::iterator it = this->options.begin(); it != this->options.end(); ++it) {
|
if (other.default_value != NULL)
|
||||||
if (it->second.default_value != NULL)
|
this->default_value = other.default_value->clone();
|
||||||
delete it->second.default_value;
|
}
|
||||||
}
|
|
||||||
|
ConfigOptionDef::~ConfigOptionDef()
|
||||||
|
{
|
||||||
|
if (this->default_value != NULL)
|
||||||
|
delete this->default_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
ConfigOptionDef*
|
ConfigOptionDef*
|
||||||
@ -43,6 +61,12 @@ ConfigDef::get(const t_config_option_key &opt_key) const
|
|||||||
return &const_cast<ConfigDef*>(this)->options[opt_key];
|
return &const_cast<ConfigDef*>(this)->options[opt_key];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ConfigDef::merge(const ConfigDef &other)
|
||||||
|
{
|
||||||
|
this->options.insert(other.options.begin(), other.options.end());
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ConfigBase::has(const t_config_option_key &opt_key) {
|
ConfigBase::has(const t_config_option_key &opt_key) {
|
||||||
return (this->option(opt_key, false) != NULL);
|
return (this->option(opt_key, false) != NULL);
|
||||||
@ -98,7 +122,7 @@ ConfigBase::serialize(const t_config_option_key &opt_key) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ConfigBase::set_deserialize(const t_config_option_key &opt_key, std::string str) {
|
ConfigBase::set_deserialize(const t_config_option_key &opt_key, std::string str, bool append) {
|
||||||
const ConfigOptionDef* optdef = this->def->get(opt_key);
|
const ConfigOptionDef* optdef = this->def->get(opt_key);
|
||||||
if (optdef == NULL) throw "Calling set_deserialize() on unknown option";
|
if (optdef == NULL) throw "Calling set_deserialize() on unknown option";
|
||||||
if (!optdef->shortcut.empty()) {
|
if (!optdef->shortcut.empty()) {
|
||||||
@ -110,7 +134,7 @@ ConfigBase::set_deserialize(const t_config_option_key &opt_key, std::string str)
|
|||||||
|
|
||||||
ConfigOption* opt = this->option(opt_key, true);
|
ConfigOption* opt = this->option(opt_key, true);
|
||||||
assert(opt != NULL);
|
assert(opt != NULL);
|
||||||
return opt->deserialize(str);
|
return opt->deserialize(str, append);
|
||||||
}
|
}
|
||||||
|
|
||||||
double
|
double
|
||||||
@ -171,6 +195,38 @@ ConfigBase::option(const t_config_option_key &opt_key, bool create) {
|
|||||||
return this->optptr(opt_key, create);
|
return this->optptr(opt_key, create);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ConfigBase::load(const std::string &file)
|
||||||
|
{
|
||||||
|
namespace pt = boost::property_tree;
|
||||||
|
pt::ptree tree;
|
||||||
|
pt::read_ini(file, tree);
|
||||||
|
BOOST_FOREACH(const pt::ptree::value_type &v, tree) {
|
||||||
|
this->set_deserialize(v.first.c_str(), v.second.get_value<std::string>().c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ConfigBase::save(const std::string &file) const
|
||||||
|
{
|
||||||
|
using namespace std;
|
||||||
|
ofstream c;
|
||||||
|
c.open(file.c_str(), ios::out | ios::trunc);
|
||||||
|
|
||||||
|
{
|
||||||
|
time_t now;
|
||||||
|
time(&now);
|
||||||
|
char buf[sizeof "0000-00-00 00:00:00"];
|
||||||
|
strftime(buf, sizeof buf, "%F %T", gmtime(&now));
|
||||||
|
c << "# generated by Slic3r " << SLIC3R_VERSION << " on " << buf << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
t_config_option_keys my_keys = this->keys();
|
||||||
|
for (t_config_option_keys::const_iterator opt_key = my_keys.begin(); opt_key != my_keys.end(); ++opt_key)
|
||||||
|
c << *opt_key << " = " << this->serialize(*opt_key) << endl;
|
||||||
|
c.close();
|
||||||
|
}
|
||||||
|
|
||||||
DynamicConfig& DynamicConfig::operator= (DynamicConfig other)
|
DynamicConfig& DynamicConfig::operator= (DynamicConfig other)
|
||||||
{
|
{
|
||||||
this->swap(other);
|
this->swap(other);
|
||||||
@ -200,9 +256,11 @@ DynamicConfig::optptr(const t_config_option_key &opt_key, bool create) {
|
|||||||
if (this->options.count(opt_key) == 0) {
|
if (this->options.count(opt_key) == 0) {
|
||||||
if (create) {
|
if (create) {
|
||||||
const ConfigOptionDef* optdef = this->def->get(opt_key);
|
const ConfigOptionDef* optdef = this->def->get(opt_key);
|
||||||
assert(optdef != NULL);
|
if (optdef == NULL) return NULL;
|
||||||
ConfigOption* opt;
|
ConfigOption* opt;
|
||||||
if (optdef->type == coFloat) {
|
if (optdef->default_value != NULL) {
|
||||||
|
opt = optdef->default_value->clone();
|
||||||
|
} else if (optdef->type == coFloat) {
|
||||||
opt = new ConfigOptionFloat ();
|
opt = new ConfigOptionFloat ();
|
||||||
} else if (optdef->type == coFloats) {
|
} else if (optdef->type == coFloats) {
|
||||||
opt = new ConfigOptionFloats ();
|
opt = new ConfigOptionFloats ();
|
||||||
@ -265,6 +323,58 @@ DynamicConfig::erase(const t_config_option_key &opt_key) {
|
|||||||
this->options.erase(opt_key);
|
this->options.erase(opt_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
DynamicConfig::read_cli(const int argc, const char** argv, t_config_option_keys* extra)
|
||||||
|
{
|
||||||
|
bool parse_options = true;
|
||||||
|
for (int i = 1; i < argc; ++i) {
|
||||||
|
std::string token = argv[i];
|
||||||
|
|
||||||
|
if (token == "--") {
|
||||||
|
// stop parsing tokens as options
|
||||||
|
parse_options = false;
|
||||||
|
} else if (parse_options && boost::starts_with(token, "--")) {
|
||||||
|
boost::algorithm::erase_head(token, 2);
|
||||||
|
// TODO: handle --key=value
|
||||||
|
|
||||||
|
// look for the option def
|
||||||
|
t_config_option_key opt_key;
|
||||||
|
const ConfigOptionDef* optdef;
|
||||||
|
for (t_optiondef_map::const_iterator oit = this->def->options.begin();
|
||||||
|
oit != this->def->options.end(); ++oit) {
|
||||||
|
optdef = &oit->second;
|
||||||
|
|
||||||
|
if (optdef->cli == token
|
||||||
|
|| optdef->cli == token + '!'
|
||||||
|
|| boost::starts_with(optdef->cli, token + "=")) {
|
||||||
|
opt_key = oit->first;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opt_key.empty()) {
|
||||||
|
printf("Warning: unknown option --%s\n", token.c_str());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ConfigOptionBool* opt = this->opt<ConfigOptionBool>(opt_key, true)) {
|
||||||
|
opt->value = !boost::starts_with(token, "no-");
|
||||||
|
} else if (ConfigOptionBools* opt = this->opt<ConfigOptionBools>(opt_key, true)) {
|
||||||
|
opt->values.push_back(!boost::starts_with(token, "no-"));
|
||||||
|
} else {
|
||||||
|
// we expect one more token carrying the value
|
||||||
|
if (i == (argc-1)) {
|
||||||
|
printf("No value supplied for --%s\n", token.c_str());
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
this->set_deserialize(opt_key, argv[++i], true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
extra->push_back(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
StaticConfig::set_defaults()
|
StaticConfig::set_defaults()
|
||||||
{
|
{
|
||||||
|
@ -20,13 +20,15 @@ typedef std::vector<std::string> t_config_option_keys;
|
|||||||
class ConfigOption {
|
class ConfigOption {
|
||||||
public:
|
public:
|
||||||
virtual ~ConfigOption() {};
|
virtual ~ConfigOption() {};
|
||||||
|
virtual ConfigOption* clone() const = 0;
|
||||||
virtual std::string serialize() const = 0;
|
virtual std::string serialize() const = 0;
|
||||||
virtual bool deserialize(std::string str) = 0;
|
virtual bool deserialize(std::string str, bool append = false) = 0;
|
||||||
virtual void set(const ConfigOption &option) = 0;
|
virtual void set(const ConfigOption &option) = 0;
|
||||||
virtual int getInt() const { return 0; };
|
virtual int getInt() const { return 0; };
|
||||||
virtual double getFloat() const { return 0; };
|
virtual double getFloat() const { return 0; };
|
||||||
virtual bool getBool() const { return false; };
|
virtual bool getBool() const { return false; };
|
||||||
virtual void setInt(int val) {};
|
virtual void setInt(int val) {};
|
||||||
|
virtual std::string getString() const { return ""; };
|
||||||
friend bool operator== (const ConfigOption &a, const ConfigOption &b);
|
friend bool operator== (const ConfigOption &a, const ConfigOption &b);
|
||||||
friend bool operator!= (const ConfigOption &a, const ConfigOption &b);
|
friend bool operator!= (const ConfigOption &a, const ConfigOption &b);
|
||||||
};
|
};
|
||||||
@ -54,8 +56,10 @@ template <class T>
|
|||||||
class ConfigOptionVector : public ConfigOptionVectorBase
|
class ConfigOptionVector : public ConfigOptionVectorBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual ~ConfigOptionVector() {};
|
|
||||||
std::vector<T> values;
|
std::vector<T> values;
|
||||||
|
ConfigOptionVector() {};
|
||||||
|
ConfigOptionVector(const std::vector<T> _values) : values(_values) {};
|
||||||
|
virtual ~ConfigOptionVector() {};
|
||||||
|
|
||||||
void set(const ConfigOption &option) {
|
void set(const ConfigOption &option) {
|
||||||
const ConfigOptionVector<T>* other = dynamic_cast< const ConfigOptionVector<T>* >(&option);
|
const ConfigOptionVector<T>* other = dynamic_cast< const ConfigOptionVector<T>* >(&option);
|
||||||
@ -76,6 +80,7 @@ class ConfigOptionFloat : public ConfigOptionSingle<double>
|
|||||||
public:
|
public:
|
||||||
ConfigOptionFloat() : ConfigOptionSingle<double>(0) {};
|
ConfigOptionFloat() : ConfigOptionSingle<double>(0) {};
|
||||||
ConfigOptionFloat(double _value) : ConfigOptionSingle<double>(_value) {};
|
ConfigOptionFloat(double _value) : ConfigOptionSingle<double>(_value) {};
|
||||||
|
ConfigOptionFloat* clone() const { return new ConfigOptionFloat(this->value); };
|
||||||
|
|
||||||
double getFloat() const { return this->value; };
|
double getFloat() const { return this->value; };
|
||||||
|
|
||||||
@ -85,7 +90,7 @@ class ConfigOptionFloat : public ConfigOptionSingle<double>
|
|||||||
return ss.str();
|
return ss.str();
|
||||||
};
|
};
|
||||||
|
|
||||||
bool deserialize(std::string str) {
|
bool deserialize(std::string str, bool append = false) {
|
||||||
std::istringstream iss(str);
|
std::istringstream iss(str);
|
||||||
iss >> this->value;
|
iss >> this->value;
|
||||||
return !iss.fail();
|
return !iss.fail();
|
||||||
@ -95,6 +100,9 @@ class ConfigOptionFloat : public ConfigOptionSingle<double>
|
|||||||
class ConfigOptionFloats : public ConfigOptionVector<double>
|
class ConfigOptionFloats : public ConfigOptionVector<double>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
ConfigOptionFloats() {};
|
||||||
|
ConfigOptionFloats(const std::vector<double> _values) : ConfigOptionVector<double>(_values) {};
|
||||||
|
ConfigOptionFloats* clone() const { return new ConfigOptionFloats(this->values); };
|
||||||
|
|
||||||
std::string serialize() const {
|
std::string serialize() const {
|
||||||
std::ostringstream ss;
|
std::ostringstream ss;
|
||||||
@ -115,8 +123,8 @@ class ConfigOptionFloats : public ConfigOptionVector<double>
|
|||||||
return vv;
|
return vv;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool deserialize(std::string str) {
|
bool deserialize(std::string str, bool append = false) {
|
||||||
this->values.clear();
|
if (!append) this->values.clear();
|
||||||
std::istringstream is(str);
|
std::istringstream is(str);
|
||||||
std::string item_str;
|
std::string item_str;
|
||||||
while (std::getline(is, item_str, ',')) {
|
while (std::getline(is, item_str, ',')) {
|
||||||
@ -134,6 +142,7 @@ class ConfigOptionInt : public ConfigOptionSingle<int>
|
|||||||
public:
|
public:
|
||||||
ConfigOptionInt() : ConfigOptionSingle<int>(0) {};
|
ConfigOptionInt() : ConfigOptionSingle<int>(0) {};
|
||||||
ConfigOptionInt(double _value) : ConfigOptionSingle<int>(_value) {};
|
ConfigOptionInt(double _value) : ConfigOptionSingle<int>(_value) {};
|
||||||
|
ConfigOptionInt* clone() const { return new ConfigOptionInt(this->value); };
|
||||||
|
|
||||||
int getInt() const { return this->value; };
|
int getInt() const { return this->value; };
|
||||||
void setInt(int val) { this->value = val; };
|
void setInt(int val) { this->value = val; };
|
||||||
@ -144,7 +153,7 @@ class ConfigOptionInt : public ConfigOptionSingle<int>
|
|||||||
return ss.str();
|
return ss.str();
|
||||||
};
|
};
|
||||||
|
|
||||||
bool deserialize(std::string str) {
|
bool deserialize(std::string str, bool append = false) {
|
||||||
std::istringstream iss(str);
|
std::istringstream iss(str);
|
||||||
iss >> this->value;
|
iss >> this->value;
|
||||||
return !iss.fail();
|
return !iss.fail();
|
||||||
@ -154,6 +163,9 @@ class ConfigOptionInt : public ConfigOptionSingle<int>
|
|||||||
class ConfigOptionInts : public ConfigOptionVector<int>
|
class ConfigOptionInts : public ConfigOptionVector<int>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
ConfigOptionInts() {};
|
||||||
|
ConfigOptionInts(const std::vector<int> _values) : ConfigOptionVector<int>(_values) {};
|
||||||
|
ConfigOptionInts* clone() const { return new ConfigOptionInts(this->values); };
|
||||||
|
|
||||||
std::string serialize() const {
|
std::string serialize() const {
|
||||||
std::ostringstream ss;
|
std::ostringstream ss;
|
||||||
@ -174,8 +186,8 @@ class ConfigOptionInts : public ConfigOptionVector<int>
|
|||||||
return vv;
|
return vv;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool deserialize(std::string str) {
|
bool deserialize(std::string str, bool append = false) {
|
||||||
this->values.clear();
|
if (!append) this->values.clear();
|
||||||
std::istringstream is(str);
|
std::istringstream is(str);
|
||||||
std::string item_str;
|
std::string item_str;
|
||||||
while (std::getline(is, item_str, ',')) {
|
while (std::getline(is, item_str, ',')) {
|
||||||
@ -193,6 +205,9 @@ class ConfigOptionString : public ConfigOptionSingle<std::string>
|
|||||||
public:
|
public:
|
||||||
ConfigOptionString() : ConfigOptionSingle<std::string>("") {};
|
ConfigOptionString() : ConfigOptionSingle<std::string>("") {};
|
||||||
ConfigOptionString(std::string _value) : ConfigOptionSingle<std::string>(_value) {};
|
ConfigOptionString(std::string _value) : ConfigOptionSingle<std::string>(_value) {};
|
||||||
|
ConfigOptionString* clone() const { return new ConfigOptionString(this->value); };
|
||||||
|
|
||||||
|
std::string getString() const { return this->value; };
|
||||||
|
|
||||||
std::string serialize() const {
|
std::string serialize() const {
|
||||||
std::string str = this->value;
|
std::string str = this->value;
|
||||||
@ -207,7 +222,7 @@ class ConfigOptionString : public ConfigOptionSingle<std::string>
|
|||||||
return str;
|
return str;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool deserialize(std::string str) {
|
bool deserialize(std::string str, bool append = false) {
|
||||||
// s/\\n/\n/g
|
// s/\\n/\n/g
|
||||||
size_t pos = 0;
|
size_t pos = 0;
|
||||||
while ((pos = str.find("\\n", pos)) != std::string::npos) {
|
while ((pos = str.find("\\n", pos)) != std::string::npos) {
|
||||||
@ -224,6 +239,9 @@ class ConfigOptionString : public ConfigOptionSingle<std::string>
|
|||||||
class ConfigOptionStrings : public ConfigOptionVector<std::string>
|
class ConfigOptionStrings : public ConfigOptionVector<std::string>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
ConfigOptionStrings() {};
|
||||||
|
ConfigOptionStrings(const std::vector<std::string> _values) : ConfigOptionVector<std::string>(_values) {};
|
||||||
|
ConfigOptionStrings* clone() const { return new ConfigOptionStrings(this->values); };
|
||||||
|
|
||||||
std::string serialize() const {
|
std::string serialize() const {
|
||||||
std::ostringstream ss;
|
std::ostringstream ss;
|
||||||
@ -238,8 +256,8 @@ class ConfigOptionStrings : public ConfigOptionVector<std::string>
|
|||||||
return this->values;
|
return this->values;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool deserialize(std::string str) {
|
bool deserialize(std::string str, bool append = false) {
|
||||||
this->values.clear();
|
if (!append) this->values.clear();
|
||||||
std::istringstream is(str);
|
std::istringstream is(str);
|
||||||
std::string item_str;
|
std::string item_str;
|
||||||
while (std::getline(is, item_str, ';')) {
|
while (std::getline(is, item_str, ';')) {
|
||||||
@ -254,6 +272,7 @@ class ConfigOptionPercent : public ConfigOptionFloat
|
|||||||
public:
|
public:
|
||||||
ConfigOptionPercent() : ConfigOptionFloat(0) {};
|
ConfigOptionPercent() : ConfigOptionFloat(0) {};
|
||||||
ConfigOptionPercent(double _value) : ConfigOptionFloat(_value) {};
|
ConfigOptionPercent(double _value) : ConfigOptionFloat(_value) {};
|
||||||
|
ConfigOptionPercent* clone() const { return new ConfigOptionPercent(this->value); };
|
||||||
|
|
||||||
double get_abs_value(double ratio_over) const {
|
double get_abs_value(double ratio_over) const {
|
||||||
return ratio_over * this->value / 100;
|
return ratio_over * this->value / 100;
|
||||||
@ -267,7 +286,7 @@ class ConfigOptionPercent : public ConfigOptionFloat
|
|||||||
return s;
|
return s;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool deserialize(std::string str) {
|
bool deserialize(std::string str, bool append = false) {
|
||||||
// don't try to parse the trailing % since it's optional
|
// don't try to parse the trailing % since it's optional
|
||||||
std::istringstream iss(str);
|
std::istringstream iss(str);
|
||||||
iss >> this->value;
|
iss >> this->value;
|
||||||
@ -282,6 +301,7 @@ class ConfigOptionFloatOrPercent : public ConfigOptionPercent
|
|||||||
ConfigOptionFloatOrPercent() : ConfigOptionPercent(0), percent(false) {};
|
ConfigOptionFloatOrPercent() : ConfigOptionPercent(0), percent(false) {};
|
||||||
ConfigOptionFloatOrPercent(double _value, bool _percent)
|
ConfigOptionFloatOrPercent(double _value, bool _percent)
|
||||||
: ConfigOptionPercent(_value), percent(_percent) {};
|
: ConfigOptionPercent(_value), percent(_percent) {};
|
||||||
|
ConfigOptionFloatOrPercent* clone() const { return new ConfigOptionFloatOrPercent(this->value, this->percent); };
|
||||||
|
|
||||||
void set(const ConfigOption &option) {
|
void set(const ConfigOption &option) {
|
||||||
const ConfigOptionFloatOrPercent* other = dynamic_cast< const ConfigOptionFloatOrPercent* >(&option);
|
const ConfigOptionFloatOrPercent* other = dynamic_cast< const ConfigOptionFloatOrPercent* >(&option);
|
||||||
@ -307,7 +327,7 @@ class ConfigOptionFloatOrPercent : public ConfigOptionPercent
|
|||||||
return s;
|
return s;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool deserialize(std::string str) {
|
bool deserialize(std::string str, bool append = false) {
|
||||||
this->percent = str.find_first_of("%") != std::string::npos;
|
this->percent = str.find_first_of("%") != std::string::npos;
|
||||||
std::istringstream iss(str);
|
std::istringstream iss(str);
|
||||||
iss >> this->value;
|
iss >> this->value;
|
||||||
@ -320,6 +340,7 @@ class ConfigOptionPoint : public ConfigOptionSingle<Pointf>
|
|||||||
public:
|
public:
|
||||||
ConfigOptionPoint() : ConfigOptionSingle<Pointf>(Pointf(0,0)) {};
|
ConfigOptionPoint() : ConfigOptionSingle<Pointf>(Pointf(0,0)) {};
|
||||||
ConfigOptionPoint(Pointf _value) : ConfigOptionSingle<Pointf>(_value) {};
|
ConfigOptionPoint(Pointf _value) : ConfigOptionSingle<Pointf>(_value) {};
|
||||||
|
ConfigOptionPoint* clone() const { return new ConfigOptionPoint(this->value); };
|
||||||
|
|
||||||
std::string serialize() const {
|
std::string serialize() const {
|
||||||
std::ostringstream ss;
|
std::ostringstream ss;
|
||||||
@ -329,7 +350,7 @@ class ConfigOptionPoint : public ConfigOptionSingle<Pointf>
|
|||||||
return ss.str();
|
return ss.str();
|
||||||
};
|
};
|
||||||
|
|
||||||
bool deserialize(std::string str) {
|
bool deserialize(std::string str, bool append = false) {
|
||||||
std::istringstream iss(str);
|
std::istringstream iss(str);
|
||||||
iss >> this->value.x;
|
iss >> this->value.x;
|
||||||
iss.ignore(std::numeric_limits<std::streamsize>::max(), ',');
|
iss.ignore(std::numeric_limits<std::streamsize>::max(), ',');
|
||||||
@ -342,6 +363,9 @@ class ConfigOptionPoint : public ConfigOptionSingle<Pointf>
|
|||||||
class ConfigOptionPoints : public ConfigOptionVector<Pointf>
|
class ConfigOptionPoints : public ConfigOptionVector<Pointf>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
ConfigOptionPoints() {};
|
||||||
|
ConfigOptionPoints(const std::vector<Pointf> _values) : ConfigOptionVector<Pointf>(_values) {};
|
||||||
|
ConfigOptionPoints* clone() const { return new ConfigOptionPoints(this->values); };
|
||||||
|
|
||||||
std::string serialize() const {
|
std::string serialize() const {
|
||||||
std::ostringstream ss;
|
std::ostringstream ss;
|
||||||
@ -364,8 +388,8 @@ class ConfigOptionPoints : public ConfigOptionVector<Pointf>
|
|||||||
return vv;
|
return vv;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool deserialize(std::string str) {
|
bool deserialize(std::string str, bool append = false) {
|
||||||
this->values.clear();
|
if (!append) this->values.clear();
|
||||||
std::istringstream is(str);
|
std::istringstream is(str);
|
||||||
std::string point_str;
|
std::string point_str;
|
||||||
while (std::getline(is, point_str, ',')) {
|
while (std::getline(is, point_str, ',')) {
|
||||||
@ -389,6 +413,7 @@ class ConfigOptionBool : public ConfigOptionSingle<bool>
|
|||||||
public:
|
public:
|
||||||
ConfigOptionBool() : ConfigOptionSingle<bool>(false) {};
|
ConfigOptionBool() : ConfigOptionSingle<bool>(false) {};
|
||||||
ConfigOptionBool(bool _value) : ConfigOptionSingle<bool>(_value) {};
|
ConfigOptionBool(bool _value) : ConfigOptionSingle<bool>(_value) {};
|
||||||
|
ConfigOptionBool* clone() const { return new ConfigOptionBool(this->value); };
|
||||||
|
|
||||||
bool getBool() const { return this->value; };
|
bool getBool() const { return this->value; };
|
||||||
|
|
||||||
@ -396,7 +421,7 @@ class ConfigOptionBool : public ConfigOptionSingle<bool>
|
|||||||
return std::string(this->value ? "1" : "0");
|
return std::string(this->value ? "1" : "0");
|
||||||
};
|
};
|
||||||
|
|
||||||
bool deserialize(std::string str) {
|
bool deserialize(std::string str, bool append = false) {
|
||||||
this->value = (str.compare("1") == 0);
|
this->value = (str.compare("1") == 0);
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
@ -405,6 +430,9 @@ class ConfigOptionBool : public ConfigOptionSingle<bool>
|
|||||||
class ConfigOptionBools : public ConfigOptionVector<bool>
|
class ConfigOptionBools : public ConfigOptionVector<bool>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
ConfigOptionBools() {};
|
||||||
|
ConfigOptionBools(const std::vector<bool> _values) : ConfigOptionVector<bool>(_values) {};
|
||||||
|
ConfigOptionBools* clone() const { return new ConfigOptionBools(this->values); };
|
||||||
|
|
||||||
std::string serialize() const {
|
std::string serialize() const {
|
||||||
std::ostringstream ss;
|
std::ostringstream ss;
|
||||||
@ -425,8 +453,8 @@ class ConfigOptionBools : public ConfigOptionVector<bool>
|
|||||||
return vv;
|
return vv;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool deserialize(std::string str) {
|
bool deserialize(std::string str, bool append = false) {
|
||||||
this->values.clear();
|
if (!append) this->values.clear();
|
||||||
std::istringstream is(str);
|
std::istringstream is(str);
|
||||||
std::string item_str;
|
std::string item_str;
|
||||||
while (std::getline(is, item_str, ',')) {
|
while (std::getline(is, item_str, ',')) {
|
||||||
@ -445,6 +473,7 @@ class ConfigOptionEnum : public ConfigOptionSingle<T>
|
|||||||
// by default, use the first value (0) of the T enum type
|
// by default, use the first value (0) of the T enum type
|
||||||
ConfigOptionEnum() : ConfigOptionSingle<T>(static_cast<T>(0)) {};
|
ConfigOptionEnum() : ConfigOptionSingle<T>(static_cast<T>(0)) {};
|
||||||
ConfigOptionEnum(T _value) : ConfigOptionSingle<T>(_value) {};
|
ConfigOptionEnum(T _value) : ConfigOptionSingle<T>(_value) {};
|
||||||
|
ConfigOptionEnum<T>* clone() const { return new ConfigOptionEnum<T>(this->value); };
|
||||||
|
|
||||||
std::string serialize() const {
|
std::string serialize() const {
|
||||||
t_config_enum_values enum_keys_map = ConfigOptionEnum<T>::get_enum_values();
|
t_config_enum_values enum_keys_map = ConfigOptionEnum<T>::get_enum_values();
|
||||||
@ -454,7 +483,7 @@ class ConfigOptionEnum : public ConfigOptionSingle<T>
|
|||||||
return "";
|
return "";
|
||||||
};
|
};
|
||||||
|
|
||||||
bool deserialize(std::string str) {
|
bool deserialize(std::string str, bool append = false) {
|
||||||
t_config_enum_values enum_keys_map = ConfigOptionEnum<T>::get_enum_values();
|
t_config_enum_values enum_keys_map = ConfigOptionEnum<T>::get_enum_values();
|
||||||
if (enum_keys_map.count(str) == 0) return false;
|
if (enum_keys_map.count(str) == 0) return false;
|
||||||
this->value = static_cast<T>(enum_keys_map[str]);
|
this->value = static_cast<T>(enum_keys_map[str]);
|
||||||
@ -478,7 +507,7 @@ class ConfigOptionEnumGeneric : public ConfigOptionInt
|
|||||||
return "";
|
return "";
|
||||||
};
|
};
|
||||||
|
|
||||||
bool deserialize(std::string str) {
|
bool deserialize(std::string str, bool append = false) {
|
||||||
if (this->keys_map->count(str) == 0) return false;
|
if (this->keys_map->count(str) == 0) return false;
|
||||||
this->value = (*const_cast<t_config_enum_values*>(this->keys_map))[str];
|
this->value = (*const_cast<t_config_enum_values*>(this->keys_map))[str];
|
||||||
return true;
|
return true;
|
||||||
@ -532,6 +561,11 @@ class ConfigOptionDef
|
|||||||
ConfigOptionDef() : type(coNone), default_value(NULL),
|
ConfigOptionDef() : type(coNone), default_value(NULL),
|
||||||
multiline(false), full_width(false), readonly(false),
|
multiline(false), full_width(false), readonly(false),
|
||||||
height(-1), width(-1), min(INT_MIN), max(INT_MAX) {};
|
height(-1), width(-1), min(INT_MIN), max(INT_MAX) {};
|
||||||
|
ConfigOptionDef(const ConfigOptionDef &other);
|
||||||
|
~ConfigOptionDef();
|
||||||
|
|
||||||
|
private:
|
||||||
|
ConfigOptionDef& operator= (ConfigOptionDef other);
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::map<t_config_option_key,ConfigOptionDef> t_optiondef_map;
|
typedef std::map<t_config_option_key,ConfigOptionDef> t_optiondef_map;
|
||||||
@ -540,9 +574,9 @@ class ConfigDef
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
t_optiondef_map options;
|
t_optiondef_map options;
|
||||||
~ConfigDef();
|
|
||||||
ConfigOptionDef* add(const t_config_option_key &opt_key, ConfigOptionType type);
|
ConfigOptionDef* add(const t_config_option_key &opt_key, ConfigOptionType type);
|
||||||
const ConfigOptionDef* get(const t_config_option_key &opt_key) const;
|
const ConfigOptionDef* get(const t_config_option_key &opt_key) const;
|
||||||
|
void merge(const ConfigDef &other);
|
||||||
};
|
};
|
||||||
|
|
||||||
class ConfigBase
|
class ConfigBase
|
||||||
@ -551,6 +585,7 @@ class ConfigBase
|
|||||||
const ConfigDef* def;
|
const ConfigDef* def;
|
||||||
|
|
||||||
ConfigBase() : def(NULL) {};
|
ConfigBase() : def(NULL) {};
|
||||||
|
ConfigBase(const ConfigDef* def) : def(def) {};
|
||||||
virtual ~ConfigBase() {};
|
virtual ~ConfigBase() {};
|
||||||
bool has(const t_config_option_key &opt_key);
|
bool has(const t_config_option_key &opt_key);
|
||||||
const ConfigOption* option(const t_config_option_key &opt_key) const;
|
const ConfigOption* option(const t_config_option_key &opt_key) const;
|
||||||
@ -561,16 +596,19 @@ class ConfigBase
|
|||||||
bool equals(ConfigBase &other);
|
bool equals(ConfigBase &other);
|
||||||
t_config_option_keys diff(ConfigBase &other);
|
t_config_option_keys diff(ConfigBase &other);
|
||||||
std::string serialize(const t_config_option_key &opt_key) const;
|
std::string serialize(const t_config_option_key &opt_key) const;
|
||||||
bool set_deserialize(const t_config_option_key &opt_key, std::string str);
|
bool set_deserialize(const t_config_option_key &opt_key, std::string str, bool append = false);
|
||||||
double get_abs_value(const t_config_option_key &opt_key);
|
double get_abs_value(const t_config_option_key &opt_key);
|
||||||
double get_abs_value(const t_config_option_key &opt_key, double ratio_over);
|
double get_abs_value(const t_config_option_key &opt_key, double ratio_over);
|
||||||
void setenv_();
|
void setenv_();
|
||||||
|
void load(const std::string &file);
|
||||||
|
void save(const std::string &file) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DynamicConfig : public virtual ConfigBase
|
class DynamicConfig : public virtual ConfigBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
DynamicConfig() {};
|
DynamicConfig() {};
|
||||||
|
DynamicConfig(const ConfigDef* def) : ConfigBase(def) {};
|
||||||
DynamicConfig(const DynamicConfig& other);
|
DynamicConfig(const DynamicConfig& other);
|
||||||
DynamicConfig& operator= (DynamicConfig other);
|
DynamicConfig& operator= (DynamicConfig other);
|
||||||
void swap(DynamicConfig &other);
|
void swap(DynamicConfig &other);
|
||||||
@ -579,6 +617,7 @@ class DynamicConfig : public virtual ConfigBase
|
|||||||
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);
|
||||||
t_config_option_keys keys() const;
|
t_config_option_keys keys() const;
|
||||||
void erase(const t_config_option_key &opt_key);
|
void erase(const t_config_option_key &opt_key);
|
||||||
|
void read_cli(const int argc, const char **argv, t_config_option_keys* extra);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
typedef std::map<t_config_option_key,ConfigOption*> t_options_map;
|
typedef std::map<t_config_option_key,ConfigOption*> t_options_map;
|
||||||
|
@ -632,16 +632,11 @@ GCode::travel_to(const Point &point, ExtrusionRole role, std::string comment)
|
|||||||
|
|
||||||
// use G1 because we rely on paths being straight (G0 may make round paths)
|
// use G1 because we rely on paths being straight (G0 may make round paths)
|
||||||
Lines lines = travel.lines();
|
Lines lines = travel.lines();
|
||||||
double path_length = 0;
|
for (Lines::const_iterator line = lines.begin(); line != lines.end(); ++line)
|
||||||
for (Lines::const_iterator line = lines.begin(); line != lines.end(); ++line) {
|
|
||||||
const double line_length = line->length() * SCALING_FACTOR;
|
|
||||||
path_length += line_length;
|
|
||||||
|
|
||||||
gcode += this->writer.travel_to_xy(this->point_to_gcode(line->b), comment);
|
gcode += this->writer.travel_to_xy(this->point_to_gcode(line->b), comment);
|
||||||
}
|
|
||||||
|
|
||||||
if (this->config.cooling)
|
if (this->config.cooling)
|
||||||
this->elapsed_time += path_length / this->config.get_abs_value("travel_speed");
|
this->elapsed_time += travel.length() / this->config.get_abs_value("travel_speed");
|
||||||
|
|
||||||
return gcode;
|
return gcode;
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,26 @@
|
|||||||
#include "IO.hpp"
|
#include "IO.hpp"
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
namespace Slic3r { namespace IO {
|
namespace Slic3r { namespace IO {
|
||||||
|
|
||||||
bool
|
bool
|
||||||
STL::read_file(std::string input_file, Model* model)
|
STL::read(std::string input_file, TriangleMesh* mesh)
|
||||||
|
{
|
||||||
|
mesh->ReadSTLFile(input_file);
|
||||||
|
mesh->check_topology();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
STL::read(std::string input_file, Model* model)
|
||||||
{
|
{
|
||||||
// TODO: encode file name
|
// TODO: encode file name
|
||||||
// TODO: check that file exists
|
// TODO: check that file exists
|
||||||
|
|
||||||
TriangleMesh mesh;
|
TriangleMesh mesh;
|
||||||
mesh.ReadSTLFile(input_file);
|
if (!STL::read(input_file, &mesh)) return false;
|
||||||
mesh.repair();
|
|
||||||
|
|
||||||
if (mesh.facets_count() == 0)
|
if (mesh.facets_count() == 0)
|
||||||
throw std::runtime_error("This STL file couldn't be read because it's empty.");
|
throw std::runtime_error("This STL file couldn't be read because it's empty.");
|
||||||
@ -44,4 +53,30 @@ OBJ::write(TriangleMesh& mesh, std::string output_file)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
POV::write(TriangleMesh& mesh, std::string output_file)
|
||||||
|
{
|
||||||
|
TriangleMesh mesh2 = mesh;
|
||||||
|
mesh2.center_around_origin();
|
||||||
|
{
|
||||||
|
Sizef3 size = mesh2.bounding_box().size();
|
||||||
|
coordf_t maxdim = fmax(size.x, fmax(size.y, size.y));
|
||||||
|
mesh2.scale(10.0/maxdim);
|
||||||
|
}
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
ofstream pov;
|
||||||
|
pov.open(output_file.c_str(), ios::out | ios::trunc);
|
||||||
|
for (int i = 0; i < mesh2.stl.stats.number_of_facets; ++i) {
|
||||||
|
const stl_facet &f = mesh2.stl.facet_start[i];
|
||||||
|
pov << "triangle { ";
|
||||||
|
pov << "<" << f.vertex[0].x << "," << f.vertex[0].y << "," << f.vertex[0].z << ">,";
|
||||||
|
pov << "<" << f.vertex[1].x << "," << f.vertex[1].y << "," << f.vertex[1].z << ">,";
|
||||||
|
pov << "<" << f.vertex[2].x << "," << f.vertex[2].y << "," << f.vertex[2].z << ">";
|
||||||
|
pov << " }" << endl;
|
||||||
|
}
|
||||||
|
pov.close();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
} }
|
} }
|
||||||
|
@ -11,14 +11,21 @@ namespace Slic3r { namespace IO {
|
|||||||
class STL
|
class STL
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
bool read_file(std::string input_file, Model* model);
|
static bool read(std::string input_file, TriangleMesh* mesh);
|
||||||
bool write(TriangleMesh& mesh, std::string output_file, bool binary = true);
|
static bool read(std::string input_file, Model* model);
|
||||||
|
static bool write(TriangleMesh& mesh, std::string output_file, bool binary = true);
|
||||||
};
|
};
|
||||||
|
|
||||||
class OBJ
|
class OBJ
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
bool write(TriangleMesh& mesh, std::string output_file);
|
static bool write(TriangleMesh& mesh, std::string output_file);
|
||||||
|
};
|
||||||
|
|
||||||
|
class POV
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static bool write(TriangleMesh& mesh, std::string output_file);
|
||||||
};
|
};
|
||||||
|
|
||||||
} }
|
} }
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
#include "Model.hpp"
|
#include "Model.hpp"
|
||||||
#include "Geometry.hpp"
|
#include "Geometry.hpp"
|
||||||
|
#include <iostream>
|
||||||
|
#include "boost/filesystem.hpp"
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
@ -159,6 +161,13 @@ Model::bounding_box() const
|
|||||||
return bb;
|
return bb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Model::repair()
|
||||||
|
{
|
||||||
|
for (ModelObjectPtrs::const_iterator o = this->objects.begin(); o != this->objects.end(); ++o)
|
||||||
|
(*o)->repair();
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Model::center_instances_around_point(const Pointf &point)
|
Model::center_instances_around_point(const Pointf &point)
|
||||||
{
|
{
|
||||||
@ -311,6 +320,13 @@ Model::duplicate_objects_grid(size_t x, size_t y, coordf_t dist)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Model::print_info() const
|
||||||
|
{
|
||||||
|
for (ModelObjectPtrs::const_iterator o = this->objects.begin(); o != this->objects.end(); ++o)
|
||||||
|
(*o)->print_info();
|
||||||
|
}
|
||||||
|
|
||||||
ModelMaterial::ModelMaterial(Model *model) : model(model) {}
|
ModelMaterial::ModelMaterial(Model *model) : model(model) {}
|
||||||
ModelMaterial::ModelMaterial(Model *model, const ModelMaterial &other)
|
ModelMaterial::ModelMaterial(Model *model, const ModelMaterial &other)
|
||||||
: attributes(other.attributes), config(other.config), model(model)
|
: attributes(other.attributes), config(other.config), model(model)
|
||||||
@ -471,6 +487,13 @@ ModelObject::update_bounding_box()
|
|||||||
this->_bounding_box_valid = true;
|
this->_bounding_box_valid = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ModelObject::repair()
|
||||||
|
{
|
||||||
|
for (ModelVolumePtrs::const_iterator v = this->volumes.begin(); v != this->volumes.end(); ++v)
|
||||||
|
(*v)->mesh.repair();
|
||||||
|
}
|
||||||
|
|
||||||
// flattens all volumes and instances into a single mesh
|
// flattens all volumes and instances into a single mesh
|
||||||
TriangleMesh
|
TriangleMesh
|
||||||
ModelObject::mesh() const
|
ModelObject::mesh() const
|
||||||
@ -479,7 +502,7 @@ ModelObject::mesh() const
|
|||||||
TriangleMesh raw_mesh = this->raw_mesh();
|
TriangleMesh raw_mesh = this->raw_mesh();
|
||||||
|
|
||||||
for (ModelInstancePtrs::const_iterator i = this->instances.begin(); i != this->instances.end(); ++i) {
|
for (ModelInstancePtrs::const_iterator i = this->instances.begin(); i != this->instances.end(); ++i) {
|
||||||
TriangleMesh m = raw_mesh;
|
TriangleMesh m(raw_mesh);
|
||||||
(*i)->transform_mesh(&m);
|
(*i)->transform_mesh(&m);
|
||||||
mesh.merge(m);
|
mesh.merge(m);
|
||||||
}
|
}
|
||||||
@ -568,6 +591,12 @@ ModelObject::translate(coordf_t x, coordf_t y, coordf_t z)
|
|||||||
if (this->_bounding_box_valid) this->_bounding_box.translate(x, y, z);
|
if (this->_bounding_box_valid) this->_bounding_box.translate(x, y, z);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ModelObject::scale(float factor)
|
||||||
|
{
|
||||||
|
this->scale(Pointf3(factor, factor, factor));
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ModelObject::scale(const Pointf3 &versor)
|
ModelObject::scale(const Pointf3 &versor)
|
||||||
{
|
{
|
||||||
@ -701,6 +730,48 @@ ModelObject::split(ModelObjectPtrs* new_objects)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ModelObject::print_info() const
|
||||||
|
{
|
||||||
|
using namespace std;
|
||||||
|
cout << "[" << boost::filesystem::path(this->input_file).filename().string() << "]" << endl;
|
||||||
|
|
||||||
|
TriangleMesh mesh = this->raw_mesh();
|
||||||
|
mesh.check_topology();
|
||||||
|
BoundingBoxf3 bb = mesh.bounding_box();
|
||||||
|
Sizef3 size = bb.size();
|
||||||
|
cout << "size_x = " << size.x << endl;
|
||||||
|
cout << "size_y = " << size.y << endl;
|
||||||
|
cout << "size_z = " << size.z << endl;
|
||||||
|
cout << "min_x = " << bb.min.x << endl;
|
||||||
|
cout << "min_y = " << bb.min.y << endl;
|
||||||
|
cout << "min_z = " << bb.min.z << endl;
|
||||||
|
cout << "max_x = " << bb.max.x << endl;
|
||||||
|
cout << "max_y = " << bb.max.y << endl;
|
||||||
|
cout << "max_z = " << bb.max.z << endl;
|
||||||
|
cout << "number_of_facets = " << mesh.stl.stats.number_of_facets << endl;
|
||||||
|
cout << "manifold = " << (mesh.is_manifold() ? "yes" : "no") << endl;
|
||||||
|
|
||||||
|
mesh.repair(); // this calculates number_of_parts
|
||||||
|
if (mesh.needed_repair()) {
|
||||||
|
mesh.repair();
|
||||||
|
if (mesh.stl.stats.degenerate_facets > 0)
|
||||||
|
cout << "degenerate_facets = " << mesh.stl.stats.degenerate_facets << endl;
|
||||||
|
if (mesh.stl.stats.edges_fixed > 0)
|
||||||
|
cout << "edges_fixed = " << mesh.stl.stats.edges_fixed << endl;
|
||||||
|
if (mesh.stl.stats.facets_removed > 0)
|
||||||
|
cout << "facets_removed = " << mesh.stl.stats.facets_removed << endl;
|
||||||
|
if (mesh.stl.stats.facets_added > 0)
|
||||||
|
cout << "facets_added = " << mesh.stl.stats.facets_added << endl;
|
||||||
|
if (mesh.stl.stats.facets_reversed > 0)
|
||||||
|
cout << "facets_reversed = " << mesh.stl.stats.facets_reversed << endl;
|
||||||
|
if (mesh.stl.stats.backwards_edges > 0)
|
||||||
|
cout << "backwards_edges = " << mesh.stl.stats.backwards_edges << endl;
|
||||||
|
}
|
||||||
|
cout << "number_of_parts = " << mesh.stl.stats.number_of_parts << endl;
|
||||||
|
cout << "volume = " << mesh.volume() << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
ModelVolume::ModelVolume(ModelObject* object, const TriangleMesh &mesh)
|
ModelVolume::ModelVolume(ModelObject* object, const TriangleMesh &mesh)
|
||||||
: mesh(mesh), modifier(false), object(object)
|
: mesh(mesh), modifier(false), object(object)
|
||||||
@ -713,6 +784,21 @@ ModelVolume::ModelVolume(ModelObject* object, const ModelVolume &other)
|
|||||||
this->material_id(other.material_id());
|
this->material_id(other.material_id());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ModelVolume& ModelVolume::operator= (ModelVolume other)
|
||||||
|
{
|
||||||
|
this->swap(other);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ModelVolume::swap(ModelVolume &other)
|
||||||
|
{
|
||||||
|
std::swap(this->name, other.name);
|
||||||
|
std::swap(this->mesh, other.mesh);
|
||||||
|
std::swap(this->config, other.config);
|
||||||
|
std::swap(this->modifier, other.modifier);
|
||||||
|
}
|
||||||
|
|
||||||
t_model_material_id
|
t_model_material_id
|
||||||
ModelVolume::material_id() const
|
ModelVolume::material_id() const
|
||||||
{
|
{
|
||||||
@ -760,6 +846,20 @@ ModelInstance::ModelInstance(ModelObject *object, const ModelInstance &other)
|
|||||||
: rotation(other.rotation), scaling_factor(other.scaling_factor), offset(other.offset), object(object)
|
: rotation(other.rotation), scaling_factor(other.scaling_factor), offset(other.offset), object(object)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
ModelInstance& ModelInstance::operator= (ModelInstance other)
|
||||||
|
{
|
||||||
|
this->swap(other);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ModelInstance::swap(ModelInstance &other)
|
||||||
|
{
|
||||||
|
std::swap(this->rotation, other.rotation);
|
||||||
|
std::swap(this->scaling_factor, other.scaling_factor);
|
||||||
|
std::swap(this->offset, other.offset);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ModelInstance::transform_mesh(TriangleMesh* mesh, bool dont_translate) const
|
ModelInstance::transform_mesh(TriangleMesh* mesh, bool dont_translate) const
|
||||||
{
|
{
|
||||||
|
@ -51,6 +51,7 @@ class Model
|
|||||||
bool has_objects_with_no_instances() const;
|
bool has_objects_with_no_instances() const;
|
||||||
bool add_default_instances();
|
bool add_default_instances();
|
||||||
BoundingBoxf3 bounding_box() const;
|
BoundingBoxf3 bounding_box() const;
|
||||||
|
void repair();
|
||||||
void center_instances_around_point(const Pointf &point);
|
void center_instances_around_point(const Pointf &point);
|
||||||
void align_instances_to_origin();
|
void align_instances_to_origin();
|
||||||
void translate(coordf_t x, coordf_t y, coordf_t z);
|
void translate(coordf_t x, coordf_t y, coordf_t z);
|
||||||
@ -61,6 +62,7 @@ class Model
|
|||||||
void duplicate(size_t copies_num, coordf_t dist, const BoundingBoxf* bb = NULL);
|
void duplicate(size_t copies_num, coordf_t dist, const BoundingBoxf* bb = NULL);
|
||||||
void duplicate_objects(size_t copies_num, coordf_t dist, const BoundingBoxf* bb = NULL);
|
void duplicate_objects(size_t copies_num, coordf_t dist, const BoundingBoxf* bb = NULL);
|
||||||
void duplicate_objects_grid(size_t x, size_t y, coordf_t dist);
|
void duplicate_objects_grid(size_t x, size_t y, coordf_t dist);
|
||||||
|
void print_info() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ModelMaterial
|
class ModelMaterial
|
||||||
@ -117,6 +119,7 @@ class ModelObject
|
|||||||
BoundingBoxf3 bounding_box();
|
BoundingBoxf3 bounding_box();
|
||||||
void invalidate_bounding_box();
|
void invalidate_bounding_box();
|
||||||
|
|
||||||
|
void repair();
|
||||||
TriangleMesh mesh() const;
|
TriangleMesh mesh() const;
|
||||||
TriangleMesh raw_mesh() const;
|
TriangleMesh raw_mesh() const;
|
||||||
BoundingBoxf3 raw_bounding_box() const;
|
BoundingBoxf3 raw_bounding_box() const;
|
||||||
@ -124,6 +127,7 @@ class ModelObject
|
|||||||
void center_around_origin();
|
void center_around_origin();
|
||||||
void translate(const Vectorf3 &vector);
|
void translate(const Vectorf3 &vector);
|
||||||
void translate(coordf_t x, coordf_t y, coordf_t z);
|
void translate(coordf_t x, coordf_t y, coordf_t z);
|
||||||
|
void scale(float factor);
|
||||||
void scale(const Pointf3 &versor);
|
void scale(const Pointf3 &versor);
|
||||||
void rotate(float angle, const Axis &axis);
|
void rotate(float angle, const Axis &axis);
|
||||||
void mirror(const Axis &axis);
|
void mirror(const Axis &axis);
|
||||||
@ -133,6 +137,7 @@ class ModelObject
|
|||||||
void cut(coordf_t z, Model* model) const;
|
void cut(coordf_t z, Model* model) const;
|
||||||
void split(ModelObjectPtrs* new_objects);
|
void split(ModelObjectPtrs* new_objects);
|
||||||
void update_bounding_box(); // this is a private method but we expose it until we need to expose it via XS
|
void update_bounding_box(); // this is a private method but we expose it until we need to expose it via XS
|
||||||
|
void print_info() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Model* model;
|
Model* model;
|
||||||
@ -167,6 +172,8 @@ class ModelVolume
|
|||||||
|
|
||||||
ModelVolume(ModelObject *object, const TriangleMesh &mesh);
|
ModelVolume(ModelObject *object, const TriangleMesh &mesh);
|
||||||
ModelVolume(ModelObject *object, const ModelVolume &other);
|
ModelVolume(ModelObject *object, const ModelVolume &other);
|
||||||
|
ModelVolume& operator= (ModelVolume other);
|
||||||
|
void swap(ModelVolume &other);
|
||||||
};
|
};
|
||||||
|
|
||||||
class ModelInstance
|
class ModelInstance
|
||||||
@ -186,6 +193,8 @@ class ModelInstance
|
|||||||
|
|
||||||
ModelInstance(ModelObject *object);
|
ModelInstance(ModelObject *object);
|
||||||
ModelInstance(ModelObject *object, const ModelInstance &other);
|
ModelInstance(ModelObject *object, const ModelInstance &other);
|
||||||
|
ModelInstance& operator= (ModelInstance other);
|
||||||
|
void swap(ModelInstance &other);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -142,7 +142,7 @@ MotionPlanner::shortest_path(const Point &from, const Point &to)
|
|||||||
{
|
{
|
||||||
// grow our environment slightly in order for simplify_by_visibility()
|
// grow our environment slightly in order for simplify_by_visibility()
|
||||||
// to work best by considering moves on boundaries valid as well
|
// to work best by considering moves on boundaries valid as well
|
||||||
ExPolygonCollection grown_env(offset_ex(env.env, +SCALED_EPSILON));
|
ExPolygonCollection grown_env(offset_ex((Polygons)env.env, +SCALED_EPSILON));
|
||||||
|
|
||||||
if (island_idx == -1) {
|
if (island_idx == -1) {
|
||||||
/* If 'from' or 'to' are not inside our env, they were connected using the
|
/* If 'from' or 'to' are not inside our env, they were connected using the
|
||||||
|
@ -492,16 +492,20 @@ PerimeterGenerator::_variable_width(const ThickPolylines &polylines, ExtrusionRo
|
|||||||
const double w = fmax(line.a_width, line.b_width);
|
const double w = fmax(line.a_width, line.b_width);
|
||||||
|
|
||||||
if (path.polyline.points.empty()) {
|
if (path.polyline.points.empty()) {
|
||||||
path.polyline.append(line.a);
|
|
||||||
path.polyline.append(line.b);
|
|
||||||
|
|
||||||
flow.width = unscale(w);
|
flow.width = unscale(w);
|
||||||
#ifdef SLIC3R_DEBUG
|
#ifdef SLIC3R_DEBUG
|
||||||
printf(" filling %f gap\n", flow.width);
|
printf(" filling %f gap\n", flow.width);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// make sure we don't include too thin segments which
|
||||||
|
// may cause even slightly negative mm3_per_mm because of floating point math
|
||||||
path.mm3_per_mm = flow.mm3_per_mm();
|
path.mm3_per_mm = flow.mm3_per_mm();
|
||||||
|
if (path.mm3_per_mm < EPSILON) continue;
|
||||||
|
|
||||||
path.width = flow.width;
|
path.width = flow.width;
|
||||||
path.height = flow.height;
|
path.height = flow.height;
|
||||||
|
path.polyline.append(line.a);
|
||||||
|
path.polyline.append(line.b);
|
||||||
} else {
|
} else {
|
||||||
thickness_delta = fabs(scale_(flow.width) - w);
|
thickness_delta = fabs(scale_(flow.width) - w);
|
||||||
if (thickness_delta <= tolerance) {
|
if (thickness_delta <= tolerance) {
|
||||||
|
@ -756,6 +756,15 @@ PrintConfigDef::PrintConfigDef()
|
|||||||
def->min = 0;
|
def->min = 0;
|
||||||
def->default_value = new ConfigOptionInt(0);
|
def->default_value = new ConfigOptionInt(0);
|
||||||
|
|
||||||
|
def = this->add("raft_offset", coFloat);
|
||||||
|
def->label = "Raft offset";
|
||||||
|
def->category = "Support material";
|
||||||
|
def->tooltip = "Horizontal margin between object base layer and raft contour.";
|
||||||
|
def->sidetext = "mm";
|
||||||
|
def->cli = "raft-offset=f";
|
||||||
|
def->min = 0;
|
||||||
|
def->default_value = new ConfigOptionFloat(4);
|
||||||
|
|
||||||
def = this->add("resolution", coFloat);
|
def = this->add("resolution", coFloat);
|
||||||
def->label = "Resolution";
|
def->label = "Resolution";
|
||||||
def->tooltip = "Minimum detail resolution, used to simplify the input file for speeding up the slicing job and reducing memory usage. High-resolution models often carry more detail than printers can render. Set to zero to disable any simplification and use full resolution from input.";
|
def->tooltip = "Minimum detail resolution, used to simplify the input file for speeding up the slicing job and reducing memory usage. High-resolution models often carry more detail than printers can render. Set to zero to disable any simplification and use full resolution from input.";
|
||||||
@ -1026,7 +1035,7 @@ PrintConfigDef::PrintConfigDef()
|
|||||||
|
|
||||||
def = this->add("start_gcode", coString);
|
def = this->add("start_gcode", coString);
|
||||||
def->label = "Start G-code";
|
def->label = "Start G-code";
|
||||||
def->tooltip = "This start procedure is inserted at the beginning, after bed has reached the target temperature and extruder just started heating, and before extruder has finished heating. If Slic3r detects M104 or M190 in your custom codes, such commands will not be prepended automatically so you're free to customize the order of heating commands and other custom actions. Note that you can use placeholder variables for all Slic3r settings, so you can put a \"M109 S[first_layer_temperature]\" command wherever you want.";
|
def->tooltip = "This start procedure is inserted at the beginning, after bed has reached the target temperature and extruder just started heating, and before extruder has finished heating. If Slic3r detects M104, M109, M140 or M190 in your custom codes, such commands will not be prepended automatically so you're free to customize the order of heating commands and other custom actions. Note that you can use placeholder variables for all Slic3r settings, so you can put a \"M109 S[first_layer_temperature]\" command wherever you want.";
|
||||||
def->cli = "start-gcode=s";
|
def->cli = "start-gcode=s";
|
||||||
def->multiline = true;
|
def->multiline = true;
|
||||||
def->full_width = true;
|
def->full_width = true;
|
||||||
@ -1344,4 +1353,67 @@ PrintConfigBase::min_object_distance() const
|
|||||||
: duplicate_distance;
|
: duplicate_distance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CLIConfigDef::CLIConfigDef()
|
||||||
|
{
|
||||||
|
t_optiondef_map &Options = this->options;
|
||||||
|
|
||||||
|
ConfigOptionDef* def;
|
||||||
|
|
||||||
|
def = this->add("export_obj", coBool);
|
||||||
|
def->label = "Export SVG";
|
||||||
|
def->tooltip = "Export the model as OBJ.";
|
||||||
|
def->cli = "export-obj";
|
||||||
|
def->default_value = new ConfigOptionBool(false);
|
||||||
|
|
||||||
|
def = this->add("export_pov", coBool);
|
||||||
|
def->label = "Export POV";
|
||||||
|
def->tooltip = "Export the model as POV-Ray definition.";
|
||||||
|
def->cli = "export-pov";
|
||||||
|
def->default_value = new ConfigOptionBool(false);
|
||||||
|
|
||||||
|
def = this->add("export_svg", coBool);
|
||||||
|
def->label = "Export SVG";
|
||||||
|
def->tooltip = "Slice the model and export slices as SVG.";
|
||||||
|
def->cli = "export-svg";
|
||||||
|
def->default_value = new ConfigOptionBool(false);
|
||||||
|
|
||||||
|
def = this->add("info", coBool);
|
||||||
|
def->label = "Output Model Info";
|
||||||
|
def->tooltip = "Write information about the model to the console.";
|
||||||
|
def->cli = "info";
|
||||||
|
def->default_value = new ConfigOptionBool(false);
|
||||||
|
|
||||||
|
def = this->add("load", coStrings);
|
||||||
|
def->label = "Load config file";
|
||||||
|
def->tooltip = "Load configuration from the specified file. It can be used more than once to load options from multiple files.";
|
||||||
|
def->cli = "load";
|
||||||
|
def->default_value = new ConfigOptionStrings();
|
||||||
|
|
||||||
|
def = this->add("output", coString);
|
||||||
|
def->label = "Output File";
|
||||||
|
def->tooltip = "The file where the output will be written (if not specified, it will be based on the input file).";
|
||||||
|
def->cli = "output";
|
||||||
|
def->default_value = new ConfigOptionString("");
|
||||||
|
|
||||||
|
def = this->add("rotate", coFloat);
|
||||||
|
def->label = "Rotate";
|
||||||
|
def->tooltip = "Rotation angle around the Z axis in degrees (0-360, default: 0).";
|
||||||
|
def->cli = "rotate";
|
||||||
|
def->default_value = new ConfigOptionFloat(0);
|
||||||
|
|
||||||
|
def = this->add("save", coString);
|
||||||
|
def->label = "Save config file";
|
||||||
|
def->tooltip = "Save configuration to the specified file.";
|
||||||
|
def->cli = "save";
|
||||||
|
def->default_value = new ConfigOptionString();
|
||||||
|
|
||||||
|
def = this->add("scale", coFloat);
|
||||||
|
def->label = "Scale";
|
||||||
|
def->tooltip = "Scaling factor (default: 1).";
|
||||||
|
def->cli = "scale";
|
||||||
|
def->default_value = new ConfigOptionFloat(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
CLIConfigDef cli_config_def;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -467,6 +467,71 @@ class FullPrintConfig
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class SVGExportConfig
|
||||||
|
: public virtual StaticPrintConfig
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ConfigOptionFloatOrPercent first_layer_height;
|
||||||
|
ConfigOptionFloat layer_height;
|
||||||
|
ConfigOptionInt raft_layers;
|
||||||
|
ConfigOptionFloat raft_offset;
|
||||||
|
|
||||||
|
SVGExportConfig() : StaticPrintConfig() {
|
||||||
|
this->set_defaults();
|
||||||
|
};
|
||||||
|
|
||||||
|
virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) {
|
||||||
|
OPT_PTR(first_layer_height);
|
||||||
|
OPT_PTR(layer_height);
|
||||||
|
OPT_PTR(raft_layers);
|
||||||
|
OPT_PTR(raft_offset);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
class CLIConfigDef : public ConfigDef
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CLIConfigDef();
|
||||||
|
};
|
||||||
|
|
||||||
|
extern CLIConfigDef cli_config_def;
|
||||||
|
|
||||||
|
class CLIConfig
|
||||||
|
: public virtual ConfigBase, public StaticConfig
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ConfigOptionBool export_obj;
|
||||||
|
ConfigOptionBool export_pov;
|
||||||
|
ConfigOptionBool export_svg;
|
||||||
|
ConfigOptionBool info;
|
||||||
|
ConfigOptionStrings load;
|
||||||
|
ConfigOptionString output;
|
||||||
|
ConfigOptionFloat rotate;
|
||||||
|
ConfigOptionString save;
|
||||||
|
ConfigOptionFloat scale;
|
||||||
|
|
||||||
|
CLIConfig() : ConfigBase(), StaticConfig() {
|
||||||
|
this->def = &cli_config_def;
|
||||||
|
this->set_defaults();
|
||||||
|
};
|
||||||
|
|
||||||
|
virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) {
|
||||||
|
OPT_PTR(export_obj);
|
||||||
|
OPT_PTR(export_pov);
|
||||||
|
OPT_PTR(export_svg);
|
||||||
|
OPT_PTR(info);
|
||||||
|
OPT_PTR(load);
|
||||||
|
OPT_PTR(output);
|
||||||
|
OPT_PTR(rotate);
|
||||||
|
OPT_PTR(save);
|
||||||
|
OPT_PTR(scale);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
88
xs/src/libslic3r/SVGExport.cpp
Normal file
88
xs/src/libslic3r/SVGExport.cpp
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
#include "SVGExport.hpp"
|
||||||
|
#include "ClipperUtils.hpp"
|
||||||
|
#include <iostream>
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
|
||||||
|
void
|
||||||
|
SVGExport::writeSVG(const std::string &outputfile)
|
||||||
|
{
|
||||||
|
// align to origin taking raft into account
|
||||||
|
BoundingBoxf3 bb = this->mesh.bounding_box();
|
||||||
|
if (this->config.raft_layers > 0) {
|
||||||
|
bb.min.x -= this->config.raft_offset.value;
|
||||||
|
bb.min.y -= this->config.raft_offset.value;
|
||||||
|
bb.max.x += this->config.raft_offset.value;
|
||||||
|
bb.max.y += this->config.raft_offset.value;
|
||||||
|
}
|
||||||
|
this->mesh.translate(-bb.min.x, -bb.min.y, -bb.min.z); // align to origin
|
||||||
|
const Sizef3 size = bb.size();
|
||||||
|
|
||||||
|
// if we are generating a raft, first_layer_height will not affect mesh slicing
|
||||||
|
const float lh = this->config.layer_height.value;
|
||||||
|
const float first_lh = this->config.first_layer_height.value;
|
||||||
|
|
||||||
|
// generate the list of Z coordinates for mesh slicing
|
||||||
|
// (we slice each layer at half of its thickness)
|
||||||
|
std::vector<float> slice_z, layer_z;
|
||||||
|
{
|
||||||
|
const float first_slice_lh = (this->config.raft_layers > 0) ? lh : first_lh;
|
||||||
|
slice_z.push_back(first_slice_lh/2);
|
||||||
|
layer_z.push_back(first_slice_lh);
|
||||||
|
}
|
||||||
|
while (layer_z.back() + lh/2 <= this->mesh.stl.stats.max.z) {
|
||||||
|
slice_z.push_back(layer_z.back() + lh/2);
|
||||||
|
layer_z.push_back(layer_z.back() + lh);
|
||||||
|
}
|
||||||
|
|
||||||
|
// perform the slicing
|
||||||
|
std::vector<ExPolygons> layers;
|
||||||
|
TriangleMeshSlicer(&this->mesh).slice(slice_z, &layers);
|
||||||
|
|
||||||
|
// generate a solid raft if requested
|
||||||
|
if (this->config.raft_layers > 0) {
|
||||||
|
ExPolygons raft = offset_ex(layers.front(), scale_(this->config.raft_offset));
|
||||||
|
for (int i = this->config.raft_layers; i >= 1; --i) {
|
||||||
|
layer_z.insert(layer_z.begin(), first_lh + lh * (i-1));
|
||||||
|
layers.insert(layers.begin(), raft);
|
||||||
|
}
|
||||||
|
|
||||||
|
// prepend total raft height to all sliced layers
|
||||||
|
for (int i = this->config.raft_layers; i < layer_z.size(); ++i)
|
||||||
|
layer_z[i] += first_lh + lh * (this->config.raft_layers-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE* f = fopen(outputfile.c_str(), "w");
|
||||||
|
fprintf(f,
|
||||||
|
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n"
|
||||||
|
"<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.0//EN\" \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n"
|
||||||
|
"<svg width=\"%f\" height=\"%f\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:svg=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" xmlns:slic3r=\"http://slic3r.org/namespaces/slic3r\">\n"
|
||||||
|
"<!-- Generated using Slic3r %s http://slic3r.org/ -->\n"
|
||||||
|
, size.x, size.y, SLIC3R_VERSION);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < layer_z.size(); ++i) {
|
||||||
|
fprintf(f, "\t<g id=\"layer%zu\" slic3r:z=\"%0.4f\">\n", i, layer_z[i]);
|
||||||
|
for (ExPolygons::const_iterator it = layers[i].begin(); it != layers[i].end(); ++it){
|
||||||
|
std::string pd;
|
||||||
|
Polygons pp = *it;
|
||||||
|
for (Polygons::const_iterator mp = pp.begin(); mp != pp.end(); ++mp) {
|
||||||
|
std::ostringstream d;
|
||||||
|
d << "M ";
|
||||||
|
for (Points::const_iterator p = mp->points.begin(); p != mp->points.end(); ++p) {
|
||||||
|
d << unscale(p->x) << " ";
|
||||||
|
d << unscale(p->y) << " ";
|
||||||
|
}
|
||||||
|
d << "z";
|
||||||
|
pd += d.str() + " ";
|
||||||
|
}
|
||||||
|
fprintf(f,"\t\t<path d=\"%s\" style=\"fill: %s; stroke: %s; stroke-width: %s; fill-type: evenodd\" slic3r:area=\"%0.4f\" />\n",
|
||||||
|
pd.c_str(), "white", "black", "0", unscale(unscale(it->area()))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
fprintf(f,"\t</g>\n");
|
||||||
|
}
|
||||||
|
fprintf(f,"</svg>\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
26
xs/src/libslic3r/SVGExport.hpp
Normal file
26
xs/src/libslic3r/SVGExport.hpp
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
#ifndef slic3r_SVGExport_hpp_
|
||||||
|
#define slic3r_SVGExport_hpp_
|
||||||
|
|
||||||
|
#include "libslic3r.h"
|
||||||
|
#include "ExPolygon.hpp"
|
||||||
|
#include "PrintConfig.hpp"
|
||||||
|
#include "SVG.hpp"
|
||||||
|
#include "TriangleMesh.hpp"
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
|
||||||
|
class SVGExport
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SVGExportConfig config;
|
||||||
|
TriangleMesh mesh;
|
||||||
|
|
||||||
|
SVGExport(const TriangleMesh &mesh) : mesh(mesh) {
|
||||||
|
this->mesh.mirror_x();
|
||||||
|
};
|
||||||
|
void writeSVG(const std::string &outputfile);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -50,19 +50,15 @@ TriangleMesh::TriangleMesh(const TriangleMesh &other)
|
|||||||
|
|
||||||
TriangleMesh& TriangleMesh::operator= (TriangleMesh other)
|
TriangleMesh& TriangleMesh::operator= (TriangleMesh other)
|
||||||
{
|
{
|
||||||
this->swap(other);
|
swap(*this, other);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
TriangleMesh::swap(TriangleMesh &other)
|
TriangleMesh::swap(TriangleMesh &first, TriangleMesh &second)
|
||||||
{
|
{
|
||||||
std::swap(this->stl, other.stl);
|
std::swap(first.repaired, second.repaired);
|
||||||
std::swap(this->repaired, other.repaired);
|
std::swap(first.stl, second.stl);
|
||||||
std::swap(this->stl.facet_start, other.stl.facet_start);
|
|
||||||
std::swap(this->stl.neighbors_start, other.stl.neighbors_start);
|
|
||||||
std::swap(this->stl.v_indices, other.stl.v_indices);
|
|
||||||
std::swap(this->stl.v_shared, other.stl.v_shared);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TriangleMesh::~TriangleMesh() {
|
TriangleMesh::~TriangleMesh() {
|
||||||
@ -93,6 +89,44 @@ TriangleMesh::repair() {
|
|||||||
// admesh fails when repairing empty meshes
|
// admesh fails when repairing empty meshes
|
||||||
if (this->stl.stats.number_of_facets == 0) return;
|
if (this->stl.stats.number_of_facets == 0) return;
|
||||||
|
|
||||||
|
this->check_topology();
|
||||||
|
|
||||||
|
// remove_unconnected
|
||||||
|
if (stl.stats.connected_facets_3_edge < stl.stats.number_of_facets) {
|
||||||
|
stl_remove_unconnected_facets(&stl);
|
||||||
|
}
|
||||||
|
|
||||||
|
// fill_holes
|
||||||
|
if (stl.stats.connected_facets_3_edge < stl.stats.number_of_facets) {
|
||||||
|
stl_fill_holes(&stl);
|
||||||
|
stl_clear_error(&stl);
|
||||||
|
}
|
||||||
|
|
||||||
|
// normal_directions
|
||||||
|
stl_fix_normal_directions(&stl);
|
||||||
|
|
||||||
|
// normal_values
|
||||||
|
stl_fix_normal_values(&stl);
|
||||||
|
|
||||||
|
// always calculate the volume and reverse all normals if volume is negative
|
||||||
|
(void)this->volume();
|
||||||
|
|
||||||
|
// neighbors
|
||||||
|
stl_verify_neighbors(&stl);
|
||||||
|
|
||||||
|
this->repaired = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
float
|
||||||
|
TriangleMesh::volume()
|
||||||
|
{
|
||||||
|
if (this->stl.stats.volume == -1) stl_calculate_volume(&this->stl);
|
||||||
|
return this->stl.stats.volume;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
TriangleMesh::check_topology()
|
||||||
|
{
|
||||||
// checking exact
|
// checking exact
|
||||||
stl_check_facets_exact(&stl);
|
stl_check_facets_exact(&stl);
|
||||||
stl.stats.facets_w_1_bad_edge = (stl.stats.connected_facets_2_edge - stl.stats.connected_facets_3_edge);
|
stl.stats.facets_w_1_bad_edge = (stl.stats.connected_facets_2_edge - stl.stats.connected_facets_3_edge);
|
||||||
@ -117,31 +151,12 @@ TriangleMesh::repair() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// remove_unconnected
|
bool
|
||||||
if (stl.stats.connected_facets_3_edge < stl.stats.number_of_facets) {
|
TriangleMesh::is_manifold() const
|
||||||
stl_remove_unconnected_facets(&stl);
|
{
|
||||||
}
|
return this->stl.stats.connected_facets_3_edge == this->stl.stats.number_of_facets;
|
||||||
|
|
||||||
// fill_holes
|
|
||||||
if (stl.stats.connected_facets_3_edge < stl.stats.number_of_facets) {
|
|
||||||
stl_fill_holes(&stl);
|
|
||||||
stl_clear_error(&stl);
|
|
||||||
}
|
|
||||||
|
|
||||||
// normal_directions
|
|
||||||
stl_fix_normal_directions(&stl);
|
|
||||||
|
|
||||||
// normal_values
|
|
||||||
stl_fix_normal_values(&stl);
|
|
||||||
|
|
||||||
// always calculate the volume and reverse all normals if volume is negative
|
|
||||||
stl_calculate_volume(&stl);
|
|
||||||
|
|
||||||
// neighbors
|
|
||||||
stl_verify_neighbors(&stl);
|
|
||||||
|
|
||||||
this->repaired = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -266,6 +281,16 @@ void TriangleMesh::align_to_origin()
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TriangleMesh::center_around_origin()
|
||||||
|
{
|
||||||
|
this->align_to_origin();
|
||||||
|
this->translate(
|
||||||
|
-(this->stl.stats.size.x/2),
|
||||||
|
-(this->stl.stats.size.y/2),
|
||||||
|
-(this->stl.stats.size.z/2)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
void TriangleMesh::rotate(double angle, Point* center)
|
void TriangleMesh::rotate(double angle, Point* center)
|
||||||
{
|
{
|
||||||
this->translate(-center->x, -center->y, 0);
|
this->translate(-center->x, -center->y, 0);
|
||||||
@ -340,9 +365,8 @@ TriangleMesh::merge(const TriangleMesh &mesh)
|
|||||||
stl_reallocate(&this->stl);
|
stl_reallocate(&this->stl);
|
||||||
|
|
||||||
// copy facets
|
// copy facets
|
||||||
for (int i = 0; i < mesh.stl.stats.number_of_facets; i++) {
|
std::copy(mesh.stl.facet_start, mesh.stl.facet_start + mesh.stl.stats.number_of_facets, this->stl.facet_start + number_of_facets);
|
||||||
this->stl.facet_start[number_of_facets + i] = mesh.stl.facet_start[i];
|
std::copy(mesh.stl.neighbors_start, mesh.stl.neighbors_start + mesh.stl.stats.number_of_facets, this->stl.neighbors_start + number_of_facets);
|
||||||
}
|
|
||||||
|
|
||||||
// update size
|
// update size
|
||||||
stl_get_size(&this->stl);
|
stl_get_size(&this->stl);
|
||||||
|
@ -22,12 +22,15 @@ class TriangleMesh
|
|||||||
TriangleMesh();
|
TriangleMesh();
|
||||||
TriangleMesh(const TriangleMesh &other);
|
TriangleMesh(const TriangleMesh &other);
|
||||||
TriangleMesh& operator= (TriangleMesh other);
|
TriangleMesh& operator= (TriangleMesh other);
|
||||||
void swap(TriangleMesh &other);
|
void swap(TriangleMesh &first, TriangleMesh &second);
|
||||||
~TriangleMesh();
|
~TriangleMesh();
|
||||||
void ReadSTLFile(const std::string &input_file);
|
void ReadSTLFile(const std::string &input_file);
|
||||||
void write_ascii(const std::string &output_file);
|
void write_ascii(const std::string &output_file);
|
||||||
void write_binary(const std::string &output_file);
|
void write_binary(const std::string &output_file);
|
||||||
void repair();
|
void repair();
|
||||||
|
void check_topology();
|
||||||
|
float volume();
|
||||||
|
bool is_manifold() const;
|
||||||
void WriteOBJFile(const std::string &output_file);
|
void WriteOBJFile(const std::string &output_file);
|
||||||
void scale(float factor);
|
void scale(float factor);
|
||||||
void scale(const Pointf3 &versor);
|
void scale(const Pointf3 &versor);
|
||||||
@ -41,6 +44,7 @@ class TriangleMesh
|
|||||||
void mirror_y();
|
void mirror_y();
|
||||||
void mirror_z();
|
void mirror_z();
|
||||||
void align_to_origin();
|
void align_to_origin();
|
||||||
|
void center_around_origin();
|
||||||
void rotate(double angle, Point* center);
|
void rotate(double angle, Point* center);
|
||||||
TriangleMeshPtrs split() const;
|
TriangleMeshPtrs split() const;
|
||||||
void merge(const TriangleMesh &mesh);
|
void merge(const TriangleMesh &mesh);
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
void scale(double factor);
|
void scale(double factor);
|
||||||
void translate(double x, double y);
|
void translate(double x, double y);
|
||||||
void offset(double delta);
|
void offset(double delta);
|
||||||
|
bool contains_point(Point* point) %code{% RETVAL = THIS->contains(*point); %};
|
||||||
Clone<Polygon> polygon();
|
Clone<Polygon> polygon();
|
||||||
Clone<Point> size();
|
Clone<Point> size();
|
||||||
Clone<Point> center();
|
Clone<Point> center();
|
||||||
@ -49,6 +50,7 @@ new_from_points(CLASS, points)
|
|||||||
void merge_point(Pointf* point) %code{% THIS->merge(*point); %};
|
void merge_point(Pointf* point) %code{% THIS->merge(*point); %};
|
||||||
void scale(double factor);
|
void scale(double factor);
|
||||||
void translate(double x, double y);
|
void translate(double x, double y);
|
||||||
|
bool contains_point(Pointf* point) %code{% RETVAL = THIS->contains(*point); %};
|
||||||
Clone<Pointf> size();
|
Clone<Pointf> size();
|
||||||
Clone<Pointf> center();
|
Clone<Pointf> center();
|
||||||
Clone<Pointf> min_point() %code{% RETVAL = THIS->min; %};
|
Clone<Pointf> min_point() %code{% RETVAL = THIS->min; %};
|
||||||
|
@ -38,6 +38,8 @@
|
|||||||
void normalize();
|
void normalize();
|
||||||
%name{setenv} void setenv_();
|
%name{setenv} void setenv_();
|
||||||
double min_object_distance();
|
double min_object_distance();
|
||||||
|
%name{_load} void load(std::string file);
|
||||||
|
void save(std::string file);
|
||||||
};
|
};
|
||||||
|
|
||||||
%name{Slic3r::Config::Static} class StaticPrintConfig {
|
%name{Slic3r::Config::Static} class StaticPrintConfig {
|
||||||
@ -84,6 +86,8 @@
|
|||||||
%};
|
%};
|
||||||
%name{setenv} void setenv_();
|
%name{setenv} void setenv_();
|
||||||
double min_object_distance();
|
double min_object_distance();
|
||||||
|
%name{_load} void load(std::string file);
|
||||||
|
void save(std::string file);
|
||||||
};
|
};
|
||||||
|
|
||||||
%package{Slic3r::Config};
|
%package{Slic3r::Config};
|
||||||
|
@ -71,6 +71,7 @@
|
|||||||
void duplicate(unsigned int copies_num, double dist, BoundingBoxf* bb = NULL);
|
void duplicate(unsigned int copies_num, double dist, BoundingBoxf* bb = NULL);
|
||||||
void duplicate_objects(unsigned int copies_num, double dist, BoundingBoxf* bb = NULL);
|
void duplicate_objects(unsigned int copies_num, double dist, BoundingBoxf* bb = NULL);
|
||||||
void duplicate_objects_grid(unsigned int x, unsigned int y, double dist);
|
void duplicate_objects_grid(unsigned int x, unsigned int y, double dist);
|
||||||
|
void print_info();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -196,6 +197,8 @@ ModelMaterial::attributes()
|
|||||||
RETVAL = new ModelObjectPtrs(); // leak?
|
RETVAL = new ModelObjectPtrs(); // leak?
|
||||||
THIS->split(RETVAL);
|
THIS->split(RETVAL);
|
||||||
%};
|
%};
|
||||||
|
|
||||||
|
void print_info();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user