This commit is contained in:
blueloveTH 2023-08-23 20:55:42 +08:00
parent f9c0a95237
commit fe64c2d5be
8 changed files with 136 additions and 420 deletions

View File

@ -42,105 +42,7 @@ It hides the details of Box2D's API and provides a high-level interface.
## API list
```python
from linalg import vec2, vec4
from typing import Iterable
class _NodeLike: # duck-type protocol
def on_contact_begin(self, other: 'Body'): ...
def on_contact_end(self, other: 'Body'): ...
class _DrawLike: # duck-type protocol
def draw_polygon(self, vertices: list[vec2], color: vec4): ...
def draw_solid_polygon(self, vertices: list[vec2], color: vec4): ...
def draw_circle(self, center: vec2, radius: float, color: vec4): ...
def draw_solid_circle(self, center: vec2, radius: float, axis: vec2, color: vec4): ...
def draw_segment(self, p1: vec2, p2: vec2, color: vec4): ...
def draw_transform(self, position: vec2, rotation: float): ...
def draw_point(self, p: vec2, size: float, color: vec4): ...
class World:
gravity: vec2 # gravity of the world, by default vec2(0, 0)
def get_bodies(self) -> Iterable['Body']:
"""return all bodies in the world."""
def ray_cast(self, start: vec2, end: vec2) -> list['Body']:
"""raycast from start to end"""
def box_cast(self, lower: vec2, upper: vec2) -> list['Body']:
"""query bodies in the AABB region."""
def step(self, dt: float, velocity_iterations: int, position_iterations: int) -> None:
"""step the simulation, e.g. world.step(1/60, 8, 3)"""
# enum
# {
# e_shapeBit = 0x0001, ///< draw shapes
# e_jointBit = 0x0002, ///< draw joint connections
# e_aabbBit = 0x0004, ///< draw axis aligned bounding boxes
# e_pairBit = 0x0008, ///< draw broad-phase pairs
# e_centerOfMassBit = 0x0010 ///< draw center of mass frame
# };
def debug_draw(self, flags: int):
"""draw debug shapes of all bodies in the world."""
def set_debug_draw(self, draw: _DrawLike):
"""set the debug draw object."""
def create_weld_joint(self, body_a: 'Body', body_b: 'Body'):
"""create a weld joint between two bodies."""
class Body:
type: int # 0-static, 1-kinematic, 2-dynamic, by default 2
gravity_scale: float
fixed_rotation: bool
enabled: bool
bullet: bool # whether to use continuous collision detection
@property
def mass(self) -> float: ...
@property
def inertia(self) -> float: ...
position: vec2
rotation: float # in radians (counter-clockwise)
velocity: vec2 # linear velocity
angular_velocity: float
damping: float # linear damping
angular_damping: float
# fixture settings
density: float
friction: float
restitution: float
restitution_threshold: float
is_sensor: bool
def __new__(cls, world: World, node: _NodeLike = None):
"""create a body in the world."""
def set_box_shape(self, hx: float, hy: float): ...
def set_circle_shape(self, radius: float): ...
def set_polygon_shape(self, points: list[vec2]): ...
def set_chain_shape(self, points: list[vec2]): ...
def apply_force(self, force: vec2, point: vec2): ...
def apply_force_to_center(self, force: vec2): ...
def apply_torque(self, torque: float): ...
def apply_impulse(self, impulse: vec2, point: vec2): ...
def apply_impulse_to_center(self, impulse: vec2): ...
def apply_angular_impulse(self, impulse: float): ...
def get_node(self) -> _NodeLike:
"""return the node that is attached to this body."""
def get_contacts(self) -> list['Body']:
"""return all bodies that are in contact with this body."""
def destroy(self):
"""destroy this body."""
```
...
## Example

View File

@ -4,148 +4,3 @@ label: c
---
Interop with pointers and C structs.
```python
from typing import overload
def malloc(size: int) -> 'void_p': ...
def free(ptr: 'void_p') -> None: ...
def sizeof(type: str) -> int: ...
def refl(name: str) -> '_refl': ...
def memset(ptr: 'void_p', value: int, size: int) -> None: ...
def memcpy(dst: 'void_p', src: 'void_p', size: int) -> None: ...
class _refl:
def __call__(self) -> 'struct': ...
def __getitem__(self, key: str) -> int: ...
def name(self) -> str: ...
def size(self) -> int: ...
class void_p:
def __add__(self, i: int) -> 'void_p': ...
def __sub__(self, i: int) -> 'void_p': ...
def __eq__(self, other: 'void_p') -> bool: ...
def __ne__(self, other: 'void_p') -> bool: ...
def offset(self, i: int) -> 'void_p': ...
def hex(self) -> str: ...
@staticmethod
def from_hex(s: str) -> 'void_p': ...
def read_char(self) -> int: ...
def read_uchar(self) -> int: ...
def read_short(self) -> int: ...
def read_ushort(self) -> int: ...
def read_int(self) -> int: ...
def read_uint(self) -> int: ...
def read_long(self) -> int: ...
def read_ulong(self) -> int: ...
def read_longlong(self) -> int: ...
def read_ulonglong(self) -> int: ...
def read_float(self) -> float: ...
def read_double(self) -> float: ...
def read_bool(self) -> bool: ...
def read_void_p(self) -> 'void_p': ...
def read_bytes(self, size: int) -> bytes: ...
def read_struct(self, type: str) -> 'struct': ...
def write_char(self, value: int) -> None: ...
def write_uchar(self, value: int) -> None: ...
def write_short(self, value: int) -> None: ...
def write_ushort(self, value: int) -> None: ...
def write_int(self, value: int) -> None: ...
def write_uint(self, value: int) -> None: ...
def write_long(self, value: int) -> None: ...
def write_ulong(self, value: int) -> None: ...
def write_longlong(self, value: int) -> None: ...
def write_ulonglong(self, value: int) -> None: ...
def write_float(self, value: float) -> None: ...
def write_double(self, value: float) -> None: ...
def write_bool(self, value: bool) -> None: ...
def write_void_p(self, value: 'void_p') -> None: ...
def write_bytes(self, value: bytes) -> None: ...
def write_struct(self, value: 'struct') -> None: ...
def get_base_offset(self) -> int: ...
@overload
def set_base_offset(self, offset: int) -> None: ...
@overload
def set_base_offset(self, offset: str) -> None: ...
class struct:
@overload
def __init__(self, size: int): ...
@overload
def __init__(self, p: 'void_p', size: int): ...
@overload
def __init__(self, s: str): ...
@overload
def __init__(self, b: bytes): ...
def addr(self) -> 'void_p': ...
def copy(self) -> 'struct': ...
def size(self) -> int: ...
def __eq__(self, other: 'struct') -> bool: ...
def __ne__(self, other: 'struct') -> bool: ...
def to_string(self) -> str: ...
def to_bytes(self) -> bytes: ...
def read_char(self, offset=0) -> int: ...
def read_uchar(self, offset=0) -> int: ...
def read_short(self, offset=0) -> int: ...
def read_ushort(self, offset=0) -> int: ...
def read_int(self, offset=0) -> int: ...
def read_uint(self, offset=0) -> int: ...
def read_long(self, offset=0) -> int: ...
def read_ulong(self, offset=0) -> int: ...
def read_longlong(self, offset=0) -> int: ...
def read_ulonglong(self, offset=0) -> int: ...
def read_float(self, offset=0) -> float: ...
def read_double(self, offset=0) -> float: ...
def read_bool(self, offset=0) -> bool: ...
def read_void_p(self, offset=0) -> 'void_p': ...
def write_char(self, value: int, offset=0) -> None: ...
def write_uchar(self, value: int, offset=0) -> None: ...
def write_short(self, value: int, offset=0) -> None: ...
def write_ushort(self, value: int, offset=0) -> None: ...
def write_int(self, value: int, offset=0) -> None: ...
def write_uint(self, value: int, offset=0) -> None: ...
def write_long(self, value: int, offset=0) -> None: ...
def write_ulong(self, value: int, offset=0) -> None: ...
def write_longlong(self, value: int, offset=0) -> None: ...
def write_ulonglong(self, value: int, offset=0) -> None: ...
def write_float(self, value: float, offset=0) -> None: ...
def write_double(self, value: float, offset=0) -> None: ...
def write_bool(self, value: bool, offset=0) -> None: ...
def write_void_p(self, value: 'void_p', offset=0) -> None: ...
char_ = refl("char")
uchar_ = refl("uchar")
short_ = refl("short")
ushort_ = refl("ushort")
int_ = refl("int")
uint_ = refl("uint")
long_ = refl("long")
ulong_ = refl("ulong")
longlong_ = refl("longlong")
ulonglong_ = refl("ulonglong")
float_ = refl("float")
double_ = refl("double")
bool_ = refl("bool")
char_p = void_p
uchar_p = void_p
short_p = void_p
ushort_p = void_p
int_p = void_p
uint_p = void_p
long_p = void_p
ulong_p = void_p
longlong_p = void_p
ulonglong_p = void_p
float_p = void_p
double_p = void_p
bool_p = void_p
```

View File

@ -4,125 +4,3 @@ label: linalg
---
Provide `mat3x3`, `vec2`, `vec3` and `vec4` types.
```python
from typing import overload
from c import float_p
class vec2:
x: float
y: float
def __init__(self, x: float, y: float) -> None: ...
def copy(self) -> vec2: ...
def __add__(self, other: vec2) -> vec2: ...
def __sub__(self, other: vec2) -> vec2: ...
def __mul__(self, other: float) -> vec2: ...
def __truediv__(self, other: float) -> vec2: ...
def dot(self, other: vec2) -> float: ...
def cross(self, other: vec2) -> float: ...
def length(self) -> float: ...
def length_squared(self) -> float: ...
def normalize(self) -> vec2: ...
def rotate(self, radians: float) -> vec2: ...
def addr(self) -> float_p: ...
class vec3:
x: float
y: float
z: float
def __init__(self, x: float, y: float, z: float) -> None: ...
def copy(self) -> vec3: ...
def __add__(self, other: vec3) -> vec3: ...
def __sub__(self, other: vec3) -> vec3: ...
def __mul__(self, other: float) -> vec3: ...
def __truediv__(self, other: float) -> vec3: ...
def dot(self, other: vec3) -> float: ...
def cross(self, other: vec3) -> float: ...
def length(self) -> float: ...
def length_squared(self) -> float: ...
def normalize(self) -> vec3: ...
def addr(self) -> float_p: ...
class vec4:
x: float
y: float
z: float
w: float
def __init__(self, x: float, y: float, z: float, w: float) -> None: ...
def copy(self) -> vec4: ...
def __add__(self, other: vec4) -> vec4: ...
def __sub__(self, other: vec4) -> vec4: ...
def __mul__(self, other: float) -> vec4: ...
def __truediv__(self, other: float) -> vec4: ...
def dot(self, other: vec4) -> float: ...
def length(self) -> float: ...
def length_squared(self) -> float: ...
def normalize(self) -> vec4: ...
def addr(self) -> float_p: ...
class mat3x3:
_11: float
_12: float
_13: float
_21: float
_22: float
_23: float
_31: float
_32: float
_33: float
@overload
def __init__(self) -> None: ...
@overload
def __init__(self, _11, _12, _13, _21, _22, _23, _31, _32, _33) -> None: ...
@overload
def __init__(self, a: list[list]): ...
def set_zeros(self) -> None: ...
def set_ones(self) -> None: ...
def set_identity(self) -> None: ...
def copy(self) -> mat3x3: ...
def __getitem__(self, index: tuple[int, int]) -> float: ...
def __setitem__(self, index: tuple[int, int], value: float) -> None: ...
def __add__(self, other: mat3x3) -> mat3x3: ...
def __sub__(self, other: mat3x3) -> mat3x3: ...
def __mul__(self, other: float) -> mat3x3: ...
def __truediv__(self, other: float) -> mat3x3: ...
@overload
def __matmul__(self, other: mat3x3) -> mat3x3: ...
@overload
def __matmul__(self, other: vec3) -> vec3: ...
@overload
def matmul(self, other: mat3x3) -> mat3x3: ...
@overload
def matmul(self, other: vec3) -> vec3: ...
def determinant(self) -> float: ...
def transpose(self) -> mat3x3: ...
def inverse(self) -> mat3x3: ...
def __invert__(self) -> mat3x3: ...
@staticmethod
def zeros() -> mat3x3: ...
@staticmethod
def ones() -> mat3x3: ...
@staticmethod
def identity() -> mat3x3: ...
# affine transformations
@staticmethod
def trs(t: vec2, r: float, s: vec2) -> mat3x3: ...
def is_affine(self) -> bool: ...
def translation(self) -> vec2: ...
def rotation(self) -> float: ...
def scale(self) -> vec2: ...
def transform_point(self, p: vec2) -> vec2: ...
def transform_vector(self, v: vec2) -> vec2: ...
```

97
include/typings/box2d.pyi Normal file
View File

@ -0,0 +1,97 @@
from linalg import vec2, vec4
from typing import Iterable
class _NodeLike: # duck-type protocol
def on_contact_begin(self, other: 'Body'): ...
def on_contact_end(self, other: 'Body'): ...
class _DrawLike: # duck-type protocol
def draw_polygon(self, vertices: list[vec2], color: vec4): ...
def draw_solid_polygon(self, vertices: list[vec2], color: vec4): ...
def draw_circle(self, center: vec2, radius: float, color: vec4): ...
def draw_solid_circle(self, center: vec2, radius: float, axis: vec2, color: vec4): ...
def draw_segment(self, p1: vec2, p2: vec2, color: vec4): ...
def draw_transform(self, position: vec2, rotation: float): ...
def draw_point(self, p: vec2, size: float, color: vec4): ...
class World:
gravity: vec2 # gravity of the world, by default vec2(0, 0)
def get_bodies(self) -> Iterable['Body']:
"""return all bodies in the world."""
def ray_cast(self, start: vec2, end: vec2) -> list['Body']:
"""raycast from start to end"""
def box_cast(self, lower: vec2, upper: vec2) -> list['Body']:
"""query bodies in the AABB region."""
def step(self, dt: float, velocity_iterations: int, position_iterations: int) -> None:
"""step the simulation, e.g. world.step(1/60, 8, 3)"""
# enum
# {
# e_shapeBit = 0x0001, ///< draw shapes
# e_jointBit = 0x0002, ///< draw joint connections
# e_aabbBit = 0x0004, ///< draw axis aligned bounding boxes
# e_pairBit = 0x0008, ///< draw broad-phase pairs
# e_centerOfMassBit = 0x0010 ///< draw center of mass frame
# };
def debug_draw(self, flags: int):
"""draw debug shapes of all bodies in the world."""
def set_debug_draw(self, draw: _DrawLike):
"""set the debug draw object."""
def create_weld_joint(self, body_a: 'Body', body_b: 'Body'):
"""create a weld joint between two bodies."""
class Body:
type: int # 0-static, 1-kinematic, 2-dynamic, by default 2
gravity_scale: float
fixed_rotation: bool
enabled: bool
bullet: bool # whether to use continuous collision detection
@property
def mass(self) -> float: ...
@property
def inertia(self) -> float: ...
position: vec2
rotation: float # in radians (counter-clockwise)
velocity: vec2 # linear velocity
angular_velocity: float
damping: float # linear damping
angular_damping: float
# fixture settings
density: float
friction: float
restitution: float
restitution_threshold: float
is_sensor: bool
def __new__(cls, world: World, node: _NodeLike = None):
"""create a body in the world."""
def set_box_shape(self, hx: float, hy: float): ...
def set_circle_shape(self, radius: float): ...
def set_polygon_shape(self, points: list[vec2]): ...
def set_chain_shape(self, points: list[vec2]): ...
def apply_force(self, force: vec2, point: vec2): ...
def apply_force_to_center(self, force: vec2): ...
def apply_torque(self, torque: float): ...
def apply_impulse(self, impulse: vec2, point: vec2): ...
def apply_impulse_to_center(self, impulse: vec2): ...
def apply_angular_impulse(self, impulse: float): ...
def get_node(self) -> _NodeLike:
"""return the node that is attached to this body."""
def get_contacts(self) -> list['Body']:
"""return all bodies that are in contact with this body."""
def destroy(self):
"""destroy this body."""

View File

@ -78,25 +78,21 @@ class mat3x3:
def set_identity(self) -> None: ...
def copy(self) -> mat3x3: ...
def determinant(self) -> float: ...
def transpose(self) -> mat3x3: ...
def __getitem__(self, index: tuple[int, int]) -> float: ...
def __setitem__(self, index: tuple[int, int], value: float) -> None: ...
def __add__(self, other: mat3x3) -> mat3x3: ...
def __sub__(self, other: mat3x3) -> mat3x3: ...
def __mul__(self, other: float) -> mat3x3: ...
def __truediv__(self, other: float) -> mat3x3: ...
def __invert__(self) -> mat3x3: ...
@overload
def __matmul__(self, other: mat3x3) -> mat3x3: ...
@overload
def __matmul__(self, other: vec3) -> vec3: ...
@overload
def matmul(self, other: mat3x3) -> mat3x3: ...
@overload
def matmul(self, other: vec3) -> vec3: ...
def determinant(self) -> float: ...
def transpose(self) -> mat3x3: ...
def inverse(self) -> mat3x3: ...
def __invert__(self) -> mat3x3: ...
@staticmethod
def zeros() -> mat3x3: ...

View File

@ -267,21 +267,20 @@ namespace pkpy{
return VAR(self.m[i][j]);
});
vm->bind_method<2>(type, "__setitem__", [](VM* vm, ArgsView args){
PyMat3x3& self = _CAST(PyMat3x3&, args[0]);
Tuple& t = CAST(Tuple&, args[1]);
vm->bind__setitem__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* obj, PyObject* index, PyObject* value){
PyMat3x3& self = _CAST(PyMat3x3&, obj);
Tuple& t = CAST(Tuple&, index);
if(t.size() != 2){
vm->TypeError("Mat3x3.__setitem__ takes a tuple of 2 integers");
return vm->None;
return;
}
i64 i = CAST(i64, t[0]);
i64 j = CAST(i64, t[1]);
if(i < 0 || i >= 3 || j < 0 || j >= 3){
vm->IndexError("index out of range");
return vm->None;
return;
}
self.m[i][j] = CAST_F(args[2]);
return vm->None;
self.m[i][j] = CAST_F(value);
});
#define PROPERTY_FIELD(field) \
@ -307,56 +306,53 @@ namespace pkpy{
#undef PROPERTY_FIELD
vm->bind_method<1>(type, "__add__", [](VM* vm, ArgsView args){
PyMat3x3& self = _CAST(PyMat3x3&, args[0]);
PyMat3x3& other = CAST(PyMat3x3&, args[1]);
vm->bind__add__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* _0, PyObject* _1){
PyMat3x3& self = _CAST(PyMat3x3&, _0);
PyMat3x3& other = CAST(PyMat3x3&, _1);
return VAR_T(PyMat3x3, self + other);
});
vm->bind_method<1>(type, "__sub__", [](VM* vm, ArgsView args){
PyMat3x3& self = _CAST(PyMat3x3&, args[0]);
PyMat3x3& other = CAST(PyMat3x3&, args[1]);
vm->bind__sub__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* _0, PyObject* _1){
PyMat3x3& self = _CAST(PyMat3x3&, _0);
PyMat3x3& other = CAST(PyMat3x3&, _1);
return VAR_T(PyMat3x3, self - other);
});
vm->bind_method<1>(type, "__mul__", [](VM* vm, ArgsView args){
PyMat3x3& self = _CAST(PyMat3x3&, args[0]);
f64 other = CAST_F(args[1]);
vm->bind__mul__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* _0, PyObject* _1){
PyMat3x3& self = _CAST(PyMat3x3&, _0);
f64 other = CAST_F(_1);
return VAR_T(PyMat3x3, self * other);
});
vm->bind_method<1>(type, "__rmul__", [](VM* vm, ArgsView args){
PyMat3x3& self = _CAST(PyMat3x3&, args[0]);
f64 other = CAST_F(args[1]);
return VAR_T(PyMat3x3, self * other);
});
vm->bind_method<1>(type, "__truediv__", [](VM* vm, ArgsView args){
PyMat3x3& self = _CAST(PyMat3x3&, args[0]);
f64 other = CAST_F(args[1]);
vm->bind__truediv__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* _0, PyObject* _1){
PyMat3x3& self = _CAST(PyMat3x3&, _0);
f64 other = CAST_F(_1);
return VAR_T(PyMat3x3, self / other);
});
auto f_mm = [](VM* vm, ArgsView args){
PyMat3x3& self = _CAST(PyMat3x3&, args[0]);
if(is_non_tagged_type(args[1], PyMat3x3::_type(vm))){
PyMat3x3& other = _CAST(PyMat3x3&, args[1]);
vm->bind__matmul__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* _0, PyObject* _1){
PyMat3x3& self = _CAST(PyMat3x3&, _0);
if(is_non_tagged_type(_1, PyMat3x3::_type(vm))){
PyMat3x3& other = _CAST(PyMat3x3&, _1);
return VAR_T(PyMat3x3, self.matmul(other));
}
if(is_non_tagged_type(args[1], PyVec3::_type(vm))){
PyVec3& other = _CAST(PyVec3&, args[1]);
if(is_non_tagged_type(_1, PyVec3::_type(vm))){
PyVec3& other = _CAST(PyVec3&, _1);
return VAR_T(PyVec3, self.matmul(other));
}
vm->BinaryOptError("@");
return vm->None;
};
return vm->NotImplemented;
});
vm->bind_method<1>(type, "__matmul__", f_mm);
vm->bind_method<1>(type, "matmul", f_mm);
vm->bind_method<1>(type, "__eq__", [](VM* vm, ArgsView args){
PyMat3x3& self = _CAST(PyMat3x3&, args[0]);
if(is_non_tagged_type(args[1], PyMat3x3::_type(vm))){
PyMat3x3& other = _CAST(PyMat3x3&, args[1]);
vm->bind__eq__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* _0, PyObject* _1){
PyMat3x3& self = _CAST(PyMat3x3&, _0);
if(is_non_tagged_type(_1, PyMat3x3::_type(vm))){
PyMat3x3& other = _CAST(PyMat3x3&, _1);
return VAR(self == other);
}
return vm->NotImplemented;
@ -372,14 +368,6 @@ namespace pkpy{
return VAR_T(PyMat3x3, self.transpose());
});
vm->bind_method<0>(type, "inverse", [](VM* vm, ArgsView args){
PyMat3x3& self = _CAST(PyMat3x3&, args[0]);
Mat3x3 ret;
bool ok = self.inverse(ret);
if(!ok) vm->ValueError("matrix is not invertible");
return VAR_T(PyMat3x3, ret);
});
vm->bind__invert__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* obj){
PyMat3x3& self = _CAST(PyMat3x3&, obj);
Mat3x3 ret;