Skip to content

Commit 7c588b5

Browse files
committed
Use the c type of correct length (64 bit) now.
Make vectors and transforms hashable.
1 parent 8dd55fb commit 7c588b5

File tree

10 files changed

+187
-104
lines changed

10 files changed

+187
-104
lines changed

codegen/codegen_helper.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -117,9 +117,9 @@ def __init__(self, name: str, c_params: tuple[str], c_ret: str):
117117
self.params = []
118118
for p in c_params:
119119
match p:
120-
case "float" | "double":
120+
case "float" | "double" | "py_float":
121121
self.params.append(float | int)
122-
case "int" | "long":
122+
case "int" | "long" | "py_int":
123123
self.params.append(int)
124124
case "self":
125125
self.params.append(Self)
@@ -130,9 +130,9 @@ def __init__(self, name: str, c_params: tuple[str], c_ret: str):
130130
match c_ret:
131131
case None | "" | "void":
132132
self.ret = None
133-
case "float" | "double":
133+
case "float" | "double" | "py_float":
134134
self.ret = float
135-
case "int" | "long":
135+
case "int" | "long" | "py_int":
136136
self.ret = int
137137
case _:
138138
self.ret = c_ret

codegen/templates/_gdmath.pyx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#cython: cdivision=True
88
#cython: always_allow_keywords=True
99
#cython: optimize.use_switch=True
10+
##distutils: language = c++
1011

1112
cimport cython
1213

codegen/templates/common_utils.pyx

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,39 @@
11
#<TEMPLATE_BEGIN>
2-
from libc.math cimport fabs, isfinite
2+
from libc.math cimport fabsl, isfinite
3+
# from libcpp.bit cimport bit_cast, rotl
4+
5+
6+
ctypedef long long py_int
7+
ctypedef long double py_float
8+
39

410
DEF DEFAULT_RELATIVE_TOLERANCE = 1e-5
511
DEF DEFAULT_ABSOLUTE_TOLERANCE = 1e-14
612

7-
cdef inline bint is_close(double a, double b, double rel_tol = DEFAULT_RELATIVE_TOLERANCE, double abs_tol = DEFAULT_ABSOLUTE_TOLERANCE) noexcept:
8-
cdef double diff = fabs(a - b)
13+
14+
cdef inline bint is_close(py_float a, py_float b, py_float rel_tol = DEFAULT_RELATIVE_TOLERANCE, py_float abs_tol = DEFAULT_ABSOLUTE_TOLERANCE) noexcept:
15+
cdef py_float diff = fabsl(a - b)
916
if a == b:
1017
return True
1118
if not isfinite(a) or not isfinite(b):
1219
return False
13-
return diff <= fabs(rel_tol * a) or diff <= fabs(rel_tol * b) or diff <= fabs(abs_tol)
20+
return diff <= fabsl(rel_tol * a) or diff <= fabsl(rel_tol * b) or diff <= fabsl(abs_tol)
21+
22+
23+
# cdef inline py_int bitcast_float(py_float d) noexcept:
24+
# return bit_cast[py_int, py_float](d)
25+
#
26+
# cdef inline py_int rotl_ratio(py_int num, int i, int total) noexcept:
27+
# return rotl[py_int](num, 64 // total * i)
28+
29+
cdef union _bitcaster:
30+
py_float f
31+
py_int i
32+
33+
cdef inline py_int bitcast_float(py_float f) noexcept:
34+
return _bitcaster(f=f).i
35+
36+
cdef inline py_int rotl_ratio(py_int num, int i, int total) noexcept:
37+
cdef int shift = (sizeof(py_int)*8) / total * i
38+
return (num << shift) | (num >> (sizeof(py_int)*8 - shift))
1439
#<TEMPLATE_END>

codegen/templates/transform_2d.pyx

Lines changed: 52 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
cimport cython
22

33
cdef class Vec2:
4-
cdef double x, y
4+
cdef py_float x, y
55

66

77
#<TEMPLATE_BEGIN>
8-
from libc.math cimport atan2, sin, cos, sqrt, NAN
8+
from libc.math cimport atan2l, sinl, cosl, sqrtl, NAN
99

1010

1111
@cython.auto_pickle(True)
1212
@cython.freelist(1024)
1313
@cython.no_gc
1414
@cython.final
1515
cdef class Transform2D:
16-
cdef double xx, xy
17-
cdef double yx, yy
18-
cdef double ox, oy
16+
cdef py_float xx, xy
17+
cdef py_float yx, yy
18+
cdef py_float ox, oy
1919

2020

2121
cdef inline void identity(self) noexcept:
@@ -28,7 +28,7 @@ cdef class Transform2D:
2828
self.identity()
2929

3030
#<OVERLOAD>
31-
cdef void __init__(self, double xx, double xy, double yx, double yy, double ox, double oy) noexcept:
31+
cdef void __init__(self, py_float xx, py_float xy, py_float yx, py_float yy, py_float ox, py_float oy) noexcept:
3232
self.xx = xx
3333
self.xy = xy
3434
self.yx = yx
@@ -61,9 +61,9 @@ cdef class Transform2D:
6161
return t
6262

6363
@staticmethod
64-
def rotating(float rotation, /, Vec2 origin = None) -> Transform2D:
65-
cdef double c = cos(rotation)
66-
cdef double s = sin(rotation)
64+
def rotating(py_float rotation, /, Vec2 origin = None) -> Transform2D:
65+
cdef py_float c = cosl(rotation)
66+
cdef py_float s = sinl(rotation)
6767
cdef Transform2D t = Transform2D.__new__(Transform2D)
6868
t.xx = c
6969
t.xy = s
@@ -109,14 +109,25 @@ cdef class Transform2D:
109109
self.yx != (<Transform2D> other).yx or self.yy != (<Transform2D> other).yy or \
110110
self.ox != (<Transform2D> other).ox or self.oy != (<Transform2D> other).oy
111111

112-
def is_close(self, Transform2D other, /, double rel_tol = DEFAULT_RELATIVE_TOLERANCE, double abs_tol = DEFAULT_ABSOLUTE_TOLERANCE) -> bool:
112+
def is_close(self, Transform2D other, /, py_float rel_tol = DEFAULT_RELATIVE_TOLERANCE, py_float abs_tol = DEFAULT_ABSOLUTE_TOLERANCE) -> bool:
113113
return is_close(self.xx, other.xx, rel_tol, abs_tol) and \
114114
is_close(self.xy, other.xy, rel_tol, abs_tol) and \
115115
is_close(self.yx, other.yx, rel_tol, abs_tol) and \
116116
is_close(self.yy, other.yy, rel_tol, abs_tol) and \
117117
is_close(self.ox, other.ox, rel_tol, abs_tol) and \
118118
is_close(self.oy, other.oy, rel_tol, abs_tol)
119119

120+
#TODO: Better hashing
121+
def __hash__(self) -> py_int:
122+
cdef py_int h = 0
123+
h ^= bitcast_float(self.xx)
124+
h ^= rotl_ratio(bitcast_float(self.xy), 1, 6)
125+
h ^= rotl_ratio(bitcast_float(self.yx), 2, 6)
126+
h ^= rotl_ratio(bitcast_float(self.yy), 3, 6)
127+
h ^= rotl_ratio(bitcast_float(self.ox), 4, 6)
128+
h ^= rotl_ratio(bitcast_float(self.oy), 5, 6)
129+
return h
130+
120131
@property
121132
def x(self) -> Vec2:
122133
cdef Vec2 vec = Vec2.__new__(Vec2)
@@ -153,7 +164,7 @@ cdef class Transform2D:
153164
self.ox = value.x
154165
self.oy = value.y
155166

156-
def __getitem__(self, int item) -> Vec2:
167+
def __getitem__(self, py_int item) -> Vec2:
157168
cdef Vec2 vec = Vec2.__new__(Vec2)
158169

159170
if item == 0:
@@ -170,7 +181,7 @@ cdef class Transform2D:
170181

171182
return vec
172183

173-
def __setitem__(self, int key, Vec2 value) -> None:
184+
def __setitem__(self, py_int key, Vec2 value) -> None:
174185
if key == 0:
175186
self.xx = value.x
176187
self.xy = value.y
@@ -183,19 +194,19 @@ cdef class Transform2D:
183194
else:
184195
raise IndexError(key)
185196

186-
def __len__(self) -> int:
197+
def __len__(self) -> py_int:
187198
return 3
188199

189-
cdef inline double tdotx(self, double x, double y) noexcept:
200+
cdef inline py_float tdotx(self, py_float x, py_float y) noexcept:
190201
return x * self.xx + y * self.yx
191202

192-
cdef inline double mulx(self, double x, double y) noexcept:
203+
cdef inline py_float mulx(self, py_float x, py_float y) noexcept:
193204
return self.tdotx(x, y) + self.ox
194205

195-
cdef inline double tdoty(self, double x, double y) noexcept:
206+
cdef inline py_float tdoty(self, py_float x, py_float y) noexcept:
196207
return x * self.xy + y * self.yy
197208

198-
cdef inline double muly(self, double x, double y) noexcept:
209+
cdef inline py_float muly(self, py_float x, py_float y) noexcept:
199210
return self.tdoty(x, y) + self.oy
200211

201212
def __mul__(self, Vec2 other) -> Vec2:
@@ -242,15 +253,15 @@ cdef class Transform2D:
242253
self.ox = other.mulx(self.ox, self.oy)
243254
self.oy = other.muly(self.ox, self.oy)
244255

245-
cdef inline double _determinant(self) noexcept:
256+
cdef inline py_float _determinant(self) noexcept:
246257
return self.xx * self.yy - self.xy * self.yx
247258

248259
@property
249-
def determinant(self) -> float:
260+
def determinant(self) -> py_float:
250261
return self._determinant()
251262

252263
def __invert__(self) -> Transform2D:
253-
cdef double i_det = 1.0 / self._determinant()
264+
cdef py_float i_det = 1.0 / self._determinant()
254265
cdef Transform2D t = Transform2D.__new__(Transform2D)
255266
t.xx = self.yy * +i_det
256267
t.xy = self.xy * -i_det
@@ -261,45 +272,45 @@ cdef class Transform2D:
261272
return t
262273

263274

264-
cdef inline double get_rotation(self) noexcept:
265-
return atan2(self.xy, self.xx)
275+
cdef inline py_float get_rotation(self) noexcept:
276+
return atan2l(self.xy, self.xx)
266277

267-
cdef inline void set_rotation(self, double rotation) noexcept:
268-
cdef double scale_x = self.get_scale_x()
269-
cdef double scale_y = self.get_scale_y()
270-
cdef double c = cos(rotation)
271-
cdef double s = sin(rotation)
278+
cdef inline void set_rotation(self, py_float rotation) noexcept:
279+
cdef py_float scale_x = self.get_scale_x()
280+
cdef py_float scale_y = self.get_scale_y()
281+
cdef py_float c = cosl(rotation)
282+
cdef py_float s = sinl(rotation)
272283
self.xx = c
273284
self.xy = s
274285
self.yx = -s
275286
self.yy = c
276287
self.set_scale_x(scale_x)
277288
self.set_scale_y(scale_y)
278289

279-
cdef inline double get_scale_x(self) noexcept:
280-
return sqrt(self.xx * self.xx + self.xy * self.xy)
290+
cdef inline py_float get_scale_x(self) noexcept:
291+
return sqrtl(self.xx * self.xx + self.xy * self.xy)
281292

282-
cdef inline double get_scale_y(self) noexcept:
283-
cdef double det = self._determinant()
284-
cdef double det_sign = 1.0 if det > 0.0 else -1.0 if det < 0.0 else 0.0 if det == 0.0 else NAN
285-
return sqrt(self.yx * self.yx + self.yy * self.yy) * det_sign
293+
cdef inline py_float get_scale_y(self) noexcept:
294+
cdef py_float det = self._determinant()
295+
cdef py_float det_sign = 1.0 if det > 0.0 else -1.0 if det < 0.0 else 0.0 if det == 0.0 else NAN
296+
return sqrtl(self.yx * self.yx + self.yy * self.yy) * det_sign
286297

287-
cdef inline void set_scale_x(self, double value) noexcept:
288-
cdef double m = value / sqrt(self.xx * self.xx + self.xy * self.xy)
298+
cdef inline void set_scale_x(self, py_float value) noexcept:
299+
cdef py_float m = value / sqrtl(self.xx * self.xx + self.xy * self.xy)
289300
self.xx *= m
290301
self.xy *= m
291302

292-
cdef inline void set_scale_y(self, double value) noexcept:
293-
cdef double m = value / sqrt(self.yx * self.yx + self.yy * self.yy)
303+
cdef inline void set_scale_y(self, py_float value) noexcept:
304+
cdef py_float m = value / sqrtl(self.yx * self.yx + self.yy * self.yy)
294305
self.yx *= m
295306
self.yy *= m
296307

297308
@property
298-
def rotation(self) -> float:
309+
def rotation(self) -> py_float:
299310
return self.get_rotation()
300311

301312
@rotation.setter
302-
def rotation(self, float value) -> None:
313+
def rotation(self, py_float value) -> None:
303314
self.set_rotation(value)
304315

305316
# @property
@@ -330,11 +341,11 @@ cdef class Transform2D:
330341
t.translate_ip(translation)
331342
return t
332343

333-
def rotate_ip(self, float rotation, /) -> Transform2D:
344+
def rotate_ip(self, py_float rotation, /) -> Transform2D:
334345
self.__imul__(Transform2D.rotation(rotation))
335346
return self
336347

337-
def rotated(self, float rotation, /) -> Transform2D:
348+
def rotated(self, py_float rotation, /) -> Transform2D:
338349
cdef Transform2D t = self.copy()
339350
t.rotate_ip(rotation)
340351
return t

0 commit comments

Comments
 (0)