def J_normal(self): '''Valor absoluto do impulso normal antes de considerar o efeito do atrito no cálculo da componente tangencial''' A, B = self.objects pos = self.pos n = self.normal J_denom = A._invmass + B._invmass if A._invinertia or A.omega: R = pos - A._pos J_denom += cross(R, n) ** 2 * A._invinertia if B._invinertia or B.omega: R = pos - B._pos J_denom += cross(R, n) ** 2 * B._invinertia # Determina o impulso total if J_denom == 0.0: return 0.0 vrel_n = dot(self.vrel_contact, n) if vrel_n > 0: return 0.0 return -(1 + self.e) * vrel_n / J_denom
def tangent(self): '''Vetor unitário tangente à colisão''' n = self.normal tangent = Vector(-n.y, n.x) if dot(tangent, self.vrel_contact) > 0: tangent *= -1 return tangent
def collision_poly(A, B, directions=None): '''Implementa a colisão entre dois polígonos arbitrários''' # Cria a lista de direções a partir das normais do polígono if directions is None: if A.num_normals + B.num_normals < 9: directions = A.get_normals() + B.get_normals() else: directions = DEFAULT_DIRECTIONS # Testa se há superposição de sombras em todas as direções consideradas # e calcula o menor valor para sombra e a direção normal min_shadow = float('inf') norm = None for u in directions: A_coords = [round(dot(pt, u), 6) for pt in A.vertices] B_coords = [round(dot(pt, u), 6) for pt in B.vertices] Amax, Amin = max(A_coords), min(A_coords) Bmax, Bmin = max(B_coords), min(B_coords) minmax, maxmin = min(Amax, Bmax), max(Amin, Bmin) shadow = minmax - maxmin if shadow < 0: return None elif shadow < min_shadow: min_shadow = shadow norm = u # Determina o sentido da normal if dot(A._pos, norm) > dot(B._pos, norm): norm = -norm # Computa o polígono de intersecção e usa o seu centro de massa como ponto # de colisão try: clipped = clip(A.vertices, B.vertices) # não houve superposição (talvez por usar normais aproximadas) except ValueError: return None if area(clipped) == 0: return None col_pt = center_of_mass(clipped) return Collision(A, B, pos=col_pt, normal=norm, delta=min_shadow)
def get_collision_poly(A, B, directions=None): '''Implementa a colisão entre dois polígonos arbitrários''' # Cria a lista de direções a partir das normais do polígono if directions is None: if A.num_normals + B.num_normals < 9: directions = A.get_normals() + B.get_normals() else: directions = DEFAULT_DIRECTIONS # Testa se há superposição de sombras em todas as direções consideradas # e calcula o menor valor para sombra e a direção normal min_shadow = float('inf') norm = None for u in directions: A_coords = [round(dot(pt, u), 6) for pt in A.vertices] B_coords = [round(dot(pt, u), 6) for pt in B.vertices] Amax, Amin = max(A_coords), min(A_coords) Bmax, Bmin = max(B_coords), min(B_coords) minmax, maxmin = min(Amax, Bmax), max(Amin, Bmin) shadow = minmax - maxmin if shadow < 0: return None elif shadow < min_shadow: min_shadow = shadow norm = u # Determina o sentido da normal if dot(A.pos, norm) > dot(B.pos, norm): norm = -norm # Computa o polígono de intersecção e usa o seu centro de massa como ponto # de colisão try: clipped = clip(A.vertices, B.vertices) # não houve superposição (talvez por usar normais aproximadas) except ValueError: return None if area(clipped) == 0: return None col_pt = center_of_mass(clipped) return Collision(A, B, col_pt, norm, min_shadow)
def J_tangent(self): '''Retorna o módulo da componente tangencial do atrito''' A, B = self.objects vrel = self.vrel_contact # Calcula o impulso tangente máximo vrel_tan = -dot(vrel, self.tangent) Jtan_max = abs(self.mu * self.J_normal) # Limita a ação do impulso tangente A_can_move = A._invmass or A._invinertia B_can_move = B._invmass or B._invinertia # Calcula o tangente dependendo do estado dinâmico de cada objeto if A_can_move and B_can_move: return min([Jtan_max, vrel_tan * A.mass, vrel_tan * B.mass]) elif A_can_move: return min([Jtan_max, vrel_tan * A.mass]) elif B_can_move: return min([Jtan_max, vrel_tan * B.mass]) else: return 0.0
def get_impulse(self, dt=0): '''Calcula o impulso devido à colisão. Retorna o impulso gerado pelo objeto A em cima do objeto B. (Ou seja: A recebe o negativo do impulso, enquanto B recebe o valor positivo).''' A, B = self.objects pos = self.pos n = self.normal e = self.rest_coeff() mu = self.friction_coeff() # Calcula a resposta impulsiva vrel = B.vel - A.vel J_denom = A._invmass + B._invmass if A._invinertia or A._omega: x, y = R = pos - A.pos vrel -= A._omega * Vector(-y, x) J_denom += cross(R, n)**2 * A._invinertia if B._invinertia or B._omega: x, y = R = pos - B.pos vrel += B.omega * Vector(-y, x) J_denom += cross(R, n)**2 * B._invinertia # Determina o impulso total if not J_denom: return 0 # Não resolve colisão se o impulso estiver orientado a favor da normal # Isto acontece se a superposição não cessa no frame seguinte ao da # colisão. vrel_n = dot(vrel, n) if vrel_n > 0: return None J = -(1 + e) * vrel_n / J_denom # Determina influência do atrito if mu: # Encontra o vetor tangente adequado t = Vector(-n.y, n.x) if dot(t, vrel) > 0: t *= -1 # Calcula o impulso tangente máximo vrel_tan = -dot(vrel, t) Jtan = abs(mu * J) # Limita a ação do impulso tangente A_can_move = A._invmass or A._invinertia B_can_move = B._invmass or B._invinertia if A_can_move and B_can_move: Jtan = min([Jtan, vrel_tan * A.mass, vrel_tan * B.mass]) elif A_can_move: Jtan = min([Jtan, vrel_tan * A.mass]) elif B_can_move: Jtan = min([Jtan, vrel_tan * B.mass]) return J * n + Jtan * t else: return J * n
def linearE(self): '''Energia cinética das variáveis lineares''' return dot(self._vel, self._vel) / (2 * self._invmass)
def linearE(self): return self._mass * dot(self.vel, self.vel) / 2
def is_internal_point(self, pt): '''Retorna True se um ponto for interno ao polígono.''' n = self.get_normal P = self.vertices return all(dot(pt - P[i], n(i)) <= 0 for i in range(self.num_sides))