mirror of
https://github.com/bendtherules/pyFun.git
synced 2026-08-18 13:52:46 +00:00
summer update
This commit is contained in:
+298
@@ -0,0 +1,298 @@
|
||||
import pygame
|
||||
from math_utils import distance
|
||||
import math
|
||||
import base
|
||||
from Circle import Circle
|
||||
|
||||
# start of Rotated_Rect class
|
||||
|
||||
|
||||
class Rotated_Rect(pygame.Rect):
|
||||
# make a metaclass which wraps a lot of functions just like move is defined below.
|
||||
# functions to be wrapped - move,inflate
|
||||
# Think about them - clamp,clip,union,fit,contains,collide
|
||||
# the metaclass can probably also change the __str__ of the class.
|
||||
# About drawing - use pygame.transform.rotate to get a rotated surface to
|
||||
# be drawed.
|
||||
|
||||
def __init__(self, leftTop_or_center, w_h, angle, centered_coords=False, color=None):
|
||||
''' Angle in degrees.
|
||||
leftTop_or_center, w_h -> both are 2-element tuples or list
|
||||
centered_coords -> whether the (left,top) is actually intended to be (center_x,center_y) or not.
|
||||
I.e. if centered_coords==True the (left,top) co-ordinates are used as (center_x,center_y) co-ordinates of the Rotated_Rect'''
|
||||
self.angle = angle
|
||||
self.color = color
|
||||
try:
|
||||
left = leftTop_or_center[0]
|
||||
top = leftTop_or_center[1]
|
||||
except:
|
||||
raise AttributeError(
|
||||
"leftTop_or_center must be of format (left,top)")
|
||||
try:
|
||||
width = w_h[0]
|
||||
height = w_h[1]
|
||||
except:
|
||||
raise AttributeError("w_h must be of format (width,height)")
|
||||
|
||||
if centered_coords:
|
||||
left = left - width / 2
|
||||
top = top - height / 2
|
||||
super(Rotated_Rect, self).__init__((left, top), (width, height))
|
||||
|
||||
def __copy__(self):
|
||||
return Rotated_Rect((self.left, self.top), (self.width, self.height), self.angle)
|
||||
|
||||
def __repr__(self):
|
||||
return "{0.__class__.__name__}((top={0.top}, left={0.left}), (width={0.width}, height={0.height}), angle={0.angle}, color={0.color})".format(self)
|
||||
|
||||
@classmethod
|
||||
def create_from_rect(cls, another_rect, angle):
|
||||
return cls((another_rect.left, another_rect.top), (another_rect.width, another_rect.height), angle)
|
||||
|
||||
def get_rect(self):
|
||||
return pygame.Rect((self.left, self.top), (self.width, self.height))
|
||||
|
||||
# most in_place versions retained from super-class
|
||||
|
||||
def move(self, x, y):
|
||||
return self.__copy__().move_ip(x, y)
|
||||
|
||||
def inflate(self, x, y):
|
||||
return self.__copy__().inflate_ip(x, y)
|
||||
|
||||
def normalize(self):
|
||||
# not sure what this one does - ask others
|
||||
return self.__copy__().normalize_ip()
|
||||
|
||||
def rotate_ip(self, some_angle):
|
||||
self.angle += some_angle
|
||||
|
||||
def rotate(self, some_angle):
|
||||
return self.__copy__().rotate_ip(some_angle)
|
||||
|
||||
# generalise the parameters to accept x1,y1,x2,y2,rot_angle
|
||||
def rotate_point_about_center(self, x, y, theta=None):
|
||||
''' x,y -> Absolute co-ords of the point to be rotated wrt to the rectangles center.
|
||||
self.centerx,self.centery are internally substracted from them.
|
||||
theta -> angle (in degrees) by which the point is to be rotated. Default value is self.angle.
|
||||
Returns absolute positions of the points after rotation. '''
|
||||
if theta is None:
|
||||
theta = self.angle
|
||||
return self.rotate_point(x, y, self.centerx, self.centery, theta)
|
||||
|
||||
@staticmethod
|
||||
# to be allowed as rotate_point(class)
|
||||
def rotate_point(x, y, origin_x, origin_y, theta):
|
||||
''' x,y -> Absolute co-ords of the point to be rotated. origin_x,origin_y are internally substracted from them.
|
||||
(origin_x,origin_y) -> Absolute co-ords of the origin wrt to which the point will be rotated
|
||||
theta -> angle (in degrees) by which the point is to be rotated.
|
||||
Returns absolute positions of the points after rotation. '''
|
||||
import math
|
||||
x_diff = x - origin_x
|
||||
y_diff = y - origin_y
|
||||
# -y_diff because of the inverted nature of y co-ordinate system in pygame
|
||||
y_diff = -y_diff
|
||||
r = math.sqrt(x_diff ** 2 + y_diff ** 2)
|
||||
initial_angle = math.atan2(y_diff, x_diff)
|
||||
final_angle = initial_angle + math.radians(theta)
|
||||
rotated_x_diff = r * math.cos(final_angle)
|
||||
rotated_y_diff = r * math.sin(final_angle)
|
||||
rotated_y_diff = -rotated_y_diff # - used for same reason as above
|
||||
rotated_x = rotated_x_diff + origin_x
|
||||
rotated_y = rotated_y_diff + origin_y
|
||||
return (rotated_x, rotated_y)
|
||||
|
||||
def rotate_point_polar(self, r, initial_angle, theta=None):
|
||||
''' theta and initial_angle in radians.
|
||||
initial_angle ->angle r makes with horizontal.
|
||||
theta -> angle to be rotated by. '''
|
||||
if theta is None:
|
||||
theta = self.angle
|
||||
final_angle = initial_angle + theta
|
||||
rotated_x = r * math.cos(final_angle)
|
||||
rotated_y = r * math.sin(final_angle)
|
||||
return (rotated_x, rotated_y)
|
||||
|
||||
@property
|
||||
def bottomleft_rotated(self):
|
||||
temp = self.bottomleft
|
||||
return self.rotate_point_about_center(temp[0], temp[1])
|
||||
|
||||
@property
|
||||
def topleft_rotated(self):
|
||||
temp = self.topleft
|
||||
return self.rotate_point_about_center(temp[0], temp[1])
|
||||
|
||||
@property
|
||||
def bottomright_rotated(self):
|
||||
temp = self.bottomright
|
||||
return self.rotate_point_about_center(temp[0], temp[1])
|
||||
|
||||
@property
|
||||
def topright_rotated(self):
|
||||
temp = self.topright
|
||||
return self.rotate_point_about_center(temp[0], temp[1])
|
||||
|
||||
@property
|
||||
def midleft_rotated(self):
|
||||
temp = self.midleft
|
||||
return self.rotate_point_about_center(temp[0], temp[1])
|
||||
|
||||
@property
|
||||
def midright_rotated(self):
|
||||
temp = self.midright
|
||||
return self.rotate_point_about_center(temp[0], temp[1])
|
||||
|
||||
@property
|
||||
def midtop_rotated(self):
|
||||
temp = self.midtop
|
||||
return self.rotate_point_about_center(temp[0], temp[1])
|
||||
|
||||
@property
|
||||
def midbottom_rotated(self):
|
||||
temp = self.midbottom
|
||||
return self.rotate_point_about_center(temp[0], temp[1])
|
||||
|
||||
# related to bounding box
|
||||
@property
|
||||
def bbox(self):
|
||||
list_x = [self.topleft_rotated[0], self.topright_rotated[0],
|
||||
self.bottomleft_rotated[0], self.bottomright_rotated[0]]
|
||||
list_y = [self.topleft_rotated[1], self.topright_rotated[1],
|
||||
self.bottomleft_rotated[1], self.bottomright_rotated[1]]
|
||||
min_x = math.floor(min(list_x))
|
||||
max_x = math.ceil(max(list_x))
|
||||
min_y = math.floor(min(list_y))
|
||||
max_y = math.ceil(max(list_y))
|
||||
return pygame.Rect((min_x, min_y), (max_x - min_x, max_y - min_y))
|
||||
# corners
|
||||
|
||||
@property
|
||||
def corners(self):
|
||||
''' List of actual (rotated) corners. '''
|
||||
return [self.topleft_rotated, self.topright_rotated, self.bottomright_rotated, self.bottomleft_rotated]
|
||||
|
||||
@property
|
||||
def corners_relative(self):
|
||||
return [(temp[0] - self.centerx, temp[1] - self.centery) for temp in self.corners]
|
||||
|
||||
# circles related to the rect
|
||||
def get_circumcircle(self):
|
||||
Circle.get_circumcircle_from_rect(self.bbox)
|
||||
|
||||
def get_incircle(self):
|
||||
# get_rect used as incircle doesnt depend upon theta, same for rot and
|
||||
# non-rot rect
|
||||
Circle.get_incircle_from_rect(self.get_rect())
|
||||
|
||||
# collision events
|
||||
def collide_point(self, *args):
|
||||
''' collidepoint(x,y) ->
|
||||
collidepoint((x,y)) -> '''
|
||||
temp_len = len(args)
|
||||
temp_x = None
|
||||
temp_y = None
|
||||
if temp_len == 2:
|
||||
temp_x = args[0]
|
||||
temp_y = args[1]
|
||||
elif temp_len == 1:
|
||||
if len(args[0]) == 2:
|
||||
temp_x, temp_y = args[0]
|
||||
else:
|
||||
raise TypeError("Arguments are of incorrect type")
|
||||
else:
|
||||
raise TypeError("Arguments are of incorrect type")
|
||||
if temp_x is None or temp_y is None:
|
||||
raise TypeError("argument must contain two numbers")
|
||||
return self.get_rect().collidepoint(self.rotate_point_about_center(temp_x, temp_y, -self.angle))
|
||||
|
||||
def collide_rect(self, rect):
|
||||
''' Tests if it collides with rect, which should be instance of rect class. '''
|
||||
# First test:whether bbox collides with rect. If not, no collision.
|
||||
if not self.bbox.colliderect(rect):
|
||||
return False
|
||||
else:
|
||||
# Todo: Else sure test: Need to do line class first. Test if any line
|
||||
# of titedRect collides with any line of (instead, the whole) rect.
|
||||
pass
|
||||
|
||||
# draw functions
|
||||
def blit(self, surface, (x, y)=(None, None), color=None, clip_rect=None, width=1, use_antialiasing=False, blend=True):
|
||||
''' Draws this Circle on surface. It must be of width and height greater than or equal to its bbox.
|
||||
(x,y)->co-ordinates whether the center of the Rotated_Rect should be drawn on the surface.
|
||||
width argument doesnt work with antialiasing. width=0 makes it filled.
|
||||
antialiasing may break in later versions as the backend pygame.gfxdraw is experimental.
|
||||
color falls back first to Circle.color (if present) and then to blue color.'''
|
||||
if not ((x, y) == (None, None)):
|
||||
sprite_to_draw = self.__copy__()
|
||||
sprite_to_draw.center = (x, y)
|
||||
else:
|
||||
sprite_to_draw = self
|
||||
# pygame.Rect does this, which is used in clear_rect
|
||||
x, y = int(x), int(y)
|
||||
if not color:
|
||||
if self.color:
|
||||
color = self.color
|
||||
else:
|
||||
color = pygame.Color("blue")
|
||||
|
||||
if clip_rect:
|
||||
# Set clip region
|
||||
surface.set_clip(clip_rect)
|
||||
|
||||
iter_corners = [(temp[0] + x, temp[1] + y)
|
||||
for temp in sprite_to_draw.corners_relative]
|
||||
if not use_antialiasing:
|
||||
pygame.draw.polygon(surface, color, iter_corners, width)
|
||||
else:
|
||||
pygame.draw.aalines(surface, color, True, iter_corners, blend)
|
||||
|
||||
if clip_rect:
|
||||
# Remove clip region
|
||||
surface.set_clip(None)
|
||||
|
||||
def draw(self):
|
||||
tmp_dirty_sprite = {
|
||||
"sprite": self,
|
||||
"shape": "Rotated_Rect",
|
||||
"bbox": self.bbox,
|
||||
"config": {
|
||||
"leftTop_or_center": (self.left, self.top),
|
||||
"w_h": (self.w, self.h),
|
||||
"angle": self.angle,
|
||||
"color": self.color
|
||||
}
|
||||
}
|
||||
for tmp_old_sprite in self.container_obj.list_rendered_sprite:
|
||||
# if any of them is eqv. to new dirty sprite, mark old one as
|
||||
# still_valid
|
||||
if tmp_old_sprite == tmp_dirty_sprite:
|
||||
# puts "still_valid" entry in the old_dirty_sprite_dict
|
||||
tmp_old_sprite["still_valid"] = True
|
||||
break
|
||||
else:
|
||||
# i.e. if no match found, list it as dirty sprite
|
||||
self.container_obj.list_dirty_sprite.append(tmp_dirty_sprite)
|
||||
|
||||
def get_surface(self, surface=None, flags=0):
|
||||
''' Returns a new surface with dimensions of (Rotated_Rect.bbox.width,Rotated_Rect.bbox.width) and other properties as surface.
|
||||
This Rotated_Rect can be safely drawn on it.'''
|
||||
if surface:
|
||||
return pygame.Surface((self.bbox.width, self.bbox.height), flags, surface)
|
||||
else:
|
||||
return pygame.Surface((self.bbox.width, self.bbox.height), flags)
|
||||
|
||||
def get_surface_drawn(self, (x, y)=(None, None), color=None, width=1, use_antialiasing=False, blend=True, surface=None, flags=0):
|
||||
''' Returns a surface which has the Rotated_Rect drwan on it. '''
|
||||
temp_surf = self.get_surface(surface, flags)
|
||||
self.draw(temp_surf, (x, y), color, width, use_antialiasing, blend)
|
||||
return temp_surf
|
||||
|
||||
def collide_Rotated_Rect(self, another_Rotated_Rect):
|
||||
'''Checks whether this Rotated_Rect collides with another_titltedrect, which must be a instance of titledRect. '''
|
||||
# Rotates both Rotated_Rect by -another_Rotated_Rect.angle. So, another_Rotated_Rect becomes a Rect called temp_Rect.
|
||||
# Then check if the rotated version of this Rotated_Rect
|
||||
# (temp_Rotated_Rect) collides with the obtained Rect.
|
||||
temp_Rotated_Rect = self.rotate(-another_Rotated_Rect.angle)
|
||||
temp_Rect = another_Rotated_Rect.get_rect()
|
||||
return temp_Rotated_Rect.colliderect(temp_Rect)
|
||||
Reference in New Issue
Block a user