Skip to content

Commit df54445

Browse files
committed
Change Space.shapes internally to behave as Space.bodies
1 parent 3f549e1 commit df54445

File tree

3 files changed

+18
-45
lines changed

3 files changed

+18
-45
lines changed

pymunk/shapes.py

+6-19
Original file line numberDiff line numberDiff line change
@@ -66,21 +66,8 @@ def shapefree(cp_shape: ffi.CData) -> None:
6666
cp.cpShapeFree(cp_shape)
6767

6868
self._shape = ffi.gc(_shape, shapefree)
69-
self._set_id()
70-
71-
@property
72-
def _id(self) -> int:
73-
"""Unique id of the Shape.
74-
75-
.. note::
76-
Experimental API. Likely to change in future major, minor orpoint
77-
releases.
78-
"""
79-
return int(ffi.cast("int", cp.cpShapeGetUserData(self._shape)))
80-
81-
def _set_id(self) -> None:
82-
cp.cpShapeSetUserData(self._shape, ffi.cast("cpDataPointer", Shape._id_counter))
83-
Shape._id_counter += 1
69+
self._h = ffi.new_handle(self) # to prevent GC of the handle
70+
cp.cpShapeSetUserData(self._shape, self._h)
8471

8572
@property
8673
def mass(self) -> float:
@@ -290,8 +277,8 @@ def point_query(self, p: Tuple[float, float]) -> PointQueryInfo:
290277
info = ffi.new("cpPointQueryInfo *")
291278
_ = cp.cpShapePointQuery(self._shape, p, info)
292279

293-
ud = int(ffi.cast("int", cp.cpShapeGetUserData(info.shape)))
294-
assert ud == self._id
280+
shape = ffi.from_handle(cp.cpShapeGetUserData(info.shape))
281+
assert shape == self, "This is a bug in Pymunk. Please report it."
295282
return PointQueryInfo(
296283
self,
297284
Vec2d(info.point.x, info.point.y),
@@ -311,8 +298,8 @@ def segment_query(
311298
info = ffi.new("cpSegmentQueryInfo *")
312299
r = cp.cpShapeSegmentQuery(self._shape, start, end, radius, info)
313300
if r:
314-
ud = int(ffi.cast("int", cp.cpShapeGetUserData(info.shape)))
315-
assert ud == self._id
301+
shape = ffi.from_handle(cp.cpShapeGetUserData(info.shape))
302+
assert shape == self, "This is a bug in Pymunk. Please report it."
316303
return SegmentQueryInfo(
317304
self,
318305
Vec2d(info.point.x, info.point.y),

pymunk/space.py

+12-22
Original file line numberDiff line numberDiff line change
@@ -132,9 +132,9 @@ def spacefree(cp_space: ffi.CData) -> None:
132132
) # To prevent the gc to collect the callbacks.
133133

134134
self._post_step_callbacks: Dict[Any, Callable[["Space"], None]] = {}
135-
self._removed_shapes: Dict[int, Shape] = {}
135+
self._removed_shapes: Dict[Shape, None] = {}
136136

137-
self._shapes: Dict[int, Shape] = {}
137+
self._shapes: Dict[Shape, None] = {}
138138
self._bodies: Dict[Body, None] = {}
139139
self._static_body: Optional[Body] = None
140140
self._constraints: Dict[Constraint, None] = {}
@@ -154,7 +154,7 @@ def shapes(self) -> List[Shape]:
154154
155155
(includes both static and non-static)
156156
"""
157-
return list(self._shapes.values())
157+
return list(self._shapes)
158158

159159
@property
160160
def bodies(self) -> List[Body]:
@@ -389,8 +389,7 @@ def remove(self, *objs: _AddableObjects) -> None:
389389

390390
def _add_shape(self, shape: "Shape") -> None:
391391
"""Adds a shape to the space"""
392-
# print("addshape", self._space, shape)
393-
assert shape._id not in self._shapes, "Shape already added to space."
392+
assert shape not in self._shapes, "Shape already added to space."
394393
assert (
395394
shape.space == None
396395
), "Shape already added to another space. A shape can only be in one space at a time."
@@ -400,7 +399,7 @@ def _add_shape(self, shape: "Shape") -> None:
400399
), "The shape's body must be added to the space before (or at the same time) as the shape."
401400

402401
shape._space = weakref.proxy(self)
403-
self._shapes[shape._id] = shape
402+
self._shapes[shape] = None
404403
cp.cpSpaceAddShape(self._space, shape._shape)
405404

406405
def _add_body(self, body: "Body") -> None:
@@ -427,13 +426,13 @@ def _add_constraint(self, constraint: "Constraint") -> None:
427426

428427
def _remove_shape(self, shape: "Shape") -> None:
429428
"""Removes a shape from the space"""
430-
assert shape._id in self._shapes, "shape not in space, already removed?"
431-
self._removed_shapes[shape._id] = shape
429+
assert shape in self._shapes, "shape not in space, already removed?"
430+
self._removed_shapes[shape] = None
432431
shape._space = None
433432
# During GC at program exit sometimes the shape might already be removed. Then skip this step.
434433
if cp.cpSpaceContainsShape(self._space, shape._shape):
435434
cp.cpSpaceRemoveShape(self._space, shape._shape)
436-
del self._shapes[shape._id]
435+
del self._shapes[shape]
437436

438437
def _remove_body(self, body: "Body") -> None:
439438
"""Removes a body from the space"""
@@ -517,7 +516,7 @@ def use_spatial_hash(self, dim: float, count: int) -> None:
517516
the shape to be inserted into many cells, setting it too low will
518517
cause too many objects into the same hash slot.
519518
520-
count is the suggested minimum number of cells in the hash table. If
519+
count is the minimum number of cells in the hash table. If
521520
there are too few cells, the spatial hash will return many false
522521
positives. Too many cells will be hard on the cache and waste memory.
523522
Setting count to ~10x the number of objects in the space is probably a
@@ -563,7 +562,7 @@ def step(self, dt: float) -> None:
563562
cp.cpHastySpaceStep(self._space, dt)
564563
else:
565564
cp.cpSpaceStep(self._space, dt)
566-
self._removed_shapes = {}
565+
self._removed_shapes.clear()
567566
finally:
568567
self._locked = False
569568
self.add(*self._add_later)
@@ -575,7 +574,7 @@ def step(self, dt: float) -> None:
575574
for key in self._post_step_callbacks:
576575
self._post_step_callbacks[key](self)
577576

578-
self._post_step_callbacks = {}
577+
self._post_step_callbacks.clear()
579578

580579
def add_collision_handler(
581580
self, collision_type_a: int, collision_type_b: int
@@ -745,16 +744,7 @@ def point_query(
745744
def _get_shape(self, _shape: Any) -> Optional[Shape]:
746745
if not bool(_shape):
747746
return None
748-
749-
shapeid = int(ffi.cast("int", cp.cpShapeGetUserData(_shape)))
750-
# return self._shapes[hashid_private]
751-
752-
if shapeid in self._shapes:
753-
return self._shapes[shapeid]
754-
elif shapeid in self._removed_shapes:
755-
return self._removed_shapes[shapeid]
756-
else:
757-
return None
747+
return ffi.from_handle(cp.cpShapeGetUserData(_shape))
758748

759749
def point_query_nearest(
760750
self, point: Tuple[float, float], max_distance: float, shape_filter: ShapeFilter

pymunk/tests/test_shape.py

-4
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,6 @@
66

77

88
class UnitTestShape(unittest.TestCase):
9-
def testId(self) -> None:
10-
c = p.Circle(None, 4)
11-
self.assertGreater(c._id, 0)
12-
139
def testPointQuery(self) -> None:
1410
b = p.Body(10, 10)
1511
c = p.Circle(b, 5)

0 commit comments

Comments
 (0)