Пример #1
0
    def __init__(self,
            node,
            source = avg.TOUCH | avg.MOUSE,
            maxSize = None, minSize = None,
            onResize = lambda: None,
            onDrop = lambda: None,
            onStop = lambda: None,
            onAction = lambda: None,
            onMotion = lambda pos, size, angle, pivot: None,
            onResetMotion = lambda: None,
            onDragStart = lambda: None,
            initialDown = None,
            inertia = 0.95,
            torque = 0.95,
            moveNode = True):

        global g_Player
        self.__inertia = inertia
        self.__torque = torque
        self.__node = node
        self.__minSize = minSize
        self.__maxSize = maxSize
        self.__moveNode = moveNode
        self.__callback = {
                'onResize': onResize,
                'onDrop': onDrop,
                'onStop': onStop,
                'onAction': onAction,
                'onMotion': onMotion,
                'onResetMotion': onResetMotion,
                'onDragStart': onDragStart,
                }

        self.__stopInertia()
        #self.__oldAngle = 0.0
        self.__lastFixPoint = None
        self.__lastNumFingers = 0
        self.__onFrameHandler = g_Player.setOnFrameHandler(self.__onFrame)

        self.__eventList = ClusteredEventList (self.__node,
                source = source,
                onMotion = self.__onMotion,
                onUp = self.__onUp,
                onDown = self.__onDown,
                resetMotion = self.__resetMotion
                )
        if initialDown:
            self.__eventList.handleInitialDown (initialDown)
        self.__lastPos = None
        self.__stopped = True
Пример #2
0
class Grabbable:
    """Helper for multitouch object movement.
    Grabbable will add the well-known multitouch gestures
    to a node, i.e. moving with one finger, rotating,
    resizing and moving with 2 fingers.
    It also works with many more cursors/fingers, clustering
    them.
    """
    def __init__(self,
            node,
            source = avg.TOUCH | avg.MOUSE,
            maxSize = None, minSize = None,
            onResize = lambda: None,
            onDrop = lambda: None,
            onStop = lambda: None,
            onAction = lambda: None,
            onMotion = lambda pos, size, angle, pivot: None,
            onResetMotion = lambda: None,
            onDragStart = lambda: None,
            initialDown = None,
            inertia = 0.95,
            torque = 0.95,
            moveNode = True):

        global g_Player
        self.__inertia = inertia
        self.__torque = torque
        self.__node = node
        self.__minSize = minSize
        self.__maxSize = maxSize
        self.__moveNode = moveNode
        self.__callback = {
                'onResize': onResize,
                'onDrop': onDrop,
                'onStop': onStop,
                'onAction': onAction,
                'onMotion': onMotion,
                'onResetMotion': onResetMotion,
                'onDragStart': onDragStart,
                }

        self.__stopInertia()
        #self.__oldAngle = 0.0
        self.__lastFixPoint = None
        self.__lastNumFingers = 0
        self.__onFrameHandler = g_Player.setOnFrameHandler(self.__onFrame)

        self.__eventList = ClusteredEventList (self.__node,
                source = source,
                onMotion = self.__onMotion,
                onUp = self.__onUp,
                onDown = self.__onDown,
                resetMotion = self.__resetMotion
                )
        if initialDown:
            self.__eventList.handleInitialDown (initialDown)
        self.__lastPos = None
        self.__stopped = True

    def isDragging(self):
        return len(self.__eventList) > 0
    
    def getSpeed(self):
        return self.__speed
    
    def getAngle(self):
        return self.__angle
    
    def delete(self):
        global g_Player
        g_Player.clearInterval(self.__onFrameHandler)
        self.__eventList.delete()
        self.__node = None

    def __onFrame(self):
        global g_Player
        if len (self.__eventList) == 0:
            # inertia
            if self.__speed.getNorm() < 1:
                if not self.__stopped:
                    pos = Point2D(round(self.__node.pos.x), round(self.__node.pos.y))
                    size = Point2D(round(self.__node.size.x), round(self.__node.size.y))
                    self.__callback['onMotion'](pos, size, self.__node.angle, 
                            self.__node.pivot)
                    self.__stopped = True
                    self.__callback['onStop']()
                    self.__stopInertia()
                return

            pos = self.__node.pos + self.__speed
            angle = self.__node.angle + self.__rotSpeed
            size = self.__node.size
            pivot = self.__node.pivot

            if self.__moveNode:
                self.__node.pos = pos
                self.__node.size = size
                self.__node.angle = angle
                self.__node.pivot = pivot

            self.__callback['onMotion'](pos, size, angle, pivot)

            self.__speed *= self.__inertia
            self.__rotSpeed *= self.__torque
        else:
            self.__stopped = False
            # save current speed for inertia
            pos = self.__node.pos
            angle = self.__node.angle
            if self.__lastPos:
                speed = pos - self.__lastPos
                rotSpeed = angle - self.__lastAngle
                # use minimal angle to avoid extreme rotation:
                if rotSpeed < -math.pi/2:
                    rotSpeed += math.pi*2
                if rotSpeed > math.pi/2:
                    rotSpeed -= math.pi*2
                self.__speeds = (self.__speeds + [speed])[-NUM_SPEEDS:]
                self.__rotSpeeds = (self.__rotSpeeds + [rotSpeed])[-NUM_SPEEDS:]
            self.__lastPos = pos
            self.__lastAngle = angle

    def __stopInertia (self):
        self.__speed = Point2D(0,0)
        self.__rotSpeed = 0
        self.__speeds = [Point2D(0,0)]
        self.__rotSpeeds = [0.0]
        self.__lastPos = None

    def __onDown(self):
        def __gotoTopLayer():
            parent = self.__node.getParent()
            numChildren = parent.getNumChildren()
            parent.reorderChild(self.__node, numChildren - 1)

        self.__stopInertia()
        self.__reshape()
        if len(self.__eventList) == 1: # first finger down
            if self.__moveNode:
                __gotoTopLayer()
            self.__callback['onDragStart']()
        self.__callback['onAction']()

    def __onMotion(self):
        self.__reshape()
        self.__callback['onAction']()

    def __drop(self):
        self.__speed = sum(self.__speeds, Point2D(0,0)) / len(self.__speeds)
        self.__rotSpeed = sum(self.__rotSpeeds, 0.0) / len(self.__rotSpeeds)
        self.__callback['onDrop']()

    def __onUp(self):
        if len(self.__eventList) == 0:
            self.__drop()
        self.__callback['onAction']()

    def __reshape (self):
        def applyAffineTransformation (parameters, p):
            """apply transformation to point:
                         M:       v:
              P' = P * (m  -n) + (v0)
                       (n   m)   (v1) """
            m, n, v0, v1 = parameters
            nx = p[0] * m - p[1] * n + v0
            ny = p[0] * n + p[1] * m + v1
            return Point2D(nx, ny)

        clusters = self.__eventList.getCursors()

        if len(clusters) == 1:
            cursor = self.__eventList.getCursors()[0]

            pos = self.__oldpos + cursor.getDelta()
            size = self.__node.size
            angle = self.__node.angle
            pivot = self.__node.pivot

        elif len(clusters) == 2:
            # calculate an affine transformation which describes cluster movement
            # TODO: explain used maths
            cursor1, cursor2 = clusters
            equationMatrix = (
                    ((cursor1.getStartPos().x, -cursor1.getStartPos().y, 1, 0), cursor1.getPos().x),
                    ((cursor1.getStartPos().y,  cursor1.getStartPos().x, 0, 1), cursor1.getPos().y),
                    ((cursor2.getStartPos().x, -cursor2.getStartPos().y, 1, 0), cursor2.getPos().x),
                    ((cursor2.getStartPos().y,  cursor2.getStartPos().x, 0, 1), cursor2.getPos().y),
                    )
            try:
                param = solveEquationMatrix (equationMatrix)
            except (EquationSingular, EquationNotSolvable):
#                g_Log.trace(g_Log.APP, 
#                        "Grabbable: cannot solve equation, skipping movement")
                return False

            absTouchCenter = cursor2.getPos() + (cursor1.getPos() - cursor2.getPos()) / 2

            n_tl = applyAffineTransformation (param, self.o_tl)
            n_bl = applyAffineTransformation (param, self.o_bl)
            n_tr = applyAffineTransformation (param, self.o_tr)
            
            pos = n_tl
            if pos.x > 1e6 and pos.x > self.o_tl.x:
                pos = self.o_tl
            if pos.y > 1e6 and pos.y > self.o_tl.y:
                pos = self.o_tl
            if pos.x < -1e6 and pos.x < self.o_tl.x:
                pos = self.o_tl
            if pos.y < -1e6 and pos.y < self.o_tl.y:
                pos = self.o_tl

            size = Point2D(getDistance (n_tl, n_tr), getDistance (n_tl, n_bl))
            if size.x<=0.1:
                size.x=0.1
            if size.y<=0.1:
                size.y=0.1
            angle = getAngle(n_tl, n_tr) # pivot for this rotation is 0,0 (rel. to node)

            # get middle of touches relative to the node
            relTouchCenter = getRelPos(absTouchCenter, pos, angle, Point2D(0,0))
            pos += getOffsetForMovedPivot(
                    oldPivot = Point2D(0,0),
                    newPivot = relTouchCenter,
                    angle = angle)
            pivot = relTouchCenter

            # the absolute position of the pivot should change when
            # resizing, so we track and revert absolute pivot movement
            oldAbsPivot = getAbsPos(pivot, pos, angle, pivot)

            newSize = getScaledDim (size,
                    max = self.__maxSize,
                    min = self.__minSize)

            ratio = newSize.x / size.x
            newPivot = pivot * ratio

            pos += getOffsetForMovedPivot(
                    oldPivot = pivot,
                    newPivot = newPivot,
                    angle = angle)
            oldPivot = pivot
            pivot = newPivot

            size = newSize

            newAbsPivot = getAbsPos(pivot, pos, angle, pivot)

            # move node to revert absolute pivot position
            pos -= (newAbsPivot - oldAbsPivot)

        else: # more than 2 clusters
            assert False

        if self.__moveNode:
            self.__node.angle = angle
            self.__node.size = size
            self.__node.pos = pos
            self.__node.pivot = pivot
            self.__callback['onResize']()

        self.__callback['onMotion'](pos, size, angle, pivot)

    def __resetMotion (self):
        """ sets a new movement start point """
        # store absolute position at the beginning of the movement
        # for 2-finger-movements:
        def getPos(pos):
            return getAbsPos(pos, self.__node.pos, self.__node.angle, self.__node.pivot)
        self.o_tl = getPos((0, 0))
        self.o_bl = getPos((0, self.__node.height))
        self.o_tr = getPos((self.__node.width, 0))

        # for 1-finger-movements:
        self.__oldpos = self.__node.pos

        self.__lastFixPoint = None
        self.__callback['onResetMotion']()