From 019e0f974ac3f83a043d63b377b40b6ec5bb0fda Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Mon, 16 Sep 2024 02:07:44 +0800 Subject: [PATCH] update `linalg` --- include/pocketpy/linalg.h | 26 ++- include/typings/linalg.pyi | 57 +++--- src/modules/linalg.c | 367 +++++++++++++++++++++---------------- src/public/internal.c | 5 +- tests/80_linalg.py | 9 + 5 files changed, 257 insertions(+), 207 deletions(-) diff --git a/include/pocketpy/linalg.h b/include/pocketpy/linalg.h index 3b5d1893..a77907f3 100644 --- a/include/pocketpy/linalg.h +++ b/include/pocketpy/linalg.h @@ -1,25 +1,23 @@ #pragma once -typedef struct c11_vec2i { - int x; - int y; +typedef union c11_vec2i { + struct { int x, y; }; + int data[2]; } c11_vec2i; -typedef struct c11_vec3i { - int x; - int y; - int z; +typedef union c11_vec3i { + struct { int x, y, z; }; + int data[3]; } c11_vec3i; -typedef struct c11_vec2 { - float x; - float y; +typedef union c11_vec2 { + struct { float x, y; }; + float data[2]; } c11_vec2; -typedef struct c11_vec3 { - float x; - float y; - float z; +typedef union c11_vec3 { + struct { float x, y, z; }; + float data[3]; } c11_vec3; typedef union c11_mat3x3 { diff --git a/include/typings/linalg.pyi b/include/typings/linalg.pyi index 9f7e9192..c0ada0c7 100644 --- a/include/typings/linalg.pyi +++ b/include/typings/linalg.pyi @@ -1,9 +1,23 @@ from typing import overload -class vec2: - ZERO = vec2(0, 0) - ONE = vec2(1, 1) +class _vecD[T]: + ONE: T + ZERO: T + def __add__(self, other: T) -> T: ... + def __sub__(self, other: T) -> T: ... + @overload + def __mul__(self, other: float) -> T: ... + @overload + def __mul__(self, other: T) -> T: ... + def __truediv__(self, other: float) -> T: ... + + def dot(self, other: T) -> float: ... + def length(self) -> float: ... + def length_squared(self) -> float: ... + def normalize(self) -> T: ... + +class vec2(_vecD['vec2']): @property def x(self) -> float: ... @property @@ -11,25 +25,10 @@ class vec2: def with_x(self, x: float) -> vec2: ... def with_y(self, y: float) -> vec2: ... + def with_z(self, z: float) -> vec3: ... def __init__(self, x: float, y: float) -> None: ... - def __repr__(self) -> str: ... - def __eq__(self, other: vec2) -> bool: ... - def __ne__(self, other: vec2) -> bool: ... - def __add__(self, other: vec2) -> vec2: ... - def __sub__(self, other: vec2) -> vec2: ... - @overload - def __mul__(self, other: float) -> vec2: ... - @overload - def __mul__(self, other: vec2) -> 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: ... @staticmethod @@ -52,9 +51,6 @@ class vec2: class mat3x3: def __init__(self, _11, _12, _13, _21, _22, _23, _31, _32, _33) -> None: ... - def __repr__(self) -> str: ... - def __eq__(self, other: mat3x3) -> bool: ... - def __ne__(self, other: mat3x3) -> bool: ... def __getitem__(self, index: tuple[int, int]) -> float: ... def __setitem__(self, index: tuple[int, int], value: float) -> None: ... @@ -104,9 +100,6 @@ class vec2i: def with_y(self, y: int) -> vec2i: ... def __init__(self, x: int, y: int) -> None: ... - def __repr__(self) -> str: ... - def __eq__(self, other: vec2i) -> bool: ... - def __ne__(self, other: vec2i) -> bool: ... class vec3i: @@ -122,12 +115,9 @@ class vec3i: def with_z(self, z: int) -> vec3i: ... def __init__(self, x: int, y: int, z: int) -> None: ... - def __repr__(self) -> str: ... - def __eq__(self, other: vec3i) -> bool: ... - def __ne__(self, other: vec3i) -> bool: ... -class vec3: +class vec3(_vecD['vec3']): @property def x(self) -> float: ... @property @@ -135,11 +125,14 @@ class vec3: @property def z(self) -> float: ... + @property + def xy(self) -> vec2: ... + def with_x(self, x: float) -> vec3: ... def with_y(self, y: float) -> vec3: ... def with_z(self, z: float) -> vec3: ... def __init__(self, x: float, y: float, z: float) -> None: ... - def __repr__(self) -> str: ... - def __eq__(self, other: vec3) -> bool: ... - def __ne__(self, other: vec3) -> bool: ... + + + diff --git a/src/modules/linalg.c b/src/modules/linalg.c index 83b08c29..a8ecf17f 100644 --- a/src/modules/linalg.c +++ b/src/modules/linalg.c @@ -88,77 +88,152 @@ c11_mat3x3* py_tomat3x3(py_Ref self) { return py_touserdata(self); } -static bool vec2__new__(int argc, py_Ref argv) { - PY_CHECK_ARGC(3); - py_f64 x, y; - if(!py_castfloat(&argv[1], &x) || !py_castfloat(&argv[2], &y)) return false; - py_newvec2(py_retval(), (c11_vec2){x, y}); - return true; -} - -static bool vec2__add__(int argc, py_Ref argv) { - PY_CHECK_ARGC(2); - if(argv[1].type != tp_vec2) { - py_newnotimplemented(py_retval()); - return true; +#define DEF_VECTOR_OPS(D) \ + static bool vec##D##__new__(int argc, py_Ref argv) { \ + PY_CHECK_ARGC(D + 1); \ + c11_vec##D res; \ + for(int i = 0; i < D; i++) { \ + if(!py_castfloat32(&argv[i + 1], &res.data[i])) return false; \ + } \ + py_newvec##D(py_retval(), res); \ + return true; \ + } \ + static bool vec##D##__add__(int argc, py_Ref argv) { \ + PY_CHECK_ARGC(2); \ + if(argv[1].type != tp_vec##D) { \ + py_newnotimplemented(py_retval()); \ + return true; \ + } \ + c11_vec##D a = py_tovec##D(&argv[0]); \ + c11_vec##D b = py_tovec##D(&argv[1]); \ + c11_vec##D res; \ + for(int i = 0; i < D; i++) \ + res.data[i] = a.data[i] + b.data[i]; \ + py_newvec##D(py_retval(), res); \ + return true; \ + } \ + static bool vec##D##__sub__(int argc, py_Ref argv) { \ + PY_CHECK_ARGC(2); \ + if(argv[1].type != tp_vec##D) { \ + py_newnotimplemented(py_retval()); \ + return true; \ + } \ + c11_vec##D a = py_tovec##D(&argv[0]); \ + c11_vec##D b = py_tovec##D(&argv[1]); \ + c11_vec##D res; \ + for(int i = 0; i < D; i++) \ + res.data[i] = a.data[i] - b.data[i]; \ + py_newvec##D(py_retval(), res); \ + return true; \ + } \ + static bool vec##D##__mul__(int argc, py_Ref argv) { \ + PY_CHECK_ARGC(2); \ + c11_vec##D res; \ + switch(argv[1].type) { \ + case tp_vec##D: { \ + c11_vec##D a = py_tovec##D(&argv[0]); \ + c11_vec##D b = py_tovec##D(&argv[1]); \ + for(int i = 0; i < D; i++) \ + res.data[i] = a.data[i] * b.data[i]; \ + py_newvec##D(py_retval(), res); \ + return true; \ + } \ + case tp_int: { \ + c11_vec##D a = py_tovec##D(&argv[0]); \ + py_i64 b = argv[1]._i64; \ + for(int i = 0; i < D; i++) \ + res.data[i] = a.data[i] * b; \ + py_newvec##D(py_retval(), res); \ + return true; \ + } \ + case tp_float: { \ + c11_vec##D a = py_tovec##D(&argv[0]); \ + py_f64 b = argv[1]._f64; \ + for(int i = 0; i < D; i++) \ + res.data[i] = a.data[i] * b; \ + py_newvec##D(py_retval(), res); \ + return true; \ + } \ + default: py_newnotimplemented(py_retval()); return true; \ + } \ + } \ + static bool vec##D##__truediv__(int argc, py_Ref argv) { \ + PY_CHECK_ARGC(2); \ + float divisor; \ + if(!py_castfloat32(&argv[1], &divisor)) { \ + py_clearexc(NULL); \ + py_newnotimplemented(py_retval()); \ + return true; \ + } \ + c11_vec##D res; \ + c11_vec##D a = py_tovec##D(&argv[0]); \ + for(int i = 0; i < D; i++) \ + res.data[i] = a.data[i] / divisor; \ + py_newvec##D(py_retval(), res); \ + return true; \ + } \ + static bool vec##D##__eq__(int argc, py_Ref argv) { \ + PY_CHECK_ARGC(2); \ + if(argv[1].type != tp_vec##D) { \ + py_newnotimplemented(py_retval()); \ + return true; \ + } \ + c11_vec##D lhs = py_tovec##D(&argv[0]); \ + c11_vec##D rhs = py_tovec##D(&argv[1]); \ + bool ok = true; \ + for(int i = 0; i < D; i++) { \ + if(!isclose(lhs.data[i], rhs.data[i])) ok = false; \ + } \ + py_newbool(py_retval(), ok); \ + return true; \ + } \ + DEFINE_BOOL_NE(vec##D, vec##D##__eq__) \ + static bool vec##D##_length(int argc, py_Ref argv) { \ + PY_CHECK_ARGC(1); \ + c11_vec##D v = py_tovec##D(argv); \ + float sum = 0; \ + for(int i = 0; i < D; i++) \ + sum += v.data[i] * v.data[i]; \ + py_newfloat(py_retval(), sqrtf(sum)); \ + return true; \ + } \ + static bool vec##D##_length_squared(int argc, py_Ref argv) { \ + PY_CHECK_ARGC(1); \ + c11_vec##D v = py_tovec##D(argv); \ + float sum = 0; \ + for(int i = 0; i < D; i++) \ + sum += v.data[i] * v.data[i]; \ + py_newfloat(py_retval(), sum); \ + return true; \ + } \ + static bool vec##D##_dot(int argc, py_Ref argv) { \ + PY_CHECK_ARGC(2); \ + PY_CHECK_ARG_TYPE(1, tp_vec##D); \ + c11_vec##D a = py_tovec##D(&argv[0]); \ + c11_vec##D b = py_tovec##D(&argv[1]); \ + float sum = 0; \ + for(int i = 0; i < D; i++) \ + sum += a.data[i] * b.data[i]; \ + py_newfloat(py_retval(), sum); \ + return true; \ + } \ + static bool vec##D##_normalize(int argc, py_Ref argv) { \ + PY_CHECK_ARGC(1); \ + c11_vec##D self = py_tovec##D(argv); \ + float len = 0; \ + for(int i = 0; i < D; i++) \ + len += self.data[i] * self.data[i]; \ + if(isclose(len, 0)) return ZeroDivisionError("cannot normalize zero vector"); \ + len = sqrtf(len); \ + c11_vec##D res; \ + for(int i = 0; i < D; i++) \ + res.data[i] = self.data[i] / len; \ + py_newvec##D(py_retval(), res); \ + return true; \ } - c11_vec2 res; - res.x = argv[0]._vec2.x + argv[1]._vec2.x; - res.y = argv[0]._vec2.y + argv[1]._vec2.y; - py_newvec2(py_retval(), res); - return true; -} -static bool vec2__sub__(int argc, py_Ref argv) { - PY_CHECK_ARGC(2); - if(argv[1].type != tp_vec2) { - py_newnotimplemented(py_retval()); - return true; - } - c11_vec2 res; - res.x = argv[0]._vec2.x - argv[1]._vec2.x; - res.y = argv[0]._vec2.y - argv[1]._vec2.y; - py_newvec2(py_retval(), res); - return true; -} - -static bool vec2__mul__(int argc, py_Ref argv) { - PY_CHECK_ARGC(2); - c11_vec2 res; - switch(argv[1].type) { - case tp_vec2: - res.x = argv[0]._vec2.x * argv[1]._vec2.x; - res.y = argv[0]._vec2.y * argv[1]._vec2.y; - py_newvec2(py_retval(), res); - return true; - case tp_int: - res.x = argv[0]._vec2.x * argv[1]._i64; - res.y = argv[0]._vec2.y * argv[1]._i64; - py_newvec2(py_retval(), res); - return true; - case tp_float: - res.x = argv[0]._vec2.x * argv[1]._f64; - res.y = argv[0]._vec2.y * argv[1]._f64; - py_newvec2(py_retval(), res); - return true; - default: py_newnotimplemented(py_retval()); return true; - } -} - -static bool vec2__truediv__(int argc, py_Ref argv) { - PY_CHECK_ARGC(2); - float divisor; - if(!py_castfloat32(&argv[1], &divisor)) { - py_clearexc(NULL); - py_newnotimplemented(py_retval()); - return true; - } - c11_vec2 res; - res.x = argv[0]._vec2.x / divisor; - res.y = argv[0]._vec2.y / divisor; - py_newvec2(py_retval(), res); - return true; -} +DEF_VECTOR_OPS(2) +DEF_VECTOR_OPS(3) static bool vec2__repr__(int argc, py_Ref argv) { PY_CHECK_ARGC(1); @@ -168,64 +243,6 @@ static bool vec2__repr__(int argc, py_Ref argv) { return true; } -static bool vec2__eq__(int argc, py_Ref argv) { - PY_CHECK_ARGC(2); - if(argv[1].type != tp_vec2) { - py_newnotimplemented(py_retval()); - return true; - } - c11_vec2 lhs = argv[0]._vec2; - c11_vec2 rhs = argv[1]._vec2; - py_newbool(py_retval(), isclose(lhs.x, rhs.x) && isclose(lhs.y, rhs.y)); - return true; -} - -DEFINE_BOOL_NE(vec2, vec2__eq__) - -static bool vec2_dot(int argc, py_Ref argv) { - PY_CHECK_ARGC(2); - PY_CHECK_ARG_TYPE(1, tp_vec2); - float x = argv[0]._vec2.x * argv[1]._vec2.x; - float y = argv[0]._vec2.y * argv[1]._vec2.y; - py_newfloat(py_retval(), x + y); - return true; -} - -static bool vec2_cross(int argc, py_Ref argv) { - PY_CHECK_ARGC(2); - PY_CHECK_ARG_TYPE(1, tp_vec2); - float x = argv[0]._vec2.x * argv[1]._vec2.y; - float y = argv[0]._vec2.y * argv[1]._vec2.x; - py_newfloat(py_retval(), x - y); - return true; -} - -static bool vec2_length(int argc, py_Ref argv) { - PY_CHECK_ARGC(1); - float x = argv[0]._vec2.x; - float y = argv[0]._vec2.y; - py_newfloat(py_retval(), sqrtf(x * x + y * y)); - return true; -} - -static bool vec2_length_squared(int argc, py_Ref argv) { - PY_CHECK_ARGC(1); - float x = argv[0]._vec2.x; - float y = argv[0]._vec2.y; - py_newfloat(py_retval(), x * x + y * y); - return true; -} - -static bool vec2_normalize(int argc, py_Ref argv) { - PY_CHECK_ARGC(1); - float x = argv[0]._vec2.x; - float y = argv[0]._vec2.y; - float len = sqrtf(x * x + y * y); - if(isclose(len, 0)) return ZeroDivisionError("cannot normalize zero vector"); - py_newvec2(py_retval(), (c11_vec2){x / len, y / len}); - return true; -} - static bool vec2_rotate(int argc, py_Ref argv) { PY_CHECK_ARGC(2); py_f64 radians; @@ -253,9 +270,9 @@ static bool vec2_angle_STATIC(int argc, py_Ref argv) { static bool vec2_smoothdamp_STATIC(int argc, py_Ref argv) { PY_CHECK_ARGC(6); - PY_CHECK_ARG_TYPE(0, tp_vec2); // current: vec2 - PY_CHECK_ARG_TYPE(1, tp_vec2); // target: vec2 - PY_CHECK_ARG_TYPE(2, tp_vec2); // current_velocity: vec2 + PY_CHECK_ARG_TYPE(0, tp_vec2); // current: vec2 + PY_CHECK_ARG_TYPE(1, tp_vec2); // target: vec2 + PY_CHECK_ARG_TYPE(2, tp_vec2); // current_velocity: vec2 float smoothTime; if(!py_castfloat32(&argv[3], &smoothTime)) return false; @@ -318,7 +335,10 @@ static bool vec2_smoothdamp_STATIC(int argc, py_Ref argv) { py_Ref ret = py_retval(); py_newtuple(ret, 2); - py_newvec2(py_tuple_getitem(ret, 0), (c11_vec2){output_x, output_y}); + py_newvec2(py_tuple_getitem(ret, 0), + (c11_vec2){ + {output_x, output_y} + }); py_newvec2(py_tuple_getitem(ret, 1), currentVelocity); return true; } @@ -326,6 +346,17 @@ static bool vec2_smoothdamp_STATIC(int argc, py_Ref argv) { DEFINE_VEC_FIELD(vec2, float, py_f64, x) DEFINE_VEC_FIELD(vec2, float, py_f64, y) +static bool vec2__with_z(int argc, py_Ref argv) { + PY_CHECK_ARGC(2); + float z; + if(!py_castfloat32(&argv[1], &z)) return false; + c11_vec3 v = { + {argv->_vec2.x, argv->_vec2.y, z} + }; + py_newvec3(py_retval(), v); + return true; +} + /* mat3x3 */ static bool mat3x3__new__(int argc, py_Ref argv) { PY_CHECK_ARGC(10); @@ -649,7 +680,10 @@ static bool vec2i__new__(int argc, py_Ref argv) { PY_CHECK_ARGC(3); PY_CHECK_ARG_TYPE(1, tp_int); PY_CHECK_ARG_TYPE(2, tp_int); - py_newvec2i(py_retval(), (c11_vec2i){argv[1]._i64, argv[2]._i64}); + py_newvec2i(py_retval(), + (c11_vec2i){ + {argv[1]._i64, argv[2]._i64} + }); return true; } @@ -685,7 +719,10 @@ static bool vec3i__new__(int argc, py_Ref argv) { PY_CHECK_ARG_TYPE(1, tp_int); PY_CHECK_ARG_TYPE(2, tp_int); PY_CHECK_ARG_TYPE(3, tp_int); - py_newvec3i(py_retval(), (c11_vec3i){argv[1]._i64, argv[2]._i64, argv[3]._i64}); + py_newvec3i(py_retval(), + (c11_vec3i){ + {argv[1]._i64, argv[2]._i64, argv[3]._i64} + }); return true; } @@ -717,15 +754,6 @@ DEFINE_VEC_FIELD(vec3i, int, py_i64, y) DEFINE_VEC_FIELD(vec3i, int, py_i64, z) /* vec3 */ -static bool vec3__new__(int argc, py_Ref argv) { - PY_CHECK_ARGC(4); - py_f64 x, y, z; - if(!py_castfloat(&argv[1], &x) || !py_castfloat(&argv[2], &y) || !py_castfloat(&argv[3], &z)) - return false; - py_newvec3(py_retval(), (c11_vec3){x, y, z}); - return true; -} - static bool vec3__repr__(int argc, py_Ref argv) { PY_CHECK_ARGC(1); c11_vec3 data = py_tovec3(argv); @@ -735,25 +763,20 @@ static bool vec3__repr__(int argc, py_Ref argv) { return true; } -static bool vec3__eq__(int argc, py_Ref argv) { - PY_CHECK_ARGC(2); - if(argv[1].type != tp_vec3) { - py_newnotimplemented(py_retval()); - return true; - } - c11_vec3 lhs = py_tovec3(argv); - c11_vec3 rhs = py_tovec3(&argv[1]); - py_newbool(py_retval(), - isclose(lhs.x, rhs.x) && isclose(lhs.y, rhs.y) && isclose(lhs.z, rhs.z)); - return true; -} - -DEFINE_BOOL_NE(vec3, vec3__eq__) - DEFINE_VEC_FIELD(vec3, float, py_f64, x) DEFINE_VEC_FIELD(vec3, float, py_f64, y) DEFINE_VEC_FIELD(vec3, float, py_f64, z) +static bool vec3__xy(int argc, py_Ref argv) { + PY_CHECK_ARGC(1); + c11_vec3 data = py_tovec3(argv); + c11_vec2 res = { + {data.x, data.y} + }; + py_newvec2(py_retval(), res); + return true; +} + void pk__add_module_linalg() { py_Ref mod = py_newmodule("linalg"); @@ -785,14 +808,19 @@ void pk__add_module_linalg() { py_bindmagic(vec2, __eq__, vec2__eq__); py_bindmagic(vec2, __ne__, vec2__ne__); py_bindmethod(vec2, "dot", vec2_dot); - py_bindmethod(vec2, "cross", vec2_cross); py_bindmethod(vec2, "length", vec2_length); py_bindmethod(vec2, "length_squared", vec2_length_squared); py_bindmethod(vec2, "normalize", vec2_normalize); py_bindmethod(vec2, "rotate", vec2_rotate); - py_newvec2(py_emplacedict(py_tpobject(vec2), py_name("ZERO")), (c11_vec2){0, 0}); - py_newvec2(py_emplacedict(py_tpobject(vec2), py_name("ONE")), (c11_vec2){1, 1}); + py_newvec2(py_emplacedict(py_tpobject(vec2), py_name("ZERO")), + (c11_vec2){ + {0, 0} + }); + py_newvec2(py_emplacedict(py_tpobject(vec2), py_name("ONE")), + (c11_vec2){ + {1, 1} + }); py_bindmethod(vec2, "angle", vec2_angle_STATIC); py_bindmethod(vec2, "smooth_damp", vec2_smoothdamp_STATIC); @@ -801,6 +829,7 @@ void pk__add_module_linalg() { py_bindproperty(vec2, "y", vec2__y, NULL); py_bindmethod(vec2, "with_x", vec2__with_x); py_bindmethod(vec2, "with_y", vec2__with_y); + py_bindmethod(vec2, "with_z", vec2__with_z); /* mat3x3 */ py_bindmagic(mat3x3, __new__, mat3x3__new__); @@ -851,13 +880,31 @@ void pk__add_module_linalg() { /* vec3 */ py_bindmagic(vec3, __new__, vec3__new__); + py_bindmagic(vec3, __add__, vec3__add__); + py_bindmagic(vec3, __sub__, vec3__sub__); + py_bindmagic(vec3, __mul__, vec3__mul__); + py_bindmagic(vec3, __truediv__, vec3__truediv__); py_bindmagic(vec3, __repr__, vec3__repr__); py_bindmagic(vec3, __eq__, vec3__eq__); py_bindmagic(vec3, __ne__, vec3__ne__); + py_bindmethod(vec3, "dot", vec3_dot); + py_bindmethod(vec3, "length", vec3_length); + py_bindmethod(vec3, "length_squared", vec3_length_squared); + py_bindmethod(vec3, "normalize", vec3_normalize); py_bindproperty(vec3, "x", vec3__x, NULL); py_bindproperty(vec3, "y", vec3__y, NULL); py_bindproperty(vec3, "z", vec3__z, NULL); + py_bindproperty(vec3, "xy", vec3__xy, NULL); py_bindmethod(vec3, "with_x", vec3__with_x); py_bindmethod(vec3, "with_y", vec3__with_y); py_bindmethod(vec3, "with_z", vec3__with_z); + + py_newvec3(py_emplacedict(py_tpobject(vec3), py_name("ZERO")), + (c11_vec3){ + {0, 0, 0} + }); + py_newvec3(py_emplacedict(py_tpobject(vec3), py_name("ONE")), + (c11_vec3){ + {1, 1, 1} + }); } \ No newline at end of file diff --git a/src/public/internal.c b/src/public/internal.c index 0480388e..c1f8dcd4 100644 --- a/src/public/internal.c +++ b/src/public/internal.c @@ -136,7 +136,10 @@ bool py_callcfunc(py_CFunction f, int argc, py_Ref argv) { c11__abort( "py_CFunction returns nothing! Did you forget to call `py_newnone(py_retval())`?"); } - if(py_checkexc(true)) { c11__abort("py_CFunction returns `true` but an exception is set!"); } + if(py_checkexc(true)) { + const char* name = py_tpname(pk_current_vm->curr_exception.type); + c11__abort("py_CFunction returns `true`, but `%s` is set!", name); + } return true; } #endif diff --git a/tests/80_linalg.py b/tests/80_linalg.py index edb135d3..287034b2 100644 --- a/tests/80_linalg.py +++ b/tests/80_linalg.py @@ -360,3 +360,12 @@ assert a == vec3i(1, 2, 3) assert a.with_x(2) == vec3i(2, 2, 3) assert a.with_y(3) == vec3i(1, 3, 3) assert a.with_z(4) == vec3i(1, 2, 4) + +# test vec2.with_z +assert vec2(1, 2).with_z(3) == vec3(1, 2, 3) +# test vec3.xy +assert vec3(1, 2, 3).xy == vec2(1, 2) +# test vec3.ONE +assert vec3.ONE == vec3(1, 1, 1) +# test vec3.ZERO +assert vec3.ZERO == vec3(0, 0, 0) \ No newline at end of file