File: collide.py

package info (click to toggle)
bouncy 0.6.20071104-6
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 1,924 kB
  • sloc: python: 4,672; makefile: 2; sh: 2
file content (176 lines) | stat: -rw-r--r-- 5,488 bytes parent folder | download | duplicates (7)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
import euclid

from OpenGL.GL import *
from OpenGL.GLUT import *

XPOS = euclid.Vector3(1., 0, 0)
XNEG = euclid.Vector3(-1., 0, 0)
YPOS = euclid.Vector3(0, 1., 0)
YNEG = euclid.Vector3(0, -1., 0)
ZPOS = euclid.Vector3(0, 0, 1.)
ZNEG = euclid.Vector3(0, 0, -1.)

class AABox(object):
    '''Axis-Aligned Box centered on "center" with dimensions x, y, z'''
    def __init__(self, c, dx, dy, dz):
        self.dx, self.dy, self.dz = dx, dy, dz
        self.c = c

    @classmethod
    def from_extents(cls, minx, maxx, miny, maxy, minz, maxz):
        '''Return an AABox created from minx, max, miny, maxy, minz and 
        maxz dimensions.
        '''
        dx = (maxx - minx) * 0.5
        dy = (maxy - miny) * 0.5
        dz = (maxz - minz) * 0.5
        c = (minx + self.dx, miny + self.dy, minz + self.dz)
        return cls(c, dx, dy, dz)


    def __add__(self, b):
        '''Adds another bounding box volume, the new box covers both volumes.
        '''
        extents = (min(self.minx, b.minx), max(self.maxx, b.maxx),
          min(self.miny, b.miny), max(self.maxy, b.maxy),
          min(self.minz, b.minz), max(self.maxz, b.maxz))
        return self.from_extents(*extents)

    def get_c(self):
        '''Just return the value.'''
        return self.__c

    def set_c(self, c):
        '''Set the extents based on the center value and our dimensions.
        '''
        if isinstance(c, euclid.Point3):
            pass
        elif isinstance(c, euclid.Vector3):
            c = euclid.Point3(c.x, c.y, c.z)
        else:
            c = euclid.Point3(*c)
        c = self.__c = c

        x = (c.x - self.dx/2, c.x + self.dx/2)
        self.xmin = min(x)
        self.xmax = max(x)

        y = (c.y - self.dy/2, c.y + self.dy/2)
        self.ymin = min(y)
        self.ymax = max(y)

        z = (c.z - self.dz/2, c.z + self.dz/2)
        self.zmin = min(z)
        self.zmax = max(z)

    c = property(get_c, set_c, None)

    def __repr__(self):
        a = tuple(self.__c) + (self.dx, self.dy, self.dz)
        return '<AABox c=%s,%s,%s d=%s,%s,%s>'%a

    def is_intersecting(self, other):
        '''Does the other thing (currently just AABox and euclid.Point3)
        intersect this box?
        '''
        if isinstance(other, euclid.Point3):
            return (self.xmin < other.x < self.xmax and
                    self.ymin < other.y < self.ymax and
                    self.zmin < other.z < self.zmax)
        elif isinstance(other, AABox):
            c = other.c
            hdx, hdy, hdz = other.dx/2, other.dy/2, other.dz/2
            return (self.xmin-hdx < c.x < self.xmax+hdx and
                self.ymin-hdy < c.y < self.ymax+hdy and
                self.zmin-hdz < c.z < self.zmax+hdz)
        else:
            raise NotImplementedError

    def resolve_collision(self, other):
        '''An AABox has been determined to have collided with this AABox
        by is_intersecting() above. This method is to be called with the
        AABox in its non-intersecting state, and will move it as far as
        it can such that it touches this AABox (which is stationary).

        Returns (other AABox new center postion, velocity modifier)

        Velocity modifier indicates which axis we modified and thus
        which velocity axis should be zeroed.
        '''
        if not isinstance(other, AABox): raise NotImplementedError
        c = other.c

        # test from middle of closest edges
        s = self.__c
        sx = s.x; cx = c.x
        if other.c.x > self.c.x:
            s.x = self.xmax
            c.x = other.xmin
            v = (c - s).normalize()
            x = v.dot(XPOS)
        else:
            s.x = self.xmin
            c.x = other.xmax
            v = (c - s).normalize()
            x = v.dot(XNEG)
        s.x = sx; c.x = cx
        sy = s.y; cy = c.y
        if other.c.y > self.c.y:
            s.y = self.ymax
            c.y = other.ymin
            v = (c - s).normalize()
            y = v.dot(YPOS)
        else:
            s.y = self.ymin
            c.y = other.ymax
            v = (c - s).normalize()
            y = v.dot(YNEG)
        s.y = sy; c.y = cy
        sz = s.z; cz = c.z
        if other.c.z < self.c.z:
            s.z = self.zmax
            c.z = other.zmin
            v = (c - s).normalize()
            z = v.dot(ZPOS)
        else:
            s.z = self.zmin
            c.z = other.zmax
            v = (c - s).normalize()
            z = v.dot(ZNEG)
        s.z = sz; c.z = cz

        # now see which axis has the dot product closest to 1
        if y > x and y > z:
            c.y = self.ymax + other.dy/2
            vmod = euclid.Vector3(1, 0, 1)
        elif x > z:
            hdx = other.dx/2
            if c.x > self.c.x: c.x = self.xmax + hdx
            else: c.x = self.xmin - hdx
            vmod = euclid.Vector3(0, 1, 1)
        else:
            hdz = other.dz/2
            if c.z > self.c.z: c.z = self.zmax + hdz
            else: c.z = self.zmin - hdz
            vmod = euclid.Vector3(1, 1, 0)

        return c, vmod

    def render(self):
        glPushMatrix()
        glTranslate(self.__c.x, self.__c.y, self.__c.z)
        glScalef(self.dx, self.dy, self.dz)
        glColor(1, .5, .5)
        glutWireCube(1)
        glPopMatrix()


class Sphere(euclid.Sphere):
    def render(self):
        glPushMatrix()
        glTranslate(self.c.x, self.c.y, self.c.z)
        glColor(1, 1, 1)
        glutWireSphere(self.r, 8, 8)
        glPopMatrix()