diff --git a/xs/src/Config.cpp b/xs/src/Config.cpp index 28077df9b..5b98a268c 100644 --- a/xs/src/Config.cpp +++ b/xs/src/Config.cpp @@ -68,15 +68,39 @@ ConfigBase::get(t_config_option_key opt_key) { if (opt == NULL) return &PL_sv_undef; if (ConfigOptionFloat* optv = dynamic_cast(opt)) { return newSVnv(optv->value); + } else if (ConfigOptionFloats* optv = dynamic_cast(opt)) { + AV* av = newAV(); + av_fill(av, optv->values.size()-1); + for (std::vector::iterator it = optv->values.begin(); it != optv->values.end(); ++it) + av_store(av, it - optv->values.begin(), newSVnv(*it)); + return newRV_noinc((SV*)av); } else if (ConfigOptionInt* optv = dynamic_cast(opt)) { return newSViv(optv->value); + } else if (ConfigOptionInts* optv = dynamic_cast(opt)) { + AV* av = newAV(); + av_fill(av, optv->values.size()-1); + for (std::vector::iterator it = optv->values.begin(); it != optv->values.end(); ++it) + av_store(av, it - optv->values.begin(), newSViv(*it)); + return newRV_noinc((SV*)av); } else if (ConfigOptionString* optv = dynamic_cast(opt)) { // we don't serialize() because that would escape newlines return newSVpvn(optv->value.c_str(), optv->value.length()); } else if (ConfigOptionPoint* optv = dynamic_cast(opt)) { return optv->point.to_SV_pureperl(); + } else if (ConfigOptionPoints* optv = dynamic_cast(opt)) { + AV* av = newAV(); + av_fill(av, optv->points.size()-1); + for (Pointfs::iterator it = optv->points.begin(); it != optv->points.end(); ++it) + av_store(av, it - optv->points.begin(), it->to_SV_pureperl()); + return newRV_noinc((SV*)av); } else if (ConfigOptionBool* optv = dynamic_cast(opt)) { return newSViv(optv->value ? 1 : 0); + } else if (ConfigOptionBools* optv = dynamic_cast(opt)) { + AV* av = newAV(); + av_fill(av, optv->values.size()-1); + for (std::vector::iterator it = optv->values.begin(); it != optv->values.end(); ++it) + av_store(av, it - optv->values.begin(), newSViv(*it ? 1 : 0)); + return newRV_noinc((SV*)av); } else { std::string serialized = opt->serialize(); return newSVpvn(serialized.c_str(), serialized.length()); @@ -90,14 +114,48 @@ ConfigBase::set(t_config_option_key opt_key, SV* value) { if (ConfigOptionFloat* optv = dynamic_cast(opt)) { optv->value = SvNV(value); + } else if (ConfigOptionFloats* optv = dynamic_cast(opt)) { + optv->values.clear(); + AV* av = (AV*)SvRV(value); + const size_t len = av_len(av)+1; + for (size_t i = 0; i < len; i++) { + SV** elem = av_fetch(av, i, 0); + optv->values.push_back(SvNV(*elem)); + } } else if (ConfigOptionInt* optv = dynamic_cast(opt)) { optv->value = SvIV(value); + } else if (ConfigOptionInts* optv = dynamic_cast(opt)) { + optv->values.clear(); + AV* av = (AV*)SvRV(value); + const size_t len = av_len(av)+1; + for (size_t i = 0; i < len; i++) { + SV** elem = av_fetch(av, i, 0); + optv->values.push_back(SvIV(*elem)); + } } else if (ConfigOptionString* optv = dynamic_cast(opt)) { optv->value = std::string(SvPV_nolen(value), SvCUR(value)); } else if (ConfigOptionPoint* optv = dynamic_cast(opt)) { optv->point.from_SV(value); + } else if (ConfigOptionPoints* optv = dynamic_cast(opt)) { + optv->points.clear(); + AV* av = (AV*)SvRV(value); + const size_t len = av_len(av)+1; + for (size_t i = 0; i < len; i++) { + SV** elem = av_fetch(av, i, 0); + Pointf point; + point.from_SV(*elem); + optv->points.push_back(point); + } } else if (ConfigOptionBool* optv = dynamic_cast(opt)) { optv->value = SvTRUE(value); + } else if (ConfigOptionBools* optv = dynamic_cast(opt)) { + optv->values.clear(); + AV* av = (AV*)SvRV(value); + const size_t len = av_len(av)+1; + for (size_t i = 0; i < len; i++) { + SV** elem = av_fetch(av, i, 0); + optv->values.push_back(SvTRUE(*elem)); + } } else { opt->deserialize( std::string(SvPV_nolen(value)) ); } @@ -119,16 +177,24 @@ DynamicConfig::option(const t_config_option_key opt_key, bool create) { ConfigOption* opt; if (Options[opt_key].type == coFloat) { opt = new ConfigOptionFloat (); + } else if (Options[opt_key].type == coFloats) { + opt = new ConfigOptionFloats (); } else if (Options[opt_key].type == coInt) { opt = new ConfigOptionInt (); + } else if (Options[opt_key].type == coInts) { + opt = new ConfigOptionInts (); } else if (Options[opt_key].type == coString) { opt = new ConfigOptionString (); } else if (Options[opt_key].type == coFloatOrPercent) { opt = new ConfigOptionFloatOrPercent (); } else if (Options[opt_key].type == coPoint) { opt = new ConfigOptionPoint (); + } else if (Options[opt_key].type == coPoints) { + opt = new ConfigOptionPoints (); } else if (Options[opt_key].type == coBool) { opt = new ConfigOptionBool (); + } else if (Options[opt_key].type == coBools) { + opt = new ConfigOptionBools (); } else if (Options[opt_key].type == coEnumGCodeFlavor) { opt = new ConfigOptionEnumGCodeFlavor (); } else { diff --git a/xs/src/Config.hpp b/xs/src/Config.hpp index 597ae183a..a59bcf405 100644 --- a/xs/src/Config.hpp +++ b/xs/src/Config.hpp @@ -42,6 +42,30 @@ class ConfigOptionFloat : public ConfigOption }; }; +class ConfigOptionFloats : public ConfigOption +{ + public: + std::vector values; + + std::string serialize() { + std::ostringstream ss; + for (std::vector::const_iterator it = this->values.begin(); it != this->values.end(); ++it) { + if (it - this->values.begin() != 0) ss << ","; + ss << *it; + } + return ss.str(); + }; + + void deserialize(std::string str) { + this->values.clear(); + std::istringstream is(str); + std::string item_str; + while (std::getline(is, item_str, ',')) { + this->values.push_back(::atof(item_str.c_str())); + } + }; +}; + class ConfigOptionInt : public ConfigOption { public: @@ -61,6 +85,30 @@ class ConfigOptionInt : public ConfigOption }; }; +class ConfigOptionInts : public ConfigOption +{ + public: + std::vector values; + + std::string serialize() { + std::ostringstream ss; + for (std::vector::const_iterator it = this->values.begin(); it != this->values.end(); ++it) { + if (it - this->values.begin() != 0) ss << ","; + ss << *it; + } + return ss.str(); + }; + + void deserialize(std::string str) { + this->values.clear(); + std::istringstream is(str); + std::string item_str; + while (std::getline(is, item_str, ',')) { + this->values.push_back(::atoi(item_str.c_str())); + } + }; +}; + class ConfigOptionString : public ConfigOption { public: @@ -141,6 +189,34 @@ class ConfigOptionPoint : public ConfigOption }; }; +class ConfigOptionPoints : public ConfigOption +{ + public: + Pointfs points; + + std::string serialize() { + std::ostringstream ss; + for (Pointfs::const_iterator it = this->points.begin(); it != this->points.end(); ++it) { + if (it - this->points.begin() != 0) ss << ","; + ss << it->x; + ss << "x"; + ss << it->y; + } + return ss.str(); + }; + + void deserialize(std::string str) { + this->points.clear(); + std::istringstream is(str); + std::string point_str; + while (std::getline(is, point_str, ',')) { + Pointf point; + sscanf(point_str.c_str(), "%fx%f", &point.x, &point.y); + this->points.push_back(point); + } + }; +}; + class ConfigOptionBool : public ConfigOption { public: @@ -158,8 +234,28 @@ class ConfigOptionBool : public ConfigOption }; }; -enum GCodeFlavor { - gcfRepRap, gcfTeacup, gcfMakerWare, gcfSailfish, gcfMach3, gcfNoExtrusion, +class ConfigOptionBools : public ConfigOption +{ + public: + std::vector values; + + std::string serialize() { + std::ostringstream ss; + for (std::vector::const_iterator it = this->values.begin(); it != this->values.end(); ++it) { + if (it - this->values.begin() != 0) ss << ","; + ss << (*it ? "1" : "0"); + } + return ss.str(); + }; + + void deserialize(std::string str) { + this->values.clear(); + std::istringstream is(str); + std::string item_str; + while (std::getline(is, item_str, ',')) { + this->values.push_back(item_str.compare("1") == 0); + } + }; }; template @@ -190,15 +286,35 @@ void ConfigOptionEnum::deserialize(std::string str) { assert(enum_keys_map.count(str) > 0); this->value = enum_keys_map[str]; }; + +enum GCodeFlavor { + gcfRepRap, gcfTeacup, gcfMakerWare, gcfSailfish, gcfMach3, gcfNoExtrusion, +}; typedef ConfigOptionEnum ConfigOptionEnumGCodeFlavor; +// we declare this as inline to keep it in this file along with all other option definitions +template<> inline std::map ConfigOptionEnum::get_enum_values() { + std::map keys_map; + keys_map["reprap"] = gcfRepRap; + keys_map["teacup"] = gcfTeacup; + keys_map["makerware"] = gcfMakerWare; + keys_map["sailfish"] = gcfSailfish; + keys_map["mach3"] = gcfMach3; + keys_map["no-extrusion"] = gcfNoExtrusion; + return keys_map; +} + enum ConfigOptionType { coFloat, + coFloats, coInt, + coInts, coString, coFloatOrPercent, coPoint, + coPoints, coBool, + coBools, coEnumGCodeFlavor, }; @@ -261,9 +377,13 @@ class FullConfig : public StaticConfig ConfigOptionInt perimeters; ConfigOptionString extrusion_axis; ConfigOptionPoint print_center; + ConfigOptionPoints extruder_offset; ConfigOptionString notes; ConfigOptionBool use_relative_e_distances; ConfigOptionEnumGCodeFlavor gcode_flavor; + ConfigOptionFloats nozzle_diameter; + ConfigOptionInts temperature; + ConfigOptionBools wipe; ConfigOption* option(const t_config_option_key opt_key, bool create = false) { assert(!create); // can't create options in StaticConfig @@ -272,9 +392,13 @@ class FullConfig : public StaticConfig if (opt_key == "perimeters") return &this->perimeters; if (opt_key == "extrusion_axis") return &this->extrusion_axis; if (opt_key == "print_center") return &this->print_center; + if (opt_key == "extruder_offset") return &this->extruder_offset; if (opt_key == "notes") return &this->notes; if (opt_key == "use_relative_e_distances") return &this->use_relative_e_distances; if (opt_key == "gcode_flavor") return &this->gcode_flavor; + if (opt_key == "nozzle_diameter") return &this->nozzle_diameter; + if (opt_key == "temperature") return &this->temperature; + if (opt_key == "wipe") return &this->wipe; return NULL; }; }; @@ -296,27 +420,23 @@ static t_optiondef_map _build_optiondef_map () { Options["print_center"].type = coPoint; + Options["extruder_offset"].type = coPoints; + Options["notes"].type = coString; Options["use_relative_e_distances"].type = coBool; Options["gcode_flavor"].type = coEnumGCodeFlavor; + Options["nozzle_diameter"].type = coFloats; + + Options["temperature"].type = coInts; + + Options["wipe"].type = coBools; + return Options; } -// we declare this as inline to keep it in this file along with all other option definitions -template<> inline std::map ConfigOptionEnum::get_enum_values() { - std::map keys_map; - keys_map["reprap"] = gcfRepRap; - keys_map["teacup"] = gcfTeacup; - keys_map["makerware"] = gcfMakerWare; - keys_map["sailfish"] = gcfSailfish; - keys_map["mach3"] = gcfMach3; - keys_map["no-extrusion"] = gcfNoExtrusion; - return keys_map; -} - static FullConfig _build_default_config () { FullConfig defconf; @@ -327,9 +447,13 @@ static FullConfig _build_default_config () { defconf.perimeters.value = 3; defconf.extrusion_axis.value = "E"; defconf.print_center.point = Pointf(100,100); + defconf.extruder_offset.points.push_back(Pointf(0,0)); defconf.notes.value = ""; defconf.use_relative_e_distances.value = false; defconf.gcode_flavor.value = gcfRepRap; + defconf.nozzle_diameter.values.push_back(0.5); + defconf.temperature.values.push_back(200); + defconf.wipe.values.push_back(true); return defconf; } diff --git a/xs/src/Point.hpp b/xs/src/Point.hpp index 202ec557d..61592a805 100644 --- a/xs/src/Point.hpp +++ b/xs/src/Point.hpp @@ -9,8 +9,10 @@ namespace Slic3r { class Line; class Point; +class Pointf; typedef std::vector Points; typedef std::vector PointPtrs; +typedef std::vector Pointfs; class Point { diff --git a/xs/t/15_config.t b/xs/t/15_config.t index 92c95919f..bafd869b8 100644 --- a/xs/t/15_config.t +++ b/xs/t/15_config.t @@ -4,7 +4,7 @@ use strict; use warnings; use Slic3r::XS; -use Test::More tests => 20; +use Test::More tests => 32; { my $config = Slic3r::Config->new; @@ -48,6 +48,31 @@ use Test::More tests => 20; $config->set('gcode_flavor', 'teacup'); is $config->get('gcode_flavor'), 'teacup', 'set/get enum'; is $config->serialize('gcode_flavor'), 'teacup', 'serialize enum'; + + $config->set('extruder_offset', [[10,20],[30,45]]); + is_deeply $config->get('extruder_offset'), [[10,20],[30,45]], 'set/get points'; + is $config->serialize('extruder_offset'), '10x20,30x45', 'serialize points'; + $config->set_deserialize('extruder_offset', '20x10'); + is_deeply $config->get('extruder_offset'), [[20,10]], 'deserialize points'; + + # truncate ->get() to first decimal digit + $config->set('nozzle_diameter', [0.2,0.3]); + is_deeply [ map int($_*10)/10, @{$config->get('nozzle_diameter')} ], [0.2,0.3], 'set/get floats'; + is $config->serialize('nozzle_diameter'), '0.2,0.3', 'serialize floats'; + $config->set_deserialize('nozzle_diameter', '0.1,0.4'); + is_deeply [ map int($_*10)/10, @{$config->get('nozzle_diameter')} ], [0.1,0.4], 'deserialize floats'; + + $config->set('temperature', [180,210]); + is_deeply $config->get('temperature'), [180,210], 'set/get ints'; + is $config->serialize('temperature'), '180,210', 'serialize ints'; + $config->set_deserialize('temperature', '195,220'); + is_deeply $config->get('temperature'), [195,220], 'deserialize ints'; + + $config->set('wipe', [1,0]); + is_deeply $config->get('wipe'), [1,0], 'set/get bools'; + is $config->serialize('wipe'), '1,0', 'serialize bools'; + $config->set_deserialize('wipe', '0,1,1'); + is_deeply $config->get('wipe'), [0,1,1], 'deserialize bools'; } __END__