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