Search code examples
pythonkivytouch

KIVY collide_point vertex Rectangle


I am having a custom widget with a rectangle and horizontal line both created using Vertex Instruction. I want to check whether users is touching within rectangle or horizontal line in my widget. Tried using Group but unable to find whether user touched rectangle or Line. Can you please provide me with clue. Find below sample code.

from kivy.app import App
from kivy.graphics import Line
from kivy.uix.scatter import Scatter
from kivy.uix.relativelayout import RelativeLayout
from kivy.lang import Builder

KV = '''
<Actor>:
    id: Actor
    canvas:
        Color:
            rgba: 0,1,0,1
        Rectangle:
            group: 'rect'
            size: 100, 30
            pos: 0, root.height - 30
        Line:
            group: 'line'
            points: 50, root.height - 30, 50, 0
            width:2
    Label:
        id: _actr_lbl
        text: 'Hello World'
        markup: True
        color: 0,0,0,1
        size_hint: None, None
        size: 100, 30
        pos: 0, root.height - 30
'''

class Actor(Scatter):
    def __init__(self, **kwargs):
        super(Actor, self).__init__(**kwargs)

    def on_touch_down(self, touch):

        print('Touch location {} Actor location {} Actor Size {}'.format(touch, self.pos, self.size))
        if self.collide_point(*touch.pos) :
           for aVertex in self.canvas.get_group('rect') :
               try:
                   print ('Vertex size {} and pos'.format(aVertex.size, aVertex.pos))
               except:
                   pass
           return True

        return super(Actor, self).on_touch_down(touch)

class MyPaintApp(App):

    def build(self):
        Builder.load_string(KV)
        root = RelativeLayout()
        root.add_widget(Actor(pos_hint={'center_x':0.5, 'center_y':0.5}, size_hint=(.2, 1.)))
        return root

if __name__ == '__main__':
    MyPaintApp().run()

Thanks in advance


Solution

  • You can do a simple bounding box check, but you must take into account the fact that the touch is in the parent coordinate system. So you can convert the touch position to local coordinates, then do the test. Here is an example for the Rectangle:

    def on_touch_down(self, touch):
    
        print('Touch location {} Actor location {} Actor Size {}'.format(touch, self.pos, self.size))
        if self.collide_point(*touch.pos) :
            localTouchPos = self.to_local(*touch.pos)
            for aVertex in self.canvas.get_group('rect') :
                print('\tVertex size {} and pos {}'.format(aVertex.size, aVertex.pos))
                intersection = True
                if localTouchPos[0] < aVertex.pos[0]:
                    intersection = False
                elif localTouchPos[0] > aVertex.pos[0] + aVertex.size[0]:
                    intersection = False
                if localTouchPos[1] < aVertex.pos[1]:
                    intersection = False
                elif localTouchPos[1] > aVertex.pos[1] + aVertex.size[1]:
                    intersection = False
            print('intersection =', intersection)
            return True
    
        return super(Actor, self).on_touch_down(touch)
    

    You can do something similar for the Line, but it may be a bit more complicated if you want to do a general Line. If your Line is always vertical, it hould be very similar.