mirror of
https://git.mirrors.martin98.com/https://github.com/slic3r/Slic3r.git
synced 2025-08-13 07:39:08 +08:00
Merge branch 'master' into adaptive-slicing
This commit is contained in:
commit
22c010791d
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
|
||||
* 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)
|
||||
* 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.
|
||||
|
||||
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.
|
||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,3 +1,4 @@
|
||||
build
|
||||
Build
|
||||
Build.bat
|
||||
MYMETA.json
|
||||
@ -9,4 +10,4 @@ xs/buildtmp
|
||||
MANIFEST.bak
|
||||
xs/MANIFEST.bak
|
||||
xs/assertlib*
|
||||
.init_bundle.ini
|
||||
.init_bundle.ini
|
||||
|
@ -1,5 +1,5 @@
|
||||
language: perl
|
||||
install: true
|
||||
install: export LDLOADLIBS=-lstdc++
|
||||
script: perl ./Build.PL
|
||||
perl:
|
||||
- "5.14"
|
||||
@ -19,3 +19,4 @@ addons:
|
||||
packages:
|
||||
- libboost-thread1.55-dev
|
||||
- libboost-system1.55-dev
|
||||
- libboost-filesystem1.55-dev
|
||||
|
34
README.md
34
README.md
@ -2,8 +2,9 @@ _Q: Oh cool, a new RepRap slicer?_
|
||||
|
||||
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
|
||||
|
||||
Slic3r takes 3D models (STL, OBJ, AMF) and converts them into G-code instructions for
|
||||
3D printers. It's compatible with any modern printer based on the RepRap toolchain,
|
||||
@ -52,25 +53,26 @@ Other major features are:
|
||||
|
||||
### How to install?
|
||||
|
||||
You can just download a precompiled package from [slic3r.org](http://slic3r.org/);
|
||||
You can download a precompiled package from [slic3r.org](http://slic3r.org/);
|
||||
it will run without the need for any dependency.
|
||||
|
||||
If you want to compile the source yourself just do the following (checkout
|
||||
[slic3r.org](http://slic3r.org/download) for more details):
|
||||
|
||||
```
|
||||
$ git clone https://github.com/alexrj/Slic3r.git
|
||||
$ cd Slic3r
|
||||
$ perl Build.PL --sudo
|
||||
$ perl Build.PL --sudo --gui
|
||||
$ ./slic3r.pl
|
||||
```
|
||||
If you want to compile the source yourself follow the instructions on one of these wiki pages:
|
||||
* [Linux](https://github.com/alexrj/Slic3r/wiki/Running-Slic3r-from-git-on-GNU-Linux)
|
||||
* [Windows](https://github.com/alexrj/Slic3r/wiki/Running-Slic3r-from-git-on-Windows)
|
||||
* [Mac OSX](https://github.com/alexrj/Slic3r/wiki/Running-Slic3r-from-git-on-OS-X)
|
||||
|
||||
### Can I help?
|
||||
|
||||
Sure! Drop me a line at aar@cpan.org. You can also
|
||||
find me in #reprap and in #slic3r on FreeNode with the nickname _Sound_.
|
||||
Before sending patches and pull requests contact me to discuss your proposed
|
||||
Sure! You can do the following to find things that are available to help with:
|
||||
* [Pull Request Milestone](https://github.com/alexrj/Slic3r/milestone/31)
|
||||
* Please comment in the related github issue that you are working on it so that other people know.
|
||||
* Items in the [TODO](https://github.com/alexrj/Slic3r/wiki/TODO) wiki page.
|
||||
* Please comment in the related github issue that you are working on it so that other people know.
|
||||
* Drop me a line at aar@cpan.org.
|
||||
* You can also find me (rarely) in #reprap and in #slic3r on [FreeNode](http://webchat.freenode.net) with the nickname _Sound_. Another contributor, _LoH_, is also in both channels.
|
||||
* Add an [issue](https://github.com/alexrj/Slic3r/issues) to the github tracker if it isn't already present.
|
||||
|
||||
Before sending patches and pull requests contact me (preferably through opening a github issue or commenting on an existing, related, issue) to discuss your proposed
|
||||
changes: this way we'll ensure nobody wastes their time and no conflicts arise
|
||||
in development.
|
||||
|
||||
@ -132,7 +134,7 @@ The author of the Silk icon set is Mark James.
|
||||
(default: 100,100)
|
||||
--z-offset Additional height in mm to add to vertical coordinates
|
||||
(+/-, default: 0)
|
||||
--gcode-flavor The type of G-code to generate (reprap/teacup/makerware/sailfish/mach3/machinekit/no-extrusion,
|
||||
--gcode-flavor The type of G-code to generate (reprap/teacup/makerware/sailfish/mach3/machinekit/smoothie/no-extrusion,
|
||||
default: reprap)
|
||||
--use-relative-e-distances Enable this to get relative E values (default: no)
|
||||
--use-firmware-retraction Enable firmware-controlled retraction using G10/G11 (default: no)
|
||||
|
@ -87,8 +87,17 @@ sub load {
|
||||
my $class = shift;
|
||||
my ($file) = @_;
|
||||
|
||||
my $ini = __PACKAGE__->read_ini($file);
|
||||
return $class->load_ini_hash($ini->{_});
|
||||
# legacy syntax of load()
|
||||
my $config = $class->new;
|
||||
$config->_load(Slic3r::encode_path($file));
|
||||
return $config;
|
||||
}
|
||||
|
||||
sub save {
|
||||
my $self = shift;
|
||||
my ($file) = @_;
|
||||
|
||||
return $self->_save(Slic3r::encode_path($file));
|
||||
}
|
||||
|
||||
sub load_ini_hash {
|
||||
@ -186,13 +195,6 @@ sub as_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
|
||||
# objects because it performs cross checks
|
||||
sub validate {
|
||||
@ -235,8 +237,8 @@ sub validate {
|
||||
die "Invalid value for --gcode-flavor\n"
|
||||
if !first { $_ eq $self->gcode_flavor } @{$Options->{gcode_flavor}{values}};
|
||||
|
||||
die "--use-firmware-retraction is only supported by Marlin firmware\n"
|
||||
if $self->use_firmware_retraction && $self->gcode_flavor ne 'reprap' && $self->gcode_flavor ne 'machinekit';
|
||||
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';
|
||||
|
||||
die "--use-firmware-retraction is not compatible with --wipe\n"
|
||||
if $self->use_firmware_retraction && first {$_} @{$self->wipe};
|
||||
|
@ -5,29 +5,43 @@ use warnings;
|
||||
use List::Util qw(min max);
|
||||
use Slic3r::Geometry qw(PI X Y unscale deg2rad);
|
||||
use Slic3r::Geometry::Clipper qw(intersection_pl);
|
||||
use Wx qw(:misc :pen :brush :font wxTAB_TRAVERSAL);
|
||||
use Wx::Event qw(EVT_PAINT EVT_MOUSE_EVENTS);
|
||||
use Wx qw(:misc :pen :brush :font :systemsettings wxTAB_TRAVERSAL wxSOLID);
|
||||
use Wx::Event qw(EVT_PAINT EVT_ERASE_BACKGROUND EVT_MOUSE_EVENTS EVT_SIZE);
|
||||
use base qw(Wx::Panel Class::Accessor);
|
||||
|
||||
__PACKAGE__->mk_accessors(qw(bed_shape interactive pos _scale_factor _shift on_move));
|
||||
__PACKAGE__->mk_accessors(qw(bed_shape interactive pos _scale_factor _shift on_move _painted));
|
||||
|
||||
sub new {
|
||||
my ($class, $parent, $bed_shape) = @_;
|
||||
|
||||
my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, [250,-1], wxTAB_TRAVERSAL);
|
||||
$self->{user_drawn_background} = $^O ne 'darwin';
|
||||
$self->bed_shape($bed_shape // []);
|
||||
EVT_PAINT($self, \&_repaint);
|
||||
EVT_ERASE_BACKGROUND($self, sub {}) if $self->{user_drawn_background};
|
||||
EVT_MOUSE_EVENTS($self, \&_mouse_event);
|
||||
|
||||
EVT_SIZE($self, sub { $self->Refresh; });
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub _repaint {
|
||||
my ($self) = @_;
|
||||
my ($self, $event) = @_;
|
||||
|
||||
my $dc = Wx::AutoBufferedPaintDC->new($self);
|
||||
my ($cw, $ch) = $self->GetSizeWH;
|
||||
return if $cw == 0; # when canvas is not rendered yet, size is 0,0
|
||||
|
||||
if ($self->{user_drawn_background}) {
|
||||
# On all systems the AutoBufferedPaintDC() achieves double buffering.
|
||||
# On MacOS the background is erased, on Windows the background is not erased
|
||||
# and on Linux/GTK the background is erased to gray color.
|
||||
# Fill DC with the background on Windows & Linux/GTK.
|
||||
my $color = Wx::SystemSettings::GetSystemColour(wxSYS_COLOUR_3DLIGHT);
|
||||
$dc->SetPen(Wx::Pen->new($color, 1, wxSOLID));
|
||||
$dc->SetBrush(Wx::Brush->new($color, wxSOLID));
|
||||
my $rect = $self->GetUpdateRegion()->GetBox();
|
||||
$dc->DrawRectangle($rect->GetLeft(), $rect->GetTop(), $rect->GetWidth(), $rect->GetHeight());
|
||||
}
|
||||
|
||||
# turn $cw and $ch from sizes to max coordinates
|
||||
$cw--;
|
||||
@ -39,11 +53,12 @@ sub _repaint {
|
||||
]);
|
||||
|
||||
# leave space for origin point
|
||||
$cbb->set_x_min($cbb->x_min + 2);
|
||||
$cbb->set_y_max($cbb->y_max - 2);
|
||||
$cbb->set_x_min($cbb->x_min + 4);
|
||||
$cbb->set_x_max($cbb->x_max - 4);
|
||||
$cbb->set_y_max($cbb->y_max - 4);
|
||||
|
||||
# leave space for origin label
|
||||
$cbb->set_y_max($cbb->y_max - 10);
|
||||
$cbb->set_y_max($cbb->y_max - 13);
|
||||
|
||||
# read new size
|
||||
($cw, $ch) = @{$cbb->size};
|
||||
@ -148,12 +163,15 @@ sub _repaint {
|
||||
$dc->DrawLine($pos_px->[X]-15, $pos_px->[Y], $pos_px->[X]+15, $pos_px->[Y]);
|
||||
$dc->DrawLine($pos_px->[X], $pos_px->[Y]-15, $pos_px->[X], $pos_px->[Y]+15);
|
||||
}
|
||||
|
||||
$self->_painted(1);
|
||||
}
|
||||
|
||||
sub _mouse_event {
|
||||
my ($self, $event) = @_;
|
||||
|
||||
return if !$self->interactive;
|
||||
return if !$self->_painted;
|
||||
|
||||
my $pos = $event->GetPosition;
|
||||
my $point = $self->to_units([ $pos->x, $pos->y ]); #]]
|
||||
|
@ -11,7 +11,7 @@ use List::Util qw(reduce min max first);
|
||||
use Slic3r::Geometry qw(X Y Z MIN MAX triangle_normal normalize deg2rad tan scale unscale scaled_epsilon);
|
||||
use Slic3r::Geometry::Clipper qw(offset_ex intersection_pl);
|
||||
use Wx::GLCanvas qw(:all);
|
||||
|
||||
|
||||
__PACKAGE__->mk_accessors( qw(_quat _dirty init
|
||||
enable_picking
|
||||
enable_moving
|
||||
@ -56,11 +56,31 @@ use constant HOVER_COLOR => [0.4,0.9,0,1];
|
||||
sub new {
|
||||
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
|
||||
# default on Linux, causing transparency issues
|
||||
my $self = $class->SUPER::new($parent, -1, Wx::wxDefaultPosition, Wx::wxDefaultSize, 0, "",
|
||||
[WX_GL_RGBA, WX_GL_DOUBLEBUFFER, WX_GL_DEPTH_SIZE, 16, 0]);
|
||||
|
||||
my $self = $class->SUPER::new($parent, -1, Wx::wxDefaultPosition, Wx::wxDefaultSize, 0, "", $attrib);
|
||||
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->_quat((0, 0, 0, 1));
|
||||
$self->_stheta(45);
|
||||
|
@ -133,11 +133,14 @@ sub on_change {
|
||||
$self->{on_change} = $cb // sub {};
|
||||
}
|
||||
|
||||
# Called from the constructor.
|
||||
# Set the initial bed shape from a list of points.
|
||||
# Deduce the bed shape type (rect, circle, custom)
|
||||
# This routine shall be smart enough if the user messes up
|
||||
# with the list of points in the ini file directly.
|
||||
sub _set_shape {
|
||||
my ($self, $points) = @_;
|
||||
|
||||
$self->{bed_shape} = $points;
|
||||
|
||||
# is this a rectangle?
|
||||
if (@$points == 4) {
|
||||
my $polygon = Slic3r::Polygon->new_scale(@$points);
|
||||
@ -164,6 +167,7 @@ sub _set_shape {
|
||||
|
||||
# is this a circle?
|
||||
{
|
||||
# Analyze the array of points. Do they reside on a circle?
|
||||
my $polygon = Slic3r::Polygon->new_scale(@$points);
|
||||
my $center = $polygon->bounding_box->center;
|
||||
my @vertex_distances = map $center->distance_to($_), @$polygon;
|
||||
@ -178,10 +182,24 @@ sub _set_shape {
|
||||
}
|
||||
}
|
||||
|
||||
if (@$points < 3) {
|
||||
# Invalid polygon. Revert to default bed dimensions.
|
||||
$self->{shape_options_book}->SetSelection(SHAPE_RECTANGULAR);
|
||||
my $optgroup = $self->{optgroups}[SHAPE_RECTANGULAR];
|
||||
$optgroup->set_value('rect_size', [200, 200]);
|
||||
$optgroup->set_value('rect_origin', [0, 0]);
|
||||
$self->_update_shape;
|
||||
return;
|
||||
}
|
||||
|
||||
# This is a custom bed shape, use the polygon provided.
|
||||
$self->{shape_options_book}->SetSelection(SHAPE_CUSTOM);
|
||||
# Copy the polygon to the canvas, make a copy of the array.
|
||||
$self->{canvas}->bed_shape([@$points]);
|
||||
$self->_update_shape;
|
||||
}
|
||||
|
||||
# Update the bed shape from the dialog fields.
|
||||
sub _update_shape {
|
||||
my ($self) = @_;
|
||||
|
||||
@ -229,8 +247,11 @@ sub _update_shape {
|
||||
sub _update_preview {
|
||||
my ($self) = @_;
|
||||
$self->{canvas}->Refresh if $self->{canvas};
|
||||
$self->Refresh;
|
||||
}
|
||||
|
||||
# Called from the constructor.
|
||||
# Create a panel for a rectangular / circular / custom bed shape.
|
||||
sub _init_shape_options_page {
|
||||
my ($self, $title) = @_;
|
||||
|
||||
@ -252,6 +273,7 @@ sub _init_shape_options_page {
|
||||
return $optgroup;
|
||||
}
|
||||
|
||||
# Loads an stl file, projects it to the XY plane and calculates a polygon.
|
||||
sub _load_stl {
|
||||
my ($self) = @_;
|
||||
|
||||
@ -266,7 +288,7 @@ sub _load_stl {
|
||||
my $model = Slic3r::Model->read_from_file($input_file);
|
||||
my $mesh = $model->raw_mesh;
|
||||
my $expolygons = $mesh->horizontal_projection;
|
||||
|
||||
|
||||
if (@$expolygons == 0) {
|
||||
Slic3r::GUI::show_error($self, "The selected file contains no geometry.");
|
||||
return;
|
||||
@ -277,9 +299,11 @@ sub _load_stl {
|
||||
}
|
||||
|
||||
my $polygon = $expolygons->[0]->contour;
|
||||
$self->{canvas}->bed_shape([ map [ unscale($_->x), unscale($_->y) ], @$polygon ]); #))
|
||||
$self->{canvas}->bed_shape([ map [ unscale($_->x), unscale($_->y) ], @$polygon ]);
|
||||
$self->_update_preview();
|
||||
}
|
||||
|
||||
# Returns the resulting bed shape polygon. This value will be stored to the ini file.
|
||||
sub GetValue {
|
||||
my ($self) = @_;
|
||||
return $self->{canvas}->bed_shape;
|
||||
|
@ -1640,8 +1640,10 @@ sub selection_changed {
|
||||
$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);
|
||||
|
||||
if (my $stats = $model_object->mesh_stats) {
|
||||
$self->{object_info_volume}->SetLabel(sprintf('%.2f', $stats->{volume} * ($model_instance->scaling_factor**3)));
|
||||
my $raw_mesh = $model_object->raw_mesh;
|
||||
$raw_mesh->repair; # this calculates number_of_parts
|
||||
if (my $stats = $raw_mesh->stats) {
|
||||
$self->{object_info_volume}->SetLabel(sprintf('%.2f', $raw_mesh->volume * ($model_instance->scaling_factor**3)));
|
||||
$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)})) {
|
||||
$self->{object_info_manifold}->SetLabel(sprintf("Auto-repaired (%d errors)", $errors));
|
||||
|
@ -7,7 +7,7 @@ use List::Util qw(min max first);
|
||||
use Slic3r::Geometry qw(X Y scale unscale convex_hull);
|
||||
use Slic3r::Geometry::Clipper qw(offset JT_ROUND intersection_pl);
|
||||
use Wx qw(:misc :pen :brush :sizer :font :cursor wxTAB_TRAVERSAL);
|
||||
use Wx::Event qw(EVT_MOUSE_EVENTS EVT_PAINT EVT_SIZE);
|
||||
use Wx::Event qw(EVT_MOUSE_EVENTS EVT_PAINT EVT_ERASE_BACKGROUND EVT_SIZE);
|
||||
use base 'Wx::Panel';
|
||||
|
||||
use constant CANVAS_TEXT => join('-', +(localtime)[3,4]) eq '13-8'
|
||||
@ -19,8 +19,9 @@ sub new {
|
||||
my ($parent, $size, $objects, $model, $config) = @_;
|
||||
|
||||
my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, $size, wxTAB_TRAVERSAL);
|
||||
# This has only effect on MacOS. On Windows and Linux/GTK, the background is painted by $self->repaint().
|
||||
$self->SetBackgroundColour(Wx::wxWHITE);
|
||||
|
||||
|
||||
$self->{objects} = $objects;
|
||||
$self->{model} = $model;
|
||||
$self->{config} = $config;
|
||||
@ -37,8 +38,11 @@ sub new {
|
||||
$self->{print_center_pen} = Wx::Pen->new(Wx::Colour->new(200,200,200), 1, wxSOLID);
|
||||
$self->{clearance_pen} = Wx::Pen->new(Wx::Colour->new(0,0,200), 1, wxSOLID);
|
||||
$self->{skirt_pen} = Wx::Pen->new(Wx::Colour->new(150,150,150), 1, wxSOLID);
|
||||
|
||||
$self->{user_drawn_background} = $^O ne 'darwin';
|
||||
|
||||
EVT_PAINT($self, \&repaint);
|
||||
EVT_ERASE_BACKGROUND($self, sub {}) if $self->{user_drawn_background};
|
||||
EVT_MOUSE_EVENTS($self, \&mouse_event);
|
||||
EVT_SIZE($self, sub {
|
||||
$self->update_bed_size;
|
||||
@ -74,7 +78,19 @@ sub repaint {
|
||||
my $dc = Wx::AutoBufferedPaintDC->new($self);
|
||||
my $size = $self->GetSize;
|
||||
my @size = ($size->GetWidth, $size->GetHeight);
|
||||
|
||||
|
||||
if ($self->{user_drawn_background}) {
|
||||
# On all systems the AutoBufferedPaintDC() achieves double buffering.
|
||||
# On MacOS the background is erased, on Windows the background is not erased
|
||||
# and on Linux/GTK the background is erased to gray color.
|
||||
# Fill DC with the background on Windows & Linux/GTK.
|
||||
my $brush_background = Wx::Brush->new(Wx::wxWHITE, wxSOLID);
|
||||
$dc->SetPen(wxWHITE_PEN);
|
||||
$dc->SetBrush($brush_background);
|
||||
my $rect = $self->GetUpdateRegion()->GetBox();
|
||||
$dc->DrawRectangle($rect->GetLeft(), $rect->GetTop(), $rect->GetWidth(), $rect->GetHeight());
|
||||
}
|
||||
|
||||
# draw grid
|
||||
$dc->SetPen($self->{grid_pen});
|
||||
$dc->DrawLine(map @$_, @$_) for @{$self->{grid}};
|
||||
|
@ -143,10 +143,16 @@ __PACKAGE__->mk_accessors(qw(
|
||||
sub new {
|
||||
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->_zoom(1);
|
||||
|
||||
|
||||
# 2D point in model space
|
||||
$self->_camera_target(Slic3r::Pointf->new(0,0));
|
||||
|
||||
|
@ -15,6 +15,11 @@ sub new {
|
||||
$self->{model_object_idx} = $params{model_object_idx};
|
||||
$self->{model_object} = $params{model_object};
|
||||
$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
|
||||
$self->{cut_options} = {
|
||||
@ -31,11 +36,18 @@ sub new {
|
||||
title => 'Cut',
|
||||
on_change => sub {
|
||||
my ($opt_id) = @_;
|
||||
|
||||
$self->{cut_options}{$opt_id} = $optgroup->get_value($opt_id);
|
||||
wxTheApp->CallAfter(sub {
|
||||
$self->_update;
|
||||
});
|
||||
# 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->{mesh_cut_valid} = 0;
|
||||
wxTheApp->CallAfter(sub {
|
||||
$self->_update;
|
||||
});
|
||||
}
|
||||
},
|
||||
label_width => 120,
|
||||
);
|
||||
@ -113,6 +125,10 @@ sub new {
|
||||
$self->{sizer}->SetSizeHints($self);
|
||||
|
||||
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->{cut_options}{rotate_lower}) {
|
||||
$self->{new_model_objects}{lower}->rotate(PI, X);
|
||||
@ -123,41 +139,86 @@ sub new {
|
||||
$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->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;
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
# 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) = @_;
|
||||
|
||||
# Early exit. If the cut is valid, don't recalculate it.
|
||||
return if $self->{mesh_cut_valid};
|
||||
|
||||
my $z = $self->_mesh_slice_z_pos();
|
||||
|
||||
my ($new_model) = $self->{model_object}->cut($z);
|
||||
my ($upper_object, $lower_object) = @{$new_model->objects};
|
||||
$self->{new_model} = $new_model;
|
||||
$self->{new_model_objects} = {};
|
||||
if ($self->{cut_options}{keep_upper} && $upper_object->volumes_count > 0) {
|
||||
$self->{new_model_objects}{upper} = $upper_object;
|
||||
}
|
||||
if ($self->{cut_options}{keep_lower} && $lower_object->volumes_count > 0) {
|
||||
$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->{cut_options}{z} / $self->{model_object}->instances->[0]->scaling_factor;
|
||||
|
||||
{
|
||||
my ($new_model) = $self->{model_object}->cut($z);
|
||||
my ($upper_object, $lower_object) = @{$new_model->objects};
|
||||
$self->{new_model} = $new_model;
|
||||
$self->{new_model_objects} = {};
|
||||
if ($self->{cut_options}{keep_upper} && $upper_object->volumes_count > 0) {
|
||||
$self->{new_model_objects}{upper} = $upper_object;
|
||||
}
|
||||
if ($self->{cut_options}{keep_lower} && $lower_object->volumes_count > 0) {
|
||||
$self->{new_model_objects}{lower} = $lower_object;
|
||||
}
|
||||
}
|
||||
my $z = $self->_mesh_slice_z_pos();
|
||||
|
||||
|
||||
# update canvas
|
||||
if ($self->{canvas}) {
|
||||
# get volumes to render
|
||||
my @objects = ();
|
||||
if ($self->{cut_options}{preview}) {
|
||||
if ($life_preview_active) {
|
||||
push @objects, values %{$self->{new_model_objects}};
|
||||
} else {
|
||||
push @objects, $self->{model_object};
|
||||
|
@ -2,7 +2,7 @@ package Slic3r::GUI::Projector;
|
||||
use strict;
|
||||
use warnings;
|
||||
use Wx qw(:dialog :id :misc :sizer :systemsettings :bitmap :button :icon wxTheApp);
|
||||
use Wx::Event qw(EVT_BUTTON EVT_TEXT_ENTER EVT_SPINCTRL EVT_SLIDER);
|
||||
use Wx::Event qw(EVT_BUTTON EVT_CLOSE EVT_TEXT_ENTER EVT_SPINCTRL EVT_SLIDER);
|
||||
use base qw(Wx::Dialog Class::Accessor);
|
||||
use utf8;
|
||||
|
||||
@ -375,12 +375,16 @@ sub new {
|
||||
}
|
||||
|
||||
{
|
||||
# should be wxCLOSE but it crashes on Linux, maybe it's a Wx bug
|
||||
my $buttons = $self->CreateStdDialogButtonSizer(wxOK);
|
||||
EVT_BUTTON($self, wxID_CLOSE, sub {
|
||||
EVT_BUTTON($self, wxID_OK, sub {
|
||||
$self->_close;
|
||||
});
|
||||
$sizer->Add($buttons, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10);
|
||||
}
|
||||
EVT_CLOSE($self, sub {
|
||||
$self->_close;
|
||||
});
|
||||
|
||||
$self->SetSizer($sizer);
|
||||
$sizer->SetSizeHints($self);
|
||||
|
@ -741,13 +741,14 @@ sub _update {
|
||||
|
||||
my $config = $self->{config};
|
||||
|
||||
if ($config->spiral_vase && !($config->perimeters == 1 && $config->top_solid_layers == 0 && $config->fill_density == 0)) {
|
||||
if ($config->spiral_vase && !($config->perimeters == 1 && $config->top_solid_layers == 0 && $config->fill_density == 0 && $config->infill_only_where_needed == 0 && $config->support_material == 0)) {
|
||||
my $dialog = Wx::MessageDialog->new($self,
|
||||
"The Spiral Vase mode requires:\n"
|
||||
. "- one perimeter\n"
|
||||
. "- no top solid layers\n"
|
||||
. "- 0% fill density\n"
|
||||
. "- no support material\n"
|
||||
. "- no infill where necessary\n"
|
||||
. "\nShall I adjust those settings in order to enable Spiral Vase?",
|
||||
'Spiral Vase', wxICON_WARNING | wxYES | wxNO);
|
||||
if ($dialog->ShowModal() == wxID_YES) {
|
||||
@ -756,6 +757,7 @@ sub _update {
|
||||
$new_conf->set("top_solid_layers", 0);
|
||||
$new_conf->set("fill_density", 0);
|
||||
$new_conf->set("support_material", 0);
|
||||
$new_conf->set("infill_only_where_needed", 0);
|
||||
$self->load_config($new_conf);
|
||||
} else {
|
||||
my $new_conf = Slic3r::Config->new;
|
||||
|
@ -67,11 +67,6 @@ sub set_material {
|
||||
return $material;
|
||||
}
|
||||
|
||||
sub print_info {
|
||||
my $self = shift;
|
||||
$_->print_info for @{$self->objects};
|
||||
}
|
||||
|
||||
sub looks_like_multipart_object {
|
||||
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;
|
||||
|
@ -35,7 +35,7 @@ sub BUILD {
|
||||
$layer_count = sum(map { $_->total_layer_count } @{$self->objects});
|
||||
}
|
||||
|
||||
# set up our helper object
|
||||
# set up our helper object: This is a C++ Slic3r::GCode instance.
|
||||
my $gcodegen = Slic3r::GCode->new;
|
||||
$self->_gcodegen($gcodegen);
|
||||
$gcodegen->set_placeholder_parser($self->placeholder_parser);
|
||||
@ -116,20 +116,21 @@ sub BUILD {
|
||||
if $self->config->pressure_advance > 0;
|
||||
}
|
||||
|
||||
# Export a G-code for the complete print.
|
||||
sub export {
|
||||
my ($self) = @_;
|
||||
|
||||
my $fh = $self->fh;
|
||||
my $gcodegen = $self->_gcodegen;
|
||||
|
||||
# write some information
|
||||
# Write information on the generator.
|
||||
my @lt = localtime;
|
||||
printf $fh "; generated by Slic3r $Slic3r::VERSION on %04d-%02d-%02d at %02d:%02d:%02d\n\n",
|
||||
$lt[5] + 1900, $lt[4]+1, $lt[3], $lt[2], $lt[1], $lt[0];
|
||||
|
||||
# Write notes (content of the Print Settings tab -> Notes)
|
||||
print $fh "; $_\n" foreach split /\R/, $self->config->notes;
|
||||
print $fh "\n" if $self->config->notes;
|
||||
|
||||
# Write some terse information on the slicing parameters.
|
||||
my $first_object = $self->objects->[0];
|
||||
my $layer_height = $first_object->config->layer_height;
|
||||
for my $region_id (0..$#{$self->print->regions}) {
|
||||
@ -156,6 +157,7 @@ sub export {
|
||||
# prepare the helper object for replacing placeholders in custom G-code and output filename
|
||||
$self->placeholder_parser->update_timestamp;
|
||||
|
||||
# disable fan
|
||||
print $fh $gcodegen->writer->set_fan(0, 1)
|
||||
if $self->config->cooling && $self->config->disable_fan_first_layers;
|
||||
|
||||
@ -341,6 +343,9 @@ sub _print_first_layer_temperature {
|
||||
}
|
||||
}
|
||||
|
||||
# Called per object's layer.
|
||||
# First a $gcode string is collected,
|
||||
# then filtered and finally written to a file $fh.
|
||||
sub process_layer {
|
||||
my $self = shift;
|
||||
my ($layer, $object_copies) = @_;
|
||||
@ -486,6 +491,17 @@ sub process_layer {
|
||||
# group extrusions by extruder and then by island
|
||||
my %by_extruder = (); # extruder_id => [ { perimeters => \@perimeters, infill => \@infill } ]
|
||||
|
||||
# cache bounding boxes of layer slices
|
||||
my @layer_slices_bb = map $_->contour->bounding_box, @{$layer->slices};
|
||||
my $point_inside_surface = sub {
|
||||
my ($i, $point) = @_;
|
||||
|
||||
my $bbox = $layer_slices_bb[$i];
|
||||
return $layer_slices_bb[$i]->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)) {
|
||||
my $layerm = $layer->regions->[$region_id] or next;
|
||||
my $region = $self->print->get_region($region_id);
|
||||
@ -500,9 +516,12 @@ sub process_layer {
|
||||
$by_extruder{$extruder_id} //= [];
|
||||
|
||||
# $perimeter_coll is an ExtrusionPath::Collection object representing a single slice
|
||||
for my $i (0 .. $#{$layer->slices}) {
|
||||
if ($i == $#{$layer->slices}
|
||||
|| $layer->slices->[$i]->contour->contains_point($perimeter_coll->first_point)) {
|
||||
for my $i (0 .. $n_slices) {
|
||||
if (
|
||||
# $perimeter_coll->first_point does not fit inside any slice
|
||||
$i == $n_slices
|
||||
# $perimeter_coll->first_point fits inside ith slice
|
||||
|| $point_inside_surface->($i, $perimeter_coll->first_point)) {
|
||||
$by_extruder{$extruder_id}[$i] //= { perimeters => {} };
|
||||
$by_extruder{$extruder_id}[$i]{perimeters}{$region_id} //= [];
|
||||
push @{ $by_extruder{$extruder_id}[$i]{perimeters}{$region_id} }, @$perimeter_coll;
|
||||
@ -528,9 +547,9 @@ sub process_layer {
|
||||
$by_extruder{$extruder_id} //= [];
|
||||
|
||||
# $fill is an ExtrusionPath::Collection object
|
||||
for my $i (0 .. $#{$layer->slices}) {
|
||||
if ($i == $#{$layer->slices}
|
||||
|| $layer->slices->[$i]->contour->contains_point($fill->first_point)) {
|
||||
for my $i (0 .. $n_slices) {
|
||||
if ($i == $n_slices
|
||||
|| $point_inside_surface->($i, $fill->first_point)) {
|
||||
$by_extruder{$extruder_id}[$i] //= { infill => {} };
|
||||
$by_extruder{$extruder_id}[$i]{infill}{$region_id} //= [];
|
||||
push @{ $by_extruder{$extruder_id}[$i]{infill}{$region_id} }, $fill;
|
||||
@ -584,6 +603,7 @@ sub process_layer {
|
||||
print {$self->fh} $self->filter($gcode);
|
||||
}
|
||||
|
||||
# Extrude perimeters: Decide where to put seams (hide or align seams).
|
||||
sub _extrude_perimeters {
|
||||
my ($self, $entities_by_region) = @_;
|
||||
|
||||
@ -596,6 +616,7 @@ sub _extrude_perimeters {
|
||||
return $gcode;
|
||||
}
|
||||
|
||||
# Chain the paths hierarchically by a greedy algorithm to minimize a travel distance.
|
||||
sub _extrude_infill {
|
||||
my ($self, $entities_by_region) = @_;
|
||||
|
||||
|
@ -469,6 +469,10 @@ sub make_perimeters {
|
||||
for my $i (0 .. ($self->layer_count - 2)) {
|
||||
my $layerm = $self->get_layer($i)->get_region($region_id);
|
||||
my $upper_layerm = $self->get_layer($i+1)->get_region($region_id);
|
||||
my $upper_layerm_polygons = [ map $_->p, @{$upper_layerm->slices} ];
|
||||
# Filter upper layer polygons in intersection_ppl by their bounding boxes?
|
||||
# my $upper_layerm_poly_bboxes= [ map $_->bounding_box, @{$upper_layerm_polygons} ];
|
||||
my $total_loop_length = sum(map $_->length, @$upper_layerm_polygons) // 0;
|
||||
|
||||
my $perimeter_spacing = $layerm->flow(FLOW_ROLE_PERIMETER)->scaled_spacing;
|
||||
my $ext_perimeter_flow = $layerm->flow(FLOW_ROLE_EXTERNAL_PERIMETER);
|
||||
@ -491,12 +495,11 @@ sub make_perimeters {
|
||||
|
||||
# check whether a portion of the upper slices falls inside the critical area
|
||||
my $intersection = intersection_ppl(
|
||||
[ map $_->p, @{$upper_layerm->slices} ],
|
||||
$upper_layerm_polygons,
|
||||
$critical_area,
|
||||
);
|
||||
|
||||
# only add an additional loop if at least 30% of the slice loop would benefit from it
|
||||
my $total_loop_length = sum(map $_->length, map $_->p, @{$upper_layerm->slices}) // 0;
|
||||
my $total_intersection_length = sum(map $_->length, @$intersection) // 0;
|
||||
last unless $total_intersection_length > $total_loop_length*0.3;
|
||||
|
||||
@ -799,7 +802,9 @@ sub detect_surfaces_type {
|
||||
# fill_surfaces but we only turn them into VOID surfaces, thus preserving the boundaries.
|
||||
sub clip_fill_surfaces {
|
||||
my $self = shift;
|
||||
return unless $self->config->infill_only_where_needed;
|
||||
# sanity check for incompatible options:
|
||||
# spiral_vase and infill_only_where_needed
|
||||
return unless $self->config->infill_only_where_needed and not $self->config->spiral_vase;
|
||||
|
||||
# We only want infill under ceilings; this is almost like an
|
||||
# internal support material.
|
||||
|
@ -296,7 +296,7 @@ $j
|
||||
(default: 100,100)
|
||||
--z-offset Additional height in mm to add to vertical coordinates
|
||||
(+/-, default: $config->{z_offset})
|
||||
--gcode-flavor The type of G-code to generate (reprap/teacup/makerware/sailfish/mach3/machinekit/no-extrusion,
|
||||
--gcode-flavor The type of G-code to generate (reprap/teacup/makerware/sailfish/mach3/machinekit/smoothie/no-extrusion,
|
||||
default: $config->{gcode_flavor})
|
||||
--use-relative-e-distances Enable this to get relative E values (default: no)
|
||||
--use-firmware-retraction Enable firmware-controlled retraction using G10/G11 (default: no)
|
||||
|
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)
|
@ -130,7 +130,7 @@ use Slic3r::Test;
|
||||
});
|
||||
my $convex_hull = convex_hull(\@extrusion_points);
|
||||
my $hull_perimeter = unscale($convex_hull->split_at_first_point->length);
|
||||
ok $skirt_length > $hull_perimeter, 'skirt lenght is large enough to contain object with support';
|
||||
ok $skirt_length > $hull_perimeter, 'skirt length is large enough to contain object with support';
|
||||
}
|
||||
|
||||
{
|
||||
|
1
utils/autorun.bat
Normal file
1
utils/autorun.bat
Normal file
@ -0,0 +1 @@
|
||||
@perl5.22.2.exe slic3r.pl %*
|
19
utils/modifier_helpers/layer_generator.jscad
Normal file
19
utils/modifier_helpers/layer_generator.jscad
Normal file
@ -0,0 +1,19 @@
|
||||
// title: Layer_generator
|
||||
// written by: Joseph Lenox
|
||||
// Used for generating cubes oriented about the center
|
||||
// for making simple modifier meshes.
|
||||
|
||||
var width = 100;
|
||||
var layer_height = 0.3;
|
||||
var z = 30;
|
||||
function main() {
|
||||
|
||||
return cube(size=[width,width,layer_height], center=true).translate([0,0,z]);
|
||||
}
|
||||
function getParameterDefinitions() {
|
||||
return [
|
||||
{ name: 'width', type: 'float', initial: 100, caption: "Width of the cube:" },
|
||||
{ name: 'layer_height', type: 'float', initial: 0.3, caption: "Layer height used:" },
|
||||
{ name: 'z', type: 'float', initial: 0, caption: "Z:" }
|
||||
];
|
||||
}
|
223
utils/package_win32.ps1
Normal file
223
utils/package_win32.ps1
Normal file
@ -0,0 +1,223 @@
|
||||
# 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}\c\bin\libglut-0_.dll;libglut-0_.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"
|
13
utils/post-processing/fan_kickstart.py
Normal file
13
utils/post-processing/fan_kickstart.py
Normal file
@ -0,0 +1,13 @@
|
||||
#!/usr/bin/python
|
||||
import sys
|
||||
import re
|
||||
|
||||
sea = re.compile("M106 S[1-9]+[0-9]*")
|
||||
rep = re.compile("M106 S255\n\g<0>")
|
||||
out = open(sys.argv[1]+"_fixed", 'w')
|
||||
with open(sys.argv[1]) as f:
|
||||
for r in f:
|
||||
if re.search(sea, r) is not None:
|
||||
out.write(re.sub(sea,"M106 S255\n\g<0>",r))
|
||||
else:
|
||||
out.write(r)
|
@ -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
|
||||
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)
|
||||
$have_boost = 1
|
||||
|
@ -38,6 +38,8 @@ src/libslic3r/GCodeWriter.cpp
|
||||
src/libslic3r/GCodeWriter.hpp
|
||||
src/libslic3r/Geometry.cpp
|
||||
src/libslic3r/Geometry.hpp
|
||||
src/libslic3r/IO.cpp
|
||||
src/libslic3r/IO.hpp
|
||||
src/libslic3r/Layer.cpp
|
||||
src/libslic3r/Layer.hpp
|
||||
src/libslic3r/LayerRegion.cpp
|
||||
|
@ -234,7 +234,7 @@ stl_write_vrml(stl_file *stl, char *file) {
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
void stl_write_obj (stl_file *stl, char *file) {
|
||||
void stl_write_obj (stl_file *stl, const char *file) {
|
||||
int i;
|
||||
FILE* fp;
|
||||
|
||||
|
@ -135,7 +135,7 @@ typedef struct {
|
||||
} stl_file;
|
||||
|
||||
|
||||
extern void stl_open(stl_file *stl, char *file);
|
||||
extern void stl_open(stl_file *stl, const char *file);
|
||||
extern void stl_close(stl_file *stl);
|
||||
extern void stl_stats_out(stl_file *stl, FILE *file, char *input_file);
|
||||
extern void stl_print_edges(stl_file *stl, FILE *file);
|
||||
@ -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_scale_versor(stl_file *stl, float versor[3]);
|
||||
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_y(stl_file *stl, float angle);
|
||||
extern void stl_rotate_z(stl_file *stl, float angle);
|
||||
@ -171,7 +172,7 @@ extern void stl_mirror_xz(stl_file *stl);
|
||||
extern void stl_open_merge(stl_file *stl, char *file);
|
||||
extern void stl_invalidate_shared_vertices(stl_file *stl);
|
||||
extern void stl_generate_shared_vertices(stl_file *stl);
|
||||
extern void stl_write_obj(stl_file *stl, char *file);
|
||||
extern void stl_write_obj(stl_file *stl, const char *file);
|
||||
extern void stl_write_off(stl_file *stl, char *file);
|
||||
extern void stl_write_dxf(stl_file *stl, char *file, char *label);
|
||||
extern void stl_write_vrml(stl_file *stl, char *file);
|
||||
@ -182,7 +183,7 @@ extern void stl_calculate_volume(stl_file *stl);
|
||||
extern void stl_repair(stl_file *stl, int fixall_flag, int exact_flag, int tolerance_flag, float tolerance, int increment_flag, float increment, int nearby_flag, int iterations, int remove_unconnected_flag, int fill_holes_flag, int normal_directions_flag, int normal_values_flag, int reverse_all_flag, int verbose_flag);
|
||||
|
||||
extern void stl_initialize(stl_file *stl);
|
||||
extern void stl_count_facets(stl_file *stl, char *file);
|
||||
extern void stl_count_facets(stl_file *stl, const char *file);
|
||||
extern void stl_allocate(stl_file *stl);
|
||||
extern void stl_read(stl_file *stl, int first_facet, int first);
|
||||
extern void stl_facet_stats(stl_file *stl, stl_facet facet, int first);
|
||||
|
@ -34,7 +34,7 @@
|
||||
#endif
|
||||
|
||||
void
|
||||
stl_open(stl_file *stl, char *file) {
|
||||
stl_open(stl_file *stl, const char *file) {
|
||||
stl_initialize(stl);
|
||||
stl_count_facets(stl, file);
|
||||
stl_allocate(stl);
|
||||
@ -65,7 +65,7 @@ stl_initialize(stl_file *stl) {
|
||||
}
|
||||
|
||||
void
|
||||
stl_count_facets(stl_file *stl, char *file) {
|
||||
stl_count_facets(stl_file *stl, const char *file) {
|
||||
long file_size;
|
||||
int header_num_facets;
|
||||
int num_facets;
|
||||
|
@ -170,7 +170,7 @@ stl_scale(stl_file *stl, float factor) {
|
||||
stl_scale_versor(stl, versor);
|
||||
}
|
||||
|
||||
static void calculate_normals(stl_file *stl) {
|
||||
void calculate_normals(stl_file *stl) {
|
||||
long i;
|
||||
float normal[3];
|
||||
|
||||
|
@ -219,4 +219,13 @@ BoundingBox3Base<PointClass>::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 offset(coordf_t delta);
|
||||
PointClass center() const;
|
||||
bool contains(const PointClass &point) const;
|
||||
};
|
||||
|
||||
template <class PointClass>
|
||||
|
@ -222,6 +222,18 @@ offset_ex(const Slic3r::Polygons &polygons, const float delta,
|
||||
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
|
||||
offset2(const Slic3r::Polygons &polygons, ClipperLib::Paths* retval, const float delta1,
|
||||
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,
|
||||
double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
||||
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,
|
||||
const float delta2, double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
||||
|
@ -1,6 +1,15 @@
|
||||
#include "Config.hpp"
|
||||
#include <stdlib.h> // for setenv()
|
||||
#include <assert.h>
|
||||
#include <ctime>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <exception> // std::runtime_error
|
||||
#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)
|
||||
#define setenv(k, v, o) _putenv_s(k, v)
|
||||
@ -20,12 +29,22 @@ operator!= (const ConfigOption &a, const ConfigOption &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 (it->second.default_value != NULL)
|
||||
delete it->second.default_value;
|
||||
}
|
||||
if (other.default_value != NULL)
|
||||
this->default_value = other.default_value->clone();
|
||||
}
|
||||
|
||||
ConfigOptionDef::~ConfigOptionDef()
|
||||
{
|
||||
if (this->default_value != NULL)
|
||||
delete this->default_value;
|
||||
}
|
||||
|
||||
ConfigOptionDef*
|
||||
@ -43,6 +62,12 @@ ConfigDef::get(const t_config_option_key &opt_key) const
|
||||
return &const_cast<ConfigDef*>(this)->options[opt_key];
|
||||
}
|
||||
|
||||
void
|
||||
ConfigDef::merge(const ConfigDef &other)
|
||||
{
|
||||
this->options.insert(other.options.begin(), other.options.end());
|
||||
}
|
||||
|
||||
bool
|
||||
ConfigBase::has(const t_config_option_key &opt_key) {
|
||||
return (this->option(opt_key, false) != NULL);
|
||||
@ -98,9 +123,9 @@ ConfigBase::serialize(const t_config_option_key &opt_key) const {
|
||||
}
|
||||
|
||||
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);
|
||||
if (optdef == NULL) throw "Calling set_deserialize() on unknown option";
|
||||
if (optdef == NULL) throw UnknownOptionException();
|
||||
if (!optdef->shortcut.empty()) {
|
||||
for (std::vector<t_config_option_key>::const_iterator it = optdef->shortcut.begin(); it != optdef->shortcut.end(); ++it) {
|
||||
if (!this->set_deserialize(*it, str)) return false;
|
||||
@ -110,7 +135,7 @@ ConfigBase::set_deserialize(const t_config_option_key &opt_key, std::string str)
|
||||
|
||||
ConfigOption* opt = this->option(opt_key, true);
|
||||
assert(opt != NULL);
|
||||
return opt->deserialize(str);
|
||||
return opt->deserialize(str, append);
|
||||
}
|
||||
|
||||
double
|
||||
@ -171,6 +196,42 @@ ConfigBase::option(const t_config_option_key &opt_key, bool 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) {
|
||||
try {
|
||||
this->set_deserialize(v.first.c_str(), v.second.get_value<std::string>().c_str());
|
||||
} catch (UnknownOptionException &e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
this->swap(other);
|
||||
@ -200,9 +261,11 @@ DynamicConfig::optptr(const t_config_option_key &opt_key, bool create) {
|
||||
if (this->options.count(opt_key) == 0) {
|
||||
if (create) {
|
||||
const ConfigOptionDef* optdef = this->def->get(opt_key);
|
||||
assert(optdef != NULL);
|
||||
if (optdef == NULL) return NULL;
|
||||
ConfigOption* opt;
|
||||
if (optdef->type == coFloat) {
|
||||
if (optdef->default_value != NULL) {
|
||||
opt = optdef->default_value->clone();
|
||||
} else if (optdef->type == coFloat) {
|
||||
opt = new ConfigOptionFloat ();
|
||||
} else if (optdef->type == coFloats) {
|
||||
opt = new ConfigOptionFloats ();
|
||||
@ -265,6 +328,58 @@ DynamicConfig::erase(const t_config_option_key &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
|
||||
StaticConfig::set_defaults()
|
||||
{
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <climits>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <exception>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
@ -20,13 +21,15 @@ typedef std::vector<std::string> t_config_option_keys;
|
||||
class ConfigOption {
|
||||
public:
|
||||
virtual ~ConfigOption() {};
|
||||
virtual ConfigOption* clone() 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 int getInt() const { return 0; };
|
||||
virtual double getFloat() const { return 0; };
|
||||
virtual bool getBool() const { return false; };
|
||||
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);
|
||||
};
|
||||
@ -54,8 +57,10 @@ template <class T>
|
||||
class ConfigOptionVector : public ConfigOptionVectorBase
|
||||
{
|
||||
public:
|
||||
virtual ~ConfigOptionVector() {};
|
||||
std::vector<T> values;
|
||||
ConfigOptionVector() {};
|
||||
ConfigOptionVector(const std::vector<T> _values) : values(_values) {};
|
||||
virtual ~ConfigOptionVector() {};
|
||||
|
||||
void set(const ConfigOption &option) {
|
||||
const ConfigOptionVector<T>* other = dynamic_cast< const ConfigOptionVector<T>* >(&option);
|
||||
@ -76,6 +81,7 @@ class ConfigOptionFloat : public ConfigOptionSingle<double>
|
||||
public:
|
||||
ConfigOptionFloat() : ConfigOptionSingle<double>(0) {};
|
||||
ConfigOptionFloat(double _value) : ConfigOptionSingle<double>(_value) {};
|
||||
ConfigOptionFloat* clone() const { return new ConfigOptionFloat(this->value); };
|
||||
|
||||
double getFloat() const { return this->value; };
|
||||
|
||||
@ -85,7 +91,7 @@ class ConfigOptionFloat : public ConfigOptionSingle<double>
|
||||
return ss.str();
|
||||
};
|
||||
|
||||
bool deserialize(std::string str) {
|
||||
bool deserialize(std::string str, bool append = false) {
|
||||
std::istringstream iss(str);
|
||||
iss >> this->value;
|
||||
return !iss.fail();
|
||||
@ -95,6 +101,9 @@ class ConfigOptionFloat : public ConfigOptionSingle<double>
|
||||
class ConfigOptionFloats : public ConfigOptionVector<double>
|
||||
{
|
||||
public:
|
||||
ConfigOptionFloats() {};
|
||||
ConfigOptionFloats(const std::vector<double> _values) : ConfigOptionVector<double>(_values) {};
|
||||
ConfigOptionFloats* clone() const { return new ConfigOptionFloats(this->values); };
|
||||
|
||||
std::string serialize() const {
|
||||
std::ostringstream ss;
|
||||
@ -115,8 +124,8 @@ class ConfigOptionFloats : public ConfigOptionVector<double>
|
||||
return vv;
|
||||
};
|
||||
|
||||
bool deserialize(std::string str) {
|
||||
this->values.clear();
|
||||
bool deserialize(std::string str, bool append = false) {
|
||||
if (!append) this->values.clear();
|
||||
std::istringstream is(str);
|
||||
std::string item_str;
|
||||
while (std::getline(is, item_str, ',')) {
|
||||
@ -134,6 +143,7 @@ class ConfigOptionInt : public ConfigOptionSingle<int>
|
||||
public:
|
||||
ConfigOptionInt() : ConfigOptionSingle<int>(0) {};
|
||||
ConfigOptionInt(double _value) : ConfigOptionSingle<int>(_value) {};
|
||||
ConfigOptionInt* clone() const { return new ConfigOptionInt(this->value); };
|
||||
|
||||
int getInt() const { return this->value; };
|
||||
void setInt(int val) { this->value = val; };
|
||||
@ -144,7 +154,7 @@ class ConfigOptionInt : public ConfigOptionSingle<int>
|
||||
return ss.str();
|
||||
};
|
||||
|
||||
bool deserialize(std::string str) {
|
||||
bool deserialize(std::string str, bool append = false) {
|
||||
std::istringstream iss(str);
|
||||
iss >> this->value;
|
||||
return !iss.fail();
|
||||
@ -154,6 +164,9 @@ class ConfigOptionInt : public ConfigOptionSingle<int>
|
||||
class ConfigOptionInts : public ConfigOptionVector<int>
|
||||
{
|
||||
public:
|
||||
ConfigOptionInts() {};
|
||||
ConfigOptionInts(const std::vector<int> _values) : ConfigOptionVector<int>(_values) {};
|
||||
ConfigOptionInts* clone() const { return new ConfigOptionInts(this->values); };
|
||||
|
||||
std::string serialize() const {
|
||||
std::ostringstream ss;
|
||||
@ -174,8 +187,8 @@ class ConfigOptionInts : public ConfigOptionVector<int>
|
||||
return vv;
|
||||
};
|
||||
|
||||
bool deserialize(std::string str) {
|
||||
this->values.clear();
|
||||
bool deserialize(std::string str, bool append = false) {
|
||||
if (!append) this->values.clear();
|
||||
std::istringstream is(str);
|
||||
std::string item_str;
|
||||
while (std::getline(is, item_str, ',')) {
|
||||
@ -193,6 +206,9 @@ class ConfigOptionString : public ConfigOptionSingle<std::string>
|
||||
public:
|
||||
ConfigOptionString() : ConfigOptionSingle<std::string>("") {};
|
||||
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 str = this->value;
|
||||
@ -207,7 +223,7 @@ class ConfigOptionString : public ConfigOptionSingle<std::string>
|
||||
return str;
|
||||
};
|
||||
|
||||
bool deserialize(std::string str) {
|
||||
bool deserialize(std::string str, bool append = false) {
|
||||
// s/\\n/\n/g
|
||||
size_t pos = 0;
|
||||
while ((pos = str.find("\\n", pos)) != std::string::npos) {
|
||||
@ -224,6 +240,9 @@ class ConfigOptionString : public ConfigOptionSingle<std::string>
|
||||
class ConfigOptionStrings : public ConfigOptionVector<std::string>
|
||||
{
|
||||
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::ostringstream ss;
|
||||
@ -238,8 +257,8 @@ class ConfigOptionStrings : public ConfigOptionVector<std::string>
|
||||
return this->values;
|
||||
};
|
||||
|
||||
bool deserialize(std::string str) {
|
||||
this->values.clear();
|
||||
bool deserialize(std::string str, bool append = false) {
|
||||
if (!append) this->values.clear();
|
||||
std::istringstream is(str);
|
||||
std::string item_str;
|
||||
while (std::getline(is, item_str, ';')) {
|
||||
@ -254,6 +273,7 @@ class ConfigOptionPercent : public ConfigOptionFloat
|
||||
public:
|
||||
ConfigOptionPercent() : ConfigOptionFloat(0) {};
|
||||
ConfigOptionPercent(double _value) : ConfigOptionFloat(_value) {};
|
||||
ConfigOptionPercent* clone() const { return new ConfigOptionPercent(this->value); };
|
||||
|
||||
double get_abs_value(double ratio_over) const {
|
||||
return ratio_over * this->value / 100;
|
||||
@ -267,7 +287,7 @@ class ConfigOptionPercent : public ConfigOptionFloat
|
||||
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
|
||||
std::istringstream iss(str);
|
||||
iss >> this->value;
|
||||
@ -282,6 +302,7 @@ class ConfigOptionFloatOrPercent : public ConfigOptionPercent
|
||||
ConfigOptionFloatOrPercent() : ConfigOptionPercent(0), percent(false) {};
|
||||
ConfigOptionFloatOrPercent(double _value, bool _percent)
|
||||
: ConfigOptionPercent(_value), percent(_percent) {};
|
||||
ConfigOptionFloatOrPercent* clone() const { return new ConfigOptionFloatOrPercent(this->value, this->percent); };
|
||||
|
||||
void set(const ConfigOption &option) {
|
||||
const ConfigOptionFloatOrPercent* other = dynamic_cast< const ConfigOptionFloatOrPercent* >(&option);
|
||||
@ -307,7 +328,7 @@ class ConfigOptionFloatOrPercent : public ConfigOptionPercent
|
||||
return s;
|
||||
};
|
||||
|
||||
bool deserialize(std::string str) {
|
||||
bool deserialize(std::string str, bool append = false) {
|
||||
this->percent = str.find_first_of("%") != std::string::npos;
|
||||
std::istringstream iss(str);
|
||||
iss >> this->value;
|
||||
@ -320,6 +341,7 @@ class ConfigOptionPoint : public ConfigOptionSingle<Pointf>
|
||||
public:
|
||||
ConfigOptionPoint() : ConfigOptionSingle<Pointf>(Pointf(0,0)) {};
|
||||
ConfigOptionPoint(Pointf _value) : ConfigOptionSingle<Pointf>(_value) {};
|
||||
ConfigOptionPoint* clone() const { return new ConfigOptionPoint(this->value); };
|
||||
|
||||
std::string serialize() const {
|
||||
std::ostringstream ss;
|
||||
@ -329,7 +351,7 @@ class ConfigOptionPoint : public ConfigOptionSingle<Pointf>
|
||||
return ss.str();
|
||||
};
|
||||
|
||||
bool deserialize(std::string str) {
|
||||
bool deserialize(std::string str, bool append = false) {
|
||||
std::istringstream iss(str);
|
||||
iss >> this->value.x;
|
||||
iss.ignore(std::numeric_limits<std::streamsize>::max(), ',');
|
||||
@ -342,6 +364,9 @@ class ConfigOptionPoint : public ConfigOptionSingle<Pointf>
|
||||
class ConfigOptionPoints : public ConfigOptionVector<Pointf>
|
||||
{
|
||||
public:
|
||||
ConfigOptionPoints() {};
|
||||
ConfigOptionPoints(const std::vector<Pointf> _values) : ConfigOptionVector<Pointf>(_values) {};
|
||||
ConfigOptionPoints* clone() const { return new ConfigOptionPoints(this->values); };
|
||||
|
||||
std::string serialize() const {
|
||||
std::ostringstream ss;
|
||||
@ -364,8 +389,8 @@ class ConfigOptionPoints : public ConfigOptionVector<Pointf>
|
||||
return vv;
|
||||
};
|
||||
|
||||
bool deserialize(std::string str) {
|
||||
this->values.clear();
|
||||
bool deserialize(std::string str, bool append = false) {
|
||||
if (!append) this->values.clear();
|
||||
std::istringstream is(str);
|
||||
std::string point_str;
|
||||
while (std::getline(is, point_str, ',')) {
|
||||
@ -389,6 +414,7 @@ class ConfigOptionBool : public ConfigOptionSingle<bool>
|
||||
public:
|
||||
ConfigOptionBool() : ConfigOptionSingle<bool>(false) {};
|
||||
ConfigOptionBool(bool _value) : ConfigOptionSingle<bool>(_value) {};
|
||||
ConfigOptionBool* clone() const { return new ConfigOptionBool(this->value); };
|
||||
|
||||
bool getBool() const { return this->value; };
|
||||
|
||||
@ -396,7 +422,7 @@ class ConfigOptionBool : public ConfigOptionSingle<bool>
|
||||
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);
|
||||
return true;
|
||||
};
|
||||
@ -405,6 +431,9 @@ class ConfigOptionBool : public ConfigOptionSingle<bool>
|
||||
class ConfigOptionBools : public ConfigOptionVector<bool>
|
||||
{
|
||||
public:
|
||||
ConfigOptionBools() {};
|
||||
ConfigOptionBools(const std::vector<bool> _values) : ConfigOptionVector<bool>(_values) {};
|
||||
ConfigOptionBools* clone() const { return new ConfigOptionBools(this->values); };
|
||||
|
||||
std::string serialize() const {
|
||||
std::ostringstream ss;
|
||||
@ -425,8 +454,8 @@ class ConfigOptionBools : public ConfigOptionVector<bool>
|
||||
return vv;
|
||||
};
|
||||
|
||||
bool deserialize(std::string str) {
|
||||
this->values.clear();
|
||||
bool deserialize(std::string str, bool append = false) {
|
||||
if (!append) this->values.clear();
|
||||
std::istringstream is(str);
|
||||
std::string item_str;
|
||||
while (std::getline(is, item_str, ',')) {
|
||||
@ -445,6 +474,7 @@ class ConfigOptionEnum : public ConfigOptionSingle<T>
|
||||
// by default, use the first value (0) of the T enum type
|
||||
ConfigOptionEnum() : ConfigOptionSingle<T>(static_cast<T>(0)) {};
|
||||
ConfigOptionEnum(T _value) : ConfigOptionSingle<T>(_value) {};
|
||||
ConfigOptionEnum<T>* clone() const { return new ConfigOptionEnum<T>(this->value); };
|
||||
|
||||
std::string serialize() const {
|
||||
t_config_enum_values enum_keys_map = ConfigOptionEnum<T>::get_enum_values();
|
||||
@ -454,7 +484,7 @@ class ConfigOptionEnum : public ConfigOptionSingle<T>
|
||||
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();
|
||||
if (enum_keys_map.count(str) == 0) return false;
|
||||
this->value = static_cast<T>(enum_keys_map[str]);
|
||||
@ -478,7 +508,7 @@ class ConfigOptionEnumGeneric : public ConfigOptionInt
|
||||
return "";
|
||||
};
|
||||
|
||||
bool deserialize(std::string str) {
|
||||
bool deserialize(std::string str, bool append = false) {
|
||||
if (this->keys_map->count(str) == 0) return false;
|
||||
this->value = (*const_cast<t_config_enum_values*>(this->keys_map))[str];
|
||||
return true;
|
||||
@ -532,6 +562,11 @@ class ConfigOptionDef
|
||||
ConfigOptionDef() : type(coNone), default_value(NULL),
|
||||
multiline(false), full_width(false), readonly(false),
|
||||
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;
|
||||
@ -540,9 +575,9 @@ class ConfigDef
|
||||
{
|
||||
public:
|
||||
t_optiondef_map options;
|
||||
~ConfigDef();
|
||||
ConfigOptionDef* add(const t_config_option_key &opt_key, ConfigOptionType type);
|
||||
const ConfigOptionDef* get(const t_config_option_key &opt_key) const;
|
||||
void merge(const ConfigDef &other);
|
||||
};
|
||||
|
||||
class ConfigBase
|
||||
@ -551,6 +586,7 @@ class ConfigBase
|
||||
const ConfigDef* def;
|
||||
|
||||
ConfigBase() : def(NULL) {};
|
||||
ConfigBase(const ConfigDef* def) : def(def) {};
|
||||
virtual ~ConfigBase() {};
|
||||
bool has(const t_config_option_key &opt_key);
|
||||
const ConfigOption* option(const t_config_option_key &opt_key) const;
|
||||
@ -561,16 +597,19 @@ class ConfigBase
|
||||
bool equals(ConfigBase &other);
|
||||
t_config_option_keys diff(ConfigBase &other);
|
||||
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 ratio_over);
|
||||
void setenv_();
|
||||
void load(const std::string &file);
|
||||
void save(const std::string &file) const;
|
||||
};
|
||||
|
||||
class DynamicConfig : public virtual ConfigBase
|
||||
{
|
||||
public:
|
||||
DynamicConfig() {};
|
||||
DynamicConfig(const ConfigDef* def) : ConfigBase(def) {};
|
||||
DynamicConfig(const DynamicConfig& other);
|
||||
DynamicConfig& operator= (DynamicConfig other);
|
||||
void swap(DynamicConfig &other);
|
||||
@ -579,6 +618,7 @@ class DynamicConfig : public virtual ConfigBase
|
||||
virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false);
|
||||
t_config_option_keys keys() const;
|
||||
void erase(const t_config_option_key &opt_key);
|
||||
void read_cli(const int argc, const char **argv, t_config_option_keys* extra);
|
||||
|
||||
private:
|
||||
typedef std::map<t_config_option_key,ConfigOption*> t_options_map;
|
||||
@ -594,6 +634,8 @@ class StaticConfig : public virtual ConfigBase
|
||||
void set_defaults();
|
||||
};
|
||||
|
||||
class UnknownOptionException : public std::exception {};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -4,6 +4,8 @@
|
||||
#include <cstdlib>
|
||||
#include <math.h>
|
||||
|
||||
#define FLAVOR_IS(val) this->config.gcode_flavor == val
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
AvoidCrossingPerimeters::AvoidCrossingPerimeters()
|
||||
@ -630,17 +632,12 @@ 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)
|
||||
Lines lines = travel.lines();
|
||||
double path_length = 0;
|
||||
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);
|
||||
}
|
||||
|
||||
for (Lines::const_iterator line = lines.begin(); line != lines.end(); ++line)
|
||||
gcode += this->writer.travel_to_xy(this->point_to_gcode(line->b), comment);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -700,8 +697,8 @@ GCode::retract(bool toolchange)
|
||||
methods even if we performed wipe, since this will ensure the entire retraction
|
||||
length is honored in case wipe path was too short. */
|
||||
gcode += toolchange ? this->writer.retract_for_toolchange() : this->writer.retract();
|
||||
|
||||
gcode += this->writer.reset_e();
|
||||
if (!(FLAVOR_IS(gcfSmoothie) && this->config.use_firmware_retraction))
|
||||
gcode += this->writer.reset_e();
|
||||
if (this->writer.extruder()->retract_length() > 0 || this->config.use_firmware_retraction)
|
||||
gcode += this->writer.lift();
|
||||
|
||||
|
@ -70,6 +70,10 @@ GCodeSender::connect(std::string devname, unsigned int baud_rate)
|
||||
this->open = true;
|
||||
this->reset();
|
||||
|
||||
// a reset firmware expect line numbers to start again from 1
|
||||
this->sent = 0;
|
||||
this->last_sent.clear();
|
||||
|
||||
/* Initialize debugger */
|
||||
#ifdef DEBUG_SERIAL
|
||||
fs.open("serial.txt", std::fstream::out | std::fstream::trunc);
|
||||
@ -153,6 +157,7 @@ GCodeSender::disconnect()
|
||||
*/
|
||||
|
||||
#ifdef DEBUG_SERIAL
|
||||
fs << "DISCONNECTED" << std::endl << std::flush;
|
||||
fs.close();
|
||||
#endif
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ GCodeWriter::preamble()
|
||||
gcode << "G21 ; set units to millimeters\n";
|
||||
gcode << "G90 ; use absolute coordinates\n";
|
||||
}
|
||||
if (FLAVOR_IS(gcfRepRap) || FLAVOR_IS(gcfTeacup)) {
|
||||
if (FLAVOR_IS(gcfRepRap) || FLAVOR_IS(gcfTeacup) || FLAVOR_IS(gcfSmoothie)) {
|
||||
if (this->config.use_relative_e_distances) {
|
||||
gcode << "M83 ; use relative distances for extrusion\n";
|
||||
} else {
|
||||
@ -441,7 +441,7 @@ GCodeWriter::_retract(double length, double restart_extra, const std::string &co
|
||||
length = length * area;
|
||||
restart_extra = restart_extra * area;
|
||||
}
|
||||
|
||||
|
||||
double dE = this->_extruder->retract(length, restart_extra);
|
||||
if (dE != 0) {
|
||||
if (this->config.use_firmware_retraction) {
|
||||
|
82
xs/src/libslic3r/IO.cpp
Normal file
82
xs/src/libslic3r/IO.cpp
Normal file
@ -0,0 +1,82 @@
|
||||
#include "IO.hpp"
|
||||
#include <stdexcept>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
namespace Slic3r { namespace IO {
|
||||
|
||||
bool
|
||||
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: check that file exists
|
||||
|
||||
TriangleMesh mesh;
|
||||
if (!STL::read(input_file, &mesh)) return false;
|
||||
|
||||
if (mesh.facets_count() == 0)
|
||||
throw std::runtime_error("This STL file couldn't be read because it's empty.");
|
||||
|
||||
ModelObject* object = model->add_object();
|
||||
object->name = input_file; // TODO: use basename()
|
||||
object->input_file = input_file;
|
||||
|
||||
ModelVolume* volume = object->add_volume(mesh);
|
||||
volume->name = input_file; // TODO: use basename()
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
STL::write(TriangleMesh& mesh, std::string output_file, bool binary)
|
||||
{
|
||||
if (binary) {
|
||||
mesh.write_binary(output_file);
|
||||
} else {
|
||||
mesh.write_ascii(output_file);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
OBJ::write(TriangleMesh& mesh, std::string output_file)
|
||||
{
|
||||
mesh.WriteOBJFile(output_file);
|
||||
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;
|
||||
}
|
||||
|
||||
} }
|
33
xs/src/libslic3r/IO.hpp
Normal file
33
xs/src/libslic3r/IO.hpp
Normal file
@ -0,0 +1,33 @@
|
||||
#ifndef slic3r_IO_hpp_
|
||||
#define slic3r_IO_hpp_
|
||||
|
||||
#include "libslic3r.h"
|
||||
#include "Model.hpp"
|
||||
#include "TriangleMesh.hpp"
|
||||
#include <string>
|
||||
|
||||
namespace Slic3r { namespace IO {
|
||||
|
||||
class STL
|
||||
{
|
||||
public:
|
||||
static bool read(std::string input_file, TriangleMesh* mesh);
|
||||
static bool read(std::string input_file, Model* model);
|
||||
static bool write(TriangleMesh& mesh, std::string output_file, bool binary = true);
|
||||
};
|
||||
|
||||
class OBJ
|
||||
{
|
||||
public:
|
||||
static bool write(TriangleMesh& mesh, std::string output_file);
|
||||
};
|
||||
|
||||
class POV
|
||||
{
|
||||
public:
|
||||
static bool write(TriangleMesh& mesh, std::string output_file);
|
||||
};
|
||||
|
||||
} }
|
||||
|
||||
#endif
|
@ -1,5 +1,7 @@
|
||||
#include "Model.hpp"
|
||||
#include "Geometry.hpp"
|
||||
#include <iostream>
|
||||
#include "boost/filesystem.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
@ -159,6 +161,13 @@ Model::bounding_box() const
|
||||
return bb;
|
||||
}
|
||||
|
||||
void
|
||||
Model::repair()
|
||||
{
|
||||
for (ModelObjectPtrs::const_iterator o = this->objects.begin(); o != this->objects.end(); ++o)
|
||||
(*o)->repair();
|
||||
}
|
||||
|
||||
void
|
||||
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, const ModelMaterial &other)
|
||||
: attributes(other.attributes), config(other.config), model(model)
|
||||
@ -471,6 +487,13 @@ ModelObject::update_bounding_box()
|
||||
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
|
||||
TriangleMesh
|
||||
ModelObject::mesh() const
|
||||
@ -479,7 +502,7 @@ ModelObject::mesh() const
|
||||
TriangleMesh raw_mesh = this->raw_mesh();
|
||||
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
void
|
||||
ModelObject::scale(float factor)
|
||||
{
|
||||
this->scale(Pointf3(factor, factor, factor));
|
||||
}
|
||||
|
||||
void
|
||||
ModelObject::scale(const Pointf3 &versor)
|
||||
{
|
||||
@ -701,6 +730,49 @@ ModelObject::split(ModelObjectPtrs* new_objects)
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
ModelObject::print_info() const
|
||||
{
|
||||
using namespace std;
|
||||
cout << fixed;
|
||||
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)
|
||||
: mesh(mesh), modifier(false), object(object)
|
||||
@ -713,6 +785,21 @@ ModelVolume::ModelVolume(ModelObject* object, const ModelVolume &other)
|
||||
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
|
||||
ModelVolume::material_id() const
|
||||
{
|
||||
@ -760,6 +847,20 @@ ModelInstance::ModelInstance(ModelObject *object, const ModelInstance &other)
|
||||
: 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
|
||||
ModelInstance::transform_mesh(TriangleMesh* mesh, bool dont_translate) const
|
||||
{
|
||||
|
@ -51,6 +51,7 @@ class Model
|
||||
bool has_objects_with_no_instances() const;
|
||||
bool add_default_instances();
|
||||
BoundingBoxf3 bounding_box() const;
|
||||
void repair();
|
||||
void center_instances_around_point(const Pointf &point);
|
||||
void align_instances_to_origin();
|
||||
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_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 print_info() const;
|
||||
};
|
||||
|
||||
class ModelMaterial
|
||||
@ -117,6 +119,7 @@ class ModelObject
|
||||
BoundingBoxf3 bounding_box();
|
||||
void invalidate_bounding_box();
|
||||
|
||||
void repair();
|
||||
TriangleMesh mesh() const;
|
||||
TriangleMesh raw_mesh() const;
|
||||
BoundingBoxf3 raw_bounding_box() const;
|
||||
@ -124,6 +127,7 @@ class ModelObject
|
||||
void center_around_origin();
|
||||
void translate(const Vectorf3 &vector);
|
||||
void translate(coordf_t x, coordf_t y, coordf_t z);
|
||||
void scale(float factor);
|
||||
void scale(const Pointf3 &versor);
|
||||
void rotate(float angle, const Axis &axis);
|
||||
void mirror(const Axis &axis);
|
||||
@ -133,6 +137,7 @@ class ModelObject
|
||||
void cut(coordf_t z, Model* model) const;
|
||||
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 print_info() const;
|
||||
|
||||
private:
|
||||
Model* model;
|
||||
@ -167,6 +172,8 @@ class ModelVolume
|
||||
|
||||
ModelVolume(ModelObject *object, const TriangleMesh &mesh);
|
||||
ModelVolume(ModelObject *object, const ModelVolume &other);
|
||||
ModelVolume& operator= (ModelVolume other);
|
||||
void swap(ModelVolume &other);
|
||||
};
|
||||
|
||||
class ModelInstance
|
||||
@ -186,6 +193,8 @@ class ModelInstance
|
||||
|
||||
ModelInstance(ModelObject *object);
|
||||
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()
|
||||
// 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 '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);
|
||||
|
||||
if (path.polyline.points.empty()) {
|
||||
path.polyline.append(line.a);
|
||||
path.polyline.append(line.b);
|
||||
|
||||
flow.width = unscale(w);
|
||||
#ifdef SLIC3R_DEBUG
|
||||
printf(" filling %f gap\n", flow.width);
|
||||
#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();
|
||||
if (path.mm3_per_mm < EPSILON) continue;
|
||||
|
||||
path.width = flow.width;
|
||||
path.height = flow.height;
|
||||
path.polyline.append(line.a);
|
||||
path.polyline.append(line.b);
|
||||
} else {
|
||||
thickness_delta = fabs(scale_(flow.width) - w);
|
||||
if (thickness_delta <= tolerance) {
|
||||
|
@ -483,6 +483,7 @@ PrintConfigDef::PrintConfigDef()
|
||||
def->enum_values.push_back("sailfish");
|
||||
def->enum_values.push_back("mach3");
|
||||
def->enum_values.push_back("machinekit");
|
||||
def->enum_values.push_back("smoothie");
|
||||
def->enum_values.push_back("no-extrusion");
|
||||
def->enum_labels.push_back("RepRap (Marlin/Sprinter/Repetier)");
|
||||
def->enum_labels.push_back("Teacup");
|
||||
@ -490,6 +491,7 @@ PrintConfigDef::PrintConfigDef()
|
||||
def->enum_labels.push_back("Sailfish (MakerBot)");
|
||||
def->enum_labels.push_back("Mach3/LinuxCNC");
|
||||
def->enum_labels.push_back("Machinekit");
|
||||
def->enum_labels.push_back("Smoothieware");
|
||||
def->enum_labels.push_back("No extrusion");
|
||||
def->default_value = new ConfigOptionEnum<GCodeFlavor>(gcfRepRap);
|
||||
|
||||
@ -799,6 +801,15 @@ PrintConfigDef::PrintConfigDef()
|
||||
def->min = 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->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.";
|
||||
@ -1060,7 +1071,7 @@ PrintConfigDef::PrintConfigDef()
|
||||
|
||||
def = this->add("standby_temperature_delta", coInt);
|
||||
def->label = "Temperature variation";
|
||||
def->tooltip = "Temperature difference to be applied when an extruder is not active.";
|
||||
def->tooltip = "Temperature difference to be applied when an extruder is not active. Enables a full-height \"sacrificial\" skirt on which the nozzles are periodically wiped.";
|
||||
def->sidetext = "∆°C";
|
||||
def->cli = "standby-temperature-delta=i";
|
||||
def->min = -500;
|
||||
@ -1069,7 +1080,7 @@ PrintConfigDef::PrintConfigDef()
|
||||
|
||||
def = this->add("start_gcode", coString);
|
||||
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->multiline = true;
|
||||
def->full_width = true;
|
||||
@ -1387,4 +1398,67 @@ PrintConfigBase::min_object_distance() const
|
||||
: 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;
|
||||
|
||||
}
|
||||
|
@ -9,7 +9,7 @@
|
||||
namespace Slic3r {
|
||||
|
||||
enum GCodeFlavor {
|
||||
gcfRepRap, gcfTeacup, gcfMakerWare, gcfSailfish, gcfMach3, gcfMachinekit, gcfNoExtrusion,
|
||||
gcfRepRap, gcfTeacup, gcfMakerWare, gcfSailfish, gcfMach3, gcfMachinekit, gcfNoExtrusion, gcfSmoothie,
|
||||
};
|
||||
|
||||
enum InfillPattern {
|
||||
@ -34,6 +34,7 @@ template<> inline t_config_enum_values ConfigOptionEnum<GCodeFlavor>::get_enum_v
|
||||
keys_map["mach3"] = gcfMach3;
|
||||
keys_map["machinekit"] = gcfMachinekit;
|
||||
keys_map["no-extrusion"] = gcfNoExtrusion;
|
||||
keys_map["smoothie"] = gcfSmoothie;
|
||||
return keys_map;
|
||||
}
|
||||
|
||||
@ -129,9 +130,10 @@ class PrintObjectConfig : public virtual StaticPrintConfig
|
||||
ConfigOptionInt support_material_threshold;
|
||||
ConfigOptionFloat xy_size_compensation;
|
||||
|
||||
PrintObjectConfig() : StaticPrintConfig() {
|
||||
this->set_defaults();
|
||||
};
|
||||
PrintObjectConfig(bool initialize = true) : StaticPrintConfig() {
|
||||
if (initialize)
|
||||
this->set_defaults();
|
||||
}
|
||||
|
||||
virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) {
|
||||
OPT_PTR(adaptive_slicing);
|
||||
@ -201,9 +203,10 @@ class PrintRegionConfig : public virtual StaticPrintConfig
|
||||
ConfigOptionInt top_solid_layers;
|
||||
ConfigOptionFloatOrPercent top_solid_infill_speed;
|
||||
|
||||
PrintRegionConfig() : StaticPrintConfig() {
|
||||
this->set_defaults();
|
||||
};
|
||||
PrintRegionConfig(bool initialize = true) : StaticPrintConfig() {
|
||||
if (initialize)
|
||||
this->set_defaults();
|
||||
}
|
||||
|
||||
virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) {
|
||||
OPT_PTR(bottom_solid_layers);
|
||||
@ -272,9 +275,10 @@ class GCodeConfig : public virtual StaticPrintConfig
|
||||
ConfigOptionBool use_relative_e_distances;
|
||||
ConfigOptionBool use_volumetric_e;
|
||||
|
||||
GCodeConfig() : StaticPrintConfig() {
|
||||
this->set_defaults();
|
||||
};
|
||||
GCodeConfig(bool initialize = true) : StaticPrintConfig() {
|
||||
if (initialize)
|
||||
this->set_defaults();
|
||||
}
|
||||
|
||||
virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) {
|
||||
OPT_PTR(before_layer_gcode);
|
||||
@ -374,9 +378,10 @@ class PrintConfig : public GCodeConfig
|
||||
ConfigOptionBools wipe;
|
||||
ConfigOptionFloat z_offset;
|
||||
|
||||
PrintConfig() : GCodeConfig() {
|
||||
this->set_defaults();
|
||||
};
|
||||
PrintConfig(bool initialize = true) : GCodeConfig(false) {
|
||||
if (initialize)
|
||||
this->set_defaults();
|
||||
}
|
||||
|
||||
virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) {
|
||||
OPT_PTR(avoid_crossing_perimeters);
|
||||
@ -448,9 +453,10 @@ class HostConfig : public virtual StaticPrintConfig
|
||||
ConfigOptionString serial_port;
|
||||
ConfigOptionInt serial_speed;
|
||||
|
||||
HostConfig() : StaticPrintConfig() {
|
||||
this->set_defaults();
|
||||
};
|
||||
HostConfig(bool initialize = true) : StaticPrintConfig() {
|
||||
if (initialize)
|
||||
this->set_defaults();
|
||||
}
|
||||
|
||||
virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) {
|
||||
OPT_PTR(octoprint_host);
|
||||
@ -466,6 +472,16 @@ class FullPrintConfig
|
||||
: public PrintObjectConfig, public PrintRegionConfig, public PrintConfig, public HostConfig
|
||||
{
|
||||
public:
|
||||
FullPrintConfig(bool initialize = true) :
|
||||
PrintObjectConfig(false),
|
||||
PrintRegionConfig(false),
|
||||
PrintConfig(false),
|
||||
HostConfig(false)
|
||||
{
|
||||
if (initialize)
|
||||
this->set_defaults();
|
||||
}
|
||||
|
||||
virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) {
|
||||
ConfigOption* opt;
|
||||
if ((opt = PrintObjectConfig::optptr(opt_key, create)) != NULL) return opt;
|
||||
@ -476,6 +492,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
|
||||
|
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)
|
||||
{
|
||||
this->swap(other);
|
||||
swap(*this, other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void
|
||||
TriangleMesh::swap(TriangleMesh &other)
|
||||
TriangleMesh::swap(TriangleMesh &first, TriangleMesh &second)
|
||||
{
|
||||
std::swap(this->stl, other.stl);
|
||||
std::swap(this->repaired, other.repaired);
|
||||
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);
|
||||
std::swap(first.repaired, second.repaired);
|
||||
std::swap(first.stl, second.stl);
|
||||
}
|
||||
|
||||
TriangleMesh::~TriangleMesh() {
|
||||
@ -70,20 +66,20 @@ TriangleMesh::~TriangleMesh() {
|
||||
}
|
||||
|
||||
void
|
||||
TriangleMesh::ReadSTLFile(char* input_file) {
|
||||
stl_open(&stl, input_file);
|
||||
TriangleMesh::ReadSTLFile(const std::string &input_file) {
|
||||
stl_open(&stl, input_file.c_str());
|
||||
}
|
||||
|
||||
void
|
||||
TriangleMesh::write_ascii(char* output_file)
|
||||
TriangleMesh::write_ascii(const std::string &output_file)
|
||||
{
|
||||
stl_write_ascii(&this->stl, output_file, "");
|
||||
stl_write_ascii(&this->stl, output_file.c_str(), "");
|
||||
}
|
||||
|
||||
void
|
||||
TriangleMesh::write_binary(char* output_file)
|
||||
TriangleMesh::write_binary(const std::string &output_file)
|
||||
{
|
||||
stl_write_binary(&this->stl, output_file, "");
|
||||
stl_write_binary(&this->stl, output_file.c_str(), "");
|
||||
}
|
||||
|
||||
void
|
||||
@ -93,6 +89,44 @@ TriangleMesh::repair() {
|
||||
// admesh fails when repairing empty meshes
|
||||
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
|
||||
stl_check_facets_exact(&stl);
|
||||
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
|
||||
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
|
||||
stl_calculate_volume(&stl);
|
||||
|
||||
// neighbors
|
||||
stl_verify_neighbors(&stl);
|
||||
|
||||
this->repaired = true;
|
||||
}
|
||||
|
||||
bool
|
||||
TriangleMesh::is_manifold() const
|
||||
{
|
||||
return this->stl.stats.connected_facets_3_edge == this->stl.stats.number_of_facets;
|
||||
}
|
||||
|
||||
void
|
||||
@ -173,9 +188,9 @@ TriangleMesh::facets_count() const
|
||||
}
|
||||
|
||||
void
|
||||
TriangleMesh::WriteOBJFile(char* output_file) {
|
||||
TriangleMesh::WriteOBJFile(const std::string &output_file) {
|
||||
stl_generate_shared_vertices(&stl);
|
||||
stl_write_obj(&stl, output_file);
|
||||
stl_write_obj(&stl, output_file.c_str());
|
||||
}
|
||||
|
||||
void TriangleMesh::scale(float factor)
|
||||
@ -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)
|
||||
{
|
||||
this->translate(-center->x, -center->y, 0);
|
||||
@ -340,9 +365,8 @@ TriangleMesh::merge(const TriangleMesh &mesh)
|
||||
stl_reallocate(&this->stl);
|
||||
|
||||
// copy facets
|
||||
for (int i = 0; i < mesh.stl.stats.number_of_facets; i++) {
|
||||
this->stl.facet_start[number_of_facets + i] = mesh.stl.facet_start[i];
|
||||
}
|
||||
std::copy(mesh.stl.facet_start, mesh.stl.facet_start + mesh.stl.stats.number_of_facets, this->stl.facet_start + number_of_facets);
|
||||
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
|
||||
stl_get_size(&this->stl);
|
||||
|
@ -22,13 +22,16 @@ class TriangleMesh
|
||||
TriangleMesh();
|
||||
TriangleMesh(const TriangleMesh &other);
|
||||
TriangleMesh& operator= (TriangleMesh other);
|
||||
void swap(TriangleMesh &other);
|
||||
void swap(TriangleMesh &first, TriangleMesh &second);
|
||||
~TriangleMesh();
|
||||
void ReadSTLFile(char* input_file);
|
||||
void write_ascii(char* output_file);
|
||||
void write_binary(char* output_file);
|
||||
void ReadSTLFile(const std::string &input_file);
|
||||
void write_ascii(const std::string &output_file);
|
||||
void write_binary(const std::string &output_file);
|
||||
void repair();
|
||||
void WriteOBJFile(char* output_file);
|
||||
void check_topology();
|
||||
float volume();
|
||||
bool is_manifold() const;
|
||||
void WriteOBJFile(const std::string &output_file);
|
||||
void scale(float factor);
|
||||
void scale(const Pointf3 &versor);
|
||||
void translate(float x, float y, float z);
|
||||
@ -41,6 +44,7 @@ class TriangleMesh
|
||||
void mirror_y();
|
||||
void mirror_z();
|
||||
void align_to_origin();
|
||||
void center_around_origin();
|
||||
void rotate(double angle, Point* center);
|
||||
TriangleMeshPtrs split() const;
|
||||
void merge(const TriangleMesh &mesh);
|
||||
|
17
xs/t/22_config.t
Normal file
17
xs/t/22_config.t
Normal file
@ -0,0 +1,17 @@
|
||||
#!/usr/bin/perl
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use Slic3r::XS;
|
||||
|
||||
use Test::More tests => 1;
|
||||
{
|
||||
use Cwd qw(abs_path);
|
||||
use File::Basename qw(dirname);
|
||||
my $class = Slic3r::Config->new;
|
||||
my $path = abs_path($0);
|
||||
my $config = $class->_load(dirname($path)."/inc/22_config_bad_config_options.ini");
|
||||
ok 1, 'did not crash on reading invalid items in config';
|
||||
}
|
||||
|
||||
__END__
|
7
xs/t/inc/22_config_bad_config_options.ini
Normal file
7
xs/t/inc/22_config_bad_config_options.ini
Normal file
@ -0,0 +1,7 @@
|
||||
# generated by Slic3r 1.1.7 on Tue Aug 19 21:49:50 2014
|
||||
avoid_crossing_perimeters = 1
|
||||
bed_size = 200,180
|
||||
g0 = 0
|
||||
perimeter_acceleration = 0
|
||||
support_material_extruder = 1
|
||||
support_material_extrusion_width = 0
|
@ -16,6 +16,7 @@
|
||||
void scale(double factor);
|
||||
void translate(double x, double y);
|
||||
void offset(double delta);
|
||||
bool contains_point(Point* point) %code{% RETVAL = THIS->contains(*point); %};
|
||||
Clone<Polygon> polygon();
|
||||
Clone<Point> size();
|
||||
Clone<Point> center();
|
||||
@ -49,6 +50,7 @@ new_from_points(CLASS, points)
|
||||
void merge_point(Pointf* point) %code{% THIS->merge(*point); %};
|
||||
void scale(double factor);
|
||||
void translate(double x, double y);
|
||||
bool contains_point(Pointf* point) %code{% RETVAL = THIS->contains(*point); %};
|
||||
Clone<Pointf> size();
|
||||
Clone<Pointf> center();
|
||||
Clone<Pointf> min_point() %code{% RETVAL = THIS->min; %};
|
||||
|
@ -38,6 +38,8 @@
|
||||
void normalize();
|
||||
%name{setenv} void setenv_();
|
||||
double min_object_distance();
|
||||
%name{_load} void load(std::string file);
|
||||
%name{_save} void save(std::string file);
|
||||
};
|
||||
|
||||
%name{Slic3r::Config::Static} class StaticPrintConfig {
|
||||
@ -84,6 +86,8 @@
|
||||
%};
|
||||
%name{setenv} void setenv_();
|
||||
double min_object_distance();
|
||||
%name{_load} void load(std::string file);
|
||||
%name{_save} void save(std::string file);
|
||||
};
|
||||
|
||||
%package{Slic3r::Config};
|
||||
|
@ -71,6 +71,7 @@
|
||||
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_grid(unsigned int x, unsigned int y, double dist);
|
||||
void print_info();
|
||||
};
|
||||
|
||||
|
||||
@ -196,6 +197,8 @@ ModelMaterial::attributes()
|
||||
RETVAL = new ModelObjectPtrs(); // leak?
|
||||
THIS->split(RETVAL);
|
||||
%};
|
||||
|
||||
void print_info();
|
||||
};
|
||||
|
||||
|
||||
|
@ -10,11 +10,13 @@
|
||||
~TriangleMesh();
|
||||
Clone<TriangleMesh> clone()
|
||||
%code{% RETVAL = THIS; %};
|
||||
void ReadSTLFile(char* input_file);
|
||||
void write_ascii(char* output_file);
|
||||
void write_binary(char* output_file);
|
||||
void ReadSTLFile(std::string input_file);
|
||||
void write_ascii(std::string output_file);
|
||||
void write_binary(std::string output_file);
|
||||
void check_topology();
|
||||
void repair();
|
||||
void WriteOBJFile(char* output_file);
|
||||
float volume();
|
||||
void WriteOBJFile(std::string output_file);
|
||||
void scale(float factor);
|
||||
void scale_xyz(Pointf3* versor)
|
||||
%code{% THIS->scale(*versor); %};
|
||||
|
Loading…
x
Reference in New Issue
Block a user