"""
Basic Pixel Perfect Point Collision in kivy
Overriding the Image's [collide_point] method to use the underlying [core.image.Image]'s
[read_pixel] method and access the alpha information of the colliding pixel.
The [keep_data: True] statement is essential, before assigning (loading) a texture.
A deviation from the [size_hint: None, None] and [size: self.texture_size], along with
the use of [allow_stretch] and other size modifications, could probably be allowed,
by using the [norm_image_size] property, but the scaling and rotation of a ScatterLayout
(as in the current example) works fine as is.
Also note that the image's local [y] coordinate is inverse as far as [read_pixel] is
concerned.
(Could it be the reason why this approach doesn't seem to work with atlases?)
Thanks to niavlys for his 3/14/14 post:
(https://groups.google.com/forum/#!topic/kivy-users/BnjrZT0NkhU)
and to tshirtman for his:
(7/24/14 https://groups.google.com/forum/#!topic/kivy-dev/dpT7yqs_Mq0)
unjuan 2015
"""
from kivy.app import App
from kivy.uix.scatterlayout import ScatterLayout
from kivy.uix.image import Image
from kivy.lang import Builder
Builder.load_string('''
<RootLayout>:
CustomImage:
keep_data: True
size_hint: None, None
size: self.texture_size
pos_hint: {'center_x': .5, 'center_y': .5}
source: 'wolf.png'
opacity: .3
''')
class CustomImage(Image):
def __init__(self, **kwargs):
# ################################### un-comment for a python-only version
# kwargs.setdefault('keep_data', True)
# kwargs.setdefault('size_hint', (None,None))
super(CustomImage, self).__init__(**kwargs)
# self.pos_hint = {'center_x': .5, 'center_y': .5}
# self.opacity = .3
#######################################################################
def collide_point(self, x, y):
# Do not want to upset the read_pixel method, in case of a bound error
try:
color = self._coreimage.read_pixel(x - self.x, self.height - (y - self.y))
except:
color = 0, 0, 0, 0
if color[-1] > 0:
return True
return False
def on_touch_down(self, touch):
if self.collide_point(*touch.pos):
self.opacity = 1
else:
self.opacity = .3
class RootLayout(ScatterLayout):
def __init__(self, **kwargs):
super(RootLayout, self).__init__(**kwargs)
# ################################### un-comment for python-only version
# cimage = CustomImage()
# cimage.source = 'wolf.png'
# cimage.size = cimage.texture.size
# self.add_widget(cimage)
###########################################################################
class CollTestApp(App):
def build(self):
return RootLayout()
if __name__ == '__main__':
CollTestApp().run()
import sys
from glob import glob
from os.path import join, dirname
from kivy.uix.scatter import Scatter
from kivy.app import App
from kivy.graphics.svg import Svg
from kivy.graphics import (
Canvas, Translate, Fbo, ClearColor, ClearBuffers, Scale)
from kivy.core.window import Window
from kivy.lang import Builder
from kivy.uix.floatlayout import FloatLayout
Builder.load_string("""
<SvgWidget>:
id:s
do_rotation: False
<FloatLayout>:
id:f
canvas.before:
Color:
rgb: (1, 1, 1)
Rectangle:
pos: self.pos
size: self.size
""")
class SvgWidget(Scatter):
def __init__(self, filename, **kwargs):
super(SvgWidget, self).__init__(**kwargs)
with self.canvas:
svg = Svg(filename)
self.size = svg.width, svg.height
def get_widget_pixel_color(self,x_pos,y_pos):
self.size = (Window.size[0]-self.x, Window.size[1]-self.y)
if self.parent is not None:
canvas_parent_index = self.parent.canvas.indexof(self.canvas)
if canvas_parent_index > -1:
self.parent.canvas.remove(self.canvas)
fbo = Fbo(size=self.size, with_stencilbuffer=True)
with fbo:
ClearColor(0, 0, 0, 0)
ClearBuffers()
Scale(1, -1, 1)
Translate(-self.x, -self.y - self.height, 0)
fbo.add(self.canvas)
fbo.draw()
x_pos = int(x_pos)
y_pos = int(y_pos)
color = fbo.get_pixel_color(x_pos-self.x, self.height-y_pos+self.y)
fbo.remove(self.canvas)
if self.parent is not None and canvas_parent_index > -1:
self.parent.canvas.insert(canvas_parent_index, self.canvas)
#======fixed but WTH?
if x_pos-self.x<0:
#print color
color = [0,0,0,0]
#====================
return color
def my_collide_point(self, x_pos, y_pos):
color = self.get_widget_pixel_color(x_pos,y_pos)
alpha = color[-1]
print color
if alpha > 0:
return True
return False
def on_touch_down(self,touch):
print self.my_collide_point(*touch.pos)
return super(SvgWidget, self).on_touch_down(touch)
class SvgApp(App):
def build(self):
self.root = FloatLayout()
filenames = sys.argv[1:]
if not filenames:
filenames = glob(join(dirname(__file__), '*.svg'))
for filename in filenames:
svg = SvgWidget(filename, size_hint=(None, None))
# svg = SvgWidget(filename)
self.root.add_widget(svg)
svg.scale = 5.
svg.center = Window.center
if __name__ == '__main__':
SvgApp().run()