def _vertices(self, N, length, pos): self.length = length alpha = pi / N theta = 2 * alpha b = length / (2 * sin(alpha)) P0 = Vector(b, 0) pos = Vector(*pos) return [(P0.rotated(n * theta)) + pos for n in range(N)]
def gravity(self, value): try: gravity = self._gravity = Vector(*value) except TypeError: gravity = self._gravity = Vector(0, -value) for obj in self._objects: if not obj.owns_gravity: obj._gravity = gravity
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 Vector(*self._fast(t)) else: return Vector(0, 0)
def __init__(self, obj, k, r0=(0, 0)): kxy = 0 x0, y0 = self._r0 = Vector(*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 Vector(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)
def __init__(self, A, B, k, delta=(0, 0)): kxy = 0 dx, dy = delta = self._delta = Vector(*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 Vector(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)
def __init__(self, shape=(800, 600), pos=(0, 0), zoom=1, background=None): self.width, self.height = shape self.pos = Vector(*pos) self.zoom = zoom self.background = background self._direct = True self.init()
def intercept_point(): '''Retorna o ponto de intercepção entre os segmentos formados por r1-r0 e v1-v0''' A = r0.x * r1.y - r0.y * r1.x B = v0.x * v1.y - v0.y * v1.x C = 1.0 / (T.x * T_.y - T.y * T_.x) return Vector((-A * T_.x + B * T.x) * C, (-A * T_.y + B * T.y) * C)
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 Vector(y, -x).normalized()
def vpoint(self, pos, 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.''' x, y = pos - self._pos return self._vel + self._omega * Vector(-y, x)
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 [Vector(y, -x).normalized() for (x, y) in segmentos] else: return [self.get_normal(i) for i in self._normals_idxs]
def test_vector_algeb(): v1 = Vector(1, 2) v2 = Vector(2, 3) v3 = Vector(4, 5) assert 2 * v1 == Vector(2, 4) assert v1 * 2 == Vector(2, 4) assert v1 + 2 * v2 - 3 * v3 == Vector(-7, -7)
def get_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 shadowx = shadow_x(A, B) shadowy = shadow_y(A, B) 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 = Vector(x_col, y_col) # Define sinal dos vetores normais: colisões tipo PONG if shadowx > shadowy: n = Vector(0, (1 if A.ymin < B.ymin else -1)) else: n = Vector((1 if A.xmin < B.xmin else -1), 0) return Collision(A, B, pos_col, n)
def _init_has_local_forces(self, gravity=None, damping=None, adamping=None, owns_gravity=None, owns_damping=None, owns_adamping=None): self._damping = 0.0 if damping is not None: self.flag_owns_damping = True self._damping = float(damping) self._adamping = 0.0 if adamping is not None: self.flag_owns_adamping = True self._adamping = float(adamping) self._gravity = Vector(0, 0) if gravity is not None: self.flag_owns_gravity = True self._gravity = (Vector(0, -gravity) if isinstance( gravity, (float, int)) else Vector(*gravity))
def aabb_pshape(bbox=None, rect=None, shape=None, pos=None, xmin=None, xmax=None, ymin=None, ymax=None): ''' Retorna uma tupla de (centro, shape) a partir dos parâmetros fornecidos. ''' x, y, xmax, ymax = aabb_bbox(bbox, rect, shape, pos, xmin, xmax, ymin, ymax) center = Vector((x + xmax) / 2.0, (y + ymax) / 2.0) shape = (xmax - x, ymax - y) return center, shape
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 = Vector(*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)
def convex_hull(points): '''Retorna a envoltória convexa do conjunto de pontos fornecidos. Implementa o algorítimo da cadeia monótona de Andrew, O(n log n) Exemplo ------- >>> convex_hull([(0, 0), (1, 1), (1, 0), (0, 1), (0.5, 0.5)]) [Vector(0, 0), Vector(1, 0), Vector(1, 1), Vector(0, 1)] ''' # Ordena os pontos pela coordenada x, depois pela coordenada y points = sorted(set(map(tuple, points))) points = [Vector(*pt) for pt in points] if len(points) <= 1: return points # Cria a lista L: lista com os vértices da parte inferior da envoltória # # Algoritimo: acrescenta os pontos de points em L e a cada novo ponto # remove o último caso não faça uma volta na direção anti-horária L = [] for p in points: while len(L) >= 2 and cross(L[-1] - L[-2], p - L[-2]) <= 0: L.pop() L.append(p) # Cria a lista U: vértices da parte superior # Semelhante à anterior, mas itera sobre os pontos na ordem inversa U = [] for p in reversed(points): while len(U) >= 2 and cross(U[-1] - U[-2], p - U[-2]) <= 0: U.pop() U.append(p) # Remove o último ponto de cada lista, pois ele se repete na outra return L[:-1] + U[:-1]
def center_of_mass(L): '''Calcula o vetor centro de massa de um polígono definido por uma lista de pontos. >>> pontos = [(0, 0), (1, 0), (1, 1), (0, 1)] >>> center_of_mass(pontos) Vector(0.5, 0.5) ''' W = _w_list(L) A = sum(W) N = len(L) x_cm = 0 y_cm = 0 for i in range(N): x1, y1 = L[(i + 1) % N] x0, y0 = L[i] wi = W[i] x_cm += (x1 + x0) * wi / 3.0 y_cm += (y1 + y0) * wi / 3.0 x_cm /= A y_cm /= A return Vector(x_cm, y_cm)
def F(rA, rB): Dx = rB._x - rA._x + dx Dy = rB._y - rA._y + dy return Vector(kx * Dx + kxy * Dy, +ky * Dy + kxy * Dx)
def F(R): Dx = x0 - R._x Dy = y0 - R._y return Vector(kx * Dx + kxy * Dy, ky * Dy + kxy * Dx)
def test_vector_getitem(): v = Vector(1, 2) assert v[0] == 1, v[1] == 2
from FGAme.mathutils import Vector, dot, area, center_of_mass, clip, pi from FGAme.physics.collision import (Collision, get_collision, get_collision_aabb) from FGAme.physics.elements import (Circle, AABB, Poly, Rectangle) u_x = Vector(1, 0) DEFAULT_DIRECTIONS = [ u_x.rotated(n * pi / 12) for n in [0, 1, 2, 3, 4, 5, 7, 8, 9, 10, 11] ] ############################################################################### # Colisões entre figuras primitivas simples ############################################################################### get_collision[AABB, AABB] = get_collision_aabb @get_collision.dispatch(Circle, Circle) def circle_collision(A, B): '''Testa a colisão pela distância dos centros''' delta = B.pos - A.pos if delta.norm() < A.radius + B.radius: n = delta.normalized() D = A.radius + B.radius - delta.norm() pos = A.pos + (A.radius - D / 2) * n return Collision(A, B, pos=pos, n=n) else: return None
def __init__(self, vertices): self._data = [Vector(*x) for x in vertices]
def vel(self): return Vector(*self._vel)
def accel(self): return Vector(*self._accel)
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 pos(self): return Vector(self.center_x, self.center_y)
def test_vector_add(): v1 = Vector(1, 2) v2 = Vector(2, 3) assert v1 + v2 == Vector(3, 5) assert tuple(v1 + v2) == (3, 5) assert v2 - v1 == Vector(1, 1)
# -*- coding: utf8 -*- import six from FGAme.mathutils import Vector from FGAme.physics import flags __all__ = ['PhysElement'] ############################################################################### # Funções úteis ############################################################################### NOT_IMPLEMENTED = NotImplementedError('must be implemented at child classes') INF = float('inf') ORIGIN = Vector(0, 0) def do_nothing(*args, **kwds): pass def raises_method(ex=NOT_IMPLEMENTED): def method(self, *args, **kwds): raise ex return method ############################################################################### # Classes Base --- todos objetos físicos derivam delas ###############################################################################
def pos(self): return Vector(*self._pos)
# -*- coding: utf8 -*- from FGAme.mathutils import Vector, 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 = Vector(1, 0) DEFAULT_DIRECTIONS = [u_x.rotated(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.normalized() D = rA + rB - delta.norm() pos = A._pos + (rA - D / 2) * n
class PhysElement(object): '''Classe mãe para os objetos que possuem física definida. Não possui propriedades, mas apenas define flags e (possivelmente) um dicionário extra de parâmetros. Por razões de performance e eficiência na alocação de memória, todas as sub-classes devem definir seus __slots__. É possível armazenar variáveis adicionais no dicionário privado do objeto utilizando o atributo _dict_. Para isso, a classe deve definir um conjunto de strings de atributos opcionais na variável _properties_. A metaclasse se encarrega de simular o comportamento de __slots__ no sentido que as sub-classes não precisam definir as _optinal_vars_ da classe mãe. Todas as variáveis dentro deste objeto possuem valores padrão de None. É possível criar parâmetros localmente utilizando o método 'set_property()'. Estas propriedades são acessadas como atributos e podem ser listadas pelo método get_properties() Example ------- Esta classe define apenas funcionalidades básicas, como acesso às flags de física e propriedades. Acesso a flags >>> elem = PhysElement() >>> elem.flag_has_mass False >>> elem.flag_has_mass = True >>> elem.flag_has_mass True Acesso a propriedades >>> elem.set_property('foo', 'bar') >>> elem.foo 'bar' ''' __slots__ = ['_flags', '_dict_'] _properties_ = set() def __init__(self, flags=0, dict_=None): self._flags = flags self._dict_ = dict_ def __getattr__(self, attr): # Captura slots não inicializados if hasattr(type(self), attr): raise AttributeError(attr) D = self._dict_ if D is not None: try: return self._dict_[attr] except KeyError: pass if attr in self._properties_: return None else: raise AttributeError(attr) # Mantêm a simetria com __getattr__, mas possivelmente incorre numa perda de # performance. Do jeito que está, as propriedades são definidas como somente # para leitura. É necessário definir propridedades explicitamente na classe # para obter acesso de escrita. # # É fácil fazer alguma mágica na metaclasse para definir acesso rw, caso # pareça interessante. # # def __setattr__(self, attr, value): # try: # descriptor = self.__class__.__dict__[attr] # except KeyError: # if (attr in self._properties_ or # (self._dict_ is not None and attr in self._dict_)): # self._dict_[attr] = value # else: # descriptor.__set__(self, value) @property def _dict(self): if self._dict_ is None: self._dict_ = {} return self._dict_ def set_property(self, name, value): '''Define uma propriedade do objeto''' if name.startswith('_'): raise ValueError('cannot change private properties' ' (names starting with _)') self._dict[name] = value def get_property(self, name): '''Retorna uma propriedade do objeto''' if name.startswith('_'): raise ValueError('cannot access private properties' ' (names starting with _)') try: return self._dict_[name] except (KeyError, TypeError): if name in self._properties_: return None else: raise ValueError('property') def get_properties(self): '''Retorna um dicionário com todas as propriedades definidas para o objeto em questão''' items = (self._dict_ or {}).items() return {k: v for (k, v) in items if not k.startswith('_')} def _getp(self, name, default): '''Versão mais rápida e privada de get_property(). Pode acessar qualquer propriedade adicional''' try: return self._dict_.get(name, default) except (TypeError, KeyError): return default def _setp(self, name, value): '''Versão mais rápida e privada de set_property(). Pode acessar qualquer propriedade adicional''' try: self._dict_[name] = value except TypeError: self._dict_ = {name: value} def _delp(self, name): '''Apaga uma propriedade do dicionário''' del self._dict_[name] if not self._dict_: self._dict_ = None def _clearp(self, *names): '''Limpa propriedades do dicionário, caso estejam definidas''' D = self._dict_ if D is not None: for name in names: if name in D: del D[name] if not D: self._dict_ = None # Interface Python # Define igualdade <==> identidade ######################################## def __eq__(self, other): return self is other def __hash__(self): return id(self) # Valores padrão ########################################################## mass = float('inf') inertia = float('inf') pos = Vector(0, 0) vel = Vector(0, 0) theta = 0.0 omega = 0.0 area = 0 ROG_sqr = 0 ROG = 0 # Métodos com implementações inócuas ###################################### move = do_nothing() boost = do_nothing() rotate = do_nothing() aboost = do_nothing() ########################################################################### # Controle de flags ########################################################################### # Propriedades de controle de flags -- metacódigo # # Descomente e rode este código caso queira recalcular as propriedades # associadas à cada flag de física. # # from FGAme.physics.flags import _FlagBits # template = ''' # @property # def flag_flagname(self): # return bool(self._flags & flags.FLAGNAME) # # @flag_flagname.setter # def flag_flagname(self, value): # if value: # self._flags |= flags.FLAGNAME # else: # self._flags &= ~flags.FLAGNAME''' # # attrs = [(n, k) for (k, n) in _FlagBits.__dict__.items() # if isinstance(n, int)] # for n, k in sorted(attrs): # func_def = template.replace('flagname', k) # func_def = func_def.replace('FLAGNAME', k.upper()) # print(func_def) # Definições geradas pelo código acima @property def flag_has_inertia(self): return bool(self._flags & flags.HAS_INERTIA) @flag_has_inertia.setter def flag_has_inertia(self, value): if value: self._flags |= flags.HAS_INERTIA else: self._flags &= ~flags.HAS_INERTIA @property def flag_has_linear(self): return bool(self._flags & flags.HAS_LINEAR) @flag_has_linear.setter def flag_has_linear(self, value): if value: self._flags |= flags.HAS_LINEAR else: self._flags &= ~flags.HAS_LINEAR @property def flag_has_angular(self): return bool(self._flags & flags.HAS_ANGULAR) @flag_has_angular.setter def flag_has_angular(self, value): if value: self._flags |= flags.HAS_ANGULAR else: self._flags &= ~flags.HAS_ANGULAR @property def flag_has_bbox(self): return bool(self._flags & flags.HAS_BBOX) @flag_has_bbox.setter def flag_has_bbox(self, value): if value: self._flags |= flags.HAS_BBOX else: self._flags &= ~flags.HAS_BBOX @property def flag_owns_gravity(self): return bool(self._flags & flags.OWNS_GRAVITY) @flag_owns_gravity.setter def flag_owns_gravity(self, value): if value: self._flags |= flags.OWNS_GRAVITY else: self._flags &= ~flags.OWNS_GRAVITY @property def flag_owns_damping(self): return bool(self._flags & flags.OWNS_DAMPING) @flag_owns_damping.setter def flag_owns_damping(self, value): if value: self._flags |= flags.OWNS_DAMPING else: self._flags &= ~flags.OWNS_DAMPING @property def flag_owns_adamping(self): return bool(self._flags & flags.OWNS_ADAMPING) @flag_owns_adamping.setter def flag_owns_adamping(self, value): if value: self._flags |= flags.OWNS_ADAMPING else: self._flags &= ~flags.OWNS_ADAMPING @property def flag_accel_static(self): return bool(self._flags & flags.ACCEL_STATIC) @flag_accel_static.setter def flag_accel_static(self, value): if value: self._flags |= flags.ACCEL_STATIC else: self._flags &= ~flags.ACCEL_STATIC @property def flag_has_world(self): return bool(self._flags & flags.HAS_WORLD) @flag_has_world.setter def flag_has_world(self, value): if value: self._flags |= flags.HAS_WORLD else: self._flags &= ~flags.HAS_WORLD @property def flag_has_visualization(self): return bool(self._flags & flags.HAS_VISUALIZATION) @flag_has_visualization.setter def flag_has_visualization(self, value): if value: self._flags |= flags.HAS_VISUALIZATION else: self._flags &= ~flags.HAS_VISUALIZATION ########################################################################### # Manipulação/consulta do estado dinâmico ########################################################################### def is_dynamic(self, what=None): '''Retorna True se o objeto for dinâmico ou nas variáveis lineares ou nas angulares. Um objeto é considerado dinâmico nas variáveis lineares se possuir massa finita. De maneira análoga, o objeto torna-se dinâmico nas variáveis angulares se possuir momento de inércia finito. Opcionalmente pode especificar um parâmetro posicional 'linear', 'angular', 'both' or 'any' (padrão) para determinar o tipo de consulta a ser realizada''' if what is None or what == 'any': return self.is_dynamic_linear() or self.is_dynamic_angular() elif what == 'both': return self.is_dynamic_linear() and self.is_dynamic_angular() elif what == 'linear': return self.is_dynamic_linear() elif what == 'angular': return self.is_dynamic_angular() else: raise ValueError('unknown mode: %r' % what) def is_dynamic_linear(self): '''Verifica se o objeto é dinâmico nas variáveis lineares''' return self.mass == INF def is_dynamic_angular(self): '''Verifica se o objeto é dinâmico nas variáveis angulares''' return self.inertia == INF def make_dynamic(self, what=None): '''Resgata a massa, inércia e velocidades anteriores de um objeto paralizado pelo método `obj.make_static()` ou `obj.make_kinematic()`. Aceita um argumento com a mesma semântica de is_dynamic() ''' if what is None or what == 'both': self.make_dynamic_linear() self.make_dynamic_angular() elif what == 'linear': self.make_dynamic_linear() elif what == 'angular': self.make_dynamic_angular() else: raise ValueError('unknown mode: %r' % what) def make_dynamic_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()`.''' if not self.is_dynamic_linear(): # Regata a massa mass = self._getp('old_mass', None) if mass is None: raise RuntimeError('old mass is not available for recovery') else: self.mass = mass # Resgata a velocidade if self.vel == ORIGIN: self.vel = self._getp('old_vel', ORIGIN) self._clearp('old_mass', 'old_vel') def make_dynamic_angular(self): '''Resgata os parâmetros dinâmicos angulares de um objeto estático ou cinemático paralizado pelos métodos `obj.make_static()` ou `obj.make_kinematic()`.''' if not self.is_dynamic_angular(): # Regata a massa inertia = self._getp('old_inertia', None) if inertia is None: raise RuntimeError('old inertia is not available for recovery') else: self.inertia = inertia # Resgata a velocidade if self.omega == 0: self.omega = self._getp('old_omega', 0) self._clearp('old_inertia', 'old_omega') # Kinematic ############################################################### def is_kinematic(self, what=None): '''Retorna True se o objeto for cinemático ou nas variáveis lineares ou nas angulares. Um objeto é considerado cinemático (em uma das variáveis) se não for dinâmico. Se, além de cinemático, o objeto possuir velocidade nula, ele é considerado estático. Opcionalmente pode especificar um parâmetro posicional 'linear', 'angular', 'both' (padrão) or 'any' para determinar o tipo de consulta a ser realizada. ''' if what is None or what == 'both': return not (self.is_dynamic_linear() or self.is_dynamic_angular()) elif what == 'any': return (not self.is_dynamic_linear()) or ( not self.is_dynamic_angular()) elif what == 'linear': return not self.is_dynamic_linear() elif what == 'angular': return not self.is_dynamic_angular() else: raise ValueError('unknown mode: %r' % what) def is_kinematic_linear(self): '''Verifica se o objeto é dinâmico nas variáveis lineares''' return not self.is_dynamic_linear() def is_kinematic_angular(self): '''Verifica se o objeto é dinâmico nas variáveis angulares''' return not self.is_dynamic_angular() def make_kinematic(self, what=None): '''Define a massa e/ou inércia como infinito. Aceita um argumento com a mesma semântica de is_kinematic() ''' if what is None or what == 'both': self.make_kinematic_linear() self.make_kinematic_angular() elif what == 'linear': self.make_kinematic_linear() elif what == 'angular': self.make_kinematic_angular() else: raise ValueError('unknown mode: %r' % what) def make_kinematic_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()`.''' if not self.is_kinematic_linear(): self._setp('old_mass', self.mass) self.mass = 'inf' def make_kinematic_angular(self): '''Resgata os parâmetros dinâmicos angulares de um objeto estático ou cinemático paralizado pelos métodos `obj.make_static()` ou `obj.make_kinematic()`.''' if not self.is_kinematic_angular(): self._setp('old_inertia', self.inertia) self.inertia = 'inf' # Static ################################################################## def is_static(self, what=None): '''Retorna True se o objeto for estatático nas variáveis lineares e nas angulares. Um objeto é considerado estático (em uma das variáveis) se além de cinemático, a velocidade for nula. Opcionalmente pode especificar um parâmetro posicional 'linear', 'angular', 'both' (padrão) or 'any' para determinar o tipo de consulta a ser realizada''' if what is None or what == 'both': return self.is_static_linear() and self.is_static_angular() elif what == 'any': return self.is_static_linear() or self.is_static_angular() elif what == 'linear': return self.is_static_linear() elif what == 'angular': return self.is_static_angular() else: raise ValueError('unknown mode: %r' % what) def is_static_linear(self): '''Verifica se o objeto é dinâmico nas variáveis lineares''' return self.is_kinematic_linear() and self.vel == ORIGIN def is_static_angular(self): '''Verifica se o objeto é dinâmico nas variáveis angulares''' return self.is_kinematic_angular() and self.omega == 0 def make_static(self, what=None): '''Define a massa e/ou inércia como infinito. Aceita um argumento com a mesma semântica de is_static() ''' if what is None or what == 'both': self.make_static_linear() self.make_static_angular() elif what == 'linear': self.make_static_linear() elif what == 'angular': self.make_static_angular() else: raise ValueError('unknown mode: %r' % what) 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 != ORIGIN: self._setp('old_vel', self.vel) self.vel = (0, 0) def make_static_angular(self): '''Resgata os parâmetros dinâmicos angulares de um objeto estático ou cinemático paralizado pelos métodos `obj.make_static()` ou `obj.make_kinematic()`.''' self.make_kinematic_angular() if self.omega != 0: self._setp('old_omega', self.omega) self.omega = 0.0