Esempio n. 1
0
    def make_static_linear(self):
        '''Resgata os parâmetros dinâmicos lineares de um objeto estático ou
        cinemático paralizado pelos métodos `obj.make_static()` ou
        `obj.make_kinematic()`.'''

        self.make_kinematic_linear()
        if self._vel != nullvec2:
            self._old_vel = self._vel
            self._vel = Vec2(0, 0)
Esempio n. 2
0
    def __init__(self,
                 vertices,
                 pos=None,
                 vel=(0, 0),
                 theta=0.0,
                 omega=0.0,
                 mass=None,
                 density=None,
                 inertia=None,
                 **kwds):

        vertices = [Vec2(*pt) for pt in vertices]
        pos_cm = center_of_mass(vertices)
        vertices = [v - pos_cm for v in vertices]
        self._vertices = vertices

        # Cache de vértices
        self._cache_theta = None
        self._cache_rvertices_last = None
        self._cache_rbbox_last = None
        self.cbb_radius = max(v.norm() for v in vertices)
        super(Poly, self).__init__(pos_cm,
                                   vel,
                                   theta,
                                   omega,
                                   mass=mass,
                                   density=density,
                                   inertia=inertia,
                                   **kwds)

        self.num_sides = len(vertices)
        self._normals_idxs = self.get_li_indexes()
        self.num_normals = len(self._normals_idxs or self.vertices)

        # Aceleramos um pouco o cálculo para o caso onde todas as normais são
        # LI. entre si. Isto é sinalizado por self._normals_idx = None, que
        # implica que todas as normais do polígono devem ser recalculadas a
        # cada frame
        if self.num_normals == self.num_sides:
            self._normals_idxs = None

        # Movemos para a posição especificada caso _pos seja fornecido
        if pos is not None:
            self._pos = Vec2(*pos)
Esempio n. 3
0
    def get_normals(self):
        '''Retorna uma lista com as normais linearmente independentes.'''

        if self._normals_idxs is None:
            N = self.num_sides
            points = self.vertices
            segmentos = (points[(i + 1) % N] - points[i] for i in range(N))
            return [Vec2(y, -x).normalize() for (x, y) in segmentos]
        else:
            return [self.get_normal(i) for i in self._normals_idxs]
Esempio n. 4
0
    def __init__(self, A, B, world=None, pos=None, normal=None, delta=0.0):
        super(Collision, self).__init__(A, B)
        self.objects = A, B
        self.world = world
        self.is_active = True
        self.resolved = False
        self.vrel = nullvec2
        if pos is None:
            raise ValueError(A, B)
        self.pos = Vec2(pos)
        self.normal = Vec2(normal)
        self.delta = delta
        self.Jn = 0.0
        self.vel_bias = 0.0
        self.init()

        # Certifica se normal foi bem escolhida
        if (self.rA.dot(self.normal) < 0):
            self.__init__(self.A, self.B, world=world, pos=pos, normal=-normal,
                          delta=delta)
Esempio n. 5
0
    def vpoint(self, pos_or_x, y=None, relative=False):
        '''Retorna a velocidade linear de um ponto em _pos preso rigidamente ao
        objeto.

        Se o parâmetro `relative` for verdadeiro, o vetor `_pos` é interpretado
        como a posição relativa ao centro de massa. O padrão é considerá-lo
        como a posição absoluta no centro de coordenadas do mundo.'''

        if relative:
            if y is None:
                x, y = pos_or_x
                return self._vel + self._omega * Vec2(-y, x)
            else:
                return self._vel + self._omega * Vec2(-y, pos_or_x)

        else:
            if y is None:
                x, y = pos_or_x - self._pos
                return self._vel + self._omega * Vec2(-y, x)
            else:
                x = pos_or_x - self._pos.x
                y = y - self._pos.y
                return self._vel + self._omega * Vec2(-y, x)
Esempio n. 6
0
    def _make_fast_combined(self, combined):
        '''Cria função que retorna a força como a soma de todas as constantes
        k e forças f para cada elemento (k, f) em combined'''

        F = Vec2(0, 0)
        Fmul = F.__imul__
        Fadd = F.__iadd__

        def fast_func(t):
            Fmul(0)
            for k, func in combined:
                fi = func(t)
                Fadd((k * fi[0], k * fi[1]))
            return F

        return fast_func
Esempio n. 7
0
    def _make_fast_adds(self, adds):
        '''Cria função que retorna a força como a soma de todas as forças em
        adds'''

        # TODO: adaptar para vetores não-mutáveis?
        F = Vec2(0, 0)
        Fmul = F._imul  # usamos estas funções para evitar problemas de
        Fadd = F._iadd  # escopo no closure fa função fast_func

        def fast_func(t):
            Fmul(0)
            for func in adds:
                Fadd(func(t))
            return F

        return fast_func
Esempio n. 8
0
    def __init__(self, obj, G, M=None, epsilon=0, r0=(0, 0)):
        if M is None:
            M = obj.mass
        M = self._M = float(M)
        self._epsilon = float(epsilon)
        self._G = float(G)
        r0 = self._r0 = Vec2(*r0)

        def F(R):
            R = r0 - R
            r = R.norm()
            r3 = (r + epsilon)**2 * r
            R *= G * obj.mass * M / r3
            return R

        def U(R):
            R = (R - r0).norm()
            return -self._G * obj.mass * M / (R + self._epsilon)

        super(GravitySF, self).__init__(obj, F, U)
Esempio n. 9
0
    def __init__(self,
                 pos=nullvec2,
                 vel=nullvec2,
                 theta=0.0,
                 omega=0.0,
                 mass=None,
                 density=None,
                 inertia=None,
                 gravity=None,
                 damping=None,
                 adamping=None,
                 restitution=None,
                 sfriction=None,
                 dfriction=None,
                 baseshape=None,
                 world=None,
                 col_layer=0,
                 col_group=0,
                 flags=DEFAULT_FLAGS):

        self._world = world
        EventDispatcher.__init__(self)

        # Flags de objeto
        self.flags = flags

        # Variáveis de estado #################################################
        self._pos = Vec2(pos)
        self._vel = Vec2(vel)
        self._e_vel = nullvec2
        self._e_omega = 0.0
        self._theta = float(theta)
        self._omega = float(omega)
        self._accel = nullvec2
        self._alpha = 0.0

        # Harmoniza massa, inércia e densidade ################################
        self._baseshape = self._shape = baseshape
        self._aabb = getattr(baseshape, 'aabb', None)
        if density is not None:
            density = float(density)
            if mass is None:
                mass = density * self.area()
            else:
                mass = float(mass)
            if inertia is None:
                inertia = density * \
                    self.area() * self.ROG_sqr() / INERTIA_SCALE
            else:
                inertia = float(inertia)

        elif mass is not None:
            mass = float(mass)
            density = mass / self.area()
            if inertia is None:
                inertia = mass * self.ROG_sqr() / INERTIA_SCALE
            else:
                inertia = float(inertia)

        else:
            density = 1.0
            mass = density * self.area()
            if inertia is None:
                inertia = density * \
                    self.area() * self.ROG_sqr() / INERTIA_SCALE
            else:
                inertia = float(inertia)

        self._invmass = 1.0 / mass
        self._invinertia = 1.0 / inertia
        self._density = float(density)

        # Controle de parâmetros físicos locais ###############################
        self._gravity = nullvec2
        self._damping = self._adamping = 0.0
        self._sfriction = self._dfriction = 0.0
        self._restitution = 1.0
        if damping is not None:
            self.damping = damping
        if adamping is not None:
            self.adamping = adamping
        if gravity is not None:
            self.gravity = gravity
        if restitution is not None:
            self.restitution = restitution
        if sfriction is not None:
            self.sfriction = sfriction
        if sfriction is not None:
            self.dfriction = dfriction

        # Vínculos e contatos #################################################
        self._contacts = []
        self._joints = []

        # Filtros de colisões #################################################
        # Colide se objetos estão em groupos diferentes (exceto os que estão
        # no grupo 0) e no mesmo layer
        self._col_layer = int(col_layer)
        if col_group:
            if isinstance(col_group, int):
                self._col_group_mask = 1 << (col_group - 1)
            else:
                mask = 0
                for n in col_group:
                    mask |= 1 << (n - 1)
                self._col_group_mask = mask
        else:
            self._col_group_mask = 0
        # TODO: fundir col_layer com col_group_mask no mesmo inteiro?
        # (talvez 50 bits para grupos e 10 ==> 1024 layers diferentes)
        # Do jeito que está, podemos utilizar strings como identificadores
        # de layers (mas não para grupos). A classe mundo poderia fazer um
        # mapa entre strings > números nos dois casos.

        # Presença em mundo ###################################################
        if world is not None:
            self._world.add(self)
Esempio n. 10
0
 def gravity(self, value):
     try:
         self._gravity = Vec2(*value)
     except TypeError:
         self._gravity = Vec2(0, -value)
     self.flags |= flags.owns_gravity
Esempio n. 11
0
 def from_pos(self, x, y):
     w, h = self._shape
     x0, y0 = self._origin
     return Vec2(x0 + a * w + x, y0 + b * h + y)
Esempio n. 12
0
    def apply_accel(self, a, dt):
        '''Aplica uma aceleração linear durante um intervalo de tempo dt.

        Tem efeito em objetos cinemáticos.

        Observations
        ------------

        Implementa a integração de Velocity-Verlet para o sistema. Este
        integrador é superior ao Euler por dois motivos: primeiro, trata-se de
        um integrador de ordem superior (O(dt^4) vs O(dt^2)). Além disto, ele
        possui a propriedade simplética, o que implica que o erro da energia
        não tende a divergir, mas sim oscilar ora positivamente ora
        negativamente em torno de zero. Isto é extremamente desejável para
        simulações de física que parecam realistas.

        A integração de Euler seria implementada como:

            x(t + dt) = x(t) + v(t) * dt + a(t) * dt**2 / 2
            v(t + dt) = v(t) + a(t) * dt

        Em código Python

        >>> self.move(self.vel * dt + a * (dt**2/2))           # doctest: +SKIP
        >>> self.boost(a * dt)                                 # doctest: +SKIP

        Este método simples e intuitivo sofre com o efeito da "deriva de
        energia". Devido aos erros de truncamento, o valor da energia da
        solução numérica flutua com relação ao valor exato. Na grande maioria
        dos sistemas, esssa flutuação ocorre com mais probabilidade para a
        região de maior energia e portanto a energia tende a crescer
        continuamente, estragando a credibilidade da simulação.

        Velocity-Verlet está numa classe de métodos numéricos que não sofrem
        com esse problema. A principal desvantagem, no entanto, é que devemos
        manter uma variável adicional com o último valor conhecido da
        aceleração. Esta pequena complicação é mais que compensada pelo ganho
        em precisão numérica. O algorítmo consiste em:

            x(t + dt) = x(t) + v(t) * dt + a(t) * dt**2 / 2
            v(t + dt) = v(t) + [(a(t) + a(t + dt)) / 2] * dt

        O termo a(t + dt) normalemente só pode ser calculado se soubermos como
        obter as acelerações como função das posições x(t + dt). Na prática,
        cada iteração de .apply_accel() calcula o valor da posição em x(t + dt)
        e da velocidade no passo anterior v(t). Calcular v(t + dt) requer uma
        avaliação de a(t + dt), que só estará disponível na iteração seguinte.
        A próxima iteração segue então para calcular v(t + dt) e x(t + 2*dt), e
        assim sucessivamente.

        A ordem de acurácia de cada passo do algoritmo Velocity-Verlet é de
        O(dt^4) para uma força que dependa exclusivamente da posição e tempo.
        Caso haja dependência na velocidade, a acurácia reduz e ficaríamos
        sujeitos aos efeitos da deriva de energia. Normalmente as forças
        físicas que dependem da velocidade são dissipativas e tendem a reduzir
        a energia total do sistema muito mais rapidamente que a deriva de
        energia tende a fornecer energia espúria ao sistema. Deste modo, a
        acurácia ficaria reduzida, mas a simulação ainda manteria alguma
        credibilidade.
        '''

        # TODO: reimplementar algoritmo correto
        a = Vec2.from_seq(a)
        self.move(self._vel * dt + a * (dt**2 / 2.0))
        self.boost(a * dt)
Esempio n. 13
0
 def pos_prop(self):
     w, h = self._shape
     x0, y0 = self._origin
     return Vec2(x0 + a * w, y0 + b * h)
Esempio n. 14
0
 def F(rA, rB):
     Dx = rB.x - rA.x + dx
     Dy = rB.y - rA.y + dy
     return Vec2(kx * Dx + kxy * Dy, +ky * Dy + kxy * Dx)
Esempio n. 15
0
# -*- coding: utf8 -*-
'''
========
Tutorial
========

Exemplo básico
==============

Este tutorial explica como utilizar a FGAme para a criação de jogos ou
simulações de física simples. A FGAme é um motor de jogos com ênfase na
simulação de física. Todos os objetos, portanto, possuem propriedades físicas
bem definidas como massa, momento de inércia, velocidade, etc. A simulação da
física é feita, em grande parte, de modo automático.

O primeiro passo é definir o palco que os objetos habitarão. Isto pode ser
feito criando um objeto da classe World().

>>> world = World()

A partir daí, podemos criar objetos e inserí-los na simulação

>>> obj1 = Circle(50)
>>> obj2 = Circle(50, color='red')
>>> world.add([obj1, obj2])

Para modificar as propriedades físicas dos objetos basta modificar diretamente
os atributos correspondentes. Uma lista completa de atributos pode ser
encontrada no módulo `FGAme.objects`:module:.

>>> obj1.mass = 10
Esempio n. 16
0
 def __set__(self, obj, value):
     if not isinstance(value, Vec2):
         value = Vec2(value)
     setter(obj, value)
Esempio n. 17
0
# -*- coding: utf8 -*-

from FGAme.mathutils import Vec2, dot, area, center_of_mass, clip, pi
from FGAme.mathutils import shadow_x, shadow_y

from FGAme.physics.collision import Collision
from FGAme.physics import Circle, AABB, Poly, Rectangle
from FGAme.util import multifunction

u_x = Vec2(1, 0)
DEFAULT_DIRECTIONS = [
    u_x.rotate(n * pi / 12) for n in [0, 1, 2, 3, 4, 5, 7, 8, 9, 10, 11]
]


class CollisionError(Exception):
    '''Declara que não existem colisões disponíveis para os dois tipos de
    objetos'''
    pass


def get_collision_generic(A, B):
    '''Retorna uma colisão genérica definido pela CBB dos objetos A e B'''

    rA = A.cbb_radius
    rB = B.cbb_radius
    delta = B._pos - A._pos
    if delta.norm() < rA + rB:
        n = delta.normalize()
        D = rA + rB - delta.norm()
        pos = A._pos + (rA - D / 2) * n
Esempio n. 18
0
 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._direct = True
Esempio n. 19
0
 def pos_sw(self):
     return Vec2(self.xmin, self.ymin)
Esempio n. 20
0
 def pos_se(self):
     return Vec2(self.xmax, self.ymin)
Esempio n. 21
0
 def pos_nw(self):
     return Vec2(self.xmin, self.ymax)
Esempio n. 22
0
 def F(R):
     Dx = x0 - R.x
     Dy = y0 - R.y
     return Vec2(kx * Dx + kxy * Dy, ky * Dy + kxy * Dx)
Esempio n. 23
0
 def pos_ne(self):
     return Vec2(self.xmax, self.ymax)
Esempio n. 24
0
    def apply_accel(self, a, dt):
        '''Aplica uma aceleração linear durante um intervalo de tempo dt.

        Tem efeito em objetos cinemáticos.

        Observations
        ------------

        Implementa a integração de Velocity-Verlet para o sistema. Este
        integrador é superior ao Euler por dois motivos: primeiro, trata-se de
        um integrador de ordem superior (O(dt^4) vs O(dt^2)). Além disto, ele
        possui a propriedade simplética, o que implica que o erro da energia
        não tende a divergir, mas sim oscilar ora positivamente ora
        negativamente em torno de zero. Isto é extremamente desejável para
        simulações de física que parecam realistas.

        A integração de Euler seria implementada como:

            x(t + dt) = x(t) + v(t) * dt + a(t) * dt**2 / 2
            v(t + dt) = v(t) + a(t) * dt

        Em código Python

        >>> self.move(self.vel * dt + a * (dt**2/2))           # doctest: +SKIP
        >>> self.boost(a * dt)                                 # doctest: +SKIP

        Este método simples e intuitivo sofre com o efeito da "deriva de
        energia". Devido aos erros de truncamento, o valor da energia da
        solução numérica flutua com relação ao valor exato. Na grande maioria
        dos sistemas, esssa flutuação ocorre com mais probabilidade para a
        região de maior energia e portanto a energia tende a crescer
        continuamente, estragando a credibilidade da simulação.

        Velocity-Verlet está numa classe de métodos numéricos que não sofrem
        com esse problema. A principal desvantagem, no entanto, é que devemos
        manter uma variável adicional com o último valor conhecido da
        aceleração. Esta pequena complicação é mais que compensada pelo ganho
        em precisão numérica. O algorítmo consiste em:

            x(t + dt) = x(t) + v(t) * dt + a(t) * dt**2 / 2
            v(t + dt) = v(t) + [(a(t) + a(t + dt)) / 2] * dt

        O termo a(t + dt) normalemente só pode ser calculado se soubermos como
        obter as acelerações como função das posições x(t + dt). Na prática,
        cada iteração de .apply_accel() calcula o valor da posição em x(t + dt)
        e da velocidade no passo anterior v(t). Calcular v(t + dt) requer uma
        avaliação de a(t + dt), que só estará disponível na iteração seguinte.
        A próxima iteração segue então para calcular v(t + dt) e x(t + 2*dt), e
        assim sucessivamente.

        A ordem de acurácia de cada passo do algoritmo Velocity-Verlet é de
        O(dt^4) para uma força que dependa exclusivamente da posição e tempo.
        Caso haja dependência na velocidade, a acurácia reduz e ficaríamos
        sujeitos aos efeitos da deriva de energia. Normalmente as forças
        físicas que dependem da velocidade são dissipativas e tendem a reduzir
        a energia total do sistema muito mais rapidamente que a deriva de
        energia tende a fornecer energia espúria ao sistema. Deste modo, a
        acurácia ficaria reduzida, mas a simulação ainda manteria alguma
        credibilidade.
        '''

        # TODO: reimplementar algoritmo correto
        a = Vec2.from_seq(a)
        self.move(self._vel * dt + a * (dt ** 2 / 2.0))
        self.boost(a * dt)
Esempio n. 25
0
    def _random(self, scale, angle):
        pass

    def random(self, angle=None):
        return self._random(self.fair, angle)

    def random_fast(self, angle=None):
        return self._random(self.slow, angle)

    def random_slow(self, angle=None):
        return self._random(self.slow, angle)


# Velocidades em direções específicas #########################################
_speeds = dict(
    up=Vec2(0, 1),
    down=Vec2(0, -1),
    right=Vec2(1, 0),
    left=Vec2(-1, 0),
    ne=Vec2(1, 1).normalize(),
    nw=Vec2(-1, 1).normalize(),
    se=Vec2(1, -1).normalize(),
    sw=Vec2(-1, -1).normalize(),
)


def speed_prop(name, scale, vec):
    def prop(self):
        return getattr(self, scale) * vec

    prop.__name__ = name