Merge branch 'master' into lift-z-fix

This commit is contained in:
Florens Wasserfall 2016-07-19 09:26:03 +02:00
commit de3de87bb6
44 changed files with 1458 additions and 198 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,7 +2,7 @@ _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

View File

@ -87,8 +87,10 @@ 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($file);
return $config;
}
sub load_ini_hash {
@ -186,13 +188,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,7 +230,7 @@ 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"
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"

View File

@ -214,16 +214,6 @@ sub make_fill {
# calculate flow spacing for infill pattern generation
my $using_internal_flow = 0;
my $infill_combined_flow = $layerm->layer->object->config->layer_height * $layerm->region->config->infill_every_layers; # multiply the required layer height by infill_every_layers
if ($infill_combined_flow == 0) {
# just in case something happens we don't want to put in a 0mm layer.
$infill_combined_flow = $layerm->layer->object->config->layer_height;
}
if ($infill_combined_flow > $flow->nozzle_diameter) {
# can't put out layers bigger than the nozzle width, go as big as possible
$infill_combined_flow = (($flow->nozzle_diameter / $layerm->layer->object->config->layer_height) % 1) * $layerm->layer->object->config->layer_height
}
if (!$is_solid && !$is_bridge) {
# it's internal infill, so we can calculate a generic flow spacing
# for all layers, for avoiding the ugly effect of
@ -231,8 +221,7 @@ sub make_fill {
# layer height
my $internal_flow = $layerm->region->flow(
FLOW_ROLE_INFILL,
$infill_combined_flow,
$layerm->layer->object->config->layer_height, # TODO: handle infill_every_layers?
0, # no bridge
0, # no first layer
-1, # auto width

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

@ -1640,7 +1640,7 @@ sub selection_changed {
$self->{object_info_size}->SetLabel(sprintf("%.2f x %.2f x %.2f", @{$model_object->instance_bounding_box(0)->size}));
$self->{object_info_materials}->SetLabel($model_object->materials_count);
if (my $stats = $model_object->mesh_stats) {
if (my $stats = $model_object->raw_mesh->stats) {
$self->{object_info_volume}->SetLabel(sprintf('%.2f', $stats->{volume} * ($model_instance->scaling_factor**3)));
$self->{object_info_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)})) {

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

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

@ -490,21 +490,18 @@ sub process_layer {
# group extrusions by extruder and then by island
my %by_extruder = (); # extruder_id => [ { perimeters => \@perimeters, infill => \@infill } ]
my $n_slices = $#{$layer->slices};
my @layer_surface_bboxes = ();
for my $i (0 .. $n_slices) {
push @layer_surface_bboxes, $layer->slices->[$i]->contour->bounding_box;
}
# 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_surface_bboxes[$i];
return
$point->x >= $bbox->x_min && $point->x < $bbox->x_max &&
$point->y >= $bbox->y_min && $point->y < $bbox->y_max &&
$layer->slices->[$i]->contour->contains_point($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);

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)

1
utils/autorun.bat Normal file
View File

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

222
utils/package_win32.ps1 Normal file
View File

@ -0,0 +1,222 @@
# Written by Joseph Lenox
# Licensed under the same license as the rest of Slic3r.
# ------------------------
# You need to have Strawberry Perl 5.22 installed for this to work,
echo "Make this is run from the perl command window."
echo "Requires PAR."
New-Variable -Name "current_branch" -Value ""
git branch | foreach {
if ($_ -match "` (.*)"){
$current_branch += $matches[1]
}
}
# Change this to where you have Strawberry Perl installed.
New-Variable -Name "STRAWBERRY_PATH" -Value "C:\Strawberry"
cpanm "PAR::Packer"
pp `
-a "../utils;utils" `
-a "autorun.bat;slic3r.bat" `
-a "../var;var" `
-a "${STRAWBERRY_PATH}\perl\bin\perl5.22.2.exe;perl5.22.2.exe" `
-a "${STRAWBERRY_PATH}\perl\bin\perl522.dll;perl522.dll" `
-a "${STRAWBERRY_PATH}\perl\bin\libgcc_s_sjlj-1.dll;libgcc_s_sjlj-1.dll" `
-a "${STRAWBERRY_PATH}\perl\bin\libstdc++-6.dll;libstdc++-6.dll" `
-a "${STRAWBERRY_PATH}\perl\bin\libwinpthread-1.dll;libwinpthread-1.dll" `
-a "${STRAWBERRY_PATH}\perl\bin\freeglut.dll;freeglut.dll" `
-a "${STRAWBERRY_PATH}\perl\site\lib\Alien\wxWidgets\msw_3_0_2_uni_gcc_3_4\lib\wxbase30u_gcc_custom.dll;wxbase30u_gcc_custom.dll" `
-a "${STRAWBERRY_PATH}\perl\site\lib\Alien\wxWidgets\msw_3_0_2_uni_gcc_3_4\lib\wxmsw30u_adv_gcc_custom.dll;wxmsw30u_adv_gcc_custom.dll" `
-a "${STRAWBERRY_PATH}\perl\site\lib\Alien\wxWidgets\msw_3_0_2_uni_gcc_3_4\lib\wxmsw30u_gl_gcc_custom.dll;wxmsw30u_gl_gcc_custom.dll" `
-a "${STRAWBERRY_PATH}\perl\site\lib\Alien\wxWidgets\msw_3_0_2_uni_gcc_3_4\lib\wxmsw30u_core_gcc_custom.dll;wxmsw30u_core_gcc_custom.dll" `
-a "${STRAWBERRY_PATH}\perl\site\lib\Alien\wxWidgets\msw_3_0_2_uni_gcc_3_4\lib\wxmsw30u_html_gcc_custom.dll;wxmsw30u_html_gcc_custom.dll" `
-a "${STRAWBERRY_PATH}\perl\site\lib\Alien\wxWidgets\msw_3_0_2_uni_gcc_3_4\lib\wxbase30u_xml_gcc_custom.dll;wxbase30u_xml_gcc_custom.dll" `
-a "${STRAWBERRY_PATH}\perl\site\lib\Alien\wxWidgets\msw_3_0_2_uni_gcc_3_4\lib\wxbase30u_net_gcc_custom.dll;wxbase30u_net_gcc_custom.dll" `
-a "../lib;lib" `
-a "../slic3r.pl;slic3r.pl" `
-M AutoLoader `
-M B `
-M Carp `
-M Class::Accessor `
-M Class::XSAccessor `
-M Class::XSAccessor::Heavy `
-M Config `
-M Crypt::CBC `
-M Cwd `
-M Data::UUID `
-M Devel::GlobalDestruction `
-M Digest `
-M Digest::MD5 `
-M Digest::SHA `
-M Digest::base `
-M DynaLoader `
-M Encode `
-M Encode::Alias `
-M Encode::Byte `
-M Encode::Config `
-M Encode::Encoding `
-M Encode::Locale `
-M Encode::MIME::Name `
-M Errno `
-M Exporter `
-M Exporter::Heavy `
-M Fcntl `
-M File::Basename `
-M File::Glob `
-M File::Spec `
-M File::Spec::Unix `
-M File::Spec::Win32 `
-M FindBin `
-M Getopt::Long `
-M Growl::GNTP `
-M HTTP::Config `
-M HTTP::Date `
-M HTTP::Headers `
-M HTTP::Headers::Util `
-M HTTP::Message `
-M HTTP::Request `
-M HTTP::Request::Common `
-M HTTP::Response `
-M HTTP::Status `
-M IO `
-M IO::Handle `
-M IO::Select `
-M IO::Socket `
-M IO::Socket::INET `
-M IO::Socket::INET6 `
-M IO::Socket::IP `
-M IO::Socket::UNIX `
-M LWP `
-M LWP::MediaTypes `
-M LWP::MemberMixin `
-M LWP::Protocol `
-M LWP::Protocol::http `
-M LWP::UserAgent `
-M List::Util `
-M Math::Libm `
-M Math::PlanePath `
-M Math::PlanePath::ArchimedeanChords `
-M Math::PlanePath::Base::Digits `
-M Math::PlanePath::Base::Generic `
-M Math::PlanePath::Base::NSEW `
-M Math::PlanePath::Flowsnake `
-M Math::PlanePath::FlowsnakeCentres `
-M Math::PlanePath::HilbertCurve `
-M Math::PlanePath::OctagramSpiral `
-M Math::PlanePath::SacksSpiral `
-M Math::Trig `
-M Method::Generate::Accessor `
-M Method::Generate::BuildAll `
-M Method::Generate::Constructor `
-M Module::Runtime `
-M Moo `
-M Moo::HandleMoose `
-M Moo::Object `
-M Moo::Role `
-M Moo::sification `
-M Net::Bonjour `
-M Net::Bonjour::Entry `
-M Net::DNS `
-M Net::DNS::Domain `
-M Net::DNS::DomainName `
-M Net::DNS::Header `
-M Net::DNS::Packet `
-M Net::DNS::Parameters `
-M Net::DNS::Question `
-M Net::DNS::RR `
-M Net::DNS::RR::OPT `
-M Net::DNS::RR::PTR `
-M Net::DNS::Resolver `
-M Net::DNS::Resolver `
-M Net::DNS::Resolver::Base `
-M Net::DNS::Resolver::MSWin32 `
-M Net::DNS::Update `
-M Net::HTTP `
-M Net::HTTP::Methods `
-M OpenGL `
-M POSIX `
-M Pod::Escapes `
-M Pod::Text `
-M Pod::Usage `
-M Role::Tiny `
-M Scalar::Util `
-M SelectSaver `
-M Slic3r::* `
-M Slic3r::XS `
-M Socket `
-M Socket6 `
-M Storable `
-M Sub::Defer `
-M Sub::Exporter `
-M Sub::Exporter::Progressive `
-M Sub::Name `
-M Sub::Quote `
-M Sub::Util `
-M Symbol `
-M Term::Cap `
-M Text::ParseWords `
-M Thread `
-M Thread::Queue `
-M Thread::Semaphore `
-M Tie::Handle `
-M Tie::Hash `
-M Tie::StdHandle `
-M Time::HiRes `
-M Time::Local `
-M URI `
-M URI::Escape `
-M URI::http `
-M Unicode::Normalize `
-M Win32 `
-M Win32::API `
-M Win32::API::Struct `
-M Win32::API::Type `
-M Win32::IPHelper `
-M Win32::TieRegistry `
-M Win32::WinError `
-M Win32API::Registry `
-M Wx `
-M Wx::App `
-M Wx::DND `
-M Wx::DropSource `
-M Wx::Event `
-M Wx::GLCanvas `
-M Wx::Grid `
-M Wx::Html `
-M Wx::Locale `
-M Wx::Menu `
-M Wx::Mini `
-M Wx::Print `
-M Wx::RadioBox `
-M Wx::Timer `
-M XSLoader `
-M attributes `
-M base `
-M bytes `
-M constant `
-M constant `
-M constant::defer `
-M enum `
-M feature `
-M integer `
-M locale `
-M lib `
-M mro `
-M overload `
-M overload::numbers `
-M overloading `
-M parent `
-M re `
-M strict `
-M threads `
-M threads::shared `
-M utf8 `
-M vars `
-M warnings `
-M warnings::register `
-e -p ..\slic3r.pl -o ..\slic3r.par
copy ..\slic3r.par "..\slic3r-${current_branch}-${APPVEYOR_BUILD_NUMBER}-$(git rev-parse --short HEAD).zip"

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

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

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,14 @@
#include "Config.hpp"
#include <stdlib.h> // for setenv()
#include <assert.h>
#include <ctime>
#include <fstream>
#include <iostream>
#include <boost/algorithm/string/erase.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/foreach.hpp>
#include <boost/property_tree/ini_parser.hpp>
#include <boost/property_tree/ptree.hpp>
#if defined(_WIN32) && !defined(setenv) && defined(_putenv_s)
#define setenv(k, v, o) _putenv_s(k, v)
@ -20,12 +28,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 +61,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,7 +122,7 @@ 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->shortcut.empty()) {
@ -110,7 +134,7 @@ ConfigBase::set_deserialize(const t_config_option_key &opt_key, std::string str)
ConfigOption* opt = this->option(opt_key, true);
assert(opt != NULL);
return opt->deserialize(str);
return opt->deserialize(str, append);
}
double
@ -171,6 +195,38 @@ 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) {
this->set_deserialize(v.first.c_str(), v.second.get_value<std::string>().c_str());
}
}
void
ConfigBase::save(const std::string &file) const
{
using namespace std;
ofstream c;
c.open(file.c_str(), ios::out | ios::trunc);
{
time_t now;
time(&now);
char buf[sizeof "0000-00-00 00:00:00"];
strftime(buf, sizeof buf, "%F %T", gmtime(&now));
c << "# generated by Slic3r " << SLIC3R_VERSION << " on " << buf << endl;
}
t_config_option_keys my_keys = this->keys();
for (t_config_option_keys::const_iterator opt_key = my_keys.begin(); opt_key != my_keys.end(); ++opt_key)
c << *opt_key << " = " << this->serialize(*opt_key) << endl;
c.close();
}
DynamicConfig& DynamicConfig::operator= (DynamicConfig other)
{
this->swap(other);
@ -200,9 +256,11 @@ DynamicConfig::optptr(const t_config_option_key &opt_key, bool create) {
if (this->options.count(opt_key) == 0) {
if (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 +323,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

@ -20,13 +20,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 +56,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 +80,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 +90,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 +100,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 +123,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 +142,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 +153,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 +163,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 +186,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 +205,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 +222,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 +239,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 +256,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 +272,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 +286,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 +301,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 +327,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 +340,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 +350,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 +363,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 +388,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 +413,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 +421,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 +430,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 +453,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 +473,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 +483,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 +507,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 +561,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 +574,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 +585,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 +596,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 +617,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;

View File

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

View File

@ -1,17 +1,26 @@
#include "IO.hpp"
#include <stdexcept>
#include <fstream>
#include <iostream>
namespace Slic3r { namespace IO {
bool
STL::read_file(std::string input_file, Model* model)
STL::read(std::string input_file, TriangleMesh* mesh)
{
mesh->ReadSTLFile(input_file);
mesh->check_topology();
return true;
}
bool
STL::read(std::string input_file, Model* model)
{
// TODO: encode file name
// TODO: check that file exists
TriangleMesh mesh;
mesh.ReadSTLFile(input_file);
mesh.repair();
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.");
@ -44,4 +53,30 @@ OBJ::write(TriangleMesh& mesh, std::string 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;
}
} }

View File

@ -11,14 +11,21 @@ namespace Slic3r { namespace IO {
class STL
{
public:
bool read_file(std::string input_file, Model* model);
bool write(TriangleMesh& mesh, std::string output_file, bool binary = true);
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:
bool write(TriangleMesh& mesh, std::string output_file);
static bool write(TriangleMesh& mesh, std::string output_file);
};
class POV
{
public:
static bool write(TriangleMesh& mesh, std::string output_file);
};
} }

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,48 @@ ModelObject::split(ModelObjectPtrs* new_objects)
return;
}
void
ModelObject::print_info() const
{
using namespace std;
cout << "[" << boost::filesystem::path(this->input_file).filename().string() << "]" << endl;
TriangleMesh mesh = this->raw_mesh();
mesh.check_topology();
BoundingBoxf3 bb = mesh.bounding_box();
Sizef3 size = bb.size();
cout << "size_x = " << size.x << endl;
cout << "size_y = " << size.y << endl;
cout << "size_z = " << size.z << endl;
cout << "min_x = " << bb.min.x << endl;
cout << "min_y = " << bb.min.y << endl;
cout << "min_z = " << bb.min.z << endl;
cout << "max_x = " << bb.max.x << endl;
cout << "max_y = " << bb.max.y << endl;
cout << "max_z = " << bb.max.z << endl;
cout << "number_of_facets = " << mesh.stl.stats.number_of_facets << endl;
cout << "manifold = " << (mesh.is_manifold() ? "yes" : "no") << endl;
mesh.repair(); // this calculates number_of_parts
if (mesh.needed_repair()) {
mesh.repair();
if (mesh.stl.stats.degenerate_facets > 0)
cout << "degenerate_facets = " << mesh.stl.stats.degenerate_facets << endl;
if (mesh.stl.stats.edges_fixed > 0)
cout << "edges_fixed = " << mesh.stl.stats.edges_fixed << endl;
if (mesh.stl.stats.facets_removed > 0)
cout << "facets_removed = " << mesh.stl.stats.facets_removed << endl;
if (mesh.stl.stats.facets_added > 0)
cout << "facets_added = " << mesh.stl.stats.facets_added << endl;
if (mesh.stl.stats.facets_reversed > 0)
cout << "facets_reversed = " << mesh.stl.stats.facets_reversed << endl;
if (mesh.stl.stats.backwards_edges > 0)
cout << "backwards_edges = " << mesh.stl.stats.backwards_edges << endl;
}
cout << "number_of_parts = " << mesh.stl.stats.number_of_parts << endl;
cout << "volume = " << mesh.volume() << endl;
}
ModelVolume::ModelVolume(ModelObject* object, const TriangleMesh &mesh)
: mesh(mesh), modifier(false), object(object)
@ -713,6 +784,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 +846,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

@ -756,6 +756,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.";
@ -1026,7 +1035,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;
@ -1344,4 +1353,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

@ -467,6 +467,71 @@ class FullPrintConfig
};
};
class SVGExportConfig
: public virtual StaticPrintConfig
{
public:
ConfigOptionFloatOrPercent first_layer_height;
ConfigOptionFloat layer_height;
ConfigOptionInt raft_layers;
ConfigOptionFloat raft_offset;
SVGExportConfig() : StaticPrintConfig() {
this->set_defaults();
};
virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) {
OPT_PTR(first_layer_height);
OPT_PTR(layer_height);
OPT_PTR(raft_layers);
OPT_PTR(raft_offset);
return NULL;
};
};
class CLIConfigDef : public ConfigDef
{
public:
CLIConfigDef();
};
extern CLIConfigDef cli_config_def;
class CLIConfig
: public virtual ConfigBase, public StaticConfig
{
public:
ConfigOptionBool export_obj;
ConfigOptionBool export_pov;
ConfigOptionBool export_svg;
ConfigOptionBool info;
ConfigOptionStrings load;
ConfigOptionString output;
ConfigOptionFloat rotate;
ConfigOptionString save;
ConfigOptionFloat scale;
CLIConfig() : ConfigBase(), StaticConfig() {
this->def = &cli_config_def;
this->set_defaults();
};
virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) {
OPT_PTR(export_obj);
OPT_PTR(export_pov);
OPT_PTR(export_svg);
OPT_PTR(info);
OPT_PTR(load);
OPT_PTR(output);
OPT_PTR(rotate);
OPT_PTR(save);
OPT_PTR(scale);
return NULL;
};
};
}
#endif

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() {
@ -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
@ -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,12 +22,15 @@ class TriangleMesh
TriangleMesh();
TriangleMesh(const TriangleMesh &other);
TriangleMesh& operator= (TriangleMesh other);
void swap(TriangleMesh &other);
void swap(TriangleMesh &first, TriangleMesh &second);
~TriangleMesh();
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 check_topology();
float volume();
bool is_manifold() const;
void WriteOBJFile(const std::string &output_file);
void scale(float factor);
void scale(const Pointf3 &versor);
@ -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);

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