lslbasefuncs: Rewrite some things in a different way.

Also some formatting changes. No functionality changes.
This commit is contained in:
Sei Lisa 2017-10-18 13:45:46 +02:00
parent ea28e13e4a
commit 028b244a9e

View file

@ -78,7 +78,7 @@ TOUCH_INVALID_TEXCOORD = Vector((-1.0, -1.0, 0.0))
Infinity = float('inf')
Indet = Infinity * 0
NaN = -Indet # Don't use float("nan") - Windows gets upset.
NaN = -Indet # Don't use float("nan") - Windows gets upset.
class ELSLTypeMismatch(Exception):
def __init__(self):
@ -110,13 +110,13 @@ class ELSONotSupported(Exception):
# * LSL list -> Python list
Types = {
int: 1, # TYPE_INTEGER
float: 2, # TYPE_FLOAT
unicode: 3, # TYPE_STRING
Key: 4, # TYPE_KEY
Vector: 5, # TYPE_VECTOR
Quaternion: 6, # TYPE_ROTATION
list: 0, # TYPE_INVALID
int: 1, # TYPE_INTEGER
float: 2, # TYPE_FLOAT
unicode: 3, # TYPE_STRING
Key: 4, # TYPE_KEY
Vector: 5, # TYPE_VECTOR
Quaternion: 6, # TYPE_ROTATION
list: 0, # TYPE_INVALID
}
# Utility functions
@ -124,10 +124,10 @@ Types = {
def F32(f, f32=True):
"""Truncate a float to have a precision equivalent to IEEE single"""
if not f32: # don't truncate
if not f32: # don't truncate
return f
if isinstance(f, tuple): # vector, quaternion
if isinstance(f, tuple): # vector, quaternion
return f.__class__(F32(i) for i in f)
# Alternative to the big blurb below. This relies on the machine using IEEE-754, though.
@ -221,7 +221,7 @@ def F32(f, f32=True):
# if f < 0.000000000000000000000000000000000000011754943508222875079687365372222456778186655567720875215087517062784172594547271728515625:
# # Denormal range
# f *= 713623846352979940529142984724747568191373312.0
# e = 0.00000000000000000000000000000000000000000000140129846432481707092372958328991613128026194187651577175706828388979108268586060148663818836212158203125 # 2^-149
# e = 0.00000000000000000000000000000000000000000000140129846432481707092372958328991613128026194187651577175706828388979108268586060148663818836212158203125 # 2^-149
# else:
# e = 1.0
# # This first loop is an optimization to get closer to the destination faster for very small numbers
@ -335,13 +335,13 @@ def f2s(val, DP=6):
if math.isnan(val):
return u'NaN'
if lslcommon.LSO or val == 0.:
return u'%.*f' % (DP, val) # deals with -0.0 too
return u'%.*f' % (DP, val) # deals with -0.0 too
# Format according to Mono rules (7 decimals after the DP, found experimentally)
s = u'%.*f' % (DP+7, val)
if s[:DP+3] == u'-0.' + '0'*DP and s[DP+3] < u'5':
return u'0.' + '0'*DP # underflown negatives return 0.0 except for -0.0 dealt with above
return u'0.' + '0'*DP # underflown negatives return 0.0 except for -0.0 dealt with above
# Separate the sign
sgn = u'-' if s[0] == u'-' else u''
@ -368,42 +368,38 @@ def f2s(val, DP=6):
if s[i if i != dot else i+1] >= u'5':
# Rounding - increment s[:i] storing result into new_s
new_s = u''
ci = i-1 # carry index
ci = i-1 # carry index
while ci >= 0 and s[ci] == u'9':
new_s = u'0' + new_s
ci -= 1
if ci == dot:
ci -= 1 # skip over the dot
new_s = u'.' + new_s # but add it to new_s
ci -= 1 # skip over the dot
new_s = u'.' + new_s # but add it to new_s
if ci < 0:
new_s = u'1' + new_s # 9...9 -> 10...0
new_s = u'1' + new_s # 9...9 -> 10...0
else:
# increment s[ci] e.g. 43999 -> 44000
new_s = s[:ci] + chr(ord(s[ci])+1) + new_s
new_s = s[:ci] + chr(ord(s[ci]) + 1) + new_s
else:
new_s = s[:i]
if i <= dot:
return sgn + new_s + u'0'*(dot-i) + u'.' + u'0'*DP
return sgn + new_s + u'0'*(dot+1+DP-i)
return sgn + new_s + u'0' * (dot - i) + u'.' + u'0' * DP
return sgn + new_s + u'0' * (dot + 1 + DP - i)
def vr2s(v, DP=6):
if type(v) == Vector:
return u'<'+f2s(v[0],DP)+u', '+f2s(v[1],DP)+u', '+f2s(v[2],DP)+u'>'
return u'<'+f2s(v[0],DP)+u', '+f2s(v[1],DP)+u', '+f2s(v[2],DP)+u', '+f2s(v[3],DP)+u'>'
assert len(v) == (3 if type(v) == Vector else 4)
return u'<' + ', '.join(f2s(x, DP) for x in v) + u'>'
def qnz(q):
if all(x == 0. for x in q):
return Quaternion((0.,0.,0.,1.))
return q
return Quaternion((0.,0.,0.,1.)) if all(x == 0. for x in q) else q
def qnorm(q):
q = qnz(q)
mag2 = math.fsum((q[0]*q[0], q[1]*q[1], q[2]*q[2], q[3]*q[3]))
# Threshold for renormalization
eps_h = 1.0000021457672119140625 #float.fromhex('0x1.000024p0')
eps_l = 0.99999797344207763671875 # float.fromhex('0x1.FFFFBCp-1')
eps_h = 1.0000021457672119140625 # float.fromhex('0x1.000024p0')
eps_l = 0.99999797344207763671875 # float.fromhex('0x1.FFFFBCp-1')
if mag2 >= eps_h or mag2 <= eps_l:
# Renormalize
mag2 = math.sqrt(mag2)
@ -421,7 +417,7 @@ def InternalTypecast(val, out, InList, f32):
if out == list:
return [val]
if tval == int: # integer
if tval == int: # integer
val = S32(val)
if out == int: return val
if out == float: return F32(val, f32)
@ -445,7 +441,7 @@ def InternalTypecast(val, out, InList, f32):
if out == Quaternion: return val
if out == unicode: return vr2s(val, 6 if InList else 5)
raise ELSLTypeMismatch
if tval == Key: # key
if tval == Key: # key
if out == Key: return zstr(val)
if out == unicode: return zstr(unicode(val))
raise ELSLTypeMismatch
@ -505,10 +501,10 @@ def InternalTypecast(val, out, InList, f32):
if val[i:i+1] != u',':
return Z
val = val[i+1:]
return out(ret) # convert type
return out(ret) # convert type
# To avoid mutual recursion, this was moved:
#if tval == list: # etc.
#if tval == list: # etc.
raise ELSLInvalidType
@ -590,12 +586,8 @@ def InternalGetDeleteSubSequence(val, start, end, isGet):
if end == -1: end += L
if (start+L if start < 0 else start) > (end+L if end < 0 else end):
# Exclusion range - get/delete from end and start
if isGet:
return val[:end+1] + val[start:]
return val[end+1:start]
if isGet:
return val[start:end+1]
return val[:start] + val[end+1:]
return val[:end+1] + val[start:] if isGet else val[end+1:start]
return val[start:end+1] if isGet else val[:start] + val[end+1:]
def typecast(val, out, InList=False, f32=True):
"""Type cast an item. Calls InternalList2Strings for lists and
@ -603,7 +595,7 @@ def typecast(val, out, InList=False, f32=True):
"""
if type(val) == list:
if out == list:
return val # NOTE: We're not duplicating it here.
return val # NOTE: We're not duplicating it here.
if out == unicode:
return u''.join(InternalList2Strings(val))
raise ELSLTypeMismatch
@ -680,8 +672,10 @@ def mul(a, b, f32=True):
if tb in (int, float):
if ta == tb == int:
return S32(a*b)
if math.isnan(a) and math.isnan(b) and math.copysign(1, a) != math.copysign(1, b):
return NaN
if math.isnan(a) and math.isnan(b):
return (-NaN
if math.copysign(1, a) == math.copysign(1, b) == -1
else NaN)
return F32(ff(a)*ff(b), f32)
if tb != Vector:
# scalar * quat is not defined
@ -702,7 +696,7 @@ def mul(a, b, f32=True):
a[3] * b[3] - a[0] * b[0] - a[1] * b[1] - a[2] * b[2]), f32))
if ta != Vector:
raise ELSLInvalidType # Should never happen at this point
raise ELSLInvalidType # Should never happen at this point
if tb in (int, float):
a = v2f(a)
@ -716,7 +710,7 @@ def mul(a, b, f32=True):
return F32(math.fsum((a[0]*b[0], a[1]*b[1], a[2]*b[2])), f32)
if tb != Quaternion:
raise ELSLInvalidType # Should never happen at this point
raise ELSLInvalidType # Should never happen at this point
# vector * quaternion: perform conjugation
#v = mul(Quaternion((-b[0], -b[1], -b[2], b[3])), mul(Quaternion((a[0], a[1], a[2], 0.0)), b, f32=False))
@ -729,11 +723,11 @@ def mul(a, b, f32=True):
a[1]*2*(b[0]*b[1]-b[2]*b[3]),
a[2]*2*(b[0]*b[2]+b[1]*b[3]))),
math.fsum(( a[0]*2*(b[0]*b[1]+b[2]*b[3]),
-a[1]*(b[0]*b[0]-b[1]*b[1]+b[2]*b[2]-b[3]*b[3]), # notice minus sign
-a[1]*(b[0]*b[0]-b[1]*b[1]+b[2]*b[2]-b[3]*b[3]), # notice minus sign
a[2]*2*(b[1]*b[2]-b[0]*b[3]))),
math.fsum(( a[0]*2*(b[0]*b[2]-b[1]*b[3]),
a[1]*2*(b[1]*b[2]+b[0]*b[3]),
-a[2]*(b[0]*b[0]+b[1]*b[1]-b[2]*b[2]-b[3]*b[3]))) # notice minus sign
-a[2]*(b[0]*b[0]+b[1]*b[1]-b[2]*b[2]-b[3]*b[3]))) # notice minus sign
), f32))
def div(a, b, f32=True):
@ -751,13 +745,13 @@ def div(a, b, f32=True):
if ta == int and tb == int:
# special case
if a == -2147483648 and b == -1:
return a # this could be handled by using S32 but it's probably faster this way
return a # this could be handled by using S32 but it's probably faster this way
if (a < 0) ^ (b < 0):
# signs differ - Python rounds towards -inf, we need rounding towards 0
return -(a//-b)
return a//b
ret = F32(ff(a)/ff(b), f32)
if math.isnan(ret): # A NaN result gives a math error.
if math.isnan(ret): # A NaN result gives a math error.
raise ELSLMathError
return ret
if ta == Vector:
@ -803,15 +797,13 @@ def compare(a, b, Eq = True):
ret = a == b
else:
ret = ff(a) == ff(b)
return int(ret) if Eq else 1-ret
return int(ret == Eq)
if ta in (unicode, Key) and tb in (unicode, Key):
ret = 0 if a == b else 1 if a > b or not lslcommon.LSO else -1
ret = 0 if a == b else 1 if not lslcommon.LSO or a > b else -1
return int(not ret) if Eq else ret
if ta == tb in (Vector, Quaternion):
for ae,be in zip(a,b):
if ae != be:
return int(not Eq)
return int(Eq)
ret = not any(ae != be for ae, be in zip(a, b))
return int(ret == Eq)
if ta == tb == list:
ret = len(a) - len(b)
return int(not ret) if Eq else ret
@ -843,12 +835,12 @@ def cond(x):
if lslcommon.LSO and tx == list:
# SVC-689: lists of 1 element count as false
return len(x) > 1
return bool(x) # works fine for int, float, string, list
return bool(x) # works fine for int, float, string, list
def reduce(t):
t = F32(t)
if not t.is_integer():
return t # Accurate-ish until big numbers come into play
return t # Accurate-ish until big numbers come into play
return int(t * 18446744073709551616) % 115904311329233965478 / 18446744073709551616.
#
@ -887,12 +879,10 @@ def llAtan2(y, x):
y = ff(y)
x = ff(x)
if math.isnan(x) and math.isnan(y):
if math.copysign(1, x) == -1 and math.copysign(1, y) == -1:
return -NaN
return NaN
elif math.isnan(x):
return mul(x, y)
if math.isnan(x):
return x
elif math.isnan(y):
if math.isnan(y):
return y
return F32(math.atan2(y, x))
@ -904,7 +894,7 @@ def llAxes2Rot(fwd, left, up):
# One of the hardest.
t = math.fsum((fwd[0], left[1], up[2]))
if t > 0.: # no danger of division by zero or negative roots
if t > 0.: # no danger of division by zero or negative roots
r = math.sqrt(1. + t)
s = 0.5/r
@ -913,12 +903,12 @@ def llAxes2Rot(fwd, left, up):
# Find a positive combo. LSL normalizes the result in these cases only, so we do the same.
if left[1] <= fwd[0] >= up[2]: # is fwd[0] the greatest?
if left[1] <= fwd[0] >= up[2]: # is fwd[0] the greatest?
r = math.sqrt(1. + fwd[0] - left[1] - up[2])
s = 0.5/r
q = (r*0.5, s*(fwd[1]+left[0]), s*(up[0]+fwd[2]), s*(left[2]-up[1]))
elif fwd[0] <= left[1] >= up[2]: # is left[1] the greatest?
elif fwd[0] <= left[1] >= up[2]: # is left[1] the greatest?
r = math.sqrt(1. - fwd[0] + left[1] - up[2])
s = 0.5/r
q = (s*(fwd[1]+left[0]), r*0.5, s*(left[2]+up[1]), s*(up[0]-fwd[2]))
@ -934,7 +924,6 @@ def llAxes2Rot(fwd, left, up):
mag = math.sqrt(math.fsum((q[0]*q[0], q[1]*q[1], q[2]*q[2], q[3]*q[3])))
return Quaternion(F32((q[0]/mag, q[1]/mag, q[2]/mag, q[3]/mag)))
def llAxisAngle2Rot(axis, angle):
axis = v2f(axis)
angle = ff(angle)
@ -964,7 +953,7 @@ def llBase64ToInteger(s):
b64tos_re = re.compile(
b'('
# Those pass through and are caught by InternalUTF8toString:
b'\x00$' # NUL at last position (zstr removes it)
b'\x00$' # NUL at last position (zstr removes it)
b'|[\x09\x0A\x0F\x1F-\x7F\xFE\xFF]|[\xC2-\xDF][\x80-\xBF]'
b'|(?:\xE0[\xA0-\xBF]|[\xE1-\xEF][\x80-\xBF])[\x80-\xBF]'
b'|(?:\xF0[\x90-\xBF]|[\xF1-\xF7][\x80-\xBF])[\x80-\xBF]{2}'
@ -979,7 +968,7 @@ b64tos_re = re.compile(
b'|[\xF0-\xF7][\x80-\xBF]{0,3}'
b'|[\xF8-\xFB][\x80-\xBF]{0,4}'
b'|[\xFC-\xFD][\x80-\xBF]{0,5}'
b')|(.)' # should never be reached
b')|(.)' # should never be reached
)
def llBase64ToString(s):
@ -1033,7 +1022,7 @@ def llCSV2List(s):
bracketlevel += 1
elif c == u'>':
bracketlevel -= 1
elif lastwascomma and c == u' ': # eat space after comma
elif lastwascomma and c == u' ': # eat space after comma
lastwascomma = False
lastidx = i+1
else:
@ -1079,7 +1068,7 @@ def llDumpList2String(lst, sep):
def llEscapeURL(s):
s = fs(s)
s = s.encode('utf8') # get bytes
s = s.encode('utf8') # get bytes
ret = u''
for c in s:
if b'A' <= c <= b'Z' or b'a' <= c <= b'z' or b'0' <= c <= b'9':
@ -1123,7 +1112,7 @@ def llEuler2Rot(v):
def llFabs(f):
f = ff(f)
if f == 0.0 or math.isnan(f): # llFabs(-0.0) is -0.0; llFabs(-nan) is -nan
if f == 0.0 or math.isnan(f): # llFabs(-0.0) is -0.0; llFabs(-nan) is -nan
return f
return math.fabs(f)
@ -1151,7 +1140,7 @@ def llFrand(lim):
if val == lim:
# this should never happen
# (it can happen on denormals, but these cause output of 0.0)
val = 0. # pragma: no cover
val = 0. # pragma: no cover
return val
# Can't give a concrete value
@ -1165,8 +1154,7 @@ def llGenerateKey():
s = hashlib.md5((u'%.17g %f %f' % (time.time(), random.random(),
random.random())).encode('utf8')
).hexdigest()
return Key(s[:8] + '-' + s[8:12] + '-' + s[12:16] + '-' + s[16:20]
+ '-' + s[20:32])
return Key('-'.join((s[:8], s[8:12], s[12:16], s[16:20], s[20:32])))
# Can't give a concrete value
raise ELSLCantCompute
@ -1178,7 +1166,7 @@ def llGetListEntryType(lst, pos):
return Types[type(lst[pos])]
except IndexError:
# list index out of bounds
return 0 # TYPE_INVALID
return 0 # TYPE_INVALID
except KeyError:
# type of element not in Types
raise ELSLInvalidType
@ -1195,12 +1183,13 @@ def llInsertString(s, pos, src):
s = fs(s)
pos = fi(pos)
src = fs(src)
if pos < 0: pos = 0 # llInsertString does not support negative indices
if pos < 0: pos = 0 # llInsertString does not support negative indices
return s[:pos] + src + s[pos:]
def llIntegerToBase64(x):
x = fi(x)
return b64encode(chr((x>>24)&255) + chr((x>>16)&255) + chr((x>>8)&255) + chr(x&255)).decode('utf8')
return b64encode(chr((x>>24)&255) + chr((x>>16)&255) + chr((x>>8)&255)
+ chr(x&255)).decode('utf8')
def llList2CSV(lst):
lst = fl(lst)
@ -1329,7 +1318,7 @@ def llListFindList(lst, elems):
L1 = len(lst)
L2 = len(elems)
if L2 > L1:
return -1 # can't find a sublist longer than the original list
return -1 # can't find a sublist longer than the original list
if L2 == 0:
# empty list is always found at position 0 in Mono,
# and in LSO if the first list isn't empty
@ -1357,15 +1346,15 @@ def llListFindList(lst, elems):
# Unfortunately, Python fails to consider (NaN,) != (NaN,) sometimes
# so we need to implement our own test
for e1e,e2e in zip(e1,e2):
if e1e != e2e: # NaNs are considered different to themselves here as normal
if e1e != e2e: # NaNs are considered different to themselves here as normal
# Mismatch in vector/quaternion sub-element
break
else:
# No mismatch in any sub-element, try next list element
continue
break # discrepancy found
break # discrepancy found
elif type(e1) != type(e2) or e1 != e2:
break # mismatch
break # mismatch
else:
# no mismatch
return i
@ -1403,9 +1392,9 @@ def llListSort(lst, stride, asc):
lst = fl(lst)
stride = fi(stride)
asc = fi(asc)
lst = lst[:] # make a copy
lst = lst[:] # make a copy
L = len(lst)
broken = u'\ufb1a' > u'\U0001d41a' # that happens on Windows
broken = u'\ufb1a' > u'\U0001d41a' # that happens on Windows
if stride < 1: stride = 1
if L % stride:
return lst
@ -1414,12 +1403,12 @@ def llListSort(lst, stride, asc):
a = lst[i]
ta = type(a)
if ta == Vector:
a = v2f(a) # list should contain vectors made only of floats
a = v2f(a) # list should contain vectors made only of floats
a = a[0]*a[0] + a[1]*a[1] + a[2]*a[2]
if broken and ta in (unicode, Key):
# Note this breaks type consistency between a and ta!
# It should be OK because only equal types are compared.
a = a.encode('utf-32-be') # pragma: no cover
a = a.encode('utf-32-be') # pragma: no cover
for j in xrange(i+stride, L, stride):
b = lst[j]
tb = type(b)
@ -1431,8 +1420,8 @@ def llListSort(lst, stride, asc):
# (note NaNs compare as > thus the reversed condition!)
elif tb != Quaternion:
if broken and tb in (unicode, Key):
b = b.encode('utf-32-be') # pragma: no cover
gt = not (a <= b) # float, integer, string, key all take this branch
b = b.encode('utf-32-be') # pragma: no cover
gt = not (a <= b) # float, integer, string, key all take this branch
# (note NaNs compare as > thus the reversed condition!)
if gt ^ (asc != 1):
# swap
@ -1444,7 +1433,7 @@ def llListSort(lst, stride, asc):
a = v2f(a)
a = a[0]*a[0] + a[1]*a[1] + a[2]*a[2]
if broken and ta in (unicode, Key):
a = a.encode('utf-32-be') # pragma: no cover
a = a.encode('utf-32-be') # pragma: no cover
return lst
def llListStatistics(op, lst):
@ -1460,10 +1449,10 @@ def llListStatistics(op, lst):
if nums == []:
return 0.0
if op == 8: # LIST_STAT_NUM_COUNT
if op == 8: # LIST_STAT_NUM_COUNT
return float(len(nums))
if op in (0, 1, 2) : # LIST_STAT_RANGE, LIST_STAT_MIN, LIST_STAT_MAX
if op in (0, 1, 2): # LIST_STAT_RANGE, LIST_STAT_MIN, LIST_STAT_MAX
min = None
for elem in nums:
if min is None:
@ -1475,7 +1464,7 @@ def llListStatistics(op, lst):
max = elem
return F32(max - min if op == 0 else min if op == 1 else max)
if op == 4: # LIST_STAT_MEDIAN requires special treatment
if op == 4: # LIST_STAT_MEDIAN requires special treatment
# The function behaves very strangely with NaNs. This seems to reproduce it:
# llListSort seems to do the right thing with NaNs as needed by the median.
@ -1485,7 +1474,7 @@ def llListStatistics(op, lst):
return F32(nums[L>>1])
return F32((nums[(L>>1)-1] + nums[L>>1])*0.5)
if op in (3, 5, 6, 7): # LIST_STAT_MEAN, STD_DEV, SUM, SUM_SQUARES
if op in (3, 5, 6, 7): # LIST_STAT_MEAN, STD_DEV, SUM, SUM_SQUARES
sum = 0.
sumsq = 0.
mean = 0.
@ -1499,15 +1488,15 @@ def llListStatistics(op, lst):
mean += delta/N
M2 += delta*(elem-mean)
if op == 5: # LIST_STAT_STD_DEV
if op == 5: # LIST_STAT_STD_DEV
return 0. if N == 1. else F32(math.sqrt(M2/(N-1.)))
if op == 6: # LIST_STAT_SUM
if op == 6: # LIST_STAT_SUM
return F32(sum)
if op == 7: # LIST_STAT_SUM_SQUARES
if op == 7: # LIST_STAT_SUM_SQUARES
return F32(sumsq)
return F32(mean)
if op == 9: # LIST_STAT_GEOMETRIC_MEAN
if op == 9: # LIST_STAT_GEOMETRIC_MEAN
N = 0.
GMlog = 0.
for elem in nums:
@ -1551,12 +1540,9 @@ def llModPow(base, exp, mod):
if exp == 0:
return 1
# Convert all numbers to unsigned
if base < 0:
base += 4294967296
if exp < 0:
exp += 4294967296
if mod < 0:
mod += 4294967296
base &= 0xFFFFFFFF
exp &= 0xFFFFFFFF
mod &= 0xFFFFFFFF
prod = base
ret = 1
while True:
@ -1606,22 +1592,22 @@ def llPow(base, exp):
if exp == 0.0:
return 1.0
if base == 0.0: # Python gives exception on these, LSL returns stuff
if base == 0.0: # Python gives exception on these, LSL returns stuff
if math.isinf(exp) and exp < 0:
return Infinity # llPow(0.0, -inf) = inf
return Infinity # llPow(0.0, -inf) = inf
if exp < 0.0:
# Negative finite exponent cases
if math.copysign(1, base) < 0 and exp.is_integer() and not (exp/2.).is_integer():
return -Infinity # llPow(-0.0, -odd_integer) = -inf
return -Infinity # llPow(-0.0, -odd_integer) = -inf
return Infinity
elif abs(base) == 1.0 and math.isinf(exp):
return NaN # Python says 1.0
return NaN # Python says 1.0
f = F32(math.pow(base, exp))
return 0.0 if f == 0.0 else f # don't return -0.0
except ValueError: # should happen only with negative base and noninteger exponent
return 0.0 if f == 0.0 else f # don't return -0.0
except ValueError: # should happen only with negative base and noninteger exponent
return Indet
def llRot2Angle(r):
@ -1701,7 +1687,7 @@ def llRotBetween(v1, v2):
# which will be perpendicular to both. But matching the SL results requires
# another cross product of the input with the result, so we do that.
ortho = mod(mod(v1, Vector((1., 0., 0.))), v1)
ortho = Vector((0. if f == 0. else f for f in ortho)) # remove minus zero
ortho = Vector((0. if f == 0. else f for f in ortho)) # remove minus zero
m = mul(ortho, ortho)
if m < float.fromhex('0x1.b7cdfep-34'):
# The input vectors were aligned with <1,0,0>, so this was not a
@ -1749,10 +1735,10 @@ def llStringTrim(s, mode):
head = 0
length = len(s)
tail = length-1
if mode & 1: # STRING_TRIM_HEAD
if mode & 1: # STRING_TRIM_HEAD
while head < length and s[head] in u'\x09\x0a\x0b\x0c\x0d\x20':
head += 1
if mode & 2: # STRING_TRIM_TAIL
if mode & 2: # STRING_TRIM_TAIL
while tail >= head and s[tail] in u'\x09\x0a\x0b\x0c\x0d\x20':
tail -= 1
return s[head:tail+1]
@ -1795,14 +1781,14 @@ def llUnescapeURL(s):
continue
if i >= L:
break
c = s[i] # First digit
c = s[i] # First digit
i += 1
if i >= L:
break
v = 0
if u'0' <= c <= u'9' or u'A' <= c <= u'F' or u'a' <= c <= u'f':
v = int(c, 16)<<4
c = s[i] # Second digit
c = s[i] # Second digit
if c == u'%':
ret += chr(v)
i += 1