From: daid303 Date: Thu, 4 Apr 2013 07:52:24 +0000 (+0200) Subject: Add partial working rotate/scale. X-Git-Tag: 13.05~126 X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=commitdiff_plain;h=543eaa00daebf8a90fe71fecc191b45030eec329;p=cura.git Add partial working rotate/scale. --- diff --git a/Cura/gui/sceneView.py b/Cura/gui/sceneView.py index fcce8848..d57e2257 100644 --- a/Cura/gui/sceneView.py +++ b/Cura/gui/sceneView.py @@ -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) diff --git a/Cura/gui/util/openglGui.py b/Cura/gui/util/openglGui.py index 9a945f02..a6b64a44 100644 --- a/Cura/gui/util/openglGui.py +++ b/Cura/gui/util/openglGui.py @@ -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): diff --git a/Cura/gui/util/previewTools.py b/Cura/gui/util/previewTools.py index 16cc6226..65d5699e 100644 --- a/Cura/gui/util/previewTools.py +++ b/Cura/gui/util/previewTools.py @@ -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: diff --git a/Cura/util/mesh.py b/Cura/util/mesh.py index 0ed57db2..a4922869 100644 --- a/Cura/util/mesh.py +++ b/Cura/util/mesh.py @@ -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 diff --git a/Cura/util/profile.py b/Cura/util/profile.py index 877976d8..cf62ee10 100644 --- a/Cura/util/profile.py +++ b/Cura/util/profile.py @@ -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.') diff --git a/Cura/util/removableStorage.py b/Cura/util/removableStorage.py index f1498586..42df48e1 100644 --- a/Cura/util/removableStorage.py +++ b/Cura/util/removableStorage.py @@ -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__':