Example #1
0
class Screen(object):
    """
    Abstract class that coordinates visualization. Must be overridden in each
    backend.
    """

    __instance = None
    is_canvas = False
    background = colorproperty('background')
    draw_circle = delegate_to('camera')
    draw_aabb = delegate_to('camera')
    draw_poly = delegate_to('camera')
    draw_segment = delegate_to('camera')
    draw_path = delegate_to('camera')
    draw_ray = delegate_to('camera')
    draw_line = delegate_to('camera')
    draw_image = delegate_to('camera')

    @property
    def shape(self):
        return self.width, self.height

    def __new__(cls, *args, **kwds):
        if cls.__instance is not None:
            raise TypeError('cannot create two instances of singleton object')
        return object.__new__(cls)

    def __init__(self, shape=(800, 600), pos=(0, 0), zoom=1, background=None):
        self.width, self.height = shape
        self.pos = Vec2(*pos)
        self.zoom = zoom
        self.background = background
        self.camera = Camera(self)
        self.visible = False

    def init(self):
        """
        Initialize game screen.
        """

    def show(self):
        """
        Show initialized game window.
        """

        self.visible = True

    def draw(self, obj):
        obj.draw(self.camera)
Example #2
0
class Body(physics.Body):
    def __init__(self, *args, **kwargs):
        # Visualization parameters
        image = {'image': kwargs.pop('image', None)}
        for k in list(kwargs):
            if k.startswith('image_'):
                image[k] = kwargs.pop(k)
        self.color = kwargs.pop('color', black)
        self.linecolor = kwargs.pop('linecolor', None)
        self.linewidth = kwargs.pop('linewidth', 1)
        self.visible = kwargs.pop('visible', True)

        self.world = kwargs.pop('world', None)

        # Init physics object and visualization
        super().__init__(*args, **kwargs)
        self.__init_image(**image)

        # Set world
        if self.world is not None:
            self.world.add(self)

    def __init_image(self,
                     image,
                     image_offset=(0, 0),
                     image_reference=None,
                     **kwargs):
        self._image = None
        self._drawshape = None

        if image is not None:
            # Get all image parameters
            img_kwargs = {}
            for k, v in kwargs.items():
                if k.startswith('image_'):
                    img_kwargs[k[6:]] = v

            if isinstance(image, str):
                image = Image(image, self.pos, **img_kwargs)
            else:
                raise NotImplementedError
            self._image = image

            offset = asvector(image_offset)
            if image_reference in [
                    'pos_ne', 'pos_nw', 'pos_se', 'pos_sw', 'pos_left',
                    'pos_right', 'pos_up', 'pos_down'
            ]:
                pos_ref = getattr(self, image_reference)
                pos_img_ref = getattr(image, image_reference)
                offset += pos_ref - pos_img_ref
            elif image_reference not in ['middle', None]:
                raise ValueError('invalid image reference: %r' %
                                 image_reference)
            image.offset = offset
        else:
            self._drawshape = self._init_drawshape(color=self.color or black,
                                                   linecolor=self.linecolor,
                                                   linewidth=self.linewidth)

    @lazy
    def _input(self):
        return conf.get_input()

    color = colorproperty('color')
    linecolor = colorproperty('linecolor')

    @property
    def image(self):
        img = self._image
        if img is None:
            return None
        img.pos = self.pos + img.offset
        return img

    @image.setter
    def image(self, value):
        if value is None:
            self._image = None
            return
        if isinstance(value, str):
            value = Image(value, self.pos)
        self._image = value

    @property
    def drawable(self):
        return self.image or self.drawshape

    @property
    def drawshape(self):
        if self._drawshape is None:
            return None
        self._drawshape.pos = self.pos
        return self._drawshape

    def show(self):
        """
        Makes object visible.
        """

        self.visible = True

    def hide(self):
        """
        Makes object invisible.

        It keeps interacting with the wold, but it is not shown on the screen.
        """

        self.visible = False

    def draw(self, screen):
        """
        Draw object in a canvas-like screen.
        """

        img = self.image
        if img is not None:
            img.pos = self.pos + img.offset
            return img.draw(screen)
        else:
            return self.draw_shape(screen)

    def draw_shape(self, screen):
        """
        Draw a shape identical to the object's bounding box.
        """

        raise NotImplementedError('must be implemented on subclass.')

    def destroy(self):
        super().destroy()
        if self.world is not None:
            self.world.remove(self)
            self.world = None

    def _init_drawshape(self, color=None, linecolor=None, linewidth=1):
        bbox = self.bb
        cls = getattr(draw, type(bbox).__name__)
        return cls(*bbox,
                   color=color,
                   linecolor=linecolor,
                   linewidth=linewidth)
Example #3
0
class World(Listener, collections.MutableSequence):
    """
    Combines physical simulation with display.
    """

    # Simulation properties
    background = colorproperty('background', 'white')
    gravity = delegate_to('_simulation')
    damping = delegate_to('_simulation')
    adamping = delegate_to('_simulation')
    time = delegate_to('_simulation', readonly=True)

    # Special properties
    @lazy
    def add(self):
        return ObjectFactory(self)

    @lazy
    def track(self):
        return Tracker(self)

    @lazy
    def _mainloop(self):
        return conf.get_mainloop()

    @lazy
    def _input(self):
        return conf.get_input()

    _last_instances = []

    def __init__(self,
                 background=None,
                 gravity=None,
                 damping=0,
                 adamping=0,
                 restitution=1,
                 friction=0,
                 bounds=None,
                 max_speed=None,
                 simulation=None):

        self.background = background
        self._render_tree = RenderTree()
        self._objects = []

        if simulation:
            self._simulation = simulation
        else:
            self._simulation = Simulation(gravity=gravity,
                                          damping=damping,
                                          adamping=adamping,
                                          restitution=restitution,
                                          friction=friction,
                                          max_speed=max_speed,
                                          bounds=bounds)

        self.is_paused = False

        # Populate world with user-customizable init
        self.init()

        # Saves instance
        self._last_instances.append(weakref.ref(self))

        # Connect signals
        self.autoconnect()

    def __len__(self):
        return len(self._objects)

    def __iter__(self):
        return iter(self._objects)

    def __getitem__(self, i):
        return self._objects[i]

    def __delitem__(self, i):
        self.remove(self._objects[i])

    def __setitem__(self, key, value):
        raise IndexError('cannot replace objects in world')

    def _add(self, obj, layer=0):
        """
        Adds object to the world.
        """

        if isinstance(obj, (tuple, list)):
            for obj in obj:
                self.add(obj, layer=layer)
        else:
            self._render_tree.add(obj, layer)
            if isinstance(obj, Body):
                self._simulation.add(obj)
                obj.world = self
            self._objects.append(obj)

    def insert(self, idx, obj):
        raise IndexError('cannot insert objects at specific positions. '
                         'Please user world.add()')

    def append(self, obj, layer=0):
        """
        Alias to World.add()
        """

        self.add(obj, layer)

    def remove(self, obj):
        """
        Remove object from the world.
        """

        if getattr(obj, 'world', None) is self:
            if obj in self._render_tree:
                self._render_tree.remove(obj)
            self._simulation.remove(obj)
            obj.world = None
        else:
            self._render_tree.remove(obj)
        self._objects.remove(obj)

    def init(self):
        """
        Executed after initialization.

        Should be overridden by sub-classes in order to populate the world with
        default objects during its creation. The default implementation does
        nothing.
        """

    def pause(self):
        """
        Pause physics simulation.
        """

        self.is_paused = True

    def resume(self):
        """
        Resume paused physics simulation.
        """

        self.is_paused = False

    def toggle_pause(self):
        """
        Toggles paused state.
        """

        self.is_paused = not self.is_paused

    def update(self, dt):
        """
        Main update routine.
        """

        if not self.is_paused:
            self._simulation.update(dt)

    def run(self, timeout=None, **kwds):
        """
        Runs simulation until the given timeout expires.

        Args:
            timeout (float):
                Maximum duration of simulation (in seconds). Leave None to
                run the simulation indefinitely.
        """

        conf.init()
        conf.show_screen()
        self._mainloop.run(self, timeout=timeout, **kwds)

    def start(self, **kwds):
        """
        Non-blocking version of World.run().

        Starts simulation in a separate thread. This must be supported by the
        backend (pygame, for instance, does).
        """

        if hasattr(self, '_thread'):
            try:
                self._thread.join(0)
            except TimeoutError:
                pass

        self._thread = threading.Thread(target=self.run, kwargs=kwds)
        self._thread.start()

    def stop(self):
        """
        Stops simulation.
        """

        # Forces thread to stop
        try:
            self._thread.join(0)
            del self._thread
        except (AttributeError, TimeoutError):
            pass
        self._mainloop.stop()

    def render_tree(self):
        """
        Return the render tree.
        """

        return self._render_tree
Example #4
0
class Screen(object):
    """
    Abstract class that coordinates visualization. Must be overridden in each
    backend.
    """

    __instance = None
    is_canvas = False
    background = colorproperty('background')
    draw_circle = delegate_to('camera')
    draw_aabb = delegate_to('camera')
    draw_poly = delegate_to('camera')
    draw_segment = delegate_to('camera')
    draw_path = delegate_to('camera')
    draw_ray = delegate_to('camera')
    draw_line = delegate_to('camera')
    draw_image = delegate_to('camera')

    @property
    def shape(self):
        return self.width, self.height

    def __new__(cls, *args, **kwds):
        if cls.__instance is not None:
            raise TypeError('cannot create two instances of singleton object')
        return object.__new__(cls)

    def __init__(self, shape=(800, 600), pos=(0, 0), zoom=1, background=None):
        self.width, self.height = shape
        self.pos = Vec2(*pos)
        self.zoom = zoom
        self.background = background
        self.camera = Camera(self)
        self.visible = False
        self.background_image = None
        self.background_color = white

    def init(self):
        """
        Initialize game screen.
        """

    def show(self):
        """
        Show initialized game window.
        """

        self.visible = True

    def set_background_image(self, image):
        """
        Configures a background image from its asset name.
        """

        if image is None:
            self.background_image = None
        else:
            from FGAme import asset

            if isinstance(image, str):
                image = asset.Asset(image, 'images', ['.png', '.jpeg', '.jpg'])
            self.background_image = self.prepare_image(image)

    def prepare_image(self, asset):
        """
        Prepares a useful image object from asset.
        """

        raise NotImplementedError

    def draw(self, obj):
        obj.draw(self.camera)