mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-31 04:31:15 -06:00 
			
		
		
		
	More work for XS Config
This commit is contained in:
		
							parent
							
								
									008f38cf68
								
							
						
					
					
						commit
						64e549a46f
					
				
					 7 changed files with 316 additions and 10 deletions
				
			
		|  | @ -60,6 +60,7 @@ t/11_clipper.t | |||
| t/12_extrusionpathcollection.t | ||||
| t/13_polylinecollection.t | ||||
| t/14_geometry.t | ||||
| t/15_config.t | ||||
| xsp/Clipper.xsp | ||||
| xsp/Config.xsp | ||||
| xsp/ExPolygon.xsp | ||||
|  |  | |||
|  | @ -5,6 +5,13 @@ namespace Slic3r { | |||
| t_optiondef_map Options = _build_optiondef_map(); | ||||
| FullConfig DefaultConfig = _build_default_config(); | ||||
| 
 | ||||
| ConfigOptionDef* | ||||
| get_config_option_def(const t_config_option_key opt_key) { | ||||
|     t_optiondef_map::iterator it = Options.find(opt_key); | ||||
|     if (it == Options.end()) return NULL; | ||||
|     return &it->second; | ||||
| } | ||||
| 
 | ||||
| void | ||||
| ConfigBase::apply(ConfigBase &other, bool ignore_nonexistent) { | ||||
|     // get list of option keys to apply
 | ||||
|  | @ -19,25 +26,112 @@ ConfigBase::apply(ConfigBase &other, bool ignore_nonexistent) { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| std::string | ||||
| ConfigBase::serialize(const t_config_option_key opt_key) { | ||||
|     ConfigOption* opt = this->option(opt_key); | ||||
|     assert(opt != NULL); | ||||
|     return opt->serialize(); | ||||
| } | ||||
| 
 | ||||
| void | ||||
| ConfigBase::set_deserialize(const t_config_option_key opt_key, std::string str) { | ||||
|     ConfigOption* opt = this->option(opt_key); | ||||
|     assert(opt != NULL); | ||||
|     opt->deserialize(str); | ||||
| } | ||||
| 
 | ||||
| float | ||||
| ConfigBase::get_abs_value(const t_config_option_key opt_key) { | ||||
|     // get option definition
 | ||||
|     ConfigOptionDef* def = get_config_option_def(opt_key); | ||||
|     assert(def != NULL); | ||||
|     assert(def->type == coFloatOrPercent); | ||||
|      | ||||
|     // get stored option value
 | ||||
|     ConfigOptionFloatOrPercent* opt = dynamic_cast<ConfigOptionFloatOrPercent*>(this->option(opt_key)); | ||||
|     assert(opt != NULL); | ||||
|      | ||||
|     // compute absolute value
 | ||||
|     if (opt->percent) { | ||||
|         ConfigOptionFloat* optbase = dynamic_cast<ConfigOptionFloat*>(this->option(def->ratio_over)); | ||||
|         assert(optbase != NULL); | ||||
|         return optbase->value * opt->value / 100; | ||||
|     } else { | ||||
|         return opt->value; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #ifdef SLIC3RXS | ||||
| SV* | ||||
| ConfigBase::get(t_config_option_key opt_key) { | ||||
|     ConfigOption* opt = this->option(opt_key); | ||||
|     if (opt == NULL) return &PL_sv_undef; | ||||
|     if (ConfigOptionFloat* v = dynamic_cast<ConfigOptionFloat*>(opt)) { | ||||
|         return newSVnv(v->value); | ||||
|     } else if (ConfigOptionInt* v = dynamic_cast<ConfigOptionInt*>(opt)) { | ||||
|         return newSViv(v->value); | ||||
|     if (ConfigOptionFloat* optv = dynamic_cast<ConfigOptionFloat*>(opt)) { | ||||
|         return newSVnv(optv->value); | ||||
|     } else if (ConfigOptionInt* optv = dynamic_cast<ConfigOptionInt*>(opt)) { | ||||
|         return newSViv(optv->value); | ||||
|     } else if (ConfigOptionString* optv = dynamic_cast<ConfigOptionString*>(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<ConfigOptionPoint*>(opt)) { | ||||
|         return optv->point.to_SV_pureperl(); | ||||
|     } else { | ||||
|         throw "Unknown option value type"; | ||||
|         std::string serialized = opt->serialize(); | ||||
|         return newSVpvn(serialized.c_str(), serialized.length()); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void | ||||
| ConfigBase::set(t_config_option_key opt_key, SV* value) { | ||||
|     ConfigOption* opt = this->option(opt_key, true); | ||||
|     assert(opt != NULL); | ||||
|      | ||||
|     if (ConfigOptionFloat* optv = dynamic_cast<ConfigOptionFloat*>(opt)) { | ||||
|         optv->value = SvNV(value); | ||||
|     } else if (ConfigOptionInt* optv = dynamic_cast<ConfigOptionInt*>(opt)) { | ||||
|         optv->value = SvIV(value); | ||||
|     } else if (ConfigOptionString* optv = dynamic_cast<ConfigOptionString*>(opt)) { | ||||
|         optv->value = std::string(SvPV_nolen(value), SvCUR(value)); | ||||
|     } else if (ConfigOptionPoint* optv = dynamic_cast<ConfigOptionPoint*>(opt)) { | ||||
|         optv->point.from_SV(value); | ||||
|     } else { | ||||
|         opt->deserialize( std::string(SvPV_nolen(value)) ); | ||||
|     } | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| DynamicConfig::~DynamicConfig () { | ||||
|     for (t_options_map::iterator it = this->options.begin(); it != this->options.end(); ++it) { | ||||
|         ConfigOption* opt = it->second; | ||||
|         if (opt != NULL) delete opt; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| ConfigOption* | ||||
| DynamicConfig::option(const t_config_option_key opt_key) { | ||||
| DynamicConfig::option(const t_config_option_key opt_key, bool create) { | ||||
|     t_options_map::iterator it = this->options.find(opt_key); | ||||
|     if (it == this->options.end()) return NULL; | ||||
|     if (it == this->options.end()) { | ||||
|         if (create) { | ||||
|             ConfigOption* opt; | ||||
|             if (Options[opt_key].type == coFloat) { | ||||
|                 opt = new ConfigOptionFloat (); | ||||
|             } else if (Options[opt_key].type == coInt) { | ||||
|                 opt = new ConfigOptionInt (); | ||||
|             } 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 { | ||||
|                 throw "Unknown option type"; | ||||
|             } | ||||
|             this->options[opt_key] = opt; | ||||
|             return opt; | ||||
|         } else { | ||||
|             return NULL; | ||||
|         } | ||||
|     } | ||||
|     return it->second; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -3,8 +3,13 @@ | |||
| 
 | ||||
| #include <myinit.h> | ||||
| #include <map> | ||||
| #include <sstream> | ||||
| #include <cstdio> | ||||
| #include <cstdlib> | ||||
| #include <iostream>; | ||||
| #include <string> | ||||
| #include <vector> | ||||
| #include "Point.hpp" | ||||
| 
 | ||||
| namespace Slic3r { | ||||
| 
 | ||||
|  | @ -14,27 +19,79 @@ typedef std::vector<std::string> t_config_option_keys; | |||
| class ConfigOption { | ||||
|     public: | ||||
|     virtual ~ConfigOption() {}; | ||||
|     virtual std::string serialize() = 0; | ||||
|     virtual void deserialize(std::string str) = 0; | ||||
| }; | ||||
| 
 | ||||
| class ConfigOptionFloat : public ConfigOption | ||||
| { | ||||
|     public: | ||||
|     float value; | ||||
|     ConfigOptionFloat() : value(0) {}; | ||||
|      | ||||
|     operator float() const { return this->value; }; | ||||
|      | ||||
|     std::string serialize() { | ||||
|         std::ostringstream ss; | ||||
|         ss << this->value; | ||||
|         return ss.str(); | ||||
|     }; | ||||
|      | ||||
|     void deserialize(std::string str) { | ||||
|         this->value = ::atof(str.c_str()); | ||||
|     }; | ||||
| }; | ||||
| 
 | ||||
| class ConfigOptionInt : public ConfigOption | ||||
| { | ||||
|     public: | ||||
|     int value; | ||||
|     ConfigOptionInt() : value(0) {}; | ||||
|      | ||||
|     operator int() const { return this->value; }; | ||||
|      | ||||
|     std::string serialize() { | ||||
|         std::ostringstream ss; | ||||
|         ss << this->value; | ||||
|         return ss.str(); | ||||
|     }; | ||||
|      | ||||
|     void deserialize(std::string str) { | ||||
|         this->value = ::atoi(str.c_str()); | ||||
|     }; | ||||
| }; | ||||
| 
 | ||||
| class ConfigOptionString : public ConfigOption | ||||
| { | ||||
|     public: | ||||
|     std::string value; | ||||
|     ConfigOptionString() : value("") {}; | ||||
|      | ||||
|     operator std::string() const { return this->value; }; | ||||
|      | ||||
|     std::string serialize() { | ||||
|         std::string str = this->value; | ||||
|          | ||||
|         // s/\R/\\n/g
 | ||||
|         size_t pos = 0; | ||||
|         while ((pos = str.find("\n", pos)) != std::string::npos || (pos = str.find("\r", pos)) != std::string::npos) { | ||||
|             str.replace(pos, 1, "\\n"); | ||||
|             pos += 2; // length of "\\n"
 | ||||
|         } | ||||
|          | ||||
|         return str;  | ||||
|     }; | ||||
|      | ||||
|     void deserialize(std::string str) { | ||||
|         // s/\\n/\n/g
 | ||||
|         size_t pos = 0; | ||||
|         while ((pos = str.find("\\n", pos)) != std::string::npos) { | ||||
|             str.replace(pos, 2, "\n"); | ||||
|             pos += 1; // length of "\n"
 | ||||
|         } | ||||
|          | ||||
|         this->value = str; | ||||
|     }; | ||||
| }; | ||||
| 
 | ||||
| class ConfigOptionFloatOrPercent : public ConfigOption | ||||
|  | @ -42,6 +99,44 @@ class ConfigOptionFloatOrPercent : public ConfigOption | |||
|     public: | ||||
|     float value; | ||||
|     bool percent; | ||||
|     ConfigOptionFloatOrPercent() : value(0), percent(false) {}; | ||||
|      | ||||
|     std::string serialize() { | ||||
|         std::ostringstream ss; | ||||
|         ss << this->value; | ||||
|         std::string s(ss.str()); | ||||
|         if (this->percent) s += "%"; | ||||
|         return s; | ||||
|     }; | ||||
|      | ||||
|     void deserialize(std::string str) { | ||||
|         if (str.find_first_of("%") != std::string::npos) { | ||||
|             sscanf(str.c_str(), "%f%%", &this->value); | ||||
|             this->percent = true; | ||||
|         } else { | ||||
|             this->value = ::atof(str.c_str()); | ||||
|             this->percent = false; | ||||
|         } | ||||
|     }; | ||||
| }; | ||||
| 
 | ||||
| class ConfigOptionPoint : public ConfigOption | ||||
| { | ||||
|     public: | ||||
|     Pointf point; | ||||
|     ConfigOptionPoint() : point(Pointf(0,0)) {}; | ||||
|      | ||||
|     std::string serialize() { | ||||
|         std::ostringstream ss; | ||||
|         ss << this->point.x; | ||||
|         ss << ","; | ||||
|         ss << this->point.y; | ||||
|         return ss.str(); | ||||
|     }; | ||||
|      | ||||
|     void deserialize(std::string str) { | ||||
|         sscanf(str.c_str(), "%f%*1[,x]%f", &this->point.x, &this->point.y); | ||||
|     }; | ||||
| }; | ||||
| 
 | ||||
| enum ConfigOptionType { | ||||
|  | @ -49,6 +144,7 @@ enum ConfigOptionType { | |||
|     coInt, | ||||
|     coString, | ||||
|     coFloatOrPercent, | ||||
|     coPoint, | ||||
| }; | ||||
| 
 | ||||
| class ConfigOptionDef | ||||
|  | @ -57,30 +153,41 @@ class ConfigOptionDef | |||
|     ConfigOptionType type; | ||||
|     std::string label; | ||||
|     std::string tooltip; | ||||
|     std::string ratio_over; | ||||
| }; | ||||
| 
 | ||||
| typedef std::map<t_config_option_key,ConfigOptionDef> t_optiondef_map; | ||||
| 
 | ||||
| ConfigOptionDef* get_config_option_def(const t_config_option_key opt_key); | ||||
| 
 | ||||
| class ConfigBase | ||||
| { | ||||
|     public: | ||||
|     virtual ConfigOption* option(const t_config_option_key opt_key) = 0; | ||||
|     virtual ConfigOption* option(const t_config_option_key opt_key, bool create = false) = 0; | ||||
|     virtual void keys(t_config_option_keys *keys) = 0; | ||||
|     void apply(ConfigBase &other, bool ignore_nonexistent = false); | ||||
|     std::string serialize(const t_config_option_key opt_key); | ||||
|     void set_deserialize(const t_config_option_key opt_key, std::string str); | ||||
|     float get_abs_value(const t_config_option_key opt_key); | ||||
|      | ||||
|     #ifdef SLIC3RXS | ||||
|     SV* get(t_config_option_key opt_key); | ||||
|     void set(t_config_option_key opt_key, SV* value); | ||||
|     #endif | ||||
| }; | ||||
| 
 | ||||
| class DynamicConfig : public ConfigBase | ||||
| { | ||||
|     public: | ||||
|     ConfigOption* option(const t_config_option_key opt_key); | ||||
|     DynamicConfig() {}; | ||||
|     ~DynamicConfig(); | ||||
|     ConfigOption* option(const t_config_option_key opt_key, bool create = false); | ||||
|     void keys(t_config_option_keys *keys); | ||||
|     bool has(const t_config_option_key opt_key) const; | ||||
|      | ||||
|     private: | ||||
|     DynamicConfig(const DynamicConfig& other);              // we disable this by making it private and unimplemented
 | ||||
|     DynamicConfig& operator= (const DynamicConfig& other);  // we disable this by making it private and unimplemented
 | ||||
|     typedef std::map<t_config_option_key,ConfigOption*> t_options_map; | ||||
|     t_options_map options; | ||||
| }; | ||||
|  | @ -96,10 +203,19 @@ class FullConfig : public StaticConfig | |||
|     public: | ||||
|     ConfigOptionFloat layer_height; | ||||
|     ConfigOptionFloatOrPercent first_layer_height; | ||||
|     ConfigOptionInt perimeters; | ||||
|     ConfigOptionString extrusion_axis; | ||||
|     ConfigOptionPoint print_center; | ||||
|     ConfigOptionString notes; | ||||
|      | ||||
|     ConfigOption* option(const t_config_option_key opt_key) { | ||||
|     ConfigOption* option(const t_config_option_key opt_key, bool create = false) { | ||||
|         assert(!create);  // can't create options in StaticConfig
 | ||||
|         if (opt_key == "layer_height")              return &this->layer_height; | ||||
|         if (opt_key == "first_layer_height")        return &this->first_layer_height; | ||||
|         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 == "notes")                     return &this->notes; | ||||
|         return NULL; | ||||
|     }; | ||||
| }; | ||||
|  | @ -111,6 +227,18 @@ static t_optiondef_map _build_optiondef_map () { | |||
|     Options["layer_height"].tooltip = "This setting controls the height (and thus the total number) of the slices/layers. Thinner layers give better accuracy but take more time to print."; | ||||
| 
 | ||||
|     Options["first_layer_height"].type = coFloatOrPercent; | ||||
|     Options["first_layer_height"].ratio_over = "layer_height"; | ||||
|      | ||||
|     Options["perimeters"].type = coInt; | ||||
|     Options["perimeters"].label = "Perimeters (minimum)"; | ||||
|     Options["perimeters"].tooltip = "This option sets the number of perimeters to generate for each layer. Note that Slic3r may increase this number automatically when it detects sloping surfaces which benefit from a higher number of perimeters if the Extra Perimeters option is enabled."; | ||||
|      | ||||
|     Options["extrusion_axis"].type = coString; | ||||
|      | ||||
|     Options["print_center"].type = coPoint; | ||||
|      | ||||
|     Options["notes"].type = coString; | ||||
|      | ||||
|     return Options; | ||||
| } | ||||
| 
 | ||||
|  | @ -120,6 +248,10 @@ static FullConfig _build_default_config () { | |||
|     defconf.layer_height.value = 0.4; | ||||
|     defconf.first_layer_height.value = 0.35; | ||||
|     defconf.first_layer_height.percent = false; | ||||
|     defconf.perimeters.value = 3; | ||||
|     defconf.extrusion_axis.value = "E"; | ||||
|     defconf.print_center.point = Pointf(100,100); | ||||
|     defconf.notes.value = ""; | ||||
|      | ||||
|     return defconf; | ||||
| } | ||||
|  |  | |||
|  | @ -171,6 +171,23 @@ Point::from_SV_check(SV* point_sv) | |||
|         this->from_SV(point_sv); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| SV* | ||||
| Pointf::to_SV_pureperl() const { | ||||
|     AV* av = newAV(); | ||||
|     av_fill(av, 1); | ||||
|     av_store(av, 0, newSVnv(this->x)); | ||||
|     av_store(av, 1, newSVnv(this->y)); | ||||
|     return newRV_noinc((SV*)av); | ||||
| } | ||||
| 
 | ||||
| void | ||||
| Pointf::from_SV(SV* point_sv) | ||||
| { | ||||
|     AV* point_av = (AV*)SvRV(point_sv); | ||||
|     this->x = SvNV(*av_fetch(point_av, 0, 0)); | ||||
|     this->y = SvNV(*av_fetch(point_av, 1, 0)); | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -42,6 +42,19 @@ class Point | |||
|     #endif | ||||
| }; | ||||
| 
 | ||||
| class Pointf | ||||
| { | ||||
|     public: | ||||
|     float x; | ||||
|     float y; | ||||
|     explicit Pointf(float _x = 0, float _y = 0): x(_x), y(_y) {}; | ||||
|      | ||||
|     #ifdef SLIC3RXS | ||||
|     void from_SV(SV* point_sv); | ||||
|     SV* to_SV_pureperl() const; | ||||
|     #endif | ||||
| }; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
|  |  | |||
							
								
								
									
										45
									
								
								xs/t/15_config.t
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								xs/t/15_config.t
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,45 @@ | |||
| #!/usr/bin/perl | ||||
| 
 | ||||
| use strict; | ||||
| use warnings; | ||||
| 
 | ||||
| use Slic3r::XS; | ||||
| use Test::More tests => 16; | ||||
| 
 | ||||
| { | ||||
|     my $config = Slic3r::Config->new; | ||||
|      | ||||
|     $config->set('layer_height', 0.3); | ||||
|     ok abs($config->get('layer_height') - 0.3) < 1e-4, 'set/get float'; | ||||
|     is $config->serialize('layer_height'), '0.3', 'serialize float'; | ||||
|      | ||||
|     $config->set('perimeters', 2); | ||||
|     is $config->get('perimeters'), 2, 'set/get int'; | ||||
|     is $config->serialize('perimeters'), '2', 'serialize int'; | ||||
|      | ||||
|     $config->set('extrusion_axis', 'A'); | ||||
|     is $config->get('extrusion_axis'), 'A', 'set/get string'; | ||||
|     is $config->serialize('extrusion_axis'), 'A', 'serialize string'; | ||||
|      | ||||
|     $config->set('notes', "foo\nbar"); | ||||
|     is $config->get('notes'), "foo\nbar", 'set/get string with newline'; | ||||
|     is $config->serialize('notes'), 'foo\nbar', 'serialize string with newline'; | ||||
|     $config->set_deserialize('notes', 'bar\nbaz'); | ||||
|     is $config->get('notes'), "bar\nbaz", 'deserialize string with newline'; | ||||
|      | ||||
|     $config->set('first_layer_height', 0.3); | ||||
|     ok abs($config->get('first_layer_height') - 0.3) < 1e-4, 'set/get absolute floatOrPercent'; | ||||
|     is $config->serialize('first_layer_height'), '0.3', 'serialize absolute floatOrPercent'; | ||||
|      | ||||
|     $config->set('first_layer_height', '50%'); | ||||
|     ok abs($config->get_abs_value('first_layer_height') - 0.15) < 1e-4, 'set/get relative floatOrPercent'; | ||||
|     is $config->serialize('first_layer_height'), '50%', 'serialize relative floatOrPercent'; | ||||
|      | ||||
|     $config->set('print_center', [50,80]); | ||||
|     is_deeply $config->get('print_center'), [50,80], 'set/get point'; | ||||
|     is $config->serialize('print_center'), '50,80', 'serialize point'; | ||||
|     $config->set_deserialize('print_center', '20,10'); | ||||
|     is_deeply $config->get('print_center'), [20,10], 'deserialize point'; | ||||
| } | ||||
| 
 | ||||
| __END__ | ||||
|  | @ -9,6 +9,10 @@ | |||
|     DynamicConfig(); | ||||
|     ~DynamicConfig(); | ||||
|     SV* get(t_config_option_key opt_key); | ||||
|     void set(t_config_option_key opt_key, SV* value); | ||||
|     void set_deserialize(t_config_option_key opt_key, std::string str); | ||||
|     std::string serialize(t_config_option_key opt_key); | ||||
|     float get_abs_value(t_config_option_key opt_key); | ||||
| %{ | ||||
| 
 | ||||
| %} | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Alessandro Ranellucci
						Alessandro Ranellucci