From: daid Date: Thu, 28 Nov 2013 13:38:44 +0000 (+0100) Subject: Add convex hulls for all objects and do collision detection with that. Add initial... X-Git-Tag: 14.01~55 X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=commitdiff_plain;h=73489ead7f32ae84d3c6045d4b1b08571cc9699d;p=cura.git Add convex hulls for all objects and do collision detection with that. Add initial handling for the printerConnectionManager. --- diff --git a/Cura/gui/sceneView.py b/Cura/gui/sceneView.py index 3e1ce545..0afafc73 100644 --- a/Cura/gui/sceneView.py +++ b/Cura/gui/sceneView.py @@ -24,6 +24,7 @@ from Cura.util import sliceEngine from Cura.util import machineCom from Cura.util import removableStorage from Cura.util import gcodeInterpreter +from Cura.util.printerConnection import printerConnectionManager from Cura.gui.util import previewTools from Cura.gui.util import opengl from Cura.gui.util import openglGui @@ -56,6 +57,7 @@ class SceneView(openglGui.glGuiPanel): self._platformMesh = {} self._isSimpleMode = True self._usbPrintMonitor = printWindow.printProcessMonitor(lambda : self._queueRefresh()) + self._printerConnectionManager = printerConnectionManager.PrinterConnectionManager() self._viewport = None self._modelMatrix = None @@ -495,7 +497,7 @@ class SceneView(openglGui.glGuiPanel): def sceneUpdated(self): self._sceneUpdateTimer.Start(500, True) self._slicer.abortSlicer() - self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32)) + self._scene.updateSizeOffsets() self.QueueRefresh() def _onRunSlicer(self, e): @@ -616,10 +618,7 @@ class SceneView(openglGui.glGuiPanel): self._objColors[2] = profile.getPreferenceColour('model_colour3') self._objColors[3] = profile.getPreferenceColour('model_colour4') self._scene.setMachineSize(self._machineSize) - self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32)) self._scene.setHeadSize(profile.getMachineSettingFloat('extruder_head_size_min_x'), profile.getMachineSettingFloat('extruder_head_size_max_x'), profile.getMachineSettingFloat('extruder_head_size_min_y'), profile.getMachineSettingFloat('extruder_head_size_max_y'), profile.getMachineSettingFloat('extruder_head_size_height')) - for n in xrange(1, 4): - self._scene.setExtruderOffset(n, profile.getMachineSettingFloat('extruder_offset_x%d' % (n)), profile.getMachineSettingFloat('extruder_offset_y%d' % (n))) if self._selectedObj is not None: scale = self._selectedObj.getScale() @@ -832,12 +831,16 @@ class SceneView(openglGui.glGuiPanel): glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT) def OnPaint(self,e): + connectionEntry = self._printerConnectionManager.getAvailableConnection() if machineCom.machineIsConnected(): self.printButton._imageID = 6 self.printButton._tooltip = _("Print") - elif len(removableStorage.getPossibleSDcardDrives()) > 0: + elif len(removableStorage.getPossibleSDcardDrives()) > 0 and (connectionEntry is None or connectionEntry.priority < 0): self.printButton._imageID = 2 self.printButton._tooltip = _("Toolpath to SD") + elif connectionEntry is not None: + self.printButton._imageID = connectionEntry.icon + self.printButton._tooltip = _("Print with %s") % (connectionEntry.name) else: self.printButton._imageID = 3 self.printButton._tooltip = _("Save toolpath") @@ -1151,19 +1154,31 @@ void main(void) glPopMatrix() else: #Draw the object box-shadow, so you can see where it will collide with other objects. - if self._selectedObj is not None and len(self._scene.objects()) > 1: - size = self._selectedObj.getSize()[0:2] / 2 + self._scene.getObjectExtend() - glPushMatrix() - glTranslatef(self._selectedObj.getPosition()[0], self._selectedObj.getPosition()[1], 0) + if self._selectedObj is not None: glEnable(GL_BLEND) glEnable(GL_CULL_FACE) - glColor4f(0,0,0,0.12) - glBegin(GL_QUADS) - glVertex3f(-size[0], size[1], 0.1) - glVertex3f(-size[0], -size[1], 0.1) - glVertex3f( size[0], -size[1], 0.1) - glVertex3f( size[0], size[1], 0.1) + glColor4f(0,0,0,0.16) + glDepthMask(False) + for obj in self._scene.objects(): + glPushMatrix() + glTranslatef(obj.getPosition()[0], obj.getPosition()[1], 0) + glBegin(GL_TRIANGLE_FAN) + for p in obj._boundaryHull[::-1]: + glVertex3f(p[0], p[1], 0) + glEnd() + glPopMatrix() + glPushMatrix() + glColor4f(0,0,0,0.06) + glTranslatef(self._selectedObj.getPosition()[0], self._selectedObj.getPosition()[1], 0) + glBegin(GL_TRIANGLE_FAN) + for p in self._selectedObj._printAreaHull[::-1]: + glVertex3f(p[0], p[1], 0) + glEnd() + glBegin(GL_TRIANGLE_FAN) + for p in self._selectedObj._headAreaHull[::-1]: + glVertex3f(p[0], p[1], 0) glEnd() + glDepthMask(True) glDisable(GL_CULL_FACE) glPopMatrix() @@ -1314,6 +1329,7 @@ void main(void) glEnableClientState(GL_VERTEX_ARRAY) glVertexPointer(3, GL_FLOAT, 3*4, vList) + glDepthMask(False) glColor4ub(5, 171, 231, 64) glDrawArrays(GL_QUADS, 0, 4) glColor4ub(5, 171, 231, 96) @@ -1341,10 +1357,10 @@ void main(void) else: glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128) glBegin(GL_QUADS) - glVertex3f(x1, y1, -0.02) #Draw bit below z0 to prevent zfighting. - glVertex3f(x2, y1, -0.02) - glVertex3f(x2, y2, -0.02) - glVertex3f(x1, y2, -0.02) + glVertex3f(x1, y1, 0) + glVertex3f(x2, y1, 0) + glVertex3f(x2, y2, 0) + glVertex3f(x1, y2, 0) glEnd() if machine == 'ultimaker2': @@ -1357,10 +1373,10 @@ void main(void) posX = sx / 2 - clipWidth posY = sy / 2 - clipHeight glBegin(GL_QUADS) - glVertex3f(posX, posY, 0.1) - glVertex3f(posX+clipWidth, posY, 0.1) - glVertex3f(posX+clipWidth, posY+clipHeight, 0.1) - glVertex3f(posX, posY+clipHeight, 0.1) + glVertex3f(posX, posY, 0) + glVertex3f(posX+clipWidth, posY, 0) + glVertex3f(posX+clipWidth, posY+clipHeight, 0) + glVertex3f(posX, posY+clipHeight, 0) glEnd() #UpperLeft clipWidth = 25 @@ -1368,10 +1384,10 @@ void main(void) posX = -sx / 2 posY = sy / 2 - clipHeight glBegin(GL_QUADS) - glVertex3f(posX, posY, 0.1) - glVertex3f(posX+clipWidth, posY, 0.1) - glVertex3f(posX+clipWidth, posY+clipHeight, 0.1) - glVertex3f(posX, posY+clipHeight, 0.1) + glVertex3f(posX, posY, 0) + glVertex3f(posX+clipWidth, posY, 0) + glVertex3f(posX+clipWidth, posY+clipHeight, 0) + glVertex3f(posX, posY+clipHeight, 0) glEnd() #LowerRight clipWidth = 25 @@ -1379,10 +1395,10 @@ void main(void) posX = sx / 2 - clipWidth posY = -sy / 2 glBegin(GL_QUADS) - glVertex3f(posX, posY, 0.1) - glVertex3f(posX+clipWidth, posY, 0.1) - glVertex3f(posX+clipWidth, posY+clipHeight, 0.1) - glVertex3f(posX, posY+clipHeight, 0.1) + glVertex3f(posX, posY, 0) + glVertex3f(posX+clipWidth, posY, 0) + glVertex3f(posX+clipWidth, posY+clipHeight, 0) + glVertex3f(posX, posY+clipHeight, 0) glEnd() #LowerLeft clipWidth = 25 @@ -1390,12 +1406,13 @@ void main(void) posX = -sx / 2 posY = -sy / 2 glBegin(GL_QUADS) - glVertex3f(posX, posY, 0.1) - glVertex3f(posX+clipWidth, posY, 0.1) - glVertex3f(posX+clipWidth, posY+clipHeight, 0.1) - glVertex3f(posX, posY+clipHeight, 0.1) + glVertex3f(posX, posY, 0) + glVertex3f(posX+clipWidth, posY, 0) + glVertex3f(posX+clipWidth, posY+clipHeight, 0) + glVertex3f(posX, posY+clipHeight, 0) glEnd() + glDepthMask(True) glDisable(GL_BLEND) glDisable(GL_CULL_FACE) diff --git a/Cura/util/gcodeInterpreter.py b/Cura/util/gcodeInterpreter.py index 00f98e36..2d674641 100644 --- a/Cura/util/gcodeInterpreter.py +++ b/Cura/util/gcodeInterpreter.py @@ -238,6 +238,8 @@ class gcode(object): pass elif M == 1: #Message with possible wait (ignored) pass + elif M == 25: #Stop SD printing + pass elif M == 80: #Enable power supply pass elif M == 81: #Suicide/disable power supply diff --git a/Cura/util/mesh.py b/Cura/util/mesh.py index 9d641426..cf196d4a 100644 --- a/Cura/util/mesh.py +++ b/Cura/util/mesh.py @@ -8,43 +8,7 @@ import os import numpy numpy.seterr(all='ignore') -def convexHull(pointList): - def _isRightTurn((p, q, r)): - sum1 = q[0]*r[1] + p[0]*q[1] + r[0]*p[1] - sum2 = q[0]*p[1] + r[0]*q[1] + p[0]*r[1] - - if sum1 - sum2 < 0: - return 1 - else: - return 0 - - unique = {} - for p in pointList: - unique[(int(p[0]),int(p[1]))] = 1 - - points = unique.keys() - points.sort() - - # Build upper half of the hull. - upper = [points[0], points[1]] - for p in points[2:]: - upper.append(p) - while len(upper) > 2 and not _isRightTurn(upper[-3:]): - del upper[-2] - - # Build lower half of the hull. - points = points[::-1] - lower = [points[0], points[1]] - for p in points[2:]: - lower.append(p) - while len(lower) > 2 and not _isRightTurn(lower[-3:]): - del lower[-2] - - # Remove duplicates. - del lower[0] - del lower[-1] - - return numpy.array(upper + lower, numpy.float32) - numpy.array([0.0,0.0], numpy.float32) +from Cura.util import polygon class printableObject(object): def __init__(self, originFilename): @@ -63,6 +27,12 @@ class printableObject(object): self._transformedSize = None self._boundaryCircleSize = None self._drawOffset = None + self._boundaryHull = None + self._printAreaExtend = numpy.array([[-1,-1],[ 1,-1],[ 1, 1],[-1, 1]], numpy.float32) + self._headAreaExtend = numpy.array([[-1,-1],[ 1,-1],[ 1, 1],[-1, 1]], numpy.float32) + self._printAreaHull = None + self._headAreaHull = None + self._loadAnim = None def copy(self): @@ -73,6 +43,8 @@ class printableObject(object): ret._transformedSize = self._transformedSize.copy() ret._boundaryCircleSize = self._boundaryCircleSize ret._boundaryHull = self._boundaryHull.copy() + ret._printAreaExtend = self._printAreaExtend.copy() + ret._printAreaHull = self._printAreaHull.copy() ret._drawOffset = self._drawOffset.copy() for m in self._meshList[:]: m2 = ret._addMesh() @@ -109,10 +81,10 @@ class printableObject(object): self._transformedMax = numpy.array([-999999999999,-999999999999,-999999999999], numpy.float64) self._boundaryCircleSize = 0 - hull = numpy.zeros((0, 2), numpy.float32) + hull = numpy.zeros((0, 2), numpy.int) for m in self._meshList: transformedVertexes = m.getTransformedVertexes() - hull = convexHull(numpy.concatenate((transformedVertexes[:,0:2], hull), 0)) + hull = polygon.convexHull(numpy.concatenate((numpy.rint(transformedVertexes[:,0:2]).astype(int), hull), 0)) transformedMin = transformedVertexes.min(0) transformedMax = transformedVertexes.max(0) for n in xrange(0, 3): @@ -127,10 +99,13 @@ class printableObject(object): self._transformedSize = self._transformedMax - self._transformedMin self._drawOffset = (self._transformedMax + self._transformedMin) / 2 self._drawOffset[2] = self._transformedMin[2] - self._boundaryHull = hull - self._drawOffset[0:2] self._transformedMax -= self._drawOffset self._transformedMin -= self._drawOffset + self._boundaryHull = polygon.minkowskiHull((hull.astype(numpy.float32) - self._drawOffset[0:2]), numpy.array([[-1,-1],[-1,1],[1,1],[1,-1]],numpy.float32)) + self._printAreaHull = polygon.minkowskiHull(self._boundaryHull, self._printAreaExtend) + self._headAreaHull = polygon.minkowskiHull(self._printAreaHull, self._headAreaExtend) + def getName(self): return self._name def getOriginFilename(self): @@ -153,6 +128,15 @@ class printableObject(object): def getBoundaryCircle(self): return self._boundaryCircleSize + def setPrintAreaExtends(self, poly): + self._printAreaExtend = poly + self._printAreaHull = polygon.minkowskiHull(self._boundaryHull, self._printAreaExtend) + self._headAreaHull = polygon.minkowskiHull(self._printAreaHull, self._headAreaExtend) + + def setHeadArea(self, poly): + self._headAreaExtend = poly + self._headAreaHull = polygon.minkowskiHull(self._printAreaHull, self._headAreaExtend) + def mirror(self, axis): matrix = [[1,0,0], [0, 1, 0], [0, 0, 1]] matrix[axis][axis] = -1 diff --git a/Cura/util/objectScene.py b/Cura/util/objectScene.py index 16744add..947bc95b 100644 --- a/Cura/util/objectScene.py +++ b/Cura/util/objectScene.py @@ -1,7 +1,9 @@ __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License" import random import numpy + from Cura.util import profile +from Cura.util import polygon class _objectOrder(object): def __init__(self, order, todo): @@ -73,26 +75,9 @@ class _objectOrderFinder(object): #Check if printing one object will cause printhead colission with other object. def _checkHit(self, addIdx, idx): - addPos = self._scene._objectList[addIdx].getPosition() - addSize = self._scene._objectList[addIdx].getSize() - pos = self._scene._objectList[idx].getPosition() - size = self._scene._objectList[idx].getSize() - - if self._leftToRight: - if addPos[0] - addSize[0] / 2 - self._offset[0] >= pos[0] + size[0] / 2: - return False - else: - if addPos[0] + addSize[0] / 2 + self._offset[0] <= pos[0] - size[0] / 2: - return False - - if self._frontToBack: - if addPos[1] - addSize[1] / 2 - self._offset[1] >= pos[1] + size[1] / 2: - return False - else: - if addPos[1] + addSize[1] / 2 + self._offset[1] <= pos[1] - size[1] / 2: - return False - - return True + obj = self._scene._objectList[idx] + addObj = self._scene._objectList[addIdx] + return polygon.polygonCollision(obj._headAreaHull + obj.getPosition(), addObj._boundaryHull + addObj.getPosition()) class Scene(object): def __init__(self): @@ -106,13 +91,22 @@ class Scene(object): self._leftToRight = False self._frontToBack = True self._gantryHeight = 60 + self._oneAtATime = True + # Physical (square) machine size. def setMachineSize(self, machineSize): self._machineSize = machineSize # Size offsets are offsets caused by brim, skirt, etc. - def setSizeOffsets(self, sizeOffsets): - self._sizeOffsets = sizeOffsets + def updateSizeOffsets(self, force=False): + newOffsets = numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32) + if not force and numpy.array_equal(self._sizeOffsets, newOffsets): + return + self._sizeOffsets = newOffsets + + extends = numpy.array([[-newOffsets[0],-newOffsets[1]],[ newOffsets[0],-newOffsets[1]],[ newOffsets[0], newOffsets[1]],[-newOffsets[0], newOffsets[1]]], numpy.float32) + for obj in self._objectList: + obj.setPrintAreaExtends(extends) #size of the printing head. def setHeadSize(self, xMin, xMax, yMin, yMax, gantryHeight): @@ -121,6 +115,11 @@ class Scene(object): self._headSizeOffsets[0] = min(xMin, xMax) self._headSizeOffsets[1] = min(yMin, yMax) self._gantryHeight = gantryHeight + self._oneAtATime = self._gantryHeight > 0 + + headArea = numpy.array([[-xMin,-yMin],[ xMax,-yMin],[ xMax, yMax],[-xMin, yMax]], numpy.float32) + for obj in self._objectList: + obj.setHeadArea(headArea) def setExtruderOffset(self, extruderNr, offsetX, offsetY): self._extruderOffset[extruderNr] = numpy.array([offsetX, offsetY], numpy.float32) @@ -140,6 +139,7 @@ class Scene(object): scale = numpy.max(self._machineSize[0:2]) * 2.5 / numpy.max(obj.getSize()[0:2]) matrix = [[scale,0,0], [0, scale, 0], [0, 0, scale]] obj.applyMatrix(numpy.matrix(matrix, numpy.float64)) + self.updateSizeOffsets(True) def remove(self, obj): self._objectList.remove(obj) @@ -155,6 +155,7 @@ class Scene(object): self.pushFree() def pushFree(self): + return n = 1000 while self._pushFree(): n -= 1 @@ -183,7 +184,10 @@ class Scene(object): obj.setPosition(obj.getPosition() + offset) def printOrder(self): - order = _objectOrderFinder(self, self._headSizeOffsets + self._sizeOffsets, self._leftToRight, self._frontToBack, self._gantryHeight).order + if self._oneAtATime: + order = _objectOrderFinder(self, self._headSizeOffsets + self._sizeOffsets, self._leftToRight, self._frontToBack, self._gantryHeight).order + else: + order = None return order def _pushFree(self): @@ -215,11 +219,10 @@ class Scene(object): def _checkHit(self, a, b): if a == b: return False - posDiff = a.getPosition() - b.getPosition() - if abs(posDiff[0]) < (a.getSize()[0] + b.getSize()[0]) / 2 + self._sizeOffsets[0] + self._headSizeOffsets[0]: - if abs(posDiff[1]) < (a.getSize()[1] + b.getSize()[1]) / 2 + self._sizeOffsets[1] + self._headSizeOffsets[1]: - return True - return False + if self._oneAtATime: + return polygon.polygonCollision(a._headAreaHull + a.getPosition(), b._boundaryHull + b.getPosition()) + else: + return polygon.polygonCollision(a._boundaryHull + a.getPosition(), b._boundaryHull + b.getPosition()) def checkPlatform(self, obj): p = obj.getPosition()