From 233c384c6c53f91e1405d797d9683f3296ddabc1 Mon Sep 17 00:00:00 2001 From: Nicolas Schodet Date: Sat, 14 Mar 2009 15:57:17 +0100 Subject: * host/inter, host/simu: - moved drawable and trans_matrix to the new directory structure. - new drawable behaviour. --- host/simu/inter/__init__.py | 0 host/simu/inter/drawable.py | 210 ++++++++++++++++++++++++++++++++++ host/simu/inter/test/test_drawable.py | 65 +++++++++++ 3 files changed, 275 insertions(+) create mode 100644 host/simu/inter/__init__.py create mode 100644 host/simu/inter/drawable.py create mode 100644 host/simu/inter/test/test_drawable.py (limited to 'host/simu/inter') diff --git a/host/simu/inter/__init__.py b/host/simu/inter/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/host/simu/inter/drawable.py b/host/simu/inter/drawable.py new file mode 100644 index 00000000..6f04ffd8 --- /dev/null +++ b/host/simu/inter/drawable.py @@ -0,0 +1,210 @@ +# inter - Robot simulation interface. {{{ +# +# Copyright (C) 2008 Nicolas Schodet +# +# APBTeam: +# Web: http://apbteam.org/ +# Email: team AT apbteam DOT org +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# }}} +"""Drawable and DrawableCanvas.""" +from math import sqrt, degrees +import simu.utils.trans_matrix +import Tkinter + +__all__ = ('Drawable', 'DrawableCanvas') + +class Drawable: + """Define a drawable area with embedded transformations.""" + + def __init__ (self, onto): + """Initialise the drawable.""" + self.__onto = onto + self.__trans_matrix = simu.utils.trans_matrix.TransMatrix () + self.__items = [ ] + self.trans_apply = self.__trans_matrix.apply + self.trans_apply_angle = self.__trans_matrix.apply_angle + self.trans_apply_distance = self.__trans_matrix.apply_distance + self.trans_rotate = self.__trans_matrix.rotate + self.trans_translate = self.__trans_matrix.translate + self.trans_scale = self.__trans_matrix.scale + self.trans_identity = self.__trans_matrix.identity + self.__children = [ ] + self.children = self.__children + self.__onto.__children.append (self) + + def __draw_rectangle (self, p1, p2, **kw): + if 'outline' not in kw: + kw = kw.copy () + kw['outline'] = 'black' + p = self.trans_apply (p1, (p2[0], p1[1]), p2, (p1[0], p2[1])) + return self.__onto.__draw_polygon (*p, **kw) + + def __draw_line (self, *p, **kw): + p = self.trans_apply (*p) + return self.__onto.__draw_line (*p, **kw) + + def __draw_polygon (self, *p, **kw): + p = self.trans_apply (*p) + return self.__onto.__draw_polygon (*p, **kw) + + def __draw_circle (self, p, r, **kw): + p = self.trans_apply (p) + r = self.trans_apply_distance (r) + return self.__onto.__draw_circle (p, r, **kw) + + def __draw_arc (self, p, r, **kw): + p = self.trans_apply (p) + r = self.trans_apply_distance (r) + if 'start' in kw: + kw = kw.copy () + kw['start'] = self.trans_apply_angle (kw['start']) + import math + return self.__onto.__draw_arc (p, r, **kw) + + def draw_rectangle (self, *p, **kw): + """Draw a rectangle.""" + self.__items.append (self.__draw_rectangle (*p, **kw)) + + def draw_line (self, *p, **kw): + """Draw a line.""" + self.__items.append (self.__draw_line (*p, **kw)) + + def draw_polygon (self, *p, **kw): + """Draw a line.""" + self.__items.append (self.__draw_polygon (*p, **kw)) + + def draw_circle (self, p, r, **kw): + """Draw a circle of the given radius centered on p.""" + self.__items.append (self.__draw_circle (p, r, **kw)) + + def draw_arc (self, p, r, **kw): + """Draw a arc of the given radius centered on p.""" + self.__items.append (self.__draw_arc (p, r, **kw)) + + def reset (self): + """Clear all drawn items, reset transformations.""" + for i in self.__children: + i.reset () + self.__delete (*self.__items) + self.__items = [ ] + self.trans_identity () + + def __delete (self, *list): + """Delete a list of items.""" + self.__onto.__delete (*list) + + def update (self, *list): + """Add provided arguments to update list, or self if no argument + provided.""" + if list: + self.__onto.update (*list) + else: + self.__onto.update (self) + + def draw (self): + """Default drawing method which redraw every children.""" + if self.__children: + self.update (*self.__children) + + +class DrawableCanvas(Tkinter.Canvas): + """Extend a Tkinter.Canvas to use Drawable on it. Children are drawn on + redraw.""" + + def __init__ (self, width, height, xorigin, yorigin, master = None, **kw): + """Initialise a DrawableCanvas. The width and height parameters + define the requested drawable area virtual size. The xorigin and + yorigin parameters define origin of the virtual coordinates relative + to the drawable center.""" + Tkinter.Canvas.__init__ (self, master, **kw) + self.__width = width + self.__height = height + self.__xorigin = xorigin + self.__yorigin = yorigin + self.bind ('', self.__resize) + self.__updated = [ ] + self._Drawable__children = [ ] + + def __resize (self, ev): + # Compute new scale. + w, h = float (ev.width), float (ev.height) + self.__scale = min (w / self.__width, h / self.__height) + self.__xoffset = w / 2 + self.__xorigin * self.__scale + self.__yoffset = h / 2 - self.__yorigin * self.__scale + # Redraw. + self.draw () + + def _Drawable__draw_line (self, *p, **kw): + p = self.__coord (*p) + return self.create_line (*p, **kw) + + def _Drawable__draw_polygon (self, *p, **kw): + p = self.__coord (*p) + return self.create_polygon (*p, **kw) + + def _Drawable__draw_circle (self, p, r, **kw): + p, = self.__coord (p) + r = r * self.__scale + p1 = (p[0] - r, p[1] - r) + p2 = (p[0] + r, p[1] + r) + return self.create_oval (p1, p2, **kw) + + def _Drawable__draw_arc (self, p, r, **kw): + p, = self.__coord (p) + r = r * self.__scale + p1 = (p[0] - r, p[1] - r) + p2 = (p[0] + r, p[1] + r) + for k in ('start', 'extent'): + if k in kw: + kw = kw.copy () + kw[k] = degrees (kw[k]) + return self.create_arc (p1, p2, **kw) + + def _Drawable__delete (self, *list): + self.delete (*list) + + def update (self, *list): + """If called with arguments, add them to the update list. + Else, redraw all element in the update list.""" + if list: + for i in list: + if i not in self.__updated: + self.__updated.append (i) + else: + while self.__updated: + updated = self.__updated + self.__updated = [ ] + for i in updated: + i.draw () + + def draw (self): + """Default drawing method which redraw every children.""" + if self._Drawable__children: + self.update (*self._Drawable__children) + self.update () + + def __coord (self, *args): + return [ (i[0] * self.__scale + self.__xoffset, + -i[1] * self.__scale + self.__yoffset) for i in args ] + + def screen_coord (self, screen): + """Return drawable coordinates corresponding to the given screen + coordinates.""" + return ((self.canvasx (screen[0]) - self.__xoffset) / self.__scale, + -(self.canvasy (screen[1]) - self.__yoffset) / self.__scale) + diff --git a/host/simu/inter/test/test_drawable.py b/host/simu/inter/test/test_drawable.py new file mode 100644 index 00000000..f67cb144 --- /dev/null +++ b/host/simu/inter/test/test_drawable.py @@ -0,0 +1,65 @@ +# inter - Robot simulation interface. {{{ +# +# Copyright (C) 2008 Nicolas Schodet +# +# APBTeam: +# Web: http://apbteam.org/ +# Email: team AT apbteam DOT org +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# }}} */ +from math import pi + +from simu.inter.drawable import * + +class Test (Drawable): + + def draw (self): + self.draw_rectangle ((-100, -100), (100, 100), fill = '', outline = 'gray') + self.draw_rectangle ((0, 0), (5, 5), fill = 'red') + self.draw_rectangle ((20, 20), (50, 50), fill = '', outline = 'blue') + self.draw_line ((20, 20), (25, 25), (80, 0), (0, 80), fill = 'green') + self.draw_line ((20, 20), (25, 25), (80, 0), (0, 80), smooth = True) + self.draw_circle ((40, -40), 10) + self.draw_arc ((-40, 0), 20, start = pi / 4, extent = pi / 2) + +class App (DrawableCanvas): + + def __init__ (self, master = None): + DrawableCanvas.__init__ (self, 300, 300, 20, 20, master) + self.pack (expand = True, fill = 'both') + self.test = Test (self) + self.animated = False + self.i = 0 + + def animate (self): + # Real user should reset at each redraw. + self.after (500, self.animate) + self.test.draw () + self.test.trans_rotate (-pi/12) + self.test.trans_translate ((10, 10)) + self.test.trans_scale (1.05) + self.i += 1 + if self.i == 10: + self.test.reset () + + def draw (self): + if not self.animated: + self.animate () + self.animated = True + +app = App () +app.mainloop () -- cgit v1.2.3