Merge branch 'master' into adaptive-slicing

This commit is contained in:
Florens Wasserfall 2016-10-13 08:05:58 +02:00
commit 22c010791d
61 changed files with 1805 additions and 262 deletions

View File

@ -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
View File

@ -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

View File

@ -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

View File

@ -2,8 +2,9 @@ _Q: Oh cool, a new RepRap slicer?_
A: Yes.
Slic3r [![Build Status](https://travis-ci.org/alexrj/Slic3r.png?branch=master)](https://travis-ci.org/alexrj/Slic3r)
Slic3r [![Build Status](https://travis-ci.org/alexrj/Slic3r.png?branch=master)](https://travis-ci.org/alexrj/Slic3r) [![Build status](https://ci.appveyor.com/api/projects/status/8iqmeat6cj158vo6?svg=true)](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)

View File

@ -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};

View File

@ -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 ]); #]]

View File

@ -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);

View File

@ -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;

View File

@ -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));

View File

@ -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}};

View File

@ -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));

View File

@ -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};

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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) = @_;

View File

@ -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.

View File

@ -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
View 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
View 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
View File

94
src/utils/extrude-tin.cpp Normal file
View 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
View 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)

View File

@ -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
View File

@ -0,0 +1 @@
@perl5.22.2.exe slic3r.pl %*

View 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
View 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"

View 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)

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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];

View File

@ -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;
}

View File

@ -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>

View File

@ -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)

View File

@ -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,

View File

@ -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()
{

View File

@ -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

View File

@ -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();

View File

@ -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
}

View File

@ -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
View 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
View 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

View File

@ -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
{

View File

@ -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);
};
}

View File

@ -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

View File

@ -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) {

View File

@ -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;
}

View File

@ -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

View 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");
}
}

View 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

View File

@ -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);

View File

@ -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
View 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__

View 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

View File

@ -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; %};

View File

@ -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};

View File

@ -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();
};

View File

@ -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); %};