Skip to content

Commit 1937fa3

Browse files
committed
Neater sint()/cost() functions
This is borrowed from the way Manifold does its `sind()`/`cosd()` functions.
1 parent 56214eb commit 1937fa3

File tree

4 files changed

+40
-48
lines changed

4 files changed

+40
-48
lines changed

src/flitter/model.pxd

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ from cpython.unicode cimport PyUnicode_DATA, PyUnicode_GET_LENGTH, PyUnicode_KIN
66

77

88
cdef double sint(double t) noexcept nogil
9-
cdef double cost(double t) noexcept nogil
9+
10+
cdef inline double cost(double t) noexcept nogil:
11+
return sint(t + 0.25)
1012

1113

1214
# SplitMix64 algorithm [http://xoshiro.di.unimi.it/splitmix64.c]

src/flitter/model.pyx

Lines changed: 16 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import cython
44
import numpy as np
55

6-
from libc.math cimport isnan, isinf, floor as c_floor, ceil as c_ceil, abs as c_abs, round as c_round, sqrt, sin, cos, acos, isnan
6+
from libc.math cimport isnan, isinf, floor as c_floor, ceil as c_ceil, abs as c_abs, round as c_round, sqrt, sin, cos, acos, isnan, remquo
77
from cpython.object cimport PyObject
88
from cpython.ref cimport Py_INCREF
99
from cpython.bool cimport PyBool_FromLong
@@ -18,8 +18,7 @@ from cpython.tuple cimport PyTuple_New, PyTuple_GET_SIZE, PyTuple_GET_ITEM, PyTu
1818

1919

2020
cdef uint64_t HASH_START = 0xe220a8397b1dcdaf
21-
cdef double Pi = 3.141592653589793115997963468544185161590576171875
22-
cdef double Tau = 6.283185307179586231995926937088370323181152343750
21+
cdef double HalfPi = 1.5707963267948965579989817342720925807952880859375
2322
cdef double NaN = float("nan")
2423
cdef uint64_t SymbolPrefix = <uint64_t>(0xffe0_0000_0000_0000)
2524
cdef frozenset EmptySet = frozenset()
@@ -30,30 +29,21 @@ cdef dict SymbolTable = {}
3029
cdef dict ReverseSymbolTable = {}
3130

3231

32+
@cython.cdivision(True)
3333
cdef double sint(double t) noexcept nogil:
34-
t = t - c_floor(t)
35-
if t == 0:
36-
return 0
37-
if t == 0.25:
38-
return 1
39-
if t == 0.5:
40-
return 0
41-
if t == 0.75:
42-
return -1
43-
return sin(Tau * t)
44-
45-
46-
cdef double cost(double t) noexcept nogil:
47-
t = t - c_floor(t)
48-
if t == 0:
49-
return 1
50-
if t == 0.25:
51-
return 0
52-
if t == 0.5:
53-
return -1
54-
if t == 0.75:
55-
return 0
56-
return cos(Tau * t)
34+
if t < 0:
35+
return -sint(-t)
36+
cdef int q
37+
cdef double r = remquo(t * 4.0, 1.0, &q) * HalfPi
38+
q = q & 0x3
39+
if q == 0:
40+
return sin(r)
41+
elif q == 1:
42+
return cos(r)
43+
elif q == 2:
44+
return -sin(r)
45+
else:
46+
return -cos(r)
5747

5848

5949
cdef inline int64_t vector_compare(Vector left, Vector right) noexcept:

tests/test_functions.py

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -496,11 +496,11 @@ def test_cos(self):
496496
self.assertEqual(cosv(null), null)
497497
self.assertEqual(cosv(Vector('hello')), null)
498498
self.assertEqual(cosv(Vector([0, 0.25, 0.5, 0.75, 1])), Vector([1, 0, -1, 0, 1]))
499-
theta = Vector.range(0.001, 1, 0.01)
499+
theta = Vector.range(0, 1, 0.01)
500500
values = [math.cos(th) for th in theta*Tau]
501501
for i in range(len(values)):
502-
self.assertEqual(cosv(theta.item(i)), values[i])
503-
self.assertEqual(cosv(theta), values)
502+
self.assertAlmostEqual(cosv(theta.item(i)), values[i])
503+
self.assertAllAlmostEqual(cosv(theta), values)
504504

505505
def test_acos(self):
506506
self.assertEqual(acosv(null), null)
@@ -515,11 +515,11 @@ def test_sin(self):
515515
self.assertEqual(sinv(null), null)
516516
self.assertEqual(sinv(Vector('hello')), null)
517517
self.assertEqual(sinv(Vector([0, 0.25, 0.5, 0.75, 1])), Vector([0, 1, 0, -1, 0]))
518-
theta = Vector.range(0.001, 1, 0.01)
518+
theta = Vector.range(0, 1, 0.01)
519519
values = [math.sin(th) for th in theta*Tau]
520520
for i in range(len(values)):
521-
self.assertEqual(sinv(theta.item(i)), values[i])
522-
self.assertEqual(sinv(theta), values)
521+
self.assertAlmostEqual(sinv(theta.item(i)), values[i])
522+
self.assertAllAlmostEqual(sinv(theta), values)
523523

524524
def test_asin(self):
525525
self.assertEqual(asinv(null), null)
@@ -574,11 +574,11 @@ def test_polar(self):
574574
self.assertEqual(polar(null), null)
575575
self.assertEqual(polar(Vector('hello')), null)
576576
self.assertEqual(polar(Vector([0, 0.25, 0.5, 0.75, 1])), Vector([1, 0, 0, 1, -1, 0, 0, -1, 1, 0]))
577-
theta = Vector.range(0.001, 1, 0.01)
577+
theta = Vector.range(0, 1, 0.01)
578578
values = [(math.cos(th), math.sin(th)) for th in theta*Tau]
579579
for i in range(len(values)):
580-
self.assertEqual(polar(theta.item(i)), values[i])
581-
self.assertEqual(polar(theta), Vector.compose(values))
580+
self.assertAllAlmostEqual(polar(theta.item(i)), values[i])
581+
self.assertAllAlmostEqual(polar(theta), Vector.compose(values))
582582

583583
def test_angle(self):
584584
self.assertEqual(angle(null), null)
@@ -607,16 +607,16 @@ def test_quaternion(self):
607607
self.assertEqual(quaternion(Vector([0, 1, 0]), Vector(0)), [1, 0, 0, 0])
608608
self.assertEqual(quaternion(Vector([0, 0, 1]), Vector(0)), [1, 0, 0, 0])
609609
c, s = math.cos(math.pi*0.25), math.sin(math.pi*0.25)
610-
self.assertEqual(quaternion(Vector([1, 0, 0]), Vector(0.25)), [c, s, 0, 0])
611-
self.assertEqual(quaternion(Vector([0, 1, 0]), Vector(0.25)), [c, 0, s, 0])
612-
self.assertEqual(quaternion(Vector([0, 0, 1]), Vector(0.25)), [c, 0, 0, s])
610+
self.assertAllAlmostEqual(quaternion(Vector([1, 0, 0]), Vector(0.25)), [c, s, 0, 0])
611+
self.assertAllAlmostEqual(quaternion(Vector([0, 1, 0]), Vector(0.25)), [c, 0, s, 0])
612+
self.assertAllAlmostEqual(quaternion(Vector([0, 0, 1]), Vector(0.25)), [c, 0, 0, s])
613613
self.assertAllAlmostEqual(quaternion(Vector([1, 0, 0]), Vector(0.5)), [0, 1, 0, 0])
614614
self.assertAllAlmostEqual(quaternion(Vector([0, 1, 0]), Vector(0.5)), [0, 0, 1, 0])
615615
self.assertAllAlmostEqual(quaternion(Vector([0, 0, 1]), Vector(0.5)), [0, 0, 0, 1])
616616
c, s = math.cos(math.pi/3), math.sin(math.pi/3)
617-
self.assertEqual(quaternion(Vector([1, 0, 0]), Vector(1/3)), [c, s, 0, 0])
618-
self.assertEqual(quaternion(Vector([0, 1, 0]), Vector(1/3)), [c, 0, s, 0])
619-
self.assertEqual(quaternion(Vector([0, 0, 1]), Vector(1/3)), [c, 0, 0, s])
617+
self.assertAllAlmostEqual(quaternion(Vector([1, 0, 0]), Vector(1/3)), [c, s, 0, 0])
618+
self.assertAllAlmostEqual(quaternion(Vector([0, 1, 0]), Vector(1/3)), [c, 0, s, 0])
619+
self.assertAllAlmostEqual(quaternion(Vector([0, 0, 1]), Vector(1/3)), [c, 0, 0, s])
620620
self.assertAllAlmostEqual(quaternion(Vector([1, 0, 0]), Vector(1)), Vector([-1, 0, 0, 0]))
621621
self.assertAllAlmostEqual(quaternion(Vector([0, 1, 0]), Vector(1)), Vector([-1, 0, 0, 0]))
622622
self.assertAllAlmostEqual(quaternion(Vector([0, 0, 1]), Vector(1)), Vector([-1, 0, 0, 0]))

tests/test_quaternion.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,16 +72,16 @@ def test_euler(self):
7272
self.assertEqual(Quaternion.euler([0, 1, 0], 0), [1, 0, 0, 0])
7373
self.assertEqual(Quaternion.euler([0, 0, 1], 0), [1, 0, 0, 0])
7474
c, s = math.cos(math.pi*0.25), math.sin(math.pi*0.25)
75-
self.assertEqual(Quaternion.euler([1, 0, 0], 0.25), [c, s, 0, 0])
76-
self.assertEqual(Quaternion.euler([0, 1, 0], 0.25), [c, 0, s, 0])
77-
self.assertEqual(Quaternion.euler([0, 0, 1], 0.25), [c, 0, 0, s])
75+
self.assertAllAlmostEqual(Quaternion.euler([1, 0, 0], 0.25), [c, s, 0, 0])
76+
self.assertAllAlmostEqual(Quaternion.euler([0, 1, 0], 0.25), [c, 0, s, 0])
77+
self.assertAllAlmostEqual(Quaternion.euler([0, 0, 1], 0.25), [c, 0, 0, s])
7878
self.assertAllAlmostEqual(Quaternion.euler([1, 0, 0], 0.5), [0, 1, 0, 0])
7979
self.assertAllAlmostEqual(Quaternion.euler([0, 1, 0], 0.5), [0, 0, 1, 0])
8080
self.assertAllAlmostEqual(Quaternion.euler([0, 0, 1], 0.5), [0, 0, 0, 1])
8181
c, s = math.cos(math.pi/3), math.sin(math.pi/3)
82-
self.assertEqual(Quaternion.euler([1, 0, 0], 1/3), [c, s, 0, 0])
83-
self.assertEqual(Quaternion.euler([0, 1, 0], 1/3), [c, 0, s, 0])
84-
self.assertEqual(Quaternion.euler([0, 0, 1], 1/3), [c, 0, 0, s])
82+
self.assertAllAlmostEqual(Quaternion.euler([1, 0, 0], 1/3), [c, s, 0, 0])
83+
self.assertAllAlmostEqual(Quaternion.euler([0, 1, 0], 1/3), [c, 0, s, 0])
84+
self.assertAllAlmostEqual(Quaternion.euler([0, 0, 1], 1/3), [c, 0, 0, s])
8585
self.assertAllAlmostEqual(Quaternion.euler([1, 0, 0], 1), [-1, 0, 0, 0])
8686
self.assertAllAlmostEqual(Quaternion.euler([0, 1, 0], 1), [-1, 0, 0, 0])
8787
self.assertAllAlmostEqual(Quaternion.euler([0, 0, 1], 1), [-1, 0, 0, 0])

0 commit comments

Comments
 (0)