pgfw/pgfw/extension.py

219 lines
7.4 KiB
Python

from random import randint, random
from math import sin, cos, atan2, radians, sqrt, pi
from pygame import Surface, PixelArray, Color
from pygame.mixer import get_num_channels, Channel
from pygame.locals import *
def get_step(start, end, speed):
x0, y0 = start
x1, y1 = end
angle = atan2(x1 - x0, y1 - y0)
return speed * sin(angle), speed * cos(angle)
def get_step_relative(start, end, step):
return get_step(start, end, get_distance(start, end) * step)
def get_angle(start, end, transpose=False):
angle = atan2(end[1] - start[1], end[0] - start[0])
if transpose:
angle = -angle - pi
return angle
def get_endpoint(start, angle, magnitude, translate_angle=True):
"""clockwise, 0 is up"""
x0, y0 = start
dx, dy = get_delta(angle, magnitude, translate_angle)
return x0 + dx, y0 + dy
def get_delta(angle, magnitude, translate_angle=True):
if translate_angle:
angle = radians(angle)
return sin(angle) * magnitude, -cos(angle) * magnitude
def reflect_angle(angle, wall):
return wall - angle
def rotate_2d(point, center, angle, translate_angle=True):
if translate_angle:
angle = radians(angle)
x, y = point
cx, cy = center
return cos(angle) * (x - cx) - sin(angle) * (y - cy) + cx, \
sin(angle) * (x - cx) + cos(angle) * (y - cy) + cy
def get_points_on_circle(center, radius, count, offset=0):
angle_step = 360.0 / count
points = []
current_angle = 0
for _ in xrange(count):
points.append(get_point_on_circle(center, radius,
current_angle + offset))
current_angle += angle_step
return points
def get_point_on_circle(center, radius, angle, translate_angle=True):
if translate_angle:
angle = radians(angle)
return center[0] + sin(angle) * radius, center[1] - cos(angle) * radius
def get_range_steps(start, end, count):
for ii in xrange(count):
yield start + (end - start) * ii / float(count - 1)
def get_distance(p0, p1):
return sqrt((p0[0] - p1[0]) ** 2 + (p0[1] - p1[1]) ** 2)
def place_in_rect(rect, incoming, contain=True, *args):
while True:
incoming.center = randint(0, rect.w), randint(0, rect.h)
if not contain or rect.contains(incoming):
collides = False
for inner in args:
if inner.colliderect(incoming):
collides = True
break
if not collides:
break
# from http://www.realtimerendering.com/resources/GraphicsGems/gemsii/xlines.c
def get_intersection(p0, p1, p2, p3):
x0, y0 = p0
x1, y1 = p1
x2, y2 = p2
x3, y3 = p3
a0 = y1 - y0
b0 = x0 - x1
c0 = x1 * y0 - x0 * y1
r2 = a0 * x2 + b0 * y2 + c0
r3 = a0 * x3 + b0 * y3 + c0
if r2 != 0 and r3 != 0 and r2 * r3 > 0:
return None
a1 = y3 - y2
b1 = x2 - x3
c1 = x3 * y2 - x2 * y3
r0 = a1 * x0 + b1 * y0 + c1
r1 = a1 * x1 + b1 * y1 + c1
if r0 != 0 and r1 != 0 and r0 * r1 > 0:
return None
denominator = a0 * b1 - a1 * b0
if denominator == 0:
return (x0 + x1 + x2 + x3) / 4, (y0 + y1 + y2 + y3) / 4
if denominator < 0:
offset = -denominator / 2
else:
offset = denominator / 2
numerator = b0 * c1 - b1 * c0
x = ((-1, 1)[numerator < 0] * offset + numerator) / denominator
numerator = a1 * c0 - a0 * c1
y = ((-1, 1)[numerator < 0] * offset + numerator) / denominator
return x, y
def collide_line_with_rect(rect, p0, p1):
for line in ((rect.topleft, rect.topright),
(rect.topright, rect.bottomright),
(rect.bottomright, rect.bottomleft),
(rect.bottomleft, rect.topleft)):
if get_intersection(p0, p1, *line):
return True
def get_random_number_in_range(start, end):
return random() * (end - start) + start
def get_value_in_range(start, end, position, reverse=False):
if reverse:
position = 1 - position
return (end - start) * position + start
def render_box(font, text, antialias, color, background=None, border=None,
border_width=1, padding=0):
surface = font.render(text, antialias, color, background)
if padding:
if isinstance(padding, int):
padding = [padding] * 2
padding = [x * 2 for x in padding]
rect = surface.get_rect()
padded_surface = Surface(rect.inflate(padding).size, SRCALPHA)
if background is not None:
padded_surface.fill(background)
rect.center = padded_surface.get_rect().center
padded_surface.blit(surface, rect)
surface = padded_surface
if border is not None:
if isinstance(border_width, int):
border_width = [border_width] * 2
border_width = [x * 2 for x in border_width]
rect = surface.get_rect()
bordered_surface = Surface(rect.inflate(border_width).size)
bordered_surface.fill(border)
rect.center = bordered_surface.get_rect().center
bordered_surface.blit(surface, rect)
surface = bordered_surface
return surface
def get_color_swapped_surface(surface, current, replacement):
swapped = surface.copy()
pixels = PixelArray(swapped)
pixels.replace(current, replacement)
del pixels
return swapped
def get_busy_channel_count():
count = 0
for index in xrange(get_num_channels()):
count += Channel(index).get_busy()
return count
def get_hue_shifted_surface(base, offset):
surface = base.copy()
pixels = PixelArray(surface)
color = Color(0, 0, 0)
for x in xrange(surface.get_width()):
for y in xrange(surface.get_height()):
h, s, l, a = Color(*surface.unmap_rgb(pixels[x][y])).hsla
if a:
color.hsla = (int(h) + offset) % 360, int(s), int(l), int(a)
pixels[x][y] = color
del pixels
return surface
def get_inverted_surface(base):
surface = base.copy()
pixels = PixelArray(surface)
for x in xrange(surface.get_width()):
for y in xrange(surface.get_height()):
color = Color(*surface.unmap_rgb(pixels[x][y]))
if color.hsla[3]:
color.r = 255 - color.r
color.g = 255 - color.g
color.b = 255 - color.b
pixels[x][y] = color
del pixels
return surface
def fill_tile(surface, tile):
for x in xrange(0, surface.get_width(), tile.get_width()):
for y in xrange(0, surface.get_height(), tile.get_height()):
surface.blit(tile, (x, y))
def get_shadowed_text(text, font, offset, color, antialias=True, shadow_color=(0, 0, 0),
colorkey=(255, 0, 255)):
foreground = font.render(text, antialias, color)
background = font.render(text, antialias, shadow_color)
alpha = SRCALPHA if antialias else 0
surface = Surface((foreground.get_width() + abs(offset[0]),
foreground.get_height() + abs(offset[1])), alpha)
if not antialias:
surface.set_colorkey(colorkey)
surface.fill(colorkey)
surface.blit(background, ((abs(offset[0]) + offset[0]) / 2,
(abs(offset[1]) + offset[1]) / 2))
surface.blit(foreground, ((abs(offset[0]) - offset[0]) / 2,
(abs(offset[1]) - offset[1]) / 2))
return surface
def get_hsla_color(hue, saturation=100, lightness=50, alpha=100):
color = Color(0, 0, 0, 0)
color.hsla = hue % 360, saturation, lightness, alpha
return color