chiark / gitweb /
Merge conflic solved
[cura.git] / Cura / util / profile.py
1 from __future__ import absolute_import\r
2 from __future__ import division\r
3 #Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module.\r
4 import __init__\r
5 \r
6 import ConfigParser\r
7 import os\r
8 import traceback\r
9 import math\r
10 \r
11 #########################################################\r
12 ## Profile and preferences functions\r
13 #########################################################\r
14 \r
15 #Single place to store the defaults, so we have a consistent set of default settings.\r
16 profileDefaultSettings = {\r
17         'nozzle_size': '0.4',\r
18         'layer_height': '0.2',\r
19         'wall_thickness': '0.8',\r
20         'solid_layer_thickness': '0.6',\r
21         'fill_density': '20',\r
22         'skirt_line_count': '1',\r
23         'skirt_gap': '6.0',\r
24         'print_speed': '50',\r
25         'print_temperature': '0',\r
26         'support': 'None',\r
27         'filament_diameter': '2.89',\r
28         'filament_density': '1.00',\r
29         'machine_center_x': '100',\r
30         'machine_center_y': '100',\r
31         'retraction_min_travel': '5.0',\r
32         'retraction_speed': '13.5',\r
33         'retraction_amount': '0.0',\r
34         'retraction_extra': '0.0',\r
35         'travel_speed': '150',\r
36         'max_z_speed': '1.0',\r
37         'bottom_layer_speed': '25',\r
38         'cool_min_layer_time': '10',\r
39         'fan_enabled': 'True',\r
40         'fan_layer': '0',\r
41         'fan_speed': '100',\r
42         'model_scale': '1.0',\r
43         'flip_x': 'False',\r
44         'flip_y': 'False',\r
45         'flip_z': 'False',\r
46         'swap_xz': 'False',\r
47         'swap_yz': 'False',\r
48         'model_rotate_base': '0',\r
49         'model_multiply_x': '1',\r
50         'model_multiply_y': '1',\r
51         'extra_base_wall_thickness': '0.0',\r
52         'sequence': 'Loops > Perimeter > Infill',\r
53         'force_first_layer_sequence': 'True',\r
54         'infill_type': 'Line',\r
55         'solid_top': 'True',\r
56         'fill_overlap': '15',\r
57         'support_rate': '50',\r
58         'support_distance': '0.5',\r
59         'joris': 'False',\r
60         'enable_skin': 'False',\r
61         'enable_raft': 'False',\r
62         'cool_min_feedrate': '5',\r
63         'bridge_speed': '100',\r
64         'bridge_material_amount': '100',\r
65         'raft_margin': '5',\r
66         'raft_base_material_amount': '100',\r
67         'raft_interface_material_amount': '100',
68         'bottom_thickness': '0.3',\r
69         'add_start_end_gcode': 'True',\r
70         'gcode_extension': 'gcode',
71 }\r
72 preferencesDefaultSettings = {\r
73         'wizardDone': 'False',\r
74         'startMode': 'Simple',\r
75         'lastFile': 'None',\r
76         'machine_width': '205',\r
77         'machine_depth': '205',\r
78         'machine_height': '200',\r
79         'filament_density': '1300',\r
80         'steps_per_e': '0',\r
81         'serial_port': 'AUTO',\r
82         'serial_baud': '250000',\r
83         'slicer': 'Cura (Skeinforge based)',\r
84         'save_profile': 'False',\r
85         'filament_cost_kg': '0',\r
86         'filament_cost_meter': '0',\r
87 }\r
88 \r
89 def getDefaultProfilePath():\r
90         return os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../current_profile.ini"))\r
91 \r
92 def loadGlobalProfile(filename):\r
93         #Read a configuration file as global config\r
94         global globalProfileParser\r
95         globalProfileParser = ConfigParser.ConfigParser()\r
96         globalProfileParser.read(filename)\r
97 \r
98 def saveGlobalProfile(filename):\r
99         #Save the current profile to an ini file\r
100         globalProfileParser.write(open(filename, 'w'))\r
101 \r
102 def loadGlobalProfileFromString(options):\r
103         global globalProfileParser\r
104         globalProfileParser = ConfigParser.ConfigParser()\r
105         globalProfileParser.add_section('profile')\r
106         for option in options.split('#'):\r
107                 (key, value) = option.split('=', 1)\r
108                 globalProfileParser.set('profile', key, value)\r
109 \r
110 def getGlobalProfileString():\r
111         global globalProfileParser\r
112         if not globals().has_key('globalProfileParser'):\r
113                 loadGlobalProfile(getDefaultProfilePath())\r
114         \r
115         ret = []\r
116         for key in globalProfileParser.options('profile'):\r
117                 ret.append(key + "=" + globalProfileParser.get('profile', key))\r
118         return '#'.join(ret)\r
119 \r
120 def getProfileSetting(name):\r
121         if name in profileDefaultSettings:\r
122                 default = profileDefaultSettings[name]\r
123         else:\r
124                 print "Missing default setting for: '" + name + "'"\r
125                 profileDefaultSettings[name] = ''\r
126                 default = ''\r
127         \r
128         #Check if we have a configuration file loaded, else load the default.\r
129         if not globals().has_key('globalProfileParser'):\r
130                 loadGlobalProfile(getDefaultProfilePath())\r
131         if not globalProfileParser.has_option('profile', name):\r
132                 if not globalProfileParser.has_section('profile'):\r
133                         globalProfileParser.add_section('profile')\r
134                 globalProfileParser.set('profile', name, str(default))\r
135                 #print name + " not found in profile, so using default: " + str(default)\r
136                 return default\r
137         return globalProfileParser.get('profile', name)\r
138 \r
139 def getProfileSettingFloat(name):\r
140         try:\r
141                 return float(eval(getProfileSetting(name), {}, {}))\r
142         except (ValueError, SyntaxError):\r
143                 return 0.0\r
144 \r
145 def putProfileSetting(name, value):\r
146         #Check if we have a configuration file loaded, else load the default.\r
147         if not globals().has_key('globalProfileParser'):\r
148                 loadGlobalProfile(getDefaultProfilePath())\r
149         if not globalProfileParser.has_section('profile'):\r
150                 globalProfileParser.add_section('profile')\r
151         globalProfileParser.set('profile', name, str(value))\r
152 \r
153 global globalPreferenceParser\r
154 globalPreferenceParser = None\r
155 \r
156 def getPreferencePath():\r
157         return os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../preferences.ini"))\r
158 \r
159 def getPreference(name):\r
160         if name in preferencesDefaultSettings:\r
161                 default = preferencesDefaultSettings[name]\r
162         else:\r
163                 print "Missing default setting for: '" + name + "'"\r
164                 preferencesDefaultSettings[name] = ''\r
165                 default = ''\r
166 \r
167         global globalPreferenceParser\r
168         if globalPreferenceParser == None:\r
169                 globalPreferenceParser = ConfigParser.ConfigParser()\r
170                 globalPreferenceParser.read(getPreferencePath())\r
171         if not globalPreferenceParser.has_option('preference', name):\r
172                 if not globalPreferenceParser.has_section('preference'):\r
173                         globalPreferenceParser.add_section('preference')\r
174                 globalPreferenceParser.set('preference', name, str(default))\r
175                 #print name + " not found in preferences, so using default: " + str(default)\r
176                 return default\r
177         return unicode(globalPreferenceParser.get('preference', name), "utf-8")\r
178 \r
179 def putPreference(name, value):\r
180         #Check if we have a configuration file loaded, else load the default.\r
181         global globalPreferenceParser\r
182         if globalPreferenceParser == None:\r
183                 globalPreferenceParser = ConfigParser.ConfigParser()\r
184                 globalPreferenceParser.read(getPreferencePath())\r
185         if not globalPreferenceParser.has_section('preference'):\r
186                 globalPreferenceParser.add_section('preference')\r
187         globalPreferenceParser.set('preference', name, unicode(value).encode("utf-8"))\r
188         globalPreferenceParser.write(open(getPreferencePath(), 'w'))\r
189 \r
190 #########################################################\r
191 ## Utility functions to calculate common profile values\r
192 #########################################################\r
193 def calculateEdgeWidth():\r
194         wallThickness = getProfileSettingFloat('wall_thickness')\r
195         nozzleSize = getProfileSettingFloat('nozzle_size')\r
196         \r
197         if wallThickness < nozzleSize:\r
198                 return wallThickness\r
199 \r
200         lineCount = int(wallThickness / nozzleSize)\r
201         lineWidth = wallThickness / lineCount\r
202         lineWidthAlt = wallThickness / (lineCount + 1)\r
203         if lineWidth > nozzleSize * 1.5:\r
204                 return lineWidthAlt\r
205         return lineWidth\r
206 \r
207 def calculateLineCount():\r
208         wallThickness = getProfileSettingFloat('wall_thickness')\r
209         nozzleSize = getProfileSettingFloat('nozzle_size')\r
210         \r
211         if wallThickness < nozzleSize:\r
212                 return 1\r
213 \r
214         lineCount = int(wallThickness / nozzleSize + 0.0001)\r
215         lineWidth = wallThickness / lineCount\r
216         lineWidthAlt = wallThickness / (lineCount + 1)\r
217         if lineWidth > nozzleSize * 1.5:\r
218                 return lineCount + 1\r
219         return lineCount\r
220 \r
221 def calculateSolidLayerCount():\r
222         layerHeight = getProfileSettingFloat('layer_height')\r
223         solidThickness = getProfileSettingFloat('solid_layer_thickness')\r
224         return int(math.ceil(solidThickness / layerHeight - 0.0001))\r
225 \r
226 #########################################################\r
227 ## Alteration file functions\r
228 #########################################################\r
229 def getCuraBasePath():\r
230         return os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(__file__)), ".."))\r
231 \r
232 def getAlterationFilePath(filename):\r
233         return os.path.join(getCuraBasePath(), "alterations", filename)\r
234 \r
235 def getAlterationFileContents(filename, allowMagicPrefix = True):\r
236         "Get the file from the fileName or the lowercase fileName in the alterations directories."\r
237         prefix = ''\r
238         if allowMagicPrefix:\r
239                 if filename == 'start.gcode':\r
240                         #For the start code, hack the temperature and the steps per E value into it. So the temperature is reached before the start code extrusion.\r
241                         #We also set our steps per E here, if configured.\r
242                         eSteps = float(getPreference('steps_per_e'))\r
243                         if eSteps > 0:\r
244                                 prefix += 'M92 E'+str(eSteps)+'\n'\r
245                         temp = getProfileSettingFloat('print_temperature')\r
246                         if temp > 0:\r
247                                 prefix += 'M109 S'+str(temp)+'\n'\r
248                 elif filename == 'replace.csv':\r
249                         prefix = 'M101\nM103\n'\r
250         fullFilename = getAlterationFilePath(filename)\r
251         if os.path.isfile(fullFilename):\r
252                 file = open(fullFilename, "r")\r
253                 fileText = file.read()\r
254                 file.close()\r
255                 return prefix + fileText\r
256         return prefix\r
257 \r