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__
2012-11-06 07:52:16 +00:00
import os , traceback , math , re , zlib , base64 , time , sys , platform , glob , string
2012-10-31 10:04:08 +00:00
import cPickle as pickle
2012-06-22 13:37:52 +00:00
if sys . version_info [ 0 ] < 3 :
2012-06-21 17:53:18 +00:00
import ConfigParser
else :
import configparser as ConfigParser
2012-10-22 12:01:19 +00:00
from util import version
2012-03-17 11:03:38 +00:00
2012-03-27 11:48:40 +00:00
#########################################################
2012-04-23 16:02:30 +00:00
## Default settings when none are found.
2012-03-27 11:48:40 +00:00
#########################################################
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 ' ,
2012-04-23 09:35:52 +00:00
' skirt_gap ' : ' 3.0 ' ,
2012-03-17 11:03:38 +00:00
' print_speed ' : ' 50 ' ,
2012-10-03 13:11:35 +00:00
' print_temperature ' : ' 220 ' ,
2012-08-03 09:10:28 +00:00
' print_bed_temperature ' : ' 70 ' ,
2012-03-17 11:03:38 +00:00
' support ' : ' None ' ,
' filament_diameter ' : ' 2.89 ' ,
' filament_density ' : ' 1.00 ' ,
' machine_center_x ' : ' 100 ' ,
' machine_center_y ' : ' 100 ' ,
' retraction_min_travel ' : ' 5.0 ' ,
2012-10-03 13:11:35 +00:00
' retraction_enable ' : ' False ' ,
2012-04-18 08:50:56 +00:00
' retraction_speed ' : ' 40.0 ' ,
2012-10-03 13:11:35 +00:00
' retraction_amount ' : ' 4.5 ' ,
2012-03-17 11:03:38 +00:00
' retraction_extra ' : ' 0.0 ' ,
2012-06-04 13:06:48 +00:00
' retract_on_jumps_only ' : ' True ' ,
2012-03-17 11:03:38 +00:00
' travel_speed ' : ' 150 ' ,
2012-04-23 09:35:52 +00:00
' max_z_speed ' : ' 3.0 ' ,
' bottom_layer_speed ' : ' 20 ' ,
2012-03-17 11:03:38 +00:00
' cool_min_layer_time ' : ' 10 ' ,
2012-04-02 14:09:56 +00:00
' fan_enabled ' : ' True ' ,
2012-04-27 11:45:32 +00:00
' fan_layer ' : ' 1 ' ,
2012-04-02 14:09:56 +00:00
' fan_speed ' : ' 100 ' ,
2012-07-03 15:45:18 +00:00
' fan_speed_max ' : ' 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 ' ,
2012-09-12 15:02:42 +00:00
' support_dual_extrusion ' : ' False ' ,
2012-03-17 11:03:38 +00:00
' 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-07-31 15:03:24 +00:00
' cool_min_feedrate ' : ' 10 ' ,
2012-03-20 10:01:47 +00:00
' bridge_speed ' : ' 100 ' ,
2012-03-20 14:59:31 +00:00
' raft_margin ' : ' 5 ' ,
' raft_base_material_amount ' : ' 100 ' ,
2012-04-19 13:08:50 +00:00
' raft_interface_material_amount ' : ' 100 ' ,
2012-04-16 10:26:55 +00:00
' bottom_thickness ' : ' 0.3 ' ,
2012-10-31 10:04:08 +00:00
' plugin_config ' : ' ' ,
2012-04-24 09:59:17 +00:00
2012-04-17 17:12:40 +00:00
' add_start_end_gcode ' : ' True ' ,
2012-04-19 13:08:50 +00:00
' gcode_extension ' : ' gcode ' ,
2012-05-03 10:29:55 +00:00
' alternative_center ' : ' ' ,
' clear_z ' : ' 0.0 ' ,
' extruder ' : ' 0 ' ,
2012-03-17 11:03:38 +00:00
}
2012-04-23 16:02:30 +00:00
alterationDefault = {
#######################################################################################
2012-07-05 13:42:59 +00:00
' start.gcode ' : """ ;Sliced {filename} at: {day} {date} {time}
2012-05-20 09:15:20 +00:00
; Basic settings : Layer height : { layer_height } Walls : { wall_thickness } Fill : { fill_density }
2012-07-05 12:12:13 +00:00
; Print time : { print_time }
; Filament used : { filament_amount } m { filament_weight } g
; Filament cost : { filament_cost }
2012-04-23 16:02:30 +00:00
G21 ; metric values
G90 ; absolute positioning
2012-05-16 14:47:51 +00:00
M107 ; start with the fan off
2012-04-23 16:02:30 +00:00
G28 X0 Y0 ; move X / Y to min endstops
G28 Z0 ; move Z to min endstops
G92 X0 Y0 Z0 E0 ; reset software position to front / left / z = 0.0
2012-07-05 14:08:18 +00:00
2012-04-23 16:02:30 +00:00
G1 Z15 .0 F { max_z_speed } ; move the platform down 15 mm
2012-07-05 14:08:18 +00:00
G92 E0 ; zero the extruded length
G1 F200 E3 ; extrude 3 mm of feed stock
2012-04-23 16:02:30 +00:00
G92 E0 ; zero the extruded length again
2012-07-06 13:52:55 +00:00
; go to the middle of the platform ( disabled , as there is no need to go to the center )
; G1 X { machine_center_x } Y { machine_center_y } F { travel_speed }
G1 F { travel_speed }
2012-04-23 16:02:30 +00:00
""" ,
#######################################################################################
' end.gcode ' : """ ;End GCode
2012-07-06 13:24:58 +00:00
M104 S0 ; extruder heater off
M140 S0 ; heated bed heater off ( if you have it )
2012-07-05 19:36:43 +00:00
2012-07-06 13:52:55 +00:00
G91 ; relative positioning
G1 E - 1 F300 ; retract the filament a bit before lifting the nozzle , to release some of the pressure
G1 Z + 0.5 E - 5 X - 20 Y - 20 F { travel_speed } ; move Z up a bit and retract filament even more
G28 X0 Y0 ; move X / Y to min endstops , so the head is out of the way
2012-07-05 19:36:43 +00:00
2012-07-06 13:24:58 +00:00
M84 ; steppers off
G90 ; absolute positioning
2012-04-23 16:02:30 +00:00
""" ,
#######################################################################################
' support_start.gcode ' : ' ' ,
' support_end.gcode ' : ' ' ,
' cool_start.gcode ' : ' ' ,
' cool_end.gcode ' : ' ' ,
' replace.csv ' : ' ' ,
#######################################################################################
' nextobject.gcode ' : """ ;Move to next object on the platform. clear_z is the minimal z height we need to make sure we do not hit any objects.
G92 E0
2012-07-19 13:30:06 +00:00
G91 ; relative positioning
G1 E - 1 F300 ; retract the filament a bit before lifting the nozzle , to release some of the pressure
G1 Z + 0.5 E - 5 F { travel_speed } ; move Z up a bit and retract filament even more
G90 ; absolute positioning
G1 Z { clear_z } F { max_z_speed }
2012-04-23 16:02:30 +00:00
G92 E0
G1 X { machine_center_x } Y { machine_center_y } F { travel_speed }
2012-10-08 10:26:18 +00:00
G1 F200 E6
2012-04-23 16:02:30 +00:00
G92 E0
""" ,
#######################################################################################
2012-05-07 15:42:46 +00:00
' switchExtruder.gcode ' : """ ;Switch between the current extruder and the next extruder, when printing with multiple extruders.
2012-10-08 13:50:35 +00:00
G92 E0
2012-05-07 15:42:46 +00:00
G1 E - 5 F5000
G92 E0
T { extruder }
G1 E5 F5000
G92 E0
""" ,
2012-04-23 16:02:30 +00:00
}
2012-03-17 11:03:38 +00:00
preferencesDefaultSettings = {
2012-03-29 12:45:14 +00:00
' startMode ' : ' Simple ' ,
2012-10-04 10:33:45 +00:00
' lastFile ' : os . path . normpath ( os . path . join ( os . path . dirname ( os . path . abspath ( __file__ ) ) , ' .. ' , ' example ' , ' UltimakerRobot_support.stl ' ) ) ,
2012-03-17 11:03:38 +00:00
' machine_width ' : ' 205 ' ,
' machine_depth ' : ' 205 ' ,
' machine_height ' : ' 200 ' ,
2012-06-30 09:51:43 +00:00
' machine_type ' : ' unknown ' ,
2012-08-03 09:10:28 +00:00
' has_heated_bed ' : ' False ' ,
2012-04-24 16:46:29 +00:00
' extruder_amount ' : ' 1 ' ,
2012-04-29 09:35:36 +00:00
' extruder_offset_x1 ' : ' -22.0 ' ,
' extruder_offset_y1 ' : ' 0.0 ' ,
' extruder_offset_x2 ' : ' 0.0 ' ,
' extruder_offset_y2 ' : ' 0.0 ' ,
' extruder_offset_x3 ' : ' 0.0 ' ,
' extruder_offset_y3 ' : ' 0.0 ' ,
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 ' ,
2012-09-10 13:49:50 +00:00
' serial_port_auto ' : ' ' ,
2012-06-30 09:51:43 +00:00
' serial_baud ' : ' AUTO ' ,
2012-09-10 13:49:50 +00:00
' serial_baud_auto ' : ' ' ,
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-06-22 04:42:07 +00:00
' sdpath ' : ' ' ,
' sdshortnames ' : ' True ' ,
2012-04-29 09:35:36 +00:00
' extruder_head_size_min_x ' : ' 70.0 ' ,
' extruder_head_size_min_y ' : ' 18.0 ' ,
' extruder_head_size_max_x ' : ' 18.0 ' ,
' extruder_head_size_max_y ' : ' 35.0 ' ,
2012-07-27 11:48:24 +00:00
' extruder_head_size_height ' : ' 80.0 ' ,
2012-07-30 10:26:30 +00:00
' model_colour ' : ' #FFCC99 ' ,
2012-07-31 09:55:21 +00:00
' model_colour2 ' : ' #33FF1A ' ,
' model_colour3 ' : ' #FF331A ' ,
' model_colour4 ' : ' #1A33FF ' ,
2012-03-17 11:03:38 +00:00
}
2012-04-23 16:02:30 +00:00
#########################################################
## Profile and preferences functions
#########################################################
2012-05-03 10:29:55 +00:00
## Profile functions
2012-03-17 11:03:38 +00:00
def getDefaultProfilePath ( ) :
2012-10-22 12:01:19 +00:00
if platform . system ( ) == " Windows " :
basePath = os . path . normpath ( os . path . join ( os . path . dirname ( os . path . abspath ( __file__ ) ) , " .. " ) )
#If we have a frozen python install, we need to step out of the library.zip
if hasattr ( sys , ' frozen ' ) :
basePath = os . path . normpath ( os . path . join ( basePath , " .. " ) )
else :
basePath = os . path . expanduser ( ' ~/.cura/ %s ' % version . getVersion ( False ) )
if not os . path . isdir ( basePath ) :
os . makedirs ( basePath )
return os . path . join ( basePath , ' current_profile.ini ' )
2012-03-17 11:03:38 +00:00
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 )
2012-06-20 07:06:29 +00:00
def resetGlobalProfile ( ) :
#Read a configuration file as global config
global globalProfileParser
globalProfileParser = ConfigParser . ConfigParser ( )
2012-03-17 11:03:38 +00:00
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 ' )
2012-04-23 16:02:30 +00:00
globalProfileParser . add_section ( ' alterations ' )
options = base64 . b64decode ( options )
options = zlib . decompress ( options )
( profileOpts , alt ) = options . split ( ' \f ' , 1 )
for option in profileOpts . split ( ' \b ' ) :
2012-05-15 07:34:45 +00:00
if len ( option ) > 0 :
( key , value ) = option . split ( ' = ' , 1 )
globalProfileParser . set ( ' profile ' , key , value )
2012-04-23 16:02:30 +00:00
for option in alt . split ( ' \b ' ) :
2012-05-15 07:34:45 +00:00
if len ( option ) > 0 :
( key , value ) = option . split ( ' = ' , 1 )
globalProfileParser . set ( ' alterations ' , key , value )
2012-03-30 11:54:49 +00:00
def getGlobalProfileString ( ) :
global globalProfileParser
if not globals ( ) . has_key ( ' globalProfileParser ' ) :
loadGlobalProfile ( getDefaultProfilePath ( ) )
2012-04-23 16:02:30 +00:00
p = [ ]
alt = [ ]
2012-05-03 10:29:55 +00:00
tempDone = [ ]
2012-04-28 16:32:34 +00:00
if globalProfileParser . has_section ( ' profile ' ) :
for key in globalProfileParser . options ( ' profile ' ) :
2012-05-03 10:29:55 +00:00
if key in tempOverride :
2012-08-30 16:03:42 +00:00
p . append ( key + " = " + tempOverride [ key ] )
2012-05-03 10:29:55 +00:00
tempDone . append ( key )
else :
p . append ( key + " = " + globalProfileParser . get ( ' profile ' , key ) )
2012-04-28 16:32:34 +00:00
if globalProfileParser . has_section ( ' alterations ' ) :
for key in globalProfileParser . options ( ' alterations ' ) :
2012-05-03 10:29:55 +00:00
if key in tempOverride :
p . append ( key + " = " + tempOverride [ key ] )
tempDone . append ( key )
else :
alt . append ( key + " = " + globalProfileParser . get ( ' alterations ' , key ) )
for key in tempOverride :
if key not in tempDone :
2012-08-30 16:03:42 +00:00
p . append ( key + " = " + tempOverride [ key ] )
2012-04-23 16:02:30 +00:00
ret = ' \b ' . join ( p ) + ' \f ' + ' \b ' . join ( alt )
ret = base64 . b64encode ( zlib . compress ( ret , 9 ) )
return ret
2012-03-21 17:22:00 +00:00
2012-03-17 11:03:38 +00:00
def getProfileSetting ( name ) :
2012-05-03 10:29:55 +00:00
if name in tempOverride :
2012-08-30 16:03:42 +00:00
return unicode ( tempOverride [ name ] , " utf-8 " )
2012-03-17 11:03:38 +00:00
#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 ) :
2012-04-20 15:06:50 +00:00
if name in profileDefaultSettings :
default = profileDefaultSettings [ name ]
else :
2012-06-21 17:53:18 +00:00
print ( " Missing default setting for: ' " + name + " ' " )
2012-04-20 15:06:50 +00:00
profileDefaultSettings [ name ] = ' '
default = ' '
2012-03-17 11:03:38 +00:00
if not globalProfileParser . has_section ( ' profile ' ) :
globalProfileParser . add_section ( ' profile ' )
globalProfileParser . set ( ' profile ' , name , str ( default ) )
2012-06-21 17:53:18 +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 :
2012-07-24 11:27:30 +00:00
setting = getProfileSetting ( name ) . replace ( ' , ' , ' . ' )
return float ( eval ( setting , { } , { } ) )
2012-07-02 10:25:40 +00:00
except ( ValueError , SyntaxError , TypeError ) :
2012-04-10 14:45:53 +00:00
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 ) )
2012-05-03 10:29:55 +00:00
def isProfileSetting ( name ) :
if name in profileDefaultSettings :
return True
return False
## Preferences functions
2012-03-17 11:03:38 +00:00
global globalPreferenceParser
globalPreferenceParser = None
def getPreferencePath ( ) :
2012-10-22 12:01:19 +00:00
if platform . system ( ) == " Windows " :
basePath = os . path . normpath ( os . path . join ( os . path . dirname ( os . path . abspath ( __file__ ) ) , " .. " ) )
#If we have a frozen python install, we need to step out of the library.zip
if hasattr ( sys , ' frozen ' ) :
basePath = os . path . normpath ( os . path . join ( basePath , " .. " ) )
else :
basePath = os . path . expanduser ( ' ~/.cura/ %s ' % version . getVersion ( False ) )
if not os . path . isdir ( basePath ) :
os . makedirs ( basePath )
return os . path . join ( basePath , ' preferences.ini ' )
2012-03-17 11:03:38 +00:00
2012-05-01 09:39:52 +00:00
def getPreferenceFloat ( name ) :
try :
2012-07-24 11:27:30 +00:00
setting = getPreference ( name ) . replace ( ' , ' , ' . ' )
return float ( eval ( setting , { } , { } ) )
2012-07-02 10:25:40 +00:00
except ( ValueError , SyntaxError , TypeError ) :
2012-05-01 09:39:52 +00:00
return 0.0
2012-07-30 10:26:30 +00:00
def getPreferenceColour ( name ) :
colorString = getPreference ( name )
return [ float ( int ( colorString [ 1 : 3 ] , 16 ) ) / 255 , float ( int ( colorString [ 3 : 5 ] , 16 ) ) / 255 , float ( int ( colorString [ 5 : 7 ] , 16 ) ) / 255 , 1.0 ]
2012-03-17 11:03:38 +00:00
def getPreference ( name ) :
2012-05-03 10:29:55 +00:00
if name in tempOverride :
return unicode ( tempOverride [ name ] )
2012-03-17 11:03:38 +00:00
global globalPreferenceParser
if globalPreferenceParser == None :
globalPreferenceParser = ConfigParser . ConfigParser ( )
globalPreferenceParser . read ( getPreferencePath ( ) )
if not globalPreferenceParser . has_option ( ' preference ' , name ) :
2012-04-20 13:25:48 +00:00
if name in preferencesDefaultSettings :
default = preferencesDefaultSettings [ name ]
else :
2012-06-21 17:53:18 +00:00
print ( " Missing default setting for: ' " + name + " ' " )
2012-04-20 13:25:48 +00:00
preferencesDefaultSettings [ name ] = ' '
default = ' '
2012-03-17 11:03:38 +00:00
if not globalPreferenceParser . has_section ( ' preference ' ) :
globalPreferenceParser . add_section ( ' preference ' )
globalPreferenceParser . set ( ' preference ' , name , str ( default ) )
2012-06-21 17:53:18 +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-05-03 10:29:55 +00:00
def isPreference ( name ) :
if name in preferencesDefaultSettings :
return True
return False
## Temp overrides for multi-extruder slicing and the project planner.
tempOverride = { }
def setTempOverride ( name , value ) :
2012-08-30 16:03:42 +00:00
tempOverride [ name ] = unicode ( value ) . encode ( " utf-8 " )
2012-09-12 15:02:42 +00:00
def clearTempOverride ( name ) :
del tempOverride [ name ]
2012-05-03 10:29:55 +00:00
def resetTempOverride ( ) :
2012-05-04 17:58:00 +00:00
tempOverride . clear ( )
2012-05-03 10:29:55 +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
#########################################################
2012-04-19 13:08:50 +00:00
def replaceTagMatch ( m ) :
2012-07-06 13:24:58 +00:00
pre = m . group ( 1 )
tag = m . group ( 2 )
2012-05-20 09:15:20 +00:00
if tag == ' time ' :
2012-07-06 13:24:58 +00:00
return pre + time . strftime ( ' % H: % M: % S ' )
2012-05-20 09:15:20 +00:00
if tag == ' date ' :
2012-07-06 13:24:58 +00:00
return pre + time . strftime ( ' %d % b % Y ' )
2012-05-20 09:15:20 +00:00
if tag == ' day ' :
2012-07-06 13:24:58 +00:00
return pre + time . strftime ( ' %a ' )
2012-07-05 12:12:13 +00:00
if tag == ' print_time ' :
2012-07-06 13:24:58 +00:00
return pre + ' #P_TIME# '
2012-07-05 12:12:13 +00:00
if tag == ' filament_amount ' :
2012-07-06 13:24:58 +00:00
return pre + ' #F_AMNT# '
2012-07-05 12:12:13 +00:00
if tag == ' filament_weight ' :
2012-07-06 13:24:58 +00:00
return pre + ' #F_WGHT# '
2012-07-05 12:12:13 +00:00
if tag == ' filament_cost ' :
2012-07-06 13:24:58 +00:00
return pre + ' #F_COST# '
if pre == ' F ' and tag in [ ' print_speed ' , ' retraction_speed ' , ' travel_speed ' , ' max_z_speed ' , ' bottom_layer_speed ' , ' cool_min_feedrate ' ] :
2012-05-09 18:16:32 +00:00
f = getProfileSettingFloat ( tag ) * 60
elif isProfileSetting ( tag ) :
f = getProfileSettingFloat ( tag )
elif isPreference ( tag ) :
f = getProfileSettingFloat ( tag )
else :
2012-07-06 13:24:58 +00:00
return ' %s ? %s ? ' % ( pre , tag )
2012-05-09 18:16:32 +00:00
if ( f % 1 ) == 0 :
2012-07-06 13:24:58 +00:00
return pre + str ( int ( f ) )
return pre + str ( f )
2012-04-19 13:08:50 +00:00
2012-07-05 12:12:13 +00:00
def replaceGCodeTags ( filename , gcodeInt ) :
f = open ( filename , ' r+ ' )
data = f . read ( 2048 )
data = data . replace ( ' #P_TIME# ' , ( ' %5d : %02d ' % ( int ( gcodeInt . totalMoveTimeMinute / 60 ) , int ( gcodeInt . totalMoveTimeMinute % 60 ) ) ) [ - 8 : ] )
data = data . replace ( ' #F_AMNT# ' , ( ' %8.2f ' % ( gcodeInt . extrusionAmount / 1000 ) ) [ - 8 : ] )
data = data . replace ( ' #F_WGHT# ' , ( ' %8.2f ' % ( gcodeInt . calculateWeight ( ) * 1000 ) ) [ - 8 : ] )
cost = gcodeInt . calculateCost ( )
if cost == False :
cost = ' Unknown '
data = data . replace ( ' #F_COST# ' , ( ' %8s ' % ( cost . split ( ' ' ) [ 0 ] ) ) [ - 8 : ] )
f . seek ( 0 )
f . write ( data )
f . close ( )
2012-04-23 16:02:30 +00:00
### Get aleration raw contents. (Used internally in Cura)
def getAlterationFile ( filename ) :
#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 ( ' alterations ' , filename ) :
if filename in alterationDefault :
default = alterationDefault [ filename ]
else :
2012-06-21 17:53:18 +00:00
print ( " Missing default alteration for: ' " + filename + " ' " )
2012-04-23 16:02:30 +00:00
alterationDefault [ filename ] = ' '
default = ' '
if not globalProfileParser . has_section ( ' alterations ' ) :
globalProfileParser . add_section ( ' alterations ' )
2012-06-21 17:53:18 +00:00
#print("Using default for: %s" % (filename))
2012-04-23 16:02:30 +00:00
globalProfileParser . set ( ' alterations ' , filename , default )
return unicode ( globalProfileParser . get ( ' alterations ' , filename ) , " utf-8 " )
def setAlterationFile ( filename , 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 ( ' alterations ' ) :
globalProfileParser . add_section ( ' alterations ' )
globalProfileParser . set ( ' alterations ' , filename , value . encode ( " utf-8 " ) )
2012-04-24 03:02:50 +00:00
saveGlobalProfile ( getDefaultProfilePath ( ) )
2012-04-23 16:02:30 +00:00
### Get the alteration file for output. (Used by Skeinforge)
def getAlterationFileContents ( filename ) :
2012-03-27 11:48:40 +00:00
prefix = ' '
2012-07-05 06:46:41 +00:00
postfix = ' '
2012-04-24 09:59:17 +00:00
alterationContents = getAlterationFile ( filename )
2012-04-23 16:02:30 +00:00
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.
2012-05-01 09:39:52 +00:00
eSteps = getPreferenceFloat ( ' steps_per_e ' )
2012-04-23 16:02:30 +00:00
if eSteps > 0 :
prefix + = ' M92 E %f \n ' % ( eSteps )
temp = getProfileSettingFloat ( ' print_temperature ' )
2012-08-03 09:10:28 +00:00
bedTemp = 0
if getPreference ( ' has_heated_bed ' ) == ' True ' :
bedTemp = getProfileSettingFloat ( ' print_bed_temperature ' )
if bedTemp > 0 and not ' {print_bed_temperature} ' in alterationContents :
prefix + = ' M140 S %f \n ' % ( bedTemp )
2012-04-24 09:59:17 +00:00
if temp > 0 and not ' {print_temperature} ' in alterationContents :
2012-04-23 16:02:30 +00:00
prefix + = ' M109 S %f \n ' % ( temp )
2012-08-03 09:10:28 +00:00
if bedTemp > 0 and not ' {print_bed_temperature} ' in alterationContents :
prefix + = ' M190 S %f \n ' % ( bedTemp )
2012-07-05 06:46:41 +00:00
elif filename == ' end.gcode ' :
#Append the profile string to the end of the GCode, so we can load it from the GCode file later.
postfix = ' ;CURA_PROFILE_STRING: %s \n ' % ( getGlobalProfileString ( ) )
2012-04-23 16:02:30 +00:00
elif filename == ' replace.csv ' :
#Always remove the extruder on/off M codes. These are no longer needed in 5D printing.
prefix = ' M101 \n M103 \n '
2012-09-12 15:02:42 +00:00
elif filename == ' support_start.gcode ' or filename == ' support_end.gcode ' :
#Add support start/end code
if getProfileSetting ( ' support_dual_extrusion ' ) == ' True ' and int ( getPreference ( ' extruder_amount ' ) ) > 1 :
if filename == ' support_start.gcode ' :
setTempOverride ( ' extruder ' , ' 1 ' )
else :
setTempOverride ( ' extruder ' , ' 0 ' )
alterationContents = getAlterationFileContents ( ' switchExtruder.gcode ' )
clearTempOverride ( ' extruder ' )
else :
alterationContents = ' '
2012-10-29 08:15:10 +00:00
return unicode ( prefix + re . sub ( " (.) \ { ([^ \ }]*) \ } " , replaceTagMatch , alterationContents ) . rstrip ( ) + ' \n ' + postfix ) . strip ( ) . encode ( ' utf-8 ' )
2012-10-29 09:10:30 +00:00
2012-10-31 10:04:08 +00:00
###### PLUGIN #####
2012-10-29 09:10:30 +00:00
2012-10-31 10:04:08 +00:00
def getPluginConfig ( ) :
try :
return pickle . loads ( getProfileSetting ( ' plugin_config ' ) )
except :
return [ ]
def setPluginConfig ( config ) :
putProfileSetting ( ' plugin_config ' , pickle . dumps ( config ) )
def getPluginBasePaths ( ) :
ret = [ os . path . normpath ( os . path . join ( os . path . dirname ( os . path . abspath ( __file__ ) ) , ' .. ' , ' plugins ' ) ) ]
if platform . system ( ) != " Windows " :
ret . append ( os . path . expanduser ( ' ~/.cura/plugins/ ' ) )
return ret
def getPluginList ( ) :
2012-10-29 09:10:30 +00:00
ret = [ ]
2012-10-31 10:04:08 +00:00
for basePath in getPluginBasePaths ( ) :
for filename in glob . glob ( os . path . join ( basePath , ' *.py ' ) ) :
filename = os . path . basename ( filename )
if filename . startswith ( ' _ ' ) :
continue
with open ( os . path . join ( basePath , filename ) , " r " ) as f :
item = { ' filename ' : filename , ' name ' : None , ' info ' : None , ' type ' : None , ' params ' : [ ] }
for line in f :
line = line . strip ( )
if not line . startswith ( ' # ' ) :
break
line = line [ 1 : ] . split ( ' : ' , 1 )
if len ( line ) != 2 :
continue
if line [ 0 ] . upper ( ) == ' NAME ' :
item [ ' name ' ] = line [ 1 ] . strip ( )
elif line [ 0 ] . upper ( ) == ' INFO ' :
item [ ' info ' ] = line [ 1 ] . strip ( )
elif line [ 0 ] . upper ( ) == ' TYPE ' :
item [ ' type ' ] = line [ 1 ] . strip ( )
2012-11-06 08:11:57 +00:00
elif line [ 0 ] . upper ( ) == ' DEPEND ' :
pass
2012-10-31 10:04:08 +00:00
elif line [ 0 ] . upper ( ) == ' PARAM ' :
m = re . match ( ' ([a-zA-Z]*) \ (([a-zA-Z_]*)(?: \ :([^ \ )]*))? \ ) +(.*) ' , line [ 1 ] . strip ( ) )
if m != None :
item [ ' params ' ] . append ( { ' name ' : m . group ( 1 ) , ' type ' : m . group ( 2 ) , ' default ' : m . group ( 3 ) , ' description ' : m . group ( 4 ) } )
else :
print " Unknown item in effect meta data: %s %s " % ( line [ 0 ] , line [ 1 ] )
if item [ ' name ' ] != None and item [ ' type ' ] == ' postprocess ' :
ret . append ( item )
2012-10-29 09:10:30 +00:00
return ret
2012-10-31 10:04:08 +00:00
def runPostProcessingPlugins ( gcodefilename ) :
pluginConfigList = getPluginConfig ( )
pluginList = getPluginList ( )
2012-10-29 09:10:30 +00:00
2012-10-31 10:04:08 +00:00
for pluginConfig in pluginConfigList :
plugin = None
for pluginTest in pluginList :
if pluginTest [ ' filename ' ] == pluginConfig [ ' filename ' ] :
plugin = pluginTest
if plugin == None :
continue
pythonFile = None
for basePath in getPluginBasePaths ( ) :
testFilename = os . path . join ( basePath , pluginConfig [ ' filename ' ] )
if os . path . isfile ( testFilename ) :
pythonFile = testFilename
if pythonFile == None :
continue
locals = { ' filename ' : gcodefilename }
for param in plugin [ ' params ' ] :
value = param [ ' default ' ]
if param [ ' name ' ] in pluginConfig [ ' params ' ] :
value = pluginConfig [ ' params ' ] [ param [ ' name ' ] ]
if param [ ' type ' ] == ' float ' :
try :
value = float ( value )
except :
2012-11-01 08:07:03 +00:00
value = float ( param [ ' default ' ] )
2012-10-31 10:04:08 +00:00
locals [ param [ ' name ' ] ] = value
2012-11-01 08:07:03 +00:00
try :
execfile ( pythonFile , locals )
except :
locationInfo = traceback . extract_tb ( sys . exc_info ( ) [ 2 ] ) [ - 1 ]
return " %s : ' %s ' @ %s : %s : %d " % ( str ( sys . exc_info ( ) [ 0 ] . __name__ ) , str ( sys . exc_info ( ) [ 1 ] ) , os . path . basename ( locationInfo [ 0 ] ) , locationInfo [ 2 ] , locationInfo [ 1 ] )
return None
2012-11-06 07:52:16 +00:00
def getSDcardDrives ( ) :
drives = [ ' ' ]
if platform . system ( ) == " Windows " :
from ctypes import windll
bitmask = windll . kernel32 . GetLogicalDrives ( )
for letter in string . uppercase :
if bitmask & 1 :
drives . append ( letter + ' :/ ' )
bitmask >> = 1
if platform . system ( ) == " Darwin " :
drives = [ ]
for volume in glob . glob ( ' /Volumes/* ' ) :
if stat . S_ISLNK ( os . lstat ( volume ) . st_mode ) :
continue
drives . append ( volume )
return drives