|
1 | 1 | __docformat__ = "reStructuredText"
|
2 | 2 |
|
| 3 | +import weakref |
3 | 4 | from typing import TYPE_CHECKING, List, Optional, Sequence, Tuple
|
4 | 5 |
|
5 | 6 | if TYPE_CHECKING:
|
@@ -42,18 +43,19 @@ class Shape(PickleMixin, TypingAttrMixing, object):
|
42 | 43 | _pickle_attrs_skip = PickleMixin._pickle_attrs_skip + ["mass", "density"]
|
43 | 44 |
|
44 | 45 | _space = None # Weak ref to the space holding this body (if any)
|
45 |
| - |
46 |
| - _id_counter = 1 |
| 46 | + _dead_ref = weakref.ref(set()) |
47 | 47 |
|
48 | 48 | def __init__(self, shape: "Shape") -> None:
|
49 | 49 | self._shape = shape
|
50 |
| - self._body: Optional["Body"] = shape.body |
| 50 | + self._body: weakref.ref = weakref.ref(shape.body) |
51 | 51 |
|
52 | 52 | def _init(self, body: Optional["Body"], _shape: ffi.CData) -> None:
|
53 |
| - self._body = body |
54 | 53 |
|
55 | 54 | if body is not None:
|
56 |
| - body._shapes.add(self) |
| 55 | + self._body = weakref.ref(body) |
| 56 | + body._shapes[self] = None |
| 57 | + else: |
| 58 | + self._body = Shape._dead_ref |
57 | 59 |
|
58 | 60 | def shapefree(cp_shape: ffi.CData) -> None:
|
59 | 61 | cp_space = cp.cpShapeGetSpace(cp_shape)
|
@@ -225,19 +227,25 @@ def _set_surface_velocity(self, surface_v: Tuple[float, float]) -> None:
|
225 | 227 |
|
226 | 228 | @property
|
227 | 229 | def body(self) -> Optional["Body"]:
|
228 |
| - """The body this shape is attached to. Can be set to None to |
229 |
| - indicate that this shape doesnt belong to a body.""" |
230 |
| - return self._body |
| 230 | + """The body this shape is attached to. |
| 231 | +
|
| 232 | + Can be set to None to indicate that this shape doesnt belong to a body. |
| 233 | + The shape only holds a weakref to the Body, meaning it wont prevent it |
| 234 | + from being GCed. |
| 235 | + """ |
| 236 | + return self._body() |
231 | 237 |
|
232 | 238 | @body.setter
|
233 | 239 | def body(self, body: Optional["Body"]) -> None:
|
234 |
| - if self._body is not None: |
235 |
| - self._body._shapes.remove(self) |
236 |
| - body_body = ffi.NULL if body is None else body._body |
237 |
| - cp.cpShapeSetBody(self._shape, body_body) |
| 240 | + if self.body is not None: |
| 241 | + del self.body._shapes[self] |
| 242 | + cp_body = ffi.NULL if body is None else body._body |
| 243 | + cp.cpShapeSetBody(self._shape, cp_body) |
238 | 244 | if body is not None:
|
239 |
| - body._shapes.add(self) |
240 |
| - self._body = body |
| 245 | + body._shapes[self] = None |
| 246 | + self._body = weakref.ref(body) |
| 247 | + else: |
| 248 | + self._body = Shape._dead_ref |
241 | 249 |
|
242 | 250 | def update(self, transform: Transform) -> BB:
|
243 | 251 | """Update, cache and return the bounding box of a shape with an
|
|
0 commit comments