#!/usr/bin/python """ 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 . -- Elements is Copyright (C) 2008, The Elements Team, Wiki: http://wiki.sugarlabs.org/go/Activities/Physics Code: git://git.sugarlabs.org/physics/mainline.git """ import os import sys import math import gtk import logging import pygame from pygame.locals import * from pygame.color import * import sugargame sys.path.append("lib/") import pkg_resources # 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") import Box2D as box2d import elements import tools from helpers import * class PhysicsGame: def __init__(self, activity): self.activity = activity # Get everything set up self.clock = pygame.time.Clock() self.canvas = activity.canvas self.in_focus = True # 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) self.box2d = box2d self.opening_queue = None self.loop = True self.pygame_started = False self.full_pos_list = [] self.tracked_bodies = 0 self.trackinfo = {} def switch_off_fake_pygame_cursor_cb(self, panel, event): self.show_fake_cursor = False def switch_on_fake_pygame_cursor_cb(self, panel, event): self.show_fake_cursor = True def write_file(self, path): #Saving to journal self.world.add.remove_mouseJoint() additional_data = { "trackinfo" : self.trackinfo, "full_pos_list" : self.full_pos_list } self.world.json_save(path, additional_data, serialize=True) def read_file(self, path): #Loading from journal self.opening_queue = path def run(self, restart=False): self.screen = pygame.display.get_surface() if not restart: pygame.init() pygame.display.init() # pygame.mixer.quit() self.pygame_started = True # Fake a Sugar cursor for the pyGame canvas area self.show_fake_cursor = True pygame.mouse.set_cursor((8, 8), (0, 0), (0, 0, 0, 0, 0, 0, 0, 0), (0, 0, 0, 0, 0, 0, 0, 0)) self.cursor_picture = pygame.image.load('standardcursor.png') self.cursor_picture.convert_alpha() self.canvas.connect("enter_notify_event", self.switch_on_fake_pygame_cursor_cb) self.canvas.connect("leave_notify_event", self.switch_off_fake_pygame_cursor_cb) self.canvas.add_events(gtk.gdk.ENTER_NOTIFY_MASK | gtk.gdk.LEAVE_NOTIFY_MASK) self.world = elements.Elements(self.screen.get_size()) self.world.renderer.set_surface(self.screen) self.world.add.ground() if self.opening_queue: path = self.opening_queue.encode('ascii', 'convert') if os.path.exists(path): self.world.json_load(path, serialized=True) self.full_pos_list = self.world.additional_vars['full_pos_list'] self.trackinfo = self.world.additional_vars['trackinfo'] while self.loop: while gtk.events_pending(): gtk.main_iteration() if not self.loop: pygame.quit() self.pygame_started = False break for event in pygame.event.get(): self.currentTool.handleEvents(event) if event.type == MOUSEBUTTONUP: if event.button == 1: self.show_fake_cursor = True if self.in_focus: # Drive motors 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: self.activity.clear_all.set_sensitive(True) elif (bodies_present > 1) is False and \ clear_all_active is True: self.activity.clear_all.set_sensitive(False) poslist = self.full_pos_list clear_trace_active = self.activity.clear_trace.get_sensitive() if poslist: if not poslist[0]: if clear_trace_active: self.activity.clear_trace.set_sensitive(False) else: if clear_trace_active is False: self.activity.clear_trace.set_sensitive(True) for key, info in self.trackinfo.iteritems(): body = info[1] # [host_body, tracker, color, destroyed?] if info[3] is False: # Not destroyed the pen trackdex = info[4] def to_screen(pos): px = self.world.meter_to_screen( pos[0]) py = self.world.meter_to_screen( pos[1]) py = self.world.renderer.get_surface() \ .get_height() - py return (px, py) x = body.position.x y = body.position.y tupled_pos = to_screen((x, y)) posx = tupled_pos[0] posy = tupled_pos[1] try: self.full_pos_list[trackdex].append(posx) self.full_pos_list[trackdex].append(posy) except IndexError: self.full_pos_list.append([posx, posy]) for body in self.world.world.GetBodyList(): if type(body.userData) == type({}): if body.userData.has_key('rollMotor'): diff = body.userData['rollMotor'] \ ['targetVelocity'] \ - body.GetAngularVelocity() body.ApplyTorque(body.userData['rollMotor'] \ ['strength'] * diff \ * body.getMassData().I) # Update & Draw World self.world.update() self.screen.fill((255, 255, 255)) # 255 for white self.world.draw() # Draw output from tools self.currentTool.draw() # Show Sugar like cursor for UI consistancy if self.show_fake_cursor: self.screen.blit(self.cursor_picture, pygame.mouse.get_pos()) # Flip Display pygame.display.flip() # Stay < 30 FPS to help keep the rest of the platform responsive self.clock.tick(30) # Originally 50 def setTool(self, tool): self.currentTool.cancel() self.currentTool = self.toolList[tool] def main(activity): game = PhysicsGame(activity) return game # Make sure that main get's called if __name__ == '__main__': main()