From 7481b2acb750ad311691a35011144cb86df191ab Mon Sep 17 00:00:00 2001 From: Walter Bender Date: Tue, 07 Jan 2014 00:00:26 +0000 Subject: add icon palettes to tools --- diff --git a/activity.py b/activity.py index 129974b..823143a 100644 --- a/activity.py +++ b/activity.py @@ -41,6 +41,8 @@ from sugar.graphics.toolbarbox import ToolbarBox from sugar.graphics.toolbarbox import ToolbarButton from sugar.graphics.style import GRID_CELL_SIZE from sugar.datastore import datastore +from sugar.graphics.icon import Icon +from sugar.graphics import style import tools import physics @@ -214,19 +216,122 @@ class PhysicsActivity(activity.Activity): button.set_group(None) firstButton = button - if c.name == tools.EraseAllTool.name: - # Add a separator - Erase all tool is a dangerous tool - separator = gtk.SeparatorToolItem() - _insert_item(create_toolbar, separator, -1) - separator.show() - button.set_tooltip(c.toolTip) button.set_accelerator(c.toolAccelerator) button.connect('clicked', self.radioClicked) + palette = self._build_palette(c) + if palette is not None: + button.get_palette().set_content(palette) _insert_item(create_toolbar, button, -1) button.show() self.radioList[button] = c.name + def __icon_path(self, name): + activity_path = activity.get_bundle_path() + icon_path = os.path.join(activity_path, 'icons', + name+".svg") + return icon_path + + def _build_palette(self, tool): + if tool.palette_enabled: + if tool.palette_mode == tools.PALETTE_MODE_SLIDER_ICON: + settings = tool.palette_settings + hbox = gtk.HBox() + hbox.set_size_request(-1, 50) + + icon1 = Icon() + icon1.set_file(self.__icon_path(settings['icon1'])) + icon2 = Icon() + icon2.set_file(self.__icon_path(settings['icon2'])) + + adj = gtk.Adjustment(lower=settings['min'], + upper=settings['max'], + step_incr=1) + slider = gtk.HScale(adjustment=adj) + slider.set_size_request(200, 25) + slider.connect("value-changed", self._slider_palette, + tool.name) + slider.set_value(settings['min']) + + hbox.pack_start(icon1, False, False, 0) + hbox.pack_start(slider, True, True, 0) + hbox.pack_start(icon2, False, False, 0) + hbox.show_all() + return hbox + elif tool.palette_mode == tools.PALETTE_MODE_SLIDER_LABEL: + table = gtk.Table(rows=len(tool.palette_settings), columns=2) + + table.set_row_spacings(spacing=style.DEFAULT_SPACING) + table.set_col_spacings(spacing=style.DEFAULT_SPACING) + for row, settings in enumerate(tool.palette_settings): + label = gtk.Label(settings["label"]) + + adj = gtk.Adjustment(lower=settings['min'], + upper=settings['max'], + step_incr=1) + slider = gtk.HScale(adjustment=adj) + + slider.set_size_request(200, 50) + slider.connect("value-changed", self._slider_label_palette, + tool.name, settings['data']) + slider.set_value(settings['min']) + + table.attach(label, + left_attach=0, + right_attach=1, + top_attach=row, + bottom_attach=row+1) + table.attach(slider, + left_attach=1, + right_attach=2, + top_attach=row, + bottom_attach=row+1) + table.show_all() + return table + elif tool.palette_mode == tools.PALETTE_MODE_ICONS: + vbox = gtk.VBox() + for settings in tool.palette_settings: + hbox = gtk.HBox() + firstButton = None + for i in range(0, settings['icon_count']): + button = RadioToolButton( + named_icon=settings['icons'][i]) + if firstButton: + button.set_group(firstButton) + else: + button.set_group(None) + firstButton = button + button.connect('clicked', + self._palette_icon_clicked, + tool.name, + settings['name'], + settings['icon_values'][i]) + if settings['active'] == settings['icons'][i]: + button.set_active(True) + hbox.pack_start(button, False, False, 0) + vbox.add(hbox) + vbox.show_all() + return vbox + + return None + + def _palette_icon_clicked(self, button, toolname, value_name, value): + for tool in tools.allTools: + if tool.name == toolname: + tool.palette_data[value_name] = value + + def _slider_label_palette(self, slider, toolname, dataname): + value = slider.get_value() + for tool in tools.allTools: + if tool.name == toolname: + tool.palette_data[dataname] = value + + def _slider_palette(self, slider, toolname): + value = slider.get_value() + for tool in tools.allTools: + if tool.name == toolname: + tool.palette_data = value + def clear_trace_alert_cb(self, alert, response): self.remove_alert(alert) if response is gtk.RESPONSE_OK: @@ -259,13 +364,14 @@ class PhysicsActivity(activity.Activity): if response_id is gtk.RESPONSE_OK: pygame.event.post(pygame.event.Event(pygame.USEREVENT, action='clear_all')) - - clear_all_alert = ConfirmationAlert() - clear_all_alert.props.title = _('Are You Sure?') - clear_all_alert.props.msg = \ - _('All your work will be discarded. This cannot be undone!') - clear_all_alert.connect('response', clear_all_alert_cb) - self.add_alert(clear_all_alert) + if len(self.game.world.world.GetBodyList()) > 2: + print len(self.game.world.world.GetBodyList()) + clear_all_alert = ConfirmationAlert() + clear_all_alert.props.title = _('Are You Sure?') + clear_all_alert.props.msg = \ + _('All your work will be discarded. This cannot be undone!') + clear_all_alert.connect('response', clear_all_alert_cb) + self.add_alert(clear_all_alert) def radioClicked(self, button): pygame.event.post(pygame.event.Event(pygame.USEREVENT, diff --git a/elements/elements.py b/elements/elements.py index 625844c..4033688 100644 --- a/elements/elements.py +++ b/elements/elements.py @@ -430,18 +430,18 @@ class Elements: return variables - def json_save(self, path, additional_vars = {}, serialize=False): + def json_save(self, path, additional_vars={}, serialize=False): import json worldmodel = {} save_id_index = 1 - self.world.GetGroundBody().userData = {"saveid" : 0} + self.world.GetGroundBody().userData = {"saveid": 0} bodylist = [] for body in self.world.GetBodyList(): if not body == self.world.GetGroundBody(): - body.userData["saveid"] = save_id_index #set temporary data - save_id_index+=1 + body.userData["saveid"] = save_id_index # set temporary data + save_id_index += 1 shapelist = body.GetShapeList() modelbody = {} modelbody['position'] = body.position.tuple() @@ -458,7 +458,7 @@ class Elements: modelshape['restitution'] = shape.restitution modelshape['friction'] = shape.friction shapename = shape.__class__.__name__ - if shapename == "b2CircleShape": + if shapename == "b2CircleShape": modelshape['type'] = 'circle' modelshape['radius'] = shape.radius modelshape['localPosition'] = shape.localPosition.tuple() @@ -493,7 +493,6 @@ class Elements: modeljoint['collideConnected'] = joint.collideConnected modeljoint['userData'] = joint.userData - jointlist.append(modeljoint) worldmodel['jointlist'] = jointlist @@ -507,8 +506,11 @@ class Elements: backup = trackinfo for key, info in backup.iteritems(): if not info[3]: - trackinfo[key][0] = info[0].userData['saveid'] - trackinfo[key][1] = info[1].userData['saveid'] + try: + trackinfo[key][0] = info[0].userData['saveid'] + trackinfo[key][1] = info[1].userData['saveid'] + except AttributeError: + pass else: addvars['trackinfo'][key][0] = None addvars['trackinfo'][key][1] = None @@ -516,12 +518,12 @@ class Elements: additional_vars['trackinfo'] = trackinfo worldmodel['additional_vars'] = additional_vars - f = open(path,'w') + f = open(path, 'w') f.write(json.dumps(worldmodel)) f.close() for body in self.world.GetBodyList(): - del body.userData['saveid'] #remove temporary data + del body.userData['saveid'] # remove temporary data def json_load(self, path, serialized=False): import json diff --git a/helpers.py b/helpers.py index 08c5da0..1d0d2fe 100644 --- a/helpers.py +++ b/helpers.py @@ -1,48 +1,51 @@ -# Physics, a 2D Physics Playground for Kids - -# Copyright (C) 2008 Alex Levenson and Brian Jordan -# Copyright (C) 2013 Walter Bender - -# 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 3 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, see . - +""" + Physics, a 2D Physics Playground for Kids + Copyright (C) 2008 Alex Levenson and Brian Jordan + + 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 3 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, see . + +""" +#================================================================== +# Physics.activity +# Helper classes and functions +# By Alex Levenson +#================================================================== import math - def distance(pt1, pt2): - '''Distance calculator, pt1 and pt2 are ordred pairs.''' + """Distance calculator, pt1 and pt2 are ordred pairs. + """ return math.sqrt((pt1[0] - pt2[0]) ** 2 + (pt1[1] - pt2[1]) ** 2) - def getAngle(pt1, pt2): - '''Returns angle between line segment pt1 -> pt2 and x axis, - from -pi to pi.''' + """Returns angle between line segment pt1 -> pt2 and x axis, from -pi to pi. + """ xcomp = pt2[0] - pt1[0] ycomp = pt1[1] - pt2[1] return math.atan2(ycomp, xcomp) - def constructTriangleFromLine(p1, p2): - '''Returns list of ordered pairs describing equilteral triangle around - segment pt1 --> pt2.''' + """Returns list of ordered pairs describing equilteral triangle around segment pt1 --> pt2. + """ halfHeightVector = (0.57735 * (p2[1] - p1[1]), 0.57735 * (p2[0] - p1[0])) p3 = (p1[0] + halfHeightVector[0], p1[1] - halfHeightVector[1]) p4 = (p1[0] - halfHeightVector[0], p1[1] + halfHeightVector[1]) return [p2, p3, p4] - def polyArea(vertices): - '''Returns the area of a polygon.''' + """Returns the area of a polygon. + """ n = len(vertices) A = 0 p = n - 1 @@ -53,10 +56,12 @@ def polyArea(vertices): q += 1 return A / 2.0 - + def insideTriangle(pt, triangle): - '''Returns true if pt is in triangle. - Some polygon magic, thanks to John W. Ratcliff on www.flipcode.com''' + """Returns true if pt is in triangle. + + Some polygon magic, thanks to John W. Ratcliff on www.flipcode.com + """ ax = triangle[2][0] - triangle[1][0] ay = triangle[2][1] - triangle[1][1] bx = triangle[0][0] - triangle[2][0] @@ -72,10 +77,9 @@ def insideTriangle(pt, triangle): aCROSSbp = ax * bpy - ay * bpx cCROSSap = cx * apy - cy * apx - bCROSScp = bx * cpy - by * cpx + bCROSScp = bx * cpy - by * cpx return aCROSSbp >= 0.0 and bCROSScp >= 0.0 and cCROSSap >= 0.0 - def polySnip(vertices, u, v, w, n): EPSILON = 0.0000000001 @@ -103,12 +107,12 @@ def polySnip(vertices, u, v, w, n): def decomposePoly(vertices): - '''Decomposes a polygon into its triangles.''' + """Decomposes a polygon into its triangles. + """ vertices = list(vertices) n = len(vertices) result = [] - if(n < 3): - return [] # not a poly! + if(n < 3): return [] # not a poly! # Force counter-clockwise polygon if 0 >= polyArea(vertices): @@ -116,23 +120,20 @@ def decomposePoly(vertices): # Remove nv-2 vertices, creating 1 triangle every time nv = n - count = 2 * nv # error detection + count = 2 * nv # error detection v = nv - 1 while nv > 2: count -= 1 if 0 >= count: - return [] # Error -- probably bad polygon + return [] # Error -- probably bad polygon # Three consecutive vertices - u = v - if nv <= u: - u = 0 # previous + u = v + if nv <= u: u = 0 # previous v = u + 1 - if nv <= v: - v = 0 # new v + if nv <= v: v = 0 # new v w = v + 1 - if nv <= w: - w = 0 # next + if nv <= w: w = 0 # next if(polySnip(vertices, u, v, w, nv)): @@ -146,8 +147,7 @@ def decomposePoly(vertices): count = 2 * nv return result - def tuple_to_int(tuple_input): - '''Cast tuple values to ints to avoid gtk+ and pygame's dislike of - floats.''' + """Cast tuple values to ints to avoid gtk+ and pygame's dislike of floats. + """ return [int(i) for i in tuple_input] diff --git a/icons/anvil.svg b/icons/anvil.svg new file mode 100644 index 0000000..322c89e --- /dev/null +++ b/icons/anvil.svg @@ -0,0 +1,22 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/icons/basketball.svg b/icons/basketball.svg new file mode 100644 index 0000000..4b9ecfe --- /dev/null +++ b/icons/basketball.svg @@ -0,0 +1,86 @@ + + + +image/svg+xml + + + + + + + + + \ No newline at end of file diff --git a/icons/bowling-ball.svg b/icons/bowling-ball.svg new file mode 100644 index 0000000..f18f465 --- /dev/null +++ b/icons/bowling-ball.svg @@ -0,0 +1,50 @@ + + + +image/svg+xml + + \ No newline at end of file diff --git a/icons/chain.svg b/icons/chain.svg new file mode 100644 index 0000000..4e3939e --- /dev/null +++ b/icons/chain.svg @@ -0,0 +1,880 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/icons/feather.svg b/icons/feather.svg new file mode 100644 index 0000000..e05c446 --- /dev/null +++ b/icons/feather.svg @@ -0,0 +1,55 @@ + +image/svg+xml + + + + + + \ No newline at end of file diff --git a/icons/ice-skate.svg b/icons/ice-skate.svg new file mode 100644 index 0000000..c2215bd --- /dev/null +++ b/icons/ice-skate.svg @@ -0,0 +1,52 @@ + +image/svg+xml + + + + \ No newline at end of file diff --git a/icons/motor-rabbit.svg b/icons/motor-rabbit.svg new file mode 100644 index 0000000..cc146d7 --- /dev/null +++ b/icons/motor-rabbit.svg @@ -0,0 +1,64 @@ + +image/svg+xmlSlice 1 +Slice 1 +Created with Sketch (http://www.bohemiancoding.com/sketch) + + + + + + + \ No newline at end of file diff --git a/icons/motor-turtle.svg b/icons/motor-turtle.svg new file mode 100644 index 0000000..b7fb511 --- /dev/null +++ b/icons/motor-turtle.svg @@ -0,0 +1,64 @@ + +image/svg+xmlSlice 1 +Slice 1 +Created with Sketch (http://www.bohemiancoding.com/sketch) + + + + + + + \ No newline at end of file diff --git a/icons/shoe.svg b/icons/shoe.svg new file mode 100644 index 0000000..7e2b3d7 --- /dev/null +++ b/icons/shoe.svg @@ -0,0 +1,83 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/icons/sneaker.svg b/icons/sneaker.svg new file mode 100644 index 0000000..eb621cc --- /dev/null +++ b/icons/sneaker.svg @@ -0,0 +1,29 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/icons/tennis-ball.svg b/icons/tennis-ball.svg new file mode 100644 index 0000000..fc8d418 --- /dev/null +++ b/icons/tennis-ball.svg @@ -0,0 +1,60 @@ + +image/svg+xml + + + + + + \ No newline at end of file diff --git a/icons/trophy-icon-physics.svg b/icons/trophy-icon-physics.svg new file mode 100644 index 0000000..70543cb --- /dev/null +++ b/icons/trophy-icon-physics.svg @@ -0,0 +1,66 @@ + + + +image/svg+xml + + + + + + \ No newline at end of file diff --git a/icons/wood.svg b/icons/wood.svg new file mode 100644 index 0000000..1f0bc6d --- /dev/null +++ b/icons/wood.svg @@ -0,0 +1,398 @@ + +image/svg+xml + + + + http://thenounproject.com + + The Noun Project + + Icon Template + + Reminders + + + + + + + + + + + + + + Strokes + + Try to keep strokes at 4px + + Minimum stroke weight is 2px + + For thicker strokes use even + + numbers: 6px, 8px etc. + + Remember to expand strokes + + before saving as an SVG + + + Size + + Cannot be wider or taller than + + 100px (artboard size) + + Scale your icon to fill as much of + + the artboard as possible + + + Ungroup + + If your design has more than one + + shape, make sure to ungroup + + + Save as + + Save as .SVG and make sure + + “Use Artboards” is checked + + 100px + + .SVG + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/lib/Box2D-2.0.2b1-py2.5-linux-i686.egg b/lib/Box2D-2.0.2b1-py2.5-linux-i686.egg deleted file mode 100644 index 71a857e..0000000 --- a/lib/Box2D-2.0.2b1-py2.5-linux-i686.egg +++ /dev/null Binary files differ diff --git a/lib/Box2D-2.0.2b2-py2.7-linux-i686.egg b/lib/Box2D-2.0.2b2-py2.7-linux-i686.egg new file mode 100644 index 0000000..7477359 --- /dev/null +++ b/lib/Box2D-2.0.2b2-py2.7-linux-i686.egg Binary files differ diff --git a/physics.py b/physics.py index b1894ad..c44f5ea 100644 --- a/physics.py +++ b/physics.py @@ -36,7 +36,7 @@ from pygame.color import * sys.path.append('lib/') # If your architecture is different, comment these lines and install # the modules in your system. -sys.path.append('lib/Box2D-2.0.2b1-py2.5-linux-i686.egg') +sys.path.append('lib/Box2D-2.0.2b2-py2.7-linux-i686.egg') import Box2D as box2d import elements @@ -54,9 +54,6 @@ class PhysicsGame: # Create the name --> instance map for components self.toolList = {} for c in tools.allTools: - if c.name == tools.EraseAllTool.name: - self.toolList[c.name] = c(self, activity) - continue self.toolList[c.name] = c(self) self.currentTool = self.toolList[tools.allTools[0].name] # Set up the world (instance of Elements) @@ -148,9 +145,9 @@ class PhysicsGame: if self.world.run_physics: bodies_present = len(self.world.world.GetBodyList()) clear_all_active = self.activity.clear_all.get_sensitive() - if (bodies_present > 1) and clear_all_active is False: + if (bodies_present > 2) and clear_all_active is False: self.activity.clear_all.set_sensitive(True) - elif (bodies_present > 1) is False and \ + elif (bodies_present > 2) is False and \ clear_all_active is True: self.activity.clear_all.set_sensitive(False) diff --git a/tools.py b/tools.py index 0c925a4..dc349af 100644 --- a/tools.py +++ b/tools.py @@ -20,12 +20,42 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +import os +from shutil import copy import pygame +import json from pygame.locals import * from helpers import * from gettext import gettext as _ -from sugar.graphics.alert import ConfirmationAlert +from sugar.activity import activity import gtk +import math + +PALETTE_MODE_SLIDER_ICON = 0 +PALETTE_MODE_ICONS = 1 +PALETTE_MODE_SLIDER_LABEL = 2 +PALETTE_ICON_SETTINGS = [ + { + "name": "density", + "icon_count": 3, + "icons": ["feather", "wood", "anvil"], + "icon_values": [0.5, 1.0, 10.0], + "active": "wood" + }, + { + "name": "restitution", + "icon_count": 3, + "icons": ["basketball", "tennis-ball", "bowling-ball"], + "icon_values": [1, 0.16, 0.01], + "active": "tennis-ball" + }, + { + "name": "friction", + "icon_count": 3, + "icons": ["ice-skate", "shoe", "sneaker"], + "icon_values": [0.5, 1, 2], + "active": "grass" + }] # Tools that can be superlcassed @@ -34,6 +64,10 @@ class Tool(object): icon = 'icon' toolTip = 'Tool Tip' toolAccelerator = None + palette_enabled = False + palette_mode = None + palette_settings = None + palette_data = None def __init__(self, gameInstance): self.game = gameInstance @@ -49,16 +83,15 @@ class Tool(object): toggle = self.game.world.run_physics self.game.world.run_physics = not toggle elif event.action == 'clear_all': - if len(self.game.world.world.GetBodyList()) > 1: - # Get bodies and destroy them too - for body in self.game.world.world.GetBodyList(): - self.game.world.world.DestroyBody(body) - - # Add ground, because we destroyed it before - self.game.world.add.ground() - # Also clear the points recorded in pens. - self.game.full_pos_list = \ - [[] for _ in self.game.full_pos_list] + # Get bodies and destroy them too + for body in self.game.world.world.GetBodyList(): + self.game.world.world.DestroyBody(body) + + # Add ground, because we destroyed it before + self.game.world.add.ground() + # Also clear the points recorded in pens. + self.game.full_pos_list = \ + [[] for _ in self.game.full_pos_list] elif event.action == 'focus_in': self.game.in_focus = True elif event.action == 'focus_out': @@ -98,6 +131,28 @@ class Tool(object): # Default cancel doesn't do anything pass + def add_badge(self, message, + icon="trophy-icon-physics", from_="Physics"): + badge = { + 'icon': icon, + 'from': from_, + 'message': message + } + icon_path = os.path.join(activity.get_bundle_path(), + "icons", + (icon+".svg")) + sugar_icons = os.path.join( + os.path.expanduser('~'), + ".icons") + copy(icon_path, sugar_icons) + + if 'comments' in self.game.activity.metadata: + comments = json.loads(self.game.activity.metadata['comments']) + comments.append(badge) + self.game.activity.metadata['comments'] = json.dumps(comments) + else: + self.game.activity.metadata['comments'] = json.dumps([badge]) + # The circle creation tool class CircleTool(Tool): @@ -106,6 +161,15 @@ class CircleTool(Tool): toolTip = _('Circle') toolAccelerator = _('c') + palette_enabled = True + palette_mode = PALETTE_MODE_ICONS + palette_settings = PALETTE_ICON_SETTINGS + palette_data = { + "density": 1.0, + "restitution": 0.16, + "friction": 1 + } + def __init__(self, gameInstance): Tool.__init__(self, gameInstance) self.pt1 = None @@ -118,9 +182,11 @@ class CircleTool(Tool): self.pt1 = tuple_to_int(event.pos) elif event.type == MOUSEBUTTONUP: if event.button == 1: - self.game.world.add.ball(self.pt1, self.radius, - dynamic=True, density=1.0, - restitution=0.16, friction=0.5) + self.game.world.add.ball( + self.pt1, self.radius, + dynamic=True, density=self.palette_data['density'], + restitution=self.palette_data['restitution'], + friction=self.palette_data['friction']) self.pt1 = None def draw(self): @@ -147,6 +213,15 @@ class BoxTool(Tool): toolTip = _('Box') toolAccelerator = _('b') + palette_enabled = True + palette_mode = PALETTE_MODE_ICONS + palette_settings = PALETTE_ICON_SETTINGS + palette_data = { + "density": 1.0, + "restitution": 0.16, + "friction": 1 + } + def __init__(self, gameInstance): Tool.__init__(self, gameInstance) self.pt1 = None @@ -166,13 +241,14 @@ class BoxTool(Tool): self.rect = pygame.Rect(self.pt1, (-self.width, -self.height)) self.rect.normalize() - self.game.world.add.rect(self.rect.center, - max(self.rect.width, 10) / 2, - max(self.rect.height, 10) / 2, - dynamic=True, - density=1.0, - restitution=0.16, - friction=0.5) + self.game.world.add.rect( + self.rect.center, + max(self.rect.width, 10) / 2, + max(self.rect.height, 10) / 2, + dynamic=True, + density=self.palette_data['density'], + restitution=self.palette_data['restitution'], + friction=self.palette_data['friction']) self.pt1 = None def draw(self): @@ -200,6 +276,15 @@ class TriangleTool(Tool): toolTip = _('Triangle') toolAccelerator = _('t') + palette_enabled = True + palette_mode = PALETTE_MODE_ICONS + palette_settings = PALETTE_ICON_SETTINGS + palette_data = { + "density": 1.0, + "restitution": 0.16, + "friction": 1 + } + def __init__(self, gameInstance): Tool.__init__(self, gameInstance) self.pt1 = None @@ -236,11 +321,12 @@ class TriangleTool(Tool): self.vertices = constructTriangleFromLine(self.pt1, mouse_x_y) - self.game.world.add.convexPoly(self.vertices, - dynamic=True, - density=1.0, - restitution=0.16, - friction=0.5) + self.game.world.add.convexPoly( + self.vertices, + dynamic=True, + density=self.palette_data['density'], + restitution=self.palette_data['restitution'], + friction=self.palette_data['friction']) self.pt1 = None self.vertices = None @@ -270,6 +356,15 @@ class PolygonTool(Tool): toolTip = _('Polygon') toolAccelerator = _('p') + palette_enabled = True + palette_mode = PALETTE_MODE_ICONS + palette_settings = PALETTE_ICON_SETTINGS + palette_data = { + "density": 1.0, + "restitution": 0.16, + "friction": 1 + } + def __init__(self, gameInstance): Tool.__init__(self, gameInstance) self.vertices = None @@ -293,11 +388,12 @@ class PolygonTool(Tool): self.vertices = [[i[0] - delta_x, i[1] - delta_y] for i in self.previous_vertices] self.safe = True - self.game.world.add.complexPoly(self.vertices, - dynamic=True, - density=1.0, - restitution=0.16, - friction=0.5) + self.game.world.add.complexPoly( + self.vertices, + dynamic=True, + density=self.palette_data['density'], + restitution=self.palette_data['restitution'], + friction=self.palette_data['friction']) self.vertices = None elif (event.type == MOUSEBUTTONUP or event.type == MOUSEBUTTONDOWN): @@ -310,11 +406,12 @@ class PolygonTool(Tool): if distance(tuple_to_int(event.pos), self.vertices[0]) < 15 \ and self.safe: self.vertices.append(self.vertices[0]) # Connect polygon - self.game.world.add.complexPoly(self.vertices, - dynamic=True, - density=1.0, - restitution=0.16, - friction=0.5) + self.game.world.add.complexPoly( + self.vertices, + dynamic=True, + density=self.palette_data['density'], + restitution=self.palette_data['restitution'], + friction=self.palette_data['friction']) self.previous_vertices = self.vertices[:] self.vertices = None elif distance(tuple_to_int(event.pos), self.vertices[0]) < 15: @@ -349,6 +446,15 @@ class MagicPenTool(Tool): toolTip = _('Draw') toolAccelerator = _('d') + palette_enabled = True + palette_mode = PALETTE_MODE_ICONS + palette_settings = PALETTE_ICON_SETTINGS + palette_data = { + "density": 1.0, + "restitution": 0.16, + "friction": 1 + } + def __init__(self, gameInstance): Tool.__init__(self, gameInstance) self.vertices = None @@ -369,10 +475,11 @@ class MagicPenTool(Tool): for i in self.previous_vertices] self.safe = True if self.vertices and self.safe: - self.game.world.add.complexPoly(self.vertices, dynamic=True, - density=1.0, - restitution=0.16, - friction=0.5) + self.game.world.add.complexPoly( + self.vertices, dynamic=True, + density=self.palette_data['density'], + restitution=self.palette_data['restitution'], + friction=self.palette_data['friction']) self.previous_vertices = self.vertices[:] self.vertices = None elif event.type == MOUSEMOTION and self.vertices: @@ -528,6 +635,17 @@ class MotorTool(Tool): icon = 'motor' toolTip = _('Motor') toolAccelerator = _('m') + # Palette settings + palette_enabled = True + palette_mode = PALETTE_MODE_SLIDER_ICON + palette_settings = { + "icon1": "motor-turtle", + "icon2": "motor-rabbit", + "min": 0, + "max": 500 + } + # Default Value + palette_data = 10 def __init__(self, gameInstance): Tool.__init__(self, gameInstance) @@ -542,7 +660,9 @@ class MotorTool(Tool): self.jb1 = self.game.world.get_bodies_at_pos( tuple_to_int(event.pos)) if self.jb1: - self.game.world.add.motor(self.jb1[0], self.jb1pos) + self.game.world.add.motor( + self.jb1[0], self.jb1pos, + speed=self.palette_data) self.jb1 = self.jb1pos = None def cancel(self): @@ -631,54 +751,6 @@ class DestroyTool(Tool): self.vertices = None -class EraseAllTool(Tool): - name = 'Erase All' - icon = 'destroy-all' - toolTip = _('Erase all') - - def __init__(self, gameInstance, activity=None): - super(EraseAllTool, self).__init__(gameInstance) - self.game = gameInstance - self.response_alert = None - self.activity = activity - - def handleToolEvent(self, event, action=False): - if event.type == MOUSEBUTTONDOWN: - if not action: - # Add alert for confirm the delete all action. - alert = ConfirmationAlert() - alert.props.title = _('Delete all shapes?') - alert.props.msg = _('This cannot be undone!') - alert.connect('response', self.alert_info, event) - self.activity.add_alert(alert) - return - else: - if self.response_alert: - self.response_alert = False - # Obtain all figures - bodys = [] - for body in self.game.world.world.GetBodyList(): - bodys.append(body) - - # Erase all ;) - for body in bodys: - self.game.world.world.DestroyBody(body) - - # The ground has deleted, restore.. - self.game.world.add.ground() - else: - pass - - def alert_info(self, alert, response_id, event): - self.activity.remove_alert(alert) - if response_id is gtk.RESPONSE_OK: - self.response_alert = True - elif response_id is gtk.RESPONSE_CANCEL: - self.response_alert = False - - self.handleToolEvent(event, True) - - # Track tool class TrackTool(Tool): name = 'Track' @@ -689,6 +761,7 @@ class TrackTool(Tool): def __init__(self, game): Tool.__init__(self, game) self.radius = 1 + self.added_badge = False def handleToolEvent(self, event): Tool.handleToolEvent(self, event) @@ -722,6 +795,103 @@ class TrackTool(Tool): self.game.trackinfo[dictkey][4] = trackdex # Tracking index. self.game.tracked_bodies += 1 # counter of tracked bodies + if not self.added_badge: + self.add_badge(message="Congratulations! You just added a" + " Pen to your machine!", + from_="Isacc Newton") + self.added_badge = True + + +class ChainTool(Tool): + name = 'Chain' + icon = 'chain' + toolTip = _("Chain") + toolAccelerator = "i" + + # Palette settings + palette_enabled = True + palette_mode = PALETTE_MODE_SLIDER_LABEL + palette_settings = {} + palette_settings = [ + { + "label": "Chain Distance", + "min": 25, + "max": 50, + "data": "distance" + }, + { + "label": "Chain Circle Size", + "min": 1, + "max": 10, + "data": "radius" + }] + palette_data = { + "distance": 25, + "radius": 1 + } + + def __init__(self, gameInstance): + Tool.__init__(self, gameInstance) + self.jb1 = self.jb2 = self.jb1pos = self.jb2pos = None + + def handleToolEvent(self, event): + Tool.handleToolEvent(self, event) + if event.type == MOUSEBUTTONDOWN: + if event.button >= 1: + # Grab the first body + self.jb1pos = tuple_to_int(event.pos) + self.jb1 = self.game.world.get_bodies_at_pos( + tuple_to_int(event.pos)) + self.jb2 = self.jb2pos = None + elif event.type == MOUSEBUTTONUP: + if event.button == 1: + # Grab the second body + self.jb2pos = tuple_to_int(event.pos) + self.jb2 = self.game.world.get_bodies_at_pos( + tuple_to_int(event.pos)) + # If we have two distinct bodies, make the chain. + if self.jb1 and self.jb2 and str(self.jb1) != str(self.jb2): + self.make_chain( + self.jb1[0], self.jb2[0], self.jb1pos, self.jb2pos) + self.jb1 = self.jb2 = self.jb1pos = self.jb2pos = None + + def draw(self): + Tool.draw(self) + if self.jb1: + pygame.draw.line(self.game.screen, (100, 180, 255), self.jb1pos, + tuple_to_int(pygame.mouse.get_pos()), 3) + + def cancel(self): + self.jb1 = self.jb2 = self.jb1pos = self.jb2pos = None + + def make_chain(self, bodyA, bodyB, pos1, pos2): + d = int(distance(pos1, pos2)) + x1, y1 = pos1 + x2, y2 = pos2 + bearing = math.atan2((y2-y1), (x2-x1)) + first_iter = True + prevcircle = None + prevpos = None + for p in range(5, d, int(self.palette_data['distance'])): + x = x1 + p * math.cos(bearing) + y = y1 + p * math.sin(bearing) + circle = self.game.world.add.ball( + (x, y), self.palette_data['radius'], dynamic=True) + circle.userData['color'] = (0, 0, 0) + if first_iter: + self.game.world.add.joint(circle, bodyA, (x, y), pos1, False) + first_iter = False + oldcircle = circle + prevpos = (x, y) + elif (p+25) > d: + self.game.world.add.joint(circle, oldcircle, (x, y), prevpos) + self.game.world.add.joint(circle, bodyB, (x, y), pos2, False) + else: + self.game.world.add.joint(circle, oldcircle, (x, y), prevpos, + False) + oldcircle = circle + prevpos = (x, y) + def getAllTools(): return [MagicPenTool, @@ -733,9 +903,8 @@ def getAllTools(): MotorTool, PinTool, JointTool, + ChainTool, TrackTool, - DestroyTool, - # EraseAllTool, # moved to main toolbar - ] + DestroyTool] allTools = getAllTools() -- cgit v0.9.1