def create_transformation(self, rotation=None, translation=None):
        """
        Creates a transformation matrix woth rotations and translation.

        Args:
            rotation: 3 component vector as a list, tuple, or :py:class:`pyrr.Vector3`
            translation: 3 component vector as a list, tuple, or :py:class:`pyrr.Vector3`

        Returns:
            A 4x4 matrix as a :py:class:`numpy.array`
        """
        mat = None
        if rotation is not None:
            mat = Matrix44.from_eulers(Vector3(rotation))

        if translation is not None:
            trans = matrix44.create_from_translation(Vector3(translation))
            if mat is None:
                mat = trans
            else:
                mat = matrix44.multiply(mat, trans)

        return mat

def create_transformation ( self , rotation = None , translation = None ) : mat = None if rotation is not None : mat = Matrix44 . from_eulers ( Vector3 ( rotation ) ) if translation is not None : trans = matrix44 . create_from_translation ( Vector3 ( translation ) ) if mat is None : mat = trans else : mat = matrix44 . multiply ( mat , trans ) return mat

Creates a transformation matrix woth rotations and translation .


def create_normal_matrix(self, modelview):
        """
        Creates a normal matrix from modelview matrix

        Args:
            modelview: The modelview matrix

        Returns:
            A 3x3 Normal matrix as a :py:class:`numpy.array`
        """
        normal_m = Matrix33.from_matrix44(modelview)
        normal_m = normal_m.inverse
        normal_m = normal_m.transpose()
        return normal_m

def create_normal_matrix ( self , modelview ) : normal_m = Matrix33 . from_matrix44 ( modelview ) normal_m = normal_m . inverse normal_m = normal_m . transpose ( ) return normal_m


def available_templates(value):
    """Scan for available templates in effect_templates"""
    templates = list_templates()

    if value not in templates:
        raise ArgumentTypeError("Effect template '{}' does not exist.\n Available templates: {} ".format(
            value, ", ".join(templates)))

    return value


def root_path():
    """Get the absolute path to the root of the demosys package"""
    module_dir = os.path.dirname(globals()['__file__'])
    return os.path.dirname(os.path.dirname(module_dir))

def root_path ( ) : module_dir = os . path . dirname ( globals ( ) [ '__file__' ] ) return os . path . dirname ( os . path . dirname ( module_dir ) )


def load(self):
        """Load a file in text mode"""
        self.meta.resolved_path = self.find_data(self.meta.path)

        if not self.meta.resolved_path:
            raise ImproperlyConfigured("Data file '{}' not found".format(self.meta.path))

        print("Loading:", self.meta.path)

        with open(self.meta.resolved_path, 'r') as fd:
            return fd.read()

def
load
(
self
)
:
self
.
meta
.
resolved_path
=
self
.
find_data
(
self
.
meta
.
path
)
if
not
self
.
meta
.
resolved_path
:
raise
ImproperlyConfigured
(
"Data file '{}' not found"
.
format
(
self
.
meta
.
path
)
)
print
(
"Loading:"
,
self
.
meta
.
path
)
with
open
(
self
.
meta
.
resolved_path
,
'r'
)
as
fd
:
return
fd
.
read
(
)


def get_finder(import_path):
    """
    Get a finder class from an import path.
    Raises ``demosys.core.exceptions.ImproperlyConfigured`` if the finder is not found.
    This function uses an lru cache.

    :param import_path: string representing an import path
    :return: An instance of the finder
    """
    Finder = import_string(import_path)
    if not issubclass(Finder, BaseFileSystemFinder):
        raise ImproperlyConfigured('Finder {} is not a subclass of core.finders.FileSystemFinder'.format(import_path))
    return Finder()

Get a finder class from an import path . Raises demosys . core . exceptions . ImproperlyConfigured if the finder is not found . This function uses an lru cache .


def find(self, path: Path):
        """
        Find a file in the path. The file may exist in multiple
        paths. The last found file will be returned.

        :param path: The path to find
        :return: The absolute path to the file or None if not found
        """
        # Update paths from settings to make them editable runtime
        # This is only possible for FileSystemFinders
        if getattr(self, 'settings_attr', None):
            self.paths = getattr(settings, self.settings_attr)

        path_found = None

        for entry in self.paths:
            abspath = entry / path
            if abspath.exists():
                path_found = abspath

        return path_found

Find a file in the path . The file may exist in multiple paths . The last found file will be returned .


def update(self, aspect_ratio=None, fov=None, near=None, far=None):
        """
        Update the internal projection matrix based on current values
        or values passed in if specified.

        :param aspect_ratio: New aspect ratio
        :param fov: New field of view
        :param near: New near value
        :param far: New far value
        """
        self.aspect_ratio = aspect_ratio or self.aspect_ratio
        self.fov = fov or self.fov
        self.near = near or self.near
        self.far = far or self.far

        self.matrix = Matrix44.perspective_projection(self.fov, self.aspect_ratio, self.near, self.far)

def update ( self , aspect_ratio = None , fov = None , near = None , far = None ) : self . aspect_ratio = aspect_ratio or self . aspect_ratio self . fov = fov or self . fov self . near = near or self . near self . far = far or self . far self . matrix = Matrix44 . perspective_projection ( self . fov , self . aspect_ratio , self . near , self . far )

Update the internal projection matrix based on current values or values passed in if specified .


def draw(self, projection_matrix=None, camera_matrix=None, time=0):
        """
        Draw node and children

        :param projection_matrix: projection matrix (bytes)
        :param camera_matrix: camera_matrix (bytes)
        :param time: The current time
        """
        if self.mesh:
            self.mesh.draw(
                projection_matrix=projection_matrix,
                view_matrix=self.matrix_global_bytes,
                camera_matrix=camera_matrix,
                time=time
            )

        for child in self.children:
            child.draw(
                projection_matrix=projection_matrix,
                camera_matrix=camera_matrix,
                time=time
            )

def draw ( self , projection_matrix = None , camera_matrix = None , time = 0 ) : if self . mesh : self . mesh . draw ( projection_matrix = projection_matrix , view_matrix = self . matrix_global_bytes , camera_matrix = camera_matrix , time = time ) for child in self . children : child . draw ( projection_matrix = projection_matrix , camera_matrix = camera_matrix , time = time )


def calc_global_bbox(self, view_matrix, bbox_min, bbox_max):
        """Recursive calculation of scene bbox"""
        if self.matrix is not None:
            view_matrix = matrix44.multiply(self.matrix, view_matrix)

        if self.mesh:
            bbox_min, bbox_max = self.mesh.calc_global_bbox(view_matrix, bbox_min, bbox_max)

        for child in self.children:
            bbox_min, bbox_max = child.calc_global_bbox(view_matrix, bbox_min, bbox_max)

        return bbox_min, bbox_max

def calc_global_bbox ( self , view_matrix , bbox_min , bbox_max ) : if self . matrix is not None : view_matrix = matrix44 . multiply ( self . matrix , view_matrix ) if self . mesh : bbox_min , bbox_max = self . mesh . calc_global_bbox ( view_matrix , bbox_min , bbox_max ) for child in self . children : bbox_min , bbox_max = child . calc_global_bbox ( view_matrix , bbox_min , bbox_max ) return bbox_min , bbox_max


def swap_buffers(self):
        """
        Swaps buffers, incement the framecounter and pull events.
        """
        self.frames += 1
        glfw.swap_buffers(self.window)
        self.poll_events()

def swap_buffers ( self ) : self . frames += 1 glfw . swap_buffers ( self . window ) self . poll_events ( )

Swaps buffers incement the framecounter and pull events .


def resize(self, width, height):
        """
        Sets the new size and buffer size internally
        """
        self.width = width
        self.height = height
        self.buffer_width, self.buffer_height = glfw.get_framebuffer_size(self.window)
        self.set_default_viewport()

def resize ( self , width , height ) : self . width = width self . height = height self . buffer_width , self . buffer_height = glfw . get_framebuffer_size ( self . window ) self . set_default_viewport ( )


def check_glfw_version(self):
        """
        Ensure glfw library  version is compatible
        """
        print("glfw version: {} (python wrapper version {})".format(glfw.get_version(), glfw.__version__))
        if glfw.get_version() < self.min_glfw_version:
            raise ValueError("Please update glfw binaries to version {} or later".format(self.min_glfw_version))


def quad_2d(width, height, xpos=0.0, ypos=0.0) -> VAO:
    """
    Creates a 2D quad VAO using 2 triangles with normals and texture coordinates.

    Args:
        width (float): Width of the quad
        height (float): Height of the quad

    Keyword Args:
        xpos (float): Center position x
        ypos (float): Center position y

    Returns:
        A :py:class:`demosys.opengl.vao.VAO` instance.
    """
    pos = numpy.array([
        xpos - width / 2.0, ypos + height / 2.0, 0.0,
        xpos - width / 2.0, ypos - height / 2.0, 0.0,
        xpos + width / 2.0, ypos - height / 2.0, 0.0,
        xpos - width / 2.0, ypos + height / 2.0, 0.0,
        xpos + width / 2.0, ypos - height / 2.0, 0.0,
        xpos + width / 2.0, ypos + height / 2.0, 0.0,
    ], dtype=numpy.float32)

    normals = numpy.array([
        0.0, 0.0, 1.0,
        0.0, 0.0, 1.0,
        0.0, 0.0, 1.0,
        0.0, 0.0, 1.0,
        0.0, 0.0, 1.0,
        0.0, 0.0, 1.0,
    ], dtype=numpy.float32)

    uvs = numpy.array([
        0.0, 1.0,
        0.0, 0.0,
        1.0, 0.0,
        0.0, 1.0,
        1.0, 0.0,
        1.0, 1.0,
    ], dtype=numpy.float32)

    vao = VAO("geometry:quad", mode=moderngl.TRIANGLES)
    vao.buffer(pos, '3f', ["in_position"])
    vao.buffer(normals, '3f', ["in_normal"])
    vao.buffer(uvs, '2f', ["in_uv"])

    return vao

def quad_2d ( width , height , xpos = 0.0 , ypos = 0.0 ) -> VAO : pos = numpy . array ( [ xpos - width / 2.0 , ypos + height / 2.0 , 0.0 , xpos - width / 2.0 , ypos - height / 2.0 , 0.0 , xpos + width / 2.0 , ypos - height / 2.0 , 0.0 , xpos - width / 2.0 , ypos + height / 2.0 , 0.0 , xpos + width / 2.0 , ypos - height / 2.0 , 0.0 , xpos + width / 2.0 , ypos + height / 2.0 , 0.0 , ] , dtype = numpy . float32 ) normals = numpy . array ( [ 0.0 , 0.0 , 1.0 , 0.0 , 0.0 , 1.0 , 0.0 , 0.0 , 1.0 , 0.0 , 0.0 , 1.0 , 0.0 , 0.0 , 1.0 , 0.0 , 0.0 , 1.0 , ] , dtype = numpy . float32 ) uvs = numpy . array ( [ 0.0 , 1.0 , 0.0 , 0.0 , 1.0 , 0.0 , 0.0 , 1.0 , 1.0 , 0.0 , 1.0 , 1.0 , ] , dtype = numpy . float32 ) vao = VAO ( "geometry:quad" , mode = moderngl . TRIANGLES ) vao . buffer ( pos , '3f' , [ "in_position" ] ) vao . buffer ( normals , '3f' , [ "in_normal" ] ) vao . buffer ( uvs , '2f' , [ "in_uv" ] ) return vao

Creates a 2D quad VAO using 2 triangles with normals and texture coordinates .


def translate_buffer_format(vertex_format):
    """Translate the buffer format"""
    buffer_format = []
    attributes = []
    mesh_attributes = []

    if "T2F" in vertex_format:
        buffer_format.append("2f")
        attributes.append("in_uv")
        mesh_attributes.append(("TEXCOORD_0", "in_uv", 2))

    if "C3F" in vertex_format:
        buffer_format.append("3f")
        attributes.append("in_color")
        mesh_attributes.append(("NORMAL", "in_color", 3))

    if "N3F" in vertex_format:
        buffer_format.append("3f")
        attributes.append("in_normal")
        mesh_attributes.append(("NORMAL", "in_normal", 3))

    buffer_format.append("3f")
    attributes.append("in_position")
    mesh_attributes.append(("POSITION", "in_position", 3))

    return " ".join(buffer_format), attributes, mesh_attributes

def
translate_buffer_format
(
vertex_format
)
:
buffer_format
=
[
]
attributes
=
[
]
mesh_attributes
=
[
]
if
"T2F"
in
vertex_format
:
buffer_format
.
append
(
"2f"
)
attributes
.
append
(
"in_uv"
)
mesh_attributes
.
append
(
(
"TEXCOORD_0"
,
"in_uv"
,
2
)
)
if
"C3F"
in
vertex_format
:
buffer_format
.
append
(
"3f"
)
attributes
.
append
(
"in_color"
)
mesh_attributes
.
append
(
(
"NORMAL"
,
"in_color"
,
3
)
)
if
"N3F"
in
vertex_format
:
buffer_format
.
append
(
"3f"
)
attributes
.
append
(
"in_normal"
)
mesh_attributes
.
append
(
(
"NORMAL"
,
"in_normal"
,
3
)
)
buffer_format
.
append
(
"3f"
)
attributes
.
append
(
"in_position"
)
mesh_attributes
.
append
(
(
"POSITION"
,
"in_position"
,
3
)
)
return
" "
.
join
(
buffer_format
)
,
attributes
,
mesh_attributes


def stop(self) -> float:
        """
        Stop the timer

        Returns:
            The time the timer was stopped
        """
        self.stop_time = time.time()
        return self.stop_time - self.start_time - self.offset

def stop ( self ) -> float : self . stop_time = time . time ( ) return self . stop_time - self . start_time - self . offset


def set_time(self, value: float):
        """
        Set the current time. This can be used to jump in the timeline.

        Args:
            value (float): The new time
        """
        if value < 0:
            value = 0

        self.offset += self.get_time() - value

def set_time ( self , value : float ) : if value < 0 : value = 0 self . offset += self . get_time ( ) - value

Set the current time . This can be used to jump in the timeline .


def resolve_loader(self, meta: SceneDescription):
        """
        Resolve scene loader based on file extension
        """
        for loader_cls in self._loaders:
            if loader_cls.supports_file(meta):
                meta.loader_cls = loader_cls
                break
        else:
            raise ImproperlyConfigured(
                "Scene {} has no loader class registered. Check settings.SCENE_LOADERS".format(meta.path))


def on_resize(self, width, height):
        """
        Pyglet specific callback for window resize events.
        """
        self.width, self.height = width, height
        self.buffer_width, self.buffer_height = width, height
        self.resize(width, height)

def on_resize ( self , width , height ) : self . width , self . height = width , height self . buffer_width , self . buffer_height = width , height self . resize ( width , height )

Pyglet specific callback for window resize events .


def swap_buffers(self):
        """
        Swap buffers, increment frame counter and pull events
        """
        if not self.window.context:
            return

        self.frames += 1
        self.window.flip()
        self.window.dispatch_events()

def swap_buffers ( self ) : if not self . window . context : return self . frames += 1 self . window . flip ( ) self . window . dispatch_events ( )

Swap buffers increment frame counter and pull events


def sphere(radius=0.5, sectors=32, rings=16) -> VAO:
    """
    Creates a sphere.

    Keyword Args:
        radius (float): Radius or the sphere
        rings (int): number or horizontal rings
        sectors (int): number of vertical segments

    Returns:
        A :py:class:`demosys.opengl.vao.VAO` instance
    """
    R = 1.0 / (rings - 1)
    S = 1.0 / (sectors - 1)

    vertices = [0] * (rings * sectors * 3)
    normals = [0] * (rings * sectors * 3)
    uvs = [0] * (rings * sectors * 2)

    v, n, t = 0, 0, 0
    for r in range(rings):
        for s in range(sectors):
            y = math.sin(-math.pi / 2 + math.pi * r * R)
            x = math.cos(2 * math.pi * s * S) * math.sin(math.pi * r * R)
            z = math.sin(2 * math.pi * s * S) * math.sin(math.pi * r * R)

            uvs[t] = s * S
            uvs[t + 1] = r * R

            vertices[v] = x * radius
            vertices[v + 1] = y * radius
            vertices[v + 2] = z * radius

            normals[n] = x
            normals[n + 1] = y
            normals[n + 2] = z

            t += 2
            v += 3
            n += 3

    indices = [0] * rings * sectors * 6
    i = 0
    for r in range(rings - 1):
        for s in range(sectors - 1):
            indices[i] = r * sectors + s
            indices[i + 1] = (r + 1) * sectors + (s + 1)
            indices[i + 2] = r * sectors + (s + 1)

            indices[i + 3] = r * sectors + s
            indices[i + 4] = (r + 1) * sectors + s
            indices[i + 5] = (r + 1) * sectors + (s + 1)
            i += 6

    vbo_vertices = numpy.array(vertices, dtype=numpy.float32)
    vbo_normals = numpy.array(normals, dtype=numpy.float32)
    vbo_uvs = numpy.array(uvs, dtype=numpy.float32)
    vbo_elements = numpy.array(indices, dtype=numpy.uint32)

    vao = VAO("sphere", mode=mlg.TRIANGLES)
    # VBOs
    vao.buffer(vbo_vertices, '3f', ['in_position'])
    vao.buffer(vbo_normals, '3f', ['in_normal'])
    vao.buffer(vbo_uvs, '2f', ['in_uv'])
    vao.index_buffer(vbo_elements, index_element_size=4)

    return vao

def
sphere
(
radius
=
0.5
,
sectors
=
32
,
rings
=
16
)
->
VAO
:
R
=
1.0
/
(
rings
1
)
S
=
1.0
/
(
sectors
1
)
vertices
=
[
0
]
*
(
rings
*
sectors
*
3
)
normals
=
[
0
]
*
(
rings
*
sectors
*
3
)
uvs
=
[
0
]
*
(
rings
*
sectors
*
2
)
v
,
n
,
t
=
0
,
0
,
0
for
r
in
range
(
rings
)
:
for
s
in
range
(
sectors
)
:
y
=
math
.
sin
(
math
.
pi
/
2
+
math
.
pi
*
r
*
R
)
x
=
math
.
cos
(
2
*
math
.
pi
*
s
*
S
)
*
math
.
sin
(
math
.
pi
*
r
*
R
)
z
=
math
.
sin
(
2
*
math
.
pi
*
s
*
S
)
*
math
.
sin
(
math
.
pi
*
r
*
R
)
uvs
[
t
]
=
s
*
S
uvs
[
t
+
1
]
=
r
*
R
vertices
[
v
]
=
x
*
radius
vertices
[
v
+
1
]
=
y
*
radius
vertices
[
v
+
2
]
=
z
*
radius
normals
[
n
]
=
x
normals
[
n
+
1
]
=
y
normals
[
n
+
2
]
=
z
t
+=
2
v
+=
3
n
+=
3
indices
=
[
0
]
*
rings
*
sectors
*
6
i
=
0
for
r
in
range
(
rings
1
)
:
for
s
in
range
(
sectors
1
)
:
indices
[
i
]
=
r
*
sectors
+
s
indices
[
i
+
1
]
=
(
r
+
1
)
*
sectors
+
(
s
+
1
)
indices
[
i
+
2
]
=
r
*
sectors
+
(
s
+
1
)
indices
[
i
+
3
]
=
r
*
sectors
+
s
indices
[
i
+
4
]
=
(
r
+
1
)
*
sectors
+
s
indices
[
i
+
5
]
=
(
r
+
1
)
*
sectors
+
(
s
+
1
)
i
+=
6
vbo_vertices
=
numpy
.
array
(
vertices
,
dtype
=
numpy
.
float32
)
vbo_normals
=
numpy
.
array
(
normals
,
dtype
=
numpy
.
float32
)
vbo_uvs
=
numpy
.
array
(
uvs
,
dtype
=
numpy
.
float32
)
vbo_elements
=
numpy
.
array
(
indices
,
dtype
=
numpy
.
uint32
)
vao
=
VAO
(
"sphere"
,
mode
=
mlg
.
TRIANGLES
)
# VBOs
vao
.
buffer
(
vbo_vertices
,
'3f'
,
[
'in_position'
]
)
vao
.
buffer
(
vbo_normals
,
'3f'
,
[
'in_normal'
]
)
vao
.
buffer
(
vbo_uvs
,
'2f'
,
[
'in_uv'
]
)
vao
.
index_buffer
(
vbo_elements
,
index_element_size
=
4
)
return
vao


def swap_buffers(self):
        """
        Headless window currently don't support double buffering.
        We only increment the frame counter here.
        """
        self.frames += 1

        if self.headless_frames and self.frames >= self.headless_frames:
            self.close()

def swap_buffers ( self ) : self . frames += 1 if self . headless_frames and self . frames >= self . headless_frames : self . close ( )

Headless window currently don t support double buffering . We only increment the frame counter here .


def load(self, meta: ResourceDescription) -> Any:
        """
        Loads a resource or return existing one

        :param meta: The resource description
        """
        self._check_meta(meta)
        self.resolve_loader(meta)
        return meta.loader_cls(meta).load()

def load ( self , meta : ResourceDescription ) -> Any : self . _check_meta ( meta ) self . resolve_loader ( meta ) return meta . loader_cls ( meta ) . load ( )


def load_pool(self):
        """
        Loads all the data files using the configured finders.
        """
        for meta in self._resources:
            resource = self.load(meta)
            yield meta, resource

        self._resources = []

def load_pool ( self ) : for meta in self . _resources : resource = self . load ( meta ) yield meta , resource self . _resources = [ ]

Loads all the data files using the configured finders .


def resolve_loader(self, meta: ResourceDescription):
        """
        Attempts to assign a loader class to a resource description

        :param meta: The resource description instance
        """
        meta.loader_cls = self.get_loader(meta, raise_on_error=True)

def resolve_loader ( self , meta : ResourceDescription ) : meta . loader_cls = self . get_loader ( meta , raise_on_error = True )


def get_loader(self, meta: ResourceDescription, raise_on_error=False) -> BaseLoader:
        """
        Attempts to get a loader

        :param meta: The resource description instance
        :param raise_on_error: Raise ImproperlyConfigured if the loader cannot be resolved
        :returns: The requested loader class
        """
        for loader in self._loaders:
            if loader.name == meta.loader:
                return loader

        if raise_on_error:
            raise ImproperlyConfigured(
                "Resource has invalid loader '{}': {}\nAvailiable loaders: {}".format(
                    meta.loader, meta, [loader.name for loader in self._loaders]))


def resize(self, width, height):
        """
        Pyqt specific resize callback.
        """
        if not self.fbo:
            return

        # pyqt reports sizes in actual buffer size
        self.width = width // self.widget.devicePixelRatio()
        self.height = height // self.widget.devicePixelRatio()
        self.buffer_width = width
        self.buffer_height = height

        super().resize(width, height)


def draw(self, current_time, frame_time):
        """
        Draws a frame. Internally it calls the
        configured timeline's draw method.

        Args:
            current_time (float): The current time (preferrably always from the configured timer class)
            frame_time (float): The duration of the previous frame in seconds
        """
        self.set_default_viewport()
        self.timeline.draw(current_time, frame_time, self.fbo)

def draw ( self , current_time , frame_time ) : self . set_default_viewport ( ) self . timeline . draw ( current_time , frame_time , self . fbo )

Draws a frame . Internally it calls the configured timeline s draw method .


def clear(self):
        """
        Clear the window buffer
        """
        self.ctx.fbo.clear(
            red=self.clear_color[0],
            green=self.clear_color[1],
            blue=self.clear_color[2],
            alpha=self.clear_color[3],
            depth=self.clear_depth,
        )

def clear ( self ) : self . ctx . fbo . clear ( red = self . clear_color [ 0 ] , green = self . clear_color [ 1 ] , blue = self . clear_color [ 2 ] , alpha = self . clear_color [ 3 ] , depth = self . clear_depth , )


def clear_values(self, red=0.0, green=0.0, blue=0.0, alpha=0.0, depth=1.0):
        """
        Sets the clear values for the window buffer.

        Args:
            red (float): red compoent
            green (float): green compoent
            blue (float): blue compoent
            alpha (float): alpha compoent
            depth (float): depth value
        """
        self.clear_color = (red, green, blue, alpha)
        self.clear_depth = depth

def clear_values ( self , red = 0.0 , green = 0.0 , blue = 0.0 , alpha = 0.0 , depth = 1.0 ) : self . clear_color = ( red , green , blue , alpha ) self . clear_depth = depth

Sets the clear values for the window buffer .


def keyboard_event(self, key, action, modifier):
        """
        Handles the standard keyboard events such as camera movements,
        taking a screenshot, closing the window etc.

        Can be overriden add new keyboard events. Ensure this method
        is also called if you want to keep the standard features.

        Arguments:
            key: The key that was pressed or released
            action: The key action. Can be `ACTION_PRESS` or `ACTION_RELEASE`
            modifier: Modifiers such as holding shift or ctrl
        """
        # The well-known standard key for quick exit
        if key == self.keys.ESCAPE:
            self.close()
            return

        # Toggle pause time
        if key == self.keys.SPACE and action == self.keys.ACTION_PRESS:
            self.timer.toggle_pause()

        # Camera movement
        # Right
        if key == self.keys.D:
            if action == self.keys.ACTION_PRESS:
                self.sys_camera.move_right(True)
            elif action == self.keys.ACTION_RELEASE:
                self.sys_camera.move_right(False)
        # Left
        elif key == self.keys.A:
            if action == self.keys.ACTION_PRESS:
                self.sys_camera.move_left(True)
            elif action == self.keys.ACTION_RELEASE:
                self.sys_camera.move_left(False)
        # Forward
        elif key == self.keys.W:
            if action == self.keys.ACTION_PRESS:
                self.sys_camera.move_forward(True)
            if action == self.keys.ACTION_RELEASE:
                self.sys_camera.move_forward(False)
        # Backwards
        elif key == self.keys.S:
            if action == self.keys.ACTION_PRESS:
                self.sys_camera.move_backward(True)
            if action == self.keys.ACTION_RELEASE:
                self.sys_camera.move_backward(False)

        # UP
        elif key == self.keys.Q:
            if action == self.keys.ACTION_PRESS:
                self.sys_camera.move_down(True)
            if action == self.keys.ACTION_RELEASE:
                self.sys_camera.move_down(False)

        # Down
        elif key == self.keys.E:
            if action == self.keys.ACTION_PRESS:
                self.sys_camera.move_up(True)
            if action == self.keys.ACTION_RELEASE:
                self.sys_camera.move_up(False)

        # Screenshots
        if key == self.keys.X and action == self.keys.ACTION_PRESS:
            screenshot.create()

        if key == self.keys.R and action == self.keys.ACTION_PRESS:
            project.instance.reload_programs()

        if key == self.keys.RIGHT and action == self.keys.ACTION_PRESS:
            self.timer.set_time(self.timer.get_time() + 10.0)

        if key == self.keys.LEFT and action == self.keys.ACTION_PRESS:
            self.timer.set_time(self.timer.get_time() - 10.0)

        # Forward the event to the timeline
        self.timeline.key_event(key, action, modifier)

Handles the standard keyboard events such as camera movements taking a screenshot closing the window etc .


def cursor_event(self, x, y, dx, dy):
        """
        The standard mouse movement event method.
        Can be overriden to add new functionality.
        By default this feeds the system camera with new values.

        Args:
            x: The current mouse x position
            y: The current mouse y position
            dx: Delta x postion (x position difference from the previous event)
            dy: Delta y postion (y position difference from the previous event)
        """
        self.sys_camera.rot_state(x, y)

def cursor_event ( self , x , y , dx , dy ) : self . sys_camera . rot_state ( x , y )

The standard mouse movement event method . Can be overriden to add new functionality . By default this feeds the system camera with new values .


def set_default_viewport(self):
        """
        Calculates the viewport based on the configured aspect ratio in settings.
        Will add black borders if the window do not match the viewport.
        """
        # The expected height with the current viewport width
        expected_height = int(self.buffer_width / self.aspect_ratio)

        # How much positive or negative y padding
        blank_space = self.buffer_height - expected_height
        self.fbo.viewport = (0, blank_space // 2, self.buffer_width, expected_height)

Calculates the viewport based on the configured aspect ratio in settings . Will add black borders if the window do not match the viewport .


def start(self):
        """Start the timer"""
        self.music.start()
        if not self.start_paused:
            self.rocket.start()

def start ( self ) : self . music . start ( ) if not self . start_paused : self . rocket . start ( )


def toggle_pause(self):
        """Toggle pause mode"""
        self.controller.playing = not self.controller.playing
        self.music.toggle_pause()

def toggle_pause ( self ) : self . controller . playing = not self . controller . playing self . music . toggle_pause ( )


def supports_file(cls, meta):
        """Check if the loader has a supported file extension"""
        path = Path(meta.path)

        for ext in cls.file_extensions:
            if path.suffixes[:len(ext)] == ext:
                return True

        return False

def supports_file ( cls , meta ) : path = Path ( meta . path ) for ext in cls . file_extensions : if path . suffixes [ : len ( ext ) ] == ext : return True return False


def get(self, name) -> Track:
        """
        Get or create a Track object.

        :param name: Name of the track
        :return: Track object
        """
        name = name.lower()
        track = self.track_map.get(name)
        if not track:
            track = Track(name)
            self.tacks.append(track)
            self.track_map[name] = track
        return track

def get ( self , name ) -> Track : name = name . lower ( ) track = self . track_map . get ( name ) if not track : track = Track ( name ) self . tacks . append ( track ) self . track_map [ name ] = track return track


def find_commands(command_dir: str) -> List[str]:
    """
    Get all command names in the a folder

    :return: List of commands names
    """
    if not command_dir:
        return []

    return [name for _, name, is_pkg in pkgutil.iter_modules([command_dir])
            if not is_pkg and not name.startswith('_')]

def find_commands ( command_dir : str ) -> List [ str ] : if not command_dir : return [ ] return [ name for _ , name , is_pkg in pkgutil . iter_modules ( [ command_dir ] ) if not is_pkg and not name . startswith ( '_' ) ]


def update(self, **kwargs):
        """Override settings values"""
        for name, value in kwargs.items():
            setattr(self, name, value)

def update ( self , * * kwargs ) : for name , value in kwargs . items ( ) : setattr ( self , name , value )


def add_program_dir(self, directory):
        """Hack in program directory"""
        dirs = list(self.PROGRAM_DIRS)
        dirs.append(directory)
        self.PROGRAM_DIRS = dirs

def add_program_dir ( self , directory ) : dirs = list ( self . PROGRAM_DIRS ) dirs . append ( directory ) self . PROGRAM_DIRS = dirs


def add_texture_dir(self, directory):
        """Hack in texture directory"""
        dirs = list(self.TEXTURE_DIRS)
        dirs.append(directory)
        self.TEXTURE_DIRS = dirs

def add_texture_dir ( self , directory ) : dirs = list ( self . TEXTURE_DIRS ) dirs . append ( directory ) self . TEXTURE_DIRS = dirs


def add_data_dir(self, directory):
        """Hack in a data directory"""
        dirs = list(self.DATA_DIRS)
        dirs.append(directory)
        self.DATA_DIRS = dirs

def add_data_dir ( self , directory ) : dirs = list ( self . DATA_DIRS ) dirs . append ( directory ) self . DATA_DIRS = dirs


def render(self, program: moderngl.Program, mode=None, vertices=-1, first=0, instances=1):
        """
        Render the VAO.

        Args:
            program: The ``moderngl.Program``

        Keyword Args:
            mode: Override the draw mode (``TRIANGLES`` etc)
            vertices (int): The number of vertices to transform
            first (int): The index of the first vertex to start with
            instances (int): The number of instances
        """
        vao = self.instance(program)

        if mode is None:
            mode = self.mode

        vao.render(mode, vertices=vertices, first=first, instances=instances)

def render ( self , program : moderngl . Program , mode = None , vertices = - 1 , first = 0 , instances = 1 ) : vao = self . instance ( program ) if mode is None : mode = self . mode vao . render ( mode , vertices = vertices , first = first , instances = instances )


def transform(self, program: moderngl.Program, buffer: moderngl.Buffer,
                  mode=None, vertices=-1, first=0, instances=1):
        """
        Transform vertices. Stores the output in a single buffer.

        Args:
            program: The ``moderngl.Program``
            buffer: The ``moderngl.buffer`` to store the output

        Keyword Args:
            mode: Draw mode (for example ``moderngl.POINTS``)
            vertices (int): The number of vertices to transform
            first (int): The index of the first vertex to start with
            instances (int): The number of instances
        """
        vao = self.instance(program)

        if mode is None:
            mode = self.mode

        vao.transform(buffer, mode=mode, vertices=vertices, first=first, instances=instances)

def transform ( self , program : moderngl . Program , buffer : moderngl . Buffer , mode = None , vertices = - 1 , first = 0 , instances = 1 ) : vao = self . instance ( program ) if mode is None : mode = self . mode vao . transform ( buffer , mode = mode , vertices = vertices , first = first , instances = instances )

Transform vertices . Stores the output in a single buffer .


def index_buffer(self, buffer, index_element_size=4):
        """
        Set the index buffer for this VAO

        Args:
            buffer: ``moderngl.Buffer``, ``numpy.array`` or ``bytes``

        Keyword Args:
            index_element_size (int): Byte size of each element. 1, 2 or 4
        """
        if not type(buffer) in [moderngl.Buffer, numpy.ndarray, bytes]:
            raise VAOError("buffer parameter must be a moderngl.Buffer, numpy.ndarray or bytes instance")

        if isinstance(buffer, numpy.ndarray):
            buffer = self.ctx.buffer(buffer.tobytes())

        if isinstance(buffer, bytes):
            buffer = self.ctx.buffer(data=buffer)

        self._index_buffer = buffer
        self._index_element_size = index_element_size


def instance(self, program: moderngl.Program) -> moderngl.VertexArray:
        """
        Obtain the ``moderngl.VertexArray`` instance for the program.
        The instance is only created once and cached internally.

        Returns: ``moderngl.VertexArray`` instance
        """
        vao = self.vaos.get(program.glo)
        if vao:
            return vao

        program_attributes = [name for name, attr in program._members.items() if isinstance(attr, moderngl.Attribute)]

        # Make sure all attributes are covered
        for attrib_name in program_attributes:
            # Ignore built in attributes for now
            if attrib_name.startswith('gl_'):
                continue

            # Do we have a buffer mapping to this attribute?
            if not sum(buffer.has_attribute(attrib_name) for buffer in self.buffers):
                raise VAOError("VAO {} doesn't have attribute {} for program {}".format(
                    self.name, attrib_name, program.name))

        vao_content = []

        # Pick out the attributes we can actually map
        for buffer in self.buffers:
            content = buffer.content(program_attributes)
            if content:
                vao_content.append(content)

        # Any attribute left is not accounted for
        if program_attributes:
            for attrib_name in program_attributes:
                if attrib_name.startswith('gl_'):
                    continue

                raise VAOError("Did not find a buffer mapping for {}".format([n for n in program_attributes]))

        # Create the vao
        if self._index_buffer:
            vao = context.ctx().vertex_array(program, vao_content,
                                             self._index_buffer, self._index_element_size)
        else:
            vao = context.ctx().vertex_array(program, vao_content)

        self.vaos[program.glo] = vao
        return vao

Obtain the moderngl . VertexArray instance for the program . The instance is only created once and cached internally .


def release(self, buffer=True):
        """
        Destroy the vao object

        Keyword Args:
            buffers (bool): also release buffers
        """
        for key, vao in self.vaos:
            vao.release()

        if buffer:
            for buff in self.buffers:
                buff.buffer.release()

            if self._index_buffer:
                self._index_buffer.release()

def release ( self , buffer = True ) : for key , vao in self . vaos : vao . release ( ) if buffer : for buff in self . buffers : buff . buffer . release ( ) if self . _index_buffer : self . _index_buffer . release ( )


def draw(self, mesh, projection_matrix=None, view_matrix=None, camera_matrix=None, time=0):
        """
        Draw code for the mesh. Should be overriden.

        :param projection_matrix: projection_matrix (bytes)
        :param view_matrix: view_matrix (bytes)
        :param camera_matrix: camera_matrix (bytes)
        :param time: The current time
        """
        self.program["m_proj"].write(projection_matrix)
        self.program["m_mv"].write(view_matrix)
        mesh.vao.render(self.program)

def draw ( self , mesh , projection_matrix = None , view_matrix = None , camera_matrix = None , time = 0 ) : self . program [ "m_proj" ] . write ( projection_matrix ) self . program [ "m_mv" ] . write ( view_matrix ) mesh . vao . render ( self . program )

Draw code for the mesh . Should be overriden .


def parse_package_string(path):
    """
    Parse the effect package string.
    Can contain the package python path or path to effect class in an effect package.

    Examples::

        # Path to effect pacakge
        examples.cubes

        # Path to effect class
        examples.cubes.Cubes

    Args:
        path: python path to effect package. May also include effect class name.

    Returns:
        tuple: (package_path, effect_class)
    """
    parts = path.split('.')

    # Is the last entry in the path capitalized?
    if parts[-1][0].isupper():
        return ".".join(parts[:-1]), parts[-1]

    return path, ""

Parse the effect package string . Can contain the package python path or path to effect class in an effect package .


def get_dirs(self) -> List[str]:
        """
        Get all effect directories for registered effects.
        """
        for package in self.packages:
            yield os.path.join(package.path, 'resources')

def get_dirs ( self ) -> List [ str ] : for package in self . packages : yield os . path . join ( package . path , 'resources' )

Get all effect directories for registered effects .


def get_effect_resources(self) -> List[Any]:
        """
        Get all resources registed in effect packages.
        These are typically located in ``resources.py``
        """
        resources = []
        for package in self.packages:
            resources.extend(package.resources)

        return resources

def get_effect_resources ( self ) -> List [ Any ] : resources = [ ] for package in self . packages : resources . extend ( package . resources ) return resources

Get all resources registed in effect packages . These are typically located in resources . py


def add_package(self, name):
        """
        Registers a single package

        :param name: (str) The effect package to add
        """
        name, cls_name = parse_package_string(name)

        if name in self.package_map:
            return

        package = EffectPackage(name)
        package.load()

        self.packages.append(package)
        self.package_map[package.name] = package

        # Load effect package dependencies
        self.polulate(package.effect_packages)

def
add_package
(
self
,
name
)
:
name
,
cls_name
=
parse_package_string
(
name
)
if
name
in
self
.
package_map
:
return
package
=
EffectPackage
(
name
)
package
.
load
(
)
self
.
packages
.
append
(
package
)
self
.
package_map
[
package
.
name
]
=
package
# Load effect package dependencies
self
.
polulate
(
package
.
effect_packages
)


def get_package(self, name) -> 'EffectPackage':
        """
        Get a package by python path. Can also contain path to an effect.

        Args:
            name (str): Path to effect package or effect

        Returns:
            The requested EffectPackage

        Raises:
            EffectError when no package is found
        """
        name, cls_name = parse_package_string(name)

        try:
            return self.package_map[name]
        except KeyError:
            raise EffectError("No package '{}' registered".format(name))

def
get_package
(
self
,
name
)
->
'EffectPackage'
:
name
,
cls_name
=
parse_package_string
(
name
)
try
:
return
self
.
package_map
[
name
]
except
KeyError
:
raise
EffectError
(
"No package '{}' registered"
.
format
(
name
)
)

Get a package by python path . Can also contain path to an effect .


def find_effect_class(self, path) -> Type[Effect]:
        """
        Find an effect class by class name or full python path to class

        Args:
            path (str): effect class name or full python path to effect class

        Returns:
            Effect class

        Raises:
            EffectError if no class is found
        """
        package_name, class_name = parse_package_string(path)

        if package_name:
            package = self.get_package(package_name)
            return package.find_effect_class(class_name, raise_for_error=True)

        for package in self.packages:
            effect_cls = package.find_effect_class(class_name)
            if effect_cls:
                return effect_cls

        raise EffectError("No effect class '{}' found in any packages".format(class_name))


def runnable_effects(self) -> List[Type[Effect]]:
        """Returns the runnable effect in the package"""
        return [cls for cls in self.effect_classes if cls.runnable]

def runnable_effects ( self ) -> List [ Type [ Effect ] ] : return [ cls for cls in self . effect_classes if cls . runnable ]


def load_package(self):
        """FInd the effect package"""
        try:
            self.package = importlib.import_module(self.name)
        except ModuleNotFoundError:
            raise ModuleNotFoundError("Effect package '{}' not found.".format(self.name))

def
load_package
(
self
)
:
try
:
self
.
package
=
importlib
.
import_module
(
self
.
name
)
except
ModuleNotFoundError
:
raise
ModuleNotFoundError
(
"Effect package '{}' not found."
.
format
(
self
.
name
)
)


def load_effects_classes(self):
        """Iterate the module attributes picking out effects"""
        self.effect_classes = []

        for _, cls in inspect.getmembers(self.effect_module):
            if inspect.isclass(cls):
                if cls == Effect:
                    continue

                if issubclass(cls, Effect):
                    self.effect_classes.append(cls)
                    self.effect_class_map[cls.__name__] = cls
                    cls._name = "{}.{}".format(self.effect_module_name, cls.__name__)

def load_effects_classes ( self ) : self . effect_classes = [ ] for _ , cls in inspect . getmembers ( self . effect_module ) : if inspect . isclass ( cls ) : if cls == Effect : continue if issubclass ( cls , Effect ) : self . effect_classes . append ( cls ) self . effect_class_map [ cls . __name__ ] = cls cls . _name = "{}.{}" . format ( self . effect_module_name , cls . __name__ )


def load_resource_module(self):
        """Fetch the resource list"""
        # Attempt to load the dependencies module
        try:
            name = '{}.{}'.format(self.name, 'dependencies')
            self.dependencies_module = importlib.import_module(name)
        except ModuleNotFoundError as err:
            raise EffectError(
                (
                    "Effect package '{}' has no 'dependencies' module or the module has errors. "
                    "Forwarded error from importlib: {}"
                ).format(self.name, err))

        # Fetch the resource descriptions
        try:
            self.resources = getattr(self.dependencies_module, 'resources')
        except AttributeError:
            raise EffectError("Effect dependencies module '{}' has no 'resources' attribute".format(name))

        if not isinstance(self.resources, list):
            raise EffectError(
                "Effect dependencies module '{}': 'resources' is of type {} instead of a list".format(
                    name, type(self.resources)))

        # Fetch the effect class list
        try:
            self.effect_packages = getattr(self.dependencies_module, 'effect_packages')
        except AttributeError:
            raise EffectError("Effect dependencies module '{}' has 'effect_packages' attribute".format(name))

        if not isinstance(self.effect_packages, list):
            raise EffectError(
                "Effect dependencies module '{}': 'effect_packages' is of type {} instead of a list".format(
                    name, type(self.effects)))


def draw(self, time, frametime, target):
        """
        Fetch track value for every runnable effect.
        If the value is > 0.5 we draw it.
        """
        for effect in self.effects:
            value = effect.rocket_timeline_track.time_value(time)
            if value > 0.5:
                effect.draw(time, frametime, target)

def draw ( self , time , frametime , target ) : for effect in self . effects : value = effect . rocket_timeline_track . time_value ( time ) if value > 0.5 : effect . draw ( time , frametime , target )

Fetch track value for every runnable effect . If the value is > 0 . 5 we draw it .


def load(self):
        """Load a 2d texture"""
        self._open_image()

        components, data = image_data(self.image)

        texture = self.ctx.texture(
            self.image.size,
            components,
            data,
        )
        texture.extra = {'meta': self.meta}

        if self.meta.mipmap:
            texture.build_mipmaps()

        self._close_image()

        return texture

def load ( self ) : self . _open_image ( ) components , data = image_data ( self . image ) texture = self . ctx . texture ( self . image . size , components , data , ) texture . extra = { 'meta' : self . meta } if self . meta . mipmap : texture . build_mipmaps ( ) self . _close_image ( ) return texture


def from_single(cls, meta: ProgramDescription, source: str):
        """Initialize a single glsl string containing all shaders"""
        instance = cls(meta)
        instance.vertex_source = ShaderSource(
            VERTEX_SHADER,
            meta.path or meta.vertex_shader,
            source
        )

        if GEOMETRY_SHADER in source:
            instance.geometry_source = ShaderSource(
                GEOMETRY_SHADER,
                meta.path or meta.geometry_shader,
                source,
            )

        if FRAGMENT_SHADER in source:
            instance.fragment_source = ShaderSource(
                FRAGMENT_SHADER,
                meta.path or meta.fragment_shader,
                source,
            )

        if TESS_CONTROL_SHADER in source:
            instance.tess_control_source = ShaderSource(
                TESS_CONTROL_SHADER,
                meta.path or meta.tess_control_shader,
                source,
            )

        if TESS_EVALUATION_SHADER in source:
            instance.tess_evaluation_source = ShaderSource(
                TESS_EVALUATION_SHADER,
                meta.path or meta.tess_evaluation_shader,
                source,
            )

        return instance

def from_single ( cls , meta : ProgramDescription , source : str ) : instance = cls ( meta ) instance . vertex_source = ShaderSource ( VERTEX_SHADER , meta . path or meta . vertex_shader , source ) if GEOMETRY_SHADER in source : instance . geometry_source = ShaderSource ( GEOMETRY_SHADER , meta . path or meta . geometry_shader , source , ) if FRAGMENT_SHADER in source : instance . fragment_source = ShaderSource ( FRAGMENT_SHADER , meta . path or meta . fragment_shader , source , ) if TESS_CONTROL_SHADER in source : instance . tess_control_source = ShaderSource ( TESS_CONTROL_SHADER , meta . path or meta . tess_control_shader , source , ) if TESS_EVALUATION_SHADER in source : instance . tess_evaluation_source = ShaderSource ( TESS_EVALUATION_SHADER , meta . path or meta . tess_evaluation_shader , source , ) return instance


def from_separate(cls, meta: ProgramDescription, vertex_source, geometry_source=None, fragment_source=None,
                      tess_control_source=None, tess_evaluation_source=None):
        """Initialize multiple shader strings"""
        instance = cls(meta)
        instance.vertex_source = ShaderSource(
            VERTEX_SHADER,
            meta.path or meta.vertex_shader,
            vertex_source,
        )

        if geometry_source:
            instance.geometry_source = ShaderSource(
                GEOMETRY_SHADER,
                meta.path or meta.geometry_shader,
                geometry_source,
            )

        if fragment_source:
            instance.fragment_source = ShaderSource(
                FRAGMENT_SHADER,
                meta.path or meta.fragment_shader,
                fragment_source,
            )

        if tess_control_source:
            instance.tess_control_source = ShaderSource(
                TESS_CONTROL_SHADER,
                meta.path or meta.tess_control_shader,
                tess_control_source,
            )

        if tess_evaluation_source:
            instance.tess_evaluation_source = ShaderSource(
                TESS_EVALUATION_SHADER,
                meta.path or meta.tess_control_shader,
                tess_evaluation_source,
            )

        return instance

def from_separate ( cls , meta : ProgramDescription , vertex_source , geometry_source = None , fragment_source = None , tess_control_source = None , tess_evaluation_source = None ) : instance = cls ( meta ) instance . vertex_source = ShaderSource ( VERTEX_SHADER , meta . path or meta . vertex_shader , vertex_source , ) if geometry_source : instance . geometry_source = ShaderSource ( GEOMETRY_SHADER , meta . path or meta . geometry_shader , geometry_source , ) if fragment_source : instance . fragment_source = ShaderSource ( FRAGMENT_SHADER , meta . path or meta . fragment_shader , fragment_source , ) if tess_control_source : instance . tess_control_source = ShaderSource ( TESS_CONTROL_SHADER , meta . path or meta . tess_control_shader , tess_control_source , ) if tess_evaluation_source : instance . tess_evaluation_source = ShaderSource ( TESS_EVALUATION_SHADER , meta . path or meta . tess_control_shader , tess_evaluation_source , ) return instance


def print(self):
        """Print the shader lines"""
        print("---[ START {} ]---".format(self.name))

        for i, line in enumerate(self.lines):
            print("{}: {}".format(str(i).zfill(3), line))

        print("---[ END {} ]---".format(self.name))

def
print
(
self
)
:
print
(
"---[ START {} ]---"
.
format
(
self
.
name
)
)
for
i
,
line
in
enumerate
(
self
.
lines
)
:
print
(
"{}: {}"
.
format
(
str
(
i
)
.
zfill
(
3
)
,
line
)
)
print
(
"---[ END {} ]---"
.
format
(
self
.
name
)
)


def load(self):
        """
        Loads this project instance
        """
        self.create_effect_classes()

        self._add_resource_descriptions_to_pools(self.create_external_resources())
        self._add_resource_descriptions_to_pools(self.create_resources())

        for meta, resource in resources.textures.load_pool():
            self._textures[meta.label] = resource

        for meta, resource in resources.programs.load_pool():
            self._programs[meta.label] = resource

        for meta, resource in resources.scenes.load_pool():
            self._scenes[meta.label] = resource

        for meta, resource in resources.data.load_pool():
            self._data[meta.label] = resource

        self.create_effect_instances()
        self.post_load()

def load ( self ) : self . create_effect_classes ( ) self . _add_resource_descriptions_to_pools ( self . create_external_resources ( ) ) self . _add_resource_descriptions_to_pools ( self . create_resources ( ) ) for meta , resource in resources . textures . load_pool ( ) : self . _textures [ meta . label ] = resource for meta , resource in resources . programs . load_pool ( ) : self . _programs [ meta . label ] = resource for meta , resource in resources . scenes . load_pool ( ) : self . _scenes [ meta . label ] = resource for meta , resource in resources . data . load_pool ( ) : self . _data [ meta . label ] = resource self . create_effect_instances ( ) self . post_load ( )


def _add_resource_descriptions_to_pools(self, meta_list):
        """
        Takes a list of resource descriptions adding them
        to the resource pool they belong to scheduling them for loading.
        """
        if not meta_list:
            return

        for meta in meta_list:
            getattr(resources, meta.resource_type).add(meta)

def _add_resource_descriptions_to_pools ( self , meta_list ) : if not meta_list : return for meta in meta_list : getattr ( resources , meta . resource_type ) . add ( meta )

Takes a list of resource descriptions adding them to the resource pool they belong to scheduling them for loading .


def reload_programs(self):
        """
        Reload all shader programs with the reloadable flag set
        """
        print("Reloading programs:")
        for name, program in self._programs.items():
            if getattr(program, 'program', None):
                print(" - {}".format(program.meta.label))
                program.program = resources.programs.load(program.meta)

def
reload_programs
(
self
)
:
print
(
"Reloading programs:"
)
for
name
,
program
in
self
.
_programs
.
items
(
)
:
if
getattr
(
program
,
'program'
,
)
:
print
(
" - {}"
.
format
(
program
.
meta
.
label
)
)
program
.
program
=
resources
.
programs
.
load
(
program
.
meta
)


def image_data(image):
    """Get components and bytes for an image"""
    # NOTE: We might want to check the actual image.mode
    #       and convert to an acceptable format.
    #       At the moment we load the data as is.
    data = image.tobytes()
    components = len(data) // (image.size[0] * image.size[1])
    return components, data

#       and convert to an acceptable format.

#       At the moment we load the data as is.


def _find_last_of(self, path, finders):
        """Find the last occurance of the file in finders"""
        found_path = None
        for finder in finders:
            result = finder.find(path)
            if result:
                found_path = result

        return found_path

def _find_last_of ( self , path , finders ) : found_path = None for finder in finders : result = finder . find ( path ) if result : found_path = result return found_path


def initial_sanity_check(self):
        """Checks if we can create the project"""
        # Check for python module collision
        self.try_import(self.project_name)

        # Is the name a valid identifier?
        self.validate_name(self.project_name)

        # Make sure we don't mess with existing directories
        if os.path.exists(self.project_name):
            print("Directory {} already exist. Aborting.".format(self.project_name))
            return False

        if os.path.exists('manage.py'):
            print("A manage.py file already exist in the current directory. Aborting.")
            return False

        return True


def create_entrypoint(self):
        """Write manage.py in the current directory"""
        with open(os.path.join(self.template_dir, 'manage.py'), 'r') as fd:
            data = fd.read().format(project_name=self.project_name)

        with open('manage.py', 'w') as fd:
            fd.write(data)

        os.chmod('manage.py', 0o777)

def create_entrypoint ( self ) : with open ( os . path . join ( self . template_dir , 'manage.py' ) , 'r' ) as fd : data = fd . read ( ) . format ( project_name = self . project_name ) with open ( 'manage.py' , 'w' ) as fd : fd . write ( data ) os . chmod ( 'manage.py' , 0o777 )

Write manage . py in the current directory


def get_template_dir(self):
        """Returns the absolute path to template directory"""
        directory = os.path.dirname(os.path.abspath(__file__))
        directory = os.path.dirname(os.path.dirname(directory))
        directory = os.path.join(directory, 'project_template')
        return directory

def get_template_dir ( self ) : directory = os . path . dirname ( os . path . abspath ( __file__ ) ) directory = os . path . dirname ( os . path . dirname ( directory ) ) directory = os . path . join ( directory , 'project_template' ) return directory


def resolve_loader(self, meta: ProgramDescription):
        """
        Resolve program loader
        """
        if not meta.loader:
            meta.loader = 'single' if meta.path else 'separate'

        for loader_cls in self._loaders:
            if loader_cls.name == meta.loader:
                meta.loader_cls = loader_cls
                break
        else:
            raise ImproperlyConfigured(
                (
                    "Program {} has no loader class registered."
                    "Check PROGRAM_LOADERS or PROGRAM_DIRS"
                ).format(meta.path)
            )


def ac_encode(text, probs):
    """Encode a text using arithmetic coding with the provided probabilities.

    This is a wrapper for :py:meth:`Arithmetic.encode`.

    Parameters
    ----------
    text : str
        A string to encode
    probs : dict
        A probability statistics dictionary generated by
        :py:meth:`Arithmetic.train`

    Returns
    -------
    tuple
        The arithmetically coded text

    Example
    -------
    >>> pr = ac_train('the quick brown fox jumped over the lazy dog')
    >>> ac_encode('align', pr)
    (16720586181, 34)

    """
    coder = Arithmetic()
    coder.set_probs(probs)
    return coder.encode(text)

def ac_encode ( text , probs ) : coder = Arithmetic ( ) coder . set_probs ( probs ) return coder . encode ( text )

Encode a text using arithmetic coding with the provided probabilities .


def train(self, text):
        r"""Generate a probability dict from the provided text.

        Text to 0-order probability statistics as a dict

        Parameters
        ----------
        text : str
            The text data over which to calculate probability statistics. This
            must not contain the NUL (0x00) character because that is used to
            indicate the end of data.

        Example
        -------
        >>> ac = Arithmetic()
        >>> ac.train('the quick brown fox jumped over the lazy dog')
        >>> ac.get_probs()
        {' ': (Fraction(0, 1), Fraction(8, 45)),
         'o': (Fraction(8, 45), Fraction(4, 15)),
         'e': (Fraction(4, 15), Fraction(16, 45)),
         'u': (Fraction(16, 45), Fraction(2, 5)),
         't': (Fraction(2, 5), Fraction(4, 9)),
         'r': (Fraction(4, 9), Fraction(22, 45)),
         'h': (Fraction(22, 45), Fraction(8, 15)),
         'd': (Fraction(8, 15), Fraction(26, 45)),
         'z': (Fraction(26, 45), Fraction(3, 5)),
         'y': (Fraction(3, 5), Fraction(28, 45)),
         'x': (Fraction(28, 45), Fraction(29, 45)),
         'w': (Fraction(29, 45), Fraction(2, 3)),
         'v': (Fraction(2, 3), Fraction(31, 45)),
         'q': (Fraction(31, 45), Fraction(32, 45)),
         'p': (Fraction(32, 45), Fraction(11, 15)),
         'n': (Fraction(11, 15), Fraction(34, 45)),
         'm': (Fraction(34, 45), Fraction(7, 9)),
         'l': (Fraction(7, 9), Fraction(4, 5)),
         'k': (Fraction(4, 5), Fraction(37, 45)),
         'j': (Fraction(37, 45), Fraction(38, 45)),
         'i': (Fraction(38, 45), Fraction(13, 15)),
         'g': (Fraction(13, 15), Fraction(8, 9)),
         'f': (Fraction(8, 9), Fraction(41, 45)),
         'c': (Fraction(41, 45), Fraction(14, 15)),
         'b': (Fraction(14, 15), Fraction(43, 45)),
         'a': (Fraction(43, 45), Fraction(44, 45)),
         '\x00': (Fraction(44, 45), Fraction(1, 1))}

        """
        text = text_type(text)
        if '\x00' in text:
            text = text.replace('\x00', ' ')
        counts = Counter(text)
        counts['\x00'] = 1
        tot_letters = sum(counts.values())

        tot = 0
        self._probs = {}
        prev = Fraction(0)
        for char, count in sorted(
            counts.items(), key=lambda x: (x[1], x[0]), reverse=True
        ):
            follow = Fraction(tot + count, tot_letters)
            self._probs[char] = (prev, follow)
            prev = follow
            tot = tot + count

def
train
(
self
,
text
)
:
text
=
text_type
(
text
)
if
'\x00'
in
text
:
text
=
text
.
replace
(
'\x00'
,
' '
)
counts
=
Counter
(
text
)
counts
[
'\x00'
]
=
1
tot_letters
=
sum
(
counts
.
values
(
)
)
tot
=
0
self
.
_probs
=
{
}
prev
=
Fraction
(
0
)
for
char
,
count
in
sorted
(
counts
.
items
(
)
,
key
=
lambda
x
:
(
x
[
1
]
,
x
[
0
]
)
,
reverse
=
True
)
:
follow
=
Fraction
(
tot
+
count
,
tot_letters
)
self
.
_probs
[
char
]
=
(
prev
,
follow
)
prev
=
follow
tot
=
tot
+
count

r Generate a probability dict from the provided text .


def encode(self, text):
        """Encode a text using arithmetic coding.

        Text and the 0-order probability statistics -> longval, nbits

        The encoded number is Fraction(longval, 2**nbits)

        Parameters
        ----------
        text : str
            A string to encode

        Returns
        -------
        tuple
            The arithmetically coded text

        Example
        -------
        >>> ac = Arithmetic('the quick brown fox jumped over the lazy dog')
        >>> ac.encode('align')
        (16720586181, 34)

        """
        text = text_type(text)
        if '\x00' in text:
            text = text.replace('\x00', ' ')
        minval = Fraction(0)
        maxval = Fraction(1)

        for char in text + '\x00':
            prob_range = self._probs[char]
            delta = maxval - minval
            maxval = minval + prob_range[1] * delta
            minval = minval + prob_range[0] * delta

        # I tried without the /2 just to check.  Doesn't work.
        # Keep scaling up until the error range is >= 1.  That
        # gives me the minimum number of bits needed to resolve
        # down to the end-of-data character.
        delta = (maxval - minval) / 2
        nbits = long(0)
        while delta < 1:
            nbits += 1
            delta *= 2
        # The below condition shouldn't ever be false
        if nbits == 0:  # pragma: no cover
            return 0, 0
        # using -1 instead of /2
        avg = (maxval + minval) * 2 ** (nbits - 1)
        # Could return a rational instead ...
        # the division truncation is deliberate
        return avg.numerator // avg.denominator, nbits


def corpus_importer(self, corpus, n_val=1, bos='_START_', eos='_END_'):
        r"""Fill in self.ngcorpus from a Corpus argument.

        Parameters
        ----------
        corpus :Corpus
            The Corpus from which to initialize the n-gram corpus
        n_val : int
            Maximum n value for n-grams
        bos : str
            String to insert as an indicator of beginning of sentence
        eos : str
            String to insert as an indicator of end of sentence

        Raises
        ------
        TypeError
            Corpus argument of the Corpus class required.

        Example
        -------
        >>> tqbf = 'The quick brown fox jumped over the lazy dog.\n'
        >>> tqbf += 'And then it slept.\n And the dog ran off.'
        >>> ngcorp = NGramCorpus()
        >>> ngcorp.corpus_importer(Corpus(tqbf))

        """
        if not corpus or not isinstance(corpus, Corpus):
            raise TypeError('Corpus argument of the Corpus class required.')

        sentences = corpus.sents()

        for sent in sentences:
            ngs = Counter(sent)
            for key in ngs.keys():
                self._add_to_ngcorpus(self.ngcorpus, [key], ngs[key])

            if n_val > 1:
                if bos and bos != '':
                    sent = [bos] + sent
                if eos and eos != '':
                    sent += [eos]
                for i in range(2, n_val + 1):
                    for j in range(len(sent) - i + 1):
                        self._add_to_ngcorpus(
                            self.ngcorpus, sent[j : j + i], 1
                        )

r Fill in self . ngcorpus from a Corpus argument .


def get_count(self, ngram, corpus=None):
        r"""Get the count of an n-gram in the corpus.

        Parameters
        ----------
        ngram : str
            The n-gram to retrieve the count of from the n-gram corpus
        corpus : Corpus
            The corpus

        Returns
        -------
        int
            The n-gram count

        Examples
        --------
        >>> tqbf = 'The quick brown fox jumped over the lazy dog.\n'
        >>> tqbf += 'And then it slept.\n And the dog ran off.'
        >>> ngcorp = NGramCorpus(Corpus(tqbf))
        >>> NGramCorpus(Corpus(tqbf)).get_count('the')
        2
        >>> NGramCorpus(Corpus(tqbf)).get_count('fox')
        1

        """
        if not corpus:
            corpus = self.ngcorpus

        # if ngram is empty, we're at our leaf node and should return the
        # value in None
        if not ngram:
            return corpus[None]

        # support strings or lists/tuples by splitting strings
        if isinstance(ngram, (text_type, str)):
            ngram = text_type(ngram).split()

        # if ngram is not empty, check whether the next element is in the
        # corpus; if so, recurse--if not, return 0
        if ngram[0] in corpus:
            return self.get_count(ngram[1:], corpus[ngram[0]])
        return 0

r Get the count of an n - gram in the corpus .


def _add_to_ngcorpus(self, corpus, words, count):
        """Build up a corpus entry recursively.

        Parameters
        ----------
        corpus : Corpus
            The corpus
        words : [str]
            Words to add to the corpus
        count : int
            Count of words

        """
        if words[0] not in corpus:
            corpus[words[0]] = Counter()

        if len(words) == 1:
            corpus[words[0]][None] += count
        else:
            self._add_to_ngcorpus(corpus[words[0]], words[1:], count)

def _add_to_ngcorpus ( self , corpus , words , count ) : if words [ 0 ] not in corpus : corpus [ words [ 0 ] ] = Counter ( ) if len ( words ) == 1 : corpus [ words [ 0 ] ] [ None ] += count else : self . _add_to_ngcorpus ( corpus [ words [ 0 ] ] , words [ 1 : ] , count )


def gng_importer(self, corpus_file):
        """Fill in self.ngcorpus from a Google NGram corpus file.

        Parameters
        ----------
        corpus_file : file
            The Google NGram file from which to initialize the n-gram corpus

        """
        with c_open(corpus_file, 'r', encoding='utf-8') as gng:
            for line in gng:
                line = line.rstrip().split('\t')
                words = line[0].split()

                self._add_to_ngcorpus(self.ngcorpus, words, int(line[2]))

def gng_importer ( self , corpus_file ) : with c_open ( corpus_file , 'r' , encoding = 'utf-8' ) as gng : for line in gng : line = line . rstrip ( ) . split ( '\t' ) words = line [ 0 ] . split ( ) self . _add_to_ngcorpus ( self . ngcorpus , words , int ( line [ 2 ] ) )

Fill in self . ngcorpus from a Google NGram corpus file .


def tf(self, term):
        r"""Return term frequency.

        Parameters
        ----------
        term : str
            The term for which to calculate tf

        Returns
        -------
        float
            The term frequency (tf)

        Raises
        ------
        ValueError
            tf can only calculate the frequency of individual words

        Examples
        --------
        >>> tqbf = 'The quick brown fox jumped over the lazy dog.\n'
        >>> tqbf += 'And then it slept.\n And the dog ran off.'
        >>> ngcorp = NGramCorpus(Corpus(tqbf))
        >>> NGramCorpus(Corpus(tqbf)).tf('the')
        1.3010299956639813
        >>> NGramCorpus(Corpus(tqbf)).tf('fox')
        1.0

        """
        if ' ' in term:
            raise ValueError(
                'tf can only calculate the term frequency of individual words'
            )
        tcount = self.get_count(term)
        if tcount == 0:
            return 0.0
        return 1 + log10(tcount)


def encode(self, word, terminator='\0'):
        r"""Return the Burrows-Wheeler transformed form of a word.

        Parameters
        ----------
        word : str
            The word to transform using BWT
        terminator : str
            A character added to signal the end of the string

        Returns
        -------
        str
            Word encoded by BWT

        Raises
        ------
        ValueError
            Specified terminator absent from code.

        Examples
        --------
        >>> bwt = BWT()
        >>> bwt.encode('align')
        'n\x00ilag'
        >>> bwt.encode('banana')
        'annb\x00aa'
        >>> bwt.encode('banana', '@')
        'annb@aa'

        """
        if word:
            if terminator in word:
                raise ValueError(
                    'Specified terminator, {}, already in word.'.format(
                        terminator if terminator != '\0' else '\\0'
                    )
                )
            else:
                word += terminator
                wordlist = sorted(
                    word[i:] + word[:i] for i in range(len(word))
                )
                return ''.join([w[-1] for w in wordlist])
        else:
            return terminator

r Return the Burrows - Wheeler transformed form of a word .


def decode(self, code, terminator='\0'):
        r"""Return a word decoded from BWT form.

        Parameters
        ----------
        code : str
            The word to transform from BWT form
        terminator : str
            A character added to signal the end of the string

        Returns
        -------
        str
            Word decoded by BWT

        Raises
        ------
        ValueError
            Specified terminator absent from code.

        Examples
        --------
        >>> bwt = BWT()
        >>> bwt.decode('n\x00ilag')
        'align'
        >>> bwt.decode('annb\x00aa')
        'banana'
        >>> bwt.decode('annb@aa', '@')
        'banana'

        """
        if code:
            if terminator not in code:
                raise ValueError(
                    'Specified terminator, {}, absent from code.'.format(
                        terminator if terminator != '\0' else '\\0'
                    )
                )
            else:
                wordlist = [''] * len(code)
                for i in range(len(code)):
                    wordlist = sorted(
                        code[i] + wordlist[i] for i in range(len(code))
                    )
                rows = [w for w in wordlist if w[-1] == terminator][0]
                return rows.rstrip(terminator)
        else:
            return ''


def dist_abs(self, src, tar):
        """Return the indel distance between two strings.

        Parameters
        ----------
        src : str
            Source string for comparison
        tar : str
            Target string for comparison

        Returns
        -------
        int
            Indel distance

        Examples
        --------
        >>> cmp = Indel()
        >>> cmp.dist_abs('cat', 'hat')
        2
        >>> cmp.dist_abs('Niall', 'Neil')
        3
        >>> cmp.dist_abs('Colin', 'Cuilen')
        5
        >>> cmp.dist_abs('ATCG', 'TAGC')
        4

        """
        return self._lev.dist_abs(
            src, tar, mode='lev', cost=(1, 1, 9999, 9999)
        )

def dist_abs ( self , src , tar ) : return self . _lev . dist_abs ( src , tar , mode = 'lev' , cost = ( 1 , 1 , 9999 , 9999 ) )

Return the indel distance between two strings .


def dist(self, src, tar):
        """Return the normalized indel distance between two strings.

        This is equivalent to normalized Levenshtein distance, when only
        inserts and deletes are possible.

        Parameters
        ----------
        src : str
            Source string for comparison
        tar : str
            Target string for comparison

        Returns
        -------
        float
            Normalized indel distance

        Examples
        --------
        >>> cmp = Indel()
        >>> round(cmp.dist('cat', 'hat'), 12)
        0.333333333333
        >>> round(cmp.dist('Niall', 'Neil'), 12)
        0.333333333333
        >>> round(cmp.dist('Colin', 'Cuilen'), 12)
        0.454545454545
        >>> cmp.dist('ATCG', 'TAGC')
        0.5

        """
        if src == tar:
            return 0.0
        return self.dist_abs(src, tar) / (len(src) + len(tar))

def dist ( self , src , tar ) : if src == tar : return 0.0 return self . dist_abs ( src , tar ) / ( len ( src ) + len ( tar ) )

Return the normalized indel distance between two strings .


def sim(self, src, tar, *args, **kwargs):
        """Return similarity.

        Parameters
        ----------
        src : str
            Source string for comparison
        tar : str
            Target string for comparison
        *args
            Variable length argument list.
        **kwargs
            Arbitrary keyword arguments.

        Returns
        -------
        float
            Similarity

        """
        return 1.0 - self.dist(src, tar, *args, **kwargs)

def sim ( self , src , tar , * args , * * kwargs ) : return 1.0 - self . dist ( src , tar , * args , * * kwargs )


def dist_abs(self, src, tar, *args, **kwargs):
        """Return absolute distance.

        Parameters
        ----------
        src : str
            Source string for comparison
        tar : str
            Target string for comparison
        *args
            Variable length argument list.
        **kwargs
            Arbitrary keyword arguments.

        Returns
        -------
        int
            Absolute distance

        """
        return self.dist(src, tar, *args, **kwargs)

def dist_abs ( self , src , tar , * args , * * kwargs ) : return self . dist ( src , tar , * args , * * kwargs )


def dist_baystat(src, tar, min_ss_len=None, left_ext=None, right_ext=None):
    """Return the Baystat distance.

    This is a wrapper for :py:meth:`Baystat.dist`.

    Parameters
    ----------
    src : str
        Source string for comparison
    tar : str
        Target string for comparison
    min_ss_len : int
        Minimum substring length to be considered
    left_ext : int
        Left-side extension length
    right_ext : int
        Right-side extension length

    Returns
    -------
    float
        The Baystat distance

    Examples
    --------
    >>> round(dist_baystat('cat', 'hat'), 12)
    0.333333333333
    >>> dist_baystat('Niall', 'Neil')
    0.6
    >>> round(dist_baystat('Colin', 'Cuilen'), 12)
    0.833333333333
    >>> dist_baystat('ATCG', 'TAGC')
    1.0

    """
    return Baystat().dist(src, tar, min_ss_len, left_ext, right_ext)

def dist_baystat ( src , tar , min_ss_len = None , left_ext = None , right_ext = None ) : return Baystat ( ) . dist ( src , tar , min_ss_len , left_ext , right_ext )


def dist_tversky(src, tar, qval=2, alpha=1, beta=1, bias=None):
    """Return the Tversky distance between two strings.

    This is a wrapper for :py:meth:`Tversky.dist`.

    Parameters
    ----------
    src : str
        Source string (or QGrams/Counter objects) for comparison
    tar : str
        Target string (or QGrams/Counter objects) for comparison
    qval : int
        The length of each q-gram; 0 for non-q-gram version
    alpha : float
        Tversky index parameter as described above
    beta : float
        Tversky index parameter as described above
    bias : float
        The symmetric Tversky index bias parameter

    Returns
    -------
    float
        Tversky distance

    Examples
    --------
    >>> dist_tversky('cat', 'hat')
    0.6666666666666667
    >>> dist_tversky('Niall', 'Neil')
    0.7777777777777778
    >>> dist_tversky('aluminum', 'Catalan')
    0.9375
    >>> dist_tversky('ATCG', 'TAGC')
    1.0

    """
    return Tversky().dist(src, tar, qval, alpha, beta, bias)

def dist_tversky ( src , tar , qval = 2 , alpha = 1 , beta = 1 , bias = None ) : return Tversky ( ) . dist ( src , tar , qval , alpha , beta , bias )

Return the Tversky distance between two strings .


def lcsseq(self, src, tar):
        """Return the longest common subsequence of two strings.

        Based on the dynamic programming algorithm from
        http://rosettacode.org/wiki/Longest_common_subsequence
        :cite:`rosettacode:2018b`. This is licensed GFDL 1.2.

        Modifications include:
            conversion to a numpy array in place of a list of lists

        Parameters
        ----------
        src : str
            Source string for comparison
        tar : str
            Target string for comparison

        Returns
        -------
        str
            The longest common subsequence

        Examples
        --------
        >>> sseq = LCSseq()
        >>> sseq.lcsseq('cat', 'hat')
        'at'
        >>> sseq.lcsseq('Niall', 'Neil')
        'Nil'
        >>> sseq.lcsseq('aluminum', 'Catalan')
        'aln'
        >>> sseq.lcsseq('ATCG', 'TAGC')
        'AC'

        """
        lengths = np_zeros((len(src) + 1, len(tar) + 1), dtype=np_int)

        # row 0 and column 0 are initialized to 0 already
        for i, src_char in enumerate(src):
            for j, tar_char in enumerate(tar):
                if src_char == tar_char:
                    lengths[i + 1, j + 1] = lengths[i, j] + 1
                else:
                    lengths[i + 1, j + 1] = max(
                        lengths[i + 1, j], lengths[i, j + 1]
                    )

        # read the substring out from the matrix
        result = ''
        i, j = len(src), len(tar)
        while i != 0 and j != 0:
            if lengths[i, j] == lengths[i - 1, j]:
                i -= 1
            elif lengths[i, j] == lengths[i, j - 1]:
                j -= 1
            else:
                result = src[i - 1] + result
                i -= 1
                j -= 1
        return result

Return the longest common subsequence of two strings .


def sim(self, src, tar):
        r"""Return the longest common subsequence similarity of two strings.

        Longest common subsequence similarity (:math:`sim_{LCSseq}`).

        This employs the LCSseq function to derive a similarity metric:
        :math:`sim_{LCSseq}(s,t) = \frac{|LCSseq(s,t)|}{max(|s|, |t|)}`

        Parameters
        ----------
        src : str
            Source string for comparison
        tar : str
            Target string for comparison

        Returns
        -------
        float
            LCSseq similarity

        Examples
        --------
        >>> sseq = LCSseq()
        >>> sseq.sim('cat', 'hat')
        0.6666666666666666
        >>> sseq.sim('Niall', 'Neil')
        0.6
        >>> sseq.sim('aluminum', 'Catalan')
        0.375
        >>> sseq.sim('ATCG', 'TAGC')
        0.5

        """
        if src == tar:
            return 1.0
        elif not src or not tar:
            return 0.0
        return len(self.lcsseq(src, tar)) / max(len(src), len(tar))

def sim ( self , src , tar ) : if src == tar : return 1.0 elif not src or not tar : return 0.0 return len ( self . lcsseq ( src , tar ) ) / max ( len ( src ) , len ( tar ) )

r Return the longest common subsequence similarity of two strings .


def sim(self, src, tar):
        """Return the prefix similarity of two strings.

        Prefix similarity is the ratio of the length of the shorter term that
        exactly matches the longer term to the length of the shorter term,
        beginning at the start of both terms.

        Parameters
        ----------
        src : str
            Source string for comparison
        tar : str
            Target string for comparison

        Returns
        -------
        float
            Prefix similarity

        Examples
        --------
        >>> cmp = Prefix()
        >>> cmp.sim('cat', 'hat')
        0.0
        >>> cmp.sim('Niall', 'Neil')
        0.25
        >>> cmp.sim('aluminum', 'Catalan')
        0.0
        >>> cmp.sim('ATCG', 'TAGC')
        0.0

        """
        if src == tar:
            return 1.0
        if not src or not tar:
            return 0.0
        min_word, max_word = (src, tar) if len(src) < len(tar) else (tar, src)
        min_len = len(min_word)
        for i in range(min_len, 0, -1):
            if min_word[:i] == max_word[:i]:
                return i / min_len
        return 0.0

def sim ( self , src , tar ) : if src == tar : return 1.0 if not src or not tar : return 0.0 min_word , max_word = ( src , tar ) if len ( src ) < len ( tar ) else ( tar , src ) min_len = len ( min_word ) for i in range ( min_len , 0 , - 1 ) : if min_word [ : i ] == max_word [ : i ] : return i / min_len return 0.0

Return the prefix similarity of two strings .


def docs_of_words(self):
        r"""Return the docs in the corpus, with sentences flattened.

        Each list within the corpus represents all the words of that document.
        Thus the sentence level of lists has been flattened.

        Returns
        -------
        [[str]]
            The docs in the corpus as a list of list of strs

        Example
        -------
        >>> tqbf = 'The quick brown fox jumped over the lazy dog.\n'
        >>> tqbf += 'And then it slept.\n And the dog ran off.'
        >>> corp = Corpus(tqbf)
        >>> corp.docs_of_words()
        [['The', 'quick', 'brown', 'fox', 'jumped', 'over', 'the', 'lazy',
        'dog.', 'And', 'then', 'it', 'slept.', 'And', 'the', 'dog', 'ran',
        'off.']]
        >>> len(corp.docs_of_words())
        1

        """
        return [
            [words for sents in doc for words in sents] for doc in self.corpus
        ]

def docs_of_words ( self ) : return [ [ words for sents in doc for words in sents ] for doc in self . corpus ]

r Return the docs in the corpus with sentences flattened .


def raw(self):
        r"""Return the raw corpus.

        This is reconstructed by joining sub-components with the corpus' split
        characters

        Returns
        -------
        str
            The raw corpus

        Example
        -------
        >>> tqbf = 'The quick brown fox jumped over the lazy dog.\n'
        >>> tqbf += 'And then it slept.\n And the dog ran off.'
        >>> corp = Corpus(tqbf)
        >>> print(corp.raw())
        The quick brown fox jumped over the lazy dog.
        And then it slept.
        And the dog ran off.
        >>> len(corp.raw())
        85

        """
        doc_list = []
        for doc in self.corpus:
            sent_list = []
            for sent in doc:
                sent_list.append(' '.join(sent))
            doc_list.append(self.sent_split.join(sent_list))
            del sent_list
        return self.doc_split.join(doc_list)

def
raw
(
self
)
:
doc_list
=
[
]
for
doc
in
self
.
corpus
:
sent_list
=
[
]
for
sent
in
doc
:
sent_list
.
append
(
' '
.
join
(
sent
)
)
doc_list
.
append
(
self
.
sent_split
.
join
(
sent_list
)
)
del
sent_list
return
self
.
doc_split
.
join
(
doc_list
)


def idf(self, term, transform=None):
        r"""Calculate the Inverse Document Frequency of a term in the corpus.

        Parameters
        ----------
        term : str
            The term to calculate the IDF of
        transform : function
            A function to apply to each document term before checking for the
            presence of term

        Returns
        -------
        float
            The IDF

        Examples
        --------
        >>> tqbf = 'The quick brown fox jumped over the lazy dog.\n\n'
        >>> tqbf += 'And then it slept.\n\n And the dog ran off.'
        >>> corp = Corpus(tqbf)
        >>> print(corp.docs())
        [[['The', 'quick', 'brown', 'fox', 'jumped', 'over', 'the', 'lazy',
        'dog.']],
        [['And', 'then', 'it', 'slept.']],
        [['And', 'the', 'dog', 'ran', 'off.']]]
        >>> round(corp.idf('dog'), 10)
        0.4771212547
        >>> round(corp.idf('the'), 10)
        0.1760912591

        """
        docs_with_term = 0
        docs = self.docs_of_words()
        for doc in docs:
            doc_set = set(doc)
            if transform:
                transformed_doc = []
                for word in doc_set:
                    transformed_doc.append(transform(word))
                doc_set = set(transformed_doc)

            if term in doc_set:
                docs_with_term += 1

        if docs_with_term == 0:
            return float('inf')

        return log10(len(docs) / docs_with_term)

def idf ( self , term , transform = None ) : docs_with_term = 0 docs = self . docs_of_words ( ) for doc in docs : doc_set = set ( doc ) if transform : transformed_doc = [ ] for word in doc_set : transformed_doc . append ( transform ( word ) ) doc_set = set ( transformed_doc ) if term in doc_set : docs_with_term += 1 if docs_with_term == 0 : return float ( 'inf' ) return log10 ( len ( docs ) / docs_with_term )

r Calculate the Inverse Document Frequency of a term in the corpus .


def stem(self, word):
        """Return Paice-Husk stem.

        Parameters
        ----------
        word : str
            The word to stem

        Returns
        -------
        str
            Word stem

        Examples
        --------
        >>> stmr = PaiceHusk()
        >>> stmr.stem('assumption')
        'assum'
        >>> stmr.stem('verifiable')
        'ver'
        >>> stmr.stem('fancies')
        'fant'
        >>> stmr.stem('fanciful')
        'fancy'
        >>> stmr.stem('torment')
        'tor'

        """
        terminate = False
        intact = True
        while not terminate:
            for n in range(6, 0, -1):
                if word[-n:] in self._rule_table[n]:
                    accept = False
                    if len(self._rule_table[n][word[-n:]]) < 4:
                        for rule in self._rule_table[n][word[-n:]]:
                            (
                                word,
                                accept,
                                intact,
                                terminate,
                            ) = self._apply_rule(word, rule, intact, terminate)
                            if accept:
                                break
                    else:
                        rule = self._rule_table[n][word[-n:]]
                        (word, accept, intact, terminate) = self._apply_rule(
                            word, rule, intact, terminate
                        )

                    if accept:
                        break
            else:
                break

        return word

def stem ( self , word ) : terminate = False intact = True while not terminate : for n in range ( 6 , 0 , - 1 ) : if word [ - n : ] in self . _rule_table [ n ] : accept = False if len ( self . _rule_table [ n ] [ word [ - n : ] ] ) < 4 : for rule in self . _rule_table [ n ] [ word [ - n : ] ] : ( word , accept , intact , terminate , ) = self . _apply_rule ( word , rule , intact , terminate ) if accept : break else : rule = self . _rule_table [ n ] [ word [ - n : ] ] ( word , accept , intact , terminate ) = self . _apply_rule ( word , rule , intact , terminate ) if accept : break else : break return word


def _language(self, name, name_mode):
        """Return the best guess language ID for the word and language choices.

        Parameters
        ----------
        name : str
            The term to guess the language of
        name_mode : str
            The name mode of the algorithm: ``gen`` (default),
            ``ash`` (Ashkenazi), or ``sep`` (Sephardic)

        Returns
        -------
        int
            Language ID

        """
        name = name.strip().lower()
        rules = BMDATA[name_mode]['language_rules']
        all_langs = (
            sum(_LANG_DICT[_] for _ in BMDATA[name_mode]['languages']) - 1
        )
        choices_remaining = all_langs
        for rule in rules:
            letters, languages, accept = rule
            if search(letters, name) is not None:
                if accept:
                    choices_remaining &= languages
                else:
                    choices_remaining &= (~languages) % (all_langs + 1)
        if choices_remaining == L_NONE:
            choices_remaining = L_ANY
        return choices_remaining

def _language ( self , name , name_mode ) : name = name . strip ( ) . lower ( ) rules = BMDATA [ name_mode ] [ 'language_rules' ] all_langs = ( sum ( _LANG_DICT [ _ ] for _ in BMDATA [ name_mode ] [ 'languages' ] ) - 1 ) choices_remaining = all_langs for rule in rules : letters , languages , accept = rule if search ( letters , name ) is not None : if accept : choices_remaining &= languages else : choices_remaining &= ( ~ languages ) % ( all_langs + 1 ) if choices_remaining == L_NONE : choices_remaining = L_ANY return choices_remaining

Return the best guess language ID for the word and language choices .


def _redo_language(
        self, term, name_mode, rules, final_rules1, final_rules2, concat
    ):
        """Reassess the language of the terms and call the phonetic encoder.

        Uses a split multi-word term.

        Parameters
        ----------
        term : str
            The term to encode via Beider-Morse
        name_mode : str
            The name mode of the algorithm: ``gen`` (default),
            ``ash`` (Ashkenazi), or ``sep`` (Sephardic)
        rules : tuple
            The set of initial phonetic transform regexps
        final_rules1 : tuple
            The common set of final phonetic transform regexps
        final_rules2 : tuple
            The specific set of final phonetic transform regexps
        concat : bool
            A flag to indicate concatenation

        Returns
        -------
        str
            A Beider-Morse phonetic code

        """
        language_arg = self._language(term, name_mode)
        return self._phonetic(
            term,
            name_mode,
            rules,
            final_rules1,
            final_rules2,
            language_arg,
            concat,
        )

def _redo_language ( self , term , name_mode , rules , final_rules1 , final_rules2 , concat ) : language_arg = self . _language ( term , name_mode ) return self . _phonetic ( term , name_mode , rules , final_rules1 , final_rules2 , language_arg , concat , )

Reassess the language of the terms and call the phonetic encoder .


def _apply_final_rules(self, phonetic, final_rules, language_arg, strip):
        """Apply a set of final rules to the phonetic encoding.

        Parameters
        ----------
        phonetic : str
            The term to which to apply the final rules
        final_rules : tuple
            The set of final phonetic transform regexps
        language_arg : int
            An integer representing the target language of the phonetic
            encoding
        strip : bool
            Flag to indicate whether to normalize the language attributes

        Returns
        -------
        str
            A Beider-Morse phonetic code

        """
        # optimization to save time
        if not final_rules:
            return phonetic

        # expand the result
        phonetic = self._expand_alternates(phonetic)
        phonetic_array = phonetic.split('|')

        for k in range(len(phonetic_array)):
            phonetic = phonetic_array[k]
            phonetic2 = ''
            phoneticx = self._normalize_lang_attrs(phonetic, True)

            i = 0
            while i < len(phonetic):
                found = False

                if phonetic[i] == '[':  # skip over language attribute
                    attrib_start = i
                    i += 1
                    while True:
                        if phonetic[i] == ']':
                            i += 1
                            phonetic2 += phonetic[attrib_start:i]
                            break
                        i += 1
                    continue

                for rule in final_rules:
                    pattern = rule[_PATTERN_POS]
                    pattern_length = len(pattern)
                    lcontext = rule[_LCONTEXT_POS]
                    rcontext = rule[_RCONTEXT_POS]

                    right = '^' + rcontext
                    left = lcontext + '$'

                    # check to see if next sequence in phonetic matches the
                    # string in the rule
                    if (pattern_length > len(phoneticx) - i) or phoneticx[
                        i : i + pattern_length
                    ] != pattern:
                        continue

                    # check that right context is satisfied
                    if rcontext != '':
                        if not search(right, phoneticx[i + pattern_length :]):
                            continue

                    # check that left context is satisfied
                    if lcontext != '':
                        if not search(left, phoneticx[:i]):
                            continue

                    # check for incompatible attributes
                    candidate = self._apply_rule_if_compat(
                        phonetic2, rule[_PHONETIC_POS], language_arg
                    )
                    # The below condition shouldn't ever be false
                    if candidate is not None:  # pragma: no branch
                        phonetic2 = candidate
                        found = True
                        break

                if not found:
                    # character in name for which there is no substitution in
                    # the table
                    phonetic2 += phonetic[i]
                    pattern_length = 1

                i += pattern_length

            phonetic_array[k] = self._expand_alternates(phonetic2)

        phonetic = '|'.join(phonetic_array)
        if strip:
            phonetic = self._normalize_lang_attrs(phonetic, True)

        if '|' in phonetic:
            phonetic = '(' + self._remove_dupes(phonetic) + ')'

        return phonetic

Apply a set of final rules to the phonetic encoding .


def _expand_alternates(self, phonetic):
        """Expand phonetic alternates separated by |s.

        Parameters
        ----------
        phonetic : str
            A Beider-Morse phonetic encoding

        Returns
        -------
        str
            A Beider-Morse phonetic code

        """
        alt_start = phonetic.find('(')
        if alt_start == -1:
            return self._normalize_lang_attrs(phonetic, False)

        prefix = phonetic[:alt_start]
        alt_start += 1  # get past the (
        alt_end = phonetic.find(')', alt_start)
        alt_string = phonetic[alt_start:alt_end]
        alt_end += 1  # get past the )
        suffix = phonetic[alt_end:]
        alt_array = alt_string.split('|')
        result = ''

        for i in range(len(alt_array)):
            alt = alt_array[i]
            alternate = self._expand_alternates(prefix + alt + suffix)
            if alternate != '' and alternate != '[0]':
                if result != '':
                    result += '|'
                result += alternate

        return result

def
_expand_alternates
(
self
,
phonetic
)
:
alt_start
=
phonetic
.
find
(
'('
)
if
alt_start
==
1
:
return
self
.
_normalize_lang_attrs
(
phonetic
,
False
)
prefix
=
phonetic
[
:
alt_start
]
alt_start
+=
1
# get past the (
alt_end
=
phonetic
.
find
(
')'
,
alt_start
)
alt_string
=
phonetic
[
alt_start
:
alt_end
]
alt_end
+=
1
# get past the )
suffix
=
phonetic
[
alt_end
:
]
alt_array
=
alt_string
.
split
(
'|'
)
result
=
''
for
i
in
range
(
len
(
alt_array
)
)
:
alt
=
alt_array
[
i
]
alternate
=
self
.
_expand_alternates
(
prefix
+
alt
+
suffix
)
if
alternate
!=
''
and
alternate
!=
'[0]'
:
if
result
!=
''
:
result
+=
'|'
result
+=
alternate
return
result

Expand phonetic alternates separated by |s .


def _pnums_with_leading_space(self, phonetic):
        """Join prefixes & suffixes in cases of alternate phonetic values.

        Parameters
        ----------
        phonetic : str
            A Beider-Morse phonetic encoding

        Returns
        -------
        str
            A Beider-Morse phonetic code

        """
        alt_start = phonetic.find('(')
        if alt_start == -1:
            return ' ' + self._phonetic_number(phonetic)

        prefix = phonetic[:alt_start]
        alt_start += 1  # get past the (
        alt_end = phonetic.find(')', alt_start)
        alt_string = phonetic[alt_start:alt_end]
        alt_end += 1  # get past the )
        suffix = phonetic[alt_end:]
        alt_array = alt_string.split('|')
        result = ''
        for alt in alt_array:
            result += self._pnums_with_leading_space(prefix + alt + suffix)

        return result

def
_pnums_with_leading_space
(
self
,
phonetic
)
:
alt_start
=
phonetic
.
find
(
'('
)
if
alt_start
==
1
:
return
' '
+
self
.
_phonetic_number
(
phonetic
)
prefix
=
phonetic
[
:
alt_start
]
alt_start
+=
1
# get past the (
alt_end
=
phonetic
.
find
(
')'
,
alt_start
)
alt_string
=
phonetic
[
alt_start
:
alt_end
]
alt_end
+=
1
# get past the )
suffix
=
phonetic
[
alt_end
:
]
alt_array
=
alt_string
.
split
(
'|'
)
result
=
''
for
alt
in
alt_array
:
result
+=
self
.
_pnums_with_leading_space
(
prefix
+
alt
+
suffix
)
return
result

Join prefixes & suffixes in cases of alternate phonetic values .


def _phonetic_numbers(self, phonetic):
        """Prepare & join phonetic numbers.

        Split phonetic value on '-', run through _pnums_with_leading_space,
        and join with ' '

        Parameters
        ----------
        phonetic : str
            A Beider-Morse phonetic encoding

        Returns
        -------
        str
            A Beider-Morse phonetic code

        """
        phonetic_array = phonetic.split('-')  # for names with spaces in them
        result = ' '.join(
            [self._pnums_with_leading_space(i)[1:] for i in phonetic_array]
        )
        return result

def
_phonetic_numbers
(
self
,
phonetic
)
:
phonetic_array
=
phonetic
.
split
(
'-'
)
# for names with spaces in them
result
=
' '
.
join
(
[
self
.
_pnums_with_leading_space
(
i
)
[
1
:
]
for
i
in
phonetic_array
]
)
return
result