ursinamath
 </>  ursina.ursinamath 
Functions
 
distance()
 copy distance(a=None, b=None)
 
 
distance_2d()
 copy distance_2 d(a=None, b=None)
 
 
distance_xz()
 copy distance_xz(a=None, b=None)
 
 
lerp()
 copy lerp(a=None, b=None, t=None)
 
 
inverselerp()
 copy inverselerp(a=None, b=None, value=None)
 
get *where* between a and b, value is (0.0 - 1.0) 
 
lerp_exponential_decay()
 copy lerp_exponential_decay(a=None, b=None, dt=None, decay_rate=1 )
 
frame-rate independent lerp for use in update. use this instead of lerp(a, b, time.dt) in update. 
 
lerp_angle()
 copy lerp_angle(start_angle=None, end_angle=None, t=None)
 
 
slerp()
 copy slerp(q1 =None, q2 =None, t=None)
 
 
slerp_exponential_decay()
 copy slerp_exponential_decay(q1 =None, q2 =None, decay_rate=None)
 
frame-rate independent version of slerp for use in update. 
 
clamp()
 copy clamp(value=None, floor=None, ceiling=None)
 
 
round_to_closest()
 copy round_to_closest(value=None, step=0 )
 
 
rotate_around_point_2d()
 copy rotate_around_point_2 d(point=None, origin =None, deg=None)
 
 
world_position_to_screen_position()
 copy world_position_to_screen_position(point=None)
 
get screen position(ui space) from world space. 
 
sum()
 copy sum(l=None)
 
 
make_gradient()
 copy make_gradient(index_value_dict=None)
 
 
sample_gradient()
 copy sample_gradient(list_of_values=None, t=None)
 
distribute list_of_values equally on a line and get the interpolated value at t (0-1). 
 
Examples
 
copy     from  ursina.ursinastuff import  _test
    _test(inverselerp(0 , 1 0 0 , 5 0 ) == .5 )
    _test(lerp(0 , 1 0 0 , .5 ) == 5 0 )
def  lerp_exponential_decay(a, b, dt, decay_rate=1 ):    # frame-rate independent lerp for  use in update. use this instead of lerp(a, b, time.dt) in update. 
    return lerp(a, b, 1  - exp(-decay_rate * dt))
def  lerp_angle(start_angle, end_angle, t):
    start_angle = start_angle % 3 6 0 
    end_angle = end_angle % 3 6 0 
    angle_diff = (end_angle - start_angle + 1 8 0 ) % 3 6 0  - 1 8 0 
    result_angle = start_angle + t * angle_diff
    result_angle = (result_angle + 3 6 0 ) % 3 6 0 
    return result_angle
def  slerp(q1 , q2 , t):
    costheta = q1 .dot(q2 )
    if  costheta < 0 .0 :
        q2  = -q2 
        costheta = -costheta
    costheta = clamp(costheta, -1 .0 , 1 .0 )   # ensure valid range for  acos 
    theta = acos(costheta)
    if  abs(theta) < 0 .0 0 0 1 :
        return q2 
    sintheta = sqrt(1 .0  - costheta * costheta)
    if  abs(sintheta) < 0 .0 0 0 1 :
        return (q1  + q2 ) * 0 .5 
    r1  = sin((1 .0  - t) * theta) / sintheta
    r2  = sin(t * theta) / sintheta
    return (q1  * r1 ) + (q2  * r2 )
def  slerp_exponential_decay(q1 , q2 , decay_rate):    # frame-rate independent version of slerp for  use in update. 
    return slerp(q1 , q2 , 1  - pow(0 .0 1 , decay_rate))
def  clamp(value, floor, ceiling):
    return max(min(value, ceiling), floor)
def  round_to_closest(value, step=0 ):
    if  not  step:
        return value
    step = 1 /step
    return round(value * step) / step
def  rotate_around_point_2 d(point, origin, deg):
    angle_rad = -deg/1 8 0  * pi # ursina rotation is positive=clockwise, so do *= -1  
    cos_angle = cos(angle_rad)
    sin_angle = sin(angle_rad)
    dx = point[0 ] - origin[0 ]
    dy = point[1 ] - origin[1 ]
    return (
        origin[0 ] + (dx*cos_angle - dy*sin_angle),
        origin[1 ] + (dx*sin_angle + dy*cos_angle)
        )
def  world_position_to_screen_position(point): # get screen position(ui space) from  world space. 
    from  ursina import  camera, Entity , destroy
    _temp_entity = Entity (position =point, add_to_scene_entities=False)
    result = _temp_entity.screen_position
    destroy(_temp_entity)
    return result
def  sum(l):
    try:
        return _sum(l)
    except:
        pass
    return _sum(l, l[0 ].__class__())
def  make_gradient(index_value_dict):
    '' '
    given a dict of positions and values (usually colors), interpolates the values into a list of with the interpolated values.
    example input: {'0 ' :color.hex('#9 d9 8 6 7 '  ), '3 8 ' :color.hex('#8 2 8 1 3 1 '  ), '5 4 ' :color.hex('#5 d5 b2 a'  ), '2 5 5 ' :color.hex('#0 0 0 0 0 0 '  )}
    '' '
    min_index = min(int(e) for  e in index_value_dict.keys())
    max_index = max(int(e) for  e in index_value_dict.keys())
    gradient = [None for  _ in range (max_index+1 -min_index)]
    sorted_dict = [(idx, index_value_dict[str(idx)]) for  idx in sorted([int(key) for  key in index_value_dict.keys()])]
    for  i in range (len(sorted_dict)-1 ):
        start_index, start_value = sorted_dict[i]
        next_index, next_value = sorted_dict[i+1 ]
        dist = next_index - start_index
        for  j in range (dist+1 ):
            gradient[start_index+j-min_index] = lerp(start_value, next_value, j/dist)
    return gradient
if  __name__ == '__main__' :
    _test(make_gradient({'0 ' :color.hex('#ff0 0 0 0 ff'  ), '2 ' :color.hex('#ffffffff'  )}) == [
        color.hex('#ff0 0 0 0 ff'  ),
        lerp(color.hex('#ff0 0 0 0 ff'  ), color.hex('#ffffffff'  ), .5 ),
        color.hex('#ffffffff'  ),
        ])
    _test(make_gradient({'0 ' :color.hex('#ff0 0 0 0 ff'  ), '4 ' :color.hex('#ffffffff'  )}) == [
        color.hex('#ff0 0 0 0 ff'  ),
        lerp(color.hex('#ff0 0 0 0 ff'  ), color.hex('#ffffffff'  ), .2 5 ),
        lerp(color.hex('#ff0 0 0 0 ff'  ), color.hex('#ffffffff'  ), .5 ),
        lerp(color.hex('#ff0 0 0 0 ff'  ), color.hex('#ffffffff'  ), .7 5 ),
        color.hex('#ffffffff'  ),
        ])
    _test(make_gradient({'0 ' :1 6 , '2 ' :0 }) == [1 6 , 8 , 0 ])
    _test(make_gradient({'6 ' :0 , '8 ' :8 }) == [0 , 4 , 8 ])
def  sample_gradient(list_of_values, t):     # distribute list_of_values equally on a line and get the interpolated value at t (0 -1 ). 
    l = len(list_of_values)
    if  l == 1 :
        return list_of_values[0 ]
    t *= l-1 
    index = floor(t - .0 0 1 )
    index = clamp(index, 0 , l-1 )
    relative = t - index
    if  index < l-1 :
        return lerp(list_of_values[index], list_of_values[index+1 ], relative)
    else :
        return lerp(list_of_values[index-1 ], list_of_values[index], relative)
class Bounds:
    __slots__ = ['start' , 'end' , 'center' , 'size' ]
    def  __init__(self, *, start:Vec3 =None, end:Vec3 =None, center:Vec3 =None, size:Vec3 =None):
        if  start is not  None and end is not  None:
            self.start = start
            self.end = end
            self.size = end - start
            self.center = start + (self.size * 0 .5 )
        elif  center is not  None and size is not  None:
            self.center = center
            self.size = size
            half = size * 0 .5 
            self.start = center - half
            self.end = center + half
        else :
            raise ValueError("Must provide either (start and end) or (center and size)")
    def  __repr__(self):
        return f"Bounds(start={self.start}, end={self.end}, center={self.center}, size={self.size})"
if  __name__ == '__main__' :
    from  ursina import  *
    from  ursinastuff import  _test
    app = Ursina()
    e1  = Entity (position = (0 ,0 ,0 ))
    e2  = Entity (position = (0 ,1 ,1 ))
    _test(distance(e1 , e2 ) == 1 .4 1 4 2 1 3 5 6 2 3 7 3 0 9 5 1 )
    _test(distance_2 d(Vec2(0 ,0 ), Vec2(1 ,1 )) == 1 .4 1 4 2 1 3 5 6 2 3 7 3 0 9 5 1 )
    distance_xz(e1 , e2 .position)
    between_color = lerp(color.lime, color.magenta, .5 )
    print (between_color)
    print (lerp((0 ,0 ), (0 ,1 ), .5 ))
    print (lerp(Vec2(0 ,0 ), Vec2(0 ,1 ), .5 ))
    print (lerp([0 ,0 ], [0 ,1 ], .5 ))
    print (round(Vec3(.3 8 , .1 3 5 1 , 3 5 3 .2 6 ), 2 ))
    p = (1 ,0 )
    print (p, 'rotated ->' , rotate_around_point_2 d(p, (0 ,0 ), 9 0 ))
    app.run()