#!/usr/bin/python """ This file is part of the 'Bridge' Project Code: http://git.sugarlabs.org/bridge git clone git://git.sugarlabs.org/bridge/mainline.git This file was originally part of the 'Physics' Project Physics is a 2D Physics Playground for Kids (supporting Box2D2) Physics Copyright (C) 2008, Alex Levenson, Brian Jordan Elements Copyright (C) 2008, The Elements Team, Wiki: http://wiki.laptop.org/wiki/Physics IRC: #olpc-physics on irc.freenode.org Code: http://git.sugarlabs.org/physics git clone git://git.sugarlabs.org/physics/mainline.git License: GPLv3 http://gplv3.fsf.org/ """ import pygame from elements import box2d from pygame.locals import * from helpers import * from inspect import getmro import math from gettext import gettext as _ def distance(pt1, pt2): """Returns the L_2 norm distance between two points. This is the common distance formula that most people know. """ return math.sqrt((pt1[0] - pt2[0]) ** 2 + (pt1[1] -pt2[1]) ** 2) def distance2(pt1, pt2, amount): """Returns True or False if the distance between points pt1 and pt2 is less than the squared amount. """ if amount == float('inf'): return True return ((pt1[0] - pt2[0]) ** 2 + (pt1[1] - pt2[1]) ** 2) <= (amount**2) # tools that can be used superlcass class Tool(object): """Tool class implements the event handling of the activity toolbar buttons. It interacts with the world to add, remove, or move objects that appear on the screen. """ name = "Tool" icon = "icon" toolTip = "Tool Tip" def __init__(self, gameinstance): self.game = gameinstance self.name = "Tool" def handleevents(self, event): """Default method of Handling events for any Tool class instances. """ handled = True # default event handling if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE): # bye bye! Hope you had fun! self.game.running = False elif event.type == KEYDOWN: # most of these key shortcuts are not in the documentation of this # activity, so they need to be added. if event.key == K_SPACE: #space pauses self.game.bridge.create_train() self.game.world.run_physics = not self.game.world.run_physics elif event.key == K_r: self.game.bridge.restart() elif event.key == K_t: self.game.bridge.create_train(force=True) elif event.key == K_b: self.game.setTool("girder") elif event.key == K_c: self.game.setTool("circle") elif event.key == K_j: self.game.setTool("bridgejoint") elif event.key == K_g: self.game.setTool("grab") elif event.key == K_d: self.game.setTool("destroy") elif event.type == USEREVENT: if hasattr(event,"action"): if self.game.toolList.has_key(event.action): self.game.setTool(event.action) elif event.type == MOUSEBUTTONDOWN and event.button == 1: self.game.canvas.grab_focus() handled = False else: handled = False return handled def draw(self): """default drawing method doesn't draw anything """ pass def cancel(self): """default cancel method doesn't do anything """ pass def to_b2vec(self, point): """returns something about adding the point to a to_b2vec """ return self.game.world.add.to_b2vec(point) # The circle creation tool class CircleTool(Tool): """The circle tool was used for testing. It probably should be removed """ icon = "circle" toolTip = _("Circle") def __init__(self, gameinstance): Tool.__init__(gameinstance) self.name = "Circle" self.pt1 = None self.radius = None def handleevents(self, event): """Handles events """ # look for default events, and if none are handled then # try the custom events if not super(CircleTool, self).handleEvents(event): if event.type == MOUSEBUTTONDOWN: if event.button == 1: self.pt1 = pygame.mouse.get_pos() elif event.type == MOUSEBUTTONUP: if event.button == 1: if self.radius > 1: # elements doesn't like tiny shapes :( self.game.world.add.ball(self.pt1, self.radius, dynamic=True, density=1.0, restitution=0.16, friction=0.5) self.pt1 = None self.radius = None def draw(self): # draw a circle from pt1 to mouse if self.pt1 != None: self.radius = distance(self.pt1, pygame.mouse.get_pos()) if self.radius > 3: thick = 3 else: thick = 0 pygame.draw.circle(self.game.screen, (100, 180, 255), self.pt1,self.radius, thick) pygame.draw.line(self.game.screen, (100, 180, 255), self.pt1,pygame.mouse.get_pos(), 1) def cancel(self): self.pt1 = None self.radius = None # The Girder creation tool class GirderTool(Tool): """The Girder tool is used to place a bridge beam on the screen. This class handles the events related to the button. """ #name = "girder" icon = "box" toolTip = _("Girder") def __init__(self, gameinstance): Tool.__init__(gameinstance) self.name = "Girder" # TODO was "box", why? self.pt1 = None self.pt2 = None self.theta = None self.thickness = 30 self.max = 300 self.min = 100 self.red = 20 self.green = 20 self.blue = 20 self.colordiff = 20 def handleevents(self, event): """Handles events """ # look for default events, and if none are handled then # try the custom events if not super(GirderTool, self).handleEvents(event): if event.type == MOUSEBUTTONDOWN: if event.button == 1: self.pt1 = pygame.mouse.get_pos() elif event.type == MOUSEBUTTONUP: if event.button == 1: if self.pt2 != None: self.game.world.set_color( (self.red, self.green, self.blue)) self.red += self.colordiff self.green += self.colordiff self.blue += self.colordiff if self.red > 200: self.colordiff *= -1 elif self.red < 20: self.colordiff *= -1 print self.theta, math.degrees(self.theta) self.game.world.add.rect(((self.pt1[0]+self.pt2[0])/2, (self.pt1[1]+self.pt2[1])/2), # tuple concluded distance(self.pt1, self.pt2)/2, self.thickness/2, angle=math.degrees(self.theta), dynamic=True, density=1.0, restitution=0.16, friction=0.5) self.game.bridge.box_added() self.game.world.reset_color() self.pt1 = None def draw(self): # draw a box from pt1 to mouse if self.pt1 != None: self.pt2 = pygame.mouse.get_pos() self.theta = getAngle(self.pt1, self.pt2) if distance2(self.pt1, self.pt2, self.min): # too small! force length self.pt2 = (self.pt1[0]+self.min * math.cos(self.theta), self.pt1[1]-self.min*math.sin(self.theta)) elif not distance2(self.pt1, self.pt2, self.max): # too big self.pt2 = (self.pt1[0]+(self.max * math.cos(self.theta)), self.pt1[1]-(self.max * math.sin(self.theta))) pygame.draw.line(self.game.screen, (255, 255, 255), self.pt1, self.pt2, 30) def cancel(self): self.pt1 = None #self.rect = None #TODO why is this here? # The grab tool class GrabTool(Tool): """The grab tool is used to move objects on the screen. This class handles the mouse events related to the use of the tool. """ name = "grab" icon = "grab" toolTip = _("Grab") def __init__(self, gameinstance): Tool.__init__(gameinstance) self.name = "Grab" def handleevents(self, event): """Handles events """ # look for default events, and if none are handled then # try the custom events if not super(GrabTool, self).handleEvents(event): if event.type == MOUSEBUTTONDOWN: if event.button == 1: # grab the first object at the mouse pointer bodylist = self.game.world.get_bodies_at_pos( event.pos, include_static=False) if bodylist and len(bodylist) > 0: self.game.world.add.mouseJoint(bodylist[0], event.pos) elif event.type == MOUSEBUTTONUP: # let it go if event.button == 1: self.game.world.add.remove_mouseJoint() # use box2D mouse motion elif event.type == MOUSEMOTION and event.buttons[0]: self.game.world.mouse_move(event.pos) def cancel(self): self.game.world.add.remove_mouseJoint() # The destroy tool class DestroyTool(Tool): """The DestroyTool is the button that allows for items in the game to be deleted. This might be used if a part is placed incorrectly or to minimize the number of objects used to construct the bridge. """ name = "destroy" icon = "destroy" toolTip = _("Destroy") def __init__(self, gameinstance): Tool.__init__(gameinstance) self.name = "Destroy" self.vertices = None def handleevents(self, event): """Event handler for the Destory tool. This is coded in a different style than the joint tool. """ # look for default events, and if none are handled then try # the custom events if not super(DestroyTool, self).handleEvents(event): if pygame.mouse.get_pressed()[0]: if not self.vertices: self.vertices = [] self.vertices.append(pygame.mouse.get_pos()) if len(self.vertices) > 10: self.vertices.pop(0) tokill = self.game.world.get_bodies_at_pos( pygame.mouse.get_pos()) if tokill: jointnode = tokill[0].GetJointList() if jointnode: joint = jointnode.joint self.game.bridge.joint_deleted(joint) while joint.GetNext(): joint = joint.GetNext() self.game.bridge.joint_deleted(joint) self.game.world.world.DestroyBody(tokill[0]) self.game.bridge.box_deleted() elif event.type == MOUSEBUTTONUP and event.button == 1: self.cancel() def draw(self): # draw the trail if self.vertices: if len(self.vertices) > 1: pygame.draw.lines(self.game.screen, (255, 0, 0), False, self.vertices, 3) def cancel(self): self.vertices = None # The joint tool class BridgeJointTool(Tool): """This class is the joint tool used by the activity. If an event occurs, which is expected to be a mouse button release, then it handles adding the joint to the two world objects """ name = "bridgejoint" icon = "joint" toolTip = _("Bridge Joint") def __init__(self, gameinstance): Tool.__init__(gameinstance) self.name = "Bridge Joint" self.jb1 = self.jb2 = self.jb1pos = self.jb2pos = None def handleevents(self, event): """Handles all events, but really if the event is not a mouse button up, then it ignores it. """ # look for default events, and if none are handled then try # the custom events if super(BridgeJointTool, self).handleEvents(event): return if event.type != MOUSEBUTTONUP or event.button != 1: return bodies = self.game.world.get_bodies_at_pos(event.pos, include_static=True) if not bodies or len(bodies) > 2: return jointdef = box2d.b2RevoluteJointDef() if len(bodies) == 1: if not bodies[0].IsStatic(): # this following if statement has something to do with checking # to see if the joint connects to the two sides of the ravine. if (event.pos[1] > 550) and (event.pos[0] < 350 or event.pos[0] > 850): jointdef.Initialize(self.game.world.world.GetGroundBody(), bodies[0], self.to_b2vec(event.pos)) else: return else: return elif len(bodies) == 2: if bodies[0].IsStatic(): jointdef.Initialize(self.game.world.world.GetGroundBody(), bodies[1], self.to_b2vec(event.pos)) elif bodies[1].IsStatic(): jointdef.Initialize(self.game.world.world.GetGroundBody(), bodies[0], self.to_b2vec(event.pos)) else: jointdef.Initialize(bodies[0], bodies[1], self.to_b2vec(event.pos)) joint = self.game.world.world.CreateJoint(jointdef) self.game.bridge.joint_added(joint) def draw(self): return def cancel(self): self.jb1 = self.jb2 = self.jb1pos = self.jb2pos = None def getalltools(): """This function builds the tool list that is used by the activity to construct the toolbar. """ this_mod = __import__(__name__) all = [val for val in this_mod.__dict__.values() if isinstance(val, type)] alltools = [] for thistool in all: if getmro(thistool).__contains__(Tool) and thistool != Tool: alltools.append(thistool) return alltools ALLTOOLS = getalltools()