Skip to content

Commit 1b4b88e

Browse files
committed
WIP Removed data from coll callbacks, made CollisionHandler private
1 parent 3686ff6 commit 1b4b88e

File tree

5 files changed

+114
-120
lines changed

5 files changed

+114
-120
lines changed

pymunk/_callbacks.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -203,23 +203,23 @@ def ext_cpCollisionBeginFunc(
203203
_arb: ffi.CData, _space: ffi.CData, data: ffi.CData
204204
) -> None:
205205
handler = ffi.from_handle(data)
206-
handler._begin(Arbiter(_arb, handler._space), handler._space, handler.data)
206+
handler._begin(Arbiter(_arb, handler._space), handler._space)
207207

208208

209209
@ffi.def_extern()
210210
def ext_cpCollisionPreSolveFunc(
211211
_arb: ffi.CData, _space: ffi.CData, data: ffi.CData
212212
) -> None:
213213
handler = ffi.from_handle(data)
214-
handler._pre_solve(Arbiter(_arb, handler._space), handler._space, handler.data)
214+
handler._pre_solve(Arbiter(_arb, handler._space), handler._space)
215215

216216

217217
@ffi.def_extern()
218218
def ext_cpCollisionPostSolveFunc(
219219
_arb: ffi.CData, _space: ffi.CData, data: ffi.CData
220220
) -> None:
221221
handler = ffi.from_handle(data)
222-
handler._post_solve(Arbiter(_arb, handler._space), handler._space, handler.data)
222+
handler._post_solve(Arbiter(_arb, handler._space), handler._space)
223223

224224

225225
@ffi.def_extern()
@@ -234,7 +234,7 @@ def ext_cpCollisionSeparateFunc(
234234
# this try is needed since a separate callback will be called
235235
# if a colliding object is removed, regardless if its in a
236236
# step or not. Meaning the unlock must succeed
237-
handler._separate(Arbiter(_arb, handler._space), handler._space, handler.data)
237+
handler._separate(Arbiter(_arb, handler._space), handler._space)
238238
finally:
239239
handler._space._locked = orig_locked
240240

pymunk/collision_handler.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from ._chipmunk_cffi import ffi, lib
99
from .arbiter import Arbiter
1010

11-
_CollisionCallback = Callable[[Arbiter, "Space", dict[Any, Any]], None]
11+
_CollisionCallback = Callable[[Arbiter, "Space"], None]
1212

1313

1414
class CollisionHandler(object):
@@ -149,7 +149,7 @@ def separate(self, func: _CollisionCallback) -> None:
149149
self._handler.separateFunc = lib.ext_cpCollisionSeparateFunc
150150

151151
@staticmethod
152-
def do_nothing(arbiter: Arbiter, space: "Space", data: dict[Any, Any]) -> None:
152+
def do_nothing(arbiter: Arbiter, space: "Space") -> None:
153153
"""The default do nothing method used for the post_solve and seprate
154154
callbacks.
155155

pymunk/space.py

Lines changed: 37 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,15 @@
2828
_AddableObjects = Union[Body, Shape, Constraint]
2929

3030

31-
class Handlers(Mapping[Union[None, int, tuple[int, int]], CollisionHandler]):
31+
class HandlersMapping(Mapping[Union[None, int, tuple[int, int]], CollisionHandler]):
3232

3333
def __init__(self, space: "Space") -> None:
3434
self.space = space
3535

36-
_handlers: dict[Union[None, int, tuple[int, int]], CollisionHandler] = {}
36+
self._handlers: dict[Union[None, int, tuple[int, int]], CollisionHandler] = {}
3737

3838
def __getitem__(self, key: Union[None, int, tuple[int, int]]) -> CollisionHandler:
39+
3940
if key in self._handlers:
4041
return self._handlers[key]
4142
if key == None:
@@ -44,7 +45,7 @@ def __getitem__(self, key: Union[None, int, tuple[int, int]]) -> CollisionHandle
4445
elif isinstance(key, int):
4546
self._handlers[key] = self.space.add_collision_handler(key, None)
4647
return self._handlers[key]
47-
elif isinstance(key, tuple):
48+
elif isinstance(key, tuple) and len(key) == 2:
4849
assert isinstance(key, tuple)
4950
self._handlers[key] = self.space.add_collision_handler(key[0], key[1])
5051
return self._handlers[key]
@@ -166,7 +167,7 @@ def spacefree(cp_space: ffi.CData) -> None:
166167
self._remove_later: set[_AddableObjects] = set()
167168
self._bodies_to_check: set[Body] = set()
168169

169-
self._collision_handlers = Handlers(self)
170+
self._collision_handlers = HandlersMapping(self)
170171

171172
@property
172173
def shapes(self) -> KeysView[Shape]:
@@ -618,24 +619,16 @@ def step(self, dt: float) -> None:
618619

619620
self._post_step_callbacks.clear()
620621

621-
@property
622-
def collision_handlers(
623-
self,
624-
) -> Mapping[Union[None, int, tuple[int, int]], CollisionHandler]:
625-
return self.collision_handlers
626-
627-
def add_collision_handler(
622+
def set_collision_callbacks(
628623
self,
629624
collision_type_a: Optional[int] = None,
630625
collision_type_b: Optional[int] = None,
631626
begin: Optional[_CollisionCallback] = None,
632-
pre_step: Optional[_CollisionCallback] = None,
633-
post_step: Optional[_CollisionCallback] = None,
627+
pre_solve: Optional[_CollisionCallback] = None,
628+
post_solve: Optional[_CollisionCallback] = None,
634629
separate: Optional[_CollisionCallback] = None,
635-
data: Optional[Mapping] = None,
636-
) -> CollisionHandler:
637-
"""Return the :py:class:`CollisionHandler` for collisions between
638-
objects of type collision_type_a and collision_type_b.
630+
):
631+
"""Set callbacks that will be called during the 4 phases of collision handling.
639632
640633
Use None to indicate any collision_type.
641634
@@ -650,6 +643,9 @@ def add_collision_handler(
650643
If multiple handlers match the collision, the order will be that the
651644
most specific handler is called first.
652645
646+
Note that if a handler already exist for the a,b pair, that existing
647+
handler will be returned.
648+
653649
:param int collision_type_a: Collision type a
654650
:param int collision_type_b: Collision type b
655651
@@ -662,23 +658,32 @@ def add_collision_handler(
662658
collision_type_b, collision_type_a = collision_type_a, collision_type_b
663659

664660
key = collision_type_a, collision_type_b
665-
if key in self._handlers:
666-
return self._handlers[key]
667-
668-
# CP_WILDCARD_COLLISION_TYPE
669-
wildcard = int(ffi.cast("uintptr_t", ~0))
670-
if collision_type_a == None:
671-
collision_type_a = wildcard
661+
if key not in self._handlers:
662+
# CP_WILDCARD_COLLISION_TYPE
663+
wildcard = int(ffi.cast("uintptr_t", ~0))
664+
if collision_type_a == None:
665+
collision_type_a = wildcard
672666

673-
if collision_type_b == None:
674-
collision_type_b = wildcard
667+
if collision_type_b == None:
668+
collision_type_b = wildcard
675669

676-
h = lib.cpSpaceAddCollisionHandler(
677-
self._space, collision_type_a, collision_type_b
678-
)
679-
ch = CollisionHandler(h, self)
680-
self._handlers[key] = ch
681-
return ch
670+
h = lib.cpSpaceAddCollisionHandler(
671+
self._space, collision_type_a, collision_type_b
672+
)
673+
ch = CollisionHandler(h, self)
674+
self._handlers[key] = ch
675+
else:
676+
ch = self._handlers[key]
677+
678+
if begin != None:
679+
ch.begin = begin
680+
if pre_solve != None:
681+
ch.pre_solve = pre_solve
682+
if post_solve != None:
683+
ch.post_solve = post_solve
684+
if separate != None:
685+
ch.separate = separate
686+
return
682687

683688
def add_post_step_callback(
684689
self,

pymunk/tests/test_arbiter.py

Lines changed: 29 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,11 @@ def testRestitution(self) -> None:
2424

2525
s.add(b1, c1, b2, c2)
2626

27-
def pre_solve(arb: p.Arbiter, space: p.Space, data: Any) -> None:
27+
def pre_solve(arb: p.Arbiter, space: p.Space) -> None:
2828
self.assertEqual(arb.restitution, 0.18)
2929
arb.restitution = 1
3030

31-
s.add_collision_handler(1, 2).pre_solve = pre_solve
31+
s.set_collision_callbacks(1, 2, pre_solve=pre_solve)
3232

3333
for x in range(10):
3434
s.step(0.1)
@@ -52,11 +52,11 @@ def testFriction(self) -> None:
5252

5353
s.add(b1, c1, b2, c2)
5454

55-
def pre_solve(arb: p.Arbiter, space: p.Space, data: Any) -> None:
55+
def pre_solve(arb: p.Arbiter, space: p.Space) -> None:
5656
self.assertEqual(arb.friction, 0.18)
5757
arb.friction = 1
5858

59-
s.add_collision_handler(1, 2).pre_solve = pre_solve
59+
s.set_collision_callbacks(1, 2, pre_solve=pre_solve)
6060

6161
for x in range(10):
6262
s.step(0.1)
@@ -80,14 +80,14 @@ def testSurfaceVelocity(self) -> None:
8080

8181
s.add(b1, c1, b2, c2)
8282

83-
def pre_solve(arb: p.Arbiter, space: p.Space, data: Any) -> None:
83+
def pre_solve(arb: p.Arbiter, space: p.Space) -> None:
8484
self.assertAlmostEqual(arb.surface_velocity.x, 1.38461538462)
8585
self.assertAlmostEqual(arb.surface_velocity.y, -0.923076923077)
8686

8787
arb.surface_velocity = (10, 10)
8888
# TODO: add assert check that setting surface_velocity has any effect
8989

90-
s.add_collision_handler(1, 2).pre_solve = pre_solve
90+
s.set_collision_callbacks(1, 2, pre_solve=pre_solve)
9191
for x in range(5):
9292
s.step(0.1)
9393

@@ -106,7 +106,7 @@ def testContactPointSet(self) -> None:
106106

107107
s.add(b1, c1, b2, c2)
108108

109-
def pre_solve(arb: p.Arbiter, space: p.Space, data: Any) -> None:
109+
def pre_solve(arb: p.Arbiter, space: p.Space) -> None:
110110
# check inital values
111111
ps = arb.contact_point_set
112112
self.assertEqual(len(ps.points), 1)
@@ -142,7 +142,7 @@ def f() -> None:
142142

143143
self.assertRaises(Exception, f)
144144

145-
s.add_collision_handler(2, 1).pre_solve = pre_solve
145+
s.set_collision_callbacks(2, 1, pre_solve=pre_solve)
146146

147147
s.step(0.1)
148148

@@ -165,12 +165,12 @@ def testImpulse(self) -> None:
165165

166166
self.post_solve_done = False
167167

168-
def post_solve(arb: p.Arbiter, space: p.Space, data: Any) -> None:
168+
def post_solve(arb: p.Arbiter, space: p.Space) -> None:
169169
self.assertAlmostEqual(arb.total_impulse.x, 3.3936651583)
170170
self.assertAlmostEqual(arb.total_impulse.y, 4.3438914027)
171171
self.post_solve_done = True
172172

173-
s.add_collision_handler(1, 2).post_solve = post_solve
173+
s.set_collision_callbacks(1, 2, post_solve=post_solve)
174174

175175
s.step(0.1)
176176

@@ -194,10 +194,10 @@ def testTotalKE(self) -> None:
194194
s.add(b1, c1, b2, c2)
195195
r = {}
196196

197-
def post_solve(arb: p.Arbiter, space: p.Space, data: Any) -> None:
197+
def post_solve(arb: p.Arbiter, space: p.Space) -> None:
198198
r["ke"] = arb.total_ke
199199

200-
s.add_collision_handler(1, 2).post_solve = post_solve
200+
s.set_collision_callbacks(1, 2, post_solve=post_solve)
201201

202202
s.step(0.1)
203203

@@ -220,17 +220,17 @@ def testIsFirstContact(self) -> None:
220220

221221
s.add(b1, c1, b2, c2)
222222

223-
def pre_solve1(arb: p.Arbiter, space: p.Space, data: Any) -> None:
223+
def pre_solve1(arb: p.Arbiter, space: p.Space) -> None:
224224
self.assertTrue(arb.is_first_contact)
225225

226-
s.add_collision_handler(1, 2).pre_solve = pre_solve1
226+
s.set_collision_callbacks(1, 2, pre_solve=pre_solve1)
227227

228228
s.step(0.1)
229229

230-
def pre_solve2(arb: p.Arbiter, space: p.Space, data: Any) -> None:
230+
def pre_solve2(arb: p.Arbiter, space: p.Space) -> None:
231231
self.assertFalse(arb.is_first_contact)
232232

233-
s.add_collision_handler(1, 2).pre_solve = pre_solve2
233+
s.set_collision_callbacks(1, 2, pre_solve=pre_solve2)
234234

235235
s.step(0.1)
236236

@@ -248,10 +248,10 @@ def testNormal(self) -> None:
248248
s.add(b1, c1, c2)
249249
r = {}
250250

251-
def pre_solve1(arb: p.Arbiter, space: p.Space, data: Any) -> None:
251+
def pre_solve1(arb: p.Arbiter, space: p.Space) -> None:
252252
r["n"] = Vec2d(*arb.normal)
253253

254-
s.add_collision_handler(1, 2).pre_solve = pre_solve1
254+
s.set_collision_callbacks(1, 2, pre_solve=pre_solve1)
255255

256256
s.step(0.1)
257257

@@ -277,11 +277,11 @@ def testIsRemoval(self) -> None:
277277

278278
self.called1 = False
279279

280-
def separate1(arb: p.Arbiter, space: p.Space, data: Any) -> None:
280+
def separate1(arb: p.Arbiter, space: p.Space) -> None:
281281
self.called1 = True
282282
self.assertFalse(arb.is_removal)
283283

284-
s.add_collision_handler(1, 2).separate = separate1
284+
s.set_collision_callbacks(1, 2, separate=separate1)
285285

286286
for x in range(10):
287287
s.step(0.1)
@@ -292,11 +292,11 @@ def separate1(arb: p.Arbiter, space: p.Space, data: Any) -> None:
292292

293293
self.called2 = False
294294

295-
def separate2(arb: p.Arbiter, space: p.Space, data: Any) -> None:
295+
def separate2(arb: p.Arbiter, space: p.Space) -> None:
296296
self.called2 = True
297297
self.assertTrue(arb.is_removal)
298298

299-
s.add_collision_handler(1, 2).separate = separate2
299+
s.set_collision_callbacks(1, 2, separate=separate2)
300300
s.remove(b1, c1)
301301

302302
self.assertTrue(self.called2)
@@ -320,15 +320,15 @@ def testShapesAndBodies(self) -> None:
320320

321321
self.called = False
322322

323-
def pre_solve(arb: p.Arbiter, space: p.Space, data: Any) -> None:
323+
def pre_solve(arb: p.Arbiter, space: p.Space) -> None:
324324
self.called = True
325325
self.assertEqual(len(arb.shapes), 2)
326326
self.assertEqual(arb.shapes[0], c1)
327327
self.assertEqual(arb.shapes[1], c2)
328328
self.assertEqual(arb.bodies[0], arb.shapes[0].body)
329329
self.assertEqual(arb.bodies[1], arb.shapes[1].body)
330330

331-
s.add_collision_handler(1, 2).post_solve = pre_solve
331+
s.set_collision_callbacks(1, 2, post_solve=pre_solve)
332332

333333
s.step(0.1)
334334
self.assertTrue(self.called)
@@ -408,15 +408,15 @@ def callback(
408408
# print("process_values, expected calls", process_values, expected_calls)
409409

410410
s = setup()
411-
h = s.add_collision_handler(1, 2)
411+
h = s.set_collision_callbacks(1, 2)
412412
h.data["process_values"] = process_values
413413
h.data["expected"] = expected_calls
414414
h.data["result"] = []
415415

416-
h.begin = functools.partial(callback, "b")
417-
h.pre_solve = functools.partial(callback, "p")
418-
h.post_solve = functools.partial(callback, "t")
419-
h.separate = functools.partial(callback, "s")
416+
begin = functools.partial(callback, "b")
417+
pre_solve = functools.partial(callback, "p")
418+
post_solve = functools.partial(callback, "t")
419+
separate = functools.partial(callback, "s")
420420

421421
s.step(0.1)
422422
s.step(0.1)

0 commit comments

Comments
 (0)