Esempio n. 1
0
class AbstractNurbsObjectEditorView(QGLViewer):
    """ The class NurbsPatchEditor is the viewer of the scene, it contains all the informations about the NurbsPatch, that's why a getter and a setter have been created, the NubrsPatch is defined by a 2 dimensional Array of 3d Vectors"""

    Edit, Rotate = list(range(2))
    valueChanged = pyqtSignal()
    manipulated = pyqtSignal()
    selectionChanged = pyqtSignal()

    def __init__(self, parent):
        """ Constructor 
            :param parent: the parent widget
        """
        QGLViewer.__init__(self, parent)
        self.setStateFileName('.nurbsobjecteditor.xml')

        self.mode = AbstractNurbsObjectEditorView.Edit

        # the nurbs patch
        self.nurbsObject = None

        # matrix of ctrl point manipulator
        self.ctrlPointManipulators = {}

        # shape and material to display the patch
        self.nurbsShape = Shape()
        self.defaultMaterial = Material((30, 200, 30), 1, transparency=0.5)
        self.nurbsShape.appearance = self.defaultMaterial

        # plantgl basic object to display nurbs
        self.discretizer = Discretizer()
        self.glrenderer = GLRenderer(self.discretizer)
        self.glrenderer.renderingMode = GLRenderer.Dynamic
        self.ctrlrenderer = GLCtrlPointRenderer(self.discretizer)
        self.bboxcomputer = BBoxComputer(self.discretizer)

        self.ctrlPointDisplay = None

        #   Current selection
        self.selectionManipulator = None
        self.focus = None

        # rectangular selection
        self.selectionRect = False  # for rectangular selection (with control)
        self.__rectangle = QRect()
        self.__rectangleInit = None

        self._drawNurbsObject = True
        self._drawGrid = True

        #creation of a default NurbsPatch
        self.setNurbsObject(self.newDefaultNurbsObject())

    def getSelection(self):
        if self.selectionManipulator:
            return self.selectionManipulator.selection
        else:
            return []

    def clear(self):
        """ clear current edition """
        self.ctrlPointManipulators = {}
        self.clearSelectionManipulator()
        self.resetDisplay()

    def getCtrlPoints(self):
        return self.nurbsObject.ctrlPointMatrix

    def getCtrlPointsDim(self):
        return self.getCtrlPoints().sizes()

    def getCtrlPointManipulators(self):
        return self.ctrlPointManipulators

    def getBounds(self):
        """ Get the Bounding Box of the scene:
            return the minpos and maxpos that can be used by setSceneBoundingBox function"""
        self.nurbsObject.apply(self.bboxcomputer)
        res = self.bboxcomputer.result
        return Vec(*res.lowerLeftCorner), Vec(*res.upperRightCorner)

    def __propagate_valuechanged__(self, pid=None):
        """ emit a SIGNAL every time a value changed """
        self.setSceneBoundingBox(*self.getBounds())
        self.valueChanged.emit()
        self.resetDisplay()

    def resetDisplay(self, pid=None):
        self.ctrlPointDisplay = None

    def init(self):
        """ init function """
        self.setSceneBoundingBox(*self.getBounds())
        self.restoreStateFromFile()

        # init mouse interaction
        self.mode = AbstractNurbsObjectEditorView.Edit
        self.setInteractionMode(False)

        self.setMouseBindingDescription(Qt.ShiftModifier, Qt.LeftButton,
                                        "Rectangular selection")
        self.setMouseBindingDescription(Qt.NoModifier, Qt.LeftButton,
                                        "Camera/Control Points manipulation")
        self.setMouseBindingDescription(
            Qt.NoModifier, Qt.LeftButton,
            "When double clicking on a line, create a new line", True)

        self.setKeyDescription(Qt.Key_K, "Toggles control point wire display")
        self.setKeyDescription(Qt.Key_G, "Toggles patch display")

    def setFocus(self, point):
        """ Set focus to given control point """
        if self.focus:
            self.focus.hasFocus = False
        self.focus = point
        if self.focus:
            point.hasFocus = True

    def clearSelectionManipulator(self):
        """ clear the selection manipulator """
        if not self.selectionManipulator is None:
            self.selectionManipulator.clear()
            self.selectionManipulator.valueChanged.disconnect(
                self.__propagate_valuechanged__
            )  # QObject.disconnect(self.selectionManipulator,SIGNAL("valueChanged()"),self.__propagate_valuechanged__)
            self.selectionManipulator = None
            self.resetDisplay()

    def createSelectionManipulator(self):
        """ ensure that the selection manipulator is existing and valid """
        if not self.selectionManipulator:
            self.selectionManipulator = SelectionManipulator()
            self.selectionManipulator.valueChanged.connect(
                self.__propagate_valuechanged__
            )  # QObject.connect(self.selectionManipulator,SIGNAL("valueChanged()"),self.__propagate_valuechanged__)
            self.resetDisplay()

    def mousePressEvent(self, event):
        """ Check for eventual operations the user asks: 
            shift start rectangular selection
            else check for which point is selected
        """
        #Rectangular selection
        if event.modifiers() == Qt.ShiftModifier:
            if event.button() == Qt.LeftButton:
                self.__rectangle = QRect(event.pos(), event.pos())
                self.__rectangleInit = event.pos()
                self.selectionRect = True
            elif event.button() == Qt.RightButton:
                self.clearSelectionManipulator()
                self.selectionChanged.emit()
            self.updateGL()
        else:
            pointSelection = False
            if not self.selectionManipulator is None:
                pointSelection = self.selectionManipulator.checkIfAxisGrabsMouse(
                    event, self)
            if not pointSelection:
                for cCtrlPoint in self.getCtrlPointManipulators().values():
                    cCtrlPoint.checkIfGrabsMouse(event.x(), event.y(),
                                                 self.camera())
                    if cCtrlPoint.grabsMouse():
                        pointSelection = True
                        self.setFocus(cCtrlPoint)
                        if self.selectionManipulator and cCtrlPoint.selected:
                            self.setManipulatedFrame(self.selectionManipulator)
                        else:
                            self.setManipulatedFrame(cCtrlPoint)
                        break
            self.setInteractionMode(pointSelection)
            self.updateGL()

        QGLViewer.mousePressEvent(self, event)

    def setInteractionMode(self, frame=True):
        if frame:
            if self.mode != AbstractNurbsObjectEditorView.Edit:
                self.mode = AbstractNurbsObjectEditorView.Edit
                self.setMouseBinding(Qt.NoModifier, Qt.LeftButton,
                                     QGLViewer.FRAME, QGLViewer.TRANSLATE)
                self.setMouseBinding(Qt.NoModifier, Qt.RightButton,
                                     QGLViewer.FRAME,
                                     QGLViewer.NO_MOUSE_ACTION)

                self.setMouseBinding(Qt.ControlModifier, Qt.LeftButton,
                                     QGLViewer.CAMERA, QGLViewer.ROTATE)
                self.setMouseBinding(Qt.ControlModifier, Qt.RightButton,
                                     QGLViewer.CAMERA, QGLViewer.TRANSLATE)
                self.setMouseBinding(Qt.ControlModifier, Qt.MiddleButton,
                                     QGLViewer.CAMERA, QGLViewer.ZOOM)
        else:
            if self.mode != AbstractNurbsObjectEditorView.Rotate:
                self.mode = AbstractNurbsObjectEditorView.Rotate
                self.setMouseBinding(Qt.ControlModifier, Qt.LeftButton,
                                     QGLViewer.FRAME, QGLViewer.TRANSLATE)
                self.setMouseBinding(Qt.ControlModifier, Qt.RightButton,
                                     QGLViewer.FRAME,
                                     QGLViewer.NO_MOUSE_ACTION)
                self.setMouseBinding(Qt.NoModifier, Qt.LeftButton,
                                     QGLViewer.CAMERA, QGLViewer.ROTATE)
                self.setMouseBinding(Qt.NoModifier, Qt.RightButton,
                                     QGLViewer.CAMERA, QGLViewer.TRANSLATE)
                self.setMouseBinding(Qt.NoModifier, Qt.MiddleButton,
                                     QGLViewer.CAMERA, QGLViewer.ZOOM)

    def mouseDoubleClickEvent(self, event):
        """ mouseDoubleClickEvent: add a control point to the selection double clicking on it, empty the selection double clicking elsewhere """
        if event.modifiers() & Qt.ShiftModifier:
            selection = False
            #Doubleclick on a control point add/delete it from the selection array#
            for cCtrlPoint in self.getCtrlPointManipulators().values():
                cCtrlPoint.checkIfGrabsMouse(event.x(), event.y(),
                                             self.camera())
                if cCtrlPoint.grabsMouse():
                    cCtrlPoint.selected = not cCtrlPoint.selected
                    selection = True
                    self.createSelectionManipulator()
                    self.selectionManipulator.toogleSelection(cCtrlPoint)
            #Double click with shift anywhere but on a control point -> empty selection #
            if not selection:
                self.clearSelectionManipulator()
            self.selectionChanged.emit()
        else:
            ## Adding a column on the NurbsPatch#
            """ when double click on a line, the line is doubled """
            self.select(
                event.pos()
            )  # select call drwWithName that render line of control points with ids
            selection = self.selectedName()
            if selection >= 0:
                selectedPoints = self.addElement(selection)
                if selectedPoints:
                    self.createSelectionManipulator()
                    for index in selectedPoints:
                        self.selectionManipulator.add(
                            self.ctrlPointManipulators[index])
                else:
                    self.clearSelectionManipulator()
            else:
                self.clearSelectionManipulator()
            self.selectionChanged.emit()

    def mouseMoveEvent(self, event):
        """ mouseMoveEvent """

        #rectangular selection
        if self.selectionRect:
            self.__rectangle = QRect(self.__rectangleInit,
                                     event.pos()).normalized()
            self.updateGL()

        # by default camera or manipulated frame is manipulated
        QGLViewer.mouseMoveEvent(self, event)

    def mouseReleaseEvent(self, event):
        """On mouse release, we release every grabbed objects"""
        QGLViewer.mouseReleaseEvent(self, event)
        # clear manipulated object
        self.setManipulatedFrame(None)
        self.setFocus(None)
        if self.selectionManipulator:
            self.selectionManipulator.clearFocus()

        # end of rectangular selection :
        if self.selectionRect:
            self.selectionRect = False
            # Possibly swap left/right and top/bottom to make rectangle_ valid.
            self.__rectangle = QRect(self.__rectangleInit,
                                     event.pos()).normalized()
            # make rectangle with a minimal size
            if self.__rectangle.width() < 10 or self.__rectangle.height() < 10:
                self.__rectangle = QRect(event.pos().x() - 5,
                                         event.pos().y() - 5, 10, 10)
            self.selectionFromRect(self.__rectangle)
        self.setInteractionMode(False)
        self.updateGL()

    def selectionFromRect(self, rect):
        """ check if control point projections are in the given rectangle. If yes, put point in selection """
        selectionOccur = False
        for cCtrlPoint in self.getCtrlPointManipulators().values():
            point = self.camera().projectedCoordinatesOf(cCtrlPoint.position())
            if (self.__rectangle.contains(int(point.x), int(point.y))):
                selectionOccur = True
                if not self.selectionManipulator:
                    self.selectionManipulator = SelectionManipulator()
                    self.selectionManipulator.valueChanged.connect(
                        self.__propagate_valuechanged__
                    )  # QObject.connect(self.selectionManipulator,SIGNAL("valueChanged()"),self.__propagate_valuechanged__)
                self.selectionManipulator.toogleSelection(cCtrlPoint)
        if self.selectionManipulator and self.selectionManipulator.empty():
            self.clearSelectionManipulator()
        self.ctrlPointDisplay = None
        if selectionOccur:
            self.selectionChanged.emit()

    def keyPressEvent(self, e):
        modifiers = e.modifiers()
        # A simple switch on e->key() is not sufficient if we want to take state key into account.
        # With a switch, it would have been impossible to separate 'F' from 'CTRL+F'.
        # That's why we use imbricated if...else and a "handled" boolean.
        if ((e.key() == Qt.Key_K) and (modifiers == Qt.NoModifier)):
            self._drawGrid = not self._drawGrid
            self.updateGL()
        elif (e.key() == Qt.Key_G) and (modifiers == Qt.NoModifier):
            self._drawNurbsObject = not self._drawNurbsObject
            self.updateGL()
        else:
            QGLViewer.keyPressEvent(self, e)

    def addElement(self, selection):
        nbcolumn = self.getCtrlPoints().getColumnNb()
        minp, maxp = self.getBounds()
        diff = norm(maxp - minp) / 20
        if selection >= nbcolumn:
            row = selection - nbcolumn
            newpatch = addRowToPatch(self.nurbsObject, row, Vector4(diff, 0))
            selectedPoints = [(row, i) for i in range(nbcolumn)]
        else:
            newpatch = addColumnToPatch(self.nurbsObject, selection,
                                        Vector4(diff, 0))
            selectedPoints = [(i, selection)
                              for i in range(self.getCtrlPoints().getRowNb())]
        self.setNurbsObject(newpatch)
        return selectedPoints

    def draw(self):
        """ 
        Draw function :
        Control points of the nurbsPatch are represented by spheres.
        If some of them are selected, a medium axis will be drawn by lines and little spheres, 
        allowing us to grab one of them to perform a constrained move
        """

        ogl.glDisable(ogl.GL_LIGHTING)
        ogl.glPolygonMode(ogl.GL_FRONT_AND_BACK, ogl.GL_LINE)
        # draw the wire of the patch
        ogl.glLineWidth(1)

        if self._drawNurbsObject:
            self.drawNurbsObjectWire()
        if self._drawGrid:
            self.drawCtrlWire()

        # patch and control point shpere rendering
        ogl.glPolygonMode(ogl.GL_FRONT_AND_BACK, ogl.GL_FILL)
        ogl.glEnable(ogl.GL_LIGHTING)
        ogl.glLightModeli(ogl.GL_LIGHT_MODEL_TWO_SIDE, ogl.GL_TRUE)
        ogl.glEnable(ogl.GL_BLEND)
        ogl.glBlendFunc(ogl.GL_SRC_ALPHA, ogl.GL_ONE_MINUS_SRC_ALPHA)

        if self._drawNurbsObject:
            self.drawNurbsObject()
        self.drawCtrlPoints()

        ogl.glDisable(ogl.GL_BLEND)

        if self.selectionRect:
            # Draw rectangular selection
            self.startScreenCoordinatesSystem()  # use screen coordinates
            r = self.__rectangle
            rect = Shape(
                Polyline2D([(r.x(), r.y()), (r.x() + r.width(), r.y()),
                            (r.x() + r.width(), r.y() + r.height()),
                            (r.x(), r.y() + r.height()), (r.x(), r.y())]),
                Material((200, 200, 200), 1))
            ogl.glEnable(ogl.GL_LINE_STIPPLE)
            ogl.glLineStipple(1, 0x0FFF)
            ogl.glLineWidth(2)
            rect.apply(self.glrenderer)
            ogl.glDisable(ogl.GL_LINE_STIPPLE)
            self.stopScreenCoordinatesSystem()
        ogl.glLineWidth(1)

    def drawCtrlPoints(self):
        if self.ctrlPointDisplay is None:
            scradius = self.sceneRadius()
            pointsize = scradius / 30.

            shapes = []
            #define the nurbsShape and the control points geometry
            for ctrlPoint in self.getCtrlPointManipulators().values():
                shapes.append(ctrlPoint.representation(pointsize))

            # draw the frame of multiple selection
            if self.selectionManipulator:
                shapes += self.selectionManipulator.representation(pointsize *
                                                                   1.2)
            self.ctrlPointDisplay = Scene(shapes)

        # control point shpere rendering
        self.ctrlPointDisplay.apply(self.glrenderer)

    def drawCtrlWire(self):

        # ctrl line rendering
        Material((255, 255, 255), 1).apply(self.glrenderer)
        self.nurbsObject.apply(self.ctrlrenderer)

    def drawNurbsObject(self):
        sc = Scene([self.nurbsShape])

        # patch rendering
        sc.apply(self.glrenderer)

    def drawNurbsObjectWire(self):
        shwire = Shape(self.nurbsObject, Material((120, 255, 0), 0.8))
        shwire.apply(self.glrenderer)

    def drawWithNames(self):
        """ draw control lines with names """
        pointmatrix = self.getCtrlPoints()
        lines = []
        m = Material()
        maxcolumn = pointmatrix.getColumnNb()
        for colid in range(maxcolumn):
            lines.append(
                Shape(
                    Polyline(Point4Array(
                        pointmatrix.getColumn(colid)).project(),
                             width=3), m, colid))
        for rowid in range(pointmatrix.getRowNb()):
            lines.append(
                Shape(
                    Polyline(Point4Array(pointmatrix.getRow(rowid)).project(),
                             width=3), m, maxcolumn + rowid))
        for sh in lines:
            ogl.glPushName(sh.id)
            sh.apply(self.glrenderer)
            ogl.glPopName()

    def getNurbsObject(self):
        """ return the edited nurbs patch """
        return self.nurbsObject

    def setNurbsObject(self, nurbsObject):
        """ set the nurbs patch to edit """
        self.clear()
        self.nurbsObject = nurbsObject
        self.setNurbsObjectView()
        self.setControlPointManipulators()

    def setControlPointManipulators(self):
        import itertools
        dims = list(map(float, self.getCtrlPointsDim()))

        def color(idx):
            try:
                r = 30 + int(220 * idx[0] / dims[0])
                g = 30 + int(220 * idx[1] / dims[1]) if len(dims) > 1 else 250
                b = 30 + int(220 * idx[2] / dims[2]) if len(dims) > 2 else 250
            except:
                r = 250
                g = 30 + int(220 * idx / dims[0])
                b = 250
            return (r, g, b)

        ctrlpoints = self.getCtrlPoints()
        self.ctrlPointManipulators = {}
        dims = self.getCtrlPointsDim()
        if len(dims) == 1:
            idxiter = list(range(dims[0]))
        else:
            idxiter = itertools.product(*[list(range(d)) for d in dims])

        for pid, index in enumerate(idxiter):
            ctrlPoint = CtrlPoint(ctrlpoints[index],
                                  Pos4Setter(self.getCtrlPoints(), index,
                                             ctrlpoints[index].w),
                                  color=color(index),
                                  id=pid)
            ctrlPoint.setCallBack(self.__propagate_valuechanged__)
            self.ctrlPointManipulators[index] = ctrlPoint

        self.setSceneBoundingBox(*self.getBounds())

    def setNurbsObjectView(self):
        self.nurbsShape.geometry = self.nurbsObject

    def helpString(self):
        return helpstr

    def closeEvent(self, event):
        """close the windows properly"""
        helpwidget = self.helpWidget()
        if helpwidget and helpwidget.isVisible():
            helpwidget.hide()
        QGLViewer.closeEvent(self, event)

    def setUStride(self, value):
        self.nurbsObject.ustride = value
        self.valueChanged.emit()
        self.updateGL()

    def setWeigthToSelection(self, value):
        if self.selectionManipulator:
            try:
                if len(value) > 0:
                    for v, p in zip(value,
                                    self.selectionManipulator.selection):
                        p.position_setter.weight = v
                        p.__propagate_position_change__()
            except:
                if abs(value) < 1e-3:
                    return
                for p in self.selectionManipulator.selection:
                    p.position_setter.weight = value
                    p.__propagate_position_change__()
        self.updateGL()
Esempio n. 2
0
class NurbsPatchEditor(QGLViewer):
    """ The class NurbsPatchEditor is the viewer of the scene, it contains all the informations about the NurbsPatch, that's why a getter and a setter have been created, the NubrsPatch is defined by a 2 dimensional Array of 3d Vectors"""

    Edit, Rotate = list(range(2))
    valueChanged = pyqtSignal()

    def __init__(self, parent):
        """ Constructor 
            :param parent: the parent widget
        """
        QGLViewer.__init__(self, parent)
        self.setStateFileName('.nurbspatcheditor.xml')

        self.mode = NurbsPatchEditor.Edit

        # the nurbs patch
        self.nurbsPatch = None

        # matrix of ctrl point manipulator
        self.ctrlPointMatrix = []

        # shape and material to display the patch
        self.nurbsShape = Shape()
        self.defaultMaterial = Material((30, 200, 30), 1, transparency=0.5)
        self.nurbsShape.appearance = self.defaultMaterial

        # plantgl basic object to display nurbs
        self.discretizer = Discretizer()
        self.glrenderer = GLRenderer(self.discretizer)
        self.glrenderer.renderingMode = GLRenderer.Dynamic
        self.ctrlrenderer = GLCtrlPointRenderer(self.discretizer)
        self.bboxcomputer = BBoxComputer(self.discretizer)

        #   Current selection
        self.selectionManipulator = None
        self.focus = None

        # rectangular selection
        self.selectionRect = False  # for rectangular selection (with control)
        self.__rectangle = QRect()
        self.__rectangleInit = None

        #creation of a default NurbsPatch
        if len(self.ctrlPointMatrix) == 0:
            self.setNurbsPatch(self.newDefaultNurbsPatch())

    def __propagate_valuechanged__(self, pid=None):
        """ emit a SIGNAL every time a value changed """
        self.setSceneBoundingBox(*self.getBounds())
        self.valueChanged.emit(
        )  # self.emit(SIGNAL("valueChanged()")) # AUTO SIGNAL TRANSLATION

    def init(self):
        """ init function """
        self.setSceneBoundingBox(*self.getBounds())
        self.restoreStateFromFile()

        # init mouse interaction
        self.mode = NurbsPatchEditor.Edit
        self.setInteractionMode(False)

        self.setMouseBindingDescription(Qt.ShiftModifier + Qt.LeftButton,
                                        "Rectangular selection")
        self.setMouseBindingDescription(Qt.LeftButton,
                                        "Camera/Control Points manipulation")
        self.setMouseBindingDescription(
            Qt.LeftButton, "When double clicking on a line, create a new line",
            True)

    def getBounds(self):
        """ Get the Bounding Box of the scene:
            return the minpos and maxpos that can be used by setSceneBoundingBox function"""
        self.nurbsPatch.apply(self.bboxcomputer)
        res = self.bboxcomputer.result
        return Vec(*res.lowerLeftCorner), Vec(*res.upperRightCorner)

    def setFocus(self, point):
        """ Set focus to given control point """
        if self.focus:
            self.focus.hasFocus = False
        self.focus = point
        if self.focus:
            point.hasFocus = True

    def clearSelectionManipulator(self):
        """ clear the selection manipulator """
        if self.selectionManipulator:
            self.selectionManipulator.clear()
            self.selectionManipulator.valueChanged.disconnect(
                self.__propagate_valuechanged__
            )  # QObject.disconnect(self.selectionManipulator,SIGNAL("valueChanged()"),self.__propagate_valuechanged__)
            self.selectionManipulator = None

    def createSelectionManipulator(self):
        """ ensure that the selection manipulator is existing and valid """
        if not self.selectionManipulator:
            self.selectionManipulator = SelectionManipulator()
            self.selectionManipulator.valueChanged.connect(
                self.__propagate_valuechanged__
            )  # QObject.connect(self.selectionManipulator,SIGNAL("valueChanged()"),self.__propagate_valuechanged__)

    def mousePressEvent(self, event):
        """ Check for eventual operations the user asks: 
            shift start rectangular selection
            else check for which point is selected
        """
        #Rectangular selection
        if event.modifiers() == Qt.ShiftModifier:
            if event.button() == Qt.LeftButton:
                self.__rectangle = QRect(event.pos(), event.pos())
                self.__rectangleInit = event.pos()
                self.selectionRect = True
            elif event.button() == Qt.RightButton:
                self.clearSelectionManipulator()
            self.updateGL()
        else:
            pointSelection = False
            if self.selectionManipulator:
                pointSelection = self.selectionManipulator.checkIfAxisGrabsMouse(
                    event, self)
            if not pointSelection:
                for ctrlPointRow in self.ctrlPointMatrix:
                    for cCtrlPoint in ctrlPointRow:
                        cCtrlPoint.checkIfGrabsMouse(event.x(), event.y(),
                                                     self.camera())
                        if cCtrlPoint.grabsMouse():
                            pointSelection = True
                            self.setFocus(cCtrlPoint)
                            if self.selectionManipulator and cCtrlPoint.selected:
                                self.setManipulatedFrame(
                                    self.selectionManipulator)
                            else:
                                self.setManipulatedFrame(cCtrlPoint)
                            break
            self.setInteractionMode(pointSelection)
            self.updateGL()

        QGLViewer.mousePressEvent(self, event)

    def setInteractionMode(self, frame=True):
        if frame:
            if self.mode != NurbsPatchEditor.Edit:
                self.mode = NurbsPatchEditor.Edit
                self.setMouseBinding(Qt.LeftButton, QGLViewer.FRAME,
                                     QGLViewer.TRANSLATE)
                self.setMouseBinding(Qt.RightButton, QGLViewer.FRAME,
                                     QGLViewer.NO_MOUSE_ACTION)

                self.setMouseBinding(Qt.ControlModifier + Qt.LeftButton,
                                     QGLViewer.CAMERA, QGLViewer.ROTATE)
                self.setMouseBinding(Qt.ControlModifier + Qt.RightButton,
                                     QGLViewer.CAMERA, QGLViewer.TRANSLATE)
                self.setMouseBinding(Qt.ControlModifier + Qt.MiddleButton,
                                     QGLViewer.CAMERA, QGLViewer.ZOOM)
        else:
            if self.mode != NurbsPatchEditor.Rotate:
                self.mode = NurbsPatchEditor.Rotate
                self.setMouseBinding(Qt.ControlModifier + Qt.LeftButton,
                                     QGLViewer.FRAME, QGLViewer.TRANSLATE)
                self.setMouseBinding(Qt.ControlModifier + Qt.RightButton,
                                     QGLViewer.FRAME,
                                     QGLViewer.NO_MOUSE_ACTION)
                self.setMouseBinding(Qt.LeftButton, QGLViewer.CAMERA,
                                     QGLViewer.ROTATE)
                self.setMouseBinding(Qt.RightButton, QGLViewer.CAMERA,
                                     QGLViewer.TRANSLATE)
                self.setMouseBinding(Qt.MiddleButton, QGLViewer.CAMERA,
                                     QGLViewer.ZOOM)

    def mouseDoubleClickEvent(self, event):
        """ mouseDoubleClickEvent: add a control point to the selection double clicking on it, empty the selection double clicking elsewhere """
        if event.modifiers() & Qt.ShiftModifier:
            selection = False
            #Doubleclick on a control point add/delete it from the selection array#
            for ctrlPointRow in self.ctrlPointMatrix:
                for cCtrlPoint in ctrlPointRow:
                    cCtrlPoint.checkIfGrabsMouse(event.x(), event.y(),
                                                 self.camera())
                    if cCtrlPoint.grabsMouse():
                        cCtrlPoint.selected = not cCtrlPoint.selected
                        selection = True
                        self.createSelectionManipulator()
                        self.selectionManipulator.toogleSelection(cCtrlPoint)
            #Double click with shift anywhere but on a control point -> empty selection #
            if not selection:
                self.clearSelectionManipulator()
        else:
            ## Adding a column on the NurbsPatch#
            """ when double click on a line, the line is doubled """
            self.select(
                event.pos()
            )  # select call drwWithName that render line of control points with ids
            selection = self.selectedName()
            if selection >= 0:
                nbcolumn = self.nurbsPatch.ctrlPointMatrix.getColumnNb()
                minp, maxp = self.getBounds()
                diff = norm(maxp - minp) / 20
                if selection >= nbcolumn:
                    row = selection - nbcolumn
                    newpatch = addRowToPatch(self.nurbsPatch, row,
                                             Vector4(diff, 0))
                    selectedPoints = [(row, i) for i in range(nbcolumn)]
                else:
                    newpatch = addColumnToPatch(self.nurbsPatch, selection,
                                                Vector4(diff, 0))
                    selectedPoints = [(i, selection) for i in range(
                        self.nurbsPatch.ctrlPointMatrix.getRowNb())]
                self.setNurbsPatch(newpatch)
                self.createSelectionManipulator()
                for index in selectedPoints:
                    self.selectionManipulator.add(
                        self.ctrlPointMatrix[index[0]][index[1]])
            else:
                self.clearSelectionManipulator()

    def mouseMoveEvent(self, event):
        """ mouseMoveEvent """

        #rectangular selection
        if self.selectionRect:
            self.__rectangle = QRect(self.__rectangleInit,
                                     event.pos()).normalized()
            self.updateGL()

        # by default camera or manipulated frame is manipulated
        QGLViewer.mouseMoveEvent(self, event)

    def mouseReleaseEvent(self, event):
        """On mouse release, we release every grabbed objects"""
        QGLViewer.mouseReleaseEvent(self, event)
        # clear manipulated object
        self.setManipulatedFrame(None)
        self.setFocus(None)
        if self.selectionManipulator:
            self.selectionManipulator.clearFocus()

        # end of rectangular selection :
        if self.selectionRect:
            self.selectionRect = False
            # Possibly swap left/right and top/bottom to make rectangle_ valid.
            self.__rectangle = QRect(self.__rectangleInit,
                                     event.pos()).normalized()
            # make rectangle with a minimal size
            if self.__rectangle.width() < 10 or self.__rectangle.height() < 10:
                self.__rectangle = QRect(event.pos().x() - 5,
                                         event.pos().y() - 5, 10, 10)
            self.selectionFromRect(self.__rectangle)
        self.setInteractionMode(False)
        self.updateGL()

    def selectionFromRect(self, rect):
        """ check if control point projections are in the given rectangle. If yes, put point in selection """
        for ctrlPointRow in self.ctrlPointMatrix:
            for cCtrlPoint in ctrlPointRow:
                point = self.camera().projectedCoordinatesOf(
                    cCtrlPoint.position())
                if (self.__rectangle.contains(point.x, point.y)):
                    if not self.selectionManipulator:
                        self.selectionManipulator = SelectionManipulator()
                        self.selectionManipulator.valueChanged.connect(
                            self.__propagate_valuechanged__
                        )  # QObject.connect(self.selectionManipulator,SIGNAL("valueChanged()"),self.__propagate_valuechanged__)
                    self.selectionManipulator.toogleSelection(cCtrlPoint)
        if self.selectionManipulator and self.selectionManipulator.empty():
            self.clearSelectionManipulator()

    def draw(self):
        """ 
        Draw function :
        Control points of the nurbsPatch are represented by spheres.
        If some of them are selected, a medium axis will be drawn by lines and little spheres, 
        allowing us to grab one of them to perform a constrained move
        """
        scradius = self.sceneRadius()
        pointsize = scradius / 30.

        shapes = []
        #define the nurbsShape and the control points geometry
        #self.p=[]
        for ctrlPointLine in self.ctrlPointMatrix:
            for ctrlPoint in ctrlPointLine:
                shapes.append(ctrlPoint.representation(pointsize))

        # draw the frame of multiple selection
        if self.selectionManipulator:
            shapes += self.selectionManipulator.representation(pointsize * 1.2)

        shapes.append(self.nurbsShape)
        sc = Scene(shapes)

        ogl.glDisable(ogl.GL_LIGHTING)
        ogl.glPolygonMode(ogl.GL_FRONT_AND_BACK, ogl.GL_LINE)
        # draw the wire of the patch
        ogl.glLineWidth(1)
        shwire = Shape(self.nurbsPatch, Material((120, 255, 0), 0.8))
        shwire.apply(self.glrenderer)
        # ctrl line rendering
        ogl.glLineWidth(1)
        Material((255, 255, 255), 1).apply(self.glrenderer)
        self.nurbsPatch.apply(self.ctrlrenderer)
        # patch and control point shpere rendering
        ogl.glPolygonMode(ogl.GL_FRONT_AND_BACK, ogl.GL_FILL)
        ogl.glEnable(ogl.GL_LIGHTING)
        ogl.glLightModeli(ogl.GL_LIGHT_MODEL_TWO_SIDE, ogl.GL_TRUE)
        ogl.glEnable(ogl.GL_BLEND)
        ogl.glBlendFunc(ogl.GL_SRC_ALPHA, ogl.GL_ONE_MINUS_SRC_ALPHA)
        sc.apply(self.glrenderer)
        ogl.glDisable(ogl.GL_BLEND)

        if self.selectionRect:
            # Draw rectangular selection
            self.startScreenCoordinatesSystem()  # use screen coordinates
            r = self.__rectangle
            rect = Shape(
                Polyline2D([(r.x(), r.y()), (r.x() + r.width(), r.y()),
                            (r.x() + r.width(), r.y() + r.height()),
                            (r.x(), r.y() + r.height()), (r.x(), r.y())]),
                Material((200, 200, 200), 1))
            ogl.glEnable(ogl.GL_LINE_STIPPLE)
            ogl.glLineStipple(1, 0x0FFF)
            ogl.glLineWidth(2)
            rect.apply(self.glrenderer)
            ogl.glDisable(ogl.GL_LINE_STIPPLE)
            self.stopScreenCoordinatesSystem()
        ogl.glLineWidth(1)

    def drawWithNames(self):
        """ draw control lines with names """
        pointmatrix = self.nurbsPatch.ctrlPointMatrix
        lines = []
        m = Material()
        maxcolumn = pointmatrix.getColumnNb()
        for colid in range(maxcolumn):
            lines.append(
                Shape(
                    Polyline(Point4Array(
                        pointmatrix.getColumn(colid)).project(),
                             width=3), m, colid))
        for rowid in range(pointmatrix.getRowNb()):
            lines.append(
                Shape(
                    Polyline(Point4Array(pointmatrix.getRow(rowid)).project(),
                             width=3), m, maxcolumn + rowid))
        for sh in lines:
            ogl.glPushName(sh.id)
            sh.apply(self.glrenderer)
            ogl.glPopName()

    @staticmethod
    def newDefaultNurbsPatch():
        """ return a default nurbs patch """
        return NurbsPatch([[Vector4(j - 1.5, i - 1.5, 0, 1) for j in range(4)]
                           for i in range(4)])

    def getNurbsPatch(self):
        """ return the edited nurbs patch """
        return self.nurbsPatch

    def setNurbsPatch(self, nurbsPatch):
        """ set the nurbs patch to edit """
        self.clear()
        self.nurbsPatch = nurbsPatch
        self.nurbsShape.geometry = self.nurbsPatch
        nbLines = float(len(self.nurbsPatch.ctrlPointMatrix))
        pid = 1
        for j, linePoint in enumerate(self.nurbsPatch.ctrlPointMatrix):
            lineCtrlPoint = []
            nbCols = len(linePoint)
            for i in range(nbCols):
                ctrlPoint = CtrlPoint(linePoint[i].project(),
                                      Pos4Setter(
                                          self.nurbsPatch.ctrlPointMatrix,
                                          (j, i)),
                                      color=(30 + int(220 * j / nbLines),
                                             30 + int(220 * i / nbCols), 250),
                                      id=pid)
                pid += 1
                ctrlPoint.setCallBack(self.__propagate_valuechanged__)
                lineCtrlPoint.append(ctrlPoint)
            self.ctrlPointMatrix.append(lineCtrlPoint)
        self.setSceneBoundingBox(*self.getBounds())

    def clear(self):
        """ clear current edition """
        self.ctrlPointMatrix = []
        self.clearSelectionManipulator()

    def helpString(self):
        return helpstr

    def closeEvent(self, event):
        """close the windows properly"""
        helpwidget = self.helpWidget()
        if helpwidget and helpwidget.isVisible():
            helpwidget.hide()
        QGLViewer.closeEvent(self, event)