Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Kivy demo #18

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 86 additions & 0 deletions src/FGAme/demos/integrations/kivy_input.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
from kivy.app import App
from kivy.clock import Clock
from kivy.uix.relativelayout import RelativeLayout

from FGAme import listen
from FGAme.kivy_backend import KivyWorld, KivyMainLoop, KivyInput


@listen('key-up', 'up')
def key_up(*args):
print('key-up')


@listen('key-down', 'down')
def key_down():
print('key-down')


@listen('mouse-button-down')
def mouse_down(x, y):
print(x)
print(y)


@listen('mouse-button-up')
def mouse_up(*args):
print("zeca")


@listen('mouse-long-press', 'left')
def mouse_long_press(*args):
print(args)


@listen('long-press', 'x')
def long_press(*args):
print('x')


class WorldExample(KivyWorld):

def init_objects(self):
self.add.circle(20, pos=(100, 110), vel=(300, 0), color='random')
self.add.aabb(shape=(20, 120), pos=(440, 300), color='random')
self.add.rectangle(shape=(20, 120), pos=(300, 100), color='red')
self.add.rectangle(shape=(20, 120), pos=(700, 100), color='red')
self.add.poly([(100, 100), (200, 100), (200, 200), (50, 200), (75, 125)],
vel=(100, 100), pos=(400, 100), color='random')
self.add.margin()


class FGAmeWidget(RelativeLayout):
"""
Wraps an FGAme World built with circles.
"""

def __init__(self, world, fps=60):
super().__init__()
self.mainloop = KivyMainLoop(None, KivyInput(), fps, self)
self.world = world
self.world.widget = self
self.world.init_objects()
Clock.schedule_interval(self.fgame_step, self.mainloop.dt)

def fgame_step(self, dt):
self.mainloop.step(self.world)


class FGAmeApp(App):
"""
Kivy App that runs FGAme simulation.
"""

def __init__(self, world, widget=None):
super().__init__()
self.world = world
self.widget = widget

def build(self):
if self.widget is None:
self.widget = FGAmeWidget(self.world)
return self.widget


if __name__ == '__main__':
FGAmeApp(WorldExample()).run()
208 changes: 208 additions & 0 deletions src/FGAme/kivy_backend.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
from FGAme.input import Input
from FGAme import World
from FGAme import Circle, AABB, Rectangle as FGAmeRectangle, Poly
from FGAme.mainloop import MainLoop

from kivy.core.window import Window
from kivy.graphics import Color, Ellipse
from kivy.graphics import Rectangle, Rotate, PushMatrix, PopMatrix, Translate
from kivy.graphics.tesselator import Tesselator
from kivy.graphics import Mesh

from math import pi

import time

from functools import singledispatch

from queue import Queue


class KivyInput(Input):

def __init__(self):
super(KivyInput, self).__init__()
self.events = Queue()
# algumas teclas possuem nomes diferentes
self.keys = {v: k for k, v in Window._system_keyboard.keycodes.items()}
Window.bind(on_key_up=self.add_on_key_up)
Window.bind(on_key_down=self.add_on_key_down)
Window.bind(on_touch_down=self.add_on_touch_down)
Window.bind(on_touch_up=self.add_on_touch_up)
self.hold_mouse = True
self.hold_key = True

def poll(self):

while not self.events.empty():
event = self.events.get()
if event.type == 'key-up':
self.process_key_up(event.key)
elif event.type == 'key-down':
self.process_key_down(event.key)
elif event.type == 'long-press':
self.process_long_press()
elif event.type == 'mouse-button-down':
self.process_mouse_button_down('left', event.pos)
elif event.type == 'mouse-button-up':
self.process_mouse_button_up('left', event.pos)
elif event.type == 'mouse-long-press':
self.process_mouse_longpress()

def add_on_key_up(self, *args):
self.events.put(Event('key-up', self.keys[args[1]], None))
self.hold_key = True

def add_on_key_down(self, *args):

if self.hold_key:
self.events.put(Event('key-down', self.keys[args[1]], None))
self.hold_key = False

self.events.put(Event('long-press', self.keys[args[1]], None))

def add_on_touch_down(self, *args):

self.hold_mouse = True

self.events.put(Event('mouse-button-down', None, args[1].pos))

if self.hold_mouse:
import threading

def work():
while self.hold_mouse:
self.events.put(
Event(
'mouse-long-press',
None,
args[1].pos))
time.sleep(0.1)

t = threading.Thread(target=work)
t.daemon = True
t.start()

def add_on_touch_up(self, *args):
self.events.put(Event('mouse-button-up', None, args[1].pos))
self.hold_mouse = False


class Event:

def __init__(self, type, key, pos):
self.type = type
self.key = key
self.pos = pos


class KivyObjectWrapper:
def __init__(self, obj_fgame, obj_kivy, translation=None, rotation=None):
self.obj_fgame = obj_fgame
self.obj_kivy = obj_kivy
self.translation = translation
self.rotation = rotation
self.initial_pos = obj_fgame.pos


class KivyWorld(World):

def __init__(self, widget=None):
super().__init__()
self.widget = widget
self.objects = []

def _add(self, obj, layer=0):
super()._add(obj, layer)
register_to_canvas(obj, self)

def update(self, dt):
super().update(dt)
for obj in self.objects:
if isinstance(obj.obj_fgame, FGAmeRectangle) or isinstance(
obj.obj_fgame, Poly):
obj.translation.x = obj.obj_fgame.pos.x - obj.initial_pos.x
obj.translation.y = obj.obj_fgame.pos.y - obj.initial_pos.y
obj.rotation.angle = obj.obj_fgame.theta * 180.0 / pi
else:
obj.obj_kivy.pos = obj.obj_fgame.pos_sw


@singledispatch
def register_to_canvas(obj, world):
pass


@register_to_canvas.register(Circle)
def _(obj, world):
with world.widget.canvas:
diameter = 2 * obj.radius
Color(*obj.color.rgbf)
kcircle = Ellipse(pos=obj.pos_sw, size=(diameter, diameter))
world.objects.append(KivyObjectWrapper(obj, kcircle))


@register_to_canvas.register(AABB)
def _(obj, world):
with world.widget.canvas:
Color(*obj.color.rgbf)
x, y = obj.xmax - obj.xmin, obj.ymax - obj.ymin
krectangle = Rectangle(pos=obj.pos_sw, size=(x, y))
world.objects.append(KivyObjectWrapper(obj, krectangle))


@register_to_canvas.register(Poly)
def _(obj, world):
tess = Tesselator()
vertices = []
for x, y in obj.vertices:
vertices.append(x)
vertices.append(y)
tess.add_contour(vertices)
if not tess.tesselate():
raise Exception('Tesselator didn\'t work')
with world.widget.canvas:
Color(*obj.color.rgbf)
PushMatrix()
translation = Translate(0, 0)
rotation = Rotate(axis=(0, 0, 1), origin=(obj.pos.x, obj.pos.y, 0))
for vertices, indices in tess.meshes:
Mesh(vertices=vertices, indices=indices, mode="triangle_fan")
PopMatrix()
world.objects.append(
KivyObjectWrapper(
obj,
tess,
translation,
rotation))


@register_to_canvas.register(FGAmeRectangle)
def _(obj, world):
with world.widget.canvas:
PushMatrix()
translation = Translate(0, 0)
rotation = Rotate(axis=(0, 0, 1), origin=(obj.pos.x, obj.pos.y, 0))
Color(*obj.color.rgbf)
x, y = obj.xmax - obj.xmin, obj.ymax - obj.ymin
krectangle = Rectangle(pos=obj.pos_sw, size=(x, y))
PopMatrix()
world.objects.append(
KivyObjectWrapper(
obj,
krectangle,
translation,
rotation))


class KivyMainLoop(MainLoop):

def __init__(self, screen, input, fps=None, widget=None):
super().__init__(screen, input, fps)
self.widget = widget

def step_screen(self, world):
pass

def sleep(self, dt):
pass
5 changes: 4 additions & 1 deletion src/FGAme/mainloop.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ def step_endframe(self, state, start_time, wait=True):
self.n_skip += 1
frame_skip_signal.trigger(-wait)
elif wait:
time.sleep(self.sleep_time)
self.sleep(self.sleep_time)

self.time += self.dt
self.n_iter += 1
Expand All @@ -173,6 +173,9 @@ def stop(self):

self._running = False

def sleep(self, dt):
time.sleep(dt)

def schedule(self, time, function=None, args=None, kwargs=None,
**passed_kwargs):
"""
Expand Down