Skip to content

Commit cf198b9

Browse files
committed
wip collision handler rework
1 parent 89c757f commit cf198b9

File tree

8 files changed

+150
-91
lines changed

8 files changed

+150
-91
lines changed

.readthedocs.yaml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,17 @@ build:
1212

1313
# Build documentation in the docs/ directory with Sphinx
1414
sphinx:
15-
configuration: docs/src/conf.py
15+
configuration: docs/src/conf.py
1616

1717
# Optionally build your docs in additional formats such as PDF
1818
formats:
19-
- pdf
19+
- pdf
2020

2121
# Optionally set the version of Python and requirements required to build your docs
2222
python:
23-
install:
24-
- requirements: docs/requirements.txt
23+
install:
24+
- requirements: docs/requirements.txt
2525

2626
# Do not include the Chipmunk2D submodule. It it not needed for docs.
2727
submodules:
28-
exclude: all
28+
exclude: all

benchmarks/pymunk-collision-callback.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
b = pymunk.Body(1,10)
99
c = pymunk.Circle(b, 5)
1010
s.add(b, c)
11-
h = s.add_global_collision_handler()
11+
h = s.add_collision_handler(None, None)
1212
def f(arb, s, data):
1313
return False
1414
h.pre_solve = f

pymunk/space.py

Lines changed: 27 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,10 @@ def __getitem__(self, key: Union[None, int, tuple[int, int]]) -> CollisionHandle
4242
if key in self._handlers:
4343
return self._handlers[key]
4444
if key == None:
45-
self._handlers[None] = self.space.add_global_collision_handler()
45+
self._handlers[None] = self.space.add_collision_handler(None, None)
4646
return self._handlers[None]
4747
elif isinstance(key, int):
48-
self._handlers[key] = self.space.add_wildcard_collision_handler(key)
48+
self._handlers[key] = self.space.add_collision_handler(key, None)
4949
return self._handlers[key]
5050
elif isinstance(key, tuple):
5151
assert isinstance(key, tuple)
@@ -626,11 +626,13 @@ def collision_handlers(
626626
return self.collision_handlers
627627

628628
def add_collision_handler(
629-
self, collision_type_a: int, collision_type_b: int
629+
self, collision_type_a: Optional[int], collision_type_b: Optional[int]
630630
) -> CollisionHandler:
631631
"""Return the :py:class:`CollisionHandler` for collisions between
632632
objects of type collision_type_a and collision_type_b.
633633
634+
Use None to indicate any collision_type.
635+
634636
Fill the desired collision callback functions, for details see the
635637
:py:class:`CollisionHandler` object.
636638
@@ -645,65 +647,34 @@ def add_collision_handler(
645647
646648
:rtype: :py:class:`CollisionHandler`
647649
"""
648-
key = min(collision_type_a, collision_type_b), max(
649-
collision_type_a, collision_type_b
650-
)
650+
# key = min(collision_type_a, collision_type_b), max(
651+
# collision_type_a, collision_type_b
652+
# )
653+
654+
if collision_type_a == None and collision_type_b != None:
655+
collision_type_b, collision_type_a = collision_type_a, collision_type_b
656+
657+
key = collision_type_a, collision_type_b
651658
if key in self._handlers:
652659
return self._handlers[key]
653660

661+
# if collision_type_a == None and collision_type_b == None:
662+
# return self.add_global_collision_handler()
663+
# CP_WILDCARD_COLLISION_TYPE
664+
wildcard = int(ffi.cast("uintptr_t", ~0))
665+
if collision_type_a == None:
666+
collision_type_a = wildcard
667+
668+
if collision_type_b == None:
669+
collision_type_b = wildcard
670+
654671
h = cp.cpSpaceAddCollisionHandler(
655672
self._space, collision_type_a, collision_type_b
656673
)
657674
ch = CollisionHandler(h, self)
658675
self._handlers[key] = ch
659676
return ch
660677

661-
def add_wildcard_collision_handler(self, collision_type_a: int) -> CollisionHandler:
662-
"""Add a wildcard collision handler for given collision type.
663-
664-
This handler will be used any time an object with this type collides
665-
with another object, regardless of its type. A good example is a
666-
projectile that should be destroyed the first time it hits anything.
667-
There may be a specific collision handler and two wildcard handlers.
668-
It's up to the specific handler to decide if and when to call the
669-
wildcard handlers and what to do with their return values.
670-
671-
When a new wildcard handler is created, the callbacks will all be
672-
set to builtin callbacks that perform the default behavior. (accept
673-
all collisions in :py:func:`~CollisionHandler.begin` and
674-
:py:func:`~CollisionHandler.pre_solve`, or do nothing for
675-
:py:func:`~CollisionHandler.post_solve` and
676-
:py:func:`~CollisionHandler.separate`.
677-
678-
:param int collision_type_a: Collision type
679-
:rtype: :py:class:`CollisionHandler`
680-
"""
681-
682-
if collision_type_a in self._handlers:
683-
return self._handlers[collision_type_a]
684-
685-
h = cp.cpSpaceAddWildcardHandler(self._space, collision_type_a)
686-
ch = CollisionHandler(h, self)
687-
self._handlers[collision_type_a] = ch
688-
return ch
689-
690-
def add_global_collision_handler(self) -> CollisionHandler:
691-
"""Return a reference to the default collision handler or that is
692-
used to process all collisions that don't have a more specific
693-
handler.
694-
695-
The default behavior for each of the callbacks is to call
696-
the wildcard handlers, ANDing their return values together if
697-
applicable.
698-
"""
699-
if None in self._handlers:
700-
return self._handlers[None]
701-
702-
_h = cp.cpSpaceAddGlobalCollisionHandler(self._space)
703-
h = CollisionHandler(_h, self)
704-
self._handlers[None] = h
705-
return h
706-
707678
def add_post_step_callback(
708679
self,
709680
callback_function: Callable[
@@ -917,7 +888,7 @@ def bb_query(self, bb: "BB", shape_filter: ShapeFilter) -> list[Shape]:
917888
The filter is applied to the query and follows the same rules as the
918889
collision detection.
919890
920-
Sensor shapes are included in the result
891+
Sensor shapes are included in the result
921892
922893
:param bb: Bounding box
923894
:param shape_filter: Shape filter
@@ -938,7 +909,7 @@ def bb_query(self, bb: "BB", shape_filter: ShapeFilter) -> list[Shape]:
938909
def shape_query(self, shape: Shape) -> list[ShapeQueryInfo]:
939910
"""Query a space for any shapes overlapping the given shape
940911
941-
Sensor shapes are included in the result
912+
Sensor shapes are included in the result
942913
943914
:param shape: Shape to query with
944915
:type shape: :py:class:`Circle`, :py:class:`Poly` or :py:class:`Segment`
@@ -1081,11 +1052,11 @@ def __setstate__(self, state: _State) -> None:
10811052
elif k == "_handlers":
10821053
for k2, hd in v:
10831054
if k2 == None:
1084-
h = self.add_global_collision_handler()
1055+
h = self.add_collision_handler(None, None)
10851056
elif isinstance(k2, tuple):
10861057
h = self.add_collision_handler(k2[0], k2[1])
10871058
else:
1088-
h = self.add_wildcard_collision_handler(k2)
1059+
h = self.add_collision_handler(k2, None)
10891060
if "_begin" in hd:
10901061
h.begin = hd["_begin"]
10911062
if "_pre_solve" in hd:

pymunk/tests/test_arbiter.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ def testContactPointSet(self) -> None:
107107

108108
def pre_solve(arb: p.Arbiter, space: p.Space, data: Any) -> None:
109109
# check inital values
110-
ps: p.ContactPointSet = arb.contact_point_set
110+
ps = arb.contact_point_set
111111
self.assertEqual(len(ps.points), 1)
112112
self.assertAlmostEqual(ps.normal.x, 0.8574929257)
113113
self.assertAlmostEqual(ps.normal.y, 0.5144957554)
@@ -141,7 +141,7 @@ def f() -> None:
141141

142142
self.assertRaises(Exception, f)
143143

144-
s.add_global_collision_handler().pre_solve = pre_solve
144+
s.add_collision_handler(2, 1).pre_solve = pre_solve
145145

146146
s.step(0.1)
147147

@@ -191,14 +191,17 @@ def testTotalKE(self) -> None:
191191
c2.friction = 0.8
192192

193193
s.add(b1, c1, b2, c2)
194+
r = {}
194195

195196
def post_solve(arb: p.Arbiter, space: p.Space, data: Any) -> None:
196-
self.assertAlmostEqual(arb.total_ke, 43.438914027)
197+
r["ke"] = arb.total_ke
197198

198199
s.add_collision_handler(1, 2).post_solve = post_solve
199200

200201
s.step(0.1)
201202

203+
self.assertAlmostEqual(r["ke"], 43.438914027)
204+
202205
def testIsFirstContact(self) -> None:
203206
s = p.Space()
204207
s.gravity = 0, -100
@@ -237,18 +240,23 @@ def testNormal(self) -> None:
237240
b1 = p.Body(1, 30)
238241
b1.position = 5, 10
239242
c1 = p.Circle(b1, 10)
243+
c1.collision_type = 1
240244
c2 = p.Circle(s.static_body, 10)
245+
c2.collision_type = 2
241246

242247
s.add(b1, c1, c2)
248+
r = {}
243249

244250
def pre_solve1(arb: p.Arbiter, space: p.Space, data: Any) -> None:
245-
self.assertAlmostEqual(arb.normal.x, 0.44721359)
246-
self.assertAlmostEqual(arb.normal.y, 0.89442719)
251+
r["n"] = Vec2d(*arb.normal)
247252

248-
s.add_global_collision_handler().pre_solve = pre_solve1
253+
s.add_collision_handler(1, 2).pre_solve = pre_solve1
249254

250255
s.step(0.1)
251256

257+
self.assertAlmostEqual(r["n"].x, -0.44721359)
258+
self.assertAlmostEqual(r["n"].y, -0.89442719)
259+
252260
def testIsRemoval(self) -> None:
253261
s = p.Space()
254262
s.gravity = 0, -100

pymunk/tests/test_shape.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,7 @@ def testSegmentSegmentCollision(self) -> None:
301301
def begin(arb: p.Arbiter, space: p.Space, data: Any) -> None:
302302
self.num_of_begins += 1
303303

304-
s.add_global_collision_handler().begin = begin
304+
s.add_collision_handler(None, None).begin = begin
305305
s.step(0.1)
306306

307307
self.assertEqual(1, self.num_of_begins)

0 commit comments

Comments
 (0)