""" Creational Pattern: Prototype pattern""" from copy import copy, deepcopy from includes import * from parameters import Settings from quaternion import * from singleton import * import geosolver from geosolver import GeometricDecomposition import delaunay._qhull as qhull import delaunay.core as dcore from geosolver.matfunc import Vec import numpy as Numeric try: from OpenGL.GL import * from OpenGL.GLU import * except ImportError: app = QtGui.QApplication( sys.argv ) QtGui.QMessageBox.critical( None, "OpenGL grabber", "PyOpenGL must be installed to run this example.", QtGui.QMessageBox.Ok | QtGui.QMessageBox.Default, QtGui.QMessageBox.NoButton ) sys.exit( 1 ) class PrototypeManager( Singleton ): """ The prototype manager handles the objects needed for the different viewports. Also it handles the connection with the geometric constraint solver, e.g. when a new object is added (deleted) it will also be added (deleted) to (from) the constraint graph. Updates for visualisation or constraint solving is handled too.""" def __init__( self ): """ The initilization of the class is done once, else the one and only instance is returned. """ Singleton.__init__( self ) if self._isNew: self.prtObjects = [] self.clsObjects = [] self.importedObjs = [] self.copyOfObjects = [] self.transpPrtObjects = [] self.axis = Axis( [0.0, 0.0, 0.0], 1.0, 40.0 ) self.objectSelected = None self.objectIsSelected = False self.showAxis = False self.objectNr = 1 self.geoProblem = geosolver.GeometricProblem( dimension=3 ) self.selectCounter = 0 self.panel = None self.result = None self.visualiseClusters = True self.settings = Settings() self.createTriggers() self.nrOfImports = 0 def createTriggers(self): QtCore.QObject.connect(self.settings.sketcherData,QtCore.SIGNAL("pointSizeChanged"), self.updateSize) QtCore.QObject.connect(self.settings.sketcherData,QtCore.SIGNAL("fPointSizeChanged"), self.updateSize) QtCore.QObject.connect(self.settings.sketcherData,QtCore.SIGNAL("lineSizeChanged"), self.updateSize) QtCore.QObject.connect(self.settings.sketcherData,QtCore.SIGNAL("distanceSizeChanged"), self.updateSize) QtCore.QObject.connect(self.settings.sketcherData,QtCore.SIGNAL("pointColorChanged"), self.updateColor) QtCore.QObject.connect(self.settings.sketcherData,QtCore.SIGNAL("fPointColorChanged"), self.updateColor) QtCore.QObject.connect(self.settings.sketcherData,QtCore.SIGNAL("lineColorChanged"), self.updateColor) QtCore.QObject.connect(self.settings.sketcherData,QtCore.SIGNAL("angleColorChanged"), self.updateColor) QtCore.QObject.connect(self.settings.sketcherData,QtCore.SIGNAL("selectionColorChanged"), self.updateColor) QtCore.QObject.connect(self.settings.sketcherData,QtCore.SIGNAL("distanceColorChanged"), self.updateColor) def updateSize(self): for prtObject in self.prtObjects: if prtObject.objType == ObjectType.POINT: prtObject.radius = self.settings.sketcherData.pointRadius elif prtObject.objType == ObjectType.FIXED_POINT: prtObject.radius = self.settings.sketcherData.fPointRadius elif prtObject.objType == ObjectType.DISTANCE_HELPER: prtObject.radius = self.settings.sketcherData.lineRadius elif prtObject.objType == ObjectType.DISTANCE_CONSTRAINT: prtObject.radius = self.settings.sketcherData.distanceRadius # Rick 20090519 #elif prtObject.objType == ObjectType.DISTANCE_CLUSTER: # prtObject.radius = self.settings.sketcherData.distanceRadius * 1.5 # but which to choose for these? #elif prtObject.objType == ObjectType.POINT_CLUSTER: # prtObject.radius = self.settings.sketcherData.pointRadius * 1.5 # prtObject.radius = self.settings.sketcherData.fpointRadius * 1.5 def updateColor(self): for prtObject in self.prtObjects: if prtObject.objType == ObjectType.POINT: prtObject.color = self.settings.sketcherData.pointColor elif prtObject.objType == ObjectType.FIXED_POINT: prtObject.color = self.settings.sketcherData.fPointColor elif prtObject.objType == ObjectType.DISTANCE_HELPER: prtObject.color = self.settings.sketcherData.lineColor elif prtObject.objType == ObjectType.DISTANCE_CONSTRAINT: prtObject.color = self.settings.sketcherData.distanceColor elif prtObject.objType == ObjectType.ANGLE_CONSTRAINT: prtObject.color = self.settings.sketcherData.angleColor prtObject.selectColor = self.settings.sketcherData.selectColor def addObject( self, prtobject ): """ Try to add a new object to the constraint graph Parameters: prtobject - prototype object which must be added to the constraint graph """ if not prtobject.ghost: if prtobject.objType == ObjectType.POINT: self.geoProblem.add_point( prtobject.key, Vec( [prtobject.position[0], prtobject.position[1], prtobject.position[2]] ) ) elif prtobject.objType == ObjectType.FIXED_POINT: self.geoProblem.add_point( prtobject.key, Vec( [prtobject.position[0], prtobject.position[1], prtobject.position[2]] ) ) self.geoProblem.add_constraint(geosolver.FixConstraint( prtobject.key, Vec ( [prtobject.position[0], prtobject.position[1], prtobject.position[2]] ) ) ) elif prtobject.objType == ObjectType.DISTANCE_CONSTRAINT: prtobject.con = geosolver.DistanceConstraint( prtobject.pointBegin.key, prtobject.pointEnd.key, prtobject.distance ) self.geoProblem.add_constraint( prtobject.con ) elif prtobject.objType == ObjectType.ANGLE_CONSTRAINT: prtobject.con = geosolver.AngleConstraint( prtobject.pointBegin.key, prtobject.pointMiddle.key, prtobject.pointEnd.key, prtobject.angle ) self.geoProblem.add_constraint( prtobject.con ) """ if the addition of the new object succeeds add it to the list in the panel """ self.panel.addItemToSelectionList( prtobject.name, prtobject.objType ) self.prtObjects += [prtobject] self.objectNr += 1 def addClusterObject(self, clsObject): if clsObject != None: self.clsObjects += [clsObject] def addTransparentObject( self, prtobject ): self.transpPrtObjects += [prtobject] self.objectNr += 1 def __handleResult(self): """ Create the clusterobjects for the sketcher """ if self.result != None: collPoints = [] clusters = [] #self.result.flag = GeometricDecomposition.S_OVER if self.result.flag == GeometricDecomposition.I_UNDER or self.result.flag == GeometricDecomposition.S_UNDER: clusters = self.result.subs for cluster in clusters: self.__createCluster(cluster) elif self.result.flag == GeometricDecomposition.I_OVER or self.result.flag == GeometricDecomposition.S_OVER: self.__getOverConstrainedClusters(self.result, clusters) wellClusters = [] self.__getWellConstrainedClusters(self.result, wellClusters, 1) for cluster in clusters: self.__createCluster(cluster) for wellCluster in wellClusters: self.__createCluster(wellCluster) elif self.result.flag == GeometricDecomposition.OK: cluster = self.result self.__createCluster(cluster) def __createCluster(self, cluster): collPoints = [] if len(cluster.variables) > 2: ptObjects = filter(lambda x: x.key in cluster.variables, self.prtObjects) prtCluster = ClusterI(cluster.flag) prtCluster.clusterPoints = ptObjects prtCluster.update() self.clsObjects += [prtCluster] elif len(cluster.variables) == 2: collPoints = filter(lambda x: x.key in cluster.variables, self.prtObjects) if len(collPoints) == 2: prtCluster = DistanceCluster(collPoints[0], collPoints[1]) prtCluster.update() self.clsObjects += [prtCluster] elif len(cluster.variables) == 1: collPoints = filter(lambda x: x.key in cluster.variables, self.prtObjects) prtCluster = PointCluster(collPoints[0].position, collPoints[0].radius*1.5, collPoints[0]) self.clsObjects += [prtCluster] def createCluster(self, variables, constrainedness = None): """ Clusters are created from a list of variables """ prtCluster = None if len(variables) == 1: point = filter(lambda x: x.key in variables, self.prtObjects) if point != []: prtCluster = PointCluster(point[0].position, point[0].radius*1.5, point[0]) elif len(variables) == 2: collPoints = filter(lambda x: x.key in variables, self.prtObjects) if collPoints != []: prtCluster = DistanceCluster(collPoints[0], collPoints[1]) elif len(variables) > 2: prtCluster = ClusterI(constrainedness) ptObjects = filter(lambda x: x.key in variables, self.prtObjects) if ptObjects != []: prtCluster.clusterPoints = ptObjects else: prtCluster = None if prtCluster != None: prtCluster.update() self.clsObjects += [prtCluster] return prtCluster def __getOverConstrainedClusters(self,parent, ocClusters): """ Get the highest cluster levels in the tree where overconstrainedness occurs. This is a recursive function to receive the highest level overconstrained clusters. Parameters: parent - parent node ocClusters - overconstrained clusters Return: level - highest level overconstrained clusters """ overconstrainedFound = False for cluster in parent.subs: if cluster.flag == GeometricDecomposition.I_OVER or cluster.flag == GeometricDecomposition.S_OVER: self.__getOverConstrainedClusters(cluster, ocClusters) overconstrainedFound = True if not overconstrainedFound: ocClusters += [parent] def __getWellConstrainedClusters(self, parent, wellClusters, level=0, currentLevel=0): if level == 0: return currentLevel += 1 for cluster in parent.subs: if currentLevel != level: self.__getWellConstrainedClusters(cluster, wellClusters, level, currentLevel) elif cluster.flag == GeometricDecomposition.OK: wellClusters += [cluster] def getMergedConstraints(self, prtObj, withPrtObj): """ Merge two points, with its constraints. Constraints which will merge or needs to be deleted, are returned. Parameters: prtObj - the point which needs to be merged with the withPrtObj withPrtObj - the point which is merged with Return: list - list of merged or deleted constraints """ mergedDConstraints = [] mergedAConstraints = [] """ Filter all the distance constraints and distance helpers where the from and with objects are represented in. The objects are first filtered, to minimize the comparison between the constraints. """ dConstraints = filter(lambda x:(x.objType == ObjectType.DISTANCE_CONSTRAINT or x.objType == ObjectType.DISTANCE_HELPER) and (x.pointBegin == prtObj or x.pointEnd == prtObj or x.pointBegin == withPrtObj or x.pointEnd == withPrtObj), self.prtObjects) """ Now the constraints are filtered they need to be checked whether they have points in common. When this is the case and both of the constraints consist out of one of the parameters, then they should be merged. """ for dConstraint in dConstraints: for dConstraint2 in dConstraints: if ((dConstraint.pointBegin == dConstraint2.pointBegin or dConstraint.pointEnd == dConstraint2.pointEnd) or \ (dConstraint.pointEnd == dConstraint2.pointBegin or dConstraint.pointBegin == dConstraint2.pointEnd)) and \ (dConstraint.pointBegin == prtObj or dConstraint.pointEnd == prtObj) and \ (dConstraint2.pointBegin == withPrtObj or dConstraint2.pointEnd == withPrtObj): mergedDConstraints += [dConstraint] break #mergedDConstraints = self.__getMergedDistanceConstraints(mergedDConstraints, prtObj, withPrtObj) """ Handling the angle constraint is similar to the distance constraint, only it needs to be extended because the angle consists of 3 points instead of 2. """ aConstraints = filter(lambda x:x.objType == ObjectType.ANGLE_CONSTRAINT and (x.pointBegin == prtObj or x.pointMiddle == prtObj or x.pointEnd == prtObj or x.pointBegin == withPrtObj or x.pointMiddle == withPrtObj or x.pointEnd == withPrtObj), self.prtObjects) for aConstraint in aConstraints: for aConstraint2 in aConstraints: if ((aConstraint.pointBegin == aConstraint2.pointBegin or aConstraint.pointMiddle == aConstraint2.pointMiddle or aConstraint.pointEnd == aConstraint2.pointEnd) or \ (aConstraint.pointBegin == aConstraint2.pointMiddle or aConstraint.pointBegin == aConstraint2.pointEnd) or \ (aConstraint.pointMiddle == aConstraint2.pointBegin or aConstraint.pointMiddle == aConstraint2.pointEnd) or \ (aConstraint.pointEnd == aConstraint2.pointBegin or aConstraint.pointEnd == aConstraint2.pointMiddle)) and \ (aConstraint.pointBegin == prtObj or aConstraint.pointMiddle == prtObj or aConstraint.pointEnd == prtObj) and \ (aConstraint2.pointBegin == withPrtObj or aConstraint2.pointMiddle == withPrtObj or aConstraint2.pointEnd == withPrtObj): mergedAConstraints += [aConstraint] break #mergedAConstraints = self.__getMergedAngleConstraints(mergedAConstraints, prtObj, withPrtObj) mergedConstraints = mergedAConstraints + mergedDConstraints return mergedConstraints def __getMergedDistanceConstraints(self,mergedDistanceConstraints, prtObj, withPrtObj): deleteConstraints = [] dConstraints = filter(lambda x:((x.objType == ObjectType.DISTANCE_CONSTRAINT or x.objType == ObjectType.DISTANCE_HELPER) and (x.pointBegin == withPrtObj or x.pointEnd == withPrtObj)), self.prtObjects) for mConstraint in mergedDistanceConstraints: for dConstraint in dConstraints: if (mConstraint.pointBegin == dConstraint.pointBegin and mConstraint.pointEnd == dConstraint.pointEnd) or \ (mConstraint.pointBegin == dConstraint.pointEnd and mConstraint.pointEnd == dConstraint.pointBegin): deleteConstraints += [mConstraint] break return deleteConstraints def __getMergedAngleConstraints(self,mergedAngleConstraints, prtObj, withPrtObj): deleteConstraints = [] aConstraints = filter(lambda x:((x.objType == ObjectType.ANGLE_CONSTRAINT) and (x.pointBegin == withPrtObj or x.pointMiddle == withPrtObj or x.pointEnd == withPrtObj)), self.prtObjects) for mConstraint in mergedAngleConstraints: for aConstraint in aConstraints: if (mConstraint.pointBegin == aConstraint.pointBegin and mConstraint.pointMiddle == aConstraint.pointMiddle and mConstraint.pointEnd == aConstraint.pointEnd) or \ (mConstraint.pointBegin == aConstraint.pointBegin and mConstraint.pointMiddle == aConstraint.pointEnd and mConstraint.pointEnd == aConstraint.pointMiddle) or \ (mConstraint.pointBegin == aConstraint.pointMiddle and mConstraint.pointMiddle == aConstraint.pointBegin and mConstraint.pointEnd == aConstraint.pointEnd) or \ (mConstraint.pointBegin == aConstraint.pointMiddle and mConstraint.pointMiddle == aConstraint.pointEnd and mConstraint.pointEnd == aConstraint.pointMiddle) or \ (mConstraint.pointBegin == aConstraint.pointEnd and mConstraint.pointMiddle == aConstraint.pointBegin and mConstraint.pointEnd == aConstraint.pointMiddle) or \ (mConstraint.pointBegin == aConstraint.pointEnd and mConstraint.pointMiddle == aConstraint.pointMiddle and mConstraint.pointEnd == aConstraint.pointBegin): deleteConstraints += [mConstraint] break return deleteConstraints def getConstraintInfoAsText(self): if self.result != None: if self.result.flag == GeometricDecomposition.I_UNDER: return "Incidentally Underconstrained (change distance/angle values)" elif self.result.flag == GeometricDecomposition.S_UNDER: return "Structurally Underconstrained (assign more distances/angles)" elif self.result.flag == GeometricDecomposition.I_OVER: return "Incidentally Overconstrained (change distance/angle values)" elif self.result.flag == GeometricDecomposition.S_OVER: return "Structurally Overconstrained (remove distances/angles)" elif self.result.flag == GeometricDecomposition.OK: return "Well Constrained" else: return "" else: return "" def removeAllObjects(self): """ Removing all of the objects in the scene. """ self.removeObjects(self.prtObjects) self.removeAllClusters() self.objectNr = 1 def removeAllClusters(self): self.removeClusterObjects(self.clsObjects) def removeObjects( self, objects ): """ Removing the objects from the list. The objects are also removed from the constraint graph. The order of removal is important. Parameters: objects - objects that are to be removed """ for obj in objects: if obj.objType == ObjectType.ANGLE_CONSTRAINT and not obj.ghost: obj.con = self.geoProblem.get_angle( obj.pointBegin.key, obj.pointMiddle.key, obj.pointEnd.key ) self.geoProblem.rem_constraint( obj.con ) #print "removal of Angles succeeded """ for obj in objects: if obj.objType == ObjectType.DISTANCE_CONSTRAINT and not obj.ghost: #print " distance constr keys: ", obj.pointBegin.key, obj.pointEnd.key obj.con = self.geoProblem.get_distance( obj.pointBegin.key, obj.pointEnd.key ) #print " got it " self.geoProblem.rem_constraint( obj.con ) #print "removal of Distance Constraints succeeded """ for obj in objects: if obj.objType == ObjectType.POINT and not obj.ghost: self.geoProblem.rem_point( obj.key ) for obj in objects: if obj.objType == ObjectType.FIXED_POINT and not obj.ghost: self.geoProblem.rem_constraint( self.geoProblem.get_fix( obj.key ) ) #print "removal of Fixed points succeeded """ self.panel.removeItems( map( lambda x:x.name, objects ) ) self.prtObjects = filter( lambda x:x not in objects, self.prtObjects ) #print "removal of All other succeeded """ def removeClusterObjects(self, clsObjects, removeOnlyTemp=False, removeFixed=False): if removeOnlyTemp and removeFixed: self.clsObjects = filter(lambda x:x not in clsObjects or (not x.temporary or not x.fixed), self.clsObjects) elif removeOnlyTemp: self.clsObjects = filter(lambda x:x not in clsObjects or not x.temporary, self.clsObjects) else: self.clsObjects = filter(lambda x:x not in clsObjects, self.clsObjects) def deleteObject( self, removeObject ): """ Delete the object from the list of prototypes. If other objects are dependent on the deleted object, then they will be removed too. Paramaters: removeObject - remove the object from the list of prototype objects. """ dellist = [] for prtObject in self.prtObjects: if prtObject.objType == ObjectType.ANGLE_CONSTRAINT: if prtObject.pointBegin == removeObject or prtObject.pointMiddle == removeObject or prtObject.pointEnd == removeObject: dellist += [prtObject] if prtObject.objType == ObjectType.DISTANCE_CONSTRAINT or prtObject.objType == ObjectType.DISTANCE_HELPER: if prtObject.pointBegin == removeObject or prtObject.pointEnd == removeObject: dellist += [prtObject] dellist += [removeObject] self.removeObjects( dellist ) def deleteSelectedObjects(self): selectedObjects = self.getSelectedObjects() for selObj in selectedObjects: exists = filter(lambda x:x==selObj, self.prtObjects) if len(exists) > 0: self.deleteObject(selObj) def getSelectedObjects(self): return filter(lambda x:x.selected, self.prtObjects) def updateObjects( self ): """ Update all the objects, this might also be a ghost object (object that does not really exist, but is visualised). """ for prtObject in self.prtObjects: prtObject.update() if prtObject.objType == ObjectType.POINT and not prtObject.ghost: pointPosition = self.geoProblem.get_point( prtObject.key ) pointPosition[0] = prtObject.position[0] pointPosition[1] = prtObject.position[1] pointPosition[2] = prtObject.position[2] elif prtObject.objType == ObjectType.DISTANCE_CONSTRAINT and not prtObject.ghost: if prtObject.distance != None: self.updateConstraint(prtObject, prtObject.distance) elif prtObject.objType == ObjectType.ANGLE_CONSTRAINT and not prtObject.ghost: if prtObject.angle != None: self.updateConstraint(prtObject, prtObject.angle) for transpPrtObject in self.transpPrtObjects: transpPrtObject.update() def updateConstraint( self, obj, value ): """ Update an existing constraint, with a new value. This value is also passed to the constraint graph. Parameters: obj - prototype object which needs to be updated. value - the new value for the object. """ if obj.objType == ObjectType.DISTANCE_CONSTRAINT: obj.distance = value dConstraint = self.geoProblem.get_distance( obj.pointBegin.key, obj.pointEnd.key ) if dConstraint != None: dConstraint._value = obj.distance elif obj.objType == ObjectType.ANGLE_CONSTRAINT: obj.angle = value dAngle = self.geoProblem.get_angle( obj.pointBegin.key, obj.pointMiddle.key, obj.pointEnd.key ) if dAngle != None: dAngle._value = math.radians(obj.angle) def updateConstraintKeys(self, oldKey, newKey): """ Update the keyvalues of a constraint. To update a key value, the constraints must first be found which have the old key. Once found it must be replaced with the new one. The constraint with the old key is therefore first to be removed from the graph and then to be added again with the new key. Parameters: oldKey - the old key of the constraint, used for lookup of the constraints newKey - the new key which has to be set for the constraints """ for prtObj in self.prtObjects: if prtObj.objType == ObjectType.DISTANCE_CONSTRAINT: if prtObj.pointBegin.key == oldKey or prtObj.pointEnd.key == oldKey: dConstraint = self.geoProblem.get_distance(prtObj.pointBegin.key, prtObj.pointEnd.key) if dConstraint != None: self.geoProblem.rem_constraint(dConstraint) if prtObj.pointBegin.key == oldKey: prtObj.con = geosolver.DistanceConstraint( newKey, prtObj.pointEnd.key, prtObj.distance ) elif prtObj.pointEnd.key == oldKey: prtObj.con = geosolver.DistanceConstraint( prtObj.pointBegin.key, newKey, prtObj.distance ) self.geoProblem.add_constraint(prtObj.con) elif prtObj.objType == ObjectType.ANGLE_CONSTRAINT: if prtObj.pointBegin.key == oldKey or prtObj.pointMiddle.key == oldKey or prtObj.pointEnd.key == oldKey: aConstraint = self.geoProblem.get_angle(prtObj.pointBegin.key, prtObj.pointMiddle.key, prtObj.pointEnd.key) self.geoProblem.rem_constraint(aConstraint) if aConstraint != None: if prtObj.pointBegin.key == oldKey: prtObj.con = geosolver.AngleConstraint( newKey, prtObj.pointMiddle.key, prtObj.pointEnd.key, prtObj.angle ) elif prtObj.pointMiddle.key == oldKey: prtObj.con = geosolver.AngleConstraint( prtObj.pointBegin.key, newKey, prtObj.pointEnd.key, prtObj.angle ) elif prtObj.pointEnd.key == oldKey: prtObj.con = geosolver.AngleConstraint( prtObj.pointBegin.key, prtObj.pointMiddle.key, newKey, prtObj.angle ) self.geoProblem.add_constraint(prtObj.con) def getImportedObjsByKey(self, key): for impObj in self.importedObjs: if (impObj.objType == ObjectType.POINT or impObj.objType == ObjectType.FIXED_POINT) and impObj.importKey == key: return impObj def addConstraint( self, obj ): """ Add a constraint to an existing object. Parameters: obj - object for which the constraint must be added. """ if obj.objType == ObjectType.POINT: self.geoProblem.add_constraint( geosolver.FixConstraint( obj.key, Vec( [obj.position[0], obj.position[1], obj.position[2]] ) ) ) newObj = FixedPoint( obj.key, obj.position, obj.radius ) self.replaceObject( obj, newObj ) def removeConstraint( self, obj ): """ Remove a constraint from an existing object. Parameters: obj - object from which the constraint must be removed. """ if obj.objType == ObjectType.FIXED_POINT: self.geoProblem.rem_constraint( self.geoProblem.get_fix( obj.key ) ) newObj = Point( obj.key, obj.position, obj.radius ) self.replaceObject( obj, newObj ) def replaceObject( self, oldObj, newObj ): """ Replace object, when a constraint is added to an existing object it is replaced with a new type of object. Because of this a new instance is created, which is not known by other prototype objects, so al the references must be set correct. Parameters: oldObj - old object where some objects may reference to. newObj - new object where must be referenced to. """ for prtObject in self.prtObjects: prtObject.setNewReference( oldObj, newObj ) if self.objectSelected == oldObj: self.objectSelected = newObj self.prtObjects = filter( lambda x:x is not oldObj, self.prtObjects ) self.prtObjects += [newObj] def replaceConstrainedObject( self, oldCstrObj, newCstrObj ): """ Replace object, when a constraint is added to an existing object it is replaced with a new type of object. Because of this a new instance is created, which is not known by other prototype objects, so al the references must be set correct. Parameters: oldCstrObj - old object where some objects may reference to. newCstrObj - new object where must be referenced to. """ mergedConstraints = self.getMergedConstraints(oldCstrObj, newCstrObj) self.removeObjects(mergedConstraints) self.updateConstraintKeys(oldCstrObj.key, newCstrObj.key) for prtObject in self.prtObjects: prtObject.setNewReference( oldCstrObj, newCstrObj ) if self.objectSelected == oldCstrObj: self.objectSelected = newCstrObj self.prtObjects = filter( lambda x:x is not oldCstrObj, self.prtObjects ) self.removeObjects([oldCstrObj]) def deselectObject( self ): """ Deselect the selected object, which is selected """ if self.objectSelected != None: self.objectSelected.selected = False self.objectSelected = None self.axis.selected = -1 self.showAxis = False self.panel.updateEdit() def deselectAllObjects(self, dsClObjects=True, onlyTemp=False): """ Deselect all of the objects. """ for prtObject in self.prtObjects: if onlyTemp: if prtObject.temporarySelected: prtObject.selected = False prtObject.temporarySelected = False else: prtObject.selected = False if dsClObjects: for clsObject in self.clsObjects: clsObject.selected = False def drawObjects( self ): """ Draw all of the objects. """ for prtObject in self.prtObjects: if prtObject.selected and prtObject.showAxis and self.showAxis: self.axis.draw() if prtObject.isVisible: prtObject.draw() if self.visualiseClusters: for clsObject in self.clsObjects: if clsObject.isVisible: clsObject.draw() def drawTransparentObjects( self ): """ Draw the transparent objects. (Obsolete) """ for transpPrtObject in self.transpPrtObjects: if transpPrtObject.selected and transpPrtObject.showAxis and self.showAxis: self.axis.draw() transpPrtObject.draw() def drawWithPicking( self ): """ Draw objects with picking, so they can be selected with the mouse. This is done by setting an unique id for every object. """ self.selectCounter=0 for prtObject in self.prtObjects: if not prtObject.ghost: glPushName( self.selectCounter ) prtObject.selectionId = self.selectCounter if prtObject.selected: self.axis.drawWithPicking() prtObject.draw() glPopName() self.selectCounter += 1 def drawTransparentWithPicking( self ): """ Draw transparent objects with picking, so they can be selected with the mouse. This is done by setting an unique id for every object. """ for transpPrtObject in self.transpPrtObjects: glPushName( self.selectCounter ) transpPrtObject.selectionId = self.selectCounter if transpPrtObject.selected: self.axis.drawWithPicking() transpPrtObject.draw() glPopName() self.selectCounter += 1 def setVisibilityOfObjects(self, visible=True): for prtObject in self.prtObjects: prtObject.isVisible = visible for clsObject in self.clsObjects: clsObject.isVisible = visible def setObjectVisibilityByClusters(self): self.setVisibilityOfObjects(False) found = False for clsObject in self.clsObjects: if clsObject.fixed == True: found = True if clsObject.objType == ObjectType.POINT: points = filter(lambda x:(x.objType == ObjectType.POINT or x.objType == ObjectType.FIXED_POINT) and clsObject.relPoint==x, self.prtObjects) for point in points: point.isVisible = True elif clsObject.objType == ObjectType.DISTANCE_HELPER: distances = filter(lambda x:(x.objType == ObjectType.DISTANCE_CONSTRAINT or x.objType == ObjectType.DISTANCE_HELPER) and ((clsObject.pointBegin==x.pointBegin and clsObject.pointEnd == x.pointEnd) or (clsObject.pointBegin==x.pointEnd and clsObject.pointEnd == x.pointBegin)), self.prtObjects) for distance in distances: distance.isVisible = True distance.pointBegin.isVisible = True distance.pointEnd.isVisible = True elif clsObject.objType == ObjectType.CLUSTER: for point in clsObject.getClusterPoints(): pointObjs = filter(lambda x:(x.objType == ObjectType.POINT or x.objType == ObjectType.FIXED_POINT) and x == point,self.prtObjects) for pointObj in pointObjs: pointObj.isVisible = True distances = filter(lambda x:x.objType == ObjectType.DISTANCE_CONSTRAINT or x.objType == ObjectType.DISTANCE_HELPER, self.prtObjects) for distance in distances: pBegin = filter(lambda x:x == distance.pointBegin, clsObject.getClusterPoints()) pEnd = filter(lambda x:x == distance.pointEnd, clsObject.getClusterPoints()) if len(pBegin) > 0 and len(pEnd) > 0: distance.isVisible = True angles = filter(lambda x:x.objType == ObjectType.ANGLE_CONSTRAINT, self.prtObjects) for angle in angles: pBegin = filter(lambda x:x == angle.pointBegin, clsObject.getClusterPoints()) pMiddle = filter(lambda x:x == angle.pointMiddle, clsObject.getClusterPoints()) pEnd = filter(lambda x:x == angle.pointEnd, clsObject.getClusterPoints()) if len(pBegin) > 0 and len(pMiddle) > 0 and len(pEnd) > 0: angle.isVisible = True for clsObject in self.clsObjects: if clsObject.fixed == True: clsObject.isVisible = True if not found: self.setVisibilityOfObjects(True) def isObjectSelected( self ): """ Check whether an object is selected. """ return self.objectIsSelected def setObjectSelected( self, selected ): """ Set whether the object is selected. Parameters: selected - whether the selected object is activated or not. """ self.objectIsSelected = selected def selectObjectByName( self, name ): """ Select an object by name. This name is NOT the key of an object, but the name which is filled in by the user in the control panel. Parameters: name - the name of the object which must be selected """ for prtObject in self.prtObjects: if prtObject.name == name: self.deselectObject() self.objectSelected = prtObject self.objectSelected.selected = True break def selectObject(self, obj): """ Select the given object. Parameters: obj - select this object. """ self.deselectObject() self.objectSelected = obj self.objectSelected.selected = True self.panel.selectItemByName( obj.name ) def selectObjectsByKeys(self, variables): """ Select multiple objects by their keys. These are unique and are the same as the keys in the solver. Parameters: variables - keys which needs to be selected """ #self.deselectAllObjects() for prtobject in self.prtObjects: keyfound = filter( lambda x:x == prtobject.key, variables ) if keyfound: prtobject.selected = True def selectClusterObjectByKeys(self, variables): """ Get a clusterObject based on the keys given Parameters: variables - key that must be part of the clusterobject """ for clsObject in self.clsObjects: clsPoints = clsObject.getClusterPoints() if len(variables) == len(clsPoints): variablesInCluster = True for variable in variables: if len(filter(lambda x:x.key == variable, clsPoints)) == 0: variablesInCluster = False break if variablesInCluster: return clsObject return None def getObjectByKey( self, key ): """ Get an object by its unique key. Parameters: key - key of an object """ for prtObject in self.prtObjects: if prtObject.key == key: return prtObject return None def setSelectedObject( self, selectionId, bOnlySelect=False, bDeselectSelected = True ): """ Set a selected object, by the selection id obtained from OpenGL. Parameters: selectionId - the selection id, which is the object that the user has chosen. bOnlySelect - if it must only be highlighted, but further actions are not allowed. """ found = False for prtObject in self.prtObjects: if bDeselectSelected: prtObject.selected = False if prtObject.selectionId == selectionId[0][0] and not prtObject.ghost: found = True prtObject.selected = True if not bOnlySelect: self.objectSelected = prtObject elif prtObject.isMovable: prtObject.selected = False self.panel.updateSelection = False self.panel.selectItemByName( prtObject.name ) self.panel.updateSelection = True if not found: for transPrtObject in self.transpPrtObjects: transPrtObject.selected = False if transPrtObject.selectionId == selectionId[0][0]: found = True transPrtObject.selected = True if not bOnlySelect: self.objectSelected = transPrtObject elif prtObject.isMovable: prtObject.selected = False def changeSelection(self, selectionId): for prtObject in self.prtObjects: found = False if prtObject.selectionId == selectionId[0][0]: if prtObject.selected: prtObject.selected = False else: prtObject.selected = True found = True break def setSelectedObjects( self, selectionIds, deselectObjects=True, temporarySel=False ): for prtObject in self.prtObjects: found = False for selectionId in selectionIds: if prtObject.selectionId == selectionId[0]: prtObject.selected = True if temporarySel: prtObject.temporarySelected = True found = True break if not found and deselectObjects: prtObject.selected = False def convertTemporySelection(self): for prtObject in self.prtObjects: if prtObject.temporarySelected: prtObject.temporarySelected = False def setPanel( self, panel ): """ Set the panel in the main window for access, for updating information Parameters: panel - panel from the main window. """ self.panel = panel def isNameUnique( self, oldName, newName ): """ Check whether the name given by the user is unique. Parameters: oldName - the old name for the object. newName - the new name for the object. Return: boolean - whether the name is unique or not. """ for prtObject in self.prtObjects: if prtObject.name == newName and newName != oldName: return False return True def setObjectName( self, oldName, newName ): """ Set a new name for an object. Parameters: oldName - old name of an object for comparison newName - new name for an object """ for prtObject in self.prtObjects: if prtObject.name == oldName: prtObject.name = newName return True return False def showClusters(self): if self.visualiseClusters: self.visualiseClusters = False else: self.visualiseClusters = True def getParameterRange(self, prtObject): if self.result != None: if self.result.flag == GeometricDecomposition.OK and prtObject.parameterRange == []: if prtObject.objType == ObjectType.DISTANCE_CONSTRAINT: dConstraint = self.geoProblem.get_distance(prtObject.pointBegin.key, prtObject.pointEnd.key) prtObject.parameterRange = geosolver.prange.sample_prange(self.geoProblem, dConstraint, 0.0, prtObject.distance+100.0,0.5) elif prtObject.objType == ObjectType.ANGLE_CONSTRAINT: aConstraint = self.geoProblem.get_angle(prtObject.pointBegin.key, prtObject.pointMiddle.key, prtObject.pointEnd.key) prtObject.parameterRange = geosolver.prange.sample_prange(self.geoProblem, aConstraint, 0.0, 360.0,0.1) return prtObject.parameterRange elif self.result.flag == GeometricDecomposition.OK and prtObject.parameterRange != []: return prtObject.parameterRange return [] def resetParameterRange(self): for prtObj in self.prtObjects: if prtObj.objType == ObjectType.DISTANCE_CONSTRAINT or prtObj.objType == ObjectType.ANGLE_CONSTRAINT: prtObj.parameterRange = [] def solve( self ): """ Solve the system of GCS. If there is a solution, then this is provided in the Solution View. The decomposition is shown in the Decomposition View. """ print self.geoProblem self.removeAllClusters() self.resetParameterRange() self.setVisibilityOfObjects(True) geoSolver = geosolver.GeometricSolver( self.geoProblem ) self.result = geoSolver.get_result() self.__handleResult() # 20090521 - geoSolver is deleted in this context, but it is not deleted because geoProblem refers to it via Listener interface # 20090521 - now fixed this bug print self.result print "solve finished" def save(self, domDocument ): pointNode = QtXml.QDomElement( domDocument.createElement( "Prototypes" ) ) pointNode.setAttribute( "number", self.objectNr ) pointNode.setAttribute( "numberOfImports", self.nrOfImports ) return pointNode def load( self, domElements ): """ Load the objects from a XML file. Parameters: domElements: different saved objects. """ elements = domElements.firstChild() while not elements.isNull(): element = elements.toElement() if not element.isNull(): prtObject = None if element.tagName() == "Point": prtObject = Point("", Vec([0.0, 0.0, 0.0]), 5.0) elif element.tagName() == "FixedPoint": prtObject = FixedPoint("", Vec([0.0, 0.0, 0.0]), 5.0) elif element.tagName() == "AngleConstraint": prtObject = AngleConstraint("", None, None, None) elif element.tagName() == "Distance": prtObject = Distance("", None, None) elif element.tagName() == "DistanceConstraint": prtObject = DistanceConstraint("", None, None) elif element.tagName() == "Prototypes": self.setObjectNumber(element) else: raise StandardError, "Unable to load unkown object", element.tagName() if prtObject != None: prtObject.load(element) self.addObject(prtObject) elements = elements.nextSibling() self.updateObjects() def importScene(self, domElements): """ Import the objects from a XML file. Parameters: domElements: different saved objects. """ elements = domElements.firstChild() while not elements.isNull(): element = elements.toElement() if not element.isNull(): prtObject = None if element.tagName() == "Point": prtObject = Point("", Vec([0.0, 0.0, 0.0]), 5.0) elif element.tagName() == "FixedPoint": prtObject = FixedPoint("", Vec([0.0, 0.0, 0.0]), 5.0) elif element.tagName() == "AngleConstraint": prtObject = AngleConstraint("", None, None, None) elif element.tagName() == "Distance": prtObject = Distance("", None, None) elif element.tagName() == "DistanceConstraint": prtObject = DistanceConstraint("", None, None) else: raise StandardError, "Unable to import unkown object", element.tagName() if prtObject != None: prtObject.importItem(element, self.nrOfImports, self.objectNr ) self.importedObjs += [prtObject] self.addObject(prtObject) elements = elements.nextSibling() self.updateObjects() self.nrOfImports += 1 self.importedObjs = [] # Rick 20090522 def setProblem(self, problem): """ Import the objects from a GeometricProblem. Parameters: problem: a GeometricProblem instance """ # remove all objects first # self.removeAllObjects() # add point variabels and store a mapping map = {} vars = problem.cg.variables(); for var in vars: if problem.has_point(var): prtObject = Point(var, Vec(problem.get_point(var)), 5.0) map[var]=prtObject self.addObject(prtObject) else: raise StandardError, "Unable to import non-point variable: "+str(var) # add constraints constraints = problem.cg.constraints(); count = 0 for con in constraints: if isinstance(con, geosolver.DistanceConstraint): name = "dist_"+str(con.variables()[0])+"_"+str(con.variables()[1]) count += 1 prtObject = DistanceConstraint(name, map[con.variables()[0]], map[con.variables()[1]] ) self.addObject(prtObject) elif isinstance(con, geosolver.AngleConstraint): name = "angle_"+str(con.variables()[0])+"_"+str(con.variables()[1])+str(con.variables()[2]) count += 1 prtObject = AngleConstraint(name, map[con.variables()[0]], map[con.variables()[1]], map[con.variables()[2]] ) self.addObject(prtObject) else: raise StandardError, "Unable to import constraint: "+str(con) # compute object visulisation, etc self.updateObjects() def setObjectNumber(self, domElement): self.objectNr = domElement.attribute("number", "").toInt()[0] self.nrOfImports = domElement.attribute("numberOfImports", "").toInt()[0] class Object: """ The general prototype object class. All objects which must be visualised, or have a connection with the constraint solver must be subclassed from the Object class. """ def __init__( self ): self.settings = Settings() self.selectionId = 0 self.selectColor = self.settings.sketcherData.selectColor self.selected = False self.temporarySelected = False self.quadric = gluNewQuadric() gluQuadricNormals(self.quadric, GLU_SMOOTH) self.ghost = False self.showAxis = False self.isMovable = False self.isVisible = True def draw( self ): """ A virtual draw function, for drawing the object. """ raise NotImplementedError( caller + ' must be implemented in subclass' ) def clone( self ): """ A virtual clone function, to clone an object. """ raise NotImplementedError( caller + ' must be implemented in subclass' ) def update( self ): """ A virtual update function, to update an object. """ raise NotImplementedError( caller + ' must be implemented in subclass' ) def setNewReference( self, oldObj, newObj ): """ A virtual set new reference function, to dereference an old object and reference to the new one. Parameters: oldObj - old object which must be dereferenced newObj - new object where must be referenced to """ raise NotImplementedError( caller + ' must be implemented in subclass' ) def load(self, domElement): """ A virtual load function to load the object from a XML file and set the initial values. Parameters: domElement - an element which contains information about an object. """ raise NotImplementedError( caller + ' must be implemented in subclass' ) def save( self, domDocument ): """ A virtual save function to save the object to a XML file. Parameters: domDocument - a document where must be saved to. """ raise NotImplementedError( caller + ' must be implemented in subclass' ) def importItem(self, domElement, nrOfImports=0, objNr=0): """ A virtual importItem function to import the object from a XML file into the scene. Parameters: domElement - an element which contains information about an object. nrOfImports - a number added to the name of the import, to make it unique objNr - total number of objects already in the scene """ raise NotImplementedError( caller + ' must be implemented in subclass' ) class Point( Object ): def __init__( self, name, position, radius ): Object.__init__( self ) self.name = name self.key = name self.importKey = name self.position = position self.radius = self.settings.sketcherData.pointRadius self.color = self.settings.sketcherData.pointColor self.showAxis = True self.objType = ObjectType.POINT self.fixed = False self.ghost = False self.needUpdate = True def draw( self ): if self.selected: glColor3fv( [self.selectColor.redF(),self.selectColor.greenF(), self.selectColor.blueF()]) else: glColor3fv( [self.color.redF(),self.color.greenF(), self.color.blueF()]) glPushMatrix() glTranslatef( self.position[0], self.position[1], self.position[2] ) gluSphere( self.quadric, self.radius, 10, 10 ) glPopMatrix() def updatePosition( self, translation ): self.position[0] += translation[0] self.position[1] += translation[1] self.position[2] += translation[2] def setPosition( self, position ): self.position[0] = position[0] self.position[1] = position[1] self.position[2] = position[2] def update( self ): pass def setNewReference( self, oldObj, newObj ): pass def clone( self, **attr ): obj = deepcopy( self ) obj.__dict__.update( attr ) return obj def setTexture( self, texture ): pass def load (self, domElement): self.key = str(domElement.attribute("key", "")) self.name = str(domElement.attribute("name", "")) self.position[0] = domElement.attribute("posX", "").toFloat()[0] self.position[1] = domElement.attribute("posY", "").toFloat()[0] self.position[2] = domElement.attribute("posZ", "").toFloat()[0] def importItem(self, domElement, nrOfImports=0, objNr=0): self.key = "p" + str(objNr) self.importKey = str(domElement.attribute("key", "")) self.name = str(domElement.attribute("name", "")) self.name += "_" + str(nrOfImports) self.position[0] = domElement.attribute("posX", "").toFloat()[0] self.position[1] = domElement.attribute("posY", "").toFloat()[0] self.position[2] = domElement.attribute("posZ", "").toFloat()[0] def save( self, domDocument ): pointNode = QtXml.QDomElement( domDocument.createElement( "Point" ) ) pointNode.setAttribute( "key", self.key ) pointNode.setAttribute( "name", self.name ) pointNode.setAttribute( "posX", str(self.position[0])) pointNode.setAttribute( "posY", str(self.position[1]) ) pointNode.setAttribute( "posZ", str(self.position[2]) ) return pointNode class FixedPoint( Point ): def __init__( self, name, position, radius ): Point.__init__( self, name, position, radius ) self.objType = ObjectType.FIXED_POINT self.radius = self.settings.sketcherData.fPointRadius self.color = self.settings.sketcherData.fPointColor def draw( self ): if self.selected: glColor3fv( [self.selectColor.redF(),self.selectColor.greenF(), self.selectColor.blueF()]) else: glColor3fv( [self.color.redF(),self.color.greenF(), self.color.blueF()]) glPushMatrix() glTranslatef( self.position[0], self.position[1], self.position[2] ) gluSphere( self.quadric, self.radius, 10, 10 ) glPopMatrix() def load (self, domElement): Point.load(self, domElement) def importItem(self, domElement, nrOfImports=0, objNr=0): Point.importItem(self, domElement, imported=False, nrOfImports=0, objNr=0) self.key = "f" + str(objNr) def save( self, domDocument ): pointNode = QtXml.QDomElement( domDocument.createElement( "FixedPoint" ) ) pointNode.setAttribute( "key", self.key ) pointNode.setAttribute( "name", self.name ) pointNode.setAttribute( "posX", str(self.position[0]) ) pointNode.setAttribute( "posY", str(self.position[1]) ) pointNode.setAttribute( "posZ", str(self.position[2]) ) return pointNode class AngleConstraint( Object ): def __init__( self, name, pBegin, pMiddle, pEnd ): Object.__init__( self ) self.pointBegin = pBegin self.pointMiddle = pMiddle self.pointEnd = pEnd self.key = name self.name = name self.realAngle = 0.0 self.angle = None self.con = None self.showAxis = False self.objType = ObjectType.ANGLE_CONSTRAINT self.color = self.settings.sketcherData.angleColor self.height = 25.0 self.angleDisc = Quaternion() self.rotAngleMatrix = Numeric.identity(4) self.normVec = Vec([0.0, 0.0, 0.0]) self.parameterRange=[] self.fixed = False # rick 20090519 self.height1 = None self.height2 = None self.orientation1 = Quaternion() self.orientation2 = Quaternion() self.radius = 0.0 # self.settings.sketcherData.distanceRadius self.update() def draw( self ): """ Visualisation of the angle constraint """ # first draw disk glPushMatrix() # Rick 20090519 glMultMatrixd doesn't work properly, see above # glMultMatrixd( self.rotAngleMatrix ) axis = -self.angleDisc.axis() angle = self.angleDisc.angle() pos = self.pointMiddle.position glTranslate(pos[0],pos[1],pos[2]) glRotate(angle*180/math.pi, axis[0], axis[1], axis[2]) #glDisable( GL_DEPTH_TEST ) glEnable ( GL_BLEND ) if self.selected: glColor4fv( [self.selectColor.redF(),self.selectColor.greenF(), self.selectColor.blueF(),self.selectColor.alphaF()]) else: glColor4fv( [self.color.redF(),self.color.greenF(), self.color.blueF(), self.color.alphaF()]) """ Visualisate angle constraint as transparent partial disk """ gluPartialDisk(self.quadric, self.pointMiddle.radius, self.height, 20, 10, 0.0, self.realAngle) glDisable ( GL_BLEND ) #glEnable( GL_DEPTH_TEST ) glPopMatrix() glPushMatrix() self.drawAxis() glPopMatrix() # draw cylinder1 glPushMatrix() if self.selected: glColor3fv( [self.selectColor.redF(),self.selectColor.greenF(), self.selectColor.blueF()]) else: glColor3fv( [self.color.redF(),self.color.greenF(), self.color.blueF()]) if self.height1 == 0.0: self.height1 = 0.001 if self.radius == 0: glBegin(GL_LINES) glVertex3f(self.pointBegin.position[0], self.pointBegin.position[1], self.pointBegin.position[2]) glVertex3f(self.pointMiddle.position[0], self.pointMiddle.position[1], self.pointMiddle.position[2]) glEnd() else: #glMultMatrixd( self.rotMatrix ) # there is either a bug in glMultMatrixd or in Quaternion.getRotationMatrix # or maybe it has to do with Numpy -> Numeric transition? # now doing it with glTranslate/Rotate axis = -self.orientation1.axis() angle = self.orientation1.angle() pos = self.pointBegin.position glTranslate(pos[0],pos[1],pos[2]) glRotate(angle*180/math.pi, axis[0], axis[1], axis[2]) #print "gluCylinder ", self.radius, self.radius, self.height, 10, 10 gluCylinder( self.quadric, float(self.radius), float(self.radius), float(self.height1), 10, 10 ) glPopMatrix() # draw cylinder2 glPushMatrix() if self.selected: glColor3fv( [self.selectColor.redF(),self.selectColor.greenF(), self.selectColor.blueF()]) else: glColor3fv( [self.color.redF(),self.color.greenF(), self.color.blueF()]) if self.height2 == 0.0: self.height2 = 0.001 if self.radius == 0: glBegin(GL_LINES) glVertex3f(self.pointMiddle.position[0], self.pointMiddle.position[1], self.pointMiddle.position[2]) glVertex3f(self.pointEnd.position[0], self.pointEnd.position[1], self.pointEnd.position[2]) glEnd() else: #glMultMatrixd( self.rotMatrix ) # there is either a bug in glMultMatrixd or in Quaternion.getRotationMatrix # or maybe it has to do with Numpy -> Numeric transition? # now doing it with glTranslate/Rotate axis = -self.orientation2.axis() angle = self.orientation2.angle() pos = self.pointMiddle.position glTranslate(pos[0],pos[1],pos[2]) glRotate(angle*180/math.pi, axis[0], axis[1], axis[2]) #print "gluCylinder ", self.radius, self.radius, self.height, 10, 10 gluCylinder( self.quadric, float(self.radius), float(self.radius), float(self.height2), 10, 10 ) glPopMatrix() def drawAxis( self ): pass def update( self ): """ The vector is calculated where to rotate to. This is the up-vector relative to the plane where the angle constraint must be visualised """ # compute transform for cylinder1: height1 and orientation1 self.height1 = (self.pointBegin.position - self.pointMiddle.position ).norm() if self.height1 == 0.0: self.height1 = 0.001 diff = self.pointMiddle.position - self.pointBegin.position self.orientation1.fromDirection( Vec( [0.0, 0.0, self.height1] ), diff ) # compute transform for cylinder2: height2 and orientation2 self.height2 = (self.pointEnd.position - self.pointMiddle.position ).norm() if self.height2 == 0.0: self.height2 = 0.001 diff = self.pointEnd.position - self.pointMiddle.position self.orientation2.fromDirection( Vec( [0.0, 0.0, self.height2] ), diff ) # compute transform for disk diffToBegin = self.pointBegin.position - self.pointMiddle.position diffToEnd = self.pointEnd.position - self.pointMiddle.position diffBeginEnd = diffToEnd - diffToBegin vMiddlePoint = self.pointMiddle.position - self.pointBegin.position vMiddleMiddle = (diffBeginEnd*0.5) - vMiddlePoint diffBeginEnd.normalize() vAngleStart = copy(vMiddleMiddle) vMiddleMiddle.normalize() if diffToBegin.norm() > 1e10: diffToBegin.normalize() #else: # diffToBegin = Vec([1.0,0,0]) if diffToEnd.norm() > 1e10: diffToEnd.normalize() #else: # diffToEnd = Vec([0,1,0]) """ Retrieve the up-vector, and rotate based upon this vector and the starting direction """ rotateTo = vMiddleMiddle.cross(diffBeginEnd) self.angleDisc.fromDirection(Vec( [0.0, 0.0, 1.0] ), rotateTo ) """ Retrieve the part of the disk that needs to be visualised """ angleFromTo = Quaternion() angleFromTo.fromDirection( diffToBegin, diffToEnd ) self.realAngle = math.degrees( angleFromTo.angle() ) if not self.ghost and not self.fixed: self.angle = self.realAngle """ Rotate to the location from where to start the visualisation of the partial disk """ angleStartPartialDisk = Quaternion() self.inverseRot = self.angleDisc.inverseRotate(Vec([0.0, 1.0, 0.0])) if self.angle > 180.0: self.realAngle = 360.0-self.realAngle angleStartPartialDisk.fromDirection(self.inverseRot, diffToBegin) else: angleStartPartialDisk.fromDirection(self.inverseRot, diffToEnd) self.angleDisc = self.angleDisc * angleStartPartialDisk """ Translate the partial disk to the middle point and store the matrix """ self.angleDisc.getRotationMatrix( self.rotAngleMatrix ) self.rotAngleMatrix[3][0] = self.pointMiddle.position[0] self.rotAngleMatrix[3][1] = self.pointMiddle.position[1] self.rotAngleMatrix[3][2] = self.pointMiddle.position[2] self.rotAngleMatrix[3][3] = 1.0 def setNewReference( self, oldObj, newObj ): """ When objects change properties new references must be set """ if self.pointBegin == oldObj: self.pointBegin = newObj elif self.pointMiddle == oldObj: self.pointMiddle = newObj elif self.pointEnd == oldObj: self.pointEnd = newObj def clone( self, **attr ): """ Create a copy of this object """ obj = deepcopy( self ) obj.__dict__.update( attr ) return obj def load (self, domElement): """ Load the angle constraint from the xml-file """ self.key = str(domElement.attribute("key", "")) self.name = str(domElement.attribute("name", "")) pBeginKey = str(domElement.attribute("pBeginKey", "")) pMiddleKey = str(domElement.attribute("pMiddleKey", "")) pEndKey = str(domElement.attribute("pEndKey", "")) self.pointBegin = PrototypeManager().getObjectByKey(pBeginKey) self.pointMiddle = PrototypeManager().getObjectByKey(pMiddleKey) self.pointEnd = PrototypeManager().getObjectByKey(pEndKey) self.angle = domElement.attribute("angle", "").toDouble()[0] if domElement.attribute("fixed", "") == "True": self.fixed = True else: self.fixed = False def importItem(self, domElement, nrOfImports=0, objNr=0): self.key = "a" + str(objNr) self.importKey = str(domElement.attribute("key", "")) self.name = str(domElement.attribute("name", "")) self.name += "_" + str(nrOfImports) pBeginKey = str(domElement.attribute("pBeginKey", "")) pMiddleKey = str(domElement.attribute("pMiddleKey", "")) pEndKey = str(domElement.attribute("pEndKey", "")) self.pointBegin = PrototypeManager().getImportedObjsByKey(pBeginKey) self.pointMiddle = PrototypeManager().getImportedObjsByKey(pMiddleKey) self.pointEnd = PrototypeManager().getImportedObjsByKey(pEndKey) self.angle = domElement.attribute("angle", "").toDouble()[0] if domElement.attribute("fixed", "") == "True": self.fixed = True else: self.fixed = False def save( self, domDocument ): """ Save the angle constraint to the xml-file """ angleNode = QtXml.QDomElement( domDocument.createElement( "AngleConstraint" ) ) angleNode.setAttribute( "pBeginKey", self.pointBegin.key ) angleNode.setAttribute( "pMiddleKey", self.pointMiddle.key ) angleNode.setAttribute( "pEndKey", self.pointEnd.key ) angleNode.setAttribute( "key", self.key ) angleNode.setAttribute( "name", self.name ) angleNode.setAttribute( "angle", str(self.angle) ) angleNode.setAttribute( "fixed", str(self.fixed) ) return angleNode class Distance( Object ): def __init__( self, name, pBegin, pEnd ): Object.__init__( self ) self.pointBegin = pBegin self.pointEnd = pEnd self.radius = self.settings.sketcherData.lineRadius self.name = name self.key = name self.orientation = Quaternion() self.rotMatrix = Numeric.identity(4) self.height = 0.1 self.fixed = False self.showAxis = False self.objType = ObjectType.DISTANCE_HELPER self.color = self.settings.sketcherData.lineColor if self.pointBegin != None and self.pointEnd != None: self.distance = (self.pointBegin.position - self.pointEnd.position ).norm() else: self.distance = 0.0 def update( self ): self.height = (self.pointBegin.position - self.pointEnd.position ).norm() if self.height == 0.0: self.height = 0.001 diff = self.pointEnd.position - self.pointBegin.position self.orientation.fromDirection( Vec( [0.0, 0.0, self.height] ), diff ) #print "cylinder orientation:",self.orientation #print "axis:",self.orientation.axis(), "angle:", self.orientation.angle() self.orientation.getRotationMatrix( self.rotMatrix ) self.rotMatrix[3][0] = self.pointBegin.position[0] self.rotMatrix[3][1] = self.pointBegin.position[1] self.rotMatrix[3][2] = self.pointBegin.position[2] self.rotMatrix[3][3] = 1.0 #print "cylinder rotMatix:",self.rotMatrix def draw( self ): glPushMatrix() if self.selected: glColor3fv( [self.selectColor.redF(),self.selectColor.greenF(), self.selectColor.blueF()]) else: glColor3fv( [self.color.redF(),self.color.greenF(), self.color.blueF()]) if self.height == 0.0: self.height = 0.001 if self.radius == 0: glBegin(GL_LINES) glVertex3f(self.pointBegin.position[0], self.pointBegin.position[1], self.pointBegin.position[2]) glVertex3f(self.pointEnd.position[0], self.pointEnd.position[1], self.pointEnd.position[2]) glEnd() else: #glMultMatrixd( self.rotMatrix ) # there is either a bug in glMultMatrixd or in Quaternion.getRotationMatrix # or maybe it has to do with Numpy -> Numeric transition? # now doing it with glTranslate/Rotate axis = -self.orientation.axis() angle = self.orientation.angle() pos = self.pointBegin.position glTranslate(pos[0],pos[1],pos[2]) glRotate(angle*180/math.pi, axis[0], axis[1], axis[2]) #print "gluCylinder ", self.radius, self.radius, self.height, 10, 10 gluCylinder( self.quadric, float(self.radius), float(self.radius), float(self.height), 10, 10 ) glPopMatrix() def setNewReference( self, oldObj, newObj ): if self.pointBegin == oldObj: self.pointBegin = newObj elif self.pointEnd == oldObj: self.pointEnd = newObj def clone( self, **attr ): obj = deepcopy( self ) obj.__dict__.update( attr ) return obj def load (self, domElement): self.key = str(domElement.attribute("key", "")) self.name = str(domElement.attribute("name", "")) pBeginKey = str(domElement.attribute("pBeginKey", "")) pEndKey = str(domElement.attribute("pEndKey", "")) self.pointBegin = PrototypeManager().getObjectByKey(pBeginKey) self.pointEnd = PrototypeManager().getObjectByKey(pEndKey) self.distance = domElement.attribute("distance", "").toDouble()[0] def importItem(self, domElement, nrOfImports=0, objNr=0): self.key = "l" + str(objNr) self.importKey = str(domElement.attribute("key", "")) self.name = str(domElement.attribute("name", "")) self.name += "_" + str(nrOfImports) pBeginKey = str(domElement.attribute("pBeginKey", "")) pEndKey = str(domElement.attribute("pEndKey", "")) self.pointBegin = PrototypeManager().getImportedObjsByKey(pBeginKey) self.pointEnd = PrototypeManager().getImportedObjsByKey(pEndKey) self.distance = domElement.attribute("distance", "").toDouble()[0] def save( self, domDocument ): distanceNode = QtXml.QDomElement( domDocument.createElement( "Distance" ) ) distanceNode.setAttribute( "pBeginKey", self.pointBegin.key ) distanceNode.setAttribute( "pEndKey", self.pointEnd.key ) distanceNode.setAttribute( "key", self.key ) distanceNode.setAttribute( "name", self.name ) distanceNode.setAttribute( "distance", str(self.distance) ) return distanceNode class DistanceConstraint( Distance ): def __init__( self, name, pBegin, pEnd ): Distance.__init__( self, name, pBegin, pEnd ) self.radius = self.settings.sketcherData.distanceRadius self.objType = ObjectType.DISTANCE_CONSTRAINT self.con = None self.color = self.settings.sketcherData.distanceColor self.texture = None self.fixed = False self.parameterRange=[] def clone( self, **attr ): obj = deepcopy( self ) obj.__dict__.update( attr ) return obj def update(self): Distance.update(self) if not self.fixed and not self.ghost: self.distance = self.height def load (self, domElement): Distance.load(self, domElement) if domElement.attribute("fixed", "") == "True": self.fixed = True else: self.fixed = False def importItem(self, domElement, nrOfImports=0, objNr=0): Distance.importItem(self, domElement, nrOfImports, objNr) self.key = "d" + str(objNr) print "Distance: ", self.key, self.name, self.pointBegin.key, self.pointEnd.key if domElement.attribute("fixed", "") == "True": self.fixed = True else: self.fixed = False def save( self, domDocument ): distanceNode = QtXml.QDomElement( domDocument.createElement( "DistanceConstraint" ) ) distanceNode.setAttribute( "pBeginKey", self.pointBegin.key ) distanceNode.setAttribute( "pEndKey", self.pointEnd.key ) distanceNode.setAttribute( "key", self.key ) distanceNode.setAttribute( "name", self.name ) distanceNode.setAttribute( "distance", str(self.distance) ) distanceNode.setAttribute( "fixed", str(self.fixed) ) return distanceNode class Cluster( Object ): def __init__(self, pBegin, pMiddle, pEnd): Object.__init__( self ) self.name = "" self.pointBegin = pBegin self.pointMiddle = pMiddle self.pointEnd = pEnd self.objType = None self.color = [1.0, 0.0, 0.0, 0.15] self.height = 40.0 def draw( self ): glPushMatrix() glDisable( GL_DEPTH_TEST ) glEnable ( GL_BLEND ) glColor4fv( self.color ) glBegin(GL_TRIANGLES) glVertex3f(self.pointBegin.position[0], self.pointBegin.position[1], self.pointBegin.position[2]) glVertex3f(self.pointMiddle.position[0], self.pointMiddle.position[1], self.pointMiddle.position[2]) glVertex3f(self.pointEnd.position[0], self.pointEnd.position[1], self.pointEnd.position[2]) glEnd() glDisable ( GL_BLEND ) glEnable( GL_DEPTH_TEST ) glPopMatrix() def update( self ): pass class Axis( Object ): def __init__( self, position, width, height ): Object.__init__( self ) self.position = position self.width = width self.height = height self.selected = False self.selectionID = 0 self.isMovable = True def draw( self ): glPushMatrix() glTranslate( self.position[0], self.position[1], self.position[2] ) self.drawXAxis() self.drawYAxis() self.drawZAxis() glPopMatrix() def drawWithPicking( self ): glPushMatrix() glTranslate( self.position[0], self.position[1], self.position[2] ) glPushName( 1 ) self.drawXAxis() glPopName() glPushName( 2 ) self.drawYAxis() glPopName() glPushName( 3 ) self.drawZAxis() glPopName() glPopMatrix() def clone( self, **attr ): obj = deepcopy( self ) obj.__dict__.update( attr ) return obj def updatePosition( self, position ): self.position = position def drawXAxis( self ): # x axis glPushMatrix() if self.selected == True and self.selectionId == 1: glColor3fv( [1.0, 1.0, 0.0] ) else: glColor3fv( [1.0, 0.0, 0.0] ) glTranslate( -1.0, 0.0, 0.0 ) glRotate( 90, 0.0, 1.0 , 0.0 ) #glRotate(180, 1.0, 0.0, 0.0) gluCylinder( self.quadric, self.width, self.width, self.height, 10, 10 ) glPushMatrix() glTranslatef( 0.0, 0.0, self.height ) gluCylinder( self.quadric, self.width*2.5, 0.0, self.height/5, 10, 10 ) glPopMatrix() glPopMatrix() def drawYAxis( self ): # y axis glPushMatrix() if self.selected == True and self.selectionId == 2: glColor3fv( [1.0, 1.0, 0.0] ) else: glColor3fv( [0.0, 1.0, 0.0] ) glRotate( -90, 1.0, 0.0 , 0.0 ) gluCylinder( self.quadric, self.width, self.width, self.height, 10, 10 ) glPushMatrix() glTranslatef( 0.0, 0.0, self.height ) gluCylinder( self.quadric, self.width*2.5, 0.0, self.height/5, 10, 10 ) newFont = QtGui.QFont() newFont.setStyleStrategy( QtGui.QFont.OpenGLCompatible ) #self.activeViewport.renderText(14.0, 10.0, 1.0, 'XY', newFont) #glEnable(GL_LIGHTING) glPopMatrix() glPopMatrix() def drawZAxis( self ): # z axis glPushMatrix() if self.selected == True and self.selectionId == 3: glColor3fv( [1.0, 1.0, 0.0] ) else: glColor3fv( [0.0, 0.0, 1.0] ) glRotate( 180, 0.0, 1.0 , 0.0 ) gluCylinder( self.quadric, self.width, self.width, self.height, 10, 10 ) glPushMatrix() glTranslatef( 0.0, 0.0, self.height ) gluCylinder( self.quadric, self.width*2.5, 0.0, self.height/5, 10, 10 ) glPopMatrix() glPopMatrix() def setNewReference( self, oldObj, newObj ): pass class PointCluster ( Point ): def __init__( self, position, radius, relatedPoint ): Point.__init__(self,"", position, radius) self.color = [1.0, 0.0, 0.0, 0.4] self.selectColor = [1.0, 1.0, 0.0, 0.4] self.temporary = False self.fixed = False self.relPoint = relatedPoint def draw( self ): glPushMatrix() glEnable ( GL_BLEND ) if self.selected: glColor4fv( self.selectColor ) else: glColor4fv( self.color ) glTranslatef( self.position[0], self.position[1], self.position[2] ) gluSphere( self.quadric, self.radius, 10, 10 ) glDisable ( GL_BLEND ) glPopMatrix() def getClusterPoints(self): clusterPoints = [] clusterPoints += [self] return clusterPoints class DistanceCluster( Distance ): def __init__( self, pBegin, pEnd ): Distance.__init__(self,"", pBegin, pEnd) # Rick 20090519 - set radius from preferences #self.radius = 3.0 self.radius = self.settings.sketcherData.distanceRadius * 1.5 self.color = [1.0, 0.0, 0.0, 0.4] self.selectColor = [1.0, 1.0, 0.0, 0.4] self.temporary = False self.fixed = False def draw( self ): glPushMatrix() if self.selected: glColor4fv( self.selectColor ) else: glColor4fv( self.color ) glEnable ( GL_BLEND ) # Rick 20090519 glMultMatrixd doesn't work properly, see above # glMultMatrixd( self.rotMatrix ) axis = -self.orientation.axis() angle = self.orientation.angle() pos = self.pointBegin.position glTranslate(pos[0],pos[1],pos[2]) glRotate(angle*180/math.pi, axis[0], axis[1], axis[2]) if self.height == 0.0: self.height = 0.1 gluCylinder( self.quadric, float(self.radius), float(self.radius), float(self.height), 10, 10 ) glDisable ( GL_BLEND ) glPopMatrix() def getClusterPoints(self): clusterPoints = [] clusterPoints += [self.pointBegin] clusterPoints += [self.pointEnd] return clusterPoints class ClusterI( Object ): def __init__(self, constrainedness): Object.__init__( self ) self.name = "" self.points = "" self.dimension = 3 self.zeroIndex = None self.objType = ObjectType.CLUSTER self.constrainedness = constrainedness self.color = [1.0, 0.0, 0.0, 0.2] self.underconstrainedClr = [1.0, 0.0, 0.0, 0.2] self.overconstrainedClr = [0.0, 0.0, 1.0, 0.2] self.wellconstrainedClr = [0.0, 1.0, 0.0, 0.2] self.selectColor = [1.0, 1.0, 0.0, 0.15] self.clusterPoints = [] self.pickColor() self.temporary = False self.fixed = False self.hull = None def pickColor(self): if self.constrainedness == GeometricDecomposition.I_UNDER or self.constrainedness == GeometricDecomposition.S_UNDER: self.color = self.underconstrainedClr elif self.constrainedness == GeometricDecomposition.I_OVER or self.constrainedness == GeometricDecomposition.S_OVER: self.color = self.overconstrainedClr elif self.constrainedness == GeometricDecomposition.OK: self.color = self.wellconstrainedClr def draw( self ): glPushMatrix() if self.selected: glColor4fv( self.selectColor ) else: glColor4fv( self.color ) if self.hull != None: for triangle in self.hull: #glDisable( GL_DEPTH_TEST) glEnable ( GL_BLEND ) glBegin(GL_TRIANGLES) for vertex in triangle: glVertex3f(vertex[0], vertex[1], vertex[2]) glEnd() glDisable ( GL_BLEND ) #glEnable( GL_DEPTH_TEST) glBegin(GL_LINES) glVertex3f(triangle[0][0], triangle[0][1], triangle[0][2]) glVertex3f(triangle[1][0], triangle[1][1], triangle[1][2]) glVertex3f(triangle[1][0], triangle[1][1], triangle[1][2]) glVertex3f(triangle[2][0], triangle[2][1], triangle[2][2]) glVertex3f(triangle[2][0], triangle[2][1], triangle[2][2]) glVertex3f(triangle[0][0], triangle[0][1], triangle[0][2]) glEnd() else: glDisable( GL_DEPTH_TEST) glEnable ( GL_BLEND ) glBegin(GL_POLYGON) for clsPoint in self.clusterPoints: glVertex3f(clsPoint.position[0], clsPoint.position[1], clsPoint.position[2]) glEnd() glDisable ( GL_BLEND ) glEnable( GL_DEPTH_TEST) glPopMatrix() def update( self ): points = [] for clsPoint in self.clusterPoints: points += [clsPoint.position] self.determineDimension(points) if self.dimension == 2: if len(self.clusterPoints) < 4: self.hull = [points] return elif self.dimension == 3: if len(self.clusterPoints) == 4: self.hull = self.createTetrahedron(points) return elif len(self.clusterPoints) == 3: self.hull = [points] return """ handle the creation of a hull for more than 3 points in 2D and more than 4 point in 3D. """ if self.dimension == 2: points = self.convert3DTo2D(points) triangulation = dcore.Triangulation(points, self.dimension) if self.dimension == 2: indexHull = self.createFaces(triangulation.get_elements_indices()) if self.dimension == 3: indexHull = self.createHull(triangulation.get_elements_indices()) self.hull = [] for iHull in indexHull: face = map(lambda x:points[x], iHull) self.hull += [face] if self.dimension == 2: self.hull = self.convert2DTo3D(self.hull) def getClusterPoints(self): return self.clusterPoints def createHull(self, tetraIndex): faces = self.createFaces(tetraIndex) faces.sort() uniqueFaces = [] for face in faces: if faces.count(face) == 1: uniqueFaces += [face] return uniqueFaces def createFaces(self, tetrahedra): faces = [] if self.dimension==2: for face in tetrahedra: if self.zeroIndex == 0: faces += [[face[0], face[1], face[2]]] elif self.zeroIndex == 1: faces += [[face[0], face[1], face[2]]] elif self.zeroIndex == 2: faces += [[face[0], face[1], face[2]]] elif self.dimension==3: for tetrahedron in tetrahedra: faces += [[tetrahedron[0], tetrahedron[1],tetrahedron[2]]] faces += [[tetrahedron[1], tetrahedron[2],tetrahedron[3]]] faces += [[tetrahedron[2], tetrahedron[3],tetrahedron[0]]] faces += [[tetrahedron[3], tetrahedron[0],tetrahedron[1]]] for face in faces: face.sort() return faces def determineDimension(self, points): pointsOfTwoDimensions = filter(lambda x:x[0] == 0.0, points) if len(pointsOfTwoDimensions) == len(points): self.dimension = 2 self.zeroIndex = 0 return pointsOfTwoDimensions = filter(lambda x:x[1] == 0.0, points) if len(pointsOfTwoDimensions) == len(points): self.dimension = 2 self.zeroIndex = 1 return pointsOfTwoDimensions = filter(lambda x:x[2] == 0.0, points) if len(pointsOfTwoDimensions) == len(points): self.dimension = 2 self.zeroIndex = 2 return self.dimension = 3 def convert3DTo2D(self, points): newDimension = [] if self.dimension == 2: for point in points: if self.zeroIndex == 0: newDimension += [[point[1], point[2]]] elif self.zeroIndex == 1: newDimension += [[point[0], point[2]]] elif self.zeroIndex == 2: newDimension += [[point[0], point[1]]] return newDimension def convert2DTo3D(self, triangles): newDimension = [] if self.dimension == 2: index = 0 for triangle in triangles: newDimension += [[]] for point in triangle: if self.zeroIndex == 0: newDimension[index] += [[0.0, point[0], point[1]]] elif self.zeroIndex == 1: newDimension[index] += [[point[0], 0.0, point[1]]] elif self.zeroIndex == 2: newDimension[index] += [[point[0], point[1], 0.0]] index += 1 return newDimension def createTetrahedron(self, points): hull = [] if len(points) == 4: hull += [[points[0], points[1], points[2]]] hull += [[points[0], points[1], points[3]]] hull += [[points[0], points[2], points[3]]] hull += [[points[1], points[2], points[3]]] return hull