vispy-0.4.0/0000775000175000017500000000000012527674621014377 5ustar larsonerlarsoner00000000000000vispy-0.4.0/setup.py0000664000175000017500000001064112527672621016111 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2013, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Vispy setup script. Steps to do a new release: Preparations: * Test on Windows, Linux, Mac * Make release notes * Update API documentation and other docs that need updating. Test installation: * clear the build and dist dir (if they exist) * python setup.py register -r http://testpypi.python.org/pypi * python setup.py sdist upload -r http://testpypi.python.org/pypi * pip install -i http://testpypi.python.org/pypi Define the version: * update __version__ in __init__.py * Tag the tip changeset as version x.x Generate and upload package (preferably on Windows) * python setup.py register * python setup.py sdist upload * python setup.py bdist_wininst upload Announcing: * It can be worth waiting a day for eager users to report critical bugs * Announce in scipy-user, vispy mailing list, G+ """ import os from os import path as op try: # use setuptools namespace, allows for "develop" import setuptools # noqa, analysis:ignore except ImportError: pass # it's not essential for installation from distutils.core import setup name = 'vispy' description = 'Interactive visualization in Python' # Get version and docstring __version__ = None __doc__ = '' docStatus = 0 # Not started, in progress, done initFile = os.path.join(os.path.dirname(__file__), 'vispy', '__init__.py') for line in open(initFile).readlines(): if (line.startswith('version_info') or line.startswith('__version__')): exec(line.strip()) elif line.startswith('"""'): if docStatus == 0: docStatus = 1 line = line.lstrip('"') elif docStatus == 1: docStatus = 2 if docStatus == 1: __doc__ += line def package_tree(pkgroot): path = os.path.dirname(__file__) subdirs = [os.path.relpath(i[0], path).replace(os.path.sep, '.') for i in os.walk(os.path.join(path, pkgroot)) if '__init__.py' in i[2]] return subdirs setup( name=name, version=__version__, author='Vispy contributors', author_email='vispy@googlegroups.com', license='(new) BSD', url='http://vispy.org', download_url='https://pypi.python.org/pypi/vispy', keywords="visualization OpenGl ES medical imaging 3D plotting " "numpy bigdata", description=description, long_description=__doc__, platforms='any', provides=['vispy'], install_requires=['numpy'], extras_requires={ 'ipython-static': ['ipython'], 'ipython-vnc': ['ipython>=2'], 'ipython-webgl': ['ipython>=2', 'tornado'], 'pyglet': ['pyglet>=1.2'], # 'pyqt4': [], # Why is this on PyPI, but without downloads? # 'pyqt5': [], # Ditto. 'pyside': ['PySide'], 'sdl2': ['PySDL2'], 'wx': ['wxPython'], }, packages=package_tree('vispy'), package_dir={ 'vispy': 'vispy'}, package_data={ 'vispy': [op.join('io', '_data', '*'), op.join('html', 'static', 'js', '*'), op.join('app', 'tests', 'qt-designer.ui') ], 'vispy.glsl': ['*.vert','*.frag', "*.glsl"], 'vispy.glsl.antialias': ['*.vert','*.frag', "*.glsl"], 'vispy.glsl.arrows': ['*.vert','*.frag', "*.glsl"], 'vispy.glsl.collections': ['*.vert','*.frag', "*.glsl"], 'vispy.glsl.colormaps': ['*.vert','*.frag', "*.glsl"], 'vispy.glsl.markers': ['*.vert','*.frag', "*.glsl"], 'vispy.glsl.math': ['*.vert','*.frag', "*.glsl"], 'vispy.glsl.misc': ['*.vert','*.frag', "*.glsl"], 'vispy.glsl.transforms': ['*.vert','*.frag', "*.glsl"], }, zip_safe=False, classifiers=[ 'Development Status :: 3 - Alpha', 'Intended Audience :: Science/Research', 'Intended Audience :: Education', 'Intended Audience :: Developers', 'Topic :: Scientific/Engineering :: Visualization', 'License :: OSI Approved :: BSD License', 'Operating System :: MacOS :: MacOS X', 'Operating System :: Microsoft :: Windows', 'Operating System :: POSIX', 'Programming Language :: Python', 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', ], ) vispy-0.4.0/vispy/0000775000175000017500000000000012527674621015551 5ustar larsonerlarsoner00000000000000vispy-0.4.0/vispy/scene/0000775000175000017500000000000012527674621016646 5ustar larsonerlarsoner00000000000000vispy-0.4.0/vispy/scene/node.py0000664000175000017500000003500512527672621020146 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from __future__ import division from ..util.event import Event, EmitterGroup from ..visuals.transforms import (NullTransform, BaseTransform, ChainTransform, create_transform) class Node(object): """ Base class representing an object in a scene. A group of nodes connected through parent-child relationships define a scenegraph. Nodes may have any number of children or parents, although it is uncommon to have more than one parent. Each Node defines a ``transform`` property, which describes the position, orientation, scale, etc. of the Node relative to its parent. The Node's children inherit this property, and then further apply their own transformations on top of that. With the ``transform`` property, each Node implicitly defines a "local" coordinate system, and the Nodes and edges in the scenegraph can be though of as coordinate systems connected by transformation functions. Parameters ---------- parent : Node The parent of the Node. name : str The name used to identify the node. """ # Needed to allow subclasses to repr() themselves before Node.__init__() _name = None def __init__(self, parent=None, name=None): self.name = name self._visible = True # Add some events to the emitter groups: events = ['parents_change', 'children_change', 'transform_change', 'mouse_press', 'mouse_move', 'mouse_release', 'mouse_wheel', 'key_press', 'key_release'] # Create event emitter if needed (in subclasses that inherit from # Visual, we already have an emitter to share) if not hasattr(self, 'events'): self.events = EmitterGroup(source=self, auto_connect=True, update=Event) self.events.add(**dict([(ev, Event) for ev in events])) # Entities are organized in a parent-children hierarchy self._children = [] # TODO: use weakrefs for parents. self._parents = [] if parent is not None: self.parents = parent self._document = None # Components that all entities in vispy have # todo: default transform should be trans-scale-rot transform self._transform = NullTransform() # todo: move visible to BaseVisualNode class when we make Node not a Visual @property def visible(self): """ Whether this node should be drawn or not. Only applicable to nodes that can be drawn. """ return self._visible @visible.setter def visible(self, val): self._visible = bool(val) self.update() @property def name(self): return self._name @name.setter def name(self, n): self._name = n @property def children(self): """ A copy of the list of children of this node. Do not add items to this list, but use ``x.parent = y`` instead. """ return list(self._children) @property def parent(self): """ Get/set the parent. If the node has multiple parents while using this property as a getter, an error is raised. """ if not self._parents: return None elif len(self._parents) == 1: return self._parents[0] else: raise RuntimeError('Ambiguous parent: there are multiple parents.') @parent.setter def parent(self, parent): # This is basically an alias self.parents = parent @property def parents(self): """ Get/set a tuple of parents. """ return tuple(self._parents) @parents.setter def parents(self, parents): # Test input if isinstance(parents, Node): parents = (parents,) if not hasattr(parents, '__iter__'): raise ValueError("Node.parents must be iterable (got %s)" % type(parents)) # Test that all parents are entities for p in parents: if not isinstance(p, Node): raise ValueError('A parent of an node must be an node too,' ' not %s.' % p.__class__.__name__) # Apply prev = list(self._parents) # No list.copy() on Py2.x with self.events.parents_change.blocker(): # Remove parents for parent in prev: if parent not in parents: self.remove_parent(parent) # Add new parents for parent in parents: if parent not in prev: self.add_parent(parent) self.events.parents_change(new=parents, old=prev) def add_parent(self, parent): """Add a parent Parameters ---------- parent : instance of Node The parent. """ if parent in self._parents: return self._parents.append(parent) parent._add_child(self) self.events.parents_change(added=parent) self.update() def remove_parent(self, parent): """Remove a parent Parameters ---------- parent : instance of Node The parent. """ if parent not in self._parents: raise ValueError("Parent not in set of parents for this node.") self._parents.remove(parent) parent._remove_child(self) self.events.parents_change(removed=parent) def _add_child(self, node): self._children.append(node) self.events.children_change(added=node) node.events.update.connect(self.events.update) def _remove_child(self, node): self._children.remove(node) self.events.children_change(removed=node) node.events.update.disconnect(self.events.update) def update(self): """ Emit an event to inform listeners that properties of this Node or its children have changed. """ self.events.update() @property def document(self): """ The document is an optional property that is an node representing the coordinate system from which this node should make physical measurements such as px, mm, pt, in, etc. This coordinate system should be used when determining line widths, font sizes, and any other lengths specified in physical units. The default is None; in this case, a default document is used during drawing (usually this is supplied by the SceneCanvas). """ return self._document @document.setter def document(self, doc): if doc is not None and not isinstance(doc, Node): raise TypeError("Document property must be Node or None.") self._document = doc self.update() @property def transform(self): """ The transform that maps the local coordinate frame to the coordinate frame of the parent. """ return self._transform @transform.setter def transform(self, tr): if self._transform is not None: self._transform.changed.disconnect(self._transform_changed) assert isinstance(tr, BaseTransform) self._transform = tr self._transform.changed.connect(self._transform_changed) self._transform_changed(None) def set_transform(self, type_, *args, **kwargs): """ Create a new transform of *type* and assign it to this node. All extra arguments are used in the construction of the transform. Parameters ---------- type_ : str The transform type. *args : tuple Arguments. **kwargs : dict Keywoard arguments. """ self.transform = create_transform(type_, *args, **kwargs) def _transform_changed(self, event): self.events.transform_change() self.update() def _parent_chain(self): """ Return the chain of parents starting from this node. The chain ends at the first node with either no parents or multiple parents. """ chain = [self] while True: try: parent = chain[-1].parent except Exception: break if parent is None: break chain.append(parent) return chain def describe_tree(self, with_transform=False): """Create tree diagram of children Parameters ---------- with_transform : bool If true, add information about node transform types. Returns ---------- tree : str The tree diagram. """ # inspired by https://github.com/mbr/asciitree/blob/master/asciitree.py return self._describe_tree('', with_transform) def _describe_tree(self, prefix, with_transform): """Helper function to actuall construct the tree""" extra = ': "%s"' % self.name if self.name is not None else '' if with_transform: extra += (' [%s]' % self.transform.__class__.__name__) output = '' if len(prefix) > 0: output += prefix[:-3] output += ' +--' output += '%s%s\n' % (self.__class__.__name__, extra) n_children = len(self.children) for ii, child in enumerate(self.children): sub_prefix = prefix + (' ' if ii+1 == n_children else ' |') output += child._describe_tree(sub_prefix, with_transform) return output def common_parent(self, node): """ Return the common parent of two entities If the entities have no common parent, return None. Does not search past multi-parent branches. Parameters ---------- node : instance of Node The other node. Returns ------- parent : instance of Node | None The parent. """ p1 = self._parent_chain() p2 = node._parent_chain() for p in p1: if p in p2: return p return None def node_path_to_child(self, node): """Return a list describing the path from this node to a child node This method assumes that the given node is a child node. Multiple parenting is allowed. Parameters ---------- node : instance of Node The child node. Returns ------- path : list | None The path. """ if node is self: return [] # Go up from the child node as far as we can path1 = [node] child = node while len(child.parents) == 1: child = child.parent path1.append(child) # Early exit if child is self: return list(reversed(path1)) # Verify that we're not cut off if len(path1[-1].parents) == 0: raise RuntimeError('%r is not a child of %r' % (node, self)) def _is_child(path, parent, child): path.append(parent) if child in parent.children: return path else: for c in parent.children: possible_path = _is_child(path[:], c, child) if possible_path: return possible_path return None # Search from the parent towards the child path2 = _is_child([], self, path1[-1]) if not path2: raise RuntimeError('%r is not a child of %r' % (node, self)) # Return return path2 + list(reversed(path1)) def node_path(self, node): """Return two lists describing the path from this node to another Parameters ---------- node : instance of Node The other node. Returns ------- p1 : list First path (see below). p2 : list Second path (see below). Notes ----- The first list starts with this node and ends with the common parent between the endpoint nodes. The second list contains the remainder of the path from the common parent to the specified ending node. For example, consider the following scenegraph:: A --- B --- C --- D \ --- E --- F Calling `D.node_path(F)` will return:: ([D, C, B], [E, F]) Note that there must be a _single_ path in the scenegraph that connects the two entities; otherwise an exception will be raised. """ p1 = self._parent_chain() p2 = node._parent_chain() cp = None for p in p1: if p in p2: cp = p break if cp is None: raise RuntimeError("No single-path common parent between nodes %s " "and %s." % (self, node)) p1 = p1[:p1.index(cp)+1] p2 = p2[:p2.index(cp)][::-1] return p1, p2 def node_path_transforms(self, node): """Return the list of transforms along the path to another node. The transforms are listed in reverse order, such that the last transform should be applied first when mapping from this node to the other. Parameters ---------- node : instance of Node The other node. Returns ------- transform : instance of Transform The transform. """ a, b = self.node_path(node) return ([n.transform.inverse for n in b] + [n.transform for n in a[:-1]])[::-1] def node_transform(self, node): """ Return the transform that maps from the coordinate system of *self* to the local coordinate system of *node*. Note that there must be a _single_ path in the scenegraph that connects the two entities; otherwise an exception will be raised. Parameters ---------- node : instance of Node The other node. Returns ------- transform : instance of ChainTransform The transform. """ return ChainTransform(self.node_path_transforms(node)) def __repr__(self): name = "" if self.name is None else " name="+self.name return "<%s%s at 0x%x>" % (self.__class__.__name__, name, id(self)) vispy-0.4.0/vispy/scene/canvas.py0000664000175000017500000004242612527672621020501 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from __future__ import division import weakref from .. import gloo from .. import app from .node import Node from ..visuals.transforms import STTransform, TransformCache from ..color import Color from ..util import logger from ..util.profiler import Profiler from .subscene import SubScene from .events import SceneDrawEvent, SceneMouseEvent from .widgets import Widget class SceneCanvas(app.Canvas): """A Canvas that automatically draws the contents of a scene Parameters ---------- title : str The widget title size : (width, height) The size of the window. position : (x, y) The position of the window in screen coordinates. show : bool Whether to show the widget immediately. Default False. autoswap : bool Whether to swap the buffers automatically after a draw event. Default True. If True, the ``swap_buffers`` Canvas method will be called last (by default) by the ``canvas.draw`` event handler. app : Application | str Give vispy Application instance to use as a backend. (vispy.app is used by default.) If str, then an application using the chosen backend (e.g., 'pyglet') will be created. Note the canvas application can be accessed at ``canvas.app``. create_native : bool Whether to create the widget immediately. Default True. vsync : bool Enable vertical synchronization. resizable : bool Allow the window to be resized. decorate : bool Decorate the window. Default True. fullscreen : bool | int If False, windowed mode is used (default). If True, the default monitor is used. If int, the given monitor number is used. config : dict A dict with OpenGL configuration options, which is combined with the default configuration options and used to initialize the context. See ``canvas.context.config`` for possible options. shared : Canvas | GLContext | None An existing canvas or context to share OpenGL objects with. keys : str | dict | None Default key mapping to use. If 'interactive', escape and F11 will close the canvas and toggle full-screen mode, respectively. If dict, maps keys to functions. If dict values are strings, they are assumed to be ``Canvas`` methods, otherwise they should be callable. parent : widget-object The parent widget if this makes sense for the used backend. dpi : float | None Resolution in dots-per-inch to use for the canvas. If dpi is None, then the value will be determined by querying the global config first, and then the operating system. always_on_top : bool If True, try to create the window in always-on-top mode. px_scale : int > 0 A scale factor to apply between logical and physical pixels in addition to the actual scale factor determined by the backend. This option allows the scale factor to be adjusted for testing. bgcolor : Color The background color to use. See also -------- vispy.app.Canvas Notes ----- Receives the following events: * initialize * resize * draw * mouse_press * mouse_release * mouse_double_click * mouse_move * mouse_wheel * key_press * key_release * stylus * touch * close The ordering of the mouse_double_click, mouse_press, and mouse_release events are not guaranteed to be consistent between backends. Only certain backends natively support double-clicking (currently Qt and WX); on other backends, they are detected manually with a fixed time delay. This can cause problems with accessibility, as increasing the OS detection time or using a dedicated double-click button will not be respected. """ def __init__(self, title='Vispy canvas', size=(800, 600), position=None, show=False, autoswap=True, app=None, create_native=True, vsync=False, resizable=True, decorate=True, fullscreen=False, config=None, shared=None, keys=None, parent=None, dpi=None, always_on_top=False, px_scale=1, bgcolor='black'): self._fb_stack = [] # for storing information about framebuffers used self._vp_stack = [] # for storing information about viewports used self._scene = None # A default widget that follows the shape of the canvas self._central_widget = None self._bgcolor = Color(bgcolor).rgba super(SceneCanvas, self).__init__( title, size, position, show, autoswap, app, create_native, vsync, resizable, decorate, fullscreen, config, shared, keys, parent, dpi, always_on_top, px_scale) self.events.mouse_press.connect(self._process_mouse_event) self.events.mouse_move.connect(self._process_mouse_event) self.events.mouse_release.connect(self._process_mouse_event) self.events.mouse_wheel.connect(self._process_mouse_event) # Collection of transform caches; one for each root visual used in # self.draw_visual(...) self._transform_caches = weakref.WeakKeyDictionary() # Set up default node stack: ndc -> fb -> canvas -> scene self.render_cs = Node(name="render_cs") self.framebuffer_cs = Node(parent=self.render_cs, name="framebuffer_cs") self.framebuffer_cs.transform = STTransform() self.canvas_cs = Node(parent=self.framebuffer_cs, name="canvas_cs") self.canvas_cs.transform = STTransform() # By default, the document coordinate system is the canvas. self.canvas_cs.document = self.canvas_cs self.scene = SubScene(parent=self.canvas_cs) @property def scene(self): """ The SubScene object that represents the root node of the scene graph to be displayed. """ return self._scene @scene.setter def scene(self, e): if self._scene is not None: self._scene.events.update.disconnect(self._scene_update) self._scene = e self._scene.events.update.connect(self._scene_update) @property def central_widget(self): """ Returns the default widget that occupies the entire area of the canvas. """ if self._central_widget is None: self._central_widget = Widget(size=self.size, parent=self.scene) return self._central_widget def _scene_update(self, event): self.update() def on_draw(self, event): """Draw handler Parameters ---------- event : instance of Event The draw event. """ if self._scene is None: return # Can happen on initialization logger.debug('Canvas draw') self._draw_scene() def render(self, region=None, size=None): """ Render the scene to an offscreen buffer and return the image array. Parameters ---------- region : tuple | None Specifies the region of the canvas to render. Format is (x, y, w, h). By default, the entire canvas is rendered. size : tuple | None Specifies the size of the image array to return. If no size is given, then the size of the *region* is used. This argument allows the scene to be rendered at resolutions different from the native canvas resolution. Returns ------- image : array Numpy array of type ubyte and shape (h, w, 4). Index [0, 0] is the upper-left corner of the rendered region. """ # Set up a framebuffer to render to offset = (0, 0) if region is None else region[:2] csize = self.size if region is None else region[2:] size = csize if size is None else size fbo = gloo.FrameBuffer(color=gloo.RenderBuffer(size[::-1]), depth=gloo.RenderBuffer(size[::-1])) self.push_fbo(fbo, offset, csize) try: self._draw_scene(viewport=(0, 0) + size) return fbo.read() finally: self.pop_fbo() def _draw_scene(self, viewport=None): self.context.clear(color=self._bgcolor, depth=True) # Draw the scene, but first disconnect its change signal-- # any changes that take place during the paint should not trigger # a subsequent repaint. with self.scene.events.update.blocker(self._scene_update): self.draw_visual(self.scene, viewport=viewport) def draw_visual(self, visual, event=None, viewport=None): """ Draw a visual to the canvas or currently active framebuffer. Parameters ---------- visual : Visual The visual to draw event : None or DrawEvent Optionally specifies the original canvas draw event that initiated this draw. viewport : tuple | None Optionally specifies the viewport to use. If None, the entire physical size is used. """ self.set_current() prof = Profiler() nfb = len(self._fb_stack) nvp = len(self._vp_stack) # Create draw event, which keeps track of the path of transforms self._process_node_count = 0 # for debugging # Get the cache of transforms used for this visual tr_cache = self._transform_caches.setdefault(visual, TransformCache()) # and mark the entire cache as aged tr_cache.roll() prof('roll transform cache') scene_event = SceneDrawEvent(canvas=self, event=event, transform_cache=tr_cache) prof('create SceneDrawEvent') vp = (0, 0) + self.physical_size if viewport is None else viewport scene_event.push_viewport(vp) prof('push_viewport') try: # Force update of transforms on base entities # TODO: this should happen as a reaction to resize, push_viewport, # etc.; not here. (but note the transforms must change # following push_viewport) self.fb_ndc_transform self.canvas_fb_transform scene_event.push_node(self.render_cs) scene_event.push_node(self.framebuffer_cs) scene_event.push_node(self.canvas_cs) scene_event.push_node(visual) prof('initialize event scenegraph') visual.draw(scene_event) prof('draw scene') finally: scene_event.pop_viewport() if len(self._vp_stack) > nvp: logger.warning("Viewport stack not fully cleared after draw.") if len(self._fb_stack) > nfb: logger.warning("Framebuffer stack not fully cleared after draw.") def _process_mouse_event(self, event): prof = Profiler() tr_cache = self._transform_caches.setdefault(self.scene, TransformCache()) scene_event = SceneMouseEvent(canvas=self, event=event, transform_cache=tr_cache) scene_event.push_node(self.render_cs) scene_event.push_node(self.framebuffer_cs) scene_event.push_node(self.canvas_cs) scene_event.push_node(self._scene) prof('prepare mouse event') self._scene._process_mouse_event(scene_event) prof('process') # If something in the scene handled the scene_event, then we mark # the original event accordingly. event.handled = scene_event.handled def on_resize(self, event): """Resize handler Parameters ---------- event : instance of Event The resize event. """ if self._central_widget is not None: self._central_widget.size = self.size # -------------------------------------------------- transform handling --- def push_viewport(self, viewport): """ Push a viewport on the stack It is the responsibility of the caller to ensure the given values are int. The viewport's origin is defined relative to the current viewport. Parameters ---------- viewport : tuple The viewport as (x, y, w, h). """ vp = list(viewport) # Normalize viewport before setting; if vp[2] < 0: vp[0] += vp[2] vp[2] *= -1 if vp[3] < 0: vp[1] += vp[3] vp[3] *= -1 self._vp_stack.append(vp) self.fb_ndc_transform # update! # Apply try: self._set_viewport(vp) except: self._vp_stack.pop() self.fb_ndc_transform # update! raise def pop_viewport(self): """ Pop a viewport from the stack. """ vp = self._vp_stack.pop() # Activate latest if len(self._vp_stack) > 0: self._set_viewport(self._vp_stack[-1]) self.fb_ndc_transform # update! return vp def _set_viewport(self, vp): self.context.set_viewport(*vp) def push_fbo(self, fbo, offset, csize): """ Push an FBO on the stack, together with the new viewport. and the transform to the FBO. Parameters ---------- fbo : instance of FrameBuffer The framebuffer. offset : tuple The offset. csize : tuple The size to use. """ self._fb_stack.append((fbo, offset, csize)) self.canvas_fb_transform # update! # Apply try: fbo.activate() h, w = fbo.color_buffer.shape[:2] self.push_viewport((0, 0, w, h)) except Exception: self._fb_stack.pop() raise def pop_fbo(self): """ Pop an FBO from the stack. """ fbo = self._fb_stack.pop() fbo[0].deactivate() self.pop_viewport() if len(self._fb_stack) > 0: old_fbo = self._fb_stack[-1] old_fbo[0].activate() self.canvas_fb_transform # update! return fbo def _current_framebuffer(self): """ Return (fbo, origin, canvas_size) for the current FBO on the stack, or for the canvas if there is no FBO. """ if len(self._fb_stack) == 0: return None, (0, 0), self.size else: return self._fb_stack[-1] @property def canvas_fb_transform(self): """ The transform that maps from the canvas coordinate system to the current framebuffer coordinate system. The framebuffer coordinate system is used for antialiasing calculations, and is also the system used when specifying coordinates for glViewport (or gloo.set_viewport). Its origin is in the lower-left corner (as opposed to the document / canvas coordinate system, which has its origin in the upper-left corner). Often the canvas and framebuffer coordinate systems are identical. However, some systems with high-resolution displays may use framebuffers with higher resolution than the reported size of the canvas. Likewise, when rendering to an FBO, the resolution and offset of the framebuffer may not match the canvas. """ fbo, offset, csize = self._current_framebuffer() if fbo is None: fbsize = self.physical_size else: fbsize = fbo.color_buffer.shape # image shape is (rows, cols), unlike canvas shape. fbsize = fbsize[1], fbsize[0] map_from = [list(offset), [offset[0] + csize[0], offset[1] + csize[1]]] map_to = [[0, fbsize[1]], [fbsize[0], 0]] self.canvas_cs.transform.set_mapping(map_from, map_to) return self.canvas_cs.transform @property def fb_ndc_transform(self): """ The transform that maps from the framebuffer coordinate system to normalized device coordinates (which is the obligatory output coordinate system for all vertex shaders). This transform accounts for the current glViewport. """ offset, csize, fbsize = self._current_framebuffer() x, y, w, h = self._vp_stack[-1] map_from = [[x, y], [x+w, y+h]] map_to = [[-1, -1], [1, 1]] self.framebuffer_cs.transform.set_mapping(map_from, map_to) return self.framebuffer_cs.transform @property def render_transform(self): """ The transform that maps from the Canvas pixel coordinate system <(0, 0) at top-left, (w, h) at bottom-right> to normalized device coordinates within the current glViewport and FBO. Most visuals should use this transform when drawing. """ return self.fb_ndc_transform * self.canvas_fb_transform @property def bgcolor(self): return Color(self._bgcolor) @bgcolor.setter def bgcolor(self, color): self._bgcolor = Color(color).rgba if hasattr(self, '_backend'): self.update() vispy-0.4.0/vispy/scene/systems.py0000664000175000017500000000601612527672621020730 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from __future__ import division import sys from ..util.logs import logger, _handle_exception from ..util.profiler import Profiler class DrawingSystem(object): """ Simple implementation of a drawing engine. There is one system per viewbox. """ def process(self, event, node): prof = Profiler(str(node)) # Draw this node if it is a visual if hasattr(node, 'draw') and node.visible: try: node.draw(event) prof('draw') except Exception: # get traceback and store (so we can do postmortem # debugging) _handle_exception(False, 'reminders', self, node=node) # Processs children recursively, unless the node has already # handled them. for sub_node in node.children: if sub_node in event.handled_children: continue event.push_node(sub_node) try: self.process(event, sub_node) finally: event.pop_node() prof('process child %s', sub_node) class MouseInputSystem(object): def process(self, event, node): # For simplicity, this system delivers the event to each node # in the scenegraph, except for widgets that are not under the # press_event. # TODO: This eventually should be replaced with a picking system. from .widgets.widget import Widget if isinstance(node, Widget): # widgets are rectangular; easy to do mouse collision # testing if event.press_event is None: deliver = node.rect.contains(*event.pos[:2]) else: deliver = node.rect.contains(*event.press_event.pos[:2]) else: deliver = True if deliver: for sub_node in node.children: event.push_node(sub_node) try: self.process(event, sub_node) finally: event.pop_node() if event.handled: break if not event.handled: try: getattr(node.events, event.type)(event) except Exception: # get traceback and store (so we can do postmortem # debugging) type, value, tb = sys.exc_info() tb = tb.tb_next # Skip *this* frame sys.last_type = type sys.last_value = value sys.last_traceback = tb del tb # Get rid of it in this namespace # Handle logger.log_exception() logger.warning("Error handling mouse event for node %s" % node) #event.pop_node() vispy-0.4.0/vispy/scene/cameras/0000775000175000017500000000000012527674621020261 5ustar larsonerlarsoner00000000000000vispy-0.4.0/vispy/scene/cameras/cameras.py0000664000175000017500000016601412527672621022254 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from __future__ import division import math import numpy as np from ...app import Timer from ...util.quaternion import Quaternion from ...util import keys from ..node import Node from ...geometry import Rect from ...visuals.transforms import (STTransform, PerspectiveTransform, NullTransform, AffineTransform, TransformCache) # todo: Make 3D cameras use same internal state: less code, smooth transitions def make_camera(cam_type, *args, **kwargs): """ Factory function for creating new cameras using a string name. Parameters ---------- cam_type : str May be one of: * 'panzoom' : Creates :class:`PanZoomCamera` * 'turntable' : Creates :class:`TurntableCamera` * None : Creates :class:`Camera` Notes ----- All extra arguments are passed to the __init__ method of the selected Camera class. """ cam_types = {None: BaseCamera} for camType in (BaseCamera, PanZoomCamera, PerspectiveCamera, TurntableCamera, FlyCamera, ArcballCamera): cam_types[camType.__name__[:-6].lower()] = camType try: return cam_types[cam_type](*args, **kwargs) except KeyError: raise KeyError('Unknown camera type "%s". Options are: %s' % (cam_type, cam_types.keys())) class BaseCamera(Node): """ Base camera class. The Camera describes the perspective from which a ViewBox views its subscene, and the way that user interaction affects that perspective. Most functionality is implemented in subclasses. This base class has no user interaction and causes the subscene to use the same coordinate system as the ViewBox. Parameters ---------- interactive : bool Whether the camera processes mouse and keyboard events. flip : tuple of bools For each dimension, specify whether it is flipped. up : {'+z', '-z', '+y', '-y', '+x', '-x'} The direction that is considered up. Default '+z'. Not all camera's may support this (yet). parent : Node The parent of the camera. name : str Name used to identify the camera in the scene. """ # These define the state of the camera _state_props = () # The fractional zoom to apply for a single pixel of mouse motion zoom_factor = 0.007 def __init__(self, interactive=True, flip=None, up='+z', parent=None, name=None): super(BaseCamera, self).__init__(parent, name) # The viewbox for which this camera is active self._viewbox = None # Linked cameras self._linked_cameras = [] self._linked_cameras_no_update = None # Variables related to transforms self.transform = NullTransform() self._pre_transform = None self._viewbox_tr = STTransform() # correction for viewbox self._projection = PerspectiveTransform() self._transform_cache = TransformCache() # For internal use, to store event related information self._event_value = None # Flags self._resetting = False # Avoid lots of updates during set_range self._key_events_bound = False # Have we connected to key events self._set_range_args = None # hold set_range() args # Limits set in reset (interesting region of the scene) self._xlim = None # None is flag that no reset has been performed self._ylim = None self._zlim = None # Our default state to apply when resetting self._default_state = None # We initialize these parameters here, because we want these props # available in all cameras. Note that PanZoom does not use _center self._fov = 0.0 self._center = None # Set parameters. These are all not part of the "camera state" self.interactive = bool(interactive) self.flip = flip if (flip is not None) else (False, False, False) self.up = up def _get_depth_value(self): """ Get the depth value to use in orthographic and perspective projection For 24 bits and more, we're fine with 100.000, but for 16 bits we need 3000 or so. The criterion is that at the center, we should be able to distinguish between 0.1, 0.0 and -0.1 etc. """ if True: # bit+depth >= 24 return 100000.0 else: return 3000.0 def _depth_to_z(self, depth): """ Get the z-coord, given the depth value. """ val = self._get_depth_value() return val - depth * 2 * val def _viewbox_set(self, viewbox): """ Friend method of viewbox to register itself. """ self._viewbox = viewbox # Connect viewbox.events.mouse_press.connect(self.viewbox_mouse_event) viewbox.events.mouse_release.connect(self.viewbox_mouse_event) viewbox.events.mouse_move.connect(self.viewbox_mouse_event) viewbox.events.mouse_wheel.connect(self.viewbox_mouse_event) viewbox.events.resize.connect(self.viewbox_resize_event) # todo: also add key events! (and also on viewbox (they're missing) def _viewbox_unset(self, viewbox): """ Friend method of viewbox to unregister itself. """ self._viewbox = None # Disconnect viewbox.events.mouse_press.disconnect(self.viewbox_mouse_event) viewbox.events.mouse_release.disconnect(self.viewbox_mouse_event) viewbox.events.mouse_move.disconnect(self.viewbox_mouse_event) viewbox.events.mouse_wheel.disconnect(self.viewbox_mouse_event) viewbox.events.resize.disconnect(self.viewbox_resize_event) @property def viewbox(self): """ The viewbox that this camera applies to. """ return self._viewbox ## Camera attributes @property def interactive(self): """ Boolean describing whether the camera should enable or disable user interaction. """ return self._interactive @interactive.setter def interactive(self, value): self._interactive = bool(value) @property def flip(self): return self._flip @flip.setter def flip(self, value): if not isinstance(value, (list, tuple)): raise ValueError('Flip must be a tuple or list.') if len(value) == 2: self._flip = bool(value[0]), bool(value[1]), False elif len(value) == 3: self._flip = bool(value[0]), bool(value[1]), bool(value[2]) else: raise ValueError('Flip must have 2 or 3 elements.') self._flip_factors = tuple([(1-x*2) for x in self._flip]) self.view_changed() @property def up(self): """ The dimension that is considered up. """ return self._up @up.setter def up(self, value): value = value.lower() value = ('+' + value) if value in 'zyx' else value if value not in ('+z', '-z', '+y', '-y', '+x', '-x'): raise ValueError('Invalid value for up.') self._up = value self.view_changed() @property def center(self): """ The center location for this camera The exact meaning of this value differs per type of camera, but generally means the point of interest or the rotation point. """ return self._center or (0, 0, 0) @center.setter def center(self, val): if len(val) == 2: self._center = float(val[0]), float(val[1]), 0.0 elif len(val) == 3: self._center = float(val[0]), float(val[1]), float(val[2]) else: raise ValueError('Center must be a 2 or 3 element tuple') self.view_changed() @property def fov(self): """ Field-of-view angle of the camera. If 0, the camera is in orthographic mode. """ return self._fov @fov.setter def fov(self, fov): fov = float(fov) if fov < 0 or fov >= 180: raise ValueError("fov must be between 0 and 180.") self._fov = fov self.view_changed() ## Camera methods def set_range(self, x=None, y=None, z=None, margin=0.05): """ Set the range of the view region for the camera Parameters ---------- x : tuple | None X range. y : tuple | None Y range. z : tuple | None Z range. margin : float Margin to use. Notes ----- The view is set to the given range or to the scene boundaries if ranges are not specified. The ranges should be 2-element tuples specifying the min and max for each dimension. For the PanZoomCamera the view is fully defined by the range. For e.g. the TurntableCamera the elevation and azimuth are not set. One should use reset() for that. """ # Flag to indicate that this is an initializing (not user-invoked) init = self._xlim is None # Collect given bounds bounds = [None, None, None] if x is not None: bounds[0] = float(x[0]), float(x[1]) if y is not None: bounds[1] = float(y[0]), float(y[1]) if z is not None: bounds[2] = float(z[0]), float(z[1]) # If there is no viewbox, store given bounds in lim variables, and stop if self._viewbox is None: self._set_range_args = bounds[0], bounds[1], bounds[2], margin return # There is a viewbox, we're going to set the range for real self._resetting = True # Get bounds from viewbox if not given if all([(b is None) for b in bounds]): bounds = self._viewbox.get_scene_bounds() else: for i in range(3): if bounds[i] is None: bounds[i] = self._viewbox.get_scene_bounds(i) # Calculate ranges and margins ranges = [b[1] - b[0] for b in bounds] margins = [(r*margin or 0.1) for r in ranges] # Assign limits for this camera bounds_margins = [(b[0]-m, b[1]+m) for b, m in zip(bounds, margins)] self._xlim, self._ylim, self._zlim = bounds_margins # Store center location if (not init) or (self._center is None): self._center = [(b[0] + r / 2) for b, r in zip(bounds, ranges)] # Let specific camera handle it self._set_range(init) # Finish self._resetting = False self.view_changed() def _set_range(self, init): pass def reset(self): """ Reset the view to the default state. """ self.set_state(self._default_state) def set_default_state(self): """ Set the current state to be the default state to be applied when calling reset(). """ self._default_state = self.get_state() def get_state(self): """ Get the current view state of the camera Returns a dict of key-value pairs. The exact keys depend on the camera. Can be passed to set_state() (of this or another camera of the same type) to reproduce the state. """ D = {} for key in self._state_props: D[key] = getattr(self, key) return D def set_state(self, state=None, **kwargs): """ Set the view state of the camera Should be a dict (or kwargs) as returned by get_state. It can be an incomlete dict, in which case only the specified properties are set. Parameters ---------- state : dict The camera state. **kwargs : dict Unused keyword arguments. """ D = state or {} D.update(kwargs) for key, val in D.items(): if key not in self._state_props: raise KeyError('Not a valid camera state property %r' % key) setattr(self, key, val) def link(self, camera): """ Link this camera with another camera of the same type Linked camera's keep each-others' state in sync. Parameters ---------- camera : instance of Camera The other camera to link. """ cam1, cam2 = self, camera # Remove if already linked while cam1 in cam2._linked_cameras: cam2._linked_cameras.remove(cam1) while cam2 in cam1._linked_cameras: cam1._linked_cameras.remove(cam2) # Link both ways cam1._linked_cameras.append(cam2) cam2._linked_cameras.append(cam1) ## Event-related methods def view_changed(self): """ Called when this camera is changes its view. Also called when its associated with a viewbox. """ if self._resetting: return # don't update anything while resetting (are in set_range) if self._viewbox: # Set range if necessary if self._xlim is None: args = self._set_range_args or () self.set_range(*args) # Store default state if we have not set it yet if self._default_state is None: self.set_default_state() # Do the actual update self._update_transform() @property def pre_transform(self): """ A transform to apply to the beginning of the scene transform, in addition to anything else provided by this Camera. """ return self._pre_transform @pre_transform.setter def pre_transform(self, tr): self._pre_transform = tr self.view_changed() def viewbox_mouse_event(self, event): """Viewbox mouse event handler Parameters ---------- event : instance of Event The event. """ # todo: connect this method to viewbox.events.key_down # viewbox does not currently support key events # A bit awkward way to connect to our canvas; we need event # object to get a reference to the canvas # todo: fix this, also only receive key events when over the viewbox if not self._key_events_bound: self._key_events_bound = True event.canvas.events.key_press.connect(self.viewbox_key_event) event.canvas.events.key_release.connect(self.viewbox_key_event) def viewbox_key_event(self, event): """ViewBox key event handler Parameters ---------- event : instance of Event The event. """ if event.key == keys.BACKSPACE: self.reset() def viewbox_resize_event(self, event): """The ViewBox resize handler to update the transform Parameters ---------- event : instance of Event The event. """ pass def _update_transform(self): """ Subclasses should reimplement this method to update the scene transform by calling self._set_scene_transform. """ self._set_scene_transform(self.transform) def _set_scene_transform(self, tr): """ Called by subclasses to configure the viewbox scene transform. """ # todo: check whether transform has changed, connect to # transform.changed event pre_tr = self.pre_transform if pre_tr is None: self._scene_transform = tr else: self._transform_cache.roll() self._scene_transform = self._transform_cache.get([pre_tr, tr]) # Update scene self._viewbox.scene.transform = self._scene_transform self._viewbox.update() # Apply same state to linked cameras, but prevent that camera # to return the favor for cam in self._linked_cameras: if cam is self._linked_cameras_no_update: continue try: cam._linked_cameras_no_update = self cam.set_state(self.get_state()) finally: cam._linked_cameras_no_update = None class PanZoomCamera(BaseCamera): """ Camera implementing 2D pan/zoom mouse interaction. For this camera, the ``scale_factor`` indicates the zoom level, and the ``center`` indicates the center position of the view. By default, this camera inverts the y axis of the scene. This usually results in the scene +y axis pointing upward because widgets (including ViewBox) have their +y axis pointing downward. Parameters ---------- rect : Rect A Rect object or 4-element tuple that specifies the rectangular area to show. aspect : float | None The aspect ratio (i.e. scaling) between x and y dimension of the scene. E.g. to show a square image as square, the aspect should be 1. If None (default) the x and y dimensions are scaled independently. **kwargs : dict Keyword arguments to pass to `BaseCamera`. Notes ----- Interaction: * LMB: pan the view * RMB or scroll: zooms the view """ _state_props = BaseCamera._state_props + ('rect', ) def __init__(self, rect=None, aspect=None, **kwargs): super(PanZoomCamera, self).__init__(**kwargs) self.transform = STTransform() # Set camera attributes self.aspect = aspect self._rect = None if rect is not None: self.rect = rect @property def aspect(self): """ The ratio between the x and y dimension. E.g. to show a square image as square, the aspect should be 1. If None, the dimensions are scaled automatically, dependening on the available space. Otherwise the ratio between the dimensions is fixed. """ return self._aspect @aspect.setter def aspect(self, value): if value is None: self._aspect = None else: self._aspect = float(value) self.view_changed() def zoom(self, factor, center=None): """ Zoom in (or out) at the given center Parameters ---------- factor : float or tuple Fraction by which the scene should be zoomed (e.g. a factor of 2 causes the scene to appear twice as large). center : tuple of 2-4 elements The center of the view. If not given or None, use the current center. """ assert len(center) in (2, 3, 4) # Get scale factor, take scale ratio into account if np.isscalar(factor): scale = [factor, factor] else: if len(factor) != 2: raise TypeError("factor must be scalar or length-2 sequence.") scale = list(factor) if self.aspect is not None: scale[0] = scale[1] # Init some variables center = center if (center is not None) else self.center rect = self.rect # Get space from given center to edges left_space = center[0] - rect.left right_space = rect.right - center[0] bottom_space = center[1] - rect.bottom top_space = rect.top - center[1] # Scale these spaces rect.left = center[0] - left_space * scale[0] rect.right = center[0] + right_space * scale[0] rect.bottom = center[1] - bottom_space * scale[1] rect.top = center[1] + top_space * scale[1] self.rect = rect def pan(self, *pan): """Pan the view. Parameters ---------- *pan : length-2 sequence The distance to pan the view, in the coordinate system of the scene. """ if len(pan) == 1: pan = pan[0] self.rect = self.rect + pan @property def rect(self): """ The rectangular border of the ViewBox visible area, expressed in the coordinate system of the scene. Note that the rectangle can have negative width or height, in which case the corresponding dimension is flipped (this flipping is independent from the camera's ``flip`` property). """ return self._rect @rect.setter def rect(self, args): if isinstance(args, tuple): self._rect = Rect(*args) else: self._rect = Rect(args) self.view_changed() @property def center(self): rect = self._rect return 0.5 * (rect.left+rect.right), 0.5 * (rect.top+rect.bottom), 0 @center.setter def center(self, center): if not (isinstance(center, (tuple, list)) and len(center) in (2, 3)): raise ValueError('center must be a 2 or 3 element tuple') rect = self.rect or Rect(0, 0, 1, 1) # Get half-ranges x2 = 0.5 * (rect.right - rect.left) y2 = 0.5 * (rect.top - rect.bottom) # Apply new ranges rect.left = center[0] - x2 rect.right = center[0] + x2 rect.bottom = center[1] - y2 rect.top = center[1] + y2 # self.rect = rect def _set_range(self, init): if init and self._rect is not None: return # Convert limits to rect w = self._xlim[1] - self._xlim[0] h = self._ylim[1] - self._ylim[0] self.rect = self._xlim[0], self._ylim[0], w, h def viewbox_resize_event(self, event): """ Modify the data aspect and scale factor, to adjust to the new window size. Parameters ---------- event : instance of Event The event. """ self.view_changed() def viewbox_mouse_event(self, event): """ The SubScene received a mouse event; update transform accordingly. Parameters ---------- event : instance of Event The event. """ if event.handled or not self.interactive: return # Scrolling BaseCamera.viewbox_mouse_event(self, event) if event.type == 'mouse_wheel': center = self._scene_transform.imap(event.pos) self.zoom((1 + self.zoom_factor) ** (-event.delta[1] * 30), center) elif event.type == 'mouse_move': if event.press_event is None: return modifiers = event.mouse_event.modifiers p1 = event.mouse_event.press_event.pos p2 = event.mouse_event.pos if 1 in event.buttons and not modifiers: # Translate p1 = np.array(event.last_event.pos)[:2] p2 = np.array(event.pos)[:2] p1s = self._transform.imap(p1) p2s = self._transform.imap(p2) self.pan(p1s-p2s) elif 2 in event.buttons and not modifiers: # Zoom p1c = np.array(event.last_event.pos)[:2] p2c = np.array(event.pos)[:2] scale = ((1 + self.zoom_factor) ** ((p1c-p2c) * np.array([1, -1]))) center = self._transform.imap(event.press_event.pos[:2]) self.zoom(scale, center) def _update_transform(self): rect = self.rect self._real_rect = Rect(rect) vbr = self._viewbox.rect.flipped(x=self.flip[0], y=(not self.flip[1])) d = self._get_depth_value() # apply scale ratio constraint if self._aspect is not None: # Aspect ratio of the requested range requested_aspect = (rect.width / rect.height if rect.height != 0 else 1) # Aspect ratio of the viewbox view_aspect = vbr.width / vbr.height if vbr.height != 0 else 1 # View aspect ratio needed to obey the scale constraint constrained_aspect = abs(view_aspect / self._aspect) if requested_aspect > constrained_aspect: # view range needs to be taller than requested dy = 0.5 * (rect.width / constrained_aspect - rect.height) self._real_rect.top += dy self._real_rect.bottom -= dy else: # view range needs to be wider than requested dx = 0.5 * (rect.height * constrained_aspect - rect.width) self._real_rect.left -= dx self._real_rect.right += dx # Apply mapping between viewbox and cam view self.transform.set_mapping(self._real_rect, vbr) # Scale z, so that the clipping planes are between -alot and +alot self.transform.zoom((1, 1, 1/d)) # We've now set self.transform, which represents our 2D # transform When up is +z this is all. In other cases, # self.transform is now set up correctly to allow pan/zoom, but # for the scene we need a different (3D) mapping. When there # is a minus in up, we simply look at the scene from the other # side (as if z was flipped). if self.up == '+z': thetransform = self.transform else: rr = self._real_rect tr = AffineTransform() d = d if (self.up[0] == '+') else -d pp1 = [(vbr.left, vbr.bottom, 0), (vbr.left, vbr.top, 0), (vbr.right, vbr.bottom, 0), (vbr.left, vbr.bottom, 1)] # Get Mapping if self.up[1] == 'z': pp2 = [(rr.left, rr.bottom, 0), (rr.left, rr.top, 0), (rr.right, rr.bottom, 0), (rr.left, rr.bottom, d)] elif self.up[1] == 'y': pp2 = [(rr.left, 0, rr.bottom), (rr.left, 0, rr.top), (rr.right, 0, rr.bottom), (rr.left, d, rr.bottom)] elif self.up[1] == 'x': pp2 = [(0, rr.left, rr.bottom), (0, rr.left, rr.top), (0, rr.right, rr.bottom), (d, rr.left, rr.bottom)] # Apply tr.set_mapping(np.array(pp2), np.array(pp1)) thetransform = tr # Set on viewbox self._set_scene_transform(thetransform) class PerspectiveCamera(BaseCamera): """ Base class for 3D cameras supporting orthographic and perspective projections. Parameters ---------- fov : float Field of view. Default 60.0. scale_factor : scalar A measure for the scale/range of the scene that the camera should show. The exact meaning differs per camera type. **kwargs : dict Keyword arguments to pass to `BaseCamera`. """ _state_props = ('scale_factor', 'center', 'fov') def __init__(self, fov=60.0, scale_factor=None, center=None, **kwargs): super(PerspectiveCamera, self).__init__(**kwargs) # Camera transform self.transform = AffineTransform() # Set camera attributes self.fov = fov self._scale_factor = None self._center = None # Only set if they are given. They're set during _set_range if None if scale_factor is not None: self.scale_factor = scale_factor if center is not None: self.center = center def viewbox_mouse_event(self, event): """ The ViewBox received a mouse event; update transform accordingly. Default implementation adjusts scale factor when scolling. Parameters ---------- event : instance of Event The event. """ BaseCamera.viewbox_mouse_event(self, event) if event.type == 'mouse_wheel': s = 1.1 ** - event.delta[1] self._scale_factor *= s if self._distance is not None: self._distance *= s self.view_changed() @property def scale_factor(self): """ The measure for the scale or range that the camera should cover For the PanZoomCamera and TurnTableCamera this translates to zooming: set to smaller values to zoom in. """ return self._scale_factor @scale_factor.setter def scale_factor(self, value): self._scale_factor = abs(float(value)) self.view_changed() @property def near_clip_distance(self): """ The distance of the near clipping plane from the camera's position. """ return self._near_clip_distance def _set_range(self, init): """ Reset the camera view using the known limits. """ if init and (self._scale_factor is not None): return # We don't have to set our scale factor # Get window size (and store factor now to sync with resizing) w, h = self._viewbox.size w, h = float(w), float(h) # Get range and translation for x and y x1, y1, z1 = self._xlim[0], self._ylim[0], self._zlim[0] x2, y2, z2 = self._xlim[1], self._ylim[1], self._zlim[1] rx, ry, rz = (x2 - x1), (y2 - y1), (z2 - z1) # Correct ranges for window size. Note that the window width # influences the x and y data range, while the height influences # the z data range. if w / h > 1: rx /= w / h ry /= w / h else: rz /= h / w # Convert to screen coordinates. In screen x, only x and y have effect. # In screen y, all three dimensions have effect. The idea of the lines # below is to calculate the range on screen when that will fit the # data under any rotation. rxs = (rx**2 + ry**2)**0.5 rys = (rx**2 + ry**2 + rz**2)**0.5 self.scale_factor = max(rxs, rys) * 1.04 # 4% extra space def viewbox_resize_event(self, event): """The ViewBox resize handler to update the transform Parameters ---------- event : instance of Event The event. """ self.view_changed() def _update_transform(self, event=None): # Do we have a viewbox if self._viewbox is None: return # Calculate viewing range for x and y fx = fy = self._scale_factor # Correct for window size w, h = self._viewbox.size if w / h > 1: fx *= w / h else: fy *= h / w self._update_projection_transform(fx, fy) # assemble complete transform mapping to viewbox bounds unit = [[-1, 1], [1, -1]] vrect = [[0, 0], self._viewbox.size] self._viewbox_tr.set_mapping(unit, vrect) transforms = [n.transform for n in self._viewbox.scene.node_path_to_child(self)[1:]] camera_tr = self._transform_cache.get(transforms).inverse full_tr = self._transform_cache.get([self._viewbox_tr, self._projection, camera_tr]) self._transform_cache.roll() self._set_scene_transform(full_tr) def _update_projection_transform(self, fx, fy): d = self._get_depth_value() if self._fov == 0: self._projection.set_ortho(-0.5*fx, 0.5*fx, -0.5*fy, 0.5*fy, 0, d) else: fov = max(0.01, self._fov) dist = fy / (2 * math.tan(math.radians(fov)/2)) val = math.sqrt(d) self._projection.set_perspective(fov, fx/fy, dist/val, dist*val) class Base3DRotationCamera(PerspectiveCamera): """Base class for TurntableCamera and ArcballCamera""" def __init__(self, fov=0.0, **kwargs): super(Base3DRotationCamera, self).__init__(fov=fov, **kwargs) self._actual_distance = 0.0 self._event_value = None @property def distance(self): """ The user-set distance. If None (default), the distance is internally calculated from the scale factor and fov. """ return self._distance @distance.setter def distance(self, distance): if distance is None: self._distance = None else: self._distance = float(distance) self.view_changed() def viewbox_mouse_event(self, event): """ The viewbox received a mouse event; update transform accordingly. Parameters ---------- event : instance of Event The event. """ if event.handled or not self.interactive: return PerspectiveCamera.viewbox_mouse_event(self, event) if event.type == 'mouse_release': self._event_value = None # Reset elif event.type == 'mouse_move': if event.press_event is None: return modifiers = event.mouse_event.modifiers p1 = event.mouse_event.press_event.pos p2 = event.mouse_event.pos d = p2 - p1 if 1 in event.buttons and not modifiers: # Rotate self._update_rotation(event) elif 2 in event.buttons and not modifiers: # Zoom if self._event_value is None: self._event_value = (self._scale_factor, self._distance) zoomy = (1 + self.zoom_factor) ** d[1] self.scale_factor = self._event_value[0] * zoomy # Modify distance if its given if self._distance is not None: self._distance = self._event_value[1] * zoomy self.view_changed() elif 1 in event.buttons and keys.SHIFT in modifiers: # Translate norm = np.mean(self._viewbox.size) if self._event_value is None: self._event_value = self.center dist = (p1 - p2) / norm * self._scale_factor dist[1] *= -1 # Black magic part 1: turn 2D into 3D translations dx, dy, dz = self._dist_to_trans(dist) # Black magic part 2: take up-vector and flipping into account ff = self._flip_factors up, forward, right = self._get_dim_vectors() dx, dy, dz = right * dx + forward * dy + up * dz dx, dy, dz = ff[0] * dx, ff[1] * dy, dz * ff[2] c = self._event_value self.center = c[0] + dx, c[1] + dy, c[2] + dz elif 2 in event.buttons and keys.SHIFT in modifiers: # Change fov if self._event_value is None: self._event_value = self._fov fov = self._event_value - d[1] / 5.0 self.fov = min(180.0, max(0.0, fov)) def _update_camera_pos(self): """ Set the camera position and orientation""" # transform will be updated several times; do not update camera # transform until we are done. ch_em = self.events.transform_change with ch_em.blocker(self._update_transform): tr = self.transform tr.reset() up, forward, right = self._get_dim_vectors() # Create mapping so correct dim is up pp1 = np.array([(0, 0, 0), (0, 0, -1), (1, 0, 0), (0, 1, 0)]) pp2 = np.array([(0, 0, 0), forward, right, up]) tr.set_mapping(pp1, pp2) tr.translate(-self._actual_distance * forward) self._rotate_tr() tr.scale([1.0/a for a in self._flip_factors]) tr.translate(np.array(self.center)) def _get_dim_vectors(self): # Specify up and forward vector M = {'+z': [(0, 0, +1), (0, 1, 0)], '-z': [(0, 0, -1), (0, 1, 0)], '+y': [(0, +1, 0), (1, 0, 0)], '-y': [(0, -1, 0), (1, 0, 0)], '+x': [(+1, 0, 0), (0, 0, 1)], '-x': [(-1, 0, 0), (0, 0, 1)], } up, forward = M[self.up] right = np.cross(forward, up) return np.array(up), np.array(forward), right def _update_projection_transform(self, fx, fy): d = self._get_depth_value() if self._fov == 0: self._projection.set_ortho(-0.5*fx, 0.5*fx, -0.5*fy, 0.5*fy, -d, d) self._actual_distance = self._distance or 0.0 else: # Figure distance to center in order to have correct FoV and fy. # Use that auto-distance, or the given distance (if not None). fov = max(0.01, self._fov) dist = fy / (2 * math.tan(math.radians(fov)/2)) self._actual_distance = dist = self._distance or dist val = math.sqrt(d*10) self._projection.set_perspective(fov, fx/fy, dist/val, dist*val) # Update camera pos, which will use our calculated _distance to offset # the camera self._update_camera_pos() def _update_rotation(self, event): """Update rotation parmeters based on mouse movement""" raise NotImplementedError def _rotate_tr(self): """Rotate the transformation matrix based on camera parameters""" raise NotImplementedError def _dist_to_trans(self, dist): """Convert mouse x, y movement into x, y, z translations""" raise NotImplementedError class TurntableCamera(Base3DRotationCamera): """ 3D camera class that orbits around a center point while maintaining a view on a center point. For this camera, the ``scale_factor`` indicates the zoom level, and the ``center`` indicates the position to put at the center of the view. Parameters ---------- fov : float Field of view. Zero (default) means orthographic projection. elevation : float Elevation angle in degrees. Positive angles place the camera above the cente point, negative angles place the camera below the center point. azimuth : float Azimuth angle in degrees. Zero degrees places the camera on the positive x-axis, pointing in the negative x direction. roll : float Roll angle in degrees distance : float | None The distance of the camera from the rotation point (only makes sense if fov > 0). If None (default) the distance is determined from the scale_factor and fov. **kwargs : dict Keyword arguments to pass to `BaseCamera`. Notes ----- Interaction: * LMB: orbits the view around its center point. * RMB or scroll: change scale_factor (i.e. zoom level) * SHIFT + LMB: translate the center point * SHIFT + RMB: change FOV """ _state_props = Base3DRotationCamera._state_props + ('elevation', 'azimuth', 'roll') def __init__(self, fov=0.0, elevation=30.0, azimuth=30.0, roll=0.0, distance=None, **kwargs): super(TurntableCamera, self).__init__(fov=fov, **kwargs) # Set camera attributes self.azimuth = azimuth self.elevation = elevation self.roll = roll # interaction not implemented yet self.distance = distance # None means auto-distance @property def elevation(self): """ The angle of the camera in degrees above the horizontal (x, z) plane. """ return self._elevation @elevation.setter def elevation(self, elev): elev = float(elev) self._elevation = min(90, max(-90, elev)) self.view_changed() @property def azimuth(self): """ The angle of the camera in degrees around the y axis. An angle of 0 places the camera within the (y, z) plane. """ return self._azimuth @azimuth.setter def azimuth(self, azim): azim = float(azim) while azim < -180: azim += 360 while azim > 180: azim -= 360 self._azimuth = azim self.view_changed() @property def roll(self): """ The angle of the camera in degrees around the z axis. An angle of 0 places puts the camera upright. """ return self._roll @roll.setter def roll(self, roll): roll = float(roll) while roll < -180: roll += 360 while roll > 180: roll -= 360 self._roll = roll self.view_changed() def orbit(self, azim, elev): """ Orbits the camera around the center position. Parameters ---------- azim : float Angle in degrees to rotate horizontally around the center point. elev : float Angle in degrees to rotate vertically around the center point. """ self.azimuth += azim self.elevation = np.clip(self.elevation + elev, -90, 90) self.view_changed() def _update_rotation(self, event): """Update rotation parmeters based on mouse movement""" p1 = event.mouse_event.press_event.pos p2 = event.mouse_event.pos if self._event_value is None: self._event_value = self.azimuth, self.elevation self.azimuth = self._event_value[0] - (p2 - p1)[0] * 0.5 self.elevation = self._event_value[1] + (p2 - p1)[1] * 0.5 def _rotate_tr(self): """Rotate the transformation matrix based on camera parameters""" up, forward, right = self._get_dim_vectors() self.transform.rotate(self.elevation, -right) self.transform.rotate(self.azimuth, up) def _dist_to_trans(self, dist): """Convert mouse x, y movement into x, y, z translations""" rae = np.array([self.roll, self.azimuth, self.elevation]) * np.pi / 180 sro, saz, sel = np.sin(rae) cro, caz, cel = np.cos(rae) dx = (+ dist[0] * (cro * caz + sro * sel * saz) + dist[1] * (sro * caz - cro * sel * saz)) dy = (+ dist[0] * (cro * saz - sro * sel * caz) + dist[1] * (sro * saz + cro * sel * caz)) dz = (- dist[0] * sro * cel + dist[1] * cro * cel) return dx, dy, dz class ArcballCamera(Base3DRotationCamera): """ 3D camera class that orbits around a center point while maintaining a view on a center point. For this camera, the ``scale_factor`` indicates the zoom level, and the ``center`` indicates the position to put at the center of the view. Parameters ---------- fov : float Field of view. Zero (default) means orthographic projection. distance : float | None The distance of the camera from the rotation point (only makes sense if fov > 0). If None (default) the distance is determined from the scale_factor and fov. **kwargs : dict Keyword arguments to pass to `BaseCamera`. Notes ----- Interaction: * LMB: orbits the view around its center point. * RMB or scroll: change scale_factor (i.e. zoom level) * SHIFT + LMB: translate the center point * SHIFT + RMB: change FOV """ _state_props = Base3DRotationCamera._state_props def __init__(self, fov=0.0, distance=None, **kwargs): super(ArcballCamera, self).__init__(fov=fov, **kwargs) # Set camera attributes self._quaternion = Quaternion() self.distance = distance # None means auto-distance def _update_rotation(self, event): """Update rotation parmeters based on mouse movement""" p2 = event.mouse_event.pos if self._event_value is None: self._event_value = p2 wh = self._viewbox.size self._quaternion = (Quaternion(*_arcball(p2, wh)) * Quaternion(*_arcball(self._event_value, wh)) * self._quaternion) self._event_value = p2 self.view_changed() def _rotate_tr(self): """Rotate the transformation matrix based on camera parameters""" rot, x, y, z = self._quaternion.get_axis_angle() up, forward, right = self._get_dim_vectors() self.transform.rotate(180 * rot / np.pi, (x, z, y)) def _dist_to_trans(self, dist): """Convert mouse x, y movement into x, y, z translations""" rot, x, y, z = self._quaternion.get_axis_angle() tr = AffineTransform() tr.rotate(180 * rot / np.pi, (x, y, z)) dx, dz, dy = np.dot(tr.matrix[:3, :3], (dist[0], dist[1], 0.)) return dx, dy, dz def _get_dim_vectors(self): # Override vectors, camera has no sense of "up" return np.eye(3)[::-1] def _arcball(xy, wh): """Convert x,y coordinates to w,x,y,z Quaternion parameters Adapted from: linalg library Copyright (c) 2010-2015, Renaud Blanch Licence at your convenience: GPLv3 or higher BSD new """ x, y = xy w, h = wh r = (w + h) / 2. x, y = -(2. * x - w) / r, (2. * y - h) / r h = np.sqrt(x*x + y*y) return (0., x/h, y/h, 0.) if h > 1. else (0., x, y, np.sqrt(1. - h*h)) class FlyCamera(PerspectiveCamera): """ The fly camera provides a way to explore 3D data using an interaction style that resembles a flight simulator. For this camera, the ``scale_factor`` indicates the speed of the camera in units per second, and the ``center`` indicates the position of the camera. Parameters ---------- fov : float Field of view. Default 60.0. rotation : float | None Rotation to use. **kwargs : dict Keyword arguments to pass to `BaseCamera`. Notes ----- Interacting with this camera might need a bit of practice. The reaction to key presses can be customized by modifying the keymap property. Moving: * arrow keys, or WASD to move forward, backward, left and right * F and C keys move up and down * Space bar to brake Viewing: * Use the mouse while holding down LMB to control the pitch and yaw. * Alternatively, the pitch and yaw can be changed using the keys IKJL * The camera auto-rotates to make the bottom point down, manual rolling can be performed using Q and E. """ # Linking this camera likely not to work very well _state_props = PerspectiveCamera._state_props + ('rotation', ) def __init__(self, fov=60, rotation=None, **kwargs): # Motion speed vector self._speed = np.zeros((6,), 'float64') # Acceleration and braking vectors, set from keyboard self._brake = np.zeros((6,), 'uint8') # bool-ish self._acc = np.zeros((6,), 'float64') # Init rotations self._auto_roll = True # Whether to roll to make Z up self._rotation1 = Quaternion() # The base rotation self._rotation2 = Quaternion() # The delta yaw and pitch rotation PerspectiveCamera.__init__(self, fov=fov, **kwargs) # Set camera attributes self.rotation = rotation if (rotation is not None) else Quaternion() # To store data at start of interaction self._event_value = None # Whether the mouse-system wants a transform update self._update_from_mouse = False # Mapping that defines keys to thrusters self._keymap = { keys.UP: (+1, 1), keys.DOWN: (-1, 1), keys.RIGHT: (+1, 2), keys.LEFT: (-1, 2), # 'W': (+1, 1), 'S': (-1, 1), 'D': (+1, 2), 'A': (-1, 2), 'F': (+1, 3), 'C': (-1, 3), # 'I': (+1, 4), 'K': (-1, 4), 'L': (+1, 5), 'J': (-1, 5), 'Q': (+1, 6), 'E': (-1, 6), # keys.SPACE: (0, 1, 2, 3), # 0 means brake, apply to translation #keys.ALT: (+5, 1), # Turbo } # Timer. Each tick we calculate new speed and new position self._timer = Timer(0.01, start=False, connect=self.on_timer) @property def rotation(self): """ Get the full rotation. This rotation is composed of the normal rotation plus the extra rotation due to the current interaction of the user. """ rotation = self._rotation2 * self._rotation1 return rotation.normalize() @rotation.setter def rotation(self, value): assert isinstance(value, Quaternion) self._rotation1 = value @property def auto_roll(self): """ Whether to rotate the camera automaticall to try and attempt to keep Z up. """ return self._auto_roll @auto_roll.setter def auto_roll(self, value): self._auto_roll = bool(value) @property def keymap(self): """ A dictionary that maps keys to thruster directions The keys in this dictionary are vispy key descriptions (from vispy.keys) or characters that represent keys. These are matched to the "key" attribute of key-press and key-release events. The values are tuples, in which the first element specifies the magnitude of the acceleration, using negative values for "backward" thrust. A value of zero means to brake. The remaining elements specify the dimension to which the acceleration should be applied. These are 1, 2, 3 for forward/backward, left/right, up/down, and 4, 5, 6 for pitch, yaw, roll. """ return self._keymap def _set_range(self, init): """ Reset the view. """ #PerspectiveCamera._set_range(self, init) # Stop moving self._speed *= 0.0 # Get window size (and store factor now to sync with resizing) w, h = self._viewbox.size w, h = float(w), float(h) # Get range and translation for x and y x1, y1, z1 = self._xlim[0], self._ylim[0], self._zlim[0] x2, y2, z2 = self._xlim[1], self._ylim[1], self._zlim[1] rx, ry, rz = (x2 - x1), (y2 - y1), (z2 - z1) # Correct ranges for window size. Note that the window width # influences the x and y data range, while the height influences # the z data range. if w / h > 1: rx /= w / h ry /= w / h else: rz /= h / w # Do not convert to screen coordinates. This camera does not need # to fit everything on screen, but we need to estimate the scale # of the data in the scene. # Set scale, depending on data range. Initial speed is such that # the scene can be traversed in about three seconds. self._scale_factor = max(rx, ry, rz) / 3.0 # Set initial position to a corner of the scene margin = np.mean([rx, ry, rz]) * 0.1 self._center = x1 - margin, y1 - margin, z1 + margin # Determine initial view direction based on flip axis yaw = 45 * self._flip_factors[0] pitch = -90 - 20 * self._flip_factors[2] if self._flip_factors[1] < 0: yaw += 90 * np.sign(self._flip_factors[0]) # Set orientation q1 = Quaternion.create_from_axis_angle(pitch*math.pi/180, 1, 0, 0) q2 = Quaternion.create_from_axis_angle(0*math.pi/180, 0, 1, 0) q3 = Quaternion.create_from_axis_angle(yaw*math.pi/180, 0, 0, 1) # self._rotation1 = (q1 * q2 * q3).normalize() self._rotation2 = Quaternion() # Update self.view_changed() def _get_directions(self): # Get reference points in reference coordinates #p0 = Point(0,0,0) pf = (0, 0, -1) # front pr = (1, 0, 0) # right pl = (-1, 0, 0) # left pu = (0, 1, 0) # up # Get total rotation rotation = self.rotation.inverse() # Transform to real coordinates pf = rotation.rotate_point(pf) pr = rotation.rotate_point(pr) pl = rotation.rotate_point(pl) pu = rotation.rotate_point(pu) def _normalize(p): L = sum(x**2 for x in p) ** 0.5 return np.array(p, 'float64') / L pf = _normalize(pf) pr = _normalize(pr) pl = _normalize(pl) pu = _normalize(pu) return pf, pr, pl, pu def on_timer(self, event): """Timer event handler Parameters ---------- event : instance of Event The event. """ # Set relative speed and acceleration rel_speed = event.dt rel_acc = 0.1 # Get what's forward pf, pr, pl, pu = self._get_directions() # Increase speed through acceleration # Note that self._speed is relative. We can balance rel_acc and # rel_speed to get a nice smooth or direct control self._speed += self._acc * rel_acc # Reduce speed. Simulate resistance. Using brakes slows down faster. # Note that the way that we reduce speed, allows for higher # speeds if keys ar bound to higher acc values (i.e. turbo) reduce = np.array([0.05, 0.05, 0.05, 0.1, 0.1, 0.1]) reduce[self._brake > 0] = 0.2 self._speed -= self._speed * reduce if np.abs(self._speed).max() < 0.05: self._speed *= 0.0 # --- Determine new position from translation speed if self._speed[:3].any(): # Create speed vectors, use scale_factor as a reference dv = np.array([1.0/d for d in self._flip_factors]) # vf = pf * dv * rel_speed * self._scale_factor vr = pr * dv * rel_speed * self._scale_factor vu = pu * dv * rel_speed * self._scale_factor direction = vf, vr, vu # Set position center_loc = np.array(self._center, dtype='float32') center_loc += (self._speed[0] * direction[0] + self._speed[1] * direction[1] + self._speed[2] * direction[2]) self._center = tuple(center_loc) # --- Determine new orientation from rotation speed roll_angle = 0 # Calculate manual roll (from speed) if self._speed[3:].any(): angleGain = np.array([1.0, 1.5, 1.0]) * 3 * math.pi / 180 angles = self._speed[3:] * angleGain q1 = Quaternion.create_from_axis_angle(angles[0], -1, 0, 0) q2 = Quaternion.create_from_axis_angle(angles[1], 0, 1, 0) q3 = Quaternion.create_from_axis_angle(angles[2], 0, 0, -1) q = q1 * q2 * q3 self._rotation1 = (q * self._rotation1).normalize() # Calculate auto-roll if self.auto_roll: up = {'x': (1, 0, 0), 'y': (0, 1, 0), 'z': (0, 0, 1)}[self.up[1]] up = np.array(up) * {'+': +1, '-': -1}[self.up[0]] def angle(p1, p2): return np.arccos(p1.dot(p2)) #au = angle(pu, (0, 0, 1)) ar = angle(pr, up) al = angle(pl, up) af = angle(pf, up) # Roll angle that's off from being leveled (in unit strength) roll_angle = math.sin(0.5*(al - ar)) # Correct for pitch roll_angle *= abs(math.sin(af)) # abs(math.sin(au)) if abs(roll_angle) < 0.05: roll_angle = 0 if roll_angle: # Correct to soften the force at 90 degree angle roll_angle = np.sign(roll_angle) * np.abs(roll_angle)**0.5 # Get correction for this iteration and apply angle_correction = 1.0 * roll_angle * math.pi / 180 q = Quaternion.create_from_axis_angle(angle_correction, 0, 0, 1) self._rotation1 = (q * self._rotation1).normalize() # Update if self._speed.any() or roll_angle or self._update_from_mouse: self._update_from_mouse = False self.view_changed() def viewbox_key_event(self, event): """ViewBox key event handler Parameters ---------- event : instance of Event The event. """ PerspectiveCamera.viewbox_key_event(self, event) if event.handled or not self.interactive: return # Ensure the timer runs if not self._timer.running: self._timer.start() if event.key in self._keymap: val_dims = self._keymap[event.key] val = val_dims[0] # Brake or accelarate? if val == 0: vec = self._brake val = 1 else: vec = self._acc # Set if event.type == 'key_release': val = 0 for dim in val_dims[1:]: factor = 1.0 vec[dim-1] = val * factor def viewbox_mouse_event(self, event): """ViewBox mouse event handler Parameters ---------- event : instance of Event The event. """ PerspectiveCamera.viewbox_mouse_event(self, event) if event.handled or not self.interactive: return if event.type == 'mouse_wheel': if not event.mouse_event.modifiers: # Move forward / backward self._speed[0] += 0.5 * event.delta[1] elif keys.SHIFT in event.mouse_event.modifiers: # Speed s = 1.1 ** - event.delta[1] self.scale_factor /= s # divide instead of multiply print('scale factor: %1.1f units/s' % self.scale_factor) return if event.type == 'mouse_release': # Reset self._event_value = None # Apply rotation self._rotation1 = (self._rotation2 * self._rotation1).normalize() self._rotation2 = Quaternion() elif not self._timer.running: # Ensure the timer runs self._timer.start() if event.type == 'mouse_move': if event.press_event is None: return if not event.buttons: return # Prepare modifiers = event.mouse_event.modifiers pos1 = event.mouse_event.press_event.pos pos2 = event.mouse_event.pos w, h = self._viewbox.size if 1 in event.buttons and not modifiers: # rotate # get normalized delta values d_az = -float(pos2[0] - pos1[0]) / w d_el = +float(pos2[1] - pos1[1]) / h # Apply gain d_az *= - 0.5 * math.pi # * self._speed_rot d_el *= + 0.5 * math.pi # * self._speed_rot # Create temporary quaternions q_az = Quaternion.create_from_axis_angle(d_az, 0, 1, 0) q_el = Quaternion.create_from_axis_angle(d_el, 1, 0, 0) # Apply to global quaternion self._rotation2 = (q_el.normalize() * q_az).normalize() elif 2 in event.buttons and keys.CONTROL in modifiers: # zoom --> fov if self._event_value is None: self._event_value = self._fov p1 = np.array(event.press_event.pos)[:2] p2 = np.array(event.pos)[:2] p1c = event.map_to_canvas(p1)[:2] p2c = event.map_to_canvas(p2)[:2] d = p2c - p1c fov = self._event_value * math.exp(-0.01*d[1]) self._fov = min(90.0, max(10, fov)) # Make transform be updated on the next timer tick. # By doing it at timer tick, we avoid shaky behavior self._update_from_mouse = True def _update_projection_transform(self, fx, fy): PerspectiveCamera._update_projection_transform(self, fx, fy) # Turn our internal quaternion representation into rotation # of our transform axis_angle = self.rotation.get_axis_angle() angle = axis_angle[0] * 180 / math.pi tr = self.transform tr.reset() # tr.rotate(-angle, axis_angle[1:]) tr.scale([1.0/a for a in self._flip_factors]) tr.translate(self._center) #class FirstPersonCamera(PerspectiveCamera): # pass vispy-0.4.0/vispy/scene/cameras/__init__.py0000664000175000017500000000172712527672621022377 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Cameras are responsible for determining which part of a scene is displayed in a viewbox and for handling user input to change the view. Several Camera subclasses are available to customize the projection of the scene such as 3D perspective and orthographic projections, 2D scale/translation, and other specialty cameras. A variety of user interaction styles are available for each camera including arcball, turntable, first-person, and pan/zoom interactions. Internally, Cameras work by setting the transform of a SubScene object such that a certain part of the scene is mapped to the bounding rectangle of the ViewBox. """ from .cameras import (make_camera, BaseCamera, PanZoomCamera, # noqa TurntableCamera, FlyCamera, ArcballCamera) # noqa from .magnify import MagnifyCamera, Magnify1DCamera # noqa vispy-0.4.0/vispy/scene/cameras/magnify.py0000664000175000017500000001262012527672621022264 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from __future__ import division import numpy as np from .cameras import PanZoomCamera from ...visuals.transforms.nonlinear import (MagnifyTransform, Magnify1DTransform) from ...app import Timer class MagnifyCamera(PanZoomCamera): """Camera implementing a MagnifyTransform combined with PanZoomCamera. Parameters ---------- size_factor : float The size factor to use. radius_ratio : float The radius ratio to use. **kwargs : dict Keyword arguments to pass to `PanZoomCamera` and create a transform. Notes ----- This Camera uses the mouse cursor position to set the center position of the MagnifyTransform, and uses mouse wheel events to adjust the magnification factor. At high magnification, very small mouse movements can result in large changes, so we use a timer to animate transitions in the transform properties. The camera also adjusts the size of its "lens" area when the view is resized. """ transform_class = MagnifyTransform def __init__(self, size_factor=0.25, radius_ratio=0.9, **kwargs): # what fraction of the view width to use for radius self.size_factor = size_factor # ratio of inner to outer lens radius self.radius_ratio = radius_ratio # Extract kwargs for panzoom camkwargs = {} for key in ('parent', 'name', 'rect', 'aspect'): if key in kwargs: camkwargs[key] = kwargs.pop(key) # Create the mag transform - kwrds go here self.mag = self.transform_class(**kwargs) # for handling smooth transitions self.mag_target = self.mag.mag self.mag._mag = self.mag_target self.mouse_pos = None self.timer = Timer(interval=0.016, connect=self.on_timer) super(MagnifyCamera, self).__init__(**camkwargs) # This tells the camera to insert the magnification transform at the # beginning of the transform it applies to the scene. This is the # correct place for the mag transform because: # 1. We want it to apply to everything inside the scene, and not to # the ViewBox itself or anything outside of the ViewBox. # 2. We do _not_ want the pan/zoom transforms applied first, because # the scale factors implemented there should not change the shape # of the lens. self.pre_transform = self.mag def _viewbox_set(self, viewbox): PanZoomCamera._viewbox_set(self, viewbox) def _viewbox_unset(self, viewbox): PanZoomCamera._viewbox_unset(self, viewbox) self.timer.stop() def viewbox_mouse_event(self, event): """ViewBox mouse event handler Parameters ---------- event : instance of Event The mouse event. """ # When the attached ViewBox reseives a mouse event, it is sent to the # camera here. self.mouse_pos = event.pos[:2] if event.type == 'mouse_wheel': # wheel rolled; adjust the magnification factor and hide the # event from the superclass m = self.mag_target m *= 1.2 ** event.delta[1] m = m if m > 1 else 1 self.mag_target = m else: # send everything _except_ wheel events to the superclass super(MagnifyCamera, self).viewbox_mouse_event(event) # start the timer to smoothly modify the transform properties. if not self.timer.running: self.timer.start() self._update_transform() def on_timer(self, event=None): """Timer event handler Parameters ---------- event : instance of Event The timer event. """ # Smoothly update center and magnification properties of the transform k = np.clip(100. / self.mag.mag, 10, 100) s = 10**(-k * event.dt) c = np.array(self.mag.center) c1 = c * s + self.mouse_pos * (1-s) m = self.mag.mag * s + self.mag_target * (1-s) # If changes are very small, then it is safe to stop the timer. if (np.all(np.abs((c - c1) / c1) < 1e-5) and (np.abs(np.log(m / self.mag.mag)) < 1e-3)): self.timer.stop() self.mag.center = c1 self.mag.mag = m self._update_transform() def viewbox_resize_event(self, event): """ViewBox resize event handler Parameters ---------- event : instance of Event The viewbox resize event. """ PanZoomCamera.viewbox_resize_event(self, event) self.view_changed() def view_changed(self): # make sure radii are updated when a view is attached. # when the view resizes, we change the lens radii to match. if self._viewbox is not None: vbs = self._viewbox.size r = min(vbs) * self.size_factor self.mag.radii = r * self.radius_ratio, r PanZoomCamera.view_changed(self) class Magnify1DCamera(MagnifyCamera): transform_class = Magnify1DTransform __doc__ = MagnifyCamera.__doc__ vispy-0.4.0/vispy/scene/events.py0000664000175000017500000003307512527672621020532 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from __future__ import division from ..util.event import Event from ..visuals.transforms import TransformCache, TransformSystem class SceneEvent(Event, TransformSystem): """ SceneEvent is an Event that tracks its path through a scenegraph, beginning at a Canvas. It exposes information useful during drawing and user interaction. """ def __init__(self, type, canvas, transform_cache=None): Event.__init__(self, type=type) # Note that we are completely replacing the TransformSystem.__init__ # implementation. self._canvas = canvas self._dpi = canvas.dpi # Init stacks self._stack = [] # list of entities self._stack_ids = set() self._viewbox_stack = [] self._doc_stack = [] self._handled_children = [] if transform_cache is None: transform_cache = TransformCache() self._transform_cache = transform_cache @property def handled_children(self): """ List of children of the current node that have already been handled. Nodes that manually process their children (as opposed to allowing drawing / mouse systems to handle them automatically) may append nodes to this list to prevent systems from handling them. """ return self._handled_children @property def canvas(self): """ The Canvas that originated this SceneEvent """ return self._canvas @property def viewbox(self): """ The current viewbox. """ if len(self._viewbox_stack) > 0: return self._viewbox_stack[-1] else: return None @property def path(self): """ The path of Entities leading from the root SubScene to the current recipient of this Event. """ return self._stack def push_node(self, node): """ Push an node on the stack. """ self._stack.append(node) self._handled_children.append([]) if id(node) in self._stack_ids: raise RuntimeError("Scenegraph cycle detected; cannot push %r" % node) self._stack_ids.add(id(node)) doc = node.document if doc is not None: self.push_document(doc) def pop_node(self): """ Pop an node from the stack. """ ent = self._stack.pop(-1) self._handled_children.pop(-1) self._stack_ids.remove(id(ent)) if ent.document is not None: assert ent.document == self.pop_document() return ent def push_viewbox(self, viewbox): self._viewbox_stack.append(viewbox) def pop_viewbox(self): return self._viewbox_stack.pop(-1) def push_document(self, doc): self._doc_stack.append(doc) def pop_document(self): return self._doc_stack.pop(-1) def push_viewport(self, viewport): """ Push a viewport (x, y, w, h) on the stack. It is the responsibility of the caller to ensure the given values are int. The viewport's origin is defined relative to the current viewport. """ self.canvas.push_viewport(viewport) def pop_viewport(self): """ Pop a viewport from the stack. """ return self.canvas.pop_viewport() def push_fbo(self, viewport, fbo, transform): """ Push an FBO on the stack, together with the new viewport. and the transform to the FBO. """ self.canvas.push_fbo(viewport, fbo, transform) def pop_fbo(self): """ Pop an FBO from the stack. """ return self.canvas.pop_fbo() # # Begin transforms # @property def document_cs(self): """ The node for the current document coordinate system. The coordinate system of this Node is used for making physical measurements--px, mm, in, etc. """ if len(self._doc_stack) > 0: return self._doc_stack[-1] else: return self.canvas_cs @property def canvas_cs(self): """ The node for the current canvas coordinate system. This cs represents the logical pixels of the canvas being drawn, with the origin in upper-left, and the canvas (width, height) in the bottom right. This coordinate system is most often used for handling mouse input. """ return self.canvas.canvas_cs @property def framebuffer_cs(self): """ The node for the current framebuffer coordinate system. This coordinate system corresponds to the physical pixels being rendered to, with the origin in lower-right, and the framebufer (width, height) in upper-left. It is used mainly for making antialiasing measurements. """ return self.canvas.framebuffer_cs @property def render_cs(self): """ Return node for the normalized device coordinate system. This coordinate system is the obligatory output of GLSL vertex shaders, with (-1, -1) in bottom-left, and (1, 1) in top-right. This coordinate system is frequently used for rendering visuals because all vertices must ultimately be mapped here. """ return self.canvas.render_cs @property def node_cs(self): """ The node at the top of the node stack. """ return self._stack[-1] @property def visual_to_canvas(self): """ Transform mapping from visual local coordinate frame to canvas coordinate frame. """ return self.node_transform(map_to=self.canvas_cs, map_from=self._stack[-1]) @property def visual_to_document(self): """ Transform mapping from visual local coordinate frame to document coordinate frame. """ return self.node_transform(map_to=self.document_cs, map_from=self._stack[-1]) @visual_to_document.setter def visual_to_document(self, tr): raise RuntimeError("Cannot set transforms on SceneEvent.") @property def document_to_framebuffer(self): """ Transform mapping from document coordinate frame to the framebuffer (physical pixel) coordinate frame. """ return self.node_transform(map_to=self.framebuffer_cs, map_from=self.document_cs) @document_to_framebuffer.setter def document_to_framebuffer(self, tr): raise RuntimeError("Cannot set transforms on SceneEvent.") @property def framebuffer_to_render(self): """ Transform mapping from pixel coordinate frame to rendering coordinate frame. """ return self.node_transform(map_to=self.render_cs, map_from=self.framebuffer_cs) @framebuffer_to_render.setter def framebuffer_to_render(self, tr): raise RuntimeError("Cannot set transforms on SceneEvent.") @property def visual_to_framebuffer(self): """ Transform mapping from visual coordinate frame to the framebuffer (physical pixel) coordinate frame. """ return self.node_transform(map_to=self.framebuffer_cs, map_from=self._stack[-1]) def get_full_transform(self): """ Return the transform that maps from the current node to normalized device coordinates within the current glViewport and FBO. This transform consists of the full_transform prepended by a correction for the current glViewport and/or FBO. Most entities will use this transform when drawing. """ return self._transform_cache.get([e.transform for e in self._stack]) @property def scene_transform(self): """ The transform that maps from the current node to the first scene in its ancestry. """ if len(self._viewbox_stack) > 1: view = self._viewbox_stack[-1] return self.node_transform(map_to=view.scene) else: return None @property def view_transform(self): """ The transform that maps from the current node to the first viewbox in its ancestry. """ if len(self._viewbox_stack) > 1: view = self._viewbox_stack[-1] return self.node_transform(map_to=view) else: return None def node_transform(self, map_to=None, map_from=None): """ Return the transform from *map_from* to *map_to*, using the current node stack to resolve parent ambiguities if needed. By default, *map_to* is the normalized device coordinate system, and *map_from* is the current top node on the stack. """ if map_to is None: map_to = self.render_cs if map_from is None: map_from = self._stack[-1] fwd_path = self._node_path(map_from, map_to) if fwd_path[-1] is map_to: fwd_path = fwd_path[:-1] rev_path = [] else: # If we have still not reached the end, try traversing from the # opposite end. Note the reversed order of start and end rev_path = self._node_path(start=map_to, end=map_from) if rev_path[-1] is map_from: fwd_path = [] rev_path = rev_path[:-1] else: # Find earliest intersection of fwd and rev paths connected = False while fwd_path[-1] is rev_path[-1]: connected = True fwd_path = fwd_path[:-1] rev_path = rev_path[:-1] if not connected: raise RuntimeError("Unable to find unique path from %r to " "%r" % (map_from, map_to)) # starting node must come _last_ in the transform chain fwd_path = fwd_path[::-1] transforms = ([e.transform.inverse for e in rev_path] + [e.transform for e in fwd_path]) return self._transform_cache.get(transforms) def _node_path(self, start, end): """ Return the path of parents leading from *start* to *end*, using the node stack to resolve multi-parent branches. If *end* is never reached, then the path is assembled as far as possible and returned. """ path = [start] # first, get parents directly from node node = start while id(node) not in self._stack_ids: if node is end or len(node.parents) != 1: return path node = node.parent path.append(node) # if we have not reached the end, follow _stack if possible. if path[-1] is not end: try: ind = self._stack.index(node) # copy stack onto path one node at a time while ind > 0 and path[-1] is not end: ind -= 1 path.append(self._stack[ind]) except IndexError: pass return path class SceneDrawEvent(SceneEvent): def __init__(self, event, canvas, **kwargs): self.draw_event = event super(SceneDrawEvent, self).__init__(type='draw', canvas=canvas, **kwargs) class SceneMouseEvent(SceneEvent): """ Represents a mouse event that occurred on a SceneCanvas. This event is delivered to all entities whose mouse interaction area is under the event. """ def __init__(self, event, canvas, **kwargs): self.mouse_event = event super(SceneMouseEvent, self).__init__(type=event.type, canvas=canvas, **kwargs) @property def pos(self): """ The position of this event in the local coordinate system of the visual. """ return self.map_from_canvas(self.mouse_event.pos) @property def last_event(self): """ The mouse event immediately prior to this one. This property is None when no mouse buttons are pressed. """ if self.mouse_event.last_event is None: return None ev = self.copy() ev.mouse_event = self.mouse_event.last_event return ev @property def press_event(self): """ The mouse press event that initiated a mouse drag, if any. """ if self.mouse_event.press_event is None: return None ev = self.copy() ev.mouse_event = self.mouse_event.press_event return ev @property def button(self): """ The button pressed or released on this event. """ return self.mouse_event.button @property def buttons(self): """ A list of all buttons currently pressed on the mouse. """ return self.mouse_event.buttons @property def delta(self): """ The increment by which the mouse wheel has moved. """ return self.mouse_event.delta def copy(self): ev = self.__class__(self.mouse_event, self._canvas) ev._stack = self._stack[:] # ev._ra_stack = self._ra_stack[:] ev._viewbox_stack = self._viewbox_stack[:] return ev def map_to_canvas(self, obj): tr = self.node_transform(map_from=self.node_cs, map_to=self.canvas_cs) return tr.map(obj) def map_from_canvas(self, obj): tr = self.node_transform(map_from=self.canvas_cs, map_to=self.node_cs) return tr.map(obj) vispy-0.4.0/vispy/scene/__init__.py0000664000175000017500000000267512527672621020767 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ The vispy.scene subpackage provides high-level, flexible, and easy to use functionality for creating scenes composed of multiple visual objects. Overview -------- Scenegraphs are a commonly used system for describing a scene as a hierarchy of visual objects. Users need only create these visual objects and specify their location in the scene, and the scenegraph system will automatically draw the entire scene whenever an update is required. Using the vispy scenegraph requires only a few steps: 1. Create a SceneCanvas to display the scene. This object has a `scene` property that is the top-level Node in the scene. 2. Create one or more Node instances (see vispy.scene.visuals) 3. Add these Node instances to the scene by making them children of canvas.scene, or children of other nodes that are already in the scene. For more information see: * complete scenegraph documentation * scene examples * scene API reference """ __all__ = ['SceneCanvas', 'Node'] from .visuals import * # noqa from .cameras import * # noqa from ..visuals.transforms import * # noqa from .widgets import * # noqa from .canvas import SceneCanvas # noqa from . import visuals # noqa from ..visuals import transforms # noqa from . import widgets # noqa from . import cameras # noqa from .node import Node # noqa vispy-0.4.0/vispy/scene/visuals.py0000664000175000017500000001137212527672621020710 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ The classes in scene.visuals are visuals that may be added to a scenegraph using the methods and properties defined in `vispy.scene.Node` such as name, visible, parent, children, etc... These classes are automatically generated by mixing `vispy.scene.Node` with the Visual classes found in `vispy.visuals`. For developing custom visuals, it is recommended to subclass from `vispy.visuals.Visual` rather than `vispy.scene.Node`. """ import re from .. import visuals from .node import Node def create_visual_node(subclass): # Create a new subclass of Node. # Decide on new class name clsname = subclass.__name__ assert clsname.endswith('Visual') clsname = clsname[:-6] # Generate new docstring based on visual docstring try: doc = generate_docstring(subclass, clsname) except Exception: # If parsing fails, just return the original Visual docstring doc = subclass.__doc__ # New __init__ method def __init__(self, *args, **kwargs): parent = kwargs.pop('parent', None) name = kwargs.pop('name', None) self.name = name # to allow __str__ before Node.__init__ subclass.__init__(self, *args, **kwargs) Node.__init__(self, parent=parent, name=name) # Create new class cls = type(clsname, (subclass, Node), {'__init__': __init__, '__doc__': doc}) return cls def generate_docstring(subclass, clsname): # Generate a Visual+Node docstring by modifying the Visual's docstring # to include information about Node inheritance and extra init args. sc_doc = subclass.__doc__ if sc_doc is None: sc_doc = "" # find locations within docstring to insert new parameters lines = sc_doc.split("\n") # discard blank lines at start while lines and lines[0].strip() == '': lines.pop(0) i = 0 params_started = False param_indent = None first_blank = None param_end = None while i < len(lines): line = lines[i] # ignore blank lines and '------' lines if re.search(r'\w', line): indent = len(line) - len(line.lstrip()) # If Params section has already started, check for end of params # (that is where we will insert new params) if params_started: if indent < param_indent: break elif indent == param_indent: # might be end of parameters block.. if re.match(r'\s*[a-zA-Z0-9_]+\s*:\s*\S+', line) is None: break param_end = i + 1 # Check for beginning of params section elif re.match(r'\s*Parameters\s*', line): params_started = True param_indent = indent if first_blank is None: first_blank = i # Check for first blank line # (this is where the Node inheritance description will be # inserted) elif first_blank is None and line.strip() == '': first_blank = i i += 1 if i == len(lines) and param_end is None: # reached end of docstring; insert here param_end = i # If original docstring has no params heading, we need to generate it. if not params_started: lines.extend(["", " Parameters", " ----------"]) param_end = len(lines) if first_blank is None: first_blank = param_end - 3 params_started = True # build class and parameter description strings class_desc = ("\n This class inherits from visuals.%sVisual and " "scene.Node, allowing the visual to be placed inside a " "scenegraph.\n" % (clsname)) parm_doc = (" parent : Node\n" " The parent node to assign to this node (optional).\n" " name : string\n" " A name for this node, used primarily for debugging\n" " (optional).") # assemble all docstring parts lines = (lines[:first_blank] + [class_desc] + lines[first_blank:param_end] + [parm_doc] + lines[param_end:]) doc = '\n'.join(lines) return doc __all__ = [] for obj_name in dir(visuals): obj = getattr(visuals, obj_name) if (isinstance(obj, type) and issubclass(obj, visuals.Visual) and obj is not visuals.Visual): cls = create_visual_node(obj) globals()[cls.__name__] = cls __all__.append(cls.__name__) vispy-0.4.0/vispy/scene/tests/0000775000175000017500000000000012527674621020010 5ustar larsonerlarsoner00000000000000vispy-0.4.0/vispy/scene/tests/test_node.py0000664000175000017500000000162112510536123022330 0ustar larsonerlarsoner00000000000000from vispy.scene.node import Node from vispy.testing import run_tests_if_main def test_graph(): # Graph looks like: # # a --- b --- c --- d --- g # \ / # --- e --- f # a = Node(name='a') b = Node(name='b', parent=a) c = Node(name='c', parent=b) d = Node(name='d', parent=c) e = Node(name='e', parent=b) f = Node(name='f', parent=e) g = Node(name='g', ) g.parents = (f, d) assert a.parent is None assert b.node_path(a) == ([b, a], []) assert a.node_path(b) == ([a], [b]) assert c.node_path(a) == ([c, b, a], []) assert a.node_path(c) == ([a], [b, c]) assert d.node_path(f) == ([d, c, b], [e, f]) assert f.node_path(d) == ([f, e, b], [c, d]) try: g.node_path(b) raise Exception("Should have raised RuntimeError") except RuntimeError: pass run_tests_if_main() vispy-0.4.0/vispy/scene/tests/__init__.py0000664000175000017500000000000012375431476022107 0ustar larsonerlarsoner00000000000000vispy-0.4.0/vispy/scene/tests/test_visuals.py0000664000175000017500000000167312426461252023106 0ustar larsonerlarsoner00000000000000from vispy.scene import visuals, Node import vispy.visuals def test_docstrings(): # test that docstring insertions worked for all Visual+Node subclasses for name in dir(visuals): obj = getattr(visuals, name) if isinstance(obj, type) and issubclass(obj, Node): if obj is Node: continue assert "This class inherits from visuals." in obj.__doc__ assert "parent : Node" in obj.__doc__ def test_visual_node_generation(): # test that all Visual classes also have Visual+Node classes visuals = [] for name in dir(vispy.visuals): obj = getattr(vispy.visuals, name) if isinstance(obj, type) and issubclass(obj, Node): if obj is Node: continue assert name.endswith('Visual') vis_node = getattr(visuals, name[:-6]) assert issubclass(vis_node, Node) assert issubclass(vis_node, obj) vispy-0.4.0/vispy/scene/subscene.py0000664000175000017500000000377012527672621021034 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from __future__ import division from .node import Node from .systems import DrawingSystem, MouseInputSystem class SubScene(Node): """ A subscene with entities. A subscene can be a child of a Canvas or a ViewBox. It is a placeholder for the transform to make the sub scene appear as intended. It's transform cannot be mannually set, but its set automatically and is based on three components: * Viewbox transform: a scale and translation to make the subscene be displayed in the boundaries of the viewbox. * Projection transform: the camera projection, e.g. perspective * position transform: the inverse of the transform from this subscene to the camera. This takes care of position and orientation of the view. TODO: should camera, lights, etc. be properties of the subscene or the viewbox? I think of the scene. In that way canvas.scene can simply be a subscene. """ def __init__(self, **kwargs): Node.__init__(self, **kwargs) # Initialize systems self._systems = {} self._systems['draw'] = DrawingSystem() self._systems['mouse'] = MouseInputSystem() self._drawing = False def draw(self, event): # Temporary workaround to avoid infinite recursion. A better solution # would be for ViewBox and Canvas to handle the systems, rather than # subscene. if self._drawing: return # Invoke our drawing system try: self._drawing = True self.process_system(event, 'draw') finally: self._drawing = False def _process_mouse_event(self, event): self.process_system(event, 'mouse') def process_system(self, event, system_name): """ Process a system. """ self._systems[system_name].process(event, self) vispy-0.4.0/vispy/scene/widgets/0000775000175000017500000000000012527674621020314 5ustar larsonerlarsoner00000000000000vispy-0.4.0/vispy/scene/widgets/anchor.py0000664000175000017500000000155212426461252022133 0ustar larsonerlarsoner00000000000000from ..node import Node class Anchor(Node): """ Anchor is a node derives parts of its transform from some other coordinate system in the scene. The purpose is to allow children of an Anchor to draw using a position (and optionally rotation) specified by one coordinate system, and scaling/ projection specified by another. For example, text attached to a point in a 3D scene should be drawn in a coordinate system with a simple relationship to the screen pixels, but should derive its location from a position within the 3D coordinate system:: root = Box() view = ViewBox(parent=box) plot = LineVisual(parent=ViewBox) anchor = Anchor(parent=root, anchor_to=plot, anchor_pos=(10, 0)) text = Text(parent=anchor, text="Always points to (10,0) relative to line.") """ vispy-0.4.0/vispy/scene/widgets/widget.py0000664000175000017500000002064012527672621022151 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from __future__ import division import numpy as np from ..node import Node from ...visuals.mesh import MeshVisual from ...visuals.transforms import STTransform from ...util.event import Event from ...geometry import Rect from ...color import Color class Widget(Node): """ A widget takes up a rectangular space, intended for use in a 2D pixel coordinate frame. The widget is positioned using the transform attribute (as any node), and its extent (size) is kept as a separate property. Parameters ---------- pos : (x, y) A 2-element tuple to specify the top left corner of the widget. size : (w, h) A 2-element tuple to spicify the size of the widget. border_color : color The color of the border. bgcolor : color The background color. clip : bool Not used :) padding : int The amount of padding in the widget (i.e. the space reserved between the contents and the border). margin : int The margin to keep outside the widget's border. """ def __init__(self, pos=(0, 0), size=(10, 10), border_color=None, bgcolor=None, clip=False, padding=0, margin=0, **kwargs): Node.__init__(self, **kwargs) # For drawing border. # A mesh is required because GL lines cannot be drawn with predictable # shape across all platforms. self._border_color = self._bgcolor = Color(None) self._face_colors = None self._visual = MeshVisual(mode='triangles') self._visual.set_gl_state('translucent', depth_test=False) # whether this widget should clip its children # (todo) self._clip = clip # reserved space inside border self._padding = padding # reserved space outside border self._margin = margin self.events.add(resize=Event) self._size = 16, 16 self.transform = STTransform() # todo: TTransform (translate only for widgets) self._widgets = [] self.pos = pos self.size = size self.border_color = border_color self.bgcolor = bgcolor @property def pos(self): return tuple(self.transform.translate[:2]) @pos.setter def pos(self, p): assert isinstance(p, tuple) assert len(p) == 2 if p == self.pos: return self.transform.translate = p[0], p[1], 0, 0 self._update_line() #self.events.resize() @property def size(self): # Note that we cannot let the size be reflected in the transform. # Consider a widget of 40x40 in a pixel grid, a child widget therin # with size 20x20 would get a scale of 800x800! return self._size @size.setter def size(self, s): assert isinstance(s, tuple) assert len(s) == 2 if self._size == s: return self._size = s self._update_line() self.events.resize() self._update_child_widgets() @property def rect(self): return Rect((0, 0), self.size) @rect.setter def rect(self, r): with self.events.resize.blocker(): self.pos = r.pos self.size = r.size self.update() self.events.resize() @property def inner_rect(self): """The rectangular area inside the margin, border and padding. Generally widgets should avoid drawing or placing widgets outside this rectangle. """ m = self.margin + self.padding if not self.border_color.is_blank: m += 1 return Rect((m, m), (self.size[0]-2*m, self.size[1]-2*m)) @property def border_color(self): """ The color of the border. """ return self._border_color @border_color.setter def border_color(self, b): self._border_color = Color(b) self._update_colors() self._update_line() self.update() @property def bgcolor(self): """ The background color of the Widget. """ return self._bgcolor @bgcolor.setter def bgcolor(self, value): self._bgcolor = Color(value) self._update_colors() self._update_line() self.update() @property def margin(self): return self._margin @margin.setter def margin(self, m): self._margin = m self._update_line() @property def padding(self): return self._padding @padding.setter def padding(self, p): self._padding = p self._update_child_widgets() def _update_line(self): """ Update border line to match new shape """ w = 1 # XXX Eventually this can be a parameter m = int(self.margin) # border is drawn within the boundaries of the widget: # # size = (8, 7) margin=2 # internal rect = (3, 3, 2, 1) # ........ # ........ # ..BBBB.. # ..B B.. # ..BBBB.. # ........ # ........ # l = b = m r = int(self.size[0]) - m t = int(self.size[1]) - m pos = np.array([ [l, b], [l+w, b+w], [r, b], [r-w, b+w], [r, t], [r-w, t-w], [l, t], [l+w, t-w], ], dtype=np.float32) faces = np.array([ [0, 2, 1], [1, 2, 3], [2, 4, 3], [3, 5, 4], [4, 5, 6], [5, 7, 6], [6, 0, 7], [7, 0, 1], [5, 3, 1], [1, 5, 7], ], dtype=np.int32) start = 8 if self._border_color.is_blank else 0 stop = 8 if self._bgcolor.is_blank else 10 face_colors = None if self._face_colors is not None: face_colors = self._face_colors[start:stop] self._visual.set_data(vertices=pos, faces=faces[start:stop], face_colors=face_colors) def _update_colors(self): self._face_colors = np.concatenate( (np.tile(self.border_color.rgba, (8, 1)), np.tile(self.bgcolor.rgba, (2, 1)))).astype(np.float32) def draw(self, event): """Draw the widget borders Parameters ---------- event : instance of Event The event containing the transforms. """ if self.border_color.is_blank and self.bgcolor.is_blank: return self._visual.draw(event) def on_resize(self, event): """On resize handler Parameters ---------- event : instance of Event The resize event. """ self._update_child_widgets() def _update_child_widgets(self): # Set the position and size of child boxes (only those added # using add_widget) for ch in self._widgets: ch.rect = self.rect.padded(self.padding + self.margin) def add_widget(self, widget): """ Add a Widget as a managed child of this Widget. The child will be automatically positioned and sized to fill the entire space inside this Widget (unless _update_child_widgets is redefined). Parameters ---------- widget : instance of Widget The widget to add. Returns ------- widget : instance of Widget The widget. """ self._widgets.append(widget) widget.parent = self self._update_child_widgets() return widget def add_grid(self, *args, **kwargs): """ Create a new Grid and add it as a child widget. All arguments are given to Grid(). """ from .grid import Grid grid = Grid(*args, **kwargs) return self.add_widget(grid) def add_view(self, *args, **kwargs): """ Create a new ViewBox and add it as a child widget. All arguments are given to ViewBox(). """ from .viewbox import ViewBox view = ViewBox(*args, **kwargs) return self.add_widget(view) def remove_widget(self, widget): """ Remove a Widget as a managed child of this Widget. Parameters ---------- widget : instance of Widget The widget to remove. """ self._widgets.remove(widget) widget.remove_parent(self) self._update_child_widgets() vispy-0.4.0/vispy/scene/widgets/console.py0000664000175000017500000003072312527672621022333 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2015, Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Fast and failsafe GL console """ # Code translated from glumpy import numpy as np from ...visuals.shaders import ModularProgram from .widget import Widget from ...gloo import VertexBuffer, set_state from ...color import Color from ...ext.six import string_types # Translated from # http://www.piclist.com/tecHREF/datafile/charset/ # extractor/charset_extractor.htm __font_6x8__ = np.array([ (0x00, 0x00, 0x00, 0x00, 0x00, 0x00), (0x10, 0xE3, 0x84, 0x10, 0x01, 0x00), (0x6D, 0xB4, 0x80, 0x00, 0x00, 0x00), (0x00, 0xA7, 0xCA, 0x29, 0xF2, 0x80), (0x20, 0xE4, 0x0C, 0x09, 0xC1, 0x00), (0x65, 0x90, 0x84, 0x21, 0x34, 0xC0), (0x21, 0x45, 0x08, 0x55, 0x23, 0x40), (0x30, 0xC2, 0x00, 0x00, 0x00, 0x00), (0x10, 0x82, 0x08, 0x20, 0x81, 0x00), (0x20, 0x41, 0x04, 0x10, 0x42, 0x00), (0x00, 0xA3, 0x9F, 0x38, 0xA0, 0x00), (0x00, 0x41, 0x1F, 0x10, 0x40, 0x00), (0x00, 0x00, 0x00, 0x00, 0xC3, 0x08), (0x00, 0x00, 0x1F, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00, 0xC3, 0x00), (0x00, 0x10, 0x84, 0x21, 0x00, 0x00), (0x39, 0x14, 0xD5, 0x65, 0x13, 0x80), (0x10, 0xC1, 0x04, 0x10, 0x43, 0x80), (0x39, 0x10, 0x46, 0x21, 0x07, 0xC0), (0x39, 0x10, 0x4E, 0x05, 0x13, 0x80), (0x08, 0x62, 0x92, 0x7C, 0x20, 0x80), (0x7D, 0x04, 0x1E, 0x05, 0x13, 0x80), (0x18, 0x84, 0x1E, 0x45, 0x13, 0x80), (0x7C, 0x10, 0x84, 0x20, 0x82, 0x00), (0x39, 0x14, 0x4E, 0x45, 0x13, 0x80), (0x39, 0x14, 0x4F, 0x04, 0x23, 0x00), (0x00, 0x03, 0x0C, 0x00, 0xC3, 0x00), (0x00, 0x03, 0x0C, 0x00, 0xC3, 0x08), (0x08, 0x42, 0x10, 0x20, 0x40, 0x80), (0x00, 0x07, 0xC0, 0x01, 0xF0, 0x00), (0x20, 0x40, 0x81, 0x08, 0x42, 0x00), (0x39, 0x10, 0x46, 0x10, 0x01, 0x00), (0x39, 0x15, 0xD5, 0x5D, 0x03, 0x80), (0x39, 0x14, 0x51, 0x7D, 0x14, 0x40), (0x79, 0x14, 0x5E, 0x45, 0x17, 0x80), (0x39, 0x14, 0x10, 0x41, 0x13, 0x80), (0x79, 0x14, 0x51, 0x45, 0x17, 0x80), (0x7D, 0x04, 0x1E, 0x41, 0x07, 0xC0), (0x7D, 0x04, 0x1E, 0x41, 0x04, 0x00), (0x39, 0x14, 0x17, 0x45, 0x13, 0xC0), (0x45, 0x14, 0x5F, 0x45, 0x14, 0x40), (0x38, 0x41, 0x04, 0x10, 0x43, 0x80), (0x04, 0x10, 0x41, 0x45, 0x13, 0x80), (0x45, 0x25, 0x18, 0x51, 0x24, 0x40), (0x41, 0x04, 0x10, 0x41, 0x07, 0xC0), (0x45, 0xB5, 0x51, 0x45, 0x14, 0x40), (0x45, 0x95, 0x53, 0x45, 0x14, 0x40), (0x39, 0x14, 0x51, 0x45, 0x13, 0x80), (0x79, 0x14, 0x5E, 0x41, 0x04, 0x00), (0x39, 0x14, 0x51, 0x55, 0x23, 0x40), (0x79, 0x14, 0x5E, 0x49, 0x14, 0x40), (0x39, 0x14, 0x0E, 0x05, 0x13, 0x80), (0x7C, 0x41, 0x04, 0x10, 0x41, 0x00), (0x45, 0x14, 0x51, 0x45, 0x13, 0x80), (0x45, 0x14, 0x51, 0x44, 0xA1, 0x00), (0x45, 0x15, 0x55, 0x55, 0x52, 0x80), (0x45, 0x12, 0x84, 0x29, 0x14, 0x40), (0x45, 0x14, 0x4A, 0x10, 0x41, 0x00), (0x78, 0x21, 0x08, 0x41, 0x07, 0x80), (0x38, 0x82, 0x08, 0x20, 0x83, 0x80), (0x01, 0x02, 0x04, 0x08, 0x10, 0x00), (0x38, 0x20, 0x82, 0x08, 0x23, 0x80), (0x10, 0xA4, 0x40, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00, 0x00, 0x3F), (0x30, 0xC1, 0x00, 0x00, 0x00, 0x00), (0x00, 0x03, 0x81, 0x3D, 0x13, 0xC0), (0x41, 0x07, 0x91, 0x45, 0x17, 0x80), (0x00, 0x03, 0x91, 0x41, 0x13, 0x80), (0x04, 0x13, 0xD1, 0x45, 0x13, 0xC0), (0x00, 0x03, 0x91, 0x79, 0x03, 0x80), (0x18, 0x82, 0x1E, 0x20, 0x82, 0x00), (0x00, 0x03, 0xD1, 0x44, 0xF0, 0x4E), (0x41, 0x07, 0x12, 0x49, 0x24, 0x80), (0x10, 0x01, 0x04, 0x10, 0x41, 0x80), (0x08, 0x01, 0x82, 0x08, 0x24, 0x8C), (0x41, 0x04, 0x94, 0x61, 0x44, 0x80), (0x10, 0x41, 0x04, 0x10, 0x41, 0x80), (0x00, 0x06, 0x95, 0x55, 0x14, 0x40), (0x00, 0x07, 0x12, 0x49, 0x24, 0x80), (0x00, 0x03, 0x91, 0x45, 0x13, 0x80), (0x00, 0x07, 0x91, 0x45, 0x17, 0x90), (0x00, 0x03, 0xD1, 0x45, 0x13, 0xC1), (0x00, 0x05, 0x89, 0x20, 0x87, 0x00), (0x00, 0x03, 0x90, 0x38, 0x13, 0x80), (0x00, 0x87, 0x88, 0x20, 0xA1, 0x00), (0x00, 0x04, 0x92, 0x49, 0x62, 0x80), (0x00, 0x04, 0x51, 0x44, 0xA1, 0x00), (0x00, 0x04, 0x51, 0x55, 0xF2, 0x80), (0x00, 0x04, 0x92, 0x31, 0x24, 0x80), (0x00, 0x04, 0x92, 0x48, 0xE1, 0x18), (0x00, 0x07, 0x82, 0x31, 0x07, 0x80), (0x18, 0x82, 0x18, 0x20, 0x81, 0x80), (0x10, 0x41, 0x00, 0x10, 0x41, 0x00), (0x30, 0x20, 0x83, 0x08, 0x23, 0x00), (0x29, 0x40, 0x00, 0x00, 0x00, 0x00), (0x10, 0xE6, 0xD1, 0x45, 0xF0, 0x00) ], dtype=np.float32) VERTEX_SHADER = """ uniform vec2 u_logical_scale; uniform float u_physical_scale; uniform vec4 u_color; uniform vec4 u_origin; attribute vec2 a_position; attribute vec3 a_bytes_012; attribute vec3 a_bytes_345; varying vec4 v_color; varying vec3 v_bytes_012, v_bytes_345; void main (void) { gl_Position = u_origin + vec4(a_position * u_logical_scale, 0., 0.); gl_PointSize = 8.0 * u_physical_scale; v_color = u_color; v_bytes_012 = a_bytes_012; v_bytes_345 = a_bytes_345; } """ FRAGMENT_SHADER = """ float segment(float edge0, float edge1, float x) { return step(edge0,x) * (1.0-step(edge1,x)); } varying vec4 v_color; varying vec3 v_bytes_012, v_bytes_345; vec4 glyph_color(vec2 uv) { if(uv.x > 5.0 || uv.y > 7.0) return vec4(0, 0, 0, 0); else { float index = floor( (uv.y*6.0+uv.x)/8.0 ); float offset = floor( mod(uv.y*6.0+uv.x,8.0)); float byte = segment(0.0,1.0,index) * v_bytes_012.x + segment(1.0,2.0,index) * v_bytes_012.y + segment(2.0,3.0,index) * v_bytes_012.z + segment(3.0,4.0,index) * v_bytes_345.x + segment(4.0,5.0,index) * v_bytes_345.y + segment(5.0,6.0,index) * v_bytes_345.z; if( floor(mod(byte / (128.0/pow(2.0,offset)), 2.0)) > 0.0 ) return v_color; else return vec4(0, 0, 0, 0); } } void main(void) { vec2 loc = gl_PointCoord.xy * 8.0; vec2 uv = floor(loc); // use multi-sampling to make the text look nicer vec2 dxy = 0.25*(abs(dFdx(loc)) + abs(dFdy(loc))); vec4 box = floor(vec4(loc-dxy, loc+dxy)); vec4 color = glyph_color(floor(loc)) + 0.25 * glyph_color(box.xy) + 0.25 * glyph_color(box.xw) + 0.25 * glyph_color(box.zy) + 0.25 * glyph_color(box.zw); gl_FragColor = color / 2.; } """ class Console(Widget): """Fast and failsafe text console Parameters ---------- text_color : instance of Color Color to use. font_size : float Point size to use. """ def __init__(self, text_color='black', font_size=12., **kwargs): # Harcoded because of font above and shader program self.text_color = text_color self.font_size = font_size self._char_width = 6 self._char_height = 10 self._program = ModularProgram(VERTEX_SHADER, FRAGMENT_SHADER) self._pending_writes = [] self._text_lines = [] self._col = 0 self._current_sizes = (-1,) * 3 Widget.__init__(self, **kwargs) @property def text_color(self): """The color of the text""" return self._text_color @text_color.setter def text_color(self, color): self._text_color = Color(color) @property def font_size(self): """The font size (in points) of the text""" return self._font_size @font_size.setter def font_size(self, font_size): self._font_size = float(font_size) def _resize_buffers(self, font_scale): """Resize buffers only if necessary""" new_sizes = (font_scale,) + self.size if new_sizes == self._current_sizes: # don't need resize return self._n_rows = int(max(self.size[1] / (self._char_height * font_scale), 1)) self._n_cols = int(max(self.size[0] / (self._char_width * font_scale), 1)) self._bytes_012 = np.zeros((self._n_rows, self._n_cols, 3), np.float32) self._bytes_345 = np.zeros((self._n_rows, self._n_cols, 3), np.float32) pos = np.empty((self._n_rows, self._n_cols, 2), np.float32) C, R = np.meshgrid(np.arange(self._n_cols), np.arange(self._n_rows)) # We are in left, top orientation x_off = 4. y_off = 4 - self.size[1] / font_scale pos[..., 0] = x_off + self._char_width * C pos[..., 1] = y_off + self._char_height * R self._position = VertexBuffer(pos) # Restore lines for ii, line in enumerate(self._text_lines[:self._n_rows]): self._insert_text_buf(line, ii) self._current_sizes = new_sizes def draw(self, event): """Draw the widget Parameters ---------- event : instance of Event The draw event. """ super(Console, self).draw(event) if event is None: raise RuntimeError('Event cannot be None') xform = event.get_full_transform() tr = (event.document_to_framebuffer * event.framebuffer_to_render) logical_scale = np.diff(tr.map(([0, 1], [1, 0])), axis=0)[0, :2] tr = event.document_to_framebuffer log_to_phy = np.mean(np.diff(tr.map(([0, 1], [1, 0])), axis=0)[0, :2]) n_pix = (self.font_size / 72.) * 92. # num of pixels tall # The -2 here is because the char_height has a gap built in font_scale = max(n_pix / float((self._char_height-2)), 1) self._resize_buffers(font_scale) self._do_pending_writes() self._program['u_origin'] = xform.map((0, 0, 0, 1)) self._program.prepare() self._program['u_logical_scale'] = font_scale * logical_scale self._program['u_color'] = self.text_color.rgba self._program['u_physical_scale'] = font_scale * log_to_phy self._program['a_position'] = self._position self._program['a_bytes_012'] = VertexBuffer(self._bytes_012) self._program['a_bytes_345'] = VertexBuffer(self._bytes_345) set_state(depth_test=False, blend=True, blend_func=('src_alpha', 'one_minus_src_alpha')) self._program.draw('points') def clear(self): """Clear the console""" if hasattr(self, '_bytes_012'): self._bytes_012.fill(0) self._bytes_345.fill(0) self._text_lines = [] * self._n_rows self._pending_writes = [] def write(self, text='', wrap=True): """Write text and scroll Parameters ---------- text : str Text to write. ``''`` can be used for a blank line, as a newline is automatically added to the end of each line. wrap : str If True, long messages will be wrapped to span multiple lines. """ # Clear line if not isinstance(text, string_types): raise TypeError('text must be a string') # ensure we only have ASCII chars text = text.encode('utf-8').decode('ascii', errors='replace') self._pending_writes.append((text, wrap)) self.update() def _do_pending_writes(self): """Do any pending text writes""" for text, wrap in self._pending_writes: # truncate in case of *really* long messages text = text[-self._n_cols*self._n_rows:] text = text.split('\n') text = [t if len(t) > 0 else '' for t in text] nr, nc = self._n_rows, self._n_cols for para in text: para = para[:nc] if not wrap else para lines = [para[ii:(ii+nc)] for ii in range(0, len(para), nc)] lines = [''] if len(lines) == 0 else lines for line in lines: # Update row and scroll if necessary self._text_lines.insert(0, line) self._text_lines = self._text_lines[:nr] self._bytes_012[1:] = self._bytes_012[:-1] self._bytes_345[1:] = self._bytes_345[:-1] self._insert_text_buf(line, 0) self._pending_writes = [] def _insert_text_buf(self, line, idx): """Insert text into bytes buffers""" self._bytes_012[idx] = 0 self._bytes_345[idx] = 0 # Crop text if necessary I = np.array([ord(c) - 32 for c in line[:self._n_cols]]) I = np.clip(I, 0, len(__font_6x8__)-1) if len(I) > 0: b = __font_6x8__[I] self._bytes_012[idx, :len(I)] = b[:, :3] self._bytes_345[idx, :len(I)] = b[:, 3:] vispy-0.4.0/vispy/scene/widgets/__init__.py0000664000175000017500000000076512527672621022433 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ The vispy.scene.widgets namespace provides a range of widgets to allow user interaction. Widgets are rectangular Visual objects such as buttons and sliders. """ __all__ = ['Console', 'Grid', 'ViewBox', 'Widget'] from .console import Console # noqa from .grid import Grid # noqa from .viewbox import ViewBox # noqa from .widget import Widget # noqa vispy-0.4.0/vispy/scene/widgets/grid.py0000664000175000017500000001701312527672621021613 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from __future__ import division import numpy as np from .widget import Widget class Grid(Widget): """ Widget that automatically sets the position and size of child Widgets to proportionally divide its internal area into a grid. Parameters ---------- spacing : int Spacing between widgets. **kwargs : dict Keyword arguments to pass to `Widget`. """ def __init__(self, spacing=6, **kwargs): from .viewbox import ViewBox self._next_cell = [0, 0] # row, col self._cells = {} self._grid_widgets = {} self.spacing = spacing self._n_added = 0 self._default_class = ViewBox # what to add when __getitem__ is used Widget.__init__(self, **kwargs) def __getitem__(self, idxs): """Return an item or create it if the location is available""" if not isinstance(idxs, tuple): idxs = (idxs,) if len(idxs) == 1: idxs = idxs + (slice(None),) elif len(idxs) != 2: raise ValueError('Incorrect index: %s' % (idxs,)) lims = np.empty((2, 2), int) for ii, idx in enumerate(idxs): if isinstance(idx, int): idx = slice(idx, idx + 1, None) if not isinstance(idx, slice): raise ValueError('indices must be slices or integers, not %s' % (type(idx),)) if idx.step is not None and idx.step != 1: raise ValueError('step must be one or None, not %s' % idx.step) start = 0 if idx.start is None else idx.start end = self.grid_size[ii] if idx.stop is None else idx.stop lims[ii] = [start, end] layout = self.layout_array existing = layout[lims[0, 0]:lims[0, 1], lims[1, 0]:lims[1, 1]] + 1 if existing.any(): existing = set(list(existing.ravel())) ii = list(existing)[0] - 1 if len(existing) != 1 or ((layout == ii).sum() != np.prod(np.diff(lims))): raise ValueError('Cannot add widget (collision)') return self._grid_widgets[ii][-1] spans = np.diff(lims)[:, 0] item = self.add_widget(self._default_class(), row=lims[0, 0], col=lims[1, 0], row_span=spans[0], col_span=spans[1]) return item def add_widget(self, widget=None, row=None, col=None, row_span=1, col_span=1): """ Add a new widget to this grid. This will cause other widgets in the grid to be resized to make room for the new widget. Parameters ---------- widget : Widget The Widget to add row : int The row in which to add the widget (0 is the topmost row) col : int The column in which to add the widget (0 is the leftmost column) row_span : int The number of rows to be occupied by this widget. Default is 1. col_span : int The number of columns to be occupied by this widget. Default is 1. Notes ----- The widget's parent is automatically set to this grid, and all other parent(s) are removed. """ if row is None: row = self._next_cell[0] if col is None: col = self._next_cell[1] if widget is None: widget = Widget() _row = self._cells.setdefault(row, {}) _row[col] = widget self._grid_widgets[self._n_added] = (row, col, row_span, col_span, widget) self._n_added += 1 widget.parent = self self._next_cell = [row, col+col_span] self._update_child_widgets() return widget def add_grid(self, row=None, col=None, row_span=1, col_span=1, **kwargs): """ Create a new Grid and add it as a child widget. Parameters ---------- row : int The row in which to add the widget (0 is the topmost row) col : int The column in which to add the widget (0 is the leftmost column) row_span : int The number of rows to be occupied by this widget. Default is 1. col_span : int The number of columns to be occupied by this widget. Default is 1. **kwargs : dict Keyword arguments to pass to the new `Grid`. """ from .grid import Grid grid = Grid(**kwargs) return self.add_widget(grid, row, col, row_span, col_span) def add_view(self, row=None, col=None, row_span=1, col_span=1, **kwargs): """ Create a new ViewBox and add it as a child widget. Parameters ---------- row : int The row in which to add the widget (0 is the topmost row) col : int The column in which to add the widget (0 is the leftmost column) row_span : int The number of rows to be occupied by this widget. Default is 1. col_span : int The number of columns to be occupied by this widget. Default is 1. **kwargs : dict Keyword arguments to pass to `ViewBox`. """ from .viewbox import ViewBox view = ViewBox(**kwargs) return self.add_widget(view, row, col, row_span, col_span) def next_row(self): self._next_cell = [self._next_cell[0] + 1, 0] @property def grid_size(self): rvals = [widget[0]+widget[2] for widget in self._grid_widgets.values()] cvals = [widget[1]+widget[3] for widget in self._grid_widgets.values()] return max(rvals + [0]), max(cvals + [0]) @property def layout_array(self): locs = -1 * np.ones(self.grid_size, int) for key in self._grid_widgets.keys(): r, c, rs, cs = self._grid_widgets[key][:4] locs[r:r + rs, c:c + cs] = key return locs def __repr__(self): return (('') def _update_child_widgets(self): # Resize all widgets in this grid to share space. # This logic will need a lot of work.. n_rows, n_cols = self.grid_size if n_rows == 0: return # determine starting/ending position of each row and column s2 = self.spacing / 2. rect = self.rect.padded(self.padding + self.margin - s2) rows = np.linspace(rect.bottom, rect.top, n_rows+1) rowstart = rows[:-1] + s2 rowend = rows[1:] - s2 cols = np.linspace(rect.left, rect.right, n_cols+1) colstart = cols[:-1] + s2 colend = cols[1:] - s2 # snap to pixel boundaries to avoid drawing artifacts colstart = np.round(colstart) colend = np.round(colend) rowstart = np.round(rowstart) rowend = np.round(rowend) for key in self._grid_widgets.keys(): row, col, rspan, cspan, ch = self._grid_widgets[key] # Translate the origin of the node to the corner of the area # ch.transform.reset() # ch.transform.translate((colstart[col], rowstart[row])) ch.pos = colstart[col], rowstart[row] # ..and set the size to match. w = colend[col+cspan-1]-colstart[col] h = rowend[row+rspan-1]-rowstart[row] ch.size = w, h vispy-0.4.0/vispy/scene/widgets/viewbox.py0000664000175000017500000003603612527672621022357 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from __future__ import division import numpy as np from .widget import Widget from ..subscene import SubScene from ..cameras import make_camera, BaseCamera from ...ext.six import string_types from ... import gloo from ...visuals.components import Clipper from ...visuals import Visual class ViewBox(Widget): """ Provides a rectangular widget to which its subscene is rendered. Three classes work together when using a ViewBox: * The :class:`SubScene` class describes a "world" coordinate system and the entities that live inside it. * ViewBox is a "window" through which we view the subscene. Multiple ViewBoxes may view the same subscene. * :class:`Camera` describes both the perspective from which the subscene is rendered, and the way user interaction affects that perspective. In general it is only necessary to create the ViewBox; a SubScene and Camera will be generated automatically. Parameters ---------- camera : None, :class:`Camera`, or str The camera through which to view the SubScene. If None, then a PanZoomCamera (2D interaction) is used. If str, then the string is used as the argument to :func:`make_camera`. scene : None or :class:`SubScene` The `SubScene` instance to view. If None, a new `SubScene` is created. clip_method : str Clipping method to use. **kwargs : dict Extra keyword arguments to pass to `Widget`. """ def __init__(self, camera=None, scene=None, clip_method='fragment', **kwargs): self._camera = None Widget.__init__(self, **kwargs) # Init method to provide a pixel grid self._clip_method = clip_method self._clipper = Clipper() # Each viewbox has a scene widget, which has a transform that # represents the transformation imposed by camera. if scene is None: if self.name is not None: name = str(self.name) + "_Scene" else: name = None self._scene = SubScene(name=name) elif isinstance(scene, SubScene): self._scene = scene else: raise TypeError('Argument "scene" must be None or SubScene.') self._scene.add_parent(self) # Camera is a helper object that handles scene transformation # and user interaction. if camera is None: camera = 'base' if isinstance(camera, string_types): self.camera = make_camera(camera, parent=self.scene) elif isinstance(camera, BaseCamera): self.camera = camera else: raise TypeError('Argument "camera" must be None, str, or Camera.') @property def camera(self): """ Get/set the Camera in use by this ViewBox If a string is given (e.g. 'panzoom', 'turntable', 'fly'). A corresponding camera is selected if it already exists in the scene, otherwise a new camera is created. The camera object is made a child of the scene (if it is not already in the scene). Multiple cameras can exist in one scene, although only one can be active at a time. A single camera can be used by multiple viewboxes at the same time. """ return self._camera @camera.setter def camera(self, cam): if isinstance(cam, string_types): # Try to select an existing camera for child in self.scene.children: if isinstance(child, BaseCamera): this_cam_type = child.__class__.__name__.lower()[:-6] if this_cam_type == cam: self.camera = child return else: # No such camera yet, create it then self.camera = make_camera(cam) elif isinstance(cam, BaseCamera): # Ensure that the camera is in the scene if not self.is_in_scene(cam): cam.add_parent(self.scene) # Disconnect / connect if self._camera is not None: self._camera._viewbox_unset(self) self._camera = cam if self._camera is not None: self._camera._viewbox_set(self) # Update view cam.view_changed() else: raise ValueError('Not a camera object.') def is_in_scene(self, node): """Get whether the given node is inside the scene of this viewbox. Parameters ---------- node : instance of Node The node. """ def _is_child(parent, child): if child in parent.children: return True else: for c in parent.children: if isinstance(c, ViewBox): continue elif _is_child(c, child): return True return False return _is_child(self.scene, node) def get_scene_bounds(self, dim=None): """Get the total bounds based on the visuals present in the scene Parameters ---------- dim : int | None Dimension to return. Returns ------- bounds : list | tuple If ``dim is None``, Returns a list of 3 tuples, otherwise the bounds for the requested dimension. """ # todo: handle sub-children # todo: handle transformations # Init mode = 'data' # or visual? bounds = [(np.inf, -np.inf), (np.inf, -np.inf), (np.inf, -np.inf)] # Get bounds of all children for ob in self.scene.children: if hasattr(ob, 'bounds'): for axis in (0, 1, 2): if (dim is not None) and dim != axis: continue b = ob.bounds(mode, axis) if b is not None: b = min(b), max(b) # Ensure correct order bounds[axis] = (min(bounds[axis][0], b[0]), max(bounds[axis][1], b[1])) # Set defaults for axis in (0, 1, 2): if any(np.isinf(bounds[axis])): bounds[axis] = -1, 1 if dim is not None: return bounds[dim] else: return bounds @property def scene(self): """ The root node of the scene viewed by this ViewBox. """ return self._scene def add(self, node): """ Add an Node to the scene for this ViewBox. This is a convenience method equivalent to `node.add_parent(viewbox.scene)` Parameters ---------- node : instance of Node The node to add. """ node.add_parent(self.scene) @property def clip_method(self): """ The method used to clip the subscene to the boundaries of the viewbox. Clipping methods are: * None - do not perform clipping. The default for now. * 'viewport' - use glViewPort to provide a clipped sub-grid onto the parent pixel grid, if possible. * 'fbo' - use an FBO to draw the subscene to a texture, and then render the texture in the parent scene. * 'fragment' - clipping in the fragment shader * 'stencil' - TODO Notes ----- The 'viewport' method requires that the transformation (from the pixel grid to this viewbox) is translate+scale only. If this is not the case, the method falls back to the default. The 'fbo' method is convenient when the result of the viewbox should be reused. Otherwise the overhead can be significant and the image can get slightly blurry if the transformations do not match. It is possible to have a graph with multiple stacked viewboxes which each use different methods (subject to the above restrictions). """ return self._clip_method @clip_method.setter def clip_method(self, value): valid_methods = (None, 'fragment', 'viewport', 'fbo') if value not in valid_methods: t = 'clip_method should be in %s' % str(valid_methods) raise ValueError((t + ', not %r') % value) self._clip_method = value self.update() def draw(self, event): """ Draw the viewbox border/background This also prepares to draw the subscene using the configured clipping method. Parameters ---------- event : instance of Event The draw event. """ # -- Calculate resolution # Get current transform and calculate the 'scale' of the viewbox size = self.size transform = event.visual_to_document p0, p1 = transform.map((0, 0)), transform.map(size) res = (p1 - p0)[:2] res = abs(res[0]), abs(res[1]) # Set resolution (note that resolution can be non-integer) self._resolution = res method = self.clip_method viewport, fbo = None, None if method == 'fragment': self._prepare_fragment() elif method == 'stencil': raise NotImplementedError('No stencil buffer clipping yet.') elif method == 'viewport': viewport = self._prepare_viewport(event) elif method == 'fbo': fbo = self._prepare_fbo(event) else: raise ValueError('Unknown clipping method %s' % method) # -- Draw super(ViewBox, self).draw(event) event.push_viewbox(self) # make sure the current drawing system does not attempt to draw # the scene. event.handled_children.append(self.scene) if fbo: canvas_transform = event.visual_to_canvas offset = canvas_transform.map((0, 0))[:2] size = canvas_transform.map(self.size)[:2] - offset # Ask the canvas to activate the new FBO event.push_fbo(fbo, offset, size) event.push_node(self.scene) try: # Draw subscene to FBO self.scene.draw(event) finally: event.pop_node() event.pop_fbo() gloo.set_state(cull_face=False) elif viewport: # Push viewport, draw, pop it event.push_viewport(viewport) event.push_node(self.scene) try: self.scene.draw(event) finally: event.pop_node() event.pop_viewport() elif method == 'fragment': self._clipper.bounds = event.visual_to_framebuffer.map(self.rect) event.push_node(self.scene) try: self.scene.draw(event) finally: event.pop_node() event.pop_viewbox() def _prepare_fragment(self, root=None): # Todo: should only be run when there are changes in the graph, not # on every frame. if root is None: root = self.scene for ch in root.children: if isinstance(ch, Visual): try: ch.attach(self._clipper) except NotImplementedError: # visual does not support clipping pass self._prepare_fragment(ch) def _prepare_viewport(self, event): fb_transform = event.node_transform(map_from=event.node_cs, map_to=event.framebuffer_cs) p1 = fb_transform.map((0, 0)) p2 = fb_transform.map(self.size) return p1[0], p1[1], p2[0]-p1[0], p2[1]-p1[1] def _prepare_fbo(self, event): """ Draw the viewbox via an FBO. This method can be applied in any situation, regardless of the transformations to this viewbox. TODO: Right now, this implementation create a program, texture and FBO on *each* draw, because it does not work otherwise. This is probably a bug in gloo that prevents using two FBO's / programs at the same time. Also, we use plain gloo and calculate the transformation ourselves, assuming 2D only. Eventually we should just use the transform of self. I could not get that to work, probably because I do not understand the component system yet. """ render_vertex = """ attribute vec3 a_position; attribute vec2 a_texcoord; varying vec2 v_texcoord; void main() { gl_Position = vec4(a_position, 1.0); v_texcoord = a_texcoord; } """ render_fragment = """ uniform sampler2D u_texture; varying vec2 v_texcoord; void main() { vec4 v = texture2D(u_texture, v_texcoord); gl_FragColor = vec4(v.rgb, 1.0); } """ # todo: don't do this on every draw if True: # Create program self._fboprogram = gloo.Program(render_vertex, render_fragment) # Create texture self._tex = gloo.Texture2D((10, 10, 4), interpolation='linear') self._fboprogram['u_texture'] = self._tex # Create texcoords and vertices # Note y-axis is inverted here because the viewbox coordinate # system has origin in the upper-left, but the image is rendered # to the framebuffer with origin in the lower-left. texcoord = np.array([[0, 1], [1, 1], [0, 0], [1, 0]], dtype=np.float32) position = np.zeros((4, 3), np.float32) self._fboprogram['a_texcoord'] = gloo.VertexBuffer(texcoord) self._fboprogram['a_position'] = self._vert = \ gloo.VertexBuffer(position) # Get fbo, ensure it exists fbo = getattr(self, '_fbo', None) if True: # fbo is None: self._fbo = 4 self._fbo = fbo = gloo.FrameBuffer(self._tex, gloo.RenderBuffer((10, 10))) # Set texture coords to make the texture be drawn in the right place # Note that we would just use -1..1 if we would use a Visual. coords = [[0, 0], [self.size[0], self.size[1]]] transform = event.get_full_transform() coords = transform.map(coords) x1, y1, z = coords[0][:3] x2, y2, z = coords[1][:3] vertices = np.array([[x1, y1, z], [x2, y1, z], [x1, y2, z], [x2, y2, z]], np.float32) self._vert.set_data(vertices) # Set fbo size (mind that this is set using shape!) resolution = [int(i+0.5) for i in self._resolution] # set in draw() shape = resolution[1], resolution[0] # todo: use fbo.resize(shape) fbo.color_buffer.resize(shape+(4,)) fbo.depth_buffer.resize(shape) return fbo vispy-0.4.0/vispy/html/0000775000175000017500000000000012527674621016515 5ustar larsonerlarsoner00000000000000vispy-0.4.0/vispy/html/static/0000775000175000017500000000000012527674621020004 5ustar larsonerlarsoner00000000000000vispy-0.4.0/vispy/html/static/js/0000775000175000017500000000000012527674621020420 5ustar larsonerlarsoner00000000000000vispy-0.4.0/vispy/html/static/js/vispy.min.js0000664000175000017500000005235412510536123022705 0ustar larsonerlarsoner00000000000000!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var t;"undefined"!=typeof window?t=window:"undefined"!=typeof global?t=global:"undefined"!=typeof self&&(t=self),t.vispy=e()}}(function(){var e;return function t(e,n,r){function i(u,s){if(!n[u]){if(!e[u]){var l="function"==typeof require&&require;if(!s&&l)return l(u,!0);if(o)return o(u,!0);var a=new Error("Cannot find module '"+u+"'");throw a.code="MODULE_NOT_FOUND",a}var f=n[u]={exports:{}};e[u][0].call(f.exports,function(t){var n=e[u][1][t];return i(n?n:t)},f,f.exports,t,e,n,r)}return n[u].exports}for(var o="function"==typeof require&&require,u=0;u-1&&this._canvases.splice(t,1)},o.prototype.start_event_loop=function(){if(!this._is_loop_running){window.requestAnimFrame=function(){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||function(e){window.setTimeout(e,1e3/60)}}();var e=this;!function t(){e._request_id=requestAnimFrame(t);try{for(var n=0;ni;i++){var o=t.charCodeAt(i);r[i]=o}return r.buffer}function r(e){var t=e.storage_type;if(void 0==t)return e;var r=e.data_type,o=e.buffer;if("javascript_array"==t)return i[r](o);if("javascript_typed_array"==t||"array_buffer"==t)return o;if("binary"==t)return o.buffer;if("base64"==t){var u=n(o);return u}}var i={float32:Float32Array,int8:Int8Array,int16:Int16Array,int32:Int32Array,uint8:Uint8Array,uint16:Uint16Array,uint32:Uint32Array};t.exports={to_array_buffer:r}},{}],3:[function(e,t){function n(e,t){var n=e.getBoundingClientRect();return[t.clientX-n.left,t.clientY-n.top]}function r(e){var t=[];return e.altKey&&t.push("alt"),e.ctrlKey&&t.push("ctrl"),e.metaKey&&t.push("meta"),e.shiftKey&&t.push("shift"),t}function i(e){return String.fromCharCode(e).toUpperCase().trim()}function o(e){return window.event?e.keyCode:e.which?e.which:void 0}function u(e){var t=o(e),n=d[t];return void 0==n&&(n=i(t)),n}function s(e,t,i){if(e._eventinfo.is_button_pressed)var o=m[t.button];else o=null;var u=n(e.$el.get(0),t),s=r(t),l=e._eventinfo.press_event,a=(e._eventinfo.last_event,{type:i,pos:u,button:o,is_dragging:null!=l,modifiers:s,delta:null,press_event:l,last_event:null});return a}function l(e,t){var n={type:"resize",size:t};return n}function a(){var e={type:"paint"};return e}function f(){var e={type:"initialize"};return e}function c(e,t,n){var i=r(t),o=(e._eventinfo.last_event,{type:n,modifiers:i,key_code:u(t),last_event:null});return o}function p(e,t){var n=e.type;return n==t.type&&"mouse_move"==n&&e.button==t.button&e.is_dragging==t.is_dragging&e.modifiers.equals(t.modifiers)?["pos"]:[]}function h(e){void 0==e&&(e=100),this._queue=[],this.maxlen=e}function _(e){e.$el.resize(function(t){e.resize([t.width(),t.height()])}),e.event_queue=new h,e._eventinfo={type:null,pos:null,button:null,is_dragging:null,key:null,modifiers:[],press_event:null,last_event:null,delta:null},e._eventinfo.is_button_pressed=0,e.$el.mousemove(function(t){var n=s(e,t,"mouse_move");e._mouse_move(n),e.event_queue.append(n)}),e.$el.mousedown(function(t){++e._eventinfo.is_button_pressed;var n=s(e,t,"mouse_press");e._mouse_press(n),e._eventinfo.press_event=n,e.event_queue.append(n)}),e.$el.mouseup(function(t){--e._eventinfo.is_button_pressed;var n=s(e,t,"mouse_release");e._mouse_release(n),e._eventinfo.press_event=null,e.event_queue.append(n)}),e.$el.click(function(){e._eventinfo.press_event=null}),e.$el.dblclick(function(){e._eventinfo.press_event=null}),void 0!=e.$el.mousewheel&&e.$el.mousewheel(function(t){var n=s(e,t,"mouse_wheel");n.delta=[t.deltaX*t.deltaFactor*.01,t.deltaY*t.deltaFactor*.01],e._mouse_wheel(n),e.event_queue.append(n),t.preventDefault(),t.stopPropagation()}),e.$el.keydown(function(t){var n=c(e,t,"key_press");e._key_press(n),e.event_queue.append(n)}),e.$el.keyup(function(t){var n=c(e,t,"key_release");e._key_release(n),e.event_queue.append(n)}),e.$el.mouseout(function(){})}var g=e("./vispycanvas.js"),d={8:"BACKSPACE",9:"TAB",13:"ENTER",16:"SHIFT",17:"CONTROL",18:"ALT",27:"ESCAPE",32:"SPACE",33:"PAGEUP",34:"PAGEDOWN",35:"END",36:"HOME",37:"LEFT",38:"UP",39:"RIGHT",40:"DOWN",45:"INSERT",46:"DELETE",91:"META",92:"META",96:"0",97:"1",98:"2",99:"3",100:"4",101:"5",102:"6",103:"7",104:"8",105:"9",106:"*",107:"+",109:"-",110:".",111:"/",112:"F1",113:"F2",114:"F3",115:"F4",116:"F5",117:"F6",118:"F7",119:"F8",120:"F9",121:"F10",122:"F11",123:"F12",186:";",187:"=",188:",",189:"-",190:".",191:"/",192:"`",219:"[",220:"\\",221:"]",222:"'"},m={0:1,2:2,1:3};g.prototype._mouse_press=function(){},g.prototype._mouse_release=function(){},g.prototype._mouse_move=function(){},g.prototype._mouse_wheel=function(){},g.prototype._mouse_click=function(){},g.prototype._mouse_dblclick=function(){},g.prototype._key_press=function(){},g.prototype._key_release=function(){},g.prototype._initialize=function(){},g.prototype._resize=function(){},g.prototype._paint=function(){},g.prototype._event_tick=function(){},g.prototype.on_mouse_press=function(e){this._mouse_press=e},g.prototype.on_mouse_release=function(e){this._mouse_release=e},g.prototype.on_mouse_move=function(e){this._mouse_move=e},g.prototype.on_mouse_wheel=function(e){this._mouse_wheel=e},g.prototype.on_mouse_dblclick=function(e){this._mouse_dblclick=e},g.prototype.on_key_press=function(e){this._key_press=e},g.prototype.on_key_release=function(e){this._key_release=e},g.prototype.on_initialize=function(e){this._initialize=e},g.prototype.on_resize=function(e){this._resize=e},g.prototype.on_paint=function(e){this._paint=e},g.prototype.on_event_tick=function(e){this._event_tick=e},g.prototype.initialize=function(){var e=f(this);this._set_size(),this._initialize(e)},g.prototype._set_size=function(e){return void 0==e&&(e=[this.$el.width(),this.$el.height()]),this.size=e,this.width=e[0],this.height=e[1],e},g.prototype.paint=function(){var e=a(this);this.event_queue.append(e)},g.prototype.update=g.prototype.paint,g.prototype.resize=function(e){e=this._set_size(e);var t=l(this,e);this.gl.canvas.width=e[0],this.gl.canvas.height=e[1],this.event_queue.append(t),this._resize(t)},g.prototype.event_tick=function(){this._event_tick();var e=this.execute_pending_commands();if(e>0){var t=a(this);this._paint(t)}},g.prototype.is_fullscreen=function(){return screenfull.enabled&screenfull.isFullscreen},g.prototype.toggle_fullscreen=function(){screenfull.enabled&&(screenfull.isFullscreen?(screenfull.exit(),this.resize(this._size)):(this.$el.width("100%").height("100%"),this._size=[this.$el.width(),this.$el.height()],screenfull.request(this.$el[0]),this.resize([screen.width,screen.height])))},g.prototype.deactivate_context_menu=function(){document.oncontextmenu=function(){return!1}},g.prototype.resizable=function(){var e=this;this.$el.resizable({resize:function(t,n){e.resize([n.size.width,n.size.height])}})},h.prototype.clear=function(){this._queue=[]},h.prototype.append=function(e,t){var n=!0;if(void 0==t&&(t=!0),t){var r=this._queue[this._queue.length-1];if(void 0!=r){var i=p(e,r);if(i.length>0){for(var o=0;othis.maxlen&&(this._queue.shift(),this._queue[0].last_event=null)},h.prototype.get=function(){return this._queue},Object.defineProperty(h.prototype,"length",{get:function(){return this._queue.length}});var v=function(){};v.prototype.init=function(e){_(e)},t.exports=new v},{"./vispycanvas.js":9}],4:[function(e,t){function n(e,t,n){n=n.replace(/\\n/g,"\n");var r=e.gl.createShader(e.gl[t]);return e.gl.shaderSource(r,n),e.gl.compileShader(r),e.gl.getShaderParameter(r,e.gl.COMPILE_STATUS)?r:(console.error(e.gl.getShaderInfoLog(r)),null)}function r(e,t,n,r){e.gl.attachShader(t,n),e.gl.attachShader(t,r),e.gl.linkProgram(t),e.gl.getProgramParameter(t,e.gl.LINK_STATUS)||console.warn("Could not initialise shaders on program '{0}'.".format(t))}function i(e,t,n){var r=e.gl.getAttribLocation(t,n);return r}function o(e,t,n,r,i,o){var u=c(r),s=u[0],l=u[1];_vbo_info=e._ns[n];var a=_vbo_info.handle;e.gl.enableVertexAttribArray(t),e.gl.bindBuffer(e.gl.ARRAY_BUFFER,a),e.gl.vertexAttribPointer(t,l,e.gl[s],!1,i,o)}function u(e,t){e.gl.disableVertexAttribArray(t)}function s(e,t,n,r){e.gl.activeTexture(e.gl.TEXTURE0+r),e.gl.bindTexture(e.gl.TEXTURE_2D,t)}function l(e,t,n,r,i,o,u){e.gl.bindTexture(n,t),e.gl.pixelStorei(e.gl.UNPACK_ALIGNMENT,1);var s=new Uint8Array(u);e.gl.texImage2D(n,0,r,i,o,0,r,e.gl.UNSIGNED_BYTE,s)}function a(e,t,n,r,i,o){e.gl.bindBuffer(n,t),o?e.gl.bufferSubData(n,r,i):e.gl.bufferData(n,i,e.gl.STATIC_DRAW)}function f(e,t,n,r){array=b(r),n.indexOf("Matrix")>0?e.gl[n](t,!1,array):e.gl[n](t,array)}function c(e){return w[e]}function p(e){return x[e]}function h(e){return E[e]}function _(e,t){for(var n=t.split("|"),r=0,i=0;i=0?(o.format=i.toUpperCase(),y("Setting texture size to {1} for '{0}'.".format(n,r))):(y("Setting buffer size to {1} for '{0}'.".format(n,r)),a(e,u,l,0,r,!1)),o.size=r},T.prototype.data=function(e,t){var n=t[0],r=t[1],i=t[2],o=e._ns[n],u=o.object_type,s=o.handle,f=e.gl[h(u)],c=b(i);if(u.indexOf("Texture")>=0){var p=o.size,_=p[0],g=p[1],d=e.gl[o.format];y("Setting texture data for '{0}'.".format(n)),l(e,s,f,d,g,_,c),o.shape=p}else y("Setting buffer data for '{0}'.".format(n)),a(e,s,f,r,c,o.size>0),o.size=c.byteLength},T.prototype.attribute=function(e,t){var n=t[0],r=t[1],o=t[2],u=t[3][0],s=t[3][1],l=t[3][2],a=e._ns[n].handle;y("Creating attribute '{0}' for program '{1}'.".format(r,n));var f=i(e,a,r);e._ns[n].attributes[r]={handle:f,type:o,vbo_id:u,stride:s,offset:l}},T.prototype.uniform=function(e,t){var n=t[0],r=t[1],i=t[2],o=t[3],u=e._ns[n].handle;if(e.gl.useProgram(u),void 0==e._ns[n].uniforms[r]){y("Creating uniform '{0}' for program '{1}'.".format(r,n));var s=e.gl.getUniformLocation(u,r),l=p(i);e._ns[n].uniforms[r]=[s,l]}y("Setting uniform '{0}' to '{1}' with {2} elements.".format(r,o,o.length));var a=e._ns[n].uniforms[r],s=a[0],l=a[1];f(e,s,l,o)},T.prototype.texture=function(e,t){var n=t[0],r=t[1],i=t[2],o=e._ns[i].handle,u=e._ns[n].handle,s=Object.keys(e._ns[n].textures).length;y("Initializing texture '{0}' number {1} for program '{2}'.".format(i,s,n));var l=e.gl.getUniformLocation(u,r);e.gl.uniform1i(l,s),e._ns[n].textures[i]={sampler_name:r,sampler_handle:l,number:s,handle:o}},T.prototype.interpolation=function(e,t){var n=t[0],r=t[1].toUpperCase(),i=t[2].toUpperCase(),o=e._ns[n].handle,u=e.gl.TEXTURE_2D;e.gl.bindTexture(u,o),e.gl.texParameteri(u,e.gl.TEXTURE_MIN_FILTER,e.gl[r]),e.gl.texParameteri(u,e.gl.TEXTURE_MAG_FILTER,e.gl[i]),e.gl.bindTexture(u,null)},T.prototype.wrapping=function(e,t){var n=t[0],r=t[1],i=e._ns[n].handle,o=e.gl.TEXTURE_2D;e.gl.bindTexture(o,i),e.gl.texParameteri(o,e.gl.TEXTURE_WRAP_S,e.gl[r[0].toUpperCase()]),e.gl.texParameteri(o,e.gl.TEXTURE_WRAP_T,e.gl[r[1].toUpperCase()]),e.gl.bindTexture(o,null)},T.prototype.draw=function(e,t){var n=t[0],r=t[1].toUpperCase(),i=t[2],l=e._ns[n].handle,a=e._ns[n].attributes,f=e._ns[n].textures;for(attribute_name in a){var c=a[attribute_name];y("Activating attribute '{0}' for program '{1}'.".format(attribute_name,n)),o(e,c.handle,c.vbo_id,c.type,c.stride,c.offset)}for(texture_id in f){var p=f[texture_id];y("Activating texture '{0}' for program '{1}'.".format(texture_id,n)),s(e,p.handle,p.sampler_handle,p.number)}if(e.gl.useProgram(l),2==i.length){var h=i[0],_=i[1];y("Rendering program '{0}' with {1}.".format(n,r)),e.gl.drawArrays(e.gl[r],h,_)}else if(3==i.length){var g=i[0],d=i[1],_=i[2],m=e._ns[g].handle;y("Rendering program '{0}' with {1} and index buffer '{2}'.".format(n,r,g)),e.gl.bindBuffer(e.gl.ELEMENT_ARRAY_BUFFER,m),e.gl.drawElements(e.gl[r],_,e.gl[d],0)}for(attribute_name in a)y("Deactivating attribute '{0}' for program '{1}'.".format(attribute_name,n)),u(e,a[attribute_name].handle)},T.prototype.func=function(e,t){var n=t[0];y("Calling {0}({1}).".format(n,t.slice(1)));for(var r=1;rh)&&(o=h,r(u,h)&&(o/=40)),r(u,h)&&(a/=40,c/=40,p/=40),a=Math[a>=1?"floor":"ceil"](a/o),c=Math[c>=1?"floor":"ceil"](c/o),p=Math[p>=1?"floor":"ceil"](p/o),f.settings.normalizeOffset&&this.getBoundingClientRect){var v=this.getBoundingClientRect();_=t.clientX-v.left,g=t.clientY-v.top}return t.deltaX=c,t.deltaY=p,t.deltaFactor=o,t.offsetX=_,t.offsetY=g,t.deltaMode=0,s.unshift(t,a,c,p),i&&clearTimeout(i),i=setTimeout(n,200),(e.event.dispatch||e.event.handle).apply(this,s)}}function n(){o=null}function r(e,t){return f.settings.adjustOldDeltas&&"mousewheel"===e.type&&t%120===0}var i,o,u=["wheel","mousewheel","DOMMouseScroll","MozMousePixelScroll"],s="onwheel"in document||document.documentMode>=9?["wheel"]:["mousewheel","DomMouseScroll","MozMousePixelScroll"],l=Array.prototype.slice;if(e.event.fixHooks)for(var a=u.length;a;)e.event.fixHooks[u[--a]]=e.event.mouseHooks;var f=e.event.special.mousewheel={version:"3.1.12",setup:function(){if(this.addEventListener)for(var n=s.length;n;)this.addEventListener(s[--n],t,!1);else this.onmousewheel=t;e.data(this,"mousewheel-line-height",f.getLineHeight(this)),e.data(this,"mousewheel-page-height",f.getPageHeight(this))},teardown:function(){if(this.removeEventListener)for(var n=s.length;n;)this.removeEventListener(s[--n],t,!1);else this.onmousewheel=null;e.removeData(this,"mousewheel-line-height"),e.removeData(this,"mousewheel-page-height")},getLineHeight:function(t){var n=e(t),r=n["offsetParent"in e.fn?"offsetParent":"parent"]();return r.length||(r=e("body")),parseInt(r.css("fontSize"),10)||parseInt(n.css("fontSize"),10)||16},getPageHeight:function(t){return e(t).height()},settings:{adjustOldDeltas:!0,normalizeOffset:!0}};e.fn.extend({mousewheel:function(e){return e?this.bind("mousewheel",e):this.trigger("mousewheel")},unmousewheel:function(e){return this.unbind("mousewheel",e)}})})},{}],7:[function(e,t){!function(){"use strict";var e="undefined"!=typeof t&&t.exports,n="undefined"!=typeof Element&&"ALLOW_KEYBOARD_INPUT"in Element,r=function(){for(var e,t,n=[["requestFullscreen","exitFullscreen","fullscreenElement","fullscreenEnabled","fullscreenchange","fullscreenerror"],["webkitRequestFullscreen","webkitExitFullscreen","webkitFullscreenElement","webkitFullscreenEnabled","webkitfullscreenchange","webkitfullscreenerror"],["webkitRequestFullScreen","webkitCancelFullScreen","webkitCurrentFullScreenElement","webkitCancelFullScreen","webkitfullscreenchange","webkitfullscreenerror"],["mozRequestFullScreen","mozCancelFullScreen","mozFullScreenElement","mozFullScreenEnabled","mozfullscreenchange","mozfullscreenerror"],["msRequestFullscreen","msExitFullscreen","msFullscreenElement","msFullscreenEnabled","MSFullscreenChange","MSFullscreenError"]],r=0,i=n.length,o={};i>r;r++)if(e=n[r],e&&e[1]in document){for(r=0,t=e.length;t>r;r++)o[n[0][r]]=e[r];return o}return!1}(),i={request:function(e){var t=r.requestFullscreen;e=e||document.documentElement,/5\.1[\.\d]* Safari/.test(navigator.userAgent)?e[t]():e[t](n&&Element.ALLOW_KEYBOARD_INPUT)},exit:function(){document[r.exitFullscreen]()},toggle:function(e){this.isFullscreen?this.exit():this.request(e)},onchange:function(){},onerror:function(){},raw:r};return r?(Object.defineProperties(i,{isFullscreen:{get:function(){return!!document[r.fullscreenElement]}},element:{enumerable:!0,get:function(){return document[r.fullscreenElement]}},enabled:{enumerable:!0,get:function(){return!!document[r.fullscreenEnabled]}}}),document.addEventListener(r.fullscreenchange,function(e){i.onchange.call(i,e)}),document.addEventListener(r.fullscreenerror,function(e){i.onerror.call(i,e)}),void(e?t.exports=i:window.screenfull=i)):void(e?t.exports=!1:window.screenfull=!1)}()},{}],8:[function(e,t){function n(e){window.VISPY_DEBUG&&console.debug(e)}String.prototype.format||(String.prototype.format=function(){var e=arguments;return this.replace(/{(\d+)}/g,function(t,n){return"undefined"!=typeof e[n]?e[n]:t})}),"undefined"==typeof String.prototype.trim&&(String.prototype.trim=function(){return String(this).replace(/^\s+|\s+$/g,"")}),Array.prototype.equals=function(e){if(!e)return!1;if(this.length!=e.length)return!1;for(var t=0,n=this.length;n>t;t++)if(this[t]instanceof Array&&e[t]instanceof Array){if(!this[t].equals(e[t]))return!1}else if(this[t]!=e[t])return!1;return!0},"function"!=typeof String.prototype.startsWith&&(String.prototype.startsWith=function(e){return this.slice(0,e.length)==e}),window.VISPY_DEBUG=!1,t.exports={debug:n}},{}],9:[function(e,t){var n=function(e){this.$el=e};t.exports=n},{}]},{},[1])(1)}); //# sourceMappingURL=vispy.min.js.mapvispy-0.4.0/vispy/html/static/js/webgl-backend.js0000664000175000017500000001161712510536123023433 0ustar larsonerlarsoner00000000000000 // VispyWidget code define(function(require) { "use strict"; function _inline_glir_commands(commands, buffers) { // Put back the buffers within the GLIR commands before passing them // to the GLIR JavaScript interpretor. for (var i = 0; i < commands.length; i++) { var command = commands[i]; if (command[0] == 'DATA') { var buffer_index = command[3]['buffer_index']; command[3] = buffers[buffer_index]; } } return commands; } var vispy = require("/nbextensions/vispy/vispy.min.js"); var widget = require("widgets/js/widget"); var VispyView = widget.DOMWidgetView.extend({ initialize: function (parameters) { VispyView.__super__.initialize.apply(this, [parameters]); this.model.on('msg:custom', this.on_msg, this); // Track canvas size changes. this.model.on('change:width', this.size_changed, this); this.model.on('change:height', this.size_changed, this); }, render: function() { var that = this; var canvas = $(''); // canvas.css('border', '1px solid rgb(171, 171, 171)'); canvas.css('background-color', '#000'); canvas.attr('tabindex', '1'); this.$el.append(canvas); this.$canvas = canvas; // Initialize the VispyCanvas. this.c = vispy.init(canvas); this.c.on_resize(function (e) { that.model.set('width', e.size[0]); that.model.set('height', e.size[1]); that.touch(); }); // Start the event loop. this.c.on_event_tick(function() { // This callback function will be called at each JS tick, // before the GLIR commands are flushed. // Retrieve and flush the event queue. var events = that.c.event_queue.get(); that.c.event_queue.clear(); // Send the events if the queue is not empty. if (events.length > 0) { // Create the message. var msg = { msg_type: 'events', contents: events }; // console.debug(events); // Send the message with the events to Python. that.send(msg); } }); vispy.start_event_loop(); var msg = { msg_type: 'init' }; this.send(msg); // Make sure the size is correctly set up upon first display. this.size_changed(); this.c.resize(); this.c.resizable(); }, on_msg: function(comm_msg) { var buffers = comm_msg.buffers; var msg = comm_msg; //.content.data.content; if (msg == undefined) return; // Receive and execute the GLIR commands. if (msg.msg_type == 'glir_commands') { var commands = msg.commands; // Get the buffers messages. if (msg.array_serialization == 'base64') { var buffers_msg = msg.buffers; } else if (msg.array_serialization == 'binary') { // Need to put the raw binary buffers in JavaScript // objects for the inline commands. var buffers_msg = []; for (var i = 0; i < buffers.length; i++) { buffers_msg[i] = { 'storage_type': 'binary', 'buffer': buffers[i] }; } } // Make the GLIR commands ready for the JavaScript parser // by inlining the buffers. var commands_inlined = _inline_glir_commands( commands, buffers_msg); for (var i = 0; i < commands_inlined.length; i++) { var command = commands[i]; // Replace // console.debug(command); this.c.command(command); } } }, // When the model's size changes. size_changed: function() { var size = [this.model.get('width'), this.model.get('height')]; this.$canvas.css('width', size[0] + 'px'); this.$canvas.css('height', size[1] + 'px'); }, remove: function() { vispy.unregister(this.c); // Inform Python that the widget has been removed. this.send({ msg_type: 'status', contents: 'removed' }); } }); //IPython.WidgetManager.register_widget_view('VispyView', VispyView); return { 'VispyView' : VispyView }; }); vispy-0.4.0/vispy/html/static/js/jquery.mousewheel.min.js0000664000175000017500000000533112437121522025220 0ustar larsonerlarsoner00000000000000/*! Copyright (c) 2013 Brandon Aaron (http://brandon.aaron.sh) * Licensed under the MIT License (LICENSE.txt). * * Version: 3.1.12 * * Requires: jQuery 1.2.2+ */ !function(a){"function"==typeof define&&define.amd?define(["jquery"],a):"object"==typeof exports?module.exports=a:a(jQuery)}(function(a){function b(b){var g=b||window.event,h=i.call(arguments,1),j=0,l=0,m=0,n=0,o=0,p=0;if(b=a.event.fix(g),b.type="mousewheel","detail"in g&&(m=-1*g.detail),"wheelDelta"in g&&(m=g.wheelDelta),"wheelDeltaY"in g&&(m=g.wheelDeltaY),"wheelDeltaX"in g&&(l=-1*g.wheelDeltaX),"axis"in g&&g.axis===g.HORIZONTAL_AXIS&&(l=-1*m,m=0),j=0===m?l:m,"deltaY"in g&&(m=-1*g.deltaY,j=m),"deltaX"in g&&(l=g.deltaX,0===m&&(j=-1*l)),0!==m||0!==l){if(1===g.deltaMode){var q=a.data(this,"mousewheel-line-height");j*=q,m*=q,l*=q}else if(2===g.deltaMode){var r=a.data(this,"mousewheel-page-height");j*=r,m*=r,l*=r}if(n=Math.max(Math.abs(m),Math.abs(l)),(!f||f>n)&&(f=n,d(g,n)&&(f/=40)),d(g,n)&&(j/=40,l/=40,m/=40),j=Math[j>=1?"floor":"ceil"](j/f),l=Math[l>=1?"floor":"ceil"](l/f),m=Math[m>=1?"floor":"ceil"](m/f),k.settings.normalizeOffset&&this.getBoundingClientRect){var s=this.getBoundingClientRect();o=b.clientX-s.left,p=b.clientY-s.top}return b.deltaX=l,b.deltaY=m,b.deltaFactor=f,b.offsetX=o,b.offsetY=p,b.deltaMode=0,h.unshift(b,j,l,m),e&&clearTimeout(e),e=setTimeout(c,200),(a.event.dispatch||a.event.handle).apply(this,h)}}function c(){f=null}function d(a,b){return k.settings.adjustOldDeltas&&"mousewheel"===a.type&&b%120===0}var e,f,g=["wheel","mousewheel","DOMMouseScroll","MozMousePixelScroll"],h="onwheel"in document||document.documentMode>=9?["wheel"]:["mousewheel","DomMouseScroll","MozMousePixelScroll"],i=Array.prototype.slice;if(a.event.fixHooks)for(var j=g.length;j;)a.event.fixHooks[g[--j]]=a.event.mouseHooks;var k=a.event.special.mousewheel={version:"3.1.12",setup:function(){if(this.addEventListener)for(var c=h.length;c;)this.addEventListener(h[--c],b,!1);else this.onmousewheel=b;a.data(this,"mousewheel-line-height",k.getLineHeight(this)),a.data(this,"mousewheel-page-height",k.getPageHeight(this))},teardown:function(){if(this.removeEventListener)for(var c=h.length;c;)this.removeEventListener(h[--c],b,!1);else this.onmousewheel=null;a.removeData(this,"mousewheel-line-height"),a.removeData(this,"mousewheel-page-height")},getLineHeight:function(b){var c=a(b),d=c["offsetParent"in a.fn?"offsetParent":"parent"]();return d.length||(d=a("body")),parseInt(d.css("fontSize"),10)||parseInt(c.css("fontSize"),10)||16},getPageHeight:function(b){return a(b).height()},settings:{adjustOldDeltas:!0,normalizeOffset:!0}};a.fn.extend({mousewheel:function(a){return a?this.bind("mousewheel",a):this.trigger("mousewheel")},unmousewheel:function(a){return this.unbind("mousewheel",a)}})});vispy-0.4.0/vispy/util/0000775000175000017500000000000012527674621016526 5ustar larsonerlarsoner00000000000000vispy-0.4.0/vispy/util/transforms.py0000664000175000017500000001214212510536123021260 0ustar larsonerlarsoner00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ Very simple transformation library that is needed for some examples. """ from __future__ import division # Note: we use functions (e.g. sin) from math module because they're faster import math import numpy as np def translate(offset, dtype=None): """Translate by an offset (x, y, z) . Parameters ---------- offset : array-like, shape (3,) Translation in x, y, z. dtype : dtype | None Output type (if None, don't cast). Returns ------- M : ndarray Transformation matrix describing the translation. """ assert len(offset) == 3 x, y, z = offset M = np.array([[1., 0., 0., 0.], [0., 1., 0., 0.], [0., 0., 1., 0.], [x, y, z, 1.0]], dtype) return M def scale(s, dtype=None): """Non-uniform scaling along the x, y, and z axes Parameters ---------- s : array-like, shape (3,) Scaling in x, y, z. dtype : dtype | None Output type (if None, don't cast). Returns ------- M : ndarray Transformation matrix describing the scaling. """ assert len(s) == 3 return np.array(np.diag(np.concatenate([s, (1.,)])), dtype) def rotate(angle, axis, dtype=None): """The 3x3 rotation matrix for rotation about a vector. Parameters ---------- angle : float The angle of rotation, in degrees. axis : ndarray The x, y, z coordinates of the axis direction vector. """ angle = np.radians(angle) assert len(axis) == 3 x, y, z = axis / np.linalg.norm(axis) c, s = math.cos(angle), math.sin(angle) cx, cy, cz = (1 - c) * x, (1 - c) * y, (1 - c) * z M = np.array([[cx * x + c, cy * x - z * s, cz * x + y * s, .0], [cx * y + z * s, cy * y + c, cz * y - x * s, 0.], [cx * z - y * s, cy * z + x * s, cz * z + c, 0.], [0., 0., 0., 1.]], dtype).T return M def ortho(left, right, bottom, top, znear, zfar): """Create orthographic projection matrix Parameters ---------- left : float Left coordinate of the field of view. right : float Right coordinate of the field of view. bottom : float Bottom coordinate of the field of view. top : float Top coordinate of the field of view. znear : float Near coordinate of the field of view. zfar : float Far coordinate of the field of view. Returns ------- M : ndarray Orthographic projection matrix (4x4). """ assert(right != left) assert(bottom != top) assert(znear != zfar) M = np.zeros((4, 4), dtype=np.float32) M[0, 0] = +2.0 / (right - left) M[3, 0] = -(right + left) / float(right - left) M[1, 1] = +2.0 / (top - bottom) M[3, 1] = -(top + bottom) / float(top - bottom) M[2, 2] = -2.0 / (zfar - znear) M[3, 2] = -(zfar + znear) / float(zfar - znear) M[3, 3] = 1.0 return M def frustum(left, right, bottom, top, znear, zfar): """Create view frustum Parameters ---------- left : float Left coordinate of the field of view. right : float Right coordinate of the field of view. bottom : float Bottom coordinate of the field of view. top : float Top coordinate of the field of view. znear : float Near coordinate of the field of view. zfar : float Far coordinate of the field of view. Returns ------- M : ndarray View frustum matrix (4x4). """ assert(right != left) assert(bottom != top) assert(znear != zfar) M = np.zeros((4, 4), dtype=np.float32) M[0, 0] = +2.0 * znear / float(right - left) M[2, 0] = (right + left) / float(right - left) M[1, 1] = +2.0 * znear / float(top - bottom) M[2, 1] = (top + bottom) / float(top - bottom) M[2, 2] = -(zfar + znear) / float(zfar - znear) M[3, 2] = -2.0 * znear * zfar / float(zfar - znear) M[2, 3] = -1.0 return M def perspective(fovy, aspect, znear, zfar): """Create perspective projection matrix Parameters ---------- fovy : float The field of view along the y axis. aspect : float Aspect ratio of the view. znear : float Near coordinate of the field of view. zfar : float Far coordinate of the field of view. Returns ------- M : ndarray Perspective projection matrix (4x4). """ assert(znear != zfar) h = math.tan(fovy / 360.0 * math.pi) * znear w = h * aspect return frustum(-w, w, -h, h, znear, zfar) def affine_map(points1, points2): """ Find a 3D transformation matrix that maps points1 onto points2. Arguments are specified as arrays of four 3D coordinates, shape (4, 3). """ A = np.ones((4, 4)) A[:, :3] = points1 B = np.ones((4, 4)) B[:, :3] = points2 # solve 3 sets of linear equations to determine # transformation matrix elements matrix = np.eye(4) for i in range(3): # solve Ax = B; x is one row of the desired transformation matrix matrix[i] = np.linalg.solve(A, B[:, i]) return matrix vispy-0.4.0/vispy/util/svg/0000775000175000017500000000000012527674621017325 5ustar larsonerlarsoner00000000000000vispy-0.4.0/vispy/util/svg/path.py0000664000175000017500000002474412510536123020630 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2014, Nicolas P. Rougier # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- import re import math import numpy as np from . import geometry from . geometry import epsilon from . transformable import Transformable # ----------------------------------------------------------------- Command --- class Command(object): def __repr__(self): s = '%s ' % self._command for arg in self._args: s += "%.2f " % arg return s def origin(self, current=None, previous=None): relative = self._command in "mlvhcsqtaz" if relative and current: return current else: return 0.0, 0.0 # -------------------------------------------------------------------- Line --- class Line(Command): def __init__(self, x=0, y=0, relative=True): self._command = 'l' if relative else 'L' self._args = [x, y] def vertices(self, current, previous=None): ox, oy = self.origin(current) x, y = self._args self.previous = x, y return (ox + x, oy + y), # ------------------------------------------------------------------- VLine --- class VLine(Command): def __init__(self, y=0, relative=True): self._command = 'v' if relative else 'V' self._args = [y] def vertices(self, current, previous=None): ox, oy = self.origin(current) y = self._args[0] self.previous = ox, oy + y return (ox, oy + y), # ------------------------------------------------------------------- HLine --- class HLine(Command): def __init__(self, x=0, relative=True): self._command = 'h' if relative else 'H' self._args = [x] def vertices(self, current, previous=None): ox, oy = self.origin(current) x = self._args[0] self.previous = ox + x, oy return (ox + x, oy), # -------------------------------------------------------------------- Move --- class Move(Command): def __init__(self, x=0, y=0, relative=True): self._command = 'm' if relative else 'M' self._args = [x, y] def vertices(self, current, previous=None): ox, oy = self.origin(current) x, y = self._args x, y = x + ox, y + oy self.previous = x, y return (x, y), # ------------------------------------------------------------------- Close --- class Close(Command): def __init__(self, relative=True): self._command = 'z' if relative else 'Z' self._args = [] def vertices(self, current, previous=None): self.previous = current return [] # --------------------------------------------------------------------- Arc --- class Arc(Command): def __init__(self, r1=1, r2=1, angle=2 * math.pi, large=True, sweep=True, x=0, y=0, relative=True): self._command = 'a' if relative else 'A' self._args = [r1, r2, angle, large, sweep, x, y] def vertices(self, current, previous=None): ox, oy = self.origin(current) rx, ry, angle, large, sweep, x, y = self._args x, y = x + ox, y + oy x0, y0 = current self.previous = x, y vertices = geometry.elliptical_arc( x0, y0, rx, ry, angle, large, sweep, x, y) return vertices[1:] # ------------------------------------------------------------------- Cubic --- class Cubic(Command): def __init__(self, x1=0, y1=0, x2=0, y2=0, x3=0, y3=0, relative=True): self._command = 'c' if relative else 'C' self._args = [x1, y1, x2, y2, x3, y3] def vertices(self, current, previous=None): ox, oy = self.origin(current) x0, y0 = current x1, y1, x2, y2, x3, y3 = self._args x1, y1 = x1 + ox, y1 + oy x2, y2 = x2 + ox, y2 + oy x3, y3 = x3 + ox, y3 + oy self.previous = x2, y2 vertices = geometry.cubic((x0, y0), (x1, y1), (x2, y2), (x3, y3)) return vertices[1:] # --------------------------------------------------------------- Quadratic --- class Quadratic(Command): def __init__(self, x1=0, y1=0, x2=0, y2=0, relative=True): self._command = 'q' if relative else 'Q' self._args = [x1, y1, x2, y2] def vertices(self, current, last_control_point=None): ox, oy = self.origin(current) x1, y1, x2, y2 = self._args x0, y0 = current x1, y1 = x1 + ox, y1 + oy x2, y2 = x2 + ox, y2 + oy self.previous = x1, y1 vertices = geometry.quadratic((x0, y0), (x1, y1), (x2, y2)) return vertices[1:] # ------------------------------------------------------------- SmoothCubic --- class SmoothCubic(Command): def __init__(self, x2=0, y2=0, x3=0, y3=0, relative=True): self._command = 's' if relative else 'S' self._args = [x2, y2, x3, y3] def vertices(self, current, previous): ox, oy = self.origin(current) x0, y0 = current x2, y2, x3, y3 = self._args x2, y2 = x2 + ox, y2 + oy x3, y3 = x3 + ox, y3 + oy x1, y1 = 2 * x0 - previous[0], 2 * y0 - previous[1] self.previous = x2, y2 vertices = geometry.cubic((x0, y0), (x1, y1), (x2, y2), (x3, y3)) return vertices[1:] # --------------------------------------------------------- SmoothQuadratic --- class SmoothQuadratic(Command): def __init__(self, x2=0, y2=0, relative=True): self._command = 't' if relative else 'T' self._args = [x2, y2] def vertices(self, current, previous): ox, oy = self.origin(current) x2, y2 = self._args x0, y0 = current x1, y1 = 2 * x0 - previous[0], 2 * y0 - previous[1] x2, y2 = x2 + ox, y2 + oy self.previous = x1, y1 vertices = geometry.quadratic((x0, y0), (x1, y1), (x2, y2)) return vertices[1:] # -------------------------------------------------------------------- Path --- class Path(Transformable): def __init__(self, content=None, parent=None): Transformable.__init__(self, content, parent) self._paths = [] if not isinstance(content, str): content = content.get("d", "") commands = re.compile( "(?P[MLVHCSQTAZmlvhcsqtaz])(?P[+\-0-9.e, \n\t]*)") path = [] for match in re.finditer(commands, content): command = match.group("command") points = match.group("points").replace(',', ' ') points = [float(v) for v in points.split()] relative = command in "mlvhcsqtaz" command = command.upper() while len(points) or command == 'Z': if command == 'M': if len(path): self._paths.append(path) path = [] path.append(Move(*points[:2], relative=relative)) points = points[2:] elif command == 'L': path.append(Line(*points[:2], relative=relative)) points = points[2:] elif command == 'V': path.append(VLine(*points[:1], relative=relative)) points = points[1:] elif command == 'H': path.append(HLine(*points[:1], relative=relative)) points = points[1:] elif command == 'C': path.append(Cubic(*points[:6], relative=relative)) points = points[6:] elif command == 'S': path.append(SmoothCubic(*points[:4], relative=relative)) points = points[4:] elif command == 'Q': path.append(Quadratic(*points[:4], relative=relative)) points = points[4:] elif command == 'T': path.append( SmoothQuadratic(*points[2:], relative=relative)) points = points[2:] elif command == 'A': path.append(Arc(*points[:7], relative=relative)) points = points[7:] elif command == 'Z': path.append(Close(relative=relative)) self._paths.append(path) path = [] break else: raise RuntimeError( "Unknown SVG path command(%s)" % command) if len(path): self._paths.append(path) def __repr__(self): s = "" for path in self._paths: for item in path: s += repr(item) return s @property def xml(self): return self._xml() def _xml(self, prefix=""): s = prefix + "\n' return s @property def vertices(self): self._vertices = [] current = 0, 0 previous = 0, 0 for path in self._paths: vertices = [] for command in path: V = command.vertices(current, previous) previous = command.previous vertices.extend(V) if len(V) > 0: current = V[-1] else: current = 0, 0 closed = False if isinstance(command, Close): closed = True if len(vertices) > 2: d = geometry.calc_sq_distance(vertices[-1][0], vertices[-1][1], # noqa vertices[0][0], vertices[0][1]) # noqa if d < epsilon: vertices = vertices[:-1] # Apply transformation V = np.ones((len(vertices), 3)) V[:, :2] = vertices V = np.dot(V, self.transform.matrix.T) V[:, 2] = 0 self._vertices.append((V, closed)) return self._vertices vispy-0.4.0/vispy/util/svg/length.py0000664000175000017500000000440712510536123021147 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2014, Nicolas P. Rougier # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- import re import math from . base import units from .. import logger class Length(object): def __init__(self, content, mode='x', parent=None): if not content: self._unit = None self._value = 0 self._computed_value = 0 return re_number = r'[-+]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][-+]?\d+)?' re_unit = r'em|ex|px|in|cm|mm|pt|pc|%' re_length = r'(?P%s)\s*(?P%s)*' % (re_number, re_unit) match = re.match(re_length, content) if match: self._value = float(match.group("value")) self._unit = match.group("unit") or "px" else: self._value = 0.0 self._unit = None scale = 1 if self._unit == '%': if not parent: logger.warn("No parent for computing length using percent") elif hasattr(parent, 'viewport'): w, h = parent.viewport if mode == 'x': scale = w elif mode == 'y': scale = h elif mode == 'xy': scale = math.sqrt(w * w + h * h) / math.sqrt(2.0) else: logger.warn("Parent doesn't have a viewport") self._computed_value = self._value * units[self._unit] * scale def __float__(self): return self._computed_value @property def value(self): return self._computed_value def __repr__(self): if self._unit: return "%g%s" % (self._value, self._unit) else: return "%g" % (self._value) class XLength(Length): def __init__(self, content, parent=None): Length.__init__(self, content, 'x', parent) class YLength(Length): def __init__(self, content, parent=None): Length.__init__(self, content, 'y', parent) class XYLength(Length): def __init__(self, content, parent=None): Length.__init__(self, content, 'xy', parent) vispy-0.4.0/vispy/util/svg/svg.py0000664000175000017500000000206612510536123020464 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2014, Nicolas P. Rougier. All rights reserved. # Distributed under the terms of the new BSD License. # ----------------------------------------------------------------------------- from . group import Group from . viewport import Viewport class SVG(Group): def __init__(self, content=None, parent=None): Group.__init__(self, content, parent) self._viewport = Viewport(content) @property def viewport(self): return self._viewport def __repr__(self): s = "" for item in self._items: s += repr(item) + "\n" return s @property def xml(self): return self._xml() def _xml(self, prefix=""): s = " _keyword_colors = { "aliceblue": (240, 248, 255), "antiquewhite": (250, 235, 215), "aqua": (0, 255, 255), "aquamarine": (127, 255, 212), "azure": (240, 255, 255), "beige": (245, 245, 220), "bisque": (255, 228, 196), "black": (0, 0, 0), "blanchedalmond": (255, 235, 205), "blue": (0, 0, 255), "blueviolet": (138, 43, 226), "brown": (165, 42, 42), "burlywood": (222, 184, 135), "cadetblue": (95, 158, 160), "chartreuse": (127, 255, 0), "chocolate": (210, 105, 30), "coral": (255, 127, 80), "cornflowerblue": (100, 149, 237), "cornsilk": (255, 248, 220), "crimson": (220, 20, 60), "cyan": (0, 255, 255), "darkblue": (0, 0, 139), "darkcyan": (0, 139, 139), "darkgoldenrod": (184, 134, 11), "darkgray": (169, 169, 169), "darkgreen": (0, 100, 0), "darkgrey": (169, 169, 169), "darkkhaki": (189, 183, 107), "darkmagenta": (139, 0, 139), "darkolivegreen": (85, 107, 47), "darkorange": (255, 140, 0), "darkorchid": (153, 50, 204), "darkred": (139, 0, 0), "darksalmon": (233, 150, 122), "darkseagreen": (143, 188, 143), "darkslateblue": (72, 61, 139), "darkslategray": (47, 79, 79), "darkslategrey": (47, 79, 79), "darkturquoise": (0, 206, 209), "darkviolet": (148, 0, 211), "deeppink": (255, 20, 147), "deepskyblue": (0, 191, 255), "dimgray": (105, 105, 105), "dimgrey": (105, 105, 105), "dodgerblue": (30, 144, 255), "firebrick": (178, 34, 34), "floralwhite": (255, 250, 240), "forestgreen": (34, 139, 34), "fuchsia": (255, 0, 255), "gainsboro": (220, 220, 220), "ghostwhite": (248, 248, 255), "gold": (255, 215, 0), "goldenrod": (218, 165, 32), "gray": (128, 128, 128), "grey": (128, 128, 128), "green": (0, 128, 0), "greenyellow": (173, 255, 47), "honeydew": (240, 255, 240), "hotpink": (255, 105, 180), "indianred": (205, 92, 92), "indigo": (75, 0, 130), "ivory": (255, 255, 240), "khaki": (240, 230, 140), "lavender": (230, 230, 250), "lavenderblush": (255, 240, 245), "lawngreen": (124, 252, 0), "lemonchiffon": (255, 250, 205), "lightblue": (173, 216, 230), "lightcoral": (240, 128, 128), "lightcyan": (224, 255, 255), "lightgoldenrodyellow": (250, 250, 210), "lightgray": (211, 211, 211), "lightgreen": (144, 238, 144), "lightgrey": (211, 211, 211), "lightpink": (255, 182, 193), "lightsalmon": (255, 160, 122), "lightseagreen": (32, 178, 170), "lightskyblue": (135, 206, 250), "lightslategray": (119, 136, 153), "lightslategrey": (119, 136, 153), "lightsteelblue": (176, 196, 222), "lightyellow": (255, 255, 224), "lime": (0, 255, 0), "limegreen": (50, 205, 50), "linen": (250, 240, 230), "magenta": (255, 0, 255), "maroon": (128, 0, 0), "mediumaquamarine": (102, 205, 170), "mediumblue": (0, 0, 205), "mediumorchid": (186, 85, 211), "mediumpurple": (147, 112, 219), "mediumseagreen": (60, 179, 113), "mediumslateblue": (123, 104, 238), "mediumspringgreen": (0, 250, 154), "mediumturquoise": (72, 209, 204), "mediumvioletred": (199, 21, 133), "midnightblue": (25, 25, 112), "mintcream": (245, 255, 250), "mistyrose": (255, 228, 225), "moccasin": (255, 228, 181), "navajowhite": (255, 222, 173), "navy": (0, 0, 128), "oldlace": (253, 245, 230), "olive": (128, 128, 0), "olivedrab": (107, 142, 35), "orange": (255, 165, 0), "orangered": (255, 69, 0), "orchid": (218, 112, 214), "palegoldenrod": (238, 232, 170), "palegreen": (152, 251, 152), "paleturquoise": (175, 238, 238), "palevioletred": (219, 112, 147), "papayawhip": (255, 239, 213), "peachpuff": (255, 218, 185), "peru": (205, 133, 63), "pink": (255, 192, 203), "plum": (221, 160, 221), "powderblue": (176, 224, 230), "purple": (128, 0, 128), "red": (255, 0, 0), "rosybrown": (188, 143, 143), "royalblue": (65, 105, 225), "saddlebrown": (139, 69, 19), "salmon": (250, 128, 114), "sandybrown": (244, 164, 96), "seagreen": (46, 139, 87), "seashell": (255, 245, 238), "sienna": (160, 82, 45), "silver": (192, 192, 192), "skyblue": (135, 206, 235), "slateblue": (106, 90, 205), "slategray": (112, 128, 144), "slategrey": (112, 128, 144), "snow": (255, 250, 250), "springgreen": (0, 255, 127), "steelblue": (70, 130, 180), "tan": (210, 180, 140), "teal": (0, 128, 128), "thistle": (216, 191, 216), "tomato": (255, 99, 71), "turquoise": (64, 224, 208), "violet": (238, 130, 238), "wheat": (245, 222, 179), "white": (255, 255, 255), "whitesmoke": (245, 245, 245), "yellow": (255, 255, 0), "yellowgreen": (154, 205, 50)} _NUMERALS = '0123456789abcdefABCDEF' _HEXDEC = {v: int(v, 16) for v in (x + y for x in _NUMERALS for y in _NUMERALS)} def _rgb(triplet): return _HEXDEC[triplet[0:2]], _HEXDEC[triplet[2:4]], _HEXDEC[triplet[4:6]] class Color(object): def __init__(self, content): color = content.strip() if color.startswith("#"): rgb = color[1:] if len(rgb) == 3: r, g, b = tuple(ord((c + c).decode('hex')) for c in rgb) else: # r,g,b = tuple(ord(c) for c in rgb.decode('hex')) r, g, b = tuple(c for c in _rgb(rgb)) elif color.startswith("rgb("): rgb = color[4:-1] r, g, b = [value.strip() for value in rgb.split(',')] if r.endswith("%"): r = 255 * int(r[:-1]) // 100 else: r = int(r) if g.endswith("%"): g = 255 * int(g[:-1]) // 100 else: g = int(r) if b.endswith("%"): b = 255 * int(b[:-1]) // 100 else: b = int(r) elif color in _keyword_colors: r, g, b = _keyword_colors[color] else: # text = "Unknown color (%s)" % color r, g, b = 0, 0, 0 self._rgb = r / 255., g / 255., b / 255. @property def rgb(self): r, g, b = self._rgb return r, g, b @property def rgba(self): r, g, b = self._rgb return r, g, b, 1 def __repr__(self): r, g, b = self._rgb r, g, b = int(r * 255), int(g * 255), int(b * 255) return "#%02x%02x%02x" % (r, g, b) ����������������������������������������������������������������������������vispy-0.4.0/vispy/util/svg/style.py�����������������������������������������������������������������0000664�0001750�0001750�00000003277�12510536123�021032� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2014, Nicolas P. Rougier # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- from . color import Color from . number import Number from . length import Length _converters = { "fill": Color, "fill-opacity": Number, "stroke": Color, "stroke-opacity": Number, "opacity": Number, "stroke-width": Length, # "stroke-miterlimit": Number, # "stroke-dasharray": Lengths, # "stroke-dashoffset": Length, } class Style(object): def __init__(self): self._unset = True for key in _converters.keys(): key_ = key.replace("-", "_") self.__setattr__(key_, None) def update(self, content): if not content: return self._unset = False items = content.strip().split(";") attributes = dict([item.strip().split(":") for item in items if item]) for key, value in attributes.items(): if key in _converters: key_ = key.replace("-", "_") self.__setattr__(key_, _converters[key](value)) @property def xml(self): return self._xml() def _xml(self, prefix=""): if self._unset: return "" s = 'style="' for key in _converters.keys(): key_ = key.replace("-", "_") value = self.__getattribute__(key_) if value is not None: s += '%s:%s ' % (key, value) s += '"' return s ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/util/svg/geometry.py��������������������������������������������������������������0000664�0001750�0001750�00000035622�12510536123�021524� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# ---------------------------------------------------------------------------- # Anti-Grain Geometry (AGG) - Version 2.5 # A high quality rendering engine for C++ # Copyright (C) 2002-2006 Maxim Shemanarev # Contact: mcseem@antigrain.com # mcseemagg@yahoo.com # http://antigrain.com # # AGG is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # AGG is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with AGG; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, # MA 02110-1301, USA. # ---------------------------------------------------------------------------- # # Python translation by Nicolas P. Rougier # Copyright (C) 2013 Nicolas P. Rougier. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # 1. Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY NICOLAS P. ROUGIER ''AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO # EVENT SHALL NICOLAS P. ROUGIER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, # INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # The views and conclusions contained in the software and documentation are # those of the authors and should not be interpreted as representing official # policies, either expressed or implied, of Nicolas P. Rougier. # # ---------------------------------------------------------------------------- import math curve_distance_epsilon = 1e-30 curve_collinearity_epsilon = 1e-30 curve_angle_tolerance_epsilon = 0.01 curve_recursion_limit = 32 m_cusp_limit = 0.0 m_angle_tolerance = 10 * math.pi / 180.0 m_approximation_scale = 1.0 m_distance_tolerance_square = (0.5 / m_approximation_scale)**2 epsilon = 1e-10 def calc_sq_distance(x1, y1, x2, y2): dx = x2 - x1 dy = y2 - y1 return dx * dx + dy * dy def quadratic_recursive(points, x1, y1, x2, y2, x3, y3, level=0): if level > curve_recursion_limit: return # Calculate all the mid-points of the line segments # ------------------------------------------------- x12 = (x1 + x2) / 2. y12 = (y1 + y2) / 2. x23 = (x2 + x3) / 2. y23 = (y2 + y3) / 2. x123 = (x12 + x23) / 2. y123 = (y12 + y23) / 2. dx = x3 - x1 dy = y3 - y1 d = math.fabs((x2 - x3) * dy - (y2 - y3) * dx) if d > curve_collinearity_epsilon: # Regular case # ------------ if d * d <= m_distance_tolerance_square * (dx * dx + dy * dy): # If the curvature doesn't exceed the distance_tolerance value # we tend to finish subdivisions. if m_angle_tolerance < curve_angle_tolerance_epsilon: points.append((x123, y123)) return # Angle & Cusp Condition da = math.fabs( math.atan2(y3 - y2, x3 - x2) - math.atan2(y2 - y1, x2 - x1)) if da >= math.pi: da = 2 * math.pi - da if da < m_angle_tolerance: # Finally we can stop the recursion points.append((x123, y123)) return else: # Collinear case # -------------- da = dx * dx + dy * dy if da == 0: d = calc_sq_distance(x1, y1, x2, y2) else: d = ((x2 - x1) * dx + (y2 - y1) * dy) / da if d > 0 and d < 1: # Simple collinear case, 1---2---3, we can leave just two # endpoints return if(d <= 0): d = calc_sq_distance(x2, y2, x1, y1) elif d >= 1: d = calc_sq_distance(x2, y2, x3, y3) else: d = calc_sq_distance(x2, y2, x1 + d * dx, y1 + d * dy) if d < m_distance_tolerance_square: points.append((x2, y2)) return # Continue subdivision # -------------------- quadratic_recursive(points, x1, y1, x12, y12, x123, y123, level + 1) quadratic_recursive(points, x123, y123, x23, y23, x3, y3, level + 1) def cubic_recursive(points, x1, y1, x2, y2, x3, y3, x4, y4, level=0): if level > curve_recursion_limit: return # Calculate all the mid-points of the line segments # ------------------------------------------------- x12 = (x1 + x2) / 2. y12 = (y1 + y2) / 2. x23 = (x2 + x3) / 2. y23 = (y2 + y3) / 2. x34 = (x3 + x4) / 2. y34 = (y3 + y4) / 2. x123 = (x12 + x23) / 2. y123 = (y12 + y23) / 2. x234 = (x23 + x34) / 2. y234 = (y23 + y34) / 2. x1234 = (x123 + x234) / 2. y1234 = (y123 + y234) / 2. # Try to approximate the full cubic curve by a single straight line # ----------------------------------------------------------------- dx = x4 - x1 dy = y4 - y1 d2 = math.fabs(((x2 - x4) * dy - (y2 - y4) * dx)) d3 = math.fabs(((x3 - x4) * dy - (y3 - y4) * dx)) s = int((d2 > curve_collinearity_epsilon) << 1) + \ int(d3 > curve_collinearity_epsilon) if s == 0: # All collinear OR p1==p4 # ---------------------- k = dx * dx + dy * dy if k == 0: d2 = calc_sq_distance(x1, y1, x2, y2) d3 = calc_sq_distance(x4, y4, x3, y3) else: k = 1. / k da1 = x2 - x1 da2 = y2 - y1 d2 = k * (da1 * dx + da2 * dy) da1 = x3 - x1 da2 = y3 - y1 d3 = k * (da1 * dx + da2 * dy) if d2 > 0 and d2 < 1 and d3 > 0 and d3 < 1: # Simple collinear case, 1---2---3---4 # We can leave just two endpoints return if d2 <= 0: d2 = calc_sq_distance(x2, y2, x1, y1) elif d2 >= 1: d2 = calc_sq_distance(x2, y2, x4, y4) else: d2 = calc_sq_distance(x2, y2, x1 + d2 * dx, y1 + d2 * dy) if d3 <= 0: d3 = calc_sq_distance(x3, y3, x1, y1) elif d3 >= 1: d3 = calc_sq_distance(x3, y3, x4, y4) else: d3 = calc_sq_distance(x3, y3, x1 + d3 * dx, y1 + d3 * dy) if d2 > d3: if d2 < m_distance_tolerance_square: points.append((x2, y2)) return else: if d3 < m_distance_tolerance_square: points.append((x3, y3)) return elif s == 1: # p1,p2,p4 are collinear, p3 is significant # ----------------------------------------- if d3 * d3 <= m_distance_tolerance_square * (dx * dx + dy * dy): if m_angle_tolerance < curve_angle_tolerance_epsilon: points.append((x23, y23)) return # Angle Condition # --------------- da1 = math.fabs( math.atan2(y4 - y3, x4 - x3) - math.atan2(y3 - y2, x3 - x2)) if da1 >= math.pi: da1 = 2 * math.pi - da1 if da1 < m_angle_tolerance: points.extend([(x2, y2), (x3, y3)]) return if m_cusp_limit != 0.0: if da1 > m_cusp_limit: points.append((x3, y3)) return elif s == 2: # p1,p3,p4 are collinear, p2 is significant # ----------------------------------------- if d2 * d2 <= m_distance_tolerance_square * (dx * dx + dy * dy): if m_angle_tolerance < curve_angle_tolerance_epsilon: points.append((x23, y23)) return # Angle Condition # --------------- da1 = math.fabs( math.atan2(y3 - y2, x3 - x2) - math.atan2(y2 - y1, x2 - x1)) if da1 >= math.pi: da1 = 2 * math.pi - da1 if da1 < m_angle_tolerance: points.extend([(x2, y2), (x3, y3)]) return if m_cusp_limit != 0.0: if da1 > m_cusp_limit: points.append((x2, y2)) return elif s == 3: # Regular case # ------------ if (d2 + d3) * (d2 + d3) <= m_distance_tolerance_square * (dx * dx + dy * dy): # noqa # If the curvature doesn't exceed the distance_tolerance value # we tend to finish subdivisions. if m_angle_tolerance < curve_angle_tolerance_epsilon: points.append((x23, y23)) return # Angle & Cusp Condition # ---------------------- k = math.atan2(y3 - y2, x3 - x2) da1 = math.fabs(k - math.atan2(y2 - y1, x2 - x1)) da2 = math.fabs(math.atan2(y4 - y3, x4 - x3) - k) if da1 >= math.pi: da1 = 2 * math.pi - da1 if da2 >= math.pi: da2 = 2 * math.pi - da2 if da1 + da2 < m_angle_tolerance: # Finally we can stop the recursion # --------------------------------- points.append((x23, y23)) return if m_cusp_limit != 0.0: if da1 > m_cusp_limit: points.append((x2, y2)) return if da2 > m_cusp_limit: points.append((x3, y3)) return # Continue subdivision # -------------------- cubic_recursive( points, x1, y1, x12, y12, x123, y123, x1234, y1234, level + 1) cubic_recursive( points, x1234, y1234, x234, y234, x34, y34, x4, y4, level + 1) def quadratic(p1, p2, p3): x1, y1 = p1 x2, y2 = p2 x3, y3 = p3 points = [] quadratic_recursive(points, x1, y1, x2, y2, x3, y3) dx, dy = points[0][0] - x1, points[0][1] - y1 if (dx * dx + dy * dy) > epsilon: points.insert(0, (x1, y1)) dx, dy = points[-1][0] - x3, points[-1][1] - y3 if (dx * dx + dy * dy) > epsilon: points.append((x3, y3)) return points def cubic(p1, p2, p3, p4): x1, y1 = p1 x2, y2 = p2 x3, y3 = p3 x4, y4 = p4 points = [] cubic_recursive(points, x1, y1, x2, y2, x3, y3, x4, y4) dx, dy = points[0][0] - x1, points[0][1] - y1 if (dx * dx + dy * dy) > epsilon: points.insert(0, (x1, y1)) dx, dy = points[-1][0] - x4, points[-1][1] - y4 if (dx * dx + dy * dy) > epsilon: points.append((x4, y4)) return points def arc(cx, cy, rx, ry, a1, a2, ccw=False): scale = 1.0 ra = (abs(rx) + abs(ry)) / 2.0 da = math.acos(ra / (ra + 0.125 / scale)) * 2.0 if ccw: while a2 < a1: a2 += math.pi * 2.0 else: while a1 < a2: a1 += math.pi * 2.0 da = -da a_start = a1 a_end = a2 vertices = [] angle = a_start while (angle < a_end - da / 4) == ccw: x = cx + math.cos(angle) * rx y = cy + math.sin(angle) * ry vertices.append((x, y)) angle += da x = cx + math.cos(a_end) * rx y = cy + math.sin(a_end) * ry vertices.append((x, y)) return vertices def elliptical_arc(x0, y0, rx, ry, angle, large_arc_flag, sweep_flag, x2, y2): radii_ok = True cos_a = math.cos(angle) sin_a = math.sin(angle) if rx < 0.0: rx = -rx if ry < 0.0: ry = -rx # Calculate the middle point between # the current and the final points # ------------------------ dx2 = (x0 - x2) / 2.0 dy2 = (y0 - y2) / 2.0 # Calculate (x1, y1) # ------------------------ x1 = cos_a * dx2 + sin_a * dy2 y1 = -sin_a * dx2 + cos_a * dy2 # Check that radii are large enough # ------------------------ prx, pry = rx * rx, ry * ry px1, py1 = x1 * x1, y1 * y1 radii_check = px1 / prx + py1 / pry if radii_check > 1.0: rx = math.sqrt(radii_check) * rx ry = math.sqrt(radii_check) * ry prx = rx * rx pry = ry * ry if radii_check > 10.0: radii_ok = False # noqa # Calculate (cx1, cy1) # ------------------------ if large_arc_flag == sweep_flag: sign = -1 else: sign = +1 sq = (prx * pry - prx * py1 - pry * px1) / (prx * py1 + pry * px1) coef = sign * math.sqrt(max(sq, 0)) cx1 = coef * ((rx * y1) / ry) cy1 = coef * -((ry * x1) / rx) # Calculate (cx, cy) from (cx1, cy1) # ------------------------ sx2 = (x0 + x2) / 2.0 sy2 = (y0 + y2) / 2.0 cx = sx2 + (cos_a * cx1 - sin_a * cy1) cy = sy2 + (sin_a * cx1 + cos_a * cy1) # Calculate the start_angle (angle1) and the sweep_angle (dangle) # ------------------------ ux = (x1 - cx1) / rx uy = (y1 - cy1) / ry vx = (-x1 - cx1) / rx vy = (-y1 - cy1) / ry # Calculate the angle start # ------------------------ n = math.sqrt(ux * ux + uy * uy) p = ux if uy < 0: sign = -1.0 else: sign = +1.0 v = p / n if v < -1.0: v = -1.0 if v > 1.0: v = 1.0 start_angle = sign * math.acos(v) # Calculate the sweep angle # ------------------------ n = math.sqrt((ux * ux + uy * uy) * (vx * vx + vy * vy)) p = ux * vx + uy * vy if ux * vy - uy * vx < 0: sign = -1.0 else: sign = +1.0 v = p / n v = min(max(v, -1.0), +1.0) sweep_angle = sign * math.acos(v) if not sweep_flag and sweep_angle > 0: sweep_angle -= math.pi * 2.0 elif sweep_flag and sweep_angle < 0: sweep_angle += math.pi * 2.0 start_angle = math.fmod(start_angle, 2.0 * math.pi) if sweep_angle >= 2.0 * math.pi: sweep_angle = 2.0 * math.pi if sweep_angle <= -2.0 * math.pi: sweep_angle = -2.0 * math.pi V = arc(cx, cy, rx, ry, start_angle, start_angle + sweep_angle, sweep_flag) c = math.cos(angle) s = math.sin(angle) X, Y = V[:, 0] - cx, V[:, 1] - cy V[:, 0] = c * X - s * Y + cx V[:, 1] = s * X + c * Y + cy return V ��������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/util/svg/transform.py�������������������������������������������������������������0000664�0001750�0001750�00000015433�12510536123�021702� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2014, Nicolas P. Rougier. All rights reserved. # Distributed under the terms of the new BSD License. # ----------------------------------------------------------------------------- import re import math import numpy as np # ------------------------------------------------------------------ Matrix --- class Matrix(object): def __init__(self, a=1, b=0, c=0, d=1, e=0, f=0): self._matrix = np.array([[a, c, e], [b, d, f], [0, 0, 1]], dtype=float) @property def matrix(self): return self._matrix def __array__(self, *args): return self._matrix def __repr__(self): a, c, e = self._matrix[0] b, d, f = self._matrix[1] return "Matrix(%g,%g,%g,%g,%g,%g)" % (a, b, c, d, e, f) # ---------------------------------------------------------------- Identity --- class Identity(Matrix): def __init__(self): Matrix.__init__(self) self._matrix[...] = ([[1, 0, 0], [0, 1, 0], [0, 0, 1]]) def __repr__(self): return "Identity()" # --------------------------------------------------------------- Translate --- class Translate(Matrix): """ Translation is equivalent to the matrix [1 0 0 1 tx ty], where tx and ty are the distances to translate coordinates in X and Y, respectively. """ def __init__(self, x, y=0): Matrix.__init__(self) self._x, self._y = x, y self._matrix[...] = ([[1, 0, x], [0, 1, y], [0, 0, 1]]) def __repr__(self): return "Translate(%g,%g)" % (self._x, self._y) # ------------------------------------------------------------------- Scale --- class Scale(Matrix): """ Scaling is equivalent to the matrix [sx 0 0 sy 0 0]. One unit in the X and Y directions in the new coordinate system equals sx and sy units in the previous coordinate system, respectively. """ def __init__(self, x, y=0): Matrix.__init__(self) self._x = x self._y = y or x self._matrix[...] = ([[x, 0, 0], [0, y, 0], [0, 0, 1]]) def __repr__(self): return "Scale(%g,%g)" % (self._x, self._y) # ------------------------------------------------------------------- Scale --- class Rotate(Matrix): """ Rotation about the origin is equivalent to the matrix [cos(a) sin(a) -sin(a) cos(a) 0 0], which has the effect of rotating the coordinate system axes by angle a. """ def __init__(self, angle, x=0, y=0): Matrix.__init__(self) self._angle = angle self._x = x self._y = y angle = math.pi * angle / 180.0 rotate = np.array([[math.cos(angle), -math.sin(angle), 0], [math.sin(angle), math.cos(angle), 0], [0, 0, 1]], dtype=float) forward = np.array([[1, 0, x], [0, 1, y], [0, 0, 1]], dtype=float) inverse = np.array([[1, 0, -x], [0, 1, -y], [0, 0, 1]], dtype=float) self._matrix = np.dot(inverse, np.dot(rotate, forward)) def __repr__(self): return "Rotate(%g,%g,%g)" % (self._angle, self._x, self._y) # ------------------------------------------------------------------- SkewX --- class SkewX(Matrix): """ A skew transformation along the x-axis is equivalent to the matrix [1 0 tan(a) 1 0 0], which has the effect of skewing X coordinates by angle a. """ def __init__(self, angle): Matrix.__init__(self) self._angle = angle angle = math.pi * angle / 180.0 self._matrix[...] = ([[1, math.tan(angle), 0], [0, 1, 0], [0, 0, 1]]) def __repr__(self): return "SkewX(%g)" % (self._angle) # ------------------------------------------------------------------- SkewY --- class SkewY(Matrix): """ A skew transformation along the y-axis is equivalent to the matrix [1 tan(a) 0 1 0 0], which has the effect of skewing Y coordinates by angle a. """ def __init__(self, angle): Matrix.__init__(self) self._angle = angle angle = math.pi * angle / 180.0 self._matrix[...] = ([[1, 0, 0], [math.tan(angle), 1, 0], [0, 0, 1]]) def __repr__(self): return "SkewY(%g)" % (self._angle) # --------------------------------------------------------------- Transform --- class Transform: """ A Transform is defined as a list of transform definitions, which are applied in the order provided. The individual transform definitions are separated by whitespace and/or a comma. """ def __init__(self, content=""): self._transforms = [] if not content: return converters = {"matrix": Matrix, "scale": Scale, "rotate": Rotate, "translate": Translate, "skewx": SkewX, "skewy": SkewY} keys = "|".join(converters.keys()) pattern = "(?P%s)\s*\((?P[^)]*)\)" % keys for match in re.finditer(pattern, content): name = match.group("name").strip() args = match.group("args").strip().replace(',', ' ') args = [float(value) for value in args.split()] transform = converters[name](*args) self._transforms.append(transform) def __add__(self, other): T = Transform() T._transforms.extend(self._transforms) T._transforms.extend(other._transforms) return T def __radd__(self, other): self._transforms.extend(other._transforms) return self @property def matrix(self): M = np.eye(3) for transform in self._transforms: M = np.dot(M, transform) return M def __array__(self, *args): return self._matrix def __repr__(self): s = "" for i in range(len(self._transforms)): s += repr(self._transforms[i]) if i < len(self._transforms) - 1: s += ", " return s @property def xml(self): return self._xml() def _xml(self, prefix=""): identity = True for transform in self._transforms: if not isinstance(transform, Identity): identity = False break if identity: return "" return 'transform="%s" ' % repr(self) �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/util/svg/__init__.py��������������������������������������������������������������0000664�0001750�0001750�00000001241�12510536123�021416� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2014, Nicolas P. Rougier. All rights reserved. # Distributed under the terms of the new BSD License. # ----------------------------------------------------------------------------- from . svg import SVG from . path import Path # noqa from . base import namespace from xml.etree import ElementTree def Document(filename): tree = ElementTree.parse(filename) root = tree.getroot() if root.tag != namespace + 'svg': text = 'File "%s" does not seem to be a valid SVG file' % filename raise TypeError(text) return SVG(root) ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/util/svg/base.py������������������������������������������������������������������0000664�0001750�0001750�00000001442�12510536123�020574� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2014, Nicolas P. Rougier. All rights reserved. # Distributed under the terms of the new BSD License. # ----------------------------------------------------------------------------- namespace = '{http://www.w3.org/2000/svg}' dpi = 90 units = { None: 1, # Default unit (same as pixel) 'px': 1, # px: pixel. Default SVG unit 'em': 10, # 1 em = 10 px FIXME 'ex': 5, # 1 ex = 5 px FIXME 'in': dpi, # 1 in = 96 px 'cm': dpi / 2.54, # 1 cm = 1/2.54 in 'mm': dpi / 25.4, # 1 mm = 1/25.4 in 'pt': dpi / 72.0, # 1 pt = 1/72 in 'pc': dpi / 6.0, # 1 pc = 1/6 in '%': 1 / 100.0 # 1 percent } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/util/svg/shapes.py����������������������������������������������������������������0000664�0001750�0001750�00000002252�12510536123�021145� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2014, Nicolas P. Rougier # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- class Rect(object): def __init__(self, x=0, y=0, width=1, height=1, rx=0, ry=0): self.x = x self.y = y self.width = width self.height = height self.rx = rx self.ry = ry def parse(self, expression): """ """ class Line(object): def __init__(self, x1=0, y1=0, x2=0, y2=0): self.x1 = x2 self.y1 = y2 self.x2 = x2 self.y2 = y2 class Circle(object): def __init__(self, cx=0, cy=0, r=1): self.cx = cx self.cy = cy self.r = r class Ellipse(object): def __init__(self, cx=0, cy=0, rx=1, ry=1): self.cx = cx self.cy = cy self.rx = rx self.ry = ry class Polygon(object): def __init__(self, points=[]): self.points = points class Polyline(object): def __init__(self, points=[]): self.points = points ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/util/svg/transformable.py���������������������������������������������������������0000664�0001750�0001750�00000001774�12510536123�022531� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2014, Nicolas P. Rougier. All rights reserved. # Distributed under the terms of the new BSD License. # ----------------------------------------------------------------------------- from . element import Element from . transform import Transform class Transformable(Element): """ Transformable SVG element """ def __init__(self, content=None, parent=None): Element.__init__(self, content, parent) if isinstance(content, str): self._transform = Transform() self._computed_transform = self._transform else: self._transform = Transform(content.get("transform", None)) self._computed_transform = self._transform if parent: self._computed_transform = self._transform + \ self.parent.transform @property def transform(self): return self._computed_transform ����vispy-0.4.0/vispy/util/svg/viewport.py��������������������������������������������������������������0000664�0001750�0001750�00000003741�12510536123�021545� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2014, Nicolas P. Rougier. All rights reserved. # Distributed under the terms of the new BSD License. # ----------------------------------------------------------------------------- from . length import XLength, YLength class Viewport(object): def __init__(self, content=None, parent=None): self._x = None self._computed_x = 0 if content.get('x'): self._x = XLength(content.get('x'), parent) self._computed_x = float(self._x) self._y = None self._computed_y = 0 if content.get('y'): self._y = XLength(content.get('y'), parent) self._computed_y = float(self._y) self._width = None self._computed_width = 800 if content.get('width'): self._width = XLength(content.get('width'), parent) self._computed_width = float(self._width) self._height = None self._computed_height = 800 if content.get('height'): self._height = YLength(content.get('height'), parent) self._computed_height = float(self._height) @property def x(self): return self._computed_x @property def y(self): return self._computed_y @property def width(self): return self._computed_width @property def height(self): return self._computed_height def __repr__(self): s = repr((self._x, self._y, self._width, self._height)) return s @property def xml(self): return self._xml @property def _xml(self, prefix=""): s = "" if self._x: s += 'x="%s" ' % repr(self._x) if self._y: s += 'y="%s" ' % repr(self._y) if self._width: s += 'width="%s" ' % repr(self._width) if self._height: s += 'height="%s" ' % repr(self._height) return s �������������������������������vispy-0.4.0/vispy/util/svg/element.py���������������������������������������������������������������0000664�0001750�0001750�00000002607�12510536123�021317� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2014, Nicolas P. Rougier. All rights reserved. # Distributed under the terms of the new BSD License. # ----------------------------------------------------------------------------- import copy from . style import Style namespace = '{http://www.w3.org/2000/svg}' class Element(object): """ Generic SVG element """ def __init__(self, content=None, parent=None): self._parent = parent self._id = hex(id(self)) self._style = Style() self._computed_style = Style() if isinstance(content, str): return self._id = content.get('id', self._id) self._style.update(content.get("style", None)) self._computed_style = Style() if parent and parent.style: self._computed_style = copy.copy(parent.style) self._computed_style.update(content.get("style", None)) @property def root(self): if self._parent: return self._parent.root return self @property def parent(self): if self._parent: return self._parent return None @property def style(self): return self._computed_style @property def viewport(self): if self._parent: return self._parent.viewport return None �������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/util/svg/group.py�����������������������������������������������������������������0000664�0001750�0001750�00000003551�12510536123�021021� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2014, Nicolas P. Rougier. All rights reserved. # Distributed under the terms of the new BSD License. # ----------------------------------------------------------------------------- import copy from vispy.util import logger from . path import Path from . base import namespace from . transformable import Transformable class Group(Transformable): def __init__(self, content=None, parent=None): Transformable.__init__(self, content, parent) self._items = [] for element in content: if not element.tag.startswith(namespace): continue tag = element.tag[len(namespace):] if tag == "g": item = Group(element, self) elif tag == "path": item = Path(element, self) else: logger.warn("Unhandled SVG tag (%s)" % tag) continue self._items.append(item) @property def flatten(self): i = 0 L = copy.deepcopy(self._items) while i < len(L): while isinstance(L[i], Group) and len(L[i]._items): L[i:i + 1] = L[i]._items i += 1 return L @property def paths(self): return [item for item in self.flatten if isinstance(item, Path)] def __repr__(self): s = "" for item in self._items: s += repr(item) return s @property def xml(self): return self._xml() def _xml(self, prefix=""): s = prefix + "\n" return s �������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/util/fourier.py�������������������������������������������������������������������0000664�0001750�0001750�00000003640�12527672621�020554� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. import numpy as np def stft(x, n_fft=1024, step=512, fs=2*np.pi, window='hann'): """Compute the STFT Parameters ---------- x : array-like 1D signal to operate on. ``If len(x) < n_fft``, x will be zero-padded to length ``n_fft``. n_fft : int Number of FFT points. Much faster for powers of two. step : int | None Step size between calculations. If None, ``n_fft // 2`` will be used. fs : float The sample rate of the data. window : str | None Window function to use. Can be ``'hann'`` for Hann window, or None for no windowing. Returns ------- stft : ndarray Spectrogram of the data, shape (n_freqs, n_steps). See also -------- fft_freqs """ x = np.asarray(x, float) if x.ndim != 1: raise ValueError('x must be 1D') if window is not None: if window not in ('hann',): raise ValueError('window must be "hann" or None') w = np.hanning(n_fft) else: w = np.ones(n_fft) n_fft = int(n_fft) step = max(n_fft // 2, 1) if step is None else int(step) fs = float(fs) zero_pad = n_fft - len(x) if zero_pad > 0: x = np.concatenate((x, np.zeros(zero_pad, float))) n_freqs = n_fft // 2 + 1 n_estimates = (len(x) - n_fft) // step + 1 result = np.empty((n_freqs, n_estimates), np.complex128) for ii in range(n_estimates): result[:, ii] = np.fft.rfft(w * x[ii * step:ii * step + n_fft]) / n_fft return result def fft_freqs(n_fft, fs): """Return frequencies for DFT Parameters ---------- n_fft : int Number of points in the FFT. fs : float The sampling rate. """ return np.arange(0, (n_fft // 2 + 1)) / float(n_fft) * float(fs) ������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/util/fonts/�����������������������������������������������������������������������0000775�0001750�0001750�00000000000�12527674621�017657� 5����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/util/fonts/_freetype.py�����������������������������������������������������������0000664�0001750�0001750�00000004654�12527672621�022222� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2015, Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- # Use freetype to get glyph bitmaps import sys import numpy as np # Convert face to filename from ._vispy_fonts import _vispy_fonts, _get_vispy_font_filename if sys.platform.startswith('linux'): from ...ext.fontconfig import find_font elif sys.platform.startswith('win'): from ._win32 import find_font # noqa, analysis:ignore else: raise NotImplementedError _font_dict = {} # Nest freetype imports in case someone doesn't have freetype on their system # and isn't using fonts (Windows) def _load_font(face, bold, italic): from ...ext.freetype import Face key = '%s-%s-%s' % (face, bold, italic) if key in _font_dict: return _font_dict[key] if face in _vispy_fonts: fname = _get_vispy_font_filename(face, bold, italic) else: fname = find_font(face, bold, italic) font = Face(fname) _font_dict[key] = font return font def _load_glyph(f, char, glyphs_dict): """Load glyph from font into dict""" from ...ext.freetype import (FT_LOAD_RENDER, FT_LOAD_NO_HINTING, FT_LOAD_NO_AUTOHINT) flags = FT_LOAD_RENDER | FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT face = _load_font(f['face'], f['bold'], f['italic']) face.set_char_size(f['size'] * 64) # get the character of interest face.load_char(char, flags) bitmap = face.glyph.bitmap width = face.glyph.bitmap.width height = face.glyph.bitmap.rows bitmap = np.array(bitmap.buffer) w0 = bitmap.size // height if bitmap.size > 0 else 0 bitmap.shape = (height, w0) bitmap = bitmap[:, :width].astype(np.ubyte) left = face.glyph.bitmap_left top = face.glyph.bitmap_top advance = face.glyph.advance.x / 64. glyph = dict(char=char, offset=(left, top), bitmap=bitmap, advance=advance, kerning={}) glyphs_dict[char] = glyph # Generate kerning for other_char, other_glyph in glyphs_dict.items(): kerning = face.get_kerning(other_char, char) glyph['kerning'][other_char] = kerning.x / 64. kerning = face.get_kerning(char, other_char) other_glyph['kerning'][char] = kerning.x / 64. ������������������������������������������������������������������������������������vispy-0.4.0/vispy/util/fonts/_win32.py��������������������������������������������������������������0000664�0001750�0001750�00000010577�12527672621�021342� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2015, Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- import os from os import path as op import warnings from ctypes import (cast, byref, sizeof, create_unicode_buffer, c_void_p, c_wchar_p) from ...ext.gdi32plus import (gdiplus, gdi32, user32, winreg, LOGFONT, OUTLINETEXTMETRIC, GM_ADVANCED, FW_NORMAL, FW_BOLD, LF_FACESIZE, DEFAULT_CHARSET, TRUETYPE_FONTTYPE, FONTENUMPROC, BOOL) # Inspired by: # http://forums.codeguru.com/showthread.php?90792-How-to-get-a-system- # font-file-name-given-a-LOGFONT-face-name # XXX This isn't perfect, but it should work for now... def find_font(face, bold, italic): style_dict = {'Regular': 0, 'Bold': 1, 'Italic': 2, 'Bold Italic': 3} # Figure out which font to actually use by trying to instantiate by name dc = user32.GetDC(0) # noqa, analysis:ignore gdi32.SetGraphicsMode(dc, GM_ADVANCED) # only TT and OT fonts logfont = LOGFONT() logfont.lfHeight = -12 # conv point to pixels logfont.lfWeight = FW_BOLD if bold else FW_NORMAL logfont.lfItalic = italic logfont.lfFaceName = face # logfont needs Unicode hfont = gdi32.CreateFontIndirectW(byref(logfont)) original = gdi32.SelectObject(dc, hfont) n_byte = gdi32.GetOutlineTextMetricsW(dc, 0, None) assert n_byte > 0 metrics = OUTLINETEXTMETRIC() assert sizeof(metrics) >= n_byte assert gdi32.GetOutlineTextMetricsW(dc, n_byte, byref(metrics)) gdi32.SelectObject(dc, original) user32.ReleaseDC(None, dc) use_face = cast(byref(metrics, metrics.otmpFamilyName), c_wchar_p).value if use_face != face: warnings.warn('Could not find face match "%s", falling back to "%s"' % (face, use_face)) use_style = cast(byref(metrics, metrics.otmpStyleName), c_wchar_p).value use_style = style_dict.get(use_style, 'Regular') # AK: I get "Standaard" for use_style, which is Dutch for standard/regular # Now we match by creating private font collections until we find # the one that was used font_dir = op.join(os.environ['WINDIR'], 'Fonts') reg = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) key = 'SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts' reg_vals = winreg.OpenKey(reg, key) n_values = winreg.QueryInfoKey(reg_vals)[1] fname = None for vi in range(n_values): name, ff = winreg.EnumValue(reg_vals, vi)[:2] if name.endswith('(TrueType)'): ff = op.join(font_dir, ff) if op.basename(ff) == ff else ff assert op.isfile(ff) pc = c_void_p() assert gdiplus.GdipNewPrivateFontCollection(byref(pc)) == 0 gdiplus.GdipPrivateAddFontFile(pc, ff) family = c_void_p() if gdiplus.GdipCreateFontFamilyFromName(use_face, pc, byref(family)) == 0: val = BOOL() assert gdiplus.GdipIsStyleAvailable(family, use_style, byref(val)) == 0 if val.value: buf = create_unicode_buffer(LF_FACESIZE) assert gdiplus.GdipGetFamilyName(family, buf, 0) == 0 assert buf.value == use_face fname = ff if fname is None: raise RuntimeError('Could not find system font %s' % use_face) return fname def _list_fonts(): dc = user32.GetDC(0) gdi32.SetGraphicsMode(dc, GM_ADVANCED) # only TT and OT fonts logfont = LOGFONT() logfont.lfCharSet = DEFAULT_CHARSET logfont.lfFaceName = '' logfont.lfPitchandFamily = 0 fonts = list() def enum_fun(lp_logfont, lp_text_metric, font_type, l_param): # Only support TTF for now (silly Windows shortcomings) if font_type == TRUETYPE_FONTTYPE: font = lp_logfont.contents.lfFaceName if not font.startswith('@') and font not in fonts: fonts.append(font) return 1 gdi32.EnumFontFamiliesExW(dc, byref(logfont), FONTENUMPROC(enum_fun), 0, 0) user32.ReleaseDC(None, dc) return fonts ���������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/util/fonts/_triage.py�������������������������������������������������������������0000664�0001750�0001750�00000002152�12527672621�021641� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2015, Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- import sys from ._vispy_fonts import _vispy_fonts if sys.platform.startswith('linux'): from ._freetype import _load_glyph from ...ext.fontconfig import _list_fonts elif sys.platform == 'darwin': from ._quartz import _load_glyph, _list_fonts elif sys.platform.startswith('win'): from ._freetype import _load_glyph # noqa, analysis:ignore from ._win32 import _list_fonts # noqa, analysis:ignore else: raise NotImplementedError('unknown system %s' % sys.platform) _fonts = {} def list_fonts(): """List system fonts Returns ------- fonts : list of str List of system fonts. """ vals = _list_fonts() for font in _vispy_fonts: vals += [font] if font not in vals else [] vals = sorted(vals, key=lambda s: s.lower()) return vals ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/util/fonts/_quartz.py�������������������������������������������������������������0000664�0001750�0001750�00000020012�12527672621�021707� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2015, Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- # Use OSX cocoa/quartz to get glyph bitmaps import numpy as np from ctypes import byref, c_int32, c_byte from ...ext.cocoapy import cf, ct, quartz, CFRange, CFSTR, CGGlyph, UniChar, \ kCTFontFamilyNameAttribute, kCTFontBoldTrait, kCTFontItalicTrait, \ kCTFontSymbolicTrait, kCTFontTraitsAttribute, kCTFontAttributeName, \ kCGImageAlphaPremultipliedLast, kCFNumberSInt32Type, ObjCClass from ._vispy_fonts import _vispy_fonts, _get_vispy_font_filename _font_dict = {} def _load_vispy_font(face, bold, italic): # http://stackoverflow.com/questions/2703085/ # how-can-you-load-a-font-ttf-from-a-file-using-core-text fname = _get_vispy_font_filename(face, bold, italic) url = cf.CFURLCreateWithFileSystemPath(None, CFSTR(fname), 0, False) # data_provider = quartz.CGDataProviderCreateWithURL(url) # cg_font = quartz.CGFontCreateWithDataProvider(data_provider) # font = ct.CTFontCreateWithGraphicsFont(cg_font, 12., None, None) array = ct.CTFontManagerCreateFontDescriptorsFromURL(url) desc = cf.CFArrayGetValueAtIndex(array, 0) font = ct.CTFontCreateWithFontDescriptor(desc, 12., None) cf.CFRelease(array) cf.CFRelease(url) if not font: raise RuntimeError("Couldn't load font: %s" % face) key = '%s-%s-%s' % (face, bold, italic) _font_dict[key] = font return font def _load_font(face, bold, italic): key = '%s-%s-%s' % (face, bold, italic) if key in _font_dict: return _font_dict[key] if face in _vispy_fonts: return _load_vispy_font(face, bold, italic) traits = 0 traits |= kCTFontBoldTrait if bold else 0 traits |= kCTFontItalicTrait if italic else 0 # Create an attribute dictionary. args = [None, 0, cf.kCFTypeDictionaryKeyCallBacks, cf.kCFTypeDictionaryValueCallBacks] attributes = cf.CFDictionaryCreateMutable(*args) # Add family name to attributes. cfname = CFSTR(face) cf.CFDictionaryAddValue(attributes, kCTFontFamilyNameAttribute, cfname) cf.CFRelease(cfname) # Construct a CFNumber to represent the traits. itraits = c_int32(traits) sym_traits = cf.CFNumberCreate(None, kCFNumberSInt32Type, byref(itraits)) if sym_traits: # Construct a dictionary to hold the traits values. args = [None, 0, cf.kCFTypeDictionaryKeyCallBacks, cf.kCFTypeDictionaryValueCallBacks] traits_dict = cf.CFDictionaryCreateMutable(*args) if traits_dict: # Add CFNumber traits to traits dictionary. cf.CFDictionaryAddValue(traits_dict, kCTFontSymbolicTrait, sym_traits) # Add traits dictionary to attributes. cf.CFDictionaryAddValue(attributes, kCTFontTraitsAttribute, traits_dict) cf.CFRelease(traits_dict) cf.CFRelease(sym_traits) # Create font descriptor with attributes. desc = ct.CTFontDescriptorCreateWithAttributes(attributes) cf.CFRelease(attributes) font = ct.CTFontCreateWithFontDescriptor(desc, 12., None) if not font: raise RuntimeError("Couldn't load font: %s" % face) _font_dict[key] = font return font def _load_glyph(f, char, glyphs_dict): font = _load_font(f['face'], f['bold'], f['italic']) # resize loaded font args = [None, 0, cf.kCFTypeDictionaryKeyCallBacks, cf.kCFTypeDictionaryValueCallBacks] attributes = cf.CFDictionaryCreateMutable(*args) desc = ct.CTFontDescriptorCreateWithAttributes(attributes) cf.CFRelease(attributes) font = ct.CTFontCreateCopyWithAttributes(font, f['size'], None, desc) cf.CFRelease(desc) if not font: raise RuntimeError("Couldn't load font") # Create an attributed string using text and font. args = [None, 1, cf.kCFTypeDictionaryKeyCallBacks, cf.kCFTypeDictionaryValueCallBacks] attributes = cf.CFDictionaryCreateMutable(*args) cf.CFDictionaryAddValue(attributes, kCTFontAttributeName, font) string = cf.CFAttributedStringCreate(None, CFSTR(char), attributes) # Create a CTLine object to render the string. line = ct.CTLineCreateWithAttributedString(string) cf.CFRelease(string) cf.CFRelease(attributes) # Get a bounding rectangle for glyphs in string. chars = (UniChar * 1)(*map(ord, char)) glyphs = (CGGlyph * 1)() ct.CTFontGetGlyphsForCharacters(font, chars, glyphs, 1) rect = ct.CTFontGetBoundingRectsForGlyphs(font, 0, glyphs, None, 1) # Get advance for all glyphs in string. advance = ct.CTFontGetAdvancesForGlyphs(font, 1, glyphs, None, 1) width = max(int(np.ceil(rect.size.width) + 1), 1) height = max(int(np.ceil(rect.size.height) + 1), 1) left = rect.origin.x baseline = -rect.origin.y top = height - baseline bits_per_component = 8 bytes_per_row = 4*width color_space = quartz.CGColorSpaceCreateDeviceRGB() args = [None, width, height, bits_per_component, bytes_per_row, color_space, kCGImageAlphaPremultipliedLast] bitmap = quartz.CGBitmapContextCreate(*args) # Draw text to bitmap context. quartz.CGContextSetShouldAntialias(bitmap, True) quartz.CGContextSetTextPosition(bitmap, -left, baseline) ct.CTLineDraw(line, bitmap) cf.CFRelease(line) # Create an image to get the data out. image_ref = quartz.CGBitmapContextCreateImage(bitmap) assert quartz.CGImageGetBytesPerRow(image_ref) == bytes_per_row data_provider = quartz.CGImageGetDataProvider(image_ref) image_data = quartz.CGDataProviderCopyData(data_provider) buffer_size = cf.CFDataGetLength(image_data) assert buffer_size == width * height * 4 buffer = (c_byte * buffer_size)() byte_range = CFRange(0, buffer_size) cf.CFDataGetBytes(image_data, byte_range, buffer) quartz.CGImageRelease(image_ref) quartz.CGDataProviderRelease(image_data) cf.CFRelease(bitmap) cf.CFRelease(color_space) # reshape bitmap (don't know why it's only alpha on OSX...) bitmap = np.array(buffer, np.ubyte) bitmap.shape = (height, width, 4) bitmap = bitmap[:, :, 3].copy() glyph = dict(char=char, offset=(left, top), bitmap=bitmap, advance=advance, kerning={}) glyphs_dict[char] = glyph # Generate kerning for other_char, other_glyph in glyphs_dict.items(): glyph['kerning'][other_char] = (_get_k_p_a(font, other_char, char) - other_glyph['advance']) other_glyph['kerning'][char] = (_get_k_p_a(font, char, other_char) - glyph['advance']) cf.CFRelease(font) def _get_k_p_a(font, left, right): """This actually calculates the kerning + advance""" # http://lists.apple.com/archives/coretext-dev/2010/Dec/msg00020.html # 1) set up a CTTypesetter chars = left + right args = [None, 1, cf.kCFTypeDictionaryKeyCallBacks, cf.kCFTypeDictionaryValueCallBacks] attributes = cf.CFDictionaryCreateMutable(*args) cf.CFDictionaryAddValue(attributes, kCTFontAttributeName, font) string = cf.CFAttributedStringCreate(None, CFSTR(chars), attributes) typesetter = ct.CTTypesetterCreateWithAttributedString(string) cf.CFRelease(string) cf.CFRelease(attributes) # 2) extract a CTLine from it range = CFRange(0, 1) line = ct.CTTypesetterCreateLine(typesetter, range) # 3) use CTLineGetOffsetForStringIndex to get the character positions offset = ct.CTLineGetOffsetForStringIndex(line, 1, None) cf.CFRelease(line) cf.CFRelease(typesetter) return offset def _list_fonts(): manager = ObjCClass('NSFontManager').sharedFontManager() avail = manager.availableFontFamilies() fonts = [avail.objectAtIndex_(ii).UTF8String().decode('utf-8') for ii in range(avail.count())] return fonts ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/util/fonts/__init__.py������������������������������������������������������������0000664�0001750�0001750�00000001100�12527672621�021756� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2015, Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ The fonts module implements some helpful functions for dealing with system fonts. """ __all__ = ['list_fonts'] from ._triage import _load_glyph, list_fonts # noqa, analysis:ignore from ._vispy_fonts import _vispy_fonts # noqa, analysis:ignore ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/util/fonts/_vispy_fonts.py��������������������������������������������������������0000664�0001750�0001750�00000001360�12527672621�022751� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2015, Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- from ..fetching import load_data_file # List the vispy fonts made available online _vispy_fonts = ('OpenSans', 'Cabin') def _get_vispy_font_filename(face, bold, italic): """Fetch a remote vispy font""" name = face + '-' name += 'Regular' if not bold and not italic else '' name += 'Bold' if bold else '' name += 'Italic' if italic else '' name += '.ttf' return load_data_file('fonts/%s' % name) ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/util/fonts/tests/�����������������������������������������������������������������0000775�0001750�0001750�00000000000�12527674621�021021� 5����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/util/fonts/tests/__init__.py������������������������������������������������������0000664�0001750�0001750�00000000000�12375431476�023120� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/util/fonts/tests/test_font.py�����������������������������������������������������0000664�0001750�0001750�00000002631�12527672621�023400� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. import os import sys import numpy as np from nose.tools import assert_true, assert_equal import warnings from vispy.testing import assert_in, run_tests_if_main from vispy.util.fonts import list_fonts, _load_glyph, _vispy_fonts warnings.simplefilter('always') def test_font_list(): """Test font listing""" f = list_fonts() assert_true(len(f) > 0) for font in _vispy_fonts: assert_in(font, f) def test_font_glyph(): """Test loading glyphs""" # try both a vispy and system font sys_fonts = set(list_fonts()) - set(_vispy_fonts) assert_true(len(sys_fonts) > 0) for face in ('OpenSans', list(sys_fonts)[0]): font_dict = dict(face=face, size=12, bold=False, italic=False) glyphs_dict = dict() chars = 'foobar^C&#' if face != 'OpenSans' and os.getenv('APPVEYOR', '').lower() == 'true': continue # strange system font failure if 'true' in os.getenv('TRAVIS', '') and sys.version[0] == '3': continue # as of April 2015 strange FontConfig error on Travis for char in chars: # Warning that Arial might not exist _load_glyph(font_dict, char, glyphs_dict) assert_equal(len(glyphs_dict), np.unique([c for c in chars]).size) run_tests_if_main() �������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/util/quaternion.py����������������������������������������������������������������0000664�0001750�0001750�00000015525�12527672621�021273� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # Based on the quaternion class in the visvis project. import numpy as np class Quaternion(object): """ Quaternion(w=1, x=0, y=0, z=0, normalize=True) A quaternion is a mathematically convenient way to describe rotations. """ def __init__(self, w=1, x=0, y=0, z=0, normalize=True): self.w = float(w) self.x, self.y, self.z = float(x), float(y), float(z) if normalize: self._normalize() def __repr__(self): return "" % ( self.w, self.x, self.y, self.z) def copy(self): """ Create an exact copy of this quaternion. """ return Quaternion(self.w, self.x, self.y, self.z, False) def norm(self): """ Returns the norm of the quaternion norm = w**2 + x**2 + y**2 + z**2 """ tmp = self.w**2 + self.x**2 + self.y**2 + self.z**2 return tmp**0.5 def _normalize(self): """ Make the quaternion unit length. """ # Get length L = self.norm() if not L: raise ValueError('Quaternion cannot have 0-length.') # Correct self.w /= L self.x /= L self.y /= L self.z /= L def normalize(self): """ Returns a normalized (unit length) version of the quaternion. """ new = self.copy() new._normalize() return new def conjugate(self): """ Obtain the conjugate of the quaternion. This is simply the same quaternion but with the sign of the imaginary (vector) parts reversed. """ new = self.copy() new.x *= -1 new.y *= -1 new.z *= -1 return new def inverse(self): """ returns q.conjugate()/q.norm()**2 So if the quaternion is unit length, it is the same as the conjugate. """ new = self.conjugate() tmp = self.norm()**2 new.w /= tmp new.x /= tmp new.y /= tmp new.z /= tmp return new def exp(self): """ Returns the exponent of the quaternion. (not tested) """ # Init vecNorm = self.x**2 + self.y**2 + self.z**2 wPart = np.exp(self.w) q = Quaternion() # Calculate q.w = wPart * np.cos(vecNorm) q.x = wPart * self.x * np.sin(vecNorm) / vecNorm q.y = wPart * self.y * np.sin(vecNorm) / vecNorm q.z = wPart * self.z * np.sin(vecNorm) / vecNorm return q def log(self): """ Returns the natural logarithm of the quaternion. (not tested) """ # Init norm = self.norm() vecNorm = self.x**2 + self.y**2 + self.z**2 tmp = self.w / norm q = Quaternion() # Calculate q.w = np.log(norm) q.x = np.log(norm) * self.x * np.arccos(tmp) / vecNorm q.y = np.log(norm) * self.y * np.arccos(tmp) / vecNorm q.z = np.log(norm) * self.z * np.arccos(tmp) / vecNorm return q def __add__(self, q): """ Add quaternions. """ new = self.copy() new.w += q.w new.x += q.x new.y += q.y new.z += q.z return new def __sub__(self, q): """ Subtract quaternions. """ new = self.copy() new.w -= q.w new.x -= q.x new.y -= q.y new.z -= q.z return new def __mul__(self, q2): """ Multiply two quaternions. """ new = Quaternion() q1 = self new.w = q1.w*q2.w - q1.x*q2.x - q1.y*q2.y - q1.z*q2.z new.x = q1.w*q2.x + q1.x*q2.w + q1.y*q2.z - q1.z*q2.y new.y = q1.w*q2.y + q1.y*q2.w + q1.z*q2.x - q1.x*q2.z new.z = q1.w*q2.z + q1.z*q2.w + q1.x*q2.y - q1.y*q2.x return new def rotate_point(self, p): """ Rotate a Point instance using this quaternion. """ # Prepare p = Quaternion(0, p[0], p[1], p[2], False) # Do not normalize! q1 = self.normalize() q2 = self.inverse() # Apply rotation r = (q1*p)*q2 # Make point and return return r.x, r.y, r.z def get_matrix(self): """ Create a 4x4 homography matrix that represents the rotation of the quaternion. """ # Init matrix (remember, a matrix, not an array) a = np.zeros((4, 4), dtype=np.float32) w, x, y, z = self.w, self.x, self.y, self.z # First row a[0, 0] = - 2.0 * (y * y + z * z) + 1.0 a[1, 0] = + 2.0 * (x * y + z * w) a[2, 0] = + 2.0 * (x * z - y * w) a[3, 0] = 0.0 # Second row a[0, 1] = + 2.0 * (x * y - z * w) a[1, 1] = - 2.0 * (x * x + z * z) + 1.0 a[2, 1] = + 2.0 * (z * y + x * w) a[3, 1] = 0.0 # Third row a[0, 2] = + 2.0 * (x * z + y * w) a[1, 2] = + 2.0 * (y * z - x * w) a[2, 2] = - 2.0 * (x * x + y * y) + 1.0 a[3, 2] = 0.0 # Fourth row a[0, 3] = 0.0 a[1, 3] = 0.0 a[2, 3] = 0.0 a[3, 3] = 1.0 return a def get_axis_angle(self): """ Get the axis-angle representation of the quaternion. (The angle is in radians) """ # Init angle = 2 * np.arccos(max(min(self.w, 1.), -1.)) scale = (self.x**2 + self.y**2 + self.z**2)**0.5 # Calc axis if scale: ax = self.x / scale ay = self.y / scale az = self.z / scale else: # No rotation, so arbitrary axis ax, ay, az = 1, 0, 0 # Return return angle, ax, ay, az @classmethod def create_from_axis_angle(cls, angle, ax, ay, az, degrees=False): """ Classmethod to create a quaternion from an axis-angle representation. (angle should be in radians). """ if degrees: angle = np.radians(angle) while angle < 0: angle += np.pi*2 angle2 = angle/2.0 sinang2 = np.sin(angle2) return Quaternion(np.cos(angle2), ax*sinang2, ay*sinang2, az*sinang2) @classmethod def create_from_euler_angles(cls, rx, ry, rz, degrees=False): """ Classmethod to create a quaternion given the euler angles. """ if degrees: rx, ry, rz = np.radians([rx, ry, rz]) # Obtain quaternions qx = Quaternion(np.cos(rx/2), 0, 0, np.sin(rx/2)) qy = Quaternion(np.cos(ry/2), 0, np.sin(ry/2), 0) qz = Quaternion(np.cos(rz/2), np.sin(rz/2), 0, 0) # Almost done return qx*qy*qz ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/util/eq.py������������������������������������������������������������������������0000664�0001750�0001750�00000002115�12375431476�017504� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- from numpy import ndarray, bool_ def eq(a, b): """ The great missing equivalence function: Guaranteed evaluation to a single bool value. """ if a is b: return True if a is None or b is None: return True if a is None and b is None else False try: e = a == b except ValueError: return False except AttributeError: return False except Exception: print("a:", str(type(a)), str(a)) print("b:", str(type(b)), str(b)) raise t = type(e) if t is bool: return e elif t is bool_: return bool(e) elif isinstance(e, ndarray): try: # disaster: if a is empty and b is not, then e.all() is True if a.shape != b.shape: return False except Exception: return False if (hasattr(e, 'implements') and e.implements('MetaArray')): return e.asarray().all() else: return e.all() else: raise Exception("== operator returned type %s" % str(type(e))) ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/util/fetching.py������������������������������������������������������������������0000664�0001750�0001750�00000024140�12527672621�020666� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """Data downloading and reading functions """ from math import log import os from os import path as op import sys import shutil import time from ..ext.six.moves import urllib from ..ext.six import string_types from ..util.config import config ############################################################################### # Vispy data directory def load_data_file(fname, directory=None, force_download=False): """Get a standard vispy demo data file Parameters ---------- fname : str The filename on the remote ``demo-data`` repository to download, e.g. ``'molecular_viewer/micelle.npy'``. These correspond to paths on ``https://github.com/vispy/demo-data/``. directory : str | None Directory to use to save the file. By default, the vispy configuration directory is used. force_download : bool | str If True, the file will be downloaded even if a local copy exists (and this copy will be overwritten). Can also be a YYYY-MM-DD date to ensure a file is up-to-date (modified date of a file on disk, if present, is checked). Returns ------- fname : str The path to the file on the local system. """ _url_root = 'https://github.com/vispy/demo-data/raw/master/' url = _url_root + fname if directory is None: directory = config['data_path'] if directory is None: raise ValueError('config["data_path"] is not defined, ' 'so directory must be supplied') fname = op.join(directory, op.normcase(fname)) # convert to native if op.isfile(fname): if not force_download: # we're done return fname if isinstance(force_download, string_types): ntime = time.strptime(force_download, '%Y-%m-%d') ftime = time.gmtime(op.getctime(fname)) if ftime >= ntime: return fname else: print('File older than %s, updating...' % force_download) if not op.isdir(op.dirname(fname)): os.makedirs(op.abspath(op.dirname(fname))) # let's go get the file _fetch_file(url, fname) return fname ############################################################################### # File downloading (most adapted from mne-python) class ProgressBar(object): """Class for generating a command-line progressbar Parameters ---------- max_value : int Maximum value of process (e.g. number of samples to process, bytes to download, etc.). initial_value : int Initial value of process, useful when resuming process from a specific value, defaults to 0. mesg : str Message to include at end of progress bar. max_chars : int Number of characters to use for progress bar (be sure to save some room for the message and % complete as well). progress_character : char Character in the progress bar that indicates the portion completed. spinner : bool Show a spinner. Useful for long-running processes that may not increment the progress bar very often. This provides the user with feedback that the progress has not stalled. """ spinner_symbols = ['|', '/', '-', '\\'] template = '\r[{0}{1}] {2:.05f} {3} {4} ' def __init__(self, max_value, initial_value=0, mesg='', max_chars=40, progress_character='.', spinner=False): self.cur_value = initial_value self.max_value = float(max_value) self.mesg = mesg self.max_chars = max_chars self.progress_character = progress_character self.spinner = spinner self.spinner_index = 0 self.n_spinner = len(self.spinner_symbols) def update(self, cur_value, mesg=None): """Update progressbar with current value of process Parameters ---------- cur_value : number Current value of process. Should be <= max_value (but this is not enforced). The percent of the progressbar will be computed as (cur_value / max_value) * 100 mesg : str Message to display to the right of the progressbar. If None, the last message provided will be used. To clear the current message, pass a null string, ''. """ # Ensure floating-point division so we can get fractions of a percent # for the progressbar. self.cur_value = cur_value progress = float(self.cur_value) / self.max_value num_chars = int(progress * self.max_chars) num_left = self.max_chars - num_chars # Update the message if mesg is not None: self.mesg = mesg # The \r tells the cursor to return to the beginning of the line rather # than starting a new line. This allows us to have a progressbar-style # display in the console window. bar = self.template.format(self.progress_character * num_chars, ' ' * num_left, progress * 100, self.spinner_symbols[self.spinner_index], self.mesg) sys.stdout.write(bar) # Increament the spinner if self.spinner: self.spinner_index = (self.spinner_index + 1) % self.n_spinner # Force a flush because sometimes when using bash scripts and pipes, # the output is not printed until after the program exits. sys.stdout.flush() def update_with_increment_value(self, increment_value, mesg=None): """Update progressbar with the value of the increment instead of the current value of process as in update() Parameters ---------- increment_value : int Value of the increment of process. The percent of the progressbar will be computed as (self.cur_value + increment_value / max_value) * 100 mesg : str Message to display to the right of the progressbar. If None, the last message provided will be used. To clear the current message, pass a null string, ''. """ self.cur_value += increment_value self.update(self.cur_value, mesg) def _chunk_read(response, local_file, chunk_size=65536, initial_size=0): """Download a file chunk by chunk and show advancement Can also be used when resuming downloads over http. Parameters ---------- response: urllib.response.addinfourl Response to the download request in order to get file size. local_file: file Hard disk file where data should be written. chunk_size: integer, optional Size of downloaded chunks. Default: 8192 initial_size: int, optional If resuming, indicate the initial size of the file. """ # Adapted from NISL: # https://github.com/nisl/tutorial/blob/master/nisl/datasets.py bytes_so_far = initial_size # Returns only amount left to download when resuming, not the size of the # entire file total_size = int(response.headers['Content-Length'].strip()) total_size += initial_size progress = ProgressBar(total_size, initial_value=bytes_so_far, max_chars=40, spinner=True, mesg='downloading') while True: chunk = response.read(chunk_size) bytes_so_far += len(chunk) if not chunk: sys.stderr.write('\n') break _chunk_write(chunk, local_file, progress) def _chunk_write(chunk, local_file, progress): """Write a chunk to file and update the progress bar""" local_file.write(chunk) progress.update_with_increment_value(len(chunk)) def _fetch_file(url, file_name, print_destination=True): """Load requested file, downloading it if needed or requested Parameters ---------- url: string The url of file to be downloaded. file_name: string Name, along with the path, of where downloaded file will be saved. print_destination: bool, optional If true, destination of where file was saved will be printed after download finishes. """ # Adapted from NISL: # https://github.com/nisl/tutorial/blob/master/nisl/datasets.py temp_file_name = file_name + ".part" local_file = None initial_size = 0 # Checking file size and displaying it alongside the download url n_try = 3 for ii in range(n_try): try: data = urllib.request.urlopen(url, timeout=15.) except Exception as e: if ii == n_try - 1: raise RuntimeError('Error while fetching file %s.\n' 'Dataset fetching aborted (%s)' % (url, e)) try: file_size = int(data.headers['Content-Length'].strip()) print('Downloading data from %s (%s)' % (url, sizeof_fmt(file_size))) local_file = open(temp_file_name, "wb") _chunk_read(data, local_file, initial_size=initial_size) # temp file must be closed prior to the move if not local_file.closed: local_file.close() shutil.move(temp_file_name, file_name) if print_destination is True: sys.stdout.write('File saved as %s.\n' % file_name) except Exception as e: raise RuntimeError('Error while fetching file %s.\n' 'Dataset fetching aborted (%s)' % (url, e)) finally: if local_file is not None: if not local_file.closed: local_file.close() def sizeof_fmt(num): """Turn number of bytes into human-readable str""" units = ['bytes', 'kB', 'MB', 'GB', 'TB', 'PB'] decimals = [0, 0, 1, 2, 2, 2] """Human friendly file size""" if num > 1: exponent = min(int(log(num, 1024)), len(units) - 1) quotient = float(num) / 1024 ** exponent unit = units[exponent] num_decimals = decimals[exponent] format_string = '{0:.%sf} {1}' % (num_decimals) return format_string.format(quotient, unit) return '0 bytes' if num == 0 else '1 byte' ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/util/__init__.py������������������������������������������������������������������0000664�0001750�0001750�00000001227�12527672621�020637� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Utilities for Vispy. A collection of modules that are used in one or more Vispy sub-packages. """ from .logs import logger, set_log_level, use_log_level # noqa from .config import (config, sys_info, save_config, get_config_keys, # noqa set_data_dir, _TempDir) # noqa from .fetching import load_data_file # noqa from . import fonts # noqa from . import transforms # noqa from .wrappers import use, run_subprocess # noqa from .bunch import SimpleBunch # noqa �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/util/keys.py����������������������������������������������������������������������0000664�0001750�0001750�00000004701�12527672621�020053� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Define constants for keys. Each key constant is defined as a Key object, which allows comparison with strings (e.g. 'A', 'Escape', 'Shift'). This enables handling of key events without using the key constants explicitly (e.g. ``if ev.key == 'Left':``). In addition, key objects that represent characters can be matched to the integer ordinal (e.g. 32 for space, 65 for A). This behavior is mainly intended as a compatibility measure. """ from ..ext.six import string_types class Key(object): """ Represent the identity of a certain key. This represents one or more names that the key in question is known by. A Key object can be compared to one of its string names (case insensitive), to the integer ordinal of the key (only for keys that represent characters), and to another Key instance. """ def __init__(self, *names): self._names = names self._names_upper = tuple([v.upper() for v in names]) @property def name(self): """ The primary name of the key. """ return self._names[0] def __hash__(self): return self._names[0].__hash__() def __repr__(self): return "" % ', '.join([repr(v) for v in self._names]) def __eq__(self, other): if isinstance(other, string_types): return other.upper() in self._names_upper elif isinstance(other, Key): return self._names[0] == other elif isinstance(other, int): return other in [ord(v) for v in self._names_upper if len(v) == 1] elif other is None: return False else: raise ValueError('Key can only be compared to str, int and Key.') SHIFT = Key('Shift') CONTROL = Key('Control') ALT = Key('Alt') META = Key('Meta') # That Mac thingy UP = Key('Up') DOWN = Key('Down') LEFT = Key('Left') RIGHT = Key('Right') PAGEUP = Key('PageUp') PAGEDOWN = Key('PageDown') INSERT = Key('Insert') DELETE = Key('Delete') HOME = Key('Home') END = Key('End') ESCAPE = Key('Escape') BACKSPACE = Key('Backspace') F1 = Key('F1') F2 = Key('F2') F3 = Key('F3') F4 = Key('F4') F5 = Key('F5') F6 = Key('F6') F7 = Key('F7') F8 = Key('F8') F9 = Key('F9') F10 = Key('F10') F11 = Key('F11') F12 = Key('F12') SPACE = Key('Space', ' ') ENTER = Key('Enter', 'Return', '\n') TAB = Key('Tab', '\t') ���������������������������������������������������������������vispy-0.4.0/vispy/util/event.py���������������������������������������������������������������������0000664�0001750�0001750�00000070044�12527672621�020224� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ The event module implements the classes that make up the event system. The Event class and its subclasses are used to represent "stuff that happens". The EventEmitter class provides an interface to connect to events and to emit events. The EmitterGroup groups EventEmitter objects. For more information see http://github.com/vispy/vispy/wiki/API_Events """ from __future__ import division import inspect import weakref import traceback from .logs import logger, _handle_exception from ..ext.ordereddict import OrderedDict from ..ext.six import string_types class Event(object): """Class describing events that occur and can be reacted to with callbacks. Each event instance contains information about a single event that has occurred such as a key press, mouse motion, timer activation, etc. Subclasses: :class:`KeyEvent`, :class:`MouseEvent`, :class:`TouchEvent`, :class:`StylusEvent` The creation of events and passing of events to the appropriate callback functions is the responsibility of :class:`EventEmitter` instances. Note that each event object has an attribute for each of the input arguments listed below. Parameters ---------- type : str String indicating the event type (e.g. mouse_press, key_release) native : object (optional) The native GUI event object **kwargs : keyword arguments All extra keyword arguments become attributes of the event object. """ def __init__(self, type, native=None, **kwargs): # stack of all sources this event has been emitted through self._sources = [] self._handled = False self._blocked = False # Store args self._type = type self._native = native for k, v in kwargs.items(): setattr(self, k, v) @property def source(self): """The object that the event applies to (i.e. the source of the event). """ return self._sources[-1] if self._sources else None @property def sources(self): """ List of objects that the event applies to (i.e. are or have been a source of the event). Can contain multiple objects in case the event traverses a hierarchy of objects. """ return self._sources def _push_source(self, source): self._sources.append(source) def _pop_source(self): return self._sources.pop() @property def type(self): # No docstring; documeted in class docstring return self._type @property def native(self): # No docstring; documeted in class docstring return self._native @property def handled(self): """This boolean property indicates whether the event has already been acted on by an event handler. Since many handlers may have access to the same events, it is recommended that each check whether the event has already been handled as well as set handled=True if it decides to act on the event. """ return self._handled @handled.setter def handled(self, val): self._handled = bool(val) @property def blocked(self): """This boolean property indicates whether the event will be delivered to event callbacks. If it is set to True, then no further callbacks will receive the event. When possible, it is recommended to use Event.handled rather than Event.blocked. """ return self._blocked @blocked.setter def blocked(self, val): self._blocked = bool(val) def __repr__(self): # Try to generate a nice string representation of the event that # includes the interesting properties. # need to keep track of depth because it is # very difficult to avoid excessive recursion. global _event_repr_depth _event_repr_depth += 1 try: if _event_repr_depth > 2: return "<...>" attrs = [] for name in dir(self): if name.startswith('_'): continue # select only properties if not hasattr(type(self), name) or \ not isinstance(getattr(type(self), name), property): continue attr = getattr(self, name) attrs.append("%s=%s" % (name, attr)) return "<%s %s>" % (self.__class__.__name__, " ".join(attrs)) finally: _event_repr_depth -= 1 def __str__(self): """Shorter string representation""" return self.__class__.__name__ _event_repr_depth = 0 class EventEmitter(object): """Encapsulates a list of event callbacks. Each instance of EventEmitter represents the source of a stream of similar events, such as mouse click events or timer activation events. For example, the following diagram shows the propagation of a mouse click event to the list of callbacks that are registered to listen for that event:: User clicks |Canvas creates mouse on |MouseEvent: |'mouse_press' EventEmitter: |callbacks in sequence: # noqa Canvas | | | # noqa -->|event = MouseEvent(...) -->|Canvas.events.mouse_press(event) -->|callback1(event) # noqa | | -->|callback2(event) # noqa | | -->|callback3(event) # noqa Callback functions may be added or removed from an EventEmitter using :func:`connect() ` or :func:`disconnect() `. Calling an instance of EventEmitter will cause each of its callbacks to be invoked in sequence. All callbacks are invoked with a single argument which will be an instance of :class:`Event `. EventEmitters are generally created by an EmitterGroup instance. Parameters ---------- source : object The object that the generated events apply to. All emitted Events will have their .source property set to this value. type : str or None String indicating the event type (e.g. mouse_press, key_release) event_class : subclass of Event The class of events that this emitter will generate. """ def __init__(self, source=None, type=None, event_class=Event): self._callbacks = [] self._callback_refs = [] # count number of times this emitter is blocked for each callback. self._blocked = {None: 0} # used to detect emitter loops self._emitting = False self.source = source self.default_args = {} if type is not None: self.default_args['type'] = type assert inspect.isclass(event_class) self.event_class = event_class self._ignore_callback_errors = True self.print_callback_errors = 'reminders' @property def ignore_callback_errors(self): """Whether exceptions during callbacks will be caught by the emitter This allows it to continue invoking other callbacks if an error occurs. """ return self._ignore_callback_errors @ignore_callback_errors.setter def ignore_callback_errors(self, val): self._ignore_callback_errors = val @property def print_callback_errors(self): """Print a message and stack trace if a callback raises an exception Valid values are "first" (only show first instance), "reminders" (show complete first instance, then counts), "always" (always show full traceback), or "never". This assumes ignore_callback_errors=True. These will be raised as warnings, so ensure that the vispy logging level is set to at least "warning". """ return self._print_callback_errors @print_callback_errors.setter def print_callback_errors(self, val): if val not in ('first', 'reminders', 'always', 'never'): raise ValueError('print_callback_errors must be "first", ' '"reminders", "always", or "never"') self._print_callback_errors = val @property def callback_refs(self): """The set of callback references""" return tuple(self._callback_refs) @property def callbacks(self): """The set of callbacks""" return tuple(self._callbacks) @property def source(self): """The object that events generated by this emitter apply to""" return None if self._source is None else self._source( ) # get object behind weakref @source.setter def source(self, s): if s is None: self._source = None else: self._source = weakref.ref(s) def connect(self, callback, ref=False, position='first', before=None, after=None): """Connect this emitter to a new callback. Parameters ---------- callback : function | tuple *callback* may be either a callable object or a tuple (object, attr_name) where object.attr_name will point to a callable object. Note that only a weak reference to ``object`` will be kept. ref : bool | str Reference used to identify the callback in ``before``/``after``. If True, the callback ref will automatically determined (see Notes). If False, the callback cannot be referred to by a string. If str, the given string will be used. Note that if ``ref`` is not unique in ``callback_refs``, an error will be thrown. position : str If ``'first'``, the first eligible position is used (that meets the before and after criteria), ``'last'`` will use the last position. before : str | callback | list of str or callback | None List of callbacks that the current callback should precede. Can be None if no before-criteria should be used. after : str | callback | list of str or callback | None List of callbacks that the current callback should follow. Can be None if no after-criteria should be used. Notes ----- If ``ref=True``, the callback reference will be determined from: 1. If ``callback`` is ``tuple``, the secend element in the tuple. 2. The ``__name__`` attribute. 3. The ``__class__.__name__`` attribute. The current list of callback refs can be obtained using ``event.callback_refs``. Callbacks can be referred to by either their string reference (if given), or by the actual callback that was attached (e.g., ``(canvas, 'swap_buffers')``). If the specified callback is already connected, then the request is ignored. If before is None and after is None (default), the new callback will be added to the beginning of the callback list. Thus the callback that is connected _last_ will be the _first_ to receive events from the emitter. """ callbacks = self.callbacks callback_refs = self.callback_refs if callback in callbacks: return # always use a weak ref if isinstance(callback, tuple): callback = (weakref.ref(callback[0]),) + callback[1:] # deal with the ref if isinstance(ref, bool): if ref: if isinstance(callback, tuple): ref = callback[1] elif hasattr(callback, '__name__'): # function ref = callback.__name__ else: # Method, or other ref = callback.__class__.__name__ else: ref = None elif not isinstance(ref, string_types): raise TypeError('ref must be a bool or string') if ref is not None and ref in self._callback_refs: raise ValueError('ref "%s" is not unique' % ref) # positions if position not in ('first', 'last'): raise ValueError('position must be "first" or "last", not %s' % position) # bounds bounds = list() # upper & lower bnds (inclusive) of possible cb locs for ri, criteria in enumerate((before, after)): if criteria is None or criteria == []: bounds.append(len(callback_refs) if ri == 0 else 0) else: if not isinstance(criteria, list): criteria = [criteria] for c in criteria: count = sum([(c == cn or c == cc) for cn, cc in zip(callback_refs, callbacks)]) if count != 1: raise ValueError('criteria "%s" is in the current ' 'callback list %s times:\n%s\n%s' % (criteria, count, callback_refs, callbacks)) matches = [ci for ci, (cn, cc) in enumerate(zip(callback_refs, callbacks)) if (cc in criteria or cn in criteria)] bounds.append(matches[0] if ri == 0 else (matches[-1] + 1)) if bounds[0] < bounds[1]: # i.e., "place before" < "place after" raise RuntimeError('cannot place callback before "%s" ' 'and after "%s" for callbacks: %s' % (before, after, callback_refs)) idx = bounds[1] if position == 'first' else bounds[0] # 'last' # actually add the callback self._callbacks.insert(idx, callback) self._callback_refs.insert(idx, ref) return callback # allows connect to be used as a decorator def disconnect(self, callback=None): """Disconnect a callback from this emitter. If no callback is specified, then *all* callbacks are removed. If the callback was not already connected, then the call does nothing. """ if callback is None: self._callbacks = [] self._callback_refs = [] else: if isinstance(callback, tuple): callback = (weakref.ref(callback[0]),) + callback[1:] if callback in self._callbacks: idx = self._callbacks.index(callback) self._callbacks.pop(idx) self._callback_refs.pop(idx) def __call__(self, *args, **kwargs): """ __call__(**kwargs) Invoke all callbacks for this emitter. Emit a new event object, created with the given keyword arguments, which must match with the input arguments of the corresponding event class. Note that the 'type' argument is filled in by the emitter. Alternatively, the emitter can also be called with an Event instance as the only argument. In this case, the specified Event will be used rather than generating a new one. This allows customized Event instances to be emitted and also allows EventEmitters to be chained by connecting one directly to another. Note that the same Event instance is sent to all callbacks. This allows some level of communication between the callbacks (notably, via Event.handled) but also requires that callbacks be careful not to inadvertently modify the Event. """ # This is a VERY highly used method; must be fast! blocked = self._blocked if self._emitting: raise RuntimeError('EventEmitter loop detected!') # create / massage event as needed event = self._prepare_event(*args, **kwargs) # Add our source to the event; remove it after all callbacks have been # invoked. event._push_source(self.source) self._emitting = True try: if blocked.get(None, 0) > 0: # this is the same as self.blocked() return event for cb in self._callbacks: if blocked.get(cb, 0) > 0: continue if isinstance(cb, tuple): cb = getattr(cb[0](), cb[1], None) if cb is None: continue self._invoke_callback(cb, event) if event.blocked: break finally: self._emitting = False if event._pop_source() != self.source: raise RuntimeError("Event source-stack mismatch.") return event def _invoke_callback(self, cb, event): try: cb(event) except Exception: _handle_exception(self.ignore_callback_errors, self.print_callback_errors, self, cb_event=(cb, event)) def _prepare_event(self, *args, **kwargs): # When emitting, this method is called to create or otherwise alter # an event before it is sent to callbacks. Subclasses may extend # this method to make custom modifications to the event. if len(args) == 1 and not kwargs and isinstance(args[0], Event): event = args[0] # Ensure that the given event matches what we want to emit assert isinstance(event, self.event_class) elif not args: args = self.default_args.copy() args.update(kwargs) event = self.event_class(**args) else: raise ValueError("Event emitters can be called with an Event " "instance or with keyword arguments only.") return event def blocked(self, callback=None): """Return boolean indicating whether the emitter is blocked for the given callback. """ return self._blocked.get(callback, 0) > 0 def block(self, callback=None): """Block this emitter. Any attempts to emit an event while blocked will be silently ignored. If *callback* is given, then the emitter is only blocked for that specific callback. Calls to block are cumulative; the emitter must be unblocked the same number of times as it is blocked. """ self._blocked[callback] = self._blocked.get(callback, 0) + 1 def unblock(self, callback=None): """ Unblock this emitter. See :func:`event.EventEmitter.block`. Note: Use of ``unblock(None)`` only reverses the effect of ``block(None)``; it does not unblock callbacks that were explicitly blocked using ``block(callback)``. """ if callback not in self._blocked or self._blocked[callback] == 0: raise RuntimeError("Cannot unblock %s for callback %s; emitter " "was not previously blocked." % (self, callback)) b = self._blocked[callback] - 1 if b == 0 and callback is not None: del self._blocked[callback] else: self._blocked[callback] = b def blocker(self, callback=None): """Return an EventBlocker to be used in 'with' statements Notes ----- For example, one could do:: with emitter.blocker(): pass # ..do stuff; no events will be emitted.. """ return EventBlocker(self, callback) class WarningEmitter(EventEmitter): """ EventEmitter subclass used to allow deprecated events to be used with a warning message. """ def __init__(self, message, *args, **kwargs): self._message = message self._warned = False EventEmitter.__init__(self, *args, **kwargs) def connect(self, cb, *args, **kwargs): self._warn(cb) return EventEmitter.connect(self, cb, *args, **kwargs) def _invoke_callback(self, cb, event): self._warn(cb) return EventEmitter._invoke_callback(self, cb, event) def _warn(self, cb): if self._warned: return # don't warn about unimplemented connections if isinstance(cb, tuple) and getattr(cb[0], cb[1], None) is None: return traceback.print_stack() logger.warning(self._message) self._warned = True class EmitterGroup(EventEmitter): """EmitterGroup instances manage a set of related :class:`EventEmitters `. Its primary purpose is to provide organization for objects that make use of multiple emitters and to reduce the boilerplate code needed to initialize those emitters with default connections. EmitterGroup instances are usually stored as an 'events' attribute on objects that use multiple emitters. For example:: EmitterGroup EventEmitter | | Canvas.events.mouse_press Canvas.events.resized Canvas.events.key_press EmitterGroup is also a subclass of :class:`EventEmitters `, allowing it to emit its own events. Any callback that connects directly to the EmitterGroup will receive *all* of the events generated by the group's emitters. Parameters ---------- source : object The object that the generated events apply to. auto_connect : bool If *auto_connect* is True (default), then one connection will be made for each emitter that looks like :func:`emitter.connect((source, 'on_' + event_name)) `. This provides a simple mechanism for automatically connecting a large group of emitters to default callbacks. emitters : keyword arguments See the :func:`add ` method. """ def __init__(self, source=None, auto_connect=True, **emitters): EventEmitter.__init__(self, source) self.auto_connect = auto_connect self.auto_connect_format = "on_%s" self._emitters = OrderedDict() # whether the sub-emitters have been connected to the group: self._emitters_connected = False self.add(**emitters) def __getitem__(self, name): """ Return the emitter assigned to the specified name. Note that emitters may also be retrieved as an attribute of the EmitterGroup. """ return self._emitters[name] def __setitem__(self, name, emitter): """ Alias for EmitterGroup.add(name=emitter) """ self.add(**{name: emitter}) def add(self, auto_connect=None, **kwargs): """ Add one or more EventEmitter instances to this emitter group. Each keyword argument may be specified as either an EventEmitter instance or an Event subclass, in which case an EventEmitter will be generated automatically:: # This statement: group.add(mouse_press=MouseEvent, mouse_release=MouseEvent) # ..is equivalent to this statement: group.add(mouse_press=EventEmitter(group.source, 'mouse_press', MouseEvent), mouse_release=EventEmitter(group.source, 'mouse_press', MouseEvent)) """ if auto_connect is None: auto_connect = self.auto_connect # check all names before adding anything for name in kwargs: if name in self._emitters: raise ValueError( "EmitterGroup already has an emitter named '%s'" % name) elif hasattr(self, name): raise ValueError("The name '%s' cannot be used as an emitter; " "it is already an attribute of EmitterGroup" % name) # add each emitter specified in the keyword arguments for name, emitter in kwargs.items(): if emitter is None: emitter = Event if inspect.isclass(emitter) and issubclass(emitter, Event): emitter = EventEmitter( source=self.source, type=name, event_class=emitter) elif not isinstance(emitter, EventEmitter): raise Exception('Emitter must be specified as either an ' 'EventEmitter instance or Event subclass. ' '(got %s=%s)' % (name, emitter)) # give this emitter the same source as the group. emitter.source = self.source setattr(self, name, emitter) self._emitters[name] = emitter if auto_connect and self.source is not None: emitter.connect((self.source, self.auto_connect_format % name)) # If emitters are connected to the group already, then this one # should be connected as well. if self._emitters_connected: emitter.connect(self) @property def emitters(self): """ List of current emitters in this group. """ return self._emitters def __iter__(self): """ Iterates over the names of emitters in this group. """ for k in self._emitters: yield k def block_all(self): """ Block all emitters in this group. """ self.block() for em in self._emitters.values(): em.block() def unblock_all(self): """ Unblock all emitters in this group. """ self.unblock() for em in self._emitters.values(): em.unblock() def connect(self, callback, ref=False, position='first', before=None, after=None): """ Connect the callback to the event group. The callback will receive events from *all* of the emitters in the group. See :func:`EventEmitter.connect() ` for arguments. """ self._connect_emitters(True) return EventEmitter.connect(self, callback, ref, position, before, after) def disconnect(self, callback=None): """ Disconnect the callback from this group. See :func:`connect() ` and :func:`EventEmitter.connect() ` for more information. """ ret = EventEmitter.disconnect(self, callback) if len(self._callbacks) == 0: self._connect_emitters(False) return ret def _connect_emitters(self, connect): # Connect/disconnect all sub-emitters from the group. This allows the # group to emit an event whenever _any_ of the sub-emitters emit, # while simultaneously eliminating the overhead if nobody is listening. if connect: for emitter in self: self[emitter].connect(self) else: for emitter in self: self[emitter].disconnect(self) self._emitters_connected = connect @property def ignore_callback_errors(self): return super(EventEmitter, self).ignore_callback_errors @ignore_callback_errors.setter def ignore_callback_errors(self, ignore): EventEmitter.ignore_callback_errors.fset(self, ignore) for emitter in self._emitters.values(): if isinstance(emitter, EventEmitter): emitter.ignore_callback_errors = ignore elif isinstance(emitter, EmitterGroup): emitter.ignore_callback_errors_all(ignore) class EventBlocker(object): """ Represents a block for an EventEmitter to be used in a context manager (i.e. 'with' statement). """ def __init__(self, target, callback=None): self.target = target self.callback = callback def __enter__(self): self.target.block(self.callback) def __exit__(self, *args): self.target.unblock(self.callback) ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/util/tests/�����������������������������������������������������������������������0000775�0001750�0001750�00000000000�12527674621�017670� 5����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/util/tests/test_vispy.py����������������������������������������������������������0000664�0001750�0001750�00000002770�12527672621�022457� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Tests to ensure that base vispy namespace functions correctly, including configuration options. """ import vispy.app from vispy.testing import (requires_application, run_tests_if_main, assert_raises, assert_equal, assert_not_equal) @requires_application('pyside') def test_use(): # Set default app to None, so we can test the use function vispy.app.use_app() default_app = vispy.app._default_app.default_app vispy.app._default_app.default_app = None app_name = default_app.backend_name.split(' ')[0] try: # With no arguments, should do nothing assert_raises(TypeError, vispy.use) assert_equal(vispy.app._default_app.default_app, None) # With only gl args, should do nothing to app vispy.use(gl='gl2') assert_equal(vispy.app._default_app.default_app, None) # Specify app (one we know works) vispy.use(app_name) assert_not_equal(vispy.app._default_app.default_app, None) # Again, but now wrong app wrong_name = 'glfw' if app_name.lower() != 'glfw' else 'pyqt4' assert_raises(RuntimeError, vispy.use, wrong_name) # And both vispy.use(app_name, 'gl2') finally: # Restore vispy.app._default_app.default_app = default_app run_tests_if_main() ��������vispy-0.4.0/vispy/util/tests/test_key.py������������������������������������������������������������0000664�0001750�0001750�00000001174�12527672621�022072� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from vispy.util.keys import Key, ENTER from vispy.testing import (run_tests_if_main, assert_raises, assert_true, assert_equal) def test_key(): """Test basic key functionality""" def bad(): return (ENTER == dict()) assert_raises(ValueError, bad) assert_true(not (ENTER == None)) # noqa assert_equal('Return', ENTER) print(ENTER.name) print(ENTER) # __repr__ assert_equal(Key('1'), 49) # ASCII code run_tests_if_main() ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/util/tests/test_import.py���������������������������������������������������������0000664�0001750�0001750�00000010532�12527672621�022612� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Test that importing vispy subpackages do not pull in any more vispy submodules than strictly necessary. """ import sys import os from vispy.testing import (assert_in, assert_not_in, requires_pyopengl, run_tests_if_main, assert_equal) from vispy.util import run_subprocess import vispy # minimum that will be imported when importing vispy _min_modules = ['vispy', 'vispy.util', 'vispy.ext', 'vispy.testing'] def loaded_vispy_modules(import_module, depth=None, all_modules=False): """ Import the given module in subprocess and return loaded modules Import a certain module in a clean subprocess and return the vispy modules that are subsequently loaded. The given depth indicates the module level (i.e. depth=1 will only yield 'vispy.app' but not 'vispy.app.backends'). """ vispy_dir = os.path.dirname(os.path.dirname(vispy.__file__)) # Get the loaded modules in a clean interpreter code = "import sys, %s; print(', '.join(sys.modules))" % import_module res = run_subprocess([sys.executable, '-c', code], cwd=vispy_dir)[0] loaded_modules = [name.strip() for name in res.split(',')] if all_modules: return loaded_modules # Get only vispy modules at the given depth vispy_modules = set() for m in loaded_modules: if m.startswith('vispy') and '__future__' not in m: if depth: parts = m.split('.') m = '.'.join(parts[:depth]) vispy_modules.add(m) return vispy_modules def test_import_nothing(): """ Not importing vispy should not import any vispy modules. """ modnames = loaded_vispy_modules('os', 2) assert_equal(modnames, set()) def test_import_vispy(): """ Importing vispy should only pull in other vispy.util submodule. """ modnames = loaded_vispy_modules('vispy', 2) assert_equal(modnames, set(_min_modules)) def test_import_vispy_util(): """ Importing vispy.util should not pull in other vispy submodules. """ modnames = loaded_vispy_modules('vispy.util', 2) assert_equal(modnames, set(_min_modules)) def test_import_vispy_app1(): """ Importing vispy.app should not pull in other vispy submodules. """ # Since the introduction of the GLContext to gloo, app depends on gloo modnames = loaded_vispy_modules('vispy.app', 2) assert_equal(modnames, set(_min_modules + ['vispy.app', 'vispy.gloo', 'vispy.glsl', 'vispy.color'])) def test_import_vispy_app2(): """ Importing vispy.app should not pull in any backend toolkit. """ allmodnames = loaded_vispy_modules('vispy.app', 2, True) assert_not_in('PySide', allmodnames) assert_not_in('PyQt4', allmodnames) assert_not_in('pyglet', allmodnames) def test_import_vispy_gloo(): """ Importing vispy.gloo should not pull in other vispy submodules. """ modnames = loaded_vispy_modules('vispy.gloo', 2) assert_equal(modnames, set(_min_modules + ['vispy.gloo', 'vispy.glsl', 'vispy.color'])) def test_import_vispy_no_pyopengl(): """ Importing vispy.gloo.gl.gl2 should not import PyOpenGL. """ # vispy.gloo desktop backend allmodnames = loaded_vispy_modules('vispy.gloo.gl.gl2', 2, True) assert_not_in('OpenGL', allmodnames) # vispy.app allmodnames = loaded_vispy_modules('vispy.app', 2, True) assert_not_in('OpenGL', allmodnames) # vispy.scene allmodnames = loaded_vispy_modules('vispy.scene', 2, True) assert_not_in('OpenGL', allmodnames) @requires_pyopengl() def test_import_vispy_pyopengl(): """ Importing vispy.gloo.gl.pyopengl2 should import PyOpenGL. """ allmodnames = loaded_vispy_modules('vispy.gloo.gl.pyopengl2', 2, True) assert_in('OpenGL', allmodnames) def test_import_vispy_scene(): """ Importing vispy.gloo.gl.desktop should not import PyOpenGL. """ modnames = loaded_vispy_modules('vispy.scene', 2) more_modules = ['vispy.app', 'vispy.gloo', 'vispy.glsl', 'vispy.scene', 'vispy.color', 'vispy.io', 'vispy.geometry', 'vispy.visuals'] assert_equal(modnames, set(_min_modules + more_modules)) run_tests_if_main() ����������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/util/tests/test_emitter_group.py��������������������������������������������������0000664�0001750�0001750�00000017211�12527672621�024166� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. import unittest import copy from vispy.util.event import Event, EventEmitter, EmitterGroup from vispy.util import use_log_level from vispy.testing import run_tests_if_main, assert_true, assert_raises class BasicEvent(Event): pass class TypedEvent(Event): def __init__(self, **kwargs): kwargs['type'] = 'typed_event' Event.__init__(self, **kwargs) class TestGroups(unittest.TestCase): def test_group_construction(self): """EmitterGroup basic construction""" grp = EmitterGroup(em1=Event, em2=BasicEvent, em3=TypedEvent) grp.em1.connect(self.record_event) grp.em2.connect(self.record_event) grp.em3.connect(self.record_event) self.result = None ev = grp.em1() self.assert_result(event=ev, type='em1', event_class=Event) ev = grp.em2() self.assert_result(event=ev, type='em2', event_class=BasicEvent) ev = grp.em3() self.assert_result( event=ev, type='typed_event', event_class=TypedEvent) def test_group_add_emitter(self): """EmitterGroup.add""" grp = EmitterGroup(em1=Event) grp.em1.connect(self.record_event) self.result = None ev = grp.em1() self.assert_result(event=ev, type='em1') grp.add(em2=BasicEvent) grp.em2.connect(self.record_event) ev = grp.em2() self.assert_result(event=ev, type='em2', event_class=BasicEvent) grp.add(em3=TypedEvent) grp.em3.connect(self.record_event) ev = grp.em3(test_key=2) self.assert_result( event=ev, type='typed_event', event_class=TypedEvent, test_key=2) try: grp.add(em3=Event) assert False, "Double-added emitter" except ValueError: pass try: grp.add(add=Event) assert False, "Added event with invalid name" except ValueError: pass def test_group_block(self): """EmitterGroup.block_all""" grp = EmitterGroup(em1=Event, em2=Event) def cb(ev): self.result = 1 grp.em1.connect(self.record_event) grp.em2.connect(self.record_event) grp.connect(cb) self.result = None grp.block_all() try: grp.em1() grp.em2() grp(type='test_event') finally: grp.unblock_all() assert self.result is None def test_group_ignore(self): """EmitterGroup.block_all""" grp = EmitterGroup(em1=Event) grp.em1.connect(self.error_event) with use_log_level('warning', record=True, print_msg=False) as l: grp.em1() assert_true(len(l) >= 1) grp.ignore_callback_errors = False assert_raises(RuntimeError, grp.em1) grp.ignore_callback_errors = True with use_log_level('warning', record=True, print_msg=False) as l: grp.em1() assert_true(len(l) >= 1) def test_group_disconnect(self): """EmitterGroup.disconnect""" grp = EmitterGroup(em1=Event) assert len(grp.em1.callbacks) == 0, grp.em1.callbacks grp.connect(self.record_event) assert len(grp.em1.callbacks) == 1 grp.add(em2=Event) assert len(grp.em2.callbacks) == 1 grp.disconnect() assert len(grp.em1.callbacks) == 0 assert len(grp.em2.callbacks) == 0 def test_group_autoconnect(self): """EmitterGroup auto-connect""" class Source: def on_em1(self, ev): self.result = 1 def em2_event(self, ev): self.result = 2 def em3_event(self, ev): self.result = 3 src = Source() grp = EmitterGroup(source=src, em1=Event, auto_connect=False) src.result = None grp.em1() assert src.result is None grp = EmitterGroup(source=src, em1=Event, auto_connect=True) src.result = None grp.em1() assert src.result == 1 grp.auto_connect_format = "%s_event" grp.add(em2=Event) src.result = None grp.em2() assert src.result == 2 grp.add(em3=Event, auto_connect=False) src.result = None grp.em3() assert src.result is None def test_add_custom_emitter(self): class Emitter(EventEmitter): def _prepare_event(self, *args, **kwargs): ev = super(Emitter, self)._prepare_event(*args, **kwargs) ev.test_key = 1 return ev class Source: pass src = Source() grp = EmitterGroup(source=src, em1=Emitter(type='test_event1')) grp.em1.connect(self.record_event) self.result = None ev = grp.em1() self.assert_result( event=ev, test_key=1, type='test_event1', source=src) grp.add(em2=Emitter(type='test_event2')) grp.em2.connect(self.record_event) self.result = None ev = grp.em2() self.assert_result( event=ev, test_key=1, type='test_event2', source=src) def test_group_connect(self): grp = EmitterGroup(source=self, em1=Event) grp.connect(self.record_event) self.result = None ev = grp.em1(test_key=1) self.assert_result( event=ev, source=self, sources=[ self, self], test_key=1) def record_event(self, ev, key=None): # get a copy of all event attributes because these may change # as the event is passed around; we want to know exactly what the event # looked like when it reached this callback. names = [name for name in dir(ev) if name[0] != '_'] attrs = {} for name in names: val = getattr(ev, name) if name == 'source': attrs[name] = val elif name == 'sources': attrs[name] = val[:] else: try: attrs[name] = copy.deepcopy(val) except Exception: try: attrs[name] = copy.copy(val) except Exception: attrs[name] = val if key is None: self.result = ev, attrs else: if not hasattr(self, 'result') or self.result is None: self.result = {} self.result[key] = ev, attrs def error_event(self, ev, key=None): raise RuntimeError('Errored') def assert_result(self, key=None, **kwargs): assert (hasattr(self, 'result') and self.result is not None), \ "No event recorded" if key is None: event, event_attrs = self.result else: event, event_attrs = self.result[key] assert isinstance(event, Event), "Emitted object is not Event instance" for name, val in kwargs.items(): if name == 'event': assert event is val, "Event objects do not match" elif name == 'event_class': assert isinstance(event, val), \ "Emitted object is not instance of %s" % val.__name__ else: attr = event_attrs[name] assert (attr == val), "Event.%s != %s (%s)" % ( name, str(val), str(attr)) run_tests_if_main() ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/util/tests/test_logging.py��������������������������������������������������������0000664�0001750�0001750�00000002622�12527672621�022727� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. import logging from vispy.util import logger, use_log_level from vispy.testing import (assert_in, assert_not_in, run_tests_if_main, assert_equal) def test_logging(): """Test logging context manager""" ll = logger.level with use_log_level('warning', print_msg=False): assert_equal(logger.level, logging.WARN) assert_equal(logger.level, ll) with use_log_level('debug', print_msg=False): assert_equal(logger.level, logging.DEBUG) assert_equal(logger.level, ll) def test_debug_logging(): """Test advanced debugging logging""" with use_log_level('debug', 'Selected', True, False) as l: logger.debug('Selected foo') assert_equal(len(l), 1) assert_in('test_logging', l[0]) # can't really parse this location with use_log_level('debug', record=True, print_msg=False) as l: logger.debug('foo') assert_equal(len(l), 1) assert_in('test_logging', l[0]) with use_log_level('debug', 'foo', True, False) as l: logger.debug('bar') assert_equal(len(l), 0) with use_log_level('info', record=True, print_msg=False) as l: logger.debug('foo') logger.info('bar') assert_equal(len(l), 1) assert_not_in('unknown', l[0]) run_tests_if_main() ��������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/util/tests/__init__.py������������������������������������������������������������0000664�0001750�0001750�00000000000�12375431476�021767� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/util/tests/test_docstring_parameters.py�������������������������������������������0000664�0001750�0001750�00000010211�12527672621�025511� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# TODO inspect for Cython (see sagenb.misc.sageinspect) from __future__ import print_function from nose.plugins.skip import SkipTest from os import path as op import inspect import warnings import imp from vispy.testing import run_tests_if_main public_modules = [ # the list of modules users need to access for all functionality 'vispy', 'vispy.color', 'vispy.geometry', 'vispy.gloo', 'vispy.io', 'vispy.mpl_plot', 'vispy.plot', 'vispy.scene', 'vispy.util', 'vispy.visuals', ] docscrape_path = op.join(op.dirname(__file__), '..', '..', '..', 'doc', 'ext', 'docscrape.py') if op.isfile(docscrape_path): docscrape = imp.load_source('docscrape', docscrape_path) else: docscrape = None def get_name(func): parts = [] module = inspect.getmodule(func) if module: parts.append(module.__name__) if hasattr(func, 'im_class'): parts.append(func.im_class.__name__) parts.append(func.__name__) return '.'.join(parts) # functions to ignore _ignores = [ 'vispy.scene.visuals', # not parsed properly by this func, copies anyway ] def check_parameters_match(func, doc=None): """Helper to check docstring, returns list of incorrect results""" incorrect = [] name_ = get_name(func) if not name_.startswith('vispy.'): return incorrect if inspect.isdatadescriptor(func): return incorrect args, varargs, varkw, defaults = inspect.getargspec(func) # drop self if len(args) > 0 and args[0] in ('self', 'cls'): args = args[1:] if doc is None: with warnings.catch_warnings(record=True) as w: doc = docscrape.FunctionDoc(func) if len(w): raise RuntimeError('Error for %s:\n%s' % (name_, w[0])) # check set param_names = [name for name, _, _ in doc['Parameters']] # clean up some docscrape output: param_names = [name.split(':')[0].strip('` ') for name in param_names] param_names = [name for name in param_names if '*' not in name] if len(param_names) != len(args): bad = str(sorted(list(set(param_names) - set(args)) + list(set(args) - set(param_names)))) if not any(d in name_ for d in _ignores): incorrect += [name_ + ' arg mismatch: ' + bad] else: for n1, n2 in zip(param_names, args): if n1 != n2: incorrect += [name_ + ' ' + n1 + ' != ' + n2] return incorrect def test_docstring_parameters(): """Test module docsting formatting""" if docscrape is None: raise SkipTest('This must be run from the vispy source directory') incorrect = [] for name in public_modules: module = __import__(name, globals()) for submod in name.split('.')[1:]: module = getattr(module, submod) classes = inspect.getmembers(module, inspect.isclass) for cname, cls in classes: if cname.startswith('_'): continue with warnings.catch_warnings(record=True) as w: cdoc = docscrape.ClassDoc(cls) if len(w): raise RuntimeError('Error for __init__ of %s in %s:\n%s' % (cls, name, w[0])) if hasattr(cls, '__init__'): incorrect += check_parameters_match(cls.__init__, cdoc) for method_name in cdoc.methods: method = getattr(cls, method_name) # skip classes that are added as attributes of classes if (inspect.ismethod(method) or inspect.isfunction(method)): incorrect += check_parameters_match(method) if hasattr(cls, '__call__'): incorrect += check_parameters_match(cls.__call__) functions = inspect.getmembers(module, inspect.isfunction) for fname, func in functions: if fname.startswith('_'): continue incorrect += check_parameters_match(func) msg = '\n' + '\n'.join(sorted(list(set(incorrect)))) if len(incorrect) > 0: msg += '\n\n%s docstring violations found' % msg.count('\n') raise AssertionError(msg) run_tests_if_main() ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/util/tests/test_fourier.py��������������������������������������������������������0000664�0001750�0001750�00000002352�12527672621�022754� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. import numpy as np from vispy.util.fourier import stft, fft_freqs from vispy.testing import assert_raises, run_tests_if_main def test_stft(): """Test STFT calculation""" assert_raises(ValueError, stft, 0) assert_raises(ValueError, stft, [], window='foo') assert_raises(ValueError, stft, [[]]) result = stft([]) assert np.allclose(result, np.zeros_like(result)) n_fft = 256 step = 128 for n_samples, n_estimates in ((256, 1), (383, 1), (384, 2), (511, 2), (512, 3)): result = stft(np.ones(n_samples), n_fft=n_fft, step=step, window=None) assert result.shape[1] == n_estimates expected = np.zeros(n_fft // 2 + 1) expected[0] = 1 for res in result.T: assert np.allclose(expected, np.abs(res)) assert np.allclose(expected, np.abs(res)) for n_pts, last_freq in zip((256, 255), (500., 498.)): freqs = fft_freqs(n_pts, 1000) assert freqs[0] == 0 assert np.allclose(freqs[-1], last_freq, atol=1e-1) run_tests_if_main() ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/util/tests/test_run.py������������������������������������������������������������0000664�0001750�0001750�00000000636�12527672621�022110� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from vispy.util import run_subprocess from vispy.testing import run_tests_if_main, assert_raises def test_run(): """Test running subprocesses """ bad_name = 'foo_nonexist_test' assert_raises(Exception, run_subprocess, [bad_name]) run_tests_if_main() ��������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/util/tests/test_config.py���������������������������������������������������������0000664�0001750�0001750�00000004112�12527672621�022542� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from os import path as op import os from vispy.util import (config, sys_info, _TempDir, set_data_dir, save_config, load_data_file) from vispy.testing import (assert_in, requires_application, run_tests_if_main, assert_raises, assert_equal, assert_true) temp_dir = _TempDir() @requires_application() def test_sys_info(): """Test printing of system information""" fname = op.join(temp_dir, 'info.txt') sys_info(fname) assert_raises(IOError, sys_info, fname) # no overwrite with open(fname, 'r') as fid: out = ''.join(fid.readlines()) keys = ['GL version', 'Python', 'Backend', 'pyglet', 'Platform:'] for key in keys: assert_in(key, out) print(out) assert_true('Info-gathering error' not in out) def test_config(): """Test vispy config methods and file downloading""" assert_raises(TypeError, config.update, data_path=dict()) assert_raises(KeyError, config.update, foo='bar') # bad key data_dir = op.join(temp_dir, 'data') assert_raises(IOError, set_data_dir, data_dir) # didn't say to create orig_val = os.environ.get('_VISPY_CONFIG_TESTING', None) os.environ['_VISPY_CONFIG_TESTING'] = 'true' try: assert_raises(IOError, set_data_dir, data_dir) # doesn't exist yet set_data_dir(data_dir, create=True, save=True) assert_equal(config['data_path'], data_dir) config['data_path'] = data_dir print(config) # __repr__ load_data_file('CONTRIBUTING.txt') fid = open(op.join(data_dir, 'test-faked.txt'), 'w') fid.close() load_data_file('test-faked.txt') # this one shouldn't download assert_raises(RuntimeError, load_data_file, 'foo-nonexist.txt') save_config() finally: if orig_val is not None: os.environ['_VISPY_CONFIG_TESTING'] = orig_val else: del os.environ['_VISPY_CONFIG_TESTING'] run_tests_if_main() ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/util/tests/test_event_emitter.py��������������������������������������������������0000664�0001750�0001750�00000043576�12527672621�024170� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. import unittest import copy import functools from vispy.util.event import Event, EventEmitter from vispy.testing import run_tests_if_main, assert_raises, assert_equal class BasicEvent(Event): pass class TypedEvent(Event): def __init__(self, **kwargs): kwargs['type'] = 'typed_event' Event.__init__(self, **kwargs) class TestEmitters(unittest.TestCase): def test_emitter(self): """Emitter constructed with no arguments""" em = EventEmitter() # type must be specified when emitting since Event requires type # argument and the emitter was constructed without it. try: em() assert False, "Emitting event with no type should have failed." except TypeError: pass # See that emitted event has all of the properties we expect ev = self.try_emitter(em, type='test_event') self.assert_result( event=ev, event_class=Event, source=None, type='test_event', sources=[None]) def test_emitter_source(self): """Emitter constructed with source argument""" em = EventEmitter(source=self) ev = self.try_emitter(em, type='test_event') self.assert_result( event=ev, event_class=Event, source=self, type='test_event', sources=[self]) # overriding source should fail: try: ev = em(type='test_event', source=None) assert False, "Should not be able to specify source when emitting" except AttributeError: pass def test_emitter_type(self): """Emitter constructed with type argument""" em = EventEmitter(type='asdf') ev = self.try_emitter(em) self.assert_result( event=ev, event_class=Event, source=None, type='asdf', sources=[None]) # overriding type is ok: ev = self.try_emitter(em, type='qwer') self.assert_result( event=ev, event_class=Event, source=None, type='qwer', sources=[None]) def test_emitter_type_event_class(self): """Emitter constructed with event_class argument""" em = EventEmitter(event_class=BasicEvent) ev = self.try_emitter(em, type='test_event') self.assert_result( event=ev, event_class=BasicEvent, source=None, type='test_event', sources=[None]) # specifying non-event class should fail (eventually): class X: def __init__(self, *args, **kwargs): self.blocked = False def _push_source(self, s): pass def _pop_source(self): pass try: em = EventEmitter(event_class=X) ev = self.try_emitter(em, type='test_event') self.assert_result() # checks event type assert False, \ "Should not be able to construct emitter with non-Event class" except Exception: pass def test_event_kwargs(self): """Extra Event kwargs""" em = EventEmitter(type='test_event') em.default_args['key1'] = 'test1' em.connect(self.record_event) self.result = None em(key2='test2') self.assert_result(key1='test1', key2='test2') def test_prebuilt_event(self): """Emit pre-built event""" em = EventEmitter(type='test_event') em.default_args['key1'] = 'test1' em.connect(self.record_event) self.result = None ev = Event(type='my_type') em(ev) self.assert_result(event=ev, type='my_type') assert not hasattr(self.result[0], 'key1') def test_emitter_subclass(self): """EventEmitter subclassing""" class MyEmitter(EventEmitter): def _prepare_event(self, *args, **kwargs): ev = super(MyEmitter, self)._prepare_event(*args, **kwargs) ev.test_tag = 1 return ev em = MyEmitter(type='test_event') em.connect(self.record_event) self.result = None em() self.assert_result(test_tag=1) def test_typed_event(self): """Emit Event class with pre-specified type""" em = EventEmitter(event_class=TypedEvent) ev = self.try_emitter(em) # no need to specify type here self.assert_result( event=ev, event_class=TypedEvent, source=None, type='typed_event', sources=[None]) def test_disconnect(self): """Emitter disconnection""" em = EventEmitter(type='test_event') def cb1(ev): self.result = 1 def cb2(ev): self.result = 2 em.connect((self, 'record_event')) em.connect(cb1) em.connect(cb2) self.result = None em.disconnect(cb2) em.disconnect(cb2) # should pass silently ev = em() self.assert_result(event=ev) self.result = None em.disconnect((self, 'record_event')) ev = em() assert self.result == 1 self.result = None em.connect(cb1) em.connect(cb2) em.connect((self, 'record_event')) em.disconnect() em() assert self.result is None def test_reconnect(self): """Ignore callback reconnect""" em = EventEmitter(type='test_event') def cb(ev): self.result += 1 em.connect(cb) em.connect(cb) # second connection should do nothing. self.result = 0 em() assert self.result == 1 def test_decorator_connection(self): """Connection by decorator""" em = EventEmitter(type='test_event') @em.connect def cb(ev): self.result = 1 self.result = None em() assert self.result == 1 def test_chained_emitters(self): """Chained emitters""" em1 = EventEmitter(source=None, type='test_event1') em2 = EventEmitter(source=self, type='test_event2') em1.connect(em2) em1.connect(self.record_event) self.result = None ev = em1() self.assert_result( event=ev, event_class=Event, source=None, type='test_event1', sources=[None]) # sources look different from second emitter, but type is the same. em1.disconnect(self.record_event) em2.connect(self.record_event) self.result = None ev = em1() self.assert_result( event=ev, event_class=Event, source=self, type='test_event1', sources=[ None, self]) def test_emitter_error_handling(self): """Emitter error handling""" em = EventEmitter(type='test_event') em.print_callback_errors = 'never' def cb(ev): raise Exception('test') # first callback fails; second callback still runs. em.connect(self.record_event) em.connect(cb) self.result = None ev = em() self.assert_result(event=ev) # this time we should get an exception self.result = None em.ignore_callback_errors = False try: em() assert False, "Emission should have raised exception" except Exception as err: if str(err) != 'test': raise def test_emission_order(self): """Event emission order""" em = EventEmitter(type='test_event') def cb1(ev): self.result = 1 def cb2(ev): self.result = 2 em.connect(cb1) em.connect(cb2) self.result = None em() assert self.result == 1, "Events emitted in wrong order" em.disconnect() em.connect(cb2) em.connect(cb1) self.result = None em() assert self.result == 2, "Events emitted in wrong order" def test_multiple_callbacks(self): """Multiple emitter callbacks""" em = EventEmitter(type='test_event') em.connect(functools.partial(self.record_event, key=1)) em.connect(functools.partial(self.record_event, key=2)) em.connect(functools.partial(self.record_event, key=3)) ev = em() self.assert_result(key=1, event=ev, sources=[None]) self.assert_result(key=2, event=ev, sources=[None]) self.assert_result(key=3, event=ev, sources=[None]) def test_symbolic_callback(self): """Symbolic callbacks""" em = EventEmitter(type='test_event') em.connect((self, 'record_event')) ev = em() self.assert_result(event=ev) # now check overriding the connected method def cb(ev): self.result = 1 self.result = None orig_method = self.record_event try: self.record_event = cb em() assert self.result == 1 finally: self.record_event = orig_method def test_source_stack_integrity(self): """Emitter checks source stack""" em = EventEmitter(type='test_event') def cb(ev): ev._sources.append('x') em.connect(cb) try: em() except RuntimeError as err: if str(err) != 'Event source-stack mismatch.': raise em.disconnect() def cb(ev): ev._sources = [] em.connect(cb) try: em() except IndexError: pass def test_emitter_loop(self): """Catch emitter loops""" em1 = EventEmitter(type='test_event1') em2 = EventEmitter(type='test_event2') em1.ignore_callback_errors = False em2.ignore_callback_errors = False # cross-connect emitters; when we emit, an exception should be raised # indicating an event loop. em1.connect(em2) em2.connect(em1) try: em1() except RuntimeError as err: if str(err) != 'EventEmitter loop detected!': raise err def test_emitter_block(self): """EventEmitter.blocker""" em = EventEmitter(type='test_event') em.connect(self.record_event) self.result = None with em.blocker(): em() assert self.result is None ev = em() self.assert_result(event=ev) def test_event_handling(self): """Event.handled""" em = EventEmitter(type='test_event') def cb1(ev): ev.handled = True def cb2(ev): assert ev.handled self.result = 1 em.connect(cb2) em.connect(cb1) self.result = None em() assert self.result == 1 def test_event_block(self): """Event.blocked""" em = EventEmitter(type='test_event') def cb1(ev): ev.handled = True self.result = 1 def cb2(ev): ev.blocked = True self.result = 2 em.connect(self.record_event) em.connect(cb1) self.result = None em() self.assert_result() em.connect(cb2) self.result = None em() assert self.result == 2 def try_emitter(self, em, **kwargs): em.connect(self.record_event) self.result = None return em(**kwargs) def record_event(self, ev, key=None): # get a copy of all event attributes because these may change # as the event is passed around; we want to know exactly what the event # looked like when it reached this callback. names = [name for name in dir(ev) if name[0] != '_'] attrs = {} for name in names: val = getattr(ev, name) if name == 'source': attrs[name] = val elif name == 'sources': attrs[name] = val[:] else: try: attrs[name] = copy.deepcopy(val) except Exception: try: attrs[name] = copy.copy(val) except Exception: attrs[name] = val if key is None: self.result = ev, attrs else: if not hasattr(self, 'result') or self.result is None: self.result = {} self.result[key] = ev, attrs def assert_result(self, key=None, **kwargs): assert (hasattr(self, 'result') and self.result is not None), \ "No event recorded" if key is None: event, event_attrs = self.result else: event, event_attrs = self.result[key] assert isinstance(event, Event), "Emitted object is not Event instance" for name, val in kwargs.items(): if name == 'event': assert event is val, "Event objects do not match" elif name == 'event_class': assert isinstance(event, val), \ "Emitted object is not instance of %s" % val.__name__ else: attr = event_attrs[name] assert (attr == val), "Event.%s != %s (%s)" % ( name, str(val), str(attr)) def test_event_connect_order(): """Test event connection order""" def a(): return def b(): return def c(): return def d(): return def e(): return def f(): return em = EventEmitter(type='test_event') assert_raises(ValueError, em.connect, c, before=['c', 'foo']) assert_raises(ValueError, em.connect, c, position='foo') assert_raises(TypeError, em.connect, c, ref=dict()) em.connect(c, ref=True) assert_equal((c,), tuple(em.callbacks)) em.connect(c) assert_equal((c,), tuple(em.callbacks)) em.connect(d, ref=True, position='last') assert_equal((c, d), tuple(em.callbacks)) em.connect(b, ref=True) # position='first' assert_equal((b, c, d), tuple(em.callbacks)) assert_raises(RuntimeError, em.connect, a, before='c', after='d') # can't em.connect(a, ref=True, before=['c', 'd']) # first possible pos == 0 assert_equal((a, b, c, d), tuple(em.callbacks)) em.connect(f, ref=True, after=['c', 'd']) assert_equal((a, b, c, d, f), tuple(em.callbacks)) em.connect(e, ref=True, after='d', before='f') assert_equal(('a', 'b', 'c', 'd', 'e', 'f'), tuple(em.callback_refs)) em.disconnect(e) em.connect(e, ref=True, after='a', before='f', position='last') assert_equal(('a', 'b', 'c', 'd', 'e', 'f'), tuple(em.callback_refs)) em.disconnect(e) em.connect(e, ref='e', after='d', before='f', position='last') assert_equal(('a', 'b', 'c', 'd', 'e', 'f'), tuple(em.callback_refs)) em.disconnect(e) em.connect(e, after='d', before='f', position='first') # no name assert_equal(('a', 'b', 'c', 'd', None, 'f'), tuple(em.callback_refs)) em.disconnect(e) assert_raises(ValueError, em.connect, e, ref='d') # duplicate name em.connect(e, ref=True, after=[], before='f', position='last') assert_equal(('a', 'b', 'c', 'd', 'e', 'f'), tuple(em.callback_refs)) assert_equal((a, b, c, d, e, f), tuple(em.callbacks)) old_e = e def e(): return assert_raises(ValueError, em.connect, e, ref=True) # duplicate name em.connect(e) assert_equal((None, 'a', 'b', 'c', 'd', 'e', 'f'), tuple(em.callback_refs)) assert_equal((e, a, b, c, d, old_e, f), tuple(em.callbacks)) def test_emitter_block(): state = [False, False] def a(ev): state[0] = True def b(ev): state[1] = True e = EventEmitter(source=None, type='event') e.connect(a) e.connect(b) def assert_state(a, b): assert state == [a, b] state[0] = False state[1] = False e() assert_state(True, True) # test global blocking e.block() e() assert_state(False, False) e.block() e() assert_state(False, False) # test global unlock, multiple depth e.unblock() e() assert_state(False, False) e.unblock() e() assert_state(True, True) # test unblock failure try: e.unblock() raise Exception("Expected RuntimeError") except RuntimeError: pass # test single block e.block(a) e() assert_state(False, True) e.block(b) e() assert_state(False, False) e.block(b) e() assert_state(False, False) # test single unblock e.unblock(a) e() assert_state(True, False) e.unblock(b) e() assert_state(True, False) e.unblock(b) e() assert_state(True, True) # Test single unblock failure try: e.unblock(a) raise Exception("Expected RuntimeError") except RuntimeError: pass # test global blocker with e.blocker(): e() assert_state(False, False) # test nested blocker with e.blocker(): e() assert_state(False, False) e() assert_state(False, False) e() assert_state(True, True) # test single blocker with e.blocker(a): e() assert_state(False, True) # test nested gloabel blocker with e.blocker(): e() assert_state(False, False) e() assert_state(False, True) # test nested single blocker with e.blocker(a): e() assert_state(False, True) with e.blocker(b): e() assert_state(False, False) e() assert_state(False, True) e() assert_state(True, True) run_tests_if_main() ����������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/util/tests/test_transforms.py�����������������������������������������������������0000664�0001750�0001750�00000002731�12527672621�023500� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. import numpy as np from numpy.testing import assert_allclose from vispy.util.transforms import (translate, scale, rotate, ortho, frustum, perspective) from vispy.testing import run_tests_if_main, assert_equal def test_transforms(): """Test basic transforms""" xfm = np.random.randn(4, 4).astype(np.float32) # Do a series of rotations that should end up into the same orientation # again, to ensure the order of computation is all correct # i.e. if rotated would return the transposed matrix this would not work # out (the translation part would be incorrect) new_xfm = xfm.dot(rotate(180, (1, 0, 0)).dot(rotate(-90, (0, 1, 0)))) new_xfm = new_xfm.dot(rotate(90, (0, 0, 1)).dot(rotate(90, (0, 1, 0)))) new_xfm = new_xfm.dot(rotate(90, (1, 0, 0))) assert_allclose(xfm, new_xfm) new_xfm = translate((1, -1, 1)).dot(translate((-1, 1, -1))).dot(xfm) assert_allclose(xfm, new_xfm) new_xfm = scale((1, 2, 3)).dot(scale((1, 1. / 2., 1. / 3.))).dot(xfm) assert_allclose(xfm, new_xfm) # These could be more complex... xfm = ortho(-1, 1, -1, 1, -1, 1) assert_equal(xfm.shape, (4, 4)) xfm = frustum(-1, 1, -1, 1, -1, 1) assert_equal(xfm.shape, (4, 4)) xfm = perspective(1, 1, -1, 1) assert_equal(xfm.shape, (4, 4)) run_tests_if_main() ���������������������������������������vispy-0.4.0/vispy/util/filter.py��������������������������������������������������������������������0000664�0001750�0001750�00000002537�12527672621�020372� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. import numpy as np def gaussian_filter(data, sigma): """ Drop-in replacement for scipy.ndimage.gaussian_filter. (note: results are only approximately equal to the output of gaussian_filter) """ if np.isscalar(sigma): sigma = (sigma,) * data.ndim baseline = data.mean() filtered = data - baseline for ax in range(data.ndim): s = float(sigma[ax]) if s == 0: continue # generate 1D gaussian kernel ksize = int(s * 6) x = np.arange(-ksize, ksize) kernel = np.exp(-x**2 / (2*s**2)) kshape = [1, ] * data.ndim kshape[ax] = len(kernel) kernel = kernel.reshape(kshape) # convolve as product of FFTs shape = data.shape[ax] + ksize scale = 1.0 / (abs(s) * (2*np.pi)**0.5) filtered = scale * np.fft.irfft(np.fft.rfft(filtered, shape, axis=ax) * np.fft.rfft(kernel, shape, axis=ax), axis=ax) # clip off extra data sl = [slice(None)] * data.ndim sl[ax] = slice(filtered.shape[ax]-data.shape[ax], None, None) filtered = filtered[sl] return filtered + baseline �����������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/util/config.py��������������������������������������������������������������������0000664�0001750�0001750�00000034232�12527672621�020347� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """Vispy configuration functions """ import os from os import path as op import json import sys import platform import getopt import traceback import tempfile import atexit from shutil import rmtree from .event import EmitterGroup, EventEmitter, Event from .logs import logger, set_log_level, use_log_level from ..ext.six import string_types, file_types config = None _data_path = None _allowed_config_keys = None def _init(): """ Create global Config object, parse command flags """ global config, _data_path, _allowed_config_keys app_dir = _get_vispy_app_dir() if app_dir is not None: _data_path = op.join(app_dir, 'data') _test_data_path = op.join(app_dir, 'test_data') else: _data_path = _test_data_path = None # All allowed config keys and the types they may have _allowed_config_keys = { 'data_path': string_types, 'default_backend': string_types, 'gl_backend': string_types, 'gl_debug': (bool,), 'glir_file': string_types+file_types, 'include_path': list, 'logging_level': string_types, 'qt_lib': string_types, 'dpi': (int, type(None)), 'profile': string_types + (type(None),), 'audit_tests': (bool,), 'test_data_path': string_types + (type(None),), } # Default values for all config options default_config_options = { 'data_path': _data_path, 'default_backend': '', 'gl_backend': 'gl2', 'gl_debug': False, 'glir_file': '', 'include_path': [], 'logging_level': 'info', 'qt_lib': 'any', 'dpi': None, 'profile': None, 'audit_tests': False, 'test_data_path': _test_data_path, } config = Config(**default_config_options) try: config.update(**_load_config()) except Exception as err: raise Exception('Error while reading vispy config file "%s":\n %s' % (_get_config_fname(), err.message)) set_log_level(config['logging_level']) _parse_command_line_arguments() ############################################################################### # Command line flag parsing VISPY_HELP = """ VisPy command line arguments: --vispy-backend=(qt|pyqt4|pyt5|pyside|glfw|pyglet|sdl2|wx) Selects the backend system for VisPy to use. This will override the default backend selection in your configuration file. --vispy-log=(debug|info|warning|error|critical)[,search string] Sets the verbosity of logging output. The default is 'warning'. If a search string is given, messages will only be displayed if they match the string, or if their call location (module.class:method(line) or module:function(line)) matches the string. --vispy-dpi=resolution Force the screen resolution to a certain value (in pixels per inch). By default, the OS is queried to determine the screen DPI. --vispy-fps Print the framerate (in Frames Per Second) in the console. --vispy-gl-debug Enables error checking for all OpenGL calls. --vispy-glir-file Export glir commands to specified file. --vispy-profile=locations Measure performance at specific code locations and display results. *locations* may be "all" or a comma-separated list of method names like "SceneCanvas.draw_visual". --vispy-cprofile Enable profiling using the built-in cProfile module and display results when the program exits. --vispy-audit-tests Enable user auditing of image test results. --vispy-help Display this help message. """ def _parse_command_line_arguments(): """ Transform vispy specific command line args to vispy config. Put into a function so that any variables dont leak in the vispy namespace. """ global config # Get command line args for vispy argnames = ['vispy-backend=', 'vispy-gl-debug', 'vispy-glir-file=', 'vispy-log=', 'vispy-help', 'vispy-profile=', 'vispy-cprofile', 'vispy-dpi=', 'vispy-audit-tests'] try: opts, args = getopt.getopt(sys.argv[1:], '', argnames) except getopt.GetoptError: opts = [] # Use them to set the config values for o, a in opts: if o.startswith('--vispy'): if o == '--vispy-backend': config['default_backend'] = a logger.info('vispy backend: %s', a) elif o == '--vispy-gl-debug': config['gl_debug'] = True elif o == '--vispy-glir-file': config['glir_file'] = a elif o == '--vispy-log': if ',' in a: verbose, match = a.split(',') else: verbose = a match = None config['logging_level'] = a set_log_level(verbose, match) elif o == '--vispy-profile': config['profile'] = a elif o == '--vispy-cprofile': _enable_profiling() elif o == '--vispy-help': print(VISPY_HELP) elif o == '--vispy-dpi': config['dpi'] = int(a) elif o == '--vispy-audit-tests': config['audit_tests'] = True else: logger.warning("Unsupported vispy flag: %s" % o) ############################################################################### # CONFIG # Adapted from pyzolib/paths.py: # https://bitbucket.org/pyzo/pyzolib/src/tip/paths.py def _get_vispy_app_dir(): """Helper to get the default directory for storing vispy data""" # Define default user directory user_dir = os.path.expanduser('~') # Get system app data dir path = None if sys.platform.startswith('win'): path1, path2 = os.getenv('LOCALAPPDATA'), os.getenv('APPDATA') path = path1 or path2 elif sys.platform.startswith('darwin'): path = os.path.join(user_dir, 'Library', 'Application Support') # On Linux and as fallback if not (path and os.path.isdir(path)): path = user_dir # Maybe we should store things local to the executable (in case of a # portable distro or a frozen application that wants to be portable) prefix = sys.prefix if getattr(sys, 'frozen', None): # See application_dir() function prefix = os.path.abspath(os.path.dirname(sys.path[0])) for reldir in ('settings', '../settings'): localpath = os.path.abspath(os.path.join(prefix, reldir)) if os.path.isdir(localpath): try: open(os.path.join(localpath, 'test.write'), 'wb').close() os.remove(os.path.join(localpath, 'test.write')) except IOError: pass # We cannot write in this directory else: path = localpath break # Get path specific for this app appname = '.vispy' if path == user_dir else 'vispy' path = os.path.join(path, appname) return path class ConfigEvent(Event): """ Event indicating a configuration change. This class has a 'changes' attribute which is a dict of all name:value pairs that have changed in the configuration. """ def __init__(self, changes): Event.__init__(self, type='config_change') self.changes = changes class Config(object): """ Container for global settings used application-wide in vispy. Events: ------- Config.events.changed - Emits ConfigEvent whenever the configuration changes. """ def __init__(self, **kwargs): self.events = EmitterGroup(source=self) self.events['changed'] = EventEmitter( event_class=ConfigEvent, source=self) self._config = {} self.update(**kwargs) self._known_keys = get_config_keys() def __getitem__(self, item): return self._config[item] def __setitem__(self, item, val): self._check_key_val(item, val) self._config[item] = val # inform any listeners that a configuration option has changed self.events.changed(changes={item: val}) def _check_key_val(self, key, val): global _allowed_config_keys # check values against acceptable ones known_keys = _allowed_config_keys if key not in known_keys: raise KeyError('key "%s" not in known keys: "%s"' % (key, known_keys)) if not isinstance(val, known_keys[key]): raise TypeError('Value for key "%s" must be one of %s, not %s.' % (key, known_keys[key], type(val))) def update(self, **kwargs): for key, val in kwargs.items(): self._check_key_val(key, val) self._config.update(kwargs) self.events.changed(changes=kwargs) def __repr__(self): return repr(self._config) def get_config_keys(): """The config keys known by vispy and their allowed data types. Returns ------- keys : dict Dict of {key: (types,)} pairs. """ global _allowed_config_keys return _allowed_config_keys.copy() def _get_config_fname(): """Helper for the vispy config file""" directory = _get_vispy_app_dir() if directory is None: return None fname = op.join(directory, 'vispy.json') if os.environ.get('_VISPY_CONFIG_TESTING', None) is not None: fname = op.join(_TempDir(), 'vispy.json') return fname def _load_config(): """Helper to load prefs from ~/.vispy/vispy.json""" fname = _get_config_fname() if fname is None or not op.isfile(fname): return dict() with open(fname, 'r') as fid: config = json.load(fid) return config def save_config(**kwargs): """Save configuration keys to vispy config file Parameters ---------- **kwargs : keyword arguments Key/value pairs to save to the config file. """ if kwargs == {}: kwargs = config._config current_config = _load_config() current_config.update(**kwargs) # write to disk fname = _get_config_fname() if fname is None: raise RuntimeError('config filename could not be determined') if not op.isdir(op.dirname(fname)): os.mkdir(op.dirname(fname)) with open(fname, 'w') as fid: json.dump(current_config, fid, sort_keys=True, indent=0) def set_data_dir(directory=None, create=False, save=False): """Set vispy data download directory Parameters ---------- directory : str | None The directory to use. create : bool If True, create directory if it doesn't exist. save : bool If True, save the configuration to the vispy config. """ if directory is None: directory = _data_path if _data_path is None: raise IOError('default path cannot be determined, please ' 'set it manually (directory != None)') if not op.isdir(directory): if not create: raise IOError('directory "%s" does not exist, perhaps try ' 'create=True to create it?' % directory) os.mkdir(directory) config.update(data_path=directory) if save: save_config(data_path=directory) def _enable_profiling(): """ Start profiling and register callback to print stats when the program exits. """ import cProfile import atexit global _profiler _profiler = cProfile.Profile() _profiler.enable() atexit.register(_profile_atexit) _profiler = None def _profile_atexit(): global _profiler _profiler.print_stats(sort='cumulative') def sys_info(fname=None, overwrite=False): """Get relevant system and debugging information Parameters ---------- fname : str | None Filename to dump info to. Use None to simply print. overwrite : bool If True, overwrite file (if it exists). Returns ------- out : str The system information as a string. """ if fname is not None and op.isfile(fname) and not overwrite: raise IOError('file exists, use overwrite=True to overwrite') out = '' try: # Nest all imports here to avoid any circular imports from ..app import use_app, Canvas from ..app.backends import BACKEND_NAMES from ..gloo import gl from ..testing import has_backend # get default app with use_log_level('warning'): app = use_app(call_reuse=False) # suppress messages out += 'Platform: %s\n' % platform.platform() out += 'Python: %s\n' % str(sys.version).replace('\n', ' ') out += 'Backend: %s\n' % app.backend_name for backend in BACKEND_NAMES: if backend.startswith('ipynb_'): continue with use_log_level('warning', print_msg=False): which = has_backend(backend, out=['which'])[1] out += '{0:<9} {1}\n'.format(backend + ':', which) out += '\n' # We need an OpenGL context to get GL info canvas = Canvas('Test', (10, 10), show=False, app=app) canvas._backend._vispy_set_current() out += 'GL version: %r\n' % (gl.glGetParameter(gl.GL_VERSION),) x_ = gl.GL_MAX_TEXTURE_SIZE out += 'MAX_TEXTURE_SIZE: %r\n' % (gl.glGetParameter(x_),) out += 'Extensions: %r\n' % (gl.glGetParameter(gl.GL_EXTENSIONS),) canvas.close() except Exception: # don't stop printing info out += '\nInfo-gathering error:\n%s' % traceback.format_exc() pass if fname is not None: with open(fname, 'w') as fid: fid.write(out) return out class _TempDir(str): """Class for creating and auto-destroying temp dir This is designed to be used with testing modules. We cannot simply use __del__() method for cleanup here because the rmtree function may be cleaned up before this object, so we use the atexit module instead. """ def __new__(self): new = str.__new__(self, tempfile.mkdtemp()) return new def __init__(self): self._path = self.__str__() atexit.register(self.cleanup) def cleanup(self): rmtree(self._path, ignore_errors=True) # initialize config options _init() ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/util/dpi/�������������������������������������������������������������������������0000775�0001750�0001750�00000000000�12527674621�017302� 5����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/util/dpi/_linux.py����������������������������������������������������������������0000664�0001750�0001750�00000003236�12527672621�021154� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2015, Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- import re from subprocess import CalledProcessError from ..logs import logger from ..wrappers import run_subprocess def _get_dpi_from(cmd, pattern, func): """Match pattern against the output of func, passing the results as floats to func. If anything fails, return None. """ try: out, _ = run_subprocess([cmd]) except (OSError, CalledProcessError): pass else: match = re.search(pattern, out) if match: return func(*map(float, match.groups())) def get_dpi(raise_error=True): """Get screen DPI from the OS Parameters ---------- raise_error : bool If True, raise an error if DPI could not be determined. Returns ------- dpi : float Dots per inch of the primary screen. """ from_xdpyinfo = _get_dpi_from( 'xdpyinfo', r'(\d+)x(\d+) dots per inch', lambda x_dpi, y_dpi: (x_dpi + y_dpi) / 2) if from_xdpyinfo is not None: return from_xdpyinfo from_xrandr = _get_dpi_from( 'xrandr', r'(\d+)x(\d+).*?(\d+)mm x (\d+)mm', lambda x_px, y_px, x_mm, y_mm: 25.4 * (x_px / x_mm + y_px / y_mm) / 2) if from_xrandr is not None: return from_xrandr if raise_error: raise RuntimeError('could not determine DPI') else: logger.warning('could not determine DPI') return 96 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/util/dpi/_win32.py����������������������������������������������������������������0000664�0001750�0001750�00000002154�12527672621�020755� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2015, Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- from ...ext.gdi32plus import (gdi32, user32, HORZSIZE, VERTSIZE, HORZRES, VERTRES) def get_dpi(raise_error=True): """Get screen DPI from the OS Parameters ---------- raise_error : bool If True, raise an error if DPI could not be determined. Returns ------- dpi : float Dots per inch of the primary screen. """ try: user32.SetProcessDPIAware() except AttributeError: pass # not present on XP dc = user32.GetDC(0) h_size = gdi32.GetDeviceCaps(dc, HORZSIZE) v_size = gdi32.GetDeviceCaps(dc, VERTSIZE) h_res = gdi32.GetDeviceCaps(dc, HORZRES) v_res = gdi32.GetDeviceCaps(dc, VERTRES) user32.ReleaseDC(None, dc) return (h_res/float(h_size) + v_res/float(v_size)) * 0.5 * 25.4 ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/util/dpi/_quartz.py���������������������������������������������������������������0000664�0001750�0001750�00000001503�12527672621�021336� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2015, Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- from ...ext.cocoapy import quartz def get_dpi(raise_error=True): """Get screen DPI from the OS Parameters ---------- raise_error : bool If True, raise an error if DPI could not be determined. Returns ------- dpi : float Dots per inch of the primary screen. """ display = quartz.CGMainDisplayID() mm = quartz.CGDisplayScreenSize(display) px = quartz.CGDisplayBounds(display).size return (px.width/mm.width + px.height/mm.height) * 0.5 * 25.4 ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/util/dpi/__init__.py��������������������������������������������������������������0000664�0001750�0001750�00000001327�12527672621�021414� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2015, Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ The dpi module enables querying the OS for the screen DPI. """ import sys __all__ = ['get_dpi'] if sys.platform.startswith('linux'): from ._linux import get_dpi elif sys.platform == 'darwin': from ._quartz import get_dpi elif sys.platform.startswith('win'): from ._win32 import get_dpi # noqa, analysis:ignore else: raise NotImplementedError('unknown system %s' % sys.platform) ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/util/dpi/tests/�������������������������������������������������������������������0000775�0001750�0001750�00000000000�12527674621�020444� 5����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/util/dpi/tests/test_dpi.py��������������������������������������������������������0000664�0001750�0001750�00000000555�12527672621�022634� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from vispy.util.dpi import get_dpi from vispy.testing import run_tests_if_main def test_dpi(): """Test dpi support""" dpi = get_dpi() assert dpi > 0. assert isinstance(dpi, float) run_tests_if_main() ���������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/util/dpi/tests/__init__.py��������������������������������������������������������0000664�0001750�0001750�00000000000�12406355656�022543� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/util/bunch.py���������������������������������������������������������������������0000664�0001750�0001750�00000000644�12527672621�020201� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # Class adapted from mne-python class SimpleBunch(dict): """ Container object for datasets: dictionnary-like object that exposes its keys as attributes. """ def __init__(self, **kwargs): dict.__init__(self, kwargs) self.__dict__ = self ��������������������������������������������������������������������������������������������vispy-0.4.0/vispy/util/ptime.py���������������������������������������������������������������������0000664�0001750�0001750�00000001674�12527672621�020224� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ ptime.py - Precision time function made os-independent (should have been taken care of by python) """ from __future__ import division import sys import time as systime START_TIME = None time = None def winTime(): """Return the current time in seconds with high precision (Windows version, use Manager.time() to stay platform independent.) """ return systime.clock() + START_TIME # return systime.time() def unixTime(): """Return the current time in seconds with high precision (Unix version, use Manager.time() to stay platform independent.) """ return systime.time() if sys.platform.startswith('win'): cstart = systime.clock() # Required to start the clock in windows START_TIME = systime.time() - cstart time = winTime else: time = unixTime ��������������������������������������������������������������������vispy-0.4.0/vispy/util/logs.py����������������������������������������������������������������������0000664�0001750�0001750�00000031337�12527672621�020051� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. import base64 import logging import math import sys import inspect import re import traceback import json from functools import partial import numpy as np from ..ext.six import string_types ############################################################################### # LOGGING (some adapted from mne-python) def _get_vispy_caller(): """Helper to get vispy calling function from the stack""" records = inspect.stack() # first few records are vispy-based logging calls for record in records[5:]: module = record[0].f_globals['__name__'] if module.startswith('vispy'): line = str(record[0].f_lineno) func = record[3] cls = record[0].f_locals.get('self', None) clsname = "" if cls is None else cls.__class__.__name__ + '.' caller = "{0}:{1}{2}({3}): ".format(module, clsname, func, line) return caller return 'unknown' # class _WrapStdOut(object): # """Class to work around how doctest captures stdout""" # def __getattr__(self, name): # # Even more ridiculous than this class, this must be sys.stdout (not # # just stdout) in order for this to work (tested on OSX and Linux) # return getattr(sys.stdout, name) class _VispyFormatter(logging.Formatter): """Formatter that optionally prepends caller""" def __init__(self): logging.Formatter.__init__(self, '%(levelname)s: %(message)s') self._vispy_prepend_caller = False def _vispy_set_prepend(self, prepend): self._vispy_prepend_caller = prepend def format(self, record): out = logging.Formatter.format(self, record) if self._vispy_prepend_caller: out = _get_vispy_caller() + out return out class _VispyStreamHandler(logging.StreamHandler): """Stream handler allowing matching and recording This handler has two useful optional additions: 1. Recording emitted messages. 2. Performing regexp substring matching. Prepending of traceback information is done in _VispyFormatter. """ def __init__(self): logging.StreamHandler.__init__(self, sys.stderr) self._vispy_formatter = _lf self.setFormatter(self._vispy_formatter) self._vispy_match = None self._vispy_emit_list = list() self._vispy_set_emit_record(False) self._vispy_set_match(None) self._vispy_print_msg = True def _vispy_emit_match_andor_record(self, record): """Log message emitter that optionally matches and/or records""" test = record.getMessage() match = self._vispy_match if (match is None or re.search(match, test) or re.search(match, _get_vispy_caller())): if self._vispy_emit_record: fmt_rec = self._vispy_formatter.format(record) self._vispy_emit_list.append(fmt_rec) if self._vispy_print_msg: return logging.StreamHandler.emit(self, record) else: return def _vispy_set_match(self, match): old_match = self._vispy_match self._vispy_match = match # Triage here to avoid a bunch of if's later (more efficient) if match is not None or self._vispy_emit_record: self.emit = self._vispy_emit_match_andor_record else: self.emit = partial(logging.StreamHandler.emit, self) return old_match def _vispy_set_emit_record(self, record): self._vispy_emit_record = record match = self._vispy_match # Triage here to avoid a bunch of if's later (more efficient) if match is not None or self._vispy_emit_record: self.emit = self._vispy_emit_match_andor_record else: self.emit = partial(logging.StreamHandler.emit, self) def _vispy_reset_list(self): self._vispy_emit_list = list() logger = logging.getLogger('vispy') _lf = _VispyFormatter() _lh = _VispyStreamHandler() # needs _lf to exist logger.addHandler(_lh) logging_types = dict(debug=logging.DEBUG, info=logging.INFO, warning=logging.WARNING, error=logging.ERROR, critical=logging.CRITICAL) def set_log_level(verbose, match=None, return_old=False): """Convenience function for setting the logging level Parameters ---------- verbose : bool, str, int, or None The verbosity of messages to print. If a str, it can be either DEBUG, INFO, WARNING, ERROR, or CRITICAL. Note that these are for convenience and are equivalent to passing in logging.DEBUG, etc. For bool, True is the same as 'INFO', False is the same as 'WARNING'. match : str | None String to match. Only those messages that both contain a substring that regexp matches ``'match'`` (and the ``verbose`` level) will be displayed. return_old : bool If True, return the old verbosity level and old match. Notes ----- If ``verbose=='debug'``, then the ``vispy`` method emitting the log message will be prepended to each log message, which is useful for debugging. If ``verbose=='debug'`` or ``match is not None``, then a small performance overhead is added. Thus it is suggested to only use these options when performance is not crucial. See also -------- vispy.util.use_log_level """ # This method is responsible for setting properties of the handler and # formatter such that proper messages (possibly with the vispy caller # prepended) are displayed. Storing log messages is only available # via the context handler (use_log_level), so that configuration is # done by the context handler itself. if isinstance(verbose, bool): verbose = 'info' if verbose else 'warning' if isinstance(verbose, string_types): verbose = verbose.lower() if verbose not in logging_types: raise ValueError('Invalid argument "%s"' % verbose) verbose = logging_types[verbose] else: raise TypeError('verbose must be a bool or string') logger = logging.getLogger('vispy') old_verbose = logger.level old_match = _lh._vispy_set_match(match) logger.setLevel(verbose) if verbose <= logging.DEBUG: _lf._vispy_set_prepend(True) else: _lf._vispy_set_prepend(False) out = None if return_old: out = (old_verbose, old_match) return out class use_log_level(object): """Context manager that temporarily sets logging level Parameters ---------- level : str See ``set_log_level`` for options. match : str | None The string to match. record : bool If True, the context manager will keep a record of the logging messages generated by vispy. Otherwise, an empty list will be returned. print_msg : bool If False, printing of (all) messages will be suppressed. This is mainly useful in testing. False only works in `record=True` mode, if not recording messages, consider setting `level` appropriately. Returns ------- records : list As a context manager, an empty list or the list of logging messages will be returned (depending on the input ``record``). """ # This method mostly wraps to set_log_level, but also takes # care of enabling/disabling message recording in the formatter. def __init__(self, level, match=None, record=False, print_msg=True): self._new_level = level self._new_match = match self._print_msg = print_msg self._record = record if match is not None and not isinstance(match, string_types): raise TypeError('match must be None or str') def __enter__(self): # set the log level old_level, old_match = set_log_level(self._new_level, self._new_match, return_old=True) for key, value in logging_types.items(): if value == old_level: old_level = key self._old_level = old_level self._old_match = old_match if not self._print_msg: _lh._vispy_print_msg = False # set handler to record, if appropriate _lh._vispy_reset_list() if self._record: _lh._vispy_set_emit_record(True) return _lh._vispy_emit_list else: return list() def __exit__(self, type, value, traceback): # reset log level set_log_level(self._old_level, self._old_match) # reset handler if self._record: _lh._vispy_set_emit_record(False) if not self._print_msg: _lh._vispy_print_msg = True # set it back def log_exception(level='warning', tb_skip=2): """ Send an exception and traceback to the logger. This function is used in cases where an exception is handled safely but nevertheless should generate a descriptive error message. An extra line is inserted into the stack trace indicating where the exception was caught. Parameters ---------- level : str See ``set_log_level`` for options. tb_skip : int The number of traceback entries to ignore, prior to the point where the exception was caught. The default is 2. """ stack = "".join(traceback.format_stack()[:-tb_skip]) tb = traceback.format_exception(*sys.exc_info()) msg = tb[0] # "Traceback (most recent call last):" msg += stack msg += " << caught exception here: >>\n" msg += "".join(tb[1:]).rstrip() logger.log(logging_types[level], msg) logger.log_exception = log_exception # make this easier to reach def _handle_exception(ignore_callback_errors, print_callback_errors, obj, cb_event=None, node=None): """Helper for prining errors in callbacks See EventEmitter._invoke_callback for a use example. """ if not hasattr(obj, '_vispy_err_registry'): obj._vispy_err_registry = {} registry = obj._vispy_err_registry if cb_event is not None: cb, event = cb_event exp_type = 'callback' else: exp_type = 'node' type_, value, tb = sys.exc_info() tb = tb.tb_next # Skip *this* frame sys.last_type = type_ sys.last_value = value sys.last_traceback = tb del tb # Get rid of it in this namespace # Handle if not ignore_callback_errors: raise if print_callback_errors != "never": this_print = 'full' if print_callback_errors in ('first', 'reminders'): # need to check to see if we've hit this yet if exp_type == 'callback': key = repr(cb) + repr(event) else: key = repr(node) if key in registry: registry[key] += 1 if print_callback_errors == 'first': this_print = None else: # reminders ii = registry[key] # Use logarithmic selection # (1, 2, ..., 10, 20, ..., 100, 200, ...) if ii % (10 ** int(math.log10(ii))) == 0: this_print = ii else: this_print = None else: registry[key] = 1 if this_print == 'full': logger.log_exception() if exp_type == 'callback': logger.error("Invoking %s for %s" % (cb, event)) else: # == 'node': logger.error("Drawing node %s" % node) elif this_print is not None: if exp_type == 'callback': logger.error("Invoking %s repeat %s" % (cb, this_print)) else: # == 'node': logger.error("Drawing node %s repeat %s" % (node, this_print)) def _serialize_buffer(buffer, array_serialization=None): """Serialize a NumPy array.""" if array_serialization == 'binary': # WARNING: in NumPy 1.9, tostring() has been renamed to tobytes() # but tostring() is still here for now for backward compatibility. return buffer.ravel().tostring() elif array_serialization == 'base64': return {'storage_type': 'base64', 'buffer': base64.b64encode(buffer).decode('ascii') } raise ValueError("The array serialization method should be 'binary' or " "'base64'.") class NumPyJSONEncoder(json.JSONEncoder): def default(self, obj): if isinstance(obj, np.ndarray): return _serialize_buffer(obj, array_serialization='base64') elif isinstance(obj, np.generic): return obj.item() return json.JSONEncoder.default(self, obj) �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/util/profiler.py������������������������������������������������������������������0000664�0001750�0001750�00000011277�12527672621�020730� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # Adapted from PyQtGraph import sys from . import ptime from .. import config class Profiler(object): """Simple profiler allowing directed, hierarchical measurement of time intervals. By default, profilers are disabled. To enable profiling, set the environment variable `VISPYPROFILE` to a comma-separated list of fully-qualified names of profiled functions. Calling a profiler registers a message (defaulting to an increasing counter) that contains the time elapsed since the last call. When the profiler is about to be garbage-collected, the messages are passed to the outer profiler if one is running, or printed to stdout otherwise. If `delayed` is set to False, messages are immediately printed instead. Example: def function(...): profiler = Profiler() ... do stuff ... profiler('did stuff') ... do other stuff ... profiler('did other stuff') # profiler is garbage-collected and flushed at function end If this function is a method of class C, setting `VISPYPROFILE` to "C.function" (without the module name) will enable this profiler. For regular functions, use the qualified name of the function, stripping only the initial "vispy.." prefix from the module. """ _profilers = (config['profile'].split(",") if config['profile'] is not None else []) _depth = 0 _msgs = [] # set this flag to disable all or individual profilers at runtime disable = False class DisabledProfiler(object): def __init__(self, *args, **kwds): pass def __call__(self, *args): pass def finish(self): pass def mark(self, msg=None): pass _disabled_profiler = DisabledProfiler() def __new__(cls, msg=None, disabled='env', delayed=True): """Optionally create a new profiler based on caller's qualname. """ if (disabled is True or (disabled == 'env' and len(cls._profilers) == 0)): return cls._disabled_profiler # determine the qualified name of the caller function caller_frame = sys._getframe(1) try: caller_object_type = type(caller_frame.f_locals["self"]) except KeyError: # we are in a regular function qualifier = caller_frame.f_globals["__name__"].split(".", 1)[1] else: # we are in a method qualifier = caller_object_type.__name__ func_qualname = qualifier + "." + caller_frame.f_code.co_name if (disabled == 'env' and func_qualname not in cls._profilers and 'all' not in cls._profilers): # don't do anything return cls._disabled_profiler # create an actual profiling object cls._depth += 1 obj = super(Profiler, cls).__new__(cls) obj._name = msg or func_qualname obj._delayed = delayed obj._mark_count = 0 obj._finished = False obj._firstTime = obj._last_time = ptime.time() obj._new_msg("> Entering " + obj._name) return obj def __call__(self, msg=None, *args): """Register or print a new message with timing information. """ if self.disable: return if msg is None: msg = str(self._mark_count) self._mark_count += 1 new_time = ptime.time() elapsed = (new_time - self._last_time) * 1000 self._new_msg(" " + msg + ": %0.4f ms", *(args + (elapsed,))) self._last_time = new_time def mark(self, msg=None): self(msg) def _new_msg(self, msg, *args): msg = " " * (self._depth - 1) + msg if self._delayed: self._msgs.append((msg, args)) else: self.flush() print(msg % args) def __del__(self): self.finish() def finish(self, msg=None): """Add a final message; flush the message list if no parent profiler. """ if self._finished or self.disable: return self._finished = True if msg is not None: self(msg) self._new_msg("< Exiting %s, total time: %0.4f ms", self._name, (ptime.time() - self._firstTime) * 1000) type(self)._depth -= 1 if self._depth < 1: self.flush() def flush(self): if self._msgs: print("\n".join([m[0] % m[1] for m in self._msgs])) type(self)._msgs = [] ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/util/wrappers.py������������������������������������������������������������������0000664�0001750�0001750�00000012040�12527672621�020736� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Some wrappers to avoid circular imports, or make certain calls easier. The idea of a 'global' vispy.use function is that although vispy.app and vispy.gloo.gl can be used independently, they are not complely independent for some configureation. E.g. when using real ES 2.0, the app backend should use EGL and not a desktop OpenGL context. Also, we probably want it to be easy to configure vispy to use the ipython notebook backend, which requires specifc config of both app and gl. This module does not have to be aware of the available app and gl backends, but it should be(come) aware of (in)compatibilities between them. """ import subprocess import inspect def use(app=None, gl=None): """ Set the usage options for vispy Specify what app backend and GL backend to use. Also see ``vispy.app.use_app()`` and ``vispy.gloo.gl.use_gl()``. Parameters ---------- app : str The app backend to use (case insensitive). Standard backends: * 'PyQt4': use Qt widget toolkit via PyQt4. * 'PyQt5': use Qt widget toolkit via PyQt5. * 'PySide': use Qt widget toolkit via PySide. * 'PyGlet': use Pyglet backend. * 'Glfw': use Glfw backend (successor of Glut). Widely available on Linux. * 'SDL2': use SDL v2 backend. Additional backends: * 'ipynb_vnc': render in the IPython notebook via a VNC approach (experimental) gl : str The gl backend to use (case insensitive). Options are: * 'gl2': use Vispy's desktop OpenGL API. * 'pyopengl2': use PyOpenGL's desktop OpenGL API. Mostly for testing. * 'es2': (TO COME) use real OpenGL ES 2.0 on Windows via Angle. Availability of ES 2.0 is larger for Windows, since it relies on DirectX. * 'gl+': use the full OpenGL functionality available on your system (via PyOpenGL). Notes ----- If the app option is given, ``vispy.app.use_app()`` is called. If the gl option is given, ``vispy.gloo.use_gl()`` is called. If an app backend name is provided, and that backend could not be loaded, an error is raised. If no backend name is provided, Vispy will first check if the GUI toolkit corresponding to each backend is already imported, and try that backend first. If this is unsuccessful, it will try the 'default_backend' provided in the vispy config. If still not succesful, it will try each backend in a predetermined order. """ if app is None and gl is None: raise TypeError('Must specify at least one of "app" or "gl".') # Example for future. This wont work (yet). if app == 'ipynb_webgl': app = 'headless' gl = 'webgl' # Apply now if gl: import vispy.gloo from vispy import config config['gl_backend'] = gl vispy.gloo.gl.use_gl(gl) if app: import vispy.app vispy.app.use_app(app) def run_subprocess(command, return_code=False, **kwargs): """Run command using subprocess.Popen Run command and wait for command to complete. If the return code was zero then return, otherwise raise CalledProcessError. By default, this will also add stdout= and stderr=subproces.PIPE to the call to Popen to suppress printing to the terminal. Parameters ---------- command : list of str Command to run as subprocess (see subprocess.Popen documentation). return_code : bool If True, the returncode will be returned, and no error checking will be performed (so this function should always return without error). **kwargs : dict Additional kwargs to pass to ``subprocess.Popen``. Returns ------- stdout : str Stdout returned by the process. stderr : str Stderr returned by the process. code : int The command exit code. Only returned if ``return_code`` is True. """ # code adapted with permission from mne-python use_kwargs = dict(stderr=subprocess.PIPE, stdout=subprocess.PIPE) use_kwargs.update(kwargs) p = subprocess.Popen(command, **use_kwargs) output = p.communicate() # communicate() may return bytes, str, or None depending on the kwargs # passed to Popen(). Convert all to unicode str: output = ['' if s is None else s for s in output] output = [s.decode('utf-8') if isinstance(s, bytes) else s for s in output] output = tuple(output) if not return_code and p.returncode: print(output[0]) print(output[1]) err_fun = subprocess.CalledProcessError.__init__ if 'output' in inspect.getargspec(err_fun).args: raise subprocess.CalledProcessError(p.returncode, command, output) else: raise subprocess.CalledProcessError(p.returncode, command) if return_code: output = output + (p.returncode,) return output ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/�����������������������������������������������������������������������������0000775�0001750�0001750�00000000000�12527674621�016512� 5����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/markers/���������������������������������������������������������������������0000775�0001750�0001750�00000000000�12527674621�020156� 5����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/markers/tag.glsl�������������������������������������������������������������0000664�0001750�0001750�00000000677�12510536123�021610� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ----------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ----------------------------------------------------------------------------- float marker_tag(vec2 P, float size) { float r1 = max(abs(P.x)- size/2.0, abs(P.y)- size/6.0); float r2 = abs(P.x-size/2.0)+abs(P.y)-size; return max(r1,.75*r2); } �����������������������������������������������������������������vispy-0.4.0/vispy/glsl/markers/markers.glsl���������������������������������������������������������0000664�0001750�0001750�00000001521�12510536123�022466� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ----------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ----------------------------------------------------------------------------- #include "markers/arrow.glsl" #include "markers/asterisk.glsl" #include "markers/chevron.glsl" #include "markers/clover.glsl" #include "markers/club.glsl" #include "markers/cross.glsl" #include "markers/diamond.glsl" #include "markers/disc.glsl" #include "markers/ellipse.glsl" #include "markers/hbar.glsl" #include "markers/heart.glsl" #include "markers/infinity.glsl" #include "markers/pin.glsl" #include "markers/ring.glsl" #include "markers/spade.glsl" #include "markers/square.glsl" #include "markers/tag.glsl" #include "markers/triangle.glsl" #include "markers/vbar.glsl" �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/markers/pin.glsl�������������������������������������������������������������0000664�0001750�0001750�00000001207�12510536123�021611� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ----------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ----------------------------------------------------------------------------- float marker_pin(vec2 P, float size) { size *= .9; vec2 c1 = vec2(0.0,-0.15)*size; float r1 = length(P-c1)-size/2.675; vec2 c2 = vec2(+1.49,-0.80)*size; float r2 = length(P-c2) - 2.*size; vec2 c3 = vec2(-1.49,-0.80)*size; float r3 = length(P-c3) - 2.*size; float r4 = length(P-c1)-size/5; return max( min(r1,max(max(r2,r3),-P.y)), -r4); } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/markers/hbar.glsl������������������������������������������������������������0000664�0001750�0001750�00000000561�12510536123�021741� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ----------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ----------------------------------------------------------------------------- float marker_hbar(vec2 P, float size) { return max(abs(P.x)- size/6.0, abs(P.y)- size/2.0); } �����������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/markers/marker.frag����������������������������������������������������������0000664�0001750�0001750�00000002636�12510536123�022271� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- // Hooks: // : "stroke", "filled" or "outline" // : "arrow", "asterisk", "chevron", "clover", "club", // "cross", "diamond", "disc", "ellipse", "hbar", // "heart", "infinity", "pin", "ring", "spade", // "square", "tag", "triangle", "vbar" // ---------------------------------------------------------------------------- #include "math/constants.glsl" #include "markers/markers.glsl" #include "antialias/antialias.glsl" // Varyings // ------------------------------------ varying float v_antialias; varying float v_linewidth; varying float v_size; varying float v_texcoord; varying vec4 v_fg_color; varying vec4 v_bg_color; varying vec2 v_orientation; // Main (hooked) // ------------------------------------ void main() { vec2 P = gl_PointCoord.xy - vec2(0.5,0.5); P = vec2(v_orientation.x*P.x - v_orientation.y*P.y, v_orientation.y*P.x + v_orientation.x*P.y); float point_size = M_SQRT2*v_size + 2 * (v_linewidth + 1.5*v_antialias); float distance = marker_(P*point_size, v_size); gl_FragColor = (distance, v_linewidth, v_antialias, v_fg_color, v_bg_color); } ��������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/markers/heart.glsl�����������������������������������������������������������0000664�0001750�0001750�00000001210�12510536123�022120� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ----------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ----------------------------------------------------------------------------- #include "math/constants.glsl" float marker_heart(vec2 P, float size) { float x = M_SQRT2/2.0 * (P.x - P.y); float y = M_SQRT2/2.0 * (P.x + P.y); float r1 = max(abs(x),abs(y))-size/3.5; float r2 = length(P - M_SQRT2/2.0*vec2(+1.0,-1.0)*size/3.5) - size/3.5; float r3 = length(P - M_SQRT2/2.0*vec2(-1.0,-1.0)*size/3.5) - size/3.5; return min(min(r1,r2),r3); } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/markers/disc.glsl������������������������������������������������������������0000664�0001750�0001750�00000000530�12510536123�021743� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ----------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ----------------------------------------------------------------------------- float marker_disc(vec2 P, float size) { return length(P) - size/2; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/markers/infinity.glsl��������������������������������������������������������0000664�0001750�0001750�00000001156�12510536123�022657� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ----------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ----------------------------------------------------------------------------- float marker_infinity(vec2 P, float size) { const vec2 c1 = vec2(+0.2125, 0.00); const vec2 c2 = vec2(-0.2125, 0.00); float r1 = length(P-c1*size) - size/3.5; float r2 = length(P-c1*size) - size/7.5; float r3 = length(P-c2*size) - size/3.5; float r4 = length(P-c2*size) - size/7.5; return min( max(r1,-r2), max(r3,-r4)); } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/markers/ellipse.glsl���������������������������������������������������������0000664�0001750�0001750�00000003545�12510536123�022467� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ----------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ----------------------------------------------------------------------------- // --- ellipse // Created by Inigo Quilez - iq/2013 // License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. float marker_ellipse(vec2 P, float size) { // Alternate version (approximation) float a = 1.0; float b = 2.0; float r = 0.5*size; float f = length( P*vec2(a,b) ); f = length( P*vec2(a,b) ); f = f*(f-r)/length( P*vec2(a*a,b*b) ); return f; /* vec2 ab = vec2(size/3.0, size/2.0); vec2 p = abs( P ); if( p.x > p.y ){ p = p.yx; ab = ab.yx; } float l = ab.y*ab.y - ab.x*ab.x; float m = ab.x*p.x/l; float n = ab.y*p.y/l; float m2 = m*m; float n2 = n*n; float c = (m2 + n2 - 1.0)/3.0; float c3 = c*c*c; float q = c3 + m2*n2*2.0; float d = c3 + m2*n2; float g = m + m*n2; float co; if(d < 0.0) { float p = acos(q/c3)/3.0; float s = cos(p); float t = sin(p)*sqrt(3.0); float rx = sqrt( -c*(s + t + 2.0) + m2 ); float ry = sqrt( -c*(s - t + 2.0) + m2 ); co = ( ry + sign(l)*rx + abs(g)/(rx*ry) - m)/2.0; } else { float h = 2.0*m*n*sqrt( d ); float s = sign(q+h)*pow( abs(q+h), 1.0/3.0 ); float u = sign(q-h)*pow( abs(q-h), 1.0/3.0 ); float rx = -s - u - c*4.0 + 2.0*m2; float ry = (s - u)*sqrt(3.0); float rm = sqrt( rx*rx + ry*ry ); float p = ry/sqrt(rm-rx); co = (p + 2.0*g/rm - m)/2.0; } float si = sqrt(1.0 - co*co); vec2 closestPoint = vec2(ab.x*co, ab.y*si); return length(closestPoint - p ) * sign(p.y-closestPoint.y); */ } �����������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/markers/cross.glsl�����������������������������������������������������������0000664�0001750�0001750�00000001204�12510536123�022151� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ----------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ----------------------------------------------------------------------------- #include "math/constants.glsl" float marker_cross(vec2 P, float size) { float x = M_SQRT2/2.0 * (P.x - P.y); float y = M_SQRT2/2.0 * (P.x + P.y); float r1 = max(abs(x - size/3.0), abs(x + size/3.0)); float r2 = max(abs(y - size/3.0), abs(y + size/3.0)); float r3 = max(abs(x), abs(y)); float r = max(min(r1,r2),r3); r -= size/2; return r; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/markers/asterisk.glsl��������������������������������������������������������0000664�0001750�0001750�00000001257�12510536123�022655� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ----------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ----------------------------------------------------------------------------- #include "math/constants.glsl" float marker_asterisk(vec2 P, float size) { float x = M_SQRT2/2 * (P.x - P.y); float y = M_SQRT2/2 * (P.x + P.y); float r1 = max(abs(x)- size/2, abs(y)- size/10); float r2 = max(abs(y)- size/2, abs(x)- size/10); float r3 = max(abs(P.x)- size/2, abs(P.y)- size/10); float r4 = max(abs(P.y)- size/2, abs(P.x)- size/10); return min( min(r1,r2), min(r3,r4)); } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/markers/__init__.py����������������������������������������������������������0000664�0001750�0001750�00000000000�12510536123�022237� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/markers/club.glsl������������������������������������������������������������0000664�0001750�0001750�00000002170�12510536123�021750� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ----------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ----------------------------------------------------------------------------- #include "math/constants.glsl" float marker_club(vec2 P, float size) { // clover (3 discs) const float t1 = -M_PI/2.0; const vec2 c1 = 0.225*vec2(cos(t1),sin(t1)); const float t2 = t1+2*M_PI/3.0; const vec2 c2 = 0.225*vec2(cos(t2),sin(t2)); const float t3 = t2+2*M_PI/3.0; const vec2 c3 = 0.225*vec2(cos(t3),sin(t3)); float r1 = length( P - c1*size) - size/4.25; float r2 = length( P - c2*size) - size/4.25; float r3 = length( P - c3*size) - size/4.25; float r4 = min(min(r1,r2),r3); // Root (2 circles and 2 planes) const vec2 c4 = vec2(+0.65, 0.125); const vec2 c5 = vec2(-0.65, 0.125); float r5 = length(P-c4*size) - size/1.6; float r6 = length(P-c5*size) - size/1.6; float r7 = P.y - 0.5*size; float r8 = 0.2*size - P.y; float r9 = max(-min(r5,r6), max(r7,r8)); return min(r4,r9); } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/markers/ring.glsl������������������������������������������������������������0000664�0001750�0001750�00000000627�12510536123�021767� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ----------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ----------------------------------------------------------------------------- float marker_ring(vec2 P, float size) { float r1 = length(P) - size/2; float r2 = length(P) - size/4; return max(r1,-r2); } ���������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/markers/marker.vert����������������������������������������������������������0000664�0001750�0001750�00000002462�12510536123�022327� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- // Hooks: // : vec4 function(position, ...) // // ---------------------------------------------------------------------------- #include "math/constants.glsl" // Uniforms // ------------------------------------ uniform float antialias; // Attributes // ------------------------------------ attribute vec2 position; attribute float size; attribute vec4 fg_color; attribute vec4 bg_color; attribute float orientation; attribute float linewidth; // Varyings // ------------------------------------ varying float v_size; varying vec4 v_fg_color; varying vec4 v_bg_color; varying vec2 v_orientation; varying float v_antialias; varying float v_linewidth; // Main (hooked) // ------------------------------------ void main (void) { v_size = size; v_linewidth = linewidth; v_antialias = antialias; v_fg_color = fg_color; v_bg_color = bg_color; v_orientation = vec2(cos(orientation), sin(orientation)); gl_Position = ; gl_PointSize = M_SQRT2 * size + 2.0 * (linewidth + 1.5*antialias); } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/markers/clover.glsl����������������������������������������������������������0000664�0001750�0001750�00000001377�12510536123�022325� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ----------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ----------------------------------------------------------------------------- #include "math/constants.glsl" float marker_clover(vec2 P, float size) { const float t1 = -M_PI/2; const vec2 c1 = 0.25*vec2(cos(t1),sin(t1)); const float t2 = t1+2*M_PI/3; const vec2 c2 = 0.25*vec2(cos(t2),sin(t2)); const float t3 = t2+2*M_PI/3; const vec2 c3 = 0.25*vec2(cos(t3),sin(t3)); float r1 = length( P - c1*size) - size/3.5; float r2 = length( P - c2*size) - size/3.5; float r3 = length( P - c3*size) - size/3.5; return min(min(r1,r2),r3); } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/markers/square.glsl����������������������������������������������������������0000664�0001750�0001750�00000000611�12510536123�022321� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ----------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ----------------------------------------------------------------------------- #include "math/constants.glsl" float marker_square(vec2 P, float size) { return max(abs(P.x), abs(P.y)) - size/2.0; } �����������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/markers/diamond.glsl���������������������������������������������������������0000664�0001750�0001750�00000000737�12510536123�022445� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ----------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ----------------------------------------------------------------------------- #include "math/constants.glsl" float marker_diamond(vec2 P, float size) { float x = M_SQRT2/2.0 * (P.x - P.y); float y = M_SQRT2/2.0 * (P.x + P.y); return max(abs(x), abs(y)) - size/(2.0*M_SQRT2); } ���������������������������������vispy-0.4.0/vispy/glsl/markers/marker-sdf.vert������������������������������������������������������0000664�0001750�0001750�00000002224�12510536123�023075� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- #version 120 // Uniform // ------------------------------------ uniform mat4 u_projection; uniform float u_antialias; // Attributes // ------------------------------------ attribute float a_size; attribute float a_orientation; attribute float a_linewidth; attribute vec3 a_position; attribute vec4 a_fg_color; attribute vec4 a_bg_color; // Varyings // ------------------------------------ varying float v_antialias; varying float v_linewidth; varying float v_size; varying vec4 v_fg_color; varying vec4 v_bg_color; varying vec2 v_rotation; void main (void) { v_size = a_size; v_linewidth = 2.5*a_linewidth; v_antialias = 3.0*u_antialias; v_fg_color = a_fg_color; v_bg_color = a_bg_color; v_rotation = vec2(cos(a_orientation), sin(a_orientation)); gl_Position = u_projection * vec4(a_position, 1.0); gl_PointSize = a_size + 2*(a_linewidth + 1.5*v_antialias); } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/markers/marker-sdf.frag������������������������������������������������������0000664�0001750�0001750�00000005570�12510536123�023043� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- #version 120 // Constants // ------------------------------------ const float SQRT_2 = 1.4142135623730951; // Uniforms // ------------------------------------ uniform sampler2D u_texture; uniform vec2 u_texture_shape; // Varyings // ------------------------------------ varying float v_antialias; varying float v_linewidth; varying vec4 v_fg_color; varying vec4 v_bg_color; varying float v_size; varying vec2 v_rotation; vec4 Nearest(sampler2D u_data, vec2 u_shape, vec2 v_texcoord); vec4 Bilinear(sampler2D u_data, vec2 u_shape, vec2 v_texcoord); vec4 Hanning(sampler2D u_data, vec2 u_shape, vec2 v_texcoord); vec4 Hamming(sampler2D u_data, vec2 u_shape, vec2 v_texcoord); vec4 Hermite(sampler2D u_data, vec2 u_shape, vec2 v_texcoord); vec4 Kaiser(sampler2D u_data, vec2 u_shape, vec2 v_texcoord); vec4 Quadric(sampler2D u_data, vec2 u_shape, vec2 v_texcoord); vec4 Bicubic(sampler2D u_data, vec2 u_shape, vec2 v_texcoord); vec4 CatRom(sampler2D u_data, vec2 u_shape, vec2 v_texcoord); vec4 Mitchell(sampler2D u_data, vec2 u_shape, vec2 v_texcoord); vec4 Spline16(sampler2D u_data, vec2 u_shape, vec2 v_texcoord); vec4 Spline36(sampler2D u_data, vec2 u_shape, vec2 v_texcoord); vec4 Gaussian(sampler2D u_data, vec2 u_shape, vec2 v_texcoord); vec4 Bessel(sampler2D u_data, vec2 u_shape, vec2 v_texcoord); vec4 Sinc(sampler2D u_data, vec2 u_shape, vec2 v_texcoord); vec4 Lanczos(sampler2D u_data, vec2 u_shape, vec2 v_texcoord); vec4 Blackman(sampler2D u_data, vec2 u_shape, vec2 v_texcoord); void main() { vec2 P = gl_PointCoord.xy - vec2(0.5,0.5); P = vec2(v_rotation.x*P.x - v_rotation.y*P.y, v_rotation.y*P.x + v_rotation.x*P.y); P += vec2(0.5,0.5); float r = v_size + 2*(v_linewidth + 1.5*v_antialias); // float signed_distance = r * (texture2D(u_texture, P).r - 0.5); float signed_distance = r * (Bicubic(u_texture, u_texture_shape, P).r - 0.5); float t = v_linewidth/2.0 - v_antialias; float border_distance = abs(signed_distance) - t; float alpha = border_distance/v_antialias; alpha = exp(-alpha*alpha); // Within linestroke if( border_distance < 0 ) gl_FragColor = v_fg_color; else if( signed_distance < 0 ) // Inside shape if( border_distance > (v_linewidth/2.0 + v_antialias) ) gl_FragColor = v_bg_color; else // Line stroke interior border gl_FragColor = mix(v_bg_color,v_fg_color,alpha); else // Outide shape if( border_distance > (v_linewidth/2.0 + v_antialias) ) discard; else // Line stroke exterior border gl_FragColor = vec4(v_fg_color.rgb, v_fg_color.a * alpha); } ����������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/markers/vbar.glsl������������������������������������������������������������0000664�0001750�0001750�00000000561�12510536123�021757� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ----------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ----------------------------------------------------------------------------- float marker_vbar(vec2 P, float size) { return max(abs(P.y)- size/2.0, abs(P.x)- size/6.0); } �����������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/markers/chevron.glsl���������������������������������������������������������0000664�0001750�0001750�00000001131�12510536123�022463� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ----------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ----------------------------------------------------------------------------- #include "math/constants.glsl" float marker_chevron(vec2 P, float size) { float x = 1.0/M_SQRT2 * ((P.x-size/6) - P.y); float y = 1.0/M_SQRT2 * ((P.x-size/6) + P.y); float r1 = max(abs(x), abs(y)) - size/3.0; float r2 = max(abs(x-size/3.0), abs(y-size/3.0)) - size/3.0; return max(r1,-r2); } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/markers/triangle.glsl��������������������������������������������������������0000664�0001750�0001750�00000001053�12510536123�022627� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ----------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ----------------------------------------------------------------------------- #include "math/constants.glsl" float marker_triangle(vec2 P, float size) { float x = M_SQRT2/2.0 * (P.x - (P.y-size/6)); float y = M_SQRT2/2.0 * (P.x + (P.y-size/6)); float r1 = max(abs(x), abs(y)) - size/(2.0*M_SQRT2); float r2 = P.y-size/6; return max(r1,r2); } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/markers/spade.glsl�����������������������������������������������������������0000664�0001750�0001750�00000002021�12510536123�022112� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ----------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ----------------------------------------------------------------------------- #include "math/constants.glsl" float marker_spade(vec2 P, float size) { // Reversed heart (diamond + 2 circles) float s= size * 0.85 / 3.5; float x = M_SQRT2/2.0 * (P.x + P.y) + 0.4*s; float y = M_SQRT2/2.0 * (P.x - P.y) - 0.4*s; float r1 = max(abs(x),abs(y)) - s; float r2 = length(P - M_SQRT2/2.0*vec2(+1.0,+0.2)*s) - s; float r3 = length(P - M_SQRT2/2.0*vec2(-1.0,+0.2)*s) - s; float r4 = min(min(r1,r2),r3); // Root (2 circles and 2 planes) const vec2 c1 = vec2(+0.65, 0.125); const vec2 c2 = vec2(-0.65, 0.125); float r5 = length(P-c1*size) - size/1.6; float r6 = length(P-c2*size) - size/1.6; float r7 = P.y - 0.5*size; float r8 = 0.1*size - P.y; float r9 = max(-min(r5,r6), max(r7,r8)); return min(r4,r9); } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/markers/arrow.glsl�����������������������������������������������������������0000664�0001750�0001750�00000001000�12510536123�022144� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ----------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ----------------------------------------------------------------------------- float marker_arrow(vec2 P, float size) { float r1 = abs(P.x) + abs(P.y) - size/2; float r2 = max(abs(P.x+size/2), abs(P.y)) - size/2; float r3 = max(abs(P.x-size/6)-size/4, abs(P.y)- size/4); return min(r3,max(.75*r1,r2)); } vispy-0.4.0/vispy/glsl/transforms/������������������������������������������������������������������0000775�0001750�0001750�00000000000�12527674621�020710� 5����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/transforms/translate.glsl����������������������������������������������������0000664�0001750�0001750�00000001774�12510536123�023563� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- uniform vec3 translate_translate; vec2 forward(float x, float y) { return vec2(x,y) + translate_translate.xy; } vec2 forward(vec2 P) { return P + translate_translate.xy; } vec3 forward(float x, float y, float z) { return vec3(x,y,z) + translate_translate); } vec3 forward(vec3 P) { return P + translate_translate; } vec4 forward(vec4 P) { return vec4(P.xyz + translate_translate, P.w); } vec2 inverse(float x, float y) { return vec2(x,y) - translate_translate.xy; } vec2 inverse(vec2 P) { return P - translate_translate.xy; } vec3 inverse(float x, float y, float z) { return vec3(x,y,z) - translate_translate); } vec3 inverse(vec3 P) { return P - translate_translate; } vec4 inverse(vec4 P) { return vec4(P.xyz - translate_translate, P.w); } ����vispy-0.4.0/vispy/glsl/transforms/panzoom.glsl������������������������������������������������������0000664�0001750�0001750�00000000671�12510536123�023244� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- uniform vec2 panzoom_scale; uniform vec2 panzoom_translate; vec4 panzoom(vec4 position) { return vec4(panzoom_scale*position.xy + panzoom_translate, position.z, 1.0); } �����������������������������������������������������������������������vispy-0.4.0/vispy/glsl/transforms/trackball.glsl����������������������������������������������������0000664�0001750�0001750�00000000775�12510536123�023525� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- uniform mat4 trackball_view; uniform mat4 trackball_model; uniform mat4 trackball_projection; vec4 transform(vec4 position) { return trackball_projection * trackball_view * trackball_model * position; } ���vispy-0.4.0/vispy/glsl/transforms/viewport-clipping.glsl��������������������������������������������0000664�0001750�0001750�00000001161�12510536123�025236� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- uniform vec4 viewport; // in pixels uniform vec2 iResolution; // in pixels void clipping(void) { vec2 position = gl_FragCoord.xy; if( position.x < (viewport.x)) discard; else if( position.x > (viewport.x+viewport.z)) discard; else if( position.y < (viewport.y)) discard; else if( position.y > (viewport.y+viewport.w)) discard; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/transforms/log-scale.glsl����������������������������������������������������0000664�0001750�0001750�00000006275�12510536123�023435� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- uniform int log_scale_clamp; uniform int log_scale_discard; uniform vec2 log_scale_range; uniform vec2 log_scale_domain; uniform float log_scale_base; float forward(float value) { vec2 domain = log_scale_domain; vec2 range = log_scale_range; float base = log_scale_base; float v = log(value) / log(base); float t = (v - domain.x) /(domain.y - domain.x); #ifdef __FRAGMENT_SHADER__ if (log_scale_discard > 0) if (t != clamp(t, 0.0, 1.0)) discard; #endif if (log_scale_clamp > 0) t = clamp(t, 0.0, 1.0); return sign(value) * (range.x + t*(range.y - range.x)); } vec2 forward(vec2 value) { vec2 domain = log_scale_domain; vec2 range = log_scale_range; float base = log_scale_base; vec2 v = log(value) / log(base); vec2 t = (v - domain.x) /(domain.y - domain.x); #ifdef __FRAGMENT_SHADER__ if (log_scale_discard > 0) if (t != clamp(t, 0.0, 1.0)) discard; #endif if (log_scale_clamp > 0) t = clamp(t, 0.0, 1.0); return sign(value) * (range.x + t*(range.y - range.x)); } vec3 forward(vec3 value) { vec2 domain = log_scale_domain; vec2 range = log_scale_range; float base = log_scale_base; vec3 v = log(value) / log(base); vec3 t = (v - domain.x) /(domain.y - domain.x); #ifdef __FRAGMENT_SHADER__ if (log_scale_discard > 0) if (t != clamp(t, 0.0, 1.0)) discard; #endif if (log_scale_clamp > 0) t = clamp(t, 0.0, 1.0); return sign(value) * (range.x + t*(range.y - range.x)); } float inverse(float value) { vec2 domain = log_scale_domain; vec2 range = log_scale_range; float base = log_scale_base; float t = (abs(value) - range.x) / (range.y - range.x); #ifdef __FRAGMENT_SHADER__ if (log_scale_discard > 0) if (t != clamp(t, 0.0, 1.0)) discard; #endif if (log_scale_clamp > 0) t = clamp(t, 0.0, 1.0); float v = domain.x + t*(domain.y - domain.x); return sign(value) * pow(base, abs(v)); } vec2 inverse(vec2 value) { vec2 domain = log_scale_domain; vec2 range = log_scale_range; float base = log_scale_base; vec2 t = (abs(value) - range.x) / (range.y - range.x); #ifdef __FRAGMENT_SHADER__ if (log_scale_discard > 0) if (t != clamp(t, 0.0, 1.0)) discard; #endif if (log_scale_clamp > 0) t = clamp(t, 0.0, 1.0); vec2 v = domain.x + t*(domain.y - domain.x); return sign(value) * pow(vec2(base), abs(v)); } vec3 inverse(vec3 value) { vec2 domain = log_scale_domain; vec2 range = log_scale_range; float base = log_scale_base; vec3 t = (abs(value) - range.x) / (range.y - range.x); #ifdef __FRAGMENT_SHADER__ if (log_scale_discard > 0) if (t != clamp(t, 0.0, 1.0)) discard; #endif if (log_scale_clamp > 0) t = clamp(t, 0.0, 1.0); vec3 v = domain.x + t*(domain.y - domain.x); return sign(value) * pow(vec3(base), abs(v)); } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/transforms/linear-scale.glsl�������������������������������������������������0000664�0001750�0001750�00000006502�12510536123�024117� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- /* Linear scales are the most common scale, and a good default choice to map a continuous input domain to a continuous output range. The mapping is linear in that the output range value y can be expressed as a linear function of the input domain value x: y = mx + b. The input domain is typically a dimension of the data that you want to visualize, such as the height of students (measured in meters) in a sample population. The output range is typically a dimension of the desired output visualization, such as the height of bars (measured in pixels) in a histogram. */ uniform int linear_scale_clamp; uniform int linear_scale_discard; uniform vec2 linear_scale_range; uniform vec2 linear_scale_domain; float forward(float value) { vec2 domain = linear_scale_domain; vec2 range = linear_scale_range; float t = (value - domain.x) /(domain.y - domain.x); #ifdef __FRAGMENT_SHADER__ if (linear_scale_discard > 0) if (t != clamp(t, 0.0, 1.0)) discard; #endif if (linear_scale_clamp > 0) t = clamp(t, 0.0, 1.0); return range.x + t*(range.y - range.x); } vec2 forward(vec2 value) { vec2 domain = linear_scale_domain; vec2 range = linear_scale_range; vec2 t = (value - domain.x) /(domain.y - domain.x); #ifdef __FRAGMENT_SHADER__ if (linear_scale_discard > 0) if (t != clamp(t, 0.0, 1.0)) discard; #endif if (linear_scale_clamp > 0) t = clamp(t, 0.0, 1.0); return range.x + t*(range.y - range.x); } vec3 forward(vec3 value) { vec2 domain = linear_scale_domain; vec2 range = linear_scale_range; vec3 t = (value - domain.x) /(domain.y - domain.x); #ifdef __FRAGMENT_SHADER__ if (linear_scale_discard > 0) if (t != clamp(t, 0.0, 1.0)) discard; #endif if (linear_scale_clamp > 0) t = clamp(t, 0.0, 1.0); return range.x + t*(range.y - range.x); } float inverse(float value) { vec2 domain = linear_scale_domain; vec2 range = linear_scale_range; float t = (value - range.x) / (range.y - range.x); #ifdef __FRAGMENT_SHADER__ if (linear_scale_discard > 0) if (t != clamp(t, 0.0, 1.0)) discard; #endif if (linear_scale_clamp > 0) t = clamp(t, 0.0, 1.0); return domain.x + t*(domain.y - domain.x); } vec2 inverse(vec2 value) { vec2 domain = linear_scale_domain; vec2 range = linear_scale_range; vec2 t = (value - range.x) / (range.y - range.x); #ifdef __FRAGMENT_SHADER__ if (linear_scale_discard > 0) if (t != clamp(t, 0.0, 1.0)) discard; #endif if (linear_scale_clamp > 0) t = clamp(t, 0.0, 1.0); return domain.x + t*(domain.y - domain.x); } vec3 inverse(vec3 value) { vec2 domain = linear_scale_domain; vec2 range = linear_scale_range; vec3 t = (value - range.x) / (range.y - range.x); #ifdef __FRAGMENT_SHADER__ if (linear_scale_discard > 0) if (t != clamp(t, 0.0, 1.0)) discard; #endif if (linear_scale_clamp > 0) t = clamp(t, 0.0, 1.0); return domain.x + t*(domain.y - domain.x); } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/transforms/viewport.glsl�����������������������������������������������������0000664�0001750�0001750�00000003416�12510536123�023440� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- uniform vec4 viewport_local; uniform vec4 viewport_global; uniform int viewport_transform; uniform int viewport_clipping; #ifdef __VERTEX_SHADER__ void transform(void) { if (viewport_transform == 0) return; vec4 position = gl_Position; float w = viewport_local.z / viewport_global.z; float h = viewport_local.w / viewport_global.w; float x = 2.0*(viewport_local.x / viewport_global.z) - 1.0 + w; float y = 2.0*(viewport_local.y / viewport_global.w) - 1.0 + h; gl_Position = vec4((x + w*position.x/position.w)*position.w, (y + h*position.y/position.w)*position.w, position.z, position.w); } #endif #ifdef __FRAGMENT_SHADER__ void clipping(void) { // if (viewport_clipping == 0) return; vec2 position = gl_FragCoord.xy; if( position.x < (viewport_local.x)) discard; else if( position.x > (viewport_local.x+viewport_local.z)) discard; else if( position.y < (viewport_local.y)) discard; else if( position.y > (viewport_local.y+viewport_local.w)) discard; /* if( length(position.x - viewport_local.x) < 1.0 ) gl_FragColor = vec4(0,0,0,1); else if( length(position.x - viewport_local.x - viewport_local.z) < 1.0 ) gl_FragColor = vec4(0,0,0,1); else if( length(position.y - viewport_local.y) < 1.0 ) gl_FragColor = vec4(0,0,0,1); else if( length(position.y - viewport_local.y - viewport_local.w) < 1.0 ) gl_FragColor = vec4(0,0,0,1); */ } #endif ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/transforms/projection.glsl���������������������������������������������������0000664�0001750�0001750�00000000170�12510536123�023727� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Simple matrix projection uniform mat4 projection; vec4 transform(vec4 position) { return projection*position; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/transforms/identity_inverse.glsl���������������������������������������������0000664�0001750�0001750�00000001203�12510536123�025135� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- /* --------------------------------------------------------- Inverse cartesian projection (identity) Parameters: ----------- position : 2d position in cartesian coordinates Return: ------- 2d position in cartesian coordinates --------------------------------------------------------- */ vec2 transform_identity_inverse(vec2 P) { return P; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/transforms/identity_forward.glsl���������������������������������������������0000664�0001750�0001750�00000001202�12510536123�025125� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- /* --------------------------------------------------------- Forward identity projection (identity) Parameters: ----------- position : 2d position in cartesian coordinates Return: ------- 2d position in cartesian coordinates --------------------------------------------------------- */ vec2 transform_identity_forward(vec2 P) { return P; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/transforms/power-scale.glsl��������������������������������������������������0000664�0001750�0001750�00000007514�12510536123�024005� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- /* Power scales are similar to linear scales, except there's an exponential transform that is applied to the input domain value before the output range value is computed. The mapping to the output range value y can be expressed as a function of the input domain value x: y = mx^k + b, where k is the exponent value. Power scales also support negative values, in which case the input value is multiplied by -1, and the resulting output value is also multiplied by -1. */ uniform int power_scale_clamp; uniform int power_scale_discard; uniform vec2 power_scale_range; uniform vec2 power_scale_domain; uniform float power_scale_exponent; float forward(float value) { vec2 domain = power_scale_domain; vec2 range = power_scale_range; float exponent = power_scale_exponent; float v = pow(abs(value), exponent); float t = (v - domain.x) /(domain.y - domain.x); #ifdef __FRAGMENT_SHADER__ if (power_scale_discard > 0) if (t != clamp(t, 0.0, 1.0)) discard; #endif if (power_scale_clamp > 0) t = clamp(t, 0.0, 1.0); return sign(value) * (range.x + t*(range.y - range.x)); } vec2 forward(vec2 value) { vec2 domain = power_scale_domain; vec2 range = power_scale_range; float exponent = power_scale_exponent; vec2 v = pow(abs(value), vec2(exponent)); vec2 t = (v - domain.x) /(domain.y - domain.x); #ifdef __FRAGMENT_SHADER__ if (power_scale_discard > 0) if (t != clamp(t, 0.0, 1.0)) discard; #endif if (power_scale_clamp > 0) t = clamp(t, 0.0, 1.0); return sign(value) * (range.x + t*(range.y - range.x)); } vec3 forward(vec3 value) { vec2 domain = power_scale_domain; vec2 range = power_scale_range; float exponent = power_scale_exponent; vec3 v = pow(abs(value), vec3(exponent)); vec3 t = (v - domain.x) /(domain.y - domain.x); #ifdef __FRAGMENT_SHADER__ if (power_scale_discard > 0) if (t != clamp(t, 0.0, 1.0)) discard; #endif if (power_scale_clamp > 0) t = clamp(t, 0.0, 1.0); return sign(value) * (range.x + t*(range.y - range.x)); } float inverse(float value) { vec2 domain = power_scale_domain; vec2 range = power_scale_range; float exponent = power_scale_exponent; float t = (abs(value) - range.x) / (range.y - range.x); #ifdef __FRAGMENT_SHADER__ if (power_scale_discard > 0) if (t != clamp(t, 0.0, 1.0)) discard; #endif if (power_scale_clamp > 0) t = clamp(t, 0.0, 1.0); float v = domain.x + t*(domain.y - domain.x); return sign(value) * pow(abs(v), 1.0/exponent); } vec2 inverse(vec2 value) { vec2 domain = power_scale_domain; vec2 range = power_scale_range; float exponent = power_scale_exponent; vec2 t = (abs(value) - range.x) / (range.y - range.x); #ifdef __FRAGMENT_SHADER__ if (power_scale_discard > 0) if (t != clamp(t, 0.0, 1.0)) discard; #endif if (power_scale_clamp > 0) t = clamp(t, 0.0, 1.0); vec2 v = domain.x + t*(domain.y - domain.x); return sign(value) * pow(abs(v), vec2(1.0/exponent)); } vec3 inverse(vec3 value) { vec2 domain = power_scale_domain; vec2 range = power_scale_range; float exponent = power_scale_exponent; vec3 t = (abs(value) - range.x) / (range.y - range.x); #ifdef __FRAGMENT_SHADER__ if (power_scale_discard > 0) if (t != clamp(t, 0.0, 1.0)) discard; #endif if (power_scale_clamp > 0) t = clamp(t, 0.0, 1.0); vec3 v = domain.x + t*(domain.y - domain.x); return sign(value) * pow(abs(v), vec3(1.0/exponent)); } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/transforms/rotate.glsl�������������������������������������������������������0000664�0001750�0001750�00000002163�12510536123�023055� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ----------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier // Distributed under the (new) BSD License. See LICENSE.txt for more info. // ----------------------------------------------------------------------------- uniform vec3 rotate_axis; uniform vec3 rotate_origin; uniform float rotate_angle; uniform mat4 rotate_forward_matrix; uniform mat4 rotate_inverse_matrix; vec2 forward(vec2 position) { vec4 P = vec4(position,0.0,1.0); P.xy -= rotate_origin.xy; P = rotate_forward_matrix*P; P.xy += rotate_origin.xy; return P.xy; } vec3 forward(vec3 position) { vec4 P = vec4(position,1.0); P.xyz -= rotate_origin; P = rotate_forward_matrix*P; P.xyz += rotate_origin; return P.xyz; } vec2 inverse(vec2 position) { vec4 P = vec4(position,0.0,1.0); P.xy -= rotate_origin.xy; P = rotate_inverse_matrix*P; P.xy += rotate_origin.xy; return P.xy; } vec3 inverse(vec3 position) { vec4 P = vec4(position,1.0); P.xyz -= rotate_origin; P = rotate_inverse_matrix*P; P.xyz += rotate_origin; return P.xyz; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/transforms/y.glsl������������������������������������������������������������0000664�0001750�0001750�00000000623�12510536123�022026� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- float get_y(vec2 xy) { return xy.y; } float get_y(vec3 xyz) { return xyz.y; } float get_y(vec4 xyzw) { return xyzw.y; } �������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/transforms/azimuthal-equidistant.glsl����������������������������������������0000664�0001750�0001750�00000002406�12510536123�026105� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- float scale(float x) { float c = acos(x); if (c != 0.0) return c / sin(c); discard; } float angle(float x) { return x; } vec2 forward(float longitude, float latitude) { float cos_lon = cos(longitude); float cos_lat = cos(latitude); float k = scale(cos_lon * cos_lat); return vec2( k * cos_lat * sin(longitude), k * sin(latitude)); } vec2 forward(vec2 P) { return forward(P.x,P.y); } vec3 forward(vec3 P) { return vec3(forward(P.x,P.y), P.z); } vec4 forward(vec4 P) { return vec4(forward(P.x,P.y), P.z, P.w); } vec2 inverse(float x, float y) { float rho = sqrt(x*x + y*y); float c = angle(rho); float sinc = sin(c); float cosc = cos(c); //if (rho != 0) return vec2( atan(x*sinc, rho*cosc), asin(y*sinc/rho)); //else //return vec2( atan(x*sinc, rho*cosc), asin(1e10)); } vec2 inverse(vec2 P) { return inverse(P.x,P.y); } vec3 inverse(vec3 P) { return vec3(inverse(P.x,P.y), P.z); } vec4 inverse(vec4 P) { return vec4(inverse(P.x,P.y), P.z, P.w); } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/transforms/z.glsl������������������������������������������������������������0000664�0001750�0001750�00000000550�12510536123�022026� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- float get_z(vec3 xyz) { return xyz.z; } float get_z(vec4 xyzw) { return xyzw.z; } ��������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/transforms/azimuthal-equal-area.glsl�����������������������������������������0000664�0001750�0001750�00000002320�12510536123�025563� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- float scale(float x) { return sqrt(2.0/(1.0+x)); } float angle(float x) { return 2.0 * asin(x/2.0); } vec2 forward(float longitude, float latitude) { float cos_lon = cos(longitude); float cos_lat = cos(latitude); float k = scale(cos_lon * cos_lat); return vec2( k * cos_lat * sin(longitude), k * sin(latitude)); } vec2 forward(vec2 P) { return forward(P.x,P.y); } vec3 forward(vec3 P) { return vec3(forward(P.x,P.y), P.z); } vec4 forward(vec4 P) { return vec4(forward(P.x,P.y), P.z, P.w); } vec2 inverse(float x, float y) { float rho = sqrt(x*x + y*y); float c = angle(rho); float sinc = sin(c); float cosc = cos(c); if (rho != 0) return vec2( atan(x*sinc, rho*cosc), asin(y*sinc/rho)); return vec2( atan(x*sinc, rho*cosc), asin(0)); } vec2 inverse(vec2 P) { return inverse(P.x,P.y); } vec3 inverse(vec3 P) { return vec3(inverse(P.x,P.y), P.z); } vec4 inverse(vec4 P) { return vec4(inverse(P.x,P.y), P.z, P.w); } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/transforms/__init__.py�������������������������������������������������������0000664�0001750�0001750�00000000000�12527672621�023005� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/transforms/hammer.glsl�������������������������������������������������������0000664�0001750�0001750�00000003133�12510536123�023026� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- // // Hammer projection // See http://en.wikipedia.org/wiki/Hammer_projection // // ---------------------------------------------------------------------------- #include "math/constants.glsl" const float B = 2.0; vec4 forward(float longitude, float latitude, float z, float w) { float cos_lat = cos(latitude); float sin_lat = sin(latitude); float cos_lon = cos(longitude/B); float sin_lon = sin(longitude/B); float d = sqrt(1.0 + cos_lat * cos_lon); float x = (B * M_SQRT2 * cos_lat * sin_lon) / d; float y = (M_SQRT2 * sin_lat) / d; return vec4(x,y,z,w); } vec4 forward(float x, float y) {return forward(x, y, 0.0, 1.0);} vec4 forward(float x, float y, float z) {return forward(x, y, 0.0, 1.0);} vec4 forward(vec2 P) { return forward(P.x, P.y); } vec4 forward(vec3 P) { return forward(P.x, P.y, P.z, 1.0); } vec4 forward(vec4 P) { return forward(P.x, P.y, P.z, P.w); } vec2 inverse(float x, float y) { float z = 1.0 - (x*x/16.0) - (y*y/4.0); // if (z < 0.0) // discard; z = sqrt(z); float longitude = 2.0*atan( (z*x),(2.0*(2.0*z*z - 1.0))); float latitude = asin(z*y); return vec2(longitude, latitude); } vec2 inverse(vec2 P) { return inverse(P.x,P.y); } vec3 inverse(vec3 P) { return vec3(inverse(P.x,P.y), P.z); } vec4 inverse(vec4 P) { return vec4(inverse(P.x,P.y), P.z, P.w); } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/transforms/identity.glsl�����������������������������������������������������0000664�0001750�0001750�00000000544�12510536123�023411� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- #include "transforms/identity_forward.glsl" #include "transforms/identity_inverse.glsl" ������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/transforms/mercator-transverse-forward.glsl����������������������������������0000664�0001750�0001750�00000002133�12510536123�027224� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- // Constants // --------- const float k0 = 0.75; const float a = 1.00; // Helper functions // ---------------- float cosh(float x) { return 0.5 * (exp(x)+exp(-x)); } float sinh(float x) { return 0.5 * (exp(x)-exp(-x)); } /* --------------------------------------------------------- Transverse Mercator projection -> http://en.wikipedia.org/wiki/Transverse_Mercator_projection Parameters: ----------- position : 2d position in (longitude,latitiude) coordinates Return: ------- 2d position in cartesian coordinates --------------------------------------------------------- */ vec2 transform_forward(vec2 P) { float lambda = P.x; float phi = P.y; float x = 0.5*k0*log((1.0+sin(lambda)*cos(phi)) / (1.0 - sin(lambda)*cos(phi))); float y = k0*a*atan(tan(phi), cos(lambda)); return vec2(x,y); } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/transforms/position.glsl�����������������������������������������������������0000664�0001750�0001750�00000001404�12510536123�023420� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- vec4 position(float x) { return vec4(x, 0.0, 0.0, 1.0); } vec4 position(float x, float y) { return vec4(x, y, 0.0, 1.0); } vec4 position(vec2 xy) { return vec4(xy, 0.0, 1.0); } vec4 position(float x, float y, float z) { return vec4(x, y, z, 1.0); } vec4 position(vec3 xyz) { return vec4(xyz, 1.0); } vec4 position(vec4 xyzw) { return xyzw; } vec4 position(vec2 xy, float z) { return vec4(xy, z, 1.0); } vec4 position(float x, vec2 yz) { return vec4(x, yz, 1.0); } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/transforms/viewport-transform.glsl�������������������������������������������0000664�0001750�0001750�00000001344�12510536123�025447� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- uniform vec4 viewport; // in pixels uniform vec2 iResolution; // in pixels vec4 transform(vec4 position) { float w = viewport.z / iResolution.x; float h = viewport.w / iResolution.y; float x = 2.0*(viewport.x / iResolution.x) - 1.0 + w; float y = 2.0*(viewport.y / iResolution.y) - 1.0 + h; return vec4((x + w*position.x/position.w)*position.w, (y + h*position.y/position.w)*position.w, position.z, position.w); } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/transforms/pvm.glsl����������������������������������������������������������0000664�0001750�0001750�00000000633�12510536123�022361� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- uniform mat4 view; uniform mat4 model; uniform mat4 projection; vec4 transform(vec4 position) { return projection*view*model*position; } �����������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/transforms/transverse_mercator.glsl������������������������������������������0000664�0001750�0001750�00000002600�12510536123�025643� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- // // Transverse Mercator projection // See http://en.wikipedia.org/wiki/Transverse_Mercator_projection // // ---------------------------------------------------------------------------- #include "math/constants.glsl" // Constants const float k0 = 0.75; const float a = 1.00; // Helper functions float cosh(float x) { return 0.5 * (exp(x)+exp(-x)); } float sinh(float x) { return 0.5 * (exp(x)-exp(-x)); } vec2 forward(float lambda, float phi) { float x = 0.5*k0*log((1.0+sin(lambda)*cos(phi)) / (1.0 - sin(lambda)*cos(phi))); float y = k0*a*atan(tan(phi), cos(lambda)); return vec2(x,y); } vec2 forward(vec2 P) { return forward(P.x,P.y); } vec3 forward(vec3 P) { return vec3(forward(P.x,P.y), P.z); } vec4 forward(vec4 P) { return vec4(forward(P.x,P.y), P.z, P.w); } vec2 inverse(float x, float y) { float lambda = atan(sinh(x/(k0*a)),cos(y/(k0*a))); float phi = asin(sin(y/(k0*a))/cosh(x/(k0*a))); return vec2(lambda,phi); } vec2 inverse(vec2 P) { return inverse(P.x,P.y); } vec3 inverse(vec3 P) { return vec3(inverse(P.x,P.y), P.z); } vec4 inverse(vec4 P) { return vec4(inverse(P.x,P.y), P.z, P.w); } ��������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/transforms/mercator-transverse-inverse.glsl����������������������������������0000664�0001750�0001750�00000002204�12510536123�027232� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- // Constants // ------------------------------------ const float k0 = 0.75; const float a = 1.00; // Helper functions // ------------------------------------ float cosh(float x) { return 0.5 * (exp(x)+exp(-x)); } float sinh(float x) { return 0.5 * (exp(x)-exp(-x)); } /* --------------------------------------------------------- Inverse Lambert azimuthal equal-area projection -> http://en.wikipedia.org/wiki/Transverse_Mercator_projection Parameters: ----------- position : 2d position in cartesian coordinates Return: ------- 2d position in (longitude,latitiude) coordinates --------------------------------------------------------- */ vec2 transform_inverse(vec2 P) { float x = P.x; float y = P.y; float lambda = atan(sinh(x/(k0*a)),cos(y/(k0*a))); float phi = asin(sin(y/(k0*a))/cosh(x/(k0*a))); return vec2(lambda,phi); } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/transforms/x.glsl������������������������������������������������������������0000664�0001750�0001750�00000000673�12510536123�022032� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- float get_x(float x) { return x; } float get_x(vec2 xy) { return xy.x; } float get_x(vec3 xyz) { return xyz.x; } float get_x(vec4 xyzw) { return xyzw.x; } ���������������������������������������������������������������������vispy-0.4.0/vispy/glsl/transforms/polar.glsl��������������������������������������������������������0000664�0001750�0001750�00000003207�12510536123�022674� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- // // Polar projection // See http://en.wikipedia.org/wiki/Hammer_projection // // ---------------------------------------------------------------------------- #include "math/constants.glsl" uniform float polar_origin; vec4 forward(float rho, float theta, float z, float w) { return vec4(rho * cos(theta + polar_origin), rho * sin(theta + polar_origin), z, w); } vec4 forward(float x, float y) {return forward(x, y, 0.0, 1.0);} vec4 forward(float x, float y, float z) {return forward(x, y, z, 1.0);} vec4 forward(vec2 P) { return forward(P.x, P.y); } vec4 forward(vec3 P) { return forward(P.x, P.y, P.z, 1.0); } vec4 forward(vec4 P) { return forward(P.x, P.y, P.z, P.w); } // vec4 forward(float x, float y, float z) { return vec3(forward(x,y),z); } vec4 inverse(float x, float y, float z, float w) { float rho = length(vec2(x,y)); float theta = atan(y,x); if( theta < 0.0 ) theta = 2.0*M_PI+theta; return vec4(rho, theta-polar_origin, z, w); } vec4 inverse(float x, float y) {return inverse(x,y,0.0,1.0); } vec4 inverse(float x, float y, float z) {return inverse(x,y,z,1.0); } vec4 inverse(vec2 P) { return inverse(P.x, P.y, 0.0, 1.0); } vec4 inverse(vec3 P) { return inverse(P.x, P.y, P.z, 1.0); } vec4 inverse(vec4 P) { return inverse(P.x, P.y, P.z, P.w); } //vec3 inverse(float x, float y, float z) { return vec3(inverse(x,y),z); } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/antialias/�������������������������������������������������������������������0000775�0001750�0001750�00000000000�12527674621�020457� 5����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/antialias/caps.glsl����������������������������������������������������������0000664�0001750�0001750�00000003257�12510536123�022261� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- #include "antialias/stroke.glsl" // Cap types // ---------------------------- const int CAP_NONE = 0; const int CAP_ROUND = 1; const int CAP_TRIANGLE_IN = 2; const int CAP_TRIANGLE_OUT = 3; const int CAP_SQUARE = 4; const int CAP_BUTT = 5; /* --------------------------------------------------------- Compute antialiased fragment color for a line cap. Require the stroke function. Parameters: ----------- type : Type of cap dx,dy : signed distances to cap point (in pixels) linewidth: Stroke line width (in pixels) antialias: Stroke antialiased area (in pixels) stroke: Stroke color Return: ------- Fragment color (vec4) --------------------------------------------------------- */ vec4 cap(int type, float dx, float dy, float linewidth, float antialias, vec4 color) { float d = 0.0; dx = abs(dx); dy = abs(dy); float t = linewidth/2.0 - antialias; // Round if (type == CAP_ROUND) d = sqrt(dx*dx+dy*dy); // Square else if (type == CAP_SQUARE) d = max(dx,dy); // Butt else if (type == CAP_BUTT) d = max(dx+t,dy); // Triangle in else if (type == CAP_TRIANGLE_IN) d = (dx+abs(dy)); // Triangle out else if (type == CAP_TRIANGLE_OUT) d = max(abs(dy),(t+dx-abs(dy))); // None else discard; return stroke(d, linewidth, antialias, color); } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/antialias/filled.glsl��������������������������������������������������������0000664�0001750�0001750�00000002570�12510536123�022567� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- /* --------------------------------------------------------- Compute antialiased fragment color for a filled shape. Parameters: ----------- distance : signed distance to border (in pixels) linewidth: Stroke line width (in pixels) antialias: Stroke antialiased area (in pixels) fill: Fill color Return: ------- Fragment color (vec4) --------------------------------------------------------- */ vec4 filled(float distance, float linewidth, float antialias, vec4 bg_color) { vec4 frag_color; float t = linewidth/2.0 - antialias; float signed_distance = distance; float border_distance = abs(signed_distance) - t; float alpha = border_distance/antialias; alpha = exp(-alpha*alpha); if( border_distance < 0.0 ) frag_color = bg_color; else if( signed_distance < 0.0 ) frag_color = bg_color; else frag_color = vec4(bg_color.rgb, alpha * bg_color.a); return frag_color; } vec4 filled(float distance, float linewidth, float antialias, vec4 fg_color, vec4 bg_color) { return filled(distance, linewidth, antialias, fg_color); } ����������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/antialias/antialias.glsl�����������������������������������������������������0000664�0001750�0001750�00000000560�12510536123�023272� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- #include "antialias/stroke.glsl" #include "antialias/filled.glsl" #include "antialias/outline.glsl" ������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/antialias/__init__.py��������������������������������������������������������0000664�0001750�0001750�00000000000�12510536123�022540� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/antialias/cap-round.glsl�����������������������������������������������������0000664�0001750�0001750�00000001636�12510536123�023222� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- #include "antialias/stroke.glsl" /* --------------------------------------------------------- Compute antialiased fragment color for a line cap. Type: round Parameters: ----------- dx,dy : signed distances to cap point (in pixels) linewidth: Stroke line width (in pixels) antialias: Stroke antialiased area (in pixels) stroke: Stroke color Return: ------- Fragment color (vec4) --------------------------------------------------------- */ vec4 cap_round(float dx, float dy, float linewidth, float antialias, vec4 color) { float d = lenght(vec2(dx,dy)); return stroke(d, linewidth, antialias, color); } ��������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/antialias/cap-square.glsl����������������������������������������������������0000664�0001750�0001750�00000001712�12510536123�023366� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- #include "antialias/stroke.glsl" /* --------------------------------------------------------- Compute antialiased fragment color for a line cap. Type: square Parameters: ----------- dx,dy : signed distances to cap point (in pixels) linewidth: Stroke line width (in pixels) antialias: Stroke antialiased area (in pixels) stroke: Stroke color Return: ------- Fragment color (vec4) --------------------------------------------------------- */ vec4 cap_square(float dx, float dy, float linewidth, float antialias, vec4 color) { float t = linewidth/2.0 - antialias; float d = max(abs(dx),abs(dy)); return stroke(d, linewidth, antialias, color); } ������������������������������������������������������vispy-0.4.0/vispy/glsl/antialias/cap.glsl�����������������������������������������������������������0000664�0001750�0001750�00000003257�12510536123�022076� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- #include "antialias/stroke.glsl" // Cap types // ---------------------------- const int CAP_NONE = 0; const int CAP_ROUND = 1; const int CAP_TRIANGLE_IN = 2; const int CAP_TRIANGLE_OUT = 3; const int CAP_SQUARE = 4; const int CAP_BUTT = 5; /* --------------------------------------------------------- Compute antialiased fragment color for a line cap. Require the stroke function. Parameters: ----------- type : Type of cap dx,dy : signed distances to cap point (in pixels) linewidth: Stroke line width (in pixels) antialias: Stroke antialiased area (in pixels) stroke: Stroke color Return: ------- Fragment color (vec4) --------------------------------------------------------- */ vec4 cap(int type, float dx, float dy, float linewidth, float antialias, vec4 color) { float d = 0.0; dx = abs(dx); dy = abs(dy); float t = linewidth/2.0 - antialias; // Round if (type == CAP_ROUND) d = sqrt(dx*dx+dy*dy); // Square else if (type == CAP_SQUARE) d = max(dx,dy); // Butt else if (type == CAP_BUTT) d = max(dx+t,dy); // Triangle in else if (type == CAP_TRIANGLE_IN) d = (dx+abs(dy)); // Triangle out else if (type == CAP_TRIANGLE_OUT) d = max(abs(dy),(t+dx-abs(dy))); // None else discard; return stroke(d, linewidth, antialias, color); } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/antialias/cap-butt.glsl������������������������������������������������������0000664�0001750�0001750�00000001712�12510536123�023044� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- #include "antialias/stroke.glsl" /* --------------------------------------------------------- Compute antialiased fragment color for a line cap. Type: butt Parameters: ----------- dx,dy : signed distances to cap point (in pixels) linewidth: Stroke line width (in pixels) antialias: Stroke antialiased area (in pixels) stroke: Stroke color Return: ------- Fragment color (vec4) --------------------------------------------------------- */ vec4 cap_butt(float dx, float dy, float linewidth, float antialias, vec4 color) { float t = linewidth/2.0 - antialias; float d = max(abs(dx)+t, abs(dy)); return stroke(d, linewidth, antialias, color); } ������������������������������������������������������vispy-0.4.0/vispy/glsl/antialias/cap-triangle-out.glsl����������������������������������������������0000664�0001750�0001750�00000001742�12510536123�024503� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- #include "antialias/stroke.glsl" /* --------------------------------------------------------- Compute antialiased fragment color for a line cap. Type: triangle out Parameters: ----------- dx,dy : signed distances to cap point (in pixels) linewidth: Stroke line width (in pixels) antialias: Stroke antialiased area (in pixels) stroke: Stroke color Return: ------- Fragment color (vec4) --------------------------------------------------------- */ vec4 cap_triangle_out(float dx, float dy, float linewidth, float antialias, vec4 color) { float t = linewidth/2.0 - antialias; float d = max(abs(dy),(t+abs(dx)-abs(dy))); return stroke(d, linewidth, antialias, color); } ������������������������������vispy-0.4.0/vispy/glsl/antialias/outline.glsl�������������������������������������������������������0000664�0001750�0001750�00000002443�12510536123�023006� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- /* --------------------------------------------------------- Compute antialiased fragment color for an outlined shape. Parameters: ----------- distance : signed distance to border (in pixels) linewidth: Stroke line width (in pixels) antialias: Stroke antialiased area (in pixels) stroke: Stroke color fill: Fill color Return: ------- Fragment color (vec4) --------------------------------------------------------- */ vec4 outline(float distance, float linewidth, float antialias, vec4 fg_color, vec4 bg_color) { vec4 frag_color; float t = linewidth/2.0 - antialias; float signed_distance = distance; float border_distance = abs(signed_distance) - t; float alpha = border_distance/antialias; alpha = exp(-alpha*alpha); if( border_distance < 0.0 ) frag_color = fg_color; else if( signed_distance < 0.0 ) frag_color = mix(bg_color, fg_color, sqrt(alpha)); else frag_color = vec4(fg_color.rgb, fg_color.a * alpha); return frag_color; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/antialias/stroke.glsl��������������������������������������������������������0000664�0001750�0001750�00000002465�12510536123�022642� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- /* --------------------------------------------------------- Compute antialiased fragment color for a stroke line. Parameters: ----------- distance : signed distance to border (in pixels) linewidth: Stroke line width (in pixels) antialias: Stroke antialiased area (in pixels) stroke: Stroke color Return: ------- Fragment color (vec4) --------------------------------------------------------- */ vec4 stroke(float distance, float linewidth, float antialias, vec4 fg_color) { vec4 frag_color; float t = linewidth/2.0 - antialias; float signed_distance = distance; float border_distance = abs(signed_distance) - t; float alpha = border_distance/antialias; alpha = exp(-alpha*alpha); if( border_distance < 0.0 ) frag_color = fg_color; else frag_color = vec4(fg_color.rgb, fg_color.a * alpha); return frag_color; } vec4 stroke(float distance, float linewidth, float antialias, vec4 fg_color, vec4 bg_color) { return stroke(distance, linewidth, antialias, fg_color); } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/antialias/cap-triangle-in.glsl�����������������������������������������������0000664�0001750�0001750�00000001752�12510536123�024303� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- #include "antialias/stroke.glsl" /* --------------------------------------------------------- Compute antialiased fragment color for a line cap. Type: triangle in Parameters: ----------- type : Type of cap dx,dy : signed distances to cap point (in pixels) linewidth: Stroke line width (in pixels) antialias: Stroke antialiased area (in pixels) stroke: Stroke color Return: ------- Fragment color (vec4) --------------------------------------------------------- */ vec4 cap_triangle_in(float dx, float dy, float linewidth, float antialias, vec4 color) { float t = linewidth/2.0 - antialias; float d = (abs(dx)+abs(dy)); return stroke(d, linewidth, antialias, color); } ����������������������vispy-0.4.0/vispy/glsl/build-spatial-filters.py�����������������������������������������������������0000664�0001750�0001750�00000044664�12510536123�023264� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env python # -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # glumpy is an OpenGL framework for the fast visualization of numpy arrays. # Copyright (C) 2009-2011 Nicolas P. Rougier. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # 1. Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY NICOLAS P. ROUGIER ''AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO # EVENT SHALL NICOLAS P. ROUGIER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, # INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # The views and conclusions contained in the software and documentation are # those of the authors and should not be interpreted as representing official # policies, either expressed or implied, of Nicolas P. Rougier. # ----------------------------------------------------------------------------- ''' A filter is a shader that transform the current displayed texture. Since shaders cannot be easily serialized within the GPU, they have to be well structured on the python side such that we can possibly merge them into a single source code for both vertex and fragment. Consequently, there is a default code for both vertex and fragment with specific entry points such that filter knows where to insert their specific code (declarations, functions and call (or code) to be inserted in the main function). Spatial interpolation filter classes for OpenGL textures. Each filter generates a one-dimensional lookup table (weights value from 0 to ceil(radius)) that is uploaded to video memory (as a 1d texture) and is then read by the shader when necessary. It avoids computing weight values for each pixel. Furthemore, each 2D-convolution filter is separable and can be computed using 2 1D-convolution with same 1d-kernel (= the lookup table values). Available filters: - Nearest (radius 0.5) - Bilinear (radius 1.0) - Hanning (radius 1.0) - Hamming (radius 1.0) - Hermite (radius 1.0) - Kaiser (radius 1.0) - Quadric (radius 1.5) - Bicubic (radius 2.0) - CatRom (radius 2.0) - Mitchell (radius 2.0) - Spline16 (radius 2.0) - Spline36 (radius 4.0) - Gaussian (radius 2.0) - Bessel (radius 3.2383) - Sinc (radius 4.0) - Lanczos (radius 4.0) - Blackman (radius 4.0) Note:: Weights code has been translated from the antigrain geometry library available at http://www.antigrain.com/ ''' import math import numpy as np class SpatialFilter(object): ''' ''' def __init__(self, radius=1.0): self.radius = radius def weight(self, x): ''' Return filter weight for a distance x. :Parameters: ``x`` : 0 < float < ceil(self.radius) Distance to be used to compute weight. ''' raise NotImplemented def kernel(self, size=4*512): radius = self.radius r = int(max(1.0, math.ceil(radius))) samples = size / r n = size # r*samples kernel = np.zeros(n) X = np.linspace(0, r, n) for i in range(n): kernel[i] = self.weight(X[i]) N = np.zeros(samples) for i in range(r): N += kernel[::+1][i*samples:(i+1)*samples] N += kernel[::-1][i*samples:(i+1)*samples] for i in range(r): kernel[i*samples:(i+1)*samples:+1] /= N return kernel def filter_code(self): n = int(math.ceil(self.radius)) filter_1 = 'filter1D_radius%d' % n filter_2 = 'filter2D_radius%d' % n code = '' code += 'vec4\n' code += '%s( sampler2D kernel, float index, float x, ' % filter_1 for i in range(2*n): if i == 2*n-1: code += 'vec4 c%d )\n' % i else: code += 'vec4 c%d, ' % i code += '{\n' code += ' float w, w_sum = 0.0;\n' code += ' vec4 r = vec4(0.0,0.0,0.0,0.0);\n' for i in range(n): code += ' w = texture2D(kernel, vec2(%f+(x/%.1f),index) ).r;\n' % (1.0 - (i + 1) / float(n), n) # noqa code += ' w = w*kernel_scale + kernel_bias;\n' # noqa # code += ' w_sum += w;' code += ' r += c%d * w;\n' % i code += ' w = texture2D(kernel, vec2(%f-(x/%.1f),index) ).r;\n' % ((i+1)/float(n), n) # noqa code += ' w = w*kernel_scale + kernel_bias;\n' # code += ' w_sum += w;' code += ' r += c%d * w;\n' % (i + n) # code += ' return r/w_sum;\n' code += ' return r;\n' code += '}\n' code += 'vec4\n' code += '%s' % filter_2 code += '(sampler2D texture, sampler2D kernel, float index, vec2 uv, vec2 pixel )\n' # noqa code += '{\n' code += ' vec2 texel = uv/pixel - vec2(0.0,0.0) ;\n' code += ' vec2 f = fract(texel);\n' code += ' texel = (texel-fract(texel)+vec2(0.001,0.001))*pixel;\n' for i in range(2*n): code += ' vec4 t%d = %s(kernel, index, f.x,\n' % (i, filter_1) for j in range(2*n): x, y = (-n+1+j, -n+1+i) code += ' texture2D( texture, texel + vec2(%d,%d)*pixel),\n' % (x, y) # noqa # Remove last trailing',' and close function call code = code[:-2] + ');\n' code += ' return %s(kernel, index, f.y, ' % filter_1 for i in range(2*n): code += 't%d, ' % i # Remove last trailing',' and close function call code = code[:-2] + ');\n' code += '}\n' return code def call_code(self, index): code = "" n = int(math.ceil(self.radius)) filter_1 = 'filter1D_radius%d' % n # noqa filter_2 = 'filter2D_radius%d' % n code += 'vec4 %s(sampler2D texture, vec2 shape, vec2 uv)\n' % self.__class__.__name__ # noqa code += '{' code += ' return %s(texture, u_kernel, %f, uv, 1.0/shape); ' % (filter_2, index) # noqa code += '}\n' return code class Nearest(SpatialFilter): ''' Nearest (=None) filter (radius = 0.5). Weight function:: w(x) = 1 ''' def __init__(self): SpatialFilter.__init__(self, radius=.5) def weight(self, x): return 1.0 def _get_code(self): self.build_LUT() code = 'vec4\n' code += 'interpolate( sampler2D texture, sampler1D kernel, vec2 uv, vec2 pixel )\n' # noqa code += '{\n return texture2D( texture, uv );\n}\n' return code code = property(_get_code, doc='''filter functions code''') class Bilinear(SpatialFilter): ''' Bilinear filter (radius = 1.0). Weight function:: w(x) = 1 - x ''' def __init__(self): SpatialFilter.__init__(self, radius=1.0) def weight(self, x): return 1.0 - x class Hanning(SpatialFilter): ''' Hanning filter (radius = 1.0). Weight function:: w(x) = 0.5 + 0.5 * cos(pi * x) ''' def __init__(self): SpatialFilter.__init__(self, radius=1.0) def weight(self, x): return 0.5 + 0.5 * math.cos(math.pi * x) class Hamming(SpatialFilter): ''' Hamming filter (radius = 1.0). Weight function:: w(x) = 0.54 + 0.46 * cos(pi * x) ''' def __init__(self): SpatialFilter.__init__(self, radius=1.0) def weight(self, x): return 0.54 + 0.46 * math.cos(math.pi * x) class Hermite(SpatialFilter): ''' Hermite filter (radius = 1.0). Weight function:: w(x) = (2*x-3)*x^2 + 1 ''' def __init__(self): SpatialFilter.__init__(self, radius=1.0) def weight(self, x): return (2.0 * x - 3.0) * x * x + 1.0 class Quadric(SpatialFilter): ''' Quadric filter (radius = 1.5). Weight function:: | 0.0 ≤ x < 0.5: 0.75 - x*x w(x) = | 0.5 ≤ x < 1.5: 0.5 - (x-1.5)^2 | 1.5 ≤ x : 0 ''' def __init__(self): SpatialFilter.__init__(self, radius=1.5) def weight(self, x): if x < 0.75: return 0.75 - x * x elif x < 1.5: t = x - 1.5 return 0.5 * t * t else: return 0.0 class Bicubic(SpatialFilter): ''' Bicubic filter (radius = 2.0). Weight function:: w(x) = 1/6((x+2)^3 - 4*(x+1)^3 + 6*x^3 -4*(x-1)^3) ''' def __init__(self): SpatialFilter.__init__(self, radius=2.0) def pow3(self, x): if x <= 0: return 0 else: return x * x * x def weight(self, x): return (1.0/6.0) * (self.pow3(x + 2) - 4 * self.pow3(x + 1) + 6 * self.pow3(x) - 4 * self.pow3(x - 1)) class Kaiser(SpatialFilter): ''' Kaiser filter (radius = 1.0). Weight function:: w(x) = bessel_i0(a sqrt(1-x^2)* 1/bessel_i0(b) ''' def __init__(self, b=6.33): self.a = b self.epsilon = 1e-12 self.i0a = 1.0 / self.bessel_i0(b) SpatialFilter.__init__(self, radius=1.0) def bessel_i0(self, x): s = 1.0 y = x * x / 4.0 t = y i = 2 while t > self.epsilon: s += t t *= float(y) / (i * i) i += 1 return s def weight(self, x): if x > 1: return 0 return self.bessel_i0(self.a * math.sqrt(1.0 - x * x)) * self.i0a class CatRom(SpatialFilter): ''' Catmull-Rom filter (radius = 2.0). Weight function:: | 0 ≤ x < 1: 0.5*(2 + x^2*(-5+x*3)) w(x) = | 1 ≤ x < 2: 0.5*(4 + x*(-8+x*(5-x))) | 2 ≤ x : 0 ''' def __init__(self, size=256*8): SpatialFilter.__init__(self, radius=2.0) def weight(self, x): if x < 1.0: return 0.5 * (2.0 + x * x * (-5.0 + x * 3.0)) elif x < 2.0: return 0.5 * (4.0 + x * (-8.0 + x * (5.0 - x))) else: return 0.0 class Mitchell(SpatialFilter): ''' Mitchell-Netravali filter (radius = 2.0). Weight function:: | 0 ≤ x < 1: p0 + x^2*(p2 + x*p3) w(x) = | 1 ≤ x < 2: q0 + x*(q1 + x*(q2 + x*q3)) | 2 ≤ x : 0 ''' def __init__(self, b=1.0/3.0, c=1.0/3.0): self.p0 = (6.0 - 2.0 * b) / 6.0 self.p2 = (-18.0 + 12.0 * b + 6.0 * c) / 6.0 self.p3 = (12.0 - 9.0 * b - 6.0 * c) / 6.0 self.q0 = (8.0 * b + 24.0 * c) / 6.0 self.q1 = (-12.0 * b - 48.0 * c) / 6.0 self.q2 = (6.0 * b + 30.0 * c) / 6.0 self.q3 = (-b - 6.0 * c) / 6.0 SpatialFilter.__init__(self, radius=2.0) def weight(self, x): if x < 1.0: return self.p0 + x * x * (self.p2 + x * self.p3) elif x < 2.0: return self.q0 + x * (self.q1 + x * (self.q2 + x * self.q3)) else: return 0.0 class Spline16(SpatialFilter): ''' Spline16 filter (radius = 2.0). Weight function:: | 0 ≤ x < 1: ((x-9/5)*x - 1/5)*x + 1 w(x) = | | 1 ≤ x < 2: ((-1/3*(x-1) + 4/5)*(x-1) - 7/15 )*(x-1) ''' def __init__(self): SpatialFilter.__init__(self, radius=2.0) def weight(self, x): if x < 1.0: return ((x - 9.0/5.0) * x - 1.0/5.0) * x + 1.0 else: return ((-1.0/3.0 * (x-1) + 4.0/5.0) * (x-1) - 7.0/15.0) * (x-1) class Spline36(SpatialFilter): ''' Spline36 filter (radius = 3.0). Weight function:: | 0 ≤ x < 1: ((13/11*x - 453/209)*x -3/209)*x +1 w(x) = | 1 ≤ x < 2: ((-6/11*(x-1) - 270/209)*(x-1) -156/209)*(x-1) | 2 ≤ x < 3: (( 1/11*(x-2) - 45/209)*(x-2) + 26/209)*(x-2) ''' def __init__(self): SpatialFilter.__init__(self, radius=3.0) def weight(self, x): if x < 1.0: return ((13.0/11.0 * x - 453.0/209.0) * x - 3.0/209.0) * x + 1.0 elif x < 2.0: return ((-6.0/11.0 * (x-1) + 270.0/209.0) * (x-1) - 156.0 / 209.0) * (x-1) # noqa else: return ((1.0 / 11.0 * (x-2) - 45.0/209.0) * (x - 2) + 26.0/209.0) * (x-2) # noqa class Gaussian(SpatialFilter): ''' Gaussian filter (radius = 2.0). Weight function:: w(x) = exp(-2x^2) * sqrt(2/pi) Note:: This filter does not seem to be correct since: x = np.linspace(0, 1.0, 100 ) f = weight z = f(x+1)+f(x)+f(1-x)+f(2-x) z should be 1 everywhere but it is not the case and it produces "grid effects". ''' def __init__(self): SpatialFilter.__init__(self, radius=2.0) def weight(self, x): return math.exp(-2.0 * x * x) * math.sqrt(2.0 / math.pi) class Bessel(SpatialFilter): ''' Bessel filter (radius = 3.2383). ''' def __init__(self): SpatialFilter.__init__(self, radius=3.2383) def besj(self, x, n): ''' Function BESJ calculates Bessel function of first kind of order n Arguments: n - an integer (>=0), the order x - value at which the Bessel function is required -------------------- C++ Mathematical Library Converted from equivalent FORTRAN library Converted by Gareth Walker for use by course 392 computational project All functions tested and yield the same results as the corresponding FORTRAN versions. If you have any problems using these functions please report them to M.Muldoon@UMIST.ac.uk Documentation available on the web http://www.ma.umist.ac.uk/mrm/Teaching/392/libs/392.html Version 1.0 8/98 29 October, 1999 -------------------- Adapted for use in AGG library by Andy Wilk (castor.vulgaris@gmail.com) Adapted for use in vispy library by Nicolas P. Rougier (Nicolas.Rougier@inria.fr) ----------------------------------------------------------------------- ''' if n < 0: return 0.0 d = 1e-6 b = 0 if math.fabs(x) <= d: if n != 0: return 0 return 1 b1 = 0 # b1 is the value from the previous iteration # Set up a starting order for recurrence m1 = int(math.fabs(x)) + 6 if math.fabs(x) > 5: m1 = int(math.fabs(1.4 * x + 60 / x)) m2 = int(n + 2 + math.fabs(x) / 4) if m1 > m2: m2 = m1 # Apply recurrence down from curent max order while True: c3 = 0 c2 = 1e-30 c4 = 0 m8 = 1 if m2 / 2 * 2 == m2: m8 = -1 imax = m2 - 2 for i in range(1, imax+1): c6 = 2 * (m2 - i) * c2 / x - c3 c3 = c2 c2 = c6 if m2 - i - 1 == n: b = c6 m8 = -1 * m8 if m8 > 0: c4 = c4 + 2 * c6 c6 = 2 * c2 / x - c3 if n == 0: b = c6 c4 += c6 b /= c4 if math.fabs(b - b1) < d: return b b1 = b m2 += 3 def weight(self, x): if x == 0.0: return math.pi/4.0 else: return self.besj(math.pi * x, 1) / (2.0 * x) class Sinc(SpatialFilter): ''' Sinc filter (radius = 4.0). Weight function:: ''' def __init__(self, size=256, radius=4.0): SpatialFilter.__init__(self, radius=max(radius, 2.0)) def weight(self, x): if x == 0.0: return 1.0 x *= math.pi return (math.sin(x) / x) class Lanczos(SpatialFilter): ''' Lanczos filter (radius = 4.0). Weight function:: ''' def __init__(self, size=256, radius=4.0): SpatialFilter.__init__(self, radius=max(radius, 2.0)) def weight(self, x): if x == 0.0: return 1.0 elif x > self.radius: return 0.0 x *= math.pi xr = x / self.radius return (math.sin(x) / x) * (math.sin(xr)/xr) class Blackman(SpatialFilter): ''' Blackman filter (radius = 4.0). ''' def __init__(self, size=256, radius=4.0): SpatialFilter.__init__(self, radius=max(radius, 2.0)) def weight(self, x): if x == 0.0: return 1.0 elif x > self.radius: return 0.0 x *= math.pi xr = x / self.radius return (math.sin(x) / x) * (0.42 + 0.5*math.cos(xr) + 0.08*math.cos(2*xr)) # noqa # Generate kernels texture (16 x 1024) filters = [Bilinear(), Hanning(), Hamming(), Hermite(), Kaiser(), Quadric(), Bicubic(), CatRom(), Mitchell(), Spline16(), Spline36(), Gaussian(), Bessel(), Sinc(), Lanczos(), Blackman()] n = 1024 K = np.zeros((16, n)) for i, f in enumerate(filters): K[i] = f.kernel(n) bias = K.min() scale = K.max()-K.min() K = (K-bias)/scale np.save("spatial-filters.npy", K.astype(np.float32)) print("// ------------------------------------") print("// Automatically generated, do not edit") print("// ------------------------------------") print("") print("const float kernel_bias = %f;" % bias) print("const float kernel_scale = %f;" % scale) print("uniform sampler2D u_kernel;") print("") F = SpatialFilter(1.0) print(F.filter_code()) F = SpatialFilter(2.0) print(F.filter_code()) F = SpatialFilter(3.0) print(F.filter_code()) F = SpatialFilter(4.0) print(F.filter_code()) # Generate filter functions # Special case for nearest print("""vec4 Nearest(sampler2D texture, vec2 shape, vec2 uv)""") print("""{ return texture2D(texture,uv); }\n""") for i, f in enumerate(filters): print(f.call_code((i+0.5)/16.0)) ����������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/__init__.py������������������������������������������������������������������0000664�0001750�0001750�00000002262�12527672621�020623� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2014, Nicolas P. Rougier # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- import os import os.path as op from vispy import config def find(name): """Locate a filename into the shader library.""" if op.exists(name): return name path = op.dirname(__file__) or '.' paths = [path] + config['include_path'] for path in paths: filename = op.abspath(op.join(path, name)) if op.exists(filename): return filename for d in os.listdir(path): fullpath = op.abspath(op.join(path, d)) if op.isdir(fullpath): filename = op.abspath(op.join(fullpath, name)) if op.exists(filename): return filename return None def get(name): """Retrieve code from the given filename.""" filename = find(name) if filename is None: raise RuntimeError('Could not find %s' % name) with open(filename) as fid: return fid.read() ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/colormaps/�������������������������������������������������������������������0000775�0001750�0001750�00000000000�12527674621�020511� 5����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/colormaps/wheel.glsl���������������������������������������������������������0000664�0001750�0001750�00000001317�12510536123�022464� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- #include "colormaps/util.glsl" // Wheel colormap by Morgan McGuire vec3 colormap_wheel(float t) { return clamp(abs(fract(t + vec3(1.0, 2.0 / 3.0, 1.0 / 3.0)) * 6.0 - 3.0) -1.0, 0.0, 1.0); } vec3 colormap_wheel(float t, vec3 under, vec3 over) { return colormap_underover(t, colormap_wheel(t), under, over); } vec4 colormap_wheel(float t, vec4 under, vec4 over) { return colormap_underover(t, vec4(colormap_wheel(t),1.0), under, over); } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/colormaps/util.glsl����������������������������������������������������������0000664�0001750�0001750�00000001746�12510536123�022343� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- /* * t <= 0 : return 0 * 0 < t < 1 : return t * t >= 1 : return 0 */ float colormap_segment(float edge0, float edge1, float x) { return step(edge0,x) * (1.0-step(edge1,x)); } /* * t <= 0 : return under * 0 < t < 1 : return color * t >= 1 : return over */ vec3 colormap_underover(float t, vec3 color, vec3 under, vec3 over) { return step(t,0.0)*under + colormap_segment(0.0,1.0,t)*color + step(1.0,t)*over; } /* * t <= 0 : return under * 0 < t < 1 : return color * t >= 1 : return over */ vec4 colormap_underover(float t, vec4 color, vec4 under, vec4 over) { return step(t,0.0)*under + colormap_segment(0.0,1.0,t)*color + step(1.0,t)*over; } ��������������������������vispy-0.4.0/vispy/glsl/colormaps/colormaps.glsl�����������������������������������������������������0000664�0001750�0001750�00000001415�12510536123�023356� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- #include "colormaps/util.glsl" #include "colormaps/user.glsl" #include "colormaps/hot.glsl" #include "colormaps/gray.glsl" #include "colormaps/cool.glsl" #include "colormaps/wheel.glsl" #include "colormaps/autumn.glsl" #include "colormaps/winter.glsl" #include "colormaps/spring.glsl" #include "colormaps/summer.glsl" #include "colormaps/ice.glsl" #include "colormaps/fire.glsl" #include "colormaps/icefire.glsl" #include "colormaps/reds.glsl" #include "colormaps/blues.glsl" #include "colormaps/greens.glsl" ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/colormaps/fire.glsl����������������������������������������������������������0000664�0001750�0001750�00000001260�12510536123�022302� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- #include "colormaps/util.glsl" vec3 colormap_fire(float t) { return mix(mix(vec3(1,1,1), vec3(1,1,0), t), mix(vec3(1,1,0), vec3(1,0,0), t*t), t); } vec3 colormap_fire(float t, vec3 under, vec3 over) { return colormap_underover(t, colormap_fire(t), under, over); } vec4 colormap_fire(float t, vec4 under, vec4 over) { return colormap_underover(t, vec4(colormap_fire(t),1.0), under, over); } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/colormaps/blues.glsl���������������������������������������������������������0000664�0001750�0001750�00000001172�12510536123�022471� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- #include "colormaps/util.glsl" vec3 colormap_blues(float t) { return mix(vec3(1,1,1), vec3(0,0,1), t); } vec3 colormap_blues(float t, vec3 under, vec3 over) { return colormap_underover(t, colormap_blues(t), under, over); } vec4 colormap_blues(float t, vec4 under, vec4 over) { return colormap_underover(t, vec4(colormap_blues(t),1.0), under, over); } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/colormaps/hot.glsl�����������������������������������������������������������0000664�0001750�0001750�00000001321�12510536123�022145� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- #include "colormaps/util.glsl" vec3 colormap_hot(float t) { return vec3(smoothstep(0.0, 1.0/3.0,t), smoothstep(1.0/3.0,2.0/3.0,t), smoothstep(2.0/3.0,1.0, t)); } vec3 colormap_hot(float t, vec3 under, vec3 over) { return colormap_underover(t, colormap_hot(t), under, over); } vec4 colormap_hot(float t, vec4 under, vec4 over) { return colormap_underover(t, vec4(colormap_hot(t),1.0), under, over); } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/colormaps/gray.glsl����������������������������������������������������������0000664�0001750�0001750�00000001134�12510536123�022317� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- #include "colormaps/util.glsl" vec3 colormap_gray(float t) { return vec3(t); } vec3 colormap_gray(float t, vec3 under, vec3 over) { return colormap_underover(t, colormap_gray(t), under, over); } vec4 colormap_gray(float t, vec4 under, vec4 over) { return colormap_underover(t, vec4(colormap_gray(t),1.0), under, over); } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/colormaps/icefire.glsl�������������������������������������������������������0000664�0001750�0001750�00000001436�12510536123�022770� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- #include "colormaps/util.glsl" #include "colormaps/ice.glsl" #include "colormaps/fire.glsl" vec3 colormap_icefire(float t) { return colormap_segment(0.0,0.5,t) * colormap_ice(2.0*(t-0.0)) + colormap_segment(0.5,1.0,t) * colormap_fire(2.0*(t-0.5)); } vec3 colormap_icefire(float t, vec3 under, vec3 over) { return colormap_underover(t, colormap_icefire(t), under, over); } vec4 colormap_icefire(float t, vec4 under, vec4 over) { return colormap_underover(t, vec4(colormap_icefire(t),1.0), under, over); } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/colormaps/ice.glsl�����������������������������������������������������������0000664�0001750�0001750�00000001136�12510536123�022117� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- #include "colormaps/util.glsl" vec3 colormap_ice(float t) { return vec3(t, t, 1.0); } vec3 colormap_ice(float t, vec3 under, vec3 over) { return colormap_underover(t, colormap_ice(t), under, over); } vec4 colormap_ice(float t, vec4 under, vec4 over) { return colormap_underover(t, vec4(colormap_ice(t),1.0), under, over); } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/colormaps/color-space.glsl���������������������������������������������������0000664�0001750�0001750�00000001051�12510536123�023562� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������vec3 hsv_to_rgb(vec3 c) { vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); } vec3 rgb_to_hsv(vec3 c) { vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); float d = q.x - min(q.w, q.y); float e = 1.0e-10; return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/colormaps/spring.glsl��������������������������������������������������������0000664�0001750�0001750�00000001213�12510536123�022655� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- #include "colormaps/util.glsl" vec3 colormap_spring(float t) { return mix(vec3(1.0,0.0,1.0), vec3(1.0,1.0,0.0), t); } vec3 colormap_spring(float t, vec3 under, vec3 over) { return colormap_underover(t, colormap_spring(t), under, over); } vec4 colormap_spring(float t, vec4 under, vec4 over) { return colormap_underover(t, vec4(colormap_spring(t),1.0), under, over); } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/colormaps/reds.glsl����������������������������������������������������������0000664�0001750�0001750�00000001165�12510536123�022316� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- #include "colormaps/util.glsl" vec3 colormap_reds(float t) { return mix(vec3(1,1,1), vec3(1,0,0), t); } vec3 colormap_reds(float t, vec3 under, vec3 over) { return colormap_underover(t, colormap_reds(t), under, over); } vec4 colormap_reds(float t, vec4 under, vec4 over) { return colormap_underover(t, vec4(colormap_reds(t),1.0), under, over); } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/colormaps/summer.glsl��������������������������������������������������������0000664�0001750�0001750�00000001213�12510536123�022663� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- #include "colormaps/util.glsl" vec3 colormap_summer(float t) { return mix(vec3(0.0,0.5,0.4), vec3(1.0,1.0,0.4), t); } vec3 colormap_summer(float t, vec3 under, vec3 over) { return colormap_underover(t, colormap_summer(t), under, over); } vec4 colormap_summer(float t, vec4 under, vec4 over) { return colormap_underover(t, vec4(colormap_summer(t),1.0), under, over); } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/colormaps/__init__.py��������������������������������������������������������0000664�0001750�0001750�00000000000�12510536123�022572� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/colormaps/winter.glsl��������������������������������������������������������0000664�0001750�0001750�00000001221�12510536123�022662� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- #include "colormaps/util.glsl" vec3 colormap_winter(float t) { return mix(vec3(0.0,0.0,1.0), vec3(0.0,1.0,0.5), sqrt(t)); } vec3 colormap_winter(float t, vec3 under, vec3 over) { return colormap_underover(t, colormap_winter(t), under, over); } vec4 colormap_winter(float t, vec4 under, vec4 over) { return colormap_underover(t, vec4(colormap_winter(t),1.0), under, over); } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/colormaps/parse.py�����������������������������������������������������������0000664�0001750�0001750�00000002035�12510536123�022157� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������import os import re def get(filename): for path in ["..", "."]: filepath = os.path.join(path, filename) if os.path.exists(filepath): with open(filepath) as infile: code = infile.read() # comment = '#line 0 // Start of "%s"\n' % filename comment = '// --- start of "%s" ---\n' % filename return comment + code return '#error "%s" not found !\n' % filename code = """ #include "colormap/colormaps.glsl" """ re_include = re.compile('\#include\s*"(?P[a-zA-Z0-9\-\.\/]+)"') includes = [] def replace(match): filename = match.group("filename") if filename not in includes: includes.append(filename) text = get(filename) # lineno = code.count("\n",0,match.start())+1 # text += '\n#line %d // End of "%s"' % (lineno, filename) text += '// --- end of "%s" ---\n' % filename return text return '' while re.search(re_include, code): code = re.sub(re_include, replace, code) print(code) ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/colormaps/user.glsl����������������������������������������������������������0000664�0001750�0001750�00000001214�12510536123�022332� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- #include "colormaps/util.glsl" uniform sampler1D colormap; vec3 colormap_user(float t) { return texture1D(colormap, t).rgb; } vec3 colormap_user(float t, vec3 under, vec3 over) { return colormap_underover(t, colormap_user(t), under, over); } vec4 colormap_user(float t, vec4 under, vec4 over) { return colormap_underover(t, vec4(colormap_user(t),1.0), under, over); } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/colormaps/autumn.glsl��������������������������������������������������������0000664�0001750�0001750�00000001213�12510536123�022664� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- #include "colormaps/util.glsl" vec3 colormap_autumn(float t) { return mix(vec3(1.0,0.0,0.0), vec3(1.0,1.0,0.0), t); } vec3 colormap_autumn(float t, vec3 under, vec3 over) { return colormap_underover(t, colormap_autumn(t), under, over); } vec4 colormap_autumn(float t, vec4 under, vec4 over) { return colormap_underover(t, vec4(colormap_autumn(t),1.0), under, over); } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/colormaps/greens.glsl��������������������������������������������������������0000664�0001750�0001750�00000001177�12510536123�022647� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- #include "colormaps/util.glsl" vec3 colormap_greens(float t) { return mix(vec3(1,1,1), vec3(0,1,0), t); } vec3 colormap_greens(float t, vec3 under, vec3 over) { return colormap_underover(t, colormap_greens(t), under, over); } vec4 colormap_greens(float t, vec4 under, vec4 over) { return colormap_underover(t, vec4(colormap_greens(t),1.0), under, over); } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/colormaps/cool.glsl����������������������������������������������������������0000664�0001750�0001750�00000001201�12510536123�022304� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- #include "colormaps/util.glsl" vec3 colormap_cool(float t) { return mix(vec3(0.0,1.0,1.0), vec3(1.0,0.0,1.0), t); } vec3 colormap_cool(float t, vec3 under, vec3 over) { return colormap_underover(t, colormap_cool(t), under, over); } vec4 colormap_cool(float t, vec4 under, vec4 over) { return colormap_underover(t, vec4(colormap_cool(t),1.0), under, over); } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/misc/������������������������������������������������������������������������0000775�0001750�0001750�00000000000�12527674621�017445� 5����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/misc/__init__.py�������������������������������������������������������������0000664�0001750�0001750�00000000000�12510536123�021526� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/misc/spatial-filters.frag����������������������������������������������������0000664�0001750�0001750�00000033554�12510536123�023405� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ------------------------------------ // Automatically generated, do not edit // ------------------------------------ const float kernel_bias = -0.234377; const float kernel_scale = 1.241974; uniform sampler2D u_kernel; vec4 filter1D_radius1( sampler2D kernel, float index, float x, vec4 c0, vec4 c1 ) { float w, w_sum = 0.0; vec4 r = vec4(0.0,0.0,0.0,0.0); w = texture2D(kernel, vec2(0.000000+(x/1.0),index) ).r; w = w*kernel_scale + kernel_bias; r += c0 * w; w = texture2D(kernel, vec2(1.000000-(x/1.0),index) ).r; w = w*kernel_scale + kernel_bias; r += c1 * w; return r; } vec4 filter2D_radius1(sampler2D texture, sampler2D kernel, float index, vec2 uv, vec2 pixel ) { vec2 texel = uv/pixel - vec2(0.0,0.0) ; vec2 f = fract(texel); texel = (texel-fract(texel)+vec2(0.001,0.001))*pixel; vec4 t0 = filter1D_radius1(kernel, index, f.x, texture2D( texture, texel + vec2(0,0)*pixel), texture2D( texture, texel + vec2(1,0)*pixel)); vec4 t1 = filter1D_radius1(kernel, index, f.x, texture2D( texture, texel + vec2(0,1)*pixel), texture2D( texture, texel + vec2(1,1)*pixel)); return filter1D_radius1(kernel, index, f.y, t0, t1); } vec4 filter1D_radius2( sampler2D kernel, float index, float x, vec4 c0, vec4 c1, vec4 c2, vec4 c3 ) { float w, w_sum = 0.0; vec4 r = vec4(0.0,0.0,0.0,0.0); w = texture2D(kernel, vec2(0.500000+(x/2.0),index) ).r; w = w*kernel_scale + kernel_bias; r += c0 * w; w = texture2D(kernel, vec2(0.500000-(x/2.0),index) ).r; w = w*kernel_scale + kernel_bias; r += c2 * w; w = texture2D(kernel, vec2(0.000000+(x/2.0),index) ).r; w = w*kernel_scale + kernel_bias; r += c1 * w; w = texture2D(kernel, vec2(1.000000-(x/2.0),index) ).r; w = w*kernel_scale + kernel_bias; r += c3 * w; return r; } vec4 filter2D_radius2(sampler2D texture, sampler2D kernel, float index, vec2 uv, vec2 pixel ) { vec2 texel = uv/pixel - vec2(0.0,0.0) ; vec2 f = fract(texel); texel = (texel-fract(texel)+vec2(0.001,0.001))*pixel; vec4 t0 = filter1D_radius2(kernel, index, f.x, texture2D( texture, texel + vec2(-1,-1)*pixel), texture2D( texture, texel + vec2(0,-1)*pixel), texture2D( texture, texel + vec2(1,-1)*pixel), texture2D( texture, texel + vec2(2,-1)*pixel)); vec4 t1 = filter1D_radius2(kernel, index, f.x, texture2D( texture, texel + vec2(-1,0)*pixel), texture2D( texture, texel + vec2(0,0)*pixel), texture2D( texture, texel + vec2(1,0)*pixel), texture2D( texture, texel + vec2(2,0)*pixel)); vec4 t2 = filter1D_radius2(kernel, index, f.x, texture2D( texture, texel + vec2(-1,1)*pixel), texture2D( texture, texel + vec2(0,1)*pixel), texture2D( texture, texel + vec2(1,1)*pixel), texture2D( texture, texel + vec2(2,1)*pixel)); vec4 t3 = filter1D_radius2(kernel, index, f.x, texture2D( texture, texel + vec2(-1,2)*pixel), texture2D( texture, texel + vec2(0,2)*pixel), texture2D( texture, texel + vec2(1,2)*pixel), texture2D( texture, texel + vec2(2,2)*pixel)); return filter1D_radius2(kernel, index, f.y, t0, t1, t2, t3); } vec4 filter1D_radius3( sampler2D kernel, float index, float x, vec4 c0, vec4 c1, vec4 c2, vec4 c3, vec4 c4, vec4 c5 ) { float w, w_sum = 0.0; vec4 r = vec4(0.0,0.0,0.0,0.0); w = texture2D(kernel, vec2(0.666667+(x/3.0),index) ).r; w = w*kernel_scale + kernel_bias; r += c0 * w; w = texture2D(kernel, vec2(0.333333-(x/3.0),index) ).r; w = w*kernel_scale + kernel_bias; r += c3 * w; w = texture2D(kernel, vec2(0.333333+(x/3.0),index) ).r; w = w*kernel_scale + kernel_bias; r += c1 * w; w = texture2D(kernel, vec2(0.666667-(x/3.0),index) ).r; w = w*kernel_scale + kernel_bias; r += c4 * w; w = texture2D(kernel, vec2(0.000000+(x/3.0),index) ).r; w = w*kernel_scale + kernel_bias; r += c2 * w; w = texture2D(kernel, vec2(1.000000-(x/3.0),index) ).r; w = w*kernel_scale + kernel_bias; r += c5 * w; return r; } vec4 filter2D_radius3(sampler2D texture, sampler2D kernel, float index, vec2 uv, vec2 pixel ) { vec2 texel = uv/pixel - vec2(0.0,0.0) ; vec2 f = fract(texel); texel = (texel-fract(texel)+vec2(0.001,0.001))*pixel; vec4 t0 = filter1D_radius3(kernel, index, f.x, texture2D( texture, texel + vec2(-2,-2)*pixel), texture2D( texture, texel + vec2(-1,-2)*pixel), texture2D( texture, texel + vec2(0,-2)*pixel), texture2D( texture, texel + vec2(1,-2)*pixel), texture2D( texture, texel + vec2(2,-2)*pixel), texture2D( texture, texel + vec2(3,-2)*pixel)); vec4 t1 = filter1D_radius3(kernel, index, f.x, texture2D( texture, texel + vec2(-2,-1)*pixel), texture2D( texture, texel + vec2(-1,-1)*pixel), texture2D( texture, texel + vec2(0,-1)*pixel), texture2D( texture, texel + vec2(1,-1)*pixel), texture2D( texture, texel + vec2(2,-1)*pixel), texture2D( texture, texel + vec2(3,-1)*pixel)); vec4 t2 = filter1D_radius3(kernel, index, f.x, texture2D( texture, texel + vec2(-2,0)*pixel), texture2D( texture, texel + vec2(-1,0)*pixel), texture2D( texture, texel + vec2(0,0)*pixel), texture2D( texture, texel + vec2(1,0)*pixel), texture2D( texture, texel + vec2(2,0)*pixel), texture2D( texture, texel + vec2(3,0)*pixel)); vec4 t3 = filter1D_radius3(kernel, index, f.x, texture2D( texture, texel + vec2(-2,1)*pixel), texture2D( texture, texel + vec2(-1,1)*pixel), texture2D( texture, texel + vec2(0,1)*pixel), texture2D( texture, texel + vec2(1,1)*pixel), texture2D( texture, texel + vec2(2,1)*pixel), texture2D( texture, texel + vec2(3,1)*pixel)); vec4 t4 = filter1D_radius3(kernel, index, f.x, texture2D( texture, texel + vec2(-2,2)*pixel), texture2D( texture, texel + vec2(-1,2)*pixel), texture2D( texture, texel + vec2(0,2)*pixel), texture2D( texture, texel + vec2(1,2)*pixel), texture2D( texture, texel + vec2(2,2)*pixel), texture2D( texture, texel + vec2(3,2)*pixel)); vec4 t5 = filter1D_radius3(kernel, index, f.x, texture2D( texture, texel + vec2(-2,3)*pixel), texture2D( texture, texel + vec2(-1,3)*pixel), texture2D( texture, texel + vec2(0,3)*pixel), texture2D( texture, texel + vec2(1,3)*pixel), texture2D( texture, texel + vec2(2,3)*pixel), texture2D( texture, texel + vec2(3,3)*pixel)); return filter1D_radius3(kernel, index, f.y, t0, t1, t2, t3, t4, t5); } vec4 filter1D_radius4( sampler2D kernel, float index, float x, vec4 c0, vec4 c1, vec4 c2, vec4 c3, vec4 c4, vec4 c5, vec4 c6, vec4 c7 ) { float w, w_sum = 0.0; vec4 r = vec4(0.0,0.0,0.0,0.0); w = texture2D(kernel, vec2(0.750000+(x/4.0),index) ).r; w = w*kernel_scale + kernel_bias; r += c0 * w; w = texture2D(kernel, vec2(0.250000-(x/4.0),index) ).r; w = w*kernel_scale + kernel_bias; r += c4 * w; w = texture2D(kernel, vec2(0.500000+(x/4.0),index) ).r; w = w*kernel_scale + kernel_bias; r += c1 * w; w = texture2D(kernel, vec2(0.500000-(x/4.0),index) ).r; w = w*kernel_scale + kernel_bias; r += c5 * w; w = texture2D(kernel, vec2(0.250000+(x/4.0),index) ).r; w = w*kernel_scale + kernel_bias; r += c2 * w; w = texture2D(kernel, vec2(0.750000-(x/4.0),index) ).r; w = w*kernel_scale + kernel_bias; r += c6 * w; w = texture2D(kernel, vec2(0.000000+(x/4.0),index) ).r; w = w*kernel_scale + kernel_bias; r += c3 * w; w = texture2D(kernel, vec2(1.000000-(x/4.0),index) ).r; w = w*kernel_scale + kernel_bias; r += c7 * w; return r; } vec4 filter2D_radius4(sampler2D texture, sampler2D kernel, float index, vec2 uv, vec2 pixel ) { vec2 texel = uv/pixel - vec2(0.0,0.0) ; vec2 f = fract(texel); texel = (texel-fract(texel)+vec2(0.001,0.001))*pixel; vec4 t0 = filter1D_radius4(kernel, index, f.x, texture2D( texture, texel + vec2(-3,-3)*pixel), texture2D( texture, texel + vec2(-2,-3)*pixel), texture2D( texture, texel + vec2(-1,-3)*pixel), texture2D( texture, texel + vec2(0,-3)*pixel), texture2D( texture, texel + vec2(1,-3)*pixel), texture2D( texture, texel + vec2(2,-3)*pixel), texture2D( texture, texel + vec2(3,-3)*pixel), texture2D( texture, texel + vec2(4,-3)*pixel)); vec4 t1 = filter1D_radius4(kernel, index, f.x, texture2D( texture, texel + vec2(-3,-2)*pixel), texture2D( texture, texel + vec2(-2,-2)*pixel), texture2D( texture, texel + vec2(-1,-2)*pixel), texture2D( texture, texel + vec2(0,-2)*pixel), texture2D( texture, texel + vec2(1,-2)*pixel), texture2D( texture, texel + vec2(2,-2)*pixel), texture2D( texture, texel + vec2(3,-2)*pixel), texture2D( texture, texel + vec2(4,-2)*pixel)); vec4 t2 = filter1D_radius4(kernel, index, f.x, texture2D( texture, texel + vec2(-3,-1)*pixel), texture2D( texture, texel + vec2(-2,-1)*pixel), texture2D( texture, texel + vec2(-1,-1)*pixel), texture2D( texture, texel + vec2(0,-1)*pixel), texture2D( texture, texel + vec2(1,-1)*pixel), texture2D( texture, texel + vec2(2,-1)*pixel), texture2D( texture, texel + vec2(3,-1)*pixel), texture2D( texture, texel + vec2(4,-1)*pixel)); vec4 t3 = filter1D_radius4(kernel, index, f.x, texture2D( texture, texel + vec2(-3,0)*pixel), texture2D( texture, texel + vec2(-2,0)*pixel), texture2D( texture, texel + vec2(-1,0)*pixel), texture2D( texture, texel + vec2(0,0)*pixel), texture2D( texture, texel + vec2(1,0)*pixel), texture2D( texture, texel + vec2(2,0)*pixel), texture2D( texture, texel + vec2(3,0)*pixel), texture2D( texture, texel + vec2(4,0)*pixel)); vec4 t4 = filter1D_radius4(kernel, index, f.x, texture2D( texture, texel + vec2(-3,1)*pixel), texture2D( texture, texel + vec2(-2,1)*pixel), texture2D( texture, texel + vec2(-1,1)*pixel), texture2D( texture, texel + vec2(0,1)*pixel), texture2D( texture, texel + vec2(1,1)*pixel), texture2D( texture, texel + vec2(2,1)*pixel), texture2D( texture, texel + vec2(3,1)*pixel), texture2D( texture, texel + vec2(4,1)*pixel)); vec4 t5 = filter1D_radius4(kernel, index, f.x, texture2D( texture, texel + vec2(-3,2)*pixel), texture2D( texture, texel + vec2(-2,2)*pixel), texture2D( texture, texel + vec2(-1,2)*pixel), texture2D( texture, texel + vec2(0,2)*pixel), texture2D( texture, texel + vec2(1,2)*pixel), texture2D( texture, texel + vec2(2,2)*pixel), texture2D( texture, texel + vec2(3,2)*pixel), texture2D( texture, texel + vec2(4,2)*pixel)); vec4 t6 = filter1D_radius4(kernel, index, f.x, texture2D( texture, texel + vec2(-3,3)*pixel), texture2D( texture, texel + vec2(-2,3)*pixel), texture2D( texture, texel + vec2(-1,3)*pixel), texture2D( texture, texel + vec2(0,3)*pixel), texture2D( texture, texel + vec2(1,3)*pixel), texture2D( texture, texel + vec2(2,3)*pixel), texture2D( texture, texel + vec2(3,3)*pixel), texture2D( texture, texel + vec2(4,3)*pixel)); vec4 t7 = filter1D_radius4(kernel, index, f.x, texture2D( texture, texel + vec2(-3,4)*pixel), texture2D( texture, texel + vec2(-2,4)*pixel), texture2D( texture, texel + vec2(-1,4)*pixel), texture2D( texture, texel + vec2(0,4)*pixel), texture2D( texture, texel + vec2(1,4)*pixel), texture2D( texture, texel + vec2(2,4)*pixel), texture2D( texture, texel + vec2(3,4)*pixel), texture2D( texture, texel + vec2(4,4)*pixel)); return filter1D_radius4(kernel, index, f.y, t0, t1, t2, t3, t4, t5, t6, t7); } vec4 Nearest(sampler2D texture, vec2 shape, vec2 uv) { return texture2D(texture,uv); } vec4 Bilinear(sampler2D texture, vec2 shape, vec2 uv) { return filter2D_radius1(texture, u_kernel, 0.031250, uv, 1.0/shape); } vec4 Hanning(sampler2D texture, vec2 shape, vec2 uv) { return filter2D_radius1(texture, u_kernel, 0.093750, uv, 1.0/shape); } vec4 Hamming(sampler2D texture, vec2 shape, vec2 uv) { return filter2D_radius1(texture, u_kernel, 0.156250, uv, 1.0/shape); } vec4 Hermite(sampler2D texture, vec2 shape, vec2 uv) { return filter2D_radius1(texture, u_kernel, 0.218750, uv, 1.0/shape); } vec4 Kaiser(sampler2D texture, vec2 shape, vec2 uv) { return filter2D_radius1(texture, u_kernel, 0.281250, uv, 1.0/shape); } vec4 Quadric(sampler2D texture, vec2 shape, vec2 uv) { return filter2D_radius2(texture, u_kernel, 0.343750, uv, 1.0/shape); } vec4 Bicubic(sampler2D texture, vec2 shape, vec2 uv) { return filter2D_radius2(texture, u_kernel, 0.406250, uv, 1.0/shape); } vec4 CatRom(sampler2D texture, vec2 shape, vec2 uv) { return filter2D_radius2(texture, u_kernel, 0.468750, uv, 1.0/shape); } vec4 Mitchell(sampler2D texture, vec2 shape, vec2 uv) { return filter2D_radius2(texture, u_kernel, 0.531250, uv, 1.0/shape); } vec4 Spline16(sampler2D texture, vec2 shape, vec2 uv) { return filter2D_radius2(texture, u_kernel, 0.593750, uv, 1.0/shape); } vec4 Spline36(sampler2D texture, vec2 shape, vec2 uv) { return filter2D_radius3(texture, u_kernel, 0.656250, uv, 1.0/shape); } vec4 Gaussian(sampler2D texture, vec2 shape, vec2 uv) { return filter2D_radius2(texture, u_kernel, 0.718750, uv, 1.0/shape); } vec4 Bessel(sampler2D texture, vec2 shape, vec2 uv) { return filter2D_radius4(texture, u_kernel, 0.781250, uv, 1.0/shape); } vec4 Sinc(sampler2D texture, vec2 shape, vec2 uv) { return filter2D_radius4(texture, u_kernel, 0.843750, uv, 1.0/shape); } vec4 Lanczos(sampler2D texture, vec2 shape, vec2 uv) { return filter2D_radius4(texture, u_kernel, 0.906250, uv, 1.0/shape); } vec4 Blackman(sampler2D texture, vec2 shape, vec2 uv) { return filter2D_radius4(texture, u_kernel, 0.968750, uv, 1.0/shape); } ����������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/misc/viewport-NDC.glsl�������������������������������������������������������0000664�0001750�0001750�00000001172�12510536123�022574� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- vec2 NDC_to_viewport(vec4 position, vec2 viewport) { vec2 p = position.xy/position.w; return (p+1.0)/2.0 * viewport; } vec4 viewport_to_NDC(vec2 position, vec2 viewport) { return vec4(2.0*(position/viewport) - 1.0, 0.0, 1.0); } vec4 viewport_to_NDC(vec3 position, vec2 viewport) { return vec4(2.0*(position.xy/viewport) - 1.0, position.z, 1.0); } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/misc/regular-grid.frag�������������������������������������������������������0000664�0001750�0001750�00000015254�12510536123�022663� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- // Constants // ------------------------------------ const float M_PI = 3.14159265358979323846; const float M_SQRT2 = 1.41421356237309504880; // Uniforms // ------------------------------------ // Line antialias area (usually 1 pixel) uniform float u_antialias; // Cartesian limits uniform vec4 u_limits1; // Projected limits uniform vec4 u_limits2; // Major grid steps uniform vec2 u_major_grid_step; // Minor grid steps uniform vec2 u_minor_grid_step; // Major grid line width (1.50 pixel) uniform float u_major_grid_width; // Minor grid line width (0.75 pixel) uniform float u_minor_grid_width; // Major grid line color uniform vec4 u_major_grid_color; // Minor grid line color uniform vec4 u_minor_grid_color; // Varyings // ------------------------------------ // Texture coordinates (from (-0.5,-0.5) to (+0.5,+0.5) varying vec2 v_texcoord; // Quad size (pixels) varying vec2 v_size; // Functions // ------------------------------------ // Hammer forward transform // ------------------------------------ vec2 transform_forward(vec2 P) { const float B = 2.0; float longitude = P.x; float latitude = P.y; float cos_lat = cos(latitude); float sin_lat = sin(latitude); float cos_lon = cos(longitude/B); float sin_lon = sin(longitude/B); float d = sqrt(1.0 + cos_lat * cos_lon); float x = (B * M_SQRT2 * cos_lat * sin_lon) / d; float y = (M_SQRT2 * sin_lat) / d; return vec2(x,y); } // Hammer inverse transform // ------------------------------------ vec2 transform_inverse(vec2 P) { const float B = 2.0; float x = P.x; float y = P.y; float z = 1.0 - (x*x/16.0) - (y*y/4.0); if (z < 0.0) discard; z = sqrt(z); float lon = 2.0*atan( (z*x),(2.0*(2.0*z*z - 1.0))); float lat = asin(z*y); return vec2(lon,lat); } /* // Forward transform (polar) // ------------------------------------ vec2 transform_forward(vec2 P) { float x = P.x * cos(P.y); float y = P.x * sin(P.y); return vec2(x,y); } // Inverse transform (polar) // ------------------------------------ vec2 transform_inverse(vec2 P) { float rho = length(P); float theta = atan(P.y,P.x); if( theta < 0.0 ) theta = 2.0*M_PI+theta; return vec2(rho,theta); } */ // [-0.5,-0.5]x[0.5,0.5] -> [xmin,xmax]x[ymin,ymax] // ------------------------------------------------ vec2 scale_forward(vec2 P, vec4 limits) { // limits = xmin,xmax,ymin,ymax P += vec2(.5,.5); P *= vec2(limits[1] - limits[0], limits[3]-limits[2]); P += vec2(limits[0], limits[2]); return P; } // [xmin,xmax]x[ymin,ymax] -> [-0.5,-0.5]x[0.5,0.5] // ------------------------------------------------ vec2 scale_inverse(vec2 P, vec4 limits) { // limits = xmin,xmax,ymin,ymax P -= vec2(limits[0], limits[2]); P /= vec2(limits[1]-limits[0], limits[3]-limits[2]); return P - vec2(.5,.5); } // Antialias stroke alpha coeff float stroke_alpha(float distance, float linewidth, float antialias) { float t = linewidth/2.0 - antialias; float signed_distance = distance; float border_distance = abs(signed_distance) - t; float alpha = border_distance/antialias; alpha = exp(-alpha*alpha); if( border_distance > (linewidth/2.0 + antialias) ) return 0.0; else if( border_distance < 0.0 ) return 1.0; else return alpha; } // Compute the nearest tick from a (normalized) t value float get_tick(float t, float vmin, float vmax, float step) { float first_tick = floor((vmin + step/2.0)/step) * step; float last_tick = floor((vmax - step/2.0)/step) * step; float tick = vmin + t*(vmax-vmin); if (tick < (vmin + (first_tick-vmin)/2.0)) return vmin; if (tick > (last_tick + (vmax-last_tick)/2.0)) return vmax; tick += step/2.0; tick = floor(tick/step)*step; return min(max(vmin,tick),vmax); } void main() { vec2 NP1 = v_texcoord; vec2 P1 = scale_forward(NP1, u_limits1); vec2 P2 = transform_inverse(P1); // Test if we are within limits but we do not discard yet because we want // to draw border. Discarding would mean half of the exterior not drawn. bvec2 outside = bvec2(false); if( P2.x < u_limits2[0] ) outside.x = true; if( P2.x > u_limits2[1] ) outside.x = true; if( P2.y < u_limits2[2] ) outside.y = true; if( P2.y > u_limits2[3] ) outside.y = true; vec2 NP2 = scale_inverse(P2,u_limits2); vec2 P; float tick; tick = get_tick(NP2.x+.5, u_limits2[0], u_limits2[1], u_major_grid_step[0]); P = transform_forward(vec2(tick,P2.y)); P = scale_inverse(P, u_limits1); float Mx = length(v_size * (NP1 - P)); tick = get_tick(NP2.x+.5, u_limits2[0], u_limits2[1], u_minor_grid_step[0]); P = transform_forward(vec2(tick,P2.y)); P = scale_inverse(P, u_limits1); float mx = length(v_size * (NP1 - P)); tick = get_tick(NP2.y+.5, u_limits2[2], u_limits2[3], u_major_grid_step[1]); P = transform_forward(vec2(P2.x,tick)); P = scale_inverse(P, u_limits1); float My = length(v_size * (NP1 - P)); tick = get_tick(NP2.y+.5, u_limits2[2], u_limits2[3], u_minor_grid_step[1]); P = transform_forward(vec2(P2.x,tick)); P = scale_inverse(P, u_limits1); float my = length(v_size * (NP1 - P)); float M = min(Mx,My); float m = min(mx,my); // Here we take care of "finishing" the border lines if( outside.x && outside.y ) { if (Mx > 0.5*(u_major_grid_width + u_antialias)) { discard; } else if (My > 0.5*(u_major_grid_width + u_antialias)) { discard; } else { M = max(Mx,My); } } else if( outside.x ) { if (Mx > 0.5*(u_major_grid_width + u_antialias)) { discard; } else { M = m = Mx; } } else if( outside.y ) { if (My > 0.5*(u_major_grid_width + u_antialias)) { discard; } else { M = m = My; } } // Mix major/minor colors to get dominant color vec4 color = u_major_grid_color; float alpha1 = stroke_alpha( M, u_major_grid_width, u_antialias); float alpha2 = stroke_alpha( m, u_minor_grid_width, u_antialias); float alpha = alpha1; if( alpha2 > alpha1*1.5 ) { alpha = alpha2; color = u_minor_grid_color; } // For the same price you could project a texture // vec4 texcolor = texture2D(u_texture, vec2(NP2.x, 1.0-NP2.y)); // gl_FragColor = mix(texcolor, color, alpha); gl_FragColor = vec4(color.rgb, color.a*alpha); } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/math/������������������������������������������������������������������������0000775�0001750�0001750�00000000000�12527674621�017443� 5����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/math/circle-through-2-points.glsl��������������������������������������������0000664�0001750�0001750�00000001661�12510536123�024704� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- /* --------------------------------------------------------- Computes the center of the 2 circles with given radius passing through p1 & p2 Parameters: ----------- p0, p1: Points ascribed in the circles radius: Radius of the circle Return: ------- Centers of the two circles with specified radius --------------------------------------------------------- */ vec4 circle_from_2_points(vec2 p1, vec2 p2, float radius) { float q = length(p2-p1); vec2 m = (p1+p2)/2.0; vec2 d = vec2( sqrt(radius*radius - (q*q/4.0)) * (p1.y-p2.y)/q, sqrt(radius*radius - (q*q/4.0)) * (p2.x-p1.x)/q); return vec4(m+d, m-d); } �������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/math/signed-segment-distance.glsl��������������������������������������������0000664�0001750�0001750�00000001634�12510536123�025015� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- /* --------------------------------------------------------- Computes the signed distance from a line segment Parameters: ----------- p0, p1: Points describing the line segment p: Point to measure distance from Return: ------- Signed distance --------------------------------------------------------- */ float segment_distance(vec2 p, vec2 p1, vec2 p2) { vec2 center = (p1 + p2) * 0.5; float len = length(p2 - p1); vec2 dir = (p2 - p1) / len; vec2 rel_p = p - center; float dist1 = abs(dot(rel_p, vec2(dir.y, -dir.x))); float dist2 = abs(dot(rel_p, dir)) - 0.5*len; return max(dist1, dist2); } ����������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/math/point-to-line-distance.glsl���������������������������������������������0000664�0001750�0001750�00000001625�12510536123�024602� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- /* --------------------------------------------------------- Compute distance from a point to a line (2d) Parameters: ----------- p0, p1: Points describing the line p: Point to computed distance to Return: ------- Distance of p to (p0,p1) --------------------------------------------------------- */ float point_to_line_distance(vec2 p0, vec2 p1, vec2 p) { // Projection p' of p such that p' = p0 + u*(p1-p0) vec2 v = p1 - p0; float l2 = v.x*v.x + v.y*v.y; float u = ((p.x-p0.x)*v.x + (p.y-p0.y)*v.y) / l2; // h is the projection of p on (p0,p1) vec2 h = p0 + u*v; return length(p-h); } �����������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/math/point-to-line-projection.glsl�������������������������������������������0000664�0001750�0001750�00000001607�12510536123�025164� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- /* --------------------------------------------------------- Project a point p onto a line (p0,p1) and return linear position u such that p' = p0 + u*(p1-p0) Parameters: ----------- p0, p1: Points describing the line p: Point to be projected Return: ------- Linear position of p onto (p0,p1) --------------------------------------------------------- */ float point_to_line_projection(vec2 p0, vec2 p1, vec2 p) { // Projection p' of p such that p' = p0 + u*(p1-p0) // Then u *= lenght(p1-p0) vec2 v = p1 - p0; float l = length(v); return ((p.x-p0.x)*v.x + (p.y-p0.y)*v.y) / l; } �������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/math/__init__.py�������������������������������������������������������������0000664�0001750�0001750�00000000000�12510536123�021524� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/math/functions.glsl����������������������������������������������������������0000664�0001750�0001750�00000001246�12510536123�022323� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- /* --------------------------------------------------------- Hyperbolic cosine --------------------------------------------------------- */ float cosh(float x) { return 0.5 * (exp(x)+exp(-x)); } /* --------------------------------------------------------- Hyperbolic sine --------------------------------------------------------- */ float sinh(float x) { return 0.5 * (exp(x)-exp(-x)); } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/math/signed-line-distance.glsl�����������������������������������������������0000664�0001750�0001750�00000001454�12510536123�024302� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- /* --------------------------------------------------------- Computes the signed distance from a line Parameters: ----------- p0, p1: Points describing the line p: Point to measure distance from Return: ------- Signed distance --------------------------------------------------------- */ float line_distance(vec2 p, vec2 p1, vec2 p2) { vec2 center = (p1 + p2) * 0.5; float len = length(p2 - p1); vec2 dir = (p2 - p1) / len; vec2 rel_p = p - center; return dot(rel_p, vec2(dir.y, -dir.x)); } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/math/double.glsl�������������������������������������������������������������0000664�0001750�0001750�00000005716�12510536123�021573� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- /* This shader program emulates double-precision variables using a vec2 instead of single-precision floats. Any function starting with double_* operates on these variables. See http://www.thasler.org/blog/?p=93. NOTE: Some NVIDIA cards optimize the double-precision code away. Results are therefore hardware dependent. */ #define double vec2 /* ------------------------------------------------------------------------- Create an emulated double by storing first part of float in first half of vec2 ------------------------------------------------------------------------- */ vec2 double_set(float value) { double result; result.x = value; result.y = 0.0; return result; } /* ------------------------------------------------------------------------- Add two emulated doubles. Complexity comes from carry-over. ------------------------------------------------------------------------- */ vec2 double_add(double value_a, double value_b) { double result; float t1, t2, e; t1 = value_a.x + value_b.x; e = t1 - value_a.x; t2 = ((value_b.x - e) + (value_a.x - (t1 - e))) + value_a.y + value_b.y; result.x = t1 + t2; result.y = t2 - (result.x - t1); return dsc; } /* ------------------------------------------------------------------------- Multiply two emulated doubles. ------------------------------------------------------------------------- */ vec2 double_mul(double value_a, double value_b) { double result; float c11, c21, c2, e, t1, t2; float a1, a2, b1, b2, cona, conb, split = 8193.; cona = value_a.x * split; conb = value_b.x * split; a1 = cona - (cona - value_a.x); b1 = conb - (conb - value_b.x); a2 = value_a.x - a1; b2 = value_b.x - b1; c11 = value_a.x * value_b.x; c21 = a2 * b2 + (a2 * b1 + (a1 * b2 + (a1 * b1 - c11))); c2 = value_a.x * value_b.y + value_a.y * value_b.x; t1 = c11 + c2; e = t1 - c11; t2 = value_a.y * value_b.y + ((c2 - e) + (c11 - (t1 - e))) + c21; result.x = t1 + t2; result.y = t2 - (result.x - t1); return result; } /* ------------------------------------------------------------------------- Compare two emulated doubles. Return -1 if a < b 0 if a == b 1 if a > b ------------------------------------------------------------------------- */ float double_compare(double value_a, double value_b) { if (value_a.x < value_b.x) { return -1.; } else if (value_a.x == value_b.x) { if (value_a.y < value_b.y) { return -1.; } else if (value_a.y == value_b.y) { return 0.; } else { return 1.; } } else { return 1.; } } ��������������������������������������������������vispy-0.4.0/vispy/glsl/math/constants.glsl����������������������������������������������������������0000664�0001750�0001750�00000003221�12510536123�022322� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- #ifndef _CONSTANTS_ #define _CONSTANTS_ // The base of natural logarithms (e) const float M_E = 2.71828182845904523536028747135266250; // The logarithm to base 2 of M_E (log2(e)) const float M_LOG2E = 1.44269504088896340735992468100189214; // The logarithm to base 10 of M_E (log10(e)) const float M_LOG10E = 0.434294481903251827651128918916605082; // The natural logarithm of 2 (loge(2)) const float M_LN2 = 0.693147180559945309417232121458176568; // The natural logarithm of 10 (loge(10)) const float M_LN10 = 2.30258509299404568401799145468436421; // Pi, the ratio of a circle's circumference to its diameter. const float M_PI = 3.14159265358979323846264338327950288; // Pi divided by two (pi/2) const float M_PI_2 = 1.57079632679489661923132169163975144; // Pi divided by four (pi/4) const float M_PI_4 = 0.785398163397448309615660845819875721; // The reciprocal of pi (1/pi) const float M_1_PI = 0.318309886183790671537767526745028724; // Two times the reciprocal of pi (2/pi) const float M_2_PI = 0.636619772367581343075535053490057448; // Two times the reciprocal of the square root of pi (2/sqrt(pi)) const float M_2_SQRTPI = 1.12837916709551257389615890312154517; // The square root of two (sqrt(2)) const float M_SQRT2 = 1.41421356237309504880168872420969808; // The reciprocal of the square root of two (1/sqrt(2)) const float M_SQRT1_2 = 0.707106781186547524400844362104849039; #endif �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/arrows/����������������������������������������������������������������������0000775�0001750�0001750�00000000000�12527674621�020027� 5����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/arrows/util.glsl�������������������������������������������������������������0000664�0001750�0001750�00000006256�12510536123�021662� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- #include "math/signed-line-distance.glsl" #include "math/point-to-line-distance.glsl" #include "math/signed-segment-distance.glsl" #include "math/circle-through-2-points.glsl" #include "math/point-to-line-projection.glsl" /* --------------------------------------------------------- Computes the signed distance to a triangle arrow Parameters: ----------- texcoord : Point to compute distance to body : Total length of the arrow (pixels, body+head) head : Length of the head (pixels) height : Height of the head (pixel) linewidth: Stroke line width (in pixels) antialias: Stroke antialiased area (in pixels) Return: ------- Signed distance to the arrow --------------------------------------------------------- */ float arrow_triangle(vec2 texcoord, float body, float head, float height, float linewidth, float antialias) { float w = linewidth/2.0 + antialias; vec2 start = -vec2(body/2.0, 0.0); vec2 end = +vec2(body/2.0, 0.0); // Head : 3 lines float d1 = line_distance(texcoord, end, end - head*vec2(+1.0,-height)); float d2 = line_distance(texcoord, end - head*vec2(+1.0,+height), end); float d3 = texcoord.x - end.x + head; // Body : 1 segment float d4 = segment_distance(texcoord, start, end - vec2(linewidth,0.0)); float d = min(max(max(d1, d2), -d3), d4); return d; } /* --------------------------------------------------------- Computes the signed distance to an angle arrow Parameters: ----------- texcoord : Point to compute distance to body : Total length of the arrow (pixels, body+head) head : Length of the head (pixels) height : Height of the head (pixel) linewidth: Stroke line width (in pixels) antialias: Stroke antialiased area (in pixels) Return: ------- Signed distance to the arrow --------------------------------------------------------- */ float arrow_angle(vec2 texcoord, float body, float head, float height, float linewidth, float antialias) { float d; float w = linewidth/2.0 + antialias; vec2 start = -vec2(body/2.0, 0.0); vec2 end = +vec2(body/2.0, 0.0); // Arrow tip (beyond segment end) if( texcoord.x > body/2.0) { // Head : 2 segments float d1 = line_distance(texcoord, end, end - head*vec2(+1.0,-height)); float d2 = line_distance(texcoord, end - head*vec2(+1.0,+height), end); // Body : 1 segment float d3 = end.x - texcoord.x; d = max(max(d1,d2), d3); } else { // Head : 2 segments float d1 = segment_distance(texcoord, end - head*vec2(+1.0,-height), end); float d2 = segment_distance(texcoord, end - head*vec2(+1.0,+height), end); // Body : 1 segment float d3 = segment_distance(texcoord, start, end - vec2(linewidth,0.0)); d = min(min(d1,d2), d3); } return d; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/arrows/angle-30.glsl���������������������������������������������������������0000664�0001750�0001750�00000000777�12510536123�022215� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- #include "arrows/util.glsl" float arrow_angle_30(vec2 texcoord, float body, float head, float linewidth, float antialias) { return arrow_angle(texcoord, body, head, 0.25, linewidth, antialias); } �vispy-0.4.0/vispy/glsl/arrows/triangle-90.glsl������������������������������������������������������0000664�0001750�0001750�00000001012�12510536123�022721� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- #include "arrows/util.glsl" float arrow_triangle_90(vec2 texcoord, float body, float head, float linewidth, float antialias) { return arrow_triangle(texcoord, body, head, 1.0, linewidth, antialias); } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/arrows/triangle-60.glsl������������������������������������������������������0000664�0001750�0001750�00000001012�12510536123�022716� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- #include "arrows/util.glsl" float arrow_triangle_60(vec2 texcoord, float body, float head, float linewidth, float antialias) { return arrow_triangle(texcoord, body, head, 0.5, linewidth, antialias); } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/arrows/arrow.vert������������������������������������������������������������0000664�0001750�0001750�00000002570�12510536123�022051� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- // Hooks: // : vec4 function(position, ...) // // ---------------------------------------------------------------------------- #include "math/constants.glsl" // Uniforms // ------------------------------------ uniform float antialias; // Attributes // ------------------------------------ attribute vec2 position; attribute float size; attribute float head; attribute vec4 fg_color; attribute vec4 bg_color; attribute float orientation; attribute float linewidth; // Varyings // ------------------------------------ varying float v_size; varying float v_head; varying vec4 v_fg_color; varying vec4 v_bg_color; varying vec2 v_orientation; varying float v_antialias; varying float v_linewidth; // Main (hooked) // ------------------------------------ void main (void) { v_size = size; v_head = head; v_linewidth = linewidth; v_antialias = antialias; v_fg_color = fg_color; v_bg_color = bg_color; v_orientation = vec2(cos(orientation), sin(orientation)); gl_Position = ; gl_PointSize = M_SQRT2 * size + 2.0 * (linewidth + 1.5*antialias); } ����������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/arrows/arrow.frag������������������������������������������������������������0000664�0001750�0001750�00000002633�12510536123�022010� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- // Hooks: // : "stroke", "filled" or "outline" // : "steath", "curved", // "angle_30", "angle_60", "angle_90", // "triangle_30", "triangle_60", "triangle_90", // ---------------------------------------------------------------------------- #include "math/constants.glsl" #include "arrows/arrows.glsl" #include "antialias/antialias.glsl" // Varyings // ------------------------------------ varying float v_antialias; varying float v_linewidth; varying float v_size; varying float v_head; varying float v_texcoord; varying vec4 v_fg_color; varying vec4 v_bg_color; varying vec2 v_orientation; // Main (hooked) // ------------------------------------ void main() { vec2 P = gl_PointCoord.xy - vec2(0.5,0.5); P = vec2(v_orientation.x*P.x - v_orientation.y*P.y, v_orientation.y*P.x + v_orientation.x*P.y) * v_size; float point_size = M_SQRT2*v_size + 2.0 * (v_linewidth + 1.5*v_antialias); float body = v_size/M_SQRT2; float distance = arrow_(P, body, v_head*body, v_linewidth, v_antialias); gl_FragColor = (distance, v_linewidth, v_antialias, v_fg_color, v_bg_color); } �����������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/arrows/angle-60.glsl���������������������������������������������������������0000664�0001750�0001750�00000000776�12510536123�022217� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- #include "arrows/util.glsl" float arrow_angle_60(vec2 texcoord, float body, float head, float linewidth, float antialias) { return arrow_angle(texcoord, body, head, 0.5, linewidth, antialias); } ��vispy-0.4.0/vispy/glsl/arrows/common.glsl�����������������������������������������������������������0000664�0001750�0001750�00000014375�12510536123�022176� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ------------------------------------------------------------------------- * Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. * Distributed under the (new) BSD License. * ------------------------------------------------------------------------- */ // Computes the signed distance from a line float line_distance(vec2 p, vec2 p1, vec2 p2) { vec2 center = (p1 + p2) * 0.5; float len = length(p2 - p1); vec2 dir = (p2 - p1) / len; vec2 rel_p = p - center; return dot(rel_p, vec2(dir.y, -dir.x)); } // Computes the signed distance from a line segment float segment_distance(vec2 p, vec2 p1, vec2 p2) { vec2 center = (p1 + p2) * 0.5; float len = length(p2 - p1); vec2 dir = (p2 - p1) / len; vec2 rel_p = p - center; float dist1 = abs(dot(rel_p, vec2(dir.y, -dir.x))); float dist2 = abs(dot(rel_p, dir)) - 0.5*len; return max(dist1, dist2); } // Computes the center with given radius passing through p1 & p2 vec4 circle_from_2_points(vec2 p1, vec2 p2, float radius) { float q = length(p2-p1); vec2 m = (p1+p2)/2.0; vec2 d = vec2( sqrt(radius*radius - (q*q/4.0)) * (p1.y-p2.y)/q, sqrt(radius*radius - (q*q/4.0)) * (p2.x-p1.x)/q); return vec4(m+d, m-d); } float arrow_curved(vec2 texcoord, float body, float head, float linewidth, float antialias) { float w = linewidth/2.0 + antialias; vec2 start = -vec2(body/2.0, 0.0); vec2 end = +vec2(body/2.0, 0.0); float height = 0.5; vec2 p1 = end - head*vec2(+1.0,+height); vec2 p2 = end - head*vec2(+1.0,-height); vec2 p3 = end; // Head : 3 circles vec2 c1 = circle_from_2_points(p1, p3, 1.25*body).zw; float d1 = length(texcoord - c1) - 1.25*body; vec2 c2 = circle_from_2_points(p2, p3, 1.25*body).xy; float d2 = length(texcoord - c2) - 1.25*body; vec2 c3 = circle_from_2_points(p1, p2, max(body-head, 1.0*body)).xy; float d3 = length(texcoord - c3) - max(body-head, 1.0*body); // Body : 1 segment float d4 = segment_distance(texcoord, start, end - vec2(linewidth,0.0)); // Outside (because of circles) if( texcoord.y > +(2.0*head + antialias) ) return 1000.0; if( texcoord.y < -(2.0*head + antialias) ) return 1000.0; if( texcoord.x < -(body/2.0 + antialias) ) return 1000.0; if( texcoord.x > c1.x ) //(body + antialias) ) return 1000.0; return min( d4, -min(d3,min(d1,d2))); } float arrow_triangle(vec2 texcoord, float body, float head, float height, float linewidth, float antialias) { float w = linewidth/2.0 + antialias; vec2 start = -vec2(body/2.0, 0.0); vec2 end = +vec2(body/2.0, 0.0); // Head : 3 lines float d1 = line_distance(texcoord, end, end - head*vec2(+1.0,-height)); float d2 = line_distance(texcoord, end - head*vec2(+1.0,+height), end); float d3 = texcoord.x - end.x + head; // Body : 1 segment float d4 = segment_distance(texcoord, start, end - vec2(linewidth,0.0)); float d = min(max(max(d1, d2), -d3), d4); return d; } float arrow_triangle_90(vec2 texcoord, float body, float head, float linewidth, float antialias) { return arrow_triangle(texcoord, body, head, 1.0, linewidth, antialias); } float arrow_triangle_60(vec2 texcoord, float body, float head, float linewidth, float antialias) { return arrow_triangle(texcoord, body, head, 0.5, linewidth, antialias); } float arrow_triangle_30(vec2 texcoord, float body, float head, float linewidth, float antialias) { return arrow_triangle(texcoord, body, head, 0.25, linewidth, antialias); } float arrow_angle(vec2 texcoord, float body, float head, float height, float linewidth, float antialias) { float d; float w = linewidth/2.0 + antialias; vec2 start = -vec2(body/2.0, 0.0); vec2 end = +vec2(body/2.0, 0.0); // Arrow tip (beyond segment end) if( texcoord.x > body/2.0) { // Head : 2 segments float d1 = line_distance(texcoord, end, end - head*vec2(+1.0,-height)); float d2 = line_distance(texcoord, end - head*vec2(+1.0,+height), end); // Body : 1 segment float d3 = end.x - texcoord.x; d = max(max(d1,d2), d3); } else { // Head : 2 segments float d1 = segment_distance(texcoord, end - head*vec2(+1.0,-height), end); float d2 = segment_distance(texcoord, end - head*vec2(+1.0,+height), end); // Body : 1 segment float d3 = segment_distance(texcoord, start, end - vec2(linewidth,0.0)); d = min(min(d1,d2), d3); } return d; } float arrow_angle_90(vec2 texcoord, float body, float head, float linewidth, float antialias) { return arrow_angle(texcoord, body, head, 1.0, linewidth, antialias); } float arrow_angle_60(vec2 texcoord, float body, float head, float linewidth, float antialias) { return arrow_angle(texcoord, body, head, 0.5, linewidth, antialias); } float arrow_angle_30(vec2 texcoord, float body, float head, float linewidth, float antialias) { return arrow_angle(texcoord, body, head, 0.25, linewidth, antialias); } float arrow_stealth(vec2 texcoord, float body, float head, float linewidth, float antialias) { float w = linewidth/2.0 + antialias; vec2 start = -vec2(body/2.0, 0.0); vec2 end = +vec2(body/2.0, 0.0); float height = 0.5; // Head : 4 lines float d1 = line_distance(texcoord, end-head*vec2(+1.0,-height), end); float d2 = line_distance(texcoord, end-head*vec2(+1.0,-height), end-vec2(3.0*head/4.0,0.0)); float d3 = line_distance(texcoord, end-head*vec2(+1.0,+height), end); float d4 = line_distance(texcoord, end-head*vec2(+1.0,+0.5), end-vec2(3.0*head/4.0,0.0)); // Body : 1 segment float d5 = segment_distance(texcoord, start, end - vec2(linewidth,0.0)); return min(d5, max( max(-d1, d3), - max(-d2,d4))); } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/arrows/__init__.py�����������������������������������������������������������0000664�0001750�0001750�00000000000�12510536123�022110� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/arrows/stealth.glsl����������������������������������������������������������0000664�0001750�0001750�00000003342�12510536123�022342� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- #include "arrows/util.glsl" /* --------------------------------------------------------- Computes the signed distance to an stealth arrow Parameters: ----------- texcoord : Point to compute distance to body : Total length of the arrow (pixels, body+head) head : Length of the head (pixels) height : Height of the head (pixel) linewidth: Stroke line width (in pixels) antialias: Stroke antialiased area (in pixels) Return: ------- Signed distance to the arrow --------------------------------------------------------- */ float arrow_stealth(vec2 texcoord, float body, float head, float linewidth, float antialias) { float w = linewidth/2.0 + antialias; vec2 start = -vec2(body/2.0, 0.0); vec2 end = +vec2(body/2.0, 0.0); float height = 0.5; // Head : 4 lines float d1 = line_distance(texcoord, end-head*vec2(+1.0,-height), end); float d2 = line_distance(texcoord, end-head*vec2(+1.0,-height), end-vec2(3.0*head/4.0,0.0)); float d3 = line_distance(texcoord, end-head*vec2(+1.0,+height), end); float d4 = line_distance(texcoord, end-head*vec2(+1.0,+0.5), end-vec2(3.0*head/4.0,0.0)); // Body : 1 segment float d5 = segment_distance(texcoord, start, end - vec2(linewidth,0.0)); return min(d5, max( max(-d1, d3), - max(-d2,d4))); } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/arrows/triangle-30.glsl������������������������������������������������������0000664�0001750�0001750�00000001013�12510536123�022714� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- #include "arrows/util.glsl" float arrow_triangle_30(vec2 texcoord, float body, float head, float linewidth, float antialias) { return arrow_triangle(texcoord, body, head, 0.25, linewidth, antialias); } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/arrows/angle-90.glsl���������������������������������������������������������0000664�0001750�0001750�00000000776�12510536123�022222� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- #include "arrows/util.glsl" float arrow_angle_90(vec2 texcoord, float body, float head, float linewidth, float antialias) { return arrow_angle(texcoord, body, head, 1.0, linewidth, antialias); } ��vispy-0.4.0/vispy/glsl/arrows/curved.glsl�����������������������������������������������������������0000664�0001750�0001750�00000004043�12510536123�022165� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- #include "arrows/util.glsl" /* --------------------------------------------------------- Computes the signed distance to an curved arrow Parameters: ----------- texcoord : Point to compute distance to body : Total length of the arrow (pixels, body+head) head : Length of the head (pixels) height : Height of the head (pixel) linewidth: Stroke line width (in pixels) antialias: Stroke antialiased area (in pixels) Return: ------- Signed distance to the arrow --------------------------------------------------------- */ float arrow_curved(vec2 texcoord, float body, float head, float linewidth, float antialias) { float w = linewidth/2.0 + antialias; vec2 start = -vec2(body/2.0, 0.0); vec2 end = +vec2(body/2.0, 0.0); float height = 0.5; vec2 p1 = end - head*vec2(+1.0,+height); vec2 p2 = end - head*vec2(+1.0,-height); vec2 p3 = end; // Head : 3 circles vec2 c1 = circle_from_2_points(p1, p3, 1.25*body).zw; float d1 = length(texcoord - c1) - 1.25*body; vec2 c2 = circle_from_2_points(p2, p3, 1.25*body).xy; float d2 = length(texcoord - c2) - 1.25*body; vec2 c3 = circle_from_2_points(p1, p2, max(body-head, 1.0*body)).xy; float d3 = length(texcoord - c3) - max(body-head, 1.0*body); // Body : 1 segment float d4 = segment_distance(texcoord, start, end - vec2(linewidth,0.0)); // Outside (because of circles) if( texcoord.y > +(2.0*head + antialias) ) return 1000.0; if( texcoord.y < -(2.0*head + antialias) ) return 1000.0; if( texcoord.x < -(body/2.0 + antialias) ) return 1000.0; if( texcoord.x > c1.x ) //(body + antialias) ) return 1000.0; return min( d4, -min(d3,min(d1,d2))); } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/arrows/arrows.glsl�����������������������������������������������������������0000664�0001750�0001750�00000001062�12510536123�022210� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- #include "arrows/util.glsl" #include "arrows/curved.glsl" #include "arrows/stealth.glsl" #include "arrows/angle-30.glsl" #include "arrows/angle-60.glsl" #include "arrows/angle-90.glsl" #include "arrows/triangle-30.glsl" #include "arrows/triangle-60.glsl" #include "arrows/triangle-90.glsl" ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/collections/�����������������������������������������������������������������0000775�0001750�0001750�00000000000�12527674621�021030� 5����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/collections/raw-path.frag����������������������������������������������������0000664�0001750�0001750�00000000665�12510536123�023405� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- // Varyings // ------------------------------------ varying vec4 v_color; // Main // ------------------------------------ void main() { gl_FragColor = v_color; } ���������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/collections/raw-triangle.frag������������������������������������������������0000664�0001750�0001750�00000000610�12510536123�024244� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- // Varyings // ------------------------------------ varying vec4 v_color; void main(void) { gl_FragColor = v_color; } ������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/collections/agg-path.frag����������������������������������������������������0000664�0001750�0001750�00000003466�12510536123�023354� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- #include "antialias/antialias.glsl" #include "antialias/caps.glsl" // Varyings // ------------------------------------ varying vec2 v_caps; varying vec4 v_color; varying float v_length; varying vec2 v_texcoord; varying float v_linewidth; varying float v_antialias; varying float v_miter_limit; varying vec2 v_bevel_distance; void main() { float distance = v_texcoord.y; if (v_caps.x < 0.0) { gl_FragColor = cap(1, v_texcoord.x, v_texcoord.y, v_linewidth, v_antialias, v_color); // Do not return here or clipping won't be enforced // return; } else if (v_caps.y > v_length) { gl_FragColor = cap(1, v_texcoord.x-v_length, v_texcoord.y, v_linewidth, v_antialias, v_color); // Do not return here or clipping won't be enforced // return; } // Round join (instead of miter) // if (v_texcoord.x < 0.0) { distance = length(v_texcoord); } // else if(v_texcoord.x > v_length) { distance = length(v_texcoord - vec2(v_length, 0.0)); } else { // Miter limit float t = (v_miter_limit-1.0)*(v_linewidth/2.0) + v_antialias; if( (v_texcoord.x < 0.0) && (v_bevel_distance.x > (abs(distance) + t)) ) { distance = v_bevel_distance.x - t; } else if( (v_texcoord.x > v_length) && (v_bevel_distance.y > (abs(distance) + t)) ) { distance = v_bevel_distance.y - t; } gl_FragColor = stroke(distance, v_linewidth, v_antialias, v_color); } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/collections/tick-labels.vert�������������������������������������������������0000664�0001750�0001750�00000004450�12510536123�024111� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- #include "math/constants.glsl" #include "misc/viewport-NDC.glsl" // Externs // ------------------------------------ // vec2 position; // vec2 texcoord; // float scale; // vec3 origin; // vec3 direction // vec4 color; // Varyings // ------------------------------------ varying float v_scale; varying vec2 v_texcoord; varying vec4 v_color; // This works because we know the matplotlib API and how a transform is built float xscale(float x) { return ; } float yscale(float y) { return ; } float zscale(float z) { return ; } vec3 data_projection(vec3 P) { return .xyz; } vec4 view_projection(vec3 P) { return ; } vec3 data_scale(vec3 P) { return vec3(xscale(P.x), yscale(P.y), zscale(P.z)); } vec4 transform(vec3 P) { return view_projection(data_projection(data_scale(P))); } // Main // ------------------------------------ void main() { fetch_uniforms(); vec3 up = vec3(0,0,-1); vec3 O = data_projection(data_scale(origin)); vec3 tangent = normalize(data_projection(data_scale(origin+scale*direction)) - O); vec3 ortho = normalize(cross(tangent, up)); vec3 P1 = O + scale*(position.x*tangent + position.y*ortho); vec4 P1_ = view_projection(P1); vec2 p1 = NDC_to_viewport(P1_, .zw); // This compute an estimation of the actual size of the glyph vec3 P2 = O + scale*(tangent*(position.x+64.0) + ortho*(position.y)); vec4 P2_ = view_projection(P2); vec2 p2 = NDC_to_viewport(P2_, .zw); vec3 P3 = O + scale*(tangent*(position.x) + ortho*(position.y+64.0)); vec4 P3_ = view_projection(P3); vec2 p3 = NDC_to_viewport(P3_, .zw); float d2 = length(p2 - p1); float d3 = length(p3 - p1); v_scale = min(d2,d3); gl_Position = P1_; v_texcoord = texcoord; v_color = color; ; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/collections/agg-fast-path.vert�����������������������������������������������0000664�0001750�0001750�00000004570�12510536123�024345� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- // Hooks: // : vec4 function(position, ...) // // ---------------------------------------------------------------------------- #include "misc/viewport-NDC.glsl" // Externs // ------------------------------------ // extern vec3 prev; // extern vec3 curr; // extern vec3 next; // extern float id; // extern vec4 color; // extern float antialias; // extern float linewidth; // extern vec4 viewport; // vec4 transform(vec3 position); // Varyings // ------------------------------------ varying float v_antialias; varying float v_linewidth; varying float v_distance; varying vec4 v_color; // Main // ------------------------------------ void main (void) { // This function is externally generated fetch_uniforms(); v_linewidth = linewidth; v_antialias = antialias; v_color = color; // transform prev/curr/next vec4 prev_ = $transform(vec4(prev, 1)); vec4 curr_ = $transform(vec4(curr, 1)); vec4 next_ = $transform(vec4(next, 1)); // prev/curr/next in viewport coordinates vec2 _prev = NDC_to_viewport(prev_, viewport.zw); vec2 _curr = NDC_to_viewport(curr_, viewport.zw); vec2 _next = NDC_to_viewport(next_, viewport.zw); // Compute vertex final position (in viewport coordinates) float w = linewidth/2.0 + 1.5*antialias; float z; vec2 P; if( curr == prev) { vec2 v = normalize(_next.xy - _curr.xy); vec2 normal = normalize(vec2(-v.y,v.x)); P = _curr.xy + normal*w*id; } else if (curr == next) { vec2 v = normalize(_curr.xy - _prev.xy); vec2 normal = normalize(vec2(-v.y,v.x)); P = _curr.xy + normal*w*id; } else { vec2 v0 = normalize(_curr.xy - _prev.xy); vec2 v1 = normalize(_next.xy - _curr.xy); vec2 normal = normalize(vec2(-v0.y,v0.x)); vec2 tangent = normalize(v0+v1); vec2 miter = vec2(-tangent.y, tangent.x); float l = abs(w / dot(miter,normal)); P = _curr.xy + miter*l*sign(id); } if( abs(id) > 1.5 ) v_color.a = 0.0; v_distance = w*id; gl_Position = viewport_to_NDC(vec3(P, curr_.z/curr_.w), viewport.zw); } ����������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/collections/marker.frag������������������������������������������������������0000664�0001750�0001750�00000002671�12510536123�023142� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- // Hooks: // : "stroke", "filled" or "outline" // : "arrow", "asterisk", "chevron", "clover", "club", // "cross", "diamond", "disc", "ellipse", "hbar", // "heart", "infinity", "pin", "ring", "spade", // "square", "tag", "triangle", "vbar" // ---------------------------------------------------------------------------- #include "math/constants.glsl" #include "markers/markers.glsl" #include "antialias/antialias.glsl" // Varyings // ------------------------------------ varying float v_antialias; varying float v_linewidth; varying float v_size; varying float v_texcoord; varying vec4 v_fg_color; varying vec4 v_bg_color; varying vec2 v_orientation; // Main (hooked) // ------------------------------------ void main() { ; vec2 P = gl_PointCoord.xy - vec2(0.5,0.5); P = vec2(v_orientation.x*P.x - v_orientation.y*P.y, v_orientation.y*P.x + v_orientation.x*P.y); float point_size = M_SQRT2*v_size + 2. * (v_linewidth + 1.5*v_antialias); float distance = marker_(P*point_size, v_size); gl_FragColor = (distance, v_linewidth, v_antialias, v_fg_color, v_bg_color); } �����������������������������������������������������������������������vispy-0.4.0/vispy/glsl/collections/agg-glyph.frag���������������������������������������������������0000664�0001750�0001750�00000003324�12510536123�023534� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- // Uniforms // ------------------------------------ uniform sampler2D atlas_data; uniform vec2 atlas_shape; // Varyings // ------------------------------------ varying vec4 v_color; varying float v_offset; varying vec2 v_texcoord; // Main // ------------------------------------ void main(void) { ; vec2 viewport = .zw; vec4 current = texture2D(atlas_data, v_texcoord); vec4 previous= texture2D(atlas_data, v_texcoord+vec2(-1.0,0.0)/viewport); vec4 next = texture2D(atlas_data, v_texcoord+vec2(+1.0,0.0)/viewport); float r = current.r; float g = current.g; float b = current.b; if( v_offset < 1.0 ) { float z = v_offset; r = mix(current.r, previous.b, z); g = mix(current.g, current.r, z); b = mix(current.b, current.g, z); } else if( v_offset < 2.0 ) { float z = v_offset - 1.0; r = mix(previous.b, previous.g, z); g = mix(current.r, previous.b, z); b = mix(current.g, current.r, z); } else //if( v_offset <= 1.0 ) { float z = v_offset - 2.0; r = mix(previous.g, previous.r, z); g = mix(previous.b, previous.g, z); b = mix(current.r, previous.b, z); } float t = max(max(r,g),b); vec4 color = vec4(v_color.rgb, (r+g+b)/3.0); color = t*color + (1.0-t)*vec4(r,g,b, min(min(r,g),b)); gl_FragColor = vec4( color.rgb, v_color.a*color.a); } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/collections/agg-glyph.vert���������������������������������������������������0000664�0001750�0001750�00000001607�12510536123�023577� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- // Externs // ------------------------------------ // vec2 origin; // vec2 position; // vec2 texcoord; // vec4 color; // Varyings // ------------------------------------ varying vec4 v_color; varying float v_offset; varying vec2 v_texcoord; // Main // ------------------------------------ void main() { fetch_uniforms(); gl_Position = ; v_color = color; v_texcoord = texcoord; ; // We set actual position after transform v_offset = 3.0*(offset + origin.x - int(origin.x)); gl_Position = gl_Position + vec4(2.0*position/.zw,0,0); } �������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/collections/agg-point.vert���������������������������������������������������0000664�0001750�0001750�00000001635�12510536123�023606� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- // Externs // ------------------------------------ // extern vec3 position; // extern float size; // extern vec4 fg_color; // extern vec4 bg_color; // extern float orientation; // extern float antialias; // extern float linewidth; // extern vec4 transform(vec3); // Varyings // ------------------------------------ varying float v_size; varying vec4 v_color; varying float v_linewidth; varying float v_antialias; // Main (hooked) // ------------------------------------ void main (void) { fetch_uniforms(); v_size = size; v_color = color; gl_Position = $transform(vec4(position, 1)); gl_PointSize = size + 2.0 * (1.0 + 1.5*1.0); } ���������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/collections/sdf-glyph.frag���������������������������������������������������0000664�0001750�0001750�00000004510�12510536123�023550� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- // Ref: http://www.java-gaming.org/index.php?topic=33612.0 // http://www.reddit.com/ // -> r/gamedev/comments/2879jd/just_found_out_about_signed_distance_field_text/ #include "math/constants.glsl" #include "misc/spatial-filters.frag" // Uniforms // ------------------------------------ uniform sampler2D atlas_data; uniform vec2 atlas_shape; // Varyings // ------------------------------------ varying float v_scale; varying vec2 v_texcoord; varying vec4 v_color; float contour(in float d, in float w) { return smoothstep(0.5 - w, 0.5 + w, d); } float sample(sampler2D texture, vec2 uv, float w) { return contour(texture2D(texture, uv).r, w); } // Main // ------------------------------------ void main(void) { ; vec4 color = v_color; // Retrieve distance from texture float dist; if(v_scale > 50.) { dist = Bicubic(atlas_data, atlas_shape, v_texcoord).r; // Debug // color = vec4(0,0,1,1); } else { dist = texture2D(atlas_data, v_texcoord).r; } // fwidth helps keep outlines a constant width irrespective of scaling // GLSL's fwidth = abs(dFdx(uv)) + abs(dFdy(uv)) float width = fwidth(dist); // Regular SDF float alpha = contour( dist, width ); // Supersampled version (when scale is small) if (v_scale < 30.) { // Debug // color = vec4(1,0,0,1); // Supersample, 4 extra points // Half of 1/sqrt2; you can play with this float dscale = 0.5 * M_SQRT1_2; vec2 duv = dscale * (dFdx(v_texcoord) + dFdy(v_texcoord)); vec4 box = vec4(v_texcoord-duv, v_texcoord+duv); float asum = sample(atlas_data, box.xy, width) + sample(atlas_data, box.zw, width) + sample(atlas_data, box.xw, width) + sample(atlas_data, box.zy, width); // weighted average, with 4 extra points having 0.5 weight each, // so 1 + 0.5*4 = 3 is the divisor alpha = (alpha + 0.5 * asum) / 3.0; } gl_FragColor = vec4(color.rgb, alpha*color.a); } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/collections/__init__.py������������������������������������������������������0000664�0001750�0001750�00000000000�12527672621�023125� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/collections/raw-point.vert���������������������������������������������������0000664�0001750�0001750�00000001337�12510536123�023640� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- // Externs // ------------------------------------ // extern vec3 position; // extern float size; // extern vec4 color; // vec4 transform(vec3 position); // Varyings // ------------------------------------ varying float v_size; varying vec4 v_color; // Main (hooked) // ------------------------------------ void main() { fetch_uniforms(); v_size = size; v_color = color; gl_Position = $transform(vec4(position, 1)); gl_PointSize = size; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/collections/agg-marker.frag��������������������������������������������������0000664�0001750�0001750�00000002576�12510536123�023702� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- // Hooks: // : "stroke", "filled" or "outline" // : "arrow", "asterisk", "chevron", "clover", "club", // "cross", "diamond", "disc", "ellipse", "hbar", // "heart", "infinity", "pin", "ring", "spade", // "square", "tag", "triangle", "vbar" // ---------------------------------------------------------------------------- #include "math/constants.h" #include "markers/markers.glsl" #include "antialias/antialias.h" // Varyings // ------------------------------------ varying float v_antialias; varying float v_linewidth; varying float v_size; varying vec4 v_fg_color; varying vec4 v_bg_color; varying vec2 v_orientation; // Main (hooked) // ------------------------------------ void main() { vec2 P = gl_PointCoord.xy - vec2(0.5,0.5); P = vec2(v_orientation.x*P.x - v_orientation.y*P.y, v_orientation.y*P.x + v_orientation.x*P.y); float point_size = SQRT_2*v_size + 2. * (v_linewidth + 1.5*v_antialias); float distance = marker_(P*point_size, v_size); gl_FragColor = (distance, v_linewidth, v_antialias, v_fg_color, v_bg_color); } ����������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/collections/raw-segment.vert�������������������������������������������������0000664�0001750�0001750�00000001305�12510536123�024144� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- // Externs // ------------------------------------ // extern vec3 position; // extern vec4 color; // extern vec4 viewport; // vec4 transform(vec3 position); // Varyings // ------------------------------------ varying vec4 v_color; // Main // ------------------------------------ void main (void) { // This function is externally generated fetch_uniforms(); v_color = color; gl_Position = $transform(vec4(position, 1)); } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/collections/raw-point.frag���������������������������������������������������0000664�0001750�0001750�00000000637�12510536123�023601� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- // Varyings // ------------------------------------ varying float v_size; varying vec4 v_color; void main(void) { gl_FragColor = v_color; } �������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/collections/agg-point.frag���������������������������������������������������0000664�0001750�0001750�00000001303�12510536123�023535� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- #include "markers/disc.glsl" #include "antialias/filled.glsl" // Varyings // ------------------------------------ varying float v_size; varying vec4 v_color; // Main // ------------------------------------ void main() { vec2 P = gl_PointCoord.xy - vec2(0.5,0.5); float point_size = v_size + 2. * (1.0 + 1.5*1.0); float distance = marker_disc(P*point_size, v_size); gl_FragColor = filled(distance, 1.0, 1.0, v_color); } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/collections/sdf-glyph-ticks.vert���������������������������������������������0000664�0001750�0001750�00000003746�12510536123�024736� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- #include "math/constants.glsl" #include "misc/viewport-NDC.glsl" // Externs // ------------------------------------ // vec2 position; // vec2 texcoord; // float scale; // vec3 origin; // vec3 direction // vec4 color; // Varyings // ------------------------------------ varying float v_scale; varying vec2 v_texcoord; varying vec4 v_color; // Main // ------------------------------------ void main() { fetch_uniforms(); vec3 up = vec3(0,0,-1); // Untransformed normalized tangent and orhogonal directions vec3 tangent = normalize(direction.xyz); vec3 ortho = normalize(cross(tangent, up)); vec4 T = - ; T = scale*normalize(T); vec4 O = - ; O = scale*normalize(O); vec4 P1_ = + ( position.x*T + position.y*O); vec2 p1 = NDC_to_viewport(P1_, .zw); /* vec3 P1 = origin + scale*(tangent*position.x + ortho*position.y); vec4 P1_ = ; vec2 p1 = NDC_to_viewport(P1_, .zw); */ // This compute an estimation of the actual size of the glyph vec3 P2 = origin + scale*(tangent*(position.x+64.0) + ortho*(position.y)); vec4 P2_ = ; vec2 p2 = NDC_to_viewport(P2_, .zw); vec3 P3 = origin + scale*(tangent*(position.x) + ortho*(position.y+64.0)); vec4 P3_ = ; vec2 p3 = NDC_to_viewport(P3_, .zw); float d2 = length(p2 - p1); float d3 = length(p3 - p1); v_scale = min(d2,d3); gl_Position = P1_; v_texcoord = texcoord; v_color = color; ; } ��������������������������vispy-0.4.0/vispy/glsl/collections/marker.vert������������������������������������������������������0000664�0001750�0001750�00000002502�12510536123�023174� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- // Hooks: // : vec4 function(position) // // ---------------------------------------------------------------------------- #include "math/constants.glsl" // Externs // ------------------------------------ // attribute vec3 position; // attribute float size; // attribute vec4 fg_color; // attribute vec4 bg_color; // attribute float orientation; // attribute float linewidth; // Varyings // ------------------------------------ varying float v_size; varying vec4 v_fg_color; varying vec4 v_bg_color; varying vec2 v_orientation; varying float v_antialias; varying float v_linewidth; // Main (hooked) // ------------------------------------ void main (void) { // From collection fetch_uniforms(); v_size = size; v_linewidth = linewidth; v_antialias = antialias; v_fg_color = fg_color; v_bg_color = bg_color; v_orientation = vec2(cos(orientation), sin(orientation)); gl_Position = ; gl_PointSize = M_SQRT2 * size + 2.0 * (linewidth + 1.5*antialias); ; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/collections/agg-segment.vert�������������������������������������������������0000664�0001750�0001750�00000004033�12510536123�024112� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- // Hooks: // : vec4 function(position, ...) // // ---------------------------------------------------------------------------- #include "misc/viewport-NDC.glsl" // Externs // ------------------------------------ // extern vec3 P0; // extern vec3 P1; // extern float index; // extern vec4 color; // extern float antialias; // extern float linewidth; // extern vec4 viewport; // vec4 transform(vec3 position); // Varyings // ------------------------------------ varying float v_length; varying float v_antialias; varying float v_linewidth; varying vec2 v_texcoord; varying vec4 v_color; // Main // ------------------------------------ void main (void) { // This function is externally generated fetch_uniforms(); v_linewidth = linewidth; v_antialias = antialias; v_color = color; vec4 P0_ = $transform(vec4(P0, 1)); vec4 P1_ = $transform(vec4(P1, 1)); // p0/p1 in viewport coordinates vec2 p0 = NDC_to_viewport(P0_, viewport.zw); vec2 p1 = NDC_to_viewport(P1_, viewport.zw); // vec2 position; vec2 T = p1 - p0; v_length = length(T); float w = v_linewidth/2.0 + 1.5*v_antialias; T = w*normalize(T); float z; if( index < 0.5 ) { position = vec2( p0.x-T.y-T.x, p0.y+T.x-T.y); v_texcoord = vec2(-w, +w); z = P0.z; } else if( index < 1.5 ) { position = vec2(p0.x+T.y-T.x, p0.y-T.x-T.y); v_texcoord= vec2(-w, -w); z = P0.z; } else if( index < 2.5 ) { position = vec2( p1.x+T.y+T.x, p1.y-T.x+T.y); v_texcoord= vec2(v_length+w, -w); z = P1.z; } else { position = vec2( p1.x-T.y+T.x, p1.y+T.x+T.y); v_texcoord = vec2(v_length+w, +w); z = P1.z; } gl_Position = viewport_to_NDC(vec3(position,z), viewport.zw); } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/collections/agg-path.vert����������������������������������������������������0000664�0001750�0001750�00000011573�12510536123�023413� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- // Hooks: // : vec4 function(position, ...) // // ---------------------------------------------------------------------------- #include "misc/viewport-NDC.glsl" #include "math/point-to-line-distance.glsl" #include "math/point-to-line-projection.glsl" // Externs // ------------------------------------ // extern vec3 p0; // extern vec3 p1; // extern vec3 p2; // extern vec3 p3; // extern vec2 uv; // extern vec2 caps; // extern vec4 color; // extern float antialias; // extern float linewidth; // extern float miter_limit; // extern vec4 viewport; // vec4 transform(vec3 position); // Varyings // ------------------------------------ varying vec2 v_caps; varying vec4 v_color; varying float v_antialias; varying float v_linewidth; varying float v_length; varying vec2 v_texcoord; varying float v_miter_limit; varying vec2 v_bevel_distance; // Main // ------------------------------------ void main (void) { // This function is externally generated fetch_uniforms(); v_color = color; v_caps = caps; v_linewidth = linewidth; v_antialias = antialias; v_miter_limit = miter_limit; // transform prev/curr/next vec4 p0_ = $transform(vec4(p0, 1)); vec4 p1_ = $transform(vec4(p1, 1)); vec4 p2_ = $transform(vec4(p2, 1)); vec4 p3_ = $transform(vec4(p3, 1)); // prev/curr/next in viewport coordinates vec2 _p0 = NDC_to_viewport(p0_, viewport.zw); vec2 _p1 = NDC_to_viewport(p1_, viewport.zw); vec2 _p2 = NDC_to_viewport(p2_, viewport.zw); vec2 _p3 = NDC_to_viewport(p3_, viewport.zw); v_antialias = antialias; v_linewidth = linewidth; v_miter_limit = miter_limit; // Determine the direction of each of the 3 segments (previous, current, next) vec2 v0 = normalize(_p1 - _p0); vec2 v1 = normalize(_p2 - _p1); vec2 v2 = normalize(_p3 - _p2); // Determine the normal of each of the 3 segments (previous, current, next) vec2 n0 = vec2(-v0.y, v0.x); vec2 n1 = vec2(-v1.y, v1.x); vec2 n2 = vec2(-v2.y, v2.x); // Determine miter lines by averaging the normals of the 2 segments vec2 miter_a; vec2 miter_b; const float epsilon = 0.1; // WARN: Here we test if v0 = -v1 relatively to epsilon if( length(v0+v1) < epsilon ) { miter_a = n1; } else { miter_a = normalize(n0 + n1); // miter at start of current segment } // WARN: Here we test if v1 = -v2 relatively to epsilon if( length(v1+v2) < epsilon ) { miter_b = n1; } else { miter_b = normalize(n1 + n2); // miter at end of current segment } // Determine the length of the miter by projecting it onto normal vec2 p,v; float d, z; float w = linewidth/2.0 + 1.5*antialias; v_length = length(_p2-_p1); float m = miter_limit*linewidth/2.0; float length_a = w / dot(miter_a, n1); float length_b = w / dot(miter_b, n1); // Angle between prev and current segment (sign only) float d0 = +1.0; if( (v0.x*v1.y - v0.y*v1.x) > 0. ) { d0 = -1.0;} // Angle between current and next segment (sign only) float d1 = +1.0; if( (v1.x*v2.y - v1.y*v2.x) > 0. ) { d1 = -1.0; } // Adjust vertex position if (uv.x == -1.) { z = p1_.z / p1_.w; // Cap at start if( p0 == p1 ) { p = _p1 - w*v1 + uv.y* w*n1; v_texcoord = vec2(-w, uv.y*w); v_caps.x = v_texcoord.x; // Regular join } else { p = _p1 + uv.y * length_a * miter_a; v_texcoord = vec2(point_to_line_projection(_p1,_p2,p), uv.y*w); v_caps.x = 1.0; } if( p2 == p3 ) { v_caps.y = v_texcoord.x; } else { v_caps.y = 1.0; } v_bevel_distance.x = uv.y*d0*point_to_line_distance(_p1+d0*n0*w, _p1+d0*n1*w, p); v_bevel_distance.y = -point_to_line_distance(_p2+d1*n1*w, _p2+d1*n2*w, p); } else { z = p2_.z / p2_.w; // Cap at end if( p2 == p3 ) { p = _p2 + w*v1 + uv.y*w*n1; v_texcoord = vec2(v_length+w, uv.y*w); v_caps.y = v_texcoord.x; // Regular join } else { p = _p2 + uv.y*length_b * miter_b; v_texcoord = vec2(point_to_line_projection(_p1,_p2,p), uv.y*w); v_caps.y = 1.0; } if( p0 == p1 ) { v_caps.x = v_texcoord.x; } else { v_caps.x = 1.0; } v_bevel_distance.x = -point_to_line_distance(_p1+d0*n0*w, _p1+d0*n1*w, p); v_bevel_distance.y = uv.y*d1*point_to_line_distance(_p2+d1*n1*w, _p2+d1*n2*w, p); } gl_Position = viewport_to_NDC(vec3(p,z), viewport.zw); } �������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/collections/raw-triangle.vert������������������������������������������������0000664�0001750�0001750�00000001222�12510536123�024305� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- // Externs // ------------------------------------ // extern vec3 position; // extern float size; // extern vec4 color; // vec4 transform(vec3 position); // Varyings // ------------------------------------ varying vec4 v_color; // Main // ------------------------------------ void main() { fetch_uniforms(); v_color = color; gl_Position = $transform(vec4(position, 1)); } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/collections/agg-segment.frag�������������������������������������������������0000664�0001750�0001750�00000002073�12510536123�024053� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- #include "antialias/caps.glsl" #include "antialias/antialias.glsl" // Varyings // ------------------------------------ varying float v_length; varying float v_linewidth; varying float v_antialias; varying vec2 v_texcoord; varying vec4 v_color; // Main // ------------------------------------ void main (void) { if (v_texcoord.x < 0.0) { gl_FragColor = cap( CAP_ROUND, v_texcoord.x, v_texcoord.y, v_linewidth, v_antialias, v_color); } else if(v_texcoord.x > v_length) { gl_FragColor = cap( CAP_ROUND, v_texcoord.x-v_length, v_texcoord.y, v_linewidth, v_antialias, v_color); } else { gl_FragColor = stroke(v_texcoord.y, v_linewidth, v_antialias, v_color); } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/collections/agg-fast-path.frag�����������������������������������������������0000664�0001750�0001750�00000001176�12510536123�024303� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- #include "antialias/antialias.glsl" // Varyings // ------------------------------------ varying vec4 v_color; varying float v_distance; varying float v_linewidth; varying float v_antialias; // Main // ------------------------------------ void main() { if (v_color.a == 0.) { discard; } gl_FragColor = stroke(v_distance, v_linewidth, v_antialias, v_color); } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/collections/raw-segment.frag�������������������������������������������������0000664�0001750�0001750�00000000725�12510536123�024110� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- // Varyings // ------------------------------------ varying vec4 v_color; // Main // ------------------------------------ void main (void) { ; gl_FragColor = v_color; } �������������������������������������������vispy-0.4.0/vispy/glsl/collections/sdf-glyph.vert���������������������������������������������������0000664�0001750�0001750�00000003212�12510536123�023607� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- #include "math/constants.glsl" #include "misc/viewport-NDC.glsl" // Externs // ------------------------------------ // vec2 position; // vec2 texcoord; // float scale; // vec3 origin; // vec3 direction // vec4 color; // Varyings // ------------------------------------ varying float v_scale; varying vec2 v_texcoord; varying vec4 v_color; // Main // ------------------------------------ void main() { fetch_uniforms(); vec3 up = vec3(0,0,-1); // Untransformed normalized tangent and orhogonal directions vec3 tangent = normalize(direction.xyz); vec3 ortho = normalize(cross(tangent, up)); vec3 P1 = origin + scale*(tangent*position.x + ortho*position.y); vec4 P1_ = ; vec2 p1 = NDC_to_viewport(P1_, .zw); // This compute an estimation of the actual size of the glyph vec3 P2 = origin + scale*(tangent*(position.x+64.0) + ortho*(position.y)); vec4 P2_ = ; vec2 p2 = NDC_to_viewport(P2_, .zw); vec3 P3 = origin + scale*(tangent*(position.x) + ortho*(position.y+64.0)); vec4 P3_ = ; vec2 p3 = NDC_to_viewport(P3_, .zw); float d2 = length(p2 - p1); float d3 = length(p3 - p1); v_scale = min(d2,d3); gl_Position = P1_; v_texcoord = texcoord; v_color = color; ; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/collections/raw-path.vert����������������������������������������������������0000664�0001750�0001750�00000001252�12510536123�023437� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- // Externs // ------------------------------------ // extern vec3 position; // extern float id; // extern vec4 color; // vec4 transform(vec3 position); // Varyings // ------------------------------------ varying vec4 v_color; // Main // ------------------------------------ void main (void) { fetch_uniforms(); v_color = vec4(color.rgb, color.a*id); gl_Position = $transform(vec4(position, 1)); } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/glsl/collections/agg-marker.vert��������������������������������������������������0000664�0001750�0001750�00000002455�12510536123�023737� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- // Hooks: // : vec4 function(position, ...) // // ---------------------------------------------------------------------------- #version 120 #include "math/constants.glsl" // Collection externs // ------------------------------------ // extern vec2 position; // extern float size; // extern vec4 fg_color; // extern vec4 bg_color; // extern float orientation; // extern float antialias; // extern float linewidth; // Varyings // ------------------------------------ varying float v_size; varying vec4 v_fg_color; varying vec4 v_bg_color; varying vec2 v_orientation; varying float v_antialias; varying float v_linewidth; // Main (hooked) // ------------------------------------ void main (void) { fetch_uniforms(); v_size = size; v_linewidth = linewidth; v_antialias = antialias; v_fg_color = fg_color; v_bg_color = bg_color; v_orientation = vec2(cos(orientation), sin(orientation)); gl_Position = ; gl_PointSize = M_SQRT2 * size + 2.0 * (linewidth + 1.5*antialias); } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/gloo/�����������������������������������������������������������������������������0000775�0001750�0001750�00000000000�12527674621�016511� 5����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/gloo/util.py����������������������������������������������������������������������0000664�0001750�0001750�00000010600�12527672621�020033� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2015, Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- from ..ext.six import string_types from .wrappers import read_pixels def _screenshot(viewport=None, alpha=True): """ Take a screenshot using glReadPixels. Not sure where to put this yet, so a private function for now. Used in make.py. Parameters ---------- viewport : array-like | None 4-element list of x, y, w, h parameters. If None (default), the current GL viewport will be queried and used. alpha : bool If True (default), the returned array has 4 elements (RGBA). Otherwise, it has 3 (RGB) Returns ------- pixels : array 3D array of pixels in np.uint8 format """ # gl.glReadBuffer(gl.GL_BACK) Not avaliable in ES 2.0 return read_pixels(viewport, alpha) KEYWORDS = set(['active', 'asm', 'cast', 'class', 'common', 'default', 'double', 'dvec2', 'dvec3', 'dvec4', 'enum', 'extern', 'external', 'filter', 'fixed', 'flat', 'fvec2', 'fvec3', 'fvec4', 'goto', 'half', 'hvec2', 'hvec3', 'hvec4', 'iimage1D', 'iimage1DArray', 'iimage2D', 'iimage2DArray', 'iimage3D', 'iimageBuffer', 'iimageCube', 'image1D', 'image1DArray', 'image1DArrayShadow', 'image1DShadow', 'image2D', 'image2DArray', 'image2DArrayShadow', 'image2DShadow', 'image3D', 'imageBuffer', 'imageCube', 'inline', 'input', 'interface', 'long', 'namespace', 'noinline', 'output', 'packed', 'partition', 'public', 'row_major', 'sampler1D', 'sampler1DShadow', 'sampler2DRect', 'sampler2DRectShadow', 'sampler2DShadow', 'sampler3D', 'sampler3DRect', 'short', 'sizeof', 'static', 'superp', 'switch', 'template', 'this', 'typedef', 'uimage1D', 'uimage1DArray', 'uimage2D', 'uimage2DArray', 'uimage3D', 'uimageBuffer', 'uimageCube', 'union', 'unsigned', 'using', 'volatile']) def check_variable(name): """ Return None if *name* is expected to be a valid variable name in any GLSL version. Otherwise, return a string describing the problem. """ # Limit imposed by glGetActive* in pyopengl if len(name) > 31: return ("Variable names >31 characters may not function on some " "systems.") return check_identifier(name) def check_identifier(name): if '__' in name: return "Identifiers may not contain double-underscores." if name[:3] == 'gl_' or name[:3] == 'GL_': return "Identifiers may not begin with gl_ or GL_." if name in KEYWORDS: return "Identifier is a reserved keyword." def check_enum(enum, name=None, valid=None): """ Get lowercase string representation of enum. """ name = name or 'enum' # Try to convert res = None if isinstance(enum, int): if hasattr(enum, 'name') and enum.name.startswith('GL_'): res = enum.name[3:].lower() elif isinstance(enum, string_types): res = enum.lower() # Check if res is None: raise ValueError('Could not determine string represenatation for' 'enum %r' % enum) elif valid and res not in valid: raise ValueError('Value of %s must be one of %r, not %r' % (name, valid, enum)) return res vert_draw = """ attribute vec2 a_position; attribute vec2 a_texcoord; varying vec2 v_uv; void main(void) { v_uv = a_texcoord.xy; gl_Position = vec4(a_position, 0, 1); } """ frag_draw = """ uniform sampler2D u_texture; varying vec2 v_uv; void main(void) { gl_FragColor = texture2D(u_texture, v_uv).rgba; } """ def draw_texture(tex): """Draw a 2D texture to the current viewport Parameters ---------- tex : instance of Texture2D The texture to draw. """ from .program import Program program = Program(vert_draw, frag_draw) program['u_texture'] = tex program['a_position'] = [[-1., -1.], [-1., 1.], [1., -1.], [1., 1.]] program['a_texcoord'] = [[0., 1.], [0., 0.], [1., 1.], [1., 0.]] program.draw('triangle_strip') ��������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/gloo/texture.py�������������������������������������������������������������������0000664�0001750�0001750�00000100450�12527672621�020561� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2015, Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- import math import numpy as np import warnings from .globject import GLObject from ..ext.six import string_types from .util import check_enum # ----------------------------------------------------------- Texture class --- class BaseTexture(GLObject): """ A Texture is used to represent a topological set of scalar values. Parameters ---------- data : ndarray | tuple | None Texture data in the form of a numpy array (or something that can be turned into one). A tuple with the shape of the texture can also be given. format : str | enum | None The format of the texture: 'luminance', 'alpha', 'luminance_alpha', 'rgb', or 'rgba'. If not given the format is chosen automatically based on the number of channels. When the data has one channel, 'luminance' is assumed. resizable : bool Indicates whether texture can be resized. Default True. interpolation : str | None Interpolation mode, must be one of: 'nearest', 'linear'. Default 'nearest'. wrapping : str | None Wrapping mode, must be one of: 'repeat', 'clamp_to_edge', 'mirrored_repeat'. Default 'clamp_to_edge'. shape : tuple | None Optional. A tuple with the shape of the texture. If ``data`` is also a tuple, it will override the value of ``shape``. internalformat : str | None Internal format to use. resizeable : None Deprecated version of `resizable`. """ _ndim = 2 _formats = { 1: 'luminance', # or alpha, or red 2: 'luminance_alpha', # or rg 3: 'rgb', 4: 'rgba' } _inv_formats = { 'luminance': 1, 'alpha': 1, 'red': 1, 'luminance_alpha': 2, 'rg': 2, 'rgb': 3, 'rgba': 4 } _inv_internalformats = dict([ (base + suffix, channels) for base, channels in [('r', 1), ('rg', 2), ('rgb', 3), ('rgba', 4)] for suffix in ['8', '16', '16f', '32f'] ] + [ ('luminance', 1), ('alpha', 1), ('red', 1), ('luminance_alpha', 2), ('rg', 2), ('rgb', 3), ('rgba', 4) ]) def __init__(self, data=None, format=None, resizable=True, interpolation=None, wrapping=None, shape=None, internalformat=None, resizeable=None): GLObject.__init__(self) if resizeable is not None: resizable = resizeable warnings.warn('resizeable has been deprecated in favor of ' 'resizable and will be removed next release', DeprecationWarning) # Init shape and format self._resizable = True # at least while we're in init self._shape = tuple([0 for i in range(self._ndim+1)]) self._format = format self._internalformat = internalformat # Set texture parameters (before setting data) self.interpolation = interpolation or 'nearest' self.wrapping = wrapping or 'clamp_to_edge' # Set data or shape (shape arg is for backward compat) if isinstance(data, tuple): shape, data = data, None if data is not None: if shape is not None: raise ValueError('Texture needs data or shape, not both.') data = np.array(data, copy=False) # So we can test the combination self._resize(data.shape, format, internalformat) self._set_data(data) elif shape is not None: self._resize(shape, format, internalformat) else: raise ValueError("Either data or shape must be given") # Set resizable (at end of init) self._resizable = bool(resizable) def _normalize_shape(self, data_or_shape): # Get data and shape from input if isinstance(data_or_shape, np.ndarray): data = data_or_shape shape = data.shape else: assert isinstance(data_or_shape, tuple) data = None shape = data_or_shape # Check and correct if shape: if len(shape) < self._ndim: raise ValueError("Too few dimensions for texture") elif len(shape) > self._ndim + 1: raise ValueError("Too many dimensions for texture") elif len(shape) == self._ndim: shape = shape + (1,) else: # if len(shape) == self._ndim + 1: if shape[-1] > 4: raise ValueError("Too many channels for texture") # Return return data.reshape(shape) if data is not None else shape @property def shape(self): """ Data shape (last dimension indicates number of color channels) """ return self._shape @property def format(self): """ The texture format (color channels). """ return self._format @property def wrapping(self): """ Texture wrapping mode """ value = self._wrapping return value[0] if all([v == value[0] for v in value]) else value @wrapping.setter def wrapping(self, value): # Convert if isinstance(value, int) or isinstance(value, string_types): value = (value,) * self._ndim elif isinstance(value, (tuple, list)): if len(value) != self._ndim: raise ValueError('Texture wrapping needs 1 or %i values' % self._ndim) else: raise ValueError('Invalid value for wrapping: %r' % value) # Check and set valid = 'repeat', 'clamp_to_edge', 'mirrored_repeat' value = tuple([check_enum(value[i], 'tex wrapping', valid) for i in range(self._ndim)]) self._wrapping = value self._glir.command('WRAPPING', self._id, value) @property def interpolation(self): """ Texture interpolation for minification and magnification. """ value = self._interpolation return value[0] if value[0] == value[1] else value @interpolation.setter def interpolation(self, value): # Convert if isinstance(value, int) or isinstance(value, string_types): value = (value,) * 2 elif isinstance(value, (tuple, list)): if len(value) != 2: raise ValueError('Texture interpolation needs 1 or 2 values') else: raise ValueError('Invalid value for interpolation: %r' % value) # Check and set valid = 'nearest', 'linear' value = (check_enum(value[0], 'tex interpolation', valid), check_enum(value[1], 'tex interpolation', valid)) self._interpolation = value self._glir.command('INTERPOLATION', self._id, *value) def resize(self, shape, format=None, internalformat=None): """Set the texture size and format Parameters ---------- shape : tuple of integers New texture shape in zyx order. Optionally, an extra dimention may be specified to indicate the number of color channels. format : str | enum | None The format of the texture: 'luminance', 'alpha', 'luminance_alpha', 'rgb', or 'rgba'. If not given the format is chosen automatically based on the number of channels. When the data has one channel, 'luminance' is assumed. internalformat : str | enum | None The internal (storage) format of the texture: 'luminance', 'alpha', 'r8', 'r16', 'r16f', 'r32f'; 'luminance_alpha', 'rg8', 'rg16', 'rg16f', 'rg32f'; 'rgb', 'rgb8', 'rgb16', 'rgb16f', 'rgb32f'; 'rgba', 'rgba8', 'rgba16', 'rgba16f', 'rgba32f'. If None, the internalformat is chosen automatically based on the number of channels. This is a hint which may be ignored by the OpenGL implementation. """ return self._resize(shape, format, internalformat) def _resize(self, shape, format=None, internalformat=None): """Internal method for resize. """ shape = self._normalize_shape(shape) # Check if not self._resizable: raise RuntimeError("Texture is not resizable") # Determine format if format is None: format = self._formats[shape[-1]] # Keep current format if channels match if self._format and \ self._inv_formats[self._format] == self._inv_formats[format]: format = self._format else: format = check_enum(format) if internalformat is None: # Keep current internalformat if channels match if self._internalformat and \ self._inv_internalformats[self._internalformat] == shape[-1]: internalformat = self._internalformat else: internalformat = check_enum(internalformat) # Check if format not in self._inv_formats: raise ValueError('Invalid texture format: %r.' % format) elif shape[-1] != self._inv_formats[format]: raise ValueError('Format does not match with given shape.') if internalformat is None: pass elif internalformat not in self._inv_internalformats: raise ValueError( 'Invalid texture internalformat: %r.' % internalformat ) elif shape[-1] != self._inv_internalformats[internalformat]: raise ValueError('Internalformat does not match with given shape.') # Store and send GLIR command self._shape = shape self._format = format self._internalformat = internalformat self._glir.command('SIZE', self._id, self._shape, self._format, self._internalformat) def set_data(self, data, offset=None, copy=False): """Set texture data Parameters ---------- data : ndarray Data to be uploaded offset: int | tuple of ints Offset in texture where to start copying data copy: bool Since the operation is deferred, data may change before data is actually uploaded to GPU memory. Asking explicitly for a copy will prevent this behavior. Notes ----- This operation implicitely resizes the texture to the shape of the data if given offset is None. """ return self._set_data(data, offset, copy) def _set_data(self, data, offset=None, copy=False): """Internal method for set_data. """ # Copy if needed, check/normalize shape data = np.array(data, copy=copy) data = self._normalize_shape(data) # Maybe resize to purge DATA commands? if offset is None: self._resize(data.shape) elif all([i == 0 for i in offset]) and data.shape == self._shape: self._resize(data.shape) # Convert offset to something usable offset = offset or tuple([0 for i in range(self._ndim)]) assert len(offset) == self._ndim # Check if data fits for i in range(len(data.shape)-1): if offset[i] + data.shape[i] > self._shape[i]: raise ValueError("Data is too large") # Send GLIR command self._glir.command('DATA', self._id, offset, data) def __setitem__(self, key, data): """ x.__getitem__(y) <==> x[y] """ # Make sure key is a tuple if isinstance(key, (int, slice)) or key == Ellipsis: key = (key,) # Default is to access the whole texture shape = self._shape slices = [slice(0, shape[i]) for i in range(len(shape))] # Check last key/Ellipsis to decide on the order keys = key[::+1] dims = range(0, len(key)) if key[0] == Ellipsis: keys = key[::-1] dims = range(len(self._shape) - 1, len(self._shape) - 1 - len(keys), -1) # Find exact range for each key for k, dim in zip(keys, dims): size = self._shape[dim] if isinstance(k, int): if k < 0: k += size if k < 0 or k > size: raise IndexError("Texture assignment index out of range") start, stop = k, k + 1 slices[dim] = slice(start, stop, 1) elif isinstance(k, slice): start, stop, step = k.indices(size) if step != 1: raise IndexError("Cannot access non-contiguous data") if stop < start: start, stop = stop, start slices[dim] = slice(start, stop, step) elif k == Ellipsis: pass else: raise TypeError("Texture indices must be integers") offset = tuple([s.start for s in slices])[:self._ndim] shape = tuple([s.stop - s.start for s in slices]) size = np.prod(shape) if len(shape) > 0 else 1 # Make sure data is an array if not isinstance(data, np.ndarray): data = np.array(data, copy=False) # Make sure data is big enough if data.shape != shape: data = np.resize(data, shape) # Set data (deferred) self._set_data(data=data, offset=offset, copy=False) def __repr__(self): return "<%s shape=%r format=%r at 0x%x>" % ( self.__class__.__name__, self._shape, self._format, id(self)) # --------------------------------------------------------- Texture1D class --- class Texture1D(BaseTexture): """ One dimensional texture Parameters ---------- data : ndarray | tuple | None Texture data in the form of a numpy array (or something that can be turned into one). A tuple with the shape of the texture can also be given. format : str | enum | None The format of the texture: 'luminance', 'alpha', 'luminance_alpha', 'rgb', or 'rgba'. If not given the format is chosen automatically based on the number of channels. When the data has one channel, 'luminance' is assumed. resizable : bool Indicates whether texture can be resized. Default True. interpolation : str | None Interpolation mode, must be one of: 'nearest', 'linear'. Default 'nearest'. wrapping : str | None Wrapping mode, must be one of: 'repeat', 'clamp_to_edge', 'mirrored_repeat'. Default 'clamp_to_edge'. shape : tuple | None Optional. A tuple with the shape of the texture. If ``data`` is also a tuple, it will override the value of ``shape``. internalformat : str | None Internal format to use. resizeable : None Deprecated version of `resizable`. """ _ndim = 1 _GLIR_TYPE = 'Texture1D' def __init__(self, data=None, format=None, resizable=True, interpolation=None, wrapping=None, shape=None, internalformat=None, resizeable=None): BaseTexture.__init__(self, data, format, resizable, interpolation, wrapping, shape, internalformat, resizeable) @property def width(self): """ Texture width """ return self._shape[0] @property def glsl_type(self): """ GLSL declaration strings required for a variable to hold this data. """ return 'uniform', 'sampler1D' @property def glsl_sampler_type(self): """ GLSL type of the sampler. """ return 'sampler1D' @property def glsl_sample(self): """ GLSL function that samples the texture. """ return 'texture1D' # --------------------------------------------------------- Texture2D class --- class Texture2D(BaseTexture): """ Two dimensional texture Parameters ---------- data : ndarray Texture data shaped as W, or a tuple with the shape for the texture (W). format : str | enum | None The format of the texture: 'luminance', 'alpha', 'luminance_alpha', 'rgb', or 'rgba'. If not given the format is chosen automatically based on the number of channels. When the data has one channel, 'luminance' is assumed. resizable : bool Indicates whether texture can be resized. Default True. interpolation : str Interpolation mode, must be one of: 'nearest', 'linear'. Default 'nearest'. wrapping : str Wrapping mode, must be one of: 'repeat', 'clamp_to_edge', 'mirrored_repeat'. Default 'clamp_to_edge'. shape : tuple Optional. A tuple with the shape HxW. If ``data`` is also a tuple, it will override the value of ``shape``. internalformat : str | None Internal format to use. resizeable : None Deprecated version of `resizable`. """ _ndim = 2 _GLIR_TYPE = 'Texture2D' def __init__(self, data=None, format=None, resizable=True, interpolation=None, wrapping=None, shape=None, internalformat=None, resizeable=None): BaseTexture.__init__(self, data, format, resizable, interpolation, wrapping, shape, internalformat, resizeable) @property def height(self): """ Texture height """ return self._shape[0] @property def width(self): """ Texture width """ return self._shape[1] @property def glsl_type(self): """ GLSL declaration strings required for a variable to hold this data. """ return 'uniform', 'sampler2D' @property def glsl_sampler_type(self): """ GLSL type of the sampler. """ return 'sampler2D' @property def glsl_sample(self): """ GLSL function that samples the texture. """ return 'texture2D' # --------------------------------------------------------- Texture3D class --- class Texture3D(BaseTexture): """ Three dimensional texture Parameters ---------- data : ndarray | tuple | None Texture data in the form of a numpy array (or something that can be turned into one). A tuple with the shape of the texture can also be given. format : str | enum | None The format of the texture: 'luminance', 'alpha', 'luminance_alpha', 'rgb', or 'rgba'. If not given the format is chosen automatically based on the number of channels. When the data has one channel, 'luminance' is assumed. resizable : bool Indicates whether texture can be resized. Default True. interpolation : str | None Interpolation mode, must be one of: 'nearest', 'linear'. Default 'nearest'. wrapping : str | None Wrapping mode, must be one of: 'repeat', 'clamp_to_edge', 'mirrored_repeat'. Default 'clamp_to_edge'. shape : tuple | None Optional. A tuple with the shape of the texture. If ``data`` is also a tuple, it will override the value of ``shape``. internalformat : str | None Internal format to use. resizeable : None Deprecated version of `resizable`. """ _ndim = 3 _GLIR_TYPE = 'Texture3D' def __init__(self, data=None, format=None, resizable=True, interpolation=None, wrapping=None, shape=None, internalformat=None, resizeable=None): BaseTexture.__init__(self, data, format, resizable, interpolation, wrapping, shape, internalformat, resizeable) @property def width(self): """ Texture width """ return self._shape[2] @property def height(self): """ Texture height """ return self._shape[1] @property def depth(self): """ Texture depth """ return self._shape[0] @property def glsl_type(self): """ GLSL declaration strings required for a variable to hold this data. """ return 'uniform', 'sampler3D' @property def glsl_sampler_type(self): """ GLSL type of the sampler. """ return 'sampler3D' @property def glsl_sample(self): """ GLSL function that samples the texture. """ return 'texture3D' # ------------------------------------------------- TextureEmulated3D class --- class TextureEmulated3D(Texture2D): """ Two dimensional texture that is emulating a three dimensional texture Parameters ---------- data : ndarray | tuple | None Texture data in the form of a numpy array (or something that can be turned into one). A tuple with the shape of the texture can also be given. format : str | enum | None The format of the texture: 'luminance', 'alpha', 'luminance_alpha', 'rgb', or 'rgba'. If not given the format is chosen automatically based on the number of channels. When the data has one channel, 'luminance' is assumed. resizable : bool Indicates whether texture can be resized. Default True. interpolation : str | None Interpolation mode, must be one of: 'nearest', 'linear'. Default 'nearest'. wrapping : str | None Wrapping mode, must be one of: 'repeat', 'clamp_to_edge', 'mirrored_repeat'. Default 'clamp_to_edge'. shape : tuple | None Optional. A tuple with the shape of the texture. If ``data`` is also a tuple, it will override the value of ``shape``. internalformat : str | None Internal format to use. resizeable : None Deprecated version of `resizable`. """ # TODO: does GL's nearest use floor or round? _glsl_sample_nearest = """ vec4 sample(sampler2D tex, vec3 texcoord) { // Don't let adjacent frames be interpolated into this one texcoord.x = min(texcoord.x * $shape.x, $shape.x - 0.5); texcoord.x = max(0.5, texcoord.x) / $shape.x; texcoord.y = min(texcoord.y * $shape.y, $shape.y - 0.5); texcoord.y = max(0.5, texcoord.y) / $shape.y; float index = floor(texcoord.z * $shape.z); // Do a lookup in the 2D texture float u = (mod(index, $r) + texcoord.x) / $r; float v = (floor(index / $r) + texcoord.y) / $c; return texture2D(tex, vec2(u,v)); } """ _glsl_sample_linear = """ vec4 sample(sampler2D tex, vec3 texcoord) { // Don't let adjacent frames be interpolated into this one texcoord.x = min(texcoord.x * $shape.x, $shape.x - 0.5); texcoord.x = max(0.5, texcoord.x) / $shape.x; texcoord.y = min(texcoord.y * $shape.y, $shape.y - 0.5); texcoord.y = max(0.5, texcoord.y) / $shape.y; float z = texcoord.z * $shape.z; float zindex1 = floor(z); float u1 = (mod(zindex1, $r) + texcoord.x) / $r; float v1 = (floor(zindex1 / $r) + texcoord.y) / $c; float zindex2 = zindex1 + 1.0; float u2 = (mod(zindex2, $r) + texcoord.x) / $r; float v2 = (floor(zindex2 / $r) + texcoord.y) / $c; vec4 s1 = texture2D(tex, vec2(u1, v1)); vec4 s2 = texture2D(tex, vec2(u2, v2)); return s1 * (zindex2 - z) + s2 * (z - zindex1); } """ _gl_max_texture_size = 1024 # For now, we just set this manually def __init__(self, data=None, format=None, resizable=True, interpolation=None, wrapping=None, shape=None, internalformat=None, resizeable=None): from ..visuals.shaders import Function self._set_emulated_shape(data) Texture2D.__init__(self, self._normalize_emulated_shape(data), format, resizable, interpolation, wrapping, shape, internalformat, resizeable) if self.interpolation == 'nearest': self._glsl_sample = Function(self.__class__._glsl_sample_nearest) else: self._glsl_sample = Function(self.__class__._glsl_sample_linear) self._update_variables() def _set_emulated_shape(self, data_or_shape): if isinstance(data_or_shape, np.ndarray): self._emulated_shape = data_or_shape.shape else: assert isinstance(data_or_shape, tuple) self._emulated_shape = tuple(data_or_shape) depth, width = self._emulated_shape[0], self._emulated_shape[1] self._r = TextureEmulated3D._gl_max_texture_size // width self._c = depth // self._r if math.fmod(depth, self._r): self._c += 1 def _normalize_emulated_shape(self, data_or_shape): if isinstance(data_or_shape, np.ndarray): new_shape = self._normalize_emulated_shape(data_or_shape.shape) new_data = np.empty(new_shape, dtype=data_or_shape.dtype) for j in range(self._c): for i in range(self._r): i0, i1 = i * self.width, (i+1) * self.width j0, j1 = j * self.height, (j+1) * self.height k = j * self._r + i if k >= self.depth: break new_data[j0:j1, i0:i1] = data_or_shape[k] return new_data assert isinstance(data_or_shape, tuple) return (self._c * self.height, self._r * self.width) + \ data_or_shape[3:] def _update_variables(self): self._glsl_sample['shape'] = self.shape[:3][::-1] self._glsl_sample['c'] = self._c self._glsl_sample['r'] = self._r def set_data(self, data, offset=None, copy=False): """Set texture data Parameters ---------- data : ndarray Data to be uploaded offset: int | tuple of ints Offset in texture where to start copying data copy: bool Since the operation is deferred, data may change before data is actually uploaded to GPU memory. Asking explicitly for a copy will prevent this behavior. Notes ----- This operation implicitely resizes the texture to the shape of the data if given offset is None. """ self._set_emulated_shape(data) Texture2D.set_data(self, self._normalize_emulated_shape(data), offset, copy) self._update_variables() def resize(self, shape, format=None, internalformat=None): """Set the texture size and format Parameters ---------- shape : tuple of integers New texture shape in zyx order. Optionally, an extra dimention may be specified to indicate the number of color channels. format : str | enum | None The format of the texture: 'luminance', 'alpha', 'luminance_alpha', 'rgb', or 'rgba'. If not given the format is chosen automatically based on the number of channels. When the data has one channel, 'luminance' is assumed. internalformat : str | enum | None The internal (storage) format of the texture: 'luminance', 'alpha', 'r8', 'r16', 'r16f', 'r32f'; 'luminance_alpha', 'rg8', 'rg16', 'rg16f', 'rg32f'; 'rgb', 'rgb8', 'rgb16', 'rgb16f', 'rgb32f'; 'rgba', 'rgba8', 'rgba16', 'rgba16f', 'rgba32f'. If None, the internalformat is chosen automatically based on the number of channels. This is a hint which may be ignored by the OpenGL implementation. """ self._set_emulated_shape(shape) Texture2D.resize(self, self._normalize_emulated_shape(shape), format, internalformat) self._update_variables() @property def shape(self): """ Data shape (last dimension indicates number of color channels) """ return self._emulated_shape @property def width(self): """ Texture width """ return self._emulated_shape[2] @property def height(self): """ Texture height """ return self._emulated_shape[1] @property def depth(self): """ Texture depth """ return self._emulated_shape[0] @property def glsl_sample(self): """ GLSL function that samples the texture. """ return self._glsl_sample # ------------------------------------------------------ TextureAtlas class --- class TextureAtlas(Texture2D): """Group multiple small data regions into a larger texture. The algorithm is based on the article by Jukka Jylänki : "A Thousand Ways to Pack the Bin - A Practical Approach to Two-Dimensional Rectangle Bin Packing", February 27, 2010. More precisely, this is an implementation of the Skyline Bottom-Left algorithm based on C++ sources provided by Jukka Jylänki at: http://clb.demon.fi/files/RectangleBinPack/. Parameters ---------- shape : tuple of int Texture shape (optional). Notes ----- This creates a 2D texture that holds 1D float32 data. An example of simple access: >>> atlas = TextureAtlas() >>> bounds = atlas.get_free_region(20, 30) >>> atlas.set_region(bounds, np.random.rand(20, 30).T) """ def __init__(self, shape=(1024, 1024)): shape = np.array(shape, int) assert shape.ndim == 1 and shape.size == 2 shape = tuple(2 ** (np.log2(shape) + 0.5).astype(int)) + (3,) self._atlas_nodes = [(0, 0, shape[1])] data = np.zeros(shape, np.float32) super(TextureAtlas, self).__init__(data, interpolation='linear', wrapping='clamp_to_edge') def get_free_region(self, width, height): """Get a free region of given size and allocate it Parameters ---------- width : int Width of region to allocate height : int Height of region to allocate Returns ------- bounds : tuple | None A newly allocated region as (x, y, w, h) or None (if failed). """ best_height = best_width = np.inf best_index = -1 for i in range(len(self._atlas_nodes)): y = self._fit(i, width, height) if y >= 0: node = self._atlas_nodes[i] if (y+height < best_height or (y+height == best_height and node[2] < best_width)): best_height = y+height best_index = i best_width = node[2] region = node[0], y, width, height if best_index == -1: return None node = region[0], region[1] + height, width self._atlas_nodes.insert(best_index, node) i = best_index+1 while i < len(self._atlas_nodes): node = self._atlas_nodes[i] prev_node = self._atlas_nodes[i-1] if node[0] < prev_node[0]+prev_node[2]: shrink = prev_node[0]+prev_node[2] - node[0] x, y, w = self._atlas_nodes[i] self._atlas_nodes[i] = x+shrink, y, w-shrink if self._atlas_nodes[i][2] <= 0: del self._atlas_nodes[i] i -= 1 else: break else: break i += 1 # Merge nodes i = 0 while i < len(self._atlas_nodes)-1: node = self._atlas_nodes[i] next_node = self._atlas_nodes[i+1] if node[1] == next_node[1]: self._atlas_nodes[i] = node[0], node[1], node[2]+next_node[2] del self._atlas_nodes[i+1] else: i += 1 return region def _fit(self, index, width, height): """Test if region (width, height) fit into self._atlas_nodes[index]""" node = self._atlas_nodes[index] x, y = node[0], node[1] width_left = width if x+width > self._shape[1]: return -1 i = index while width_left > 0: node = self._atlas_nodes[i] y = max(y, node[1]) if y+height > self._shape[0]: return -1 width_left -= node[2] i += 1 return y ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/gloo/gl/��������������������������������������������������������������������������0000775�0001750�0001750�00000000000�12527674621�017113� 5����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/gloo/gl/_es2.py�������������������������������������������������������������������0000664�0001750�0001750�00000112540�12426461252�020310� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������""" THIS CODE IS AUTO-GENERATED. DO NOT EDIT. GL ES 2.0 API (via Angle/DirectX on Windows) """ import ctypes from .es2 import _lib _lib.glActiveTexture.argtypes = ctypes.c_uint, # void = glActiveTexture(GLenum texture) def glActiveTexture(texture): _lib.glActiveTexture(texture) _lib.glAttachShader.argtypes = ctypes.c_uint, ctypes.c_uint, # void = glAttachShader(GLuint program, GLuint shader) def glAttachShader(program, shader): _lib.glAttachShader(program, shader) _lib.glBindAttribLocation.argtypes = ctypes.c_uint, ctypes.c_uint, ctypes.c_char_p, # void = glBindAttribLocation(GLuint program, GLuint index, GLchar* name) def glBindAttribLocation(program, index, name): name = ctypes.c_char_p(name.encode('utf-8')) res = _lib.glBindAttribLocation(program, index, name) _lib.glBindBuffer.argtypes = ctypes.c_uint, ctypes.c_uint, # void = glBindBuffer(GLenum target, GLuint buffer) def glBindBuffer(target, buffer): _lib.glBindBuffer(target, buffer) _lib.glBindFramebuffer.argtypes = ctypes.c_uint, ctypes.c_uint, # void = glBindFramebuffer(GLenum target, GLuint framebuffer) def glBindFramebuffer(target, framebuffer): _lib.glBindFramebuffer(target, framebuffer) _lib.glBindRenderbuffer.argtypes = ctypes.c_uint, ctypes.c_uint, # void = glBindRenderbuffer(GLenum target, GLuint renderbuffer) def glBindRenderbuffer(target, renderbuffer): _lib.glBindRenderbuffer(target, renderbuffer) _lib.glBindTexture.argtypes = ctypes.c_uint, ctypes.c_uint, # void = glBindTexture(GLenum target, GLuint texture) def glBindTexture(target, texture): _lib.glBindTexture(target, texture) _lib.glBlendColor.argtypes = ctypes.c_float, ctypes.c_float, ctypes.c_float, ctypes.c_float, # void = glBlendColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha) def glBlendColor(red, green, blue, alpha): _lib.glBlendColor(red, green, blue, alpha) _lib.glBlendEquation.argtypes = ctypes.c_uint, # void = glBlendEquation(GLenum mode) def glBlendEquation(mode): _lib.glBlendEquation(mode) _lib.glBlendEquationSeparate.argtypes = ctypes.c_uint, ctypes.c_uint, # void = glBlendEquationSeparate(GLenum modeRGB, GLenum modeAlpha) def glBlendEquationSeparate(modeRGB, modeAlpha): _lib.glBlendEquationSeparate(modeRGB, modeAlpha) _lib.glBlendFunc.argtypes = ctypes.c_uint, ctypes.c_uint, # void = glBlendFunc(GLenum sfactor, GLenum dfactor) def glBlendFunc(sfactor, dfactor): _lib.glBlendFunc(sfactor, dfactor) _lib.glBlendFuncSeparate.argtypes = ctypes.c_uint, ctypes.c_uint, ctypes.c_uint, ctypes.c_uint, # void = glBlendFuncSeparate(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) def glBlendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha): _lib.glBlendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha) _lib.glBufferData.argtypes = ctypes.c_uint, ctypes.c_int, ctypes.c_void_p, ctypes.c_uint, # void = glBufferData(GLenum target, GLsizeiptr size, GLvoid* data, GLenum usage) def glBufferData(target, data, usage): """ Data can be numpy array or the size of data to allocate. """ if isinstance(data, int): size = data data = ctypes.c_voidp(0) else: if not data.flags['C_CONTIGUOUS'] or not data.flags['ALIGNED']: data = data.copy('C') data_ = data size = data_.nbytes data = data_.ctypes.data res = _lib.glBufferData(target, size, data, usage) _lib.glBufferSubData.argtypes = ctypes.c_uint, ctypes.c_int, ctypes.c_int, ctypes.c_void_p, # void = glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, GLvoid* data) def glBufferSubData(target, offset, data): if not data.flags['C_CONTIGUOUS']: data = data.copy('C') data_ = data size = data_.nbytes data = data_.ctypes.data res = _lib.glBufferSubData(target, offset, size, data) _lib.glCheckFramebufferStatus.argtypes = ctypes.c_uint, _lib.glCheckFramebufferStatus.restype = ctypes.c_uint # GLenum = glCheckFramebufferStatus(GLenum target) def glCheckFramebufferStatus(target): return _lib.glCheckFramebufferStatus(target) _lib.glClear.argtypes = ctypes.c_uint, # void = glClear(GLbitfield mask) def glClear(mask): _lib.glClear(mask) _lib.glClearColor.argtypes = ctypes.c_float, ctypes.c_float, ctypes.c_float, ctypes.c_float, # void = glClearColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha) def glClearColor(red, green, blue, alpha): _lib.glClearColor(red, green, blue, alpha) _lib.glClearDepthf.argtypes = ctypes.c_float, # void = glClearDepthf(GLclampf depth) def glClearDepth(depth): _lib.glClearDepthf(depth) _lib.glClearStencil.argtypes = ctypes.c_int, # void = glClearStencil(GLint s) def glClearStencil(s): _lib.glClearStencil(s) _lib.glColorMask.argtypes = ctypes.c_bool, ctypes.c_bool, ctypes.c_bool, ctypes.c_bool, # void = glColorMask(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha) def glColorMask(red, green, blue, alpha): _lib.glColorMask(red, green, blue, alpha) _lib.glCompileShader.argtypes = ctypes.c_uint, # void = glCompileShader(GLuint shader) def glCompileShader(shader): _lib.glCompileShader(shader) _lib.glCompressedTexImage2D.argtypes = ctypes.c_uint, ctypes.c_int, ctypes.c_uint, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_void_p, # void = glCompressedTexImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, GLvoid* data) def glCompressedTexImage2D(target, level, internalformat, width, height, border, data): # border = 0 # set in args if not data.flags['C_CONTIGUOUS']: data = data.copy('C') data_ = data size = data_.size data = data_.ctypes.data res = _lib.glCompressedTexImage2D(target, level, internalformat, width, height, border, imageSize, data) _lib.glCompressedTexSubImage2D.argtypes = ctypes.c_uint, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_uint, ctypes.c_int, ctypes.c_void_p, # void = glCompressedTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, GLvoid* data) def glCompressedTexSubImage2D(target, level, xoffset, yoffset, width, height, format, data): if not data.flags['C_CONTIGUOUS']: data = data.copy('C') data_ = data size = data_.size data = data_.ctypes.data res = _lib.glCompressedTexSubImage2D(target, level, xoffset, yoffset, width, height, format, imageSize, data) _lib.glCopyTexImage2D.argtypes = ctypes.c_uint, ctypes.c_int, ctypes.c_uint, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int, # void = glCopyTexImage2D(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border) def glCopyTexImage2D(target, level, internalformat, x, y, width, height, border): _lib.glCopyTexImage2D(target, level, internalformat, x, y, width, height, border) _lib.glCopyTexSubImage2D.argtypes = ctypes.c_uint, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int, # void = glCopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height) def glCopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height): _lib.glCopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height) _lib.glCreateProgram.argtypes = () _lib.glCreateProgram.restype = ctypes.c_uint # GLuint = glCreateProgram() def glCreateProgram(): return _lib.glCreateProgram() _lib.glCreateShader.argtypes = ctypes.c_uint, _lib.glCreateShader.restype = ctypes.c_uint # GLuint = glCreateShader(GLenum type) def glCreateShader(type): return _lib.glCreateShader(type) _lib.glCullFace.argtypes = ctypes.c_uint, # void = glCullFace(GLenum mode) def glCullFace(mode): _lib.glCullFace(mode) _lib.glDeleteBuffers.argtypes = ctypes.c_int, ctypes.POINTER(ctypes.c_uint), # void = glDeleteBuffers(GLsizei n, GLuint* buffers) def glDeleteBuffer(buffer): n = 1 buffers = (ctypes.c_uint*n)(buffer) res = _lib.glDeleteBuffers(n, buffers) _lib.glDeleteFramebuffers.argtypes = ctypes.c_int, ctypes.POINTER(ctypes.c_uint), # void = glDeleteFramebuffers(GLsizei n, GLuint* framebuffers) def glDeleteFramebuffer(framebuffer): n = 1 framebuffers = (ctypes.c_uint*n)(framebuffer) res = _lib.glDeleteFramebuffers(n, framebuffers) _lib.glDeleteProgram.argtypes = ctypes.c_uint, # void = glDeleteProgram(GLuint program) def glDeleteProgram(program): _lib.glDeleteProgram(program) _lib.glDeleteRenderbuffers.argtypes = ctypes.c_int, ctypes.POINTER(ctypes.c_uint), # void = glDeleteRenderbuffers(GLsizei n, GLuint* renderbuffers) def glDeleteRenderbuffer(renderbuffer): n = 1 renderbuffers = (ctypes.c_uint*n)(renderbuffer) res = _lib.glDeleteRenderbuffers(n, renderbuffers) _lib.glDeleteShader.argtypes = ctypes.c_uint, # void = glDeleteShader(GLuint shader) def glDeleteShader(shader): _lib.glDeleteShader(shader) _lib.glDeleteTextures.argtypes = ctypes.c_int, ctypes.POINTER(ctypes.c_uint), # void = glDeleteTextures(GLsizei n, GLuint* textures) def glDeleteTexture(texture): n = 1 textures = (ctypes.c_uint*n)(texture) res = _lib.glDeleteTextures(n, textures) _lib.glDepthFunc.argtypes = ctypes.c_uint, # void = glDepthFunc(GLenum func) def glDepthFunc(func): _lib.glDepthFunc(func) _lib.glDepthMask.argtypes = ctypes.c_bool, # void = glDepthMask(GLboolean flag) def glDepthMask(flag): _lib.glDepthMask(flag) _lib.glDepthRangef.argtypes = ctypes.c_float, ctypes.c_float, # void = glDepthRangef(GLclampf zNear, GLclampf zFar) def glDepthRange(zNear, zFar): _lib.glDepthRangef(zNear, zFar) _lib.glDetachShader.argtypes = ctypes.c_uint, ctypes.c_uint, # void = glDetachShader(GLuint program, GLuint shader) def glDetachShader(program, shader): _lib.glDetachShader(program, shader) _lib.glDisable.argtypes = ctypes.c_uint, # void = glDisable(GLenum cap) def glDisable(cap): _lib.glDisable(cap) _lib.glDisableVertexAttribArray.argtypes = ctypes.c_uint, # void = glDisableVertexAttribArray(GLuint index) def glDisableVertexAttribArray(index): _lib.glDisableVertexAttribArray(index) _lib.glDrawArrays.argtypes = ctypes.c_uint, ctypes.c_int, ctypes.c_int, # void = glDrawArrays(GLenum mode, GLint first, GLsizei count) def glDrawArrays(mode, first, count): _lib.glDrawArrays(mode, first, count) _lib.glDrawElements.argtypes = ctypes.c_uint, ctypes.c_int, ctypes.c_uint, ctypes.c_void_p, # void = glDrawElements(GLenum mode, GLsizei count, GLenum type, GLvoid* indices) def glDrawElements(mode, count, type, offset): if offset is None: offset = ctypes.c_void_p(0) elif isinstance(offset, ctypes.c_void_p): pass elif isinstance(offset, (int, ctypes.c_int)): offset = ctypes.c_void_p(int(offset)) else: if not offset.flags['C_CONTIGUOUS']: offset = offset.copy('C') offset_ = offset offset = offset.ctypes.data indices = offset res = _lib.glDrawElements(mode, count, type, indices) _lib.glEnable.argtypes = ctypes.c_uint, # void = glEnable(GLenum cap) def glEnable(cap): _lib.glEnable(cap) _lib.glEnableVertexAttribArray.argtypes = ctypes.c_uint, # void = glEnableVertexAttribArray(GLuint index) def glEnableVertexAttribArray(index): _lib.glEnableVertexAttribArray(index) _lib.glFinish.argtypes = () # void = glFinish() def glFinish(): _lib.glFinish() _lib.glFlush.argtypes = () # void = glFlush() def glFlush(): _lib.glFlush() _lib.glFramebufferRenderbuffer.argtypes = ctypes.c_uint, ctypes.c_uint, ctypes.c_uint, ctypes.c_uint, # void = glFramebufferRenderbuffer(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) def glFramebufferRenderbuffer(target, attachment, renderbuffertarget, renderbuffer): _lib.glFramebufferRenderbuffer(target, attachment, renderbuffertarget, renderbuffer) _lib.glFramebufferTexture2D.argtypes = ctypes.c_uint, ctypes.c_uint, ctypes.c_uint, ctypes.c_uint, ctypes.c_int, # void = glFramebufferTexture2D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) def glFramebufferTexture2D(target, attachment, textarget, texture, level): _lib.glFramebufferTexture2D(target, attachment, textarget, texture, level) _lib.glFrontFace.argtypes = ctypes.c_uint, # void = glFrontFace(GLenum mode) def glFrontFace(mode): _lib.glFrontFace(mode) _lib.glGenBuffers.argtypes = ctypes.c_int, ctypes.POINTER(ctypes.c_uint), # void = glGenBuffers(GLsizei n, GLuint* buffers) def glCreateBuffer(): n = 1 buffers = (ctypes.c_uint*n)() res = _lib.glGenBuffers(n, buffers) return buffers[0] _lib.glGenFramebuffers.argtypes = ctypes.c_int, ctypes.POINTER(ctypes.c_uint), # void = glGenFramebuffers(GLsizei n, GLuint* framebuffers) def glCreateFramebuffer(): n = 1 framebuffers = (ctypes.c_uint*n)() res = _lib.glGenFramebuffers(n, framebuffers) return framebuffers[0] _lib.glGenRenderbuffers.argtypes = ctypes.c_int, ctypes.POINTER(ctypes.c_uint), # void = glGenRenderbuffers(GLsizei n, GLuint* renderbuffers) def glCreateRenderbuffer(): n = 1 renderbuffers = (ctypes.c_uint*n)() res = _lib.glGenRenderbuffers(n, renderbuffers) return renderbuffers[0] _lib.glGenTextures.argtypes = ctypes.c_int, ctypes.POINTER(ctypes.c_uint), # void = glGenTextures(GLsizei n, GLuint* textures) def glCreateTexture(): n = 1 textures = (ctypes.c_uint*n)() res = _lib.glGenTextures(n, textures) return textures[0] _lib.glGenerateMipmap.argtypes = ctypes.c_uint, # void = glGenerateMipmap(GLenum target) def glGenerateMipmap(target): _lib.glGenerateMipmap(target) _lib.glGetActiveAttrib.argtypes = ctypes.c_uint, ctypes.c_uint, ctypes.c_int, ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_uint), ctypes.c_char_p, # void = glGetActiveAttrib(GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, GLchar* name) def glGetActiveAttrib(program, index): bufsize = 256 length = (ctypes.c_int*1)() size = (ctypes.c_int*1)() type = (ctypes.c_uint*1)() name = ctypes.create_string_buffer(bufsize) res = _lib.glGetActiveAttrib(program, index, bufsize, length, size, type, name) name = name[:length[0]].decode('utf-8') return name, size[0], type[0] _lib.glGetActiveUniform.argtypes = ctypes.c_uint, ctypes.c_uint, ctypes.c_int, ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_uint), ctypes.c_char_p, # void = glGetActiveUniform(GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, GLchar* name) def glGetActiveUniform(program, index): bufsize = 256 length = (ctypes.c_int*1)() size = (ctypes.c_int*1)() type = (ctypes.c_uint*1)() name = ctypes.create_string_buffer(bufsize) res = _lib.glGetActiveUniform(program, index, bufsize, length, size, type, name) name = name[:length[0]].decode('utf-8') return name, size[0], type[0] _lib.glGetAttachedShaders.argtypes = ctypes.c_uint, ctypes.c_int, ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_uint), # void = glGetAttachedShaders(GLuint program, GLsizei maxcount, GLsizei* count, GLuint* shaders) def glGetAttachedShaders(program): maxcount = 256 count = (ctypes.c_int*1)() shaders = (ctypes.c_uint*maxcount)() res = _lib.glGetAttachedShaders(program, maxcount, count, shaders) return tuple(shaders[:count[0]]) _lib.glGetAttribLocation.argtypes = ctypes.c_uint, ctypes.c_char_p, _lib.glGetAttribLocation.restype = ctypes.c_int # GLint = glGetAttribLocation(GLuint program, GLchar* name) def glGetAttribLocation(program, name): name = ctypes.c_char_p(name.encode('utf-8')) res = _lib.glGetAttribLocation(program, name) return res _lib.glGetBooleanv.argtypes = ctypes.c_uint, ctypes.POINTER(ctypes.c_bool), # void = glGetBooleanv(GLenum pname, GLboolean* params) def _glGetBooleanv(pname): params = (ctypes.c_bool*1)() res = _lib.glGetBooleanv(pname, params) return params[0] _lib.glGetBufferParameteriv.argtypes = ctypes.c_uint, ctypes.c_uint, ctypes.POINTER(ctypes.c_int), # void = glGetBufferParameteriv(GLenum target, GLenum pname, GLint* params) def glGetBufferParameter(target, pname): d = -2**31 # smallest 32bit integer params = (ctypes.c_int*1)(d) res = _lib.glGetBufferParameteriv(target, pname, params) return params[0] _lib.glGetError.argtypes = () _lib.glGetError.restype = ctypes.c_uint # GLenum = glGetError() def glGetError(): return _lib.glGetError() _lib.glGetFloatv.argtypes = ctypes.c_uint, ctypes.POINTER(ctypes.c_float), # void = glGetFloatv(GLenum pname, GLfloat* params) def _glGetFloatv(pname): n = 16 d = float('Inf') params = (ctypes.c_float*n)(*[d for i in range(n)]) res = _lib.glGetFloatv(pname, params) params = [p for p in params if p!=d] if len(params) == 1: return params[0] else: return tuple(params) _lib.glGetFramebufferAttachmentParameteriv.argtypes = ctypes.c_uint, ctypes.c_uint, ctypes.c_uint, ctypes.POINTER(ctypes.c_int), # void = glGetFramebufferAttachmentParameteriv(GLenum target, GLenum attachment, GLenum pname, GLint* params) def glGetFramebufferAttachmentParameter(target, attachment, pname): d = -2**31 # smallest 32bit integer params = (ctypes.c_int*1)(d) res = _lib.glGetFramebufferAttachmentParameteriv(target, attachment, pname, params) return params[0] _lib.glGetIntegerv.argtypes = ctypes.c_uint, ctypes.POINTER(ctypes.c_int), # void = glGetIntegerv(GLenum pname, GLint* params) def _glGetIntegerv(pname): n = 16 d = -2**31 # smallest 32bit integer params = (ctypes.c_int*n)(*[d for i in range(n)]) res = _lib.glGetIntegerv(pname, params) params = [p for p in params if p!=d] if len(params) == 1: return params[0] else: return tuple(params) _lib.glGetProgramInfoLog.argtypes = ctypes.c_uint, ctypes.c_int, ctypes.POINTER(ctypes.c_int), ctypes.c_char_p, # void = glGetProgramInfoLog(GLuint program, GLsizei bufsize, GLsizei* length, GLchar* infolog) def glGetProgramInfoLog(program): bufsize = 1024 length = (ctypes.c_int*1)() infolog = ctypes.create_string_buffer(bufsize) res = _lib.glGetProgramInfoLog(program, bufsize, length, infolog) return infolog[:length[0]].decode('utf-8') _lib.glGetProgramiv.argtypes = ctypes.c_uint, ctypes.c_uint, ctypes.POINTER(ctypes.c_int), # void = glGetProgramiv(GLuint program, GLenum pname, GLint* params) def glGetProgramParameter(program, pname): params = (ctypes.c_int*1)() res = _lib.glGetProgramiv(program, pname, params) return params[0] _lib.glGetRenderbufferParameteriv.argtypes = ctypes.c_uint, ctypes.c_uint, ctypes.POINTER(ctypes.c_int), # void = glGetRenderbufferParameteriv(GLenum target, GLenum pname, GLint* params) def glGetRenderbufferParameter(target, pname): d = -2**31 # smallest 32bit integer params = (ctypes.c_int*1)(d) res = _lib.glGetRenderbufferParameteriv(target, pname, params) return params[0] _lib.glGetShaderInfoLog.argtypes = ctypes.c_uint, ctypes.c_int, ctypes.POINTER(ctypes.c_int), ctypes.c_char_p, # void = glGetShaderInfoLog(GLuint shader, GLsizei bufsize, GLsizei* length, GLchar* infolog) def glGetShaderInfoLog(shader): bufsize = 1024 length = (ctypes.c_int*1)() infolog = ctypes.create_string_buffer(bufsize) res = _lib.glGetShaderInfoLog(shader, bufsize, length, infolog) return infolog[:length[0]].decode('utf-8') _lib.glGetShaderPrecisionFormat.argtypes = ctypes.c_uint, ctypes.c_uint, ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int), # void = glGetShaderPrecisionFormat(GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision) def glGetShaderPrecisionFormat(shadertype, precisiontype): range = (ctypes.c_int*1)() precision = (ctypes.c_int*1)() res = _lib.glGetShaderPrecisionFormat(shadertype, precisiontype, range, precision) return range[0], precision[0] _lib.glGetShaderSource.argtypes = ctypes.c_uint, ctypes.c_int, ctypes.POINTER(ctypes.c_int), ctypes.c_char_p, # void = glGetShaderSource(GLuint shader, GLsizei bufsize, GLsizei* length, GLchar* source) def glGetShaderSource(shader): bufsize = 1024*1024 length = (ctypes.c_int*1)() source = (ctypes.c_char*bufsize)() res = _lib.glGetShaderSource(shader, bufsize, length, source) return source.value[:length[0]].decode('utf-8') _lib.glGetShaderiv.argtypes = ctypes.c_uint, ctypes.c_uint, ctypes.POINTER(ctypes.c_int), # void = glGetShaderiv(GLuint shader, GLenum pname, GLint* params) def glGetShaderParameter(shader, pname): params = (ctypes.c_int*1)() res = _lib.glGetShaderiv(shader, pname, params) return params[0] _lib.glGetString.argtypes = ctypes.c_uint, _lib.glGetString.restype = ctypes.c_char_p # GLubyte* = glGetString(GLenum name) def glGetParameter(pname): if pname in [33902, 33901, 32773, 3106, 2931, 2928, 2849, 32824, 10752, 32938]: # GL_ALIASED_LINE_WIDTH_RANGE GL_ALIASED_POINT_SIZE_RANGE # GL_BLEND_COLOR GL_COLOR_CLEAR_VALUE GL_DEPTH_CLEAR_VALUE # GL_DEPTH_RANGE GL_LINE_WIDTH GL_POLYGON_OFFSET_FACTOR # GL_POLYGON_OFFSET_UNITS GL_SAMPLE_COVERAGE_VALUE return _glGetFloatv(pname) elif pname in [7936, 7937, 7938, 35724, 7939]: # GL_VENDOR, GL_RENDERER, GL_VERSION, GL_SHADING_LANGUAGE_VERSION, # GL_EXTENSIONS are strings pass # string handled below else: return _glGetIntegerv(pname) name = pname res = _lib.glGetString(name) return ctypes.string_at(res).decode('utf-8') if res else '' _lib.glGetTexParameterfv.argtypes = ctypes.c_uint, ctypes.c_uint, ctypes.POINTER(ctypes.c_float), # void = glGetTexParameterfv(GLenum target, GLenum pname, GLfloat* params) def glGetTexParameter(target, pname): d = float('Inf') params = (ctypes.c_float*1)(d) res = _lib.glGetTexParameterfv(target, pname, params) return params[0] _lib.glGetUniformfv.argtypes = ctypes.c_uint, ctypes.c_int, ctypes.POINTER(ctypes.c_float), # void = glGetUniformfv(GLuint program, GLint location, GLfloat* params) def glGetUniform(program, location): n = 16 d = float('Inf') params = (ctypes.c_float*n)(*[d for i in range(n)]) res = _lib.glGetUniformfv(program, location, params) params = [p for p in params if p!=d] if len(params) == 1: return params[0] else: return tuple(params) _lib.glGetUniformLocation.argtypes = ctypes.c_uint, ctypes.c_char_p, _lib.glGetUniformLocation.restype = ctypes.c_int # GLint = glGetUniformLocation(GLuint program, GLchar* name) def glGetUniformLocation(program, name): name = ctypes.c_char_p(name.encode('utf-8')) res = _lib.glGetUniformLocation(program, name) return res _lib.glGetVertexAttribfv.argtypes = ctypes.c_uint, ctypes.c_uint, ctypes.POINTER(ctypes.c_float), # void = glGetVertexAttribfv(GLuint index, GLenum pname, GLfloat* params) def glGetVertexAttrib(index, pname): n = 4 d = float('Inf') params = (ctypes.c_float*n)(*[d for i in range(n)]) res = _lib.glGetVertexAttribfv(index, pname, params) params = [p for p in params if p!=d] if len(params) == 1: return params[0] else: return tuple(params) _lib.glGetVertexAttribPointerv.argtypes = ctypes.c_uint, ctypes.c_uint, ctypes.POINTER(ctypes.c_void_p), # void = glGetVertexAttribPointerv(GLuint index, GLenum pname, GLvoid** pointer) def glGetVertexAttribOffset(index, pname): pointer = (ctypes.c_void_p*1)() res = _lib.glGetVertexAttribPointerv(index, pname, pointer) return pointer[0] or 0 _lib.glHint.argtypes = ctypes.c_uint, ctypes.c_uint, # void = glHint(GLenum target, GLenum mode) def glHint(target, mode): _lib.glHint(target, mode) _lib.glIsBuffer.argtypes = ctypes.c_uint, _lib.glIsBuffer.restype = ctypes.c_bool # GLboolean = glIsBuffer(GLuint buffer) def glIsBuffer(buffer): return _lib.glIsBuffer(buffer) _lib.glIsEnabled.argtypes = ctypes.c_uint, _lib.glIsEnabled.restype = ctypes.c_bool # GLboolean = glIsEnabled(GLenum cap) def glIsEnabled(cap): return _lib.glIsEnabled(cap) _lib.glIsFramebuffer.argtypes = ctypes.c_uint, _lib.glIsFramebuffer.restype = ctypes.c_bool # GLboolean = glIsFramebuffer(GLuint framebuffer) def glIsFramebuffer(framebuffer): return _lib.glIsFramebuffer(framebuffer) _lib.glIsProgram.argtypes = ctypes.c_uint, _lib.glIsProgram.restype = ctypes.c_bool # GLboolean = glIsProgram(GLuint program) def glIsProgram(program): return _lib.glIsProgram(program) _lib.glIsRenderbuffer.argtypes = ctypes.c_uint, _lib.glIsRenderbuffer.restype = ctypes.c_bool # GLboolean = glIsRenderbuffer(GLuint renderbuffer) def glIsRenderbuffer(renderbuffer): return _lib.glIsRenderbuffer(renderbuffer) _lib.glIsShader.argtypes = ctypes.c_uint, _lib.glIsShader.restype = ctypes.c_bool # GLboolean = glIsShader(GLuint shader) def glIsShader(shader): return _lib.glIsShader(shader) _lib.glIsTexture.argtypes = ctypes.c_uint, _lib.glIsTexture.restype = ctypes.c_bool # GLboolean = glIsTexture(GLuint texture) def glIsTexture(texture): return _lib.glIsTexture(texture) _lib.glLineWidth.argtypes = ctypes.c_float, # void = glLineWidth(GLfloat width) def glLineWidth(width): _lib.glLineWidth(width) _lib.glLinkProgram.argtypes = ctypes.c_uint, # void = glLinkProgram(GLuint program) def glLinkProgram(program): _lib.glLinkProgram(program) _lib.glPixelStorei.argtypes = ctypes.c_uint, ctypes.c_int, # void = glPixelStorei(GLenum pname, GLint param) def glPixelStorei(pname, param): _lib.glPixelStorei(pname, param) _lib.glPolygonOffset.argtypes = ctypes.c_float, ctypes.c_float, # void = glPolygonOffset(GLfloat factor, GLfloat units) def glPolygonOffset(factor, units): _lib.glPolygonOffset(factor, units) _lib.glReadPixels.argtypes = ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_uint, ctypes.c_uint, ctypes.c_void_p, # void = glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid* pixels) def glReadPixels(x, y, width, height, format, type): # GL_ALPHA, GL_RGB, GL_RGBA t = {6406:1, 6407:3, 6408:4}[format] # GL_UNSIGNED_BYTE, GL_FLOAT nb = {5121:1, 5126:4}[type] size = int(width*height*t*nb) pixels = ctypes.create_string_buffer(size) res = _lib.glReadPixels(x, y, width, height, format, type, pixels) return pixels[:] _lib.glRenderbufferStorage.argtypes = ctypes.c_uint, ctypes.c_uint, ctypes.c_int, ctypes.c_int, # void = glRenderbufferStorage(GLenum target, GLenum internalformat, GLsizei width, GLsizei height) def glRenderbufferStorage(target, internalformat, width, height): _lib.glRenderbufferStorage(target, internalformat, width, height) _lib.glSampleCoverage.argtypes = ctypes.c_float, ctypes.c_bool, # void = glSampleCoverage(GLclampf value, GLboolean invert) def glSampleCoverage(value, invert): _lib.glSampleCoverage(value, invert) _lib.glScissor.argtypes = ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int, # void = glScissor(GLint x, GLint y, GLsizei width, GLsizei height) def glScissor(x, y, width, height): _lib.glScissor(x, y, width, height) _lib.glShaderSource.argtypes = ctypes.c_uint, ctypes.c_int, ctypes.POINTER(ctypes.c_char_p), ctypes.POINTER(ctypes.c_int), # void = glShaderSource(GLuint shader, GLsizei count, GLchar** string, GLint* length) def glShaderSource(shader, source): # Some implementation do not like getting a list of single chars if isinstance(source, (tuple, list)): strings = [s for s in source] else: strings = [source] count = len(strings) string = (ctypes.c_char_p*count)(*[s.encode('utf-8') for s in strings]) length = (ctypes.c_int*count)(*[len(s) for s in strings]) res = _lib.glShaderSource(shader, count, string, length) _lib.glStencilFunc.argtypes = ctypes.c_uint, ctypes.c_int, ctypes.c_uint, # void = glStencilFunc(GLenum func, GLint ref, GLuint mask) def glStencilFunc(func, ref, mask): _lib.glStencilFunc(func, ref, mask) _lib.glStencilFuncSeparate.argtypes = ctypes.c_uint, ctypes.c_uint, ctypes.c_int, ctypes.c_uint, # void = glStencilFuncSeparate(GLenum face, GLenum func, GLint ref, GLuint mask) def glStencilFuncSeparate(face, func, ref, mask): _lib.glStencilFuncSeparate(face, func, ref, mask) _lib.glStencilMask.argtypes = ctypes.c_uint, # void = glStencilMask(GLuint mask) def glStencilMask(mask): _lib.glStencilMask(mask) _lib.glStencilMaskSeparate.argtypes = ctypes.c_uint, ctypes.c_uint, # void = glStencilMaskSeparate(GLenum face, GLuint mask) def glStencilMaskSeparate(face, mask): _lib.glStencilMaskSeparate(face, mask) _lib.glStencilOp.argtypes = ctypes.c_uint, ctypes.c_uint, ctypes.c_uint, # void = glStencilOp(GLenum fail, GLenum zfail, GLenum zpass) def glStencilOp(fail, zfail, zpass): _lib.glStencilOp(fail, zfail, zpass) _lib.glStencilOpSeparate.argtypes = ctypes.c_uint, ctypes.c_uint, ctypes.c_uint, ctypes.c_uint, # void = glStencilOpSeparate(GLenum face, GLenum fail, GLenum zfail, GLenum zpass) def glStencilOpSeparate(face, fail, zfail, zpass): _lib.glStencilOpSeparate(face, fail, zfail, zpass) _lib.glTexImage2D.argtypes = ctypes.c_uint, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_uint, ctypes.c_uint, ctypes.c_void_p, # void = glTexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, GLvoid* pixels) def glTexImage2D(target, level, internalformat, format, type, pixels): border = 0 if isinstance(pixels, (tuple, list)): height, width = pixels pixels = ctypes.c_void_p(0) pixels = None else: if not pixels.flags['C_CONTIGUOUS']: pixels = pixels.copy('C') pixels_ = pixels pixels = pixels_.ctypes.data height, width = pixels_.shape[:2] res = _lib.glTexImage2D(target, level, internalformat, width, height, border, format, type, pixels) _lib.glTexParameterf.argtypes = ctypes.c_uint, ctypes.c_uint, ctypes.c_float, def glTexParameterf(target, pname, param): _lib.glTexParameterf(target, pname, param) _lib.glTexParameteri.argtypes = ctypes.c_uint, ctypes.c_uint, ctypes.c_int, def glTexParameteri(target, pname, param): _lib.glTexParameteri(target, pname, param) _lib.glTexSubImage2D.argtypes = ctypes.c_uint, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_uint, ctypes.c_uint, ctypes.c_void_p, # void = glTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid* pixels) def glTexSubImage2D(target, level, xoffset, yoffset, format, type, pixels): if not pixels.flags['C_CONTIGUOUS']: pixels = pixels.copy('C') pixels_ = pixels pixels = pixels_.ctypes.data height, width = pixels_.shape[:2] res = _lib.glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels) _lib.glUniform1f.argtypes = ctypes.c_int, ctypes.c_float, def glUniform1f(location, v1): _lib.glUniform1f(location, v1) _lib.glUniform2f.argtypes = ctypes.c_int, ctypes.c_float, ctypes.c_float, def glUniform2f(location, v1, v2): _lib.glUniform2f(location, v1, v2) _lib.glUniform3f.argtypes = ctypes.c_int, ctypes.c_float, ctypes.c_float, ctypes.c_float, def glUniform3f(location, v1, v2, v3): _lib.glUniform3f(location, v1, v2, v3) _lib.glUniform4f.argtypes = ctypes.c_int, ctypes.c_float, ctypes.c_float, ctypes.c_float, ctypes.c_float, def glUniform4f(location, v1, v2, v3, v4): _lib.glUniform4f(location, v1, v2, v3, v4) _lib.glUniform1i.argtypes = ctypes.c_int, ctypes.c_int, def glUniform1i(location, v1): _lib.glUniform1i(location, v1) _lib.glUniform2i.argtypes = ctypes.c_int, ctypes.c_int, ctypes.c_int, def glUniform2i(location, v1, v2): _lib.glUniform2i(location, v1, v2) _lib.glUniform3i.argtypes = ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int, def glUniform3i(location, v1, v2, v3): _lib.glUniform3i(location, v1, v2, v3) _lib.glUniform4i.argtypes = ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int, def glUniform4i(location, v1, v2, v3, v4): _lib.glUniform4i(location, v1, v2, v3, v4) _lib.glUniform1fv.argtypes = ctypes.c_int, ctypes.c_int, ctypes.POINTER(ctypes.c_float), def glUniform1fv(location, count, values): values = [float(val) for val in values] values = (ctypes.c_float*len(values))(*values) _lib.glUniform1fv(location, count, values) _lib.glUniform2fv.argtypes = ctypes.c_int, ctypes.c_int, ctypes.POINTER(ctypes.c_float), def glUniform2fv(location, count, values): values = [float(val) for val in values] values = (ctypes.c_float*len(values))(*values) _lib.glUniform2fv(location, count, values) _lib.glUniform3fv.argtypes = ctypes.c_int, ctypes.c_int, ctypes.POINTER(ctypes.c_float), def glUniform3fv(location, count, values): values = [float(val) for val in values] values = (ctypes.c_float*len(values))(*values) _lib.glUniform3fv(location, count, values) _lib.glUniform4fv.argtypes = ctypes.c_int, ctypes.c_int, ctypes.POINTER(ctypes.c_float), def glUniform4fv(location, count, values): values = [float(val) for val in values] values = (ctypes.c_float*len(values))(*values) _lib.glUniform4fv(location, count, values) _lib.glUniform1iv.argtypes = ctypes.c_int, ctypes.c_int, ctypes.POINTER(ctypes.c_int), def glUniform1iv(location, count, values): values = [int(val) for val in values] values = (ctypes.c_int*len(values))(*values) _lib.glUniform1iv(location, count, values) _lib.glUniform2iv.argtypes = ctypes.c_int, ctypes.c_int, ctypes.POINTER(ctypes.c_int), def glUniform2iv(location, count, values): values = [int(val) for val in values] values = (ctypes.c_int*len(values))(*values) _lib.glUniform2iv(location, count, values) _lib.glUniform3iv.argtypes = ctypes.c_int, ctypes.c_int, ctypes.POINTER(ctypes.c_int), def glUniform3iv(location, count, values): values = [int(val) for val in values] values = (ctypes.c_int*len(values))(*values) _lib.glUniform3iv(location, count, values) _lib.glUniform4iv.argtypes = ctypes.c_int, ctypes.c_int, ctypes.POINTER(ctypes.c_int), def glUniform4iv(location, count, values): values = [int(val) for val in values] values = (ctypes.c_int*len(values))(*values) _lib.glUniform4iv(location, count, values) _lib.glUniformMatrix2fv.argtypes = ctypes.c_int, ctypes.c_int, ctypes.c_bool, ctypes.POINTER(ctypes.c_float), def glUniformMatrix2fv(location, count, transpose, values): if not values.flags["C_CONTIGUOUS"]: values = values.copy() assert values.dtype.name == "float32" values_ = values values = values_.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) _lib.glUniformMatrix2fv(location, count, transpose, values) _lib.glUniformMatrix3fv.argtypes = ctypes.c_int, ctypes.c_int, ctypes.c_bool, ctypes.POINTER(ctypes.c_float), def glUniformMatrix3fv(location, count, transpose, values): if not values.flags["C_CONTIGUOUS"]: values = values.copy() assert values.dtype.name == "float32" values_ = values values = values_.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) _lib.glUniformMatrix3fv(location, count, transpose, values) _lib.glUniformMatrix4fv.argtypes = ctypes.c_int, ctypes.c_int, ctypes.c_bool, ctypes.POINTER(ctypes.c_float), def glUniformMatrix4fv(location, count, transpose, values): if not values.flags["C_CONTIGUOUS"]: values = values.copy() assert values.dtype.name == "float32" values_ = values values = values_.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) _lib.glUniformMatrix4fv(location, count, transpose, values) _lib.glUseProgram.argtypes = ctypes.c_uint, # void = glUseProgram(GLuint program) def glUseProgram(program): _lib.glUseProgram(program) _lib.glValidateProgram.argtypes = ctypes.c_uint, # void = glValidateProgram(GLuint program) def glValidateProgram(program): _lib.glValidateProgram(program) _lib.glVertexAttrib1f.argtypes = ctypes.c_uint, ctypes.c_float, def glVertexAttrib1f(index, v1): _lib.glVertexAttrib1f(index, v1) _lib.glVertexAttrib2f.argtypes = ctypes.c_uint, ctypes.c_float, ctypes.c_float, def glVertexAttrib2f(index, v1, v2): _lib.glVertexAttrib2f(index, v1, v2) _lib.glVertexAttrib3f.argtypes = ctypes.c_uint, ctypes.c_float, ctypes.c_float, ctypes.c_float, def glVertexAttrib3f(index, v1, v2, v3): _lib.glVertexAttrib3f(index, v1, v2, v3) _lib.glVertexAttrib4f.argtypes = ctypes.c_uint, ctypes.c_float, ctypes.c_float, ctypes.c_float, ctypes.c_float, def glVertexAttrib4f(index, v1, v2, v3, v4): _lib.glVertexAttrib4f(index, v1, v2, v3, v4) _lib.glVertexAttribPointer.argtypes = ctypes.c_uint, ctypes.c_int, ctypes.c_uint, ctypes.c_bool, ctypes.c_int, ctypes.c_void_p, # void = glVertexAttribPointer(GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, GLvoid* ptr) def glVertexAttribPointer(indx, size, type, normalized, stride, offset): if offset is None: offset = ctypes.c_void_p(0) elif isinstance(offset, ctypes.c_void_p): pass elif isinstance(offset, (int, ctypes.c_int)): offset = ctypes.c_void_p(int(offset)) else: if not offset.flags['C_CONTIGUOUS']: offset = offset.copy('C') offset_ = offset offset = offset.ctypes.data # We need to ensure that the data exists at draw time :( # PyOpenGL does this too key = '_vert_attr_'+str(indx) setattr(glVertexAttribPointer, key, offset_) ptr = offset res = _lib.glVertexAttribPointer(indx, size, type, normalized, stride, ptr) _lib.glViewport.argtypes = ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int, # void = glViewport(GLint x, GLint y, GLsizei width, GLsizei height) def glViewport(x, y, width, height): _lib.glViewport(x, y, width, height) ����������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/gloo/gl/_proxy.py�����������������������������������������������������������������0000664�0001750�0001750�00000034464�12426461252�021010� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������""" THIS CODE IS AUTO-GENERATED. DO NOT EDIT. Base proxy API for GL ES 2.0. """ class BaseGLProxy(object): """ Base proxy class for the GL ES 2.0 API. Subclasses should implement __call__ to process the API calls. """ def __call__(self, funcname, returns, *args): raise NotImplementedError() def glActiveTexture(self, texture): self("glActiveTexture", False, texture) def glAttachShader(self, program, shader): self("glAttachShader", False, program, shader) def glBindAttribLocation(self, program, index, name): self("glBindAttribLocation", False, program, index, name) def glBindBuffer(self, target, buffer): self("glBindBuffer", False, target, buffer) def glBindFramebuffer(self, target, framebuffer): self("glBindFramebuffer", False, target, framebuffer) def glBindRenderbuffer(self, target, renderbuffer): self("glBindRenderbuffer", False, target, renderbuffer) def glBindTexture(self, target, texture): self("glBindTexture", False, target, texture) def glBlendColor(self, red, green, blue, alpha): self("glBlendColor", False, red, green, blue, alpha) def glBlendEquation(self, mode): self("glBlendEquation", False, mode) def glBlendEquationSeparate(self, modeRGB, modeAlpha): self("glBlendEquationSeparate", False, modeRGB, modeAlpha) def glBlendFunc(self, sfactor, dfactor): self("glBlendFunc", False, sfactor, dfactor) def glBlendFuncSeparate(self, srcRGB, dstRGB, srcAlpha, dstAlpha): self("glBlendFuncSeparate", False, srcRGB, dstRGB, srcAlpha, dstAlpha) def glBufferData(self, target, data, usage): self("glBufferData", False, target, data, usage) def glBufferSubData(self, target, offset, data): self("glBufferSubData", False, target, offset, data) def glCheckFramebufferStatus(self, target): return self("glCheckFramebufferStatus", True, target) def glClear(self, mask): self("glClear", False, mask) def glClearColor(self, red, green, blue, alpha): self("glClearColor", False, red, green, blue, alpha) def glClearDepth(self, depth): self("glClearDepth", False, depth) def glClearStencil(self, s): self("glClearStencil", False, s) def glColorMask(self, red, green, blue, alpha): self("glColorMask", False, red, green, blue, alpha) def glCompileShader(self, shader): self("glCompileShader", False, shader) def glCompressedTexImage2D(self, target, level, internalformat, width, height, border, data): self("glCompressedTexImage2D", False, target, level, internalformat, width, height, border, data) def glCompressedTexSubImage2D(self, target, level, xoffset, yoffset, width, height, format, data): self("glCompressedTexSubImage2D", False, target, level, xoffset, yoffset, width, height, format, data) def glCopyTexImage2D(self, target, level, internalformat, x, y, width, height, border): self("glCopyTexImage2D", False, target, level, internalformat, x, y, width, height, border) def glCopyTexSubImage2D(self, target, level, xoffset, yoffset, x, y, width, height): self("glCopyTexSubImage2D", False, target, level, xoffset, yoffset, x, y, width, height) def glCreateProgram(self, ): return self("glCreateProgram", True, ) def glCreateShader(self, type): return self("glCreateShader", True, type) def glCullFace(self, mode): self("glCullFace", False, mode) def glDeleteBuffer(self, buffer): self("glDeleteBuffer", False, buffer) def glDeleteFramebuffer(self, framebuffer): self("glDeleteFramebuffer", False, framebuffer) def glDeleteProgram(self, program): self("glDeleteProgram", False, program) def glDeleteRenderbuffer(self, renderbuffer): self("glDeleteRenderbuffer", False, renderbuffer) def glDeleteShader(self, shader): self("glDeleteShader", False, shader) def glDeleteTexture(self, texture): self("glDeleteTexture", False, texture) def glDepthFunc(self, func): self("glDepthFunc", False, func) def glDepthMask(self, flag): self("glDepthMask", False, flag) def glDepthRange(self, zNear, zFar): self("glDepthRange", False, zNear, zFar) def glDetachShader(self, program, shader): self("glDetachShader", False, program, shader) def glDisable(self, cap): self("glDisable", False, cap) def glDisableVertexAttribArray(self, index): self("glDisableVertexAttribArray", False, index) def glDrawArrays(self, mode, first, count): self("glDrawArrays", False, mode, first, count) def glDrawElements(self, mode, count, type, offset): self("glDrawElements", False, mode, count, type, offset) def glEnable(self, cap): self("glEnable", False, cap) def glEnableVertexAttribArray(self, index): self("glEnableVertexAttribArray", False, index) def glFinish(self, ): self("glFinish", False, ) def glFlush(self, ): self("glFlush", False, ) def glFramebufferRenderbuffer(self, target, attachment, renderbuffertarget, renderbuffer): self("glFramebufferRenderbuffer", False, target, attachment, renderbuffertarget, renderbuffer) def glFramebufferTexture2D(self, target, attachment, textarget, texture, level): self("glFramebufferTexture2D", False, target, attachment, textarget, texture, level) def glFrontFace(self, mode): self("glFrontFace", False, mode) def glCreateBuffer(self, ): return self("glCreateBuffer", True, ) def glCreateFramebuffer(self, ): return self("glCreateFramebuffer", True, ) def glCreateRenderbuffer(self, ): return self("glCreateRenderbuffer", True, ) def glCreateTexture(self, ): return self("glCreateTexture", True, ) def glGenerateMipmap(self, target): self("glGenerateMipmap", False, target) def glGetActiveAttrib(self, program, index): return self("glGetActiveAttrib", True, program, index) def glGetActiveUniform(self, program, index): return self("glGetActiveUniform", True, program, index) def glGetAttachedShaders(self, program): return self("glGetAttachedShaders", True, program) def glGetAttribLocation(self, program, name): return self("glGetAttribLocation", True, program, name) def _glGetBooleanv(self, pname): self("_glGetBooleanv", False, pname) def glGetBufferParameter(self, target, pname): return self("glGetBufferParameter", True, target, pname) def glGetError(self, ): return self("glGetError", True, ) def _glGetFloatv(self, pname): self("_glGetFloatv", False, pname) def glGetFramebufferAttachmentParameter(self, target, attachment, pname): return self("glGetFramebufferAttachmentParameter", True, target, attachment, pname) def _glGetIntegerv(self, pname): self("_glGetIntegerv", False, pname) def glGetProgramInfoLog(self, program): return self("glGetProgramInfoLog", True, program) def glGetProgramParameter(self, program, pname): return self("glGetProgramParameter", True, program, pname) def glGetRenderbufferParameter(self, target, pname): return self("glGetRenderbufferParameter", True, target, pname) def glGetShaderInfoLog(self, shader): return self("glGetShaderInfoLog", True, shader) def glGetShaderPrecisionFormat(self, shadertype, precisiontype): return self("glGetShaderPrecisionFormat", True, shadertype, precisiontype) def glGetShaderSource(self, shader): return self("glGetShaderSource", True, shader) def glGetShaderParameter(self, shader, pname): return self("glGetShaderParameter", True, shader, pname) def glGetParameter(self, pname): return self("glGetParameter", True, pname) def glGetTexParameter(self, target, pname): return self("glGetTexParameter", True, target, pname) def glGetUniform(self, program, location): return self("glGetUniform", True, program, location) def glGetUniformLocation(self, program, name): return self("glGetUniformLocation", True, program, name) def glGetVertexAttrib(self, index, pname): return self("glGetVertexAttrib", True, index, pname) def glGetVertexAttribOffset(self, index, pname): return self("glGetVertexAttribOffset", True, index, pname) def glHint(self, target, mode): self("glHint", False, target, mode) def glIsBuffer(self, buffer): return self("glIsBuffer", True, buffer) def glIsEnabled(self, cap): return self("glIsEnabled", True, cap) def glIsFramebuffer(self, framebuffer): return self("glIsFramebuffer", True, framebuffer) def glIsProgram(self, program): return self("glIsProgram", True, program) def glIsRenderbuffer(self, renderbuffer): return self("glIsRenderbuffer", True, renderbuffer) def glIsShader(self, shader): return self("glIsShader", True, shader) def glIsTexture(self, texture): return self("glIsTexture", True, texture) def glLineWidth(self, width): self("glLineWidth", False, width) def glLinkProgram(self, program): self("glLinkProgram", False, program) def glPixelStorei(self, pname, param): self("glPixelStorei", False, pname, param) def glPolygonOffset(self, factor, units): self("glPolygonOffset", False, factor, units) def glReadPixels(self, x, y, width, height, format, type): return self("glReadPixels", True, x, y, width, height, format, type) def glRenderbufferStorage(self, target, internalformat, width, height): self("glRenderbufferStorage", False, target, internalformat, width, height) def glSampleCoverage(self, value, invert): self("glSampleCoverage", False, value, invert) def glScissor(self, x, y, width, height): self("glScissor", False, x, y, width, height) def glShaderSource(self, shader, source): self("glShaderSource", False, shader, source) def glStencilFunc(self, func, ref, mask): self("glStencilFunc", False, func, ref, mask) def glStencilFuncSeparate(self, face, func, ref, mask): self("glStencilFuncSeparate", False, face, func, ref, mask) def glStencilMask(self, mask): self("glStencilMask", False, mask) def glStencilMaskSeparate(self, face, mask): self("glStencilMaskSeparate", False, face, mask) def glStencilOp(self, fail, zfail, zpass): self("glStencilOp", False, fail, zfail, zpass) def glStencilOpSeparate(self, face, fail, zfail, zpass): self("glStencilOpSeparate", False, face, fail, zfail, zpass) def glTexImage2D(self, target, level, internalformat, format, type, pixels): self("glTexImage2D", False, target, level, internalformat, format, type, pixels) def glTexParameterf(self, target, pname, param): self("glTexParameterf", False, target, pname, param) def glTexParameteri(self, target, pname, param): self("glTexParameteri", False, target, pname, param) def glTexSubImage2D(self, target, level, xoffset, yoffset, format, type, pixels): self("glTexSubImage2D", False, target, level, xoffset, yoffset, format, type, pixels) def glUniform1f(self, location, v1): self("glUniform1f", False, location, v1) def glUniform2f(self, location, v1, v2): self("glUniform2f", False, location, v1, v2) def glUniform3f(self, location, v1, v2, v3): self("glUniform3f", False, location, v1, v2, v3) def glUniform4f(self, location, v1, v2, v3, v4): self("glUniform4f", False, location, v1, v2, v3, v4) def glUniform1i(self, location, v1): self("glUniform1i", False, location, v1) def glUniform2i(self, location, v1, v2): self("glUniform2i", False, location, v1, v2) def glUniform3i(self, location, v1, v2, v3): self("glUniform3i", False, location, v1, v2, v3) def glUniform4i(self, location, v1, v2, v3, v4): self("glUniform4i", False, location, v1, v2, v3, v4) def glUniform1fv(self, location, count, values): self("glUniform1fv", False, location, count, values) def glUniform2fv(self, location, count, values): self("glUniform2fv", False, location, count, values) def glUniform3fv(self, location, count, values): self("glUniform3fv", False, location, count, values) def glUniform4fv(self, location, count, values): self("glUniform4fv", False, location, count, values) def glUniform1iv(self, location, count, values): self("glUniform1iv", False, location, count, values) def glUniform2iv(self, location, count, values): self("glUniform2iv", False, location, count, values) def glUniform3iv(self, location, count, values): self("glUniform3iv", False, location, count, values) def glUniform4iv(self, location, count, values): self("glUniform4iv", False, location, count, values) def glUniformMatrix2fv(self, location, count, transpose, values): self("glUniformMatrix2fv", False, location, count, transpose, values) def glUniformMatrix3fv(self, location, count, transpose, values): self("glUniformMatrix3fv", False, location, count, transpose, values) def glUniformMatrix4fv(self, location, count, transpose, values): self("glUniformMatrix4fv", False, location, count, transpose, values) def glUseProgram(self, program): self("glUseProgram", False, program) def glValidateProgram(self, program): self("glValidateProgram", False, program) def glVertexAttrib1f(self, index, v1): self("glVertexAttrib1f", False, index, v1) def glVertexAttrib2f(self, index, v1, v2): self("glVertexAttrib2f", False, index, v1, v2) def glVertexAttrib3f(self, index, v1, v2, v3): self("glVertexAttrib3f", False, index, v1, v2, v3) def glVertexAttrib4f(self, index, v1, v2, v3, v4): self("glVertexAttrib4f", False, index, v1, v2, v3, v4) def glVertexAttribPointer(self, indx, size, type, normalized, stride, offset): self("glVertexAttribPointer", False, indx, size, type, normalized, stride, offset) def glViewport(self, x, y, width, height): self("glViewport", False, x, y, width, height) ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/gloo/gl/es2.py��������������������������������������������������������������������0000664�0001750�0001750�00000003322�12527672621�020154� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ GL ES 2.0 API. On Windows implemented via Angle (i.e translated to DirectX). """ import sys import os import ctypes from . import _copy_gl_functions from ._constants import * # noqa ## Ctypes stuff if hasattr(ctypes, 'TEST_DLL'): # Load dummy lib _lib = ctypes.TEST_DLL.LoadLibrary('') elif sys.platform.startswith('win'): raise RuntimeError('ES 2.0 is not available on Windows yet') # todo: were are we going to put our libs? dirname = r'C:\Users\Almar\AppData\Local\Chromium\Application\34.0.1790.0' # Load dependency (so that libGLESv2 can find it fname = dirname + r'\d3dcompiler_46.dll' _libdum = ctypes.windll.LoadLibrary(fname) # Load GL ES 2.0 lib (Angle) fname = dirname + r'\libGLESv2.dll' _lib = ctypes.windll.LoadLibrary(fname) elif sys.platform.startswith('linux'): es2_file = None # Load from env if 'ES2_LIBRARY' in os.environ: # todo: is this the correct name? if os.path.exists(os.environ['ES2_LIBRARY']): es2_file = os.path.realpath(os.environ['ES2_LIBRARY']) # Else, try to find it if es2_file is None: es2_file = ctypes.util.find_library('GLESv2') # Else, we failed and exit if es2_file is None: raise OSError('GL ES 2.0 library not found') # Load it _lib = ctypes.CDLL(es2_file) elif sys.platform.startswith('darwin'): raise RuntimeError('ES 2.0 is not available on OSX yet') else: raise RuntimeError('Unknown platform: %s' % sys.platform) ## Inject from . import _es2 # noqa _copy_gl_functions(_es2, globals()) ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/gloo/gl/dummy.py������������������������������������������������������������������0000664�0001750�0001750�00000001331�12527672621�020614� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ A dummy backend. """ from . import BaseGLProxy, _copy_gl_functions from ._constants import * # noqa class DummyProxy(BaseGLProxy): """ A dummy backend that can be activated when the GL is not processed in this process. Each GL function call will raise an error. """ def __call__(self, funcname, returns, *args): raise RuntimeError('Cannot call %r (or any other GL function), ' 'since GL is disabled.' % funcname) # Instantiate proxy and inject functions _proxy = DummyProxy() _copy_gl_functions(_proxy, globals()) �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/gloo/gl/__init__.py���������������������������������������������������������������0000664�0001750�0001750�00000015716�12527672621�021234� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ This module provides a (functional) API to OpenGL ES 2.0. There are multiple backend implementations of this API, available as submodules of this module. One can use one of the backends directly, or call `gl.use_gl()` to select one. The backend system allow running visualizations using Angle, WebGL, or other forms of remote rendering. This is in part possible by the widespread availability of OpenGL ES 2.0. All functions that this API provides accept and return Python arguments (no ctypes is required); strings are real strings and you can pass data as numpy arrays. In general the input arguments are not checked (for performance reasons). Each function results in exactly one OpenGL API call, except when using the pyopengl backend. The functions do not have docstrings, but most IDE's should provide you with the function signature. For more documentation see http://www.khronos.org/opengles/sdk/docs/man/ """ # NOTE: modules in this package that start with one underscore are # autogenerated, and should not be edited. from __future__ import division from ...util import config, logger from ._constants import * # noqa from ._proxy import BaseGLProxy # Variable that will hold the module corresponding to the current backend # This variable is used in our proxy classes to call the right functions. current_backend = None class MainProxy(BaseGLProxy): """ Main proxy for the GL ES 2.0 API. The functions in this namespace always call into the correct GL backend. Therefore these function objects can be safely stored for reuse. However, for efficienty it would probably be better to store the function name and then do ``func = getattr(gloo.gl, funcname)``. """ def __call__(self, funcname, returns, *args): func = getattr(current_backend, funcname) return func(*args) class DebugProxy(BaseGLProxy): """ Proxy for debug version of the GL ES 2.0 API. This proxy calls the functions of the currently selected backend. In addition it logs debug information, and it runs check_error() on each API call. Intended for internal use. """ def _arg_repr(self, arg): """ Get a useful (and not too large) represetation of an argument. """ r = repr(arg) max = 40 if len(r) > max: if hasattr(arg, 'shape'): r = 'array:' + 'x'.join([repr(s) for s in arg.shape]) else: r = r[:max-3] + '...' return r def __call__(self, funcname, returns, *args): # Avoid recursion for glGetError if funcname == 'glGetError': func = getattr(current_backend, funcname) return func() # Log function call argstr = ', '.join(map(self._arg_repr, args)) logger.debug("%s(%s)" % (funcname, argstr)) # Call function func = getattr(current_backend, funcname) ret = func(*args) # Log return value if returns: logger.debug(" <= %s" % repr(ret)) # Check for errors (raises if an error occured) check_error(funcname) # Return return ret # Instantiate proxy objects proxy = MainProxy() _debug_proxy = DebugProxy() def use_gl(target='gl2'): """ Let Vispy use the target OpenGL ES 2.0 implementation Also see ``vispy.use()``. Parameters ---------- target : str The target GL backend to use. Available backends: * gl2 - Use ES 2.0 subset of desktop (i.e. normal) OpenGL * gl+ - Use the desktop ES 2.0 subset plus all non-deprecated GL functions on your system (requires PyOpenGL) * es2 - Use the ES2 library (Angle/DirectX on Windows) * pyopengl2 - Use ES 2.0 subset of pyopengl (for fallback and testing) * dummy - Prevent usage of gloo.gl (for when rendering occurs elsewhere) You can use vispy's config option "gl_debug" to check for errors on each API call. Or, one can specify it as the target, e.g. "gl2 debug". (Debug does not apply to 'gl+', since PyOpenGL has its own debug mechanism) """ target = target or 'gl2' target = target.replace('+', 'plus') # Get options target, _, options = target.partition(' ') debug = config['gl_debug'] or 'debug' in options # Select modules to import names from try: mod = __import__(target, globals(), level=1) except ImportError as err: msg = 'Could not import gl target "%s":\n%s' % (target, str(err)) raise RuntimeError(msg) # Apply global current_backend current_backend = mod _clear_namespace() if 'plus' in target: # Copy PyOpenGL funcs, extra funcs, constants, no debug _copy_gl_functions(mod._pyopengl2, globals()) _copy_gl_functions(mod, globals(), True) elif debug: _copy_gl_functions(_debug_proxy, globals()) else: _copy_gl_functions(mod, globals()) def _clear_namespace(): """ Clear names that are not part of the strict ES API """ ok_names = set(default_backend.__dict__) ok_names.update(['gl2', 'glplus']) # don't remove the module NS = globals() for name in list(NS.keys()): if name.lower().startswith('gl'): if name not in ok_names: del NS[name] def _copy_gl_functions(source, dest, constants=False): """ Inject all objects that start with 'gl' from the source into the dest. source and dest can be dicts, modules or BaseGLProxy's. """ # Get dicts if isinstance(source, BaseGLProxy): s = {} for key in dir(source): s[key] = getattr(source, key) source = s elif not isinstance(source, dict): source = source.__dict__ if not isinstance(dest, dict): dest = dest.__dict__ # Copy names funcnames = [name for name in source.keys() if name.startswith('gl')] for name in funcnames: dest[name] = source[name] # Copy constants if constants: constnames = [name for name in source.keys() if name.startswith('GL_')] for name in constnames: dest[name] = source[name] def check_error(when='periodic check'): """ Check this from time to time to detect GL errors. Parameters ---------- when : str Shown in the exception to help the developer determine when this check was done. """ errors = [] while True: err = glGetError() if err == GL_NO_ERROR or (errors and err == errors[-1]): break errors.append(err) if errors: msg = ', '.join([repr(ENUM_MAP.get(e, e)) for e in errors]) err = RuntimeError('OpenGL got errors (%s): %s' % (when, msg)) err.errors = errors err.err = errors[-1] # pyopengl compat raise err # Load default gl backend from . import gl2 as default_backend # noqa # Call use to start using our default backend use_gl() ��������������������������������������������������vispy-0.4.0/vispy/gloo/gl/_constants.py�������������������������������������������������������������0000664�0001750�0001750�00000040347�12403630204�021626� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������""" THIS CODE IS AUTO-GENERATED. DO NOT EDIT. Constants for OpenGL ES 2.0. """ class Enum(int): ''' Enum (integer) with a meaningfull repr. ''' def __new__(cls, name, value): base = int.__new__(cls, value) base.name = name return base def __repr__(self): return self.name GL_ACTIVE_ATTRIBUTES = Enum('GL_ACTIVE_ATTRIBUTES', 35721) GL_ACTIVE_ATTRIBUTE_MAX_LENGTH = Enum('GL_ACTIVE_ATTRIBUTE_MAX_LENGTH', 35722) GL_ACTIVE_TEXTURE = Enum('GL_ACTIVE_TEXTURE', 34016) GL_ACTIVE_UNIFORMS = Enum('GL_ACTIVE_UNIFORMS', 35718) GL_ACTIVE_UNIFORM_MAX_LENGTH = Enum('GL_ACTIVE_UNIFORM_MAX_LENGTH', 35719) GL_ALIASED_LINE_WIDTH_RANGE = Enum('GL_ALIASED_LINE_WIDTH_RANGE', 33902) GL_ALIASED_POINT_SIZE_RANGE = Enum('GL_ALIASED_POINT_SIZE_RANGE', 33901) GL_ALPHA = Enum('GL_ALPHA', 6406) GL_ALPHA_BITS = Enum('GL_ALPHA_BITS', 3413) GL_ALWAYS = Enum('GL_ALWAYS', 519) GL_ARRAY_BUFFER = Enum('GL_ARRAY_BUFFER', 34962) GL_ARRAY_BUFFER_BINDING = Enum('GL_ARRAY_BUFFER_BINDING', 34964) GL_ATTACHED_SHADERS = Enum('GL_ATTACHED_SHADERS', 35717) GL_BACK = Enum('GL_BACK', 1029) GL_BLEND = Enum('GL_BLEND', 3042) GL_BLEND_COLOR = Enum('GL_BLEND_COLOR', 32773) GL_BLEND_DST_ALPHA = Enum('GL_BLEND_DST_ALPHA', 32970) GL_BLEND_DST_RGB = Enum('GL_BLEND_DST_RGB', 32968) GL_BLEND_EQUATION = Enum('GL_BLEND_EQUATION', 32777) GL_BLEND_EQUATION_ALPHA = Enum('GL_BLEND_EQUATION_ALPHA', 34877) GL_BLEND_EQUATION_RGB = Enum('GL_BLEND_EQUATION_RGB', 32777) GL_BLEND_SRC_ALPHA = Enum('GL_BLEND_SRC_ALPHA', 32971) GL_BLEND_SRC_RGB = Enum('GL_BLEND_SRC_RGB', 32969) GL_BLUE_BITS = Enum('GL_BLUE_BITS', 3412) GL_BOOL = Enum('GL_BOOL', 35670) GL_BOOL_VEC2 = Enum('GL_BOOL_VEC2', 35671) GL_BOOL_VEC3 = Enum('GL_BOOL_VEC3', 35672) GL_BOOL_VEC4 = Enum('GL_BOOL_VEC4', 35673) GL_BUFFER_SIZE = Enum('GL_BUFFER_SIZE', 34660) GL_BUFFER_USAGE = Enum('GL_BUFFER_USAGE', 34661) GL_BYTE = Enum('GL_BYTE', 5120) GL_CCW = Enum('GL_CCW', 2305) GL_CLAMP_TO_EDGE = Enum('GL_CLAMP_TO_EDGE', 33071) GL_COLOR_ATTACHMENT0 = Enum('GL_COLOR_ATTACHMENT0', 36064) GL_COLOR_BUFFER_BIT = Enum('GL_COLOR_BUFFER_BIT', 16384) GL_COLOR_CLEAR_VALUE = Enum('GL_COLOR_CLEAR_VALUE', 3106) GL_COLOR_WRITEMASK = Enum('GL_COLOR_WRITEMASK', 3107) GL_COMPILE_STATUS = Enum('GL_COMPILE_STATUS', 35713) GL_COMPRESSED_TEXTURE_FORMATS = Enum('GL_COMPRESSED_TEXTURE_FORMATS', 34467) GL_CONSTANT_ALPHA = Enum('GL_CONSTANT_ALPHA', 32771) GL_CONSTANT_COLOR = Enum('GL_CONSTANT_COLOR', 32769) GL_CULL_FACE = Enum('GL_CULL_FACE', 2884) GL_CULL_FACE_MODE = Enum('GL_CULL_FACE_MODE', 2885) GL_CURRENT_PROGRAM = Enum('GL_CURRENT_PROGRAM', 35725) GL_CURRENT_VERTEX_ATTRIB = Enum('GL_CURRENT_VERTEX_ATTRIB', 34342) GL_CW = Enum('GL_CW', 2304) GL_DECR = Enum('GL_DECR', 7683) GL_DECR_WRAP = Enum('GL_DECR_WRAP', 34056) GL_DELETE_STATUS = Enum('GL_DELETE_STATUS', 35712) GL_DEPTH_ATTACHMENT = Enum('GL_DEPTH_ATTACHMENT', 36096) GL_DEPTH_BITS = Enum('GL_DEPTH_BITS', 3414) GL_DEPTH_BUFFER_BIT = Enum('GL_DEPTH_BUFFER_BIT', 256) GL_DEPTH_CLEAR_VALUE = Enum('GL_DEPTH_CLEAR_VALUE', 2931) GL_DEPTH_COMPONENT = Enum('GL_DEPTH_COMPONENT', 6402) GL_DEPTH_COMPONENT16 = Enum('GL_DEPTH_COMPONENT16', 33189) GL_DEPTH_FUNC = Enum('GL_DEPTH_FUNC', 2932) GL_DEPTH_RANGE = Enum('GL_DEPTH_RANGE', 2928) GL_DEPTH_TEST = Enum('GL_DEPTH_TEST', 2929) GL_DEPTH_WRITEMASK = Enum('GL_DEPTH_WRITEMASK', 2930) GL_DITHER = Enum('GL_DITHER', 3024) GL_DONT_CARE = Enum('GL_DONT_CARE', 4352) GL_DST_ALPHA = Enum('GL_DST_ALPHA', 772) GL_DST_COLOR = Enum('GL_DST_COLOR', 774) GL_DYNAMIC_DRAW = Enum('GL_DYNAMIC_DRAW', 35048) GL_ELEMENT_ARRAY_BUFFER = Enum('GL_ELEMENT_ARRAY_BUFFER', 34963) GL_ELEMENT_ARRAY_BUFFER_BINDING = Enum('GL_ELEMENT_ARRAY_BUFFER_BINDING', 34965) GL_EQUAL = Enum('GL_EQUAL', 514) GL_ES_VERSION_2_0 = Enum('GL_ES_VERSION_2_0', 1) GL_EXTENSIONS = Enum('GL_EXTENSIONS', 7939) GL_FALSE = Enum('GL_FALSE', 0) GL_FASTEST = Enum('GL_FASTEST', 4353) GL_FIXED = Enum('GL_FIXED', 5132) GL_FLOAT = Enum('GL_FLOAT', 5126) GL_FLOAT_MAT2 = Enum('GL_FLOAT_MAT2', 35674) GL_FLOAT_MAT3 = Enum('GL_FLOAT_MAT3', 35675) GL_FLOAT_MAT4 = Enum('GL_FLOAT_MAT4', 35676) GL_FLOAT_VEC2 = Enum('GL_FLOAT_VEC2', 35664) GL_FLOAT_VEC3 = Enum('GL_FLOAT_VEC3', 35665) GL_FLOAT_VEC4 = Enum('GL_FLOAT_VEC4', 35666) GL_FRAGMENT_SHADER = Enum('GL_FRAGMENT_SHADER', 35632) GL_FRAMEBUFFER = Enum('GL_FRAMEBUFFER', 36160) GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME = Enum('GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME', 36049) GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE = Enum('GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE', 36048) GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE = Enum('GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE', 36051) GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL = Enum('GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL', 36050) GL_FRAMEBUFFER_BINDING = Enum('GL_FRAMEBUFFER_BINDING', 36006) GL_FRAMEBUFFER_COMPLETE = Enum('GL_FRAMEBUFFER_COMPLETE', 36053) GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT = Enum('GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT', 36054) GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS = Enum('GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS', 36057) GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT = Enum('GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT', 36055) GL_FRAMEBUFFER_UNSUPPORTED = Enum('GL_FRAMEBUFFER_UNSUPPORTED', 36061) GL_FRONT = Enum('GL_FRONT', 1028) GL_FRONT_AND_BACK = Enum('GL_FRONT_AND_BACK', 1032) GL_FRONT_FACE = Enum('GL_FRONT_FACE', 2886) GL_FUNC_ADD = Enum('GL_FUNC_ADD', 32774) GL_FUNC_REVERSE_SUBTRACT = Enum('GL_FUNC_REVERSE_SUBTRACT', 32779) GL_FUNC_SUBTRACT = Enum('GL_FUNC_SUBTRACT', 32778) GL_GENERATE_MIPMAP_HINT = Enum('GL_GENERATE_MIPMAP_HINT', 33170) GL_GEQUAL = Enum('GL_GEQUAL', 518) GL_GREATER = Enum('GL_GREATER', 516) GL_GREEN_BITS = Enum('GL_GREEN_BITS', 3411) GL_HIGH_FLOAT = Enum('GL_HIGH_FLOAT', 36338) GL_HIGH_INT = Enum('GL_HIGH_INT', 36341) GL_IMPLEMENTATION_COLOR_READ_FORMAT = Enum('GL_IMPLEMENTATION_COLOR_READ_FORMAT', 35739) GL_IMPLEMENTATION_COLOR_READ_TYPE = Enum('GL_IMPLEMENTATION_COLOR_READ_TYPE', 35738) GL_INCR = Enum('GL_INCR', 7682) GL_INCR_WRAP = Enum('GL_INCR_WRAP', 34055) GL_INFO_LOG_LENGTH = Enum('GL_INFO_LOG_LENGTH', 35716) GL_INT = Enum('GL_INT', 5124) GL_INT_VEC2 = Enum('GL_INT_VEC2', 35667) GL_INT_VEC3 = Enum('GL_INT_VEC3', 35668) GL_INT_VEC4 = Enum('GL_INT_VEC4', 35669) GL_INVALID_ENUM = Enum('GL_INVALID_ENUM', 1280) GL_INVALID_FRAMEBUFFER_OPERATION = Enum('GL_INVALID_FRAMEBUFFER_OPERATION', 1286) GL_INVALID_OPERATION = Enum('GL_INVALID_OPERATION', 1282) GL_INVALID_VALUE = Enum('GL_INVALID_VALUE', 1281) GL_INVERT = Enum('GL_INVERT', 5386) GL_KEEP = Enum('GL_KEEP', 7680) GL_LEQUAL = Enum('GL_LEQUAL', 515) GL_LESS = Enum('GL_LESS', 513) GL_LINEAR = Enum('GL_LINEAR', 9729) GL_LINEAR_MIPMAP_LINEAR = Enum('GL_LINEAR_MIPMAP_LINEAR', 9987) GL_LINEAR_MIPMAP_NEAREST = Enum('GL_LINEAR_MIPMAP_NEAREST', 9985) GL_LINES = Enum('GL_LINES', 1) GL_LINE_LOOP = Enum('GL_LINE_LOOP', 2) GL_LINE_STRIP = Enum('GL_LINE_STRIP', 3) GL_LINE_WIDTH = Enum('GL_LINE_WIDTH', 2849) GL_LINK_STATUS = Enum('GL_LINK_STATUS', 35714) GL_LOW_FLOAT = Enum('GL_LOW_FLOAT', 36336) GL_LOW_INT = Enum('GL_LOW_INT', 36339) GL_LUMINANCE = Enum('GL_LUMINANCE', 6409) GL_LUMINANCE_ALPHA = Enum('GL_LUMINANCE_ALPHA', 6410) GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS = Enum('GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS', 35661) GL_MAX_CUBE_MAP_TEXTURE_SIZE = Enum('GL_MAX_CUBE_MAP_TEXTURE_SIZE', 34076) GL_MAX_FRAGMENT_UNIFORM_VECTORS = Enum('GL_MAX_FRAGMENT_UNIFORM_VECTORS', 36349) GL_MAX_RENDERBUFFER_SIZE = Enum('GL_MAX_RENDERBUFFER_SIZE', 34024) GL_MAX_TEXTURE_IMAGE_UNITS = Enum('GL_MAX_TEXTURE_IMAGE_UNITS', 34930) GL_MAX_TEXTURE_SIZE = Enum('GL_MAX_TEXTURE_SIZE', 3379) GL_MAX_VARYING_VECTORS = Enum('GL_MAX_VARYING_VECTORS', 36348) GL_MAX_VERTEX_ATTRIBS = Enum('GL_MAX_VERTEX_ATTRIBS', 34921) GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS = Enum('GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS', 35660) GL_MAX_VERTEX_UNIFORM_VECTORS = Enum('GL_MAX_VERTEX_UNIFORM_VECTORS', 36347) GL_MAX_VIEWPORT_DIMS = Enum('GL_MAX_VIEWPORT_DIMS', 3386) GL_MEDIUM_FLOAT = Enum('GL_MEDIUM_FLOAT', 36337) GL_MEDIUM_INT = Enum('GL_MEDIUM_INT', 36340) GL_MIRRORED_REPEAT = Enum('GL_MIRRORED_REPEAT', 33648) GL_NEAREST = Enum('GL_NEAREST', 9728) GL_NEAREST_MIPMAP_LINEAR = Enum('GL_NEAREST_MIPMAP_LINEAR', 9986) GL_NEAREST_MIPMAP_NEAREST = Enum('GL_NEAREST_MIPMAP_NEAREST', 9984) GL_NEVER = Enum('GL_NEVER', 512) GL_NICEST = Enum('GL_NICEST', 4354) GL_NONE = Enum('GL_NONE', 0) GL_NOTEQUAL = Enum('GL_NOTEQUAL', 517) GL_NO_ERROR = Enum('GL_NO_ERROR', 0) GL_NUM_COMPRESSED_TEXTURE_FORMATS = Enum('GL_NUM_COMPRESSED_TEXTURE_FORMATS', 34466) GL_NUM_SHADER_BINARY_FORMATS = Enum('GL_NUM_SHADER_BINARY_FORMATS', 36345) GL_ONE = Enum('GL_ONE', 1) GL_ONE_MINUS_CONSTANT_ALPHA = Enum('GL_ONE_MINUS_CONSTANT_ALPHA', 32772) GL_ONE_MINUS_CONSTANT_COLOR = Enum('GL_ONE_MINUS_CONSTANT_COLOR', 32770) GL_ONE_MINUS_DST_ALPHA = Enum('GL_ONE_MINUS_DST_ALPHA', 773) GL_ONE_MINUS_DST_COLOR = Enum('GL_ONE_MINUS_DST_COLOR', 775) GL_ONE_MINUS_SRC_ALPHA = Enum('GL_ONE_MINUS_SRC_ALPHA', 771) GL_ONE_MINUS_SRC_COLOR = Enum('GL_ONE_MINUS_SRC_COLOR', 769) GL_OUT_OF_MEMORY = Enum('GL_OUT_OF_MEMORY', 1285) GL_PACK_ALIGNMENT = Enum('GL_PACK_ALIGNMENT', 3333) GL_POINTS = Enum('GL_POINTS', 0) GL_POLYGON_OFFSET_FACTOR = Enum('GL_POLYGON_OFFSET_FACTOR', 32824) GL_POLYGON_OFFSET_FILL = Enum('GL_POLYGON_OFFSET_FILL', 32823) GL_POLYGON_OFFSET_UNITS = Enum('GL_POLYGON_OFFSET_UNITS', 10752) GL_RED_BITS = Enum('GL_RED_BITS', 3410) GL_RENDERBUFFER = Enum('GL_RENDERBUFFER', 36161) GL_RENDERBUFFER_ALPHA_SIZE = Enum('GL_RENDERBUFFER_ALPHA_SIZE', 36179) GL_RENDERBUFFER_BINDING = Enum('GL_RENDERBUFFER_BINDING', 36007) GL_RENDERBUFFER_BLUE_SIZE = Enum('GL_RENDERBUFFER_BLUE_SIZE', 36178) GL_RENDERBUFFER_DEPTH_SIZE = Enum('GL_RENDERBUFFER_DEPTH_SIZE', 36180) GL_RENDERBUFFER_GREEN_SIZE = Enum('GL_RENDERBUFFER_GREEN_SIZE', 36177) GL_RENDERBUFFER_HEIGHT = Enum('GL_RENDERBUFFER_HEIGHT', 36163) GL_RENDERBUFFER_INTERNAL_FORMAT = Enum('GL_RENDERBUFFER_INTERNAL_FORMAT', 36164) GL_RENDERBUFFER_RED_SIZE = Enum('GL_RENDERBUFFER_RED_SIZE', 36176) GL_RENDERBUFFER_STENCIL_SIZE = Enum('GL_RENDERBUFFER_STENCIL_SIZE', 36181) GL_RENDERBUFFER_WIDTH = Enum('GL_RENDERBUFFER_WIDTH', 36162) GL_RENDERER = Enum('GL_RENDERER', 7937) GL_REPEAT = Enum('GL_REPEAT', 10497) GL_REPLACE = Enum('GL_REPLACE', 7681) GL_RGB = Enum('GL_RGB', 6407) GL_RGB565 = Enum('GL_RGB565', 36194) GL_RGB5_A1 = Enum('GL_RGB5_A1', 32855) GL_RGBA = Enum('GL_RGBA', 6408) GL_RGBA4 = Enum('GL_RGBA4', 32854) GL_SAMPLER_2D = Enum('GL_SAMPLER_2D', 35678) GL_SAMPLER_CUBE = Enum('GL_SAMPLER_CUBE', 35680) GL_SAMPLES = Enum('GL_SAMPLES', 32937) GL_SAMPLE_ALPHA_TO_COVERAGE = Enum('GL_SAMPLE_ALPHA_TO_COVERAGE', 32926) GL_SAMPLE_BUFFERS = Enum('GL_SAMPLE_BUFFERS', 32936) GL_SAMPLE_COVERAGE = Enum('GL_SAMPLE_COVERAGE', 32928) GL_SAMPLE_COVERAGE_INVERT = Enum('GL_SAMPLE_COVERAGE_INVERT', 32939) GL_SAMPLE_COVERAGE_VALUE = Enum('GL_SAMPLE_COVERAGE_VALUE', 32938) GL_SCISSOR_BOX = Enum('GL_SCISSOR_BOX', 3088) GL_SCISSOR_TEST = Enum('GL_SCISSOR_TEST', 3089) GL_SHADER_BINARY_FORMATS = Enum('GL_SHADER_BINARY_FORMATS', 36344) GL_SHADER_COMPILER = Enum('GL_SHADER_COMPILER', 36346) GL_SHADER_SOURCE_LENGTH = Enum('GL_SHADER_SOURCE_LENGTH', 35720) GL_SHADER_TYPE = Enum('GL_SHADER_TYPE', 35663) GL_SHADING_LANGUAGE_VERSION = Enum('GL_SHADING_LANGUAGE_VERSION', 35724) GL_SHORT = Enum('GL_SHORT', 5122) GL_SRC_ALPHA = Enum('GL_SRC_ALPHA', 770) GL_SRC_ALPHA_SATURATE = Enum('GL_SRC_ALPHA_SATURATE', 776) GL_SRC_COLOR = Enum('GL_SRC_COLOR', 768) GL_STATIC_DRAW = Enum('GL_STATIC_DRAW', 35044) GL_STENCIL_ATTACHMENT = Enum('GL_STENCIL_ATTACHMENT', 36128) GL_STENCIL_BACK_FAIL = Enum('GL_STENCIL_BACK_FAIL', 34817) GL_STENCIL_BACK_FUNC = Enum('GL_STENCIL_BACK_FUNC', 34816) GL_STENCIL_BACK_PASS_DEPTH_FAIL = Enum('GL_STENCIL_BACK_PASS_DEPTH_FAIL', 34818) GL_STENCIL_BACK_PASS_DEPTH_PASS = Enum('GL_STENCIL_BACK_PASS_DEPTH_PASS', 34819) GL_STENCIL_BACK_REF = Enum('GL_STENCIL_BACK_REF', 36003) GL_STENCIL_BACK_VALUE_MASK = Enum('GL_STENCIL_BACK_VALUE_MASK', 36004) GL_STENCIL_BACK_WRITEMASK = Enum('GL_STENCIL_BACK_WRITEMASK', 36005) GL_STENCIL_BITS = Enum('GL_STENCIL_BITS', 3415) GL_STENCIL_BUFFER_BIT = Enum('GL_STENCIL_BUFFER_BIT', 1024) GL_STENCIL_CLEAR_VALUE = Enum('GL_STENCIL_CLEAR_VALUE', 2961) GL_STENCIL_FAIL = Enum('GL_STENCIL_FAIL', 2964) GL_STENCIL_FUNC = Enum('GL_STENCIL_FUNC', 2962) GL_STENCIL_INDEX8 = Enum('GL_STENCIL_INDEX8', 36168) GL_STENCIL_PASS_DEPTH_FAIL = Enum('GL_STENCIL_PASS_DEPTH_FAIL', 2965) GL_STENCIL_PASS_DEPTH_PASS = Enum('GL_STENCIL_PASS_DEPTH_PASS', 2966) GL_STENCIL_REF = Enum('GL_STENCIL_REF', 2967) GL_STENCIL_TEST = Enum('GL_STENCIL_TEST', 2960) GL_STENCIL_VALUE_MASK = Enum('GL_STENCIL_VALUE_MASK', 2963) GL_STENCIL_WRITEMASK = Enum('GL_STENCIL_WRITEMASK', 2968) GL_STREAM_DRAW = Enum('GL_STREAM_DRAW', 35040) GL_SUBPIXEL_BITS = Enum('GL_SUBPIXEL_BITS', 3408) GL_TEXTURE = Enum('GL_TEXTURE', 5890) GL_TEXTURE0 = Enum('GL_TEXTURE0', 33984) GL_TEXTURE1 = Enum('GL_TEXTURE1', 33985) GL_TEXTURE10 = Enum('GL_TEXTURE10', 33994) GL_TEXTURE11 = Enum('GL_TEXTURE11', 33995) GL_TEXTURE12 = Enum('GL_TEXTURE12', 33996) GL_TEXTURE13 = Enum('GL_TEXTURE13', 33997) GL_TEXTURE14 = Enum('GL_TEXTURE14', 33998) GL_TEXTURE15 = Enum('GL_TEXTURE15', 33999) GL_TEXTURE16 = Enum('GL_TEXTURE16', 34000) GL_TEXTURE17 = Enum('GL_TEXTURE17', 34001) GL_TEXTURE18 = Enum('GL_TEXTURE18', 34002) GL_TEXTURE19 = Enum('GL_TEXTURE19', 34003) GL_TEXTURE2 = Enum('GL_TEXTURE2', 33986) GL_TEXTURE20 = Enum('GL_TEXTURE20', 34004) GL_TEXTURE21 = Enum('GL_TEXTURE21', 34005) GL_TEXTURE22 = Enum('GL_TEXTURE22', 34006) GL_TEXTURE23 = Enum('GL_TEXTURE23', 34007) GL_TEXTURE24 = Enum('GL_TEXTURE24', 34008) GL_TEXTURE25 = Enum('GL_TEXTURE25', 34009) GL_TEXTURE26 = Enum('GL_TEXTURE26', 34010) GL_TEXTURE27 = Enum('GL_TEXTURE27', 34011) GL_TEXTURE28 = Enum('GL_TEXTURE28', 34012) GL_TEXTURE29 = Enum('GL_TEXTURE29', 34013) GL_TEXTURE3 = Enum('GL_TEXTURE3', 33987) GL_TEXTURE30 = Enum('GL_TEXTURE30', 34014) GL_TEXTURE31 = Enum('GL_TEXTURE31', 34015) GL_TEXTURE4 = Enum('GL_TEXTURE4', 33988) GL_TEXTURE5 = Enum('GL_TEXTURE5', 33989) GL_TEXTURE6 = Enum('GL_TEXTURE6', 33990) GL_TEXTURE7 = Enum('GL_TEXTURE7', 33991) GL_TEXTURE8 = Enum('GL_TEXTURE8', 33992) GL_TEXTURE9 = Enum('GL_TEXTURE9', 33993) GL_TEXTURE_2D = Enum('GL_TEXTURE_2D', 3553) GL_TEXTURE_BINDING_2D = Enum('GL_TEXTURE_BINDING_2D', 32873) GL_TEXTURE_BINDING_CUBE_MAP = Enum('GL_TEXTURE_BINDING_CUBE_MAP', 34068) GL_TEXTURE_CUBE_MAP = Enum('GL_TEXTURE_CUBE_MAP', 34067) GL_TEXTURE_CUBE_MAP_NEGATIVE_X = Enum('GL_TEXTURE_CUBE_MAP_NEGATIVE_X', 34070) GL_TEXTURE_CUBE_MAP_NEGATIVE_Y = Enum('GL_TEXTURE_CUBE_MAP_NEGATIVE_Y', 34072) GL_TEXTURE_CUBE_MAP_NEGATIVE_Z = Enum('GL_TEXTURE_CUBE_MAP_NEGATIVE_Z', 34074) GL_TEXTURE_CUBE_MAP_POSITIVE_X = Enum('GL_TEXTURE_CUBE_MAP_POSITIVE_X', 34069) GL_TEXTURE_CUBE_MAP_POSITIVE_Y = Enum('GL_TEXTURE_CUBE_MAP_POSITIVE_Y', 34071) GL_TEXTURE_CUBE_MAP_POSITIVE_Z = Enum('GL_TEXTURE_CUBE_MAP_POSITIVE_Z', 34073) GL_TEXTURE_MAG_FILTER = Enum('GL_TEXTURE_MAG_FILTER', 10240) GL_TEXTURE_MIN_FILTER = Enum('GL_TEXTURE_MIN_FILTER', 10241) GL_TEXTURE_WRAP_S = Enum('GL_TEXTURE_WRAP_S', 10242) GL_TEXTURE_WRAP_T = Enum('GL_TEXTURE_WRAP_T', 10243) GL_TRIANGLES = Enum('GL_TRIANGLES', 4) GL_TRIANGLE_FAN = Enum('GL_TRIANGLE_FAN', 6) GL_TRIANGLE_STRIP = Enum('GL_TRIANGLE_STRIP', 5) GL_TRUE = Enum('GL_TRUE', 1) GL_UNPACK_ALIGNMENT = Enum('GL_UNPACK_ALIGNMENT', 3317) GL_UNSIGNED_BYTE = Enum('GL_UNSIGNED_BYTE', 5121) GL_UNSIGNED_INT = Enum('GL_UNSIGNED_INT', 5125) GL_UNSIGNED_SHORT = Enum('GL_UNSIGNED_SHORT', 5123) GL_UNSIGNED_SHORT_4_4_4_4 = Enum('GL_UNSIGNED_SHORT_4_4_4_4', 32819) GL_UNSIGNED_SHORT_5_5_5_1 = Enum('GL_UNSIGNED_SHORT_5_5_5_1', 32820) GL_UNSIGNED_SHORT_5_6_5 = Enum('GL_UNSIGNED_SHORT_5_6_5', 33635) GL_VALIDATE_STATUS = Enum('GL_VALIDATE_STATUS', 35715) GL_VENDOR = Enum('GL_VENDOR', 7936) GL_VERSION = Enum('GL_VERSION', 7938) GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING = Enum('GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING', 34975) GL_VERTEX_ATTRIB_ARRAY_ENABLED = Enum('GL_VERTEX_ATTRIB_ARRAY_ENABLED', 34338) GL_VERTEX_ATTRIB_ARRAY_NORMALIZED = Enum('GL_VERTEX_ATTRIB_ARRAY_NORMALIZED', 34922) GL_VERTEX_ATTRIB_ARRAY_POINTER = Enum('GL_VERTEX_ATTRIB_ARRAY_POINTER', 34373) GL_VERTEX_ATTRIB_ARRAY_SIZE = Enum('GL_VERTEX_ATTRIB_ARRAY_SIZE', 34339) GL_VERTEX_ATTRIB_ARRAY_STRIDE = Enum('GL_VERTEX_ATTRIB_ARRAY_STRIDE', 34340) GL_VERTEX_ATTRIB_ARRAY_TYPE = Enum('GL_VERTEX_ATTRIB_ARRAY_TYPE', 34341) GL_VERTEX_SHADER = Enum('GL_VERTEX_SHADER', 35633) GL_VIEWPORT = Enum('GL_VIEWPORT', 2978) GL_ZERO = Enum('GL_ZERO', 0) ENUM_MAP = {} for ob in list(globals().values()): if repr(ob).startswith('GL_'): ENUM_MAP[int(ob)] = ob del ob �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/gloo/gl/_pyopengl2.py�������������������������������������������������������������0000664�0001750�0001750�00000027132�12510536123�021532� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������""" THIS CODE IS AUTO-GENERATED. DO NOT EDIT. Proxy API for GL ES 2.0 subset, via the PyOpenGL library. """ import ctypes from OpenGL import GL import OpenGL.GL.framebufferobjects as FBO def glBindAttribLocation(program, index, name): name = name.encode('utf-8') return GL.glBindAttribLocation(program, index, name) def glBufferData(target, data, usage): """ Data can be numpy array or the size of data to allocate. """ if isinstance(data, int): size = data data = None else: size = data.nbytes GL.glBufferData(target, size, data, usage) def glBufferSubData(target, offset, data): size = data.nbytes GL.glBufferSubData(target, offset, size, data) def glCompressedTexImage2D(target, level, internalformat, width, height, border, data): # border = 0 # set in args size = data.size GL.glCompressedTexImage2D(target, level, internalformat, width, height, border, size, data) def glCompressedTexSubImage2D(target, level, xoffset, yoffset, width, height, format, data): size = data.size GL.glCompressedTexSubImage2D(target, level, xoffset, yoffset, width, height, format, size, data) def glDeleteBuffer(buffer): GL.glDeleteBuffers(1, [buffer]) def glDeleteFramebuffer(framebuffer): FBO.glDeleteFramebuffers(1, [framebuffer]) def glDeleteRenderbuffer(renderbuffer): FBO.glDeleteRenderbuffers(1, [renderbuffer]) def glDeleteTexture(texture): GL.glDeleteTextures([texture]) def glDrawElements(mode, count, type, offset): if offset is None: offset = ctypes.c_void_p(0) elif isinstance(offset, (int, ctypes.c_int)): offset = ctypes.c_void_p(int(offset)) return GL.glDrawElements(mode, count, type, offset) def glCreateBuffer(): return GL.glGenBuffers(1) def glCreateFramebuffer(): return FBO.glGenFramebuffers(1) def glCreateRenderbuffer(): return FBO.glGenRenderbuffers(1) def glCreateTexture(): return GL.glGenTextures(1) def glGetActiveAttrib(program, index): bufsize = 256 length = (ctypes.c_int*1)() size = (ctypes.c_int*1)() type = (ctypes.c_uint*1)() name = ctypes.create_string_buffer(bufsize) # pyopengl has a bug, this is a patch GL.glGetActiveAttrib(program, index, bufsize, length, size, type, name) name = name[:length[0]].decode('utf-8') return name, size[0], type[0] def glGetActiveUniform(program, index): name, size, type = GL.glGetActiveUniform(program, index) return name.decode('utf-8'), size, type def glGetAttribLocation(program, name): name = name.encode('utf-8') return GL.glGetAttribLocation(program, name) def glGetFramebufferAttachmentParameter(target, attachment, pname): d = -2**31 # smallest 32bit integer params = (ctypes.c_int*1)(d) FBO.glGetFramebufferAttachmentParameteriv(target, attachment, pname, params) return params[0] def glGetProgramInfoLog(program): res = GL.glGetProgramInfoLog(program) return res.decode('utf-8') if isinstance(res, bytes) else res def glGetRenderbufferParameter(target, pname): d = -2**31 # smallest 32bit integer params = (ctypes.c_int*1)(d) FBO.glGetRenderbufferParameteriv(target, pname, params) return params[0] def glGetShaderInfoLog(shader): res = GL.glGetShaderInfoLog(shader) return res.decode('utf-8') if isinstance(res, bytes) else res def glGetShaderSource(shader): res = GL.glGetShaderSource(shader) return res.decode('utf-8') def glGetParameter(pname): if pname in [33902, 33901, 32773, 3106, 2931, 2928, 2849, 32824, 10752, 32938]: # GL_ALIASED_LINE_WIDTH_RANGE GL_ALIASED_POINT_SIZE_RANGE # GL_BLEND_COLOR GL_COLOR_CLEAR_VALUE GL_DEPTH_CLEAR_VALUE # GL_DEPTH_RANGE GL_LINE_WIDTH GL_POLYGON_OFFSET_FACTOR # GL_POLYGON_OFFSET_UNITS GL_SAMPLE_COVERAGE_VALUE return _glGetFloatv(pname) elif pname in [7936, 7937, 7938, 35724, 7939]: # GL_VENDOR, GL_RENDERER, GL_VERSION, GL_SHADING_LANGUAGE_VERSION, # GL_EXTENSIONS are strings pass # string handled below else: return _glGetIntegerv(pname) name = pname res = GL.glGetString(pname) return res.decode('utf-8') def glGetUniform(program, location): n = 16 d = float('Inf') params = (ctypes.c_float*n)(*[d for i in range(n)]) GL.glGetUniformfv(program, location, params) params = [p for p in params if p!=d] if len(params) == 1: return params[0] else: return tuple(params) def glGetUniformLocation(program, name): name = name.encode('utf-8') return GL.glGetUniformLocation(program, name) def glGetVertexAttrib(index, pname): # From PyOpenGL v3.1.0 the glGetVertexAttribfv(index, pname) does # work, but it always returns 4 values, with zeros in the empty # spaces. We have no way to tell whether they are empty or genuine # zeros. Fortunately, pyopengl also supports the old syntax. n = 4 d = float('Inf') params = (ctypes.c_float*n)(*[d for i in range(n)]) GL.glGetVertexAttribfv(index, pname, params) params = [p for p in params if p!=d] if len(params) == 1: return params[0] else: return tuple(params) def glGetVertexAttribOffset(index, pname): try: # maybe the fixed it return GL.glGetVertexAttribPointerv(index, pname) except TypeError: pointer = (ctypes.c_void_p*1)() GL.glGetVertexAttribPointerv(index, pname, pointer) return pointer[0] or 0 def glShaderSource(shader, source): # Some implementation do not like getting a list of single chars if isinstance(source, (tuple, list)): strings = [s for s in source] else: strings = [source] GL.glShaderSource(shader, strings) def glTexImage2D(target, level, internalformat, format, type, pixels): border = 0 if isinstance(pixels, (tuple, list)): height, width = pixels pixels = None else: height, width = pixels.shape[:2] GL.glTexImage2D(target, level, internalformat, width, height, border, format, type, pixels) def glTexSubImage2D(target, level, xoffset, yoffset, format, type, pixels): height, width = pixels.shape[:2] GL.glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels) def glVertexAttribPointer(indx, size, type, normalized, stride, offset): if offset is None: offset = ctypes.c_void_p(0) elif isinstance(offset, (int, ctypes.c_int)): offset = ctypes.c_void_p(int(offset)) return GL.glVertexAttribPointer(indx, size, type, normalized, stride, offset) # List of functions that we should import from OpenGL.GL _functions_to_import = [ ("glActiveTexture", "glActiveTexture"), ("glAttachShader", "glAttachShader"), ("glBindBuffer", "glBindBuffer"), ("glBindFramebuffer", "glBindFramebuffer"), ("glBindRenderbuffer", "glBindRenderbuffer"), ("glBindTexture", "glBindTexture"), ("glBlendColor", "glBlendColor"), ("glBlendEquation", "glBlendEquation"), ("glBlendEquationSeparate", "glBlendEquationSeparate"), ("glBlendFunc", "glBlendFunc"), ("glBlendFuncSeparate", "glBlendFuncSeparate"), ("glCheckFramebufferStatus", "glCheckFramebufferStatus"), ("glClear", "glClear"), ("glClearColor", "glClearColor"), ("glClearDepthf", "glClearDepth"), ("glClearStencil", "glClearStencil"), ("glColorMask", "glColorMask"), ("glCompileShader", "glCompileShader"), ("glCopyTexImage2D", "glCopyTexImage2D"), ("glCopyTexSubImage2D", "glCopyTexSubImage2D"), ("glCreateProgram", "glCreateProgram"), ("glCreateShader", "glCreateShader"), ("glCullFace", "glCullFace"), ("glDeleteProgram", "glDeleteProgram"), ("glDeleteShader", "glDeleteShader"), ("glDepthFunc", "glDepthFunc"), ("glDepthMask", "glDepthMask"), ("glDepthRangef", "glDepthRange"), ("glDetachShader", "glDetachShader"), ("glDisable", "glDisable"), ("glDisableVertexAttribArray", "glDisableVertexAttribArray"), ("glDrawArrays", "glDrawArrays"), ("glEnable", "glEnable"), ("glEnableVertexAttribArray", "glEnableVertexAttribArray"), ("glFinish", "glFinish"), ("glFlush", "glFlush"), ("glFramebufferRenderbuffer", "glFramebufferRenderbuffer"), ("glFramebufferTexture2D", "glFramebufferTexture2D"), ("glFrontFace", "glFrontFace"), ("glGenerateMipmap", "glGenerateMipmap"), ("glGetAttachedShaders", "glGetAttachedShaders"), ("glGetBooleanv", "_glGetBooleanv"), ("glGetBufferParameteriv", "glGetBufferParameter"), ("glGetError", "glGetError"), ("glGetFloatv", "_glGetFloatv"), ("glGetIntegerv", "_glGetIntegerv"), ("glGetProgramiv", "glGetProgramParameter"), ("glGetShaderPrecisionFormat", "glGetShaderPrecisionFormat"), ("glGetShaderiv", "glGetShaderParameter"), ("glGetTexParameterfv", "glGetTexParameter"), ("glHint", "glHint"), ("glIsBuffer", "glIsBuffer"), ("glIsEnabled", "glIsEnabled"), ("glIsFramebuffer", "glIsFramebuffer"), ("glIsProgram", "glIsProgram"), ("glIsRenderbuffer", "glIsRenderbuffer"), ("glIsShader", "glIsShader"), ("glIsTexture", "glIsTexture"), ("glLineWidth", "glLineWidth"), ("glLinkProgram", "glLinkProgram"), ("glPixelStorei", "glPixelStorei"), ("glPolygonOffset", "glPolygonOffset"), ("glReadPixels", "glReadPixels"), ("glRenderbufferStorage", "glRenderbufferStorage"), ("glSampleCoverage", "glSampleCoverage"), ("glScissor", "glScissor"), ("glStencilFunc", "glStencilFunc"), ("glStencilFuncSeparate", "glStencilFuncSeparate"), ("glStencilMask", "glStencilMask"), ("glStencilMaskSeparate", "glStencilMaskSeparate"), ("glStencilOp", "glStencilOp"), ("glStencilOpSeparate", "glStencilOpSeparate"), ("glTexParameterf", "glTexParameterf"), ("glTexParameteri", "glTexParameteri"), ("glUniform1f", "glUniform1f"), ("glUniform2f", "glUniform2f"), ("glUniform3f", "glUniform3f"), ("glUniform4f", "glUniform4f"), ("glUniform1i", "glUniform1i"), ("glUniform2i", "glUniform2i"), ("glUniform3i", "glUniform3i"), ("glUniform4i", "glUniform4i"), ("glUniform1fv", "glUniform1fv"), ("glUniform2fv", "glUniform2fv"), ("glUniform3fv", "glUniform3fv"), ("glUniform4fv", "glUniform4fv"), ("glUniform1iv", "glUniform1iv"), ("glUniform2iv", "glUniform2iv"), ("glUniform3iv", "glUniform3iv"), ("glUniform4iv", "glUniform4iv"), ("glUniformMatrix2fv", "glUniformMatrix2fv"), ("glUniformMatrix3fv", "glUniformMatrix3fv"), ("glUniformMatrix4fv", "glUniformMatrix4fv"), ("glUseProgram", "glUseProgram"), ("glValidateProgram", "glValidateProgram"), ("glVertexAttrib1f", "glVertexAttrib1f"), ("glVertexAttrib2f", "glVertexAttrib2f"), ("glVertexAttrib3f", "glVertexAttrib3f"), ("glVertexAttrib4f", "glVertexAttrib4f"), ("glViewport", "glViewport"), ] # List of functions in OpenGL.GL that we use _used_functions = [ "glBindAttribLocation", "glBufferData", "glBufferSubData", "glCompressedTexImage2D", "glCompressedTexSubImage2D", "glDeleteBuffers", "glDeleteFramebuffers", "glDeleteRenderbuffers", "glDeleteTextures", "glDrawElements", "glGenBuffers", "glGenFramebuffers", "glGenRenderbuffers", "glGenTextures", "glGetActiveAttrib", "glGetActiveUniform", "glGetAttribLocation", "glGetFramebufferAttachmentParameteriv", "glGetProgramInfoLog", "glGetRenderbufferParameteriv", "glGetShaderInfoLog", "glGetShaderSource", "glGetString", "glGetUniformfv", "glGetUniformLocation", "glGetVertexAttribfv", "glGetVertexAttribPointerv", "glShaderSource", "glTexImage2D", "glTexSubImage2D", "glVertexAttribPointer", ] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/gloo/gl/_gl2.py�������������������������������������������������������������������0000664�0001750�0001750�00000152236�12510536123�020303� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������""" THIS CODE IS AUTO-GENERATED. DO NOT EDIT. Subset of desktop GL API compatible with GL ES 2.0 """ import ctypes from .gl2 import _lib, _get_gl_func # void = glActiveTexture(GLenum texture) def glActiveTexture(texture): try: nativefunc = glActiveTexture._native except AttributeError: nativefunc = glActiveTexture._native = _get_gl_func("glActiveTexture", None, (ctypes.c_uint,)) nativefunc(texture) # void = glAttachShader(GLuint program, GLuint shader) def glAttachShader(program, shader): try: nativefunc = glAttachShader._native except AttributeError: nativefunc = glAttachShader._native = _get_gl_func("glAttachShader", None, (ctypes.c_uint, ctypes.c_uint,)) nativefunc(program, shader) # void = glBindAttribLocation(GLuint program, GLuint index, GLchar* name) def glBindAttribLocation(program, index, name): name = ctypes.c_char_p(name.encode('utf-8')) try: nativefunc = glBindAttribLocation._native except AttributeError: nativefunc = glBindAttribLocation._native = _get_gl_func("glBindAttribLocation", None, (ctypes.c_uint, ctypes.c_uint, ctypes.c_char_p,)) res = nativefunc(program, index, name) # void = glBindBuffer(GLenum target, GLuint buffer) def glBindBuffer(target, buffer): try: nativefunc = glBindBuffer._native except AttributeError: nativefunc = glBindBuffer._native = _get_gl_func("glBindBuffer", None, (ctypes.c_uint, ctypes.c_uint,)) nativefunc(target, buffer) # void = glBindFramebuffer(GLenum target, GLuint framebuffer) def glBindFramebuffer(target, framebuffer): try: nativefunc = glBindFramebuffer._native except AttributeError: nativefunc = glBindFramebuffer._native = _get_gl_func("glBindFramebuffer", None, (ctypes.c_uint, ctypes.c_uint,)) nativefunc(target, framebuffer) # void = glBindRenderbuffer(GLenum target, GLuint renderbuffer) def glBindRenderbuffer(target, renderbuffer): try: nativefunc = glBindRenderbuffer._native except AttributeError: nativefunc = glBindRenderbuffer._native = _get_gl_func("glBindRenderbuffer", None, (ctypes.c_uint, ctypes.c_uint,)) nativefunc(target, renderbuffer) # void = glBindTexture(GLenum target, GLuint texture) def glBindTexture(target, texture): try: nativefunc = glBindTexture._native except AttributeError: nativefunc = glBindTexture._native = _get_gl_func("glBindTexture", None, (ctypes.c_uint, ctypes.c_uint,)) nativefunc(target, texture) # void = glBlendColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha) def glBlendColor(red, green, blue, alpha): try: nativefunc = glBlendColor._native except AttributeError: nativefunc = glBlendColor._native = _get_gl_func("glBlendColor", None, (ctypes.c_float, ctypes.c_float, ctypes.c_float, ctypes.c_float,)) nativefunc(red, green, blue, alpha) # void = glBlendEquation(GLenum mode) def glBlendEquation(mode): try: nativefunc = glBlendEquation._native except AttributeError: nativefunc = glBlendEquation._native = _get_gl_func("glBlendEquation", None, (ctypes.c_uint,)) nativefunc(mode) # void = glBlendEquationSeparate(GLenum modeRGB, GLenum modeAlpha) def glBlendEquationSeparate(modeRGB, modeAlpha): try: nativefunc = glBlendEquationSeparate._native except AttributeError: nativefunc = glBlendEquationSeparate._native = _get_gl_func("glBlendEquationSeparate", None, (ctypes.c_uint, ctypes.c_uint,)) nativefunc(modeRGB, modeAlpha) # void = glBlendFunc(GLenum sfactor, GLenum dfactor) def glBlendFunc(sfactor, dfactor): try: nativefunc = glBlendFunc._native except AttributeError: nativefunc = glBlendFunc._native = _get_gl_func("glBlendFunc", None, (ctypes.c_uint, ctypes.c_uint,)) nativefunc(sfactor, dfactor) # void = glBlendFuncSeparate(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) def glBlendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha): try: nativefunc = glBlendFuncSeparate._native except AttributeError: nativefunc = glBlendFuncSeparate._native = _get_gl_func("glBlendFuncSeparate", None, (ctypes.c_uint, ctypes.c_uint, ctypes.c_uint, ctypes.c_uint,)) nativefunc(srcRGB, dstRGB, srcAlpha, dstAlpha) # void = glBufferData(GLenum target, GLsizeiptr size, GLvoid* data, GLenum usage) def glBufferData(target, data, usage): """ Data can be numpy array or the size of data to allocate. """ if isinstance(data, int): size = data data = ctypes.c_voidp(0) else: if not data.flags['C_CONTIGUOUS'] or not data.flags['ALIGNED']: data = data.copy('C') data_ = data size = data_.nbytes data = data_.ctypes.data try: nativefunc = glBufferData._native except AttributeError: nativefunc = glBufferData._native = _get_gl_func("glBufferData", None, (ctypes.c_uint, ctypes.c_int, ctypes.c_void_p, ctypes.c_uint,)) res = nativefunc(target, size, data, usage) # void = glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, GLvoid* data) def glBufferSubData(target, offset, data): if not data.flags['C_CONTIGUOUS']: data = data.copy('C') data_ = data size = data_.nbytes data = data_.ctypes.data try: nativefunc = glBufferSubData._native except AttributeError: nativefunc = glBufferSubData._native = _get_gl_func("glBufferSubData", None, (ctypes.c_uint, ctypes.c_int, ctypes.c_int, ctypes.c_void_p,)) res = nativefunc(target, offset, size, data) # GLenum = glCheckFramebufferStatus(GLenum target) def glCheckFramebufferStatus(target): try: nativefunc = glCheckFramebufferStatus._native except AttributeError: nativefunc = glCheckFramebufferStatus._native = _get_gl_func("glCheckFramebufferStatus", ctypes.c_uint, (ctypes.c_uint,)) return nativefunc(target) # void = glClear(GLbitfield mask) def glClear(mask): try: nativefunc = glClear._native except AttributeError: nativefunc = glClear._native = _get_gl_func("glClear", None, (ctypes.c_uint,)) nativefunc(mask) # void = glClearColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha) def glClearColor(red, green, blue, alpha): try: nativefunc = glClearColor._native except AttributeError: nativefunc = glClearColor._native = _get_gl_func("glClearColor", None, (ctypes.c_float, ctypes.c_float, ctypes.c_float, ctypes.c_float,)) nativefunc(red, green, blue, alpha) # void = glClearDepthf(GLclampf depth) def glClearDepth(depth): try: nativefunc = glClearDepth._native except AttributeError: nativefunc = glClearDepth._native = _get_gl_func("glClearDepth", None, (ctypes.c_double,)) nativefunc(depth) # void = glClearStencil(GLint s) def glClearStencil(s): try: nativefunc = glClearStencil._native except AttributeError: nativefunc = glClearStencil._native = _get_gl_func("glClearStencil", None, (ctypes.c_int,)) nativefunc(s) # void = glColorMask(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha) def glColorMask(red, green, blue, alpha): try: nativefunc = glColorMask._native except AttributeError: nativefunc = glColorMask._native = _get_gl_func("glColorMask", None, (ctypes.c_bool, ctypes.c_bool, ctypes.c_bool, ctypes.c_bool,)) nativefunc(red, green, blue, alpha) # void = glCompileShader(GLuint shader) def glCompileShader(shader): try: nativefunc = glCompileShader._native except AttributeError: nativefunc = glCompileShader._native = _get_gl_func("glCompileShader", None, (ctypes.c_uint,)) nativefunc(shader) # void = glCompressedTexImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, GLvoid* data) def glCompressedTexImage2D(target, level, internalformat, width, height, border, data): # border = 0 # set in args if not data.flags['C_CONTIGUOUS']: data = data.copy('C') data_ = data size = data_.size data = data_.ctypes.data try: nativefunc = glCompressedTexImage2D._native except AttributeError: nativefunc = glCompressedTexImage2D._native = _get_gl_func("glCompressedTexImage2D", None, (ctypes.c_uint, ctypes.c_int, ctypes.c_uint, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_void_p,)) res = nativefunc(target, level, internalformat, width, height, border, imageSize, data) # void = glCompressedTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, GLvoid* data) def glCompressedTexSubImage2D(target, level, xoffset, yoffset, width, height, format, data): if not data.flags['C_CONTIGUOUS']: data = data.copy('C') data_ = data size = data_.size data = data_.ctypes.data try: nativefunc = glCompressedTexSubImage2D._native except AttributeError: nativefunc = glCompressedTexSubImage2D._native = _get_gl_func("glCompressedTexSubImage2D", None, (ctypes.c_uint, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_uint, ctypes.c_int, ctypes.c_void_p,)) res = nativefunc(target, level, xoffset, yoffset, width, height, format, imageSize, data) # void = glCopyTexImage2D(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border) def glCopyTexImage2D(target, level, internalformat, x, y, width, height, border): try: nativefunc = glCopyTexImage2D._native except AttributeError: nativefunc = glCopyTexImage2D._native = _get_gl_func("glCopyTexImage2D", None, (ctypes.c_uint, ctypes.c_int, ctypes.c_uint, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int,)) nativefunc(target, level, internalformat, x, y, width, height, border) # void = glCopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height) def glCopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height): try: nativefunc = glCopyTexSubImage2D._native except AttributeError: nativefunc = glCopyTexSubImage2D._native = _get_gl_func("glCopyTexSubImage2D", None, (ctypes.c_uint, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int,)) nativefunc(target, level, xoffset, yoffset, x, y, width, height) # GLuint = glCreateProgram() def glCreateProgram(): try: nativefunc = glCreateProgram._native except AttributeError: nativefunc = glCreateProgram._native = _get_gl_func("glCreateProgram", ctypes.c_uint, ()) return nativefunc() # GLuint = glCreateShader(GLenum type) def glCreateShader(type): try: nativefunc = glCreateShader._native except AttributeError: nativefunc = glCreateShader._native = _get_gl_func("glCreateShader", ctypes.c_uint, (ctypes.c_uint,)) return nativefunc(type) # void = glCullFace(GLenum mode) def glCullFace(mode): try: nativefunc = glCullFace._native except AttributeError: nativefunc = glCullFace._native = _get_gl_func("glCullFace", None, (ctypes.c_uint,)) nativefunc(mode) # void = glDeleteBuffers(GLsizei n, GLuint* buffers) def glDeleteBuffer(buffer): n = 1 buffers = (ctypes.c_uint*n)(buffer) try: nativefunc = glDeleteBuffer._native except AttributeError: nativefunc = glDeleteBuffer._native = _get_gl_func("glDeleteBuffers", None, (ctypes.c_int, ctypes.POINTER(ctypes.c_uint),)) res = nativefunc(n, buffers) # void = glDeleteFramebuffers(GLsizei n, GLuint* framebuffers) def glDeleteFramebuffer(framebuffer): n = 1 framebuffers = (ctypes.c_uint*n)(framebuffer) try: nativefunc = glDeleteFramebuffer._native except AttributeError: nativefunc = glDeleteFramebuffer._native = _get_gl_func("glDeleteFramebuffers", None, (ctypes.c_int, ctypes.POINTER(ctypes.c_uint),)) res = nativefunc(n, framebuffers) # void = glDeleteProgram(GLuint program) def glDeleteProgram(program): try: nativefunc = glDeleteProgram._native except AttributeError: nativefunc = glDeleteProgram._native = _get_gl_func("glDeleteProgram", None, (ctypes.c_uint,)) nativefunc(program) # void = glDeleteRenderbuffers(GLsizei n, GLuint* renderbuffers) def glDeleteRenderbuffer(renderbuffer): n = 1 renderbuffers = (ctypes.c_uint*n)(renderbuffer) try: nativefunc = glDeleteRenderbuffer._native except AttributeError: nativefunc = glDeleteRenderbuffer._native = _get_gl_func("glDeleteRenderbuffers", None, (ctypes.c_int, ctypes.POINTER(ctypes.c_uint),)) res = nativefunc(n, renderbuffers) # void = glDeleteShader(GLuint shader) def glDeleteShader(shader): try: nativefunc = glDeleteShader._native except AttributeError: nativefunc = glDeleteShader._native = _get_gl_func("glDeleteShader", None, (ctypes.c_uint,)) nativefunc(shader) # void = glDeleteTextures(GLsizei n, GLuint* textures) def glDeleteTexture(texture): n = 1 textures = (ctypes.c_uint*n)(texture) try: nativefunc = glDeleteTexture._native except AttributeError: nativefunc = glDeleteTexture._native = _get_gl_func("glDeleteTextures", None, (ctypes.c_int, ctypes.POINTER(ctypes.c_uint),)) res = nativefunc(n, textures) # void = glDepthFunc(GLenum func) def glDepthFunc(func): try: nativefunc = glDepthFunc._native except AttributeError: nativefunc = glDepthFunc._native = _get_gl_func("glDepthFunc", None, (ctypes.c_uint,)) nativefunc(func) # void = glDepthMask(GLboolean flag) def glDepthMask(flag): try: nativefunc = glDepthMask._native except AttributeError: nativefunc = glDepthMask._native = _get_gl_func("glDepthMask", None, (ctypes.c_bool,)) nativefunc(flag) # void = glDepthRangef(GLclampf zNear, GLclampf zFar) def glDepthRange(zNear, zFar): try: nativefunc = glDepthRange._native except AttributeError: nativefunc = glDepthRange._native = _get_gl_func("glDepthRange", None, (ctypes.c_double, ctypes.c_double,)) nativefunc(zNear, zFar) # void = glDetachShader(GLuint program, GLuint shader) def glDetachShader(program, shader): try: nativefunc = glDetachShader._native except AttributeError: nativefunc = glDetachShader._native = _get_gl_func("glDetachShader", None, (ctypes.c_uint, ctypes.c_uint,)) nativefunc(program, shader) # void = glDisable(GLenum cap) def glDisable(cap): try: nativefunc = glDisable._native except AttributeError: nativefunc = glDisable._native = _get_gl_func("glDisable", None, (ctypes.c_uint,)) nativefunc(cap) # void = glDisableVertexAttribArray(GLuint index) def glDisableVertexAttribArray(index): try: nativefunc = glDisableVertexAttribArray._native except AttributeError: nativefunc = glDisableVertexAttribArray._native = _get_gl_func("glDisableVertexAttribArray", None, (ctypes.c_uint,)) nativefunc(index) # void = glDrawArrays(GLenum mode, GLint first, GLsizei count) def glDrawArrays(mode, first, count): try: nativefunc = glDrawArrays._native except AttributeError: nativefunc = glDrawArrays._native = _get_gl_func("glDrawArrays", None, (ctypes.c_uint, ctypes.c_int, ctypes.c_int,)) nativefunc(mode, first, count) # void = glDrawElements(GLenum mode, GLsizei count, GLenum type, GLvoid* indices) def glDrawElements(mode, count, type, offset): if offset is None: offset = ctypes.c_void_p(0) elif isinstance(offset, ctypes.c_void_p): pass elif isinstance(offset, (int, ctypes.c_int)): offset = ctypes.c_void_p(int(offset)) else: if not offset.flags['C_CONTIGUOUS']: offset = offset.copy('C') offset_ = offset offset = offset.ctypes.data indices = offset try: nativefunc = glDrawElements._native except AttributeError: nativefunc = glDrawElements._native = _get_gl_func("glDrawElements", None, (ctypes.c_uint, ctypes.c_int, ctypes.c_uint, ctypes.c_void_p,)) res = nativefunc(mode, count, type, indices) # void = glEnable(GLenum cap) def glEnable(cap): try: nativefunc = glEnable._native except AttributeError: nativefunc = glEnable._native = _get_gl_func("glEnable", None, (ctypes.c_uint,)) nativefunc(cap) # void = glEnableVertexAttribArray(GLuint index) def glEnableVertexAttribArray(index): try: nativefunc = glEnableVertexAttribArray._native except AttributeError: nativefunc = glEnableVertexAttribArray._native = _get_gl_func("glEnableVertexAttribArray", None, (ctypes.c_uint,)) nativefunc(index) # void = glFinish() def glFinish(): try: nativefunc = glFinish._native except AttributeError: nativefunc = glFinish._native = _get_gl_func("glFinish", None, ()) nativefunc() # void = glFlush() def glFlush(): try: nativefunc = glFlush._native except AttributeError: nativefunc = glFlush._native = _get_gl_func("glFlush", None, ()) nativefunc() # void = glFramebufferRenderbuffer(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) def glFramebufferRenderbuffer(target, attachment, renderbuffertarget, renderbuffer): try: nativefunc = glFramebufferRenderbuffer._native except AttributeError: nativefunc = glFramebufferRenderbuffer._native = _get_gl_func("glFramebufferRenderbuffer", None, (ctypes.c_uint, ctypes.c_uint, ctypes.c_uint, ctypes.c_uint,)) nativefunc(target, attachment, renderbuffertarget, renderbuffer) # void = glFramebufferTexture2D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) def glFramebufferTexture2D(target, attachment, textarget, texture, level): try: nativefunc = glFramebufferTexture2D._native except AttributeError: nativefunc = glFramebufferTexture2D._native = _get_gl_func("glFramebufferTexture2D", None, (ctypes.c_uint, ctypes.c_uint, ctypes.c_uint, ctypes.c_uint, ctypes.c_int,)) nativefunc(target, attachment, textarget, texture, level) # void = glFrontFace(GLenum mode) def glFrontFace(mode): try: nativefunc = glFrontFace._native except AttributeError: nativefunc = glFrontFace._native = _get_gl_func("glFrontFace", None, (ctypes.c_uint,)) nativefunc(mode) # void = glGenBuffers(GLsizei n, GLuint* buffers) def glCreateBuffer(): n = 1 buffers = (ctypes.c_uint*n)() try: nativefunc = glCreateBuffer._native except AttributeError: nativefunc = glCreateBuffer._native = _get_gl_func("glGenBuffers", None, (ctypes.c_int, ctypes.POINTER(ctypes.c_uint),)) res = nativefunc(n, buffers) return buffers[0] # void = glGenFramebuffers(GLsizei n, GLuint* framebuffers) def glCreateFramebuffer(): n = 1 framebuffers = (ctypes.c_uint*n)() try: nativefunc = glCreateFramebuffer._native except AttributeError: nativefunc = glCreateFramebuffer._native = _get_gl_func("glGenFramebuffers", None, (ctypes.c_int, ctypes.POINTER(ctypes.c_uint),)) res = nativefunc(n, framebuffers) return framebuffers[0] # void = glGenRenderbuffers(GLsizei n, GLuint* renderbuffers) def glCreateRenderbuffer(): n = 1 renderbuffers = (ctypes.c_uint*n)() try: nativefunc = glCreateRenderbuffer._native except AttributeError: nativefunc = glCreateRenderbuffer._native = _get_gl_func("glGenRenderbuffers", None, (ctypes.c_int, ctypes.POINTER(ctypes.c_uint),)) res = nativefunc(n, renderbuffers) return renderbuffers[0] # void = glGenTextures(GLsizei n, GLuint* textures) def glCreateTexture(): n = 1 textures = (ctypes.c_uint*n)() try: nativefunc = glCreateTexture._native except AttributeError: nativefunc = glCreateTexture._native = _get_gl_func("glGenTextures", None, (ctypes.c_int, ctypes.POINTER(ctypes.c_uint),)) res = nativefunc(n, textures) return textures[0] # void = glGenerateMipmap(GLenum target) def glGenerateMipmap(target): try: nativefunc = glGenerateMipmap._native except AttributeError: nativefunc = glGenerateMipmap._native = _get_gl_func("glGenerateMipmap", None, (ctypes.c_uint,)) nativefunc(target) # void = glGetActiveAttrib(GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, GLchar* name) def glGetActiveAttrib(program, index): bufsize = 256 length = (ctypes.c_int*1)() size = (ctypes.c_int*1)() type = (ctypes.c_uint*1)() name = ctypes.create_string_buffer(bufsize) try: nativefunc = glGetActiveAttrib._native except AttributeError: nativefunc = glGetActiveAttrib._native = _get_gl_func("glGetActiveAttrib", None, (ctypes.c_uint, ctypes.c_uint, ctypes.c_int, ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_uint), ctypes.c_char_p,)) res = nativefunc(program, index, bufsize, length, size, type, name) name = name[:length[0]].decode('utf-8') return name, size[0], type[0] # void = glGetActiveUniform(GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, GLchar* name) def glGetActiveUniform(program, index): bufsize = 256 length = (ctypes.c_int*1)() size = (ctypes.c_int*1)() type = (ctypes.c_uint*1)() name = ctypes.create_string_buffer(bufsize) try: nativefunc = glGetActiveUniform._native except AttributeError: nativefunc = glGetActiveUniform._native = _get_gl_func("glGetActiveUniform", None, (ctypes.c_uint, ctypes.c_uint, ctypes.c_int, ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_uint), ctypes.c_char_p,)) res = nativefunc(program, index, bufsize, length, size, type, name) name = name[:length[0]].decode('utf-8') return name, size[0], type[0] # void = glGetAttachedShaders(GLuint program, GLsizei maxcount, GLsizei* count, GLuint* shaders) def glGetAttachedShaders(program): maxcount = 256 count = (ctypes.c_int*1)() shaders = (ctypes.c_uint*maxcount)() try: nativefunc = glGetAttachedShaders._native except AttributeError: nativefunc = glGetAttachedShaders._native = _get_gl_func("glGetAttachedShaders", None, (ctypes.c_uint, ctypes.c_int, ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_uint),)) res = nativefunc(program, maxcount, count, shaders) return tuple(shaders[:count[0]]) # GLint = glGetAttribLocation(GLuint program, GLchar* name) def glGetAttribLocation(program, name): name = ctypes.c_char_p(name.encode('utf-8')) try: nativefunc = glGetAttribLocation._native except AttributeError: nativefunc = glGetAttribLocation._native = _get_gl_func("glGetAttribLocation", ctypes.c_int, (ctypes.c_uint, ctypes.c_char_p,)) res = nativefunc(program, name) return res # void = glGetBooleanv(GLenum pname, GLboolean* params) def _glGetBooleanv(pname): params = (ctypes.c_bool*1)() try: nativefunc = _glGetBooleanv._native except AttributeError: nativefunc = _glGetBooleanv._native = _get_gl_func("glGetBooleanv", None, (ctypes.c_uint, ctypes.POINTER(ctypes.c_bool),)) res = nativefunc(pname, params) return params[0] # void = glGetBufferParameteriv(GLenum target, GLenum pname, GLint* params) def glGetBufferParameter(target, pname): d = -2**31 # smallest 32bit integer params = (ctypes.c_int*1)(d) try: nativefunc = glGetBufferParameter._native except AttributeError: nativefunc = glGetBufferParameter._native = _get_gl_func("glGetBufferParameteriv", None, (ctypes.c_uint, ctypes.c_uint, ctypes.POINTER(ctypes.c_int),)) res = nativefunc(target, pname, params) return params[0] # GLenum = glGetError() def glGetError(): try: nativefunc = glGetError._native except AttributeError: nativefunc = glGetError._native = _get_gl_func("glGetError", ctypes.c_uint, ()) return nativefunc() # void = glGetFloatv(GLenum pname, GLfloat* params) def _glGetFloatv(pname): n = 16 d = float('Inf') params = (ctypes.c_float*n)(*[d for i in range(n)]) try: nativefunc = _glGetFloatv._native except AttributeError: nativefunc = _glGetFloatv._native = _get_gl_func("glGetFloatv", None, (ctypes.c_uint, ctypes.POINTER(ctypes.c_float),)) res = nativefunc(pname, params) params = [p for p in params if p!=d] if len(params) == 1: return params[0] else: return tuple(params) # void = glGetFramebufferAttachmentParameteriv(GLenum target, GLenum attachment, GLenum pname, GLint* params) def glGetFramebufferAttachmentParameter(target, attachment, pname): d = -2**31 # smallest 32bit integer params = (ctypes.c_int*1)(d) try: nativefunc = glGetFramebufferAttachmentParameter._native except AttributeError: nativefunc = glGetFramebufferAttachmentParameter._native = _get_gl_func("glGetFramebufferAttachmentParameteriv", None, (ctypes.c_uint, ctypes.c_uint, ctypes.c_uint, ctypes.POINTER(ctypes.c_int),)) res = nativefunc(target, attachment, pname, params) return params[0] # void = glGetIntegerv(GLenum pname, GLint* params) def _glGetIntegerv(pname): n = 16 d = -2**31 # smallest 32bit integer params = (ctypes.c_int*n)(*[d for i in range(n)]) try: nativefunc = _glGetIntegerv._native except AttributeError: nativefunc = _glGetIntegerv._native = _get_gl_func("glGetIntegerv", None, (ctypes.c_uint, ctypes.POINTER(ctypes.c_int),)) res = nativefunc(pname, params) params = [p for p in params if p!=d] if len(params) == 1: return params[0] else: return tuple(params) # void = glGetProgramInfoLog(GLuint program, GLsizei bufsize, GLsizei* length, GLchar* infolog) def glGetProgramInfoLog(program): bufsize = 1024 length = (ctypes.c_int*1)() infolog = ctypes.create_string_buffer(bufsize) try: nativefunc = glGetProgramInfoLog._native except AttributeError: nativefunc = glGetProgramInfoLog._native = _get_gl_func("glGetProgramInfoLog", None, (ctypes.c_uint, ctypes.c_int, ctypes.POINTER(ctypes.c_int), ctypes.c_char_p,)) res = nativefunc(program, bufsize, length, infolog) return infolog[:length[0]].decode('utf-8') # void = glGetProgramiv(GLuint program, GLenum pname, GLint* params) def glGetProgramParameter(program, pname): params = (ctypes.c_int*1)() try: nativefunc = glGetProgramParameter._native except AttributeError: nativefunc = glGetProgramParameter._native = _get_gl_func("glGetProgramiv", None, (ctypes.c_uint, ctypes.c_uint, ctypes.POINTER(ctypes.c_int),)) res = nativefunc(program, pname, params) return params[0] # void = glGetRenderbufferParameteriv(GLenum target, GLenum pname, GLint* params) def glGetRenderbufferParameter(target, pname): d = -2**31 # smallest 32bit integer params = (ctypes.c_int*1)(d) try: nativefunc = glGetRenderbufferParameter._native except AttributeError: nativefunc = glGetRenderbufferParameter._native = _get_gl_func("glGetRenderbufferParameteriv", None, (ctypes.c_uint, ctypes.c_uint, ctypes.POINTER(ctypes.c_int),)) res = nativefunc(target, pname, params) return params[0] # void = glGetShaderInfoLog(GLuint shader, GLsizei bufsize, GLsizei* length, GLchar* infolog) def glGetShaderInfoLog(shader): bufsize = 1024 length = (ctypes.c_int*1)() infolog = ctypes.create_string_buffer(bufsize) try: nativefunc = glGetShaderInfoLog._native except AttributeError: nativefunc = glGetShaderInfoLog._native = _get_gl_func("glGetShaderInfoLog", None, (ctypes.c_uint, ctypes.c_int, ctypes.POINTER(ctypes.c_int), ctypes.c_char_p,)) res = nativefunc(shader, bufsize, length, infolog) return infolog[:length[0]].decode('utf-8') # void = glGetShaderPrecisionFormat(GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision) def glGetShaderPrecisionFormat(shadertype, precisiontype): range = (ctypes.c_int*1)() precision = (ctypes.c_int*1)() try: nativefunc = glGetShaderPrecisionFormat._native except AttributeError: nativefunc = glGetShaderPrecisionFormat._native = _get_gl_func("glGetShaderPrecisionFormat", None, (ctypes.c_uint, ctypes.c_uint, ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int),)) res = nativefunc(shadertype, precisiontype, range, precision) return range[0], precision[0] # void = glGetShaderSource(GLuint shader, GLsizei bufsize, GLsizei* length, GLchar* source) def glGetShaderSource(shader): bufsize = 1024*1024 length = (ctypes.c_int*1)() source = (ctypes.c_char*bufsize)() try: nativefunc = glGetShaderSource._native except AttributeError: nativefunc = glGetShaderSource._native = _get_gl_func("glGetShaderSource", None, (ctypes.c_uint, ctypes.c_int, ctypes.POINTER(ctypes.c_int), ctypes.c_char_p,)) res = nativefunc(shader, bufsize, length, source) return source.value[:length[0]].decode('utf-8') # void = glGetShaderiv(GLuint shader, GLenum pname, GLint* params) def glGetShaderParameter(shader, pname): params = (ctypes.c_int*1)() try: nativefunc = glGetShaderParameter._native except AttributeError: nativefunc = glGetShaderParameter._native = _get_gl_func("glGetShaderiv", None, (ctypes.c_uint, ctypes.c_uint, ctypes.POINTER(ctypes.c_int),)) res = nativefunc(shader, pname, params) return params[0] # GLubyte* = glGetString(GLenum name) def glGetParameter(pname): if pname in [33902, 33901, 32773, 3106, 2931, 2928, 2849, 32824, 10752, 32938]: # GL_ALIASED_LINE_WIDTH_RANGE GL_ALIASED_POINT_SIZE_RANGE # GL_BLEND_COLOR GL_COLOR_CLEAR_VALUE GL_DEPTH_CLEAR_VALUE # GL_DEPTH_RANGE GL_LINE_WIDTH GL_POLYGON_OFFSET_FACTOR # GL_POLYGON_OFFSET_UNITS GL_SAMPLE_COVERAGE_VALUE return _glGetFloatv(pname) elif pname in [7936, 7937, 7938, 35724, 7939]: # GL_VENDOR, GL_RENDERER, GL_VERSION, GL_SHADING_LANGUAGE_VERSION, # GL_EXTENSIONS are strings pass # string handled below else: return _glGetIntegerv(pname) name = pname try: nativefunc = glGetParameter._native except AttributeError: nativefunc = glGetParameter._native = _get_gl_func("glGetString", ctypes.c_char_p, (ctypes.c_uint,)) res = nativefunc(name) return ctypes.string_at(res).decode('utf-8') if res else '' # void = glGetTexParameterfv(GLenum target, GLenum pname, GLfloat* params) def glGetTexParameter(target, pname): d = float('Inf') params = (ctypes.c_float*1)(d) try: nativefunc = glGetTexParameter._native except AttributeError: nativefunc = glGetTexParameter._native = _get_gl_func("glGetTexParameterfv", None, (ctypes.c_uint, ctypes.c_uint, ctypes.POINTER(ctypes.c_float),)) res = nativefunc(target, pname, params) return params[0] # void = glGetUniformfv(GLuint program, GLint location, GLfloat* params) def glGetUniform(program, location): n = 16 d = float('Inf') params = (ctypes.c_float*n)(*[d for i in range(n)]) try: nativefunc = glGetUniform._native except AttributeError: nativefunc = glGetUniform._native = _get_gl_func("glGetUniformfv", None, (ctypes.c_uint, ctypes.c_int, ctypes.POINTER(ctypes.c_float),)) res = nativefunc(program, location, params) params = [p for p in params if p!=d] if len(params) == 1: return params[0] else: return tuple(params) # GLint = glGetUniformLocation(GLuint program, GLchar* name) def glGetUniformLocation(program, name): name = ctypes.c_char_p(name.encode('utf-8')) try: nativefunc = glGetUniformLocation._native except AttributeError: nativefunc = glGetUniformLocation._native = _get_gl_func("glGetUniformLocation", ctypes.c_int, (ctypes.c_uint, ctypes.c_char_p,)) res = nativefunc(program, name) return res # void = glGetVertexAttribfv(GLuint index, GLenum pname, GLfloat* params) def glGetVertexAttrib(index, pname): n = 4 d = float('Inf') params = (ctypes.c_float*n)(*[d for i in range(n)]) try: nativefunc = glGetVertexAttrib._native except AttributeError: nativefunc = glGetVertexAttrib._native = _get_gl_func("glGetVertexAttribfv", None, (ctypes.c_uint, ctypes.c_uint, ctypes.POINTER(ctypes.c_float),)) res = nativefunc(index, pname, params) params = [p for p in params if p!=d] if len(params) == 1: return params[0] else: return tuple(params) # void = glGetVertexAttribPointerv(GLuint index, GLenum pname, GLvoid** pointer) def glGetVertexAttribOffset(index, pname): pointer = (ctypes.c_void_p*1)() try: nativefunc = glGetVertexAttribOffset._native except AttributeError: nativefunc = glGetVertexAttribOffset._native = _get_gl_func("glGetVertexAttribPointerv", None, (ctypes.c_uint, ctypes.c_uint, ctypes.POINTER(ctypes.c_void_p),)) res = nativefunc(index, pname, pointer) return pointer[0] or 0 # void = glHint(GLenum target, GLenum mode) def glHint(target, mode): try: nativefunc = glHint._native except AttributeError: nativefunc = glHint._native = _get_gl_func("glHint", None, (ctypes.c_uint, ctypes.c_uint,)) nativefunc(target, mode) # GLboolean = glIsBuffer(GLuint buffer) def glIsBuffer(buffer): try: nativefunc = glIsBuffer._native except AttributeError: nativefunc = glIsBuffer._native = _get_gl_func("glIsBuffer", ctypes.c_bool, (ctypes.c_uint,)) return nativefunc(buffer) # GLboolean = glIsEnabled(GLenum cap) def glIsEnabled(cap): try: nativefunc = glIsEnabled._native except AttributeError: nativefunc = glIsEnabled._native = _get_gl_func("glIsEnabled", ctypes.c_bool, (ctypes.c_uint,)) return nativefunc(cap) # GLboolean = glIsFramebuffer(GLuint framebuffer) def glIsFramebuffer(framebuffer): try: nativefunc = glIsFramebuffer._native except AttributeError: nativefunc = glIsFramebuffer._native = _get_gl_func("glIsFramebuffer", ctypes.c_bool, (ctypes.c_uint,)) return nativefunc(framebuffer) # GLboolean = glIsProgram(GLuint program) def glIsProgram(program): try: nativefunc = glIsProgram._native except AttributeError: nativefunc = glIsProgram._native = _get_gl_func("glIsProgram", ctypes.c_bool, (ctypes.c_uint,)) return nativefunc(program) # GLboolean = glIsRenderbuffer(GLuint renderbuffer) def glIsRenderbuffer(renderbuffer): try: nativefunc = glIsRenderbuffer._native except AttributeError: nativefunc = glIsRenderbuffer._native = _get_gl_func("glIsRenderbuffer", ctypes.c_bool, (ctypes.c_uint,)) return nativefunc(renderbuffer) # GLboolean = glIsShader(GLuint shader) def glIsShader(shader): try: nativefunc = glIsShader._native except AttributeError: nativefunc = glIsShader._native = _get_gl_func("glIsShader", ctypes.c_bool, (ctypes.c_uint,)) return nativefunc(shader) # GLboolean = glIsTexture(GLuint texture) def glIsTexture(texture): try: nativefunc = glIsTexture._native except AttributeError: nativefunc = glIsTexture._native = _get_gl_func("glIsTexture", ctypes.c_bool, (ctypes.c_uint,)) return nativefunc(texture) # void = glLineWidth(GLfloat width) def glLineWidth(width): try: nativefunc = glLineWidth._native except AttributeError: nativefunc = glLineWidth._native = _get_gl_func("glLineWidth", None, (ctypes.c_float,)) nativefunc(width) # void = glLinkProgram(GLuint program) def glLinkProgram(program): try: nativefunc = glLinkProgram._native except AttributeError: nativefunc = glLinkProgram._native = _get_gl_func("glLinkProgram", None, (ctypes.c_uint,)) nativefunc(program) # void = glPixelStorei(GLenum pname, GLint param) def glPixelStorei(pname, param): try: nativefunc = glPixelStorei._native except AttributeError: nativefunc = glPixelStorei._native = _get_gl_func("glPixelStorei", None, (ctypes.c_uint, ctypes.c_int,)) nativefunc(pname, param) # void = glPolygonOffset(GLfloat factor, GLfloat units) def glPolygonOffset(factor, units): try: nativefunc = glPolygonOffset._native except AttributeError: nativefunc = glPolygonOffset._native = _get_gl_func("glPolygonOffset", None, (ctypes.c_float, ctypes.c_float,)) nativefunc(factor, units) # void = glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid* pixels) def glReadPixels(x, y, width, height, format, type): # GL_ALPHA, GL_RGB, GL_RGBA t = {6406:1, 6407:3, 6408:4}[format] # GL_UNSIGNED_BYTE, GL_FLOAT nb = {5121:1, 5126:4}[type] size = int(width*height*t*nb) pixels = ctypes.create_string_buffer(size) try: nativefunc = glReadPixels._native except AttributeError: nativefunc = glReadPixels._native = _get_gl_func("glReadPixels", None, (ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_uint, ctypes.c_uint, ctypes.c_void_p,)) res = nativefunc(x, y, width, height, format, type, pixels) return pixels[:] # void = glRenderbufferStorage(GLenum target, GLenum internalformat, GLsizei width, GLsizei height) def glRenderbufferStorage(target, internalformat, width, height): try: nativefunc = glRenderbufferStorage._native except AttributeError: nativefunc = glRenderbufferStorage._native = _get_gl_func("glRenderbufferStorage", None, (ctypes.c_uint, ctypes.c_uint, ctypes.c_int, ctypes.c_int,)) nativefunc(target, internalformat, width, height) # void = glSampleCoverage(GLclampf value, GLboolean invert) def glSampleCoverage(value, invert): try: nativefunc = glSampleCoverage._native except AttributeError: nativefunc = glSampleCoverage._native = _get_gl_func("glSampleCoverage", None, (ctypes.c_float, ctypes.c_bool,)) nativefunc(value, invert) # void = glScissor(GLint x, GLint y, GLsizei width, GLsizei height) def glScissor(x, y, width, height): try: nativefunc = glScissor._native except AttributeError: nativefunc = glScissor._native = _get_gl_func("glScissor", None, (ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int,)) nativefunc(x, y, width, height) # void = glShaderSource(GLuint shader, GLsizei count, GLchar** string, GLint* length) def glShaderSource(shader, source): # Some implementation do not like getting a list of single chars if isinstance(source, (tuple, list)): strings = [s for s in source] else: strings = [source] count = len(strings) string = (ctypes.c_char_p*count)(*[s.encode('utf-8') for s in strings]) length = (ctypes.c_int*count)(*[len(s) for s in strings]) try: nativefunc = glShaderSource._native except AttributeError: nativefunc = glShaderSource._native = _get_gl_func("glShaderSource", None, (ctypes.c_uint, ctypes.c_int, ctypes.POINTER(ctypes.c_char_p), ctypes.POINTER(ctypes.c_int),)) res = nativefunc(shader, count, string, length) # void = glStencilFunc(GLenum func, GLint ref, GLuint mask) def glStencilFunc(func, ref, mask): try: nativefunc = glStencilFunc._native except AttributeError: nativefunc = glStencilFunc._native = _get_gl_func("glStencilFunc", None, (ctypes.c_uint, ctypes.c_int, ctypes.c_uint,)) nativefunc(func, ref, mask) # void = glStencilFuncSeparate(GLenum face, GLenum func, GLint ref, GLuint mask) def glStencilFuncSeparate(face, func, ref, mask): try: nativefunc = glStencilFuncSeparate._native except AttributeError: nativefunc = glStencilFuncSeparate._native = _get_gl_func("glStencilFuncSeparate", None, (ctypes.c_uint, ctypes.c_uint, ctypes.c_int, ctypes.c_uint,)) nativefunc(face, func, ref, mask) # void = glStencilMask(GLuint mask) def glStencilMask(mask): try: nativefunc = glStencilMask._native except AttributeError: nativefunc = glStencilMask._native = _get_gl_func("glStencilMask", None, (ctypes.c_uint,)) nativefunc(mask) # void = glStencilMaskSeparate(GLenum face, GLuint mask) def glStencilMaskSeparate(face, mask): try: nativefunc = glStencilMaskSeparate._native except AttributeError: nativefunc = glStencilMaskSeparate._native = _get_gl_func("glStencilMaskSeparate", None, (ctypes.c_uint, ctypes.c_uint,)) nativefunc(face, mask) # void = glStencilOp(GLenum fail, GLenum zfail, GLenum zpass) def glStencilOp(fail, zfail, zpass): try: nativefunc = glStencilOp._native except AttributeError: nativefunc = glStencilOp._native = _get_gl_func("glStencilOp", None, (ctypes.c_uint, ctypes.c_uint, ctypes.c_uint,)) nativefunc(fail, zfail, zpass) # void = glStencilOpSeparate(GLenum face, GLenum fail, GLenum zfail, GLenum zpass) def glStencilOpSeparate(face, fail, zfail, zpass): try: nativefunc = glStencilOpSeparate._native except AttributeError: nativefunc = glStencilOpSeparate._native = _get_gl_func("glStencilOpSeparate", None, (ctypes.c_uint, ctypes.c_uint, ctypes.c_uint, ctypes.c_uint,)) nativefunc(face, fail, zfail, zpass) # void = glTexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, GLvoid* pixels) def glTexImage2D(target, level, internalformat, format, type, pixels): border = 0 if isinstance(pixels, (tuple, list)): height, width = pixels pixels = ctypes.c_void_p(0) pixels = None else: if not pixels.flags['C_CONTIGUOUS']: pixels = pixels.copy('C') pixels_ = pixels pixels = pixels_.ctypes.data height, width = pixels_.shape[:2] try: nativefunc = glTexImage2D._native except AttributeError: nativefunc = glTexImage2D._native = _get_gl_func("glTexImage2D", None, (ctypes.c_uint, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_uint, ctypes.c_uint, ctypes.c_void_p,)) res = nativefunc(target, level, internalformat, width, height, border, format, type, pixels) def glTexParameterf(target, pname, param): try: nativefunc = glTexParameterf._native except AttributeError: nativefunc = glTexParameterf._native = _get_gl_func("glTexParameterf", None, (ctypes.c_uint, ctypes.c_uint, ctypes.c_float,)) nativefunc(target, pname, param) def glTexParameteri(target, pname, param): try: nativefunc = glTexParameteri._native except AttributeError: nativefunc = glTexParameteri._native = _get_gl_func("glTexParameteri", None, (ctypes.c_uint, ctypes.c_uint, ctypes.c_int,)) nativefunc(target, pname, param) # void = glTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid* pixels) def glTexSubImage2D(target, level, xoffset, yoffset, format, type, pixels): if not pixels.flags['C_CONTIGUOUS']: pixels = pixels.copy('C') pixels_ = pixels pixels = pixels_.ctypes.data height, width = pixels_.shape[:2] try: nativefunc = glTexSubImage2D._native except AttributeError: nativefunc = glTexSubImage2D._native = _get_gl_func("glTexSubImage2D", None, (ctypes.c_uint, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_uint, ctypes.c_uint, ctypes.c_void_p,)) res = nativefunc(target, level, xoffset, yoffset, width, height, format, type, pixels) def glUniform1f(location, v1): try: nativefunc = glUniform1f._native except AttributeError: nativefunc = glUniform1f._native = _get_gl_func("glUniform1f", None, (ctypes.c_int, ctypes.c_float,)) nativefunc(location, v1) def glUniform2f(location, v1, v2): try: nativefunc = glUniform2f._native except AttributeError: nativefunc = glUniform2f._native = _get_gl_func("glUniform2f", None, (ctypes.c_int, ctypes.c_float, ctypes.c_float,)) nativefunc(location, v1, v2) def glUniform3f(location, v1, v2, v3): try: nativefunc = glUniform3f._native except AttributeError: nativefunc = glUniform3f._native = _get_gl_func("glUniform3f", None, (ctypes.c_int, ctypes.c_float, ctypes.c_float, ctypes.c_float,)) nativefunc(location, v1, v2, v3) def glUniform4f(location, v1, v2, v3, v4): try: nativefunc = glUniform4f._native except AttributeError: nativefunc = glUniform4f._native = _get_gl_func("glUniform4f", None, (ctypes.c_int, ctypes.c_float, ctypes.c_float, ctypes.c_float, ctypes.c_float,)) nativefunc(location, v1, v2, v3, v4) def glUniform1i(location, v1): try: nativefunc = glUniform1i._native except AttributeError: nativefunc = glUniform1i._native = _get_gl_func("glUniform1i", None, (ctypes.c_int, ctypes.c_int,)) nativefunc(location, v1) def glUniform2i(location, v1, v2): try: nativefunc = glUniform2i._native except AttributeError: nativefunc = glUniform2i._native = _get_gl_func("glUniform2i", None, (ctypes.c_int, ctypes.c_int, ctypes.c_int,)) nativefunc(location, v1, v2) def glUniform3i(location, v1, v2, v3): try: nativefunc = glUniform3i._native except AttributeError: nativefunc = glUniform3i._native = _get_gl_func("glUniform3i", None, (ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int,)) nativefunc(location, v1, v2, v3) def glUniform4i(location, v1, v2, v3, v4): try: nativefunc = glUniform4i._native except AttributeError: nativefunc = glUniform4i._native = _get_gl_func("glUniform4i", None, (ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int,)) nativefunc(location, v1, v2, v3, v4) def glUniform1fv(location, count, values): values = [float(val) for val in values] values = (ctypes.c_float*len(values))(*values) try: nativefunc = glUniform1fv._native except AttributeError: nativefunc = glUniform1fv._native = _get_gl_func("glUniform1fv", None, (ctypes.c_int, ctypes.c_int, ctypes.POINTER(ctypes.c_float),)) nativefunc(location, count, values) def glUniform2fv(location, count, values): values = [float(val) for val in values] values = (ctypes.c_float*len(values))(*values) try: nativefunc = glUniform2fv._native except AttributeError: nativefunc = glUniform2fv._native = _get_gl_func("glUniform2fv", None, (ctypes.c_int, ctypes.c_int, ctypes.POINTER(ctypes.c_float),)) nativefunc(location, count, values) def glUniform3fv(location, count, values): values = [float(val) for val in values] values = (ctypes.c_float*len(values))(*values) try: nativefunc = glUniform3fv._native except AttributeError: nativefunc = glUniform3fv._native = _get_gl_func("glUniform3fv", None, (ctypes.c_int, ctypes.c_int, ctypes.POINTER(ctypes.c_float),)) nativefunc(location, count, values) def glUniform4fv(location, count, values): values = [float(val) for val in values] values = (ctypes.c_float*len(values))(*values) try: nativefunc = glUniform4fv._native except AttributeError: nativefunc = glUniform4fv._native = _get_gl_func("glUniform4fv", None, (ctypes.c_int, ctypes.c_int, ctypes.POINTER(ctypes.c_float),)) nativefunc(location, count, values) def glUniform1iv(location, count, values): values = [int(val) for val in values] values = (ctypes.c_int*len(values))(*values) try: nativefunc = glUniform1iv._native except AttributeError: nativefunc = glUniform1iv._native = _get_gl_func("glUniform1iv", None, (ctypes.c_int, ctypes.c_int, ctypes.POINTER(ctypes.c_int),)) nativefunc(location, count, values) def glUniform2iv(location, count, values): values = [int(val) for val in values] values = (ctypes.c_int*len(values))(*values) try: nativefunc = glUniform2iv._native except AttributeError: nativefunc = glUniform2iv._native = _get_gl_func("glUniform2iv", None, (ctypes.c_int, ctypes.c_int, ctypes.POINTER(ctypes.c_int),)) nativefunc(location, count, values) def glUniform3iv(location, count, values): values = [int(val) for val in values] values = (ctypes.c_int*len(values))(*values) try: nativefunc = glUniform3iv._native except AttributeError: nativefunc = glUniform3iv._native = _get_gl_func("glUniform3iv", None, (ctypes.c_int, ctypes.c_int, ctypes.POINTER(ctypes.c_int),)) nativefunc(location, count, values) def glUniform4iv(location, count, values): values = [int(val) for val in values] values = (ctypes.c_int*len(values))(*values) try: nativefunc = glUniform4iv._native except AttributeError: nativefunc = glUniform4iv._native = _get_gl_func("glUniform4iv", None, (ctypes.c_int, ctypes.c_int, ctypes.POINTER(ctypes.c_int),)) nativefunc(location, count, values) def glUniformMatrix2fv(location, count, transpose, values): if not values.flags["C_CONTIGUOUS"]: values = values.copy() assert values.dtype.name == "float32" values_ = values values = values_.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) try: nativefunc = glUniformMatrix2fv._native except AttributeError: nativefunc = glUniformMatrix2fv._native = _get_gl_func("glUniformMatrix2fv", None, (ctypes.c_int, ctypes.c_int, ctypes.c_bool, ctypes.POINTER(ctypes.c_float),)) nativefunc(location, count, transpose, values) def glUniformMatrix3fv(location, count, transpose, values): if not values.flags["C_CONTIGUOUS"]: values = values.copy() assert values.dtype.name == "float32" values_ = values values = values_.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) try: nativefunc = glUniformMatrix3fv._native except AttributeError: nativefunc = glUniformMatrix3fv._native = _get_gl_func("glUniformMatrix3fv", None, (ctypes.c_int, ctypes.c_int, ctypes.c_bool, ctypes.POINTER(ctypes.c_float),)) nativefunc(location, count, transpose, values) def glUniformMatrix4fv(location, count, transpose, values): if not values.flags["C_CONTIGUOUS"]: values = values.copy() assert values.dtype.name == "float32" values_ = values values = values_.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) try: nativefunc = glUniformMatrix4fv._native except AttributeError: nativefunc = glUniformMatrix4fv._native = _get_gl_func("glUniformMatrix4fv", None, (ctypes.c_int, ctypes.c_int, ctypes.c_bool, ctypes.POINTER(ctypes.c_float),)) nativefunc(location, count, transpose, values) # void = glUseProgram(GLuint program) def glUseProgram(program): try: nativefunc = glUseProgram._native except AttributeError: nativefunc = glUseProgram._native = _get_gl_func("glUseProgram", None, (ctypes.c_uint,)) nativefunc(program) # void = glValidateProgram(GLuint program) def glValidateProgram(program): try: nativefunc = glValidateProgram._native except AttributeError: nativefunc = glValidateProgram._native = _get_gl_func("glValidateProgram", None, (ctypes.c_uint,)) nativefunc(program) def glVertexAttrib1f(index, v1): try: nativefunc = glVertexAttrib1f._native except AttributeError: nativefunc = glVertexAttrib1f._native = _get_gl_func("glVertexAttrib1f", None, (ctypes.c_uint, ctypes.c_float,)) nativefunc(index, v1) def glVertexAttrib2f(index, v1, v2): try: nativefunc = glVertexAttrib2f._native except AttributeError: nativefunc = glVertexAttrib2f._native = _get_gl_func("glVertexAttrib2f", None, (ctypes.c_uint, ctypes.c_float, ctypes.c_float,)) nativefunc(index, v1, v2) def glVertexAttrib3f(index, v1, v2, v3): try: nativefunc = glVertexAttrib3f._native except AttributeError: nativefunc = glVertexAttrib3f._native = _get_gl_func("glVertexAttrib3f", None, (ctypes.c_uint, ctypes.c_float, ctypes.c_float, ctypes.c_float,)) nativefunc(index, v1, v2, v3) def glVertexAttrib4f(index, v1, v2, v3, v4): try: nativefunc = glVertexAttrib4f._native except AttributeError: nativefunc = glVertexAttrib4f._native = _get_gl_func("glVertexAttrib4f", None, (ctypes.c_uint, ctypes.c_float, ctypes.c_float, ctypes.c_float, ctypes.c_float,)) nativefunc(index, v1, v2, v3, v4) # void = glVertexAttribPointer(GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, GLvoid* ptr) def glVertexAttribPointer(indx, size, type, normalized, stride, offset): if offset is None: offset = ctypes.c_void_p(0) elif isinstance(offset, ctypes.c_void_p): pass elif isinstance(offset, (int, ctypes.c_int)): offset = ctypes.c_void_p(int(offset)) else: if not offset.flags['C_CONTIGUOUS']: offset = offset.copy('C') offset_ = offset offset = offset.ctypes.data # We need to ensure that the data exists at draw time :( # PyOpenGL does this too key = '_vert_attr_'+str(indx) setattr(glVertexAttribPointer, key, offset_) ptr = offset try: nativefunc = glVertexAttribPointer._native except AttributeError: nativefunc = glVertexAttribPointer._native = _get_gl_func("glVertexAttribPointer", None, (ctypes.c_uint, ctypes.c_int, ctypes.c_uint, ctypes.c_bool, ctypes.c_int, ctypes.c_void_p,)) res = nativefunc(indx, size, type, normalized, stride, ptr) # void = glViewport(GLint x, GLint y, GLsizei width, GLsizei height) def glViewport(x, y, width, height): try: nativefunc = glViewport._native except AttributeError: nativefunc = glViewport._native = _get_gl_func("glViewport", None, (ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int,)) nativefunc(x, y, width, height) ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/gloo/gl/tests/��������������������������������������������������������������������0000775�0001750�0001750�00000000000�12527674621�020255� 5����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/gloo/gl/tests/test_functionality.py�����������������������������������������������0000664�0001750�0001750�00000041057�12510536123�024547� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������""" Test to verify the functionality of the OpenGL backends. This test sets up a real visualization with shaders and all. This tests setting source code, setting texture and buffer data, and we touch many other functions of the API too. The end result is an image with four colored quads. The result is tested for pixel color. The visualization ----------------- We create a visualization where the screen is divided in 4 quadrants, and each quadrant is drawn a different color (black, red, green, blue). The drawing is done for 50% using attribute data, and 50% using a texture. The end result should be fully saturated colors. Remember: the bottom left is (-1, -1) and the first quadrant. """ import sys import numpy as np from vispy.app import Canvas from vispy.testing import (requires_application, requires_pyopengl, SkipTest, run_tests_if_main, assert_equal, assert_true) from vispy.gloo import gl # All these tests require a working backend. ## High level tests def teardown_module(): gl.use_gl() # Reset to default @requires_application() def test_functionality_desktop(): """ Test desktop GL backend for full functionality. """ _test_functonality('gl2') @requires_application() def test_functionality_proxy(): """ Test GL proxy class for full functionality. """ # By using debug mode, we are using the proxy class _test_functonality('gl2 debug') @requires_application() @requires_pyopengl() def test_functionality_pyopengl(): """ Test pyopengl GL backend for full functionality. """ _test_functonality('pyopengl2') @requires_application() def test_functionality_es2(): """ Test es2 GL backend for full functionality. """ if True: raise SkipTest('Skip es2 functionality test for now.') if not sys.platform.startswith('win'): raise SkipTest('Can only test es2 functionality on Windows.') _test_functonality('es2') def _clear_screen(): gl.glClear(gl.GL_COLOR_BUFFER_BIT) gl.glFinish() def _test_functonality(backend): """ Create app and canvas so we have a context. Then run tests. """ # use the backend gl.use_gl(backend) with Canvas() as canvas: _clear_screen() # Prepare w, h = canvas.size gl.glViewport(0, 0, w, h) gl.glScissor(0, 0, w, h) # touch gl.glClearColor(0.0, 0.0, 0.0, 1.0) # Setup visualization, ensure to do it in a draw event objects = _prepare_vis() _clear_screen() _draw1() _clear_screen() _draw2() _clear_screen() _draw3() # Clean up for delete_func, handle in objects: delete_func(handle) gl.glFinish() ## Create CPU data # Create vertex and fragments shader. They are designed to that all # OpenGL func can be tested, i.e. all types of uniforms are present. # Most variables are nullified however, but we must make sure we do this # in a way that the compiler won't optimize out :) VERT = """ attribute float a_1; attribute vec2 a_2; attribute vec3 a_3; attribute vec4 a_4; uniform float u_f1; uniform vec2 u_f2; uniform vec3 u_f3; uniform vec4 u_f4; uniform int u_i1; uniform ivec2 u_i2; uniform ivec3 u_i3; uniform ivec4 u_i4; uniform mat2 u_m2; uniform mat3 u_m3; uniform mat4 u_m4; varying vec2 v_2; // tex coords varying vec4 v_4; // attr colors void main() { float zero = float(u_i1); // Combine int with float uniforms (i.e. ints are "used") float u1 = u_f1 + float(u_i1); vec2 u2 = u_f2 + vec2(u_i2); vec3 u3 = u_f3 + vec3(u_i3); vec4 u4 = u_f4 + vec4(u_i4); // Set varyings (use every 2D and 4D variable, and u1) v_2 = a_1 * a_2 + zero*u_m2 * a_2 * u2 * u1; v_4 = u_m4 * a_4 * u4; // Set position (use 3D variables) gl_Position = vec4(u_m3* a_3* u3, 1.0); } """ FRAG = """ uniform sampler2D s_1; uniform int u_i1; varying vec2 v_2; // rex coords varying vec4 v_4; // attr colors void main() { float zero = float(u_i1); gl_FragColor = (texture2D(s_1, v_2) + v_4); } """ # Color texture texquad = 5 im1 = np.zeros((texquad*2, texquad*2, 3), np.uint8) im1[texquad:, :texquad, 0] = 128 im1[texquad:, texquad:, 1] = 128 im1[:texquad, texquad:, 2] = 128 # Grayscale texture (uploaded but not used) im2 = im1[:, :, 0] # A non-contiguous view assert im2.flags['C_CONTIGUOUS'] is False # Vertex Buffers # Create coordinates for upper left quad quad = np.array([[0, 0, 0], [-1, 0, 0], [-1, -1, 0], [0, 0, 0], [-1, -1, 0], [0, -1, 0]], np.float32) N = quad.shape[0] * 4 # buf3 contains coordinates in device coordinates for four quadrants buf3 = np.row_stack([quad + (0, 0, 0), quad + (0, 1, 0), quad + (1, 1, 0), quad + (1, 0, 0)]).astype(np.float32) # buf2 is texture coords. Note that this is a view on buf2 buf2 = ((buf3+1.0)*0.5)[:, :2] # not C-contiguous assert buf2.flags['C_CONTIGUOUS'] is False # Array of colors buf4 = np.zeros((N, 5), np.float32) buf4[6:12, 0] = 0.5 buf4[12:18, 1] = 0.5 buf4[18:24, 2] = 0.5 buf4[:, 3] = 1.0 # alpha buf4 = buf4[:, :4] # make non-contiguous # Element buffer # elements = np.arange(N, dtype=np.uint8) # C-contiguous elements = np.arange(0, N, 0.5).astype(np.uint8)[::2] # not C-contiguous helements = None # the OpenGL object ref ## The GL calls def _prepare_vis(): objects = [] # --- program and shaders # Create program and shaders hprog = gl.glCreateProgram() hvert = gl.glCreateShader(gl.GL_VERTEX_SHADER) hfrag = gl.glCreateShader(gl.GL_FRAGMENT_SHADER) objects.append((gl.glDeleteProgram, hprog)) objects.append((gl.glDeleteShader, hvert)) objects.append((gl.glDeleteShader, hfrag)) # Compile source code gl.glShaderSource(hvert, VERT) gl.glShaderSource(hfrag, FRAG) gl.glCompileShader(hvert) gl.glCompileShader(hfrag) # Check assert_equal(gl.glGetShaderInfoLog(hvert), '') assert_equal(gl.glGetShaderInfoLog(hfrag), '') assert_equal(gl.glGetShaderParameter(hvert, gl.GL_COMPILE_STATUS), 1) assert_equal(gl.glGetShaderParameter(hfrag, gl.GL_COMPILE_STATUS), 1) # Attach and link gl.glAttachShader(hprog, hvert) gl.glAttachShader(hprog, hfrag) # touch glDetachShader gl.glDetachShader(hprog, hvert) gl.glAttachShader(hprog, hvert) gl.glLinkProgram(hprog) # Test that indeed these shaders are attached attached_shaders = gl.glGetAttachedShaders(hprog) assert_equal(set(attached_shaders), set([hvert, hfrag])) # Check assert_equal(gl.glGetProgramInfoLog(hprog), '') assert_equal(gl.glGetProgramParameter(hprog, gl.GL_LINK_STATUS), 1) gl.glValidateProgram(hprog) assert_equal(gl.glGetProgramParameter(hprog, gl.GL_VALIDATE_STATUS), 1) # Use it! gl.glUseProgram(hprog) # Bind one attribute gl.glBindAttribLocation(hprog, 1, 'a_2') # Check if all is ok assert_equal(gl.glGetError(), 0) # Check source vert_source = gl.glGetShaderSource(hvert) assert_true('attribute vec2 a_2;' in vert_source) # --- get information on attributes and uniforms # Count attribbutes and uniforms natt = gl.glGetProgramParameter(hprog, gl.GL_ACTIVE_ATTRIBUTES) nuni = gl.glGetProgramParameter(hprog, gl.GL_ACTIVE_UNIFORMS) assert_equal(natt, 4) assert_equal(nuni, 4+4+3+1) # Get names names = {} for i in range(natt): name, count, type = gl.glGetActiveAttrib(hprog, i) names[name] = type assert_equal(count, 1) for i in range(nuni): name, count, type = gl.glGetActiveUniform(hprog, i) names[name] = type assert_equal(count, 1) # Check assert_equal(names['a_1'], gl.GL_FLOAT) assert_equal(names['a_2'], gl.GL_FLOAT_VEC2) assert_equal(names['a_3'], gl.GL_FLOAT_VEC3) assert_equal(names['a_4'], gl.GL_FLOAT_VEC4) assert_equal(names['s_1'], gl.GL_SAMPLER_2D) # for i, type in enumerate([gl.GL_FLOAT, gl.GL_FLOAT_VEC2, gl.GL_FLOAT_VEC3, gl.GL_FLOAT_VEC4]): assert_equal(names['u_f%i' % (i+1)], type) for i, type in enumerate([gl.GL_INT, gl.GL_INT_VEC2, gl.GL_INT_VEC3, gl.GL_INT_VEC4]): assert_equal(names['u_i%i' % (i+1)], type) for i, type in enumerate([gl.GL_FLOAT_MAT2, gl.GL_FLOAT_MAT3, gl.GL_FLOAT_MAT4]): assert_equal(names['u_m%i' % (i+2)], type) # Check if all is ok assert_equal(gl.glGetError(), 0) # --- texture # Create, bind, activate htex = gl.glCreateTexture() objects.append((gl.glDeleteTexture, htex)) gl.glPixelStorei(gl.GL_UNPACK_ALIGNMENT, 1) gl.glBindTexture(gl.GL_TEXTURE_2D, htex) # Allocate data and upload # This data is luminance and not C-contiguous gl.glTexImage2D(gl.GL_TEXTURE_2D, 0, gl.GL_LUMINANCE, gl.GL_LUMINANCE, gl.GL_UNSIGNED_BYTE, im2) # touch gl.glTexImage2D(gl.GL_TEXTURE_2D, 0, gl.GL_LUMINANCE, gl.GL_LUMINANCE, gl.GL_UNSIGNED_BYTE, im2.shape[:2]) gl.glTexSubImage2D(gl.GL_TEXTURE_2D, 0, 0, 0, gl.GL_LUMINANCE, gl.GL_UNSIGNED_BYTE, im2) # Set texture parameters (use f and i to touch both) T = gl.GL_TEXTURE_2D gl.glTexParameterf(T, gl.GL_TEXTURE_MIN_FILTER, gl.GL_LINEAR) gl.glTexParameteri(T, gl.GL_TEXTURE_MAG_FILTER, gl.GL_LINEAR) # Re-allocate data and upload gl.glTexImage2D(gl.GL_TEXTURE_2D, 0, gl.GL_RGB, gl.GL_RGB, gl.GL_UNSIGNED_BYTE, im1.shape[:2]) gl.glTexSubImage2D(gl.GL_TEXTURE_2D, 0, 0, 0, gl.GL_RGB, gl.GL_UNSIGNED_BYTE, im1) # Attach! loc = gl.glGetUniformLocation(hprog, 's_1') unit = 0 gl.glActiveTexture(gl.GL_TEXTURE0+unit) gl.glUniform1i(loc, unit) # Mipmaps (just to touch this function) gl.glGenerateMipmap(gl.GL_TEXTURE_2D) # Check min filter (touch getTextParameter) minfilt = gl.glGetTexParameter(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MIN_FILTER) assert_equal(minfilt, gl.GL_LINEAR) # Check if all is ok assert_equal(gl.glGetError(), 0) # --- buffer vec2 (contiguous VBO) # Create buffer hbuf2 = gl.glCreateBuffer() objects.append((gl.glDeleteBuffer, hbuf2)) gl.glBindBuffer(gl.GL_ARRAY_BUFFER, hbuf2) # Allocate and set data gl.glBufferData(gl.GL_ARRAY_BUFFER, buf2.nbytes, gl.GL_DYNAMIC_DRAW) gl.glBufferSubData(gl.GL_ARRAY_BUFFER, 0, buf2) # Attach! loc = gl.glGetAttribLocation(hprog, 'a_2') gl.glDisableVertexAttribArray(loc) # touch gl.glEnableVertexAttribArray(loc) gl.glVertexAttribPointer(loc, 2, gl.GL_FLOAT, False, 2*4, 0) # Check (touch glGetBufferParameter, glGetVertexAttrib and # glGetVertexAttribOffset) size = gl.glGetBufferParameter(gl.GL_ARRAY_BUFFER, gl.GL_BUFFER_SIZE) assert_equal(size, buf2.nbytes) stride = gl.glGetVertexAttrib(loc, gl.GL_VERTEX_ATTRIB_ARRAY_STRIDE) assert_equal(stride, 2*4) offset = gl.glGetVertexAttribOffset(loc, gl.GL_VERTEX_ATTRIB_ARRAY_POINTER) assert_equal(offset, 0) # Check if all is ok assert_equal(gl.glGetError(), 0) # --- buffer vec3 (non-contiguous VBO) # Create buffer hbuf3 = gl.glCreateBuffer() objects.append((gl.glDeleteBuffer, hbuf3)) gl.glBindBuffer(gl.GL_ARRAY_BUFFER, hbuf3) # Allocate and set data gl.glBufferData(gl.GL_ARRAY_BUFFER, buf3.nbytes, gl.GL_DYNAMIC_DRAW) gl.glBufferSubData(gl.GL_ARRAY_BUFFER, 0, buf3) # Attach! loc = gl.glGetAttribLocation(hprog, 'a_3') gl.glEnableVertexAttribArray(loc) gl.glVertexAttribPointer(loc, 3, gl.GL_FLOAT, False, 3*4, 0) # Check if all is ok assert_equal(gl.glGetError(), 0) # --- buffer vec4 (client vertex data) # Select no FBO gl.glBindBuffer(gl.GL_ARRAY_BUFFER, 0) # Attach! loc = gl.glGetAttribLocation(hprog, 'a_4') gl.glEnableVertexAttribArray(loc) gl.glVertexAttribPointer(loc, 4, gl.GL_FLOAT, False, 4*4, buf4) # Check if all is ok assert_equal(gl.glGetError(), 0) # --- element buffer # Create buffer global helements helements = gl.glCreateBuffer() objects.append((gl.glDeleteBuffer, helements)) gl.glBindBuffer(gl.GL_ELEMENT_ARRAY_BUFFER, helements) # Allocate and set data gl.glBufferData(gl.GL_ELEMENT_ARRAY_BUFFER, elements, gl.GL_DYNAMIC_DRAW) gl.glBufferSubData(gl.GL_ELEMENT_ARRAY_BUFFER, 0, elements) # Turn off gl.glBindBuffer(gl.GL_ELEMENT_ARRAY_BUFFER, 0) # Check if all is ok assert_equal(gl.glGetError(), 0) # --- uniforms # Set integer uniforms to 0 # We set them twice just to touch both i and iv functions for i, fun1, fun2 in [(1, gl.glUniform1i, gl.glUniform1iv), (2, gl.glUniform2i, gl.glUniform2iv), (3, gl.glUniform3i, gl.glUniform3iv), (4, gl.glUniform4i, gl.glUniform4iv)]: name = 'u_i%i' % i value = [0] * i loc = gl.glGetUniformLocation(hprog, name) fun1(loc, *value) # e.g. glUniform4i fun2(loc, 1, value) # e.g. glUniform4iv # Set float uniforms to 1.0 # We set them twice just to touch both i and iv functions for i, fun1, fun2 in [(1, gl.glUniform1f, gl.glUniform1fv), (2, gl.glUniform2f, gl.glUniform2fv), (3, gl.glUniform3f, gl.glUniform3fv), (4, gl.glUniform4f, gl.glUniform4fv)]: name = 'u_f%i' % i value = [1.0] * i loc = gl.glGetUniformLocation(hprog, name) fun1(loc, *value) # e.g. glUniform4f fun2(loc, 1, value) # e.g. glUniform4fv # Set matrix uniforms m = np.eye(5, dtype='float32') loc = gl.glGetUniformLocation(hprog, 'u_m2') gl.glUniformMatrix2fv(loc, 1, False, m[:2, :2]) # loc = gl.glGetUniformLocation(hprog, 'u_m3') m = np.eye(3, dtype='float32') gl.glUniformMatrix3fv(loc, 1, False, m[:3, :3]) # loc = gl.glGetUniformLocation(hprog, 'u_m4') m = np.eye(4, dtype='float32') gl.glUniformMatrix4fv(loc, 1, False, m[:4, :4]) # Check some uniforms loc = gl.glGetUniformLocation(hprog, 'u_i1') assert_equal(gl.glGetUniform(hprog, loc), 0) loc = gl.glGetUniformLocation(hprog, 'u_i2') assert_equal(gl.glGetUniform(hprog, loc), (0, 0)) loc = gl.glGetUniformLocation(hprog, 'u_f2') assert_equal(gl.glGetUniform(hprog, loc), (1.0, 1.0)) # Check if all is ok assert_equal(gl.glGetError(), 0) # --- attributes # Constant values for attributes. We do not even use this ... loc = gl.glGetAttribLocation(hprog, 'a_1') gl.glVertexAttrib1f(loc, 1.0) loc = gl.glGetAttribLocation(hprog, 'a_2') gl.glVertexAttrib2f(loc, 1.0, 1.0) loc = gl.glGetAttribLocation(hprog, 'a_3') gl.glVertexAttrib3f(loc, 1.0, 1.0, 1.0) loc = gl.glGetAttribLocation(hprog, 'a_4') gl.glVertexAttrib4f(loc, 1.0, 1.0, 1.0, 1.0) # --- flush and finish # Not really necessary, but we want to touch the functions gl.glFlush() gl.glFinish() #print([i[1] for i in objects]) return objects def _draw1(): # Draw using arrays gl.glDrawArrays(gl.GL_TRIANGLES, 0, N) gl.glFinish() _check_result() def _draw2(): # Draw using elements via buffer gl.glBindBuffer(gl.GL_ELEMENT_ARRAY_BUFFER, helements) gl.glDrawElements(gl.GL_TRIANGLES, elements.size, gl.GL_UNSIGNED_BYTE, 0) gl.glBindBuffer(gl.GL_ELEMENT_ARRAY_BUFFER, 0) gl.glFinish() _check_result() def _draw3(): # Draw using elements via numpy array gl.glDrawElements(gl.GL_TRIANGLES, elements.size, gl.GL_UNSIGNED_BYTE, elements) gl.glFinish() _check_result() def _check_result(assert_result=True): """ Test the color of each quadrant by picking the center pixel of each quadrant and comparing it with the reference color. """ # Take screenshot x, y, w, h = gl.glGetParameter(gl.GL_VIEWPORT) data = gl.glReadPixels(x, y, w, h, gl.GL_RGB, gl.GL_UNSIGNED_BYTE) im = np.frombuffer(data, np.uint8) im.shape = h, w, 3 # Get center pixel from each quadrant pix1 = tuple(im[int(1*h/4), int(1*w/4)]) pix2 = tuple(im[int(3*h/4), int(1*w/4)]) pix3 = tuple(im[int(3*h/4), int(3*w/4)]) pix4 = tuple(im[int(1*h/4), int(3*w/4)]) #print(pix1, pix2, pix3, pix4) if assert_result: # Test their value assert_equal(pix1, (0, 0, 0)) assert_equal(pix2, (255, 0, 0)) assert_equal(pix3, (0, 255, 0)) assert_equal(pix4, (0, 0, 255)) run_tests_if_main() ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/gloo/gl/tests/test_names.py�������������������������������������������������������0000664�0001750�0001750�00000025564�12510536123�022767� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������""" Tests to verify that all ES 2.0 function names are defined in all backends, and no more than that. """ from vispy.testing import requires_pyopengl from vispy.gloo import gl from vispy.testing import run_tests_if_main def teardown_module(): gl.use_gl() # Reset to default class _DummyObject: """ To be able to import es2 even in Linux, so that we can test the names defined inside. """ def LoadLibrary(self, fname): return _DummyObject() def __getattr__(self, name): setattr(self, name, self.__class__()) return getattr(self, name) def _test_function_names(mod): # The .difference(['gl2']) is to allow the gl2 module name fnames = set([name for name in dir(mod) if name.startswith('gl')]) assert function_names.difference(fnames) == set() assert fnames.difference(function_names).difference(ok_names) == set() def _test_constant_names(mod): cnames = set([name for name in dir(mod) if name.startswith('GL')]) assert constant_names.difference(cnames) == set() assert cnames.difference(constant_names) == set() def test_destop(): """ Desktop backend should have all ES 2.0 names. No more, no less. """ from vispy.gloo.gl import gl2 _test_function_names(gl2) _test_constant_names(gl2) def test_es2(): """ es2 backend should have all ES 2.0 names. No more, no less. """ # Import. Install a dummy lib so that at least we can import es2. try: from vispy.gloo.gl import es2 # noqa except Exception: import ctypes ctypes.TEST_DLL = _DummyObject() from vispy.gloo.gl import es2 # noqa # Test _test_function_names(es2) _test_constant_names(es2) @requires_pyopengl() def test_pyopengl(): """ Pyopengl backend should have all ES 2.0 names. No more, no less. """ from vispy.gloo.gl import pyopengl2 _test_function_names(pyopengl2) _test_constant_names(pyopengl2) @requires_pyopengl() def test_glplus(): """ Run glplus, check that mo names, set back, check exact set of names. """ gl.use_gl('gl+') # Check that there are more names fnames = set([name for name in dir(gl) if name.startswith('gl')]) assert len(fnames.difference(function_names).difference(['gl2'])) > 50 cnames = set([name for name in dir(gl) if name.startswith('GL')]) assert len(cnames.difference(constant_names)) > 50 gl.use_gl('gl2') _test_function_names(gl) _test_constant_names(gl) def test_proxy(): """ GLProxy class should have all ES 2.0 names. No more, no less. """ _test_function_names(gl.proxy) _test_constant_names(gl._constants) def test_main(): """ Main gl namespace should have all ES 2.0 names. No more, no less. """ _test_function_names(gl) _test_constant_names(gl) def _main(): """ For testing this test suite :) """ test_main() test_proxy() test_destop() test_es2() test_pyopengl() # Note: I took these names below from _main and _constants, which is a # possible cause for error. function_names = """ glActiveTexture glAttachShader glBindAttribLocation glBindBuffer glBindFramebuffer glBindRenderbuffer glBindTexture glBlendColor glBlendEquation glBlendEquationSeparate glBlendFunc glBlendFuncSeparate glBufferData glBufferSubData glCheckFramebufferStatus glClear glClearColor glClearDepth glClearStencil glColorMask glCompileShader glCompressedTexImage2D glCompressedTexSubImage2D glCopyTexImage2D glCopyTexSubImage2D glCreateBuffer glCreateFramebuffer glCreateProgram glCreateRenderbuffer glCreateShader glCreateTexture glCullFace glDeleteBuffer glDeleteFramebuffer glDeleteProgram glDeleteRenderbuffer glDeleteShader glDeleteTexture glDepthFunc glDepthMask glDepthRange glDetachShader glDisable glDisableVertexAttribArray glDrawArrays glDrawElements glEnable glEnableVertexAttribArray glFinish glFlush glFramebufferRenderbuffer glFramebufferTexture2D glFrontFace glGenerateMipmap glGetActiveAttrib glGetActiveUniform glGetAttachedShaders glGetAttribLocation glGetBufferParameter glGetError glGetFramebufferAttachmentParameter glGetParameter glGetProgramInfoLog glGetProgramParameter glGetRenderbufferParameter glGetShaderInfoLog glGetShaderParameter glGetShaderPrecisionFormat glGetShaderSource glGetTexParameter glGetUniform glGetUniformLocation glGetVertexAttrib glGetVertexAttribOffset glHint glIsBuffer glIsEnabled glIsFramebuffer glIsProgram glIsRenderbuffer glIsShader glIsTexture glLineWidth glLinkProgram glPixelStorei glPolygonOffset glReadPixels glRenderbufferStorage glSampleCoverage glScissor glShaderSource glStencilFunc glStencilFuncSeparate glStencilMask glStencilMaskSeparate glStencilOp glStencilOpSeparate glTexImage2D glTexParameterf glTexParameteri glTexSubImage2D glUniform1f glUniform1fv glUniform1i glUniform1iv glUniform2f glUniform2fv glUniform2i glUniform2iv glUniform3f glUniform3fv glUniform3i glUniform3iv glUniform4f glUniform4fv glUniform4i glUniform4iv glUniformMatrix2fv glUniformMatrix3fv glUniformMatrix4fv glUseProgram glValidateProgram glVertexAttrib1f glVertexAttrib2f glVertexAttrib3f glVertexAttrib4f glVertexAttribPointer glViewport """.replace('\n', ' ') constant_names = """ GL_ACTIVE_ATTRIBUTES GL_ACTIVE_ATTRIBUTE_MAX_LENGTH GL_ACTIVE_TEXTURE GL_ACTIVE_UNIFORMS GL_ACTIVE_UNIFORM_MAX_LENGTH GL_ALIASED_LINE_WIDTH_RANGE GL_ALIASED_POINT_SIZE_RANGE GL_ALPHA GL_ALPHA_BITS GL_ALWAYS GL_ARRAY_BUFFER GL_ARRAY_BUFFER_BINDING GL_ATTACHED_SHADERS GL_BACK GL_BLEND GL_BLEND_COLOR GL_BLEND_DST_ALPHA GL_BLEND_DST_RGB GL_BLEND_EQUATION GL_BLEND_EQUATION_ALPHA GL_BLEND_EQUATION_RGB GL_BLEND_SRC_ALPHA GL_BLEND_SRC_RGB GL_BLUE_BITS GL_BOOL GL_BOOL_VEC2 GL_BOOL_VEC3 GL_BOOL_VEC4 GL_BUFFER_SIZE GL_BUFFER_USAGE GL_BYTE GL_CCW GL_CLAMP_TO_EDGE GL_COLOR_ATTACHMENT0 GL_COLOR_BUFFER_BIT GL_COLOR_CLEAR_VALUE GL_COLOR_WRITEMASK GL_COMPILE_STATUS GL_COMPRESSED_TEXTURE_FORMATS GL_CONSTANT_ALPHA GL_CONSTANT_COLOR GL_CULL_FACE GL_CULL_FACE_MODE GL_CURRENT_PROGRAM GL_CURRENT_VERTEX_ATTRIB GL_CW GL_DECR GL_DECR_WRAP GL_DELETE_STATUS GL_DEPTH_ATTACHMENT GL_DEPTH_BITS GL_DEPTH_BUFFER_BIT GL_DEPTH_CLEAR_VALUE GL_DEPTH_COMPONENT GL_DEPTH_COMPONENT16 GL_DEPTH_FUNC GL_DEPTH_RANGE GL_DEPTH_TEST GL_DEPTH_WRITEMASK GL_DITHER GL_DONT_CARE GL_DST_ALPHA GL_DST_COLOR GL_DYNAMIC_DRAW GL_ELEMENT_ARRAY_BUFFER GL_ELEMENT_ARRAY_BUFFER_BINDING GL_EQUAL GL_ES_VERSION_2_0 GL_EXTENSIONS GL_FALSE GL_FASTEST GL_FIXED GL_FLOAT GL_FLOAT_MAT2 GL_FLOAT_MAT3 GL_FLOAT_MAT4 GL_FLOAT_VEC2 GL_FLOAT_VEC3 GL_FLOAT_VEC4 GL_FRAGMENT_SHADER GL_FRAMEBUFFER GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL GL_FRAMEBUFFER_BINDING GL_FRAMEBUFFER_COMPLETE GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT GL_FRAMEBUFFER_UNSUPPORTED GL_FRONT GL_FRONT_AND_BACK GL_FRONT_FACE GL_FUNC_ADD GL_FUNC_REVERSE_SUBTRACT GL_FUNC_SUBTRACT GL_GENERATE_MIPMAP_HINT GL_GEQUAL GL_GREATER GL_GREEN_BITS GL_HIGH_FLOAT GL_HIGH_INT GL_IMPLEMENTATION_COLOR_READ_FORMAT GL_IMPLEMENTATION_COLOR_READ_TYPE GL_INCR GL_INCR_WRAP GL_INFO_LOG_LENGTH GL_INT GL_INT_VEC2 GL_INT_VEC3 GL_INT_VEC4 GL_INVALID_ENUM GL_INVALID_FRAMEBUFFER_OPERATION GL_INVALID_OPERATION GL_INVALID_VALUE GL_INVERT GL_KEEP GL_LEQUAL GL_LESS GL_LINEAR GL_LINEAR_MIPMAP_LINEAR GL_LINEAR_MIPMAP_NEAREST GL_LINES GL_LINE_LOOP GL_LINE_STRIP GL_LINE_WIDTH GL_LINK_STATUS GL_LOW_FLOAT GL_LOW_INT GL_LUMINANCE GL_LUMINANCE_ALPHA GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS GL_MAX_CUBE_MAP_TEXTURE_SIZE GL_MAX_FRAGMENT_UNIFORM_VECTORS GL_MAX_RENDERBUFFER_SIZE GL_MAX_TEXTURE_IMAGE_UNITS GL_MAX_TEXTURE_SIZE GL_MAX_VARYING_VECTORS GL_MAX_VERTEX_ATTRIBS GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS GL_MAX_VERTEX_UNIFORM_VECTORS GL_MAX_VIEWPORT_DIMS GL_MEDIUM_FLOAT GL_MEDIUM_INT GL_MIRRORED_REPEAT GL_NEAREST GL_NEAREST_MIPMAP_LINEAR GL_NEAREST_MIPMAP_NEAREST GL_NEVER GL_NICEST GL_NONE GL_NOTEQUAL GL_NO_ERROR GL_NUM_COMPRESSED_TEXTURE_FORMATS GL_NUM_SHADER_BINARY_FORMATS GL_ONE GL_ONE_MINUS_CONSTANT_ALPHA GL_ONE_MINUS_CONSTANT_COLOR GL_ONE_MINUS_DST_ALPHA GL_ONE_MINUS_DST_COLOR GL_ONE_MINUS_SRC_ALPHA GL_ONE_MINUS_SRC_COLOR GL_OUT_OF_MEMORY GL_PACK_ALIGNMENT GL_POINTS GL_POLYGON_OFFSET_FACTOR GL_POLYGON_OFFSET_FILL GL_POLYGON_OFFSET_UNITS GL_RED_BITS GL_RENDERBUFFER GL_RENDERBUFFER_ALPHA_SIZE GL_RENDERBUFFER_BINDING GL_RENDERBUFFER_BLUE_SIZE GL_RENDERBUFFER_DEPTH_SIZE GL_RENDERBUFFER_GREEN_SIZE GL_RENDERBUFFER_HEIGHT GL_RENDERBUFFER_INTERNAL_FORMAT GL_RENDERBUFFER_RED_SIZE GL_RENDERBUFFER_STENCIL_SIZE GL_RENDERBUFFER_WIDTH GL_RENDERER GL_REPEAT GL_REPLACE GL_RGB GL_RGB565 GL_RGB5_A1 GL_RGBA GL_RGBA4 GL_SAMPLER_2D GL_SAMPLER_CUBE GL_SAMPLES GL_SAMPLE_ALPHA_TO_COVERAGE GL_SAMPLE_BUFFERS GL_SAMPLE_COVERAGE GL_SAMPLE_COVERAGE_INVERT GL_SAMPLE_COVERAGE_VALUE GL_SCISSOR_BOX GL_SCISSOR_TEST GL_SHADER_BINARY_FORMATS GL_SHADER_COMPILER GL_SHADER_SOURCE_LENGTH GL_SHADER_TYPE GL_SHADING_LANGUAGE_VERSION GL_SHORT GL_SRC_ALPHA GL_SRC_ALPHA_SATURATE GL_SRC_COLOR GL_STATIC_DRAW GL_STENCIL_ATTACHMENT GL_STENCIL_BACK_FAIL GL_STENCIL_BACK_FUNC GL_STENCIL_BACK_PASS_DEPTH_FAIL GL_STENCIL_BACK_PASS_DEPTH_PASS GL_STENCIL_BACK_REF GL_STENCIL_BACK_VALUE_MASK GL_STENCIL_BACK_WRITEMASK GL_STENCIL_BITS GL_STENCIL_BUFFER_BIT GL_STENCIL_CLEAR_VALUE GL_STENCIL_FAIL GL_STENCIL_FUNC GL_STENCIL_INDEX8 GL_STENCIL_PASS_DEPTH_FAIL GL_STENCIL_PASS_DEPTH_PASS GL_STENCIL_REF GL_STENCIL_TEST GL_STENCIL_VALUE_MASK GL_STENCIL_WRITEMASK GL_STREAM_DRAW GL_SUBPIXEL_BITS GL_TEXTURE GL_TEXTURE0 GL_TEXTURE1 GL_TEXTURE10 GL_TEXTURE11 GL_TEXTURE12 GL_TEXTURE13 GL_TEXTURE14 GL_TEXTURE15 GL_TEXTURE16 GL_TEXTURE17 GL_TEXTURE18 GL_TEXTURE19 GL_TEXTURE2 GL_TEXTURE20 GL_TEXTURE21 GL_TEXTURE22 GL_TEXTURE23 GL_TEXTURE24 GL_TEXTURE25 GL_TEXTURE26 GL_TEXTURE27 GL_TEXTURE28 GL_TEXTURE29 GL_TEXTURE3 GL_TEXTURE30 GL_TEXTURE31 GL_TEXTURE4 GL_TEXTURE5 GL_TEXTURE6 GL_TEXTURE7 GL_TEXTURE8 GL_TEXTURE9 GL_TEXTURE_2D GL_TEXTURE_BINDING_2D GL_TEXTURE_BINDING_CUBE_MAP GL_TEXTURE_CUBE_MAP GL_TEXTURE_CUBE_MAP_NEGATIVE_X GL_TEXTURE_CUBE_MAP_NEGATIVE_Y GL_TEXTURE_CUBE_MAP_NEGATIVE_Z GL_TEXTURE_CUBE_MAP_POSITIVE_X GL_TEXTURE_CUBE_MAP_POSITIVE_Y GL_TEXTURE_CUBE_MAP_POSITIVE_Z GL_TEXTURE_MAG_FILTER GL_TEXTURE_MIN_FILTER GL_TEXTURE_WRAP_S GL_TEXTURE_WRAP_T GL_TRIANGLES GL_TRIANGLE_FAN GL_TRIANGLE_STRIP GL_TRUE GL_UNPACK_ALIGNMENT GL_UNSIGNED_BYTE GL_UNSIGNED_INT GL_UNSIGNED_SHORT GL_UNSIGNED_SHORT_4_4_4_4 GL_UNSIGNED_SHORT_5_5_5_1 GL_UNSIGNED_SHORT_5_6_5 GL_VALIDATE_STATUS GL_VENDOR GL_VERSION GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING GL_VERTEX_ATTRIB_ARRAY_ENABLED GL_VERTEX_ATTRIB_ARRAY_NORMALIZED GL_VERTEX_ATTRIB_ARRAY_POINTER GL_VERTEX_ATTRIB_ARRAY_SIZE GL_VERTEX_ATTRIB_ARRAY_STRIDE GL_VERTEX_ATTRIB_ARRAY_TYPE GL_VERTEX_SHADER GL_VIEWPORT GL_ZERO """.replace('\n', ' ') function_names = [n.strip() for n in function_names.split(' ')] function_names = set([n for n in function_names if n]) constant_names = [n.strip() for n in constant_names.split(' ')] constant_names = set([n for n in constant_names if n]) ok_names = set(['gl2', 'glplus']) # module names run_tests_if_main() ��������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/gloo/gl/tests/__init__.py���������������������������������������������������������0000664�0001750�0001750�00000000000�12375431476�022354� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/gloo/gl/tests/test_basics.py������������������������������������������������������0000664�0001750�0001750�00000021365�12510536123�023123� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������""" Test to verify some basic functionality of our OpenGL API. We cover much more in the test_functionality. Together, these two tests should touch *all* ES 2.0 API calls. The only exception is glCompressedTexImage2D and glCompressedTexSubImage2D. """ import sys from vispy.app import Canvas from numpy.testing import assert_almost_equal from vispy.testing import (requires_application, requires_pyopengl, SkipTest, run_tests_if_main, assert_equal, assert_true) from vispy.ext.six import string_types from vispy.util import use_log_level from vispy.gloo import gl def teardown_module(): gl.use_gl() # Reset to default @requires_application() def test_basics_desktop(): """ Test desktop GL backend for basic functionality. """ _test_basics('gl2') @requires_application() def test_functionality_proxy(): """ Test GL proxy class for basic functionality. """ # By using debug mode, we are using the proxy class _test_basics('gl2 debug') @requires_application() @requires_pyopengl() def test_basics_pypengl(): """ Test pyopengl GL backend for basic functionality. """ _test_basics('pyopengl2') @requires_application() def test_functionality_es2(): """ Test es2 GL backend for basic functionality. """ if True: raise SkipTest('Skip es2 functionality test for now.') if sys.platform.startswith('win'): raise SkipTest('Can only test es2 functionality on Windows.') _test_basics('es2') def _test_basics(backend): """ Create app and canvas so we have a context. Then run tests. """ # use the backend with use_log_level('error', print_msg=False): gl.use_gl(backend) # pyopengl throws warning on injection with Canvas(): _test_setting_parameters() _test_enabling_disabling() _test_setting_stuff() _test_object_creation_and_deletion() _test_fbo() gl.glFinish() def _test_setting_parameters(): # Set some parameters and get result clr = 1.0, 0.1, 0.2, 0.7 gl.glClearColor(*clr) assert_almost_equal(gl.glGetParameter(gl.GL_COLOR_CLEAR_VALUE), clr) # gl.glCullFace(gl.GL_FRONT) assert_equal(gl.glGetParameter(gl.GL_CULL_FACE_MODE), gl.GL_FRONT) gl.glCullFace(gl.GL_BACK) assert_equal(gl.glGetParameter(gl.GL_CULL_FACE_MODE), gl.GL_BACK) # gl.glDepthFunc(gl.GL_NOTEQUAL) assert_equal(gl.glGetParameter(gl.GL_DEPTH_FUNC), gl.GL_NOTEQUAL) # val = 0.2, 0.3 gl.glDepthRange(*val) assert_almost_equal(gl.glGetParameter(gl.GL_DEPTH_RANGE), val) gl.check_error() def _test_enabling_disabling(): # Enabling/disabling gl.glEnable(gl.GL_DEPTH_TEST) assert_equal(gl.glIsEnabled(gl.GL_DEPTH_TEST), True) assert_equal(gl.glGetParameter(gl.GL_DEPTH_TEST), 1) gl.glDisable(gl.GL_DEPTH_TEST) assert_equal(gl.glIsEnabled(gl.GL_DEPTH_TEST), False) assert_equal(gl.glGetParameter(gl.GL_DEPTH_TEST), 0) # gl.glEnable(gl.GL_BLEND) assert_equal(gl.glIsEnabled(gl.GL_BLEND), True) assert_equal(gl.glGetParameter(gl.GL_BLEND), 1) gl.glDisable(gl.GL_BLEND) assert_equal(gl.glIsEnabled(gl.GL_BLEND), False) assert_equal(gl.glGetParameter(gl.GL_BLEND), 0) gl.check_error() def _test_setting_stuff(): # Set stuff to touch functions gl.glClear(gl.GL_COLOR_BUFFER_BIT) # gl.glBlendColor(1.0, 1.0, 1.0, 1.0) gl.glBlendEquation(gl.GL_FUNC_ADD) gl.glBlendEquationSeparate(gl.GL_FUNC_ADD, gl.GL_FUNC_ADD) gl.glBlendFunc(gl.GL_ONE, gl.GL_ZERO) gl.glBlendFuncSeparate(gl.GL_ONE, gl.GL_ZERO, gl.GL_ONE, gl.GL_ZERO) # gl.glClearColor(0.0, 0.0, 0.0, 1.0) gl.glClearDepth(1) gl.glClearStencil(0) # gl.glColorMask(True, True, True, True) gl.glDepthMask(False) gl.glStencilMask(255) gl.glStencilMaskSeparate(gl.GL_FRONT, 128) # gl.glStencilFunc(gl.GL_ALWAYS, 0, 255) gl.glStencilFuncSeparate(gl.GL_FRONT, gl.GL_ALWAYS, 0, 255) gl.glStencilOp(gl.GL_KEEP, gl.GL_KEEP, gl.GL_KEEP) gl.glStencilOpSeparate(gl.GL_FRONT, gl.GL_KEEP, gl.GL_KEEP, gl.GL_KEEP) # gl.glFrontFace(gl.GL_CW) gl.glHint(gl.GL_GENERATE_MIPMAP_HINT, gl.GL_FASTEST) gl.glLineWidth(2.0) gl.glPolygonOffset(0.0, 0.0) gl.glSampleCoverage(1.0, False) # And getting stuff try: with use_log_level('error', print_msg=False): r, p = gl.glGetShaderPrecisionFormat(gl.GL_FRAGMENT_SHADER, gl.GL_HIGH_FLOAT) gl.check_error() # Sometimes the func is there but OpenGL errs except Exception: pass # accept if the function is not there ... # We should catch RuntimeError and GL.error.NullFunctionError, # but PyOpenGL may not be available. # On Travis this function was not there on one machine according # to PyOpenGL, but our desktop backend worked fine ... # v = gl.glGetParameter(gl.GL_VERSION) assert_true(isinstance(v, string_types)) assert_true(len(v) > 0) gl.check_error() def _test_object_creation_and_deletion(): # Stuff that is originally glGenX # Note that if we test glIsTexture(x), we cannot assume x to be a # nonexisting texture; we might have created a texture in another # test and failed to clean it up. # Create/delete texture #assert_equal(gl.glIsTexture(12), False) handle = gl.glCreateTexture() gl.glBindTexture(gl.GL_TEXTURE_2D, handle) assert_equal(gl.glIsTexture(handle), True) gl.glDeleteTexture(handle) assert_equal(gl.glIsTexture(handle), False) # Create/delete buffer #assert_equal(gl.glIsBuffer(12), False) handle = gl.glCreateBuffer() gl.glBindBuffer(gl.GL_ARRAY_BUFFER, handle) assert_equal(gl.glIsBuffer(handle), True) gl.glDeleteBuffer(handle) assert_equal(gl.glIsBuffer(handle), False) # Create/delete framebuffer #assert_equal(gl.glIsFramebuffer(12), False) handle = gl.glCreateFramebuffer() gl.glBindFramebuffer(gl.GL_FRAMEBUFFER, handle) assert_equal(gl.glIsFramebuffer(handle), True) gl.glDeleteFramebuffer(handle) assert_equal(gl.glIsFramebuffer(handle), False) # Create/delete renderbuffer #assert_equal(gl.glIsRenderbuffer(12), False) handle = gl.glCreateRenderbuffer() gl.glBindRenderbuffer(gl.GL_RENDERBUFFER, handle) assert_equal(gl.glIsRenderbuffer(handle), True) gl.glDeleteRenderbuffer(handle) assert_equal(gl.glIsRenderbuffer(handle), False) # Stuff that is originally called glCreate # Create/delete program #assert_equal(gl.glIsProgram(12), False) handle = gl.glCreateProgram() assert_equal(gl.glIsProgram(handle), True) gl.glDeleteProgram(handle) assert_equal(gl.glIsProgram(handle), False) # Create/delete shader #assert_equal(gl.glIsShader(12), False) handle = gl.glCreateShader(gl.GL_VERTEX_SHADER) assert_equal(gl.glIsShader(handle), True) gl.glDeleteShader(handle) assert_equal(gl.glIsShader(handle), False) gl.check_error() def _test_fbo(): w, h = 120, 130 # Create frame buffer hframebuf = gl.glCreateFramebuffer() gl.glBindFramebuffer(gl.GL_FRAMEBUFFER, hframebuf) #Create render buffer (for depth) hrenderbuf = gl.glCreateRenderbuffer() gl.glBindRenderbuffer(gl.GL_RENDERBUFFER, hrenderbuf) gl.glRenderbufferStorage(gl.GL_RENDERBUFFER, gl.GL_DEPTH_COMPONENT16, w, h) gl.glFramebufferRenderbuffer(gl.GL_FRAMEBUFFER, gl.GL_DEPTH_ATTACHMENT, gl.GL_RENDERBUFFER, hrenderbuf) # Create texture (for color) htex = gl.glCreateTexture() gl.glBindTexture(gl.GL_TEXTURE_2D, htex) gl.glTexImage2D(gl.GL_TEXTURE_2D, 0, gl.GL_RGB, gl.GL_RGB, gl.GL_UNSIGNED_BYTE, (h, w)) gl.glFramebufferTexture2D(gl.GL_FRAMEBUFFER, gl.GL_COLOR_ATTACHMENT0, gl.GL_TEXTURE_2D, htex, 0) # Check framebuffer status status = gl.glCheckFramebufferStatus(gl.GL_FRAMEBUFFER) assert_equal(status, gl.GL_FRAMEBUFFER_COMPLETE) # Tests renderbuffer params name = gl.glGetFramebufferAttachmentParameter( gl.GL_FRAMEBUFFER, gl.GL_DEPTH_ATTACHMENT, gl.GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME) assert_equal(name, hrenderbuf) # width = gl.glGetRenderbufferParameter(gl.GL_RENDERBUFFER, gl.GL_RENDERBUFFER_WIDTH) assert_equal(width, w) # Touch copy tex functions gl.glBindTexture(gl.GL_TEXTURE_2D, htex) gl.glCopyTexSubImage2D(gl.GL_TEXTURE_2D, 0, 5, 5, 5, 5, 20, 20) gl.glCopyTexImage2D(gl.GL_TEXTURE_2D, 0, gl.GL_RGB, 0, 0, 30, 30, 0) gl.check_error() # Clean up gl.glDeleteTexture(htex) gl.glDeleteRenderbuffer(hrenderbuf) gl.glDeleteFramebuffer(hframebuf) gl.check_error() run_tests_if_main() ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/gloo/gl/tests/test_use.py���������������������������������������������������������0000664�0001750�0001750�00000003511�12510536123�022444� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������""" Test the use function. """ from vispy.testing import assert_is, requires_pyopengl from vispy.gloo import gl from vispy.testing import run_tests_if_main def teardown_module(): gl.use_gl() # Reset to default @requires_pyopengl() def test_use_desktop(): """ Testing that gl.use injects all names in gl namespace """ # Use desktop gl.use_gl('gl2') # for name in dir(gl.gl2): if name.lower().startswith('gl'): val1 = getattr(gl, name) val2 = getattr(gl.gl2, name) assert_is(val1, val2) # Use pyopengl gl.use_gl('pyopengl2') # for name in dir(gl.gl2): if name.lower().startswith('gl'): val1 = getattr(gl, name) val2 = getattr(gl.pyopengl2, name) assert_is(val1, val2) # Use gl+ gl.use_gl('gl+') # uses all ES2 names from pyopengl2 backend for name in dir(gl.gl2): if name.lower().startswith('gl'): val1 = getattr(gl, name) val2 = getattr(gl.pyopengl2, name) assert_is(val1, val2) # But provides extra names too for name in dir(gl.glplus): if name.lower().startswith('gl'): val1 = getattr(gl, name) val2 = getattr(gl.glplus, name) assert_is(val1, val2) # Use dummy gl.use_gl('dummy') # for name in dir(gl.gl2): if name.lower().startswith('gl'): val1 = getattr(gl, name) val2 = getattr(gl.dummy, name) assert_is(val1, val2) # Touch debug wrapper stuff gl.use_gl('gl2 debug') # Use desktop again gl.use_gl('gl2') # for name in dir(gl.gl2): if name.lower().startswith('gl'): val1 = getattr(gl, name) val2 = getattr(gl.gl2, name) assert_is(val1, val2) run_tests_if_main() ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/gloo/gl/gl2.py��������������������������������������������������������������������0000664�0001750�0001750�00000005000�12527672621�020142� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ GL ES 2.0 API implemented via desktop GL (i.e subset of normal OpenGL). """ import os import sys import ctypes.util from . import _copy_gl_functions from ._constants import * # noqa # Ctypes stuff # Load the OpenGL library. We more or less follow the same approach # as PyOpenGL does internally _have_get_proc_address = False _lib = os.getenv('VISPY_GL_LIB', '') if _lib != '': if sys.platform.startswith('win'): _lib = ctypes.windll.LoadLibrary(_lib) else: _lib = ctypes.cdll.LoadLibrary(_lib) elif sys.platform.startswith('win'): # Windows _lib = ctypes.windll.opengl32 try: wglGetProcAddress = _lib.wglGetProcAddress wglGetProcAddress.restype = ctypes.CFUNCTYPE( ctypes.POINTER(ctypes.c_int)) wglGetProcAddress.argtypes = [ctypes.c_char_p] _have_get_proc_address = True except AttributeError: pass else: # Unix-ish if sys.platform.startswith('darwin'): _fname = ctypes.util.find_library('OpenGL') else: _fname = ctypes.util.find_library('GL') if not _fname: raise RuntimeError('Could not load OpenGL library.') # Load lib _lib = ctypes.cdll.LoadLibrary(_fname) def _have_context(): return _lib.glGetError() != 1282 # GL_INVALID_OPERATION def _get_gl_func(name, restype, argtypes): # Based on a function in Pyglet try: # Try using normal ctypes stuff func = getattr(_lib, name) func.restype = restype func.argtypes = argtypes return func except AttributeError: if sys.platform.startswith('win'): # Ask for a pointer to the function, this is the approach # for OpenGL extensions on Windows fargs = (restype,) + argtypes ftype = ctypes.WINFUNCTYPE(*fargs) if not _have_get_proc_address: raise RuntimeError('Function %s not available.' % name) if not _have_context(): raise RuntimeError('Using %s with no OpenGL context.' % name) address = wglGetProcAddress(name.encode('utf-8')) if address: return ctypes.cast(address, ftype) # If not Windows or if we did not return function object on Windows: raise RuntimeError('Function %s not present in context.' % name) # Inject from . import _gl2 # noqa _copy_gl_functions(_gl2, globals()) vispy-0.4.0/vispy/gloo/gl/pyopengl2.py��������������������������������������������������������������0000664�0001750�0001750�00000005163�12527672621�021407� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ GL ES 2.0 API implemented via pyOpenGL library. Intended as a fallback and for testing. """ from OpenGL import GL as _GL import OpenGL.GL.framebufferobjects as _FBO from ...util import logger from . import _copy_gl_functions from ._constants import * # noqa def _patch(): """ Monkey-patch pyopengl to fix a bug in glBufferSubData. """ import sys from OpenGL import GL if sys.version_info > (3,): buffersubdatafunc = GL.glBufferSubData if hasattr(buffersubdatafunc, 'wrapperFunction'): buffersubdatafunc = buffersubdatafunc.wrapperFunction _m = sys.modules[buffersubdatafunc.__module__] _m.long = int # Fix missing enum try: from OpenGL.GL.VERSION import GL_2_0 GL_2_0.GL_OBJECT_SHADER_SOURCE_LENGTH = GL_2_0.GL_SHADER_SOURCE_LENGTH except Exception: pass # Patch OpenGL package _patch() ## Inject def _make_unavailable_func(funcname): def cb(*args, **kwargs): raise RuntimeError('OpenGL API call "%s" is not available.' % funcname) return cb def _get_function_from_pyopengl(funcname): """ Try getting the given function from PyOpenGL, return a dummy function (that shows a warning when called) if it could not be found. """ func = None # Get function from GL try: func = getattr(_GL, funcname) except AttributeError: # Get function from FBO try: func = getattr(_FBO, funcname) except AttributeError: func = None # Try using "alias" if not bool(func): # Some functions are known by a slightly different name # e.g. glDepthRangef, glClearDepthf if funcname.endswith('f'): try: func = getattr(_GL, funcname[:-1]) except AttributeError: pass # Set dummy function if we could not find it if func is None: func = _make_unavailable_func(funcname) logger.warning('warning: %s not available' % funcname) return func def _inject(): """ Copy functions from OpenGL.GL into _pyopengl namespace. """ NS = _pyopengl2.__dict__ for glname, ourname in _pyopengl2._functions_to_import: func = _get_function_from_pyopengl(glname) NS[ourname] = func from . import _pyopengl2 # noqa # Inject remaining functions from OpenGL.GL # copies name to _pyopengl2 namespace _inject() # Inject all function definitions in _pyopengl2 _copy_gl_functions(_pyopengl2, globals()) �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/gloo/gl/glplus.py�����������������������������������������������������������������0000664�0001750�0001750�00000022115�12527672621�020772� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ This module provides a namespace for additional desktop OpenGL functions. The functions in this module are copied from PyOpenGL, but any deprecated functions are omitted, as well as any functions that are in our ES 2.0 API. """ from OpenGL import GL as _GL from . import _pyopengl2 from . import _constants def _inject(): """ Inject functions and constants from PyOpenGL but leave out the names that are deprecated or that we provide in our API. """ # Get namespaces NS = globals() GLNS = _GL.__dict__ # Get names that we use in our API used_names = [] used_names.extend([names[0] for names in _pyopengl2._functions_to_import]) used_names.extend([name for name in _pyopengl2._used_functions]) NS['_used_names'] = used_names # used_constants = set(_constants.__dict__) # Count injected_constants = 0 injected_functions = 0 for name in dir(_GL): if name.startswith('GL_'): # todo: find list of deprecated constants if name not in used_constants: NS[name] = GLNS[name] injected_constants += 1 elif name.startswith('gl'): # Functions if (name + ',') in _deprecated_functions: pass # Function is deprecated elif name in used_names: pass # Function is in our GL ES 2.0 API else: NS[name] = GLNS[name] injected_functions += 1 #print('injected %i constants and %i functions in glplus' % # (injected_constants, injected_functions)) # List of deprecated functions, obtained by parsing gl.spec _deprecated_functions = """ glAccum, glAlphaFunc, glAreTexturesResident, glArrayElement, glBegin, glBitmap, glCallList, glCallLists, glClearAccum, glClearIndex, glClientActiveTexture, glClipPlane, glColor3b, glColor3bv, glColor3d, glColor3dv, glColor3f, glColor3fv, glColor3i, glColor3iv, glColor3s, glColor3sv, glColor3ub, glColor3ubv, glColor3ui, glColor3uiv, glColor3us, glColor3usv, glColor4b, glColor4bv, glColor4d, glColor4dv, glColor4f, glColor4fv, glColor4i, glColor4iv, glColor4s, glColor4sv, glColor4ub, glColor4ubv, glColor4ui, glColor4uiv, glColor4us, glColor4usv, glColorMaterial, glColorPointer, glColorSubTable, glColorTable, glColorTableParameterfv, glColorTableParameteriv, glConvolutionFilter1D, glConvolutionFilter2D, glConvolutionParameterf, glConvolutionParameterfv, glConvolutionParameteri, glConvolutionParameteriv, glCopyColorSubTable, glCopyColorTable, glCopyConvolutionFilter1D, glCopyConvolutionFilter2D, glCopyPixels, glDeleteLists, glDisableClientState, glDrawPixels, glEdgeFlag, glEdgeFlagPointer, glEdgeFlagv, glEnableClientState, glEnd, glEndList, glEvalCoord1d, glEvalCoord1dv, glEvalCoord1f, glEvalCoord1fv, glEvalCoord2d, glEvalCoord2dv, glEvalCoord2f, glEvalCoord2fv, glEvalMesh1, glEvalMesh2, glEvalPoint1, glEvalPoint2, glFeedbackBuffer, glFogCoordPointer, glFogCoordd, glFogCoorddv, glFogCoordf, glFogCoordfv, glFogf, glFogfv, glFogi, glFogiv, glFrustum, glGenLists, glGetClipPlane, glGetColorTable, glGetColorTableParameterfv, glGetColorTableParameteriv, glGetConvolutionFilter, glGetConvolutionParameterfv, glGetConvolutionParameteriv, glGetHistogram, glGetHistogramParameterfv, glGetHistogramParameteriv, glGetLightfv, glGetLightiv, glGetMapdv, glGetMapfv, glGetMapiv, glGetMaterialfv, glGetMaterialiv, glGetMinmax, glGetMinmaxParameterfv, glGetMinmaxParameteriv, glGetPixelMapfv, glGetPixelMapuiv, glGetPixelMapusv, glGetPolygonStipple, glGetSeparableFilter, glGetTexEnvfv, glGetTexEnviv, glGetTexGendv, glGetTexGenfv, glGetTexGeniv, glHistogram, glIndexMask, glIndexPointer, glIndexd, glIndexdv, glIndexf, glIndexfv, glIndexi, glIndexiv, glIndexs, glIndexsv, glInitNames, glInterleavedArrays, glIsList, glLightModelf, glLightModelfv, glLightModeli, glLightModeliv, glLightf, glLightfv, glLighti, glLightiv, glLineStipple, glListBase, glLoadIdentity, glLoadMatrixd, glLoadMatrixf, glLoadName, glLoadTransposeMatrixd, glLoadTransposeMatrixf, glMap1d, glMap1f, glMap2d, glMap2f, glMapGrid1d, glMapGrid1f, glMapGrid2d, glMapGrid2f, glMaterialf, glMaterialfv, glMateriali, glMaterialiv, glMatrixMode, glMinmax, glMultMatrixd, glMultMatrixf, glMultTransposeMatrixd, glMultTransposeMatrixf, glMultiTexCoord1d, glMultiTexCoord1dv, glMultiTexCoord1f, glMultiTexCoord1fv, glMultiTexCoord1i, glMultiTexCoord1iv, glMultiTexCoord1s, glMultiTexCoord1sv, glMultiTexCoord2d, glMultiTexCoord2dv, glMultiTexCoord2f, glMultiTexCoord2fv, glMultiTexCoord2i, glMultiTexCoord2iv, glMultiTexCoord2s, glMultiTexCoord2sv, glMultiTexCoord3d, glMultiTexCoord3dv, glMultiTexCoord3f, glMultiTexCoord3fv, glMultiTexCoord3i, glMultiTexCoord3iv, glMultiTexCoord3s, glMultiTexCoord3sv, glMultiTexCoord4d, glMultiTexCoord4dv, glMultiTexCoord4f, glMultiTexCoord4fv, glMultiTexCoord4i, glMultiTexCoord4iv, glMultiTexCoord4s, glMultiTexCoord4sv, glNewList, glNormal3b, glNormal3bv, glNormal3d, glNormal3dv, glNormal3f, glNormal3fv, glNormal3i, glNormal3iv, glNormal3s, glNormal3sv, glNormalPointer, glOrtho, glPassThrough, glPixelMapfv, glPixelMapuiv, glPixelMapusv, glPixelTransferf, glPixelTransferi, glPixelZoom, glPolygonStipple, glPopAttrib, glPopClientAttrib, glPopMatrix, glPopName, glPrioritizeTextures, glPushAttrib, glPushClientAttrib, glPushMatrix, glPushName, glRasterPos2d, glRasterPos2dv, glRasterPos2f, glRasterPos2fv, glRasterPos2i, glRasterPos2iv, glRasterPos2s, glRasterPos2sv, glRasterPos3d, glRasterPos3dv, glRasterPos3f, glRasterPos3fv, glRasterPos3i, glRasterPos3iv, glRasterPos3s, glRasterPos3sv, glRasterPos4d, glRasterPos4dv, glRasterPos4f, glRasterPos4fv, glRasterPos4i, glRasterPos4iv, glRasterPos4s, glRasterPos4sv, glRectd, glRectdv, glRectf, glRectfv, glRecti, glRectiv, glRects, glRectsv, glRenderMode, glResetHistogram, glResetMinmax, glRotated, glRotatef, glScaled, glScalef, glSecondaryColor3b, glSecondaryColor3bv, glSecondaryColor3d, glSecondaryColor3dv, glSecondaryColor3f, glSecondaryColor3fv, glSecondaryColor3i, glSecondaryColor3iv, glSecondaryColor3s, glSecondaryColor3sv, glSecondaryColor3ub, glSecondaryColor3ubv, glSecondaryColor3ui, glSecondaryColor3uiv, glSecondaryColor3us, glSecondaryColor3usv, glSecondaryColorPointer, glSelectBuffer, glSeparableFilter2D, glShadeModel, glTexCoord1d, glTexCoord1dv, glTexCoord1f, glTexCoord1fv, glTexCoord1i, glTexCoord1iv, glTexCoord1s, glTexCoord1sv, glTexCoord2d, glTexCoord2dv, glTexCoord2f, glTexCoord2fv, glTexCoord2i, glTexCoord2iv, glTexCoord2s, glTexCoord2sv, glTexCoord3d, glTexCoord3dv, glTexCoord3f, glTexCoord3fv, glTexCoord3i, glTexCoord3iv, glTexCoord3s, glTexCoord3sv, glTexCoord4d, glTexCoord4dv, glTexCoord4f, glTexCoord4fv, glTexCoord4i, glTexCoord4iv, glTexCoord4s, glTexCoord4sv, glTexCoordPointer, glTexEnvf, glTexEnvfv, glTexEnvi, glTexEnviv, glTexGend, glTexGendv, glTexGenf, glTexGenfv, glTexGeni, glTexGeniv, glTranslated, glTranslatef, glVertex2d, glVertex2dv, glVertex2f, glVertex2fv, glVertex2i, glVertex2iv, glVertex2s, glVertex2sv, glVertex3d, glVertex3dv, glVertex3f, glVertex3fv, glVertex3i, glVertex3iv, glVertex3s, glVertex3sv, glVertex4d, glVertex4dv, glVertex4f, glVertex4fv, glVertex4i, glVertex4iv, glVertex4s, glVertex4sv, glVertexAttrib1d, glVertexAttrib1dv, glVertexAttrib1f, glVertexAttrib1fv, glVertexAttrib1s, glVertexAttrib1sv, glVertexAttrib2d, glVertexAttrib2dv, glVertexAttrib2f, glVertexAttrib2fv, glVertexAttrib2s, glVertexAttrib2sv, glVertexAttrib3d, glVertexAttrib3dv, glVertexAttrib3f, glVertexAttrib3fv, glVertexAttrib3s, glVertexAttrib3sv, glVertexAttrib4Nbv, glVertexAttrib4Niv, glVertexAttrib4Nsv, glVertexAttrib4Nub, glVertexAttrib4Nubv, glVertexAttrib4Nuiv, glVertexAttrib4Nusv, glVertexAttrib4bv, glVertexAttrib4d, glVertexAttrib4dv, glVertexAttrib4f, glVertexAttrib4fv, glVertexAttrib4iv, glVertexAttrib4s, glVertexAttrib4sv, glVertexAttrib4ubv, glVertexAttrib4uiv, glVertexAttrib4usv, glVertexAttribI1i, glVertexAttribI1iv, glVertexAttribI1ui, glVertexAttribI1uiv, glVertexAttribI2i, glVertexAttribI2iv, glVertexAttribI2ui, glVertexAttribI2uiv, glVertexAttribI3i, glVertexAttribI3iv, glVertexAttribI3ui, glVertexAttribI3uiv, glVertexAttribI4bv, glVertexAttribI4i, glVertexAttribI4iv, glVertexAttribI4sv, glVertexAttribI4ubv, glVertexAttribI4ui, glVertexAttribI4uiv, glVertexAttribI4usv, glVertexPointer, glWindowPos2d, glWindowPos2dv, glWindowPos2f, glWindowPos2fv, glWindowPos2i, glWindowPos2iv, glWindowPos2s, glWindowPos2sv, glWindowPos3d, glWindowPos3dv, glWindowPos3f, glWindowPos3fv, glWindowPos3i, glWindowPos3iv, glWindowPos3s, glWindowPos3sv, """ _inject() ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/gloo/context.py�������������������������������������������������������������������0000664�0001750�0001750�00000017764�12527672621�020564� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Functionality to deal with GL Contexts in vispy. This module is defined in gloo, because gloo (and the layers that depend on it) need to be context aware. The vispy.app module "provides" a context, and therefore depends on this module. Although the GLContext class is aimed for use by vispy.app (for practical reasons), it should be possible to use GLContext without using vispy.app by overloading it in an appropriate manner. An GLContext object acts as a placeholder on which different parts of vispy (or other systems) can keep track of information related to an OpenGL context. """ from copy import deepcopy import weakref from .glir import GlirQueue, BaseGlirParser, GlirParser, glir_logger from .wrappers import BaseGlooFunctions from .. import config _default_dict = dict(red_size=8, green_size=8, blue_size=8, alpha_size=8, depth_size=16, stencil_size=0, double_buffer=True, stereo=False, samples=0) canvasses = [] def get_default_config(): """Get the default OpenGL context configuration Returns ------- config : dict Dictionary of config values. """ return deepcopy(_default_dict) def get_current_canvas(): """ Get the currently active canvas Returns None if there is no canvas available. A canvas is made active on initialization and before the draw event is emitted. When a gloo object is created, it is associated with the currently active Canvas, or with the next Canvas to be created if there is no current Canvas. Use Canvas.set_current() to manually activate a canvas. """ cc = [c() for c in canvasses if c() is not None] if cc: return cc[-1] else: return None def set_current_canvas(canvas): """ Make a canvas active. Used primarily by the canvas itself. """ # Notify glir canvas.context._do_CURRENT_command = True # Try to be quick if canvasses and canvasses[-1]() is canvas: return # Make this the current cc = [c() for c in canvasses if c() is not None] while canvas in cc: cc.remove(canvas) cc.append(canvas) canvasses[:] = [weakref.ref(c) for c in cc] def forget_canvas(canvas): """ Forget about the given canvas. Used by the canvas when closed. """ cc = [c() for c in canvasses if c() is not None] while canvas in cc: cc.remove(canvas) canvasses[:] = [weakref.ref(c) for c in cc] class GLContext(BaseGlooFunctions): """An object encapsulating data necessary for a OpenGL context Parameters ---------- config : dict | None The requested configuration. shared : instance of GLContext | None The shared context. """ def __init__(self, config=None, shared=None): self._set_config(config) self._shared = shared if (shared is not None) else GLShared() assert isinstance(self._shared, GLShared) self._glir = GlirQueue() self._do_CURRENT_command = False # flag that CURRENT cmd must be given def __repr__(self): return "" % id(self) def _set_config(self, config): self._config = deepcopy(_default_dict) self._config.update(config or {}) # Check the config dict for key, val in self._config.items(): if key not in _default_dict: raise KeyError('Key %r is not a valid GL config key.' % key) if not isinstance(val, type(_default_dict[key])): raise TypeError('Context value of %r has invalid type.' % key) def create_shared(self, name, ref): """ For the app backends to create the GLShared object. Parameters ---------- name : str The name. ref : object The reference. """ if self._shared is not None: raise RuntimeError('Can only set_shared once.') self._shared = GLShared(name, ref) @property def config(self): """ A dictionary describing the configuration of this GL context. """ return self._config @property def glir(self): """ The glir queue for the context. This queue is for objects that can be shared accross canvases (if they share a contex). """ return self._glir @property def shared(self): """ Get the object that represents the namespace that can potentially be shared between multiple contexts. """ return self._shared def flush_commands(self, event=None): """ Flush Parameters ---------- event : instance of Event The event. """ if self._do_CURRENT_command: self._do_CURRENT_command = False self.shared.parser.parse([('CURRENT', 0)]) self.glir.flush(self.shared.parser) class GLShared(object): """ Representation of a "namespace" that can be shared between different contexts. App backends can associate themselves with this object via add_ref(). This object can be used to establish whether two contexts/canvases share objects, and can be used as a placeholder to store shared information, such as glyph atlasses. """ # We keep a (weak) ref of each backend that gets associated with # this object. In theory, this means that multiple canvases can # be created and also deleted; as long as there is at least one # left, things should Just Work. def __init__(self): glir_file = config['glir_file'] parser_cls = GlirParser if glir_file: parser_cls = glir_logger(parser_cls, glir_file) self._parser = parser_cls() self._name = None self._refs = [] def __repr__(self): return "" % (str(self.name), id(self)) @property def parser(self): """The GLIR parser (shared between contexts) """ return self._parser @parser.setter def parser(self, parser): assert isinstance(parser, BaseGlirParser) or parser is None self._parser = parser def add_ref(self, name, ref): """ Add a reference for the backend object that gives access to the low level context. Used in vispy.app.canvas.backends. The given name must match with that of previously added references. """ if self._name is None: self._name = name elif name != self._name: raise RuntimeError('Contexts can only share between backends of ' 'the same type') self._refs.append(weakref.ref(ref)) @property def name(self): """ The name of the canvas backend that this shared namespace is associated with. Can be None. """ return self._name @property def ref(self): """ A reference (stored internally via a weakref) to an object that the backend system can use to obtain the low-level information of the "reference context". In Vispy this will typically be the CanvasBackend object. """ # Clean self._refs = [r for r in self._refs if (r() is not None)] # Get ref ref = self._refs[0]() if self._refs else None if ref is not None: return ref else: raise RuntimeError('No reference for available for GLShared') class FakeCanvas(object): """ Fake canvas to allow using gloo without vispy.app Instantiate this class to collect GLIR commands from gloo interactions. Call flush() in your draw event handler to execute the commands in the active contect. """ def __init__(self): self.context = GLContext() set_current_canvas(self) def flush(self): """ Flush commands. Call this after setting to context to current. """ self.context.flush_commands() ������������vispy-0.4.0/vispy/gloo/framebuffer.py���������������������������������������������������������������0000664�0001750�0001750�00000021552�12527672621�021352� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2015, Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- from .globject import GLObject from .texture import Texture2D from .wrappers import _check_valid, read_pixels from .context import get_current_canvas from ..ext.six import string_types # ------------------------------------------------------ RenderBuffer class --- class RenderBuffer(GLObject): """ Base class for render buffer object A render buffer can be in color, depth or stencil format. If this format is not specified, it is set when attached to the FrameBuffer. Parameters ---------- shape : tuple The shape of the render buffer. format : {None, 'color', 'depth', 'stencil'} The format of the render buffer. See resize. resizeable : bool Indicates whether texture can be resized """ _GLIR_TYPE = 'RenderBuffer' def __init__(self, shape=None, format=None, resizeable=True): GLObject.__init__(self) self._format = None self._resizeable = True self.resize(shape, format) self._resizeable = bool(resizeable) @property def shape(self): """RenderBuffer shape """ return self._shape @property def format(self): """ RenderBuffer format """ return self._format def resize(self, shape, format=None): """ Set the render-buffer size and format Parameters ---------- shape : tuple of integers New shape in yx order. A render buffer is always 2D. For symmetry with the texture class, a 3-element tuple can also be given, in which case the last dimension is ignored. format : {None, 'color', 'depth', 'stencil'} The buffer format. If None, the current format is maintained. If that is also None, the format will be set upon attaching it to a framebuffer. One can also specify the explicit enum: GL_RGB565, GL_RGBA4, GL_RGB5_A1, GL_DEPTH_COMPONENT16, or GL_STENCIL_INDEX8 """ if not self._resizeable: raise RuntimeError("RenderBuffer is not resizeable") # Check shape if not (isinstance(shape, tuple) and len(shape) in (2, 3)): raise ValueError('RenderBuffer shape must be a 2/3 element tuple') # Check format if format is None: format = self._format # Use current format (may be None) elif isinstance(format, int): pass # Do not check, maybe user needs desktop GL formats elif isinstance(format, string_types): if format not in ('color', 'depth', 'stencil'): raise ValueError('RenderBuffer format must be "color", "depth"' ' or "stencil", not %r' % format) else: raise ValueError('Invalid RenderBuffer format: %r' % format) # Store and send GLIR command self._shape = tuple(shape[:2]) self._format = format if self._format is not None: self._glir.command('SIZE', self._id, self._shape, self._format) # ------------------------------------------------------- FrameBuffer class --- class FrameBuffer(GLObject): """ Frame buffer object Parameters ---------- color : RenderBuffer (optional) The color buffer to attach to this frame buffer depth : RenderBuffer (optional) The depth buffer to attach to this frame buffer stencil : RenderBuffer (optional) The stencil buffer to attach to this frame buffer """ _GLIR_TYPE = 'FrameBuffer' def __init__(self, color=None, depth=None, stencil=None): GLObject.__init__(self) # Init buffers self._color_buffer = None self._depth_buffer = None self._stencil_buffer = None if color is not None: self.color_buffer = color if depth is not None: self.depth_buffer = depth if stencil is not None: self.stencil_buffer = stencil def activate(self): """ Activate/use this frame buffer. """ # Send command self._glir.command('FRAMEBUFFER', self._id, True) # Associate canvas now canvas = get_current_canvas() if canvas is not None: canvas.context.glir.associate(self.glir) def deactivate(self): """ Stop using this frame buffer, the previous framebuffer will be made active. """ self._glir.command('FRAMEBUFFER', self._id, False) def __enter__(self): self.activate() return self def __exit__(self, t, val, trace): self.deactivate() def _set_buffer(self, buffer, format): formats = ('color', 'depth', 'stencil') assert format in formats # Auto-format or check render buffer if isinstance(buffer, RenderBuffer): if buffer.format is None: buffer.resize(buffer.shape, format) elif buffer.format in formats and buffer.format != format: raise ValueError('Cannot attach a %s buffer as %s buffer.' % (buffer.format, format)) # Attach if buffer is None: setattr(self, '_%s_buffer' % format, None) self._glir.command('ATTACH', self._id, format, 0) elif isinstance(buffer, (Texture2D, RenderBuffer)): self.glir.associate(buffer.glir) setattr(self, '_%s_buffer' % format, buffer) self._glir.command('ATTACH', self._id, format, buffer.id) else: raise TypeError("Buffer must be a RenderBuffer, Texture2D or None." " (got %s)" % type(buffer)) @property def color_buffer(self): """Color buffer attachment""" return self._color_buffer @color_buffer.setter def color_buffer(self, buffer): self._set_buffer(buffer, 'color') @property def depth_buffer(self): """Depth buffer attachment""" return self._depth_buffer @depth_buffer.setter def depth_buffer(self, buffer): self._set_buffer(buffer, 'depth') @property def stencil_buffer(self): """Stencil buffer attachment""" return self._stencil_buffer @stencil_buffer.setter def stencil_buffer(self, buffer): self._set_buffer(buffer, 'stencil') @property def shape(self): """ The shape of the Texture/RenderBuffer attached to this FrameBuffer """ if self.color_buffer is not None: return self.color_buffer.shape[:2] # in case its a texture if self.depth_buffer is not None: return self.depth_buffer.shape[:2] if self.stencil_buffer is not None: return self.stencil_buffer.shape[:2] raise RuntimeError('FrameBuffer without buffers has undefined shape') def resize(self, shape): """ Resize all attached buffers with the given shape Parameters ---------- shape : tuple of two integers New buffer shape (h, w), to be applied to all currently attached buffers. For buffers that are a texture, the number of color channels is preserved. """ # Check if not (isinstance(shape, tuple) and len(shape) == 2): raise ValueError('RenderBuffer shape must be a 2-element tuple') # Resize our buffers for buf in (self.color_buffer, self.depth_buffer, self.stencil_buffer): if buf is None: continue shape_ = shape if isinstance(buf, Texture2D): shape_ = shape + (self.color_buffer.shape[-1], ) buf.resize(shape_, buf.format) def read(self, mode='color', alpha=True): """ Return array of pixel values in an attached buffer Parameters ---------- mode : str The buffer type to read. May be 'color', 'depth', or 'stencil'. alpha : bool If True, returns RGBA array. Otherwise, returns RGB. Returns ------- buffer : array 3D array of pixels in np.uint8 format. The array shape is (h, w, 3) or (h, w, 4), with the top-left corner of the framebuffer at index [0, 0] in the returned array. """ _check_valid('mode', mode, ['color', 'depth', 'stencil']) buffer = getattr(self, mode+'_buffer') h, w = buffer.shape[:2] # todo: this is ostensibly required, but not available in gloo.gl #gl.glReadBuffer(buffer._target) return read_pixels((0, 0, w, h), alpha=alpha) ������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/gloo/buffer.py��������������������������������������������������������������������0000664�0001750�0001750�00000037645�12527672621�020351� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2015, Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- import numpy as np from os import path as op from traceback import extract_stack, format_list import weakref from . globject import GLObject from ..util import logger from ..ext.six import string_types # ------------------------------------------------------------ Buffer class --- class Buffer(GLObject): """ Generic GPU buffer. A generic buffer is an interface used to upload data to a GPU array buffer (ARRAY_BUFFER or ELEMENT_ARRAY_BUFFER). It keeps track of buffer size but does not have any CPU storage. You can consider it as write-only. The `set_data` is a deferred operation: you can call it even if an OpenGL context is not available. The `update` function is responsible to upload pending data to GPU memory and requires an active GL context. The Buffer class only deals with data in terms of bytes; it is not aware of data type or element size. Parameters ---------- data : ndarray | None Buffer data. nbytes : int | None Buffer byte size. """ def __init__(self, data=None, nbytes=None): GLObject.__init__(self) self._views = [] # Views on this buffer (stored using weakrefs) self._valid = True # To invalidate buffer views self._nbytes = 0 # Bytesize in bytes, set in resize_bytes() # Set data if data is not None: if nbytes is not None: raise ValueError("Cannot specify both data and nbytes.") self.set_data(data, copy=False) elif nbytes is not None: self.resize_bytes(nbytes) @property def nbytes(self): """ Buffer size in bytes """ return self._nbytes def set_subdata(self, data, offset=0, copy=False): """ Set a sub-region of the buffer (deferred operation). Parameters ---------- data : ndarray Data to be uploaded offset: int Offset in buffer where to start copying data (in bytes) copy: bool Since the operation is deferred, data may change before data is actually uploaded to GPU memory. Asking explicitly for a copy will prevent this behavior. """ data = np.array(data, copy=copy) nbytes = data.nbytes if offset < 0: raise ValueError("Offset must be positive") elif (offset + nbytes) > self._nbytes: raise ValueError("Data does not fit into buffer") # If the whole buffer is to be written, we clear any pending data # (because they will be overwritten anyway) if nbytes == self._nbytes and offset == 0: self._glir.command('SIZE', self._id, nbytes) self._glir.command('DATA', self._id, offset, data) def set_data(self, data, copy=False): """ Set data in the buffer (deferred operation). This completely resets the size and contents of the buffer. Parameters ---------- data : ndarray Data to be uploaded copy: bool Since the operation is deferred, data may change before data is actually uploaded to GPU memory. Asking explicitly for a copy will prevent this behavior. """ data = np.array(data, copy=copy) nbytes = data.nbytes if nbytes != self._nbytes: self.resize_bytes(nbytes) else: # Use SIZE to discard any previous data setting self._glir.command('SIZE', self._id, nbytes) if nbytes: # Only set data if there *is* data self._glir.command('DATA', self._id, 0, data) def resize_bytes(self, size): """ Resize this buffer (deferred operation). Parameters ---------- size : int New buffer size in bytes. """ self._nbytes = size self._glir.command('SIZE', self._id, size) # Invalidate any view on this buffer for view in self._views: if view() is not None: view()._valid = False self._views = [] # -------------------------------------------------------- DataBuffer class --- class DataBuffer(Buffer): """ GPU data buffer that is aware of data type and elements size Parameters ---------- data : ndarray | None Buffer data. """ def __init__(self, data=None): self._size = 0 # number of elements in buffer, set in resize_bytes() self._dtype = None self._stride = 0 self._itemsize = 0 self._last_dim = None Buffer.__init__(self, data) def _prepare_data(self, data): # Can be overrriden by subclasses if not isinstance(data, np.ndarray): raise TypeError("DataBuffer data must be numpy array.") return data def set_subdata(self, data, offset=0, copy=False, **kwargs): """ Set a sub-region of the buffer (deferred operation). Parameters ---------- data : ndarray Data to be uploaded offset: int Offset in buffer where to start copying data (in bytes) copy: bool Since the operation is deferred, data may change before data is actually uploaded to GPU memory. Asking explicitly for a copy will prevent this behavior. **kwargs : dict Additional keyword arguments. """ data = self._prepare_data(data, **kwargs) offset = offset * self.itemsize Buffer.set_subdata(self, data=data, offset=offset, copy=copy) def set_data(self, data, copy=False, **kwargs): """ Set data (deferred operation) Parameters ---------- data : ndarray Data to be uploaded copy: bool Since the operation is deferred, data may change before data is actually uploaded to GPU memory. Asking explicitly for a copy will prevent this behavior. **kwargs : dict Additional arguments. """ data = self._prepare_data(data, **kwargs) self._dtype = data.dtype self._stride = data.strides[-1] self._itemsize = self._dtype.itemsize Buffer.set_data(self, data=data, copy=copy) @property def dtype(self): """ Buffer dtype """ return self._dtype @property def offset(self): """ Buffer offset (in bytes) relative to base """ return 0 @property def stride(self): """ Stride of data in memory """ return self._stride @property def size(self): """ Number of elements in the buffer """ return self._size @property def itemsize(self): """ The total number of bytes required to store the array data """ return self._itemsize @property def glsl_type(self): """ GLSL declaration strings required for a variable to hold this data. """ if self.dtype is None: return None dtshape = self.dtype[0].shape n = dtshape[0] if dtshape else 1 if n > 1: dtype = 'vec%d' % n else: dtype = 'float' if 'f' in self.dtype[0].base.kind else 'int' return 'attribute', dtype def resize_bytes(self, size): """ Resize the buffer (in-place, deferred operation) Parameters ---------- size : integer New buffer size in bytes Notes ----- This clears any pending operations. """ Buffer.resize_bytes(self, size) self._size = size // self.itemsize def __getitem__(self, key): """ Create a view on this buffer. """ view = DataBufferView(self, key) self._views.append(weakref.ref(view)) return view def __setitem__(self, key, data): """ Set data (deferred operation) """ # Setting a whole field of the buffer: only allowed if we have CPU # storage. Note this case (key is string) only happen with base buffer if isinstance(key, string_types): raise ValueError("Cannot set non-contiguous data on buffer") # Setting one or several elements elif isinstance(key, int): if key < 0: key += self.size if key < 0 or key > self.size: raise IndexError("Buffer assignment index out of range") start, stop, step = key, key + 1, 1 elif isinstance(key, slice): start, stop, step = key.indices(self.size) if stop < start: start, stop = stop, start elif key == Ellipsis: start, stop, step = 0, self.size, 1 else: raise TypeError("Buffer indices must be integers or strings") # Contiguous update? if step != 1: raise ValueError("Cannot set non-contiguous data on buffer") # Make sure data is an array if not isinstance(data, np.ndarray): data = np.array(data, dtype=self.dtype, copy=False) # Make sure data is big enough if data.size < stop - start: data = np.resize(data, stop - start) elif data.size > stop - start: raise ValueError('Data too big to fit GPU data.') # Set data offset = start # * self.itemsize self.set_subdata(data=data, offset=offset, copy=True) def __repr__(self): return ("<%s size=%s last_dim=%s>" % (self.__class__.__name__, self.size, self._last_dim)) class DataBufferView(DataBuffer): """ View on a sub-region of a DataBuffer. Parameters ---------- base : DataBuffer The buffer accessed by this view. key : str, int, slice, or Ellpsis The index into the base buffer that defines a sub-region of the buffer to view. String arguments select a single field from multi-field dtypes, and other allowed types select a subset of rows. Notes ----- It is generally not necessary to instantiate this class manually; use ``base_buffer[key]`` instead. """ # Note that this class is a bit evil: it is a subclass of GLObject, # Buffer and DataBuffer, but any of these __init__'s are not called ... def __init__(self, base, key): # Note how this never runs the super's __init__, # all attributes must thus be set here ... self._base = base self._key = key self._stride = base.stride if isinstance(key, string_types): self._dtype = base.dtype[key] self._offset = base.dtype.fields[key][1] self._nbytes = base.size * self._dtype.itemsize self._size = base.size self._itemsize = self._dtype.itemsize return if isinstance(key, int): if key < 0: key += base.size if key < 0 or key > base.size: raise IndexError("Buffer assignment index out of range") start, stop, step = key, key + 1, 1 elif isinstance(key, slice): start, stop, step = key.indices(base.size) if stop < start: start, stop = stop, start elif key == Ellipsis: start, stop, step = 0, base.size, 1 else: raise TypeError("Buffer indices must be integers or strings") if step != 1: raise ValueError("Cannot access non-contiguous data") self._itemsize = base.itemsize self._offset = start * self.itemsize self._size = stop - start self._dtype = base.dtype self._nbytes = self.size * self.itemsize @property def glir(self): return self._base.glir @property def id(self): return self._base.id @property def _last_dim(self): return self._base._last_dim def set_subdata(self, data, offset=0, copy=False, **kwargs): raise RuntimeError("Cannot set data on buffer view.") def set_data(self, data, copy=False, **kwargs): raise RuntimeError("Cannot set data on buffer view.") @property def offset(self): """ Buffer offset (in bytes) relative to base """ return self._offset @property def base(self): """Buffer base if this buffer is a view on another buffer. """ return self._base def resize_bytes(self, size): raise RuntimeError("Cannot resize buffer view.") def __getitem__(self, key): raise RuntimeError("Can only access data from a base buffer") def __setitem__(self, key, data): raise RuntimeError("Cannot set data on Buffer view") def __repr__(self): return ("" % (self.base, self.offset, self.size)) # ------------------------------------------------------ VertexBuffer class --- class VertexBuffer(DataBuffer): """ Buffer for vertex attribute data Parameters ---------- data : ndarray Buffer data (optional) """ _GLIR_TYPE = 'VertexBuffer' def _prepare_data(self, data, convert=False): # Build a structured view of the data if: # -> it is not already a structured array # -> shape if 1-D or last dimension is 1,2,3 or 4 if isinstance(data, list): data = np.array(data, dtype=np.float32) if not isinstance(data, np.ndarray): raise ValueError('Data must be a ndarray (got %s)' % type(data)) if data.dtype.isbuiltin: if convert is True: data = data.astype(np.float32) if data.dtype in (np.float64, np.int64): raise TypeError('data must be 32-bit not %s' % data.dtype) c = data.shape[-1] if data.ndim > 1 else 1 if c in [2, 3, 4]: if not data.flags['C_CONTIGUOUS']: logger.warning('Copying discontiguous data for struct ' 'dtype:\n%s' % _last_stack_str()) data = data.copy() else: c = 1 if self._last_dim and c != self._last_dim: raise ValueError('Last dimension should be %s not %s' % (self._last_dim, c)) data = data.view(dtype=[('f0', data.dtype.base, c)]) self._last_dim = c return data def _last_stack_str(): """Print stack trace from call that didn't originate from here""" stack = extract_stack() for s in stack[::-1]: if op.join('vispy', 'gloo', 'buffer.py') not in __file__: break return format_list([s])[0] # ------------------------------------------------------- IndexBuffer class --- class IndexBuffer(DataBuffer): """ Buffer for index data Parameters ---------- data : ndarray | None Buffer data. """ _GLIR_TYPE = 'IndexBuffer' def __init__(self, data=None): DataBuffer.__init__(self, data) self._last_dim = 1 def _prepare_data(self, data, convert=False): if isinstance(data, list): data = np.array(data, dtype=np.uint32) if not isinstance(data, np.ndarray): raise ValueError('Data must be a ndarray (got %s)' % type(data)) if not data.dtype.isbuiltin: raise TypeError("Element buffer dtype cannot be structured") else: if convert: if data.dtype is not np.uint32: data = data.astype(np.uint32) else: if data.dtype not in [np.uint32, np.uint16, np.uint8]: raise TypeError("Invalid dtype for IndexBuffer: %r" % data.dtype) return data �������������������������������������������������������������������������������������������vispy-0.4.0/vispy/gloo/preprocessor.py��������������������������������������������������������������0000664�0001750�0001750�00000004372�12527672621�021615� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ # -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2015, Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- import re from .. import glsl from ..util import logger def remove_comments(code): """Remove C-style comment from GLSL code string.""" pattern = r"(\".*?\"|\'.*?\')|(/\*.*?\*/|//[^\r\n]*\n)" # first group captures quoted strings (double or single) # second group captures comments (//single-line or /* multi-line */) regex = re.compile(pattern, re.MULTILINE | re.DOTALL) def do_replace(match): # if the 2nd group (capturing comments) is not None, # it means we have captured a non-quoted (real) comment string. if match.group(2) is not None: return "" # so we will return empty to remove the comment else: # otherwise, we will return the 1st group return match.group(1) # captured quoted-string return regex.sub(do_replace, code) def merge_includes(code): """Merge all includes recursively.""" pattern = '\#\s*include\s*"(?P[a-zA-Z0-9\_\-\.\/]+)"' regex = re.compile(pattern) includes = [] def replace(match): filename = match.group("filename") if filename not in includes: includes.append(filename) path = glsl.find(filename) if not path: logger.critical('"%s" not found' % filename) raise RuntimeError("File not found", filename) text = '\n// --- start of "%s" ---\n' % filename with open(path) as fh: text += fh.read() text += '// --- end of "%s" ---\n' % filename return text return '' # Limit recursion to depth 10 for i in range(10): if re.search(regex, code): code = re.sub(regex, replace, code) else: break return code def preprocess(code): """Preprocess a code by removing comments, version and merging includes.""" if code: #code = remove_comments(code) code = merge_includes(code) return code ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/gloo/program.py�������������������������������������������������������������������0000664�0001750�0001750�00000046261�12527672621�020541� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2015, Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Implementation of a GL Program object. This class parses the source code to obtain the names and types of uniforms, attributes, varyings and constants. This information is used to provide the user with a natural way to set variables. Gloo vs GLIR ------------ Done in this class: * Check the data shape given for uniforms and attributes * Convert uniform data to array of the correct type * Check whether any variables are set that are not present in source code Done by GLIR: * Check whether a set uniform/attribute is not active (a warning is given) * Check whether anactive attribute or uniform is not set (a warning is given) """ import re import numpy as np from .globject import GLObject from .buffer import VertexBuffer, IndexBuffer, DataBuffer from .texture import BaseTexture, Texture2D, Texture3D, Texture1D from ..util import logger from .util import check_enum from ..ext.six import string_types from .context import get_current_canvas from .preprocessor import preprocess # ----------------------------------------------------------- Program class --- class Program(GLObject): """ Shader program object A Program is an object to which shaders can be attached and linked to create the final program. Uniforms and attributes can be set using indexing: e.g. ``program['a_pos'] = pos_data`` and ``program['u_color'] = (1, 0, 0)``. Parameters ---------- vert : str The vertex shader to be used by this program frag : str The fragment shader to be used by this program count : int (optional) The program will prepare a structured vertex buffer of count vertices. All attributes set using ``prog['attr'] = X`` will be combined into a structured vbo with interleaved elements, which is more efficient than having one vbo per attribute. Notes ----- If several shaders are specified, only one can contain the main function. OpenGL ES 2.0 does not support a list of shaders. """ _GLIR_TYPE = 'Program' _gtypes = { # DTYPE, NUMEL 'float': (np.float32, 1), 'vec2': (np.float32, 2), 'vec3': (np.float32, 3), 'vec4': (np.float32, 4), 'int': (np.int32, 1), 'ivec2': (np.int32, 2), 'ivec3': (np.int32, 3), 'ivec4': (np.int32, 4), 'bool': (np.bool, 1), 'bvec2': (np.bool, 2), 'bvec3': (np.bool, 3), 'bvec4': (np.bool, 4), 'mat2': (np.float32, 4), 'mat3': (np.float32, 9), 'mat4': (np.float32, 16), 'sampler1D': (np.uint32, 1), 'sampler2D': (np.uint32, 1), 'sampler3D': (np.uint32, 1), } # --------------------------------- def __init__(self, vert=None, frag=None, count=0): GLObject.__init__(self) # Init source code for vertex and fragment shader self._shaders = '', '' # Init description of variables obtained from source code self._code_variables = {} # name -> (kind, type_, name) # Init user-defined data for attributes and uniforms self._user_variables = {} # name -> data / buffer / texture # Init pending user-defined data self._pending_variables = {} # name -> data # NOTE: we *could* allow vert and frag to be a tuple/list of shaders, # but that would complicate the GLIR implementation, and it seems # unncessary # Check and set shaders if isinstance(vert, string_types) and isinstance(frag, string_types): self.set_shaders(vert, frag) elif not (vert is None and frag is None): raise ValueError('Vert and frag must either both be str or None') # Build associated structured vertex buffer if count is given. # This makes it easy to create a structured vertex buffer # without having to create a numpy array with structured dtype. # All assignments must be done before the GLIR commands are # sent away for parsing (in draw) though. self._count = count self._buffer = None # Set to None in draw() if self._count > 0: dtype = [] for kind, type_, name, size in self._code_variables.values(): if kind == 'attribute': dt, numel = self._gtypes[type_] dtype.append((name, dt, numel)) self._buffer = np.zeros(self._count, dtype=dtype) self.bind(VertexBuffer(self._buffer)) def set_shaders(self, vert, frag): """ Set the vertex and fragment shaders. Parameters ---------- vert : str Source code for vertex shader. frag : str Source code for fragment shaders. """ if not vert or not frag: raise ValueError('Vertex and fragment code must both be non-empty') # pre-process shader code for #include directives vert, frag = preprocess(vert), preprocess(frag) # Store source code, send it to glir, parse the code for variables self._shaders = vert, frag self._glir.command('SHADERS', self._id, vert, frag) # All current variables become pending variables again for key, val in self._user_variables.items(): self._pending_variables[key] = val self._user_variables = {} # Parse code (and process pending variables) self._parse_variables_from_code() @property def shaders(self): """ Source code for vertex and fragment shader """ return self._shaders @property def variables(self): """ A list of the variables in use by the current program The list is obtained by parsing the GLSL source code. Returns ------- variables : list Each variable is represented as a tuple (kind, type, name), where `kind` is 'attribute', 'uniform', 'uniform_array', 'varying' or 'const'. """ # Note that internally the variables are stored as a dict # that maps names -> tuples, for easy looking up by name. return [x[:3] for x in self._code_variables.values()] def _parse_variables_from_code(self): """ Parse uniforms, attributes and varyings from the source code. """ # Get one string of code with comments removed code = '\n\n'.join(self._shaders) code = re.sub(r'(.*)(//.*)', r'\1', code, re.M) # Regexp to look for variable names var_regexp = ("\s*VARIABLE\s+" # kind of variable "((highp|mediump|lowp)\s+)?" # Precision (optional) "(?P\w+)\s+" # type "(?P\w+)\s*" # name "(\[(?P\d+)\])?" # size (optional) "(\s*\=\s*[0-9.]+)?" # default value (optional) "\s*;" # end ) # Parse uniforms, attributes and varyings self._code_variables = {} for kind in ('uniform', 'attribute', 'varying', 'const'): regex = re.compile(var_regexp.replace('VARIABLE', kind), flags=re.MULTILINE) for m in re.finditer(regex, code): gtype = m.group('type') size = int(m.group('size')) if m.group('size') else -1 this_kind = kind if size >= 1: # uniform arrays get added both as individuals and full for i in range(size): name = '%s[%d]' % (m.group('name'), i) self._code_variables[name] = kind, gtype, name, -1 this_kind = 'uniform_array' name = m.group('name') self._code_variables[name] = this_kind, gtype, name, size # Now that our code variables are up-to date, we can process # the variables that were set but yet unknown. self._process_pending_variables() def bind(self, data): """ Bind a VertexBuffer that has structured data Parameters ---------- data : VertexBuffer The vertex buffer to bind. The field names of the array are mapped to attribute names in GLSL. """ # Check if not isinstance(data, VertexBuffer): raise ValueError('Program.bind() requires a VertexBuffer.') # Apply for name in data.dtype.names: self[name] = data[name] def _process_pending_variables(self): """ Try to apply the variables that were set but not known yet. """ # Clear our list of pending variables self._pending_variables, pending = {}, self._pending_variables # Try to apply it. On failure, it will be added again for name, data in pending.items(): self[name] = data def __setitem__(self, name, data): """ Setting uniform or attribute data This method requires the information about the variable that we know from parsing the source code. If this information is not yet available, the data is stored in a list of pending data, and we attempt to set it once new shading code has been set. For uniforms, the data can represent a plain uniform or a sampler. In the latter case, this method accepts a Texture object or a numpy array which is used to update the existing texture. A new texture is created if necessary. For attributes, the data can be a tuple/float which GLSL will use for the value of all vertices. This method also acceps VBO data as a VertexBuffer object or a numpy array which is used to update the existing VertexBuffer. A new VertexBuffer is created if necessary. By passing None as data, the uniform or attribute can be "unregistered". This can be useful to get rid of variables that are no longer present or active in the new source code that is about to be set. """ # Deal with local buffer storage (see count argument in __init__) if (self._buffer is not None) and not isinstance(data, DataBuffer): if name in self._buffer.dtype.names: self._buffer[name] = data return # Delete? if data is None: self._user_variables.pop(name, None) self._pending_variables.pop(name, None) return if name in self._code_variables: kind, type_, name, size = self._code_variables[name] if kind == 'uniform': if type_.startswith('sampler'): # Texture data; overwrite or update tex = self._user_variables.get(name, None) if isinstance(data, BaseTexture): pass elif tex and hasattr(tex, 'set_data'): tex.set_data(data) return elif type_ == 'sampler1D': data = Texture1D(data) elif type_ == 'sampler2D': data = Texture2D(data) elif type_ == 'sampler3D': data = Texture3D(data) else: # This should not happen raise RuntimeError('Unknown type %s' % type_) # Store and send GLIR command self._user_variables[name] = data self.glir.associate(data.glir) self._glir.command('TEXTURE', self._id, name, data.id) else: # Normal uniform; convert to np array and check size dtype, numel = self._gtypes[type_] data = np.array(data, dtype=dtype).ravel() if data.size != numel: raise ValueError('Uniform %r needs %i elements, ' 'not %i.' % (name, numel, data.size)) # Store and send GLIR command self._user_variables[name] = data self._glir.command('UNIFORM', self._id, name, type_, data) elif kind == 'uniform_array': # Normal uniform; convert to np array and check size dtype, numel = self._gtypes[type_] data = np.atleast_2d(data).astype(dtype) need_shape = (size, numel) if data.shape != need_shape: raise ValueError('Uniform array %r needs shape %s not %s' % (name, need_shape, data.shape)) data = data.ravel() # Store and send GLIR command self._user_variables[name] = data self._glir.command('UNIFORM', self._id, name, type_, data) elif kind == 'attribute': # Is this a constant value per vertex is_constant = False def isscalar(x): return isinstance(x, (float, int)) if isscalar(data): is_constant = True elif isinstance(data, (tuple, list)): is_constant = all([isscalar(e) for e in data]) if not is_constant: # VBO data; overwrite or update vbo = self._user_variables.get(name, None) if isinstance(data, DataBuffer): pass elif vbo is not None and hasattr(vbo, 'set_data'): vbo.set_data(data) return else: data = VertexBuffer(data) # Store and send GLIR command if data.dtype is not None: numel = self._gtypes[type_][1] if data._last_dim and data._last_dim != numel: raise ValueError('data.shape[-1] must be %s ' 'not %s for %s' % (numel, data._last_dim, name)) self._user_variables[name] = data value = (data.id, data.stride, data.offset) self.glir.associate(data.glir) self._glir.command('ATTRIBUTE', self._id, name, type_, value) else: # Single-value attribute; convert to array and check size dtype, numel = self._gtypes[type_] data = np.array(data, dtype=dtype) if data.ndim == 0: data.shape = data.size if data.size != numel: raise ValueError('Attribute %r needs %i elements, ' 'not %i.' % (name, numel, data.size)) # Store and send GLIR command self._user_variables[name] = data value = tuple([0] + [i for i in data]) self._glir.command('ATTRIBUTE', self._id, name, type_, value) else: raise KeyError('Cannot set data for a %s.' % kind) else: # This variable is not defined in the current source code, # so we cannot establish whether this is a uniform or # attribute, nor check its type. Try again later. self._pending_variables[name] = data def __getitem__(self, name): """ Get user-defined data for attributes and uniforms. """ if name in self._user_variables: return self._user_variables[name] elif name in self._pending_variables: return self._pending_variables[name] else: raise KeyError("Unknown uniform or attribute %s" % name) def draw(self, mode='triangles', indices=None, check_error=True): """ Draw the attribute arrays in the specified mode. Parameters ---------- mode : str | GL_ENUM 'points', 'lines', 'line_strip', 'line_loop', 'triangles', 'triangle_strip', or 'triangle_fan'. indices : array Array of indices to draw. check_error: Check error after draw. """ # Invalidate buffer (data has already been sent) self._buffer = None # Check if mode is valid mode = check_enum(mode) if mode not in ['points', 'lines', 'line_strip', 'line_loop', 'triangles', 'triangle_strip', 'triangle_fan']: raise ValueError('Invalid draw mode: %r' % mode) # Check leftover variables, warn, discard them # In GLIR we check whether all attributes are indeed set for name in self._pending_variables: logger.warn('Variable %r is given but not known.' % name) self._pending_variables = {} # Check attribute sizes attributes = [vbo for vbo in self._user_variables.values() if isinstance(vbo, DataBuffer)] sizes = [a.size for a in attributes] if len(attributes) < 1: raise RuntimeError('Must have at least one attribute') if not all(s == sizes[0] for s in sizes[1:]): msg = '\n'.join(['%s: %s' % (str(a), a.size) for a in attributes]) raise RuntimeError('All attributes must have the same size, got:\n' '%s' % msg) # Get the glir queue that we need now canvas = get_current_canvas() assert canvas is not None # Associate canvas canvas.context.glir.associate(self.glir) # Indexbuffer if isinstance(indices, IndexBuffer): canvas.context.glir.associate(indices.glir) logger.debug("Program drawing %r with index buffer" % mode) gltypes = {np.dtype(np.uint8): 'UNSIGNED_BYTE', np.dtype(np.uint16): 'UNSIGNED_SHORT', np.dtype(np.uint32): 'UNSIGNED_INT'} selection = indices.id, gltypes[indices.dtype], indices.size canvas.context.glir.command('DRAW', self._id, mode, selection) elif indices is None: selection = 0, attributes[0].size logger.debug("Program drawing %r with %r" % (mode, selection)) canvas.context.glir.command('DRAW', self._id, mode, selection) else: raise TypeError("Invalid index: %r (must be IndexBuffer)" % indices) # Process GLIR commands canvas.context.flush_commands() �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/gloo/__init__.py������������������������������������������������������������������0000664�0001750�0001750�00000003712�12527672621�020623� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Object oriented interface to OpenGL. This module implements classes for the things that are "objects" in OpenGL, such as textures, FBO's, VBO's and shaders. Further, some convenience classes are implemented (like the collection class). This set of classes provides a friendly (Pythonic) interface to OpenGL, and is designed to provide OpenGL's full functionality. All classes inherit from GLObject, which provide a basic interface, enabling, activating and deleting the object. Central to each visualization is the Program. Other objects, such as Texture2D and VertexBuffer should be set as uniforms and attributes of the Program object. Example:: # Init program = gloo.Program(vertex_source, fragment_source) program['a_position'] = gloo.VertexBuffer(my_positions_array) program['s_texture'] = gloo.Texture2D(my_image) ... # Draw event handler program['u_color'] = 0.0, 1.0, 0.0 program.draw(gl.GL_TRIANGLES) .. Note:: With vispy.gloo we strive to offer a Python interface that provides the full functionality of OpenGL. However, this layer is a work in progress and there are still a few known limitations. Most notably: * TextureCubeMap is not yet implemented * FBOs can only do 2D textures (not 3D textures or cube maps) * No support for compressed textures. """ from __future__ import division from . import gl # noqa from .wrappers import * # noqa from .context import (GLContext, get_default_config, # noqa get_current_canvas) # noqa from .globject import GLObject # noqa from .buffer import VertexBuffer, IndexBuffer # noqa from .texture import Texture1D, Texture2D, TextureAtlas, Texture3D, TextureEmulated3D # noqa from .program import Program # noqa from .framebuffer import FrameBuffer, RenderBuffer # noqa from . import util # noqa ������������������������������������������������������vispy-0.4.0/vispy/gloo/glir.py����������������������������������������������������������������������0000664�0001750�0001750�00000134577�12527672621�020037� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2015, Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Implementation to execute GL Intermediate Representation (GLIR) """ import sys import re import json import numpy as np from . import gl from ..ext.six import string_types from ..util import logger # TODO: expose these via an extension space in .gl? _internalformats = [ gl.Enum('GL_RED', 6403), gl.Enum('GL_R', 8194), gl.Enum('GL_R8', 33321), gl.Enum('GL_R16', 33322), gl.Enum('GL_R16F', 33325), gl.Enum('GL_R32F', 33326), gl.Enum('GL_RG', 33319), gl.Enum('GL_RG8', 333323), gl.Enum('GL_RG16', 333324), gl.Enum('GL_RG16F', 333327), gl.Enum('GL_RG32F', 33328), gl.Enum('GL_RGB', 6407), gl.Enum('GL_RGB8', 32849), gl.Enum('GL_RGB16', 32852), gl.Enum('GL_RGB16F', 34843), gl.Enum('GL_RGB32F', 34837), gl.Enum('GL_RGBA', 6408), gl.Enum('GL_RGBA8', 32856), gl.Enum('GL_RGBA16', 32859), gl.Enum('GL_RGBA16F', 34842), gl.Enum('GL_RGBA32F', 34836) ] _internalformats = dict([(enum.name, enum) for enum in _internalformats]) # Value to mark a glir object that was just deleted. So we can safely # ignore it (and not raise an error that the object could not be found). # This can happen e.g. if A is created, A is bound to B and then A gets # deleted. The commands may get executed in order: A gets created, A # gets deleted, A gets bound to B. JUST_DELETED = 'JUST_DELETED' def as_enum(enum): """ Turn a possibly string enum into an integer enum. """ if isinstance(enum, string_types): try: enum = getattr(gl, 'GL_' + enum.upper()) except AttributeError: try: enum = _internalformats['GL_' + enum.upper()] except KeyError: raise ValueError('Could not find int value for enum %r' % enum) return enum class GlirQueue(object): """ Representation of a queue of GLIR commands One instance of this class is attached to each context object, and to each gloo object. Upon drawing (i.e. `Program.draw()`) and framebuffer switching, the commands in the queue are pushed to a parser, which is stored at context.shared. The parser can interpret the commands in Python, send them to a browser, etc. """ def __init__(self): self._commands = [] # local commands self._verbose = False self._associations = set() def command(self, *args): """ Send a command. See the command spec at: https://github.com/vispy/vispy/wiki/Spec.-Gloo-IR """ self._commands.append(args) def set_verbose(self, verbose): """ Set verbose or not. If True, the GLIR commands are printed right before they get parsed. If a string is given, use it as a filter. """ self._verbose = verbose def show(self, filter=None): """ Print the list of commands currently in the queue. If filter is given, print only commands that match the filter. """ # Show commands in associated queues for q in self._associations: q.show() for command in self._commands: if command[0] is None: # or command[1] in self._invalid_objects: continue # Skip nill commands if filter and command[0] != filter: continue t = [] for e in command: if isinstance(e, np.ndarray): t.append('array %s' % str(e.shape)) elif isinstance(e, str): s = e.strip() if len(s) > 20: s = s[:18] + '... %i lines' % (e.count('\n')+1) t.append(s) else: t.append(e) print(tuple(t)) def clear(self): """ Pop the whole queue (and associated queues) and return a list of commands. """ # Get all commands, discard deletable queues (ques no longer in use) commands = [] for q in list(self._associations): commands.extend(q.clear()) if hasattr(q, '_deletable'): # this flag gets set by GLObject self._associations.discard(q) commands.extend(self._commands) self._commands[:] = [] return commands def associate(self, queue): """ Associate the given queue. When the current queue gets cleared, it first clears all the associated queues and prepends these commands to the total list. One should call associate() on the queue that relies on the other (e.g. ``program.glir.associate(texture.glir``). """ assert isinstance(queue, GlirQueue) self._associations.add(queue) def flush(self, parser): """ Flush all current commands to the GLIR interpreter. """ # if self._parser is None: # raise RuntimeError('Cannot flush queue if parser is None') if self._verbose: show = self._verbose if isinstance(self._verbose, str) else None self.show(show) parser.parse(self._filter(self.clear(), parser)) def _filter(self, commands, parser): """ Filter DATA/SIZE commands that are overridden by a SIZE command. """ resized = set() commands2 = [] for command in reversed(commands): if command[0] == 'SHADERS': convert = parser.convert_shaders() if convert: shaders = self._convert_shaders(convert, command[2:]) command = command[:2] + shaders elif command[1] in resized: if command[0] in ('SIZE', 'DATA'): continue # remove this command elif command[0] == 'SIZE': resized.add(command[1]) commands2.append(command) return list(reversed(commands2)) def _convert_shaders(self, convert, shaders): return convert_shaders(convert, shaders) def convert_shaders(convert, shaders): """ Modify shading code so that we can write code once and make it run "everywhere". """ # New version of the shaders out = [] if convert == 'es2': for isfragment, shader in enumerate(shaders): has_version = False has_prec_float = False has_prec_int = False lines = [] # Iterate over lines for line in shader.lstrip().splitlines(): if line.startswith('#version'): has_version = True continue if line.startswith('precision '): has_prec_float = has_prec_float or 'float' in line has_prec_int = has_prec_int or 'int' in line lines.append(line.rstrip()) # Write # BUG: fails on WebGL (Chrome) # if True: # lines.insert(has_version, '#line 0') if not has_prec_float: lines.insert(has_version, 'precision highp float;') if not has_prec_int: lines.insert(has_version, 'precision highp int;') # BUG: fails on WebGL (Chrome) # if not has_version: # lines.insert(has_version, '#version 100') out.append('\n'.join(lines)) elif convert == 'desktop': for isfragment, shader in enumerate(shaders): has_version = False lines = [] # Iterate over lines for line in shader.lstrip().splitlines(): has_version = has_version or line.startswith('#version') if line.startswith('precision '): line = '' for prec in (' highp ', ' mediump ', ' lowp '): line = line.replace(prec, ' ') lines.append(line.rstrip()) # Write if not has_version: lines.insert(0, '#version 120\n#line 2\n') out.append('\n'.join(lines)) else: raise ValueError('Cannot convert shaders to %r.' % convert) return tuple(out) def as_es2_command(command): """ Modify a desktop command so it works on es2. """ if command[0] == 'FUNC': return (command[0], re.sub(r'^gl([A-Z])', lambda m: m.group(1).lower(), command[1])) + command[2:] if command[0] == 'SHADERS': return command[:2] + convert_shaders('es2', command[2:]) if command[0] == 'UNIFORM': return command[:-1] + (command[-1].tolist(),) return command class BaseGlirParser(object): """ Base clas for GLIR parsers that can be attached to a GLIR queue. """ def is_remote(self): """ Whether the code is executed remotely. i.e. gloo.gl cannot be used. """ raise NotImplementedError() def convert_shaders(self): """ Whether to convert shading code. Valid values are 'es2' and 'desktop'. If None, the shaders are not modified. """ raise NotImplementedError() def parse(self, commands): """ Parse the GLIR commands. Or sent them away. """ raise NotImplementedError() class GlirParser(BaseGlirParser): """ A class for interpreting GLIR commands using gloo.gl We make use of relatively light GLIR objects that are instantiated on CREATE commands. These objects are stored by their id in a dictionary so that commands like ACTIVATE and DATA can easily be executed on the corresponding objects. """ def __init__(self): self._objects = {} self._invalid_objects = set() self._classmap = {'Program': GlirProgram, 'VertexBuffer': GlirVertexBuffer, 'IndexBuffer': GlirIndexBuffer, 'Texture1D': GlirTexture1D, 'Texture2D': GlirTexture2D, 'Texture3D': GlirTexture3D, 'RenderBuffer': GlirRenderBuffer, 'FrameBuffer': GlirFrameBuffer, } # We keep a dict that the GLIR objects use for storing # per-context information. This dict is cleared each time # that the context is made current. This seems necessary for # when two Canvases share a context. self.env = {} def is_remote(self): return False def convert_shaders(self): if '.es' in gl.current_backend.__name__: return 'es2' else: return 'desktop' def _parse(self, command): """ Parse a single command. """ cmd, id_, args = command[0], command[1], command[2:] if cmd == 'CURRENT': # This context is made current self.env.clear() self._gl_initialize() gl.glBindFramebuffer(gl.GL_FRAMEBUFFER, 0) elif cmd == 'FUNC': # GL function call args = [as_enum(a) for a in args] try: getattr(gl, id_)(*args) except AttributeError: logger.warning('Invalid gl command: %r' % id_) elif cmd == 'CREATE': # Creating an object if args[0] is not None: klass = self._classmap[args[0]] self._objects[id_] = klass(self, id_) else: self._invalid_objects.add(id_) elif cmd == 'DELETE': # Deleting an object ob = self._objects.get(id_, None) if ob is not None: self._objects[id_] = JUST_DELETED ob.delete() else: # Doing somthing to an object ob = self._objects.get(id_, None) if ob == JUST_DELETED: return if ob is None: if id_ not in self._invalid_objects: raise RuntimeError('Cannot %s object %i because it ' 'does not exist' % (cmd, id_)) return # Triage over command. Order of commands is set so most # common ones occur first. if cmd == 'DRAW': # Program ob.draw(*args) elif cmd == 'TEXTURE': # Program ob.set_texture(*args) elif cmd == 'UNIFORM': # Program ob.set_uniform(*args) elif cmd == 'ATTRIBUTE': # Program ob.set_attribute(*args) elif cmd == 'DATA': # VertexBuffer, IndexBuffer, Texture ob.set_data(*args) elif cmd == 'SIZE': # VertexBuffer, IndexBuffer, ob.set_size(*args) # Texture[1D, 2D, 3D], RenderBuffer elif cmd == 'ATTACH': # FrameBuffer ob.attach(*args) elif cmd == 'FRAMEBUFFER': # FrameBuffer ob.set_framebuffer(*args) elif cmd == 'SHADERS': # Program ob.set_shaders(*args) elif cmd == 'WRAPPING': # Texture1D, Texture2D, Texture3D ob.set_wrapping(*args) elif cmd == 'INTERPOLATION': # Texture1D, Texture2D, Texture3D ob.set_interpolation(*args) else: logger.warning('Invalid GLIR command %r' % cmd) def parse(self, commands): """ Parse a list of commands. """ # Get rid of dummy objects that represented deleted objects in # the last parsing round. to_delete = [] for id_, val in self._objects.items(): if val == JUST_DELETED: to_delete.append(id_) for id_ in to_delete: self._objects.pop(id_) for command in commands: self._parse(command) def get_object(self, id_): """ Get the object with the given id or None if it does not exist. """ return self._objects.get(id_, None) def _gl_initialize(self): """ Deal with compatibility; desktop does not have sprites enabled by default. ES has. """ if '.es' in gl.current_backend.__name__: pass # ES2: no action required else: # Desktop, enable sprites GL_VERTEX_PROGRAM_POINT_SIZE = 34370 GL_POINT_SPRITE = 34913 gl.glEnable(GL_VERTEX_PROGRAM_POINT_SIZE) gl.glEnable(GL_POINT_SPRITE) def glir_logger(parser_cls, file_or_filename): from ..util.logs import NumPyJSONEncoder class cls(parser_cls): def __init__(self, *args, **kwargs): parser_cls.__init__(self, *args, **kwargs) if isinstance(file_or_filename, string_types): self._file = open(file_or_filename, 'w') else: self._file = file_or_filename self._file.write('[]') self._empty = True def _parse(self, command): parser_cls._parse(self, command) self._file.seek(self._file.tell() - 1) if self._empty: self._empty = False else: self._file.write(',\n') json.dump(as_es2_command(command), self._file, cls=NumPyJSONEncoder) self._file.write(']') return cls ## GLIR objects class GlirObject(object): def __init__(self, parser, id_): self._parser = parser self._id = id_ self._handle = -1 # Must be set by subclass in create() self.create() @property def handle(self): return self._handle @property def id(self): return self._id def __repr__(self): return '<%s %i at 0x%x>' % (self.__class__.__name__, self.id, id(self)) class GlirProgram(GlirObject): UTYPEMAP = { 'float': 'glUniform1fv', 'vec2': 'glUniform2fv', 'vec3': 'glUniform3fv', 'vec4': 'glUniform4fv', 'int': 'glUniform1iv', 'ivec2': 'glUniform2iv', 'ivec3': 'glUniform3iv', 'ivec4': 'glUniform4iv', 'bool': 'glUniform1iv', 'bvec2': 'glUniform2iv', 'bvec3': 'glUniform3iv', 'bvec4': 'glUniform4iv', 'mat2': 'glUniformMatrix2fv', 'mat3': 'glUniformMatrix3fv', 'mat4': 'glUniformMatrix4fv', 'sampler1D': 'glUniform1i', 'sampler2D': 'glUniform1i', 'sampler3D': 'glUniform1i', } ATYPEMAP = { 'float': 'glVertexAttrib1f', 'vec2': 'glVertexAttrib2f', 'vec3': 'glVertexAttrib3f', 'vec4': 'glVertexAttrib4f', } ATYPEINFO = { 'float': (1, gl.GL_FLOAT, np.float32), 'vec2': (2, gl.GL_FLOAT, np.float32), 'vec3': (3, gl.GL_FLOAT, np.float32), 'vec4': (4, gl.GL_FLOAT, np.float32), 'int': (1, gl.GL_INT, np.int32), } def create(self): self._handle = gl.glCreateProgram() self._validated = False self._linked = False # Keeping track of uniforms/attributes self._handles = {} # cache with handles to attributes/uniforms self._unset_variables = set() # Store samplers in buffers that are bount to uniforms/attributes self._samplers = {} # name -> (tex-target, tex-handle, unit) self._attributes = {} # name -> (vbo-handle, attr-handle, func, args) self._known_invalid = set() # variables that we know are invalid def delete(self): gl.glDeleteProgram(self._handle) def activate(self): """ Avoid overhead in calling glUseProgram with same arg. Warning: this will break if glUseProgram is used somewhere else. Per context we keep track of one current program. """ if self._handle != self._parser.env.get('current_program', False): self._parser.env['current_program'] = self._handle gl.glUseProgram(self._handle) def deactivate(self): """ Avoid overhead in calling glUseProgram with same arg. Warning: this will break if glUseProgram is used somewhere else. Per context we keep track of one current program. """ if self._parser.env.get('current_program', 0) != 0: self._parser.env['current_program'] = 0 gl.glUseProgram(0) def set_shaders(self, vert, frag): """ This function takes care of setting the shading code and compiling+linking it into a working program object that is ready to use. """ self._linked = False # Create temporary shader objects vert_handle = gl.glCreateShader(gl.GL_VERTEX_SHADER) frag_handle = gl.glCreateShader(gl.GL_FRAGMENT_SHADER) # For both vertex and fragment shader: set source, compile, check for code, handle, type_ in [(vert, vert_handle, 'vertex'), (frag, frag_handle, 'fragment')]: gl.glShaderSource(handle, code) gl.glCompileShader(handle) status = gl.glGetShaderParameter(handle, gl.GL_COMPILE_STATUS) if not status: errors = gl.glGetShaderInfoLog(handle) errormsg = self._get_error(code, errors, 4) raise RuntimeError("Shader compilation error in %s:\n%s" % (type_ + ' shader', errormsg)) # Attach shaders gl.glAttachShader(self._handle, vert_handle) gl.glAttachShader(self._handle, frag_handle) # Link the program and check gl.glLinkProgram(self._handle) if not gl.glGetProgramParameter(self._handle, gl.GL_LINK_STATUS): print(gl.glGetProgramInfoLog(self._handle)) raise RuntimeError('Program linking error') # Now we can remove the shaders. We no longer need them and it # frees up precious GPU memory: # http://gamedev.stackexchange.com/questions/47910 gl.glDetachShader(self._handle, vert_handle) gl.glDetachShader(self._handle, frag_handle) gl.glDeleteShader(vert_handle) gl.glDeleteShader(frag_handle) # Now we know what variables will be used by the program self._unset_variables = self._get_active_attributes_and_uniforms() self._handles = {} self._known_invalid = set() self._linked = True def _get_active_attributes_and_uniforms(self): """ Retrieve active attributes and uniforms to be able to check that all uniforms/attributes are set by the user. Other GLIR implementations may omit this. """ # This match a name of the form "name[size]" (= array) regex = re.compile("""(?P\w+)\s*(\[(?P\d+)\])\s*""") # Get how many active attributes and uniforms there are cu = gl.glGetProgramParameter(self._handle, gl.GL_ACTIVE_UNIFORMS) ca = gl.glGetProgramParameter(self.handle, gl.GL_ACTIVE_ATTRIBUTES) # Get info on each one attributes = [] uniforms = [] for container, count, func in [(attributes, ca, gl.glGetActiveAttrib), (uniforms, cu, gl.glGetActiveUniform)]: for i in range(count): name, size, gtype = func(self._handle, i) m = regex.match(name) # Check if xxx[0] instead of xx if m: name = m.group('name') for i in range(size): container.append(('%s[%d]' % (name, i), gtype)) else: container.append((name, gtype)) #return attributes, uniforms return set([v[0] for v in attributes] + [v[0] for v in uniforms]) def _parse_error(self, error): """ Parses a single GLSL error and extracts the linenr and description Other GLIR implementations may omit this. """ error = str(error) # Nvidia # 0(7): error C1008: undefined variable "MV" m = re.match(r'(\d+)\((\d+)\)\s*:\s(.*)', error) if m: return int(m.group(2)), m.group(3) # ATI / Intel # ERROR: 0:131: '{' : syntax error parse error m = re.match(r'ERROR:\s(\d+):(\d+):\s(.*)', error) if m: return int(m.group(2)), m.group(3) # Nouveau # 0:28(16): error: syntax error, unexpected ')', expecting '(' m = re.match(r'(\d+):(\d+)\((\d+)\):\s(.*)', error) if m: return int(m.group(2)), m.group(4) # Other ... return None, error def _get_error(self, code, errors, indentation=0): """Get error and show the faulty line + some context Other GLIR implementations may omit this. """ # Init results = [] lines = None if code is not None: lines = [line.strip() for line in code.split('\n')] for error in errors.split('\n'): # Strip; skip empy lines error = error.strip() if not error: continue # Separate line number from description (if we can) linenr, error = self._parse_error(error) if None in (linenr, lines): results.append('%s' % error) else: results.append('on line %i: %s' % (linenr, error)) if linenr > 0 and linenr < len(lines): results.append(' %s' % lines[linenr - 1]) # Add indentation and return results = [' ' * indentation + r for r in results] return '\n'.join(results) def set_texture(self, name, value): """ Set a texture sampler. Value is the id of the texture to link. """ if not self._linked: raise RuntimeError('Cannot set uniform when program has no code') # Get handle for the uniform, first try cache handle = self._handles.get(name, -1) if handle < 0: if name in self._known_invalid: return handle = gl.glGetUniformLocation(self._handle, name) self._unset_variables.discard(name) # Mark as set self._handles[name] = handle # Store in cache if handle < 0: self._known_invalid.add(name) logger.info('Variable %s is not an active uniform' % name) return # Program needs to be active in order to set uniforms self.activate() if True: # Sampler: the value is the id of the texture tex = self._parser.get_object(value) if tex == JUST_DELETED: return if tex is None: raise RuntimeError('Could not find texture with id %i' % value) unit = len(self._samplers) if name in self._samplers: unit = self._samplers[name][-1] # Use existing unit self._samplers[name] = tex._target, tex.handle, unit gl.glUniform1i(handle, unit) def set_uniform(self, name, type_, value): """ Set a uniform value. Value is assumed to have been checked. """ if not self._linked: raise RuntimeError('Cannot set uniform when program has no code') # Get handle for the uniform, first try cache handle = self._handles.get(name, -1) count = 1 if handle < 0: if name in self._known_invalid: return handle = gl.glGetUniformLocation(self._handle, name) self._unset_variables.discard(name) # Mark as set # if we set a uniform_array, mark all as set if not type_.startswith('mat'): count = value.nbytes // (4 * self.ATYPEINFO[type_][0]) if count > 1: for ii in range(count): if '%s[%s]' % (name, ii) in self._unset_variables: self._unset_variables.discard('%s[%s]' % (name, ii)) self._handles[name] = handle # Store in cache if handle < 0: self._known_invalid.add(name) logger.info('Variable %s is not an active uniform' % name) return # Look up function to call funcname = self.UTYPEMAP[type_] func = getattr(gl, funcname) # Program needs to be active in order to set uniforms self.activate() # Triage depending on type if type_.startswith('mat'): # Value is matrix, these gl funcs have alternative signature transpose = False # OpenGL ES 2.0 does not support transpose func(handle, 1, transpose, value) else: # Regular uniform func(handle, count, value) def set_attribute(self, name, type_, value): """ Set an attribute value. Value is assumed to have been checked. """ if not self._linked: raise RuntimeError('Cannot set attribute when program has no code') # Get handle for the attribute, first try cache handle = self._handles.get(name, -1) if handle < 0: if name in self._known_invalid: return handle = gl.glGetAttribLocation(self._handle, name) self._unset_variables.discard(name) # Mark as set self._handles[name] = handle # Store in cache if handle < 0: self._known_invalid.add(name) if value[0] != 0 and value[2] > 0: # VBO with offset return # Probably an unused element in a structured VBO logger.info('Variable %s is not an active attribute' % name) return # Program needs to be active in order to set uniforms self.activate() # Triage depending on VBO or tuple data if value[0] == 0: # Look up function call funcname = self.ATYPEMAP[type_] func = getattr(gl, funcname) # Set data self._attributes[name] = 0, handle, func, value[1:] else: # Get meta data vbo_id, stride, offset = value size, gtype, dtype = self.ATYPEINFO[type_] # Get associated VBO vbo = self._parser.get_object(vbo_id) if vbo == JUST_DELETED: return if vbo is None: raise RuntimeError('Could not find VBO with id %i' % vbo_id) # Set data func = gl.glVertexAttribPointer args = size, gtype, gl.GL_FALSE, stride, offset self._attributes[name] = vbo.handle, handle, func, args def _pre_draw(self): self.activate() # Activate textures for tex_target, tex_handle, unit in self._samplers.values(): gl.glActiveTexture(gl.GL_TEXTURE0 + unit) gl.glBindTexture(tex_target, tex_handle) # Activate attributes for vbo_handle, attr_handle, func, args in self._attributes.values(): if vbo_handle: gl.glBindBuffer(gl.GL_ARRAY_BUFFER, vbo_handle) gl.glEnableVertexAttribArray(attr_handle) func(attr_handle, *args) else: gl.glBindBuffer(gl.GL_ARRAY_BUFFER, 0) gl.glDisableVertexAttribArray(attr_handle) func(attr_handle, *args) # Validate. We need to validate after textures units get assigned if not self._validated: self._validated = True self._validate() def _validate(self): # Validate ourselves if self._unset_variables: logger.info('Program has unset variables: %r' % self._unset_variables) # Validate via OpenGL gl.glValidateProgram(self._handle) if not gl.glGetProgramParameter(self._handle, gl.GL_VALIDATE_STATUS): print(gl.glGetProgramInfoLog(self._handle)) raise RuntimeError('Program validation error') def _post_draw(self): # No need to deactivate each texture/buffer, just set to 0 gl.glBindBuffer(gl.GL_ARRAY_BUFFER, 0) gl.glBindTexture(gl.GL_TEXTURE_2D, 0) if USE_TEX_3D: gl.glBindTexture(GL_TEXTURE_3D, 0) gl.glBindTexture(GL_TEXTURE_1D, 0) #Deactivate program - should not be necessary. In single-program #apps it would not even make sense. #self.deactivate() def draw(self, mode, selection): """ Draw program in given mode, with given selection (IndexBuffer or first, count). """ if not self._linked: raise RuntimeError('Cannot draw program if code has not been set') # Init gl.check_error('Check before draw') mode = as_enum(mode) # Draw if len(selection) == 3: # Selection based on indices id_, gtype, count = selection if count: self._pre_draw() ibuf = self._parser.get_object(id_) ibuf.activate() gl.glDrawElements(mode, count, as_enum(gtype), None) ibuf.deactivate() else: # Selection based on start and count first, count = selection if count: self._pre_draw() gl.glDrawArrays(mode, first, count) # Wrap up gl.check_error('Check after draw') self._post_draw() class GlirBuffer(GlirObject): _target = None _usage = gl.GL_DYNAMIC_DRAW # STATIC_DRAW, STREAM_DRAW or DYNAMIC_DRAW def create(self): self._handle = gl.glCreateBuffer() self._buffer_size = 0 self._bufferSubDataOk = False def delete(self): gl.glDeleteBuffer(self._handle) def activate(self): gl.glBindBuffer(self._target, self._handle) def deactivate(self): gl.glBindBuffer(self._target, 0) def set_size(self, nbytes): # in bytes if nbytes != self._buffer_size: self.activate() gl.glBufferData(self._target, nbytes, self._usage) self._buffer_size = nbytes def set_data(self, offset, data): self.activate() nbytes = data.nbytes # Determine whether to check errors to try handling the ATI bug check_ati_bug = ((not self._bufferSubDataOk) and (gl.current_backend is gl.gl2) and sys.platform.startswith('win')) # flush any pending errors if check_ati_bug: gl.check_error('periodic check') try: gl.glBufferSubData(self._target, offset, data) if check_ati_bug: gl.check_error('glBufferSubData') self._bufferSubDataOk = True # glBufferSubData seems to work except Exception: # This might be due to a driver error (seen on ATI), issue #64. # We try to detect this, and if we can use glBufferData instead if offset == 0 and nbytes == self._buffer_size: gl.glBufferData(self._target, data, self._usage) logger.debug("Using glBufferData instead of " + "glBufferSubData (known ATI bug).") else: raise class GlirVertexBuffer(GlirBuffer): _target = gl.GL_ARRAY_BUFFER class GlirIndexBuffer(GlirBuffer): _target = gl.GL_ELEMENT_ARRAY_BUFFER class GlirTexture(GlirObject): _target = None _types = { np.dtype(np.int8): gl.GL_BYTE, np.dtype(np.uint8): gl.GL_UNSIGNED_BYTE, np.dtype(np.int16): gl.GL_SHORT, np.dtype(np.uint16): gl.GL_UNSIGNED_SHORT, np.dtype(np.int32): gl.GL_INT, np.dtype(np.uint32): gl.GL_UNSIGNED_INT, # np.dtype(np.float16) : gl.GL_HALF_FLOAT, np.dtype(np.float32): gl.GL_FLOAT, # np.dtype(np.float64) : gl.GL_DOUBLE } def create(self): self._handle = gl.glCreateTexture() self._shape_formats = 0 # To make setting size cheap def delete(self): gl.glDeleteTexture(self._handle) def activate(self): gl.glBindTexture(self._target, self._handle) def deactivate(self): gl.glBindTexture(self._target, 0) # Taken from pygly def _get_alignment(self, width): """Determines a textures byte alignment. If the width isn't a power of 2 we need to adjust the byte alignment of the image. The image height is unimportant www.opengl.org/wiki/Common_Mistakes#Texture_upload_and_pixel_reads """ # we know the alignment is appropriate # if we can divide the width by the # alignment cleanly # valid alignments are 1,2,4 and 8 # put 4 first, since it's the default alignments = [4, 8, 2, 1] for alignment in alignments: if width % alignment == 0: return alignment def set_wrapping(self, wrapping): self.activate() wrapping = [as_enum(w) for w in wrapping] if len(wrapping) == 3: GL_TEXTURE_WRAP_R = 32882 gl.glTexParameterf(self._target, GL_TEXTURE_WRAP_R, wrapping[0]) if len(wrapping) >= 2: gl.glTexParameterf(self._target, gl.GL_TEXTURE_WRAP_S, wrapping[-2]) gl.glTexParameterf(self._target, gl.GL_TEXTURE_WRAP_T, wrapping[-1]) def set_interpolation(self, min, mag): self.activate() min, mag = as_enum(min), as_enum(mag) gl.glTexParameterf(self._target, gl.GL_TEXTURE_MIN_FILTER, min) gl.glTexParameterf(self._target, gl.GL_TEXTURE_MAG_FILTER, mag) # these should be auto generated in _constants.py. But that doesn't seem # to be happening. TODO - figure out why the C parser in (createglapi.py) # is not extracting these constanst out. # found the constant value at: # http://docs.factorcode.org/content/word-GL_TEXTURE_1D,opengl.gl.html # http://docs.factorcode.org/content/word-GL_SAMPLER_1D%2Copengl.gl.html GL_SAMPLER_1D = gl.Enum('GL_SAMPLER_1D', 35677) GL_TEXTURE_1D = gl.Enum('GL_TEXTURE_1D', 3552) class GlirTexture1D(GlirTexture): _target = GL_TEXTURE_1D def set_size(self, shape, format, internalformat): format = as_enum(format) if internalformat is not None: internalformat = as_enum(internalformat) else: internalformat = format # Shape is width if (shape, format, internalformat) != self._shape_formats: self.activate() self._shape_formats = shape, format, internalformat glTexImage1D(self._target, 0, internalformat, format, gl.GL_BYTE, shape[:1]) def set_data(self, offset, data): self.activate() shape, format, internalformat = self._shape_formats x = offset[0] # Get gtype gtype = self._types.get(np.dtype(data.dtype), None) if gtype is None: raise ValueError("Type %r not allowed for texture" % data.dtype) # Set alignment (width is nbytes_per_pixel * npixels_per_line) alignment = self._get_alignment(data.shape[-1]) if alignment != 4: gl.glPixelStorei(gl.GL_UNPACK_ALIGNMENT, alignment) # Upload glTexSubImage1D(self._target, 0, x, format, gtype, data) # Set alignment back if alignment != 4: gl.glPixelStorei(gl.GL_UNPACK_ALIGNMENT, 4) class GlirTexture2D(GlirTexture): _target = gl.GL_TEXTURE_2D def set_size(self, shape, format, internalformat): # Shape is height, width format = as_enum(format) internalformat = format if internalformat is None \ else as_enum(internalformat) if (shape, format, internalformat) != self._shape_formats: self._shape_formats = shape, format, internalformat self.activate() gl.glTexImage2D(self._target, 0, internalformat, format, gl.GL_UNSIGNED_BYTE, shape[:2]) def set_data(self, offset, data): self.activate() shape, format, internalformat = self._shape_formats y, x = offset # Get gtype gtype = self._types.get(np.dtype(data.dtype), None) if gtype is None: raise ValueError("Type %r not allowed for texture" % data.dtype) # Set alignment (width is nbytes_per_pixel * npixels_per_line) alignment = self._get_alignment(data.shape[-2]*data.shape[-1]) if alignment != 4: gl.glPixelStorei(gl.GL_UNPACK_ALIGNMENT, alignment) # Upload gl.glTexSubImage2D(self._target, 0, x, y, format, gtype, data) # Set alignment back if alignment != 4: gl.glPixelStorei(gl.GL_UNPACK_ALIGNMENT, 4) GL_SAMPLER_3D = gl.Enum('GL_SAMPLER_3D', 35679) GL_TEXTURE_3D = gl.Enum('GL_TEXTURE_3D', 32879) USE_TEX_3D = False def _check_pyopengl_3D(): """Helper to ensure users have OpenGL for 3D texture support (for now)""" global USE_TEX_3D USE_TEX_3D = True try: import OpenGL.GL as _gl except ImportError: raise ImportError('PyOpenGL is required for 3D texture support') return _gl def glTexImage3D(target, level, internalformat, format, type, pixels): # Import from PyOpenGL _gl = _check_pyopengl_3D() border = 0 assert isinstance(pixels, (tuple, list)) # the only way we use this now depth, height, width = pixels _gl.glTexImage3D(target, level, internalformat, width, height, depth, border, format, type, None) def glTexImage1D(target, level, internalformat, format, type, pixels): # Import from PyOpenGL _gl = _check_pyopengl_3D() border = 0 assert isinstance(pixels, (tuple, list)) # the only way we use this now # pixels will be a tuple of the form (width, ) # we only need the first argument width = pixels[0] _gl.glTexImage1D(target, level, internalformat, width, border, format, type, None) def glTexSubImage1D(target, level, xoffset, format, type, pixels): # Import from PyOpenGL _gl = _check_pyopengl_3D() width = pixels.shape[:1] # width will be a tuple of the form (w, ) # we need to take the first element (integer) _gl.glTexSubImage1D(target, level, xoffset, width[0], format, type, pixels) def glTexSubImage3D(target, level, xoffset, yoffset, zoffset, format, type, pixels): # Import from PyOpenGL _gl = _check_pyopengl_3D() depth, height, width = pixels.shape[:3] _gl.glTexSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels) class GlirTexture3D(GlirTexture): _target = GL_TEXTURE_3D def set_size(self, shape, format, internalformat): format = as_enum(format) if internalformat is not None: internalformat = as_enum(internalformat) else: internalformat = format # Shape is depth, height, width if (shape, format, internalformat) != self._shape_formats: self.activate() self._shape_formats = shape, format, internalformat glTexImage3D(self._target, 0, internalformat, format, gl.GL_BYTE, shape[:3]) def set_data(self, offset, data): self.activate() shape, format, internalformat = self._shape_formats z, y, x = offset # Get gtype gtype = self._types.get(np.dtype(data.dtype), None) if gtype is None: raise ValueError("Type not allowed for texture") # Set alignment (width is nbytes_per_pixel * npixels_per_line) alignment = self._get_alignment(data.shape[-3] * data.shape[-2] * data.shape[-1]) if alignment != 4: gl.glPixelStorei(gl.GL_UNPACK_ALIGNMENT, alignment) # Upload glTexSubImage3D(self._target, 0, x, y, z, format, gtype, data) # Set alignment back if alignment != 4: gl.glPixelStorei(gl.GL_UNPACK_ALIGNMENT, 4) class GlirRenderBuffer(GlirObject): def create(self): self._handle = gl.glCreateRenderbuffer() self._shape_format = 0 # To make setting size cheap def delete(self): gl.glDeleteRenderbuffer(self._handle) def activate(self): gl.glBindRenderbuffer(gl.GL_RENDERBUFFER, self._handle) def deactivate(self): gl.glBindRenderbuffer(gl.GL_RENDERBUFFER, 0) def set_size(self, shape, format): if isinstance(format, string_types): format = GlirFrameBuffer._formats[format][1] if (shape, format) != self._shape_format: self._shape_format = shape, format self.activate() gl.glRenderbufferStorage(gl.GL_RENDERBUFFER, format, shape[1], shape[0]) class GlirFrameBuffer(GlirObject): # todo: on ES 2.0 -> gl.gl_RGBA4 _formats = {'color': (gl.GL_COLOR_ATTACHMENT0, gl.GL_RGBA), 'depth': (gl.GL_DEPTH_ATTACHMENT, gl.GL_DEPTH_COMPONENT16), 'stencil': (gl.GL_STENCIL_ATTACHMENT, gl.GL_STENCIL_INDEX8)} def create(self): #self._parser._fb_stack = [0] # To keep track of active FB self._handle = gl.glCreateFramebuffer() self._validated = False def delete(self): gl.glDeleteFramebuffer(self._handle) def set_framebuffer(self, yes): if yes: if not self._validated: self._validated = True self._validate() self.activate() else: self.deactivate() def activate(self): stack = self._parser.env.setdefault('fb_stack', [0]) if stack[-1] != self._handle: stack.append(self._handle) gl.glBindFramebuffer(gl.GL_FRAMEBUFFER, self._handle) def deactivate(self): stack = self._parser.env.setdefault('fb_stack', [0]) while self._handle in stack: stack.remove(self._handle) gl.glBindFramebuffer(gl.GL_FRAMEBUFFER, stack[-1]) def attach(self, attachment, buffer_id): attachment = GlirFrameBuffer._formats[attachment][0] self.activate() if buffer_id == 0: gl.glFramebufferRenderbuffer(gl.GL_FRAMEBUFFER, attachment, gl.GL_RENDERBUFFER, 0) else: buffer = self._parser.get_object(buffer_id) if buffer == JUST_DELETED: return if buffer is None: raise ValueError("Unknown buffer with id %i for attachement" % buffer_id) elif isinstance(buffer, GlirRenderBuffer): buffer.activate() gl.glFramebufferRenderbuffer(gl.GL_FRAMEBUFFER, attachment, gl.GL_RENDERBUFFER, buffer.handle) buffer.deactivate() elif isinstance(buffer, GlirTexture2D): buffer.activate() # INFO: 0 is for mipmap level 0 (default) of the texture gl.glFramebufferTexture2D(gl.GL_FRAMEBUFFER, attachment, gl.GL_TEXTURE_2D, buffer.handle, 0) buffer.deactivate() else: raise ValueError("Invalid attachment: %s" % type(buffer)) self._validated = False self.deactivate() def _validate(self): res = gl.glCheckFramebufferStatus(gl.GL_FRAMEBUFFER) if res == gl.GL_FRAMEBUFFER_COMPLETE: pass elif res == 0: raise RuntimeError('Target not equal to GL_FRAMEBUFFER') elif res == gl.GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: raise RuntimeError( 'FrameBuffer attachments are incomplete.') elif res == gl.GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: raise RuntimeError( 'No valid attachments in the FrameBuffer.') elif res == gl.GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS: raise RuntimeError( 'attachments do not have the same width and height.') #elif res == gl.GL_FRAMEBUFFER_INCOMPLETE_FORMATS: # not in es 2.0 # raise RuntimeError('Internal format of attachment ' # 'is not renderable.') elif res == gl.GL_FRAMEBUFFER_UNSUPPORTED: raise RuntimeError('Combination of internal formats used ' 'by attachments is not supported.') else: raise RuntimeError('Unknown framebuffer error: %r.' % res) ���������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/gloo/globject.py������������������������������������������������������������������0000664�0001750�0001750�00000007400�12527672621�020653� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2015, Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Base gloo object On queues --------- The queue on the GLObject can be associated with other queues. These can be queues of other gloo objects, or of the canvas.context. A program associates the textures/buffers when they are set via __setitem__. A FrameBuffer does so when assigning buffers. A program associates itself with the canvas.context in draw(). A FrameBuffer does the same in activate(). Example: prog1, prog2 = Program(), Program() tex1, tex2 = Texture(), Texture() prog1.glir.associate(tex1.glir) prog1.glir.associate(tex2.glir) canvas1.context.glir.associate(prog1.glir) canvas1.context.glir.associate(prog2.glir) canvas2.context.glir.associate(prog2.glir) Now, when canvas1 flushes its queue, it takes all the pending commands from prog1 and prog2, and subsequently from tex1 and tex2. When canvas2 is flushed, only commands from prog2 get taken. A similar situation holds for a texture that is associated with a program and a frame buffer. """ from .glir import GlirQueue class GLObject(object): """ Generic GL object that represents an object on the GPU. When a GLObject is instantiated, it is associated with the currently active Canvas, or with the next Canvas to be created if there is no current Canvas """ # Type of GLIR object, reset in subclasses _GLIR_TYPE = 'DummyGlirType' # Internal id counter to keep track of GPU objects _idcount = 0 def __init__(self): """ Initialize the object in the default state """ # Give this object an id GLObject._idcount += 1 self._id = GLObject._idcount # Create the GLIR queue in which we queue our commands. # See docs above for details. self._glir = GlirQueue() # Give glir command to create GL representation of this object self._glir.command('CREATE', self._id, self._GLIR_TYPE) def __del__(self): # You never know when this is goint to happen. The window might # already be closed and no OpenGL context might be available. # However, since we are using GLIR queue, this does not matter! # If the command gets transported to the canvas, that is great, # if not, this probably means that the canvas no longer exists. self.delete() def delete(self): """ Delete the object from GPU memory. Note that the GPU object will also be deleted when this gloo object is about to be deleted. However, sometimes you want to explicitly delete the GPU object explicitly. """ # We only allow the object from being deleted once, otherwise # we might be deleting another GPU object that got our gl-id # after our GPU object was deleted. Also note that e.g. # DataBufferView does not have the _glir attribute. if hasattr(self, '_glir'): # Send our final command into the queue self._glir.command('DELETE', self._id) # Tell master glir queue that this queue is no longer being used self._glir._deletable = True # Detach the queue del self._glir @property def id(self): """ The id of this GL object used to reference the GL object in GLIR. id's are unique within a process. """ return self._id @property def glir(self): """ The glir queue for this object. """ return self._glir ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/gloo/tests/�����������������������������������������������������������������������0000775�0001750�0001750�00000000000�12527674621�017653� 5����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/gloo/tests/test_context.py��������������������������������������������������������0000664�0001750�0001750�00000005743�12510536123�022743� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- import gc from vispy.testing import (assert_in, run_tests_if_main, assert_raises, assert_equal, assert_not_equal) from vispy import gloo from vispy.gloo import (GLContext, get_default_config) class DummyCanvas(object): @property def glir(self): return self def command(self, *args): pass class DummyCanvasBackend(object): def __init__(self): self.set_current = False self._vispy_canvas = DummyCanvas() def _vispy_set_current(self): self.set_current = True def test_context_config(): """ Test GLContext handling of config dict """ default_config = get_default_config() # Pass default config unchanged c = GLContext(default_config) assert_equal(c.config, default_config) # Must be deep copy c.config['double_buffer'] = False assert_not_equal(c.config, default_config) # Passing nothing should yield default config c = GLContext() assert_equal(c.config, default_config) # Must be deep copy c.config['double_buffer'] = False assert_not_equal(c.config, default_config) # This should work c = GLContext({'red_size': 4, 'double_buffer': False}) assert_equal(c.config.keys(), default_config.keys()) # Passing crap should raise assert_raises(KeyError, GLContext, {'foo': 3}) assert_raises(TypeError, GLContext, {'double_buffer': 'not_bool'}) def test_context_taking(): """ Test GLContext ownership and taking """ def get_canvas(c): return c.shared.ref cb = DummyCanvasBackend() c = GLContext() # Context is not taken and cannot get backend_canvas assert c.shared.name is None assert_raises(RuntimeError, get_canvas, c) assert_in('None backend', repr(c.shared)) # Take it c.shared.add_ref('test-foo', cb) assert c.shared.ref is cb assert_in('test-foo backend', repr(c.shared)) # Now we can take it again c.shared.add_ref('test-foo', cb) assert len(c.shared._refs) == 2 #assert_raises(RuntimeError, c.take, 'test', cb) # Canvas backend can delete (we use a weak ref) cb = DummyCanvasBackend() # overwrite old object gc.collect() # No more refs assert_raises(RuntimeError, get_canvas, c) def test_gloo_without_app(): """ Test gloo without vispy.app (with FakeCanvas) """ # Create dummy parser class DummyParser(gloo.glir.BaseGlirParser): def __init__(self): self.commands = [] def parse(self, commands): self.commands.extend(commands) p = DummyParser() # Create fake canvas and attach our parser c = gloo.context.FakeCanvas() c.context.shared.parser = p # Do some commands gloo.clear() c.flush() gloo.clear() c.flush() assert len(p.commands) in (2, 3) # there may be a CURRENT command assert p.commands[-1][1] == 'glClear' run_tests_if_main() �����������������������������vispy-0.4.0/vispy/gloo/tests/test_buffer.py���������������������������������������������������������0000664�0001750�0001750�00000046604�12510536123�022531� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2014, Nicolas P. Rougier. All rights reserved. # Distributed under the terms of the new BSD License. # ----------------------------------------------------------------------------- import unittest import numpy as np from vispy.testing import run_tests_if_main from vispy.gloo.buffer import (Buffer, DataBuffer, DataBufferView, VertexBuffer, IndexBuffer) # ----------------------------------------------------------------------------- class BufferTest(unittest.TestCase): # Default init # ------------ def test_init_default(self): """ Test buffer init""" # No data B = Buffer() assert B.nbytes == 0 glir_cmd = B._glir.clear()[-1] assert glir_cmd[0] == 'CREATE' # With data data = np.zeros(100) B = Buffer(data=data) assert B.nbytes == data.nbytes glir_cmd = B._glir.clear()[-1] assert glir_cmd[0] == 'DATA' # With nbytes B = Buffer(nbytes=100) assert B.nbytes == 100 glir_cmd = B._glir.clear()[-1] assert glir_cmd[0] == 'SIZE' # Wrong data self.assertRaises(ValueError, Buffer, data, 4) self.assertRaises(ValueError, Buffer, data, data.nbytes) # Check setting the whole buffer clear pending operations # ------------------------------------------------------- def test_set_whole_data(self): data = np.zeros(100) B = Buffer(data=data) B._glir.clear() B.set_data(data=data) glir_cmds = B._glir.clear() assert len(glir_cmds) == 2 assert glir_cmds[0][0] == 'SIZE' assert glir_cmds[1][0] == 'DATA' # And sub data B.set_subdata(data[:50], 20) glir_cmds = B._glir.clear() assert len(glir_cmds) == 1 assert glir_cmds[0][0] == 'DATA' assert glir_cmds[0][2] == 20 # offset # And sub data B.set_subdata(data) glir_cmds = B._glir.clear() assert glir_cmds[-1][0] == 'DATA' # Wrong ways to set subdata self.assertRaises(ValueError, B.set_subdata, data[:50], -1) # neg self.assertRaises(ValueError, B.set_subdata, data, 10) # no fit # Check stored data is data # ------------------------- def test_data_storage(self): data = np.zeros(100) B = Buffer(data=data) B.set_data(data=data[:50], copy=False) glir_cmd = B._glir.clear()[-1] assert glir_cmd[-1].base is data # Check setting oversized data # ---------------------------- def test_oversized_data(self): data = np.zeros(10) B = Buffer(data=data) # with self.assertRaises(ValueError): # B.set_data(np.ones(20)) self.assertRaises(ValueError, B.set_subdata, np.ones(20), offset=0) # Check negative offset # --------------------- def test_negative_offset(self): data = np.zeros(10) B = Buffer(data=data) # with self.assertRaises(ValueError): # B.set_data(np.ones(1), offset=-1) self.assertRaises(ValueError, B.set_subdata, np.ones(1), offset=-1) # Check offlimit offset # --------------------- def test_offlimit_offset(self): data = np.zeros(10) B = Buffer(data=data) # with self.assertRaises(ValueError): # B.set_data(np.ones(1), offset=10 * data.dtype.itemsize) self.assertRaises(ValueError, B.set_subdata, np.ones(1), offset=10 * data.dtype.itemsize) # Buffer size # ----------- def test_buffer_size(self): data = np.zeros(10) B = Buffer(data=data) assert B.nbytes == data.nbytes # Resize # ------ def test_buffer_resize(self): data = np.zeros(10) B = Buffer(data=data) data = np.zeros(20) B.set_data(data) assert B.nbytes == data.nbytes # ----------------------------------------------------------------------------- class DataBufferTest(unittest.TestCase): # Default init # ------------ def test_default_init(self): # Check default storage and copy flags data = np.ones(100) B = DataBuffer(data) assert B.nbytes == data.nbytes assert B.offset == 0 assert B.size == 100 assert B.itemsize == data.itemsize assert B.stride == data.itemsize assert B.dtype == data.dtype # Given data must be actual numeric data self.assertRaises(TypeError, DataBuffer, 'this is not nice data') # Default init with structured data # --------------------------------- def test_structured_init(self): # Check structured type dtype = np.dtype([('position', np.float32, 3), ('texcoord', np.float32, 2), ('color', np.float32, 4)]) data = np.zeros(10, dtype=dtype) B = DataBuffer(data) assert B.nbytes == data.nbytes assert B.offset == 0 assert B.size == 10 assert B.itemsize == data.itemsize assert B.stride == data.itemsize assert B.dtype == data.dtype # No CPU storage # -------------- def test_no_storage_copy(self): data = np.ones(100, np.float32) B = DataBuffer(data) assert B.stride == 4 # Wrong storage # ------------- def test_non_contiguous_storage(self): # Ask to have CPU storage and to use data as storage # Not possible since data[::2] is not contiguous data = np.ones(100, np.float32) data_given = data[::2] B = DataBuffer(data_given) assert B.stride == 4*2 # Get buffer field # ---------------- def test_getitem_field(self): dtype = np.dtype([('position', np.float32, 3), ('texcoord', np.float32, 2), ('color', np.float32, 4)]) data = np.zeros(10, dtype=dtype) B = DataBuffer(data) Z = B["position"] assert Z.nbytes == 10 * 3 * np.dtype(np.float32).itemsize assert Z.offset == 0 assert Z.size == 10 assert Z.itemsize == 3 * np.dtype(np.float32).itemsize assert Z.stride == (3 + 2 + 4) * np.dtype(np.float32).itemsize assert Z.dtype == (np.float32, 3) Z = B["texcoord"] assert Z.nbytes == 10 * 2 * np.dtype(np.float32).itemsize assert Z.offset == 3 * np.dtype(np.float32).itemsize assert Z.size == 10 assert Z.itemsize == 2 * np.dtype(np.float32).itemsize assert Z.stride == (3 + 2 + 4) * np.dtype(np.float32).itemsize assert Z.dtype == (np.float32, 2) Z = B["color"] assert Z.nbytes == 10 * 4 * np.dtype(np.float32).itemsize assert Z.offset == (2 + 3) * np.dtype(np.float32).itemsize assert Z.size == 10 assert Z.itemsize == 4 * np.dtype(np.float32).itemsize assert Z.stride == (3 + 2 + 4) * np.dtype(np.float32).itemsize assert Z.dtype == (np.float32, 4) # Get view via index # ------------------ def test_getitem_index(self): dtype = np.dtype([('position', np.float32, 3), ('texcoord', np.float32, 2), ('color', np.float32, 4)]) data = np.zeros(10, dtype=dtype) B = DataBuffer(data) Z = B[0:1] assert Z.base == B assert Z.id == B.id assert Z.nbytes == 1 * (3 + 2 + 4) * np.dtype(np.float32).itemsize assert Z.offset == 0 assert Z.size == 1 assert Z.itemsize == (3 + 2 + 4) * np.dtype(np.float32).itemsize assert Z.stride == (3 + 2 + 4) * np.dtype(np.float32).itemsize assert Z.dtype == B.dtype assert 'DataBufferView' in repr(Z) # There's a few things we cannot do with a view self.assertRaises(RuntimeError, Z.set_data, data) self.assertRaises(RuntimeError, Z.set_subdata, data) self.assertRaises(RuntimeError, Z.resize_bytes, 20) self.assertRaises(RuntimeError, Z.__getitem__, 3) self.assertRaises(RuntimeError, Z.__setitem__, 3, data) # View get invalidated when base is resized # ----------------------------------------- def test_invalid_view_after_resize(self): dtype = np.dtype([('position', np.float32, 3), ('texcoord', np.float32, 2), ('color', np.float32, 4)]) data = np.zeros(10, dtype=dtype) B = DataBuffer(data) Y = B['position'] Z = B[5:] B.resize_bytes(5) assert Y._valid is False assert Z._valid is False # View get invalidated after setting oversized data # ------------------------------------------------- def test_invalid_view_after_set_data(self): dtype = np.dtype([('position', np.float32, 3), ('texcoord', np.float32, 2), ('color', np.float32, 4)]) data = np.zeros(10, dtype=dtype) B = DataBuffer(data) Z = B[5:] B.set_data(np.zeros(15, dtype=dtype)) assert Z._valid is False # Set data on base buffer : ok # ---------------------------- def test_set_data_base(self): dtype = np.dtype([('position', np.float32, 3), ('texcoord', np.float32, 2), ('color', np.float32, 4)]) data = np.zeros(10, dtype=dtype) B = DataBuffer(data) B.set_data(data) last_cmd = B._glir.clear()[-1] assert last_cmd[0] == 'DATA' # Extra kwargs are caught self.assertRaises(TypeError, B.set_data, data, foo=4) # Check set_data using offset in data buffer # ------------------------------------------ def test_set_data_offset(self): data = np.zeros(100, np.float32) subdata = data[:10] B = DataBuffer(data) B.set_subdata(subdata, offset=10) last_cmd = B._glir.clear()[-1] offset = last_cmd[2] assert offset == 10*4 def test_getitem(self): dtype = np.dtype([('position', np.float32, 3), ('texcoord', np.float32, 2), ('color', np.float32, 4)]) data = np.zeros(10, dtype=dtype) B = DataBuffer(data) assert B[1].dtype == dtype assert B[1].size == 1 assert B[-1].dtype == dtype assert B[-1].size == 1 self.assertRaises(IndexError, B.__getitem__, +999) self.assertRaises(IndexError, B.__getitem__, -999) def test_setitem(self): dtype = np.dtype([('position', np.float32, 3), ('texcoord', np.float32, 2), ('color', np.float32, 4)]) data = np.zeros(10, dtype=dtype) B = DataBuffer(data) B[1] = data[0] B[-1] = data[0] B[:5] = data[:5] B[5:0] = data[:5] # Weird, but we apparently support this B[1] = b'' # Gets conveted into array of dtype. Lists do not work self.assertRaises(IndexError, B.__setitem__, +999, data[0]) self.assertRaises(IndexError, B.__setitem__, -999, data[0]) self.assertRaises(TypeError, B.__setitem__, [], data[0]) # Setitem + broadcast # ------------------------------------------------------ def test_setitem_broadcast(self): dtype = np.dtype([('position', np.float32, 3), ('texcoord', np.float32, 2), ('color', np.float32, 4)]) data = np.zeros(10, dtype=dtype) B = DataBuffer(data) self.assertRaises(ValueError, B.__setitem__, 'position', (1, 2, 3)) # Set every 2 item # ------------------------------------------------------ def test_setitem_strided(self): dtype = np.dtype([('position', np.float32, 3), ('texcoord', np.float32, 2), ('color', np.float32, 4)]) data1 = np.zeros(10, dtype=dtype) data2 = np.ones(10, dtype=dtype) B = DataBuffer(data1) s = slice(None, None, 2) self.assertRaises(ValueError, B.__setitem__, s, data2[::2]) # Set half the array # ------------------------------------------------------ def test_setitem_half(self): dtype = np.dtype([('position', np.float32, 3), ('texcoord', np.float32, 2), ('color', np.float32, 4)]) data1 = np.zeros(10, dtype=dtype) data2 = np.ones(10, dtype=dtype) B = DataBuffer(data1) B._glir.clear() B[:5] = data2[:5] glir_cmds = B._glir.clear() assert len(glir_cmds) == 1 set_data = glir_cmds[0][-1] assert np.allclose(set_data['position'], data2['position'][:5]) assert np.allclose(set_data['texcoord'][:5], data2['texcoord'][:5]) assert np.allclose(set_data['color'][:5], data2['color'][:5]) # Set field without storage: error # -------------------------------- def test_setitem_field_no_storage(self): dtype = np.dtype([('position', np.float32, 3), ('texcoord', np.float32, 2), ('color', np.float32, 4)]) data = np.zeros(10, dtype=dtype) B = DataBuffer(data) self.assertRaises(ValueError, B.__setitem__, 'position', (1, 2, 3)) # Set every 2 item without storage: error # ---------------------------------------- def test_every_two_item_no_storage(self): dtype = np.dtype([('position', np.float32, 3), ('texcoord', np.float32, 2), ('color', np.float32, 4)]) data = np.zeros(10, dtype=dtype) B = DataBuffer(data) # with self.assertRaises(ValueError): # B[::2] = data[::2] s = slice(None, None, 2) self.assertRaises(ValueError, B.__setitem__, s, data[::2]) # Resize # ------ def test_resize(self): data = np.zeros(10) B = DataBuffer(data=data) data = np.zeros(20) B.set_data(data) assert B.nbytes == data.nbytes # Resize now allowed using ellipsis # ----------------------------- def test_no_resize_ellipsis(self): data = np.zeros(10) B = DataBuffer(data=data) data = np.zeros(30) self.assertRaises(ValueError, B.__setitem__, Ellipsis, data) # Broadcast when using ellipses def test_broadcast_ellipsis(self): data = np.zeros(10) B = DataBuffer(data=data) data = np.zeros(5) B[Ellipsis] = data glir_cmd = B._glir.clear()[-1] assert glir_cmd[-1].shape == (10,) class DataBufferViewTest(unittest.TestCase): def test_init_view(self): data = np.zeros(10) B = DataBuffer(data=data) V = DataBufferView(B, 1) assert V.size == 1 V = DataBufferView(B, slice(0, 5)) assert V.size == 5 V = DataBufferView(B, slice(5, 0)) assert V.size == 5 V = DataBufferView(B, Ellipsis) assert V.size == 10 self.assertRaises(TypeError, DataBufferView, B, []) self.assertRaises(ValueError, DataBufferView, B, slice(0, 10, 2)) # ----------------------------------------------------------------------------- class VertexBufferTest(unittest.TestCase): # VertexBuffer allowed base types # ------------------------------- def test_init_allowed_dtype(self): for dtype in (np.uint8, np.int8, np.uint16, np.int16, np.float32): V = VertexBuffer(np.zeros((10, 3), dtype=dtype)) names = V.dtype.names assert V.dtype[names[0]].base == dtype assert V.dtype[names[0]].shape == (3,) for dtype in (np.float64, np.int64): self.assertRaises(TypeError, VertexBuffer, np.zeros((10, 3), dtype=dtype)) # Tuple/list is also allowed V = VertexBuffer([1, 2, 3]) assert V.size == 3 assert V.itemsize == 4 # V = VertexBuffer([[1, 2], [3, 4], [5, 6]]) assert V.size == 3 assert V.itemsize == 2 * 4 # Convert data = np.zeros((10,), 'uint8') B = VertexBuffer(data) assert B.dtype[0].base == np.uint8 assert B.dtype[0].itemsize == 1 # data = np.zeros((10, 2), 'uint8') B = VertexBuffer(data) assert B.dtype[0].base == np.uint8 assert B.dtype[0].itemsize == 2 B.set_data(data, convert=True) assert B.dtype[0].base == np.float32 assert B.dtype[0].itemsize == 8 B = VertexBuffer(data[::2].copy()) # This is converted to 1D B = VertexBuffer([[1, 2, 3, 4, 5], [1, 2, 3, 4, 5]]) assert B.size == 10 # Not allowed self.assertRaises(TypeError, VertexBuffer, dtype=np.float64) #self.assertRaises(TypeError, VertexBuffer, [[1,2,3,4,5],[1,2,3,4,5]]) # VertexBuffer not allowed base types # ----------------------------------- def test_init_not_allowed_dtype(self): for dtype in (np.uint32, np.int32, np.float64): # with self.assertRaises(TypeError): # V = VertexBuffer(dtype=dtype) self.assertRaises(TypeError, VertexBuffer, dtype=dtype) def test_glsl_type(self): data = np.zeros((10,), np.float32) B = VertexBuffer(data) C = B[1:] assert B.glsl_type == ('attribute', 'float') assert C.glsl_type == ('attribute', 'float') data = np.zeros((10, 2), np.float32) B = VertexBuffer(data) C = B[1:] assert B.glsl_type == ('attribute', 'vec2') assert C.glsl_type == ('attribute', 'vec2') data = np.zeros((10, 4), np.float32) B = VertexBuffer(data) C = B[1:] assert B.glsl_type == ('attribute', 'vec4') assert C.glsl_type == ('attribute', 'vec4') # ----------------------------------------------------------------------------- class IndexBufferTest(unittest.TestCase): # IndexBuffer allowed base types # ------------------------------ def test_init_allowed_dtype(self): # allowed dtypes for dtype in (np.uint8, np.uint16, np.uint32): b = IndexBuffer(np.zeros(10, dtype=dtype)) b.dtype == dtype # no data => no dtype V = IndexBuffer() V.dtype is None # Not allowed dtypes for dtype in (np.int8, np.int16, np.int32, np.float16, np.float32, np.float64): # with self.assertRaises(TypeError): # V = IndexBuffer(dtype=dtype) data = np.zeros(10, dtype=dtype) self.assertRaises(TypeError, IndexBuffer, data) # Prepare some data dtype = np.dtype([('position', np.float32, 3), ('texcoord', np.float32, 2), ]) sdata = np.zeros(10, dtype=dtype) # Normal data is data = np.zeros([1, 2, 3], np.uint8) B = IndexBuffer(data) assert B.dtype == np.uint8 # We can also convert B.set_data(data, convert=True) assert B.dtype == np.uint32 # Structured data not allowed self.assertRaises(TypeError, IndexBuffer, dtype=dtype) self.assertRaises(TypeError, B.set_data, sdata) run_tests_if_main() ����������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/gloo/tests/test_glir.py�����������������������������������������������������������0000664�0001750�0001750�00000004707�12510536123�022213� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- import json import tempfile from vispy import config from vispy.app import Canvas from vispy.gloo import glir from vispy.testing import requires_application, run_tests_if_main def test_queue(): q = glir.GlirQueue() parser = glir.GlirParser() # Test adding commands and clear N = 5 for i in range(N): q.command('FOO', 'BAR', i) cmds = q.clear() for i in range(N): assert cmds[i] == ('FOO', 'BAR', i) # Test filter 1 cmds1 = [('DATA', 1), ('SIZE', 1), ('FOO', 1), ('SIZE', 1), ('FOO', 1), ('DATA', 1), ('DATA', 1)] cmds2 = [c[0] for c in q._filter(cmds1, parser)] assert cmds2 == ['FOO', 'SIZE', 'FOO', 'DATA', 'DATA'] # Test filter 2 cmds1 = [('DATA', 1), ('SIZE', 1), ('FOO', 1), ('SIZE', 2), ('SIZE', 2), ('DATA', 2), ('SIZE', 1), ('FOO', 1), ('DATA', 1), ('DATA', 1)] cmds2 = q._filter(cmds1, parser) assert cmds2 == [('FOO', 1), ('SIZE', 2), ('DATA', 2), ('SIZE', 1), ('FOO', 1), ('DATA', 1), ('DATA', 1)] # Define shader shader1 = """ precision highp float;uniform mediump vec4 u_foo;uniform vec4 u_bar; """.strip().replace(';', ';\n') # Convert for desktop shader2 = q._convert_shaders('desktop', ['', shader1])[1] assert 'highp' not in shader2 assert 'mediump' not in shader2 assert 'precision' not in shader2 # Convert for es2 shader3 = q._convert_shaders('es2', ['', shader2])[1] assert 'precision highp float;' in shader3 @requires_application() def test_log_parser(): glir_file = tempfile.TemporaryFile(mode='r+') config.update(glir_file=glir_file) with Canvas() as c: c.context.set_clear_color('white') c.context.clear() glir_file.seek(0) lines = glir_file.read().split(',\n') assert lines[0][0] == '[' lines[0] = lines[0][1:] assert lines[-1][-1] == ']' lines[-1] = lines[-1][:-1] i = 0 assert lines[i] == json.dumps(['CURRENT', 0]) i += 1 # The 'CURRENT' command may have been called multiple times while lines[i] == lines[i - 1]: i += 1 assert lines[i] == json.dumps(['FUNC', 'clearColor', 1.0, 1.0, 1.0, 1.0]) i += 1 assert lines[i] == json.dumps(['FUNC', 'clear', 17664]) i += 1 assert lines[i] == json.dumps(['FUNC', 'finish']) i += 1 config.update(glir_file='') # The rest is basically tested via our examples run_tests_if_main() ���������������������������������������������������������vispy-0.4.0/vispy/gloo/tests/test_framebuffer.py����������������������������������������������������0000664�0001750�0001750�00000011614�12456026505�023544� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- from vispy.testing import run_tests_if_main, assert_raises from vispy import gloo from vispy.gloo import FrameBuffer, RenderBuffer def test_renderbuffer(): # Set with no args assert_raises(ValueError, RenderBuffer) # Set shape only R = RenderBuffer((10, 20)) assert R.shape == (10, 20) assert R.format is None # Set both shape and format R = RenderBuffer((10, 20), 'color') assert R.shape == (10, 20) assert R.format is 'color' # glir_cmds = R._glir.clear() assert len(glir_cmds) == 2 assert glir_cmds[0][0] == 'CREATE' assert glir_cmds[1][0] == 'SIZE' # Orther formats assert RenderBuffer((10, 20), 'depth').format == 'depth' assert RenderBuffer((10, 20), 'stencil').format == 'stencil' # Test reset size and format R.resize((9, 9), 'depth') assert R.shape == (9, 9) assert R.format == 'depth' R.resize((8, 8), 'stencil') assert R.shape == (8, 8) assert R.format == 'stencil' # Wrong formats assert_raises(ValueError, R.resize, (9, 9), 'no_format') assert_raises(ValueError, R.resize, (9, 9), []) # Resizable R = RenderBuffer((10, 20), 'color', False) assert_raises(RuntimeError, R.resize, (9, 9), 'color') # Attaching sets the format F = FrameBuffer() # R = RenderBuffer((9, 9)) F.color_buffer = R assert F.color_buffer is R assert R.format == 'color' # F.depth_buffer = RenderBuffer((9, 9)) assert F.depth_buffer.format == 'depth' # F.stencil_buffer = RenderBuffer((9, 9)) assert F.stencil_buffer.format == 'stencil' def test_framebuffer(): # Test init with no args F = FrameBuffer() glir_cmds = F._glir.clear() assert len(glir_cmds) == 1 glir_cmds[0][0] == 'CREATE' # Activate / deactivate F.activate() glir_cmd = F._glir.clear()[-1] assert glir_cmd[0] == 'FRAMEBUFFER' assert glir_cmd[2] is True # F.deactivate() glir_cmd = F._glir.clear()[-1] assert glir_cmd[0] == 'FRAMEBUFFER' assert glir_cmd[2] is False # with F: pass glir_cmds = F._glir.clear() assert len(glir_cmds) == 2 assert glir_cmds[0][0] == 'FRAMEBUFFER' assert glir_cmds[1][0] == 'FRAMEBUFFER' assert glir_cmds[0][2] is True and glir_cmds[1][2] is False # Init with args R = RenderBuffer((3, 3)) F = FrameBuffer(R) assert F.color_buffer is R # R2 = RenderBuffer((3, 3)) F.color_buffer = R2 assert F.color_buffer is R2 # Wrong buffers F = FrameBuffer() assert_raises(TypeError, FrameBuffer.color_buffer.fset, F, 'FOO') assert_raises(TypeError, FrameBuffer.color_buffer.fset, F, []) assert_raises(TypeError, FrameBuffer.depth_buffer.fset, F, 'FOO') assert_raises(TypeError, FrameBuffer.stencil_buffer.fset, F, 'FOO') color_buffer = RenderBuffer((9, 9), 'color') assert_raises(ValueError, FrameBuffer.depth_buffer.fset, F, color_buffer) # But None is allowed! F.color_buffer = None # Shape R1 = RenderBuffer((3, 3)) R2 = RenderBuffer((3, 3)) R3 = RenderBuffer((3, 3)) F = FrameBuffer(R1, R2, R3) assert F.shape == R1.shape assert R1.format == 'color' assert R2.format == 'depth' assert R3.format == 'stencil' # Resize F.resize((10, 10)) assert F.shape == (10, 10) assert F.shape == R1.shape assert F.shape == R2.shape assert F.shape == R3.shape assert R1.format == 'color' assert R2.format == 'depth' assert R3.format == 'stencil' # Shape from any buffer F.color_buffer = None assert F.shape == (10, 10) F.depth_buffer = None assert F.shape == (10, 10) F.stencil_buffer = None assert_raises(RuntimeError, FrameBuffer.shape.fget, F) # Also with Texture luminance T = gloo.Texture2D((20, 30)) R = RenderBuffer(T.shape) assert T.format == 'luminance' F = FrameBuffer(T, R) assert F.shape == T.shape[:2] assert F.shape == R.shape assert T.format == 'luminance' assert R.format == 'depth' # Resize F.resize((10, 10)) assert F.shape == (10, 10) assert T.shape == (10, 10, 1) assert F.shape == R.shape assert T.format == 'luminance' assert R.format == 'depth' # Also with Texture RGB T = gloo.Texture2D((20, 30, 3)) R = RenderBuffer(T.shape) assert T.format == 'rgb' F = FrameBuffer(T, R) assert F.shape == T.shape[:2] assert F.shape == R.shape assert T.format == 'rgb' assert R.format == 'depth' # Resize F.resize((10, 10)) assert F.shape == (10, 10) assert T.shape == (10, 10, 3) assert F.shape == R.shape assert T.format == 'rgb' assert R.format == 'depth' # Wrong shape in resize assert_raises(ValueError, F. resize, (9, 9, 1)) assert_raises(ValueError, F. resize, (9,)) assert_raises(ValueError, F. resize, 'FOO') run_tests_if_main() ��������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/gloo/tests/__init__.py������������������������������������������������������������0000664�0001750�0001750�00000000000�12375431476�021752� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/gloo/tests/test_program.py��������������������������������������������������������0000664�0001750�0001750�00000025674�12510536123�022733� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2014, Nicolas P. Rougier. All rights reserved. # Distributed under the terms of the new BSD License. # ----------------------------------------------------------------------------- import unittest import numpy as np from vispy import gloo, app from vispy.gloo.program import Program from vispy.testing import run_tests_if_main, assert_in, requires_application from vispy.gloo.context import set_current_canvas, forget_canvas class DummyParser(gloo.glir.BaseGlirParser): def convert_shaders(self): return 'desktop' def parse(self, commands): pass class DummyCanvas: def __init__(self): self.context = gloo.context.GLContext() self.context.shared.parser = DummyParser() self.context.glir.flush = lambda *args: None # No flush class ProgramTest(unittest.TestCase): def test_init(self): # Test ok init, no shaders program = Program() assert program._user_variables == {} assert program._code_variables == {} assert program._pending_variables == {} assert program.shaders == ('', '') # Test ok init, with shader program = Program('A', 'B') assert program.shaders == ('A', 'B') # False inits self.assertRaises(ValueError, Program, 'A', None) self.assertRaises(ValueError, Program, None, 'B') self.assertRaises(ValueError, Program, 3, 'B') self.assertRaises(ValueError, Program, 3, None) self.assertRaises(ValueError, Program, 'A', 3) self.assertRaises(ValueError, Program, None, 3) self.assertRaises(ValueError, Program, "", "") self.assertRaises(ValueError, Program, "foo", "") self.assertRaises(ValueError, Program, "", "foo") def test_setting_shaders(self): program = Program("A", "B") assert program.shaders[0] == "A" assert program.shaders[1] == "B" program.set_shaders('C', 'D') assert program.shaders[0] == "C" assert program.shaders[1] == "D" @requires_application() def test_error(self): vert = ''' void main() { vec2 xy; error on this line vec2 ab; } ''' frag = 'void main() { glFragColor = vec4(1, 1, 1, 1); }' with app.Canvas() as c: program = Program(vert, frag) try: program._glir.flush(c.context.shared.parser) except Exception as err: assert_in('error on this line', str(err)) else: raise Exception("Compile program should have failed.") def test_uniform(self): # Text array unoforms program = Program("uniform float A[10];", "foo") assert ('uniform_array', 'float', 'A') in program.variables assert len(program.variables) == 11 # array plus elements self.assertRaises(ValueError, program.__setitem__, 'A', np.ones((9, 1))) program['A'] = np.ones((10, 1)) program['A[0]'] = 0 assert 'A[0]' in program._user_variables assert 'A[0]' not in program._pending_variables # Init program program = Program("uniform float A;", "uniform float A; uniform vec4 B;") assert ('uniform', 'float', 'A') in program.variables assert ('uniform', 'vec4', 'B') in program.variables assert len(program.variables) == 2 # Set existing uniforms program['A'] = 3.0 assert isinstance(program['A'], np.ndarray) assert program['A'] == 3.0 assert 'A' in program._user_variables # program['B'] = 1.0, 2.0, 3.0, 4.0 assert isinstance(program['B'], np.ndarray) assert all(program['B'] == np.array((1.0, 2.0, 3.0, 4.0), np.float32)) assert 'B' in program._user_variables # Set non-existent uniforms program['C'] = 1.0, 2.0 assert program['C'] == (1.0, 2.0) assert 'C' not in program._user_variables assert 'C' in program._pending_variables # Set samplers program.set_shaders("""uniform sampler1D T1; uniform sampler2D T2; uniform sampler3D T3;""", "f") program['T1'] = np.zeros((10, ), np.float32) program['T2'] = np.zeros((10, 10), np.float32) program['T3'] = np.zeros((10, 10, 10), np.float32) assert isinstance(program['T1'], gloo.Texture1D) assert isinstance(program['T2'], gloo.Texture2D) assert isinstance(program['T3'], gloo.Texture3D) # Set samplers with textures tex = gloo.Texture2D((10, 10)) program['T2'] = tex assert program['T2'] is tex program['T2'] = np.zeros((10, 10), np.float32) # Update texture assert program['T2'] is tex # C should be taken up when code comes along that mentions it program.set_shaders("uniform float A; uniform vec2 C;", "uniform float A; uniform vec4 B;") assert isinstance(program['C'], np.ndarray) assert all(program['C'] == np.array((1.0, 2.0), np.float32)) assert 'C' in program._user_variables assert 'C' not in program._pending_variables # Set wrong values self.assertRaises(ValueError, program.__setitem__, 'A', (1.0, 2.0)) self.assertRaises(ValueError, program.__setitem__, 'B', (1.0, 2.0)) self.assertRaises(ValueError, program.__setitem__, 'C', 1.0) # Set wrong values beforehand program['D'] = 1.0, 2.0 self.assertRaises(ValueError, program.set_shaders, '', 'uniform vec3 D;') def test_attributes(self): program = Program("attribute float A; attribute vec4 B;", "foo") assert ('attribute', 'float', 'A') in program.variables assert ('attribute', 'vec4', 'B') in program.variables assert len(program.variables) == 2 from vispy.gloo import VertexBuffer vbo = VertexBuffer() # Set existing uniforms program['A'] = vbo assert program['A'] == vbo assert 'A' in program._user_variables assert program._user_variables['A'] is vbo # Set data - update existing vbp program['A'] = np.zeros((10,), np.float32) assert program._user_variables['A'] is vbo # Set data - create new vbo program['B'] = np.zeros((10, 4), np.float32) assert isinstance(program._user_variables['B'], VertexBuffer) # Set non-existent uniforms vbo = VertexBuffer() # new one since old one is now wrong size program['C'] = vbo assert program['C'] == vbo assert 'C' not in program._user_variables assert 'C' in program._pending_variables # C should be taken up when code comes along that mentions it program.set_shaders("attribute float A; attribute vec2 C;", "foo") assert program['C'] == vbo assert 'C' in program._user_variables assert 'C' not in program._pending_variables # Set wrong values self.assertRaises(ValueError, program.__setitem__, 'A', 'asddas') # Set wrong values beforehand program['D'] = "" self.assertRaises(ValueError, program.set_shaders, 'attribute vec3 D;', '') # Set to one value per vertex program.set_shaders("attribute float A; attribute vec2 C;", "foo") program['A'] = 1.0 assert program['A'] == 1.0 program['C'] = 1.0, 2.0 assert all(program['C'] == np.array((1.0, 2.0), np.float32)) # self.assertRaises(ValueError, program.__setitem__, 'A', (1.0, 2.0)) self.assertRaises(ValueError, program.__setitem__, 'C', 1.0) self.assertRaises(ValueError, program.bind, 'notavertexbuffer') program = Program("attribute vec2 C;", "foo") # first code path: no exsting variable self.assertRaises(ValueError, program.__setitem__, 'C', np.ones((2, 10), np.float32)) # second code path: variable exists (VertexBuffer.set_data) program['C'] = np.ones((10, 2), np.float32) self.assertRaises(ValueError, program.__setitem__, 'C', np.ones((2, 10), np.float32)) def test_vbo(self): # Test with count program = Program('attribute float a; attribute vec2 b;', 'foo', 10) assert program._count == 10 assert ('attribute', 'float', 'a') in program.variables assert ('attribute', 'vec2', 'b') in program.variables # Set program['a'] = np.ones((10,), np.float32) assert np.all(program._buffer['a'] == 1) def test_vayings(self): # Varyings and constants are detected program = Program("varying float A; const vec4 B;", "foo") assert ('varying', 'float', 'A') in program.variables assert ('const', 'vec4', 'B') in program.variables # But cannot be set self.assertRaises(KeyError, program.__setitem__, 'A', 3.0) self.assertRaises(KeyError, program.__setitem__, 'B', (1.0, 2.0, 3.0)) # And anything else also fails self.assertRaises(KeyError, program.__getitem__, 'fooo') def test_draw(self): # Init program = Program("attribute float A;", "uniform float foo") program['A'] = np.zeros((10,), np.float32) dummy_canvas = DummyCanvas() glir = dummy_canvas.context.glir set_current_canvas(dummy_canvas) try: # Draw arrays program.draw('triangles') glir_cmd = glir.clear()[-1] assert glir_cmd[0] == 'DRAW' assert len(glir_cmd[-1]) == 2 # Draw elements indices = gloo.IndexBuffer(np.zeros(10, dtype=np.uint8)) program.draw('triangles', indices) glir_cmd = glir.clear()[-1] assert glir_cmd[0] == 'DRAW' assert len(glir_cmd[-1]) == 3 # Invalid mode self.assertRaises(ValueError, program.draw, 'nogeometricshape') # Invalid index self.assertRaises(TypeError, program.draw, 'triangles', 'notindex') # No atributes program = Program("attribute float A;", "uniform float foo") self.assertRaises(RuntimeError, program.draw, 'triangles') # Atributes with different sizes program = Program("attribute float A; attribute float B;", "foo") program['A'] = np.zeros((10,), np.float32) program['B'] = np.zeros((11,), np.float32) self.assertRaises(RuntimeError, program.draw, 'triangles') finally: forget_canvas(dummy_canvas) run_tests_if_main() ��������������������������������������������������������������������vispy-0.4.0/vispy/gloo/tests/test_texture.py��������������������������������������������������������0000664�0001750�0001750�00000057260�12527672621�022774� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2014, Nicolas P. Rougier. All rights reserved. # Distributed under the terms of the new BSD License. # ----------------------------------------------------------------------------- import unittest import numpy as np from vispy.gloo import Texture1D, Texture2D, Texture3D, TextureAtlas from vispy.testing import requires_pyopengl, run_tests_if_main, assert_raises # here we test some things that will be true of all Texture types: Texture = Texture2D # ----------------------------------------------------------------- Texture --- class TextureTest(unittest.TestCase): # No data, no dtype : forbidden # --------------------------------- def test_init_none(self): self.assertRaises(ValueError, Texture) # Data only # --------------------------------- def test_init_data(self): data = np.zeros((10, 10, 3), dtype=np.uint8) T = Texture(data=data, interpolation='linear', wrapping='repeat') assert T._shape == (10, 10, 3) assert T._interpolation == ('linear', 'linear') assert T._wrapping == ('repeat', 'repeat') # Setting data and shape # --------------------------------- def test_init_dtype_shape(self): T = Texture((10, 10)) assert T._shape == (10, 10, 1) self.assertRaises(ValueError, Texture, shape=(10, 10), data=np.zeros((10, 10), np.float32)) # Set data with store # --------------------------------- def test_setitem_all(self): data = np.zeros((10, 10), dtype=np.uint8) T = Texture(data=data) T[...] = np.ones((10, 10, 1)) glir_cmd = T._glir.clear()[-1] assert glir_cmd[0] == 'DATA' assert np.allclose(glir_cmd[3], np.ones((10, 10, 1))) # Set data without store # --------------------------------- def test_setitem_all_no_store(self): data = np.zeros((10, 10), dtype=np.uint8) T = Texture(data=data) T[...] = np.ones((10, 10), np.uint8) assert np.allclose(data, np.zeros((10, 10))) # Set a single data # --------------------------------- def test_setitem_single(self): data = np.zeros((10, 10), dtype=np.uint8) T = Texture(data=data) T[0, 0, 0] = 1 glir_cmd = T._glir.clear()[-1] assert glir_cmd[0] == 'DATA' assert np.allclose(glir_cmd[3], np.array([1])) # We apparently support this T[8:3, 3] = 1 # Set some data # --------------------------------- def test_setitem_partial(self): data = np.zeros((10, 10), dtype=np.uint8) T = Texture(data=data) T[5:, 5:] = 1 glir_cmd = T._glir.clear()[-1] assert glir_cmd[0] == 'DATA' assert np.allclose(glir_cmd[3], np.ones((5, 5))) # Set non contiguous data # --------------------------------- def test_setitem_wrong(self): data = np.zeros((10, 10), dtype=np.uint8) T = Texture(data=data) # with self.assertRaises(ValueError): # T[::2, ::2] = 1 s = slice(None, None, 2) self.assertRaises(IndexError, T.__setitem__, (s, s), 1) self.assertRaises(IndexError, T.__setitem__, (-100, 3), 1) self.assertRaises(TypeError, T.__setitem__, ('foo', 'bar'), 1) # Set properties def test_set_texture_properties(self): T = Texture((10, 10)) # Interpolation T.interpolation = 'nearest' assert T.interpolation == 'nearest' T.interpolation = 'linear' assert T.interpolation == 'linear' T.interpolation = ['linear'] * 2 assert T.interpolation == 'linear' T.interpolation = ['linear', 'nearest'] assert T.interpolation == ('linear', 'nearest') # Wrong interpolation iset = Texture.interpolation.fset self.assertRaises(ValueError, iset, T, ['linear'] * 3) self.assertRaises(ValueError, iset, T, True) self.assertRaises(ValueError, iset, T, []) self.assertRaises(ValueError, iset, T, 'linearios') # Wrapping T.wrapping = 'clamp_to_edge' assert T.wrapping == 'clamp_to_edge' T.wrapping = 'repeat' assert T.wrapping == 'repeat' T.wrapping = 'mirrored_repeat' assert T.wrapping == 'mirrored_repeat' T.wrapping = 'repeat', 'repeat' assert T.wrapping == 'repeat' T.wrapping = 'repeat', 'clamp_to_edge' assert T.wrapping == ('repeat', 'clamp_to_edge') # Wrong wrapping wset = Texture.wrapping.fset self.assertRaises(ValueError, wset, T, ['repeat'] * 3) self.assertRaises(ValueError, wset, T, True) self.assertRaises(ValueError, wset, T, []) self.assertRaises(ValueError, wset, T, 'repeatos') # --------------------------------------------------------------- Texture2D --- class Texture2DTest(unittest.TestCase): # Note: put many tests related to (re)sizing here, because Texture # is not really aware of shape. # Shape extension # --------------------------------- def test_init(self): data = np.zeros((10, 10), dtype=np.uint8) T = Texture2D(data=data) assert 'Texture2D' in repr(T) assert T._shape == (10, 10, 1) assert T.glsl_type == ('uniform', 'sampler2D') # Width & height # --------------------------------- def test_width_height(self): data = np.zeros((10, 20), dtype=np.uint8) T = Texture2D(data=data) assert T.width == 20 assert T.height == 10 # Resize # --------------------------------- def test_resize(self): data = np.zeros((10, 10), dtype=np.uint8) T = Texture2D(data=data) T.resize((5, 5)) assert T.shape == (5, 5, 1) glir_cmd = T._glir.clear()[-1] assert glir_cmd[0] == 'SIZE' # Wong arg self.assertRaises(ValueError, T.resize, (5, 5), 4) # Resize with bad shape # --------------------------------- def test_resize_bad_shape(self): data = np.zeros((10, 10), dtype=np.uint8) T = Texture2D(data=data) # with self.assertRaises(ValueError): # T.resize((5, 5, 5)) self.assertRaises(ValueError, T.resize, (5,)) self.assertRaises(ValueError, T.resize, (5, 5, 5)) self.assertRaises(ValueError, T.resize, (5, 5, 5, 1)) # Resize not resizable # --------------------------------- def test_resize_unresizable(self): data = np.zeros((10, 10), dtype=np.uint8) T = Texture2D(data=data, resizable=False) # with self.assertRaises(RuntimeError): # T.resize((5, 5)) self.assertRaises(RuntimeError, T.resize, (5, 5)) # Set oversized data (-> resize) # --------------------------------- def test_set_oversized_data(self): data = np.zeros((10, 10), dtype=np.uint8) T = Texture2D(data=data) T.set_data(np.ones((20, 20), np.uint8)) assert T.shape == (20, 20, 1) glir_cmds = T._glir.clear() assert glir_cmds[-2][0] == 'SIZE' assert glir_cmds[-1][0] == 'DATA' # Set undersized data # --------------------------------- def test_set_undersized_data(self): data = np.zeros((10, 10), dtype=np.uint8) T = Texture2D(data=data) T.set_data(np.ones((5, 5), np.uint8)) assert T.shape == (5, 5, 1) glir_cmds = T._glir.clear() assert glir_cmds[-2][0] == 'SIZE' assert glir_cmds[-1][0] == 'DATA' # Set misplaced data # --------------------------------- def test_set_misplaced_data(self): data = np.zeros((10, 10), dtype=np.uint8) T = Texture2D(data=data) # with self.assertRaises(ValueError): # T.set_data(np.ones((5, 5)), offset=(8, 8)) self.assertRaises(ValueError, T.set_data, np.ones((5, 5)), offset=(8, 8)) # Set misshaped data # --------------------------------- def test_set_misshaped_data_2D(self): data = np.zeros((10, 10), dtype=np.uint8) T = Texture2D(data=data) # with self.assertRaises(ValueError): # T.set_data(np.ones((10, 10))) self.assertRaises(ValueError, T.set_data, np.ones((10,))) self.assertRaises(ValueError, T.set_data, np.ones((5, 5, 5, 1)),) # Set whole data (clear pending data) # --------------------------------- def test_set_whole_data(self): data = np.zeros((10, 10), dtype=np.uint8) T = Texture2D(data=data) T.set_data(np.ones((10, 10), np.uint8)) assert T.shape == (10, 10, 1) # Test set data with different shape # --------------------------------- def test_reset_data_shape(self): shape1 = 10, 10 shape3 = 10, 10, 3 # Init data (explicit shape) data = np.zeros((10, 10, 1), dtype=np.uint8) T = Texture2D(data=data) assert T.shape == (10, 10, 1) assert T.format == 'luminance' # Set data to rgb T.set_data(np.zeros(shape3, np.uint8)) assert T.shape == (10, 10, 3) assert T.format == 'rgb' # Set data to grayscale T.set_data(np.zeros(shape1, np.uint8)) assert T.shape == (10, 10, 1) assert T.format == 'luminance' # Set size to rgb T.resize(shape3) assert T.shape == (10, 10, 3) assert T._format == 'rgb' # Set size to grayscale T.resize(shape1) assert T.shape == (10, 10, 1) assert T._format == 'luminance' # Keep using old format T.resize(shape1, 'alpha') T.resize(shape1) assert T._format == 'alpha' # Use luminance as default T.resize(shape3) T.resize(shape1) assert T._format == 'luminance' # Too large self.assertRaises(ValueError, T.resize, (5, 5, 5, 1)) # Cannot determine format self.assertRaises(ValueError, T.resize, (5, 5, 5)) # Invalid format self.assertRaises(ValueError, T.resize, shape3, 'foo') self.assertRaises(ValueError, T.resize, shape3, 'alpha') #self.assertRaises(ValueError, T.resize, shape3, 4) # Test set data with different shape and type # ------------------------------------------- def test_reset_data_type(self): data = np.zeros((10, 10), dtype=np.uint8) T = Texture2D(data=data) data = np.zeros((10, 11), dtype=np.float32) T.set_data(data) data = np.zeros((12, 10), dtype=np.int32) T.set_data(data) self.assertRaises(ValueError, T.set_data, np.zeros([10, 10, 10, 1])) # --------------------------------------------------------------- Texture1D --- @requires_pyopengl() def test_texture_1D(): # Note: put many tests related to (re)sizing here, because Texture # is not really aware of shape. # Shape extension # --------------------------------- data = np.zeros((10, ), dtype=np.uint8) T = Texture1D(data=data) assert T._shape == (10, 1) assert 'Texture1D' in repr(T) assert T.glsl_type == ('uniform', 'sampler1D') # Width # --------------------------------- data = np.zeros((10, ), dtype=np.uint8) T = Texture1D(data=data) assert T.width == 10 # Resize # --------------------------------- data = np.zeros((10, ), dtype=np.uint8) T = Texture1D(data=data) T.resize((5, )) assert T.shape == (5, 1) glir_cmd = T._glir.clear()[-1] assert glir_cmd[0] == 'SIZE' assert glir_cmd[2] == (5, 1) # Resize with bad shape # --------------------------------- data = np.zeros((10, ), dtype=np.uint8) T = Texture1D(data=data) # with self.assertRaises(ValueError): # T.resize((5, 5, 5, 5)) assert_raises(ValueError, T.resize, (5, 5, 5, 5)) # Resize not resizable # --------------------------------- data = np.zeros((10, ), dtype=np.uint8) T = Texture1D(data=data, resizable=False) # with self.assertRaises(RuntimeError): # T.resize((5, 5, 5)) assert_raises(RuntimeError, T.resize, (5, )) # Set oversized data (-> resize) # --------------------------------- data = np.zeros((10, ), dtype=np.uint8) T = Texture1D(data=data) T.set_data(np.ones((20, ), np.uint8)) assert T.shape == (20, 1) # Set undersized data # --------------------------------- data = np.zeros((10, ), dtype=np.uint8) T = Texture1D(data=data) T.set_data(np.ones((5, ), np.uint8)) assert T.shape == (5, 1) # Set misplaced data # --------------------------------- data = np.zeros((10, ), dtype=np.uint8) T = Texture1D(data=data) # with self.assertRaises(ValueError): # T.set_data(np.ones((5, 5, 5)), offset=(8, 8, 8)) assert_raises(ValueError, T.set_data, np.ones((5, )), offset=(8, )) # Set misshaped data # --------------------------------- data = np.zeros((10, ), dtype=np.uint8) T = Texture1D(data=data) # with self.assertRaises(ValueError): # T.set_data(np.ones((10, 10, 10))) assert_raises(ValueError, T.set_data, np.ones((10, 10))) # Set whole data (clear pending data) # --------------------------------- data = np.zeros((10, ), dtype=np.uint8) T = Texture1D(data=data) T.set_data(np.ones((10, ), np.uint8)) assert T.shape == (10, 1) glir_cmd = T._glir.clear()[-1] assert glir_cmd[0] == 'DATA' # Test set data with different shape # --------------------------------- shape1 = (10, ) shape3 = (10, 3) # Init data (explicit shape) data = np.zeros((10, 1), dtype=np.uint8) T = Texture1D(data=data) assert T.shape == (10, 1) assert T._format == 'luminance' # Set data to rgb T.set_data(np.zeros(shape3, np.uint8)) assert T.shape == (10, 3) assert T._format == 'rgb' # Set data to grayscale T.set_data(np.zeros(shape1, np.uint8)) assert T.shape == (10, 1) assert T._format == 'luminance' # Set size to rgb T.resize(shape3) assert T.shape == (10, 3) assert T._format == 'rgb' # Set size to grayscale T.resize(shape1) assert T.shape == (10, 1) assert T._format == 'luminance' # Test set data with different shape and type # ------------------------------------------- data = np.zeros((10, ), dtype=np.uint8) T = Texture1D(data=data) data = np.zeros((10, ), dtype=np.float32) T.set_data(data) data = np.zeros((12, ), dtype=np.int32) T.set_data(data) # --------------------------------------------------------------- Texture3D --- @requires_pyopengl() def test_texture_3D(): # Note: put many tests related to (re)sizing here, because Texture # is not really aware of shape. # Shape extension # --------------------------------- data = np.zeros((10, 10, 10), dtype=np.uint8) T = Texture3D(data=data) assert T._shape == (10, 10, 10, 1) assert 'Texture3D' in repr(T) assert T.glsl_type == ('uniform', 'sampler3D') # Width & height # --------------------------------- data = np.zeros((10, 20, 30), dtype=np.uint8) T = Texture3D(data=data) assert T.width == 30 assert T.height == 20 assert T.depth == 10 # Resize # --------------------------------- data = np.zeros((10, 10, 10), dtype=np.uint8) T = Texture3D(data=data) T.resize((5, 5, 5)) assert T.shape == (5, 5, 5, 1) glir_cmd = T._glir.clear()[-1] assert glir_cmd[0] == 'SIZE' assert glir_cmd[2] == (5, 5, 5, 1) # Resize with bad shape # --------------------------------- data = np.zeros((10, 10, 10), dtype=np.uint8) T = Texture3D(data=data) # with self.assertRaises(ValueError): # T.resize((5, 5, 5, 5)) assert_raises(ValueError, T.resize, (5, 5, 5, 5)) # Resize not resizable # --------------------------------- data = np.zeros((10, 10, 10), dtype=np.uint8) T = Texture3D(data=data, resizable=False) # with self.assertRaises(RuntimeError): # T.resize((5, 5, 5)) assert_raises(RuntimeError, T.resize, (5, 5, 5)) # Set oversized data (-> resize) # --------------------------------- data = np.zeros((10, 10, 10), dtype=np.uint8) T = Texture3D(data=data) T.set_data(np.ones((20, 20, 20), np.uint8)) assert T.shape == (20, 20, 20, 1) # Set undersized data # --------------------------------- data = np.zeros((10, 10, 10), dtype=np.uint8) T = Texture3D(data=data) T.set_data(np.ones((5, 5, 5), np.uint8)) assert T.shape == (5, 5, 5, 1) # Set misplaced data # --------------------------------- data = np.zeros((10, 10, 10), dtype=np.uint8) T = Texture3D(data=data) # with self.assertRaises(ValueError): # T.set_data(np.ones((5, 5, 5)), offset=(8, 8, 8)) assert_raises(ValueError, T.set_data, np.ones((5, 5, 5)), offset=(8, 8, 8)) # Set misshaped data # --------------------------------- data = np.zeros((10, 10, 10), dtype=np.uint8) T = Texture3D(data=data) # with self.assertRaises(ValueError): # T.set_data(np.ones((10, 10, 10))) assert_raises(ValueError, T.set_data, np.ones((10,))) # Set whole data (clear pending data) # --------------------------------- data = np.zeros((10, 10, 10), dtype=np.uint8) T = Texture3D(data=data) T.set_data(np.ones((10, 10, 10), np.uint8)) assert T.shape == (10, 10, 10, 1) glir_cmd = T._glir.clear()[-1] assert glir_cmd[0] == 'DATA' # Test set data with different shape # --------------------------------- shape1 = 10, 10, 10 shape3 = 10, 10, 10, 3 # Init data (explicit shape) data = np.zeros((10, 10, 10, 1), dtype=np.uint8) T = Texture3D(data=data) assert T.shape == (10, 10, 10, 1) assert T._format == 'luminance' # Set data to rgb T.set_data(np.zeros(shape3, np.uint8)) assert T.shape == (10, 10, 10, 3) assert T._format == 'rgb' # Set data to grayscale T.set_data(np.zeros(shape1, np.uint8)) assert T.shape == (10, 10, 10, 1) assert T._format == 'luminance' # Set size to rgb T.resize(shape3) assert T.shape == (10, 10, 10, 3) assert T._format == 'rgb' # Set size to grayscale T.resize(shape1) assert T.shape == (10, 10, 10, 1) assert T._format == 'luminance' # Test set data with different shape and type # ------------------------------------------- data = np.zeros((10, 10, 10), dtype=np.uint8) T = Texture3D(data=data) data = np.zeros((10, 11, 11), dtype=np.float32) T.set_data(data) data = np.zeros((12, 12, 10), dtype=np.int32) T.set_data(data) class TextureAtlasTest(unittest.TestCase): def test_init_atas(self): T = TextureAtlas((100, 100)) assert T.shape == (128, 128, 3) # rounds to powers of 2 for i in [10, 20, 30, 40, 50, 60]: reg = T.get_free_region(10, 10) assert len(reg) == 4 reg = T.get_free_region(129, 129) assert reg is None # --------------------------------------------------------- Texture formats --- def _test_texture_formats(Texture, baseshape, formats): # valid channel count and format combinations for channels in range(1, 5): for format in [f for n, f in formats if n == channels]: shape = baseshape + (channels,) T = Texture(shape=shape, format=format) assert 'Texture' in repr(T) assert T._shape == shape data = np.zeros(shape, dtype=np.uint8) T = Texture(data=data, format=format) assert 'Texture' in repr(T) assert T._shape == shape # invalid channel count and format combinations for channels in range(1, 5): for format in [f for n, f in formats + [(5, 'junk')] if n != channels]: shape = baseshape + (channels,) assert_raises(ValueError, Texture, shape=shape, format=format) data = np.zeros(shape, dtype=np.uint8) assert_raises(ValueError, Texture, data=data, format=format) # --------------------------------------------------------- Texture formats --- def _test_texture_basic_formats(Texture, baseshape): _test_texture_formats( Texture, baseshape, [ (1, 'alpha'), (1, 'luminance'), (2, 'luminance_alpha'), (3, 'rgb'), (4, 'rgba') ] ) # ------------------------------------------------------- Texture1D formats --- def test_texture_1D_formats(): _test_texture_basic_formats(Texture1D, (10, )) # ------------------------------------------------------- Texture2D formats --- def test_texture_2D_formats(): _test_texture_basic_formats(Texture2D, (10, 10)) # ------------------------------------------------------- Texture3D formats --- def test_texture_3D_formats(): _test_texture_basic_formats(Texture3D, (10, 10, 10)) # -------------------------------------------------- Texture OpenGL formats --- def _test_texture_opengl_formats(Texture, baseshape): _test_texture_formats( Texture, baseshape, [ (1, 'red'), (2, 'rg'), (3, 'rgb'), (4, 'rgba') ] ) # ------------------------------------------------ Texture1D OpenGL formats --- @requires_pyopengl() def test_texture_1D_opengl_formats(): _test_texture_opengl_formats(Texture1D, (10, )) # ------------------------------------------------ Texture2D OpenGL formats --- @requires_pyopengl() def test_texture_2D_opengl_formats(): _test_texture_opengl_formats(Texture2D, (10, 10)) # ------------------------------------------------ Texture3D OpenGL formats --- @requires_pyopengl() def test_texture_3D_opengl_formats(): _test_texture_opengl_formats(Texture3D, (10, 10, 10)) # ------------------------------------------ Texture OpenGL internalformats --- def _test_texture_internalformats(Texture, baseshape): # Test format for concrete Texture class and baseshape + (numchannels,) # Test internalformats valid with desktop OpenGL formats = [ (1, 'red', ['red', 'r8', 'r16', 'r16f', 'r32f']), (2, 'rg', ['rg', 'rg8', 'rg16', 'rg16f', 'rg32f']), (3, 'rgb', ['rgb', 'rgb8', 'rgb16', 'rgb16f', 'rgb32f']), (4, 'rgba', ['rgba', 'rgba8', 'rgba16', 'rgba16f', 'rgba32f']) ] for channels in range(1, 5): for fmt, ifmts in [(f, iL) for n, f, iL in formats if n == channels]: shape = baseshape + (channels,) data = np.zeros(shape, dtype=np.uint8) for ifmt in ifmts: T = Texture(shape=shape, format=fmt, internalformat=ifmt) assert 'Texture' in repr(T) assert T._shape == shape T = Texture(data=data, format=fmt, internalformat=ifmt) assert 'Texture' in repr(T) assert T._shape == shape for channels in range(1, 5): for fmt, ifmts in [(f, iL) for n, f, iL in formats if n != channels]: shape = baseshape + (channels,) data = np.zeros(shape, dtype=np.uint8) for ifmt in ifmts: assert_raises(ValueError, Texture, shape=shape, format=fmt, internalformat=ifmt) assert_raises(ValueError, Texture, data=data, format=fmt, internalformat=ifmt) # ---------------------------------------- Texture2D OpenGL internalformats --- @requires_pyopengl() def test_texture_2D_internalformats(): _test_texture_internalformats(Texture2D, (10, 10)) # ---------------------------------------- Texture3D OpenGL internalformats --- @requires_pyopengl() def test_texture_3D_internalformats(): _test_texture_internalformats(Texture3D, (10, 10, 10)) run_tests_if_main() ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/gloo/tests/test_wrappers.py�������������������������������������������������������0000664�0001750�0001750�00000020111�12510536123�023104� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2014, Nicolas P. Rougier. All rights reserved. # Distributed under the terms of the new BSD License. # ----------------------------------------------------------------------------- import numpy as np from numpy.testing import assert_array_equal, assert_allclose from vispy import gloo from vispy.gloo import gl from vispy.app import Canvas from vispy.testing import (requires_application, run_tests_if_main, assert_true, assert_equal, assert_raises) from vispy.gloo import read_pixels from vispy.gloo.glir import GlirQueue from vispy.gloo import wrappers # Dummy queue dummy_glir = GlirQueue() dummy_glir.context = dummy_glir dummy_glir.glir = dummy_glir def install_dummy_glir(): wrappers.get_current_canvas = lambda x=None: dummy_glir dummy_glir.clear() return dummy_glir def reset_glir(): wrappers.get_current_canvas = gloo.get_current_canvas def teardown_module(): reset_glir() def test_wrappers_basic_glir(): """ Test that basic gloo wrapper functions emit right GLIR command """ glir = install_dummy_glir() funcs = [('viewport', 0, 0, 10, 10), ('depth_range', 0, 1), ('front_face', 'ccw'), ('cull_face', 'back'), ('line_width', 1), ('polygon_offset', 0, 0), ('clear_color', ), ('clear_depth', ), ('clear_stencil', ), ('blend_func', ), ('blend_color', 'red'), ('blend_equation', 'X'), ('scissor', 0, 0, 10, 10), ('stencil_func', ), ('stencil_mask', ), ('stencil_op', ), ('depth_func', ), ('depth_mask', 'X'), ('color_mask', False, False, False, False), ('sample_coverage', ), ('hint', 'foo', 'bar'), # not finish and flush, because that would flush the glir queue ] for func in funcs: name, args = func[0], func[1:] f = getattr(gloo, 'set_' + name) f(*args) cmds = glir.clear() assert len(cmds) == len(funcs) for i, func in enumerate(funcs): cmd = cmds[i] nameparts = [a.capitalize() for a in func[0].split('_')] name = 'gl' + ''.join(nameparts) assert cmd[0] == 'FUNC' if cmd[1].endswith('Separate'): assert cmd[1][:-8] == name else: assert cmd[1] == name reset_glir() def test_wrappers_glir(): """ Test that special wrapper functions do what they must do """ glir = install_dummy_glir() # Test clear() function gloo.clear() cmds = glir.clear() assert len(cmds) == 1 assert cmds[0][0] == 'FUNC' assert cmds[0][1] == 'glClear' # gloo.clear(True, False, False) cmds = glir.clear() assert len(cmds) == 1 assert cmds[0][0] == 'FUNC' assert cmds[0][1] == 'glClear' assert cmds[0][2] == gl.GL_COLOR_BUFFER_BIT # gloo.clear('red') cmds = glir.clear() assert len(cmds) == 2 assert cmds[0][0] == 'FUNC' assert cmds[0][1] == 'glClearColor' assert cmds[1][0] == 'FUNC' assert cmds[1][1] == 'glClear' # gloo.clear('red', 4, 3) cmds = glir.clear() assert len(cmds) == 4 assert cmds[0][1] == 'glClearColor' assert cmds[1][1] == 'glClearDepth' assert cmds[2][1] == 'glClearStencil' assert cmds[3][1] == 'glClear' # Test set_state() function gloo.set_state(foo=True, bar=False) cmds = set(glir.clear()) assert len(cmds) == 2 assert ('FUNC', 'glEnable', 'foo') in cmds assert ('FUNC', 'glDisable', 'bar') in cmds # gloo.set_state(viewport=(0, 0, 10, 10), clear_color='red') cmds = sorted(glir.clear()) assert len(cmds) == 2 assert cmds[0][1] == 'glClearColor' assert cmds[1][1] == 'glViewport' # presets = gloo.get_state_presets() a_preset = list(presets.keys())[0] gloo.set_state(a_preset) cmds = sorted(glir.clear()) assert len(cmds) == len(presets[a_preset]) reset_glir() def assert_cmd_raises(E, fun, *args, **kwargs): gloo.flush() # no error here fun(*args, **kwargs) assert_raises(E, gloo.flush) @requires_application() def test_wrappers(): """Test gloo wrappers""" with Canvas(): gl.use_gl('gl2 debug') gloo.clear('#112233') # make it so that there's something non-zero # check presets assert_raises(ValueError, gloo.set_state, preset='foo') for state in gloo.get_state_presets().keys(): gloo.set_state(state) assert_raises(ValueError, gloo.set_blend_color, (0., 0.)) # bad color assert_raises(TypeError, gloo.set_hint, 1, 2) # need strs # this doesn't exist in ES 2.0 namespace assert_cmd_raises(ValueError, gloo.set_hint, 'fog_hint', 'nicest') # test bad enum assert_raises(RuntimeError, gloo.set_line_width, -1) # check read_pixels x = gloo.read_pixels() assert_true(isinstance(x, np.ndarray)) assert_true(isinstance(gloo.read_pixels((0, 0, 1, 1)), np.ndarray)) assert_raises(ValueError, gloo.read_pixels, (0, 0, 1)) # bad port y = gloo.read_pixels(alpha=False, out_type=np.ubyte) assert_equal(y.shape, x.shape[:2] + (3,)) assert_array_equal(x[..., :3], y) y = gloo.read_pixels(out_type='float') assert_allclose(x/255., y) # now let's (indirectly) check our set_* functions viewport = (0, 0, 1, 1) blend_color = (0., 0., 0.) _funs = dict(viewport=viewport, # checked hint=('generate_mipmap_hint', 'nicest'), depth_range=(1., 2.), front_face='cw', # checked cull_face='front', line_width=1., polygon_offset=(1., 1.), blend_func=('zero', 'one'), blend_color=blend_color, blend_equation='func_add', scissor=(0, 0, 1, 1), stencil_func=('never', 1, 2, 'back'), stencil_mask=4, stencil_op=('zero', 'zero', 'zero', 'back'), depth_func='greater', depth_mask=True, color_mask=(True, True, True, True), sample_coverage=(0.5, True)) gloo.set_state(**_funs) gloo.clear((1., 1., 1., 1.), 0.5, 1) gloo.flush() gloo.finish() # check some results assert_array_equal(gl.glGetParameter(gl.GL_VIEWPORT), viewport) assert_equal(gl.glGetParameter(gl.GL_FRONT_FACE), gl.GL_CW) assert_equal(gl.glGetParameter(gl.GL_BLEND_COLOR), blend_color + (1,)) @requires_application() def test_read_pixels(): """Test read_pixels to ensure that the image is not flipped""" # Create vertices vPosition = np.array([[-1, 1], [0, 1], # For drawing a square to top left [-1, 0], [0, 0]], np.float32) VERT_SHADER = """ // simple vertex shader attribute vec2 a_position; void main (void) { gl_Position = vec4(a_position, 0., 1.0); } """ FRAG_SHADER = """ // simple fragment shader void main() { gl_FragColor = vec4(1,1,1,1); } """ with Canvas() as c: gloo.set_viewport(0, 0, *c.size) c._program = gloo.Program(VERT_SHADER, FRAG_SHADER) c._program['a_position'] = gloo.VertexBuffer(vPosition) gloo.clear(color='black') c._program.draw('triangle_strip') # Check if the return of read_pixels is the same as our drawing img = read_pixels(alpha=False) assert_equal(img.shape[:2], c.size[::-1]) top_left = sum(img[0, 0]) assert_true(top_left > 0) # Should be > 0 (255*4) # Sum of the pixels in top right + bottom left + bottom right corners corners = sum(img[0, -1] + img[-1, 0] + img[-1, -1]) assert_true(corners == 0) # Should be all 0 gloo.flush() gloo.finish() run_tests_if_main() �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/gloo/tests/test_util.py�����������������������������������������������������������0000664�0001750�0001750�00000003641�12456026505�022236� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- from vispy.gloo import util from vispy.testing import run_tests_if_main, assert_raises def test_check_enum(): from vispy.gloo import gl # Test enums assert util.check_enum(gl.GL_RGB) == 'rgb' assert util.check_enum(gl.GL_TRIANGLE_STRIP) == 'triangle_strip' # Test strings assert util.check_enum('RGB') == 'rgb' assert util.check_enum('Triangle_STRIp') == 'triangle_strip' # Test wrong input assert_raises(ValueError, util.check_enum, int(gl.GL_RGB)) assert_raises(ValueError, util.check_enum, int(gl.GL_TRIANGLE_STRIP)) assert_raises(ValueError, util.check_enum, []) # Test with test util.check_enum('RGB', 'test', ('rgb', 'alpha')) == 'rgb' util.check_enum(gl.GL_ALPHA, 'test', ('rgb', 'alpha')) == 'alpha' # assert_raises(ValueError, util.check_enum, 'RGB', 'test', ('a', 'b')) assert_raises(ValueError, util.check_enum, gl.GL_ALPHA, 'test', ('a', 'b')) # Test PyOpenGL enums try: from OpenGL import GL except ImportError: return # we cannot test PyOpenGL # assert util.check_enum(GL.GL_RGB) == 'rgb' assert util.check_enum(GL.GL_TRIANGLE_STRIP) == 'triangle_strip' def test_check_identifier(): # Tests check_identifier() assert util.check_identifier('foo') is None assert util.check_identifier('_fooBarBla') is None assert util.check_identifier('_glPosition') is None # Wrong identifier assert util.check_identifier('__').startswith('Identifier') assert util.check_identifier('gl_').startswith('Identifier') assert util.check_identifier('GL_').startswith('Identifier') assert util.check_identifier('double').startswith('Identifier') # Test check_variable() assert util.check_variable('foo') is None assert util.check_variable('a' * 30) is None assert util.check_variable('a' * 32) run_tests_if_main() �����������������������������������������������������������������������������������������������vispy-0.4.0/vispy/gloo/tests/test_globject.py�������������������������������������������������������0000664�0001750�0001750�00000002121�12510536123�023033� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2014, Nicolas P. Rougier. All rights reserved. # Distributed under the terms of the new BSD License. # ----------------------------------------------------------------------------- from vispy.testing import run_tests_if_main from vispy.gloo.globject import GLObject def test_globject(): """ Test gl object uinique id and GLIR CREATE command """ objects = [GLObject() for i in range(10)] ids = [ob.id for ob in objects] # Verify that each id is unique (test should not care how) assert len(set(ids)) == len(objects) # Verify that glir commands have been created commands = [] for ob in objects: commands.extend(ob._glir.clear()) assert len(commands) == len(objects) for cmd in commands: assert cmd[0] == 'CREATE' # Delete ob = objects[-1] q = ob._glir # get it now, because its gone after we delete it ob.delete() cmd = q.clear()[-1] assert cmd[0] == 'DELETE' run_tests_if_main() �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/gloo/tests/test_use_gloo.py�������������������������������������������������������0000664�0001750�0001750�00000013550�12510536123�023066� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2014, Nicolas P. Rougier. All rights reserved. # Distributed under the terms of the new BSD License. # ----------------------------------------------------------------------------- import numpy as np from numpy.testing import assert_allclose from vispy.app import Canvas from vispy.gloo import (Texture2D, Texture3D, Program, FrameBuffer, RenderBuffer, set_viewport, clear) from vispy.gloo.util import draw_texture, _screenshot from vispy.testing import (requires_application, has_pyopengl, run_tests_if_main, assert_raises, assert_equal) @requires_application() def test_use_textures(): """Test using textures and FBO""" assert_raises(ValueError, Texture2D, np.zeros((2, 2, 3), np.float32), format='rgba') # format and data size mismatch @requires_application() def test_use_framebuffer(): """Test drawing to a framebuffer""" shape = (100, 300) # for some reason Windows wants a tall window... data = np.random.rand(*shape).astype(np.float32) use_shape = shape + (3,) with Canvas(size=shape[::-1]) as c: orig_tex = Texture2D(data) fbo_tex = Texture2D(use_shape, format='rgb') rbo = RenderBuffer(shape, 'color') fbo = FrameBuffer(color=fbo_tex) c.context.glir.set_verbose(True) assert_equal(c.size, shape[::-1]) set_viewport((0, 0) + c.size) with fbo: draw_texture(orig_tex) draw_texture(fbo_tex) out_tex = _screenshot()[::-1, :, 0].astype(np.float32) assert_equal(out_tex.shape, c.size[::-1]) assert_raises(TypeError, FrameBuffer.color_buffer.fset, fbo, 1.) assert_raises(TypeError, FrameBuffer.depth_buffer.fset, fbo, 1.) assert_raises(TypeError, FrameBuffer.stencil_buffer.fset, fbo, 1.) fbo.color_buffer = rbo fbo.depth_buffer = RenderBuffer(shape) fbo.stencil_buffer = None print((fbo.color_buffer, fbo.depth_buffer, fbo.stencil_buffer)) clear(color='black') with fbo: clear(color='black') draw_texture(orig_tex) out_rbo = _screenshot()[:, :, 0].astype(np.float32) assert_allclose(data * 255., out_tex, atol=1) assert_allclose(data * 255., out_rbo, atol=1) @requires_application() def test_use_texture3D(): """Test using a 3D texture""" vals = [0, 200, 100, 0, 255, 0, 100] d, h, w = len(vals), 3, 5 data = np.zeros((d, h, w), np.float32) VERT_SHADER = """ attribute vec2 a_pos; varying vec2 v_pos; void main (void) { v_pos = a_pos; gl_Position = vec4(a_pos, 0., 1.); } """ FRAG_SHADER = """ uniform sampler3D u_texture; varying vec2 v_pos; uniform float i; void main() { gl_FragColor = texture3D(u_texture, vec3((v_pos.y+1.)/2., (v_pos.x+1.)/2., i)); gl_FragColor.a = 1.; } """ # populate the depth "slices" with different gray colors in the bottom left for ii, val in enumerate(vals): data[ii, :2, :3] = val / 255. with Canvas(size=(100, 100)) as c: if not has_pyopengl(): t = Texture3D(data) assert_raises(ImportError, t.glir.flush, c.context.shared.parser) return program = Program(VERT_SHADER, FRAG_SHADER) program['a_pos'] = [[-1., -1.], [1., -1.], [-1., 1.], [1., 1.]] tex = Texture3D(data, interpolation='nearest') assert_equal(tex.width, w) assert_equal(tex.height, h) assert_equal(tex.depth, d) program['u_texture'] = tex for ii, val in enumerate(vals): set_viewport(0, 0, w, h) clear(color='black') iii = (ii + 0.5) / float(d) print(ii, iii) program['i'] = iii program.draw('triangle_strip') out = _screenshot()[:, :, 0].astype(int)[::-1] expected = np.zeros_like(out) expected[:2, :3] = val assert_allclose(out, expected, atol=1./255.) @requires_application() def test_use_uniforms(): """Test using uniform arrays""" VERT_SHADER = """ attribute vec2 a_pos; varying vec2 v_pos; void main (void) { v_pos = a_pos; gl_Position = vec4(a_pos, 0., 1.); } """ FRAG_SHADER = """ varying vec2 v_pos; uniform vec3 u_color[2]; void main() { gl_FragColor = vec4((u_color[0] + u_color[1]) / 2., 1.); } """ shape = (300, 300) with Canvas(size=shape) as c: c.context.glir.set_verbose(True) assert_equal(c.size, shape[::-1]) shape = (3, 3) set_viewport((0, 0) + shape) program = Program(VERT_SHADER, FRAG_SHADER) program['a_pos'] = [[-1., -1.], [1., -1.], [-1., 1.], [1., 1.]] program['u_color'] = np.ones((2, 3)) c.context.clear('k') program.draw('triangle_strip') out = _screenshot() assert_allclose(out[:, :, 0] / 255., np.ones(shape), atol=1. / 255.) # now set one element program['u_color[1]'] = np.zeros(3, np.float32) c.context.clear('k') program.draw('triangle_strip') out = _screenshot() assert_allclose(out[:, :, 0] / 255., 127.5 / 255. * np.ones(shape), atol=1. / 255.) # and the other assert_raises(ValueError, program.__setitem__, 'u_color', np.zeros(3, np.float32)) program['u_color'] = np.zeros((2, 3), np.float32) program['u_color[0]'] = np.ones(3, np.float32) c.context.clear((0.33,) * 3) program.draw('triangle_strip') out = _screenshot() assert_allclose(out[:, :, 0] / 255., 127.5 / 255. * np.ones(shape), atol=1. / 255.) run_tests_if_main() ��������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/gloo/wrappers.py������������������������������������������������������������������0000664�0001750�0001750�00000060564�12527672621�020737� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2015, Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- import numpy as np from copy import deepcopy from . import gl from ..ext.six import string_types from ..color import Color #from ..util import logger __all__ = ('set_viewport', 'set_depth_range', 'set_front_face', # noqa 'set_cull_face', 'set_line_width', 'set_polygon_offset', # noqa 'clear', 'set_clear_color', 'set_clear_depth', 'set_clear_stencil', # noqa 'set_blend_func', 'set_blend_color', 'set_blend_equation', # noqa 'set_scissor', 'set_stencil_func', 'set_stencil_mask', # noqa 'set_stencil_op', 'set_depth_func', 'set_depth_mask', # noqa 'set_color_mask', 'set_sample_coverage', # noqa 'get_state_presets', 'set_state', 'finish', 'flush', # noqa 'read_pixels', 'set_hint', # noqa 'get_gl_configuration', '_check_valid', 'GlooFunctions', 'global_gloo_functions', ) _setters = [s[4:] for s in __all__ if s.startswith('set_') and s != 'set_state'] # NOTE: If these are updated to have things beyond glEnable/glBlendFunc # calls, set_preset_state will need to be updated to deal with it. _gl_presets = { 'opaque': dict( depth_test=True, cull_face=False, blend=False), 'translucent': dict( depth_test=True, cull_face=False, blend=True, blend_func=('src_alpha', 'one_minus_src_alpha')), 'additive': dict( depth_test=False, cull_face=False, blend=True, blend_func=('src_alpha', 'one')), } def get_current_canvas(): """ Proxy for context.get_current_canvas to avoud circular import. This function replaces itself with the real function the first time it is called. (Bah) """ from .context import get_current_canvas globals()['get_current_canvas'] = get_current_canvas return get_current_canvas() # Helpers that are needed for efficient wrapping def _check_valid(key, val, valid): """Helper to check valid options""" if val not in valid: raise ValueError('%s must be one of %s, not "%s"' % (key, valid, val)) def _to_args(x): """Convert to args representation""" if not isinstance(x, (list, tuple, np.ndarray)): x = [x] return x def _check_conversion(key, valid_dict): """Check for existence of key in dict, return value or raise error""" if key not in valid_dict and key not in valid_dict.values(): # Only show users the nice string values keys = [v for v in valid_dict.keys() if isinstance(v, string_types)] raise ValueError('value must be one of %s, not %s' % (keys, key)) return valid_dict[key] if key in valid_dict else key class BaseGlooFunctions(object): """ Class that provides a series of GL functions that do not fit in the object oriented part of gloo. An instance of this class is associated with each canvas. """ ########################################################################## # PRIMITIVE/VERTEX # # Viewport, DepthRangef, CullFace, FrontFace, LineWidth, PolygonOffset # def set_viewport(self, *args): """Set the OpenGL viewport This is a wrapper for gl.glViewport. Parameters ---------- *args : tuple X and Y coordinates, plus width and height. Can be passed in as individual components, or as a single tuple with four values. """ x, y, w, h = args[0] if len(args) == 1 else args self.glir.command('FUNC', 'glViewport', int(x), int(y), int(w), int(h)) def set_depth_range(self, near=0., far=1.): """Set depth values Parameters ---------- near : float Near clipping plane. far : float Far clipping plane. """ self.glir.command('FUNC', 'glDepthRange', float(near), float(far)) def set_front_face(self, mode='ccw'): """Set which faces are front-facing Parameters ---------- mode : str Can be 'cw' for clockwise or 'ccw' for counter-clockwise. """ self.glir.command('FUNC', 'glFrontFace', mode) def set_cull_face(self, mode='back'): """Set front, back, or both faces to be culled Parameters ---------- mode : str Culling mode. Can be "front", "back", or "front_and_back". """ self.glir.command('FUNC', 'glCullFace', mode) def set_line_width(self, width=1.): """Set line width Parameters ---------- width : float The line width. """ width = float(width) if width < 0: raise RuntimeError('Cannot have width < 0') self.glir.command('FUNC', 'glLineWidth', width) def set_polygon_offset(self, factor=0., units=0.): """Set the scale and units used to calculate depth values Parameters ---------- factor : float Scale factor used to create a variable depth offset for each polygon. units : float Multiplied by an implementation-specific value to create a constant depth offset. """ self.glir.command('FUNC', 'glPolygonOffset', float(factor), float(units)) ########################################################################## # FRAGMENT/SCREEN # # glClear, glClearColor, glClearDepthf, glClearStencil # def clear(self, color=True, depth=True, stencil=True): """Clear the screen buffers This is a wrapper for gl.glClear. Parameters ---------- color : bool | str | tuple | instance of Color Clear the color buffer bit. If not bool, ``set_clear_color`` will be used to set the color clear value. depth : bool | float Clear the depth buffer bit. If float, ``set_clear_depth`` will be used to set the depth clear value. stencil : bool | int Clear the stencil buffer bit. If int, ``set_clear_stencil`` will be used to set the stencil clear index. """ bits = 0 if isinstance(color, np.ndarray) or bool(color): if not isinstance(color, bool): self.set_clear_color(color) bits |= gl.GL_COLOR_BUFFER_BIT if depth: if not isinstance(depth, bool): self.set_clear_depth(depth) bits |= gl.GL_DEPTH_BUFFER_BIT if stencil: if not isinstance(stencil, bool): self.set_clear_stencil(stencil) bits |= gl.GL_STENCIL_BUFFER_BIT self.glir.command('FUNC', 'glClear', bits) def set_clear_color(self, color='black', alpha=None): """Set the screen clear color This is a wrapper for gl.glClearColor. Parameters ---------- color : str | tuple | instance of Color Color to use. See vispy.color.Color for options. alpha : float | None Alpha to use. """ self.glir.command('FUNC', 'glClearColor', *Color(color, alpha).rgba) def set_clear_depth(self, depth=1.0): """Set the clear value for the depth buffer This is a wrapper for gl.glClearDepth. Parameters ---------- depth : float The depth to use. """ self.glir.command('FUNC', 'glClearDepth', float(depth)) def set_clear_stencil(self, index=0): """Set the clear value for the stencil buffer This is a wrapper for gl.glClearStencil. Parameters ---------- index : int The index to use when the stencil buffer is cleared. """ self.glir.command('FUNC', 'glClearStencil', int(index)) # glBlendFunc(Separate), glBlendColor, glBlendEquation(Separate) def set_blend_func(self, srgb='one', drgb='zero', salpha=None, dalpha=None): """Specify pixel arithmetic for RGB and alpha Parameters ---------- srgb : str Source RGB factor. drgb : str Destination RGB factor. salpha : str | None Source alpha factor. If None, ``srgb`` is used. dalpha : str Destination alpha factor. If None, ``drgb`` is used. """ salpha = srgb if salpha is None else salpha dalpha = drgb if dalpha is None else dalpha self.glir.command('FUNC', 'glBlendFuncSeparate', srgb, drgb, salpha, dalpha) def set_blend_color(self, color): """Set the blend color Parameters ---------- color : str | tuple | instance of Color Color to use. See vispy.color.Color for options. """ self.glir.command('FUNC', 'glBlendColor', *Color(color).rgba) def set_blend_equation(self, mode_rgb, mode_alpha=None): """Specify the equation for RGB and alpha blending Parameters ---------- mode_rgb : str Mode for RGB. mode_alpha : str | None Mode for Alpha. If None, ``mode_rgb`` is used. Notes ----- See ``set_blend_equation`` for valid modes. """ mode_alpha = mode_rgb if mode_alpha is None else mode_alpha self.glir.command('FUNC', 'glBlendEquationSeparate', mode_rgb, mode_alpha) # glScissor, glStencilFunc(Separate), glStencilMask(Separate), # glStencilOp(Separate), def set_scissor(self, x, y, w, h): """Define the scissor box Parameters ---------- x : int Left corner of the box. y : int Lower corner of the box. w : int The width of the box. h : int The height of the box. """ self.glir.command('FUNC', 'glScissor', int(x), int(y), int(w), int(h)) def set_stencil_func(self, func='always', ref=0, mask=8, face='front_and_back'): """Set front or back function and reference value Parameters ---------- func : str See set_stencil_func. ref : int Reference value for the stencil test. mask : int Mask that is ANDed with ref and stored stencil value. face : str Can be 'front', 'back', or 'front_and_back'. """ self.glir.command('FUNC', 'glStencilFuncSeparate', face, func, int(ref), int(mask)) def set_stencil_mask(self, mask=8, face='front_and_back'): """Control the front or back writing of individual bits in the stencil Parameters ---------- mask : int Mask that is ANDed with ref and stored stencil value. face : str Can be 'front', 'back', or 'front_and_back'. """ self.glir.command('FUNC', 'glStencilMaskSeparate', face, int(mask)) def set_stencil_op(self, sfail='keep', dpfail='keep', dppass='keep', face='front_and_back'): """Set front or back stencil test actions Parameters ---------- sfail : str Action to take when the stencil fails. Must be one of 'keep', 'zero', 'replace', 'incr', 'incr_wrap', 'decr', 'decr_wrap', or 'invert'. dpfail : str Action to take when the stencil passes. dppass : str Action to take when both the stencil and depth tests pass, or when the stencil test passes and either there is no depth buffer or depth testing is not enabled. face : str Can be 'front', 'back', or 'front_and_back'. """ self.glir.command('FUNC', 'glStencilOpSeparate', face, sfail, dpfail, dppass) # glDepthFunc, glDepthMask, glColorMask, glSampleCoverage def set_depth_func(self, func='less'): """Specify the value used for depth buffer comparisons Parameters ---------- func : str The depth comparison function. Must be one of 'never', 'less', 'equal', 'lequal', 'greater', 'gequal', 'notequal', or 'always'. """ self.glir.command('FUNC', 'glDepthFunc', func) def set_depth_mask(self, flag): """Toggle writing into the depth buffer Parameters ---------- flag : bool Whether depth writing should be enabled. """ self.glir.command('FUNC', 'glDepthMask', bool(flag)) def set_color_mask(self, red, green, blue, alpha): """Toggle writing of frame buffer color components Parameters ---------- red : bool Red toggle. green : bool Green toggle. blue : bool Blue toggle. alpha : bool Alpha toggle. """ self.glir.command('FUNC', 'glColorMask', bool(red), bool(green), bool(blue), bool(alpha)) def set_sample_coverage(self, value=1.0, invert=False): """Specify multisample coverage parameters Parameters ---------- value : float Sample coverage value (will be clamped between 0. and 1.). invert : bool Specify if the coverage masks should be inverted. """ self.glir.command('FUNC', 'glSampleCoverage', float(value), bool(invert)) ########################################################################## # STATE # # glEnable/Disable # def get_state_presets(self): """The available GL state presets Returns ------- presets : dict The dictionary of presets usable with ``set_options``. """ return deepcopy(_gl_presets) def set_state(self, preset=None, **kwargs): """Set OpenGL rendering state, optionally using a preset Parameters ---------- preset : str | None Can be one of ('opaque', 'translucent', 'additive') to use use reasonable defaults for these typical use cases. **kwargs : keyword arguments Other supplied keyword arguments will override any preset defaults. Options to be enabled or disabled should be supplied as booleans (e.g., ``'depth_test=True'``, ``cull_face=False``), non-boolean entries will be passed as arguments to ``set_*`` functions (e.g., ``blend_func=('src_alpha', 'one')`` will call ``set_blend_func``). Notes ----- This serves three purposes: 1. Set GL state using reasonable presets. 2. Wrapping glEnable/glDisable functionality. 3. Convienence wrapping of other ``gloo.set_*`` functions. For example, one could do the following: >>> from vispy import gloo >>> gloo.set_state('translucent', depth_test=False, clear_color=(1, 1, 1, 1)) # noqa, doctest:+SKIP This would take the preset defaults for 'translucent', turn depth testing off (which would normally be on for that preset), and additionally set the glClearColor parameter to be white. Another example to showcase glEnable/glDisable wrapping: >>> gloo.set_state(blend=True, depth_test=True, polygon_offset_fill=False) # noqa, doctest:+SKIP This would be equivalent to calling >>> from vispy.gloo import gl >>> gl.glDisable(gl.GL_BLEND) >>> gl.glEnable(gl.GL_DEPTH_TEST) >>> gl.glEnable(gl.GL_POLYGON_OFFSET_FILL) Or here's another example: >>> gloo.set_state(clear_color=(0, 0, 0, 1), blend=True, blend_func=('src_alpha', 'one')) # noqa, doctest:+SKIP Thus arbitrary GL state components can be set directly using ``set_state``. Note that individual functions are exposed e.g., as ``set_clear_color``, with some more informative docstrings about those particular functions. """ kwargs = deepcopy(kwargs) # Load preset, if supplied if preset is not None: _check_valid('preset', preset, tuple(list(_gl_presets.keys()))) for key, val in _gl_presets[preset].items(): # only overwrite user input with preset if user's input is None if key not in kwargs: kwargs[key] = val # cull_face is an exception because GL_CULL_FACE, glCullFace both exist if 'cull_face' in kwargs: cull_face = kwargs.pop('cull_face') if isinstance(cull_face, bool): funcname = 'glEnable' if cull_face else 'glDisable' self.glir.command('FUNC', funcname, 'cull_face') else: self.glir.command('FUNC', 'glEnable', 'cull_face') self.set_cull_face(*_to_args(cull_face)) # Iterate over kwargs for key, val in kwargs.items(): if key in _setters: # Setter args = _to_args(val) # these actually need tuples if key in ('blend_color', 'clear_color') and \ not isinstance(args[0], string_types): args = [args] getattr(self, 'set_' + key)(*args) else: # Enable / disable funcname = 'glEnable' if val else 'glDisable' self.glir.command('FUNC', funcname, key) # # glFinish, glFlush, glReadPixels, glHint # def finish(self): """Wait for GL commands to to finish This creates a GLIR command for glFinish and then processes the GLIR commands. If the GLIR interpreter is remote (e.g. WebGL), this function will return before GL has finished processing the commands. """ if hasattr(self, 'flush_commands'): context = self else: context = get_current_canvas().context context.glir.command('FUNC', 'glFinish') context.flush_commands() # Process GLIR commands def flush(self): """Flush GL commands This is a wrapper for glFlush(). This also flushes the GLIR command queue. """ if hasattr(self, 'flush_commands'): context = self else: context = get_current_canvas().context context.glir.command('FUNC', 'glFlush') context.flush_commands() # Process GLIR commands def set_hint(self, target, mode): """Set OpenGL drawing hint Parameters ---------- target : str The target, e.g. 'fog_hint', 'line_smooth_hint', 'point_smooth_hint'. mode : str The mode to set (e.g., 'fastest', 'nicest', 'dont_care'). """ if not all(isinstance(tm, string_types) for tm in (target, mode)): raise TypeError('target and mode must both be strings') self.glir.command('FUNC', 'glHint', target, mode) class GlooFunctions(BaseGlooFunctions): @property def glir(self): """ The GLIR queue corresponding to the current canvas """ canvas = get_current_canvas() if canvas is None: msg = ("If you want to use gloo without vispy.app, " + "use a gloo.context.FakeCanvas.") raise RuntimeError('Gloo requires a Canvas to run.\n' + msg) return canvas.context.glir ## Create global functions object and inject names here # GlooFunctions without queue: use queue of canvas that is current at call-time global_gloo_functions = GlooFunctions() for name in dir(global_gloo_functions): if name.startswith('_') or name in ('glir'): continue fun = getattr(global_gloo_functions, name) if callable(fun): globals()[name] = fun ## Functions that do not use the glir queue def read_pixels(viewport=None, alpha=True, out_type='unsigned_byte'): """Read pixels from the currently selected buffer. Under most circumstances, this function reads from the front buffer. Unlike all other functions in vispy.gloo, this function directly executes an OpenGL command. Parameters ---------- viewport : array-like | None 4-element list of x, y, w, h parameters. If None (default), the current GL viewport will be queried and used. alpha : bool If True (default), the returned array has 4 elements (RGBA). If False, it has 3 (RGB). out_type : str | dtype Can be 'unsigned_byte' or 'float'. Note that this does not use casting, but instead determines how values are read from the current buffer. Can also be numpy dtypes ``np.uint8``, ``np.ubyte``, or ``np.float32``. Returns ------- pixels : array 3D array of pixels in np.uint8 or np.float32 format. The array shape is (h, w, 3) or (h, w, 4), with the top-left corner of the framebuffer at index [0, 0] in the returned array. """ # Check whether the GL context is direct or remote context = get_current_canvas().context if context.shared.parser.is_remote(): raise RuntimeError('Cannot use read_pixels() with remote GLIR parser') finish() # noqa - finish first, also flushes GLIR commands type_dict = {'unsigned_byte': gl.GL_UNSIGNED_BYTE, np.uint8: gl.GL_UNSIGNED_BYTE, 'float': gl.GL_FLOAT, np.float32: gl.GL_FLOAT} type_ = _check_conversion(out_type, type_dict) if viewport is None: viewport = gl.glGetParameter(gl.GL_VIEWPORT) viewport = np.array(viewport, int) if viewport.ndim != 1 or viewport.size != 4: raise ValueError('viewport should be 1D 4-element array-like, not %s' % (viewport,)) x, y, w, h = viewport gl.glPixelStorei(gl.GL_PACK_ALIGNMENT, 1) # PACK, not UNPACK fmt = gl.GL_RGBA if alpha else gl.GL_RGB im = gl.glReadPixels(x, y, w, h, fmt, type_) gl.glPixelStorei(gl.GL_PACK_ALIGNMENT, 4) # reshape, flip, and return if not isinstance(im, np.ndarray): np_dtype = np.uint8 if type_ == gl.GL_UNSIGNED_BYTE else np.float32 im = np.frombuffer(im, np_dtype) im.shape = h, w, (4 if alpha else 3) # RGBA vs RGB im = im[::-1, :, :] # flip the image return im def get_gl_configuration(): """Read the current gl configuration This function uses constants that are not in the OpenGL ES 2.1 namespace, so only use this on desktop systems. Returns ------- config : dict The currently active OpenGL configuration. """ # XXX eventually maybe we can ask `gl` whether or not we can access these gl.check_error('pre-config check') config = dict() gl.glBindFramebuffer(gl.GL_FRAMEBUFFER, 0) fb_param = gl.glGetFramebufferAttachmentParameter # copied since they aren't in ES: GL_FRONT_LEFT = 1024 GL_DEPTH = 6145 GL_STENCIL = 6146 GL_SRGB = 35904 GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING = 33296 GL_STEREO = 3123 GL_DOUBLEBUFFER = 3122 sizes = dict(red=(GL_FRONT_LEFT, 33298), green=(GL_FRONT_LEFT, 33299), blue=(GL_FRONT_LEFT, 33300), alpha=(GL_FRONT_LEFT, 33301), depth=(GL_DEPTH, 33302), stencil=(GL_STENCIL, 33303)) for key, val in sizes.items(): config[key + '_size'] = fb_param(gl.GL_FRAMEBUFFER, val[0], val[1]) val = fb_param(gl.GL_FRAMEBUFFER, GL_FRONT_LEFT, GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING) if val not in (gl.GL_LINEAR, GL_SRGB): raise RuntimeError('unknown value for SRGB: %s' % val) config['srgb'] = True if val == GL_SRGB else False # GL_LINEAR config['stereo'] = True if gl.glGetParameter(GL_STEREO) else False config['double_buffer'] = (True if gl.glGetParameter(GL_DOUBLEBUFFER) else False) config['samples'] = gl.glGetParameter(gl.GL_SAMPLES) gl.check_error('post-config check') return config ��������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/plot/�����������������������������������������������������������������������������0000775�0001750�0001750�00000000000�12527674621�016527� 5����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/plot/plotwidget.py����������������������������������������������������������������0000664�0001750�0001750�00000020442�12527672621�021263� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from ..scene import (Image, LinePlot, Volume, Mesh, Histogram, Spectrogram, ViewBox, PanZoomCamera, TurntableCamera) from ..io import read_mesh from ..geometry import MeshData __all__ = ['PlotWidget'] class PlotWidget(ViewBox): """Widget to facilitate plotting Parameters ---------- *args : arguments Arguments passed to the `ViewBox` super class. **kwargs : keywoard arguments Keyword arguments passed to the `ViewBox` super class. Notes ----- This class is typically instantiated implicitly by a `Figure` instance, e.g., by doing ``fig[0, 0]``. See Also -------- """ def __init__(self, *args, **kwargs): super(PlotWidget, self).__init__(*args, **kwargs) self._camera_set = False def _set_camera(self, cls, *args, **kwargs): if not self._camera_set: self._camera_set = True self.camera = cls(*args, **kwargs) self.camera.set_range(margin=0) def histogram(self, data, bins=10, color='w', orientation='h'): """Calculate and show a histogram of data Parameters ---------- data : array-like Data to histogram. Currently only 1D data is supported. bins : int | array-like Number of bins, or bin edges. color : instance of Color Color of the histogram. orientation : {'h', 'v'} Orientation of the histogram. Returns ------- hist : instance of Polygon The histogram polygon. """ hist = Histogram(data, bins, color, orientation) self.add(hist) self._set_camera(PanZoomCamera) return hist def image(self, data, cmap='cubehelix', clim='auto'): """Show an image Parameters ---------- data : ndarray Should have shape (N, M), (N, M, 3) or (N, M, 4). cmap : str Colormap name. clim : str | tuple Colormap limits. Should be ``'auto'`` or a two-element tuple of min and max values. Returns ------- image : instance of Image The image. Notes ----- The colormap is only used if the image pixels are scalars. """ image = Image(data, cmap=cmap, clim=clim) self.add(image) self._set_camera(PanZoomCamera, aspect=1) return image def mesh(self, vertices=None, faces=None, vertex_colors=None, face_colors=None, color=(0.5, 0.5, 1.), fname=None, meshdata=None): """Show a 3D mesh Parameters ---------- vertices : array Vertices. faces : array | None Face definitions. vertex_colors : array | None Vertex colors. face_colors : array | None Face colors. color : instance of Color Color to use. fname : str | None Filename to load. If not None, then vertices, faces, and meshdata must be None. meshdata : MeshData | None Meshdata to use. If not None, then vertices, faces, and fname must be None. Returns ------- mesh : instance of Mesh The mesh. """ if fname is not None: if not all(x is None for x in (vertices, faces, meshdata)): raise ValueError('vertices, faces, and meshdata must be None ' 'if fname is not None') vertices, faces = read_mesh(fname)[:2] if meshdata is not None: if not all(x is None for x in (vertices, faces, fname)): raise ValueError('vertices, faces, and fname must be None if ' 'fname is not None') else: meshdata = MeshData(vertices, faces) mesh = Mesh(meshdata=meshdata, vertex_colors=vertex_colors, face_colors=face_colors, color=color, shading='smooth') self.add(mesh) self._set_camera(TurntableCamera, azimuth=0, elevation=0) return mesh def plot(self, data, color='k', symbol='o', line_kind='-', width=1., marker_size=0., edge_color='k', face_color='k', edge_width=1.): """Plot a series of data using lines and markers Parameters ---------- data : array | two arrays Arguments can be passed as ``(Y,)``, ``(X, Y)`` or ``np.array((X, Y))``. color : instance of Color Color of the line. symbol : str Marker symbol to use. line_kind : str Kind of line to draw. For now, only solid lines (``'-'``) are supported. width : float Line width. marker_size : float Marker size. If `size == 0` markers will not be shown. edge_color : instance of Color Color of the marker edge. face_color : instance of Color Color of the marker face. edge_width : float Edge width of the marker. Returns ------- line : instance of LinePlot The line plot. See also -------- marker_types, LinePlot """ line = LinePlot(data, connect='strip', color=color, symbol=symbol, line_kind=line_kind, width=width, marker_size=marker_size, edge_color=edge_color, face_color=face_color, edge_width=edge_width) self.add(line) self._set_camera(PanZoomCamera) return line def spectrogram(self, x, n_fft=256, step=None, fs=1., window='hann', color_scale='log', cmap='cubehelix', clim='auto'): """Calculate and show a spectrogram Parameters ---------- x : array-like 1D signal to operate on. ``If len(x) < n_fft``, x will be zero-padded to length ``n_fft``. n_fft : int Number of FFT points. Much faster for powers of two. step : int | None Step size between calculations. If None, ``n_fft // 2`` will be used. fs : float The sample rate of the data. window : str | None Window function to use. Can be ``'hann'`` for Hann window, or None for no windowing. color_scale : {'linear', 'log'} Scale to apply to the result of the STFT. ``'log'`` will use ``10 * log10(power)``. cmap : str Colormap name. clim : str | tuple Colormap limits. Should be ``'auto'`` or a two-element tuple of min and max values. Returns ------- spec : instance of Spectrogram The spectrogram. See also -------- Image """ # XXX once we have axes, we should use "fft_freqs", too spec = Spectrogram(x, n_fft, step, fs, window, color_scale, cmap, clim) self.add(spec) self._set_camera(PanZoomCamera) return spec def volume(self, vol, clim=None, method='mip', threshold=None, cmap='grays'): """Show a 3D volume Parameters ---------- vol : ndarray Volume to render. clim : tuple of two floats | None The contrast limits. The values in the volume are mapped to black and white corresponding to these values. Default maps between min and max. method : {'mip', 'iso', 'translucent', 'additive'} The render style to use. See corresponding docs for details. Default 'mip'. threshold : float The threshold to use for the isosurafce render style. By default the mean of the given volume is used. cmap : str The colormap to use. Returns ------- volume : instance of Volume The volume visualization. See also -------- Volume """ volume = Volume(vol, clim, method, threshold, cmap=cmap) self.add(volume) self._set_camera(TurntableCamera, fov=30.) return volume ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/plot/__init__.py������������������������������������������������������������������0000664�0001750�0001750�00000001717�12527672621�020644� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ This module provides functions for displaying data from a command-line interface. **NOTE**: This module is still experimental, and under development. It currently lacks axes, but that is a high-priority target for the next release. Usage ----- To use `vispy.plot` typically the main class `Fig` is first instantiated:: >>> from vispy.plot import Fig >>> fig = Fig() And then `PlotWidget` instances are automatically created by accessing the ``fig`` instance:: >>> ax_left = fig[0, 0] >>> ax_right = fig[0, 1] Then plots are accomplished via methods of the `PlotWidget` instances:: >>> import numpy as np >>> data = np.random.randn(2, 10) >>> ax_left.plot(data) >>> ax_right.histogram(data[1]) """ from .fig import Fig from .plotwidget import PlotWidget __all__ = ['Fig', 'PlotWidget'] �������������������������������������������������vispy-0.4.0/vispy/plot/fig.py�����������������������������������������������������������������������0000664�0001750�0001750�00000002760�12527672621�017651� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from ..scene import SceneCanvas from .plotwidget import PlotWidget class Fig(SceneCanvas): """Create a figure window Parameters ---------- bgcolor : instance of Color Color to use for the background. size : tuple Size of the figure window in pixels. show : bool If True, show the window. Notes ----- You can create a Figure, PlotWidget, and diagonal line plot like this:: >>> from vispy.plot import Fig >>> fig = Fig() >>> ax = fig[0, 0] # this creates a PlotWidget >>> ax.plot([[0, 1], [0, 1]]) See the gallery for many other examples. See Also -------- PlotWidget : the axis widget for plotting SceneCanvas : the super class """ def __init__(self, bgcolor='w', size=(800, 600), show=True): super(Fig, self).__init__(bgcolor=bgcolor, keys='interactive', show=show, size=size) self._grid = self.central_widget.add_grid() self._grid._default_class = PlotWidget self._plot_widgets = [] @property def plot_widgets(self): """List of the associated PlotWidget instances""" return tuple(self._plot_widgets) def __getitem__(self, idxs): """Get an axis""" pw = self._grid.__getitem__(idxs) self._plot_widgets += [pw] return pw ����������������vispy-0.4.0/vispy/plot/tests/�����������������������������������������������������������������������0000775�0001750�0001750�00000000000�12527674621�017671� 5����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/plot/tests/__init__.py������������������������������������������������������������0000664�0001750�0001750�00000000000�12510536123�021752� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/plot/tests/test_plot.py�����������������������������������������������������������0000664�0001750�0001750�00000001160�12527672621�022254� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. import vispy.plot as vp from vispy.testing import (assert_raises, requires_application, run_tests_if_main) @requires_application() def test_figure_creation(): """Test creating a figure""" with vp.Fig(show=False) as fig: fig[0, 0:2] fig[1:3, 0:2] ax_right = fig[1:3, 2] assert fig[1:3, 2] is ax_right # collision assert_raises(ValueError, fig.__getitem__, (slice(1, 3), 1)) run_tests_if_main() ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/__init__.py�����������������������������������������������������������������������0000664�0001750�0001750�00000001626�12527673355�017672� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ ===== Vispy ===== Vispy is a **high-performance interactive 2D/3D data visualization library**. Vispy leverages the computational power of modern **Graphics Processing Units (GPUs)** through the **OpenGL** library to display very large datasets. For more information, see http://vispy.org. """ from __future__ import division __all__ = ['use', 'sys_info', 'set_log_level', 'test'] # Definition of the version number version_info = 0, 4, 0 # major, minor, patch, extra # Nice string for the version (mimic how IPython composes its version str) __version__ = '-'.join(map(str, version_info)).replace('-', '.', 2).strip('-') from .util import config, set_log_level, keys, sys_info # noqa from .util.wrappers import use # noqa from .testing import test # noqa ����������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/ext/������������������������������������������������������������������������������0000775�0001750�0001750�00000000000�12527674621�016351� 5����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/ext/decorator.py������������������������������������������������������������������0000664�0001750�0001750�00000024640�12406355656�020713� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������########################## LICENCE ############################### # Copyright (c) 2005-2012, Michele Simionato # All rights reserved. # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are # met: # Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # Redistributions in bytecode form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS # OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR # TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE # USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH # DAMAGE. """ Decorator module, see http://pypi.python.org/pypi/decorator for the documentation. """ from __future__ import print_function __version__ = '3.4.0' __all__ = ["decorator", "FunctionMaker", "contextmanager"] import sys, re, inspect if sys.version >= '3': from inspect import getfullargspec def get_init(cls): return cls.__init__ else: class getfullargspec(object): "A quick and dirty replacement for getfullargspec for Python 2.X" def __init__(self, f): self.args, self.varargs, self.varkw, self.defaults = \ inspect.getargspec(f) self.kwonlyargs = [] self.kwonlydefaults = None def __iter__(self): yield self.args yield self.varargs yield self.varkw yield self.defaults def get_init(cls): return cls.__init__.__func__ DEF = re.compile('\s*def\s*([_\w][_\w\d]*)\s*\(') # basic functionality class FunctionMaker(object): """ An object with the ability to create functions with a given signature. It has attributes name, doc, module, signature, defaults, dict and methods update and make. """ def __init__(self, func=None, name=None, signature=None, defaults=None, doc=None, module=None, funcdict=None): self.shortsignature = signature if func: # func can be a class or a callable, but not an instance method self.name = func.__name__ if self.name == '': # small hack for lambda functions self.name = '_lambda_' self.doc = func.__doc__ self.module = func.__module__ if inspect.isfunction(func): argspec = getfullargspec(func) self.annotations = getattr(func, '__annotations__', {}) for a in ('args', 'varargs', 'varkw', 'defaults', 'kwonlyargs', 'kwonlydefaults'): setattr(self, a, getattr(argspec, a)) for i, arg in enumerate(self.args): setattr(self, 'arg%d' % i, arg) if sys.version < '3': # easy way self.shortsignature = self.signature = \ inspect.formatargspec( formatvalue=lambda val: "", *argspec)[1:-1] else: # Python 3 way allargs = list(self.args) allshortargs = list(self.args) if self.varargs: allargs.append('*' + self.varargs) allshortargs.append('*' + self.varargs) elif self.kwonlyargs: allargs.append('*') # single star syntax for a in self.kwonlyargs: allargs.append('%s=None' % a) allshortargs.append('%s=%s' % (a, a)) if self.varkw: allargs.append('**' + self.varkw) allshortargs.append('**' + self.varkw) self.signature = ', '.join(allargs) self.shortsignature = ', '.join(allshortargs) self.dict = func.__dict__.copy() # func=None happens when decorating a caller if name: self.name = name if signature is not None: self.signature = signature if defaults: self.defaults = defaults if doc: self.doc = doc if module: self.module = module if funcdict: self.dict = funcdict # check existence required attributes assert hasattr(self, 'name') if not hasattr(self, 'signature'): raise TypeError('You are decorating a non function: %s' % func) def update(self, func, **kw): "Update the signature of func with the data in self" func.__name__ = self.name func.__doc__ = getattr(self, 'doc', None) func.__dict__ = getattr(self, 'dict', {}) func.__defaults__ = getattr(self, 'defaults', ()) func.__kwdefaults__ = getattr(self, 'kwonlydefaults', None) func.__annotations__ = getattr(self, 'annotations', None) callermodule = sys._getframe(3).f_globals.get('__name__', '?') func.__module__ = getattr(self, 'module', callermodule) func.__dict__.update(kw) def make(self, src_templ, evaldict=None, addsource=False, **attrs): "Make a new function from a given template and update the signature" src = src_templ % vars(self) # expand name and signature evaldict = evaldict or {} mo = DEF.match(src) if mo is None: raise SyntaxError('not a valid function template\n%s' % src) name = mo.group(1) # extract the function name names = set([name] + [arg.strip(' *') for arg in self.shortsignature.split(',')]) for n in names: if n in ('_func_', '_call_'): raise NameError('%s is overridden in\n%s' % (n, src)) if not src.endswith('\n'): # add a newline just for safety src += '\n' # this is needed in old versions of Python try: code = compile(src, '', 'single') # print >> sys.stderr, 'Compiling %s' % src exec(code, evaldict) except: print('Error in generated code:', file=sys.stderr) print(src, file=sys.stderr) raise func = evaldict[name] if addsource: attrs['__source__'] = src self.update(func, **attrs) return func @classmethod def create(cls, obj, body, evaldict, defaults=None, doc=None, module=None, addsource=True, **attrs): """ Create a function from the strings name, signature and body. evaldict is the evaluation dictionary. If addsource is true an attribute __source__ is added to the result. The attributes attrs are added, if any. """ if isinstance(obj, str): # "name(signature)" name, rest = obj.strip().split('(', 1) signature = rest[:-1] #strip a right parens func = None else: # a function name = None signature = None func = obj self = cls(func, name, signature, defaults, doc, module) ibody = '\n'.join(' ' + line for line in body.splitlines()) return self.make('def %(name)s(%(signature)s):\n' + ibody, evaldict, addsource, **attrs) def decorator(caller, func=None): """ decorator(caller) converts a caller function into a decorator; decorator(caller, func) decorates a function using a caller. """ if func is not None: # returns a decorated function evaldict = func.__globals__.copy() evaldict['_call_'] = caller evaldict['_func_'] = func return FunctionMaker.create( func, "return _call_(_func_, %(shortsignature)s)", evaldict, undecorated=func, __wrapped__=func) else: # returns a decorator if inspect.isclass(caller): name = caller.__name__.lower() callerfunc = get_init(caller) doc = 'decorator(%s) converts functions/generators into ' \ 'factories of %s objects' % (caller.__name__, caller.__name__) fun = getfullargspec(callerfunc).args[1] # second arg elif inspect.isfunction(caller): name = '_lambda_' if caller.__name__ == '' \ else caller.__name__ callerfunc = caller doc = caller.__doc__ fun = getfullargspec(callerfunc).args[0] # first arg else: # assume caller is an object with a __call__ method name = caller.__class__.__name__.lower() callerfunc = caller.__call__.__func__ doc = caller.__call__.__doc__ fun = getfullargspec(callerfunc).args[1] # second arg evaldict = callerfunc.__globals__.copy() evaldict['_call_'] = caller evaldict['decorator'] = decorator return FunctionMaker.create( '%s(%s)' % (name, fun), 'return decorator(_call_, %s)' % fun, evaldict, undecorated=caller, __wrapped__=caller, doc=doc, module=caller.__module__) ######################### contextmanager ######################## def __call__(self, func): 'Context manager decorator' return FunctionMaker.create( func, "with _self_: return _func_(%(shortsignature)s)", dict(_self_=self, _func_=func), __wrapped__=func) try: # Python >= 3.2 from contextlib import _GeneratorContextManager ContextManager = type( 'ContextManager', (_GeneratorContextManager,), dict(__call__=__call__)) except ImportError: # Python >= 2.5 from contextlib import GeneratorContextManager def __init__(self, f, *a, **k): return GeneratorContextManager.__init__(self, f(*a, **k)) ContextManager = type( 'ContextManager', (GeneratorContextManager,), dict(__call__=__call__, __init__=__init__)) contextmanager = decorator(ContextManager) ������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/ext/freetype.py�������������������������������������������������������������������0000775�0001750�0001750�00000043762�12510536123�020547� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env python # -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # # FreeType high-level python API - Copyright 2011 Nicolas P. Rougier # Distributed under the terms of the new BSD license. # # ----------------------------------------------------------------------------- ''' FreeType high-level python API Adapted from freetype-py. ''' import sys import struct from ctypes import (byref, c_char_p, c_ushort, cast, util, CDLL, Structure, POINTER, c_int, c_short, c_long, c_void_p, c_uint, c_char, c_ubyte, CFUNCTYPE) from .six import string_types from ..util import load_data_file FT_LOAD_RENDER = 4 FT_KERNING_DEFAULT = 0 FT_KERNING_UNFITTED = 1 FT_LOAD_NO_HINTING = 2 FT_LOAD_FORCE_AUTOHINT = 32 FT_LOAD_NO_AUTOHINT = 32768 FT_LOAD_TARGET_LCD = 196608 FT_LOAD_TARGET_LIGHT = 65536 FT_LOAD_NO_SCALE = 1 _64_bit = (8 * struct.calcsize("P")) == 64 ############################################################################## # ft_structs FT_Int = c_int FT_UInt = c_uint FT_F2Dot14 = c_short FT_Pos = FT_Fixed = FT_Long = c_long FT_Glyph_Format = c_int FT_String_p = c_char_p FT_Short = c_short # A typedef for signed short. FT_UShort = c_ushort # A typedef for unsigned short. FT_Generic_Finalizer = CFUNCTYPE(None, c_void_p) FT_Encoding = c_int class FT_LibraryRec(Structure): _fields_ = [] FT_Library = POINTER(FT_LibraryRec) class FT_Vector(Structure): _fields_ = [('x', FT_Pos), ('y', FT_Pos)] class FT_UnitVector(Structure): _fields_ = [('x', FT_F2Dot14), ('y', FT_F2Dot14)] class FT_Matrix(Structure): _fields_ = [('xx', FT_Fixed), ('xy', FT_Fixed), ('yx', FT_Fixed), ('yy', FT_Fixed)] class FT_GlyphRec(Structure): _fields_ = [('library', FT_Library), ('clazz', c_void_p), ('format', FT_Glyph_Format), ('advance', FT_Vector)] FT_Glyph = POINTER(FT_GlyphRec) class FT_Bitmap(Structure): _fields_ = [('rows', c_int), ('width', c_int), ('pitch', c_int), ('buffer', POINTER(c_ubyte)), ('num_grays', c_short), ('pixel_mode', c_ubyte), ('palette_mode', c_char), ('palette', c_void_p)] class FT_BitmapGlyphRec(Structure): _fields_ = [('root', FT_GlyphRec), ('left', FT_Int), ('top', FT_Int), ('bitmap', FT_Bitmap)] FT_BitmapGlyph = POINTER(FT_BitmapGlyphRec) class FT_Glyph_Metrics(Structure): _fields_ = [('width', FT_Pos), ('height', FT_Pos), ('horiBearingX', FT_Pos), ('horiBearingY', FT_Pos), ('horiAdvance', FT_Pos), ('vertBearingX', FT_Pos), ('vertBearingY', FT_Pos), ('vertAdvance', FT_Pos)] class FT_Outline(Structure): _fields_ = [('n_contours', c_short), ('n_points', c_short), ('points', POINTER(FT_Vector)), ('tags', POINTER(c_ubyte)), ('contours', POINTER(c_short)), ('flags', c_int)] class FT_Size_Metrics(Structure): _fields_ = [('x_ppem', FT_UShort), ('y_ppem', FT_UShort), ('x_scale', FT_Fixed), ('y_scale', FT_Fixed), ('ascender', FT_Pos), ('descender', FT_Pos), ('height', FT_Pos), ('max_advance', FT_Pos)] class FT_BBox(Structure): _fields_ = [('xMin', FT_Pos), ('yMin', FT_Pos), ('xMax', FT_Pos), ('yMax', FT_Pos)] class FT_Generic(Structure): _fields_ = [('data', c_void_p), ('finalizer', FT_Generic_Finalizer)] class FT_SizeRec(Structure): _fields_ = [('face', c_void_p), ('generic', FT_Generic), ('metrics', FT_Size_Metrics), ('internal', c_void_p)] FT_Size = POINTER(FT_SizeRec) class FT_CharmapRec(Structure): _fields_ = [('face', c_void_p), ('encoding', FT_Encoding), ('platform_id', FT_UShort), ('encoding_id', FT_UShort)] FT_Charmap = POINTER(FT_CharmapRec) class FT_Bitmap_Size(Structure): _fields_ = [('height', FT_Short), ('width', FT_Short), ('size', FT_Pos), ('x_ppem', FT_Pos), ('y_ppem', FT_Pos)] class FT_GlyphSlotRec(Structure): _fields_ = [('library', FT_Library), ('face', c_void_p), ('next', c_void_p), ('reserved', c_uint), ('generic', FT_Generic), ('metrics', FT_Glyph_Metrics), ('linearHoriAdvance', FT_Fixed), ('linearVertAdvance', FT_Fixed), ('advance', FT_Vector), ('format', FT_Glyph_Format), ('bitmap', FT_Bitmap), ('bitmap_left', FT_Int), ('bitmap_top', FT_Int), ('outline', FT_Outline), ('num_subglyphs', FT_UInt), ('subglyphs', c_void_p), ('control_data', c_void_p), ('control_len', c_long), ('lsb_delta', FT_Pos), ('rsb_delta', FT_Pos), ('other', c_void_p), ('internal', c_void_p)] FT_GlyphSlot = POINTER(FT_GlyphSlotRec) class FT_FaceRec(Structure): _fields_ = [('num_faces', FT_Long), ('face_index', FT_Long), ('face_flags', FT_Long), ('style_flags', FT_Long), ('num_glyphs', FT_Long), ('family_name', FT_String_p), ('style_name', FT_String_p), ('num_fixed_sizes', FT_Int), ('available_sizes', POINTER(FT_Bitmap_Size)), ('num_charmaps', c_int), ('charmaps', POINTER(FT_Charmap)), ('generic', FT_Generic), ('bbox', FT_BBox), ('units_per_EM', FT_UShort), ('ascender', FT_Short), ('descender', FT_Short), ('height', FT_Short), ('max_advance_width', FT_Short), ('max_advance_height', FT_Short), ('underline_position', FT_Short), ('underline_thickness', FT_Short), ('glyph', FT_GlyphSlot), ('size', FT_Size), ('charmap', FT_Charmap), ('driver', c_void_p), ('memory', c_void_p), ('stream', c_void_p), ('sizes_list_head', c_void_p), ('sizes_list_tail', c_void_p), ('autohint', FT_Generic), ('extensions', c_void_p), ('internal', c_void_p)] FT_Face = POINTER(FT_FaceRec) ############################################################################## # __init__.py __dll__ = None FT_Library_filename = util.find_library('freetype') if not FT_Library_filename and sys.platform.startswith('win'): fname_end = '_x64.dll' if _64_bit else '.dll' FT_Library_filename = load_data_file('freetype/freetype253' + fname_end) if not FT_Library_filename: raise ImportError('Freetype library not found') if not __dll__: __dll__ = CDLL(FT_Library_filename) FT_Init_FreeType = __dll__.FT_Init_FreeType FT_Done_FreeType = __dll__.FT_Done_FreeType FT_Library_Version = __dll__.FT_Library_Version __handle__ = None # Comment out to avoid segfaults on Py34 # def __del_library__(self): # global __handle__ # if __handle__: # try: # FT_Done_FreeType(self) # __handle__ = None # except: # pass # FT_Library.__del__ = __del_library__ def get_handle(): ''' Get unique FT_Library handle ''' global __handle__ if not __handle__: __handle__ = FT_Library() error = FT_Init_FreeType(byref(__handle__)) if error: raise RuntimeError(hex(error)) return __handle__ def version(): ''' Return the version of the FreeType library being used as a tuple of ( major version number, minor version number, patch version number ) ''' amajor = FT_Int() aminor = FT_Int() apatch = FT_Int() library = get_handle() FT_Library_Version(library, byref(amajor), byref(aminor), byref(apatch)) return (amajor.value, aminor.value, apatch.value) try: FT_Library_SetLcdFilter = __dll__.FT_Library_SetLcdFilter except: def FT_Library_SetLcdFilter(*args, **kwargs): return 0 if version() >= (2, 4, 0): FT_Library_SetLcdFilterWeights = __dll__.FT_Library_SetLcdFilterWeights FT_New_Face = __dll__.FT_New_Face FT_New_Memory_Face = __dll__.FT_New_Memory_Face FT_Open_Face = __dll__.FT_Open_Face FT_Attach_File = __dll__.FT_Attach_File FT_Attach_Stream = __dll__.FT_Attach_Stream if version() >= (2, 4, 2): FT_Reference_Face = __dll__.FT_Reference_Face FT_Done_Face = __dll__.FT_Done_Face FT_Done_Glyph = __dll__.FT_Done_Glyph FT_Select_Size = __dll__.FT_Select_Size FT_Request_Size = __dll__.FT_Request_Size FT_Set_Char_Size = __dll__.FT_Set_Char_Size FT_Set_Pixel_Sizes = __dll__.FT_Set_Pixel_Sizes FT_Load_Glyph = __dll__.FT_Load_Glyph FT_Load_Char = __dll__.FT_Load_Char FT_Set_Transform = __dll__.FT_Set_Transform FT_Render_Glyph = __dll__.FT_Render_Glyph FT_Get_Kerning = __dll__.FT_Get_Kerning FT_Get_Track_Kerning = __dll__.FT_Get_Track_Kerning FT_Get_Glyph_Name = __dll__.FT_Get_Glyph_Name FT_Get_Glyph = __dll__.FT_Get_Glyph FT_Glyph_Get_CBox = __dll__.FT_Glyph_Get_CBox FT_Get_Postscript_Name = __dll__.FT_Get_Postscript_Name FT_Get_Postscript_Name.restype = c_char_p FT_Select_Charmap = __dll__.FT_Select_Charmap FT_Set_Charmap = __dll__.FT_Set_Charmap FT_Get_Charmap_Index = __dll__.FT_Get_Charmap_Index FT_Get_CMap_Language_ID = __dll__.FT_Get_CMap_Language_ID FT_Get_CMap_Format = __dll__.FT_Get_CMap_Format FT_Get_Char_Index = __dll__.FT_Get_Char_Index FT_Get_First_Char = __dll__.FT_Get_First_Char FT_Get_Next_Char = __dll__.FT_Get_Next_Char FT_Get_Name_Index = __dll__.FT_Get_Name_Index FT_Get_SubGlyph_Info = __dll__.FT_Get_SubGlyph_Info if version() >= (2, 3, 8): FT_Get_FSType_Flags = __dll__.FT_Get_FSType_Flags FT_Get_FSType_Flags.restype = c_ushort FT_Get_X11_Font_Format = __dll__.FT_Get_X11_Font_Format FT_Get_X11_Font_Format.restype = c_char_p FT_Get_Sfnt_Name_Count = __dll__.FT_Get_Sfnt_Name_Count FT_Get_Sfnt_Name = __dll__.FT_Get_Sfnt_Name FT_Get_Advance = __dll__.FT_Get_Advance FT_Outline_GetInsideBorder = __dll__.FT_Outline_GetInsideBorder FT_Outline_GetOutsideBorder = __dll__.FT_Outline_GetOutsideBorder FT_Outline_Get_BBox = __dll__.FT_Outline_Get_BBox FT_Outline_Get_CBox = __dll__.FT_Outline_Get_CBox FT_Stroker_New = __dll__.FT_Stroker_New FT_Stroker_Set = __dll__.FT_Stroker_Set FT_Stroker_Rewind = __dll__.FT_Stroker_Rewind FT_Stroker_ParseOutline = __dll__.FT_Stroker_ParseOutline FT_Stroker_BeginSubPath = __dll__.FT_Stroker_BeginSubPath FT_Stroker_EndSubPath = __dll__.FT_Stroker_EndSubPath FT_Stroker_LineTo = __dll__.FT_Stroker_LineTo FT_Stroker_ConicTo = __dll__.FT_Stroker_ConicTo FT_Stroker_CubicTo = __dll__.FT_Stroker_CubicTo FT_Stroker_GetBorderCounts = __dll__.FT_Stroker_GetBorderCounts FT_Stroker_ExportBorder = __dll__.FT_Stroker_ExportBorder FT_Stroker_GetCounts = __dll__.FT_Stroker_GetCounts FT_Stroker_Export = __dll__.FT_Stroker_Export FT_Stroker_Done = __dll__.FT_Stroker_Done FT_Glyph_Stroke = __dll__.FT_Glyph_Stroke FT_Glyph_StrokeBorder = __dll__.FT_Glyph_StrokeBorder FT_Glyph_To_Bitmap = __dll__.FT_Glyph_To_Bitmap Vector = FT_Vector Matrix = FT_Matrix class Bitmap(object): def __init__(self, bitmap): self._FT_Bitmap = bitmap rows = property(lambda self: self._FT_Bitmap.rows) width = property(lambda self: self._FT_Bitmap.width) pitch = property(lambda self: self._FT_Bitmap.pitch) buffer = property(lambda self: [self._FT_Bitmap.buffer[i] for i in range(self.rows * self.pitch)]) class Glyph(object): def __init__(self, glyph): self._FT_Glyph = glyph def __del__(self): if self._FT_Glyph is not None and FT_Done_Glyph is not None: FT_Done_Glyph(self._FT_Glyph) def to_bitmap(self, mode, origin, destroy=False): error = FT_Glyph_To_Bitmap(byref(self._FT_Glyph), mode, origin, destroy) if error: raise RuntimeError(hex(error)) return BitmapGlyph(self._FT_Glyph) class BitmapGlyph(object): def __init__(self, glyph): self._FT_BitmapGlyph = cast(glyph, FT_BitmapGlyph) bitmap = property(lambda self: Bitmap(self._FT_BitmapGlyph.contents.bitmap)) left = property(lambda self: self._FT_BitmapGlyph.contents.left) top = property(lambda self: self._FT_BitmapGlyph.contents.top) class GlyphSlot(object): def __init__(self, slot): self._FT_GlyphSlot = slot def get_glyph(self): aglyph = FT_Glyph() error = FT_Get_Glyph(self._FT_GlyphSlot, byref(aglyph)) if error: raise RuntimeError(hex(error)) return Glyph(aglyph) bitmap = property(lambda self: Bitmap(self._FT_GlyphSlot.contents.bitmap)) metrics = property(lambda self: self._FT_GlyphSlot.contents.metrics) next = property(lambda self: GlyphSlot(self._FT_GlyphSlot.contents.next)) advance = property(lambda self: self._FT_GlyphSlot.contents.advance) format = property(lambda self: self._FT_GlyphSlot.contents.format) bitmap_top = property(lambda self: self._FT_GlyphSlot.contents.bitmap_top) bitmap_left = property(lambda self: self._FT_GlyphSlot.contents.bitmap_left) class Face(object): def __init__(self, filename, index=0): library = get_handle() face = FT_Face() self._FT_Face = None # error = FT_New_Face( library, filename, 0, byref(face) ) u_filename = c_char_p(filename.encode('utf-8')) error = FT_New_Face(library, u_filename, index, byref(face)) if error: raise RuntimeError(hex(error)) self._filename = filename self._index = index self._FT_Face = face def __del__(self): if self._FT_Face is not None and FT_Done_Face is not None: FT_Done_Face(self._FT_Face) def attach_file(self, filename): error = FT_Attach_File(self._FT_Face, filename) if error: raise RuntimeError(hex(error)) def set_char_size(self, width=0, height=0, hres=72, vres=72): error = FT_Set_Char_Size(self._FT_Face, width, height, hres, vres) if error: raise RuntimeError('Could not set size: %s' % hex(error)) def set_pixel_sizes(self, width, height): error = FT_Set_Pixel_Sizes(self._FT_Face, width, height) if error: raise RuntimeError(hex(error)) def select_charmap(self, encoding): error = FT_Select_Charmap(self._FT_Face, encoding) if error: raise RuntimeError(hex(error)) def set_charmap(self, charmap): error = FT_Set_Charmap(self._FT_Face, charmap._FT_Charmap) if error: raise RuntimeError(hex(error)) def get_char_index(self, charcode): if isinstance(charcode, string_types): charcode = ord(charcode) return FT_Get_Char_Index(self._FT_Face, charcode) def get_first_char(self): agindex = FT_UInt() charcode = FT_Get_First_Char(self._FT_Face, byref(agindex)) return charcode, agindex.value def get_next_char(self, charcode, agindex): agindex = FT_UInt(0) # agindex ) charcode = FT_Get_Next_Char(self._FT_Face, charcode, byref(agindex)) return charcode, agindex.value def get_name_index(self, name): return FT_Get_Name_Index(self._FT_Face, name) def set_transform(self, matrix, delta): FT_Set_Transform(self._FT_Face, byref(matrix), byref(delta)) def select_size(self, strike_index): error = FT_Select_Size(self._FT_Face, strike_index) if error: raise RuntimeError(hex(error)) def load_glyph(self, index, flags=FT_LOAD_RENDER): error = FT_Load_Glyph(self._FT_Face, index, flags) if error: raise RuntimeError(hex(error)) def load_char(self, char, flags=FT_LOAD_RENDER): if len(char) == 1: char = ord(char) error = FT_Load_Char(self._FT_Face, char, flags) if error: raise RuntimeError(hex(error)) def get_advance(self, gindex, flags): padvance = FT_Fixed(0) error = FT_Get_Advance(self._FT_Face, gindex, flags, byref(padvance)) if error: raise RuntimeError(hex(error)) return padvance.value def get_kerning(self, left, right, mode=FT_KERNING_DEFAULT): left_glyph = self.get_char_index(left) right_glyph = self.get_char_index(right) kerning = FT_Vector(0, 0) error = FT_Get_Kerning(self._FT_Face, left_glyph, right_glyph, mode, byref(kerning)) if error: raise RuntimeError(hex(error)) return kerning def get_format(self): return FT_Get_X11_Font_Format(self._FT_Face) sfnt_name_count = property(lambda self: FT_Get_Sfnt_Name_Count(self._FT_Face)) postscript_name = property(lambda self: FT_Get_Postscript_Name(self._FT_Face)) num_faces = property(lambda self: self._FT_Face.contents.num_faces) face_index = property(lambda self: self._FT_Face.contents.face_index) face_flags = property(lambda self: self._FT_Face.contents.face_flags) style_flags = property(lambda self: self._FT_Face.contents.style_flags) num_glyphs = property(lambda self: self._FT_Face.contents.num_glyphs) family_name = property(lambda self: self._FT_Face.contents.family_name) style_name = property(lambda self: self._FT_Face.contents.style_name) num_fixed_sizes = property(lambda self: self._FT_Face.contents.num_fixed_sizes) num_charmaps = property(lambda self: self._FT_Face.contents.num_charmaps) units_per_EM = property(lambda self: self._FT_Face.contents.units_per_EM) ascender = property(lambda self: self._FT_Face.contents.ascender) descender = property(lambda self: self._FT_Face.contents.descender) height = property(lambda self: self._FT_Face.contents.height) max_advance_width = property(lambda self: self._FT_Face.contents.max_advance_width) max_advance_height = property(lambda self: self._FT_Face.contents.max_advance_height) underline_position = property(lambda self: self._FT_Face.contents.underline_position) underline_thickness = property(lambda self: self._FT_Face.contents.underline_thickness) glyph = property(lambda self: GlyphSlot(self._FT_Face.contents.glyph)) ��������������vispy-0.4.0/vispy/ext/mplutils.py�������������������������������������������������������������������0000664�0001750�0001750�00000026363�12502352362�020572� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������""" Utility Routines for Working with Matplotlib Objects ==================================================== """ import itertools import io import base64 import numpy as np import warnings import matplotlib from matplotlib.colors import colorConverter from matplotlib.path import Path from matplotlib.markers import MarkerStyle from matplotlib.transforms import Affine2D from matplotlib import ticker def color_to_hex(color): """Convert matplotlib color code to hex color code""" if color is None or colorConverter.to_rgba(color)[3] == 0: return 'none' else: rgb = colorConverter.to_rgb(color) return '#{0:02X}{1:02X}{2:02X}'.format(*(int(255 * c) for c in rgb)) def _many_to_one(input_dict): """Convert a many-to-one mapping to a one-to-one mapping""" return dict((key, val) for keys, val in input_dict.items() for key in keys) LINESTYLES = _many_to_one({('solid', '-', (None, None)): 'none', ('dashed', '--'): "6,6", ('dotted', ':'): "2,2", ('dashdot', '-.'): "4,4,2,4", ('', ' ', 'None', 'none'): None}) def get_dasharray(obj): """Get an SVG dash array for the given matplotlib linestyle Parameters ---------- obj : matplotlib object The matplotlib line or path object, which must have a get_linestyle() method which returns a valid matplotlib line code Returns ------- dasharray : string The HTML/SVG dasharray code associated with the object. """ if obj.__dict__.get('_dashSeq', None) is not None: return ','.join(map(str, obj._dashSeq)) else: ls = obj.get_linestyle() dasharray = LINESTYLES.get(ls, 'not found') if dasharray == 'not found': warnings.warn("line style '{0}' not understood: " "defaulting to solid line.".format(ls)) dasharray = LINESTYLES['solid'] return dasharray PATH_DICT = {Path.LINETO: 'L', Path.MOVETO: 'M', Path.CURVE3: 'S', Path.CURVE4: 'C', Path.CLOSEPOLY: 'Z'} def SVG_path(path, transform=None, simplify=False): """Construct the vertices and SVG codes for the path Parameters ---------- path : matplotlib.Path object transform : matplotlib transform (optional) if specified, the path will be transformed before computing the output. Returns ------- vertices : array The shape (M, 2) array of vertices of the Path. Note that some Path codes require multiple vertices, so the length of these vertices may be longer than the list of path codes. path_codes : list A length N list of single-character path codes, N <= M. Each code is a single character, in ['L','M','S','C','Z']. See the standard SVG path specification for a description of these. """ if transform is not None: path = path.transformed(transform) vc_tuples = [(vertices if path_code != Path.CLOSEPOLY else [], PATH_DICT[path_code]) for (vertices, path_code) in path.iter_segments(simplify=simplify)] if not vc_tuples: # empty path is a special case return np.zeros((0, 2)), [] else: vertices, codes = zip(*vc_tuples) vertices = np.array(list(itertools.chain(*vertices))).reshape(-1, 2) return vertices, list(codes) def get_path_style(path, fill=True): """Get the style dictionary for matplotlib path objects""" style = {} style['alpha'] = path.get_alpha() if style['alpha'] is None: style['alpha'] = 1 style['edgecolor'] = color_to_hex(path.get_edgecolor()) if fill: style['facecolor'] = color_to_hex(path.get_facecolor()) else: style['facecolor'] = 'none' style['edgewidth'] = path.get_linewidth() style['dasharray'] = get_dasharray(path) style['zorder'] = path.get_zorder() return style def get_line_style(line): """Get the style dictionary for matplotlib line objects""" style = {} style['alpha'] = line.get_alpha() if style['alpha'] is None: style['alpha'] = 1 style['color'] = color_to_hex(line.get_color()) style['linewidth'] = line.get_linewidth() style['dasharray'] = get_dasharray(line) style['zorder'] = line.get_zorder() return style def get_marker_style(line): """Get the style dictionary for matplotlib marker objects""" style = {} style['alpha'] = line.get_alpha() if style['alpha'] is None: style['alpha'] = 1 style['facecolor'] = color_to_hex(line.get_markerfacecolor()) style['edgecolor'] = color_to_hex(line.get_markeredgecolor()) style['edgewidth'] = line.get_markeredgewidth() style['marker'] = line.get_marker() markerstyle = MarkerStyle(line.get_marker()) markersize = line.get_markersize() markertransform = (markerstyle.get_transform() + Affine2D().scale(markersize, -markersize)) style['markerpath'] = SVG_path(markerstyle.get_path(), markertransform) style['markersize'] = markersize style['zorder'] = line.get_zorder() return style def get_text_style(text): """Return the text style dict for a text instance""" style = {} style['alpha'] = text.get_alpha() if style['alpha'] is None: style['alpha'] = 1 style['fontsize'] = text.get_size() style['color'] = color_to_hex(text.get_color()) style['halign'] = text.get_horizontalalignment() # left, center, right style['valign'] = text.get_verticalalignment() # baseline, center, top style['malign'] = text._multialignment # text alignment when '\n' in text style['rotation'] = text.get_rotation() style['zorder'] = text.get_zorder() return style def get_axis_properties(axis): """Return the property dictionary for a matplotlib.Axis instance""" props = {} label1On = axis._major_tick_kw.get('label1On', True) if isinstance(axis, matplotlib.axis.XAxis): if label1On: props['position'] = "bottom" else: props['position'] = "top" elif isinstance(axis, matplotlib.axis.YAxis): if label1On: props['position'] = "left" else: props['position'] = "right" else: raise ValueError("{0} should be an Axis instance".format(axis)) # Use tick values if appropriate locator = axis.get_major_locator() props['nticks'] = len(locator()) if isinstance(locator, ticker.FixedLocator): props['tickvalues'] = list(locator()) else: props['tickvalues'] = None # Find tick formats formatter = axis.get_major_formatter() if isinstance(formatter, ticker.NullFormatter): props['tickformat'] = "" elif isinstance(formatter, ticker.FixedFormatter): props['tickformat'] = list(formatter.seq) elif not any(label.get_visible() for label in axis.get_ticklabels()): props['tickformat'] = "" else: props['tickformat'] = None # Get axis scale props['scale'] = axis.get_scale() # Get major tick label size (assumes that's all we really care about!) labels = axis.get_ticklabels() if labels: props['fontsize'] = labels[0].get_fontsize() else: props['fontsize'] = None # Get associated grid props['grid'] = get_grid_style(axis) return props def get_grid_style(axis): gridlines = axis.get_gridlines() if axis._gridOnMajor and len(gridlines) > 0: color = color_to_hex(gridlines[0].get_color()) alpha = gridlines[0].get_alpha() dasharray = get_dasharray(gridlines[0]) return dict(gridOn=True, color=color, dasharray=dasharray, alpha=alpha) else: return {"gridOn": False} def get_figure_properties(fig): return {'figwidth': fig.get_figwidth(), 'figheight': fig.get_figheight(), 'dpi': fig.dpi} def get_axes_properties(ax): props = {'axesbg': color_to_hex(ax.patch.get_facecolor()), 'axesbgalpha': ax.patch.get_alpha(), 'bounds': ax.get_position().bounds, 'dynamic': ax.get_navigate(), 'axison': ax.axison, 'frame_on': ax.get_frame_on(), 'axes': [get_axis_properties(ax.xaxis), get_axis_properties(ax.yaxis)]} for axname in ['x', 'y']: axis = getattr(ax, axname + 'axis') domain = getattr(ax, 'get_{0}lim'.format(axname))() lim = domain if isinstance(axis.converter, matplotlib.dates.DateConverter): scale = 'date' try: import pandas as pd from pandas.tseries.converter import PeriodConverter except ImportError: pd = None if (pd is not None and isinstance(axis.converter, PeriodConverter)): _dates = [pd.Period(ordinal=int(d), freq=axis.freq) for d in domain] domain = [(d.year, d.month - 1, d.day, d.hour, d.minute, d.second, 0) for d in _dates] else: domain = [(d.year, d.month - 1, d.day, d.hour, d.minute, d.second, d.microsecond * 1E-3) for d in matplotlib.dates.num2date(domain)] else: scale = axis.get_scale() if scale not in ['date', 'linear', 'log']: raise ValueError("Unknown axis scale: " "{0}".format(axis[axname].get_scale())) props[axname + 'scale'] = scale props[axname + 'lim'] = lim props[axname + 'domain'] = domain return props def iter_all_children(obj, skipContainers=False): """ Returns an iterator over all childen and nested children using obj's get_children() method if skipContainers is true, only childless objects are returned. """ if hasattr(obj, 'get_children') and len(obj.get_children()) > 0: for child in obj.get_children(): if not skipContainers: yield child # could use `yield from` in python 3... for grandchild in iter_all_children(child, skipContainers): yield grandchild else: yield obj def get_legend_properties(ax, legend): handles, labels = ax.get_legend_handles_labels() visible = legend.get_visible() return {'handles': handles, 'labels': labels, 'visible': visible} def image_to_base64(image): """ Convert a matplotlib image to a base64 png representation Parameters ---------- image : matplotlib image object The image to be converted. Returns ------- image_base64 : string The UTF8-encoded base64 string representation of the png image. """ ax = image.axes binary_buffer = io.BytesIO() # image is saved in axes coordinates: we need to temporarily # set the correct limits to get the correct image lim = ax.axis() ax.axis(image.get_extent()) image.write_png(binary_buffer) ax.axis(lim) binary_buffer.seek(0) return base64.b64encode(binary_buffer.read()).decode('utf-8') �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/ext/cocoapy.py��������������������������������������������������������������������0000664�0001750�0001750�00000150364�12406355656�020371� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- from ctypes import (cdll, util, Structure, cast, byref, POINTER, CFUNCTYPE, c_int, c_long, c_ulong, c_ushort, c_wchar, c_uint32, c_double, c_uint, c_float, c_void_p, c_char_p, c_bool, c_buffer, c_ubyte, c_byte, c_int8, c_int16, c_int32, c_int64, c_short, c_longlong, c_size_t, sizeof, c_uint8, c_longdouble, c_char, c_ulonglong, py_object, alignment, ArgumentError) import platform import struct import sys if sys.version_info[0] >= 3: string_types = str, else: string_types = basestring, # noqa # Based on Pyglet code ############################################################################## # cocoatypes.py __LP64__ = (8 * struct.calcsize("P") == 64) __i386__ = (platform.machine() == 'i386') PyObjectEncoding = b'{PyObject=@}' def encoding_for_ctype(vartype): typecodes = {c_char: b'c', c_int: b'i', c_short: b's', c_long: b'l', c_longlong: b'q', c_ubyte: b'C', c_uint: b'I', c_ushort: b'S', c_ulong: b'L', c_ulonglong: b'Q', c_float: b'f', c_double: b'd', c_bool: b'B', c_char_p: b'*', c_void_p: b'@', py_object: PyObjectEncoding} return typecodes.get(vartype, b'?') if __LP64__: NSInteger = c_long NSUInteger = c_ulong CGFloat = c_double NSPointEncoding = b'{CGPoint=dd}' NSSizeEncoding = b'{CGSize=dd}' NSRectEncoding = b'{CGRect={CGPoint=dd}{CGSize=dd}}' NSRangeEncoding = b'{_NSRange=QQ}' else: NSInteger = c_int NSUInteger = c_uint CGFloat = c_float NSPointEncoding = b'{_NSPoint=ff}' NSSizeEncoding = b'{_NSSize=ff}' NSRectEncoding = b'{_NSRect={_NSPoint=ff}{_NSSize=ff}}' NSRangeEncoding = b'{_NSRange=II}' NSIntegerEncoding = encoding_for_ctype(NSInteger) NSUIntegerEncoding = encoding_for_ctype(NSUInteger) CGFloatEncoding = encoding_for_ctype(CGFloat) CGImageEncoding = b'{CGImage=}' NSZoneEncoding = b'{_NSZone=}' class NSPoint(Structure): _fields_ = [("x", CGFloat), ("y", CGFloat)] CGPoint = NSPoint class NSSize(Structure): _fields_ = [("width", CGFloat), ("height", CGFloat)] CGSize = NSSize class NSRect(Structure): _fields_ = [("origin", NSPoint), ("size", NSSize)] CGRect = NSRect NSTimeInterval = c_double CFIndex = c_long UniChar = c_ushort unichar = c_wchar CGGlyph = c_ushort class CFRange(Structure): _fields_ = [("location", CFIndex), ("length", CFIndex)] class NSRange(Structure): _fields_ = [("location", NSUInteger), ("length", NSUInteger)] CFTypeID = c_ulong CFNumberType = c_uint32 ############################################################################## # runtime.py __LP64__ = (8*struct.calcsize("P") == 64) __i386__ = (platform.machine() == 'i386') if sizeof(c_void_p) == 4: c_ptrdiff_t = c_int32 elif sizeof(c_void_p) == 8: c_ptrdiff_t = c_int64 objc = cdll.LoadLibrary(util.find_library('objc')) objc.class_addIvar.restype = c_bool objc.class_addIvar.argtypes = [c_void_p, c_char_p, c_size_t, c_uint8, c_char_p] objc.class_addMethod.restype = c_bool objc.class_addProtocol.restype = c_bool objc.class_addProtocol.argtypes = [c_void_p, c_void_p] objc.class_conformsToProtocol.restype = c_bool objc.class_conformsToProtocol.argtypes = [c_void_p, c_void_p] objc.class_copyIvarList.restype = POINTER(c_void_p) objc.class_copyIvarList.argtypes = [c_void_p, POINTER(c_uint)] objc.class_copyMethodList.restype = POINTER(c_void_p) objc.class_copyMethodList.argtypes = [c_void_p, POINTER(c_uint)] objc.class_copyPropertyList.restype = POINTER(c_void_p) objc.class_copyPropertyList.argtypes = [c_void_p, POINTER(c_uint)] objc.class_copyProtocolList.restype = POINTER(c_void_p) objc.class_copyProtocolList.argtypes = [c_void_p, POINTER(c_uint)] objc.class_createInstance.restype = c_void_p objc.class_createInstance.argtypes = [c_void_p, c_size_t] objc.class_getClassMethod.restype = c_void_p objc.class_getClassMethod.argtypes = [c_void_p, c_void_p] objc.class_getClassVariable.restype = c_void_p objc.class_getClassVariable.argtypes = [c_void_p, c_char_p] objc.class_getInstanceMethod.restype = c_void_p objc.class_getInstanceMethod.argtypes = [c_void_p, c_void_p] objc.class_getInstanceSize.restype = c_size_t objc.class_getInstanceSize.argtypes = [c_void_p] objc.class_getInstanceVariable.restype = c_void_p objc.class_getInstanceVariable.argtypes = [c_void_p, c_char_p] objc.class_getIvarLayout.restype = c_char_p objc.class_getIvarLayout.argtypes = [c_void_p] objc.class_getMethodImplementation.restype = c_void_p objc.class_getMethodImplementation.argtypes = [c_void_p, c_void_p] objc.class_getMethodImplementation_stret.restype = c_void_p objc.class_getMethodImplementation_stret.argtypes = [c_void_p, c_void_p] objc.class_getName.restype = c_char_p objc.class_getName.argtypes = [c_void_p] objc.class_getProperty.restype = c_void_p objc.class_getProperty.argtypes = [c_void_p, c_char_p] objc.class_getSuperclass.restype = c_void_p objc.class_getSuperclass.argtypes = [c_void_p] objc.class_getVersion.restype = c_int objc.class_getVersion.argtypes = [c_void_p] objc.class_getWeakIvarLayout.restype = c_char_p objc.class_getWeakIvarLayout.argtypes = [c_void_p] objc.class_isMetaClass.restype = c_bool objc.class_isMetaClass.argtypes = [c_void_p] objc.class_replaceMethod.restype = c_void_p objc.class_replaceMethod.argtypes = [c_void_p, c_void_p, c_void_p, c_char_p] objc.class_respondsToSelector.restype = c_bool objc.class_respondsToSelector.argtypes = [c_void_p, c_void_p] objc.class_setIvarLayout.restype = None objc.class_setIvarLayout.argtypes = [c_void_p, c_char_p] objc.class_setSuperclass.restype = c_void_p objc.class_setSuperclass.argtypes = [c_void_p, c_void_p] objc.class_setVersion.restype = None objc.class_setVersion.argtypes = [c_void_p, c_int] objc.class_setWeakIvarLayout.restype = None objc.class_setWeakIvarLayout.argtypes = [c_void_p, c_char_p] objc.ivar_getName.restype = c_char_p objc.ivar_getName.argtypes = [c_void_p] objc.ivar_getOffset.restype = c_ptrdiff_t objc.ivar_getOffset.argtypes = [c_void_p] objc.ivar_getTypeEncoding.restype = c_char_p objc.ivar_getTypeEncoding.argtypes = [c_void_p] objc.method_copyArgumentType.restype = c_char_p objc.method_copyArgumentType.argtypes = [c_void_p, c_uint] objc.method_copyReturnType.restype = c_char_p objc.method_copyReturnType.argtypes = [c_void_p] objc.method_exchangeImplementations.restype = None objc.method_exchangeImplementations.argtypes = [c_void_p, c_void_p] objc.method_getArgumentType.restype = None objc.method_getArgumentType.argtypes = [c_void_p, c_uint, c_char_p, c_size_t] objc.method_getImplementation.restype = c_void_p objc.method_getImplementation.argtypes = [c_void_p] objc.method_getName.restype = c_void_p objc.method_getName.argtypes = [c_void_p] objc.method_getNumberOfArguments.restype = c_uint objc.method_getNumberOfArguments.argtypes = [c_void_p] objc.method_getReturnType.restype = None objc.method_getReturnType.argtypes = [c_void_p, c_char_p, c_size_t] objc.method_getTypeEncoding.restype = c_char_p objc.method_getTypeEncoding.argtypes = [c_void_p] objc.method_setImplementation.restype = c_void_p objc.method_setImplementation.argtypes = [c_void_p, c_void_p] objc.objc_allocateClassPair.restype = c_void_p objc.objc_allocateClassPair.argtypes = [c_void_p, c_char_p, c_size_t] objc.objc_copyProtocolList.restype = POINTER(c_void_p) objc.objc_copyProtocolList.argtypes = [POINTER(c_int)] objc.objc_getAssociatedObject.restype = c_void_p objc.objc_getAssociatedObject.argtypes = [c_void_p, c_void_p] objc.objc_getClass.restype = c_void_p objc.objc_getClass.argtypes = [c_char_p] objc.objc_getClassList.restype = c_int objc.objc_getClassList.argtypes = [c_void_p, c_int] objc.objc_getMetaClass.restype = c_void_p objc.objc_getMetaClass.argtypes = [c_char_p] objc.objc_getProtocol.restype = c_void_p objc.objc_getProtocol.argtypes = [c_char_p] objc.objc_msgSendSuper_stret.restype = None objc.objc_msgSend_stret.restype = None objc.objc_registerClassPair.restype = None objc.objc_registerClassPair.argtypes = [c_void_p] objc.objc_removeAssociatedObjects.restype = None objc.objc_removeAssociatedObjects.argtypes = [c_void_p] objc.objc_setAssociatedObject.restype = None objc.objc_setAssociatedObject.argtypes = [c_void_p, c_void_p, c_void_p, c_int] objc.object_copy.restype = c_void_p objc.object_copy.argtypes = [c_void_p, c_size_t] objc.object_dispose.restype = c_void_p objc.object_dispose.argtypes = [c_void_p] objc.object_getClass.restype = c_void_p objc.object_getClass.argtypes = [c_void_p] objc.object_getClassName.restype = c_char_p objc.object_getClassName.argtypes = [c_void_p] objc.object_getInstanceVariable.restype = c_void_p objc.object_getInstanceVariable.argtypes = [c_void_p, c_char_p, c_void_p] objc.object_getIvar.restype = c_void_p objc.object_getIvar.argtypes = [c_void_p, c_void_p] objc.object_setClass.restype = c_void_p objc.object_setClass.argtypes = [c_void_p, c_void_p] objc.object_setInstanceVariable.restype = c_void_p objc.object_setIvar.restype = None objc.object_setIvar.argtypes = [c_void_p, c_void_p, c_void_p] objc.property_getAttributes.restype = c_char_p objc.property_getAttributes.argtypes = [c_void_p] objc.property_getName.restype = c_char_p objc.property_getName.argtypes = [c_void_p] objc.protocol_conformsToProtocol.restype = c_bool objc.protocol_conformsToProtocol.argtypes = [c_void_p, c_void_p] class OBJC_METHOD_DESCRIPTION(Structure): _fields_ = [("name", c_void_p), ("types", c_char_p)] objc.protocol_copyMethodDescriptionList.restype = \ POINTER(OBJC_METHOD_DESCRIPTION) objc.protocol_copyMethodDescriptionList.argtypes = [c_void_p, c_bool, c_bool, POINTER(c_uint)] objc.protocol_copyPropertyList.restype = c_void_p objc.protocol_copyPropertyList.argtypes = [c_void_p, POINTER(c_uint)] objc.protocol_copyProtocolList = POINTER(c_void_p) objc.protocol_copyProtocolList.argtypes = [c_void_p, POINTER(c_uint)] objc.protocol_getMethodDescription.restype = OBJC_METHOD_DESCRIPTION objc.protocol_getMethodDescription.argtypes = [c_void_p, c_void_p, c_bool, c_bool] objc.protocol_getName.restype = c_char_p objc.protocol_getName.argtypes = [c_void_p] objc.sel_getName.restype = c_char_p objc.sel_getName.argtypes = [c_void_p] objc.sel_isEqual.restype = c_bool objc.sel_isEqual.argtypes = [c_void_p, c_void_p] objc.sel_registerName.restype = c_void_p objc.sel_registerName.argtypes = [c_char_p] def ensure_bytes(x): if isinstance(x, bytes): return x return x.encode('ascii') def get_selector(name): return c_void_p(objc.sel_registerName(ensure_bytes(name))) def get_class(name): return c_void_p(objc.objc_getClass(ensure_bytes(name))) def get_object_class(obj): return c_void_p(objc.object_getClass(obj)) def get_metaclass(name): return c_void_p(objc.objc_getMetaClass(ensure_bytes(name))) def get_superclass_of_object(obj): cls = c_void_p(objc.object_getClass(obj)) return c_void_p(objc.class_getSuperclass(cls)) def x86_should_use_stret(restype): if type(restype) != type(Structure): return False if not __LP64__ and sizeof(restype) <= 8: return False if __LP64__ and sizeof(restype) <= 16: # maybe? I don't know? return False return True def should_use_fpret(restype): if not __i386__: return False if __LP64__ and restype == c_longdouble: return True if not __LP64__ and restype in (c_float, c_double, c_longdouble): return True return False def send_message(receiver, selName, *args, **kwargs): if isinstance(receiver, string_types): receiver = get_class(receiver) selector = get_selector(selName) restype = kwargs.get('restype', c_void_p) argtypes = kwargs.get('argtypes', []) if should_use_fpret(restype): objc.objc_msgSend_fpret.restype = restype objc.objc_msgSend_fpret.argtypes = [c_void_p, c_void_p] + argtypes result = objc.objc_msgSend_fpret(receiver, selector, *args) elif x86_should_use_stret(restype): objc.objc_msgSend_stret.argtypes = [POINTER(restype), c_void_p, c_void_p] + argtypes result = restype() objc.objc_msgSend_stret(byref(result), receiver, selector, *args) else: objc.objc_msgSend.restype = restype objc.objc_msgSend.argtypes = [c_void_p, c_void_p] + argtypes result = objc.objc_msgSend(receiver, selector, *args) if restype == c_void_p: result = c_void_p(result) return result class OBJC_SUPER(Structure): _fields_ = [('receiver', c_void_p), ('class', c_void_p)] OBJC_SUPER_PTR = POINTER(OBJC_SUPER) def send_super(receiver, selName, *args, **kwargs): if hasattr(receiver, '_as_parameter_'): receiver = receiver._as_parameter_ superclass = get_superclass_of_object(receiver) super_struct = OBJC_SUPER(receiver, superclass) selector = get_selector(selName) restype = kwargs.get('restype', c_void_p) argtypes = kwargs.get('argtypes', None) objc.objc_msgSendSuper.restype = restype if argtypes: objc.objc_msgSendSuper.argtypes = [OBJC_SUPER_PTR, c_void_p] + argtypes else: objc.objc_msgSendSuper.argtypes = None result = objc.objc_msgSendSuper(byref(super_struct), selector, *args) if restype == c_void_p: result = c_void_p(result) return result cfunctype_table = {} def parse_type_encoding(encoding): type_encodings = [] brace_count = 0 # number of unclosed curly braces bracket_count = 0 # number of unclosed square brackets typecode = b'' for c in encoding: if isinstance(c, int): c = bytes([c]) if c == b'{': if typecode and typecode[-1:] != b'^' and brace_count == 0 and \ bracket_count == 0: type_encodings.append(typecode) typecode = b'' typecode += c brace_count += 1 elif c == b'}': typecode += c brace_count -= 1 assert(brace_count >= 0) elif c == b'[': if typecode and typecode[-1:] != b'^' and brace_count == 0 and \ bracket_count == 0: type_encodings.append(typecode) typecode = b'' typecode += c bracket_count += 1 elif c == b']': typecode += c bracket_count -= 1 assert(bracket_count >= 0) elif brace_count or bracket_count: typecode += c elif c in b'0123456789': pass elif c in b'rnNoORV': pass elif c in b'^cislqCISLQfdBv*@#:b?': if typecode and typecode[-1:] == b'^': typecode += c else: if typecode: type_encodings.append(typecode) typecode = c if typecode: type_encodings.append(typecode) return type_encodings def cfunctype_for_encoding(encoding): if encoding in cfunctype_table: return cfunctype_table[encoding] typecodes = {b'c': c_char, b'i': c_int, b's': c_short, b'l': c_long, b'q': c_longlong, b'C': c_ubyte, b'I': c_uint, b'S': c_ushort, b'L': c_ulong, b'Q': c_ulonglong, b'f': c_float, b'd': c_double, b'B': c_bool, b'v': None, b'*': c_char_p, b'@': c_void_p, b'#': c_void_p, b':': c_void_p, NSPointEncoding: NSPoint, NSSizeEncoding: NSSize, NSRectEncoding: NSRect, NSRangeEncoding: NSRange, PyObjectEncoding: py_object} argtypes = [] for code in parse_type_encoding(encoding): if code in typecodes: argtypes.append(typecodes[code]) elif code[0:1] == b'^' and code[1:] in typecodes: argtypes.append(POINTER(typecodes[code[1:]])) else: raise Exception('unknown type encoding: ' + code) cfunctype = CFUNCTYPE(*argtypes) cfunctype_table[encoding] = cfunctype return cfunctype def create_subclass(superclass, name): if isinstance(superclass, string_types): superclass = get_class(superclass) return c_void_p(objc.objc_allocateClassPair(superclass, ensure_bytes(name), 0)) def register_subclass(subclass): objc.objc_registerClassPair(subclass) def add_method(cls, selName, method, types): type_encodings = parse_type_encoding(types) assert(type_encodings[1] == b'@') # ensure id self typecode assert(type_encodings[2] == b':') # ensure SEL cmd typecode selector = get_selector(selName) cfunctype = cfunctype_for_encoding(types) imp = cfunctype(method) objc.class_addMethod.argtypes = [c_void_p, c_void_p, cfunctype, c_char_p] objc.class_addMethod(cls, selector, imp, types) return imp def add_ivar(cls, name, vartype): return objc.class_addIvar(cls, ensure_bytes(name), sizeof(vartype), alignment(vartype), encoding_for_ctype(vartype)) def set_instance_variable(obj, varname, value, vartype): objc.object_setInstanceVariable.argtypes = [c_void_p, c_char_p, vartype] objc.object_setInstanceVariable(obj, ensure_bytes(varname), value) def get_instance_variable(obj, varname, vartype): variable = vartype() objc.object_getInstanceVariable(obj, ensure_bytes(varname), byref(variable)) return variable.value class ObjCMethod(object): """This represents an unbound Objective-C method (really an IMP).""" typecodes = {b'c': c_byte, b'i': c_int, b's': c_short, b'l': c_long, b'q': c_longlong, b'C': c_ubyte, b'I': c_uint, b'S': c_ushort, b'L': c_ulong, b'Q': c_ulonglong, b'f': c_float, b'd': c_double, b'B': c_bool, b'v': None, b'Vv': None, b'*': c_char_p, b'@': c_void_p, b'#': c_void_p, b':': c_void_p, b'^v': c_void_p, b'?': c_void_p, NSPointEncoding: NSPoint, NSSizeEncoding: NSSize, NSRectEncoding: NSRect, NSRangeEncoding: NSRange, PyObjectEncoding: py_object} cfunctype_table = {} def __init__(self, method): self.selector = c_void_p(objc.method_getName(method)) self.name = objc.sel_getName(self.selector) self.pyname = self.name.replace(b':', b'_') self.encoding = objc.method_getTypeEncoding(method) self.return_type = objc.method_copyReturnType(method) self.nargs = objc.method_getNumberOfArguments(method) self.imp = c_void_p(objc.method_getImplementation(method)) self.argument_types = [] for i in range(self.nargs): buffer = c_buffer(512) objc.method_getArgumentType(method, i, buffer, len(buffer)) self.argument_types.append(buffer.value) try: self.argtypes = [self.ctype_for_encoding(t) for t in self.argument_types] except: self.argtypes = None try: if self.return_type == b'@': self.restype = ObjCInstance elif self.return_type == b'#': self.restype = ObjCClass else: self.restype = self.ctype_for_encoding(self.return_type) except: self.restype = None self.func = None def ctype_for_encoding(self, encoding): """Return ctypes type for an encoded Objective-C type.""" if encoding in self.typecodes: return self.typecodes[encoding] elif encoding[0:1] == b'^' and encoding[1:] in self.typecodes: return POINTER(self.typecodes[encoding[1:]]) elif encoding[0:1] == b'^' and encoding[1:] in [CGImageEncoding, NSZoneEncoding]: return c_void_p elif encoding[0:1] == b'r' and encoding[1:] in self.typecodes: return self.typecodes[encoding[1:]] elif encoding[0:2] == b'r^' and encoding[2:] in self.typecodes: return POINTER(self.typecodes[encoding[2:]]) else: raise Exception('unknown encoding for %s: %s' % (self.name, encoding)) def get_prototype(self): if self.restype == ObjCInstance or self.restype == ObjCClass: self.prototype = CFUNCTYPE(c_void_p, *self.argtypes) else: self.prototype = CFUNCTYPE(self.restype, *self.argtypes) return self.prototype def __repr__(self): return "" % (self.name, self.encoding) def get_callable(self): if not self.func: prototype = self.get_prototype() self.func = cast(self.imp, prototype) if self.restype == ObjCInstance or self.restype == ObjCClass: self.func.restype = c_void_p else: self.func.restype = self.restype self.func.argtypes = self.argtypes return self.func def __call__(self, objc_id, *args): f = self.get_callable() try: result = f(objc_id, self.selector, *args) if self.restype == ObjCInstance: result = ObjCInstance(result) elif self.restype == ObjCClass: result = ObjCClass(result) return result except ArgumentError as error: error.args += ('selector = ' + self.name, 'argtypes =' + str(self.argtypes), 'encoding = ' + self.encoding) raise class ObjCBoundMethod(object): def __init__(self, method, objc_id): self.method = method self.objc_id = objc_id def __repr__(self): return '' % (self.method.name, self.objc_id) def __call__(self, *args): return self.method(self.objc_id, *args) class ObjCClass(object): _registered_classes = {} def __new__(cls, class_name_or_ptr): if isinstance(class_name_or_ptr, string_types): name = class_name_or_ptr ptr = get_class(name) else: ptr = class_name_or_ptr if not isinstance(ptr, c_void_p): ptr = c_void_p(ptr) name = objc.class_getName(ptr) if name in cls._registered_classes: return cls._registered_classes[name] objc_class = super(ObjCClass, cls).__new__(cls) objc_class.ptr = ptr objc_class.name = name objc_class.instance_methods = {} # mapping of name -> instance method objc_class.class_methods = {} # mapping of name -> class method objc_class._as_parameter_ = ptr # for ctypes argument passing cls._registered_classes[name] = objc_class objc_class.cache_instance_methods() objc_class.cache_class_methods() return objc_class def __repr__(self): return "" % (self.name, str(self.ptr.value)) def cache_instance_methods(self): count = c_uint() method_array = objc.class_copyMethodList(self.ptr, byref(count)) for i in range(count.value): method = c_void_p(method_array[i]) objc_method = ObjCMethod(method) self.instance_methods[objc_method.pyname] = objc_method def cache_class_methods(self): count = c_uint() args = [objc.object_getClass(self.ptr), byref(count)] method_array = objc.class_copyMethodList(*args) for i in range(count.value): method = c_void_p(method_array[i]) objc_method = ObjCMethod(method) self.class_methods[objc_method.pyname] = objc_method def get_instance_method(self, name): if name in self.instance_methods: return self.instance_methods[name] else: selector = get_selector(name.replace(b'_', b':')) method = c_void_p(objc.class_getInstanceMethod(self.ptr, selector)) if method.value: objc_method = ObjCMethod(method) self.instance_methods[name] = objc_method return objc_method return None def get_class_method(self, name): if name in self.class_methods: return self.class_methods[name] else: selector = get_selector(name.replace(b'_', b':')) method = c_void_p(objc.class_getClassMethod(self.ptr, selector)) if method.value: objc_method = ObjCMethod(method) self.class_methods[name] = objc_method return objc_method return None def __getattr__(self, name): name = ensure_bytes(name) method = self.get_class_method(name) if method: return ObjCBoundMethod(method, self.ptr) method = self.get_instance_method(name) if method: return method raise AttributeError('ObjCClass %s has no attribute %s' % (self.name, name)) class ObjCInstance(object): _cached_objects = {} def __new__(cls, object_ptr): if not isinstance(object_ptr, c_void_p): object_ptr = c_void_p(object_ptr) if not object_ptr.value: return None if object_ptr.value in cls._cached_objects: return cls._cached_objects[object_ptr.value] objc_instance = super(ObjCInstance, cls).__new__(cls) objc_instance.ptr = object_ptr objc_instance._as_parameter_ = object_ptr class_ptr = c_void_p(objc.object_getClass(object_ptr)) objc_instance.objc_class = ObjCClass(class_ptr) cls._cached_objects[object_ptr.value] = objc_instance observer = send_message(send_message('DeallocationObserver', 'alloc'), 'initWithObject:', objc_instance) objc.objc_setAssociatedObject(objc_instance, observer, observer, 0x301) send_message(observer, 'release') return objc_instance def __repr__(self): if self.objc_class.name == b'NSCFString': from .cocoalibs import cfstring_to_string string = cfstring_to_string(self) return ("" % (id(self), self.objc_class.name, string, str(self.ptr.value))) return ("" % (id(self), self.objc_class.name, str(self.ptr.value))) def __getattr__(self, name): name = ensure_bytes(name) method = self.objc_class.get_instance_method(name) if method: return ObjCBoundMethod(method, self) method = self.objc_class.get_class_method(name) if method: return ObjCBoundMethod(method, self.objc_class.ptr) keys = list(self.objc_class.instance_methods.keys()) raise AttributeError('ObjCInstance %s has no attribute %s, only:\n%s' % (self.objc_class.name, name, keys)) def convert_method_arguments(encoding, args): new_args = [] arg_encodings = parse_type_encoding(encoding)[3:] for e, a in zip(arg_encodings, args): if e == b'@': new_args.append(ObjCInstance(a)) elif e == b'#': new_args.append(ObjCClass(a)) else: new_args.append(a) return new_args class ObjCSubclass(object): def __init__(self, superclass, name, register=True): self._imp_table = {} self.name = name self.objc_cls = create_subclass(superclass, name) self._as_parameter_ = self.objc_cls if register: self.register() def register(self): objc.objc_registerClassPair(self.objc_cls) self.objc_metaclass = get_metaclass(self.name) def add_ivar(self, varname, vartype): return add_ivar(self.objc_cls, varname, vartype) def add_method(self, method, name, encoding): imp = add_method(self.objc_cls, name, method, encoding) self._imp_table[name] = imp def add_class_method(self, method, name, encoding): imp = add_method(self.objc_metaclass, name, method, encoding) self._imp_table[name] = imp def rawmethod(self, encoding): encoding = ensure_bytes(encoding) typecodes = parse_type_encoding(encoding) typecodes.insert(1, b'@:') encoding = b''.join(typecodes) def decorator(f): name = f.__name__.replace('_', ':') self.add_method(f, name, encoding) return f return decorator def method(self, encoding): encoding = ensure_bytes(encoding) typecodes = parse_type_encoding(encoding) typecodes.insert(1, b'@:') encoding = b''.join(typecodes) def decorator(f): def objc_method(objc_self, objc_cmd, *args): py_self = ObjCInstance(objc_self) py_self.objc_cmd = objc_cmd args = convert_method_arguments(encoding, args) result = f(py_self, *args) if isinstance(result, ObjCClass): result = result.ptr.value elif isinstance(result, ObjCInstance): result = result.ptr.value return result name = f.__name__.replace('_', ':') self.add_method(objc_method, name, encoding) return objc_method return decorator def classmethod(self, encoding): """Function decorator for class methods.""" # Add encodings for hidden self and cmd arguments. encoding = ensure_bytes(encoding) typecodes = parse_type_encoding(encoding) typecodes.insert(1, b'@:') encoding = b''.join(typecodes) def decorator(f): def objc_class_method(objc_cls, objc_cmd, *args): py_cls = ObjCClass(objc_cls) py_cls.objc_cmd = objc_cmd args = convert_method_arguments(encoding, args) result = f(py_cls, *args) if isinstance(result, ObjCClass): result = result.ptr.value elif isinstance(result, ObjCInstance): result = result.ptr.value return result name = f.__name__.replace('_', ':') self.add_class_method(objc_class_method, name, encoding) return objc_class_method return decorator # XXX This causes segfaults in all backends (yikes!), and makes it so that # pyglet can't even be loaded. We'll just have to live with leaks for now, # which is probably alright since we only use the # NSFontManager.sharedFontManager class currently. # class DeallocationObserver_Implementation(object): # DeallocationObserver = ObjCSubclass('NSObject', 'DeallocationObserver', # register=False) # DeallocationObserver.add_ivar('observed_object', c_void_p) # DeallocationObserver.register() # # @DeallocationObserver.rawmethod('@@') # def initWithObject_(self, cmd, anObject): # self = send_super(self, 'init') # self = self.value # set_instance_variable(self, 'observed_object', anObject, c_void_p) # return self # # @DeallocationObserver.rawmethod('v') # def dealloc(self, cmd): # anObject = get_instance_variable(self, 'observed_object', c_void_p) # ObjCInstance._cached_objects.pop(anObject, None) # send_super(self, 'dealloc') # # @DeallocationObserver.rawmethod('v') # def finalize(self, cmd): # anObject = get_instance_variable(self, 'observed_object', c_void_p) # ObjCInstance._cached_objects.pop(anObject, None) # send_super(self, 'finalize') ############################################################################## # cocoalibs.py cf = cdll.LoadLibrary(util.find_library('CoreFoundation')) kCFStringEncodingUTF8 = 0x08000100 CFAllocatorRef = c_void_p CFStringEncoding = c_uint32 cf.CFStringCreateWithCString.restype = c_void_p cf.CFStringCreateWithCString.argtypes = [CFAllocatorRef, c_void_p, CFStringEncoding] cf.CFRelease.restype = c_void_p cf.CFRelease.argtypes = [c_void_p] cf.CFStringGetLength.restype = CFIndex cf.CFStringGetLength.argtypes = [c_void_p] cf.CFStringGetMaximumSizeForEncoding.restype = CFIndex cf.CFStringGetMaximumSizeForEncoding.argtypes = [CFIndex, CFStringEncoding] cf.CFStringGetCString.restype = c_bool cf.CFStringGetCString.argtypes = [c_void_p, c_char_p, CFIndex, CFStringEncoding] cf.CFStringGetTypeID.restype = CFTypeID cf.CFStringGetTypeID.argtypes = [] cf.CFAttributedStringCreate.restype = c_void_p cf.CFAttributedStringCreate.argtypes = [CFAllocatorRef, c_void_p, c_void_p] cf.CFURLCreateWithFileSystemPath.restype = c_void_p cf.CFURLCreateWithFileSystemPath.argtypes = [CFAllocatorRef, c_void_p, CFIndex, c_bool] def CFSTR(string): args = [None, string.encode('utf8'), kCFStringEncodingUTF8] return ObjCInstance(c_void_p(cf.CFStringCreateWithCString(*args))) def get_NSString(string): """Autoreleased version of CFSTR""" return CFSTR(string).autorelease() def cfstring_to_string(cfstring): length = cf.CFStringGetLength(cfstring) size = cf.CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) buffer = c_buffer(size + 1) result = cf.CFStringGetCString(cfstring, buffer, len(buffer), kCFStringEncodingUTF8) if result: return buffer.value.decode('utf8') cf.CFDataCreate.restype = c_void_p cf.CFDataCreate.argtypes = [c_void_p, c_void_p, CFIndex] cf.CFDataGetBytes.restype = None cf.CFDataGetBytes.argtypes = [c_void_p, CFRange, c_void_p] cf.CFDataGetLength.restype = CFIndex cf.CFDataGetLength.argtypes = [c_void_p] cf.CFDictionaryGetValue.restype = c_void_p cf.CFDictionaryGetValue.argtypes = [c_void_p, c_void_p] cf.CFDictionaryAddValue.restype = None cf.CFDictionaryAddValue.argtypes = [c_void_p, c_void_p, c_void_p] cf.CFDictionaryCreateMutable.restype = c_void_p cf.CFDictionaryCreateMutable.argtypes = [CFAllocatorRef, CFIndex, c_void_p, c_void_p] cf.CFNumberCreate.restype = c_void_p cf.CFNumberCreate.argtypes = [CFAllocatorRef, CFNumberType, c_void_p] cf.CFNumberGetType.restype = CFNumberType cf.CFNumberGetType.argtypes = [c_void_p] cf.CFNumberGetValue.restype = c_ubyte cf.CFNumberGetValue.argtypes = [c_void_p, CFNumberType, c_void_p] cf.CFNumberGetTypeID.restype = CFTypeID cf.CFNumberGetTypeID.argtypes = [] cf.CFGetTypeID.restype = CFTypeID cf.CFGetTypeID.argtypes = [c_void_p] # CFNumber.h kCFNumberSInt8Type = 1 kCFNumberSInt16Type = 2 kCFNumberSInt32Type = 3 kCFNumberSInt64Type = 4 kCFNumberFloat32Type = 5 kCFNumberFloat64Type = 6 kCFNumberCharType = 7 kCFNumberShortType = 8 kCFNumberIntType = 9 kCFNumberLongType = 10 kCFNumberLongLongType = 11 kCFNumberFloatType = 12 kCFNumberDoubleType = 13 kCFNumberCFIndexType = 14 kCFNumberNSIntegerType = 15 kCFNumberCGFloatType = 16 kCFNumberMaxType = 16 def cfnumber_to_number(cfnumber): """Convert CFNumber to python int or float.""" numeric_type = cf.CFNumberGetType(cfnumber) cfnum_to_ctype = {kCFNumberSInt8Type: c_int8, kCFNumberSInt16Type: c_int16, kCFNumberSInt32Type: c_int32, kCFNumberSInt64Type: c_int64, kCFNumberFloat32Type: c_float, kCFNumberFloat64Type: c_double, kCFNumberCharType: c_byte, kCFNumberShortType: c_short, kCFNumberIntType: c_int, kCFNumberLongType: c_long, kCFNumberLongLongType: c_longlong, kCFNumberFloatType: c_float, kCFNumberDoubleType: c_double, kCFNumberCFIndexType: CFIndex, kCFNumberCGFloatType: CGFloat} if numeric_type in cfnum_to_ctype: t = cfnum_to_ctype[numeric_type] result = t() if cf.CFNumberGetValue(cfnumber, numeric_type, byref(result)): return result.value else: raise Exception( 'cfnumber_to_number: unhandled CFNumber type %d' % numeric_type) # Dictionary of cftypes matched to the method converting them to python values. known_cftypes = {cf.CFStringGetTypeID(): cfstring_to_string, cf.CFNumberGetTypeID(): cfnumber_to_number} def cftype_to_value(cftype): """Convert a CFType into an equivalent python type. The convertible CFTypes are taken from the known_cftypes dictionary, which may be added to if another library implements its own conversion methods.""" if not cftype: return None typeID = cf.CFGetTypeID(cftype) if typeID in known_cftypes: convert_function = known_cftypes[typeID] return convert_function(cftype) else: return cftype cf.CFSetGetCount.restype = CFIndex cf.CFSetGetCount.argtypes = [c_void_p] cf.CFSetGetValues.restype = None # PyPy 1.7 is fine with 2nd arg as POINTER(c_void_p), # but CPython ctypes 1.1.0 complains, so just use c_void_p. cf.CFSetGetValues.argtypes = [c_void_p, c_void_p] def cfset_to_set(cfset): """Convert CFSet to python set.""" count = cf.CFSetGetCount(cfset) buffer = (c_void_p * count)() cf.CFSetGetValues(cfset, byref(buffer)) return set([cftype_to_value(c_void_p(buffer[i])) for i in range(count)]) cf.CFArrayGetCount.restype = CFIndex cf.CFArrayGetCount.argtypes = [c_void_p] cf.CFArrayGetValueAtIndex.restype = c_void_p cf.CFArrayGetValueAtIndex.argtypes = [c_void_p, CFIndex] def cfarray_to_list(cfarray): """Convert CFArray to python list.""" count = cf.CFArrayGetCount(cfarray) return [cftype_to_value(c_void_p(cf.CFArrayGetValueAtIndex(cfarray, i))) for i in range(count)] kCFRunLoopDefaultMode = c_void_p.in_dll(cf, 'kCFRunLoopDefaultMode') cf.CFRunLoopGetCurrent.restype = c_void_p cf.CFRunLoopGetCurrent.argtypes = [] cf.CFRunLoopGetMain.restype = c_void_p cf.CFRunLoopGetMain.argtypes = [] cf.CFShow.restype = None cf.CFShow.argtypes = [c_void_p] ###################################################################### # APPLICATION KIT # Even though we don't use this directly, it must be loaded so that # we can find the NSApplication, NSWindow, and NSView classes. appkit = cdll.LoadLibrary(util.find_library('AppKit')) NSDefaultRunLoopMode = c_void_p.in_dll(appkit, 'NSDefaultRunLoopMode') NSEventTrackingRunLoopMode = c_void_p.in_dll( appkit, 'NSEventTrackingRunLoopMode') NSApplicationDidHideNotification = c_void_p.in_dll( appkit, 'NSApplicationDidHideNotification') NSApplicationDidUnhideNotification = c_void_p.in_dll( appkit, 'NSApplicationDidUnhideNotification') # /System/Library/Frameworks/AppKit.framework/Headers/NSEvent.h # NSAnyEventMask = 0xFFFFFFFFL # NSUIntegerMax # Commented out b/c not Py3k compatible NSKeyDown = 10 NSKeyUp = 11 NSFlagsChanged = 12 NSApplicationDefined = 15 NSAlphaShiftKeyMask = 1 << 16 NSShiftKeyMask = 1 << 17 NSControlKeyMask = 1 << 18 NSAlternateKeyMask = 1 << 19 NSCommandKeyMask = 1 << 20 NSNumericPadKeyMask = 1 << 21 NSHelpKeyMask = 1 << 22 NSFunctionKeyMask = 1 << 23 NSInsertFunctionKey = 0xF727 NSDeleteFunctionKey = 0xF728 NSHomeFunctionKey = 0xF729 NSBeginFunctionKey = 0xF72A NSEndFunctionKey = 0xF72B NSPageUpFunctionKey = 0xF72C NSPageDownFunctionKey = 0xF72D # /System/Library/Frameworks/AppKit.framework/Headers/NSWindow.h NSBorderlessWindowMask = 0 NSTitledWindowMask = 1 << 0 NSClosableWindowMask = 1 << 1 NSMiniaturizableWindowMask = 1 << 2 NSResizableWindowMask = 1 << 3 # /System/Library/Frameworks/AppKit.framework/Headers/NSPanel.h NSUtilityWindowMask = 1 << 4 # /System/Library/Frameworks/AppKit.framework/Headers/NSGraphics.h NSBackingStoreRetained = 0 NSBackingStoreNonretained = 1 NSBackingStoreBuffered = 2 # /System/Library/Frameworks/AppKit.framework/Headers/NSTrackingArea.h NSTrackingMouseEnteredAndExited = 0x01 NSTrackingMouseMoved = 0x02 NSTrackingCursorUpdate = 0x04 NSTrackingActiveInActiveApp = 0x40 # /System/Library/Frameworks/AppKit.framework/Headers/NSOpenGL.h NSOpenGLPFAAllRenderers = 1 # choose from all available renderers NSOpenGLPFADoubleBuffer = 5 # choose a double buffered pixel format NSOpenGLPFAStereo = 6 # stereo buffering supported NSOpenGLPFAAuxBuffers = 7 # number of aux buffers NSOpenGLPFAColorSize = 8 # number of color buffer bits NSOpenGLPFAAlphaSize = 11 # number of alpha component bits NSOpenGLPFADepthSize = 12 # number of depth buffer bits NSOpenGLPFAStencilSize = 13 # number of stencil buffer bits NSOpenGLPFAAccumSize = 14 # number of accum buffer bits NSOpenGLPFAMinimumPolicy = 51 # never choose smaller buffers than requested NSOpenGLPFAMaximumPolicy = 52 # choose largest buffers of type requested NSOpenGLPFAOffScreen = 53 # choose an off-screen capable renderer NSOpenGLPFAFullScreen = 54 # choose a full-screen capable renderer NSOpenGLPFASampleBuffers = 55 # number of multi sample buffers NSOpenGLPFASamples = 56 # number of samples per multi sample buffer NSOpenGLPFAAuxDepthStencil = 57 # each aux buffer has its own depth stencil NSOpenGLPFAColorFloat = 58 # color buffers store floating point pixels NSOpenGLPFAMultisample = 59 # choose multisampling NSOpenGLPFASupersample = 60 # choose supersampling NSOpenGLPFASampleAlpha = 61 # request alpha filtering NSOpenGLPFARendererID = 70 # request renderer by ID NSOpenGLPFASingleRenderer = 71 # choose a single renderer for all screens NSOpenGLPFANoRecovery = 72 # disable all failure recovery systems NSOpenGLPFAAccelerated = 73 # choose a hardware accelerated renderer NSOpenGLPFAClosestPolicy = 74 # choose the closest color buffer to request NSOpenGLPFARobust = 75 # renderer does not need failure recovery NSOpenGLPFABackingStore = 76 # back buffer contents are valid after swap NSOpenGLPFAMPSafe = 78 # renderer is multi-processor safe NSOpenGLPFAWindow = 80 # can be used to render to an onscreen window NSOpenGLPFAMultiScreen = 81 # single window can span multiple screens NSOpenGLPFACompliant = 83 # renderer is opengl compliant NSOpenGLPFAScreenMask = 84 # bit mask of supported physical screens NSOpenGLPFAPixelBuffer = 90 # can be used to render to a pbuffer # can be used to render offline to a pbuffer NSOpenGLPFARemotePixelBuffer = 91 NSOpenGLPFAAllowOfflineRenderers = 96 # allow use of offline renderers # choose a hardware accelerated compute device NSOpenGLPFAAcceleratedCompute = 97 # number of virtual screens in this format NSOpenGLPFAVirtualScreenCount = 128 NSOpenGLCPSwapInterval = 222 # /System/Library/Frameworks/ApplicationServices.framework/Frameworks/... # CoreGraphics.framework/Headers/CGImage.h kCGImageAlphaNone = 0 kCGImageAlphaPremultipliedLast = 1 kCGImageAlphaPremultipliedFirst = 2 kCGImageAlphaLast = 3 kCGImageAlphaFirst = 4 kCGImageAlphaNoneSkipLast = 5 kCGImageAlphaNoneSkipFirst = 6 kCGImageAlphaOnly = 7 kCGImageAlphaPremultipliedLast = 1 kCGBitmapAlphaInfoMask = 0x1F kCGBitmapFloatComponents = 1 << 8 kCGBitmapByteOrderMask = 0x7000 kCGBitmapByteOrderDefault = 0 << 12 kCGBitmapByteOrder16Little = 1 << 12 kCGBitmapByteOrder32Little = 2 << 12 kCGBitmapByteOrder16Big = 3 << 12 kCGBitmapByteOrder32Big = 4 << 12 # NSApplication.h NSApplicationPresentationDefault = 0 NSApplicationPresentationHideDock = 1 << 1 NSApplicationPresentationHideMenuBar = 1 << 3 NSApplicationPresentationDisableProcessSwitching = 1 << 5 NSApplicationPresentationDisableHideApplication = 1 << 8 # NSRunningApplication.h NSApplicationActivationPolicyRegular = 0 NSApplicationActivationPolicyAccessory = 1 NSApplicationActivationPolicyProhibited = 2 ###################################################################### # QUARTZ / COREGRAPHICS quartz = cdll.LoadLibrary(util.find_library('quartz')) CGDirectDisplayID = c_uint32 # CGDirectDisplay.h CGError = c_int32 # CGError.h CGBitmapInfo = c_uint32 # CGImage.h # /System/Library/Frameworks/ApplicationServices.framework/Frameworks/... # ImageIO.framework/Headers/CGImageProperties.h kCGImagePropertyGIFDictionary = c_void_p.in_dll( quartz, 'kCGImagePropertyGIFDictionary') kCGImagePropertyGIFDelayTime = c_void_p.in_dll( quartz, 'kCGImagePropertyGIFDelayTime') # /System/Library/Frameworks/ApplicationServices.framework/Frameworks/... # CoreGraphics.framework/Headers/CGColorSpace.h kCGRenderingIntentDefault = 0 quartz.CGDisplayIDToOpenGLDisplayMask.restype = c_uint32 quartz.CGDisplayIDToOpenGLDisplayMask.argtypes = [c_uint32] quartz.CGMainDisplayID.restype = CGDirectDisplayID quartz.CGMainDisplayID.argtypes = [] quartz.CGShieldingWindowLevel.restype = c_int32 quartz.CGShieldingWindowLevel.argtypes = [] quartz.CGCursorIsVisible.restype = c_bool quartz.CGDisplayCopyAllDisplayModes.restype = c_void_p quartz.CGDisplayCopyAllDisplayModes.argtypes = [CGDirectDisplayID, c_void_p] quartz.CGDisplaySetDisplayMode.restype = CGError quartz.CGDisplaySetDisplayMode.argtypes = [ CGDirectDisplayID, c_void_p, c_void_p] quartz.CGDisplayCapture.restype = CGError quartz.CGDisplayCapture.argtypes = [CGDirectDisplayID] quartz.CGDisplayRelease.restype = CGError quartz.CGDisplayRelease.argtypes = [CGDirectDisplayID] quartz.CGDisplayCopyDisplayMode.restype = c_void_p quartz.CGDisplayCopyDisplayMode.argtypes = [CGDirectDisplayID] quartz.CGDisplayModeGetRefreshRate.restype = c_double quartz.CGDisplayModeGetRefreshRate.argtypes = [c_void_p] quartz.CGDisplayModeRetain.restype = c_void_p quartz.CGDisplayModeRetain.argtypes = [c_void_p] quartz.CGDisplayModeRelease.restype = None quartz.CGDisplayModeRelease.argtypes = [c_void_p] quartz.CGDisplayModeGetWidth.restype = c_size_t quartz.CGDisplayModeGetWidth.argtypes = [c_void_p] quartz.CGDisplayModeGetHeight.restype = c_size_t quartz.CGDisplayModeGetHeight.argtypes = [c_void_p] quartz.CGDisplayModeCopyPixelEncoding.restype = c_void_p quartz.CGDisplayModeCopyPixelEncoding.argtypes = [c_void_p] quartz.CGGetActiveDisplayList.restype = CGError quartz.CGGetActiveDisplayList.argtypes = [ c_uint32, POINTER(CGDirectDisplayID), POINTER(c_uint32)] quartz.CGDisplayBounds.restype = CGRect quartz.CGDisplayBounds.argtypes = [CGDirectDisplayID] quartz.CGImageSourceCreateWithData.restype = c_void_p quartz.CGImageSourceCreateWithData.argtypes = [c_void_p, c_void_p] quartz.CGImageSourceCreateImageAtIndex.restype = c_void_p quartz.CGImageSourceCreateImageAtIndex.argtypes = [ c_void_p, c_size_t, c_void_p] quartz.CGImageSourceCopyPropertiesAtIndex.restype = c_void_p quartz.CGImageSourceCopyPropertiesAtIndex.argtypes = [ c_void_p, c_size_t, c_void_p] quartz.CGImageGetDataProvider.restype = c_void_p quartz.CGImageGetDataProvider.argtypes = [c_void_p] quartz.CGDataProviderCopyData.restype = c_void_p quartz.CGDataProviderCopyData.argtypes = [c_void_p] quartz.CGDataProviderCreateWithCFData.restype = c_void_p quartz.CGDataProviderCreateWithCFData.argtypes = [c_void_p] quartz.CGImageCreate.restype = c_void_p quartz.CGImageCreate.argtypes = [c_size_t, c_size_t, c_size_t, c_size_t, c_size_t, c_void_p, c_uint32, c_void_p, c_void_p, c_bool, c_int] quartz.CGImageRelease.restype = None quartz.CGImageRelease.argtypes = [c_void_p] quartz.CGImageGetBytesPerRow.restype = c_size_t quartz.CGImageGetBytesPerRow.argtypes = [c_void_p] quartz.CGImageGetWidth.restype = c_size_t quartz.CGImageGetWidth.argtypes = [c_void_p] quartz.CGImageGetHeight.restype = c_size_t quartz.CGImageGetHeight.argtypes = [c_void_p] quartz.CGImageGetBitsPerPixel.restype = c_size_t quartz.CGImageGetBitsPerPixel.argtypes = [c_void_p] quartz.CGImageGetBitmapInfo.restype = CGBitmapInfo quartz.CGImageGetBitmapInfo.argtypes = [c_void_p] quartz.CGColorSpaceCreateDeviceRGB.restype = c_void_p quartz.CGColorSpaceCreateDeviceRGB.argtypes = [] quartz.CGDataProviderRelease.restype = None quartz.CGDataProviderRelease.argtypes = [c_void_p] quartz.CGColorSpaceRelease.restype = None quartz.CGColorSpaceRelease.argtypes = [c_void_p] quartz.CGWarpMouseCursorPosition.restype = CGError quartz.CGWarpMouseCursorPosition.argtypes = [CGPoint] quartz.CGDisplayMoveCursorToPoint.restype = CGError quartz.CGDisplayMoveCursorToPoint.argtypes = [CGDirectDisplayID, CGPoint] quartz.CGAssociateMouseAndMouseCursorPosition.restype = CGError quartz.CGAssociateMouseAndMouseCursorPosition.argtypes = [c_bool] quartz.CGBitmapContextCreate.restype = c_void_p quartz.CGBitmapContextCreate.argtypes = [ c_void_p, c_size_t, c_size_t, c_size_t, c_size_t, c_void_p, CGBitmapInfo] quartz.CGBitmapContextCreateImage.restype = c_void_p quartz.CGBitmapContextCreateImage.argtypes = [c_void_p] quartz.CGFontCreateWithDataProvider.restype = c_void_p quartz.CGFontCreateWithDataProvider.argtypes = [c_void_p] quartz.CGFontCreateWithFontName.restype = c_void_p quartz.CGFontCreateWithFontName.argtypes = [c_void_p] quartz.CGContextDrawImage.restype = None quartz.CGContextDrawImage.argtypes = [c_void_p, CGRect, c_void_p] quartz.CGContextRelease.restype = None quartz.CGContextRelease.argtypes = [c_void_p] quartz.CGContextSetTextPosition.restype = None quartz.CGContextSetTextPosition.argtypes = [c_void_p, CGFloat, CGFloat] quartz.CGContextSetShouldAntialias.restype = None quartz.CGContextSetShouldAntialias.argtypes = [c_void_p, c_bool] quartz.CGDataProviderCreateWithURL.restype = c_void_p quartz.CGDataProviderCreateWithURL.argtypes = [c_void_p] quartz.CGFontCreateWithDataProvider.restype = c_void_p quartz.CGFontCreateWithDataProvider.argtypes = [c_void_p] quartz.CGDisplayScreenSize.argtypes = [CGDirectDisplayID] quartz.CGDisplayScreenSize.restype = CGSize quartz.CGDisplayBounds.argtypes = [CGDirectDisplayID] quartz.CGDisplayBounds.restype = CGRect ###################################################################### # CORETEXT ct = cdll.LoadLibrary(util.find_library('CoreText')) # Types CTFontOrientation = c_uint32 # CTFontDescriptor.h CTFontSymbolicTraits = c_uint32 # CTFontTraits.h # CoreText constants kCTFontAttributeName = c_void_p.in_dll(ct, 'kCTFontAttributeName') kCTFontFamilyNameAttribute = c_void_p.in_dll(ct, 'kCTFontFamilyNameAttribute') kCTFontSymbolicTrait = c_void_p.in_dll(ct, 'kCTFontSymbolicTrait') kCTFontWeightTrait = c_void_p.in_dll(ct, 'kCTFontWeightTrait') kCTFontTraitsAttribute = c_void_p.in_dll(ct, 'kCTFontTraitsAttribute') # constants from CTFontTraits.h kCTFontItalicTrait = (1 << 0) kCTFontBoldTrait = (1 << 1) ct.CTLineCreateWithAttributedString.restype = c_void_p ct.CTLineCreateWithAttributedString.argtypes = [c_void_p] ct.CTLineDraw.restype = None ct.CTLineDraw.argtypes = [c_void_p, c_void_p] ct.CTFontGetBoundingRectsForGlyphs.restype = CGRect ct.CTFontGetBoundingRectsForGlyphs.argtypes = [ c_void_p, CTFontOrientation, POINTER(CGGlyph), POINTER(CGRect), CFIndex] ct.CTFontGetAdvancesForGlyphs.restype = c_double ct.CTFontGetAdvancesForGlyphs.argtypes = [ c_void_p, CTFontOrientation, POINTER(CGGlyph), POINTER(CGSize), CFIndex] ct.CTFontGetAscent.restype = CGFloat ct.CTFontGetAscent.argtypes = [c_void_p] ct.CTFontGetDescent.restype = CGFloat ct.CTFontGetDescent.argtypes = [c_void_p] ct.CTFontGetSymbolicTraits.restype = CTFontSymbolicTraits ct.CTFontGetSymbolicTraits.argtypes = [c_void_p] ct.CTFontGetGlyphsForCharacters.restype = c_bool ct.CTFontGetGlyphsForCharacters.argtypes = [ c_void_p, POINTER(UniChar), POINTER(CGGlyph), CFIndex] ct.CTFontCreateWithGraphicsFont.restype = c_void_p ct.CTFontCreateWithGraphicsFont.argtypes = [c_void_p, CGFloat, c_void_p, c_void_p] ct.CTFontCopyFamilyName.restype = c_void_p ct.CTFontCopyFamilyName.argtypes = [c_void_p] ct.CTFontCopyFullName.restype = c_void_p ct.CTFontCopyFullName.argtypes = [c_void_p] ct.CTFontCreateWithFontDescriptor.restype = c_void_p ct.CTFontCreateWithFontDescriptor.argtypes = [c_void_p, CGFloat, c_void_p] ct.CTFontCreateCopyWithAttributes.restype = c_void_p ct.CTFontCreateCopyWithAttributes.argtypes = [c_void_p, CGFloat, c_void_p, c_void_p] ct.CTFontDescriptorCreateWithAttributes.restype = c_void_p ct.CTFontDescriptorCreateWithAttributes.argtypes = [c_void_p] ct.CTTypesetterCreateWithAttributedString.restype = c_void_p ct.CTTypesetterCreateWithAttributedString.argtypes = [c_void_p] ct.CTTypesetterCreateLine.restype = c_void_p ct.CTTypesetterCreateLine.argtypes = [c_void_p, CFRange] ct.CTLineGetOffsetForStringIndex.restype = CGFloat ct.CTLineGetOffsetForStringIndex.argtypes = [c_void_p, CFIndex, POINTER(CGFloat)] ct.CTFontManagerCreateFontDescriptorsFromURL.restype = c_void_p ct.CTFontManagerCreateFontDescriptorsFromURL.argtypes = [c_void_p] ###################################################################### # FOUNDATION # foundation = cdll.LoadLibrary(util.find_library('Foundation')) # foundation.NSMouseInRect.restype = c_bool # foundation.NSMouseInRect.argtypes = [NSPoint, NSRect, c_bool] ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/ext/py24_ordereddict.py�����������������������������������������������������������0000664�0001750�0001750�00000021452�12510536123�022057� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Backport of OrderedDict() class that runs on Python 2.4, 2.5, 2.6, 2.7 and pypy. # Passes Python2.7's test suite and incorporates all the latest updates. # MIT-licensed from: # http://code.activestate.com/recipes/576693/ # http://docs.python.org/2/library/collections.html#ordereddict-objects try: from thread import get_ident as _get_ident except ImportError: from dummy_thread import get_ident as _get_ident try: from _abcoll import KeysView, ValuesView, ItemsView except ImportError: pass class OrderedDict(dict): 'Dictionary that remembers insertion order' # An inherited dict maps keys to values. # The inherited dict provides __getitem__, __len__, __contains__, and get. # The remaining methods are order-aware. # Big-O running times for all methods are the same as for regular # dictionaries. # The internal self.__map dictionary maps keys to links in a doubly linked list. # The circular doubly linked list starts and ends with a sentinel element. # The sentinel element never gets deleted (this simplifies the algorithm). # Each link is stored as a list of length three: [PREV, NEXT, KEY]. def __init__(self, *args, **kwargs): '''Initialize an ordered dictionary. Signature is the same as for regular dictionaries, but keyword arguments are not recommended because their insertion order is arbitrary. ''' if len(args) > 1: raise TypeError('expected at most 1 arguments, got %d' % len(args)) try: self.__root except AttributeError: self.__root = root = [] # sentinel node root[:] = [root, root, None] self.__map = {} self.__update(*args, **kwargs) def __setitem__(self, key, value, dict_setitem=dict.__setitem__): 'od.__setitem__(i, y) <==> od[i]=y' # Setting a new item creates a new link which goes at the end of the linked # list, and the inherited dictionary is updated with the new key/value # pair. if key not in self: root = self.__root last = root[0] last[1] = root[0] = self.__map[key] = [last, root, key] dict_setitem(self, key, value) def __delitem__(self, key, dict_delitem=dict.__delitem__): 'od.__delitem__(y) <==> del od[y]' # Deleting an existing item uses self.__map to find the link which is # then removed by updating the links in the predecessor and successor # nodes. dict_delitem(self, key) link_prev, link_next, key = self.__map.pop(key) link_prev[1] = link_next link_next[0] = link_prev def __iter__(self): 'od.__iter__() <==> iter(od)' root = self.__root curr = root[1] while curr is not root: yield curr[2] curr = curr[1] def __reversed__(self): 'od.__reversed__() <==> reversed(od)' root = self.__root curr = root[0] while curr is not root: yield curr[2] curr = curr[0] def clear(self): 'od.clear() -> None. Remove all items from od.' try: for node in self.__map.itervalues(): del node[:] root = self.__root root[:] = [root, root, None] self.__map.clear() except AttributeError: pass dict.clear(self) def popitem(self, last=True): '''od.popitem() -> (k, v), return and remove a (key, value) pair. Pairs are returned in LIFO order if last is true or FIFO order if false. ''' if not self: raise KeyError('dictionary is empty') root = self.__root if last: link = root[0] link_prev = link[0] link_prev[1] = root root[0] = link_prev else: link = root[1] link_next = link[1] root[1] = link_next link_next[0] = root key = link[2] del self.__map[key] value = dict.pop(self, key) return key, value # -- the following methods do not depend on the internal structure -- def keys(self): 'od.keys() -> list of keys in od' return list(self) def values(self): 'od.values() -> list of values in od' return [self[key] for key in self] def items(self): 'od.items() -> list of (key, value) pairs in od' return [(key, self[key]) for key in self] def iterkeys(self): 'od.iterkeys() -> an iterator over the keys in od' return iter(self) def itervalues(self): 'od.itervalues -> an iterator over the values in od' for k in self: yield self[k] def iteritems(self): 'od.iteritems -> an iterator over the (key, value) items in od' for k in self: yield (k, self[k]) def update(*args, **kwargs): '''od.update(E, **F) -> None. Update od from dict/iterable E and F. If E is a dict instance, does: for k in E: od[k] = E[k] If E has a .keys() method, does: for k in E.keys(): od[k] = E[k] Or if E is an iterable of items, does: for k, v in E: od[k] = v In either case, this is followed by: for k, v in F.items(): od[k] = v ''' if len(args) > 2: raise TypeError('update() takes at most 2 positional ' 'arguments (%d given)' % (len(args),)) elif not args: raise TypeError('update() takes at least 1 argument (0 given)') self = args[0] # Make progressively weaker assumptions about "other" other = () if len(args) == 2: other = args[1] if isinstance(other, dict): for key in other: self[key] = other[key] elif hasattr(other, 'keys'): for key in other.keys(): self[key] = other[key] else: for key, value in other: self[key] = value for key, value in kwargs.items(): self[key] = value # let subclasses override update without breaking __init__ __update = update __marker = object() def pop(self, key, default=__marker): '''od.pop(k[,d]) -> v, remove specified key and return the corresponding value. If key is not found, d is returned if given, otherwise KeyError is raised. ''' if key in self: result = self[key] del self[key] return result if default is self.__marker: raise KeyError(key) return default def setdefault(self, key, default=None): 'od.setdefault(k[,d]) -> od.get(k,d), also set od[k]=d if k not in od' if key in self: return self[key] self[key] = default return default def __repr__(self, _repr_running={}): 'od.__repr__() <==> repr(od)' call_key = id(self), _get_ident() if call_key in _repr_running: return '...' _repr_running[call_key] = 1 try: if not self: return '%s()' % (self.__class__.__name__,) return '%s(%r)' % (self.__class__.__name__, self.items()) finally: del _repr_running[call_key] def __reduce__(self): 'Return state information for pickling' items = [[k, self[k]] for k in self] inst_dict = vars(self).copy() for k in vars(OrderedDict()): inst_dict.pop(k, None) if inst_dict: return (self.__class__, (items,), inst_dict) return self.__class__, (items,) def copy(self): 'od.copy() -> a shallow copy of od' return self.__class__(self) @classmethod def fromkeys(cls, iterable, value=None): '''OD.fromkeys(S[, v]) -> New ordered dictionary with keys from S and values equal to v (which defaults to None). ''' d = cls() for key in iterable: d[key] = value return d def __eq__(self, other): '''od.__eq__(y) <==> od==y. Comparison to another OD is order-sensitive while comparison to a regular mapping is order-insensitive. ''' if isinstance(other, OrderedDict): return len(self) == len(other) and self.items() == other.items() return dict.__eq__(self, other) def __ne__(self, other): return not self == other # -- the following methods are only used in Python 2.7 -- def viewkeys(self): "od.viewkeys() -> a set-like object providing a view on od's keys" return KeysView(self) def viewvalues(self): "od.viewvalues() -> an object providing a view on od's values" return ValuesView(self) def viewitems(self): "od.viewitems() -> a set-like object providing a view on od's items" return ItemsView(self) ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/ext/mplexporter.py����������������������������������������������������������������0000664�0001750�0001750�00000067000�12375431476�021307� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������""" Matplotlib Exporter =================== This submodule contains tools for crawling a matplotlib figure and exporting relevant pieces to a renderer. Copyright (c) 2014, mpld3 All rights reserved. Redistribution and use in source and binary forms, with or without modification, # noqa are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this # noqa list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the {organization} nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR # noqa ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. """ import warnings import io from . import mplutils as utils import matplotlib from matplotlib import transforms class Exporter(object): """Matplotlib Exporter Parameters ---------- renderer : Renderer object The renderer object called by the exporter to create a figure visualization. See mplexporter.Renderer for information on the methods which should be defined within the renderer. close_mpl : bool If True (default), close the matplotlib figure as it is rendered. This is useful for when the exporter is used within the notebook, or with an interactive matplotlib backend. """ def __init__(self, renderer, close_mpl=True): self.close_mpl = close_mpl self.renderer = renderer def run(self, fig): """ Run the exporter on the given figure Parmeters --------- fig : matplotlib.Figure instance The figure to export """ # Calling savefig executes the draw() command, putting elements # in the correct place. fig.savefig(io.BytesIO(), format='png', dpi=fig.dpi) if self.close_mpl: import matplotlib.pyplot as plt plt.close(fig) self.crawl_fig(fig) @staticmethod def process_transform(transform, ax=None, data=None, return_trans=False, force_trans=None): """Process the transform and convert data to figure or data coordinates Parameters ---------- transform : matplotlib Transform object The transform applied to the data ax : matplotlib Axes object (optional) The axes the data is associated with data : ndarray (optional) The array of data to be transformed. return_trans : bool (optional) If true, return the final transform of the data force_trans : matplotlib.transform instance (optional) If supplied, first force the data to this transform Returns ------- code : string Code is either "data", "axes", "figure", or "display", indicating the type of coordinates output. transform : matplotlib transform the transform used to map input data to output data. Returned only if return_trans is True new_data : ndarray Data transformed to match the given coordinate code. Returned only if data is specified """ if isinstance(transform, transforms.BlendedGenericTransform): warnings.warn("Blended transforms not yet supported. " "Zoom behavior may not work as expected.") if force_trans is not None: if data is not None: data = (transform - force_trans).transform(data) transform = force_trans code = "display" if ax is not None: for (c, trans) in [("data", ax.transData), ("axes", ax.transAxes), ("figure", ax.figure.transFigure), ("display", transforms.IdentityTransform())]: if transform.contains_branch(trans): code, transform = (c, transform - trans) break if data is not None: if return_trans: return code, transform.transform(data), transform else: return code, transform.transform(data) else: if return_trans: return code, transform else: return code def crawl_fig(self, fig): """Crawl the figure and process all axes""" with self.renderer.draw_figure(fig=fig, props=utils.get_figure_properties(fig)): for ax in fig.axes: self.crawl_ax(ax) def crawl_ax(self, ax): """Crawl the axes and process all elements within""" with self.renderer.draw_axes(ax=ax, props=utils.get_axes_properties(ax)): for line in ax.lines: self.draw_line(ax, line) for text in ax.texts: self.draw_text(ax, text) for (text, ttp) in zip([ax.xaxis.label, ax.yaxis.label, ax.title], ["xlabel", "ylabel", "title"]): if(hasattr(text, 'get_text') and text.get_text()): self.draw_text(ax, text, force_trans=ax.transAxes, text_type=ttp) for artist in ax.artists: # TODO: process other artists if isinstance(artist, matplotlib.text.Text): self.draw_text(ax, artist) for patch in ax.patches: self.draw_patch(ax, patch) for collection in ax.collections: self.draw_collection(ax, collection) for image in ax.images: self.draw_image(ax, image) legend = ax.get_legend() if legend is not None: props = utils.get_legend_properties(ax, legend) with self.renderer.draw_legend(legend=legend, props=props): if props['visible']: self.crawl_legend(ax, legend) def crawl_legend(self, ax, legend): """ Recursively look through objects in legend children """ legendElements = list(utils.iter_all_children(legend._legend_box, skipContainers=True)) legendElements.append(legend.legendPatch) for child in legendElements: # force a large zorder so it appears on top child.set_zorder(1E6 + child.get_zorder()) try: # What kind of object... if isinstance(child, matplotlib.patches.Patch): self.draw_patch(ax, child, force_trans=ax.transAxes) elif isinstance(child, matplotlib.text.Text): if not (child is legend.get_children()[-1] and child.get_text() == 'None'): self.draw_text(ax, child, force_trans=ax.transAxes) elif isinstance(child, matplotlib.lines.Line2D): self.draw_line(ax, child, force_trans=ax.transAxes) elif isinstance(child, matplotlib.collections.Collection): self.draw_collection(ax, child, force_pathtrans=ax.transAxes) else: warnings.warn("Legend element %s not impemented" % child) except NotImplementedError: warnings.warn("Legend element %s not impemented" % child) def draw_line(self, ax, line, force_trans=None): """Process a matplotlib line and call renderer.draw_line""" coordinates, data = self.process_transform(line.get_transform(), ax, line.get_xydata(), force_trans=force_trans) linestyle = utils.get_line_style(line) if linestyle['dasharray'] is None: linestyle = None markerstyle = utils.get_marker_style(line) if (markerstyle['marker'] in ['None', 'none', None] or markerstyle['markerpath'][0].size == 0): markerstyle = None label = line.get_label() if markerstyle or linestyle: self.renderer.draw_marked_line(data=data, coordinates=coordinates, linestyle=linestyle, markerstyle=markerstyle, label=label, mplobj=line) def draw_text(self, ax, text, force_trans=None, text_type=None): """Process a matplotlib text object and call renderer.draw_text""" content = text.get_text() if content: transform = text.get_transform() position = text.get_position() coords, position = self.process_transform(transform, ax, position, force_trans=force_trans) style = utils.get_text_style(text) self.renderer.draw_text(text=content, position=position, coordinates=coords, text_type=text_type, style=style, mplobj=text) def draw_patch(self, ax, patch, force_trans=None): """Process a matplotlib patch object and call renderer.draw_path""" vertices, pathcodes = utils.SVG_path(patch.get_path()) transform = patch.get_transform() coordinates, vertices = self.process_transform(transform, ax, vertices, force_trans=force_trans) linestyle = utils.get_path_style(patch, fill=patch.get_fill()) self.renderer.draw_path(data=vertices, coordinates=coordinates, pathcodes=pathcodes, style=linestyle, mplobj=patch) def draw_collection(self, ax, collection, force_pathtrans=None, force_offsettrans=None): """Process a matplotlib collection and call renderer.draw_collection""" (transform, transOffset, offsets, paths) = collection._prepare_points() offset_coords, offsets = self.process_transform( transOffset, ax, offsets, force_trans=force_offsettrans) path_coords = self.process_transform( transform, ax, force_trans=force_pathtrans) processed_paths = [utils.SVG_path(path) for path in paths] processed_paths = [(self.process_transform( transform, ax, path[0], force_trans=force_pathtrans)[1], path[1]) for path in processed_paths] path_transforms = collection.get_transforms() try: # matplotlib 1.3: path_transforms are transform objects. # Convert them to numpy arrays. path_transforms = [t.get_matrix() for t in path_transforms] except AttributeError: # matplotlib 1.4: path transforms are already numpy arrays. pass styles = {'linewidth': collection.get_linewidths(), 'facecolor': collection.get_facecolors(), 'edgecolor': collection.get_edgecolors(), 'alpha': collection._alpha, 'zorder': collection.get_zorder()} offset_dict = {"data": "before", "screen": "after"} offset_order = offset_dict[collection.get_offset_position()] self.renderer.draw_path_collection(paths=processed_paths, path_coordinates=path_coords, path_transforms=path_transforms, offsets=offsets, offset_coordinates=offset_coords, offset_order=offset_order, styles=styles, mplobj=collection) def draw_image(self, ax, image): """Process a matplotlib image object and call renderer.draw_image""" self.renderer.draw_image(imdata=utils.image_to_base64(image), extent=image.get_extent(), coordinates="data", style={"alpha": image.get_alpha(), "zorder": image.get_zorder()}, mplobj=image) ############################################################################## # Renderers/base.py import itertools from contextlib import contextmanager import numpy as np from . import _mpl_py3k_compat as py3k class Renderer(object): @staticmethod def ax_zoomable(ax): return bool(ax and ax.get_navigate()) @staticmethod def ax_has_xgrid(ax): return bool(ax and ax.xaxis._gridOnMajor and ax.yaxis.get_gridlines()) @staticmethod def ax_has_ygrid(ax): return bool(ax and ax.yaxis._gridOnMajor and ax.yaxis.get_gridlines()) @property def current_ax_zoomable(self): return self.ax_zoomable(self._current_ax) @property def current_ax_has_xgrid(self): return self.ax_has_xgrid(self._current_ax) @property def current_ax_has_ygrid(self): return self.ax_has_ygrid(self._current_ax) @contextmanager def draw_figure(self, fig, props): if hasattr(self, "_current_fig") and self._current_fig is not None: warnings.warn("figure embedded in figure: something is wrong") self._current_fig = fig self._fig_props = props self.open_figure(fig=fig, props=props) yield self.close_figure(fig=fig) self._current_fig = None self._fig_props = {} @contextmanager def draw_axes(self, ax, props): if hasattr(self, "_current_ax") and self._current_ax is not None: warnings.warn("axes embedded in axes: something is wrong") self._current_ax = ax self._ax_props = props self.open_axes(ax=ax, props=props) yield self.close_axes(ax=ax) self._current_ax = None self._ax_props = {} @contextmanager def draw_legend(self, legend, props): self._current_legend = legend self._legend_props = props self.open_legend(legend=legend, props=props) yield self.close_legend(legend=legend) self._current_legend = None self._legend_props = {} # Following are the functions which should be overloaded in subclasses def open_figure(self, fig, props): """ Begin commands for a particular figure. Parameters ---------- fig : matplotlib.Figure The Figure which will contain the ensuing axes and elements props : dictionary The dictionary of figure properties """ pass def close_figure(self, fig): """ Finish commands for a particular figure. Parameters ---------- fig : matplotlib.Figure The figure which is finished being drawn. """ pass def open_axes(self, ax, props): """ Begin commands for a particular axes. Parameters ---------- ax : matplotlib.Axes The Axes which will contain the ensuing axes and elements props : dictionary The dictionary of axes properties """ pass def close_axes(self, ax): """ Finish commands for a particular axes. Parameters ---------- ax : matplotlib.Axes The Axes which is finished being drawn. """ pass def open_legend(self, legend, props): """ Beging commands for a particular legend. Parameters ---------- legend : matplotlib.legend.Legend The Legend that will contain the ensuing elements props : dictionary The dictionary of legend properties """ pass def close_legend(self, legend): """ Finish commands for a particular legend. Parameters ---------- legend : matplotlib.legend.Legend The Legend which is finished being drawn """ pass def draw_marked_line(self, data, coordinates, linestyle, markerstyle, label, mplobj=None): """Draw a line that also has markers. If this isn't reimplemented by a renderer object, by default, it will make a call to BOTH draw_line and draw_markers when both markerstyle and linestyle are not None in the same Line2D object. """ if linestyle is not None: self.draw_line(data, coordinates, linestyle, label, mplobj) if markerstyle is not None: self.draw_markers(data, coordinates, markerstyle, label, mplobj) def draw_line(self, data, coordinates, style, label, mplobj=None): """ Draw a line. By default, draw the line via the draw_path() command. Some renderers might wish to override this and provide more fine-grained behavior. In matplotlib, lines are generally created via the plt.plot() command, though this command also can create marker collections. Parameters ---------- data : array_like A shape (N, 2) array of datapoints. coordinates : string A string code, which should be either 'data' for data coordinates, or 'figure' for figure (pixel) coordinates. style : dictionary a dictionary specifying the appearance of the line. mplobj : matplotlib object the matplotlib plot element which generated this line """ pathcodes = ['M'] + (data.shape[0] - 1) * ['L'] pathstyle = dict(facecolor='none', **style) pathstyle['edgecolor'] = pathstyle.pop('color') pathstyle['edgewidth'] = pathstyle.pop('linewidth') self.draw_path(data=data, coordinates=coordinates, pathcodes=pathcodes, style=pathstyle, mplobj=mplobj) @staticmethod def _iter_path_collection(paths, path_transforms, offsets, styles): """Build an iterator over the elements of the path collection""" N = max(len(paths), len(offsets)) if not path_transforms: path_transforms = [np.eye(3)] edgecolor = styles['edgecolor'] if np.size(edgecolor) == 0: edgecolor = ['none'] facecolor = styles['facecolor'] if np.size(facecolor) == 0: facecolor = ['none'] elements = [paths, path_transforms, offsets, edgecolor, styles['linewidth'], facecolor] it = itertools return it.islice(py3k.zip(*py3k.map(it.cycle, elements)), N) def draw_path_collection(self, paths, path_coordinates, path_transforms, offsets, offset_coordinates, offset_order, styles, mplobj=None): """ Draw a collection of paths. The paths, offsets, and styles are all iterables, and the number of paths is max(len(paths), len(offsets)). By default, this is implemented via multiple calls to the draw_path() function. For efficiency, Renderers may choose to customize this implementation. Examples of path collections created by matplotlib are scatter plots, histograms, contour plots, and many others. Parameters ---------- paths : list list of tuples, where each tuple has two elements: (data, pathcodes). See draw_path() for a description of these. path_coordinates: string the coordinates code for the paths, which should be either 'data' for data coordinates, or 'figure' for figure (pixel) coordinates. path_transforms: array_like an array of shape (*, 3, 3), giving a series of 2D Affine transforms for the paths. These encode translations, rotations, and scalings in the standard way. offsets: array_like An array of offsets of shape (N, 2) offset_coordinates : string the coordinates code for the offsets, which should be either 'data' for data coordinates, or 'figure' for figure (pixel) coordinates. offset_order : string either "before" or "after". This specifies whether the offset is applied before the path transform, or after. The matplotlib backend equivalent is "before"->"data", "after"->"screen". styles: dictionary A dictionary in which each value is a list of length N, containing the style(s) for the paths. mplobj : matplotlib object the matplotlib plot element which generated this collection """ if offset_order == "before": raise NotImplementedError("offset before transform") for tup in self._iter_path_collection(paths, path_transforms, offsets, styles): (path, path_transform, offset, ec, lw, fc) = tup vertices, pathcodes = path path_transform = transforms.Affine2D(path_transform) vertices = path_transform.transform(vertices) # This is a hack: if path_coordinates == "figure": path_coordinates = "points" style = {"edgecolor": utils.color_to_hex(ec), "facecolor": utils.color_to_hex(fc), "edgewidth": lw, "dasharray": "10,0", "alpha": styles['alpha'], "zorder": styles['zorder']} self.draw_path(data=vertices, coordinates=path_coordinates, pathcodes=pathcodes, style=style, offset=offset, offset_coordinates=offset_coordinates, mplobj=mplobj) def draw_markers(self, data, coordinates, style, label, mplobj=None): """ Draw a set of markers. By default, this is done by repeatedly calling draw_path(), but renderers should generally overload this method to provide a more efficient implementation. In matplotlib, markers are created using the plt.plot() command. Parameters ---------- data : array_like A shape (N, 2) array of datapoints. coordinates : string A string code, which should be either 'data' for data coordinates, or 'figure' for figure (pixel) coordinates. style : dictionary a dictionary specifying the appearance of the markers. mplobj : matplotlib object the matplotlib plot element which generated this marker collection """ vertices, pathcodes = style['markerpath'] pathstyle = dict((key, style[key]) for key in ['alpha', 'edgecolor', 'facecolor', 'zorder', 'edgewidth']) pathstyle['dasharray'] = "10,0" for vertex in data: self.draw_path(data=vertices, coordinates="points", pathcodes=pathcodes, style=pathstyle, offset=vertex, offset_coordinates=coordinates, mplobj=mplobj) def draw_text(self, text, position, coordinates, style, text_type=None, mplobj=None): """ Draw text on the image. Parameters ---------- text : string The text to draw position : tuple The (x, y) position of the text coordinates : string A string code, which should be either 'data' for data coordinates, or 'figure' for figure (pixel) coordinates. style : dictionary a dictionary specifying the appearance of the text. text_type : string or None if specified, a type of text such as "xlabel", "ylabel", "title" mplobj : matplotlib object the matplotlib plot element which generated this text """ raise NotImplementedError() def draw_path(self, data, coordinates, pathcodes, style, offset=None, offset_coordinates="data", mplobj=None): """ Draw a path. In matplotlib, paths are created by filled regions, histograms, contour plots, patches, etc. Parameters ---------- data : array_like A shape (N, 2) array of datapoints. coordinates : string A string code, which should be either 'data' for data coordinates, 'figure' for figure (pixel) coordinates, or "points" for raw point coordinates (useful in conjunction with offsets, below). pathcodes : list A list of single-character SVG pathcodes associated with the data. Path codes are one of ['M', 'm', 'L', 'l', 'Q', 'q', 'T', 't', 'S', 's', 'C', 'c', 'Z', 'z'] See the SVG specification for details. Note that some path codes consume more than one datapoint (while 'Z' consumes none), so in general, the length of the pathcodes list will not be the same as that of the data array. style : dictionary a dictionary specifying the appearance of the line. offset : list (optional) the (x, y) offset of the path. If not given, no offset will be used. offset_coordinates : string (optional) A string code, which should be either 'data' for data coordinates, or 'figure' for figure (pixel) coordinates. mplobj : matplotlib object the matplotlib plot element which generated this path """ raise NotImplementedError() def draw_image(self, imdata, extent, coordinates, style, mplobj=None): """ Draw an image. Parameters ---------- imdata : string base64 encoded png representation of the image extent : list the axes extent of the image: [xmin, xmax, ymin, ymax] coordinates: string A string code, which should be either 'data' for data coordinates, or 'figure' for figure (pixel) coordinates. style : dictionary a dictionary specifying the appearance of the image mplobj : matplotlib object the matplotlib plot object which generated this image """ raise NotImplementedError() vispy-0.4.0/vispy/ext/cubehelix.py������������������������������������������������������������������0000664�0001750�0001750�00000012334�12510536123�020660� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- """Modified from: https://raw.githubusercontent.com/jradavenport/cubehelix/master/cubehelix.py Copyright (c) 2014, James R. A. Davenport and contributors All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. """ from math import pi import numpy as np def cubehelix(start=0.5, rot=1, gamma=1.0, reverse=True, nlev=256., minSat=1.2, maxSat=1.2, minLight=0., maxLight=1., **kwargs): """ A full implementation of Dave Green's "cubehelix" for Matplotlib. Based on the FORTRAN 77 code provided in D.A. Green, 2011, BASI, 39, 289. http://adsabs.harvard.edu/abs/2011arXiv1108.5083G User can adjust all parameters of the cubehelix algorithm. This enables much greater flexibility in choosing color maps, while always ensuring the color map scales in intensity from black to white. A few simple examples: Default color map settings produce the standard "cubehelix". Create color map in only blues by setting rot=0 and start=0. Create reverse (white to black) backwards through the rainbow once by setting rot=1 and reverse=True. Parameters ---------- start : scalar, optional Sets the starting position in the color space. 0=blue, 1=red, 2=green. Defaults to 0.5. rot : scalar, optional The number of rotations through the rainbow. Can be positive or negative, indicating direction of rainbow. Negative values correspond to Blue->Red direction. Defaults to -1.5 gamma : scalar, optional The gamma correction for intensity. Defaults to 1.0 reverse : boolean, optional Set to True to reverse the color map. Will go from black to white. Good for density plots where shade~density. Defaults to False nlev : scalar, optional Defines the number of discrete levels to render colors at. Defaults to 256. sat : scalar, optional The saturation intensity factor. Defaults to 1.2 NOTE: this was formerly known as "hue" parameter minSat : scalar, optional Sets the minimum-level saturation. Defaults to 1.2 maxSat : scalar, optional Sets the maximum-level saturation. Defaults to 1.2 startHue : scalar, optional Sets the starting color, ranging from [0, 360], as in D3 version by @mbostock NOTE: overrides values in start parameter endHue : scalar, optional Sets the ending color, ranging from [0, 360], as in D3 version by @mbostock NOTE: overrides values in rot parameter minLight : scalar, optional Sets the minimum lightness value. Defaults to 0. maxLight : scalar, optional Sets the maximum lightness value. Defaults to 1. Returns ------- data : ndarray, shape (N, 3) Control points. """ # override start and rot if startHue and endHue are set if kwargs is not None: if 'startHue' in kwargs: start = (kwargs.get('startHue') / 360. - 1.) * 3. if 'endHue' in kwargs: rot = kwargs.get('endHue') / 360. - start / 3. - 1. if 'sat' in kwargs: minSat = kwargs.get('sat') maxSat = kwargs.get('sat') # set up the parameters fract = np.linspace(minLight, maxLight, nlev) angle = 2.0 * pi * (start / 3.0 + rot * fract + 1.) fract = fract**gamma satar = np.linspace(minSat, maxSat, nlev) amp = satar * fract * (1. - fract) / 2. # compute the RGB vectors according to main equations red = fract + amp * (-0.14861 * np.cos(angle) + 1.78277 * np.sin(angle)) grn = fract + amp * (-0.29227 * np.cos(angle) - 0.90649 * np.sin(angle)) blu = fract + amp * (1.97294 * np.cos(angle)) # find where RBB are outside the range [0,1], clip red[np.where((red > 1.))] = 1. grn[np.where((grn > 1.))] = 1. blu[np.where((blu > 1.))] = 1. red[np.where((red < 0.))] = 0. grn[np.where((grn < 0.))] = 0. blu[np.where((blu < 0.))] = 0. # optional color reverse if reverse is True: red = red[::-1] blu = blu[::-1] grn = grn[::-1] return np.array((red, blu, grn)).T ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/ext/glfw.py�����������������������������������������������������������������������0000664�0001750�0001750�00000057207�12513244635�017666� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # GLFW - An OpenGL framework # API version: 3.0.1 # WWW: http://www.glfw.org/ # ---------------------------------------------------------------------------- # Copyright (c) 2002-2006 Marcus Geelnard # Copyright (c) 2006-2010 Camilla Berglund # # Python bindings - Copyright (c) 2013 Nicolas P. Rougier # # This software is provided 'as-is', without any express or implied # warranty. In no event will the authors be held liable for any damages # arising from the use of this software. # # Permission is granted to anyone to use this software for any purpose, # including commercial applications, and to alter it and redistribute it # freely, subject to the following restrictions: # # 1. The origin of this software must not be misrepresented; you must not # claim that you wrote the original software. If you use this software # in a product, an acknowledgment in the product documentation would # be appreciated but is not required. # # 2. Altered source versions must be plainly marked as such, and must not # be misrepresented as being the original software. # # 3. This notice may not be removed or altered from any source # distribution. # # ----------------------------------------------------------------------------- # NOTE: # This source has been modified from its original form by the vispy dev team import os import ctypes.util from ctypes import (Structure, POINTER, CFUNCTYPE, byref, c_char_p, c_int, c_uint, c_double, c_float, c_ushort) _glfw_file = None # First if there is an environment variable pointing to the library if 'GLFW_LIBRARY' in os.environ: if os.path.exists(os.environ['GLFW_LIBRARY']): _glfw_file = os.path.realpath(os.environ['GLFW_LIBRARY']) # Else, try to find it if _glfw_file is None: order = ['glfw3', 'glfw'] for check in order: _glfw_file = ctypes.util.find_library(check) if _glfw_file is not None: break # Else, we failed and exit if _glfw_file is None: raise OSError('GLFW library not found') # Load it _glfw = ctypes.CDLL(_glfw_file) # Ensure it's new enough def glfwGetVersion(): major, minor, rev = c_int(0), c_int(0), c_int(0) _glfw.glfwGetVersion(byref(major), byref(minor), byref(rev)) return major.value, minor.value, rev.value version = glfwGetVersion() if version[0] != 3: version = '.'.join([str(v) for v in version]) raise OSError('Need GLFW library version 3, found version %s' % version) # --- Version ----------------------------------------------------------------- GLFW_VERSION_MAJOR = version[0] GLFW_VERSION_MINOR = version[1] GLFW_VERSION_REVISION = version[2] __version__ = GLFW_VERSION_MAJOR, GLFW_VERSION_MINOR, GLFW_VERSION_REVISION # --- Input handling definitions ---------------------------------------------- GLFW_RELEASE = 0 GLFW_PRESS = 1 GLFW_REPEAT = 2 # --- Keys -------------------------------------------------------------------- # --- The unknown key --------------------------------------------------------- GLFW_KEY_UNKNOWN = -1 # --- Printable keys ---------------------------------------------------------- GLFW_KEY_SPACE = 32 GLFW_KEY_APOSTROPHE = 39 # '' GLFW_KEY_COMMA = 44 # , GLFW_KEY_MINUS = 45 # - GLFW_KEY_PERIOD = 46 # . GLFW_KEY_SLASH = 47 # / GLFW_KEY_0 = 48 GLFW_KEY_1 = 49 GLFW_KEY_2 = 50 GLFW_KEY_3 = 51 GLFW_KEY_4 = 52 GLFW_KEY_5 = 53 GLFW_KEY_6 = 54 GLFW_KEY_7 = 55 GLFW_KEY_8 = 56 GLFW_KEY_9 = 57 GLFW_KEY_SEMICOLON = 59 # ; GLFW_KEY_EQUAL = 61 # = GLFW_KEY_A = 65 GLFW_KEY_B = 66 GLFW_KEY_C = 67 GLFW_KEY_D = 68 GLFW_KEY_E = 69 GLFW_KEY_F = 70 GLFW_KEY_G = 71 GLFW_KEY_H = 72 GLFW_KEY_I = 73 GLFW_KEY_J = 74 GLFW_KEY_K = 75 GLFW_KEY_L = 76 GLFW_KEY_M = 77 GLFW_KEY_N = 78 GLFW_KEY_O = 79 GLFW_KEY_P = 80 GLFW_KEY_Q = 81 GLFW_KEY_R = 82 GLFW_KEY_S = 83 GLFW_KEY_T = 84 GLFW_KEY_U = 85 GLFW_KEY_V = 86 GLFW_KEY_W = 87 GLFW_KEY_X = 88 GLFW_KEY_Y = 89 GLFW_KEY_Z = 90 GLFW_KEY_LEFT_BRACKET = 91 # [ GLFW_KEY_BACKSLASH = 92 # \ GLFW_KEY_RIGHT_BRACKET = 93 # ] GLFW_KEY_GRAVE_ACCENT = 96 # ` GLFW_KEY_WORLD_1 = 161 # non-US #1 GLFW_KEY_WORLD_2 = 162 # non-US #2 # --- Function keys ----------------------------------------------------------- GLFW_KEY_ESCAPE = 256 GLFW_KEY_ENTER = 257 GLFW_KEY_TAB = 258 GLFW_KEY_BACKSPACE = 259 GLFW_KEY_INSERT = 260 GLFW_KEY_DELETE = 261 GLFW_KEY_RIGHT = 262 GLFW_KEY_LEFT = 263 GLFW_KEY_DOWN = 264 GLFW_KEY_UP = 265 GLFW_KEY_PAGE_UP = 266 GLFW_KEY_PAGE_DOWN = 267 GLFW_KEY_HOME = 268 GLFW_KEY_END = 269 GLFW_KEY_CAPS_LOCK = 280 GLFW_KEY_SCROLL_LOCK = 281 GLFW_KEY_NUM_LOCK = 282 GLFW_KEY_PRINT_SCREEN = 283 GLFW_KEY_PAUSE = 284 GLFW_KEY_F1 = 290 GLFW_KEY_F2 = 291 GLFW_KEY_F3 = 292 GLFW_KEY_F4 = 293 GLFW_KEY_F5 = 294 GLFW_KEY_F6 = 295 GLFW_KEY_F7 = 296 GLFW_KEY_F8 = 297 GLFW_KEY_F9 = 298 GLFW_KEY_F10 = 299 GLFW_KEY_F11 = 300 GLFW_KEY_F12 = 301 GLFW_KEY_F13 = 302 GLFW_KEY_F14 = 303 GLFW_KEY_F15 = 304 GLFW_KEY_F16 = 305 GLFW_KEY_F17 = 306 GLFW_KEY_F18 = 307 GLFW_KEY_F19 = 308 GLFW_KEY_F20 = 309 GLFW_KEY_F21 = 310 GLFW_KEY_F22 = 311 GLFW_KEY_F23 = 312 GLFW_KEY_F24 = 313 GLFW_KEY_F25 = 314 GLFW_KEY_KP_0 = 320 GLFW_KEY_KP_1 = 321 GLFW_KEY_KP_2 = 322 GLFW_KEY_KP_3 = 323 GLFW_KEY_KP_4 = 324 GLFW_KEY_KP_5 = 325 GLFW_KEY_KP_6 = 326 GLFW_KEY_KP_7 = 327 GLFW_KEY_KP_8 = 328 GLFW_KEY_KP_9 = 329 GLFW_KEY_KP_DECIMAL = 330 GLFW_KEY_KP_DIVIDE = 331 GLFW_KEY_KP_MULTIPLY = 332 GLFW_KEY_KP_SUBTRACT = 333 GLFW_KEY_KP_ADD = 334 GLFW_KEY_KP_ENTER = 335 GLFW_KEY_KP_EQUAL = 336 GLFW_KEY_LEFT_SHIFT = 340 GLFW_KEY_LEFT_CONTROL = 341 GLFW_KEY_LEFT_ALT = 342 GLFW_KEY_LEFT_SUPER = 343 GLFW_KEY_RIGHT_SHIFT = 344 GLFW_KEY_RIGHT_CONTROL = 345 GLFW_KEY_RIGHT_ALT = 346 GLFW_KEY_RIGHT_SUPER = 347 GLFW_KEY_MENU = 348 GLFW_KEY_LAST = GLFW_KEY_MENU # --- Modifiers --------------------------------------------------------------- GLFW_MOD_SHIFT = 0x0001 GLFW_MOD_CONTROL = 0x0002 GLFW_MOD_ALT = 0x0004 GLFW_MOD_SUPER = 0x0008 # --- Mouse ------------------------------------------------------------------- GLFW_MOUSE_BUTTON_1 = 0 GLFW_MOUSE_BUTTON_2 = 1 GLFW_MOUSE_BUTTON_3 = 2 GLFW_MOUSE_BUTTON_4 = 3 GLFW_MOUSE_BUTTON_5 = 4 GLFW_MOUSE_BUTTON_6 = 5 GLFW_MOUSE_BUTTON_7 = 6 GLFW_MOUSE_BUTTON_8 = 7 GLFW_MOUSE_BUTTON_LAST = GLFW_MOUSE_BUTTON_8 GLFW_MOUSE_BUTTON_LEFT = GLFW_MOUSE_BUTTON_1 GLFW_MOUSE_BUTTON_RIGHT = GLFW_MOUSE_BUTTON_2 GLFW_MOUSE_BUTTON_MIDDLE = GLFW_MOUSE_BUTTON_3 # --- Joystick ---------------------------------------------------------------- GLFW_JOYSTICK_1 = 0 GLFW_JOYSTICK_2 = 1 GLFW_JOYSTICK_3 = 2 GLFW_JOYSTICK_4 = 3 GLFW_JOYSTICK_5 = 4 GLFW_JOYSTICK_6 = 5 GLFW_JOYSTICK_7 = 6 GLFW_JOYSTICK_8 = 7 GLFW_JOYSTICK_9 = 8 GLFW_JOYSTICK_10 = 9 GLFW_JOYSTICK_11 = 10 GLFW_JOYSTICK_12 = 11 GLFW_JOYSTICK_13 = 12 GLFW_JOYSTICK_14 = 13 GLFW_JOYSTICK_15 = 14 GLFW_JOYSTICK_16 = 15 GLFW_JOYSTICK_LAST = GLFW_JOYSTICK_16 # --- Error codes ------------------------------------------------------------- GLFW_NOT_INITIALIZED = 0x00010001 GLFW_NO_CURRENT_CONTEXT = 0x00010002 GLFW_INVALID_ENUM = 0x00010003 GLFW_INVALID_VALUE = 0x00010004 GLFW_OUT_OF_MEMORY = 0x00010005 GLFW_API_UNAVAILABLE = 0x00010006 GLFW_VERSION_UNAVAILABLE = 0x00010007 GLFW_PLATFORM_ERROR = 0x00010008 GLFW_FORMAT_UNAVAILABLE = 0x00010009 # --- GLFW_FOCUSED = 0x00020001 GLFW_ICONIFIED = 0x00020002 GLFW_RESIZABLE = 0x00020003 GLFW_VISIBLE = 0x00020004 GLFW_DECORATED = 0x00020005 GLFW_AUTO_ICONIFY = 0x00020006 GLFW_FLOATING = 0x00020007 # --- GLFW_RED_BITS = 0x00021001 GLFW_GREEN_BITS = 0x00021002 GLFW_BLUE_BITS = 0x00021003 GLFW_ALPHA_BITS = 0x00021004 GLFW_DEPTH_BITS = 0x00021005 GLFW_STENCIL_BITS = 0x00021006 GLFW_ACCUM_RED_BITS = 0x00021007 GLFW_ACCUM_GREEN_BITS = 0x00021008 GLFW_ACCUM_BLUE_BITS = 0x00021009 GLFW_ACCUM_ALPHA_BITS = 0x0002100A GLFW_AUX_BUFFERS = 0x0002100B GLFW_STEREO = 0x0002100C GLFW_SAMPLES = 0x0002100D GLFW_SRGB_CAPABLE = 0x0002100E GLFW_REFRESH_RATE = 0x0002100F # --- GLFW_CLIENT_API = 0x00022001 GLFW_CONTEXT_VERSION_MAJOR = 0x00022002 GLFW_CONTEXT_VERSION_MINOR = 0x00022003 GLFW_CONTEXT_REVISION = 0x00022004 GLFW_CONTEXT_ROBUSTNESS = 0x00022005 GLFW_OPENGL_FORWARD_COMPAT = 0x00022006 GLFW_OPENGL_DEBUG_CONTEXT = 0x00022007 GLFW_OPENGL_PROFILE = 0x00022008 # --- GLFW_OPENGL_API = 0x00030001 GLFW_OPENGL_ES_API = 0x00030002 # --- GLFW_NO_ROBUSTNESS = 0 GLFW_NO_RESET_NOTIFICATION = 0x00031001 GLFW_LOSE_CONTEXT_ON_RESET = 0x00031002 # --- GLFW_OPENGL_ANY_PROFILE = 0 GLFW_OPENGL_CORE_PROFILE = 0x00032001 GLFW_OPENGL_COMPAT_PROFILE = 0x00032002 # --- GLFW_CURSOR = 0x00033001 GLFW_STICKY_KEYS = 0x00033002 GLFW_STICKY_MOUSE_BUTTONS = 0x00033003 # --- GLFW_CURSOR_NORMAL = 0x00034001 GLFW_CURSOR_HIDDEN = 0x00034002 GLFW_CURSOR_DISABLED = 0x00034003 # --- GLFW_CONNECTED = 0x00040001 GLFW_DISCONNECTED = 0x00040002 # --- Structures -------------------------------------------------------------- class GLFWvidmode(Structure): _fields_ = [ ('width', c_int), ('height', c_int), ('redBits', c_int), ('greenBits', c_int), ('blueBits', c_int), ('refreshRate', c_int) ] class GLFWgammaramp(Structure): _fields_ = [ ('red', POINTER(c_ushort)), ('green', POINTER(c_ushort)), ('blue', POINTER(c_ushort)), ('size', c_int) ] class GLFWwindow(Structure): pass class GLFWmonitor(Structure): pass # --- Callbacks --------------------------------------------------------------- errorfun = CFUNCTYPE(None, c_int, c_char_p) windowposfun = CFUNCTYPE(None, POINTER(GLFWwindow), c_int, c_int) windowsizefun = CFUNCTYPE(None, POINTER(GLFWwindow), c_int, c_int) windowclosefun = CFUNCTYPE(None, POINTER(GLFWwindow)) windowrefreshfun = CFUNCTYPE(None, POINTER(GLFWwindow)) windowfocusfun = CFUNCTYPE(None, POINTER(GLFWwindow), c_int) windowiconifyfun = CFUNCTYPE(None, POINTER(GLFWwindow), c_int) framebuffersizefun = CFUNCTYPE(None, POINTER(GLFWwindow), c_int, c_int) mousebuttonfun = CFUNCTYPE(None, POINTER(GLFWwindow), c_int, c_int, c_int) cursorposfun = CFUNCTYPE(None, POINTER(GLFWwindow), c_double, c_double) cursorenterfun = CFUNCTYPE(None, POINTER(GLFWwindow), c_int) scrollfun = CFUNCTYPE(None, POINTER(GLFWwindow), c_double, c_double) keyfun = CFUNCTYPE(None, POINTER(GLFWwindow), c_int, c_int, c_int, c_int) charfun = CFUNCTYPE(None, POINTER(GLFWwindow), c_uint) monitorfun = CFUNCTYPE(None, POINTER(GLFWmonitor), c_int) # --- Init -------------------------------------------------------------------- glfwInit = _glfw.glfwInit glfwTerminate = _glfw.glfwTerminate #glfwGetVersion = _glfw.glfwGetVersion # --- Error ------------------------------------------------------------------- #glfwSetErrorCallback = _glfw.glfwSetErrorCallback # --- Monitor ----------------------------------------------------------------- # glfwGetMonitors = _glfw.glfwGetMonitors # glfwGetMonitors.restype = POINTER(GLFWmonitor) glfwGetPrimaryMonitor = _glfw.glfwGetPrimaryMonitor # glfwGetMonitorPos = _glfw.glfwGetMonitorPos # glfwGetMonitorPhysicalSize = _glfw.glfwGetMonitorPhysicalSize glfwGetMonitorName = _glfw.glfwGetMonitorName glfwGetMonitorName.restype = c_char_p # glfwSetMonitorCallback = _glfw.glfwSetMonitorCallback # glfwGetVideoModes = _glfw.glfwGetVideoModes # glfwGetVideoMode = _glfw.glfwGetVideoMode # --- Gama -------------------------------------------------------------------- glfwSetGamma = _glfw.glfwSetGamma # glfwGetGammaRamp = _glfw.glfwGetGammaRamp # glfwSetGammaRamp = _glfw.glfwSetGammaRamp # --- Window ------------------------------------------------------------------ glfwDefaultWindowHints = _glfw.glfwDefaultWindowHints glfwWindowHint = _glfw.glfwWindowHint # glfwCreateWindow = _glfw.glfwCreateWindow # glfwDestroyWindow = _glfw.glfwDestroyWindow glfwWindowShouldClose = _glfw.glfwWindowShouldClose glfwSetWindowShouldClose = _glfw.glfwSetWindowShouldClose glfwSetWindowTitle = _glfw.glfwSetWindowTitle # glfwGetWindowPos = _glfw.glfwGetWindowPos glfwSetWindowPos = _glfw.glfwSetWindowPos # glfwGetWindowSize = _glfw.glfwGetWindowSize glfwSetWindowSize = _glfw.glfwSetWindowSize # glfwGetFramebufferSize = _glfw.glfwGetFramebufferSize glfwIconifyWindow = _glfw.glfwIconifyWindow glfwRestoreWindow = _glfw.glfwRestoreWindow glfwShowWindow = _glfw.glfwShowWindow glfwHideWindow = _glfw.glfwHideWindow glfwGetWindowMonitor = _glfw.glfwGetWindowMonitor glfwGetWindowAttrib = _glfw.glfwGetWindowAttrib glfwSetWindowUserPointer = _glfw.glfwSetWindowUserPointer glfwGetWindowUserPointer = _glfw.glfwGetWindowUserPointer # glfwSetWindowPosCallback = _glfw.glfwSetWindowPosCallback # glfwSetWindowSizeCallback = _glfw.glfwSetWindowSizeCallback # glfwSetWindowCloseCallback = _glfw.glfwSetWindowCloseCallback # glfwSetWindowRefreshCallback = _glfw.glfwSetWindowRefreshCallback # glfwSetWindowFocusCallback = _glfw.glfwSetWindowFocusCallback # glfwSetWindowIconifyCallback = _glfw.glfwSetWindowIconifyCallback # glfwSetFramebufferSizeCallback = _glfw.glfwSetFramebufferSizeCallback glfwPollEvents = _glfw.glfwPollEvents glfwWaitEvents = _glfw.glfwWaitEvents # --- Input ------------------------------------------------------------------- glfwGetInputMode = _glfw.glfwGetInputMode glfwSetInputMode = _glfw.glfwSetInputMode glfwGetKey = _glfw.glfwGetKey glfwGetMouseButton = _glfw.glfwGetMouseButton # glfwGetCursorPos = _glfw.glfwGetCursorPos glfwSetCursorPos = _glfw.glfwSetCursorPos # glfwSetKeyCallback = _glfw.glfwSetKeyCallback # glfwSetCharCallback = _glfw.glfwSetCharCallback # glfwSetMouseButtonCallback = _glfw.glfwSetMouseButtonCallback # glfwSetCursorPosCallback = _glfw.glfwSetCursorPosCallback # glfwSetCursorEnterCallback = _glfw.glfwSetCursorEnterCallback # glfwSetScrollCallback = _glfw.glfwSetScrollCallback glfwJoystickPresent = _glfw.glfwJoystickPresent # glfwGetJoystickAxes = _glfw.glfwGetJoystickAxes # glfwGetJoystickButtons = _glfw.glfwGetJoystickButtons glfwGetJoystickName = _glfw.glfwGetJoystickName glfwGetJoystickName.restype = c_char_p # --- Clipboard --------------------------------------------------------------- glfwSetClipboardString = _glfw.glfwSetClipboardString glfwGetClipboardString = _glfw.glfwGetClipboardString glfwGetClipboardString.restype = c_char_p # --- Timer ------------------------------------------------------------------- glfwGetTime = _glfw.glfwGetTime glfwGetTime.restype = c_double glfwSetTime = _glfw.glfwSetTime # --- Context ----------------------------------------------------------------- glfwMakeContextCurrent = _glfw.glfwMakeContextCurrent glfwGetCurrentContext = _glfw.glfwGetCurrentContext glfwSwapBuffers = _glfw.glfwSwapBuffers glfwSwapInterval = _glfw.glfwSwapInterval glfwExtensionSupported = _glfw.glfwExtensionSupported glfwGetProcAddress = _glfw.glfwGetProcAddress # --- Pythonizer -------------------------------------------------------------- # This keeps track of current windows __windows__ = [] __destroyed__ = [] # This is to prevent garbage collection on callbacks __c_callbacks__ = {} __py_callbacks__ = {} __c_error_callback__ = None def glfwCreateWindow(width=640, height=480, title="GLFW Window", monitor=None, share=None): _glfw.glfwCreateWindow.restype = POINTER(GLFWwindow) window = _glfw.glfwCreateWindow(int(width), int(height), title.encode('utf-8'), monitor, share) __windows__.append(window) __destroyed__.append(False) index = __windows__.index(window) __c_callbacks__[index] = {} __py_callbacks__[index] = { 'errorfun' : None, 'monitorfun' : None, 'windowposfun' : None, 'windowsizefun' : None, 'windowclosefun' : None, 'windowrefreshfun' : None, 'windowfocusfun' : None, 'windowiconifyfun' : None, 'framebuffersizefun' : None, 'keyfun' : None, 'charfun' : None, 'mousebuttonfun' : None, 'cursorposfun' : None, 'cursorenterfun' : None, 'scrollfun' : None } return window def glfwDestroyWindow(window): index = __windows__.index(window) if not __destroyed__[index]: # We do not delete window from the list (or it would impact numbering) __windows__[index] = None _glfw.glfwDestroyWindow(window) del __c_callbacks__[index] del __py_callbacks__[index] __destroyed__[index] = True def glfwGetWindowPos(window): xpos, ypos = c_int(0), c_int(0) _glfw.glfwGetWindowPos(window, byref(xpos), byref(ypos)) return xpos.value, ypos.value def glfwGetCursorPos(window): xpos, ypos = c_double(0), c_double(0) _glfw.glfwGetCursorPos(window, byref(xpos), byref(ypos)) return int(xpos.value), int(ypos.value) def glfwGetWindowSize(window): width, height = c_int(0), c_int(0) _glfw.glfwGetWindowSize(window, byref(width), byref(height)) return width.value, height.value def glfwGetFramebufferSize(window): width, height = c_int(0), c_int(0) _glfw.glfwGetFramebufferSize(window, byref(width), byref(height)) return width.value, height.value def glfwGetMonitors(): count = c_int(0) _glfw.glfwGetMonitors.restype = POINTER(POINTER(GLFWmonitor)) c_monitors = _glfw.glfwGetMonitors( byref(count) ) return [c_monitors[i] for i in range(count.value)] def glfwGetVideoModes(monitor): count = c_int(0) _glfw.glfwGetVideoModes.restype = POINTER(GLFWvidmode) c_modes = _glfw.glfwGetVideoModes( monitor, byref(count) ) modes = [] for i in range(count.value): modes.append( (c_modes[i].width, c_modes[i].height, c_modes[i].redBits, c_modes[i].blueBits, c_modes[i].greenBits, c_modes[i].refreshRate ) ) return modes def glfwGetMonitorPos(monitor): xpos, ypos = c_int(0), c_int(0) _glfw.glfwGetMonitorPos(monitor, byref(xpos), byref(ypos)) return xpos.value, ypos.value def glfwGetMonitorPhysicalSize(monitor): width, height = c_int(0), c_int(0) _glfw.glfwGetMonitorPhysicalSize(monitor, byref(width), byref(height)) return width.value, height.value def glfwGetVideoMode(monitor): _glfw.glfwGetVideoMode.restype = POINTER(GLFWvidmode) c_mode = _glfw.glfwGetVideoMode(monitor).contents return (c_mode.width, c_mode.height, c_mode.redBits, c_mode.blueBits, c_mode.greenBits, c_mode.refreshRate ) def GetGammaRamp(monitor): _glfw.glfwGetGammaRamp.restype = POINTER(GLFWgammaramp) c_gamma = _glfw.glfwGetGammaRamp(monitor).contents gamma = {'red':[], 'green':[], 'blue':[]} if c_gamma: for i in range(c_gamma.size): gamma['red'].append(c_gamma.red[i]) gamma['green'].append(c_gamma.green[i]) gamma['blue'].append(c_gamma.blue[i]) return gamma def glfwGetJoystickAxes(joy): count = c_int(0) _glfw.glfwGetJoystickAxes.restype = POINTER(c_float) c_axes = _glfw.glfwGetJoystickAxes(joy, byref(count)) axes = [c_axes[i].value for i in range(count)] return axes def glfwGetJoystickButtons(joy): count = c_int(0) _glfw.glfwGetJoystickButtons.restype = POINTER(c_int) c_buttons = _glfw.glfwGetJoystickButtons(joy, byref(count)) buttons = [c_buttons[i].value for i in range(count)] return buttons # --- Callbacks --------------------------------------------------------------- def __callback__(name): callback = 'glfwSet%sCallback' % name fun = '%sfun' % name.lower() code = """ def %(callback)s(window, callback = None): index = __windows__.index(window) old_callback = __py_callbacks__[index]['%(fun)s'] __py_callbacks__[index]['%(fun)s'] = callback if callback: callback = %(fun)s(callback) __c_callbacks__[index]['%(fun)s'] = callback _glfw.%(callback)s(window, callback) return old_callback""" % {'callback': callback, 'fun': fun} return code exec(__callback__('Monitor')) exec(__callback__('WindowPos')) exec(__callback__('WindowSize')) exec(__callback__('WindowClose')) exec(__callback__('WindowRefresh')) exec(__callback__('WindowFocus')) exec(__callback__('WindowIconify')) exec(__callback__('FramebufferSize')) exec(__callback__('Key')) exec(__callback__('Char')) exec(__callback__('MouseButton')) exec(__callback__('CursorPos')) exec(__callback__('Scroll')) # Error callback does not take window parameter def glfwSetErrorCallback(callback = None): global __c_error_callback__ __c_error_callback__ = errorfun(callback) _glfw.glfwSetErrorCallback(__c_error_callback__)�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/ext/ipy_inputhook.py��������������������������������������������������������������0000664�0001750�0001750�00000024415�12466440051�021620� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# coding: utf-8 """ Inputhook management for GUI event loop integration. """ #----------------------------------------------------------------------------- # Source: # https://github.com/ipython/ipython/commits/master/IPython/lib/inputhook.py # Revision: # 29a0deb452d4fa7f59edb7e059c1a46ceb5a124d # Modifications: # Removed dependencies and other backends for VisPy. #----------------------------------------------------------------------------- # Copyright (C) 2008-2011 The IPython Development Team # # Distributed under the terms of the BSD License. The full license is in # the file COPYING, distributed as part of this software. #----------------------------------------------------------------------------- #----------------------------------------------------------------------------- # Imports #----------------------------------------------------------------------------- try: import ctypes except ImportError: ctypes = None except SystemError: # IronPython issue, 2/8/2014 ctypes = None import os import sys from distutils.version import LooseVersion as V #----------------------------------------------------------------------------- # Constants #----------------------------------------------------------------------------- # Constants for identifying the GUI toolkits. GUI_WX = 'wx' GUI_QT = 'qt' GUI_QT4 = 'qt4' GUI_GTK = 'gtk' GUI_TK = 'tk' GUI_OSX = 'osx' GUI_PYGLET = 'pyglet' GUI_GTK3 = 'gtk3' GUI_NONE = 'none' # i.e. disable #----------------------------------------------------------------------------- # Utilities #----------------------------------------------------------------------------- def _stdin_ready_posix(): """Return True if there's something to read on stdin (posix version).""" infds, outfds, erfds = select.select([sys.stdin],[],[],0) return bool(infds) def _stdin_ready_nt(): """Return True if there's something to read on stdin (nt version).""" return msvcrt.kbhit() def _stdin_ready_other(): """Return True, assuming there's something to read on stdin.""" return True # def _ignore_CTRL_C_posix(): """Ignore CTRL+C (SIGINT).""" signal.signal(signal.SIGINT, signal.SIG_IGN) def _allow_CTRL_C_posix(): """Take CTRL+C into account (SIGINT).""" signal.signal(signal.SIGINT, signal.default_int_handler) def _ignore_CTRL_C_other(): """Ignore CTRL+C (not implemented).""" pass def _allow_CTRL_C_other(): """Take CTRL+C into account (not implemented).""" pass if os.name == 'posix': import select import signal stdin_ready = _stdin_ready_posix ignore_CTRL_C = _ignore_CTRL_C_posix allow_CTRL_C = _allow_CTRL_C_posix elif os.name == 'nt': import msvcrt stdin_ready = _stdin_ready_nt ignore_CTRL_C = _ignore_CTRL_C_other allow_CTRL_C = _allow_CTRL_C_other else: stdin_ready = _stdin_ready_other ignore_CTRL_C = _ignore_CTRL_C_other allow_CTRL_C = _allow_CTRL_C_other #----------------------------------------------------------------------------- # Main InputHookManager class #----------------------------------------------------------------------------- class InputHookManager(object): """Manage PyOS_InputHook for different GUI toolkits. This class installs various hooks under ``PyOSInputHook`` to handle GUI event loop integration. """ def __init__(self): if ctypes is None: print("IPython GUI event loop requires ctypes, %gui will not be available") return self.PYFUNC = ctypes.PYFUNCTYPE(ctypes.c_int) self.guihooks = {} self.aliases = {} self.apps = {} self._reset() def _reset(self): self._callback_pyfunctype = None self._callback = None self._installed = False self._current_gui = None def get_pyos_inputhook(self): """Return the current PyOS_InputHook as a ctypes.c_void_p.""" return ctypes.c_void_p.in_dll(ctypes.pythonapi,"PyOS_InputHook") def get_pyos_inputhook_as_func(self): """Return the current PyOS_InputHook as a ctypes.PYFUNCYPE.""" return self.PYFUNC.in_dll(ctypes.pythonapi,"PyOS_InputHook") def set_inputhook(self, callback): """Set PyOS_InputHook to callback and return the previous one.""" # On platforms with 'readline' support, it's all too likely to # have a KeyboardInterrupt signal delivered *even before* an # initial ``try:`` clause in the callback can be executed, so # we need to disable CTRL+C in this situation. ignore_CTRL_C() self._callback = callback self._callback_pyfunctype = self.PYFUNC(callback) pyos_inputhook_ptr = self.get_pyos_inputhook() original = self.get_pyos_inputhook_as_func() pyos_inputhook_ptr.value = \ ctypes.cast(self._callback_pyfunctype, ctypes.c_void_p).value self._installed = True return original def clear_inputhook(self, app=None): """Set PyOS_InputHook to NULL and return the previous one. Parameters ---------- app : optional, ignored This parameter is allowed only so that clear_inputhook() can be called with a similar interface as all the ``enable_*`` methods. But the actual value of the parameter is ignored. This uniform interface makes it easier to have user-level entry points in the main IPython app like :meth:`enable_gui`.""" pyos_inputhook_ptr = self.get_pyos_inputhook() original = self.get_pyos_inputhook_as_func() pyos_inputhook_ptr.value = ctypes.c_void_p(None).value allow_CTRL_C() self._reset() return original def clear_app_refs(self, gui=None): """Clear IPython's internal reference to an application instance. Whenever we create an app for a user on qt4 or wx, we hold a reference to the app. This is needed because in some cases bad things can happen if a user doesn't hold a reference themselves. This method is provided to clear the references we are holding. Parameters ---------- gui : None or str If None, clear all app references. If ('wx', 'qt4') clear the app for that toolkit. References are not held for gtk or tk as those toolkits don't have the notion of an app. """ if gui is None: self.apps = {} elif gui in self.apps: del self.apps[gui] def register(self, toolkitname, *aliases): """Register a class to provide the event loop for a given GUI. This is intended to be used as a class decorator. It should be passed the names with which to register this GUI integration. The classes themselves should subclass :class:`InputHookBase`. :: @inputhook_manager.register('qt') class QtInputHook(InputHookBase): def enable(self, app=None): ... """ def decorator(cls): inst = cls(self) self.guihooks[toolkitname] = inst for a in aliases: self.aliases[a] = toolkitname return cls return decorator def current_gui(self): """Return a string indicating the currently active GUI or None.""" return self._current_gui def enable_gui(self, gui=None, app=None): """Switch amongst GUI input hooks by name. This is a higher level method than :meth:`set_inputhook` - it uses the GUI name to look up a registered object which enables the input hook for that GUI. Parameters ---------- gui : optional, string or None If None (or 'none'), clears input hook, otherwise it must be one of the recognized GUI names (see ``GUI_*`` constants in module). app : optional, existing application object. For toolkits that have the concept of a global app, you can supply an existing one. If not given, the toolkit will be probed for one, and if none is found, a new one will be created. Note that GTK does not have this concept, and passing an app if ``gui=="GTK"`` will raise an error. Returns ------- The output of the underlying gui switch routine, typically the actual PyOS_InputHook wrapper object or the GUI toolkit app created, if there was one. """ if gui in (None, GUI_NONE): return self.disable_gui() if gui in self.aliases: return self.enable_gui(self.aliases[gui], app) try: gui_hook = self.guihooks[gui] except KeyError: e = "Invalid GUI request {!r}, valid ones are: {}" raise ValueError(e.format(gui, ', '.join(self.guihooks))) self._current_gui = gui app = gui_hook.enable(app) if app is not None: app._in_event_loop = True self.apps[gui] = app return app def disable_gui(self): """Disable GUI event loop integration. If an application was registered, this sets its ``_in_event_loop`` attribute to False. It then calls :meth:`clear_inputhook`. """ gui = self._current_gui if gui in self.apps: self.apps[gui]._in_event_loop = False return self.clear_inputhook() class InputHookBase(object): """Base class for input hooks for specific toolkits. Subclasses should define an :meth:`enable` method with one argument, ``app``, which will either be an instance of the toolkit's application class, or None. They may also define a :meth:`disable` method with no arguments. """ def __init__(self, manager): self.manager = manager def disable(self): pass inputhook_manager = InputHookManager() @inputhook_manager.register('osx') class NullInputHook(InputHookBase): """A null inputhook that doesn't need to do anything""" def enable(self, app=None): pass clear_inputhook = inputhook_manager.clear_inputhook set_inputhook = inputhook_manager.set_inputhook current_gui = inputhook_manager.current_gui clear_app_refs = inputhook_manager.clear_app_refs enable_gui = inputhook_manager.enable_gui disable_gui = inputhook_manager.disable_gui register = inputhook_manager.register guis = inputhook_manager.guihooks���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/ext/png.py������������������������������������������������������������������������0000664�0001750�0001750�00000243127�12375431476�017520� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env python # png.py - PNG encoder/decoder in pure Python # # Copyright (C) 2006 Johann C. Rocholl # Portions Copyright (C) 2009 David Jones # And probably portions Copyright (C) 2006 Nicko van Someren # # Original concept by Johann C. Rocholl. # # LICENCE (MIT) # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files # (the "Software"), to deal in the Software without restriction, # including without limitation the rights to use, copy, modify, merge, # publish, distribute, sublicense, and/or sell copies of the Software, # and to permit persons to whom the Software is furnished to do so, # subject to the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. # Adapted for vispy # http://www.python.org/doc/2.2.3/whatsnew/node5.html from __future__ import generators __version__ = "0.0.17" from array import array import math # http://www.python.org/doc/2.4.4/lib/module-operator.html import operator import struct import zlib # http://www.python.org/doc/2.4.4/lib/module-warnings.html import warnings from .six.moves import map as imap from .six import string_types import itertools __all__ = ['Image', 'Reader', 'Writer', 'write_chunks', 'from_array'] # The PNG signature. # http://www.w3.org/TR/PNG/#5PNG-file-signature _signature = struct.pack('8B', 137, 80, 78, 71, 13, 10, 26, 10) _adam7 = ((0, 0, 8, 8), (4, 0, 8, 8), (0, 4, 4, 8), (2, 0, 4, 4), (0, 2, 2, 4), (1, 0, 2, 2), (0, 1, 1, 2)) def group(s, n): # See http://www.python.org/doc/2.6/library/functions.html#zip return zip(*[iter(s)]*n) def isarray(x): """Same as ``isinstance(x, array)`` except on Python 2.2, where it always returns ``False``. This helps PyPNG work on Python 2.2. """ try: return isinstance(x, array) except TypeError: # Because on Python 2.2 array.array is not a type. return False def tostring(row): """ Python3 definition, array.tostring() is deprecated in Python3 """ return row.tobytes() # Conditionally convert to bytes. Works on Python 2 and Python 3. try: bytes('', 'ascii') def strtobytes(x): return bytes(x, 'iso8859-1') def bytestostr(x): return str(x, 'iso8859-1') except (NameError, TypeError): # We get NameError when bytes() does not exist (most Python # 2.x versions), and TypeError when bytes() exists but is on # Python 2.x (when it is an alias for str() and takes at most # one argument). strtobytes = str bytestostr = str def interleave_planes(ipixels, apixels, ipsize, apsize): """ Interleave (colour) planes, e.g. RGB + A = RGBA. Return an array of pixels consisting of the `ipsize` elements of data from each pixel in `ipixels` followed by the `apsize` elements of data from each pixel in `apixels`. Conventionally `ipixels` and `apixels` are byte arrays so the sizes are bytes, but it actually works with any arrays of the same type. The returned array is the same type as the input arrays which should be the same type as each other. """ itotal = len(ipixels) atotal = len(apixels) newtotal = itotal + atotal newpsize = ipsize + apsize # Set up the output buffer # See http://www.python.org/doc/2.4.4/lib/module-array.html#l2h-1356 out = array(ipixels.typecode) # It's annoying that there is no cheap way to set the array size :-( out.extend(ipixels) out.extend(apixels) # Interleave in the pixel data for i in range(ipsize): out[i:newtotal:newpsize] = ipixels[i:itotal:ipsize] for i in range(apsize): out[i+ipsize:newtotal:newpsize] = apixels[i:atotal:apsize] return out def check_palette(palette): """Check a palette argument (to the :class:`Writer` class) for validity. Returns the palette as a list if okay; raises an exception otherwise. """ # None is the default and is allowed. if palette is None: return None p = list(palette) if not (0 < len(p) <= 256): raise ValueError("a palette must have between 1 and 256 entries") seen_triple = False for i,t in enumerate(p): if len(t) not in (3,4): raise ValueError( "palette entry %d: entries must be 3- or 4-tuples." % i) if len(t) == 3: seen_triple = True if seen_triple and len(t) == 4: raise ValueError( "palette entry %d: all 4-tuples must precede all 3-tuples" % i) for x in t: if int(x) != x or not(0 <= x <= 255): raise ValueError( "palette entry %d: values must be integer: 0 <= x <= 255" % i) return p def check_sizes(size, width, height): """Check that these arguments, in supplied, are consistent. Return a (width, height) pair. """ if not size: return width, height if len(size) != 2: raise ValueError( "size argument should be a pair (width, height)") if width is not None and width != size[0]: raise ValueError( "size[0] (%r) and width (%r) should match when both are used." % (size[0], width)) if height is not None and height != size[1]: raise ValueError( "size[1] (%r) and height (%r) should match when both are used." % (size[1], height)) return size def check_color(c, greyscale, which): """Checks that a colour argument for transparent or background options is the right form. Returns the colour (which, if it's a bar integer, is "corrected" to a 1-tuple). """ if c is None: return c if greyscale: try: l = len(c) except TypeError: c = (c,) if len(c) != 1: raise ValueError("%s for greyscale must be 1-tuple" % which) if not isinteger(c[0]): raise ValueError( "%s colour for greyscale must be integer" % which) else: if not (len(c) == 3 and isinteger(c[0]) and isinteger(c[1]) and isinteger(c[2])): raise ValueError( "%s colour must be a triple of integers" % which) return c class Error(Exception): def __str__(self): return self.__class__.__name__ + ': ' + ' '.join(self.args) class FormatError(Error): """Problem with input file format. In other words, PNG file does not conform to the specification in some way and is invalid. """ class ChunkError(FormatError): pass class Writer: """ PNG encoder in pure Python. """ def __init__(self, width=None, height=None, size=None, greyscale=False, alpha=False, bitdepth=8, palette=None, transparent=None, background=None, gamma=None, compression=None, interlace=False, bytes_per_sample=None, # deprecated planes=None, colormap=None, maxval=None, chunk_limit=2**20): """ Create a PNG encoder object. Arguments: width, height Image size in pixels, as two separate arguments. size Image size (w,h) in pixels, as single argument. greyscale Input data is greyscale, not RGB. alpha Input data has alpha channel (RGBA or LA). bitdepth Bit depth: from 1 to 16. palette Create a palette for a colour mapped image (colour type 3). transparent Specify a transparent colour (create a ``tRNS`` chunk). background Specify a default background colour (create a ``bKGD`` chunk). gamma Specify a gamma value (create a ``gAMA`` chunk). compression zlib compression level: 0 (none) to 9 (more compressed); default: -1 or None. interlace Create an interlaced image. chunk_limit Write multiple ``IDAT`` chunks to save memory. The image size (in pixels) can be specified either by using the `width` and `height` arguments, or with the single `size` argument. If `size` is used it should be a pair (*width*, *height*). `greyscale` and `alpha` are booleans that specify whether an image is greyscale (or colour), and whether it has an alpha channel (or not). `bitdepth` specifies the bit depth of the source pixel values. Each source pixel value must be an integer between 0 and ``2**bitdepth-1``. For example, 8-bit images have values between 0 and 255. PNG only stores images with bit depths of 1,2,4,8, or 16. When `bitdepth` is not one of these values, the next highest valid bit depth is selected, and an ``sBIT`` (significant bits) chunk is generated that specifies the original precision of the source image. In this case the supplied pixel values will be rescaled to fit the range of the selected bit depth. The details of which bit depth / colour model combinations the PNG file format supports directly, are somewhat arcane (refer to the PNG specification for full details). Briefly: "small" bit depths (1,2,4) are only allowed with greyscale and colour mapped images; colour mapped images cannot have bit depth 16. For colour mapped images (in other words, when the `palette` argument is specified) the `bitdepth` argument must match one of the valid PNG bit depths: 1, 2, 4, or 8. (It is valid to have a PNG image with a palette and an ``sBIT`` chunk, but the meaning is slightly different; it would be awkward to press the `bitdepth` argument into service for this.) The `palette` option, when specified, causes a colour mapped image to be created: the PNG colour type is set to 3; greyscale must not be set; alpha must not be set; transparent must not be set; the bit depth must be 1,2,4, or 8. When a colour mapped image is created, the pixel values are palette indexes and the `bitdepth` argument specifies the size of these indexes (not the size of the colour values in the palette). The palette argument value should be a sequence of 3- or 4-tuples. 3-tuples specify RGB palette entries; 4-tuples specify RGBA palette entries. If both 4-tuples and 3-tuples appear in the sequence then all the 4-tuples must come before all the 3-tuples. A ``PLTE`` chunk is created; if there are 4-tuples then a ``tRNS`` chunk is created as well. The ``PLTE`` chunk will contain all the RGB triples in the same sequence; the ``tRNS`` chunk will contain the alpha channel for all the 4-tuples, in the same sequence. Palette entries are always 8-bit. If specified, the `transparent` and `background` parameters must be a tuple with three integer values for red, green, blue, or a simple integer (or singleton tuple) for a greyscale image. If specified, the `gamma` parameter must be a positive number (generally, a float). A ``gAMA`` chunk will be created. Note that this will not change the values of the pixels as they appear in the PNG file, they are assumed to have already been converted appropriately for the gamma specified. The `compression` argument specifies the compression level to be used by the ``zlib`` module. Values from 1 to 9 specify compression, with 9 being "more compressed" (usually smaller and slower, but it doesn't always work out that way). 0 means no compression. -1 and ``None`` both mean that the default level of compession will be picked by the ``zlib`` module (which is generally acceptable). If `interlace` is true then an interlaced image is created (using PNG's so far only interace method, *Adam7*). This does not affect how the pixels should be presented to the encoder, rather it changes how they are arranged into the PNG file. On slow connexions interlaced images can be partially decoded by the browser to give a rough view of the image that is successively refined as more image data appears. .. note :: Enabling the `interlace` option requires the entire image to be processed in working memory. `chunk_limit` is used to limit the amount of memory used whilst compressing the image. In order to avoid using large amounts of memory, multiple ``IDAT`` chunks may be created. """ # At the moment the `planes` argument is ignored; # its purpose is to act as a dummy so that # ``Writer(x, y, **info)`` works, where `info` is a dictionary # returned by Reader.read and friends. # Ditto for `colormap`. width, height = check_sizes(size, width, height) del size if width <= 0 or height <= 0: raise ValueError("width and height must be greater than zero") if not isinteger(width) or not isinteger(height): raise ValueError("width and height must be integers") # http://www.w3.org/TR/PNG/#7Integers-and-byte-order if width > 2**32-1 or height > 2**32-1: raise ValueError("width and height cannot exceed 2**32-1") if alpha and transparent is not None: raise ValueError( "transparent colour not allowed with alpha channel") if bytes_per_sample is not None: warnings.warn('please use bitdepth instead of bytes_per_sample', DeprecationWarning) if bytes_per_sample not in (0.125, 0.25, 0.5, 1, 2): raise ValueError( "bytes per sample must be .125, .25, .5, 1, or 2") bitdepth = int(8*bytes_per_sample) del bytes_per_sample if not isinteger(bitdepth) or bitdepth < 1 or 16 < bitdepth: raise ValueError("bitdepth (%r) must be a positive integer <= 16" % bitdepth) self.rescale = None if palette: if bitdepth not in (1,2,4,8): raise ValueError("with palette, bitdepth must be 1, 2, 4, or 8") if transparent is not None: raise ValueError("transparent and palette not compatible") if alpha: raise ValueError("alpha and palette not compatible") if greyscale: raise ValueError("greyscale and palette not compatible") else: # No palette, check for sBIT chunk generation. if alpha or not greyscale: if bitdepth not in (8,16): targetbitdepth = (8,16)[bitdepth > 8] self.rescale = (bitdepth, targetbitdepth) bitdepth = targetbitdepth del targetbitdepth else: assert greyscale assert not alpha if bitdepth not in (1,2,4,8,16): if bitdepth > 8: targetbitdepth = 16 elif bitdepth == 3: targetbitdepth = 4 else: assert bitdepth in (5,6,7) targetbitdepth = 8 self.rescale = (bitdepth, targetbitdepth) bitdepth = targetbitdepth del targetbitdepth if bitdepth < 8 and (alpha or not greyscale and not palette): raise ValueError( "bitdepth < 8 only permitted with greyscale or palette") if bitdepth > 8 and palette: raise ValueError( "bit depth must be 8 or less for images with palette") transparent = check_color(transparent, greyscale, 'transparent') background = check_color(background, greyscale, 'background') # It's important that the true boolean values (greyscale, alpha, # colormap, interlace) are converted to bool because Iverson's # convention is relied upon later on. self.width = width self.height = height self.transparent = transparent self.background = background self.gamma = gamma self.greyscale = bool(greyscale) self.alpha = bool(alpha) self.colormap = bool(palette) self.bitdepth = int(bitdepth) self.compression = compression self.chunk_limit = chunk_limit self.interlace = bool(interlace) self.palette = check_palette(palette) self.color_type = 4*self.alpha + 2*(not greyscale) + 1*self.colormap assert self.color_type in (0,2,3,4,6) self.color_planes = (3,1)[self.greyscale or self.colormap] self.planes = self.color_planes + self.alpha # :todo: fix for bitdepth < 8 self.psize = (self.bitdepth/8) * self.planes def make_palette(self): """Create the byte sequences for a ``PLTE`` and if necessary a ``tRNS`` chunk. Returned as a pair (*p*, *t*). *t* will be ``None`` if no ``tRNS`` chunk is necessary. """ p = array('B') t = array('B') for x in self.palette: p.extend(x[0:3]) if len(x) > 3: t.append(x[3]) p = tostring(p) t = tostring(t) if t: return p,t return p,None def write(self, outfile, rows): """Write a PNG image to the output file. `rows` should be an iterable that yields each row in boxed row flat pixel format. The rows should be the rows of the original image, so there should be ``self.height`` rows of ``self.width * self.planes`` values. If `interlace` is specified (when creating the instance), then an interlaced PNG file will be written. Supply the rows in the normal image order; the interlacing is carried out internally. .. note :: Interlacing will require the entire image to be in working memory. """ if self.interlace: fmt = 'BH'[self.bitdepth > 8] a = array(fmt, itertools.chain(*rows)) return self.write_array(outfile, a) nrows = self.write_passes(outfile, rows) if nrows != self.height: raise ValueError( "rows supplied (%d) does not match height (%d)" % (nrows, self.height)) def write_passes(self, outfile, rows, packed=False): """ Write a PNG image to the output file. Most users are expected to find the :meth:`write` or :meth:`write_array` method more convenient. The rows should be given to this method in the order that they appear in the output file. For straightlaced images, this is the usual top to bottom ordering, but for interlaced images the rows should have already been interlaced before passing them to this function. `rows` should be an iterable that yields each row. When `packed` is ``False`` the rows should be in boxed row flat pixel format; when `packed` is ``True`` each row should be a packed sequence of bytes. """ # http://www.w3.org/TR/PNG/#5PNG-file-signature outfile.write(_signature) # http://www.w3.org/TR/PNG/#11IHDR write_chunk(outfile, 'IHDR', struct.pack("!2I5B", self.width, self.height, self.bitdepth, self.color_type, 0, 0, self.interlace)) # See :chunk:order # http://www.w3.org/TR/PNG/#11gAMA if self.gamma is not None: write_chunk(outfile, 'gAMA', struct.pack("!L", int(round(self.gamma*1e5)))) # See :chunk:order # http://www.w3.org/TR/PNG/#11sBIT if self.rescale: write_chunk(outfile, 'sBIT', struct.pack('%dB' % self.planes, *[self.rescale[0]]*self.planes)) # :chunk:order: Without a palette (PLTE chunk), ordering is # relatively relaxed. With one, gAMA chunk must precede PLTE # chunk which must precede tRNS and bKGD. # See http://www.w3.org/TR/PNG/#5ChunkOrdering if self.palette: p,t = self.make_palette() write_chunk(outfile, 'PLTE', p) if t: # tRNS chunk is optional. Only needed if palette entries # have alpha. write_chunk(outfile, 'tRNS', t) # http://www.w3.org/TR/PNG/#11tRNS if self.transparent is not None: if self.greyscale: write_chunk(outfile, 'tRNS', struct.pack("!1H", *self.transparent)) else: write_chunk(outfile, 'tRNS', struct.pack("!3H", *self.transparent)) # http://www.w3.org/TR/PNG/#11bKGD if self.background is not None: if self.greyscale: write_chunk(outfile, 'bKGD', struct.pack("!1H", *self.background)) else: write_chunk(outfile, 'bKGD', struct.pack("!3H", *self.background)) # http://www.w3.org/TR/PNG/#11IDAT if self.compression is not None: compressor = zlib.compressobj(self.compression) else: compressor = zlib.compressobj() # Choose an extend function based on the bitdepth. The extend # function packs/decomposes the pixel values into bytes and # stuffs them onto the data array. data = array('B') if self.bitdepth == 8 or packed: extend = data.extend elif self.bitdepth == 16: # Decompose into bytes def extend(sl): fmt = '!%dH' % len(sl) data.extend(array('B', struct.pack(fmt, *sl))) else: # Pack into bytes assert self.bitdepth < 8 # samples per byte spb = int(8/self.bitdepth) def extend(sl): a = array('B', sl) # Adding padding bytes so we can group into a whole # number of spb-tuples. l = float(len(a)) extra = math.ceil(l / float(spb))*spb - l a.extend([0]*int(extra)) # Pack into bytes l = group(a, spb) l = map(lambda e: reduce(lambda x,y: (x << self.bitdepth) + y, e), l) data.extend(l) if self.rescale: oldextend = extend factor = \ float(2**self.rescale[1]-1) / float(2**self.rescale[0]-1) def extend(sl): oldextend(map(lambda x: int(round(factor*x)), sl)) # Build the first row, testing mostly to see if we need to # changed the extend function to cope with NumPy integer types # (they cause our ordinary definition of extend to fail, so we # wrap it). See # http://code.google.com/p/pypng/issues/detail?id=44 enumrows = enumerate(rows) del rows # First row's filter type. data.append(0) # :todo: Certain exceptions in the call to ``.next()`` or the # following try would indicate no row data supplied. # Should catch. i,row = enumrows.next() try: # If this fails... extend(row) except: # ... try a version that converts the values to int first. # Not only does this work for the (slightly broken) NumPy # types, there are probably lots of other, unknown, "nearly" # int types it works for. def wrapmapint(f): return lambda sl: f(map(int, sl)) extend = wrapmapint(extend) del wrapmapint extend(row) for i,row in enumrows: # Add "None" filter type. Currently, it's essential that # this filter type be used for every scanline as we do not # mark the first row of a reduced pass image; that means we # could accidentally compute the wrong filtered scanline if # we used "up", "average", or "paeth" on such a line. data.append(0) extend(row) if len(data) > self.chunk_limit: compressed = compressor.compress(tostring(data)) if len(compressed): write_chunk(outfile, 'IDAT', compressed) # Because of our very witty definition of ``extend``, # above, we must re-use the same ``data`` object. Hence # we use ``del`` to empty this one, rather than create a # fresh one (which would be my natural FP instinct). del data[:] if len(data): compressed = compressor.compress(tostring(data)) else: compressed = strtobytes('') flushed = compressor.flush() if len(compressed) or len(flushed): write_chunk(outfile, 'IDAT', compressed + flushed) # http://www.w3.org/TR/PNG/#11IEND write_chunk(outfile, 'IEND') return i+1 def write_array(self, outfile, pixels): """ Write an array in flat row flat pixel format as a PNG file on the output file. See also :meth:`write` method. """ if self.interlace: self.write_passes(outfile, self.array_scanlines_interlace(pixels)) else: self.write_passes(outfile, self.array_scanlines(pixels)) def write_packed(self, outfile, rows): """ Write PNG file to `outfile`. The pixel data comes from `rows` which should be in boxed row packed format. Each row should be a sequence of packed bytes. Technically, this method does work for interlaced images but it is best avoided. For interlaced images, the rows should be presented in the order that they appear in the file. This method should not be used when the source image bit depth is not one naturally supported by PNG; the bit depth should be 1, 2, 4, 8, or 16. """ if self.rescale: raise Error("write_packed method not suitable for bit depth %d" % self.rescale[0]) return self.write_passes(outfile, rows, packed=True) def convert_pnm(self, infile, outfile): """ Convert a PNM file containing raw pixel data into a PNG file with the parameters set in the writer object. Works for (binary) PGM, PPM, and PAM formats. """ if self.interlace: pixels = array('B') pixels.fromfile(infile, (self.bitdepth/8) * self.color_planes * self.width * self.height) self.write_passes(outfile, self.array_scanlines_interlace(pixels)) else: self.write_passes(outfile, self.file_scanlines(infile)) def convert_ppm_and_pgm(self, ppmfile, pgmfile, outfile): """ Convert a PPM and PGM file containing raw pixel data into a PNG outfile with the parameters set in the writer object. """ pixels = array('B') pixels.fromfile(ppmfile, (self.bitdepth/8) * self.color_planes * self.width * self.height) apixels = array('B') apixels.fromfile(pgmfile, (self.bitdepth/8) * self.width * self.height) pixels = interleave_planes(pixels, apixels, (self.bitdepth/8) * self.color_planes, (self.bitdepth/8)) if self.interlace: self.write_passes(outfile, self.array_scanlines_interlace(pixels)) else: self.write_passes(outfile, self.array_scanlines(pixels)) def file_scanlines(self, infile): """ Generates boxed rows in flat pixel format, from the input file `infile`. It assumes that the input file is in a "Netpbm-like" binary format, and is positioned at the beginning of the first pixel. The number of pixels to read is taken from the image dimensions (`width`, `height`, `planes`) and the number of bytes per value is implied by the image `bitdepth`. """ # Values per row vpr = self.width * self.planes row_bytes = vpr if self.bitdepth > 8: assert self.bitdepth == 16 row_bytes *= 2 fmt = '>%dH' % vpr def line(): return array('H', struct.unpack(fmt, infile.read(row_bytes))) else: def line(): scanline = array('B', infile.read(row_bytes)) return scanline for y in range(self.height): yield line() def array_scanlines(self, pixels): """ Generates boxed rows (flat pixels) from flat rows (flat pixels) in an array. """ # Values per row vpr = self.width * self.planes stop = 0 for y in range(self.height): start = stop stop = start + vpr yield pixels[start:stop] def array_scanlines_interlace(self, pixels): """ Generator for interlaced scanlines from an array. `pixels` is the full source image in flat row flat pixel format. The generator yields each scanline of the reduced passes in turn, in boxed row flat pixel format. """ # http://www.w3.org/TR/PNG/#8InterlaceMethods # Array type. fmt = 'BH'[self.bitdepth > 8] # Value per row vpr = self.width * self.planes for xstart, ystart, xstep, ystep in _adam7: if xstart >= self.width: continue # Pixels per row (of reduced image) ppr = int(math.ceil((self.width-xstart)/float(xstep))) # number of values in reduced image row. row_len = ppr*self.planes for y in range(ystart, self.height, ystep): if xstep == 1: offset = y * vpr yield pixels[offset:offset+vpr] else: row = array(fmt) # There's no easier way to set the length of an array row.extend(pixels[0:row_len]) offset = y * vpr + xstart * self.planes end_offset = (y+1) * vpr skip = self.planes * xstep for i in range(self.planes): row[i::self.planes] = \ pixels[offset+i:end_offset:skip] yield row def write_chunk(outfile, tag, data=strtobytes('')): """ Write a PNG chunk to the output file, including length and checksum. """ # http://www.w3.org/TR/PNG/#5Chunk-layout outfile.write(struct.pack("!I", len(data))) tag = strtobytes(tag) outfile.write(tag) outfile.write(data) checksum = zlib.crc32(tag) checksum = zlib.crc32(data, checksum) checksum &= 2**32-1 outfile.write(struct.pack("!I", checksum)) def write_chunks(out, chunks): """Create a PNG file by writing out the chunks.""" out.write(_signature) for chunk in chunks: write_chunk(out, *chunk) def filter_scanline(type, line, fo, prev=None): """Apply a scanline filter to a scanline. `type` specifies the filter type (0 to 4); `line` specifies the current (unfiltered) scanline as a sequence of bytes; `prev` specifies the previous (unfiltered) scanline as a sequence of bytes. `fo` specifies the filter offset; normally this is size of a pixel in bytes (the number of bytes per sample times the number of channels), but when this is < 1 (for bit depths < 8) then the filter offset is 1. """ assert 0 <= type < 5 # The output array. Which, pathetically, we extend one-byte at a # time (fortunately this is linear). out = array('B', [type]) def sub(): ai = -fo for x in line: if ai >= 0: x = (x - line[ai]) & 0xff out.append(x) ai += 1 def up(): for i,x in enumerate(line): x = (x - prev[i]) & 0xff out.append(x) def average(): ai = -fo for i,x in enumerate(line): if ai >= 0: x = (x - ((line[ai] + prev[i]) >> 1)) & 0xff else: x = (x - (prev[i] >> 1)) & 0xff out.append(x) ai += 1 def paeth(): # http://www.w3.org/TR/PNG/#9Filter-type-4-Paeth ai = -fo # also used for ci for i,x in enumerate(line): a = 0 b = prev[i] c = 0 if ai >= 0: a = line[ai] c = prev[ai] p = a + b - c pa = abs(p - a) pb = abs(p - b) pc = abs(p - c) if pa <= pb and pa <= pc: Pr = a elif pb <= pc: Pr = b else: Pr = c x = (x - Pr) & 0xff out.append(x) ai += 1 if not prev: # We're on the first line. Some of the filters can be reduced # to simpler cases which makes handling the line "off the top" # of the image simpler. "up" becomes "none"; "paeth" becomes # "left" (non-trivial, but true). "average" needs to be handled # specially. if type == 2: # "up" type = 0 elif type == 3: prev = [0]*len(line) elif type == 4: # "paeth" type = 1 if type == 0: out.extend(line) elif type == 1: sub() elif type == 2: up() elif type == 3: average() else: # type == 4 paeth() return out def from_array(a, mode=None, info={}): """Create a PNG :class:`Image` object from a 2- or 3-dimensional array. One application of this function is easy PIL-style saving: ``png.from_array(pixels, 'L').save('foo.png')``. .. note : The use of the term *3-dimensional* is for marketing purposes only. It doesn't actually work. Please bear with us. Meanwhile enjoy the complimentary snacks (on request) and please use a 2-dimensional array. Unless they are specified using the *info* parameter, the PNG's height and width are taken from the array size. For a 3 dimensional array the first axis is the height; the second axis is the width; and the third axis is the channel number. Thus an RGB image that is 16 pixels high and 8 wide will use an array that is 16x8x3. For 2 dimensional arrays the first axis is the height, but the second axis is ``width*channels``, so an RGB image that is 16 pixels high and 8 wide will use a 2-dimensional array that is 16x24 (each row will be 8*3==24 sample values). *mode* is a string that specifies the image colour format in a PIL-style mode. It can be: ``'L'`` greyscale (1 channel) ``'LA'`` greyscale with alpha (2 channel) ``'RGB'`` colour image (3 channel) ``'RGBA'`` colour image with alpha (4 channel) The mode string can also specify the bit depth (overriding how this function normally derives the bit depth, see below). Appending ``';16'`` to the mode will cause the PNG to be 16 bits per channel; any decimal from 1 to 16 can be used to specify the bit depth. When a 2-dimensional array is used *mode* determines how many channels the image has, and so allows the width to be derived from the second array dimension. The array is expected to be a ``numpy`` array, but it can be any suitable Python sequence. For example, a list of lists can be used: ``png.from_array([[0, 255, 0], [255, 0, 255]], 'L')``. The exact rules are: ``len(a)`` gives the first dimension, height; ``len(a[0])`` gives the second dimension; ``len(a[0][0])`` gives the third dimension, unless an exception is raised in which case a 2-dimensional array is assumed. It's slightly more complicated than that because an iterator of rows can be used, and it all still works. Using an iterator allows data to be streamed efficiently. The bit depth of the PNG is normally taken from the array element's datatype (but if *mode* specifies a bitdepth then that is used instead). The array element's datatype is determined in a way which is supposed to work both for ``numpy`` arrays and for Python ``array.array`` objects. A 1 byte datatype will give a bit depth of 8, a 2 byte datatype will give a bit depth of 16. If the datatype does not have an implicit size, for example it is a plain Python list of lists, as above, then a default of 8 is used. The *info* parameter is a dictionary that can be used to specify metadata (in the same style as the arguments to the :class:``png.Writer`` class). For this function the keys that are useful are: height overrides the height derived from the array dimensions and allows *a* to be an iterable. width overrides the width derived from the array dimensions. bitdepth overrides the bit depth derived from the element datatype (but must match *mode* if that also specifies a bit depth). Generally anything specified in the *info* dictionary will override any implicit choices that this function would otherwise make, but must match any explicit ones. For example, if the *info* dictionary has a ``greyscale`` key then this must be true when mode is ``'L'`` or ``'LA'`` and false when mode is ``'RGB'`` or ``'RGBA'``. """ # We abuse the *info* parameter by modifying it. Take a copy here. # (Also typechecks *info* to some extent). info = dict(info) # Syntax check mode string. bitdepth = None try: # Assign the 'L' or 'RGBA' part to `gotmode`. if mode.startswith('L'): gotmode = 'L' mode = mode[1:] elif mode.startswith('RGB'): gotmode = 'RGB' mode = mode[3:] else: raise Error() if mode.startswith('A'): gotmode += 'A' mode = mode[1:] # Skip any optional ';' while mode.startswith(';'): mode = mode[1:] # Parse optional bitdepth if mode: try: bitdepth = int(mode) except (TypeError, ValueError): raise Error() except Error: raise Error("mode string should be 'RGB' or 'L;16' or similar.") mode = gotmode # Get bitdepth from *mode* if possible. if bitdepth: if info.get('bitdepth') and bitdepth != info['bitdepth']: raise Error("mode bitdepth (%d) should match info bitdepth (%d)." % (bitdepth, info['bitdepth'])) info['bitdepth'] = bitdepth # Fill in and/or check entries in *info*. # Dimensions. if 'size' in info: # Check width, height, size all match where used. for dimension,axis in [('width', 0), ('height', 1)]: if dimension in info: if info[dimension] != info['size'][axis]: raise Error( "info[%r] should match info['size'][%r]." % (dimension, axis)) info['width'],info['height'] = info['size'] if 'height' not in info: try: l = len(a) except TypeError: raise Error( "len(a) does not work, supply info['height'] instead.") info['height'] = l # Colour format. if 'greyscale' in info: if bool(info['greyscale']) != ('L' in mode): raise Error("info['greyscale'] should match mode.") info['greyscale'] = 'L' in mode if 'alpha' in info: if bool(info['alpha']) != ('A' in mode): raise Error("info['alpha'] should match mode.") info['alpha'] = 'A' in mode planes = len(mode) if 'planes' in info: if info['planes'] != planes: raise Error("info['planes'] should match mode.") # In order to work out whether we the array is 2D or 3D we need its # first row, which requires that we take a copy of its iterator. # We may also need the first row to derive width and bitdepth. a,t = itertools.tee(a) row = t.next() del t try: row[0][0] threed = True testelement = row[0] except (IndexError, TypeError): threed = False testelement = row if 'width' not in info: if threed: width = len(row) else: width = len(row) // planes info['width'] = width # Not implemented yet assert not threed if 'bitdepth' not in info: try: dtype = testelement.dtype # goto the "else:" clause. Sorry. except AttributeError: try: # Try a Python array.array. bitdepth = 8 * testelement.itemsize except AttributeError: # We can't determine it from the array element's # datatype, use a default of 8. bitdepth = 8 else: # If we got here without exception, we now assume that # the array is a numpy array. if dtype.kind == 'b': bitdepth = 1 else: bitdepth = 8 * dtype.itemsize info['bitdepth'] = bitdepth for thing in 'width height bitdepth greyscale alpha'.split(): assert thing in info return Image(a, info) # So that refugee's from PIL feel more at home. Not documented. fromarray = from_array class Image: """A PNG image. You can create an :class:`Image` object from an array of pixels by calling :meth:`png.from_array`. It can be saved to disk with the :meth:`save` method. """ def __init__(self, rows, info): """ .. note :: The constructor is not public. Please do not call it. """ self.rows = rows self.info = info def save(self, file): """Save the image to *file*. If *file* looks like an open file descriptor then it is used, otherwise it is treated as a filename and a fresh file is opened. In general, you can only call this method once; after it has been called the first time and the PNG image has been saved, the source data will have been streamed, and cannot be streamed again. """ w = Writer(**self.info) try: file.write def close(): pass except AttributeError: file = open(file, 'wb') def close(): file.close() try: w.write(file, self.rows) finally: close() class _readable: """ A simple file-like interface for strings and arrays. """ def __init__(self, buf): self.buf = buf self.offset = 0 def read(self, n): r = self.buf[self.offset:self.offset+n] if isarray(r): r = r.tostring() self.offset += n return r class Reader: """ PNG decoder in pure Python. """ def __init__(self, _guess=None, **kw): """ Create a PNG decoder object. The constructor expects exactly one keyword argument. If you supply a positional argument instead, it will guess the input type. You can choose among the following keyword arguments: filename Name of input file (a PNG file). file A file-like object (object with a read() method). bytes ``array`` or ``string`` with PNG data. """ if ((_guess is not None and len(kw) != 0) or (_guess is None and len(kw) != 1)): raise TypeError("Reader() takes exactly 1 argument") # Will be the first 8 bytes, later on. See validate_signature. self.signature = None self.transparent = None # A pair of (len,type) if a chunk has been read but its data and # checksum have not (in other words the file position is just # past the 4 bytes that specify the chunk type). See preamble # method for how this is used. self.atchunk = None if _guess is not None: if isarray(_guess): kw["bytes"] = _guess elif isinstance(_guess, string_types): kw["filename"] = _guess elif hasattr(_guess, 'read'): kw["file"] = _guess if "filename" in kw: self.file = open(kw["filename"], "rb") elif "file" in kw: self.file = kw["file"] elif "bytes" in kw: self.file = _readable(kw["bytes"]) else: raise TypeError("expecting filename, file or bytes array") def chunk(self, seek=None, lenient=False): """ Read the next PNG chunk from the input file; returns a (*type*,*data*) tuple. *type* is the chunk's type as a string (all PNG chunk types are 4 characters long). *data* is the chunk's data content, as a string. If the optional `seek` argument is specified then it will keep reading chunks until it either runs out of file or finds the type specified by the argument. Note that in general the order of chunks in PNGs is unspecified, so using `seek` can cause you to miss chunks. If the optional `lenient` argument evaluates to True, checksum failures will raise warnings rather than exceptions. """ self.validate_signature() while True: # http://www.w3.org/TR/PNG/#5Chunk-layout if not self.atchunk: self.atchunk = self.chunklentype() length,type = self.atchunk self.atchunk = None data = self.file.read(length) if len(data) != length: raise ChunkError('Chunk %s too short for required %i octets.' % (type, length)) checksum = self.file.read(4) if len(checksum) != 4: raise ValueError('Chunk %s too short for checksum.', tag) if seek and type != seek: continue verify = zlib.crc32(strtobytes(type)) verify = zlib.crc32(data, verify) # Whether the output from zlib.crc32 is signed or not varies # according to hideous implementation details, see # http://bugs.python.org/issue1202 . # We coerce it to be positive here (in a way which works on # Python 2.3 and older). verify &= 2**32 - 1 verify = struct.pack('!I', verify) if checksum != verify: (a, ) = struct.unpack('!I', checksum) (b, ) = struct.unpack('!I', verify) message = "Checksum error in %s chunk: 0x%08X != 0x%08X." % (type, a, b) if lenient: warnings.warn(message, RuntimeWarning) else: raise ChunkError(message) return type, data def chunks(self): """Return an iterator that will yield each chunk as a (*chunktype*, *content*) pair. """ while True: t,v = self.chunk() yield t,v if t == 'IEND': break def undo_filter(self, filter_type, scanline, previous): """Undo the filter for a scanline. `scanline` is a sequence of bytes that does not include the initial filter type byte. `previous` is decoded previous scanline (for straightlaced images this is the previous pixel row, but for interlaced images, it is the previous scanline in the reduced image, which in general is not the previous pixel row in the final image). When there is no previous scanline (the first row of a straightlaced image, or the first row in one of the passes in an interlaced image), then this argument should be ``None``. The scanline will have the effects of filtering removed, and the result will be returned as a fresh sequence of bytes. """ # :todo: Would it be better to update scanline in place? # Yes, with the Cython extension making the undo_filter fast, # updating scanline inplace makes the code 3 times faster # (reading 50 images of 800x800 went from 40s to 16s) result = scanline if filter_type == 0: return result if filter_type not in (1,2,3,4): raise FormatError('Invalid PNG Filter Type.' ' See http://www.w3.org/TR/2003/REC-PNG-20031110/#9Filters .') # Filter unit. The stride from one pixel to the corresponding # byte from the previous pixel. Normally this is the pixel # size in bytes, but when this is smaller than 1, the previous # byte is used instead. fu = max(1, self.psize) # For the first line of a pass, synthesize a dummy previous # line. An alternative approach would be to observe that on the # first line 'up' is the same as 'null', 'paeth' is the same # as 'sub', with only 'average' requiring any special case. if not previous: previous = array('B', [0]*len(scanline)) def sub(): """Undo sub filter.""" ai = 0 # Loop starts at index fu. Observe that the initial part # of the result is already filled in correctly with # scanline. for i in range(fu, len(result)): x = scanline[i] a = result[ai] result[i] = (x + a) & 0xff ai += 1 def up(): """Undo up filter.""" for i in range(len(result)): x = scanline[i] b = previous[i] result[i] = (x + b) & 0xff def average(): """Undo average filter.""" ai = -fu for i in range(len(result)): x = scanline[i] if ai < 0: a = 0 else: a = result[ai] b = previous[i] result[i] = (x + ((a + b) >> 1)) & 0xff ai += 1 def paeth(): """Undo Paeth filter.""" # Also used for ci. ai = -fu for i in range(len(result)): x = scanline[i] if ai < 0: a = c = 0 else: a = result[ai] c = previous[ai] b = previous[i] p = a + b - c pa = abs(p - a) pb = abs(p - b) pc = abs(p - c) if pa <= pb and pa <= pc: pr = a elif pb <= pc: pr = b else: pr = c result[i] = (x + pr) & 0xff ai += 1 # Call appropriate filter algorithm. Note that 0 has already # been dealt with. (None, pngfilters.undo_filter_sub, pngfilters.undo_filter_up, pngfilters.undo_filter_average, pngfilters.undo_filter_paeth)[filter_type](fu, scanline, previous, result) return result def deinterlace(self, raw): """ Read raw pixel data, undo filters, deinterlace, and flatten. Return in flat row flat pixel format. """ # Values per row (of the target image) vpr = self.width * self.planes # Make a result array, and make it big enough. Interleaving # writes to the output array randomly (well, not quite), so the # entire output array must be in memory. fmt = 'BH'[self.bitdepth > 8] a = array(fmt, [0]*vpr*self.height) source_offset = 0 for xstart, ystart, xstep, ystep in _adam7: if xstart >= self.width: continue # The previous (reconstructed) scanline. None at the # beginning of a pass to indicate that there is no previous # line. recon = None # Pixels per row (reduced pass image) ppr = int(math.ceil((self.width-xstart)/float(xstep))) # Row size in bytes for this pass. row_size = int(math.ceil(self.psize * ppr)) for y in range(ystart, self.height, ystep): filter_type = raw[source_offset] source_offset += 1 scanline = raw[source_offset:source_offset+row_size] source_offset += row_size recon = self.undo_filter(filter_type, scanline, recon) # Convert so that there is one element per pixel value flat = self.serialtoflat(recon, ppr) if xstep == 1: assert xstart == 0 offset = y * vpr a[offset:offset+vpr] = flat else: offset = y * vpr + xstart * self.planes end_offset = (y+1) * vpr skip = self.planes * xstep for i in range(self.planes): a[offset+i:end_offset:skip] = \ flat[i::self.planes] return a def iterboxed(self, rows): """Iterator that yields each scanline in boxed row flat pixel format. `rows` should be an iterator that yields the bytes of each row in turn. """ def asvalues(raw): """Convert a row of raw bytes into a flat row. Result will be a freshly allocated object, not shared with argument. """ if self.bitdepth == 8: return array('B', raw) if self.bitdepth == 16: raw = tostring(raw) return array('H', struct.unpack('!%dH' % (len(raw)//2), raw)) assert self.bitdepth < 8 width = self.width # Samples per byte spb = 8//self.bitdepth out = array('B') mask = 2**self.bitdepth - 1 shifts = map(self.bitdepth.__mul__, reversed(range(spb))) for o in raw: out.extend(map(lambda i: mask&(o>>i), shifts)) return out[:width] return imap(asvalues, rows) def serialtoflat(self, bytes, width=None): """Convert serial format (byte stream) pixel data to flat row flat pixel. """ if self.bitdepth == 8: return bytes if self.bitdepth == 16: bytes = tostring(bytes) return array('H', struct.unpack('!%dH' % (len(bytes)//2), bytes)) assert self.bitdepth < 8 if width is None: width = self.width # Samples per byte spb = 8//self.bitdepth out = array('B') mask = 2**self.bitdepth - 1 shifts = map(self.bitdepth.__mul__, reversed(range(spb))) l = width for o in bytes: out.extend([(mask&(o>>s)) for s in shifts][:l]) l -= spb if l <= 0: l = width return out def iterstraight(self, raw): """Iterator that undoes the effect of filtering, and yields each row in serialised format (as a sequence of bytes). Assumes input is straightlaced. `raw` should be an iterable that yields the raw bytes in chunks of arbitrary size. """ # length of row, in bytes rb = self.row_bytes a = array('B') # The previous (reconstructed) scanline. None indicates first # line of image. recon = None for some in raw: a.extend(some) while len(a) >= rb + 1: filter_type = a[0] scanline = a[1:rb+1] del a[:rb+1] recon = self.undo_filter(filter_type, scanline, recon) yield recon if len(a) != 0: # :file:format We get here with a file format error: # when the available bytes (after decompressing) do not # pack into exact rows. raise FormatError( 'Wrong size for decompressed IDAT chunk.') assert len(a) == 0 def validate_signature(self): """If signature (header) has not been read then read and validate it; otherwise do nothing. """ if self.signature: return self.signature = self.file.read(8) if self.signature != _signature: raise FormatError("PNG file has invalid signature.") def preamble(self, lenient=False): """ Extract the image metadata by reading the initial part of the PNG file up to the start of the ``IDAT`` chunk. All the chunks that precede the ``IDAT`` chunk are read and either processed for metadata or discarded. If the optional `lenient` argument evaluates to True, checksum failures will raise warnings rather than exceptions. """ self.validate_signature() while True: if not self.atchunk: self.atchunk = self.chunklentype() if self.atchunk is None: raise FormatError( 'This PNG file has no IDAT chunks.') if self.atchunk[1] == 'IDAT': return self.process_chunk(lenient=lenient) def chunklentype(self): """Reads just enough of the input to determine the next chunk's length and type, returned as a (*length*, *type*) pair where *type* is a string. If there are no more chunks, ``None`` is returned. """ x = self.file.read(8) if not x: return None if len(x) != 8: raise FormatError( 'End of file whilst reading chunk length and type.') length,type = struct.unpack('!I4s', x) type = bytestostr(type) if length > 2**31-1: raise FormatError('Chunk %s is too large: %d.' % (type,length)) return length,type def process_chunk(self, lenient=False): """Process the next chunk and its data. This only processes the following chunk types, all others are ignored: ``IHDR``, ``PLTE``, ``bKGD``, ``tRNS``, ``gAMA``, ``sBIT``. If the optional `lenient` argument evaluates to True, checksum failures will raise warnings rather than exceptions. """ type, data = self.chunk(lenient=lenient) method = '_process_' + type m = getattr(self, method, None) if m: m(data) def _process_IHDR(self, data): # http://www.w3.org/TR/PNG/#11IHDR if len(data) != 13: raise FormatError('IHDR chunk has incorrect length.') (self.width, self.height, self.bitdepth, self.color_type, self.compression, self.filter, self.interlace) = struct.unpack("!2I5B", data) check_bitdepth_colortype(self.bitdepth, self.color_type) if self.compression != 0: raise Error("unknown compression method %d" % self.compression) if self.filter != 0: raise FormatError("Unknown filter method %d," " see http://www.w3.org/TR/2003/REC-PNG-20031110/#9Filters ." % self.filter) if self.interlace not in (0,1): raise FormatError("Unknown interlace method %d," " see http://www.w3.org/TR/2003/REC-PNG-20031110/#8InterlaceMethods ." % self.interlace) # Derived values # http://www.w3.org/TR/PNG/#6Colour-values colormap = bool(self.color_type & 1) greyscale = not (self.color_type & 2) alpha = bool(self.color_type & 4) color_planes = (3,1)[greyscale or colormap] planes = color_planes + alpha self.colormap = colormap self.greyscale = greyscale self.alpha = alpha self.color_planes = color_planes self.planes = planes self.psize = float(self.bitdepth)/float(8) * planes if int(self.psize) == self.psize: self.psize = int(self.psize) self.row_bytes = int(math.ceil(self.width * self.psize)) # Stores PLTE chunk if present, and is used to check # chunk ordering constraints. self.plte = None # Stores tRNS chunk if present, and is used to check chunk # ordering constraints. self.trns = None # Stores sbit chunk if present. self.sbit = None def _process_PLTE(self, data): # http://www.w3.org/TR/PNG/#11PLTE if self.plte: warnings.warn("Multiple PLTE chunks present.") self.plte = data if len(data) % 3 != 0: raise FormatError( "PLTE chunk's length should be a multiple of 3.") if len(data) > (2**self.bitdepth)*3: raise FormatError("PLTE chunk is too long.") if len(data) == 0: raise FormatError("Empty PLTE is not allowed.") def _process_bKGD(self, data): try: if self.colormap: if not self.plte: warnings.warn( "PLTE chunk is required before bKGD chunk.") self.background = struct.unpack('B', data) else: self.background = struct.unpack("!%dH" % self.color_planes, data) except struct.error: raise FormatError("bKGD chunk has incorrect length.") def _process_tRNS(self, data): # http://www.w3.org/TR/PNG/#11tRNS self.trns = data if self.colormap: if not self.plte: warnings.warn("PLTE chunk is required before tRNS chunk.") else: if len(data) > len(self.plte)/3: # Was warning, but promoted to Error as it # would otherwise cause pain later on. raise FormatError("tRNS chunk is too long.") else: if self.alpha: raise FormatError( "tRNS chunk is not valid with colour type %d." % self.color_type) try: self.transparent = \ struct.unpack("!%dH" % self.color_planes, data) except struct.error: raise FormatError("tRNS chunk has incorrect length.") def _process_gAMA(self, data): try: self.gamma = struct.unpack("!L", data)[0] / 100000.0 except struct.error: raise FormatError("gAMA chunk has incorrect length.") def _process_sBIT(self, data): self.sbit = data if (self.colormap and len(data) != 3 or not self.colormap and len(data) != self.planes): raise FormatError("sBIT chunk has incorrect length.") def read(self, lenient=False): """ Read the PNG file and decode it. Returns (`width`, `height`, `pixels`, `metadata`). May use excessive memory. `pixels` are returned in boxed row flat pixel format. If the optional `lenient` argument evaluates to True, checksum failures will raise warnings rather than exceptions. """ def iteridat(): """Iterator that yields all the ``IDAT`` chunks as strings.""" while True: try: type, data = self.chunk(lenient=lenient) except ValueError as e: raise ChunkError(e.args[0]) if type == 'IEND': # http://www.w3.org/TR/PNG/#11IEND break if type != 'IDAT': continue # type == 'IDAT' # http://www.w3.org/TR/PNG/#11IDAT if self.colormap and not self.plte: warnings.warn("PLTE chunk is required before IDAT chunk") yield data def iterdecomp(idat): """Iterator that yields decompressed strings. `idat` should be an iterator that yields the ``IDAT`` chunk data. """ # Currently, with no max_length parameter to decompress, # this routine will do one yield per IDAT chunk: Not very # incremental. d = zlib.decompressobj() # Each IDAT chunk is passed to the decompressor, then any # remaining state is decompressed out. for data in idat: # :todo: add a max_length argument here to limit output # size. yield array('B', d.decompress(data)) yield array('B', d.flush()) self.preamble(lenient=lenient) raw = iterdecomp(iteridat()) if self.interlace: raw = array('B', itertools.chain(*raw)) arraycode = 'BH'[self.bitdepth>8] # Like :meth:`group` but producing an array.array object for # each row. pixels = imap(lambda *row: array(arraycode, row), *[iter(self.deinterlace(raw))]*self.width*self.planes) else: pixels = self.iterboxed(self.iterstraight(raw)) meta = dict() for attr in 'greyscale alpha planes bitdepth interlace'.split(): meta[attr] = getattr(self, attr) meta['size'] = (self.width, self.height) for attr in 'gamma transparent background'.split(): a = getattr(self, attr, None) if a is not None: meta[attr] = a if self.plte: meta['palette'] = self.palette() return self.width, self.height, pixels, meta def read_flat(self): """ Read a PNG file and decode it into flat row flat pixel format. Returns (*width*, *height*, *pixels*, *metadata*). May use excessive memory. `pixels` are returned in flat row flat pixel format. See also the :meth:`read` method which returns pixels in the more stream-friendly boxed row flat pixel format. """ x, y, pixel, meta = self.read() arraycode = 'BH'[meta['bitdepth']>8] pixel = array(arraycode, itertools.chain(*pixel)) return x, y, pixel, meta def palette(self, alpha='natural'): """Returns a palette that is a sequence of 3-tuples or 4-tuples, synthesizing it from the ``PLTE`` and ``tRNS`` chunks. These chunks should have already been processed (for example, by calling the :meth:`preamble` method). All the tuples are the same size: 3-tuples if there is no ``tRNS`` chunk, 4-tuples when there is a ``tRNS`` chunk. Assumes that the image is colour type 3 and therefore a ``PLTE`` chunk is required. If the `alpha` argument is ``'force'`` then an alpha channel is always added, forcing the result to be a sequence of 4-tuples. """ if not self.plte: raise FormatError( "Required PLTE chunk is missing in colour type 3 image.") plte = group(array('B', self.plte), 3) if self.trns or alpha == 'force': trns = array('B', self.trns or '') trns.extend([255]*(len(plte)-len(trns))) plte = map(operator.add, plte, group(trns, 1)) return plte def asDirect(self): """Returns the image data as a direct representation of an ``x * y * planes`` array. This method is intended to remove the need for callers to deal with palettes and transparency themselves. Images with a palette (colour type 3) are converted to RGB or RGBA; images with transparency (a ``tRNS`` chunk) are converted to LA or RGBA as appropriate. When returned in this format the pixel values represent the colour value directly without needing to refer to palettes or transparency information. Like the :meth:`read` method this method returns a 4-tuple: (*width*, *height*, *pixels*, *meta*) This method normally returns pixel values with the bit depth they have in the source image, but when the source PNG has an ``sBIT`` chunk it is inspected and can reduce the bit depth of the result pixels; pixel values will be reduced according to the bit depth specified in the ``sBIT`` chunk (PNG nerds should note a single result bit depth is used for all channels; the maximum of the ones specified in the ``sBIT`` chunk. An RGB565 image will be rescaled to 6-bit RGB666). The *meta* dictionary that is returned reflects the `direct` format and not the original source image. For example, an RGB source image with a ``tRNS`` chunk to represent a transparent colour, will have ``planes=3`` and ``alpha=False`` for the source image, but the *meta* dictionary returned by this method will have ``planes=4`` and ``alpha=True`` because an alpha channel is synthesized and added. *pixels* is the pixel data in boxed row flat pixel format (just like the :meth:`read` method). All the other aspects of the image data are not changed. """ self.preamble() # Simple case, no conversion necessary. if not self.colormap and not self.trns and not self.sbit: return self.read() x,y,pixels,meta = self.read() if self.colormap: meta['colormap'] = False meta['alpha'] = bool(self.trns) meta['bitdepth'] = 8 meta['planes'] = 3 + bool(self.trns) plte = self.palette() def iterpal(pixels): for row in pixels: row = map(plte.__getitem__, row) yield array('B', itertools.chain(*row)) pixels = iterpal(pixels) elif self.trns: # It would be nice if there was some reasonable way # of doing this without generating a whole load of # intermediate tuples. But tuples does seem like the # easiest way, with no other way clearly much simpler or # much faster. (Actually, the L to LA conversion could # perhaps go faster (all those 1-tuples!), but I still # wonder whether the code proliferation is worth it) it = self.transparent maxval = 2**meta['bitdepth']-1 planes = meta['planes'] meta['alpha'] = True meta['planes'] += 1 typecode = 'BH'[meta['bitdepth']>8] def itertrns(pixels): for row in pixels: # For each row we group it into pixels, then form a # characterisation vector that says whether each # pixel is opaque or not. Then we convert # True/False to 0/maxval (by multiplication), # and add it as the extra channel. row = group(row, planes) opa = map(it.__ne__, row) opa = map(maxval.__mul__, opa) opa = zip(opa) # convert to 1-tuples yield array(typecode, itertools.chain(*map(operator.add, row, opa))) pixels = itertrns(pixels) targetbitdepth = None if self.sbit: sbit = struct.unpack('%dB' % len(self.sbit), self.sbit) targetbitdepth = max(sbit) if targetbitdepth > meta['bitdepth']: raise Error('sBIT chunk %r exceeds bitdepth %d' % (sbit,self.bitdepth)) if min(sbit) <= 0: raise Error('sBIT chunk %r has a 0-entry' % sbit) if targetbitdepth == meta['bitdepth']: targetbitdepth = None if targetbitdepth: shift = meta['bitdepth'] - targetbitdepth meta['bitdepth'] = targetbitdepth def itershift(pixels): for row in pixels: yield map(shift.__rrshift__, row) pixels = itershift(pixels) return x,y,pixels,meta def asFloat(self, maxval=1.0): """Return image pixels as per :meth:`asDirect` method, but scale all pixel values to be floating point values between 0.0 and *maxval*. """ x,y,pixels,info = self.asDirect() sourcemaxval = 2**info['bitdepth']-1 del info['bitdepth'] info['maxval'] = float(maxval) factor = float(maxval)/float(sourcemaxval) def iterfloat(): for row in pixels: yield map(factor.__mul__, row) return x,y,iterfloat(),info def _as_rescale(self, get, targetbitdepth): """Helper used by :meth:`asRGB8` and :meth:`asRGBA8`.""" width,height,pixels,meta = get() maxval = 2**meta['bitdepth'] - 1 targetmaxval = 2**targetbitdepth - 1 factor = float(targetmaxval) / float(maxval) meta['bitdepth'] = targetbitdepth def iterscale(): for row in pixels: yield map(lambda x: int(round(x*factor)), row) if maxval == targetmaxval: return width, height, pixels, meta else: return width, height, iterscale(), meta def asRGB8(self): """Return the image data as an RGB pixels with 8-bits per sample. This is like the :meth:`asRGB` method except that this method additionally rescales the values so that they are all between 0 and 255 (8-bit). In the case where the source image has a bit depth < 8 the transformation preserves all the information; where the source image has bit depth > 8, then rescaling to 8-bit values loses precision. No dithering is performed. Like :meth:`asRGB`, an alpha channel in the source image will raise an exception. This function returns a 4-tuple: (*width*, *height*, *pixels*, *metadata*). *width*, *height*, *metadata* are as per the :meth:`read` method. *pixels* is the pixel data in boxed row flat pixel format. """ return self._as_rescale(self.asRGB, 8) def asRGBA8(self): """Return the image data as RGBA pixels with 8-bits per sample. This method is similar to :meth:`asRGB8` and :meth:`asRGBA`: The result pixels have an alpha channel, *and* values are rescaled to the range 0 to 255. The alpha channel is synthesized if necessary (with a small speed penalty). """ return self._as_rescale(self.asRGBA, 8) def asRGB(self): """Return image as RGB pixels. RGB colour images are passed through unchanged; greyscales are expanded into RGB triplets (there is a small speed overhead for doing this). An alpha channel in the source image will raise an exception. The return values are as for the :meth:`read` method except that the *metadata* reflect the returned pixels, not the source image. In particular, for this method ``metadata['greyscale']`` will be ``False``. """ width,height,pixels,meta = self.asDirect() if meta['alpha']: raise Error("will not convert image with alpha channel to RGB") if not meta['greyscale']: return width,height,pixels,meta meta['greyscale'] = False typecode = 'BH'[meta['bitdepth'] > 8] def iterrgb(): for row in pixels: a = array(typecode, [0]) * 3 * width for i in range(3): a[i::3] = row yield a return width,height,iterrgb(),meta def asRGBA(self): """Return image as RGBA pixels. Greyscales are expanded into RGB triplets; an alpha channel is synthesized if necessary. The return values are as for the :meth:`read` method except that the *metadata* reflect the returned pixels, not the source image. In particular, for this method ``metadata['greyscale']`` will be ``False``, and ``metadata['alpha']`` will be ``True``. """ width,height,pixels,meta = self.asDirect() if meta['alpha'] and not meta['greyscale']: return width,height,pixels,meta typecode = 'BH'[meta['bitdepth'] > 8] maxval = 2**meta['bitdepth'] - 1 maxbuffer = struct.pack('=' + typecode, maxval) * 4 * width def newarray(): return array(typecode, maxbuffer) if meta['alpha'] and meta['greyscale']: # LA to RGBA def convert(): for row in pixels: # Create a fresh target row, then copy L channel # into first three target channels, and A channel # into fourth channel. a = newarray() pngfilters.convert_la_to_rgba(row, a) yield a elif meta['greyscale']: # L to RGBA def convert(): for row in pixels: a = newarray() pngfilters.convert_l_to_rgba(row, a) yield a else: assert not meta['alpha'] and not meta['greyscale'] # RGB to RGBA def convert(): for row in pixels: a = newarray() pngfilters.convert_rgb_to_rgba(row, a) yield a meta['alpha'] = True meta['greyscale'] = False return width,height,convert(),meta def check_bitdepth_colortype(bitdepth, colortype): """Check that `bitdepth` and `colortype` are both valid, and specified in a valid combination. Returns if valid, raise an Exception if not valid. """ if bitdepth not in (1,2,4,8,16): raise FormatError("invalid bit depth %d" % bitdepth) if colortype not in (0,2,3,4,6): raise FormatError("invalid colour type %d" % colortype) # Check indexed (palettized) images have 8 or fewer bits # per pixel; check only indexed or greyscale images have # fewer than 8 bits per pixel. if colortype & 1 and bitdepth > 8: raise FormatError( "Indexed images (colour type %d) cannot" " have bitdepth > 8 (bit depth %d)." " See http://www.w3.org/TR/2003/REC-PNG-20031110/#table111 ." % (bitdepth, colortype)) if bitdepth < 8 and colortype not in (0,3): raise FormatError("Illegal combination of bit depth (%d)" " and colour type (%d)." " See http://www.w3.org/TR/2003/REC-PNG-20031110/#table111 ." % (bitdepth, colortype)) def isinteger(x): try: return int(x) == x except (TypeError, ValueError): return False class pngfilters(object): def undo_filter_sub(filter_unit, scanline, previous, result): """Undo sub filter.""" ai = 0 # Loops starts at index fu. Observe that the initial part # of the result is already filled in correctly with # scanline. for i in range(filter_unit, len(result)): x = scanline[i] a = result[ai] result[i] = (x + a) & 0xff ai += 1 undo_filter_sub = staticmethod(undo_filter_sub) def undo_filter_up(filter_unit, scanline, previous, result): """Undo up filter.""" for i in range(len(result)): x = scanline[i] b = previous[i] result[i] = (x + b) & 0xff undo_filter_up = staticmethod(undo_filter_up) def undo_filter_average(filter_unit, scanline, previous, result): """Undo up filter.""" ai = -filter_unit for i in range(len(result)): x = scanline[i] if ai < 0: a = 0 else: a = result[ai] b = previous[i] result[i] = (x + ((a + b) >> 1)) & 0xff ai += 1 undo_filter_average = staticmethod(undo_filter_average) def undo_filter_paeth(filter_unit, scanline, previous, result): """Undo Paeth filter.""" # Also used for ci. ai = -filter_unit for i in range(len(result)): x = scanline[i] if ai < 0: a = c = 0 else: a = result[ai] c = previous[ai] b = previous[i] p = a + b - c pa = abs(p - a) pb = abs(p - b) pc = abs(p - c) if pa <= pb and pa <= pc: pr = a elif pb <= pc: pr = b else: pr = c result[i] = (x + pr) & 0xff ai += 1 undo_filter_paeth = staticmethod(undo_filter_paeth) def convert_la_to_rgba(row, result): for i in range(3): result[i::4] = row[0::2] result[3::4] = row[1::2] convert_la_to_rgba = staticmethod(convert_la_to_rgba) def convert_l_to_rgba(row, result): """Convert a grayscale image to RGBA. This method assumes the alpha channel in result is already correctly initialized. """ for i in range(3): result[i::4] = row convert_l_to_rgba = staticmethod(convert_l_to_rgba) def convert_rgb_to_rgba(row, result): """Convert an RGB image to RGBA. This method assumes the alpha channel in result is already correctly initialized. """ for i in range(3): result[i::4] = row[i::3] convert_rgb_to_rgba = staticmethod(convert_rgb_to_rgba) �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/ext/fontconfig.py�����������������������������������������������������������������0000664�0001750�0001750�00000010177�12406355656�021065� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- import warnings from ctypes import (util, cdll, c_void_p, c_char_p, c_double, c_int, c_bool, Union, Structure, byref, POINTER) from ..util.wrappers import run_subprocess # Some code adapted from Pyglet fc = util.find_library('fontconfig') if fc is None: raise ImportError('fontconfig not found') fontconfig = cdll.LoadLibrary(fc) FC_FAMILY = 'family'.encode('ASCII') FC_SIZE = 'size'.encode('ASCII') FC_SLANT = 'slant'.encode('ASCII') FC_WEIGHT = 'weight'.encode('ASCII') FC_FT_FACE = 'ftface'.encode('ASCII') FC_FILE = 'file'.encode('ASCII') FC_STYLE = 'style'.encode('ASCII') FC_LANG = 'lang'.encode('ASCII') FC_WEIGHT_REGULAR = 80 FC_WEIGHT_BOLD = 200 FC_SLANT_ROMAN = 0 FC_SLANT_ITALIC = 100 FcMatchPattern = 1 FcTypeVoid = 0 FcTypeInteger = 1 FcTypeDouble = 2 FcTypeString = 3 FcTypeBool = 4 FcTypeMatrix = 5 FcTypeCharSet = 6 FcTypeFTFace = 7 FcTypeLangSet = 8 FcType = c_int class _FcValueUnion(Union): _fields_ = [('s', c_char_p), ('i', c_int), ('b', c_int), ('d', c_double), ('m', c_void_p), ('c', c_void_p), ('f', c_void_p), ('p', c_void_p), ('l', c_void_p)] class FcValue(Structure): _fields_ = [('type', FcType), ('u', _FcValueUnion)] class FcFontSet(Structure): _fields_ = [('nfont', c_int), ('sfont', c_int), ('fonts', POINTER(c_void_p))] class FcObjectSet(Structure): _fields_ = [('nobject', c_int), ('sobject', c_int), ('objects', c_void_p)] fontconfig.FcConfigSubstitute.argtypes = [c_void_p, c_void_p, c_int] fontconfig.FcDefaultSubstitute.argtypes = [c_void_p] fontconfig.FcFontMatch.restype = c_void_p fontconfig.FcFontMatch.argtypes = [c_void_p, c_void_p, c_void_p] fontconfig.FcPatternBuild.restype = c_void_p fontconfig.FcPatternCreate.restype = c_void_p fontconfig.FcPatternAddDouble.argtypes = [c_void_p, c_char_p, c_double] fontconfig.FcPatternAddInteger.argtypes = [c_void_p, c_char_p, c_int] fontconfig.FcPatternAddString.argtypes = [c_void_p, c_char_p, c_char_p] fontconfig.FcPatternDestroy.argtypes = [c_void_p] fontconfig.FcPatternGetFTFace.argtypes = [c_void_p, c_char_p, c_int, c_void_p] fontconfig.FcPatternGet.argtypes = [c_void_p, c_char_p, c_int, c_void_p] fontconfig.FcObjectSetBuild.argtypes = [c_char_p, c_char_p, c_char_p, c_char_p] fontconfig.FcObjectSetBuild.restype = FcObjectSet fontconfig.FcFontList.restype = FcFontSet fontconfig.FcFontList.argtypes = [c_void_p, c_void_p, FcObjectSet] fontconfig.FcNameUnparse.restype = c_char_p fontconfig.FcNameUnparse.argtypes = [c_void_p] fontconfig.FcFontSetDestroy.argtypes = [FcFontSet] fontconfig.FcFontSort.restype = FcFontSet fontconfig.FcFontSort.argtypes = [c_void_p, c_void_p, c_bool, c_void_p, c_void_p] fontconfig.FcConfigGetCurrent.restype = c_void_p def find_font(face, bold, italic): """Find font""" bold = FC_WEIGHT_BOLD if bold else FC_WEIGHT_REGULAR italic = FC_SLANT_ITALIC if italic else FC_SLANT_ROMAN face = face.encode('utf8') fontconfig.FcInit() pattern = fontconfig.FcPatternCreate() fontconfig.FcPatternAddInteger(pattern, FC_WEIGHT, bold) fontconfig.FcPatternAddInteger(pattern, FC_SLANT, italic) fontconfig.FcPatternAddString(pattern, FC_FAMILY, face) fontconfig.FcConfigSubstitute(0, pattern, FcMatchPattern) fontconfig.FcDefaultSubstitute(pattern) result = FcType() match = fontconfig.FcFontMatch(0, pattern, byref(result)) fontconfig.FcPatternDestroy(pattern) if not match: raise RuntimeError('Could not match font "%s"' % face) value = FcValue() fontconfig.FcPatternGet(match, FC_FAMILY, 0, byref(value)) if(value.u.s != face): warnings.warn('Could not find face match "%s", falling back to "%s"' % (face, value.u.s)) result = fontconfig.FcPatternGet(match, FC_FILE, 0, byref(value)) if result != 0: raise RuntimeError('No filename or FT face for "%s"' % face) fname = value.u.s return fname.decode('utf-8') def _list_fonts(): """List system fonts""" stdout_, stderr = run_subprocess(['fc-list', ':', 'family']) vals = [v.split(',')[0] for v in stdout_.strip().splitlines(False)] return vals �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/ext/gdi32plus.py������������������������������������������������������������������0000664�0001750�0001750�00000014737�12527672621�020551� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2015, Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- # Adapted from Pyglet import atexit from functools import partial import struct from ctypes import (windll, Structure, POINTER, byref, WINFUNCTYPE, c_uint, c_float, c_int, c_ulong, c_uint64, c_void_p, c_uint32, c_wchar, c_wchar_p) from ctypes.wintypes import (LONG, BYTE, HFONT, HGDIOBJ, BOOL, UINT, INT, DWORD, LPARAM) try: import _winreg as winreg except ImportError: import winreg # noqa, analysis:ignore _64_bit = (8 * struct.calcsize("P")) == 64 LF_FACESIZE = 32 FW_BOLD = 700 FW_NORMAL = 400 ANTIALIASED_QUALITY = 4 FontStyleBold = 1 FontStyleItalic = 2 UnitPixel = 2 UnitPoint = 3 DEFAULT_CHARSET = 1 ANSI_CHARSET = 0 TRUETYPE_FONTTYPE = 4 GM_ADVANCED = 2 CSIDL_FONTS = 0x0014 PixelFormat24bppRGB = 137224 PixelFormat32bppRGB = 139273 PixelFormat32bppARGB = 2498570 DriverStringOptionsCmapLookup = 1 DriverStringOptionsRealizedAdvance = 4 TextRenderingHintAntiAlias = 4 TextRenderingHintAntiAliasGridFit = 3 ImageLockModeRead = 1 StringFormatFlagsMeasureTrailingSpaces = 0x00000800 StringFormatFlagsNoClip = 0x00004000 StringFormatFlagsNoFitBlackBox = 0x00000004 INT_PTR = c_int REAL = c_float TCHAR = c_wchar UINT32 = c_uint32 HDC = c_void_p PSTR = c_uint64 if _64_bit else c_uint HORZSIZE = 4 VERTSIZE = 6 HORZRES = 8 VERTRES = 10 # gdi32 class POINT(Structure): _fields_ = [('x', LONG), ('y', LONG)] class RECT(Structure): _fields_ = [('left', LONG), ('top', LONG), ('right', LONG), ('bottom', LONG)] class PANOSE(Structure): _fields_ = [ ('bFamilyType', BYTE), ('bSerifStyle', BYTE), ('bWeight', BYTE), ('bProportion', BYTE), ('bContrast', BYTE), ('bStrokeVariation', BYTE), ('bArmStyle', BYTE), ('bLetterform', BYTE), ('bMidline', BYTE), ('bXHeight', BYTE)] class TEXTMETRIC(Structure): _fields_ = [ ('tmHeight', LONG), ('tmAscent', LONG), ('tmDescent', LONG), ('tmInternalLeading', LONG), ('tmExternalLeading', LONG), ('tmAveCharWidth', LONG), ('tmMaxCharWidth', LONG), ('tmWeight', LONG), ('tmOverhang', LONG), ('tmDigitizedAspectX', LONG), ('tmDigitizedAspectY', LONG), ('tmFirstChar', TCHAR), ('tmLastChar', TCHAR), ('tmDefaultChar', TCHAR), ('tmBreakChar', TCHAR), ('tmItalic', BYTE), ('tmUnderlined', BYTE), ('tmStruckOut', BYTE), ('tmPitchAndFamily', BYTE), ('tmCharSet', BYTE)] class OUTLINETEXTMETRIC(Structure): _fields_ = [ ('otmSize', UINT), ('otmTextMetrics', TEXTMETRIC), ('otmMysteryBytes', BYTE), ('otmPanoseNumber', PANOSE), ('otmMysteryByte', BYTE), ('otmfsSelection', UINT), ('otmfsType', UINT), ('otmsCharSlopeRise', INT), ('otmsCharSlopeRun', INT), ('otmItalicAngle', INT), ('otmEMSquare', UINT), ('otmAscent', INT), ('otmDescent', INT), ('otmLineGap', UINT), ('otmsCapEmHeight', UINT), ('otmsXHeight', UINT), ('otmrcFontBox', RECT), ('otmMacAscent', INT), ('otmMacDescent', INT), ('otmMacLineGap', UINT), ('otmusMinimumPPEM', UINT), ('otmptSubscriptSize', POINT), ('otmptSubscriptOffset', POINT), ('otmptSuperscriptSize', POINT), ('otmptSuperscriptOffset', POINT), ('otmsStrikeoutSize', UINT), ('otmsStrikeoutPosition', INT), ('otmsUnderscoreSize', INT), ('otmsUnderscorePosition', INT), ('otmpFamilyName', PSTR), ('otmpFaceName', PSTR), ('otmpStyleName', PSTR), ('otmpFullName', PSTR), ('junk', (BYTE) * 1024)] # room for strs class LOGFONT(Structure): _fields_ = [ ('lfHeight', LONG), ('lfWidth', LONG), ('lfEscapement', LONG), ('lfOrientation', LONG), ('lfWeight', LONG), ('lfItalic', BYTE), ('lfUnderline', BYTE), ('lfStrikeOut', BYTE), ('lfCharSet', BYTE), ('lfOutPrecision', BYTE), ('lfClipPrecision', BYTE), ('lfQuality', BYTE), ('lfPitchAndFamily', BYTE), ('lfFaceName', (TCHAR * LF_FACESIZE))] gdi32 = windll.gdi32 gdi32.CreateFontIndirectW.restype = HFONT gdi32.CreateFontIndirectW.argtypes = [POINTER(LOGFONT)] gdi32.SelectObject.restype = HGDIOBJ gdi32.SelectObject.argtypes = [HDC, HGDIOBJ] gdi32.SetGraphicsMode.restype = INT gdi32.SetGraphicsMode.argtypes = [HDC, INT] gdi32.GetTextMetricsW.restype = BOOL gdi32.GetTextMetricsW.argtypes = [HDC, POINTER(TEXTMETRIC)] FONTENUMPROC = WINFUNCTYPE(INT, POINTER(LOGFONT), POINTER(TEXTMETRIC), DWORD, c_void_p) gdi32.EnumFontFamiliesExW.restype = INT gdi32.EnumFontFamiliesExW.argtypes = [HDC, POINTER(LOGFONT), FONTENUMPROC, LPARAM, DWORD] gdi32.GetOutlineTextMetricsW.restype = UINT gdi32.GetOutlineTextMetricsW.argtypes = [HDC, UINT, POINTER(OUTLINETEXTMETRIC)] gdi32.GetDeviceCaps.argtypes = [HDC, INT] gdi32.GetDeviceCaps.restype = INT user32 = windll.user32 user32.GetDC.restype = HDC # HDC user32.GetDC.argtypes = [UINT32] # HWND user32.ReleaseDC.argtypes = [c_void_p, HDC] try: user32.SetProcessDPIAware.argtypes = [] except AttributeError: pass # not present on XP # gdiplus class GdiplusStartupInput(Structure): _fields_ = [ ('GdiplusVersion', UINT32), ('DebugEventCallback', c_void_p), ('SuppressBackgroundThread', BOOL), ('SuppressExternalCodecs', BOOL)] class GdiplusStartupOutput(Structure): _fields = [('NotificationHookProc', c_void_p), ('NotificationUnhookProc', c_void_p)] gdiplus = windll.gdiplus gdiplus.GdipCreateFontFamilyFromName.restype = c_int gdiplus.GdipCreateFontFamilyFromName.argtypes = [c_wchar_p, c_void_p, c_void_p] gdiplus.GdipNewPrivateFontCollection.restype = c_int gdiplus.GdipNewPrivateFontCollection.argtypes = [c_void_p] gdiplus.GdipPrivateAddFontFile.restype = c_int gdiplus.GdipPrivateAddFontFile.argtypes = [c_void_p, c_wchar_p] gdiplus.GdipGetFamilyName.restype = c_int gdiplus.GdipGetFamilyName.argtypes = [c_void_p, c_wchar_p, c_int] def gdiplus_init(): token = c_ulong() startup_in = GdiplusStartupInput() startup_in.GdiplusVersion = 1 startup_out = GdiplusStartupOutput() gdiplus.GdiplusStartup(byref(token), byref(startup_in), byref(startup_out)) atexit.register(partial(gdiplus.GdiplusShutdown, token)) gdiplus_init() ���������������������������������vispy-0.4.0/vispy/ext/__init__.py�������������������������������������������������������������������0000664�0001750�0001750�00000000000�12375431476�020450� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/ext/ordereddict.py����������������������������������������������������������������0000664�0001750�0001750�00000000265�12402356143�021202� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- from sys import version_info if version_info >= (2, 7): from collections import OrderedDict else: from .py24_ordereddict import OrderedDict # noqa �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/ext/gzip_open.py������������������������������������������������������������������0000664�0001750�0001750�00000001146�12527672621�020715� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from gzip import GzipFile # Python < 2.7 doesn't have context handling class gzip_open(GzipFile): def __enter__(self): if hasattr(GzipFile, '__enter__'): return GzipFile.__enter__(self) else: return self def __exit__(self, exc_type, exc_value, traceback): if hasattr(GzipFile, '__exit__'): return GzipFile.__exit__(self, exc_type, exc_value, traceback) else: return self.close() ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/ext/_mpl_py3k_compat.py�����������������������������������������������������������0000664�0001750�0001750�00000000670�12375431476�022166� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������""" Simple fixes for Python 2/3 compatibility """ import sys PY3K = sys.version_info[0] >= 3 if PY3K: import builtins import functools reduce = functools.reduce zip = builtins.zip xrange = builtins.range map = builtins.map else: import __builtin__ import itertools builtins = __builtin__ reduce = __builtin__.reduce zip = itertools.izip xrange = __builtin__.xrange map = itertools.imap ������������������������������������������������������������������������vispy-0.4.0/vispy/ext/egl.py������������������������������������������������������������������������0000664�0001750�0001750�00000027041�12527672621�017474� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ A ctypes-based API to EGL. """ import os import ctypes from ctypes import c_int as _c_int, POINTER as _POINTER, c_void_p, c_char_p _egl_file = None if 'EGL_LIBRARY' in os.environ: if os.path.exists(os.environ['EGL_LIBRARY']): _egl_file = os.path.realpath(os.environ['EGL_LIBRARY']) # Else, try to find it if _egl_file is None: _egl_file = ctypes.util.find_library('EGL') # Else, we failed and exit if _egl_file is None: raise OSError('EGL library not found') # Load it _lib = ctypes.CDLL(_egl_file) ## Constants EGL_FALSE = 0 EGL_TRUE = 1 # Out-of-band handle values EGL_DEFAULT_DISPLAY = 0 EGL_NO_CONTEXT = 0 EGL_NO_DISPLAY = 0 EGL_NO_SURFACE = 0 # Out-of-band attribute value EGL_DONT_CARE = -1 # Errors / GetError return values EGL_SUCCESS = 0x3000 EGL_NOT_INITIALIZED = 0x3001 EGL_BAD_ACCESS = 0x3002 EGL_BAD_ALLOC = 0x3003 EGL_BAD_ATTRIBUTE = 0x3004 EGL_BAD_CONFIG = 0x3005 EGL_BAD_CONTEXT = 0x3006 EGL_BAD_CURRENT_SURFACE = 0x3007 EGL_BAD_DISPLAY = 0x3008 EGL_BAD_MATCH = 0x3009 EGL_BAD_NATIVE_PIXMAP = 0x300A EGL_BAD_NATIVE_WINDOW = 0x300B EGL_BAD_PARAMETER = 0x300C EGL_BAD_SURFACE = 0x300D EGL_CONTEXT_LOST = 0x300E # EGL 1.1 - IMG_power_management # Reserved 0x300F-0x301F for additional errors # Config attributes EGL_BUFFER_SIZE = 0x3020 EGL_ALPHA_SIZE = 0x3021 EGL_BLUE_SIZE = 0x3022 EGL_GREEN_SIZE = 0x3023 EGL_RED_SIZE = 0x3024 EGL_DEPTH_SIZE = 0x3025 EGL_STENCIL_SIZE = 0x3026 EGL_CONFIG_CAVEAT = 0x3027 EGL_CONFIG_ID = 0x3028 EGL_LEVEL = 0x3029 EGL_MAX_PBUFFER_HEIGHT = 0x302A EGL_MAX_PBUFFER_PIXELS = 0x302B EGL_MAX_PBUFFER_WIDTH = 0x302C EGL_NATIVE_RENDERABLE = 0x302D EGL_NATIVE_VISUAL_ID = 0x302E EGL_NATIVE_VISUAL_TYPE = 0x302F EGL_SAMPLES = 0x3031 EGL_SAMPLE_BUFFERS = 0x3032 EGL_SURFACE_TYPE = 0x3033 EGL_TRANSPARENT_TYPE = 0x3034 EGL_TRANSPARENT_BLUE_VALUE = 0x3035 EGL_TRANSPARENT_GREEN_VALUE = 0x3036 EGL_TRANSPARENT_RED_VALUE = 0x3037 EGL_NONE = 0x3038 # Attrib list terminator EGL_BIND_TO_TEXTURE_RGB = 0x3039 EGL_BIND_TO_TEXTURE_RGBA = 0x303A EGL_MIN_SWAP_INTERVAL = 0x303B EGL_MAX_SWAP_INTERVAL = 0x303C EGL_LUMINANCE_SIZE = 0x303D EGL_ALPHA_MASK_SIZE = 0x303E EGL_COLOR_BUFFER_TYPE = 0x303F EGL_RENDERABLE_TYPE = 0x3040 EGL_MATCH_NATIVE_PIXMAP = 0x3041 # Pseudo-attribute (not queryable) EGL_CONFORMANT = 0x3042 # Reserved 0x3041-0x304F for additional config attributes # Config attribute values EGL_SLOW_CONFIG = 0x3050 # EGL_CONFIG_CAVEAT value EGL_NON_CONFORMANT_CONFIG = 0x3051 # EGL_CONFIG_CAVEAT value EGL_TRANSPARENT_RGB = 0x3052 # EGL_TRANSPARENT_TYPE value EGL_RGB_BUFFER = 0x308E # EGL_COLOR_BUFFER_TYPE value EGL_LUMINANCE_BUFFER = 0x308F # EGL_COLOR_BUFFER_TYPE value # More config attribute values, for EGL_TEXTURE_FORMAT EGL_NO_TEXTURE = 0x305C EGL_TEXTURE_RGB = 0x305D EGL_TEXTURE_RGBA = 0x305E EGL_TEXTURE_2D = 0x305F # Config attribute mask bits EGL_PBUFFER_BIT = 0x0001 # EGL_SURFACE_TYPE mask bits EGL_PIXMAP_BIT = 0x0002 # EGL_SURFACE_TYPE mask bits EGL_WINDOW_BIT = 0x0004 # EGL_SURFACE_TYPE mask bits EGL_VG_COLORSPACE_LINEAR_BIT = 0x0020 # EGL_SURFACE_TYPE mask bits EGL_VG_ALPHA_FORMAT_PRE_BIT = 0x0040 # EGL_SURFACE_TYPE mask bits EGL_MULTISAMPLE_RESOLVE_BOX_BIT = 0x0200 # EGL_SURFACE_TYPE mask bits EGL_SWAP_BEHAVIOR_PRESERVED_BIT = 0x0400 # EGL_SURFACE_TYPE mask bits EGL_OPENGL_ES_BIT = 0x0001 # EGL_RENDERABLE_TYPE mask bits EGL_OPENVG_BIT = 0x0002 # EGL_RENDERABLE_TYPE mask bits EGL_OPENGL_ES2_BIT = 0x0004 # EGL_RENDERABLE_TYPE mask bits EGL_OPENGL_BIT = 0x0008 # EGL_RENDERABLE_TYPE mask bits # QueryString targets EGL_VENDOR = 0x3053 EGL_VERSION = 0x3054 EGL_EXTENSIONS = 0x3055 EGL_CLIENT_APIS = 0x308D # QuerySurface / SurfaceAttrib / CreatePbufferSurface targets EGL_HEIGHT = 0x3056 EGL_WIDTH = 0x3057 EGL_LARGEST_PBUFFER = 0x3058 EGL_TEXTURE_FORMAT = 0x3080 EGL_TEXTURE_TARGET = 0x3081 EGL_MIPMAP_TEXTURE = 0x3082 EGL_MIPMAP_LEVEL = 0x3083 EGL_RENDER_BUFFER = 0x3086 EGL_VG_COLORSPACE = 0x3087 EGL_VG_ALPHA_FORMAT = 0x3088 EGL_HORIZONTAL_RESOLUTION = 0x3090 EGL_VERTICAL_RESOLUTION = 0x3091 EGL_PIXEL_ASPECT_RATIO = 0x3092 EGL_SWAP_BEHAVIOR = 0x3093 EGL_MULTISAMPLE_RESOLVE = 0x3099 # EGL_RENDER_BUFFER values / BindTexImage / ReleaseTexImage buffer targets EGL_BACK_BUFFER = 0x3084 EGL_SINGLE_BUFFER = 0x3085 # OpenVG color spaces EGL_VG_COLORSPACE_sRGB = 0x3089 # EGL_VG_COLORSPACE value EGL_VG_COLORSPACE_LINEAR = 0x308A # EGL_VG_COLORSPACE value # OpenVG alpha formats EGL_VG_ALPHA_FORMAT_NONPRE = 0x308B # EGL_ALPHA_FORMAT value EGL_VG_ALPHA_FORMAT_PRE = 0x308C # EGL_ALPHA_FORMAT value # Constant scale factor by which fractional display resolutions & # * aspect ratio are scaled when queried as integer values. EGL_DISPLAY_SCALING = 10000 # Unknown display resolution/aspect ratio EGL_UNKNOWN = -1 # Back buffer swap behaviors EGL_BUFFER_PRESERVED = 0x3094 # EGL_SWAP_BEHAVIOR value EGL_BUFFER_DESTROYED = 0x3095 # EGL_SWAP_BEHAVIOR value # CreatePbufferFromClientBuffer buffer types EGL_OPENVG_IMAGE = 0x3096 # QueryContext targets EGL_CONTEXT_CLIENT_TYPE = 0x3097 # CreateContext attributes EGL_CONTEXT_CLIENT_VERSION = 0x3098 # Multisample resolution behaviors EGL_MULTISAMPLE_RESOLVE_DEFAULT = 0x309A # EGL_MULTISAMPLE_RESOLVE value EGL_MULTISAMPLE_RESOLVE_BOX = 0x309B # EGL_MULTISAMPLE_RESOLVE value # BindAPI/QueryAPI targets EGL_OPENGL_ES_API = 0x30A0 EGL_OPENVG_API = 0x30A1 EGL_OPENGL_API = 0x30A2 # GetCurrentSurface targets EGL_DRAW = 0x3059 EGL_READ = 0x305A # WaitNative engines EGL_CORE_NATIVE_ENGINE = 0x305B # EGL 1.2 tokens renamed for consistency in EGL 1.3 EGL_COLORSPACE = EGL_VG_COLORSPACE EGL_ALPHA_FORMAT = EGL_VG_ALPHA_FORMAT EGL_COLORSPACE_sRGB = EGL_VG_COLORSPACE_sRGB EGL_COLORSPACE_LINEAR = EGL_VG_COLORSPACE_LINEAR EGL_ALPHA_FORMAT_NONPRE = EGL_VG_ALPHA_FORMAT_NONPRE EGL_ALPHA_FORMAT_PRE = EGL_VG_ALPHA_FORMAT_PRE ## The functions _lib.eglGetDisplay.argtypes = _c_int, _lib.eglGetDisplay.restype = _c_int _lib.eglInitialize.argtypes = c_void_p, _POINTER(_c_int), _POINTER(_c_int) _lib.eglTerminate.argtypes = c_void_p, _lib.eglChooseConfig.argtypes = (c_void_p, _POINTER(_c_int), _POINTER(c_void_p), _c_int, _POINTER(_c_int)) _lib.eglCreateWindowSurface.argtypes = (c_void_p, c_void_p, c_void_p, _POINTER(_c_int)) _lib.eglCreatePbufferSurface.argtypes = (c_void_p, c_void_p, _POINTER(_c_int)) _lib.eglCreateContext.argtypes = c_void_p, c_void_p, c_void_p, _POINTER(_c_int) _lib.eglMakeCurrent.argtypes = (c_void_p,) * 4 _lib.eglSwapBuffers.argtypes = (c_void_p,) * 2 _lib.eglDestroySurface.argtypes = (c_void_p,) * 2 _lib.eglQueryString.argtypes = (c_void_p, _c_int) _lib.eglQueryString.restype = c_char_p def eglGetError(): """ Check for errors, returns an enum (int). """ return _lib.eglGetError() def eglGetDisplay(display=EGL_DEFAULT_DISPLAY): """ Connect to the EGL display server. """ res = _lib.eglGetDisplay(display) if not res or res == EGL_NO_DISPLAY: raise RuntimeError('Could not create display') return res def eglInitialize(display): """ Initialize EGL and return EGL version tuple. """ majorVersion = (_c_int*1)() minorVersion = (_c_int*1)() res = _lib.eglInitialize(display, majorVersion, minorVersion) if res == EGL_FALSE: raise RuntimeError('Could not initialize') return majorVersion[0], minorVersion[0] def eglTerminate(display): """ Initialize EGL and return EGL version tuple. """ _lib.eglTerminate(display) def eglQueryString(display, name): """ Query string from display """ out = _lib.eglQueryString(display, name) if not out: raise RuntimeError('Could not query %s' % name) return out DEFAULT_ATTRIB_LIST = (EGL_RED_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_ALPHA_SIZE, 8, EGL_BIND_TO_TEXTURE_RGBA, EGL_TRUE, EGL_COLOR_BUFFER_TYPE, EGL_RGB_BUFFER, EGL_CONFORMANT, EGL_OPENGL_ES2_BIT, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_NATIVE_RENDERABLE, EGL_TRUE, EGL_SURFACE_TYPE, EGL_PBUFFER_BIT) def _convert_attrib_list(attribList): attribList = attribList or [] attribList = [a for a in attribList] + [EGL_NONE] attribList = (_c_int*len(attribList))(*attribList) return attribList def eglChooseConfig(display, attribList=DEFAULT_ATTRIB_LIST): attribList = _convert_attrib_list(attribList) numConfigs = (_c_int*1)() _lib.eglChooseConfig(display, attribList, None, 0, numConfigs) n = numConfigs[0] if n <= 0: raise RuntimeError('Could not find any suitable config.') config = (c_void_p*n)() _lib.eglChooseConfig(display, attribList, config, n, numConfigs) return config def _check_res(res): if res == EGL_NO_SURFACE: e = eglGetError() else: return res if e == EGL_BAD_MATCH: raise ValueError('Cannot create surface: attributes do not match ' + 'or given config cannot render in window.') elif e == EGL_BAD_CONFIG: raise ValueError('Cannot create surface: given config is not ' + 'supported by this system.') elif e == EGL_BAD_NATIVE_WINDOW: raise ValueError('Cannot create surface: the given native window ' + 'handle is invalid.') elif e == EGL_BAD_ALLOC: raise RuntimeError('Could not allocate surface: not enough ' + 'resources or native window already associated ' + 'with another config.') else: raise RuntimeError('Could not create window surface due to ' + 'unknown error: %i' % e) def eglCreateWindowSurface(display, config, window, attribList=None): # Deal with attrib list attribList = _convert_attrib_list(attribList) return _check_res(_lib.eglCreateWindowSurface(display, config, window, attribList)) def eglCreatePbufferSurface(display, config, attribList=None): # Deal with attrib list attribList = _convert_attrib_list(attribList) # return _check_res(_lib.eglCreatePbufferSurface(display, config, attribList)) def eglCreateContext(display, config, shareContext=EGL_NO_CONTEXT, attribList=None): # Deal with attrib list attribList = attribList or [EGL_CONTEXT_CLIENT_VERSION, 2] attribList = [a for a in attribList] + [EGL_NONE] attribList = (_c_int*len(attribList))(*attribList) # res = _lib.eglCreateContext(display, config, shareContext, attribList) if res == EGL_NO_CONTEXT: e = eglGetError() if e == EGL_BAD_CONFIG: raise ValueError('Could not create context: given config is ' + 'not supported by this system.') else: raise RuntimeError('Could not create context due to ' + 'unknown error: %i' % e) return res def eglMakeCurrent(display, draw, read, context): res = _lib.eglMakeCurrent(display, draw, read, context) if res == EGL_FALSE: raise RuntimeError('Could not make the context current.') def eglSwapBuffers(display, surface): res = _lib.eglSwapBuffers(display, surface) if res == EGL_FALSE: raise RuntimeError('Could not swap buffers.') def eglDestroySurface(display, surface): res = _lib.eglDestroySurface(display, surface) if res == EGL_FALSE: raise RuntimeError('Could not destroy surface') �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/ext/six.py������������������������������������������������������������������������0000664�0001750�0001750�00000050351�12510536123�017514� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������"""Utilities for writing code that runs on Python 2 and 3""" # Copyright (c) 2010-2013 Benjamin Peterson # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. import io import operator import sys import types __author__ = "Benjamin Peterson " __version__ = "1.4.1" # Useful for very coarse version differentiation. PY2 = sys.version_info[0] == 2 PY3 = sys.version_info[0] == 3 if PY3: string_types = str, integer_types = int, class_types = type, text_type = str binary_type = bytes file_types = (io.TextIOWrapper, io.BufferedRandom) MAXSIZE = sys.maxsize else: string_types = basestring, integer_types = (int, long) class_types = (type, types.ClassType) text_type = unicode binary_type = str file_types = (file, io.TextIOWrapper, io.BufferedRandom) if sys.platform.startswith("java"): # Jython always uses 32 bits. MAXSIZE = int((1 << 31) - 1) else: # It's possible to have sizeof(long) != sizeof(Py_ssize_t). class X(object): def __len__(self): return 1 << 31 try: len(X()) except OverflowError: # 32-bit MAXSIZE = int((1 << 31) - 1) else: # 64-bit MAXSIZE = int((1 << 63) - 1) del X def _add_doc(func, doc): """Add documentation to a function.""" func.__doc__ = doc def _import_module(name): """Import module, returning the module after the last dot.""" __import__(name) return sys.modules[name] class _LazyDescr(object): def __init__(self, name): self.name = name def __get__(self, obj, tp): result = self._resolve() setattr(obj, self.name, result) # This is a bit ugly, but it avoids running this again. delattr(tp, self.name) return result class MovedModule(_LazyDescr): def __init__(self, name, old, new=None): super(MovedModule, self).__init__(name) if PY3: if new is None: new = name self.mod = new else: self.mod = old def _resolve(self): return _import_module(self.mod) class MovedAttribute(_LazyDescr): def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None): super(MovedAttribute, self).__init__(name) if PY3: if new_mod is None: new_mod = name self.mod = new_mod if new_attr is None: if old_attr is None: new_attr = name else: new_attr = old_attr self.attr = new_attr else: self.mod = old_mod if old_attr is None: old_attr = name self.attr = old_attr def _resolve(self): module = _import_module(self.mod) return getattr(module, self.attr) class _MovedItems(types.ModuleType): """Lazy loading of moved objects""" _moved_attributes = [ MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"), MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"), MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"), MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"), MovedAttribute("map", "itertools", "builtins", "imap", "map"), MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"), MovedAttribute("reload_module", "__builtin__", "imp", "reload"), MovedAttribute("reduce", "__builtin__", "functools"), MovedAttribute("StringIO", "StringIO", "io"), MovedAttribute("UserString", "UserString", "collections"), MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"), MovedAttribute("zip", "itertools", "builtins", "izip", "zip"), MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"), MovedModule("builtins", "__builtin__"), MovedModule("configparser", "ConfigParser"), MovedModule("copyreg", "copy_reg"), MovedModule("http_cookiejar", "cookielib", "http.cookiejar"), MovedModule("http_cookies", "Cookie", "http.cookies"), MovedModule("html_entities", "htmlentitydefs", "html.entities"), MovedModule("html_parser", "HTMLParser", "html.parser"), MovedModule("http_client", "httplib", "http.client"), MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"), MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"), MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"), MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"), MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"), MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"), MovedModule("cPickle", "cPickle", "pickle"), MovedModule("queue", "Queue"), MovedModule("reprlib", "repr"), MovedModule("socketserver", "SocketServer"), MovedModule("tkinter", "Tkinter"), MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"), MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"), MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"), MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"), MovedModule("tkinter_tix", "Tix", "tkinter.tix"), MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"), MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"), MovedModule("tkinter_colorchooser", "tkColorChooser", "tkinter.colorchooser"), MovedModule("tkinter_commondialog", "tkCommonDialog", "tkinter.commondialog"), MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"), MovedModule("tkinter_font", "tkFont", "tkinter.font"), MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"), MovedModule("tkinter_tksimpledialog", "tkSimpleDialog", "tkinter.simpledialog"), MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"), MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"), MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"), MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"), MovedModule("winreg", "_winreg"), ] for attr in _moved_attributes: setattr(_MovedItems, attr.name, attr) del attr moves = sys.modules[__name__ + ".moves"] = _MovedItems(__name__ + ".moves") class Module_six_moves_urllib_parse(types.ModuleType): """Lazy loading of moved objects in six.moves.urllib_parse""" _urllib_parse_moved_attributes = [ MovedAttribute("ParseResult", "urlparse", "urllib.parse"), MovedAttribute("parse_qs", "urlparse", "urllib.parse"), MovedAttribute("parse_qsl", "urlparse", "urllib.parse"), MovedAttribute("urldefrag", "urlparse", "urllib.parse"), MovedAttribute("urljoin", "urlparse", "urllib.parse"), MovedAttribute("urlparse", "urlparse", "urllib.parse"), MovedAttribute("urlsplit", "urlparse", "urllib.parse"), MovedAttribute("urlunparse", "urlparse", "urllib.parse"), MovedAttribute("urlunsplit", "urlparse", "urllib.parse"), MovedAttribute("quote", "urllib", "urllib.parse"), MovedAttribute("quote_plus", "urllib", "urllib.parse"), MovedAttribute("unquote", "urllib", "urllib.parse"), MovedAttribute("unquote_plus", "urllib", "urllib.parse"), MovedAttribute("urlencode", "urllib", "urllib.parse"), ] for attr in _urllib_parse_moved_attributes: setattr(Module_six_moves_urllib_parse, attr.name, attr) del attr sys.modules[__name__ + ".moves.urllib_parse"] = Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse") sys.modules[__name__ + ".moves.urllib.parse"] = Module_six_moves_urllib_parse(__name__ + ".moves.urllib.parse") class Module_six_moves_urllib_error(types.ModuleType): """Lazy loading of moved objects in six.moves.urllib_error""" _urllib_error_moved_attributes = [ MovedAttribute("URLError", "urllib2", "urllib.error"), MovedAttribute("HTTPError", "urllib2", "urllib.error"), MovedAttribute("ContentTooShortError", "urllib", "urllib.error"), ] for attr in _urllib_error_moved_attributes: setattr(Module_six_moves_urllib_error, attr.name, attr) del attr sys.modules[__name__ + ".moves.urllib_error"] = Module_six_moves_urllib_error(__name__ + ".moves.urllib_error") sys.modules[__name__ + ".moves.urllib.error"] = Module_six_moves_urllib_error(__name__ + ".moves.urllib.error") class Module_six_moves_urllib_request(types.ModuleType): """Lazy loading of moved objects in six.moves.urllib_request""" _urllib_request_moved_attributes = [ MovedAttribute("urlopen", "urllib2", "urllib.request"), MovedAttribute("install_opener", "urllib2", "urllib.request"), MovedAttribute("build_opener", "urllib2", "urllib.request"), MovedAttribute("pathname2url", "urllib", "urllib.request"), MovedAttribute("url2pathname", "urllib", "urllib.request"), MovedAttribute("getproxies", "urllib", "urllib.request"), MovedAttribute("Request", "urllib2", "urllib.request"), MovedAttribute("OpenerDirector", "urllib2", "urllib.request"), MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"), MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"), MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"), MovedAttribute("ProxyHandler", "urllib2", "urllib.request"), MovedAttribute("BaseHandler", "urllib2", "urllib.request"), MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"), MovedAttribute("HTTPPasswordMgrWithDefaultRealm", "urllib2", "urllib.request"), MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"), MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"), MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"), MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"), MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"), MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"), MovedAttribute("HTTPHandler", "urllib2", "urllib.request"), MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"), MovedAttribute("FileHandler", "urllib2", "urllib.request"), MovedAttribute("FTPHandler", "urllib2", "urllib.request"), MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"), MovedAttribute("UnknownHandler", "urllib2", "urllib.request"), MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"), MovedAttribute("urlretrieve", "urllib", "urllib.request"), MovedAttribute("urlcleanup", "urllib", "urllib.request"), MovedAttribute("URLopener", "urllib", "urllib.request"), MovedAttribute("FancyURLopener", "urllib", "urllib.request"), ] for attr in _urllib_request_moved_attributes: setattr(Module_six_moves_urllib_request, attr.name, attr) del attr sys.modules[__name__ + ".moves.urllib_request"] = Module_six_moves_urllib_request(__name__ + ".moves.urllib_request") sys.modules[__name__ + ".moves.urllib.request"] = Module_six_moves_urllib_request(__name__ + ".moves.urllib.request") class Module_six_moves_urllib_response(types.ModuleType): """Lazy loading of moved objects in six.moves.urllib_response""" _urllib_response_moved_attributes = [ MovedAttribute("addbase", "urllib", "urllib.response"), MovedAttribute("addclosehook", "urllib", "urllib.response"), MovedAttribute("addinfo", "urllib", "urllib.response"), MovedAttribute("addinfourl", "urllib", "urllib.response"), ] for attr in _urllib_response_moved_attributes: setattr(Module_six_moves_urllib_response, attr.name, attr) del attr sys.modules[__name__ + ".moves.urllib_response"] = Module_six_moves_urllib_response(__name__ + ".moves.urllib_response") sys.modules[__name__ + ".moves.urllib.response"] = Module_six_moves_urllib_response(__name__ + ".moves.urllib.response") class Module_six_moves_urllib_robotparser(types.ModuleType): """Lazy loading of moved objects in six.moves.urllib_robotparser""" _urllib_robotparser_moved_attributes = [ MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"), ] for attr in _urllib_robotparser_moved_attributes: setattr(Module_six_moves_urllib_robotparser, attr.name, attr) del attr sys.modules[__name__ + ".moves.urllib_robotparser"] = Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib_robotparser") sys.modules[__name__ + ".moves.urllib.robotparser"] = Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser") class Module_six_moves_urllib(types.ModuleType): """Create a six.moves.urllib namespace that resembles the Python 3 namespace""" parse = sys.modules[__name__ + ".moves.urllib_parse"] error = sys.modules[__name__ + ".moves.urllib_error"] request = sys.modules[__name__ + ".moves.urllib_request"] response = sys.modules[__name__ + ".moves.urllib_response"] robotparser = sys.modules[__name__ + ".moves.urllib_robotparser"] sys.modules[__name__ + ".moves.urllib"] = Module_six_moves_urllib(__name__ + ".moves.urllib") def add_move(move): """Add an item to six.moves.""" setattr(_MovedItems, move.name, move) def remove_move(name): """Remove item from six.moves.""" try: delattr(_MovedItems, name) except AttributeError: try: del moves.__dict__[name] except KeyError: raise AttributeError("no such move, %r" % (name,)) if PY3: _meth_func = "__func__" _meth_self = "__self__" _func_closure = "__closure__" _func_code = "__code__" _func_defaults = "__defaults__" _func_globals = "__globals__" _iterkeys = "keys" _itervalues = "values" _iteritems = "items" _iterlists = "lists" else: _meth_func = "im_func" _meth_self = "im_self" _func_closure = "func_closure" _func_code = "func_code" _func_defaults = "func_defaults" _func_globals = "func_globals" _iterkeys = "iterkeys" _itervalues = "itervalues" _iteritems = "iteritems" _iterlists = "iterlists" try: advance_iterator = next except NameError: def advance_iterator(it): return it.next() next = advance_iterator try: callable = callable except NameError: def callable(obj): return any("__call__" in klass.__dict__ for klass in type(obj).__mro__) if PY3: def get_unbound_function(unbound): return unbound create_bound_method = types.MethodType Iterator = object else: def get_unbound_function(unbound): return unbound.im_func def create_bound_method(func, obj): return types.MethodType(func, obj, obj.__class__) class Iterator(object): def next(self): return type(self).__next__(self) callable = callable _add_doc(get_unbound_function, """Get the function out of a possibly unbound function""") get_method_function = operator.attrgetter(_meth_func) get_method_self = operator.attrgetter(_meth_self) get_function_closure = operator.attrgetter(_func_closure) get_function_code = operator.attrgetter(_func_code) get_function_defaults = operator.attrgetter(_func_defaults) get_function_globals = operator.attrgetter(_func_globals) def iterkeys(d, **kw): """Return an iterator over the keys of a dictionary.""" return iter(getattr(d, _iterkeys)(**kw)) def itervalues(d, **kw): """Return an iterator over the values of a dictionary.""" return iter(getattr(d, _itervalues)(**kw)) def iteritems(d, **kw): """Return an iterator over the (key, value) pairs of a dictionary.""" return iter(getattr(d, _iteritems)(**kw)) def iterlists(d, **kw): """Return an iterator over the (key, [values]) pairs of a dictionary.""" return iter(getattr(d, _iterlists)(**kw)) if PY3: def b(s): return s.encode("latin-1") def u(s): return s unichr = chr if sys.version_info[1] <= 1: def int2byte(i): return bytes((i,)) else: # This is about 2x faster than the implementation above on 3.2+ int2byte = operator.methodcaller("to_bytes", 1, "big") byte2int = operator.itemgetter(0) indexbytes = operator.getitem iterbytes = iter import io StringIO = io.StringIO BytesIO = io.BytesIO else: def b(s): return s def u(s): return unicode(s, "unicode_escape") unichr = unichr int2byte = chr def byte2int(bs): return ord(bs[0]) def indexbytes(buf, i): return ord(buf[i]) def iterbytes(buf): return (ord(byte) for byte in buf) import StringIO StringIO = BytesIO = StringIO.StringIO _add_doc(b, """Byte literal""") _add_doc(u, """Text literal""") if PY3: import builtins exec_ = getattr(builtins, "exec") def reraise(tp, value, tb=None): if value.__traceback__ is not tb: raise value.with_traceback(tb) raise value print_ = getattr(builtins, "print") del builtins else: def exec_(_code_, _globs_=None, _locs_=None): """Execute code in a namespace.""" if _globs_ is None: frame = sys._getframe(1) _globs_ = frame.f_globals if _locs_ is None: _locs_ = frame.f_locals del frame elif _locs_ is None: _locs_ = _globs_ exec("""exec _code_ in _globs_, _locs_""") exec_("""def reraise(tp, value, tb=None): raise tp, value, tb """) def print_(*args, **kwargs): """The new-style print function.""" fp = kwargs.pop("file", sys.stdout) if fp is None: return def write(data): if not isinstance(data, basestring): data = str(data) fp.write(data) want_unicode = False sep = kwargs.pop("sep", None) if sep is not None: if isinstance(sep, unicode): want_unicode = True elif not isinstance(sep, str): raise TypeError("sep must be None or a string") end = kwargs.pop("end", None) if end is not None: if isinstance(end, unicode): want_unicode = True elif not isinstance(end, str): raise TypeError("end must be None or a string") if kwargs: raise TypeError("invalid keyword arguments to print()") if not want_unicode: for arg in args: if isinstance(arg, unicode): want_unicode = True break if want_unicode: newline = unicode("\n") space = unicode(" ") else: newline = "\n" space = " " if sep is None: sep = space if end is None: end = newline for i, arg in enumerate(args): if i: write(sep) write(arg) write(end) _add_doc(reraise, """Reraise an exception.""") def with_metaclass(meta, *bases): """Create a base class with a metaclass.""" return meta("NewBase", bases, {}) def add_metaclass(metaclass): """Class decorator for creating a class with a metaclass.""" def wrapper(cls): orig_vars = cls.__dict__.copy() orig_vars.pop('__dict__', None) orig_vars.pop('__weakref__', None) for slots_var in orig_vars.get('__slots__', ()): orig_vars.pop(slots_var) return metaclass(cls.__name__, cls.__bases__, orig_vars) return wrapper���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/geometry/�������������������������������������������������������������������������0000775�0001750�0001750�00000000000�12527674621�017404� 5����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/geometry/_triangulation_debugger.py�����������������������������������������������0000664�0001750�0001750�00000012047�12527672621�024643� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf8 -*- """ Debugging system for Triangulation class. Displays stepwise visual representation of the algorithm. This system currently requires pyqtgraph for its visual output. """ from __future__ import division import numpy as np import time from vispy.util.geometry.triangulation import Triangulation class DebugTriangulation(Triangulation): """ Visualize triangulation process stepwise to aid in debugging. *interval* specifies the diration to wait before drawing each update in the triangulation procedure. Negative values cause the display to wait until the user clicks on the window for each update. *skip* causes the display to immediately process the first N events before pausing. """ def __init__(self, pts, edges, interval=0.01, skip=0): self.interval = interval self.iteration = 0 self.skip = skip Triangulation.__init__(self, pts, edges) # visual #debugging: draw edges, front, triangles self.win = pg.plot() self.graph = pg.GraphItem(pos=pts.copy(), adj=edges.copy(), pen={'width': 3, 'color': (0, 100, 0)}) self.win.addItem(self.graph) self.front_line = pg.PlotCurveItem(pen={'width': 2, 'dash': [5, 5], 'color': 'y'}) self.win.addItem(self.front_line) self.tri_shapes = {} self.nextStep = False self.win.scene().sigMouseClicked.connect(self.mouseClicked) def mouseClicked(self): self.nextStep = True def draw_state(self): global app print("State %s" % self.iteration) self.iteration += 1 if self.iteration <= self.skip: return front_pts = self.pts[np.array(self.front)] self.front_line.setData(front_pts[:, 0], front_pts[:, 1]) self.graph.setData(pos=self.pts, adj=self.edges) # Auto-advance on timer if self.interval < 0: #Advance once per click while True: app.processEvents() time.sleep(0.01) if self.nextStep: self.nextStep = False break else: # sleep, but keep ui responsive for i in range(int(self.interval / 0.01)): app.processEvents() time.sleep(0.01) def draw_tri(self, tri, source=None): # assign triangle color based on the source that generated it color = { None: (0, 255, 255, 50), 'smooth1': (0, 255, 0, 50), 'fill_hull': (255, 255, 0, 50), 'edge_event': (100, 100, 255, 100), }[source] tpts = self.pts[np.array(tri)] path = pg.arrayToQPath(tpts[:, 0], tpts[:, 1]) shape = pg.QtGui.QGraphicsPathItem(path) shape.setPen(pg.mkPen(255, 255, 255, 100)) brush = pg.mkBrush(color) shape.setBrush(brush) self.win.addItem(shape) self.tri_shapes[tri] = shape self.draw_state() def undraw_tri(self, tri): shape = self.tri_shapes.pop(tri) self.win.removeItem(shape) self.draw_state() def add_tri(self, *args, **kwargs): Triangulation._add_tri(self, *args, **kwargs) self.draw_tri(list(self.tris.keys())[-1], source=kwargs.get('source', None)) def remove_tri(self, *args, **kwargs): k = Triangulation._remove_tri(self, *args, **kwargs) self.undraw_tri(k) def edge_event(self, *args, **kwargs): self.draw_state() Triangulation._edge_event(self, *args, **kwargs) self.draw_state() if __name__ == '__main__': import pyqtgraph as pg app = pg.mkQApp() #user input data - points and constraining edges # # Test 1 # pts = [(0, 0), (10, 0), (10, 10), (20, 10), (20, 20), (25, 20), (25, 25), (20, 25), (20, 20), (10, 17), (5, 25), (9, 30), (6, 15), (15, 12.5), (0, 5)] l = len(pts) edges = [(i, (i+1) % l) for i in range(l)] pts += [(21, 21), (24, 21), (24, 24), (21, 24)] edges += [(l, l+1), (l+1, l+2), (l+2, l+3), (l+3, l)] pts = np.array(pts, dtype=float) edges = np.array(edges, dtype=int) #t = DebugTriangulation(pts, edges, interval=-1, skip=19570) #t.triangulate() # make lines that are entirely vertical / horizontal np.random.seed(1) N = 100 pts = [[0, 0]] for i in range(N - 1): p = pts[-1][:] p[i % 2] += np.random.normal() pts.append(p) pts = np.array(pts) edges = np.zeros((N, 2), dtype=int) edges[:, 0] = np.arange(N) edges[:, 1] = np.arange(1, N + 1) % N t = DebugTriangulation(pts, edges) t.triangulate() �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/geometry/isocurve.py��������������������������������������������������������������0000664�0001750�0001750�00000013375�12527672621�021624� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2015, Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- from __future__ import division import numpy as np def isocurve(data, level, connected=False, extend_to_edge=False): """ Generate isocurve from 2D data using marching squares algorithm. Parameters ---------- data : ndarray 2D numpy array of scalar values level : float The level at which to generate an isosurface connected : bool If False, return a single long list of point pairs If True, return multiple long lists of connected point locations. (This is slower but better for drawing continuous lines) extend_to_edge : bool If True, extend the curves to reach the exact edges of the data. """ # This function is SLOW; plenty of room for optimization here. if extend_to_edge: d2 = np.empty((data.shape[0]+2, data.shape[1]+2), dtype=data.dtype) d2[1:-1, 1:-1] = data d2[0, 1:-1] = data[0] d2[-1, 1:-1] = data[-1] d2[1:-1, 0] = data[:, 0] d2[1:-1, -1] = data[:, -1] d2[0, 0] = d2[0, 1] d2[0, -1] = d2[1, -1] d2[-1, 0] = d2[-1, 1] d2[-1, -1] = d2[-1, -2] data = d2 side_table = [ [], [0, 1], [1, 2], [0, 2], [0, 3], [1, 3], [0, 1, 2, 3], [2, 3], [2, 3], [0, 1, 2, 3], [1, 3], [0, 3], [0, 2], [1, 2], [0, 1], [] ] edge_key = [ [(0, 1), (0, 0)], [(0, 0), (1, 0)], [(1, 0), (1, 1)], [(1, 1), (0, 1)] ] level = float(level) lines = [] # mark everything below the isosurface level mask = data < level ## make four sub-fields and compute indexes for grid cells index = np.zeros([x-1 for x in data.shape], dtype=np.ubyte) fields = np.empty((2, 2), dtype=object) slices = [slice(0, -1), slice(1, None)] for i in [0, 1]: for j in [0, 1]: fields[i, j] = mask[slices[i], slices[j]] vertIndex = i+2*j index += (fields[i, j] * 2**vertIndex).astype(np.ubyte) # add lines for i in range(index.shape[0]): # data x-axis for j in range(index.shape[1]): # data y-axis sides = side_table[index[i, j]] for l in range(0, len(sides), 2): # faces for this grid cell edges = sides[l:l+2] pts = [] for m in [0, 1]: # points in this face # p1, p2 are points at either side of an edge p1 = edge_key[edges[m]][0] p2 = edge_key[edges[m]][1] # v1 and v2 are the values at p1 and p2 v1 = data[i+p1[0], j+p1[1]] v2 = data[i+p2[0], j+p2[1]] f = (level-v1) / (v2-v1) fi = 1.0 - f # interpolate between corners p = (p1[0]*fi + p2[0]*f + i + 0.5, p1[1]*fi + p2[1]*f + j + 0.5) if extend_to_edge: # check bounds p = (min(data.shape[0]-2, max(0, p[0]-1)), min(data.shape[1]-2, max(0, p[1]-1))) if connected: gridKey = (i + (1 if edges[m] == 2 else 0), j + (1 if edges[m] == 3 else 0), edges[m] % 2) # give the actual position and a key identifying the # grid location (for connecting segments) pts.append((p, gridKey)) else: pts.append(p) lines.append(pts) if not connected: return lines # turn disjoint list of segments into continuous lines points = {} # maps each point to its connections for a, b in lines: if a[1] not in points: points[a[1]] = [] points[a[1]].append([a, b]) if b[1] not in points: points[b[1]] = [] points[b[1]].append([b, a]) # rearrange into chains for k in list(points.keys()): try: chains = points[k] except KeyError: # already used this point elsewhere continue for chain in chains: x = None while True: if x == chain[-1][1]: break # nothing left to do on this chain x = chain[-1][1] if x == k: # chain has looped; we're done and can ignore the opposite # chain break y = chain[-2][1] connects = points[x] for conn in connects[:]: if conn[1][1] != y: chain.extend(conn[1:]) del points[x] if chain[0][1] == chain[-1][1]: # looped chain; no need to continue the other direction chains.pop() break # extract point locations lines = [] for chain in points.values(): if len(chain) == 2: # join together ends of chain chain = chain[1][1:][::-1] + chain[0] else: chain = chain[0] lines.append([pt[0] for pt in chain]) return lines # a list of pairs of points �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/geometry/isosurface.py������������������������������������������������������������0000664�0001750�0001750�00000050241�12403376622�022114� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������import numpy as np _data_cache = None def isosurface(data, level): """ Generate isosurface from volumetric data using marching cubes algorithm. See Paul Bourke, "Polygonising a Scalar Field" (http://paulbourke.net/geometry/polygonise/) *data* 3D numpy array of scalar values *level* The level at which to generate an isosurface Returns an array of vertex coordinates (Nv, 3) and an array of per-face vertex indexes (Nf, 3) """ # For improvement, see: # # Efficient implementation of Marching Cubes' cases with topological # guarantees. # Thomas Lewiner, Helio Lopes, Antonio Wilson Vieira and Geovan Tavares. # Journal of Graphics Tools 8(2): pp. 1-15 (december 2003) (face_shift_tables, edge_shifts, edge_table, n_table_faces) = _get_data_cache() ## mark everything below the isosurface level mask = data < level ### make eight sub-fields and compute indexes for grid cells index = np.zeros([x-1 for x in data.shape], dtype=np.ubyte) fields = np.empty((2, 2, 2), dtype=object) slices = [slice(0, -1), slice(1, None)] for i in [0, 1]: for j in [0, 1]: for k in [0, 1]: fields[i, j, k] = mask[slices[i], slices[j], slices[k]] # this is just to match Bourk's vertex numbering scheme: vertIndex = i - 2*j*i + 3*j + 4*k index += (fields[i, j, k] * 2**vertIndex).astype(np.ubyte) ### Generate table of edges that have been cut cut_edges = np.zeros([x+1 for x in index.shape]+[3], dtype=np.uint32) edges = edge_table[index] for i, shift in enumerate(edge_shifts[:12]): slices = [slice(shift[j], cut_edges.shape[j]+(shift[j]-1)) for j in range(3)] cut_edges[slices[0], slices[1], slices[2], shift[3]] += edges & 2**i # for each cut edge, interpolate to see where exactly the edge is cut and # generate vertex positions m = cut_edges > 0 vertex_inds = np.argwhere(m) # argwhere is slow! vertexes = vertex_inds[:, :3].astype(np.float32).copy() dataFlat = data.reshape(data.shape[0]*data.shape[1]*data.shape[2]) ## re-use the cut_edges array as a lookup table for vertex IDs cut_edges[vertex_inds[:, 0], vertex_inds[:, 1], vertex_inds[:, 2], vertex_inds[:, 3]] = np.arange(vertex_inds.shape[0]) for i in [0, 1, 2]: vim = vertex_inds[:, 3] == i vi = vertex_inds[vim, :3] vi_flat = (vi * (np.array(data.strides[:3]) // data.itemsize)[np.newaxis, :]).sum(axis=1) v1 = dataFlat[vi_flat] v2 = dataFlat[vi_flat + data.strides[i]//data.itemsize] vertexes[vim, i] += (level-v1) / (v2-v1) ### compute the set of vertex indexes for each face. ## This works, but runs a bit slower. ## all cells with at least one face: #cells = np.argwhere((index != 0) & (index != 255)) #cellInds = index[cells[:, 0], cells[:, 1], cells[:, 2]] #verts = faceTable[cellInds] #mask = verts[..., 0, 0] != 9 ## we now have indexes into cut_edges: #verts[...,:3] += cells[:, np.newaxis, np.newaxis,:] #verts = verts[mask] ## and these are the vertex indexes we want: #faces = cut_edges[verts[..., 0], verts[..., 1], verts[..., 2], # verts[..., 3]] # To allow this to be vectorized efficiently, we count the number of faces # in each grid cell and handle each group of cells with the same number # together. # determine how many faces to assign to each grid cell n_faces = n_table_faces[index] tot_faces = n_faces.sum() faces = np.empty((tot_faces, 3), dtype=np.uint32) ptr = 0 ## this helps speed up an indexing operation later on cs = np.array(cut_edges.strides)//cut_edges.itemsize cut_edges = cut_edges.flatten() ## this, strangely, does not seem to help. #ins = np.array(index.strides)/index.itemsize #index = index.flatten() for i in range(1, 6): # expensive: # all cells which require i faces (argwhere is expensive) cells = np.argwhere(n_faces == i) if cells.shape[0] == 0: continue # index values of cells to process for this round: cellInds = index[cells[:, 0], cells[:, 1], cells[:, 2]] # expensive: verts = face_shift_tables[i][cellInds] # we now have indexes into cut_edges: verts[..., :3] += (cells[:, np.newaxis, np.newaxis, :]).astype(np.uint16) verts = verts.reshape((verts.shape[0]*i,)+verts.shape[2:]) # expensive: verts = (verts * cs[np.newaxis, np.newaxis, :]).sum(axis=2) vert_inds = cut_edges[verts] nv = vert_inds.shape[0] faces[ptr:ptr+nv] = vert_inds # .reshape((nv, 3)) ptr += nv return vertexes, faces def _get_data_cache(): # Precompute lookup tables on the first run global _data_cache if _data_cache is None: # map from grid cell index to edge index. # grid cell index tells us which corners are below the isosurface, # edge index tells us which edges are cut by the isosurface. # (Data stolen from Bourk; see above.) edge_table = np.array([ 0x0, 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c, 0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00, 0x190, 0x99, 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c, 0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90, 0x230, 0x339, 0x33, 0x13a, 0x636, 0x73f, 0x435, 0x53c, 0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30, 0x3a0, 0x2a9, 0x1a3, 0xaa, 0x7a6, 0x6af, 0x5a5, 0x4ac, 0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0, 0x460, 0x569, 0x663, 0x76a, 0x66, 0x16f, 0x265, 0x36c, 0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60, 0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff, 0x3f5, 0x2fc, 0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0, 0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55, 0x15c, 0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950, 0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc, 0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0, 0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc, 0xcc, 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0, 0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c, 0x15c, 0x55, 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650, 0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc, 0x2fc, 0x3f5, 0xff, 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0, 0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c, 0x36c, 0x265, 0x16f, 0x66, 0x76a, 0x663, 0x569, 0x460, 0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac, 0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa, 0x1a3, 0x2a9, 0x3a0, 0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c, 0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33, 0x339, 0x230, 0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c, 0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99, 0x190, 0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c, 0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x0 ], dtype=np.uint16) # Table of triangles to use for filling each grid cell. # Each set of three integers tells us which three edges to # draw a triangle between. # (Data stolen from Bourk; see above.) triTable = [ [], [0, 8, 3], [0, 1, 9], [1, 8, 3, 9, 8, 1], [1, 2, 10], [0, 8, 3, 1, 2, 10], [9, 2, 10, 0, 2, 9], [2, 8, 3, 2, 10, 8, 10, 9, 8], [3, 11, 2], [0, 11, 2, 8, 11, 0], [1, 9, 0, 2, 3, 11], [1, 11, 2, 1, 9, 11, 9, 8, 11], [3, 10, 1, 11, 10, 3], [0, 10, 1, 0, 8, 10, 8, 11, 10], [3, 9, 0, 3, 11, 9, 11, 10, 9], [9, 8, 10, 10, 8, 11], [4, 7, 8], [4, 3, 0, 7, 3, 4], [0, 1, 9, 8, 4, 7], [4, 1, 9, 4, 7, 1, 7, 3, 1], [1, 2, 10, 8, 4, 7], [3, 4, 7, 3, 0, 4, 1, 2, 10], [9, 2, 10, 9, 0, 2, 8, 4, 7], [2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4], [8, 4, 7, 3, 11, 2], [11, 4, 7, 11, 2, 4, 2, 0, 4], [9, 0, 1, 8, 4, 7, 2, 3, 11], [4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1], [3, 10, 1, 3, 11, 10, 7, 8, 4], [1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4], [4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3], [4, 7, 11, 4, 11, 9, 9, 11, 10], [9, 5, 4], [9, 5, 4, 0, 8, 3], [0, 5, 4, 1, 5, 0], [8, 5, 4, 8, 3, 5, 3, 1, 5], [1, 2, 10, 9, 5, 4], [3, 0, 8, 1, 2, 10, 4, 9, 5], [5, 2, 10, 5, 4, 2, 4, 0, 2], [2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8], [9, 5, 4, 2, 3, 11], [0, 11, 2, 0, 8, 11, 4, 9, 5], [0, 5, 4, 0, 1, 5, 2, 3, 11], [2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5], [10, 3, 11, 10, 1, 3, 9, 5, 4], [4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10], [5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3], [5, 4, 8, 5, 8, 10, 10, 8, 11], [9, 7, 8, 5, 7, 9], [9, 3, 0, 9, 5, 3, 5, 7, 3], [0, 7, 8, 0, 1, 7, 1, 5, 7], [1, 5, 3, 3, 5, 7], [9, 7, 8, 9, 5, 7, 10, 1, 2], [10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3], [8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2], [2, 10, 5, 2, 5, 3, 3, 5, 7], [7, 9, 5, 7, 8, 9, 3, 11, 2], [9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11], [2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7], [11, 2, 1, 11, 1, 7, 7, 1, 5], [9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11], [5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0], [11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0], [11, 10, 5, 7, 11, 5], [10, 6, 5], [0, 8, 3, 5, 10, 6], [9, 0, 1, 5, 10, 6], [1, 8, 3, 1, 9, 8, 5, 10, 6], [1, 6, 5, 2, 6, 1], [1, 6, 5, 1, 2, 6, 3, 0, 8], [9, 6, 5, 9, 0, 6, 0, 2, 6], [5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8], [2, 3, 11, 10, 6, 5], [11, 0, 8, 11, 2, 0, 10, 6, 5], [0, 1, 9, 2, 3, 11, 5, 10, 6], [5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11], [6, 3, 11, 6, 5, 3, 5, 1, 3], [0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6], [3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9], [6, 5, 9, 6, 9, 11, 11, 9, 8], [5, 10, 6, 4, 7, 8], [4, 3, 0, 4, 7, 3, 6, 5, 10], [1, 9, 0, 5, 10, 6, 8, 4, 7], [10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4], [6, 1, 2, 6, 5, 1, 4, 7, 8], [1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7], [8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6], [7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9], [3, 11, 2, 7, 8, 4, 10, 6, 5], [5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11], [0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6], [9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6], [8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6], [5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11], [0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7], [6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9], [10, 4, 9, 6, 4, 10], [4, 10, 6, 4, 9, 10, 0, 8, 3], [10, 0, 1, 10, 6, 0, 6, 4, 0], [8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10], [1, 4, 9, 1, 2, 4, 2, 6, 4], [3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4], [0, 2, 4, 4, 2, 6], [8, 3, 2, 8, 2, 4, 4, 2, 6], [10, 4, 9, 10, 6, 4, 11, 2, 3], [0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6], [3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10], [6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1], [9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3], [8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1], [3, 11, 6, 3, 6, 0, 0, 6, 4], [6, 4, 8, 11, 6, 8], [7, 10, 6, 7, 8, 10, 8, 9, 10], [0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10], [10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0], [10, 6, 7, 10, 7, 1, 1, 7, 3], [1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7], [2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9], [7, 8, 0, 7, 0, 6, 6, 0, 2], [7, 3, 2, 6, 7, 2], [2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7], [2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7], [1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11], [11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1], [8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6], [0, 9, 1, 11, 6, 7], [7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0], [7, 11, 6], [7, 6, 11], [3, 0, 8, 11, 7, 6], [0, 1, 9, 11, 7, 6], [8, 1, 9, 8, 3, 1, 11, 7, 6], [10, 1, 2, 6, 11, 7], [1, 2, 10, 3, 0, 8, 6, 11, 7], [2, 9, 0, 2, 10, 9, 6, 11, 7], [6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8], [7, 2, 3, 6, 2, 7], [7, 0, 8, 7, 6, 0, 6, 2, 0], [2, 7, 6, 2, 3, 7, 0, 1, 9], [1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6], [10, 7, 6, 10, 1, 7, 1, 3, 7], [10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8], [0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7], [7, 6, 10, 7, 10, 8, 8, 10, 9], [6, 8, 4, 11, 8, 6], [3, 6, 11, 3, 0, 6, 0, 4, 6], [8, 6, 11, 8, 4, 6, 9, 0, 1], [9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6], [6, 8, 4, 6, 11, 8, 2, 10, 1], [1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6], [4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9], [10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3], [8, 2, 3, 8, 4, 2, 4, 6, 2], [0, 4, 2, 4, 6, 2], [1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8], [1, 9, 4, 1, 4, 2, 2, 4, 6], [8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1], [10, 1, 0, 10, 0, 6, 6, 0, 4], [4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3], [10, 9, 4, 6, 10, 4], [4, 9, 5, 7, 6, 11], [0, 8, 3, 4, 9, 5, 11, 7, 6], [5, 0, 1, 5, 4, 0, 7, 6, 11], [11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5], [9, 5, 4, 10, 1, 2, 7, 6, 11], [6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5], [7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2], [3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6], [7, 2, 3, 7, 6, 2, 5, 4, 9], [9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7], [3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0], [6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8], [9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7], [1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4], [4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10], [7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10], [6, 9, 5, 6, 11, 9, 11, 8, 9], [3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5], [0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11], [6, 11, 3, 6, 3, 5, 5, 3, 1], [1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6], [0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10], [11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5], [6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3], [5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2], [9, 5, 6, 9, 6, 0, 0, 6, 2], [1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8], [1, 5, 6, 2, 1, 6], [1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6], [10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0], [0, 3, 8, 5, 6, 10], [10, 5, 6], [11, 5, 10, 7, 5, 11], [11, 5, 10, 11, 7, 5, 8, 3, 0], [5, 11, 7, 5, 10, 11, 1, 9, 0], [10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1], [11, 1, 2, 11, 7, 1, 7, 5, 1], [0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11], [9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7], [7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2], [2, 5, 10, 2, 3, 5, 3, 7, 5], [8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5], [9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2], [9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2], [1, 3, 5, 3, 7, 5], [0, 8, 7, 0, 7, 1, 1, 7, 5], [9, 0, 3, 9, 3, 5, 5, 3, 7], [9, 8, 7, 5, 9, 7], [5, 8, 4, 5, 10, 8, 10, 11, 8], [5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0], [0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5], [10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4], [2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8], [0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11], [0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5], [9, 4, 5, 2, 11, 3], [2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4], [5, 10, 2, 5, 2, 4, 4, 2, 0], [3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9], [5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2], [8, 4, 5, 8, 5, 3, 3, 5, 1], [0, 4, 5, 1, 0, 5], [8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5], [9, 4, 5], [4, 11, 7, 4, 9, 11, 9, 10, 11], [0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11], [1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11], [3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4], [4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2], [9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3], [11, 7, 4, 11, 4, 2, 2, 4, 0], [11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4], [2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9], [9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7], [3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10], [1, 10, 2, 8, 7, 4], [4, 9, 1, 4, 1, 7, 7, 1, 3], [4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1], [4, 0, 3, 7, 4, 3], [4, 8, 7], [9, 10, 8, 10, 11, 8], [3, 0, 9, 3, 9, 11, 11, 9, 10], [0, 1, 10, 0, 10, 8, 8, 10, 11], [3, 1, 10, 11, 3, 10], [1, 2, 11, 1, 11, 9, 9, 11, 8], [3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9], [0, 2, 11, 8, 0, 11], [3, 2, 11], [2, 3, 8, 2, 8, 10, 10, 8, 9], [9, 10, 2, 0, 9, 2], [2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8], [1, 10, 2], [1, 3, 8, 9, 1, 8], [0, 9, 1], [0, 3, 8], [] ] # maps edge ID (0-11) to (x, y, z) cell offset and edge ID (0-2) edge_shifts = np.array([ [0, 0, 0, 0], [1, 0, 0, 1], [0, 1, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0], [1, 0, 1, 1], [0, 1, 1, 0], [0, 0, 1, 1], [0, 0, 0, 2], [1, 0, 0, 2], [1, 1, 0, 2], [0, 1, 0, 2], #[9, 9, 9, 9] ## fake # don't use ubyte here! This value gets added to cell index later; # will need the extra precision. ], dtype=np.uint16) n_table_faces = np.array([len(f)/3 for f in triTable], dtype=np.ubyte) face_shift_tables = [None] for i in range(1, 6): ## compute lookup table of index: vertexes mapping faceTableI = np.zeros((len(triTable), i*3), dtype=np.ubyte) faceTableInds = np.argwhere(n_table_faces == i) faceTableI[faceTableInds[:, 0]] = np.array([triTable[j] for j in faceTableInds]) faceTableI = faceTableI.reshape((len(triTable), i, 3)) face_shift_tables.append(edge_shifts[faceTableI]) _data_cache = (face_shift_tables, edge_shifts, edge_table, n_table_faces) return _data_cache ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/geometry/__init__.py��������������������������������������������������������������0000664�0001750�0001750�00000001600�12527672621�021510� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ This module implements classes and methods for handling geometric data. """ from __future__ import division __all__ = ['MeshData', 'PolygonData', 'Rect', 'Triangulation', 'triangulate', 'create_arrow', 'create_cone', 'create_cube', 'create_cylinder', 'create_sphere', 'resize'] from .polygon import PolygonData # noqa from .meshdata import MeshData # noqa from .rect import Rect # noqa from .triangulation import Triangulation, triangulate # noqa from .torusknot import TorusKnot # noqa from .calculations import (_calculate_normals, _fast_cross_3d, # noqa resize) # noqa from .generation import create_arrow, create_cone, create_cube, \ create_cylinder, create_sphere # noqa ��������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/geometry/calculations.py����������������������������������������������������������0000664�0001750�0001750�00000010175�12527672621�022441� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """Miscellaneous functions """ import numpy as np from ..ext.six import string_types ############################################################################### # These fast normal calculation routines are adapted from mne-python def _fast_cross_3d(x, y): """Compute cross product between list of 3D vectors Much faster than np.cross() when the number of cross products becomes large (>500). This is because np.cross() methods become less memory efficient at this stage. Parameters ---------- x : array Input array 1. y : array Input array 2. Returns ------- z : array Cross product of x and y. Notes ----- x and y must both be 2D row vectors. One must have length 1, or both lengths must match. """ assert x.ndim == 2 assert y.ndim == 2 assert x.shape[1] == 3 assert y.shape[1] == 3 assert (x.shape[0] == 1 or y.shape[0] == 1) or x.shape[0] == y.shape[0] if max([x.shape[0], y.shape[0]]) >= 500: return np.c_[x[:, 1] * y[:, 2] - x[:, 2] * y[:, 1], x[:, 2] * y[:, 0] - x[:, 0] * y[:, 2], x[:, 0] * y[:, 1] - x[:, 1] * y[:, 0]] else: return np.cross(x, y) def _calculate_normals(rr, tris): """Efficiently compute vertex normals for triangulated surface""" # ensure highest precision for our summation/vectorization "trick" rr = rr.astype(np.float64) # first, compute triangle normals r1 = rr[tris[:, 0], :] r2 = rr[tris[:, 1], :] r3 = rr[tris[:, 2], :] tri_nn = _fast_cross_3d((r2 - r1), (r3 - r1)) # Triangle normals and areas size = np.sqrt(np.sum(tri_nn * tri_nn, axis=1)) size[size == 0] = 1.0 # prevent ugly divide-by-zero tri_nn /= size[:, np.newaxis] npts = len(rr) # the following code replaces this, but is faster (vectorized): # # for p, verts in enumerate(tris): # nn[verts, :] += tri_nn[p, :] # nn = np.zeros((npts, 3)) for verts in tris.T: # note this only loops 3x (number of verts per tri) for idx in range(3): # x, y, z nn[:, idx] += np.bincount(verts, tri_nn[:, idx], minlength=npts) size = np.sqrt(np.sum(nn * nn, axis=1)) size[size == 0] = 1.0 # prevent ugly divide-by-zero nn /= size[:, np.newaxis] return nn def resize(image, shape, kind='linear'): """Resize an image Parameters ---------- image : ndarray Array of shape (N, M, ...). shape : tuple 2-element shape. kind : str Interpolation, either "linear" or "nearest". Returns ------- scaled_image : ndarray New image, will have dtype np.float64. """ image = np.array(image, float) shape = np.array(shape, int) if shape.ndim != 1 or shape.size != 2: raise ValueError('shape must have two elements') if image.ndim < 2: raise ValueError('image must have two dimensions') if not isinstance(kind, string_types) or kind not in ('nearest', 'linear'): raise ValueError('mode must be "nearest" or "linear"') r = np.linspace(0, image.shape[0] - 1, shape[0]) c = np.linspace(0, image.shape[1] - 1, shape[1]) if kind == 'linear': r_0 = np.floor(r).astype(int) c_0 = np.floor(c).astype(int) r_1 = r_0 + 1 c_1 = c_0 + 1 top = (r_1 - r)[:, np.newaxis] bot = (r - r_0)[:, np.newaxis] lef = (c - c_0)[np.newaxis, :] rig = (c_1 - c)[np.newaxis, :] c_1 = np.minimum(c_1, image.shape[1] - 1) r_1 = np.minimum(r_1, image.shape[0] - 1) for arr in (top, bot, lef, rig): arr.shape = arr.shape + (1,) * (image.ndim - 2) out = top * rig * image[r_0][:, c_0, ...] out += bot * rig * image[r_1][:, c_0, ...] out += top * lef * image[r_0][:, c_1, ...] out += bot * lef * image[r_1][:, c_1, ...] else: # kind == 'nearest' r = np.round(r).astype(int) c = np.round(c).astype(int) out = image[r][:, c, ...] return out ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/geometry/meshdata.py��������������������������������������������������������������0000664�0001750�0001750�00000054332�12527672621�021551� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. import numpy as np from ..ext.six.moves import xrange def _fix_colors(colors): colors = np.asarray(colors) if colors.ndim not in (2, 3): raise ValueError('colors must have 2 or 3 dimensions') if colors.shape[-1] not in (3, 4): raise ValueError('colors must have 3 or 4 elements') if colors.shape[-1] == 3: pad = np.ones((len(colors), 1), colors.dtype) if colors.ndim == 3: pad = pad[:, :, np.newaxis] colors = np.concatenate((colors, pad), axis=-1) return colors class MeshData(object): """ Class for storing and operating on 3D mesh data. Parameters ---------- vertices : ndarray, shape (Nv, 3) Vertex coordinates. If faces is not specified, then this will instead be interpreted as (Nf, 3, 3) array of coordinates. faces : ndarray, shape (Nf, 3) Indices into the vertex array. edges : None [not available yet] vertex_colors : ndarray, shape (Nv, 4) Vertex colors. If faces is not specified, this will be interpreted as (Nf, 3, 4) array of colors. face_colors : ndarray, shape (Nf, 4) Face colors. Notes ----- All arguments are optional. The object may contain: - list of vertex locations - list of edges - list of triangles - colors per vertex, edge, or tri - normals per vertex or tri This class handles conversion between the standard [list of vertices, list of faces] format (suitable for use with glDrawElements) and 'indexed' [list of vertices] format (suitable for use with glDrawArrays). It will automatically compute face normal vectors as well as averaged vertex normal vectors. The class attempts to be as efficient as possible in caching conversion results and avoiding unnecessary conversions. """ def __init__(self, vertices=None, faces=None, edges=None, vertex_colors=None, face_colors=None): self._vertices = None # (Nv,3) array of vertex coordinates self._vertices_indexed_by_faces = None # (Nf, 3, 3) vertex coordinates self._vertices_indexed_by_edges = None # (Ne, 2, 3) vertex coordinates # mappings between vertices, faces, and edges self._faces = None # Nx3 indices into self._vertices, 3 verts/face self._edges = None # Nx2 indices into self._vertices, 2 verts/edge self._edges_indexed_by_faces = None # (Ne, 3, 2) indices into # self._vertices, 3 edge / face and 2 verts/edge # inverse mappings self._vertex_faces = None # maps vertex ID to a list of face IDs self._vertex_edges = None # maps vertex ID to a list of edge IDs # Per-vertex data self._vertex_normals = None # (Nv, 3) normals self._vertex_normals_indexed_by_faces = None # (Nf, 3, 3) normals self._vertex_colors = None # (Nv, 3) colors self._vertex_colors_indexed_by_faces = None # (Nf, 3, 4) colors self._vertex_colors_indexed_by_edges = None # (Nf, 2, 4) colors # Per-face data self._face_normals = None # (Nf, 3) face normals self._face_normals_indexed_by_faces = None # (Nf, 3, 3) face normals self._face_colors = None # (Nf, 4) face colors self._face_colors_indexed_by_faces = None # (Nf, 3, 4) face colors self._face_colors_indexed_by_edges = None # (Ne, 2, 4) face colors # Per-edge data self._edge_colors = None # (Ne, 4) edge colors self._edge_colors_indexed_by_edges = None # (Ne, 2, 4) edge colors # default color to use if no face/edge/vertex colors are given # self._meshColor = (1, 1, 1, 0.1) if vertices is not None: if faces is None: self.set_vertices(vertices, indexed='faces') if vertex_colors is not None: self.set_vertex_colors(vertex_colors, indexed='faces') if face_colors is not None: self.set_face_colors(face_colors, indexed='faces') else: self.set_vertices(vertices) self.set_faces(faces) if vertex_colors is not None: self.set_vertex_colors(vertex_colors) if face_colors is not None: self.set_face_colors(face_colors) def get_faces(self): """Array (Nf, 3) of vertex indices, three per triangular face. If faces have not been computed for this mesh, returns None. """ return self._faces def get_edges(self, indexed=None): """Edges of the mesh Parameters ---------- indexed : str | None If indexed is None, return (Nf, 3) array of vertex indices, two per edge in the mesh. If indexed is 'faces', then return (Nf, 3, 2) array of vertex indices with 3 edges per face, and two vertices per edge. Returns ------- edges : ndarray The edges. """ if indexed is None: if self._edges is None: self._compute_edges(indexed=None) return self._edges elif indexed == 'faces': if self._edges_indexed_by_faces is None: self._compute_edges(indexed='faces') return self._edges_indexed_by_faces else: raise Exception("Invalid indexing mode. Accepts: None, 'faces'") def set_faces(self, faces): """Set the faces Parameters ---------- faces : ndarray (Nf, 3) array of faces. Each row in the array contains three indices into the vertex array, specifying the three corners of a triangular face. """ self._faces = faces self._edges = None self._edges_indexed_by_faces = None self._vertex_faces = None self._vertices_indexed_by_faces = None self.reset_normals() self._vertex_colors_indexed_by_faces = None self._face_colors_indexed_by_faces = None def get_vertices(self, indexed=None): """Get the vertices Parameters ---------- indexed : str | None If Note, return an array (N,3) of the positions of vertices in the mesh. By default, each unique vertex appears only once. If indexed is 'faces', then the array will instead contain three vertices per face in the mesh (and a single vertex may appear more than once in the array). Returns ------- vertices : ndarray The vertices. """ if indexed is None: if (self._vertices is None and self._vertices_indexed_by_faces is not None): self._compute_unindexed_vertices() return self._vertices elif indexed == 'faces': if (self._vertices_indexed_by_faces is None and self._vertices is not None): self._vertices_indexed_by_faces = \ self._vertices[self.get_faces()] return self._vertices_indexed_by_faces else: raise Exception("Invalid indexing mode. Accepts: None, 'faces'") def get_bounds(self): """Get the mesh bounds Returns ------- bounds : list A list of tuples of mesh bounds. """ if self._vertices_indexed_by_faces is not None: v = self._vertices_indexed_by_faces elif self._vertices is not None: v = self._vertices else: return None bounds = [(v[:, ax].min(), v[:, ax].max()) for ax in range(v.shape[1])] return bounds def set_vertices(self, verts=None, indexed=None, reset_normals=True): """Set the mesh vertices Parameters ---------- verts : ndarray | None The array (Nv, 3) of vertex coordinates. indexed : str | None If indexed=='faces', then the data must have shape (Nf, 3, 3) and is assumed to be already indexed as a list of faces. This will cause any pre-existing normal vectors to be cleared unless reset_normals=False. reset_normals : bool If True, reset the normals. """ if indexed is None: if verts is not None: self._vertices = verts self._vertices_indexed_by_faces = None elif indexed == 'faces': self._vertices = None if verts is not None: self._vertices_indexed_by_faces = verts else: raise Exception("Invalid indexing mode. Accepts: None, 'faces'") if reset_normals: self.reset_normals() def reset_normals(self): self._vertex_normals = None self._vertex_normals_indexed_by_faces = None self._face_normals = None self._face_normals_indexed_by_faces = None def has_face_indexed_data(self): """Return True if this object already has vertex positions indexed by face""" return self._vertices_indexed_by_faces is not None def has_edge_indexed_data(self): return self._vertices_indexed_by_edges is not None def has_vertex_color(self): """Return True if this data set has vertex color information""" for v in (self._vertex_colors, self._vertex_colors_indexed_by_faces, self._vertex_colors_indexed_by_edges): if v is not None: return True return False def has_face_color(self): """Return True if this data set has face color information""" for v in (self._face_colors, self._face_colors_indexed_by_faces, self._face_colors_indexed_by_edges): if v is not None: return True return False def get_face_normals(self, indexed=None): """Get face normals Parameters ---------- indexed : str | None If None, return an array (Nf, 3) of normal vectors for each face. If 'faces', then instead return an indexed array (Nf, 3, 3) (this is just the same array with each vector copied three times). Returns ------- normals : ndarray The normals. """ if self._face_normals is None: v = self.get_vertices(indexed='faces') self._face_normals = np.cross(v[:, 1] - v[:, 0], v[:, 2] - v[:, 0]) if indexed is None: return self._face_normals elif indexed == 'faces': if self._face_normals_indexed_by_faces is None: norms = np.empty((self._face_normals.shape[0], 3, 3), dtype=np.float32) norms[:] = self._face_normals[:, np.newaxis, :] self._face_normals_indexed_by_faces = norms return self._face_normals_indexed_by_faces else: raise Exception("Invalid indexing mode. Accepts: None, 'faces'") def get_vertex_normals(self, indexed=None): """Get vertex normals Parameters ---------- indexed : str | None If None, return an (N, 3) array of normal vectors with one entry per unique vertex in the mesh. If indexed is 'faces', then the array will contain three normal vectors per face (and some vertices may be repeated). Returns ------- normals : ndarray The normals. """ if self._vertex_normals is None: faceNorms = self.get_face_normals() vertFaces = self.get_vertex_faces() self._vertex_normals = np.empty(self._vertices.shape, dtype=np.float32) for vindex in xrange(self._vertices.shape[0]): faces = vertFaces[vindex] if len(faces) == 0: self._vertex_normals[vindex] = (0, 0, 0) continue norms = faceNorms[faces] # get all face normals norm = norms.sum(axis=0) # sum normals norm /= (norm**2).sum()**0.5 # and re-normalize self._vertex_normals[vindex] = norm if indexed is None: return self._vertex_normals elif indexed == 'faces': return self._vertex_normals[self.get_faces()] else: raise Exception("Invalid indexing mode. Accepts: None, 'faces'") def get_vertex_colors(self, indexed=None): """Get vertex colors Parameters ---------- indexed : str | None If None, return an array (Nv, 4) of vertex colors. If indexed=='faces', then instead return an indexed array (Nf, 3, 4). Returns ------- colors : ndarray The vertex colors. """ if indexed is None: return self._vertex_colors elif indexed == 'faces': if self._vertex_colors_indexed_by_faces is None: self._vertex_colors_indexed_by_faces = \ self._vertex_colors[self.get_faces()] return self._vertex_colors_indexed_by_faces else: raise Exception("Invalid indexing mode. Accepts: None, 'faces'") def set_vertex_colors(self, colors, indexed=None): """Set the vertex color array Parameters ---------- colors : array Array of colors. Must have shape (Nv, 4) (indexing by vertex) or shape (Nf, 3, 4) (vertices indexed by face). indexed : str | None Should be 'faces' if colors are indexed by faces. """ colors = _fix_colors(np.asarray(colors)) if indexed is None: if colors.ndim != 2: raise ValueError('colors must be 2D if indexed is None') if colors.shape[0] != self.n_vertices: raise ValueError('incorrect number of colors %s, expected %s' % (colors.shape[0], self.n_vertices)) self._vertex_colors = colors self._vertex_colors_indexed_by_faces = None elif indexed == 'faces': if colors.ndim != 3: raise ValueError('colors must be 3D if indexed is "faces"') if colors.shape[0] != self.n_faces: raise ValueError('incorrect number of faces') self._vertex_colors = None self._vertex_colors_indexed_by_faces = colors else: raise ValueError('indexed must be None or "faces"') def get_face_colors(self, indexed=None): """Get the face colors Parameters ---------- indexed : str | None If indexed is None, return (Nf, 4) array of face colors. If indexed=='faces', then instead return an indexed array (Nf, 3, 4) (note this is just the same array with each color repeated three times). Returns ------- colors : ndarray The colors. """ if indexed is None: return self._face_colors elif indexed == 'faces': if (self._face_colors_indexed_by_faces is None and self._face_colors is not None): Nf = self._face_colors.shape[0] self._face_colors_indexed_by_faces = \ np.empty((Nf, 3, 4), dtype=self._face_colors.dtype) self._face_colors_indexed_by_faces[:] = \ self._face_colors.reshape(Nf, 1, 4) return self._face_colors_indexed_by_faces else: raise Exception("Invalid indexing mode. Accepts: None, 'faces'") def set_face_colors(self, colors, indexed=None): """Set the face color array Parameters ---------- colors : array Array of colors. Must have shape (Nf, 4) (indexed by face), or shape (Nf, 3, 4) (face colors indexed by faces). indexed : str | None Should be 'faces' if colors are indexed by faces. """ colors = _fix_colors(colors) if colors.shape[0] != self.n_faces: raise ValueError('incorrect number of colors %s, expected %s' % (colors.shape[0], self.n_faces)) if indexed is None: if colors.ndim != 2: raise ValueError('colors must be 2D if indexed is None') self._face_colors = colors self._face_colors_indexed_by_faces = None elif indexed == 'faces': if colors.ndim != 3: raise ValueError('colors must be 3D if indexed is "faces"') self._face_colors = None self._face_colors_indexed_by_faces = colors else: raise ValueError('indexed must be None or "faces"') @property def n_faces(self): """The number of faces in the mesh""" if self._faces is not None: return self._faces.shape[0] elif self._vertices_indexed_by_faces is not None: return self._vertices_indexed_by_faces.shape[0] @property def n_vertices(self): """The number of vertices in the mesh""" if self._vertices is None: self._compute_unindexed_vertices() return len(self._vertices) def get_edge_colors(self): return self._edge_colors def _compute_unindexed_vertices(self): # Given (Nv, 3, 3) array of vertices-indexed-by-face, convert # backward to unindexed vertices # This is done by collapsing into a list of 'unique' vertices # (difference < 1e-14) # I think generally this should be discouraged.. faces = self._vertices_indexed_by_faces verts = {} # used to remember the index of each vertex position self._faces = np.empty(faces.shape[:2], dtype=np.uint32) self._vertices = [] self._vertex_faces = [] self._face_normals = None self._vertex_normals = None for i in xrange(faces.shape[0]): face = faces[i] for j in range(face.shape[0]): pt = face[j] # quantize to ensure nearly-identical points will be merged pt2 = tuple([round(x*1e14) for x in pt]) index = verts.get(pt2, None) if index is None: self._vertices.append(pt) self._vertex_faces.append([]) index = len(self._vertices)-1 verts[pt2] = index # track which vertices belong to which faces self._vertex_faces[index].append(i) self._faces[i, j] = index self._vertices = np.array(self._vertices, dtype=np.float32) def get_vertex_faces(self): """ List mapping each vertex index to a list of face indices that use it. """ if self._vertex_faces is None: self._vertex_faces = [[] for i in xrange(len(self.get_vertices()))] for i in xrange(self._faces.shape[0]): face = self._faces[i] for ind in face: self._vertex_faces[ind].append(i) return self._vertex_faces def _compute_edges(self, indexed=None): if indexed is None: if self._faces is not None: # generate self._edges from self._faces nf = len(self._faces) edges = np.empty(nf*3, dtype=[('i', np.uint32, 2)]) edges['i'][0:nf] = self._faces[:, :2] edges['i'][nf:2*nf] = self._faces[:, 1:3] edges['i'][-nf:, 0] = self._faces[:, 2] edges['i'][-nf:, 1] = self._faces[:, 0] # sort per-edge mask = edges['i'][:, 0] > edges['i'][:, 1] edges['i'][mask] = edges['i'][mask][:, ::-1] # remove duplicate entries self._edges = np.unique(edges)['i'] else: raise Exception("MeshData cannot generate edges--no faces in " "this data.") elif indexed == 'faces': if self._vertices_indexed_by_faces is not None: verts = self._vertices_indexed_by_faces edges = np.empty((verts.shape[0], 3, 2), dtype=np.uint32) nf = verts.shape[0] edges[:, 0, 0] = np.arange(nf) * 3 edges[:, 0, 1] = edges[:, 0, 0] + 1 edges[:, 1, 0] = edges[:, 0, 1] edges[:, 1, 1] = edges[:, 1, 0] + 1 edges[:, 2, 0] = edges[:, 1, 1] edges[:, 2, 1] = edges[:, 0, 0] self._edges_indexed_by_faces = edges else: raise Exception("MeshData cannot generate edges--no faces in " "this data.") else: raise Exception("Invalid indexing mode. Accepts: None, 'faces'") def save(self): """Serialize this mesh to a string appropriate for disk storage Returns ------- state : dict The state. """ import pickle if self._faces is not None: names = ['_vertices', '_faces'] else: names = ['_vertices_indexed_by_faces'] if self._vertex_colors is not None: names.append('_vertex_colors') elif self._vertex_colors_indexed_by_faces is not None: names.append('_vertex_colors_indexed_by_faces') if self._face_colors is not None: names.append('_face_colors') elif self._face_colors_indexed_by_faces is not None: names.append('_face_colors_indexed_by_faces') state = dict([(n, getattr(self, n)) for n in names]) return pickle.dumps(state) def restore(self, state): """Restore the state of a mesh previously saved using save() Parameters ---------- state : dict The previous state. """ import pickle state = pickle.loads(state) for k in state: if isinstance(state[k], list): state[k] = np.array(state[k]) setattr(self, k, state[k]) ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/geometry/triangulation.py���������������������������������������������������������0000664�0001750�0001750�00000115243�12527672621�022642� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf8 -*- from __future__ import division, print_function import sys from itertools import permutations import numpy as np from ..ext.ordereddict import OrderedDict try: # Try to use the C++ triangle library, faster than the # pure Python version. # The latest stable release only works with Python 2. The GitHub version # works on Python 3 though, but the release has yet to be done. import triangle assert sys.version_info.major == 2 _TRIANGLE_AVAILABLE = True except (ImportError, AssertionError): _TRIANGLE_AVAILABLE = False class Triangulation(object): """Constrained delaunay triangulation Implementation based on [1]_. Parameters ---------- pts : array Nx2 array of points. edges : array Nx2 array of edges (dtype=int). Notes ----- * Delaunay legalization is not yet implemented. This produces a proper triangulation, but adding legalisation would produce fewer thin triangles. * The pts and edges arrays may be modified. References ---------- .. [1] Domiter, V. and Žalik, B. Sweepâ€line algorithm for constrained Delaunay triangulation """ def __init__(self, pts, edges): self.pts = pts[:, :2].astype(np.float32) self.edges = edges if self.pts.ndim != 2 or self.pts.shape[1] != 2: raise TypeError('pts argument must be ndarray of shape (N, 2).') if self.edges.ndim != 2 or self.edges.shape[1] != 2: raise TypeError('edges argument must be ndarray of shape (N, 2).') # described in initialize() self._front = None self.tris = OrderedDict() self._edges_lookup = {} def _normalize(self): # Clean up data (not discussed in original publication) # (i) Split intersecting edges. Every edge that intersects another # edge or point is split. This extends self.pts and self.edges. self._split_intersecting_edges() # (ii) Merge identical points. If any two points are found to be equal, # the second is removed and the edge table is updated accordingly. self._merge_duplicate_points() # (iii) Remove duplicate edges # TODO def _initialize(self): self._normalize() ## Initialization (sec. 3.3) # sort points by y, then x flat_shape = self.pts.shape[0] * self.pts.shape[1] pts = self.pts.reshape(flat_shape).view([('x', np.float32), ('y', np.float32)]) order = np.argsort(pts, order=('y', 'x')) pts = pts[order] # update edges to match new point order invorder = np.argsort(order) self.edges = invorder[self.edges] self.pts = pts.view(np.float32).reshape(len(pts), 2) # make artificial points P-1 and P-2 xmax = self.pts[:, 0].max() xmin = self.pts[:, 0].min() ymax = self.pts[:, 1].max() ymin = self.pts[:, 1].min() xa = (xmax-xmin) * 0.3 ya = (ymax-ymin) * 0.3 p1 = (xmin - xa, ymin - ya) p2 = (xmax + xa, ymin - ya) # prepend artificial points to point list newpts = np.empty((self.pts.shape[0]+2, 2), dtype=float) newpts[0] = p1 newpts[1] = p2 newpts[2:] = self.pts self.pts = newpts self.edges += 2 # find topmost point in each edge self._tops = self.edges.max(axis=1) self._bottoms = self.edges.min(axis=1) # inintialize sweep front # values in this list are indexes into self.pts self._front = [0, 2, 1] # empty triangle list. # This will contain [(a, b, c), ...] where a,b,c are indexes into # self.pts self.tris = OrderedDict() # For each triangle, maps (a, b): c # This is used to look up the thrid point in a triangle, given any # edge. Since each edge has two triangles, they are independently # stored as (a, b): c and (b, a): d self._edges_lookup = {} def triangulate(self): """Do the triangulation """ self._initialize() pts = self.pts front = self._front ## Begin sweep (sec. 3.4) for i in range(3, pts.shape[0]): pi = pts[i] #debug("========== New point %d: %s ==========" % (i, pi)) # First, triangulate from front to new point # This applies to both "point events" (3.4.1) # and "edge events" (3.4.2). # get index along front that intersects pts[i] l = 0 while pts[front[l+1], 0] <= pi[0]: l += 1 pl = pts[front[l]] # "(i) middle case" if pi[0] > pl[0]: #debug(" mid case") # Add a single triangle connecting pi,pl,pr self._add_tri(front[l], front[l+1], i) front.insert(l+1, i) # "(ii) left case" else: #debug(" left case") # Add triangles connecting pi,pl,ps and pi,pl,pr self._add_tri(front[l], front[l+1], i) self._add_tri(front[l-1], front[l], i) front[l] = i #debug(front) # Continue adding triangles to smooth out front # (heuristics shown in figs. 9, 10) #debug("Smoothing front...") for direction in -1, 1: while True: # Find point connected to pi ind0 = front.index(i) ind1 = ind0 + direction ind2 = ind1 + direction if ind2 < 0 or ind2 >= len(front): break # measure angle made with front p1 = pts[front[ind1]] p2 = pts[front[ind2]] err = np.geterr() np.seterr(invalid='ignore') try: angle = np.arccos(self._cosine(pi, p1, p2)) finally: np.seterr(**err) # if angle is < pi/2, make new triangle #debug("Smooth angle:", pi, p1, p2, angle) if angle > np.pi/2. or np.isnan(angle): break assert (i != front[ind1] and front[ind1] != front[ind2] and front[ind2] != i) self._add_tri(i, front[ind1], front[ind2], source='smooth1') front.pop(ind1) #debug("Finished smoothing front.") # "edge event" (sec. 3.4.2) # remove any triangles cut by completed edges and re-fill # the holes. if i in self._tops: for j in self._bottoms[self._tops == i]: # Make sure edge (j, i) is present in mesh # because edge event may have created a new front list self._edge_event(i, j) front = self._front self._finalize() self.tris = np.array(list(self.tris.keys()), dtype=int) #debug("Finished with %d tris:" % self.tris.shape[0]) #debug(str(self.tris)) def _finalize(self): ## Finalize (sec. 3.5) # (i) Add bordering triangles to fill hull #debug("== Fill hull") front = list(OrderedDict.fromkeys(self._front)) l = len(front) - 2 k = 1 while k < l-1: # if edges lie in counterclockwise direction, then signed area # is positive if self._iscounterclockwise(front[k], front[k+1], front[k+2]): self._add_tri(front[k], front[k+1], front[k+2], legal=False, source='fill_hull') front.pop(k+1) l -= 1 continue k += 1 # (ii) Remove all triangles not inside the hull # (not described in article) #debug("== Remove triangles outside hull") tris = [] # triangles to check tri_state = {} # 0 for outside, 1 for inside # find a starting triangle for t in self.tris: if 0 in t or 1 in t: tri_state[t] = 0 tris.append(t) break while tris: #debug("iterate:", tris) next_tris = [] for t in tris: v = tri_state[t] for i in (0, 1, 2): edge = (t[i], t[(i + 1) % 3]) pt = t[(i + 2) % 3] t2 = self._adjacent_tri(edge, pt) if t2 is None: continue t2a = t2[1:3] + t2[0:1] t2b = t2[2:3] + t2[0:2] if t2 in tri_state or t2a in tri_state or t2b in tri_state: continue if self._is_constraining_edge(edge): tri_state[t2] = 1 - v else: tri_state[t2] = v next_tris.append(t2) tris = next_tris for t, v in tri_state.items(): if v == 0: self._remove_tri(*t) def _edge_event(self, i, j): """ Force edge (i, j) to be present in mesh. This works by removing intersected triangles and filling holes up to the cutting edge. """ front_index = self._front.index(i) #debug(" == edge event ==") front = self._front # First just see whether this edge is already present # (this is not in the published algorithm) if (i, j) in self._edges_lookup or (j, i) in self._edges_lookup: #debug(" already added.") return #debug(" Edge (%d,%d) not added yet. Do edge event. (%s - %s)" % # (i, j, pts[i], pts[j])) # traverse in two different modes: # 1. If cutting edge is below front, traverse through triangles. These # must be removed and the resulting hole re-filled. (fig. 12) # 2. If cutting edge is above the front, then follow the front until # crossing under again. (fig. 13) # We must be able to switch back and forth between these # modes (fig. 14) # Collect points that draw the open polygons on either side of the # cutting edge. Note that our use of 'upper' and 'lower' is not strict; # in some cases the two may be swapped. upper_polygon = [i] lower_polygon = [i] # Keep track of which section of the front must be replaced # and with what it should be replaced front_holes = [] # contains indexes for sections of front to remove next_tri = None # next triangle to cut (already set if in mode 1) last_edge = None # or last triangle edge crossed (if in mode 1) # Which direction to traverse front front_dir = 1 if self.pts[j][0] > self.pts[i][0] else -1 # Initialize search state if self._edge_below_front((i, j), front_index): mode = 1 # follow triangles tri = self._find_cut_triangle((i, j)) last_edge = self._edge_opposite_point(tri, i) next_tri = self._adjacent_tri(last_edge, i) assert next_tri is not None self._remove_tri(*tri) # todo: does this work? can we count on last_edge to be clockwise # around point i? lower_polygon.append(last_edge[1]) upper_polygon.append(last_edge[0]) else: mode = 2 # follow front # Loop until we reach point j while True: #debug(" == edge_event loop: mode %d ==" % mode) #debug(" front_holes:", front_holes, front) #debug(" front_index:", front_index) #debug(" next_tri:", next_tri) #debug(" last_edge:", last_edge) #debug(" upper_polygon:", upper_polygon) #debug(" lower_polygon:", lower_polygon) #debug(" =====") if mode == 1: # crossing from one triangle into another if j in next_tri: #debug(" -> hit endpoint!") # reached endpoint! # update front / polygons upper_polygon.append(j) lower_polygon.append(j) #debug(" Appended to upper_polygon:", upper_polygon) #debug(" Appended to lower_polygon:", lower_polygon) self._remove_tri(*next_tri) break else: # next triangle does not contain the end point; we will # cut one of the two far edges. tri_edges = self._edges_in_tri_except(next_tri, last_edge) # select the edge that is cut last_edge = self._intersected_edge(tri_edges, (i, j)) #debug(" set last_edge to intersected edge:", last_edge) last_tri = next_tri next_tri = self._adjacent_tri(last_edge, last_tri) #debug(" set next_tri:", next_tri) self._remove_tri(*last_tri) # Crossing an edge adds one point to one of the polygons if lower_polygon[-1] == last_edge[0]: upper_polygon.append(last_edge[1]) #debug(" Appended to upper_polygon:", upper_polygon) elif lower_polygon[-1] == last_edge[1]: upper_polygon.append(last_edge[0]) #debug(" Appended to upper_polygon:", upper_polygon) elif upper_polygon[-1] == last_edge[0]: lower_polygon.append(last_edge[1]) #debug(" Appended to lower_polygon:", lower_polygon) elif upper_polygon[-1] == last_edge[1]: lower_polygon.append(last_edge[0]) #debug(" Appended to lower_polygon:", lower_polygon) else: raise RuntimeError("Something went wrong..") # If we crossed the front, go to mode 2 x = self._edge_in_front(last_edge) if x >= 0: # crossing over front #debug(" -> crossed over front, prepare for mode 2") mode = 2 next_tri = None #debug(" set next_tri: None") # where did we cross the front? # nearest to new point front_index = x + (1 if front_dir == -1 else 0) #debug(" set front_index:", front_index) # Select the correct polygon to be lower_polygon # (because mode 2 requires this). # We know that last_edge is in the front, and # front[front_index] is the point _above_ the front. # So if this point is currently the last element in # lower_polygon, then the polys must be swapped. if lower_polygon[-1] == front[front_index]: tmp = lower_polygon, upper_polygon upper_polygon, lower_polygon = tmp #debug(' Swap upper/lower polygons') else: assert upper_polygon[-1] == front[front_index] else: assert next_tri is not None else: # mode == 2 # At each iteration, we require: # * front_index is the starting index of the edge _preceding_ # the edge that will be handled in this iteration # * lower_polygon is the polygon to which points should be # added while traversing the front front_index += front_dir #debug(" Increment front_index: %d" % front_index) next_edge = (front[front_index], front[front_index+front_dir]) #debug(" Set next_edge: %s" % repr(next_edge)) assert front_index >= 0 if front[front_index] == j: # found endpoint! #debug(" -> hit endpoint!") lower_polygon.append(j) upper_polygon.append(j) #debug(" Appended to upper_polygon:", upper_polygon) #debug(" Appended to lower_polygon:", lower_polygon) break # Add point to lower_polygon. # The conditional is because there are cases where the # point was already added if we just crossed from mode 1. if lower_polygon[-1] != front[front_index]: lower_polygon.append(front[front_index]) #debug(" Appended to lower_polygon:", lower_polygon) front_holes.append(front_index) #debug(" Append to front_holes:", front_holes) if self._edges_intersect((i, j), next_edge): # crossing over front into triangle #debug(" -> crossed over front, prepare for mode 1") mode = 1 last_edge = next_edge #debug(" Set last_edge:", last_edge) # we are crossing the front, so this edge only has one # triangle. next_tri = self._tri_from_edge(last_edge) #debug(" Set next_tri:", next_tri) upper_polygon.append(front[front_index+front_dir]) #debug(" Appended to upper_polygon:", upper_polygon) #else: #debug(" -> did not cross front..") #debug("Finished edge_event:") #debug(" front_holes:", front_holes) #debug(" upper_polygon:", upper_polygon) #debug(" lower_polygon:", lower_polygon) # (iii) triangluate empty areas #debug("Filling edge_event polygons...") for polygon in [lower_polygon, upper_polygon]: dist = self._distances_from_line((i, j), polygon) #debug("Distances:", dist) while len(polygon) > 2: ind = np.argmax(dist) #debug("Next index: %d" % ind) self._add_tri(polygon[ind], polygon[ind-1], polygon[ind+1], legal=False, source='edge_event') polygon.pop(ind) dist.pop(ind) #debug("Finished filling edge_event polygons.") # update front by removing points in the holes (places where front # passes below the cut edge) front_holes.sort(reverse=True) for i in front_holes: front.pop(i) #debug("Finished updating front after edge_event.") def _find_cut_triangle(self, edge): """ Return the triangle that has edge[0] as one of its vertices and is bisected by edge. Return None if no triangle is found. """ edges = [] # opposite edge for each triangle attached to edge[0] for tri in self.tris: if edge[0] in tri: edges.append(self._edge_opposite_point(tri, edge[0])) for oedge in edges: o1 = self._orientation(edge, oedge[0]) o2 = self._orientation(edge, oedge[1]) #debug(edge, oedge, o1, o2) #debug(self.pts[np.array(edge)]) #debug(self.pts[np.array(oedge)]) if o1 != o2: return (edge[0], oedge[0], oedge[1]) return None def _edge_in_front(self, edge): """ Return the index where *edge* appears in the current front. If the edge is not in the front, return -1 """ e = (list(edge), list(edge)[::-1]) for i in range(len(self._front)-1): if self._front[i:i+2] in e: return i return -1 def _edge_opposite_point(self, tri, i): """ Given a triangle, return the edge that is opposite point i. Vertexes are returned in the same orientation as in tri. """ ind = tri.index(i) return (tri[(ind+1) % 3], tri[(ind+2) % 3]) def _adjacent_tri(self, edge, i): """ Given a triangle formed by edge and i, return the triangle that shares edge. *i* may be either a point or the entire triangle. """ if not np.isscalar(i): i = [x for x in i if x not in edge][0] try: pt1 = self._edges_lookup[edge] pt2 = self._edges_lookup[(edge[1], edge[0])] except KeyError: return None if pt1 == i: return (edge[1], edge[0], pt2) elif pt2 == i: return (edge[1], edge[0], pt1) else: raise RuntimeError("Edge %s and point %d do not form a triangle " "in this mesh." % (edge, i)) def _tri_from_edge(self, edge): """Return the only tri that contains *edge*. If two tris share this edge, raise an exception. """ edge = tuple(edge) p1 = self._edges_lookup.get(edge, None) p2 = self._edges_lookup.get(edge[::-1], None) if p1 is None: if p2 is None: raise RuntimeError("No tris connected to edge %r" % (edge,)) return edge + (p2,) elif p2 is None: return edge + (p1,) else: raise RuntimeError("Two triangles connected to edge %r" % (edge,)) def _edges_in_tri_except(self, tri, edge): """Return the edges in *tri*, excluding *edge*. """ edges = [(tri[i], tri[(i+1) % 3]) for i in range(3)] try: edges.remove(tuple(edge)) except ValueError: edges.remove(tuple(edge[::-1])) return edges def _edge_below_front(self, edge, front_index): """Return True if *edge* is below the current front. One of the points in *edge* must be _on_ the front, at *front_index*. """ f0 = self._front[front_index-1] f1 = self._front[front_index+1] return (self._orientation(edge, f0) > 0 and self._orientation(edge, f1) < 0) def _is_constraining_edge(self, edge): mask1 = self.edges == edge[0] mask2 = self.edges == edge[1] return (np.any(mask1[:, 0] & mask2[:, 1]) or np.any(mask2[:, 0] & mask1[:, 1])) def _intersected_edge(self, edges, cut_edge): """ Given a list of *edges*, return the first that is intersected by *cut_edge*. """ for edge in edges: if self._edges_intersect(edge, cut_edge): return edge def _find_edge_intersections(self): """ Return a dictionary containing, for each edge in self.edges, a list of the positions at which the edge should be split. """ edges = self.pts[self.edges] cuts = {} # { edge: [(intercept, point), ...], ... } for i in range(edges.shape[0]-1): # intersection of edge i onto all others int1 = self._intersect_edge_arrays(edges[i:i+1], edges[i+1:]) # intersection of all edges onto edge i int2 = self._intersect_edge_arrays(edges[i+1:], edges[i:i+1]) # select for pairs that intersect err = np.geterr() np.seterr(divide='ignore', invalid='ignore') try: mask1 = (int1 >= 0) & (int1 <= 1) mask2 = (int2 >= 0) & (int2 <= 1) mask3 = mask1 & mask2 # all intersections finally: np.seterr(**err) # compute points of intersection inds = np.argwhere(mask3)[:, 0] if len(inds) == 0: continue h = int2[inds][:, np.newaxis] pts = (edges[i, 0][np.newaxis, :] * (1.0 - h) + edges[i, 1][np.newaxis, :] * h) # record for all edges the location of cut points edge_cuts = cuts.setdefault(i, []) for j, ind in enumerate(inds): if 0 < int2[ind] < 1: edge_cuts.append((int2[ind], pts[j])) if 0 < int1[ind] < 1: other_cuts = cuts.setdefault(ind+i+1, []) other_cuts.append((int1[ind], pts[j])) # sort all cut lists by intercept, remove duplicates for k, v in cuts.items(): v.sort(key=lambda x: x[0]) for i in range(len(v)-2, -1, -1): if v[i][0] == v[i+1][0]: v.pop(i+1) return cuts def _split_intersecting_edges(self): # we can do all intersections at once, but this has excessive memory # overhead. #int1 = self._intersection_matrix(edges) #int2 = int1.T # measure intersection point between all pairs of edges all_cuts = self._find_edge_intersections() # cut edges at each intersection add_pts = [] add_edges = [] for edge, cuts in all_cuts.items(): if len(cuts) == 0: continue #debug("Edge intersections:", edge, self.edges[edge], # self.pts[self.edges[edge]], cuts) # add new points pt_offset = self.pts.shape[0] + len(add_pts) new_pts = [x[1] for x in cuts] add_pts.extend(new_pts) #debug("Add new points:", new_pts) # list of point indexes for all new edges pt_indexes = list(range(pt_offset, pt_offset + len(cuts))) pt_indexes.append(self.edges[edge, 1]) # modify original edge self.edges[edge, 1] = pt_indexes[0] # add new edges new_edges = [[pt_indexes[i-1], pt_indexes[i]] for i in range(1, len(pt_indexes))] add_edges.extend(new_edges) #debug("Adding %d points and %d edges to remove intersections." % # (len(add_pts), len(add_edges))) if add_pts: add_pts = np.array(add_pts, dtype=self.pts.dtype) self.pts = np.append(self.pts, add_pts, axis=0) if add_edges: add_edges = np.array(add_edges, dtype=self.edges.dtype) self.edges = np.append(self.edges, add_edges, axis=0) def _merge_duplicate_points(self): # generate a list of all pairs (i,j) of identical points dups = [] for i in range(self.pts.shape[0]-1): test_pt = self.pts[i:i+1] comp_pts = self.pts[i+1:] eq = test_pt == comp_pts eq = eq[:, 0] & eq[:, 1] for j in np.argwhere(eq)[:, 0]: dups.append((i, i+1+j)) dups_arr = np.array(dups) # remove duplicate points pt_mask = np.ones(self.pts.shape[0], dtype=bool) for i, inds in enumerate(dups_arr): # remove j from points # (note we pull the index from the original dups instead of # dups_arr because the indexes in pt_mask do not change) pt_mask[dups[i][1]] = False i, j = inds # rewrite edges to use i instead of j self.edges[self.edges == j] = i #assert not np.any(self.edges[:,0] == self.edges[:,1]) # decrement all point indexes > j self.edges[self.edges > j] -= 1 dups_arr[dups_arr > j] -= 1 #assert not np.any(self.edges[:,0] == self.edges[:,1]) self.pts = self.pts[pt_mask] # remove zero-length edges mask = self.edges[:, 0] != self.edges[:, 1] self.edges = self.edges[mask] def _distance(self, A, B): # Distance between points A and B n = len(A) assert len(B) == n return np.linalg.norm(np.array(list(A)) - np.array(list(B))) def _distances_from_line(self, edge, points): # Distance of a set of points from a given line #debug("distance from %r to %r" % (points, edge)) e1 = self.pts[edge[0]] e2 = self.pts[edge[1]] distances = [] for i in points: p = self.pts[i] proj = self._projection(e1, p, e2) distances.append(((p - proj)**2).sum()**0.5) assert distances[0] == 0 and distances[-1] == 0 return distances def _projection(self, a, b, c): """Return projection of (a,b) onto (a,c) Arguments are point locations, not indexes. """ ab = b - a ac = c - a return a + ((ab*ac).sum() / (ac*ac).sum()) * ac def _cosine(self, A, B, C): # Cosine of angle ABC a = ((C - B)**2).sum() b = ((C - A)**2).sum() c = ((B - A)**2).sum() d = (a + c - b) / ((4 * a * c)**0.5) return d #def _barycentric(self, A, B, C, p, q, r): ## Cartesian coordinates of the point whose barycentric coordinates ## with respect to the triangle ABC are [p,q,r] #n = len(A) #assert len(B) == len(C) == n #s = p+q+r #p, q, r = p/s, q/s, r/s #return tuple([p*A[i]+q*B[i]+r*C[i] for i in range(n)]) #def _trilinear(self, A, B, C, alpha, beta, gamma): ## Cartesian coordinates of the point whose trilinear coordinates ## with respect to the triangle ABC are [alpha,beta,gamma] #a = distance(B, C) #b = distance(A, C) #c = distance(A, B) #return barycentric(A, B, C, a*alpha, b*beta, c*gamma) #def _circuminfo(self, A, B, C): ## Cartesian coordinates of the circumcenter of triangle ABC #cosA = cosine(C, A, B) #cosB = cosine(A, B, C) #cosC = cosine(B, C, A) #cc = trilinear(A, B, C, cosA, cosB, cosC) ## returns circumcenter and circumradius #return cc, distance(cc, A) def _iscounterclockwise(self, a, b, c): # Check if the points lie in counter-clockwise order or not A = self.pts[a] B = self.pts[b] C = self.pts[c] return np.cross(B-A, C-B) > 0 def _edges_intersect(self, edge1, edge2): """ Return 1 if edges intersect completely (endpoints excluded) """ h12 = self._intersect_edge_arrays(self.pts[np.array(edge1)], self.pts[np.array(edge2)]) h21 = self._intersect_edge_arrays(self.pts[np.array(edge2)], self.pts[np.array(edge1)]) err = np.geterr() np.seterr(divide='ignore', invalid='ignore') try: out = (0 < h12 < 1) and (0 < h21 < 1) finally: np.seterr(**err) return out def _intersection_matrix(self, lines): """ Return a 2D array of intercepts such that intercepts[i, j] is the intercept of lines[i] onto lines[j]. *lines* must be an array of point locations with shape (N, 2, 2), where the axes are (lines, points_per_line, xy_per_point). The intercept is described in intersect_edge_arrays(). """ return self._intersect_edge_arrays(lines[:, np.newaxis, ...], lines[np.newaxis, ...]) def _intersect_edge_arrays(self, lines1, lines2): """Return the intercepts of all lines defined in *lines1* as they intersect all lines in *lines2*. Arguments are of shape (..., 2, 2), where axes are: 0: number of lines 1: two points per line 2: x,y pair per point Lines are compared elementwise across the arrays (lines1[i] is compared against lines2[i]). If one of the arrays has N=1, then that line is compared against all lines in the other array. Returns an array of shape (N,) where each value indicates the intercept relative to the defined line segment. A value of 0 indicates intersection at the first endpoint, and a value of 1 indicates intersection at the second endpoint. Values between 1 and 0 are on the segment, whereas values outside 1 and 0 are off of the segment. """ # vector for each line in lines1 l1 = lines1[..., 1, :] - lines1[..., 0, :] # vector for each line in lines2 l2 = lines2[..., 1, :] - lines2[..., 0, :] # vector between first point of each line diff = lines1[..., 0, :] - lines2[..., 0, :] p = l1.copy()[..., ::-1] # vectors perpendicular to l1 p[..., 0] *= -1 f = (l2 * p).sum(axis=-1) # l2 dot p # tempting, but bad idea! #f = np.where(f==0, 1, f) err = np.geterr() np.seterr(divide='ignore', invalid='ignore') try: h = (diff * p).sum(axis=-1) / f # diff dot p / f finally: np.seterr(**err) return h def _orientation(self, edge, point): """ Returns +1 if edge[0]->point is clockwise from edge[0]->edge[1], -1 if counterclockwise, and 0 if parallel. """ v1 = self.pts[point] - self.pts[edge[0]] v2 = self.pts[edge[1]] - self.pts[edge[0]] c = np.cross(v1, v2) # positive if v1 is CW from v2 return 1 if c > 0 else (-1 if c < 0 else 0) #def _legalize(self, p): ### Legalize recursively - incomplete #return p # disabled for now #f00, f11, p = p #debug("Legalizing points = {}, {}, {}".format(f00, f11, p)) #a = pts[f00] #b = pts[f11] #c = pts[p] #cc, cr = circuminfo(a, b, c) #for point in pts: # if np.all(point == a) or np.all(point == b) or np.all(point == c): # continue # elif distance(cc, point) < cr: # #debug("Illegal point") # #debug(point) # pass #return (f00, f11, p) def _add_tri(self, a, b, c, legal=True, source=None): # source is just used for #debugging #debug("Add triangle [%s]:" % source, (a, b, c)) # sanity check assert a != b and b != c and c != a # ignore flat tris pa = self.pts[a] pb = self.pts[b] pc = self.pts[c] if np.all(pa == pb) or np.all(pb == pc) or np.all(pc == pa): #debug(" Triangle is flat; refusing to add.") return # check this tri is unique for t in permutations((a, b, c)): if t in self.tris: raise Exception("Cannot add %s; already have %s" % ((a, b, c), t)) # TODO: should add to edges_lookup after legalization?? if self._iscounterclockwise(a, b, c): #debug(" ", (a, b), (b, c), (c, a)) assert (a, b) not in self._edges_lookup assert (b, c) not in self._edges_lookup assert (c, a) not in self._edges_lookup self._edges_lookup[(a, b)] = c self._edges_lookup[(b, c)] = a self._edges_lookup[(c, a)] = b else: #debug(" ", (b, a), (c, b), (a, c)) assert (b, a) not in self._edges_lookup assert (c, b) not in self._edges_lookup assert (a, c) not in self._edges_lookup self._edges_lookup[(b, a)] = c self._edges_lookup[(c, b)] = a self._edges_lookup[(a, c)] = b #if legal: #tri = self._legalize((a, b, c)) #else: tri = (a, b, c) self.tris[tri] = None def _remove_tri(self, a, b, c): #debug("Remove triangle:", (a, b, c)) for k in permutations((a, b, c)): if k in self.tris: break del self.tris[k] (a, b, c) = k if self._edges_lookup.get((a, b), -1) == c: #debug(" ", (a,b), (b,c), (c,a)) del self._edges_lookup[(a, b)] del self._edges_lookup[(b, c)] del self._edges_lookup[(c, a)] elif self._edges_lookup.get((b, a), -1) == c: #debug(" ", (b,a), (c,b), (a,c)) del self._edges_lookup[(b, a)] del self._edges_lookup[(a, c)] del self._edges_lookup[(c, b)] else: raise RuntimeError("Lost edges_lookup for tri (%d, %d, %d)" % (a, b, c)) return k def _triangulate_python(vertices_2d, segments): segments = segments.reshape(len(segments) / 2, 2) T = Triangulation(vertices_2d, segments) T.triangulate() vertices_2d = T.pts triangles = T.tris.ravel() return vertices_2d, triangles def _triangulate_cpp(vertices_2d, segments): T = triangle.triangulate({'vertices': vertices_2d, 'segments': segments}, "p") vertices_2d = T["vertices"] triangles = T["triangles"] return vertices_2d, triangles def triangulate(vertices): """Triangulate a set of vertices Parameters ---------- vertices : array-like The vertices. Returns ------- vertices : array-like The vertices. tringles : array-like The triangles. """ n = len(vertices) vertices = np.asarray(vertices) zmean = vertices[:, 2].mean() vertices_2d = vertices[:, :2] segments = np.repeat(np.arange(n + 1), 2)[1:-1] segments[-2:] = n - 1, 0 if _TRIANGLE_AVAILABLE: vertices_2d, triangles = _triangulate_cpp(vertices_2d, segments) else: vertices_2d, triangles = _triangulate_python(vertices_2d, segments) vertices = np.empty((len(vertices_2d), 3)) vertices[:, :2] = vertices_2d vertices[:, 2] = zmean return vertices, triangles # Note: using custom #debug instead of logging because # there are MANY messages and logger might be too expensive. # After this becomes stable, we might just remove them altogether. def debug(*args): print(*args) �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/geometry/parametric.py������������������������������������������������������������0000664�0001750�0001750�00000003630�12510536123�022071� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2014, Nicolas P. Rougier # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- import numpy as np from .normals import normals def surface(func, umin=0, umax=2 * np.pi, ucount=64, urepeat=1.0, vmin=0, vmax=2 * np.pi, vcount=64, vrepeat=1.0): """ Computes the parameterization of a parametric surface func: function(u,v) Parametric function used to build the surface """ vtype = [('position', np.float32, 3), ('texcoord', np.float32, 2), ('normal', np.float32, 3)] itype = np.uint32 # umin, umax, ucount = 0, 2*np.pi, 64 # vmin, vmax, vcount = 0, 2*np.pi, 64 vcount += 1 ucount += 1 n = vcount * ucount Un = np.repeat(np.linspace(0, 1, ucount, endpoint=True), vcount) Vn = np.tile(np.linspace(0, 1, vcount, endpoint=True), ucount) U = umin + Un * (umax - umin) V = vmin + Vn * (vmax - vmin) vertices = np.zeros(n, dtype=vtype) for i, (u, v) in enumerate(zip(U, V)): vertices["position"][i] = func(u, v) vertices["texcoord"][:, 0] = Un * urepeat vertices["texcoord"][:, 1] = Vn * vrepeat indices = [] for i in range(ucount - 1): for j in range(vcount - 1): indices.append(i * (vcount) + j) indices.append(i * (vcount) + j + 1) indices.append(i * (vcount) + j + vcount + 1) indices.append(i * (vcount) + j + vcount) indices.append(i * (vcount) + j + vcount + 1) indices.append(i * (vcount) + j) indices = np.array(indices, dtype=itype) vertices["normal"] = normals(vertices["position"], indices.reshape(len(indices) / 3, 3)) return vertices, indices ��������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/geometry/tests/�������������������������������������������������������������������0000775�0001750�0001750�00000000000�12527674621�020546� 5����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/geometry/tests/test_triangulation.py����������������������������������������������0000664�0001750�0001750�00000031343�12527672621�025041� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������import numpy as np from numpy.testing import assert_array_almost_equal from vispy.testing import run_tests_if_main from vispy.geometry.triangulation import Triangulation as T def assert_array_eq(a, b): assert a.shape == b.shape assert a.dtype == b.dtype mask = np.isnan(a) assert np.all(np.isnan(b[mask])) assert np.all(a[~mask] == b[~mask]) def test_intersect_edge_arrays(): global t pts = np.array([ [0., 0.], [0., 10.], [5., 0.], [-5., 0.], [-1., 11.], [1., 9.], ]) edges = np.array([ [0, 1], [2, 3], [0, 3], [4, 5], [4, 1], [0, 1], ]) lines = pts[edges] t = T(pts, edges) # intersect array of one edge with a array of many edges intercepts = t._intersect_edge_arrays(lines[0:1], lines[1:]) expect = np.array([0.5, 0.0, 0.5, 1.0, np.nan]) assert_array_eq(intercepts, expect) # intersect every line with every line intercepts = t._intersect_edge_arrays(lines[:, np.newaxis, ...], lines[np.newaxis, ...]) for i in range(lines.shape[0]): int2 = t._intersect_edge_arrays(lines[i], lines) assert_array_eq(intercepts[i], int2) def test_edge_intersections(): global t pts = np.array([ [0, 0], [1, 0], [1, 1], [0, 1], [0, 0.5], # three edges intersect here [2, 0.5], [-1, 0.2], [2, 0.8], [-1, 1], [0, 0.5], ]) edges = np.array([ [0, 1], [1, 2], [2, 3], [3, 0], [4, 5], [6, 7], [8, 9], ]) t = T(pts, edges) # first test find_edge_intersections cuts = t._find_edge_intersections() expect = { 0: [], 1: [(0.5, [1., 0.5]), (0.6, [1., 0.6])], 2: [], 3: [(0.5, [0., 0.5]), (0.6, [0., 0.4])], 4: [(0.25, [0.5, 0.5]), (0.5, [1., 0.5])], 5: [(1./3., [0., 0.4]), (0.5, [0.5, 0.5]), (2./3., [1., 0.6])], } assert len(expect) == len(cuts) for k, v in expect.items(): assert len(v) == len(cuts[k]) for i, ecut in enumerate(v): vcut = cuts[k][i] assert len(vcut) == len(ecut) for j in range(len(vcut)): assert_array_almost_equal(np.array(ecut[j]), np.array(vcut[j])) # next test that we can split the edges correctly t._split_intersecting_edges() pts = np.array([[0., 0.], [1., 0.], [1., 1.], [0., 1.], [0., 0.5], [2., 0.5], [-1., 0.2], [2., 0.8], [-1., 1.], [0., 0.5], [1., 0.5], [1., 0.6], [0., 0.5], [0., 0.4], [0.5, 0.5], [1., 0.5], [0., 0.4], [0.5, 0.5], [1., 0.6]]) edges = np.array([[0, 1], [1, 10], [2, 3], [3, 12], [4, 14], [6, 16], [8, 9], [10, 11], [11, 2], [12, 13], [13, 0], [14, 15], [15, 5], [16, 17], [17, 18], [18, 7]]) assert_array_almost_equal(pts, t.pts) assert np.all(edges == t.edges) # Test _nearly_ parallel lines. pts = np.array([[0., 0.], [1.62434542, 0.], [1.62434542, -0.61175638], [1.09617364, -0.61175638]]) edges = np.array([[0, 1], [1, 2], [2, 3], [3, 0]]) t = T(pts, edges) for edge, cuts in t._find_edge_intersections().items(): assert len(cuts) == 0 def test_merge_duplicate_points(): global t pts = np.array([ [0, 0], [1, 1], [0.1, 0.7], [2, 3], [0, 0], [0.1, 0.7], [5, 6], ]) edges = np.array([ [0, 6], [1, 5], [2, 4], [3, 6], [4, 5], ]) t = T(pts, edges) t._merge_duplicate_points() pts = np.array([ [0, 0], [1, 1], [0.1, 0.7], [2, 3], [5, 6], ]) edges = np.array([ [0, 4], [1, 2], [2, 0], [3, 4], [0, 2], ]) assert np.allclose(t.pts, pts) assert np.all(t.edges == edges) def test_initialize(): # check points are correctly sorted # check artificial points are outside bounds of all others # check tops / bottoms pass def test_utility_methods(): global t pts = np.array([ [0, 0], [1, 0], [2, 0], [3, 0], [1.5, 2], [1.5, -2], ]) edges = np.array([ [4, 5], # edge cuts through triangle (1, 2, 4) ]) t = T(pts, edges) # skip initialization and just simulate being part-way through # triangulation for tri in [[0, 1, 4], [1, 2, 4], [2, 3, 4]]: t._add_tri(*tri) # find_cut_triangle assert t._find_cut_triangle((4, 5)) == (4, 1, 2) # orientation assert t._orientation((4, 5), 0) == 1 assert t._orientation((4, 5), 1) == 1 assert t._orientation((4, 5), 2) == -1 assert t._orientation((4, 5), 3) == -1 assert t._orientation((4, 5), 4) == 0 assert t._orientation((4, 5), 5) == 0 # distance dist = ((t.pts[0]-t.pts[1])**2).sum()**0.5 assert t._distance(t.pts[0], t.pts[1]) == dist # adjacent_tri assert t._adjacent_tri((1, 4), 0) == (4, 1, 2) assert t._adjacent_tri((0, 4), 1) is None assert t._adjacent_tri((1, 4), (1, 4, 0)) == (4, 1, 2) assert t._adjacent_tri((0, 4), (1, 4, 0)) is None try: t._adjacent_tri((1, 4), 5) except RuntimeError: pass else: raise Exception("Expected RuntimeError.") # edges_intersect assert not t._edges_intersect((0, 1), (1, 2)) assert not t._edges_intersect((0, 2), (1, 2)) assert t._edges_intersect((4, 5), (1, 2)) # is_constraining_edge assert t._is_constraining_edge((4, 5)) assert t._is_constraining_edge((5, 4)) assert not t._is_constraining_edge((3, 5)) assert not t._is_constraining_edge((3, 2)) def test_projection(): pts = np.array([[0, 0], [5, 0], [1, 2], [3, 4]]) t = T(pts, np.zeros((0, 2))) a, b, c, d = pts assert np.allclose(t._projection(a, c, b), [1, 0]) assert np.allclose(t._projection(b, c, a), [1, 0]) assert np.allclose(t._projection(a, d, b), [3, 0]) assert np.allclose(t._projection(b, d, a), [3, 0]) assert np.allclose(t._projection(a, b, c), [1, 2]) assert np.allclose(t._projection(c, b, a), [1, 2]) def test_random(): # Just test that these triangulate without exception. # TODO: later on, we can turn this same test into an image comparison # with Polygon. N = 10 np.random.seed(0) for i in range(4): pts = np.random.normal(size=(N, 2)) edges = np.zeros((N, 2), dtype=int) edges[:, 0] = np.arange(N) edges[:, 1] = np.arange(1, N+1) % N t = T(pts, edges) t.triangulate() theta = np.linspace(0, 2*np.pi, 11)[:-1] pts = np.hstack([np.cos(theta)[:, np.newaxis], np.sin(theta)[:, np.newaxis]]) pts[::2] *= 0.4 edges = np.empty((pts.shape[0], 2), dtype=np.uint) edges[:, 0] = np.arange(pts.shape[0]) edges[:, 1] = edges[:, 0] + 1 edges[-1, 1] = 0 t = T(pts, edges) t.triangulate() # much larger test # this should pass, but takes forever.. #N = 4000 #pts = np.random.normal(size=(N, 2)) #pts = np.cumsum(pts, axis=0) #edges = np.zeros((N, 2), dtype=int) #edges[:,0] = np.arange(N) #edges[:,1] = np.arange(1,N+1) % N #t = T(pts, edges) #t.triangulate() def test_orthogonal(): # make lines that are entirely vertical / horizontal np.random.seed(1) N = 100 pts = [[0, 0]] for i in range(N - 1): p = pts[-1][:] p[i % 2] += np.random.normal() pts.append(p) pts = np.array(pts) edges = np.zeros((N, 2), dtype=int) edges[:, 0] = np.arange(N) edges[:, 1] = np.arange(1, N + 1) % N t = T(pts, edges) t.triangulate() def test_edge_event(): # mode 1 pts = np.array([[0, 0], [5, -10], [10, 0], [6, -5], [5, 5], ]) inds = np.arange(pts.shape[0])[:, np.newaxis] edges = np.hstack([inds, np.roll(inds, -1)]) t = T(pts, edges) t.triangulate() t = T(pts * [-1, 1], edges) t.triangulate() # mode 2 pts = np.array([[0, 0], [10, 0], [20, 0], [5, 11], ]) inds = np.arange(pts.shape[0])[:, np.newaxis] edges = np.hstack([inds, np.roll(inds, -1)]) t = T(pts, edges) t.triangulate() t = T(pts * [-1, 1], edges) t.triangulate() # mode 1, 2 pts = np.array([[0, 0], [10, 0], [20, 0], [5, 11], [9, 10], [0, 20], ]) inds = np.arange(pts.shape[0])[:, np.newaxis] edges = np.hstack([inds, np.roll(inds, -1)]) t = T(pts, edges) t.triangulate() t = T(pts * [-1, 1], edges) t.triangulate() # mode 2, 1 pts = np.array([[0, 0], [10, 0], [20, 0], [15, 8], [15, 1], [-5, 10], ]) inds = np.arange(pts.shape[0])[:, np.newaxis] edges = np.hstack([inds, np.roll(inds, -1)]) t = T(pts, edges) t.triangulate() t = T(pts * [-1, 1], edges) t.triangulate() # mode 2, 1 with many triangles pts = np.array([[0, 10], [2, 8], [4, 6], [6, 4], [8, 2], [10, 0], [20, 5], [20, 20], [2, 13], [4, 11], [6, 9], [8, 7], [10, 5], [10, 1], [0, 15], ]) inds = np.arange(pts.shape[0])[:, np.newaxis] edges = np.hstack([inds, np.roll(inds, -1)]) t = T(pts, edges) t.triangulate() t = T(pts * [-1, 1], edges) t.triangulate() # mode 1, 2, 1, 2, 1 pts = np.array([[0, 10], [2, 9], [4, 8], [6, 7], [8, 6], [10, 5], [20, 5], [20, 20], [2, 11], [19, 19], [6, 9], [19, 18], [10, 7], [11, 5.1], [0, 11.1], ]) inds = np.arange(pts.shape[0])[:, np.newaxis] edges = np.hstack([inds, np.roll(inds, -1)]) t = T(pts, edges) t.triangulate() t = T(pts * [-1, 1], edges) t.triangulate() # mode 2, 1, 2, 1 pts = np.array([[0, 10], [2, 9], [4, 8], [6, 7], [8, 6], [10, 5], [20, 5], [20, 20], [6, 9], [19, 18], [10, 7], [11, 5.1], [0, 11.1], ]) inds = np.arange(pts.shape[0])[:, np.newaxis] edges = np.hstack([inds, np.roll(inds, -1)]) t = T(pts, edges) t.triangulate() t = T(pts * [-1, 1], edges) t.triangulate() # 1, 2 upper/lower polygon order check pts = np.array([[-5, 0], [-3, 0], [10, 0], [15, 15], [4, 9], [6, 8.8], [9, 10], ]) inds = np.arange(pts.shape[0])[:, np.newaxis] edges = np.hstack([inds, np.roll(inds, -1)]) t = T(pts, edges) t.triangulate() t = T(pts * [-1, 1], edges) t.triangulate() run_tests_if_main() ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/geometry/tests/test_generation.py�������������������������������������������������0000664�0001750�0001750�00000001777�12527672621�024324� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. import numpy as np from numpy.testing import assert_array_equal, assert_allclose from vispy.testing import run_tests_if_main from vispy.geometry import create_cube, create_cylinder, create_sphere def test_cube(): """Test cube function""" vertices, filled, outline = create_cube() assert_array_equal(np.arange(len(vertices)), np.unique(filled)) assert_array_equal(np.arange(len(vertices)), np.unique(outline)) def test_sphere(): """Test sphere function""" md = create_sphere(10, 20, radius=10) radii = np.sqrt((md.get_vertices() ** 2).sum(axis=1)) assert_allclose(radii, np.ones_like(radii) * 10) def test_cylinder(): """Test cylinder function""" md = create_cylinder(10, 20, radius=[10, 10]) radii = np.sqrt((md.get_vertices()[:, :2] ** 2).sum(axis=1)) assert_allclose(radii, np.ones_like(radii) * 10) run_tests_if_main() �vispy-0.4.0/vispy/geometry/tests/__init__.py��������������������������������������������������������0000664�0001750�0001750�00000000000�12376730723�022643� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/geometry/tests/test_calculations.py�����������������������������������������������0000664�0001750�0001750�00000002004�12527672621�024632� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. import numpy as np from numpy.testing import assert_allclose from vispy.testing import assert_raises from vispy.geometry import resize def test_resize(): """Test image resizing algorithms """ assert_raises(ValueError, resize, np.zeros(3), (3, 3)) assert_raises(ValueError, resize, np.zeros((3, 3)), (3,)) assert_raises(ValueError, resize, np.zeros((3, 3)), (4, 4), kind='foo') for kind, tol in (('nearest', 1e-5), ('linear', 2e-1)): shape = np.array((10, 11, 3)) data = np.random.RandomState(0).rand(*shape) assert_allclose(data, resize(data, shape[:2], kind=kind), rtol=1e-5, atol=1e-5) # this won't actually be that close for bilinear interp assert_allclose(data, resize(resize(data, 2 * shape[:2], kind=kind), shape[:2], kind=kind), atol=tol, rtol=tol) ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/geometry/tests/test_meshdata.py���������������������������������������������������0000664�0001750�0001750�00000002354�12527672621�023747� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. import numpy as np from numpy.testing import assert_array_equal from vispy.testing import run_tests_if_main from vispy.geometry.meshdata import MeshData def test_meshdata(): """Test meshdata Class It's a unit square cut in two triangular element """ square_vertices = np.array([[0, 0, 0], [1, 0, 0], [1, 1, 0], [0, 1, 0]], dtype=np.float) square_faces = np.array([[0, 1, 2], [0, 2, 3]], dtype=np.uint) square_normals = np.array([[0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 0, 1]], dtype=np.float) square_edges = np.array([[0, 1], [0, 2], [0, 3], [1, 2], [2, 3]], dtype=np.uint) mesh = MeshData(vertices=square_vertices, faces=square_faces) # test vertices and faces assignement assert_array_equal(square_vertices, mesh.get_vertices()) assert_array_equal(square_faces, mesh.get_faces()) # test normals calculus assert_array_equal(square_normals, mesh.get_vertex_normals()) # test edge calculus assert_array_equal(square_edges, mesh.get_edges()) run_tests_if_main() ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/geometry/torusknot.py�������������������������������������������������������������0000664�0001750�0001750�00000010252�12502352362�022012� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������from __future__ import division import numpy as np from fractions import gcd class TorusKnot(object): """Representation of a torus knot or link. A torus knot is one that can be drawn on the surface of a torus. It is parameterised by two integers p and q as below; in fact this returns a single knot (a single curve) only if p and q are coprime, otherwise it describes multiple linked curves. Parameters ---------- p : int The number of times the knot winds around the outside of the torus. Defaults to 2. q : int The number of times the knot passes through the hole in the centre of the torus. Defaults to 3. num_points : int The number of points in the returned piecewise linear curve. If there are multiple curves (i.e. a torus link), this is the number of points in *each* curve. Defaults to 100. major_radius : float Distance from the center of the torus tube to the center of the torus. Defaults to 10. minor_radius : float The radius of the torus tube. Defaults to 5. """ def __init__(self, p=3, q=2, num_points=100, major_radius=10., minor_radius=5.): self._p = p self._q = q self._num_points = num_points self._major_radius = major_radius self._minor_radius = minor_radius self._calculate_vertices() def _calculate_vertices(self): angles = np.linspace(0, 2*np.pi, self._num_points) num_components = self.num_components divisions = (np.max([self._q, self._p]) * np.min([self._q, self._p]) / self.num_components) starting_angles = np.linspace( 0, 2*np.pi, divisions + 1)[ :num_components] q = self._q / num_components p = self._p / num_components components = [] for starting_angle in starting_angles: vertices = np.zeros((self._num_points, 3)) local_angles = angles + starting_angle radii = (self._minor_radius * np.cos(q * angles) + self._major_radius) vertices[:, 0] = radii * np.cos(p * local_angles) vertices[:, 1] = radii * np.sin(p * local_angles) vertices[:, 2] = (self._minor_radius * -1 * np.sin(q * angles)) components.append(vertices) self._components = components @property def first_component(self): '''The vertices of the first component line of the torus knot or link. ''' return self._components[0] @property def components(self): '''A list of the vertices in each line of the torus knot or link. Even if p and q are coprime, this is a list with just one entry. ''' return self._components @property def num_components(self): '''The number of component lines in the torus link. This is equal to the greatest common divisor of p and q. ''' return gcd(self._p, self._q) @property def q(self): '''The q parameter of the torus knot or link.''' return self._q @q.setter def q(self, q): self._q = q self._calculate_vertices() @property def p(self): '''The p parameter of the torus knot or link.''' return self._p @p.setter def p(self, p): self._p = p self._calculate_vertices() @property def minor_radius(self): '''The minor radius of the torus.''' return self._minor_radius @minor_radius.setter def minor_radius(self, r): self._minor_radius = r self._calculate_vertices() @property def major_radius(self): '''The major radius of the torus.''' return self._major_radius @major_radius.setter def major_radius(self, r): self._major_radius = r self._calculate_vertices() @property def num_points(self): '''The number of points in the vertices returned for each knot/link component''' return self._num_points @num_points.setter def num_points(self, r): self._num_points = r self._calculate_vertices() ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/geometry/generation.py������������������������������������������������������������0000664�0001750�0001750�00000022217�12527672621�022113� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2015, Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- # Author: Nicolas P .Rougier # Date: 04/03/2014 # ----------------------------------------------------------------------------- from __future__ import division import numpy as np from .meshdata import MeshData def create_cube(): """ Generate vertices & indices for a filled and outlined cube Returns ------- vertices : array Array of vertices suitable for use as a VertexBuffer. filled : array Indices to use to produce a filled cube. outline : array Indices to use to produce an outline of the cube. """ vtype = [('position', np.float32, 3), ('texcoord', np.float32, 2), ('normal', np.float32, 3), ('color', np.float32, 4)] itype = np.uint32 # Vertices positions p = np.array([[1, 1, 1], [-1, 1, 1], [-1, -1, 1], [1, -1, 1], [1, -1, -1], [1, 1, -1], [-1, 1, -1], [-1, -1, -1]]) # Face Normals n = np.array([[0, 0, 1], [1, 0, 0], [0, 1, 0], [-1, 0, 1], [0, -1, 0], [0, 0, -1]]) # Vertice colors c = np.array([[1, 1, 1, 1], [0, 1, 1, 1], [0, 0, 1, 1], [1, 0, 1, 1], [1, 0, 0, 1], [1, 1, 0, 1], [0, 1, 0, 1], [0, 0, 0, 1]]) # Texture coords t = np.array([[0, 0], [0, 1], [1, 1], [1, 0]]) faces_p = [0, 1, 2, 3, 0, 3, 4, 5, 0, 5, 6, 1, 1, 6, 7, 2, 7, 4, 3, 2, 4, 7, 6, 5] faces_c = [0, 1, 2, 3, 0, 3, 4, 5, 0, 5, 6, 1, 1, 6, 7, 2, 7, 4, 3, 2, 4, 7, 6, 5] faces_n = [0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5] faces_t = [0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 3, 2, 1, 0, 0, 1, 2, 3, 0, 1, 2, 3] vertices = np.zeros(24, vtype) vertices['position'] = p[faces_p] vertices['normal'] = n[faces_n] vertices['color'] = c[faces_c] vertices['texcoord'] = t[faces_t] filled = np.resize( np.array([0, 1, 2, 0, 2, 3], dtype=itype), 6 * (2 * 3)) filled += np.repeat(4 * np.arange(6, dtype=itype), 6) filled = filled.reshape((len(filled) // 3, 3)) outline = np.resize( np.array([0, 1, 1, 2, 2, 3, 3, 0], dtype=itype), 6 * (2 * 4)) outline += np.repeat(4 * np.arange(6, dtype=itype), 8) return vertices, filled, outline def create_sphere(rows, cols, radius=1.0, offset=True): """Create a sphere Parameters ---------- rows : int Number of rows. cols : int Number of columns. radius : float Sphere radius. offset : bool Rotate each row by half a column. Returns ------- sphere : MeshData Vertices and faces computed for a spherical surface. """ verts = np.empty((rows+1, cols, 3), dtype=np.float32) # compute vertices phi = (np.arange(rows+1) * np.pi / rows).reshape(rows+1, 1) s = radius * np.sin(phi) verts[..., 2] = radius * np.cos(phi) th = ((np.arange(cols) * 2 * np.pi / cols).reshape(1, cols)) if offset: # rotate each row by 1/2 column th = th + ((np.pi / cols) * np.arange(rows+1).reshape(rows+1, 1)) verts[..., 0] = s * np.cos(th) verts[..., 1] = s * np.sin(th) # remove redundant vertices from top and bottom verts = verts.reshape((rows+1)*cols, 3)[cols-1:-(cols-1)] # compute faces faces = np.empty((rows*cols*2, 3), dtype=np.uint32) rowtemplate1 = (((np.arange(cols).reshape(cols, 1) + np.array([[1, 0, 0]])) % cols) + np.array([[0, 0, cols]])) rowtemplate2 = (((np.arange(cols).reshape(cols, 1) + np.array([[1, 0, 1]])) % cols) + np.array([[0, cols, cols]])) for row in range(rows): start = row * cols * 2 faces[start:start+cols] = rowtemplate1 + row * cols faces[start+cols:start+(cols*2)] = rowtemplate2 + row * cols # cut off zero-area triangles at top and bottom faces = faces[cols:-cols] # adjust for redundant vertices that were removed from top and bottom vmin = cols-1 faces[faces < vmin] = vmin faces -= vmin vmax = verts.shape[0]-1 faces[faces > vmax] = vmax return MeshData(vertices=verts, faces=faces) def create_cylinder(rows, cols, radius=[1.0, 1.0], length=1.0, offset=False): """Create a cylinder Parameters ---------- rows : int Number of rows. cols : int Number of columns. radius : tuple of float Cylinder radii. length : float Length of the cylinder. offset : bool Rotate each row by half a column. Returns ------- cylinder : MeshData Vertices and faces computed for a cylindrical surface. """ verts = np.empty((rows+1, cols, 3), dtype=np.float32) if isinstance(radius, int): radius = [radius, radius] # convert to list # compute vertices th = np.linspace(2 * np.pi, 0, cols).reshape(1, cols) # radius as a function of z r = np.linspace(radius[0], radius[1], num=rows+1, endpoint=True).reshape(rows+1, 1) verts[..., 2] = np.linspace(0, length, num=rows+1, endpoint=True).reshape(rows+1, 1) # z if offset: # rotate each row by 1/2 column th = th + ((np.pi / cols) * np.arange(rows+1).reshape(rows+1, 1)) verts[..., 0] = r * np.cos(th) # x = r cos(th) verts[..., 1] = r * np.sin(th) # y = r sin(th) # just reshape: no redundant vertices... verts = verts.reshape((rows+1)*cols, 3) # compute faces faces = np.empty((rows*cols*2, 3), dtype=np.uint32) rowtemplate1 = (((np.arange(cols).reshape(cols, 1) + np.array([[0, 1, 0]])) % cols) + np.array([[0, 0, cols]])) rowtemplate2 = (((np.arange(cols).reshape(cols, 1) + np.array([[0, 1, 1]])) % cols) + np.array([[cols, 0, cols]])) for row in range(rows): start = row * cols * 2 faces[start:start+cols] = rowtemplate1 + row * cols faces[start+cols:start+(cols*2)] = rowtemplate2 + row * cols return MeshData(vertices=verts, faces=faces) def create_cone(cols, radius=1.0, length=1.0): """Create a cone Parameters ---------- cols : int Number of faces. radius : float Base cone radius. length : float Length of the cone. Returns ------- cone : MeshData Vertices and faces computed for a cone surface. """ verts = np.empty((cols+1, 3), dtype=np.float32) # compute vertexes th = np.linspace(2 * np.pi, 0, cols+1).reshape(1, cols+1) verts[:-1, 2] = 0.0 verts[:-1, 0] = radius * np.cos(th[0, :-1]) # x = r cos(th) verts[:-1, 1] = radius * np.sin(th[0, :-1]) # y = r sin(th) # Add the extremity verts[-1, 0] = 0.0 verts[-1, 1] = 0.0 verts[-1, 2] = length verts = verts.reshape((cols+1), 3) # just reshape: no redundant vertices # compute faces faces = np.empty((cols, 3), dtype=np.uint32) template = np.array([[0, 1]]) for pos in range(cols): faces[pos, :-1] = template + pos faces[:, 2] = cols faces[-1, 1] = 0 return MeshData(vertices=verts, faces=faces) def create_arrow(rows, cols, radius=0.1, length=1.0, cone_radius=None, cone_length=None): """Create a 3D arrow using a cylinder plus cone Parameters ---------- rows : int Number of rows. cols : int Number of columns. radius : float Base cylinder radius. length : float Length of the arrow. cone_radius : float Radius of the cone base. If None, then this defaults to 2x the cylinder radius. cone_length : float Length of the cone. If None, then this defaults to 1/3 of the arrow length. Returns ------- arrow : MeshData Vertices and faces computed for a cone surface. """ # create the cylinder md_cyl = None if cone_radius is None: cone_radius = radius*2.0 if cone_length is None: con_L = length/3.0 cyl_L = length*2.0/3.0 else: cyl_L = max(0, length - cone_length) con_L = min(cone_length, length) if cyl_L != 0: md_cyl = create_cylinder(rows, cols, radius=[radius, radius], length=cyl_L) # create the cone md_con = create_cone(cols, radius=cone_radius, length=con_L) verts = md_con.get_vertices() nbr_verts_con = verts.size//3 faces = md_con.get_faces() if md_cyl is not None: trans = np.array([[0.0, 0.0, cyl_L]]) verts = np.vstack((verts+trans, md_cyl.get_vertices())) faces = np.vstack((faces, md_cyl.get_faces()+nbr_verts_con)) return MeshData(vertices=verts, faces=faces) ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/geometry/normals.py���������������������������������������������������������������0000664�0001750�0001750�00000004615�12510536123�021421� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2014, Nicolas P. Rougier # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- import numpy as np def compact(vertices, indices, tolerance=1e-3): """ Compact vertices and indices within given tolerance """ # Transform vertices into a structured array for np.unique to work n = len(vertices) V = np.zeros(n, dtype=[("pos", np.float32, 3)]) V["pos"][:, 0] = vertices[:, 0] V["pos"][:, 1] = vertices[:, 1] V["pos"][:, 2] = vertices[:, 2] epsilon = 1e-3 decimals = int(np.log(epsilon)/np.log(1/10.)) # Round all vertices within given decimals V_ = np.zeros_like(V) X = V["pos"][:, 0].round(decimals=decimals) X[np.where(abs(X) < epsilon)] = 0 V_["pos"][:, 0] = X Y = V["pos"][:, 1].round(decimals=decimals) Y[np.where(abs(Y) < epsilon)] = 0 V_["pos"][:, 1] = Y Z = V["pos"][:, 2].round(decimals=decimals) Z[np.where(abs(Z) < epsilon)] = 0 V_["pos"][:, 2] = Z # Find the unique vertices AND the mapping U, RI = np.unique(V_, return_inverse=True) # Translate indices from original vertices into the reduced set (U) indices = indices.ravel() I_ = indices.copy().ravel() for i in range(len(indices)): I_[i] = RI[indices[i]] I_ = I_.reshape(len(indices)/3, 3) # Return reduced vertices set, transalted indices and mapping that allows # to go from U to V return U.view(np.float32).reshape(len(U), 3), I_, RI def normals(vertices, indices): """ Compute normals over a triangulated surface Parameters ---------- vertices : ndarray (n,3) triangles vertices indices : ndarray (p,3) triangles indices """ # Compact similar vertices vertices, indices, mapping = compact(vertices, indices) T = vertices[indices] N = np.cross(T[:, 1] - T[:, 0], T[:, 2]-T[:, 0]) L = np.sqrt(np.sum(N * N, axis=1)) L[L == 0] = 1.0 # prevent divide-by-zero N /= L[:, np.newaxis] normals = np.zeros_like(vertices) normals[indices[:, 0]] += N normals[indices[:, 1]] += N normals[indices[:, 2]] += N L = np.sqrt(np.sum(normals*normals, axis=1)) L[L == 0] = 1.0 normals /= L[:, np.newaxis] return normals[mapping] �������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/geometry/polygon.py���������������������������������������������������������������0000664�0001750�0001750�00000010224�12527672621�021442� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2015, Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- import numpy as np from .triangulation import Triangulation class PolygonData(object): """Polygon class for data handling Parameters ---------- vertices : (Nv, 3) array Vertex coordinates. If faces is not specified, then this will instead be interpreted as (Nf, 3, 3) array of coordinates. edges : (Nv, 2) array Constraining edges specified by vertex indices. faces : (Nf, 3) array Indexes into the vertex array. Notes ----- All arguments are optional. """ def __init__(self, vertices=None, edges=None, faces=None): self._vertices = vertices self._edges = edges self._faces = faces self._convex_hull = None @property def faces(self): """Return an array (Nf, 3) of vertex indexes, three per triangular face in the mesh. If faces have not been computed for this mesh, the function computes them. If no vertices or faces are specified, the function returns None. """ if self._faces is None: if self._vertices is None: return None self.triangulate() return self._faces @faces.setter def faces(self, f): """ If vertices and faces are incompatible, this will generate vertices from these faces and set them. """ self._faces = f @property def vertices(self): """Return an array (Nf, 3) of vertices. If only faces exist, the function computes the vertices and returns them. If no vertices or faces are specified, the function returns None. """ if self._faces is None: if self._vertices is None: return None self.triangulate() return self._vertices @vertices.setter def vertices(self, v): """ If vertices and faces are incompatible, this will generate faces from these vertices and set them. """ self._vertices = v @property def edges(self): """Return an array (Nv, 2) of vertex indices. If no vertices or faces are specified, the function returns None. """ return self._edges @edges.setter def edges(self, e): """ Ensures that all edges are valid. """ self._edges = e @property def convex_hull(self): """Return an array of vertex indexes representing the convex hull. If faces have not been computed for this mesh, the function computes them. If no vertices or faces are specified, the function returns None. """ if self._faces is None: if self._vertices is None: return None self.triangulate() return self._convex_hull def triangulate(self): """ Triangulates the set of vertices and stores the triangles in faces and the convex hull in convex_hull. """ npts = self._vertices.shape[0] if np.any(self._vertices[0] != self._vertices[1]): # start != end, so edges must wrap around to beginning. edges = np.empty((npts, 2), dtype=np.uint32) edges[:, 0] = np.arange(npts) edges[:, 1] = edges[:, 0] + 1 edges[-1, 1] = 0 else: # start == end; no wrapping required. edges = np.empty((npts-1, 2), dtype=np.uint32) edges[:, 0] = np.arange(npts) edges[:, 1] = edges[:, 0] + 1 tri = Triangulation(self._vertices, edges) tri.triangulate() return tri.pts, tri.tris def add_vertex(self, vertex): """ Adds given vertex and retriangulates to generate new faces. Parameters ---------- vertex : array-like The vertex to add. """ raise NotImplementedError ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/geometry/rect.py������������������������������������������������������������������0000664�0001750�0001750�00000012267�12527672621�020721� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������import numpy as np class Rect(object): """ Representation of a rectangular area in a 2D coordinate system. Parameters ---------- *args : arguments Can be in the form `Rect(x, y, w, h)`, `Rect(pos, size)`, or `Rect(Rect)`. """ def __init__(self, *args, **kwargs): self._pos = (0, 0) self._size = (0, 0) if len(args) == 1 and isinstance(args[0], Rect): self._pos = args[0]._pos self._size = args[0]._size elif (len(args) == 1 and isinstance(args[0], (list, tuple)) and len(args[0]) == 4): self._pos = args[0][:2] self._size = args[0][2:] elif len(args) == 2: self._pos = tuple(args[0]) self._size = tuple(args[1]) elif len(args) == 4: self._pos = tuple(args[:2]) self._size = tuple(args[2:]) elif len(args) != 0: raise TypeError("Rect must be instantiated with 0, 1, 2, or 4 " "non-keyword arguments.") self._pos = kwargs.get('pos', self._pos) self._size = kwargs.get('size', self._size) if len(self._pos) != 2 or len(self._size) != 2: raise ValueError("Rect pos and size arguments must have 2 " "elements.") @property def pos(self): return tuple(self._pos) @pos.setter def pos(self, p): assert len(p) == 2 self._pos = p @property def size(self): return tuple(self._size) @size.setter def size(self, s): assert len(s) == 2 self._size = s @property def width(self): return self.size[0] @width.setter def width(self, w): self.size[0] = w @property def height(self): return self.size[1] @height.setter def height(self, h): self.size[1] = h @property def left(self): return self.pos[0] @left.setter def left(self, x): self.size = (self.size[0] + (self.pos[0] - x), self.size[1]) self.pos = (x, self.pos[1]) @property def right(self): return self.pos[0] + self.size[0] @right.setter def right(self, x): self.size = (x - self.pos[0], self.size[1]) @property def bottom(self): return self.pos[1] @bottom.setter def bottom(self, y): self.size = (self.size[0], self.size[1] + (self.pos[1] - y)) self.pos = (self.pos[0], y) @property def top(self): return self.pos[1] + self.size[1] @top.setter def top(self, y): self.size = (self.size[0], y - self.pos[1]) def padded(self, padding): """Return a new Rect padded (smaller) by padding on all sides Parameters ---------- padding : float The padding. Returns ------- rect : instance of Rect The padded rectangle. """ return Rect(pos=(self.pos[0]+padding, self.pos[1]+padding), size=(self.size[0]-2*padding, self.size[1]-2*padding)) def normalized(self): """Return a Rect covering the same area, but with height and width guaranteed to be positive.""" return Rect(pos=(min(self.left, self.right), min(self.top, self.bottom)), size=(abs(self.width), abs(self.height))) def flipped(self, x=False, y=True): """Return a Rect with the same bounds but with axes inverted Parameters ---------- x : bool Flip the X axis. y : bool Flip the Y axis. Returns ------- rect : instance of Rect The flipped rectangle. """ pos = list(self.pos) size = list(self.size) for i, flip in enumerate((x, y)): if flip: pos[i] += size[i] size[i] *= -1 return Rect(pos, size) def __eq__(self, r): if not isinstance(r, Rect): return False return (np.all(np.equal(r.pos, self.pos)) and np.all(np.equal(r.size, self.size))) def __add__(self, a): """ Return this Rect translated by *a*. """ return self._transform_out(self._transform_in()[:, :2] + a[:2]) def contains(self, x, y): """Query if the rectangle contains points Parameters ---------- x : float X coordinate. y : float Y coordinate. Returns ------- contains : bool True if the point is within the rectangle. """ return (x >= self.left and x <= self.right and y >= self.bottom and y <= self.top) def __repr__(self): return "" % (self.pos + self.size) def _transform_in(self): """Return array of coordinates that can be mapped by Transform classes.""" return np.array([ [self.left, self.bottom, 0, 1], [self.right, self.top, 0, 1]]) def _transform_out(self, coords): """Return a new Rect from coordinates mapped after _transform_in().""" return Rect(pos=coords[0, :2], size=coords[1, :2]-coords[0, :2]) �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/io/�������������������������������������������������������������������������������0000775�0001750�0001750�00000000000�12527674621�016160� 5����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/io/_data/�������������������������������������������������������������������������0000775�0001750�0001750�00000000000�12527674621�017230� 5����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.4.0/vispy/io/_data/spatial-filters.npy������������������������������������������������������0000664�0001750�0001750�00000200120�12376730723�023054� 0����������������������������������������������������������������������������������������������������ustar �larsoner������������������������larsoner������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������“NUMPY�F�{'descr': '{?ªÎz?›z?€gz?ë3z?V�z?ÂÌy?-™y?˜ey?2y?nþx?ÚÊx?E—x?°cx?0x?†üw?òÈw?]•w?Èaw?3.w?žúv? Çv?u“v?à_v?K,v?·øu?"Åu?‘u?ø]u?c*u?Ïöt?:Ãt?¥t?\t?{(t?çôs?RÁs?½s?(Zs?“&s?ÿòr?j¿r?Õ‹r?@Xr?«$r?ñq?‚½q?í‰q?XVq?Ã"q?/ïp?š»p?ˆp?pTp?Û p?Gío?²¹o?†o?ˆRo?ôo?_ën?Ê·n?5„n? Pn? n?wém?âµm?M‚m?¸Nm?$m?çl?ú³l?e€l?ÐLl?<l?§åk?²k?}~k?èJk?Tk?¿ãj?*°j?•|j?�Ij?lj?×ái?B®i?­zi?Gi?„i?ïßh?Z¬h?Åxh?0Eh?œh?Þg?rªg?Ývg?ICg?´g?Üf?Ѝf?õtf?aAf?Ì f?7Úe?¢¦e? se?y?e?ä e?OØd?º¤d?%qd?‘=d?ü d?gÖc?Ò¢c?=oc?©;c?c?Ôb?ê b?Umb?Á9b?,b?—Òa?Ÿa?mka?Ù7a?Da?¯Ð`?`?†i`?ñ5`?\`?ÇÎ_?2›_?žg_? 4_?t�_?ßÌ^?J™^?¶e^?!2^?Œþ]?÷Ê]?b—]?Îc]?90]?¤ü\?É\?z•\?æa\?Q.\?¼ú[?'Ç[?’“[?þ_[?i,[?ÔøZ??ÅZ?ª‘Z?^Z?*Z?ìöY?WÃY?ÃY?.\Y?™(Y?õX?oÁX?ÛX?FZX?±&X?óW?‡¿W?ó‹W?^XW?É$W?4ñV?Ÿ½V? ŠV?vVV?á"V?LïU?·»U?#ˆU?ŽTU?ù U?díT?ϹT?;†T?¦RT?T?|ëS?ç·S?S„S?¾PS?)S?”éR?�¶R?k‚R?ÖNR?AR?¬çQ?´Q?ƒ€Q?îLQ?YQ?ÄåP?0²P?›~P?KP?qP?ÜãO?H°O?³|O?IO?‰O?ôáN?`®N?ËzN?6GN?¡N? àM?x¬M?ãxM?NEM?¹M?$ÞL?ªL?ûvL?fCL?ÑL?<ÜK?¨¨K?uK?~AK?é K?UÚJ?À¦J?+sJ?–?J? J?mØI?ؤI?CqI?®=I? I?…ÖH?ð¢H?[oH?Æ;H?1H?ÔG?¡G?smG?Þ9G?IG?µÒF? ŸF?‹kF?ö7F?aF?ÍÐE?8E?£iE?6E?yE?åÎD?P›D?»gD?&4D?’�D?ýÌC?h™C?ÓeC?>2C?ªþB?ËB?€—B?ëcB?V0B?ÂüA?-ÉA?˜•A?bA?n.A?Úú@?EÇ@?°“@?`@?†,@?òø??]Å??È‘??3^??ž*?? ÷>?uÃ>?à>?K\>?¶(>?"õ=?Á=?ø=?cZ=?Ï&=?:ób ?ª. ?û ?€Ç ?ë“ ?V` ?Â, ?-ù ?˜Å ?’ ?n^ ?Ú* ?E÷?°Ã??†\?ò(?]õ?ÈÁ?3Ž?žZ? '?uó?à¿?KŒ?¶X?"%?ñ?ø½?cŠ?ÎV?:#?¥ï?¼?{ˆ?çT?R!?½í?(º?“†?ÿR?j?Õë?@¸?«„?Q?‚?íé?X¶?Â?/O?š?è�?p´�?Û€�?GM�?²�?:Ìÿ>eÿ>çýþ>½–þ>”/þ>jÈý>Aaý>úü>í’ü>Ä+ü>šÄû>q]û>Göú>ú>ô'ú>ÊÀù>¡Yù>wòø>M‹ø>$$ø>ú¼÷>ÑU÷>§îö>~‡ö>T ö>*¹õ>Rõ>×êô>®ƒô>„ô>Zµó>1Nó>çò>Þò>´ò>бñ>aJñ>7ãð>|ð>äð>»­ï>‘Fï>gßî>>xî>î>ë©í>ÁBí>—Ûì>ntì>D ì>¦ë>ñ>ë>Ç×ê>žpê>t ê>K¢é>!;é>øÓè>Îlè>¤è>{žç>Q7ç>(Ðæ>þhæ>Ôæ>«šå>3å>XÌä>.eä>þã>Û–ã>±/ã>ˆÈâ>^aâ>5úá> “á>á+á>¸Äà>Ž]à>eöß>;ß>(ß>èÀÞ>¾YÞ>•òÝ>k‹Ý>A$Ý>½Ü>îUÜ>ÅîÛ>›‡Û>r Û>H¹Ú>RÚ>õêÙ>˃Ù>¢Ù>xµØ>NNØ>%ç×>û×>Ò×>¨±Ö>~JÖ>UãÕ>+|Õ>Õ>Ø­Ô>¯FÔ>…ßÓ>[xÓ>2Ó>ªÒ>ßBÒ>µÛÑ>‹tÑ>b Ñ>8¦Ð>?Ð>å×Ï>»pÏ>’ Ï>h¢Î>?;Î>ÔÍ>ìlÍ>ÂÍ>˜žÌ>o7Ì>EÐË>iË>òË>ÈšÊ>Ÿ3Ê>uÌÉ>LeÉ>"þÈ>ø–È>Ï/È>¥ÈÇ>|aÇ>RúÆ>(“Æ>ÿ+Æ>ÕÄÅ>¬]Å>‚öÄ>YÄ>/(Ä>ÁÃ>ÜYÃ>²òÂ>‰‹Â>_$Â>5½Á> VÁ>âîÀ>¹‡À> À>e¹¿>ë¾>郾>¿¾>–µ½>lN½>Bç¼>€¼>ï¼>Ʊ»>œJ»>rãº>I|º>º>ö­¹>ÌF¹>¢ß¸>yx¸>O¸>&ª·>üB·>ÓÛ¶>©t¶> ¶>V¦µ>,?µ>Ø´>Ùp´>¯ ´>†¢³>\;³>3Ô²> m²>ß²>¶ž±>Œ7±>cа>9i°>°>暯>¼3¯>“Ì®>ie®>@þ­>—­>ì/­>ÃȬ>™a¬>pú«>F“«>,«>óĪ>É]ª> ö©>v©>M(©>#Á¨>ùY¨>Ðò§>¦‹§>}$§>S½¦>)V¦>�ï¥>Ö‡¥>­ ¥>ƒ¹¤>YR¤>0ë£>„£>Ý£>³µ¢>ŠN¢>`ç¡>6€¡> ¡>ã± >ºJ >ãŸ>f|Ÿ>=Ÿ>®ž>êFž>Àß>–x>m>Cªœ>Cœ>ðÛ›>Çt›> ›>s¦š>J?š> Ø™>÷p™>Í ™>£¢˜>z;˜>PÔ—>'m—>ý—>Óž–>ª7–>€Ð•>Wi•>-•>›”>Ú3”>°Ì“>‡e“>]þ’>4—’> 0’>àÈ‘>·a‘>ú>d“>:,>Å>ç]>½öŽ>”Ž>j(Ž>AÁ>Z>íòŒ>Ä‹Œ>š$Œ>q½‹>GV‹>ïŠ>ô‡Š>Ê Š>¡¹‰>wR‰>Mëˆ>$„ˆ>úˆ>ѵ‡>§N‡>}ç†>T€†>*†>²…>×J…>®ã„>„|„>Z„>1®ƒ>Gƒ>Þß‚>´x‚>Š‚>aª>7C>Ü€>ät€>º €>"M>Ï~~>{°}>(â|>Õ|>‚E{>/wz>ܨy>ˆÚx>5 x>â=w>ov><¡u>èÒt>•t>B6s>ïgr>œ™q>IËp>õüo>¢.o>O`n>ü‘m>©Ãl>Võk>'k>¯Xj>\Ši> ¼h>¶íg>bg>Qf>¼‚e>i´d>æc>Ãc>oIb>{a>ɬ`>vÞ_>#_>ÐA^>|s]>)¥\>ÖÖ[>ƒ[>0:Z>ÜkY>‰X>6ÏW>ã�W>2V>=dU>é•T>–ÇS>CùR>ð*R>\Q>JŽP>ö¿O>£ñN>P#N>ýTM>ª†L>V¸K>êJ>°J>]MI> H>·°G>câF>F>½EE>jwD>©C>ÄÚB>p B>>A> o~?�o~? n~?n~?"m~?l~?¥j~?i~?*g~? e~?¯b~?`~?6]~?Z~?¾V~?#S~?HO~?.K~?ÓF~?:B~?`=~?G8~?ï2~?W-~?'~?h!~?~?{~?¦ ~?‘~?<ÿ}?¨÷}?Õï}?Âç}?pß}?ÞÖ}?Î}?ýÄ}?®»}?²}?R¨}?Dž}?ø“}?m‰}?¢~}?˜s}?Oh}?Ç\}?Q}?ûD}?¶8}?2,}?o}?m}?-}?­÷|?ïé|?òÛ|?·Í|?<¿|?„°|?Œ¡|?V’|?á‚|?.s|?n?Ên?áæm?úm?pŽm?çam?)5m?5m? Ûl?±­l?€l?YRl?^$l?/ök?ÌÇk?4™k?hjk?i;k?5 k?ÎÜj?4­j?e}j?dMj?/j?Çìi?,¼i?^‹i?^Zi?*)i?Ä÷h?,Æh?a”h?dbh?60h?Õýg?BËg?~˜g?ˆeg?`2g?ÿf?~Ëf?×f?×cf?º/f?lûe?îÆe??’e?`]e?Q(e?ód?£½d?ˆd?5Rd?7d? æc?¬¯c? yc?eBc?{ c?bÔb?b?¥eb?�.b?.öa?-¾a?ÿ…a?¢Ma?a?`Ü`?{£`?ij`?)1`?¼÷_?#¾_?]„_?jJ_?K_?ÿÕ^?ˆ›^?ä`^?&^?ë]?ò¯]? t]?"9]?yý\?¥Á\?¦…\?}I\?) \?ªÐ[?”[?.W[?1[? ÝZ?ºŸZ??bZ?œ$Z?ÏæY?Ù¨Y?ºjY?r,Y?îX?i¯X?§pX?¾1X?¬òW?s³W?tW?‰4W?ØôV?µV?uV?Ü4V?ôU?´U?‚sU?Â2U?ÜñT?ϰT?oT?E.T?ÇìS?$«S?\iS?n'S?\åR?%£R?É`R?IR?¤ÛQ?Û˜Q?îUQ?ÞQ?ªÏP?RŒP?×HP?9P?wÁO?“}O?Œ9O?cõN?±N?ªlN?(N?hãM?•žM? YM?‰M?RÏL?ù‰L?€DL?æþK?+¹K?PsK?U-K?:çJ?ÿ J?¤ZJ?*J?ÍI?׆I?�@I? ùH?ô±H?ÀjH?n#H?þÛG?p”G?ÄLG?ûG?½F?uF?î,F?°äE?UœE?ÝSE?I E?™ÂD?ÌyD?ä0D?àçC?ÁžC?†UC?0 C?¿ÂB?4yB?/B?ÍåA?ñ›A?üQA?íA?Ľ@?‚s@?&)@?±Þ??#”??|I??¼þ>?ä³>?ôh>?ë>?ËÒ=?’‡=?C<=?ÛðÑ1?æ‚1?~41?æ0?—0?çH0?Aú/?‹«/?Æ\/?ò /?¿.?p.? !.?Ò-?ø‚-?Ð3-?šä,?V•,?F,?¨ö+?>§+?ÇW+?D+?µ¸*?i*?s*?ÁÉ)?z)?:*)?fÚ(?‡Š(?:(?©ê'?«š'?£J'?‘ú&?uª&?PZ&?" &?ê¹%?ªi%?a%?É$?¶x$?T($?ê×#?y‡#?�7#?€æ"?ø•"?jE"?Õô!?9¤!?—S!?ï!?A² ?a ?Ô ?À?Qo?ˆ?»Í?è|?,?7Û?XŠ?u9?è?¥—?¸F?Èõ?Õ¤?ßS?ç?í±?ñ`?ó?ó¾?òm?ï?ìË?çz?â)?ÝØ?ׇ?Ñ6?Ëå?Å”?ÀC?»ò?¸¡?µP?´ÿ?´®?¶]?º ?À»?Èj?Ò?ßÈ?ïw?'?Ö?2…?O4?qã?–’?¿A?íð? ?VO?’þ?Ó­?]?f ?¸» ?k ?n ?ÒÉ ?=y ?¯( ?'Ø ?§‡ ?.7 ?½æ ?S– ?ñE ?—õ ?F¥ ?ýT ?½ ?…´?Wd?2?Ä?t?ü#?þÓ? „?!4?Bä?n”?¤D?çô?4¥?U?ò?c¶?àf?i?ÿÇ?¡x?Q)?Ú?׊?¯;?”ì�?‡�?ˆN�?.ÿÿ>iaÿ>ÂÃþ>8&þ>͈ý>€ëü>QNü>B±û>Rû>ƒwú>ÓÚù>D>ù>סø>Šø>`i÷>XÍö>r1ö>¯•õ>úô>”^ô>=Ãó> (ó>ûŒò>òñ>OWñ>±¼ð>:"ð>é‡ï>Àíî>¾Sî>ã¹í>1 í>¨†ì>Gíë>Të>»ê>"ê>f‰é>×ðè>tXè><Àç>1(ç>Qæ>žøå>aå>¿Éä>”2ä>—›ã>Éã>)nâ>¹×á>xAá>g«à>†à>Öß>WêÞ> UÞ>í¿Ý>+Ý>K–Ü>ÆÜ>tmÛ>VÙÚ>kEÚ>µ±Ù>4Ù>çŠØ>Ï÷×>îd×>BÒÖ>Ì?Ö>­Õ>†Õ>µ‰Ô>øÓ>¼fÓ>”ÕÒ>¥DÒ>ï³Ñ>r#Ñ>0“Ð>'Ð>YsÏ>ÆãÎ>nTÎ>RÅÍ>r6Í>ΧÌ>fÌ><‹Ë>OýÊ>ŸoÊ>.âÉ>ûTÉ>ÈÈ>Q;È>Û®Ç>¥"Ç>®–Æ>ø Æ>ƒÅ>OôÄ>\iÄ>«ÞÃ>ÊÂ>%@Â>~¶Á>-Á>û£À>À>ˆ’¿>5 ¿>(‚¾>`ú½>Ýr½>¡ë¼>«d¼>ûÝ»>“W»>qѺ>˜Kº>ƹ>½@¹>½»¸>7¸>—²·>r.·>—ª¶>'¶>À£µ>Å µ>ž´>°´>—™³>ʳ>J–²>²>/”±>–±>K“°>M°>ž“¯>=¯>+•®>i®>ö—­>Ó­>ÿ›¬>}¬>K¡«>j$«>Ú§ª>œ+ª>±¯©>4©>и¨>Û=¨>:ç>ìH§>òΦ>LU¦>úÛ¥>ýb¥>Uê¤>r¤>ú£>\‚£> £>”¢>j¢>§¡>&1¡>†» >?F >PÑŸ>¹\Ÿ>zèž>•tž>ž>Õ>ü>}¨œ>X6œ>ŽÄ›>S›> âš>Qqš>ô�š>ó™>M!™>²˜>C˜>ŠÔ—>Xf—>„ø–>‹–>ö–><±•>àD•>䨔>Fm”> ”>*—“>¬,“>Â’>ÐX’>rï‘>v†‘>Û‘>¡µ>ÉM>Sæ>?>Ž>?²Ž>SLŽ>Êæ>¥>ã>…¸Œ>ŒTŒ>öð‹>Æ‹>ú*‹>“ÈŠ>’fŠ>öŠ>À£‰>ðB‰>‡âˆ>„‚ˆ>ç"ˆ>²Ã‡>ãd‡>}‡>~¨†>æJ†>·í…>ð…>’4…>Ø„>}„>í!„>3ǃ>ãlƒ>ýƒ>¹‚>o`‚>È‚>‹¯>ºW>T�>Y©€>ÉR€>Kù>ÜM>E£~>‡ù}>£P}>˜¨|>g|>[{>•µz>õz>0my>HÊx><(x> ‡w>ºæv>FGv>°¨u>ø u>nt>%Òs> 7s>Мr>ur>üjq>cÓp>¬

Ö¦o>ão>Ò}n>¤êm>YXm>òÆl>n6l>Ϧk>k>>Šj>Nýi>Cqi>æh>ß[h>‡Òg>Jg>ŒÂf>ê;f>0¶e>^1e>t­d>t*d>]¨c>/'c>ë¦b>‘'b>!©a>+a>¯`>U3`>’¸_>¼>_>ÑÅ^>ÔM^>ÃÖ]>Ÿ`]>hë\>w\>Å\>X‘[>Ú[>K¯Z>ª?Z>úÐY>8cY>göX>†ŠX>•X>•µW>…LW>gäV>:}V>ÿV>¶±U>_MU>úéT>ˆ‡T> &T>}ÅS>åeS>?S>Ž©R>ÑLR>ñQ>3–Q>ShãP>s‹P>r4P>gÞO>R‰O>35O> âN>×N>›>N>UîM>ŸM>¯PM>OM>ç¶L>vkL>ý L>|×K>ôŽK>dGK>ÌK>-»J>‡vJ>Û2J>'ðI>m®I>¬mI>æ-I>ïH>F±H>mtH>8H>«ýG>ÁÃG>ÓŠG>ßRG>çG>éåF>ç°F>à|F>ÕIF>ÆF>²æE>š¶E>‡E>_YE><,E>E>êÔD>¼ªD>‹D>WYD>2D>ä D>§æC>fÂC>#ŸC>Ý|C>•[C>J;C>üC>¬ýB>ZàB>ÄB>°¨B>WŽB>ýtB> \B>BEB>á.B>B>B>¶ñA>OßA>æÍA>|½A>®A>£ŸA>4’A>Ä…A>SzA>àoA>lfA>ö]A>VA>PA>ŽJA>FA>˜BA>@A>>A>>A>j*o?O*o?ý)o?v)o?¸(o?Ä'o?™&o?9%o?¢#o?Ô!o?Ño?—o?(o?o?¥o?“o?Jo?Ë o?o?+o? o?±ûn?$÷n?`òn?fín?6èn?Ïân?3Ýn?a×n?XÑn?Ën?¥Än?ú½n?·n?°n?·¨n?4¡n?|™n?Ž‘n?j‰n?n?€xn?ºon?¾fn?]n?&Tn?‰Jn?¶@n?®6n?p,n?ü!n?Sn?t n?`n?öm?—êm?âÞm?÷Òm?ØÆm?‚ºm?ø­m?8¡m?B”m?‡m?¸ym?#lm?Y^m?ZPm?%Bm?¼3m?%m?Im?Am?øl?‘èl?êØl? Él?ý¸l?·¨l?=˜l?‡l?ªvl?’el?ETl?ÃBl?1l?#l? l?²úk?+èk?oÕk?€Âk?\¯k?œk?xˆk?¸tk?Ä`k?œLk?@8k?±#k?ík?öùj?Ëäj?mÏj?Û¹j?¤j?Žj?ðwj?aj?ýJj?74j?>j?j?±îi?×i?Y¿i?a§i?5i?×vi?F^i?‚Ei?Œ,i?ci?úh?zàh?ºÆh?Ǭh?£’h?Lxh?Ã]h?Ch?(h?ü h?«ñg?(Ög?tºg?Žžg?v‚g?-fg?²Ig?-g?)g?óf?ÛÕf?j¸f?Èšf?õ|f?ñ^f?¼@f?W"f?Áf?úäe?Æe?Û¦e?ƒ‡e?ûge?BHe?Z(e?Ae?øçd?Çd?צd?ÿ…d?÷dd?¿Cd?X"d?Ád?ûÞc?½c?âšc?Žxc? Vc?Z3c?zc?jíb?-Êb?À¦b?%ƒb?\_b?d;b?>b?êòa?gÎa?·©a?Ù„a?Í_a?“:a?+a?–ï`?ÔÉ`?ä£`?Ç}`?|W`?1`?` `?ã_?‘¼_?f•_?n_?ŠF_?Ú_?ýö^?ôÎ^?¿¦^?]~^?ÐU^?-^?2^?!Û]?å±]?~ˆ]?ë^]?-5]?C ]?/á\?ï¶\?…Œ\?ða\?17\?F \?2á[?óµ[?ŠŠ[?ö^[?93[?Q[?@ÛZ?¯Z?¡‚Z?VZ?\)Z?{üY?qÏY??¢Y?ãtY?^GY?±Y?ÛëX?ܽX?¶X?faX?ï2X?PX?ˆÕW?™¦W?‚wW?CHW?ÝW?PéV?›¹V?¿‰V?¼YV?“)V?BùU?ËÈU?-˜U?hgU?~6U?mU?6ÔT?Ù¢T?VqT?­?T?ß T?ëÛS?Ò©S?“wS?0ES?§S?úßR?(­R?1zR?GR?ÕR?qàQ?é¬Q?=yQ?mEQ?yQ?aÝP?&©P?ÇtP?E@P?  P?ØÖO?í¡O?ßlO?¯7O?\O?çÌN?O—N?–aN?º+N?¼õM?¿M?\‰M?úRM?vM?ÑåL? ¯L?$xL?AL?ô L?«ÒK?B›K?¸cK?,K?DôJ?[¼J?Q„J?(LJ?ßJ?wÛI?ð¢I?JjI?…1I?¡øH?Ÿ¿H?~†H?>MH?àH?eÚG?Ë G?gG?>-G?KóF?;¹F? F?ÃDF?[ F?×ÏE?6•E?xZE?žE?§äD?•©D?fnD?3D?¶÷C?4¼C?—€C?ßDC? C?ÍB?‘B?ðTB?±B?XÜA?åŸA?WcA?°&A?ïé@?­@? p@?3@?ëõ??«¸??R{??á=??V??³Â>?ø„>?%G>?9 >?6Ë=?=?èN=?ž=?<Ò ?ÄÆ?Uƒ?ó??œü?Q¹?v?ß2?¹ï? ¬?“i?“&?¡ã?» ?ä]??]Ø?®•?S?{?ï›ÿ>ÿ>8’þ>ˆ þ>÷ˆý>„ý>0€ü>ûûû>åwû>ïóú>pú>dìù>Ïhù>[åø>bø>×Þ÷>È[÷>ÛØö>Vö>iÓõ>äPõ>ƒÎô>FLô>-Êó>9Hó>iÆò>¾Dò>9Ãñ>ÙAñ>ŸÀð>Œ?ð>Ÿ¾ï>Ù=ï>;½î>Ä<î>u¼í>N<í>O¼ì>z<ì>ͼë>J=ë>ñ½ê>Á>ê>¼¿é>â@é>3Âè>®Cè>VÅç>)Gç>(Éæ>TKæ>­Íå>3På>æÒä>ÇUä>ÕØã>\ã>~ßâ>câ>ãæá>Üjá>ïà>^sà>è÷ß>¢|ß>ß>ª†Þ>ø Þ>x‘Ý>*Ý>Ü>&#Ü>p©Û>î/Û>Ÿ¶Ú>…=Ú>žÄÙ>ìKÙ>oÓØ>'[Ø>ã×>8k×>‘óÖ> |Ö>æÖ>ãÕ>Õ>‚ Ô>%*Ô>´Ó>>Ó>_ÈÒ>ãRÒ>¡ÝÑ>˜hÑ>ÉóÐ>4Ð>Ù Ð>¸–Ï>Ò"Ï>(¯Î>¹;Î>…ÈÍ>ŽUÍ>ÒâÌ>SpÌ>þË> ŒË>DË>º¨Ê>n7Ê>_ÆÉ>UÉ>þäÈ>¬tÈ>™È>Æ”Ç>2%Ç>ÞµÆ>ËFÆ>ø×Å>fiÅ>ûÄ>Ä>8Ä>¬±Ã>bDÃ>Z×Â>–jÂ>þÁ>Õ‘Á>Ú%Á>#ºÀ>°NÀ>ã¿>–x¿>ð ¿>£¾>t9¾>žÏ½>f½>Äü¼>À“¼>+¼>ŒÂ»>]Z»>uòº>ÕŠº>|#º>l¼¹>£U¹>$ï¸>툸>ÿ"¸>[½·>X·>îò¶>'޶>«)¶>xŵ>‘aµ>ôý´>£š´>7´>ãÔ³>ur³>S³>~®²>õL²>¹ë±>ËŠ±>)*±>Öɰ>Ði°> °>¯ª¯>”K¯>Çì®>JŽ®>0®>>Ò­>¯t­>p­>‚º¬>ã]¬>•¬>˜¥«>íI«>’îª>‰“ª>Ñ8ª>kÞ©>X„©>—*©>(Ѩ> x¨>C¨>ÎÆ§>«n§>ݧ>b¿¦>;h¦>i¦>뺥>Âd¥>í¥>n¹¤>Dd¤>o¤>ñº£>Èf£>õ£>y¿¢>Sl¢>„¢> Ç¡>êt¡> #¡>®Ñ >”€ >Ñ/ >gߟ>TŸ>›?Ÿ>:ðž>1¡ž>‚Rž>-ž>0¶>h>E>VΜ>Áœ>‡5œ>§é›>"ž›>øR›>)›>µ½š>sš>á)š>€à™>{—™>ÓN™>‡™>—¾˜>w˜>Î/˜>õè—>y¢—>[\—>š—>7Ñ–>2Œ–>‹G–>B–>X¿•>Ì{•>Ÿ8•>Ðõ”>a³”>Qq”>¡/”>Oî“>^­“>Ìl“>›,“>Éì’>X­’>Hn’>—/’>Hñ‘>Z³‘>Ìu‘> 8‘>Õû>l¿>eƒ>¿G>{ >™Ñ>—>ü\>B#>êéŽ>ô°Ž>bxŽ>3@Ž>gŽ>þÐ>ù™>Wc>->?÷Œ>ÉÁŒ>·ŒŒ> XŒ>¿#Œ>Ûï‹>Z¼‹>?‰‹>ˆV‹>6$‹>JòŠ>ÃÀŠ>¡Š>ä^Š>.Š>œþ‰>ω>량>,q‰>ÓB‰>à‰>Sçˆ>-ºˆ>nˆ>aˆ>#5ˆ>˜ ˆ>uÞ‡>¸³‡>b‰‡>t_‡>í5‡>Î ‡>ä†>Ç»†>ß“†>_l†>GE†>—†>Oø…>pÒ…>ù¬…>뇅>Ec…>?…>3…>È÷„>ÅÔ„>+²„>û„>3n„>ÕL„>á+„>U „>3ëƒ>{˃>-¬ƒ>Hƒ>Ínƒ>¼Pƒ>3ƒ>׃>ù‚>›Ü‚>œÀ‚>¥‚>Þ‰‚>o‚>ÉT‚>ß:‚>_!‚>J‚>Ÿï>`×>‹¿>!¨>"‘>Žz>ed>¨N>U9>n$>ò>âû€>=è€>Õ€>5€>Ò¯€>Û€>OŒ€>/{€>{j€>3Z€>VJ€>æ:€>á+€>H€>€>Y€>è>6Î><µ>>Ñ…>_o>ÆY>E>1>>× >xú~>óé~>EÚ~>qË~>u½~>R°~>¤~>—˜~>ÿ~>?„~>X{~>Ks~>l~>ºe~>8`~>Ž[~>½W~>ÆT~>§R~>bQ~>õP~> o~?ùn~?…n~?Äm~?¶l~?[k~?³i~?¿g~?~e~?òb~?`~?ô\~?ƒY~?ÇU~?¿Q~?lM~?ÎH~?åC~?±>~?29~?h3~?T-~?ö&~?N ~?\~? ~?š ~?Ë~?²ú}?Qò}?¦é}?²à}?v×}?ñÍ}?#Ä}? º}?¯¯}? ¥}?š}?æŽ}?iƒ}?¥w}?™k}?F_}?¬R}?ÌE}?¥8}?7+}?ƒ}?‰}?I}?Ãò|?÷ã|?æÔ|?Å|?óµ|?¦|?ì•|?…|?Ñt|?Ýc|?¥R|?(A|?g/|?c|? |?Žø{?¿å{?¬Ò{?V¿{?½«{?â—{?Ã{?bo{?¿Z{?ÙE{?±0{?H{?œ{?¯ïz?€Ùz?Ãz?_¬z?m•z?:~z?Çfz?Oz?7z?èz?sz?¾íy?ÉÔy?•»y?!¢y?mˆy?zny?ITy?Ø9y?(y?:y?éx?£Íx?ú±x?–x?îyx?Œ]x?ì@x?$x?ôx?œéw?Ìw?5®w?'w?Ýqw?VSw?’4w?“w?Xöv?áÖv?/·v?A—v?wv?³Vv?6v?:v?%ôu?ÖÒu?M±u?‰u?‹mu?SKu?á(u?6u?Qãt?3Àt?Üœt?Lyt?ƒUt?1t?G t?Õès?*Äs?GŸs?,zs?ÙTs?O/s? s?“ãr?c½r?ü–r?]pr?ˆIr?}"r?:ûq?ÂÓq?¬q?/„q?\q?Å3q?? q?„âp?”¹p?op?gp?†=p?Ãp?Ëéo?Ÿ¿o?>•o?ªjo?á?o?åo?¶én?S¾n?½’n?ófn?÷:n?Èn?fâm?Òµm? ‰m?\m?è.m?Œm?ýÓl?=¦l?Lxl?)Jl?Õl?Pík?›¾k?´k?`k?V1k?ßk?7Òj?`¢j?Xrj?"Bj?»j?&ái?a°i?mi?JNi?ùi?yëh?˹h?î‡h?ãUh?«#h?Dñg?°¾g?î‹g?ÿXg?ã%g?šòf?$¿f?‹f?²Wf?¶#f?Žïe?9»e?¹†e? Re?5e?2èd?³d?©}d?$Hd?td?™Üc?”¦c?dpc? :c?†c?×Ìb?ÿ•b?ý^b?Ò'b?}ða?ÿ¸a?Xa?‡Ia?Ža?mÙ`?#¡`?°h`?0`?S÷_?h¾_?V…_?L_?º_?1Ù^?‚Ÿ^?«e^?­+^?‰ñ]?>·]?Ì|]?5B]?w]?”Ì\?Š‘\?[V\?\?ß[?î£[?*h[?B,[?4ðZ?´Z?«wZ?1;Z?’þY?ÏÁY?è„Y?ÞGY?° Y?_ÍX?ëX?SRX?™X?¼ÖW?¼˜W?šZW?VW?ïÝV?fŸV?¼`V?ð!V?ãU?ó£U?ÃdU?r%U?æT?m¦T?¹fT?å&T?ðæS?ܦS?§fS?R&S?ÞåR?J¥R?—dR?Å#R?ÓâQ?¡Q?“`Q?EQ?ØÝP?MœP?¤ZP?ÝP?øÖO?õ”O?ÕRO?—O?;ÎN?ËN?.IN?|N?­ÃM?Á€M?¹=M?•úL?U·L?ùsL?0L?îìK??©K?ueK?!K?ÝJ?s™J?=UJ?ìJ?ÌI?ü‡I?\CI?£þH?йH?ãtH?Ü/H?¼êG?ƒ¥G?1`G?ÆG?CÕF?¦F?ñIF?$F??¾E?BxE?-2E?ìD?¼¥D?`_D?íD?cÒC?‹C? EC?<þB?W·B?[pB?J)B?#âA?åšA?’SA?* A?«Ä@?}@?p5@?²í??à¥??ù]??þ??îÍ>?Ê…>?’=>?Fõ=?æ¬=?sd=?ì=?RÓ?íð?“£?7V?Û?~»? n?Á ?bÓ?†?¤8?Eë?æ?‡P?*?̵?ph??ºÍ?a€? 3?³å?_˜? K?¼ý?n°?"c?Ù?’È?N{?.?Ðà?•“?^F?+ù?û«?Ï^?§?ƒÄ?cw?H*?2Ý ? ?C ? ö ? © ? \ ? ?# ?7u ?Q( ?qÛ ?—Ž ?ÄA ?÷ô ?1¨ ?r[ ?» ? Â?`u?¾(?$Ü?’?C?…ö? ª?™]?0?ÏÄ?wx?),?ãß?§“?tG?Kû?,¯?c? ? Ë??'3?Fç?o›?£O?â?-¸?ƒl?å ?¥ªÿ>—ÿ>¢|þ>Ååý>Oý>U¸ü>Ã!ü>K‹û>ìôú>¨^ú>~Èù>o2ù>{œø>£ø>æp÷>EÛö>ÁEö>Y°õ>õ>à…ô>Ððó>Þ[ó> Çò>T2ò>¼ñ>D ñ>ìtð>³àï>šLï>¡¸î>È$î>‘í>{ýì>jì>³Öë>‚Cë>s°ê>‡ê>¾Šé>øè>—eè>8Óç>þ@ç>鮿>øæ>,‹å>†ùä>hä>«Öã>wEã>i´â>‚#â>Ã’á>+á>»qà>sáß>SQß>\ÁÞ>Ž1Þ>ê¡Ý>oÝ>ƒÜ>÷óÛ>ûdÛ>*ÖÚ>„GÚ> ¹Ù>º*Ù>—œØ>¡Ø>×€×>:óÖ>ËeÖ>‰ØÕ>tKÕ>¾Ô>×1Ô>N¥Ó>õÓ>ËŒÒ>ÐÒ>uÑ>kéÐ>^Ð>ÉÒÏ>ÂGÏ>ì¼Î>G2Î>Õ§Í>–Í>‰“Ì>¯ Ì>€Ë>•öÊ>VmÊ>LäÉ>v[É>ÔÒÈ>hJÈ>1ÂÇ>0:Ç>e²Æ>Ñ*Æ>s£Å>LÅ>\•Ä>¤Ä>#ˆÃ>ÛÃ>Ì{Â>õõÁ>WpÁ>óêÀ>ÈeÀ>×à¿>!\¿>¥×¾>dS¾>_Ͻ>”K½>ȼ>´D¼>žÁ»>Å>»>(¼º>Ê9º>¨·¹>Å5¹> ´¸>º2¸>’±·>ª0·>°¶>—/¶>n¯µ>…/µ>ݯ´>u0´>O±³>k2³>ȳ²>h5²>J·±>n9±>Ö»°>>°>pÁ¯>£D¯>È®>ÖK®>×Ï­>T­>¨Ø¬>y]¬>â«>îg«>’íª>~sª>°ù©>+€©>í©>÷¨>J¨>朧>Ë$§>ú¬¦>r5¦>4¾¥>@G¥>˜Ð¤>:Z¤>'ä£>`n£>åø¢>¶ƒ¢>Ó¢>=š¡>ô%¡>ù± >K> >ëÊŸ>ÚWŸ>åž>£rž>~ž>©Ž>#>> ;œ>uÊ›>1Z›>?êš>Ÿzš>P š>Tœ™>ª-™>T¿˜>PQ˜>Ÿã—>Cv—>: —>†œ–>&0–>Ä•>fX•>í”>ü”>H”>묓>äB“>4Ù’>Üo’>Ü’>3ž‘>ã5‘>ëÍ>Lf>ÿ>˜>ˆ1>OËŽ>qeŽ>îÿ>Æš>ù5>ˆÑŒ>rmŒ>¹ Œ>\¦‹>\C‹>ºàŠ>t~Š>Š>»‰>ØY‰> ùˆ>˜ˆ>8ˆ>àØ‡>‘y‡>¢‡>¼†>æ]†>†>®¢…>¤E…>üè„>·Œ„>Ô0„>TÕƒ>7zƒ>~ƒ>(Å‚>7k‚>ª‚>¸>¾_>`>g¯€>ÕW€>¨€>ÅS>§~>û}>öO}>¤¥|>"ü{>qS{>’«z>„z>H^y>à¸x>Kx>‹pw>ŸÍv>‰+v>IŠu>àét>NJt>”«s>³ s>ªpr>|Ôq>'9q>®žp>p>Olo>jÔn>b=n>9§m>îm>‚}l>öék>JWk>€Åj>—4j>¤i>li>+‡h>Ïùg>Wmg>Äáf>Wf>QÍe>rDe>z¼d>k5d>D¯c>*c>´¥b>L"b>ÏŸa>>a>™`>â`>Ÿ_>=!_>P¤^>S(^>F­]>*3]>º\>ÇA\>Ê[>.T[>ÎÞZ>djZ>îöY>m„Y>ãY>P¢X>´2X>ÄW>eVW>³éV>û}V>=V>z©U>³@U>èØT>rT>J T>x§S>¤CS>ÏàR>û~R>'R>T¾Q>‚_Q>³Q>ç¤P>IP>ZîO>›”O>à;O>,äN>~N>×7N>8ãM>¢M>=M>ëL>›L>§KL>DýK>ì¯K>¡cK>cK>3ÎJ>…J>ÿüõI> °I>(kI>W'I>™äH>í¢H>UbH>Ð"H>`äG>§G>ÀjG>‘/G>yõF>x¼F>„F>ÀMF> F>mãE>ë¯E>„}E>9LE> E>ùìD>¿D>/’D>wfD>ß;D>hD>êC>ÚÂC>ÆœC>ÔwC>TC>Z1C>ÓC>qïB>4ÐB>²B>-•B>dyB>Ã^B>KEB>ü,B>ÖB>ÚÿA> ëA>e×A>ìÄA> ³A>£A>”A>͆A>:zA>ÖnA>£dA> [A>ÐSA>1MA>ÅGA>ŒCA>ˆ@A>¸>A>>A>1|?ô%|?°|?T|?Þ|?Oø{?§ì{?æà{? Õ{?É{? ½{?à°{?ž¤{?B˜{?Ì‹{?;{?r{?Ëe{?êX{?ïK{?Ù>{?¨1{?\${?õ{?r {?Óûz?îz?Càz?RÒz?DÄz?¶z?Ô§z?r™z?óŠz?X|z?Ÿmz?Ê^z?ØOz?É@z?1z?S"z?ìz?gz?Äóy?äy?&Ôy?)Äy?´y?Ö£y?~“y?ƒy?try?Àay?îPy?ü?y?ë.y?»y?l y?üúx?néx?¿×x?ðÅx?´x?ò¡x?Ãx?s}x?kx?rXx?ÀEx?í2x?ùx?ã x?­ùw?Uæw?ÛÒw?@¿w?‚«w?£—w?¡ƒw?~ow?8[w?ÏFw?D2w?–w?Åw?Òóv?»Þv?Év?#´v?¢žv?ýˆv?5sv?H]v?8Gv?1v?«v?.v?Œíu?ÆÖu?Ú¿u?ʨu?•‘u?;zu?¼bu?Ku?L3u?\u?Fu? ët?©Òt?!ºt?r¡t?žˆt?¢ot?€Vt?8=t?È#t?2 t?tðs?Ös?‚¼s?N¢s?ó‡s?oms?ÄRs?ñ7s?õs?Ñs?…ær?Ër?s¯r?­“r?¿wr?§[r?f?r?ü"r?hr?«éq?ÅÌq?´¯q?z’q?uq?ˆWq?Ð9q?íq?àýp?©ßp?GÁp?º¢p?„p?ep?Fp?Ø&p?tp?äço?)Èo?B¨o?/ˆo?ñgo?†Go?ï&o?,o?=ån?!Än?Ù¢n?dn?Â_n?ô=n?øn?Ðùm?z×m?÷´m?G’m?iom?^Lm?$)m?¾m?)âl?f¾l?ušl?Vvl? Rl?-l?ãl? äk?¿k?Ì™k?gtk?ÓNk?)k?k?ýÜj?¬¶j?,j?|ij?Bj?j?Pôi?âÌi?C¥i?u}i?wUi?H-i?éi?ZÜh?›³h?«Šh?Šah?98h?·h?åg?!»g? ‘g?Çfg?P,^?ð÷]?pÃ]?½Ž]?ÖY]?¼$]?oï\?ï¹\?<„\?VN\?=\?ðá[?p«[?¾t[?Ø=[?¿[?sÏZ?ô—Z?B`Z?](Z?DðY?ù·Y?{Y?ÊFY?ç Y?ÐÔX?†›X? bX?[(X?yîW?e´W?zW?¤?W?÷W?ÊV?V?ÃSV?MV?¥ÜU?Ê U?½dU?~(U? ìT?i¯T?”rT?5T?TøS?éºS?M}S??S?S?NÃR?ë„R?WFR?’R?›ÈQ?t‰Q?JQ?’ Q?ØÊP?íŠP?ÒJP?† P? ÊO?\‰O?HO?rO?5ÆN?È„N?+CN?^N?b¿M?6}M?Û:M?QøL?—µL?¯rL?˜/L?RìK?ݨK?:eK?i!K?iÝJ?<™J?àTJ?WJ? ËI?»†I?©AI?jüH?þ¶H?eqH? +H?­åG?ŸG?DYG?ÍG?)ÌF?[…F?`>F?:÷E?é¯E?mhE?Æ E?ôØD?øD?ÑHD?D?¸C?aoC?“&C?›ÝB?z”B?0KB?½B?"¸A?^nA?r$A?^Ú@?"@?¿E@?4û??‚°??©e??©??ƒÏ>?6„>?Ä8>?+í=?m¡=?‰U=? =?S½;?Gñ:?¤:?ÑV:?b :?л9?n9?F 9?OÒ8?6„8?û58? ç7?$™7?‡J7?Êû6?î¬6?ò]6?Ö6?›¿5?Bp5?Ê 5?3Ñ4?4?­14?¾á3?±‘3?ˆA3?Bñ2?ß 2?aP2?Çÿ1?¯1?B^1?V 1?Q¼0?1k0?÷0?£È/?6w/?±%/?Ô.?[‚.?Œ0.?¦Þ-?§Œ-?’:-?fè,?#–,?ÊC,?\ñ+?מ+?>L+?ù*?̦*?õS*? *? ®)?øZ)?Ó)?›´(?Qa(?õ (?‡º'?g'?x'?׿&?&l&?e&?”Ä%?´p%?Å%?ÇÈ$?»t$?¢ $?zÌ#?Ex#?$#?¶Ï"?[{"?õ&"?ƒÒ!?~!?)!?ìÔ ?P€ ?ª+ ?úÖ?B‚?€-?·Ø?åƒ? /?+Ú?C…?U0?`Û?f†?f1?`Ü?V‡?H2?5Ý?ˆ?3?èÝ?Ɉ?§3?ƒÞ?^‰?74?ß?è‰?¿4?—ß?pŠ?I5?$à?‹?Þ5?¿à?¢‹?ˆ6?rá?_Œ?Q7?Gâ?B?B8?Gã?SŽ?d9?}ä?œ?Â:?ñå?'‘?e<?­ç?ý’?W>?»é ?(• ?¡@ ?$ì ?²— ?LC ?ñî ?£š ?bF ?-ò ?ž ?ìI ?àõ ?â¡ ?óM ?ú?C¦?‚R?Ðþ?0«? W? ?³°?V]? ?Ô¶?¯c??ž½?²j?Û?Å?ir?Ð?LÍ?Ýz?„(?AÖ?„?2?à?Ž?L<?*Õÿ>í1ÿ>áŽþ>ìý>aIý>í¦ü>­ü>¢bû>ËÀú>*ú>À}ù>ŒÜø>;ø>Ëš÷>?úö>ìYö>Ó¹õ>ôõ>Pzô>èÚó>»;ó>Ëœò>þñ>¢_ñ>kÁð>s#ð>º…ï>@èî>Kî>®í>Xí>ãtì>±Øë>Á<ë>¡ê>­ê>Šjé>¬Ïè>5è>Àšç>³ç>îfæ>pÍå>:4å>M›ä>¨ä>Mjã>;Òâ>t:â>ø¢á>Ç á>âtà>IÞß>üGß>ý±Þ>KÞ>ç†Ý>ÑñÜ> ]Ü>’ÈÛ>j4Û>’ Ú> Ú>ÔyÙ>îæØ>ZTØ>Â×>)0×>ŒžÖ>C Ö>M|Õ>¬ëÔ>^[Ô>fËÓ>Â;Ó>t¬Ò>|Ò>ÙŽÑ>ŽÑ>™rÐ>ûäÏ>µWÏ>ÇÊÎ>1>Î>ô±Í>&Í>„šÌ>RÌ>z„Ë>üùÊ>ØoÊ>æÉ>¡\É>ŽÓÈ>×JÈ>|ÂÇ>}:Ç>Ú²Æ>”+Æ>«¤Å>Å>ð—Ä> Ä>­ŒÃ>˜Ã>â‚Â>‹þÁ>’zÁ>ùöÀ>¿sÀ>åð¿>jn¿>Pì¾>–j¾><é½>Ch½>«ç¼>tg¼>žç»>*h»>éº>fjº>ì¹>+n¹> ð¸>xs¸>³ö·>Qz·>Qþ¶>µ‚¶>|¶>¦Œµ>4µ>&˜´>{´>5¥³>R,³>Ô³²>º;²>ı>´L±>ÇÕ°>@_°>é¯>_s¯>þ®>‰®>…®>\ ­>˜,­>:¹¬>AF¬>®Ó«>a«>¹ïª>W~ª>[ ª>Åœ©>•,©>˼¨>gM¨>iÞ§>Ño§>Ÿ§>Ó“¦>n&¦>n¹¥>ÕL¥>¢à¤>Öt¤>p ¤>ož£>Ö3£>¢É¢>Õ_¢>nö¡>n¡>Ó$¡>Ÿ¼ >ÑT >iíŸ>h†Ÿ>ÌŸ>—¹ž>ÈSž>_î>\‰>¿$>‡Àœ>¶\œ>Kù›>E–›>¥3›>kÑš>–oš>'š>­™>yL™>:ì˜>aŒ˜>ì,˜>ÝÍ—>3o—>î—>³–>’U–>{ø•>É›•>{?•>’ã”> ˆ”>ì,”>/Ò“>×w“>â“>QÄ’>$k’>Z’>ô¹‘>ña‘>Q ‘>³>:\>Ã>¯¯>ýY>®>Á¯Ž>6[Ž> Ž>F³>à_>Ý >:ºŒ>ùgŒ>Œ>šÄ‹>|s‹>¾"‹>aÒŠ>d‚Š>È2Š>‹ã‰>®”‰>1F‰>øˆ>Uªˆ>ö\ˆ>öˆ>UÇ>w‡>.+‡>¨ß†>€”†>µI†>Iÿ…>:µ…>‰k…>4"…>=Ù„>¢„>dH„>‚„>ý¸ƒ>Óqƒ>+ƒ>“ä‚>|ž‚>ÁX‚>`‚>ZÎ>¯‰>]E>g>ʽ€>†z€>œ7€>ê>©e>ìá~>à^~>…Ü}>ÚZ}>àÙ|>•Y|>ùÙ{> [{>ÌÜz>;_z>Wây>fy>”êx>µox>‚õw>ù{w>w>èŠv>]v>|œu>D&u>´°t>Ë;t>ŠÇs>ðSs>üàr>®nr>ýq>Œq>¢q>ç«p>Ï

ZÎo>‡`o>Wón>Ȇn>Ún>¯m>àDm>ÒÚl>dql>”l>b k>Î8k>×Ñj>|kj>¾j>› i>'Øh>Ôth>h>û¯g>sNg>„íf>,f>l-f>BÎe>®oe>°e>G´d>sWd>3ûc>†Ÿc>mDc>æéb>ñb>Ž6b>¼Ýa>{…a>É-a>§Ö`>€`>*`>šÔ_>²_>V+_>‡×^>D„^>Œ1^>`ß]>½]>¥<]>ì\>œ\>”L\>žý[>1¯[>Ja[>é[>ÇZ>ºzZ>ê.Z>žãY>Ö˜Y>‘NY>ÐY>»X>ÓrX>—*X>ÜâW>¡›W>æTW>«W>îÈV>°ƒV>ï>V>¬úU>æ¶U>œsU>Î0U>{îT>£¬T>EkT>a*T>÷éS>ªS>ŒjS>‹+S>íR>î®R>QqR>*4R>y÷Q><»Q>tQ>DQ>? Q>ÑÎP>Õ”P>K[P>3"P>ŒéO>V±O>yO>8BO>P O>ÖÔN>ËžN>-iN>ü3N>8ÿM>àÊM>ó–M>rcM>[0M>¯ýL>mËL>“™L>#hL>7L>{L>BÖK>p¦K>wK>ÿGK>_K>$ëJ>N½J>ÜJ>ÍbJ>"6J>0çJ?ÖæJ?æJ?ëäJ?[ãJ?dáJ?ßJ??ÜJ?ÙJ?~ÕJ?‚ÑJ?ÍJ?UÈJ?#ÃJ?‹½J?‹·J?$±J?UªJ? £J?ƒ›J?“J?‹J?A‚J?yJ?foJ?]eJ?îZJ?PJ?ÙDJ?49J?'-J?´ J?ÙJ?—J?íøI?ÝêI?eÜI?†ÍI?@¾I?’®I?}žI?ŽI?}I?ÔkI?#ZI? HI?Š5I?£"I?TI?ŸûH?‚çH?þÒH?¾H?À¨H?“H?æ|H?^fH?oOH?8H?[ H?6H?ªïG?·ÖG?]½G?›£G?r‰G?ânG?ëSG?8G?ÈG?›G?äF? ÇF?ª©F?á‹F?°mF?OF?0F?³F?æðE?²ÐE?°E?E?ªmE?ÙKE? )E?E?ûãD?ÀD?¸œD?|xD?ÙSD?Î.D?] D?„ãC?E½C?ž–C?oC?HC?> C?ú÷B?PÏB?>¦B?Å|B?åRB?ž(B?ïýA?ÚÒA?]§A?y{A?.OA?|"A?cõ@?ãÇ@?û™@?­k@?÷<@?Ú @?VÞ??k®??~??`M?????¸ê>?ɸ>?s†>?·S>?’ >?iL?ðK?awK?2ÿJ?p‡J?J?3™I?³"I?›¬H?é6H?œÁG?²LG?*ØF?dF?9ðE?Ì|E?» E?—D?¦$D?Ÿ²C?î@C?‘ÏB?‡^B?ÏíA?g}A?N A?‚@?.@?Ͼ??äO??Aá>?år>?Ï>?þ–=?o)=?#¼RHþ>ûaý>ùzü>H“û>èªú>ÕÁù> Øø>Ží÷>T÷>]ö>¨)õ>0<ô>ôMó>ñ^ò>$oñ>Š~ð>"ï>çšî>اí>ñ³ì>1¿ë>“Éê>Óé>´Ûè>nãç>?êæ>$ðå>õä> ùã>1üâ>Iþá>gÿà>‡ÿß>¥þÞ>¿üÝ>ÑùÜ>ÙõÛ>ÒðÚ>¹êÙ>ŒãØ>FÛ×>äÑÖ>cÇÕ>¿»Ô>ô®Ó>¡Ò>Ý‘Ñ>ŠÐ>pÏ>?]Î>AIÍ>4Ì>Ë>³Ê>›ìÈ>3ÒÇ>v¶Æ>`™Å>îzÄ>[Ã>â9Â>?Á>/ó¿>¬Í¾>²¦½>=~¼>HT»>Î(º>Êû¸>8Í·>¶>nHÔ>:®Ó>lÓ>{Ò>âÑ>oIÑ>=±Ð>sÐ>‚Ï>ëÎ>}TÎ>N¾Í>†(Í>%“Ì>+þË>˜iË>lÕÊ>¦AÊ>H®É>QÉ>ÀˆÈ>—öÇ>ÔdÇ>xÓÆ>„BÆ>ö±Å>Ï!Å>’Ä>¶Ä>ÄsÃ>9åÂ>WÂ>XÉÁ><Á>¯À>Š"À>i–¿>® ¿>[¾>nô½>éi½>Êß¼>V¼>ÂÌ»>ØC»>U»º>93º>…«¹>7$¹>P¸>и>··> ·>º…¶>Ö¶>Y|µ>Cø´>”t´>Lñ³>jn³>ðë²>Ýi²>1è±>ìf±> æ°>–e°>†å¯>Ýe¯>šæ®>¿g®>Ké­>>k­>—í¬>Xp¬>€ó«>w«>ûª>aª>$ª>O‰©>á©>Ù”¨>9¨>¢§>.)§>°¦>¾8¦>!Á¥>ëI¥>Ó¤>³\¤>²æ£>q£>åû¢>‡¢>´¢>µž¡>+¡>î· >%E >ÃÒŸ>É`Ÿ>5ïž>~ž>B ž>ãœ>ë,>[½œ>1Nœ>nß›>q›>›>•š>j(š>ª»™>RO™>aã˜>Öw˜>³ ˜>÷¡—>¢7—>´Í–>-d–> û•>T’•>*•>”>“Z”>jó“>›Œ“>3&“>3À’>šZ’>iõ‘>Ÿ‘>=,‘>BÈ>¯d>ƒ>¾ž>a<>lÚŽ>ÞxŽ>·Ž>ø¶> V>°öŒ>'—Œ>8Œ>KÙ‹>ùz‹> ‹>Š¿Š>nbŠ>¹Š>k©‰>…M‰>òˆ>ð–ˆ>@<ˆ>øá‡>ˆ‡>ž.‡>ŒÕ†>á|†>ž$†>ÂÌ…>Nu…>A…>œÇ„>^q„>‡„>ƃ>qƒ>pƒ>7È‚>et‚>û ‚>øÍ>]{>))>]×€>ø…€>ú4€>ÇÈ>i(>Úˆ~>ê}>(L}>¯|>²|>-w{>vÜz>Bz>v©y>,y>±yx>ãw>'Mw>¸v>×#v>fu>Ãýt>ïkt>êÚs>´Js>L»r>³,r>éžq>îq>Á…p>cúo>Ôoo>æn>"]n>ÿÔm>ªMm>%Çl>nAl>†¼k>l8k>!µj>¥2j>ø°i>0i> °h>È0h>V²g>²4g>Ý·f>Ö;f>ŸÀe>6Fe>›Ìd>ÏSd>ÒÛc>¤dc>Dîb>³xb>ñb>ýa>Øa>‚ª`>ú8`>AÈ_>WX_>;é^>îz^>p ^>À ]>ß4]>ÌÉ\>ˆ_\>ö[>m[>•%[>GZ]>DÞ\> c\>Wê[>hr[>ÏûZ>ІZ>–Z>ðŸY>–.Y>†¾X>»OX>5âW>ñuW>í W>%¡V>˜8V>CÑU>$kU>9U>€¢T>÷?T>›ÞS>j~S>cS>ƒÁR>ÈdR>0 R>º®Q>dUQ>+ýP>¦P> PP> ûO>K§O>ŒTO>ßO>D²N>¸bN>;N>ÊÆM>dzM>/M>³äL>e›L>SL>Ö L>“ÅK>P€K> ÈøJ>¶J>2uJ>ß4J>…õI>#·I>·yI>A=I>¾I>/ÇH>’H>åTH>(H>YæG>x°G>„{G>{GG>]G>)âF>ݰF>y€F>ûPF>d"F>²ôE>äÇE>ù›E>ñpE>ËFE>†E>!õD>œÍD>õ¦D>-D>B\D>48D>D>«òC>.ÑC>°C>ÄC>ÕqC>¾SC>6C>C>‡þB>ÍãB>éÉB>Ú°B> ˜B>:B>©jB>ëTB>@B>ê+B>¥B>3B>’ôA>ÃãA>ÅÓA>™ÄA>=¶A>±¨A>ö›A> A>ð„A>¤zA>(qA>{hA>`A>ŽYA>MSA>ÜMA>9IA>eEA>_BA>'@A>¾>A>$>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>>A>ä¹9?¹9?Ò¸9?¯·9?%¶9?6´9?á±9?&¯9?¬9?ƒ¨9?›¤9?N 9?ž›9?Š–9?‘9?:‹9?ý„9?_~9?^w9?üo9?8h9?`9?ŽW9?¨N9?bE9?½;9?·19?S'9?9?m9?í9?ú8?Òí8?8á8?BÔ8?îÆ8?>¹8?2«8?Êœ8?Ž8?è~8?no8?™_8?jO8?á>8?þ-8?Â8?- 8?>ù7?øæ7?YÔ7?bÁ7?®7?mš7?p†7?r7?r]7?rH7?37?p7?o7?ñ6?oÚ6?pÃ6?¬6?w”6?}|6?1d6?‘K6?Ÿ26?[6?Äÿ5?Ýå5?£Ë5?±5??–5?{5?™_5?ÎC5?³'5?J 5?’î4?‹Ñ4?6´4?“–4?¢x4?dZ4?Ù;4?4?Ýý3?mÞ3?°¾3?©ž3?V~3?¸]3?Ð<3?3? ú2?ZØ2?J¶2?ñ“2?Pq2?eN2?3+2?¹2?÷ã1?î¿1?ž›1?w1?*R1?-1?ž1?ðá0?ü»0?Ä•0?Go0?†H0?!0?8ú/?¬Ò/?ݪ/?Ë‚/?vZ/?à1/? /?îß.?“¶.?÷Œ.?c.?ý8.?¡.?ä-?(¹-? Ž-?³b-?7-?D -?0ß,?Þ²,?N†,?‚Y,?y,,?3ÿ+?²Ñ+?õ£+?üu+?ÈG+?Y+?°ê*?Ì»*?¯Œ*?X]*?Ç-*?ýý)?ûÍ)?À)?Mm)?¢<)?À )?¦Ú(?V©(?Ïw(?F(?(?ôá'?–¯'?}'?9J'?<'? ä&?¥°&? }&?@I&?A&?á%?«¬%?x%?LC%?S%?(Ù$?Ì£$?@n$?ƒ8$?–$?zÌ#?.–#?³_#? )#?2ò"?,»"?øƒ"?–L"?"?KÝ!?c¥!?Nm!? 5!? ü ?Ä ?E‹ ?WR ?> ?ûß?ަ?øl?83?Où?=¿?…?¡J??fÕ?š?_?g$?é?¨­?r?R6?oú?h¾?;‚?ëE?w ?ßÌ?$?FS?E?"Ù?Ý›?v^?í ?Cã?y¥?Žg?‚)?Wë? ­?¡n?0?oñ?©²?Äs?Á4? õ?c¶?w?‘7?þ÷?N¸?ƒx?œ8?šø?}¸?Fx?ô7?‰÷?·?fv?®5?Þô?õ³?õr?Ü1?¬ð?e¯?n?’,?ë?f©?¯g?ã%?ä? ¢?`?â?¯Û ?i™ ?W ?¢ ?#Ò ?‘ ?îL ?8 ?qÇ ?™„ ?°A ?¶þ ?¬» ?“x ?i5 ?0ò ?è® ?‘k ?,( ?¹ä?8¡?©]? ?dÖ?®’?ìN? ?DÇ?_ƒ?o??tû?n·?^s?D/?!ë?ô¦?¾b??8Ú?é•?’Q?4 ?ÎÈ?b„?î??uû?õ¶?pr?å-?Vé?Á¤?(`?‹?Ó­ÿ>Š$ÿ>:›þ>äþ>ˆˆý>(ÿü>Äuü>\ìû>ñbû>„Ùú>Pú>¥Æù>4=ù>óø>T*ø>å ÷>x÷>Žö>§ö>C{õ>äñô>‰hô>4ßó>æUó>žÌò>]Cò>$ºñ>ó0ñ>̧ð>¯ð>œ•ï>“ ï>—ƒî>¦úí>Âqí>ìèì>#`ì>i×ë>¾Në>#Æê>˜=ê>µé>¶,é>`¤è>è>í“ç>Ñ ç>ʃæ>Øûå>üså>6ìä>‡dä>ðÜã>qUã> Îâ>¾Fâ>‹¿á>s8á>v±à>•*à>Уß>(ß>ž–Þ>3Þ>æ‰Ý>¹Ý>«}Ü>¿÷Û>óqÛ>JìÚ>ÃfÚ>_áÙ>\Ù>ר> RØ>;Í×>H×> ÄÖ>­?Ö>x»Õ>k7Õ>‡³Ô>Í/Ô>=¬Ó>Ù(Ó>Ÿ¥Ò>’"Ò>²ŸÑ>ÿÑ>zšÐ>$Ð>ý•Ï>Ï>>’Î>¨Î>DÍ>Í>Ì>F Ì>®‹Ë>J Ë>‹Ê># Ê>a‹É>Ö É>ƒŒÈ>h È>†ŽÇ>ÝÇ>n‘Æ>:Æ>A•Å>„Å>šÄ>ÀÄ>»ŸÃ>ó"Ã>k¦Â>"*Â>®Á>P2Á>ɶÀ>„;À>‚À¿>ÂE¿>G˾>Q¾>×½>p]½> ä¼>éj¼>ò»>€y»>8»>9‰º>„º>š¹>ù"¹>%¬¸>5¸>b¿·>tI·>ÔÓ¶>ƒ^¶>éµ>Ïtµ>mµ>]Œ´>ž´>1¥³>2³>P¿²>ÞL²>ÀÚ±>øh±>…÷°>i†°>¤°>7¥¯>!5¯>eÅ®>V®>úæ­>Lx­>ú ­>œ¬>i.¬>,Á«>MT«>Ìçª>ª{ª>èª>†¤©>…9©>åΨ>§d¨>Ëú§>S‘§>>(§>Ž¿¦>CW¦>^ï¥>߇¥>µ ¥>⹤>vS¤>pí£>Ò‡£>š"£>ɽ¢>^Y¢>Yõ¡>¹‘¡>€.¡>«Ë >2 >¥Ÿ>LDŸ>oãž>÷‚ž>ã"ž>2Ã>åc>û>u¦œ>QHœ>ê›>2›>60›>œÓš>dwš>Žš>À™>e™>T ™>°˜>V˜>ü—>Q£—>‚J—>ò–>š–>PB–>ÿê•> ”•>y=•>Dç”>m‘”>õ;”>Ûæ“>’“>À=“>¿é’>–’>ÔB’>éï‘>\‘>+K‘>Vù>ݧ>ÀV>þ>˜µ>e>Ý>ˆÆŽ>ŽwŽ>í(Ž>§Ú>»Œ>)?>ññŒ>¥Œ>‹XŒ>^ Œ>ŠÀ‹>u‹>ê)‹>ߊ>«”Š>JŠ>ËŠ>_·‰>In‰>Š%‰>"݈>•ˆ>UMˆ>ðˆ>ᾇ>(x‡>Ä1‡>µë†>û¥†>–`†>††>ËÖ…>d’…>PN…>‘ …>&Ç„> „„>HA„>×þƒ>¸¼ƒ>ëzƒ>q9ƒ>Jø‚>t·‚>ñv‚>¾6‚>Þö>N·>x>"9>…ú€>9¼€>=~€>@€>4€>NŒ>Ó>÷™~>¸!~>ª}>3}>­¼|>âF|>´Ñ{>!]{>)éz>Ìuz> z>ày>Qy>[®x>þ=x>9Îw> _w>vðv>w‚v>v>>¨u>\Ðt>Ket>Îús>æs>’'s>Ѿr>£Vr>ïq>ÿ‡q>ˆ!q>¢»p>NVp>Šño>Vo>²)o>Æn>dn>!n>¸ m>Ý?m>ßl>Îl>š l>òÁk>Öck>Ek>?©j>ÃLj>Òði>j•i>Œ:i>7àh>j†h>%-h>hÔg>2|g>ƒ$g>[Íf>¸vf>œ f>Ëe>òue>d!e>ZÍd>Óyd>Ð&d>PÔc>R‚c>Ö0c>Üßb>cb>k?b>óïa>ü a>ƒRa>Ša>·`>j`>—`>—Ñ_>†_> ;_>ƒð^>u¦^>ã\^>Ì^>/Ë]> ƒ]>e;]>6ô\>€­\>Cg\>!\>2Ü[>]—[>ÿR[>[>§ËZ>«ˆZ>&FZ>Z>yÂY>RY>ž@Y>]Y>ÀX>5X>MBX>ÖX>ÑÅW>=ˆW>KW>gW>#ÒV>O–V>êZV>ôV>låU>Q«U>¤qU>d8U>‘ÿT>*ÇT>.T>žWT>y T>¿éS>n³S>ˆ}S> HS>öS>JÞR>ªR>+vR>¶BR>©R>ÝQ>ÀªQ>åxQ>oGQ>^Q>²åP>iµP>„…P>VP>ä&P>(øO>ÎÉO>Ö›O>?nO>AO>3O>½çN>§»N>ðN>˜dN>ž9N>N>ÅäM>äºM>a‘M>9hM>n?M>þM>êîL>0ÇL>ÑŸL>ËxL>RL>Í+L>ÓL>2àK>èºK>÷•K>\qK>MK>*)K>“K>QâJ>d¿J>ÌœJ>ˆzJ>˜XJ>ü6J>³J>¼ôI>ÔI>ƳI>Æ“I>tI>·TI>©5I>êI>{øH>[ÚH>‰¼H>ŸH>ÑH>êdH>OHH>,H>ÿH>JôG>ߨG>À½G>ë¢G>aˆG> nG>)TG>{:G>!G>ùG>#ïF>•ÖF>N¾F>N¦F>”ŽF> wF>ñ_F>IF>b2F>F>äF> ðE>sÚE>ÅE> °E><›E>­†E>_rE>R^E>…JE>÷6E>©#E>šE>ÉýD>6ëD>âØD>ÊÆD>ð´D>R£D>ð‘D>Ê€D>ßoD>/_D>ºND>~>D>}.D>µD>%D>ÎÿC>°ðC>ÈáC>ÓC> ÄC>]¶C>Q¨C>zšC>ÙŒC>lC>4rC>0eC>`XC>ÂKC>X?C> 3C>'C>FC>£C>1C>ïøB>ÞíB>üâB>IØB>ÅÍB>oÃB>G¹B>M¯B>€¥B>à›B>m’B>%‰B> €B>wB>RnB>¶eB>D]B>üTB>ÝLB>çDB>=B>s5B>ô-B>&B>mB>bB>~B>¿ B>&B>±ýA>`÷A>4ñA>*ëA>DåA>ßA>àÙA>aÔA>ÏA>ÇÉA>«ÄA>¯¿A>ÔºA>¶A>z±A>ü¬A>œ¨A>Z¤A>5 A>-œA>B˜A>s”A>ÀA>(A>¬‰A>J†A>ƒA>ÔA>À|A>ÅyA>âvA>tA>eqA>ÉnA>ElA>×iA>€gA>>eA>cA>ú`A>ø^A> ]A>.[A>gYA>²WA>VA>TA>SA>–QA>:PA>ïNA>´MA>‰LA>mKA>`JA>bIA>rHA>GA>ºFA>òEA>6EA>†DA>âCA>JCA>¼BA>9BA>¿AA>PAA>ê@A>Œ@A>7@A>ë?A>¦?A>h?A>1?A>?A>Ö>A>±>A>‘>A>w>A>`>A>N>A>@>A>4>A>,>A>&>A>!>A>>A>>A>>A>>A>ùn~?Þm~?Âk~?¦h~?Œd~?s_~?^Y~?LR~?@J~?9A~?97~?A,~?Q ~?j~?Ž~?¾ö}?ùæ}?BÖ}?˜Ä}?þ±}?sž}?ù‰}?‘t}?;^}?ùF}?Ì.}?´}?²û|?Çà|?ôÄ|?:¨|?›Š|?l|?¬L|?`,|?1 |? é{?/Æ{?_¢{?¯}{?"X{?¸1{?q {?Pâz?T¹z?z?Òdz?M9z?ò z?Áßy?»±y?â‚y?5Sy?·"y?gñx?H¿x?YŒx?œXx?$x?ºîw?—¸w?ªw?òIw?rw?*Øv?žv?Fcv?«'v?Lëu?*®u?Epu?Ÿ1u?8òt?²t?,qt?ˆ/t?(ís? ªs?3fs?¡!s?UÜr?Q–r?•Or?#r?û¿q?wq?-q?Iãp?S˜p?¬Lp?Tp?M³o?˜eo?5o?%Èn?jxn?(n?óÖm?:…m?Ø2m?Ðßl?!Œl?Í7l?Ôâk?8k?ù6k?àj?–ˆj?u0j?´×i?U~i?Y$i?ÁÉh?nh?¿h?W¶g?VYg?¾ûf?f?Ê>f?pße?e?e?ì½d?F\d?úc?K—c?÷3c?Ðb?¦kb?«b?%¡a?;a?{Ô`?Ym`?°`?€_?Ê4_?Ë^?Òa^?‘÷]?ÎŒ]?Š!]?ŵ\?‚I\?¿Ü[?€o[?Ä[?Œ“Z?Ù$Z?¬µY?FY?éÕX?UeX?JôW?É‚W?ÔW?lžV?‘+V?D¸U?‡DU?YÐT?½[T?²æS?:qS?VûR?…R?MR?)—Q?Q?©§P?N/P?¶O?g=O?ÝÃN?ïIN?ŸÏM?îTM?ÜÙL?k^L?›âK?mfK?âéJ?ûlJ?¹ïI?rI?'ôH?ÙuH?4÷G?7xG?æøF??yF?DùE?÷xE?WøD?gwD?%öC?•tC?¶òB?ŠpB?îA?LkA?<è@?âd@??á??T]??"Ù>?ªT>?ìÏ=?éJ=?£ÅË}þ>ôdý>bLü>4û>ú>gù>í÷>ìÕö>(¿õ>·¨ô>š’ó>Õ|ò>ggñ>URð>ž=ï>E)î>Mí>µì>îê>³Ûé>KÉè>L·ç>¸¥æ>”å>Öƒä>Œsã>´câ>PTá>aEà>ê6ß>ì(Þ>hÝ>aÜ>ÙÛ>ÑõÙ>JêØ>Hß×>ÌÔÖ>×ÊÕ>kÁÔ>‹¸Ó>7°Ò>s¨Ñ>>¡Ð>šÏ>”Î>Í>7ŠÌ>ñ…Ë>F‚Ê>8É>Ê|È>üzÇ>ÑyÆ>JyÅ>iyÄ>1zÃ>¢{Â>¿}Á>Š€À>„¿>/ˆ¾> ½>Ÿ’¼>阻>ꟺ>¦§¹>°¸>S¹·>Hö>þ͵>xÙ´>·å³>¼ò²>в>#±>‡°>º.¯>½?®>’Q­>9d¬>·w«> Œª>8¡©>@·¨>%Χ>èå¦>‹þ¥>¥>y2¤>ÈM£>þi¢>‡¡>(¥ >ÄŸ>äž>Ûž>¤&>`Iœ>m›>¾‘š>b·™>Þ˜>ž˜>9.—>ÖW–>u‚•>®”>ÂÚ“>t“>/7’>÷f‘>Ì—>°É>¥üŽ>®0Ž>Ëe>þ›Œ>JÓ‹>± ‹>3EŠ>Ó‰>“»ˆ>uø‡>y6‡>£u†>ôµ…>m÷„>:„>â}ƒ>à‚> ‚>pP>™€>›Å>[~>ô|>ûŽ{>^,z>?Ìx> nw>†v>óºt>íds>vr>‘Àp>Dro>&n>{Ýl>—k>8Sj>i>˜Óg>Ï—f>¹^e>[(d>·ôb>ÓÃa>°•`>Tj_>ÁA^>û]>ù[>æØZ>ž»Y>1¡X>¥‰W>ûtV>8cU>_TT>uHS>|?R>x9Q>n6P>`6O>R9N>H?M>FHL>OTK>gcJ>’uI>ÓŠH>-£G>¥¾F>?ÝE>ýþD>ã#D>öKC>8wB>®¥A>'×@>r @>X??>Úu>>ö­=>ªç<>ö"<>Ù_;>Pž:>[Þ9>ù9>(c8>ç§7>5î6>66>y5>lÊ4>é4>ïd3>|´2>2>)X1>E¬0>ä0>Y/>¥±.>Å .>bg->{Ä,>#,>ƒ+>¦ä*>¥G*>¬)>)>by(>3â'>uL'>'¸&>I%&>Ø“%>Ó%>:u$> è#>D\#>åÑ">ìH">XÁ!>);!>\¶ >ð2 >å°>90>ê°>ø2>a¶>%;>AÁ>µH>Ñ>Ÿ[>ç>Ùs>ñ>Y‘>">´>gG>Ü>ëq> >”¡>R;>VÖ>žr>)>õ®>O>Nð>Ø’>Ÿ6>¢Û>Þ>T)>Ò>æ{>ÿ&>MÓ>΀>/>dß>w>·B>%ö>¾ª>‚`>o>ƒÏ >¿ˆ > C >¦þ >O» >y >8 >ø >9¹ >{ >á> >^ >ôÈ >¢ >gW >B >1ê >4µ >I >nN >¤ >çë>8¼>•>ý_>n3>è>iÝ>ð³>{‹> d>›=>.>Àó>PÐ>ß­>iŒ>îk>mL>å->T>¹ó>Ø>a½>¡£>ÒŠ>ôr>\>F>í0>Â>‚ >*÷>ºå>0Õ>‹Å>ʶ>ì¨>ð›>Ó>–„>7z>´p> h>?`>KY>.S>éM>xI>ÜE>C>A>ó?>›?>@>UA>cC>ßI>IN>zS>qY>,`>ªg>ëo>ìx>¬‚>+>g˜>_¤>±>~¾>¢Ì>~Û>ë>Uû>N >ú>V0>cC>W>†k>š€>Z–>ì>ÕÃ>ŽÛ>íó>ñ >™&>ã@>Ï[>[w>…“>M°>²Í>²ë>K >~)>HI>©i>ŸŠ>)¬>EÎ>óð>2>ÿ7>[\>C>·¦>µÌ><ó>K >áA >üi >›’ >¾» >bå >‡ >+: >Ne >î > ½ >Ÿé >® >6D >5r >©  >’Ï >îþ >¼. >û^ >ª >ÈÀ >Rò >I$ >«V >w‰ >ª¼ >Fð >G$>­X>v>£Â>0ø>.>jd>›>Ò>{ >6A>Jy>¶±>xê>#>ú\>¸–>ÇÐ>' >ÖE>Ó€>¼>±÷>3>¸o>)¬>ßè>Ü%>c>  >eÞ>k>°Z>4™>õ×>ñ>(V>™•>AÕ>!>6U>€•>ýÕ>¬>W>˜>ÛÙ>G>ß\>¢ž>Žà>£">àd>B§>Éé>t,>Ao>0²>?õ>l8>·{>¿>¡>>F>ó‰>ÀÍ>¤>œU>©™>ÈÝ>ù!>:f>‹ª>éî>T3 >Ëw >K¼ >Õ!>gE!>Š!>Î!>@">åW">Œœ">4á">Û%#>j#>#¯#>Âó#>Z8$>í|$>wÁ$>ù%>pJ%>ÜŽ%>;Ó%>&>Ï[&> &>"ä&>0('>*l'>°'>Ýó'>•7(>3{(>¸¾(>!)>nE)>žˆ)>¯Ë)>Ÿ*>oQ*>”*>¦Ö*> +>J[+>b+>Qß+>!,>±b,> ¤,>bå,>u&->Xg-> ¨->‹è->Ù(.>ñh.>Ô¨.>€è.>ô'/>/g/>/¦/>óä/>z#0>Äa0>ÍŸ0>—Ý0>1>bX1>b•1>Ò1>‘2>¾J2>¡†2>:Â2>‡ý2>ˆ83>;s3>Ÿ­3>²ç3>t!4>ãZ4>ÿ“4>ÅÌ4>45>M=5> u5>q¬5>|ã5>)6>zP6>k†6>ü»6>,ñ6>ú%7>dZ7>iŽ7>Â7>?õ7>(8>tZ8>nŒ8>ü½8>ï8>Ð9>P9>ä9>C¯9>/Þ9>¦ :>¨::>2h:>D•:>ÝÁ:>ûí:>;>ÂD;>ho;>™;>6Ã;>Zì;>û<>=<>¯d<>¿‹<>H²<>GØ<>»ý<>¤"=>G=>Îj=> Ž=>»°=>ØÒ=>aô=>V>>¶5>>€U>>±t>>J“>>H±>>«Î>>që>>™?>##?> >?>SX?>øq?>øŠ?>T£?> »?>Ò?>|è?>6þ?>F@>©'@>^;@>eN@>»`@>`r@>Sƒ@>’“@>£@>ï±@> À@>oÍ@>Ú@>æ@>;ñ@>°û@>fA>]A>’A>A>´$A>Ÿ*A>Ä/A>!4A>¶7A>:A>‚¶=A>>A>ô‡g?"‡g?ƒ…g?ƒg?ßg?Ü{g?wg?vqg?kg?ícg?ü[g?DSg?ÆIg?‚?g?z4g?­(g?g?Êg?µg?ßñf?Hâf?òÑf?ÜÀf?¯f?vœf?'‰f?uf?U`f?ÓJf?—4f?¡f?óf?íe?oÔe?›ºe? e?Ñ„e?Ýhe?5Le?Ú.e?Íe?òd?žÒd?~²d?®‘d?0pd?Nd?)+d?¢d?oãc?‘¾c?™c?Örc?úKc?v$c?Jüb?wÓb?þ©b?ßb?Ub?³)b?¨ýa?ùÐa?©£a?·ua?%Ga?òa?!è`?±·`?£†`?øT`?°"`?Íï_?O¼_?6ˆ_?„S_?9_?Vè^?Û±^?Êz^?#C^?æ ^?Ò]?¯˜]?·^]?,$]?é\?a­\?#q\?U4\?øö[? ¹[?”z[?;[?ýûZ?à»Z?8{Z?:Z?KøY?¶Y?;sY?è/Y?ìX?¯§X?ËbX?bX?v×W?‘W?JW?¢W?®ºV?:rV?G)V?ÔßU?ä•U?wKU?ŒU?&µT?EiT?éT?ÐS?Å‚S?þ4S?ÀæR? ˜R?ÞHR?=ùQ?'©Q?œXQ?žQ?.¶P?KdP?÷P?3¿O?ÿkO?[O?IÄN?ÉoN?ÜN?ƒÅM?¾oM?ŽM?óÂL?ðkL?ƒL?®¼K?rdK?Ï K?ƲJ?WYJ?„ÿI?N¥I?³JI?·ïH?X”H?™8H?yÜG?ùG?#G?ÞÅF?ChF?L F?ø«E?IME??îD?ÛŽD?/D?ÏC?šnC?Õ C?º¬B?HKB?‚éA?g‡A?ø$A?6Â@?"_@?¼û??˜??ÿ3??©Ï>?k>?>?Р=?C;=?iÕl:?d:?Dœ9?ß39?5Ë8?Fb8?ù7?Ÿ7?è%7?ï»6?¶Q6?=ç5?„|5?5?X¦4?å:4?7Ï3?Lc3?&÷2?ÆŠ2?,2?Y±1?ND1? ×0?’i0?âû/?ý/?ã/?•±.?C.?_Ô-?ye-?bö,?‡,?¢,?ü§+?&8+?#È*?óW*?—ç)?w)?\)?~•(?x$(?H³'?ðA'?qÐ&?Ì^&?í%?{%?ù%?¿–$?b$$?ã±#?B?#?€Ì"?Y"?›æ!?zs!?:!?ÝŒ ?c ?Í¥?2?O¾?iJ?iÖ?Pb? î?Øy?z?‘?}?ß§?.3?i¾?’I?ªÔ?±_?§ê?Žu?f?0‹?í? ?A+?Ùµ?g@?ëÊ?fU?Øß?Cj?¦ô??[ ?­“?û?F¨?Ž2?Ó¼?G?ZÑ ?ž[ ?áå ?'p ?nú ?¸„ ? ?W™ ?­# ? ® ?k8 ?ÕÂ?FM?¿×?Ab?Íì?dw??³Œ?m?5¢? -?î·?âB?åÍ?úX? ä?Xo?£ú?†?u?ú9ÿ>5Qþ>hý>4€ü>ú—û>ò¯ú>Èù>yàø> ù÷>Õ÷>×*ö>Dõ>ˆ]ô>;wó>+‘ò>Z«ñ>ÊÅð>|àï>qûî>«î>+2í>òMì>jë>]†ê>£é>ø¿è>:Ýç>Íúæ>±æ>ç6å>rUä>Stã>Š“â>³á>Óà>Ióß>ëß>ê4Þ>JVÝ> xÜ>,šÛ>²¼Ú>žßÙ>ïÙ>©&Ø>ÌJ×>ZoÖ>T”Õ>»¹Ô>‘ßÓ>ØÓ>,Ò>¼SÑ>\{Ð>r£Ï>ÌÎ>õÍ>‡Í>ƒHÌ>ýrË>ôÊ>lÉÉ>eõÈ>à!È>àNÇ>e|Æ>qªÅ>ÙÄ>#Ä>Ì7Ã>hÂ>ŘÁ>ÊÀ>ûû¿>q.¿>za¾>•½>Mɼ>þ»>3»>iº> ¹>T׸>-¸>¦G·>À€¶>}ºµ>ßô´>ç/´>–k³>î§²>ðä±>"±>÷`°> ¯>¸ß®>" ®>>a­>£¬>”å«>Ð(«>Älª>r±©>Üö¨>=¨>僧>‡Ë¦>ë¦>]¥>ù¦¤>§ñ£>=£>W‰¢>\Ö¡>,$¡>Çr >0Ÿ>hŸ>pcž>Iµ>ö>w[œ>ί›>ü›>[š>ã±™>  ™>9b˜>±»—>—>@q–>[Í•>Z*•>?ˆ”> ç“>½F“>Z§’>â’>Wk‘>¹Î> 3>M˜>þŽ>©eŽ>ÆÍ>Ù6>ä Œ>é Œ>çw‹>âäŠ>ÚRŠ>ÑÁ‰>È1‰>Á¢ˆ>½ˆ>¾‡‡>Äû†>Òp†>èæ…> ^…>5Ö„>nO„>¶Éƒ> Eƒ>vÁ‚>ñ>‚>€½>%=>ཀ>´?€>D…>V~>¡—}>(¤|>í²{>ôÃz>@×y>Óìx>°x>Úw>T;v> Zu>B{t>¼žs>’Är>Åìq>Yq>QDp>¯so>:¥n>$Øm>g m>Bl>ðxk>4±j>Ìêi>·%i>õah>ƒŸg>bÞf>f> `e>Õ¢d>ëæc>L,c>ørb>íºa>*a>°N`>{š_>ç^>ã5^>}…]>ZÖ\>x(\>Ø{[>wÐZ>V&Z>r}Y>ÌÕX>a/X>2ŠW>=æV>‚CV>þ¡U>²U>œbT>¼ÄS>(S>˜ŒR>SòQ>?YQ>\ÁP>¨*P>#•O>ÍO>£mN>¤ÛM>ÑJM>(»L>¨,L>PŸK>K>ˆJ>/þI>nuI>ÑíH>UgH>üáG>Â]G>©ÚF>­XF>Ð×E>XE>jÙD>à[D>oßC>dC>ØéB>°pB>øA> A>¶ A>à–@>#@>j°?>È>?>5Î>>°^>>9ð=>Ï‚=>p=>«<>Ò@<>×;>Vo;>#;>ö¡:>Î<:>ªØ9>‰u9>j9>L²8>.R8>ó7>ð”7>Í77>§Û6>|€6>K&6>Í5>×t5>5>@Ç4>çq4>4>Ê3>’w3>&3>kÕ2>¿…2>72>6é1>Uœ1>aP1>X1>:»0>r0>¸)0>Sâ/>Õ›/>=V/>‰/>¹Í.>ËŠ.>ÀH.>–.>KÇ->à‡->SI->£ ->ÏÎ,>×’,>¸W,>t,>ä+>r«+>´s+>Ë<+>·+>wÑ*> *>ni*>£6*>¨*>|Ó)>£)>s)>ÈD)>Ï)> é(>:½(>œ‘(>Æf(>¶<(>l(>çê'>%Ã'>&œ'>èu'>lP'>¯+'>±'>qä&>ïÁ&>( &>&>Ë^&>3?&>T &>+&>¹ä%>ýÇ%>õ«%>¡%>ÿu%>\%>ÐB%>A*%>a%>.û$>©ä$>ÏÎ$>¡¹$>¥$>B‘$>~$>…k$> Y$>`H$>Å7$>Î'$>z$>Ç $>´û#>Bî#>ná#>9Õ#> É#>£¾#>B´#>{ª#>L¡#>¶˜#>¸#>P‰#>}‚#>?|#>”v#>|q#>õl#>ÿh#>še#>Âb#>y`#>½^#>]#>è\#>Í\#>;]#>1^#>¯_#>³a#>=d#>Kg#>Üj#>ðn#>†s#>x#>3~#>H„#>ÛŠ#>ë‘#>x™#>¡#>ª#>ú²#>m¼#>WÆ#>·Ð#>Û#>׿#>•ò#>Æþ#>h $>{$>ý%$>ï3$>NB$>Q$>S`$>öo$>€$>z$>Y¡$> ²$>LÄ$>^Ö$>Õè$>¯û$>ì%>Š"%>Š6%>èJ%>¦_%>Ât%>;Š%> %>?¶%>ÉÌ%>¬ã%>èú%>{&>d*&>¢B&>5[&>t&>U&>à¦&>¼À&>èÚ&>bõ&>+'>@+'>¡F'>Nb'>D~'>„š'> ·'>ÜÓ'>òð'>M(>í+(>ÑI(>÷g(>^†(>¥(>ðÃ(>ã(>|)>")>ýA)>b)>j‚)>÷¢)>¼Ã)>¸ä)>ë*>T'*>ñH*>Áj*>ÅŒ*>ú®*>`Ñ*>õó*>º+>¬9+>Ì\+>€+>Ž£+>/Ç+>ùê+>ì,>3,>FW,>¬{,>7 ,>åÄ,>¶é,>¨->¼3->ïX->A~->±£->?É->èî->¬.>‹:.>ƒ`.>“†.>»¬.>ùÒ.>Mù.>µ/>1F/>Àl/>a“/>º/>Óà/>£0>.0>mU0>d|0>g£0>sÊ0>‰ñ0>¨1>Î?1>úf1>,Ž1>cµ1>Ü1>Ú2>+2>XR2>˜y2>Ö 2>È2>Lï2>‚3>³=3>Þd3>Œ3> ³3>4Ú3>?4>?(4>4O4>v4>øœ4>ÆÃ4>„ê4>25>Ï75>Z^5>Ò„5>7«5>†Ñ5>À÷5>ã6>ïC6>âi6>»6>zµ6>Û6>¦7>&7>\K7>‰p7>–•7>‚º7>Kß7>ò8>v(8>ÔL8> q8>•8> ¹8>ËÜ8>c9>Ñ#9>G9>*j9>9>ί9>ZÒ9>µô9>à:>Ù8:>ŸZ:>1|:>Ž:>µ¾:>¦ß:>`;>à ;>'A;>4a;>;>› ;>ó¿;> ß;>çý;><>Û:<>òX<>Çv<>X”<>¤±<>ªÎ<>ië<>á=>$=>÷?=>’[=>âv=>æ‘=>¬=>Ç=>á=>çú=>_>>…->>XF>>×^>>w>>ÖŽ>>T¦>>{½>>IÔ>>½ê>>×?>•?>÷+?>ü@?>¢U?>éi?>Ñ}?>W‘?>{¤?><·?>™É?>’Û?>$í?>Pþ?>@>p@>b/@>ê>@>N@>µ\@>øj@>Ìx@>1†@>%“@>©Ÿ@>º«@>Y·@>ƒÂ@>9Í@>y×@>Bá@>“ê@>ló@>Ëû@>°A> A>A>tA>eA>×#A>È(A>8-A>&1A>‘4A>x7A>Ú9A>¶;A> =A>Ø=A>>A>Ö]~?rH~?V2~?~?õ~?²ë}?¸Ò}? ¹}?¥ž}?ƒ}?Àg}?AK}?.}?*}?”ñ|?NÒ|?W²|?±‘|?\p|?XN|?§+|?H|?=ä{?†¿{?$š{?t{?`M{?ÿ%{?õýz?CÕz?ê«z?éz?BWz?õ+z?z?lÓy?1¦y?Sxy?ÒIy?¯y?êêx?„ºx?}‰x?×Wx?’%x?®òw?,¿w? ‹w?QVw?ù w?ëv?w´v?N}v?ŒEv?0 v?<Ôu?°šu?Œ`u?Ò%u?‚êt?®t?"rt?5t?q÷s?<¹s?tzs?;s?0ûr?´ºr?©yr?8r?äõq?,³q?çoq?,q?µçp?Ë¢p?U]p?Up?ÊÐo?·‰o?Bo?öùn?J±n?hn?^n?Ôm?Z‰m?>m?Eòl?ö¥l?#Yl?Ï l?ù½k?¢ok?Ë k?tÑj?žj?J1j?wài?(i?\=i?ëh?P˜h?Eh?Yñg?'g?|Hg?Yóf?½f?«Gf?"ñe?#še?®Be?Åêd?g’d?–9d?Ràc?œ†c?t,c?ÛÑb?Ñvb?Xb?o¿a?ca?Qa?©`?}K`?qí_?øŽ_?0_?ÇÐ^?q^?î^?d°]?rO]?î\?YŒ\?2*\?¦Ç[?µd[?_[?¥Z?‰9Z? ÕY?(pY?å Y?A¥X?=?X?ÙØW?rW?õ W?v£V?š;V?aÓU?ÌjU?ÜU?‘˜T?ì.T?íÄS?•ZS?åïR?Ý„R?~R?È­Q?½AQ?[ÕP?¦hP?›ûO?>ŽO? O?вN?6DN?ÕM?™fM?S÷L?½‡L?ÙL?¦§K?&7K?YÆJ?@UJ?ÛãI?*rI?0I?ëH?]H?†¨G?g5G?ÂF?TNF?`ÚE?'fE?©ñD?æ|D?ßD?•’C?C?:§B?)1B?غA?GDA?vÍ@?eV@?ß??Šg??Áï>?ºw>?xÿ=?ú†=?A=?N•ø>þ>ö8ý>3ü>T-û>·'ú>?"ù>ìø>Á÷>½ö>â õ>2 ô>­ó>Uò>+üð>1øï>gôî>Îðí>híì>7êë>:çê>täé>æáè>‘ßç>uÝæ>•Ûå>òÙä>ŒØã>e×â>~Öá>ØÕà>uÕß>VÕÞ>{ÕÝ>çÕÜ>šÖÛ>•×Ú>ÚØÙ>jÚØ>GÜ×>pÞÖ>èàÕ>°ãÔ>ÉæÓ>4êÒ>óíÑ>òÐ>nöÏ>.ûÎ>FÎ>·Í>ƒ Ì>«Ë>/Ê>É>U&È>ø-Ç>ý5Æ>d>Å>1GÄ>bPÃ>úYÂ>úcÁ>cnÀ>7y¿>v„¾>!½>;œ¼>è»>¼µº>&ù>Ѹ>Tß·>î¶>Wýµ> µ>8´>Þ-³>?²>žP±>ºb°>Uu¯>oˆ®> œ­>)°¬>ËÄ«>ñÙª>žï©>Ñ©>ލ>Ó3§>¤K¦>d¥>ë|¤>c–£>k°¢>Ë¡>/æ >î >AŸ>);ž>©X>Àvœ>q•›>½´š>¤Ô™>(õ˜>J˜> 8—>nZ–>r}•>¡”>eÅ“>Uê’>í’>,6‘>]>¦„>䬎>ÏÕ>gÿŒ>®)Œ>¦T‹>O€Š>«¬‰>»Ùˆ>€ˆ>û5‡>.e†>•…>¿Å„> ÷ƒ>=)ƒ>\‚>±> Ä€>Jò>^~>EË|>:{>hªy>Mx>Äv>Îu>n{s>¦óq>xmp>èèn>÷em>¨äk>ýdj>ùæh>žjg>ïïe>îvd>ÿb>ÿ‰a>`>å£^>n3]>³Ä[>·WZ>}ìX>ƒW>WV>oµT>RQS>ïQ>„ŽP>×/O>ÿÒM>þwL>×K>ŒÇI>rH>“G>êÌE>(}D>M/C>]ãA>Þ@>Î@>á`?><¤>>àè=>Ì.=>ýu<>u¾;>1;>2S:>vŸ9>ýì8>Å;8>΋7>Ý6>Ÿ/6>fƒ5>jØ4>«.4>(†3>àÞ2>Ò82>þ“1>cð0>ÿM0>Ó¬/>Ü />n.>Ð->64->™,>ÿ+>[f+>ÊÎ*>h8*>5£)>0)>X|(>­ê'>-Z'>ØÊ&>­<&>«¯%>Ò#%> ™$>”$>/‡#>ïÿ">Óy">Úô!>q!>Pî >½l >Jì>÷l>Âî>«q>°õ>Òz>>gˆ>Ø>bš>%>¾°>Ž=>sË>mZ>{ê>œ{>Ð >¡>k5>ÑÊ>Fa>Éø>Y‘>÷*> Å>Ta>þ>Ú›>ª:>‚Ú>a{>F>0À>d> >¯>ÿU>øý>ñ¦>êP>áû>×§>ÊT>¹>¤± >Ša >i >BÄ >w >Ü* >›ß >P• >úK >™ >+¼ >¯u >&0 >ë >å§ >,e >b# >…â>•¢>’c>z%>Lè>¬>­p>:6>®ü> Ä>JŒ>oU>y>eê>4¶>å‚>vP>è>9î>h¾>t>]a>#4>Ã>>Ü>’±>¿‡>Ä^> 6>R>Ùè>6Ã>fž>iz>>W>å4>\>£ò>ºÒ>ž³>P•>Îw>[>-?> $>´ >%ð>^×>]¿>#¨>®‘>ý{>g>æR>}?>×,>ð>É >Âòÿ=nÓÿ=•µÿ=4™ÿ=J~ÿ=Ödÿ=ÕLÿ=G6ÿ=*!ÿ={ ÿ=;ûþ=fêþ=ûÚþ=ùÌþ=]Àþ=(µþ=V«þ=æ¢þ=Ö›þ=&–þ=Ó‘þ=ÛŽþ=>þ=ùŒþ= Žþ=rþ=,”þ=9™þ=–Ÿþ=B§þ=;°þ=ºþ=Æþ=äÒþ=áþ=dðþ= ÿ=òÿ=&ÿ=:ÿ=%Pÿ=gÿ=ÿ=m˜ÿ=õ²ÿ=²Îÿ=¡ëÿ=á>Š>Ê$>¡5> G>Y>£k>Ë~>†’>Ò¦>¯»>Ñ>ç>£ý>»>`,>D>L]>“v>b>»ª>›Å>á>ñü>d>\6>ØS>Øq>Y>\¯>ßÎ>ãî>e>f0>äQ>ßs>U–>G¹>³Ü>˜>ö$>ÌI>o>Û”>»>Áá>á>u0>{X>ò€>Ú©>1Ó>÷ü>,'>ÎQ>Ü|>V¨><Ô>‹>C->dZ>í‡>ݵ>2ä>í> B>q>v¡>¾Ñ>g >q3 >Úd >¢– >ÉÈ >Lû >,. >ha >þ” >ïÈ >8ý >Ú1 >Ôf >$œ >ËÑ >Æ >> >ºt >°« >øâ >’ >{R >µŠ >=à >ü >65>¥n>`¨>fâ>µ>NW>/’>XÍ>Ç>|D>v€>µ¼>7ù>û5>s>I°>Ñí>˜+>ži>â§>cæ> %>d>L£>¸â>^">R¢>žâ> #>×c>¤>áå>2'>µh>hª>Lì>`.>¢p>³>®õ>w8>k{>‰¾>Ò>CE>܈>Ì>„>‘T>Ø>Ý>’!>-f>ëª>Éï>Ç4>åy>!¿>{>ñI>„>1Õ>ú>Û`>Ö¦>éì>3>Sy>©¿> >“L >%“ >ÉÙ > !>Fg!>®!>õ!>ö;">ø‚">Ê">!#>GX#>wŸ#>°æ#>ò-$>=u$>޼$>å%>CK%>¤’%> Ú%>s!&>Þh&>J°&>¸÷&>%?'>‘†'>ûÍ'>c(>È\(>(¤(>„ë(>Ú2)>)z)>pÁ)>°*>çO*>—*>6Þ*>L%+>Wl+>U³+>Dú+>&A,>÷‡,>¹Î,>i->\->”¢-> é->q/.>Àu.>ù».>/>'H/>Ž/>ôÓ/>´0>Y_0>â¤0>Pê0> /1>Òt1>æ¹1>Úþ1>®C2>`ˆ2>ñÌ2>_3>©U3>Ï™3>ÏÝ3>ª!4>^e4>ê¨4>Nì4>‰/5>šr5>µ5>:ø5>Ç:6>(}6>[¿6>^7>2C7>Ö„7>HÆ7>ˆ8>•H8>o‰8>Ê8>„ 9>½J9>ÀŠ9>‹Ê9> :>wI:>–ˆ:>zÇ:>";>D;>¼‚;>¬À;>]þ;>Î;<>ÿx<>îµ<>›ò<>/=>,k=> §=>ªâ=>ÿ>>Y>>Õ“>>TÎ>>ˆ?>sB?>|?>fµ?>mî?>&'@>‘_@>­—@>xÏ@>ôA>>A> o~?ìk~?Ãf~?§_~?šV~? K~?º>~?ì/~?6~?œ ~?!ø}?Åá}?É}?{¯}?“}?Ïu}?;V}?Ö4}?¢}?£ì|?ÚÅ|?I|?ór|?ÜF|?|?né{?¸{?…{?TP{?à{?»áz?æ§z?elz?9/z?fðy?í¯y?Ñmy?*y?ºäx?Ãx?3Ux? x?Q¿w?rw?&#w?¼Òv?Æ€v?H-v?DØu?¼u?´)u?,Ðt?(ut?ªt?´ºs?I[s?lúr?˜r?b4r?;Ïq?«hq?´q?Y—p?œ,p?€Ào?So?3än?tn?…n?°m?‰m?¦l?T/l?J·k?ù=k?bÃj?‰Gj?qÊi?Li?‰Ìh?¾Kh?¾Ég?‰Fg?#Âf?Ž?6¦=?|èLþ>ž°ü>‚û>Åzù>kà÷>yFö>ó¬ô>àó>C{ñ>!ãï>Kî>c´ì>Ðë>̇é>\òç>„]æ>IÉä>°5ã>¾¢á>xà>â~Þ>îÜ>Ü]Û>tÎÙ>Ñ?Ø>ö±Ö>é$Õ>®˜Ó>J Ò>ÂÐ>ùÎ>ZpÍ>„èË>œaÊ>©ÛÈ>¯VÇ>²ÒÅ>¸OÄ>ÆÍÂ>àLÁ> Í¿>LN¾>¨Ð¼>$T»>ÄØ¹>^¸>„å¶>¯mµ>÷³>°²> ±>¶š¯>()®>긬>J«>pÜ©>?p¨>p§> œ¥>4¤>‰Í¢>yh¡>ã >΢ž>>B>8ã›>À…š>Ý)™>‘Ï—>ãv–>ו>rÊ“>¹v’>°$‘>]Ô>Ä…Ž>é8>Óí‹>†¤Š>]‰>Yˆ>‚Ó†>ˆ‘…>oQ„><ƒ>óÖ>™œ€>gÈ~>[|>²òy>Ûw>-u>cÐr>Õwp>t#n>IÓk>]‡i>º?g>jüd>v½b>é‚`>ËL^>&\>îY>pÅW>q¡U>‚S>^gQ>]QO>@M>›3K>î+I>)G>++E>(2C>>A>rr?>s«=>é;>h+:>Sr8>Ù½6>ô 5> b3>Ú»1>œ0>â{.>§â,>èM+>Ÿ½)>É1(>`ª&>`'%>Ũ#>‹.">­¸ >%G>ñÙ> q>p >¬>P>-ø>¤>!U>ä >ÒÂ>æ>A>p>ÜÏ >^ >ïn >ŒD >0>Öû>{Ý>Ã>®¬>2š>¤‹>ý€>sôþ=ªîü=–ðú=/úø=j ÷=@$õ=¨Dó=˜lñ=œï=îÒí=Bì=ûVê=¤è=wøæ=)Tå=·ã=G!â=¢’à=# ß=ÁŠÝ=tÜ=2ŸÚ=ô3Ù=¯Ï×=[rÖ=ïÕ=aÌÓ=ªƒÒ=ÁAÑ=›Ð=1ÒÎ=y¤Í=k}Ì=ý\Ë=(CÊ=à/É=#È=ÛÇ= Æ=¥#Å=¢0Ä=øCÃ=ž]Â=Œ}Á=¸£À=п=¨¿=[;¾='z½=¿¼=î ¼=ÖZ»=µ±º=‚º=5q¹=ÄÙ¸=&H¸=S¼·=A6·=èµ¶=?;¶==Ƶ=ÙVµ= í´=ƈ´=*´=Àг=ì|³=€.³=så²=½¡²=Uc²=1*²=Iö±=”DZ= ž±=Ÿy±=NZ±= @±=Ï*±=±=F±=ç±=j±=È ±=ö±=ì±=¡1±= H±=$c±=Ⴑ=8§±="б=–ý±=‰/²=õe²=Ï ²=à²=«#³=œk³=Ø·³=U´= ]´=óµ´=µ=.tµ=pÙµ=¿B¶=°¶=^!·=–·=Ÿ=ÌŒ¸=« ¹=X’¹=ʺ=ø¦º=Ú6»=fÊ»=“a¼=Yü¼=¯š½=Œ<¾=æá¾=µŠ¿=ñ6À=æÀ=ˆ™Á=ÑOÂ=d Ã=5ÆÃ==†Ä=sIÅ=ÍÆ=CÙÆ=Ë¥Ç=^uÈ=ñGÉ=}Ê=øõÊ=YÑË=—¯Ì=©Í=‡tÎ=([Ï=‚DÐ=0Ñ=?Ò=‘Ó=xÔ=ìúÔ=åóÕ=XïÖ=>í×=íØ==ðÙ=DõÚ=šüÛ=5Ý= Þ= ß=P0à=¨Bá=Wâ=œmã=%†ä=¬ å=)½æ=’Ûç=ßûè=ê=Bë=Ágì=Cí=|¸î=bãï=îñ=>ò=Ñmó=Ÿô=ÞÑõ=÷=Ì;ø=ârù=U«ú=åû=2 ý=‰\þ=šÿ=nl>c >è¬>ùM>ï>¨‘>?4>P×>Õz>Ë>,Ã>õg>! >¬²>‘X >Ìþ >W¥ >0L >Qó >µš >YB >8ê >N’>•:> ã>¨‹>k4>NÝ>L†>b/>ŠØ>Á>+>HÔ>}>Ó&>Ð>=y>[">dË>St>$>ÒÅ>Yn>´>ß¾>Õf>“>¶>Q] >H!>õª!>RQ">[÷"> #>aB$>Uç$>â‹%>0&>»Ó&>ýv'>È(>¼(>å])>.ÿ)>îŸ*> @+>Àß+>Ê~,>8->»->1X.>³ô.>ˆ/>¬+0>Æ0>Í_1>Áø1>ò2>\(3>ù¾3>ÅT4>½é4>Û}5>6>x£6>î47>yÅ7>U8>ºã8>hq9>þ9>lj:>p;>ž;>&<>®<>{4=>¹=>è=>>èÀ>>¿B?>gÃ?>ÜB@>Á@>>A>ºŠA>ÖA>!B>ækB>iµB>)þB>$FC>]C>ÓÓC>ˆD>|^D>±¢D>&æD>Ý(E>ÖjE>¬E>’ìE>V,F>`kF>°©F>FçF>$$G>K`G>º›G>sÖG>vH>ÅIH>`‚H>GºH>|ñH>ÿ'I>Ñ]I>ó’I>eÇI>(ûI>=.J>¥`J>`’J>oÃJ>ÔóJ>Ž#K>žRK>K>Ä®K>ÜÛK>ML>4L>?_L>À‰L>ž³L>ÙÜL>rM>i-M>ÀTM>w{M>Ž¡M>ÇM>âëM>!N>Ã3N>ÉVN>5yN>›N>?¼N>ßÜN>çüN>XO>3;O>xYO>)wO>E”O>ΰO>ÅÌO>)èO>ýP>@P>ó6P>PP>®hP>¸€P>4˜P>%¯P>ŠÅP>eÛP>·ðP>Q>¿Q>x-Q>ª@Q>VSQ>}eQ> wQ>?ˆQ>Ú˜Q>ô¨Q>Œ¸Q>£ÇQ>;ÖQ>SäQ>ìñQ>ÿQ>§ R>ÊR>p#R>.R>O9R>‡CR>HMR>VR>a_R>¼gR>¢oR>wR>~R>—„R>®ŠR>RR>†•R>IšR>œžR>€¢R>÷¥R>ÿ¨R>œ«R>Ì­R>‘¯R>ë°R>ܱR>d²R>„²R><²R>ޱR>y°R>ÿ®R>!­R>ߪR>:¨R>3¥R>Ê¡R>žR>×™R>N•R>fR>!‹R>~…R>€R>%yR>prR>akR>øcR>7\R>TR>®KR>çBR>Ë9R>Z0R>•&R>|R>R>TR>EüQ>çðQ>8åQ>;ÙQ>ïÌQ>VÀQ>p³Q>>¦Q>Á˜Q>úŠQ>è|Q>ŽnQ>ë_Q>QQ>ÑAQ>Z2Q>ž"Q>Q>XQ>ÑñP>áP>üÏP>°¾P>#­P>X›P>M‰P>wP>€dP>¿QP>Á>P>‰+P>P>lP>‡ðO>kÜO>ÈO>޳O>ÏžO>Û‰O>²tO>V_O>ÈIO>4O>O>óO>¡ñN> ÛN>qÄN>”­N>‹–N>UN>ôgN>iPN>´8N>Ö N>ÏN>¡ðM>MØM>Ò¿M>2§M>mŽM>„uM>y\M>KCM>û)M>‹M>úöL>JÝL>|ÃL>©L>†L>`uL>[L>Á@L>K&L>º L>ñK>QÖK>y»K>Š K>†…K>mjK>?OK>ÿ3K>«K>EýJ>ÎáJ>GÆJ>°ªJ> J>UsJ>“WJ>Å;J>êJ>J>èI>ÌI>°I> ”I>öwI>Û[I>»?I>•#I>kI><ëH> ÏH>ײH>¢–H>kzH>5^H>ÿAH>Ê%H>˜ H>híG><ÑG>µG>ñ˜G>Ô|G>¾`G>¯DG>¨(G>© G>´ðF>ÊÔF>ê¸F>F>OF>”eF>èIF>K.F>¼F>?÷E>ÒÛE>vÀE>.¥E>ø‰E>ÖnE>ÉSE>Ñ8E>ðE>%E>rèD>×ÍD>V³D>î˜D>¡~D>odD>YJD>a0D>…D>ÈüC>+ãC>¬ÉC>O°C>—C>ù}C>eC>-LC>~3C>óC>ŽC>PêB>9ÒB>IºB>ƒ¢B>æŠB>ssB>+\B>EB> .B>]B>ÉB>cêA>,ÔA>&¾A>Q¨A>­’A><}A>ýgA>óRA>>A>á{R?<{R? zR?PxR? vR?7sR?ÛoR?ókR?€gR?‚bR?ù\R?äVR?EPR?IR?fAR?&9R?[0R?'R?%R?¹R?ÃR?BüQ?7ðQ?¡ãQ?€ÖQ?ÕÈQ?ŸºQ?ß«Q?”œQ?¿ŒQ?`|Q?wkQ?ZQ?HQ?5Q?m"Q?ÒQ?­úP?ÿåP?ÇÐP?»P?»¤P?çP?‰vP?£^P?4FP?<-P?»P?±ùO?ßO?ÄO?b¨O?7ŒO?„oO?JRO?‡4O?=O?k÷N?ØN?2¸N?Ë—N?ÝvN?hUN?m3N?ëN?ãíM?UÊM?A¦M?§M?ˆ\M?ä6M?ºM? êL?ØÂL?!›L?ärL?$JL?à L?÷K?ÍÌK?þ¡K?¬vK?ØJK?K?§ñJ?LÄJ?o–J?hJ?09J?Ï J?íÙI?Š©I?¨xI?EGI?cI?ãH? °H?Á|H?ãHH?‡H?­ßG?VªG?tG?/>G?aG?ÐF?Q˜F?`F?R'F?îE?i´E?=zE?—?E?xE?ßÈD?ÏŒD?FPD?FD?ÎÕC?ß—C?zYC?žC?MÛB?‡›B?L[B?œB?yÙA?â—A?ØUA?\A?mÐ@? @??*©>?Pb>?>?SÓ=?3‹=?§B=?±ù3†þ>üÀý>üü>l7ü>sû> ¯ú>Qëù>è'ù>Ðdø> ¢÷>œßö>„ö>Â[õ>[šô>MÙó>œó>HXò>S˜ñ>¾Øð>Šð>¹Zï>Kœî>DÞí>¢ í>icì>™¦ë>3êê>8.ê>«ré>‹·è>Úüç>šBç>ˈæ>oÏå>‡å>^ä>¦ã>îâ>7â>ì€á>ÑÊà>1à> `ß>g«Þ>?÷Ý>–CÝ>nÜ>ÆÝÛ>¡+Û>ÿyÚ>àÈÙ>FÙ>2hØ>¤¸×>ž ×> [Ö>+­Õ>ÀÿÔ>ßRÔ>‰¦Ó>ÀúÒ>ƒOÒ>Õ¤Ñ>´úÐ>"QÐ> ¨Ï>¯ÿÎ>ÎWÎ>°Í>à Í>™cÌ>¾Ë>Ë>”tÊ>»ÐÉ>y-É>ÍŠÈ>¸èÇ>:GÇ>T¦Æ>Æ>RfÅ>7ÇÄ>µ(Ä>ÍŠÃ>€íÂ>ÎPÂ>·´Á><Á>]~À>ä¿>uJ¿>l±¾>¾>4½>ê¼>sS¼>€½»>,(»>w“º>bÿ¹>ìk¹>Ù¸>ßF¸>Hµ·>R$·>ü“¶>F¶>2uµ>¾æ´>ëX´>¸Ë³>'?³>8³²>é'²><±>0±>ʼn°>ü°>Õx¯>Nñ®>jj®>&ä­>„^­>„Ù¬>$U¬>fÑ«>IN«>Î˪>óIª>ºÈ©>!H©>)Ȩ>ÒH¨>ʧ>L§>ŽÎ¦>¸Q¦>‚Õ¥>ìY¥>õÞ¤>žd¤>æê£>Íq£>Sù¢>x¢>: ¢>œ“¡>›¡>7¨ >r3 >I¿Ÿ>½KŸ>ÎØž>|fž>Åô>ªƒ>+>G£œ>þ3œ>OÅ›>;W›>Áéš>à|š>™š>ꤙ>Ô9™>VϘ>qe˜>"ü—>k“—>J+—>ÀÖ>Ì\–>nö•>¤•>p+•>ÐÆ”>Ãb”>Kÿ“>eœ“>:“>RØ’>$w’>‡’>{¶‘>ÿV‘>ø>¸™>ì;>®Þ>ÿ>Þ%>JÊŽ>CoŽ>ÈŽ>Ùº>va>ž>P°Œ>pXŒ>ýŒ>ª‹>´S‹>ÝýŠ>¨Š>ÈSŠ>‰ÿ‰>Ñ«‰> X‰>ô‰>γˆ>,bˆ>ˆ>vÀ‡>`p‡>Í ‡>¼Ñ†>-ƒ†>5†>‘ç…>„š…>öM…>ç…>W¶„>Ek„>° „>˜Öƒ>ýŒƒ>ÝCƒ>9û‚>³‚>`k‚>*$‚>mÝ>)—>]Q> >+Ç€>€>Ò>€>¬ö>p>wë~>8g~>ßã}>la}>Ýß|>0_|>fß{>}`{>sâz>Hez>úèy>‰my>ôòx>9yx>Wx>Mˆw>w>¾šv>6%v>‚°u>¡’Ét>SWt>äås>Cus>ps>j–r>.(r>½ºq>Nq>6âp>wp>Ë p>=£o>t:o>mÒn>(kn>¤n>àžm>Û9m>“Õl>rl>8l>$­k>ÈKk>&ëj>;‹j>,j>‡Íi>½oi>¦i>B¶h>Zh>Œÿg>9¥g>•Kg>žòf>Tšf>µBf>Áëe>v•e>Ô?e>Ùêd>…–d>×Bd>Îïc>hc>¥Kc>„úb>ªb>$Zb>ã b>@¼a>:na>Ð a>Ô`>͇`>2<`>0ñ_>Ŧ_>ñ\_>²_> Ë^>ó‚^>p;^>ô]> ®]>Qh]>#]>`Þ\><š\>¥V\>š\>Ñ[>%[>¸M[>Ô [>xÌZ>¢ŒZ>RMZ>‡Z>AÐY>}’Y>}Y>?ÜX>€ X>AeX>€*X>=ðW>v¶W>+}W>\DW> W>*ÔV>ÇœV>ÜeV>h/V>jùU>âÃU>ÎŽU>.ZU>&U>HòT>¿T>)ŒT>ÂYT>Ê'T>AöS>%ÅS>w”S>6dS>`4S>õS>ôÕR>]§R>.yR>hKR>R>ñQ>}ÄQ>O˜Q>†lQ>!AQ>Q>~ëP>@ÁP>c—P>æmP>ÉDP> P>ªóO>¨ËO>¤O>¹|O>ËUO>8/O> O>!ãN>›½N>m˜N>—sN>ON>ð*N>N> ãM>wÀM>¢M> {M>ñXM>7M>ˆM>MôL>cÓL>DzL>{’L>~rL>ÎRL>k3L>UL>ŒõK> ×K>Ú¸K>ñšK>Q}K>û_K>íBK>(&K>ª K>ríJ>‚ÑJ>×µJ>qšJ>PJ>tdJ>ÚIJ>…/J>qJ> ûI>âI>ÁÈI>³¯I>å–I>V~I>fI>õMI>!6I>‹I>2I>ðH>4ÙH>ŽÂH>$¬H>ô•H>ýH>AjH>½TH>r?H>_*H>ƒH>ßH>qìG>9ØG>8ÄG>k°G>ÓœG>p‰G>@vG>DcG>{PG>å=G>€+G>NG>MG>|õF>ÜãF>lÒF>+ÁF>°F>8ŸF>ƒŽF>ý}F>¤mF>x]F>yMF>¦=F>.F>„F>4F>F>ñE>BâE>šÓE>ÅE>ƶE>˜¨E>“šE>µŒE>ÿ~E>pqE>dE>ÄVE>¨IE>±Þ/E>1#E>©E>D E>þD>æñD>ëåD>ÚD>^ÎD>ËÂD>Z·D> ¬D>Û D>Í•D>àŠD>€D>euD>×jD>h`D>VD>çKD>ÓAD>Þ7D>.D>M$D>°D>0D>ÍD>†þC>ZõC>KìC>VãC>}ÚC>¿ÑC>ÉC>’ÀC>"¸C>̯C>§C>mŸC>c—C>rC>™‡C>ØC>/xC>žpC>%iC>ÂaC>vZC>BSC>#LC>EC>)>C>L7C>…0C>Ô)C>7#C>¯C><C>ÝC>“ C>\C>9ýB>*÷B>-ñB>DëB>nåB>«ßB>ùÙB>[ÔB>ÎÎB>SÉB>êÃB>’¾B>K¹B>´B>ñ®B>Ý©B>Ú¤B>çŸB>›B>1–B>m‘B>ºŒB>ˆB>€ƒB>ú~B>ƒzB>vB>ÁqB>vmB>8iB> eB>è`B>Ô\B>ÎXB>ÖTB>êPB> MB>;IB>vEB>¾AB>>B>s:B>á6B>Z3B>ß/B>o,B> )B>´%B>g"B>%B>ïB>ÃB>£B>B>B>€ B>‰ B>B>ºB>áB>þA>MûA>’øA>àõA>7óA>—ðA>îA>sëA>ïèA>sæA>ÿãA>•áA>3ßA>ÙÜA>‡ÚA>=ØA>üÕA>ÂÓA>‘ÑA>gÏA>DÍA>)ËA>ÉA> ÇA>ÅA>ÃA>ÁA>!¿A>8½A>V»A>{¹A>¦·A>صA>´A>O²A>”°A>à®A>1­A>ˆ«A>æ©A>I¨A>²¦A>!¥A>–£A>¢A> A>ŸA>ŸA>.œA>ÚA>^™A>ý—A>¡–A>J•A>ø“A>«’A>c‘A>A>àŽA>¦A>pŒA>?‹A>ŠA>éˆA>ŇA>¤†A>ˆ…A>p„A>]ƒA>M‚A>AA>9€A>5A>4~A>8}A>?|A>I{A>WzA>iyA>~xA>—wA>³vA>ÓuA>ÖqQ?ü{Q?™„Q?ª‹Q?0‘Q?(•Q?”—Q?q˜Q?À—Q?€•Q?°‘Q?PŒQ?_…Q?Ý|Q?ÊrQ?$gQ?íYQ?"KQ?Å:Q?Ô(Q?PQ?9Q?éP?MÑP?z·P?œP?P?‡`P?c@P?«P?_ûO?ÖO? °O?ˆO?k^O?>3O?O?.ØN?K¨N?ÖvN?ÑCN?<N?ÙM?b¡M?hM?M-M?ïðL?³L?ŽsL?Œ2L?ðK?ì«K?OfK?+K?€ÖJ?PŒJ?œ@J?eóI?¬¤I?rTI?¹I?‚¯H?ÍZH?žH?ô¬G?ÑSG?7ùF?'F?£?F?¬àE?D€E?mE?(»D?wVD?\ðC?؈C?îC?ŸµB?íIB?ÛÜA?jnA?œþ@?s@?ò@?§??î1??p»>?¢C>?†Ê=?P=?oÔ­ºý>i@ü>&Æú>îKù>ÆÑ÷>¶Wö>ÅÝô>úcó>]êñ>õpð>Ç÷î>Ý~í><ì>ìê>óé>Xžç>#'æ>Z°ä>:ã>&Äá>ÊNà>ôÙÞ>«eÝ>÷ñÛ>Þ~Ú>e Ù>”š×>q)Ö>¹Ô>OIÓ>\ÚÑ>0lÐ>ÑþÎ>F’Í>”&Ì>»Ê>ÕQÉ>ÔèÇ>ÀÆ>«Å>³Ã>uNÂ>dêÀ>a‡¿>q%¾>šÄ¼>âd»>Mº>⨸>¥L·>›ñµ>Ë—´>8?³>éç±>â‘°>'=¯>¿é­>­—¬>÷F«>¡÷©>¯©¨>(]§>¦>gȤ>6€£>9¢>Lô >š°Ÿ>qnž>Ó->Æî›><µš>ú€™>ÿM˜>P—>ñë•>å¼”>1“>Ùb’>á7‘>M>!æŽ>a¿>šŒ>3v‹>ÍSŠ>ã2‰>xˆ>õ†>-Ù…>U¾„> ¥ƒ>S‚>0w>¦b€>oŸ~>Ó||>}]z>uAx>Á(v>it>rr>ãòo>Äçm>àk>ìÛi>@Ûg>Þe>ˆäc>ˆîa>#ü_>^ ^>@"\>Ï:Z>WX>wV>¼šT>4ÂR>tíP>O>`OM>†K>ªÀI>ÿG>zAF>À‡D>öÑB> A>Br?>aÈ=>"<>§€:>Öâ8>I7>`³5>Ã!4>>”2>Ö 1>…/>g.>h‡,>’+>è™)>n)(>&½&>U%>7ñ#>•‘">06!> ß>$Œ>‚=>$ó> ­>?k>»->ƒô>—¿>úŽ>­b>°:>>¬÷>¦Ü>ôÅ >–³ >¥ >Ù› >z– >o•>º˜>Z >N¬>–¼>1Ñ> ê>`>ò(>ÔN> òþ=Oý=ž´û=Ë"ú=‹™ø=Ú÷=¶ õ=1ô=Êò=ikñ=Lð=¦Çî=q‚í=©Eì=Hë=Iåé=¦Áè=X¦ç=Z“æ=¤ˆå=1†ä=ú‹ã=÷™â= °á=oÎà=Üôß=_#ß=ïYÞ=„˜Ý=ßÜ=œ-Ü= „Û=`âÚ=ŒHÚ=†¶Ù=F,Ù=Á©Ø=í.Ø=À»×=/P×=1ìÖ=¹Ö=½:Ö=3íÕ= §Õ=BhÕ=Ä0Õ=‰Õ=„×Ô=ªµÔ=íšÔ=A‡Ô=šzÔ=ëtÔ=&vÔ=?~Ô=)Ô=Ö¢Ô=8¿Ô=BâÔ=å Õ=<Õ=ÃrÕ=á¯Õ=`óÕ=2=Ö=HÖ=”ãÖ=@×=’¢×=& Ø=´yØ=-îØ=hÙ= èÙ=|nÚ=úÚ=*‹Û=Ý!Ü= ¾Ü=«_Ý=¦Þ=î²Þ=tdß=&à=ôÖà=Ï—á=¥]â=g(ã=øã=iÌä=ˆ¥å=Pƒæ=°eç=–Lè=ó7é=¶'ê=Ìë=&ì=³í=aî=ï=Ýð=Š+ñ=<ò=iPó=zhô=5„õ=‰£ö=eÆ÷=·ìø=oú={Cû=Ësü=M§ý=ðÝþ=Ñ >*ª>ûI>9ë>Þ>à1>7×>Û}>Ã%>çÎ>?y>Á$>fÑ>%>ö- >ÑÝ >®Ž >„@ >Jó >ú¦ >Š[ >ó>,Ç>-~>ï5>jî>”§>ha>Û>èÖ>…’>¬N>f>Ô¿>æw>–0>Ýé>³£>^>ñ>KÔ>>PL>î>èÅ>:ƒ>Ú@ >Äþ >î¼!>S{">ë9#>°ø#>š·$>¢v%>Ã5&>óô&>.´'>ks(>¥2)>Ôñ)>ñ°*>öo+>Ý.,>í,>1¬->’j.>º(/>¡æ/>A¤0>•a1>”2>9Û2>}—3>[S4>Ë5>ÇÉ5>I„6>J>7>Å÷7>´°8>i9>Ò :>õ×:>tŽ;>GD<>jù<>Õ­=>…a>>q?>–Æ?>ìw@>o(A>ØA>ä†B>Ë4C>ÈáC>×D>ð8E>ãE>1ŒF>N4G>aÛG>fH>X&I>1ÊI>ìlJ>†K>ø®K>?NL>UìL>6‰M>Ý$N>G¿N>mXO>MðO>á†P>&Q>°Q>°BR>îÓR>ËcS>DòS>VT>ý U>4•U>øV>E¥V>+W>o¯W>D2X>•³X>_3Y>ž±Y>O.Z>p©Z>ý"[>óš[>P\>†\>2ù\>³j]>Ú]>ÅH^>Sµ^>5 _>j‰_>ïð_>ÃV`>äº`>Na>~a>üÜa>;:b>¾•b>ƒïb>‰Gc>Íc>Oòc>Ed>–d>=åd>ª2e>P~e>-Èe>@f>‰Vf>›f>»Ýf>¢g>½]g> ›g>‹Ög>?h>%Hh>>~h>вh>åh>¸i>œDi>³qi>þœi>}Æi>0îi>j>78j>ŒZj>{j>Ý™j>Û¶j>Òj>…ëj>4k>!k>L-k>·?k>dPk>T_k>ˆlk>xk>Ãk>Ήk>$k>Ç”k>¹—k>û˜k>˜k>z–k>º’k>Tk>I†k>›}k>Msk>bgk>ÛYk>¼Jk>:k>½'k>ãk>{þj>‡çj> Ïj>µj>‚™j>||j>ú]j>ý=j>‰j>¢ùi>JÕi>„¯i>Uˆi>¾_i>Ã5i>i i>±Ýh> ¯h>9€h>€Oh>wh>#êg>ˆµg>¨g>ˆHg>+g>”Öf>É›f>Ì_f>¡"f>Läe>Ò¤e>5de>y"e>¤ßd>¸›d>ºVd>­d>•Éc>xc>X8c>9îb>!£b>Wb> b>#¼a>Kma>a>íÌ`>q{`>)`>ðÕ_>õ_>--_>×^>H^>4*^>eÒ]>Þy]>¤ ]>»Æ\>'l\>í\>µ[>—X[>ƒûZ>ÚZ>Ÿ?Z>ØàY>ˆY>R#Y>'ÆX>ehX> X>0«W>ÃKW>ÑëV>[‹V>h*V>ùÈU>gU>¼U>õ¡T>Ä>T>,ÛS>1wS>×S>"®R>IR>¹ãQ> ~Q>Q>Ö±P>UKP>•äO>š}O>iO>¯N>sGN>¶ßM>ÓwM>ÍM>©§L>j?L>×K>¬nK>6K>µJ>.5J>¤ÌI>dI>™ûH> “H>´*H>YÂG>ZG>çñF>׉F>é!F> ºE>RE> ëD>ƃD>·D>ßµC>COC>çèB>΂B>üB>u·A>=RA>Wí@>Lj@>$@>·À?>>]?>*ú>>~—>>=5>>lÓ=> r=>#=>³°<>ÀP<>Mñ;>^’;>ö3;>Ö:>Çx:>:>Û¿9>Fd9>K 9>í®8>1U8>ü7>¤£7>ÛK7>¾ô6>Qž6>–H6>ó5>CŸ5>°K5>Úø4>Ŧ4>sU4>æ4>"µ3>(f3>û3>žÊ2>~2>]22>~ç1>x1>MT1> 1>”Ä0> ~0>c80>¢ó/>˯/>Ýl/>Ü*/>Èé.>¥©.>tj.>6,.>íî->œ²->Cw->ä<->€->Ë,>³“,>L],>æ',>‚ó+>#À+>É+>u\+>),+>åü*>«Î*>{¡*>Wu*>@J*>5 *>9÷)>KÏ)>m¨)>Ÿ‚)>á])>5:)>›)>ö(>Õ(>;¶(>ì—(>±z(>‰^(>vC(>w)(>Œ(>¶ø'>ôá'>FÌ'>­·'>'¤'>¶‘'>X€'>p'>×`'>²R'> E'> 9'>±.'>Ó$'>'>H'>™ '>ù'>f'>àÿ&>fý&>øû&>“û&>8ü&>åý&>™'>T'> '>×'>'>e'>-&'>õ/'>º:'>{F'>7S'>í`'>›o'>@'>Ù'>f¡'>å³'>TÇ'>±Û'>üð'>1(>P(>W6(>CO(>i(>Ń(>XŸ(>È»(>Ù(><÷(><)>6)>»V)>7x)>ƒš)>œ½)>‚á)>1*>§+*>âQ*>àx*>Ÿ *>É*>Uò*>G+>ñF+>Pr+>až+>#Ë+>’ø+>­&,>pU,>Ú„,>è´,>—å,>æ->ÑH->V{->r®->#â->f.>9K.>š€.>…¶.>÷ì.>ð#/>j[/>e“/>ÞË/>Ñ0>=>0>x0>r²0>6í0>h(1>d1>û)~?ÀZ~?íˆ~?´~?rÝ~?Â?l'?lH?¿f?a‚?P›?†±?Å?ÂÕ?Àã?úî?m÷?ý?óÿ?€?;ý?¡÷?0ï?åã?¾Õ?¸Ä?Ѱ?š?X€?Ác?BD?×!?€ü~?:Ô~?©~?Üz~?ÂI~?³~?¯Þ}?´¤}?Âg}?×'}?ôä|?Ÿ|?=V|?j |?›»{?Ñi{? {?H½z?Šbz?Ðz?¤y?k@y?ÁÙx?px?x?é“w?]!w?Ú«v?b3v?÷·u?™9u?L¸t?4t?æ¬s?Ò"s?Õ•r?òr?*sq?Ýp?ùDp?•©o?W o?Bjn?[Æm?£m?vl?ÑÉk?¾k?êhj?W´i? ýh? Ch?V†g?öÆf?íf?A@e?öxd?¯c?—âb?b?ùAa?àm`?G—_?5¾^?®â]?¹]?]$\?žA[?„\Z?uY?U‹X?NŸW?±V?ƒÀU?ÌÍT?èØS?ßáR?·èQ?yíP?*ðO?ÔðN?~ïM?.ìL?îæK?ÅßJ?»ÖI?ØËH?%¿G?©°F?m E?zŽD?ØzC?eB?¨NA?,6@?$??˜>?’ã¾,ý>@¿ú>ÀRø>Mçõ>ù|ó>Ôñ>ð«î>]Eì>+àé>j|ç>*å>|¹â>oZà>ýÝ>u¡Û>¨GÙ>¸ïÖ>¶™Ô>¯EÒ>³óÏ>Ï£Í>VË>‰ É>CÁÆ>MzÄ>µ5Â>‡ó¿>ѳ½>Ÿv»>þ;¹>û·>¡Î´>ý›²>l°>?®>ʬ>qí©>ɧ>˜§¥>,‰£>Ïm¡>‹UŸ>j@>u.›>·™>9—> •> “>—‘>r>¸ >r‹>¨!‰>b1‡>¦D…>~[ƒ>ïu>(>tk{>B¶w>zt>(bp>WÃl>,i>gœe>_b>”^>b[>ªW>kAT>*àP>ņM>F5J>´ëF>ªC>Cp@>ë;=>ˆ :>+â6>ä¼3>Ü0>×->2l*>ä['>ûP$>‰K!>K>HQ>˜\>Ÿm>j„> ¡>‘à > ì >‰>O>͉>b•ý=¬#ø=•¾ò=:fí=ºè=1Üâ=½ªÝ=z†Ø=„oÓ=ùeÎ=ôiÉ=‘{Ä=ëš¿=Ⱥ=D¶=yL±=Õ£¬=t ¨=n}£=Ýÿž=Ùš=|0–=ÝÞ‘=œ=9h‰=aC…=¤-=1Nz=¦_r=Òj=ßÞb=õL[=<ÚS=Û†L=÷RE=µ>>=:J7=¦u0=Á)=¿,#=«¸=e=Û1=X =“-=K¹üzý>&Ë>–˜>¶e>s2 >¼þ >}Ê >¥•>!`>ß)>Îò>ݺ>ú>H> >ûÐ>¨“>U >"">ÏÓ#>‘%>½L'>Þ)>\¿*>*v,>8+.>wÞ/>Ú1>S?3>Ôì4>N˜6>¶A8>þè9>Ž;>ù0=>”Ñ>>Üo@>÷ B>§C>*AE>WÚF>rH>’ J>ƒŸK>D4M>ÆÇN>ûYP>ÕêQ>FzS>@U>´”V>”X>Ò¨Y>a0[>2¶\>6:^>a¼_>£ðºb>97d>p±e>ˆ)g>sŸh>"j>‰„k>šól>G`n>ƒÊo>@2q>q—r> ús>úYu>8·v>´x>ciy>8¾z>%|>_}>«~>ô>蜀>>>>úÝ>|‚>ƒ>\³ƒ>wL„>Üã„>…y…>k †>‰Ÿ†>Ú/‡>X¾‡>ýJˆ>ÅÕˆ>ª^‰>§å‰>·jŠ>ÔíŠ>ûn‹>%î‹>NkŒ>ræŒ>Œ_>—Ö>KŽ>p¾Ž>5/>Ú>\ >µt>ãÜ>âB‘>®¦‘>C’>žg’>»Ä’>™“>2x“>…Γ>Ž"”>Kt”>¹Ã”>Õ•>[•>¤•>(ê•>æ-–>Ho–>K®–>ïê–>0%—>]—>‡’—>šÅ—>Gö—>‹$˜>fP˜>Øy˜>à ˜>|Ř>®ç˜>t™>Ï$™>¿?™>CX™>\n™> ‚™>O“™>*¢™>œ®™>§¸™>KÀ™>ŠÅ™>dÈ™>ÜÈ™>òÆ™>©Â™>¼™>ÿ²™>¢§™>í™™>䉙>‡w™>Úb™>ßK™>š2™> ™>9ù˜>$Ù˜>ж˜>B’˜>{k˜>€B˜>T˜>ûé—>zº—>Ôˆ—> U—>)—>-ç–>­–>þp–>Õ2–>¥ò•>t°•>Hl•>#&•> Þ”> ””>H”>Rú“>§ª“>%Y“>Ò“>²°’>ÌY’>%’>æ‘>­J‘>çì>y>h,>»É>xe>¤ÿŽ>G˜Ž>h/Ž> Å>9Y>÷ëŒ>L}Œ>> Œ>Ö›‹>)‹> µŠ>¹?Š>%ɉ>XQ‰>X؈>,^ˆ>Ûâ‡>lf‡>çè†>Qj†>²ê…>j…>uè„>åe„>iâƒ>^ƒ>ÅØ‚>«R‚>ÁË>D>—»€>e2€>þP>Õ;~>`%}>« |>Åôz>¼Úy>ž¿x>x£w>X†v>Lhu>aIt>¦)s>( r>õçp>Æo>££n>Ÿ€m>]l>%9k>Éj>ðh>Ëg>Ô¥f>c€e>ÍZd>5c>cb>©é`>ûÃ_>fž^>÷x]>¹S\>¸.[> Z>åX>›ÁW>žV>æzU>JXT><6S>ÇR>÷óP>ÕÓO>n´N>Ê•M>öwL>ûZK>ã>J>º#I>ˆ H>WðF>3ØE>#ÁD>2«C>i–B>Ò‚A>Cp@>ÿ]?>ýK>>J:=>î(<>ô;>f:>O÷8>¸ç7>¬Ø6>6Ê5>_¼4>2¯3>¹¢2>þ–1> Œ0>í/>«x.>Rp->êh,>~b+>]*>ÆX)>U(>zS'>—R&>îR%>‰T$>rW#>µ[">Za!>lh >õp>{>•†>À“>Š¢>ý²>#Å>Ù>°î>)>}>µ:>ÚW>öv>˜>7»>oà>Ä>>1>æ\>ÆŠ >æº >Oí > " > Y >š’ >Î>Ø >®M>‘>ïÖ>k>„j>@¸>©>Æ[>±>7 >še>œ‡ÿ=²Iþ=…ý=!ßû=•²ú=ë‹ù=2kø=uP÷=¿;ö=-õ=›$ô=C"ó=&ò=:0ñ=ž@ð=UWï=htî=á—í=ÈÁì=&òë=)ë=efê=Vªé=Üôè=þEè=Âç=.üæ=Haæ=Íå=›?å=ݸä=à8ä=©¿ã=9Mã=•áâ=À|â=»â=‰Çá=,wá=¤-á=òêà=¯à=zà=èKà=’$à=à=eêß=‹×ß=Ëß=DÆß=ÒÇß=(Ðß=Bßß=õß=±à=ý4à=ú^à=¤à=õÆà=æá=pIá=”á=6æá=c>â= â=(ã=¯mã=˜ßã=ÚWä=lÖä=B[å=Sæå=”wæ=úç=z¬ç=Pè=™ùè=©é=^ê=Ýë=ûÚë=Ú¡ì=oní=«@î=ï=Þõï=¸Øð=ÿÀñ=¤®ò=–¡ó=Ç™ô='—õ=¤™ö=0¡÷=¹­ø=/¿ù=Õú=ðû=rý=î4þ=^ÿ=ËE>ÏÞ>z>^>Ö¶>aX>øû>¡>I>›ò>ý>9K>Eú>«>ª] >ï >ÜÇ >j >Œ8 >:ó >j¯ >m>$,>›ì>k®>‰q>í5>Šû>XÂ>MŠ>]S>>©è>д>ë>ïO>Ò>‹î>¿>S>Nb>÷4>C>(Ü>œ° >–…!> [">ó0#>C$>ñÝ$>ô´%>CŒ&>Óc'>œ;(>”)>±ë)>êÃ*>7œ+>t,>äL->3%.>qý.>”Õ/>”­0>i…1>]2>k43>ˆ 4>Vâ4>θ5>çŽ6>˜d7>Ú98>¥9>ðâ9>³¶:>æ‰;>ƒ\<>.=>Øÿ=>Ð>>t ?>«o@>>A>3~?J~?†~?å ~?iÿ}?ñ}?àß}?ÖË}?ò´}?7›}?¥~}?=_}?=}?ð}? ð|?YÅ|?Ö—|?„g|?f4|?~þ{?ÌÅ{?RŠ{?L{? {?MÇz?Ê€z?Š7z?Žëy?Úœy?oKy?Q÷x? x?Gx?Ùêw?Œw?‹*w?mÆv?¯_v?Söu?\Šu?Îu?«ªt?ø6t?·Às?ìGs?šÌr?ÄNr?pÎq?ŸKq?UÆp?—>p?i´o?Í'o?ɘn?`n?–sm?oÝl?ïDl?ªk?÷ k?ˆmj?ÐËi?Ö'i?h?+Ùg?‚.g?©f?¤Òe?x!e?(nd?»¸c?5c?›Gb?ò‹a??Î`?†`?ÍL_?‰^?pÃ]?Öû\?Q2\?æf[?™™Z?qÊY?tùX?¥&X? RW?¬{V?Œ£U?±ÉT?!îS?âS?ù1R?lQQ?@oP?|‹O?$¦N??¿M?ÓÖL?åìK?{K?›J?L&I?’6H?tEG?øRF?$_E?þiD?ŒsC?Ô{B?Û‚A?©ˆ@?C??°>?ô’=?”,ý>¤íú>ÃÁø>—–ö>)lô>‡Bò>¼ð>Ôñí>ÙÊë>ؤé>Ýç>ò[å>#9ã>|á>÷Þ>Ñ×Ü>ã¹Ú>JØ>‚Ö>AhÔ>çOÒ> 9Ð>¾#Î>Ì>ëýÉ>}íÇ>ÅÞÅ>ÍÑÃ>ŸÆÁ>F½¿>̵½>;°»>ž¬¹>þª·>e«µ>Þ­³>r²±>*¹¯>­>0Í«>‘Ú©><ê§><ü¥>™¤>\'¢>@ >;\ž>izœ> ›š>j¾˜>Pä–>Ú •>8“>ûe‘>£–>Ê>JŒ>Y9Š>Euˆ>´†>Ñõ„>:ƒ>,‚>³™> 5|>¯Öx>m~u>h,r>­àn>J›k>L\h>¿#e>±ña>.Æ^>A¡[>÷‚X>\kU>{ZR>_PO>ML>£PI>[F>~lC>Þ„@>T¤=>ìÊ:>¯ø7>¥-5>Øi2>P­/>ø,>-J*>¡£'>x%>¸l">iÜ>‘S>5Ò>\X> æ>H{>>}¼>h >! >h×>Uš>îd>67>]"þ=¶åù=|¸õ=³šñ=_Œí=ƒé="žå=?¾á=ÚíÝ=ö,Ú=“{Ö=±ÙÒ=QGÏ=qÄË=QÈ=-íÄ=ŘÁ=ÖS¾=\»=Tø·=ºá´=ŠÚ±=¾â®=Pú«=R=á³S=0=U=ŸÚV=ö‹X=ýPZ=y)\=2^=î`=r%b=„Id=ëf=jÈh=Ç"k=ÇŽm=. p=Ášr=C:u=yêw='«z=|}=|.€=Ѧ=è&ƒ=¤®„=å=†=Ô‡=}r‰=—‹=¼ÃŒ=ÌvŽ=ª0=7ñ‘=S¸“=ß…•=½Y—=Î3™=ò›= úœ=ùåž=ž× =Û΢=ˤ=ŸÍ¦=éÔ¨=Náª=±ò¬=ñ¯=ð#±=C³=¯gµ=2·=ù¼¹=åí»=×"¾=²[À=U˜Â=£ØÄ=~Ç=ÇcÉ=`®Ë=*üÍ=MÐ=Ü Ò=‡÷Ô=ìP×=ì¬Ù=k Ü=KlÞ=nÏà=·4ã=œå=Eè=Qpê= Ýì=_Kï=)»ñ=O,ô=³žö=;ù=Ɇû=Cüý=F9>Ãt>°>•ì>Î(>)e>›¡>Þ>‰ >ëV >.“ >EÏ ># >ºF>ÿ>ä¼>]÷>]1>Ùj>Ä£>Ü>µ>¤J>Ò€>3¶>»ê>` >Q!>Ï‚">„³#>'ã$>¯&>?'>@k(>3–)>à¿*><è+>=->Ø4.>Y/>¸{0>èœ1>Œ¼2>™Ú3>÷4>Í6>á*7>:B8>ÐW9>™k:>};>¤<>Ö›=>¨>>g²?>·º@>ÁA>6ÅB>PÇC>GÇD>ÅE>®ÀF>ºG>6±H>¦I>©˜J>êˆK>ÔvL>abM>ŠKN>I2O>šP>xøP>Ü×Q>´R>&S>gT>QV>:ßV>̬W>ÀwX>@Y>ÃZ>ËÈZ>'‰[>ÕF\>Ñ]>º]>©o^>"_>™Ò_>õ`>*a>fÒa>xwb>Ãc>F¹c>þUd>ëïd> ‡e>_f>ã¬f>˜;g>|Çg>Ph>ÓÖh>DZi>ãÚi>°Xj>¬Ój>×Kk>0Ák>¹3l>q£l>[m>vzm>Ãám>DFn>ú§n>åo> co>e¼o>üp>Ïfp>á·p>4q>ÉQq>¢šq>Ãàq>-$r>ãdr>ç¢r>=Þr>ès>éLs>E€s>þ°s>ßs>• t>z3t>ÊYt>ˆ}t>¹žt>_½t>Ùt>ót>= u>ãu>1u>Ñ@u>"Nu> Yu>Žau>²gu>{ku>îlu>lu>ähu>qcu>»[u>ÈQu>œEu>=7u>°&u>úu>!ÿt>*èt>Ït>ù³t>É–t>’wt>YVt>#3t>ø t>Ûæs>Ô½s>è’s>fs>z7s>s>ÁÔr>· r>íjr>i3r>1úq>K¿q>¾‚q>Dq>Æq>hÃp>|€p>

öo>¤®o>Àeo>no>µÏn>›‚n>'4n>_äm>J“m>ï@m>Síl>~˜l>wBl>Cëk>ê’k>r9k>áÞj>?ƒj>’&j>àÈi>1ji>Š i>ó©h>rHh> æg>Ì‚g>µg>͹f>Tf>«íe>|†e>˜e>¶d>ÊLd>íâc>txc>f c>Ê¡b>¥5b>ÿÈa>Ý[a>Fî`>@€`>Ñ`>£_>Ô3_>QÄ^>~T^>bä]>t]>e]>‘’\>‹!\>Z°[>?[>ÍZ>ý[Z>YêY>§xY>íY>0•X>u#X>ıW> @W>‘ÎV>]V>ÂëU>ŽzU>ƒ U>§˜T>ÿ'T>·S>^GS>q×R>ËgR>søQ>m‰Q>¿Q>l¬P>z>P>îÐO>ÌcO>÷N>ØŠN>N>ijM>ùHM>²ÞL>õtL>Å L>'£K>;K>°ÓJ>ßlJ>¯J>%¡I>DØH>tH>½H>¥¯G>HNG>ªíF>ÍF>¶.F>gÐE>ãrE>-E>IºD>9_D>D>¢«C> SC>~ûB>½¤B>áNB>ìùA>à¥A>ÀRA>ŽA>O¯@>_@>¯@>RÁ?>ðs?>Š'?>"Ü>>¹‘>>QH>>ëÿ=>‰¸=>-r=>×,=>‰è<>C¥<>c<>Ø!<>³á;>œ¢;>’d;>–';>ªë:>Ͱ:>w:>E>:>š:>Ð9>yš9>f9> 29>N9>Ï8>âž8>Ço8>¾A8>Ç8>áè7> ¾7>H”7>”k7>ïC7>Z7>Ó÷6>ZÓ6>î¯6>Ž6>:l6>ðK6>°,6>x6>Gñ5>Õ5>÷¹5>ÖŸ5>·†5>šn5>|W5>]A5>;,5>5>è5>³ò4>vá4>-Ñ4>ØÁ4>t³4>¦4>z™4>à4>0ƒ4>gy4>…p4>‡h4>ka4>.[4>ÏU4>LQ4>¡M4>ÎJ4>ÐH4>£G4>GG4>¹G4>öH4>ûJ4>ÇM4>WQ4>©U4>¹Z4>†`4> g4>In4>;v4>ß~4>2ˆ4>2’4>Ûœ4>,¨4>!´4>¸À4>íÍ4>¿Û4>*ê4>,ù4>Á5>ç5>›)5>Ú:5>¢L5>ï^5>¿q5>…5>Ú˜5> ­5>ÝÁ5>×5>°ì5>Á6><6>!06>jG6>_6>#w6>Œ6>N¨6>hÁ6>ÕÚ6>”ô6>¡7>ù(7>™C7>^7>¨y7>•7>µ°7>•Ì7>«è7>ö8>s!8>>8>õZ8>õw8>•8>e²8>ÐÏ8>Yí8>ý 9>º(9>F9>sd9>j‚9>o 9>¾9>—Ü9>¶ú9>Ù:>ü6:>U:>T‘:>c¯:>fÍ:>[ë:>A ;>';>ÑD;>xb;>€;>v;>ɺ;>ü×;> õ;>ø<>½.<>YK<>Êg<>„<># <>¼<>¸×<>3ó<>x=>…)=>VD=>ë^=>By=>Y“=>.­=>ÀÆ=> à=>ù=>Ð>>C*>>kB>>GZ>>Ôq>>‰>>üŸ>>–¶>>ÛÌ>>Ëâ>>dø>>¦ ?>"?>7?>QK?>(_?>¢r?>½…?>y˜?>Õª?>ϼ?>hÎ?>ß?>nð?>Û@>ã@>„ @>¿/@>“>@>þL@>[@>œh@>Ìu@>“‚@>ïŽ@>áš@>g¦@>‚±@>1¼@>tÆ@>KÐ@>¶Ù@>´â@>Fë@>kó@>#û@>oA>N A>ÀA>ÆA>`A> A>N%A>¤)A>Ž-A> 1A> 4A>Ê6A> 9A>Þ:A>KN=A>ê=A>>A>7!~?%~?~?Í~?ˆ~?2ó}?Ëà}?VË}?Ò²}?A—}?¤x}?ûV}?G2}?‹ }?Èß|?þ±|?0|?_M|?|?¼Ü{?íŸ{?#`{?`{?¦×z?øŽz?WCz?Æôy?H£y?àNy?÷x?[x?C@x?Màw?{}w?Ðw?P¯v?ýCv?ÜÕu?ïdu?;ñt?Ãzt?‹t?–…s?és?ˆ…r?vr?¸zq?Rñp?Hep?ŸÖo?[Eo?±n?n?‚m?œæl?—Hl?¨k?k?¦_j?Æ·i?{ i?Ê`h?º±g?Pg?‘Lf?‚–e?)Þd?‹#d?¯fc?š§b?Ræa?Ü"a??]`?€•_?¥Ë^?¶ÿ]?¶1]?®a\?¢[?š»Z?›åY?¬ Y?Ô3X?XW?€zV?›U?Ò¹T?ËÖS?òR?| R?A#Q?Y9P?ÉMO?™`N?ÏqM?rL?ŠK?œJ?2§I?аH?ÿ¸G?Å¿F?*ÅE?4ÉD?ìËC?WÍB?~ÍA?gÌ@?Ê??ŸÆ>?ûÁ=?7¼à?«Æ?Ϭ?°’?Vx?È]?C?.(?1 ?ò?øÖ?Ì»?ž ?v…?\j?UO?i4?  ?ÿþ ?ä ?VÊ ?[°?¥–?:}?#d?dK?3??„?ÜØÿ>¥«ý>qû>LTù>E*÷>fõ>¾Ùò>Y³ð>CŽî>‰jì>8Hê>\'è>æ>3êã>ÿÍá>o³ß>‘šÝ>pƒÛ>nÙ>“Z×>ïHÕ>59Ó>q+Ñ>¯Ï>ùÍ>[Ë>ÞÉ>ŽÇ>vÅ>ŸÃ> Á>à¿> ½>£!»>®.¹>7>·>HPµ>êd³>&|±>–¯>”²­>×Ñ«>Ùó©>¢¨>;@¦>­j¤>˜¢><È >iûž>1>·j›>禙>(æ—>(–>öm”>“¶’>]‘>ZQ>’£> ù‹>ÍQŠ>Ü­ˆ>> ‡>ûo…>Öƒ>›?‚>‰¬€>Ï9~>x!{>x>¸u>cr>!o>ül>ý"i>,’\c>7„`>"³]>\éZ>ê&X>ÕkU>"¸R>Ù P>þfM>™ÉJ>­3H>A¥E>YC>üž@>;'>>·;>§N9>Ûí6>¼”4>LC2>ù/>…·->0}+>’J)>¬'>ü$> á">PÍ >NÁ>½>uÀ>œË>zÞ> ù>S>JE>ðv>C° >@ñ >ã9 >+Š>â>–A>²¨>c>£>Ûþ=|!û=;ø=¸cõ=;›ò=žáï=Ô6í=Ïšê=„ è=ãŽå=ßã=j½à=tjÞ=ï%Ü=ÉïÙ=óÇ×=]®Õ=õ¢Ó=ª¥Ñ=j¶Ï=#ÕÍ=ÂÌ=4<Ê=g„È=EÚÆ=¼=Å=¶®Ã=-Â=ã¸À=êQ¿=!ø½=p«¼=Ák»=þ8º=¹=Ýù·=Pí¶=Ríµ=Èù´=œ´=³7³=öh²=K¦±=˜ï°=ÄD°=´¥¯=O¯=zŠ®=®=­=M7­=ªÜ¬=¬=aH¬=„¬=[ß«=˺«=· «=‘«=‹«=B«=ýž«=¤·«=Ú«=?¬=ø;¬=&{¬=«Ã¬=j­=Ep­=Ô­=Ô@®=L¶®=g4¯=»¯= J°=Wá°=Ë€±=J(²=³×²=鎳=ÍM´=?µ=!âµ=U·¶=»“·=4w¸=¢a¹=åRº=àJ»=rI¼=~N½=äY¾=†k¿=EƒÀ=¡Á=žÄÂ=üíÃ=üÅ=€QÆ=i‹Ç=šÊÈ=óÊ=WXË=¨¦Ì=ÇùÍ=—QÏ=ù­Ð=ÐÒ=þsÓ=fÝÔ=ëJÖ=n¼×=Ó1Ù=üªÚ=Í'Ü=)¨Ý=ó+ß=³à=_=â=ÈÊã=.[å=uîæ=„è=6ê=y¸ë=/Ví=<öî=…˜ð=ï<ò=aãó=¿‹õ=ï5÷=Øáø=_ú=j>ü=áîý=© ÿ=Õ©>æƒ>z^>†9>ü>Óð>üÌ>m©>†>÷b>ù? > >?ú >l× >‘´ >£‘ >˜n>eK>(>]>sà>8¼>¡—>¥r>9M>U'>ï>ýÙ>u²>PŠ>„a>8>Ó >Üâ>·>‰Š>]>Ì. >’ÿ >fÏ!>?ž">l#>å8$>¤%>JÏ%>Ò˜&>5a'>k((>nî(>8³)>Âv*>9+>þù+>£¹,>ñw->á4.>mð.>‘ª/>Gc0>‰1>TÐ1>¡„2>k73>°è3>i˜4>’F5>(ó5>%ž6>‡G7>Hï7>f•8>Ý99>©Ü9>Æ}:>2;>êº;>êV<>/ñ<>·‰=>€ >>…µ>>ÇH?>AÚ?>òi@>×÷@>ïƒA>4B>¥–B>@C>¢C>í$D>ý¥D>3%E>Œ¢E> F>¨—F>jG>M…G>RùG>wkH>¾ÛH>%JI>­¶I>W!J>!ŠJ>ñJ>VK>M¹K>¢L>zL>¸×L>|3M>gM>zåM>¶;N>N>¯âN>o3O>^‚O>~ÏO>ÐP>VdP>¬P>òP>56Q> xQ>I¹Q>2øQ>^5R>ÐpR>‰ªR>âR>ÝS>}MS>o€S>¶±S>UáS>NT>¥;T>]fT>yT>ü¶T>èÜT>BU> $U>KEU>eU>0ƒU>ߟU>»U>ÃÔU>íU>ËV>%V>-V>—?V>¸PV>w`V>ÙnV>â{V>•‡V>ö‘V> ›V>Õ¢V>Z©V>®V>¢²V>nµV>·V>j·V>¢¶V>±´V>›±V>d­V>¨V>¤¡V>#šV>“‘V>ö‡V>R}V>«qV>eV>bWV>ÊHV>>9V>Å(V>bV>V>íñU>åÝU>ÉU>M³U>ÆœU>s…U>WmU>wTU>×:U>{ U>hU>¢éT>,ÍT> °T>B’T>×sT>ÍTT>(5T>ìT>ôS>ÀÒS>ذS>iŽS>xkS>HS>$S>¸ÿR>âÚR>œµR>ëR>ÒiR>UCR>wR><õQ>©ÍQ>À¥Q>†}Q>ýTQ>*,Q>Q>±ÙP>°P>9†P>%\P>Û1P>^P>²ÜO>Û±O>Ú†O>´[O>l0O>O>ÙN>ä­N>1‚N>kVN>–*N>³þM>ÅÒM>ЦM>×zM>ÜNM>á"M>êöL>úÊL>ŸL>6sL>gGL>ªL>ÿïK>iÄK>ë˜K>ˆmK>ABK>K>ìJ>,ÁJ>m–J>ÕkJ>fAJ>#J>íI>(ÃI>s™I>òoI>¦FI>I>³ôH>ÌH>ª£H>‚{H>˜SH>ð+H>‰H>gÝG>жG>ôG>¥iG>¡CG>çG>yøF>XÓF>…®F>ŠF>ÐeF>ðAF>bF>(ûE>CØE>´µE>{“E>šqE>PE>à.E> E>ŽíD>nÍD>©­D>AŽD>6oD>‰PD>:2D>ID>¸öC>†ÙC>´¼C>B C>1„C>hC>2MC>D2C>¹C>ýB>ÇãB>aÊB>]±B>¼˜B>}€B> hB>%QB> :B>X#B> B>÷A>‚áA>TÌA>ˆ·A>£A>A>j{A>!hA>9UA>°BA>ˆ0A>¿A>U A>Jü@>žë@>PÛ@>_Ë@>Ì»@>”¬@>¹@>:@>@>Js@>Ùe@>ÁX@>L@>š?@>‰3@>Ï'@>j@>[@>Ÿ@>7ü?>"ò?>_è?>íÞ?>ÌÕ?>úÌ?>wÄ?>B¼?>Z´?>¿¬?>o¥?>jž?>¯—?><‘?>‹?>/…?>’?>;z?>(u?>Xp?>Ìk?>g?>wc?>­_?>"\?>ÕX?>ÅU?>ñR?>YP?>úM?>ÕK?>éI?>4H?>µF?>lE?>XD?>wC?>ÉB?>LB?>B?>åA?>÷A?>8B?>¦B?>@C?>D?>ôD?> F?>LG?>´H?>BJ?>öK?>ÎM?>ÊO?>èQ?>)T?>ŠV?> Y?>«[?>j^?>Fa?>>d?>Rg?>€j?>Ém?>*q?>¤t?>5x?>Ü{?>™?>kƒ?>Q‡?>J‹?>V?>s“?>¡—?>ß›?>, ?>‰¤?>ò¨?>i­?>í±?>|¶?>»?>¹¿?>gÄ?>É?>ÜÍ?>¢Ò?>o×?>BÜ?>á?>øå?>Úê?>Àï?>¨ô?>“ù?>þ?>o@>^@>M @>=@>+@>@>!@>ì%@>Ñ*@>´/@>“4@>m9@>C>@>C@>ÞG@>£L@>bQ@>V@>ËZ@>t_@>d@>®h@>>m@>Åq@>Cv@>·z@>"@>‚ƒ@>ׇ@>"Œ@>b@>—”@>À˜@>Ýœ@>î @>ó¤@>ë¨@>׬@>¶°@>ˆ´@>M¸@>¼@>®¿@>KÃ@>ÙÆ@>ZÊ@>ÍÍ@>1Ñ@>ˆÔ@>Ð×@> Û@>5Þ@>Qá@>_ä@>_ç@>Pê@>2í@>ð@>Êò@>€õ@>'ø@>Àú@>Jý@>Åÿ@>2A>A>ßA> A>R A>w A>ŒA>”A>A>yA>VA>&A>èA>A>CA>ÝA>j!A>é"A>[$A>Á%A>'A>g(A>§)A>Ü*A>,A>!-A>2.A>8/A>30A>#1A>2A>â2A>²3A>y4A>55A>ç5A>‘6A>17A>È7A>V8A>Ü8A>Y9A>Ï9A>=:A>£:A>;A>Z;A>¬;A>÷;A>;z³ç=A>?=A>d=A>…=A>¡=A>º=A>Ï=A>á=A>ð=A>ü=A>>A>>A>>A>>A>>A>>A>>A>>A>>A>vispy-0.4.0/vispy/io/datasets.py0000664000175000017500000000156412527672621020346 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. import numpy as np from os import path as op from ..util import load_data_file # This is the package data dir, not the dir for config, etc. DATA_DIR = op.join(op.dirname(__file__), '_data') def load_iris(): """Load the iris dataset Returns ------- iris : NpzFile data['data'] : a (150, 4) NumPy array with the iris' features data['group'] : a (150,) NumPy array with the iris' group """ return np.load(load_data_file('iris/iris.npz', force_download='2014-09-04')) def load_crate(): """Load an image of a crate Returns ------- crate : array 256x256x3 crate image. """ return np.load(load_data_file('orig/crate.npz'))['crate'] vispy-0.4.0/vispy/io/image.py0000664000175000017500000001422212527672621017613 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2015, Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- # Author: Luke Campagnola # ----------------------------------------------------------------------------- import struct import zlib import numpy as np from ..ext.png import Reader def _make_png(data, level=6): """Convert numpy array to PNG byte array. Parameters ---------- data : numpy.ndarray Data must be (H, W, 3 | 4) with dtype = np.ubyte (np.uint8) level : int https://docs.python.org/2/library/zlib.html#zlib.compress An integer from 0 to 9 controlling the level of compression: * 1 is fastest and produces the least compression, * 9 is slowest and produces the most. * 0 is no compression. The default value is 6. Returns ------- png : array PNG formatted array """ # Eventually we might want to use ext/png.py for this, but this # routine *should* be faster b/c it's speacialized for our use case def mkchunk(data, name): if isinstance(data, np.ndarray): size = data.nbytes else: size = len(data) chunk = np.empty(size + 12, dtype=np.ubyte) chunk.data[0:4] = np.array(size, '>u4').tostring() chunk.data[4:8] = name.encode('ASCII') chunk.data[8:8 + size] = data # and-ing may not be necessary, but is done for safety: # https://docs.python.org/3/library/zlib.html#zlib.crc32 chunk.data[-4:] = np.array(zlib.crc32(chunk[4:-4]) & 0xffffffff, '>u4').tostring() return chunk if data.dtype != np.ubyte: raise TypeError('data.dtype must be np.ubyte (np.uint8)') dim = data.shape[2] # Dimension if dim not in (3, 4): raise TypeError('data.shape[2] must be in (3, 4)') # www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html#C.IHDR if dim == 4: ctyp = 0b0110 # RGBA else: ctyp = 0b0010 # RGB # www.libpng.org/pub/png/spec/1.2/PNG-Structure.html header = b'\x89PNG\x0d\x0a\x1a\x0a' # header h, w = data.shape[:2] depth = data.itemsize * 8 ihdr = struct.pack('!IIBBBBB', w, h, depth, ctyp, 0, 0, 0) c1 = mkchunk(ihdr, 'IHDR') # www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html#C.IDAT # insert filter byte at each scanline idat = np.empty((h, w * dim + 1), dtype=np.ubyte) idat[:, 1:] = data.reshape(h, w * dim) idat[:, 0] = 0 comp_data = zlib.compress(idat, level) c2 = mkchunk(comp_data, 'IDAT') c3 = mkchunk(np.empty((0,), dtype=np.ubyte), 'IEND') # concatenate lh = len(header) png = np.empty(lh + c1.nbytes + c2.nbytes + c3.nbytes, dtype=np.ubyte) png.data[:lh] = header p = lh for chunk in (c1, c2, c3): png[p:p + len(chunk)] = chunk p += chunk.nbytes return png def read_png(filename): """Read a PNG file to RGB8 or RGBA8 Unlike imread, this requires no external dependencies. Parameters ---------- filename : str File to read. Returns ------- data : array Image data. See also -------- write_png, imread, imsave """ x = Reader(filename) try: alpha = x.asDirect()[3]['alpha'] if alpha: y = x.asRGBA8()[2] n = 4 else: y = x.asRGB8()[2] n = 3 y = np.array([yy for yy in y], np.uint8) finally: x.file.close() y.shape = (y.shape[0], y.shape[1] // n, n) return y def write_png(filename, data): """Write a PNG file Unlike imsave, this requires no external dependencies. Parameters ---------- filename : str File to save to. data : array Image data. See also -------- read_png, imread, imsave """ data = np.asarray(data) if not data.ndim == 3 and data.shape[-1] in (3, 4): raise ValueError('data must be a 3D array with last dimension 3 or 4') with open(filename, 'wb') as f: f.write(_make_png(data)) # Save array with make_png def imread(filename, format=None): """Read image data from disk Requires imageio or PIL. Parameters ---------- filename : str Filename to read. format : str | None Format of the file. If None, it will be inferred from the filename. Returns ------- data : array Image data. See also -------- imsave, read_png, write_png """ imageio, PIL = _check_img_lib() if imageio is not None: return imageio.imread(filename, format) elif PIL is not None: im = PIL.Image.open(filename) if im.mode == 'P': im = im.convert() # Make numpy array a = np.asarray(im) if len(a.shape) == 0: raise MemoryError("Too little memory to convert PIL image to " "array") return a else: raise RuntimeError("imread requires the imageio or PIL package.") def imsave(filename, im, format=None): """Save image data to disk Requires imageio or PIL. Parameters ---------- filename : str Filename to write. im : array Image data. format : str | None Format of the file. If None, it will be inferred from the filename. See also -------- imread, read_png, write_png """ # Import imageio or PIL imageio, PIL = _check_img_lib() if imageio is not None: return imageio.imsave(filename, im, format) elif PIL is not None: pim = PIL.Image.fromarray(im) pim.save(filename, format) else: raise RuntimeError("imsave requires the imageio or PIL package.") def _check_img_lib(): """Utility to search for imageio or PIL""" # Import imageio or PIL imageio = PIL = None try: import imageio except ImportError: try: import PIL.Image except ImportError: pass return imageio, PIL vispy-0.4.0/vispy/io/__init__.py0000664000175000017500000000126512527672621020273 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Utilities related to data reading, writing, fetching, and generation. """ from os import path as _op from .datasets import load_iris, load_crate, load_data_file # noqa from .mesh import read_mesh, write_mesh # noqa from .image import (read_png, write_png, imread, imsave, _make_png, # noqa _check_img_lib) # noqa _data_dir = _op.join(_op.dirname(__file__), '_data') __all__ = ['imread', 'imsave', 'load_iris', 'load_crate', 'load_data_file', 'read_mesh', 'read_png', 'write_mesh', 'write_png'] vispy-0.4.0/vispy/io/tests/0000775000175000017500000000000012527674621017322 5ustar larsonerlarsoner00000000000000vispy-0.4.0/vispy/io/tests/test_image.py0000664000175000017500000000301412527672621022011 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. import numpy as np from numpy.testing import assert_array_equal, assert_allclose from os import path as op import warnings from vispy.io import load_crate, imsave, imread, read_png, write_png from vispy.testing import requires_img_lib, run_tests_if_main from vispy.util import _TempDir temp_dir = _TempDir() def test_make_png(): """ Test to ensure that make_png functions correctly. """ # Save random RGBA and RGB arrays onto disk as PNGs using make_png. # Read them back with an image library and check whether the array # saved is equal to the array read. # Create random RGBA array as type ubyte rgba_save = np.random.randint(256, size=(100, 100, 4)).astype(np.ubyte) # Get rid of the alpha for RGB rgb_save = rgba_save[:, :, :3] # Output file should be in temp png_out = op.join(temp_dir, 'random.png') # write_png implicitly tests _make_png for rgb_a in (rgba_save, rgb_save): write_png(png_out, rgb_a) rgb_a_read = read_png(png_out) assert_array_equal(rgb_a, rgb_a_read) @requires_img_lib() def test_read_write_image(): """Test reading and writing of images""" fname = op.join(temp_dir, 'out.png') im1 = load_crate() imsave(fname, im1, format='png') with warnings.catch_warnings(record=True): # PIL unclosed file im2 = imread(fname) assert_allclose(im1, im2) run_tests_if_main() vispy-0.4.0/vispy/io/tests/__init__.py0000664000175000017500000000000012376730723021417 0ustar larsonerlarsoner00000000000000vispy-0.4.0/vispy/io/tests/test_io.py0000664000175000017500000000465412527672621021351 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. import numpy as np from os import path as op from numpy.testing import assert_allclose, assert_array_equal from vispy.io import write_mesh, read_mesh, load_data_file from vispy.geometry import _fast_cross_3d from vispy.util import _TempDir from vispy.testing import run_tests_if_main, assert_equal, assert_raises temp_dir = _TempDir() def test_wavefront(): """Test wavefront reader""" fname_mesh = load_data_file('orig/triceratops.obj.gz') fname_out = op.join(temp_dir, 'temp.obj') mesh1 = read_mesh(fname_mesh) assert_raises(IOError, read_mesh, 'foo.obj') assert_raises(ValueError, read_mesh, op.abspath(__file__)) assert_raises(ValueError, write_mesh, fname_out, *mesh1, format='foo') write_mesh(fname_out, mesh1[0], mesh1[1], mesh1[2], mesh1[3]) assert_raises(IOError, write_mesh, fname_out, *mesh1) write_mesh(fname_out, *mesh1, overwrite=True) mesh2 = read_mesh(fname_out) assert_equal(len(mesh1), len(mesh2)) for m1, m2 in zip(mesh1, mesh2): if m1 is None: assert_equal(m2, None) else: assert_allclose(m1, m2, rtol=1e-5) # test our efficient normal calculation routine assert_allclose(mesh1[2], _slow_calculate_normals(mesh1[0], mesh1[1]), rtol=1e-7, atol=1e-7) def _slow_calculate_normals(rr, tris): """Efficiently compute vertex normals for triangulated surface""" # first, compute triangle normals rr = rr.astype(np.float64) r1 = rr[tris[:, 0], :] r2 = rr[tris[:, 1], :] r3 = rr[tris[:, 2], :] tri_nn = np.cross((r2 - r1), (r3 - r1)) # Triangle normals and areas size = np.sqrt(np.sum(tri_nn * tri_nn, axis=1)) zidx = np.where(size == 0)[0] size[zidx] = 1.0 # prevent ugly divide-by-zero tri_nn /= size[:, np.newaxis] # accumulate the normals nn = np.zeros((len(rr), 3)) for p, verts in enumerate(tris): nn[verts] += tri_nn[p, :] size = np.sqrt(np.sum(nn * nn, axis=1)) size[size == 0] = 1.0 # prevent ugly divide-by-zero nn /= size[:, np.newaxis] return nn def test_huge_cross(): """Test cross product with lots of elements """ x = np.random.rand(100000, 3) y = np.random.rand(1, 3) z = np.cross(x, y) zz = _fast_cross_3d(x, y) assert_array_equal(z, zz) run_tests_if_main() vispy-0.4.0/vispy/io/mesh.py0000664000175000017500000000410212527672621017461 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Reading and writing of data like images and meshes. """ from os import path as op from .wavefront import WavefrontReader, WavefrontWriter def read_mesh(fname): """Read mesh data from file. Parameters ---------- fname : str File name to read. Format will be inferred from the filename. Currently only '.obj' and '.obj.gz' are supported. Returns ------- vertices : array Vertices. faces : array | None Triangle face definitions. normals : array Normals for the mesh. texcoords : array | None Texture coordinates. """ # Check format fmt = op.splitext(fname)[1].lower() if fmt == '.gz': fmt = op.splitext(op.splitext(fname)[0])[1].lower() if fmt in ('.obj'): return WavefrontReader.read(fname) elif not format: raise ValueError('read_mesh needs could not determine format.') else: raise ValueError('read_mesh does not understand format %s.' % fmt) def write_mesh(fname, vertices, faces, normals, texcoords, name='', format='obj', overwrite=False): """ Write mesh data to file. Parameters ---------- fname : str Filename to write. Must end with ".obj" or ".gz". vertices : array Vertices. faces : array | None Triangle face definitions. normals : array Normals for the mesh. texcoords : array | None Texture coordinates. name : str Name of the object. format : str Currently only "obj" is supported. overwrite : bool If the file exists, overwrite it. """ # Check file if op.isfile(fname) and not overwrite: raise IOError('file "%s" exists, use overwrite=True' % fname) # Check format if format not in ('obj'): raise ValueError('Only "obj" format writing currently supported') WavefrontWriter.write(fname, vertices, faces, normals, texcoords, name) vispy-0.4.0/vispy/io/wavefront.py0000664000175000017500000002675412527672621020561 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # This module was taken from visvis """ This module produces functionality to read and write wavefront (.OBJ) files. http://en.wikipedia.org/wiki/Wavefront_.obj_file The wavefront format is quite powerful and allows a wide variety of surfaces to be described. This implementation does only supports mesh stuff, so no nurbs etc. Further, material properties are ignored, although this might be implemented later, The classes are written with compatibility of Python3 in mind. """ import numpy as np import time from os import path as op from ..ext.gzip_open import gzip_open from ..geometry import _calculate_normals from ..util import logger class WavefrontReader(object): def __init__(self, f): self._f = f # Original vertices, normals and texture coords. # These are not necessarily of the same length. self._v = [] self._vn = [] self._vt = [] # Final vertices, normals and texture coords. # All three lists are of the same length, as opengl wants it. self._vertices = [] self._normals = [] self._texcords = [] # The faces, indices to vertex/normal/texcords arrays. self._faces = [] # Dictionary to keep track of processed face data, so we can # convert the original v/vn/vn to the final vertices/normals/texcords. self._facemap = {} @classmethod def read(cls, fname): """ read(fname, fmt) This classmethod is the entry point for reading OBJ files. Parameters ---------- fname : str The name of the file to read. fmt : str Can be "obj" or "gz" to specify the file format. """ # Open file fmt = op.splitext(fname)[1].lower() assert fmt in ('.obj', '.gz') opener = open if fmt == '.obj' else gzip_open with opener(fname, 'rb') as f: try: reader = WavefrontReader(f) while True: reader.readLine() except EOFError: pass # Done t0 = time.time() mesh = reader.finish() logger.debug('reading mesh took ' + str(time.time() - t0) + ' seconds') return mesh def readLine(self): """ The method that reads a line and processes it. """ # Read line line = self._f.readline().decode('ascii', 'ignore') if not line: raise EOFError() line = line.strip() if line.startswith('v '): # self._vertices.append( *self.readTuple(line) ) self._v.append(self.readTuple(line)) elif line.startswith('vt '): self._vt.append(self.readTuple(line, 3)) elif line.startswith('vn '): self._vn.append(self.readTuple(line)) elif line.startswith('f '): self._faces.append(self.readFace(line)) elif line.startswith('#'): pass # Comment elif line.startswith('mtllib '): logger.warning('Notice reading .OBJ: material properties are ' 'ignored.') elif any(line.startswith(x) for x in ('g ', 's ', 'o ', 'usemtl ')): pass # Ignore groups and smoothing groups, obj names, material elif not line.strip(): pass else: logger.warning('Notice reading .OBJ: ignoring %s command.' % line.strip()) def readTuple(self, line, n=3): """ Reads a tuple of numbers. e.g. vertices, normals or teture coords. """ numbers = [num for num in line.split(' ') if num] return [float(num) for num in numbers[1:n + 1]] def readFace(self, line): """ Each face consists of three or more sets of indices. Each set consists of 1, 2 or 3 indices to vertices/normals/texcords. """ # Get parts (skip first) indexSets = [num for num in line.split(' ') if num][1:] final_face = [] for indexSet in indexSets: # Did we see this exact index earlier? If so, it's easy final_index = self._facemap.get(indexSet) if final_index is not None: final_face.append(final_index) continue # If not, we need to sync the vertices/normals/texcords ... # Get and store final index final_index = len(self._vertices) final_face.append(final_index) self._facemap[indexSet] = final_index # What indices were given? indices = [i for i in indexSet.split('/')] # Store new set of vertex/normal/texcords. # If there is a single face that does not specify the texcord # index, the texcords are ignored. Likewise for the normals. if True: vertex_index = self._absint(indices[0], len(self._v)) self._vertices.append(self._v[vertex_index]) if self._texcords is not None: if len(indices) > 1 and indices[1]: texcord_index = self._absint(indices[1], len(self._vt)) self._texcords.append(self._vt[texcord_index]) else: if self._texcords: logger.warning('Ignoring texture coordinates because ' 'it is not specified for all faces.') self._texcords = None if self._normals is not None: if len(indices) > 2 and indices[2]: normal_index = self._absint(indices[2], len(self._vn)) self._normals.append(self._vn[normal_index]) else: if self._normals: logger.warning('Ignoring normals because it is not ' 'specified for all faces.') self._normals = None # Check face if self._faces and len(self._faces[0]) != len(final_face): raise RuntimeError( 'Vispy requires that all faces are either triangles or quads.') # Done return final_face def _absint(self, i, ref): i = int(i) if i > 0: return i - 1 else: return ref + i def _calculate_normals(self): vertices, faces = self._vertices, self._faces if faces is None: # ensure it's always 2D so we can use our methods faces = np.arange(0, vertices.size, dtype=np.uint32)[:, np.newaxis] normals = _calculate_normals(vertices, faces) return normals def finish(self): """ Converts gathere lists to numpy arrays and creates BaseMesh instance. """ self._vertices = np.array(self._vertices, 'float32') if self._faces: self._faces = np.array(self._faces, 'uint32') else: # Use vertices only self._vertices = np.array(self._v, 'float32') self._faces = None if self._normals: self._normals = np.array(self._normals, 'float32') else: self._normals = self._calculate_normals() if self._texcords: self._texcords = np.array(self._texcords, 'float32') else: self._texcords = None return self._vertices, self._faces, self._normals, self._texcords class WavefrontWriter(object): def __init__(self, f): self._f = f @classmethod def write(cls, fname, vertices, faces, normals, texcoords, name=''): """ This classmethod is the entry point for writing mesh data to OBJ. Parameters ---------- fname : string The filename to write to. Must end with ".obj" or ".gz". vertices : numpy array The vertex data faces : numpy array The face data texcoords : numpy array The texture coordinate per vertex name : str The name of the object (e.g. 'teapot') """ # Open file fmt = op.splitext(fname)[1].lower() if fmt not in ('.obj', '.gz'): raise ValueError('Filename must end with .obj or .gz, not "%s"' % (fmt,)) opener = open if fmt == '.obj' else gzip_open f = opener(fname, 'wb') try: writer = WavefrontWriter(f) writer.writeMesh(vertices, faces, normals, texcoords, name) except EOFError: pass finally: f.close() def writeLine(self, text): """ Simple writeLine function to write a line of code to the file. The encoding is done here, and a newline character is added. """ text += '\n' self._f.write(text.encode('ascii')) def writeTuple(self, val, what): """ Writes a tuple of numbers (on one line). """ # Limit to three values. so RGBA data drops the alpha channel # Format can handle up to 3 texcords val = val[:3] # Make string val = ' '.join([str(v) for v in val]) # Write line self.writeLine('%s %s' % (what, val)) def writeFace(self, val, what='f'): """ Write the face info to the net line. """ # OBJ counts from 1 val = [v + 1 for v in val] # Make string if self._hasValues and self._hasNormals: val = ' '.join(['%i/%i/%i' % (v, v, v) for v in val]) elif self._hasNormals: val = ' '.join(['%i//%i' % (v, v) for v in val]) elif self._hasValues: val = ' '.join(['%i/%i' % (v, v) for v in val]) else: val = ' '.join(['%i' % v for v in val]) # Write line self.writeLine('%s %s' % (what, val)) def writeMesh(self, vertices, faces, normals, values, name=''): """ Write the given mesh instance. """ # Store properties self._hasNormals = normals is not None self._hasValues = values is not None self._hasFaces = faces is not None # Get faces and number of vertices if faces is None: faces = np.arange(len(vertices)) # Reshape faces Nfaces = faces.size // 3 faces = faces.reshape((Nfaces, 3)) # Number of vertices N = vertices.shape[0] # Get string with stats stats = [] stats.append('%i vertices' % N) if self._hasValues: stats.append('%i texcords' % N) else: stats.append('no texcords') if self._hasNormals: stats.append('%i normals' % N) else: stats.append('no normals') stats.append('%i faces' % faces.shape[0]) # Write header self.writeLine('# Wavefront OBJ file') self.writeLine('# Created by vispy.') self.writeLine('#') if name: self.writeLine('# object %s' % name) else: self.writeLine('# unnamed object') self.writeLine('# %s' % ', '.join(stats)) self.writeLine('') # Write data if True: for i in range(N): self.writeTuple(vertices[i], 'v') if self._hasNormals: for i in range(N): self.writeTuple(normals[i], 'vn') if self._hasValues: for i in range(N): self.writeTuple(values[i], 'vt') if True: for i in range(faces.shape[0]): self.writeFace(faces[i]) vispy-0.4.0/vispy/mpl_plot/0000775000175000017500000000000012527674621017377 5ustar larsonerlarsoner00000000000000vispy-0.4.0/vispy/mpl_plot/_mpl_to_vispy.py0000664000175000017500000001546712527672621022647 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. import numpy as np import base64 import warnings try: import matplotlib.pyplot as plt from ..ext.mplexporter import Exporter, Renderer except ImportError as exp: Exporter = None Renderer = object has_mplexporter = False why_not = str(exp) else: has_mplexporter = True why_not = None from ..ext.six import BytesIO from ..color import Color from ..io import read_png from ..scene.visuals import Line, Markers, Text, Image from ..scene.widgets import ViewBox from ..visuals.transforms import STTransform from ..scene import SceneCanvas, PanZoomCamera from ..testing import has_matplotlib def _check_coords(coords, valid): if coords not in valid: raise RuntimeError('Coords must be %s, not %s' % (valid, coords)) class VispyRenderer(Renderer): def __init__(self, *args, **kwargs): self._line_count = 0 self._axs = {} Renderer.__init__(self, *args, **kwargs) def open_figure(self, fig, props): self._dpi = props['dpi'] size = (props['figwidth'] * self._dpi, props['figheight'] * self._dpi) self.canvas = SceneCanvas(size=size, show=True, keys='interactive', bgcolor='lightgray') @self.canvas.events.resize.connect def on_resize(event): self._resize(*event.size) self.canvas.events.resize.connect(on_resize) def close_figure(self, fig): # self.canvas.close() pass # don't do this, it closes when done rendering def open_axes(self, ax, props): bounds = np.array(props['bounds']) bounds[1] = 1. - bounds[1] - bounds[3] xlim = props['xlim'] ylim = props['ylim'] # for a in props['axes']: # a['position'] # add borders vb = ViewBox(parent=self.canvas.scene, border_color='black', bgcolor=props['axesbg']) vb.clip_method = 'fbo' # necessary for bgcolor vb.camera = PanZoomCamera() vb.camera.set_range(xlim, ylim, margin=0) ax_dict = dict(ax=ax, bounds=bounds, vb=vb, lims=xlim+ylim) self._axs[ax] = ax_dict self._resize(*self.canvas.size) def _resize(self, w, h): for ax in self._axs.values(): ax['vb'].pos = (w * ax['bounds'][0], h * ax['bounds'][1]) ax['vb'].size = (w * ax['bounds'][2], h * ax['bounds'][3]) def close_axes(self, ax): # self._axs.pop(ax)['vb'].parent = [] pass # don't do anything, or all plots get closed (!) def open_legend(self, legend, props): raise NotImplementedError('Legends not supported yet') def close_legend(self, legend): pass def draw_image(self, imdata, extent, coordinates, style, mplobj=None): _check_coords(coordinates, 'data') imdata = read_png(BytesIO(base64.b64decode(imdata.encode('utf-8')))) assert imdata.ndim == 3 and imdata.shape[2] == 4 imdata[:, :, 3] = (imdata[:, :, 3] * (style['alpha'] if style['alpha'] is not None else 1.)).astype(np.uint8) img = Image(imdata) vb = self._mpl_ax_to(mplobj) img.transform = STTransform.from_mapping([[0, 0], img.size], [[extent[0], extent[3]], [extent[1], extent[2]]]) img.parent = vb.scene def draw_text(self, text, position, coordinates, style, text_type=None, mplobj=None): _check_coords(coordinates, 'data') color = Color(style['color']) color.alpha = style['alpha'] color = color.rgba text = Text(text, color=color, pos=position, font_size=style['fontsize'], rotation=style['rotation'], anchor_x=style['halign'], anchor_y=style['valign']) text.parent = self._mpl_ax_to(mplobj).scene def draw_markers(self, data, coordinates, style, label, mplobj=None): _check_coords(coordinates, 'data') edge_color = Color(style['edgecolor']) edge_color.alpha = style['alpha'] face_color = Color(style['facecolor']) face_color.alpha = style['alpha'] markers = Markers() markers.set_data(data, face_color=face_color, edge_color=edge_color, size=style['markersize'], symbol=style['marker']) markers.parent = self._mpl_ax_to(mplobj).scene def draw_path(self, data, coordinates, pathcodes, style, offset=None, offset_coordinates="data", mplobj=None): _check_coords(coordinates, 'data') if offset is not None: raise NotImplementedError('cannot handle offset') _check_coords(offset_coordinates, 'data') # TODO --, :, etc. color = Color(style['edgecolor']) color.alpha = style['alpha'] line = Line(data, color=color, width=style['edgewidth'], method='gl') # XXX Looks bad with agg :( line.parent = self._mpl_ax_to(mplobj).scene def _mpl_ax_to(self, mplobj, output='vb'): """Helper to get the parent axes of a given mplobj""" for ax in self._axs.values(): if ax['ax'] is mplobj.axes: return ax[output] raise RuntimeError('Parent axes could not be found!') def _vispy_done(self): """Things to do once all objects have been collected""" self._resize(*self.canvas.size) # def draw_path_collection(...) TODO add this for efficiency # https://github.com/mpld3/mplexporter/blob/master/ # mplexporter/renderers/base.py def _mpl_to_vispy(fig): """Convert a given matplotlib figure to vispy This function is experimental and subject to change! Requires matplotlib and mplexporter. Parameters ---------- fig : instance of matplotlib Figure The populated figure to display. Returns ------- canvas : instance of Canvas The resulting vispy Canvas. """ renderer = VispyRenderer() exporter = Exporter(renderer) with warnings.catch_warnings(record=True): # py3k mpl warning exporter.run(fig) renderer._vispy_done() return renderer.canvas def show(block=False): """Show current figures using vispy Parameters ---------- block : bool If True, blocking mode will be used. If False, then non-blocking / interactive mode will be used. Returns ------- canvases : list List of the vispy canvases that were created. """ if not has_matplotlib(): raise ImportError('Requires matplotlib version >= 1.2') cs = [_mpl_to_vispy(plt.figure(ii)) for ii in plt.get_fignums()] if block and len(cs) > 0: cs[0].app.run() return cs vispy-0.4.0/vispy/mpl_plot/__init__.py0000664000175000017500000000105412527672621021506 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Matplotlib plotting backend wrapper. This module enables converting matplotlib plotting commands to vispy plotting commands. Support is experimental and incomplete, proceed with caution. """ __all__ = ['show'] try: from matplotlib.pyplot import * # noqa except ImportError: def show(): raise ImportError('matplotlib could not be found') else: from ._mpl_to_vispy import show # noqa vispy-0.4.0/vispy/mpl_plot/tests/0000775000175000017500000000000012527674621020541 5ustar larsonerlarsoner00000000000000vispy-0.4.0/vispy/mpl_plot/tests/__init__.py0000664000175000017500000000000012375431476022640 0ustar larsonerlarsoner00000000000000vispy-0.4.0/vispy/mpl_plot/tests/test_show_vispy.py0000664000175000017500000000167212527672621024370 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. import numpy as np from vispy.io import read_png, load_data_file from vispy.testing import (has_matplotlib, requires_application, run_tests_if_main, assert_raises) import vispy.mpl_plot as plt @requires_application() def test_show_vispy(): """Some basic tests of show_vispy""" if has_matplotlib(): n = 200 t = np.arange(n) noise = np.random.RandomState(0).randn(n) # Need, image, markers, line, axes, figure plt.figure() ax = plt.subplot(211) ax.imshow(read_png(load_data_file('pyplot/logo.png'))) ax = plt.subplot(212) ax.plot(t, noise, 'ko-') plt.draw() canvases = plt.show() canvases[0].close() else: assert_raises(ImportError, plt.show) run_tests_if_main() vispy-0.4.0/vispy/app/0000775000175000017500000000000012527674621016331 5ustar larsonerlarsoner00000000000000vispy-0.4.0/vispy/app/timer.py0000664000175000017500000001322412527672621020023 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from __future__ import division from ..util.event import Event, EmitterGroup from ..util.ptime import time as precision_time from ..ext.six import string_types from .base import BaseTimerBackend as TimerBackend # noqa from . import use_app, Application class Timer(object): """Timer used to schedule events in the future or on a repeating schedule Parameters ---------- interval : float | 'auto' Time between events in seconds. The default is 'auto', which attempts to find the interval that matches the refresh rate of the current monitor. Currently this is simply 1/60. connect : function | None The function to call. iterations : int Number of iterations. Can be -1 for infinite. start : bool Whether to start the timer. app : instance of vispy.app.Application The application to attach the timer to. """ def __init__(self, interval='auto', connect=None, iterations=-1, start=False, app=None): self.events = EmitterGroup(source=self, start=Event, stop=Event, timeout=Event) #self.connect = self.events.timeout.connect #self.disconnect = self.events.timeout.disconnect # Get app instance if app is None: self._app = use_app(call_reuse=False) elif isinstance(app, Application): self._app = app elif isinstance(app, string_types): self._app = Application(app) else: raise ValueError('Invalid value for app %r' % app) # Ensure app has backend app object self._app.native # Instantiate the backed with the right class self._backend = self._app.backend_module.TimerBackend(self) if interval == 'auto': interval = 1.0 / 60 self._interval = float(interval) self._running = False self._first_emit_time = None self._last_emit_time = None self.iter_count = 0 self.max_iterations = iterations if connect is not None: self.connect(connect) if start: self.start() @property def app(self): """ The vispy Application instance on which this Timer is based. """ return self._app @property def interval(self): return self._interval @interval.setter def interval(self, val): self._interval = val if self.running: self.stop() self.start() @property def elapsed(self): return precision_time() - self._first_emit_time @property def running(self): return self._running def start(self, interval=None, iterations=None): """Start the timer. A timeout event will be generated every *interval* seconds. If *interval* is None, then self.interval will be used. If *iterations* is specified, the timer will stop after emitting that number of events. If unspecified, then the previous value of self.iterations will be used. If the value is negative, then the timer will continue running until stop() is called. If the timer is already running when this function is called, nothing happens (timer continues running as it did previously, without changing the interval, number of iterations, or emitting a timer start event). """ if self.running: return # don't do anything if already running self.iter_count = 0 if interval is not None: self.interval = interval if iterations is not None: self.max_iterations = iterations self._backend._vispy_start(self.interval) self._running = True self._first_emit_time = precision_time() self._last_emit_time = precision_time() self.events.start(type='timer_start') def stop(self): """Stop the timer.""" self._backend._vispy_stop() self._running = False self.events.stop(type='timer_stop') # use timer.app.run() and .quit() instead. # def run_event_loop(self): #"""Execute the event loop for this Timer's backend. #""" # return self._backend._vispy_run() # def quit_event_loop(self): #"""Exit the event loop for this Timer's backend. #""" # return self._backend._vispy_quit() @property def native(self): """ The native timer on which this Timer is based. """ return self._backend._vispy_get_native_timer() def _timeout(self, *args): # called when the backend timer has triggered. if not self.running: return if self.max_iterations >= 0 and self.iter_count >= self.max_iterations: self.stop() return # compute dt since last event now = precision_time() dt = now - self._last_emit_time elapsed = now - self._first_emit_time self._last_emit_time = now self.events.timeout( type='timer_timeout', iteration=self.iter_count, elapsed=elapsed, dt=dt, count=self.iter_count) self.iter_count += 1 def connect(self, callback): """ Alias for self.events.timeout.connect() """ return self.events.timeout.connect(callback) def disconnect(self, callback=None): """ Alias for self.events.timeout.disconnect() """ return self.events.timeout.disconnect(callback) vispy-0.4.0/vispy/app/canvas.py0000664000175000017500000006257712527672621020175 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from __future__ import division, print_function import sys import numpy as np from time import sleep from ..util.event import EmitterGroup, Event, WarningEmitter from ..util.ptime import time from ..util.dpi import get_dpi from ..util import config as util_config from ..ext.six import string_types from . import Application, use_app from ..gloo.context import (GLContext, set_current_canvas, forget_canvas) # todo: add functions for asking about current mouse/keyboard state # todo: add hover enter/exit events # todo: add focus events class Canvas(object): """Representation of a GUI element with an OpenGL context Parameters ---------- title : str The widget title size : (width, height) The size of the window. position : (x, y) The position of the window in screen coordinates. show : bool Whether to show the widget immediately. Default False. autoswap : bool Whether to swap the buffers automatically after a draw event. Default True. If True, the ``swap_buffers`` Canvas method will be called last (by default) by the ``canvas.draw`` event handler. app : Application | str Give vispy Application instance to use as a backend. (vispy.app is used by default.) If str, then an application using the chosen backend (e.g., 'pyglet') will be created. Note the canvas application can be accessed at ``canvas.app``. create_native : bool Whether to create the widget immediately. Default True. vsync : bool Enable vertical synchronization. resizable : bool Allow the window to be resized. decorate : bool Decorate the window. Default True. fullscreen : bool | int If False, windowed mode is used (default). If True, the default monitor is used. If int, the given monitor number is used. config : dict A dict with OpenGL configuration options, which is combined with the default configuration options and used to initialize the context. See ``canvas.context.config`` for possible options. shared : Canvas | GLContext | None An existing canvas or context to share OpenGL objects with. keys : str | dict | None Default key mapping to use. If 'interactive', escape and F11 will close the canvas and toggle full-screen mode, respectively. If dict, maps keys to functions. If dict values are strings, they are assumed to be ``Canvas`` methods, otherwise they should be callable. parent : widget-object The parent widget if this makes sense for the used backend. dpi : float | None Resolution in dots-per-inch to use for the canvas. If dpi is None, then the value will be determined by querying the global config first, and then the operating system. always_on_top : bool If True, try to create the window in always-on-top mode. px_scale : int > 0 A scale factor to apply between logical and physical pixels in addition to the actual scale factor determined by the backend. This option allows the scale factor to be adjusted for testing. Notes ----- The `Canvas` receives the following events: * initialize * resize * draw * mouse_press * mouse_release * mouse_double_click * mouse_move * mouse_wheel * key_press * key_release * stylus * touch * close The ordering of the mouse_double_click, mouse_press, and mouse_release events are not guaranteed to be consistent between backends. Only certain backends natively support double-clicking (currently Qt and WX); on other backends, they are detected manually with a fixed time delay. This can cause problems with accessibility, as increasing the OS detection time or using a dedicated double-click button will not be respected. """ def __init__(self, title='Vispy canvas', size=(800, 600), position=None, show=False, autoswap=True, app=None, create_native=True, vsync=False, resizable=True, decorate=True, fullscreen=False, config=None, shared=None, keys=None, parent=None, dpi=None, always_on_top=False, px_scale=1): size = [int(s) * px_scale for s in size] if len(size) != 2: raise ValueError('size must be a 2-element list') title = str(title) if not isinstance(fullscreen, (bool, int)): raise TypeError('fullscreen must be bool or int') # Initialize some values self._autoswap = autoswap self._title = title self._frame_count = 0 self._fps = 0 self._basetime = time() self._fps_callback = None self._backend = None self._closed = False self._px_scale = int(px_scale) if dpi is None: dpi = util_config['dpi'] if dpi is None: dpi = get_dpi(raise_error=False) self.dpi = dpi # Create events self.events = EmitterGroup(source=self, initialize=Event, resize=ResizeEvent, draw=DrawEvent, mouse_press=MouseEvent, mouse_release=MouseEvent, mouse_double_click=MouseEvent, mouse_move=MouseEvent, mouse_wheel=MouseEvent, key_press=KeyEvent, key_release=KeyEvent, stylus=Event, touch=Event, close=Event) # Deprecated paint emitter emitter = WarningEmitter('Canvas.events.paint and Canvas.on_paint are ' 'deprecated; use Canvas.events.draw and ' 'Canvas.on_draw instead.', source=self, type='draw', event_class=DrawEvent) self.events.add(paint=emitter) self.events.draw.connect(self.events.paint) # Get app instance if app is None: self._app = use_app(call_reuse=False) elif isinstance(app, Application): self._app = app elif isinstance(app, string_types): self._app = Application(app) else: raise ValueError('Invalid value for app %r' % app) # Check shared and context if shared is None: pass elif isinstance(shared, Canvas): shared = shared.context.shared elif isinstance(shared, GLContext): shared = shared.shared else: raise TypeError('shared must be a Canvas, not %s' % type(shared)) config = config or {} if not isinstance(config, dict): raise TypeError('config must be a dict, not %s' % type(config)) # Create new context self._context = GLContext(config, shared) # Deal with special keys self._set_keys(keys) # store arguments that get set on Canvas init kwargs = dict(title=title, size=size, position=position, show=show, vsync=vsync, resizable=resizable, decorate=decorate, fullscreen=fullscreen, context=self._context, parent=parent, always_on_top=always_on_top) self._backend_kwargs = kwargs # Create widget now (always do this *last*, after all err checks) if create_native: self.create_native() # Now we're ready to become current self.set_current() if '--vispy-fps' in sys.argv: self.measure_fps() def create_native(self): """ Create the native widget if not already done so. If the widget is already created, this function does nothing. """ if self._backend is not None: return # Make sure that the app is active assert self._app.native # Instantiate the backend with the right class self._app.backend_module.CanvasBackend(self, **self._backend_kwargs) # self._backend = set by BaseCanvasBackend self._backend_kwargs = None # Clean up # Connect to draw event (append to the end) # Process GLIR commands at each paint event self.events.draw.connect(self.context.flush_commands, position='last') if self._autoswap: self.events.draw.connect((self, 'swap_buffers'), ref=True, position='last') def _set_keys(self, keys): if keys is not None: if isinstance(keys, string_types): if keys != 'interactive': raise ValueError('keys, if string, must be "interactive", ' 'not %s' % (keys,)) def toggle_fs(): self.fullscreen = not self.fullscreen keys = dict(escape='close', F11=toggle_fs) else: keys = {} if not isinstance(keys, dict): raise TypeError('keys must be a dict, str, or None') if len(keys) > 0: # ensure all are callable for key, val in keys.items(): if isinstance(val, string_types): new_val = getattr(self, val, None) if new_val is None: raise ValueError('value %s is not an attribute of ' 'Canvas' % val) val = new_val if not hasattr(val, '__call__'): raise TypeError('Entry for key %s is not callable' % key) # convert to lower-case representation keys.pop(key) keys[key.lower()] = val self._keys_check = keys def keys_check(event): if event.key is not None: use_name = event.key.name.lower() if use_name in self._keys_check: self._keys_check[use_name]() self.events.key_press.connect(keys_check, ref=True) @property def context(self): """ The OpenGL context of the native widget It gives access to OpenGL functions to call on this canvas object, and to the shared context namespace. """ return self._context @property def app(self): """ The vispy Application instance on which this Canvas is based. """ return self._app @property def native(self): """ The native widget object on which this Canvas is based. """ return self._backend._vispy_get_native_canvas() @property def dpi(self): """ The physical resolution of the canvas in dots per inch. """ return self._dpi @dpi.setter def dpi(self, dpi): self._dpi = float(dpi) self.update() def connect(self, fun): """ Connect a function to an event The name of the function should be on_X, with X the name of the event (e.g. 'on_draw'). This method is typically used as a decorator on a function definition for an event handler. Parameters ---------- fun : callable The function. """ # Get and check name name = fun.__name__ if not name.startswith('on_'): raise ValueError('When connecting a function based on its name, ' 'the name should start with "on_"') eventname = name[3:] # Get emitter try: emitter = self.events[eventname] except KeyError: raise ValueError( 'Event "%s" not available on this canvas.' % eventname) # Connect emitter.connect(fun) # ---------------------------------------------------------------- size --- @property def size(self): """ The size of canvas/window """ size = self._backend._vispy_get_size() return (size[0] // self._px_scale, size[1] // self._px_scale) @size.setter def size(self, size): return self._backend._vispy_set_size(size[0] * self._px_scale, size[1] * self._px_scale) @property def physical_size(self): """ The physical size of the canvas/window, which may differ from the size property on backends that expose HiDPI """ return self._backend._vispy_get_physical_size() @property def pixel_scale(self): """ The ratio between the number of logical pixels, or 'points', and the physical pixels on the device. In most cases this will be 1.0, but on certain backends this will be greater than 1. This should be used as a scaling factor when writing your own visualisations with Gloo (make a copy and multiply all your logical pixel values by it) but you should rarely, if ever, need to use this in your own Visuals or SceneGraph visualisations; instead you should apply the canvas_fb_transform in the SceneGraph canvas. """ return self._px_scale * self.physical_size[0] // self.size[0] @property def fullscreen(self): return self._backend._vispy_get_fullscreen() @fullscreen.setter def fullscreen(self, fullscreen): return self._backend._vispy_set_fullscreen(fullscreen) # ------------------------------------------------------------ position --- @property def position(self): """ The position of canvas/window relative to screen """ return self._backend._vispy_get_position() @position.setter def position(self, position): assert len(position) == 2 return self._backend._vispy_set_position(position[0], position[1]) # --------------------------------------------------------------- title --- @property def title(self): """ The title of canvas/window """ return self._title @title.setter def title(self, title): self._title = title self._backend._vispy_set_title(title) # ----------------------------------------------------------------- fps --- @property def fps(self): """The fps of canvas/window, as the rate that events.draw is emitted """ return self._fps def set_current(self, event=None): """Make this the active GL canvas Parameters ---------- event : None Not used. """ self._backend._vispy_set_current() set_current_canvas(self) def swap_buffers(self, event=None): """Swap GL buffers such that the offscreen buffer becomes visible Parameters ---------- event : None Not used. """ self._backend._vispy_swap_buffers() def show(self, visible=True, run=False): """Show or hide the canvas Parameters ---------- visible : bool Make the canvas visible. run : bool Run the backend event loop. """ self._backend._vispy_set_visible(visible) if run: self.app.run() def update(self, event=None): """Inform the backend that the Canvas needs to be redrawn Parameters ---------- event : None Not used. """ if self._backend is not None: self._backend._vispy_update() def close(self): """Close the canvas Notes ----- This will usually destroy the GL context. For Qt, the context (and widget) will be destroyed only if the widget is top-level. To avoid having the widget destroyed (more like standard Qt behavior), consider making the widget a sub-widget. """ if self._backend is not None and not self._closed: self._closed = True self.events.close() self._backend._vispy_close() forget_canvas(self) def _update_fps(self, event): """Update the fps after every window""" self._frame_count += 1 diff = time() - self._basetime if (diff > self._fps_window): self._fps = self._frame_count / diff self._basetime = time() self._frame_count = 0 self._fps_callback(self.fps) def measure_fps(self, window=1, callback='%1.1f FPS'): """Measure the current FPS Sets the update window, connects the draw event to update_fps and sets the callback function. Parameters ---------- window : float The time-window (in seconds) to calculate FPS. Default 1.0. callback : function | str The function to call with the float FPS value, or the string to be formatted with the fps value and then printed. The default is ``'%1.1f FPS'``. If callback evaluates to False, the FPS measurement is stopped. """ # Connect update_fps function to draw self.events.draw.disconnect(self._update_fps) if callback: if isinstance(callback, string_types): callback_str = callback # because callback gets overwritten def callback(x): print(callback_str % x) self._fps_window = window self.events.draw.connect(self._update_fps) self._fps_callback = callback else: self._fps_callback = None # ---------------------------------------------------------------- misc --- def __repr__(self): return ('<%s (%s) at %s>' % (self.__class__.__name__, self.app.backend_name, hex(id(self)))) def __enter__(self): self.show() self._backend._vispy_warmup() return self def __exit__(self, type, value, traceback): # ensure all GL calls are complete if not self._closed: self._backend._vispy_set_current() self.context.finish() self.close() sleep(0.1) # ensure window is really closed/destroyed # Event subclasses specific to the Canvas class MouseEvent(Event): """Mouse event class Note that each event object has an attribute for each of the input arguments listed below, as well as a "time" attribute with the event's precision start time. Parameters ---------- type : str String indicating the event type (e.g. mouse_press, key_release) pos : (int, int) The position of the mouse (in screen coordinates). button : int | None The button that generated this event (can be None). Left=1, right=2, middle=3. During a mouse drag, this will return the button that started the drag (same thing as ``event.press_event.button``). buttons : [int, ...] The list of buttons depressed during this event. modifiers : tuple of Key instances Tuple that specifies which modifier keys were pressed down at the time of the event (shift, control, alt, meta). delta : (float, float) The amount of scrolling in horizontal and vertical direction. One "tick" corresponds to a delta of 1.0. press_event : MouseEvent The press event that was generated at the start of the current drag, if any. last_event : MouseEvent The MouseEvent immediately preceding the current event. During drag operations, all generated events retain their last_event properties, allowing the entire drag to be reconstructed. native : object (optional) The native GUI event object **kwargs : keyword arguments All extra keyword arguments become attributes of the event object. """ def __init__(self, type, pos=None, button=None, buttons=None, modifiers=None, delta=None, last_event=None, press_event=None, **kwargs): Event.__init__(self, type, **kwargs) self._pos = np.array([0, 0]) if (pos is None) else np.array(pos) self._button = int(button) if (button is not None) else None self._buttons = [] if (buttons is None) else buttons self._modifiers = tuple(modifiers or ()) self._delta = np.zeros(2) if (delta is None) else np.array(delta) self._last_event = last_event self._press_event = press_event self._time = time() @property def pos(self): return self._pos @property def button(self): return self._button @property def buttons(self): return self._buttons @property def modifiers(self): return self._modifiers @property def delta(self): return self._delta @property def press_event(self): return self._press_event @property def last_event(self): return self._last_event @property def time(self): return self._time def _forget_last_event(self): # Needed to break otherwise endless last-event chains self._last_event = None @property def is_dragging(self): """ Indicates whether this event is part of a mouse drag operation. """ return self.press_event is not None def drag_events(self): """ Return a list of all mouse events in the current drag operation. Returns None if there is no current drag operation. """ if not self.is_dragging: return None event = self events = [] while True: # mouse_press events can only be the start of a trail if event is None or event.type == 'mouse_press': break events.append(event) event = event.last_event return events[::-1] def trail(self): """ Return an (N, 2) array of mouse coordinates for every event in the current mouse drag operation. Returns None if there is no current drag operation. """ events = self.drag_events() if events is None: return None trail = np.empty((len(events), 2), dtype=int) for i, ev in enumerate(events): trail[i] = ev.pos return trail class KeyEvent(Event): """Key event class Note that each event object has an attribute for each of the input arguments listed below. Parameters ---------- type : str String indicating the event type (e.g. mouse_press, key_release) key : vispy.keys.Key instance The Key object for this event. Can be compared to string names. text : str The text representation of the key (can be an empty string). modifiers : tuple of Key instances Tuple that specifies which modifier keys were pressed down at the time of the event (shift, control, alt, meta). native : object (optional) The native GUI event object **kwargs : keyword arguments All extra keyword arguments become attributes of the event object. """ def __init__(self, type, key=None, text='', modifiers=None, **kwargs): Event.__init__(self, type, **kwargs) self._key = key self._text = text self._modifiers = tuple(modifiers or ()) @property def key(self): return self._key @property def text(self): return self._text @property def modifiers(self): return self._modifiers class ResizeEvent(Event): """Resize event class Note that each event object has an attribute for each of the input arguments listed below. Parameters ---------- type : str String indicating the event type (e.g. mouse_press, key_release) size : (int, int) The new size of the Canvas, in points (logical pixels). physical_size : (int, int) The new physical size of the Canvas, in pixels. native : object (optional) The native GUI event object **kwargs : extra keyword arguments All extra keyword arguments become attributes of the event object. """ def __init__(self, type, size=None, physical_size=None, **kwargs): Event.__init__(self, type, **kwargs) self._size = tuple(size) if physical_size is None: self._physical_size = self._size else: self._physical_size = tuple(physical_size) @property def size(self): return self._size @property def physical_size(self): return self._physical_size class DrawEvent(Event): """Draw event class This type of event is sent to Canvas.events.draw when a redraw is required. Note that each event object has an attribute for each of the input arguments listed below. Parameters ---------- type : str String indicating the event type (e.g. mouse_press, key_release) region : (int, int, int, int) or None The region of the canvas which needs to be redrawn (x, y, w, h). If None, the entire canvas must be redrawn. native : object (optional) The native GUI event object **kwargs : extra keyword arguments All extra keyword arguments become attributes of the event object. """ def __init__(self, type, region=None, **kwargs): Event.__init__(self, type, **kwargs) self._region = region @property def region(self): return self._region vispy-0.4.0/vispy/app/inputhook.py0000664000175000017500000000463412527672621020730 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Support for interactive mode to allow VisPy's event loop to be run alongside a console terminal, without using threads. This code relies on inputhooks built-in to the Python interpreter, and supports IPython too. The underlying inputhook implementation is from IPython 3.x. Note that IPython notebook integration is not supported, as the browser does not use Python's PyOS_InputHook functionality. """ from ..ext.ipy_inputhook import inputhook_manager, InputHookBase, stdin_ready from time import sleep from ..util.ptime import time def set_interactive(enabled=True, app=None): """Activate the IPython hook for VisPy. If the app is not specified, the default is used. """ if enabled: inputhook_manager.enable_gui('vispy', app) else: inputhook_manager.disable_gui() @inputhook_manager.register('vispy') class VisPyInputHook(InputHookBase): """Implementation of an IPython 3.x InputHook for VisPy. This is loaded by default when you call vispy.app.run() in a console-based interactive session, but you can also trigger it manually by importing this module then typing: >>> %enable_gui vispy """ def enable(self, app=None): """Activate event loop integration with this VisPy application. Parameters ---------- app : instance of Application The VisPy application that's being used. If None, then the default application is retrieved. Notes ----- This methods sets the ``PyOS_InputHook`` to this implementation, which allows Vispy to integrate with terminal-based applications running in interactive mode (Python or IPython). """ from .. import app as _app self.app = app or _app.use_app() self.manager.set_inputhook(self._vispy_inputhook) return app def _vispy_inputhook(self): try: t = time() while not stdin_ready(): self.app.process_events() used_time = time() - t if used_time > 10.0: sleep(1.0) elif used_time > 0.1: sleep(0.05) else: sleep(0.001) except KeyboardInterrupt: pass return 0 vispy-0.4.0/vispy/app/_default_app.py0000664000175000017500000000447212527672621021333 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from .application import Application # Initialize default app # Only for use within *this* module. # One should always call use_app() to obtain the default app. default_app = None def use_app(backend_name=None, call_reuse=True): """ Get/create the default Application object It is safe to call this function multiple times, as long as backend_name is None or matches the already selected backend. Parameters ---------- backend_name : str | None The name of the backend application to use. If not specified, Vispy tries to select a backend automatically. See ``vispy.use()`` for details. call_reuse : bool Whether to call the backend's `reuse()` function (True by default). Not implemented by default, but some backends need it. For example, the notebook backends need to inject some JavaScript in a notebook as soon as `use_app()` is called. """ global default_app # If we already have a default_app, raise error or return if default_app is not None: names = default_app.backend_name.lower().replace('(', ' ').strip(') ') names = [name for name in names.split(' ') if name] if backend_name and backend_name.lower() not in names: raise RuntimeError('Can only select a backend once.') else: if call_reuse: default_app.reuse() return default_app # Current backend matches backend_name # Create default app default_app = Application(backend_name) return default_app def create(): """Create the native application. """ use_app(call_reuse=False) return default_app.create() def run(): """Enter the native GUI event loop. """ use_app(call_reuse=False) return default_app.run() def quit(): """Quit the native GUI event loop. """ use_app(call_reuse=False) return default_app.quit() def process_events(): """Process all pending GUI events If the mainloop is not running, this should be done regularly to keep the visualization interactive and to keep the event system going. """ use_app(call_reuse=False) return default_app.process_events() vispy-0.4.0/vispy/app/application.py0000664000175000017500000002075312527672621021213 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Implements the global singleton app object. """ from __future__ import division import os import sys from . import backends, inputhook from .backends import CORE_BACKENDS, BACKEND_NAMES, BACKENDMAP, TRIED_BACKENDS from .. import config from .base import BaseApplicationBackend as ApplicationBackend # noqa from ..util import logger from ..ext import six class Application(object): """Representation of the vispy application This wraps a native GUI application instance. Vispy has a default instance of this class that can be created/obtained via `vispy.app.use_app()`. Parameters ---------- backend_name : str | None The name of the backend application to use. If not specified, Vispy tries to select a backend automatically. See ``vispy.use()`` for details. Notes ----- Upon creating an Application object, a backend is selected, but the native backend application object is only created when `create()` is called or `native` is used. The Canvas and Timer do this automatically. """ def __init__(self, backend_name=None): self._backend_module = None self._backend = None self._use(backend_name) def __repr__(self): name = self.backend_name if not name: return '' else: return '' % name @property def backend_name(self): """ The name of the GUI backend that this app wraps. """ if self._backend is not None: return self._backend._vispy_get_backend_name() else: return '' @property def backend_module(self): """ The module object that defines the backend. """ return self._backend_module def process_events(self): """ Process all pending GUI events. If the mainloop is not running, this should be done regularly to keep the visualization interactive and to keep the event system going. """ return self._backend._vispy_process_events() def create(self): """ Create the native application. """ # Ensure that the native app exists self.native def is_interactive(self): """ Determine if the user requested interactive mode. """ # The Python interpreter sets sys.flags correctly, so use them! if sys.flags.interactive: return True # IPython does not set sys.flags when -i is specified, so first # check it if it is already imported. if '__IPYTHON__' not in dir(six.moves.builtins): return False # Then we check the application singleton and determine based on # a variable it sets. try: from IPython.config.application import Application as App return App.initialized() and App.instance().interact except (ImportError, AttributeError): return False def run(self, allow_interactive=True): """ Enter the native GUI event loop. Parameters ---------- allow_interactive : bool Is the application allowed to handle interactive mode for console terminals? By default, typing ``python -i main.py`` results in an interactive shell that also regularly calls the VisPy event loop. In this specific case, the run() function will terminate immediately and rely on the interpreter's input loop to be run after script execution. """ if allow_interactive and self.is_interactive(): inputhook.set_interactive(enabled=True, app=self) else: return self._backend._vispy_run() def reuse(self): """ Called when the application is reused in an interactive session. This allow the backend to do stuff in the client when `use_app()` is called multiple times by the user. For example, the notebook backends need to inject JavaScript code as soon as `use_app()` is called. """ return self._backend._vispy_reuse() def quit(self): """ Quit the native GUI event loop. """ return self._backend._vispy_quit() @property def native(self): """ The native GUI application instance. """ return self._backend._vispy_get_native_app() def _use(self, backend_name=None): """Select a backend by name. See class docstring for details. """ # See if we're in a specific testing mode, if so DONT check to see # if it's a valid backend. If it isn't, it's a good thing we # get an error later because we should have decorated our test # with requires_application() test_name = os.getenv('_VISPY_TESTING_APP', None) # Check whether the given name is valid if backend_name is not None: if backend_name.lower() == 'default': backend_name = None # Explicitly use default, avoid using test elif backend_name.lower() not in BACKENDMAP: raise ValueError('backend_name must be one of %s or None, not ' '%r' % (BACKEND_NAMES, backend_name)) elif test_name is not None: backend_name = test_name.lower() assert backend_name in BACKENDMAP # Should we try and load any backend, or just this specific one? try_others = backend_name is None # Get backends to try ... imported_toolkits = [] # Backends for which the native lib is imported backends_to_try = [] if not try_others: # We should never hit this, since we check above assert backend_name.lower() in BACKENDMAP.keys() # Add it backends_to_try.append(backend_name.lower()) else: # See if a backend is loaded for name, module_name, native_module_name in CORE_BACKENDS: if native_module_name and native_module_name in sys.modules: imported_toolkits.append(name.lower()) backends_to_try.append(name.lower()) # See if a default is given default_backend = config['default_backend'].lower() if default_backend.lower() in BACKENDMAP.keys(): if default_backend not in backends_to_try: backends_to_try.append(default_backend) # After this, try each one for name, module_name, native_module_name in CORE_BACKENDS: name = name.lower() if name not in backends_to_try: backends_to_try.append(name) # Now try each one for key in backends_to_try: name, module_name, native_module_name = BACKENDMAP[key] TRIED_BACKENDS.append(name) mod_name = 'backends.' + module_name __import__(mod_name, globals(), level=1) mod = getattr(backends, module_name) if not mod.available: msg = ('Could not import backend "%s":\n%s' % (name, str(mod.why_not))) if not try_others: # Fail if user wanted to use a specific backend raise RuntimeError(msg) elif key in imported_toolkits: # Warn if were unable to use an already imported toolkit msg = ('Although %s is already imported, the %s backend ' 'could not\nbe used ("%s"). \nNote that running ' 'multiple GUI toolkits simultaneously can cause ' 'side effects.' % (native_module_name, name, str(mod.why_not))) logger.warning(msg) else: # Inform otherwise logger.info(msg) else: # Success! self._backend_module = mod logger.debug('Selected backend %s' % module_name) break else: raise RuntimeError('Could not import any of the backends. ' 'You need to install any of %s. We recommend ' 'PyQt' % [b[0] for b in CORE_BACKENDS]) # Store classes for app backend and canvas backend self._backend = self.backend_module.ApplicationBackend() vispy-0.4.0/vispy/app/__init__.py0000664000175000017500000000122312527672621020436 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ The app module defines three classes: Application, Canvas, and Timer. On loading, vispy creates a default Application instance which can be used via functions in the module's namespace. """ from __future__ import division from .application import Application # noqa from ._default_app import use_app, create, run, quit, process_events # noqa from .canvas import Canvas, MouseEvent, KeyEvent # noqa from .inputhook import set_interactive # noqa from .timer import Timer # noqa from . import base # noqa vispy-0.4.0/vispy/app/qt.py0000664000175000017500000000505412527672621017331 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # Force the selection of an application backend. If the user has already # imported PyQt or PySide, this should result in selection of the corresponding # backend. from . import use_app app = use_app() try: QtGui = app.backend_module.QtGui except AttributeError: raise RuntimeError("Cannot import Qt library; non-Qt backend is already " "in use.") class QtCanvas(QtGui.QWidget): """ Qt widget containing a vispy Canvas. This is a convenience class that allows a vispy canvas to be embedded directly into a Qt application. All methods and properties of the Canvas are wrapped by this class. Parameters ---------- parent : QWidget or None The Qt parent to assign to this widget. canvas : instance or subclass of Canvas The vispy Canvas to display inside this widget, or a Canvas subclass to instantiate using any remaining keyword arguments. """ def __init__(self, parent=None, canvas=None, **kwargs): from .canvas import Canvas if canvas is None: canvas = Canvas if issubclass(canvas, Canvas): canvas = canvas(**kwargs) elif len(**kwargs) > 0: raise TypeError('Invalid keyword arguments: %s' % list(kwargs.keys())) if not isinstance(canvas, Canvas): raise TypeError('canvas argument must be an instance or subclass ' 'of Canvas.') QtGui.QWidget.__init__(self, parent) self.layout = QtGui.QGridLayout() self.setLayout(self.layout) self.layout.setContentsMargins(0, 0, 0, 0) self._canvas = canvas self.layout.addWidget(canvas.native) self.setSizePolicy(canvas.native.sizePolicy()) def __getattr__(self, attr): if hasattr(self._canvas, attr): return getattr(self._canvas, attr) else: raise AttributeError(attr) def update(self): """Call update() on both this widget and the internal canvas. """ QtGui.QWidget.update(self) self._canvas.update() class QtSceneCanvas(QtCanvas): """ Convenience class embedding a vispy SceneCanvas inside a QWidget. See QtCanvas. """ def __init__(self, parent=None, **kwargs): from ..scene.canvas import SceneCanvas QtCanvas.__init__(self, parent, canvas=SceneCanvas, **kwargs) vispy-0.4.0/vispy/app/base.py0000664000175000017500000002316712527672621017624 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from ..util import SimpleBunch class BaseApplicationBackend(object): """BaseApplicationBackend() Abstract class that provides an interface between backends and Application. Each backend must implement a subclass of ApplicationBackend, and implement all its _vispy_xxx methods. """ def _vispy_get_backend_name(self): raise NotImplementedError() def _vispy_process_events(self): raise NotImplementedError() def _vispy_run(self): raise NotImplementedError() def _vispy_reuse(self): # Does nothing by default. pass def _vispy_quit(self): raise NotImplementedError() def _vispy_get_native_app(self): # Should return the native application object return self class BaseCanvasBackend(object): """BaseCanvasBackend(vispy_canvas, capability, context_type) Abstract class that provides an interface between backends and Canvas. Each backend must implement a subclass of CanvasBackend, and implement all its _vispy_xxx methods. Also, also a backend must make sure to generate the following events: 'initialize', 'resize', 'draw', 'mouse_press', 'mouse_release', 'mouse_move', 'mouse_wheel', 'key_press', 'key_release'. When a backend detects that the canvas should be closed, the backend should call 'self._vispy_canvas.close', because the close event is handled within the canvas itself. """ def __init__(self, vispy_canvas): from .canvas import Canvas # Avoid circular import assert isinstance(vispy_canvas, Canvas) self._vispy_canvas = vispy_canvas # We set the _backend attribute of the vispy_canvas to self, # because at the end of the __init__ of the CanvasBackend # implementation there might be a call to show or draw. By # setting it here, we ensure that the Canvas is "ready to go". vispy_canvas._backend = self # Data used in the construction of new mouse events self._vispy_mouse_data = { 'buttons': [], 'press_event': None, 'last_event': None, 'last_mouse_press': None, } def _process_backend_kwargs(self, kwargs): """ Simple utility to retrieve kwargs in predetermined order. Also checks whether the values of the backend arguments do not violate the backend capabilities. """ # Verify given argument with capability of the backend app = self._vispy_canvas.app capability = app.backend_module.capability if kwargs['context'].shared.name: # name already assigned: shared if not capability['context']: raise RuntimeError('Cannot share context with this backend') for key in [key for (key, val) in capability.items() if not val]: if key in ['context', 'multi_window', 'scroll']: continue invert = key in ['resizable', 'decorate'] if bool(kwargs[key]) - invert: raise RuntimeError('Config %s is not supported by backend %s' % (key, app.backend_name)) # Return items in sequence out = SimpleBunch() keys = ['title', 'size', 'position', 'show', 'vsync', 'resizable', 'decorate', 'fullscreen', 'parent', 'context', 'always_on_top', ] for key in keys: out[key] = kwargs[key] return out def _vispy_set_current(self): # Make this the current context raise NotImplementedError() def _vispy_swap_buffers(self): # Swap front and back buffer raise NotImplementedError() def _vispy_set_title(self, title): # Set the window title. Has no effect for widgets raise NotImplementedError() def _vispy_set_size(self, w, h): # Set size of the widget or window raise NotImplementedError() def _vispy_set_position(self, x, y): # Set location of the widget or window. May have no effect for widgets raise NotImplementedError() def _vispy_set_visible(self, visible): # Show or hide the window or widget raise NotImplementedError() def _vispy_set_fullscreen(self, fullscreen): # Set fullscreen mode raise NotImplementedError() def _vispy_update(self): # Invoke a redraw raise NotImplementedError() def _vispy_close(self): # Force the window or widget to shut down raise NotImplementedError() def _vispy_get_size(self): # Should return widget size raise NotImplementedError() def _vispy_get_physical_size(self): # Should return physical widget size (actual number of screen pixels). # This may differ from _vispy_get_size on backends that expose HiDPI # screens. If not overriden, return the logical sizeself. return self._vispy_get_size() def _vispy_get_position(self): # Should return widget position raise NotImplementedError() def _vispy_get_fullscreen(self): # Should return bool for fullscreen status raise NotImplementedError() def _vispy_get_geometry(self): # Should return widget (x, y, w, h) x, y = self._vispy_get_position() w, h = self._vispy_get_size() return x, y, w, h def _vispy_get_native_canvas(self): # Should return the native widget object # Most backends would not need to implement this return self def _vispy_mouse_press(self, **kwargs): # default method for delivering mouse press events to the canvas kwargs.update(self._vispy_mouse_data) ev = self._vispy_canvas.events.mouse_press(**kwargs) if self._vispy_mouse_data['press_event'] is None: self._vispy_mouse_data['press_event'] = ev self._vispy_mouse_data['buttons'].append(ev.button) self._vispy_mouse_data['last_event'] = ev if not getattr(self, '_double_click_supported', False): # double-click events are not supported by this backend, so we # detect them manually self._vispy_detect_double_click(ev) return ev def _vispy_mouse_move(self, **kwargs): # default method for delivering mouse move events to the canvas kwargs.update(self._vispy_mouse_data) # Break the chain of prior mouse events if no buttons are pressed # (this means that during a mouse drag, we have full access to every # move event generated since the drag started) if self._vispy_mouse_data['press_event'] is None: last_event = self._vispy_mouse_data['last_event'] if last_event is not None: last_event._forget_last_event() else: kwargs['button'] = self._vispy_mouse_data['press_event'].button ev = self._vispy_canvas.events.mouse_move(**kwargs) self._vispy_mouse_data['last_event'] = ev return ev def _vispy_mouse_release(self, **kwargs): # default method for delivering mouse release events to the canvas kwargs.update(self._vispy_mouse_data) ev = self._vispy_canvas.events.mouse_release(**kwargs) if (self._vispy_mouse_data['press_event'] and self._vispy_mouse_data['press_event'].button == ev.button): self._vispy_mouse_data['press_event'] = None if ev.button in self._vispy_mouse_data['buttons']: self._vispy_mouse_data['buttons'].remove(ev.button) self._vispy_mouse_data['last_event'] = ev return ev def _vispy_mouse_double_click(self, **kwargs): # default method for delivering double-click events to the canvas kwargs.update(self._vispy_mouse_data) ev = self._vispy_canvas.events.mouse_double_click(**kwargs) self._vispy_mouse_data['last_event'] = ev return ev def _vispy_detect_double_click(self, ev, **kwargs): # Called on every mouse_press or mouse_release, and calls # _vispy_mouse_double_click if a double-click is calculated. # Should be overridden with an empty function on backends which # natively support double-clicking. dt_max = 0.3 # time in seconds for a double-click detection lastev = self._vispy_mouse_data['last_mouse_press'] if lastev is None: self._vispy_mouse_data['last_mouse_press'] = ev return assert lastev.type == 'mouse_press' assert ev.type == 'mouse_press' # For a double-click to be detected, the button should be the same, # the position should be the same, and the two mouse-presses should # be within dt_max. if ((ev.time - lastev.time <= dt_max) & (lastev.pos[0] - ev.pos[0] == 0) & (lastev.pos[1] - ev.pos[1] == 0) & (lastev.button == ev.button)): self._vispy_mouse_double_click(**kwargs) self._vispy_mouse_data['last_mouse_press'] = ev class BaseTimerBackend(object): """BaseTimerBackend(vispy_timer) Abstract class that provides an interface between backends and Timer. Each backend must implement a subclass of TimerBackend, and implement all its _vispy_xxx methods. """ def __init__(self, vispy_timer): self._vispy_timer = vispy_timer def _vispy_start(self, interval): raise NotImplementedError def _vispy_stop(self): raise NotImplementedError def _vispy_get_native_timer(self): # Should return the native timer object # Most backends would not need to implement this return self vispy-0.4.0/vispy/app/backends/0000775000175000017500000000000012527674621020103 5ustar larsonerlarsoner00000000000000vispy-0.4.0/vispy/app/backends/_ipynb_util.py0000664000175000017500000000730612527672621022776 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """Tools used by the IPython notebook backends.""" import re import numpy as np from ...ext.six import string_types, iteritems from ...util.logs import _serialize_buffer # ----------------------------------------------------------------------------- # GLIR commands serialization # ----------------------------------------------------------------------------- def _extract_buffers(commands): """Extract all data buffers from the list of GLIR commands, and replace them by buffer pointers {buffer: }. Return the modified list # of GILR commands and the list of buffers as well.""" # First, filter all DATA commands. data_commands = [command for command in commands if command[0] == 'DATA'] # Extract the arrays. buffers = [data_command[3] for data_command in data_commands] # Modify the commands by replacing the array buffers with pointers. commands_modified = list(commands) buffer_index = 0 for i, command in enumerate(commands_modified): if command[0] == 'DATA': commands_modified[i] = command[:3] + \ ({'buffer_index': buffer_index},) buffer_index += 1 return commands_modified, buffers def _serialize_item(item): """Internal function: serialize native types.""" # Recursively serialize lists, tuples, and dicts. if isinstance(item, (list, tuple)): return [_serialize_item(subitem) for subitem in item] elif isinstance(item, dict): return dict([(key, _serialize_item(value)) for (key, value) in iteritems(item)]) # Serialize strings. elif isinstance(item, string_types): # Replace glSomething by something (needed for WebGL commands). if item.startswith('gl'): return re.sub(r'^gl([A-Z])', lambda m: m.group(1).lower(), item) else: return item # Process NumPy arrays that are not buffers (typically, uniform values). elif isinstance(item, np.ndarray): return _serialize_item(item.ravel().tolist()) # Serialize numbers. else: try: return np.asscalar(item) except Exception: return item def _serialize_command(command_modified): """Serialize a single GLIR (modified) command. The modification relates to the fact that buffers are replaced by pointers.""" return _serialize_item(command_modified) def create_glir_message(commands, array_serialization=None): """Create a JSON-serializable message of GLIR commands. NumPy arrays are serialized according to the specified method. Arguments --------- commands : list List of GLIR commands. array_serialization : string or None Serialization method for NumPy arrays. Possible values are: 'binary' (default) : use a binary string 'base64' : base64 encoded string of the array """ # Default serialization method for NumPy arrays. if array_serialization is None: array_serialization = 'binary' # Extract the buffers. commands_modified, buffers = _extract_buffers(commands) # Serialize the modified commands (with buffer pointers) and the buffers. commands_serialized = [_serialize_command(command_modified) for command_modified in commands_modified] buffers_serialized = [_serialize_buffer(buffer, array_serialization) for buffer in buffers] # Create the final message. msg = { 'msg_type': 'glir_commands', 'commands': commands_serialized, 'buffers': buffers_serialized, } return msg vispy-0.4.0/vispy/app/backends/_wx.py0000664000175000017500000003403012527672621021250 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ vispy backend for wxPython. """ from __future__ import division from time import sleep import gc import warnings from ..base import (BaseApplicationBackend, BaseCanvasBackend, BaseTimerBackend) from ...util import keys, logger from ...util.ptime import time from ... import config USE_EGL = config['gl_backend'].lower().startswith('es') # -------------------------------------------------------------------- init --- try: # avoid silly locale warning on OSX with warnings.catch_warnings(record=True): import wx from wx import glcanvas from wx.glcanvas import GLCanvas # Map native keys to vispy keys KEYMAP = { wx.WXK_SHIFT: keys.SHIFT, wx.WXK_CONTROL: keys.CONTROL, wx.WXK_ALT: keys.ALT, wx.WXK_WINDOWS_MENU: keys.META, wx.WXK_LEFT: keys.LEFT, wx.WXK_UP: keys.UP, wx.WXK_RIGHT: keys.RIGHT, wx.WXK_DOWN: keys.DOWN, wx.WXK_PAGEUP: keys.PAGEUP, wx.WXK_PAGEDOWN: keys.PAGEDOWN, wx.WXK_INSERT: keys.INSERT, wx.WXK_DELETE: keys.DELETE, wx.WXK_HOME: keys.HOME, wx.WXK_END: keys.END, wx.WXK_ESCAPE: keys.ESCAPE, wx.WXK_BACK: keys.BACKSPACE, wx.WXK_F1: keys.F1, wx.WXK_F2: keys.F2, wx.WXK_F3: keys.F3, wx.WXK_F4: keys.F4, wx.WXK_F5: keys.F5, wx.WXK_F6: keys.F6, wx.WXK_F7: keys.F7, wx.WXK_F8: keys.F8, wx.WXK_F9: keys.F9, wx.WXK_F10: keys.F10, wx.WXK_F11: keys.F11, wx.WXK_F12: keys.F12, wx.WXK_SPACE: keys.SPACE, wx.WXK_RETURN: keys.ENTER, # == pyglet.window.key.RETURN wx.WXK_NUMPAD_ENTER: keys.ENTER, wx.WXK_TAB: keys.TAB, } except Exception as exp: available, testable, why_not, which = False, False, str(exp), None class GLCanvas(object): pass else: if USE_EGL: available, testable, why_not = False, False, 'EGL not supported' else: available, testable, why_not = True, True, None which = 'wxPython ' + str(wx.__version__) # -------------------------------------------------------------- capability --- capability = dict( # things that can be set by the backend title=True, size=True, position=True, show=True, vsync=True, resizable=True, decorate=True, fullscreen=True, context=True, multi_window=True, scroll=True, parent=True, always_on_top=True, ) # ------------------------------------------------------- set_configuration --- def _set_config(c): """Set gl configuration""" gl_attribs = [glcanvas.WX_GL_RGBA, glcanvas.WX_GL_DEPTH_SIZE, c['depth_size'], glcanvas.WX_GL_STENCIL_SIZE, c['stencil_size'], glcanvas.WX_GL_MIN_RED, c['red_size'], glcanvas.WX_GL_MIN_GREEN, c['green_size'], glcanvas.WX_GL_MIN_BLUE, c['blue_size'], glcanvas.WX_GL_MIN_ALPHA, c['alpha_size']] gl_attribs += [glcanvas.WX_GL_DOUBLEBUFFER] if c['double_buffer'] else [] gl_attribs += [glcanvas.WX_GL_STEREO] if c['stereo'] else [] return gl_attribs # ------------------------------------------------------------- application --- _wx_app = None _timers = [] class ApplicationBackend(BaseApplicationBackend): def __init__(self): BaseApplicationBackend.__init__(self) self._event_loop = wx.EventLoop() wx.EventLoop.SetActive(self._event_loop) def _vispy_get_backend_name(self): return 'wx' def _vispy_process_events(self): # inpsired by https://github.com/wxWidgets/wxPython/blob/master/ # samples/mainloop/mainloop.py for _ in range(3): # trial-and-error found this to work (!) while self._event_loop.Pending(): self._event_loop.Dispatch() _wx_app.ProcessIdle() sleep(0.01) def _vispy_run(self): return _wx_app.MainLoop() def _vispy_quit(self): global _wx_app _wx_app.ExitMainLoop() def _vispy_get_native_app(self): # Get native app in save way. Taken from guisupport.py global _wx_app _wx_app = wx.GetApp() # in case the user already has one if _wx_app is None: _wx_app = wx.PySimpleApp() _wx_app.SetExitOnFrameDelete(True) return _wx_app # ------------------------------------------------------------------ canvas --- def _get_mods(evt): """Helper to extract list of mods from event""" mods = [] mods += [keys.CONTROL] if evt.ControlDown() else [] mods += [keys.ALT] if evt.AltDown() else [] mods += [keys.SHIFT] if evt.ShiftDown() else [] mods += [keys.META] if evt.MetaDown() else [] return mods def _process_key(evt): """Helper to convert from wx keycode to vispy keycode""" key = evt.GetKeyCode() if key in KEYMAP: return KEYMAP[key], '' if 97 <= key <= 122: key -= 32 if key >= 32 and key <= 127: return keys.Key(chr(key)), chr(key) else: return None, None class DummySize(object): def __init__(self, size): self.size = size def GetSize(self): return self.size def Skip(self): pass class CanvasBackend(GLCanvas, BaseCanvasBackend): """ wxPython backend for Canvas abstract class.""" # args are for BaseCanvasBackend, kwargs are for us. def __init__(self, *args, **kwargs): BaseCanvasBackend.__init__(self, *args) p = self._process_backend_kwargs(kwargs) # WX supports OS double-click events, so we set this here to # avoid double events self._double_click_supported = True # Set config self._gl_attribs = _set_config(p.context.config) # Deal with context p.context.shared.add_ref('wx', self) if p.context.shared.ref is self: self._gl_context = None # set for real once we init the GLCanvas else: self._gl_context = p.context.shared.ref._gl_context if p.parent is None: style = (wx.MINIMIZE_BOX | wx.MAXIMIZE_BOX | wx.CLOSE_BOX | wx.SYSTEM_MENU | wx.CAPTION | wx.CLIP_CHILDREN) style |= wx.NO_BORDER if not p.decorate else wx.RESIZE_BORDER style |= wx.STAY_ON_TOP if p.always_on_top else 0 self._frame = wx.Frame(None, wx.ID_ANY, p.title, p.position, p.size, style) if not p.resizable: self._frame.SetSizeHints(p.size[0], p.size[1], p.size[0], p.size[1]) if p.fullscreen is not False: if p.fullscreen is not True: logger.warning('Cannot specify monitor number for wx ' 'fullscreen, using default') self._fullscreen = True else: self._fullscreen = False _wx_app.SetTopWindow(self._frame) parent = self._frame self._frame.Raise() self._frame.Bind(wx.EVT_CLOSE, self.on_close) else: parent = p.parent self._frame = None self._fullscreen = False self._init = False GLCanvas.__init__(self, parent, wx.ID_ANY, pos=p.position, size=p.size, style=0, name='GLCanvas', attribList=self._gl_attribs) if self._gl_context is None: self._gl_context = glcanvas.GLContext(self) self.SetFocus() self._vispy_set_title(p.title) self._size = None self.Bind(wx.EVT_SIZE, self.on_resize) self.Bind(wx.EVT_PAINT, self.on_draw) self.Bind(wx.EVT_KEY_DOWN, self.on_key_down) self.Bind(wx.EVT_KEY_UP, self.on_key_up) self.Bind(wx.EVT_MOUSE_EVENTS, self.on_mouse_event) self._size_init = p.size self._vispy_set_visible(p.show) def on_resize(self, event): if self._vispy_canvas is None or not self._init: event.Skip() return size = event.GetSize() self._vispy_canvas.events.resize(size=size) self.Refresh() event.Skip() def on_draw(self, event): if self._vispy_canvas is None: return dc = wx.PaintDC(self) # needed for wx if not self._init: self._initialize() self._vispy_canvas.set_current() self._vispy_canvas.events.draw(region=None) del dc event.Skip() def _initialize(self): if self._vispy_canvas is None: return self._init = True self._vispy_canvas.set_current() self._vispy_canvas.events.initialize() self.on_resize(DummySize(self._size_init)) def _vispy_set_current(self): self.SetCurrent(self._gl_context) def _vispy_warmup(self): etime = time() + 0.3 while time() < etime: sleep(0.01) self._vispy_canvas.set_current() self._vispy_canvas.app.process_events() def _vispy_swap_buffers(self): # Swap front and back buffer self._vispy_canvas.set_current() self.SwapBuffers() def _vispy_set_title(self, title): # Set the window title. Has no effect for widgets if self._frame is not None: self._frame.SetLabel(title) def _vispy_set_size(self, w, h): # Set size of the widget or window if not self._init: self._size_init = (w, h) self.SetSizeWH(w, h) def _vispy_set_position(self, x, y): # Set positionof the widget or window. May have no effect for widgets if self._frame is not None: self._frame.SetPosition((x, y)) def _vispy_get_fullscreen(self): return self._fullscreen def _vispy_set_fullscreen(self, fullscreen): if self._frame is not None: self._fullscreen = bool(fullscreen) self._vispy_set_visible(True) def _vispy_set_visible(self, visible): # Show or hide the window or widget self.Show(visible) if visible: if self._frame is not None: self._frame.ShowFullScreen(self._fullscreen) def _vispy_update(self): # Invoke a redraw self.Refresh() def _vispy_close(self): if self._vispy_canvas is None: return # Force the window or widget to shut down canvas = self frame = self._frame self._gl_context = None # let RC destroy this in case it's shared canvas.Close() canvas.Destroy() if frame: frame.Close() frame.Destroy() gc.collect() # ensure context gets destroyed if it should be def _vispy_get_size(self): if self._vispy_canvas is None: return w, h = self.GetClientSize() return w, h def _vispy_get_position(self): if self._vispy_canvas is None: return x, y = self.GetPosition() return x, y def on_close(self, evt): if not self: # wx control evaluates to false if C++ part deleted return if self._vispy_canvas is None: return self._vispy_canvas.close() def on_mouse_event(self, evt): if self._vispy_canvas is None: return pos = (evt.GetX(), evt.GetY()) mods = _get_mods(evt) if evt.GetWheelRotation() != 0: delta = (0., float(evt.GetWheelRotation())) self._vispy_canvas.events.mouse_wheel(delta=delta, pos=pos, modifiers=mods) elif evt.Moving() or evt.Dragging(): # mouse move event self._vispy_mouse_move(pos=pos, modifiers=mods) elif evt.ButtonDown(): if evt.LeftDown(): button = 0 elif evt.MiddleDown(): button = 1 elif evt.RightDown(): button = 2 else: evt.Skip() self._vispy_mouse_press(pos=pos, button=button, modifiers=mods) elif evt.ButtonUp(): if evt.LeftUp(): button = 0 elif evt.MiddleUp(): button = 1 elif evt.RightUp(): button = 2 else: evt.Skip() self._vispy_mouse_release(pos=pos, button=button, modifiers=mods) elif evt.ButtonDClick(): if evt.LeftDClick(): button = 0 elif evt.MiddleDClick(): button = 1 elif evt.RightDClick(): button = 2 else: evt.Skip() self._vispy_mouse_press(pos=pos, button=button, modifiers=mods) self._vispy_mouse_double_click(pos=pos, button=button, modifiers=mods) evt.Skip() def on_key_down(self, evt): if self._vispy_canvas is None: return key, text = _process_key(evt) self._vispy_canvas.events.key_press(key=key, text=text, modifiers=_get_mods(evt)) def on_key_up(self, evt): if self._vispy_canvas is None: return key, text = _process_key(evt) self._vispy_canvas.events.key_release(key=key, text=text, modifiers=_get_mods(evt)) # ------------------------------------------------------------------- timer --- class TimerBackend(BaseTimerBackend): def __init__(self, vispy_timer): BaseTimerBackend.__init__(self, vispy_timer) assert _wx_app is not None parent = _wx_app.GetTopWindow() # assume it's the parent window self._timer = wx.Timer(parent, -1) parent.Bind(wx.EVT_TIMER, self._vispy_timeout, self._timer) def _vispy_start(self, interval): self._timer.Start(interval * 1000., False) def _vispy_stop(self): self._timer.Stop() def _vispy_timeout(self, evt): self._vispy_timer._timeout() evt.Skip() vispy-0.4.0/vispy/app/backends/_pyside.py0000664000175000017500000000310012527672621022101 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ PySide proxy backend for the qt backend. """ import sys from .. import backends from ...util import logger from ... import config USE_EGL = config['gl_backend'].lower().startswith('es') try: # Make sure no conflicting libraries have been imported. for lib in ['PyQt4', 'PyQt5']: lib += '.QtCore' if lib in sys.modules: raise RuntimeError("Refusing to import PySide because %s is " "already imported." % lib) # Try importing (QtOpenGL first to fail without import QtCore) if not USE_EGL: from PySide import QtOpenGL # noqa from PySide import QtGui, QtCore # noqa except Exception as exp: # Fail: this backend cannot be used available, testable, why_not, which = False, False, str(exp), None else: # Success available, testable, why_not = True, True, None has_uic = False import PySide which = ('PySide', PySide.__version__, QtCore.__version__) # Remove _qt module to force an import even if it was already imported sys.modules.pop(__name__.replace('_pyside', '_qt'), None) # Import _qt. Keep a ref to the module object! if backends.qt_lib is None: backends.qt_lib = 'pyside' # Signal to _qt what it should import from . import _qt # noqa from ._qt import * # noqa else: logger.info('%s already imported, cannot switch to %s' % (backends.qt_lib, 'PySide')) vispy-0.4.0/vispy/app/backends/_pyglet.py0000664000175000017500000003517512527672621022131 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ vispy backend for pyglet. """ from __future__ import division from distutils.version import LooseVersion from time import sleep from ..base import (BaseApplicationBackend, BaseCanvasBackend, BaseTimerBackend) from ...util import keys from ...util.ptime import time from ... import config USE_EGL = config['gl_backend'].lower().startswith('es') # -------------------------------------------------------------------- init --- try: import pyglet version = pyglet.version if LooseVersion(version) < LooseVersion('1.2'): help_ = ('You can install the latest pyglet using:\n ' 'pip install http://pyglet.googlecode.com/archive/tip.zip') raise ImportError('Pyglet version too old (%s), need >= 1.2\n%s' % (version, help_)) # Map native keys to vispy keys KEYMAP = { pyglet.window.key.LSHIFT: keys.SHIFT, pyglet.window.key.RSHIFT: keys.SHIFT, pyglet.window.key.LCTRL: keys.CONTROL, pyglet.window.key.RCTRL: keys.CONTROL, pyglet.window.key.LALT: keys.ALT, pyglet.window.key.RALT: keys.ALT, pyglet.window.key.LMETA: keys.META, pyglet.window.key.RMETA: keys.META, pyglet.window.key.LEFT: keys.LEFT, pyglet.window.key.UP: keys.UP, pyglet.window.key.RIGHT: keys.RIGHT, pyglet.window.key.DOWN: keys.DOWN, pyglet.window.key.PAGEUP: keys.PAGEUP, pyglet.window.key.PAGEDOWN: keys.PAGEDOWN, pyglet.window.key.INSERT: keys.INSERT, pyglet.window.key.DELETE: keys.DELETE, pyglet.window.key.HOME: keys.HOME, pyglet.window.key.END: keys.END, pyglet.window.key.ESCAPE: keys.ESCAPE, pyglet.window.key.BACKSPACE: keys.BACKSPACE, pyglet.window.key.F1: keys.F1, pyglet.window.key.F2: keys.F2, pyglet.window.key.F3: keys.F3, pyglet.window.key.F4: keys.F4, pyglet.window.key.F5: keys.F5, pyglet.window.key.F6: keys.F6, pyglet.window.key.F7: keys.F7, pyglet.window.key.F8: keys.F8, pyglet.window.key.F9: keys.F9, pyglet.window.key.F10: keys.F10, pyglet.window.key.F11: keys.F11, pyglet.window.key.F12: keys.F12, pyglet.window.key.SPACE: keys.SPACE, pyglet.window.key.ENTER: keys.ENTER, # == pyglet.window.key.RETURN pyglet.window.key.NUM_ENTER: keys.ENTER, pyglet.window.key.TAB: keys.TAB, } BUTTONMAP = {pyglet.window.mouse.LEFT: 1, pyglet.window.mouse.RIGHT: 2, pyglet.window.mouse.MIDDLE: 3 } except Exception as exp: available, testable, why_not, which = False, False, str(exp), None class _Window(object): pass else: if USE_EGL: available, testable, why_not = False, False, 'EGL not supported' else: available, testable, why_not = True, True, None which = 'pyglet ' + str(pyglet.version) _Window = pyglet.window.Window # -------------------------------------------------------------- capability --- capability = dict( # things that can be set by the backend title=True, size=True, position=True, show=True, vsync=True, resizable=True, decorate=True, fullscreen=True, context=True, multi_window=True, scroll=True, parent=False, always_on_top=False, ) # ------------------------------------------------------- set_configuration --- def _set_config(config): """Set gl configuration""" pyglet_config = pyglet.gl.Config() pyglet_config.red_size = config['red_size'] pyglet_config.green_size = config['green_size'] pyglet_config.blue_size = config['blue_size'] pyglet_config.alpha_size = config['alpha_size'] pyglet_config.accum_red_size = 0 pyglet_config.accum_green_size = 0 pyglet_config.accum_blue_size = 0 pyglet_config.accum_alpha_size = 0 pyglet_config.depth_size = config['depth_size'] pyglet_config.stencil_size = config['stencil_size'] pyglet_config.double_buffer = config['double_buffer'] pyglet_config.stereo = config['stereo'] pyglet_config.samples = config['samples'] return pyglet_config # ------------------------------------------------------------- application --- class ApplicationBackend(BaseApplicationBackend): def __init__(self): BaseApplicationBackend.__init__(self) def _vispy_get_backend_name(self): return 'Pyglet' def _vispy_process_events(self): # pyglet.app.platform_event_loop.step(0.0) pyglet.clock.tick() for window in pyglet.app.windows: window.switch_to() window.dispatch_events() window.dispatch_event('on_draw') def _vispy_run(self): return pyglet.app.run() def _vispy_quit(self): return pyglet.app.exit() def _vispy_get_native_app(self): return pyglet.app # ------------------------------------------------------------------ canvas --- class CanvasBackend(_Window, BaseCanvasBackend): """ Pyglet backend for Canvas abstract class.""" # args are for BaseCanvasBackend, kwargs are for us. def __init__(self, *args, **kwargs): BaseCanvasBackend.__init__(self, *args) p = self._process_backend_kwargs(kwargs) # Deal with config config = _set_config(p.context.config) # Also used further below # Deal with context p.context.shared.add_ref('pyglet', self) # contexts are shared by default in Pyglet style = (pyglet.window.Window.WINDOW_STYLE_DEFAULT if p.decorate else pyglet.window.Window.WINDOW_STYLE_BORDERLESS) # We keep track of modifier keys so we can pass them to mouse_motion self._current_modifiers = set() # self._buttons_accepted = 0 self._draw_ok = False # whether it is ok to draw yet self._pending_position = None if p.fullscreen is not False: screen = pyglet.window.get_platform().get_default_display() self._vispy_fullscreen = True if p.fullscreen is True: self._vispy_screen = screen.get_default_screen() else: screen = screen.get_screens() if p.fullscreen >= len(screen): raise RuntimeError('fullscreen must be < %s' % len(screen)) self._vispy_screen = screen[p.fullscreen] else: self._vispy_fullscreen = False self._vispy_screen = None self._initialize_sent = False pyglet.window.Window.__init__(self, width=p.size[0], height=p.size[1], caption=p.title, visible=p.show, config=config, vsync=p.vsync, resizable=p.resizable, style=style, screen=self._vispy_screen) if p.position is not None: self._vispy_set_position(*p.position) def _vispy_warmup(self): etime = time() + 0.1 while time() < etime: sleep(0.01) self._vispy_canvas.set_current() self._vispy_canvas.app.process_events() # Override these ... def flip(self): # Is called by event loop after each draw pass def on_draw(self): # Is called by event loop after each event, whatever event ... really if not self._draw_ok: self._draw_ok = True self.our_draw_func() def draw_mouse_cursor(self): # Prevent legacy OpenGL pass def _vispy_set_current(self): # Make this the current context self.switch_to() def _vispy_swap_buffers(self): # Swap front and back buffer pyglet.window.Window.flip(self) def _vispy_set_title(self, title): # Set the window title. Has no effect for widgets self.set_caption(title) def _vispy_set_size(self, w, h): # Set size of the widget or window self.set_size(w, h) def _vispy_set_position(self, x, y): # Set positionof the widget or window. May have no effect for widgets if self._draw_ok: self.set_location(x, y) else: self._pending_position = x, y def _vispy_set_visible(self, visible): # Show or hide the window or widget self.set_visible(visible) def _vispy_update(self): # Invoke a redraw pyglet.clock.schedule_once(self.our_draw_func, 0.0) def _vispy_close(self): # Force the window or widget to shut down # In Pyglet close is equivalent to destroy (window becomes invalid) self._vispy_canvas = None self.close() def _vispy_get_size(self): w, h = self.get_size() return w, h def _vispy_get_position(self): x, y = self.get_location() return x, y def _vispy_get_fullscreen(self): return self._vispy_fullscreen def _vispy_set_fullscreen(self, fullscreen): self._vispy_fullscreen = bool(fullscreen) self.set_fullscreen(self._vispy_fullscreen, self._vispy_screen) def on_show(self): if self._vispy_canvas is None: return if not self._initialize_sent: self._initialize_sent = True self._vispy_canvas.set_current() self._vispy_canvas.events.initialize() # Set location now if we must. For some reason we get weird # offsets in viewport if set_location is called before the # widget is shown. if self._pending_position: x, y = self._pending_position self._pending_position = None self.set_location(x, y) # Redraw self._vispy_update() def on_close(self): if self._vispy_canvas is None: return self._vispy_canvas.close() def on_resize(self, w, h): if self._vispy_canvas is None: return self._vispy_canvas.events.resize(size=(w, h)) # self._vispy_update() def our_draw_func(self, dummy=None): if not self._draw_ok or self._vispy_canvas is None: return # (0, 0, self.width, self.height)) self._vispy_canvas.set_current() self._vispy_canvas.events.draw(region=None) def on_mouse_press(self, x, y, button, modifiers=None): if self._vispy_canvas is None: return self._vispy_mouse_press( pos=(x, self.get_size()[1] - y), button=BUTTONMAP.get(button, 0), modifiers=self._modifiers(), ) # if ev2.handled: # self._buttons_accepted |= button def on_mouse_release(self, x, y, button, modifiers=None): if self._vispy_canvas is None: return if True: # (button & self._buttons_accepted) > 0: self._vispy_mouse_release( pos=(x, self.get_size()[1] - y), button=BUTTONMAP.get(button, 0), modifiers=self._modifiers(), ) #self._buttons_accepted &= ~button def on_mouse_motion(self, x, y, dx, dy): if self._vispy_canvas is None: return self._vispy_mouse_move( pos=(x, self.get_size()[1] - y), modifiers=self._modifiers(), ) def on_mouse_drag(self, x, y, dx, dy, button, modifiers): self.on_mouse_motion(x, y, dx, dy) def on_mouse_scroll(self, x, y, scroll_x, scroll_y): if self._vispy_canvas is None: return self._vispy_canvas.events.mouse_wheel( delta=(float(scroll_x), float(scroll_y)), pos=(x, y), modifiers=self._modifiers(), ) def on_key_press(self, key, modifiers): # Process modifiers if key in (pyglet.window.key.LCTRL, pyglet.window.key.RCTRL, pyglet.window.key.LALT, pyglet.window.key.RALT, pyglet.window.key.LSHIFT, pyglet.window.key.RSHIFT): self._current_modifiers.add(key) # Emit self._vispy_canvas.events.key_press( key=self._processKey(key), text='', # Handlers that trigger on text wont see this event modifiers=self._modifiers(modifiers)) def on_text(self, text): # Typically this is called after on_key_press and before # on_key_release self._vispy_canvas.events.key_press( key=None, # Handlers that trigger on key wont see this event text=text, modifiers=self._modifiers()) def on_key_release(self, key, modifiers): # Process modifiers if key in (pyglet.window.key.LCTRL, pyglet.window.key.RCTRL, pyglet.window.key.LALT, pyglet.window.key.RALT, pyglet.window.key.LSHIFT, pyglet.window.key.RSHIFT): self._current_modifiers.discard(key) # Get txt try: text = chr(key) except Exception: text = '' # Emit self._vispy_canvas.events.key_release( key=self._processKey(key), text=text, modifiers=self._modifiers(modifiers)) def _processKey(self, key): if 97 <= key <= 122: key -= 32 if key in KEYMAP: return KEYMAP[key] elif key >= 32 and key <= 127: return keys.Key(chr(key)) else: return None def _modifiers(self, pygletmod=None): mod = () if pygletmod is None: pygletmod = self._current_modifiers if isinstance(pygletmod, set): for key in pygletmod: mod += KEYMAP[key], else: if pygletmod & pyglet.window.key.MOD_SHIFT: mod += keys.SHIFT, if pygletmod & pyglet.window.key.MOD_CTRL: mod += keys.CONTROL, if pygletmod & pyglet.window.key.MOD_ALT: mod += keys.ALT, return mod # ------------------------------------------------------------------- timer --- class TimerBackend(BaseTimerBackend): def _vispy_start(self, interval): interval = self._vispy_timer._interval if self._vispy_timer.max_iterations == 1: pyglet.clock.schedule_once(self._vispy_timer._timeout, interval) else: # seems pyglet does not give the expected behavior when interval==0 if interval == 0: interval = 1e-9 pyglet.clock.schedule_interval( self._vispy_timer._timeout, interval) def _vispy_stop(self): pyglet.clock.unschedule(self._vispy_timer._timeout) def _vispy_get_native_timer(self): return pyglet.clock vispy-0.4.0/vispy/app/backends/_pyqt5.py0000664000175000017500000000305612527672621021700 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ PyQt5 proxy backend for the qt backend. """ import sys from .. import backends from ...util import logger from ... import config USE_EGL = config['gl_backend'].lower().startswith('es') try: # Make sure no conflicting libraries have been imported. for lib in ['PySide', 'PyQt4']: lib += '.QtCore' if lib in sys.modules: raise RuntimeError("Refusing to import PyQt5 because %s is " "already imported." % lib) # Try importing (QtOpenGL first to fail without import QtCore) if not USE_EGL: from PyQt5 import QtOpenGL # noqa from PyQt5 import QtGui, QtCore # noqa except Exception as exp: # Fail: this backend cannot be used available, testable, why_not, which = False, False, str(exp), None else: # Success available, testable, why_not = True, True, None has_uic = True which = ('PyQt5', QtCore.PYQT_VERSION_STR, QtCore.QT_VERSION_STR) # Remove _qt module to force an import even if it was already imported sys.modules.pop(__name__.replace('_pyqt5', '_qt'), None) # Import _qt. Keep a ref to the module object! if backends.qt_lib is None: backends.qt_lib = 'pyqt5' # Signal to _qt what it should import from . import _qt # noqa from ._qt import * # noqa else: logger.info('%s already imported, cannot switch to %s' % (backends.qt_lib, 'pyqt5')) vispy-0.4.0/vispy/app/backends/_pyqt4.py0000664000175000017500000000305612527672621021677 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ PyQt4 proxy backend for the qt backend. """ import sys from .. import backends from ...util import logger from ... import config USE_EGL = config['gl_backend'].lower().startswith('es') try: # Make sure no conflicting libraries have been imported. for lib in ['PySide', 'PyQt5']: lib += '.QtCore' if lib in sys.modules: raise RuntimeError("Refusing to import PyQt4 because %s is " "already imported." % lib) # Try importing (QtOpenGL first to fail without import QtCore) if not USE_EGL: from PyQt4 import QtOpenGL # noqa from PyQt4 import QtGui, QtCore # noqa except Exception as exp: # Fail: this backend cannot be used available, testable, why_not, which = False, False, str(exp), None else: # Success available, testable, why_not = True, True, None has_uic = True which = ('PyQt4', QtCore.PYQT_VERSION_STR, QtCore.QT_VERSION_STR) # Remove _qt module to force an import even if it was already imported sys.modules.pop(__name__.replace('_pyqt4', '_qt'), None) # Import _qt. Keep a ref to the module object! if backends.qt_lib is None: backends.qt_lib = 'pyqt4' # Signal to _qt what it should import from . import _qt # noqa from ._qt import * # noqa else: logger.info('%s already imported, cannot switch to %s' % (backends.qt_lib, 'pyqt4')) vispy-0.4.0/vispy/app/backends/_ipynb_webgl.py0000664000175000017500000002454012527672621023120 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2014, 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Vispy backend for the IPython notebook (WebGL approach). """ from __future__ import division from ..base import (BaseApplicationBackend, BaseCanvasBackend, BaseTimerBackend) from ...util import logger, keys from ...ext import six from vispy.gloo.glir import BaseGlirParser from vispy.app.backends.ipython import VispyWidget import os.path as op import os # -------------------------------------------------------------------- init --- capability = dict( # things that can be set by the backend title=True, # But it only applies to the dummy window :P size=True, # We cannot possibly say we dont, because Canvas always sets it position=True, # Dito show=True, vsync=False, resizable=True, decorate=False, fullscreen=True, context=True, multi_window=False, scroll=True, parent=False, always_on_top=False, ) # Try importing IPython try: import tornado import IPython IPYTHON_MAJOR_VERSION = IPython.version_info[0] if IPYTHON_MAJOR_VERSION < 2: raise RuntimeError('ipynb_webgl backend requires IPython >= 2.0') from IPython.html.nbextensions import install_nbextension from IPython.display import display except Exception as exp: # raise ImportError("The WebGL backend requires IPython >= 2.0") available, testable, why_not, which = False, False, str(exp), None else: available, testable, why_not, which = True, False, None, None # ------------------------------------------------------------- application --- def _prepare_js(force=False): pkgdir = op.dirname(__file__) jsdir = op.join(pkgdir, '../../html/static/js/') # Make sure the JS files are installed to user directory (new argument # in IPython 3.0). if IPYTHON_MAJOR_VERSION >= 3: kwargs = {'user': True} else: kwargs = {} install_nbextension(jsdir, overwrite=force, destination='vispy', symlink=(os.name != 'nt'), **kwargs) class ApplicationBackend(BaseApplicationBackend): def __init__(self): BaseApplicationBackend.__init__(self) _prepare_js() def _vispy_reuse(self): _prepare_js() def _vispy_get_backend_name(self): return 'ipynb_webgl' def _vispy_process_events(self): # TODO: may be implemented later. raise NotImplementedError() def _vispy_run(self): pass def _vispy_quit(self): pass def _vispy_get_native_app(self): return self # ------------------------------------------------------------------ canvas --- class WebGLGlirParser(BaseGlirParser): def __init__(self, widget=None): self._widget = widget def set_widget(self, widget): self._widget = widget def is_remote(self): return True def convert_shaders(self): return 'es2' def parse(self, commands): self._widget.send_glir_commands(commands) class CanvasBackend(BaseCanvasBackend): # args are for BaseCanvasBackend, kwargs are for us. def __init__(self, *args, **kwargs): BaseCanvasBackend.__init__(self, *args) self._widget = None p = self._process_backend_kwargs(kwargs) self._context = p.context # TODO: do something with context.config # Take the context. p.context.shared.add_ref('webgl', self) if p.context.shared.ref is self: pass # ok else: raise RuntimeError("WebGL doesn't yet support context sharing.") #store a default size before the widget is available. #then we set the default size on the widget and only use the #widget size self._default_size = p.size self._init_glir() def set_widget(self, widget): self._widget = widget self._vispy_canvas.context.shared.parser.set_widget(widget) def _init_glir(self): context = self._vispy_canvas.context context.shared.parser = WebGLGlirParser() def _reinit_widget(self): self._vispy_canvas.set_current() self._vispy_canvas.events.initialize() self._vispy_canvas.events.resize(size=(self._widget.width, self._widget.height)) self._vispy_canvas.events.draw() def _vispy_warmup(self): pass # Uncommenting these makes the backend crash. def _vispy_set_current(self): pass def _vispy_swap_buffers(self): pass def _vispy_set_title(self, title): raise NotImplementedError() def _vispy_get_fullscreen(self): # We don't want error messages to show up when the user presses # F11 to fullscreen the browser. pass def _vispy_set_fullscreen(self, fullscreen): # We don't want error messages to show up when the user presses # F11 to fullscreen the browser. pass def _vispy_get_size(self): if self._widget: return (self._widget.width, self._widget.height) else: return self._default_size def _vispy_set_size(self, w, h): if self._widget: self._widget.width = w self._widget.height = h else: self._default_size = (w, h) def _vispy_get_position(self): raise NotImplementedError() def _vispy_set_position(self, x, y): logger.warning('IPython notebook canvas cannot be repositioned.') def _vispy_set_visible(self, visible): if not visible: logger.warning('IPython notebook canvas cannot be hidden.') return if self._widget is None: self._widget = VispyWidget() self._widget.set_canvas(self._vispy_canvas) display(self._widget) def _vispy_update(self): ioloop = tornado.ioloop.IOLoop.current() ioloop.add_callback(self._draw_event) def _draw_event(self): self._vispy_canvas.set_current() self._vispy_canvas.events.draw() def _vispy_close(self): raise NotImplementedError() def _vispy_mouse_release(self, **kwargs): # HACK: override this method from the base canvas in order to # avoid breaking other backends. kwargs.update(self._vispy_mouse_data) ev = self._vispy_canvas.events.mouse_release(**kwargs) if ev is None: return self._vispy_mouse_data['press_event'] = None # TODO: this is a bit ugly, need to improve mouse button handling in # app ev._button = None self._vispy_mouse_data['buttons'] = [] self._vispy_mouse_data['last_event'] = ev return ev # Generate vispy events according to upcoming JS events _modifiers_map = { 'ctrl': keys.CONTROL, 'shift': keys.SHIFT, 'alt': keys.ALT, } def _gen_event(self, ev): if self._vispy_canvas is None: return event_type = ev['type'] key_code = ev.get('key_code', None) if key_code is None: key, key_text = None, None else: if hasattr(keys, key_code): key = getattr(keys, key_code) else: key = keys.Key(key_code) # Generate the key text to pass to the event handler. if key_code == 'SPACE': key_text = ' ' else: key_text = six.text_type(key_code) # Process modifiers. modifiers = ev.get('modifiers', None) if modifiers: modifiers = tuple([self._modifiers_map[modifier] for modifier in modifiers if modifier in self._modifiers_map]) if event_type == "mouse_move": self._vispy_mouse_move(native=ev, button=ev["button"], pos=ev["pos"], modifiers=modifiers, ) elif event_type == "mouse_press": self._vispy_mouse_press(native=ev, pos=ev["pos"], button=ev["button"], modifiers=modifiers, ) elif event_type == "mouse_release": self._vispy_mouse_release(native=ev, pos=ev["pos"], button=ev["button"], modifiers=modifiers, ) elif event_type == "mouse_wheel": self._vispy_canvas.events.mouse_wheel(native=ev, delta=ev["delta"], pos=ev["pos"], button=ev["button"], modifiers=modifiers, ) elif event_type == "key_press": self._vispy_canvas.events.key_press(native=ev, key=key, text=key_text, modifiers=modifiers, ) elif event_type == "key_release": self._vispy_canvas.events.key_release(native=ev, key=key, text=key_text, modifiers=modifiers, ) elif event_type == "resize": self._vispy_canvas.events.resize(native=ev, size=ev["size"]) elif event_type == "paint": self._vispy_canvas.events.draw() # ------------------------------------------------------------------- Timer --- class TimerBackend(BaseTimerBackend): def __init__(self, *args, **kwargs): super(TimerBackend, self).__init__(*args, **kwargs) self._timer = tornado.ioloop.PeriodicCallback( self._vispy_timer._timeout, 1000) def _vispy_start(self, interval): self._timer.callback_time = interval * 1000 self._timer.start() def _vispy_stop(self): self._timer.stop() vispy-0.4.0/vispy/app/backends/_glfw.py0000664000175000017500000003777712527672621021576 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ vispy backend for glfw. """ # Note: to install GLFW on Ubuntu: # $ git clone git://github.com/glfw/glfw.git # $ cd glfw # $ cmake -DBUILD_SHARED_LIBS=true -DGLFW_BUILD_EXAMPLES=false \ # -DGLFW_BUILD_TESTS=false -DGLFW_BUILD_DOCS=false . # $ make # $ sudo make install # $ sudo apt-get -qq install libx11-dev # On OSX, consider using brew. from __future__ import division import atexit from time import sleep import gc import os from ..base import (BaseApplicationBackend, BaseCanvasBackend, BaseTimerBackend) from ...util import keys, logger from ...util.ptime import time from ... import config USE_EGL = config['gl_backend'].lower().startswith('es') # -------------------------------------------------------------------- init --- try: from ...ext import glfw # Map native keys to vispy keys KEYMAP = { glfw.GLFW_KEY_LEFT_SHIFT: keys.SHIFT, glfw.GLFW_KEY_RIGHT_SHIFT: keys.SHIFT, glfw.GLFW_KEY_LEFT_CONTROL: keys.CONTROL, glfw.GLFW_KEY_RIGHT_CONTROL: keys.CONTROL, glfw.GLFW_KEY_LEFT_ALT: keys.ALT, glfw.GLFW_KEY_RIGHT_ALT: keys.ALT, glfw.GLFW_KEY_LEFT_SUPER: keys.META, glfw.GLFW_KEY_RIGHT_SUPER: keys.META, glfw.GLFW_KEY_LEFT: keys.LEFT, glfw.GLFW_KEY_UP: keys.UP, glfw.GLFW_KEY_RIGHT: keys.RIGHT, glfw.GLFW_KEY_DOWN: keys.DOWN, glfw.GLFW_KEY_PAGE_UP: keys.PAGEUP, glfw.GLFW_KEY_PAGE_DOWN: keys.PAGEDOWN, glfw.GLFW_KEY_INSERT: keys.INSERT, glfw.GLFW_KEY_DELETE: keys.DELETE, glfw.GLFW_KEY_HOME: keys.HOME, glfw.GLFW_KEY_END: keys.END, glfw.GLFW_KEY_ESCAPE: keys.ESCAPE, glfw.GLFW_KEY_BACKSPACE: keys.BACKSPACE, glfw.GLFW_KEY_F1: keys.F1, glfw.GLFW_KEY_F2: keys.F2, glfw.GLFW_KEY_F3: keys.F3, glfw.GLFW_KEY_F4: keys.F4, glfw.GLFW_KEY_F5: keys.F5, glfw.GLFW_KEY_F6: keys.F6, glfw.GLFW_KEY_F7: keys.F7, glfw.GLFW_KEY_F8: keys.F8, glfw.GLFW_KEY_F9: keys.F9, glfw.GLFW_KEY_F10: keys.F10, glfw.GLFW_KEY_F11: keys.F11, glfw.GLFW_KEY_F12: keys.F12, glfw.GLFW_KEY_SPACE: keys.SPACE, glfw.GLFW_KEY_ENTER: keys.ENTER, '\r': keys.ENTER, glfw.GLFW_KEY_TAB: keys.TAB, } BUTTONMAP = {glfw.GLFW_MOUSE_BUTTON_LEFT: 1, glfw.GLFW_MOUSE_BUTTON_RIGHT: 2, glfw.GLFW_MOUSE_BUTTON_MIDDLE: 3 } except Exception as exp: available, testable, why_not, which = False, False, str(exp), None else: if USE_EGL: available, testable, why_not = False, False, 'EGL not supported' which = 'glfw ' + str(glfw.__version__) else: available, testable, why_not = True, True, None which = 'glfw ' + str(glfw.__version__) MOD_KEYS = [keys.SHIFT, keys.ALT, keys.CONTROL, keys.META] _GLFW_INITIALIZED = False _VP_GLFW_ALL_WINDOWS = [] def _get_glfw_windows(): wins = list() for win in _VP_GLFW_ALL_WINDOWS: if isinstance(win, CanvasBackend): wins.append(win) return wins # -------------------------------------------------------------- capability --- capability = dict( # things that can be set by the backend title=True, size=True, position=True, show=True, vsync=True, resizable=True, decorate=True, fullscreen=True, context=True, multi_window=True, scroll=True, parent=False, always_on_top=True, ) # ------------------------------------------------------- set_configuration --- def _set_config(c): """Set gl configuration for GLFW """ glfw.glfwWindowHint(glfw.GLFW_RED_BITS, c['red_size']) glfw.glfwWindowHint(glfw.GLFW_GREEN_BITS, c['green_size']) glfw.glfwWindowHint(glfw.GLFW_BLUE_BITS, c['blue_size']) glfw.glfwWindowHint(glfw.GLFW_ALPHA_BITS, c['alpha_size']) glfw.glfwWindowHint(glfw.GLFW_ACCUM_RED_BITS, 0) glfw.glfwWindowHint(glfw.GLFW_ACCUM_GREEN_BITS, 0) glfw.glfwWindowHint(glfw.GLFW_ACCUM_BLUE_BITS, 0) glfw.glfwWindowHint(glfw.GLFW_ACCUM_ALPHA_BITS, 0) glfw.glfwWindowHint(glfw.GLFW_DEPTH_BITS, c['depth_size']) glfw.glfwWindowHint(glfw.GLFW_STENCIL_BITS, c['stencil_size']) # glfw.glfwWindowHint(glfw.GLFW_CONTEXT_VERSION_MAJOR, c['major_version']) # glfw.glfwWindowHint(glfw.GLFW_CONTEXT_VERSION_MINOR, c['minor_version']) # glfw.glfwWindowHint(glfw.GLFW_SRGB_CAPABLE, c['srgb']) glfw.glfwWindowHint(glfw.GLFW_SAMPLES, c['samples']) glfw.glfwWindowHint(glfw.GLFW_STEREO, c['stereo']) if not c['double_buffer']: raise RuntimeError('GLFW must double buffer, consider using a ' 'different backend, or using double buffering') # ------------------------------------------------------------- application --- _glfw_errors = [] def _error_callback(num, descr): _glfw_errors.append('Error %s: %s' % (num, descr)) class ApplicationBackend(BaseApplicationBackend): def __init__(self): BaseApplicationBackend.__init__(self) self._timers = list() def _add_timer(self, timer): if timer not in self._timers: self._timers.append(timer) def _vispy_get_backend_name(self): return 'Glfw' def _vispy_process_events(self): glfw.glfwPollEvents() for timer in self._timers: timer._tick() wins = _get_glfw_windows() for win in wins: if win._needs_draw: win._needs_draw = False win._on_draw() def _vispy_run(self): wins = _get_glfw_windows() while all(w._id is not None and not glfw.glfwWindowShouldClose(w._id) for w in wins): self._vispy_process_events() self._vispy_quit() # to clean up def _vispy_quit(self): # Close windows wins = _get_glfw_windows() for win in wins: if win._vispy_canvas is not None: win._vispy_canvas.close() # tear down timers for timer in self._timers: timer._vispy_stop() self._timers = [] def _vispy_get_native_app(self): global _GLFW_INITIALIZED if not _GLFW_INITIALIZED: cwd = os.getcwd() glfw.glfwSetErrorCallback(_error_callback) try: if not glfw.glfwInit(): # only ever call once raise OSError('Could not init glfw:\n%r' % _glfw_errors) finally: os.chdir(cwd) glfw.glfwSetErrorCallback(0) atexit.register(glfw.glfwTerminate) _GLFW_INITIALIZED = True return glfw # ------------------------------------------------------------------ canvas --- class CanvasBackend(BaseCanvasBackend): """ Glfw backend for Canvas abstract class.""" # args are for BaseCanvasBackend, kwargs are for us. def __init__(self, *args, **kwargs): BaseCanvasBackend.__init__(self, *args) p = self._process_backend_kwargs(kwargs) self._initialized = False # Deal with config _set_config(p.context.config) # Deal with context p.context.shared.add_ref('glfw', self) if p.context.shared.ref is self: share = None else: share = p.context.shared.ref._id glfw.glfwWindowHint(glfw.GLFW_REFRESH_RATE, 0) # highest possible glfw.glfwSwapInterval(1 if p.vsync else 0) glfw.glfwWindowHint(glfw.GLFW_RESIZABLE, int(p.resizable)) glfw.glfwWindowHint(glfw.GLFW_DECORATED, int(p.decorate)) glfw.glfwWindowHint(glfw.GLFW_VISIBLE, 0) # start out hidden glfw.glfwWindowHint(glfw.GLFW_FLOATING, int(p.always_on_top)) if p.fullscreen is not False: self._fullscreen = True if p.fullscreen is True: monitor = glfw.glfwGetPrimaryMonitor() else: monitor = glfw.glfwGetMonitors() if p.fullscreen >= len(monitor): raise ValueError('fullscreen must be <= %s' % len(monitor)) monitor = monitor[p.fullscreen] use_size = glfw.glfwGetVideoMode(monitor)[:2] if use_size != tuple(p.size): logger.debug('Requested size %s, will be ignored to ' 'use fullscreen mode %s' % (p.size, use_size)) size = use_size else: self._fullscreen = False monitor = None size = p.size self._id = glfw.glfwCreateWindow(width=size[0], height=size[1], title=p.title, monitor=monitor, share=share) if not self._id: raise RuntimeError('Could not create window') _VP_GLFW_ALL_WINDOWS.append(self) self._mod = list() # Register callbacks glfw.glfwSetWindowRefreshCallback(self._id, self._on_draw) glfw.glfwSetWindowSizeCallback(self._id, self._on_resize) glfw.glfwSetKeyCallback(self._id, self._on_key_press) glfw.glfwSetMouseButtonCallback(self._id, self._on_mouse_button) glfw.glfwSetScrollCallback(self._id, self._on_mouse_scroll) glfw.glfwSetCursorPosCallback(self._id, self._on_mouse_motion) glfw.glfwSetWindowCloseCallback(self._id, self._on_close) self._vispy_canvas_ = None self._needs_draw = False self._vispy_canvas.set_current() if p.position is not None: self._vispy_set_position(*p.position) if p.show: glfw.glfwShowWindow(self._id) # Init self._initialized = True self._vispy_canvas.set_current() self._vispy_canvas.events.initialize() def _vispy_warmup(self): etime = time() + 0.25 while time() < etime: sleep(0.01) self._vispy_canvas.set_current() self._vispy_canvas.app.process_events() def _vispy_set_current(self): if self._id is None: return # Make this the current context glfw.glfwMakeContextCurrent(self._id) def _vispy_swap_buffers(self): if self._id is None: return # Swap front and back buffer glfw.glfwSwapBuffers(self._id) def _vispy_set_title(self, title): if self._id is None: return # Set the window title. Has no effect for widgets glfw.glfwSetWindowTitle(self._id, title) def _vispy_set_size(self, w, h): if self._id is None: return # Set size of the widget or window glfw.glfwSetWindowSize(self._id, w, h) def _vispy_set_position(self, x, y): if self._id is None: return # Set position of the widget or window. May have no effect for widgets glfw.glfwSetWindowPos(self._id, x, y) def _vispy_set_visible(self, visible): # Show or hide the window or widget if self._id is None: return if visible: glfw.glfwShowWindow(self._id) # this ensures that the show takes effect self._vispy_update() else: glfw.glfwHideWindow(self._id) def _vispy_set_fullscreen(self, fullscreen): logger.warn('Cannot change fullscreen mode for GLFW backend') def _vispy_update(self): # Invoke a redraw, passing it on to the canvas if self._vispy_canvas is None or self._id is None: return # Mark that this window wants to be drawn on the next loop iter self._needs_draw = True def _vispy_close(self): # Force the window or widget to shut down if self._id is not None: self._vispy_canvas = None # glfw.glfwSetWindowShouldClose() # Does not really cause a close self._vispy_set_visible(False) self._id, id_ = None, self._id glfw.glfwDestroyWindow(id_) gc.collect() # help ensure context gets destroyed def _vispy_get_size(self): if self._id is None: return w, h = glfw.glfwGetWindowSize(self._id) return w, h def _vispy_get_physical_size(self): if self._id is None: return w, h = glfw.glfwGetFramebufferSize(self._id) return w, h def _vispy_get_position(self): if self._id is None: return x, y = glfw.glfwGetWindowPos(self._id) return x, y def _vispy_get_fullscreen(self): return self._fullscreen ########################################## # Notify vispy of events triggered by GLFW def _on_resize(self, _id, w, h): if self._vispy_canvas is None: return self._vispy_canvas.events.resize( size=(w, h), physical_size=self._vispy_get_physical_size()) def _on_close(self, _id): if self._vispy_canvas is None: return self._vispy_canvas.close() def _on_draw(self, _id=None): if self._vispy_canvas is None or self._id is None: return self._vispy_canvas.set_current() self._vispy_canvas.events.draw(region=None) # (0, 0, w, h)) def _on_mouse_button(self, _id, button, action, mod): if self._vispy_canvas is None and self._id is not None: return pos = glfw.glfwGetCursorPos(self._id) if button < 3: # Mouse click event button = BUTTONMAP.get(button, 0) if action == glfw.GLFW_PRESS: fun = self._vispy_mouse_press elif action == glfw.GLFW_RELEASE: fun = self._vispy_mouse_release else: return fun(pos=pos, button=button, modifiers=self._mod) def _on_mouse_scroll(self, _id, x_off, y_off): if self._vispy_canvas is None and self._id is not None: return pos = glfw.glfwGetCursorPos(self._id) delta = (float(x_off), float(y_off)) self._vispy_canvas.events.mouse_wheel(pos=pos, delta=delta, modifiers=self._mod) def _on_mouse_motion(self, _id, x, y): if self._vispy_canvas is None: return self._vispy_mouse_move(pos=(x, y), modifiers=self._mod) def _on_key_press(self, _id, key, scancode, action, mod): if self._vispy_canvas is None: return key, text = self._process_key(key) if action == glfw.GLFW_PRESS: fun = self._vispy_canvas.events.key_press down = True elif action == glfw.GLFW_RELEASE: fun = self._vispy_canvas.events.key_release down = False else: return self._process_mod(key, down=down) fun(key=key, text=text, modifiers=self._mod) def _process_key(self, key): if 32 <= key <= 127: return keys.Key(chr(key)), chr(key) elif key in KEYMAP: return KEYMAP[key], '' else: return None, '' def _process_mod(self, key, down): """Process (possible) keyboard modifiers GLFW provides "mod" with many callbacks, but not (critically) the scroll callback, so we keep track on our own here. """ if key in MOD_KEYS: if down: if key not in self._mod: self._mod.append(key) elif key in self._mod: self._mod.pop(self._mod.index(key)) return self._mod # ------------------------------------------------------------------- timer --- class TimerBackend(BaseTimerBackend): def __init__(self, vispy_timer): BaseTimerBackend.__init__(self, vispy_timer) vispy_timer._app._backend._add_timer(self) self._vispy_stop() def _vispy_start(self, interval): self._interval = interval self._next_time = time() + self._interval def _vispy_stop(self): self._next_time = float('inf') def _tick(self): if time() >= self._next_time: self._vispy_timer._timeout() self._next_time = time() + self._interval vispy-0.4.0/vispy/app/backends/__init__.py0000664000175000017500000000325212527672621022214 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ vispy.app.backends The backend modules are dynamically imported when needed. This module defines a small description of each supported backend, so that for instance we can test whether the GUI toolkit for a backend is already imported. This stuff is mostly used in the Application.use method. """ # Define backends: name, vispy.app.backends.xxx module, native module name. # This is the order in which they are attempted to be imported. CORE_BACKENDS = [ ('PyQt4', '_pyqt4', 'PyQt4'), ('PyQt5', '_pyqt5', 'PyQt5'), ('PySide', '_pyside', 'PySide'), ('Pyglet', '_pyglet', 'pyglet'), ('Glfw', '_glfw', 'vispy.ext.glfw'), ('SDL2', '_sdl2', 'sdl2'), ('wx', '_wx', 'wx'), ('EGL', '_egl', 'vispy.ext.egl'), ] # Whereas core backends really represents libraries that can create a # canvas, the pseudo backends act more like a proxy. PSEUDO_BACKENDS = [ # ('ipynb_vnc', '_ipynb_vnc', None), # ('ipynb_static', '_ipynb_static', None), ('ipynb_webgl', '_ipynb_webgl', None), ('_test', '_test', 'vispy.app.backends._test'), # add one that will fail ] # Combine BACKENDS = CORE_BACKENDS + PSEUDO_BACKENDS # Get list of backend names BACKEND_NAMES = [b[0].lower() for b in BACKENDS] # Map of the lowercase backend names to the backend descriptions above # so that we can look up its properties if we only have a name. BACKENDMAP = dict([(be[0].lower(), be) for be in BACKENDS]) # List of attempted backends. For logging. TRIED_BACKENDS = [] # Flag for _pyside, _pyqt4 and _qt modules to communicate. qt_lib = None vispy-0.4.0/vispy/app/backends/_ipynb_static.py0000664000175000017500000001525312527672621023310 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ vispy backend for the IPython notebook (static approach). We aim to have: * ipynb_static - export visualization to a static notebook * ipynb_vnc - vnc-approach: render in Python, send result to JS as png * ipynb_webgl - send gl commands to JS and execute in webgl context """ from __future__ import division from ..base import BaseApplicationBackend, BaseCanvasBackend from .. import Application, Canvas from ...util import logger # Imports for screenshot from ...gloo.util import _screenshot from ...io import _make_png from base64 import b64encode # -------------------------------------------------------------------- init --- capability = dict( # things that can be set by the backend title=True, # But it only applies to the dummy window :P size=True, # We cannot possibly say we dont, because Canvas always sets it position=True, # Dito show=True, # Note: we don't alow this, but all scripts call show ... vsync=False, resizable=True, # Yes, you can set to not be resizable (it always is) decorate=False, fullscreen=False, context=True, multi_window=True, scroll=True, parent=False, always_on_top=False, ) def _set_config(c): _app.backend_module._set_config(c) # Create our "backend" backend; The toolkit that is going to provide a # canvas (e.g. OpenGL context) so we can render images. # Note that if IPython has already loaded a GUI backend, vispy is # probably going to use that as well, because it prefers loaded backends. try: # Explicitly use default (avoid using test-app) _app = Application('default') except Exception: _msg = 'ipynb_static backend relies on a core backend' available, testable, why_not, which = False, False, _msg, None else: # Try importing IPython try: from IPython.display import display_png except Exception as exp: available, testable, why_not, which = False, False, str(exp), None else: available, testable, why_not = True, False, None which = _app.backend_module.which # Use that backend's shared context KEYMAP = _app.backend_module.KEYMAP # ------------------------------------------------------------- application --- # todo: maybe trigger something in JS on any of these methods? class ApplicationBackend(BaseApplicationBackend): def __init__(self): BaseApplicationBackend.__init__(self) self._backend2 = _app._backend def _vispy_get_backend_name(self): realname = self._backend2._vispy_get_backend_name() return 'ipynb_static (via %s)' % realname def _vispy_process_events(self): return self._backend2._vispy_process_events() def _vispy_run(self): pass # We run in IPython, so we don't run! #return self._backend2._vispy_run() def _vispy_quit(self): return self._backend2._vispy_quit() def _vispy_get_native_app(self): return self._backend2._vispy_get_native_app() # ------------------------------------------------------------------ canvas --- class CanvasBackend(BaseCanvasBackend): # args are for BaseCanvasBackend, kwargs are for us. def __init__(self, *args, **kwargs): BaseCanvasBackend.__init__(self, *args) self._initialized = False # Test kwargs # if kwargs['position']: # raise RuntimeError('ipynb_static Canvas is not positionable') if not kwargs['decorate']: raise RuntimeError('ipynb_static Canvas is not decoratable') if kwargs['vsync']: raise RuntimeError('ipynb_static Canvas does not support vsync') if kwargs['fullscreen']: raise RuntimeError('ipynb_static Canvas does not support ' 'fullscreen') # Create real canvas. It is a backend to this backend kwargs.pop('vispy_canvas', None) kwargs['autoswap'] = False canvas = Canvas(app=_app, **kwargs) # Pass kwargs to underlying canvas self._backend2 = canvas.native # Connect to events of canvas to keep up to date with size and draw canvas.events.draw.connect(self._on_draw) canvas.events.resize.connect(self._on_resize) # Show the widget canvas.show() # todo: hide that canvas # Raw PNG that will be displayed on canvas.show() self._im = "" def _vispy_warmup(self): return self._backend2._vispy_warmup() def _vispy_set_current(self): return self._backend2._vispy_set_current() def _vispy_swap_buffers(self): return self._backend2._vispy_swap_buffers() def _vispy_set_title(self, title): return self._backend2._vispy_set_title(title) #logger.warn('IPython notebook canvas has not title.') def _vispy_set_size(self, w, h): return self._backend2._vispy_set_size(w, h) def _vispy_set_position(self, x, y): logger.warn('IPython notebook canvas cannot be repositioned.') def _vispy_set_visible(self, visible): #self._backend2._vispy_set_visible(visible) if not visible: logger.warn('IPython notebook canvas cannot be hidden.') else: self._vispy_update() self._vispy_canvas.app.process_events() self._vispy_close() display_png(self._im, raw=True) def _vispy_update(self): return self._backend2._vispy_update() def _vispy_close(self): return self._backend2._vispy_close() # todo: can we close on IPython side? def _vispy_get_position(self): return 0, 0 def _vispy_get_size(self): return self._backend2._vispy_get_size() def _on_resize(self, event=None): # Event handler that is called by the underlying canvas if self._vispy_canvas is None: return size = self._backend2._vispy_get_size() self._vispy_canvas.events.resize(size=size) def _on_draw(self, event=None): # Event handler that is called by the underlying canvas if self._vispy_canvas is None: return # Handle initialization if not self._initialized: self._initialized = True self._vispy_canvas.events.initialize() self._on_resize() # Normal behavior self._vispy_canvas.set_current() self._vispy_canvas.events.draw(region=None) # Generate base64 encoded PNG string self._gen_png() def _gen_png(self): # Take the screenshot screenshot = _screenshot() # Convert to PNG png = _make_png(screenshot) # Encode base64 self._im = b64encode(png) vispy-0.4.0/vispy/app/backends/_qt.py0000664000175000017500000005315012527672621021242 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Base code for the Qt backends. Note that this is *not* (anymore) a backend by itself! One has to explicitly use either PySide, PyQt4 or PyQt5. Note that the automatic backend selection prefers a GUI toolkit that is already imported. The _pyside, _pyqt4 and _pyqt5 modules will import * from this module, and also keep a ref to the module object. Note that if two of the backends are used, this module is actually reloaded. This is a sorts of poor mans "subclassing" to get a working version for both backends using the same code. Note that it is strongly discouraged to use the PySide/PyQt4/PyQt5 backends simultaneously. It is known to cause unpredictable behavior and segfaults. """ from __future__ import division from time import sleep, time import os import sys import atexit import ctypes from ...util import logger from ..base import (BaseApplicationBackend, BaseCanvasBackend, BaseTimerBackend) from ...util import keys from ...ext.six import text_type from ... import config from . import qt_lib USE_EGL = config['gl_backend'].lower().startswith('es') # Get platform IS_LINUX = IS_OSX = IS_WIN = IS_RPI = False if sys.platform.startswith('linux'): if os.uname()[4].startswith('arm'): IS_RPI = True else: IS_LINUX = True elif sys.platform.startswith('darwin'): IS_OSX = True elif sys.platform.startswith('win'): IS_WIN = True # -------------------------------------------------------------------- init --- def _check_imports(lib): # Make sure no conflicting libraries have been imported. libs = ['PyQt4', 'PyQt5', 'PySide'] libs.remove(lib) for lib2 in libs: lib2 += '.QtCore' if lib2 in sys.modules: raise RuntimeError("Refusing to import %s because %s is already " "imported." % (lib, lib2)) # Get what qt lib to try. This tells us wheter this module is imported # via _pyside or _pyqt4 or _pyqt5 QGLWidget = object if qt_lib == 'pyqt4': _check_imports('PyQt4') if not USE_EGL: from PyQt4.QtOpenGL import QGLWidget, QGLFormat from PyQt4 import QtGui, QtCore QWidget, QApplication = QtGui.QWidget, QtGui.QApplication # Compat elif qt_lib == 'pyqt5': _check_imports('PyQt5') if not USE_EGL: from PyQt5.QtOpenGL import QGLWidget, QGLFormat from PyQt5 import QtGui, QtCore, QtWidgets QWidget, QApplication = QtWidgets.QWidget, QtWidgets.QApplication # Compat elif qt_lib == 'pyside': _check_imports('PySide') if not USE_EGL: from PySide.QtOpenGL import QGLWidget, QGLFormat from PySide import QtGui, QtCore QWidget, QApplication = QtGui.QWidget, QtGui.QApplication # Compat elif qt_lib: raise RuntimeError("Invalid value for qt_lib %r." % qt_lib) else: raise RuntimeError("Module backends._qt should not be imported directly.") # todo: add support for distinguishing left and right shift/ctrl/alt keys. # Linux scan codes: (left, right) # Shift 50, 62 # Ctrl 37, 105 # Alt 64, 108 KEYMAP = { QtCore.Qt.Key_Shift: keys.SHIFT, QtCore.Qt.Key_Control: keys.CONTROL, QtCore.Qt.Key_Alt: keys.ALT, QtCore.Qt.Key_AltGr: keys.ALT, QtCore.Qt.Key_Meta: keys.META, QtCore.Qt.Key_Left: keys.LEFT, QtCore.Qt.Key_Up: keys.UP, QtCore.Qt.Key_Right: keys.RIGHT, QtCore.Qt.Key_Down: keys.DOWN, QtCore.Qt.Key_PageUp: keys.PAGEUP, QtCore.Qt.Key_PageDown: keys.PAGEDOWN, QtCore.Qt.Key_Insert: keys.INSERT, QtCore.Qt.Key_Delete: keys.DELETE, QtCore.Qt.Key_Home: keys.HOME, QtCore.Qt.Key_End: keys.END, QtCore.Qt.Key_Escape: keys.ESCAPE, QtCore.Qt.Key_Backspace: keys.BACKSPACE, QtCore.Qt.Key_F1: keys.F1, QtCore.Qt.Key_F2: keys.F2, QtCore.Qt.Key_F3: keys.F3, QtCore.Qt.Key_F4: keys.F4, QtCore.Qt.Key_F5: keys.F5, QtCore.Qt.Key_F6: keys.F6, QtCore.Qt.Key_F7: keys.F7, QtCore.Qt.Key_F8: keys.F8, QtCore.Qt.Key_F9: keys.F9, QtCore.Qt.Key_F10: keys.F10, QtCore.Qt.Key_F11: keys.F11, QtCore.Qt.Key_F12: keys.F12, QtCore.Qt.Key_Space: keys.SPACE, QtCore.Qt.Key_Enter: keys.ENTER, QtCore.Qt.Key_Return: keys.ENTER, QtCore.Qt.Key_Tab: keys.TAB, } BUTTONMAP = {0: 0, 1: 1, 2: 2, 4: 3, 8: 4, 16: 5} # Properly log Qt messages # Also, ignore spam about tablet input def message_handler(msg_type, msg): if msg == ("QCocoaView handleTabletEvent: This tablet device is " "unknown (received no proximity event for it). Discarding " "event."): return else: logger.warning(msg) try: QtCore.qInstallMsgHandler(message_handler) except AttributeError: QtCore.qInstallMessageHandler(message_handler) # PyQt5 # -------------------------------------------------------------- capability --- capability = dict( # things that can be set by the backend title=True, size=True, position=True, show=True, vsync=True, resizable=True, decorate=True, fullscreen=True, context=True, multi_window=True, scroll=True, parent=True, always_on_top=True, ) # ------------------------------------------------------- set_configuration --- def _set_config(c): """Set the OpenGL configuration""" glformat = QGLFormat() glformat.setRedBufferSize(c['red_size']) glformat.setGreenBufferSize(c['green_size']) glformat.setBlueBufferSize(c['blue_size']) glformat.setAlphaBufferSize(c['alpha_size']) glformat.setAccum(False) glformat.setRgba(True) glformat.setDoubleBuffer(True if c['double_buffer'] else False) glformat.setDepth(True if c['depth_size'] else False) glformat.setDepthBufferSize(c['depth_size'] if c['depth_size'] else 0) glformat.setStencil(True if c['stencil_size'] else False) glformat.setStencilBufferSize(c['stencil_size'] if c['stencil_size'] else 0) glformat.setSampleBuffers(True if c['samples'] else False) glformat.setSamples(c['samples'] if c['samples'] else 0) glformat.setStereo(c['stereo']) return glformat # ------------------------------------------------------------- application --- class ApplicationBackend(BaseApplicationBackend): def __init__(self): BaseApplicationBackend.__init__(self) def _vispy_get_backend_name(self): name = QtCore.__name__.split('.')[0] return name def _vispy_process_events(self): app = self._vispy_get_native_app() app.flush() app.processEvents() def _vispy_run(self): app = self._vispy_get_native_app() if hasattr(app, '_in_event_loop') and app._in_event_loop: pass # Already in event loop else: return app.exec_() def _vispy_quit(self): return self._vispy_get_native_app().quit() def _vispy_get_native_app(self): # Get native app in save way. Taken from guisupport.py app = QApplication.instance() if app is None: app = QApplication(['']) # Store so it won't be deleted, but not on a vispy object, # or an application may produce error when closed. QtGui._qApp = app # Return return app # ------------------------------------------------------------------ canvas --- class QtBaseCanvasBackend(BaseCanvasBackend): """Base functionality of Qt backend. No OpenGL Stuff.""" # args are for BaseCanvasBackend, kwargs are for us. def __init__(self, *args, **kwargs): BaseCanvasBackend.__init__(self, *args) # Maybe to ensure that exactly all arguments are passed? p = self._process_backend_kwargs(kwargs) self._initialized = False # Init in desktop GL or EGL way self._init_specific(p, kwargs) assert self._initialized self.setMouseTracking(True) self._vispy_set_title(p.title) self._vispy_set_size(*p.size) if p.fullscreen is not False: if p.fullscreen is not True: logger.warning('Cannot specify monitor number for Qt ' 'fullscreen, using default') self._fullscreen = True else: self._fullscreen = False if not p.resizable: self.setFixedSize(self.size()) if p.position is not None: self._vispy_set_position(*p.position) if p.show: self._vispy_set_visible(True) # Qt supports OS double-click events, so we set this here to # avoid double events self._double_click_supported = True def _vispy_warmup(self): etime = time() + 0.25 while time() < etime: sleep(0.01) self._vispy_canvas.set_current() self._vispy_canvas.app.process_events() def _vispy_set_title(self, title): # Set the window title. Has no effect for widgets if self._vispy_canvas is None: return self.setWindowTitle(title) def _vispy_set_size(self, w, h): # Set size of the widget or window self.resize(w, h) def _vispy_set_position(self, x, y): # Set location of the widget or window. May have no effect for widgets self.move(x, y) def _vispy_set_visible(self, visible): # Show or hide the window or widget if visible: if self._fullscreen: self.showFullScreen() else: self.showNormal() else: self.hide() def _vispy_set_fullscreen(self, fullscreen): self._fullscreen = bool(fullscreen) self._vispy_set_visible(True) def _vispy_get_fullscreen(self): return self._fullscreen def _vispy_update(self): if self._vispy_canvas is None: return # Invoke a redraw self.update() def _vispy_get_position(self): g = self.geometry() return g.x(), g.y() def _vispy_get_size(self): g = self.geometry() return g.width(), g.height() def sizeHint(self): return self.size() def mousePressEvent(self, ev): if self._vispy_canvas is None: return self._vispy_mouse_press( native=ev, pos=(ev.pos().x(), ev.pos().y()), button=BUTTONMAP.get(ev.button(), 0), modifiers=self._modifiers(ev), ) def mouseReleaseEvent(self, ev): if self._vispy_canvas is None: return self._vispy_mouse_release( native=ev, pos=(ev.pos().x(), ev.pos().y()), button=BUTTONMAP[ev.button()], modifiers=self._modifiers(ev), ) def mouseDoubleClickEvent(self, ev): if self._vispy_canvas is None: return self._vispy_mouse_double_click( native=ev, pos=(ev.pos().x(), ev.pos().y()), button=BUTTONMAP.get(ev.button(), 0), modifiers=self._modifiers(ev), ) def mouseMoveEvent(self, ev): if self._vispy_canvas is None: return self._vispy_mouse_move( native=ev, pos=(ev.pos().x(), ev.pos().y()), modifiers=self._modifiers(ev), ) def wheelEvent(self, ev): if self._vispy_canvas is None: return # Get scrolling deltax, deltay = 0.0, 0.0 if hasattr(ev, 'orientation'): if ev.orientation == QtCore.Qt.Horizontal: deltax = ev.delta() / 120.0 else: deltay = ev.delta() / 120.0 else: # PyQt5 delta = ev.angleDelta() deltax, deltay = delta.x() / 120.0, delta.y() / 120.0 # Emit event self._vispy_canvas.events.mouse_wheel( native=ev, delta=(deltax, deltay), pos=(ev.pos().x(), ev.pos().y()), modifiers=self._modifiers(ev), ) def keyPressEvent(self, ev): self._keyEvent(self._vispy_canvas.events.key_press, ev) def keyReleaseEvent(self, ev): self._keyEvent(self._vispy_canvas.events.key_release, ev) def _keyEvent(self, func, ev): # evaluates the keycode of qt, and transform to vispy key. key = int(ev.key()) if key in KEYMAP: key = KEYMAP[key] elif key >= 32 and key <= 127: key = keys.Key(chr(key)) else: key = None mod = self._modifiers(ev) func(native=ev, key=key, text=text_type(ev.text()), modifiers=mod) def _modifiers(self, event): # Convert the QT modifier state into a tuple of active modifier keys. mod = () qtmod = event.modifiers() for q, v in ([QtCore.Qt.ShiftModifier, keys.SHIFT], [QtCore.Qt.ControlModifier, keys.CONTROL], [QtCore.Qt.AltModifier, keys.ALT], [QtCore.Qt.MetaModifier, keys.META]): if q & qtmod: mod += (v,) return mod _EGL_DISPLAY = None egl = None # todo: Make work on Windows # todo: Make work without readpixels on Linux? # todo: Make work on OSX? # todo: Make work on Raspberry Pi! class CanvasBackendEgl(QtBaseCanvasBackend, QWidget): def _init_specific(self, p, kwargs): # Initialize egl. Note that we only import egl if needed. global _EGL_DISPLAY global egl if egl is None: from ...ext import egl as _egl egl = _egl # Use MESA driver on Linux if IS_LINUX and not IS_RPI: os.environ['EGL_SOFTWARE'] = 'true' # Create and init display _EGL_DISPLAY = egl.eglGetDisplay() CanvasBackendEgl._EGL_VERSION = egl.eglInitialize(_EGL_DISPLAY) atexit.register(egl.eglTerminate, _EGL_DISPLAY) # Deal with context p.context.shared.add_ref('qt-egl', self) if p.context.shared.ref is self: self._native_config = c = egl.eglChooseConfig(_EGL_DISPLAY)[0] self._native_context = egl.eglCreateContext(_EGL_DISPLAY, c, None) else: self._native_config = p.context.shared.ref._native_config self._native_context = p.context.shared.ref._native_context # Init widget if p.always_on_top or not p.decorate: hint = 0 hint |= 0 if p.decorate else QtCore.Qt.FramelessWindowHint hint |= QtCore.Qt.WindowStaysOnTopHint if p.always_on_top else 0 else: hint = QtCore.Qt.Widget # can also be a window type QWidget.__init__(self, p.parent, hint) if 0: # IS_LINUX or IS_RPI: self.setAutoFillBackground(False) self.setAttribute(QtCore.Qt.WA_NoSystemBackground, True) self.setAttribute(QtCore.Qt.WA_OpaquePaintEvent, True) elif IS_WIN: self.setAttribute(QtCore.Qt.WA_PaintOnScreen, True) self.setAutoFillBackground(False) # Init surface w = self.get_window_id() self._surface = egl.eglCreateWindowSurface(_EGL_DISPLAY, c, w) self.initializeGL() self._initialized = True def get_window_id(self): """ Get the window id of a PySide Widget. Might also work for PyQt4. """ # Get Qt win id winid = self.winId() # On Linux this is it if IS_RPI: nw = (ctypes.c_int * 3)(winid, self.width(), self.height()) return ctypes.pointer(nw) elif IS_LINUX: return int(winid) # Is int on PySide, but sip.voidptr on PyQt # Get window id from stupid capsule thingy # http://translate.google.com/translate?hl=en&sl=zh-CN&u=http://www.cnb #logs.com/Shiren-Y/archive/2011/04/06/2007288.html&prev=/search%3Fq%3Dp # yside%2Bdirectx%26client%3Dfirefox-a%26hs%3DIsJ%26rls%3Dorg.mozilla:n #l:official%26channel%3Dfflb%26biw%3D1366%26bih%3D614 # Prepare ctypes.pythonapi.PyCapsule_GetName.restype = ctypes.c_char_p ctypes.pythonapi.PyCapsule_GetName.argtypes = [ctypes.py_object] ctypes.pythonapi.PyCapsule_GetPointer.restype = ctypes.c_void_p ctypes.pythonapi.PyCapsule_GetPointer.argtypes = [ctypes.py_object, ctypes.c_char_p] # Extract handle from capsule thingy name = ctypes.pythonapi.PyCapsule_GetName(winid) handle = ctypes.pythonapi.PyCapsule_GetPointer(winid, name) return handle def _vispy_close(self): # Destroy EGL surface if self._surface is not None: egl.eglDestroySurface(_EGL_DISPLAY, self._surface) self._surface = None # Force the window or widget to shut down self.close() def _vispy_set_current(self): egl.eglMakeCurrent(_EGL_DISPLAY, self._surface, self._surface, self._native_context) def _vispy_swap_buffers(self): egl.eglSwapBuffers(_EGL_DISPLAY, self._surface) def initializeGL(self): self._vispy_canvas.set_current() self._vispy_canvas.events.initialize() def resizeEvent(self, event): w, h = event.size().width(), event.size().height() self._vispy_canvas.events.resize(size=(w, h)) def paintEvent(self, event): self._vispy_canvas.events.draw(region=None) if IS_LINUX or IS_RPI: # Arg, cannot get GL to draw to the widget, so we take a # screenshot and draw that for now ... # Further, QImage keeps a ref to the data that we pass, so # we need to use a static buffer to prevent memory leakage from vispy import gloo import numpy as np if not hasattr(self, '_gl_buffer'): self._gl_buffer = np.ones((3000 * 3000 * 4), np.uint8) * 255 # Take screenshot and turn into RGB QImage im = gloo.read_pixels() sze = im.shape[0] * im.shape[1] self._gl_buffer[0:0+sze*4:4] = im[:, :, 2].ravel() self._gl_buffer[1:0+sze*4:4] = im[:, :, 1].ravel() self._gl_buffer[2:2+sze*4:4] = im[:, :, 0].ravel() img = QtGui.QImage(self._gl_buffer, im.shape[1], im.shape[0], QtGui.QImage.Format_RGB32) # Paint the image painter = QtGui.QPainter() painter.begin(self) rect = QtCore.QRect(0, 0, self.width(), self.height()) painter.drawImage(rect, img) painter.end() def paintEngine(self): if IS_LINUX and not IS_RPI: # For now we are drawing a screenshot return QWidget.paintEngine(self) else: return None # Disable Qt's native drawing system class CanvasBackendDesktop(QtBaseCanvasBackend, QGLWidget): def _init_specific(self, p, kwargs): # Deal with config glformat = _set_config(p.context.config) glformat.setSwapInterval(1 if p.vsync else 0) # Deal with context widget = kwargs.pop('shareWidget', None) or self p.context.shared.add_ref('qt', widget) if p.context.shared.ref is widget: if widget is self: widget = None # QGLWidget does not accept self ;) else: widget = p.context.shared.ref if 'shareWidget' in kwargs: raise RuntimeError('Cannot use vispy to share context and ' 'use built-in shareWidget.') # first arg can be glformat, or a gl context if p.always_on_top or not p.decorate: hint = 0 hint |= 0 if p.decorate else QtCore.Qt.FramelessWindowHint hint |= QtCore.Qt.WindowStaysOnTopHint if p.always_on_top else 0 else: hint = QtCore.Qt.Widget # can also be a window type QGLWidget.__init__(self, glformat, p.parent, widget, hint) self._initialized = True if not self.isValid(): raise RuntimeError('context could not be created') self.setAutoBufferSwap(False) # to make consistent with other backends self.setFocusPolicy(QtCore.Qt.WheelFocus) def _vispy_close(self): # Force the window or widget to shut down self.close() self.doneCurrent() self.context().reset() def _vispy_set_current(self): if self._vispy_canvas is None: return # todo: can we get rid of this now? if self.isValid(): self.makeCurrent() def _vispy_swap_buffers(self): # Swap front and back buffer if self._vispy_canvas is None: return self.swapBuffers() def initializeGL(self): if self._vispy_canvas is None: return self._vispy_canvas.events.initialize() def resizeGL(self, w, h): if self._vispy_canvas is None: return self._vispy_canvas.events.resize(size=(w, h)) def paintGL(self): if self._vispy_canvas is None: return # (0, 0, self.width(), self.height())) self._vispy_canvas.set_current() self._vispy_canvas.events.draw(region=None) # Select CanvasBackend if USE_EGL: CanvasBackend = CanvasBackendEgl else: CanvasBackend = CanvasBackendDesktop # ------------------------------------------------------------------- timer --- class TimerBackend(BaseTimerBackend, QtCore.QTimer): def __init__(self, vispy_timer): # Make sure there is an app app = ApplicationBackend() app._vispy_get_native_app() # Init BaseTimerBackend.__init__(self, vispy_timer) QtCore.QTimer.__init__(self) self.timeout.connect(self._vispy_timeout) def _vispy_start(self, interval): self.start(interval * 1000.) def _vispy_stop(self): self.stop() def _vispy_timeout(self): self._vispy_timer._timeout() vispy-0.4.0/vispy/app/backends/ipython/0000775000175000017500000000000012527674621021575 5ustar larsonerlarsoner00000000000000vispy-0.4.0/vispy/app/backends/ipython/__init__.py0000775000175000017500000000030012510536123023664 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2014, 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from ._widget import VispyWidget # NOQA vispy-0.4.0/vispy/app/backends/ipython/_widget.py0000664000175000017500000000673712510536123023570 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2014, 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. try: from IPython.html.widgets import DOMWidget from IPython.utils.traitlets import Unicode, Int, Bool except Exception as exp: # Init dummy objects needed to import this module withour errors. # These are all overwritten with imports from IPython (on success) DOMWidget = object Unicode = Int = Float = Bool = lambda *args, **kwargs: None available, testable, why_not, which = False, False, str(exp), None else: available, testable, why_not, which = True, False, None, None from vispy.app.backends._ipynb_util import create_glir_message from vispy.app import Timer # ---------------------------------------------------------- IPython Widget --- def _stop_timers(canvas): """Stop all timers in a canvas.""" for attr in dir(canvas): try: attr_obj = getattr(canvas, attr) except NotImplementedError: # This try/except is needed because canvas.position raises # an error (it is not implemented in this backend). attr_obj = None if isinstance(attr_obj, Timer): attr_obj.stop() class VispyWidget(DOMWidget): _view_name = Unicode("VispyView", sync=True) _view_module = Unicode('/nbextensions/vispy/webgl-backend.js', sync=True) #height/width of the widget is managed by IPython. #it's a string and can be anything valid in CSS. #here we only manage the size of the viewport. width = Int(sync=True) height = Int(sync=True) resizable = Bool(value=True, sync=True) def __init__(self, **kwargs): super(VispyWidget, self).__init__(**kwargs) self.on_msg(self.events_received) self.canvas = None self.canvas_backend = None self.gen_event = None def set_canvas(self, canvas): self.width, self.height = canvas._backend._default_size self.canvas = canvas self.canvas_backend = self.canvas._backend self.canvas_backend.set_widget(self) self.gen_event = self.canvas_backend._gen_event #setup the backend widget then. def events_received(self, _, msg): if msg['msg_type'] == 'init': self.canvas_backend._reinit_widget() elif msg['msg_type'] == 'events': events = msg['contents'] for ev in events: self.gen_event(ev) elif msg['msg_type'] == 'status': if msg['contents'] == 'removed': # Stop all timers associated to the widget. _stop_timers(self.canvas_backend._vispy_canvas) def send_glir_commands(self, commands): # TODO: check whether binary websocket is available (ipython >= 3) # Until IPython 3.0 is released, use base64. array_serialization = 'base64' # array_serialization = 'binary' if array_serialization == 'base64': msg = create_glir_message(commands, 'base64') msg['array_serialization'] = 'base64' self.send(msg) elif array_serialization == 'binary': msg = create_glir_message(commands, 'binary') msg['array_serialization'] = 'binary' # Remove the buffers from the JSON message: they will be sent # independently via binary WebSocket. buffers = msg.pop('buffers') self.comm.send({"method": "custom", "content": msg}, buffers=buffers) vispy-0.4.0/vispy/app/backends/_sdl2.py0000664000175000017500000003543412527672621021467 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ vispy backend for sdl2. """ from __future__ import division import atexit import ctypes from time import sleep import warnings import gc from ..base import (BaseApplicationBackend, BaseCanvasBackend, BaseTimerBackend) from ...util import keys, logger from ...util.ptime import time from ... import config USE_EGL = config['gl_backend'].lower().startswith('es') # -------------------------------------------------------------------- init --- try: with warnings.catch_warnings(record=True): # can throw warnings import sdl2 import sdl2.ext # Map native keys to vispy keys KEYMAP = { # http://www.ginkgobitter.org/sdl/?SDL_Keycode sdl2.SDLK_LSHIFT: keys.SHIFT, sdl2.SDLK_RSHIFT: keys.SHIFT, sdl2.SDLK_LCTRL: keys.CONTROL, sdl2.SDLK_RCTRL: keys.CONTROL, sdl2.SDLK_LALT: keys.ALT, sdl2.SDLK_RALT: keys.ALT, sdl2.SDLK_LGUI: keys.META, sdl2.SDLK_RGUI: keys.META, sdl2.SDLK_LEFT: keys.LEFT, sdl2.SDLK_UP: keys.UP, sdl2.SDLK_RIGHT: keys.RIGHT, sdl2.SDLK_DOWN: keys.DOWN, sdl2.SDLK_PAGEUP: keys.PAGEUP, sdl2.SDLK_PAGEDOWN: keys.PAGEDOWN, sdl2.SDLK_INSERT: keys.INSERT, sdl2.SDLK_DELETE: keys.DELETE, sdl2.SDLK_HOME: keys.HOME, sdl2.SDLK_END: keys.END, sdl2.SDLK_ESCAPE: keys.ESCAPE, sdl2.SDLK_BACKSPACE: keys.BACKSPACE, sdl2.SDLK_F1: keys.F1, sdl2.SDLK_F2: keys.F2, sdl2.SDLK_F3: keys.F3, sdl2.SDLK_F4: keys.F4, sdl2.SDLK_F5: keys.F5, sdl2.SDLK_F6: keys.F6, sdl2.SDLK_F7: keys.F7, sdl2.SDLK_F8: keys.F8, sdl2.SDLK_F9: keys.F9, sdl2.SDLK_F10: keys.F10, sdl2.SDLK_F11: keys.F11, sdl2.SDLK_F12: keys.F12, sdl2.SDLK_SPACE: keys.SPACE, sdl2.SDLK_RETURN: keys.ENTER, sdl2.SDLK_TAB: keys.TAB, } BUTTONMAP = {sdl2.SDL_BUTTON_LEFT: 1, sdl2.SDL_BUTTON_MIDDLE: 2, sdl2.SDL_BUTTON_RIGHT: 3 } except Exception as exp: available, testable, why_not, which = False, False, str(exp), None else: if USE_EGL: available, testable, why_not = False, False, 'EGL not supported' else: available, testable, why_not = True, True, None which = 'sdl2 %d.%d.%d' % sdl2.version_info[:3] _SDL2_INITIALIZED = False _VP_SDL2_ALL_WINDOWS = {} def _get_sdl2_windows(): return list(_VP_SDL2_ALL_WINDOWS.values()) # -------------------------------------------------------------- capability --- capability = dict( # things that can be set by the backend title=True, size=True, position=True, show=True, vsync=True, resizable=True, decorate=True, fullscreen=True, context=True, multi_window=True, scroll=True, parent=False, always_on_top=False, ) # ------------------------------------------------------- set_configuration --- def _set_config(c): """Set gl configuration for SDL2""" func = sdl2.SDL_GL_SetAttribute func(sdl2.SDL_GL_RED_SIZE, c['red_size']) func(sdl2.SDL_GL_GREEN_SIZE, c['green_size']) func(sdl2.SDL_GL_BLUE_SIZE, c['blue_size']) func(sdl2.SDL_GL_ALPHA_SIZE, c['alpha_size']) func(sdl2.SDL_GL_DEPTH_SIZE, c['depth_size']) func(sdl2.SDL_GL_STENCIL_SIZE, c['stencil_size']) func(sdl2.SDL_GL_DOUBLEBUFFER, 1 if c['double_buffer'] else 0) samps = c['samples'] func(sdl2.SDL_GL_MULTISAMPLEBUFFERS, 1 if samps > 0 else 0) func(sdl2.SDL_GL_MULTISAMPLESAMPLES, samps if samps > 0 else 0) func(sdl2.SDL_GL_STEREO, c['stereo']) # ------------------------------------------------------------- application --- class ApplicationBackend(BaseApplicationBackend): def __init__(self): BaseApplicationBackend.__init__(self) self._timers = list() def _add_timer(self, timer): if timer not in self._timers: self._timers.append(timer) def _vispy_get_backend_name(self): return 'SDL2' def _vispy_process_events(self): events = sdl2.ext.get_events() while len(events) > 0: for event in events: _id = event.window.windowID if _id in _VP_SDL2_ALL_WINDOWS: win = _VP_SDL2_ALL_WINDOWS[_id] win._on_event(event) events = sdl2.ext.get_events() for timer in self._timers: timer._tick() wins = _get_sdl2_windows() for win in wins: if win._needs_draw: win._needs_draw = False win._on_draw() def _vispy_run(self): wins = _get_sdl2_windows() while all(w._id is not None for w in wins): self._vispy_process_events() self._vispy_quit() # to clean up def _vispy_quit(self): # Close windows wins = _get_sdl2_windows() for win in wins: win._vispy_close() # tear down timers for timer in self._timers: timer._vispy_stop() self._timers = [] def _vispy_get_native_app(self): global _SDL2_INITIALIZED if not _SDL2_INITIALIZED: sdl2.ext.init() atexit.register(sdl2.ext.quit) _SDL2_INITIALIZED = True return sdl2 # ------------------------------------------------------------------ canvas --- class CanvasBackend(BaseCanvasBackend): """ SDL2 backend for Canvas abstract class.""" # args are for BaseCanvasBackend, kwargs are for us. def __init__(self, *args, **kwargs): BaseCanvasBackend.__init__(self, *args) p = self._process_backend_kwargs(kwargs) self._initialized = False # Deal with config _set_config(p.context.config) # Deal with context p.context.shared.add_ref('sdl2', self) if p.context.shared.ref is self: share = None else: other = p.context.shared.ref share = other._id.window, other._native_context sdl2.SDL_GL_MakeCurrent(*share) sdl2.SDL_GL_SetAttribute(sdl2.SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1) sdl2.SDL_GL_SetSwapInterval(1 if p.vsync else 0) flags = sdl2.SDL_WINDOW_OPENGL flags |= sdl2.SDL_WINDOW_SHOWN # start out shown flags |= sdl2.SDL_WINDOW_ALLOW_HIGHDPI flags |= sdl2.SDL_WINDOW_RESIZABLE if p.resizable else 0 flags |= sdl2.SDL_WINDOW_BORDERLESS if not p.decorate else 0 if p.fullscreen is not False: self._fullscreen = True if p.fullscreen is not True: logger.warning('Cannot specify monitor number for SDL2 ' 'fullscreen, using default') flags |= sdl2.SDL_WINDOW_FULLSCREEN_DESKTOP else: self._fullscreen = False self._mods = list() if p.position is None: position = [sdl2.SDL_WINDOWPOS_UNDEFINED] * 2 else: position = None self._id = sdl2.ext.Window(p.title, p.size, position, flags) if not self._id.window: raise RuntimeError('Could not create window') if share is None: self._native_context = sdl2.SDL_GL_CreateContext(self._id.window) else: self._native_context = sdl2.SDL_GL_CreateContext(share[0]) self._sdl_id = sdl2.SDL_GetWindowID(self._id.window) _VP_SDL2_ALL_WINDOWS[self._sdl_id] = self # Init self._initialized = True self._needs_draw = False self._vispy_canvas.set_current() self._vispy_canvas.events.initialize() if not p.show: self._vispy_set_visible(False) def _vispy_warmup(self): etime = time() + 0.1 while time() < etime: sleep(0.01) self._vispy_canvas.set_current() self._vispy_canvas.app.process_events() def _vispy_set_current(self): if self._id is None: return # Make this the current context sdl2.SDL_GL_MakeCurrent(self._id.window, self._native_context) def _vispy_swap_buffers(self): if self._id is None: return # Swap front and back buffer sdl2.SDL_GL_SwapWindow(self._id.window) def _vispy_set_title(self, title): if self._id is None: return # Set the window title. Has no effect for widgets sdl2.SDL_SetWindowTitle(self._id.window, title.encode('UTF-8')) def _vispy_set_size(self, w, h): if self._id is None: return # Set size of the widget or window sdl2.SDL_SetWindowSize(self._id.window, w, h) def _vispy_set_position(self, x, y): if self._id is None: return # Set position of the widget or window. May have no effect for widgets sdl2.SDL_SetWindowPosition(self._id.window, x, y) def _vispy_set_visible(self, visible): # Show or hide the window or widget if self._id is None: return if visible: self._id.show() # this ensures that the show takes effect self._vispy_update() else: self._id.hide() def _vispy_update(self): # Invoke a redraw, passing it on to the canvas if self._vispy_canvas is None or self._id is None: return # Mark that this window wants to be drawn on the next loop iter self._needs_draw = True def _vispy_close(self): # Force the window or widget to shut down if self._id is not None: _id = self._id.window self._vispy_canvas = None self._id = None sdl2.SDL_DestroyWindow(_id) del _VP_SDL2_ALL_WINDOWS[self._sdl_id] self._sdl_id = None gc.collect() # enforce gc to help context get destroyed def _vispy_get_size(self): if self._id is None: return w, h = ctypes.c_int(), ctypes.c_int() sdl2.SDL_GetWindowSize(self._id.window, ctypes.byref(w), ctypes.byref(h)) w, h = w.value, h.value return w, h def _vispy_get_fullscreen(self): return self._fullscreen def _vispy_set_fullscreen(self, fullscreen): self._fullscreen = bool(fullscreen) flags = sdl2.SDL_WINDOW_FULLSCREEN_DESKTOP if self._fullscreen else 0 sdl2.SDL_SetWindowFullscreen(self._id.window, flags) def _vispy_get_position(self): if self._id is None: return x, y = ctypes.c_int(), ctypes.c_int() sdl2.SDL_GetWindowPosition(self._id.window, ctypes.byref(x), ctypes.byref(y)) x, y = x.value, y.value return x, y ########################################## # Notify vispy of events triggered by SDL2 def _get_mouse_position(self): if self._id is None: return (0, 0) x, y = ctypes.c_int(), ctypes.c_int() sdl2.SDL_GetMouseState(ctypes.byref(x), ctypes.byref(y)) return x.value, y.value def _on_draw(self): if self._vispy_canvas is None or self._id is None: return self._vispy_canvas.set_current() self._vispy_canvas.events.draw(region=None) # (0, 0, w, h)) def _on_event(self, event): if self._vispy_canvas is None: return # triage event to proper handler if event.type == sdl2.SDL_QUIT: self._vispy_canvas.close() elif event.type == sdl2.SDL_WINDOWEVENT: if event.window.event == sdl2.SDL_WINDOWEVENT_RESIZED: w, h = event.window.data1, event.window.data2 self._vispy_canvas.events.resize(size=(w, h)) elif event.window.event == sdl2.SDL_WINDOWEVENT_CLOSE: self._vispy_canvas.close() elif event.type == sdl2.SDL_MOUSEMOTION: x, y = event.motion.x, event.motion.y self._vispy_mouse_move(pos=(x, y), modifiers=self._mods) elif event.type in (sdl2.SDL_MOUSEBUTTONDOWN, sdl2.SDL_MOUSEBUTTONUP): x, y = event.button.x, event.button.y button = event.button.button if button in BUTTONMAP: button = BUTTONMAP.get(button, 0) if event.type == sdl2.SDL_MOUSEBUTTONDOWN: func = self._vispy_mouse_press else: func = self._vispy_mouse_release func(pos=(x, y), button=button, modifiers=self._mods) elif event.type == sdl2.SDL_MOUSEWHEEL: pos = self._get_mouse_position() delta = float(event.wheel.x), float(event.wheel.y) self._vispy_canvas.events.mouse_wheel(pos=pos, delta=delta, modifiers=self._mods) elif event.type in (sdl2.SDL_KEYDOWN, sdl2.SDL_KEYUP): down = (event.type == sdl2.SDL_KEYDOWN) keysym = event.key.keysym mods = keysym.mod key = keysym.sym self._process_mod(mods, down) if key in KEYMAP: key, text = KEYMAP[key], '' elif key >= 32 and key <= 127: key, text = keys.Key(chr(key)), chr(key) else: key, text = None, '' if down: fun = self._vispy_canvas.events.key_press else: fun = self._vispy_canvas.events.key_release fun(key=key, text=text, modifiers=self._mods) def _process_mod(self, key, down): _modifiers = list() if key & (sdl2.SDLK_LSHIFT | sdl2.SDLK_RSHIFT): _modifiers.append(keys.SHIFT) if key & (sdl2.SDLK_LCTRL | sdl2.SDLK_RCTRL): _modifiers.append(keys.CONTROL) if key & (sdl2.SDLK_LALT | sdl2.SDLK_RALT): _modifiers.append(keys.ALT) if key & (sdl2.SDLK_LGUI | sdl2.SDLK_RGUI): _modifiers.append(keys.META) for mod in _modifiers: if mod not in self._mods: if down: self._mods.append(mod) elif not down: self._mods.pop(self._mods.index(mod)) # ------------------------------------------------------------------- timer --- # XXX should probably use SDL_Timer (and SDL_INIT_TIMER) class TimerBackend(BaseTimerBackend): def __init__(self, vispy_timer): BaseTimerBackend.__init__(self, vispy_timer) vispy_timer._app._backend._add_timer(self) self._vispy_stop() def _vispy_start(self, interval): self._interval = interval self._next_time = time() + self._interval def _vispy_stop(self): self._next_time = float('inf') def _tick(self): if time() >= self._next_time: self._vispy_timer._timeout() self._next_time = time() + self._interval vispy-0.4.0/vispy/app/backends/_test.py0000664000175000017500000000035412527672621021573 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. available = False why_not = 'test backend should be skipped' testable = False which = None vispy-0.4.0/vispy/app/backends/_template.py0000664000175000017500000001745612527672621022442 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ This module provides an template for creating backends for vispy. It clearly indicates what methods should be implemented and what events should be emitted. """ from __future__ import division from ..base import (BaseApplicationBackend, BaseCanvasBackend, BaseTimerBackend) from ...util import keys from ... import config USE_EGL = config['gl_backend'].lower().startswith('es') # -------------------------------------------------------------------- init --- # Map native keys to vispy keys KEYMAP = { -1: keys.SHIFT, -2: keys.CONTROL, -3: keys.ALT, -4: keys.META, -5: keys.LEFT, -6: keys.UP, -7: keys.RIGHT, -8: keys.DOWN, -9: keys.PAGEUP, -10: keys.PAGEDOWN, -11: keys.INSERT, -12: keys.DELETE, -13: keys.HOME, -14: keys.END, -15: keys.ESCAPE, -16: keys.BACKSPACE, -17: keys.SPACE, -18: keys.ENTER, -19: keys.TAB, -20: keys.F1, -21: keys.F2, -22: keys.F3, -23: keys.F4, -24: keys.F5, -25: keys.F6, -26: keys.F7, -27: keys.F8, -28: keys.F9, -29: keys.F10, -30: keys.F11, -31: keys.F12, } # -------------------------------------------------------------- capability --- # These are all booleans. Note that they mirror many of the kwargs to # the initialization of the Canvas class. capability = dict( # if True they mean: title=False, # can set title on the fly size=False, # can set size on the fly position=False, # can set position on the fly show=False, # can show/hide window XXX ? vsync=False, # can set window to sync to blank resizable=False, # can toggle resizability (e.g., no user resizing) decorate=False, # can toggle decorations fullscreen=False, # fullscreen window support context=False, # can share contexts between windows multi_window=False, # can use multiple windows at once scroll=False, # scroll-wheel events are supported parent=False, # can pass native widget backend parent always_on_top=False, # can be made always-on-top ) # ------------------------------------------------------- set_configuration --- def _set_config(c): """Set gl configuration for template""" raise NotImplementedError # ------------------------------------------------------------- application --- class ApplicationBackend(BaseApplicationBackend): def __init__(self): BaseApplicationBackend.__init__(self) def _vispy_get_backend_name(self): return 'ThisBackendsName' def _vispy_process_events(self): raise NotImplementedError() def _vispy_run(self): raise NotImplementedError() def _vispy_quit(self): raise NotImplementedError() def _vispy_get_native_app(self): raise NotImplementedError() # ------------------------------------------------------------------ canvas --- # You can mix this class with the native widget class CanvasBackend(BaseCanvasBackend): """Template backend Events to emit are shown below. Most backends will probably have one method for each event: self._vispy_canvas.events.initialize() self._vispy_canvas.events.resize(size=(w, h)) self._vispy_canvas.events.draw(region=None) self._vispy_canvas.close() self._vispy_canvas.events.mouse_press(pos=(x, y), button=1, modifiers=()) self._vispy_canvas.events.mouse_release(pos=(x, y), button=1, modifiers=()) self._vispy_canvas.events.mouse_double_click(pos=(x, y), button=1, modifiers=()) self._vispy_canvas.events.mouse_move(pos=(x, y), modifiers=()) self._vispy_canvas.events.mouse_wheel(pos=(x, y), delta=(0, 0), modifiers=()) self._vispy_canvas.events.key_press(key=key, text=text, modifiers=()) self._vispy_canvas.events.key_release(key=key, text=text, modifiers=()) In most cases, if the window-cross is clicked, a native close-event is generated, which should then call canvas.close(). The Canvas class is responsible for firing the close event and calling backend_canvas._vispy_close, which closes the native widget. If this happens to result in a second close event, canvas.close() gets called again, but Canvas knows it is closing so it stops there. If canvas.close() is called (by the user), it calls backend_canvas._vispy_close, which closes the native widget, and we get the same stream of actions as above. This deviation from having events come from the CanvasBackend is necessitated by how different backends handle close events, and the various ways such events can be triggered. """ # args are for BaseCanvasBackend, kwargs are for us. def __init__(self, *args, **kwargs): BaseCanvasBackend.__init__(self, *args) # We use _process_backend_kwargs() to "serialize" the kwargs # and to check whether they match this backend's capability p = self._process_backend_kwargs(kwargs) # Deal with config # ... use context.config # Deal with context p.context.shared.add_ref('backend-name', self) if p.context.shared.ref is self: self._native_context = None # ... else: self._native_context = p.context.shared.ref._native_context # NativeWidgetClass.__init__(self, foo, bar) def _vispy_set_current(self): # Make this the current context raise NotImplementedError() def _vispy_swap_buffers(self): # Swap front and back buffer raise NotImplementedError() def _vispy_set_title(self, title): # Set the window title. Has no effect for widgets raise NotImplementedError() def _vispy_set_size(self, w, h): # Set size of the widget or window raise NotImplementedError() def _vispy_set_position(self, x, y): # Set location of the widget or window. May have no effect for widgets raise NotImplementedError() def _vispy_set_visible(self, visible): # Show or hide the window or widget raise NotImplementedError() def _vispy_set_fullscreen(self, fullscreen): # Set the current fullscreen state raise NotImplementedError() def _vispy_update(self): # Invoke a redraw raise NotImplementedError() def _vispy_close(self): # Force the window or widget to shut down raise NotImplementedError() def _vispy_get_size(self): # Should return widget size raise NotImplementedError() def _vispy_get_position(self): # Should return widget position raise NotImplementedError() def _vispy_get_fullscreen(self): # Should return the current fullscreen state raise NotImplementedError() def _vispy_get_native_canvas(self): # Should return the native widget object. # If this is self, this method can be omitted. return self # ------------------------------------------------------------------- timer --- class TimerBackend(BaseTimerBackend): # Can be mixed with native timer class def __init__(self, vispy_timer): BaseTimerBackend.__init__(self, vispy_timer) def _vispy_start(self, interval): raise NotImplementedError() def _vispy_stop(self): raise NotImplementedError() def _vispy_timeout(self): raise NotImplementedError() def _vispy_get_native_timer(self): # Should return the native widget object. # If this is self, this method can be omitted. return self vispy-0.4.0/vispy/app/backends/tests/0000775000175000017500000000000012527674621021245 5ustar larsonerlarsoner00000000000000vispy-0.4.0/vispy/app/backends/tests/__init__.py0000664000175000017500000000000012437121522023327 0ustar larsonerlarsoner00000000000000vispy-0.4.0/vispy/app/backends/tests/test_ipynb_util.py0000664000175000017500000001064512527672621025040 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. import numpy as np from vispy.app.backends._ipynb_util import (_extract_buffers, _serialize_command, create_glir_message) from vispy.testing import run_tests_if_main, assert_equal def test_extract_buffers(): arr = np.random.rand(10, 2).astype(np.float32) arr2 = np.random.rand(20, 2).astype(np.int16) # No DATA command. commands = [('CREATE', 4, 'VertexBuffer')] commands_modified, buffers = _extract_buffers(commands) assert_equal(commands_modified, commands) assert_equal(buffers, []) # A single DATA command. commands = [('DATA', 4, 0, arr)] commands_modified, buffers = _extract_buffers(commands) assert_equal(commands_modified, [('DATA', 4, 0, {'buffer_index': 0})]) assert_equal(buffers, [arr]) # Several commands. commands = [('DATA', 0, 10, arr), ('UNIFORM', 4, 'u_scale', 'vec3', (1, 2, 3)), ('DATA', 2, 20, arr2) ] commands_modified_expected = [ ('DATA', 0, 10, {'buffer_index': 0}), ('UNIFORM', 4, 'u_scale', 'vec3', (1, 2, 3)), ('DATA', 2, 20, {'buffer_index': 1})] commands_modified, buffers = _extract_buffers(commands) assert_equal(commands_modified, commands_modified_expected) assert_equal(buffers, [arr, arr2]) def test_serialize_command(): command = ('CREATE', 4, 'VertexBuffer') command_serialized = _serialize_command(command) assert_equal(command_serialized, list(command)) command = ('UNIFORM', 4, 'u_scale', 'vec3', (1, 2, 3)) commands_serialized_expected = ['UNIFORM', 4, 'u_scale', 'vec3', [1, 2, 3]] command_serialized = _serialize_command(command) assert_equal(command_serialized, commands_serialized_expected) def test_create_glir_message_binary(): arr = np.zeros((3, 2)).astype(np.float32) arr2 = np.ones((4, 5)).astype(np.int16) commands = [('CREATE', 1, 'VertexBuffer'), ('UNIFORM', 2, 'u_scale', 'vec3', (1, 2, 3)), ('DATA', 3, 0, arr), ('UNIFORM', 4, 'u_pan', 'vec2', np.array([1, 2, 3])), ('DATA', 5, 20, arr2)] msg = create_glir_message(commands) assert_equal(msg['msg_type'], 'glir_commands') commands_serialized = msg['commands'] assert_equal(commands_serialized, [['CREATE', 1, 'VertexBuffer'], ['UNIFORM', 2, 'u_scale', 'vec3', [1, 2, 3]], ['DATA', 3, 0, {'buffer_index': 0}], ['UNIFORM', 4, 'u_pan', 'vec2', [1, 2, 3]], ['DATA', 5, 20, {'buffer_index': 1}]]) buffers_serialized = msg['buffers'] buf0 = buffers_serialized[0] assert_equal(buf0, b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') # noqa buf1 = buffers_serialized[1] assert_equal(buf1, b'\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00') # noqa def test_create_glir_message_base64(): arr = np.zeros((3, 2)).astype(np.float32) arr2 = np.ones((4, 5)).astype(np.int16) commands = [('CREATE', 1, 'VertexBuffer'), ('UNIFORM', 2, 'u_scale', 'vec3', (1, 2, 3)), ('DATA', 3, 0, arr), ('UNIFORM', 4, 'u_pan', 'vec2', np.array([1, 2, 3])), ('DATA', 5, 20, arr2)] msg = create_glir_message(commands, array_serialization='base64') assert_equal(msg['msg_type'], 'glir_commands') commands_serialized = msg['commands'] assert_equal(commands_serialized, [['CREATE', 1, 'VertexBuffer'], ['UNIFORM', 2, 'u_scale', 'vec3', [1, 2, 3]], ['DATA', 3, 0, {'buffer_index': 0}], ['UNIFORM', 4, 'u_pan', 'vec2', [1, 2, 3]], ['DATA', 5, 20, {'buffer_index': 1}]]) buffers_serialized = msg['buffers'] buf0 = buffers_serialized[0] assert_equal(buf0['storage_type'], 'base64') assert_equal(buf0['buffer'], 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') buf1 = buffers_serialized[1] assert_equal(buf0['storage_type'], 'base64') assert_equal(buf1['buffer'], 'AQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAA==') run_tests_if_main() vispy-0.4.0/vispy/app/backends/_ipynb_vnc.py0000664000175000017500000003263012527672621022605 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ vispy backend for the IPython notebook (vnc approach). We aim to have: * ipynb_static - export visualization to a static notebook * ipynb_vnc - vnc-approach: render in Python, send result to JS as png * ipynb_webgl - send gl commands to JS and execute in webgl context """ from __future__ import division from ..base import (BaseApplicationBackend, BaseCanvasBackend, BaseTimerBackend) from .. import Application, Canvas from ...util import logger #from ...util.event import Event # For timer # Imports for screenshot # Perhaps we should refactor these to have just one import from ...gloo.util import _screenshot from ...io import _make_png from base64 import b64encode # Import for displaying Javascript on notebook import os.path as op # -------------------------------------------------------------------- init --- capability = dict( # things that can be set by the backend title=True, # But it only applies to the dummy window :P size=True, # We cannot possibly say we dont, because Canvas always sets it position=True, # Dito show=True, # Note: we don't alow this, but all scripts call show ... vsync=False, resizable=True, # Yes, you can set to not be resizable (it always is) decorate=False, fullscreen=False, context=True, multi_window=True, scroll=True, parent=False, always_on_top=False, ) def _set_config(c): _app.backend_module._set_config(c) # Init dummy objects needed to import this module withour errors. # These are all overwritten with imports from IPython (on success) DOMWidget = object Unicode = Int = Float = Bool = lambda *args, **kwargs: None # Create our "backend" backend; The toolkit that is going to provide a # canvas (e.g. OpenGL context) so we can render images. # Note that if IPython has already loaded a GUI backend, vispy is # probably going to use that as well, because it prefers loaded backends. try: # Explicitly use default (avoid using test-app) _app = Application('default') except Exception: _msg = 'ipynb_vnc backend relies on a core backend' available, testable, why_not, which = False, False, _msg, None else: # Try importing IPython try: import IPython if IPython.version_info < (2,): raise RuntimeError('ipynb_vnc backend need IPython version >= 2.0') from IPython.html.widgets import DOMWidget from IPython.utils.traitlets import Unicode, Int, Float, Bool from IPython.display import display, Javascript from IPython.html.nbextensions import install_nbextension except Exception as exp: available, testable, why_not, which = False, False, str(exp), None else: available, testable, why_not = True, False, None which = _app.backend_module.which print(' NOTE: this backend requires the Chromium browser') # Use that backend's shared context KEYMAP = _app.backend_module.KEYMAP # ------------------------------------------------------------- application --- # todo: maybe trigger something in JS on any of these methods? class ApplicationBackend(BaseApplicationBackend): def __init__(self): BaseApplicationBackend.__init__(self) self._backend2 = _app._backend def _vispy_get_backend_name(self): realname = self._backend2._vispy_get_backend_name() return 'ipynb_vnc (via %s)' % realname def _vispy_process_events(self): return self._backend2._vispy_process_events() def _vispy_run(self): pass # We run in IPython, so we don't run! #return self._backend2._vispy_run() def _vispy_quit(self): return self._backend2._vispy_quit() def _vispy_get_native_app(self): return self._backend2._vispy_get_native_app() # ------------------------------------------------------------------ canvas --- class CanvasBackend(BaseCanvasBackend): # args are for BaseCanvasBackend, kwargs are for us. def __init__(self, *args, **kwargs): BaseCanvasBackend.__init__(self, *args) self._initialized = False # Test kwargs # if kwargs['size']: # raise RuntimeError('ipynb_vnc Canvas is not resizable') # if kwargs['position']: # raise RuntimeError('ipynb_vnc Canvas is not positionable') if not kwargs['decorate']: raise RuntimeError('ipynb_vnc Canvas is not decoratable (or not)') if kwargs['vsync']: raise RuntimeError('ipynb_vnc Canvas does not support vsync') if kwargs['fullscreen']: raise RuntimeError('ipynb_vnc Canvas does not support fullscreen') # Create real canvas. It is a backend to this backend kwargs.pop('vispy_canvas', None) kwargs['autoswap'] = False canvas = Canvas(app=_app, **kwargs) # Pass kwargs to underlying canvas self._backend2 = canvas.native # Connect to events of canvas to keep up to date with size and draws canvas.events.draw.connect(self._on_draw) canvas.events.resize.connect(self._on_resize) # Show the widget, we will hide it after the first time it's drawn self._backend2._vispy_set_visible(True) self._need_draw = False # Prepare Javascript code by displaying on notebook self._prepare_js() # Create IPython Widget self._widget = Widget(self._gen_event, size=canvas.size) def _vispy_warmup(self): return self._backend2._vispy_warmup() def _vispy_set_current(self): return self._backend2._vispy_set_current() def _vispy_swap_buffers(self): return self._backend2._vispy_swap_buffers() def _vispy_set_title(self, title): return self._backend2._vispy_set_title(title) #logger.warning('IPython notebook canvas has not title.') def _vispy_set_size(self, w, h): #logger.warn('IPython notebook canvas cannot be resized.') res = self._backend2._vispy_set_size(w, h) self._backend2._vispy_set_visible(True) return res def _vispy_set_position(self, x, y): logger.warning('IPython notebook canvas cannot be repositioned.') def _vispy_set_visible(self, visible): #self._backend2._vispy_set_visible(visible) if not visible: logger.warning('IPython notebook canvas cannot be hidden.') else: display(self._widget) def _vispy_update(self): self._need_draw = True return self._backend2._vispy_update() def _vispy_close(self): self._need_draw = False self._widget.quit() return self._backend2._vispy_close() def _vispy_get_position(self): return 0, 0 def _vispy_get_size(self): return self._backend2._vispy_get_size() def _on_resize(self, event=None): # Event handler that is called by the underlying canvas if self._vispy_canvas is None: return size = self._backend2._vispy_get_size() self._widget.size = size self._vispy_canvas.events.resize(size=size) def _on_draw(self, event=None): # Event handler that is called by the underlying canvas if self._vispy_canvas is None: return # Handle initialization if not self._initialized: self._initialized = True #self._vispy_canvas.events.add(timer=Event) self._vispy_canvas.events.initialize() self._on_resize() # We are drawn, so no need for a redraw self._need_draw = False # We hide the widget once it has received a paint event. So at # initialization and after a resize the widget is briefly visible. # Now that it is hidden the widget is unlikely to receive paint # events anymore, so we need to force repaints from now on, via # a trigger from JS. self._backend2._vispy_set_visible(False) # Normal behavior self._vispy_canvas.set_current() self._vispy_canvas.events.draw(region=None) # Save the encoded screenshot image to widget self._save_screenshot() def _save_screenshot(self): # Take the screenshot img = _screenshot() # Convert to PNG and encode self._widget.value = b64encode(_make_png(img)) # Generate vispy events according to upcoming JS events def _gen_event(self, ev): if self._vispy_canvas is None: return ev = ev.get("event") # Parse and generate event if ev.get("name") == "MouseEvent": mouse = ev.get("properties") # Generate if mouse.get("type") == "mouse_move": self._vispy_mouse_move(native=mouse, pos=mouse.get("pos"), modifiers=mouse.get("modifiers"), ) elif mouse.get("type") == "mouse_press": self._vispy_mouse_press(native=mouse, pos=mouse.get("pos"), button=mouse.get("button"), modifiers=mouse.get("modifiers"), ) elif mouse.get("type") == "mouse_release": self._vispy_mouse_release(native=mouse, pos=mouse.get("pos"), button=mouse.get("button"), modifiers=mouse.get("modifiers"), ) elif mouse.get("type") == "mouse_wheel": self._vispy_canvas.events.mouse_wheel(native=mouse, delta=mouse.get("delta"), pos=mouse.get("pos"), modifiers=mouse.get ("modifiers"), ) elif ev.get("name") == "KeyEvent": key = ev.get("properties") if key.get("type") == "key_press": self._vispy_canvas.events.key_press(native=key, key=key.get("key"), text=key.get("text"), modifiers=key.get ("modifiers"), ) elif key.get("type") == "key_release": self._vispy_canvas.events.key_release(native=key, key=key.get("key"), text=key.get("text"), modifiers=key.get ("modifiers"), ) elif ev.get("name") == "PollEvent": # Ticking from front-end (JS) # Allthough the event originates from JS, this is basically # a poll event from IPyhon's event loop, which we use to # update the backend app and draw stuff if necessary. If we # succeed to make IPython process GUI app events directly, # this "JS timer" should not be necessary. self._vispy_canvas.app.process_events() if self._need_draw: self._on_draw() # Generate a timer event on every poll from JS # AK: no, just use app.Timer as usual! #self._vispy_canvas.events.timer(type="timer") def _prepare_js(self): pkgdir = op.dirname(__file__) install_nbextension([op.join(pkgdir, '../../html/static/js')]) script = 'IPython.load_extensions("js/vispy");' display(Javascript(script)) # ------------------------------------------------------------------- timer --- class TimerBackend(BaseTimerBackend): def __init__(self, vispy_timer): self._backend2 = _app.backend_module.TimerBackend(vispy_timer) def _vispy_start(self, interval): return self._backend2._vispy_start(interval) def _vispy_stop(self): return self._backend2._vispy_stop() def _vispy_timeout(self): return self._backend2._vispy_timeout() # ---------------------------------------------------------- IPython Widget --- class Widget(DOMWidget): _view_name = Unicode("Widget", sync=True) # Define the custom state properties to sync with the front-end format = Unicode('png', sync=True) width = Int(sync=True) height = Int(sync=True) interval = Float(sync=True) is_closing = Bool(sync=True) value = Unicode(sync=True) def __init__(self, gen_event, **kwargs): super(Widget, self).__init__(**kwargs) self.size = kwargs["size"] self.interval = 50.0 self.gen_event = gen_event self.on_msg(self._handle_event_msg) def _handle_event_msg(self, _, content): # If closing, don't bother generating the event if not self.is_closing: self.gen_event(content) @property def size(self): return self.width, self.height @size.setter def size(self, size): self.width, self.height = size def quit(self): self.is_closing = True self.close() vispy-0.4.0/vispy/app/backends/_egl.py0000664000175000017500000001657712527672621021401 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ vispy headless backend for egl. """ from __future__ import division import atexit from time import sleep from ..base import (BaseApplicationBackend, BaseCanvasBackend, BaseTimerBackend) from ...util.ptime import time # -------------------------------------------------------------------- init --- try: # Inspired by http://www.mesa3d.org/egl.html # This is likely necessary on Linux since proprietary drivers # (e.g., NVIDIA) are unlikely to provide EGL support for now. # XXX TODO: Add use_gl('es2') and somehow incorporate here. # Also would be good to have us_gl('es3'), since libGLESv2.so on linux # seems to support both. from os import environ environ['EGL_SOFTWARE'] = 'true' from ...ext import egl _EGL_DISPLAY = egl.eglGetDisplay() egl.eglInitialize(_EGL_DISPLAY) version = [egl.eglQueryString(_EGL_DISPLAY, x) for x in [egl.EGL_VERSION, egl.EGL_VENDOR, egl.EGL_CLIENT_APIS]] version = [v.decode('utf-8') for v in version] version = version[0] + ' ' + version[1] + ': ' + version[2].strip() atexit.register(egl.eglTerminate, _EGL_DISPLAY) except Exception as exp: available, testable, why_not, which = False, False, str(exp), None else: # XXX restore "testable" and "available" once it works properly, and # remove from ignore list in .coveragerc available, testable, why_not = False, False, 'Not ready for testing' which = 'EGL ' + str(version) _VP_EGL_ALL_WINDOWS = [] def _get_egl_windows(): wins = list() for win in _VP_EGL_ALL_WINDOWS: if isinstance(win, CanvasBackend): wins.append(win) return wins # -------------------------------------------------------------- capability --- capability = dict( # things that can be set by the backend title=True, size=True, position=True, show=True, vsync=False, resizable=True, decorate=False, fullscreen=False, context=False, multi_window=True, scroll=False, parent=False, always_on_top=False, ) # ------------------------------------------------------------- application --- class ApplicationBackend(BaseApplicationBackend): def __init__(self): BaseApplicationBackend.__init__(self) self._timers = list() def _add_timer(self, timer): if timer not in self._timers: self._timers.append(timer) def _vispy_get_backend_name(self): return 'egl' def _vispy_process_events(self): for timer in self._timers: timer._tick() wins = _get_egl_windows() for win in wins: if win._needs_draw: win._needs_draw = False win._on_draw() def _vispy_run(self): wins = _get_egl_windows() while all(w._surface is not None for w in wins): self._vispy_process_events() self._vispy_quit() # to clean up def _vispy_quit(self): # Close windows wins = _get_egl_windows() for win in wins: win._vispy_close() # tear down timers for timer in self._timers: timer._vispy_stop() self._timers = [] def _vispy_get_native_app(self): return egl # ------------------------------------------------------------------ canvas --- class CanvasBackend(BaseCanvasBackend): """ EGL backend for Canvas abstract class.""" # args are for BaseCanvasBackend, kwargs are for us. def __init__(self, *args, **kwargs): BaseCanvasBackend.__init__(self, *args) p = self._process_backend_kwargs(kwargs) self._initialized = False # Deal with context p.context.shared.add_ref('egl', self) if p.context.shared.ref is self: # Store context information self._native_config = egl.eglChooseConfig(_EGL_DISPLAY)[0] self._native_context = egl.eglCreateContext(_EGL_DISPLAY, self._native_config, None) else: # Reuse information from other context self._native_config = p.context.shared.ref._native_config self._native_context = p.context.shared.ref._native_context self._surface = None self._vispy_set_size(*p.size) _VP_EGL_ALL_WINDOWS.append(self) # Init self._initialized = True self._vispy_canvas.set_current() self._vispy_canvas.events.initialize() def _destroy_surface(self): if self._surface is not None: egl.eglDestroySurface(_EGL_DISPLAY, self._surface) self._surface = None def _vispy_set_size(self, w, h): if self._surface is not None: self._destroy_surface() attrib_list = (egl.EGL_WIDTH, w, egl.EGL_HEIGHT, h) self._surface = egl.eglCreatePbufferSurface(_EGL_DISPLAY, self._native_config, attrib_list) if self._surface == egl.EGL_NO_SURFACE: raise RuntimeError('Could not create rendering surface') self._size = (w, h) self._vispy_update() def _vispy_warmup(self): etime = time() + 0.25 while time() < etime: sleep(0.01) self._vispy_canvas.set_current() self._vispy_canvas.app.process_events() def _vispy_set_current(self): if self._surface is None: return # Make this the current context self._vispy_canvas.set_current() # Mark canvs as current egl.eglMakeCurrent(_EGL_DISPLAY, self._surface, self._surface, self._native_context) def _vispy_swap_buffers(self): if self._surface is None: return # Swap front and back buffer egl.eglSwapBuffers(_EGL_DISPLAY, self._surface) def _vispy_set_title(self, title): pass def _vispy_set_position(self, x, y): pass def _vispy_set_visible(self, visible): pass def _vispy_update(self): # Mark that this window wants to be drawn on the next loop iter self._needs_draw = True def _vispy_close(self): self._destroy_surface() def _vispy_get_size(self): if self._surface is None: return return self._size def _vispy_get_position(self): return 0, 0 def _on_draw(self, _id=None): # This is called by the processing app if self._vispy_canvas is None or self._surface is None: return self._vispy_canvas.set_current() self._vispy_canvas.events.draw(region=None) # (0, 0, w, h)) # ------------------------------------------------------------------- timer --- class TimerBackend(BaseTimerBackend): def __init__(self, vispy_timer): BaseTimerBackend.__init__(self, vispy_timer) vispy_timer._app._backend._add_timer(self) self._vispy_stop() def _vispy_start(self, interval): self._interval = interval self._next_time = time() + self._interval def _vispy_stop(self): self._next_time = float('inf') def _tick(self): if time() >= self._next_time: self._vispy_timer._timeout() self._next_time = time() + self._interval vispy-0.4.0/vispy/app/tests/0000775000175000017500000000000012527674621017473 5ustar larsonerlarsoner00000000000000vispy-0.4.0/vispy/app/tests/test_context.py0000664000175000017500000000610012466440051022553 0ustar larsonerlarsoner00000000000000import os import sys from vispy.testing import (requires_application, SkipTest, run_tests_if_main, assert_equal, assert_raises) from vispy.app import Canvas, use_app from vispy.gloo import get_gl_configuration, Program from vispy.gloo.gl import check_error @requires_application() def test_context_properties(): """Test setting context properties""" a = use_app() if a.backend_name.lower() == 'pyglet': return # cannot set more than once on Pyglet # stereo, double buffer won't work on every sys configs = [dict(samples=4), dict(stencil_size=8), dict(samples=4, stencil_size=8)] if a.backend_name.lower() != 'glfw': # glfw *always* double-buffers configs.append(dict(double_buffer=False, samples=4)) configs.append(dict(double_buffer=False)) else: assert_raises(RuntimeError, Canvas, app=a, config=dict(double_buffer=False)) if a.backend_name.lower() == 'sdl2' and os.getenv('TRAVIS') == 'true': raise SkipTest('Travis SDL cannot set context') for config in configs: n_items = len(config) with Canvas(config=config): if 'true' in (os.getenv('TRAVIS', ''), os.getenv('APPVEYOR', '').lower()): # Travis and Appveyor cannot handle obtaining these values props = config else: props = get_gl_configuration() assert_equal(len(config), n_items) for key, val in config.items(): # XXX knownfail for windows samples, and wx (all platforms) if key == 'samples': iswx = a.backend_name.lower() == 'wx' if not (sys.platform.startswith('win') or iswx): assert_equal(val, props[key], key) assert_raises(TypeError, Canvas, config='foo') assert_raises(KeyError, Canvas, config=dict(foo=True)) assert_raises(TypeError, Canvas, config=dict(double_buffer='foo')) @requires_application() def test_context_sharing(): """Test context sharing""" with Canvas() as c1: vert = "attribute vec4 pos;\nvoid main (void) {gl_Position = pos;}" frag = "void main (void) {gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);}" program = Program(vert, frag) program['pos'] = [(1, 2, 3, 1), (4, 5, 6, 1)] program.draw('points') def check(): # Do something to program and see if it worked program['pos'] = [(1, 2, 3, 1), (4, 5, 6, 1)] # Do command program.draw('points') check_error() # Check while c1 is active check() # Check while c2 is active (with different context) with Canvas() as c2: # pyglet always shares if 'pyglet' not in c2.app.backend_name.lower(): assert_raises(Exception, check) # Check while c2 is active (with *same* context) with Canvas(shared=c1.context) as c2: assert c1.context.shared is c2.context.shared # same object check() run_tests_if_main() vispy-0.4.0/vispy/app/tests/test_backends.py0000664000175000017500000001413512527672621022660 0ustar larsonerlarsoner00000000000000""" Tests to quickly see if the backends look good. This tests only to see if all the necessary methods are implemented, whether all the right events are mentioned, and whether the keymap contains all keys that should be supported. This test basically checks whether nothing was forgotten, not that the implementation is corect. """ from inspect import getargspec import vispy from vispy import keys from vispy.testing import (requires_application, assert_in, run_tests_if_main, assert_raises) from vispy.app import use_app, Application from vispy.app.backends import _template class DummyApplication(Application): def _use(self, backend_namd): pass def _test_module_properties(_module=None): """Test application module""" if _module is None: app = use_app() _module = app.backend_module # Test that the keymap contains all keys supported by vispy. module_fname = _module.__name__.split('.')[-1] if module_fname != '_egl': # skip keys for EGL keymap = _module.KEYMAP vispy_keys = keymap.values() for keyname in dir(keys): if keyname.upper() != keyname: continue key = getattr(keys, keyname) assert_in(key, vispy_keys) # For Qt backend, we have a common implementation alt_modname = '' if module_fname in ('_pyside', '_pyqt4', '_pyqt5'): alt_modname = _module.__name__.rsplit('.', 1)[0] + '._qt' # Test that all _vispy_x methods are there. exceptions = ( '_vispy_get_native_canvas', '_vispy_get_native_timer', '_vispy_get_native_app', '_vispy_reuse', '_vispy_mouse_move', '_vispy_mouse_press', '_vispy_mouse_release', '_vispy_mouse_double_click', '_vispy_detect_double_click', '_vispy_get_geometry', '_vispy_get_physical_size', '_process_backend_kwargs') # defined in base class class KlassRef(vispy.app.base.BaseCanvasBackend): def __init__(self, *args, **kwargs): pass # Do not call the base class, since it will check for Canvas Klass = _module.CanvasBackend base = KlassRef() for key in dir(KlassRef): if not key.startswith('__'): method = getattr(Klass, key) if key not in exceptions: print(key) args = [None] * (len(getargspec(method).args) - 1) assert_raises(NotImplementedError, getattr(base, key), *args) if hasattr(method, '__module__'): mod_str = method.__module__ # Py3k else: mod_str = method.im_func.__module__ assert_in(mod_str, (_module.__name__, alt_modname), "Method %s.%s not defined in %s" % (Klass, key, _module.__name__)) Klass = _module.TimerBackend KlassRef = vispy.app.timer.TimerBackend for key in dir(KlassRef): if not key.startswith('__'): method = getattr(Klass, key) if key not in exceptions: if hasattr(method, '__module__'): # Py3k assert_in(method.__module__, (_module.__name__, alt_modname)) else: t = method.im_func.__module__ == _module.__name__ assert t Klass = _module.ApplicationBackend KlassRef = vispy.app.application.ApplicationBackend for key in dir(KlassRef): if not key.startswith('__'): method = getattr(Klass, key) if key not in exceptions: if hasattr(method, '__module__'): # Py3k assert_in(method.__module__, (_module.__name__, alt_modname)) else: t = method.im_func.__module__ == _module.__name__ assert t # Test that all events seem to be emitted. # Get text fname = _module.__file__.rstrip('c') # "strip" will break windows! with open(fname, 'rb') as fid: text = fid.read().decode('utf-8') canvas = vispy.app.Canvas(create_native=False, app=DummyApplication()) # Stylus and touch are ignored because they are not yet implemented. # Mouse events are emitted from the CanvasBackend base class. ignore = set(['stylus', 'touch', 'mouse_press', 'paint', 'mouse_move', 'mouse_release', 'mouse_double_click', 'detect_double_click', 'close']) if module_fname == '_egl': ignore += ['key_release', 'key_press'] eventNames = set(canvas.events._emitters.keys()) - ignore if not alt_modname: # Only check for non-proxy modules for name in eventNames: assert_in('events.%s' % name, text, 'events.%s does not appear in %s' % (name, fname)) def test_template(): """Test application module template""" _test_module_properties(_template) assert_raises(NotImplementedError, _template._set_config, dict()) a = _template.ApplicationBackend() print(a._vispy_get_backend_name()) for method in (a._vispy_process_events, a._vispy_run, a._vispy_quit, a._vispy_get_native_app): assert_raises(NotImplementedError, method) class TemplateCanvasBackend(_template.CanvasBackend): def __init__(self, *args, **kwargs): pass # Do not call the base class, since it will check for Canvas c = TemplateCanvasBackend() # _template.CanvasBackend(None) print(c._vispy_get_native_canvas()) for method in (c._vispy_set_current, c._vispy_swap_buffers, c._vispy_close, c._vispy_update, c._vispy_get_size, c._vispy_get_position): assert_raises(NotImplementedError, method) for method in (c._vispy_set_title, c._vispy_set_visible): assert_raises(NotImplementedError, method, 0) for method in (c._vispy_set_size, c._vispy_set_position): assert_raises(NotImplementedError, method, 0, 0) @requires_application() def test_actual(): """Test actual application module""" _test_module_properties(None) run_tests_if_main() vispy-0.4.0/vispy/app/tests/test_qt.py0000664000175000017500000000241612527672621021531 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from os import path as op import warnings from vispy.testing import requires_application @requires_application('pyqt4', has=['uic']) def test_qt_designer(): """Embed Canvas via Qt Designer""" from PyQt4 import QtGui, uic app = QtGui.QApplication.instance() if app is None: app = QtGui.QApplication([]) fname = op.join(op.dirname(__file__), 'qt-designer.ui') with warnings.catch_warnings(record=True): # pyqt4 deprecation warning WindowTemplate, TemplateBaseClass = uic.loadUiType(fname) class MainWindow(TemplateBaseClass): def __init__(self): TemplateBaseClass.__init__(self) self.ui = WindowTemplate() self.ui.setupUi(self) win = MainWindow() try: canvas = win.ui.canvas # test we can access properties of the internal canvas: canvas.central_widget.add_view() win.show() app.processEvents() finally: win.close() return win # Don't use run_tests_if_main(), because we want to show the win if __name__ == '__main__': win = test_qt_designer() win.show() vispy-0.4.0/vispy/app/tests/__init__.py0000664000175000017500000000000012375431476021572 0ustar larsonerlarsoner00000000000000vispy-0.4.0/vispy/app/tests/test_app.py0000664000175000017500000003325312510536123021654 0ustar larsonerlarsoner00000000000000import numpy as np import sys from collections import namedtuple from time import sleep from numpy.testing import assert_array_equal from vispy.app import use_app, Canvas, Timer, MouseEvent, KeyEvent from vispy.app.base import BaseApplicationBackend from vispy.testing import (requires_application, SkipTest, assert_is, assert_in, run_tests_if_main, assert_equal, assert_true, assert_raises) from vispy.util import keys, use_log_level from vispy.gloo.program import (Program, VertexBuffer, IndexBuffer) from vispy.gloo.util import _screenshot from vispy.gloo import gl from vispy.ext.six.moves import StringIO gl.use_gl('gl2 debug') def on_nonexist(self, *args): return def on_mouse_move(self, *args): return def _on_mouse_move(self, *args): return def _test_callbacks(canvas): """Tests input capabilities, triaging based on backend""" backend_name = canvas._app.backend_name backend = canvas._backend if backend_name.lower() == 'pyglet': # Test Pyglet callbacks can take reasonable args backend.on_resize(100, 100) backend.our_draw_func() backend.on_mouse_press(10, 10, 1) backend.on_mouse_release(10, 11, 1) backend.on_mouse_motion(10, 12, 0, 1) backend.on_mouse_drag(10, 13, 0, 1, 1, 0) backend.on_mouse_scroll(10, 13, 1, 1) backend.on_key_press(10, 0) backend.on_key_release(10, 0) backend.on_text('foo') elif backend_name.lower() == 'glfw': # Test GLFW callbacks can take reasonable args _id = backend._id backend._on_draw(_id) backend._on_resize(_id, 100, 100) backend._on_key_press(_id, 50, 50, 1, 0) backend._on_mouse_button(_id, 1, 1, 0) backend._on_mouse_scroll(_id, 1, 0) backend._on_mouse_motion(_id, 10, 10) backend._on_close(_id) elif 'qt' in backend_name.lower(): # constructing fake Qt events is too hard :( pass elif 'sdl2' in backend_name.lower(): event = namedtuple('event', ['type', 'window', 'motion', 'button', 'wheel', 'key']) event.type = 512 # WINDOWEVENT event.window = namedtuple('window', ['event', 'data1', 'data2']) event.motion = namedtuple('motion', ['x', 'y']) event.button = namedtuple('button', ['x', 'y', 'button']) event.wheel = namedtuple('wheel', ['x', 'y']) event.key = namedtuple('key', ['keysym']) event.key.keysym = namedtuple('keysym', ['mod', 'sym']) event.window.event = 5 # WINDOWEVENT_RESIZED event.window.data1 = 10 event.window.data2 = 20 backend._on_event(event) event.type = 1024 # SDL_MOUSEMOTION event.motion.x, event.motion.y = 1, 1 backend._on_event(event) event.type = 1025 # MOUSEBUTTONDOWN event.button.x, event.button.y, event.button.button = 1, 1, 1 backend._on_event(event) event.type = 1026 # MOUSEBUTTONUP backend._on_event(event) event.type = 1027 # sdl2.SDL_MOUSEWHEEL event.wheel.x, event.wheel.y = 0, 1 backend._on_event(event) event.type = 768 # SDL_KEYDOWN event.key.keysym.mod = 1073742049 # SLDK_LSHIFT event.key.keysym.sym = 1073741906 # SDLK_UP backend._on_event(event) event.type = 769 # SDL_KEYUP backend._on_event(event) elif 'wx' in backend_name.lower(): # Constructing fake wx events is too hard pass else: raise ValueError @requires_application() def test_run(): """Test app running""" for _ in range(2): with Canvas(size=(100, 100), show=True, title='run') as c: @c.events.draw.connect def draw(event): print(event) # test event __repr__ c.app.quit() c.update() c.app.run() c.app.quit() # make sure it doesn't break if a user quits twice @requires_application() def test_capability(): """Test application capability enumeration""" non_default_vals = dict(title='foo', size=[100, 100], position=[0, 0], show=True, decorate=False, resizable=False, vsync=True) # context is tested elsewhere good_kwargs = dict() bad_kwargs = dict() with Canvas() as c: for key, val in c.app.backend_module.capability.items(): if key in non_default_vals: if val: good_kwargs[key] = non_default_vals[key] else: bad_kwargs[key] = non_default_vals[key] # ensure all settable values can be set with Canvas(**good_kwargs): # some of these are hard to test, and the ones that are easy are # tested elsewhere, so let's just make sure it runs here pass # ensure that *any* bad argument gets caught for key, val in bad_kwargs.items(): assert_raises(RuntimeError, Canvas, **{key: val}) @requires_application() def test_application(): """Test application running""" app = use_app() print(app) # __repr__ without app app.create() wrong = 'glfw' if app.backend_name.lower() != 'glfw' else 'pyqt4' assert_raises(RuntimeError, use_app, wrong) app.process_events() print(app) # test __repr__ assert_raises(ValueError, Canvas, keys='foo') assert_raises(TypeError, Canvas, keys=dict(escape=1)) assert_raises(ValueError, Canvas, keys=dict(escape='foo')) # not an attr pos = [0, 0] if app.backend_module.capability['position'] else None size = (100, 100) # Use "with" statement so failures don't leave open window # (and test context manager behavior) title = 'default' with Canvas(title=title, size=size, app=app, show=True, position=pos) as canvas: context = canvas.context assert_true(canvas.create_native() is None) # should be done already assert_is(canvas.app, app) assert_true(canvas.native) assert_equal('swap_buffers', canvas.events.draw.callback_refs[-1]) canvas.measure_fps(0.001) sleep(0.002) canvas.update() app.process_events() assert_true(canvas.fps > 0) # Other methods print(canvas) # __repr__ assert_equal(canvas.title, title) canvas.title = 'you' with use_log_level('warning', record=True, print_msg=False) as l: if app.backend_module.capability['position']: # todo: disable more tests based on capability canvas.position = pos canvas.size = size if 'ipynb_vnc' in canvas.app.backend_name.lower(): assert_true(len(l) >= 1) else: assert_true(len(l) == 0) canvas.connect(on_mouse_move) assert_raises(ValueError, canvas.connect, _on_mouse_move) if sys.platform != 'darwin': # XXX knownfail, prob. needs warmup canvas.show(False) canvas.show() app.process_events() assert_raises(ValueError, canvas.connect, on_nonexist) # deprecation of "paint" with use_log_level('info', record=True, print_msg=False) as log: olderr = sys.stderr try: fid = StringIO() sys.stderr = fid @canvas.events.paint.connect def fake(event): pass finally: sys.stderr = olderr assert_equal(len(log), 1) assert_in('deprecated', log[0]) # screenshots gl.glViewport(0, 0, *size) ss = _screenshot() assert_array_equal(ss.shape, size + (4,)) assert_equal(len(canvas._backend._vispy_get_geometry()), 4) if sys.platform != 'win32': # XXX knownfail for windows assert_array_equal(canvas.size, size) assert_equal(len(canvas.position), 2) # XXX knawnfail, doesn't "take" # GLOO: should have an OpenGL context already, so these should work vert = "void main (void) {gl_Position = pos;}" frag = "void main (void) {gl_FragColor = pos;}" program = Program(vert, frag) assert_raises(RuntimeError, program.glir.flush, context.shared.parser) vert = "uniform vec4 pos;\nvoid main (void) {gl_Position = pos;}" frag = "uniform vec4 pos;\nvoid main (void) {gl_FragColor = pos;}" program = Program(vert, frag) #uniform = program.uniforms[0] program['pos'] = [1, 2, 3, 4] vert = "attribute vec4 pos;\nvoid main (void) {gl_Position = pos;}" frag = "void main (void) {}" program = Program(vert, frag) #attribute = program.attributes[0] program["pos"] = [1, 2, 3, 4] # use a real program program._glir.clear() vert = ("uniform mat4 u_model;" "attribute vec2 a_position; attribute vec4 a_color;" "varying vec4 v_color;" "void main (void) {v_color = a_color;" "gl_Position = u_model * vec4(a_position, 0.0, 1.0);" "v_color = a_color;}") frag = "void main() {gl_FragColor = vec4(0, 0, 0, 1);}" n, p = 250, 50 T = np.random.uniform(0, 2 * np.pi, n) position = np.zeros((n, 2), dtype=np.float32) position[:, 0] = np.cos(T) position[:, 1] = np.sin(T) color = np.ones((n, 4), dtype=np.float32) * (1, 1, 1, 1) data = np.zeros(n * p, [('a_position', np.float32, 2), ('a_color', np.float32, 4)]) data['a_position'] = np.repeat(position, p, axis=0) data['a_color'] = np.repeat(color, p, axis=0) program = Program(vert, frag) program.bind(VertexBuffer(data)) program['u_model'] = np.eye(4, dtype=np.float32) # different codepath if no call to activate() program.draw(gl.GL_POINTS) subset = IndexBuffer(np.arange(10, dtype=np.uint32)) program.draw(gl.GL_POINTS, subset) # bad programs frag_bad = ("varying vec4 v_colors") # no semicolon program = Program(vert, frag_bad) assert_raises(RuntimeError, program.glir.flush, context.shared.parser) frag_bad = None # no fragment code. no main is not always enough assert_raises(ValueError, Program, vert, frag_bad) # Timer timer = Timer(interval=0.001, connect=on_mouse_move, iterations=2, start=True, app=app) timer.start() timer.interval = 0.002 assert_equal(timer.interval, 0.002) assert_true(timer.running) sleep(.003) assert_true(timer.elapsed >= 0.002) timer.stop() assert_true(not timer.running) assert_true(timer.native) timer.disconnect() # test that callbacks take reasonable inputs _test_callbacks(canvas) # cleanup canvas.swap_buffers() canvas.update() app.process_events() # put this in even though __exit__ will call it to make sure we don't # have problems calling it multiple times canvas.close() # done by context @requires_application() def test_fs(): """Test fullscreen support""" a = use_app() if not a.backend_module.capability['fullscreen']: return assert_raises(TypeError, Canvas, fullscreen='foo') if (a.backend_name.lower() == 'glfw' or (a.backend_name.lower() == 'sdl2' and sys.platform == 'darwin')): raise SkipTest('Backend takes over screen') with use_log_level('warning', record=True, print_msg=False) as l: with Canvas(fullscreen=False) as c: assert_equal(c.fullscreen, False) c.fullscreen = True assert_equal(c.fullscreen, True) assert_equal(len(l), 0) with use_log_level('warning', record=True, print_msg=False): # some backends print a warning b/c fullscreen can't be specified with Canvas(fullscreen=0) as c: assert_equal(c.fullscreen, True) @requires_application() def test_close_keys(): """Test close keys""" c = Canvas(keys='interactive') x = list() @c.events.close.connect def closer(event): x.append('done') c.events.key_press(key=keys.ESCAPE, text='', modifiers=[]) assert_equal(len(x), 1) # ensure the close event was sent c.app.process_events() @requires_application() def test_event_order(): """Test event order""" x = list() class MyCanvas(Canvas): def on_initialize(self, event): x.append('init') def on_draw(self, event): sz = True if self.size is not None else False x.append('draw size=%s show=%s' % (sz, show)) def on_close(self, event): x.append('close') for show in (False, True): # clear our storage variable while x: x.pop() with MyCanvas(show=show) as c: c.update() c.app.process_events() print(x) assert_true(len(x) >= 3) assert_equal(x[0], 'init') assert_in('draw size=True', x[1]) assert_in('draw size=True', x[-2]) assert_equal(x[-1], 'close') def test_abstract(): """Test app abstract template""" app = BaseApplicationBackend() for fun in (app._vispy_get_backend_name, app._vispy_process_events, app._vispy_run, app._vispy_quit): assert_raises(NotImplementedError, fun) def test_mouse_key_events(): """Test mouse and key events""" me = MouseEvent('mouse_press') for fun in (me.pos, me.button, me.buttons, me.modifiers, me.delta, me.press_event, me.last_event, me.is_dragging): fun me.drag_events() me._forget_last_event() me.trail() ke = KeyEvent('key_release') ke.key ke.text ke.modifiers run_tests_if_main() vispy-0.4.0/vispy/app/tests/qt-designer.ui0000664000175000017500000000301012406355656022246 0ustar larsonerlarsoner00000000000000 Form 0 0 609 432 Form For testing that vispy Canvas can be embedded in Qt designer GUI QFrame::StyledPanel QFrame::Raised 0 0 QtSceneCanvas QWidget

vispy.app.qt
1 vispy-0.4.0/vispy/app/tests/test_interactive.py0000664000175000017500000000124512456026505023414 0ustar larsonerlarsoner00000000000000from vispy.testing import run_tests_if_main from vispy.app import set_interactive from vispy.ext.ipy_inputhook import inputhook_manager # Expect the inputhook_manager to set boolean `_in_event_loop` # on instances of this class when enabled. class MockApp(object): pass def test_interactive(): f = MockApp() set_interactive(enabled=True, app=f) assert inputhook_manager._current_gui == 'vispy' assert f._in_event_loop assert 'vispy' in inputhook_manager.apps assert f == inputhook_manager.apps['vispy'] set_interactive(enabled=False) assert inputhook_manager._current_gui is None assert not f._in_event_loop run_tests_if_main() vispy-0.4.0/vispy/app/tests/test_simultaneous.py0000664000175000017500000001034112510536123023615 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- import numpy as np from numpy.testing import assert_allclose from time import sleep from vispy.app import use_app, Canvas, Timer from vispy.testing import requires_application, SkipTest, run_tests_if_main from vispy.util.ptime import time from vispy.gloo import gl from vispy.gloo.util import _screenshot _win_size = (200, 50) def _update_process_check(canvas, val, draw=True): """Update, process, and check result""" if draw: canvas.update() canvas.app.process_events() canvas.app.process_events() sleep(0.03) # give it time to swap (Qt?) canvas._backend._vispy_set_current() print(' check %s' % val) # check screenshot to see if it's all one color ss = _screenshot() try: assert_allclose(ss.shape[:2], _win_size[::-1]) except Exception: print('!!!!!!!!!! FAIL bad size %s' % list(ss.shape[:2])) raise goal = val * np.ones(ss.shape) try: # Get rid of the alpha value before testing # It can be off by 1 due to rounding assert_allclose(ss[:, :, :3], goal[:, :, :3], atol=1) except Exception: print('!!!!!!!!!! FAIL %s' % np.unique(ss)) raise @requires_application() def test_multiple_canvases(): """Testing multiple canvases""" n_check = 3 app = use_app() with Canvas(app=app, size=_win_size, title='same_0') as c0: with Canvas(app=app, size=_win_size, title='same_1') as c1: ct = [0, 0] @c0.events.draw.connect def draw0(event): ct[0] += 1 c0.update() @c1.events.draw.connect # noqa, analysis:ignore def draw1(event): ct[1] += 1 c1.update() c0.show() # ensure visible c1.show() c0.update() # force first draw c1.update() timeout = time() + 2.0 while (ct[0] < n_check or ct[1] < n_check) and time() < timeout: app.process_events() print((ct, n_check)) assert n_check <= ct[0] <= n_check + 2 # be a bit lenient assert n_check <= ct[1] <= n_check + 2 # check timer global timer_ran timer_ran = False def on_timer(_): global timer_ran timer_ran = True t = Timer(0.1, app=app, connect=on_timer, iterations=1, # noqa start=True) app.process_events() sleep(0.5) # long for slow systems app.process_events() app.process_events() assert timer_ran if app.backend_name.lower() == 'wx': raise SkipTest('wx fails test #2') # XXX TODO Fix this kwargs = dict(app=app, autoswap=False, size=_win_size, show=True) with Canvas(title='0', **kwargs) as c0: with Canvas(title='1', **kwargs) as c1: bgcolors = [None] * 2 @c0.events.draw.connect def draw00(event): print(' {0:7}: {1}'.format('0', bgcolors[0])) if bgcolors[0] is not None: gl.glViewport(0, 0, *list(_win_size)) gl.glClearColor(*bgcolors[0]) gl.glClear(gl.GL_COLOR_BUFFER_BIT) gl.glFinish() @c1.events.draw.connect def draw11(event): print(' {0:7}: {1}'.format('1', bgcolors[1])) if bgcolors[1] is not None: gl.glViewport(0, 0, *list(_win_size)) gl.glClearColor(*bgcolors[1]) gl.glClear(gl.GL_COLOR_BUFFER_BIT) gl.glFinish() for ci, canvas in enumerate((c0, c1)): print('draw %s' % canvas.title) bgcolors[ci] = [0.5, 0.5, 0.5, 1.0] _update_process_check(canvas, 127) for ci, canvas in enumerate((c0, c1)): print('test') _update_process_check(canvas, 127, draw=False) bgcolors[ci] = [1., 1., 1., 1.] _update_process_check(canvas, 255) bgcolors[ci] = [0.25, 0.25, 0.25, 0.25] _update_process_check(canvas, 64) run_tests_if_main() vispy-0.4.0/vispy/testing/0000775000175000017500000000000012527674621017226 5ustar larsonerlarsoner00000000000000vispy-0.4.0/vispy/testing/_runners.py0000664000175000017500000002743512527672621021444 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # vispy: testskip # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """Test running functions""" from __future__ import print_function import sys import os from os import path as op from copy import deepcopy from functools import partial from ..util import use_log_level, run_subprocess from ..util.ptime import time from ._testing import SkipTest, has_backend, has_application, nottest _line_sep = '-' * 70 def _get_root_dir(): root_dir = os.getcwd() if (op.isfile(op.join(root_dir, 'setup.py')) and op.isdir(op.join(root_dir, 'vispy'))): dev = True else: root_dir = op.abspath(op.join(op.dirname(__file__), '..', '..')) dev = True if op.isfile(op.join(root_dir, 'setup.py')) else False return root_dir, dev _unit_script = """ import pytest try: import faulthandler faulthandler.enable() except Exception: pass raise SystemExit(pytest.main(%r)) """ def _unit(mode, extra_arg_string): """Run unit tests using a particular mode""" cwd = os.getcwd() try: import pytest # noqa, analysis:ignore except ImportError: print('Skipping pytest, pytest not installed') raise SkipTest() if mode == 'nobackend': msg = 'Running tests with no backend' extra_arg_string = '-m "not vispy_app_test" ' + extra_arg_string coverage = True else: with use_log_level('warning', print_msg=False): has, why_not = has_backend(mode, out=['why_not']) if not has: msg = ('Skipping tests for backend %s, not found (%s)' % (mode, why_not)) print(_line_sep + '\n' + msg + '\n' + _line_sep + '\n') raise SkipTest(msg) msg = 'Running tests with %s backend' % mode extra_arg_string = '-m vispy_app_test ' + extra_arg_string coverage = True if coverage: extra_arg_string += ' --cov vispy --no-cov-on-fail ' # make a call to "python" so that it inherits whatever the system # thinks is "python" (e.g., virtualenvs) cmd = [sys.executable, '-c', _unit_script % extra_arg_string] env = deepcopy(os.environ) # We want to set this for all app backends plus "nobackend" to # help ensure that app tests are appropriately decorated env.update(dict(_VISPY_TESTING_APP=mode)) env_str = '_VISPY_TESTING_APP=%s ' % mode if len(msg) > 0: msg = ('%s\n%s:\n%s%s' % (_line_sep, msg, env_str, extra_arg_string)) print(msg) sys.stdout.flush() return_code = run_subprocess(cmd, return_code=True, cwd=cwd, env=env, stdout=None, stderr=None)[2] if return_code: raise RuntimeError('unit failure (%s)' % return_code) else: out_name = '.coverage.%s' % mode if op.isfile(out_name): os.remove(out_name) os.rename('.coverage', out_name) def _flake(): """Test flake8""" orig_dir = os.getcwd() root_dir, dev = _get_root_dir() os.chdir(root_dir) if dev: sys.argv[1:] = ['vispy', 'examples', 'make'] else: sys.argv[1:] = ['vispy'] sys.argv.append('--ignore=E226,E241,E265,E266,W291,W293,W503') sys.argv.append('--exclude=six.py,py24_ordereddict.py,glfw.py,' '_proxy.py,_es2.py,_gl2.py,_pyopengl2.py,' '_constants.py,png.py,decorator.py,ipy_inputhook.py,' 'experimental,wiki,_old,mplexporter.py,cubehelix.py') try: from flake8.main import main except ImportError: print('Skipping flake8 test, flake8 not installed') else: print('Running flake8... ') # if end='', first error gets ugly sys.stdout.flush() try: main() except SystemExit as ex: if ex.code in (None, 0): pass # do not exit yet, we want to print a success msg else: raise RuntimeError('flake8 failed') finally: os.chdir(orig_dir) def _check_line_endings(): """Check all files in the repository for CR characters""" if sys.platform == 'win32': print('Skipping line endings check on Windows') sys.stdout.flush() return print('Running line endings check... ') sys.stdout.flush() report = [] root_dir, dev = _get_root_dir() if not dev: root_dir = op.join(root_dir, 'vispy') for dirpath, dirnames, filenames in os.walk(root_dir): for fname in filenames: if op.splitext(fname)[1] in ('.pyc', '.pyo', '.so', '.dll'): continue # Get filename filename = op.join(dirpath, fname) relfilename = op.relpath(filename, root_dir) # Open and check try: with open(filename, 'rb') as fid: text = fid.read().decode('utf-8') except UnicodeDecodeError: continue # Probably a binary file crcount = text.count('\r') if crcount: lfcount = text.count('\n') report.append('In %s found %i/%i CR/LF' % (relfilename, crcount, lfcount)) # Process result if len(report) > 0: raise RuntimeError('Found %s files with incorrect endings:\n%s' % (len(report), '\n'.join(report))) _script = """ import sys import time import warnings try: import faulthandler faulthandler.enable() except Exception: pass import {0} if hasattr({0}, 'canvas'): canvas = {0}.canvas elif hasattr({0}, 'Canvas'): canvas = {0}.Canvas() elif hasattr({0}, 'fig'): canvas = {0}.fig else: raise RuntimeError('Bad example formatting: fix or add `# vispy: testskip`' ' to the top of the file.') with canvas as c: for _ in range(5): c.update() c.app.process_events() time.sleep(1./60.) """ def _examples(fnames_str): """Run examples and make sure they work. Parameters ---------- fnames_str : str Can be a space-separated list of paths to test, or an empty string to auto-detect and run all examples. """ root_dir, dev = _get_root_dir() reason = None if not dev: reason = 'Cannot test examples unless in vispy git directory' else: with use_log_level('warning', print_msg=False): good, backend = has_application(capable=('multi_window',)) if not good: reason = 'Must have suitable app backend' if reason is not None: msg = 'Skipping example test: %s' % reason print(msg) raise SkipTest(msg) # if we're given individual file paths as a string in fnames_str, # then just use them as the fnames # otherwise, use the full example paths that have been # passed to us if fnames_str: fnames = fnames_str.split(' ') else: fnames = [op.join(d[0], fname) for d in os.walk(op.join(root_dir, 'examples')) for fname in d[2] if fname.endswith('.py')] fnames = sorted(fnames, key=lambda x: x.lower()) print(_line_sep + '\nRunning %s examples using %s backend' % (len(fnames), backend)) op.join('tutorial', 'app', 'shared_context.py'), # non-standard fails = [] n_ran = n_skipped = 0 t0 = time() for fname in fnames: n_ran += 1 root_name = op.split(fname) root_name = op.join(op.split(op.split(root_name[0])[0])[1], op.split(root_name[0])[1], root_name[1]) good = True with open(fname, 'r') as fid: for _ in range(10): # just check the first 10 lines line = fid.readline() if line == '': break elif line.startswith('# vispy: ') and 'testskip' in line: good = False break if not good: n_ran -= 1 n_skipped += 1 continue sys.stdout.flush() cwd = op.dirname(fname) cmd = [sys.executable, '-c', _script.format(op.split(fname)[1][:-3])] sys.stdout.flush() stdout, stderr, retcode = run_subprocess(cmd, return_code=True, cwd=cwd, env=os.environ) if retcode or len(stderr.strip()) > 0: # Skipping due to missing dependency is okay if "ImportError: " in stderr: print('S', end='') else: ext = '\n' + _line_sep + '\n' fails.append('%sExample %s failed (%s):%s%s%s' % (ext, root_name, retcode, ext, stderr, ext)) print(fails[-1]) else: print('.', end='') sys.stdout.flush() print('') t = (': %s failed, %s succeeded, %s skipped in %s seconds' % (len(fails), n_ran - len(fails), n_skipped, round(time()-t0))) if len(fails) > 0: raise RuntimeError('Failed%s' % t) print('Success%s' % t) @nottest def test(label='full', extra_arg_string=''): """Test vispy software Parameters ---------- label : str Can be one of 'full', 'unit', 'nobackend', 'extra', 'lineendings', 'flake', or any backend name (e.g., 'qt'). extra_arg_string : str Extra arguments to sent to ``pytest``. """ from vispy.app.backends import BACKEND_NAMES as backend_names label = label.lower() label = 'pytest' if label == 'nose' else label known_types = ['full', 'unit', 'lineendings', 'extra', 'flake', 'nobackend', 'examples'] if label not in known_types + backend_names: raise ValueError('label must be one of %s, or a backend name %s, ' 'not \'%s\'' % (known_types, backend_names, label)) work_dir = _get_root_dir()[0] orig_dir = os.getcwd() # figure out what we actually need to run runs = [] if label in ('full', 'unit'): for backend in backend_names: runs.append([partial(_unit, backend, extra_arg_string), backend]) elif label in backend_names: runs.append([partial(_unit, label, extra_arg_string), label]) if label == "examples": # take the extra arguments so that specific examples can be run runs.append([partial(_examples, extra_arg_string), 'examples']) elif label == 'full': # run all the examples runs.append([partial(_examples, ""), 'examples']) if label in ('full', 'unit', 'nobackend'): runs.append([partial(_unit, 'nobackend', extra_arg_string), 'nobackend']) if label in ('full', 'extra', 'lineendings'): runs.append([_check_line_endings, 'lineendings']) if label in ('full', 'extra', 'flake'): runs.append([_flake, 'flake']) t0 = time() fail = [] skip = [] for run in runs: try: os.chdir(work_dir) run[0]() except RuntimeError as exp: print('Failed: %s' % str(exp)) fail += [run[1]] except SkipTest: skip += [run[1]] except Exception as exp: # this should only happen if we've screwed up the test setup fail += [run[1]] print('Failed strangely (%s): %s\n' % (type(exp), str(exp))) import traceback type_, value, tb = sys.exc_info() traceback.print_exception(type_, value, tb) else: print('Passed\n') finally: sys.stdout.flush() os.chdir(orig_dir) dt = time() - t0 stat = '%s failed, %s skipped' % (fail if fail else 0, skip if skip else 0) extra = 'failed' if fail else 'succeeded' print('Testing %s (%s) in %0.3f seconds' % (extra, stat, dt)) sys.stdout.flush() if len(fail) > 0: raise RuntimeError('FAILURE') vispy-0.4.0/vispy/testing/image_tester.py0000664000175000017500000004062112527672621022251 0ustar larsonerlarsoner00000000000000 """ Procedure for unit-testing with images: 1. Run unit tests at least once; this initializes a git clone of vispy/test-data in config['test_data_path']. This path is `~/.vispy/test-data` unless the config variable has been modified. 2. Run individual test scripts with the --vispy-audit flag: $ python vispy/visuals/tests/test_ellipse.py --vispy-audit Any failing tests will display the test results, standard image, and the differences between the two. If the test result is bad, then press (f)ail. If the test result is good, then press (p)ass and the new image will be saved to the test-data directory. 3. After adding or changing test images, create a new commit: $ cd ~/.vispy/test-data $ git add ... $ git commit -a 4. Look up the most recent tag name from the `test_data_tag` variable in get_test_data_repo() below. Increment the tag name by 1 in the function and create a new tag in the test-data repository: $ git tag test-data-NNN $ git push --tags origin master This tag is used to ensure that each vispy commit is linked to a specific commit in the test-data repository. This makes it possible to push new commits to the test-data repository without interfering with existing tests, and also allows unit tests to continue working on older vispy versions. """ import time import os import sys import inspect import base64 from subprocess import check_call, CalledProcessError import numpy as np from ..ext.six.moves import http_client as httplib from ..ext.six.moves import urllib_parse as urllib from .. import scene, config from ..io import read_png, write_png from ..gloo.util import _screenshot from ..util import run_subprocess tester = None def get_tester(): global tester if tester is None: tester = ImageTester() return tester def assert_image_approved(image, standard_file, message=None, **kwargs): """Check that an image test result matches a pre-approved standard. If the result does not match, then the user can optionally invoke a GUI to compare the images and decide whether to fail the test or save the new image as the standard. This function will automatically clone the test-data repository into ~/.vispy/test-data. However, it is up to the user to ensure this repository is kept up to date and to commit/push new images after they are saved. Parameters ---------- image : (h, w, 4) ndarray or 'screenshot' The test result to check standard_file : str The name of the approved test image to check against. This file name is relative to the root of the vispy test-data repository and will be automatically fetched. message : str A string description of the image. It is recommended to describe specific features that an auditor should look for when deciding whether to fail a test. Extra keyword arguments are used to set the thresholds for automatic image comparison (see ``assert_image_match()``). """ if image == "screenshot": image = _screenshot(alpha=True) if message is None: code = inspect.currentframe().f_back.f_code message = "%s::%s" % (code.co_filename, code.co_name) # Make sure we have a test data repo available, possibly invoking git data_path = get_test_data_repo() # Read the standard image if it exists std_file = os.path.join(data_path, standard_file) if not os.path.isfile(std_file): std_image = None else: std_image = read_png(std_file) # If the test image does not match, then we go to audit if requested. try: if image.shape != std_image.shape: # Allow im1 to be an integer multiple larger than im2 to account # for high-resolution displays ims1 = np.array(image.shape).astype(float) ims2 = np.array(std_image.shape).astype(float) sr = ims1 / ims2 if (sr[0] != sr[1] or not np.allclose(sr, np.round(sr)) or sr[0] < 1): raise TypeError("Test result shape %s is not an integer factor" " larger than standard image shape %s." % (ims1, ims2)) sr = np.round(sr).astype(int) image = downsample(image, sr[0], axis=(0, 1)).astype(image.dtype) assert_image_match(image, std_image, **kwargs) except Exception: if standard_file in git_status(data_path): print("\n\nWARNING: unit test failed against modified standard " "image %s.\nTo revert this file, run `cd %s; git checkout " "%s`\n" % (std_file, data_path, standard_file)) if config['audit_tests']: sys.excepthook(*sys.exc_info()) get_tester().test(image, std_image, message) std_path = os.path.dirname(std_file) print('Saving new standard image to "%s"' % std_file) if not os.path.isdir(std_path): os.makedirs(std_path) write_png(std_file, image) else: if std_image is None: raise Exception("Test standard %s does not exist." % std_file) else: if os.getenv('TRAVIS') is not None: _save_failed_test(image, std_image, standard_file) raise def assert_image_match(im1, im2, min_corr=0.9, px_threshold=50., px_count=None, max_px_diff=None, avg_px_diff=None, img_diff=None): """Check that two images match. Images that differ in shape or dtype will fail unconditionally. Further tests for similarity depend on the arguments supplied. Parameters ---------- im1 : (h, w, 4) ndarray Test output image im2 : (h, w, 4) ndarray Test standard image min_corr : float or None Minimum allowed correlation coefficient between corresponding image values (see numpy.corrcoef) px_threshold : float Minimum value difference at which two pixels are considered different px_count : int or None Maximum number of pixels that may differ max_px_diff : float or None Maximum allowed difference between pixels avg_px_diff : float or None Average allowed difference between pixels img_diff : float or None Maximum allowed summed difference between images """ assert im1.ndim == 3 assert im1.shape[2] == 4 assert im1.dtype == im2.dtype diff = im1.astype(float) - im2.astype(float) if img_diff is not None: assert np.abs(diff).sum() <= img_diff pxdiff = diff.max(axis=2) # largest value difference per pixel mask = np.abs(pxdiff) >= px_threshold if px_count is not None: assert mask.sum() <= px_count masked_diff = diff[mask] if max_px_diff is not None and masked_diff.size > 0: assert masked_diff.max() <= max_px_diff if avg_px_diff is not None and masked_diff.size > 0: assert masked_diff.mean() <= avg_px_diff if min_corr is not None: with np.errstate(invalid='ignore'): corr = np.corrcoef(im1.ravel(), im2.ravel())[0, 1] assert corr >= min_corr def _save_failed_test(data, expect, filename): from ..io import _make_png commit, error = run_subprocess(['git', 'rev-parse', 'HEAD']) name = filename.split('/') name.insert(-1, commit.strip()) filename = '/'.join(name) host = 'data.vispy.org' # concatenate data, expect, and diff into a single image ds = data.shape es = expect.shape shape = (max(ds[0], es[0]) + 4, ds[1] + es[1] + 8 + max(ds[1], es[1]), 4) img = np.empty(shape, dtype=np.ubyte) img[..., :3] = 100 img[..., 3] = 255 img[2:2+ds[0], 2:2+ds[1], :ds[2]] = data img[2:2+es[0], ds[1]+4:ds[1]+4+es[1], :es[2]] = expect diff = make_diff_image(data, expect) img[2:2+diff.shape[0], -diff.shape[1]-2:-2] = diff png = _make_png(img) conn = httplib.HTTPConnection(host) req = urllib.urlencode({'name': filename, 'data': base64.b64encode(png)}) conn.request('POST', '/upload.py', req) response = conn.getresponse().read() conn.close() print("\nImage comparison failed. Test result: %s %s Expected result: " "%s %s" % (data.shape, data.dtype, expect.shape, expect.dtype)) print("Uploaded to: \nhttp://%s/data/%s" % (host, filename)) if not response.startswith(b'OK'): print("WARNING: Error uploading data to %s" % host) print(response) def make_diff_image(im1, im2): """Return image array showing the differences between im1 and im2. Handles images of different shape. Alpha channels are not compared. """ ds = im1.shape es = im2.shape diff = np.empty((max(ds[0], es[0]), max(ds[1], es[1]), 4), dtype=int) diff[..., :3] = 128 diff[..., 3] = 255 diff[:ds[0], :ds[1], :min(ds[2], 3)] += im1[..., :3] diff[:es[0], :es[1], :min(es[2], 3)] -= im2[..., :3] diff = np.clip(diff, 0, 255).astype(np.ubyte) return diff def downsample(data, n, axis=0): """Downsample by averaging points together across axis. If multiple axes are specified, runs once per axis. """ if hasattr(axis, '__len__'): if not hasattr(n, '__len__'): n = [n]*len(axis) for i in range(len(axis)): data = downsample(data, n[i], axis[i]) return data if n <= 1: return data nPts = int(data.shape[axis] / n) s = list(data.shape) s[axis] = nPts s.insert(axis+1, n) sl = [slice(None)] * data.ndim sl[axis] = slice(0, nPts*n) d1 = data[tuple(sl)] d1.shape = tuple(s) d2 = d1.mean(axis+1) return d2 class ImageTester(scene.SceneCanvas): """Graphical interface for auditing image comparison tests. """ def __init__(self): scene.SceneCanvas.__init__(self, size=(1000, 800)) self.bgcolor = (0.1, 0.1, 0.1, 1) self.grid = self.central_widget.add_grid() border = (0.3, 0.3, 0.3, 1) self.views = (self.grid.add_view(row=0, col=0, border_color=border), self.grid.add_view(row=0, col=1, border_color=border), self.grid.add_view(row=0, col=2, border_color=border)) label_text = ['test output', 'standard', 'diff'] for i, v in enumerate(self.views): v.camera = 'panzoom' v.camera.aspect = 1 v.camera.flip = (False, True) v.image = scene.Image(parent=v.scene) v.label = scene.Text(label_text[i], parent=v, color='yellow', anchor_x='left', anchor_y='top') self.views[1].camera.link(self.views[0].camera) self.views[2].camera.link(self.views[0].camera) self.console = scene.Console(text_color='white', border_color=border) self.grid.add_widget(self.console, row=1, col=0, col_span=3) def test(self, im1, im2, message): self.show() self.console.write('------------------') self.console.write(message) if im2 is None: self.console.write('Image1: %s %s Image2: [no standard]' % (im1.shape, im1.dtype)) im2 = np.zeros((1, 1, 3), dtype=np.ubyte) else: self.console.write('Image1: %s %s Image2: %s %s' % (im1.shape, im1.dtype, im2.shape, im2.dtype)) self.console.write('(P)ass or (F)ail this test?') self.views[0].image.set_data(im1) self.views[1].image.set_data(im2) diff = make_diff_image(im1, im2) self.views[2].image.set_data(diff) self.views[0].camera.set_range() self.last_key = None while True: self.app.process_events() if self.last_key is None: pass elif self.last_key.lower() == 'p': self.console.write('PASS') break elif self.last_key.lower() in ('f', 'esc'): self.console.write('FAIL') raise Exception("User rejected test result.") time.sleep(0.03) for v in self.views: v.image.set_data(np.zeros((1, 1, 3), dtype=np.ubyte)) def on_key_press(self, event): self.last_key = event.key.name def get_test_data_repo(): """Return the path to a git repository with the required commit checked out. If the repository does not exist, then it is cloned from https://github.com/vispy/test-data. If the repository already exists then the required commit is checked out. """ # This tag marks the test-data commit that this version of vispy should # be tested against. When adding or changing test images, create # and push a new tag and update this variable. test_data_tag = 'test-data-1' data_path = config['test_data_path'] git_path = 'https://github.com/vispy/test-data' gitbase = git_cmd_base(data_path) if os.path.isdir(data_path): # Already have a test-data repository to work with. # Get the commit ID of test_data_tag. Do a fetch if necessary. try: tag_commit = git_commit_id(data_path, test_data_tag) except NameError: cmd = gitbase + ['fetch', '--tags', 'origin'] print(' '.join(cmd)) check_call(cmd) try: tag_commit = git_commit_id(data_path, test_data_tag) except NameError: raise Exception("Could not find tag '%s' in test-data repo at" " %s" % (test_data_tag, data_path)) except Exception: if not os.path.exists(os.path.join(data_path, '.git')): raise Exception("Directory '%s' does not appear to be a git " "repository. Please remove this directory." % data_path) else: raise # If HEAD is not the correct commit, then do a checkout if git_commit_id(data_path, 'HEAD') != tag_commit: print("Checking out test-data tag '%s'" % test_data_tag) check_call(gitbase + ['checkout', test_data_tag]) else: print("Attempting to create git clone of test data repo in %s.." % data_path) parent_path = os.path.split(data_path)[0] if not os.path.isdir(parent_path): os.makedirs(parent_path) if os.getenv('TRAVIS') is not None: # Create a shallow clone of the test-data repository (to avoid # downloading more data than is necessary) os.makedirs(data_path) cmds = [ gitbase + ['init'], gitbase + ['remote', 'add', 'origin', git_path], gitbase + ['fetch', '--tags', 'origin', test_data_tag, '--depth=1'], gitbase + ['checkout', '-b', 'master', 'FETCH_HEAD'], ] else: # Create a full clone cmds = [['git', 'clone', git_path, data_path]] for cmd in cmds: print(' '.join(cmd)) rval = check_call(cmd) if rval == 0: continue raise RuntimeError("Test data path '%s' does not exist and could " "not be created with git. Either create a git " "clone of %s or set the test_data_path " "variable to an existing clone." % (data_path, git_path)) return data_path def git_cmd_base(path): return ['git', '--git-dir=%s/.git' % path, '--work-tree=%s' % path] def git_status(path): """Return a string listing all changes to the working tree in a git repository. """ cmd = git_cmd_base(path) + ['status', '--porcelain'] return run_subprocess(cmd, stderr=None, universal_newlines=True)[0] def git_commit_id(path, ref): """Return the commit id of *ref* in the git repository at *path*. """ cmd = git_cmd_base(path) + ['show', ref] try: output = run_subprocess(cmd, stderr=None, universal_newlines=True)[0] except CalledProcessError: raise NameError("Unknown git reference '%s'" % ref) commit = output.split('\n')[0] assert commit[:7] == 'commit ' return commit[7:] vispy-0.4.0/vispy/testing/_testing.py0000664000175000017500000002543312527672621021421 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2015, Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- from __future__ import print_function import numpy as np import sys import os import inspect from distutils.version import LooseVersion from ..ext.six import string_types from ..util import use_log_level ############################################################################### # Adapted from Python's unittest2 # http://docs.python.org/2/license.html try: from unittest.case import SkipTest except ImportError: try: from unittest2.case import SkipTest except ImportError: class SkipTest(Exception): pass def _safe_rep(obj, short=False): """Helper for assert_* ports""" try: result = repr(obj) except Exception: result = object.__repr__(obj) if not short or len(result) < 80: return result return result[:80] + ' [truncated]...' def _safe_str(obj): """Helper for assert_* ports""" try: return str(obj) except Exception: return object.__str__(obj) def _format_msg(msg, std_msg): """Helper for assert_* ports""" if msg is None: msg = std_msg else: try: msg = '%s : %s' % (std_msg, msg) except UnicodeDecodeError: msg = '%s : %s' % (_safe_str(std_msg), _safe_str(msg)) return msg def nottest(func): """Decorator to mark a function or method as *not* a test """ func.__test__ = False return func def assert_raises(exp, func, *args, **kwargs): """Backport""" try: func(*args, **kwargs) except exp: return std_msg = '%s not raised' % (_safe_rep(exp)) raise AssertionError(_format_msg(None, std_msg)) def assert_in(member, container, msg=None): """Backport""" if member in container: return std_msg = '%s not found in %s' % (_safe_rep(member), _safe_rep(container)) raise AssertionError(_format_msg(msg, std_msg)) def assert_true(x, msg=None): """Backport""" if x: return std_msg = '%s is not True' % (_safe_rep(x),) raise AssertionError(_format_msg(msg, std_msg)) def assert_equal(x, y, msg=None): """Backport""" if x == y: return std_msg = '%s not equal to %s' % (_safe_rep(x), _safe_rep(y)) raise AssertionError(_format_msg(msg, std_msg)) def assert_not_equal(x, y, msg=None): """Backport""" if x != y: return std_msg = '%s equal to %s' % (_safe_rep(x), _safe_rep(y)) raise AssertionError(_format_msg(msg, std_msg)) def assert_not_in(member, container, msg=None): """Backport""" if member not in container: return std_msg = '%s found in %s' % (_safe_rep(member), _safe_rep(container)) raise AssertionError(_format_msg(msg, std_msg)) def assert_is(expr1, expr2, msg=None): """Backport""" if expr1 is not expr2: std_msg = '%s is not %s' % (_safe_rep(expr1), _safe_rep(expr2)) raise AssertionError(_format_msg(msg, std_msg)) ############################################################################### # GL stuff def has_pyopengl(): try: from OpenGL import GL # noqa, analysis:ignore except Exception: return False else: return True def requires_pyopengl(): return np.testing.dec.skipif(not has_pyopengl(), 'Requires PyOpenGL') ############################################################################### # App stuff def has_backend(backend, has=(), capable=(), out=()): from ..app.backends import BACKENDMAP using = os.getenv('_VISPY_TESTING_APP', None) if using is not None and using != backend: # e.g., we are on a 'pyglet' run but the test requires PyQt4 ret = (False,) if len(out) > 0 else False for o in out: ret += (None,) return ret # let's follow the standard code path module_name = BACKENDMAP[backend.lower()][1] with use_log_level('warning', print_msg=False): mod = __import__('app.backends.%s' % module_name, globals(), level=2) mod = getattr(mod.backends, module_name) good = mod.testable for h in has: good = (good and getattr(mod, 'has_%s' % h)) for cap in capable: good = (good and mod.capability[cap]) ret = (good,) if len(out) > 0 else good for o in out: ret += (getattr(mod, o),) return ret def has_application(backend=None, has=(), capable=()): """Determine if a suitable app backend exists""" from ..app.backends import BACKEND_NAMES # avoid importing other backends if we don't need to if backend is None: for backend in BACKEND_NAMES: if has_backend(backend, has=has, capable=capable): good = True msg = backend break else: good = False msg = 'Requires application backend' else: good, why = has_backend(backend, has=has, capable=capable, out=['why_not']) if not good: msg = 'Requires %s: %s' % (backend, why) else: msg = backend return good, msg def composed(*decs): def deco(f): for dec in reversed(decs): f = dec(f) return f return deco def requires_application(backend=None, has=(), capable=()): """Return a decorator for tests that require an application""" good, msg = has_application(backend, has, capable) dec_backend = np.testing.dec.skipif(not good, "Skipping test: %s" % msg) try: import pytest except Exception: return dec_backend dec_app = pytest.mark.vispy_app_test return composed(dec_app, dec_backend) def requires_img_lib(): """Decorator for tests that require an image library""" from ..io import _check_img_lib if sys.platform.startswith('win'): has_img_lib = False # PIL breaks tests on windows (!) else: has_img_lib = not all(c is None for c in _check_img_lib()) return np.testing.dec.skipif(not has_img_lib, 'imageio or PIL required') def has_matplotlib(version='1.2'): """Determine if mpl is a usable version""" try: import matplotlib except Exception: has_mpl = False else: if LooseVersion(matplotlib.__version__) >= LooseVersion(version): has_mpl = True else: has_mpl = False return has_mpl ############################################################################### # Visuals stuff def _has_scipy(min_version): try: assert isinstance(min_version, string_types) import scipy # noqa, analysis:ignore from distutils.version import LooseVersion this_version = LooseVersion(scipy.__version__) if this_version < min_version: return False except Exception: return False else: return True def requires_scipy(min_version='0.13'): return np.testing.dec.skipif(not _has_scipy(min_version), 'Requires Scipy version >= %s' % min_version) @nottest def TestingCanvas(bgcolor='black', size=(100, 100), dpi=None, **kwargs): """Class wrapper to avoid importing scene until necessary""" from ..scene import SceneCanvas class TestingCanvas(SceneCanvas): def __init__(self, bgcolor, size, dpi, **kwargs): self._entered = False kwargs['bgcolor'] = bgcolor kwargs['size'] = size kwargs['dpi'] = dpi SceneCanvas.__init__(self, **kwargs) def __enter__(self): SceneCanvas.__enter__(self) # sometimes our window can be larger than our requsted draw # area (e.g. on Windows), and this messes up our tests that # typically use very small windows. Here we "fix" it. scale = np.array(self.physical_size) / np.array(self.size, float) scale = int(np.round(np.mean(scale))) self._wanted_vp = 0, 0, size[0] * scale, size[1] * scale self.context.set_state(clear_color=self._bgcolor) self.context.set_viewport(*self._wanted_vp) self._entered = True return self def draw_visual(self, visual, event=None, viewport=None, clear=True): if not self._entered: return if clear: self.context.clear() SceneCanvas.draw_visual(self, visual, event, viewport) # must set this because draw_visual sets it back to the # canvas size when it's done self.context.set_viewport(*self._wanted_vp) self.context.finish() return TestingCanvas(bgcolor, size, dpi, **kwargs) @nottest def save_testing_image(image, location): from ..gloo.util import _screenshot from ..util import make_png if image == "screenshot": image = _screenshot(alpha=False) with open(location+'.png', 'wb') as fid: fid.write(make_png(image)) @nottest def run_tests_if_main(): """Run tests in a given file if it is run as a script""" local_vars = inspect.currentframe().f_back.f_locals if not local_vars.get('__name__', '') == '__main__': return # we are in a "__main__" fname = local_vars['__file__'] # Run ourselves. post-mortem debugging! try: import faulthandler faulthandler.enable() except Exception: pass import __main__ try: import pytest pytest.main(['-s', '--tb=short', fname]) except ImportError: print('==== Running tests in script\n==== %s' % fname) run_tests_in_object(__main__) print('==== Tests pass') def run_tests_in_object(ob): # Setup for name in dir(ob): if name.lower().startswith('setup'): print('Calling %s' % name) getattr(ob, name)() # Exec for name in sorted(dir(ob), key=lambda x: x.lower()): # consistent order val = getattr(ob, name) if name.startswith('_'): continue elif callable(val) and (name[:4] == 'test' or name[-4:] == 'test'): print('Running test-func %s ... ' % name, end='') try: val() print('ok') except Exception as err: if 'skiptest' in err.__class__.__name__.lower(): print('skip') else: raise elif isinstance(val, type) and 'Test' in name: print('== Running test-class %s' % name) run_tests_in_object(val()) print('== Done with test-class %s' % name) # Teardown for name in dir(ob): if name.lower().startswith('teardown'): print('Calling %s' % name) getattr(ob, name)() vispy-0.4.0/vispy/testing/__init__.py0000664000175000017500000000443112527672621021337 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Testing ======= This module provides functions useful for running tests in vispy. Tests can be run in a few ways: * From Python, you can import ``vispy`` and do ``vispy.test()``. * From the source root, you can do ``make test`` which wraps to a call to ``python make test``. There are various diffrent testing "modes", including: * "full": run all tests. * any backend name (e.g., "glfw"): run application/GL tests using a specific backend. * "nobackend": run tests that do not require a backend. * "examples": run repo examples to check for errors and warnings. * "flake": check style errors. Examples get automatically tested unless they have a special comment toward the top ``# vispy: testskip``. Examples that should be tested should be formatted so that 1) a ``Canvas`` class is defined, or a ``canvas`` class is instantiated; and 2) the ``app.run()`` call is protected by a check if ``__name__ == "__main__"``. This makes it so that the event loop is not started when running examples in the test suite -- the test suite instead manually updates the canvas (using ``app.process_events()``) for under one second to ensure that things like timer events are processed. For examples on how to test various bits of functionality (e.g., application functionality, or drawing things with OpenGL), it's best to look at existing examples in the test suite. The code base gets automatically tested by Travis-CI (Linux) and AppVeyor (Windows) on Python 2.6, 2.7, 3.4. There are multiple testing modes that use e.g. full dependencies, minimal dependencies, etc. See ``.travis.yml`` to determine what automatic tests are run. """ from ._testing import (SkipTest, requires_application, requires_img_lib, # noqa has_backend, requires_pyopengl, # noqa requires_scipy, has_matplotlib, # noqa save_testing_image, TestingCanvas, has_pyopengl, # noqa run_tests_if_main, assert_is, assert_in, assert_not_in, assert_equal, assert_not_equal, assert_raises, assert_true) # noqa from ._runners import test # noqa vispy-0.4.0/vispy/testing/tests/0000775000175000017500000000000012527674621020370 5ustar larsonerlarsoner00000000000000vispy-0.4.0/vispy/testing/tests/test_testing.py0000664000175000017500000000142712527672621023460 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2015, Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- from vispy.testing import (assert_in, assert_not_in, assert_is, run_tests_if_main, assert_raises) def test_testing(): """Test testing ports""" assert_raises(AssertionError, assert_in, 'foo', 'bar') assert_in('foo', 'foobar') assert_raises(AssertionError, assert_not_in, 'foo', 'foobar') assert_not_in('foo', 'bar') assert_raises(AssertionError, assert_is, None, 0) assert_is(None, None) run_tests_if_main() vispy-0.4.0/vispy/testing/tests/__init__.py0000664000175000017500000000000012375431476022467 0ustar larsonerlarsoner00000000000000vispy-0.4.0/vispy/visuals/0000775000175000017500000000000012527674621017237 5ustar larsonerlarsoner00000000000000vispy-0.4.0/vispy/visuals/gridlines.py0000664000175000017500000000715712527672621021601 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from __future__ import division import numpy as np from .. import gloo from .visual import Visual from .shaders import ModularProgram from .transforms import TransformCache VERT = """ attribute vec2 pos; varying vec4 v_pos; void main() { v_pos = vec4(pos, 0, 1); gl_Position = v_pos; } """ FRAG = """ varying vec4 v_pos; uniform vec2 scale; void main() { vec4 px_pos = $map_nd_to_doc(v_pos); // Compute vectors representing width, height of pixel in local coords float s = 1.; vec4 local_pos = $map_doc_to_local(px_pos); vec4 dx = $map_doc_to_local(px_pos + vec4(1.0 / s, 0, 0, 0)) - local_pos; vec4 dy = $map_doc_to_local(px_pos + vec4(0, 1.0 / s, 0, 0)) - local_pos; // Pixel length along each axis, rounded to the nearest power of 10 vec2 px = s * vec2(abs(dx.x) + abs(dy.x), abs(dx.y) + abs(dy.y)); float log10 = log(10.0); float sx = pow(10.0, floor(log(px.x) / log10)+1) * scale.x; float sy = pow(10.0, floor(log(px.y) / log10)+1) * scale.y; float max_alpha = 0.6; float x_alpha = 0.0; if (mod(local_pos.x, 1000 * sx) < px.x) { x_alpha = clamp(1 * sx/px.x, 0, max_alpha); } else if (mod(local_pos.x, 100 * sx) < px.x) { x_alpha = clamp(.1 * sx/px.x, 0, max_alpha); } else if (mod(local_pos.x, 10 * sx) < px.x) { x_alpha = clamp(0.01 * sx/px.x, 0, max_alpha); } float y_alpha = 0.0; if (mod(local_pos.y, 1000 * sy) < px.y) { y_alpha = clamp(1 * sy/px.y, 0, max_alpha); } else if (mod(local_pos.y, 100 * sy) < px.y) { y_alpha = clamp(.1 * sy/px.y, 0, max_alpha); } else if (mod(local_pos.y, 10 * sy) < px.y) { y_alpha = clamp(0.01 * sy/px.y, 0, max_alpha); } float alpha = (((log(max(x_alpha, y_alpha))/log(10.))+2) / 3); if (alpha == 0) { discard; } gl_FragColor = vec4(1, 1, 1, alpha); } """ class GridLinesVisual(Visual): """ Displays regularly spaced grid lines in any coordinate system and at any scale. Parameters ---------- scale : tuple The scale to use. """ def __init__(self, scale=(1, 1), **kwargs): super(GridLinesVisual, self).__init__(**kwargs) self._program = ModularProgram(VERT, FRAG) self._vbo = None self._scale = scale self._tr_cache = TransformCache() self.set_gl_state('additive', cull_face=False) def _buffer(self): if self._vbo is None: # quad covers entire view; frag. shader will deal with image shape quad = np.array([[-1, -1], [1, -1], [1, 1], [-1, -1], [1, 1], [-1, 1]], dtype=np.float32) self._vbo = gloo.VertexBuffer(quad) return self._vbo def draw(self, transforms): """Draw the visual Parameters ---------- transforms : instance of TransformSystem The transforms to use. """ Visual.draw(self, transforms) doc_to_ndc = self._tr_cache.get([transforms.framebuffer_to_render, transforms.document_to_framebuffer]) self._tr_cache.roll() local_to_doc = transforms.visual_to_document self._program.frag['map_nd_to_doc'] = doc_to_ndc.inverse self._program.frag['map_doc_to_local'] = local_to_doc.inverse self._program.prepare() self._program['pos'] = self._buffer() self._program['scale'] = self._scale self._program.draw('triangles') vispy-0.4.0/vispy/visuals/isocurve.py0000664000175000017500000000460512527672621021453 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from __future__ import division import numpy as np from .line import LineVisual from ..geometry.isocurve import isocurve class IsocurveVisual(LineVisual): """Displays an isocurve of a 2D scalar array. Parameters ---------- data : ndarray | None 2D scalar array. level: float | None The level at which the isocurve is constructed from *data*. Notes ----- """ def __init__(self, data=None, level=None, **kwargs): self._data = None self._level = level self._recompute = True kwargs['method'] = 'gl' kwargs['antialias'] = False LineVisual.__init__(self, **kwargs) if data is not None: self.set_data(data) @property def level(self): """ The threshold at which the isocurve is constructed from the 2D data. """ return self._level @level.setter def level(self, level): self._level = level self._recompute = True self.update() def set_data(self, data): """ Set the scalar array data Parameters ---------- data : ndarray A 2D array of scalar values. The isocurve is constructed to show all locations in the scalar field equal to ``self.level``. """ self._data = data self._recompute = True self.update() def draw(self, transforms): """Draw the visual Parameters ---------- transforms : instance of TransformSystem The transforms to use. """ if self._data is None or self._level is None: return if self._recompute: verts = [] paths = isocurve(self._data.astype(float).T, self._level, extend_to_edge=True, connected=True) tot = 0 gaps = [] for path in paths: verts.extend(path) tot += len(path) gaps.append(tot-1) connect = np.ones(tot-1, dtype=bool) connect[gaps[:-1]] = False verts = np.array(verts) LineVisual.set_data(self, pos=verts, connect=connect) self._recompute = False LineVisual.draw(self, transforms) vispy-0.4.0/vispy/visuals/cube.py0000664000175000017500000000404512527672621020530 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2015, Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- from ..geometry import create_cube from ..gloo import set_state from .mesh import MeshVisual class CubeVisual(MeshVisual): """Visual that displays a cube or cuboid Parameters ---------- size : float or tuple The size of the cuboid. A float gives a cube, whereas tuples may specify the size of each axis (x, y, z) independently. vertex_colors : ndarray Same as for `MeshVisual` class. See `create_cube` for vertex ordering. face_colors : ndarray Same as for `MeshVisual` class. See `create_cube` for vertex ordering. color : Color The `Color` to use when drawing the cube faces. edge_color : tuple or Color The `Color` to use when drawing the cube edges. If `None`, then no cube edges are drawn. """ def __init__(self, size=1.0, vertex_colors=None, face_colors=None, color=(0.5, 0.5, 1, 1), edge_color=None): vertices, filled_indices, outline_indices = create_cube() vertices['position'] *= size MeshVisual.__init__(self, vertices['position'], filled_indices, vertex_colors, face_colors, color) if edge_color: self._outline = MeshVisual(vertices['position'], outline_indices, color=edge_color, mode='lines') else: self._outline = None def draw(self, transforms): """Draw the visual Parameters ---------- transforms : instance of TransformSystem The transforms to use. """ MeshVisual.draw(self, transforms) if self._outline: set_state(polygon_offset=(1, 1), polygon_offset_fill=True) self._outline.draw(transforms) vispy-0.4.0/vispy/visuals/volume.py0000664000175000017500000005352712527672621021132 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ About this technique -------------------- In Python, we define the six faces of a cuboid to draw, as well as texture cooridnates corresponding with the vertices of the cuboid. The back faces of the cuboid are drawn (and front faces are culled) because only the back faces are visible when the camera is inside the volume. In the vertex shader, we intersect the view ray with the near and far clipping planes. In the fragment shader, we use these two points to compute the ray direction and then compute the position of the front cuboid surface (or near clipping plane) along the view ray. Next we calculate the number of steps to walk from the front surface to the back surface and iterate over these positions in a for-loop. At each iteration, the fragment color or other voxel information is updated depending on the selected rendering method. It is important for the texture interpolation is 'linear', since with nearest the result look very ugly. The wrapping should be clamp_to_edge to avoid artifacts when the ray takes a small step outside the volume. The ray direction is established by mapping the vertex to the document coordinate frame, adjusting z to +/-1, and mapping the coordinate back. The ray is expressed in coordinates local to the volume (i.e. texture coordinates). """ from ..gloo import Texture3D, TextureEmulated3D, VertexBuffer, IndexBuffer from . import Visual from .shaders import Function, ModularProgram from ..color import get_colormap import numpy as np # todo: implement more render methods (port from visvis) # todo: allow anisotropic data # todo: what to do about lighting? ambi/diffuse/spec/shinynes on each visual? # Vertex shader VERT_SHADER = """ attribute vec3 a_position; attribute vec3 a_texcoord; uniform vec3 u_shape; varying vec3 v_texcoord; varying vec3 v_position; varying vec4 v_nearpos; varying vec4 v_farpos; void main() { v_texcoord = a_texcoord; v_position = a_position; // Project local vertex coordinate to camera position. Then do a step // backward (in cam coords) and project back. Voila, we get our ray vector. vec4 pos_in_cam = $viewtransformf(vec4(v_position, 1)); // intersection of ray and near clipping plane (z = -1 in clip coords) pos_in_cam.z = -pos_in_cam.w; v_nearpos = $viewtransformi(pos_in_cam); // intersection of ray and far clipping plane (z = +1 in clip coords) pos_in_cam.z = pos_in_cam.w; v_farpos = $viewtransformi(pos_in_cam); gl_Position = $transform(vec4(v_position, 1.0)); } """ # noqa # Fragment shader FRAG_SHADER = """ // uniforms uniform $sampler_type u_volumetex; uniform vec3 u_shape; uniform float u_threshold; uniform float u_relative_step_size; //varyings varying vec3 v_texcoord; varying vec3 v_position; varying vec4 v_nearpos; varying vec4 v_farpos; // uniforms for lighting. Hard coded until we figure out how to do lights const vec4 u_ambient = vec4(0.2, 0.4, 0.2, 1.0); const vec4 u_diffuse = vec4(0.8, 0.2, 0.2, 1.0); const vec4 u_specular = vec4(1.0, 1.0, 1.0, 1.0); const float u_shininess = 40.0; //varying vec3 lightDirs[1]; // global holding view direction in local coordinates vec3 view_ray; vec4 calculateColor(vec4, vec3, vec3); float rand(vec2 co); void main() {{ vec3 farpos = v_farpos.xyz / v_farpos.w; vec3 nearpos = v_nearpos.xyz / v_nearpos.w; // Calculate unit vector pointing in the view direction through this // fragment. view_ray = normalize(farpos.xyz - nearpos.xyz); // Compute the distance to the front surface or near clipping plane float distance = dot(nearpos-v_position, view_ray); distance = max(distance, min((-0.5 - v_position.x) / view_ray.x, (u_shape.x - 0.5 - v_position.x) / view_ray.x)); distance = max(distance, min((-0.5 - v_position.y) / view_ray.y, (u_shape.y - 0.5 - v_position.y) / view_ray.y)); distance = max(distance, min((-0.5 - v_position.z) / view_ray.z, (u_shape.z - 0.5 - v_position.z) / view_ray.z)); // Now we have the starting position on the front surface vec3 front = v_position + view_ray * distance; // Decide how many steps to take int nsteps = int(-distance / u_relative_step_size + 0.5); if( nsteps < 1 ) discard; // Get starting location and step vector in texture coordinates vec3 step = ((v_position - front) / u_shape) / nsteps; vec3 start_loc = front / u_shape; // For testing: show the number of steps. This helps to establish // whether the rays are correctly oriented //gl_FragColor = vec4(0.0, nsteps / 3.0 / u_shape.x, 1.0, 1.0); //return; {before_loop} // This outer loop seems necessary on some systems for large // datasets. Ugly, but it works ... vec3 loc = start_loc; int iter = 0; while (iter < nsteps) {{ for (iter=iter; iter 0.0); N = (2.0*Nselect - 1.0) * N; // == Nselect * N - (1.0-Nselect)*N; // Get color of the texture (albeido) color1 = betterColor; color2 = color1; // todo: parametrise color1_to_color2 // Init colors vec4 ambient_color = vec4(0.0, 0.0, 0.0, 0.0); vec4 diffuse_color = vec4(0.0, 0.0, 0.0, 0.0); vec4 specular_color = vec4(0.0, 0.0, 0.0, 0.0); vec4 final_color; // todo: allow multiple light, define lights on viewvox or subscene int nlights = 1; for (int i=0; i 0.0 ); L = normalize(L+(1.0-lightEnabled)); // Calculate lighting properties float lambertTerm = clamp( dot(N,L), 0.0, 1.0 ); vec3 H = normalize(L+V); // Halfway vector float specularTerm = pow( max(dot(H,N),0.0), u_shininess); // Calculate mask float mask1 = lightEnabled; // Calculate colors ambient_color += mask1 * u_ambient; // * gl_LightSource[i].ambient; diffuse_color += mask1 * lambertTerm; specular_color += mask1 * specularTerm * u_specular; }} // Calculate final color by componing different components final_color = color2 * ( ambient_color + diffuse_color) + specular_color; final_color.a = color2.a; // Done return final_color; }} """ # noqa MIP_SNIPPETS = dict( before_loop=""" float maxval = -99999.0; // The maximum encountered value int maxi = 0; // Where the maximum value was encountered """, in_loop=""" if( val > maxval ) { maxval = val; maxi = iter; } """, after_loop=""" // Refine search for max value loc = start_loc + step * (float(maxi) - 0.5); for (int i=0; i<10; i++) { maxval = max(maxval, $sample(u_volumetex, loc).g); loc += step * 0.1; } gl_FragColor = $cmap(maxval); """, ) MIP_FRAG_SHADER = FRAG_SHADER.format(**MIP_SNIPPETS) TRANSLUCENT_SNIPPETS = dict( before_loop=""" vec4 integrated_color = vec4(0., 0., 0., 0.); """, in_loop=""" color = $cmap(val); float a1 = integrated_color.a; float a2 = color.a * (1 - a1); float alpha = max(a1 + a2, 0.001); // Doesn't work.. GLSL optimizer bug? //integrated_color = (integrated_color * a1 / alpha) + // (color * a2 / alpha); // This should be identical but does work correctly: integrated_color *= a1 / alpha; integrated_color += color * a2 / alpha; integrated_color.a = alpha; if( alpha > 0.99 ){ // stop integrating if the fragment becomes opaque iter = nsteps; } """, after_loop=""" gl_FragColor = integrated_color; """, ) TRANSLUCENT_FRAG_SHADER = FRAG_SHADER.format(**TRANSLUCENT_SNIPPETS) ADDITIVE_SNIPPETS = dict( before_loop=""" vec4 integrated_color = vec4(0., 0., 0., 0.); """, in_loop=""" color = $cmap(val); integrated_color = 1.0 - (1.0 - integrated_color) * (1.0 - color); """, after_loop=""" gl_FragColor = integrated_color; """, ) ADDITIVE_FRAG_SHADER = FRAG_SHADER.format(**ADDITIVE_SNIPPETS) ISO_SNIPPETS = dict( before_loop=""" vec4 color3 = vec4(0.0); // final color vec3 dstep = 1.5 / u_shape; // step to sample derivative """, in_loop=""" if (val > u_threshold-0.2) { // Take the last interval in smaller steps vec3 iloc = loc - step; for (int i=0; i<10; i++) { val = $sample(u_volumetex, iloc).g; if (val > u_threshold) { color = $cmap(val); gl_FragColor = calculateColor(color, iloc, dstep); iter = nsteps; break; } iloc += step * 0.1; } } """, after_loop=""" """, ) ISO_FRAG_SHADER = FRAG_SHADER.format(**ISO_SNIPPETS) frag_dict = {'mip': MIP_FRAG_SHADER, 'iso': ISO_FRAG_SHADER, 'translucent': TRANSLUCENT_FRAG_SHADER, 'additive': ADDITIVE_FRAG_SHADER} class VolumeVisual(Visual): """ Displays a 3D Volume Parameters ---------- vol : ndarray The volume to display. Must be ndim==2. clim : tuple of two floats | None The contrast limits. The values in the volume are mapped to black and white corresponding to these values. Default maps between min and max. method : {'mip', 'translucent', 'additive', 'iso'} The render method to use. See corresponding docs for details. Default 'mip'. threshold : float The threshold to use for the isosurafce render method. By default the mean of the given volume is used. relative_step_size : float The relative step size to step through the volume. Default 0.8. Increase to e.g. 1.5 to increase performance, at the cost of quality. cmap : str Colormap to use. emulate_texture : bool Use 2D textures to emulate a 3D texture. OpenGL ES 2.0 compatible, but has lower performance on desktop platforms. """ def __init__(self, vol, clim=None, method='mip', threshold=None, relative_step_size=0.8, cmap='grays', emulate_texture=False): Visual.__init__(self) # Only show back faces of cuboid. This is required because if we are # inside the volume, then the front faces are outside of the clipping # box and will not be drawn. self.set_gl_state('translucent', cull_face=False) tex_cls = TextureEmulated3D if emulate_texture else Texture3D # Storage of information of volume self._vol_shape = () self._vertex_cache_id = () self._clim = None # Set the colormap self._cmap = get_colormap(cmap) # Create gloo objects self._vbo = None self._tex = tex_cls((10, 10, 10), interpolation='linear', wrapping='clamp_to_edge') # Create program self._program = ModularProgram(VERT_SHADER) self._program['u_volumetex'] = self._tex self._index_buffer = None # Set data self.set_data(vol, clim) # Set params self.method = method self.relative_step_size = relative_step_size self.threshold = threshold if (threshold is not None) else vol.mean() def set_data(self, vol, clim=None): """ Set the volume data. Parameters ---------- vol : ndarray The 3D volume. clim : tuple | None Colormap limits to use. None will use the min and max values. """ # Check volume if not isinstance(vol, np.ndarray): raise ValueError('Volume visual needs a numpy array.') if not ((vol.ndim == 3) or (vol.ndim == 4 and vol.shape[-1] <= 4)): raise ValueError('Volume visual needs a 3D image.') # Handle clim if clim is not None: clim = np.array(clim, float) if not (clim.ndim == 1 and clim.size == 2): raise ValueError('clim must be a 2-element array-like') self._clim = tuple(clim) if self._clim is None: self._clim = vol.min(), vol.max() # Apply clim vol = np.array(vol, dtype='float32', copy=False) vol -= self._clim[0] vol *= 1.0 / (self._clim[1] - self._clim[0]) # Apply to texture self._tex.set_data(vol) # will be efficient if vol is same shape self._program['u_shape'] = vol.shape[2], vol.shape[1], vol.shape[0] self._vol_shape = vol.shape[:3] # Create vertices? if self._index_buffer is None: self._create_vertex_data() @property def clim(self): """ The contrast limits that were applied to the volume data. Settable via set_data(). """ return self._clim @property def cmap(self): return self._cmap @cmap.setter def cmap(self, cmap): self._cmap = get_colormap(cmap) self._program.frag['cmap'] = Function(self._cmap.glsl_map) self.update() @property def method(self): """The render method to use Current options are: * translucent: voxel colors are blended along the view ray until the result is opaque. * mip: maxiumum intensity projection. Cast a ray and display the maximum value that was encountered. * additive: voxel colors are added along the view ray until the result is saturated. * iso: isosurface. Cast a ray until a certain threshold is encountered. At that location, lighning calculations are performed to give the visual appearance of a surface. """ return self._method @method.setter def method(self, method): # Check and save known_methods = list(frag_dict.keys()) if method not in known_methods: raise ValueError('Volume render method should be in %r, not %r' % (known_methods, method)) self._method = method # Get rid of specific variables - they may become invalid self._program['u_threshold'] = None self._program.frag = frag_dict[method] #self._program.frag['calculate_steps'] = Function(calc_steps) self._program.frag['sampler_type'] = self._tex.glsl_sampler_type self._program.frag['sample'] = self._tex.glsl_sample self._program.frag['cmap'] = Function(self._cmap.glsl_map) self.update() @property def threshold(self): """ The threshold value to apply for the isosurface render method. """ return self._threshold @threshold.setter def threshold(self, value): self._threshold = float(value) self.update() @property def relative_step_size(self): """ The relative step size used during raycasting. Larger values yield higher performance at reduced quality. If set > 2.0 the ray skips entire voxels. Recommended values are between 0.5 and 1.5. The amount of quality degredation depends on the render method. """ return self._relative_step_size @relative_step_size.setter def relative_step_size(self, value): value = float(value) if value < 0.1: raise ValueError('relative_step_size cannot be smaller than 0.1') self._relative_step_size = value def _create_vertex_data(self): """ Create and set positions and texture coords from the given shape We have six faces with 1 quad (2 triangles) each, resulting in 6*2*3 = 36 vertices in total. """ shape = self._vol_shape # Do we already have this or not? vertex_cache_id = self._vol_shape if vertex_cache_id == self._vertex_cache_id: return self._vertex_cache_id = None # Get corner coordinates. The -0.5 offset is to center # pixels/voxels. This works correctly for anisotropic data. x0, x1 = -0.5, shape[2] - 0.5 y0, y1 = -0.5, shape[1] - 0.5 z0, z1 = -0.5, shape[0] - 0.5 data = np.empty(8, dtype=[ ('a_position', np.float32, 3), ('a_texcoord', np.float32, 3) ]) data['a_position'] = np.array([ [x0, y0, z0], [x1, y0, z0], [x0, y1, z0], [x1, y1, z0], [x0, y0, z1], [x1, y0, z1], [x0, y1, z1], [x1, y1, z1], ], dtype=np.float32) data['a_texcoord'] = np.array([ [0, 0, 0], [1, 0, 0], [0, 1, 0], [1, 1, 0], [0, 0, 1], [1, 0, 1], [0, 1, 1], [1, 1, 1], ], dtype=np.float32) """ 6-------7 /| /| 4-------5 | | | | | | 2-----|-3 |/ |/ 0-------1 """ # Order is chosen such that normals face outward; front faces will be # culled. indices = np.array([2, 6, 0, 4, 5, 6, 7, 2, 3, 0, 1, 5, 3, 7], dtype=np.uint32) # Get some stats self._kb_for_texture = np.prod(self._vol_shape) / 1024 self._kb_for_vertices = (indices.nbytes + data.nbytes) / 1024 # Apply if self._vbo is not None: self._vbo.delete() self._index_buffer.delete() self._vbo = VertexBuffer(data) self._program.bind(self._vbo) self._index_buffer = IndexBuffer(indices) self._vertex_cache_id = vertex_cache_id def bounds(self, mode, axis): """Get the visual bounds Parameters ---------- mode : str The mode. axis : int The axis number. Returns ------- bounds : tuple The lower and upper bounds. """ # Not sure if this is right. Do I need to take the transform if this # node into account? # Also, this method has no docstring, and I don't want to repeat # the docstring here. Maybe Visual implements _bounds that subclasses # can implement? return 0, self._vol_shape[2-axis] def draw(self, transforms): """Draw the visual Parameters ---------- transforms : instance of TransformSystem The transforms to use. """ Visual.draw(self, transforms) full_tr = transforms.get_full_transform() self._program.vert['transform'] = full_tr self._program['u_relative_step_size'] = self._relative_step_size # Get and set transforms view_tr_f = transforms.visual_to_document view_tr_i = view_tr_f.inverse self._program.vert['viewtransformf'] = view_tr_f self._program.vert['viewtransformi'] = view_tr_i # Set attributes that are specific to certain methods self._program.build_if_needed() if self._method == 'iso': self._program['u_threshold'] = self._threshold # Draw! self._program.draw('triangle_strip', self._index_buffer) vispy-0.4.0/vispy/visuals/regular_polygon.py0000664000175000017500000000421012527672621023014 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ RegularPolygonVisual visual based on EllipseVisual """ from __future__ import division from ..color import Color from .ellipse import EllipseVisual class RegularPolygonVisual(EllipseVisual): """ Displays a regular polygon Parameters ---------- pos : array Center of the regular polygon color : str | tuple | list of colors Fill color of the polygon border_color : str | tuple | list of colors Border color of the polygon radius : float Radius of the regular polygon Defaults to 0.1 sides : int Number of sides of the regular polygon """ def __init__(self, pos=None, color='black', border_color=None, radius=0.1, sides=4, **kwargs): super(EllipseVisual, self).__init__() self.mesh.mode = 'triangle_fan' self._pos = pos self._color = Color(color) self._border_color = Color(border_color) self._radius = radius self._sides = sides self._update() @property def sides(self): """ The number of sides in the regular polygon. """ return self._sides @sides.setter def sides(self, sides): if sides < 3: raise ValueError('PolygonVisual must have at least 3 sides, not %s' % sides) self._sides = sides self._update() def _update(self): if self._pos is None: return self._generate_vertices(pos=self._pos, radius=self._radius, start_angle=0., span_angle=360., num_segments=self._sides) if not self._color.is_blank: self.mesh.set_data(vertices=self._vertices, color=self._color.rgba) if not self._border_color.is_blank: self.border.set_data(pos=self._vertices[1:], color=self._border_color.rgba) self.update() vispy-0.4.0/vispy/visuals/transforms/0000775000175000017500000000000012527674621021435 5ustar larsonerlarsoner00000000000000vispy-0.4.0/vispy/visuals/transforms/linear.py0000664000175000017500000003705312527672621023267 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from __future__ import division import numpy as np from ...util import transforms from ...geometry import Rect from ._util import arg_to_vec4, as_vec4 from .base_transform import BaseTransform class NullTransform(BaseTransform): """ Transform having no effect on coordinates (identity transform). """ glsl_map = "vec4 null_transform_map(vec4 pos) {return pos;}" glsl_imap = "vec4 null_transform_imap(vec4 pos) {return pos;}" Linear = True Orthogonal = True NonScaling = True Isometric = True @arg_to_vec4 def map(self, coords): """Map coordinates Parameters ---------- coords : array-like Coordinates to map. """ return coords def imap(self, coords): """Inverse map coordinates Parameters ---------- coords : array-like Coordinates to inverse map. """ return coords def __mul__(self, tr): return tr def __rmul__(self, tr): return tr class STTransform(BaseTransform): """ Transform performing only scale and translate, in that order. Parameters ---------- scale : array-like Scale factors for X, Y, Z axes. translate : array-like Scale factors for X, Y, Z axes. """ glsl_map = """ vec4 st_transform_map(vec4 pos) { return vec4(pos.xyz * $scale.xyz + $translate.xyz * pos.w, pos.w); } """ glsl_imap = """ vec4 st_transform_imap(vec4 pos) { return vec4((pos.xyz - $translate.xyz * pos.w) / $scale.xyz, pos.w); } """ Linear = True Orthogonal = True NonScaling = False Isometric = False def __init__(self, scale=None, translate=None): super(STTransform, self).__init__() self._update_map = True self._update_imap = True self._scale = np.ones(4, dtype=np.float32) self._translate = np.zeros(4, dtype=np.float32) s = ((1.0, 1.0, 1.0, 1.0) if scale is None else as_vec4(scale, default=(1, 1, 1, 1))) t = ((0.0, 0.0, 0.0, 0.0) if translate is None else as_vec4(translate, default=(0, 0, 0, 0))) self._set_st(s, t) @arg_to_vec4 def map(self, coords): """Map coordinates Parameters ---------- coords : array-like Coordinates to map. Returns ------- coords : ndarray Coordinates. """ m = np.empty(coords.shape) m[:, :3] = (coords[:, :3] * self.scale[np.newaxis, :3] + coords[:, 3:] * self.translate[np.newaxis, :3]) m[:, 3] = coords[:, 3] return m @arg_to_vec4 def imap(self, coords): """Invert map coordinates Parameters ---------- coords : array-like Coordinates to inverse map. Returns ------- coords : ndarray Coordinates. """ m = np.empty(coords.shape) m[:, :3] = ((coords[:, :3] - coords[:, 3:] * self.translate[np.newaxis, :3]) / self.scale[np.newaxis, :3]) m[:, 3] = coords[:, 3] return m def shader_map(self): if self._update_map: self._shader_map['scale'] = self.scale self._shader_map['translate'] = self.translate self._update_map = False return self._shader_map def shader_imap(self): if self._update_imap: self._shader_imap['scale'] = self.scale self._shader_imap['translate'] = self.translate self._update_imap = False return self._shader_imap @property def scale(self): return self._scale.copy() @scale.setter def scale(self, s): s = as_vec4(s, default=(1, 1, 1, 1)) self._set_st(scale=s) @property def translate(self): return self._translate.copy() @translate.setter def translate(self, t): t = as_vec4(t, default=(0, 0, 0, 0)) self._set_st(translate=t) def _set_st(self, scale=None, translate=None): update = False if scale is not None and not np.all(scale == self._scale): self._scale[:] = scale update = True if translate is not None and not np.all(translate == self._translate): self._translate[:] = translate update = True if update: self._update_map = True self._update_imap = True self.update() # inform listeners there has been a change def move(self, move): """Change the translation of this transform by the amount given. Parameters ---------- move : array-like The values to be added to the current translation of the transform. """ move = as_vec4(move, default=(0, 0, 0, 0)) self.translate = self.translate + move def zoom(self, zoom, center=(0, 0, 0), mapped=True): """Update the transform such that its scale factor is changed, but the specified center point is left unchanged. Parameters ---------- zoom : array-like Values to multiply the transform's current scale factors. center : array-like The center point around which the scaling will take place. mapped : bool Whether *center* is expressed in mapped coordinates (True) or unmapped coordinates (False). """ zoom = as_vec4(zoom, default=(1, 1, 1, 1)) center = as_vec4(center, default=(0, 0, 0, 0)) scale = self.scale * zoom if mapped: trans = center - (center - self.translate) * zoom else: trans = self.scale * (1 - zoom) * center + self.translate self._set_st(scale=scale, translate=trans) def as_affine(self): m = AffineTransform() m.scale(self.scale) m.translate(self.translate) return m @classmethod def from_mapping(cls, x0, x1): """ Create an STTransform from the given mapping See `set_mapping` for details. Parameters ---------- x0 : array-like Start. x1 : array-like End. Returns ------- t : instance of STTransform The transform. """ t = cls() t.set_mapping(x0, x1) return t def set_mapping(self, x0, x1): """Configure this transform such that it maps points x0 => x1 Parameters ---------- x0 : array-like, shape (2, 2) or (2, 3) Start location. x1 : array-like, shape (2, 2) or (2, 3) End location. Examples -------- For example, if we wish to map the corners of a rectangle:: >>> p1 = [[0, 0], [200, 300]] onto a unit cube:: >>> p2 = [[-1, -1], [1, 1]] then we can generate the transform as follows:: >>> tr = STTransform() >>> tr.set_mapping(p1, p2) >>> assert tr.map(p1)[:,:2] == p2 # test """ # if args are Rect, convert to array first if isinstance(x0, Rect): x0 = x0._transform_in()[:3] if isinstance(x1, Rect): x1 = x1._transform_in()[:3] x0 = np.asarray(x0) x1 = np.asarray(x1) if (x0.ndim != 2 or x0.shape[0] != 2 or x1.ndim != 2 or x1.shape[0] != 2): raise TypeError("set_mapping requires array inputs of shape " "(2, N).") denom = x0[1] - x0[0] mask = denom == 0 denom[mask] = 1.0 s = (x1[1] - x1[0]) / denom s[mask] = 1.0 s[x0[1] == x0[0]] = 1.0 t = x1[0] - s * x0[0] s = as_vec4(s, default=(1, 1, 1, 1)) t = as_vec4(t, default=(0, 0, 0, 0)) self._set_st(scale=s, translate=t) def __mul__(self, tr): if isinstance(tr, STTransform): s = self.scale * tr.scale t = self.translate + (tr.translate * self.scale) return STTransform(scale=s, translate=t) elif isinstance(tr, AffineTransform): return self.as_affine() * tr else: return super(STTransform, self).__mul__(tr) def __rmul__(self, tr): if isinstance(tr, AffineTransform): return tr * self.as_affine() return super(STTransform, self).__rmul__(tr) def __repr__(self): return ("" % (self.scale, self.translate)) class AffineTransform(BaseTransform): """Affine transformation class Parameters ---------- matrix : array-like | None 4x4 array to use for the transform. """ glsl_map = """ vec4 affine_transform_map(vec4 pos) { return $matrix * pos; } """ glsl_imap = """ vec4 affine_transform_imap(vec4 pos) { return $inv_matrix * pos; } """ Linear = True Orthogonal = False NonScaling = False Isometric = False def __init__(self, matrix=None): super(AffineTransform, self).__init__() if matrix is not None: self.matrix = matrix else: self.reset() @arg_to_vec4 def map(self, coords): """Map coordinates Parameters ---------- coords : array-like Coordinates to map. Returns ------- coords : ndarray Coordinates. """ # looks backwards, but both matrices are transposed. return np.dot(coords, self.matrix) @arg_to_vec4 def imap(self, coords): """Inverse map coordinates Parameters ---------- coords : array-like Coordinates to inverse map. Returns ------- coords : ndarray Coordinates. """ return np.dot(coords, self.inv_matrix) def shader_map(self): fn = super(AffineTransform, self).shader_map() fn['matrix'] = self.matrix # uniform mat4 return fn def shader_imap(self): fn = super(AffineTransform, self).shader_imap() fn['inv_matrix'] = self.inv_matrix # uniform mat4 return fn @property def matrix(self): return self._matrix @matrix.setter def matrix(self, m): self._matrix = m self._inv_matrix = None self.shader_map() self.shader_imap() self.update() @property def inv_matrix(self): if self._inv_matrix is None: self._inv_matrix = np.linalg.inv(self.matrix) return self._inv_matrix @arg_to_vec4 def translate(self, pos): """ Translate the matrix The translation is applied *after* the transformations already present in the matrix. Parameters ---------- pos : arrayndarray Position to translate by. """ self.matrix = np.dot(self.matrix, transforms.translate(pos[0, :3])) def scale(self, scale, center=None): """ Scale the matrix about a given origin. The scaling is applied *after* the transformations already present in the matrix. Parameters ---------- scale : array-like Scale factors along x, y and z axes. center : array-like or None The x, y and z coordinates to scale around. If None, (0, 0, 0) will be used. """ scale = transforms.scale(as_vec4(scale, default=(1, 1, 1, 1))[0, :3]) if center is not None: center = as_vec4(center)[0, :3] scale = np.dot(np.dot(transforms.translate(-center), scale), transforms.translate(center)) self.matrix = np.dot(self.matrix, scale) def rotate(self, angle, axis): """ Rotate the matrix by some angle about a given axis. The rotation is applied *after* the transformations already present in the matrix. Parameters ---------- angle : float The angle of rotation, in degrees. axis : array-like The x, y and z coordinates of the axis vector to rotate around. """ self.matrix = np.dot(self.matrix, transforms.rotate(angle, axis)) def set_mapping(self, points1, points2): """ Set to a 3D transformation matrix that maps points1 onto points2. Parameters ---------- points1 : array-like, shape (4, 3) Four starting 3D coordinates. points2 : array-like, shape (4, 3) Four ending 3D coordinates. """ # note: need to transpose because util.functions uses opposite # of standard linear algebra order. self.matrix = transforms.affine_map(points1, points2).T def set_ortho(self, l, r, b, t, n, f): """Set ortho transform Parameters ---------- l : float Left. r : float Right. b : float Bottom. t : float Top. n : float Near. f : float Far. """ self.matrix = transforms.ortho(l, r, b, t, n, f) def reset(self): self.matrix = np.eye(4) def __mul__(self, tr): if (isinstance(tr, AffineTransform) and not any(tr.matrix[:3, 3] != 0)): # don't multiply if the perspective column is used return AffineTransform(matrix=np.dot(tr.matrix, self.matrix)) else: return tr.__rmul__(self) def __repr__(self): s = "%s(matrix=[" % self.__class__.__name__ indent = " "*len(s) s += str(list(self.matrix[0])) + ",\n" s += indent + str(list(self.matrix[1])) + ",\n" s += indent + str(list(self.matrix[2])) + ",\n" s += indent + str(list(self.matrix[3])) + "] at 0x%x)" % id(self) return s #class SRTTransform(BaseTransform): # """ Transform performing scale, rotate, and translate, in that order. # # This transformation allows objects to be placed arbitrarily in a scene # much the same way AffineTransform does. However, an incorrect order of # operations in AffineTransform may result in shearing the object (if scale # is applied after rotate) or in unpredictable translation (if scale/rotate # is applied after translation). SRTTransform avoids these problems by # enforcing the correct order of operations. # """ # # TODO class PerspectiveTransform(AffineTransform): """ Matrix transform that also implements perspective division. Parameters ---------- matrix : array-like | None 4x4 array to use for the transform. """ def set_perspective(self, fov, aspect, near, far): """Set the perspective Parameters ---------- fov : float Field of view. aspect : float Aspect ratio. near : float Near location. far : float Far location. """ self.matrix = transforms.perspective(fov, aspect, near, far) def set_frustum(self, l, r, b, t, n, f): """Set the frustum Parameters ---------- l : float Left. r : float Right. b : float Bottom. t : float Top. n : float Near. f : float Far. """ self.matrix = transforms.frustum(l, r, b, t, n, f) vispy-0.4.0/vispy/visuals/transforms/transform_system.py0000664000175000017500000002065512527672621025434 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from __future__ import division from .linear import NullTransform, STTransform from ._util import TransformCache class TransformSystem(object): """ TransformSystem encapsulates information about the coordinate systems needed to draw a Visual. Visual rendering operates in four coordinate systems: * **Visual** - arbitrary local coordinate frame of the visual. Vertex buffers used by the visual are usually specified in this coordinate system. * **Document** - This coordinate system has units of _logical_ pixels, and should usually represent the pixel coordinates of the canvas being drawn to. Visuals use this coordinate system to make measurements for font size, line width, and in general anything that is specified in physical units (px, pt, mm, in, etc.). Note that, by convention, _logical_ pixels are not necessarily the same size as the _physical_ pixels in the framebuffer that is being rendered to. * **Buffer** - The buffer coordinate system has units of _physical_ pixels, and should usually represent the coordinates of the current framebuffer being rendered to. Visuals use this coordinate system primarily for antialiasing calculations. In most cases, this will have the same scale as the document coordinate system because the active framebuffer is the back buffer of the canvas, and the canvas will have _logical_ and _physical_ pixels of the same size. The scale may be different in the case of high-resolution displays, or when rendering to an off-screen framebuffer with different scaling or boundaries than the canvas. * **Render** - This coordinate system is the obligatory system for vertexes returned by a vertex shader. It has coordinates (-1, -1) to (1, 1) across the current glViewport. In OpenGL terminology, this is called normalized device coordinates. Parameters ---------- canvas : Canvas The canvas being drawn to. dpi : float The dot-per-inch resolution of the document coordinate system. By default this is set to the resolution of the canvas. Notes ----- By default, TransformSystems are configured such that the document coordinate system matches the logical pixels of the canvas, Examples -------- Use by Visuals ~~~~~~~~~~~~~~ 1. To convert local vertex coordinates to normalized device coordinates in the vertex shader, we first need a vertex shader that supports configurable transformations:: vec4 a_position; void main() { gl_Position = $transform(a_position); } Next, we supply the complete chain of transforms when drawing the visual: def draw(tr_sys): tr = tr_sys.get_full_transform() self.program['transform'] = tr.shader_map() self.program['a_position'] = self.vertex_buffer self.program.draw('triangles') 2. Draw a line whose width is given in mm. To start, we need normal vectors for each vertex, which tell us the direction the vertex should move in order to set the line width:: vec4 a_position; vec4 a_normal; float u_line_width; float u_dpi; void main() { // map vertex position and normal vector to the document cs vec4 doc_pos = $visual_to_doc(a_position); vec4 doc_normal = $visual_to_doc(a_position + a_normal) - doc_pos; // Use DPI to convert mm line width to logical pixels float px_width = (u_line_width / 25.4) * dpi; // expand by line width doc_pos += normalize(doc_normal) * px_width; // finally, map the remainder of the way to normalized device // coordinates. gl_Position = $doc_to_render(a_position); } In this case, we need to access the transforms independently, so ``get_full_transform()`` is not useful here:: def draw(tr_sys): # Send two parts of the full transform separately self.program['visual_to_doc'] = tr_sys.visual_to_doc.shader_map() doc_to_render = (tr_sys.framebuffer_to_render * tr_sys.document_to_framebuffer) self.program['visual_to_doc'] = doc_to_render.shader_map() self.program['u_line_width'] = self.line_width self.program['u_dpi'] = tr_sys.dpi self.program['a_position'] = self.vertex_buffer self.program['a_normal'] = self.normal_buffer self.program.draw('triangles') 3. Draw a triangle with antialiasing at the edge. 4. Using inverse transforms in the fragment shader Creating TransformSystem instances ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1. Basic example, including checks for canvas resolution 2. How to handle off-screen framebuffers """ def __init__(self, canvas, dpi=None): self._canvas = canvas self._cache = TransformCache() if dpi is None: dpi = canvas.dpi self._dpi = dpi # Null by default; visuals draw directly to the document coordinate # system. self._visual_to_document = NullTransform() self._document_to_framebuffer = STTransform() self._framebuffer_to_render = STTransform() self.auto_configure() def auto_configure(self): """Automatically configure the TransformSystem: * document_to_framebuffer maps from the Canvas logical pixel coordinate system to the framebuffer coordinate system, assuming physical pixels of the same size. The y-axis is inverted in this transform. * framebuffer_to_render maps from the framebuffer coordinate system to normalized device coordinates (-1 to 1). """ # By default, this should invert the y axis -- no difference between # the scale of logical and physical pixels. canvas = self._canvas map_from = [(0, 0), canvas.size] map_to = [(0, canvas.size[1]), (canvas.size[0], 0)] self._document_to_framebuffer.set_mapping(map_from, map_to) # Automatically configure buffer coordinate system to match the canvas map_from = [(0, 0), canvas.size] map_to = [(-1, -1), (1, 1)] self._framebuffer_to_render.set_mapping(map_from, map_to) @property def canvas(self): """ The Canvas being drawn to. """ return self._canvas @property def dpi(self): """ Physical resolution of the document coordinate system (dots per inch). """ return self._dpi @dpi.setter def dpi(self, dpi): assert dpi > 0 self._dpi = dpi @property def visual_to_document(self): """ Transform mapping from visual local coordinate frame to document coordinate frame. """ return self._visual_to_document @visual_to_document.setter def visual_to_document(self, tr): if self._visual_to_document is not tr: self._visual_to_document = tr @property def document_to_framebuffer(self): """ Transform mapping from document coordinate frame to the framebuffer (physical pixel) coordinate frame. """ return self._document_to_framebuffer @document_to_framebuffer.setter def document_to_framebuffer(self, tr): if self._document_to_framebuffer is not tr: self._document_to_framebuffer = tr @property def framebuffer_to_render(self): """ Transform mapping from pixel coordinate frame to rendering coordinate frame. """ return self._framebuffer_to_render @framebuffer_to_render.setter def framebuffer_to_render(self, tr): if self._framebuffer_to_render is not tr: self._framebuffer_to_render = tr def get_full_transform(self): """ Convenience method that returns the composition of all three transforms:: framebuffer_to_render * document_to_framebuffer * visual_to_document This is used for visuals that do not require physical measurements or antialiasing. """ # noqa return self._cache.get([self.framebuffer_to_render, self.document_to_framebuffer, self.visual_to_document]) vispy-0.4.0/vispy/visuals/transforms/__init__.py0000664000175000017500000000235612527672621023552 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Provides classes representing different transform types suitable for use with visuals and scenes. """ from .base_transform import BaseTransform # noqa from .linear import (NullTransform, STTransform, # noqa AffineTransform, PerspectiveTransform) # noqa from .nonlinear import LogTransform, PolarTransform # noqa from .interactive import PanZoomTransform from .chain import ChainTransform # noqa from ._util import arg_to_array, arg_to_vec4, as_vec4, TransformCache # noqa from .transform_system import TransformSystem __all__ = ['NullTransform', 'STTransform', 'AffineTransform', 'PerspectiveTransform', 'LogTransform', 'PolarTransform', 'ChainTransform', 'TransformSystem', 'PanZoomTransform'] transform_types = {} for o in list(globals().values()): try: if issubclass(o, BaseTransform) and o is not BaseTransform: name = o.__name__[:-len('Transform')].lower() transform_types[name] = o except TypeError: continue def create_transform(type, *args, **kwargs): return transform_types[type](*args, **kwargs) vispy-0.4.0/vispy/visuals/transforms/chain.py0000664000175000017500000002025512527672621023073 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from __future__ import division from ..shaders import FunctionChain from .base_transform import BaseTransform from .linear import NullTransform class ChainTransform(BaseTransform): """ BaseTransform subclass that performs a sequence of transformations in order. Internally, this class uses shaders.FunctionChain to generate its glsl_map and glsl_imap functions. Arguments: transforms : list of BaseTransform instances See ``transforms`` property. """ glsl_map = None glsl_imap = None Linear = False Orthogonal = False NonScaling = False Isometric = False def __init__(self, *transforms): super(ChainTransform, self).__init__() # Set input transforms trs = [] for tr in transforms: if isinstance(tr, (tuple, list)): trs.extend(tr) else: trs.append(tr) self._transforms = trs # ChainTransform does not have shader maps self._shader_map = None self._shader_imap = None @property def transforms(self): """ The list of transform that make up the transform chain. The order of transforms is given such that the last transform in the list is the first to be invoked when mapping coordinates through the chain. For example, the following two mappings are equivalent:: # Map coordinates through individual transforms: trans1 = STTransform(scale=(2, 3), translate=(0, 1)) trans2 = PolarTransform() mapped = trans1.map(trans2.map(coords)) # Equivalent mapping through chain: chain = ChainTransform([trans1, trans2]) mapped = chain.map(coords) """ return self._transforms # @transforms.setter # def transforms(self, tr): # #if self._enabled: # #raise RuntimeError("Shader is already enabled; cannot modify.") # if not isinstance(tr, list): # raise TypeError("Transform chain must be a list") # self._transforms = tr @property def Linear(self): b = True for tr in self._transforms: b &= tr.Linear return b @property def Orthogonal(self): b = True for tr in self._transforms: b &= tr.Orthogonal return b @property def NonScaling(self): b = True for tr in self._transforms: b &= tr.NonScaling return b @property def Isometric(self): b = True for tr in self._transforms: b &= tr.Isometric return b def map(self, coords): """Map coordinates Parameters ---------- coords : array-like Coordinates to map. Returns ------- coords : ndarray Coordinates. """ for tr in reversed(self.transforms): coords = tr.map(coords) return coords def imap(self, coords): """Inverse map coordinates Parameters ---------- coords : array-like Coordinates to inverse map. Returns ------- coords : ndarray Coordinates. """ for tr in self.transforms: coords = tr.imap(coords) return coords def shader_map(self): if self._shader_map is None: self._shader_map = self._make_shader_map(imap=False) else: for tr in self._transforms: tr.shader_map() # force transform to update its shader return self._shader_map def shader_imap(self): if self._shader_imap is None: self._shader_imap = self._make_shader_map(imap=True) else: for tr in self._transforms: tr.shader_imap() # force transform to update its shader return self._shader_imap def _make_shader_map(self, imap): if bool(imap): funcs = [tr.shader_imap() for tr in self.transforms] else: funcs = [tr.shader_map() for tr in reversed(self.transforms)] name = "transform_%s_chain" % ('imap' if bool(imap) else 'map') return FunctionChain(name, funcs) def flat(self): """ Return a simplified chain by expanding any nested chains. """ transforms = self.transforms[:] new_chain = [] while len(transforms) > 0: tr = transforms.pop(0) if isinstance(tr, ChainTransform): transforms = tr.transforms[:] + transforms else: new_chain.append(tr) return ChainTransform(new_chain) def simplified(self): """ Return a simplified chain by joining adjacent transforms. If the result is a single transform, return that transform. """ tr = self.flat() if len(tr.transforms) == 0: return NullTransform() cont = True tr = tr.transforms while cont: new_tr = [tr[0]] cont = False for t2 in tr[1:]: t1 = new_tr[-1] pr = t1 * t2 if not isinstance(pr, ChainTransform): cont = True new_tr.pop() new_tr.append(pr) else: new_tr.append(t2) tr = new_tr if len(tr) == 1: return tr[0] else: return ChainTransform(tr) def append(self, tr): """ Add a new transform to the end of this chain. Parameters ---------- tr : instance of Transform The transform to use. """ self.transforms.append(tr) self.update() # Keep simple for now. Let's look at efficienty later # I feel that this class should not decide when to compose transforms # while len(self.transforms) > 0: # pr = tr * self.transforms[-1] # if isinstance(pr, ChainTransform): # self.transforms.append(tr) # break # else: # self.transforms.pop() # tr = pr # if len(self.transforms) == 0: # self._transforms = [pr] # break def prepend(self, tr): """ Add a new transform to the beginning of this chain. Parameters ---------- tr : instance of Transform The transform to use. """ self.transforms.insert(0, tr) self.update() # Keep simple for now. Let's look at efficienty later # while len(self.transforms) > 0: # pr = self.transforms[0] * tr # if isinstance(pr, ChainTransform): # self.transforms.insert(0, tr) # break # else: # self.transforms.pop(0) # tr = pr # if len(self.transforms) == 0: # self._transforms = [pr] # break def __setitem__(self, index, tr): self._transforms[index] = tr if self._shader_map is not None: self._shader_map[-(index+1)] = tr.shader_map() if self._shader_imap is not None: self._shader_imap[index] = tr.shader_imap() self.update() def __mul__(self, tr): if isinstance(tr, ChainTransform): trs = tr.transforms else: trs = [tr] return ChainTransform(self.transforms+trs) def __rmul__(self, tr): if isinstance(tr, ChainTransform): trs = tr.transforms else: trs = [tr] return ChainTransform(trs+self.transforms) def __str__(self): names = [tr.__class__.__name__ for tr in self.transforms] return "" % (", ".join(names), id(self)) def __repr__(self): tr = ",\n ".join(map(repr, self.transforms)) return "" % (tr, id(self)) vispy-0.4.0/vispy/visuals/transforms/base_transform.py0000664000175000017500000001534112527672621025016 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ API Issues to work out: - AffineTransform and STTransform both have 'scale' and 'translate' attributes, but they are used in very different ways. It would be nice to keep this consistent, but how? - Need a transform.map_rect function that returns the bounding rectangle of a rect after transformation. Non-linear transforms might need to work harder at this, but we can provide a default implementation that works by mapping a selection of points across a grid within the original rect. """ from __future__ import division from ..shaders import Function from ...util.event import EventEmitter class BaseTransform(object): """ BaseTransform is a base class that defines a pair of complementary coordinate mapping functions in both python and GLSL. All BaseTransform subclasses define map() and imap() methods that map an object through the forward or inverse transformation, respectively. The two class variables glsl_map and glsl_imap are instances of shaders.Function that define the forward- and inverse-mapping GLSL function code. Optionally, an inverse() method returns a new transform performing the inverse mapping. Note that although all classes should define both map() and imap(), it is not necessarily the case that imap(map(x)) == x; there may be instances where the inverse mapping is ambiguous or otherwise meaningless. """ glsl_map = None # Must be GLSL code glsl_imap = None # Flags used to describe the transformation. Subclasses should define each # as True or False. # (usually used for making optimization decisions) # If True, then for any 3 colinear points, the # transformed points will also be colinear. Linear = None # The transformation's effect on one axis is independent # of the input position along any other axis. Orthogonal = None # If True, then the distance between two points is the # same as the distance between the transformed points. NonScaling = None # Scale factors are applied equally to all axes. Isometric = None def __init__(self): self._inverse = None self.changed = EventEmitter(source=self, type='transform_changed') if self.glsl_map is not None: self._shader_map = Function(self.glsl_map) if self.glsl_imap is not None: self._shader_imap = Function(self.glsl_imap) def map(self, obj): """ Return *obj* mapped through the forward transformation. Parameters ---------- obj : tuple (x,y) or (x,y,z) array with shape (..., 2) or (..., 3) """ raise NotImplementedError() def imap(self, obj): """ Return *obj* mapped through the inverse transformation. Parameters ---------- obj : tuple (x,y) or (x,y,z) array with shape (..., 2) or (..., 3) """ raise NotImplementedError() @property def inverse(self): """ The inverse of this transform. """ if self._inverse is None: self._inverse = InverseTransform(self) return self._inverse def shader_map(self): """ Return a shader Function that accepts only a single vec4 argument and defines new attributes / uniforms supplying the Function with any static input. """ return self._shader_map def shader_imap(self): """ see shader_map. """ return self._shader_imap def _shader_object(self): """ This method allows transforms to be assigned directly to shader template variables. Example:: code = 'void main() { gl_Position = $transform($position); }' func = shaders.Function(code) tr = STTransform() func['transform'] = tr # use tr's forward mapping for $function """ return self.shader_map() def update(self): """ Called to inform any listeners that this transform has changed. """ self.changed() def __mul__(self, tr): """ Transform multiplication returns a new transform that is equivalent to the two operands performed in series. By default, multiplying two Transforms `A * B` will return ChainTransform([A, B]). Subclasses may redefine this operation to return more optimized results. To ensure that both operands have a chance to simplify the operation, all subclasses should follow the same procedure. For `A * B`: 1. A.__mul__(B) attempts to generate an optimized transform product. 2. If that fails, it must: * return super(A).__mul__(B) OR * return NotImplemented if the superclass would return an invalid result. 3. When BaseTransform.__mul__(A, B) is called, it returns NotImplemented, which causes B.__rmul__(A) to be invoked. 4. B.__rmul__(A) attempts to generate an optimized transform product. 5. If that fails, it must: * return super(B).__rmul__(A) OR * return ChainTransform([B, A]) if the superclass would return an invalid result. 6. When BaseTransform.__rmul__(B, A) is called, ChainTransform([A, B]) is returned. """ # switch to __rmul__ attempts. # Don't use the "return NotImplemted" trick, because that won't work if # self and tr are of the same type. return tr.__rmul__(self) def __rmul__(self, tr): return ChainTransform([tr, self]) def __repr__(self): return "<%s at 0x%x>" % (self.__class__.__name__, id(self)) class InverseTransform(BaseTransform): def __init__(self, transform): self._transform = transform self.shader_map = self._transform.shader_imap self.shader_imap = self._transform.shader_map self.map = self._transform.imap self.imap = self._transform.map @property def inverse(self): return self._transform @property def Linear(self): return self._transform.Linear @property def Orthogonal(self): return self._transform.Orthogonal @property def NonScaling(self): return self._transform.NonScaling @property def Isometric(self): return self._transform.Isometric def __repr__(self): return ("" % repr(self._transform)) # import here to avoid import cycle; needed for BaseTransform.__mul__. from .chain import ChainTransform # noqa vispy-0.4.0/vispy/visuals/transforms/interactive.py0000664000175000017500000000535612527672621024333 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from __future__ import division import numpy as np from .linear import STTransform class PanZoomTransform(STTransform): """Pan-zoom transform Parameters ---------- canvas : instance of Canvas | None The canvas to attch to. aspect : float | None The aspect ratio to apply. **kwargs : dict Keyword arguments to pass to the underlying `STTransform`. """ def __init__(self, canvas=None, aspect=None, **kwargs): self._aspect = aspect self.attach(canvas) STTransform.__init__(self, **kwargs) self.on_resize(None) def attach(self, canvas): """Attach this tranform to a canvas Parameters ---------- canvas : instance of Canvas The canvas. """ self._canvas = canvas canvas.events.resize.connect(self.on_resize) canvas.events.mouse_wheel.connect(self.on_mouse_wheel) canvas.events.mouse_move.connect(self.on_mouse_move) @property def canvas_tr(self): return STTransform.from_mapping( [(0, 0), self._canvas.size], [(-1, 1), (1, -1)]) def on_resize(self, event): """Resize handler Parameters ---------- event : instance of Event The event. """ if self._aspect is None: return w, h = self._canvas.size aspect = self._aspect / (w / h) self.scale = (self.scale[0], self.scale[0] / aspect) self.shader_map() def on_mouse_move(self, event): """Mouse move handler Parameters ---------- event : instance of Event The event. """ if event.is_dragging: dxy = event.pos - event.last_event.pos button = event.press_event.button if button == 1: dxy = self.canvas_tr.map(dxy) o = self.canvas_tr.map([0, 0]) t = dxy - o self.move(t) elif button == 2: center = self.canvas_tr.map(event.press_event.pos) if self._aspect is None: self.zoom(np.exp(dxy * (0.01, -0.01)), center) else: s = dxy[1] * -0.01 self.zoom(np.exp(np.array([s, s])), center) self.shader_map() def on_mouse_wheel(self, event): """Mouse wheel handler Parameters ---------- event : instance of Event The event. """ self.zoom(np.exp(event.delta * (0.01, -0.01)), event.pos) vispy-0.4.0/vispy/visuals/transforms/_util.py0000664000175000017500000001246212527672621023126 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from __future__ import division import numpy as np from ...ext.decorator import decorator from ...util import logger def arg_to_array(func): """ Decorator to convert argument to array. Parameters ---------- func : function The function to decorate. Returns ------- func : function The decorated function. """ def fn(self, arg, *args, **kwargs): """Function Parameters ---------- arg : array-like Argument to convert. *args : tuple Arguments. **kwargs : dict Keyword arguments. Returns ------- value : object The return value of the function. """ return func(self, np.array(arg), *args, **kwargs) return fn def as_vec4(obj, default=(0, 0, 0, 1)): """ Convert `obj` to 4-element vector (numpy array with shape[-1] == 4) Parameters ---------- obj : array-like Original object. default : array-like The defaults to use if the object does not have 4 entries. Returns ------- obj : array-like The object promoted to have 4 elements. Notes ----- `obj` will have at least two dimensions. If `obj` has < 4 elements, then new elements are added from `default`. For inputs intended as a position or translation, use default=(0,0,0,1). For inputs intended as scale factors, use default=(1,1,1,1). """ obj = np.atleast_2d(obj) # For multiple vectors, reshape to (..., 4) if obj.shape[-1] < 4: new = np.empty(obj.shape[:-1] + (4,), dtype=obj.dtype) new[:] = default new[..., :obj.shape[-1]] = obj obj = new elif obj.shape[-1] > 4: raise TypeError("Array shape %s cannot be converted to vec4" % obj.shape) return obj @decorator def arg_to_vec4(func, self_, arg, *args, **kwargs): """ Decorator for converting argument to vec4 format suitable for 4x4 matrix multiplication. [x, y] => [[x, y, 0, 1]] [x, y, z] => [[x, y, z, 1]] [[x1, y1], [[x1, y1, 0, 1], [x2, y2], => [x2, y2, 0, 1], [x3, y3]] [x3, y3, 0, 1]] If 1D input is provided, then the return value will be flattened. Accepts input of any dimension, as long as shape[-1] <= 4 Alternatively, any class may define its own transform conversion interface by defining a _transform_in() method that returns an array with shape (.., 4), and a _transform_out() method that accepts the same array shape and returns a new (mapped) object. """ if isinstance(arg, (tuple, list, np.ndarray)): arg = np.array(arg) flatten = arg.ndim == 1 arg = as_vec4(arg) ret = func(self_, arg, *args, **kwargs) if flatten and ret is not None: return ret.flatten() return ret elif hasattr(arg, '_transform_in'): arr = arg._transform_in() ret = func(self_, arr, *args, **kwargs) return arg._transform_out(ret) else: raise TypeError("Cannot convert argument to 4D vector: %s" % arg) class TransformCache(object): """ Utility class for managing a cache of ChainTransforms. This is an LRU cache; items are removed if they are not accessed after *max_age* calls to roll(). Notes ----- This class is used by SceneCanvas to ensure that ChainTransform instances are re-used across calls to draw_visual(). SceneCanvas creates one TransformCache instance for each top-level visual drawn, and calls roll() on each cache before drawing, which removes from the cache any transforms that were not accessed during the last draw cycle. """ def __init__(self, max_age=1): self._cache = {} # maps {key: [age, transform]} self.max_age = max_age def get(self, path): """ Get a transform from the cache that maps along *path*, which must be a list of Transforms to apply in reverse order (last transform is applied first). Accessed items have their age reset to 0. """ key = tuple(map(id, path)) item = self._cache.get(key, None) if item is None: logger.debug("Transform cache miss: %s", key) item = [0, self._create(path)] self._cache[key] = item item[0] = 0 # reset age for this item # make sure the chain is up to date #tr = item[1] #for i, node in enumerate(path[1:]): # if tr.transforms[i] is not node.transform: # tr[i] = node.transform return item[1] def _create(self, path): # import here to avoid import cycle from .chain import ChainTransform return ChainTransform(path) def roll(self): """ Increase the age of all items in the cache by 1. Items whose age is greater than self.max_age will be removed from the cache. """ rem = [] for key, item in self._cache.items(): if item[0] > self.max_age: rem.append(key) item[0] += 1 for key in rem: logger.debug("TransformCache remove: %s", key) del self._cache[key] vispy-0.4.0/vispy/visuals/transforms/nonlinear.py0000664000175000017500000003065012527672621023776 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from __future__ import division import numpy as np from ._util import arg_to_array, arg_to_vec4, as_vec4 from .base_transform import BaseTransform from ... import gloo class LogTransform(BaseTransform): """ Transform perfoming logarithmic transformation on three axes. Maps (x, y, z) => (log(base.x, x), log(base.y, y), log(base.z, z)) No transformation is applied for axes with base == 0. If base < 0, then the inverse function is applied: x => base.x ** x Parameters ---------- base : array-like Base for the X, Y, Z axes. """ # TODO: Evaluate the performance costs of using conditionals. # An alternative approach is to transpose the vector before # log-transforming, and then transpose back afterward. glsl_map = """ vec4 LogTransform_map(vec4 pos) { if($base.x > 1.0) pos.x = log(pos.x) / log($base.x); else if($base.x < -1.0) pos.x = pow(-$base.x, pos.x); if($base.y > 1.0) pos.y = log(pos.y) / log($base.y); else if($base.y < -1.0) pos.y = pow(-$base.y, pos.y); if($base.z > 1.0) pos.z = log(pos.z) / log($base.z); else if($base.z < -1.0) pos.z = pow(-$base.z, pos.z); return pos; } """ glsl_imap = glsl_map Linear = False Orthogonal = True NonScaling = False Isometric = False def __init__(self, base=None): super(LogTransform, self).__init__() self._base = np.zeros(3, dtype=np.float32) self.base = (0.0, 0.0, 0.0) if base is None else base @property def base(self): """ *base* is a tuple (x, y, z) containing the log base that should be applied to each axis of the input vector. If any axis has a base <= 0, then that axis is not affected. """ return self._base.copy() @base.setter def base(self, s): self._base[:len(s)] = s self._base[len(s):] = 0.0 @arg_to_array def map(self, coords, base=None): ret = np.empty(coords.shape, coords.dtype) if base is None: base = self.base for i in range(min(ret.shape[-1], 3)): if base[i] > 1.0: ret[..., i] = np.log(coords[..., i]) / np.log(base[i]) elif base[i] < -1.0: ret[..., i] = -base[i] ** coords[..., i] else: ret[..., i] = coords[..., i] return ret @arg_to_array def imap(self, coords): return self.map(coords, -self.base) def shader_map(self): fn = super(LogTransform, self).shader_map() fn['base'] = self.base # uniform vec3 return fn def shader_imap(self): fn = super(LogTransform, self).shader_imap() fn['base'] = -self.base # uniform vec3 return fn def __repr__(self): return "" % (self.base) class PolarTransform(BaseTransform): """Polar transform Maps (theta, r, z) to (x, y, z), where `x = r*cos(theta)` and `y = r*sin(theta)`. """ glsl_map = """ vec4 polar_transform_map(vec4 pos) { return vec4(pos.y * cos(pos.x), pos.y * sin(pos.x), pos.z, 1); } """ glsl_imap = """ vec4 polar_transform_map(vec4 pos) { // TODO: need some modulo math to handle larger theta values..? float theta = atan(pos.y, pos.x); float r = length(pos.xy); return vec4(theta, r, pos.z, 1); } """ Linear = False Orthogonal = False NonScaling = False Isometric = False @arg_to_array def map(self, coords): ret = np.empty(coords.shape, coords.dtype) ret[..., 0] = coords[..., 1] * np.cos(coords[..., 0]) ret[..., 1] = coords[..., 1] * np.sin(coords[..., 0]) for i in range(2, coords.shape[-1]): # copy any further axes ret[..., i] = coords[..., i] return ret @arg_to_array def imap(self, coords): ret = np.empty(coords.shape, coords.dtype) ret[..., 0] = np.arctan2(coords[..., 0], coords[..., 1]) ret[..., 1] = (coords[..., 0]**2 + coords[..., 1]**2) ** 0.5 for i in range(2, coords.shape[-1]): # copy any further axes ret[..., i] = coords[..., i] return ret #class BilinearTransform(BaseTransform): # # TODO # pass #class WarpTransform(BaseTransform): # """ Multiple bilinear transforms in a grid arrangement. # """ # # TODO class MagnifyTransform(BaseTransform): """ Magnifying lens transform. This transform causes a circular region to appear with larger scale around its center point. Parameters ---------- mag : float Magnification factor. Objects around the transform's center point will appear scaled by this amount relative to objects outside the circle. radii : (float, float) Inner and outer radii of the "lens". Objects inside the inner radius appear scaled, whereas objects outside the outer radius are unscaled, and the scale factor transitions smoothly between the two radii. center: (float, float) The center (x, y) point of the "lens". Notes ----- This transform works by segmenting its input coordinates into three regions--inner, outer, and transition. Coordinates in the inner region are multiplied by a constant scale factor around the center point, and coordinates in the transition region are scaled by a factor that transitions smoothly from the inner radius to the outer radius. Smooth functions that are appropriate for the transition region also tend to be difficult to invert analytically, so this transform instead samples the function numerically to allow trivial inversion. In OpenGL, the sampling is implemented as a texture holding a lookup table. """ glsl_map = """ vec4 mag_transform(vec4 pos) { vec2 d = vec2(pos.x - $center.x, pos.y - $center.y); float dist = length(d); if (dist == 0 || dist > $radii.y || ($mag < 1.01 && $mag > 0.99)) { return pos; } vec2 dir = d / dist; if( dist < $radii.x ) { dist = dist * $mag; } else { float r1 = $radii.x; float r2 = $radii.y; float x = (dist - r1) / (r2 - r1); float s = texture2D($trans, vec2(0, x)).r * $trans_max; dist = s; } d = $center + dir * dist; return vec4(d, pos.z, pos.w); }""" glsl_imap = glsl_map Linear = False _trans_resolution = 1000 def __init__(self, mag=3, radii=(7, 10), center=(0, 0)): self._center = center self._mag = mag self._radii = radii self._trans = None res = self._trans_resolution self._trans_tex = (gloo.Texture2D((res, 1, 1), interpolation='linear'), gloo.Texture2D((res, 1, 1), interpolation='linear')) self._trans_tex_max = None super(MagnifyTransform, self).__init__() @property def center(self): """ The (x, y) center point of the transform. """ return self._center @center.setter def center(self, center): if np.allclose(self._center, center): return self._center = center self.shader_map() self.shader_imap() @property def mag(self): """ The scale factor used in the central region of the transform. """ return self._mag @mag.setter def mag(self, mag): if self._mag == mag: return self._mag = mag self._trans = None self.shader_map() self.shader_imap() @property def radii(self): """ The inner and outer radii of the circular area bounding the transform. """ return self._radii @radii.setter def radii(self, radii): if np.allclose(self._radii, radii): return self._radii = radii self._trans = None self.shader_map() self.shader_imap() def shader_map(self): fn = super(MagnifyTransform, self).shader_map() fn['center'] = self._center # uniform vec2 fn['mag'] = self._mag fn['radii'] = (self._radii[0] / self._mag, self._radii[1]) self._get_transition() # make sure transition texture is up to date fn['trans'] = self._trans_tex[0] fn['trans_max'] = self._trans_tex_max[0] return fn def shader_imap(self): fn = super(MagnifyTransform, self).shader_imap() fn['center'] = self._center # uniform vec2 fn['mag'] = 1. / self._mag fn['radii'] = self._radii self._get_transition() # make sure transition texture is up to date fn['trans'] = self._trans_tex[1] fn['trans_max'] = self._trans_tex_max[1] return fn @arg_to_vec4 def map(self, x, _inverse=False): c = as_vec4(self.center)[0] m = self.mag r1, r2 = self.radii #c = np.array(c).reshape(1,2) xm = np.empty(x.shape, dtype=x.dtype) dx = (x - c) dist = (((dx**2).sum(axis=-1)) ** 0.5)[..., np.newaxis] dist[np.isnan(dist)] = 0 unit = dx / dist # magnified center region if _inverse: inner = (dist < r1)[:, 0] s = dist / m else: inner = (dist < (r1 / m))[:, 0] s = dist * m xm[inner] = c + unit[inner] * s[inner] # unmagnified outer region outer = (dist > r2)[:, 0] xm[outer] = x[outer] # smooth transition region, interpolated from trans trans = ~(inner | outer) # look up scale factor from trans temp, itemp = self._get_transition() if _inverse: tind = (dist[trans] - r1) * len(itemp) / (r2 - r1) temp = itemp else: tind = (dist[trans] - (r1/m)) * len(temp) / (r2 - (r1/m)) tind = np.clip(tind, 0, temp.shape[0]-1) s = temp[tind.astype(int)] xm[trans] = c + unit[trans] * s return xm def imap(self, coords): return self.map(coords, _inverse=True) def _get_transition(self): # Generate forward/reverse transition templates. # We would prefer to express this with an invertible function, but that # turns out to be tricky. The templates make any function invertible. if self._trans is None: m, r1, r2 = self.mag, self.radii[0], self.radii[1] res = self._trans_resolution xi = np.linspace(r1, r2, res) t = 0.5 * (1 + np.cos((xi - r2) * np.pi / (r2 - r1))) yi = (xi * t + xi * (1-t) / m).astype(np.float32) x = np.linspace(r1 / m, r2, res) y = np.interp(x, yi, xi).astype(np.float32) self._trans = (y, yi) # scale to 0.0-1.0 to prevent clipping (is this necessary?) mx = y.max(), yi.max() self._trans_tex_max = mx self._trans_tex[0].set_data((y/mx[0])[:, np.newaxis, np.newaxis]) self._trans_tex[1].set_data((yi/mx[1])[:, np.newaxis, np.newaxis]) return self._trans class Magnify1DTransform(MagnifyTransform): """ A 1-dimensional analog of MagnifyTransform. This transform expands its input along the x-axis, around a center x value. """ glsl_map = """ vec4 mag_transform(vec4 pos) { float dist = pos.x - $center.x; if (dist == 0 || abs(dist) > $radii.y || $mag == 1) { return pos; } float dir = dist / abs(dist); if( abs(dist) < $radii.x ) { dist = dist * $mag; } else { float r1 = $radii.x; float r2 = $radii.y; float x = (abs(dist) - r1) / (r2 - r1); dist = dir * texture2D($trans, vec2(0, x)).r * $trans_max; } return vec4($center.x + dist, pos.y, pos.z, pos.w); }""" glsl_imap = glsl_map vispy-0.4.0/vispy/visuals/isosurface.py0000664000175000017500000000365612527672621021764 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from __future__ import division from .mesh import MeshVisual from ..geometry.isosurface import isosurface class IsosurfaceVisual(MeshVisual): """Displays an isosurface of a 3D scalar array. Parameters ---------- data : ndarray | None 3D scalar array. level: float | None The level at which the isosurface is constructed from *data*. Notes ----- """ def __init__(self, data=None, level=None, **kwargs): self._data = None self._level = level self._recompute = True MeshVisual.__init__(self, **kwargs) if data is not None: self.set_data(data) @property def level(self): """ The threshold at which the isosurface is constructed from the 3D data. """ return self._level @level.setter def level(self, level): self._level = level self._recompute = True self.update() def set_data(self, data): """ Set the scalar array data Parameters ---------- data : ndarray A 3D array of scalar values. The isosurface is constructed to show all locations in the scalar field equal to ``self.level``. """ self._data = data self._recompute = True self.update() def draw(self, transforms): """Draw the visual Parameters ---------- transforms : instance of TransformSystem The transforms to use. """ if self._data is None or self._level is None: return if self._recompute: verts, faces = isosurface(self._data, self._level) MeshVisual.set_data(self, vertices=verts, faces=faces) self._recompute = False MeshVisual.draw(self, transforms) vispy-0.4.0/vispy/visuals/spectrogram.py0000664000175000017500000000406412527672621022141 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2015, Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- import numpy as np from .image import ImageVisual from ..util.fourier import stft, fft_freqs from ..ext.six import string_types class SpectrogramVisual(ImageVisual): """Calculate and show a spectrogram Parameters ---------- x : array-like 1D signal to operate on. ``If len(x) < n_fft``, x will be zero-padded to length ``n_fft``. n_fft : int Number of FFT points. Much faster for powers of two. step : int | None Step size between calculations. If None, ``n_fft // 2`` will be used. fs : float The sample rate of the data. window : str | None Window function to use. Can be ``'hann'`` for Hann window, or None for no windowing. color_scale : {'linear', 'log'} Scale to apply to the result of the STFT. ``'log'`` will use ``10 * log10(power)``. cmap : str Colormap name. clim : str | tuple Colormap limits. Should be ``'auto'`` or a two-element tuple of min and max values. """ def __init__(self, x, n_fft=256, step=None, fs=1., window='hann', color_scale='log', cmap='cubehelix', clim='auto'): self._n_fft = int(n_fft) self._fs = float(fs) if not isinstance(color_scale, string_types) or \ color_scale not in ('log', 'linear'): raise ValueError('color_scale must be "linear" or "log"') data = stft(x, self._n_fft, step, self._fs, window) data = np.abs(data) data = 20 * np.log10(data) if color_scale == 'log' else data super(SpectrogramVisual, self).__init__(data, clim=clim, cmap=cmap) @property def freqs(self): """The spectrogram frequencies""" return fft_freqs(self._n_fft, self._fs) vispy-0.4.0/vispy/visuals/ellipse.py0000664000175000017500000001045712527672621021253 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Simple ellipse visual based on PolygonVisual """ from __future__ import division import numpy as np from ..color import Color from .polygon import PolygonVisual class EllipseVisual(PolygonVisual): """ Displays a 2D ellipse Parameters ---------- pos : array Center of the ellipse color : instance of Color The face color to use. border_color : instance of Color The border color to use. radius : float | tuple Radius or radii of the ellipse Defaults to (0.1, 0.1) start_angle : float Start angle of the ellipse in degrees Defaults to 0. span_angle : float Span angle of the ellipse in degrees Defaults to 0. num_segments : int Number of segments to be used to draw the ellipse Defaults to 100 """ def __init__(self, pos=None, color='black', border_color=None, radius=(0.1, 0.1), start_angle=0., span_angle=360., num_segments=100, **kwargs): super(EllipseVisual, self).__init__() self.mesh.mode = 'triangle_fan' self._vertices = None self._pos = pos self._color = Color(color) self._border_color = Color(border_color) self._radius = radius self._start_angle = start_angle self._span_angle = span_angle self._num_segments = num_segments self._update() def _generate_vertices(self, pos, radius, start_angle, span_angle, num_segments): if isinstance(radius, (list, tuple)): if len(radius) == 2: xr, yr = radius else: raise ValueError("radius must be float or 2 value tuple/list" " (got %s of length %d)" % (type(radius), len(radius))) else: xr = yr = radius curve_segments = int(num_segments * span_angle / 360.) start_angle *= (np.pi/180.) self._vertices = np.empty([curve_segments+2, 2], dtype=np.float32) self._vertices[0] = np.float32([pos[0], pos[1]]) theta = np.linspace(start_angle, start_angle + (span_angle/180.)*np.pi, curve_segments+1) self._vertices[1:, 0] = pos[0] + xr * np.cos(theta) self._vertices[1:, 1] = pos[1] + yr * np.sin(theta) @property def radius(self): """ The start radii of the ellipse. """ return self._radius @radius.setter def radius(self, radius): self._radius = radius self._update() @property def start_angle(self): """ The start start_angle of the ellipse. """ return self._start_angle @start_angle.setter def start_angle(self, start_angle): self._start_angle = start_angle self._update() @property def span_angle(self): """ The angular span of the ellipse. """ return self._span_angle @span_angle.setter def span_angle(self, span_angle): self._span_angle = span_angle self._update() @property def num_segments(self): """ The number of segments in the ellipse. """ return self._num_segments @num_segments.setter def num_segments(self, num_segments): if num_segments < 1: raise ValueError('EllipseVisual must consist of more than 1 ' 'segment') self._num_segments = num_segments self._update() def _update(self): if self._pos is None: return self._generate_vertices(pos=self._pos, radius=self._radius, start_angle=self._start_angle, span_angle=self._span_angle, num_segments=self._num_segments) if not self._color.is_blank: self.mesh.set_data(vertices=self._vertices, color=self._color.rgba) if not self._border_color.is_blank: self.border.set_data(pos=self._vertices[1:], color=self._border_color.rgba) self.update() vispy-0.4.0/vispy/visuals/glsl/0000775000175000017500000000000012527674621020200 5ustar larsonerlarsoner00000000000000vispy-0.4.0/vispy/visuals/glsl/antialiasing.py0000664000175000017500000000630312437121522023202 0ustar larsonerlarsoner00000000000000# Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. # Distributed under the (new) BSD License. """Antialiasing GLSL functions.""" # ----------------------------------------------------------------------------- # Stroke # ----------------------------------------------------------------------------- """Compute antialiased fragment color for a stroke line. Inputs ------ distance (float): Signed distance to border (in pixels). Template variables ------------------ linewidth (float): Stroke line width (in pixels). antialias (float): Stroke antialiased area (in pixels). stroke (vec4): Stroke color. Outputs ------- color (vec4): The final color. """ ANTIALIAS_STROKE = """ vec4 stroke(float distance) { vec4 frag_color; float t = $linewidth/2.0 - $antialias; float signed_distance = distance; float border_distance = abs(signed_distance) - t; float alpha = border_distance/$antialias; alpha = exp(-alpha*alpha); if( border_distance < 0.0 ) frag_color = $stroke; else frag_color = vec4($stroke.rgb, $stroke.a * alpha); return frag_color; } """ # ----------------------------------------------------------------------------- # Stroke # ----------------------------------------------------------------------------- """Compute antialiased fragment color for an outlined shape. Inputs ------ distance (float): Signed distance to border (in pixels). Template variables ------------------ linewidth (float): Stroke line width (in pixels). antialias (float): Stroke antialiased area (in pixels). stroke (vec4): Stroke color. fill (vec4): Fill color. Outputs ------- color (vec4): The final color. """ ANTIALIAS_OUTLINE = """ vec4 outline(float distance) { vec4 frag_color; float t = $linewidth/2.0 - $antialias; float signed_distance = distance; float border_distance = abs(signed_distance) - t; float alpha = border_distance/$antialias; alpha = exp(-alpha*alpha); if( border_distance < 0.0 ) frag_color = $stroke; else if( signed_distance < 0.0 ) frag_color = mix($fill, $stroke, sqrt(alpha)); else frag_color = vec4($stroke.rgb, $stroke.a * alpha); return frag_color; } """ # ----------------------------------------------------------------------------- # Filled # ----------------------------------------------------------------------------- """Compute antialiased fragment color for a filled shape. Inputs ------ distance (float): Signed distance to border (in pixels). Template variables ------------------ linewidth (float): Stroke line width (in pixels). antialias (float): Stroke antialiased area (in pixels). fill (vec4): Fill color. Outputs ------- color (vec4): The final color. """ ANTIALIAS_FILLED = """ vec4 filled(float distance) { vec4 frag_color; float t = $linewidth/2.0 - $antialias; float signed_distance = distance; float border_distance = abs(signed_distance) - t; float alpha = border_distance/$antialias; alpha = exp(-alpha*alpha); if( border_distance < 0.0 ) frag_color = $fill; else if( signed_distance < 0.0 ) frag_color = $fill; else frag_color = vec4($fill.rgb, alpha * $fill.a); return frag_color; } """ vispy-0.4.0/vispy/visuals/glsl/color.py0000664000175000017500000000323712462464411021665 0ustar larsonerlarsoner00000000000000"""Color-related GLSL functions.""" # ----------------------------------------------------------------------------- # Colormaps # ----------------------------------------------------------------------------- """Texture lookup for a discrete color map stored in a 1*ncolors 2D texture. The `get_color()` function returns a RGB color from an index integer referring to the colormap. Inputs ------ index (int): The color index. Template variables ------------------ $ncolors (int): The number of colors in the colormap. $colormap (2D texture sampler): The sampler for the 2D 1*ncolors colormap texture. Outputs ------- color (vec3): The color. """ COLORMAP_TEXTURE = """ vec3 get_color(int index) { float x = (float(index) + .5) / float($ncolors); return texture2D($colormap, vec2(x, .5)).rgb; } """ # ----------------------------------------------------------------------------- # Color space transformations # ----------------------------------------------------------------------------- # From http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl # TODO: unit tests HSV_TO_RGB = """ vec3 hsv_to_rgb(vec3 c) { vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); } """ RGB_TO_HSV = """ vec3 rgb_to_hsv(vec3 c) { vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); float d = q.x - min(q.w, q.y); float e = 1.0e-10; return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); } """ vispy-0.4.0/vispy/visuals/glsl/__init__.py0000664000175000017500000000005312437121522022272 0ustar larsonerlarsoner00000000000000"""Repository of common GLSL functions.""" vispy-0.4.0/vispy/visuals/isoline.py0000664000175000017500000001644112527672621021257 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from __future__ import division import numpy as np from .line import LineVisual from ..color import ColorArray from ..ext.six import string_types from ..color.colormap import _normalize, get_colormap def iso_mesh_line(vertices, tris, vertex_data, levels): """Generate an isocurve from vertex data in a surface mesh. Parameters ---------- vertices : ndarray, shape (Nv, 3) Vertex coordinates. tris : ndarray, shape (Nf, 3) Indices of triangular element into the vertices array. vertex_data : ndarray, shape (Nv,) data at vertex. levels : ndarray, shape (Nl,) Levels at which to generate an isocurve Returns ------- lines : ndarray, shape (Nvout, 3) Vertex coordinates for lines points connects : ndarray, shape (Ne, 2) Indices of line element into the vertex array. vertex_level: ndarray, shape (Nvout,) level for vertex in lines Notes ----- Uses a marching squares algorithm to generate the isolines. """ lines = None connects = None vertex_level = None if not all([isinstance(x, np.ndarray) for x in (vertices, tris, vertex_data, levels)]): raise ValueError('all inputs must be numpy arrays') if vertices.shape[1] <= 3: verts = vertices elif vertices.shape[1] == 4: verts = vertices[:, :-1] else: verts = None if (verts is not None and tris.shape[1] == 3 and vertex_data.shape[0] == verts.shape[0]): edges = np.vstack((tris.reshape((-1)), np.roll(tris, -1, axis=1).reshape((-1)))).T edge_datas = vertex_data[edges] edge_coors = verts[edges].reshape(tris.shape[0]*3, 2, 3) for lev in levels: # index for select edges with vertices have only False - True # or True - False at extremity index = (edge_datas >= lev) index = index[:, 0] ^ index[:, 1] # xor calculation # Selectect edge edge_datas_Ok = edge_datas[index, :] xyz = edge_coors[index] # Linear interpolation ratio = np.array([(lev - edge_datas_Ok[:, 0]) / (edge_datas_Ok[:, 1] - edge_datas_Ok[:, 0])]) point = xyz[:, 0, :] + ratio.T*(xyz[:, 1, :] - xyz[:, 0, :]) nbr = point.shape[0]//2 if connects is not None: connect = np.arange(0, nbr*2).reshape((nbr, 2)) + \ len(lines) connects = np.append(connects, connect, axis=0) lines = np.append(lines, point, axis=0) vertex_level = np.append(vertex_level, np.zeros(len(point)) + lev) else: lines = point connects = np.arange(0, nbr*2).reshape((nbr, 2)) + \ len(lines) vertex_level = np.zeros(len(point)) + lev vertex_level = vertex_level.reshape((vertex_level.size, 1)) return lines, connects, vertex_level class IsolineVisual(LineVisual): """Isocurves of a tri mesh with data at vertices at different levels. Parameters ---------- vertices : ndarray, shape (Nv, 3) | None Vertex coordinates. tris : ndarray, shape (Nf, 3) | None Indices into the vertex array. data : ndarray, shape (Nv,) | None scalar at vertices levels : ndarray, shape (Nlev,) | None The levels at which the isocurve is constructed from "data". color_lev : Color, tuple, colormap name or array The color to use when drawing the line. If an array is given, it must be of shape (Nlev, 4) and provide one rgba color by level. **kwargs : dict Keyword arguments to pass to `LineVisual`. """ def __init__(self, vertices=None, tris=None, data=None, levels=None, color_lev=None, **kwargs): self._data = None self._vertices = None self._tris = None self._levels = levels self._color_lev = color_lev self._update_color_lev = True self._recompute = True kwargs['antialias'] = False LineVisual.__init__(self, method='gl', **kwargs) self.set_data(vertices=vertices, tris=tris, data=data) @property def levels(self): """ The threshold at which the isocurves are constructed from the data. """ return self._levels @levels.setter def levels(self, levels): self._levels = levels self._recompute = True self.update() @property def data(self): """The mesh data""" return self._vertices, self._tris, self._data def set_data(self, vertices=None, tris=None, data=None): """Set the data Parameters ---------- vertices : ndarray, shape (Nv, 3) | None Vertex coordinates. tris : ndarray, shape (Nf, 3) | None Indices into the vertex array. data : ndarray, shape (Nv,) | None scalar at vertices """ # modifier pour tenier compte des None self._recompute = True if data is not None: self._data = data self._recompute = True if vertices is not None: self._vertices = vertices self._recompute = True if tris is not None: self._tris = tris self._recompute = True self.update() @property def color(self): return self._color_lev def set_color(self, color): """Set the color Parameters ---------- color : instance of Color The color to use. """ if color is not None: self._color_lev = color self._update_color_lev = True self.update() def _levels_to_colors(self): if isinstance(self._color_lev, string_types): f_color_levs = get_colormap(self._color_lev) lev = _normalize(self._vl, self._vl.min(), self._vl.max()) colors = f_color_levs.map(lev) else: colors = ColorArray(self._color_lev).rgba if len(colors) == 1: colors = colors[0] return colors def draw(self, transforms): """Draw the visual Parameters ---------- transforms : instance of TransformSystem The transforms to use. """ if (self._data is None or self._levels is None or self._tris is None or self._vertices is None or self._color_lev is None): return if self._recompute: self._v, self._c, self._vl = iso_mesh_line(self._vertices, self._tris, self._data, self._levels) self._cl = self._levels_to_colors() self._recompute = False self._update_color_lev = False if self._update_color_lev: self._cl = self._levels_to_colors() self._update_color_lev = False LineVisual.set_data(self, pos=self._v, connect=self._c, color=self._cl) LineVisual.draw(self, transforms) vispy-0.4.0/vispy/visuals/components/0000775000175000017500000000000012527674621021424 5ustar larsonerlarsoner00000000000000vispy-0.4.0/vispy/visuals/components/texture.py0000664000175000017500000001160012527672621023472 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Normal components are modular shader components used for retrieving or generating surface normal vectors. These components generate a function in the fragment shader that accepts no arguments and returns a vec4 normal vector. Typically, the normal vector is computed in the vertex shader and passed by varying to the fragment shader. """ from __future__ import division from .component import VisualComponent from ..shaders import Varying from ... import gloo class TextureComponent(VisualComponent): """ Component that reads a texture uniform. A separate texture coordinate component must be provided. If the texture coordinate is outside the edge of the texture, then the fragment is discarded. """ SHADERS = dict( frag_color=""" vec4 texture_read() { vec2 tex_coord = $texture_coordinate(); if(tex_coord.x < 0.0 || tex_coord.x > 1.0 || tex_coord.y < 0.0 || tex_coord.y > 1.0) { discard; } return texture2D($texture, tex_coord.xy); } """) def __init__(self, texture, tex_coord_comp): super(TextureComponent, self).__init__() self.tex_coord_comp = tex_coord_comp self.texture = texture self._deps = [tex_coord_comp] def activate(self, program, mode): # Texture coordinates are generated by a separate component. ff = self._funcs['frag_color'] ff['texture_coordinate'] = self.tex_coord_comp.coord_shader() #ff['texture'] = ('uniform', 'sampler2D', self.texture) ff['texture'] = self.texture class VertexTextureCoordinateComponent(VisualComponent): """ Class that reads texture coordinates from a vertex buffer. """ SHADERS = dict( vert_post_hook=""" void texture_coord_support() { $tex_local_pos = $local_pos; } """, texture_coord=""" vec2 vertex_tex_coord() { vec4 tex_coord = $map_local_to_tex($tex_local_pos); return tex_coord.xy; } """) # exclude texture_coord when auto-attaching shaders because the visual # does not have a 'texture_coord' hook; instead this function will be # called by another component. AUTO_ATTACH = ['vert_post_hook'] def __init__(self, transform): super(VertexTextureCoordinateComponent, self).__init__() self.transform = transform # Create Varying to connect vertex / fragment shaders var = Varying('v_tex_local_pos', dtype='vec4') self.coord_shader()['tex_local_pos'] = var self._funcs['vert_post_hook']['tex_local_pos'] = var def coord_shader(self): """ Return the fragment shader function that returns a texture coordinate. """ return self._funcs['texture_coord'] def activate(self, program, mode): ff = self.coord_shader() ff['map_local_to_tex'] = self.transform self._funcs['vert_post_hook']['local_pos'] = \ self.visual._program.vert['local_pos'] class TextureCoordinateComponent(VisualComponent): """ Component that outputs texture coordinates derived from the local vertex coordinate and a transform. """ SHADERS = dict( vert_post_hook=""" void texture_coord_support() { $tex_coord_output = $tex_coord; } """, texture_coord=""" vec2 tex_coord() { return $tex_coord_input; } """) # exclude texture_coord when auto-attaching shaders because the visual # does not have a 'texture_coord' hook; instead this function will be # called by another component. AUTO_ATTACH = ['vert_post_hook'] def __init__(self, coords): super(TextureCoordinateComponent, self).__init__() self.coords = coords self._vbo = None # Create Varying to connect vertex / fragment shaders var = Varying('v_tex_coord', dtype='vec2') self.coord_shader()['tex_coord_input'] = var self._funcs['vert_post_hook']['tex_coord_output'] = var def coord_shader(self): """ Return the fragment shader function that returns a texture coordinate. """ return self._funcs['texture_coord'] @property def vbo(self): if self._vbo is None: self._vbo = gloo.VertexBuffer(self.coords) return self._vbo def activate(self, program, mode): vf = self._funcs['vert_post_hook'] #vf['tex_coord_output'] = Varying('v_tex_coord', dtype='vec2') #self._funcs['texture_coord']['tex_coord_input'] = \ # vf['tex_coord_output'] vf['tex_coord'] = self.vbo # attribute vec2 vispy-0.4.0/vispy/visuals/components/color.py0000664000175000017500000000434212527672621023115 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Color components are modular shader components used for retrieving or generating fragment colors. These components create a function in the fragment shader that accepts no arguments and returns a vec4 color. """ from __future__ import division import numpy as np from .component import VisualComponent from ..shaders import Varying from ... import gloo class UniformColorComponent(VisualComponent): """ Generates a uniform color for all vertices. """ SHADERS = dict( frag_color=""" vec4 colorInput() { return $rgba; } """) def __init__(self, color=(1, 1, 1, 1)): super(UniformColorComponent, self).__init__() self._color = color @property def color(self): return self._color @color.setter def color(self, c): self._color = c def activate(self, program, mode): self._funcs['frag_color']['rgba'] = np.array(self._color) class VertexColorComponent(VisualComponent): """ Reads color in from (N,4) array or vertex buffer. """ SHADERS = dict( frag_color=""" vec4 colorInput() { return $rgba; } """, vert_post_hook=""" void colorInputSupport() { $output_color = $input_color; } """) def __init__(self, color=None): super(VertexColorComponent, self).__init__() self._color = color self._vbo = None # Create Varying to connect vertex / fragment shaders var = Varying('rgba', dtype='vec4') self._funcs['frag_color']['rgba'] = var self._funcs['vert_post_hook']['output_color'] = var @property def color(self): return self._color @color.setter def color(self, c): self._color = c @property def vbo(self): if self._vbo is None: self._vbo = gloo.VertexBuffer(self._color.astype(np.float32)) return self._vbo def activate(self, program, mode): vf = self._funcs['vert_post_hook'] vf['input_color'] = self.vbo vispy-0.4.0/vispy/visuals/components/vertex.py0000664000175000017500000001506212527672621023315 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Vertex components are modular shader components used for retrieving or generating untransformed vertex locations. These components create a function in the vertex shader that accepts no arguments and returns a vec4 vertex location in the local coordinate system of the visual. """ from __future__ import division import numpy as np from .component import VisualComponent from ... import gloo class XYPosComponent(VisualComponent): """ generate local coordinate from xy (vec2) attribute and z (float) uniform """ SHADERS = dict( local_position=""" vec4 input_xy_pos() { return vec4($xy_pos, $z_pos, 1.0); } """) def __init__(self, xy=None, z=0.0, index=None): super(XYPosComponent, self).__init__() self._xy = None self._z = 0.0 self._index = False self._vbo = None self._ibo = None self.set_data(xy[:, :2], z, index) @property def supported_draw_modes(self): # TODO: Add support for converting between pre-indexed and unindexed if self._index is False: return set([self.DRAW_PRE_INDEXED]) else: return set([self.DRAW_UNINDEXED]) def set_data(self, xy=None, z=None, index=None): if xy is not None: self._xy = xy if z is not None: self._z = z if index is not None: self._index = index # TODO: might be better to re-upload data rather than creating # a new VB, if possible. self._vbo = None self.update() @property def vbo(self): if self._vbo is None: self._vbo = gloo.VertexBuffer(self._xy) return self._vbo @property def ibo(self): if self._ibo is None: self._ibo = gloo.IndexBuffer(self._index) return self._ibo def activate(self, program, draw_mode): fn = self._funcs['local_position'] #fn['xy_pos'] = ('attribute', 'vec2', self.vbo) #fn['z_pos'] = ('uniform', 'float', self._z) fn['xy_pos'] = self.vbo fn['z_pos'] = self._z @property def index(self): if self._index is False: return None else: return self.ibo class XYZPosComponent(VisualComponent): """ generate local coordinate from xyz (vec3) attribute """ SHADERS = dict( local_position=""" vec4 input_xyz_pos() { return vec4($xyz_pos, 1.0); } """) def __init__(self, pos=None, index=None): super(XYZPosComponent, self).__init__() self._pos = None self._index = False self._vbo = None self._ibo = None self.set_data(pos, index) @property def supported_draw_modes(self): # TODO: Add support for converting between pre-indexed and unindexed if self._index is False: return set([self.DRAW_PRE_INDEXED]) else: return set([self.DRAW_UNINDEXED]) def set_data(self, pos=None, index=None): if pos is not None: self._pos = pos if index is not None: self._index = index # TODO: might be better to re-upload data rather than creating # a new VB, if possible. self._vbo = None self.update() @property def vbo(self): if self._vbo is None: self._vbo = gloo.VertexBuffer(self._pos) return self._vbo @property def ibo(self): if self._ibo is None: self._ibo = gloo.IndexBuffer(self._index) return self._ibo def activate(self, program, draw_mode): #self._funcs['local_position']['xyz_pos'] = ('attribute', 'vec3', #self.vbo) self._funcs['local_position']['xyz_pos'] = self.vbo @property def index(self): if self._index is False: return None else: return self.ibo class HeightFieldComponent(VisualComponent): """ Generate vertex coordinate from 2D array of z-positions. x,y will be generated in the vertex shader using uniforms that specify the range. """ SHADERS = dict( local_position=""" vec4 input_z_pos() { int xind = int($index % $x_size); float x = $x_min + (xind * $x_step); int yind = int($index % $y_size); float y = $y_min + (yind * $y_step); return vec4(x, y, $z_pos, 1.0); } """) def __init__(self, z=None): super(HeightFieldComponent, self).__init__() self._z = None self._vbo = None if z is not None: self.set_data(z) @property def supported_draw_modes(self): # TODO: add support for pre-indexed data # (possibly here, possibly in another component class?) return set([self.DRAW_UNINDEXED]) def set_data(self, z): if self._z is None or self._z.shape != z.shape: # if this data has a new shape, we need a new index buffer self._ibo = None self._z = z # TODO: might be better to re-upload data rather than creating # a new VB, if possible. self._vbo = None self.update() @property def vbo(self): if self._vbo is None: self._vbo = gloo.VertexBuffer(self._z) self._index = gloo.VertexBuffer(np.arange(self._z.size)) return self._vbo def activate(self, program, draw_mode): self._funcs['local_position']['z_pos'] = self.vbo # attribute vec3 @property def index(self): """ The IndexBuffer used by this input component. """ if self._ibo is None: cols = self._z.shape[1]-1 rows = self._z.shape[0]-1 faces = np.empty((cols*rows*2, 3), dtype=np.uint) rowtemplate1 = (np.arange(cols).reshape(cols, 1) + np.array([[0, 1, cols+1]])) rowtemplate2 = (np.arange(cols).reshape(cols, 1) + np.array([[cols+1, 1, cols+2]])) for row in range(rows): start = row * cols * 2 faces[start:start+cols] = rowtemplate1 + row * (cols+1) faces[start+cols:start+(cols*2)] = (rowtemplate2 + row * (cols+1)) self._ibo = gloo.IndexBuffer(faces) return self._ibo vispy-0.4.0/vispy/visuals/components/__init__.py0000664000175000017500000000126212527672621023534 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from .color import UniformColorComponent, VertexColorComponent # noqa from .component import VisualComponent # noqa from .material import GridContourComponent, ShadingComponent # noqa from .normal import VertexNormalComponent # noqa from .texture import (TextureComponent, VertexTextureCoordinateComponent, # noqa TextureCoordinateComponent) # noqa from .vertex import XYPosComponent, XYZPosComponent, HeightFieldComponent # noqa from .clipper import Clipper # noqa from .color2 import Alpha, ColorFilter # noqa vispy-0.4.0/vispy/visuals/components/color2.py0000664000175000017500000000257512527672621023205 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. import weakref from ..shaders import Function # To replace color.py soon.. class Alpha(object): def __init__(self, alpha=1.0): self.shader = Function(""" void apply_alpha() { gl_FragColor.a = gl_FragColor.a * $alpha; } """) self.alpha = alpha @property def alpha(self): return self._alpha @alpha.setter def alpha(self, a): self._alpha = a self.shader['alpha'] = a def _attach(self, visual): self._visual = weakref.ref(visual) hook = visual._get_hook('frag', 'post') hook.add(self.shader()) class ColorFilter(object): def __init__(self, filter=(1, 1, 1, 1)): self.shader = Function(""" void apply_color_filter() { gl_FragColor = gl_FragColor * $filter; } """) self.filter = filter @property def filter(self): return self._filter @filter.setter def filter(self, f): self._filter = tuple(f) self.shader['filter'] = self._filter def _attach(self, visual): self._visual = visual hook = self._visual._get_hook('frag', 'post') hook.add(self.shader()) vispy-0.4.0/vispy/visuals/components/normal.py0000664000175000017500000000613212527672621023266 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Normal components are modular shader components used for retrieving or generating surface normal vectors. These components generate a function in the fragment shader that accepts no arguments and returns a vec4 normal vector. Typically, the normal vector is computed in the vertex shader and passed by varying to the fragment shader. """ from __future__ import division from .component import VisualComponent from ..shaders import Varying from ... import gloo class VertexNormalComponent(VisualComponent): SHADERS = dict( frag_normal=""" vec4 normal() { return $norm; } """, vert_post_hook=""" void normal_support() { //vec3 o = vec3(0,0,0); //vec3 i = o + $input_normal.xyz; //$output_normal = $map_local_to_nd(vec4(i,1)) - // $map_local_to_nd(vec4(o,1)); $output_normal = vec4($input_normal, 1); } """) # exclude frag_normal when auto-attaching shaders because the visual # does not have a 'frag_normal' hook; instead this function will be called # by another component. AUTO_ATTACH = ['vert_post_hook'] def __init__(self, meshdata, smooth=True): super(VertexNormalComponent, self).__init__() self._meshdata = meshdata self.smooth = smooth self._vbo = None self._vbo_mode = None # Create Varying to connect vertex / fragment shaders var = Varying('norm', dtype='vec4') self._funcs['frag_normal']['norm'] = var self._funcs['vert_post_hook']['output_normal'] = var def _make_vbo(self, mode): if self._vbo is None or self._vbo_mode != mode: if mode is self.DRAW_PRE_INDEXED: index = 'faces' else: index = None if self.smooth: norm = self._meshdata.get_vertex_normals(indexed=index) else: if index != 'faces': raise Exception("Not possible to draw faceted mesh without" "pre-indexing.") norm = self._meshdata.get_face_normals(indexed=index) self._vbo = gloo.VertexBuffer(norm) self._vbo_mode = mode return self._vbo def normal_shader(self): """ Return the fragment shader function that returns a normal vector. """ return self._funcs['frag_normal'] def activate(self, program, mode): vf = self._funcs['vert_post_hook'] vf['input_normal'] = self._make_vbo(mode) # attribute vec4 vf['map_local_to_nd'] = self.visual._program.vert['map_local_to_nd'] @property def supported_draw_modes(self): if self.smooth: return set([self.DRAW_PRE_INDEXED, self.DRAW_UNINDEXED]) else: # not possible to draw faceted mesh without pre-indexing. return set([self.DRAW_PRE_INDEXED]) vispy-0.4.0/vispy/visuals/components/material.py0000664000175000017500000000626012527672621023576 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Material components are modular shader components used for modifying fragment colors to change the visual's appearance. These generally create a function in the fragment shader that accepts a vec4 color as its only argument and returns a modified vec4 color. """ from __future__ import division from .component import VisualComponent from ..shaders import Varying class GridContourComponent(VisualComponent): """ Draw grid lines across a surface. """ SHADERS = dict( frag_color=""" vec4 grid_contour(vec4 color) { if ( mod($pos.x, $spacing.x) < 0.005 || mod($pos.y, $spacing.y) < 0.005 || mod($pos.z, $spacing.z) < 0.005 ) { return color + 0.7 * (vec4(1,1,1,1) - color); } else { return color; } } """, vert_post_hook=""" void grid_contour_support() { $output_pos = local_position(); } """) def __init__(self, spacing): super(GridContourComponent, self).__init__() self.spacing = spacing # Create Varying to connect vertex / fragment shaders var = Varying('pos', dtype='vec4') self._funcs['frag_color']['pos'] = var self._funcs['vert_post_hook']['output_pos'] = var @property def color(self): return self._color @color.setter def color(self, c): self._color = c def activate(self, program, mode): ff = self._funcs['frag_color'] ff['spacing'] = self.spacing # uniform vec3 class ShadingComponent(VisualComponent): """ Phong reflection and shading material. """ SHADERS = dict( frag_color=""" vec4 shading(vec4 color) { vec3 norm = normalize($normal().xyz); vec3 light = normalize($light_direction.xyz); float p = dot(light, norm); p = (p < 0. ? 0. : p); vec4 diffuse = $light_color * p; diffuse.a = 1.0; p = dot(reflect(light, norm), vec3(0,0,1)); if (p < 0.0) { p = 0.0; } vec4 specular = $light_color * 5.0 * pow(p, 100.); return color * ($ambient + diffuse) + specular; } """) def __init__(self, normal_comp, lights, ambient=0.2): super(ShadingComponent, self).__init__() self.normal_comp = normal_comp self._deps = [normal_comp] self.lights = lights self.ambient = ambient def activate(self, program, mode): # Normals are generated by output of another component ff = self._funcs['frag_color'] ff['normal'] = self.normal_comp.normal_shader() # TODO: add support for multiple lights ff['light_direction'] = tuple(self.lights[0][0][:3]) + (1,) # u vec4 ff['light_color'] = tuple(self.lights[0][1][:3]) + (1,) # u vec4 ff['ambient'] = self.ambient # u float vispy-0.4.0/vispy/visuals/components/component.py0000664000175000017500000001145512426461252023776 0ustar larsonerlarsoner00000000000000from ..shaders import Function class VisualComponent(object): """ Base for classes that encapsulate some modular component of a Visual. These define Functions for extending the shader code as well as an activate() method that inserts these Functions into a program. VisualComponents may be considered friends of the Visual they are attached to; often they will need to access internal data structures of the Visual to make decisions about constructing shader components. """ DRAW_PRE_INDEXED = 1 DRAW_UNINDEXED = 2 # Maps {'program_hook': 'GLSL code'} SHADERS = {} # List of shaders to automatically attach to the visual's program. # If None, then all shaders are attached. AUTO_ATTACH = None def __init__(self, visual=None): self._visual = None if visual is not None: self._attach(visual) self._funcs = dict([(name, Function(code)) for name, code in self.SHADERS.items()]) # components that are required by this component self._deps = [] # only detach when count drops to 0; # this is like a reference count for component dependencies. self._attach_count = 0 @property def visual(self): """The Visual that this component is attached to.""" return self._visual def _attach(self, visual): """Attach this component to a Visual. This should be called by the Visual itself. A component may only be attached to a single Visual. However, it may be attached _multiple times_ to the same visual. The default implementation of this method calls self._auto_attach_shaders() to generate the list of shader callbacks that should be added to the Visual's program. """ if visual is not self._visual and self._visual is not None: raise Exception("Cannot attach component %s to %s; already " "attached to %s" % (self, visual, self._visual)) self._visual = visual self._attach_count += 1 for hook in self._auto_attach_shaders(): func = self._funcs[hook] try: visual._program.vert.add_callback(hook, func) except KeyError: visual._program.frag.add_callback(hook, func) for comp in self._deps: comp._attach(visual) def _detach(self): """Detach this component from its Visual. This should be called by the visual itself. If the component was attached multiple times, it must be detached the same number of times. """ if self._attach_count == 0: raise Exception("Cannot detach component %s; not attached." % self) self._attach_count -= 1 if self._attach_count == 0: for hook in self._auto_attach_shaders(): func = self._funcs[hook] try: self._visual._program.vert.remove_callback(hook, func) except KeyError: self._visual._program.frag.remove_callback(hook, func) self._visual = None for comp in self._deps: comp._detach() def _auto_attach_shaders(self): """ Return a list of shaders to automatically attach/detach """ if self.AUTO_ATTACH is None: return self._funcs.keys() else: return self.AUTO_ATTACH @property def supported_draw_modes(self): """ A set of the draw modes (either DRAW_PRE_INDEXED, DRAW_UNINDEXED, or both) currently supported by this component. DRAW_PRE_INDEXED indicates that the component may be used when the program uses an array of indices to determine the order of elements to draw from its vertex buffers (using glDrawElements). DRAW_UNINDEXED indicates that the component may be used when the program will not use an array of indices; rather, vertex buffers are processed in the order they appear in the buffer (using glDrawArrays). By default, this method returns a tuple with both values. Components that only support one mode must override this method. """ # TODO: This should be expanded to include other questions, such as # whether the component supports geometry shaders. return set([self.DRAW_PRE_INDEXED, self.DRAW_UNINDEXED]) def update(self): """ Inform the attached visual that this component has changed. """ if self.visual is not None: self.visual.update() def activate(self, program): """ *program* is about to draw; attach to *program* all functions and data required by this component. """ raise NotImplementedError vispy-0.4.0/vispy/visuals/components/clipper.py0000664000175000017500000000274612527672621023443 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. import weakref from ..shaders import Function from ..transforms import NullTransform from ...geometry import Rect clip_frag = """ void clip() { vec4 pos = $fb_to_clip(gl_FragCoord); if( pos.x < $view.x || pos.x > $view.y || pos.y < $view.z || pos.y > $view.w ) { discard; } } """ class Clipper(object): """Clips visual output to a rectangular region. """ def __init__(self, bounds=(0, 0, 1, 1), transform=None): self.clip_shader = Function(clip_frag) self.clip_expr = self.clip_shader() self.bounds = bounds # (x, y, w, h) if transform is None: transform = NullTransform() self.set_transform(transform) @property def bounds(self): return self._bounds @bounds.setter def bounds(self, b): self._bounds = Rect(b).normalized() b = self._bounds self.clip_shader['view'] = (b.left, b.right, b.bottom, b.top) def _attach(self, visual): self._visual = weakref.ref(visual) try: hook = visual._get_hook('frag', 'pre') except KeyError: raise NotImplementedError("Visual %s does not support clipping" % visual) hook.add(self.clip_expr) def set_transform(self, tr): self.clip_shader['fb_to_clip'] = tr vispy-0.4.0/vispy/visuals/text/0000775000175000017500000000000012527674621020223 5ustar larsonerlarsoner00000000000000vispy-0.4.0/vispy/visuals/text/text.py0000664000175000017500000004423512527672621021567 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2015, Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- ############################################################################## # Load font into texture from __future__ import division import numpy as np from copy import deepcopy from os import path as op import sys from ._sdf import SDFRenderer from ...gloo import (TextureAtlas, set_state, IndexBuffer, VertexBuffer, set_viewport) from ...gloo import gl from ...gloo.wrappers import _check_valid from ...ext.six import string_types from ...util.fonts import _load_glyph from ..transforms import STTransform from ..shaders import ModularProgram from ...color import Color from ..visual import Visual from ...io import _data_dir class TextureFont(object): """Gather a set of glyphs relative to a given font name and size Parameters ---------- font : dict Dict with entries "face", "size", "bold", "italic". renderer : instance of SDFRenderer SDF renderer to use. """ def __init__(self, font, renderer): self._atlas = TextureAtlas() self._atlas.wrapping = 'clamp_to_edge' self._kernel = np.load(op.join(_data_dir, 'spatial-filters.npy')) self._renderer = renderer self._font = deepcopy(font) self._font['size'] = 256 # use high resolution point size for SDF self._lowres_size = 64 # end at this point size for storage assert (self._font['size'] % self._lowres_size) == 0 # spread/border at the high-res for SDF calculation; must be chosen # relative to fragment_insert.glsl multiplication factor to ensure we # get to zero at the edges of characters self._spread = 32 assert self._spread % self.ratio == 0 self._glyphs = {} @property def ratio(self): """Ratio of the initial high-res to final stored low-res glyph""" return self._font['size'] // self._lowres_size @property def slop(self): """Extra space along each glyph edge due to SDF borders""" return self._spread // self.ratio def __getitem__(self, char): if not (isinstance(char, string_types) and len(char) == 1): raise TypeError('index must be a 1-character string') if char not in self._glyphs: self._load_char(char) return self._glyphs[char] def _load_char(self, char): """Build and store a glyph corresponding to an individual character Parameters ---------- char : str A single character to be represented. """ assert isinstance(char, string_types) and len(char) == 1 assert char not in self._glyphs # load new glyph data from font _load_glyph(self._font, char, self._glyphs) # put new glyph into the texture glyph = self._glyphs[char] bitmap = glyph['bitmap'] # convert to padded array data = np.zeros((bitmap.shape[0] + 2*self._spread, bitmap.shape[1] + 2*self._spread), np.uint8) data[self._spread:-self._spread, self._spread:-self._spread] = bitmap # Store, while scaling down to proper size height = data.shape[0] // self.ratio width = data.shape[1] // self.ratio region = self._atlas.get_free_region(width + 2, height + 2) if region is None: raise RuntimeError('Cannot store glyph') x, y, w, h = region x, y, w, h = x + 1, y + 1, w - 2, h - 2 self._renderer.render_to_texture(data, self._atlas, (x, y), (w, h)) u0 = x / float(self._atlas.shape[1]) v0 = y / float(self._atlas.shape[0]) u1 = (x+w) / float(self._atlas.shape[1]) v1 = (y+h) / float(self._atlas.shape[0]) texcoords = (u0, v0, u1, v1) glyph.update(dict(size=(w, h), texcoords=texcoords)) class FontManager(object): """Helper to create TextureFont instances and reuse them when possible""" # todo: store a font-manager on each context, # or let TextureFont use a TextureAtlas for each context def __init__(self): self._fonts = {} self._renderer = SDFRenderer() def get_font(self, face, bold=False, italic=False): """Get a font described by face and size""" key = '%s-%s-%s' % (face, bold, italic) if key not in self._fonts: font = dict(face=face, bold=bold, italic=italic) self._fonts[key] = TextureFont(font, self._renderer) return self._fonts[key] ############################################################################## # The visual def _text_to_vbo(text, font, anchor_x, anchor_y, lowres_size): """Convert text characters to VBO""" text_vtype = np.dtype([('a_position', 'f4', 2), ('a_texcoord', 'f4', 2)]) vertices = np.zeros(len(text) * 4, dtype=text_vtype) prev = None width = height = ascender = descender = 0 ratio, slop = 1. / font.ratio, font.slop x_off = -slop # Need to make sure we have a unicode string here (Py2.7 mis-interprets # characters like "•" otherwise) if sys.version[0] == '2' and isinstance(text, str): text = text.decode('utf-8') # Need to store the original viewport, because the font[char] will # trigger SDF rendering, which changes our viewport # todo: get rid of call to glGetParameter! orig_viewport = gl.glGetParameter(gl.GL_VIEWPORT) for ii, char in enumerate(text): glyph = font[char] kerning = glyph['kerning'].get(prev, 0.) * ratio x0 = x_off + glyph['offset'][0] * ratio + kerning y0 = glyph['offset'][1] * ratio + slop x1 = x0 + glyph['size'][0] y1 = y0 - glyph['size'][1] u0, v0, u1, v1 = glyph['texcoords'] position = [[x0, y0], [x0, y1], [x1, y1], [x1, y0]] texcoords = [[u0, v0], [u0, v1], [u1, v1], [u1, v0]] vi = ii * 4 vertices['a_position'][vi:vi+4] = position vertices['a_texcoord'][vi:vi+4] = texcoords x_move = glyph['advance'] * ratio + kerning x_off += x_move ascender = max(ascender, y0 - slop) descender = min(descender, y1 + slop) width += x_move height = max(height, glyph['size'][1] - 2*slop) prev = char # Also analyse chars with large ascender and descender, otherwise the # vertical alignment can be very inconsistent for char in 'hy': glyph = font[char] y0 = glyph['offset'][1] * ratio + slop y1 = y0 - glyph['size'][1] ascender = max(ascender, y0 - slop) descender = min(descender, y1 + slop) height = max(height, glyph['size'][1] - 2*slop) set_viewport(*orig_viewport) # Tight bounding box (loose would be width, font.height /.asc / .desc) width -= glyph['advance'] * ratio - (glyph['size'][0] - 2*slop) dx = dy = 0 if anchor_y == 'top': dy = -ascender elif anchor_y in ('center', 'middle'): dy = -(height / 2 + descender) elif anchor_y == 'bottom': dy = -descender # Already referenced to baseline # elif anchor_y == 'baseline': # dy = -descender if anchor_x == 'right': dx = -width elif anchor_x == 'center': dx = -width / 2. vertices['a_position'] += (dx, dy) vertices['a_position'] /= lowres_size return VertexBuffer(vertices) class TextVisual(Visual): """Visual that displays text Parameters ---------- text : str Text to display. color : instance of Color Color to use. bold : bool Bold face. italic : bool Italic face. face : str Font face to use. font_size : float Point size to use. pos : tuple Position (x, y) of the text. rotation : float Rotation (in degrees) of the text clockwise. anchor_x : str Horizontal text anchor. anchor_y : str Vertical text anchor. font_manager : object | None Font manager to use (can be shared if the GLContext is shared). """ VERTEX_SHADER = """ uniform vec3 u_pos; // anchor position uniform float u_rotation; // rotation in rad attribute vec2 a_position; // in point units attribute vec2 a_texcoord; varying vec2 v_texcoord; void main(void) { // Eventually "rot" should be handled by SRTTransform or so... mat4 rot = mat4(cos(u_rotation), -sin(u_rotation), 0, 0, sin(u_rotation), cos(u_rotation), 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); vec4 pos = $transform(vec4(u_pos, 1.0)) + $text_scale(rot * vec4(a_position, 0, 0)); gl_Position = pos; v_texcoord = a_texcoord; } """ FRAGMENT_SHADER = """ // Adapted from glumpy with permission const float M_SQRT1_2 = 0.707106781186547524400844362104849039; const float kernel_bias = -0.234377; const float kernel_scale = 1.241974; uniform sampler2D u_font_atlas; uniform vec2 u_font_atlas_shape; uniform vec4 u_color; uniform float u_npix; uniform sampler2D u_kernel; varying vec2 v_texcoord; const float center = 0.5; // CatRom interpolation code vec4 filter1D_radius2(sampler2D kernel, float index, float x, vec4 c0, vec4 c1, vec4 c2, vec4 c3) { float w, w_sum = 0.0; vec4 r = vec4(0.0,0.0,0.0,0.0); w = texture2D(kernel, vec2(0.500000+(x/2.0),index) ).r; w = w*kernel_scale + kernel_bias; r += c0 * w; w = texture2D(kernel, vec2(0.500000-(x/2.0),index) ).r; w = w*kernel_scale + kernel_bias; r += c2 * w; w = texture2D(kernel, vec2(0.000000+(x/2.0),index) ).r; w = w*kernel_scale + kernel_bias; r += c1 * w; w = texture2D(kernel, vec2(1.000000-(x/2.0),index) ).r; w = w*kernel_scale + kernel_bias; r += c3 * w; return r; } vec4 filter2D_radius2(sampler2D texture, sampler2D kernel, float index, vec2 uv, vec2 pixel) { vec2 texel = uv/pixel - vec2(0.0,0.0) ; vec2 f = fract(texel); texel = (texel-fract(texel)+vec2(0.001,0.001))*pixel; vec4 t0 = filter1D_radius2(kernel, index, f.x, texture2D( texture, texel + vec2(-1,-1)*pixel), texture2D( texture, texel + vec2(0,-1)*pixel), texture2D( texture, texel + vec2(1,-1)*pixel), texture2D( texture, texel + vec2(2,-1)*pixel)); vec4 t1 = filter1D_radius2(kernel, index, f.x, texture2D( texture, texel + vec2(-1,0)*pixel), texture2D( texture, texel + vec2(0,0)*pixel), texture2D( texture, texel + vec2(1,0)*pixel), texture2D( texture, texel + vec2(2,0)*pixel)); vec4 t2 = filter1D_radius2(kernel, index, f.x, texture2D( texture, texel + vec2(-1,1)*pixel), texture2D( texture, texel + vec2(0,1)*pixel), texture2D( texture, texel + vec2(1,1)*pixel), texture2D( texture, texel + vec2(2,1)*pixel)); vec4 t3 = filter1D_radius2(kernel, index, f.x, texture2D( texture, texel + vec2(-1,2)*pixel), texture2D( texture, texel + vec2(0,2)*pixel), texture2D( texture, texel + vec2(1,2)*pixel), texture2D( texture, texel + vec2(2,2)*pixel)); return filter1D_radius2(kernel, index, f.y, t0, t1, t2, t3); } vec4 CatRom(sampler2D texture, vec2 shape, vec2 uv) { return filter2D_radius2(texture, u_kernel, 0.468750, uv, 1.0/shape); } float contour(in float d, in float w) { return smoothstep(center - w, center + w, d); } float sample(sampler2D texture, vec2 uv, float w) { return contour(texture2D(texture, uv).r, w); } void main(void) { vec4 color = u_color; vec2 uv = v_texcoord.xy; vec4 rgb; // Use interpolation at high font sizes if(u_npix >= 50.0) rgb = CatRom(u_font_atlas, u_font_atlas_shape, uv); else rgb = texture2D(u_font_atlas, uv); float distance = rgb.r; // GLSL's fwidth = abs(dFdx(uv)) + abs(dFdy(uv)) float width = 0.5 * fwidth(distance); // sharpens a bit // Regular SDF float alpha = contour(distance, width); if (u_npix < 30.) { // Supersample, 4 extra points // Half of 1/sqrt2; you can play with this float dscale = 0.5 * M_SQRT1_2; vec2 duv = dscale * (dFdx(v_texcoord) + dFdy(v_texcoord)); vec4 box = vec4(v_texcoord-duv, v_texcoord+duv); float asum = sample(u_font_atlas, box.xy, width) + sample(u_font_atlas, box.zw, width) + sample(u_font_atlas, box.xw, width) + sample(u_font_atlas, box.zy, width); // weighted average, with 4 extra points having 0.5 weight // each, so 1 + 0.5*4 = 3 is the divisor alpha = (alpha + 0.5 * asum) / 3.0; } gl_FragColor = vec4(color.rgb, color.a * alpha); } """ def __init__(self, text, color='black', bold=False, italic=False, face='OpenSans', font_size=12, pos=[0, 0, 0], rotation=0., anchor_x='center', anchor_y='center', font_manager=None, **kwargs): Visual.__init__(self, **kwargs) # Check input valid_keys = ('top', 'center', 'middle', 'baseline', 'bottom') _check_valid('anchor_y', anchor_y, valid_keys) valid_keys = ('left', 'center', 'right') _check_valid('anchor_x', anchor_x, valid_keys) # Init font handling stuff # _font_manager is a temporary solution to use global mananger self._font_manager = font_manager or FontManager() self._font = self._font_manager.get_font(face, bold, italic) self._program = ModularProgram(self.VERTEX_SHADER, self.FRAGMENT_SHADER) self._vertices = None self._anchors = (anchor_x, anchor_y) # Init text properties self.color = color self.text = text self.font_size = font_size self.pos = pos self.rotation = rotation self._text_scale = STTransform() @property def text(self): """The text string""" return self._text @text.setter def text(self, text): assert isinstance(text, string_types) self._text = text self._vertices = None @property def font_size(self): """ The font size (in points) of the text """ return self._font_size @font_size.setter def font_size(self, size): self._font_size = max(0.0, float(size)) @property def color(self): """ The color of the text """ return self._color @color.setter def color(self, color): self._color = Color(color) @property def rotation(self): """ The rotation of the text (clockwise, in degrees) """ return self._rotation * 180. / np.pi @rotation.setter def rotation(self, rotation): self._rotation = float(rotation) * np.pi / 180. @property def pos(self): """ The position of the text anchor in the local coordinate frame """ return self._pos @pos.setter def pos(self, pos): self._pos = np.array(pos, np.float32) if self._pos.ndim != 1 or self._pos.size not in (2, 3): raise ValueError('pos must be array-like with 2 or 3 elements') if self._pos.size == 2: self._pos = np.concatenate((self._pos, [0.])) def draw(self, transforms): """Draw the Text Parameters ---------- transforms : instance of TransformSystem The transforms to use. """ # attributes / uniforms are not available until program is built if len(self.text) == 0: return if self._vertices is None: # we delay creating vertices because it requires a context, # which may or may not exist when the object is initialized transforms.canvas.context.flush_commands() # flush GLIR commands self._vertices = _text_to_vbo(self._text, self._font, self._anchors[0], self._anchors[1], self._font._lowres_size) idx = (np.array([0, 1, 2, 0, 2, 3], np.uint32) + np.arange(0, 4*len(self._text), 4, dtype=np.uint32)[:, np.newaxis]) self._ib = IndexBuffer(idx.ravel()) self._program.bind(self._vertices) # todo: do some testing to verify that the scaling is correct n_pix = (self._font_size / 72.) * transforms.dpi # logical pix tr = (transforms.document_to_framebuffer * transforms.framebuffer_to_render) px_scale = (tr.map((1, 0)) - tr.map((0, 1)))[:2] self._program.vert['transform'] = transforms.get_full_transform() self._text_scale.scale = px_scale * n_pix self._program.vert['text_scale'] = self._text_scale self._program['u_npix'] = n_pix self._program['u_kernel'] = self._font._kernel self._program['u_rotation'] = self._rotation self._program['u_pos'] = self._pos self._program['u_color'] = self._color.rgba self._program['u_font_atlas'] = self._font._atlas self._program['u_font_atlas_shape'] = self._font._atlas.shape[:2] set_state(blend=True, depth_test=False, blend_func=('src_alpha', 'one_minus_src_alpha')) self._program.draw('triangles', self._ib) vispy-0.4.0/vispy/visuals/text/__init__.py0000664000175000017500000000055312527672621022335 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2015, Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- from .text import TextVisual # noqa vispy-0.4.0/vispy/visuals/text/_sdf.py0000664000175000017500000002441312456026505021505 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- """ Jump flooding algoritm for EDT using GLSL code: Author: Stefan Gustavson (stefan.gustavson@gmail.com) 2010-08-24. This code is in the public domain. Adapted to `vispy` by Eric Larson . """ import numpy as np from os import path as op from ...gloo import (Program, FrameBuffer, VertexBuffer, Texture2D, set_viewport, set_state) this_dir = op.dirname(__file__) vert_seed = """ attribute vec2 a_position; attribute vec2 a_texcoord; varying vec2 v_uv; void main( void ) { v_uv = a_texcoord.xy; gl_Position = vec4(a_position.xy, 0., 1.); } """ vert = """ uniform float u_texw; uniform float u_texh; uniform float u_step; attribute vec2 a_position; attribute vec2 a_texcoord; varying float v_stepu; varying float v_stepv; varying vec2 v_uv; void main( void ) { v_uv = a_texcoord.xy; v_stepu = u_step / u_texw; // Saves a division in the fragment shader v_stepv = u_step / u_texh; gl_Position = vec4(a_position.xy, 0., 1.); } """ frag_seed = """ uniform sampler2D u_texture; varying vec2 v_uv; void main( void ) { float pixel = texture2D(u_texture, v_uv).r; vec4 myzero = vec4(128. / 255., 128. / 255., 0., 0.); // Zero vec4 myinfinity = vec4(0., 0., 0., 0.); // Infinity // Pixels >= 0.5 are objects, others are background gl_FragColor = pixel >= 0.5 ? myzero : myinfinity; } """ frag_flood = """ uniform sampler2D u_texture; varying float v_stepu; varying float v_stepv; varying vec2 v_uv; vec2 remap(vec4 floatdata) { vec2 scaleddata = vec2(floatdata.x * 65280. + floatdata.z * 255., floatdata.y * 65280. + floatdata.w * 255.); return scaleddata / 32768. - 1.0; } vec4 remap_inv(vec2 floatvec) { vec2 data = (floatvec + 1.0) * 32768.; float x = floor(data.x / 256.); float y = floor(data.y / 256.); return vec4(x, y, data.x - x * 256., data.y - y * 256.) / 255.; } void main( void ) { // Search for better distance vectors among 8 candidates vec2 stepvec; // Relative offset to candidate being tested vec2 newvec; // Absolute position of that candidate vec3 newseed; // Closest point from that candidate (.xy) and its dist (.z) vec3 bestseed; // Closest seed so far bestseed.xy = remap(texture2D(u_texture, v_uv).rgba); bestseed.z = length(bestseed.xy); stepvec = vec2(-v_stepu, -v_stepv); newvec = v_uv + stepvec; if (all(bvec4(lessThan(newvec, vec2(1.0)), greaterThan(newvec, vec2(0.0))))){ newseed.xy = remap(texture2D(u_texture, newvec).rgba); if(newseed.x > -0.99999) { // if the new seed is not "indeterminate dist" newseed.xy = newseed.xy + stepvec; newseed.z = length(newseed.xy); if(newseed.z < bestseed.z) { bestseed = newseed; } } } stepvec = vec2(-v_stepu, 0.0); newvec = v_uv + stepvec; if (all(bvec4(lessThan(newvec, vec2(1.0)), greaterThan(newvec, vec2(0.0))))){ newseed.xy = remap(texture2D(u_texture, newvec).rgba); if(newseed.x > -0.99999) { // if the new seed is not "indeterminate dist" newseed.xy = newseed.xy + stepvec; newseed.z = length(newseed.xy); if(newseed.z < bestseed.z) { bestseed = newseed; } } } stepvec = vec2(-v_stepu, v_stepv); newvec = v_uv + stepvec; if (all(bvec4(lessThan(newvec, vec2(1.0)), greaterThan(newvec, vec2(0.0))))){ newseed.xy = remap(texture2D(u_texture, newvec).rgba); if(newseed.x > -0.99999) { // if the new seed is not "indeterminate dist" newseed.xy = newseed.xy + stepvec; newseed.z = length(newseed.xy); if(newseed.z < bestseed.z) { bestseed = newseed; } } } stepvec = vec2(0.0, -v_stepv); newvec = v_uv + stepvec; if (all(bvec4(lessThan(newvec, vec2(1.0)), greaterThan(newvec, vec2(0.0))))){ newseed.xy = remap(texture2D(u_texture, newvec).rgba); if(newseed.x > -0.99999) { // if the new seed is not "indeterminate dist" newseed.xy = newseed.xy + stepvec; newseed.z = length(newseed.xy); if(newseed.z < bestseed.z) { bestseed = newseed; } } } stepvec = vec2(0.0, v_stepv); newvec = v_uv + stepvec; if (all(bvec4(lessThan(newvec, vec2(1.0)), greaterThan(newvec, vec2(0.0))))){ newseed.xy = remap(texture2D(u_texture, newvec).rgba); if(newseed.x > -0.99999) { // if the new seed is not "indeterminate dist" newseed.xy = newseed.xy + stepvec; newseed.z = length(newseed.xy); if(newseed.z < bestseed.z) { bestseed = newseed; } } } stepvec = vec2(v_stepu, -v_stepv); newvec = v_uv + stepvec; if (all(bvec4(lessThan(newvec, vec2(1.0)), greaterThan(newvec, vec2(0.0))))){ newseed.xy = remap(texture2D(u_texture, newvec).rgba); if(newseed.x > -0.99999) { // if the new seed is not "indeterminate dist" newseed.xy = newseed.xy + stepvec; newseed.z = length(newseed.xy); if(newseed.z < bestseed.z) { bestseed = newseed; } } } stepvec = vec2(v_stepu, 0.0); newvec = v_uv + stepvec; if (all(bvec4(lessThan(newvec, vec2(1.0)), greaterThan(newvec, vec2(0.0))))){ newseed.xy = remap(texture2D(u_texture, newvec).rgba); if(newseed.x > -0.99999) { // if the new seed is not "indeterminate dist" newseed.xy = newseed.xy + stepvec; newseed.z = length(newseed.xy); if(newseed.z < bestseed.z) { bestseed = newseed; } } } stepvec = vec2(v_stepu, v_stepv); newvec = v_uv + stepvec; if (all(bvec4(lessThan(newvec, vec2(1.0)), greaterThan(newvec, vec2(0.0))))){ newseed.xy = remap(texture2D(u_texture, newvec).rgba); if(newseed.x > -0.99999) { // if the new seed is not "indeterminate dist" newseed.xy = newseed.xy + stepvec; newseed.z = length(newseed.xy); if(newseed.z < bestseed.z) { bestseed = newseed; } } } gl_FragColor = remap_inv(bestseed.xy); } """ frag_insert = """ uniform sampler2D u_texture; uniform sampler2D u_pos_texture; uniform sampler2D u_neg_texture; varying float v_stepu; varying float v_stepv; varying vec2 v_uv; vec2 remap(vec4 floatdata) { vec2 scaled_data = vec2(floatdata.x * 65280. + floatdata.z * 255., floatdata.y * 65280. + floatdata.w * 255.); return scaled_data / 32768. - 1.0; } void main( void ) { float pixel = texture2D(u_texture, v_uv).r; // convert distance from normalized units -> pixels vec2 rescale = vec2(v_stepu, v_stepv); float shrink = 8.; rescale = rescale * 256. / shrink; // Without the division, 1 RGB increment = 1 px distance vec2 pos_distvec = remap(texture2D(u_pos_texture, v_uv).rgba) / rescale; vec2 neg_distvec = remap(texture2D(u_neg_texture, v_uv).rgba) / rescale; if (pixel <= 0.5) gl_FragColor = vec4(0.5 - length(pos_distvec)); else gl_FragColor = vec4(0.5 - (shrink - 1.) / 256. + length(neg_distvec)); } """ class SDFRenderer(object): def __init__(self): self.program_seed = Program(vert_seed, frag_seed) self.program_flood = Program(vert, frag_flood) self.program_insert = Program(vert, frag_insert) self.programs = [self.program_seed, self.program_flood, self.program_insert] # Initialize variables self.fbo_to = [FrameBuffer(), FrameBuffer(), FrameBuffer()] vtype = np.dtype([('a_position', 'f4', 2), ('a_texcoord', 'f4', 2)]) vertices = np.zeros(4, dtype=vtype) vertices['a_position'] = [[-1., -1.], [-1., 1.], [1., -1.], [1., 1.]] vertices['a_texcoord'] = [[0., 0.], [0., 1.], [1., 0.], [1., 1.]] vertices = VertexBuffer(vertices) self.program_insert['u_step'] = 1. for program in self.programs: program.bind(vertices) def render_to_texture(self, data, texture, offset, size): """Render a SDF to a texture at a given offset and size Parameters ---------- data : array Must be 2D with type np.ubyte. texture : instance of Texture2D The texture to render to. offset : tuple of int Offset (x, y) to render to inside the texture. size : tuple of int Size (w, h) to render inside the texture. """ assert isinstance(texture, Texture2D) set_state(blend=False, depth_test=False) # calculate the negative half (within object) orig_tex = Texture2D(255 - data, format='luminance', wrapping='clamp_to_edge', interpolation='nearest') edf_neg_tex = self._render_edf(orig_tex) # calculate positive half (outside object) orig_tex[:, :, 0] = data edf_pos_tex = self._render_edf(orig_tex) # render final product to output texture self.program_insert['u_texture'] = orig_tex self.program_insert['u_pos_texture'] = edf_pos_tex self.program_insert['u_neg_texture'] = edf_neg_tex self.fbo_to[-1].color_buffer = texture with self.fbo_to[-1]: set_viewport(tuple(offset) + tuple(size)) self.program_insert.draw('triangle_strip') def _render_edf(self, orig_tex): """Render an EDF to a texture""" # Set up the necessary textures sdf_size = orig_tex.shape[:2] comp_texs = [] for _ in range(2): tex = Texture2D(sdf_size + (4,), format='rgba', interpolation='nearest', wrapping='clamp_to_edge') comp_texs.append(tex) self.fbo_to[0].color_buffer = comp_texs[0] self.fbo_to[1].color_buffer = comp_texs[1] for program in self.programs[1:]: # program_seed does not need this program['u_texh'], program['u_texw'] = sdf_size # Do the rendering last_rend = 0 with self.fbo_to[last_rend]: set_viewport(0, 0, sdf_size[1], sdf_size[0]) self.program_seed['u_texture'] = orig_tex self.program_seed.draw('triangle_strip') stepsize = (np.array(sdf_size) // 2).max() while stepsize > 0: self.program_flood['u_step'] = stepsize self.program_flood['u_texture'] = comp_texs[last_rend] last_rend = 1 if last_rend == 0 else 0 with self.fbo_to[last_rend]: set_viewport(0, 0, sdf_size[1], sdf_size[0]) self.program_flood.draw('triangle_strip') stepsize //= 2 return comp_texs[last_rend] vispy-0.4.0/vispy/visuals/markers.py0000664000175000017500000005046312527672621021263 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Marker Visual and shader definitions. """ import numpy as np from ..color import ColorArray from ..gloo import VertexBuffer, _check_valid from .shaders import ModularProgram, Function, Variable from .visual import Visual vert = """ uniform mat4 u_projection; uniform float u_antialias; uniform int u_px_scale; attribute vec3 a_position; attribute vec4 a_fg_color; attribute vec4 a_bg_color; attribute float a_edgewidth; attribute float a_size; varying vec4 v_fg_color; varying vec4 v_bg_color; varying float v_edgewidth; varying float v_antialias; void main (void) { $v_size = a_size * u_px_scale; v_edgewidth = a_edgewidth; v_antialias = u_antialias; v_fg_color = a_fg_color; v_bg_color = a_bg_color; gl_Position = $transform(vec4(a_position,1.0)); float edgewidth = max(v_edgewidth, 1.0); gl_PointSize = $scalarsize($v_size) + 4*(edgewidth + 1.5*v_antialias); } """ frag = """ varying vec4 v_fg_color; varying vec4 v_bg_color; varying float v_edgewidth; varying float v_antialias; void main() { float edgewidth = max(v_edgewidth, 1.0); float edgealphafactor = min(v_edgewidth, 1.0); float size = $scalarsize($v_size) + 4*(edgewidth + 1.5*v_antialias); // factor 6 for acute edge angles that need room as for star marker // The marker function needs to be linked with this shader float r = $marker(gl_PointCoord, size); // it takes into account an antialising layer // of size v_antialias inside the edge // r: // [-e/2-a, -e/2+a] antialising face-edge // [-e/2+a, e/2-a] core edge (center 0, diameter e-2a = 2t) // [e/2-a, e/2+a] antialising edge-background float t = 0.5*v_edgewidth - v_antialias; float d = abs(r) - t; vec4 edgecolor = vec4(v_fg_color.rgb, edgealphafactor*v_fg_color.a); if (r > 0.5*v_edgewidth + v_antialias) { // out of the marker (beyond the outer edge of the edge // including transition zone due to antialiasing) discard; } else if (d < 0.0) { // inside the width of the edge // (core, out of the transition zone for antialiasing) gl_FragColor = edgecolor; } else { if (v_edgewidth == 0.) {// no edge if (r > -v_antialias) { float alpha = 1.0 + r/v_antialias; alpha = exp(-alpha*alpha); gl_FragColor = vec4(v_bg_color.rgb, alpha*v_bg_color.a); } else { gl_FragColor = v_bg_color; } } else { float alpha = d/v_antialias; alpha = exp(-alpha*alpha); if (r > 0) { // outer part of the edge: fade out into the background... gl_FragColor = vec4(edgecolor.rgb, alpha*edgecolor.a); } else { gl_FragColor = mix(v_bg_color, edgecolor, alpha); } } } } """ size1d = """ float size1d(float size) { return size; } """ size2d = """ float size2d(vec2 size) { return max(size.x, size.y); } """ disc = """ float disc(vec2 pointcoord, float size) { float r = length((pointcoord.xy - vec2(0.5,0.5))*size); r -= $v_size/2; return r; } """ arrow = """ const float sqrt2 = sqrt(2.); float rect(vec2 pointcoord, float size) { float half_size = $v_size/2.; float ady = abs(pointcoord.y -.5)*size; float dx = (pointcoord.x -.5)*size; float r1 = abs(dx) + ady - half_size; float r2 = dx + 0.25*$v_size + ady - half_size; float r = max(r1,-r2); return r/sqrt2;//account for slanted edge and correct for width } """ ring = """ float ring(vec2 pointcoord, float size) { float r1 = length((pointcoord.xy - vec2(0.5,0.5))*size) - $v_size/2; float r2 = length((pointcoord.xy - vec2(0.5,0.5))*size) - $v_size/4; float r = max(r1,-r2); return r; } """ clobber = """ const float sqrt3 = sqrt(3.); float clobber(vec2 pointcoord, float size) { const float PI = 3.14159265358979323846264; const float t1 = -PI/2; float circle_radius = 0.32 * $v_size; float center_shift = 0.36/sqrt3 * $v_size; //total size (horizontal) = 2*circle_radius + sqrt3*center_shirt = $v_size vec2 c1 = vec2(cos(t1),sin(t1))*center_shift; const float t2 = t1+2*PI/3; vec2 c2 = vec2(cos(t2),sin(t2))*center_shift; const float t3 = t2+2*PI/3; vec2 c3 = vec2(cos(t3),sin(t3))*center_shift; //xy is shift to center marker vertically vec2 xy = (pointcoord.xy-vec2(0.5,0.5))*size + vec2(0.,-0.25*center_shift); float r1 = length(xy - c1) - circle_radius; float r2 = length(xy - c2) - circle_radius; float r3 = length(xy - c3) - circle_radius; float r = min(min(r1,r2),r3); return r; } """ square = """ float square(vec2 pointcoord, float size) { float r = max(abs(pointcoord.x -.5)*size, abs(pointcoord.y -.5)*size); r -= $v_size/2; return r; } """ x_ = """ float x_(vec2 pointcoord, float size) { vec2 rotcoord = vec2((pointcoord.x + pointcoord.y - 1.) / sqrt(2.), (pointcoord.y - pointcoord.x) / sqrt(2.)); //vbar float r1 = abs(rotcoord.x)*size - $v_size/6; float r2 = abs(rotcoord.y)*size - $v_size/2; float vbar = max(r1,r2); //hbar float r3 = abs(rotcoord.y)*size - $v_size/6; float r4 = abs(rotcoord.x)*size - $v_size/2; float hbar = max(r3,r4); return min(vbar, hbar); } """ diamond = """ float diamond(vec2 pointcoord, float size) { float r = abs(pointcoord.x -.5)*size + abs(pointcoord.y -.5)*size; r -= $v_size/2; return r / sqrt(2.);//account for slanted edge and correct for width } """ vbar = """ float vbar(vec2 pointcoord, float size) { float r1 = abs(pointcoord.x - 0.5)*size - $v_size/6; float r3 = abs(pointcoord.y - 0.5)*size - $v_size/2; float r = max(r1,r3); return r; } """ hbar = """ float rect(vec2 pointcoord, float size) { float r2 = abs(pointcoord.y - 0.5)*size - $v_size/6; float r3 = abs(pointcoord.x - 0.5)*size - $v_size/2; float r = max(r2,r3); return r; } """ cross = """ float cross(vec2 pointcoord, float size) { //vbar float r1 = abs(pointcoord.x - 0.5)*size - $v_size/6; float r2 = abs(pointcoord.y - 0.5)*size - $v_size/2; float vbar = max(r1,r2); //hbar float r3 = abs(pointcoord.y - 0.5)*size - $v_size/6; float r4 = abs(pointcoord.x - 0.5)*size - $v_size/2; float hbar = max(r3,r4); return min(vbar, hbar); } """ tailed_arrow = """ const float sqrt2 = sqrt(2.); float rect(vec2 pointcoord, float size) { float half_size = $v_size/2.; float ady = abs(pointcoord.y -.5)*size; float dx = (pointcoord.x -.5)*size; float r1 = abs(dx) + ady - half_size; float r2 = dx + 0.25*$v_size + ady - half_size; float arrow = max(r1,-r2); //hbar float upper_bottom_edges = ady - $v_size/8./sqrt2; float left_edge = -dx - half_size; float right_edge = dx + ady - half_size; float hbar = max(upper_bottom_edges, left_edge); float scale = 1.; //rescaling for slanted edge if (right_edge >= hbar) { hbar = right_edge; scale = sqrt2; } if (arrow <= hbar) { return arrow / sqrt2;//account for slanted edge and correct for width } else { return hbar / scale; } } """ triangle_up = """ float rect(vec2 pointcoord, float size) { float height = $v_size*sqrt(3.)/2.; float bottom = ((pointcoord.y - 0.5)*size - height/2.); float rotated_y = sqrt(3.)/2. * (pointcoord.x - 0.5) * size - 0.5 * ((pointcoord.y - 0.5)*size - height/6.) + height/6.; float right_edge = (rotated_y - height/2.); float cc_rotated_y = -sqrt(3.)/2. * (pointcoord.x - 0.5)*size - 0.5 * ((pointcoord.y - 0.5)*size - height/6.) + height/6.; float left_edge = (cc_rotated_y - height/2.); float slanted_edges = max(right_edge, left_edge); return max(slanted_edges, bottom); } """ triangle_down = """ float rect(vec2 pointcoord, float size) { float height = -$v_size*sqrt(3.)/2.; float bottom = -((pointcoord.y - 0.5)*size - height/2.); float rotated_y = sqrt(3.)/2. * (pointcoord.x - 0.5) * size - 0.5 * ((pointcoord.y - 0.5)*size - height/6.) + height/6.; float right_edge = -(rotated_y - height/2.); float cc_rotated_y = -sqrt(3.)/2. * (pointcoord.x - 0.5)*size - 0.5 * ((pointcoord.y - 0.5)*size - height/6.) + height/6.; float left_edge = -(cc_rotated_y - height/2.); float slanted_edges = max(right_edge, left_edge); return max(slanted_edges, bottom); } """ star = """ float rect(vec2 pointcoord, float size) { float star = -10000.; const float PI2_5 = 3.141592653589*2./5.; const float PI2_20 = 3.141592653589/10.; //PI*2/20 // downwards shift to that the marker center is halfway vertically // between the top of the upward spike (y = -v_size/2) // and the bottom of one of two downward spikes // (y = +v_size/2*cos(2*pi/10) approx +v_size/2*0.8) // center is at -v_size/2*0.1 float shift_y = -0.05*$v_size; // first spike upwards, // rotate spike by 72 deg four times to complete the star for (int i = 0; i <= 4; i++) { //if not the first spike, rotate it upwards float x = (pointcoord.x - 0.5)*size; float y = (pointcoord.y - 0.5)*size; float spike_rot_angle = i*PI2_5; float cosangle = cos(spike_rot_angle); float sinangle = sin(spike_rot_angle); float spike_x = x; float spike_y = y + shift_y; if (i > 0) { spike_x = cosangle * x - sinangle * (y + shift_y); spike_y = sinangle * x + cosangle * (y + shift_y); } // in the frame where the spike is upwards: // rotate 18 deg the zone x < 0 around the top of the star // (point whose coords are -s/2, 0 where s is the size of the marker) // compute y coordonates as well because // we do a second rotation to put the spike at its final position float rot_center_y = -$v_size/2; float rot18x = cos(PI2_20) * spike_x - sin(PI2_20) * (spike_y - rot_center_y); //rotate -18 deg the zone x > 0 arount the top of the star float rot_18x = cos(PI2_20) * spike_x + sin(PI2_20) * (spike_y - rot_center_y); float bottom = spike_y - $v_size/10.; // max(left edge, right edge) float spike = max(bottom, max(rot18x, -rot_18x)); if (i == 0) {// first spike, skip the rotation star = spike; } else // i > 0 { star = min(star, spike); } } return star; } """ # the following two markers needs x and y sizes rect = """ float rect(vec2 pointcoord, float size) { float x_boundaries = abs(pointcoord.x - 0.5)*size - $v_size.x/2.; float y_boundaries = abs(pointcoord.y - 0.5)*size - $v_size.y/2.; return max(x_boundaries, y_boundaries); } """ ellipse = """ float rect(vec2 pointcoord, float size) { float x = (pointcoord.x - 0.5)*size; float y = (pointcoord.y - 0.5)*size; // normalise radial distance (for edge and antialising to remain isotropic) // Scaling factor is the norm of the gradient of the function defining // the surface taken at a well chosen point on the edge of the ellipse // f(x, y) = (sqrt(x^2/a^2 + y^2/b^2) = 0.5 in this case // where a = v_size.x and b = v_size.y) // The well chosen point on the edge of the ellipse should be the point // whose normal points towards the current point. // Below we choose a different point whose computation // is simple enough to fit here. float f = length(vec2(x / $v_size.x, y / $v_size.y)); // We set the default value of the norm so that // - near the axes (x=0 or y=0 +/- 1 pixel), the norm is correct // (the computation below is unstable near the axes) // - if the ellipse is a circle, the norm is correct // - if we are deep in the interior of the ellipse the norm // is set to an arbitrary value (but is not used) float norm = abs(x) < 1. ? 1./$v_size.y : 1./$v_size.x; if (f > 1e-3 && abs($v_size.x - $v_size.y) > 1e-3 && abs(x) > 1. && abs(y) > 1.) { // Find the point x0, y0 on the ellipse which has the same hyperbola // coordinate in the elliptic coordinate system linked to the ellipse // (finding the right 'well chosen' point is too complicated) // Visually it's nice, even at high eccentricities, where // the approximation is visible but not ugly. float a = $v_size.x/2.; float b = $v_size.y/2.; float C = max(a, b); float c = min(a, b); float focal_length = sqrt(C*C - c*c); float fl2 = focal_length*focal_length; float x2 = x*x; float y2 = y*y; float tmp = fl2 + x2 + y2; float x0 = 0; float y0 = 0; if ($v_size.x > $v_size.y) { float cos2v = 0.5 * (tmp - sqrt(tmp*tmp - 4.*fl2*x2)) / fl2; cos2v = fract(cos2v); x0 = a * sqrt(cos2v); // v_size.x = focal_length*cosh m where m is the ellipse coordinate y0 = b * sqrt(1-cos2v); // v_size.y = focal_length*sinh m } else // $v_size.x < $v_size.y {//exchange x and y axis for elliptic coordinate float cos2v = 0.5 * (tmp - sqrt(tmp*tmp - 4.*fl2*y2)) / fl2; cos2v = fract(cos2v); x0 = a * sqrt(1-cos2v); // v_size.x = focal_length*sinh m where m is the ellipse coordinate y0 = b * sqrt(cos2v); // v_size.y = focal_length*cosh m } vec2 normal = vec2(2.*x0/v_size.x/v_size.x, 2.*y0/v_size.y/v_size.y); norm = length(normal); } return (f - 0.5) / norm; } """ _marker_dict = { 'disc': disc, 'arrow': arrow, 'ring': ring, 'clobber': clobber, 'square': square, 'diamond': diamond, 'vbar': vbar, 'hbar': hbar, 'cross': cross, 'tailed_arrow': tailed_arrow, 'x': x_, 'triangle_up': triangle_up, 'triangle_down': triangle_down, 'star': star, # aliases 'o': disc, '+': cross, 's': square, '-': hbar, '|': vbar, '->': tailed_arrow, '>': arrow, '^': triangle_up, 'v': triangle_down, '*': star } marker_types = tuple(sorted(list(_marker_dict.keys()))) class MarkersVisual(Visual): """ Visual displaying marker symbols. """ def __init__(self): self._program = ModularProgram(vert, frag) self._v_size_var = Variable('varying float v_size') self._program.vert['v_size'] = self._v_size_var self._program.frag['v_size'] = self._v_size_var self._program.vert['scalarsize'] = Function(size1d) self._program.frag['scalarsize'] = Function(size1d) Visual.__init__(self) self.set_gl_state(depth_test=False, blend=True, blend_func=('src_alpha', 'one_minus_src_alpha')) def set_data(self, pos=None, symbol='o', size=10., edge_width=1., edge_width_rel=None, edge_color='black', face_color='white', scaling=False): """ Set the data used to display this visual. Parameters ---------- pos : array The array of locations to display each symbol. symbol : str The style of symbol to draw (see Notes). size : float or array The symbol size in px. edge_width : float | None The width of the symbol outline in pixels. edge_width_rel : float | None The width as a fraction of marker size. Exactly one of `edge_width` and `edge_width_rel` must be supplied. edge_color : Color | ColorArray The color used to draw each symbol outline. face_color : Color | ColorArray The color used to draw each symbol interior. scaling : bool If set to True, marker scales when rezooming. Notes ----- Allowed style strings are: disc, arrow, ring, clobber, square, diamond, vbar, hbar, cross, tailed_arrow, x, triangle_up, triangle_down, and star. """ assert (isinstance(pos, np.ndarray) and pos.ndim == 2 and pos.shape[1] in (2, 3)) if (edge_width is not None) + (edge_width_rel is not None) != 1: raise ValueError('exactly one of edge_width and edge_width_rel ' 'must be non-None') if edge_width is not None: if edge_width < 0: raise ValueError('edge_width cannot be negative') else: if edge_width_rel < 0: raise ValueError('edge_width_rel cannot be negative') self.set_symbol(symbol) self.scaling = scaling edge_color = ColorArray(edge_color).rgba if len(edge_color) == 1: edge_color = edge_color[0] face_color = ColorArray(face_color).rgba if len(face_color) == 1: face_color = face_color[0] n = len(pos) data = np.zeros(n, dtype=[('a_position', np.float32, 3), ('a_fg_color', np.float32, 4), ('a_bg_color', np.float32, 4), ('a_size', np.float32, 1), ('a_edgewidth', np.float32, 1)]) data['a_fg_color'] = edge_color data['a_bg_color'] = face_color if edge_width is not None: data['a_edgewidth'] = edge_width else: data['a_edgewidth'] = size*edge_width_rel data['a_position'][:, :pos.shape[1]] = pos data['a_size'] = size self.antialias = 1. self._data = data self._vbo = VertexBuffer(data) self.update() def set_symbol(self, symbol='o'): """Set the symbol Parameters ---------- symbol : str The symbol. """ _check_valid('symbol', symbol, marker_types) self._marker_fun = Function(_marker_dict[symbol]) self._marker_fun['v_size'] = self._v_size_var self._program.frag['marker'] = self._marker_fun def draw(self, transforms): """Draw the visual Parameters ---------- transforms : instance of TransformSystem The transforms to use. """ Visual.draw(self, transforms) xform = transforms.get_full_transform() self._program.vert['transform'] = xform # TO DO: find a way to avoid copying data and rebinding them to the vbo if self.scaling: update_data = self._data.copy() # 0.5 factor due to the difference between the viewbox spanning # [-1, 1] in the framebuffer coordinates intervals # and the viewbox spanning [0, 1] intervals # in the Visual coordinates # TO DO: find a way to get the scale directly scale = ( 0.5*transforms.visual_to_document.simplified().scale[:2] * transforms.document_to_framebuffer.simplified().scale[:2] * transforms.framebuffer_to_render.simplified().scale[:2] ) update_data['a_size'] *= min(scale) update_data['a_edgewidth'] *= min(scale) self._vbo.set_data(update_data) self._program.prepare() self._program['u_antialias'] = self.antialias d2f = transforms.document_to_framebuffer self._program['u_px_scale'] = (d2f.map((1, 0)) - d2f.map((0, 0)))[0] self._program.bind(self._vbo) self._program.draw('points') def bounds(self, mode, axis): """Get the bounds Parameters ---------- mode : str Describes the type of boundary requested. Can be "visual", "data", or "mouse". axis : 0, 1, 2 The axis along which to measure the bounding values, in x-y-z order. """ pos = self._data['a_position'] if pos is None: return None if pos.shape[1] > axis: return (pos[:, axis].min(), pos[:, axis].max()) else: return (0, 0) vispy-0.4.0/vispy/visuals/rectangle.py0000664000175000017500000001354012527672621021556 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copradiusight (c) 2014, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Simple ellipse visual based on PolygonVisual """ from __future__ import division import numpy as np from ..color import Color from .polygon import PolygonVisual class RectangleVisual(PolygonVisual): """ Displays a 2D rectangle with optional rounded corners Parameters ---------- pos : array Center of the rectangle color : instance of Color The fill color to use. border_color : instance of Color The border color to use. height : float Length of the rectangle along y-axis Defaults to 1.0 width : float Length of the rectangle along x-axis Defaults to 1.0 radius : float | array Radii of curvatures of corners in clockwise order from top-left Defaults to 0. """ def __init__(self, pos=None, color='black', border_color=None, height=1.0, width=1.0, radius=[0., 0., 0., 0.], **kwargs): super(RectangleVisual, self).__init__() self.mesh.mode = 'triangle_fan' self._vertices = None self._pos = pos self._color = Color(color) self._border_color = Color(border_color) self._height = height self._width = width self.radius = radius self._update() def _generate_vertices(self, pos, radius, height, width): half_height = self._height / 2. half_width = self._width / 2. hw = min(half_height, half_width) num_segments = (radius / hw * 500.).astype(int) bias1 = np.full(4, half_width) - radius bias2 = np.full(4, half_height) - radius corner1 = np.empty([num_segments[0]+1, 3], dtype=np.float32) corner2 = np.empty([num_segments[1]+1, 3], dtype=np.float32) corner3 = np.empty([num_segments[2]+1, 3], dtype=np.float32) corner4 = np.empty([num_segments[3]+1, 3], dtype=np.float32) start_angle = 0. end_angle = np.pi / 2. theta = np.linspace(end_angle, start_angle, num_segments[0]+1) corner1[:, 0] = pos[0] - bias1[0] - radius[0] * np.sin(theta) corner1[:, 1] = pos[1] - bias2[0] - radius[0] * np.cos(theta) corner1[:, 2] = 0 theta = np.linspace(start_angle, end_angle, num_segments[1]+1) corner2[:, 0] = pos[0] + bias1[1] + radius[1] * np.sin(theta) corner2[:, 1] = pos[1] - bias2[1] - radius[1] * np.cos(theta) corner2[:, 2] = 0 theta = np.linspace(end_angle, start_angle, num_segments[2]+1) corner3[:, 0] = pos[0] + bias1[2] + radius[2] * np.sin(theta) corner3[:, 1] = pos[1] + bias2[2] + radius[2] * np.cos(theta) corner3[:, 2] = 0 theta = np.linspace(start_angle, end_angle, num_segments[3]+1) corner4[:, 0] = pos[0] - bias1[3] - radius[3] * np.sin(theta) corner4[:, 1] = pos[1] + bias2[3] + radius[3] * np.cos(theta) corner4[:, 2] = 0 output = np.concatenate(([[pos[0], pos[1], 0.]], [[pos[0] - half_width, pos[1], 0.]], corner1, [[pos[0], pos[1] - half_height, 0.]], corner2, [[pos[0] + half_width, pos[1], 0.]], corner3, [[pos[0], pos[1] + half_height, 0.]], corner4, [[pos[0] - half_width, pos[1], 0.]])) self._vertices = np.array(output, dtype=np.float32) @property def height(self): """ The height of the rectangle. """ return self._height @height.setter def height(self, height): if height <= 0.: raise ValueError('Height must be positive') self._height = height self._update() @property def width(self): """ The width of the rectangle. """ return self._width @width.setter def width(self, width): if width <= 0.: raise ValueError('Width must be positive') self._width = width self._update() @property def radius(self): """ The radius of curvature of rounded corners. """ return self._radius @radius.setter def radius(self, radius): half_height = self._height / 2. half_width = self._width / 2. hw = min(half_height, half_width) if isinstance(radius, (list, tuple)): if len(radius) != 4: raise ValueError("radius must be float or 4 value tuple/list" " (got %s of length %d)" % (type(radius), len(radius))) if (radius > np.full(4, hw)).all(): raise ValueError('Radius of curvature cannot be greater than\ half of min(width, height)') radius = np.array(radius, dtype=np.float32) else: if radius > hw: raise ValueError('Radius of curvature cannot be greater than\ half of min(width, height)') radius = np.full(4, radius) self._radius = radius self._update() def _update(self): if self._pos is None: return self._generate_vertices(pos=self._pos, radius=self._radius, height=self._height, width=self._width) if not self._color.is_blank: self.mesh.set_data(vertices=self._vertices, color=self._color.rgba) if not self._border_color.is_blank: self.border.set_data(pos=self._vertices[1:, ..., :2], color=self._border_color.rgba) self.update() vispy-0.4.0/vispy/visuals/tube.py0000664000175000017500000001320112527672621020543 0ustar larsonerlarsoner00000000000000from __future__ import division from .mesh import MeshVisual import numpy as np from numpy.linalg import norm from ..util.transforms import rotate from ..color import ColorArray class TubeVisual(MeshVisual): """Displays a tube around a piecewise-linear path. The tube mesh is corrected following its Frenet curvature and torsion such that it varies smoothly along the curve, including if the tube is closed. Parameters ---------- points : ndarray An array of (x, y, z) points describing the path along which the tube will be extruded. radius : float The radius of the tube. Defaults to 1.0. closed : bool Whether the tube should be closed, joining the last point to the first. Defaults to False. color : Color | ColorArray The color(s) to use when drawing the tube. The same color is applied to each vertex of the mesh surrounding each point of the line. If the input is a ColorArray, the argument will be cycled; for instance if 'red' is passed then the entire tube will be red, or if ['green', 'blue'] is passed then the points will alternate between these colours. Defaults to 'purple'. tube_points : int The number of points in the circle-approximating polygon of the tube's cross section. Defaults to 8. shading : str | None Same as for the `MeshVisual` class. Defaults to 'smooth'. vertex_colors: ndarray | None Same as for the `MeshVisual` class. face_colors: ndarray | None Same as for the `MeshVisual` class. mode : str Same as for the `MeshVisual` class. Defaults to 'triangles'. """ def __init__(self, points, radius=1.0, closed=False, color='purple', tube_points=8, shading='smooth', vertex_colors=None, face_colors=None, mode='triangles'): points = np.array(points) tangents, normals, binormals = _frenet_frames(points, closed) segments = len(points) - 1 # get the positions of each vertex grid = np.zeros((len(points), tube_points, 3)) for i in range(len(points)): pos = points[i] normal = normals[i] binormal = binormals[i] # Add a vertex for each point on the circle v = np.arange(tube_points, dtype=np.float) / tube_points * 2 * np.pi cx = -1. * radius * np.cos(v) cy = radius * np.sin(v) grid[i] = (pos + cx[:, np.newaxis]*normal + cy[:, np.newaxis]*binormal) # construct the mesh indices = [] for i in range(segments): for j in range(tube_points): ip = (i+1) % segments if closed else i+1 jp = (j+1) % tube_points index_a = i*tube_points + j index_b = ip*tube_points + j index_c = ip*tube_points + jp index_d = i*tube_points + jp indices.append([index_a, index_b, index_d]) indices.append([index_b, index_c, index_d]) vertices = grid.reshape(grid.shape[0]*grid.shape[1], 3) color = ColorArray(color) if vertex_colors is None: point_colors = np.resize(color.rgba, (len(points), 4)) vertex_colors = np.repeat(point_colors, tube_points, axis=0) indices = np.array(indices, dtype=np.uint32) MeshVisual.__init__(self, vertices, indices, vertex_colors=vertex_colors, face_colors=face_colors, shading=shading, mode=mode) def draw(self, transforms): """Draw the visual Parameters ---------- transforms : instance of TransformSystem The transforms to use. """ MeshVisual.draw(self, transforms) def _frenet_frames(points, closed): '''Calculates and returns the tangents, normals and binormals for the tube.''' tangents = np.zeros((len(points), 3)) normals = np.zeros((len(points), 3)) epsilon = 0.0001 # Compute tangent vectors for each segment tangents = np.roll(points, -1, axis=0) - np.roll(points, 1, axis=0) if not closed: tangents[0] = points[1] - points[0] tangents[-1] = points[-1] - points[-2] mags = np.sqrt(np.sum(tangents * tangents, axis=1)) tangents /= mags[:, np.newaxis] # Get initial normal and binormal t = np.abs(tangents[0]) smallest = np.argmin(t) normal = np.zeros(3) normal[smallest] = 1. vec = np.cross(tangents[0], normal) normals[0] = np.cross(tangents[0], vec) # Compute normal and binormal vectors along the path for i in range(1, len(points)): normals[i] = normals[i-1] vec = np.cross(tangents[i-1], tangents[i]) if norm(vec) > epsilon: vec /= norm(vec) theta = np.arccos(np.clip(tangents[i-1].dot(tangents[i]), -1, 1)) normals[i] = rotate(-np.degrees(theta), vec)[:3, :3].dot(normals[i]) if closed: theta = np.arccos(np.clip(normals[0].dot(normals[-1]), -1, 1)) theta /= len(points) - 1 if tangents[0].dot(np.cross(normals[0], normals[-1])) > 0: theta *= -1. for i in range(1, len(points)): normals[i] = rotate(-np.degrees(theta*i), tangents[i])[:3, :3].dot(normals[i]) binormals = np.cross(tangents, normals) return tangents, normals, binormals vispy-0.4.0/vispy/visuals/line/0000775000175000017500000000000012527674621020166 5ustar larsonerlarsoner00000000000000vispy-0.4.0/vispy/visuals/line/fragment.py0000664000175000017500000002326512426461252022343 0ustar larsonerlarsoner00000000000000FRAGMENT_SHADER = """ const float PI = 3.14159265358979323846264; const float THETA = 15.0 * 3.14159265358979323846264/180.0; float cap( int type, float dx, float dy, float t ) { float d = 0.0; dx = abs(dx); dy = abs(dy); // None if (type == 0) discard; // Round else if (type == 1) d = sqrt(dx*dx+dy*dy); // Triangle in else if (type == 3) d = (dx+abs(dy)); // Triangle out else if (type == 2) d = max(abs(dy),(t+dx-abs(dy))); // Square else if (type == 4) d = max(dx,dy); // Butt else if (type == 5) d = max(dx+t,dy); return d; } float join( in int type, in float d, in vec2 segment, in vec2 texcoord, in vec2 miter, in float miter_limit, in float linewidth ) { float dx = texcoord.x; // Round join // -------------------------------- if( type == 1 ) { if (dx < segment.x) { d = max(d,length( texcoord - vec2(segment.x,0.0))); //d = length( texcoord - vec2(segment.x,0.0)); } else if (dx > segment.y) { d = max(d,length( texcoord - vec2(segment.y,0.0))); //d = length( texcoord - vec2(segment.y,0.0)); } } // Bevel join // -------------------------------- else if ( type == 2 ) { if( (dx < segment.x) || (dx > segment.y) ) d = max(d, min(abs(miter.x),abs(miter.y))); } // Miter limit // -------------------------------- if( (dx < segment.x) || (dx > segment.y) ) { d = max(d, min(abs(miter.x), abs(miter.y)) - miter_limit*linewidth/2.0 ); } return d; } // Uniforms uniform sampler2D u_dash_atlas; // Varying varying vec4 v_color; varying vec2 v_segment; varying vec2 v_angles; varying vec2 v_linecaps; varying vec2 v_texcoord; varying vec2 v_miter; varying float v_miter_limit; varying float v_length; varying float v_linejoin; varying float v_linewidth; varying float v_antialias; varying float v_dash_phase; varying float v_dash_period; varying float v_dash_index; varying vec2 v_dash_caps; varying float v_closed; void main() { // gl_FragColor = v_color; return; // vec4 color = v_color; // If color is fully transparent we just discard the fragment if( v_color.a <= 0.0 ) { discard; } // Test if dash pattern is the solid one (0) bool solid = (v_dash_index == 0.0); float dx = v_texcoord.x; float dy = v_texcoord.y; float t = v_linewidth/2.0-v_antialias; float width = v_linewidth; float d = 0.0; vec2 linecaps = v_linecaps; vec2 dash_caps = v_dash_caps; float line_start = 0.0; float line_stop = v_length; // Test if path is closed bool closed = (v_closed > 0.0); // ------------------------------------------------------------------------ // Solid line // ------------------------------------------------------------------------ if( solid ) { d = abs(dy); if( (!closed) && (dx < line_start) ) { d = cap( int(v_linecaps.x), abs(dx), abs(dy), t ); } else if( (!closed) && (dx > line_stop) ) { d = cap( int(v_linecaps.y), abs(dx)-line_stop, abs(dy), t ); } else { d = join( int(v_linejoin), abs(dy), v_segment, v_texcoord, v_miter, v_miter_limit, v_linewidth ); } // ------------------------------------------------------------------------ // Dash line // ------------------------------------------------------------------------ } else { float segment_start = v_segment.x; float segment_stop = v_segment.y; float segment_center = (segment_start+segment_stop)/2.0; float freq = v_dash_period*width; float u = mod( dx + v_dash_phase*width,freq ); vec4 tex = texture2D(u_dash_atlas, vec2(u/freq, v_dash_index)); float dash_center= tex.x * width; float dash_type = tex.y; float _start = tex.z * width; float _stop = tex.a * width; float dash_start = dx - u + _start; float dash_stop = dx - u + _stop; // This test if the we are dealing with a discontinuous angle bool discont = ((dx < segment_center) && abs(v_angles.x) > THETA) || ((dx >= segment_center) && abs(v_angles.y) > THETA); if( dx < line_start) discont = false; if( dx > line_stop) discont = false; // When path is closed, we do not have room for linecaps, so we make // room by shortening the total length if (closed){ line_start += v_linewidth/2.0; line_stop -= v_linewidth/2.0; linecaps = v_dash_caps; } // Check is dash stop is before line start if( dash_stop <= line_start ) { discard; } // Check is dash start is beyond line stop if( dash_start >= line_stop ) { discard; } // Check if current pattern start is beyond segment stop if( discont ) { // Dash start is beyond segment, we discard if( dash_start > segment_stop ) { discard; } // Dash stop is before segment, we discard if( dash_stop < segment_start ) { discard; } // Special case for round caps (nicer with this) if( (u > _stop) && (dash_stop > segment_stop ) && (abs(v_angles.y) < PI/2.0)) { if( dash_caps.x == 1.0) discard; } // Special case for round caps (nicer with this) else if( (u < _start) && (dash_start < segment_start ) && (abs(v_angles.x) < PI/2.0)) { if( dash_caps.y == 1.0) discard; } // Special case for triangle caps (in & out) and square // We make sure the cap stop at crossing frontier if( (dash_caps.x != 1.0) && (dash_caps.x != 5.0) ) { if( (dash_start < segment_start ) && (abs(v_angles.x) < PI/2.0) ) { float a = v_angles.x/2.0; float x = (segment_start-dx)*cos(a) - dy*sin(a); float y = (segment_start-dx)*sin(a) + dy*cos(a); if( (x > 0.0) ) discard; // We transform the cap into square to avoid holes dash_caps.x = 4.0; } } // Special case for triangle caps (in & out) and square // We make sure the cap stop at crossing frontier if( (dash_caps.y != 1.0) && (dash_caps.y != 5.0) ) { if( (dash_stop > segment_stop ) && (abs(v_angles.y) < PI/2.0) ) { float a = v_angles.y/2.0; float x = (dx-segment_stop)*cos(a) - dy*sin(a); float y = (dx-segment_stop)*sin(a) + dy*cos(a); if( (x > 0.0) ) discard; // We transform the caps into square to avoid holes dash_caps.y = 4.0; } } } // Line cap at start if( (dx < line_start) && (dash_start < line_start) && (dash_stop > line_start) ) { d = cap( int(linecaps.x), dx-line_start, dy, t); } // Line cap at stop else if( (dx > line_stop) && (dash_stop > line_stop) && (dash_start < line_stop) ) { d = cap( int(linecaps.y), dx-line_stop, dy, t); } // Dash cap left else if( dash_type < 0.0 ) { float u = max( u-dash_center , 0.0 ); d = cap( int(dash_caps.y), abs(u), dy, t); } // Dash cap right else if( dash_type > 0.0 ) { float u = max( dash_center-u, 0.0 ); d = cap( int(dash_caps.x), abs(u), dy, t); } // Dash body (plain) else if( dash_type == 0.0 ) { d = abs(dy); } // Antialiasing at segment angles region if( discont ) { if( dx < segment_start ) { // For sharp angles, we do not enforce cap shape if( (dash_start < segment_start ) && (abs(v_angles.x) > PI/2.0)) { d = abs(dy); } // Antialias at outer border dx = segment_start - dx; float angle = PI/2.+v_angles.x; float f = abs( dx*cos(angle) - dy*sin(angle)); d = max(f,d); } else if( (dx > segment_stop) ) { // For sharp angles, we do not enforce cap shape if( (dash_stop > segment_stop ) && (abs(v_angles.y) > PI/2.0) ) { d = abs(dy); } // Antialias at outer border dx = dx - segment_stop; float angle = PI/2.+v_angles.y; float f = abs( dx*cos(angle) - dy*sin(angle)); d = max(f,d); } } // Line join //if( (dx > line_start) && (dx < line_stop) ) { d = join( int(v_linejoin), d, v_segment, v_texcoord, v_miter, v_miter_limit, v_linewidth ); } } // Distance to border // ------------------------------------------------------------------------ d = d - t; if( d < 0.0 ) { gl_FragColor = v_color; } else { d /= v_antialias; gl_FragColor = vec4(v_color.xyz, exp(-d*d)*v_color.a); } } """ vispy-0.4.0/vispy/visuals/line/line.py0000664000175000017500000004371612527672621021500 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Line visual implementing Agg- and GL-based drawing modes. """ from __future__ import division import numpy as np from ... import gloo from ...color import Color, ColorArray, get_colormap from ...ext.six import string_types from ..shaders import ModularProgram, Function from ..visual import Visual from ...util.profiler import Profiler from .dash_atlas import DashAtlas from . import vertex from . import fragment vec2to4 = Function(""" vec4 vec2to4(vec2 inp) { return vec4(inp, 0, 1); } """) vec3to4 = Function(""" vec4 vec3to4(vec3 inp) { return vec4(inp, 1); } """) """ TODO: * Agg support is very minimal; needs attention. * Optimization--avoid creating new buffers, avoid triggering program recompile. """ joins = {'miter': 0, 'round': 1, 'bevel': 2} caps = {'': 0, 'none': 0, '.': 0, 'round': 1, ')': 1, '(': 1, 'o': 1, 'triangle in': 2, '<': 2, 'triangle out': 3, '>': 3, 'square': 4, '=': 4, 'butt': 4, '|': 5} class LineVisual(Visual): """Line visual Parameters ---------- pos : array Array of shape (..., 2) or (..., 3) specifying vertex coordinates. color : Color, tuple, or array The color to use when drawing the line. If an array is given, it must be of shape (..., 4) and provide one rgba color per vertex. Can also be a colormap name, or appropriate `Function`. width: The width of the line in px. Line widths > 1px are only guaranteed to work when using 'agg' method. connect : str or array Determines which vertices are connected by lines. * "strip" causes the line to be drawn with each vertex connected to the next. * "segments" causes each pair of vertices to draw an independent line segment * numpy arrays specify the exact set of segment pairs to connect. method : str Mode to use for drawing. * "agg" uses anti-grain geometry to draw nicely antialiased lines with proper joins and endcaps. * "gl" uses OpenGL's built-in line rendering. This is much faster, but produces much lower-quality results and is not guaranteed to obey the requested line width or join/endcap styles. antialias : bool Enables or disables antialiasing. For method='gl', this specifies whether to use GL's line smoothing, which may be unavailable or inconsistent on some platforms. """ def __init__(self, pos=None, color=(0.5, 0.5, 0.5, 1), width=1, connect='strip', method='gl', antialias=False): Visual.__init__(self) self._changed = {'pos': False, 'color': False, 'width': False, 'connect': False} self._pos = None self._color = None self._width = None self._connect = None self._bounds = None # don't call subclass set_data; these often have different # signatures. LineVisual.set_data(self, pos=pos, color=color, width=width, connect=connect) self._method = 'none' self.antialias = antialias self.method = method @property def _program(self): return self._line_visual._program @property def antialias(self): return self._antialias @antialias.setter def antialias(self, aa): self._antialias = bool(aa) self.update() @property def method(self): """The current drawing method""" return self._method @method.setter def method(self, method): if method not in ('agg', 'gl'): raise ValueError('method argument must be "agg" or "gl".') if method == self._method: return self._method = method if method == 'gl': self._line_visual = _GLLineVisual(self) elif method == 'agg': self._line_visual = _AggLineVisual(self) for k in self._changed: self._changed[k] = True def set_data(self, pos=None, color=None, width=None, connect=None): """ Set the data used to draw this visual. Parameters ---------- pos : array Array of shape (..., 2) or (..., 3) specifying vertex coordinates. color : Color, tuple, or array The color to use when drawing the line. If an array is given, it must be of shape (..., 4) and provide one rgba color per vertex. width: The width of the line in px. Line widths > 1px are only guaranteed to work when using 'agg' method. connect : str or array Determines which vertices are connected by lines. * "strip" causes the line to be drawn with each vertex connected to the next. * "segments" causes each pair of vertices to draw an independent line segment * int numpy arrays specify the exact set of segment pairs to connect. * bool numpy arrays specify which _adjacent_ pairs to connect. """ if pos is not None: self._bounds = None self._pos = pos self._changed['pos'] = True if color is not None: self._color = color self._changed['color'] = True if width is not None: self._width = width self._changed['width'] = True if connect is not None: self._connect = connect self._changed['connect'] = True self.update() @property def color(self): return self._color @property def width(self): return self._width @property def connect(self): return self._connect @property def pos(self): return self._pos def _interpret_connect(self): if isinstance(self._connect, np.ndarray): # Convert a boolean connection array to a vertex index array if self._connect.ndim == 1 and self._connect.dtype == bool: index = np.empty((len(self._connect), 2), dtype=np.uint32) index[:] = np.arange(len(self._connect))[:, np.newaxis] index[:, 1] += 1 return index[self._connect] elif self._connect.ndim == 2 and self._connect.shape[1] == 2: return self._connect.astype(np.uint32) else: raise TypeError("Got invalid connect array of shape %r and " "dtype %r" % (self._connect.shape, self._connect.dtype)) else: return self._connect def _interpret_color(self): if isinstance(self._color, string_types): try: colormap = get_colormap(self._color) color = Function(colormap.glsl_map) except KeyError: color = Color(self._color).rgba elif isinstance(self._color, Function): color = Function(self._color) else: color = ColorArray(self._color).rgba if len(color) == 1: color = color[0] return color def bounds(self, mode, axis): """Get the bounds Parameters ---------- mode : str Describes the type of boundary requested. Can be "visual", "data", or "mouse". axis : 0, 1, 2 The axis along which to measure the bounding values, in x-y-z order. """ # Can and should we calculate bounds? if (self._bounds is None) and self._pos is not None: pos = self._pos self._bounds = [(pos[:, d].min(), pos[:, d].max()) for d in range(pos.shape[1])] # Return what we can if self._bounds is None: return else: if axis < len(self._bounds): return self._bounds[axis] else: return (0, 0) def draw(self, transforms): """Draw the visual Parameters ---------- transforms : instance of TransformSystem The transforms to use. """ if self.width == 0: return self._line_visual.draw(transforms) for k in self._changed: self._changed[k] = False def set_gl_state(self, **kwargs): Visual.set_gl_state(self, **kwargs) self._line_visual.set_gl_state(**kwargs) def update_gl_state(self, **kwargs): Visual.update_gl_state(self, **kwargs) self._line_visual.update_gl_state(**kwargs) class _GLLineVisual(Visual): VERTEX_SHADER = """ varying vec4 v_color; void main(void) { gl_Position = $transform($to_vec4($position)); v_color = $color; } """ FRAGMENT_SHADER = """ varying vec4 v_color; void main() { gl_FragColor = v_color; } """ def __init__(self, parent): self._parent = parent self._pos_vbo = gloo.VertexBuffer() self._color_vbo = gloo.VertexBuffer() self._connect_ibo = gloo.IndexBuffer() self._connect = None # Set up the GL program self._program = ModularProgram(self.VERTEX_SHADER, self.FRAGMENT_SHADER) self.set_gl_state('translucent') def draw(self, transforms): prof = Profiler() Visual.draw(self, transforms) # first see whether we can bail out early if self._parent._width <= 0: return if self._parent._changed['pos']: if self._parent._pos is None: return # todo: does this result in unnecessary copies? pos = np.ascontiguousarray(self._parent._pos.astype(np.float32)) self._pos_vbo.set_data(pos) self._program.vert['position'] = self._pos_vbo if pos.shape[-1] == 2: self._program.vert['to_vec4'] = vec2to4 elif pos.shape[-1] == 3: self._program.vert['to_vec4'] = vec3to4 else: raise TypeError("Got bad position array shape: %r" % (pos.shape,)) if self._parent._changed['color']: color = self._parent._interpret_color() # If color is not visible, just quit now if isinstance(color, Color) and color.is_blank: return if isinstance(color, Function): # TODO: Change to the parametric coordinate once that is done self._program.vert['color'] = color( '(gl_Position.x + 1.0) / 2.0') else: if color.ndim == 1: self._program.vert['color'] = color else: self._color_vbo.set_data(color) self._program.vert['color'] = self._color_vbo xform = transforms.get_full_transform() self._program.vert['transform'] = xform # Do we want to use OpenGL, and can we? GL = None try: import OpenGL.GL as GL except ImportError: pass # Turn on line smooth and/or line width if GL: if self._parent._antialias: GL.glEnable(GL.GL_LINE_SMOOTH) else: GL.glDisable(GL.GL_LINE_SMOOTH) # this is a bit of a hack to deal with HiDPI tr = transforms.document_to_framebuffer px_scale = np.mean((tr.map((1, 0)) - tr.map((0, 1)))[:2]) width = px_scale * self._parent._width GL.glLineWidth(max(width, 1.)) if self._parent._changed['connect']: self._connect = self._parent._interpret_connect() if isinstance(self._connect, np.ndarray): self._connect_ibo.set_data(self._connect) if self._connect is None: return prof('prepare') # Draw if self._connect == 'strip': self._program.draw('line_strip') elif self._connect == 'segments': self._program.draw('lines') elif isinstance(self._connect, np.ndarray): self._program.draw('lines', self._connect_ibo) else: raise ValueError("Invalid line connect mode: %r" % self._connect) prof('draw') class _AggLineVisual(Visual): _agg_vtype = np.dtype([('a_position', 'f4', 2), ('a_tangents', 'f4', 4), ('a_segment', 'f4', 2), ('a_angles', 'f4', 2), ('a_texcoord', 'f4', 2), ('alength', 'f4', 1), ('color', 'f4', 4)]) def __init__(self, parent): self._parent = parent self._vbo = gloo.VertexBuffer() self._ibo = gloo.IndexBuffer() self._pos = None self._color = None self._program = ModularProgram(vertex.VERTEX_SHADER, fragment.FRAGMENT_SHADER) self._da = DashAtlas() dash_index, dash_period = self._da['solid'] self._U = dict(dash_index=dash_index, dash_period=dash_period, linejoin=joins['round'], linecaps=(caps['round'], caps['round']), dash_caps=(caps['round'], caps['round']), antialias=1.0) self._dash_atlas = gloo.Texture2D(self._da._data) self.set_gl_state('translucent') def draw(self, transforms): Visual.draw(self, transforms) bake = False if self._parent._changed['pos']: if self._parent._pos is None: return # todo: does this result in unnecessary copies? self._pos = np.ascontiguousarray( self._parent._pos.astype(np.float32)) bake = True if self._parent._changed['color']: self._color = self._parent._interpret_color() bake = True if self._parent._changed['connect']: if self._parent._connect not in [None, 'strip']: raise NotImplementedError("Only 'strip' connection mode " "allowed for agg-method lines.") if bake: V, I = self._agg_bake(self._pos, self._color) self._vbo.set_data(V) self._ibo.set_data(I) gloo.set_state('translucent', depth_test=False) data_doc = transforms.visual_to_document doc_px = transforms.document_to_framebuffer px_ndc = transforms.framebuffer_to_render vert = self._program.vert vert['doc_px_transform'] = doc_px vert['px_ndc_transform'] = px_ndc vert['transform'] = data_doc #self._program.prepare() self._program.bind(self._vbo) uniforms = dict(closed=False, miter_limit=4.0, dash_phase=0.0, linewidth=self._parent._width) for n, v in uniforms.items(): self._program[n] = v for n, v in self._U.items(): self._program[n] = v self._program['u_dash_atlas'] = self._dash_atlas self._program.draw('triangles', self._ibo) @classmethod def _agg_bake(cls, vertices, color, closed=False): """ Bake a list of 2D vertices for rendering them as thick line. Each line segment must have its own vertices because of antialias (this means no vertex sharing between two adjacent line segments). """ n = len(vertices) P = np.array(vertices).reshape(n, 2).astype(float) idx = np.arange(n) # used to eventually tile the color array dx, dy = P[0] - P[-1] d = np.sqrt(dx*dx+dy*dy) # If closed, make sure first vertex = last vertex (+/- epsilon=1e-10) if closed and d > 1e-10: P = np.append(P, P[0]).reshape(n+1, 2) idx = np.append(idx, idx[-1]) n += 1 V = np.zeros(len(P), dtype=cls._agg_vtype) V['a_position'] = P # Tangents & norms T = P[1:] - P[:-1] N = np.sqrt(T[:, 0]**2 + T[:, 1]**2) # T /= N.reshape(len(T),1) V['a_tangents'][+1:, :2] = T V['a_tangents'][0, :2] = T[-1] if closed else T[0] V['a_tangents'][:-1, 2:] = T V['a_tangents'][-1, 2:] = T[0] if closed else T[-1] # Angles T1 = V['a_tangents'][:, :2] T2 = V['a_tangents'][:, 2:] A = np.arctan2(T1[:, 0]*T2[:, 1]-T1[:, 1]*T2[:, 0], T1[:, 0]*T2[:, 0]+T1[:, 1]*T2[:, 1]) V['a_angles'][:-1, 0] = A[:-1] V['a_angles'][:-1, 1] = A[+1:] # Segment L = np.cumsum(N) V['a_segment'][+1:, 0] = L V['a_segment'][:-1, 1] = L #V['a_lengths'][:,2] = L[-1] # Step 1: A -- B -- C => A -- B, B' -- C V = np.repeat(V, 2, axis=0)[1:-1] V['a_segment'][1:] = V['a_segment'][:-1] V['a_angles'][1:] = V['a_angles'][:-1] V['a_texcoord'][0::2] = -1 V['a_texcoord'][1::2] = +1 idx = np.repeat(idx, 2)[1:-1] # Step 2: A -- B, B' -- C -> A0/A1 -- B0/B1, B'0/B'1 -- C0/C1 V = np.repeat(V, 2, axis=0) V['a_texcoord'][0::2, 1] = -1 V['a_texcoord'][1::2, 1] = +1 idx = np.repeat(idx, 2) I = np.resize(np.array([0, 1, 2, 1, 2, 3], dtype=np.uint32), (n-1)*(2*3)) I += np.repeat(4*np.arange(n-1, dtype=np.uint32), 6) # Length V['alength'] = L[-1] * np.ones(len(V)) # Color if color.ndim == 1: color = np.tile(color, (len(V), 1)) elif color.ndim == 2 and len(color) == n: color = color[idx] else: raise ValueError('Color length %s does not match number of ' 'vertices %s' % (len(color), n)) V['color'] = color return V, I vispy-0.4.0/vispy/visuals/line/vertex.py0000664000175000017500000001735512426461252022060 0ustar larsonerlarsoner00000000000000VERTEX_SHADER = """ const float PI = 3.14159265358979323846264; const float THETA = 15.0 * 3.14159265358979323846264/180.0; // Cross product of v1 and v2 float cross(in vec2 v1, in vec2 v2) { return v1.x*v2.y - v1.y*v2.x; } // Returns distance of v3 to line v1-v2 float signed_distance(in vec2 v1, in vec2 v2, in vec2 v3) { return cross(v2-v1,v1-v3) / length(v2-v1); } // Rotate v around origin void rotate( in vec2 v, in float alpha, out vec2 result ) { float c = cos(alpha); float s = sin(alpha); result = vec2( c*v.x - s*v.y, s*v.x + c*v.y ); } vec2 transform_vector(vec2 x, vec2 base) { vec4 o = $transform(vec4(base, 0, 1)); return ($transform(vec4(base+x, 0, 1)) - o).xy; } // Uniforms //uniform mat4 u_matrix; //uniform mat4 u_view; attribute vec4 color; // uniform vec2 u_scale; // uniform vec2 tr_scale; uniform float linewidth; uniform float antialias; uniform vec2 linecaps; uniform float linejoin; uniform float miter_limit; attribute float alength; uniform float dash_phase; uniform float dash_period; uniform float dash_index; uniform vec2 dash_caps; uniform float closed; // Attributes attribute vec2 a_position; // position of each vertex attribute vec4 a_tangents; // vector pointing from one vertex to the next attribute vec2 a_segment; // distance along path attribute vec2 a_angles; attribute vec2 a_texcoord; // Varying varying vec4 v_color; varying vec2 v_segment; varying vec2 v_angles; varying vec2 v_linecaps; varying vec2 v_texcoord; varying vec2 v_miter; varying float v_miter_limit; varying float v_length; varying float v_linejoin; varying float v_linewidth; varying float v_antialias; varying float v_dash_phase; varying float v_dash_period; varying float v_dash_index; varying vec2 v_dash_caps; varying float v_closed; void main() { v_color = color; v_linewidth = linewidth; v_antialias = antialias; v_linecaps = linecaps; v_linejoin = linejoin; v_miter_limit = miter_limit; v_length = alength; v_dash_phase = dash_phase; v_dash_period = dash_period; v_dash_index = dash_index; v_dash_caps = dash_caps; v_closed = closed; bool closed = (v_closed > 0.0); // Attributes to varyings v_angles = a_angles; //v_segment = a_segment * u_scale.x * tr_scale.x; // TODO: proper scaling //v_length = v_length * u_scale * tr_scale; // TODO: proper scaling v_segment = a_segment; // Thickness below 1 pixel are represented using a 1 pixel thickness // and a modified alpha v_color.a = min(v_linewidth, v_color.a); v_linewidth = max(v_linewidth, 1.0); // If color is fully transparent we just will discard the fragment anyway if( v_color.a <= 0.0 ) { gl_Position = vec4(0.0,0.0,0.0,1.0); return; } // This is the actual half width of the line // TODO: take care of logical - physical pixel difference here. float w = ceil(1.25*v_antialias+v_linewidth)/2.0; //vec2 position = $transform(vec4(a_position,0.,1.)).xy*u_scale; vec2 position = $transform(vec4(a_position,0.,1.)).xy; // At this point, position must be in _doc_ coordinates because the line // width will be added to it. //vec2 t1 = normalize(tr_scale*a_tangents.xy); //vec2 t2 = normalize(tr_scale*a_tangents.zw); vec2 t1 = normalize(transform_vector(a_tangents.xy, a_position)); vec2 t2 = normalize(transform_vector(a_tangents.zw, a_position)); float u = a_texcoord.x; float v = a_texcoord.y; vec2 o1 = vec2( +t1.y, -t1.x); vec2 o2 = vec2( +t2.y, -t2.x); // This is a join // ---------------------------------------------------------------- if( t1 != t2 ) { float angle = atan (t1.x*t2.y-t1.y*t2.x, t1.x*t2.x+t1.y*t2.y); vec2 t = normalize(t1+t2); vec2 o = vec2( + t.y, - t.x); if ( v_dash_index > 0.0 ) { // Broken angle // ---------------------------------------------------------------- if( (abs(angle) > THETA) ) { position += v * w * o / cos(angle/2.0); float s = sign(angle); if( angle < 0.0 ) { if( u == +1.0 ) { u = v_segment.y + v * w * tan(angle/2.0); if( v == 1.0 ) { position -= 2.0 * w * t1 / sin(angle); u -= 2.0 * w / sin(angle); } } else { u = v_segment.x - v * w * tan(angle/2.0); if( v == 1.0 ) { position += 2.0 * w * t2 / sin(angle); u += 2.0*w / sin(angle); } } } else { if( u == +1.0 ) { u = v_segment.y + v * w * tan(angle/2.0); if( v == -1.0 ) { position += 2.0 * w * t1 / sin(angle); u += 2.0 * w / sin(angle); } } else { u = v_segment.x - v * w * tan(angle/2.0); if( v == -1.0 ) { position -= 2.0 * w * t2 / sin(angle); u -= 2.0*w / sin(angle); } } } // Continuous angle // ------------------------------------------------------------ } else { position += v * w * o / cos(angle/2.0); if( u == +1.0 ) u = v_segment.y; else u = v_segment.x; } } // Solid line // -------------------------------------------------------------------- else { position.xy += v * w * o / cos(angle/2.0); if( angle < 0.0 ) { if( u == +1.0 ) { u = v_segment.y + v * w * tan(angle/2.0); } else { u = v_segment.x - v * w * tan(angle/2.0); } } else { if( u == +1.0 ) { u = v_segment.y + v * w * tan(angle/2.0); } else { u = v_segment.x - v * w * tan(angle/2.0); } } } // This is a line start or end (t1 == t2) // ------------------------------------------------------------------------ } else { position += v * w * o1; if( u == -1.0 ) { u = v_segment.x - w; position -= w * t1; } else { u = v_segment.y + w; position += w * t2; } } // Miter distance // ------------------------------------------------------------------------ vec2 t; //vec2 curr = $transform(vec4(a_position,0.,1.)).xy*u_scale; vec2 curr = $transform(vec4(a_position,0.,1.)).xy; if( a_texcoord.x < 0.0 ) { vec2 next = curr + t2*(v_segment.y-v_segment.x); rotate( t1, +a_angles.x/2.0, t); v_miter.x = signed_distance(curr, curr+t, position); rotate( t2, +a_angles.y/2.0, t); v_miter.y = signed_distance(next, next+t, position); } else { vec2 prev = curr - t1*(v_segment.y-v_segment.x); rotate( t1, -a_angles.x/2.0,t); v_miter.x = signed_distance(prev, prev+t, position); rotate( t2, -a_angles.y/2.0,t); v_miter.y = signed_distance(curr, curr+t, position); } if (!closed && v_segment.x <= 0.0) { v_miter.x = 1e10; } if (!closed && v_segment.y >= v_length) { v_miter.y = 1e10; } v_texcoord = vec2( u, v*w ); gl_Position = $px_ndc_transform($doc_px_transform(vec4(position, 0.0, 1.0))); } """ vispy-0.4.0/vispy/visuals/line/__init__.py0000664000175000017500000000026612527672621022301 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from .line import LineVisual # noqa vispy-0.4.0/vispy/visuals/line/dash_atlas.py0000664000175000017500000000557112527672621022651 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. import numpy as np class DashAtlas(object): """ """ def __init__(self, shape=(64, 1024, 4)): # 512 patterns at max self._data = np.zeros(shape, dtype=np.float32) self._index = 0 self._atlas = {} self['solid'] = (1e20, 0), (1, 1) self['densely dotted'] = (0, 1), (1, 1) self['dotted'] = (0, 2), (1, 1) self['loosely dotted'] = (0, 3), (1, 1) self['densely dashed'] = (1, 1), (1, 1) self['dashed'] = (1, 2), (1, 1) self['loosely dashed'] = (1, 4), (1, 1) self['densely dashdotted'] = (1, 1, 0, 1), (1, 1, 1, 1) self['dashdotted'] = (1, 2, 0, 2), (1, 1, 1, 1) self['loosely dashdotted'] = (1, 3, 0, 3), (1, 1, 1, 1) self['densely dashdotdotted'] = (1, 1, 0, 1, 0, 1), (1, 1, 1, 1) self['dashdotdotted'] = (1, 2, 0, 2, 0, 2), (1, 1, 1, 1, 1, 1) self['loosely dashdotdotted'] = (1, 3, 0, 3, 0, 3), (1, 1, 1, 1) self._dirty = True def __getitem__(self, key): return self._atlas[key] def __setitem__(self, key, value): data, period = self.make_pattern(value[0], value[1]) self._data[self._index] = data self._atlas[key] = [self._index / float(self._data.shape[0]), period] self._index += 1 self._dirty = True # self.add_pattern(value) def make_pattern(self, pattern, caps=[1, 1]): """ """ # A pattern is defined as on/off sequence of segments # It must be a multiple of 2 if len(pattern) > 1 and len(pattern) % 2: pattern = [pattern[0] + pattern[-1]] + pattern[1:-1] P = np.array(pattern) # Period is the sum of all segment length period = np.cumsum(P)[-1] # Find all start and end of on-segment only C, c = [], 0 for i in range(0, len(P) + 2, 2): a = max(0.0001, P[i % len(P)]) b = max(0.0001, P[(i + 1) % len(P)]) C.extend([c, c + a]) c += a + b C = np.array(C) # Build pattern length = self._data.shape[1] Z = np.zeros((length, 4), dtype=np.float32) for i in np.arange(0, len(Z)): x = period * (i) / float(len(Z) - 1) index = np.argmin(abs(C - (x))) if index % 2 == 0: if x <= C[index]: dash_type = +1 else: dash_type = 0 dash_start, dash_end = C[index], C[index + 1] else: if x > C[index]: dash_type = -1 else: dash_type = 0 dash_start, dash_end = C[index - 1], C[index] Z[i] = C[index], dash_type, dash_start, dash_end return Z, period vispy-0.4.0/vispy/visuals/image.py0000664000175000017500000002402012527672621020667 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from __future__ import division import numpy as np from ..gloo import set_state, Texture2D from ..color import get_colormap from .shaders import ModularProgram, Function, FunctionChain from .transforms import NullTransform from .visual import Visual from ..ext.six import string_types VERT_SHADER = """ attribute vec2 a_position; attribute vec2 a_texcoord; varying vec2 v_texcoord; void main() { v_texcoord = a_texcoord; gl_Position = $transform(vec4(a_position, 0., 1.)); } """ FRAG_SHADER = """ uniform sampler2D u_texture; varying vec2 v_texcoord; void main() { vec2 texcoord = $map_uv_to_tex(vec4(v_texcoord, 0, 1)).xy; if(texcoord.x < 0.0 || texcoord.x > 1.0 || texcoord.y < 0.0 || texcoord.y > 1.0) { discard; } gl_FragColor = $color_transform(texture2D(u_texture, texcoord)); } """ # noqa _null_color_transform = 'vec4 pass(vec4 color) { return color; }' _c2l = 'float cmap(vec4 color) { return (color.r + color.g + color.b) / 3.; }' class ImageVisual(Visual): """Visual subclass displaying an image. Parameters ---------- data : ndarray ImageVisual data. Can be shape (M, N), (M, N, 3), or (M, N, 4). method : str Selects method of rendering image in case of non-linear transforms. Each method produces similar results, but may trade efficiency and accuracy. If the transform is linear, this parameter is ignored and a single quad is drawn around the area of the image. * 'auto': Automatically select 'impostor' if the image is drawn with a nonlinear transform; otherwise select 'subdivide'. * 'subdivide': ImageVisual is represented as a grid of triangles with texture coordinates linearly mapped. * 'impostor': ImageVisual is represented as a quad covering the entire view, with texture coordinates determined by the transform. This produces the best transformation results, but may be slow. grid: tuple (rows, cols) If method='subdivide', this tuple determines the number of rows and columns in the image grid. cmap : str | ColorMap Colormap to use for luminance images. clim : str | tuple Limits to use for the colormap. Can be 'auto' to auto-set bounds to the min and max of the data. **kwargs : dict Keyword arguments to pass to `Visual`. Notes ----- The colormap functionality through ``cmap`` and ``clim`` are only used if the data are 2D. """ def __init__(self, data=None, method='auto', grid=(10, 10), cmap='cubehelix', clim='auto', **kwargs): super(ImageVisual, self).__init__(**kwargs) self._program = ModularProgram(VERT_SHADER, FRAG_SHADER) self.clim = clim self.cmap = cmap self._data = None self._texture = None self._interpolation = 'nearest' if data is not None: self.set_data(data) self._method = method self._method_used = None self._grid = grid self._need_vertex_update = True def set_data(self, image): """Set the data Parameters ---------- image : array-like The image data. """ data = np.asarray(image) if self._data is None or self._data.shape != data.shape: self._need_vertex_update = True self._data = data self._texture = None @property def clim(self): return (self._clim if isinstance(self._clim, string_types) else tuple(self._clim)) @clim.setter def clim(self, clim): if isinstance(clim, string_types): if clim != 'auto': raise ValueError('clim must be "auto" if a string') else: clim = np.array(clim, float) if clim.shape != (2,): raise ValueError('clim must have two elements') self._clim = clim self._need_vertex_update = True self.update() @property def cmap(self): return self._cmap @cmap.setter def cmap(self, cmap): self._cmap = get_colormap(cmap) self.update() @property def method(self): return self._method @method.setter def method(self, m): if self._method != m: self._method = m self._need_vertex_update = True self.update() @property def size(self): return self._data.shape[:2][::-1] def _build_vertex_data(self, transforms): method = self._method grid = self._grid if method == 'auto': if transforms.get_full_transform().Linear: method = 'subdivide' grid = (1, 1) else: method = 'impostor' self._method_used = method # TODO: subdivision and impostor modes should be handled by new # components? if method == 'subdivide': # quads cover area of image as closely as possible w = 1.0 / grid[1] h = 1.0 / grid[0] quad = np.array([[0, 0, 0], [w, 0, 0], [w, h, 0], [0, 0, 0], [w, h, 0], [0, h, 0]], dtype=np.float32) quads = np.empty((grid[1], grid[0], 6, 3), dtype=np.float32) quads[:] = quad mgrid = np.mgrid[0.:grid[1], 0.:grid[0]].transpose(1, 2, 0) mgrid = mgrid[:, :, np.newaxis, :] mgrid[..., 0] *= w mgrid[..., 1] *= h quads[..., :2] += mgrid tex_coords = quads.reshape(grid[1]*grid[0]*6, 3) tex_coords = np.ascontiguousarray(tex_coords[:, :2]) vertices = tex_coords * self.size # vertex shader provides correct texture coordinates self._program.frag['map_uv_to_tex'] = NullTransform() elif method == 'impostor': # quad covers entire view; frag. shader will deal with image shape vertices = np.array([[-1, -1], [1, -1], [1, 1], [-1, -1], [1, 1], [-1, 1]], dtype=np.float32) tex_coords = vertices # vertex shader provides ND coordinates; # fragment shader maps to texture coordinates self._program.vert['transform'] = NullTransform() self._raycast_func = Function(''' vec4 map_local_to_tex(vec4 x) { // Cast ray from 3D viewport to surface of image // (if $transform does not affect z values, then this // can be optimized as simply $transform.map(x) ) vec4 p1 = $transform(x); vec4 p2 = $transform(x + vec4(0, 0, 0.5, 0)); p1 /= p1.w; p2 /= p2.w; vec4 d = p2 - p1; float f = p2.z / d.z; vec4 p3 = p2 - d * f; // finally map local to texture coords return vec4(p3.xy / $image_size, 0, 1); } ''') self._raycast_func['image_size'] = self.size self._program.frag['map_uv_to_tex'] = self._raycast_func else: raise ValueError("Unknown image draw method '%s'" % method) self._program['a_position'] = vertices.astype(np.float32) self._program['a_texcoord'] = tex_coords.astype(np.float32) self._need_vertex_update = False def _build_texture(self): data = self._data if data.dtype == np.float64: data = data.astype(np.float32) if data.ndim == 2 or data.shape[2] == 1: # deal with clim on CPU b/c of texture depth limits :( # can eventually do this by simulating 32-bit float... maybe clim = self._clim if isinstance(clim, string_types) and clim == 'auto': clim = np.min(data), np.max(data) clim = np.asarray(clim, dtype=np.float32) data = data - clim[0] # not inplace so we don't modify orig data if clim[1] - clim[0] > 0: data /= clim[1] - clim[0] else: data[:] = 1 if data[0, 0] != 0 else 0 fun = FunctionChain(None, [Function(_c2l), Function(self.cmap.glsl_map)]) self._clim = np.array(clim) else: fun = Function(_null_color_transform) self._program.frag['color_transform'] = fun self._texture = Texture2D(data, interpolation=self._interpolation) self._program['u_texture'] = self._texture def bounds(self, mode, axis): """Get the bounds Parameters ---------- mode : str Describes the type of boundary requested. Can be "visual", "data", or "mouse". axis : 0, 1, 2 The axis along which to measure the bounding values, in x-y-z order. """ if axis > 1: return (0, 0) else: return (0, self.size[axis]) def draw(self, transforms): """Draw the visual Parameters ---------- transforms : instance of TransformSystem The transforms to use. """ if self._data is None: return set_state(cull_face=False) # upload texture is needed if self._texture is None: self._build_texture() # rebuild vertex buffers if needed if self._need_vertex_update: self._build_vertex_data(transforms) # update transform method = self._method_used if method == 'subdivide': self._program.vert['transform'] = transforms.get_full_transform() else: self._raycast_func['transform'] = \ transforms.get_full_transform().inverse self._program.draw('triangles') vispy-0.4.0/vispy/visuals/__init__.py0000664000175000017500000000272112527672621021350 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ This module provides a library of Visual classes, which are drawable objects intended to encapsulate simple graphic objects such as lines, meshes, points, 2D shapes, images, text, etc. These classes define only the OpenGL machinery and connot be used directly in a scenegraph. For scenegraph use, see the complementary Visual+Node classes defined in vispy.scene. """ from .cube import CubeVisual # noqa from .ellipse import EllipseVisual # noqa from .gridlines import GridLinesVisual # noqa from .image import ImageVisual # noqa from .histogram import HistogramVisual # noqa from .isocurve import IsocurveVisual # noqa from .isoline import IsolineVisual # noqa from .isosurface import IsosurfaceVisual # noqa from .line import LineVisual # noqa from .line_plot import LinePlotVisual # noqa from .markers import MarkersVisual, marker_types # noqa from .mesh import MeshVisual # noqa from .polygon import PolygonVisual # noqa from .rectangle import RectangleVisual # noqa from .regular_polygon import RegularPolygonVisual # noqa from .spectrogram import SpectrogramVisual # noqa from .surface_plot import SurfacePlotVisual # noqa from .text import TextVisual # noqa from .tube import TubeVisual # noqa from .visual import Visual # noqa from .volume import VolumeVisual # noqa from .xyz_axis import XYZAxisVisual # noqa vispy-0.4.0/vispy/visuals/xyz_axis.py0000664000175000017500000000147612510536123021461 0ustar larsonerlarsoner00000000000000 import numpy as np from .line import LineVisual class XYZAxisVisual(LineVisual): """ Simple 3D axis for indicating coordinate system orientation. Axes are x=red, y=green, z=blue. """ def __init__(self, **kwargs): verts = np.array([[0, 0, 0], [1, 0, 0], [0, 0, 0], [0, 1, 0], [0, 0, 0], [0, 0, 1]]) color = np.array([[1, 0, 0, 1], [1, 0, 0, 1], [0, 1, 0, 1], [0, 1, 0, 1], [0, 0, 1, 1], [0, 0, 1, 1]]) LineVisual.__init__(self, pos=verts, color=color, connect='segments', method='gl', **kwargs) vispy-0.4.0/vispy/visuals/surface_plot.py0000664000175000017500000001252712527672621022304 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from __future__ import division import numpy as np from .mesh import MeshVisual from ..geometry import MeshData class SurfacePlotVisual(MeshVisual): """Displays a surface plot on a regular x,y grid Parameters ---------- x : ndarray | None 1D array of values specifying the x positions of vertices in the grid. If None, values will be assumed to be integers. y : ndarray | None 1D array of values specifying the x positions of vertices in the grid. If None, values will be assumed to be integers. z : ndarray 2D array of height values for each grid vertex. colors : ndarray (width, height, 4) array of vertex colors. Notes ----- All arguments are optional. Note that if vertex positions are updated, the normal vectors for each triangle must be recomputed. This is somewhat expensive if the surface was initialized with smooth=False and very expensive if smooth=True. For faster performance, initialize with compute_normals=False and use per-vertex colors or a material that does not require normals. """ def __init__(self, x=None, y=None, z=None, colors=None, **kwargs): # The x, y, z, and colors arguments are passed to set_data(). # All other keyword arguments are passed to MeshVisual.__init__(). self._x = None self._y = None self._z = None self.__color = None self.__vertices = None self.__meshdata = MeshData() MeshVisual.__init__(self, **kwargs) self.set_data(x, y, z, colors) def set_data(self, x=None, y=None, z=None, colors=None): """Update the data in this surface plot. Parameters ---------- x : ndarray | None 1D array of values specifying the x positions of vertices in the grid. If None, values will be assumed to be integers. y : ndarray | None 1D array of values specifying the x positions of vertices in the grid. If None, values will be assumed to be integers. z : ndarray 2D array of height values for each grid vertex. colors : ndarray (width, height, 4) array of vertex colors. """ if x is not None: if self._x is None or len(x) != len(self._x): self.__vertices = None self._x = x if y is not None: if self._y is None or len(y) != len(self._y): self.__vertices = None self._y = y if z is not None: if self._x is not None and z.shape[0] != len(self._x): raise TypeError('Z values must have shape (len(x), len(y))') if self._y is not None and z.shape[1] != len(self._y): raise TypeError('Z values must have shape (len(x), len(y))') self._z = z if (self.__vertices is not None and self._z.shape != self.__vertices.shape[:2]): self.__vertices = None if colors is not None: self.__colors = colors self.__meshdata.set_vertex_colors(colors) if self._z is None: return update_mesh = False new_vertices = False ## Generate vertex and face array if self.__vertices is None: new_vertices = True self.__vertices = np.empty((self._z.shape[0], self._z.shape[1], 3), dtype=np.float32) self.generate_faces() self.__meshdata.set_faces(self.__faces) update_mesh = True ## Copy x, y, z data into vertex array if new_vertices or x is not None: if x is None: if self._x is None: x = np.arange(self._z.shape[0]) else: x = self._x self.__vertices[:, :, 0] = x.reshape(len(x), 1) update_mesh = True if new_vertices or y is not None: if y is None: if self._y is None: y = np.arange(self._z.shape[1]) else: y = self._y self.__vertices[:, :, 1] = y.reshape(1, len(y)) update_mesh = True if new_vertices or z is not None: self.__vertices[..., 2] = self._z update_mesh = True ## Update MeshData if update_mesh: self.__meshdata.set_vertices( self.__vertices.reshape(self.__vertices.shape[0] * self.__vertices.shape[1], 3)) MeshVisual.set_data(self, meshdata=self.__meshdata) def generate_faces(self): cols = self._z.shape[1]-1 rows = self._z.shape[0]-1 faces = np.empty((cols*rows*2, 3), dtype=np.uint) rowtemplate1 = (np.arange(cols).reshape(cols, 1) + np.array([[0, 1, cols+1]])) rowtemplate2 = (np.arange(cols).reshape(cols, 1) + np.array([[cols+1, 1, cols+2]])) for row in range(rows): start = row * cols * 2 faces[start:start+cols] = rowtemplate1 + row * (cols+1) faces[start+cols:start+(cols*2)] = rowtemplate2 + row * (cols+1) self.__faces = faces vispy-0.4.0/vispy/visuals/visual.py0000664000175000017500000001333412527672621021116 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from __future__ import division from ..util.event import EmitterGroup, Event from .shaders import StatementList from .. import gloo """ API Issues to work out: * Need Visual.bounds() as described here: https://github.com/vispy/vispy/issues/141 """ class Visual(object): """ Abstract class representing a drawable object. At a minimum, Visual subclasses should extend the draw() method. Events: update : Event Emitted when the visual has changed and needs to be redrawn. bounds_change : Event Emitted when the bounds of the visual have changed. Notes ----- When used in the scenegraph, all Visual classes are mixed with `vispy.scene.Node` in order to implement the methods, attributes and capabilities required for their usage within it. """ def __init__(self): self._visible = True self.events = EmitterGroup(source=self, auto_connect=True, update=Event, bounds_change=Event ) self._gl_state = {'preset': None} self._filters = set() self._hooks = {} def set_gl_state(self, preset=None, **kwargs): """Define the set of GL state parameters to use when drawing Parameters ---------- preset : str Preset to use. **kwargs : dict Keyword argments to use. """ self._gl_state = kwargs self._gl_state['preset'] = preset def update_gl_state(self, *args, **kwargs): """Modify the set of GL state parameters to use when drawing Parameters ---------- *args : tuple Arguments. **kwargs : dict Keyword argments. """ if len(args) == 1: self._gl_state['preset'] = args[0] elif len(args) != 0: raise TypeError("Only one positional argument allowed.") self._gl_state.update(kwargs) def _update(self): """ This method is called internally whenever the Visual needs to be redrawn. By default, it emits the update event. """ self.events.update() def draw(self, transforms): """Draw this visual now. The default implementation calls gloo.set_state(). This function is called automatically when the visual needs to be drawn as part of a scenegraph, or when calling ``SceneCanvas.draw_visual(...)``. It is uncommon to call this method manually. The *transforms* argument is a TransformSystem instance that provides access to transforms that the visual may use to determine its relationship to the document coordinate system (which provides physical measurements) and the framebuffer coordinate system (which is necessary for antialiasing calculations). Vertex transformation can be done either on the CPU using Transform.map(), or on the GPU using the GLSL functions generated by Transform.shader_map(). Parameters ---------- transforms : instance of TransformSystem The transforms to use. """ gloo.set_state(**self._gl_state) def bounds(self, mode, axis): """ Return the (min, max) bounding values describing the location of this node in its local coordinate system. Parameters ---------- mode : str Describes the type of boundary requested. Can be "visual", "data", or "mouse". axis : 0, 1, 2 The axis along which to measure the bounding values, in x-y-z order. Returns ------- None or (min, max) tuple. Notes ----- This is used primarily to allow automatic ViewBox zoom/pan. By default, this method returns None which indicates the object should be ignored for automatic zooming along *axis*. A scenegraph may also use this information to cull visuals from the display list. """ return None def update(self): """ Emit an event to inform listeners that this Visual needs to be redrawn. """ self.events.update() def _get_hook(self, shader, name): """Return a FunctionChain that Filters may use to modify the program. *shader* should be "frag" or "vert" *name* should be "pre" or "post" """ assert name in ('pre', 'post') key = (shader, name) if key in self._hooks: return self._hooks[key] prog = getattr(self, '_program', None) if prog is None: raise NotImplementedError("%s shader does not implement hook '%s'" % key) hook = StatementList() if shader == 'vert': prog.vert[name] = hook elif shader == 'frag': prog.frag[name] = hook self._hooks[key] = hook return hook def attach(self, filt): """Attach a Filter to this visual Each filter modifies the appearance or behavior of the visual. Parameters ---------- filt : object The filter to attach. """ filt._attach(self) self._filters.add(filt) def detach(self, filt): """Detach a filter Parameters ---------- filt : object The filter to detach. """ self._filters.remove(filt) filt._detach(self) vispy-0.4.0/vispy/visuals/line_plot.py0000664000175000017500000001127112527672621021576 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. import numpy as np from .line import LineVisual from .markers import MarkersVisual from .visual import Visual class LinePlotVisual(Visual): """Visual displaying a plot line with optional markers. Parameters ---------- data : array-like Arguments can be passed as ``(Y,)``, ``(X, Y)`` or ``np.array((X, Y))``. color : instance of Color Color of the line. symbol : str Marker symbol to use. line_kind : str Kind of line to draw. For now, only solid lines (``'-'``) are supported. width : float Line width. marker_size : float Marker size. If `size == 0` markers will not be shown. edge_color : instance of Color Color of the marker edge. face_color : instance of Color Color of the marker face. edge_width : float Edge width of the marker. connect : str | array See LineVisual. **kwargs : keyword arguments Argements to pass to the super class. Examples -------- All of these syntaxes will work: >>> LinePlotVisual(y_vals) >>> LinePlotVisual(x_vals, y_vals) >>> LinePlotVisual(xy_vals) See also -------- LineVisual, MarkersVisual, marker_types """ _line_kwargs = ('color', 'width', 'connect') _marker_kwargs = ('edge_color', 'face_color', 'edge_width', 'marker_size', 'symbol') _kw_trans = dict(marker_size='size') def __init__(self, data, color='k', symbol='o', line_kind='-', width=1., marker_size=10., edge_color='k', face_color='w', edge_width=1., connect='strip', **kwargs): Visual.__init__(self, **kwargs) if line_kind != '-': raise ValueError('Only solid lines currently supported') self._line = LineVisual() self._markers = MarkersVisual() self.set_data(data, color=color, symbol=symbol, width=width, marker_size=marker_size, edge_color=edge_color, face_color=face_color, edge_width=edge_width, connect=connect) def set_data(self, data, **kwargs): """Set the line data Parameters ---------- data : array-like The data. **kwargs : dict Keywoard arguments to pass to MarkerVisual and LineVisal. """ pos = np.atleast_1d(data).astype(np.float32) if pos.ndim == 1: pos = pos[:, np.newaxis] elif pos.ndim > 2: raise ValueError('data must have at most two dimensions') if pos.size == 0: pos = self._line.pos # if both args and keywords are zero, then there is no # point in calling this function. if len(kwargs) == 0: raise TypeError("neither line points nor line properties" "are provided") elif pos.shape[1] == 1: x = np.arange(pos.shape[0], dtype=np.float32)[:, np.newaxis] pos = np.concatenate((x, pos), axis=1) # if args are empty, don't modify position elif pos.shape[1] > 2: raise TypeError("Too many coordinates given (%s; max is 2)." % pos.shape[1]) # todo: have both sub-visuals share the same buffers. line_kwargs = {} for k in self._line_kwargs: if k in kwargs: k_ = self._kw_trans[k] if k in self._kw_trans else k line_kwargs[k] = kwargs.pop(k_) self._line.set_data(pos=pos, **line_kwargs) marker_kwargs = {} for k in self._marker_kwargs: if k in kwargs: k_ = self._kw_trans[k] if k in self._kw_trans else k marker_kwargs[k_] = kwargs.pop(k) self._markers.set_data(pos=pos, **marker_kwargs) if len(kwargs) > 0: raise TypeError("Invalid keyword arguments: %s" % kwargs.keys()) def bounds(self, mode, axis): """Get the bounds Parameters ---------- mode : str Describes the type of boundary requested. Can be "visual", "data", or "mouse". axis : 0, 1, 2 The axis along which to measure the bounding values, in x-y-z order. """ return self._line.bounds(mode, axis) def draw(self, transforms): """Draw the visual Parameters ---------- transforms : instance of TransformSystem The transforms to use. """ for v in self._line, self._markers: v.draw(transforms) vispy-0.4.0/vispy/visuals/mesh.py0000664000175000017500000002566212527672621020556 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2015, Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ A MeshVisual Visual that uses the new shader Function. """ from __future__ import division import numpy as np from .visual import Visual from .shaders import ModularProgram, Function, Varying from ..gloo import VertexBuffer, IndexBuffer from ..geometry import MeshData from ..color import Color ## Snippet templates (defined as string to force user to create fresh Function) # Consider these stored in a central location in vispy ... vertex_template = """ void main() { gl_Position = $transform($to_vec4($position)); } """ fragment_template = """ void main() { gl_FragColor = $color; } """ phong_template = """ vec4 phong_shading(vec4 color) { vec4 o = $transform(vec4(0, 0, 0, 1)); vec4 n = $transform(vec4($normal, 1)); vec3 norm = normalize((n-o).xyz); vec3 light = normalize($light_dir.xyz); float p = dot(light, norm); p = (p < 0. ? 0. : p); vec4 diffuse = $light_color * p; diffuse.a = 1.0; p = dot(reflect(light, norm), vec3(0,0,1)); if (p < 0.0) { p = 0.0; } vec4 specular = $light_color * 5.0 * pow(p, 100.); return color * ($ambient + diffuse) + specular; } """ ## Functions that can be used as is (don't have template variables) # Consider these stored in a central location in vispy ... vec3to4 = Function(""" vec4 vec3to4(vec3 xyz) { return vec4(xyz, 1.0); } """) vec2to4 = Function(""" vec4 vec2to4(vec2 xyz) { return vec4(xyz, 0.0, 1.0); } """) class MeshVisual(Visual): """Mesh visual Parameters ---------- vertices : array-like | None The vertices. faces : array-like | None The faces. vertex_colors : array-like | None Colors to use for each vertex. face_colors : array-like | None Colors to use for each face. color : instance of Color The color to use. meshdata : instance of MeshData | None The meshdata. shading : str | None Shading to use. mode : str The drawing mode. **kwargs : dict Keyword arguments to pass to `Visual`. """ def __init__(self, vertices=None, faces=None, vertex_colors=None, face_colors=None, color=(0.5, 0.5, 1, 1), meshdata=None, shading=None, mode='triangles', **kwargs): Visual.__init__(self, **kwargs) self.set_gl_state('translucent', depth_test=True, cull_face=False) # Create a program self._program = ModularProgram(vertex_template, fragment_template) self._program.vert['pre'] = '' self._program.vert['post'] = '' self._program.frag['pre'] = '' self._program.frag['post'] = '' # Define buffers self._vertices = VertexBuffer(np.zeros((0, 3), dtype=np.float32)) self._normals = None self._faces = IndexBuffer() self._colors = VertexBuffer(np.zeros((0, 4), dtype=np.float32)) self._normals = VertexBuffer(np.zeros((0, 3), dtype=np.float32)) # Whether to use _faces index self._indexed = None # Uniform color self._color = None # primitive mode self._mode = mode # varyings self._color_var = Varying('v_color', dtype='vec4') self._normal_var = Varying('v_normal', dtype='vec3') # Function for computing phong shading self._phong = Function(phong_template) # Init self.shading = shading self._bounds = None # Note we do not call subclass set_data -- often the signatures # do no match. MeshVisual.set_data(self, vertices=vertices, faces=faces, vertex_colors=vertex_colors, face_colors=face_colors, meshdata=meshdata, color=color) def set_data(self, vertices=None, faces=None, vertex_colors=None, face_colors=None, color=None, meshdata=None): """Set the mesh data Parameters ---------- vertices : array-like | None The vertices. faces : array-like | None The faces. vertex_colors : array-like | None Colors to use for each vertex. face_colors : array-like | None Colors to use for each face. color : instance of Color The color to use. meshdata : instance of MeshData | None The meshdata. """ if meshdata is not None: self._meshdata = meshdata else: self._meshdata = MeshData(vertices=vertices, faces=faces, vertex_colors=vertex_colors, face_colors=face_colors) self._bounds = self._meshdata.get_bounds() if color is not None: self._color = Color(color) self.mesh_data_changed() @property def mode(self): """The triangle mode used to draw this mesh. Options are: * 'triangles': Draw one triangle for every three vertices (eg, [1,2,3], [4,5,6], [7,8,9) * 'triangle_strip': Draw one strip for every vertex excluding the first two (eg, [1,2,3], [2,3,4], [3,4,5]) * 'triangle_fan': Draw each triangle from the first vertex and the last two vertices (eg, [1,2,3], [1,3,4], [1,4,5]) """ return self._mode @mode.setter def mode(self, m): modes = ['triangles', 'triangle_strip', 'triangle_fan'] if m not in modes: raise ValueError("Mesh mode must be one of %s" % ', '.join(modes)) self._mode = m @property def mesh_data(self): """The mesh data""" return self._meshdata @property def color(self): """The uniform color for this mesh. This value is only used if per-vertex or per-face colors are not specified. """ return self._color @color.setter def color(self, c): self.set_data(color=c) def mesh_data_changed(self): self._data_changed = True self.update() def _update_data(self): md = self.mesh_data # Update vertex/index buffers if self.shading == 'smooth' and not md.has_face_indexed_data(): v = md.get_vertices() if v is None: return False if v.shape[-1] == 2: v = np.concatenate((v, np.zeros((v.shape[:-1] + (1,)))), -1) self._vertices.set_data(v, convert=True) self._normals.set_data(md.get_vertex_normals(), convert=True) self._faces.set_data(md.get_faces(), convert=True) self._indexed = True if md.has_vertex_color(): self._colors.set_data(md.get_vertex_colors(), convert=True) elif md.has_face_color(): self._colors.set_data(md.get_face_colors(), convert=True) else: self._colors.set_data(np.zeros((0, 4), dtype=np.float32)) else: v = md.get_vertices(indexed='faces') if v is None: return False if v.shape[-1] == 2: v = np.concatenate((v, np.zeros((v.shape[:-1] + (1,)))), -1) self._vertices.set_data(v, convert=True) if self.shading == 'smooth': normals = md.get_vertex_normals(indexed='faces') self._normals.set_data(normals, convert=True) elif self.shading == 'flat': normals = md.get_face_normals(indexed='faces') self._normals.set_data(normals, convert=True) else: self._normals.set_data(np.zeros((0, 3), dtype=np.float32)) self._indexed = False if md.has_vertex_color(): self._colors.set_data(md.get_vertex_colors(indexed='faces'), convert=True) elif md.has_face_color(): self._colors.set_data(md.get_face_colors(indexed='faces'), convert=True) else: self._colors.set_data(np.zeros((0, 4), dtype=np.float32)) self._program.vert['position'] = self._vertices # Position input handling if v.shape[-1] == 2: self._program.vert['to_vec4'] = vec2to4 elif v.shape[-1] == 3: self._program.vert['to_vec4'] = vec3to4 else: raise TypeError("Vertex data must have shape (...,2) or (...,3).") # Color input handling colors = self._colors if self._colors.size > 0 else self._color.rgba self._program.vert[self._color_var] = colors # Shading if self.shading is None: self._program.frag['color'] = self._color_var else: # Normal data comes via vertex shader if self._normals.size > 0: normals = self._normals else: normals = (1., 0., 0.) self._program.vert[self._normal_var] = normals self._phong['normal'] = self._normal_var # Additional phong properties self._phong['light_dir'] = (1.0, 1.0, 5.0) self._phong['light_color'] = (1.0, 1.0, 1.0, 1.0) self._phong['ambient'] = (0.3, 0.3, 0.3, 1.0) self._program.frag['color'] = self._phong(self._color_var) self._data_changed = False @property def shading(self): """ The shading method used. """ return self._shading @shading.setter def shading(self, value): assert value in (None, 'flat', 'smooth') self._shading = value def draw(self, transforms): """Draw the visual Parameters ---------- transforms : instance of TransformSystem The transforms to use. """ if self._data_changed: if self._update_data() is False: return self._data_changed = False Visual.draw(self, transforms) full_tr = transforms.get_full_transform() self._program.vert['transform'] = full_tr doc_tr = transforms.visual_to_document self._phong['transform'] = doc_tr # Draw if self._indexed: self._program.draw(self._mode, self._faces) else: self._program.draw(self._mode) def bounds(self, mode, axis): """Get the bounds Parameters ---------- mode : str Describes the type of boundary requested. Can be "visual", "data", or "mouse". axis : 0, 1, 2 The axis along which to measure the bounding values, in x-y-z order. """ if self._bounds is None: return None return self._bounds[axis] vispy-0.4.0/vispy/visuals/histogram.py0000664000175000017500000000410112527672621021600 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2015, Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- import numpy as np from .mesh import MeshVisual from ..ext.six import string_types class HistogramVisual(MeshVisual): """Visual that calculates and displays a histogram of data Parameters ---------- data : array-like Data to histogram. Currently only 1D data is supported. bins : int | array-like Number of bins, or bin edges. color : instance of Color Color of the histogram. orientation : {'h', 'v'} Orientation of the histogram. """ def __init__(self, data, bins=10, color='w', orientation='h'): # 4-5 # | | # 1-2/7-8 # |/| | | # 0-3-6-9 data = np.asarray(data) if data.ndim != 1: raise ValueError('Only 1D data currently supported') if not isinstance(orientation, string_types) or \ orientation not in ('h', 'v'): raise ValueError('orientation must be "h" or "v", not %s' % (orientation,)) X, Y = (0, 1) if orientation == 'h' else (1, 0) # do the histogramming data, bin_edges = np.histogram(data, bins) # construct our vertices rr = np.zeros((3 * len(bin_edges) - 2, 3), np.float32) rr[:, X] = np.repeat(bin_edges, 3)[1:-1] rr[1::3, Y] = data rr[2::3, Y] = data bin_edges.astype(np.float32) # and now our tris tris = np.zeros((2 * len(bin_edges) - 2, 3), np.uint32) offsets = 3 * np.arange(len(bin_edges) - 1, dtype=np.uint32)[:, np.newaxis] tri_1 = np.array([0, 2, 1]) tri_2 = np.array([2, 0, 3]) tris[::2] = tri_1 + offsets tris[1::2] = tri_2 + offsets MeshVisual.__init__(self, rr, tris, color=color) vispy-0.4.0/vispy/visuals/collections/0000775000175000017500000000000012527674621021555 5ustar larsonerlarsoner00000000000000vispy-0.4.0/vispy/visuals/collections/util.py0000664000175000017500000001173612527672621023112 0ustar larsonerlarsoner00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2013, Nicolas P. Rougier. All rights reserved. # Distributed under the terms of the new BSD License. # ----------------------------------------------------------------------------- import numpy as np from functools import reduce from operator import mul def dtype_reduce(dtype, level=0, depth=0): """ Try to reduce dtype up to a given level when it is possible dtype = [ ('vertex', [('x', 'f4'), ('y', 'f4'), ('z', 'f4')]), ('normal', [('x', 'f4'), ('y', 'f4'), ('z', 'f4')]), ('color', [('r', 'f4'), ('g', 'f4'), ('b', 'f4'), ('a', 'f4')])] level 0: ['color,vertex,normal,', 10, 'float32'] level 1: [['color', 4, 'float32'] ['normal', 3, 'float32'] ['vertex', 3, 'float32']] """ dtype = np.dtype(dtype) fields = dtype.fields # No fields if fields is None: if len(dtype.shape): count = reduce(mul, dtype.shape) else: count = 1 # size = dtype.itemsize / count if dtype.subdtype: name = str(dtype.subdtype[0]) else: name = str(dtype) return ['', count, name] else: items = [] name = '' # Get reduced fields for key, value in fields.items(): l = dtype_reduce(value[0], level, depth + 1) if type(l[0]) is str: items.append([key, l[1], l[2]]) else: items.append(l) name += key + ',' # Check if we can reduce item list ctype = None count = 0 for i, item in enumerate(items): # One item is a list, we cannot reduce if type(item[0]) is not str: return items else: if i == 0: ctype = item[2] count += item[1] else: if item[2] != ctype: return items count += item[1] if depth >= level: return [name, count, ctype] else: return items def fetchcode(utype, prefix=""): """ Generate the GLSL code needed to retrieve fake uniform values from a texture. uniforms : sampler2D Texture to fetch uniforms from uniforms_shape: vec3 Size of texture (width,height,count) where count is the number of float to be fetched. collection_index: float Attribute giving the index of the uniforms to be fetched. This index relates to the index in the uniform array from python side. """ utype = np.dtype(utype) _utype = dtype_reduce(utype, level=1) header = """ uniform sampler2D uniforms; uniform vec3 uniforms_shape; attribute float collection_index; """ # Header generation (easy) types = {1: 'float', 2: 'vec2 ', 3: 'vec3 ', 4: 'vec4 ', 9: 'mat3 ', 16: 'mat4 '} for name, count, _ in _utype: if name != '__unused__': header += "varying %s %s%s;\n" % (types[count], prefix, name) # Body generation (not so easy) body = """\nvoid fetch_uniforms() { float rows = uniforms_shape.x; float cols = uniforms_shape.y; float count = uniforms_shape.z; float index = collection_index; int index_x = int(mod(index, (floor(cols/(count/4.0))))) * int(count/4.0); int index_y = int(floor(index / (floor(cols/(count/4.0))))); float size_x = cols - 1.0; float size_y = rows - 1.0; float ty = 0.0; if (size_y > 0.0) ty = float(index_y)/size_y; int i = index_x; vec4 _uniform;\n""" _utype = dict([(name, count) for name, count, _ in _utype]) store = 0 # Be very careful with utype name order (_utype.keys is wrong) for name in utype.names: if name == '__unused__': continue count, shift = _utype[name], 0 size = count while count: if store == 0: body += "\n _uniform = texture2D(uniforms, vec2(float(i++)/size_x,ty));\n" # noqa store = 4 if store == 4: a = "xyzw" elif store == 3: a = "yzw" elif store == 2: a = "zw" elif store == 1: a = "w" if shift == 0: b = "xyzw" elif shift == 1: b = "yzw" elif shift == 2: b = "zw" elif shift == 3: b = "w" i = min(min(len(b), count), len(a)) if size > 1: body += " %s%s.%s = _uniform.%s;\n" % (prefix, name, b[:i], a[:i]) # noqa else: body += " %s%s = _uniform.%s;\n" % (prefix, name, a[:i]) count -= i shift += i store -= i body += """}\n\n""" return header + body vispy-0.4.0/vispy/visuals/collections/polygon_collection.py0000664000175000017500000000203112510536123026007 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2014, Nicolas P. Rougier # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ """ from . raw_polygon_collection import RawPolygonCollection #from . agg_polygon_collection import AggPolygonCollection #from . agg_fast_polygon_collection import AggPolygonCollection def PolygonCollection(mode="raw", *args, **kwargs): """ mode: string - "raw" (speed: fastest, size: small, output: ugly, no dash, no thickness) - "agg" (speed: medium, size: medium output: nice, some flaws, no dash) - "agg+" (speed: slow, size: big, output: perfect, no dash) """ # if mode == "raw": return RawPolygonCollection(*args, **kwargs) # elif mode == "agg": # return AggFastPolygonCollection(*args, **kwargs) # return AggPolygonCollection(*args, **kwargs) vispy-0.4.0/vispy/visuals/collections/collection.py0000664000175000017500000002054712510536123024254 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2014, Nicolas P. Rougier # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ A collection is a container for several items having the same data structure (dtype). Each data type can be declared as local (it specific to a vertex), shared (it is shared among an item vertices) or global (it is shared by all vertices). It is based on the BaseCollection but offers a more intuitive interface. """ import numpy as np from ... import gloo from . util import fetchcode from . base_collection import BaseCollection from ..shaders import ModularProgram from ...util.event import EventEmitter class Collection(BaseCollection): """ A collection is a container for several items having the same data structure (dtype). Each data type can be declared as local (it is specific to a vertex), shared (it is shared among item vertices) or global (it is shared by all items). It is based on the BaseCollection but offers a more intuitive interface. Parameters ---------- dtype: list Data individual types as (name, dtype, scope, default) itype: np.dtype or None Indices data type mode : GL_ENUM GL_POINTS, GL_LINES, GL_LINE_STRIP, GL_LINE_LOOP, GL_TRIANGLES, GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN vertex: str or tuple of str Vertex shader to use to draw this collection fragment: str or tuple of str Fragment shader to use to draw this collection kwargs: str Scope can also be specified using keyword argument, where parameter name must be one of the dtype. """ _gtypes = {('float32', 1): "float", ('float32', 2): "vec2", ('float32', 3): "vec3", ('float32', 4): "vec4", ('int32', 1): "int", ('int32', 2): "ivec2", ('int32', 3): "ivec3", ('int32', 4): "ivec4"} def __init__(self, dtype, itype, mode, vertex, fragment, **kwargs): """ """ self._uniforms = {} self._attributes = {} self._varyings = {} self._mode = mode vtype = [] utype = [] self.update = EventEmitter(source=self, type='collection_update') # Build vtype and utype according to parameters declarations = {"uniforms": "", "attributes": "", "varyings": ""} defaults = {} for item in dtype: name, (basetype, count), scope, default = item basetype = np.dtype(basetype).name if scope[0] == "!": scope = scope[1:] else: scope = kwargs.get(name, scope) defaults[name] = default gtype = Collection._gtypes[(basetype, count)] if scope == "local": vtype.append((name, basetype, count)) declarations[ "attributes"] += "attribute %s %s;\n" % (gtype, name) elif scope == "shared": utype.append((name, basetype, count)) declarations["varyings"] += "varying %s %s;\n" % (gtype, name) else: declarations["uniforms"] += "uniform %s %s;\n" % (gtype, name) self._uniforms[name] = None vtype = np.dtype(vtype) itype = np.dtype(itype) if itype else None utype = np.dtype(utype) if utype else None BaseCollection.__init__(self, vtype=vtype, utype=utype, itype=itype) self._declarations = declarations self._defaults = defaults # Build program (once base collection is built) saved = vertex vertex = "" if self.utype is not None: vertex += fetchcode(self.utype) + vertex else: vertex += "void fetch_uniforms(void) { }\n" + vertex vertex += self._declarations["uniforms"] vertex += self._declarations["attributes"] vertex += saved self._vertex = vertex self._fragment = fragment program = ModularProgram(vertex, fragment) program.changed.connect(self.update) self._programs.append(program) # Initialize uniforms for name in self._uniforms.keys(): self._uniforms[name] = self._defaults.get(name) program[name] = self._uniforms[name] def view(self, transform, viewport=None): """ Return a view on the collection using provided transform """ return CollectionView(self, transform, viewport) # program = gloo.Program(self._vertex, self._fragment) # if "transform" in program.hooks: # program["transform"] = transform # if "viewport" in program.hooks: # if viewport is not None: # program["viewport"] = viewport # else: # program["viewport"] = Viewport() # self._programs.append(program) # program.bind(self._vertices_buffer) # for name in self._uniforms.keys(): # program[name] = self._uniforms[name] # #if self._uniforms_list is not None: # # program["uniforms"] = self._uniforms_texture # # program["uniforms_shape"] = self._ushape # # Piggy backing # def draw(): # if self._need_update: # self._update() # program.bind(self._vertices_buffer) # if self._uniforms_list is not None: # program["uniforms"] = self._uniforms_texture # program["uniforms_shape"] = self._ushape # if self._indices_list is not None: # Program.draw(program, self._mode, self._indices_buffer) # else: # Program.draw(program, self._mode) # program.draw = draw # return program def __getitem__(self, key): program = self._programs[0] for name, (storage, _, _) in program._code_variables.items(): if name == key and storage == 'uniform': return program[key] return BaseCollection.__getitem__(self, key) def __setitem__(self, key, value): found = False for program in self._programs: program.build_if_needed() for name, (storage, _, _, _) in program._code_variables.items(): if name == key and storage == 'uniform': found = True program[key] = value if not found: BaseCollection.__setitem__(self, key, value) def draw(self, mode=None): """ Draw collection """ if self._need_update: self._update() program = self._programs[0] mode = mode or self._mode if self._indices_list is not None: program.draw(mode, self._indices_buffer) else: program.draw(mode) class CollectionView(object): def __init__(self, collection, transform=None, viewport=None): vertex = collection._vertex fragment = collection._fragment program = gloo.Program(vertex, fragment) # if "transform" in program.hooks and transform is not None: # program["transform"] = transform # if "viewport" in program.hooks and viewport is not None: # program["viewport"] = viewport program.bind(collection._vertices_buffer) for name in collection._uniforms.keys(): program[name] = collection._uniforms[name] collection._programs.append(program) self._program = program self._collection = collection def __getitem__(self, key): return self._program[key] def __setitem__(self, key, value): self._program[key] = value def draw(self): program = self._program collection = self._collection mode = collection._mode if collection._need_update: collection._update() # self._program.bind(self._vertices_buffer) if collection._uniforms_list is not None: program["uniforms"] = collection._uniforms_texture program["uniforms_shape"] = collection._ushape if collection._indices_list is not None: program.draw(mode, collection._indices_buffer) else: program.draw(mode) vispy-0.4.0/vispy/visuals/collections/agg_segment_collection.py0000664000175000017500000001116212510536123026605 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2014, Nicolas P. Rougier # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Antigrain Geometry Segment Collection This collection provides antialiased and accurate segments with caps. It consume x2 more memory than regular lines and is a bit slower, but the quality of the output is worth the cost. """ import numpy as np from vispy import glsl from . collection import Collection from ..transforms import NullTransform class AggSegmentCollection(Collection): """ Antigrain Geometry Segment Collection This collection provides antialiased and accurate segments with caps. It consume x2 more memory than regular lines and is a bit slower, but the quality of the output is worth the cost. """ def __init__(self, user_dtype=None, transform=None, vertex=None, fragment=None, **kwargs): """ Initialize the collection. Parameters ---------- user_dtype: list The base dtype can be completed (appended) by the used_dtype. It only make sense if user also provide vertex and/or fragment shaders transform : string GLSL Transform code defining the vec4 transform(vec3) function vertex: string Vertex shader code fragment: string Fragment shader code caps : string 'local', 'shared' or 'global' color : string 'local', 'shared' or 'global' linewidth : string 'local', 'shared' or 'global' antialias : string 'local', 'shared' or 'global' """ base_dtype = [('P0', (np.float32, 3), '!local', (0, 0, 0)), ('P1', (np.float32, 3), '!local', (0, 0, 0)), ('index', (np.float32, 1), '!local', 0), ('color', (np.float32, 4), 'shared', (0, 0, 0, 1)), ('linewidth', (np.float32, 1), 'shared', 1), ('antialias', (np.float32, 1), 'shared', 1), ('viewport', (np.float32, 4), 'global', (0, 0, 512, 512))] # noqa dtype = base_dtype if user_dtype: dtype.extend(user_dtype) if vertex is None: vertex = glsl.get('collections/agg-segment.vert') if transform is None: transform = NullTransform() self.transform = transform if fragment is None: fragment = glsl.get('collections/agg-segment.frag') Collection.__init__(self, dtype=dtype, itype=np.uint32, mode="triangles", vertex=vertex, fragment=fragment, **kwargs) self._programs[0].vert['transform'] = self.transform def append(self, P0, P1, itemsize=None, **kwargs): """ Append a new set of segments to the collection. For kwargs argument, n is the number of vertices (local) or the number of item (shared) Parameters ---------- P : np.array Vertices positions of the path(s) to be added itemsize: int or None Size of an individual path caps : list, array or 2-tuple Path start /end cap color : list, array or 4-tuple Path color linewidth : list, array or float Path linewidth antialias : list, array or float Path antialias area """ itemsize = itemsize or 1 itemcount = len(P0) // itemsize V = np.empty(itemcount, dtype=self.vtype) # Apply default values on vertices for name in self.vtype.names: if name not in ['collection_index', 'P0', 'P1', 'index']: V[name] = kwargs.get(name, self._defaults[name]) V['P0'] = P0 V['P1'] = P1 V = V.repeat(4, axis=0) V['index'] = np.resize([0, 1, 2, 3], 4 * itemcount * itemsize) I = np.ones((itemcount, 6), dtype=int) I[:] = 0, 1, 2, 0, 2, 3 I[:] += 4 * np.arange(itemcount)[:, np.newaxis] I = I.ravel() # Uniforms if self.utype: U = np.zeros(itemcount, dtype=self.utype) for name in self.utype.names: if name not in ["__unused__"]: U[name] = kwargs.get(name, self._defaults[name]) else: U = None Collection.append( self, vertices=V, uniforms=U, indices=I, itemsize=4 * itemcount) vispy-0.4.0/vispy/visuals/collections/agg_path_collection.py0000664000175000017500000001506712510536123026107 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2014, Nicolas P. Rougier # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Antigrain Geometry Path Collection This collection provides antialiased and accurate paths with caps and joins. It is memory hungry (x8) and slow (x.25) so it is to be used sparingly, mainly for thick paths where quality is critical. """ import numpy as np from vispy import glsl from vispy.gloo import gl from . collection import Collection from ..transforms import NullTransform class AggPathCollection(Collection): """ Antigrain Geometry Path Collection This collection provides antialiased and accurate paths with caps and joins. It is memory hungry (x8) and slow (x.25) so it is to be used sparingly, mainly for thick paths where quality is critical. """ def __init__(self, user_dtype=None, transform=None, vertex=None, fragment=None, **kwargs): """ Initialize the collection. Parameters ---------- user_dtype: list The base dtype can be completed (appended) by the used_dtype. It only make sense if user also provide vertex and/or fragment shaders transform : Transform instance Used to define the transform(vec4) function vertex: string Vertex shader code fragment: string Fragment shader code caps : string 'local', 'shared' or 'global' join : string 'local', 'shared' or 'global' color : string 'local', 'shared' or 'global' miter_limit : string 'local', 'shared' or 'global' linewidth : string 'local', 'shared' or 'global' antialias : string 'local', 'shared' or 'global' """ base_dtype = [('p0', (np.float32, 3), '!local', (0, 0, 0)), ('p1', (np.float32, 3), '!local', (0, 0, 0)), ('p2', (np.float32, 3), '!local', (0, 0, 0)), ('p3', (np.float32, 3), '!local', (0, 0, 0)), ('uv', (np.float32, 2), '!local', (0, 0)), ('caps', (np.float32, 2), 'global', (0, 0)), ('join', (np.float32, 1), 'global', 0), ('color', (np.float32, 4), 'global', (0, 0, 0, 1)), ('miter_limit', (np.float32, 1), 'global', 4), ('linewidth', (np.float32, 1), 'global', 1), ('antialias', (np.float32, 1), 'global', 1), ('viewport', (np.float32, 4), 'global', (0, 0, 512, 512))] # noqa dtype = base_dtype if user_dtype: dtype.extend(user_dtype) if vertex is None: vertex = glsl.get('collections/agg-path.vert') if transform is None: transform = NullTransform() self.transform = transform if fragment is None: fragment = glsl.get('collections/agg-path.frag') Collection.__init__(self, dtype=dtype, itype=np.uint32, # 16 for WebGL mode="triangles", vertex=vertex, fragment=fragment, **kwargs) self._programs[0].vert['transform'] = self.transform def append(self, P, closed=False, itemsize=None, **kwargs): """ Append a new set of vertices to the collection. For kwargs argument, n is the number of vertices (local) or the number of item (shared) Parameters ---------- P : np.array Vertices positions of the path(s) to be added closed: bool Whether path(s) is/are closed itemsize: int or None Size of an individual path caps : list, array or 2-tuple Path start /end cap join : list, array or float path segment join color : list, array or 4-tuple Path color miter_limit : list, array or float Miter limit for join linewidth : list, array or float Path linewidth antialias : list, array or float Path antialias area """ itemsize = itemsize or len(P) itemcount = len(P) / itemsize # Computes the adjacency information n, p = len(P), P.shape[-1] Z = np.tile(P, 2).reshape(2 * len(P), p) V = np.empty(n, dtype=self.vtype) V['p0'][1:-1] = Z[0::2][:-2] V['p1'][:-1] = Z[1::2][:-1] V['p2'][:-1] = Z[1::2][+1:] V['p3'][:-2] = Z[0::2][+2:] # Apply default values on vertices for name in self.vtype.names: if name not in ['collection_index', 'p0', 'p1', 'p2', 'p3']: V[name] = kwargs.get(name, self._defaults[name]) # Extract relevant segments only V = (V.reshape(n / itemsize, itemsize)[:, :-1]) if closed: V['p0'][:, 0] = V['p2'][:, -1] V['p3'][:, -1] = V['p1'][:, 0] else: V['p0'][:, 0] = V['p1'][:, 0] V['p3'][:, -1] = V['p2'][:, -1] V = V.ravel() # Quadruple each point (we're using 2 triangles / segment) # No shared vertices between segment because of joins V = np.repeat(V, 4, axis=0).reshape((len(V), 4)) V['uv'] = (-1, -1), (-1, +1), (+1, -1), (+1, +1) V = V.ravel() n = itemsize if closed: # uint16 for WebGL I = np.resize( np.array([0, 1, 2, 1, 2, 3], dtype=np.uint32), n * 2 * 3) I += np.repeat(4 * np.arange(n), 6) I[-6:] = 4 * n - 6, 4 * n - 5, 0, 4 * n - 5, 0, 1 else: I = np.resize( np.array([0, 1, 2, 1, 2, 3], dtype=np.uint32), (n - 1) * 2 * 3) I += np.repeat(4 * np.arange(n - 1), 6) I = I.ravel() # Uniforms if self.utype: U = np.zeros(itemcount, dtype=self.utype) for name in self.utype.names: if name not in ["__unused__"]: U[name] = kwargs.get(name, self._defaults[name]) else: U = None Collection.append(self, vertices=V, uniforms=U, indices=I, itemsize=itemsize * 4 - 4) def draw(self, mode="triangles"): """ Draw collection """ gl.glDepthMask(0) Collection.draw(self, mode) gl.glDepthMask(1) vispy-0.4.0/vispy/visuals/collections/raw_segment_collection.py0000664000175000017500000000667412510536123026654 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2014, Nicolas P. Rougier # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Raw Segment Collection This collection provides fast raw (& ugly) line segments. """ import numpy as np from vispy import glsl from . collection import Collection from ..transforms import NullTransform class RawSegmentCollection(Collection): """ Raw Segment Collection This collection provides fast raw (& ugly) line segments. """ def __init__(self, user_dtype=None, transform=None, vertex=None, fragment=None, **kwargs): """ Initialize the collection. Parameters ---------- user_dtype: list The base dtype can be completed (appended) by the used_dtype. It only make sense if user also provide vertex and/or fragment shaders transform : string GLSL Transform code defining the vec4 transform(vec3) function vertex: string Vertex shader code fragment: string Fragment shader code color : string 'local', 'shared' or 'global' """ base_dtype = [("position", (np.float32, 3), "!local", (0, 0, 0)), ("color", (np.float32, 4), "global", (0, 0, 0, 1)), ("viewport", (np.float32, 4), "global", (0, 0, 512, 512)) ] dtype = base_dtype if user_dtype: dtype.extend(user_dtype) if vertex is None: vertex = glsl.get('collections/raw-segment.vert') if transform is None: transform = NullTransform() self.transform = transform if fragment is None: fragment = glsl.get('collections/raw-segment.frag') Collection.__init__(self, dtype=dtype, itype=None, mode='lines', vertex=vertex, fragment=fragment, **kwargs) self._programs[0].vert['transform'] = self.transform def append(self, P0, P1, itemsize=None, **kwargs): """ Append a new set of segments to the collection. For kwargs argument, n is the number of vertices (local) or the number of item (shared) Parameters ---------- P : np.array Vertices positions of the path(s) to be added closed: bool Whether path(s) is/are closed itemsize: int or None Size of an individual path color : list, array or 4-tuple Path color """ itemsize = itemsize or 1 itemcount = len(P0) / itemsize V = np.empty(itemcount, dtype=self.vtype) # Apply default values on vertices for name in self.vtype.names: if name not in ['collection_index', 'P']: V[name] = kwargs.get(name, self._defaults[name]) V = np.repeat(V, 2, axis=0) V['P'][0::2] = P0 V['P'][1::2] = P1 # Uniforms if self.utype: U = np.zeros(itemcount, dtype=self.utype) for name in self.utype.names: if name not in ["__unused__"]: U[name] = kwargs.get(name, self._defaults[name]) else: U = None Collection.append(self, vertices=V, uniforms=U, itemsize=itemsize) vispy-0.4.0/vispy/visuals/collections/raw_triangle_collection.py0000664000175000017500000000505012510536123027002 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2014, Nicolas P. Rougier # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- import numpy as np from vispy import glsl from . collection import Collection from ..transforms import NullTransform class RawTriangleCollection(Collection): """ """ def __init__(self, user_dtype=None, transform=None, vertex=None, fragment=None, **kwargs): base_dtype = [('position', (np.float32, 3), '!local', (0, 0, 0)), ('color', (np.float32, 4), 'local', (0, 0, 0, 1))] dtype = base_dtype if user_dtype: dtype.extend(user_dtype) if vertex is None: vertex = glsl.get('collections/raw-triangle.vert') if transform is None: transform = NullTransform() self.transform = transform if fragment is None: fragment = glsl.get('collections/raw-triangle.frag') Collection.__init__(self, dtype=dtype, itype=np.uint32, mode="triangles", vertex=vertex, fragment=fragment, **kwargs) self._programs[0].vert['transform'] = self.transform def append(self, points, indices, **kwargs): """ Append a new set of vertices to the collection. For kwargs argument, n is the number of vertices (local) or the number of item (shared) Parameters ---------- points : np.array Vertices composing the triangles indices : np.array Indices describing triangles color : list, array or 4-tuple Path color """ itemsize = len(points) itemcount = 1 V = np.empty(itemcount * itemsize, dtype=self.vtype) for name in self.vtype.names: if name not in ['collection_index', 'position']: V[name] = kwargs.get(name, self._defaults[name]) V["position"] = points # Uniforms if self.utype: U = np.zeros(itemcount, dtype=self.utype) for name in self.utype.names: if name not in ["__unused__"]: U[name] = kwargs.get(name, self._defaults[name]) else: U = None I = np.array(indices).ravel() Collection.append(self, vertices=V, uniforms=U, indices=I, itemsize=itemsize) vispy-0.4.0/vispy/visuals/collections/base_collection.py0000664000175000017500000004047312510536123025246 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2014, Nicolas P. Rougier # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ A collection is a container for several (optionally indexed) objects having the same vertex structure (vtype) and same uniforms type (utype). A collection allows to manipulate objects individually and each object can have its own set of uniforms provided they are a combination of floats. """ import math import numpy as np from vispy.gloo import Texture2D, VertexBuffer, IndexBuffer from . util import dtype_reduce from . array_list import ArrayList def next_power_of_2(n): """ Return next power of 2 greater than or equal to n """ n -= 1 # greater than OR EQUAL TO n shift = 1 while (n + 1) & n: # n+1 is not a power of 2 yet n |= n >> shift shift *= 2 return max(4, n + 1) class Item(object): """ An item represent an object within a collection and is created on demand when accessing a specific object of the collection. """ def __init__(self, parent, key, vertices, indices, uniforms): """ Create an item from an existing collection. Parameters ---------- parent : Collection Collection this item belongs to key : int Collection index of this item vertices: array-like Vertices of the item indices: array-like Indices of the item uniforms: array-like Uniform parameters of the item """ self._parent = parent self._key = key self._vertices = vertices self._indices = indices self._uniforms = uniforms @property def vertices(self): return self._vertices @vertices.setter def vertices(self, data): self._vertices[...] = np.array(data) @property def indices(self): return self._indices @indices.setter def indices(self, data): if self._indices is None: raise ValueError("Item has no indices") start = self._parent.vertices._items[self._key][0] self._indices[...] = np.array(data) + start @property def uniforms(self): return self._uniforms @uniforms.setter def uniforms(self, data): if self._uniforms is None: raise ValueError("Item has no associated uniform") self._uniforms[...] = data def __getitem__(self, key): """ Get a specific uniforms value """ if key in self._vertices.dtype.names: return self._vertices[key] elif key in self._uniforms.dtype.names: return self._uniforms[key] else: raise IndexError("Unknown key ('%s')" % key) def __setitem__(self, key, value): """ Set a specific uniforms value """ if key in self._vertices.dtype.names: self._vertices[key] = value elif key in self._uniforms.dtype.names: self._uniforms[key] = value else: raise IndexError("Unknown key") def __str__(self): return "Item (%s, %s, %s)" % (self._vertices, self._indices, self._uniforms) class BaseCollection(object): def __init__(self, vtype, utype=None, itype=None): # Vertices and type (mandatory) self._vertices_list = None self._vertices_buffer = None # Vertex indices and type (optional) self._indices_list = None self._indices_buffer = None # Uniforms and type (optional) self._uniforms_list = None self._uniforms_texture = None # Make sure types are np.dtype (or None) vtype = np.dtype(vtype) if vtype is not None else None itype = np.dtype(itype) if itype is not None else None utype = np.dtype(utype) if utype is not None else None # Vertices type (mandatory) # ------------------------- if vtype.names is None: raise ValueError("vtype must be a structured dtype") # Indices type (optional) # ----------------------- if itype is not None: if itype not in [np.uint8, np.uint16, np.uint32]: raise ValueError("itype must be unsigned integer or None") self._indices_list = ArrayList(dtype=itype) # No program yet self._programs = [] # Need to update buffers & texture self._need_update = True # Uniforms type (optional) # ------------------------- if utype is not None: if utype.names is None: raise ValueError("utype must be a structured dtype") # Convert types to lists (in case they were already dtypes) such # that we can append new fields vtype = eval(str(np.dtype(vtype))) # We add a uniform index to access uniform data vtype.append(('collection_index', 'f4')) vtype = np.dtype(vtype) # Check utype is made of float32 only utype = eval(str(np.dtype(utype))) r_utype = dtype_reduce(utype) if type(r_utype[0]) is not str or r_utype[2] != 'float32': raise RuntimeError("utype cannot be reduced to float32 only") # Make utype divisible by 4 # count = ((r_utype[1]-1)//4+1)*4 # Make utype a power of two count = next_power_of_2(r_utype[1]) if (count - r_utype[1]) > 0: utype.append(('__unused__', 'f4', count - r_utype[1])) self._uniforms_list = ArrayList(dtype=utype) self._uniforms_float_count = count # Reserve some space in texture such that we have # at least one full line shape = self._compute_texture_shape(1) self._uniforms_list.reserve(shape[1] / (count / 4)) # Last since utype may add a new field in vtype (collecion_index) self._vertices_list = ArrayList(dtype=vtype) # Record all types self._vtype = np.dtype(vtype) self._itype = np.dtype(itype) if itype is not None else None self._utype = np.dtype(utype) if utype is not None else None def __len__(self): """ x.__len__() <==> len(x) """ return len(self._vertices_list) @property def vtype(self): """ Vertices dtype """ return self._vtype @property def itype(self): """ Indices dtype """ return self._itype @property def utype(self): """ Uniforms dtype """ return self._utype def append(self, vertices, uniforms=None, indices=None, itemsize=None): """ Parameters ---------- vertices : numpy array An array whose dtype is compatible with self.vdtype uniforms: numpy array An array whose dtype is compatible with self.utype indices : numpy array An array whose dtype is compatible with self.idtype All index values must be between 0 and len(vertices) itemsize: int, tuple or 1-D array If `itemsize is an integer, N, the array will be divided into elements of size N. If such partition is not possible, an error is raised. If `itemsize` is 1-D array, the array will be divided into elements whose succesive sizes will be picked from itemsize. If the sum of itemsize values is different from array size, an error is raised. """ # Vertices # ----------------------------- vertices = np.array(vertices).astype(self.vtype).ravel() vsize = self._vertices_list.size # No itemsize given # ----------------- if itemsize is None: index = 0 count = 1 # Uniform itemsize (int) # ---------------------- elif isinstance(itemsize, int): count = len(vertices) / itemsize index = np.repeat(np.arange(count), itemsize) # Individual itemsize (array) # --------------------------- elif isinstance(itemsize, (np.ndarray, list)): count = len(itemsize) index = np.repeat(np.arange(count), itemsize) else: raise ValueError("Itemsize not understood") if self.utype: vertices["collection_index"] = index + len(self) self._vertices_list.append(vertices, itemsize) # Indices # ----------------------------- if self.itype is not None: # No indices given (-> automatic generation) if indices is None: indices = vsize + np.arange(len(vertices)) self._indices_list.append(indices, itemsize) # Indices given # FIXME: variables indices (list of list or ArrayList) else: if itemsize is None: I = np.array(indices) + vsize elif isinstance(itemsize, int): I = vsize + (np.tile(indices, count) + itemsize * np.repeat(np.arange(count), len(indices))) # noqa else: raise ValueError("Indices not compatible with items") self._indices_list.append(I, len(indices)) # Uniforms # ----------------------------- if self.utype: if uniforms is None: uniforms = np.zeros(count, dtype=self.utype) else: uniforms = np.array(uniforms).astype(self.utype).ravel() self._uniforms_list.append(uniforms, itemsize=1) self._need_update = True def __delitem__(self, index): """ x.__delitem__(y) <==> del x[y] """ # Deleting one item if isinstance(index, int): if index < 0: index += len(self) if index < 0 or index > len(self): raise IndexError("Collection deletion index out of range") istart, istop = index, index + 1 # Deleting several items elif isinstance(index, slice): istart, istop, _ = index.indices(len(self)) if istart > istop: istart, istop = istop, istart if istart == istop: return # Deleting everything elif index is Ellipsis: istart, istop = 0, len(self) # Error else: raise TypeError("Collection deletion indices must be integers") vsize = len(self._vertices_list[index]) if self.itype is not None: del self._indices_list[index] self._indices_list[index:] -= vsize if self.utype: self._vertices_list[index:]["collection_index"] -= istop - istart del self._vertices_list[index] if self.utype is not None: del self._uniforms_list[index] self._need_update = True def __getitem__(self, key): """ """ # WARNING # Here we want to make sure to use buffers and texture (instead of # lists) since only them are aware of any external modification. if self._need_update: self._update() V = self._vertices_buffer I = None U = None if self._indices_list is not None: I = self._indices_buffer if self._uniforms_list is not None: U = self._uniforms_texture.data.ravel().view(self.utype) # Getting a whole field if isinstance(key, str): # Getting a named field from vertices if key in V.dtype.names: return V[key] # Getting a named field from uniforms elif U is not None and key in U.dtype.names: # Careful, U is the whole texture that can be bigger than list # return U[key] return U[key][:len(self._uniforms_list)] else: raise IndexError("Unknown field name ('%s')" % key) # Getting individual item elif isinstance(key, int): vstart, vend = self._vertices_list._items[key] vertices = V[vstart:vend] indices = None uniforms = None if I is not None: istart, iend = self._indices_list._items[key] indices = I[istart:iend] if U is not None: ustart, uend = self._uniforms_list._items[key] uniforms = U[ustart:uend] return Item(self, key, vertices, indices, uniforms) # Error else: raise IndexError("Cannot get more than one item at once") def __setitem__(self, key, data): """ x.__setitem__(i, y) <==> x[i]=y """ # if len(self._programs): # found = False # for program in self._programs: # if key in program.hooks: # program[key] = data # found = True # if found: return # WARNING # Here we want to make sure to use buffers and texture (instead of # lists) since only them are aware of any external modification. if self._need_update: self._update() V = self._vertices_buffer I = None U = None if self._indices_list is not None: I = self._indices_buffer # noqa if self._uniforms_list is not None: U = self._uniforms_texture.data.ravel().view(self.utype) # Setting a whole field if isinstance(key, str): # Setting a named field in vertices if key in self.vtype.names: V[key] = data # Setting a named field in uniforms elif self.utype and key in self.utype.names: # Careful, U is the whole texture that can be bigger than list # U[key] = data U[key][:len(self._uniforms_list)] = data else: raise IndexError("Unknown field name ('%s')" % key) # # Setting individual item # elif isinstance(key, int): # #vstart, vend = self._vertices_list._items[key] # #istart, iend = self._indices_list._items[key] # #ustart, uend = self._uniforms_list._items[key] # vertices, indices, uniforms = data # del self[key] # self.insert(key, vertices, indices, uniforms) else: raise IndexError("Cannot set more than one item") def _compute_texture_shape(self, size=1): """ Compute uniform texture shape """ # We should use this line but we may not have a GL context yet # linesize = gl.glGetInteger(gl.GL_MAX_TEXTURE_SIZE) linesize = 1024 count = self._uniforms_float_count cols = linesize // float(count / 4) rows = max(1, int(math.ceil(size / float(cols)))) shape = rows, cols * (count / 4), count self._ushape = shape return shape def _update(self): """ Update vertex buffers & texture """ if self._vertices_buffer is not None: self._vertices_buffer.delete() self._vertices_buffer = VertexBuffer(self._vertices_list.data) if self.itype is not None: if self._indices_buffer is not None: self._indices_buffer.delete() self._indices_buffer = IndexBuffer(self._indices_list.data) if self.utype is not None: if self._uniforms_texture is not None: self._uniforms_texture.delete() # We take the whole array (_data), not the data one texture = self._uniforms_list._data.view(np.float32) size = len(texture) / self._uniforms_float_count shape = self._compute_texture_shape(size) # shape[2] = float count is only used in vertex shader code texture = texture.reshape(shape[0], shape[1], 4) self._uniforms_texture = Texture2D(texture) self._uniforms_texture.data = texture self._uniforms_texture.interpolation = 'nearest' if len(self._programs): for program in self._programs: program.bind(self._vertices_buffer) if self._uniforms_list is not None: program["uniforms"] = self._uniforms_texture program["uniforms_shape"] = self._ushape vispy-0.4.0/vispy/visuals/collections/agg_fast_path_collection.py0000664000175000017500000001666412510536123027130 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2014, Nicolas P. Rougier # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Antigrain Geometry Fast Path Collection This collection provides antialiased and accurate paths with caps and miter joins. It consume x4 more memory than regular lines and is a bit slower, but the quality of the output is worth the cost. Note that no control can be made on miter joins which may result in some glitches on screen. """ import numpy as np from vispy import glsl from vispy.gloo import gl from . collection import Collection from ..transforms import NullTransform class AggFastPathCollection(Collection): """ Antigrain Geometry Fast Path Collection This collection provides antialiased and accurate paths with caps and miter joins. It consume x4 more memory than regular lines and is a bit slower, but the quality of the output is worth the cost. Note that no control can be made on miter joins which may result in some glitches on screen. """ def __init__(self, user_dtype=None, transform=None, vertex=None, fragment=None, **kwargs): """ Initialize the collection. Parameters ---------- user_dtype: list The base dtype can be completed (appended) by the used_dtype. It only make sense if user also provide vertex and/or fragment shaders transform : string GLSL Transform code defining the vec4 transform(vec3) function vertex: string Vertex shader code fragment: string Fragment shader code caps : string 'local', 'shared' or 'global' color : string 'local', 'shared' or 'global' linewidth : string 'local', 'shared' or 'global' antialias : string 'local', 'shared' or 'global' """ base_dtype = [('prev', (np.float32, 3), '!local', (0, 0, 0)), ('curr', (np.float32, 3), '!local', (0, 0, 0)), ('next', (np.float32, 3), '!local', (0, 0, 0)), ('id', (np.float32, 1), '!local', 0), ('color', (np.float32, 4), 'global', (0, 0, 0, 1)), ('linewidth', (np.float32, 1), 'global', 1), ('antialias', (np.float32, 1), 'global', 1), ("viewport", (np.float32, 4), 'global', (0, 0, 512, 512))] # noqa dtype = base_dtype if user_dtype: dtype.extend(user_dtype) if vertex is None: vertex = glsl.get('collections/agg-fast-path.vert') if transform is None: transform = NullTransform() self.transform = transform if fragment is None: fragment = glsl.get('collections/agg-fast-path.frag') Collection.__init__(self, dtype=dtype, itype=None, mode="triangle_strip", vertex=vertex, fragment=fragment, **kwargs) program = self._programs[0] program.vert['transform'] = self.transform def append(self, P, closed=False, itemsize=None, **kwargs): """ Append a new set of vertices to the collection. For kwargs argument, n is the number of vertices (local) or the number of item (shared) Parameters ---------- P : np.array Vertices positions of the path(s) to be added closed: bool Whether path(s) is/are closed itemsize: int or None Size of an individual path caps : list, array or 2-tuple Path start /end cap color : list, array or 4-tuple Path color linewidth : list, array or float Path linewidth antialias : list, array or float Path antialias area """ itemsize = itemsize or len(P) itemcount = len(P) / itemsize P = P.reshape(itemcount, itemsize, 3) if closed: V = np.empty((itemcount, itemsize + 3), dtype=self.vtype) # Apply default values on vertices for name in self.vtype.names: if name not in ['collection_index', 'prev', 'curr', 'next']: V[name][1:-2] = kwargs.get(name, self._defaults[name]) V['prev'][:, 2:-1] = P V['prev'][:, 1] = V['prev'][:, -2] V['curr'][:, 1:-2] = P V['curr'][:, -2] = V['curr'][:, 1] V['next'][:, 0:-3] = P V['next'][:, -3] = V['next'][:, 0] V['next'][:, -2] = V['next'][:, 1] else: V = np.empty((itemcount, itemsize + 2), dtype=self.vtype) # Apply default values on vertices for name in self.vtype.names: if name not in ['collection_index', 'prev', 'curr', 'next']: V[name][1:-1] = kwargs.get(name, self._defaults[name]) V['prev'][:, 2:] = P V['prev'][:, 1] = V['prev'][:, 2] V['curr'][:, 1:-1] = P V['next'][:, :-2] = P V['next'][:, -2] = V['next'][:, -3] V[:, 0] = V[:, 1] V[:, -1] = V[:, -2] V = V.ravel() V = np.repeat(V, 2, axis=0) V['id'] = np.tile([1, -1], len(V) / 2) if closed: V = V.reshape(itemcount, 2 * (itemsize + 3)) else: V = V.reshape(itemcount, 2 * (itemsize + 2)) V["id"][:, :2] = 2, -2 V["id"][:, -2:] = 2, -2 V = V.ravel() # Uniforms if self.utype: U = np.zeros(itemcount, dtype=self.utype) for name in self.utype.names: if name not in ["__unused__"]: U[name] = kwargs.get(name, self._defaults[name]) else: U = None Collection.append(self, vertices=V, uniforms=U, itemsize=2 * (itemsize + 2 + closed)) def bake(self, P, key='curr', closed=False, itemsize=None): """ Given a path P, return the baked vertices as they should be copied in the collection if the path has already been appended. Example: -------- paths.append(P) P *= 2 paths['prev'][0] = bake(P,'prev') paths['curr'][0] = bake(P,'curr') paths['next'][0] = bake(P,'next') """ itemsize = itemsize or len(P) itemcount = len(P) / itemsize # noqa n = itemsize if closed: I = np.arange(n + 3) if key == 'prev': I -= 2 I[0], I[1], I[-1] = n - 1, n - 1, n - 1 elif key == 'next': I[0], I[-3], I[-2], I[-1] = 1, 0, 1, 1 else: I -= 1 I[0], I[-1], I[n + 1] = 0, 0, 0 else: I = np.arange(n + 2) if key == 'prev': I -= 2 I[0], I[1], I[-1] = 0, 0, n - 2 elif key == 'next': I[0], I[-1], I[-2] = 1, n - 1, n - 1 else: I -= 1 I[0], I[-1] = 0, n - 1 I = np.repeat(I, 2) return P[I] def draw(self, mode="triangle_strip"): """ Draw collection """ gl.glDepthMask(gl.GL_FALSE) Collection.draw(self, mode) gl.glDepthMask(gl.GL_TRUE) vispy-0.4.0/vispy/visuals/collections/__init__.py0000664000175000017500000000213012510536123023644 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Collections allow batch rendering of object of the same type: - Points - Line segments - Polylines (paths) - Raw Triangles - Polygons Each collection has several modes: - raw (point, segment, path, triangle, polygon) - agg (point, segment, path, polygon) - agg+ (path, polygon) Note: Storage of shared attributes requires non-clamped textures which is not the case on all graphic cards. This means such shared attributes must be normalized on CPU and scales back on GPU (in shader code). """ from . path_collection import PathCollection # noqa from . point_collection import PointCollection # noqa from . polygon_collection import PolygonCollection # noqa from . segment_collection import SegmentCollection # noqa from . triangle_collection import TriangleCollection # noqa vispy-0.4.0/vispy/visuals/collections/point_collection.py0000664000175000017500000000134512510536123025460 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2014, Nicolas P. Rougier # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- from . raw_point_collection import RawPointCollection from . agg_point_collection import AggPointCollection def PointCollection(mode="raw", *args, **kwargs): """ mode: string - "raw" (speed: fastest, size: small, output: ugly) - "agg" (speed: fast, size: small, output: beautiful) """ if mode == "raw": return RawPointCollection(*args, **kwargs) return AggPointCollection(*args, **kwargs) vispy-0.4.0/vispy/visuals/collections/raw_polygon_collection.py0000664000175000017500000000515412510536123026671 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2014, Nicolas P. Rougier # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- import numpy as np from vispy import glsl from . collection import Collection from ..transforms import NullTransform from ...geometry import triangulate class RawPolygonCollection(Collection): def __init__(self, user_dtype=None, transform=None, vertex=None, fragment=None, **kwargs): base_dtype = [('position', (np.float32, 3), '!local', (0, 0, 0)), ('color', (np.float32, 4), 'local', (0, 0, 0, 1))] dtype = base_dtype if user_dtype: dtype.extend(user_dtype) if vertex is None: vertex = glsl.get('collections/raw-triangle.vert') if transform is None: transform = NullTransform() self.transform = transform if fragment is None: fragment = glsl.get('collections/raw-triangle.frag') Collection.__init__(self, dtype=dtype, itype=np.uint32, # 16 for WebGL mode="triangles", vertex=vertex, fragment=fragment, **kwargs) # Set hooks if necessary program = self._programs[0] program.vert['transform'] = self.transform def append(self, points, **kwargs): """ Append a new set of vertices to the collection. For kwargs argument, n is the number of vertices (local) or the number of item (shared) Parameters ---------- points : np.array Vertices composing the triangles color : list, array or 4-tuple Path color """ vertices, indices = triangulate(points) itemsize = len(vertices) itemcount = 1 V = np.empty(itemcount * itemsize, dtype=self.vtype) for name in self.vtype.names: if name not in ['collection_index', 'position']: V[name] = kwargs.get(name, self._defaults[name]) V["position"] = vertices # Uniforms if self.utype: U = np.zeros(itemcount, dtype=self.utype) for name in self.utype.names: if name not in ["__unused__"]: U[name] = kwargs.get(name, self._defaults[name]) else: U = None I = np.array(indices).ravel() Collection.append(self, vertices=V, uniforms=U, indices=I, itemsize=itemsize) vispy-0.4.0/vispy/visuals/collections/agg_point_collection.py0000664000175000017500000000326612510536123026302 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2014, Nicolas P. Rougier # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Antigrain Geometry Point Collection This collection provides fast points. Output quality is perfect. """ from vispy import glsl from . raw_point_collection import RawPointCollection class AggPointCollection(RawPointCollection): """ Antigrain Geometry Point Collection This collection provides fast points. Output quality is perfect. """ def __init__(self, user_dtype=None, transform=None, vertex=None, fragment=None, **kwargs): """ Initialize the collection. Parameters ---------- user_dtype: list The base dtype can be completed (appended) by the used_dtype. It only make sense if user also provide vertex and/or fragment shaders vertex: string Vertex shader code fragment: string Fragment shader code transform : Transform instance Used to define the GLSL transform(vec4) function color : string 'local', 'shared' or 'global' """ if vertex is None: vertex = glsl.get("collections/agg-point.vert") if fragment is None: fragment = glsl.get("collections/agg-point.frag") RawPointCollection.__init__(self, user_dtype=user_dtype, transform=transform, vertex=vertex, fragment=fragment, **kwargs) vispy-0.4.0/vispy/visuals/collections/segment_collection.py0000664000175000017500000000143612510536123025772 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2014, Nicolas P. Rougier # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- from . raw_segment_collection import RawSegmentCollection from . agg_segment_collection import AggSegmentCollection def SegmentCollection(mode="agg-fast", *args, **kwargs): """ mode: string - "raw" (speed: fastest, size: small, output: ugly, no dash, no thickness) - "agg" (speed: slower, size: medium, output: perfect, no dash) """ if mode == "raw": return RawSegmentCollection(*args, **kwargs) return AggSegmentCollection(*args, **kwargs) vispy-0.4.0/vispy/visuals/collections/raw_path_collection.py0000664000175000017500000001005612510536123026133 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2014, Nicolas P. Rougier # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- import numpy as np from vispy import glsl from . collection import Collection from ..transforms import NullTransform class RawPathCollection(Collection): """ """ def __init__(self, user_dtype=None, transform=None, vertex=None, fragment=None, **kwargs): """ Initialize the collection. Parameters ---------- user_dtype: list The base dtype can be completed (appended) by the used_dtype. It only make sense if user also provide vertex and/or fragment shaders transform : Transform instance Used to define the transform(vec4) function vertex: string Vertex shader code fragment: string Fragment shader code color : string 'local', 'shared' or 'global' """ base_dtype = [('position', (np.float32, 3), '!local', (0, 0, 0)), ('id', (np.float32, 1), '!local', 0), ('color', (np.float32, 4), 'local', (0, 0, 0, 1)), ("linewidth", (np.float32, 1), 'global', 1), ("viewport", (np.float32, 4), 'global', (0, 0, 512, 512)) ] dtype = base_dtype if user_dtype: dtype.extend(user_dtype) if vertex is None: vertex = glsl.get('collections/raw-path.vert') if transform is None: transform = NullTransform() self.transform = transform if fragment is None: fragment = glsl.get('collections/raw-path.frag') vertex = transform + vertex Collection.__init__(self, dtype=dtype, itype=None, mode='line_strip', vertex=vertex, fragment=fragment, **kwargs) self._programs[0].vert['transform'] = self.transform def append(self, P, closed=False, itemsize=None, **kwargs): """ Append a new set of vertices to the collection. For kwargs argument, n is the number of vertices (local) or the number of item (shared) Parameters ---------- P : np.array Vertices positions of the path(s) to be added closed: bool Whether path(s) is/are closed itemsize: int or None Size of an individual path color : list, array or 4-tuple Path color """ itemsize = itemsize or len(P) itemcount = len(P) / itemsize P = P.reshape(itemcount, itemsize, 3) if closed: V = np.empty((itemcount, itemsize + 3), dtype=self.vtype) # Apply default values on vertices for name in self.vtype.names: if name not in ['collection_index', 'position']: V[name][1:-2] = kwargs.get(name, self._defaults[name]) V["position"][:, 1:-2] = P V["position"][:, -2] = V["position"][:, 1] else: V = np.empty((itemcount, itemsize + 2), dtype=self.vtype) # Apply default values on vertices for name in self.vtype.names: if name not in ['collection_index', 'position']: V[name][1:-1] = kwargs.get(name, self._defaults[name]) V["position"][:, 1:-1] = P V["id"] = 1 V[:, 0] = V[:, 1] V[:, -1] = V[:, -2] V["id"][:, 0] = 0 V["id"][:, -1] = 0 # Uniforms if self.utype: U = np.zeros(itemcount, dtype=self.utype) for name in self.utype.names: if name not in ["__unused__"]: U[name] = kwargs.get(name, self._defaults[name]) else: U = None Collection.append(self, vertices=V, uniforms=U, itemsize=itemsize + 2 + closed) vispy-0.4.0/vispy/visuals/collections/raw_point_collection.py0000664000175000017500000000675112510536123026337 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2014, Nicolas P. Rougier # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Raw Point Collection This collection provides very fast points. Output quality is ugly so it must be used at small size only (2/3 pixels). You've been warned. """ import numpy as np from vispy import glsl from . collection import Collection from ..transforms import NullTransform class RawPointCollection(Collection): """ Raw Point Collection This collection provides very fast points. Output quality is ugly so it must be used at small size only (2/3 pixels). You've been warned. """ def __init__(self, user_dtype=None, transform=None, vertex=None, fragment=None, **kwargs): """ Initialize the collection. Parameters ---------- user_dtype: list The base dtype can be completed (appended) by the used_dtype. It only make sense if user also provide vertex and/or fragment shaders transform : Transform instance Used to define the transform(vec4) function vertex: string Vertex shader code fragment: string Fragment shader code color : string 'local', 'shared' or 'global' """ base_dtype = [('position', (np.float32, 3), "!local", (0, 0, 0)), ('size', (np.float32, 1), "global", 3.0), ('color', (np.float32, 4), "global", (0, 0, 0, 1))] dtype = base_dtype if user_dtype: dtype.extend(user_dtype) if vertex is None: vertex = glsl.get("collections/raw-point.vert") if transform is None: transform = NullTransform() self.transform = transform if fragment is None: fragment = glsl.get("collections/raw-point.frag") Collection.__init__(self, dtype=dtype, itype=None, mode="points", vertex=vertex, fragment=fragment, **kwargs) # Set hooks if necessary program = self._programs[0] program.vert['transform'] = self.transform def append(self, P, itemsize=None, **kwargs): """ Append a new set of vertices to the collection. For kwargs argument, n is the number of vertices (local) or the number of item (shared) Parameters ---------- P : np.array Vertices positions of the points(s) to be added itemsize: int or None Size of an individual path color : list, array or 4-tuple Path color """ itemsize = itemsize or 1 itemcount = len(P) / itemsize V = np.empty(len(P), dtype=self.vtype) # Apply default values on vertices for name in self.vtype.names: if name not in ['position', "collection_index"]: V[name] = kwargs.get(name, self._defaults[name]) V["position"] = P # Uniforms if self.utype: U = np.zeros(itemcount, dtype=self.utype) for name in self.utype.names: if name not in ["__unused__"]: U[name] = kwargs.get(name, self._defaults[name]) else: U = None Collection.append(self, vertices=V, uniforms=U, itemsize=itemsize) vispy-0.4.0/vispy/visuals/collections/path_collection.py0000664000175000017500000000174112510536123025263 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2014, Nicolas P. Rougier # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- from . raw_path_collection import RawPathCollection from . agg_path_collection import AggPathCollection from . agg_fast_path_collection import AggFastPathCollection def PathCollection(mode="agg", *args, **kwargs): """ mode: string - "raw" (speed: fastest, size: small, output: ugly, no dash, no thickness) - "agg" (speed: medium, size: medium output: nice, some flaws, no dash) - "agg+" (speed: slow, size: big, output: perfect, no dash) """ if mode == "raw": return RawPathCollection(*args, **kwargs) elif mode == "agg+": return AggPathCollection(*args, **kwargs) return AggFastPathCollection(*args, **kwargs) vispy-0.4.0/vispy/visuals/collections/array_list.py0000664000175000017500000003342612510536123024272 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2014, Nicolas P. Rougier. All rights reserved. # Distributed under the terms of the new BSD License. # ----------------------------------------------------------------------------- """ An ArrayList is a strongly typed list whose type can be anything that can be interpreted as a numpy data type. Example ------- >>> L = ArrayList( [[0], [1,2], [3,4,5], [6,7,8,9]] ) >>> print L [ [0] [1 2] [3 4 5] [6 7 8 9] ] >>> print L.data [0 1 2 3 4 5 6 7 8 9] You can add several items at once by specifying common or individual size: a single scalar means all items are the same size while a list of sizes is used to specify individual item sizes. Example ------- >>> L = ArrayList( np.arange(10), [3,3,4]) >>> print L [ [0 1 2] [3 4 5] [6 7 8 9] ] >>> print L.data [0 1 2 3 4 5 6 7 8 9] """ import numpy as np class ArrayList(object): """ An ArrayList is a strongly typed list whose type can be anything that can be interpreted as a numpy data type. """ def __init__(self, data=None, itemsize=None, dtype=float, sizeable=True, writeable=True): """ Create a new buffer using given data and sizes or dtype Parameters ---------- data : array_like An array, any object exposing the array interface, an object whose __array__ method returns an array, or any (nested) sequence. itemsize: int or 1-D array If `itemsize is an integer, N, the array will be divided into elements of size N. If such partition is not possible, an error is raised. If `itemsize` is 1-D array, the array will be divided into elements whose succesive sizes will be picked from itemsize. If the sum of itemsize values is different from array size, an error is raised. dtype: np.dtype Any object that can be interpreted as a numpy data type. sizeable : boolean Indicate whether item can be appended/inserted/deleted writeable : boolean Indicate whether content can be changed """ self._sizeable = sizeable self._writeable = writeable if data is not None: if isinstance(data, (list, tuple)): if isinstance(data[0], (list, tuple)): itemsize = [len(l) for l in data] data = [item for sublist in data for item in sublist] self._data = np.array(data, copy=False) self._size = self._data.size # Default is one group with all data inside _itemsize = np.ones(1) * self._data.size # Check item sizes and get items count if itemsize is not None: if isinstance(itemsize, int): if (self._size % itemsize) != 0: raise ValueError("Cannot partition data as requested") self._count = self._size // itemsize _itemsize = np.ones( self._count, dtype=int) * (self._size // self._count) else: _itemsize = np.array(itemsize, copy=False) self._count = len(itemsize) if _itemsize.sum() != self._size: raise ValueError("Cannot partition data as requested") else: self._count = 1 # Store items self._items = np.zeros((self._count, 2), int) C = _itemsize.cumsum() self._items[1:, 0] += C[:-1] self._items[0:, 1] += C else: self._data = np.zeros(1, dtype=dtype) self._items = np.zeros((1, 2), dtype=int) self._size = 0 self._count = 0 @property def data(self): """ The array's elements, in memory. """ return self._data[:self._size] @property def size(self): """ Number of base elements, in memory. """ return self._size @property def itemsize(self): """ Individual item sizes """ return self._items[:self._count, 1] - self._items[:self._count, 0] @property def dtype(self): """ Describes the format of the elements in the buffer. """ return self._data.dtype def reserve(self, capacity): """ Set current capacity of the underlying array""" if capacity >= self._data.size: capacity = int(2 ** np.ceil(np.log2(capacity))) self._data = np.resize(self._data, capacity) def __len__(self): """ x.__len__() <==> len(x) """ return self._count def __str__(self): s = '[ ' for item in self: s += str(item) + ' ' s += ']' return s def __getitem__(self, key): """ x.__getitem__(y) <==> x[y] """ if isinstance(key, int): if key < 0: key += len(self) if key < 0 or key >= len(self): raise IndexError("Tuple index out of range") dstart = self._items[key][0] dstop = self._items[key][1] return self._data[dstart:dstop] elif isinstance(key, slice): istart, istop, step = key.indices(len(self)) if istart > istop: istart, istop = istop, istart dstart = self._items[istart][0] if istart == istop: dstop = dstart else: dstop = self._items[istop - 1][1] return self._data[dstart:dstop] elif isinstance(key, str): return self._data[key][:self._size] elif key is Ellipsis: return self.data else: raise TypeError("List indices must be integers") def __setitem__(self, key, data): """ x.__setitem__(i, y) <==> x[i]=y """ if not self._writeable: raise AttributeError("List is not writeable") if isinstance(key, (int, slice)): if isinstance(key, int): if key < 0: key += len(self) if key < 0 or key > len(self): raise IndexError("List assignment index out of range") dstart = self._items[key][0] dstop = self._items[key][1] istart = key elif isinstance(key, slice): istart, istop, step = key.indices(len(self)) if istart == istop: return if istart > istop: istart, istop = istop, istart if istart > len(self) or istop > len(self): raise IndexError("Can only assign iterable") dstart = self._items[istart][0] if istart == istop: dstop = dstart else: dstop = self._items[istop - 1][1] if hasattr(data, "__len__"): if len(data) == dstop - dstart: # or len(data) == 1: self._data[dstart:dstop] = data else: self.__delitem__(key) self.insert(istart, data) else: # we assume len(data) = 1 if dstop - dstart == 1: self._data[dstart:dstop] = data else: self.__delitem__(key) self.insert(istart, data) elif key is Ellipsis: self.data[...] = data elif isinstance(key, str): self._data[key][:self._size] = data else: raise TypeError("List assignment indices must be integers") def __delitem__(self, key): """ x.__delitem__(y) <==> del x[y] """ if not self._sizeable: raise AttributeError("List is not sizeable") # Deleting a single item if isinstance(key, int): if key < 0: key += len(self) if key < 0 or key > len(self): raise IndexError("List deletion index out of range") istart, istop = key, key + 1 dstart, dstop = self._items[key] # Deleting several items elif isinstance(key, slice): istart, istop, step = key.indices(len(self)) if istart > istop: istart, istop = istop, istart if istart == istop: return dstart = self._items[istart][0] dstop = self._items[istop - 1][1] elif key is Ellipsis: istart = 0 istop = len(self) dstart = 0 dstop = self.size # Error else: raise TypeError("List deletion indices must be integers") # Remove data size = self._size - (dstop - dstart) self._data[ dstart:dstart + self._size - dstop] = self._data[dstop:self._size] self._size -= dstop - dstart # Remove corresponding items size = self._count - istop self._items[istart:istart + size] = self._items[istop:istop + size] # Update other items size = dstop - dstart self._items[istart:istop + size + 1] -= size, size self._count -= istop - istart def insert(self, index, data, itemsize=None): """ Insert data before index Parameters ---------- index : int Index before which data will be inserted. data : array_like An array, any object exposing the array interface, an object whose __array__ method returns an array, or any (nested) sequence. itemsize: int or 1-D array If `itemsize is an integer, N, the array will be divided into elements of size N. If such partition is not possible, an error is raised. If `itemsize` is 1-D array, the array will be divided into elements whose succesive sizes will be picked from itemsize. If the sum of itemsize values is different from array size, an error is raised. """ if not self._sizeable: raise AttributeError("List is not sizeable") if isinstance(data, (list, tuple)) and isinstance(data[0], (list, tuple)): # noqa itemsize = [len(l) for l in data] data = [item for sublist in data for item in sublist] data = np.array(data, copy=False).ravel() size = data.size # Check item size and get item number if itemsize is not None: if isinstance(itemsize, int): if (size % itemsize) != 0: raise ValueError("Cannot partition data as requested") _count = size // itemsize _itemsize = np.ones(_count, dtype=int) * (size // _count) else: _itemsize = np.array(itemsize, copy=False) _count = len(itemsize) if _itemsize.sum() != size: raise ValueError("Cannot partition data as requested") else: _count = 1 # Check if data array is big enough and resize it if necessary if self._size + size >= self._data.size: capacity = int(2 ** np.ceil(np.log2(self._size + size))) self._data = np.resize(self._data, capacity) # Check if item array is big enough and resize it if necessary if self._count + _count >= len(self._items): capacity = int(2 ** np.ceil(np.log2(self._count + _count))) self._items = np.resize(self._items, (capacity, 2)) # Check index if index < 0: index += len(self) if index < 0 or index > len(self): raise IndexError("List insertion index out of range") # Inserting if index < self._count: istart = index dstart = self._items[istart][0] dstop = self._items[istart][1] # Move data Z = self._data[dstart:self._size] self._data[dstart + size:self._size + size] = Z # Update moved items I = self._items[istart:self._count] + size self._items[istart + _count:self._count + _count] = I # Appending else: dstart = self._size istart = self._count # Only one item (faster) if _count == 1: # Store data self._data[dstart:dstart + size] = data self._size += size # Store data location (= item) self._items[istart][0] = dstart self._items[istart][1] = dstart + size self._count += 1 # Several items else: # Store data dstop = dstart + size self._data[dstart:dstop] = data self._size += size # Store items items = np.ones((_count, 2), int) * dstart C = _itemsize.cumsum() items[1:, 0] += C[:-1] items[0:, 1] += C istop = istart + _count self._items[istart:istop] = items self._count += _count def append(self, data, itemsize=None): """ Append data to the end. Parameters ---------- data : array_like An array, any object exposing the array interface, an object whose __array__ method returns an array, or any (nested) sequence. itemsize: int or 1-D array If `itemsize is an integer, N, the array will be divided into elements of size N. If such partition is not possible, an error is raised. If `itemsize` is 1-D array, the array will be divided into elements whose succesive sizes will be picked from itemsize. If the sum of itemsize values is different from array size, an error is raised. """ self.insert(len(self), data, itemsize) vispy-0.4.0/vispy/visuals/collections/triangle_collection.py0000664000175000017500000000116212510536123026131 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2014, Nicolas P. Rougier # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- from . raw_triangle_collection import RawTriangleCollection def TriangleCollection(mode="raw", *args, **kwargs): """ mode: string - "raw" (speed: fastest, size: small, output: ugly) - "agg" (speed: fast, size: small, output: beautiful) """ return RawTriangleCollection(*args, **kwargs) vispy-0.4.0/vispy/visuals/shaders/0000775000175000017500000000000012527674621020670 5ustar larsonerlarsoner00000000000000vispy-0.4.0/vispy/visuals/shaders/expression.py0000664000175000017500000000553612527672621023450 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from ...ext.six import string_types from .shader_object import ShaderObject class Expression(ShaderObject): """ Base class for expressions (ShaderObjects that do not have a definition nor dependencies) """ def definition(self, names): # expressions are declared inline. return None class TextExpression(Expression): """ Plain GLSL text to insert inline """ def __init__(self, text): super(TextExpression, self).__init__() if not isinstance(text, string_types): raise TypeError("Argument must be string.") self._text = text def __repr__(self): return '' % (self.text, id(self)) def expression(self, names=None): return self._text @property def text(self): return self._text @text.setter def text(self, t): self._text = t self.changed() def __eq__(self, a): if isinstance(a, TextExpression): return a._text == self._text elif isinstance(a, string_types): return a == self._text else: return False def __hash__(self): return self._text.__hash__() class FunctionCall(Expression): """ Representation of a call to a function Essentially this is container for a Function along with its signature. """ def __init__(self, function, args): from .function import Function super(FunctionCall, self).__init__() if not isinstance(function, Function): raise TypeError('FunctionCall needs a Function') sig_len = len(function.args) if len(args) != sig_len: raise TypeError('Function %s requires %d arguments (got %d)' % (function.name, sig_len, len(args))) # Ensure all expressions sig = function.args self._function = function # Convert all arguments to ShaderObject, using arg name if possible. self._args = [ShaderObject.create(arg, ref=sig[i][1]) for i, arg in enumerate(args)] self._add_dep(function) for arg in self._args: self._add_dep(arg) def __repr__(self): return '' % (self.function.name, id(self)) @property def function(self): return self._function @property def dtype(self): return self._function.rtype def expression(self, names): str_args = [arg.expression(names) for arg in self._args] args = ', '.join(str_args) fname = self.function.expression(names) return '%s(%s)' % (fname, args) vispy-0.4.0/vispy/visuals/shaders/shader_object.py0000664000175000017500000001343212527672621024037 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from weakref import WeakKeyDictionary from ...util import logger from ...ext.ordereddict import OrderedDict from ...ext.six import string_types from .compiler import Compiler class ShaderObject(object): """ Base class for all objects that may be included in a GLSL program (Functions, Variables, Expressions). Shader objects have a *definition* that defines the object in GLSL, an *expression* that is used to reference the object, and a set of *dependencies* that must be declared before the object is used. Dependencies are tracked hierarchically such that changes to any object will be propagated up the dependency hierarchy to trigger a recompile. """ @classmethod def create(self, obj, ref=None): """ Convert *obj* to a new ShaderObject. If the output is a Variable with no name, then set its name using *ref*. """ if isinstance(ref, Variable): ref = ref.name elif isinstance(ref, string_types) and ref.startswith('gl_'): # gl_ names not allowed for variables ref = ref[3:].lower() # Allow any type of object to be converted to ShaderObject if it # provides a magic method: if hasattr(obj, '_shader_object'): obj = obj._shader_object() if isinstance(obj, ShaderObject): if isinstance(obj, Variable) and obj.name is None: obj.name = ref elif isinstance(obj, string_types): obj = TextExpression(obj) else: obj = Variable(ref, obj) # Try prepending the name to indicate attribute, uniform, varying if obj.vtype and obj.vtype[0] in 'auv': obj.name = obj.vtype[0] + '_' + obj.name return obj def __init__(self): # objects that must be declared before this object's definition. # {obj: refcount} self._deps = OrderedDict() # OrderedDict for consistent code output # Objects that depend on this one will be informed of changes. self._dependents = WeakKeyDictionary() @property def name(self): """ The name of this shader object. """ return None def definition(self, obj_names): """ Return the GLSL definition for this object. Use *obj_names* to determine the names of dependencies. """ return None def expression(self, obj_names): """ Return the GLSL expression used to reference this object inline. """ return obj_names[self] def dependencies(self, sort=False): """ Return all dependencies required to use this object. The last item in the list is *self*. """ alldeps = [] if sort: def key(obj): # sort deps such that we get functions, variables, self. if not isinstance(obj, Variable): return (0, 0) else: return (1, obj.vtype) deps = sorted(self._deps, key=key) else: deps = self._deps for dep in deps: alldeps.extend(dep.dependencies(sort=sort)) alldeps.append(self) return alldeps def static_names(self): """ Return a list of names that are declared in this object's definition (not including the name of the object itself). These names will be reserved by the compiler when automatically determining object names. """ return [] def _add_dep(self, dep): """ Increment the reference count for *dep*. If this is a new dependency, then connect to its *changed* event. """ if dep in self._deps: self._deps[dep] += 1 else: self._deps[dep] = 1 dep._dependents[self] = None def _remove_dep(self, dep): """ Decrement the reference count for *dep*. If the reference count reaches 0, then the dependency is removed and its *changed* event is disconnected. """ refcount = self._deps[dep] if refcount == 1: self._deps.pop(dep) dep._dependents.pop(self) else: self._deps[dep] -= 1 def _dep_changed(self, dep, code_changed=False, value_changed=False): """ Called when a dependency's expression has changed. """ logger.debug("ShaderObject changed [code=%s, value=%s]", code_changed, value_changed) self.changed(code_changed, value_changed) def changed(self, code_changed=False, value_changed=False): """Inform dependents that this shaderobject has changed. """ for d in self._dependents: d._dep_changed(self, code_changed=code_changed, value_changed=value_changed) def compile(self): """ Return a compilation of this object and its dependencies. Note: this is mainly for debugging purposes; the names in this code are not guaranteed to match names in any other compilations. Use Compiler directly to ensure consistent naming across multiple objects. """ compiler = Compiler(obj=self) return compiler.compile()['obj'] def __repr__(self): if self.name is not None: return '<%s "%s" at 0x%x>' % (self.__class__.__name__, self.name, id(self)) else: return '<%s at 0x%x>' % (self.__class__.__name__, id(self)) from .variable import Variable # noqa from .expression import TextExpression # noqa vispy-0.4.0/vispy/visuals/shaders/program.py0000664000175000017500000000765412527672621022723 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from __future__ import division from ...gloo import Program from ...gloo.preprocessor import preprocess from ...util import logger from ...util.event import EventEmitter from .function import MainFunction from .variable import Variable from .compiler import Compiler class ModularProgram(Program): """ Shader program using Function instances as basis for its shaders. Automatically rebuilds program when functions have changed and uploads program variables. """ def __init__(self, vcode=None, fcode=None): Program.__init__(self) self.changed = EventEmitter(source=self, type='program_change') # Cache state of Variables so we know which ones require update self._variable_state = {} self.vert = vcode self.frag = fcode @property def vert(self): return self._vert @vert.setter def vert(self, vcode): if hasattr(self, '_vert') and self._vert is not None: self._vert._dependents.pop(self) self._vert = vcode if self._vert is None: return vcode = preprocess(vcode) self._vert = MainFunction(vcode) self._vert._dependents[self] = None self._need_build = True self.changed(code_changed=True, value_changed=False) @property def frag(self): return self._frag @frag.setter def frag(self, fcode): if hasattr(self, '_frag') and self._frag is not None: self._frag._dependents.pop(self) self._frag = fcode if self._frag is None: return fcode = preprocess(fcode) self._frag = MainFunction(fcode) self._frag._dependents[self] = None self._need_build = True self.changed(code_changed=True, value_changed=False) def prepare(self): """ Prepare the Program so we can set attributes and uniforms. """ pass # todo: remove! def _dep_changed(self, dep, code_changed=False, value_changed=False): logger.debug("ModularProgram source changed: %s", self) if code_changed: self._need_build = True self.changed(code_changed=code_changed, value_changed=value_changed) def draw(self, *args, **kwargs): self.build_if_needed() Program.draw(self, *args, **kwargs) def build_if_needed(self): """ Reset shader source if necesssary. """ if self._need_build: self._build() self._need_build = False self.update_variables() def _build(self): logger.debug("Rebuild ModularProgram: %s", self) self.compiler = Compiler(vert=self.vert, frag=self.frag) code = self.compiler.compile() self.set_shaders(code['vert'], code['frag']) logger.debug('==== Vertex Shader ====\n\n%s\n', code['vert']) logger.debug('==== Fragment shader ====\n\n%s\n', code['frag']) # Note: No need to reset _variable_state, gloo.Program resends # attribute/uniform data on setting shaders def update_variables(self): # Clear any variables that we may have set another time. # Otherwise we get lots of warnings. self._pending_variables = {} # set all variables settable_vars = 'attribute', 'uniform' logger.debug("Apply variables:") deps = self.vert.dependencies() + self.frag.dependencies() for dep in deps: if not isinstance(dep, Variable) or dep.vtype not in settable_vars: continue name = self.compiler[dep] logger.debug(" %s = %s", name, dep.value) state_id = dep.state_id if self._variable_state.get(name, None) != state_id: self[name] = dep.value self._variable_state[name] = state_id vispy-0.4.0/vispy/visuals/shaders/__init__.py0000664000175000017500000000112412527672621022775 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Provides functionality for composing shaders from multiple GLSL code snippets. """ __all__ = ['ModularProgram', 'Function', 'MainFunction', 'Variable', 'Varying', 'FunctionChain', 'Compiler'] from .program import ModularProgram # noqa from .function import Function, MainFunction, FunctionChain # noqa from .function import StatementList # noqa from .variable import Variable, Varying # noqa from .compiler import Compiler # noqa vispy-0.4.0/vispy/visuals/shaders/parsing.py0000664000175000017500000001066312527672621022711 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from __future__ import division import re # regular expressions for parsing GLSL re_type = r'(?:void|int|float|vec2|vec3|vec4|mat2|mat3|mat4|\ sampler1D|sampler2D|sampler3D)' re_identifier = r'(?:[a-zA-Z_][\w_]*)' # variable qualifiers re_qualifier = r'(const|uniform|attribute|varying)' # template variables like # $func_name re_template_var = (r"(?:(?:\$" + re_identifier + ")|(?:\$\{" + re_identifier + "\}))") # function names may be either identifier or template var re_func_name = r"(" + re_identifier + "|" + re_template_var + ")" # type and identifier like "vec4 var_name" re_declaration = "(?:(" + re_type + ")\s+(" + re_identifier + "))" # qualifier, type, and identifier like "uniform vec4 var_name" # qualifier is optional. # may include multiple names like "attribute float x, y, z" re_prog_var_declaration = ("(?:" + re_qualifier + "?\s*(" + re_type + ")\s+(" + re_identifier + "(\s*,\s*(" + re_identifier + "))*))") # list of variable declarations like "vec4 var_name, float other_var_name" re_arg_list = "(" + re_declaration + "(?:,\s*" + re_declaration + ")*)?" # function declaration like "vec4 function_name(float x, float y)" re_func_decl = ("(" + re_type + ")\s+" + re_func_name + "\s*\((void|" + re_arg_list + ")\)") # anonymous variable declarations may or may not include a name: # "vec4" or "vec4 var_name" re_anon_decl = "(?:(" + re_type + ")(?:\s+" + re_identifier + ")?)" # list of anonymous declarations re_anon_arg_list = "(" + re_anon_decl + "(?:,\s*" + re_anon_decl + ")*)?" # function prototype declaration like # "vec4 function_name(float, float);" re_func_prot = ("(" + re_type + ")\s+" + re_func_name + "\((void|" + re_anon_arg_list + ")\)\s*;") def parse_function_signature(code): """ Return the name, arguments, and return type of the first function definition found in *code*. Arguments are returned as [(type, name), ...]. """ m = re.search("^\s*" + re_func_decl + "\s*{", code, re.M) if m is None: print(code) raise Exception("Failed to parse function signature. " "Full code is printed above.") rtype, name, args = m.groups()[:3] if args == 'void' or args.strip() == '': args = [] else: args = [tuple(arg.strip().split(' ')) for arg in args.split(',')] return name, args, rtype def find_functions(code): """ Return a list of (name, arguments, return type) for all function definition2 found in *code*. Arguments are returned as [(type, name), ...]. """ regex = "^\s*" + re_func_decl + "\s*{" funcs = [] while True: m = re.search(regex, code, re.M) if m is None: return funcs rtype, name, args = m.groups()[:3] if args == 'void' or args.strip() == '': args = [] else: args = [tuple(arg.strip().split(' ')) for arg in args.split(',')] funcs.append((name, args, rtype)) code = code[m.end():] def find_prototypes(code): """ Return a list of signatures for each function prototype declared in *code*. Format is [(name, [args], rtype), ...]. """ prots = [] lines = code.split('\n') for line in lines: m = re.match("\s*" + re_func_prot, line) if m is not None: rtype, name, args = m.groups()[:3] if args == 'void' or args.strip() == '': args = [] else: args = [tuple(arg.strip().split(' ')) for arg in args.split(',')] prots.append((name, args, rtype)) return prots def find_program_variables(code): """ Return a dict describing program variables:: {'var_name': ('uniform|attribute|varying', type), ...} """ vars = {} lines = code.split('\n') for line in lines: m = re.match(r"\s*" + re_prog_var_declaration + r"\s*(=|;)", line) if m is not None: vtype, dtype, names = m.groups()[:3] for name in names.split(','): vars[name.strip()] = (vtype, dtype) return vars def find_template_variables(code): """ Return a list of template variables found in *code*. """ return re.findall(re_template_var, code) vispy-0.4.0/vispy/visuals/shaders/compiler.py0000664000175000017500000001666412527672621023067 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from __future__ import division import re from ... import gloo class Compiler(object): """ Compiler is used to convert Function and Variable instances into ready-to-use GLSL code. This class handles name mangling to ensure that there are no name collisions amongst global objects. The final name of each object may be retrieved using ``Compiler.__getitem__(obj)``. Accepts multiple root Functions as keyword arguments. ``compile()`` then returns a dict of GLSL strings with the same keys. Example:: # initialize with two main functions compiler = Compiler(vert=v_func, frag=f_func) # compile and extract shaders code = compiler.compile() v_code = code['vert'] f_code = code['frag'] # look up name of some object name = compiler[obj] """ def __init__(self, **shaders): # cache of compilation results for each function and variable self._object_names = {} # {object: name} self.shaders = shaders def __getitem__(self, item): """ Return the name of the specified object, if it has been assigned one. """ return self._object_names[item] def compile(self, pretty=True): """ Compile all code and return a dict {name: code} where the keys are determined by the keyword arguments passed to __init__(). Parameters ---------- pretty : bool If True, use a slower method to mangle object names. This produces GLSL that is more readable. If False, then the output is mostly unreadable GLSL, but is about 10x faster to compile. """ # Authoritative mapping of {obj: name} self._object_names = {} # # 1. collect list of dependencies for each shader # # maps {shader_name: [deps]} self._shader_deps = {} for shader_name, shader in self.shaders.items(): this_shader_deps = [] self._shader_deps[shader_name] = this_shader_deps dep_set = set() for dep in shader.dependencies(sort=True): # visit each object no more than once per shader if dep.name is None or dep in dep_set: continue this_shader_deps.append(dep) dep_set.add(dep) # # 2. Assign names to all objects. # if pretty: self._rename_objects_pretty() else: self._rename_objects_fast() # # 3. Now we have a complete namespace; concatenate all definitions # together in topological order. # compiled = {} obj_names = self._object_names for shader_name, shader in self.shaders.items(): code = [] for dep in self._shader_deps[shader_name]: dep_code = dep.definition(obj_names) if dep_code is not None: # strip out version pragma if present; regex = r'#version (\d+)' m = re.search(regex, dep_code) if m is not None: # check requested version if m.group(1) != '120': raise RuntimeError("Currently only GLSL #version " "120 is supported.") dep_code = re.sub(regex, '', dep_code) code.append(dep_code) compiled[shader_name] = '\n'.join(code) self.code = compiled return compiled def _rename_objects_fast(self): """ Rename all objects quickly to guaranteed-unique names using the id() of each object. This produces mostly unreadable GLSL, but is about 10x faster to compile. """ for shader_name, deps in self._shader_deps.items(): for dep in deps: name = dep.name if name != 'main': ext = '_%x' % id(dep) name = name[:32-len(ext)] + ext self._object_names[dep] = name def _rename_objects_pretty(self): """ Rename all objects like "name_1" to avoid conflicts. Objects are only renamed if necessary. This method produces more readable GLSL, but is rather slow. """ # # 1. For each object, add its static names to the global namespace # and make a list of the shaders used by the object. # # {name: obj} mapping for finding unique names # initialize with reserved keywords. self._global_ns = dict([(kwd, None) for kwd in gloo.util.KEYWORDS]) # functions are local per-shader self._shader_ns = dict([(shader, {}) for shader in self.shaders]) # for each object, keep a list of shaders the object appears in obj_shaders = {} for shader_name, deps in self._shader_deps.items(): for dep in deps: # Add static names to namespace for name in dep.static_names(): self._global_ns[name] = None obj_shaders.setdefault(dep, []).append(shader_name) # # 2. Assign new object names # name_index = {} for obj, shaders in obj_shaders.items(): name = obj.name if self._name_available(obj, name, shaders): # hooray, we get to keep this name self._assign_name(obj, name, shaders) else: # boo, find a new name while True: index = name_index.get(name, 0) + 1 name_index[name] = index ext = '_%d' % index new_name = name[:32-len(ext)] + ext if self._name_available(obj, new_name, shaders): self._assign_name(obj, new_name, shaders) break def _is_global(self, obj): """ Return True if *obj* should be declared in the global namespace. Some objects need to be declared only in per-shader namespaces: functions, static variables, and const variables may all be given different definitions in each shader. """ # todo: right now we assume all Variables are global, and all # Functions are local. Is this actually correct? Are there any # global functions? Are there any local variables? from .variable import Variable return isinstance(obj, Variable) def _name_available(self, obj, name, shaders): """ Return True if *name* is available for *obj* in *shaders*. """ if name in self._global_ns: return False shaders = self.shaders if self._is_global(obj) else shaders for shader in shaders: if name in self._shader_ns[shader]: return False return True def _assign_name(self, obj, name, shaders): """ Assign *name* to *obj* in *shaders*. """ if self._is_global(obj): assert name not in self._global_ns self._global_ns[name] = obj else: for shader in shaders: ns = self._shader_ns[shader] assert name not in ns ns[name] = obj self._object_names[obj] = name vispy-0.4.0/vispy/visuals/shaders/function.py0000664000175000017500000006222112527672621023070 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Classses representing GLSL objects (functions, variables, etc) that may be composed together to create complete shaders. See the docstring of Function for details. Details ------- A complete GLSL program is composed of ShaderObjects, each of which may be used inline as an expression, and some of which include a definition that must be included on the final code. ShaderObjects keep track of a hierarchy of dependencies so that all necessary code is included at compile time, and changes made to any object may be propagated to the root of the hierarchy to trigger a recompile. """ import re import logging import numpy as np from ...util.eq import eq from ...util import logger from ...ext.ordereddict import OrderedDict from ...ext.six import string_types from . import parsing from .shader_object import ShaderObject from .variable import Variable, Varying from .expression import TextExpression, FunctionCall class Function(ShaderObject): """Representation of a GLSL function Objects of this class can be used for re-using and composing GLSL snippets. Each Function consists of a GLSL snippet in the form of a function. The code may have template variables that start with the dollar sign. These stubs can be replaced with expressions using the index operation. Expressions can be: * plain text that is inserted verbatim in the code * a Function object or a call to a funcion * a Variable (or Varying) object * float, int, tuple are automatically turned into a uniform Variable * a VertexBuffer is automatically turned into an attribute Variable All functions have implicit "$pre" and "$post" placeholders that may be used to insert code at the beginning and end of the function. Examples -------- This example shows the basic usage of the Function class:: vert_code_template = Function(''' void main() { gl_Position = $pos; gl_Position.x += $xoffset; gl_Position.y += $yoffset; }''') scale_transform = Function(''' vec4 transform_scale(vec4 pos){ return pos * $scale; }''') # If you get the function from a snippet collection, always # create new Function objects to ensure they are 'fresh'. vert_code = Function(vert_code_template) trans1 = Function(scale_transform) trans2 = Function(scale_transform) # trans2 != trans1 # Three ways to assign to template variables: # # 1) Assign verbatim code vert_code['xoffset'] = '(3.0 / 3.1415)' # 2) Assign a value (this creates a new uniform or attribute) vert_code['yoffset'] = 5.0 # 3) Assign a function call expression pos_var = Variable('attribute vec4 a_position') vert_code['pos'] = trans1(trans2(pos_var)) # Transforms also need their variables set trans1['scale'] = 0.5 trans2['scale'] = (1.0, 0.5, 1.0, 1.0) # You can actually change any code you want, but use this with care! vert_code.replace('gl_Position.y', 'gl_Position.z') # Finally, you can set special variables explicitly. This generates # a new statement at the end of the vert_code function. vert_code['gl_PointSize'] = '10.' If we use ``vert_code.compile()`` we get:: attribute vec4 a_position; uniform float u_yoffset; uniform float u_scale_1; uniform vec4 u_scale_2; uniform float u_pointsize; vec4 transform_scale_1(vec4 pos){ return pos * u_scale_1; } vec4 transform_scale_2(vec4 pos){ return pos * u_scale_2; } void main() { gl_Position = transform_scale_1(transform_scale_2(a_position)); gl_Position.x += (3.0 / 3.1415); gl_Position.z += u_yoffset; gl_PointSize = u_pointsize; } Note how the two scale function resulted in two different functions and two uniforms for the scale factors. Function calls -------------- As can be seen above, the arguments with which a function is to be called must be specified by calling the Function object. The arguments can be any of the expressions mentioned earlier. If the signature is already specified in the template code, that function itself must be given. code = Function(''' void main() { vec4 position = $pos; gl_Position = $scale(position) } ''') # Example of a function call with all possible three expressions vert_code['pos'] = func1('3.0', 'uniform float u_param', func2()) # For scale, the sigfnature is already specified code['scale'] = scale_func # Must not specify args Data for uniform and attribute variables ---------------------------------------- To each variable a value can be associated. In fact, in most cases the Function class is smart enough to be able to create a Variable object if only the data is given. code['offset'] = Variable('uniform float offset') # No data code['offset'] = Variable('uniform float offset', 3.0) # With data code['offset'] = 3.0 # -> Uniform Variable position['position'] = VertexBuffer() # -> attribute Variable # Updating variables code['offset'].value = 4.0 position['position'].value.set_data(...) """ def __init__(self, code, dependencies=None): super(Function, self).__init__() # Add depencencies is given. This is to allow people to # manually define deps for a function that they use. if dependencies is not None: for dep in dependencies: self._add_dep(dep) # Get and strip code if isinstance(code, Function): code = code._code elif not isinstance(code, string_types): raise ValueError('Function needs a string or Function; got %s.' % type(code)) self._code = self._clean_code(code) # (name, args, rval) self._signature = None # $placeholders parsed from the code self._template_vars = None # Expressions replace template variables (also our dependencies) self._expressions = OrderedDict() # Verbatim string replacements self._replacements = OrderedDict() # Stuff to do at the end self._assignments = OrderedDict() # Create static Variable instances for any global variables declared # in the code self._static_vars = None def __setitem__(self, key, val): """ Setting of replacements through a dict-like syntax. Each replacement can be: * verbatim code: ``fun1['foo'] = '3.14159'`` * a FunctionCall: ``fun1['foo'] = fun2()`` * a Variable: ``fun1['foo'] = Variable(...)`` (can be auto-generated) """ # Check the key. Must be Varying, 'gl_X' or a known template variable if isinstance(key, Variable): if key.vtype == 'varying': if self.name != 'main': raise Exception("Varying assignment only alowed in 'main' " "function.") storage = self._assignments else: raise TypeError("Variable assignment only allowed for " "varyings, not %s (in %s)" % (key.vtype, self.name)) elif isinstance(key, string_types): if any(map(key.startswith, ('gl_PointSize', 'gl_Position', 'gl_FragColor'))): storage = self._assignments elif key in self.template_vars or key in ('pre', 'post'): storage = self._expressions else: raise KeyError('Invalid template variable %r' % key) else: raise TypeError('In `function[key]` key must be a string or ' 'varying.') # If values already match, bail out now if eq(storage.get(key), val): return # If we are only changing the value (and not the dtype) of a uniform, # we can set that value and return immediately to avoid triggering a # recompile. if val is not None and not isinstance(val, Variable): # We are setting a value. If there is already a variable set here, # try just updating its value. variable = storage.get(key, None) if isinstance(variable, Variable): if np.any(variable.value != val): variable.value = val self.changed(value_changed=True) return # Could not set variable.value directly; instead we will need # to create a new ShaderObject val = ShaderObject.create(val, ref=key) if variable is val: # This can happen if ShaderObject.create returns the same # object (such as when setting a Transform). return # Remove old references, if any oldval = storage.pop(key, None) if oldval is not None: for obj in (key, oldval): if isinstance(obj, ShaderObject): self._remove_dep(obj) # Add new references if val is not None: if isinstance(key, Varying): # tell this varying to inherit properties from # its source attribute / expression. key.link(val) # Store value and dependencies storage[key] = val for obj in (key, val): if isinstance(obj, ShaderObject): self._add_dep(obj) # In case of verbatim text, we might have added new template vars if isinstance(val, TextExpression): for var in parsing.find_template_variables(val.expression()): if var not in self.template_vars: self.template_vars.add(var.lstrip('$')) self.changed(code_changed=True, value_changed=True) if logger.level <= logging.DEBUG: import traceback last = traceback.format_list(traceback.extract_stack()[-2:-1]) logger.debug("Assignment would trigger shader recompile:\n" "Original:\n%r\nReplacement:\n%r\nSource:\n%s", oldval, val, ''.join(last)) def __getitem__(self, key): """ Return a reference to a program variable from this function. This allows variables between functions to be linked together:: func1['var_name'] = func2['other_var_name'] In the example above, the two local variables would be assigned to the same program variable whenever func1 and func2 are attached to the same program. """ try: return self._expressions[key] except KeyError: pass try: return self._assignments[key] except KeyError: pass if key not in self.template_vars: raise KeyError('Invalid template variable %r' % key) else: raise KeyError('No value known for key %r' % key) def __call__(self, *args): """ Set the signature for this function and return an FunctionCall object. Each argument can be verbatim code or a FunctionCall object. """ return FunctionCall(self, args) ## Public API methods @property def signature(self): if self._signature is None: try: self._signature = parsing.parse_function_signature(self._code) except Exception as err: raise ValueError('Invalid code: ' + str(err)) return self._signature @property def name(self): """ The function name. The name may be mangled in the final code to avoid name clashes. """ return self.signature[0] @property def args(self): """ List of input arguments in the function signature:: [(arg_name, arg_type), ...] """ return self.signature[1] @property def rtype(self): """ The return type of this function. """ return self.signature[2] @property def code(self): """ The template code used to generate the definition for this function. """ return self._code @property def template_vars(self): if self._template_vars is None: self._template_vars = self._parse_template_vars() return self._template_vars def static_names(self): if self._static_vars is None: self._static_vars = parsing.find_program_variables(self._code) return list(self._static_vars.keys()) + [arg[0] for arg in self.args] def replace(self, str1, str2): """ Set verbatim code replacement It is strongly recommended to use function['$foo'] = 'bar' where possible because template variables are less likely to changed than the code itself in future versions of vispy. Parameters ---------- str1 : str String to replace str2 : str String to replace str1 with """ if str2 != self._replacements.get(str1, None): self._replacements[str1] = str2 self.changed(code_changed=True) #self._last_changed = time.time() ## Private methods def _parse_template_vars(self): """ find all template variables in self._code, excluding the function name. """ template_vars = set() for var in parsing.find_template_variables(self._code): var = var.lstrip('$') if var == self.name: continue if var in ('pre', 'post'): raise ValueError('GLSL uses reserved template variable $%s' % var) template_vars.add(var) return template_vars def _get_replaced_code(self, names): """ Return code, with new name, expressions, and replacements applied. """ code = self._code # Modify name fname = names[self] code = code.replace(" " + self.name + "(", " " + fname + "(") # Apply string replacements first -- these may contain $placeholders for key, val in self._replacements.items(): code = code.replace(key, val) # Apply assignments to the end of the function # Collect post lines post_lines = [] for key, val in self._assignments.items(): if isinstance(key, Variable): key = names[key] if isinstance(val, ShaderObject): val = val.expression(names) line = ' %s = %s;' % (key, val) post_lines.append(line) # Add a default $post placeholder if needed if 'post' in self._expressions: post_lines.append(' $post') # Apply placeholders for hooks post_text = '\n'.join(post_lines) if post_text: post_text = '\n' + post_text + '\n' code = code.rpartition('}') code = code[0] + post_text + code[1] + code[2] # Add a default $pre placeholder if needed if 'pre' in self._expressions: m = re.search(fname + r'\s*\([^{]*\)\s*{', code) if m is None: raise RuntimeError("Cound not find beginning of function '%s'" % fname) ind = m.span()[1] code = code[:ind] + "\n $pre\n" + code[ind:] # Apply template variables for key, val in self._expressions.items(): val = val.expression(names) search = r'\$' + key + r'($|[^a-zA-Z0-9_])' code = re.sub(search, val+r'\1', code) # Done if '$' in code: v = parsing.find_template_variables(code) logger.warning('Unsubstituted placeholders in code: %s\n' ' replacements made: %s', (v, list(self._expressions.keys()))) return code + '\n' def definition(self, names): return self._get_replaced_code(names) def expression(self, names): return names[self] def _clean_code(self, code): """ Return *code* with indentation and leading/trailing blank lines removed. """ lines = code.split("\n") min_indent = 100 for line in lines: if line.strip() != "": indent = len(line) - len(line.lstrip()) min_indent = min(indent, min_indent) if min_indent > 0: lines = [line[min_indent:] for line in lines] code = "\n".join(lines) return code def __repr__(self): try: args = ', '.join([' '.join(arg) for arg in self.args]) except Exception: return ('<%s (error parsing signature) at 0x%x>' % (self.__class__.__name__, id(self))) return '<%s "%s %s(%s)" at 0x%x>' % (self.__class__.__name__, self.rtype, self.name, args, id(self)) class MainFunction(Function): """ Subclass of Function that allows multiple functions and variables to be defined in a single code string. The code must contain a main() function definition. """ def __init__(self, *args, **kwargs): self._chains = {} Function.__init__(self, *args, **kwargs) @property def signature(self): return ('main', [], 'void') def static_names(self): # parse static variables names = Function.static_names(self) # parse all function names + argument names funcs = parsing.find_functions(self.code) for f in funcs: if f[0] == 'main': continue names.append(f[0]) for arg in f[1]: names.append(arg[1]) return names def add_chain(self, var): """ Create a new ChainFunction and attach to $var. """ chain = FunctionChain(var, []) self._chains[var] = chain self[var] = chain def add_callback(self, hook, func): self._chains[hook].append(func) def remove_callback(self, hook, func): self._chains[hook].remove(func) class FunctionChain(Function): """Subclass that generates GLSL code to call Function list in order Functions may be called independently, or composed such that the output of each function provides the input to the next. Parameters ---------- name : str The name of the generated function funcs : list of Functions The list of Functions that will be called by the generated GLSL code. Examples -------- This creates a function chain: >>> func1 = Function('void my_func_1() {}') >>> func2 = Function('void my_func_2() {}') >>> chain = FunctionChain('my_func_chain', [func1, func2]) If *chain* is included in a ModularProgram, it will generate the following output: void my_func_1() {} void my_func_2() {} void my_func_chain() { my_func_1(); my_func_2(); } The return type of the generated function is the same as the return type of the last function in the chain. Likewise, the arguments for the generated function are the same as the first function in the chain. If the return type is not 'void', then the return value of each function will be used to supply the first input argument of the next function in the chain. For example: vec3 my_func_1(vec3 input) {return input + vec3(1, 0, 0);} void my_func_2(vec3 input) {return input + vec3(0, 1, 0);} vec3 my_func_chain(vec3 input) { return my_func_2(my_func_1(input)); } """ def __init__(self, name=None, funcs=()): # bypass Function.__init__ completely. ShaderObject.__init__(self) if not (name is None or isinstance(name, string_types)): raise TypeError("Name argument must be string or None.") self._funcs = [] self._code = None self._name = name or "chain" self._args = [] self._rtype = 'void' self.functions = funcs @property def functions(self): return self._funcs[:] @functions.setter def functions(self, funcs): while self._funcs: self.remove(self._funcs[0], update=False) for f in funcs: self.append(f, update=False) self._update() @property def signature(self): return self._name, self._args, self._rtype def _update(self): funcs = self._funcs if len(funcs) > 0: self._rtype = funcs[-1].rtype self._args = funcs[0].args[:] else: self._rtype = 'void' self._args = [] self.changed(code_changed=True) @property def template_vars(self): return {} def append(self, function, update=True): """ Append a new function to the end of this chain. """ self._funcs.append(function) self._add_dep(function) if update: self._update() def __setitem__(self, index, func): self._remove_dep(self._funcs[index]) self._add_dep(func) self._funcs[index] = func self._update() def __getitem__(self, k): return self.functions[k] def insert(self, index, function, update=True): """ Insert a new function into the chain at *index*. """ self._funcs.insert(index, function) self._add_dep(function) if update: self._update() def remove(self, function, update=True): """ Remove a function from the chain. """ self._funcs.remove(function) self._remove_dep(function) if update: self._update() def definition(self, obj_names): name = obj_names[self] args = ", ".join(["%s %s" % arg for arg in self.args]) code = "%s %s(%s) {\n" % (self.rtype, name, args) result_index = 0 if len(self.args) == 0: last_rtype = 'void' last_result = '' else: last_rtype, last_result = self.args[0][:2] for fn in self._funcs: # Use previous return value as an argument to the next function if last_rtype == 'void': args = '' else: args = last_result if len(fn.args) != 1 or last_rtype != fn.args[0][0]: raise Exception("Cannot chain output '%s' of function to " "input of '%s'" % (last_rtype, fn.signature)) last_rtype = fn.rtype # Store the return value of this function if fn.rtype == 'void': set_str = '' else: result_index += 1 result = 'result_%d' % result_index set_str = '%s %s = ' % (fn.rtype, result) last_result = result code += " %s%s(%s);\n" % (set_str, obj_names[fn], args) # return the last function's output if self.rtype != 'void': code += " return result_%d;\n" % result_index code += "}\n" return code def static_names(self): return [] def __repr__(self): fn = ",\n ".join(map(repr, self.functions)) return "" % (fn, id(self)) class StatementList(ShaderObject): """Represents a list of statements. """ def __init__(self): self.items = [] ShaderObject.__init__(self) def append(self, item): self.items.append(item) self._add_dep(item) self.changed(code_changed=True) def add(self, item): """Add an item to the list unless it is already present. If the item is an expression, then a semicolon will be appended to it in the final compiled code. """ if item in self.items: return self.append(item) def remove(self, item): """Remove an item from the list. """ self.items.remove(item) self._remove_dep(item) self.changed(code_changed=True) def expression(self, obj_names): code = "" for item in self.items: code += item.expression(obj_names) + ';\n' return code vispy-0.4.0/vispy/visuals/shaders/tests/0000775000175000017500000000000012527674621022032 5ustar larsonerlarsoner00000000000000vispy-0.4.0/vispy/visuals/shaders/tests/test_parsing.py0000664000175000017500000000267512527672621025116 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. import re from vispy.visuals.shaders.parsing import re_identifier, find_program_variables from vispy.testing import run_tests_if_main def test_identifier(): assert(re.match('('+re_identifier+')', 'Ax2_d3__7').groups()[0] == 'Ax2_d3__7') assert(re.match('('+re_identifier+')', '_Ax2_d3__7').groups()[0] == '_Ax2_d3__7') assert(re.match(re_identifier, '7Ax2_d3__7') is None) assert(re.match('('+re_identifier+')', 'x,y').groups()[0] == 'x') assert(re.match('('+re_identifier+')', 'x y').groups()[0] == 'x') def test_find_variables(): code = """ float x; float y, z; int w,v,u; junk vec4 t = vec4(0, 0, 1, 1); junk junk junk; uniform vec2 s; attribute float r,q; const mat4 p; void main() { vec2 float undetectable; } """ expect = dict( x=(None, 'float'), y=(None, 'float'), z=(None, 'float'), w=(None, 'int'), v=(None, 'int'), u=(None, 'int'), t=(None, 'vec4'), s=('uniform', 'vec2'), q=('attribute', 'float'), r=('attribute', 'float'), p=('const', 'mat4'), ) vars = find_program_variables(code) for k in expect: assert expect[k] == vars.pop(k) assert len(vars) == 0 run_tests_if_main() vispy-0.4.0/vispy/visuals/shaders/tests/__init__.py0000664000175000017500000000000012426461252024121 0ustar larsonerlarsoner00000000000000vispy-0.4.0/vispy/visuals/shaders/tests/test_function.py0000664000175000017500000003177412527672621025302 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from vispy.visuals.shaders import (Function, MainFunction, Variable, Varying, FunctionChain, StatementList) # Users normally don't need these, but I want to test them from vispy.visuals.shaders.expression import FunctionCall, TextExpression from vispy.testing import (assert_in, assert_not_in, assert_is, run_tests_if_main, assert_raises, assert_equal) ## Define some snippets transformScale = Function(""" vec4 transform_scale(vec4 pos) { pos.xyz *= $scale; return pos; } """) transformZOffset = Function(""" vec4 transform_zoffset(vec4 pos) { pos.z += $offset; return pos; } """) vert_template = Function(""" void main(void) { int nlights = $nlights; vec4 pos = $position; pos += $correction; gl_Position = $endtransform(pos); } """) frag_template = Function(""" void main(void) { gl_Fragcolor = $color; } """) data = 'just some dummy variable, Function is agnostic about this' ## Examples def test_example1(): """ Just a few simple compositions. """ # Get function objects. Generate random name for transforms code = Function(vert_template) t1 = Function(transformScale) t2 = Function(transformZOffset) t3 = Function(transformScale) # We need to create a variable in order to use it in two places pos = Variable('attribute vec4 a_position') # Compose everything together code['position'] = t1(t2(pos)) code['correction'] = t1(pos) # Look, we use t1 again, different sig code['endtransform'] = t3 # function pointer rather than function call code['nlights'] = '4' t1['scale'] = t2 t3['scale'] = (3.0, 4.0, 5.0) t2['offset'] = '1.0' code2 = Function(frag_template) code2['color'] = Varying('v_position') code['gl_PointSize'] = '3.0' code[code2['color']] = pos print(code) def test_example2(): """ Demonstrate how a transform would work. """ vert_template = Function(""" void main(void) { gl_Position = $position; } """) transformScale = Function(""" vec4 transform_scale(vec4 pos) { pos.xyz *= $scale; return pos; } """) class Transform(object): def __init__(self): # Equivalent methods to create new function object self.func = Function(transformScale) self.func['scale'] = 'uniform float' #self.func = Function(transformScale) def set_scale(self, scale): self.func['scale'].value = scale transforms = [Transform(), Transform(), Transform()] code = Function(vert_template) ob = Variable('attribute vec3 a_position') for trans in transforms: ob = trans.func(ob) code['position'] = ob print(code) ## Tests def test_TextExpression(): exp = TextExpression('foo bar') assert_equal('foo bar', exp.expression(None)) assert_equal(None, exp.definition(None)) assert_raises(TypeError, TextExpression, 4) def test_FunctionCall(): fun = Function(transformScale) fun['scale'] = '1.0' fun2 = Function(transformZOffset) # No args assert_raises(TypeError, fun) # need 1 arg assert_raises(TypeError, fun, 1, 2) # need 1 arg call = fun('x') # Test repr exp = call.expression({fun: 'y'}) assert_equal(exp, 'y(x)') # Test sig assert len(call._args) == 1 # Test dependencies assert_in(fun, call.dependencies()) assert_in(call._args[0], call.dependencies()) # More args call = fun(fun2('foo')) # Test repr exp = call.expression({fun: 'y', fun2: 'z'}) assert_in('y(z(', exp) # Test sig assert len(call._args) == 1 call2 = call._args[0] assert len(call2._args) == 1 # Test dependencies assert_in(fun, call.dependencies()) assert_in(call._args[0], call.dependencies()) assert_in(fun2, call.dependencies()) assert_in(call2._args[0], call.dependencies()) def test_Variable(): # Test init fail assert_raises(TypeError, Variable) # no args assert_raises(TypeError, Variable, 3) # wrong type assert_raises(TypeError, Variable, "name", "str") # wrong type assert_raises(ValueError, Variable, 'bla bla') # need correct vtype assert_raises(ValueError, Variable, 'uniform b l a') # too many # Test init success var = Variable('uniform float bla') # Finally assert_equal(var.name, 'bla') assert_equal(var.dtype, 'float') assert_equal(var.vtype, 'uniform') assert var.value is None # test assign new value var.value = 10. assert_equal(var.dtype, 'float') # type is locked; won't change # test name-only init var = Variable('bla') # Finally assert_equal(var.name, 'bla') assert_equal(var.dtype, None) assert_equal(var.vtype, None) assert var.value is None # test assign new value var.value = 10 assert_equal(var.dtype, 'int') assert_equal(var.vtype, 'uniform') assert_equal(var.value, 10) # test init with value var = Variable('bla', (1, 2, 3)) # Also valid assert_equal(var.name, 'bla') assert_equal(var.dtype, 'vec3') assert_equal(var.vtype, 'uniform') assert_equal(var.value, (1, 2, 3)) # Test value #var = Variable('uniform float bla', data) # Also valid #assert_equal(var.value, data) #var.value = 3 #assert_equal(var.value, 3) # Test repr var = Variable('uniform float bla') assert_in('uniform float bla', var.compile()) # Test injection, definition, dependencies assert_equal(var.expression({var: 'xxx'}), 'xxx') assert_equal(var.definition({var: 'xxx'}), 'uniform float xxx;') assert_in(var, var.dependencies()) # Renaming var = Variable('uniform float bla') assert_equal(var.name, 'bla') var.name = 'foo' assert_equal(var.name, 'foo') def test_function_basics(): # Test init fail assert_raises(TypeError, Function) # no args assert_raises(ValueError, Function, 3) # need string # Test init success 1 fun = Function('void main(){}') assert_equal(fun.name, 'main') assert len(fun.template_vars) == 0 # Test init success with template vars fun = Function('void main(){$foo; $bar;}') assert_equal(fun.name, 'main') assert len(fun.template_vars) == 2 assert_in('foo', fun.template_vars) assert_in('bar', fun.template_vars) # Test setting verbatim expressions assert_raises(KeyError, fun.__setitem__, 'bla', '33') # no such template fun['foo'] = '33' fun['bar'] = 'bla bla' assert_is(type(fun['foo']), TextExpression) assert_equal(fun['foo'].expression(None), '33') assert_is(type(fun['bar']), TextExpression) assert_equal(fun['bar'].expression(None), 'bla bla') # Test setting call expressions fun = Function('void main(){\n$foo;\n$bar;\n$spam(XX);\n$eggs(YY);\n}') trans = Function('float transform_scale(float x) {return x+1.0;}') assert_raises(TypeError, trans) # requires 1 arg assert_raises(TypeError, trans, '1', '2') fun['foo'] = trans('2') fun['bar'] = trans('3') fun['spam'] = trans fun['eggs'] = trans # for name in ['foo', 'bar']: assert_is(type(fun[name]), FunctionCall) assert_equal(fun[name].function, trans) assert_in(trans, fun.dependencies()) for name in ['spam', 'eggs']: assert_equal(fun[name], trans) # text = fun.compile() assert_in('\ntransform_scale(2);\n', text) assert_in('\ntransform_scale(3);\n', text) assert_in('\ntransform_scale(XX);\n', text) assert_in('\ntransform_scale(YY);\n', text) # test pre/post assignments fun = Function('void main() {some stuff;}') fun['pre'] = '__pre__' fun['post'] = '__post__' text = fun.compile() assert text == 'void main() {\n __pre__\nsome stuff;\n __post__\n}\n' # Test variable expressions fun = Function('void main(){$foo; $bar;}') fun['foo'] = Variable('uniform float bla') fun['bar'] = Variable('attribute float bla') assert_is(type(fun['foo']), Variable) assert_is(type(fun['bar']), Variable) assert_in(fun['foo'], fun.dependencies()) assert_in(fun['bar'], fun.dependencies()) # Test special variables fun = Function('void main(){$foo; $bar;}') variable = Variable('attribute vec3 v_pos') varying = Variable('varying vec3 color') # These do not work due to index assert_raises(TypeError, fun.__setitem__, 3, 3) # not a string assert_raises(KeyError, fun.__setitem__, 'xxx', 3) # unknown template var assert_raises(TypeError, fun.__setitem__, variable, 3) # only varyings # These work fun['gl_PointSize'] = '3.0' fun[varying] = variable # And getting works assert_equal(fun['gl_PointSize'].text, '3.0') assert_equal(fun[varying], variable) def test_function_changed(): ch = [] class C(object): def _dep_changed(self, dep, **kwargs): ch.append(dep) ch_obj = C() def assert_changed(*objs): assert set(ch) == set(objs) while ch: ch.pop() fun1 = Function('void main(){$var1; $var2;}') fun1._dependents[ch_obj] = None fun1['var1'] = 'x' fun1['var2'] = 'y' assert_changed(fun1) fun1['var1'] = 'z' assert_changed(fun1) # same value; should result in no change events fun1['var1'] = 'z' assert_changed() fun1['var1'] = 0.5 var1 = fun1['var1'] var1._dependents[ch_obj] = None assert_changed(fun1) var1.name = 'xxx' assert_changed(fun1, var1) # changing type requires code change var1.value = 7 assert_changed(fun1, var1) # changing value (but not type) requires no code changes var1.value = 6 assert_changed() # test variable disconnect fun1['var1'] = Variable('var1', 7) var2 = fun1['var1'] var2._dependents[ch_obj] = None #assert_changed(fun1) # var2 is now connected var2.value = (1, 2, 3, 4) assert_changed(fun1, var2) # ..but var1 no longer triggers fun1.changed assert_changed() var1.value = 0.5 assert_changed(var1) # test expressions fun2 = Function('float fn(float x){return $var1 + x;}') fun3 = Function('float fn(float x){return $var1 + x;}') exp1 = fun2(fun3(0.5)) fun1['var2'] = exp1 assert_changed(fun1) fun2._dependents[ch_obj] = None fun3._dependents[ch_obj] = None exp1._dependents[ch_obj] = None fun2['var1'] = 'x' assert_changed(fun1, fun2, exp1) fun3['var1'] = 'x' assert_changed(fun1, fun3, exp1) # test disconnect fun1['var2'] = fun2 assert_changed(fun1) # triggers change fun2['var1'] = 0.9 assert_changed(fun1, fun2, exp1) # no longer triggers change fun3['var1'] = 0.9 assert_changed(fun3, exp1) def test_FunctionChain(): f1 = Function("void f1(){}") f2 = Function("void f2(){}") f3 = Function("float f3(vec3 x){}") f4 = Function("vec3 f4(vec3 y){}") f5 = Function("vec3 f5(vec4 z){}") ch = FunctionChain('chain', [f1, f2]) assert ch.name == 'chain' assert ch.args == [] assert ch.rtype == 'void' assert_in('f1', ch.compile()) assert_in('f2', ch.compile()) ch.remove(f2) assert_not_in('f2', ch.compile()) ch.append(f2) assert_in('f2', ch.compile()) ch = FunctionChain(funcs=[f5, f4, f3]) assert_equal('float', ch.rtype) assert_equal([('vec4', 'z')], ch.args) assert_in('f3', ch.compile()) assert_in('f4', ch.compile()) assert_in('f5', ch.compile()) assert_in(f3, ch.dependencies()) assert_in(f4, ch.dependencies()) assert_in(f5, ch.dependencies()) def test_StatementList(): func = Function("void func() {}") main = Function("void main() {}") main['pre'] = StatementList() expr = func() main['pre'].append(expr) assert main['pre'].items == [expr] main['pre'].add(expr) assert main['pre'].items == [expr] code = main.compile() assert " func();" in code main['pre'].remove(expr) assert main['pre'].items == [] def test_MainFunction(): code = """ const float pi = 3.0; // close enough. vec4 rotate(vec4 pos) { return pos; // just kidding. } attribute mat4 m_transform; attribute vec4 a_pos; void main() { gl_Position = m_transform * a_pos; } """ mf = MainFunction(code) assert mf.name == 'main' assert mf.rtype == 'void' assert len(mf.args) == 0 sn = set(mf.static_names()) assert sn == set(['pi', 'rotate', 'pos', 'm_transform', 'a_pos']) if __name__ == '__main__': for key in [key for key in globals()]: if key.startswith('test_'): func = globals()[key] print('running', func.__name__) func() # Uncomment to run example print('='*80) test_example1() run_tests_if_main() vispy-0.4.0/vispy/visuals/shaders/variable.py0000664000175000017500000001673012527672621023034 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. import numpy as np from ...ext.six import string_types from .shader_object import ShaderObject VARIABLE_TYPES = ('const', 'uniform', 'attribute', 'varying', 'inout') class Variable(ShaderObject): """ Representation of global shader variable Parameters ---------- name : str the name of the variable. This string can also contain the full definition of the variable, e.g. 'uniform vec2 foo'. value : {float, int, tuple, GLObject} If given, vtype and dtype are determined automatically. If a float/int/tuple is given, the variable is a uniform. If a gloo object is given that has a glsl_type property, the variable is an attribute and vtype : {'const', 'uniform', 'attribute', 'varying', 'inout'} The type of variable. dtype : str The data type of the variable, e.g. 'float', 'vec4', 'mat', etc. """ def __init__(self, name, value=None, vtype=None, dtype=None): super(Variable, self).__init__() # allow full definition in first argument if ' ' in name: fields = name.split(' ') if len(fields) == 3: vtype, dtype, name = fields elif len(fields) == 4 and fields[0] == 'const': vtype, dtype, name, value = fields else: raise ValueError('Variable specifications given by string must' ' be of the form "vtype dtype name" or ' '"const dtype name value".') if not (isinstance(name, string_types) or name is None): raise TypeError("Variable name must be string or None.") self._state_counter = 0 self._name = name self._vtype = vtype self._dtype = dtype self._value = None # If vtype/dtype were given at init, then we will never # try to set these values automatically. self._type_locked = self._vtype is not None and self._dtype is not None if value is not None: self.value = value if self._vtype and self._vtype not in VARIABLE_TYPES: raise ValueError('Not a valid vtype: %r' % self._vtype) @property def name(self): """ The name of this variable. """ return self._name @name.setter def name(self, n): # Settable mostly to allow automatic setting of varying names # See ShaderObject.create() if self._name != n: self._name = n self.changed(code_changed=True) @property def vtype(self): """ The type of variable (const, uniform, attribute, varying or inout). """ return self._vtype @property def dtype(self): """ The type of data (float, int, vec, mat, ...). """ return self._dtype @property def value(self): """ The value associated with this variable. """ return self._value @value.setter def value(self, value): if isinstance(value, (tuple, list)) and 1 < len(value) < 5: vtype = 'uniform' dtype = 'vec%d' % len(value) elif isinstance(value, np.ndarray): if value.ndim == 1 and (1 < len(value) < 5): vtype = 'uniform' dtype = 'vec%d' % len(value) elif value.ndim == 2 and value.shape in ((2, 2), (3, 3), (4, 4)): vtype = 'uniform' dtype = 'mat%d' % value.shape[0] else: raise ValueError("Cannot make uniform value for %s from array " "of shape %s." % (self.name, value.shape)) elif np.isscalar(value): vtype = 'uniform' if isinstance(value, (float, np.floating)): dtype = 'float' elif isinstance(value, (int, np.integer)): dtype = 'int' else: raise TypeError("Unknown data type %r for variable %r" % (type(value), self)) elif getattr(value, 'glsl_type', None) is not None: # Note: hasattr() is broken by design--swallows all exceptions! vtype, dtype = value.glsl_type else: raise TypeError("Unknown data type %r for variable %r" % (type(value), self)) self._value = value self._state_counter += 1 if self._type_locked: if dtype != self._dtype or vtype != self._vtype: raise TypeError('Variable is type "%s"; cannot assign value ' '%r.' % (self.dtype, value)) return # update vtype/dtype and emit changed event if necessary changed = False if self._dtype != dtype: self._dtype = dtype changed = True if self._vtype != vtype: self._vtype = vtype changed = True if changed: self.changed(code_changed=True, value_changed=True) @property def state_id(self): """Return a unique ID that changes whenever the state of the Variable has changed. This allows ModularProgram to quickly determine whether the value has changed since it was last used.""" return id(self), self._state_counter def __repr__(self): return ("<%s \"%s %s %s\" at 0x%x>" % (self.__class__.__name__, self._vtype, self._dtype, self.name, id(self))) def expression(self, names): return names[self] def definition(self, names): if self.vtype is None: raise RuntimeError("Variable has no vtype: %r" % self) if self.dtype is None: raise RuntimeError("Variable has no dtype: %r" % self) name = names[self] if self.vtype == 'const': return '%s %s %s = %s;' % (self.vtype, self.dtype, name, self.value) else: return '%s %s %s;' % (self.vtype, self.dtype, name) class Varying(Variable): """ Representation of a varying Varyings can inherit their dtype from another Variable, allowing for more flexibility in composing shaders. """ def __init__(self, name, dtype=None): self._link = None Variable.__init__(self, name, vtype='varying', dtype=dtype) @property def value(self): """ The value associated with this variable. """ return self._value @value.setter def value(self, value): if value is not None: raise TypeError("Cannot assign value directly to varying.") @property def dtype(self): if self._dtype is None: if self._link is None: return None else: return self._link.dtype else: return self._dtype def link(self, var): """ Link this Varying to another object from which it will derive its dtype. This method is used internally when assigning an attribute to a varying using syntax ``Function[varying] = attr``. """ assert self._dtype is not None or hasattr(var, 'dtype') self._link = var self.changed() vispy-0.4.0/vispy/visuals/polygon.py0000664000175000017500000000727112527672621021305 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Simple polygon visual based on MeshVisual and LineVisual """ from __future__ import division import numpy as np from .. import gloo from .visual import Visual from .mesh import MeshVisual from .line import LineVisual from ..color import Color from ..geometry import PolygonData class PolygonVisual(Visual): """ Displays a 2D polygon Parameters ---------- pos : array Set of vertices defining the polygon. color : str | tuple | list of colors Fill color of the polygon. border_color : str | tuple | list of colors Border color of the polygon. border_width : int Border width in pixels. **kwargs : dict Keyword arguments to pass to `PolygonVisual`. """ def __init__(self, pos=None, color='black', border_color=None, border_width=1, **kwargs): super(PolygonVisual, self).__init__(**kwargs) self.mesh = MeshVisual() self.border = LineVisual() self._pos = pos self._color = Color(color) self._border_width = border_width self._border_color = Color(border_color) self._update() #glopts = kwargs.pop('gl_options', 'translucent') #self.set_gl_options(glopts) @property def pos(self): """ The vertex position of the polygon. """ return self._pos @pos.setter def pos(self, pos): self._pos = pos self._update() @property def color(self): """ The color of the polygon. """ return self._color @color.setter def color(self, color): self._color = Color(color, clip=True) self._update() @property def border_color(self): """ The border color of the polygon. """ return self._border_color @border_color.setter def border_color(self, border_color): self._border_color = Color(border_color) self._update() def _update(self): self.data = PolygonData(vertices=np.array(self._pos, dtype=np.float32)) if self._pos is None: return if not self._color.is_blank: pts, tris = self.data.triangulate() self.mesh.set_data(vertices=pts, faces=tris.astype(np.uint32), color=self._color.rgba) if not self._border_color.is_blank: # Close border if it is not already. border_pos = self._pos if np.any(border_pos[0] != border_pos[1]): border_pos = np.concatenate([border_pos, border_pos[:1]], axis=0) self.border.set_data(pos=border_pos, color=self._border_color.rgba, width=self._border_width, connect='strip') self.update() def set_gl_options(self, *args, **kwargs): self.mesh.set_gl_options(*args, **kwargs) def update_gl_options(self, *args, **kwargs): self.mesh.update_gl_options(*args, **kwargs) def draw(self, transforms): """Draw the visual Parameters ---------- transforms : instance of TransformSystem The transforms to use. """ if self._pos is None: return if not self._color.is_blank: gloo.set_state(polygon_offset_fill=True, cull_face=False) gloo.set_polygon_offset(1, 1) self.mesh.draw(transforms) if not self._border_color.is_blank: self.border.draw(transforms) vispy-0.4.0/vispy/color/0000775000175000017500000000000012527674621016667 5ustar larsonerlarsoner00000000000000vispy-0.4.0/vispy/color/_color_dict.py0000664000175000017500000001211012527672621021512 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. def get_color_names(): """Get the known color names Returns ------- names : list List of color names known by Vispy. """ names = list(_color_dict.keys()) names.sort() return names def get_color_dict(): """Get the known colors Returns ------- color_dict : dict Dict of colors known by Vispy {name: #rgb}. """ return _color_dict.copy() # This is used by color functions to translate user strings to colors # For now, this is web colors, and all in hex. It will take some simple # but annoying refactoring to deal with non-hex entries if we want them. # Add the CSS colors, courtesy MIT-licensed code from Dave Eddy: # github.com/bahamas10/css-color-names/blob/master/css-color-names.json _color_dict = { "k": '#000000', "w": '#FFFFFF', "r": '#FF0000', "g": '#00FF00', "b": '#0000FF', "y": '#FFFF00', "m": '#FF00FF', "c": '#00FFFF', "aqua": "#00ffff", "aliceblue": "#f0f8ff", "antiquewhite": "#faebd7", "black": "#000000", "blue": "#0000ff", "cyan": "#00ffff", "darkblue": "#00008b", "darkcyan": "#008b8b", "darkgreen": "#006400", "darkturquoise": "#00ced1", "deepskyblue": "#00bfff", "green": "#008000", "lime": "#00ff00", "mediumblue": "#0000cd", "mediumspringgreen": "#00fa9a", "navy": "#000080", "springgreen": "#00ff7f", "teal": "#008080", "midnightblue": "#191970", "dodgerblue": "#1e90ff", "lightseagreen": "#20b2aa", "forestgreen": "#228b22", "seagreen": "#2e8b57", "darkslategray": "#2f4f4f", "darkslategrey": "#2f4f4f", "limegreen": "#32cd32", "mediumseagreen": "#3cb371", "turquoise": "#40e0d0", "royalblue": "#4169e1", "steelblue": "#4682b4", "darkslateblue": "#483d8b", "mediumturquoise": "#48d1cc", "indigo": "#4b0082", "darkolivegreen": "#556b2f", "cadetblue": "#5f9ea0", "cornflowerblue": "#6495ed", "mediumaquamarine": "#66cdaa", "dimgray": "#696969", "dimgrey": "#696969", "slateblue": "#6a5acd", "olivedrab": "#6b8e23", "slategray": "#708090", "slategrey": "#708090", "lightslategray": "#778899", "lightslategrey": "#778899", "mediumslateblue": "#7b68ee", "lawngreen": "#7cfc00", "aquamarine": "#7fffd4", "chartreuse": "#7fff00", "gray": "#808080", "grey": "#808080", "maroon": "#800000", "olive": "#808000", "purple": "#800080", "lightskyblue": "#87cefa", "skyblue": "#87ceeb", "blueviolet": "#8a2be2", "darkmagenta": "#8b008b", "darkred": "#8b0000", "saddlebrown": "#8b4513", "darkseagreen": "#8fbc8f", "lightgreen": "#90ee90", "mediumpurple": "#9370db", "darkviolet": "#9400d3", "palegreen": "#98fb98", "darkorchid": "#9932cc", "yellowgreen": "#9acd32", "sienna": "#a0522d", "brown": "#a52a2a", "darkgray": "#a9a9a9", "darkgrey": "#a9a9a9", "greenyellow": "#adff2f", "lightblue": "#add8e6", "paleturquoise": "#afeeee", "lightsteelblue": "#b0c4de", "powderblue": "#b0e0e6", "firebrick": "#b22222", "darkgoldenrod": "#b8860b", "mediumorchid": "#ba55d3", "rosybrown": "#bc8f8f", "darkkhaki": "#bdb76b", "silver": "#c0c0c0", "mediumvioletred": "#c71585", "indianred": "#cd5c5c", "peru": "#cd853f", "chocolate": "#d2691e", "tan": "#d2b48c", "lightgray": "#d3d3d3", "lightgrey": "#d3d3d3", "thistle": "#d8bfd8", "goldenrod": "#daa520", "orchid": "#da70d6", "palevioletred": "#db7093", "crimson": "#dc143c", "gainsboro": "#dcdcdc", "plum": "#dda0dd", "burlywood": "#deb887", "lightcyan": "#e0ffff", "lavender": "#e6e6fa", "darksalmon": "#e9967a", "palegoldenrod": "#eee8aa", "violet": "#ee82ee", "azure": "#f0ffff", "honeydew": "#f0fff0", "khaki": "#f0e68c", "lightcoral": "#f08080", "sandybrown": "#f4a460", "beige": "#f5f5dc", "mintcream": "#f5fffa", "wheat": "#f5deb3", "whitesmoke": "#f5f5f5", "ghostwhite": "#f8f8ff", "lightgoldenrodyellow": "#fafad2", "linen": "#faf0e6", "salmon": "#fa8072", "oldlace": "#fdf5e6", "bisque": "#ffe4c4", "blanchedalmond": "#ffebcd", "coral": "#ff7f50", "cornsilk": "#fff8dc", "darkorange": "#ff8c00", "deeppink": "#ff1493", "floralwhite": "#fffaf0", "fuchsia": "#ff00ff", "gold": "#ffd700", "hotpink": "#ff69b4", "ivory": "#fffff0", "lavenderblush": "#fff0f5", "lemonchiffon": "#fffacd", "lightpink": "#ffb6c1", "lightsalmon": "#ffa07a", "lightyellow": "#ffffe0", "magenta": "#ff00ff", "mistyrose": "#ffe4e1", "moccasin": "#ffe4b5", "navajowhite": "#ffdead", "orange": "#ffa500", "orangered": "#ff4500", "papayawhip": "#ffefd5", "peachpuff": "#ffdab9", "pink": "#ffc0cb", "red": "#ff0000", "seashell": "#fff5ee", "snow": "#fffafa", "tomato": "#ff6347", "white": "#ffffff", "yellow": "#ffff00", } vispy-0.4.0/vispy/color/colormap.py0000664000175000017500000004361012527672621021057 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from __future__ import division # just to be safe... import numpy as np from .color_array import ColorArray from ..ext.six import string_types from ..ext.cubehelix import cubehelix ############################################################################### # Color maps # Utility functions for interpolation in NumPy. def _vector_or_scalar(x, type='row'): """Convert an object to either a scalar or a row or column vector.""" if isinstance(x, (list, tuple)): x = np.array(x) if isinstance(x, np.ndarray): assert x.ndim == 1 if type == 'column': x = x[:, None] return x def _vector(x, type='row'): """Convert an object to a row or column vector.""" if isinstance(x, (list, tuple)): x = np.array(x, dtype=np.float32) elif not isinstance(x, np.ndarray): x = np.array([x], dtype=np.float32) assert x.ndim == 1 if type == 'column': x = x[:, None] return x def _find_controls(x, controls=None, clip=None): x_controls = np.clip(np.searchsorted(controls, x) - 1, 0, clip) return x_controls.astype(np.int32) # Normalization def _normalize(x, cmin=None, cmax=None, clip=True): """Normalize an array from the range [cmin, cmax] to [0,1], with optional clipping.""" if not isinstance(x, np.ndarray): x = np.array(x) if cmin is None: cmin = x.min() if cmax is None: cmax = x.max() if cmin == cmax: return .5 * np.ones(x.shape) else: cmin, cmax = float(cmin), float(cmax) y = (x - cmin) * 1. / (cmax - cmin) if clip: y = np.clip(y, 0., 1.) return y # Interpolation functions in NumPy. def _mix_simple(a, b, x): """Mix b (with proportion x) with a.""" x = np.clip(x, 0.0, 1.0) return (1.0 - x)*a + x*b def _interpolate_multi(colors, x, controls): x = x.ravel() n = len(colors) # For each element in x, the control index of its bin's left boundary. x_step = _find_controls(x, controls, n-2) # The length of each bin. controls_length = np.diff(controls).astype(np.float32) # Prevent division by zero error. controls_length[controls_length == 0.] = 1. # Like x, but relative to each bin. _to_clip = x - controls[x_step] _to_clip /= controls_length[x_step] x_rel = np.clip(_to_clip, 0., 1.) return (colors[x_step], colors[x_step + 1], x_rel[:, None]) def mix(colors, x, controls=None): a, b, x_rel = _interpolate_multi(colors, x, controls) return _mix_simple(a, b, x_rel) def smoothstep(edge0, edge1, x): """ performs smooth Hermite interpolation between 0 and 1 when edge0 < x < edge1. """ # Scale, bias and saturate x to 0..1 range x = np.clip((x - edge0)/(edge1 - edge0), 0.0, 1.0) # Evaluate polynomial return x*x*(3 - 2*x) def step(colors, x, controls=None): x = x.ravel() """Step interpolation from a set of colors. x belongs in [0, 1].""" assert (controls[0], controls[-1]) == (0., 1.) ncolors = len(colors) assert ncolors == len(controls) - 1 assert ncolors >= 2 x_step = _find_controls(x, controls, ncolors-1) return colors[x_step, ...] # GLSL interpolation functions. def _glsl_mix(controls=None): """Generate a GLSL template function from a given interpolation patterns and control points.""" assert (controls[0], controls[-1]) == (0., 1.) ncolors = len(controls) assert ncolors >= 2 if ncolors == 2: s = " return mix($color_0, $color_1, t);\n" else: s = "" for i in range(ncolors-1): if i == 0: ifs = 'if (t < %.6f)' % (controls[i+1]) elif i == (ncolors-2): ifs = 'else' else: ifs = 'else if (t < %.6f)' % (controls[i+1]) adj_t = '(t - %s) / %s' % (controls[i], controls[i+1] - controls[i]) s += ("%s {\n return mix($color_%d, $color_%d, %s);\n} " % (ifs, i, i+1, adj_t)) return "vec4 colormap(float t) {\n%s\n}" % s def _glsl_step(controls=None): assert (controls[0], controls[-1]) == (0., 1.) ncolors = len(controls) - 1 assert ncolors >= 2 s = "" for i in range(ncolors-1): if i == 0: ifs = 'if (t < %.6f)' % (controls[i+1]) elif i == (ncolors-2): ifs = 'else' else: ifs = 'else if (t < %.6f)' % (controls[i+1]) s += """%s {\n return $color_%d;\n} """ % (ifs, i) return """vec4 colormap(float t) {\n%s\n}""" % s # Mini GLSL template system for colors. def _process_glsl_template(template, colors): """Replace $color_i by color #i in the GLSL template.""" for i in range(len(colors) - 1, -1, -1): color = colors[i] assert len(color) == 4 vec4_color = 'vec4(%.3f, %.3f, %.3f, %.3f)' % tuple(color) template = template.replace('$color_%d' % i, vec4_color) return template class BaseColormap(object): """Class representing a colormap: t \in [0, 1] --> rgba_color Parameters ---------- colors : list of lists, tuples, or ndarrays The control colors used by the colormap (shape = (ncolors, 4)). Notes ----- Must be overriden. Child classes need to implement: glsl_map : string The GLSL function for the colormap. Use $color_0 to refer to the first color in `colors`, and so on. These are vec4 vectors. map(item) : function Takes a (N, 1) vector of values in [0, 1], and returns a rgba array of size (N, 4). """ # Control colors used by the colormap. colors = None # GLSL string with a function implementing the color map. glsl_map = None def __init__(self, colors=None): # Ensure the colors are arrays. if colors is not None: self.colors = colors if not isinstance(self.colors, ColorArray): self.colors = ColorArray(self.colors) # Process the GLSL map function by replacing $color_i by the if len(self.colors) > 0: self.glsl_map = _process_glsl_template(self.glsl_map, self.colors.rgba) def map(self, item): """Return a rgba array for the requested items. This function must be overriden by child classes. This function doesn't need to implement argument checking on `item`. It can always assume that `item` is a (N, 1) array of values between 0 and 1. Parameters ---------- item : ndarray An array of values in [0,1]. Returns ------- rgba : ndarray An array with rgba values, with one color per item. The shape should be ``item.shape + (4,)``. Notes ----- Users are expected to use a colormap with ``__getitem__()`` rather than ``map()`` (which implements a lower-level API). """ raise NotImplementedError() def __getitem__(self, item): if isinstance(item, tuple): raise ValueError('ColorArray indexing is only allowed along ' 'the first dimension.') # Ensure item is either a scalar or a column vector. item = _vector(item, type='column') # Clip the values in [0, 1]. item = np.clip(item, 0., 1.) colors = self.map(item) return ColorArray(colors) def __setitem__(self, item, value): raise RuntimeError("It is not possible to set items to " "BaseColormap instances.") def _repr_html_(self): n = 100 html = (""" """ + '\n'.join([(("""""") % (color, color)) for color in self[np.linspace(0., 1., n)].hex]) + """
""") return html def _default_controls(ncolors): """Generate linearly spaced control points from a set of colors.""" return np.linspace(0., 1., ncolors) # List the parameters of every supported interpolation mode. _interpolation_info = { 'linear': { 'ncontrols': lambda ncolors: ncolors, # take ncolors as argument 'glsl_map': _glsl_mix, # take 'controls' as argument 'map': mix, }, 'zero': { 'ncontrols': lambda ncolors: (ncolors+1), 'glsl_map': _glsl_step, 'map': step, } } class Colormap(BaseColormap): """A colormap defining several control colors and an interpolation scheme. Parameters ---------- colors : list of colors | ColorArray The list of control colors. If not a ``ColorArray``, a new ``ColorArray`` instance is created from this list. See the documentation of ``ColorArray``. controls : array-like The list of control points for the given colors. It should be an increasing list of floating-point number between 0.0 and 1.0. The first control point must be 0.0. The last control point must be 1.0. The number of control points depends on the interpolation scheme. interpolation : str The interpolation mode of the colormap. Default: 'linear'. Can also be 'zero'. If 'linear', ncontrols = ncolors (one color per control point). If 'zero', ncontrols = ncolors+1 (one color per bin). Examples -------- Here is a basic example: >>> from vispy.color import Colormap >>> cm = Colormap(['r', 'g', 'b']) >>> cm[0.], cm[0.5], cm[np.linspace(0., 1., 100)] """ def __init__(self, colors, controls=None, interpolation='linear'): self.interpolation = interpolation ncontrols = self._ncontrols(len(colors)) # Default controls. if controls is None: controls = _default_controls(ncontrols) assert len(controls) == ncontrols self._controls = np.array(controls, dtype=np.float32) self.glsl_map = self._glsl_map_generator(self._controls) super(Colormap, self).__init__(colors) @property def interpolation(self): """The interpolation mode of the colormap""" return self._interpolation @interpolation.setter def interpolation(self, val): if val not in _interpolation_info: raise ValueError('The interpolation mode can only be one of: ' + ', '.join(sorted(_interpolation_info.keys()))) # Get the information of the interpolation mode. info = _interpolation_info[val] # Get the function that generates the GLSL map, as a function of the # controls array. self._glsl_map_generator = info['glsl_map'] # Number of controls as a function of the number of colors. self._ncontrols = info['ncontrols'] # Python map function. self._map_function = info['map'] self._interpolation = val def map(self, x): """The Python mapping function from the [0,1] interval to a list of rgba colors Parameters ---------- x : array-like The values to map. Returns ------- colors : list List of rgba colors. """ return self._map_function(self.colors.rgba, x, self._controls) class CubeHelixColormap(Colormap): def __init__(self, start=0.5, rot=1, gamma=1.0, reverse=True, nlev=32, minSat=1.2, maxSat=1.2, minLight=0., maxLight=1., **kwargs): """Cube helix colormap A full implementation of Dave Green's "cubehelix" for Matplotlib. Based on the FORTRAN 77 code provided in D.A. Green, 2011, BASI, 39, 289. http://adsabs.harvard.edu/abs/2011arXiv1108.5083G User can adjust all parameters of the cubehelix algorithm. This enables much greater flexibility in choosing color maps, while always ensuring the color map scales in intensity from black to white. A few simple examples: Default color map settings produce the standard "cubehelix". Create color map in only blues by setting rot=0 and start=0. Create reverse (white to black) backwards through the rainbow once by setting rot=1 and reverse=True. Parameters ---------- start : scalar, optional Sets the starting position in the color space. 0=blue, 1=red, 2=green. Defaults to 0.5. rot : scalar, optional The number of rotations through the rainbow. Can be positive or negative, indicating direction of rainbow. Negative values correspond to Blue->Red direction. Defaults to -1.5 gamma : scalar, optional The gamma correction for intensity. Defaults to 1.0 reverse : boolean, optional Set to True to reverse the color map. Will go from black to white. Good for density plots where shade~density. Defaults to False nlev : scalar, optional Defines the number of discrete levels to render colors at. Defaults to 32. sat : scalar, optional The saturation intensity factor. Defaults to 1.2 NOTE: this was formerly known as "hue" parameter minSat : scalar, optional Sets the minimum-level saturation. Defaults to 1.2 maxSat : scalar, optional Sets the maximum-level saturation. Defaults to 1.2 startHue : scalar, optional Sets the starting color, ranging from [0, 360], as in D3 version by @mbostock NOTE: overrides values in start parameter endHue : scalar, optional Sets the ending color, ranging from [0, 360], as in D3 version by @mbostock NOTE: overrides values in rot parameter minLight : scalar, optional Sets the minimum lightness value. Defaults to 0. maxLight : scalar, optional Sets the maximum lightness value. Defaults to 1. """ super(CubeHelixColormap, self).__init__( cubehelix(start=start, rot=rot, gamma=gamma, reverse=reverse, nlev=nlev, minSat=minSat, maxSat=maxSat, minLight=minLight, maxLight=maxLight, **kwargs)) class _Fire(BaseColormap): colors = [(1.0, 1.0, 1.0, 1.0), (1.0, 1.0, 0.0, 1.0), (1.0, 0.0, 0.0, 1.0)] glsl_map = """ vec4 fire(float t) { return mix(mix($color_0, $color_1, t), mix($color_1, $color_2, t*t), t); } """ def map(self, t): a, b, d = self.colors.rgba c = _mix_simple(a, b, t) e = _mix_simple(b, d, t**2) return _mix_simple(c, e, t) class _Grays(BaseColormap): glsl_map = """ vec4 grays(float t) { return vec4(t, t, t, 1.0); } """ def map(self, t): if isinstance(t, np.ndarray): return np.hstack([t, t, t, np.ones(t.shape)]).astype(np.float32) else: return np.array([t, t, t, 1.0], dtype=np.float32) class _Ice(BaseColormap): glsl_map = """ vec4 ice(float t) { return vec4(t, t, 1.0, 1.0); } """ def map(self, t): if isinstance(t, np.ndarray): return np.hstack([t, t, np.ones(t.shape), np.ones(t.shape)]).astype(np.float32) else: return np.array([t, t, 1.0, 1.0], dtype=np.float32) class _Hot(BaseColormap): colors = [(0., .33, .66, 1.0), (.33, .66, 1., 1.0)] glsl_map = """ vec4 hot(float t) { return vec4(smoothstep($color_0.rgb, $color_1.rgb, vec3(t, t, t)), 1.0); } """ def map(self, t): rgba = self.colors.rgba smoothed = smoothstep(rgba[0, :3], rgba[1, :3], t) return np.hstack((smoothed, np.ones((len(t), 1)))) class _Winter(BaseColormap): colors = [(0.0, 0.0, 1.0, 1.0), (0.0, 1.0, 0.5, 1.0)] glsl_map = """ vec4 winter(float t) { return mix($color_0, $color_1, sqrt(t)); } """ def map(self, t): return _mix_simple(self.colors.rgba[0], self.colors.rgba[1], np.sqrt(t)) _colormaps = dict( autumn=Colormap([(1., 0., 0., 1.), (1., 1., 0., 1.)]), blues=Colormap([(1., 1., 1., 1.), (0., 0., 1., 1.)]), cool=Colormap([(0., 1., 1., 1.), (1., 0., 1., 1.)]), greens=Colormap([(1., 1., 1., 1.), (0., 1., 0., 1.)]), reds=Colormap([(1., 1., 1., 1.), (1., 0., 0., 1.)]), spring=Colormap([(1., 0., 1., 1.), (1., 1., 0., 1.)]), summer=Colormap([(0., .5, .4, 1.), (1., 1., .4, 1.)]), fire=_Fire(), grays=_Grays(), hot=_Hot(), ice=_Ice(), winter=_Winter(), cubehelix=CubeHelixColormap(), ) def get_colormap(name): """Obtain a colormap Parameters ---------- name : str | Colormap Colormap name. Can also be a Colormap for pass-through. """ if isinstance(name, BaseColormap): cmap = name else: if not isinstance(name, string_types): raise TypeError('colormap must be a Colormap or string name') if name not in _colormaps: raise KeyError('colormap name %s not found' % name) cmap = _colormaps[name] return cmap def get_colormaps(): """Return the list of colormap names.""" return _colormaps.copy() vispy-0.4.0/vispy/color/color_space.py0000664000175000017500000001367012527672621021537 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from __future__ import division # just to be safe... import numpy as np from ..ext.six import string_types ############################################################################### # Utility functions def _check_color_dim(val): """Ensure val is Nx(n_col), usually Nx3""" val = np.atleast_2d(val) if val.shape[1] not in (3, 4): raise RuntimeError('Value must have second dimension of size 3 or 4') return val, val.shape[1] ############################################################################### # RGB<->HEX conversion def _hex_to_rgba(hexs): """Convert hex to rgba, permitting alpha values in hex""" hexs = np.atleast_1d(np.array(hexs, '|U9')) out = np.ones((len(hexs), 4), np.float32) for hi, h in enumerate(hexs): assert isinstance(h, string_types) off = 1 if h[0] == '#' else 0 assert len(h) in (6+off, 8+off) e = (len(h)-off) // 2 out[hi, :e] = [int(h[i:i+2], 16) / 255. for i in range(off, len(h), 2)] return out def _rgb_to_hex(rgbs): """Convert rgb to hex triplet""" rgbs, n_dim = _check_color_dim(rgbs) return np.array(['#%02x%02x%02x' % tuple((255*rgb[:3]).astype(np.uint8)) for rgb in rgbs], '|U7') ############################################################################### # RGB<->HSV conversion def _rgb_to_hsv(rgbs): """Convert Nx3 or Nx4 rgb to hsv""" rgbs, n_dim = _check_color_dim(rgbs) hsvs = list() for rgb in rgbs: rgb = rgb[:3] # don't use alpha here idx = np.argmax(rgb) val = rgb[idx] c = val - np.min(rgb) if c == 0: hue = 0 sat = 0 else: if idx == 0: # R == max hue = ((rgb[1] - rgb[2]) / c) % 6 elif idx == 1: # G == max hue = (rgb[2] - rgb[0]) / c + 2 else: # B == max hue = (rgb[0] - rgb[1]) / c + 4 hue *= 60 sat = c / val hsv = [hue, sat, val] hsvs.append(hsv) hsvs = np.array(hsvs, dtype=np.float32) if n_dim == 4: hsvs = np.concatenate((hsvs, rgbs[:, 3]), axis=1) return hsvs def _hsv_to_rgb(hsvs): """Convert Nx3 or Nx4 hsv to rgb""" hsvs, n_dim = _check_color_dim(hsvs) # In principle, we *might* be able to vectorize this, but might as well # wait until a compelling use case appears rgbs = list() for hsv in hsvs: c = hsv[1] * hsv[2] m = hsv[2] - c hp = hsv[0] / 60 x = c * (1 - abs(hp % 2 - 1)) if 0 <= hp < 1: r, g, b = c, x, 0 elif hp < 2: r, g, b = x, c, 0 elif hp < 3: r, g, b = 0, c, x elif hp < 4: r, g, b = 0, x, c elif hp < 5: r, g, b = x, 0, c else: r, g, b = c, 0, x rgb = [r + m, g + m, b + m] rgbs.append(rgb) rgbs = np.array(rgbs, dtype=np.float32) if n_dim == 4: rgbs = np.concatenate((rgbs, hsvs[:, 3]), axis=1) return rgbs ############################################################################### # RGB<->CIELab conversion # These numbers are adapted from MIT-licensed MATLAB code for # Lab<->RGB conversion. They provide an XYZ<->RGB conversion matrices, # w/D65 white point normalization built in. #_rgb2xyz = np.array([[0.412453, 0.357580, 0.180423], # [0.212671, 0.715160, 0.072169], # [0.019334, 0.119193, 0.950227]]) #_white_norm = np.array([0.950456, 1.0, 1.088754]) #_rgb2xyz /= _white_norm[:, np.newaxis] #_rgb2xyz_norm = _rgb2xyz.T _rgb2xyz_norm = np.array([[0.43395276, 0.212671, 0.01775791], [0.37621941, 0.71516, 0.10947652], [0.18982783, 0.072169, 0.87276557]]) #_xyz2rgb = np.array([[3.240479, -1.537150, -0.498535], # [-0.969256, 1.875992, 0.041556], # [0.055648, -0.204043, 1.057311]]) #_white_norm = np.array([0.950456, 1., 1.088754]) #_xyz2rgb *= _white_norm[np.newaxis, :] _xyz2rgb_norm = np.array([[3.07993271, -1.53715, -0.54278198], [-0.92123518, 1.875992, 0.04524426], [0.05289098, -0.204043, 1.15115158]]) def _rgb_to_lab(rgbs): rgbs, n_dim = _check_color_dim(rgbs) # convert RGB->XYZ xyz = rgbs[:, :3].copy() # a misnomer for now but will end up being XYZ over = xyz > 0.04045 xyz[over] = ((xyz[over] + 0.055) / 1.055) ** 2.4 xyz[~over] /= 12.92 xyz = np.dot(xyz, _rgb2xyz_norm) over = xyz > 0.008856 xyz[over] = xyz[over] ** (1. / 3.) xyz[~over] = 7.787 * xyz[~over] + 0.13793103448275862 # Convert XYZ->LAB L = (116. * xyz[:, 1]) - 16 a = 500 * (xyz[:, 0] - xyz[:, 1]) b = 200 * (xyz[:, 1] - xyz[:, 2]) labs = [L, a, b] # Append alpha if necessary if n_dim == 4: labs.append(np.atleast1d(rgbs[:, 3])) labs = np.array(labs, order='F').T # Becomes 'C' order b/c of .T return labs def _lab_to_rgb(labs): """Convert Nx3 or Nx4 lab to rgb""" # adapted from BSD-licensed work in MATLAB by Mark Ruzon # Based on ITU-R Recommendation BT.709 using the D65 labs, n_dim = _check_color_dim(labs) # Convert Lab->XYZ (silly indexing used to preserve dimensionality) y = (labs[:, 0] + 16.) / 116. x = (labs[:, 1] / 500.) + y z = y - (labs[:, 2] / 200.) xyz = np.concatenate(([x], [y], [z])) # 3xN over = xyz > 0.2068966 xyz[over] = xyz[over] ** 3. xyz[~over] = (xyz[~over] - 0.13793103448275862) / 7.787 # Convert XYZ->LAB rgbs = np.dot(_xyz2rgb_norm, xyz).T over = rgbs > 0.0031308 rgbs[over] = 1.055 * (rgbs[over] ** (1. / 2.4)) - 0.055 rgbs[~over] *= 12.92 if n_dim == 4: rgbs = np.concatenate((rgbs, labs[:, 3]), axis=1) rgbs = np.clip(rgbs, 0., 1.) return rgbs vispy-0.4.0/vispy/color/__init__.py0000664000175000017500000000116512527672621021001 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Convience interfaces to manipulate colors. This module provides support for manipulating colors. """ from ._color_dict import get_color_names, get_color_dict # noqa from .color_array import Color, ColorArray from .colormap import (Colormap, BaseColormap, # noqa get_colormap, get_colormaps) # noqa __all__ = ['Color', 'ColorArray', 'Colormap', 'BaseColormap', 'get_colormap', 'get_colormaps', 'get_color_names', 'get_color_dict'] vispy-0.4.0/vispy/color/color_array.py0000664000175000017500000003362112527672621021560 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from __future__ import division # just to be safe... import numpy as np from copy import deepcopy from ..ext.six import string_types from ..util import logger from ._color_dict import _color_dict from .color_space import (_hex_to_rgba, _rgb_to_hex, _rgb_to_hsv, # noqa _hsv_to_rgb, _rgb_to_lab, _lab_to_rgb) # noqa ############################################################################### # User-friendliness helpers def _string_to_rgb(color): """Convert user string or hex color to color array (length 3 or 4)""" if not color.startswith('#'): if color.lower() not in _color_dict: raise ValueError('Color "%s" unknown' % color) color = _color_dict[color] assert color[0] == '#' # hex color color = color[1:] lc = len(color) if lc in (3, 4): color = ''.join(c + c for c in color) lc = len(color) if lc not in (6, 8): raise ValueError('Hex color must have exactly six or eight ' 'elements following the # sign') color = np.array([int(color[i:i+2], 16) / 255. for i in range(0, lc, 2)]) return color def _user_to_rgba(color, expand=True, clip=False): """Convert color(s) from any set of fmts (str/hex/arr) to RGB(A) array""" if color is None: color = np.zeros(4, np.float32) if isinstance(color, string_types): color = _string_to_rgb(color) elif isinstance(color, ColorArray): color = color.rgba # We have to treat this specially elif isinstance(color, (list, tuple)): if any(isinstance(c, string_types) for c in color): color = [_user_to_rgba(c, expand=expand, clip=clip) for c in color] if any(len(c) > 1 for c in color): raise RuntimeError('could not parse colors, are they nested?') color = [c[0] for c in color] color = np.atleast_2d(color).astype(np.float32) if color.shape[1] not in (3, 4): raise ValueError('color must have three or four elements') if expand and color.shape[1] == 3: # only expand if requested color = np.concatenate((color, np.ones((color.shape[0], 1))), axis=1) if color.min() < 0 or color.max() > 1: if clip: color = np.clip(color, 0, 1) else: raise ValueError("Color values must be between 0 and 1 (or use " "clip=True to automatically clip the values).") return color def _array_clip_val(val): """Helper to turn val into array and clip between 0 and 1""" val = np.array(val) if val.max() > 1 or val.min() < 0: logger.warning('value will be clipped between 0 and 1') val[...] = np.clip(val, 0, 1) return val ############################################################################### # Color Array class ColorArray(object): """An array of colors Parameters ---------- color : str | tuple | list of colors If str, can be any of the names in ``vispy.color.get_color_names``. Can also be a hex value if it starts with ``'#'`` as ``'#ff0000'``. If array-like, it must be an Nx3 or Nx4 array-like object. Can also be a list of colors, such as ``['red', '#00ff00', ColorArray('blue')]``. alpha : float | None If no alpha is not supplied in ``color`` entry and ``alpha`` is None, then this will default to 1.0 (opaque). If float, it will override any alpha values in ``color``, if provided. clip : bool Clip the color value. color_space : 'rgb' | 'hsv' 'rgb' (default) : color tuples are interpreted as (r, g, b) components. 'hsv' : color tuples are interpreted as (h, s, v) components. Examples -------- There are many ways to define colors. Here are some basic cases: >>> from vispy.color import ColorArray >>> r = ColorArray('red') # using string name >>> r >>> g = ColorArray((0, 1, 0, 1)) # RGBA tuple >>> b = ColorArray('#0000ff') # hex color >>> w = ColorArray() # defaults to black >>> w.rgb = r.rgb + g.rgb + b.rgb >>>hsv_color = ColorArray(color_space="hsv", color=(0, 0, 0.5)) >>>hsv_color >>> w == ColorArray('white') True >>> w.alpha = 0 >>> w >>> rgb = ColorArray(['r', (0, 1, 0), '#0000FFFF']) >>> rgb >>> rgb == ColorArray(['red', '#00ff00', ColorArray('blue')]) True Notes ----- Under the hood, this class stores data in RGBA format suitable for use on the GPU. """ def __init__(self, color=(0., 0., 0.), alpha=None, clip=False, color_space='rgb'): # if color is RGB, then set the default color to black color = (0,) * 4 if color is None else color if color_space == 'hsv': # if the color space is hsv, convert hsv to rgb color = _hsv_to_rgb(color) elif color_space != 'rgb': raise ValueError('color_space should be either "rgb" or' '"hsv", it is ' + color_space) # Parse input type, and set attribute""" rgba = _user_to_rgba(color, clip=clip) if alpha is not None: rgba[:, 3] = alpha self._rgba = None self.rgba = rgba ########################################################################### # Builtins and utilities def copy(self): """Return a copy""" return deepcopy(self) @classmethod def _name(cls): """Helper to get the class name once it's been created""" return cls.__name__ def __len__(self): return self._rgba.shape[0] def __repr__(self): nice_str = str(tuple(self._rgba[0])) plural = '' if len(self) > 1: plural = 's' nice_str += ' ... ' + str(tuple(self.rgba[-1])) # use self._name() here instead of hard-coding name in case # we eventually subclass this class return ('<%s: %i color%s (%s)>' % (self._name(), len(self), plural, nice_str)) def __eq__(self, other): return np.array_equal(self._rgba, other._rgba) ########################################################################### def __getitem__(self, item): if isinstance(item, tuple): raise ValueError('ColorArray indexing is only allowed along ' 'the first dimension.') subrgba = self._rgba[item] if subrgba.ndim == 1: assert len(subrgba) == 4 elif subrgba.ndim == 2: assert subrgba.shape[1] in (3, 4) return ColorArray(subrgba) def __setitem__(self, item, value): if isinstance(item, tuple): raise ValueError('ColorArray indexing is only allowed along ' 'the first dimension.') # value should be a RGBA array, or a ColorArray instance if isinstance(value, ColorArray): value = value.rgba self._rgba[item] = value def extend(self, colors): """Extend a ColorArray with new colors Parameters ---------- colors : instance of ColorArray The new colors. """ colors = ColorArray(colors) self._rgba = np.vstack((self._rgba, colors._rgba)) return self # RGB(A) @property def rgba(self): """Nx4 array of RGBA floats""" return self._rgba.copy() @rgba.setter def rgba(self, val): """Set the color using an Nx4 array of RGBA floats""" # Note: all other attribute sets get routed here! # This method is meant to do the heavy lifting of setting data rgba = _user_to_rgba(val, expand=False) if self._rgba is None: self._rgba = rgba # only on init else: self._rgba[:, :rgba.shape[1]] = rgba @property def rgb(self): """Nx3 array of RGB floats""" return self._rgba[:, :3].copy() @rgb.setter def rgb(self, val): """Set the color using an Nx3 array of RGB floats""" self.rgba = val @property def RGBA(self): """Nx4 array of RGBA uint8s""" return (self._rgba * 255).astype(np.uint8) @RGBA.setter def RGBA(self, val): """Set the color using an Nx4 array of RGBA uint8 values""" # need to convert to normalized float val = np.atleast_1d(val).astype(np.float32) / 255 self.rgba = val @property def RGB(self): """Nx3 array of RGBA uint8s""" return np.round(self._rgba[:, :3] * 255).astype(int) @RGB.setter def RGB(self, val): """Set the color using an Nx3 array of RGB uint8 values""" # need to convert to normalized float val = np.atleast_1d(val).astype(np.float32) / 255. self.rgba = val @property def alpha(self): """Length-N array of alpha floats""" return self._rgba[:, 3] @alpha.setter def alpha(self, val): """Set the color using alpha""" self._rgba[:, 3] = _array_clip_val(val) ########################################################################### # HEX @property def hex(self): """Numpy array with N elements, each one a hex triplet string""" return _rgb_to_hex(self._rgba) @hex.setter def hex(self, val): """Set the color values using a list of hex strings""" self.rgba = _hex_to_rgba(val) ########################################################################### # HSV @property def hsv(self): """Nx3 array of HSV floats""" return self._hsv @hsv.setter def hsv(self, val): """Set the color values using an Nx3 array of HSV floats""" self.rgba = _hsv_to_rgb(val) @property def _hsv(self): """Nx3 array of HSV floats""" # this is done privately so that overriding functions work return _rgb_to_hsv(self._rgba[:, :3]) @property def value(self): """Length-N array of color HSV values""" return self._hsv[:, 2] @value.setter def value(self, val): """Set the color using length-N array of (from HSV)""" hsv = self._hsv hsv[:, 2] = _array_clip_val(val) self.rgba = _hsv_to_rgb(hsv) def lighter(self, dv=0.1, copy=True): """Produce a lighter color (if possible) Parameters ---------- dv : float Amount to increase the color value by. copy : bool If False, operation will be carried out in-place. Returns ------- color : instance of ColorArray The lightened Color. """ color = self.copy() if copy else self color.value += dv return color def darker(self, dv=0.1, copy=True): """Produce a darker color (if possible) Parameters ---------- dv : float Amount to decrease the color value by. copy : bool If False, operation will be carried out in-place. Returns ------- color : instance of ColorArray The darkened Color. """ color = self.copy() if copy else self color.value -= dv return color ########################################################################### # Lab @property def lab(self): return _rgb_to_lab(self._rgba[:, :3]) @lab.setter def lab(self, val): self.rgba = _lab_to_rgb(val) class Color(ColorArray): """A single color Parameters ---------- color : str | tuple If str, can be any of the names in ``vispy.color.get_color_names``. Can also be a hex value if it starts with ``'#'`` as ``'#ff0000'``. If array-like, it must be an 1-dimensional array with 3 or 4 elements. alpha : float | None If no alpha is not supplied in ``color`` entry and ``alpha`` is None, then this will default to 1.0 (opaque). If float, it will override the alpha value in ``color``, if provided. clip : bool If True, clip the color values. """ def __init__(self, color='black', alpha=None, clip=False): """Parse input type, and set attribute""" if isinstance(color, (list, tuple)): color = np.array(color, np.float32) rgba = _user_to_rgba(color, clip=clip) if rgba.shape[0] != 1: raise ValueError('color must be of correct shape') if alpha is not None: rgba[:, 3] = alpha self._rgba = None self.rgba = rgba.ravel() @ColorArray.rgba.getter def rgba(self): return super(Color, self).rgba[0] @ColorArray.rgb.getter def rgb(self): return super(Color, self).rgb[0] @ColorArray.RGBA.getter def RGBA(self): return super(Color, self).RGBA[0] @ColorArray.RGB.getter def RGB(self): return super(Color, self).RGB[0] @ColorArray.alpha.getter def alpha(self): return super(Color, self).alpha[0] @ColorArray.hex.getter def hex(self): return super(Color, self).hex[0] @ColorArray.hsv.getter def hsv(self): return super(Color, self).hsv[0] @ColorArray.value.getter def value(self): return super(Color, self).value[0] @ColorArray.lab.getter def lab(self): return super(Color, self).lab[0] @property def is_blank(self): """Boolean indicating whether the color is invisible. """ return self.rgba[3] == 0 def __repr__(self): nice_str = str(tuple(self._rgba[0])) return ('<%s: %s>' % (self._name(), nice_str)) vispy-0.4.0/vispy/color/tests/0000775000175000017500000000000012527674621020031 5ustar larsonerlarsoner00000000000000vispy-0.4.0/vispy/color/tests/test_color.py0000664000175000017500000003075212527672621022565 0ustar larsonerlarsoner00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2015, Vispy Development Team. # Distributed under the (new) BSD License. See LICENSE.txt for more info. import numpy as np from numpy.testing import assert_array_equal, assert_allclose from vispy.color import (Color, ColorArray, get_color_names, Colormap, get_color_dict, get_colormap, get_colormaps) from vispy.visuals.shaders import Function from vispy.util import use_log_level from vispy.testing import (run_tests_if_main, assert_equal, assert_raises, assert_true) def test_color(): """Basic tests for Color class""" x = Color('white') assert_array_equal(x.rgba, [1.] * 4) assert_array_equal(x.rgb, [1.] * 3) assert_array_equal(x.RGBA, [255] * 4) assert_array_equal(x.RGB, [255] * 3) assert_equal(x.value, 1.) assert_equal(x.alpha, 1.) x.rgb = [0, 0, 1] assert_array_equal(x.hsv, [240, 1, 1]) assert_equal(x.hex, '#0000ff') x.hex = '#00000000' assert_array_equal(x.rgba, [0.]*4) def test_color_array(): """Basic tests for ColorArray class""" x = ColorArray(['r', 'g', 'b']) assert_array_equal(x.rgb, np.eye(3)) # Test ColorArray.__getitem__. assert isinstance(x[0], ColorArray) assert isinstance(x[:], ColorArray) assert_array_equal(x.rgba[:], x[:].rgba) assert_array_equal(x.rgba[0], x[0].rgba.squeeze()) assert_array_equal(x.rgba[1:3], x[1:3].rgba) assert_raises(ValueError, x.__getitem__, (0, 1)) # Test ColorArray.__setitem__. x[0] = 0 assert_array_equal(x.rgba[0, :], np.zeros(4)) assert_array_equal(x.rgba, x[:].rgba) x[1] = 1 assert_array_equal(x[1].rgba, np.ones((1, 4))) x[:] = .5 assert_array_equal(x.rgba, .5 * np.ones((3, 4))) assert_raises(ValueError, x.__setitem__, (0, 1), 0) # test hsv color space colors x = ColorArray(color_space="hsv", color=[(0, 0, 1), (0, 0, 0.5), (0, 0, 0)]) assert_array_equal(x.rgba[0], [1, 1, 1, 1]) assert_array_equal(x.rgba[1], [0.5, 0.5, 0.5, 1]) assert_array_equal(x.rgba[2], [0, 0, 0, 1]) x = ColorArray(color_space="hsv") assert_array_equal(x.rgba[0], [0, 0, 0, 1]) def test_color_interpretation(): """Test basic color interpretation API""" # test useful ways of single color init r = ColorArray('r') print(r) # test repr r2 = ColorArray(r) assert_equal(r, r2) r2.rgb = 0, 0, 0 assert_equal(r2, ColorArray('black')) assert_equal(r, ColorArray('r')) # modifying new one preserves old assert_equal(r, r.copy()) assert_equal(r, ColorArray('#ff0000')) assert_equal(r, ColorArray('#FF0000FF')) assert_equal(r, ColorArray('red')) assert_equal(r, ColorArray('red', alpha=1.0)) assert_equal(ColorArray((1, 0, 0, 0.1)), ColorArray('red', alpha=0.1)) assert_array_equal(r.rgb.ravel(), (1., 0., 0.)) assert_array_equal(r.rgba.ravel(), (1., 0., 0., 1.)) assert_array_equal(r.RGBA.ravel(), (255, 0, 0, 255)) # handling multiple colors rgb = ColorArray(list('rgb')) print(rgb) # multi repr assert_array_equal(rgb, ColorArray(np.eye(3))) # complex/annoying case rgb = ColorArray(['r', (0, 1, 0), '#0000ffff']) assert_array_equal(rgb, ColorArray(np.eye(3))) assert_raises(RuntimeError, ColorArray, ['r', np.eye(3)]) # can't nest # getting/setting properties r = ColorArray('#ffff') assert_equal(r, ColorArray('white')) r = ColorArray('#ff000000') assert_true('turquoise' in get_color_names()) # make sure our JSON loaded assert_equal(r.alpha, 0) r.alpha = 1.0 assert_equal(r, ColorArray('r')) r.alpha = 0 r.rgb = (1, 0, 0) assert_equal(r.alpha, 0) assert_equal(r.hex, ['#ff0000']) r.alpha = 1 r.hex = '00ff00' assert_equal(r, ColorArray('g')) assert_array_equal(r.rgb.ravel(), (0., 1., 0.)) r.RGB = 255, 0, 0 assert_equal(r, ColorArray('r')) assert_array_equal(r.RGB.ravel(), (255, 0, 0)) r.RGBA = 255, 0, 0, 0 assert_equal(r, ColorArray('r', alpha=0)) w = ColorArray() w.rgb = ColorArray('r').rgb + ColorArray('g').rgb + ColorArray('b').rgb assert_equal(w, ColorArray('white')) w = ColorArray('white') assert_equal(w, w.darker().lighter()) assert_equal(w, w.darker(0.1).darker(-0.1)) w2 = w.darker() assert_true(w != w2) w.darker(copy=False) assert_equal(w, w2) with use_log_level('warning', record=True, print_msg=False) as w: w = ColorArray('white') w.value = 2 assert_equal(len(w), 1) assert_equal(w, ColorArray('white')) # warnings and errors assert_raises(ValueError, ColorArray, '#ffii00') # non-hex assert_raises(ValueError, ColorArray, '#ff000') # too short assert_raises(ValueError, ColorArray, [0, 0]) # not enough vals assert_raises(ValueError, ColorArray, [2, 0, 0]) # val > 1 assert_raises(ValueError, ColorArray, [-1, 0, 0]) # val < 0 c = ColorArray([2., 0., 0.], clip=True) # val > 1 assert_true(np.all(c.rgb <= 1)) c = ColorArray([-1., 0., 0.], clip=True) # val < 0 assert_true(np.all(c.rgb >= 0)) # make sure our color dict works for key in get_color_names(): assert_true(ColorArray(key)) assert_raises(ValueError, ColorArray, 'foo') # unknown color error _color_dict = get_color_dict() assert isinstance(_color_dict, dict) assert set(_color_dict.keys()) == set(get_color_names()) # Taken from known values hsv_dict = dict(red=(0, 1, 1), lime=(120, 1, 1), yellow=(60, 1, 1), silver=(0, 0, 0.75), olive=(60, 1, 0.5), purple=(300, 1, 0.5), navy=(240, 1, 0.5)) # Taken from skimage conversions lab_dict = dict(red=(53.2405879437448, 80.0941668344849, 67.2015369950715), lime=(87.7350994883189, -86.1812575110439, 83.1774770684517), yellow=(97.1395070397132, -21.5523924360088, 94.4757817840079), black=(0., 0., 0.), white=(100., 0., 0.), gray=(53.5850240, 0., 0.), olive=(51.86909754, -12.93002583, 56.67467593)) def test_color_conversion(): """Test color conversions""" # HSV # test known values test = ColorArray() for key in hsv_dict: c = ColorArray(key) test.hsv = hsv_dict[key] assert_allclose(c.RGB, test.RGB, atol=1) test.value = 0 assert_equal(test.value, 0) assert_equal(test, ColorArray('black')) c = ColorArray('black') assert_array_equal(c.hsv.ravel(), (0, 0, 0)) rng = np.random.RandomState(0) for _ in range(50): hsv = rng.rand(3) hsv[0] *= 360 hsv[1] = hsv[1] * 0.99 + 0.01 # avoid ugly boundary effects hsv[2] = hsv[2] * 0.99 + 0.01 c.hsv = hsv assert_allclose(c.hsv.ravel(), hsv, rtol=1e-4, atol=1e-4) # Lab test = ColorArray() for key in lab_dict: c = ColorArray(key) test.lab = lab_dict[key] assert_allclose(c.rgba, test.rgba, atol=1e-4, rtol=1e-4) assert_allclose(test.lab.ravel(), lab_dict[key], atol=1e-4, rtol=1e-4) for _ in range(50): # boundaries can have ugly rounding errors in some parameters rgb = (rng.rand(3)[np.newaxis, :] * 0.9 + 0.05) c.rgb = rgb lab = c.lab c.lab = lab assert_allclose(c.lab, lab, atol=1e-4, rtol=1e-4) assert_allclose(c.rgb, rgb, atol=1e-4, rtol=1e-4) def test_colormap_interpolation(): """Test interpolation routines for colormaps.""" import vispy.color.colormap as c assert_raises(AssertionError, c._glsl_step, [0., 1.],) c._glsl_mix(controls=[0., 1.]) c._glsl_mix(controls=[0., .25, 1.]) for fun in (c._glsl_step, c._glsl_mix): assert_raises(AssertionError, fun, controls=[0.1, 1.],) assert_raises(AssertionError, fun, controls=[0., .9],) assert_raises(AssertionError, fun, controls=[0.1, .9],) # Interpolation tests. color_0 = np.array([1., 0., 0.]) color_1 = np.array([0., 1., 0.]) color_2 = np.array([0., 0., 1.]) colors_00 = np.vstack((color_0, color_0)) colors_01 = np.vstack((color_0, color_1)) colors_11 = np.vstack((color_1, color_1)) # colors_012 = np.vstack((color_0, color_1, color_2)) colors_021 = np.vstack((color_0, color_2, color_1)) controls_2 = np.array([0., 1.]) controls_3 = np.array([0., .25, 1.]) x = np.array([-1., 0., 0.1, 0.4, 0.5, 0.6, 1., 2.])[:, None] mixed_2 = c.mix(colors_01, x, controls_2) mixed_3 = c.mix(colors_021, x, controls_3) for y in mixed_2, mixed_3: assert_allclose(y[:2, :], colors_00) assert_allclose(y[-2:, :], colors_11) assert_allclose(mixed_2[:, -1], np.zeros(len(y))) def test_colormap_gradient(): """Test gradient colormaps.""" cm = Colormap(['r', 'g']) assert_allclose(cm[-1].rgba, [[1, 0, 0, 1]]) assert_allclose(cm[0.].rgba, [[1, 0, 0, 1]]) assert_allclose(cm[0.5].rgba, [[.5, .5, 0, 1]]) assert_allclose(cm[1.].rgba, [[0, 1, 0, 1]]) cm = Colormap(['r', 'g', 'b']) assert_allclose(cm[-1].rgba, [[1, 0, 0, 1]]) assert_allclose(cm[0.].rgba, [[1, 0, 0, 1]]) assert_allclose(cm[.5].rgba, [[0, 1, 0, 1]]) assert_allclose(cm[1].rgba, [[0, 0, 1, 1]]) assert_allclose(cm[2].rgba, [[0, 0, 1, 1]]) cm = Colormap(['r', 'g', 'b'], [0., 0.1, 1.0]) assert_allclose(cm[-1].rgba, [[1, 0, 0, 1]]) assert_allclose(cm[0.].rgba, [[1, 0, 0, 1]]) assert_allclose(cm[.1].rgba, [[0, 1, 0, 1]]) assert_allclose(cm[1].rgba, [[0, 0, 1, 1]], 1e-6, 1e-6) assert_allclose(cm[2].rgba, [[0, 0, 1, 1]], 1e-6, 1e-6) def test_colormap_discrete(): """Test discrete colormaps.""" cm = Colormap(['r', 'g'], interpolation='zero') assert_allclose(cm[-1].rgba, [[1, 0, 0, 1]]) assert_allclose(cm[0.].rgba, [[1, 0, 0, 1]]) assert_allclose(cm[0.49].rgba, [[1, 0, 0, 1]]) assert_allclose(cm[0.51].rgba, [[0, 1, 0, 1]]) assert_allclose(cm[1.].rgba, [[0, 1, 0, 1]]) cm = Colormap(['r', 'g', 'b'], interpolation='zero') assert_allclose(cm[-1].rgba, [[1, 0, 0, 1]]) assert_allclose(cm[0.].rgba, [[1, 0, 0, 1]]) assert_allclose(cm[.32].rgba, [[1, 0, 0, 1]]) assert_allclose(cm[.34].rgba, [[0, 1, 0, 1]]) assert_allclose(cm[.66].rgba, [[0, 1, 0, 1]]) assert_allclose(cm[.67].rgba, [[0, 0, 1, 1]]) assert_allclose(cm[.99].rgba, [[0, 0, 1, 1]]) assert_allclose(cm[1].rgba, [[0, 0, 1, 1]]) assert_allclose(cm[1.1].rgba, [[0, 0, 1, 1]]) cm = Colormap(['r', 'g', 'b'], [0., 0.1, 0.8, 1.0], interpolation='zero') assert_allclose(cm[-1].rgba, [[1, 0, 0, 1]]) assert_allclose(cm[0.].rgba, [[1, 0, 0, 1]]) assert_allclose(cm[.099].rgba, [[1, 0, 0, 1]]) assert_allclose(cm[.101].rgba, [[0, 1, 0, 1]]) assert_allclose(cm[.799].rgba, [[0, 1, 0, 1]]) assert_allclose(cm[.801].rgba, [[0, 0, 1, 1]]) assert_allclose(cm[1].rgba, [[0, 0, 1, 1]], 1e-6, 1e-6) assert_allclose(cm[2].rgba, [[0, 0, 1, 1]], 1e-6, 1e-6) def test_colormap(): """Test named colormaps.""" autumn = get_colormap('autumn') assert autumn.glsl_map is not "" assert len(autumn[0.]) == 1 assert len(autumn[0.5]) == 1 assert len(autumn[1.]) == 1 assert len(autumn[[0., 0.5, 1.]]) == 3 assert len(autumn[np.array([0., 0.5, 1.])]) == 3 fire = get_colormap('fire') assert_array_equal(fire[0].rgba, np.ones((1, 4))) assert_array_equal(fire[1].rgba, np.array([[1, 0, 0, 1]])) grays = get_colormap('grays') assert_array_equal(grays[.5].rgb, np.ones((1, 3)) * .5) hot = get_colormap('hot') assert_allclose(hot[0].rgba, [[0, 0, 0, 1]], 1e-6, 1e-6) assert_allclose(hot[0.5].rgba, [[1, .52272022, 0, 1]], 1e-6, 1e-6) assert_allclose(hot[1.].rgba, [[1, 1, 1, 1]], 1e-6, 1e-6) # Test the GLSL and Python mapping. for name in get_colormaps(): colormap = get_colormap(name) Function(colormap.glsl_map) colors = colormap[np.linspace(-2., 2., 50)] assert colors.rgba.min() >= 0 assert colors.rgba.max() <= 1 def test_normalize(): """Test the _normalize() function.""" from vispy.color.colormap import _normalize for x in (-1, 0., .5, 1., 10., 20): assert _normalize(x) == .5 assert_allclose(_normalize((-1., 0., 1.)), (0., .5, 1.)) assert_allclose(_normalize((-1., 0., 1.), 0., 1.), (0., 0., 1.)) assert_allclose(_normalize((-1., 0., 1.), 0., 1., clip=False), (-1., 0., 1.)) y = _normalize(np.random.randn(100, 5), -10., 10.) assert_allclose([y.min(), y.max()], [0.2975, 1-0.2975], 1e-1, 1e-1) run_tests_if_main() vispy-0.4.0/vispy/color/tests/__init__.py0000664000175000017500000000000012375431476022130 0ustar larsonerlarsoner00000000000000vispy-0.4.0/vispy.egg-info/0000775000175000017500000000000012527674621017243 5ustar larsonerlarsoner00000000000000vispy-0.4.0/vispy.egg-info/dependency_links.txt0000664000175000017500000000000112527674621023311 0ustar larsonerlarsoner00000000000000 vispy-0.4.0/vispy.egg-info/top_level.txt0000664000175000017500000000000612527674621021771 0ustar larsonerlarsoner00000000000000vispy vispy-0.4.0/vispy.egg-info/PKG-INFO0000664000175000017500000000265112527674621020344 0ustar larsonerlarsoner00000000000000Metadata-Version: 1.1 Name: vispy Version: 0.4.0 Summary: Interactive visualization in Python Home-page: http://vispy.org Author: Vispy contributors Author-email: vispy@googlegroups.com License: (new) BSD Download-URL: https://pypi.python.org/pypi/vispy Description: ===== Vispy ===== Vispy is a **high-performance interactive 2D/3D data visualization library**. Vispy leverages the computational power of modern **Graphics Processing Units (GPUs)** through the **OpenGL** library to display very large datasets. For more information, see http://vispy.org. Keywords: visualization OpenGl ES medical imaging 3D plotting numpy bigdata Platform: any Classifier: Development Status :: 3 - Alpha Classifier: Intended Audience :: Science/Research Classifier: Intended Audience :: Education Classifier: Intended Audience :: Developers Classifier: Topic :: Scientific/Engineering :: Visualization Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: MacOS :: MacOS X Classifier: Operating System :: Microsoft :: Windows Classifier: Operating System :: POSIX Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 Provides: vispy vispy-0.4.0/vispy.egg-info/not-zip-safe0000664000175000017500000000000112403401757021460 0ustar larsonerlarsoner00000000000000 vispy-0.4.0/vispy.egg-info/SOURCES.txt0000664000175000017500000003331312527674621021132 0ustar larsonerlarsoner00000000000000README.rst setup.py vispy/__init__.py vispy.egg-info/PKG-INFO vispy.egg-info/SOURCES.txt vispy.egg-info/dependency_links.txt vispy.egg-info/not-zip-safe vispy.egg-info/requires.txt vispy.egg-info/top_level.txt vispy/app/__init__.py vispy/app/_default_app.py vispy/app/application.py vispy/app/base.py vispy/app/canvas.py vispy/app/inputhook.py vispy/app/qt.py vispy/app/timer.py vispy/app/backends/__init__.py vispy/app/backends/_egl.py vispy/app/backends/_glfw.py vispy/app/backends/_ipynb_static.py vispy/app/backends/_ipynb_util.py vispy/app/backends/_ipynb_vnc.py vispy/app/backends/_ipynb_webgl.py vispy/app/backends/_pyglet.py vispy/app/backends/_pyqt4.py vispy/app/backends/_pyqt5.py vispy/app/backends/_pyside.py vispy/app/backends/_qt.py vispy/app/backends/_sdl2.py vispy/app/backends/_template.py vispy/app/backends/_test.py vispy/app/backends/_wx.py vispy/app/backends/ipython/__init__.py vispy/app/backends/ipython/_widget.py vispy/app/backends/tests/__init__.py vispy/app/backends/tests/test_ipynb_util.py vispy/app/tests/__init__.py vispy/app/tests/qt-designer.ui vispy/app/tests/test_app.py vispy/app/tests/test_backends.py vispy/app/tests/test_context.py vispy/app/tests/test_interactive.py vispy/app/tests/test_qt.py vispy/app/tests/test_simultaneous.py vispy/color/__init__.py vispy/color/_color_dict.py vispy/color/color_array.py vispy/color/color_space.py vispy/color/colormap.py vispy/color/tests/__init__.py vispy/color/tests/test_color.py vispy/ext/__init__.py vispy/ext/_mpl_py3k_compat.py vispy/ext/cocoapy.py vispy/ext/cubehelix.py vispy/ext/decorator.py vispy/ext/egl.py vispy/ext/fontconfig.py vispy/ext/freetype.py vispy/ext/gdi32plus.py vispy/ext/glfw.py vispy/ext/gzip_open.py vispy/ext/ipy_inputhook.py vispy/ext/mplexporter.py vispy/ext/mplutils.py vispy/ext/ordereddict.py vispy/ext/png.py vispy/ext/py24_ordereddict.py vispy/ext/six.py vispy/geometry/__init__.py vispy/geometry/_triangulation_debugger.py vispy/geometry/calculations.py vispy/geometry/generation.py vispy/geometry/isocurve.py vispy/geometry/isosurface.py vispy/geometry/meshdata.py vispy/geometry/normals.py vispy/geometry/parametric.py vispy/geometry/polygon.py vispy/geometry/rect.py vispy/geometry/torusknot.py vispy/geometry/triangulation.py vispy/geometry/tests/__init__.py vispy/geometry/tests/test_calculations.py vispy/geometry/tests/test_generation.py vispy/geometry/tests/test_meshdata.py vispy/geometry/tests/test_triangulation.py vispy/gloo/__init__.py vispy/gloo/buffer.py vispy/gloo/context.py vispy/gloo/framebuffer.py vispy/gloo/glir.py vispy/gloo/globject.py vispy/gloo/preprocessor.py vispy/gloo/program.py vispy/gloo/texture.py vispy/gloo/util.py vispy/gloo/wrappers.py vispy/gloo/gl/__init__.py vispy/gloo/gl/_constants.py vispy/gloo/gl/_es2.py vispy/gloo/gl/_gl2.py vispy/gloo/gl/_proxy.py vispy/gloo/gl/_pyopengl2.py vispy/gloo/gl/dummy.py vispy/gloo/gl/es2.py vispy/gloo/gl/gl2.py vispy/gloo/gl/glplus.py vispy/gloo/gl/pyopengl2.py vispy/gloo/gl/tests/__init__.py vispy/gloo/gl/tests/test_basics.py vispy/gloo/gl/tests/test_functionality.py vispy/gloo/gl/tests/test_names.py vispy/gloo/gl/tests/test_use.py vispy/gloo/tests/__init__.py vispy/gloo/tests/test_buffer.py vispy/gloo/tests/test_context.py vispy/gloo/tests/test_framebuffer.py vispy/gloo/tests/test_glir.py vispy/gloo/tests/test_globject.py vispy/gloo/tests/test_program.py vispy/gloo/tests/test_texture.py vispy/gloo/tests/test_use_gloo.py vispy/gloo/tests/test_util.py vispy/gloo/tests/test_wrappers.py vispy/glsl/__init__.py vispy/glsl/build-spatial-filters.py vispy/glsl/antialias/__init__.py vispy/glsl/antialias/antialias.glsl vispy/glsl/antialias/cap-butt.glsl vispy/glsl/antialias/cap-round.glsl vispy/glsl/antialias/cap-square.glsl vispy/glsl/antialias/cap-triangle-in.glsl vispy/glsl/antialias/cap-triangle-out.glsl vispy/glsl/antialias/cap.glsl vispy/glsl/antialias/caps.glsl vispy/glsl/antialias/filled.glsl vispy/glsl/antialias/outline.glsl vispy/glsl/antialias/stroke.glsl vispy/glsl/arrows/__init__.py vispy/glsl/arrows/angle-30.glsl vispy/glsl/arrows/angle-60.glsl vispy/glsl/arrows/angle-90.glsl vispy/glsl/arrows/arrow.frag vispy/glsl/arrows/arrow.vert vispy/glsl/arrows/arrows.glsl vispy/glsl/arrows/common.glsl vispy/glsl/arrows/curved.glsl vispy/glsl/arrows/stealth.glsl vispy/glsl/arrows/triangle-30.glsl vispy/glsl/arrows/triangle-60.glsl vispy/glsl/arrows/triangle-90.glsl vispy/glsl/arrows/util.glsl vispy/glsl/collections/__init__.py vispy/glsl/collections/agg-fast-path.frag vispy/glsl/collections/agg-fast-path.vert vispy/glsl/collections/agg-glyph.frag vispy/glsl/collections/agg-glyph.vert vispy/glsl/collections/agg-marker.frag vispy/glsl/collections/agg-marker.vert vispy/glsl/collections/agg-path.frag vispy/glsl/collections/agg-path.vert vispy/glsl/collections/agg-point.frag vispy/glsl/collections/agg-point.vert vispy/glsl/collections/agg-segment.frag vispy/glsl/collections/agg-segment.vert vispy/glsl/collections/marker.frag vispy/glsl/collections/marker.vert vispy/glsl/collections/raw-path.frag vispy/glsl/collections/raw-path.vert vispy/glsl/collections/raw-point.frag vispy/glsl/collections/raw-point.vert vispy/glsl/collections/raw-segment.frag vispy/glsl/collections/raw-segment.vert vispy/glsl/collections/raw-triangle.frag vispy/glsl/collections/raw-triangle.vert vispy/glsl/collections/sdf-glyph-ticks.vert vispy/glsl/collections/sdf-glyph.frag vispy/glsl/collections/sdf-glyph.vert vispy/glsl/collections/tick-labels.vert vispy/glsl/colormaps/__init__.py vispy/glsl/colormaps/autumn.glsl vispy/glsl/colormaps/blues.glsl vispy/glsl/colormaps/color-space.glsl vispy/glsl/colormaps/colormaps.glsl vispy/glsl/colormaps/cool.glsl vispy/glsl/colormaps/fire.glsl vispy/glsl/colormaps/gray.glsl vispy/glsl/colormaps/greens.glsl vispy/glsl/colormaps/hot.glsl vispy/glsl/colormaps/ice.glsl vispy/glsl/colormaps/icefire.glsl vispy/glsl/colormaps/parse.py vispy/glsl/colormaps/reds.glsl vispy/glsl/colormaps/spring.glsl vispy/glsl/colormaps/summer.glsl vispy/glsl/colormaps/user.glsl vispy/glsl/colormaps/util.glsl vispy/glsl/colormaps/wheel.glsl vispy/glsl/colormaps/winter.glsl vispy/glsl/markers/__init__.py vispy/glsl/markers/arrow.glsl vispy/glsl/markers/asterisk.glsl vispy/glsl/markers/chevron.glsl vispy/glsl/markers/clover.glsl vispy/glsl/markers/club.glsl vispy/glsl/markers/cross.glsl vispy/glsl/markers/diamond.glsl vispy/glsl/markers/disc.glsl vispy/glsl/markers/ellipse.glsl vispy/glsl/markers/hbar.glsl vispy/glsl/markers/heart.glsl vispy/glsl/markers/infinity.glsl vispy/glsl/markers/marker-sdf.frag vispy/glsl/markers/marker-sdf.vert vispy/glsl/markers/marker.frag vispy/glsl/markers/marker.vert vispy/glsl/markers/markers.glsl vispy/glsl/markers/pin.glsl vispy/glsl/markers/ring.glsl vispy/glsl/markers/spade.glsl vispy/glsl/markers/square.glsl vispy/glsl/markers/tag.glsl vispy/glsl/markers/triangle.glsl vispy/glsl/markers/vbar.glsl vispy/glsl/math/__init__.py vispy/glsl/math/circle-through-2-points.glsl vispy/glsl/math/constants.glsl vispy/glsl/math/double.glsl vispy/glsl/math/functions.glsl vispy/glsl/math/point-to-line-distance.glsl vispy/glsl/math/point-to-line-projection.glsl vispy/glsl/math/signed-line-distance.glsl vispy/glsl/math/signed-segment-distance.glsl vispy/glsl/misc/__init__.py vispy/glsl/misc/regular-grid.frag vispy/glsl/misc/spatial-filters.frag vispy/glsl/misc/viewport-NDC.glsl vispy/glsl/transforms/__init__.py vispy/glsl/transforms/azimuthal-equal-area.glsl vispy/glsl/transforms/azimuthal-equidistant.glsl vispy/glsl/transforms/hammer.glsl vispy/glsl/transforms/identity.glsl vispy/glsl/transforms/identity_forward.glsl vispy/glsl/transforms/identity_inverse.glsl vispy/glsl/transforms/linear-scale.glsl vispy/glsl/transforms/log-scale.glsl vispy/glsl/transforms/mercator-transverse-forward.glsl vispy/glsl/transforms/mercator-transverse-inverse.glsl vispy/glsl/transforms/panzoom.glsl vispy/glsl/transforms/polar.glsl vispy/glsl/transforms/position.glsl vispy/glsl/transforms/power-scale.glsl vispy/glsl/transforms/projection.glsl vispy/glsl/transforms/pvm.glsl vispy/glsl/transforms/rotate.glsl vispy/glsl/transforms/trackball.glsl vispy/glsl/transforms/translate.glsl vispy/glsl/transforms/transverse_mercator.glsl vispy/glsl/transforms/viewport-clipping.glsl vispy/glsl/transforms/viewport-transform.glsl vispy/glsl/transforms/viewport.glsl vispy/glsl/transforms/x.glsl vispy/glsl/transforms/y.glsl vispy/glsl/transforms/z.glsl vispy/html/static/js/jquery.mousewheel.min.js vispy/html/static/js/vispy.min.js vispy/html/static/js/webgl-backend.js vispy/io/__init__.py vispy/io/datasets.py vispy/io/image.py vispy/io/mesh.py vispy/io/wavefront.py vispy/io/_data/spatial-filters.npy vispy/io/tests/__init__.py vispy/io/tests/test_image.py vispy/io/tests/test_io.py vispy/mpl_plot/__init__.py vispy/mpl_plot/_mpl_to_vispy.py vispy/mpl_plot/tests/__init__.py vispy/mpl_plot/tests/test_show_vispy.py vispy/plot/__init__.py vispy/plot/fig.py vispy/plot/plotwidget.py vispy/plot/tests/__init__.py vispy/plot/tests/test_plot.py vispy/scene/__init__.py vispy/scene/canvas.py vispy/scene/events.py vispy/scene/node.py vispy/scene/subscene.py vispy/scene/systems.py vispy/scene/visuals.py vispy/scene/cameras/__init__.py vispy/scene/cameras/cameras.py vispy/scene/cameras/magnify.py vispy/scene/tests/__init__.py vispy/scene/tests/test_node.py vispy/scene/tests/test_visuals.py vispy/scene/widgets/__init__.py vispy/scene/widgets/anchor.py vispy/scene/widgets/console.py vispy/scene/widgets/grid.py vispy/scene/widgets/viewbox.py vispy/scene/widgets/widget.py vispy/testing/__init__.py vispy/testing/_runners.py vispy/testing/_testing.py vispy/testing/image_tester.py vispy/testing/tests/__init__.py vispy/testing/tests/test_testing.py vispy/util/__init__.py vispy/util/bunch.py vispy/util/config.py vispy/util/eq.py vispy/util/event.py vispy/util/fetching.py vispy/util/filter.py vispy/util/fourier.py vispy/util/keys.py vispy/util/logs.py vispy/util/profiler.py vispy/util/ptime.py vispy/util/quaternion.py vispy/util/transforms.py vispy/util/wrappers.py vispy/util/dpi/__init__.py vispy/util/dpi/_linux.py vispy/util/dpi/_quartz.py vispy/util/dpi/_win32.py vispy/util/dpi/tests/__init__.py vispy/util/dpi/tests/test_dpi.py vispy/util/fonts/__init__.py vispy/util/fonts/_freetype.py vispy/util/fonts/_quartz.py vispy/util/fonts/_triage.py vispy/util/fonts/_vispy_fonts.py vispy/util/fonts/_win32.py vispy/util/fonts/tests/__init__.py vispy/util/fonts/tests/test_font.py vispy/util/svg/__init__.py vispy/util/svg/base.py vispy/util/svg/color.py vispy/util/svg/element.py vispy/util/svg/geometry.py vispy/util/svg/group.py vispy/util/svg/length.py vispy/util/svg/number.py vispy/util/svg/path.py vispy/util/svg/shapes.py vispy/util/svg/style.py vispy/util/svg/svg.py vispy/util/svg/transform.py vispy/util/svg/transformable.py vispy/util/svg/viewport.py vispy/util/tests/__init__.py vispy/util/tests/test_config.py vispy/util/tests/test_docstring_parameters.py vispy/util/tests/test_emitter_group.py vispy/util/tests/test_event_emitter.py vispy/util/tests/test_fourier.py vispy/util/tests/test_import.py vispy/util/tests/test_key.py vispy/util/tests/test_logging.py vispy/util/tests/test_run.py vispy/util/tests/test_transforms.py vispy/util/tests/test_vispy.py vispy/visuals/__init__.py vispy/visuals/cube.py vispy/visuals/ellipse.py vispy/visuals/gridlines.py vispy/visuals/histogram.py vispy/visuals/image.py vispy/visuals/isocurve.py vispy/visuals/isoline.py vispy/visuals/isosurface.py vispy/visuals/line_plot.py vispy/visuals/markers.py vispy/visuals/mesh.py vispy/visuals/polygon.py vispy/visuals/rectangle.py vispy/visuals/regular_polygon.py vispy/visuals/spectrogram.py vispy/visuals/surface_plot.py vispy/visuals/tube.py vispy/visuals/visual.py vispy/visuals/volume.py vispy/visuals/xyz_axis.py vispy/visuals/collections/__init__.py vispy/visuals/collections/agg_fast_path_collection.py vispy/visuals/collections/agg_path_collection.py vispy/visuals/collections/agg_point_collection.py vispy/visuals/collections/agg_segment_collection.py vispy/visuals/collections/array_list.py vispy/visuals/collections/base_collection.py vispy/visuals/collections/collection.py vispy/visuals/collections/path_collection.py vispy/visuals/collections/point_collection.py vispy/visuals/collections/polygon_collection.py vispy/visuals/collections/raw_path_collection.py vispy/visuals/collections/raw_point_collection.py vispy/visuals/collections/raw_polygon_collection.py vispy/visuals/collections/raw_segment_collection.py vispy/visuals/collections/raw_triangle_collection.py vispy/visuals/collections/segment_collection.py vispy/visuals/collections/triangle_collection.py vispy/visuals/collections/util.py vispy/visuals/components/__init__.py vispy/visuals/components/clipper.py vispy/visuals/components/color.py vispy/visuals/components/color2.py vispy/visuals/components/component.py vispy/visuals/components/material.py vispy/visuals/components/normal.py vispy/visuals/components/texture.py vispy/visuals/components/vertex.py vispy/visuals/glsl/__init__.py vispy/visuals/glsl/antialiasing.py vispy/visuals/glsl/color.py vispy/visuals/line/__init__.py vispy/visuals/line/dash_atlas.py vispy/visuals/line/fragment.py vispy/visuals/line/line.py vispy/visuals/line/vertex.py vispy/visuals/shaders/__init__.py vispy/visuals/shaders/compiler.py vispy/visuals/shaders/expression.py vispy/visuals/shaders/function.py vispy/visuals/shaders/parsing.py vispy/visuals/shaders/program.py vispy/visuals/shaders/shader_object.py vispy/visuals/shaders/variable.py vispy/visuals/shaders/tests/__init__.py vispy/visuals/shaders/tests/test_function.py vispy/visuals/shaders/tests/test_parsing.py vispy/visuals/text/__init__.py vispy/visuals/text/_sdf.py vispy/visuals/text/text.py vispy/visuals/transforms/__init__.py vispy/visuals/transforms/_util.py vispy/visuals/transforms/base_transform.py vispy/visuals/transforms/chain.py vispy/visuals/transforms/interactive.py vispy/visuals/transforms/linear.py vispy/visuals/transforms/nonlinear.py vispy/visuals/transforms/transform_system.pyvispy-0.4.0/vispy.egg-info/requires.txt0000664000175000017500000000000612527674621021637 0ustar larsonerlarsoner00000000000000numpy vispy-0.4.0/PKG-INFO0000664000175000017500000000265112527674621015500 0ustar larsonerlarsoner00000000000000Metadata-Version: 1.1 Name: vispy Version: 0.4.0 Summary: Interactive visualization in Python Home-page: http://vispy.org Author: Vispy contributors Author-email: vispy@googlegroups.com License: (new) BSD Download-URL: https://pypi.python.org/pypi/vispy Description: ===== Vispy ===== Vispy is a **high-performance interactive 2D/3D data visualization library**. Vispy leverages the computational power of modern **Graphics Processing Units (GPUs)** through the **OpenGL** library to display very large datasets. For more information, see http://vispy.org. Keywords: visualization OpenGl ES medical imaging 3D plotting numpy bigdata Platform: any Classifier: Development Status :: 3 - Alpha Classifier: Intended Audience :: Science/Research Classifier: Intended Audience :: Education Classifier: Intended Audience :: Developers Classifier: Topic :: Scientific/Engineering :: Visualization Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: MacOS :: MacOS X Classifier: Operating System :: Microsoft :: Windows Classifier: Operating System :: POSIX Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 Provides: vispy vispy-0.4.0/setup.cfg0000664000175000017500000000007312527674621016220 0ustar larsonerlarsoner00000000000000[egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 vispy-0.4.0/README.rst0000664000175000017500000001333512527673355016076 0ustar larsonerlarsoner00000000000000Vispy: interactive scientific visualization in Python ----------------------------------------------------- Main website: http://vispy.org |Build Status| |Appveyor Status| |Coverage Status| |Zenodo Link| ---- Vispy is a **high-performance interactive 2D/3D data visualization library**. Vispy leverages the computational power of modern **Graphics Processing Units (GPUs)** through the **OpenGL** library to display very large datasets. Applications of Vispy include: - High-quality interactive scientific plots with millions of points. - Direct visualization of real-time data. - Fast interactive visualization of 3D models (meshes, volume rendering). - OpenGL visualization demos. - Scientific GUIs with fast, scalable visualization widgets (Qt or `IPython notebook `__ with WebGL). Announcements ------------- - `Vispy tutorial in the IPython Cookbook `__ - **Release!** Version 0.3, August 29, 2014 - **EuroSciPy 2014**: talk at Saturday 30, and sprint at Sunday 31, August 2014 - `Article in Linux Magazine, French Edition `__, July 2014 - **GSoC 2014**: `two GSoC students are currently working on Vispy under the PSF umbrella `__ - **Release!**, Version 0.2.1 04-11-2013 - **Presentation at BI forum**, Budapest, 6 November 2013 - **Presentation at Euroscipy**, Belgium, August 2013 - **EuroSciPy Sprint**, Belgium, August 2013 - **Release!** Version 0.1.0 14-08-2013 Using Vispy ----------- Vispy is a young library under heavy development at this time. It targets two categories of users: 1. **Users knowing OpenGL**, or willing to learn OpenGL, who want to create beautiful and fast interactive 2D/3D visualizations in Python as easily as possible. 2. **Scientists without any knowledge of OpenGL**, who are seeking a high-level, high-performance plotting toolkit. If you're in the first category, you can already start using Vispy. Vispy offers a Pythonic, NumPy-aware, user-friendly interface for OpenGL ES 2.0 called **gloo**. You can focus on writing your GLSL code instead of dealing with the complicated OpenGL API - Vispy takes care of that automatically for you. If you're in the second category, we're starting to build experimental high-level plotting interfaces. Notably, Vispy now ships a very basic and experimental OpenGL backend for matplotlib. Installation ------------ Vispy runs on Python 2.6+ and Python 3.3+ and depends on NumPy. You also need a backend (PyQt4/PySide, glfw, pyglet, SDL, or wx). As Vispy is under heavy development at this time, we highly recommend you to use the development version on Github (master branch). You need to clone the repository and install Vispy with ``python setup.py install``. If you need to install Python for the first time, consider using the `Anaconda `_ Python distribution. It provides a convenient package management system. Structure of Vispy ------------------ Currently, the main subpackages are: - **app**: integrates an event system and offers a unified interface on top of many window backends (Qt4, wx, glfw, IPython notebook with/without WebGL, and others). Relatively stable API. - **gloo**: a Pythonic, object-oriented interface to OpenGL. Relatively stable API. - **mpl\_plot**: an OpenGL backend for matplotlib. Experimental. - **scene**: this is the system underlying our upcoming high level visualization interfaces. Under heavy development and still experimental, it contains several modules. - **Visuals** are graphical abstractions representing 2D shapes, 3D meshes, text, etc. - **Transforms** implement 2D/3D transformations implemented on both CPU and GPU. - **Shaders** implements a shader composition system for plumbing together snippets of GLSL code. - The **scene graph** tracks all objects within a transformation graph. The API of all public interfaces are subject to change in the future, although **app** and **gloo** are *relatively* stable at this point. About us -------- The core development team consists of: - `Luke Campagnola `__ - `Almar Klein `__ - `Eric Larson `__ - `Cyrille Rossant `__ - `Nicolas Rougier `__ Four of us have written our own Python visualization toolkit (`PyQtGraph `__ by LC, `Visvis `__ by AK, `Glumpy `__ by NR, and `Galry `__ by CR), and we decided to team up to create a unique high-performance, high-quality interactive visualization library. ---- External links -------------- - `User mailing list `__ - `Dev mailing list `__ - `Dev chat room `__ - `Wiki `__ - `Gallery `__ - `Documentation `__ .. |Build Status| image:: https://travis-ci.org/vispy/vispy.png?branch=master :target: https://travis-ci.org/vispy/vispy .. |Appveyor Status| image:: https://ci.appveyor.com/api/projects/status/dsxgkrbfj29xf9ef/branch/master :target: https://ci.appveyor.com/project/Eric89GXL/vispy/branch/master .. |Coverage Status| image:: https://coveralls.io/repos/vispy/vispy/badge.png?branch=master :target: https://coveralls.io/r/vispy/vispy?branch=master .. |Zenodo Link| image:: https://zenodo.org/badge/5822/vispy/vispy.png :target: http://dx.doi.org/10.5281/zenodo.11532