Exemple #1
0
class FragmentGraph(DomainGraph):

    DEFAULT_FRAGMENT_KERNEL = """
        vec4 fragment_kernel(vec2 txcoord) {
            return tovec4($D.domain(txcoord));
        }
    """

    FRAGMENT_KERNELS = {
        'expr':
        lambda e: "vec4 fragment_kernel(vec2 txcoord) { \n\treturn " + e +
        "; \n}",
    }
    cs = attributes.VectorAttribute(4)

    # main domain name
    fragment_kernel = attributes.CastedAttribute(str)

    def __init__(self, domain=None, cs=None, fragment_kernel=None):

        super().__init__(domain)

        self.cs = cs

        if 'domain' in self.domains:
            self.fragment_kernel = fragment_kernel or self.DEFAULT_FRAGMENT_KERNEL
        elif fragment_kernel is None:
            raise ValueError(
                'argument fragment_kernel cannot be None without default domain'
            )
        else:
            self.fragment_kernel = fragment_kernel

        self.mesh = None
        self.program = None
        self._fkernel_template = None

    def init(self):
        self._build_kernel()
        self.program = self._build_shader()
        self.mesh = StridedVertexMesh(
            mesh3d_rectangle(),
            GL_TRIANGLES,
            attribute_locations=self.program.attributes)
        self.on_tick.once(self.sync_gpu)

    @cs.on_change
    def _properties_changed(self, *e):
        self.on_tick.once(self.sync_gpu)

    def _build_kernel(self):
        context = self.get_domain_glsl_substitutions()
        kernel = Template(self.fragment_kernel, context)
        kernel.context.update({'size': 'gl_PointSize', 'color': 'v_col'})
        self._fkernel_template = kernel

    def _build_shader(self):
        prg = DomainProgram(vrt_file='fragmentgraph.vrt.glsl',
                            frg_file='fragmentgraph.frg.glsl')

        prg.declare_uniform('camera',
                            components.camera.Camera2D.DTYPE,
                            variable='camera')
        prg.declare_uniform('plot',
                            plotter2d.Plotter2d.UBO_DTYPE,
                            variable='plot')

        prg.prepare_domains(self.domains)
        prg.get_shader(GL_FRAGMENT_SHADER).substitutions.update({
            'fragment_kernel':
            self._fkernel_template.render(),
        })
        prg.link()
        prg.uniform_block_binding(
            'plot', G_.CONTEXT.buffer_base('gpupy.plot.plotter2d'))
        prg.uniform_block_binding('camera',
                                  G_.CONTEXT.buffer_base('gpupy.gl.camera'))

        self._src = prg.get_shader(GL_FRAGMENT_SHADER)._precompiled_source
        return prg

    def sync_gpu(self):
        cs = self.cs.values
        self.program.uniform('cs', cs)
        self.program.uniform('cs_size',
                             (np.abs(cs[1] - cs[0]), np.abs(cs[3] - cs[2])))

    def render(self):
        # enable all domains
        domain.enable_domains(self.program, self.domains.items())

        # blending
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)

        # draw mesh
        self.program.use()
        self.mesh.draw()
        self.program.unuse()

    @fragment_kernel.transformation
    def set_color_kernel(self, ckrn):
        kernels = self.__class__.FRAGMENT_KERNELS
        ckrn_args, ckrn_kwargs = (), {}

        # we have ('kernel_name', ***)
        # i   ('kernel_name', {kwargs})          --> kernel(**kwargs)
        # ii  ('kernel_name', [args])            --> kernel(*args)
        # iii ('kernel_name', (args))            --> kernel(*args)
        # iv  ('kernel_name', (args), {kwargs})  --> kernel(*args, **kwargs)
        # v   ('kernel_name', x, y, z, ...)      --> kernel(x, y, z, ...)
        if isinstance(ckrn, tuple) \
        or isinstance(ckrn, list):
            if len(ckrn) == 2:  # i - iii
                if isinstance(ckrn[1], dict):
                    ckrn_kwargs = ckrn[1]
                elif isinstance(ckrn[1], tuple) \
                  or isinstance(ckrn[1], list):
                    ckrn_args = ckrn[1]
                else:
                    ckrn_args = (ckrn[1], )
            elif len(ckrn) == 3 \
             and isinstance(ckrn[2], dict) \
             and (isinstance(ckrn[1], tuple)
               or isinstance(ckrn[1], list)):
                ckrn_args, ckrn_kwargs = ckrn[1:3]  # iv
            elif len(ckrn) > 1:
                ckrn_args = ckrn[1:]  # v
            ckrn = ckrn[0]

        # if ckrn is registred, use it.
        if ckrn in kernels:
            ckrn = kernels[ckrn]

        # is it callable?
        if hasattr(ckrn, '__call__'):
            ckrn = ckrn(*ckrn_args, **ckrn_kwargs)
        elif len(ckrn_args) or len(ckrn_kwargs):
            msg = 'kernel {} is not callable, but kernel args (), kwargs () are defined.'
            raise RuntimeError(msg.format(ckrn, ckrn_args, ckrn_kwargs))
        return ckrn
Exemple #2
0
class Cartesian2D(Camera):
    """
    2d cartesian camera 

    Attributes:
        screensize (float[2]): lengths of the space dimensions
        position (float[3]): camera position
        roll (float): rotation of the coordinate space

    Examples:

        XXX

    """
    screensize = attributes.VectorAttribute(2, (1, 1))
    roll = attributes.CastedAttribute(float, 0)
    position = attributes.VectorAttribute(3, (0, 0, 0))

    DTYPE = np.dtype([
        ('mat_view', np.float32, (4, 4)),
        ('mat_projection', np.float32, (4, 4)),
        ('position', np.float32, 3),
        ('roll', np.float32),
        ('direction', np.float32, 3),
        ('_b1', np.float32),
        ('direction_right', np.float32, 3),
        ('_b2', np.float32),
        ('direction_top', np.float32, 3),
    ])

    def __init__(self,
                 screensize,
                 position=(0, 0, 0),
                 roll=0,
                 buffer_base=None):

        super().__init__(
            Cartesian2D.DTYPE, buffer_base
            or GPUPY_GL.CONTEXT.buffer_base('gpupy.gl.camera'))

        self._camera = np.zeros(1, dtype=Cartesian2D.DTYPE)

        self.screensize = screensize
        self.position = position
        self.roll = roll

        self.commit()

    # -- api --

    def __buffer__(self):
        mat_projection = mat4_rot_z(self.roll) @ np.array(
            [
                1,
                0,
                0,
                -self.position.x,
                0,
                1,
                0,
                -self.position.y,
                0,
                0,
                1,
                0,
                0,
                0,
                0,
                1,
            ],
            dtype=np.float32).reshape((4, 4))
        mat_view = np.array([
            2.0 / self.screensize.x,
            0,
            0,
            0,
            0,
            -2.0 / self.screensize.y,
            0,
            0,
            0,
            0,
            1,
            0,
            0,
            0,
            0,
            1,
        ],
                            dtype=np.float32).reshape((4, 4))

        self._camera['mat_view'] = mat_view.T
        self._camera['mat_projection'] = mat_projection.T
        self._camera['roll'] = self.roll
        self._camera['position'] = self.position.xyz

        # XXX is a function of roll
        self._camera['direction'] = (0, 0, -1)
        self._camera['direction_right'] = (1, 0, 0)
        self._camera['direction_top'] = (0, 1, 0)
        return self._camera

    # -- matrix methods

    @roll.on_change
    @position.on_change
    @screensize.on_change
    def _attributes_changed(self, *e):
        self.commit()
Exemple #3
0
class Context():
    """
    context API:

    events:
    -------
    on_ready        when the context is ready to let 
                    the OpenGL.GL functions do their job.

    on_cycle        when the cycle logic should be executed

    on_resize       when the context was resized. It is allowed
                    to perform the tick and rendering logic
                    like on_cycle to provide fluid window resizing.

    """
    size = attributes.VectorAttribute(2)
    resolution = attributes.VectorAttribute(2)
    position = attributes.VectorAttribute(2)
    title = attributes.CastedAttribute(str, 'window')
    visible = attributes.CastedAttribute(bool)

    def __init__(self):
        self.gl_buffer_base_register = {}
        self._next_free_gl_buffer_base_index = 0

        self.on_ready = Event()
        self.on_cycle = Event()
        self.on_resize = Event()
        self.on_close = Event()

        self.active_keys = set()

    def buffer_base(self, name, index=None):
        """ 
        reserves a buffer base with a **name** at **index**.
        if no **index** was given, the next free buffer base
        will be reserved. 

        The reserved buffer index is then returned. 

        Example:

        ```python
            from gpupy.gl import VertexBuffer, GPUPY_GL

            ubo = VertexBuffer.to_device(ndarray, target=UNIFORM_BUFFER)
            ubo.bind_buffer_base(GPUPY_GL.CONTEXT.buffer_base('some_name'))
        ```

        """
        i_max = glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS)
        if not name in self.gl_buffer_base_register:
            if index is not None and index in self.gl_buffer_base_register:
                raise RuntimeError('allready registred.')

            index = index or self._next_free_gl_buffer_base_index
            if index > i_max:
                raise OverflowError()
            self.gl_buffer_base_register[
                name] = index or self._next_free_gl_buffer_base_index

            # find lowest free index
            ui = sorted(self.gl_buffer_base_register.values())
            for i in range(len(ui) - 1):
                if ui[i + 1] - ui[i] != 1:
                    self._next_free_gl_buffer_base_index = ui[i] + 1
                    return
            self._next_free_gl_buffer_base_index = len(ui)

        return self.gl_buffer_base_register[name]

    def __gl_context_enable__(self):
        """ activates OpenGL context """
        GPUPY_GL.CONTEXT = self
Exemple #4
0
class Perspective3D(Camera):
    """
    3d perspective camera

    Attributes:
        fov (float): field of view angle
        frustum (float[2]): near and far plane 
        screensize (float[2]): coordinate space of near plane
        position (float[3]): camera position
        rotation (float[3]): roll, pitch, yaw

    Examples:

        XXX

    """
    screensize = attributes.VectorAttribute(2, (1, 1))
    rotation = attributes.VectorAttribute(3, (0, 0, 0))
    position = attributes.VectorAttribute(3, (0, 0, 0))
    frustum = attributes.VectorAttribute(2, (0.1, 10000))
    fov = attributes.CastedAttribute(float, 0.5 * 65.0 * np.pi / 180.0)

    DTYPE = np.dtype([
        ('mat_view', np.float32, (4, 4)),
        ('mat_projection', np.float32, (4, 4)),
        ('mat_viewprojection', np.float32, (4, 4)),
        ('position', np.float32, 3),
        ('yaw', np.float32),
        ('direction', np.float32, 3),
        ('pitch', np.float32),
        ('direction_right', np.float32, 3),
        ('roll', np.float32),
        ('direction_top', np.float32, 3),
    ])

    def __init__(self,
                 screensize,
                 position=(0, 0, 0),
                 rotation=(0, 0, 0),
                 buffer_base=None):

        super().__init__(
            Perspective3D.DTYPE, buffer_base
            or GPUPY_GL.CONTEXT.buffer_base('gpupy.gl.camera'))

        self._camera = np.zeros(1, dtype=Perspective3D.DTYPE)
        self._mat_projection = None
        self._mat_view = None

        self.screensize = screensize
        self.position = position
        self.rotation = rotation

        self.direction = (0, 0, 0)
        self.right = (0, 0, 0)
        self.top = (0, 0, 0)

        self.commit()

    # -- api --
    def __buffer__(self):
        # camera position and rotation matrix
        reflection_xy = mat4_reflection_xy()
        position_matrix = mat4_translation(*self.position)
        rot_roll = mat4_rot_z(self.rotation[2])
        rot_yaw = mat4_rot_y(self.rotation[1])
        rot_pitch = mat4_rot_x(self.rotation[0])
        mat_view = (
            rot_roll @ rot_pitch @ rot_yaw @ position_matrix @ reflection_xy)

        # perspective projection
        (n, f), ratio = self.frustum, self.screensize[0] / self.screensize[1]
        h = n * np.tan(self.fov)
        w = h * ratio
        mat_proj = np.array([
            n / w, 0, 0, 0, 0, n / h, 0, 0, 0, 0, -(f + n) /
            (f - n), -2.0 * f * n / (f - n), 0, 0, -1, 0
        ],
                            dtype=np.float32).reshape((4, 4))

        # ubo
        self._camera['mat_view'] = mat_view.T
        self._camera['mat_projection'] = mat_proj.T
        # remember.. (AB)^T = (B^T)(A^T)
        self._camera['mat_viewprojection'] = (mat_proj @ mat_view).T

        self._camera['roll'], \
        self._camera['pitch'], \
        self._camera['yaw'] = self.rotation

        self._camera['position'] = self.position

        self._camera['direction'] = self.direction
        self._camera['direction_right'] = self.right
        self._camera['direction_top'] = self.top

        return self._camera

    # -- camera control events --
    @position.on_change
    @screensize.on_change
    @rotation.on_change
    def _attributes_changed(self, *e):
        self.commit()
Exemple #5
0
class AbstractGrid(Widget):
    """

    """
    # widget configuration
    size = attributes.VectorAttribute(2)
    resolution = attributes.VectorAttribute(2)
    position = attributes.VectorAttribute(4)
    cs = attributes.VectorAttribute(4)

    # grid configuration
    major_grid = attributes.VectorAttribute(2, (.5, .5))
    major_grid_width = attributes.CastedAttribute(float, 0.5)
    major_grid_color = attributes.VectorAttribute(4, (0, 0, 0, 1))

    minor_grid_width = attributes.CastedAttribute(float, .5)
    minor_grid_color = attributes.VectorAttribute(4, (0, 0, 0, 1))
    minor_grid_n = attributes.VectorAttribute(2, (5, 5))

    antialiasing = attributes.CastedAttribute(float, .5)

    # style
    background_color = attributes.VectorAttribute(4, (1, 1, 1, 1))

    def __init__(self,
                 size,
                 position=(0, 0, 0, 1),
                 cs=(0, 0),
                 major_grid=(1, 1),
                 background_color=(1, 1, 1, 1),
                 major_grid_color=(0, 0, 0, 1),
                 resolution=None,
                 minor_grid_color=(0, 0, 0, 1),
                 minor_grid_n=(5, 5)):

        super().__init__()

        self.position = position
        self.size = size
        self.cs = cs
        self._t = time()

        self.major_grid = major_grid
        self.major_grid_color = major_grid_color
        self.minor_grid_color = minor_grid_color
        self.minor_grid_n = minor_grid_n
        self.background_color = background_color

        self._req_uniforms = True
        self.resolution = resolution or self.size
        self._dev = False

        self._mat_model = np.array([
            self.resolution.x, 0, 0, 0, 0, self.resolution.y, 0, 0, 0, 0, 1, 0,
            self.position.x, self.position.y, 0, 1
        ],
                                   dtype=np.float32).reshape((4, 4)).T

        self._init_plane()

    @size.on_change
    @cs.on_change
    @major_grid.on_change
    @minor_grid_n.on_change
    @minor_grid_width.on_change
    @major_grid_width.on_change
    @resolution.on_change
    @position.on_change
    def req_uniforms(self, *e):
        self._req_uniforms = True

    def upload_uniforms(self):
        # update model matrix
        self._mat_model[0][0] = self.resolution[0]
        self._mat_model[1][1] = self.resolution[1]
        self._mat_model[3] = self.position
        self.program.uniform('mat_model', self._mat_model)

        # -- polar
        #  self.program.uniform('u_limits1',[-5.1,+5.1,-5.1,+5.1])
        #  self.program.uniform('u_limits2',  [-5.0,+5.0, np.pi/6.0, 11.0*np.pi/6.0])
        #  self.program.uniform('u_major_grid_step', [1.00,np.pi/ 6.0])
        #  self.program.uniform('u_minor_grid_step', [0.25,np.pi/60.0])

        # -- cartesian
        # add this extra bit and assure that u_limits1 != u_limit2
        l1 = self.cs.values * 1.000
        mg = self.major_grid.values
        cs = self.cs.values

        Mw = self.minor_grid_width * max(1.0,
                                         self.resolution[0] / self.size[0])
        mw = self.major_grid_width * max(1.0,
                                         self.resolution[0] / self.size[0])
        aa = self.antialiasing

        self.program.uniform('u_limits1', l1)
        self.program.uniform('u_limits2', cs)
        self.program.uniform('u_major_grid_step', mg)
        self.program.uniform('u_minor_grid_step',
                             self.major_grid.values / self.minor_grid_n)
        self.program.uniform('u_major_grid_width', Mw)
        self.program.uniform('u_minor_grid_width', mw)
        self.program.uniform('u_major_grid_color', self.major_grid_color)
        self.program.uniform('u_minor_grid_color', self.minor_grid_color)
        self.program.uniform('u_resolution', self.resolution)
        self.program.uniform('u_antialias', aa)
        self.program.uniform('c_bg', self.background_color)

        self._req_uniforms = False

    #   print('RES', self.resolution, 'size', self.size, 'CS', self.cs)
    #   print('GRID', self.major_grid)
    def _init_plane(self):
        self.program = GridProgram()

        self.program.uniform_block_binding(
            'camera', GPUPY_GL.CONTEXT.buffer_base('gpupy.gl.camera'))
        self.upload_uniforms()

        self.mesh = StridedVertexMesh(
            mesh3d_rectangle(),
            GL_TRIANGLES,
            attribute_locations=self.program.attributes)

    def dev(self):
        self.program = GridProgramDev()
        self.program.uniform_block_binding(
            'camera', GPUPY_GL.CONTEXT.buffer_base('gpupy.gl.camera'))
        self.upload_uniforms()
        self._dev = True
        return self

    def tick(self):
        if True or self._req_uniforms:
            self.upload_uniforms()
        super().tick()

    def _render(self):
        glEnable(GL_BLEND)
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
        if self._dev:
            self.program.uniform('dev_color1', (1, 0, 0, 1))
            self.program.uniform('dev_color2', (1, 0, 1, 1))
        self.program.use()
        self.mesh.draw()
        self.program.unuse()

        if self._dev:
            glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
            self.program.uniform('dev_color1', (0, 1, 0, 1))
            self.program.uniform('dev_color2', (0, 1, 1, 1))
            self.program.use()
            self.mesh.draw()
            self.program.unuse()
            glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
Exemple #6
0
class Plotter2d(Widget):

    UBO_DTYPE = np.dtype([('mat_cs', np.float32, (4, 4)),
                          ('cs', np.float32, 4), ('cs_size', np.float32, 2)])

    # widget configuration
    size = attributes.VectorAttribute(2)
    position = attributes.VectorAttribute(4, (0, 0, 0, 1))

    # configuration space determines [minx, maxx, miny, maxy]
    cs = attributes.VectorAttribute(4, (0, 1, 0, 1))

    # axes configuration
    # size of one unit (sx, sy) in configuration space
    axes_unit = attributes.VectorAttribute(2, (0.25, 0.25))

    # division of one axes_unit into sub units
    minor_axes_n = attributes.VectorAttribute(2, (5, 5))

    # the plot plane is implemented via framebuffer. this factor
    # allows to adjust the resolution of the framebuffer viewport.
    plot_resolution_factor = attributes.CastedAttribute(float, 1)

    background_color = attributes.VectorAttribute(4, (0, 0, 0, 1))
    plot_background_color = attributes.VectorAttribute(4, (0, 0, 0, 1))
    plot_padding = attributes.VectorAttribute(4, (0, 0, 0, 0))

    # common precomputed properties
    cs_size = attributes.ComputedAttribute(
        cs, descriptor=attributes.VectorAttribute(2), transformation=cs_size)

    def __init__(self,
                 size,
                 position=(0, 0, 0, 1),
                 cs=(0, 1, 0, 1),
                 style=DEFAULT_STYLE,
                 axes_unit=None):

        super().__init__()

        # state
        self.cs = cs
        self.size = size
        self.position = position

        if axes_unit is not None:
            self.axes_unit = axes_unit

        #self.axes_unit = (0.1, 0.1)
        self._style = Style(
            {
                'border': parse_4f1_1c4,
                'background-color': parse_1c4,
                'grid-color': parse_1c4,
                'grid-sub-color': parse_1c4,
                'plot-background-color': parse_1c4,
                'plot-padding': parse_4f1,
                'min-size': parse_2f1,
                'plot-scaling': float,
            }, style)

        self.background_color = self._style['background-color']
        self.plot_background_color = self._style['plot-background-color']
        self.plot_padding = self._style['plot-padding']
        self.ubo = None

        self.on_plot = Event()

        self._plot_margin = vec4((0, 0, 0, 0))
        self.grid = None
        self.layer = None

        self._graphs = []
        self._graphs_initialized = False

        self._init()
        self.a = False
        self.last_fr = False
        self.on_plot.once(self.init_graphs)

        self.cmc = [
            [1, 0, 0, 1],
            [1, 1, 0, 1],
            [1, 0, 1, 1],
            [0, 1, 0, 1],
            [0, 0, 1, 1],
            [1, 1, 1, 1],
        ]

    # -- graph api

    def init_graphs(self):
        for graph in self._graphs:
            graph.init()
        self._graphs_initialized = True

    def append(self, graph):
        self._graphs.append(graph)
        if self._graphs_initialized:
            graph.init()
            graph.resolution = self.plotframe.resulution
            graph.viewport = self.plotframe.resulution

    def __iadd__(self, graph):
        self.append(graph)
        return self

    # -- init

    def _init(self):
        self._initlayer()
        self._initplotcam()
        self._init_grid()
        self._init_ubo()

    # -- plot ubo

    def _init_ubo(self):
        """ initializes plotting ubo """
        self.ubo = BufferObject.to_device(np.zeros(1,
                                                   dtype=Plotter2d.UBO_DTYPE),
                                          target=GL_UNIFORM_BUFFER)
        buffer_base = GPUPY_GL.CONTEXT.buffer_base('gpupy.plot.plotter2d')
        self.ubo.bind_buffer_base(buffer_base)
        self.update_ubo()

    @cs.on_change
    def update_ubo(self, *e):
        self.ubo.host['mat_cs'] = mat_cs(self.cs, self.layer.content_size)
        self.ubo.host['cs'] = self.cs.values
        self.ubo.host['cs_size'] = cs_size(self.cs)
        self.ubo.sync_gpu()

    # -- grid

    def _init_grid(self):
        """ initializes grid component """
        major_grid = observables.transform_observables(
            transformation=grid, observables=(self.axes_unit, self.cs))

        self.grid = CartesianGrid(
            size=self.layer.content_size,
            position=self.layer.content_position,
            cs=self.cs,
            major_grid=major_grid,
            major_grid_color=self._style['grid-color'],
            minor_grid_color=self._style['grid-sub-color'],
            background_color=self.plot_background_color,
            resolution=self.layer.content_size,
            minor_grid_n=self.minor_axes_n)  #.dev()

    # -- camera

    def _initplotcam(self):
        """
        creates a plot camera which is connected to
        the configuration space
        """
        pos = observables.transform_observables(
            lambda s: (s[0] * 0.5, s[1] * 0.5, 1), vecn((0, 0, 0)),
            (self.layer.content_size, ))
        self.plotcam = Camera2D(self.layer.content_size, pos)

    # -- container

    def _initlayer(self):
        """ initializes main plotcontainer.
            the plotcontainer manages border, margin padding
            and contains the main plot framebuffer """

        layer = Container(size=self.size,
                          position=self.position,
                          margin=self._plot_margin,
                          padding=self.plot_padding,
                          border=self._style['border'][0],
                          border_color=self._style['border'][1])

        self.plotframe = FrameWidget(position=layer.content_position,
                                     size=layer.content_size,
                                     resulution=layer.content_size,
                                     clear_color=(0, 0, 0, 0))
        self.layer = layer
        self.layer.content_size.on_change.append(self.update_ubo)

    def tick(self):
        self.on_tick()

        # -- tick the components
        self.plotcam.enable()
        self.layer.tick()
        self.grid.tick()
        self.plotframe.tick()

        # -- graph rendering
        self.plotframe.use()
        self.plotcam.enable()
        self.on_plot()
        self.ubo.bind_buffer_base(
            GPUPY_GL.CONTEXT.buffer_base('gpupy.plot.plotter2d'))
        for graph in self._graphs:
            graph.tick()
            graph.render()
        self.plotframe.unuse()

    def draw(self):
        self.grid.render()
        self.layer.render()
        self.plotframe.render()
Exemple #7
0
class GLFW_Window(Context):
    size = attributes.VectorAttribute(2)
    resolution = attributes.VectorAttribute(2)
    position = attributes.VectorAttribute(2)
    title = attributes.CastedAttribute(str, 'window')
    visible = attributes.CastedAttribute(bool)

    def __init__(self,
                 size=(400, 400),
                 title='gpupy glfw window',
                 bootstrap=True,
                 widget=None):
        super().__init__()
        self._glfw_initialized = False
        self.size = size
        self.title = title
        self.visible = True
        self._active = False
        self.active_keys = set()
        self._in_cycle = False
        if bootstrap:
            self.bootstrap()

        self.make_context()
        self.widget = widget or (lambda *a: True)

    @visible.on_change
    def set_visible(self, visible):
        if visible:
            glfwShowWindow(self._handle)
        else:
            glfwHideWindow(self._handle)

    @size.on_change
    def set_size(self, size):
        glfwSetWindowSize(self._handle, int(size[0]), int(size[1]))

    def bootstrap(self):
        """
        start GLFW window context
        """
        if self._glfw_initialized:
            raise RuntimeError('allready initialized.')

        self._handle = glfwCreateWindow(int(self.size[0]), int(self.size[1]),
                                        self.title)

        if not self._handle:
            raise RuntimeError('glfw.CreateWindow() error')

        glfwWindowHint(GLFW_VISIBLE, int(self.visible))

        def _resize_callback(window, width, height):
            self.size = (width, height)

            if len(self.on_resize):
                # at this point we only make a new context if we are not just
                # within GLFW_Application.cycle method. E.g. if GLFW_Window.set_size()
                # was performed within the GLFW_Window.cycle() method.
                if not self._in_cycle:
                    self.make_context()
                    self.on_cycle(self)
                self.on_resize(self)
                if not self._in_cycle:
                    glfwSwapBuffers(self._handle)

        def _key_callback(window, keycode, scancode, action, option):
            """ put glfw keyboard event data into active and
                pressed keyboard buffer """
            if action == GLFW_PRESS:
                self.active_keys.add(GLFW_Context.KEYBOARD_MAP[keycode])
            elif action == GLFW_RELEASE:
                self.active_keys.remove(GLFW_Context.KEYBOARD_MAP[keycode])

        def _v2_callback(attr, window, width, height):
            setattr(self, attr, (width, height))

        def _close_callback(*e):
            self.on_close(self)

        glfwSetWindowSizeCallback(self._handle, _resize_callback)
        glfwSetFramebufferSizeCallback(self._handle,
                                       partial(_v2_callback, 'resolution'))
        glfwSetWindowCloseCallback(self._handle, _close_callback)
        glfwSetKeyCallback(self._handle, _key_callback)
        glfwSetWindowTitle(self._handle, self.title)

        self.resolution = glfwGetFramebufferSize(self._handle)

        self._glfw_initialized = True

    def make_context(self):
        """
        make the glfw handle the current context
        and assigns itself to GPUPY_GL.CONTEXT.
        """
        if not self._active:
            glfwMakeContextCurrent(self._handle)
            self._active = True

        GPUPY_GL.CONTEXT = self

    def __call__(self):
        """
        runs the gl cycle and executed
        the widget. 

        Returns:
        - Bool: whether the window should be closed or not
        """
        self.make_context()
        self._in_cycle = True

        # GLFW close state
        if glfwWindowShouldClose(self._handle):
            self._active = False
            return False

        # run widget and close if return value is False
        self.on_cycle(self)
        success = self.widget()
        glfwSwapBuffers(self._handle)
        self._in_cycle = False
        if not success:
            self._active = False
            return False

        self._active = True
        return True
Exemple #8
0
class GLFW_Context(Context):
    KEYBOARD_MAP = {
        GLFW_KEY_SPACE: KEY_SPACE,
        GLFW_KEY_APOSTROPHE: KEY_APOSTROPHE,
        GLFW_KEY_COMMA: KEY_COMMA,
        GLFW_KEY_MINUS: KEY_MINUS,
        GLFW_KEY_PERIOD: KEY_PERIOD,
        GLFW_KEY_SLASH: KEY_SLASH,
        GLFW_KEY_0: KEY_0,
        GLFW_KEY_1: KEY_1,
        GLFW_KEY_2: KEY_2,
        GLFW_KEY_3: KEY_3,
        GLFW_KEY_4: KEY_4,
        GLFW_KEY_5: KEY_5,
        GLFW_KEY_6: KEY_6,
        GLFW_KEY_7: KEY_7,
        GLFW_KEY_8: KEY_8,
        GLFW_KEY_9: KEY_9,
        GLFW_KEY_SEMICOLON: KEY_SEMICOLON,
        GLFW_KEY_EQUAL: KEY_EQUAL,
        GLFW_KEY_A: KEY_A,
        GLFW_KEY_B: KEY_B,
        GLFW_KEY_C: KEY_C,
        GLFW_KEY_D: KEY_D,
        GLFW_KEY_E: KEY_E,
        GLFW_KEY_F: KEY_F,
        GLFW_KEY_G: KEY_G,
        GLFW_KEY_H: KEY_H,
        GLFW_KEY_I: KEY_I,
        GLFW_KEY_J: KEY_J,
        GLFW_KEY_K: KEY_K,
        GLFW_KEY_L: KEY_L,
        GLFW_KEY_M: KEY_M,
        GLFW_KEY_N: KEY_N,
        GLFW_KEY_O: KEY_O,
        GLFW_KEY_P: KEY_P,
        GLFW_KEY_Q: KEY_Q,
        GLFW_KEY_R: KEY_R,
        GLFW_KEY_S: KEY_S,
        GLFW_KEY_T: KEY_T,
        GLFW_KEY_U: KEY_U,
        GLFW_KEY_V: KEY_V,
        GLFW_KEY_W: KEY_W,
        GLFW_KEY_X: KEY_X,
        GLFW_KEY_Y: KEY_Y,
        GLFW_KEY_Z: KEY_Z,
        GLFW_KEY_LEFT_BRACKET: KEY_LEFT_BRACKET,
        GLFW_KEY_BACKSLASH: KEY_BACKSLASH,
        GLFW_KEY_RIGHT_BRACKET: KEY_RIGHT_BRACKET,
        GLFW_KEY_GRAVE_ACCENT: KEY_GRAVE_ACCENT,
        GLFW_KEY_WORLD_1: KEY_WORLD_1,
        GLFW_KEY_WORLD_2: KEY_WORLD_2,
        GLFW_KEY_ESCAPE: KEY_ESCAPE,
        GLFW_KEY_ENTER: KEY_ENTER,
        GLFW_KEY_TAB: KEY_TAB,
        GLFW_KEY_BACKSPACE: KEY_BACKSPACE,
        GLFW_KEY_INSERT: KEY_INSERT,
        GLFW_KEY_DELETE: KEY_DELETE,
        GLFW_KEY_RIGHT: KEY_RIGHT,
        GLFW_KEY_LEFT: KEY_LEFT,
        GLFW_KEY_DOWN: KEY_DOWN,
        GLFW_KEY_UP: KEY_UP,
        GLFW_KEY_PAGE_UP: KEY_PAGE_UP,
        GLFW_KEY_PAGE_DOWN: KEY_PAGE_DOWN,
        GLFW_KEY_HOME: KEY_HOME,
        GLFW_KEY_END: KEY_END,
        GLFW_KEY_CAPS_LOCK: KEY_CAPS_LOCK,
        GLFW_KEY_SCROLL_LOCK: KEY_SCROLL_LOCK,
        GLFW_KEY_NUM_LOCK: KEY_NUM_LOCK,
        GLFW_KEY_PRINT_SCREEN: KEY_PRINT_SCREEN,
        GLFW_KEY_PAUSE: KEY_PAUSE,
        GLFW_KEY_F1: KEY_F1,
        GLFW_KEY_F2: KEY_F2,
        GLFW_KEY_F3: KEY_F3,
        GLFW_KEY_F4: KEY_F4,
        GLFW_KEY_F5: KEY_F5,
        GLFW_KEY_F6: KEY_F6,
        GLFW_KEY_F7: KEY_F7,
        GLFW_KEY_F8: KEY_F8,
        GLFW_KEY_F9: KEY_F9,
        GLFW_KEY_F10: KEY_F10,
        GLFW_KEY_F11: KEY_F11,
        GLFW_KEY_F12: KEY_F12,
        GLFW_KEY_F13: KEY_F13,
        GLFW_KEY_F14: KEY_F14,
        GLFW_KEY_F15: KEY_F15,
        GLFW_KEY_F16: KEY_F16,
        GLFW_KEY_F17: KEY_F17,
        GLFW_KEY_F18: KEY_F18,
        GLFW_KEY_F19: KEY_F19,
        GLFW_KEY_F20: KEY_F20,
        GLFW_KEY_F21: KEY_F21,
        GLFW_KEY_F22: KEY_F22,
        GLFW_KEY_F23: KEY_F23,
        GLFW_KEY_F24: KEY_F24,
        GLFW_KEY_F25: KEY_F25,
        GLFW_KEY_KP_0: KEY_KP_0,
        GLFW_KEY_KP_1: KEY_KP_1,
        GLFW_KEY_KP_2: KEY_KP_2,
        GLFW_KEY_KP_3: KEY_KP_3,
        GLFW_KEY_KP_4: KEY_KP_4,
        GLFW_KEY_KP_5: KEY_KP_5,
        GLFW_KEY_KP_6: KEY_KP_6,
        GLFW_KEY_KP_7: KEY_KP_7,
        GLFW_KEY_KP_8: KEY_KP_8,
        GLFW_KEY_KP_9: KEY_KP_9,
        GLFW_KEY_KP_DECIMAL: KEY_KP_DECIMAL,
        GLFW_KEY_KP_DIVIDE: KEY_KP_DIVIDE,
        GLFW_KEY_KP_MULTIPLY: KEY_KP_MULTIPLY,
        GLFW_KEY_KP_SUBTRACT: KEY_KP_SUBTRACT,
        GLFW_KEY_KP_ADD: KEY_KP_ADD,
        GLFW_KEY_KP_ENTER: KEY_KP_ENTER,
        GLFW_KEY_KP_EQUAL: KEY_KP_EQUAL,
        GLFW_KEY_LEFT_SHIFT: KEY_LEFT_SHIFT,
        GLFW_KEY_LEFT_CONTROL: KEY_LEFT_CONTROL,
        GLFW_KEY_LEFT_ALT: KEY_LEFT_ALT,
        GLFW_KEY_LEFT_SUPER: KEY_LEFT_SUPER,
        GLFW_KEY_RIGHT_SHIFT: KEY_RIGHT_SHIFT,
        GLFW_KEY_RIGHT_CONTROL: KEY_RIGHT_CONTROL,
        GLFW_KEY_RIGHT_ALT: KEY_RIGHT_ALT,
        GLFW_KEY_RIGHT_SUPER: KEY_RIGHT_SUPER,
        GLFW_KEY_MENU: KEY_MENU,
        GLFW_KEY_LAST: KEY_LAST,
    }
    size = attributes.VectorAttribute(2)
    resolution = attributes.VectorAttribute(2)
    position = attributes.VectorAttribute(2)
    title = attributes.CastedAttribute(str, 'window')
    visible = attributes.CastedAttribute(bool)
    """ 
    a glfw context manages the window created by glfwCreateWindow.
    """
    def __init__(self, size, title='no title'):
        super().__init__()
        self.size = size
        self.title = title
        self.visible = True
        self._handle = None
        self._glfw_initialized = False
        self._active = False

    def _close_callback(self, *e):
        self.on_close(self)

    @visible.on_change
    def set_visible(self, visible):
        if visible:
            glfwShowWindow(self._handle)
        else:
            glfwHideWindow(self._handle)

    @size.on_change
    def set_size(self, size):
        glfwSetWindowSize(self._handle, int(size[0]), int(size[1]))

    def bootstrap(self):
        if self._glfw_initialized:
            raise RuntimeError('allready initialized.')

        self._handle = glfwCreateWindow(int(self.size[0]), int(self.size[1]),
                                        self.title)
        if not self._handle:
            raise RuntimeError('glfw.CreateWindow() error')

        glfwWindowHint(GLFW_VISIBLE, int(self.visible))

        def _v2_callback(attr, window, width, height):
            setattr(self, attr, (width, height))

        glfwSetWindowSizeCallback(self._handle, self.resize_callback)
        glfwSetFramebufferSizeCallback(self._handle,
                                       partial(_v2_callback, 'resolution'))
        glfwSetWindowCloseCallback(self._handle, self._close_callback)
        glfwSetKeyCallback(self._handle, self.key_callback)
        glfwSetWindowTitle(self._handle, 'wewf')
        self.resolution = glfwGetFramebufferSize(self._handle)

        self._glfw_initialized = True

    def context(self):
        if glfwWindowShouldClose(self._handle):
            self.on_close(self)

        if not self._active:
            glfwMakeContextCurrent(self._handle)
            self._active = True

        super().__gl_context_enable__()

    def resize_callback(self, window, width, height):
        """ triggers on_resize event queue and swaps GLFW buffers. """
        self.size = (width, height)

        if len(self.on_resize):
            # at this point we only make a new context if we are not just
            # within GLFW_Application.cycle method. E.g. if GLFW_Window.set_size()
            # was performed within the GLFW_Window.cycle() method.
            if not self._in_cycle:
                self.__gl_context_enable__()
            self.on_resize(self)
            if not self._in_cycle:
                glfwSwapBuffers(self._handle)

    def key_callback(self, window, keycode, scancode, action, option):
        """ put glfw keyboard event data into active and
            pressed keyboard buffer """
        if action == GLFW_PRESS:
            self.active_keys.add(GLFW_Context.KEYBOARD_MAP[keycode])
        elif action == GLFW_RELEASE:
            self.active_keys.remove(GLFW_Context.KEYBOARD_MAP[keycode])

    def cycle(self):
        if glfwWindowShouldClose(self._handle):
            raise CloseContextException()

        self._in_cycle = True
        self.__gl_context_enable__()
        self.on_cycle(self)
        glfwSwapBuffers(self._handle)
        self._in_cycle = False

    def __del__(self):
        glfwDestroyWindow(self._handle)