chiark / gitweb /
Added model transformation options (scale/flip/rotate)
authordaid <daid303@gmail.com>
Wed, 7 Mar 2012 15:58:04 +0000 (16:58 +0100)
committerdaid <daid303@gmail.com>
Wed, 7 Mar 2012 15:58:04 +0000 (16:58 +0100)
SkeinPyPy_NewUI/fabmetheus_utilities/settings.py
SkeinPyPy_NewUI/newui/advancedConfig.py
SkeinPyPy_NewUI/newui/configBase.py
SkeinPyPy_NewUI/newui/configWizard.py
SkeinPyPy_NewUI/newui/mainWindow.py
SkeinPyPy_NewUI/newui/preview3d.py
SkeinPyPy_NewUI/skeinforge_application/skeinforge_plugins/craft_plugins/carve.py

index 32727e34a9c1bb5dd4d8f9e0805baee8f913ba1e..6cf7c00c31e23b0b062afb3cc980a60bf7554ed2 100644 (file)
@@ -85,6 +85,11 @@ def getSkeinPyPyProfileInformation():
                        'Correct_Mesh': DEFSET,
                        'Unproven_Mesh': DEFSET,
                        'SVG_Viewer': DEFSET,
+                       'FlipX': storedSetting("flip_x"),
+                       'FlipY': storedSetting("flip_y"),
+                       'FlipZ': storedSetting("flip_z"),
+                       'Scale': storedSetting("model_scale"),
+                       'Rotate': storedSetting("model_rotate_base"),
                },'scale': {
                        'Activate_Scale': "False",
                        'XY_Plane_Scale_ratio': DEFSET,
index 7de21eff9256d81121b1c0f889dfeba7a219ef42..dc54b30d16d83d41ec13761dae702650a8081c50 100644 (file)
@@ -26,14 +26,14 @@ class advancedConfigWindow(configBase.configWindowBase):
                validators.validFloat(c, 0.0)
                configBase.TitleRow(left, "Sequence")
                c = configBase.SettingRow(left, "Print order sequence", 'sequence', ['Loops > Perimeter > Infill', 'Loops > Infill > Perimeter', 'Infill > Loops > Perimeter', 'Infill > Perimeter > Loops', 'Perimeter > Infill > Loops', 'Perimeter > Loops > Infill'], 'Sequence of printing. The perimeter is the outer print edge, the loops are the insides of the walls, and the infill is the insides.');
-               c = configBase.SettingRow(left, "Force first layer sequence", 'force_first_layer_sequence', ['True', 'False'], 'This setting forces the order of the first layer to be \'Perimeter > Loops > Infill\'')
+               c = configBase.SettingRow(left, "Force first layer sequence", 'force_first_layer_sequence', True, 'This setting forces the order of the first layer to be \'Perimeter > Loops > Infill\'')
 
                configBase.TitleRow(left, "Infill")
                c = configBase.SettingRow(left, "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(left, "Solid infill top", 'solid_top', ['True', 'False'], 'Create a solid top surface, if set to false the top is filled with the fill percentage. Useful for cups/vases.')
+               c = configBase.SettingRow(left, "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.')
 
                configBase.TitleRow(left, "Joris")
-               c = configBase.SettingRow(left, "Joris the outer edge", 'joris', ['False', 'True'], '[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.')
+               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.')
 
                main.Fit()
                self.Fit()
index 4cf0a1694140f225192d881b8973518f1d50091d..39a914c0f8f5c831e8568fc29e9b517715b125dd 100644 (file)
@@ -107,20 +107,22 @@ class SettingRow():
                self.type = type
                
                self.label = wx.StaticText(panel, -1, label)
+               getSettingFunc = settings.getPreference
                if self.type == 'profile':
-                       if isinstance(defaultValue, types.StringTypes):
-                               self.ctrl = wx.TextCtrl(panel, -1, settings.getProfileSetting(configName, defaultValue))
-                       else:
-                               self.ctrl = wx.ComboBox(panel, -1, settings.getProfileSetting(configName, defaultValue[0]), choices=defaultValue, style=wx.CB_DROPDOWN|wx.CB_READONLY)
+                       getSettingFunc = settings.getProfileSetting
+               if isinstance(defaultValue, types.StringTypes):
+                       self.ctrl = wx.TextCtrl(panel, -1, getSettingFunc(configName, defaultValue))
+                       self.ctrl.Bind(wx.EVT_TEXT, self.OnSettingChange)
+               elif isinstance(defaultValue, types.BooleanType):
+                       self.ctrl = wx.CheckBox(panel, -1, style=wx.ALIGN_RIGHT)
+                       self.ctrl.SetValue(getSettingFunc(configName, defaultValue) == "True")
+                       self.ctrl.Bind(wx.EVT_CHECKBOX, self.OnSettingChange)
                else:
-                       if isinstance(defaultValue, types.StringTypes):
-                               self.ctrl = wx.TextCtrl(panel, -1, settings.getPreference(configName, defaultValue))
-                       else:
-                               self.ctrl = wx.ComboBox(panel, -1, settings.getPreference(configName, defaultValue[0]), choices=defaultValue, style=wx.CB_DROPDOWN|wx.CB_READONLY)
+                       self.ctrl = wx.ComboBox(panel, -1, getSettingFunc(configName, defaultValue[0]), choices=defaultValue, style=wx.CB_DROPDOWN|wx.CB_READONLY)
+                       self.ctrl.Bind(wx.EVT_TEXT, self.OnSettingChange)
                #self.helpButton = wx.Button(panel, -1, "?", style=wx.BU_EXACTFIT)
                #self.helpButton.SetToolTip(wx.ToolTip(help))
                
-               self.ctrl.Bind(wx.EVT_TEXT, self.OnSettingTextChange)
                self.ctrl.Bind(wx.EVT_ENTER_WINDOW, lambda e: panel.main.OnPopupDisplay(self))
                self.ctrl.Bind(wx.EVT_LEAVE_WINDOW, panel.main.OnPopupHide)
                
@@ -131,7 +133,7 @@ class SettingRow():
                #sizer.Add(helpButton, (x,y+2))
                sizer.SetRows(x+1)
 
-       def OnSettingTextChange(self, e):
+       def OnSettingChange(self, e):
                if self.type == 'profile':
                        settings.putProfileSetting(self.configName, self.GetValue())
                else:
@@ -158,10 +160,13 @@ class SettingRow():
                self.panel.main.UpdatePopup(self)
 
        def GetValue(self):
-               return self.ctrl.GetValue()
+               return str(self.ctrl.GetValue())
 
        def SetValue(self, value):
-               self.ctrl.SetValue(value)
+               if isinstance(self.ctrl, wx.CheckBox):
+                       self.ctrl.SetValue(str(value) == "True")
+               else:
+                       self.ctrl.SetValue(value)
 
 #Settings notify works as a validator, but instead of validating anything, it calls another function, which can use the value.
 class settingNotify():
@@ -176,4 +181,6 @@ class settingNotify():
                        self.func(f)
                        return validators.SUCCESS, ''
                except ValueError:
+                       self.func()
                        return validators.SUCCESS, ''
+
index 8c54188b1250ef04c331c968f945810cd4471266..0995b0685dcadf15b3146dc6556329d1ea87c8b9 100644 (file)
@@ -181,7 +181,7 @@ class UltimakerCheckupPage(InfoPage):
 \r
                wx.MessageBox('Please move the printer head to the center of the machine\nalso move the platform so it is not at the highest or lowest position,\nand make sure the machine is powered on.', 'Machine check', wx.OK | wx.ICON_INFORMATION)\r
                wx.CallAfter(self.AddProgressText, "Checking endstops")\r
-               if self.DoCommCommandWithTimeout('M119') != "ok x_min:l x_max:l y_min:l y_max:l z_min:l z_max:l"\r
+               if self.DoCommCommandWithTimeout('M119') != "ok x_min:l x_max:l y_min:l y_max:l z_min:l z_max:l":\r
                        wx.CallAfter(self.AddProgressText, "Error: There is a problem in your endstops!")\r
                        wx.CallAfter(self.AddProgressText, "Error: One of them seems to be pressed while it shouldn't")\r
                        return\r
index 64dbafc2e811de8c3f2911135b85b4313274ad93..af7150f682d5200ba178db5ee0b9f6b5323c32b2 100644 (file)
@@ -147,6 +147,23 @@ 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(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)
+               configBase.settingNotify(c, self.preview3d.updateModelTransform)
+
                # load and slice buttons.
                loadButton = wx.Button(self, -1, 'Load STL')
                sliceButton = wx.Button(self, -1, 'Slice to GCode')
index d9f96f3af128efd55be6cb0fb35d54579214ec4c..992d729b73688d203170da829899567fe55f450c 100644 (file)
@@ -110,9 +110,13 @@ class previewPanel(wx.Panel):
        \r
        def DoModelLoad(self):\r
                self.modelDirty = False\r
-               self.triangleMesh = fabmetheus_interpret.getCarving(self.modelFilename)\r
+               triangleMesh = fabmetheus_interpret.getCarving(self.modelFilename)\r
+               triangleMesh.origonalVertexes = list(triangleMesh.vertexes)\r
+               for i in xrange(0, len(triangleMesh.origonalVertexes)):\r
+                       triangleMesh.origonalVertexes[i] = triangleMesh.origonalVertexes[i].copy()\r
+               self.triangleMesh = triangleMesh\r
                self.gcode = None\r
-               self.moveModel()\r
+               self.updateModelTransform()\r
                wx.CallAfter(self.updateToolbar)\r
                wx.CallAfter(self.glCanvas.Refresh)\r
        \r
@@ -145,6 +149,38 @@ class previewPanel(wx.Panel):
                        self.glCanvas.renderTransparent = False\r
                self.glCanvas.Refresh()\r
        \r
+       def updateModelTransform(self, f=0):\r
+               if self.triangleMesh == None:\r
+                       return\r
+               for face in self.triangleMesh.faces:\r
+                       face.normal = None\r
+               scale = 1.0\r
+               rotate = 0.0\r
+               try:\r
+                       scale = float(settings.getProfileSetting('model_scale', '1.0'))\r
+                       rotate = float(settings.getProfileSetting('model_rotate_base', '0.0')) / 180 * math.pi\r
+               except:\r
+                       pass\r
+               scaleX = scale\r
+               scaleY = scale\r
+               scaleZ = scale\r
+               if settings.getProfileSetting('flip_x') == 'True':\r
+                       scaleX = -scaleX\r
+               if settings.getProfileSetting('flip_y') == 'True':\r
+                       scaleY = -scaleY\r
+               if settings.getProfileSetting('flip_z') == 'True':\r
+                       scaleZ = -scaleZ\r
+               mat00 = math.cos(rotate) * scaleX\r
+               mat01 =-math.sin(rotate) * scaleY\r
+               mat10 = math.sin(rotate) * scaleX\r
+               mat11 = math.cos(rotate) * scaleY\r
+               \r
+               for i in xrange(0, len(self.triangleMesh.origonalVertexes)):\r
+                       self.triangleMesh.vertexes[i].x = self.triangleMesh.origonalVertexes[i].x * mat00 + self.triangleMesh.origonalVertexes[i].y * mat01\r
+                       self.triangleMesh.vertexes[i].y = self.triangleMesh.origonalVertexes[i].x * mat10 + self.triangleMesh.origonalVertexes[i].y * mat11\r
+                       self.triangleMesh.vertexes[i].z = self.triangleMesh.origonalVertexes[i].z * scaleZ\r
+               self.moveModel()\r
+       \r
        def moveModel(self):\r
                if self.triangleMesh == None:\r
                        return\r
@@ -159,6 +195,7 @@ class previewPanel(wx.Panel):
                        v.y += self.machineCenter.y\r
                self.triangleMesh.getMinimumZ()\r
                self.modelDirty = True\r
+               self.glCanvas.Refresh()\r
 \r
 class PreviewGLCanvas(glcanvas.GLCanvas):\r
        def __init__(self, parent):\r
@@ -343,13 +380,17 @@ class PreviewGLCanvas(glcanvas.GLCanvas):
                                        v1 = self.parent.triangleMesh.vertexes[face.vertexIndexes[0]]\r
                                        v2 = self.parent.triangleMesh.vertexes[face.vertexIndexes[1]]\r
                                        v3 = self.parent.triangleMesh.vertexes[face.vertexIndexes[2]]\r
-                                       if not hasattr(face, 'normal'):\r
+                                       if face.normal == None:\r
                                                face.normal = (v2 - v1).cross(v3 - v1)\r
                                                face.normal.normalize()\r
                                        glNormal3f(face.normal.x, face.normal.y, face.normal.z)\r
                                        glVertex3f(v1.x, v1.y, v1.z)\r
                                        glVertex3f(v2.x, v2.y, v2.z)\r
                                        glVertex3f(v3.x, v3.y, v3.z)\r
+                                       glNormal3f(-face.normal.x, -face.normal.y, -face.normal.z)\r
+                                       glVertex3f(v1.x, v1.y, v1.z)\r
+                                       glVertex3f(v3.x, v3.y, v3.z)\r
+                                       glVertex3f(v2.x, v2.y, v2.z)\r
                                glEnd()\r
                                glEndList()\r
                        if self.renderTransparent:\r
@@ -432,6 +473,7 @@ class PreviewGLCanvas(glcanvas.GLCanvas):
                glEnable(GL_LIGHTING)\r
                glEnable(GL_LIGHT0)\r
                glEnable(GL_DEPTH_TEST)\r
+               glEnable(GL_CULL_FACE)\r
                glDisable(GL_BLEND)\r
 \r
                glClearColor(0.0, 0.0, 0.0, 1.0)\r
index 7114caadd7acfeae64f40ea6759c5874e9c61825..cb2a5ec30ff28aba627b679f01f75de9b4f1417e 100644 (file)
@@ -107,10 +107,12 @@ from fabmetheus_utilities import settings
 from fabmetheus_utilities import svg_writer
 from skeinforge_application.skeinforge_utilities import skeinforge_polyfile
 from skeinforge_application.skeinforge_utilities import skeinforge_profile
+from fabmetheus_utilities.vector3 import Vector3
 import math
 import os
 import sys
 import time
+import math
 
 
 __author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
@@ -178,6 +180,13 @@ class CarveRepository:
                settings.LabelSeparator().getFromRepository(self)
                self.executeTitle = 'Carve'
 
+               self.flipX = settings.BooleanSetting().getFromValue('FlipX', self, False)
+               self.flipY = settings.BooleanSetting().getFromValue('FlipY', self, False)
+               self.flipZ = settings.BooleanSetting().getFromValue('FlipZ', self, False)
+               self.scale = settings.FloatSpin().getFromValue( 0.1, 'Scale', self, 10.0, 1.0 )
+               self.rotate = settings.FloatSpin().getFromValue( -180.0, 'Rotate', self, 180.0, 0.0 )
+
+
        def execute(self):
                "Carve button has been clicked."
                fileNames = skeinforge_polyfile.getFileOrDirectoryTypes(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled)
@@ -189,6 +198,29 @@ class CarveSkein:
        "A class to carve a carving."
        def getCarvedSVG(self, carving, fileName, repository):
                "Parse gnu triangulated surface text and store the carved gcode."
+
+               scale = repository.scale.value
+               rotate = repository.rotate.value / 180 * math.pi
+               scaleX = scale
+               scaleY = scale
+               scaleZ = scale
+               if repository.flipX.value == 'True':
+                       scaleX = -scaleX
+               if repository.flipY.value == 'True':
+                       scaleY = -scaleY
+               if repository.flipZ.value == 'True':
+                       scaleZ = -scaleZ
+               mat00 = math.cos(rotate) * scaleX
+               mat01 =-math.sin(rotate) * scaleY
+               mat10 = math.sin(rotate) * scaleX
+               mat11 = math.cos(rotate) * scaleY
+               
+               for i in xrange(0, len(carving.vertexes)):
+                       carving.vertexes[i] = Vector3(
+                               carving.vertexes[i].x * mat00 + carving.vertexes[i].y * mat01,
+                               carving.vertexes[i].x * mat10 + carving.vertexes[i].y * mat11,
+                               carving.vertexes[i].z * scaleZ)
+
                layerHeight = repository.layerHeight.value
                edgeWidth = repository.edgeWidth.value
                carving.setCarveLayerHeight(layerHeight)