Source code for mini_arcade_core.spaces.d2.boundaries2d
"""
Boundary behaviors for 2D rectangular objects.
This module is transitional:
- Supports the legacy d2 model (`position`, `size`, `velocity`)
- Supports the new model (`transform`, `kinematic`)
"""
from __future__ import annotations
from dataclasses import dataclass
from typing import Protocol
from mini_arcade_core.spaces.geometry.transform import Transform2D
from mini_arcade_core.spaces.physics.kinematics2d import Kinematic2D
from ..geometry.bounds import Bounds2D, Position2D, Size2D
from .physics2d import Velocity2D
class LegacyRectKinematic(Protocol):
"""
Legacy contract for something that can bounce.
"""
position: Position2D
size: Size2D
velocity: Velocity2D
[docs]
class RectKinematic(Protocol):
"""
New contract for something that can bounce.
Note:
- In the current engine usage, `transform.center` is treated as top-left.
"""
transform: Transform2D
kinematic: Kinematic2D
[docs]
@dataclass
class VerticalBounce:
"""
Bounce an object off the top/bottom bounds by inverting vertical velocity
and clamping its position inside bounds.
"""
bounds: Bounds2D
[docs]
def apply(self, obj: RectKinematic | LegacyRectKinematic) -> bool:
"""
Apply vertical bounce to the given object.
"""
top = self.bounds.top
bottom = self.bounds.bottom
bounced = False
# New model
if hasattr(obj, "transform") and hasattr(obj, "kinematic"):
if obj.kinematic is None:
return False
y = obj.transform.center.y
h = obj.transform.size.height
if y <= top:
y = top
obj.kinematic.velocity.y *= -1
bounced = True
if y + h >= bottom:
y = bottom - h
obj.kinematic.velocity.y *= -1
bounced = True
obj.transform.center.y = y
return bounced
# Legacy model
if obj.position.y <= top:
obj.position.y = top
obj.velocity.vy *= -1
bounced = True
if obj.position.y + obj.size.height >= bottom:
obj.position.y = bottom - obj.size.height
obj.velocity.vy *= -1
bounced = True
return bounced
[docs]
class LegacyRectSprite(Protocol):
"""
Legacy contract for something that can wrap.
"""
position: Position2D
size: Size2D
[docs]
class RectSprite(Protocol):
"""
New contract for something that can wrap.
"""
transform: Transform2D
[docs]
@dataclass
class VerticalWrap:
"""
Wrap an object top <-> bottom.
"""
bounds: Bounds2D
[docs]
def apply(self, obj: RectSprite | LegacyRectSprite) -> None:
"""
Apply vertical wrap to the given object.
"""
top = self.bounds.top
bottom = self.bounds.bottom
# New model
if hasattr(obj, "transform"):
y = obj.transform.center.y
h = obj.transform.size.height
if y + h < top:
obj.transform.center.y = bottom
elif y > bottom:
obj.transform.center.y = top - h
return
# Legacy model
if obj.position.y + obj.size.height < top:
obj.position.y = bottom
elif obj.position.y > bottom:
obj.position.y = top - obj.size.height