reorganized collider code

This commit is contained in:
=
2026-03-12 14:46:20 -04:00
parent 2414080c64
commit ee0194831e
6 changed files with 108 additions and 105 deletions

Binary file not shown.

Binary file not shown.

View File

@@ -1,110 +1,8 @@
from dataclasses import dataclass
from abc import ABC, abstractmethod
from typing import Generator
import pygame as pg
from math import pi
from collider.types import *
from transform import Transform
@dataclass
class Face:
begin: pg.Vector2
end: pg.Vector2
@property
def normal(self) -> pg.Vector2:
return (self.end-self.begin).rotate(-90).normalize()
@dataclass
class ColliderContact:
__slots__ = ["points", "normal", "penetration"]
points: list[pg.Vector2]
normal: pg.Vector2
penetration: float
@dataclass(frozen=True)
class PolygonalHull:
_vertices: list[pg.Vector2]
def get_vertex_at_index(self, key: int) -> pg.Vector2:
return self._vertices[key]
def get_face_at_index(self, key: int) -> Face:
return Face(self._vertices[key], self._vertices[(key + 1) % len(self._vertices)])
def vertices(self) -> Generator[pg.Vector2, None, None]:
for v in self._vertices:
yield v
def faces(self) -> Generator[Face, None, None]:
for i in range(len(self._vertices)):
yield self.get_face_at_index(i)
def project(self, axis: pg.Vector2) -> tuple[float, float]:
projections = [v.dot(axis) for v in self._vertices]
return (min(projections), max(projections))
class BaseCollider(ABC):
@abstractmethod
def moment_of_inertia(self, mass: float) -> float:
pass
class ConvexCollider(BaseCollider):
@abstractmethod
def hull(self, transform: Transform) -> PolygonalHull:
pass
@dataclass
class CircleCollider(BaseCollider):
radius: float
def moment_of_inertia(self, mass: float) -> float:
return 0.5 * self.radius ** 2 * mass
@dataclass
class LineCollider(ConvexCollider):
length: float
def hull(self, transform: Transform) -> PolygonalHull:
return PolygonalHull([
transform.global_position - pg.Vector2(self.length / 2.0, 0).rotate(transform.global_degrees) * transform.global_scale,
transform.global_position + pg.Vector2(self.length / 2.0, 0).rotate(transform.global_degrees) * transform.global_scale
])
def moment_of_inertia(self, mass):
return 1.0 / 12.0 * mass * self.length**2
@dataclass
class RectCollider(ConvexCollider):
dimensions: tuple[float, float]
@property
def width(self):
return self.dimensions[0]
@property
def height(self):
return self.dimensions[1]
def hull(self, transform: Transform) -> PolygonalHull:
return PolygonalHull([
transform.global_position - pg.Vector2(self.width / 2.0, self.height / 2.0).rotate(transform.global_degrees) * transform.global_scale,
transform.global_position - pg.Vector2(-self.width / 2.0, self.height / 2.0).rotate(transform.global_degrees) * transform.global_scale,
transform.global_position - pg.Vector2(-self.width / 2.0, -self.height / 2.0).rotate(transform.global_degrees) * transform.global_scale,
transform.global_position - pg.Vector2(self.width / 2.0, -self.height / 2.0).rotate(transform.global_degrees) * transform.global_scale
])
def moment_of_inertia(self, mass: float) -> float:
return (1.0 / 12.0) * mass * (self.width ** 2 + self.height ** 2)
def _interval_overlap(a: tuple[float, float], b: tuple[float, float]) -> float | None:
if a[0] <= b[1] and b[0] <= a[1]:
return min(a[1], b[1]) - max(a[0], b[0])

104
collider/types.py Normal file
View File

@@ -0,0 +1,104 @@
from dataclasses import dataclass
from typing import Generator
from abc import ABC, abstractmethod
import pygame as pg
from transform import Transform
@dataclass
class Face:
begin: pg.Vector2
end: pg.Vector2
@property
def normal(self) -> pg.Vector2:
return (self.end-self.begin).rotate(-90).normalize()
@dataclass
class ColliderContact:
__slots__ = ["points", "normal", "penetration"]
points: list[pg.Vector2]
normal: pg.Vector2
penetration: float
@dataclass(frozen=True)
class PolygonalHull:
_vertices: list[pg.Vector2]
def get_vertex_at_index(self, key: int) -> pg.Vector2:
return self._vertices[key]
def get_face_at_index(self, key: int) -> Face:
return Face(self._vertices[key], self._vertices[(key + 1) % len(self._vertices)])
def vertices(self) -> Generator[pg.Vector2, None, None]:
for v in self._vertices:
yield v
def faces(self) -> Generator[Face, None, None]:
for i in range(len(self._vertices)):
yield self.get_face_at_index(i)
def project(self, axis: pg.Vector2) -> tuple[float, float]:
projections = [v.dot(axis) for v in self._vertices]
return (min(projections), max(projections))
class BaseCollider(ABC):
@abstractmethod
def moment_of_inertia(self, mass: float) -> float:
pass
class ConvexCollider(BaseCollider):
@abstractmethod
def hull(self, transform: Transform) -> PolygonalHull:
pass
@dataclass
class CircleCollider(BaseCollider):
radius: float
def moment_of_inertia(self, mass: float) -> float:
return 0.5 * self.radius ** 2 * mass
@dataclass
class LineCollider(ConvexCollider):
length: float
def hull(self, transform: Transform) -> PolygonalHull:
return PolygonalHull([
transform.global_position - pg.Vector2(self.length / 2.0, 0).rotate(transform.global_degrees) * transform.global_scale,
transform.global_position + pg.Vector2(self.length / 2.0, 0).rotate(transform.global_degrees) * transform.global_scale
])
def moment_of_inertia(self, mass):
return 1.0 / 12.0 * mass * self.length**2
@dataclass
class RectCollider(ConvexCollider):
dimensions: tuple[float, float]
@property
def width(self):
return self.dimensions[0]
@property
def height(self):
return self.dimensions[1]
def hull(self, transform: Transform) -> PolygonalHull:
return PolygonalHull([
transform.global_position - pg.Vector2(self.width / 2.0, self.height / 2.0).rotate(transform.global_degrees) * transform.global_scale,
transform.global_position - pg.Vector2(-self.width / 2.0, self.height / 2.0).rotate(transform.global_degrees) * transform.global_scale,
transform.global_position - pg.Vector2(-self.width / 2.0, -self.height / 2.0).rotate(transform.global_degrees) * transform.global_scale,
transform.global_position - pg.Vector2(self.width / 2.0, -self.height / 2.0).rotate(transform.global_degrees) * transform.global_scale
])
def moment_of_inertia(self, mass: float) -> float:
return (1.0 / 12.0) * mass * (self.width ** 2 + self.height ** 2)

View File

@@ -1,7 +1,7 @@
import pygame as pg
from math import pi
from rigidbody import *
from collider import RectCollider
from collider.types import RectCollider
from tools import debug

View File

@@ -4,7 +4,8 @@ from itertools import combinations
import pygame as pg
from tools import debug
from collider import *
from collider.system import intersect
from collider.types import *
from transform import Transform
@dataclass