def circle_aabb(A, B, collision_class=Collision): cx, cx_, cy, cy_ = A.rect_coords ax, ax_, ay, ay_ = B.rect_coords dx = min(cx_, ax_) - max(cx, ax) dy = min(cy_, ay_) - max(cy, ay) if dy < 0 or dx < 0: return None x, y = pos = A.pos if dx < dy: # Circle is right at the bottom/top of AABB if ay <= y <= ay_: left = x < ax pos = Vec2(ax if left else ax_, y) normal = Vec2(1 if left else -1, 0) delta = dx / 2 return collision_class(A, B, pos=pos, normal=normal, delta=delta) else: # Circle is right at the bottom/top of AABB if ax <= x <= ax_: bottom = y < ay pos = Vec2(x, ay if bottom else ay_) normal = Vec2(0, 1 if bottom else -1) delta = dy / 2 return collision_class(A, B, pos=pos, normal=normal, delta=delta) # If we reached this point, the circle encountered the AABB at a vertex. # We must find the vertex that is closest to the center and define the # normal vector using it. vertex = min(B.vertices, key=lambda v: abs(v - pos)) normal = (vertex - pos).normalize() delta = A.radius - (pos - vertex).norm() return collision_class(A, B, pos=vertex, normal=normal, delta=delta)
def _vertices(self, N, pos): length = self.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)]
def gravity(self, value): owns_prop = flags.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 gravity_changed_signal.trigger(self, old, self._gravity)
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, obj, k, pos=(0, 0), register=False): self._k = k kxy = 0 x0, y0 = self._pos = Vec2(*pos) 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.0 super(SpringTensor, self).__init__(obj, F, U, register=register)
def __init__(self, vertices, pos=None, vel=(0, 0), theta=0.0, omega=0.0, mass=None, density=None, inertia=None, **kwargs): if isinstance(vertices, int): raise ValueError('must pass a list of vertices. ' 'Use RegularPoly to define a polygon by the ' 'number of vertices') 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 vertices self._cache_theta = None self._cache_rvertices_last = None self._cache_rbbox_last = None super(Poly, self).__init__(pos_cm, vel, theta, omega, mass=mass, density=density, inertia=inertia, cbb_radius=max(v.norm() for v in vertices), **kwargs) self.num_sides = len(vertices) self._normals_idxs = self.get_li_indexes() self.num_normals = len(self._normals_idxs or self.vertices) # Slightly faster when all normals are linear dependent: if # self._normals_idx = None, all normals are recomputed at each frame. if self.num_normals == self.num_sides: self._normals_idxs = None # Move to specified position, if pos is given. if pos is not None: self._pos = Vec2(*pos)
def runner(self, dt, target, **kwds): time = 0 interval = self.interval position = self.trajectory pos_shift = Vec2(0, 0) if not self.relative else target.pos while time < interval: time += dt target.pos = pos_shift + position(time)
def momentumP(self): """ Linear momentum. """ try: return self._vel / self._invmass except ZeroDivisionError: return Vec2(float('inf'), float('inf'))
def fast_func(t): F = null2D for k, func in nmuls: Fi = asvector(func(t)) try: F += Fi * k except TypeError: x, y = Fi F += Vec2(k * x, k * y) return F
def get_normal(self, i): """ Normal unity vector to the ith side. Each segment goes from point i to i + 1. """ points = self.vertices x, y = points[(i + 1) % self.num_sides] - points[i] return Vec2(y, -x).normalize()
def orientation(self, theta=0.0): """ Return an unitary vector in the direction the object is oriented. If the rotation is zero, the orientation vector is Vec2(1, 0). Rotation is applied accordingly. """ theta += self._theta return Vec2(self._cos(theta), self._sin(theta))
def fast_func(t): F = null2D for scalar_func, func in fmuls: Fi = func(t) k = scalar_func(t) try: F += Fi * k except TypeError: x, y = Fi F += Vec2(k * x, k * y) return F
def collision_aabb(A, B): # Detects collision using bounding box shadows. x0, x1 = max(A.xmin, B.xmin), min(A.xmax, B.xmax) y0, y1 = max(A.ymin, B.ymin), min(A.ymax, B.ymax) dx = x1 - x0 dy = y1 - y0 if x1 < x0 or y1 < y0: return None # Chose collision center as the center point in the intersection pos = Vec2((x1 + x0) / 2, (y1 + y0) / 2) # Normal is the direction with smallest penetration if dy < dx: delta = dy normal = Vec2(0, (1 if A.pos.y < B.pos.y else -1)) else: delta = dx normal = Vec2((1 if A.pos.x < B.pos.x else -1), 0) return Collision(A, B, pos=pos, normal=normal, delta=delta)
def get_normals(self): """ List of linearly independent normals. """ 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]
def momentumL(self, *args): """ Angular momentum. Angular momentum depends on a reference point. By default, we use the object's center of mass. One can pass any coordinate to specify the reference. Example: Consider the object >>> b1 = Body(pos=(0, 1), vel=(1, 0), mass=2) Its center of mass angualar momentum is zero since it has no angular velocity >>> b1.momentumL() 0.0 However, linear movement creates an angular moment around origin or other reference points >>> b1.momentumL(0, 0) -2.0 >>> b1.momentumL(0, 2) 2.0 """ if not args: momentumL = None else: if len(args) == 1: vec = args[0] else: vec = Vec2(*args) delta_pos = self.pos - vec momentumL = delta_pos.cross(self.momentumP()) if self._invinertia: return momentumL + self.omega * self.inertia else: return momentumL
def __init__(self, obj, G, M=None, epsilon=0, pos=(0, 0), register=False): if M is None: M = obj.mass self._M = M = float(M) self._epsilon = epsilon = float(epsilon) self._G = G = float(G) self._pos = pos = Vec2(*pos) def F(R): R = pos - R r = R.norm() r3 = (r + epsilon)**2 * r R *= G * obj.mass * M / r3 return R def U(R): R = (R - pos).norm() return -self._G * obj.mass * M / (R + epsilon) super(Gravity, self).__init__(obj, F, U, register=register)
def random(self, *args): """ Return random position in the visible screen. Signature: ``pos.random(size)``: create objects within screen and inside a margin with the given number of pixels. ``pos.random(margin_x, margin_y)``: specify margin in x and y directions. ``pos.random(margin_left, margin_bottom, margin_right, margin_top)``: specify each margin separately """ x0 = y0 = 0 x1, y1 = self._shape if len(args) == 1: margin = args[0] x0 += margin x1 -= margin y0 += margin y1 -= margin elif len(args) == 2: dx, dy = args x0 += dx x1 -= dx y0 += dy y1 -= dy elif len(args) == 4: x0 += args[0] y0 += args[1] x1 -= args[2] y1 -= args[3] elif len(args) != 0: raise TypeError('must be called with 0, 1, 2, or 4 arguments') return Vec2(random.uniform(x0, x1), random.uniform(y0, y1)) + self._origin
def vy(self, value): self._vel = Vec2(self._vel.x, value)
def runner(self, dt, target, **kwds): n_steps = dt / self.duration delta = self.step / n_steps for _ in range(n_steps): target.pos += Vec2.frompolar(delta, target.theta)
def __init__(self, pos, interval): super(move_to, self).__init__(interval) self.pos = Vec2(*pos)
def test_move_functions_signature(self, obj): for args in [[Vec2(1, 2)], [(1, 2)], (1, 2)]: obj.move(*args) obj.imove(*args) obj.boost(*args) obj.move_to(*args)
def move_tip(self, pos): '''Move a ponta para a nova posição pos''' self._pos = self._current[-1] = Vec2(*pos)
def vx(self, value): self._vel = Vec2(value, self._vel.y)
def _origin(self): return Vec2(0, 0)
def F(R): Dx = x0 - R.x Dy = y0 - R.y return Vec2(kx * Dx + kxy * Dy, ky * Dy + kxy * Dx)
def vel(self): return Vec2(1, 0)
def gravity(self, value): try: self._gravity = Vec2(*value) except TypeError: self._gravity = Vec2(0, -value) self.owns_gravity = True
def __init__(self, x, y=None): if y is None: self.pos = Vec2(*x) else: self.pos = Vec2(x, y)
def F(rA, rB): dx, dy = rB - rA return Vec2(kx * dx + kxy * dy, +ky * dy + kxy * dx)
def from_pos(self, x, y): w, h = self._shape x0, y0 = self._origin return Vec2(x0 + a * w + x, y0 + b * h + y)