2.0 RELEASE (candidate :)
- removed old garbage - distutils
This commit is contained in:
parent
76ab4c1d32
commit
3a8ae90597
11 changed files with 20 additions and 820 deletions
|
@ -13,12 +13,3 @@ from . import misc
|
|||
from . import text
|
||||
from . import types
|
||||
from . import version
|
||||
|
||||
print = text.Output("over.__init__", stream=sys.stderr)
|
||||
|
||||
core = aux.DeprecationForwarder(sys.modules[__name__], "over.core", "over")
|
||||
textui = aux.DeprecationForwarder(text, "over.core.textui", "over.text")
|
||||
|
||||
#for module in [types]:
|
||||
# if module.__file__[-2:] == "py":
|
||||
# print("<r>unable to load C implementation<.> of <y>%s<.>, falling back to pure Python instead" %(module.__name__), print.tl.warn)
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
#! /usr/bin/env python3
|
||||
# encoding: utf-8
|
||||
|
||||
class compare_float:
|
||||
"""
|
||||
Sets .lt, .eq or .gt iff A is less than, equal or greather than B.
|
||||
"""
|
||||
|
||||
def __init__(self, A, B, epsilon=1e-12):
|
||||
self.A = A
|
||||
self.B = B
|
||||
self.epsilon = epsilon
|
||||
|
||||
self.lt = False
|
||||
self.le = True
|
||||
self.eq = False
|
||||
self.ge = True
|
||||
self.gt = False
|
||||
self.ne = False
|
||||
|
||||
if A < (B - epsilon):
|
||||
self.lt = True
|
||||
self.ge = False
|
||||
elif A > (B + epsilon):
|
||||
self.gt = True
|
||||
self.le = False
|
||||
|
||||
if abs(A - B) < epsilon:
|
||||
self.eq = True
|
||||
else:
|
||||
self.ne = True
|
||||
|
||||
# convenience
|
||||
self.less = self.lt
|
||||
self.less_or_equal = self.le
|
||||
self.equal = self.eq
|
||||
self.greater_or_equal = self.ge
|
||||
self.greater = self.gt
|
||||
self.not_equal = self.ne
|
||||
|
||||
def __repr__(self):
|
||||
return "compare_float(%.2f, %.2f, epsilon=%.03e)" %(self.A, self.B, self.epsilon)
|
||||
|
||||
if __name__ == '__main__':
|
||||
x = compare_float(1, 2, 0.5)
|
||||
assert x.lt and x.less and not (x.gt or x.ge)
|
||||
x = compare_float(2, 2, 0.5)
|
||||
assert x.eq and x.le and x.ge and not (x.lt or x.gt)
|
||||
x = compare_float(3, 2, 0.5)
|
||||
assert x.gt and x.ge and not (x.le or x.eq or x.lt)
|
|
@ -1,470 +0,0 @@
|
|||
#! /bin/env python3
|
||||
# encoding: utf-8
|
||||
|
||||
"""
|
||||
Vector and Matrix Math
|
||||
"""
|
||||
|
||||
from libc.stdlib cimport malloc, realloc, free
|
||||
from libc.stdint cimport uint8_t, uint16_t, uint64_t
|
||||
from libc.math cimport sin, cos, sqrt
|
||||
|
||||
cdef class mat4:
|
||||
"""
|
||||
A float 4x4 matrix.
|
||||
|
||||
All arrays are column-major, i.e. OpenGL style:
|
||||
|
||||
0 4 8 12
|
||||
1 5 9 13
|
||||
2 6 10 14
|
||||
3 7 11 15
|
||||
|
||||
The matrix implements stacking useful for graphics.
|
||||
"""
|
||||
|
||||
def __cinit__(mat4 self):
|
||||
to_alloc = 16 * sizeof(float)
|
||||
self.stack = <float *>malloc(to_alloc)
|
||||
|
||||
if not self.stack:
|
||||
raise MemoryError("Unable to malloc %d B for mat4." %(to_alloc))
|
||||
|
||||
self.m = self.stack
|
||||
self.size = 1
|
||||
|
||||
def _debug(mat4 self):
|
||||
print("--- self.stack = %d" %(<uint64_t>self.stack))
|
||||
print("--- self.m = %d (+%d)" %(<uint64_t>self.m, self.m - self.stack))
|
||||
print("--- self.size = %d" %(self.size))
|
||||
|
||||
def __init__(mat4 self, *args):
|
||||
"""
|
||||
Create a ma4t.
|
||||
|
||||
Accepts any number of parameters between 0 and 16 to fill the
|
||||
matrix from the upper left corner going down (column-wise).
|
||||
"""
|
||||
|
||||
length = len(args)
|
||||
|
||||
if length == 1 and isinstance(args[0], (list, tuple)):
|
||||
args = args[0]
|
||||
length = len(args)
|
||||
|
||||
if length > 16:
|
||||
raise ValueError("Attempt to initialize a mat4 with %d arguments." %(length))
|
||||
|
||||
self.load_from(args)
|
||||
|
||||
def __dealloc__(mat4 self):
|
||||
free(self.stack)
|
||||
|
||||
def __getstate__(mat4 self):
|
||||
state = []
|
||||
|
||||
for i in range(self.m - self.stack + 16):
|
||||
state.append(self.stack[i])
|
||||
|
||||
return state
|
||||
|
||||
def __setstate__(mat4 self, state):
|
||||
length = len(state)
|
||||
matrices = length//16
|
||||
|
||||
if not matrices*16 == length:
|
||||
raise ValueError("mat4 __setstate__ got %d floats as a state" %(length))
|
||||
|
||||
self.m = self.stack
|
||||
|
||||
slot_full = False
|
||||
for start in range(0, length, 16):
|
||||
if slot_full:
|
||||
self.push()
|
||||
slot_full = False
|
||||
|
||||
self.load_from(state[start:start+16])
|
||||
slot_full = True
|
||||
|
||||
def __getitem__(mat4 self, int i):
|
||||
if i > 16 or i < 0:
|
||||
raise IndexError("element index out of range(16)")
|
||||
|
||||
return self.m[i]
|
||||
|
||||
def __setitem__(self, int i, value):
|
||||
if i > 16 or i < 0:
|
||||
raise IndexError("element index out of range(16)")
|
||||
|
||||
self.m[i] = value
|
||||
|
||||
def push(mat4 self):
|
||||
"""
|
||||
Push the current matrix into the stack and load up an empty one (a zero matrix).
|
||||
|
||||
TODO consider copying the matrix instead?
|
||||
"""
|
||||
|
||||
# self.m points to the current matrix
|
||||
# self.stack points to the first matrix
|
||||
# self.size how many matrices are allocated
|
||||
|
||||
# ensure there's room for one more
|
||||
cdef unsigned int used = 1 + (self.m - self.stack) / 16
|
||||
cdef unsigned int empty = self.size - used
|
||||
cdef float *tmp
|
||||
|
||||
if not empty:
|
||||
self.size += 1
|
||||
to_alloc = self.size * 16 * sizeof(float)
|
||||
tmp = <float *>realloc(self.stack, to_alloc)
|
||||
|
||||
if tmp:
|
||||
self.stack = tmp
|
||||
else:
|
||||
raise MemoryError("Unable to malloc %d B for mat4." %(to_alloc))
|
||||
|
||||
# advance the pointer to the new one
|
||||
self.m = self.stack + 16 * used
|
||||
|
||||
# at this point there"s at least enough space for one matrix
|
||||
# copy the old matrix into the new one
|
||||
cdef uint8_t i
|
||||
cdef float *old_m = self.m - 16
|
||||
for i in range(16):
|
||||
self.m[i] = old_m[i]
|
||||
|
||||
def pop(mat4 self):
|
||||
"""
|
||||
Pop a matrix from the stack.
|
||||
"""
|
||||
|
||||
if self.m == self.stack:
|
||||
raise IndexError("pop from an empty stack")
|
||||
|
||||
self.m -= 16
|
||||
|
||||
def get_list(mat4 self):
|
||||
L = []
|
||||
|
||||
for i in range(16):
|
||||
L.append(self.m[i])
|
||||
|
||||
return L
|
||||
|
||||
def load_from(mat4 self, L):
|
||||
"""
|
||||
Fill the current matrix from a either a list of values, column-major,
|
||||
or another matrix. This method doesn't modify the stack, only the
|
||||
current matrix is read and modified.
|
||||
|
||||
If the number of values isn"t 16, it will be padded to 16 by zeros.
|
||||
If it's larger, GeneralError will be raised.
|
||||
"""
|
||||
|
||||
if isinstance(L, mat4):
|
||||
L = L.get_list()
|
||||
length = 16
|
||||
else:
|
||||
length = len(L)
|
||||
|
||||
if length > 16:
|
||||
raise ValueError("supplied list is longer than 16")
|
||||
|
||||
for i in range(16):
|
||||
if i < length:
|
||||
self.m[i] = L[i]
|
||||
else:
|
||||
self.m[i] = 0.0
|
||||
|
||||
def zero(mat4 self):
|
||||
"""
|
||||
Fill the matrix with zeroes.
|
||||
"""
|
||||
|
||||
for i in range(16):
|
||||
self.m[i] = 0.0
|
||||
|
||||
def identity(mat4 self):
|
||||
"""
|
||||
Make the matrix an identity.
|
||||
"""
|
||||
|
||||
self.zero()
|
||||
|
||||
self.m[0] = 1.0
|
||||
self.m[5] = 1.0
|
||||
self.m[10] = 1.0
|
||||
self.m[15] = 1.0
|
||||
|
||||
def transpose(mat4 self):
|
||||
"""
|
||||
Transpose the matrix.
|
||||
"""
|
||||
|
||||
cdef float tmp
|
||||
|
||||
tmp = self.m[1]
|
||||
self.m[1] = self.m[4]
|
||||
self.m[4] = tmp
|
||||
|
||||
tmp = self.m[2]
|
||||
self.m[2] = self.m[8]
|
||||
self.m[8] = tmp
|
||||
|
||||
tmp = self.m[3]
|
||||
self.m[3] = self.m[12]
|
||||
self.m[12] = tmp
|
||||
|
||||
tmp = self.m[7]
|
||||
self.m[7] = self.m[13]
|
||||
self.m[13] = tmp
|
||||
|
||||
tmp = self.m[11]
|
||||
self.m[11] = self.m[14]
|
||||
self.m[14] = tmp
|
||||
|
||||
tmp = self.m[6]
|
||||
self.m[6] = self.m[9]
|
||||
self.m[9] = tmp
|
||||
|
||||
def invert(mat4 self):
|
||||
"""
|
||||
Invert the matrix.
|
||||
"""
|
||||
|
||||
cdef float tmp[16]
|
||||
cdef float det
|
||||
|
||||
tmp[0] = self.m[5]*self.m[10]*self.m[15] - self.m[5]*self.m[11]*self.m[14] - self.m[9]*self.m[6]*self.m[15] + self.m[9]*self.m[7]*self.m[14] + self.m[13]*self.m[6]*self.m[11] - self.m[13]*self.m[7]*self.m[10]
|
||||
tmp[4] = -self.m[4]*self.m[10]*self.m[15] + self.m[4]*self.m[11]*self.m[14] + self.m[8]*self.m[6]*self.m[15] - self.m[8]*self.m[7]*self.m[14] - self.m[12]*self.m[6]*self.m[11] + self.m[12]*self.m[7]*self.m[10]
|
||||
tmp[8] = self.m[4]*self.m[9]*self.m[15] - self.m[4]*self.m[11]*self.m[13] - self.m[8]*self.m[5]*self.m[15] + self.m[8]*self.m[7]*self.m[13] + self.m[12]*self.m[5]*self.m[11] - self.m[12]*self.m[7]*self.m[9]
|
||||
tmp[12] = -self.m[4]*self.m[9]*self.m[14] + self.m[4]*self.m[10]*self.m[13] + self.m[8]*self.m[5]*self.m[14] - self.m[8]*self.m[6]*self.m[13] - self.m[12]*self.m[5]*self.m[10] + self.m[12]*self.m[6]*self.m[9]
|
||||
|
||||
det = self.m[0]*tmp[0] + self.m[1]*tmp[4] + self.m[2]*tmp[8] + self.m[3]*tmp[12]
|
||||
|
||||
# epsilon pulled straight out of Uranus
|
||||
if det < 0.00005 and det > -0.00005:
|
||||
print("det=%.1f" %(det))
|
||||
return
|
||||
|
||||
tmp[1] = -self.m[1]*self.m[10]*self.m[15] + self.m[1]*self.m[11]*self.m[14] + self.m[9]*self.m[2]*self.m[15] - self.m[9]*self.m[3]*self.m[14] - self.m[13]*self.m[2]*self.m[11] + self.m[13]*self.m[3]*self.m[10]
|
||||
tmp[5] = self.m[0]*self.m[10]*self.m[15] - self.m[0]*self.m[11]*self.m[14] - self.m[8]*self.m[2]*self.m[15] + self.m[8]*self.m[3]*self.m[14] + self.m[12]*self.m[2]*self.m[11] - self.m[12]*self.m[3]*self.m[10]
|
||||
tmp[9] = -self.m[0]*self.m[9]*self.m[15] + self.m[0]*self.m[11]*self.m[13] + self.m[8]*self.m[1]*self.m[15] - self.m[8]*self.m[3]*self.m[13] - self.m[12]*self.m[1]*self.m[11] + self.m[12]*self.m[3]*self.m[9]
|
||||
tmp[13] = self.m[0]*self.m[9]*self.m[14] - self.m[0]*self.m[10]*self.m[13] - self.m[8]*self.m[1]*self.m[14] + self.m[8]*self.m[2]*self.m[13] + self.m[12]*self.m[1]*self.m[10] - self.m[12]*self.m[2]*self.m[9]
|
||||
tmp[2] = self.m[1]*self.m[6]*self.m[15] - self.m[1]*self.m[7]*self.m[14] - self.m[5]*self.m[2]*self.m[15] + self.m[5]*self.m[3]*self.m[14] + self.m[13]*self.m[2]*self.m[7] - self.m[13]*self.m[3]*self.m[6]
|
||||
tmp[6] = -self.m[0]*self.m[6]*self.m[15] + self.m[0]*self.m[7]*self.m[14] + self.m[4]*self.m[2]*self.m[15] - self.m[4]*self.m[3]*self.m[14] - self.m[12]*self.m[2]*self.m[7] + self.m[12]*self.m[3]*self.m[6]
|
||||
tmp[10] = self.m[0]*self.m[5]*self.m[15] - self.m[0]*self.m[7]*self.m[13] - self.m[4]*self.m[1]*self.m[15] + self.m[4]*self.m[3]*self.m[13] + self.m[12]*self.m[1]*self.m[7] - self.m[12]*self.m[3]*self.m[5]
|
||||
tmp[14] = -self.m[0]*self.m[5]*self.m[14] + self.m[0]*self.m[6]*self.m[13] + self.m[4]*self.m[1]*self.m[14] - self.m[4]*self.m[2]*self.m[13] - self.m[12]*self.m[1]*self.m[6] + self.m[12]*self.m[2]*self.m[5]
|
||||
tmp[3] = -self.m[1]*self.m[6]*self.m[11] + self.m[1]*self.m[7]*self.m[10] + self.m[5]*self.m[2]*self.m[11] - self.m[5]*self.m[3]*self.m[10] - self.m[9]*self.m[2]*self.m[7] + self.m[9]*self.m[3]*self.m[6]
|
||||
tmp[7] = self.m[0]*self.m[6]*self.m[11] - self.m[0]*self.m[7]*self.m[10] - self.m[4]*self.m[2]*self.m[11] + self.m[4]*self.m[3]*self.m[10] + self.m[8]*self.m[2]*self.m[7] - self.m[8]*self.m[3]*self.m[6]
|
||||
tmp[11] = -self.m[0]*self.m[5]*self.m[11] + self.m[0]*self.m[7]*self.m[9] + self.m[4]*self.m[1]*self.m[11] - self.m[4]*self.m[3]*self.m[9] - self.m[8]*self.m[1]*self.m[7] + self.m[8]*self.m[3]*self.m[5]
|
||||
tmp[15] = self.m[0]*self.m[5]*self.m[10] - self.m[0]*self.m[6]*self.m[9] - self.m[4]*self.m[1]*self.m[10] + self.m[4]*self.m[2]*self.m[9] + self.m[8]*self.m[1]*self.m[6] - self.m[8]*self.m[2]*self.m[5]
|
||||
|
||||
det = 1.0 / det
|
||||
self.m[0] = tmp[0] * det
|
||||
self.m[1] = tmp[1] * det
|
||||
self.m[2] = tmp[2] * det
|
||||
self.m[3] = tmp[3] * det
|
||||
self.m[4] = tmp[4] * det
|
||||
self.m[5] = tmp[5] * det
|
||||
self.m[6] = tmp[6] * det
|
||||
self.m[7] = tmp[7] * det
|
||||
self.m[8] = tmp[8] * det
|
||||
self.m[9] = tmp[9] * det
|
||||
self.m[10] = tmp[10] * det
|
||||
self.m[11] = tmp[11] * det
|
||||
self.m[12] = tmp[12] * det
|
||||
self.m[13] = tmp[13] * det
|
||||
self.m[14] = tmp[14] * det
|
||||
self.m[15] = tmp[15] * det
|
||||
|
||||
def mulm(mat4 self, mat4 B, bint inplace=False):
|
||||
"""
|
||||
Return a matrix that is the result of multiplying this matrix by another.
|
||||
|
||||
M = self * mat4 B
|
||||
"""
|
||||
|
||||
cdef uint8_t i
|
||||
cdef mat4 tmp = mat4()
|
||||
|
||||
tmp.m[0] = self.m[0] * B.m[0] + self.m[4] * B.m[1] + self.m[8] * B.m[2] + self.m[12] * B.m[3]
|
||||
tmp.m[1] = self.m[1] * B.m[0] + self.m[5] * B.m[1] + self.m[9] * B.m[2] + self.m[13] * B.m[3]
|
||||
tmp.m[2] = self.m[2] * B.m[0] + self.m[6] * B.m[1] + self.m[10] * B.m[2] + self.m[14] * B.m[3]
|
||||
tmp.m[3] = self.m[3] * B.m[0] + self.m[7] * B.m[1] + self.m[11] * B.m[2] + self.m[15] * B.m[3]
|
||||
tmp.m[4] = self.m[0] * B.m[4] + self.m[4] * B.m[5] + self.m[8] * B.m[6] + self.m[12] * B.m[7]
|
||||
tmp.m[5] = self.m[1] * B.m[4] + self.m[5] * B.m[5] + self.m[9] * B.m[6] + self.m[13] * B.m[7]
|
||||
tmp.m[6] = self.m[2] * B.m[4] + self.m[6] * B.m[5] + self.m[10] * B.m[6] + self.m[14] * B.m[7]
|
||||
tmp.m[7] = self.m[3] * B.m[4] + self.m[7] * B.m[5] + self.m[11] * B.m[6] + self.m[15] * B.m[7]
|
||||
tmp.m[8] = self.m[0] * B.m[8] + self.m[4] * B.m[9] + self.m[8] * B.m[10] + self.m[12] * B.m[11]
|
||||
tmp.m[9] = self.m[1] * B.m[8] + self.m[5] * B.m[9] + self.m[9] * B.m[10] + self.m[13] * B.m[11]
|
||||
tmp.m[10] = self.m[2] * B.m[8] + self.m[6] * B.m[9] + self.m[10] * B.m[10] + self.m[14] * B.m[11]
|
||||
tmp.m[11] = self.m[3] * B.m[8] + self.m[7] * B.m[9] + self.m[11] * B.m[10] + self.m[15] * B.m[11]
|
||||
tmp.m[12] = self.m[0] * B.m[12] + self.m[4] * B.m[13] + self.m[8] * B.m[14] + self.m[12] * B.m[15]
|
||||
tmp.m[13] = self.m[1] * B.m[12] + self.m[5] * B.m[13] + self.m[9] * B.m[14] + self.m[13] * B.m[15]
|
||||
tmp.m[14] = self.m[2] * B.m[12] + self.m[6] * B.m[13] + self.m[10] * B.m[14] + self.m[14] * B.m[15]
|
||||
tmp.m[15] = self.m[3] * B.m[12] + self.m[7] * B.m[13] + self.m[11] * B.m[14] + self.m[15] * B.m[15]
|
||||
|
||||
if inplace:
|
||||
for i in range(16):
|
||||
self.m[i] = tmp.m[i]
|
||||
else:
|
||||
return tmp
|
||||
|
||||
def mulv(mat4 self, vec3 v):
|
||||
"""
|
||||
Return a vec3 that is the result of multiplying this matrix by a vec3.
|
||||
|
||||
u = self * v
|
||||
"""
|
||||
|
||||
cdef mat4 tmp = vec3()
|
||||
|
||||
tmp.v[0] = v.v[0]*self.m[0] + v.v[1]*self.m[4] + v.v[2]*self.m[8] + self.m[12]
|
||||
tmp.v[1] = v.v[0]*self.m[1] + v.v[1]*self.m[5] + v.v[2]*self.m[9] + self.m[13]
|
||||
tmp.v[2] = v.v[0]*self.m[2] + v.v[1]*self.m[6] + v.v[2]*self.m[10] + self.m[14]
|
||||
|
||||
return tmp
|
||||
|
||||
def mulf(mat4 self, f):
|
||||
"""
|
||||
Return a matrix that is the result of multiplying this matrix by a scalar.
|
||||
|
||||
M = self * f
|
||||
"""
|
||||
|
||||
cdef mat4 tmp = mat4()
|
||||
cdef int i
|
||||
|
||||
for i in range(16):
|
||||
tmp.m[i] = self.m[i] * f
|
||||
|
||||
return tmp
|
||||
|
||||
def __repr__(mat4 self):
|
||||
lines = []
|
||||
|
||||
lines.append("mat4(%.1f %.1f %.1f %.1f" %(self.m[0], self.m[4], self.m[8], self.m[12]))
|
||||
lines.append(" %.1f %.1f %.1f %.1f" %(self.m[1], self.m[5], self.m[9], self.m[13]))
|
||||
lines.append(" %.1f %.1f %.1f %.1f" %(self.m[2], self.m[6], self.m[10], self.m[14]))
|
||||
lines.append(" %.1f %.1f %.1f %.1f)" %(self.m[3], self.m[7], self.m[11], self.m[15]))
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
cdef class vec3:
|
||||
"""
|
||||
A float 3D vector.
|
||||
|
||||
>>> v = vec3(1, 1, 0)
|
||||
>>> w = vec3(0, 1, 1)
|
||||
>>> v.length
|
||||
1.4142135623730951
|
||||
>>> v.dot(w)
|
||||
1.0
|
||||
>>> v.cross(w)
|
||||
vec4(1.00, 1.00, 1.00)
|
||||
>>> v + w
|
||||
vec4(1.00, 2.00, 1.00)
|
||||
>>> w - v
|
||||
vec4(-1.00, 0.00, 1.00)
|
||||
|
||||
"""
|
||||
|
||||
def __init__(vec3 self, *args):
|
||||
"""
|
||||
Create a vec3.
|
||||
|
||||
Accepts any number of parameters between 0 and 3 to fill the vector from the left.
|
||||
"""
|
||||
|
||||
length = len(args)
|
||||
|
||||
if length == 1 and isinstance(args[0], (list, tuple)):
|
||||
args = args[0]
|
||||
length = len(args)
|
||||
|
||||
if length > 3:
|
||||
raise ValueError("Attempt to initialize a vec3 with %d arguments." %(length))
|
||||
|
||||
for i in range(3):
|
||||
if i < length:
|
||||
self.v[i] = args[i]
|
||||
else:
|
||||
self.v[i] = 0.0
|
||||
|
||||
def __getitem__(vec3 self, int i):
|
||||
if i >= 3 or i < 0:
|
||||
raise IndexError("element index out of range(3)")
|
||||
|
||||
return self.v[i]
|
||||
|
||||
def __setitem__(vec3 self, int i, float value):
|
||||
if i >= 3 or i < 0:
|
||||
raise IndexError("element index out of range(3)")
|
||||
|
||||
self.v[i] = value
|
||||
|
||||
def __repr__(vec3 self):
|
||||
return "vec3(%.2f, %.2f, %.2f)" %(self.v[0], self.v[1], self.v[2])
|
||||
|
||||
def __getstate__(vec3 self):
|
||||
return (self.v[0], self.v[1], self.v[2])
|
||||
|
||||
def __setstate__(vec3 self, state):
|
||||
self.v[0] = state[0]
|
||||
self.v[1] = state[1]
|
||||
self.v[2] = state[2]
|
||||
|
||||
@property
|
||||
def length(vec3 self):
|
||||
"""
|
||||
Contains the geometric length of the vector.
|
||||
"""
|
||||
|
||||
return sqrt(self.v[0]**2 + self.v[1]**2 + self.v[2]**2)
|
||||
|
||||
def normalized(vec3 self):
|
||||
"""
|
||||
Returns this vector, normalized.
|
||||
"""
|
||||
|
||||
length = self.length
|
||||
|
||||
return vec3(self.v[0] / length, self.v[1] / length, self.v[2] / length)
|
||||
|
||||
def __add__(vec3 L, vec3 R):
|
||||
return vec3(L.v[0] + R.v[0], L.v[1] + R.v[1], L.v[2] + R.v[2])
|
||||
|
||||
def __sub__(vec3 L, vec3 R):
|
||||
return vec3(L.v[0] - R.v[0], L.v[1] - R.v[1], L.v[2] - R.v[2])
|
||||
|
||||
def __neg__(vec3 self):
|
||||
return vec3(-self.v[0], -self.v[1], -self.v[2])
|
||||
|
||||
def dot(vec3 L, vec3 R):
|
||||
"""
|
||||
Returns the dot product of the two vectors.
|
||||
|
||||
E.g. u.dot(v) -> u . v
|
||||
"""
|
||||
|
||||
return L.v[0] * R.v[0] + L.v[1] * R.v[1] + L.v[2] * R.v[2]
|
||||
|
||||
def cross(vec3 L, vec3 R):
|
||||
"""
|
||||
Returns the cross product of the two vectors.
|
||||
|
||||
E.g. u.cross(v) -> u x v
|
||||
"""
|
||||
|
||||
return vec3(L.v[1]*R.v[2] - L.v[2]*R.v[1], L.v[0]*R.v[2] - L.v[2]*R.v[0], L.v[0]*R.v[1] - L.v[1]*R.v[0])
|
||||
|
||||
def __mul__(vec3 L, R):
|
||||
"""
|
||||
Multiplication of a vec3 by a float.
|
||||
|
||||
The float has to be on the right.
|
||||
"""
|
||||
|
||||
return vec3(L.v[0] * R, L.v[1] * R, L.v[2] * R)
|
|
@ -1,195 +0,0 @@
|
|||
#! /usr/bin/env python3
|
||||
# encoding: utf-8
|
||||
|
||||
import time
|
||||
import serial
|
||||
|
||||
from .. import text
|
||||
|
||||
"""
|
||||
OverTalk protocol spec
|
||||
======================
|
||||
|
||||
Transport layer
|
||||
---------------
|
||||
TODO: Swap checksum and payload.
|
||||
|
||||
frame = [ 0x2a | destination (1) | source (1) | payload length (1) | payload (n) | checksum (1) ]
|
||||
|
||||
Any time a 0x2a is encountered the parser should flush its state and begin reading a new frame.
|
||||
Bytes 0x2a, 0x2b, 0x11 and 0x13 are prefixed with 0x2b and inverted (XORed with 0xff) to avoid collisions.
|
||||
|
||||
Frames with destinations unknown to an OverShell are sent to the default interface, or dropped if
|
||||
that's the interface they came from.
|
||||
|
||||
Command layer
|
||||
-------------
|
||||
NOTE: This is going to change.
|
||||
|
||||
Payload consists of one or more commands:
|
||||
|
||||
get_command = [ 0x00 | register (1) | target register (1) ]
|
||||
set_command = [ 0x01 | register (1) | length (1) | value (n) ]
|
||||
|
||||
With a get_command the sender requests the receiver to read its own `register` and issue
|
||||
a set_command that sets the sender's `target register` to that value.
|
||||
|
||||
A set_command does what is says on the box.
|
||||
"""
|
||||
|
||||
class Transport:
|
||||
"""
|
||||
OverTalk Transport layer implementation.
|
||||
|
||||
Reads data from multiple interfaces and either routes frames or receives them.
|
||||
A received frame is then made available via a public attribute.
|
||||
|
||||
Interfaces are objects that implement methods read() (returns one byte of data as int), write(buffer, len),
|
||||
and waiting property (contains number of bytes waiting).
|
||||
"""
|
||||
def __init__(self, my_id, interfaces, def_route):
|
||||
"""
|
||||
@param my_id OverTalk address of this Transport instance
|
||||
@param interfaces a dict of interfaces keyed by their IDs
|
||||
@param def_route ID of default interface for sending
|
||||
"""
|
||||
|
||||
assert my_id not in interfaces
|
||||
assert def_route in interfaces
|
||||
|
||||
self.print = text.Output("over.serial.Transport")
|
||||
|
||||
self.my_id = my_id
|
||||
self.def_route = def_route
|
||||
self.interfaces = interfaces
|
||||
|
||||
self.destination_unknown = 0
|
||||
self.incoming = None
|
||||
|
||||
self.print("on-line", text.Output.tl.done)
|
||||
|
||||
def update(self):
|
||||
self.incoming = None
|
||||
|
||||
for interface_id, interface in self.interfaces.items():
|
||||
if interface.waiting:
|
||||
interface.last_data_received = time.time()
|
||||
byte = interface.read()
|
||||
|
||||
if byte == 0x2a:
|
||||
interface.rxbuffer = []
|
||||
interface.reading_escape = False
|
||||
interface.reading_frame = True
|
||||
|
||||
if byte == 0x2b:
|
||||
interface.reading_escape = True
|
||||
continue
|
||||
|
||||
if interface.reading_escape:
|
||||
byte ^= 0xff
|
||||
interface.reading_escape = False
|
||||
|
||||
if interface.reading_frame:
|
||||
interface.rxbuffer.append(byte)
|
||||
|
||||
if self.verify_frame(interface):
|
||||
self.process_frame(interface_id, interface.rxbuffer)
|
||||
interface.rxbuffer = []
|
||||
|
||||
def verify_frame(self, interface):
|
||||
buffer_length = len(interface.rxbuffer)
|
||||
|
||||
if buffer_length >= 4:
|
||||
# at this point we can read frame"s declared size
|
||||
if buffer_length >= interface.rxbuffer[3] + 5: # header (4) + payload + checksum (1)
|
||||
# a frame has been read, huzzah!
|
||||
interface.reading_frame = False
|
||||
|
||||
# checksum
|
||||
if sum(interface.rxbuffer[:-1]) % 0x100 == interface.rxbuffer[-1]:
|
||||
return True
|
||||
|
||||
else:
|
||||
interface.malformed_frames += 1
|
||||
self.print("broken frame received: §r§%s§.§" %(interface.rxbuffer))
|
||||
interface.rxbuffer = []
|
||||
|
||||
return False
|
||||
|
||||
def escape_frame(self, frame):
|
||||
assert frame[0] == 0x2a
|
||||
|
||||
frame_escaped = [0x2a]
|
||||
|
||||
for byte in frame[1:]:
|
||||
if byte in (0x2a, 0x2b, 0x11, 0x13):
|
||||
frame_escaped.append(0x2b)
|
||||
byte ^= 0xff
|
||||
|
||||
frame_escaped.append(byte)
|
||||
|
||||
return frame_escaped
|
||||
|
||||
def process_frame(self, source_interface_id, frame):
|
||||
payload = frame[4:-1]
|
||||
destination = frame[1]
|
||||
|
||||
if destination == self.my_id:
|
||||
self.incoming = (frame[2], payload)
|
||||
else:
|
||||
if destination in self.interfaces:
|
||||
self.print("routing frame to [%d]" %(destination))
|
||||
self.interfaces[destination].write(self.escape_frame(frame))
|
||||
else:
|
||||
if source_interface_id == self.def_route:
|
||||
self.destination_unknown += 1
|
||||
self.print("unknown destination <r>%d<.> for frame: <y>%s<.>" %(destination,
|
||||
repr(frame)), text.Output.tl.fail)
|
||||
else:
|
||||
self.print("routing frame to default route [%d]" %(self.def_route))
|
||||
self.interfaces[self.def_route].write(self.escape_frame(frame))
|
||||
|
||||
def send_data(self, destination, data):
|
||||
frame = [0x2a, destination, self.my_id, len(data)] + list(data)
|
||||
frame.append(sum(frame) % 0x100)
|
||||
frame = self.escape_frame(frame)
|
||||
|
||||
if destination in self.interfaces:
|
||||
s = self.interfaces[destination]
|
||||
else:
|
||||
s = self.interfaces[self.def_route]
|
||||
|
||||
s.write(frame)
|
||||
|
||||
class TTL_Interface:
|
||||
def __init__(self, interface="/dev/ttyUSB0", baudrate=57600):
|
||||
try:
|
||||
self.s = serial.Serial(interface, baudrate, timeout=1)
|
||||
except serial.serialutil.SerialException:
|
||||
self.s = None
|
||||
|
||||
self.rxbuffer = []
|
||||
self.reading_escape = False
|
||||
self.reading_frame = False
|
||||
|
||||
self.malformed_frames = 0
|
||||
self.last_data_received = 0
|
||||
|
||||
@property
|
||||
def waiting(self):
|
||||
if self.s:
|
||||
return self.s.inWaiting()
|
||||
else:
|
||||
return 0
|
||||
|
||||
def read(self):
|
||||
if self.s:
|
||||
return ord(self.s.read())
|
||||
else:
|
||||
return 0
|
||||
|
||||
def write(self, data):
|
||||
print("Sending:", "".join([hex(x)[2:].zfill(2) for x in data]))
|
||||
|
||||
if self.s:
|
||||
self.s.write(bytes(data))
|
|
@ -1,14 +0,0 @@
|
|||
#! /bin/zsh
|
||||
# encoding: utf-8
|
||||
|
||||
if [[ -a ".over_inhibit_deployment" ]]; then
|
||||
echo '!! deployment inhibited by lock file (hint: run this from the target directory, not the over directory)'
|
||||
elif [[ -n "$1" ]]; then
|
||||
OVER_DIR="$(readlink -f "$(dirname "$0")")"
|
||||
ln -s "$OVER_DIR"
|
||||
cp "$OVER_DIR/template.py" "$1"
|
||||
chmod +x "$1"
|
||||
echo "over\n__pycache__" >> .gitignore
|
||||
else
|
||||
echo '!! Missing argument (program name).'
|
||||
fi
|
|
@ -1,36 +0,0 @@
|
|||
#! /usr/bin/env python3
|
||||
# encoding: utf-8
|
||||
|
||||
from collections import OrderedDict
|
||||
|
||||
# --------------------------------------------------
|
||||
|
||||
cdef class ndict(OrderedDict):
|
||||
"""
|
||||
An OrderedDict subclass whose keys are exposed as attributes.
|
||||
|
||||
>>> d = ndict()
|
||||
>>> d.alpha = 1
|
||||
>>> d["alpha"]
|
||||
1
|
||||
>>> d["beta"] = 42
|
||||
>>> d.beta
|
||||
42
|
||||
>>> d
|
||||
{"alpha": 1, "beta": 42}
|
||||
"""
|
||||
|
||||
def __getattr__(self, str name):
|
||||
"""
|
||||
@while looking up an attribute
|
||||
"""
|
||||
|
||||
if name in self:
|
||||
return self[name]
|
||||
elif name.replace("_", "-") in self:
|
||||
return self[name.replace("_", "-")]
|
||||
else:
|
||||
raise AttributeError('"ndict" object has no attribute "%s"' %(name))
|
||||
|
||||
def __setattr__(self, str name, value):
|
||||
self[name] = value
|
|
@ -1,8 +1,8 @@
|
|||
#! /usr/bin/env python3
|
||||
# encoding: utf-8
|
||||
|
||||
major = 1 # VERSION_MAJOR_IDENTIFIER
|
||||
minor = 101 # VERSION_MINOR_IDENTIFIER
|
||||
# VERSION_LAST_MM 1.101
|
||||
patch = 2 # VERSION_PATCH_IDENTIFIER
|
||||
str = "1.101.2" # VERSION_STRING_IDENTIFIER
|
||||
major = 2 # VERSION_MAJOR_IDENTIFIER
|
||||
minor = 0 # VERSION_MINOR_IDENTIFIER
|
||||
# VERSION_LAST_MM 2.0
|
||||
patch = 0 # VERSION_PATCH_IDENTIFIER
|
||||
str = "2.0.0" # VERSION_STRING_IDENTIFIER
|
||||
|
|
15
setup.py
15
setup.py
|
@ -0,0 +1,15 @@
|
|||
from distutils.core import setup
|
||||
|
||||
setup(
|
||||
name="over",
|
||||
version="2.0.0",
|
||||
author="Aggregate",
|
||||
packages=["over"],
|
||||
url="https://git.covalent.cz/overwatch/over/",
|
||||
license="LICENSE.txt",
|
||||
description="Useful libraries derived from the Overwatch UAV.",
|
||||
install_requires=[
|
||||
"pytz >= 2017.2",
|
||||
"tzlocal >= 1.2"
|
||||
]
|
||||
)
|
41
test.py
41
test.py
|
@ -1,41 +0,0 @@
|
|||
#! /usr/bin/env python3
|
||||
# encoding: utf-8
|
||||
|
||||
# library imports
|
||||
import over
|
||||
|
||||
# local imports
|
||||
import version
|
||||
|
||||
# --------------------------------------------------
|
||||
# Exceptions
|
||||
|
||||
class ConfigurationError(Exception):
|
||||
pass
|
||||
|
||||
# --------------------------------------------------
|
||||
# Functions
|
||||
|
||||
# --------------------------------------------------
|
||||
# Classes
|
||||
|
||||
# --------------------------------------------------
|
||||
|
||||
def noop(*args):
|
||||
return args
|
||||
|
||||
def int4(*args):
|
||||
return [int(x) for x in args]
|
||||
|
||||
if __name__ == "__main__":
|
||||
main = over.app.Main("new-over-test", version.str, "LICENSE", features={"config_file": True})
|
||||
# name, description, callback, default=Option_sources.none, count=0, overwrite=True, abbr=None, in_cfg_file=True, show_in_help=True
|
||||
main.add_option("boolean-single", "ISO 8601 date for a new transfer (valid with +EHMU), defaults to current date. It's also used for relative times with --analysis-timeframe. You can use a day-count relative to today here.", over.callback.boolean, [False], abbr="1")
|
||||
main.add_option("boolean-triple", "", over.callback.booleans, [False, False, False], abbr="3", count=3)
|
||||
main.add_option("str-single", "", str, ["kek"], abbr="s", count=1)
|
||||
main.add_option("str-quad", "", noop, ["ze", "kek", "is", "bek"], abbr="4", count=4)
|
||||
main.add_option("int-quad", "", int4, [45, 72, 97, 18], abbr="i", count=4)
|
||||
main.add_option("int-quad-multi", "", int4, [45, 72, 97, 18], abbr="I", count=4, overwrite=False)
|
||||
main.add_doc("Description", ["What it does.", "Another paragraph."])
|
||||
main.add_doc("Moar", ["When we launched Raspberry Pi Zero last November, it’s fair to say we were <W>blindsided<.> by the level of demand. We immediately sold every copy of MagPi issue 40 and every Zero in stock at our distributors; and every time a new batch of Zeros came through from the factory they’d sell out in minutes. To complicate matters, Zero then had to compete for factory space with Raspberry Pi 3, which was ramping for launch at the end of February.", "To connect the camera to the Zero, we offer a custom six-inch adapter cable. This converts from the fine-pitch connector format to the coarser pitch used by the camera board. Liz has a great picture of Mooncake, the official Raspberry Pi cat, attempting to eat the camera cable. She won’t let me use it in this post so that you aren’t distracted from the pictures of the new Zero itself. I’ve a feeling she’ll be tweeting it later today."])
|
||||
main.setup()
|
Loading…
Add table
Add a link
Reference in a new issue