chiark / gitweb /
Allow math expressions in fields.
authordaid <daid303@gmail.com>
Tue, 10 Apr 2012 14:45:53 +0000 (16:45 +0200)
committerdaid <daid303@gmail.com>
Tue, 10 Apr 2012 14:45:53 +0000 (16:45 +0200)
Cura/cura_sf/fabmetheus_utilities/settings.py
Cura/gui/mainWindow.py
Cura/gui/preview3d.py
Cura/gui/validators.py
Cura/util/profile.py

index e765bff0bf2274e632fb3dc663766a3272c13571..3be5a2927f1983fd45cffe67ab921b0cab42432b 100644 (file)
@@ -4,6 +4,7 @@ Settings is a collection of utilities to display, read & write the settings and
 """
 
 from __future__ import absolute_import
+from __future__ import division
 #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__
 
@@ -18,11 +19,15 @@ def DEFSET(setting):
 
 def storedSetting(name):
        return lambda setting: profile.getProfileSetting(name)
+def storedSettingFloat(name):
+       return lambda setting: profile.getProfileSettingFloat(name)
+def storedSettingInt(name):
+       return lambda setting: int(profile.getProfileSettingFloat(name))
 def storedPreference(name):
        return lambda setting: profile.getPreference(name)
 
 def ifSettingAboveZero(name):
-       return lambda setting: float(profile.getProfileSetting(name)) > 0
+       return lambda setting: profile.getProfileSettingFloat(name) > 0
 
 def ifSettingIs(name, value):
        return lambda setting: profile.getProfileSetting(name) == value
@@ -33,7 +38,7 @@ def raftLayerCount(setting):
        return '0'
 
 def storedPercentSetting(name):
-       return lambda setting: float(profile.getProfileSetting(name)) / 100
+       return lambda setting: profile.getProfileSettingFloat(name) / 100
 
 def calculateEdgeWidth(setting):
        return profile.calculateEdgeWidth()
@@ -43,7 +48,7 @@ def calculateShells(setting):
 
 def calculateShellsBase(setting):
        edgeWidth = profile.calculateEdgeWidth()
-       extraWall = float(profile.getProfileSetting('extra_base_wall_thickness'))
+       extraWall = profile.getProfileSettingFloat('extra_base_wall_thickness')
        
        return profile.calculateLineCount() - 1 + int(extraWall / edgeWidth + 0.0001)
 
@@ -51,13 +56,13 @@ def calculateSolidLayerCount(setting):
        return profile.calculateSolidLayerCount()
 
 def firstLayerSpeedRatio(setting):
-       bottomSpeed = float(profile.getProfileSetting('bottom_layer_speed'))
-       speed = float(profile.getProfileSetting('print_speed'))
+       bottomSpeed = profile.getProfileSettingFloat('bottom_layer_speed')
+       speed = profile.getProfileSettingFloat('print_speed')
        return bottomSpeed/speed
 
 def calcSupportDistanceRatio(setting):
        edgeWidth = calculateEdgeWidth(setting)
-       distance = float(profile.getProfileSetting('support_distance'))
+       distance = profile.getProfileSettingFloat('support_distance')
        return distance / edgeWidth
 
 def calculateMultiplyDistance(setting):
@@ -65,22 +70,22 @@ def calculateMultiplyDistance(setting):
        return 10.0 / edgeWidth
 
 def calcBottomLayerFlowRateRatio(setting):
-       bottomThickness = float(profile.getProfileSetting('bottom_thickness'))
-       layerThickness = float(profile.getProfileSetting('layer_height'))
+       bottomThickness = profile.getProfileSettingFloat('bottom_thickness')
+       layerThickness = profile.getProfileSettingFloat('layer_height')
        if bottomThickness < layerThickness:
                return 1.0
        return bottomThickness / layerThickness
 
 def calcExtraBottomThickness(setting):
-       bottomThickness = float(profile.getProfileSetting('bottom_thickness'))
-       layerThickness = float(profile.getProfileSetting('layer_height'))
+       bottomThickness = profile.getProfileSettingFloat('bottom_thickness')
+       layerThickness = profile.getProfileSettingFloat('layer_height')
        if bottomThickness < layerThickness:
                return 0.0
        return bottomThickness - layerThickness
 
 def calcLayerSkip(setting):
-       bottomThickness = float(profile.getProfileSetting('bottom_thickness'))
-       layerThickness = float(profile.getProfileSetting('layer_height'))
+       bottomThickness = profile.getProfileSettingFloat('bottom_thickness')
+       layerThickness = profile.getProfileSettingFloat('layer_height')
        if bottomThickness < layerThickness:
                return 0
        return int(math.ceil((bottomThickness - layerThickness) / layerThickness + 0.0001) - 1)
@@ -92,7 +97,7 @@ def getProfileInformation():
                        'Edge_Width_mm': calculateEdgeWidth,
                        'Extra_Decimal_Places_float': DEFSET,
                        'Import_Coarseness_ratio': DEFSET,
-                       'Layer_Height_mm': storedSetting("layer_height"),
+                       'Layer_Height_mm': storedSettingFloat("layer_height"),
                        'Layers_From_index': calcLayerSkip,
                        'Layers_To_index': DEFSET,
                        'Correct_Mesh': DEFSET,
@@ -103,8 +108,8 @@ def getProfileInformation():
                        'FlipZ': storedSetting("flip_z"),
                        'SwapXZ': storedSetting("swap_xz"),
                        'SwapYZ': storedSetting("swap_yz"),
-                       'Scale': storedSetting("model_scale"),
-                       'Rotate': storedSetting("model_rotate_base"),
+                       'Scale': storedSettingFloat("model_scale"),
+                       'Rotate': storedSettingFloat("model_rotate_base"),
                },'scale': {
                        'Activate_Scale': "False",
                        'XY_Plane_Scale_ratio': DEFSET,
@@ -128,7 +133,7 @@ def getProfileInformation():
                },'inset': {
                        'Add_Custom_Code_for_Temperature_Reading': "False",
                        'Infill_in_Direction_of_Bridge': "True",
-                       'Infill_Width': storedSetting("nozzle_size"),
+                       'Infill_Width': storedSettingFloat("nozzle_size"),
                        'Loop_Order_Choice': DEFSET,
                        'Overlap_Removal_Width_over_Perimeter_Width_ratio': DEFSET,
                        'Turn_Extruder_Heater_Off_at_Shut_Down': DEFSET,
@@ -156,7 +161,7 @@ def getProfileInformation():
                        'Line': ifSettingIs('infill_type', 'Line'),
                        'Infill_Perimeter_Overlap_ratio': storedPercentSetting('fill_overlap'),
                        'Infill_Solidity_ratio': storedPercentSetting('fill_density'),
-                       'Infill_Width': storedSetting("nozzle_size"),
+                       'Infill_Width': storedSettingFloat("nozzle_size"),
                        'Sharpest_Angle_degrees': DEFSET,
                        'Solid_Surface_Thickness_layers': calculateSolidLayerCount,
                        'Start_From_Choice': DEFSET,
@@ -164,8 +169,8 @@ def getProfileInformation():
                        'Thread_Sequence_Choice': storedSetting('sequence'),
                },'multiply': {
                        'Activate_Multiply': "True",
-                       'Center_X_mm': storedSetting("machine_center_x"),
-                       'Center_Y_mm': storedSetting("machine_center_y"),
+                       'Center_X_mm': storedSettingFloat("machine_center_x"),
+                       'Center_Y_mm': storedSettingFloat("machine_center_y"),
                        'Number_of_Columns_integer': storedSetting('model_multiply_x'),
                        'Number_of_Rows_integer': storedSetting('model_multiply_y'),
                        'Reverse_Sequence_every_Odd_Layer': DEFSET,
@@ -177,8 +182,8 @@ def getProfileInformation():
                        'Bridge_Flow_Rate_Multiplier_ratio': storedPercentSetting('bridge_material_amount'),
                        'Duty_Cyle_at_Beginning_portion': DEFSET,
                        'Duty_Cyle_at_Ending_portion': DEFSET,
-                       'Feed_Rate_mm/s': storedSetting("print_speed"),
-                       'Flow_Rate_Setting_float': storedSetting("print_speed"),
+                       'Feed_Rate_mm/s': storedSettingFloat("print_speed"),
+                       'Flow_Rate_Setting_float': storedSettingFloat("print_speed"),
                        'Object_First_Layer_Feed_Rate_Infill_Multiplier_ratio': firstLayerSpeedRatio,
                        'Object_First_Layer_Feed_Rate_Perimeter_Multiplier_ratio': firstLayerSpeedRatio,
                        'Object_First_Layer_Feed_Rate_Travel_Multiplier_ratio': firstLayerSpeedRatio,
@@ -189,19 +194,19 @@ def getProfileInformation():
                        'Maximum_Z_Feed_Rate_mm/s': DEFSET,
                        'Perimeter_Feed_Rate_Multiplier_ratio': DEFSET,
                        'Perimeter_Flow_Rate_Multiplier_ratio': DEFSET,
-                       'Travel_Feed_Rate_mm/s': storedSetting("travel_speed"),
+                       'Travel_Feed_Rate_mm/s': storedSettingFloat("travel_speed"),
                        'Bottom_layer_flow_rate_ratio': calcBottomLayerFlowRateRatio,
                },'temperature': {
                        'Activate_Temperature': DEFSET,#ifSettingAboveZero('print_temperature'),
                        'Cooling_Rate_Celcius/second': DEFSET,
                        'Heating_Rate_Celcius/second': DEFSET,
-                       'Base_Temperature_Celcius': DEFSET,#storedSetting("print_temperature"),
-                       'Interface_Temperature_Celcius': DEFSET,#storedSetting("print_temperature"),
-                       'Object_First_Layer_Infill_Temperature_Celcius': DEFSET,#storedSetting("print_temperature"),
-                       'Object_First_Layer_Perimeter_Temperature_Celcius': DEFSET,#storedSetting("print_temperature"),
-                       'Object_Next_Layers_Temperature_Celcius': DEFSET,#storedSetting("print_temperature"),
-                       'Support_Layers_Temperature_Celcius': DEFSET,#storedSetting("print_temperature"),
-                       'Supported_Layers_Temperature_Celcius': DEFSET,#storedSetting("print_temperature"),
+                       'Base_Temperature_Celcius': DEFSET,#storedSettingFloat("print_temperature"),
+                       'Interface_Temperature_Celcius': DEFSET,#storedSettingFloat("print_temperature"),
+                       'Object_First_Layer_Infill_Temperature_Celcius': DEFSET,#storedSettingFloat("print_temperature"),
+                       'Object_First_Layer_Perimeter_Temperature_Celcius': DEFSET,#storedSettingFloat("print_temperature"),
+                       'Object_Next_Layers_Temperature_Celcius': DEFSET,#storedSettingFloat("print_temperature"),
+                       'Support_Layers_Temperature_Celcius': DEFSET,#storedSettingFloat("print_temperature"),
+                       'Supported_Layers_Temperature_Celcius': DEFSET,#storedSettingFloat("print_temperature"),
                },'raft': {
                        'Activate_Raft': "True",
                        'Add_Raft,_Elevate_Nozzle,_Orbit': DEFSET,
@@ -223,7 +228,7 @@ def getProfileInformation():
                        'Name_of_Support_Start_File': DEFSET,
                        'Operating_Nozzle_Lift_over_Layer_Thickness_ratio': DEFSET,
                        'Raft_Additional_Margin_over_Length_%': DEFSET,
-                       'Raft_Margin_mm': storedSetting('raft_margin'),
+                       'Raft_Margin_mm': storedSettingFloat('raft_margin'),
                        'Support_Cross_Hatch': 'False',
                        'Support_Flow_Rate_over_Operating_Flow_Rate_ratio': storedPercentSetting('support_rate'),
                        'Support_Gap_over_Perimeter_Extrusion_Width_ratio': calcSupportDistanceRatio,
@@ -284,16 +289,16 @@ def getProfileInformation():
                        'Bridge_Cool_Celcius': DEFSET,
                        'Cool_Type': DEFSET,
                        'Maximum_Cool_Celcius': DEFSET,
-                       'Minimum_Layer_Time_seconds': storedSetting("cool_min_layer_time"),
+                       'Minimum_Layer_Time_seconds': storedSettingFloat("cool_min_layer_time"),
                        'Minimum_Orbital_Radius_millimeters': DEFSET,
                        'Name_of_Cool_End_File': DEFSET,
                        'Name_of_Cool_Start_File': DEFSET,
                        'Orbital_Outset_millimeters': DEFSET,
                        'Turn_Fan_On_at_Beginning': storedSetting("fan_enabled"),
                        'Turn_Fan_Off_at_Ending': "False",
-                       'Minimum_feed_rate_mm/s': storedSetting("cool_min_feedrate"),
-                       'Fan_on_at_layer': storedSetting('fan_layer'),
-                       'Fan_speed_%': storedSetting('fan_speed'),
+                       'Minimum_feed_rate_mm/s': storedSettingFloat("cool_min_feedrate"),
+                       'Fan_on_at_layer': storedSettingInt('fan_layer'),
+                       'Fan_speed_%': storedSettingInt('fan_speed'),
                },'hop': {
                        'Activate_Hop': "False",
                        'Hop_Over_Layer_Thickness_ratio': DEFSET,
@@ -362,14 +367,14 @@ def getProfileInformation():
                        'Activate_Dimension': "True",
                        'Absolute_Extrusion_Distance': "True",
                        'Relative_Extrusion_Distance': "False",
-                       'Extruder_Retraction_Speed_mm/s': storedSetting('retraction_speed'),
-                       'Filament_Diameter_mm': storedSetting("filament_diameter"),
-                       'Filament_Packing_Density_ratio': storedSetting("filament_density"),
+                       'Extruder_Retraction_Speed_mm/s': storedSettingFloat('retraction_speed'),
+                       'Filament_Diameter_mm': storedSettingFloat("filament_diameter"),
+                       'Filament_Packing_Density_ratio': storedSettingFloat("filament_density"),
                        'Maximum_E_Value_before_Reset_float': DEFSET,
-                       'Minimum_Travel_for_Retraction_millimeters': storedSetting("retraction_min_travel"),
+                       'Minimum_Travel_for_Retraction_millimeters': storedSettingFloat("retraction_min_travel"),
                        'Retract_Within_Island': DEFSET,
-                       'Retraction_Distance_millimeters': storedSetting('retraction_amount'),
-                       'Restart_Extra_Distance_millimeters': storedSetting('retraction_extra'),
+                       'Retraction_Distance_millimeters': storedSettingFloat('retraction_amount'),
+                       'Restart_Extra_Distance_millimeters': storedSettingFloat('retraction_extra'),
                },'alteration': {
                        'Activate_Alteration': "True",
                        'Name_of_End_File': "end.gcode",
index 8275272eeca3f4eb04def66bc176da587890e034..d81ef925ed1f04c80432b99d4fc786c1d5dec598 100644 (file)
@@ -95,10 +95,10 @@ class mainWindow(configBase.configWindowBase):
                
                configBase.TitleRow(left, "Accuracy")
                c = configBase.SettingRow(left, "Layer height (mm)", 'layer_height', '0.2', 'Layer height in millimeters.\n0.2 is a good value for quick prints.\n0.1 gives high quality prints.')
-               validators.validFloat(c, 0.0)
+               validators.validFloat(c, 0.0001)
                validators.warningAbove(c, lambda : (float(profile.getProfileSetting('nozzle_size')) * 80.0 / 100.0), "Thicker layers then %.2fmm (80%% nozzle size) usually give bad results and are not recommended.")
                c = configBase.SettingRow(left, "Wall thickness (mm)", 'wall_thickness', '0.8', 'Thickness of the walls.\nThis is used in combination with the nozzle size to define the number\nof perimeter lines and the thickness of those perimeter lines.')
-               validators.validFloat(c, 0.0)
+               validators.validFloat(c, 0.0001)
                validators.wallThicknessValidator(c)
                
                configBase.TitleRow(left, "Fill")
index b081f86dbf8bc14a7cd30bc4ae4f438037814306..92ce6db9fb7cd4e83bbc95a2ec180c4fdca7aae2 100644 (file)
@@ -1,3 +1,5 @@
+from __future__ import division\r
+\r
 import sys\r
 import math\r
 import threading\r
@@ -163,11 +165,7 @@ class previewPanel(wx.Panel):
                self.updateModelTransform()\r
 \r
        def OnScale(self, e):\r
-               try:\r
-                       scale = float(self.scale.GetValue())\r
-               except:\r
-                       scale = 1.0\r
-               profile.putProfileSetting('model_scale', str(scale))\r
+               profile.putProfileSetting('model_scale', self.scale.GetValue())\r
                self.updateModelTransform()\r
        \r
        def OnRotate(self, e):\r
@@ -292,8 +290,8 @@ class previewPanel(wx.Panel):
                scale = 1.0\r
                rotate = 0.0\r
                try:\r
-                       scale = float(profile.getProfileSetting('model_scale'))\r
-                       rotate = float(profile.getProfileSetting('model_rotate_base')) / 180 * math.pi\r
+                       scale = profile.getProfileSettingFloat('model_scale')\r
+                       rotate = profile.getProfileSettingFloat('model_rotate_base') / 180.0 * math.pi\r
                except:\r
                        pass\r
                scaleX = scale\r
index d7db5c828385e3dcaa749022d1e7a5e7c11d9bca..b1da7783a363945f8383ecb8c0bb5bd6e9ebdb5a 100644 (file)
@@ -1,4 +1,5 @@
 from __future__ import absolute_import
+from __future__ import division
 import __init__
 
 import types
@@ -19,14 +20,14 @@ class validFloat():
        
        def validate(self):
                try:
-                       f = float(self.setting.GetValue())
+                       f = float(eval(self.setting.GetValue(), {}, {}))
                        if self.minValue != None and f < self.minValue:
                                return ERROR, 'This setting should not be below ' + str(self.minValue)
                        if self.maxValue != None and f > self.maxValue:
                                return ERROR, 'This setting should not be above ' + str(self.maxValue)
                        return SUCCESS, ''
-               except ValueError:
-                       return ERROR, '"' + str(self.setting.GetValue()) + '" is not a valid number'
+               except (ValueError, SyntaxError):
+                       return ERROR, '"' + str(self.setting.GetValue()) + '" is not a valid number or expression'
 
 class validInt():
        def __init__(self, setting, minValue = None, maxValue = None):
@@ -37,14 +38,14 @@ class validInt():
        
        def validate(self):
                try:
-                       f = int(self.setting.GetValue())
+                       f = int(eval(self.setting.GetValue(), {}, {}))
                        if self.minValue != None and f < self.minValue:
                                return ERROR, 'This setting should not be below ' + str(self.minValue)
                        if self.maxValue != None and f > self.maxValue:
                                return ERROR, 'This setting should not be above ' + str(self.maxValue)
                        return SUCCESS, ''
-               except ValueError:
-                       return ERROR, '"' + str(self.setting.GetValue()) + '" is not a valid whole number'
+               except (ValueError, SyntaxError):
+                       return ERROR, '"' + str(self.setting.GetValue()) + '" is not a valid whole number or expression'
 
 class warningAbove():
        def __init__(self, setting, minValueForWarning, warningMessage):
@@ -55,7 +56,7 @@ class warningAbove():
        
        def validate(self):
                try:
-                       f = float(self.setting.GetValue())
+                       f = float(eval(self.setting.GetValue(), {}, {}))
                        if isinstance(self.minValueForWarning, types.FunctionType):
                                if f >= self.minValueForWarning():
                                        return WARNING, self.warningMessage % (self.minValueForWarning())
@@ -63,7 +64,7 @@ class warningAbove():
                                if f >= self.minValueForWarning:
                                        return WARNING, self.warningMessage
                        return SUCCESS, ''
-               except ValueError:
+               except (ValueError, SyntaxError):
                        #We already have an error by the int/float validator in this case.
                        return SUCCESS, ''
 
@@ -74,8 +75,8 @@ class wallThicknessValidator():
        
        def validate(self):
                try:
-                       wallThickness = float(self.setting.GetValue())
-                       nozzleSize = float(profile.getProfileSetting('nozzle_size'))
+                       wallThickness = profile.getProfileSettingFloat('wall_thickness')
+                       nozzleSize = profile.getProfileSettingFloat('nozzle_size')
                        if wallThickness <= nozzleSize * 0.5:
                                return ERROR, 'Trying to print walls thinner then the half of your nozzle size, this will not produce anything usable'
                        if wallThickness <= nozzleSize * 0.85:
@@ -100,9 +101,9 @@ class printSpeedValidator():
 
        def validate(self):
                try:
-                       nozzleSize = float(profile.getProfileSetting('nozzle_size'))
-                       layerHeight = float(profile.getProfileSetting('layer_height'))
-                       printSpeed = float(profile.getProfileSetting('print_speed'))
+                       nozzleSize = profile.getProfileSettingFloat('nozzle_size')
+                       layerHeight = profile.getProfileSettingFloat('layer_height')
+                       printSpeed = profile.getProfileSettingFloat('print_speed')
                        
                        printVolumePerMM = layerHeight * nozzleSize
                        printVolumePerSecond = printVolumePerMM * printSpeed
index 34df82db335990703e3148bd0e5d0338be26b591..bfa84f975a48040a6e54b59f983a76e32543c192 100644 (file)
@@ -1,4 +1,5 @@
 from __future__ import absolute_import\r
+from __future__ import division\r
 #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
 import __init__\r
 \r
@@ -126,10 +127,16 @@ def getProfileSetting(name):
                if not globalProfileParser.has_section('profile'):\r
                        globalProfileParser.add_section('profile')\r
                globalProfileParser.set('profile', name, str(default))\r
-               print name + " not found in profile, so using default: " + str(default)\r
+               #print name + " not found in profile, so using default: " + str(default)\r
                return default\r
        return globalProfileParser.get('profile', name)\r
 \r
+def getProfileSettingFloat(name):\r
+       try:\r
+               return float(eval(getProfileSetting(name), {}, {}))\r
+       except (ValueError, SyntaxError):\r
+               return 0.0\r
+\r
 def putProfileSetting(name, value):\r
        #Check if we have a configuration file loaded, else load the default.\r
        if not globals().has_key('globalProfileParser'):\r
@@ -160,7 +167,7 @@ def getPreference(name):
                if not globalPreferenceParser.has_section('preference'):\r
                        globalPreferenceParser.add_section('preference')\r
                globalPreferenceParser.set('preference', name, str(default))\r
-               print name + " not found in preferences, so using default: " + str(default)\r
+               #print name + " not found in preferences, so using default: " + str(default)\r
                return default\r
        return unicode(globalPreferenceParser.get('preference', name), "utf-8")\r
 \r
@@ -179,8 +186,8 @@ def putPreference(name, value):
 ## Utility functions to calculate common profile values\r
 #########################################################\r
 def calculateEdgeWidth():\r
-       wallThickness = float(getProfileSetting('wall_thickness'))\r
-       nozzleSize = float(getProfileSetting('nozzle_size'))\r
+       wallThickness = getProfileSettingFloat('wall_thickness')\r
+       nozzleSize = getProfileSettingFloat('nozzle_size')\r
        \r
        if wallThickness < nozzleSize:\r
                return wallThickness\r
@@ -193,8 +200,8 @@ def calculateEdgeWidth():
        return lineWidth\r
 \r
 def calculateLineCount():\r
-       wallThickness = float(getProfileSetting('wall_thickness'))\r
-       nozzleSize = float(getProfileSetting('nozzle_size'))\r
+       wallThickness = getProfileSettingFloat('wall_thickness')\r
+       nozzleSize = getProfileSettingFloat('nozzle_size')\r
        \r
        if wallThickness < nozzleSize:\r
                return 1\r
@@ -207,8 +214,8 @@ def calculateLineCount():
        return lineCount\r
 \r
 def calculateSolidLayerCount():\r
-       layerHeight = float(getProfileSetting('layer_height'))\r
-       solidThickness = float(getProfileSetting('solid_layer_thickness'))\r
+       layerHeight = getProfileSettingFloat('layer_height')\r
+       solidThickness = getProfileSettingFloat('solid_layer_thickness')\r
        return int(math.ceil(solidThickness / layerHeight - 0.0001))\r
 \r
 #########################################################\r
@@ -230,7 +237,7 @@ def getAlterationFileContents(filename, allowMagicPrefix = True):
                        eSteps = float(getPreference('steps_per_e'))\r
                        if eSteps > 0:\r
                                prefix += 'M92 E'+str(eSteps)+'\n'\r
-                       temp = float(getProfileSetting('print_temperature'))\r
+                       temp = getProfileSettingFloat('print_temperature')\r
                        if temp > 0:\r
                                prefix += 'M109 S'+str(temp)+'\n'\r
                elif filename == 'replace.csv':\r