From 946f04012f22fa08b167cd4f6ac9c9e7076b2f0f Mon Sep 17 00:00:00 2001 From: daid303 Date: Wed, 16 Jan 2013 11:13:59 +0100 Subject: [PATCH] Update on rotation tools, this breaks a lot of functionality, but is really needed. --- Cura/gui/preview3d.py | 450 ++++++++++++++++++++++------------------ Cura/gui/simpleMode.py | 7 - Cura/gui/util/opengl.py | 4 +- Cura/util/mesh.py | 72 ++----- Cura/util/profile.py | 34 +-- 5 files changed, 276 insertions(+), 291 deletions(-) diff --git a/Cura/gui/preview3d.py b/Cura/gui/preview3d.py index fecda902..1dbb02ef 100644 --- a/Cura/gui/preview3d.py +++ b/Cura/gui/preview3d.py @@ -36,6 +36,85 @@ class previewObject(): self.displayList = None self.dirty = False +class toolRotate(object): + def __init__(self, parent): + self.parent = parent + self.rotateRingDist = 1.5 + + def _ProjectToPlanes(self, p0, p1): + pp0 = p0 - [0,0,self.parent.getObjectSize()[2]/2] + pp1 = p1 - [0,0,self.parent.getObjectSize()[2]/2] + cursorX0 = pp0 - (pp1 - pp0) * (pp0[0] / (pp1[0] - pp0[0])) + cursorY0 = pp0 - (pp1 - pp0) * (pp0[1] / (pp1[1] - pp0[1])) + cursorZ0 = pp0 - (pp1 - pp0) * (pp0[2] / (pp1[2] - pp0[2])) + cursorYZ = math.sqrt((cursorX0[1] * cursorX0[1]) + (cursorX0[2] * cursorX0[2])) + cursorXZ = math.sqrt((cursorY0[0] * cursorY0[0]) + (cursorY0[2] * cursorY0[2])) + cursorXY = math.sqrt((cursorZ0[0] * cursorZ0[0]) + (cursorZ0[1] * cursorZ0[1])) + return cursorX0, cursorY0, cursorZ0, cursorYZ, cursorXZ, cursorXY + + def OnMouseMove(self, p0, p1): + radius = self.parent.getObjectBoundaryCircle() + cursorX0, cursorY0, cursorZ0, cursorYZ, cursorXZ, cursorXY = self._ProjectToPlanes(p0, p1) + if radius * (self.rotateRingDist - 0.1) <= cursorXY <= radius * (self.rotateRingDist + 0.1) or radius * (self.rotateRingDist - 0.1) <= cursorYZ <= radius * (self.rotateRingDist + 0.1) or radius * (self.rotateRingDist - 0.1) <= cursorXZ <= radius * (self.rotateRingDist + 0.1): + self.parent.SetCursor(wx.StockCursor(wx.CURSOR_SIZING)) + else: + self.parent.SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT)) + + def OnDragStart(self, p0, p1): + radius = self.parent.getObjectBoundaryCircle() + cursorX0, cursorY0, cursorZ0, cursorYZ, cursorXZ, cursorXY = self._ProjectToPlanes(p0, p1) + if radius * (self.rotateRingDist - 0.1) <= cursorXY <= radius * (self.rotateRingDist + 0.1) or radius * (self.rotateRingDist - 0.1) <= cursorYZ <= radius * (self.rotateRingDist + 0.1) or radius * (self.rotateRingDist - 0.1) <= cursorXZ <= radius * (self.rotateRingDist + 0.1): + if radius * (self.rotateRingDist - 0.1) <= cursorXY <= radius * (self.rotateRingDist + 0.1): + self.dragPlane = 'XY' + self.dragStartAngle = math.atan2(cursorZ0[1], cursorZ0[0]) * 180 / math.pi + elif radius * (self.rotateRingDist - 0.1) <= cursorXZ <= radius * (self.rotateRingDist + 0.1): + self.dragPlane = 'XZ' + self.dragStartAngle = math.atan2(cursorY0[2], cursorY0[0]) * 180 / math.pi + else: + self.dragPlane = 'YZ' + self.dragStartAngle = math.atan2(cursorX0[2], cursorX0[1]) * 180 / math.pi + return True + return False + + def OnDrag(self, p0, p1): + cursorX0, cursorY0, cursorZ0, cursorYZ, cursorXZ, cursorXY = self._ProjectToPlanes(p0, p1) + if self.dragPlane == 'XY': + angle = math.atan2(cursorZ0[1], cursorZ0[0]) * 180 / math.pi + elif self.dragPlane == 'XZ': + angle = math.atan2(cursorY0[2], cursorY0[0]) * 180 / math.pi + else: + angle = math.atan2(cursorX0[2], cursorX0[1]) * 180 / math.pi + diff = angle - self.dragStartAngle + diff = round(diff / 5) * 5 + rad = diff / 180.0 * math.pi + if self.dragPlane == 'XY': + self.parent.tempMatrix = numpy.matrix([[math.cos(rad), math.sin(rad), 0], [-math.sin(rad), math.cos(rad), 0], [0,0,1]], numpy.float64) + elif self.dragPlane == 'XZ': + self.parent.tempMatrix = numpy.matrix([[math.cos(rad), 0, math.sin(rad)], [0,1,0], [-math.sin(rad), 0, math.cos(rad)]], numpy.float64) + else: + self.parent.tempMatrix = numpy.matrix([[1,0,0], [0, math.cos(rad), math.sin(rad)], [0, -math.sin(rad), math.cos(rad)]], numpy.float64) + + def OnDraw(self): + glDisable(GL_LIGHTING) + glDisable(GL_BLEND) + radius = self.parent.getObjectBoundaryCircle() + glScalef(radius, radius, radius) + glColor4ub(255,0,0,255) + glBegin(GL_LINE_LOOP) + for i in xrange(0, 64): + glVertex3f(self.rotateRingDist * math.cos(i/32.0*math.pi), self.rotateRingDist * math.sin(i/32.0*math.pi),0) + glEnd() + glColor4ub(0,255,0,255) + glBegin(GL_LINE_LOOP) + for i in xrange(0, 64): + glVertex3f(0, self.rotateRingDist * math.cos(i/32.0*math.pi), self.rotateRingDist * math.sin(i/32.0*math.pi)) + glEnd() + glColor4ub(0,0,255,255) + glBegin(GL_LINE_LOOP) + for i in xrange(0, 64): + glVertex3f(self.rotateRingDist * math.cos(i/32.0*math.pi), 0, self.rotateRingDist * math.sin(i/32.0*math.pi)) + glEnd() + class previewPanel(wx.Panel): def __init__(self, parent): super(previewPanel, self).__init__(parent,-1) @@ -48,7 +127,7 @@ class previewPanel(wx.Panel): self.gcode = None self.objectsMinV = None self.objectsMaxV = None - self.objectsBounderyCircleSize = None + self.objectsBoundaryCircleSize = None self.loadThread = None self.machineSize = util3d.Vector3(profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')) self.machineCenter = util3d.Vector3(self.machineSize.x / 2, self.machineSize.y / 2, 0) @@ -103,15 +182,15 @@ class previewPanel(wx.Panel): self.toolbar2 = toolbarUtil.Toolbar(self) - # Mirror - self.mirrorX = toolbarUtil.ToggleButton(self.toolbar2, 'flip_x', 'object-mirror-x-on.png', 'object-mirror-x-off.png', 'Mirror X', callback=self.returnToModelViewAndUpdateModel) - self.mirrorY = toolbarUtil.ToggleButton(self.toolbar2, 'flip_y', 'object-mirror-y-on.png', 'object-mirror-y-off.png', 'Mirror Y', callback=self.returnToModelViewAndUpdateModel) - self.mirrorZ = toolbarUtil.ToggleButton(self.toolbar2, 'flip_z', 'object-mirror-z-on.png', 'object-mirror-z-off.png', 'Mirror Z', callback=self.returnToModelViewAndUpdateModel) + group = [] + self.mirrorToolButton = toolbarUtil.RadioButton(self.toolbar2, group, 'object-mirror-x-on.png', 'object-mirror-x-off.png', 'Mirror object') + self.rotateToolButton = toolbarUtil.RadioButton(self.toolbar2, group, 'object-rotate.png', 'object-rotate.png', 'Rotate object') + self.scaleToolButton = toolbarUtil.RadioButton(self.toolbar2, group, 'object-scale.png', 'object-scale.png', 'Scale object') self.toolbar2.AddSeparator() - - # Swap - self.swapXZ = toolbarUtil.ToggleButton(self.toolbar2, 'swap_xz', 'object-swap-xz-on.png', 'object-swap-xz-off.png', 'Swap XZ', callback=self.returnToModelViewAndUpdateModel) - self.swapYZ = toolbarUtil.ToggleButton(self.toolbar2, 'swap_yz', 'object-swap-yz-on.png', 'object-swap-yz-off.png', 'Swap YZ', callback=self.returnToModelViewAndUpdateModel) + # Mirror + self.mirrorX = toolbarUtil.NormalButton(self.toolbar2, self.OnMirrorX, 'object-mirror-x-on.png', 'Mirror X') + self.mirrorY = toolbarUtil.NormalButton(self.toolbar2, self.OnMirrorY, 'object-mirror-y-on.png', 'Mirror Y') + self.mirrorZ = toolbarUtil.NormalButton(self.toolbar2, self.OnMirrorZ, 'object-mirror-z-on.png', 'Mirror Z') self.toolbar2.AddSeparator() # Scale @@ -123,19 +202,9 @@ class previewPanel(wx.Panel): self.toolbar2.AddSeparator() - # Multiply - #self.mulXadd = toolbarUtil.NormalButton(self.toolbar2, self.OnMulXAddClick, 'object-mul-x-add.png', 'Increase number of models on X axis') - #self.mulXsub = toolbarUtil.NormalButton(self.toolbar2, self.OnMulXSubClick, 'object-mul-x-sub.png', 'Decrease number of models on X axis') - #self.mulYadd = toolbarUtil.NormalButton(self.toolbar2, self.OnMulYAddClick, 'object-mul-y-add.png', 'Increase number of models on Y axis') - #self.mulYsub = toolbarUtil.NormalButton(self.toolbar2, self.OnMulYSubClick, 'object-mul-y-sub.png', 'Decrease number of models on Y axis') - #self.toolbar2.AddSeparator() - # Rotate self.rotateReset = toolbarUtil.NormalButton(self.toolbar2, self.OnRotateReset, 'object-rotate.png', 'Reset model rotation') - 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.rotate.Bind(wx.EVT_TEXT, self.OnRotate) - self.toolbar2.AddControl(self.rotate) + self.layFlat = toolbarUtil.NormalButton(self.toolbar2, self.OnLayFlat, 'object-rotate.png', 'Lay flat') self.toolbar2.Realize() self.OnViewChange() @@ -149,34 +218,33 @@ class previewPanel(wx.Panel): self.checkReloadFileTimer = wx.Timer(self) self.Bind(wx.EVT_TIMER, self.OnCheckReloadFile, self.checkReloadFileTimer) self.checkReloadFileTimer.Start(1000) + + self.matrix = numpy.matrix(numpy.array(profile.getObjectMatrix(), numpy.float64).reshape((3,3,))) + self.tool = toolRotate(self.glCanvas) def returnToModelViewAndUpdateModel(self): if self.glCanvas.viewMode == 'GCode' or self.glCanvas.viewMode == 'Mixed': self.setViewMode('Normal') self.updateModelTransform() - + + def OnMirrorX(self, e): + self.matrix *= numpy.matrix([[-1,0,0],[0,1,0],[0,0,1]], numpy.float64) + self.returnToModelViewAndUpdateModel() + + def OnMirrorY(self, e): + self.matrix *= numpy.matrix([[1,0,0],[0,-1,0],[0,0,1]], numpy.float64) + self.returnToModelViewAndUpdateModel() + + def OnMirrorZ(self, e): + self.matrix *= numpy.matrix([[1,0,0],[0,1,0],[0,0,-1]], numpy.float64) + self.returnToModelViewAndUpdateModel() + def OnMove(self, e = None): - if e != None: + if e is not None: e.Skip() x, y = self.glCanvas.ClientToScreenXY(0, 0) sx, sy = self.glCanvas.GetClientSizeTuple() self.warningPopup.SetPosition((x, y+sy-self.warningPopup.GetSize().height)) - - def OnMulXAddClick(self, e): - profile.putProfileSetting('model_multiply_x', str(max(1, int(profile.getProfileSetting('model_multiply_x'))+1))) - self.glCanvas.Refresh() - - def OnMulXSubClick(self, e): - profile.putProfileSetting('model_multiply_x', str(max(1, int(profile.getProfileSetting('model_multiply_x'))-1))) - self.glCanvas.Refresh() - - def OnMulYAddClick(self, e): - profile.putProfileSetting('model_multiply_y', str(max(1, int(profile.getProfileSetting('model_multiply_y'))+1))) - self.glCanvas.Refresh() - - def OnMulYSubClick(self, e): - profile.putProfileSetting('model_multiply_y', str(max(1, int(profile.getProfileSetting('model_multiply_y'))-1))) - self.glCanvas.Refresh() def OnScaleReset(self, e): self.scale.SetValue('1.0') @@ -191,12 +259,11 @@ class previewPanel(wx.Panel): self.setViewMode('Normal') self.glCanvas.Refresh() - if self.objectsMaxV != None: - size = (self.objectsMaxV - self.objectsMinV) * float(scale) - self.toolbarInfo.SetValue('%0.1f %0.1f %0.1f' % (size[0], size[1], size[2])) + if self.objectsMaxV is not None: + self.toolbarInfo.SetValue('%0.1f %0.1f %0.1f' % (self.objectsSize[0], self.objectsSize[1], self.objectsSize[2])) def OnScaleMax(self, e = None, onlyScaleDown = False): - if self.objectsMinV == None: + if self.objectsMinV is None: return vMin = self.objectsMinV vMax = self.objectsMaxV @@ -218,12 +285,53 @@ class previewPanel(wx.Panel): self.glCanvas.Refresh() def OnRotateReset(self, e): - self.rotate.SetValue(0) - self.OnRotate(None) + self.matrix = numpy.matrix([[1,0,0],[0,1,0],[0,0,1]], numpy.float64) + self.updateModelTransform() - def OnRotate(self, e): - profile.putProfileSetting('model_rotate_base', self.rotate.GetValue()) - self.returnToModelViewAndUpdateModel() + def OnLayFlat(self, e): + transformedVertexes = (numpy.matrix(self.objectList[0].mesh.vertexes, copy = False) * self.matrix).getA() + minZvertex = transformedVertexes[transformedVertexes.argmin(0)[2]] + dotMin = 1.0 + dotV = None + for v in transformedVertexes: + diff = v - minZvertex + len = math.sqrt(diff[0] * diff[0] + diff[1] * diff[1] + diff[2] * diff[2]) + if len < 5: + continue + dot = (diff[2] / len) + if dotMin > dot: + dotMin = dot + dotV = diff + if dotV is None: + return + rad = -math.atan2(dotV[1], dotV[0]) + self.matrix *= numpy.matrix([[math.cos(rad), math.sin(rad), 0], [-math.sin(rad), math.cos(rad), 0], [0,0,1]], numpy.float64) + rad = -math.asin(dotMin) + self.matrix *= numpy.matrix([[math.cos(rad), 0, math.sin(rad)], [0,1,0], [-math.sin(rad), 0, math.cos(rad)]], numpy.float64) + + + transformedVertexes = (numpy.matrix(self.objectList[0].mesh.vertexes, copy = False) * self.matrix).getA() + minZvertex = transformedVertexes[transformedVertexes.argmin(0)[2]] + dotMin = 1.0 + dotV = None + for v in transformedVertexes: + diff = v - minZvertex + len = math.sqrt(diff[1] * diff[1] + diff[2] * diff[2]) + if len < 5: + continue + dot = (diff[2] / len) + if dotMin > dot: + dotMin = dot + dotV = diff + if dotV is None: + return + if dotV[1] < 0: + rad = math.asin(dotMin) + else: + rad = -math.asin(dotMin) + self.matrix *= numpy.matrix([[1,0,0], [0, math.cos(rad), math.sin(rad)], [0, -math.sin(rad), math.cos(rad)]], numpy.float64) + + self.updateModelTransform() def On3DClick(self): self.glCanvas.yaw = 30 @@ -266,14 +374,14 @@ class previewPanel(wx.Panel): self.gcodeFilename = sliceRun.getExportFilename(filelist[0]) #Do the STL file loading in a background thread so we don't block the UI. - if self.loadThread != None and self.loadThread.isAlive(): + if self.loadThread is not None and self.loadThread.isAlive(): self.loadThread.join() self.loadThread = threading.Thread(target=self.doFileLoadThread) self.loadThread.daemon = True self.loadThread.start() if showWarning: - if profile.getProfileSettingFloat('model_scale') != 1.0 or profile.getProfileSettingFloat('model_rotate_base') != 0 or profile.getProfileSetting('flip_x') != 'False' or profile.getProfileSetting('flip_y') != 'False' or profile.getProfileSetting('flip_z') != 'False' or profile.getProfileSetting('swap_xz') != 'False' or profile.getProfileSetting('swap_yz') != 'False' or len(profile.getPluginConfig()) > 0: + if (self.matrix - numpy.matrix([[1,0,0],[0,1,0],[0,0,1]], numpy.float64)).any() or len(profile.getPluginConfig()) > 0: self.ShowWarningPopup('Reset scale, rotation, mirror and plugins?', self.OnResetAll) def OnCheckReloadFile(self, e): @@ -281,7 +389,7 @@ class previewPanel(wx.Panel): if self.GetParent().FindFocus() is None: return for obj in self.objectList: - if obj.filename != None and os.path.isfile(obj.filename) and obj.fileTime != os.stat(obj.filename).st_mtime: + if obj.filename is not None and os.path.isfile(obj.filename) and obj.fileTime != os.stat(obj.filename).st_mtime: self.checkReloadFileTimer.Stop() self.ShowWarningPopup('File changed, reload?', self.reloadModelFiles) @@ -300,17 +408,16 @@ class previewPanel(wx.Panel): def doFileLoadThread(self): for obj in self.objectList: - if obj.filename != None and os.path.isfile(obj.filename) and obj.fileTime != os.stat(obj.filename).st_mtime: + if obj.filename is not None and os.path.isfile(obj.filename) and obj.fileTime != os.stat(obj.filename).st_mtime: obj.fileTime = os.stat(obj.filename).st_mtime mesh = meshLoader.loadMesh(obj.filename) - obj.dirty = False obj.mesh = mesh + obj.dirty = True self.updateModelTransform() self.OnScaleMax(None, True) scale = profile.getProfileSettingFloat('model_scale') - size = (self.objectsMaxV - self.objectsMinV) * scale - self.toolbarInfo.SetValue('%0.1f %0.1f %0.1f' % (size[0], size[1], size[2])) - self.glCanvas.zoom = numpy.max(size) * 2.5 + self.toolbarInfo.SetValue('%0.1f %0.1f %0.1f' % (self.objectsSize[0], self.objectsSize[1], self.objectsSize[2])) + self.glCanvas.zoom = numpy.max(self.objectsSize) * 2.5 self.errorList = [] wx.CallAfter(self.updateToolbar) wx.CallAfter(self.glCanvas.Refresh) @@ -327,7 +434,7 @@ class previewPanel(wx.Panel): errorList = [] for line in open(self.gcodeFilename, "rt"): res = re.search(';Model error\(([a-z ]*)\): \(([0-9\.\-e]*), ([0-9\.\-e]*), ([0-9\.\-e]*)\) \(([0-9\.\-e]*), ([0-9\.\-e]*), ([0-9\.\-e]*)\)', line) - if res != None: + if res is not None: v1 = util3d.Vector3(float(res.group(2)), float(res.group(3)), float(res.group(4))) v2 = util3d.Vector3(float(res.group(5)), float(res.group(6)), float(res.group(7))) errorList.append([v1, v2]) @@ -343,13 +450,7 @@ class previewPanel(wx.Panel): pass def OnResetAll(self, e = None): - profile.putProfileSetting('model_scale', '1.0') - profile.putProfileSetting('model_rotate_base', '0') - profile.putProfileSetting('flip_x', 'False') - profile.putProfileSetting('flip_y', 'False') - profile.putProfileSetting('flip_z', 'False') - profile.putProfileSetting('swap_xz', 'False') - profile.putProfileSetting('swap_yz', 'False') + profile.putProfileSetting('model_matrix', '1,0,0,0,1,0,0,0,1') profile.setPluginConfig([]) self.GetParent().updateProfileToControls() @@ -403,62 +504,38 @@ class previewPanel(wx.Panel): self.glCanvas.Refresh() def updateModelTransform(self, f=0): - if len(self.objectList) < 1 or self.objectList[0].mesh == None: + if len(self.objectList) < 1 or self.objectList[0].mesh is None: return - - rotate = profile.getProfileSettingFloat('model_rotate_base') - mirrorX = profile.getProfileSetting('flip_x') == 'True' - mirrorY = profile.getProfileSetting('flip_y') == 'True' - mirrorZ = profile.getProfileSetting('flip_z') == 'True' - swapXZ = profile.getProfileSetting('swap_xz') == 'True' - swapYZ = profile.getProfileSetting('swap_yz') == 'True' + profile.putProfileSetting('model_matrix', ','.join(map(str, list(self.matrix.getA().flatten())))) for obj in self.objectList: - if obj.mesh == None: + if obj.mesh is None: continue - obj.mesh.setRotateMirror(rotate, mirrorX, mirrorY, mirrorZ, swapXZ, swapYZ) - + obj.mesh.matrix = self.matrix + obj.mesh.processMatrix() + minV = self.objectList[0].mesh.getMinimum() maxV = self.objectList[0].mesh.getMaximum() - objectsBounderyCircleSize = self.objectList[0].mesh.bounderyCircleSize + objectsBoundaryCircleSize = self.objectList[0].mesh.bounderyCircleSize for obj in self.objectList: - if obj.mesh == None: + if obj.mesh is None: continue - obj.mesh.getMinimumZ() minV = numpy.minimum(minV, obj.mesh.getMinimum()) maxV = numpy.maximum(maxV, obj.mesh.getMaximum()) - objectsBounderyCircleSize = max(objectsBounderyCircleSize, obj.mesh.bounderyCircleSize) + objectsBoundaryCircleSize = max(objectsBoundaryCircleSize, obj.mesh.bounderyCircleSize) self.objectsMaxV = maxV self.objectsMinV = minV - self.objectsBounderyCircleSize = objectsBounderyCircleSize - for obj in self.objectList: - if obj.mesh == None: - continue - - obj.mesh.vertexes -= numpy.array([minV[0] + (maxV[0] - minV[0]) / 2, minV[1] + (maxV[1] - minV[1]) / 2, minV[2]]) - #for v in obj.mesh.vertexes: - # v[2] -= minV[2] - # v[0] -= minV[0] + (maxV[0] - minV[0]) / 2 - # v[1] -= minV[1] + (maxV[1] - minV[1]) / 2 - obj.mesh.getMinimumZ() - obj.dirty = True + self.objectsSize = self.objectsMaxV - self.objectsMinV + self.objectsBoundaryCircleSize = objectsBoundaryCircleSize - scale = profile.getProfileSettingFloat('model_scale') - size = (self.objectsMaxV - self.objectsMinV) * scale - self.toolbarInfo.SetValue('%0.1f %0.1f %0.1f' % (size[0], size[1], size[2])) + self.toolbarInfo.SetValue('%0.1f %0.1f %0.1f' % (self.objectsSize[0], self.objectsSize[1], self.objectsSize[2])) self.glCanvas.Refresh() def updateProfileToControls(self): - self.scale.SetValue(profile.getProfileSetting('model_scale')) - self.rotate.SetValue(profile.getProfileSettingFloat('model_rotate_base')) - self.mirrorX.SetValue(profile.getProfileSetting('flip_x') == 'True') - self.mirrorY.SetValue(profile.getProfileSetting('flip_y') == 'True') - self.mirrorZ.SetValue(profile.getProfileSetting('flip_z') == 'True') - self.swapXZ.SetValue(profile.getProfileSetting('swap_xz') == 'True') - self.swapYZ.SetValue(profile.getProfileSetting('swap_yz') == 'True') + self.matrix = numpy.matrix(numpy.array(profile.getObjectMatrix(), numpy.float64).reshape((3,3,))) self.updateModelTransform() self.glCanvas.updateProfileToControls() @@ -486,9 +563,9 @@ class PreviewGLCanvas(glcanvas.GLCanvas): self.oldX = 0 self.oldY = 0 self.dragType = '' - self.tempRotate = 0 + self.tempMatrix = None self.viewport = None - + def updateProfileToControls(self): self.objColor[0] = profile.getPreferenceColour('model_colour') self.objColor[1] = profile.getPreferenceColour('model_colour2') @@ -496,29 +573,19 @@ class PreviewGLCanvas(glcanvas.GLCanvas): self.objColor[3] = profile.getPreferenceColour('model_colour4') def OnMouseMotion(self,e): - cursorXY = 100000 - radius = 0 - if self.parent.objectsMaxV is not None and self.viewport is not None: - radius = self.parent.objectsBounderyCircleSize * profile.getProfileSettingFloat('model_scale') - + if self.parent.objectsMaxV is not None and self.viewport is not None and self.viewMode != 'GCode' and self.viewMode != 'Mixed': p0 = numpy.array(gluUnProject(e.GetX(), self.viewport[1] + self.viewport[3] - e.GetY(), 0, self.modelMatrix, self.projMatrix, self.viewport)) p1 = numpy.array(gluUnProject(e.GetX(), self.viewport[1] + self.viewport[3] - e.GetY(), 1, self.modelMatrix, self.projMatrix, self.viewport)) - cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2])) - cursorXY = math.sqrt((cursorZ0[0] * cursorZ0[0]) + (cursorZ0[1] * cursorZ0[1])) - if radius * 1.1 <= cursorXY <= radius * 1.3: - self.SetCursor(wx.StockCursor(wx.CURSOR_SIZING)) - else: - self.SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT)) + self.parent.tool.OnMouseMove(p0, p1) if e.Dragging() and e.LeftIsDown(): if self.dragType == '': #Define the drag type depending on the cursor position. - if radius * 1.1 <= cursorXY <= radius * 1.3 and self.viewMode != 'GCode' and self.viewMode != 'Mixed': - self.dragType = 'modelRotate' - self.dragStart = math.atan2(cursorZ0[0], cursorZ0[1]) - else: - self.dragType = 'viewRotate' - + self.dragType = 'viewRotate' + if self.viewMode != 'GCode' and self.viewMode != 'Mixed': + if self.parent.tool.OnDragStart(p0, p1): + self.dragType = 'tool' + if self.dragType == 'viewRotate': if self.view3D: self.yaw += e.GetX() - self.oldX @@ -530,29 +597,20 @@ class PreviewGLCanvas(glcanvas.GLCanvas): else: self.offsetX += float(e.GetX() - self.oldX) * self.zoom / self.GetSize().GetHeight() * 2 self.offsetY -= float(e.GetY() - self.oldY) * self.zoom / self.GetSize().GetHeight() * 2 - elif self.dragType == 'modelRotate': - angle = math.atan2(cursorZ0[0], cursorZ0[1]) - diff = self.dragStart - angle - self.tempRotate = diff * 180 / math.pi - rot = profile.getProfileSettingFloat('model_rotate_base') - self.tempRotate = round((self.tempRotate + rot) / 15) * 15 - rot + elif self.dragType == 'tool': + self.parent.tool.OnDrag(p0, p1) + #Workaround for buggy ATI cards. size = self.GetSizeTuple() self.SetSize((size[0]+1, size[1])) self.SetSize((size[0], size[1])) self.Refresh() else: - if self.tempRotate != 0: - newRotation = profile.getProfileSettingFloat('model_rotate_base') + self.tempRotate - while newRotation >= 360: - newRotation -= 360 - while newRotation < 0: - newRotation += 360 - profile.putProfileSetting('model_rotate_base', newRotation) - self.parent.rotate.SetValue(newRotation) + if self.tempMatrix is not None: + self.parent.matrix *= self.tempMatrix self.parent.updateModelTransform() - self.tempRotate = 0 - + self.tempMatrix = None + self.dragType = '' if e.Dragging() and e.RightIsDown(): self.zoom += e.GetY() - self.oldY @@ -565,8 +623,13 @@ class PreviewGLCanvas(glcanvas.GLCanvas): self.oldY = e.GetY() #self.Refresh() - - + + def getObjectBoundaryCircle(self): + return self.parent.objectsBoundaryCircleSize + + def getObjectSize(self): + return self.parent.objectsSize + def OnMouseWheel(self,e): self.zoom *= 1.0 - float(e.GetWheelRotation() / e.GetWheelDelta()) / 10.0 if self.zoom < 1.0: @@ -595,17 +658,17 @@ class PreviewGLCanvas(glcanvas.GLCanvas): glRotate(-self.pitch, 1,0,0) glRotate(self.yaw, 0,0,1) if self.viewMode == "GCode" or self.viewMode == "Mixed": - if self.parent.gcode != None and len(self.parent.gcode.layerList) > self.parent.layerSpin.GetValue() and len(self.parent.gcode.layerList[self.parent.layerSpin.GetValue()]) > 0: + if self.parent.gcode is not None and len(self.parent.gcode.layerList) > self.parent.layerSpin.GetValue() and len(self.parent.gcode.layerList[self.parent.layerSpin.GetValue()]) > 0: glTranslate(0,0,-self.parent.gcode.layerList[self.parent.layerSpin.GetValue()][0].list[-1].z) else: - if self.parent.objectsMaxV != None: - glTranslate(0,0,-(self.parent.objectsMaxV[2]-self.parent.objectsMinV[2]) * profile.getProfileSettingFloat('model_scale') / 2) + if self.parent.objectsMaxV is not None: + glTranslate(0,0,-self.parent.objectsSize[2] / 2) else: glTranslate(self.offsetX, self.offsetY, 0) - 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) glTranslate(-self.parent.machineCenter.x, -self.parent.machineCenter.y, 0) @@ -615,16 +678,16 @@ class PreviewGLCanvas(glcanvas.GLCanvas): def OnDraw(self): machineSize = self.parent.machineSize - if self.parent.gcode != None and self.parent.gcodeDirty: + if self.parent.gcode is not None and self.parent.gcodeDirty: if self.gcodeDisplayListCount < len(self.parent.gcode.layerList) or self.gcodeDisplayList == None: - if self.gcodeDisplayList != None: + if self.gcodeDisplayList is not None: glDeleteLists(self.gcodeDisplayList, self.gcodeDisplayListCount) - self.gcodeDisplayList = glGenLists(len(self.parent.gcode.layerList)); + self.gcodeDisplayList = glGenLists(len(self.parent.gcode.layerList)) self.gcodeDisplayListCount = len(self.parent.gcode.layerList) self.parent.gcodeDirty = False self.gcodeDisplayListMade = 0 - if self.parent.gcode != None and self.gcodeDisplayListMade < len(self.parent.gcode.layerList): + if self.parent.gcode is not None and self.gcodeDisplayListMade < len(self.parent.gcode.layerList): glNewList(self.gcodeDisplayList + self.gcodeDisplayListMade, GL_COMPILE) opengl.DrawGCodeLayer(self.parent.gcode.layerList[self.gcodeDisplayListMade]) glEndList() @@ -689,9 +752,9 @@ class PreviewGLCanvas(glcanvas.GLCanvas): glPushMatrix() glTranslate(self.parent.machineCenter.x, self.parent.machineCenter.y, 0) for obj in self.parent.objectList: - if obj.mesh == None: + if obj.mesh is None: continue - + if self.viewMode == "Transparent" or self.viewMode == "Mixed": glLightfv(GL_LIGHT0, GL_DIFFUSE, map(lambda x: x / 2, self.objColor[self.parent.objectList.index(obj)])) glLightfv(GL_LIGHT0, GL_AMBIENT, map(lambda x: x / 10, self.objColor[self.parent.objectList.index(obj)])) @@ -782,65 +845,46 @@ class PreviewGLCanvas(glcanvas.GLCanvas): glPopMatrix() glPopMatrix() - if self.viewMode == "Normal" or self.viewMode == "Transparent" or self.viewMode == "X-Ray": - glDisable(GL_LIGHTING) - glDisable(GL_DEPTH_TEST) - glDisable(GL_BLEND) - glColor3f(1,0,0) - glBegin(GL_LINES) - for err in self.parent.errorList: - glVertex3f(err[0].x, err[0].y, err[0].z) - glVertex3f(err[1].x, err[1].y, err[1].z) - glEnd() - glEnable(GL_DEPTH_TEST) - - glPushMatrix() - glTranslate(self.parent.machineCenter.x, self.parent.machineCenter.y, 0) - - #Draw the rotate circle + #if self.viewMode == "Normal" or self.viewMode == "Transparent" or self.viewMode == "X-Ray": + # glDisable(GL_LIGHTING) + # glDisable(GL_DEPTH_TEST) + # glDisable(GL_BLEND) + # glColor3f(1,0,0) + # glBegin(GL_LINES) + # for err in self.parent.errorList: + # glVertex3f(err[0].x, err[0].y, err[0].z) + # glVertex3f(err[1].x, err[1].y, err[1].z) + # glEnd() + # glEnable(GL_DEPTH_TEST) + + #Draw the current selected tool if self.parent.objectsMaxV is not None and self.viewMode != 'GCode' and self.viewMode != 'Mixed': - glDisable(GL_LIGHTING) - glDisable(GL_CULL_FACE) - glEnable(GL_BLEND) - glRotate(self.tempRotate + profile.getProfileSettingFloat('model_rotate_base'), 0, 0, 1) - radius = self.parent.objectsBounderyCircleSize * profile.getProfileSettingFloat('model_scale') - glScalef(radius, radius, 1) - glBegin(GL_TRIANGLE_STRIP) - for i in xrange(0, 64+1): - f = i if i < 64/2 else 64 - i - glColor4ub(255,int(f*255/(64/2)),0,255) - glVertex3f(1.1 * math.cos(i/32.0*math.pi), 1.1 * math.sin(i/32.0*math.pi),0.1) - glColor4ub( 0,128,0,255) - glVertex3f(1.3 * math.cos(i/32.0*math.pi), 1.3 * math.sin(i/32.0*math.pi),0.1) - glEnd() - glBegin(GL_TRIANGLES) - glColor4ub(0,0,0,192) - glVertex3f(1, 0.1,0.15) - glVertex3f(1,-0.1,0.15) - glVertex3f(1.4,0,0.15) - glEnd() - glEnable(GL_CULL_FACE) - - glPopMatrix() + glPushMatrix() + glTranslate(self.parent.machineCenter.x, self.parent.machineCenter.y, self.parent.objectsSize[2]/2) + self.parent.tool.OnDraw() + glPopMatrix() opengl.DrawMachine(machineSize) glFlush() - + + def convert3x3MatrixTo4x4(self, matrix): + return list(matrix.getA()[0]) + [0] + list(matrix.getA()[1]) + [0] + list(matrix.getA()[2]) + [0, 0,0,0,1] + def drawModel(self, obj): - multiX = 1 #int(profile.getProfileSetting('model_multiply_x')) - multiY = 1 #int(profile.getProfileSetting('model_multiply_y')) - modelScale = profile.getProfileSettingFloat('model_scale') - modelSize = (obj.mesh.getMaximum() - obj.mesh.getMinimum()) * modelScale + vMin = self.parent.objectsMinV + vMax = self.parent.objectsMaxV + offset = - vMin - (vMax - vMin) / 2 + + matrix = self.convert3x3MatrixTo4x4(obj.mesh.matrix) + glPushMatrix() - glRotate(self.tempRotate, 0, 0, 1) - glTranslate(-(modelSize[0]+10)*(multiX-1)/2,-(modelSize[1]+10)*(multiY-1)/2, 0) - for mx in xrange(0, multiX): - for my in xrange(0, multiY): - glPushMatrix() - glTranslate((modelSize[0]+10)*mx,(modelSize[1]+10)*my, 0) - glScalef(modelScale, modelScale, modelScale) - glCallList(obj.displayList) - glPopMatrix() + glTranslate(0, 0, self.parent.objectsSize[2]/2) + if self.tempMatrix is not None: + tempMatrix = self.convert3x3MatrixTo4x4(self.tempMatrix) + glMultMatrixf(tempMatrix) + glTranslate(0, 0, -self.parent.objectsSize[2]/2) + glTranslate(offset[0], offset[1], -vMin[2]) + glMultMatrixf(matrix) + glCallList(obj.displayList) glPopMatrix() - diff --git a/Cura/gui/simpleMode.py b/Cura/gui/simpleMode.py index 04400524..e70d3daf 100644 --- a/Cura/gui/simpleMode.py +++ b/Cura/gui/simpleMode.py @@ -83,13 +83,6 @@ class simpleModePanel(wx.Panel): put('fan_enabled', 'True') put('fan_layer', '1') put('fan_speed', '100') - #put('model_scale', '1.0') - #put('flip_x', 'False') - #put('flip_y', 'False') - #put('flip_z', 'False') - #put('model_rotate_base', '0') - #put('model_multiply_x', '1') - #put('model_multiply_y', '1') put('extra_base_wall_thickness', '0.0') put('sequence', 'Loops > Perimeter > Infill') put('force_first_layer_sequence', 'True') diff --git a/Cura/gui/util/opengl.py b/Cura/gui/util/opengl.py index f8fc985b..9e009ca2 100644 --- a/Cura/gui/util/opengl.py +++ b/Cura/gui/util/opengl.py @@ -12,6 +12,7 @@ try: import OpenGL OpenGL.ERROR_CHECKING = False + from OpenGL.GLUT import * from OpenGL.GLU import * from OpenGL.GL import * @@ -69,7 +70,6 @@ def DrawMachine(machineSize): if platformMesh is None: try: platformMesh = meshLoader.loadMesh(getPathForMesh('ultimaker_platform.stl')) - platformMesh.setRotateMirror(0, False, False, False, False, False) except: platformMesh = False @@ -247,6 +247,8 @@ def DrawMachine(machineSize): glVertex3f(-0.8, -1, 0) glVertex3f(0.8, -1, 0) glEnd() + glutBitmapCharacter(GLUT_BITMAP_HELVETICA_18, 64) + glutStrokeCharacter(GLUT_STROKE_ROMAN, 64) glPopMatrix() glPopMatrix() diff --git a/Cura/util/mesh.py b/Cura/util/mesh.py index 857d6201..d013d597 100644 --- a/Cura/util/mesh.py +++ b/Cura/util/mesh.py @@ -11,70 +11,44 @@ from Cura.util import util3d class mesh(object): def __init__(self): self.vertexes = None - self.origonalVertexes = None + self.matrix = numpy.matrix([[1,0,0], [0,1,0], [0,0,1]], numpy.float32); self.vertexCount = 0 def addVertex(self, x, y, z): n = self.vertexCount - self.origonalVertexes[n][0] = x - self.origonalVertexes[n][1] = y - self.origonalVertexes[n][2] = z + self.vertexes[n][0] = x + self.vertexes[n][1] = y + self.vertexes[n][2] = z self.vertexCount += 1 def _prepareVertexCount(self, vertexNumber): #Set the amount of faces before loading data in them. This way we can create the numpy arrays before we fill them. - self.origonalVertexes = numpy.zeros((vertexNumber, 3), numpy.float32) + self.vertexes = numpy.zeros((vertexNumber, 3), numpy.float32) self.normal = numpy.zeros((vertexNumber, 3), numpy.float32) self.vertexCount = 0 def _postProcessAfterLoad(self): - self.vertexes = self.origonalVertexes.copy() - self.getMinimumZ() - - def getMinimumZ(self): - self.min = self.vertexes.min(0) - self.max = self.vertexes.max(0) - self.size = self.max - self.min - return self.min[2] - + self.processMatrix() + self._calculateNormals() + + def processMatrix(self): + transformedVertexes = (numpy.matrix(self.vertexes, copy = False) * self.matrix).getA() + self.transformedMin = transformedVertexes.min(0) + self.transformedMax = transformedVertexes.max(0) + self.transformedSize = self.transformedMax - self.transformedMin + + #Calculate the boundery circle + center = self.transformedMin + self.transformedSize / 2.0 + self.bounderyCircleSize = round(math.sqrt(numpy.max(((transformedVertexes[::,0] - center[0]) * (transformedVertexes[::,0] - center[0])) + ((transformedVertexes[::,1] - center[1]) * (transformedVertexes[::,1] - center[1])))), 3) + def getMaximum(self): - return self.max + return self.transformedMax def getMinimum(self): - return self.min + return self.transformedMin def getSize(self): - return self.size - - def setRotateMirror(self, rotate, mirrorX, mirrorY, mirrorZ, swapXZ, swapYZ): - #Modify the vertexes with the rotation/mirror - rotate = rotate / 180.0 * math.pi - scaleX = 1.0 - scaleY = 1.0 - scaleZ = 1.0 - if mirrorX: - scaleX = -scaleX - if mirrorY: - scaleY = -scaleY - if mirrorZ: - scaleZ = -scaleZ - mat00 = math.cos(rotate) * scaleX - mat01 =-math.sin(rotate) * scaleY - mat10 = math.sin(rotate) * scaleX - mat11 = math.cos(rotate) * scaleY - - mat = numpy.array([[mat00,mat10,0],[mat01,mat11,0],[0,0,scaleZ]], numpy.float32) - if swapXZ: - mat = numpy.array([mat[2],mat[1],mat[0]], numpy.float32) - if swapYZ: - mat = numpy.array([mat[0],mat[2],mat[1]], numpy.float32) - self.matrix = mat - self.vertexes = (numpy.matrix(self.origonalVertexes, copy = False) * numpy.matrix(mat)).getA() - - #Calculate the boundery box of the object - self.getMinimumZ() - #Calculate the boundery circle - center = (self.max + self.min) / 2.0 - self.bounderyCircleSize = round(math.sqrt(numpy.max(((self.vertexes[::,0] - center[0]) * (self.vertexes[::,0] - center[0])) + ((self.vertexes[::,1] - center[1]) * (self.vertexes[::,1] - center[1])))), 3) - + return self.transformedSize + + def _calculateNormals(self): #Calculate the normals tris = self.vertexes.reshape(self.vertexCount / 3, 3, 3) normals = numpy.cross( tris[::,1 ] - tris[::,0] , tris[::,2 ] - tris[::,0] ) diff --git a/Cura/util/profile.py b/Cura/util/profile.py index 064b7748..9d34d107 100644 --- a/Cura/util/profile.py +++ b/Cura/util/profile.py @@ -44,15 +44,7 @@ profileDefaultSettings = { 'fan_layer': '1', 'fan_speed': '100', 'fan_speed_max': '100', - 'model_scale': '1.0', - 'flip_x': 'False', - 'flip_y': 'False', - 'flip_z': 'False', - 'swap_xz': 'False', - 'swap_yz': 'False', - 'model_rotate_base': '0', - 'model_multiply_x': '1', - 'model_multiply_y': '1', + 'model_matrix': '1,0,0,0,1,0,0,0,1', 'extra_base_wall_thickness': '0.0', 'sequence': 'Loops > Perimeter > Infill', 'force_first_layer_sequence': 'True', @@ -431,28 +423,8 @@ def getMachineCenterCoords(): return [getPreferenceFloat('machine_width') / 2, getPreferenceFloat('machine_depth') / 2] def getObjectMatrix(): - rotate = getProfileSettingFloat('model_rotate_base') - rotate = rotate / 180.0 * math.pi - scaleX = getProfileSettingFloat('model_scale') - scaleY = getProfileSettingFloat('model_scale') - scaleZ = getProfileSettingFloat('model_scale') - if getProfileSetting('flip_x') == 'True': - scaleX = -scaleX - if getProfileSetting('flip_y') == 'True': - scaleY = -scaleY - if getProfileSetting('flip_z') == 'True': - scaleZ = -scaleZ - mat00 = math.cos(rotate) * scaleX - mat01 =-math.sin(rotate) * scaleY - mat10 = math.sin(rotate) * scaleX - mat11 = math.cos(rotate) * scaleY - - mat = [mat00,mat10,0, mat01,mat11,0, 0,0,scaleZ] - if getProfileSetting('swap_xz') == 'True': - mat = mat[6:9] + mat[3:6] + mat[0:3] - if getProfileSetting('swap_yz') == 'True': - mat = mat[0:3] + mat[6:9] + mat[3:6] - return mat + scale = getProfileSettingFloat('model_scale') + return map(lambda x: float(x) * scale, getProfileSetting('model_matrix').split(',')) ######################################################### ## Alteration file functions -- 2.30.2