From 3ca5462fb8877710059aa4bb26dbd87c706de903 Mon Sep 17 00:00:00 2001 From: daid Date: Tue, 20 Mar 2012 18:00:53 +0100 Subject: [PATCH 01/21] Change the way intersection warnings are reported, so we actually know where they happened and so we can show them. --- .../geometry/solids/triangle_mesh.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/solids/triangle_mesh.py b/SkeinPyPy/fabmetheus_utilities/geometry/solids/triangle_mesh.py index a738c41f..9bf210d4 100644 --- a/SkeinPyPy/fabmetheus_utilities/geometry/solids/triangle_mesh.py +++ b/SkeinPyPy/fabmetheus_utilities/geometry/solids/triangle_mesh.py @@ -441,11 +441,18 @@ def getLoopsFromCorrectMesh( edges, faces, vertexes, z ): loops = [] while isPathAdded( edges, faces, loops, remainingEdgeTable, vertexes, z ): pass - if euclidean.isLoopListIntersecting(loops): - print('Warning, the triangle mesh slice intersects itself in getLoopsFromCorrectMesh in triangle_mesh.') - print('Something will still be printed, but there is no guarantee that it will be the correct shape.') - print('Once the gcode is saved, you should check over the layer with a z of:') - print(z) + + warning = False + for idx in xrange(0, len(loops)-1): + loop = loops[idx] + p0 = loop[-1] + for p1 in loop: + if euclidean.isLineIntersectingLoops(loops[idx+1:], p0, p1): + print('Warning, the triangle mesh slice intersects itself in getLoopsFromCorrectMesh in triangle_mesh.') + print('Model error(intersect): (%f, %f, %f) (%f, %f, %f)' % (p0.real, p0.imag, z, p1.real, p1.imag, z)) + warning = True + p0 = p1 + if warning: return [] return loops # untouchables = [] From 0b06341fc0a84705b35f0cea6683943bd5feb297 Mon Sep 17 00:00:00 2001 From: daid Date: Wed, 21 Mar 2012 10:42:17 +0100 Subject: [PATCH 02/21] Do not emit a warning on M109. #26 --- SkeinPyPy/newui/gcodeInterpreter.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/SkeinPyPy/newui/gcodeInterpreter.py b/SkeinPyPy/newui/gcodeInterpreter.py index 0dfd669b..e94abd39 100644 --- a/SkeinPyPy/newui/gcodeInterpreter.py +++ b/SkeinPyPy/newui/gcodeInterpreter.py @@ -142,6 +142,8 @@ class gcode(): pass elif M == 108: #Extruder RPM (these should not be in the final GCode, but they are) pass + elif M == 109: #Set temperature, wait + pass elif M == 113: #Extruder PWM (these should not be in the final GCode, but they are) pass else: From 71a9f24c8fe41fb7be60d3fb96e2705147de10fe Mon Sep 17 00:00:00 2001 From: daid Date: Wed, 21 Mar 2012 10:46:04 +0100 Subject: [PATCH 03/21] Use the proper even for comboboxes #25. --- SkeinPyPy/newui/configBase.py | 2 +- SkeinPyPy/newui/preview3d.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/SkeinPyPy/newui/configBase.py b/SkeinPyPy/newui/configBase.py index efc747f7..265a687d 100644 --- a/SkeinPyPy/newui/configBase.py +++ b/SkeinPyPy/newui/configBase.py @@ -123,7 +123,7 @@ class SettingRow(): self.ctrl.Bind(wx.EVT_CHECKBOX, self.OnSettingChange) else: self.ctrl = wx.ComboBox(panel, -1, getSettingFunc(configName), choices=defaultValue, style=wx.CB_DROPDOWN|wx.CB_READONLY) - self.ctrl.Bind(wx.EVT_TEXT, self.OnSettingChange) + self.ctrl.Bind(wx.EVT_COMBOBOX, self.OnSettingChange) sizer.Add(self.label, (x,y), flag=wx.ALIGN_CENTER_VERTICAL) sizer.Add(self.ctrl, (x,y+1), flag=wx.ALIGN_BOTTOM|wx.EXPAND) diff --git a/SkeinPyPy/newui/preview3d.py b/SkeinPyPy/newui/preview3d.py index 483af4dd..4a54db6e 100644 --- a/SkeinPyPy/newui/preview3d.py +++ b/SkeinPyPy/newui/preview3d.py @@ -50,7 +50,7 @@ class previewPanel(wx.Panel): self.viewSelect = wx.ComboBox(self.toolbar, -1, 'Model - Normal', choices=['Model - Normal', 'Model - Transparent', 'Model - X-Ray', 'GCode', 'Mixed'], style=wx.CB_DROPDOWN|wx.CB_READONLY) self.toolbar.AddControl(self.viewSelect) - self.viewSelect.Bind(wx.EVT_TEXT, self.OnViewChange) + self.viewSelect.Bind(wx.EVT_COMBOBOX, self.OnViewChange) self.glCanvas.viewMode = self.viewSelect.GetValue() self.layerSpin = wx.SpinCtrl(self.toolbar, -1, '', size=(21*4,21), style=wx.SP_ARROW_KEYS) From 4e5fc1402545a011b9d02273b260f8754dc5964f Mon Sep 17 00:00:00 2001 From: daid Date: Wed, 21 Mar 2012 12:18:57 +0100 Subject: [PATCH 04/21] Show error lines in transparent and x-ray view --- SkeinPyPy/newui/preview3d.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/SkeinPyPy/newui/preview3d.py b/SkeinPyPy/newui/preview3d.py index 4a54db6e..25250bb5 100644 --- a/SkeinPyPy/newui/preview3d.py +++ b/SkeinPyPy/newui/preview3d.py @@ -499,9 +499,11 @@ class PreviewGLCanvas(glcanvas.GLCanvas): elif self.viewMode == "Model - Normal": glEnable(GL_LIGHTING) glCallList(self.modelDisplayList) - + + if self.viewMode == "Model - Normal" or self.viewMode == "Model - Transparent" or self.viewMode == "Model - X-Ray": glDisable(GL_LIGHTING) glDisable(GL_DEPTH_TEST) + glDisable(GL_BLEND) glColor3f(1,0,0) glTranslate(self.parent.machineCenter.x, self.parent.machineCenter.y, 0) glBegin(GL_LINES) From 9db415edfc59b6725774ed6f2a8de48a984a0b60 Mon Sep 17 00:00:00 2001 From: daid Date: Wed, 21 Mar 2012 13:20:30 +0100 Subject: [PATCH 05/21] Fix the multiply seperation to be always 10mm --- SkeinPyPy/fabmetheus_utilities/settings.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/SkeinPyPy/fabmetheus_utilities/settings.py b/SkeinPyPy/fabmetheus_utilities/settings.py index ce6f48be..d211deb7 100644 --- a/SkeinPyPy/fabmetheus_utilities/settings.py +++ b/SkeinPyPy/fabmetheus_utilities/settings.py @@ -84,6 +84,10 @@ def calcSupportDistanceRatio(setting): distance = float(profile.getProfileSetting('support_distance')) return distance / edgeWidth +def calculateMultiplyDistance(setting): + edgeWidth = calculateEdgeWidth(setting) + return 10.0 / edgeWidth + def getSkeinPyPyProfileInformation(): return { 'carve': { @@ -164,7 +168,7 @@ def getSkeinPyPyProfileInformation(): 'Number_of_Columns_integer': storedSetting('model_multiply_x'), 'Number_of_Rows_integer': storedSetting('model_multiply_y'), 'Reverse_Sequence_every_Odd_Layer': DEFSET, - 'Separation_over_Perimeter_Width_ratio': DEFSET, + 'Separation_over_Perimeter_Width_ratio': calculateMultiplyDistance, },'speed': { 'Activate_Speed': "True", 'Add_Flow_Rate': "True", From dd9d5f75cb68a96088a4d592c1c199cae900f567 Mon Sep 17 00:00:00 2001 From: daid Date: Wed, 21 Mar 2012 13:33:01 +0100 Subject: [PATCH 06/21] Remove conditional import for pypy, instead import depending on arguements. --- SkeinPyPy/skeinpypy.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/SkeinPyPy/skeinpypy.py b/SkeinPyPy/skeinpypy.py index 5a706ac4..6aa5ef3d 100644 --- a/SkeinPyPy/skeinpypy.py +++ b/SkeinPyPy/skeinpypy.py @@ -18,9 +18,6 @@ import platform from optparse import OptionParser from newui import skeinRun -if platform.python_implementation() != "PyPy": - from newui import mainWindow - __author__ = 'Daid' __credits__ = """ Enrique Perez (perez_enrique@yahoo.com) @@ -52,6 +49,7 @@ def main(): if len( args ) > 0: skeinRun.runSkein(args) else: + from newui import mainWindow mainWindow.main() if __name__ == '__main__': From 7e8c8ece12e3ec216541c26a5abb427ea0abba05 Mon Sep 17 00:00:00 2001 From: daid Date: Wed, 21 Mar 2012 13:59:21 +0100 Subject: [PATCH 07/21] Do not need stepsPerE from gcode interperter --- SkeinPyPy/newui/gcodeInterpreter.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/SkeinPyPy/newui/gcodeInterpreter.py b/SkeinPyPy/newui/gcodeInterpreter.py index e94abd39..64d46675 100644 --- a/SkeinPyPy/newui/gcodeInterpreter.py +++ b/SkeinPyPy/newui/gcodeInterpreter.py @@ -23,7 +23,6 @@ class gcode(): pathType = 'CUSTOM'; layerNr = 0; #Note layer 0 will be the start code. startCodeDone = False - self.stepsPerE = 865.888 currentPath = {'type': 'move', 'pathType': pathType, 'list': [pos.copy()], 'layerNr': layerNr} currentPath['list'][-1].e = totalExtrusion for line in f: @@ -128,9 +127,6 @@ class gcode(): elif M == 84: #Disable step drivers pass elif M == 92: #Set steps per unit - e = self.getCodeFloat(line, 'E') - if e is not None: - self.stepsPerE = e pass elif M == 104: #Set temperature, no wait pass From d923d8d3b80acc739d09eb9b20a1f177f7cd662a Mon Sep 17 00:00:00 2001 From: daid Date: Wed, 21 Mar 2012 15:53:29 +0100 Subject: [PATCH 08/21] Move flip option to 3D preview window --- SkeinPyPy/newui/mainWindow.py | 7 ------- SkeinPyPy/newui/preview3d.py | 30 ++++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/SkeinPyPy/newui/mainWindow.py b/SkeinPyPy/newui/mainWindow.py index 84f025af..d8688c4a 100644 --- a/SkeinPyPy/newui/mainWindow.py +++ b/SkeinPyPy/newui/mainWindow.py @@ -163,13 +163,6 @@ class mainWindow(configBase.configWindowBase): c = configBase.SettingRow(left, "Scale", 'model_scale', '1.0', '') validators.validFloat(c, 0.01) configBase.settingNotify(c, self.preview3d.updateModelTransform) - configBase.TitleRow(left, "Flip") - c = configBase.SettingRow(left, "Flip X", 'flip_x', False, '') - configBase.settingNotify(c, self.preview3d.updateModelTransform) - c = configBase.SettingRow(left, "Flip Y", 'flip_y', False, '') - configBase.settingNotify(c, self.preview3d.updateModelTransform) - c = configBase.SettingRow(left, "Flip Z", 'flip_z', False, '') - configBase.settingNotify(c, self.preview3d.updateModelTransform) configBase.TitleRow(right, "Rotate") c = configBase.SettingRow(right, "Rotate (deg)", 'model_rotate_base', '0', '') validators.validFloat(c) diff --git a/SkeinPyPy/newui/preview3d.py b/SkeinPyPy/newui/preview3d.py index 25250bb5..4941a8f6 100644 --- a/SkeinPyPy/newui/preview3d.py +++ b/SkeinPyPy/newui/preview3d.py @@ -56,13 +56,43 @@ class previewPanel(wx.Panel): self.layerSpin = wx.SpinCtrl(self.toolbar, -1, '', size=(21*4,21), style=wx.SP_ARROW_KEYS) self.toolbar.AddControl(self.layerSpin) self.Bind(wx.EVT_SPINCTRL, self.OnLayerNrChange, self.layerSpin) + + self.toolbar2 = wx.ToolBar( self, -1 ) + self.toolbar2.SetToolBitmapSize( ( 21, 21 ) ) + self.toolbar2.AddControl(wx.StaticText(self.toolbar2, -1, 'Flip:')) + + self.flipX = wx.CheckBox(self.toolbar2, -1, "X") + self.flipX.SetValue(profile.getProfileSetting('flip_x') == 'True') + self.toolbar2.AddControl(self.flipX) + self.Bind(wx.EVT_CHECKBOX, self.OnFlipXClick, self.flipX) + self.flipY = wx.CheckBox(self.toolbar2, -1, "Y") + self.flipY.SetValue(profile.getProfileSetting('flip_y') == 'True') + self.toolbar2.AddControl(self.flipY) + self.Bind(wx.EVT_CHECKBOX, self.OnFlipYClick, self.flipY) + self.flipZ = wx.CheckBox(self.toolbar2, -1, "Z") + self.flipZ.SetValue(profile.getProfileSetting('flip_z') == 'True') + self.toolbar2.AddControl(self.flipZ) + self.Bind(wx.EVT_CHECKBOX, self.OnFlipZClick, self.flipZ) self.updateToolbar() sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(self.toolbar, 0, flag=wx.EXPAND|wx.TOP|wx.LEFT|wx.RIGHT, border=1) sizer.Add(self.glCanvas, 1, flag=wx.EXPAND) + sizer.Add(self.toolbar2, 0, flag=wx.EXPAND|wx.BOTTOM|wx.LEFT|wx.RIGHT, border=1) self.SetSizer(sizer) + + def OnFlipXClick(self, e): + profile.putProfileSetting('flip_x', str(self.flipX.GetValue())) + self.updateModelTransform() + + def OnFlipYClick(self, e): + profile.putProfileSetting('flip_y', str(self.flipY.GetValue())) + self.updateModelTransform() + + def OnFlipZClick(self, e): + profile.putProfileSetting('flip_z', str(self.flipZ.GetValue())) + self.updateModelTransform() def On3DClick(self, e): self.glCanvas.yaw = 30 From b398e2a2cd00394c0db80d32e986d2eee82fd0c0 Mon Sep 17 00:00:00 2001 From: daid Date: Wed, 21 Mar 2012 16:53:05 +0100 Subject: [PATCH 09/21] Added scale to preview window instead of model tab --- SkeinPyPy/newui/mainWindow.py | 4 ---- SkeinPyPy/newui/preview3d.py | 15 +++++++++++++++ 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/SkeinPyPy/newui/mainWindow.py b/SkeinPyPy/newui/mainWindow.py index d8688c4a..b6200ed3 100644 --- a/SkeinPyPy/newui/mainWindow.py +++ b/SkeinPyPy/newui/mainWindow.py @@ -159,10 +159,6 @@ class mainWindow(configBase.configWindowBase): nb.AddPage(alterationPanel.alterationPanel(nb), "Start/End-GCode") (left, right) = self.CreateConfigTab(nb, '3D Model') - configBase.TitleRow(left, "Scale") - c = configBase.SettingRow(left, "Scale", 'model_scale', '1.0', '') - validators.validFloat(c, 0.01) - configBase.settingNotify(c, self.preview3d.updateModelTransform) configBase.TitleRow(right, "Rotate") c = configBase.SettingRow(right, "Rotate (deg)", 'model_rotate_base', '0', '') validators.validFloat(c) diff --git a/SkeinPyPy/newui/preview3d.py b/SkeinPyPy/newui/preview3d.py index 4941a8f6..fc36fd66 100644 --- a/SkeinPyPy/newui/preview3d.py +++ b/SkeinPyPy/newui/preview3d.py @@ -74,6 +74,13 @@ class previewPanel(wx.Panel): self.toolbar2.AddControl(self.flipZ) self.Bind(wx.EVT_CHECKBOX, self.OnFlipZClick, self.flipZ) + self.toolbar2.InsertSeparator(4) + self.toolbar2.AddControl(wx.StaticText(self.toolbar2, -1, 'Scale')) + self.scale = wx.TextCtrl(self.toolbar2, -1, profile.getProfileSetting('model_scale'), size=(21*2,21)) + self.toolbar2.AddControl(self.scale) + self.Bind(wx.EVT_TEXT, self.OnScale, self.scale) + + self.toolbar2.Realize() self.updateToolbar() sizer = wx.BoxSizer(wx.VERTICAL) @@ -94,6 +101,14 @@ class previewPanel(wx.Panel): profile.putProfileSetting('flip_z', str(self.flipZ.GetValue())) self.updateModelTransform() + def OnScale(self, e): + try: + scale = float(self.scale.GetValue()) + except: + scale = 1.0 + profile.putProfileSetting('model_scale', str(scale)) + self.updateModelTransform() + def On3DClick(self, e): self.glCanvas.yaw = 30 self.glCanvas.pitch = 60 From 8990a097f7afa3c48e984345a9dcebca5af683cb Mon Sep 17 00:00:00 2001 From: daid Date: Wed, 21 Mar 2012 17:23:02 +0100 Subject: [PATCH 10/21] Add a few float validators to advanced config. --- SkeinPyPy/newui/advancedConfig.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/SkeinPyPy/newui/advancedConfig.py b/SkeinPyPy/newui/advancedConfig.py index 8312e8b4..2febe8bc 100644 --- a/SkeinPyPy/newui/advancedConfig.py +++ b/SkeinPyPy/newui/advancedConfig.py @@ -28,27 +28,36 @@ class advancedConfigWindow(configBase.configWindowBase): configBase.TitleRow(left, "Cool") c = configBase.SettingRow(left, "Minimum feedrate (mm/s)", 'cool_min_feedrate', '5', 'The minimal layer time can cause the print to slow down so much it starts to ooze. The minimal feedrate protects against this. Even if a print gets slown down it will never be slower then this minimal feedrate.') + validators.validFloat(c, 0.0) configBase.TitleRow(left, "Joris") c = configBase.SettingRow(left, "Joris the outer edge", 'joris', False, '[Joris] is a code name for smoothing out the Z move of the outer edge. This will create a steady Z increase over the whole print. It is intended to be used with a single walled wall thickness to make cups/vases.') configBase.TitleRow(left, "Raft (if enabled)") c = configBase.SettingRow(left, "Raft extra margin (mm)", 'raft_margin', '3.0', 'If the raft is enabled, this is the extra raft area around the object which is also rafted. Increasing this margin will create a stronger raft.') + validators.validFloat(c, 0.0) c = configBase.SettingRow(left, "Raft base material amount (%)", 'raft_base_material_amount', '100', 'The base layer is the first layer put down as a raft. This layer has thick strong lines and is put firmly on the bed to prevent warping. This setting adjust the amount of material used for the base layer.') + validators.validFloat(c, 0.0) c = configBase.SettingRow(left, "Raft interface material amount (%)", 'raft_interface_material_amount', '100', 'The interface layer is a weak thin layer between the base layer and the printed object. It is designed to has little material to make it easy to break the base off the printed object. This setting adjusts the amount of material used for the interface layer.') + validators.validFloat(c, 0.0) configBase.TitleRow(right, "Infill") c = configBase.SettingRow(right, "Infill pattern", 'infill_type', ['Line', 'Grid Circular', 'Grid Hexagonal', 'Grid Rectangular'], 'Pattern of the none-solid infill. Line is default, but grids can provide a strong print.') c = configBase.SettingRow(right, "Solid infill top", 'solid_top', True, 'Create a solid top surface, if set to false the top is filled with the fill percentage. Useful for cups/vases.') c = configBase.SettingRow(right, "Infill overlap (%)", 'fill_overlap', '15', 'Amount of overlap between the infill and the walls. There is a slight overlap with the walls and the infill so the walls connect firmly to the infill.') + validators.validFloat(c, 0.0) configBase.TitleRow(right, "Support") c = configBase.SettingRow(right, "Support material amount (%)", 'support_rate', '100', 'Amount of material used for support, less material gives a weaker support structure which is easier to remove.') + validators.validFloat(c, 0.0) c = configBase.SettingRow(right, "Support distance from object (mm)", 'support_distance', '0.5', 'Distance between the support structure and the object.') + validators.validFloat(c, 0.0) configBase.TitleRow(right, "Bridge") c = configBase.SettingRow(right, "Bridge speed (%)", 'bridge_speed', '100', 'Speed at which bridges are printed, compared to normal printing speed.') + validators.validFloat(c, 0.0) c = configBase.SettingRow(right, "Bridge material (%)", 'bridge_material_amount', '100', 'Amount of material used for bridges, increase go extrude more material when printing a bridge.') + validators.validFloat(c, 0.0) main.Fit() self.Fit() From 25be7369352ebfb9fb81e4fbade7a595a65485eb Mon Sep 17 00:00:00 2001 From: daid Date: Wed, 21 Mar 2012 17:46:04 +0100 Subject: [PATCH 11/21] . --- SkeinPyPy/profiles/CupDeJoris.ini | 40 ------------------------------- 1 file changed, 40 deletions(-) delete mode 100644 SkeinPyPy/profiles/CupDeJoris.ini diff --git a/SkeinPyPy/profiles/CupDeJoris.ini b/SkeinPyPy/profiles/CupDeJoris.ini deleted file mode 100644 index d0e7c1f3..00000000 --- a/SkeinPyPy/profiles/CupDeJoris.ini +++ /dev/null @@ -1,40 +0,0 @@ -[profile] -max_z_speed = 1.0 -sequence = Infill > Loops > Perimeter -nozzle_size = 0.4 -machine_center_x = 100 -machine_center_y = 100 -flip_z = False -flip_x = False -cool_min_layer_time = 10 -infill_type = Line -skirt_line_count = 3 -retraction_amount = 0.0 -travel_speed = 150 -model_rotate_base = 0 -support_rate = 100 -wall_thickness = 0.4 -print_temperature = 0 -skirt_gap = 6.0 -support = None -bottom_layer_speed = 25 -filament_density = 1.00 -joris = True -model_multiply_y = 1 -model_multiply_x = 1 -support_distance = 0.5 -fill_density = 0 -filament_diameter = 2.89 -print_speed = 50 -fill_overlap = 15 -retraction_min_travel = 5.0 -solid_top = False -retraction_speed = 13.5 -extra_base_wall_thickness = 15 -flip_y = False -solid_layer_thickness = 0.6 -model_scale = 1.0 -retraction_extra = 0.0 -force_first_layer_sequence = False -layer_height = 0.2 - From 77b16b5ccf824d99f3d4e5edf2a115b45b6e3252 Mon Sep 17 00:00:00 2001 From: daid Date: Wed, 21 Mar 2012 18:22:00 +0100 Subject: [PATCH 12/21] Added reset profile function, preperation for "simple" mode --- SkeinPyPy/newui/profile.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/SkeinPyPy/newui/profile.py b/SkeinPyPy/newui/profile.py index 3c1ec2ac..5a3e4036 100644 --- a/SkeinPyPy/newui/profile.py +++ b/SkeinPyPy/newui/profile.py @@ -69,14 +69,20 @@ def getDefaultProfilePath(): return os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../current_profile.ini")) def loadGlobalProfile(filename): - "Read a configuration file as global config" + #Read a configuration file as global config global globalProfileParser globalProfileParser = ConfigParser.ConfigParser() globalProfileParser.read(filename) def saveGlobalProfile(filename): + #Save the current profile to an ini file globalProfileParser.write(open(filename, 'w')) +def resetGlobalProfile(): + #Create an empty profile with no settings, so everything gets default settings. + global globalProfileParser + globalProfileParser = ConfigParser.ConfigParser() + def getProfileSetting(name): if name in profileDefaultSettings: default = profileDefaultSettings[name] From befd9bfdc5404aee10ca63a103d4adfdcf7ea9ef Mon Sep 17 00:00:00 2001 From: daid Date: Thu, 22 Mar 2012 11:24:52 +0100 Subject: [PATCH 13/21] Move multiply to preview window. (Only rotate left to move) --- SkeinPyPy/newui/mainWindow.py | 7 ------- SkeinPyPy/newui/preview3d.py | 34 +++++++++++++++++++++++++++++++++- 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/SkeinPyPy/newui/mainWindow.py b/SkeinPyPy/newui/mainWindow.py index b6200ed3..d7fef3c7 100644 --- a/SkeinPyPy/newui/mainWindow.py +++ b/SkeinPyPy/newui/mainWindow.py @@ -163,13 +163,6 @@ class mainWindow(configBase.configWindowBase): c = configBase.SettingRow(right, "Rotate (deg)", 'model_rotate_base', '0', '') validators.validFloat(c) configBase.settingNotify(c, self.preview3d.updateModelTransform) - configBase.TitleRow(right, "Multiply") - c = configBase.SettingRow(right, "Multiple X", 'model_multiply_x', '1', '') - validators.validInt(c) - configBase.settingNotify(c, self.preview3d.updateModelTransform) - c = configBase.SettingRow(right, "Multiple Y", 'model_multiply_y', '1', '') - validators.validInt(c) - configBase.settingNotify(c, self.preview3d.updateModelTransform) # load and slice buttons. loadButton = wx.Button(self, -1, 'Load Model') diff --git a/SkeinPyPy/newui/preview3d.py b/SkeinPyPy/newui/preview3d.py index fc36fd66..798eddbf 100644 --- a/SkeinPyPy/newui/preview3d.py +++ b/SkeinPyPy/newui/preview3d.py @@ -74,11 +74,27 @@ class previewPanel(wx.Panel): self.toolbar2.AddControl(self.flipZ) self.Bind(wx.EVT_CHECKBOX, self.OnFlipZClick, self.flipZ) - self.toolbar2.InsertSeparator(4) + self.toolbar2.InsertSeparator(self.toolbar2.GetToolsCount()) self.toolbar2.AddControl(wx.StaticText(self.toolbar2, -1, 'Scale')) self.scale = wx.TextCtrl(self.toolbar2, -1, profile.getProfileSetting('model_scale'), size=(21*2,21)) self.toolbar2.AddControl(self.scale) self.Bind(wx.EVT_TEXT, self.OnScale, self.scale) + self.toolbar2.InsertSeparator(self.toolbar2.GetToolsCount()) + + self.toolbar2.AddControl(wx.StaticText(self.toolbar2, -1, 'Copy:')) + self.mulXsub = wx.Button(self.toolbar2, -1, '-', size=(21,21)) + self.toolbar2.AddControl(self.mulXsub) + self.Bind(wx.EVT_BUTTON, self.OnMulXSubClick, self.mulXsub) + self.mulXadd = wx.Button(self.toolbar2, -1, '+', size=(21,21)) + self.toolbar2.AddControl(self.mulXadd) + self.Bind(wx.EVT_BUTTON, self.OnMulXAddClick, self.mulXadd) + + self.mulYsub = wx.Button(self.toolbar2, -1, '-', size=(21,21)) + self.toolbar2.AddControl(self.mulYsub) + self.Bind(wx.EVT_BUTTON, self.OnMulYSubClick, self.mulYsub) + self.mulYadd = wx.Button(self.toolbar2, -1, '+', size=(21,21)) + self.toolbar2.AddControl(self.mulYadd) + self.Bind(wx.EVT_BUTTON, self.OnMulYAddClick, self.mulYadd) self.toolbar2.Realize() self.updateToolbar() @@ -101,6 +117,22 @@ class previewPanel(wx.Panel): profile.putProfileSetting('flip_z', str(self.flipZ.GetValue())) self.updateModelTransform() + def OnMulXAddClick(self, e): + profile.putProfileSetting('model_multiply_x', str(max(1, int(profile.getProfileSetting('model_multiply_x'))+1))) + self.updateModelTransform() + + def OnMulXSubClick(self, e): + profile.putProfileSetting('model_multiply_x', str(max(1, int(profile.getProfileSetting('model_multiply_x'))-1))) + self.updateModelTransform() + + def OnMulYAddClick(self, e): + profile.putProfileSetting('model_multiply_y', str(max(1, int(profile.getProfileSetting('model_multiply_y'))+1))) + self.updateModelTransform() + + def OnMulYSubClick(self, e): + profile.putProfileSetting('model_multiply_y', str(max(1, int(profile.getProfileSetting('model_multiply_y'))-1))) + self.updateModelTransform() + def OnScale(self, e): try: scale = float(self.scale.GetValue()) From 53c39d5b4f6631b6a1ad19e01fe1eef8af72e48b Mon Sep 17 00:00:00 2001 From: daid Date: Thu, 22 Mar 2012 12:33:39 +0100 Subject: [PATCH 14/21] Move the rotate button to the preview window. And... model tab be-gone! --- SkeinPyPy/newui/mainWindow.py | 7 +------ SkeinPyPy/newui/preview3d.py | 19 +++++++++++++++---- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/SkeinPyPy/newui/mainWindow.py b/SkeinPyPy/newui/mainWindow.py index d7fef3c7..69e2f427 100644 --- a/SkeinPyPy/newui/mainWindow.py +++ b/SkeinPyPy/newui/mainWindow.py @@ -158,12 +158,6 @@ class mainWindow(configBase.configWindowBase): nb.AddPage(alterationPanel.alterationPanel(nb), "Start/End-GCode") - (left, right) = self.CreateConfigTab(nb, '3D Model') - configBase.TitleRow(right, "Rotate") - c = configBase.SettingRow(right, "Rotate (deg)", 'model_rotate_base', '0', '') - validators.validFloat(c) - configBase.settingNotify(c, self.preview3d.updateModelTransform) - # load and slice buttons. loadButton = wx.Button(self, -1, 'Load Model') sliceButton = wx.Button(self, -1, 'Slice to GCode') @@ -193,6 +187,7 @@ class mainWindow(configBase.configWindowBase): self.SetMinSize(self.GetSize()) self.Centre() self.Show(True) + print self.GetSize() def OnLoadProfile(self, e): dlg=wx.FileDialog(self, "Select profile file to load", self.lastPath, style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST) diff --git a/SkeinPyPy/newui/preview3d.py b/SkeinPyPy/newui/preview3d.py index 798eddbf..a888ba7c 100644 --- a/SkeinPyPy/newui/preview3d.py +++ b/SkeinPyPy/newui/preview3d.py @@ -27,7 +27,7 @@ class previewPanel(wx.Panel): wx.Panel.__init__(self, parent,-1) self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DDKSHADOW)) - self.SetMinSize((400,300)) + self.SetMinSize((440,320)) self.glCanvas = PreviewGLCanvas(self) self.init = 0 @@ -59,7 +59,7 @@ class previewPanel(wx.Panel): self.toolbar2 = wx.ToolBar( self, -1 ) self.toolbar2.SetToolBitmapSize( ( 21, 21 ) ) - self.toolbar2.AddControl(wx.StaticText(self.toolbar2, -1, 'Flip:')) + self.toolbar2.AddControl(wx.StaticText(self.toolbar2, -1, 'Flip')) self.flipX = wx.CheckBox(self.toolbar2, -1, "X") self.flipX.SetValue(profile.getProfileSetting('flip_x') == 'True') @@ -79,9 +79,9 @@ class previewPanel(wx.Panel): self.scale = wx.TextCtrl(self.toolbar2, -1, profile.getProfileSetting('model_scale'), size=(21*2,21)) self.toolbar2.AddControl(self.scale) self.Bind(wx.EVT_TEXT, self.OnScale, self.scale) - self.toolbar2.InsertSeparator(self.toolbar2.GetToolsCount()) - self.toolbar2.AddControl(wx.StaticText(self.toolbar2, -1, 'Copy:')) + self.toolbar2.InsertSeparator(self.toolbar2.GetToolsCount()) + self.toolbar2.AddControl(wx.StaticText(self.toolbar2, -1, 'Copy')) self.mulXsub = wx.Button(self.toolbar2, -1, '-', size=(21,21)) self.toolbar2.AddControl(self.mulXsub) self.Bind(wx.EVT_BUTTON, self.OnMulXSubClick, self.mulXsub) @@ -96,6 +96,13 @@ class previewPanel(wx.Panel): self.toolbar2.AddControl(self.mulYadd) self.Bind(wx.EVT_BUTTON, self.OnMulYAddClick, self.mulYadd) + self.toolbar2.InsertSeparator(self.toolbar2.GetToolsCount()) + self.toolbar2.AddControl(wx.StaticText(self.toolbar2, -1, 'Rot')) + self.rotate = wx.SpinCtrl(self.toolbar2, -1, profile.getProfileSetting('model_rotate_base'), size=(21*3,21), style=wx.SP_WRAP|wx.SP_ARROW_KEYS) + self.rotate.SetRange(0, 360) + self.toolbar2.AddControl(self.rotate) + self.Bind(wx.EVT_SPINCTRL, self.OnRotate, self.rotate) + self.toolbar2.Realize() self.updateToolbar() @@ -140,6 +147,10 @@ class previewPanel(wx.Panel): scale = 1.0 profile.putProfileSetting('model_scale', str(scale)) self.updateModelTransform() + + def OnRotate(self, e): + profile.putProfileSetting('model_rotate_base', self.rotate.GetValue()) + self.updateModelTransform() def On3DClick(self, e): self.glCanvas.yaw = 30 From c9746b95b99eecc1b572f91b929650c947e6aee7 Mon Sep 17 00:00:00 2001 From: daid Date: Thu, 22 Mar 2012 13:43:07 +0100 Subject: [PATCH 15/21] Patch from SF49 to SF50. Only notiable change is the addition of the "Sharpest Angle" setting. --- SkeinPyPy/SkeinforgeVersion | 2 +- SkeinPyPy/fabmetheus_utilities/euclidean.py | 68 +++++++++---------- .../geometry/solids/triangle_mesh.py | 2 +- SkeinPyPy/fabmetheus_utilities/intercircle.py | 2 +- SkeinPyPy/fabmetheus_utilities/svg_writer.py | 4 +- SkeinPyPy/fabmetheus_utilities/version.txt | 2 +- .../craft_plugins/chamber.py | 3 + .../skeinforge_plugins/craft_plugins/comb.py | 11 --- .../skeinforge_plugins/craft_plugins/cool.py | 3 +- .../craft_plugins/dwindle.py | 39 ++++++++--- .../skeinforge_plugins/craft_plugins/fill.py | 24 +++++-- .../skeinforge_plugins/craft_plugins/inset.py | 5 ++ .../skeinforge_plugins/craft_plugins/mill.py | 2 +- .../skeinforge_plugins/craft_plugins/raft.py | 17 +++-- .../skeinforge_plugins/craft_plugins/skin.py | 24 ++++--- .../skeinforge_plugins/craft_plugins/widen.py | 22 +++--- .../skeinforge_utilities/skeinforge_craft.py | 21 +++--- 17 files changed, 142 insertions(+), 109 deletions(-) diff --git a/SkeinPyPy/SkeinforgeVersion b/SkeinPyPy/SkeinforgeVersion index 4f56f122..fb1ff680 100644 --- a/SkeinPyPy/SkeinforgeVersion +++ b/SkeinPyPy/SkeinforgeVersion @@ -1,2 +1,2 @@ -This SkeinPyPy version is based in Skeinforge: 49 +This SkeinPyPy version is based in Skeinforge: 50 diff --git a/SkeinPyPy/fabmetheus_utilities/euclidean.py b/SkeinPyPy/fabmetheus_utilities/euclidean.py index 072a5fa5..24708e02 100644 --- a/SkeinPyPy/fabmetheus_utilities/euclidean.py +++ b/SkeinPyPy/fabmetheus_utilities/euclidean.py @@ -363,41 +363,41 @@ def compareSegmentLength( endpoint, otherEndpoint ): return - 1 return 0 -def concatenateRemovePath( connectedPaths, pathIndex, paths, pixelDictionary, segments, width ): +def concatenateRemovePath(connectedPaths, pathIndex, paths, pixelDictionary, segments, sharpestProduct, width): 'Get connected paths from paths.' - bottomSegment = segments[ pathIndex ] - path = paths[ pathIndex ] + bottomSegment = segments[pathIndex] + path = paths[pathIndex] if bottomSegment == None: connectedPaths.append(path) return - endpoints = getEndpointsFromSegments( segments[ pathIndex + 1 : ] ) + endpoints = getEndpointsFromSegments(segments[pathIndex + 1 :]) bottomSegmentEndpoint = bottomSegment[0] - nextEndpoint = bottomSegmentEndpoint.getClosestMissCheckEndpointPath( endpoints, bottomSegmentEndpoint.path, pixelDictionary, width ) + nextEndpoint = bottomSegmentEndpoint.getClosestMissCheckEndpointPath(endpoints, bottomSegmentEndpoint.path, pixelDictionary, sharpestProduct, width) if nextEndpoint == None: bottomSegmentEndpoint = bottomSegment[1] - nextEndpoint = bottomSegmentEndpoint.getClosestMissCheckEndpointPath( endpoints, bottomSegmentEndpoint.path, pixelDictionary, width ) + nextEndpoint = bottomSegmentEndpoint.getClosestMissCheckEndpointPath(endpoints, bottomSegmentEndpoint.path, pixelDictionary, sharpestProduct, width) if nextEndpoint == None: connectedPaths.append(path) return - if len( bottomSegmentEndpoint.path ) > 0 and len( nextEndpoint.path ) > 0: + if len(bottomSegmentEndpoint.path) > 0 and len(nextEndpoint.path) > 0: bottomEnd = bottomSegmentEndpoint.path[-1] nextBegin = nextEndpoint.path[-1] - nextMinusBottomNormalized = getNormalized( nextBegin - bottomEnd ) + nextMinusBottomNormalized = getNormalized(nextBegin - bottomEnd) if len( bottomSegmentEndpoint.path ) > 1: bottomPenultimate = bottomSegmentEndpoint.path[-2] - if getDotProduct( getNormalized( bottomPenultimate - bottomEnd ), nextMinusBottomNormalized ) > 0.9: + if getDotProduct(getNormalized(bottomPenultimate - bottomEnd), nextMinusBottomNormalized) > 0.99: connectedPaths.append(path) return if len( nextEndpoint.path ) > 1: nextPenultimate = nextEndpoint.path[-2] - if getDotProduct( getNormalized( nextPenultimate - nextBegin ), - nextMinusBottomNormalized ) > 0.9: + if getDotProduct(getNormalized(nextPenultimate - nextBegin), - nextMinusBottomNormalized) > 0.99: connectedPaths.append(path) return nextEndpoint.path.reverse() concatenatedPath = bottomSegmentEndpoint.path + nextEndpoint.path - paths[ nextEndpoint.pathIndex ] = concatenatedPath - segments[ nextEndpoint.pathIndex ] = getSegmentFromPath( concatenatedPath, nextEndpoint.pathIndex ) - addValueSegmentToPixelTable( bottomSegmentEndpoint.point, nextEndpoint.point, pixelDictionary, None, width ) + paths[nextEndpoint.pathIndex] = concatenatedPath + segments[nextEndpoint.pathIndex] = getSegmentFromPath(concatenatedPath, nextEndpoint.pathIndex) + addValueSegmentToPixelTable(bottomSegmentEndpoint.point, nextEndpoint.point, pixelDictionary, None, width) def getAngleAroundZAxisDifference( subtractFromVec3, subtractVec3 ): 'Get the angle around the Z axis difference between a pair of Vector3s.' @@ -668,18 +668,18 @@ def getConcatenatedList(originalLists): concatenatedList += originalList return concatenatedList -def getConnectedPaths( paths, pixelDictionary, width ): +def getConnectedPaths(paths, pixelDictionary, sharpestProduct, width): 'Get connected paths from paths.' if len(paths) < 2: return paths connectedPaths = [] segments = [] - for pathIndex in xrange( len(paths) ): - path = paths[ pathIndex ] - segments.append( getSegmentFromPath( path, pathIndex ) ) - for pathIndex in xrange( 0, len(paths) - 1 ): - concatenateRemovePath( connectedPaths, pathIndex, paths, pixelDictionary, segments, width ) - connectedPaths.append( paths[-1] ) + for pathIndex in xrange(len(paths)): + path = paths[pathIndex] + segments.append(getSegmentFromPath(path, pathIndex)) + for pathIndex in xrange(0, len(paths) - 1): + concatenateRemovePath(connectedPaths, pathIndex, paths, pixelDictionary, segments, sharpestProduct, width) + connectedPaths.append(paths[-1]) return connectedPaths def getCrossProduct(firstComplex, secondComplex): @@ -1327,7 +1327,7 @@ def getPathLength(path): pathLength += abs(firstPoint - secondPoint) return pathLength -def getPathsFromEndpoints(endpoints, maximumConnectionLength, pixelDictionary, width): +def getPathsFromEndpoints(endpoints, maximumConnectionLength, pixelDictionary, sharpestProduct, width): 'Get paths from endpoints.' if len(endpoints) < 2: return [] @@ -1343,7 +1343,7 @@ def getPathsFromEndpoints(endpoints, maximumConnectionLength, pixelDictionary, w path = [] paths = [path] if len(endpoints) > 1: - nextEndpoint = otherEndpoint.getClosestMiss(endpoints, path, pixelDictionary, width) + nextEndpoint = otherEndpoint.getClosestMiss(endpoints, path, pixelDictionary, sharpestProduct, width) if nextEndpoint != None: if abs(nextEndpoint.point - endpointFirst.point) < abs(nextEndpoint.point - otherEndpoint.point): endpointFirst = endpointFirst.otherEndpoint @@ -1359,7 +1359,7 @@ def getPathsFromEndpoints(endpoints, maximumConnectionLength, pixelDictionary, w if len(endpointTable.values()[0]) < 2: return [] endpoints = getSquareValuesFromPoint(endpointTable, otherEndpoint.point * oneOverEndpointWidth) - nextEndpoint = otherEndpoint.getClosestMiss(endpoints, path, pixelDictionary, width) + nextEndpoint = otherEndpoint.getClosestMiss(endpoints, path, pixelDictionary, sharpestProduct, width) if nextEndpoint == None: path = [] paths.append(path) @@ -2096,7 +2096,7 @@ class Endpoint: closestEndpoint = endpoint return closestEndpoint - def getClosestMiss(self, endpoints, path, pixelDictionary, width): + def getClosestMiss(self, endpoints, path, pixelDictionary, sharpestProduct, width): 'Get the closest endpoint which the segment to that endpoint misses the other extrusions.' pathMaskTable = {} smallestDistance = 987654321.0 @@ -2115,7 +2115,7 @@ class Endpoint: endpoints.sort(compareSegmentLength) for endpoint in endpoints[: 15]: # increasing the number of searched endpoints increases the search time, with 20 fill took 600 seconds for cilinder.gts, with 10 fill took 533 seconds normalizedSegment = endpoint.segment / endpoint.segmentLength - isOverlappingSelf = getDotProduct(penultimateMinusPoint, normalizedSegment) > 0.9 + isOverlappingSelf = getDotProduct(penultimateMinusPoint, normalizedSegment) > sharpestProduct if not isOverlappingSelf: if len(path) > 2: segmentYMirror = complex(normalizedSegment.real, -normalizedSegment.imag) @@ -2132,14 +2132,14 @@ class Endpoint: return endpoint return None - def getClosestMissCheckEndpointPath( self, endpoints, path, pixelDictionary, width ): + def getClosestMissCheckEndpointPath(self, endpoints, path, pixelDictionary, sharpestProduct, width): 'Get the closest endpoint which the segment to that endpoint misses the other extrusions, also checking the path of the endpoint.' pathMaskTable = {} smallestDistance = 987654321.0 penultimateMinusPoint = complex(0.0, 0.0) if len(path) > 1: penultimatePoint = path[-2] - addSegmentToPixelTable( penultimatePoint, self.point, pathMaskTable, 0, 0, width ) + addSegmentToPixelTable(penultimatePoint, self.point, pathMaskTable, 0, 0, width) penultimateMinusPoint = penultimatePoint - self.point if abs(penultimateMinusPoint) > 0.0: penultimateMinusPoint /= abs(penultimateMinusPoint) @@ -2151,27 +2151,27 @@ class Endpoint: endpoints.sort( compareSegmentLength ) for endpoint in endpoints[ : 15 ]: # increasing the number of searched endpoints increases the search time, with 20 fill took 600 seconds for cilinder.gts, with 10 fill took 533 seconds normalizedSegment = endpoint.segment / endpoint.segmentLength - isOverlappingSelf = getDotProduct( penultimateMinusPoint, normalizedSegment ) > 0.9 + isOverlappingSelf = getDotProduct(penultimateMinusPoint, normalizedSegment) > sharpestProduct if not isOverlappingSelf: if len(path) > 2: segmentYMirror = complex(normalizedSegment.real, -normalizedSegment.imag) pointRotated = segmentYMirror * self.point endpointPointRotated = segmentYMirror * endpoint.point - if isXSegmentIntersectingPath( path[ max( 0, len(path) - 21 ) : - 1 ], pointRotated.real, endpointPointRotated.real, segmentYMirror, pointRotated.imag ): + if isXSegmentIntersectingPath(path[ max(0, len(path) - 21) : -1], pointRotated.real, endpointPointRotated.real, segmentYMirror, pointRotated.imag): isOverlappingSelf = True endpointPath = endpoint.path - if len( endpointPath ) > 2: + if len(endpointPath) > 2: segmentYMirror = complex(normalizedSegment.real, -normalizedSegment.imag) pointRotated = segmentYMirror * self.point endpointPointRotated = segmentYMirror * endpoint.point - if isXSegmentIntersectingPath( endpointPath, pointRotated.real, endpointPointRotated.real, segmentYMirror, pointRotated.imag ): + if isXSegmentIntersectingPath(endpointPath, pointRotated.real, endpointPointRotated.real, segmentYMirror, pointRotated.imag): isOverlappingSelf = True if not isOverlappingSelf: totalMaskTable = pathMaskTable.copy() - addSegmentToPixelTable( endpoint.point, endpoint.otherEndpoint.point, totalMaskTable, 0, 0, width ) + addSegmentToPixelTable(endpoint.point, endpoint.otherEndpoint.point, totalMaskTable, 0, 0, width) segmentTable = {} - addSegmentToPixelTable( self.point, endpoint.point, segmentTable, 0, 0, width ) - if not isPixelTableIntersecting( pixelDictionary, segmentTable, totalMaskTable ): + addSegmentToPixelTable(self.point, endpoint.point, segmentTable, 0, 0, width) + if not isPixelTableIntersecting(pixelDictionary, segmentTable, totalMaskTable): return endpoint return None diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/solids/triangle_mesh.py b/SkeinPyPy/fabmetheus_utilities/geometry/solids/triangle_mesh.py index 9bf210d4..0cfffdcc 100644 --- a/SkeinPyPy/fabmetheus_utilities/geometry/solids/triangle_mesh.py +++ b/SkeinPyPy/fabmetheus_utilities/geometry/solids/triangle_mesh.py @@ -311,7 +311,7 @@ def getDescendingAreaLoops(allPoints, corners, importRadius): sortLoopsInOrderOfArea(True, loops) pointDictionary = {} for loop in loops: - if len(loop) > 2 and getOverlapRatio(loop, pointDictionary) < 0.3: + if len(loop) > 2 and getOverlapRatio(loop, pointDictionary) < 0.3 and intercircle.getIsLarge(loop, importRadius): intercircle.directLoop(not euclidean.getIsInFilledRegion(descendingAreaLoops, loop[0]), loop) descendingAreaLoops.append(loop) addLoopToPointTable(loop, pointDictionary) diff --git a/SkeinPyPy/fabmetheus_utilities/intercircle.py b/SkeinPyPy/fabmetheus_utilities/intercircle.py index 3c4e6026..ea448d06 100644 --- a/SkeinPyPy/fabmetheus_utilities/intercircle.py +++ b/SkeinPyPy/fabmetheus_utilities/intercircle.py @@ -427,7 +427,7 @@ def getLargestInsetLoopFromLoopRegardless( loop, radius ): largestInsetLoop = getLargestInsetLoopFromLoop( loop, decreasingRadius ) if len( largestInsetLoop ) > 0: return largestInsetLoop - print('This should never happen, there should always be a largestInsetLoop in getLargestInsetLoopFromLoopRegardless in intercircle.') + print('Warning, there should always be a largestInsetLoop in getLargestInsetLoopFromLoopRegardless in intercircle.') print(loop) return loop diff --git a/SkeinPyPy/fabmetheus_utilities/svg_writer.py b/SkeinPyPy/fabmetheus_utilities/svg_writer.py index ae836dfe..15855d26 100644 --- a/SkeinPyPy/fabmetheus_utilities/svg_writer.py +++ b/SkeinPyPy/fabmetheus_utilities/svg_writer.py @@ -218,9 +218,7 @@ class SVGWriter: self.setTexts('volume', 'Volume: %s cm3' % self.getRounded(volume)) if not self.addLayerTemplateToSVG: self.svgElement.getFirstChildByLocalName('script').removeFromIDNameParent() - self.svgElement.getElementNodeByID('isoControlBox').removeFromIDNameParent() - self.svgElement.getElementNodeByID('layerControlBox').removeFromIDNameParent() - self.svgElement.getElementNodeByID('scrollControlBox').removeFromIDNameParent() + self.svgElement.getElementNodeByID('controls').removeFromIDNameParent() self.graphicsElementNode.removeFromIDNameParent() self.addOriginalAsComment(elementNode) return documentNode.__repr__() diff --git a/SkeinPyPy/fabmetheus_utilities/version.txt b/SkeinPyPy/fabmetheus_utilities/version.txt index 0d73a0c7..2dc4ea15 100644 --- a/SkeinPyPy/fabmetheus_utilities/version.txt +++ b/SkeinPyPy/fabmetheus_utilities/version.txt @@ -1 +1 @@ -12.02.10 \ No newline at end of file +12.03.14 \ No newline at end of file diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/chamber.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/chamber.py index ce54b113..181614ba 100644 --- a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/chamber.py +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/chamber.py @@ -17,6 +17,9 @@ The default 'Activate Chamber' checkbox is on. When it is on, the functions des ===Bed=== The initial bed temperature is defined by 'Bed Temperature'. If the 'Bed Temperature End Change Height' is greater or equal to the 'Bed Temperature Begin Change Height' and the 'Bed Temperature Begin Change Height' is greater or equal to zero, then the temperature will be ramped toward the 'Bed Temperature End'. The ramp will start once the extruder reaches the 'Bed Temperature Begin Change Height', then the bed temperature will approach the 'Bed Temperature End' as the extruder reaches the 'Bed Temperature End Change Height', finally the bed temperature will stay at the 'Bed Temperature End' for the remainder of the build. +The idea is described at: +http://www.makerbot.com/blog/2011/03/17/if-you-cant-stand-the-heat/ + ====Bed Temperature==== Default: 60C diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/comb.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/comb.py index a817f52b..665f7bfc 100644 --- a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/comb.py +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/comb.py @@ -174,7 +174,6 @@ class CombSkein: "A class to comb a skein of extrusions." def __init__(self): 'Initialize' -# self.betweenTable = {} self.boundaryLoop = None self.distanceFeedRate = gcodec.DistanceFeedRate() self.extruderActive = False @@ -240,7 +239,6 @@ class CombSkein: def getAroundBetweenPath(self, begin, end): 'Get the path around the loops in the way of the original line segment.' aroundBetweenPath = [] -# betweens = self.getBetweens() boundaries = self.getBoundaries() boundarySegments = self.getBoundarySegments(begin, boundaries, end) for boundarySegmentIndex, boundarySegment in enumerate(boundarySegments): @@ -264,14 +262,6 @@ class CombSkein: del aroundBetweenPath[pointIndex] return aroundBetweenPath -# def getBetweens(self): -# 'Get betweens for the layer.' -# if not self.layerZ in self.betweenTable: -# self.betweenTable[self.layerZ] = [] -# for boundary in self.getBoundaries(): -# self.betweenTable[self.layerZ] += intercircle.getInsetLoopsFromLoop(boundary, self.betweenInset) -# return self.betweenTable[self.layerZ] -# def getBoundaries(self): "Get boundaries for the layer." if self.layerZ in self.layerTable: @@ -439,7 +429,6 @@ class CombSkein: return elif firstWord == '(': self.edgeWidth = float(splitLine[1]) -# self.betweenInset = 0.7 * self.edgeWidth self.doubleEdgeWidth = self.edgeWidth + self.edgeWidth self.halfEdgeWidth = 0.5 * self.edgeWidth self.quadrupleEdgeWidth = self.doubleEdgeWidth + self.doubleEdgeWidth diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/cool.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/cool.py index 419eab9f..d78f194c 100644 --- a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/cool.py +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/cool.py @@ -230,7 +230,8 @@ class CoolSkein: def addFlowRate(self, flowRate): 'Add a multipled line of flow rate if different.' - self.distanceFeedRate.addLine('M108 S' + euclidean.getFourSignificantFigures(flowRate)) + if flowRate != None: + self.distanceFeedRate.addLine('M108 S' + euclidean.getFourSignificantFigures(flowRate)) def addGcodeFromFeedRateMovementZ(self, feedRateMinute, point, z): 'Add a movement to the output.' diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/dwindle.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/dwindle.py index 05c1b599..8f175ea4 100644 --- a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/dwindle.py +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/dwindle.py @@ -1,8 +1,6 @@ """ This page is in the table of contents. -Dwindle is a plugin to smooth the surface dwindle of an object by replacing the edge surface with a surface printed at a fraction of the carve -height. This gives the impression that the object was carved at a much thinner height giving a high-quality finish, but still prints -in a relatively short time. The latest process has some similarities with a description at: +Dwindle is a plugin to reduce the feed rate and flow rate at the end of the thread, in order to reduce the ooze when traveling. It reduces the flow rate by a bit more than the feed rate, in order to use up the pent up plastic in the thread so that there is less remaining in the ooze. The dwindle manual page is at: http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Dwindle @@ -11,10 +9,25 @@ http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Dwindle The default 'Activate Dwindle' checkbox is off. When it is on, the functions described below will work, when it is off, nothing will be done. ==Settings== -====Vertical Divisions==== -Default: 2 +===End Rate Multiplier=== +Default: 0.5 -Defines the number of times the dwindle infill and edges are divided vertically. +Defines the ratio of the feed and flow rate at the end over the feed and flow rate of the rest of the thread. With reasonable values for the 'Pent Up Volume' and 'Slowdown Volume', the amount of ooze should be roughly proportional to the square of the 'End Rate Multiplier'. If the 'End Rate Multiplier' is too low, the printing will be very slow because the feed rate will be lower. If the 'End Rate Multiplier' is too high, there will still be a lot of ooze. + +===Pent Up Volume=== +Default: 0.4 mm3 + +When the filament is stopped, there is a pent up volume of plastic that comes out afterwards. For best results, the 'Pent Up Volume' in dwindle should be set to that amount. If the 'Pent Up Volume' is too small, there will still be a lot of ooze. If the 'Pent Up Volume' is too large, the end of the thread will be thinner than the rest of the thread. + +===Slowdown Steps=== +Default: 3 + +Dwindle reduces the feed rate and flow rate in steps so the thread will remain at roughly the same thickness until the end. The "Slowdown Steps" setting is the number of steps, the more steps the smaller the variation in the thread thickness, but the larger the size of the resulting gcode file and the more time spent pausing between segments. + +===Slowdown Volume=== +Default: 5 mm3 + +The 'Slowdown Volume' is the volume of the end of the thread where the feed and flow rates will be decreased. If the 'Slowdown Volume' is too small, there won't be enough time to get rid of the pent up plastic, so there will still be a lot of ooze. If the 'Slowdown Volume' is too large, a bit of time will be wasted because for a large portion of the thread, the feed rate will be slow. Overall, it is best to err on being too large, because too large would only waste machine time in production, rather than the more important string removal labor time. ==Examples== The following examples dwindle the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and dwindle.py. @@ -80,15 +93,15 @@ class DwindleRepository: 'A class to handle the dwindle settings.' def __init__(self): 'Set the default settings, execute title & settings fileName.' - skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.dwindle.html', self ) - self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Dwindle', self, '') + skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.dwindle.html', self) + self.fileNameInput = settings.FileNameInput().getFromFileName(fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Dwindle', self, '') self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Dwindle') self.activateDwindle = settings.BooleanSetting().getFromValue('Activate Dwindle', self, False) settings.LabelSeparator().getFromRepository(self) self.endRateMultiplier = settings.FloatSpin().getFromValue(0.4, 'End Rate Multiplier (ratio):', self, 0.8, 0.5) self.pentUpVolume = settings.FloatSpin().getFromValue(0.1, 'Pent Up Volume (cubic millimeters):', self, 1.0, 0.4) self.slowdownSteps = settings.IntSpin().getFromValue(2, 'Slowdown Steps (positive integer):', self, 10, 3) - self.slowdownVolume = settings.FloatSpin().getFromValue(0.4, 'Slowdown Volume (cubic millimeters):', self, 4.0, 2.0) + self.slowdownVolume = settings.FloatSpin().getFromValue(1.0, 'Slowdown Volume (cubic millimeters):', self, 10.0, 5.0) self.executeTitle = 'Dwindle' def execute(self): @@ -110,6 +123,7 @@ class DwindleSkein: self.lines = None self.oldFlowRate = None self.oldLocation = None + self.operatingFlowRate = None self.threadSections = [] def addThread(self): @@ -138,7 +152,10 @@ class DwindleSkein: self.lines = archive.getTextLines(gcodeText) self.repository = repository self.parseInitialization() - self.area = self.infillWidth * self.layerHeight + if self.operatingFlowRate == None: + print('Warning, there is no operatingFlowRate so dwindle will do nothing.') + return gcodeText + self.area = self.infillWidth * self.layerHeight * self.volumeFraction self.oneOverSteps = 1.0 / float(repository.slowdownSteps.value) self.halfOverSteps = 0.5 * self.oneOverSteps for self.lineIndex in xrange(self.lineIndex, len(self.lines)): @@ -165,6 +182,8 @@ class DwindleSkein: elif firstWord == '(': self.operatingFlowRate = float(splitLine[1]) self.oldFlowRate = self.operatingFlowRate + elif firstWord == '(': + self.volumeFraction = float(splitLine[1]) self.distanceFeedRate.addLine(line) def parseLine(self, line): diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/fill.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/fill.py index 7cf5bcce..28e79b7e 100644 --- a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/fill.py +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/fill.py @@ -118,6 +118,13 @@ Default is 1.5. Defines the ratio of the infill width over the layer height. The higher the value the wider apart the infill will be and therefore the sparser the infill will be. +===Sharpest Angle=== +Default: 60 degrees + +Defines the sharpest angle that a thread is allowed to make before it is separated into two threads. If 'Sharpest Angle' is too low, the extruder will stop and start often, slowing printing and putting more wear and tear on the extruder. If 'Sharpest Angle' is too high, then threads will almost double back on themselves, leading to bumps in the fill, and sometimes filament being dragged by the nozzle. + +This parameter is used in fill, raft and skin. + ===Solid Surface Thickness=== Default is three. @@ -791,7 +798,7 @@ class FillRepository: settings.LabelDisplay().getFromName('- Infill -', self ) self.infillBeginRotation = settings.FloatSpin().getFromValue( 0.0, 'Infill Begin Rotation (degrees):', self, 90.0, 45.0 ) self.infillBeginRotationRepeat = settings.IntSpin().getFromValue( 0, 'Infill Begin Rotation Repeat (layers):', self, 3, 1 ) - self.infillOddLayerExtraRotation = settings.FloatSpin().getFromValue( 30.0, 'Infill Odd Layer Extra Rotation (degrees):', self, 90.0, 90.0 ) + self.infillOddLayerExtraRotation = settings.FloatSpin().getFromValue(30.0, 'Infill Odd Layer Extra Rotation (degrees):', self, 90.0, 90.0) self.infillPatternLabel = settings.LabelDisplay().getFromName('Infill Pattern:', self ) infillLatentStringVar = settings.LatentStringVar() self.infillPatternGridCircular = settings.Radio().getFromRadio( infillLatentStringVar, 'Grid Circular', self, False ) @@ -800,8 +807,8 @@ class FillRepository: self.infillPatternLine = settings.Radio().getFromRadio( infillLatentStringVar, 'Line', self, True ) self.infillPerimeterOverlap = settings.FloatSpin().getFromValue( 0.0, 'Infill Perimeter Overlap (ratio):', self, 0.4, 0.15 ) self.infillSolidity = settings.FloatSpin().getFromValue( 0.04, 'Infill Solidity (ratio):', self, 0.3, 0.2 ) - self.infillWidth = settings.FloatSpin().getFromValue( 0.1, 'Infill Width:', self, 1.7, 0.4 ) settings.LabelSeparator().getFromRepository(self) + self.sharpestAngle = settings.FloatSpin().getFromValue(50.0, 'Sharpest Angle (degrees):', self, 70.0, 60.0) self.solidSurfaceThickness = settings.IntSpin().getFromValue(0, 'Solid Surface Thickness (layers):', self, 5, 3) self.startFromChoice = settings.MenuButtonDisplay().getFromName('Start From Choice:', self) self.startFromLowerLeft = settings.MenuRadio().getFromMenuButtonDisplay(self.startFromChoice, 'Lower Left', self, True) @@ -878,7 +885,8 @@ class FillSkein: extraShells = 0 self.distanceFeedRate.addLine('( %s )' % layerRotation) self.distanceFeedRate.addLine('( %s )' % layerRotation) - aroundWidth = 0.34321 * self.infillWidth +# aroundWidth = 0.34321 * self.infillWidth + aroundWidth = 0.24321 * self.infillWidth doubleInfillWidth = 2.0 * self.infillWidth gridPointInsetX = 0.5 * self.fillInset self.lastExtraShells = extraShells @@ -929,7 +937,7 @@ class FillSkein: for segments in self.horizontalSegmentsDictionary.values(): for segment in segments: endpoints += segment - paths = euclidean.getPathsFromEndpoints(endpoints, 5.0 * self.infillWidth, pixelTable, aroundWidth) + paths = euclidean.getPathsFromEndpoints(endpoints, 5.0 * self.infillWidth, pixelTable, self.sharpestProduct, aroundWidth) if gridCircular: startAngle = euclidean.globalGoldenAngle * float(layerIndex) for gridPoint in self.getGridPoints(fillLoops, reverseRotation): @@ -942,7 +950,7 @@ class FillSkein: while oldRemovedEndpointLength - len(removedEndpoints) > 0: oldRemovedEndpointLength = len(removedEndpoints) removeEndpoints(self.infillWidth, paths, pixelTable, removedEndpoints, aroundWidth) - paths = euclidean.getConnectedPaths(paths, pixelTable, aroundWidth) + paths = euclidean.getConnectedPaths(paths, pixelTable, self.sharpestProduct, aroundWidth) for path in paths: addPath(self.infillWidth, infillPaths, path, layerRotation) euclidean.transferPathsToNestedRings(nestedRings, infillPaths) @@ -1118,6 +1126,7 @@ class FillSkein: 'Parse gcode text and store the bevel gcode.' self.repository = repository self.lines = archive.getTextLines(gcodeText) + self.sharpestProduct = math.sin(math.radians(repository.sharpestAngle.value)) self.threadSequence = None if repository.threadSequenceInfillLoops.value: self.threadSequence = ['infill', 'loops', 'edge'] @@ -1251,12 +1260,13 @@ class FillSkein: if firstWord == '()': self.distanceFeedRate.addLine(line) return + elif firstWord == '(': + self.infillWidth = float(splitLine[1]) elif firstWord == '(': self.layerHeight = float(splitLine[1]) - self.infillWidth = self.repository.infillWidth.value self.surroundingSlope = math.tan(math.radians(min(self.repository.surroundingAngle.value, 80.0))) self.distanceFeedRate.addTagRoundedLine('infillPerimeterOverlap', self.repository.infillPerimeterOverlap.value) - self.distanceFeedRate.addTagRoundedLine('infillWidth', self.infillWidth) + self.distanceFeedRate.addTagRoundedLine('sharpestProduct', self.sharpestProduct) elif firstWord == '(': self.edgeWidth = float(splitLine[1]) threadSequenceString = ' '.join( self.threadSequence ) diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/inset.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/inset.py index 74cc85c0..bb68db20 100644 --- a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/inset.py +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/inset.py @@ -38,6 +38,11 @@ Default is on. When selected, the M104 S0 gcode line will be added to the end of the file to turn the extruder heater off by setting the extruder heater temperature to 0. +===Volume Fraction=== +Default: 0.82 + +The 'Volume Fraction' is the estimated volume of the thread compared to the box defined by the layer height and infill width. This is used in dwindle, splodge, and statistic. It is in inset because inset is a required extrusion tool, earlier in the chain than dwindle and splodge. In dwindle and splodge it is used to determine the filament volume, in statistic it is used to determine the extrusion diameter. + ==Examples== The following examples inset the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and inset.py. diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/mill.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/mill.py index 90248e56..867e3a99 100644 --- a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/mill.py +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/mill.py @@ -200,7 +200,7 @@ class MillSkein: endpoints = euclidean.getEndpointsFromSegmentTable( boundaryLayer.segmentTable ) if len(endpoints) < 1: return - paths = euclidean.getPathsFromEndpoints(endpoints, 5.0 * self.millWidth, self.aroundPixelTable, self.aroundWidth) + paths = euclidean.getPathsFromEndpoints(endpoints, 5.0 * self.millWidth, self.aroundPixelTable, 1.0, self.aroundWidth) averageZ = self.average.getAverage() if self.repository.addInnerLoops.value: self.addGcodeFromLoops( boundaryLayer.innerLoops, averageZ ) diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/raft.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/raft.py index cdb47ce4..1fc1c868 100644 --- a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/raft.py +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/raft.py @@ -443,6 +443,7 @@ class RaftSkein: self.operatingLayerEndLine = '( )' self.operatingJump = None self.orbitalFeedRatePerSecond = 2.01 + self.sharpestProduct = 0.94 self.supportFlowRate = None self.supportLayers = [] self.supportLayersTemperature = None @@ -547,7 +548,7 @@ class RaftSkein: return aroundPixelTable = {} aroundWidth = 0.34321 * step - paths = euclidean.getPathsFromEndpoints(endpoints, 1.5 * step, aroundPixelTable, aroundWidth) + paths = euclidean.getPathsFromEndpoints(endpoints, 1.5 * step, aroundPixelTable, self.sharpestProduct, aroundWidth) self.addLayerLine(z) if self.operatingFlowRate != None: self.addFlowRate(flowRateMultiplier * self.operatingFlowRate) @@ -704,7 +705,7 @@ class RaftSkein: aroundBoundaryLoops = intercircle.getAroundsFromLoops(boundaryLoops, halfSupportOutset) for aroundBoundaryLoop in aroundBoundaryLoops: euclidean.addLoopToPixelTable(aroundBoundaryLoop, aroundPixelTable, aroundWidth) - paths = euclidean.getPathsFromEndpoints(endpoints, 1.5 * self.interfaceStep, aroundPixelTable, aroundWidth) + paths = euclidean.getPathsFromEndpoints(endpoints, 1.5 * self.interfaceStep, aroundPixelTable, self.sharpestProduct, aroundWidth) feedRateMinuteMultiplied = self.operatingFeedRateMinute supportFlowRateMultiplied = self.supportFlowRate if self.layerIndex == 0: @@ -894,6 +895,11 @@ class RaftSkein: self.baseTemperature = float(splitLine[1]) elif firstWord == '(': self.coolingRate = float(splitLine[1]) + elif firstWord == '(': + self.edgeWidth = float(splitLine[1]) + self.halfEdgeWidth = 0.5 * self.edgeWidth + self.quarterEdgeWidth = 0.25 * self.edgeWidth + self.supportOutset = self.edgeWidth + self.edgeWidth * self.repository.supportGapOverPerimeterExtrusionWidth.value elif firstWord == '()': self.distanceFeedRate.addTagBracketedProcedure('raft') elif firstWord == '(': @@ -925,11 +931,8 @@ class RaftSkein: self.operatingFlowRate = float(splitLine[1]) self.oldFlowRate = self.operatingFlowRate self.supportFlowRate = self.operatingFlowRate * self.repository.supportFlowRateOverOperatingFlowRate.value - elif firstWord == '(': - self.edgeWidth = float(splitLine[1]) - self.halfEdgeWidth = 0.5 * self.edgeWidth - self.quarterEdgeWidth = 0.25 * self.edgeWidth - self.supportOutset = self.edgeWidth + self.edgeWidth * self.repository.supportGapOverPerimeterExtrusionWidth.value + elif firstWord == '(': + self.sharpestProduct = float(splitLine[1]) elif firstWord == '(': self.supportLayersTemperature = float(splitLine[1]) elif firstWord == '(': diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/skin.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/skin.py index 37e0841d..d3ea4ea9 100644 --- a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/skin.py +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/skin.py @@ -154,11 +154,13 @@ class SkinSkein: self.maximumZFeedRateMinute = 60.0 self.oldFlowRate = None self.oldLocation = None + self.sharpestProduct = 0.94 self.travelFeedRateMinute = 957.0 def addFlowRateLine(self, flowRate): 'Add a flow rate line.' - self.distanceFeedRate.addLine('M108 S' + euclidean.getFourSignificantFigures(flowRate)) + if flowRate != None: + self.distanceFeedRate.addLine('M108 S' + euclidean.getFourSignificantFigures(flowRate)) def addPerimeterLoop(self, thread, z): 'Add the edge loop to the gcode.' @@ -170,7 +172,8 @@ class SkinSkein: return bottomZ = self.oldLocation.z + self.layerHeight / self.verticalDivisionsFloat - self.layerHeight offsetY = 0.5 * self.skinInfillWidth - self.addFlowRateLine(self.oldFlowRate / self.verticalDivisionsFloat / self.horizontalInfillDivisionsFloat) + if self.oldFlowRate != None: + self.addFlowRateLine(self.oldFlowRate / self.verticalDivisionsFloat / self.horizontalInfillDivisionsFloat) for verticalDivisionIndex in xrange(self.verticalDivisions): z = bottomZ + self.layerHeight / self.verticalDivisionsFloat * float(verticalDivisionIndex) self.addSkinnedInfillBoundary(self.infillBoundaries, offsetY * (verticalDivisionIndex % 2 == 0), self.oldLocation.z, z) @@ -199,7 +202,7 @@ class SkinSkein: for endpoint in segment: endpoint.point = complex(endpoint.point.real, endpoint.point.imag + offsetY) endpoints.append(endpoint) - infillPaths = euclidean.getPathsFromEndpoints(endpoints, 5.0 * self.skinInfillWidth, pixelTable, aroundWidth) + infillPaths = euclidean.getPathsFromEndpoints(endpoints, 5.0 * self.skinInfillWidth, pixelTable, self.sharpestProduct, aroundWidth) for infillPath in infillPaths: addPointBeforeThread = True infillRotated = euclidean.getRotatedComplexes(self.rotation, infillPath) @@ -239,9 +242,12 @@ class SkinSkein: for division in xrange(self.repository.horizontalPerimeterDivisions.value): edges.append(self.getClippedSimplifiedLoopPathByLoop(intercircle.getLargestInsetLoopFromLoop(edgeThread, radius))) radius += radiusAddition - skinnedPerimeterFlowRate = self.oldFlowRate / self.verticalDivisionsFloat + skinnedPerimeterFlowRate = None + if self.oldFlowRate != None: + skinnedPerimeterFlowRate = self.oldFlowRate / self.verticalDivisionsFloat if getIsMinimumSides(edges): - self.addFlowRateLine(skinnedPerimeterFlowRate / self.horizontalPerimeterDivisionsFloat) + if self.oldFlowRate != None: + self.addFlowRateLine(skinnedPerimeterFlowRate / self.horizontalPerimeterDivisionsFloat) for verticalDivisionIndex in xrange(self.verticalDivisions): z = bottomZ + self.layerHeight / self.verticalDivisionsFloat * float(verticalDivisionIndex) for edge in edges: @@ -314,6 +320,9 @@ class SkinSkein: self.distanceFeedRate.parseSplitLine(firstWord, splitLine) if firstWord == '(': self.clipOverEdgeWidth = float(splitLine[1]) + elif firstWord == '(': + self.edgeWidth = float(splitLine[1]) + self.halfEdgeWidth = 0.5 * self.edgeWidth elif firstWord == '()': self.distanceFeedRate.addTagBracketedProcedure('skin') return @@ -328,9 +337,8 @@ class SkinSkein: self.maximumZFeedRateMinute = 60.0 * float(splitLine[1]) elif firstWord == '(': self.oldFlowRate = float(splitLine[1]) - elif firstWord == '(': - self.edgeWidth = float(splitLine[1]) - self.halfEdgeWidth = 0.5 * self.edgeWidth + elif firstWord == '(': + self.sharpestProduct = float(splitLine[1]) elif firstWord == '(': self.travelFeedRateMinute = 60.0 * float(splitLine[1]) self.distanceFeedRate.addLine(line) diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/widen.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/widen.py index 432b34cd..cc612376 100644 --- a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/widen.py +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/widen.py @@ -96,15 +96,15 @@ def getNewRepository(): 'Get new repository.' return WidenRepository() -def getWidenedLoop(loop, loopList, outsetLoop, radius): +def getWidenedLoops(loop, loopList, outsetLoop, radius): 'Get the widened loop.' intersectingWithinLoops = getIntersectingWithinLoops(loop, loopList, outsetLoop) if len(intersectingWithinLoops) < 1: - return loop + return [loop] loopsUnified = boolean_solid.getLoopsUnion(radius, [[loop], intersectingWithinLoops]) if len(loopsUnified) < 1: - return loop - return euclidean.getLargestLoop(loopsUnified) + return [loop] + return loopsUnified def writeOutput(fileName, shouldAnalyze=True): 'Widen the carving of a gcode file.' @@ -121,6 +121,7 @@ class WidenRepository: self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute( 'http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Widen') self.activateWiden = settings.BooleanSetting().getFromValue('Activate Widen', self, False) + self.widenWidthOverEdgeWidth = settings.IntSpin().getFromValue(2, 'Widen Width over Edge Width (ratio):', self, 4, 2) self.executeTitle = 'Widen' def execute(self): @@ -155,15 +156,15 @@ class WidenSkein: else: widdershinsLoops.append(loop) else: -# clockwiseInsetLoop = intercircle.getLargestInsetLoopFromLoop(loop, self.doubleEdgeWidth) +# clockwiseInsetLoop = intercircle.getLargestInsetLoopFromLoop(loop, self.widenEdgeWidth) # clockwiseInsetLoop.reverse() # clockwiseInsetLoops.append(clockwiseInsetLoop) - clockwiseInsetLoops += intercircle.getInsetLoopsFromLoop(loop, self.doubleEdgeWidth) + clockwiseInsetLoops += intercircle.getInsetLoopsFromLoop(loop, self.widenEdgeWidth) self.distanceFeedRate.addGcodeFromLoop(loop, loopLayer.z) for widdershinsLoop in widdershinsLoops: - outsetLoop = intercircle.getLargestInsetLoopFromLoop(widdershinsLoop, -self.doubleEdgeWidth) - widenedLoop = getWidenedLoop(widdershinsLoop, clockwiseInsetLoops, outsetLoop, self.edgeWidth) - self.distanceFeedRate.addGcodeFromLoop(widenedLoop, loopLayer.z) + outsetLoop = intercircle.getLargestInsetLoopFromLoop(widdershinsLoop, -self.widenEdgeWidth) + for widenedLoop in getWidenedLoops(widdershinsLoop, clockwiseInsetLoops, outsetLoop, self.lessThanHalfEdgeWidth): + self.distanceFeedRate.addGcodeFromLoop(widenedLoop, loopLayer.z) def getCraftedGcode(self, gcodeText, repository): 'Parse gcode text and store the widen gcode.' @@ -188,7 +189,8 @@ class WidenSkein: return elif firstWord == '(': self.edgeWidth = float(splitLine[1]) - self.doubleEdgeWidth = 2.0 * self.edgeWidth + self.widenEdgeWidth = float(self.repository.widenWidthOverEdgeWidth.value) * self.edgeWidth + self.lessThanHalfEdgeWidth = 0.49 * self.edgeWidth self.distanceFeedRate.addLine(line) def parseLine(self, line): diff --git a/SkeinPyPy/skeinforge_application/skeinforge_utilities/skeinforge_craft.py b/SkeinPyPy/skeinforge_application/skeinforge_utilities/skeinforge_craft.py index d4413732..bbd30242 100644 --- a/SkeinPyPy/skeinforge_application/skeinforge_utilities/skeinforge_craft.py +++ b/SkeinPyPy/skeinforge_application/skeinforge_utilities/skeinforge_craft.py @@ -87,28 +87,23 @@ def getPluginsDirectoryPath(): "Get the plugins directory path." return archive.getCraftPluginsDirectoryPath() -def getProcedures( procedure, text ): - "Get the procedures up to and including the given procedure." +def getProcedures(procedure, text): + 'Get the procedures up to and including the given procedure.' craftSequence = getReadCraftSequence() sequenceIndexFromProcedure = 0 if procedure in craftSequence: sequenceIndexFromProcedure = craftSequence.index(procedure) - sequenceIndexPlusOneFromText = getSequenceIndexPlusOneFromText(text) - return craftSequence[ sequenceIndexPlusOneFromText : sequenceIndexFromProcedure + 1 ] + craftSequence = craftSequence[: sequenceIndexFromProcedure + 1] + for craftSequenceIndex in xrange(len(craftSequence) - 1, -1, -1): + procedure = craftSequence[craftSequenceIndex] + if gcodec.isProcedureDone(text, procedure): + return craftSequence[craftSequenceIndex + 1 :] + return craftSequence def getReadCraftSequence(): "Get profile sequence." return skeinforge_profile.getCraftTypePluginModule().getCraftSequence() -def getSequenceIndexPlusOneFromText(fileText): - "Get the profile sequence index of the file plus one. Return zero if the procedure is not in the file" - craftSequence = getReadCraftSequence() - for craftSequenceIndex in xrange( len( craftSequence ) - 1, - 1, - 1 ): - procedure = craftSequence[ craftSequenceIndex ] - if gcodec.isProcedureDone( fileText, procedure ): - return craftSequenceIndex + 1 - return 0 - def writeChainTextWithNounMessage(fileName, procedure, shouldAnalyze=True): 'Get and write a crafted shape file.' print('') From 2612610381ff4f2edd1b618df5997cb5d785e1ca Mon Sep 17 00:00:00 2001 From: daid Date: Thu, 22 Mar 2012 13:46:55 +0100 Subject: [PATCH 16/21] Update to SkeinPyPy to work with SF50 --- SkeinPyPy/fabmetheus_utilities/settings.py | 4 +++- .../skeinforge_plugins/craft_plugins/inset.py | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/SkeinPyPy/fabmetheus_utilities/settings.py b/SkeinPyPy/fabmetheus_utilities/settings.py index d211deb7..3c312b16 100644 --- a/SkeinPyPy/fabmetheus_utilities/settings.py +++ b/SkeinPyPy/fabmetheus_utilities/settings.py @@ -125,10 +125,11 @@ def getSkeinPyPyProfileInformation(): 'Turn_Extruder_Off_at_Start_Up': DEFSET, },'widen': { 'Activate_Widen': DEFSET, + 'Widen_Width_over_Edge_Width_ratio': DEFSET, },'inset': { 'Add_Custom_Code_for_Temperature_Reading': DEFSET, 'Infill_in_Direction_of_Bridge': "True", - 'Infill_Width_over_Thickness_ratio': DEFSET, + 'Infill_Width': storedPreference("nozzle_size"), 'Loop_Order_Choice': DEFSET, 'Overlap_Removal_Width_over_Perimeter_Width_ratio': DEFSET, 'Turn_Extruder_Heater_Off_at_Shut_Down': DEFSET, @@ -157,6 +158,7 @@ def getSkeinPyPyProfileInformation(): 'Infill_Perimeter_Overlap_ratio': storedPercentSetting('fill_overlap'), 'Infill_Solidity_ratio': storedPercentSetting('fill_density'), 'Infill_Width': storedPreference("nozzle_size"), + 'Sharpest_Angle_degrees': DEFSET, 'Solid_Surface_Thickness_layers': calculateSolidLayerCount, 'Start_From_Choice': DEFSET, 'Surrounding_Angle_degrees': DEFSET, diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/inset.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/inset.py index bb68db20..3a5115ae 100644 --- a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/inset.py +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/inset.py @@ -301,7 +301,7 @@ class InsetRepository: self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Inset') self.addCustomCodeForTemperatureReading = settings.BooleanSetting().getFromValue('Add Custom Code for Temperature Reading', self, True) self.infillInDirectionOfBridge = settings.BooleanSetting().getFromValue('Infill in Direction of Bridge', self, True) - self.infillWidthOverThickness = settings.FloatSpin().getFromValue(1.3, 'Infill Width over Thickness (ratio):', self, 1.7, 1.5) + self.infillWidth = settings.FloatSpin().getFromValue(0.1, 'Infill Width:', self, 1.7, 0.4) self.loopOrderChoice = settings.MenuButtonDisplay().getFromName('Loop Order Choice:', self ) self.loopOrderAscendingArea = settings.MenuRadio().getFromMenuButtonDisplay(self.loopOrderChoice, 'Ascending Area', self, True) self.loopOrderDescendingArea = settings.MenuRadio().getFromMenuButtonDisplay(self.loopOrderChoice, 'Descending Area', self, False) @@ -430,7 +430,7 @@ class InsetSkein: return elif firstWord == '(': layerHeight = float(splitLine[1]) - self.infillWidth = self.repository.infillWidthOverThickness.value * layerHeight + self.infillWidth = self.repository.infillWidth.value self.distanceFeedRate.addTagRoundedLine('infillWidth', self.infillWidth) self.distanceFeedRate.addTagRoundedLine('volumeFraction', self.repository.volumeFraction.value) elif firstWord == '(': From a1ce9a995b41d307b96289c2fce34b75749c4a08 Mon Sep 17 00:00:00 2001 From: daid Date: Thu, 22 Mar 2012 15:12:37 +0100 Subject: [PATCH 17/21] Disable OpenGL error checking, which makes the 3D preview a lot faster. --- SkeinPyPy/newui/gcodeInterpreter.py | 11 ++++++++--- SkeinPyPy/newui/mainWindow.py | 1 - SkeinPyPy/newui/preview3d.py | 2 ++ 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/SkeinPyPy/newui/gcodeInterpreter.py b/SkeinPyPy/newui/gcodeInterpreter.py index 64d46675..79c278dc 100644 --- a/SkeinPyPy/newui/gcodeInterpreter.py +++ b/SkeinPyPy/newui/gcodeInterpreter.py @@ -8,8 +8,9 @@ from newui import util3d class gcode(): def __init__(self, filename): - print os.stat(filename).st_size - f = open(filename, 'r') + fileSize = os.stat(filename).st_size + filePos = 0 + gcodeFile = open(filename, 'r') pos = util3d.Vector3() posOffset = util3d.Vector3() currentE = 0.0 @@ -25,7 +26,10 @@ class gcode(): startCodeDone = False currentPath = {'type': 'move', 'pathType': pathType, 'list': [pos.copy()], 'layerNr': layerNr} currentPath['list'][-1].e = totalExtrusion - for line in f: + for line in gcodeFile: + if filePos != gcodeFile.tell(): + filePos = gcodeFile.tell() + #print float(filePos) / float(fileSize) if line.startswith(';TYPE:'): pathType = line[6:].strip() if pathType != "CUSTOM": @@ -144,6 +148,7 @@ class gcode(): pass else: print "Unknown M code:" + str(M) + gcodeFile.close() self.layerCount = layerNr self.pathList = pathList self.extrusionAmount = maxExtrusion diff --git a/SkeinPyPy/newui/mainWindow.py b/SkeinPyPy/newui/mainWindow.py index 69e2f427..9d04138f 100644 --- a/SkeinPyPy/newui/mainWindow.py +++ b/SkeinPyPy/newui/mainWindow.py @@ -187,7 +187,6 @@ class mainWindow(configBase.configWindowBase): self.SetMinSize(self.GetSize()) self.Centre() self.Show(True) - print self.GetSize() def OnLoadProfile(self, e): dlg=wx.FileDialog(self, "Select profile file to load", self.lastPath, style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST) diff --git a/SkeinPyPy/newui/preview3d.py b/SkeinPyPy/newui/preview3d.py index a888ba7c..7d0c1b1d 100644 --- a/SkeinPyPy/newui/preview3d.py +++ b/SkeinPyPy/newui/preview3d.py @@ -8,6 +8,8 @@ import os from wx import glcanvas import wx try: + import OpenGL + OpenGL.ERROR_CHECKING = False from OpenGL.GLU import * from OpenGL.GL import * hasOpenGLlibs = True From ef26c9029e4209f91703c86c66e5544851d56729 Mon Sep 17 00:00:00 2001 From: daid Date: Thu, 22 Mar 2012 15:30:28 +0100 Subject: [PATCH 18/21] Speedup gcodeInterpreter a bit by compiling the regexp --- SkeinPyPy/newui/gcodeInterpreter.py | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/SkeinPyPy/newui/gcodeInterpreter.py b/SkeinPyPy/newui/gcodeInterpreter.py index 79c278dc..d4fc68f3 100644 --- a/SkeinPyPy/newui/gcodeInterpreter.py +++ b/SkeinPyPy/newui/gcodeInterpreter.py @@ -1,3 +1,6 @@ +from __future__ import absolute_import +import __init__ + import sys import math import threading @@ -8,6 +11,8 @@ from newui import util3d class gcode(): def __init__(self, filename): + self.regMatch = {} + fileSize = os.stat(filename).st_size filePos = 0 gcodeFile = open(filename, 'r') @@ -156,8 +161,10 @@ class gcode(): print "Extruded a total of: %d mm of filament" % (self.extrusionAmount) print "Estimated print duration: %.2f minutes" % (self.totalMoveTimeMinute) - def getCodeInt(self, str, id): - m = re.search(id + '([^\s]+)', str) + def getCodeInt(self, line, code): + if code not in self.regMatch: + self.regMatch[code] = re.compile(code + '([^\s]+)') + m = self.regMatch[code].search(line) if m == None: return None try: @@ -165,8 +172,10 @@ class gcode(): except: return None - def getCodeFloat(self, str, id): - m = re.search(id + '([^\s]+)', str) + def getCodeFloat(self, line, code): + if code not in self.regMatch: + self.regMatch[code] = re.compile(code + '([^\s]+)') + m = self.regMatch[code].search(line) if m == None: return None try: @@ -174,3 +183,7 @@ class gcode(): except: return None +if __name__ == '__main__': + for filename in sys.argv[1:]: + gcode(filename) + From a0fb01ba49273c3104e2f0a8855789a979bbf7c0 Mon Sep 17 00:00:00 2001 From: daid Date: Thu, 22 Mar 2012 16:04:53 +0100 Subject: [PATCH 19/21] Only have 1 load thread at a time, else we run into strange bugs --- SkeinPyPy/newui/gcodeInterpreter.py | 13 ++++++--- SkeinPyPy/newui/preview3d.py | 41 ++++++++++++++++++++--------- 2 files changed, 38 insertions(+), 16 deletions(-) diff --git a/SkeinPyPy/newui/gcodeInterpreter.py b/SkeinPyPy/newui/gcodeInterpreter.py index d4fc68f3..cd10cdfa 100644 --- a/SkeinPyPy/newui/gcodeInterpreter.py +++ b/SkeinPyPy/newui/gcodeInterpreter.py @@ -10,9 +10,15 @@ import os from newui import util3d class gcode(): - def __init__(self, filename): + def __init__(self): self.regMatch = {} + self.layerCount = 0 + self.pathList = [] + self.extrusionAmount = 0 + self.totalMoveTimeMinute = 0 + self.progressCallback = None + def load(self, filename): fileSize = os.stat(filename).st_size filePos = 0 gcodeFile = open(filename, 'r') @@ -34,7 +40,8 @@ class gcode(): for line in gcodeFile: if filePos != gcodeFile.tell(): filePos = gcodeFile.tell() - #print float(filePos) / float(fileSize) + if self.progressCallback != None: + self.progressCallback(float(filePos) / float(fileSize)) if line.startswith(';TYPE:'): pathType = line[6:].strip() if pathType != "CUSTOM": @@ -185,5 +192,5 @@ class gcode(): if __name__ == '__main__': for filename in sys.argv[1:]: - gcode(filename) + gcode().load(filename) diff --git a/SkeinPyPy/newui/preview3d.py b/SkeinPyPy/newui/preview3d.py index 7d0c1b1d..5607f9c1 100644 --- a/SkeinPyPy/newui/preview3d.py +++ b/SkeinPyPy/newui/preview3d.py @@ -36,6 +36,8 @@ class previewPanel(wx.Panel): self.triangleMesh = None self.gcode = None self.modelFilename = None + self.loadingProgressAmount = 0 + self.loadThread = None self.machineSize = Vector3(float(profile.getPreference('machine_width')), float(profile.getPreference('machine_depth')), float(profile.getPreference('machine_height'))) self.machineCenter = Vector3(0, 0, 0) @@ -192,15 +194,19 @@ class previewPanel(wx.Panel): self.gcodeFilename = filename[: filename.rfind('.')] + "_export.gcode" self.logFilename = filename[: filename.rfind('.')] + "_export.log" #Do the STL file loading in a background thread so we don't block the UI. - threading.Thread(target=self.doFileLoad).start() + if self.loadThread != None and self.loadThread.isAlive(): + self.loadThread.join() + self.loadThread = threading.Thread(target=self.doFileLoadThread) + self.loadThread.daemon = True + self.loadThread.start() def loadReModelFile(self, filename): #Only load this again if the filename matches the file we have already loaded (for auto loading GCode after slicing) if self.modelFilename != filename: return - threading.Thread(target=self.doFileLoad).start() + self.loadModelFile(filename) - def doFileLoad(self): + def doFileLoadThread(self): if os.path.isfile(self.modelFilename) and self.modelFileTime != os.stat(self.modelFilename).st_mtime: self.modelFileTime = os.stat(self.modelFilename).st_mtime triangleMesh = fabmetheus_interpret.getCarving(self.modelFilename) @@ -217,7 +223,10 @@ class previewPanel(wx.Panel): if os.path.isfile(self.gcodeFilename) and self.gcodeFileTime != os.stat(self.gcodeFilename).st_mtime: self.gcodeFileTime = os.stat(self.gcodeFilename).st_mtime - gcode = gcodeInterpreter.gcode(self.gcodeFilename) + gcode = gcodeInterpreter.gcode() + gcode.progressCallback = self.loadProgress + gcode.load(self.gcodeFilename) + self.loadingProgressAmount = 0 self.gcodeDirty = False self.errorList = [] self.gcode = gcode @@ -238,6 +247,10 @@ class previewPanel(wx.Panel): self.errorList = errorList wx.CallAfter(self.glCanvas.Refresh) + def loadProgress(self, progress): + self.loadingProgressAmount = progress + wx.CallAfter(self.glCanvas.Refresh) + def updateToolbar(self): self.layerSpin.Show(self.gcode != None) if self.gcode != None: @@ -480,15 +493,16 @@ class PreviewGLCanvas(glcanvas.GLCanvas): glVertex3f(v3.x, v3.y, v3.z - 0.01) glVertex3f(v2.x, v2.y, v2.z - 0.01) glEnd() - for v in path['list']: - glBegin(GL_TRIANGLE_FAN) - glVertex3f(v.x, v.y, v.z - 0.001) - for i in xrange(0, 16+1): - if path['pathType'] == 'FILL': #Remove depth buffer fighting on infill/wall overlap - glVertex3f(v.x + math.cos(math.pi*2/16*i) * lineWidth, v.y + math.sin(math.pi*2/16*i) * lineWidth, v.z - 0.02) - else: - glVertex3f(v.x + math.cos(math.pi*2/16*i) * lineWidth, v.y + math.sin(math.pi*2/16*i) * lineWidth, v.z - 0.01) - glEnd() + + #for v in path['list']: + # glBegin(GL_TRIANGLE_FAN) + # glVertex3f(v.x, v.y, v.z - 0.001) + # for i in xrange(0, 16+1): + # if path['pathType'] == 'FILL': #Remove depth buffer fighting on infill/wall overlap + # glVertex3f(v.x + math.cos(math.pi*2/16*i) * lineWidth, v.y + math.sin(math.pi*2/16*i) * lineWidth, v.z - 0.02) + # else: + # glVertex3f(v.x + math.cos(math.pi*2/16*i) * lineWidth, v.y + math.sin(math.pi*2/16*i) * lineWidth, v.z - 0.01) + # glEnd() else: glBegin(GL_LINE_STRIP) for v in path['list']: @@ -601,6 +615,7 @@ class PreviewGLCanvas(glcanvas.GLCanvas): glVertex3f(err[0].x, err[0].y, err[0].z) glVertex3f(err[1].x, err[1].y, err[1].z) glEnd() + glFlush() def InitGL(self): From 1ec67a61d7e588d17d80a2ac7d7bc20478fe20b6 Mon Sep 17 00:00:00 2001 From: daid Date: Thu, 22 Mar 2012 17:36:35 +0100 Subject: [PATCH 20/21] Fix joris plugin, it caused moves to be also extruded, and removed the first data point. --- .../skeinforge_plugins/craft_plugins/joris.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/joris.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/joris.py index cd6ba5bf..8c128d32 100644 --- a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/joris.py +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/joris.py @@ -100,6 +100,7 @@ class JorisSkein: self.travelFeedRateMinute = 957.0 self.perimeter = None self.oldLocation = None + self.doJoris = False def getCraftedGcode( self, gcodeText, repository ): 'Parse gcode text and store the joris gcode.' @@ -134,7 +135,7 @@ class JorisSkein: if len(splitLine) < 1: return firstWord = splitLine[0] - if firstWord == 'G1': + if firstWord == 'G1' and self.doJoris: self.feedRateMinute = gcodec.getFeedRateMinute(self.feedRateMinute, splitLine) location = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine) self.oldLocation = location @@ -148,9 +149,15 @@ class JorisSkein: self.oldFlowRate = gcodec.getDoubleAfterFirstLetter(splitLine[1]) elif firstWord == '(': if self.layerIndex >= self.layersFromBottom: - self.perimeter = [] - elif firstWord == '()': + self.doJoris = True + elif firstWord == 'M101' and self.doJoris: + self.perimeter = [] + return + elif firstWord == 'M103' and self.doJoris: self.addJorisedPerimeter() + return + elif firstWord == '()': + self.doJoris = False self.distanceFeedRate.addLine(line) def addJorisedPerimeter(self): @@ -160,7 +167,7 @@ class JorisSkein: #Calculate the total length of the perimeter. p = self.oldLocation.dropAxis() perimeterLength = 0; - for point in self.perimeter[1 :]: + for point in self.perimeter: perimeterLength += abs( point - p ); p = point @@ -168,7 +175,7 @@ class JorisSkein: p = self.oldLocation.dropAxis() len = 0; self.distanceFeedRate.addLine('M101') # Turn extruder on. - for point in self.perimeter[1 :]: + for point in self.perimeter: len += abs( point - p ); p = point self.distanceFeedRate.addGcodeMovementZWithFeedRate(self.feedRateMinute, point, self.oldLocation.z + self.layerThickness * len / perimeterLength) From 25687ea3b8a2583ba30b27c30d78672c7135374a Mon Sep 17 00:00:00 2001 From: Daid Date: Thu, 22 Mar 2012 20:29:03 +0100 Subject: [PATCH 21/21] Rename of skein to slice --- SkeinPyPy/newui/preview3d.py | 4 ++-- SkeinPyPy/newui/sliceProgessPanel.py | 4 ++-- SkeinPyPy/newui/{skeinRun.py => sliceRun.py} | 4 ++-- SkeinPyPy/skeinpypy.py | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) rename SkeinPyPy/newui/{skeinRun.py => sliceRun.py} (96%) diff --git a/SkeinPyPy/newui/preview3d.py b/SkeinPyPy/newui/preview3d.py index 5607f9c1..2778b664 100644 --- a/SkeinPyPy/newui/preview3d.py +++ b/SkeinPyPy/newui/preview3d.py @@ -30,7 +30,7 @@ class previewPanel(wx.Panel): self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DDKSHADOW)) self.SetMinSize((440,320)) - + self.glCanvas = PreviewGLCanvas(self) self.init = 0 self.triangleMesh = None @@ -60,7 +60,7 @@ class previewPanel(wx.Panel): self.layerSpin = wx.SpinCtrl(self.toolbar, -1, '', size=(21*4,21), style=wx.SP_ARROW_KEYS) self.toolbar.AddControl(self.layerSpin) self.Bind(wx.EVT_SPINCTRL, self.OnLayerNrChange, self.layerSpin) - + self.toolbar2 = wx.ToolBar( self, -1 ) self.toolbar2.SetToolBitmapSize( ( 21, 21 ) ) self.toolbar2.AddControl(wx.StaticText(self.toolbar2, -1, 'Flip')) diff --git a/SkeinPyPy/newui/sliceProgessPanel.py b/SkeinPyPy/newui/sliceProgessPanel.py index 8d2a83e8..ba10bb4d 100644 --- a/SkeinPyPy/newui/sliceProgessPanel.py +++ b/SkeinPyPy/newui/sliceProgessPanel.py @@ -8,7 +8,7 @@ import threading import subprocess import time -from newui import skeinRun +from newui import sliceRun class sliceProgessPanel(wx.Panel): def __init__(self, mainWindow, parent, filename): @@ -115,7 +115,7 @@ class WorkerThread(threading.Thread): self.start() def run(self): - p = subprocess.Popen(skeinRun.getSkeinCommand(self.filename), stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + p = subprocess.Popen(sliceRun.getSliceCommand(self.filename), stdout=subprocess.PIPE, stderr=subprocess.STDOUT) line = p.stdout.readline() maxValue = 1 self.progressLog = [] diff --git a/SkeinPyPy/newui/skeinRun.py b/SkeinPyPy/newui/sliceRun.py similarity index 96% rename from SkeinPyPy/newui/skeinRun.py rename to SkeinPyPy/newui/sliceRun.py index 8709d215..22327682 100644 --- a/SkeinPyPy/newui/skeinRun.py +++ b/SkeinPyPy/newui/sliceRun.py @@ -23,7 +23,7 @@ def getPyPyExe(): return pypyExe return False -def runSkein(fileNames): +def runSlice(fileNames): "Run the slicer on the files. If we are running with PyPy then just do the slicing action. If we are running as Python, try to find pypy." pypyExe = getPyPyExe() for fileName in fileNames: @@ -40,7 +40,7 @@ def runSkein(fileNames): else: subprocess.call([pypyExe, os.path.join(sys.path[0], sys.argv[0]), fileName]) -def getSkeinCommand(filename): +def getSliceCommand(filename): pypyExe = getPyPyExe() if pypyExe == False: pypyExe = sys.executable diff --git a/SkeinPyPy/skeinpypy.py b/SkeinPyPy/skeinpypy.py index 6aa5ef3d..f3079617 100644 --- a/SkeinPyPy/skeinpypy.py +++ b/SkeinPyPy/skeinpypy.py @@ -16,7 +16,7 @@ import sys import platform from optparse import OptionParser -from newui import skeinRun +from newui import sliceRun __author__ = 'Daid' __credits__ = """ @@ -47,7 +47,7 @@ def main(): (options, args) = parser.parse_args() sys.argv = [sys.argv[0]] + args if len( args ) > 0: - skeinRun.runSkein(args) + sliceRun.runSlice(args) else: from newui import mainWindow mainWindow.main()