chiark / gitweb /
Add partial working rotate/scale.
authordaid303 <daid303@gmail.com>
Thu, 4 Apr 2013 07:52:24 +0000 (09:52 +0200)
committerdaid303 <daid303@gmail.com>
Thu, 4 Apr 2013 07:52:24 +0000 (09:52 +0200)
Cura/gui/sceneView.py
Cura/gui/util/openglGui.py
Cura/gui/util/previewTools.py
Cura/util/mesh.py
Cura/util/profile.py
Cura/util/removableStorage.py

index fcce88482f533771c4fe3e93cf7b15f53dfbc232..d57e2257559ec8f2b55c9acc4d28abe89d5ae674 100644 (file)
@@ -20,6 +20,7 @@ from Cura.util import resources
 from Cura.util import sliceEngine
 from Cura.util import machineCom
 from Cura.util import removableStorage
+from Cura.gui.util import previewTools
 from Cura.gui.util import opengl
 from Cura.gui.util import openglGui
 
@@ -65,15 +66,57 @@ class SceneView(openglGui.glGuiPanel):
                self._platformMesh._drawOffset = numpy.array([0,0,0.5], numpy.float32)
                self._isSimpleMode = True
 
+               self._viewport = None
+               self._modelMatrix = None
+               self._projMatrix = None
+               self.tempMatrix = None
+
                self.openFileButton      = openglGui.glButton(self, 4, 'Load', (0,0), self.ShowLoadModel)
                self.printButton         = openglGui.glButton(self, 6, 'Print', (1,0), self.ShowPrintWindow)
                self.printButton.setDisabled(True)
 
+               group = []
+               self.rotateToolButton = openglGui.glRadioButton(self, 8, 'Rotate', (0,-1), group, self.OnToolSelect)
+               self.scaleToolButton  = openglGui.glRadioButton(self, 9, 'Scale', (1,-1), group, self.OnToolSelect)
+               self.mirrorToolButton  = openglGui.glRadioButton(self, 10, 'Mirror', (2,-1), group, self.OnToolSelect)
+
+               self.resetRotationButton = openglGui.glButton(self, 12, 'Reset', (0,-2), self.OnRotateReset)
+               self.layFlatButton       = openglGui.glButton(self, 16, 'Lay flat', (0,-3), self.OnLayFlat)
+
+               self.resetScaleButton    = openglGui.glButton(self, 13, 'Reset', (1,-2), self.OnScaleReset)
+               self.scaleMaxButton      = openglGui.glButton(self, 17, 'To max', (1,-3), self.OnScaleMax)
+
+               self.mirrorXButton       = openglGui.glButton(self, 14, 'Mirror X', (2,-2), lambda button: self.OnMirror(0))
+               self.mirrorYButton       = openglGui.glButton(self, 18, 'Mirror Y', (2,-3), lambda button: self.OnMirror(1))
+               self.mirrorZButton       = openglGui.glButton(self, 22, 'Mirror Z', (2,-4), lambda button: self.OnMirror(2))
+
+               self.rotateToolButton.setExpandArrow(True)
+               self.scaleToolButton.setExpandArrow(True)
+               self.mirrorToolButton.setExpandArrow(True)
+
+               self.scaleForm = openglGui.glFrame(self, (2, -2))
+               openglGui.glGuiLayoutGrid(self.scaleForm)
+               openglGui.glLabel(self.scaleForm, 'Scale X', (0,0))
+               self.scaleXctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,0), lambda value: self.OnScaleEntry(value, 0))
+               openglGui.glLabel(self.scaleForm, 'Scale Y', (0,1))
+               self.scaleYctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,1), lambda value: self.OnScaleEntry(value, 1))
+               openglGui.glLabel(self.scaleForm, 'Scale Z', (0,2))
+               self.scaleZctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,2), lambda value: self.OnScaleEntry(value, 2))
+               openglGui.glLabel(self.scaleForm, 'Size X (mm)', (0,4))
+               self.scaleXmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,4), lambda value: self.OnScaleEntryMM(value, 0))
+               openglGui.glLabel(self.scaleForm, 'Size Y (mm)', (0,5))
+               self.scaleYmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,5), lambda value: self.OnScaleEntryMM(value, 1))
+               openglGui.glLabel(self.scaleForm, 'Size Z (mm)', (0,6))
+               self.scaleZmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,6), lambda value: self.OnScaleEntryMM(value, 2))
+               openglGui.glLabel(self.scaleForm, 'Uniform scale', (0,8))
+               self.scaleUniform = openglGui.glCheckbox(self.scaleForm, True, (1,8), None)
+
                self._slicer = sliceEngine.Slicer(self._updateSliceProgress)
                self._sceneUpdateTimer = wx.Timer(self)
                self.Bind(wx.EVT_TIMER, lambda e : self._slicer.runSlicer(self._scene), self._sceneUpdateTimer)
                self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel)
 
+               self.OnToolSelect(0)
                self.updateProfileToControls()
                wx.EVT_IDLE(self, self.OnIdle)
 
@@ -99,7 +142,12 @@ class SceneView(openglGui.glGuiPanel):
                        elif len(removableStorage.getPossibleSDcardDrives()) > 0:
                                drives = removableStorage.getPossibleSDcardDrives()
                                if len(drives) > 1:
-                                       pass
+                                       drive = drives[0]
+                               else:
+                                       drive = drives[0]
+                               filename = os.path.basename(profile.getPreference('lastFile'))
+                               filename = filename[0:filename.rfind('.')] + '.gcode'
+                               shutil.copy(self._slicer.getGCodeFilename(), drive[1] + filename)
                        else:
                                defPath = profile.getPreference('lastFile')
                                defPath = defPath[0:defPath.rfind('.')] + '.gcode'
@@ -123,6 +171,49 @@ class SceneView(openglGui.glGuiPanel):
                                self.Refresh()
                                return
 
+       def OnToolSelect(self, button):
+               if self.rotateToolButton.getSelected():
+                       self.tool = previewTools.toolRotate(self)
+               elif self.scaleToolButton.getSelected():
+                       self.tool = previewTools.toolScale(self)
+               elif self.mirrorToolButton.getSelected():
+                       self.tool = previewTools.toolNone(self)
+               else:
+                       self.tool = previewTools.toolNone(self)
+               self.resetRotationButton.setHidden(not self.rotateToolButton.getSelected())
+               self.layFlatButton.setHidden(not self.rotateToolButton.getSelected())
+               self.resetScaleButton.setHidden(not self.scaleToolButton.getSelected())
+               self.scaleMaxButton.setHidden(not self.scaleToolButton.getSelected())
+               self.scaleForm.setHidden(not self.scaleToolButton.getSelected())
+               self.mirrorXButton.setHidden(not self.mirrorToolButton.getSelected())
+               self.mirrorYButton.setHidden(not self.mirrorToolButton.getSelected())
+               self.mirrorZButton.setHidden(not self.mirrorToolButton.getSelected())
+
+       def OnRotateReset(self, button):
+               if self._selectedObj is None:
+                       return
+               pass
+
+       def OnLayFlat(self, button):
+               if self._selectedObj is None:
+                       return
+               pass
+
+       def OnScaleReset(self, button):
+               if self._selectedObj is None:
+                       return
+               pass
+
+       def OnScaleMax(self, button):
+               if self._selectedObj is None:
+                       return
+               pass
+
+       def OnMirror(self, axis):
+               if self._selectedObj is None:
+                       return
+               self._selectedObj.mirror(axis)
+
        def sceneUpdated(self):
                self._sceneUpdateTimer.Start(1, True)
                self._slicer.abortSlicer()
@@ -212,6 +303,11 @@ class SceneView(openglGui.glGuiPanel):
                        self._mouseState = 'doubleClick'
                else:
                        self._mouseState = 'dragOrClick'
+               p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
+               p0 -= self.getObjectCenterPos() - self._viewTarget
+               p1 -= self.getObjectCenterPos() - self._viewTarget
+               if self.tool.OnDragStart(p0, p1):
+                       self._mouseState = 'tool'
                if self._mouseState == 'dragOrClick':
                        if e.GetButton() == 1:
                                if self._focusObj is not None:
@@ -234,15 +330,26 @@ class SceneView(openglGui.glGuiPanel):
                                #self.PopupMenu(menu)
                                #menu.Destroy()
                                pass
-               if self._mouseState == 'dragObject' and self._selectedObj is not None:
+               elif self._mouseState == 'dragObject' and self._selectedObj is not None:
                        self._scene.pushFree()
                        self.sceneUpdated()
+               elif self._mouseState == 'tool':
+                       if self.tempMatrix is not None and self._selectedObj is not None:
+                               self._selectedObj.applyMatrix(self.tempMatrix)
+                       self.tempMatrix = None
+                       self.tool.OnDragEnd()
+                       self.sceneUpdated()
                self._mouseState = None
 
        def OnMouseMotion(self,e):
+               p0, p1 = self.getMouseRay(e.GetX(), e.GetY())
+               p0 -= self.getObjectCenterPos() - self._viewTarget
+               p1 -= self.getObjectCenterPos() - self._viewTarget
+
                if e.Dragging() and self._mouseState is not None:
-                       self._mouseState = 'drag'
-                       if not e.LeftIsDown() and e.RightIsDown():
+                       if self._mouseState == 'tool':
+                               self.tool.OnDrag(p0, p1)
+                       elif not e.LeftIsDown() and e.RightIsDown():
                                self._yaw += e.GetX() - self._mouseX
                                self._pitch -= e.GetY() - self._mouseY
                                if self._pitch > 170:
@@ -258,10 +365,8 @@ class SceneView(openglGui.glGuiPanel):
                        elif e.LeftIsDown() and self._selectedObj is not None and self._selectedObj == self._mouseClickFocus and not self._isSimpleMode:
                                self._mouseState = 'dragObject'
                                z = max(0, self._mouseClick3DPos[2])
-                               p0 = opengl.unproject(self._mouseX, self.viewport[1] + self.viewport[3] - self._mouseY, 0, self.modelMatrix, self.projMatrix, self.viewport)
-                               p1 = opengl.unproject(self._mouseX, self.viewport[1] + self.viewport[3] - self._mouseY, 1, self.modelMatrix, self.projMatrix, self.viewport)
-                               p2 = opengl.unproject(e.GetX(), self.viewport[1] + self.viewport[3] - e.GetY(), 0, self.modelMatrix, self.projMatrix, self.viewport)
-                               p3 = opengl.unproject(e.GetX(), self.viewport[1] + self.viewport[3] - e.GetY(), 1, self.modelMatrix, self.projMatrix, self.viewport)
+                               p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
+                               p2, p3 = self.getMouseRay(e.GetX(), e.GetY())
                                p0[2] -= z
                                p1[2] -= z
                                p2[2] -= z
@@ -270,6 +375,8 @@ class SceneView(openglGui.glGuiPanel):
                                cursorZ1 = p2 - (p3 - p2) * (p2[2] / (p3[2] - p2[2]))
                                diff = cursorZ1 - cursorZ0
                                self._selectedObj.setPosition(self._selectedObj.getPosition() + diff[0:2])
+               if not e.Dragging() or self._mouseState != 'tool':
+                       self.tool.OnMouseMove(p0, p1)
 
                self._mouseX = e.GetX()
                self._mouseY = e.GetY()
@@ -282,6 +389,15 @@ class SceneView(openglGui.glGuiPanel):
                        self._zoom = numpy.max(self._machineSize) * 3
                self.Refresh()
 
+       def getMouseRay(self, x, y):
+               if self._viewport is None:
+                       return numpy.array([0,0,0],numpy.float32), numpy.array([0,0,1],numpy.float32)
+               p0 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 0, self._modelMatrix, self._projMatrix, self._viewport)
+               p1 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 1, self._modelMatrix, self._projMatrix, self._viewport)
+               p0 -= self._viewTarget
+               p1 -= self._viewTarget
+               return p0, p1
+
        def _init3DView(self):
                # set viewing projection
                size = self.GetSize()
@@ -387,9 +503,9 @@ void main(void)
                glRotate(self._yaw, 0,0,1)
                glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
 
-               self.viewport = glGetIntegerv(GL_VIEWPORT)
-               self.modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
-               self.projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
+               self._viewport = glGetIntegerv(GL_VIEWPORT)
+               self._modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
+               self._projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
 
                glClearColor(1,1,1,1)
                glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
@@ -406,7 +522,8 @@ void main(void)
                        else:
                                self._focusObj = None
                        f = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0]
-                       self._mouse3Dpos = opengl.unproject(self._mouseX, self.viewport[1] + self.viewport[3] - self._mouseY, f, self.modelMatrix, self.projMatrix, self.viewport)
+                       self._mouse3Dpos = opengl.unproject(self._mouseX, self._viewport[1] + self._viewport[3] - self._mouseY, f, self._modelMatrix, self._projMatrix, self._viewport)
+                       self._mouse3Dpos -= self._viewTarget
 
                self._init3DView()
                glTranslate(0,0,-self._zoom)
@@ -429,11 +546,10 @@ void main(void)
                                col = [0.5,0.5,0.5,0.8]
                        glDisable(GL_STENCIL_TEST)
                        if self._selectedObj == obj:
-                               col = map(lambda n: n * 1.5, col)
                                glEnable(GL_STENCIL_TEST)
-                       elif self._focusObj == obj:
+                       if self._focusObj == obj:
                                col = map(lambda n: n * 1.2, col)
-                       elif self._focusObj is not None or  self._selectedObj is not None:
+                       elif self._focusObj is not None or self._selectedObj is not None and obj != self._selectedObj:
                                col = map(lambda n: n * 0.8, col)
                        glColor4f(col[0], col[1], col[2], col[3])
                        self._renderObject(obj)
@@ -470,11 +586,27 @@ void main(void)
                        glDisable(GL_CULL_FACE)
                        glEnable(GL_DEPTH_TEST)
 
+               if self._selectedObj is not None:
+                       glPushMatrix()
+                       pos = self.getObjectCenterPos()
+                       glTranslate(pos[0], pos[1], pos[2])
+                       self.tool.OnDraw()
+                       glPopMatrix()
+
        def _renderObject(self, obj):
                glPushMatrix()
-               glTranslate(obj.getPosition()[0], obj.getPosition()[1], 0)
+               glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2)
+
+               if self.tempMatrix is not None and obj == self._selectedObj:
+                       tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
+                       glMultMatrixf(tempMatrix)
+
                offset = obj.getDrawOffset()
-               glTranslate(-offset[0], -offset[1], -offset[2])
+               glTranslate(-offset[0], -offset[1], -offset[2] - obj.getSize()[2] / 2)
+
+               tempMatrix = opengl.convert3x3MatrixTo4x4(obj.getMatrix())
+               glMultMatrixf(tempMatrix)
+
                for m in obj._meshList:
                        if m.vbo is None:
                                m.vbo = opengl.GLVBO(m.vertexes, m.normal)
@@ -540,6 +672,28 @@ void main(void)
                glDisable(GL_BLEND)
                glDisable(GL_CULL_FACE)
 
+       def getObjectCenterPos(self):
+               if self._selectedObj is None:
+                       return [0.0, 0.0, 0.0]
+               pos = self._selectedObj.getPosition()
+               size = self._selectedObj.getSize()
+               return [pos[0], pos[1], size[2]/2]
+
+       def getObjectBoundaryCircle(self):
+               if self._selectedObj is None:
+                       return 0.0
+               return self._selectedObj.getBoundaryCircle()
+
+       def getObjectSize(self):
+               if self._selectedObj is None:
+                       return [0.0, 0.0, 0.0]
+               return self._selectedObj.getSize()
+
+       def getObjectMatrix(self):
+               if self._selectedObj is None:
+                       return numpy.matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
+               return self._selectedObj.getMatrix()
+
 class shaderEditor(wx.Dialog):
        def __init__(self, parent, callback, v, f):
                super(shaderEditor, self).__init__(parent, title="Shader editor", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
index 9a945f027905e5aa2e3566330ba77899db235a85..a6b64a44cc9a5e347639cc217d171584d47fff4b 100644 (file)
@@ -458,7 +458,7 @@ class glRadioButton(glButton):
        def setSelected(self, value):
                self._selected = value
 
-       def _onRadioSelect(self):
+       def _onRadioSelect(self, button):
                self._base._focus = None
                for ctrl in self._group:
                        if ctrl != self:
@@ -467,7 +467,7 @@ class glRadioButton(glButton):
                        self.setSelected(False)
                else:
                        self.setSelected(True)
-               self._radioCallback()
+               self._radioCallback(button)
 
 class glComboButton(glButton):
        def __init__(self, parent, tooltip, imageIDs, tooltips, pos, callback):
index 16cc6226e0ee78feb9ce6aea13f09eb6d09c717b..65d5699ec213be2df9cf6907bcd027796c1d52d0 100644 (file)
@@ -149,6 +149,7 @@ class toolRotate(object):
                        else:
                                self.dragPlane = 'YZ'
                                self.dragStartAngle = math.atan2(cursorX0[2], cursorX0[1]) * 180 / math.pi
+                       self.dragEndAngle = self.dragStartAngle
                        return True
                return False
 
@@ -315,7 +316,7 @@ class toolScale(object):
                return t
 
        def _nodeSize(self):
-               return float(self.parent.zoom) / float(self.parent.GetSize().GetWidth()) * 6.0
+               return float(self.parent._zoom) / float(self.parent.GetSize().GetWidth()) * 6.0
 
        def OnMouseMove(self, p0, p1):
                self.node = self._traceNodes(p0, p1)
@@ -398,11 +399,11 @@ class toolScale(object):
                        radius *= self.scale
                glPushMatrix()
                glTranslate(0,0,size[2]/2 + 5)
-               glRotate(-self.parent.yaw, 0,0,1)
-               if self.parent.pitch < 80:
+               glRotate(-self.parent._yaw, 0,0,1)
+               if self.parent._pitch < 80:
                        glTranslate(0, radius + 5,0)
-               elif self.parent.pitch < 100:
-                       glTranslate(0, (radius + 5) * (90 - self.parent.pitch) / 10,0)
+               elif self.parent._pitch < 100:
+                       glTranslate(0, (radius + 5) * (90 - self.parent._pitch) / 10,0)
                else:
                        glTranslate(0,-(radius + 5),0)
                if self.parent.tempMatrix is not None:
index 0ed57db21fb055a2ed82cfebe66cce5f4c5980b7..a4922869675913960a7f5a08a513814866bf8442 100644 (file)
@@ -27,6 +27,10 @@ class printableObject(object):
                        m._calculateNormals()
                self.processMatrix()
 
+       def applyMatrix(self, m):
+               self._matrix *= m
+               self.processMatrix()
+
        def processMatrix(self):
                self._transformedMin = numpy.array([999999999999,999999999999,999999999999], numpy.float64)
                self._transformedMax = numpy.array([-999999999999,-999999999999,-999999999999], numpy.float64)
@@ -55,6 +59,8 @@ class printableObject(object):
                return self._position
        def setPosition(self, newPos):
                self._position = newPos
+       def getMatrix(self):
+               return self._matrix
 
        def getMaximum(self):
                return self._transformedMax
@@ -67,6 +73,11 @@ class printableObject(object):
        def getBoundaryCircle(self):
                return self._boundaryCircleSize
 
+       def mirror(self, axis):
+               matrix = [[1,0,0], [0, 1, 0], [0, 0, 1]]
+               matrix[axis][axis] = -1
+               self.applyMatrix(numpy.matrix(matrix, numpy.float64))
+
 class mesh(object):
        def __init__(self):
                self.vertexes = None
index 877976d84963f9a67695cf290862bb5bf9b5bbbe..cf62ee1026ef1852aa1b9b0bcde6805d548d7b2b 100644 (file)
@@ -138,7 +138,7 @@ setting('retraction_amount',         4.5, float, 'advanced', 'Retraction').setRa
 #setting('retraction_extra',          0.0, float, 'advanced', 'Retraction').setRange(0).setLabel('Extra length on start (mm)', 'Extra extrusion amount when restarting after a retraction, to better "Prime" your extruder after retraction.')
 setting('bottom_thickness',          0.3, float, 'advanced', 'Quality').setRange(0).setLabel('Initial layer thickness (mm)', 'Layer thickness of the bottom layer. A thicker bottom layer makes sticking to the bed easier. Set to 0.0 to have the bottom layer thickness the same as the other layers.')
 setting('object_sink',               0.0, float, 'advanced', 'Quality').setLabel('Cut off object bottom (mm)', 'Sinks the object into the platform, this can be used for objects that do not have a flat bottom and thus create a too small first layer.')
-setting('enable_skin',             False, bool,  'advanced', 'Quality').setLabel('Duplicate outlines', 'Skin prints the outer lines of the prints twice, each time with half the thickness. This gives the illusion of a higher print quality.')
+#setting('enable_skin',             False, bool,  'advanced', 'Quality').setLabel('Duplicate outlines', 'Skin prints the outer lines of the prints twice, each time with half the thickness. This gives the illusion of a higher print quality.')
 setting('travel_speed',            150.0, float, 'advanced', 'Speed').setRange(0.1).setLabel('Travel speed (mm/s)', 'Speed at which travel moves are done, a high quality build Ultimaker can reach speeds of 250mm/s. But some machines might miss steps then.')
 setting('bottom_layer_speed',         20, float, 'advanced', 'Speed').setRange(0.1).setLabel('Bottom layer speed (mm/s)', 'Print speed for the bottom layer, you want to print the first layer slower so it sticks better to the printer bed.')
 #setting('cool_min_layer_time',         5, float, 'advanced', 'Cool').setRange(0).setLabel('Minimal layer time (sec)', 'Minimum time spend in a layer, gives the layer time to cool down before the next layer is put on top. If the layer will be placed down too fast the printer will slow down to make sure it has spend at least this amount of seconds printing this layer.')
index f1498586f72e2bb12290a39585275ff390147390..42df48e10c041815baae138df1aeb3016b2669a0 100644 (file)
@@ -73,10 +73,10 @@ def getPossibleSDcardDrives():
                                for vol in dev['volumes']:
                                        if 'mount_point' in vol:
                                                volume = vol['mount_point']
-                                               drives.append((os.path.basename(volume), os.path.basename(volume), volume))
+                                               drives.append((os.path.basename(volume), volume, os.path.basename(volume)))
        else:
                for volume in glob.glob('/media/*'):
-                       drives.append((os.path.basename(volume), os.path.basename(volume), volume))
+                       drives.append((os.path.basename(volume), volume, os.path.basename(volume)))
        return drives
 
 if __name__ == '__main__':