M

#### Martin Manns

I am looking for a Python library for 2D collision checks of rotated

rectangles. Currently, I have found vizier 0.5b that is based on pygame.

Since I do not want to add a pygame dependency to my app, I replaced the

pygame.rect.Rect by a wxPython wx.Rect (see code below).

However, collision checks do not work correctly, i. e. identical rects

are not found to be colliding:

$ python

Python 2.6.6 (r266:84292, Nov 28 2010, 18:42:58)

[GCC 4.4.5] on linux2

Type "help", "copyright", "credits" or "license" for more information.

Is my replacement of the rectangle object wrong or is vizier not

working correctly with pygame as well?

Do you know of any other pure Python library for rotated rectangle

collision checks?

Cheers

Martin

----------------------------------------

from __future__ import division

import math

import wx

##############################################################################

cos_table = dict([(deg, math.cos(math.radians(deg))) for deg in xrange(360)])

sin_table = dict([(deg, math.sin(math.radians(deg))) for deg in xrange(360)])

class RotoRect(wx.Rect):

def __init__(self, *a, **kw):

self.deg = kw.pop('angle')

wx.Rect.__init__(self, *a, **kw)

# pygame rect compatibility for wx.Rect

def get_center(self):

return self.centerx, self.centery

def set_center(self, center):

self.centerx, self.centery = center

def get_centerx(self):

return self.x + self.width / 2

def set_centerx(self, centerx):

self.SetX(centerx - self.width / 2)

def get_centery(self):

return self.y + self.height / 2

def set_centery(self, centery):

self.SetY(centery - self.height / 2)

def get_topleft(self):

return self.GetTopLeft()

def set_topleft(self, p):

self.SetTopLeft(p)

def get_topright(self):

return self.GetTopRight()

def set_topright(self, p):

self.SetTopRight(p)

center = property(get_center, set_center)

centerx = property(get_centerx, set_centerx)

centery = property(get_centery, set_centery)

topleft = property(get_topleft, set_topleft)

topright = property(get_topright, set_topright)

# Now for the vizier code

def rotate(self, point, origin = 0):

"""Returns coords of point p rotated self.theta radians with

the rectangle around its center

"""

if not origin: origin = self.center

p_x = point[0]

p_y = point[1]

o_x = origin[0]

o_y = origin[1]

costheta = cos_table[self.deg]

sintheta = sin_table[self.deg]

return ((o_x + costheta * (p_x - o_x)) - (sintheta * (p_y - o_y)),

(o_y + sintheta * (p_x - o_x)) + (costheta * (p_y - o_y)))

def rotoxt(self, a, b):

"""Returns the y extremity xt of rect between self.rect"""

a_x = a[0]

a_y = a[1]

b_x = b[0]

b_y = b[1]

#calculate difference between self.left and b_x

dxl = self.left - b_x

#calculate difference between self.right and b_x

dxr = self.right - b_x

#if b_x isn't between self.left and self.right

if (dxl * dxr) > 0:

#if dxl < 1, b_x is on the right

if (dxl < 0):

#xt = (m * dxr) + b_y

xt = ((((b_y - (-a_y)) / (b_x - (-a_x))) * dxr) + b_y)

else:

#else b_x is on the left

#xt = (m * dxl) + b_y

xt = ((((b_y - a_y) / (b_x - a_x)) * dxl) + b_y)

return xt

else:

#else b_x is between self.left and self.right, and xt = b_y

return b_y

def rotocollide(self, rect):

"""Check for collision between self.rect and rect"""

#initialize collision to False

col = False

#transforming the plane to set rect's center to the origin

transplane = rect.center

#set rect's center to the origin

rect.center = (0, 0)

#transform self.rect's center by the transplane amount

self.center = (self.centerx - transplane[0],

self.centery - transplane[1])

#transforming the plane to set self.rect's theta to 0

transdeg = self.deg

#set self.rect's theta to 0

self.deg = 0

#transform rect's theta by the transtheta amount

rect.deg -= transdeg

#determine which vertice is min/max x/y NOTE:

# a = left/right, b = top/bottom

if (sin_table[rect.deg] * cos_table[rect.deg]) > 0:

#a = extreme left/right, b = extreme top/bottom

a, b = rect.topright, rect.topleft

else:

a, b = rect.topleft, rect.topright

#determine if a.x is min/max

if sin_table[rect.deg] < 0:

#ensure a is always max

a = -a[0], -a[1]

a_x = a[0]

negb = -b[0], -b[1]

#check that range of rect (-a.x, a.x) overlaps range of

#self.rect (self.left, self.right)

if (a_x >= self.left) and (self.right >= -a_x):

#get the first extremity

xt1 = self.rotoxt(a, b)

#get the other extermity

xt2 = self.rotoxt(a, negb)

#check for an intersection between the two extremities and

#self.rect's top/bottom

col = (((xt1 >= self.top) and (self.bottom >= xt2)) or

((xt2 >= self.top) and (self.bottom >= xt1)))

#retransform rect.center

rect.center = transplane

#retransform self.rect.center

self.center = (self.centerx + transplane[0],

self.centery + transplane[1])

#retransform self.theta

self.deg = transdeg

#retransform rect.theta

rect.deg += transdeg

#return results of collision test

return col

@property

def rotox(self):

return self.rotate(self.topleft)[0]

@property

def rotoy(self):

return self.rotate(self.topleft)[1]

@property

def rotoleft(self):

return self.rotate(self.left)

@property

def rotoright(self):

return self.rotate(self.right)

@property

def rototop(self):

return self.rotate(self.top)

@property

def rotobottom(self):

return self.rotate(self.bottom)

@property

def rototopleft(self):

return self.rotate(self.topleft)

@property

def rototopright(self):

return self.rotate(self.topright)

@property

def rotobottomright(self):

return self.rotate(self.bottomright)

@property

def rotobottomleft(self):

return self.rotate(self.bottomleft)

@property

def rotomidleft(self):

return self.rotate(self.midleft)

@property

def rotomidright(self):

return self.rotate(self.midright)

@property

def rotomidtop(self):

return self.rotate(self.midtop)

@property

def rotomidbottom(self):

return self.rotate(self.midbottom)