Merge branch 'cppsvg'

This commit is contained in:
Alessandro Ranellucci 2016-07-09 16:18:46 +02:00
commit f7e37fa9ab
23 changed files with 750 additions and 65 deletions

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

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

111
src/CMakeLists.txt Normal file
View File

@ -0,0 +1,111 @@
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/ExPolygon.cpp
${LIBDIR}/libslic3r/GCode.cpp
${LIBDIR}/libslic3r/LayerRegion.cpp
${LIBDIR}/libslic3r/PerimeterGenerator.cpp
${LIBDIR}/libslic3r/Polyline.cpp
${LIBDIR}/libslic3r/SurfaceCollection.cpp
${LIBDIR}/libslic3r/BridgeDetector.cpp
${LIBDIR}/libslic3r/Extruder.cpp
${LIBDIR}/libslic3r/GCodeSender.cpp
${LIBDIR}/libslic3r/IO.cpp
${LIBDIR}/libslic3r/Line.cpp
${LIBDIR}/libslic3r/PlaceholderParser.cpp
${LIBDIR}/libslic3r/PrintConfig.cpp
${LIBDIR}/libslic3r/Surface.cpp
${LIBDIR}/libslic3r/ClipperUtils.cpp
${LIBDIR}/libslic3r/ExtrusionEntityCollection.cpp
${LIBDIR}/libslic3r/GCodeWriter.cpp
${LIBDIR}/libslic3r/Model.cpp
${LIBDIR}/libslic3r/Point.cpp
${LIBDIR}/libslic3r/Print.cpp
${LIBDIR}/libslic3r/SVG.cpp
${LIBDIR}/libslic3r/SVGExport.cpp
${LIBDIR}/libslic3r/Config.cpp
${LIBDIR}/libslic3r/ExtrusionEntity.cpp
${LIBDIR}/libslic3r/Geometry.cpp
${LIBDIR}/libslic3r/MotionPlanner.cpp
${LIBDIR}/libslic3r/Polygon.cpp
${LIBDIR}/libslic3r/PrintObject.cpp
${LIBDIR}/libslic3r/TriangleMesh.cpp
${LIBDIR}/libslic3r/ExPolygonCollection.cpp
${LIBDIR}/libslic3r/Flow.cpp
${LIBDIR}/libslic3r/Layer.cpp
${LIBDIR}/libslic3r/MultiPoint.cpp
${LIBDIR}/libslic3r/PolylineCollection.cpp
${LIBDIR}/libslic3r/PrintRegion.cpp)
add_library(admesh STATIC ${LIBDIR}/admesh/util.c ${LIBDIR}/admesh/stl_io.c ${LIBDIR}/admesh/stlinit.c ${LIBDIR}/admesh/shared.c ${LIBDIR}/admesh/normals.c ${LIBDIR}/admesh/connect.c)
add_library(clipper STATIC ${LIBDIR}/clipper.cpp)
add_library(polypartition STATIC ${LIBDIR}/polypartition.cpp)
add_library(poly2tri STATIC ${LIBDIR}/poly2tri/sweep/sweep.cc ${LIBDIR}/poly2tri/sweep/sweep_context.cc ${LIBDIR}/poly2tri/sweep/cdt.cc ${LIBDIR}/poly2tri/sweep/advancing_front.cc ${LIBDIR}/poly2tri/common/shapes.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)
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)

95
src/slic3r.cpp Normal file
View File

@ -0,0 +1,95 @@
#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 specific DynamicPrintConfig which provides normalize()
DynamicPrintConfig print_config;
print_config.apply(config, true);
print_config.normalize();
// apply command line options to a more handy CLIConfig
CLIConfig cli_config;
cli_config.apply(config, true);
/* TODO: loop through the config files supplied on the command line (now stored in
cli_config), load each one, normalize it and apply it to print_config */
// 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) {
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();
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();
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.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

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

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

@ -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,8 @@
#include "Config.hpp"
#include <stdlib.h> // for setenv()
#include <assert.h>
#include <boost/algorithm/string/erase.hpp>
#include <boost/algorithm/string/predicate.hpp>
#if defined(_WIN32) && !defined(setenv) && defined(_putenv_s)
#define setenv(k, v, o) _putenv_s(k, v)
@ -20,12 +22,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 +55,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);
@ -200,7 +218,7 @@ 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) {
opt = new ConfigOptionFloat ();
@ -265,6 +283,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) {
printf("No value supplied for --%s\n", token.c_str());
exit(1);
}
this->option(opt_key, true)->deserialize(argv[++i]);
}
} else {
extra->push_back(token);
}
}
}
void
StaticConfig::set_defaults()
{

View File

@ -20,6 +20,7 @@ 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 void set(const ConfigOption &option) = 0;
@ -54,8 +55,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 +79,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; };
@ -95,6 +99,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;
@ -134,6 +141,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; };
@ -154,6 +162,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;
@ -193,6 +204,7 @@ 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 serialize() const {
std::string str = this->value;
@ -224,6 +236,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;
@ -254,6 +269,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;
@ -282,6 +298,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);
@ -320,6 +337,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;
@ -342,6 +360,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;
@ -389,6 +410,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; };
@ -405,6 +427,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;
@ -445,6 +470,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();
@ -532,6 +558,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 +571,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 +582,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;
@ -571,6 +603,7 @@ 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 +612,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

@ -1,10 +1,12 @@
#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, Model* model)
{
// TODO: encode file name
// TODO: check that file exists
@ -44,4 +46,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,20 @@ 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, 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 {
@ -311,6 +313,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)
@ -479,7 +488,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 +577,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 +716,32 @@ ModelObject::split(ModelObjectPtrs* new_objects)
return;
}
void
ModelObject::print_info() const
{
using namespace std;
cout << "Info about " << boost::filesystem::basename(this->input_file) << ":" << endl;
TriangleMesh mesh = this->raw_mesh();
mesh.repair();
Sizef3 size = mesh.bounding_box().size();
cout << " size: x=" << size.x << " y=" << size.y << " z=" << size.z << endl;
cout << " number of facets: " << mesh.stl.stats.number_of_facets << endl;
cout << " number of shells: " << mesh.stl.stats.number_of_parts << endl;
cout << " volume: " << mesh.stl.stats.volume << endl;
if (this->needed_repair()) {
cout << " needed repair: yes" << endl;
cout << " degenerate facets: " << mesh.stl.stats.degenerate_facets << endl;
cout << " edges fixed: " << mesh.stl.stats.edges_fixed << endl;
cout << " facets removed: " << mesh.stl.stats.facets_removed << endl;
cout << " facets added: " << mesh.stl.stats.facets_added << endl;
cout << " facets reversed: " << mesh.stl.stats.facets_reversed << endl;
cout << " backwards edges: " << mesh.stl.stats.backwards_edges << endl;
} else {
cout << " needed repair: no" << endl;
}
}
ModelVolume::ModelVolume(ModelObject* object, const TriangleMesh &mesh)
: mesh(mesh), modifier(false), object(object)
@ -713,6 +754,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 +816,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

@ -61,6 +61,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
@ -124,6 +125,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 +135,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 +170,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 +191,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

@ -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.";
@ -1344,4 +1353,55 @@ 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("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("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,67 @@ 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;
ConfigOptionString output;
ConfigOptionFloat rotate;
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(output);
OPT_PTR(rotate);
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,28 @@
#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;
SVGExport(const TriangleMesh &mesh) : mesh(mesh) {
this->mesh.mirror_x();
};
void writeSVG(const std::string &outputfile);
private:
TriangleMesh mesh;
};
}
#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() {
@ -266,6 +262,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 +346,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);
std::copy(mesh.stl.neighbors_start, mesh.stl.neighbors_start + mesh.stl.stats.number_of_facets, this->stl.neighbors_start);
// update size
stl_get_size(&this->stl);

View File

@ -22,7 +22,7 @@ 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);
@ -41,6 +41,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

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