Пример #1
0
    def init(self):
        '''Calcula propriedades da colisão'''

        A, B = self.A, self.B
        n = self.normal
        P = self.pos

        # Coeficiente de atrito e restituição
        self.mu = self.get_friction_coeff()
        self.e = self.get_restitution()

        # Pontos de contato
        self.rA = rA = P - A.pos
        self.rB = rB = P - B.pos
        self.rA_ortho = Vec2(-rA.y, rA.x)
        self.rB_ortho = Vec2(-rB.y, rB.x)

        # Massa efetiva
        eff_invmass = A._invmass + B._invmass

        if A._invinertia:
            eff_invmass += rA.cross(n) ** 2 * A._invinertia

        if B._invinertia:
            eff_invmass += rB.cross(n) ** 2 * B._invinertia

        self.effmass = 1.0 / eff_invmass

        # Viés na velocidade relativa para definir o coeficiente de restituição
        vrel = ((B.vel + B.omega * self.rB_ortho)
                - (A.vel + A.omega * self.rA_ortho))
        vrel_normal = vrel.dot(n)
        if -vrel_normal > self.min_vel_bias:
            self.vel_bias = -self.e * vrel_normal
Пример #2
0
def collision_aabb(A, B):
    '''Retorna uma colisão com o objeto other considerando apenas a caixas
    de contorno alinhadas ao eixo.'''

    # Detecta colisão pelas sombras das caixas de contorno
    rx, ry = B._pos - A._pos
    shadowx = A._delta_x + B._delta_x - abs(rx)
    shadowy = A._delta_y + B._delta_y - abs(ry)
    if shadowx <= 0 or shadowy <= 0:
        return None

    # Calcula ponto de colisão
    x_col = max(A.xmin, B.xmin) + shadowx / 2.
    y_col = max(A.ymin, B.ymin) + shadowy / 2.
    pos_col = Vec2(x_col, y_col)

    # Define sinal dos vetores normais: colisões tipo PONG
    if shadowx > shadowy:
        n = Vec2(0, (1 if A.ymin < B.ymin else -1))
        delta = shadowy
    else:
        n = Vec2((1 if A.xmin < B.xmin else -1), 0)
        delta = shadowx

    return Collision(A, B, pos=pos_col, normal=n, delta=delta)
Пример #3
0
def circle_aabb(A, B):
    # TODO: implementar direito, está utilizando AABBs
    r = A.cbb_radius
    x, y = A._pos
    Axmin, Axmax = x - r, x + r
    Aymin, Aymax = y - r, y + r

    x, y = B._pos
    dx, dy = B._delta_x, B._delta_y
    Bxmin, Bxmax = x - dx, x + dx
    Bymin, Bymax = y - dy, y + dy

    shadowx = min(Axmax, Bxmax) - max(Axmin, Bxmin)
    shadowy = min(Aymax, Bymax) - max(Aymin, Bymin)
    if shadowx < 0 or shadowy < 0:
        return None

    # Calcula ponto de colisão
    x_col = max(A.xmin, B.xmin) + shadowx / 2.
    y_col = max(A.ymin, B.ymin) + shadowy / 2.
    pos_col = Vec2(x_col, y_col)

    # Define sinal dos vetores normais: colisões tipo PONG
    if shadowx > shadowy:
        n = Vec2(0, (1 if A.ymin < B.ymin else -1))
    else:
        n = Vec2((1 if A.xmin < B.xmin else -1), 0)

    return Collision(A, B, pos=pos_col, normal=n)
Пример #4
0
 def _vertices(self, N, length, pos):
     self.length = length
     alpha = pi / N
     theta = 2 * alpha
     b = length / (2 * sin(alpha))
     P0 = Vec2(b, 0)
     pos = Vec2(*pos)
     return [(P0.rotate(n * theta)) + pos for n in range(N)]
Пример #5
0
    def gravity(self, value):
        owns_prop = BodyFlags.owns_gravity
        old = self._gravity
        try:
            gravity = self._gravity = Vec2(*value)
        except TypeError:
            gravity = self._gravity = Vec2(0, -value)

        for obj in self._objects:
            if not obj.flags & owns_prop:
                obj._gravity = gravity
        self.trigger('gravity-change', old, self._gravity)
Пример #6
0
    def __call__(self, t):
        # Se o argumento for uma função, significa que estamos usando o
        # decorador para definir o valor da força
        if callable(t):
            func = t
            self.clear()
            self.add(func)
            return func

        # Caso contrário, assume que o argumento é numérico e executa
        t = float(t)
        if self._fast is not None:
            return Vec2(*self._fast(t))
        else:
            return Vec2(0, 0)
Пример #7
0
    def orientation(self, theta=0.0):
        '''Retorna um vetor unitário na direção em que o objeto está orientado.
        Pode aplicar um ângulo adicional a este vetor fornecendo o parâmetro
        _theta.'''

        theta += self._theta
        return Vec2(cos(theta), sin(theta))
Пример #8
0
    def boost(self, delta_or_x, y=None):
        '''Adiciona um valor vetorial delta à velocidade linear'''

        if y is None:
            self._vel += delta_or_x
        else:
            self._vel += Vec2(delta_or_x, y)
Пример #9
0
    def __init__(self, obj, k, r0=(0, 0)):
        kxy = 0
        x0, y0 = self._r0 = Vec2(*r0)

        try:  # Caso isotrópico
            kx = ky = k = self._k = float(k)
        except TypeError:  # Caso anisotrópico
            k = self._k = tuple(map(float, k))
            if len(k) == 2:
                kx, ky = k
            else:
                kx, ky, kxy = k

        # Define forças e potenciais
        def F(R):
            Dx = x0 - R.x
            Dy = y0 - R.y
            return Vec2(kx * Dx + kxy * Dy, ky * Dy + kxy * Dx)

        def U(R):
            Dx = x0 - R.x
            Dy = y0 - R.y
            return (kx * Dx**2 + ky * Dy**2 + 2 * kxy * Dx * Dy) / 2

        super(SpringSF, self).__init__(obj, F, U)
Пример #10
0
    def __init__(self, A, B, k, delta=(0, 0)):
        kxy = 0
        dx, dy = delta = self._delta = Vec2(*delta)

        try:  # Caso isotrópico
            kx = ky = k = self._k = float(k)
        except TypeError:  # Caso anisotrópico
            k = self._k = tuple(map(float, k))
            if len(k) == 2:
                kx, ky = k
            else:
                kx, ky, kxy = k

        # Define forças e potenciais
        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)

        def U(rA, rB):
            Dx = rB.x - rA.x + dx
            Dy = rB.y - rA.y + dy
            return (kx * Dx**2 + ky * Dy**2 + 2 * kxy * Dx * Dy) / 2

        super(SpringF, self).__init__(A, B, F, U)
Пример #11
0
 def get_tangent(self):
     # Vetor unitário tangente à colisão
     n = self.normal
     tangent = Vec2(-n.y, n.x)
     if tangent.dot(self.vrel) > 0:
         tangent *= -1
     return tangent
Пример #12
0
    def get_normal(self, i):
        '''Retorna a normal unitária associada ao i-ésimo segmento. Cada
        segmento é definido pela diferença entre o (i+1)-ésimo ponto e o
        i-ésimo ponto.'''

        points = self.vertices
        x, y = points[(i + 1) % self.num_sides] - points[i]
        return Vec2(y, -x).normalize()
Пример #13
0
    def apply_impulse(self, impulse_or_x, y=None):
        '''Aplica um impulso linear ao objeto. Isto altera sua velocidade
        linear com relação ao centro de massa.

        Se for chamado com dois agumentos aplica o impulso em um ponto
        específico e também resolve a dinâmica angular.
        '''

        self.boost(Vec2(impulse_or_x, y) * self._invmass)
Пример #14
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)
Пример #15
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)
Пример #16
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]
Пример #17
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)
Пример #18
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)
Пример #19
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
Пример #20
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
Пример #21
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)
Пример #22
0
 def gravity(self, value):
     try:
         self._gravity = Vec2(*value)
     except TypeError:
         self._gravity = Vec2(0, -value)
     self.flags |= flags.owns_gravity
Пример #23
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)
Пример #24
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)
Пример #25
0
 def F(R):
     Dx = x0 - R.x
     Dy = y0 - R.y
     return Vec2(kx * Dx + kxy * Dy, ky * Dy + kxy * Dx)
Пример #26
0
 def pos_sw(self):
     return Vec2(self.xmin, self.ymin)
Пример #27
0
 def pos_se(self):
     return Vec2(self.xmax, self.ymin)
Пример #28
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
Пример #29
0
 def pos_nw(self):
     return Vec2(self.xmin, self.ymax)
Пример #30
0
 def pos_ne(self):
     return Vec2(self.xmax, self.ymax)