2012-03-17 11:03:38 +00:00
from __future__ import absolute_import
2012-04-10 14:45:53 +00:00
from __future__ import division
2012-03-17 11:03:38 +00:00
#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.
import __init__
import ConfigParser
import os
import traceback
2012-03-26 13:03:26 +00:00
import math
2012-03-17 11:03:38 +00:00
2012-03-27 11:48:40 +00:00
#########################################################
## Profile and preferences functions
#########################################################
2012-03-17 11:03:38 +00:00
#Single place to store the defaults, so we have a consistent set of default settings.
profileDefaultSettings = {
2012-03-26 11:45:31 +00:00
' nozzle_size ' : ' 0.4 ' ,
2012-03-17 11:03:38 +00:00
' layer_height ' : ' 0.2 ' ,
' wall_thickness ' : ' 0.8 ' ,
' solid_layer_thickness ' : ' 0.6 ' ,
' fill_density ' : ' 20 ' ,
' skirt_line_count ' : ' 1 ' ,
' skirt_gap ' : ' 6.0 ' ,
' print_speed ' : ' 50 ' ,
' print_temperature ' : ' 0 ' ,
' support ' : ' None ' ,
' filament_diameter ' : ' 2.89 ' ,
' filament_density ' : ' 1.00 ' ,
' machine_center_x ' : ' 100 ' ,
' machine_center_y ' : ' 100 ' ,
' retraction_min_travel ' : ' 5.0 ' ,
2012-04-18 08:50:56 +00:00
' retraction_speed ' : ' 40.0 ' ,
2012-03-17 11:03:38 +00:00
' retraction_amount ' : ' 0.0 ' ,
' retraction_extra ' : ' 0.0 ' ,
' travel_speed ' : ' 150 ' ,
' max_z_speed ' : ' 1.0 ' ,
' bottom_layer_speed ' : ' 25 ' ,
' cool_min_layer_time ' : ' 10 ' ,
2012-04-02 14:09:56 +00:00
' fan_enabled ' : ' True ' ,
' fan_layer ' : ' 0 ' ,
' fan_speed ' : ' 100 ' ,
2012-03-17 11:03:38 +00:00
' model_scale ' : ' 1.0 ' ,
' flip_x ' : ' False ' ,
' flip_y ' : ' False ' ,
' flip_z ' : ' False ' ,
2012-04-06 13:15:41 +00:00
' swap_xz ' : ' False ' ,
' swap_yz ' : ' False ' ,
2012-03-17 11:03:38 +00:00
' model_rotate_base ' : ' 0 ' ,
' model_multiply_x ' : ' 1 ' ,
' model_multiply_y ' : ' 1 ' ,
' extra_base_wall_thickness ' : ' 0.0 ' ,
' sequence ' : ' Loops > Perimeter > Infill ' ,
' force_first_layer_sequence ' : ' True ' ,
' infill_type ' : ' Line ' ,
' solid_top ' : ' True ' ,
' fill_overlap ' : ' 15 ' ,
2012-04-05 16:00:22 +00:00
' support_rate ' : ' 50 ' ,
2012-03-17 11:03:38 +00:00
' support_distance ' : ' 0.5 ' ,
' joris ' : ' False ' ,
2012-04-06 15:08:49 +00:00
' enable_skin ' : ' False ' ,
2012-03-18 22:00:33 +00:00
' enable_raft ' : ' False ' ,
2012-03-20 10:01:47 +00:00
' cool_min_feedrate ' : ' 5 ' ,
' bridge_speed ' : ' 100 ' ,
' bridge_material_amount ' : ' 100 ' ,
2012-03-20 14:59:31 +00:00
' raft_margin ' : ' 5 ' ,
' raft_base_material_amount ' : ' 100 ' ,
2012-04-17 17:14:48 +00:00
' raft_interface_material_amount ' : ' 100 ' ,
2012-04-16 10:26:55 +00:00
' bottom_thickness ' : ' 0.3 ' ,
2012-04-17 17:12:40 +00:00
' add_start_end_gcode ' : ' True ' ,
2012-04-17 17:14:48 +00:00
' gcode_extension ' : ' gcode ' ,
2012-03-17 11:03:38 +00:00
}
preferencesDefaultSettings = {
' wizardDone ' : ' False ' ,
2012-03-29 12:45:14 +00:00
' startMode ' : ' Simple ' ,
2012-03-17 11:03:38 +00:00
' lastFile ' : ' None ' ,
' machine_width ' : ' 205 ' ,
' machine_depth ' : ' 205 ' ,
' machine_height ' : ' 200 ' ,
2012-04-03 10:06:02 +00:00
' filament_density ' : ' 1300 ' ,
2012-03-17 11:03:38 +00:00
' steps_per_e ' : ' 0 ' ,
' serial_port ' : ' AUTO ' ,
' serial_baud ' : ' 250000 ' ,
2012-03-26 13:03:26 +00:00
' slicer ' : ' Cura (Skeinforge based) ' ,
2012-04-12 11:58:19 +00:00
' save_profile ' : ' False ' ,
2012-04-12 12:26:03 +00:00
' filament_cost_kg ' : ' 0 ' ,
' filament_cost_meter ' : ' 0 ' ,
2012-03-17 11:03:38 +00:00
}
def getDefaultProfilePath ( ) :
return os . path . normpath ( os . path . join ( os . path . dirname ( os . path . abspath ( __file__ ) ) , " ../current_profile.ini " ) )
def loadGlobalProfile ( filename ) :
2012-03-21 17:22:00 +00:00
#Read a configuration file as global config
2012-03-17 11:03:38 +00:00
global globalProfileParser
globalProfileParser = ConfigParser . ConfigParser ( )
globalProfileParser . read ( filename )
def saveGlobalProfile ( filename ) :
2012-03-21 17:22:00 +00:00
#Save the current profile to an ini file
2012-03-17 11:03:38 +00:00
globalProfileParser . write ( open ( filename , ' w ' ) )
2012-03-30 11:54:49 +00:00
def loadGlobalProfileFromString ( options ) :
2012-03-21 17:22:00 +00:00
global globalProfileParser
globalProfileParser = ConfigParser . ConfigParser ( )
2012-03-30 11:54:49 +00:00
globalProfileParser . add_section ( ' profile ' )
for option in options . split ( ' # ' ) :
( key , value ) = option . split ( ' = ' , 1 )
globalProfileParser . set ( ' profile ' , key , value )
def getGlobalProfileString ( ) :
global globalProfileParser
if not globals ( ) . has_key ( ' globalProfileParser ' ) :
loadGlobalProfile ( getDefaultProfilePath ( ) )
ret = [ ]
for key in globalProfileParser . options ( ' profile ' ) :
ret . append ( key + " = " + globalProfileParser . get ( ' profile ' , key ) )
return ' # ' . join ( ret )
2012-03-21 17:22:00 +00:00
2012-03-17 11:03:38 +00:00
def getProfileSetting ( name ) :
if name in profileDefaultSettings :
default = profileDefaultSettings [ name ]
else :
print " Missing default setting for: ' " + name + " ' "
profileDefaultSettings [ name ] = ' '
default = ' '
#Check if we have a configuration file loaded, else load the default.
if not globals ( ) . has_key ( ' globalProfileParser ' ) :
loadGlobalProfile ( getDefaultProfilePath ( ) )
if not globalProfileParser . has_option ( ' profile ' , name ) :
if not globalProfileParser . has_section ( ' profile ' ) :
globalProfileParser . add_section ( ' profile ' )
globalProfileParser . set ( ' profile ' , name , str ( default ) )
2012-04-10 14:45:53 +00:00
#print name + " not found in profile, so using default: " + str(default)
2012-03-17 11:03:38 +00:00
return default
return globalProfileParser . get ( ' profile ' , name )
2012-04-10 14:45:53 +00:00
def getProfileSettingFloat ( name ) :
try :
return float ( eval ( getProfileSetting ( name ) , { } , { } ) )
except ( ValueError , SyntaxError ) :
return 0.0
2012-03-17 11:03:38 +00:00
def putProfileSetting ( name , value ) :
#Check if we have a configuration file loaded, else load the default.
if not globals ( ) . has_key ( ' globalProfileParser ' ) :
loadGlobalProfile ( getDefaultProfilePath ( ) )
if not globalProfileParser . has_section ( ' profile ' ) :
globalProfileParser . add_section ( ' profile ' )
globalProfileParser . set ( ' profile ' , name , str ( value ) )
global globalPreferenceParser
globalPreferenceParser = None
def getPreferencePath ( ) :
return os . path . normpath ( os . path . join ( os . path . dirname ( os . path . abspath ( __file__ ) ) , " ../preferences.ini " ) )
def getPreference ( name ) :
if name in preferencesDefaultSettings :
default = preferencesDefaultSettings [ name ]
else :
print " Missing default setting for: ' " + name + " ' "
preferencesDefaultSettings [ name ] = ' '
default = ' '
global globalPreferenceParser
if globalPreferenceParser == None :
globalPreferenceParser = ConfigParser . ConfigParser ( )
globalPreferenceParser . read ( getPreferencePath ( ) )
if not globalPreferenceParser . has_option ( ' preference ' , name ) :
if not globalPreferenceParser . has_section ( ' preference ' ) :
globalPreferenceParser . add_section ( ' preference ' )
globalPreferenceParser . set ( ' preference ' , name , str ( default ) )
2012-04-10 14:45:53 +00:00
#print name + " not found in preferences, so using default: " + str(default)
2012-03-17 11:03:38 +00:00
return default
2012-04-04 09:10:08 +00:00
return unicode ( globalPreferenceParser . get ( ' preference ' , name ) , " utf-8 " )
2012-03-17 11:03:38 +00:00
def putPreference ( name , value ) :
#Check if we have a configuration file loaded, else load the default.
global globalPreferenceParser
if globalPreferenceParser == None :
globalPreferenceParser = ConfigParser . ConfigParser ( )
globalPreferenceParser . read ( getPreferencePath ( ) )
if not globalPreferenceParser . has_section ( ' preference ' ) :
globalPreferenceParser . add_section ( ' preference ' )
2012-04-17 11:49:34 +00:00
globalPreferenceParser . set ( ' preference ' , name , unicode ( value ) . encode ( " utf-8 " ) )
2012-03-17 11:03:38 +00:00
globalPreferenceParser . write ( open ( getPreferencePath ( ) , ' w ' ) )
2012-03-26 13:03:26 +00:00
2012-03-27 11:48:40 +00:00
#########################################################
2012-03-26 13:03:26 +00:00
## Utility functions to calculate common profile values
2012-03-27 11:48:40 +00:00
#########################################################
2012-03-26 13:03:26 +00:00
def calculateEdgeWidth ( ) :
2012-04-10 14:45:53 +00:00
wallThickness = getProfileSettingFloat ( ' wall_thickness ' )
nozzleSize = getProfileSettingFloat ( ' nozzle_size ' )
2012-03-26 13:03:26 +00:00
if wallThickness < nozzleSize :
return wallThickness
lineCount = int ( wallThickness / nozzleSize )
lineWidth = wallThickness / lineCount
lineWidthAlt = wallThickness / ( lineCount + 1 )
if lineWidth > nozzleSize * 1.5 :
return lineWidthAlt
return lineWidth
def calculateLineCount ( ) :
2012-04-10 14:45:53 +00:00
wallThickness = getProfileSettingFloat ( ' wall_thickness ' )
nozzleSize = getProfileSettingFloat ( ' nozzle_size ' )
2012-03-26 13:03:26 +00:00
if wallThickness < nozzleSize :
return 1
lineCount = int ( wallThickness / nozzleSize + 0.0001 )
lineWidth = wallThickness / lineCount
lineWidthAlt = wallThickness / ( lineCount + 1 )
if lineWidth > nozzleSize * 1.5 :
return lineCount + 1
return lineCount
def calculateSolidLayerCount ( ) :
2012-04-10 14:45:53 +00:00
layerHeight = getProfileSettingFloat ( ' layer_height ' )
solidThickness = getProfileSettingFloat ( ' solid_layer_thickness ' )
2012-03-26 13:03:26 +00:00
return int ( math . ceil ( solidThickness / layerHeight - 0.0001 ) )
2012-03-27 11:48:40 +00:00
#########################################################
## Alteration file functions
#########################################################
def getCuraBasePath ( ) :
return os . path . abspath ( os . path . join ( os . path . dirname ( os . path . abspath ( __file__ ) ) , " .. " ) )
def getAlterationFilePath ( filename ) :
return os . path . join ( getCuraBasePath ( ) , " alterations " , filename )
def getAlterationFileContents ( filename , allowMagicPrefix = True ) :
" Get the file from the fileName or the lowercase fileName in the alterations directories. "
prefix = ' '
if allowMagicPrefix :
if filename == ' start.gcode ' :
#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.
#We also set our steps per E here, if configured.
eSteps = float ( getPreference ( ' steps_per_e ' ) )
if eSteps > 0 :
prefix + = ' M92 E ' + str ( eSteps ) + ' \n '
2012-04-10 14:45:53 +00:00
temp = getProfileSettingFloat ( ' print_temperature ' )
2012-03-27 11:48:40 +00:00
if temp > 0 :
prefix + = ' M109 S ' + str ( temp ) + ' \n '
elif filename == ' replace.csv ' :
prefix = ' M101 \n M103 \n '
fullFilename = getAlterationFilePath ( filename )
if os . path . isfile ( fullFilename ) :
file = open ( fullFilename , " r " )
fileText = file . read ( )
file . close ( )
return prefix + fileText
return prefix