kivy/examples/widgets/customcollide.py

82 lines
2.2 KiB
Python

'''
Custom shape & collide widget
=============================
This is a Triangle widget with a triangle shape based on 3 points (p1, p2, p3),
plus a custom collision function.
The p1, p2, p3 are automatically calculated from the position and the size of
the Widget bounding box. We are using them to draw the triangle shape.
(Please note in the kv the special case for Scatter.)
Then, we need to setup a new collision function to collide only on the triangle.
We are using a external method that will check if a point is inside a polygon
(we consider our triangle as a polygon).
'''
import kivy
kivy.require('1.0.8')
from kivy.uix.scatter import Scatter
from kivy.properties import ListProperty
from kivy.lang import Builder
Builder.load_string('''
<Triangle>:
# example for doing a triangle
# this will automatically recalculate pX from pos/size
p1: 0, 0
p2: self.width, 0
p3: self.width / 2, self.height
# If you use a Widget instead of Scatter as base class, you need that:
#p1: self.pos
#p2: self.right, self.y
#p3: self.center_x, self.top
# draw something
canvas:
Color:
rgb: 1, 0, 0
Triangle:
points: self.p1 + self.p2 + self.p3
''')
def point_inside_polygon(x, y, poly):
'''Taken from http://www.ariel.com.au/a/python-point-int-poly.html
'''
n = len(poly)
inside = False
p1x = poly[0]
p1y = poly[1]
for i in range(0, n + 2, 2):
p2x = poly[i % n]
p2y = poly[(i + 1) % n]
if y > min(p1y, p2y):
if y <= max(p1y, p2y):
if x <= max(p1x, p2x):
if p1y != p2y:
xinters = (y - p1y) * (p2x - p1x) / (p2y - p1y) + p1x
if p1x == p2x or x <= xinters:
inside = not inside
p1x, p1y = p2x, p2y
return inside
class Triangle(Scatter):
p1 = ListProperty([0, 0])
p2 = ListProperty([0, 0])
p3 = ListProperty([0, 0])
def collide_point(self, x, y):
x, y = self.to_local(x, y)
return point_inside_polygon(x, y,
self.p1 + self.p2 + self.p3)
if __name__ == '__main__':
from kivy.base import runTouchApp
runTouchApp(Triangle(size_hint=(None, None)))